如何將檔案及紀錄從 git 中真正刪除?

git23 mins

不小心把含有敏感資料(Sensitive Data)的 commit 合併到 master,想保留原本的變動,但是又想移除特定 ”檔案“ 或 “資料夾”?

在開始之前,“務必”要先告知同事或協作夥伴!因為刪除版控上的檔案及紀錄勢必會改寫當前版控紀錄(git history),為了避免有大量衝突(merged conflict), 把不要的檔案紀錄又推回去修改後的版控及版控混亂,因此行前通知及修改後的通知都是必須的!

以下將介紹兩種方法來幫助你做到這件事情,

👉 BFG Repo-Cleaner

BFG 是一個由 rtyley 開發的工具,主要可以用來取代 git command 讓我們能很快的將檔案或想刪除的東西從版控中移除,這個工具是基於 Scala 開發,所以必須要有 JAVA Runtime.

步驟一、檢查 JAVA 版本

$ java -version

# java version "1.8.0_251"
# Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
# Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

如果沒有 JAVA Runtime,可以從這邊 下載

如果你想用 git command 來實作,請看第二個部分使用 git filter branch 來刪除版控中的檔案

步驟二、先備份檔案及下載 BFG

# 複製一份全新的 repo
$ git clone --mirror git://example.com/test.git

# 備一份本地端的檔案在當前檔案的目錄下
$ cp -R test test-backup

兩個備份方式,任何一種都可以,主要是避免檔案刪除錯了或者操作不熟悉,可以快速復原。為什麼要備份? 因為 ”永久“ 刪除檔案或資料夾勢必會改寫當前版控的紀錄

接著到這邊下載 BFG ,並將檔案放置於想要變動的資料夾根目錄,以筆者來說是 test folder 的根目錄,如圖:

bfg in root folder

步驟三、檢查檔案是否存在並開始刪除檔案、資料夾

這個步驟可做也可不做,但是養成習慣確認影響的範圍是一個不錯的習慣

# 查看哪些 Commit 中有要刪除的檔案內容
$ git log --all .env

# 查看哪些 Commit 中有要刪除的資料夾
$ git log --all some-folder/*

# 沒有你要刪除的東西會得到以下訊息
fatal: ambiguous argument '.env': unknown revision or path not in the working tree.

# 有你要刪除的東西
commit dfcd6104107829e04fca0c2157f82479c0daaacb (HEAD -> master)
Author: Tim <mail@gmail.com>
Date:   Tue Apr 13 16:03:37 2021 +0800

    delete: remove .env

commit dfcd6104107829e04fca0c2157f82479c0daaacb
Author: Tim <mail@gmail.com>
Date:   Mon Apr 12 16:03:37 2021 +0800

    feat: add .env

使用 BFG 刪除檔案

# 刪除當前資料夾版控中有 .env file 的紀錄
$ java -jar bfg-1.14.0.jar --delete-files .env test.git

# 執行完後會出現以下訊息,提醒我們需要啟動 git 的 reflog 過期及啟用 gc 將不要的東西回收
# BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive

$ git reflog expire --expire=now --all && git gc --prune=now --aggressive

使用 BFG 刪除資料夾

# 刪除當前資料夾版控中有 some-folder 的紀錄
$ java -jar bfg-1.14.0.jar --delete-folders some-folder test.git

# 執行完後會出現以下訊息,提醒我們需要啟動 git 的 reflog 過期及啟用 gc 將不要的東西回收
# BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive

$ git reflog expire --expire=now --all && git gc --prune=now --aggressive

⭐️ 要特別注意的地方是, BFG 預設會保護當前版控中還存在的檔案。舉例來說,如果你想要刪除 .env 這個檔案,還存在當前的版控中,這時候需要在指令前面加上 -no-blob-protection 才能夠將保護中的檔案刪除

如:

# 刪除當前資料夾版控中的 .env
$ java -jar bfg-1.14.0.jar -no-blob-protection --delete-files .env test.git

# 刪除當前資料夾版控中的 some-folder
$ java -jar bfg-1.14.0.jar -no-blob-protection --delete-folders some-folder test.git

接著再用 git log -all 來檢查檔案或資料夾是否存在

# 檔案
fatal: ambiguous argument '.env': unknown revision or path not in the working tree.
# 資料夾
fatal: ambiguous argument 'some-folder': unknown revision or path not in the working tree.

步驟四、將變更後的版本紀錄推上 master

將刪除檔案後的 commit history 推到 master,並且通知有在協作的朋友,將既有的版控刪除,並重新 clone 一份就大功告成啦!!

$ git push -f origin master

👉 使用 git filter branch 來刪除版控中的檔案

步驟一、首先可以先透過這個指令看一下要刪除的檔案/資料夾存在哪幾個 commit ,可做可不做

# 查看哪些 Commit 中有要刪除的檔案內容
$ git log --all .env

# 查看哪些 Commit 中有要刪除的資料夾
$ git log --all some-folder/*

# 沒有你要刪除的東西會得到以下訊息
fatal: ambiguous argument '.env': unknown revision or path not in the working tree.

# 有你要刪除的東西
commit dfcd6104107829e04fca0c2157f82479c0daaacb (HEAD -> master)
Author: Tim <mail@gmail.com>
Date:   Tue Apr 13 16:03:37 2021 +0800

    delete: remove .env

commit dfcd6104107829e04fca0c2157f82479c0daaacb
Author: Tim <mail@gmail.com>
Date:   Mon Apr 12 16:03:37 2021 +0800

    feat: add .env

步驟二、使用 git filter-branch 刪除檔案、資料夾

刪除檔案

# 刪除 .env
$ git filter-branch -f --tree-filter "rm -f .env"

# Rewrite d903cf3b0f06accd717033ffc117b773a482f579 (7/7) (0 seconds passed, remaining 0 predicted)
# Ref 'refs/heads/master' was rewritten

刪除 git 裡面的備份點

$ rm .git/refs/original/refs/heads/master
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive

刪除檔案資料夾

# 刪除 some-folder
$ git filter-branch -f --tree-filter "rm -rf some-folder"

# Rewrite 51ca2ad9f29b89157ced96a289c5f6a051db4c99 (2/2) (0 seconds passed, remaining 0 predicted)
# Ref 'refs/heads/master' was rewritten

刪除 git 裡面的備份點

$ rm .git/refs/original/refs/heads/master
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive

步驟三、將變更後的版本紀錄推上 master

和使用 BFG 一樣,完成後需要通知其他有在協作的朋友,將既有的版控刪除,並重新 clone 一份就大功告成啦!!

$ git push -f origin master

相關連結

© 2024, All rights reserved.