簡體   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