可以“git拉 – 所有”更新我所有的本地分支?

我经常有至less3个远程分支:主,分段和生产。 我有3个地方分支跟踪那些远程分支。

更新我所有的本地分行是非常繁琐的:

git fetch --all git rebase origin/master git checkout staging git rebase origin/staging git checkout production git rebase origin/production 

我希望能够做一个“git pull -all”,但是我一直没能把它运行起来。 它似乎做一个“获取 – 所有”,然后更新(快进或合并)当前工作分支,但不是其他本地分支。

我仍然坚持手动切换到每个本地分支和更新。

你所描述的pull --all行为与预期完全相同,但不一定有用。 该选项被传递给git fetch,然后从所有远程获取所有的refs,而不是仅仅需要的。 pull然后合并(或在你的情况下,重新)合适的单一分支。

如果你想看看其他分支,你将不得不检查出来。 是的,合并(和重新分配) 绝对需要一个工作树,所以他们不能检查出其他分支。 如果你愿意的话,你可以把你描述的步骤合并到一个脚本/别名中,但是我build议用&&join这些命令,这样如果其中一个失败了,它就不会尝试继续。

git-up使这个自动化。 从自述文件:

所以git pull默认合并,当它真的应该rebase 。 你可以让它自动重新绑定 ,但它仍然不会触及除了当前签出的分支之外的任何东西。 如果您正在追踪一大堆远程分支机构,那么您下次推送时就会收到非快速投诉。

一劳永逸解决:

宝石安装git-up

注意:即使我发布了自己的解决scheme,我也会推荐使用git-up ,这也是可以接受的答案。


我知道这个问题差不多3岁,但是我问自己同样的问题,没有find任何现成的解决scheme。 所以,我创build了一个自定义的git命令shell脚本。

在这里, git-ffwd-update脚本执行以下操作:

  1. 它发出一个git remote update来获取激活转速
  2. 然后使用git remote show来获取跟踪远程分支的本地分支的列表(例如,可以使用git pull分支)
  3. 那么它检查与git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>本地分支在远程后面提交多less次(反之亦然)
  4. 如果本地分公司提前一个或多个提交,则不能快速转发,需要手工合并或重新分配
  5. 如果本地分支为0提交并提交1个或多个提交,则可以通过git branch -l -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>快速转发git branch -l -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

该脚本可以这样调用:

 $ git ffwd-update Fetching origin branch bigcouch was 10 commit(s) behind of origin/bigcouch. reseting local branch to remote branch develop was 3 commit(s) behind of origin/develop. reseting local branch to remote branch master is 6 commit(s) behind and 1 commit(s) ahead of origin/master. could not be fast-forwarded 

完整的脚本应该保存为git-ffwd-update并且需要在PATH

 #!/bin/bash main() { REMOTES="$@"; if [ -z "$REMOTES" ]; then REMOTES=$(git remote); fi REMOTES=$(echo "$REMOTES" | xargs -n1 echo) CLB=$(git branch -l|awk '/^\*/{print $2}'); echo "$REMOTES" | while read REMOTE; do git remote update $REMOTE git remote show $REMOTE -n \ | awk '/merges with remote/{print $5" "$1}' \ | while read line; do RB=$(echo "$line"|cut -f1 -d" "); ARB="refs/remotes/$REMOTE/$RB"; LB=$(echo "$line"|cut -f2 -d" "); 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. reseting local branch to remote"; git branch -l -f $LB -t $ARB >/dev/null; fi fi done done } main $@ 

自动化并不难:

 #!/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 

这仍然不是自动的,因为我希望有一个选项 – 应该有一些检查,以确保这只能发生快速更新(这就是为什么手动拉一个更安全!!),但要注意的是你可以:

 git fetch origin git update-ref refs/heads/other-branch origin/other-branch 

更新您当地分行的位置,而无需查看。

注意:您将失去当前的分支位置并将其移至原始分支的位置,这意味着如果您需要合并,将会丢失数据!

这个问题还没有解决,至less不是很容易/没有脚本:在Junit C Hamano的git邮件列表上看到这篇文章解释情况,并提供一个简单的解决scheme。

主要原因是你不应该需要这个:

使用不是古代的git(即v1.5.0或更新版本),没有理由有本地的“开发”,纯粹跟踪远程了。 如果你只想去看看,你可以直接在分离的HEAD上用“ git checkout origin/dev ”检查远程跟踪分支。

这意味着我们需要为用户提供便利的唯一情况是处理这些本地分支机构,这些分支机构在进行本地更改或者计划拥有一些分支机构时会“跟踪”远程分支机构。

如果你确实在“dev”上做了本地修改,这个修改被标记为跟踪删除“dev”,如果你在一个不同于“dev”的分支上,那么在“ git fetch ”更新远程跟踪之后​​,我们不应该做任何事情。开发”。 无论如何,它不会快进

对于解决scheme的呼吁是为了select一个选项或外部脚本来修剪现在跟踪远程分支的本地分支,而不是像请求的原始海报那样通过快速转发来保持它们的最新状态。

那么怎么样“ git branch --prune --remote=<upstream> ”迭代本地分支,如果

(1)它不是现在的分支; 和
(2)它被标记为追踪从<上游>取得的一些分支; 和
(3)它本身没有任何承诺;

然后删除该分支? “ git remote --prune-local-forks <upstream> ”也很好; 我不关心哪个命令实现了这个function。

注意:从git 2.10开始,不存在这样的解决scheme。 请注意, git remote prune子命令和git fetch --prune是关于移除远程不再存在的分支的远程跟踪分支,而不是移除跟踪远程跟踪分支的本地分支(远程跟踪分支是上游科)。

这里有很多可以接受的答案,但是一些pipe道可能对于外行人来说有点不透明。 这是一个更简单的例子,可以很容易地定制:

 $ 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 "$@" 

如果你添加~/bin/git到你的PATH (假设文件是~/bin/git/git-update-all ),你可以运行:

 $ git update-all 

这是一个很好的答案: 如何获取所有的git分支

 for remote in `git branch -r`; do git branch --track $remote; done git pull --all 

这里有很多答案,但没有一个使用git-fetch直接更新本地ref,这比检查分支要简单得多,而且比git-update-ref更安全。

这里我们使用git-fetch来更新非当前分支,并为当前分支提供git pull --ff-only 。 它:

  • 不需要检查分支
  • 更新只在可以快速转发时才分支
  • 将无法快进时报告

这里是:

 #!/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 

git-fetch的manpage中:

  <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. 

通过指定git fetch <remote> <ref>:<ref> (没有任何+ ),我们得到一个fetch,只有当它可以被快速转发时才更新本地ref。

注意 :假定本地和远程分支的命名相同(并且要跟踪所有分支),它应该真正使用关于您拥有哪些本地分支以及设置哪些分支的信息。

将此脚本添加到Mac OS X上的.profile文件中:

 # 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写的脚本。 完成以下操作:

  • 默认情况下,所有设置为跟踪原点的分支从原点拉,允许您指定一个不同的遥控器,如果需要的话。
  • 如果您的当前分支处于脏状态,则会存储您的更改,并在最后尝试恢复这些更改。
  • 对于设置为跟踪远程分支的每个本地分支将:
    • git checkout branch
    • git pull origin
  • 最后,将返回到你原来的分支和恢复状态。

**我使用这个可是没有彻底testing过,使用时有风险。 在这里的.bash_alias文件中查看这个脚本的一个例子。

  # 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 } 

如果refs / heads / master可以被快速转发到refs / remotes / foo / master ,则输出

 git merge-base refs/heads/master refs/remotes/foo/master 

应该返回refs / heads / master指向的SHA1标识。 有了这个,你可以放在一起的脚本,自动更新所有没有转移提交适用于他们的本地分支机构。

这个小小的shell脚本(我把它称为git-can-ff )说明了它是如何完成的。

 #!/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 

如果你在Windows上,你可以使用PyGitUp ,它是Python的git-up的克隆。 你可以使用pip来安装它,使用pip pip install --user git-up或者通过scoop install git-up使用scoop install git-upscoop install git-up

[ 4]

为了完成Matt Connolly的答案,这是一个更安全的方法来更新可以快速转发的本地分支引用,而不必检出分支。 它不更新不能被快速转发的分支(即已经分支的分支),也不更新当前检出的分支(因为工作副本也应该被更新)。

 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 

@larsmans的脚本有点改进:

 #!/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 

这完成后,将从同一分支中检出的工作副本与脚本被调用之前一样

git pull版本:

 #!/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 

看起来很多其他人提供了类似的解决scheme,但我想我会分享我想出的内容,并邀请其他人做出贡献。 这个解决scheme有一个很好的丰富多彩的输出,优雅地处理您当前的工作目录,并且是快速的,因为它不做任何结账,并保持工作目录的机智。 另外,它只是一个没有git以外的依赖的shell脚本。 (到目前为止只在OSX上testing过)

 #!/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

对不起,我似乎也拿出了与上面的其他工具相同的名称。

只是发布更新的答案。 git-up不再被维护,如果你阅读文档,他们提到现在在git中提供了这个function 。

从Git 2.9开始,git pull –rebase –autostash的function基本相同。

因此,如果您更新到Git 2.9或更高版本,则可以使用此别名来代替安装git-up:

git config --global alias.up 'pull --rebase --autostash'

你也可以为Git 2.9的每一个git pull设置它(谢谢@VonC请在这里看到他的答案)

 git config --global pull.rebase true git config --global rebase.autoStash true 

一个稍微不同的脚本,只能快速转发名称与上游分支匹配的分支。 它也更新当前的分支,如果快进是可能的。

通过运行git branch -vv确保所有分支的上游分支都正确设置。 用git branch -u origin/yourbanchname设置上游分支

复制粘贴到文件和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; 

从git 2.9开始:

git pull --rebase --autostash

请参阅https://git-scm.com/docs/git-rebase

在操作开始之前自动创build临时存储,并在操作结束后应用它。 这意味着你可以在肮脏的工作树上运行rebase。 但是,谨慎使用:成功重新绑定后的最终隐藏应用程序可能会导致不小的冲突。