Git:删除给定分支上某一文件的所有更改的最佳方法

18

我的分支有大约20个提交,比较混乱。现在我正在准备将其合并回主分支。我已经将其 rebase 到主分支,并仔细查看后发现有些文件被修改,但这些修改与当前分支无关,也没有准备好被提交。对于这些文件的更改并不限于特定的提交。

所以基本上,如果可能的话,我不希望这些文件中的任何内容包含在这个分支中。有没有好的方法来处理这个问题?我的备选方案 #1 显然是只复制每个文件的最新版本,然后提交。但是历史记录仍将包含更改,Git之神会不满意。

备选方案 #2 是执行相同的操作,然后将整个分支历史压缩为一个提交。

还有其他更好的方法吗?

3个回答

19

假设你的历史记录为

$ git lola
* 6473d7f (master) 更新
| * 9bcfa7e (HEAD, topic) 修改 a、b 和 c
| * 99af942 修改 b 和 c
| * 8383e2c 修改 a 和 b
|/
* d1363f4 基准线

注意:lola 是一个非标准但有用的别名。

这些提交修改了三个不同的文件。

$ git log --decorate=short --pretty=oneline --name-status topic
9bcfa7e946a92c226ad50ce430a9e4ae55b32490 (HEAD, topic)
M       a
M       b
M       c
99af942dbb922effcad8a72e96bec9ee9afcc437 修改 b 和 c
M       b
M       c
8383e2c8d6092550fec13d3c888c037b3a68af15 修改 a 和 b
M       a
M       b
d1363f4fba67d94999b269b51bdb50a8a68ba27a 基准线
A       a
A       b
A       c

您想保留对文件 b 的更改,并且要丢弃所有对 ac 的更改。使用 git filter-branch 可以实现这一点。

$ git checkout -b tmp topic
切换到新分支“tmp”

$ git merge-base topic master
d1363f4fba67d94999b269b51bdb50a8a68ba27a

$ git filter-branch --tree-filter 'git checkout d1363f -- a c' master..tmp
重写 8383e2c8d6092550fec13d3c888c037b3a68af15 (1/3) 重写 99af942dbb922effcad8a72e96bec9ee9afcc437 (2/3) 重写 9bcfa7e946a92c226ad50ce430a9e4ae55b32490 (3/3) 参考 'refs/heads/tmp' 被重写

上述的树过滤器检查了指定范围内的提交并将文件 ac 恢复到所谓的“合并基础”内容,也就是 topic 分支与 master 分支分离时的提交。

现在,tmp 分支拥有 topicb 的所有更改,但没有对其他任何文件的更改。

$ git log --decorate --pretty=oneline --name-status tmp
9ee7e2bd2f380cc338b0264686bcd6f071eb1087 (HEAD, tmp) 修改 a、b、c
M       b
226c22f150af1ddc1f9adc19f97fc4f220851ada 修改 b 和 c
M       b
45e706f7b22c37ee2025ee0d04c651135e7b31cd 修改 a 和 b
M       b
d1363f4fba67d94999b269b51bdb50a8a68ba27a 基准
A       a
A       b
A       c

作为安全措施,git filter-branch 会存储原始引用的备份。当您满意更改并想要删除备份 tmp 时,请运行以下命令:

$ git update-ref -d refs/original/refs/heads/tmp

4
+1 绝佳回答。一旦你掌握了 filter-branch 这个工具,它就非常好用了。 - ralphtheninja
1
这里有很多好东西我还在消化中。“log --name-status <branch>”很酷。 - Steve Bennett
我按照这个出色答案的所有步骤进行操作,一切都很顺利,直到filter-branch结束时出现了“警告:Ref 'refs/heads/tmp'未更改”的提示。检查历史记录,文件修改仍然存在,而且据我所知,没有任何提交ID发生了变化,尽管git报告说已经“重写”了它们所有。嗯。 - Steve Bennett
啊, "git checkout" 命令的参数被当做大小写敏感处理了(尽管我使用的是 Windows)。现在它可以正常工作了。运行 "git log" 命令时出现了两个奇怪的情况:即使在 diff 中没有更改,它仍然将文件列为已修改,并保留旧的提交 ID(但现在以黄色显示)。这个命令有点可怕! - Steve Bennett
错误,下一个问题:如何从refs/original/heads/...中看到的备份中恢复? - Steve Bennett
@SteveBennett 任君选择。运行git refloggit for-each-ref以获取旧分支的SHA-1。使用git checkout -b old refs/original/refs/heads/tmpgit checkout -b $sha1复活旧分支。 - Greg Bacon

1
要检出特定版本,您可以执行以下操作: git checkout <sha1> <file> 其中sha1是您想要文件所在版本的唯一sha1哈希值。

好奇如果在一个分支上检入以这种方式恢复的文件,是否会从分支历史记录中彻底删除该文件的任何更改? - colm.anseo

0

作为 Windows 用户,我遇到了一些问题:

  1. 在 --tree-filter 命令中使用双引号。
  2. 对于文件路径,请使用正斜杠。
  3. 不要以正斜杠开头文件路径。

以下是我的命令最终看起来的方式:

git filter-branch -f --tree-filter "git checkout a59f75 -- source/_posts/blog1.md source/_posts/blog2.md" master..tmp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接