[ 狀況題 ] 如何徹底將檔案從 Git 中移除?


Posted by krebikshaw on 2020-08-09

為何會寫這篇文章?

程式導師計畫第四期 的課程,到了 week9 的時候,需要開始練習如何利用 php 來串接資料庫。由於連線資料庫的過程,是需要透過帳號及密碼來連線的。所以至少會有一份 php 檔案當中會寫有你的資料庫的帳號跟密碼,也就是我們所謂的「敏感資訊」。在 week9 的作業中,這份檔案就是 conn.php

其實這個錯誤不會發生的

  • Huli 有在「如何上傳作業」說明中,提到了他已經幫我們將 conn.php 這份會帶有敏感資訊的檔案給加入了 .gitignore 當中。(Huli 已經貼心的幫我們設定好了)

    正常的情況下,在各位 commit 的時候,conn.php 這份檔案是不會被加入版本控制的

但它偏偏還是發生了

  • 當我交完作業之後發現,咦?這份檔案怎麼傳不上去,裡面有程式碼唉,助教看不到這份檔案,不就沒辦法幫我裡面寫的內容了嗎? ( 聰明的你應該已經發現我當時已經把老師的警告都忘記啦! )
  • 我很天真的認為應該要把檔案放上去讓助教可以改作業,我打開了我的瀏覽器,連結至 GitHub 的 repository 直接把那份檔案用滑鼠拉進來雲端的 repository 裡面!

說時遲那時快

機警的 Huli 在第一時間發現了問題,立馬來關心這位迷糊的同學!

Huli:
你的 conn.php 沒有被 git ignore 掉嗎?
因為照理來說應該會被弄掉才對
我有放到 .gitignore 裡面
接下來要請你試著把那個檔案完全拿掉了
直接刪掉是沒用的 因為還是留在紀錄裡

  • 所以這篇文章就誕生啦

不小心把帳號密碼放在 Git 裡了,想把它刪掉…

若你看過 Z 同學先前分享過的這篇文章「刪除了 branch 將 commit 資料也刪除了~怎麼辦!!!」,你就會察覺,加入版本控制的檔案,一但刪除是可以有機會救回來的,所以要把檔案刪掉,就必須把所有能救回來的路線跟方法通通斬斷。

刪除檔案前置動作: 首要任務

  • 若不小心把帳號密碼放在 Git 的首要任務,就是在刪除檔案之前先趕快換掉你的密碼!
  • 確保密碼重設之後,再來次要任務就是刪除這份被加進 Git 的檔案了

方法一: 把 .git 刪除

  • 若這份專案是你的個人專案,你不在乎以前 commit 的紀錄,而且這份專案從來沒有 push 到倉庫過,那其實只要把 .git 目錄刪掉就可以了因為所有的 Commit 紀錄都在 .git 目錄裡。

  • 但若是這份專案的 .git 是不能刪掉的,或者已經把 push 到倉庫上,那只能參考「方法二」把檔案全面修改了。

方法二: 把所有版本當中的 conn.php 連根拔除

  • Z 同學分享的這篇文章「 刪除了 branch 將 commit 資料也刪除了~怎麼辦!!!」 提到刪除的 branch 是可以救回來的,也就是說,你刪掉任何東西,通通可以反悔。( 我刪除了,又反悔了,我又刪除了,我又反悔了! )
  • 那這樣要如何操作呢?

步驟 1:使用 filter-branch 指令

  • git filter-branch --tree-filter "rm -f conn.php"
  • 我們都知道使用 rm 可以刪除掉檔案
  • 但是因為所有 commit 過的版本,只要裡頭有記錄到 conn.php 這個檔案的都要刪掉
  • 所以 Git 有一個叫做 filter-branch 的指令,它可以一次大量的修改 Commit。
    • filter-branch 這個指令可以根據不同的 filter,一個一個 Commit 的來處理它。
    • --tree-filter 可以在 Checkout 到每個 Commit 的時候執行接下來的指令,執行完後再自動幫你重新再 Commit。
    • 以下面的例子來說,便是執行「強制刪除 conn.php 檔案」並在重新 commit 一次。
$ git filter-branch --tree-filter "rm -f conn.php"

WARNING: git-filter-branch has a glut of gotchas generating mangled history
     rewrites.  Hit Ctrl-C before proceeding to abort, then use an
     alternative filtering tool such as 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) instead.  See the
     filter-branch manual page for more details; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

You need to run this command from the toplevel of the working tree.
  • 它會跟你說,你必須要先切換到專案最上層的資料夾,才能下這個指令

    • 也就是 mentor-program-4th 這個資料夾
  • 所以我們切換到正確路徑之後,再下一次指令

$ git filter-branch --tree-filter "rm -f conn.php"

WARNING: git-filter-branch has a glut of gotchas generating mangled history
     rewrites.  Hit Ctrl-C before proceeding to abort, then use an
     alternative filtering tool such as 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) instead.  See the
     filter-branch manual page for more details; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Rewrite de410b35495fe34dbd5763bc31195ea6b2065e97 (107/109) (11 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten
  • 這樣所有 commit 的紀錄中,conn.php 這份檔案就通通拿掉了

步驟 2:再重頭來一次

  • git filter-branch -f --tree-filter "rm -f conn.php"
  • 剛剛刪除掉都是假的,因為你隨時可以反悔
  • 所以為了要把 filter-branch 的備份點蓋過去,我們要在剛剛的指令中加入一個 -f,強制覆寫 filter-branch 的備份點
$ git filter-branch -f --tree-filter "rm -f conn.php"

WARNING: git-filter-branch has a glut of gotchas generating mangled history
     rewrites.  Hit Ctrl-C before proceeding to abort, then use an
     alternative filtering tool such as 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) instead.  See the
     filter-branch manual page for more details; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Rewrite f63bdc1d284d649a2fde6474873be812b6715daa (101/109) (9 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten

步驟 3:製造不在場證明

  • rm .git/refs/original/refs/heads/master
  • 由於剛剛執行 filter-branch 指令的「動作」也會被儲存下來,隨時可以透過它再跳回去,所以要把這份紀錄也刪掉
$ rm .git/refs/original/refs/heads/master

步驟 4:把 Reflog 清掉

  • $ git reflog expire --all --expire=now
  • Z 同學就是利用 Reflog 來把刪除的 branch 救回來的,所以這份 Reflog 我們要立刻讓他過期
$ git reflog expire --all --expire=now

步驟 5:git fsck

  • $ git fsck --unreachable
  • 這行指令我沒能很明白其中的意思,說明上是寫能印出存在但是不能從任何參考節點到達的對象。
    • 這裡指的應該是查看狀態為 unreachable 的這些物件
$ git fsck --unreachable

Checking object directories: 100% (256/256), done.
Checking objects: 100% (410/410), done.
unreachable tree 21f053d4331219ed36df3defc62f813a0a3bdcc4
unreachable tree 27e052e5194d4093c0717b2ff638db039198dfa4
unreachable tree 30004f2209bb3181a14aca171587f8834c319149
unreachable tree 5060b7e072951cda0e6299a58059d7c1a5ca4c1b
unreachable blob 7ee0adb09094d06384a33d9da509b30ba02b6128
unreachable tree 9240fa4cf76000df5cb65c2519cff724242a52ff
unreachable tree a33066762b73fd6ee9d0b30e1a23b34120dc8f4a
.
.
.

步驟 6:啟動 Git 回收機制

  • $ git gc --prune=now
  • git gc 可以清理不必要的文件並優化電腦的記憶體空間
  • --prune=now 修剪早於現在的所有鬆散對象
  • 這段指令白話文就是把剛剛印出的這些 unreachable 的物件通通回收掉
$ git gc --prune=now

Enumerating objects: 1073, done.
Counting objects: 100% (1073/1073), done.
Delta compression using up to 4 threads
Compressing objects: 100% (893/893), done.
Writing objects: 100% (1073/1073), done.
Total 1073 (delta 498), reused 382 (delta 128)
Computing commit graph generation numbers: 100% (213/213), done.
  • 再下一次剛剛的 git fsck 可以發現這些物件都回收掉了
$  git fsck

Checking object directories: 100% (256/256), done.
Checking objects: 100% (1073/1073), done.
  • 目前為止,應該已經沒辦法找回原來的 SHA-1 值了

步驟 7:同步倉庫的紀錄

  • git push -f
  • 本地端處理完後,最後就是要把雲端的資料也修正過來了
  • git push -f 可以把線上的紀錄覆蓋掉
$  git push -f

Enumerating objects: 855, done.
Counting objects: 100% (773/773), done.
Delta compression using up to 4 threads
Compressing objects: 100% (363/363), done.
Writing objects: 100% (703/703), 8.16 MiB | 106.00 KiB/s, done.
Total 703 (delta 331), reused 622 (delta 320)
remote: Resolving deltas: 100% (331/331), completed with 12 local objects.
remote:
remote: GitHub found 10 vulnerabilities on Lidemy/mentor-program-4th-krebikshaw's default branch (2 critical, 3 high, 4 moderate, 1 low). To find out more, visit:
remote:      https://github.com/Lidemy/mentor-program-4th-krebikshaw/network/alerts
remote:
To https://github.com/Lidemy/mentor-program-4th-krebikshaw.git
 + de410b3...f63bdc1 master -> master (forced update)

  • 從 GitHub 上的的資料,可以看到有 103 筆的 commit 被重新修正了

結語

大家真的在繳交作業的時候需要注意,帶有敏感資訊的檔案,不可以加入版本控制。由於 Huli 幫我們加進 .gitignore 裡的是 conn.php 這份檔案,所以若是你的帳號密碼不是寫在這份檔案裡面,一但 push 上雲端後要修正是件浩大的工程。

避免的方法就是,乖乖的只把帳號密碼寫在 conn.php 裡面,並且不要沒事去把檔案拉上去 repository。另外一種狀況是,你如果有把帳號密碼寫在其他檔案的,就應該馬上先把檔案加入 .gitignore 裡面。

此篇文章是我自己處理過程的紀錄,程式碼是參考這篇 Huli 給我的文章 「【狀況題】不小心把帳號密碼放在 Git 裡了,想把它刪掉…」來操作的。










Related Posts

交作業流程 Week1-hw1

交作業流程 Week1-hw1

[SQL] Update Where 依照條件更新指定欄位

[SQL] Update Where 依照條件更新指定欄位

二、使用Node.js運行Server

二、使用Node.js運行Server


Comments