使用 Git 2.25(2020 年第一季度,五年后),Perl 不应再成为git add
.
Here, "git add -i
" 正在用 C 重写。
警告,自 Git 2.37(2022 年第 2 季度)以来,C 重写是默认的.
请参阅此答案的末尾。
See commit 8c15904, commit 3d965c7 (15 Nov 2019), and commit 1daaebc (13 Nov 2019) by Slavica Đukić (slavicaDj).
See commit 68db1cb, commit 76b7432, commit 6348bfb (15 Nov 2019), and commit f83dff6 (13 Nov 2019) by Johannes Schindelin (dscho).
See commit 5e82b9e, commit e4cb659 (13 Nov 2019) by Daniel Ferreira (theiostream).
(Merged by Junio C Hamano -- gitster -- in commit f7998d9, 05 Dec 2019)
built-in add -i: 实施help
command
Signed-off-by: Slavica Đukić
Signed-off-by: Johannes Schindelin
这模仿了显示 Perl 脚本中的帮助文本的代码git-add--interactive.perl
在内置版本中。
And:
built-in add -i:显示命令的唯一前缀
Original-patch-by: Slavica Đukić
Helped-by: SZEDER Gábor
Signed-off-by: Johannes Schindelin
就像 Perl 脚本中一样git-add--interactive.perl
,对于每个命令都有一个唯一的prefix确定(如果给定参数中存在任何参数),并显示在列表中,并接受作为命令的快捷方式。
为了确定唯一的前缀以及查找相关命令,我们使用列表的副本并对其进行排序。
虽然这对于单个命令来说似乎有点大材小用,但当所有命令都实现时,并且当我们重用相同的逻辑来提供要编辑的文件列表并使用方便的唯一前缀时,它会更有意义。
在这个补丁系列的开发之初,引入了一种模仿 Perl 版本实现的 Trie 的专用数据结构。然而,这被认为是多余的,我们现在只需对列表进行排序,然后通过查看每个项目的邻居来确定唯一前缀的长度。作为奖励,我们现在使用相同的排序列表来使用用户提供的前缀作为搜索键来执行二分搜索。
add-interactive.c解释:
“前缀项目列表”是由字符串标识的项目的列表,并且为每个项目确定唯一的前缀(如果有)。
一开始:
git add -i:开始实现内置版本git add --interactive
Signed-off-by: Johannes Schindelin
与之前的 C 转换不同,我们从内置助手开始,我们通过在run_add_interactive()
新选择加入时的功能add.interactive.useBuiltin
配置旋钮已打开(或相应的环境变量GIT_TEST_ADD_I_USE_BUILTIN
),并调用新的内部API函数run_add_i()
直接实现在libgit.a
.
整个转换过程可以在 PR 中找到#170-175 at github.com/gitgitgadget/git.
The "--helper
不幸的是,这里不能使用“方法”:在 Windows 上,我们面临一个非常具体的问题,即system()
Perl 中的调用似乎已关闭stdin
在父进程中,当生成的进程消耗甚至一个字符时stdin
。这阻止我们在 C 中实现主循环并仍然尝试将其交给 Perl 脚本。
我们在这里必须采取的方法的真正缺点是测试套件不会通过GIT_TEST_ADD_I_USE_BUILTIN=true
直到转换完成(--helper
即使在每个增量转换步骤中,方法也会让它通过)。
注意:使用 Git 2.25,您可以看到错误消息(报告在git-for-windows/git第 2466 期):
BUG: pathspec.c:555: PATHSPEC_PREFER_CWD requires arguments
这应该在 Git 2.25.2(2020 年 3 月)中修复。
See commit 849e43c, commit d660a30 (16 Jan 2020) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit f094074, 30 Jan 2020)
built-in add -i:再次接受开放式范围
Signed-off-by: Johannes Schindelin
互动式add
命令允许通过唯一的前缀、索引或索引范围为其某些子命令选择多个文件。
重新实施时git add -i
在 C 中,我们甚至添加了一个代码注释,讨论缺少结束索引的范围,例如2-
,但代码实际上并没有接受这些,正如 a 中指出的那样git-for-windows/git 问题 2466 的评论 by qhill.
顺便说一下重写git add --interactive
in C
破坏了使用开放范围选择文件的能力,例如Patch update>> 2-
选择除第一个文件之外的所有文件。
这与此问题相关,因为尝试此操作会导致与此问题相同的行为(即 2.24.1.windows.2 中的 BUG 和 2.25.0.windows.1 中的立即退出)。
让我们解决这个问题,并添加一个测试用例来验证这个问题是否永远保持不变。
使用 Git 2.26(2020 年第一季度),努力移动“git-add--interactive
“到C继续。
See commit c480eeb, commit cee6cb7, commit 52628f9, commit 6610e46, commit 90a6bb9, commit 36bae1d, commit d2a233c (21 Dec 2019) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit 9a5315e, 05 Feb 2020)
内置add -p:准备“stage”以外的补丁模式
Signed-off-by: Johannes Schindelin
Perl 脚本支持git add -p
不仅用于该命令,还用于git stash -p
, git reset -p
and git checkout -p
.
为C语言版本的教学做准备git add -p
为了也支持后面的命令,让我们将特定于“阶段”的内容抽象为描述补丁模式之间差异的专用数据结构。
最后,请注意 Perl 版本尝试确保仅为修改后的文件生成差异。
这实际上并不是必需的,因为对 Git 的 diff 机制的调用已经执行了该工作,并且执行得很好。这使得无需移植FILTER
领域的%patch_modes
结构体,以及get_diff_reference()
功能。
Git 2.37 附带git add -i默认情况下在 C 中.
如果您有任何问题,例如msys2/MSYS2-packages问题 3066,尝试解决方法git config --global add.interactive.useBuiltin=false
.
That particular issue should be resolved with Git 2.37.1 (Q3 2022): rewrite of "git add -i"(man) in C that appeared in Git 2.25 did not correctly record a removed file to the index, which was fixed.
See commit 4788e8b (28 Jun 2022) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit 0f0bc21, 02 Jul 2022)
add --interactive: allow update
暂存已删除的文件
Reported-by: Christoph Reiter
Signed-off-by: Johannes Schindelin
The scripted version of git add -i(man) used git update-index --add --remove``(man), but the built-in version implemented only the --add
part.
这修复了https://github.com/msys2/MSYS2-packages/issues/3066
问题是,在使用添加交互时:
git add -i
fatal: unable to stat 'myfile': No such file or directory
的后果add.interactive.useBuiltin
:
With Git 2.38 (Q3 2022), fix deadlocks between main Git process and subprocess spawned via the pipe_command()
API, that can kill "git add -p"(man) that was reimplemented in C recently.
See commit 716c1f6, commit c6d3cce, commit 14eab81, commit ec4f39b, commit 10f7433 (17 Aug 2022) by Jeff King (peff).
See commit 24b56ae (17 Aug 2022) by René Scharfe (rscharfe).
(Merged by Junio C Hamano -- gitster -- in commit a103ad6, 25 Aug 2022)
pipe_command():将标准输入描述符标记为非阻塞
Signed-off-by: Jeff King
Our pipe_command()
helper 允许您在子进程的 stdin/stdout 上写入和读取。
它应该可以无死锁地工作,因为我们使用poll()
检查描述符何时准备好读取或写入。
但有一个错误:如果要写入的数据和要读回的数据都超过管道缓冲区,我们就会死锁。
问题是代码假设如果您有一个 2MB 的缓冲区要写入并且poll()
告诉您管道描述符已准备好写入,调用:
write(cmd->in, buf, 2*1024*1024);
将进行部分写入,填充管道缓冲区,然后返回它所写入的内容。
这就是它在套接字上执行的操作,但不适用于管道。
当写入管道时,至少在 Linux 上,它将阻塞等待子进程read()
more.
现在我们有一个潜在的僵局,因为孩子可能正在给我们回信,等待我们read()
我们自己。
触发此操作的一个简单方法是:
git -c add.interactive.useBuiltin=true \
-c interactive.diffFilter=cat \
checkout -p HEAD~200
与 HEAD~200 的差异将会很大,并且过滤器想要将所有内容写回给我们(显然这是一个虚拟过滤器,但在现实世界中,像 diff-highlight 这样的东西同样会流回一个大输出)。
如果你设置add.interactive.useBuiltin
为 false,问题就消失了,因为现在我们不使用pipe_command()
不再了(相反,这部分发生在 Perl 中)。
但这根本不是交互式代码中的错误。
这是底层pipe_command()
代码已经被破坏,并且一直存在。