iT邦幫忙

2021 iThome 鐵人賽

DAY 26
2
Modern Web

【Git】從零開始學習 Git - 30 天的學習筆記系列 第 26

Day26|【Git】 從 Git 中移除重要個資或徹底清除檔案 - git filter-branch

有時候當我們在寫程式碼時會用到一些 API 金鑰,或是個人的帳號、密碼,這些都是屬於「敏感資訊」,一旦不小心 Push 出去,可能會受到有心人士的利用。所以當不小心把重要個資、機密文件傳出去時,請先改密碼改密碼改密碼!後續再來找解決辦法,這樣才是保護資料的首要之責。


改完密碼、確保密碼重設之後,接下來可以思考如何從 Git 中將這些資料給移除。

最直接、直覺、也最快的方式 - 直接砍掉重練

將檔案 Push 之前都會先經過 Commit 將檔案提交到儲存庫,而這些紀錄都是被存在 .git 目錄之中。

因此我們可以對 .git 進行動作,將 Commit 的資料都給刪除。

執行步驟

  1. 將 .git 目錄刪除 → 如此一來檔案就不再被 Git 掌控了!

    https://ithelp.ithome.com.tw/upload/images/20211009/20141010kh1iN1ZPUt.png

  2. 將原先有保留重要機密資料或帳號密碼的檔案刪除/修改。

  3. 重新再 Commit 一次新的版本。(重新進行版本控制流程)

    $ git init # 進行版本控制(此時就會有 .git 目錄產生)
    $ git add . # 將檔案加至暫存區
    $ git commit -m"紀錄訊息" # 提交檔案至儲存庫
    

⚠️要注意的是:

一但將 .git 目錄移除時,同時也代表之前所有的 Commit 紀錄也一併刪除

也許這樣並不是最好的辦法,但它是其中一個選擇。

假設今天只是單純是你個人的練習、本地端的操作,或是專案只有你一人負責,那麼不妨可以參考這個做法。


上面的方法提到會將整個 .git 目錄刪除,同時代表整個 Commit 紀錄也會被刪除。

但是今天如果你是團隊分工,這份專案的 .git 是不能夠隨便刪除的,或者已經被 Push 出去到遠端,那麼方法一就不太適合,這時候可以考慮接下來的做法。

步驟一|使用 filter-branch 指令

filter-branch - 可以一次修改大量 Commit

$ git filter-branch --tree-filter "rm -f 檔案名稱"

🛠實際操作

  • filter-branch 指令

    狀況|假設今天我是將「敏感資訊」存在 insex.html 內,但我將它 Commit 出去,我想把有關於 insex.html 的 Commit 紀錄移除掉。

    目前 git_practice 目錄下有的檔案:

    https://ithelp.ithome.com.tw/upload/images/20211009/20141010PQHQvDBmdC.png

    執行指令來移除 Commit:

    $ git filter-branch --tree-filter "rm -f insex.html"
    

    https://ithelp.ithome.com.tw/upload/images/20211009/201410108Sy6yUwtJR.png

    得到的回饋訊息:
    Rewrite 0e915cf76c1822389d8ab862fe0e70d1faeed313 (4/4) (0 seconds passed, remaining 0 predicted)
    Ref 'refs/heads/one' was rewritten

    回到 git_practice 目錄可以看到 insex.html 檔案不見了。

    https://ithelp.ithome.com.tw/upload/images/20211009/20141010W207HM9hCZ.png

    如此一來之前 Commit 紀錄裡若有 Iinsex.html 這個檔案,都會一併被移除掉,不再 Commit 裡。

👉 指令說明:

  1. filter-branch 可以根據不同的 filter ,逐一 Commit 處理它。
  2. --tree-filter 這個篩選參數,意思是可以讓你在切換/檢查Checkout)到每個 Commit 時執行指定的指令,執行完後再自動 Commit 新的版本
  3. 先前有提到過 Commit 的 SHA-1 校驗碼是有一定規則計算的,因此現在將其中一個檔案刪除後,計算結果將有所不同,則重新 Commit 後整個數值都改變了,相當於產生了一份新的歷史紀錄
  4. 在回饋訊息中可以看到 Ref 'refs/heads/one' was rewritten 這些訊息,這意思是 Git 將你之前的狀態備份一份在 .git/refs/orignal/refs/heads/one 這個目錄裡,或是也可以說是備份開始進行 filter-branch 之前的 HEAD 的 SHA-1 值

https://ithelp.ithome.com.tw/upload/images/20211009/20141010DMIH186C8A.png

https://ithelp.ithome.com.tw/upload/images/20211009/20141010d9shNQZDgr.png

👉 透過第四點的說明可以了解到,我們擁有之前的 HEAD 的 SHA-1 值,所以當如果我們後悔剛才所執行的 filter-branch 指令時,可以使用以下指令來回復:

$ git reset refs/orignal/refs/heads/one --hard

如此一來就可以取消先前的動作囉!


如上述提到的,即使我們執行了指令, Git 還是會自動幫你備份,讓你有辦法救回。

所以進來 Git 容易,想要離開卻很難啊!

那麼有沒有什麼辦法可以真正地將檔案從 Git 中移除呢?

#將檔案徹底從 Git 中移除

  1. 移除整個 .git 目錄

    因為 Commit 紀錄都在 .git 目錄中,直接砍掉重練是最快的方式,但之後還需要重建一次。

  2. 使用 Rebase 或 filter-branch 指令整理

    • Rebase - 適用於只有少量的 Commit,可以直接進行編輯、重整。
    • filter-branch - 可以大範圍對每個 Commit 執行某個指令,並於修改完後自動重新 Commit。

    先前執行 filter-branch 指令依然會保留備份的檔案在其他處,因此我們可以加上 -f 參數,意指強制覆寫 filter-branch 的備份點。

    👉加上 -f 參數:

    $ git filter-branch -f --tree-filter "rm -f 檔案名稱"
    

    執行 filter-branch 指令後,可以成功將檔案移除掉,但是我們還有一些需要做的事情,才能將檔案全部斷乾淨。

    👉移除備份點 - rm 指令

    $ rm refs/orignal/refs/heads/one
    

    👉Reflog

    $ git Reflog expire --all --expire=now  
    

    此指令是因為 Git 有個資源回收的機制,預設是要等 30 天才會整個移除檔案。因此這裡的指令是要要求 Reflog 立刻過期

    接著可以使用 git fsck 指令看到我們先前移除掉的檔案,這些都變成 Unreachable 狀態的檔案。

    $ git fsck --unreachable  
    

    Unreachable 是指在 Commit 與分支中沒有一個連結點可以到達的檔案。

    得到 Unreachable 狀態的檔案後,啟動資源回收機制,呼叫垃圾車來將檔案載走,如此一來就成功地將檔案完完全全地從 Git 中脫離控制啦!

    $ git gc --prune-now
    

    整理流程:

    1. 執行 filter-branch 強制覆寫指令 git filter-branch -f --tree-filter "rm -f 檔案名稱"
    2. 移除備份點 rm refs/orignal/refs/heads/one
    3. Reflog 中的也移除並要求立馬過期 git Reflog expire --all --expire=now
    4. 啟動資源回收機制將垃圾載走 git gc --prune-now

上一篇
Day25|【Git】git stash 暫存檔案
下一篇
Day27|在 GitHub 上建立專案與使用 git push 指令將檔案上傳到 GitHub
系列文
【Git】從零開始學習 Git - 30 天的學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
碼農
iT邦新手 4 級 ‧ 2023-12-12 11:19:17

友善提醒跟我一樣採坑的人XD

1.本來以為只要檔案名,結果刪不掉,所以還要路徑
git filter-branch -f --tree-filter "rm -f 路徑+檔案名稱"

2.路徑有錯字,然後沒切到.git
rm .git/refs/original/refs/heads/分支名稱

我要留言

立即登入留言