There is no way to add a commit to an alternate branch with git commit. There are ways of using the low level “plumbing” commands to do exactly what you described, but the interface formed by those commands is not designed for interactive use1. There are certainly ways to do what you want; depending on the details of your changes and the contents of the branches involved it can be quite simple.
The Easy Case: Just Use git checkout
When switching branches, git checkout will preserve uncommitted modifications or refuse to switch (unless you use --force
, --merge
, or --conflict
). So, as long as your uncommitted changes only touch files that are the same in both HEAD (the current branch) and your destination branch, git checkout will leave those changes in the index and/or the working tree while switching branches. If your uncommitted changes satisfy this condition then you can do this:
git checkout safe-branch
git add -- files-with-safe-changes
git commit
git checkout -
You can also use git add --patch
to stage and commit only some of the changes in the files.
After this, your “safe changes” will be a part of ‘safe‑branch’; switching back to your original branch will “leave them behind” (remember, git checkout only preserves uncommitted changes when switching branches).
If your other changes depend on the “safe changes” you may need to merge ‘safe‑branch’ into your working branch (or, depending on your workflow, rebase your working branch onto the new tip of ‘safe‑branch’). To do this you will have to stash your uncommitted changes (since both merge and rebase will refuse to function if there are uncommitted changes).
git stash save
git merge safe-branch
git stash pop --index
If your other changes do not depend on the “safe changes”, then you should probably not
bother with a merge or rebase. Eventually you will merge these branches together (e.g. by merging them both into a ‘qa’ branch for pre-release testing), but there is no reason to merge them prematurely.
Still Easy, but a Bit Risky: git checkout -m
If the first git checkout complains “You have local changes to some‑file
; not switching branches.”, it means you have uncomitted changes to some-file
and that the file is different in the tips of ‘safe‑branch’ and your current branch; you will need a different approach.
If you are confident that the changes would apply cleanly to the version of some‑file
that is in ‘safe‑branch’, then you can use the -m
/--merge
option to tell git checkout to try to adapt the changes so that they apply to the files in ‘safe‑branch’. If the merge can not be done cleanly, then you will end up with a merge conflict and it may be difficult to recover your original changes (this is why I call it “risky”).
Safe: git stash
+ git checkout -m
Since you really only want to move a subset of the changes back to ‘safe‑branch’, it may be better to focus on just those changes. One method is to use git stash to temporarily save your current changes so you do not have to drag all of them back to the ‘safe‑branch’ (and later drag some/most of them back to your working branch).
git stash save
git checkout stash -- files-with-save-changes
git checkout -m safe-branch
git commit
git checkout -
git stash pop --index
Other variations are possible. You can use git checkout -p stash -- files
to pick out only some of the changes in those files. If there are no staged changes in the index, then you could first stage the “safe changes”, git add -- files
(again, optionally with -p
) , use git stash save --keep-index
, switch branches (with merge), and then commit (i.e. replace the git checkout stash -- files
with pre-staged “safe changes” and git stash --keep-index
).
In this situation I consider git checkout -m
to be safe because we used git stash to preserve a copy of the current changes; if the three-way merge attempt results in a hopeless mess, then you can easily abandon the idea of putting the “safe changes” on ‘safe‑branch’ and get back to work: switch back to your original branch and pop the stash (git checkout -f - && git stash pop
).
Again, if your other changes depend on the “safe changes” then you will need to merge or rebase. You might as well do this before popping the stash (since you need a clean index and working tree to do the merge/rebase).
If you are not going to merge your working branch with (or rebase it onto) the ‘safe‑branch’ right away, then you will probably want to undo the “safe changes” after you pop the stash (the “safe changes” were saved in the original stash and you probably do not want to end up with commits that make the same changes from scratch in two different branches2). Once you have popped the stash, use git checkout -- files-with-safe-changes
to revert those files back to versions at the tip of the working branch.
1
The “plumbing” interface is designed for use in scripts. It would be cumbersome to use them directly on the command line. Early versions of git commit (and most other Git commands) were shell scripts that were based on this interface. They could still be written as shell scripts today, but the C versions are generally much faster.
The steps required to commit to an alternate branch are:
setup an alternate index based on the tree at the tip of the “safe branch”,
update the index with the “safe” changes (what if the changes can not be applied cleanly? it is nice to have a working tree to let the user resolve the conflicts),
write the index out as a tree object,
make a new commit object that points to the new tree and has the current tip of “safe branch” as its parent,
update the “safe branch” ref to point to the new commit.
2
There is nothing technically wrong with committing your “safe changes” in two branches, but it is usually a good idea to make sure each change originates from only a single place.