簡體   English   中英

使用子樹合並策略,歷史沒有合並

[英]Using subtree-merge strategy, history is not merging

我正在嘗試通過“子樹合並”將外部 SVN 存儲庫用作我的存儲庫中的子樹。我相信這應該保持庫中文件的歷史完整,但它不起作用 - 來自合並到我的主分支中的子樹中的庫沒有歷史記錄,但在我添加它們時提交 - 這是一個歷史記錄來說明我的意思,正是我將要達到的狀態如下。

lappy8086:YACYAML jamie$ git log --graph 
* commit 0cc6c4e5061741e67d009f3375ce1d2bcd3ab540
| Author: James Montgomerie
| Date:   Thu May 17 12:04:43 2012 +0100
| 
|     Subtree-merge in libYAML (from a git-svn checkout).
|  
* commit b5af5af109d77f6adafebc3dcf5a4796a5035a2e
Author: James Montgomerie
Date:   Thu May 17 11:47:32 2012 +0100

First commit, add .gitignore.

這就是我正在做的嘗試讓它發揮作用:

# check out SVN repo
git svn clone http://svn.pyyaml.org/libyaml/branches/stable libYAML

# create my repo
mkdir YACYAML
cd YACYAML
git init
touch .gitignore
git add .gitignore
git commit -m "First commit, add .gitignore"

# Fetch from git-svn repo I got earlier
git remote add libyaml-svn ../libYAML/
git fetch libyaml-svn
git checkout -b libyaml-svn libyaml-svn/master

# Switch back to master, and try to merge in subtree
git checkout master
git read-tree --prefix=libYAML/ -u libyaml-svn/master
git commit -m "Merge in libYAML as subtree (from git-svn checkout of SVN repo)"

這“有效”,但是,正如我所說,當我查看我的歷史記錄時,我希望從 libYAML 存儲庫中看到完整的歷史記錄,但我沒有——如上所述。

好吧,一個答案是安裝git-subtree並將其用於:

git subtree add --prefix=libYAML/ ../libYAML master

這導致我正在尋找(和預期)手動操作:

lappy8086:YACYAML jamie$ git log --graph
*   commit 453d464cfc140c798d0dea85ab667fe16250181d
|\  Merge: 9fb083d 0ca365a
| | Author: James Montgomerie 
| | Date:   Thu May 17 14:32:36 2012 +0100
| | 
| |     Add 'libYAML/' from commit '0ca365adeb5711bf918d4401e98fce00bab8b3ec'
| |     
| |     git-subtree-dir: libYAML
| |     git-subtree-mainline: 9fb083d923011dd990222da2a58eda42e5220cde
| |     git-subtree-split: 0ca365adeb5711bf918d4401e98fce00bab8b3ec
| |   
| * commit 0ca365adeb5711bf918d4401e98fce00bab8b3ec
| | Author: xi
| | Date:   Sun May 29 05:52:36 2011 +0000
| | 
| |     Bumped the version number and updated the announcement.
| |     
| |     git-svn-id: http://svn.pyyaml.org/libyaml/branches/stable@374 18f92427-320e-0410-9341-c67f048884a3
| |   
| * commit 210b313e5ab158f32d8f09db6a8df8cb9bd6a982
| | Author: xi
| | Date:   Sun May 29 05:29:39 2011 +0000
| | 
| |     Added support for pkg-config.
| |     
| |     git-svn-id: http://svn.pyyaml.org/libyaml/branches/stable@373 18f92427-320e-0410-9341-c67f048884a3
...etc...

我仍然想知道這樣做的正確方法,而不依賴於git-subtree。

當合並包括在git log [--follow] <filename>樹時, git log [--follow] <filename>行為似乎不一致,就像git subtree那樣。

我運行了一些實驗,如果您在第一個子樹合並之前在源代碼行上引入合成重新提交提交,則會通過git log --follow <filename>報告歷史git log --follow <filename>

我可以看到的選項:

  1. 修復git log以跟蹤合並期間發生的重命名
  2. 更改git subtree以為每次添加創建兩個提交,首先在一個提交中重新創建樹,然后在之后合並重新提交的提交
  3. 通過.git/info/graftsgit filter-branch完成#2,手動解決問題

解決方法:

$ git log --grep git-subtree-mainline
commit 8789f3c80122d1fc52ff43ab776a7b186f51c3c6
Merge: 0c11300 4757376
Author: John Sumsion <email>
Date:   Wed Apr 17 09:43:21 2013

    Add 'some-subdir/' from commit 'f54875a391499f910eeb8d6ff3e6b00f9778a8ab'

    git-subtree-dir: some-subdir
    git-subtree-mainline: 0c113003278e58d32116c8bd5a60f2c848b61bbb
    git-subtree-split: f54875a391499f910eeb8d6ff3e6b00f9778a8ab
$ git checkout -b fix 
Switched to a new branch 'fix'
$ mkdir -p some-subdir
$ git mv <files> some-subdir
$ git commit -m "Re-parenting files before subtree merge to preserve 'git log --follow' history"
$ echo <orig_merge> <orig_parent> <fixed_merge_parent> >> .git/info/grafts
$ git filter-branch --index-filter true --tag-name-filter cat master

以下提交的地方是:

  • orig_merge :8789f3c80122d1fc52ff43ab776a7b186f51c3c6
  • orig_parent :0c113003278e58d32116c8bd5a60f2c848b61bbb
  • fixed_merge_parent :來自git commit sha

不幸的是,在第一個子樹合並之后通過git subtree樹合並的后續更改似乎沒有通過git log --follow <filename>即使第一個子樹合並是合成重新父級的。

出於某種原因,我似乎記得這在Git 1.7.x時間框架中運行正常,但這是一個遙遠過去的模糊記憶,我沒有時間研究。 用Git 1.8.3.2觀察到上述情況。

添加到 jdsumsion 所說的內容中, subtree-merge (或git subtree ,它在一個步驟中執行相同的操作)將不起作用,因為它所做的只是為您提供一個合並提交,將所有文件從根目錄移動到您的子目錄. 為了維護您的文件歷史記錄,該文件需要始終位於其最終位置,這需要重寫所有先前的提交。

所以你這樣做的方式是你使用git filter-branch ,因為這是一個非常不希望你使用它的小 bash 腳本。 您應該改用git-filter-repo

該過程僅涉及將外部項目作為其自己的遠程獲取,與子樹合並一樣,然后創建一個本地跟蹤分支並重寫該分支上的所有提交以追溯始終使用您想要的路徑。 然后,您可以使用unrelated-histories標志將該分支合並到您的主項目中。

使用 bash 變量主要是為了便於重用和可讀性。 如果您希望子目錄包含空格等,我不希望這會起作用,但在這種情況下手動調整應該相當容易。

export SUBTREE_PREFIX="MySubproject"

git remote add -f "${SUBTREE_PREFIX:?}-remote" https://my-git-repo.invalid/Subproject.git

git checkout "${SUBTREE_PREFIX:?}-remote"/master -b "${SUBTREE_PREFIX:?}-master"

# --force is to skip the "freshly cloned repo" check.
# All the refs we'll be operating on are fresh, even if the repo isn't

# Remove --dry-run once you've checked .git/filter-repo/fast-export.filtered
# to be sure that everything is correct.
git filter-repo --refs "${SUBTREE_PREFIX:?}-master" --to-subdirectory-filter "${SUBTREE_PREFIX:?}" --force --dry-run

git checkout master
git merge "${SUBTREE_PREFIX:?}-master" --allow-unrelated-histories

# Repeat for however many repos you need to add

就我自己而言,考慮到操作的全部意義在於如何將多個存儲庫的提交歷史歸為一個,我還想在提交消息前加上這些來自子項目的前綴,以便事后進行跟蹤。

git filter-repo --refs "${SUBTREE_PREFIX:?}-master" --to-subdirectory-filter "${SUBTREE_PREFIX:?}" --message-callback="return message if message.startswith(b'${SUBTREE_PREFIX:?}:') else b'${SUBTREE_PREFIX:?}: ' + message" --force --dry-run

此外,如果您嘗試推送不是由您提交的提交,一些 git 服務器將拒絕您的分支。 git rebase通常會為您設置提交者,同時保持提交作者不變,但在這里您需要手動完成。

git filter-repo --refs "${SUBTREE_PREFIX:?}-master" --to-subdirectory-filter "${SUBTREE_PREFIX:?}" --commit-callback '
        commit.committer_name = "You"
        commit.committer_email = "your@email.example"
' --message-callback="return message if message.startswith(b'${SUBTREE_PREFIX:?}:') else b'${SUBTREE_PREFIX:?}: ' + message" --force --dry-run

請記住,與git subtree或子模塊不同,您將無法單獨維護項目的獨立副本和更改副本,因為它們將不再具有任何歷史記錄。 如果這是一個第三方庫,您試圖在您的樹中保留一個供應商的最新副本,您會發現合並上游更改實際上是不可能的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM