Search code examples
gitgit-mergegit-pullgit-checkout

Merge, update, and pull Git branches without using checkouts


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?


Solution

  • The Short Answer

    As long as you're doing a fast-forward merge, then you can simply use

    git fetch <remote> <sourceBranch>:<destinationBranch>
    

    Examples:

    # Merge local branch foo into local branch master,
    # without having to checkout master first.
    # Here `.` means to use the local repository as the "remote":
    git fetch . foo:master
    
    # Merge remote branch origin/foo into local branch foo,
    # without having to checkout foo first:
    git fetch origin foo:foo
    

    While Amber's answer will also work in fast-forward cases, using git fetch in this way instead is a little safer than just force-moving the branch reference, since git fetch will automatically prevent accidental non-fast-forwards as long as you don't use + in the refspec.

    The Long Answer

    You cannot merge a branch B into branch A without checking out A first if it would result in a non-fast-forward merge. This is because a working copy is needed to resolve any potential conflicts.

    However, in the case of fast-forward merges, this is possible, because such merges can never result in conflicts, by definition. To do this without checking out a branch first, you can use git fetch with a refspec.

    Here's an example of updating master (disallowing non-fast-forward changes) if you have another branch feature checked out:

    git fetch upstream master:master
    

    This use-case is so common, that you'll probably want to make an alias for it in your git configuration file, like this one:

    [alias]
        sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
    

    What this alias does is the following:

    1. git checkout HEAD: this puts your working copy into a detached-head state. This is useful if you want to update master while you happen to have it checked-out. I think it was necessary to do with because otherwise the branch reference for master won't move, but I don't remember if that's really right off-the-top of my head.

    2. git fetch upstream master:master: this fast-forwards your local master to the same place as upstream/master.

    3. git checkout - checks out your previously checked-out branch (that's what the - does in this case).

    The syntax of git fetch for (non-)fast-forward merges

    If you want the fetch command to fail if the update is non-fast-forward, then you simply use a refspec of the form

    git fetch <remote> <remoteBranch>:<localBranch>
    

    If you want to allow non-fast-forward updates, then you add a + to the front of the refspec:

    git fetch <remote> +<remoteBranch>:<localBranch>
    

    Note that you can pass your local repo as the "remote" parameter using .:

    git fetch . <sourceBranch>:<destinationBranch>
    

    The Documentation

    From the git fetch documentation that explains this syntax (emphasis mine):

    <refspec>

    The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>, followed by a colon :, followed by the destination ref <dst>.

    The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref that matches it is fast-forwarded using <src>. If the optional plus + is used, the local ref is updated even if it does not result in a fast-forward update.

    See Also

    1. Git checkout and merge without touching working tree

    2. Merging without changing the working directory