Git - Automatically fast forward all tracking branches on pull
Asked Answered
F

6

43

I've set up tracking branches with the --track option, and when I do a git pull on master, it fetches all branches to origin/branchname but doesn't merge with the local tracking branches. This is extra annoying, because if I later do a git push on master, it says that non-fast-forward updates were rejected on the tracking branches, since they weren't fast-forwarded on the initial git pull.

My question is: How do I make it so that git pull with fetch all branches and automatically fast-forward all the tracking branches?

Note: git pull used to fast-forward all my tracking branches with my GitHub repos, but now that I've set up my own repos using Gitolite, this problem is cropping up.

Fungible answered 2/1, 2011 at 9:40 Comment(6)
Can you show the exact commands that you are using? Doing a git push on your master branch should push anything except your master branch and if you haven't made any changes to master then you shouldn't be trying yo push it.Vinnie
I'm not exactly sure what your workflow is but my immediate reaction is that you don't need to create local tracking branches if you aren't doing any work on them (i.e. the case where they can be fast forwarded); the remote tracking branches (origin/*) are automatically updated and are available for your reference in any case.Vinnie
@CharlesBailey Here's an example: Multiple developers share a set of branches, and do work on all of them at least occasionally, but usually when pulling updates most of the branches can be fast forwarded.Kanaka
Note: Git 2.0 will help in insuring a pull a ff-only (fast-forward only). See my edited answer belowSalisbarry
Possible duplicate of Can "git pull --all" update all my local branches?Fleer
https://mcmap.net/q/53589/-merge-update-and-pull-git-branches-without-using-checkoutsSymmetrize
S
6

But wait:

Note: I suppose you have tracked all your remote branches as in "Track all remote git branches as local branches."


Note: Git 2.0 (Q2 2014) will introduce with commit b814da8 a config pull.ff:

pull.ff::

By default, Git does not create an extra merge commit when merging a commit that is a descendant of the current commit. Instead, the tip of the current branch is fast-forwarded.

  • When set to false, this variable tells Git to create an extra merge commit in such a case (equivalent to giving the --no-ff option from the command line).
  • When set to only, only such fast-forward merges are allowed (equivalent to giving the --ff-only option from the command line).
Salisbarry answered 2/1, 2011 at 10:8 Comment(2)
As always, thorough, reliable answer @Salisbarry but there are some confusing details. Is push.ff a typo? So setting git config --add pull.ff true will also do a ff on all tracking branches as a part of the pull, IIF it's possible? Do you happen to know how it interacts with pull --rebase and related configuration?Marcellemarcellina
@Marcellemarcellina Thank you for the feedback. I have edited the answer accordingly. I have not tested it in a rebase scenario, but I suppose it only applies to merge, not rebase.Salisbarry
I
24

Shell script that fast-forwards all branches that have their upstream branch set to the matching origin/ branch without doing any checkouts

  • it doesn't change your current branch at any time, no need to deal with working copy changes and time lost checking out

  • it only does fast-forwards, branches that cannot be fast-forwarded will show an error message and will be skipped

Make sure all your branches' upstream branches are set correctly by running git branch -vv. Set the upstream branch with git branch -u origin/yourbanchname

Copy-paste into a file and chmod 755:

#!/bin/sh

curbranch=$(git rev-parse --abbrev-ref HEAD)

for branch in $(git for-each-ref refs/heads --format="%(refname:short)"); do
        upbranch=$(git config --get branch.$branch.merge | sed 's:refs/heads/::');
        if [ "$branch" = "$upbranch" ]; then
                if [ "$branch" = "$curbranch" ]; then
                        echo Fast forwarding current branch $curbranch
                        git merge --ff-only origin/$upbranch
                else
                        echo Fast forwarding $branch with origin/$upbranch
                        git fetch . origin/$upbranch:$branch
                fi
        fi
done;
Intolerant answered 27/6, 2014 at 11:59 Comment(5)
If you use git fetch --update-head-ok then you don't need to have a special case for the current branch.Lombardi
You have to use to add a plus sign at the start like +origin/foo:foo to force updates that are not fast forwards. (The man page mentions that this might be different for git < 2.20 though.)Lombardi
I think --update-head-ok works the same as the current answer does w.r.t. fast forwards. What it changes is that it allows the command to fetch to happen if you ask it to update the current branch.Lombardi
Made a one line version to be used as a git alias (not as safe though, doesn't check the upstream name): ffa = !"for b in $(git for-each-ref refs/heads --format='%(refname:short)') ; do git config --get branch.$b.merge && git fetch . origin/$b:$b --update-head-ok ; done"Intolerant
The OP happens to me all the time... but usually I'm not working on master and the master branch is the only one that needs fast forwarding. The one line modified from the script in this answer worked great for me: git fetch . origin/master:master. This is git fetch <repository> <remote-branch>:<local-branch>.Kellerman
R
7

If you really want to fast forward all local branches that are tracking remote branches you might want to consider adding this as an alias to your ~/.gitconfig:

[alias]
    pull-all = !"for b in $(git for-each-ref refs/heads --format='%(refname)') ; do git checkout ${b#refs/heads/} ; git pull --ff-only ; done"

You can then run git pull-all, it will iterate through your local branches and run a git pull --ff-only on each.

Ragan answered 15/9, 2013 at 21:57 Comment(4)
The checkout may be undesirable because it requires updating the working tree, which may trigger updates in IDEs or anything else watching the files. To avoid that, you could use update-ref instead, as seen here: #5148037Pest
I made a naive guess that the tracking branch is origin/$local_branch: merge-all = !"for local_branch in $(git for-each-ref refs/heads --format=\"%(refname:short)\") ; do remote_branch=\"origin/$local_branch\"; local_commit=$(git rev-parse --verify \"$local_branch\"); remote_commit=$(git rev-parse --verify \"$remote_branch\"); common_ancestor=$(git merge-base $local_branch $remote_branch); if [ ! \"$common_ancestor\" = \"$local_commit\" ]; then echo \"Could not be fast-forwarded: $local_branch\" >&2; else git update-ref refs/heads/$local_branch $remote_commit $local_commit; fi done"Pest
In case edit never go through, you can replace the command as below to get back to the branch you were: pull-all = !"current_branch=$(git rev-parse --abbrev-ref HEAD); for b in $(git for-each-ref refs/heads --format='%(refname)') ; do git checkout ${b#refs/heads/} ; git pull --ff-only ; done; echo $current_branch; git checkout $current_branch"Edgy
Don't checkout, that's needlessly expensive. Don't update-ref, that's not for fast forwarding. Use git fetchMarcellemarcellina
S
6

But wait:

Note: I suppose you have tracked all your remote branches as in "Track all remote git branches as local branches."


Note: Git 2.0 (Q2 2014) will introduce with commit b814da8 a config pull.ff:

pull.ff::

By default, Git does not create an extra merge commit when merging a commit that is a descendant of the current commit. Instead, the tip of the current branch is fast-forwarded.

  • When set to false, this variable tells Git to create an extra merge commit in such a case (equivalent to giving the --no-ff option from the command line).
  • When set to only, only such fast-forward merges are allowed (equivalent to giving the --ff-only option from the command line).
Salisbarry answered 2/1, 2011 at 10:8 Comment(2)
As always, thorough, reliable answer @Salisbarry but there are some confusing details. Is push.ff a typo? So setting git config --add pull.ff true will also do a ff on all tracking branches as a part of the pull, IIF it's possible? Do you happen to know how it interacts with pull --rebase and related configuration?Marcellemarcellina
@Marcellemarcellina Thank you for the feedback. I have edited the answer accordingly. I have not tested it in a rebase scenario, but I suppose it only applies to merge, not rebase.Salisbarry
F
6

The following one-liner fast-forwards all branches that have an upstream branch if possible, and prints an error otherwise:

git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

How does it work?

It uses a custom format with the git branch command. For each branch that has an upstream branch, it prints a line with the following pattern:

git push . <remote-ref>:<branch>

This can be piped directly into sh (assuming that the branch names are well-formed). Omit the | sh to see what it's doing.

Caveat

The currently checked-out branch will not be updated with a message like

! [remote rejected] origin/master -> master (branch is currently checked out)

For this, you can resort to regular git pull --ff-only .

Alias

Add the following to your .gitconfig so that git fft performs this command:

[alias]
        fft = !sh -c 'git branch --format \"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)\" | sh' -

The alias is a shorthand to "fast-forward tracking (branches)".

Fleer answered 15/4, 2019 at 18:21 Comment(1)
Posted the same answer to the alleged duplicate: https://mcmap.net/q/13034/-can-quot-git-pull-all-quot-update-all-my-local-branches. Will delete this answer once the duplicate is identified as such.Fleer
U
1

I just wrote a small tool to do so. https://github.com/changyuheng/git-fast-forward-all

Advantages of this tool:

  1. Supports multiple remotes in one repository. (hub sync doesn't support multiple remotes at the moment.)
  2. Supports having different names on the local branch and the corresponding remote tracking branche.
  3. Much faster than other scripts that fetches remote for every single branch.
  4. No error-prone regex parsing/editing.
Uredium answered 20/11, 2019 at 10:6 Comment(0)
M
0

SmartGit, for example, has an option to automatically merge changes from the tracked branch if you switch to a branch. This should do what you want to achieve.

Merman answered 2/1, 2011 at 12:20 Comment(1)
This particular script also has the undesireable side-effect in that it changes the current branch. Is there any way to modify it to keep the current branch?Destroyer

© 2022 - 2024 — McMap. All rights reserved.