Stories

Detail Return Return

為什麼 .gitignore 不生效?其實你忘了用 git rm --cached! - Stories Detail

簡介

命令格式:

git rm --cached <file>

意思:

Git 的 索引(index,暫存區) 中移除文件,但保留工作區中的實際文件。

也就是説:

  • 文件仍然留在硬盤(工作區);
  • 但不再被 Git 跟蹤(tracked)。

<file>...:要移除的文件或目錄路徑。可以指定多個文件,或使用通配符(如 *.log)。
常用選項:

  • --cached:僅從索引移除(必須使用)。
  • -r--recursive:遞歸移除目錄及其內容(如果指定目錄)。
  • -f--force:強制移除,即使暫存內容不匹配分支 tip 或磁盤文件。
  • -q--quiet:安靜模式,抑制輸出。
  • --ignore-unmatch:如果文件不在索引中,繼續執行而不報錯。

三大區域

Git 有三層結構:

層級 名稱 説明
Working Directory 工作區 真實存在的文件
Index (Staging Area) 暫存區 下次提交的快照
Repository 倉庫 提交的歷史記錄(.git/objects)

當執行:

git add file.txt

文件被添加到 index(暫存區)。

當執行:

git commit

暫存區內容被保存為新的 commit(快照)。

而:

git rm --cached file.txt

是從暫存區刪除這個文件的記錄(同時停止跟蹤),但不刪除工作區文件。

效果演示

假設當前有一個文件:

$ echo "test" > test.log
$ git add test.log
$ git commit -m "add log file"

現在 test.log 已經被 Git 跟蹤。

如果加了 .gitignore

*.log

這時 .gitignoretest.log 不起作用,因為它已經在倉庫歷史中被跟蹤了。

解決辦法:

git rm --cached test.log

執行結果:

rm 'test.log'

然後執行:

git status

會顯示:

deleted:    test.log

Git 認為你要“從版本控制中刪除它”,但文件還在硬盤上。

接着提交一次:

git commit -m "Stop tracking test.log"

最終效果:

  • test.log 在倉庫中被移除(不再跟蹤)
  • 本地文件還在(不會被刪除)
  • .gitignore 會開始對它生效(以後不會再被加入)

常見用法場景

場景 命令 説明
.gitignore 生效(停止跟蹤) git rm --cached <file> 典型用途
停止跟蹤整個目錄 git rm -r --cached <dir> 遞歸地移除目錄
停止跟蹤所有已跟蹤但應忽略的文件 git rm -r --cached . + git add . 重置索引(慎用)
移除緩存但不刪物理文件 git rm --cached 保留文件在工作區

與 git rm(不帶 --cached)的區別

命令 移除暫存區 刪除工作區文件 説明
git rm file 刪除文件並記錄提交(徹底刪)
git rm --cached file 僅從 Git 跟蹤中移除(保留物理文件)

查看哪些文件仍在索引中

可以用以下命令查看:

git ls-files

移除緩存前:

test.log

執行 git rm --cached test.log 後:

(test.log 不再出現)

高級用法

批量移除已跟蹤但應忽略的文件

# 查看所有已跟蹤的文件
git ls-files

# 結合 grep 找到特定模式的文件並移除
git ls-files | grep '\.tmp$' | xargs git rm --cached

# 或者使用 find 命令
find . -name "*.log" -exec git rm --cached {} \;

從所有提交歷史中完全刪除文件

如果文件包含敏感信息且已經推送到遠程,需要從歷史中完全刪除:

# 1. 使用 filter-branch 從所有提交中刪除文件
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch secrets.txt' \
  --prune-empty --tag-name-filter cat -- --all

# 2. 強制推送到遠程
git push origin --force --all

實際場景案例

遷移到大文件存儲 (Git LFS)

# 發現大文件已被普通 Git 跟蹤
git ls-files | grep -E '\.(psd|ai|zip)$'

# 從普通 Git 跟蹤中移除
git rm --cached design.psd large-video.mp4

# 配置 Git LFS
git lfs track "*.psd" "*.mp4"

# 重新添加文件(現在通過 LFS 跟蹤)
git add design.psd large-video.mp4
git commit -m "Migrate large files to LFS"

清理誤提交的依賴目錄

# 誤提交了 node_modules
git add .  # 不小心包含了 node_modules

# 從 Git 中移除
git rm --cached -r node_modules/

# 確保 .gitignore 包含 node_modules
echo "node_modules/" >> .gitignore

# 提交
git add .gitignore
git commit -m "Remove node_modules from version control"

分離環境配置文件

# 開發環境配置文件不應提交
git rm --cached config/dev.json

# 創建模板文件供其他開發者使用
cp config/dev.json config/dev.json.template
git add config/dev.json.template

echo "config/dev.json" >> .gitignore
git add .gitignore

git commit -m "Make dev config local only"

注意事項

  • 不會刪除物理文件(只改 Git 索引);
  • 必須提交一次,遠程倉庫才會真正刪除它;
  • 如果多人協作,建議在 .gitignore 裏同步寫入規則,防止其他人再次添加;
  • 想恢復跟蹤,只需:
git add <file>
git commit -m "track file again"

底層原理

git rm --cached 的工作機制涉及 Git 的核心數據結構:

索引(Index/Staging Area):

  • Git 的索引是一個二進制文件(.git/index),記錄暫存的文件狀態(內容哈希、模式、路徑)。
  • --cached 只修改索引,而不觸及工作目錄或對象數據庫(objects)。

安全檢查:

  • Git 要求暫存內容與分支 tip(最新提交的樹對象)或磁盤文件匹配。這防止你意外移除有本地修改的文件。
  • 如果不匹配,Git 報錯:error: path 'file.txt' is unmergeddid not match any files
  • 使用 -f 繞過此檢查。

對象影響:

  • 移除後,下次提交的樹對象(tree object)將不包含該文件。
  • 歷史記錄不受影響:舊提交中仍保留文件(通過 Blob 對象)。
  • 與快照機制相關:Git 存儲完整快照,但移除隻影響未來快照。

與 git add 的反向:

  • git add 將工作目錄文件添加到索引。
  • git rm --cached 是其逆操作,從索引移除。
user avatar aerfazhe Avatar q_bit Avatar mingtiaoiv Avatar zuiyuesi Avatar sukaaa Avatar iex365 Avatar
Favorites 6 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.