270 lines
5.6 KiB
Markdown
270 lines
5.6 KiB
Markdown
# 清理 Git 历史中的本地目录模板
|
||
|
||
这份模板用于把某个已经推送过的本地目录,从远端仓库的历史提交中移除。
|
||
|
||
适用场景:
|
||
|
||
```text
|
||
某个目录已经被 push 到远端
|
||
后来发现它不适合公开,例如简历、密码本、密钥、隐私资料
|
||
希望远端最新版本和历史提交中都不再包含它
|
||
但本地仍然要保留这个目录
|
||
```
|
||
|
||
## 参数
|
||
|
||
把下面这些参数替换成自己的实际值:
|
||
|
||
```text
|
||
<原仓库路径> 当前正在使用的本地仓库路径
|
||
<旧仓库备份路径> 原仓库的备份路径
|
||
<清理仓库路径> 新 clone 出来的临时清理仓库路径
|
||
<远端地址> GitHub/Gitee 等远端仓库地址
|
||
<分支名> 要重写并推送的分支,例如 master 或 main
|
||
<目录路径> 要从历史中移除的目录,例如 简历/
|
||
```
|
||
|
||
多个目录可以写多个 `--path`:
|
||
|
||
```powershell
|
||
git filter-repo --path "<目录1>/" --path "<目录2>/" --invert-paths
|
||
```
|
||
|
||
## 重要提醒
|
||
|
||
如果目录里包含密码、Recovery Key、Token 等敏感信息,应先更换或吊销这些信息。
|
||
|
||
清理 Git 历史只能减少仓库历史中的暴露,不等于保证已经泄露的信息重新安全。
|
||
|
||
重写历史后,提交 ID 会变化,因此远端需要强推:
|
||
|
||
```powershell
|
||
git push --force-with-lease origin <分支名>
|
||
```
|
||
|
||
`--force-with-lease` 比 `--force` 更安全。它会先检查远端分支是否仍停在本地记录的位置,避免覆盖别人新推送的提交。
|
||
|
||
## 流程
|
||
|
||
### 1. 检查原仓库状态
|
||
|
||
进入当前本地仓库:
|
||
|
||
```powershell
|
||
cd "<原仓库路径>"
|
||
git status
|
||
```
|
||
|
||
如果有未提交修改,先提交、备份或手动处理。不要在状态混乱时重写历史。
|
||
|
||
### 2. 备份原仓库
|
||
|
||
把当前仓库复制或改名保留:
|
||
|
||
```powershell
|
||
cd "<原仓库的上级目录>"
|
||
ren "<原仓库文件夹名>" "<旧仓库备份文件夹名>"
|
||
```
|
||
|
||
也可以用资源管理器手动复制一份。
|
||
|
||
重点是保留本地私有目录,后面要从 old 里复制回新仓库。
|
||
|
||
### 3. 在非同步目录重新 clone
|
||
|
||
建议不要在 OneDrive、网盘同步目录里执行历史重写。
|
||
|
||
```powershell
|
||
cd "<临时工作目录>"
|
||
git clone <远端地址> "<清理仓库文件夹名>"
|
||
cd "<清理仓库路径>"
|
||
```
|
||
|
||
### 4. 清理历史
|
||
|
||
清理单个目录:
|
||
|
||
```powershell
|
||
git filter-repo --path "<目录路径>/" --invert-paths
|
||
```
|
||
|
||
清理多个目录:
|
||
|
||
```powershell
|
||
git filter-repo --path "<目录1>/" --path "<目录2>/" --invert-paths
|
||
```
|
||
|
||
说明:
|
||
|
||
```text
|
||
--path 指定要匹配的路径
|
||
--invert-paths 反转选择,保留其他路径,移除指定路径
|
||
```
|
||
|
||
如果提示不是 fresh clone,可以在确认备份无误后使用:
|
||
|
||
```powershell
|
||
git filter-repo --force --path "<目录路径>/" --invert-paths
|
||
```
|
||
|
||
### 5. 检查并恢复 remote
|
||
|
||
`git filter-repo` 可能会删除 `origin` remote。
|
||
|
||
检查:
|
||
|
||
```powershell
|
||
git remote -v
|
||
```
|
||
|
||
如果没有输出,重新添加:
|
||
|
||
```powershell
|
||
git remote add origin <远端地址>
|
||
```
|
||
|
||
### 6. 修改 .gitignore
|
||
|
||
把本地要保留、但以后不再提交的目录写入 `.gitignore`:
|
||
|
||
```gitignore
|
||
/<目录路径>/
|
||
```
|
||
|
||
例如:
|
||
|
||
```gitignore
|
||
/简历/
|
||
```
|
||
|
||
提交 `.gitignore` 修改:
|
||
|
||
```powershell
|
||
git add .gitignore
|
||
git commit -m "Ignore local-only directory"
|
||
```
|
||
|
||
### 7. 获取远端状态
|
||
|
||
执行:
|
||
|
||
```powershell
|
||
git fetch origin
|
||
```
|
||
|
||
`fetch` 不是合并。它只是把远端当前状态下载到本地,例如更新 `origin/<分支名>`。
|
||
|
||
这里的作用是让 `--force-with-lease` 能检查远端分支是否被别人更新过。
|
||
|
||
### 8. 强推清理后的历史
|
||
|
||
```powershell
|
||
git push --force-with-lease origin <分支名>
|
||
```
|
||
|
||
如果成功,远端 `<分支名>` 会被替换成清理后的新历史。
|
||
|
||
### 9. 检查清理结果
|
||
|
||
检查历史中是否还存在目标目录:
|
||
|
||
```powershell
|
||
git log --all -- "<目录路径>/"
|
||
```
|
||
|
||
没有输出,说明当前可见历史里已经找不到这个目录。
|
||
|
||
也可以检查远端分支:
|
||
|
||
```powershell
|
||
git fetch origin
|
||
git log origin/<分支名> -- "<目录路径>/"
|
||
```
|
||
|
||
### 10. 重建日常使用的本地仓库
|
||
|
||
回到原来的仓库位置,从远端重新 clone:
|
||
|
||
```powershell
|
||
cd "<原仓库的上级目录>"
|
||
git clone <远端地址> "<原仓库文件夹名>"
|
||
```
|
||
|
||
### 11. 复制回本地私有目录
|
||
|
||
从 old 备份中,把本地要保留但不提交的目录复制回新仓库:
|
||
|
||
```text
|
||
<旧仓库备份路径>/<目录路径>
|
||
复制到
|
||
<原仓库路径>/<目录路径>
|
||
```
|
||
|
||
### 12. 最终验证
|
||
|
||
进入新仓库:
|
||
|
||
```powershell
|
||
cd "<原仓库路径>"
|
||
git status
|
||
git check-ignore -v --no-index -- "<目录路径>/任意测试文件.md"
|
||
git log --all -- "<目录路径>/"
|
||
```
|
||
|
||
理想结果:
|
||
|
||
```text
|
||
git status 没有异常改动
|
||
git check-ignore 能看到 .gitignore 命中规则
|
||
git log 没有输出
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### filter-repo 不是 Git 命令
|
||
|
||
报错:
|
||
|
||
```text
|
||
git: 'filter-repo' is not a git command
|
||
```
|
||
|
||
说明还没有安装 `git-filter-repo`。
|
||
|
||
可以尝试:
|
||
|
||
```powershell
|
||
python -m pip install git-filter-repo
|
||
```
|
||
|
||
### push 被拒绝 stale info
|
||
|
||
如果强推时报:
|
||
|
||
```text
|
||
! [rejected] <分支名> -> <分支名> (stale info)
|
||
```
|
||
|
||
通常是因为本地没有最新的 `origin/<分支名>` 信息。
|
||
|
||
先执行:
|
||
|
||
```powershell
|
||
git fetch origin
|
||
git push --force-with-lease origin <分支名>
|
||
```
|
||
|
||
### .gitignore 不会影响已跟踪文件
|
||
|
||
`.gitignore` 只影响未被跟踪的新文件。
|
||
|
||
如果某个文件已经被 Git 跟踪,需要取消跟踪:
|
||
|
||
```powershell
|
||
git rm --cached -r -- "<目录路径>"
|
||
git commit -m "Stop tracking local-only directory"
|
||
git push
|
||
```
|
||
|
||
这只会从 Git 跟踪列表中移除,不会删除本地实际文件。
|