我在TXR 中创建了这个实验性且测试不佳的程序:
示例运行:首先我们在 repo 中的位置:
$ git diff
diff --git a/lorem.txt b/lorem.txt
index d5d20a4..58609a7 100644
--- a/lorem.txt
+++ b/lorem.txt
@@ -2,10 +2,14 @@ Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor
incididunt ut labore et dolore
-magna aliqua. Ut enim ad minim
+minim
+minim
veniam, quis nostrud
exercitation ullamco laboris
+maxim
+maxim
nisi ut aliquip ex ea commodo
+minim
consequat. Duis aute irure
dolor in reprehenderit in
voluptate velit esse cillum
还有:
$ git diff --cached # nothing staged in the index
目标是只提交包含匹配 min 的行:
$ txr addmatch.txr min lorem.txt
patching file .merge_file_BilTfQ
现在是什么状态?
$ git diff
diff --git a/lorem.txt b/lorem.txt
index 7e1b4cb..58609a7 100644
--- a/lorem.txt
+++ b/lorem.txt
@@ -6,6 +6,8 @@ minim
minim
veniam, quis nostrud
exercitation ullamco laboris
+maxim
+maxim
nisi ut aliquip ex ea commodo
minim
consequat. Duis aute irure
还有:
$ git diff --cached
diff --git a/lorem.txt b/lorem.txt
index d5d20a4..7e1b4cb 100644
--- a/lorem.txt
+++ b/lorem.txt
@@ -2,10 +2,12 @@ Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor
incididunt ut labore et dolore
-magna aliqua. Ut enim ad minim
+minim
+minim
veniam, quis nostrud
exercitation ullamco laboris
nisi ut aliquip ex ea commodo
+minim
consequat. Duis aute irure
dolor in reprehenderit in
voluptate velit esse cillum
匹配的东西在索引中,不匹配的+maxim 行仍然未暂存。
addmatch.txr中的代码:
@(next :args)
@(assert)
@pattern
@file
@(bind regex @(regex-compile pattern))
@(next (open-command `git diff @file`))
diff @diffjunk
index @indexjunk
--- a/@file
+++ b/@file
@(collect)
@@@@ -@bfline,@bflen +@afline,@aflen @@@@@(skip)
@ (bind (nminus nplus) (0 0))
@ (collect)
@ (cases)
@line
@ (bind zerocol " ")
@ (or)
+@line
@ (bind zerocol "+")
@ (require (search-regex line regex))
@ (do (inc nplus))
@ (or)
-@line
@ (bind zerocol "-")
@ (require (search-regex line regex))
@ (do (inc nminus))
@ (or)
-@line
@;; unmatched - line becomes context line
@ (bind zerocol " ")
@ (end)
@ (until)
@/[^+\- ]/@(skip)
@ (end)
@ (set (bfline bflen afline aflen)
@[mapcar int-str (list bfline bflen afline aflen)])
@ (set aflen @(+ bflen nplus (- nminus)))
@(end)
@(output :into stripped-diff)
diff @diffjunk
index @indexjunk
--- a/@file
+++ b/@file
@ (repeat)
@@@@ -@bfline,@bflen +@afline,@aflen @@@@
@ (repeat)
@zerocol@line
@ (end)
@ (end)
@(end)
@(next (open-command `git checkout-index --temp @file`))
@tempname@\t@file
@(try)
@ (do
(with-stream (patch-stream (open-command `patch -p1 @tempname` "w"))
(put-lines stripped-diff patch-stream)))
@ (next (open-command `git hash-object -w @tempname`))
@newsha
@ (do (sh `git update-index --cacheinfo 100644 @newsha @file`))
@(catch)
@ (fail)
@(finally)
@ (do
(ignerr [mapdo remove-path #`@tempname @tempname.orig @tempname.rej`]))
@(end)
基本上策略是:
对git diff 输出进行一些模式匹配,以将大块过滤到匹配行。我们必须重新计算大块头中的“之后”行数,并保留上下文行。
将过滤后的差异输出到变量中。
使用git checkout-index --temp 从索引中获取文件的原始副本。此命令输出它生成的临时名称,我们将其捕获。
现在将过滤/减少的差异发送到patch -p1,目标是保存索引中原始副本的这个临时文件。好的,我们现在已经将我们想要的更改应用于原始文件。
接下来,使用 git hash-object -w 从修补文件中创建一个 Git 对象。捕获此命令输出的哈希值。
最后,使用git update-index --cacheinfo ... 将这个新对象输入到原始文件名下的索引中,有效地暂存文件的更改。
如果这搞砸了,我们可以通过 git reset 擦除索引,修复我们损坏的脚本并重试。
只是通过+ 和- 行盲目匹配有明显的问题。它应该在模式匹配配置文件中的变量名而不是内容的情况下工作。例如
替换:
-CONFIG_VAR=foo
+CONFIG_VAR=bar
这里,如果我们匹配CONFIG_VAR,那么这两行都包括在内。如果我们匹配右侧的foo,我们就会破坏:我们最终得到一个补丁,只是减去CONFIG_VAR=foo 行!
显然,考虑到配置文件的语法和语义,这可以做得很聪明。
我将如何“真正地”解决这个问题是编写一个强大的配置文件解析器和重新生成器(保留 cmets、空白和所有内容)。然后将新的和原始的原始文件解析为配置对象,将匹配的更改从一个对象迁移到另一个对象,并生成一个更新的文件以进入索引。不要乱用补丁。