我如何git只添加与模式匹配的行?

Ben*_*oît 30 git git-add

我正在使用git跟踪一些配置文件.我通常做一个交互式,git add -p但我正在寻找一种方法来自动添加与模式匹配的所有新/修改/删除的行.否则,我需要花费很多时间来完成所有交互式拆分并添加.git add有一个模式匹配的文件名,但我找不到有关内容的任何内容.

web*_*ebb 6

这是一种方式:

  1. 使用git diff > patch使当前的diff补丁.

  2. 用于gawk仅创建+/-与模式匹配的第二个补丁:-从与模式不匹配的已删除行中删除,删除与模式不匹配的+行,修改hunk标题行号,输出每个修改后的hunk,但不输出任何修改的帅哥不再有任何变化.

  3. 使用git stash save,apply patch,add -u,和stash pop以应用和舞台改型的接线和离开的变化不分级的其余部分.

这适用于几个测试用例,它可以同时处理整个差异(所有文件),而且速度很快.

#!/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
Run Code Online (Sandbox Code Playgroud)

裁判:

git diff apply

统一差异格式

gawk match 组到数组

git add -u


Kaz*_*Kaz 5

我在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
Run Code Online (Sandbox Code Playgroud)

和:

$ git diff --cached  # nothing staged in the index
Run Code Online (Sandbox Code Playgroud)

目标是只提交包含匹配项的行min

$ txr addmatch.txr min lorem.txt
patching file .merge_file_BilTfQ
Run Code Online (Sandbox Code Playgroud)

现在是什么状态?

$ 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
Run Code Online (Sandbox Code Playgroud)

和:

$ 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
Run Code Online (Sandbox Code Playgroud)

匹配的东西在索引中,不匹配的+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)
Run Code Online (Sandbox Code Playgroud)

基本上策略是:

  • git diff输出进行一些模式匹配以将大块过滤到匹配的行。我们必须重新计算大块头中的“之后”行数,并保留上下文行。

  • 将过滤后的差异输出到变量中。

  • 使用git checkout-index --temp.从索引中获取文件的原始副本。这个命令输出它生成的临时名称,我们捕获它。

  • 现在将过滤/减少的差异发送到patch -p1,以这个临时文件为目标,其中包含来自索引的原始副本。好的,我们现在有我们想要的更改,应用于原始文件。

  • 接下来,从打过补丁的文件中创建一个 Git 对象,使用git hash-object -w. 捕获此命令输出的哈希。

  • 最后,使用git update-index --cacheinfo ...将这个新对象输入到原始文件名下的索引中,有效地暂存文件的更改。

如果这git reset搞砸了,我们可以擦除索引,修复损坏的脚本,然后再试一次。

只是盲目匹配通过+-线路有明显的问题。它应该在模式匹配配置文件中的变量名称而不是内容的情况下工作。例如

替代品:

-CONFIG_VAR=foo
+CONFIG_VAR=bar
Run Code Online (Sandbox Code Playgroud)

在这里,如果我们匹配CONFIG_VAR,那么两行都包括在内。如果我们foo在右手边匹配 ,我们就会破坏事情:我们最终得到一个只是减去这CONFIG_VAR=foo条线的补丁!

显然,考虑到配置文件的语法和语义,这可以做得很聪明。

我将如何“真正地”解决这个问题是编写一个强大的配置文件解析器和重新生成器(保留注释、空格等)。然后将新的和原始的原始文件解析为 config 对象,将匹配的更改从一个对象迁移到另一个对象,并生成一个更新文件以转到索引。不要乱用补丁。