I work on a project that has 2 branches, A and B. I typically work on branch A, and merge stuff from branch B. For the merging, I would typically do:
git merge origin/branchB
However, I would also like to keep a local copy of branch B, as I may occasionally check out the branch without first merging with my branch A. For this, I would do:
git checkout branchB
git pull
git checkout branchA
Is there a way to do the above in one command, and without having to switch branch back and forth? Should I be using git update-ref
for that? How?
No, there is not. A checkout of the target branch is necessary to allow you to resolve conflicts, among other things (if Git is unable to automatically merge them).
However, if the merge is one that would be fast-forward, you don't need to check out the target branch, because you don't actually need to merge anything - all you have to do is update the branch to point to the new head ref. You can do this with
git branch -f
:Will update
branch-b
to point to the head ofbranch-a
.The
-f
option stands for--force
, which means you must be careful when using it. Don't use it unless you are sure you the merge will be fast-forward.In your case you can use
which does what you want (assuming the merge is fast-forward). If the branch can't be updated because it requires a non-fast-forward merge, then this fails safely with a message.
This form of fetch has some more useful options too:
Note that
<remote>
can be a local repository, and<sourceBranch>
can be a tracking branch. So you can update a local branch, even if it's not checked out, without accessing the network.Currently, my upstream server access is via a slow VPN, so I periodically connect,
git fetch
to update all remotes, and then disconnect. Then if, say, the remote master has changed, I can doto safely bring my local master up to date, even if I currently have some other branch checked out. No network access required.
As Amber said, fast-forward merges are the only case in which you could conceivably do this. Any other merge conceivably needs to go through the whole three-way merge, applying patches, resolving conflicts deal - and that means there need to be files around.
I happen to have a script around I use for exactly this: doing fast-forward merges without touching the work tree (unless you're merging into HEAD). It's a little long, because it's at least a bit robust - it checks to make sure that the merge would be a fast-forward, then performs it without checking out the branch, but producing the same results as if you had - you see the
diff --stat
summary of changes, and the entry in the reflog is exactly like a fast forward merge, instead of the "reset" one you get if you usebranch -f
. If you name itgit-merge-ff
and drop it in your bin directory, you can call it as a git command:git merge-ff
.P.S. If anyone sees any issues with that script, please comment! It was a write-and-forget job, but I'd be happy to improve it.
I wrote a shell function for a similar use case I encounter daily on projects. This is basically a shortcut for keeping local branches up to date with a common branch like develop before opening a PR, etc.
glmh
("git pull and merge here") will automaticallycheckout branchB
,pull
the latest, re-checkout branchA
, andmerge branchB
.Doesn't address the need to keep a local copy of branchA, but could easily be modified to do so by adding a step before checking out branchB. Something like...
For simple fast-forward merges, this skips to the commit message prompt.
For non fast-forward merges, this places your branch in the conflict resolution state (you likely need to intervene).
To setup, add to
.bashrc
or.zshrc
, etc:Usage:
You can only do this if the merge is a fast-forward. If it's not, then git needs to have the files checked out so it can merge them!
To do it for a fast-forward only:
where
<commit>
is the fetched commit, the one you want to fast-forward to. This is basically like usinggit branch -f
to move the branch, except it also records it in the reflog as if you actually did the merge.Please, please, please don't do this for something that's not a fast-forward, or you'll just be resetting your branch to the other commit. (To check, see if
git merge-base <branch> <commit>
gives the branch's SHA1.)Another, admittedly pretty brute way is to just re-create the branch:
This throws away the local outdated branch and re-creates one with the same name, so use with care ...