Подтвердить что ты не робот

Как git добавить только строки, соответствующие шаблону?

Я отслеживаю с помощью git некоторые файлы конфигурации. Обычно я делаю интерактивный git add -p, но я ищу способ автоматического добавления всех новых/модифицированных/удаленных строк, соответствующих шаблону. В противном случае это займет у меня возраст, чтобы сделать все интерактивное разделение и добавить. git add имеет сопоставление шаблонов для имен файлов, но я ничего не могу найти о содержании.

4b9b3361

Ответ 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!

Очевидно, что это можно сделать умным, учитывая синтаксис и семантику конфигурационного файла.

Как бы я решил это "по-настоящему", было бы написать надежный синтаксический анализатор и регенератор файлов (который сохраняет комментарии, пробелы и все). Затем проанализируйте новый и оригинальный нетронутый файл для объектов конфигурации, перенесите соответствующие изменения из одного объекта в другой и создайте обновленный файл, чтобы перейти к индексу.

Ответ 2

Я не думаю, что это возможно; поскольку git add -p всегда показывает вам кучи изменений; но этот кусок может содержать некоторую строку, которую вы хотите добавить (и соответствует вашему шаблону), и строку, содержащую изменения, которые вы не хотите добавлять.

Иногда мне приходится сталкиваться с подобной проблемой, когда я сделал два изменения и хочу их отдельно записать:

  • переименование переменной
  • добавить некоторые функции

Существует обходное решение:

  • Отложите мои изменения (используя git stash или просто скопируйте файлы)
  • переименуйте переменную (поэтому я переделаю легкую часть моей работы, так как переименование переменной обычно выполняется средой IDE)
  • зафиксировать эти изменения.
  • повторить мои изменения (используя git stash pop или скопировать файлы обратно)
  • передать остальные изменения.

Ответ 3

вот путь:

  • используйте git diff > patch, чтобы сделать патч текущего diff.

  • используйте gawk, чтобы сделать второй патч только из строк +/-, соответствующих шаблону: удалите - из удаленных строк, не соответствующих шаблону, удалите строки +, не соответствующие шаблону, измените ханк номера строк заголовка, выводят каждый измененный hunk, но не выводят никаких модифицированных кусков, которые больше не имеют каких-либо изменений в них.

  • используйте git stash save, apply patch, add -u и stash pop для применения и установки модифицированного патча и оставления остальных изменений в неактивном состоянии.

это работало для нескольких тестовых случаев, оно работает на весь diff сразу (все файлы), и это быстро.

#!/bin/sh

diff=`mktemp`
git diff > $diff
[ -s $diff ] || exit

patch=`mktemp`

gawk -v pat="$1" '
function hh(){
  if(keep && n > 0){
    for(i=0;i<n;i++){
      if(i==hrn){
        printf "@@ -%d,%d +%d,%d @@\n", har[1],har[2],har[3],har[4];
      }
      print out[i];
    }
  }
}
{
  if(/^diff --git a\/.* b\/.*/){
    hh();
    keep=0;
    dr=NR;
    n=0;
    out[n++]=$0
  }
  else if(NR == dr+1 && /^index [0-9a-f]+\.\.[0-9a-f]+ [0-9]+$/){
    ir=NR;
    out[n++]=$0
  }
  else if(NR == ir+1 && /^\-\-\- a\//){
    mr=NR;
    out[n++]=$0
  }
  else if(NR == mr+1 && /^\+\+\+ b\//){
    pr=NR;
    out[n++]=$0
  }
  else if(NR == pr+1 && match($0, /^@@ \-([0-9]+),?([0-9]+)? \+([0-9]+),?([0-9]+)? @@/, har)){
    hr=NR;
    hrn=n
  }
  else if(NR > hr){
    if(/^\-/ && $0 !~ pat){
      har[4]++;
      sub(/^\-/, " ", $0);
      out[n++] = $0
    }
    else if(/^\+/ && $0 !~ pat){
      har[4]--;
    }
    else{
      if(/^[+-]/){
        keep=1
      }
      out[n++] = $0
    }
  }
}
END{
  hh()
}' $diff > $patch

git stash save &&
  git apply $patch &&
  git add -u &&
  git stash pop

rm $diff
rm $patch

рефов:

git diff apply

унифицированный формат diff

gawk match группы для массива

git add -u

Ответ 4

Это, конечно, безумие. Но вы знаете, у меня есть некоторые сумасшедшие рабочие процессы в моей работе, о которых я иногда спрашиваю, и обычно есть веская причина для сумасшествия.

Хорошо, это шаблон по строкам или шаблон "если он содержит его"? Если это строка за строкой, возможно, вы могли бы сделать что-то подобное. Это не будет работать точно, но это начало

git diff <file> | egrep '^[^+]|<pattern' > file.patch
git stash
git apply file.patch

Если вам нужно применить любой фрагмент, который имеет шаблон, который вы ищете, вам понадобится более длинный, более состоятельный script, чтобы проанализировать ваш diff. На самом деле это, вероятно, так или иначе необходимо. Пролистайте diff, ища символы '@@', указывающие начало раздела diff. Буферируйте этот раздел, пока не дойдете до конца. Если вы столкнулись с рассматриваемым шаблоном, выведите раздел, если нет, а затем выбросите его. Затем примените этот новый diff как патч.

git diff <file> | parse_diff_script.sh > file.patch
git stash
git apply file.patch

Ответ 5

Вы можете начать с git ls-files, чтобы получить список интересующих файлов для заданного пути. Затем вы можете передать этот список в grep и ограничить на основе соответствия регулярных выражений. Наконец, этот сокращенный список файлов можно передать в git add через xargs git add:

git ls-files [path] | grep '^some regex goes here$' | xargs git add -p

Этот подход позволит вам применить интеллектуальное регулярное выражение, которое, надеюсь, может уменьшить количество файлов для вашего интерактивного сеанса git add. По определению, для выполнения git add -p требуется взаимодействие с человеком, поэтому, если после применения шаблона у вас все еще есть слишком много файлов, вы должны найти другой подход.

Кстати, иногда это помогает искать Qaru перед публикацией, где вы можете найти очень полезные сообщения, например этот.