Git:如何在重命名文件时进行交互式添加(-p)

4
假设我修改了一个已经进行版本控制的文件,并且使用非GIT方式将其重命名,但我只想包含该文件的部分差异。我该怎么做?
问题在于GIT只能添加完整的新文件。
这种情况可能更好地通过示例理解:
$ mkdir temp
$ cd temp
$ git init
$ nano 1.txt
$ cat 1.txt
I am file 1.txt
2
3
4
5

$ git add 1.txt
$ git commit -m "1st commit"
$ #edit some stuff and rename using something else than git
$ cat 2.txt #note, 1.txt is renamed to 2.txt
1 I am file 1.txt
2 I am now file 2.txt
3
4
5
6

$ #note the line with '6 'is added

现在我的选择是什么,只提交将"2"的行更改为 "2 I am now file 2.txt" 和重命名的更改,而不提交添加 '6' 行的更改。由于IDE中可能存在许多重命名,因此外部重新更名并使用 git mv 不是真正的解决方法。基本上,我希望获得通过执行 git mv 1.txt 2.txt 正常情况下会有的情况,在这种情况下,您可以交互式地选择要提交的 2.txt 中的哪些行。

您想为文件取什么最终名称? - Math
正如这个(人为制造的)例子所示,2.txt是一个文件名,但对于问题本身并不重要。 - hbogert
事实上,使用mv 2.txt 1.txt(不是git mv,而是mv)可以解决问题,因为git会跟踪文件的名称。 - Math
单独执行 rm 1.txt 命令也能产生你期望的效果,因为如果两个文件足够相似,git会将 2.txt 视为是 1.txt:https://dev59.com/FnRC5IYBdhLWcg3wCMg6#433142 - Math
我认为你可以使用 git mv --cached oldfile newfile 来实现这个目标。不幸的是,--cachedgit mv 中无法使用(只能在 git rm 中使用)。 - Ajedi32
2个回答

3
git add 命令有一个很棒的 -N 标记:
-N, --intent-to-add
    Record only the fact that the path will be added later. An entry for
    the path is placed in the index with no content. This is useful for,
    among other things, showing the unstaged content of such files with
    git diff and committing them with git commit -a.

毋庸置疑,这个标志也适用于git add -p
不需要使用git mv。正如@Ajedi32在下面正确指出的那样,git mv没有做任何特殊处理。索引条目重命名(rename_index_entry_atread-cache.c中)归结为:
remove_index_entry_at(istate, nr);
add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);

编辑。(根据@Math上面的评论)。由于git跟踪文件名,只需要两个命令:

mv 2.txt 1.txt                # Rename it back to the first name
git mv 1.txt git 2.txt        # Tell git you've moved it

git add -p -- 2.txt           # You can now stage the hunks


虽然这个方法可行,但我已经考虑过了,这就是为什么我在问题中添加了“Externally renaming back and using git mv is not really a solution in my case, because of the many renames possible by the refactoring in an IDE.”这一行。如果没有更好的方法而不得不重新命名,我会接受它。此外,在你的代码片段的第二行中是否应该有第二次提到'git'? - hbogert
1
据我所知,git mv并不会告诉git你是否移动了文件,它只是帮助你更新索引:https://dev59.com/_XNA5IYBdhLWcg3wF5uO 那么这样做真的有效吗?如果有效,为什么呢? - Ajedi32
@Ajedi32 是的,那个策略确实有效。我把它作为可能的解决方案用于今天提交我的日常工作。 - hbogert
谈论read-cache.c,rename_index_entry_at还执行以下操作:copy_cache_entry(new,old);`这可能解释了为什么如果使用git mv,则可以添加块吗? - hbogert

1
如果我理解正确,你的问题源于无法交互地添加块(使用-p)到新建的文件,只能添加到修改过的文件。
你可以使用-N标志告诉git你打算添加该文件,然后git add -p应该就可以工作了。
$ mv 1.txt 2.txt    # not `git mv`
$ git add -N 2.txt  # Stages an empty file; you can now see the contents with `git diff`
...
$ git add -p        # Will include changes to `2.txt`

这难道不会导致git add -p中显示的差异是"所有内容都被添加到了 2.txt 中"? 还是说git会检测到重命名并以某种方式为您提供更精细的选项来分阶段地处理文件的部分内容? - Ajedi32
@Ajedi32,确实这不允许分阶段处理块,只能处理整个文件。 - hbogert

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