[英]How to Git checkout on repo and submodules simultaneously?
I'm currently working on a repo with its own files, as well as two submodules which have files in them as well.我目前正在开发一个带有自己文件的存储库,以及两个包含文件的子模块。 I want to be able to checkout branches on all three of them at once.
我希望能够同时检查所有三个分支。
During my workflow, I create and move between branches in all three repos simultaneously (so for example if I start working on a new feature, I'll make a new feature branch on the main repo, as well as a new branch with the same name on both submodule's repos).在我的工作流程中,我同时在所有三个 repo 中创建和移动分支(例如,如果我开始研究一个新功能,我将在主 repo 上创建一个新功能分支,以及一个具有相同功能的新分支两个子模块的 repos 上的名称)。 It's important to note this is less of a personal preference and more a convention where I am working.
重要的是要注意,这与其说是个人偏好,不如说是我工作的惯例。
Whenever I checkout a branch on the main repo (Even if it's using --recurse-submodules), the subrepos are detached heads and not the latest commit on their branch of the same name.每当我在主仓库上签出一个分支时(即使它使用 --recurse-submodules),子仓库都是分离的头,而不是其同名分支上的最新提交。
Is this even possible to do?这甚至可能吗? If so, how can one do this?
如果是这样,怎么能做到这一点?
It takes at least two shell commands, though you can put them all on one line, or make a shell or Git alias for them:它至少需要两个 shell 命令,尽管您可以将它们全部放在一行上,或者为它们创建一个 shell 或 Git 别名:
git checkout <branch>
git submodule foreach --recursive 'git checkout <branch>'
You only need the --recursive
if some submodule(s) have their own submodule(s) and you want to apply the foreach recursively.如果某些子模块有自己的子模块并且您想递归地应用 foreach,则只需要
--recursive
。
Submodules are supposed to be in detached-HEAD mode.子模块应该处于 detached-HEAD 模式。 That's how they work: the superproject repository contains, as part of each commit, the hash ID of the correct commit for the submodule.
这就是它们的工作方式:作为每个提交的一部分,超级项目存储库包含子模块正确提交的 hash ID。
That said, sometimes, to make a new superproject commit, you'd like to stop using the commit specified by some previous superproject commit, and start using some other commit, in one or more of your submodules.也就是说,有时,要进行新的超级项目提交,您希望在一个或多个子模块中停止使用某个先前超级项目提交指定的提交,并开始使用其他一些提交。 To do that, you must tell a submodule-controlling-Git-command to
git checkout
some other commit in that submodule.为此,您必须将 submodule-controlling-Git-command 告诉
git checkout
该子模块中的其他一些提交。
For instance, suppose we are in the superproject, which I'll call R :例如,假设我们在超级项目中,我将其称为R :
$ cd R
$ git status
... superproject status shows up here ...
Within R , we have dir/subm
, which is a submodule.在R中,我们有
dir/subm
,它是一个子模块。 If we run:如果我们运行:
$ git rev-parse :dir/subm
(note the leading colon, which is gitrevisions syntax for accessing the index) we get the raw hash ID stored in Git's index (that is, the index for R , as currently checked out in our current working directory, which is our working tree here). (注意前导冒号,这是用于访问索引的 gitrevisions 语法)我们得到存储在 Git 索引中的原始 hash ID(即R的索引,当前在我们当前的工作目录中签出,这是我们的工作树在这里)。 That's the commit that the superproject says should be checked out, as a detached HEAD.
这就是超级项目所说的应该作为独立的 HEAD 签出的提交。 So if we then run:
所以如果我们再运行:
$ cd dir/subm
$ git status
we can expect to see HEAD detached at hash
, where the hash
is the one we just saw above.我们可以预期
HEAD detached at hash
,其中hash
是我们刚刚在上面看到的那个。 That's because the superproject Git ran:那是因为超级项目 Git 运行:
(cd dir/subm && git checkout --detach $hash)
with $hash
set to that hash ID. $hash
设置为 hash ID。 But now that we're in dir/subm
, we have a regular ordinary Git repository.但是现在我们在
dir/subm
中,我们有一个普通的 Git 存储库。 We can run:我们可以运行:
git checkout dev
or whatever we like: we're in the submodule repository S now, not the superproject repository R .或任何我们喜欢的:我们现在在子模块存储库S中,而不是超级项目存储库R 。 Technically we're in the working tree of S .
从技术上讲,我们在S的工作树中。 There will be a directory named
.git
here, or, in modern Git, a file named .git
containing the path to the S repository, contained within ../../.git/modules/
somewhere, most likely.这里将有一个名为
.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
. 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
. That way, we can make an entirely new commit, if we need to.这样,如果需要,我们可以进行全新的提交。
If we just need to pick some existing commit, well, we can run git log
and find it and git checkout
the appropriate commit.如果我们只需要选择一些现有的提交,那么我们可以运行
git log
并找到它,然后git checkout
出适当的提交。 One way or another, now that we're sitting in S , we pick out or create the commit we want.一种或另一种方式,现在我们坐在S中,我们选择或创建我们想要的提交。
Now we just have to pop back up to the working tree for R .现在我们只需要弹回R的工作树。 Since we got here by doing down twice—into
dir/subm/
—we get there by going up twice:因为我们通过两次向下(进入
dir/subm/
)到达这里,所以我们通过两次向上到达那里:
cd ../..
Now that we're back in R , we run:现在我们回到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 . (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 . The R Git got this by running:
R Git 通过运行:
(cd dir/subm; git rev-parse HEAD)
which printed out the hash ID of the commit we just forced into place in S by working there.它打印出我们刚刚通过在那里工作在S中强制到位的提交的 hash ID。
Since Git makes a new commit from whatever is in Git's index, we can now, or some time soon in the future, run git commit
here, in R , to make a new commit.由于 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. 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.
In other words, a future checkout will still use a detached HEAD in S , but now it will use this one.换句话说,未来的结帐仍然会在S中使用分离的 HEAD,但现在它将使用这个。
Now, as to this part:现在,关于这部分:
During my workflow, I create and move between branches in all three repos simultaneously (so for example if I start working on a new feature, I'll make a new feature branch on the main repo, as well as a new branch with the same name on both submodule's repos).
在我的工作流程中,我同时在所有三个 repo 中创建和移动分支(例如,如果我开始研究一个新功能,我将在主 repo 上创建一个新功能分支,以及一个具有相同功能的新分支两个子模块的 repos 上的名称)。 It's important to note this is less of a personal preference and more a convention where I am working.
重要的是要注意,这与其说是个人偏好,不如说是我工作的惯例。
That's all fine.没关系。 You can
(cd dir/subm; git checkout -b feature/xyzzy $hash)
to create a new branch named feature/xyzzy
and put yourself on that branch, in the submodules.您可以
(cd dir/subm; git checkout -b feature/xyzzy $hash)
创建一个名为feature/xyzzy
的新分支,并将自己放在该分支的子模块中。 The superproject Git doesn't care about that, it only cares—at some point, when you run git add dir/subm
—what HEAD
resolves to, hash-ID-wise, in dir/subm
at that time .超级项目 Git 不关心这一点,它只关心 - 在某些时候,当你运行
git add dir/subm
- HEAD
解析为,hash-ID-wise,在dir/subm
中。 It doesn't matter whether the submodule HEAD
is attached or not.是否附加了子模块
HEAD
并不重要。 The name —if any.—in the submodule is irrelevant to the superproject.子模块中的名称(如果有的话)与超级项目无关。
Note that the name in the submodule S is relevant, though, when you go to git push
commits from S to some other Git.请注意,子模块S中的名称是相关的,但是,当您从 go 到
git push
提交从S推送到其他一些 Git 时。 So this part almost requires an attached HEAD, with a branch name.所以这部分几乎需要一个附加的 HEAD,带有一个分支名称。 (It is possible to develop on a detached HEAD, then use
git push url-or-remote HEAD:refs/heads/ branch
, but that's not a fun way of working. For one thing, it's too easy to accidentally lose track of the right commit hash ID that way. It's much nicer to put S on a branch name to do work in it.) So that's why you might use git checkout -b feature/xyzzy $hash
. (可以在分离的 HEAD 上开发,然后使用
git push url-or-remote HEAD:refs/heads/ branch
,但这不是一种有趣的工作方式。一方面,很容易不小心忘记以这种方式正确提交 hash ID。将S放在分支名称上以在其中工作要好得多。)这就是为什么您可以使用git checkout -b feature/xyzzy $hash
的原因。 But: where should $hash
come from, here?但是:
$hash
应该从哪里来? Well, that part is up to you.好吧,那部分取决于你。 Instead of a raw hash ID, you can use
HEAD
or some existing branch name or some existing remote-tracking name or whatever you like.除了原始的 hash ID,您可以使用
HEAD
或一些现有的分支名称或一些现有的远程跟踪名称或任何您喜欢的名称。 Git just needs some commit ID to put into the new branch name, if you're creating a new branch name. Git 只需要一些提交 ID 来放入新的分支名称,如果你正在创建一个新的分支名称。
If there's an origin/feature/xyzzy
name that contains the correct hash ID, you don't even have to fish it out.如果有一个包含正确 hash ID 的
origin/feature/xyzzy
名称,您甚至不必将其找出来。 Remember, S is an ordinary Git repository.请记住, S是一个普通的 Git 存储库。 So
git checkout feature/xyzzy
, while in S , means "use the existing name, but if it doesn't exist, use the DWIM mode to look for origin/whatever".所以
git checkout feature/xyzzy
,而在S中,意思是“使用现有名称,但如果它不存在,则使用 DWIM 模式查找来源/任何内容”。 The only catch here is that you must run these commands in the S submodule.这里唯一的问题是您必须在S子模块中运行这些命令。
Let's me recommend a post-checkout
hook for the superproject like this:让我为超级项目推荐一个
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
For recursive checkout: git submodule foreach --recursive
and git submodule update --recursive
.对于递归结帐:
git submodule foreach --recursive
和git submodule update --recursive
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.