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?
This question is related to
git
git-merge
git-pull
git-checkout
For many cases (such as merging), you can just use the remote branch without having to update the local tracking branch. Adding a message in the reflog sounds like overkill and will stop it being quicker. To make it easier to recover, add the following into your git config
[core]
logallrefupdates=true
Then type
git reflog show mybranch
to see the recent history for your branch
Another, admittedly pretty brute way is to just re-create the branch:
git fetch remote
git branch -f localbranch remote/remotebranch
This throws away the local outdated branch and re-creates one with the same name, so use with care ...
If you want to keep the same tree as one of the branch you want to merge (ie. not a real "merge"), you can do it like this.
# Check if you can fast-forward
if git merge-base --is-ancestor a b; then
git update-ref refs/heads/a refs/heads/b
exit
fi
# Else, create a "merge" commit
commit="$(git commit-tree -p a -p b -m "merge b into a" "$(git show -s --pretty=format:%T b)")"
# And update the branch to point to that commit
git update-ref refs/heads/a "$commit"
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:
git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>
where <commit>
is the fetched commit, the one you want to fast-forward to. This is basically like using git 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.)
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
:
git branch -f branch-b branch-a
Will update branch-b
to point to the head of branch-a
.
The -f
option stands for --force
, which means you must be careful when using it.
Don't use it unless you are absolutely sure the merge will be fast-forward.
It is absolutely possible to do any merge, even non-fast forward merges, without git checkout
. The worktree
answer by @grego is a good hint. To expand on that:
cd local_repo
git worktree add _master_wt master
cd _master_wt
git pull origin master:master
git merge --no-ff -m "merging workbranch" my_work_branch
cd ..
git worktree remove _master_wt
You have now merged the local work branch to the local master
branch without switching your checkout.
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
You can try git worktree
to have two branches open side by side, this sounds like it might be what you want but very different than some of the other answers I've seen here.
In this way you can have two separate branches tracking in the same git repo so you only have to fetch once to get updates in both work trees (rather than having to git clone twice and git pull on each)
Worktree will create a new working directory for your code where you can have a different branch checked out simultaneously instead of swapping branches in place.
When you want to remove it you can clean up with
git worktree remove [-f] <worktree>
You can simply git pull origin branchB
into your branchA
and git will do the trick for you.
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 use branch -f
. If you name it git-merge-ff
and drop it in your bin directory, you can call it as a git command: git merge-ff
.
#!/bin/bash
_usage() {
echo "Usage: git merge-ff <branch> <committish-to-merge>" 1>&2
exit 1
}
_merge_ff() {
branch="$1"
commit="$2"
branch_orig_hash="$(git show-ref -s --verify refs/heads/$branch 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown branch $branch" 1>&2
_usage
fi
commit_orig_hash="$(git rev-parse --verify $commit 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown revision $commit" 1>&2
_usage
fi
if [ "$(git symbolic-ref HEAD)" = "refs/heads/$branch" ]; then
git merge $quiet --ff-only "$commit"
else
if [ "$(git merge-base $branch_orig_hash $commit_orig_hash)" != "$branch_orig_hash" ]; then
echo "Error: merging $commit into $branch would not be a fast-forward" 1>&2
exit 1
fi
echo "Updating ${branch_orig_hash:0:7}..${commit_orig_hash:0:7}"
if git update-ref -m "merge $commit: Fast forward" "refs/heads/$branch" "$commit_orig_hash" "$branch_orig_hash"; then
if [ -z $quiet ]; then
echo "Fast forward"
git diff --stat "$branch@{1}" "$branch"
fi
else
echo "Error: fast forward using update-ref failed" 1>&2
fi
fi
}
while getopts "q" opt; do
case $opt in
q ) quiet="-q";;
* ) ;;
esac
done
shift $((OPTIND-1))
case $# in
2 ) _merge_ff "$1" "$2";;
* ) _usage
esac
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.
In your case you can use
git fetch origin branchB:branchB
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:
git fetch <remote> <sourceBranch>:<destinationBranch>
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 do
git fetch . remotes/origin/master:master
to safely bring my local master up to date, even if I currently have some other branch checked out. No network access required.
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.
Posting this even though you don't want to use
checkout
, in case others don't mind that constraint.
glmh
("git pull and merge here") will automatically checkout branchB
, pull
the latest, re-checkout branchA
, and merge 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...
git branch ${branchA}-no-branchB ${branchA}
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).
.bashrc
or .zshrc
, etc:glmh() {
branchB=$1
[ $# -eq 0 ] && { branchB="develop" }
branchA="$(git branch | grep '*' | sed 's/* //g')"
git checkout ${branchB} && git pull
git checkout ${branchA} && git merge ${branchB}
}
# No argument given, will assume "develop"
> glmh
# Pass an argument to pull and merge a specific branch
> glmh your-other-branch
Note: This is not robust enough to hand-off of args beyond branch name to
git merge
For many GitFlow users the most useful commands are:
git fetch origin master:master --update-head-ok
git fetch origin dev:dev --update-head-ok
The --update-head-ok
flag allows using the same command while on dev
or master
branches.
A handy alias in .gitconfig
:
[alias]
f=!git fetch origin master:master --update-head-ok && git fetch origin dev:dev --update-head-ok
Another way to effectively do this is:
git fetch
git branch -d branchB
git branch -t branchB origin/branchB
Because it's a lower case -d
, it will only delete it if the data will still exist somewhere. It's similar to @kkoehne's answer except it doesn't force. Because of the -t
it will set up the remote again.
I had a slightly different need than OP, which was to create a new feature branch off develop
(or master
), after merging a pull request. That can be accomplished in a one-liner without force, but it doesn't update the local develop
branch. It's just a matter of checking out a new branch and having it be based off origin/develop
:
git checkout -b new-feature origin/develop
The question is simple and the answer should be as simple. All the OP is asking is to merge the upstream origin/branchB
into his current branch without switching branches.
TL;DR:
git fetch
git merge origin/branchB
The full answer:
git pull
does a fetch + merge. It's roughly the the same the two commands below, where <remote>
is usually origin
(default), and the remote tracking branch starts with <remote>/
followed by the remote branch name:
git fetch [<remote>]
git merge @{u}
The @{u}
notation is the configured remote tracking branch for the current branch. If branchB
tracks origin/branchB
then @{u}
from branchB
is the same as typing origin/branchB
(see git rev-parse --help
for more info).
Since you already merge with origin/branchB
, all that is missing is the git fetch
(which can run from any branch) to update that remote-tracking branch.
Note though that if there was any merge from the pull to include, you should rather merge branchB
into branchA
after having done a pull from branchB
(and eventually push the changes to orign/branchB), but as long as they're fast-forward they would remain the same.
Keep in mind the local branchB
will not be updated until you switch to it and do an actual pull, however as long as there are no local commits added to this branch it will just remain a fast-forward to the remote branch.
Enter git-forward-merge:
Without needing to checkout destination,
git-forward-merge <source> <destination>
merges source into destination branch.
https://github.com/schuyler1d/git-forward-merge
Only works for automatic merges, if there are conflicts you need to use the regular merge.
You can clone the repo and do the merge in the new repo. On the same filesystem, this will hardlink rather than copy most of the data. Finish by pulling the results into the original repo.
just to pull the master without checking out the master I use
git fetch origin master:master
Source: Stackoverflow.com