簡體   English   中英

刪除git歷史記錄中的前x個提交,並刪除其余歷史記錄中的所有合並分支

[英]delete first x commits in git history and remove all merge branches from the rest of the history

我有一個git項目的歷史記錄,據我有近400次提交。 我想刪除第一個(最早的)200次提交。 然后在其余的200次提交中,我只想刪除所有合並提交並保持其余順序。

完成之后,我要檢查所有其余的提交並更改一封特定的作者電子郵件。

有沒有辦法優雅地做到這一點?

正如幾個人已經說過的那樣,這很少是一個好主意,原因有幾個,我不再贅述。 我想再添加一件事,然后展示如何使用git filter-branch來做到這一點。

這不是刪除,而是新副本:本質上是新的倉庫

了解這一點的關鍵是,您不能從一系列提交的開頭或中間刪除提交。 原因很簡單:每個提交都將其父提交的身份記錄為身份的一部分。 術語的技術術語是提交圖形成Merkle樹

更具體地講,提交的身份(如果您願意的話,為“真實名稱”)是其SHA-1。 SHA-1是提交中數據的加密1哈希。 數據之一是parent行。 這是git來源本身的實際提交內容(減號@可以阻止垃圾郵件的收集):

tree 55c0d854767f92185f0399ec0b72062374f9ff12
parent 8413a79e67177d026d2d8e1ac66451b80bb25d62
author Junio C Hamano <gitster pobox.com> 1436563740 -0700
committer Junio C Hamano <gitster pobox.com> 1436563740 -0700

The last minute bits of fixes

Signed-off-by: Junio C Hamano <gitster pobox.com>

如果要嘗試刪除鏈中任何位置的父提交,則將為子提交獲得一個新的,不同的哈希號。 這意味着,所有孩子都需要改變為好,以納入新的SHA-1,全部環比下滑。

這對您來說意味着要獲取任何內容,包括git filter-branch似乎要刪除一些提交,則必須每個要保留的提交復制具有新的,不同ID的提交(具有與之前相同的消息等,但parent行不同)。 2

從本質上講,執行git filter-branch的結果是創建存儲庫的新副本 ,其中至少包含一些(也許完全是)新的和不同的提交。 反過來,這意味着使用舊存儲庫的其他任何人都必須丟棄其舊存儲庫並切換到新存儲庫。

git filter-branch

盡管git filter-branch有很多選項,但其核心工作歸結為這一點。 每次提交: 3

  • 擴展提交的源代碼樹
  • 獲取作者和提交者(姓名,電子郵件和時間戳)
  • 應用所有過濾器:
    • 對樹進行必要的更改
    • 對作者和提交者進行必要的更改
    • 保留或跳過此特定提交:如果保留此提交,請從剩余內容中進行一次新提交
  • 向映射文件“原始SHA-1”添加項到“新SHA-1”

項目符號指向的列表是“復制”步驟,此后還有最后一項任務,即“更新引用”。 為了正確地理解這一部分,您需要知道git的引用是如何工作的,但是總之,要檢查分支名稱(如果您添加--tag-filter ,標記名稱為wee),以查看它們是否指向了舊的提交,重寫。 如果是這樣,它們將更改為指向新副本,或者在跳過提交的情況下指向最近的new-copy提交,

為了實現skip_commit功能,您需要編寫一個提交過濾器,該過濾器使用skip_commit函數忽略要刪除的提交(前200個和合並),其余使用git commit-tree 有關更多詳細信息,請參見git filter-branch文檔

git filter-branch有這么多選項的一個原因是,擴展和重新壓縮整個源樹非常慢。腳本試圖避免這種情況,並且如果所有的過濾器都可以在索引和提交圖中完成,則無需擴展刪除源樹-過濾器的完成速度要快得多。)

基於新提交根的示例實現:

下面的代碼將創建一個僅由指定新STARTCOMMIT以下的所有提交組成的新存儲庫。 分支和標簽被保留。

export STARTCOMMIT=.....

git filter-branch --tag-name-filter cat \
   --commit-filter '
     git merge-base --is-ancestor ${STARTCOMMIT} ${GIT_COMMIT};
     if [ $? -eq 1 ]; 
     then
        skip_commit "$@";
     else
        git commit-tree "$@";
     fi' \
   -- --all

# remove original references
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
# reduce repo size
git reflog expire --expire=now --all && git gc --aggressive --prune=all

1 “密碼”形容詞的含義是,您不能簡單地對提交進行輕微更改,例如,在消息中添加文本,以產生與以前相同的舊SHA-1。 在計算上可行的時間內完成此操作的唯一方法是破壞加密。

2在變更較少的情況下,如果您精確復制原始提交,則將使用以前的SHA-1。 例如,如果您有一個篩選分支操作,該操作刪除了鏈中第二至最尖端的提交,則只有最尖端的提交才獲得新的SHA-1。 不過,在這種特殊情況下,我們建議刪除根提交,該根提交必定會為每個后續提交重新編號。

3要復制的提交是從您在filter-branch操作中提供的gitrevisions -style參數獲得的。 還可以使用“正引用”從此處獲取要重寫的分支名稱。

首先,如果您確實想這樣做,請三思。 (更改歷史記錄,尤其是在公共存儲庫上更改記錄,通常是個壞主意。)

您可以使用git rebase -i這樣做。 在那里,您可以使用fixup將兩個提交合並為一個,可以使用edit更改提交。 (包括作者變更。)

對於多個提交的自動更改,可以使用git filter-branch 但是,只有在知道自己在做什么的情況下,才使用此功能。

暫無
暫無

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

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