如何git只添加符合模式的行?

yruzcnhs  于 2023-08-01  发布在  Git
关注(0)|答案(6)|浏览(105)

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

cx6n0qe3

cx6n0qe31#

这里有一个方法:
1.使用git diff > patch对当前的diff进行修补。
1.使用gawk仅生成+/-行的第二个面片,与图案匹配:从不匹配模式的删除行中删除-,删除不匹配模式的+行,修改块标题行号,输出每个修改后的块,但不输出任何修改后的块,这些块中不再有任何更改。
1.使用git stash saveapply patchadd -ustash 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 diffapply
统一差分格式
gawkmatch groups to array
git add -u

atmip9wb

atmip9wb2#

我在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


匹配的内容在索引中,而不匹配的+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输出上进行一些模式匹配,以将块向下过滤到匹配行。我们必须重新计算大块标题中的“after”行计数,并保留上下文行。
  • 将过滤后的diff输出到变量中。
  • 使用git checkout-index --temp从索引中获取该文件的原始副本。这个命令输出它生成的临时名称,我们捕获它。
  • 现在,将过滤后的/简化后的diff发送到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行!
显然,考虑到配置文件的语法和语义,这可以做得更聪明。

真实的解决这个问题的方法是编写一个健壮的配置文件解析器和重新生成器(保留注解、空格等)。然后解析新的和原始的原始文件以配置对象,将匹配的更改从一个对象迁移到另一个对象,并生成一个更新的文件以转到索引。不要乱打补丁。

jchrr9hc

jchrr9hc3#

我认为这是不可能的因为git add -p总是显示大量的变化;但是该块可能包含一些您想要添加的行(并且与您的模式匹配)和一个包含您不想添加的更改的行。
有时候,当我做了两个更改并希望分别提交它们时,我会遇到类似的问题:

  • 变量的重命名
  • 添加一些功能

有一个我使用的解决方法:

  • 将我的更改放在一边(使用git stash或仅复制文件)
  • 重命名变量(所以我重做了我工作中容易的部分;因为重命名变量通常由IDE处理)
  • 提交这些更改
  • 重新应用我的更改(使用git stash pop或复制回文件)
  • 提交其余的更改
v7pvogib

v7pvogib4#

这当然很疯狂但你知道,我们有一些疯狂的工作流程在我的工作,我偶尔会问,通常有一些很好的理由疯狂。
好的,这个模式是逐行模式还是“如果块包含它”模式?如果是一行一行的,也许你可以这样做。这不会完全奏效,但这是一个开始

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

字符串
如果您必须应用任何具有您正在寻找的模式的块,那么您将需要一个更长、更有状态的脚本来解析您的diff。事实上,这可能是必要的,无论如何。在diff中搜索表示diff部分开始的'@@'字符。把那一部分缓冲起来,直到你走到最后。如果遇到了有问题的模式,输出该部分,如果没有,则将其丢弃。然后将新差异作为补丁应用。

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

fwzugrvs

fwzugrvs5#

如果你使用VSCode,你可以选择所有匹配文件中模式的行,并暂存它们:
1.在“源代码管理”窗格中打开一个比较。
1.选择示例行,然后按住ctrlD以选择所有其他匹配行。点击ctrlD一次后,可以使用右上角的浮动面板启用基于regex的搜索。

  1. ctrlK+ctrlaltS暂存所有选择。
uqdfh47h

uqdfh47h6#

您可以从git ls-files开始获取给定路径的感兴趣文件列表。然后,您可以将该列表通过管道传输到grep,并基于正则表达式匹配进行限制。最后,这个精简的文件列表可以通过xargs git add管道传输到git add

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

字符串
这种方法将允许您应用智能正则表达式,希望它可以减少交互式git add会话的文件数量。根据定义,执行git add -p需要人工交互,因此如果在应用模式之后仍然有太多文件,那么应该找到另一种方法。
如果你想添加整个文件,而不是行,尝试this answer

相关问题