git裸存储库、工作树和跟踪分支

git裸存储库、工作树和跟踪分支,git,git-branch,git-bare,git-worktree,Git,Git Branch,Git Bare,Git Worktree,我正在使用一个代码库,在这个代码库中,为了不同的目的,我需要同时处理多个分支。因此,我克隆到一个裸存储库,然后设置一些工作树: git clone --bare ssh://git@git.example.com/project/repo repo.git cd repo.git git worktree add ../branch-1 branch-1 git worktree add ../branch-2 branch-2 ... someone else creates branch-3

我正在使用一个代码库,在这个代码库中,为了不同的目的,我需要同时处理多个分支。因此,我克隆到一个裸存储库,然后设置一些工作树:

git clone --bare ssh://git@git.example.com/project/repo repo.git
cd repo.git
git worktree add ../branch-1 branch-1
git worktree add ../branch-2 branch-2
... someone else creates branch-3 and pushes is ...
git fetch origin +refs/heads/*:refs/heads/* --prune
git worktree add ../branch-3 branch-3
现在,
branch-3
worktree没有设置为跟踪远程树,并且试图让它这样做,我陷入了可怕的混乱

$ cd ../branch-3
$ git branch -u origin/branch-3
error: the requested upstream branch 'origin/refs/heads/feature/SW-5884-move-database-container-to-alpine-base-2' does not exist
hint: ...<snip>
$ git fetch +refs/heads/*:refs/remotes/origin/* --prune
$ git branch -u origin/branch-3
fatal: Cannot setup tracking information; starting point 'origin/feature/SW-5884-move-database-container-to-alpine-base-2' is not a branch.
$cd../branch-3
$git分行-u来源/分行-3
错误:请求的上游分支“origin/refs/heads/feature/SW-5884-move-database-container-to-alpine-base-2”不存在
提示:。。。
$git fetch+refs/heads/*:refs/remotes/origin/*--prune
$git分行-u来源/分行-3
致命:无法设置跟踪信息;起点“origin/feature/SW-5884-move-database-container-to-alpine-base-2”不是分支。

什么是让这项工作正常运行的正确方法?

首先,一个旁注:如果您打算在非竞争时段(一次超过两周)使用
git worktree add
,请确保您的git至少是2.15.1版

出于您的特殊目的,我建议不要使用
git clone--bare
。取而代之的是,使用一个常规的克隆,然后是您想要执行的
git工作树add
s。您注意到:

。。。您最终必须创建一个虚拟分支来放置存储库本身,因为不可能同时在同一个分支上拥有存储库和工作树

有几种简单的解决方法:

  • 从要添加的N个工作树中选择一个,并将其用作主工作树中的分支:

    git checkout -b branch-1 ssh://git@git.example.com/project/repo branch-1
    
    缺点是您现在有一个特殊的“主”分支,您不能在任何时候删除它,所有其他分支都依赖于它

  • 或者,在克隆之后,在工作树中使用
    git checkout--detach
    ,在默认分支上获得分离的头:

    git clone ssh://git@git.example.com/project/repo repo.git
    cd repo.git
    git checkout --detach
    
  • 第二种方法的唯一缺点是工作树中充满了文件,可能是浪费空间。还有一个解决方案:使用空树创建一个空白提交,并检查:

    git clone ssh://git@git.example.com/project/repo repo.git
    cd repo.git
    git checkout $(git commit-tree $(git hash-object -t tree /dev/null) < /dev/null)
    

    (或者将
    xargs
    替换为
    xargs-n1-I{}git分支——将上游设置为=origin/{}{{}
    )。

    我已经为此挣扎了很长时间,从来都不记得我为创建一个行为不同的裸回购所采取的步骤。最后,我编写了以下脚本,名为“
    git clone bare for worktrees”
    ,以创建用于worktrees的裸repo。它似乎工作得相当好,但请注意,它没有做很多错误处理

    #/bin/env bash
    set-e
    url=$1
    name=${url##*/}
    git init--裸“${name}”
    cd“${name}”
    git config remote.origin.url“$url”
    git config remote.origin.fetch'+refs/heads/*:refs/remotes/origin/*'
    git获取
    firstCommit=$(git rev list--all--max parents=0--date order--reverse | head-n1)
    git分支裸伪$firstCommit
    git符号参考头部参考/头部/裸假人
    

    它创建一个名为
    bare dummy
    的分支,指向repo中的第一个提交,并将其设置为
    HEAD
    ,确保所有“真实”分支都可以在工作树中安全签出。除此之外,回购协议将不包含任何本地分支,甚至不包含主分支,但远程跟踪分支的创建与普通非裸克隆完全相同。因此,只需运行一个快速的
    git worktree add../master worktree master
    ,您就可以开始运行了。

    将工作树添加到
    --bare
    存储库不是一个好主意。我建议您将裸存储库保留为裸存储,并创建一个非裸克隆,在其中创建工作树。(特别是
    --bare
    更改fetch refspec的方式可能会严重影响正在进行的工作。)好的,谢谢您的建议。我以为这就是工作树的用途;如果存储库是非裸的,那么您最终必须创建一个虚拟分支来放置存储库本身,因为存储库和工作树不可能同时位于同一个分支上。你愿意把这句话写进我能接受的答案吗?这很有趣也很有用。就我个人而言,我已经创建了一个
    git make bare
    脚本,它完成了@torek在公认答案中的第三个要点。克隆时我的工作流是
    git clone;cd回购;赤裸裸;git worktree add master
    @tom,你如何处理裸repo中的HEAD?我在第一次提交的基础上创建一个虚拟分支的技术非常蹩脚,但这是迄今为止我发现的最好的方法来防止裸repo“占用”一个分支。我的
    git make bare
    基本上做到了这一点:
    git checkout$(git commit tree$(git hash object-t tree/dev/null)
    @tom,我记得我曾尝试在裸回购中使用分离的头,但如果我没记错的话,一些worktree操作会抱怨如果是这样的话(尽管我不明白为什么操作需要分支)。不过,这可能是早期版本的
    git worktree
    的结果,所以我可能应该再试一次。它对我很有效,但我只使用
    add
    prune
    。我喜欢您的第三个选项,但您知道该提交的windows等价物(使用/dev/null)吗?我使用git bash运行它,但我正在为Windows开发人员开发一个构建环境,并使用批处理文件自动执行这些命令。我不知道如何在Windows上模拟
    /dev/null
    。古代DOS有NUL:like-CON:之类的(我不确定这些拼写是否正确),也许这样行得通;在任何情况下,我被告知WIndows有一些可以工作的东西。我尝试了NUL。没用。我可以解决这个问题。当我无法运行替换NUL的命令时,我很惊讶。
    git clone --bare ssh://git@git.example.com/project/repo repo.git
    cd repo.git
    git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
    git fetch
    git for-each-ref --format='%(refname:short)' refs/heads | xargs git branch -d