簡體   English   中英

Git - 在特定提交之前壓縮歷史中的所有提交

[英]Git - Squash All Commits in History Before Specific Commit

我有一個要轉換為 Git 的 Mercurial 存儲庫。 提交歷史非常大,我不需要新倉庫中的所有提交歷史。 一旦我將提交歷史轉換為 Git(並且在推送到新存儲庫之前),我想將某個標簽之前的所有提交壓縮為一個提交。

所以,如果我有:

commit 6
commit 5
commit 4
commit 3
commit 2
commit 1 -- First commit ever

我想結束:

commit 6
commit 5
commit X -- squashed 1, 2, 3, 4

注意:我需要壓縮數以千計的提交。 因此,手動挑選/標記它們不是一種選擇。

到目前為止,其他答案都建議 rebase。 可以工作,在某些情況下,這取決於在轉換到Git倉庫提交圖表。 帶有--rebase-merges的新愛好者 rebase 絕對可以做到。 但這是一種笨拙的方法。 執行此操作的理想方法是從您要保留的第一個提交開始轉換提交 也就是說,讓您的 Mercurial 導出器導出到 Git,作為 Git 的第一次提交,您要假裝的修訂版是根。 讓 Mercurial 導出器繼續將提交的后代導出到導入器中,一次一個,就像導出器總是要做這項工作一樣(無論以何種方式)。

無論你如何做到這一點取決於你使用的轉換什么工具(S)。 (我實際上並沒有進行任何這些轉換,但大多數人似乎使用hg-fast-exportgit fast-import 。我沒有過多地查看hg-fast-export的內部細節,但沒有明顯的原因它不能“T做到這一點。)


從根本上(內部),Mercurial 將提交存儲為變更集。 Git不是這種情況:Git 存儲快照 但是,Mercurial 通過根據需要將變更集匯總在一起來檢查(即提取)快照,因此如果您的工具通過執行hg checkout (或其內部等效項)來工作,那么首先這里沒有問題:您只需避免檢查在您想要的第一個快照之前刪除修訂,並將它們導入 Git,生成的 Git 歷史記錄將從所需的點開始。


但是,如果您使用的工具使這變得不方便,請注意,在將整個存儲庫歷史記錄(包括所有分支和合並)轉換為 Git 快照后,您的Git存儲庫將相對容易地作為第二遍。 例如,您的 Git 歷史記錄可能如下所示:

          o-..-o            o--o   <-- br1
         /      \          /
...--o--o--....--o--*--o--o--o--o   <-- br2
      \         /             \
       o--...--o               o   <-- master

其中 commit *是您希望在 Git 存儲庫中看到的第一個提交。 (請注意,如果在*之前有多個歷史記錄,您會遇到不同的問題,並且在沒有額外的歷史修改的情況下首先無法進行這種轉換。但只要*處於某種阻塞點,因為它在這張圖中,很容易在這里剪下圖表。)

要刪除*之前的所有內容,只需使用git replace進行一個非常commit *的替代提交,但沒有父級:

git replace --graft <hash-of-*>

你現在有了一個大多數 Git 將使用的替代品,而不是* ,它沒有父提交。 然后在所有分支和標簽上運行git filter-branch ,使用 no-op 過濾器:

git filter-branch --tag-name-filter cat -- --all

或者,一旦git filter-repo包含在 Git 中(或者如果您已經安裝了它):

git filter-repo --force

(在使用filter-repo時要小心--force選項:這會破壞這個存儲庫中的舊歷史,但在這個 csae 中,這就是我們想要的)。

這會將每個可訪問的提交(包括替代*但不包括*及其自己的歷史記錄)復制到新提交,然后更新您的分支和標簽名稱。

如果使用 filter-branch,請刪除refs/originals/命名空間(有關詳細信息,請參閱git filter-branch文檔),如果您願意,可以強制盡早清除原始對象(額外的提交最終會自行消失),你就完成了。

為了准確地完成所有這些,步驟將是

  1. 簽出到特定的提交
  2. 將它之前的所有內容壓縮到此特定提交
  3. 櫻桃挑選在此之后發生的提交
  4. 刪除現有分支
  5. 將你最近煮熟的頭保存到相同的分支名稱中

function git_squash_from() {
    COMMIT_TO_SQUASH=$1
    SQUASH_MESSAGE=$2

    STARTING_BRANCH=$(git rev-parse --abbrev-ref HEAD) # This will be overwritten
    CURRENT_HEAD=$(git rev-parse HEAD)

    echo From $CURRENT_HEAD to the successor of  $COMMIT_TO_SQUASH will retain, from $COMMIT_TO_SQUASH to beginging will be squashed

    git checkout $COMMIT_TO_SQUASH
    git reset $(git commit-tree HEAD^{tree} -m "$SQUASH_MESSAGE")
    git cherry-pick $CURRENT_HEAD...$COMMIT_TO_SQUASH
    git branch -D $STARTING_BRANCH
    git checkout -b $STARTING_BRANCH    
}

git_squash_from 87ef7fa "Squash ... "

您可以進一步擴展它以從所有提交消息構建 SQUASH_MESSAGE。

假設原來的分支是master ,新的分支是new

git checkout --orphan new commit4
git commit -m "squash commits"
git branch tmp master
git rebase commit4 tmp --onto new
git checkout new
git merge tmp
git branch -D tmp

如果要保留合並提交,則“git rebase”中需要選項“-p”。

雖然git reset --soft可能是git reset --soft組提交的選項(如此處所示),但我建議,對於多組提交:

  • 擁有一個原始的 Git 存儲庫
  • 在兩個標簽之間打補丁(如果你可以從一個標簽轉到下一個),
  • 將每個補丁應用到一個新的 Git 存儲庫,在那里您將這些壓縮的提交存儲為一個接一個的補丁。

請注意,這適用於第一次提交,通過git rebase --root選項

暫無
暫無

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

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