簡體   English   中英

僅在兩次提交之間從Git歷史記錄中刪除文件

[英]Removing files from your Git history exclusively between two commits

我試圖使用filter-branch從我的歷史記錄中刪除一些大文件。 我之前已成功使用此命令,但我目前遇到特殊邊緣情況的問題。

問題是這些大文件從未真正刪除過,而是被具有相同路徑的較小版本所取代

據我所知,我相信我有一個獨特的問題。

Git Log

詳細說明,這里是我的回購的基本代表:

----- A ------ B ----------- HEAD

哪里:

A is the commit where the large files were introduced
B is the commit (about 30 later) where the large files were replaced with smaller ones
HEAD is thousands of commits forward of B (~2 years of active development)

Git Filter-Branch

從理論上講,我應該能夠做到這樣的事情:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filenames' <parent of A>..B 

我相信我應該使用<parent of A>因為filter-branch不包含在內。 (我不確定我是否也需要使用B的父母,但這是我現在最不擔心的事情)。

運行這個給我錯誤:

$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch filenames' <parent of A>..B 
Which ref do you want to rewrite?

所以我在命令的末尾包括了--glob="refs/heads/master*" ,這似乎是訣竅( 源碼 )。

執行完成后,文件已被完全刪除 - 似乎git忽略了我指定的上限。

所以我想知道這種方法是否可行?

替代方法

我想我應該列出一些其他的想法,以便潛在的答案可以集中在解決問題上。

  1. 實用的方法是在HEAD提交文件名更改,然后運行git filter-branch ... HEAD 但是,我的存儲庫在活動開發中有許多分支,我相信這種方法會非常混亂。
  2. 另一種方法可能是做類似這里描述的事情 引用: create a temporary branch to point at HEAD^, filter-branch it, then add a graft to stitch the remaining commit on top of it, then filter-branch HEAD and then remove the graft.

希望有人之前遇到過這個問題並且可以提供他們的專業知識。

更新

我要刪除的文件總數大約500MB,所以我很想知道要刪除它們! 他們在我加入公司之前很久就已經投入了,並且是我們從內部Mercurial服務器遷移到GitHub的剩余部分(我想將500MB推送到內部服務器將比GitHub更不明顯......)。

更新2

我一直在關注twalberg的第二個答案(我想我正在以正確的方式使用它):

git filter-branch --index-filter '(( $(git rev-list <SHA-of-child-of-B> --not $GIT_COMMIT | wc -l) > 0 )) && git rm --cached --ignore-unmatch <filenames>' 

這產生了我期望的那種輸出:

...
Rewrite dc8a4b29463bfa43c2f3efe0c6e5a29a5cc6e0ef (1071/5680)rm 'file1'
rm 'file2'
rm 'file3'
rm 'file4'
...

在結束(預期?)錯誤之前:

Rewrite e6b712b57257e2edd0bb9fbbac59e4c9d7b5aa79 (1072/5680)index filter failed: (( $(git rev-list e6b712b --not $GIT_COMMIT | wc -l) > 0 )) && git rm -rf --ignore-unmatch <filename>

其中e6b712bB的孩子。

在這一點上,我假設一切都有效,所以我做了我的存儲庫的本地文件系統克隆來測試它:

git clone file://<repo> <new repo>

對象數量和包文件大小減少了很少 - 我不知道為什么。 通過對原始存儲庫運行git count-objects -v而不是對其運行filter-branch那個:

原始存儲庫:

count: 0
size: 0
in-pack: 106640
packs: 1
size-pack: 815512
prune-packable: 0
garbage: 0

filter-branch ed和filesystem克隆的存儲庫:

count: 0
size: 0
in-pack: 96165
packs: 1
size-pack: 793656
prune-packable: 0
garbage: 0

我真的不確定為什么這仍然不起作用 - 也許我沒有正確地遵循建議的答案?

不幸的是,如果你真的想要從你的存儲庫中刪除這些對象(相比之下只是從當前和未來的版本中刪除它們), filter-branch就是這樣做的方式,如果你要重寫提交A ,每次提交都是由於提交的提交哈希值取決於該提交的每個父級的提交哈希值,因此必須重寫其歷史記錄中包含A每個分支頭。 如果您不重寫包含A所有分支,那么這些對象仍然是您可訪問歷史記錄中某些提交的合法部分,並且它們將不會被修剪。

對於在其歷史中包含A每個分支BR ,這應該有效:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filenames' BR --not A~1

它將從A (通過修剪A s父級的分支)重寫到分支BR的當前尖端。 它會刪除所有這些提交中的文件,即使它們被較新的較小版本替換。 為了刪除它們只提交B ,您可以像這樣展開過濾器腳本:

... --index-filter '(( $(git rev-list <SHA-of-child-of-B> --not $GIT_COMMIT | wc -l) > 0 )) && git rm ...' ...

這使用rev-list列出當前正在重寫的提交之后的所有修訂,直到B的子B ,計算這些行,並且只有當一個或多個修訂屬於該范圍時才會執行git rm (當$GIT_COMMIT == B ,將打印一行 - 因此需要使用B的孩子。

即使對於單個分支來說,這也是一個相當大的變化,如果你有很多分支是在A之后或之后產生A ,那么很多工作,所以你必須決定它是否最終值得,或者你只需​​要一個更大的磁盤(你沒有提到這些文件究竟有多大 )。

A     is the commit where the large files were introduced
B     is the commit (about 30 later) where the large files were replaced 
      with smaller ones
HEAD  is thousands of commits forward of B (~2 years of active development)

你說過這個我會強烈反對filter-branch ,因為我相信它會重寫2年的提交SHA。 也許另一種解決方案是git revert

git revert SHA_A..SHA_B
    Revert the changes done by commits from commit SHA_A (included) to
    SHA_B (included)

暫無
暫無

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

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