“git pull --all”可以更新我所有的本地分支吗?

Posted

技术标签:

【中文标题】“git pull --all”可以更新我所有的本地分支吗?【英文标题】:Can "git pull --all" update all my local branches? 【发布时间】:2011-05-18 02:50:29 【问题描述】:

我经常有至少 3 个远程分支:master、staging 和 production。我有 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”,但我无法让它工作。它似乎做了一个“fetch --all”,然后更新(快进或合并)当前工作分支,而不是其他本地分支。

我仍然无法手动切换到每个本地分支并进行更新。

【问题讨论】:

您是否希望仅在快进情况下自动更新本地跟踪分支? Ypu 应该,因为合并可能有你必须解决的冲突...... 假设保守的咨询时间为 300 美元来解决这个问题,使用 77,476 的浏览次数,这一问题已花费公司 23,242,800 美元。现在考虑这个问题***.com/questions/179123/… 和所有其他问题。哇。 @Luke 你是我听到的第一个指出试图让 git 做我们想做的事情花费公司金钱的人。这些简单的事情应该是自动的,应该很简单,我不必打开浏览器来阅读论坛,IMO。 @LukePuplett 与 Mercurial 相比,关于 SO 上的 git 的问题几乎是大约 9 倍,而前者的大部分似乎是“我如何在 git 中执行 ?”。这表明 git 要么设计不良、文档记录不完善、不直观,要么三者兼而有之。 @IanKemp 我不确定在不了解 SO 的人口统计数据的情况下提出该声明是否安全。如果 Mercurial 在这里不常用,或者如果它的用户使用其他论坛来询问它,我希望看到相同的结果。 :) 与 Assembly 相比,关于 javascript 的问题多出约 51 倍——因此仅通过这些指标来判断工具可能并不总是准确的。 【参考方案1】:

不知道这是否好的,但如果我想快进多个分支,我通常会调用

git pull origin master staging production

另外,如果我想推送多个分支,我会打电话

git push origin master staging production

但只有在所有提到的分支都不需要任何类型的合并时,两者才有效。

【讨论】:

【参考方案2】:

你不能只用一个 git 命令来完成它,但你可以用一个 bash 行来自动化它。

为了安全地用一行更新所有分支,我这样做:

git fetch --all && for branch in $(git branch | sed '/*/$q;h;d;$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done

如果它不能快进一个分支或遇到错误,它将停止并将您留在该分支中,以便您可以收回控制权并手动合并。

如果所有分支都可以快进,它将以您当前所在的分支结束,让您留在更新前的位置。

解释:

为了更好的可读性,它可以分成几行:

git fetch --all && \
for branch in $(git branch | sed '/*/$q;h;d;$G' | tr -d '*')
    do git checkout $branch && \
    git merge --ff-only || break
done

    git fetch --all && ... => 从所有远程获取所有引用,如果没有错误则继续执行下一个命令。

    git branch | sed '/*/$q;h;d;$G' | tr -d '*' => 从git branchsed 的输出中取出带有* 的行并将其移至末尾(这样当前分支将最后更新)。然后tr 只需删除*

    for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done = > 对于从上一条命令中获得的每个分支名称,签出该分支并尝试以快进方式合并。如果失败,则调用break,命令在此处停止。

当然,如果你想要的话,你可以用git rebase替换git merge --ff-only

最后,你可以把它作为别名放在你的bashrc中:

alias git-pull-all='git fetch --all && for branch in $(git branch | sed '\''/*/$q;h;d;$G'\'' | tr -d "*") ; do git checkout $branch && git merge --ff-only || break ; done'

或者,如果您害怕弄乱 ' 和 ",或者您只是希望在编辑器中保持语法可读性,您可以将其声明为函数:

git-pull-all()

    git fetch --all && for branch in $(git branch | sed '/*/$q;h;d;$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done

奖金:

对于那些想要sed '/*/$q;h;d;$G' 部分的解释的人:

/*/ => 搜索带有* 的行。

$q => 如果在最后一行,退出(我们不需要做任何事情,因为当前分支已经是列表中的最后一个)。

;h;d => 否则,将该行存储在保持缓冲区中并在当前列表位置删除它。

;$G => 当到达最后一行时,追加保持缓冲区的内容。

【讨论】:

你可以通过在脚本顶部设置set -e来避免无穷无尽的行和&&的所有疯狂。【参考方案3】:

“git pull --all”可以更新我所有的本地分支吗?

不,它不能。对于快进,我只是写了一个小工具来做到这一点。 https://github.com/changyuheng/git-fast-forward-all

此工具的优点:

    在一个存储库中支持多个遥控器。 (hub sync 目前不支持多个遥控器。) 支持在本地分支和对应的远程跟踪分支上使用不同的名称。 比其他为每个分支获取远程的脚本要快得多。 没有容易出错的正则表达式解析/编辑。

【讨论】:

您可以使用git fetch . refspec避免访问网络。 . 表示从当前存储库中获取而不是从远程存储库中获取。【参考方案4】:

事实上,使用 git version 1.8.3.1,它可以工作:

[root@test test]# git br
* master
  release/0.1
  update
[root@test test]# git pull --rebase
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From http://xxx/scm/csdx/test-git
   d32ca6d..2caa393  release/0.1 -> origin/release/0.1
Current branch master is up to date.
[root@test test]# git --version
git version 1.8.3.1

在 master 分支中,您可以更新所有其他分支。 @Cascabel

我不知道哪个版本破坏/修复它,在 2.17(我使用的)中,它可以工作。

【讨论】:

【参考方案5】:

我遇到了同样的问题...

想知道自己,我在.bashrc 文件中做了一个小别名函数:

gitPullAll() 
    for branch in `git branch | sed -E 's/^\*/ /' | awk 'print $1'`; do
        git checkout $branch
        git pull -p
        printf "\n"
    done
    echo "Done"

为我工作(:

【讨论】:

【参考方案6】:

如果可能,以下单行将所有具有上游分支的分支快进,否则打印错误:

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

它是如何工作的?

它使用git branch 命令的自定义格式。对于每个具有上游分支的分支,它会打印一行具有以下模式:

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

这可以直接通过管道传递到sh(假设分支名称格式正确)。省略 | sh 以查看它在做什么。

注意事项

one-liner 不会联系您的遥控器。在运行之前发出git fetchgit fetch --all

当前签出的分支不会更新类似消息

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

为此,您可以求助于普通的git pull --ff-only

别名

将以下内容添加到您的.gitconfig,以便git fft 执行此命令:

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

另见我的.gitconfig。别名是“快进跟踪(分支)”的简写。

【讨论】:

这是一个不错的解决方案,不过我想我会使用the hub soluction propsed by @John 以获得更好的输出。 这快速、简单,而且确实有效!我对git push 感到困惑,因为它与您的预期完全相反。秘诀是什么? @BrandonLWhite:我不明白这个问题。你对git push 有什么期望? git push 具有上传语义——我在本地有一些提交,我想向上游发送。 git pull 具有下载语义——我想将一些上游远程提交放到我的本地分支中。由于我们正在讨论从远程下载新提交到本地,git pull 是显而易见的选择。但是不,这个技巧使用git pushgit push 如何将远程更改拉到我的本地分支?! git push 也可用于更新本地分支,只要这是快进更新即可。【参考方案7】:

我使用hub 的sync subcommand 来自动执行此操作。我的.bash_profile 中有alias git=hub,所以我输入的命令是:

git sync

这会更新所有具有匹配上游分支的本地分支。从手册页:

如果本地分支过时,快进; 如果本地分支包含未推送的工作,请发出警告; 如果分支似乎已合并并且其上游分支已被删除,请将其删除。

它还处理当前分支上未提交的更改的存储/取消存储。

我曾经使用过类似的工具 git-up,但它不再维护,而 git sync 几乎完全相同。

【讨论】:

@TrentonD.Adams 提交日期和作者日期是不同的概念。变基将更改提交日期,但不会更改作者日期(冲突除外,其中作者日期也会更改)。作者日期反映了提交树的创作时间,并且在无冲突的变基期间不应更改。提交日期会发生变化,因为 rebase 总是会创建一个新的提交。所以提交日期将始终按正确的顺序排列。 要关闭 git-up 的自动变基行为,运行git config --global git-up.rebase.auto false @MaxYankov 通常要避免重新设置共享历史记录,在拉取期间重新设置本地提交并没有错。 重新定位本地提交正在重写历史并使其比实际更简单。使用 rebase,您可能会发现自己的代码自动合并但无法编译,或者更糟糕的是,编译但无法工作。合并承认您的工作方式:您在合并其他人的更改之前实现了更改并测试了它们,合并提交是一个非常有用的点:您可以确保不同的 chageset 可以很好地协同工作。变基使得这个过程看起来永远不会发生,这根本不是真的,是一种非常危险的做法。 请注意以下描述:“这个项目不再维护,有几个原因:我已经停止使用与我相关的工作流。Git 2.0 更新了默认行为以删除主要它正在解决的问题(通过更改 git push 的默认行为,使其仅作用于当前分支,而不是所有分支)。从 Git 2.9 开始,git pull --rebase --autostash 的作用基本相同。”【参考方案8】:

我知道这个问题已经快 3 年了,但我问自己同样的问题,但没有找到任何现成的解决方案。所以,我自己创建了一个自定义的 git 命令 shell 脚本。

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

    它发出git remote update 来获取最新的转速 然后使用git remote show 获取跟踪远程分支的本地分支列表(例如,可以与git pull 一起使用的分支) 然后它使用git rev-list --count &lt;REMOTE_BRANCH&gt;..&lt;LOCAL_BRANCH&gt; 检查本地分支在远程之后有多少提交(反之亦然) 如果本地分支提前 1 次或多次提交,它可以不能快进,需要手动合并或重新定位 如果本地分支是 0 次前提交和 1 次或更多次提交,可以通过git branch -f &lt;LOCAL_BRANCH&gt; -t &lt;REMOTE_BRANCH&gt; 快进

脚本可以这样调用:

$ 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

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

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

【讨论】:

感谢您提供此脚本。是否有人可以将该脚本转换为 Windows 批处理? @Saariko 为什么你不想在普通的 Windows shell 上使用 git?如果你使用像 cygwin 这样的脚本应该可以正常工作......(虽然我没有测试过) @RyanWilcox 谢谢,我每天(工作日)都在使用它... ;-) 您可能想查看我的点文件以获取更多与 git 相关的脚本和别名: github.com/muhqu/dotfiles @muhqu 我正在尝试使用您的脚本,但我不知道为什么它第一次可以正常工作,但现在无法“按预期”工作。例如,看看this。为什么我运行你的脚本后 master 仍然落后 78 次提交? @muhqu 在较新的 git 版本上,-t 和 -l 不应该在一个 git branch 调用中一起使用。我删除了 -l 以将调用更改为 git branch -f $LB -t $ARB &gt;/dev/null;,现在脚本可以正常工作了。【参考方案9】:

可以使用下面的脚本来完成...它会首先获取所有分支并逐个结帐并自行更新。

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

【讨论】:

添加一些解释来给你的答案打分【参考方案10】:

只是发布一个更新的答案。 git-up 不再维护,如果您阅读文档,他们会看到mention the functionality is now available in git。

从 Git 2.9 开始, git pull --rebase --autostash 的作用基本相同。

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

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

您也可以从 Git 2.9 开始为每个 git pull 设置此设置(感谢@VonC,请查看他的回答 here)

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

【讨论】:

您不需要别名。一个简单的 git pull 就足够了,配置正确:***.com/a/40067353/6309 非常感谢 @VonC 我更新了我的答案 :) 也可能会向 git-up 文档提交 PR,因为他们没有提到这一点 这不会一次更新所有本地分支,这也是我主要使用git-up的原因。 文档更新于git-up :)【参考方案11】:

这个问题还没有解决(还),至少不容易/没有脚本:请参阅 Junio C Hamano 在 git 邮件列表上的 this post 解释情况并提供简单解决方案的呼吁。

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

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

这意味着我们需要为用户提供方便的唯一情况 是处理这些本地分支,当你这样做时“跟踪”远程分支 进行本地更改,或者当您计划进行一些更改时。

如果您确实对标记为跟踪删除的“dev”进行了本地更改 “dev”,如果您在与“dev”不同的分支上,那么我们不应该 在“git fetch”更新远程跟踪“dev”之后执行任何操作。它 反正不会快进

解决方案的调用是使用一个选项或外部脚本来修剪现在远程跟踪分支之后的本地分支,而不是通过快速转发使它们保持最新,例如要求原始海报。

那么迭代的“git branch --prune --remote=&lt;upstream&gt;”怎么样 本地分支机构,如果

(1) 不是当前分支;和 (2) 它被标记为跟踪从 获取的一些分支;和 (3) 它自己没有任何提交;

然后删除那个分支? “git remote --prune-local-forks &lt;upstream&gt;”是 也很好;我不关心哪个命令实现的功能 很多。

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

【讨论】:

不要只发布链接,发布实际内容,使用链接作为参考。该链接现在已失效。太糟糕了,听起来很有希望。 (我知道这个答案是 2009 年的,所以这只是一个注释,以供将来参考。) 谢谢(哇,这么多年后的快速响应)。我现在看到这个帖子是“呼吁一个简单的解决方案”,而不是我最初的误读,“提供一个简单的解决方案”。 @michael_n: 展开...嗯,现在我看到这篇文章并不完全是关于请求的解决方案,而是关于问题(假设 XY 问题案例)。 嗯,用分离的头窥视应该更容易,特别是它应该在状态中显示有用的信息,并允许通过一些反馈(如拉取的提交)快进工作区。然后它将成为只读本地分支的替代品。【参考方案12】:

从 git 2.9 开始:

git pull --rebase --autostash

见https://git-scm.com/docs/git-rebase

在操作开始之前自动创建一个临时存储, 并在操作结束后应用。这意味着您可以运行 在脏工作树上变基。但是,请小心使用:最后的藏匿处 成功变基后的应用程序可能会导致不平凡 冲突。

【讨论】:

【参考方案13】:

如果您使用的是 Windows,则可以使用 PyGitUp,它是 Python 的 git-up 的克隆。您可以使用pip 和pip install --user git-up 或通过Scoop 使用scoop install git-up 安装它

[

【讨论】:

【参考方案14】:

看起来很多其他人都贡献了类似的解决方案,但我想我会分享我的想法并邀请其他人做出贡献。该解决方案具有漂亮的彩色输出,优雅地处理您当前的工作目录,并且速度很快,因为它不进行任何检查,并且使您的工作目录保持完整。此外,它只是一个 shell 脚本,除了 git 之外没有其他依赖项。 (目前只在 OSX 上测试过)

#!/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 "$YELLOWORKING$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 "$GREENOK$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 "$GREENUPDATED$NC\t\t$branch"
        elif [ "$REMOTE" = "$BASE" ]; then
            echo -e "$REDAHEAD$NC\t\t$branch"
        else
            echo -e "$REDDIVERGED$NC\t\t$branch"
        fi
    else
        echo -e "$REDNO REMOTE$NC\t$branch"
    fi
done

https://github.com/davestimpert/gitup

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

【讨论】:

你是写这篇文章的人吗?如果是这样,请披露您的隶属关系,即告诉我们您与它的关系。请read more on this了解更多信息。特别是不要说 - 显示!;告诉我们脚本的哪些部分以及它如何/为什么解决了问题。 是的,我写的。我已经包含了上面的源代码,以便快速复制粘贴到您的 .bashrc 或 .zshrc 中。【参考方案15】:

这是一个很好的答案:How to fetch all git branches

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

【讨论】:

你为什么建议做git fetchgit pull,而不是git pull 谢谢。似乎拉取了所有遥控器的所有分支。改了 这将获取所有远程,但它只会合并当前分支。如果您有 10 个遥控器,则需要手动签出每个遥控器并合并。 这样做会在本地创建所有带有origin/前缀的远程分支【参考方案16】:

我为GitBash 编写的脚本。完成以下任务:

默认情况下,所有设置为跟踪原点的分支都从原点提取,允许您根据需要指定不同的远程。 如果您当前的分支处于脏状态,那么它会隐藏您的更改并在最后尝试恢复这些更改。 对于设置为跟踪远程分支的每个本地分支将: git checkout branch git pull origin 最后,会让您回到原来的分支并恢复状态。

** 我使用这个,但没有彻底测试,使用风险自负。在 .bash_alias 文件 here 中查看此脚本的示例。

    # 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 bat文件? @Jaffy 不知道我手头有多少时间,而且我不是很流利,但我可以试一试。我会发布我的进度here,也许其他人可以介入并提供帮助?【参考方案17】:

一个稍微不同的脚本,它只快进名称与上游分支匹配的分支。如果可以进行快进,它还会更新当前分支。

通过运行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;

【讨论】:

【参考方案18】:

这里有很多答案,但没有一个使用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 的手册页:

   <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 &lt;remote&gt; &lt;ref&gt;:&lt;ref&gt;(不带任何+),我们得到一个仅在可以快进时更新本地引用的获取。

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

【讨论】:

"只有在可以快进的情况下才更新分支"——快进的意义是什么?如果我想要所有分支中的最新资源,那么我为什么要关心快速转发?像这样的事情让我嘲笑 Git 和它的 Fanboi。您不能仅通过一个命令来执行此操作。相反,您需要执行 c*n 步骤(而不是 1 个),其中 c 是一些重复命令的数量,n 是分支的数量。 @jww 当它是世界上大多数人使用的 VCS 时,“嘲笑 Git 和它的 Fanboi 的”[原文如此] 于事无补。但我离题了......我认为在这种“全局拉动”脚本的上下文中,如果非当前分支存在合并冲突,则不要尝试对它们进行更改是谨慎的。 这很有帮助,谢谢。我唯一不喜欢的是它为每个远程分支(包括我不感兴趣的分支)在本地创建了一个分支,所以我将git branch -r | grep -v ' -&gt; ' | while read remotebranch 更改为git branch -r | grep -v ' -&gt; ' | grep -f &lt;(git branch | cut -c 3- | awk 'print "\\S*/"$0"$"') | while read remotebranch 以将其限制为我已经在本地拥有的分支。我还在开头添加了git fetch --prune,以便在执行任何操作之前更新远程分支列表,从而避免一些警告。【参考方案19】:

来自@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

【讨论】:

【参考方案20】:

在 Mac OS X 上将 this script 添加到 .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;
;

【讨论】:

不应该先把所有的改动都存起来,然后再恢复吗?【参考方案21】:

这里有很多可以接受的答案,但有些管道对于外行来说可能有点不透明。下面是一个可以轻松定制的更简单的示例:

$ 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

【讨论】:

【参考方案22】:

为了完成 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

【讨论】:

【参考方案23】:

这仍然不是自动的,因为我希望有一个选项 - 并且应该进行一些检查以确保这只会发生在快进更新中(这就是为什么手动执行拉取更安全! !),但除了注意事项之外,您可以:

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

无需检查即可更新本地分支的位置。

注意:您将丢失当前分支位置并将其移动到源分支所在的位置,这意味着如果您需要合并,您将丢失数据!

【讨论】:

这正是我正在寻找的解决方案。我通常不会在多个分支上进行未推送的更改,只想更新我的各个本地分支以匹配远程。这个解决方案比我通常的删除/重新签出方法要好得多! 合并为一个命令:git fetch origin other-branch:other-branch【参考方案24】:

自动化并不难:

#!/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 rebase origin/$branch 更改为git pull,以便它将从适当的跟踪分支(可能在原点)获取并根据配置确定合并或变基。 @Jefromi:我忘记了fetch。已编辑;额外的功能/修复取决于 OP。 我仍然认为您可能想要使用pull(或检查branch.&lt;branch&gt;.rebase),这样您就不会意外地重新设置设置为正常拉动(合并)的分支。跨度> 考虑使用set -e 而不是|| exit 1 使解释器在第一个错误时退出。【参考方案25】:

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

如果您想查看其他分支,则必须查看它们。是的,合并(和变基)绝对需要一个工作树,所以如果不检查其他分支就无法完成。如果你愿意,你可以将你描述的步骤包装成一个脚本/别名,不过我建议使用&amp;&amp; 加入命令,这样如果其中一个失败,它就不会尝试继续下去。

【讨论】:

如果你给出一个示例命令行,我会投赞成票。我在github上有这个问题。我在 UI 上创建了一个分支。现在我需要我的本地人来展示分支。混帐拉——全部; git branch...啊...命令:git branch -a @mariotti 取决于你想要做什么,你的评论并不清楚。你最好问一个新问题。 或@Jefromi .. 举个例子。我确实同意你的看法。 @mariotti 这个答案的重点是内置命令实际上并没有执行 OP 要求的操作,因此它们所具有的步骤顺序是必要的。可以使这些步骤自动化(例如参见约翰的回答),但必须完成。因此,如果您尝试做的事情与 OP 完全相同,那么就没有真正的例子可以给出,如果您尝试做一些不同的事情,那么您应该提出一个新问题 - 这就是 *** 的工作原理! (你的评论不清楚,但我最好的猜测是你想要的东西与这里的 OP 不同,所以是的,新问题。) 是的,有些不同。但是您的答案非常适合上下文。而且我可能不需要再问了,因为你的回答。只是:接受的答案使用 git-up,它只是 git 命令行的接口(我假设)。我希望你能在几行 git 命令中明确说明。当前的答案不是 git。【参考方案26】:

如果refs/heads/master可以快进到refs/remotes/foo/master,输出

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

应该返回 refs/heads/master 指向的 SHA1 id。有了这个,您可以编写一个脚本,自动更新所有没有应用转移提交的本地分支。

这个小 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

【讨论】:

那条评论意味着什么? 我自己没有能力编写 hillu 建议的脚本,而且我对自己的 git 知识没有足够的信心使用 git-merge-base。 恐怕我对模型的理解不够好,无法利用如此友好提供的脚本。足以让人想要改用 mercurcial。 我个人觉得 Tommi Virtanen 的文章“计算机科学家的 Git”对熟悉 git 的模型和术语很有帮助。

以上是关于“git pull --all”可以更新我所有的本地分支吗?的主要内容,如果未能解决你的问题,请参考以下文章

批量拉取所有子目录 git pull --all

git 多个工程一次更新所有工程模块的办法

在所有子目录上运行 git pull [重复]

git pull后为啥分支没有更新

git的pull和clone有啥区别

如何防止root运行git pull?