如何從Git存儲庫中的提交歷史記錄中刪除/刪除大文件?

有時,我將DVD-rip放入一個網站項目中,而後不當心git commit -a -m ... ,而後,zap的回購膨脹了2.2個演出。 下次我進行一些編輯,刪除視頻文件並提交全部內容,可是壓縮文件仍在歷史記錄中。 html

我知道我能夠從這些提交開始分支,並將一個分支從新創建到另外一個分支。 可是,我應該怎麼作才能將2個提交合並在一塊兒,以便大文件不顯示在歷史記錄中,並在垃圾回收過程當中清除? java


#1樓

請注意,此命令可能具備很大的破壞性。 若是更多的人在回購上工做,他們都將不得不拉新的樹。 若是您的目標不是減少大小,則不須要三個中間命令。 因爲filter分支會建立已刪除文件的備份,所以能夠在其中保留很長時間。 git

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

#2樓

git filter-branch --tree-filter 'rm -f path/to/file' HEAD對我來講效果很好,儘管我遇到了與此處所述相同的問題,我經過遵循此建議解決了這個問題。 github

這本pro-git書包含一整章有關重寫歷史的內容 -看看filter-branch /從「每次提交中刪除文件」部分。 shell


#3樓

這些命令在個人狀況下有效: c#

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

與上述版本幾乎沒有什麼不一樣。 bash

對於那些須要將其推送到github / bitbucket的用戶(我僅使用bitbucket進行了測試): less

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work

#4樓

使用BFG Repo-Cleaner ,這是git-filter-branch一種更簡單,更快的替代方法,專門用於從Git歷史記錄中刪除不須要的文件。 編輯器

認真遵循使用說明 ,核心部分就是這樣: ide

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

任何大小超過100MB的文件(不在您的最新提交中)都將從Git存儲庫的歷史記錄中刪除。 而後,您可使用git gc清除失效的數據:

$ git gc --prune=now --aggressive

BFG一般比運行git-filter-branch至少快10-50倍,而且一般更易於使用。

徹底公開:我是BFG Repo-Cleaner的做者。


#5樓

若是您已將歷史記錄發佈給其餘開發人員,那麼您想要作的就是極具破壞性的。 有關修復歷史記錄後的必要步驟,請參見git rebase文檔中的「從上游Rebase恢復」

您至少有兩個選擇: git filter-branch和交互式rebase,這兩個都在下面說明。

使用git filter-branch

我從Subversion導入中獲取大量的二進制測試數據時遇到了相似的問題,並寫了有關從git存儲庫中刪除數據的信息

說您的git歷史記錄是:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

請注意, git lola是非標準但很是有用的別名。 使用--name-status開關,咱們能夠看到與每次提交關聯的樹修改。

在「 Careless」提交(SHA1對象名稱爲ce36c98)中,文件oops.iso是偶然添加的DVD-rip,並在下一個提交cb14efd中刪除。 使用上述博客文章中描述的技術,要執行的命令是:

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

選項:

  • --prune-empty刪除因爲過濾操做而變爲空的提交( ,不更改樹)。 在典型狀況下,此選項會產生更清晰的歷史記錄。
  • -d命名一個臨時目錄,該目錄尚不用於構建過濾的歷史記錄。 若是您在現代Linux發行版上運行,則/dev/shm指定樹將加快執行速度
  • --index-filter是主要事件,在歷史記錄的每一步都針對索引運行。 您但願刪除oops.iso不管它在哪裏找到,但並不是全部提交中都存在。 git rm --cached -f --ignore-unmatch oops.iso刪除存在的DVD-rip,不然不會失敗。
  • --tag-name-filter描述瞭如何重寫標籤名稱。 cat的過濾器是身份操做。 您的存儲庫與上面的示例同樣,可能沒有任何標籤,可是出於全面考慮,我包括了此選項。
  • --指定git filter-branch選項的結尾
  • --all如下--是全部裁判的簡寫。 像上面的示例同樣,您的存儲庫可能只有一個ref(主文件),可是出於全面考慮,我包括了此選項。

通過一番攪拌,如今的歷史是:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/
|   A   oops.iso
|   A   other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

請注意,新的「 Careless」提交僅添加other.html ,而「 Remove DVD-rip」提交再也不位於master分支上。 標爲refs/original/refs/heads/master的分支包含您的原始提交,以防您出錯。 要刪除它,請按照「收縮存儲庫清單」中的步驟進行操做

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

對於更簡單的選擇,克隆存儲庫以丟棄不須要的位。

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

使用file:///...克隆URL複製對象而不是僅建立硬連接。

如今您的歷史記錄是:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

前兩個提交(「索引」和「管理頁面」)的SHA1對象名稱保持不變,由於過濾操做未修改這些提交。 「 Careless」丟失了oops.iso ,「 Login page」有了新的父代,所以其SHA1 確實發生了變化。

互動基礎

具備如下歷史:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

您想從「無憂無慮」中刪除oops.iso ,就好像您從未添加過它同樣,而後「刪除DVD-rip」對您毫無用處。 所以,咱們計劃進行交互式基礎調整的是保留「管理員頁面」,編輯「無憂無慮」並丟棄「刪除DVD-rip」。

運行$ git rebase -i 5af4522啓動具備如下內容的編輯器。

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

執行咱們的計劃,咱們將其修改成

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

也就是說,咱們用「 Remove DVD-rip」刪除該行,並將「 Careless」上的操做更改成edit而不是pick

保存退出編輯器後,在命令提示符處顯示如下消息。

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

消息告訴咱們,咱們處於咱們要編輯的「 Careless」提交中,所以咱們運行兩個命令。

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

第一個從索引中刪除有問題的文件。 第二個將「 Careless」修改或修改成更新的索引,而且-C HEAD指示git重用舊的提交消息。 最後, git rebase --continue繼續進行其他的rebase操做。

這提供瞭如下歷史記錄:

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

這就是你想要的。

相關文章
相關標籤/搜索