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

时间:2016-04-29 07:33:29

标签: git git-add

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

5 个答案:

答案 0 :(得分:5)

这是一种方式:

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

  2. 使用gawk仅制作与模式匹配的+/-行的第二个补丁:从与模式不匹配的已删除行中删除-,删除+行不匹配模式,修改hunk标题行号,输出每个修改后的hunk,但不输出任何不再有任何修改的修改后的黑客。

  3. 使用git stash saveapply patchadd -ustash pop来应用和暂存已修改的修补程序,并将其余更改保留为未分级。

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

    #!/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

答案 1 :(得分:4)

我认为这是不可能的;因为git add -p总能显示你的变化;但是,该块可能包含一些您想要添加的行(并匹配您的模式)以及包含您不想添加的更改的行。

有时候,当我做两次更改并希望单独提交时,我会遇到类似的问题:

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

我使用了一种解决方法:

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

答案 2 :(得分:3)

我在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输出上进行一些模式匹配,以便将这些模式过滤到匹配的行。我们必须重新计算hunk标头中的“after”行数,并保留上下文行。

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

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

  • 现在将过滤/缩减差异发送到patch -p1,目标是保存索引中原始副本的临时文件。好的,我们现在只需要我们想要的更改,应用到原始文件。

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

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

如果这搞砸了,我们可以git reset擦除索引,修复我们破碎的脚本并重试。

通过+-行进行盲目匹配会产生明显的问题。它应该适用于模式匹配配置文件中的变量名称而不是内容的情况。 E.g。

的更换:

-CONFIG_VAR=foo
+CONFIG_VAR=bar

在这里,如果我们匹配CONFIG_VAR,则包含两行。如果我们在右侧的foo匹配,我们会破坏一些事情:我们最终得到的补丁只会减去CONFIG_VAR=foo行!

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

我如何解决这个“真实”将是编写一个强大的配置文件解析器和重新生成器(保留注释,空白和所有)。然后将新的原始pristine文件解析为配置对象,将匹配的更改从一个对象迁移到另一个对象,并生成更新的文件以转到索引。没有乱七八糟的补丁。

答案 3 :(得分:1)

这当然是疯了。但是你知道,我工作中有一些疯狂的工作流程,我偶尔会问这些工作流程,并且通常有一些很好的理由让人感到疯狂。

好的,模式是逐行模式还是&#34;如果块包含它&#34;图案?如果它是逐行的,也许你可以做这样的事情。这不会完全奏效,但这只是一个开始

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

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

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

答案 4 :(得分:-1)

您可以从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需要人工交互,因此如果在应用模式后仍然有太多文件,那么您应该找到另一种方法。

顺便说一下,有时候在发布之前搜索Stack Overflow会有所帮助,在那里你可能会找到非常有用的帖子,比如this one