【问题标题】:How do I git add only lines matching a pattern?我如何 git 只添加与模式匹配的行?
【发布时间】:2016-08-24 06:13:04
【问题描述】:

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

【问题讨论】:

  • 所以要明确一点,您想git add 一个基于在其内容中匹配的模式的文件?
  • 不,我只想根据行模式在文件中添加块。
  • 我很确定不可能使它对任意干扰具有鲁棒性。您能否给出您希望自动识别/接受的更改的具体示例?
  • 不仅拆分;当更改过于靠近时,您将无法将它们分开。然后只能使用手动编辑操作将它们分开,您可以删除+ 行或将- 转换为上下文行。
  • 这方面肯定有很好的用例。在团队中编辑 Winforms Designer.cs 文件(是的,这仍然会发生)、配置文件编辑等。

标签: git git-add


【解决方案1】:

有一个办法:

  1. 使用git diff > patch 制作当前差异的补丁。

  2. 使用gawk 仅对匹配模式的+/- 行进行第二个补丁:从删除的不匹配模式的行中删除-,删除不匹配模式的+ 行,修改大块标题行号,输出每个修改过的hunk,但不输出任何修改过的hunk,不再有任何变化。

  3. 使用 git stash saveapply patchadd -ustash 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

参考:

git diff apply

unified diff format

gawk match groups to array

git add -u

【讨论】:

    【解决方案2】:

    我在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、空白和所有内容)。然后将新的和原始的原始文件解析为配置对象,将匹配的更改从一个对象迁移到另一个对象,并生成一个更新的文件以进入索引。不要乱用补丁。

    【讨论】:

    • 这令人印象深刻。我肯定在研究那种 TXR 语言!
    【解决方案3】:

    我认为这是不可能的;因为git add -p 总是向你展示大量的变化;但该块可能包含您想要添加的某些行(并且与您的模式匹配)和包含您不想添加的更改的行。

    当我做了两个更改并想分别提交它们时,有时我会遇到类似的问题:

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

    我使用了一种解决方法:

    • 将我的更改放在一边(使用 git stash 或仅复制文件)
    • 重命名变量(所以我重做我工作中简单的部分;因为重命名变量通常由 IDE 负责)
    • 提交这些更改
    • 重新应用我的更改(使用git stash pop 或将文件复制回来)
    • 提交我的其余更改​​li>

    【讨论】:

    • 有可能,q.v.我的回答。
    • 当且仅当他想添加与内容中的模式匹配的文件时,我才有可能,但如果他想添加与模式匹配的 HUNKS(文件的一部分)则不行
    • 感谢您向我指出这么多。我仍然认为如果他能想出一个足够聪明和限制性足够的正则表达式,那么做一个交互式git add -p 只是添加所有文件可能会满足他的用例。
    【解决方案4】:

    这当然很疯狂。但你知道,我的工作中有一些疯狂的工作流程,我偶尔会问这些问题,而且这些疯狂通常是有充分理由的。

    好的,该模式是逐行模式还是“如果块包含它”模式?如果它是逐行的,也许你可以做这样的事情。这并不完全有效,但这是一个开始

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

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

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

    【讨论】:

      【解决方案5】:

      您可以从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

      【讨论】:

      • 我认为这不能回答他的问题,因为他说他不想进行“交互式拆分”
      • 我不同意。他可以限制在感兴趣的路径上,我的回答应该可以正常工作。这可能需要很长时间的唯一情况是在大量代码库的情况下(例如 Microsoft 的 Windows),他不知道文件的位置。
      • 如果我不够精确,我很抱歉。如果他想“根据内容匹配的模式添加文件”(正如您在他的问题上所问的那样);那么你的答案是完全有效的。但是,如果他想根据文件是否匹配模式添加大块文件;那么你的方法不起作用。
      猜你喜欢
      • 2019-09-22
      • 1970-01-01
      • 1970-01-01
      • 2023-01-17
      • 1970-01-01
      • 2017-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多