Ответ 1
Я свернул эту экспериментальную и плохо протестированную программу в TXR:
Пример прогона: сначала, где мы находимся в репо:
$ 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
Соответствующий материал находится в индексе, а строки nonmatching +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)
@@@@ [email protected],@bflen [email protected],@aflen @@@@@(skip)
@ (bind (nminus nplus) (0 0))
@ (collect)
@ (cases)
@line
@ (bind zerocol " ")
@ (or)
[email protected]
@ (bind zerocol "+")
@ (require (search-regex line regex))
@ (do (inc nplus))
@ (or)
[email protected]
@ (bind zerocol "-")
@ (require (search-regex line regex))
@ (do (inc nminus))
@ (or)
[email protected]
@;; 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)
@@@@ [email protected],@bflen [email protected],@aflen @@@@
@ (repeat)
@[email protected]
@ (end)
@ (end)
@(end)
@(next (open-command `git checkout-index --temp @file`))
@[email protected]\[email protected]ile
@(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
, чтобы отфильтровать кланы до соответствующих строк. Мы должны повторно вычислить счетчик строк "после" в заголовке hunk и сохранить строки контекста. -
выводит фильтрованный diff в переменную.
-
получить нетронутую копию файла из индекса с помощью
git checkout-index --temp
. Эта команда выводит временное имя, которое она сгенерировала, и мы ее фиксируем. -
Теперь отправьте отфильтрованный/уменьшенный diff на
patch -p1
, нацелив этот временный файл на первозданную копию из индекса. Итак, теперь у нас есть только те изменения, которые мы хотели, применительно к исходному файлу. -
Затем создайте объект Git из исправленного файла, используя
git hash-object -w
. Захватите хэш, который выдает эта команда. -
Наконец, используйте
git update-index --cacheinfo ...
, чтобы ввести этот новый объект в индекс под исходным именем файла, эффективно выполнив изменение для файла.
Если это завинчивается, мы можем просто сделать git reset
, чтобы стереть индекс, исправить нашу сломанную скриптологию и повторить попытку.
Просто слепое соответствие строк +
и -
имеет очевидные проблемы. Он должен работать в том случае, если шаблоны соответствуют именам переменных в файлах конфигурации, а не в содержимом. Например.
Замена:
-CONFIG_VAR=foo
+CONFIG_VAR=bar
Здесь, если мы сопоставим с CONFIG_VAR
, тогда обе строки включены. Если мы сопоставим с foo
в правой части, мы сломаем вещи: в итоге получим патч, который просто вычитает строку CONFIG_VAR=foo
!
Очевидно, что это можно сделать умным, учитывая синтаксис и семантику конфигурационного файла.
Как бы я решил это "по-настоящему", было бы написать надежный синтаксический анализатор и регенератор файлов (который сохраняет комментарии, пробелы и все). Затем проанализируйте новый и оригинальный нетронутый файл для объектов конфигурации, перенесите соответствующие изменения из одного объекта в другой и создайте обновленный файл, чтобы перейти к индексу.