![](/img/trans.png)
[英]Git: How to squash all commits between two commits into a single commit
[英]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-export
和git 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
文檔),如果您願意,可以強制盡早清除原始對象(額外的提交最終會自行消失),你就完成了。
為了准確地完成所有這些,步驟將是
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 rebase --root
選項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.