繁体   English   中英

Git 如何同时在 repo 和子模块上结帐?

[英]How to Git checkout on repo and submodules simultaneously?

我目前正在开发一个带有自己文件的存储库,以及两个包含文件的子模块。 我希望能够同时检查所有三个分支。

在我的工作流程中,我同时在所有三个 repo 中创建和移动分支(例如,如果我开始研究一个新功能,我将在主 repo 上创建一个新功能分支,以及一个具有相同功能的新分支两个子模块的 repos 上的名称)。 重要的是要注意,这与其说是个人偏好,不如说是我工作的惯例。

每当我在主仓库上签出一个分支时(即使它使用 --recurse-submodules),子仓库都是分离的头,而不是其同名分支上的最新提交。

这甚至可能吗? 如果是这样,怎么能做到这一点?

TL;博士

它至少需要两个 shell 命令,尽管您可以将它们全部放在一行上,或者为它们创建一个 shell 或 Git 别名:

git checkout <branch>
git submodule foreach --recursive 'git checkout <branch>'

如果某些子模块有自己的子模块并且您想递归地应用 foreach,则只需要--recursive

子模块应该处于 detached-HEAD 模式。 这就是它们的工作方式:作为每个提交的一部分,超级项目存储库包含子模块正确提交的 hash ID。

也就是说,有时,要进行超级项目提交,您希望在一个或多个子模块中停止使用某个先前超级项目提交指定的提交,并开始使用其他一些提交。 为此,您必须将 submodule-controlling-Git-command 告诉git checkout该子模块中的其他一些提交。

例如,假设我们在超级项目中,我将其称为R

$ cd R
$ git status
... superproject status shows up here ...

R中,我们有dir/subm ,它是一个子模块。 如果我们运行:

$ git rev-parse :dir/subm

(注意前导冒号,这是用于访问索引的 gitrevisions 语法)我们得到存储在 Git 索引中的原始 hash ID(即R的索引,当前在我们当前的工作目录中签出,这是我们的工作树在这里)。 这就是超级项目所说的应该作为独立的 HEAD 签出的提交。 所以如果我们再运行:

$ cd dir/subm
$ git status

我们可以预期HEAD detached at hash ,其中hash是我们刚刚在上面看到的那个。 那是因为超级项目 Git 运行:

(cd dir/subm && git checkout --detach $hash)

$hash设置为 hash ID。 但是现在我们dir/subm中,我们有一个普通的 Git 存储库。 我们可以运行:

git checkout dev

或任何我们喜欢的:我们现在在子模块存储库S中,而不是超级项目存储库R 从技术上讲,我们在S工作树中。 这里将有一个名为.git的目录,或者,在现代 Git 中,一个名为.git文件包含S存储库的路径,最有可能包含在../../.git/modules/某处。 But the key is that we're in a Git repository so we can run git checkout , do work, run git add , and then run git commit . 这样,如果需要,我们可以进行全新的提交。

如果我们只需要选择一些现有的提交,那么我们可以运行git log并找到它,然后git checkout出适当的提交。 一种或另一种方式,现在我们坐在S中,我们选择或创建我们想要的提交。

现在我们只需要弹回R的工作树。 因为我们通过两次向下(进入dir/subm/ )到达这里,所以我们通过两次向上到达那里:

cd ../..

现在我们回到R ,我们运行:

git add dir/subm

(If you have an ancient Git, be very careful to leave off the trailing slash here!) This updates Git's index for R so that instead of the previous detached HEAD commit hash ID, it now contains the actual, current HEAD hash ID for S . R Git 通过运行:

(cd dir/subm; git rev-parse HEAD)

它打印出我们刚刚通过在那里工作在S中强制到位的提交的 hash ID。

由于 Git 从 Git 索引中的任何内容进行的提交,我们现在或不久的将来可以在此处运行git commit ,在R中进行的提交, This new commit will tell anyone using this repository later to have their R Git command their S Git to git checkout --detach $hash where $hash is the hash we just had our Git stuff into Git's index.

换句话说,未来的结帐仍然会在S中使用分离的 HEAD,但现在它将使用这个

现在,关于这部分:

在我的工作流程中,我同时在所有三个 repo 中创建和移动分支(例如,如果我开始研究一个新功能,我将在主 repo 上创建一个新功能分支,以及一个具有相同功能的新分支两个子模块的 repos 上的名称)。 重要的是要注意,这与其说是个人偏好,不如说是我工作的惯例。

没关系。 您可以(cd dir/subm; git checkout -b feature/xyzzy $hash)创建一个名为feature/xyzzy的新分支,并将自己放在该分支的子模块中。 超级项目 Git 不关心这一点,它只关心 - 在某些时候,当你运行git add dir/subm - HEAD解析为,hash-ID-wise,在dir/subm 是否附加了子模块HEAD并不重要。 子模块中的名称(如果有的话)与超级项目无关。

请注意,子模块S中的名称相关的,但是,当您从 go 到git push提交从S推送到其他一些 Git 时。 所以这部分几乎需要一个附加的 HEAD,带有一个分支名称。 (可以在分离的 HEAD 上开发,然后使用git push url-or-remote HEAD:refs/heads/ branch ,但这不是一种有趣的工作方式。一方面,很容易不小心忘记以这种方式正确提交 hash ID。将S放在分支名称上以在其中工作好得多。)这就是为什么您可以使用git checkout -b feature/xyzzy $hash的原因。 但是: $hash应该从哪里来? 好吧,那部分取决于你。 除了原始的 hash ID,您可以使用HEAD或一些现有的分支名称或一些现有的远程跟踪名称或任何您喜欢的名称。 Git 只需要一些提交 ID 来放入新的分支名称,如果你正在创建一个新的分支名称。

如果有一个包含正确 hash ID 的origin/feature/xyzzy名称,您甚至不必将其找出来。 请记住, S是一个普通的 Git 存储库。 所以git checkout feature/xyzzy ,而在S中,意思是“使用现有名称,但如果它不存在,则使用 DWIM 模式查找来源/任何内容”。 这里唯一的问题是您必须S子模块中运行这些命令。

让我为超级项目推荐一个post-checkout挂钩,如下所示:

#!/bin/sh

# post-checkout hook that switches submodule branches to the same branch as the superproject

prev_HEAD="$1"
new_HEAD="$2"
new_branch="$3"

if [ "$new_branch" = 1 ]; then
    branch_name=`git symbolic-ref -q HEAD`
    if test -n "$branch_name"; then
        git submodule foreach git checkout "$branch_name"
    else
        echo "WARNING: detached HEAD"
        git submodule update
    fi
fi

exit 0

对于递归结帐: git submodule foreach --recursivegit submodule update --recursive

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM