在Git中只提交文件部分更改

3555

当我在 Git 中修改一个文件时,如何只提交其中的一部分更改?

例如,如果我在文件中修改了 30 行,我只想提交其中的 15 行,应该怎么办呢?


7
相关链接 https://dev59.com/elsW5IYBdhLWcg3wwZjG:如果您需要将一个块分成更小的块。 - Trevor Boyd Smith
6
总结:就能力而言:git gui = git add -e > git add -i -p;就方便性而言:git gui > git add -i -p > git add -e。因此:当您可以访问X时,请选择git gui。对于简单的东西和当您没有或不想使用X时,请选择git add -i -p。对于复杂的暂存操作而无需X,请选择git add -e - Penghe Geng
如果使用VSCode,请参考https://dev59.com/U3NA5IYBdhLWcg3wH6AW#65649756。 - Wenfang Du
29个回答

4859

你可以使用:

git add --patch <filename>

或简称:

git add -p <filename>

Git会将您的文件分解为它认为合理的“块”(文件的部分)。然后,它会提示您这个问题:

Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?

以下是每个选项的描述:

  • y:将此hunk准备好下一次提交
  • n:不将此hunk准备好下一次提交
  • q:退出;不将此hunk或任何其余的hunk准备好下一次提交
  • a:将此hunk和文件中所有后续的hunk都准备好
  • d:不将此hunk或文件中任何后续的hunk准备好
  • g:选择要转到的hunk
  • /:搜索与给定正则表达式匹配的hunk
  • j:暂时保留此hunk未决,查看下一个未决的hunk
  • J:暂时保留此hunk未决,查看下一个hunk
  • k:暂时保留此hunk未决,查看上一个未决的hunk
  • K:暂时保留此hunk未决,查看上一个hunk
  • s:将当前hunk分割成更小的hunk
  • e:手动编辑当前hunk
    • 然后,您可以通过用# 替换+/-来手动编辑hunk(感谢veksen
  • ?:打印hunk帮助

如果文件尚未在存储库中,您可以首先运行git add -N <filename>。 然后,您可以继续运行git add -p <filename>

之后,您可以使用以下命令:

  • git diff --staged检查您是否已准备好正确的更改
  • git reset -p取消暂存错误添加的hunk
  • git commit -v在编辑提交消息时查看您的提交。

请注意,这与git format-patch命令大不相同,其目的是将提交数据解析为.patch文件。

参考文献:Git 工具 - 交互式添加


138
值得注意的是,-p/--patch-i/--interactive 命令中补丁操作的快捷方式,该命令启动了有用的交互模式 - tutuDajuju
8
如果该文件已经被暂存,会显示未暂存的更改,就像 git diff 命令一样。我会将其翻译为:文件已经暂存,只会显示未暂存的更改,与 git diff 命令相同。 - Eugen Konkov
4
我该如何手动编辑当前的代码块?在输入 e 后,我不知道该怎么做。 - Hunsu
49
按下 e 后,您可以通过将 +- 替换为 # 来手动编辑代码块。 - veksen
6
当我执行git commit file时,它提交了所有内容而不仅仅是我想要的特定更改(即使git diff --staged只显示了我想要的特定更改)。编辑:使用git commit -p可以选择“块”并一次性提交;而且它确实只提交了特定的更改。 - Rafa
显示剩余11条评论

290
你可以使用git add --interactivegit add -p <文件>,之后使用git commit(而不是git commit -a);请参阅git-add手册中的交互模式,或者按照指示操作。
现代Git还有git commit --interactive(以及git commit --patch,这是交互式提交中补丁选项的快捷方式)。
如果你更喜欢通过GUI来完成,你可以使用git-gui。你只需右键单击要包含在提交中的块并选择:

为提交暂存块

如果你只想添加块中的某些行,你也可以选择它们,然后右键单击:

为提交暂存行

enter image description here

我个人认为这比使用git add -i更容易。其他像QGit或GitX这样的git GUI也可能具有此功能。


1
有趣的是,windows.github.com 曾经支持部分文件提交,但最近似乎已经取消了该功能。 - Juri
1
@Juri 我认为部分文件提交的支持已经恢复了。 - Ela782
@Juri 不用谢。其实我之前从没注意到它已经在里面了——上周我看到它时,还想着“哦,这是一个很棒的新功能”! :-) - Ela782
1
现在,windows.github.com 重定向到重新品牌的 GitHub Desktop,它比 Git GUI 更好,并支持部分提交...但不支持提交签名。唉。 - user4942583
@user4942583:GitHub Desktop 一年前已经添加了 GPG 提交签名功能。 - kaios

181

git gui 在差异视图下提供了此功能。只需右键单击您感兴趣的行,就会看到一个“将此行暂存以进行提交”菜单项。


18
对于复杂的补丁,这通常是我最快的方法。 - hochl
9
这是一种非常高效和直观的方法,可以以细粒度方式向暂存区添加更改。同时,可以选择多行,并将该选择中的所有更改添加进去。 - jox
3
请注意,这不是gitk,但它包含在Git Bash for Windows中;你应该有一个开始菜单项或可以使用命令git gui启动它。还有一个名为“stage this hunk”的选项,可能比“stage this line”更有用。自从10年前创建了此答案后,可能已经更新过了。 - Chris
那正是我所需要的 :) - StayCool
1
它似乎也是唯一一个在Linux上支持此功能的软件 :/ - kdb
我在Ubuntu 20.04上没有这个。 - qwr

121

我认为git add -e myfile是最简单的方法(至少是我偏爱的方法),因为它只是打开一个文本编辑器,让您选择要暂存的行和不想暂存的行。

关于编辑命令:

添加内容:

添加的内容以“+”开头的行表示。您可以通过删除这些行来防止将任何添加行暂存。

删除内容:

删除的内容以“-”开头的行表示。您可以将“-”转换为空格,以防止暂存其删除。

修改内容:

修改的内容由“-”行(删除旧内容)和“+”行(添加替换内容)组成。您可以通过将“-”行转换为空格并删除“+”行来防止暂存修改。请注意,仅修改其中一半很可能会向索引中引入混乱的更改。

有关git add的所有详细信息都可以在git --help add上找到。


13
如果有一个清晰的解释告诉我们如何选择这些行(即输入实际的命令),那么这会更有用。我在文档中没有找到这个解释。你能否添加一个参考资料? - Alex
12
不喜欢使用速记选项的人,-e 相当于 --edit - Kolyunya
3
@Alex theFreedomBanana添加的参考引用来自于git --help add中的“编辑补丁”部分。 - Randall
14
这是唯一一个答案(只需要使用Git),可以解决在交互式打补丁模式下无法通过 s 使块变小,从而添加/删除每行的问题。 - cdosborn
11
理解这个命令(git add -e)并不会改变磁盘上的文件内容,它只是将部分更改从未暂存的状态转移到了已暂存的状态(索引)。 - Penghe Geng
显示剩余4条评论

71

如果你正在使用VS Code,那么你很幸运。选择要暂存的代码段,然后使用Git: Stage Selected Ranges命令将它们暂存,如果需要的话,进行提交。

我录制了一个gif来演示我的意思:

enter image description here


有对应的git命令吗?逐行添加,而不是自动生成巨块? - sayandcode
1
@sayandcode 你可以尝试这个,但我觉得在这种情况下GUI更方便 :)。 - Wenfang Du
我希望1)他们的下一个块快捷键(Alt + F5)可以横跨文件: https://github.com/microsoft/vscode/issues/24389 | https://github.com/microsoft/vscode/issues/50434 和2)如果您想要整个块,有一种方法可以添加块而无需选择行: https://github.com/microsoft/vscode/issues/48323 | https://github.com/Microsoft/vscode/issues/13740。 - Ciro Santilli OurBigBook.com

51
如果你使用vim,可以尝试一下优秀的插件fugitive。通过:Gdiff命令,你可以看到一个文件在工作副本和索引之间的差异,然后使用经典的vim diff命令(如dp)将行或hunks添加到索引中。将修改保存在索引中并使用:Gcommit提交即可。非常好的入门演示视频here(特别是part 2部分)。

非常感谢您提供这些链接。正是我所需要的。特别是在可视模式下使用:diffget/:diffput,我可以选择要重置/提交的特定行。所以,请再次确认:vim真的很棒。 - goodniceweb

40

值得注意的是,如果要对新文件使用git add --patch命令,您需要先使用git add --intent-to-add将其添加到索引中:

git add -N file
git add -p file

40

我强烈建议使用Atlassian的SourceTree。(它是免费的。) 它使得这个琐碎的工作变得容易。你可以快速轻松地分阶段提交单独的代码块或单独的代码行。

enter image description here


2
我认为SourceTree是一个很好的工具,因为它比命令行提供了更精细的控制。 - user456814
22
@cupcake我会持相反的观点,因为SourceTree可能使用那些命令行git可执行文件,所以本质上,通过“命令行”做同样(或更多)细粒度的操作总是可能的。 - tutuDajuju
3
无论 细粒度 的争议如何,我强烈推荐使用 SourceTree,因为对暂存块和单独行进行分段非常容易:http://i.imgur.com/85bNu4C.png - Mars Robertson
1
如果您只想提交50-100行,而不是1-50行,但SourceTree将1-100行视为一个“hunk”,那么您该怎么办呢? - Sun
5
@Sun,您可以单击第50行,然后按住Shift键再单击第100行,然后将它们暂存。您可以在Sourcetree中轻松地将第50-100行进行暂存 :) - CodeMonkey123

23

当我有很多更改,并且最终要从这些更改中创建一些提交时,我希望在暂存这些更改之前临时保存我的起点。

像这样:

$ git stash -u
Saved working directory and index state WIP on master: 47a1413 ...
$ git checkout -p stash
... step through patch hunks
$ git commit -m "message for 1st commit"
$ git checkout -p stash
... step through patch hunks
$ git commit -m "message for 2nd commit"
$ git stash pop

Whymarrh的回答是我通常所做的,但有时候有很多变化,我可以感觉到在暂存事物时可能会犯错,因此我希望有一个已提交的状态,以便我可以进行第二次尝试。


17

在之前的回答基础上,如果您更喜欢使用命令行,输入 git add -e myfile 将会让您可以逐行选择要提交的内容,因为这个命令会打开一个编辑器显示差异,如下所示:

输入图像描述

您可能已经知道以 + 开头的行是添加的,以 - 开头的行是删除的。所以:

  • 要取消提交一个添加,只需删除该行。
  • 要取消提交一个删除,只需将 - 替换为空格

关于这种方式(补丁文件)添加文件,git add -h上有这样的说明:

添加的内容 添加的内容以“+”开头的行表示。您可以通过删除它们来防止暂存任何添加的行。

删除的内容: 删除的内容以“-”开头的行表示。您可以通过将“-”转换为“ ”(空格)来防止暂存它们的删除。

修改的内容: 修改的内容以“-”行(删除旧内容)后面跟着“+”行(添加替换内容)表示。您可以通过将“-”行转换为“ ”来防止暂存修改

注意:不要更改文件的内容,这不是更改文件内容的好地方。只需更改已删除或添加行的运算符即可。同时要注意删除“-”行并移除“+”行会导致索引混乱。


什么之前的答案?答案是根据它们所拥有的点数存储的。因此,与其他答案相比,您的答案现在所处的位置与您编写它时不同。 - KulaGGin
我认为所提及的答案是来自@theFreedomBanana。 - K. Symbol

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