如何将本地 Git 仓库中的所有远程分支转换为本地跟踪分支

3
如何将本地git存储库中的所有远程分支转换为本地跟踪分支,而不需要一个一个检查每个分支。你可能想要这样做的原因之一(我想要这样做的原因)是,这样您就可以复制本地存储库,并在新的克隆版本中拥有来自原始远程源的所有分支。因为“clone”只克隆本地分支。 编辑:已提供了几个脚本答案(感谢!)...我真的希望有一种git内部的方法,这样它就是完全可移植的(我有使用“仅限Windows”的用户,到目前为止他们还没有使用过bash(git-bash或其他方式)。)

你可能想将“第一个”克隆设置为镜像,而不是常规的工作存储库,以便重新克隆。 镜像类似于裸克隆,但它直接从远程复制所有引用。 (更准确地说,它将fetch refspec设置为+refs / *:refs / *,而不是例如+refs / heads / *:refs / remotes / origin / *。) - torek
嗯 - 当地的开发人员(在这种情况下从镜像克隆)将向这个镜像推送回去,这是“正确”的吗?(我不太清楚“镜像”是什么,但我有一些模糊的想法,它是只读的) - GreenAsJade
这完全取决于你想如何处理事情,但请注意,你不必将推送到与拉取相同的URL。例如,每个仓库的remote.origin.url都有一个对应的remote.origin.pushurl。如果你希望镜像 - 让我们称之为M(表示镜像),而“主”仓库为P,开发人员克隆为D - 成为“纯镜像”,那么是的,你不希望从D推送到M,因为当MP获取时,它们将会丢失;所以D应该推送到P。你可以设置一个预接收钩子来告诉M:“拒绝,改为推送到P”。 - torek
谢谢您的建议。实际上,我希望开发人员将代码推送到 M 分支,并将其用作本地“origin”。我想在发布时偶尔将代码推送到 P 分支,并且如果有其他地方的远程开发人员将代码推送到 P 分支,我也会偶尔从那里获取代码。通常当工具不能轻松完成某些操作时,这是因为这样做不是一个好主意。这是一个不好的主意吗? - GreenAsJade
它看起来有些混乱,但是 可以 做到,需要一些注意和修改:(1) 将 M 设置为镜像,但不要自动从 P 更新它(这样它就可以积累开发人员所做的更改);(2) 使用第二个系统来获取来自 MP 的更改并将其合并后发送到 PM;以及 (3) 在步骤 2 的过程中将 M 设置为只读,但在其他时间允许开发人员进行读/写。至于这是个好主意还是坏主意,取决于太多其他因素而无法确定。 - torek
4个回答

5

这个答案是jast在freenode的#git频道上提供给我的:

git push . refs/remotes/origin/*:refs/heads/*

注意:如下评论所述,此操作不会创建跟踪分支,但至少会使本地仓库中的分支成为“本地”而非“远程”。

1
我本以为这会起作用,但我发现生成的分支并不是跟踪分支。 - cforbish
这里有一个愚蠢的问题:你怎么知道他们没有跟踪分支?在我执行了上述命令之后,分支的行为就是我想要的... - GreenAsJade
他们没有追踪,因为git pull在之后不会自动将本地分支指向新的远程分支位置。此外,单独的git push也无法知道要推送到哪个远程分支。 - cforbish
是的,我明白。有没有一种方法可以在不实际尝试推送的情况下判断分支是否正在跟踪? - GreenAsJade
1
你可以运行 git config branch.${branch}.remote。如果它返回空,那么它没有进行跟踪。如果它返回一些东西(通常是 origin),那么它有一个远程对应分支。另外,我已经回答了你最初的问题。这将设置远程跟踪分支。 - cforbish
是的,感谢您的回答。正如我在问题中编辑的那样,我一直在寻找一个在Git中的解决方案。现在我大约认为没有这样的解决方案,这让我对我的原始目标产生了疑问... - GreenAsJade

4

最好的方法可能是使用脚本:

#!/bin/bash
IFS=$'\n'
for branch in `git branch -r`; do
    if [[ ${branch} =~ ^\ *(.+)/(.+)$ ]]; then
        git show-branch "${BASH_REMATCH[2]}" > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            git branch ${BASH_REMATCH[2]} ${BASH_REMATCH[1]}/${BASH_REMATCH[2]}
        fi
    fi
done

那些在这里询问如何做到这一点的人并不太可能知道,但是这段代码假定多部分分支名称是远程的,这并不一定正确。 - jthill

2
我认为@cforbish的回答只有在说出应该使用这样的脚本来生成命令时才能得到改进:
# git branch <local-branch-name> <remote-name>/<remote-branch-name>

例如,如果您有以下远程分支:
# git remote -v
  remote-repo <repo-directory> (fetch)
  remote-repo <repo-directory> (push)
# git branch -r
  remote-repo/branch1
  remote-repo/branch2
  remote-repo/branch3

您可以通过运行以下命令来获取本地跟踪分支:

git branch -r

# git branch branch1 remote-repo/branch1
# git branch branch2 remote-repo/branch2
# git branch branch3 remote-repo/branch3
# git branch
  branch1
  branch2
  branch3

1
我喜欢使用shell命令生成器来处理这种事情。虽然这比之前的版本更丑陋,但它也可以在基本的shell上运行,并且具有获取其构建的分支命令中参数的正确顺序的额外优势,以便它们实际起作用。
还有一件事 - 像这样的脚本片段确实是“内置解决方案”。
git-track-all-remote-branches () 
{ 
    awk '
     $0=="////"{doneloading=1;next}
     !doneloading {drop[$0]=1;next}
     !drop[$0] {
            print "b='\''"$0"'\''; git branch -t ${b##*/} $b"
     }'  <<///EOD///
$(git for-each-ref --format="%(upstream:short)" refs/heads)
////
$(git for-each-ref --format="%(refname:short)" refs/remotes)
///EOD///

}

一个相当新的 checkout 特性是,如果你检出一个裸名字,它目前不是一个分支,但恰好匹配一个远程分支,它会自动为其设置跟踪分支:

$ git branch
  master
$ git branch -r
  origin/notyet
  origin/master
$ git checkout notyet
Checking out files: 100% (2/2), done.
Branch notyet set up to track remote branch notyet from origin.
Switched to a new branch 'notyet'

寻找git内置解决方案的一个原因是它更具可移植性 - 我知道大多数情况下git用户都有可用的bash shell,但我正在与一支团队合作,这对他们来说可能会比较困难。出于同样的原因,我正在尝试避免看起来令人生畏的脚本 :) Git在检出时设置跟踪分支非常好...但我希望不必检出每个分支,只需将其设为本地和跟踪即可! - GreenAsJade
我数不清楚那些简单编码也许有用的五行代码的数量。Git开发人员追求的是性价比高的功能,其中性价比可以是任何东西,但一个评级是“广泛使用*节省时间”。这个很容易实现,但使用范围不广泛。上述代码也许只需要我十分钟就能实现,你只需要一天的时间就可以达到熟练甚至流畅的awk编程水平,并且你将能够在比寻求帮助更少的时间内满足你的下一个需求。我会让它看起来更美观。 - jthill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接