Git helpers for MuseScore 4 scores, built as MuseScore plugins. All workflows are designed around a single invariant:
- Scores in the repo are stored as
.msczonly.
The plugins live in the MuseScore Plugins folder next to the Scores folder, and assume your git working tree is the Scores directory:
.../MuseScore4/Plugins.../MuseScore4/Scores← git repo with.githere
-
Daily flow (no remote changes):
- Open
Scores/<Name>.msczin MuseScore. - Edit and save in MuseScore.
- Run Commit to GitHub to stage, commit, and push
<Name>.mscz.
- Open
-
When remote has changes for the same score:
- Open
Scores/<Name>.mscz. - Run Pull from GitHub:
- If pull succeeds: you are up to date, continue editing, then Commit to GitHub.
- If pull cannot merge: it extracts the remote version to
Scores/<Name> - Copy/<Name> - Copy.mscxand tells you to run MuseScore Diff.
- Run MuseScore Diff:
- It compares your local score to the
- Copyversion and opens a diff score. - Edit the diff score until you are happy.
- Click Save to Main in the review dialog to write the result back into
Scores/<Name>.mscz.
- It compares your local score to the
- Run Commit to GitHub:
- It sees
Scores/<Name>_diff.log.txt, force-pushes<Name>.mscz, and clears the marker.
- It sees
- Open
Menu: Plugins → Commit to GitHub
Behavior:
- Only runs when a score is open and saved as
Scores/<Name>.mscz. - Refuses to run on
.mscxscores and tells you to use “Save As…”.msczinstead. - First-time setup:
- Asks for a GitHub repo URL.
- Clones the repo into a temp folder and moves only the
.gitdirectory intoScores.
- On each run:
- Generates a per-run temporary
musescore-git-push-<timestamp>.batinPlugins/. - That script:
cd’s intoScores.git fetch origin.- If
Scores/<Name>_diff.log.txtexists:- Stages only
<Name>.mscz. - Commits with message
Commit from MuseScore plugin: <Name>. - Runs
git push --force. - Deletes
Scores/<Name>_diff.log.txt(marker has been consumed).
- Stages only
- Otherwise:
- Checks whether the remote is ahead for
<Name>.mscz. - If up to date, stages
<Name>.mscz, commits, and does a normalgit push. - If remote is ahead and there is no diff marker, calls into a small PowerShell helper that:
- Uses
git worktreeto check out the remote tree. - Extracts the remote
.mscz’s embedded.mscxintoScores/<Name> - Copy/<Name> - Copy.mscx. - Shows a Windows message instructing you to run “MuseScore Diff” before committing.
- Uses
- Checks whether the remote is ahead for
- The
.batis designed to be temporary and self-deletes after it runs.
- Generates a per-run temporary
Menu: Plugins → Pull from GitHub
Behavior:
- Only runs when a score is open and saved as
Scores/<Name>.mscz. - Validates that
Scores/.gitexists (otherwise asks you to run Commit first-time setup). - Generates a per-run temporary
musescore-git-pull-<timestamp>.batinPlugins/that:cd’s intoScores.- Runs
git fetch origin. - Tries
git pull --no-edit.- On success: shows a “Pull completed successfully” message.
- On failure (e.g. unmergeable changes):
- Aborts any merge (
git merge --abort) to keep the tree clean. - Uses
git worktreeand PowerShell to:- Check out the remote tree at
@{upstream}. - Extract remote
<Name>.mscz’s embedded.mscxintoScores/<Name> - Copy/<Name> - Copy.mscx.
- Check out the remote tree at
- Shows a Windows message telling you to run “MuseScore Diff” to resolve differences.
- Aborts any merge (
- The pull
.batalso self-deletes after it runs.
Menu: Plugins → MuseScore Diff
Purpose:
- Given a current score and a remote copy at
Scores/<Name> - Copy/<Name> - Copy.mscx, produces a visual diff, lets you edit, and then writes the result back intoScores/<Name>.mscz.
Key paths for a score Name:
- Local working copy in repo:
Scores/Name.mscz. - Extracted original for diff:
Scores/Name.mscx(unzipped from the.msczwhen needed). - Remote copy for diff:
Scores/Name - Copy/Name - Copy.mscx. - Diff output:
Scores/Name_diff.mscx. - Diff log / marker:
Scores/Name_diff.log.txt.
On run:
- Resolves
PluginsandScorespaths. - Ensures either
Scores/Name.mscxorScores/Name.msczexists.- If only
.msczexists, unzips the embedded.mscxintoScores/Name.mscx.
- If only
- Verifies that
Scores/Name - Copy/Name - Copy.mscxexists (created by Commit/Pull when the remote is ahead). - Generates a PowerShell script that:
- Loads original and copy as XML.
- Computes a measure-level diff and writes
Scores/Name_diff.mscx. - Opens the diff score in MuseScore.
- Logs details and errors into
Scores/Name_diff.log.txt.
Review and Save to Main:
- A second PowerShell script shows a Windows Forms dialog with a “Save to Main” button.
- When you click “Save to Main”:
- It sends
Ctrl+Sto MuseScore so your edits in the diff score are saved. - Loads
Scores/Name_diff.mscx, strips color markers and diff-only staff text. - Saves a cleaned temporary
.mscx. - If
Scores/Name.msczexists:- Loads
System.IO.Compression/System.IO.Compression.FileSystem. - Opens the
.msczzip in update mode and replaces the embedded.mscxentry with the cleaned diff. - Logs the update and last-write timestamps to
Scores/Name_diff.log.txt.
- Loads
- Otherwise (no
.mscz), overwritesScores/Name.mscxas a fallback. - Cleans up temporary artifacts:
Scores/Name_diff.mscx.- The cleaned
.mscxtemp file. Scores/Name.mscx(the extracted original).Scores/Name - Copy/Name - Copy.mscxand theScores/Name - Copyfolder.score_style.mssandviewsettings.jsonunder both original and copy directories, if present.
- Leaves
Scores/Name_diff.log.txtin place as the “diff completed” marker.
- It sends
Scores/Name_diff.log.txtplays two roles:- Human-readable debug log for diff and review steps.
- Machine-readable marker telling
Commit to GitHubthat you have already diffed and reconciled the score.
- When this file exists and you run
Commit to GitHubforName:- The commit script skips remote-ahead extraction and goes directly to the force-push path for
<Name>.mscz. - After the force push, the script deletes
Scores/Name_diff.log.txtso the marker is consumed.
- The commit script skips remote-ahead extraction and goes directly to the force-push path for
The following files are generated on demand and are safe to delete at any time (they are all intended to be temporary and generally self-delete):
- In
Plugins/:musescore-git-push-<timestamp>.batmusescore-git-setup-<timestamp>.ps1/.batmusescore-git-pull-<timestamp>.batmusescore-diff.ps1musescore-diff-review.ps1musescore-diff.batmusescore-diff-launch.vbs