Can “git pull --all” update all my local branches?
我通常至少有3个远程分支:主分支、分段分支和生产分支。我有3个本地分支跟踪这些远程分支。
更新我所有的本地分支非常繁琐:
1 2 3 4 5 6 | git fetch --all git rebase origin/master git checkout staging git rebase origin/staging git checkout production git rebase origin/production |
我很想能做一个"吉特拉全部",但我还没能让它工作。它似乎执行"全部获取",然后更新(快进或合并)当前工作分支,但不更新其他本地分支。
我仍然坚持手动切换到每个本地分支并进行更新。
我使用hub的
1 | git sync |
。
这将更新具有匹配上游分支的所有本地分支。从手册页:
- If the local branch is outdated, fast-forward it;
- If the local branch contains unpushed work, warn about it;
- If the branch seems merged and its upstream branch was deleted, delete it.
号
它还处理当前分支上的隐藏/取消隐藏未提交的更改。
我曾经使用过一个类似的工具,叫做git up,但是它不再被维护,并且
您为
如果你想签出其他分行,你就必须签出它们。是的,合并(和重新平衡)绝对需要一个工作树,所以如果不签出其他分支,就无法完成它们。如果您愿意的话,您可以将所描述的步骤总结成脚本/别名,不过我建议您将命令与
我知道这个问题已经快3岁了,但我问自己同样的问题,没有找到任何现成的解决方案。所以,我自己创建了一个自定义的git命令shell脚本。
在这里,
脚本的调用方式如下:
1 2 3 4 5 | $ git ffwd-update Fetching origin branch bigcouch was 10 commit(s) behind of origin/bigcouch. resetting local branch to remote branch develop was 3 commit(s) behind of origin/develop. resetting local branch to remote branch master is 6 commit(s) behind and 1 commit(s) ahead of origin/master. could not be fast-forwarded |
完整的脚本应该保存为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #!/bin/bash main() { REMOTES="$@"; if [ -z"$REMOTES" ]; then REMOTES=$(git remote); fi REMOTES=$(echo"$REMOTES" | xargs -n1 echo) CLB=$(git rev-parse --abbrev-ref HEAD); echo"$REMOTES" | while read REMOTE; do git remote update $REMOTE git remote show $REMOTE -n \ | awk '/merges with remote/{print $5""$1}' \ | while read RB LB; do ARB="refs/remotes/$REMOTE/$RB"; ALB="refs/heads/$LB"; NBEHIND=$(( $(git rev-list --count $ALB..$ARB 2>/dev/null) +0)); NAHEAD=$(( $(git rev-list --count $ARB..$ALB 2>/dev/null) +0)); if ["$NBEHIND" -gt 0 ]; then if ["$NAHEAD" -gt 0 ]; then echo" branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $REMOTE/$RB. could not be fast-forwarded"; elif ["$LB" ="$CLB" ]; then echo" branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. fast-forward merge"; git merge -q $ARB; else echo" branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. resetting local branch to remote"; git branch -f $LB -t $ARB >/dev/null; fi fi done done } main $@ |
。
自动化并不难:
1 2 3 4 5 6 7 8 9 | #!/bin/sh # Usage: fetchall.sh branch ... set -x git fetch --all for branch in"$@"; do git checkout"$branch" || exit 1 git rebase"origin/$branch" || exit 1 done |
这仍然不是自动的,因为我希望有一个选项-和应该有一些检查,以确保这只能发生在快进更新(这就是为什么手动拉是更安全的!)!),但除此之外,您还可以:
1 2 | git fetch origin git update-ref refs/heads/other-branch origin/other-branch |
。
更新本地分支机构的职位而不必签出它。
注意:您将丢失当前分支位置,并将其移动到原点分支所在的位置,这意味着如果需要合并,您将丢失数据!
这个问题还没有解决,至少不容易/不需要脚本:请参阅Junio C Hamano在Git邮件列表上的这篇文章,解释情况并提供一个简单的解决方案。
主要的理由是你不应该需要这个:
With git that is not ancient (i.e. v1.5.0 or newer), there is no reason to
have local"dev" that purely track the remote anymore. If you only want
to go-look-and-see, you can check out the remote tracking branch directly
on a detached HEAD with"git checkout origin/dev ".Which means that the only cases we need to make it convenient for users
are to handle these local branches that"track" remote ones when you do
have local changes, or when you plan to have some.If you do have local changes on"dev" that is marked to track the remove
"dev", and if you are on a branch different from"dev", then we should not
do anything after"git fetch " updates the remote tracking"dev". It
won't fast forward anyway
号
对解决方案的要求是使用一个选项或外部脚本来修剪跟随现在的远程跟踪分支的本地分支,而不是像最初的海报所要求的那样通过快速转发使它们保持最新。
So how about"
git branch --prune --remote= " that iterates over
local branches, and if(1) it is not the current branch; and
(2) it is marked to track some branch taken from the; and
(3) it does not have any commits on its own;then remove that branch? "
git remote --prune-local-forks " is
also fine; I do not care about which command implements the feature that
much.
号
注:自Git 2.10起,不存在此类解决方案。注意,
这里有很多答案,但是没有一个答案使用
这里我们使用
- 不需要签出分支
- 仅当分支可以快速转发时才更新分支
- 无法快速前进时将报告
这里是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/bin/bash currentbranchref="$(git symbolic-ref HEAD 2>&-)" git branch -r | grep -v ' -> ' | while read remotebranch do # Split <remote>/<branch> into remote and branchref parts remote="${remotebranch%%/*}" branchref="refs/heads/${remotebranch#*/}" if ["$branchref" =="$currentbranchref" ] then echo"Updating current branch $branchref from $remote..." git pull --ff-only else echo"Updating non-current ref $branchref from $remote..." git fetch"$remote""$branchref:$branchref" fi done |
。
从
1 2 3 4 5 6 7 | <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. |
通过指定
注意:这假定本地和远程分支的名称相同(并且您希望跟踪所有分支),它应该真正使用有关您拥有哪些本地分支以及要跟踪哪些本地分支的信息。
这里有很多可以接受的答案,但有些管道可能对未经培训的人来说有点不透明。下面是一个更简单的例子,可以很容易地定制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ cat ~/bin/git/git-update-all #!/bin/bash # Update all local branches, checking out each branch in succession. # Eventually returns to the original branch. Use"-n" for dry-run. git_update_all() { local run br br=$(git name-rev --name-only HEAD 2>/dev/null) ["$1" ="-n" ] && shift && run=echo for x in $( git branch | cut -c3- ) ; do $run git checkout $x && $run git pull --ff-only || return 2 done [ ${#br} -gt 0 ] && $run git checkout"$br" } git_update_all"$@" |
号
如果将
1 | $ git update-all |
这里有一个很好的答案:如何获取所有git分支
1 2 | for remote in `git branch -r`; do git branch --track $remote; done git pull --all |
将此脚本添加到Mac OS X上的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # Usage: # `git-pull-all` to pull all your local branches from origin # `git-pull-all remote` to pull all your local branches from a named remote function git-pull-all() { START=$(git symbolic-ref --short -q HEAD); for branch in $(git branch | sed 's/^.//'); do git checkout $branch; git pull ${1:-origin} $branch || break; done; git checkout $START; }; function git-push-all() { git push --all ${1:-origin}; }; |
。
我为我的Gitbash写的剧本。完成以下任务:
- 默认情况下,为所有设置为跟踪原点的分支从原点提取,允许您根据需要指定不同的远程。
- 如果当前分支处于脏状态,那么它将保存您的更改,并在最后尝试恢复这些更改。
- 对于为跟踪远程分支而设置的每个本地分支,将:
- 埃多克斯1〔14〕
- 埃多克斯1〔15〕
- 最后,将返回到原始分支并恢复状态。
**我使用此产品,但未进行彻底测试,使用风险自负。请在.bash_别名文件中查看此脚本的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | # Do a pull on all branches that are tracking a remote branches, will from origin by default. # If current branch is dirty, will stash changes and reply after pull. # Usage: pullall [remoteName] alias pullall=pullAll function pullAll (){ # if -h then show help if [[ $1 == '-h' ]] then echo"Description: Pulls new changes from upstream on all branches that are tracking remotes." echo echo"Usage:" echo"- Default: pullall" echo"- Specify upstream to pull from: pullall [upstreamName]" echo"- Help: pull-all -h" else # default remote to origin remote="origin" if [ $1 !="" ] then remote=$1 fi # list all branches that are tracking remote # git branch -vv : list branches with their upstreams # grep origin : keep only items that have upstream of origin # sed"s/^.."... : remove leading * # sed"s/^"..... : remove leading white spaces # cut -d""..... : cut on spaces, take first item # cut -d splits on space, -f1 grabs first item branches=($(git branch -vv | grep $remote | sed"s/^[ *]*//" | sed"s/^[ /t]*//" | cut -d"" -f1)) # get starting branch name startingBranch=$(git rev-parse --abbrev-ref HEAD) # get starting stash size startingStashSize=$(git stash list | wc -l) echo"Saving starting branch state: $startingBranch" git stash # get the new stash size newStashSize=$(git stash list | wc -l) # for each branch in the array of remote tracking branches for branch in ${branches[*]} do echo"Switching to $branch" git checkout $branch echo"Pulling $remote" git pull $remote done echo"Switching back to $startingBranch" git checkout $startingBranch # compare before and after stash size to see if anything was stashed if ["$startingStashSize" -lt"$newStashSize" ] then echo"Restoring branch state" git stash pop fi fi } |
。
如果您在Windows上,可以使用pygitup,它是用于python的
[MGXY1(0]
只是发布一个更新的答案。
As of Git 2.9, git pull --rebase --autostash does basically the same thing.
Accordingly, if you update to Git 2.9 or later, you can use this alias instead of installing git-up:
git config --global alias.up 'pull --rebase --autostash'
号
你也可以为每台2.9吉特的
1 2 | git config --global pull.rebase true git config --global rebase.autoStash true |
号
要完成Matt Connolly的回答,这是一种更新本地分支引用的更安全的方法,可以快速转发这些引用,而无需签出分支。它不会更新无法快速转发的分支(即已分叉的分支),也不会更新当前签出的分支(因为这样工作副本也应该更新)。
1 2 3 4 5 6 7 8 9 10 11 12 13 | git fetch head="$(git symbolic-ref HEAD)" git for-each-ref --format="%(refname) %(upstream)" refs/heads | while read ref up; do if [ -n"$up" -a"$ref" !="$head" ]; then mine="$(git rev-parse"$ref")" theirs="$(git rev-parse"$up")" base="$(git merge-base"$ref""$up")" if ["$mine" !="$theirs" -a"$mine" =="$base" ]; then git update-ref"$ref""$theirs" fi fi done |
如果refs/heads/master可以快速转发到refs/remotes/foo/master,则
1 | git merge-base refs/heads/master refs/remotes/foo/master |
应返回引用/heads/master指向的sha1 id。通过这一点,您可以组合一个脚本,自动更新所有没有应用转移提交的本地分支。
这个小小的shell脚本(我称之为git can ff)说明了它是如何完成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #!/bin/sh set -x usage() { echo"usage: $(basename $0) <from-ref> <to-ref>">&2 exit 2 } [ $# -ne 2 ] && usage FROM_REF=$1 TO_REF=$2 FROM_HASH=$(git show-ref --hash $FROM_REF) TO_HASH=$(git show-ref --hash $TO_REF) BASE_HASH=$(git merge-base $FROM_REF $TO_REF) if ["$BASE_HASH" ="$FROM_HASH" -o \ "$BASE_HASH" ="$FROM_REF" ]; then exit 0 else exit 1 fi |
号
我也遇到了这个问题…
出于对它的好奇,我在我的
1 2 3 4 5 6 7 8 9 | gitPullAll() { for branch in `git branch | sed -E 's/^\*/ /' | awk '{print $1}'`; do git checkout $branch git pull -p printf" " done echo"Done" } |
。
为我工作(:
一个稍有不同的脚本,它只对名称与上游分支匹配的分支进行快速转发。如果可能,它还将更新当前分支。
通过运行
复制粘贴到文件中,chmod 755:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/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; |
。
看起来很多其他人都提供了类似的解决方案,但我想我会分享我的想法,并邀请其他人提供帮助。这个解决方案有一个漂亮的彩色输出,可以很好地处理当前的工作目录,而且速度很快,因为它不执行任何签出操作,并在tact中离开工作目录。另外,它只是一个没有依赖性的shell脚本,而不是git。(目前仅在OSX上测试)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #!/usr/bin/env bash gitup(){ RED='\033[33;31m' YELLO='\033[33;33m' GREEN='\033[33;32m' NC='\033[0m' # No Color HEAD=$(git rev-parse HEAD) CHANGED=$(git status --porcelain | wc -l) echo"Fetching..." git fetch --all --prune &>/dev/null for branch in `git for-each-ref --format='%(refname:short)' refs/heads`; do LOCAL=$(git rev-parse --quiet --verify $branch) if ["$HEAD" ="$LOCAL" ] && [ $CHANGED -gt 0 ]; then echo -e"${YELLO}WORKING${NC}\t\t$branch" elif git rev-parse --verify --quiet $branch@{u}&>/dev/null; then REMOTE=$(git rev-parse --quiet --verify $branch@{u}) BASE=$(git merge-base $branch $branch@{u}) if ["$LOCAL" ="$REMOTE" ]; then echo -e"${GREEN}OK${NC}\t\t$branch" elif ["$LOCAL" ="$BASE" ]; then if ["$HEAD" ="$LOCAL" ]; then git merge $REMOTE&>/dev/null else git branch -f $branch $REMOTE fi echo -e"${GREEN}UPDATED${NC}\t\t$branch" elif ["$REMOTE" ="$BASE" ]; then echo -e"${RED}AHEAD${NC}\t\t$branch" else echo -e"${RED}DIVERGED${NC}\t\t$branch" fi else echo -e"${RED}NO REMOTE${NC}\t$branch" fi done } |
号
https://github.com/davestimpert/gitup
抱歉,我似乎也想到了与上面的其他工具相同的名称。
可以用下面的脚本来完成…它将首先获取所有分支并逐个签出并自行更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/bin/bash git branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}""$remote"; done set -x CURRENT=`git rev-parse --abbrev-ref HEAD` git fetch --all branch_name=$(git branch | awk '{print $1""}' | grep -v '*' | xargs) for branch in $branch_name; do git checkout"$branch" || exit 1 git rebase"origin/$branch" || exit 1 git pull origin $branch|| exit 1 done git checkout"$CURRENT" || exit 1 git pull || exit 1 |
@larsmans的脚本有点改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/bin/sh set -x CURRENT=`git rev-parse --abbrev-ref HEAD` git fetch --all for branch in"$@"; do if ["$branch" -ne"$CURRENT"]; then git checkout"$branch" || exit 1 git rebase"origin/$branch" || exit 1 fi done git checkout"$CURRENT" || exit 1 git rebase"origin/$CURRENT" || exit 1 |
完成后,工作副本将从调用脚本之前的同一分支签出。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/bin/sh set -x CURRENT=`git rev-parse --abbrev-ref HEAD` git fetch --all for branch in"$@"; do if ["$branch" -ne"$CURRENT"]; then git checkout"$branch" || exit 1 git pull || exit 1 fi done git checkout"$CURRENT" || exit 1 git pull || exit 1 |
。
如果可能,下面的一行程序将快速转发具有上游分支的所有分支,否则将打印错误:
1 2 3 | git branch \ --format"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" | sh |
号它是如何工作的?
它使用带有
1 | git push . <remote-ref>:<branch> |
这可以直接通过管道传输到
一行程序不会联系到你的遥控器。在运行之前,发出
当前已签出的分支将不会用以下消息更新
1 | ! [remote rejected] origin/master -> master (branch is currently checked out) |
。
为此,您可以使用常规的
将以下内容添加到您的
1 2 | [alias] fft = !sh -c 'git branch --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" | sh' - |
另见我的
从Git 2.9开始:
埃多克斯1〔19〕
请参阅https://git-scm.com/docs/git-rebase
Automatically create a temporary stash before the operation begins,
and apply it after the operation ends. This means that you can run
rebase on a dirty worktree. However, use with care: the final stash
application after a successful rebase might result in non-trivial
conflicts.
号