如何使用diff-filter简化git-status命令,例如git-diff-index

时间:2019-04-25 13:12:18

标签: git

是否可以在没有grep和sed的情况下编写以下命令?

我认为这可以通过例如git-diff-index,但细节让我不知所措。

原始命令是:

git status -s --porcelain /some/path | grep -E '^(.M|[MA].|\?\?)' | sed -e 's/^.. //g';

这等效吗?

git diff-index --name-only --diff-filter=AM HEAD -- /some/path

1 个答案:

答案 0 :(得分:1)

好吧,我们首先从git status命令开始:

git status -s --porcelain /some/path | grep -E '^(.M|[MA].|\?\?)' | sed -e 's/^.. //g'

--porcelain(或--porcelain=v1)输出格式是--short格式的未经颜色编码的变体,它为每个文件名打印两个字符,然后打印一个空格,然后文件名(可能根据core.quotePath和名称中的字符进行引用)。 -s产生短输出,但是我们已经有--porcelain=v1输出,因此是多余的(可能应该删除)。

如果您处于合并冲突中,则这两个字符来自合并的每个“边”,即,与进行中的合并状态有更多关系。在这种情况下,您可以获得两个字符之一的U条目。 git diff-index不可重复,而git diff-files至少很难做到;它需要读取索引中较高级的条目,例如git ls-files --stagegit diff-files -1git diff-files -2。 (我还没有尝试过git diff-files -digit。)

但是,如果我们可以忽略此未合并索引的情况,则两个字符为:

  • 在左侧:HEAD与索引,A,D,M或R之一; ,空白,或问号,这两种情况只有在右侧字符强制下才会出现。
  • 在右边:索引与工作树,A,D,M,R,空白或问号之一。

(手册页将C列为已复制状态,作为可能的状态。此状态可以来自Git的内部差异引擎,但前提是您启用了复制查找功能,并且git status命令本身默认情况下不会 启用复制查找,并且(至少目前)没有标志来指示这样做,因此C状态从不实际发生)

当文件存在于被比较的两个实体中时,空白出现,并且在两个实体中都相同。例如,如果文件 F 同时出现在HEAD(一次提交)和索引中,并且两者都相同,则其左字符状态为空白。但是,如果 F 然后出现在索引和工作树中,并且它们的状态都相同,则git status根本不会提及它,因此您永远不会出现两个空格。这就是为什么我说只有在强制下才会出现空白。

当文件作为未跟踪文件存在于工作树中时,出现问号状态。在这种情况下,根据定义,文件在索引中不是 :未跟踪的文件是出现在工作树中但不在索引中的文件。因此,在这种情况下,您得到的是一行读为?? filename的行。请注意,在这种情况下,命名文件确实可能出现在HEAD中。如果是这样,则必须在HEAD-vs-index中删除。有人会认为这可能是:

D? filename

这很有意义,但是Git却显示为:

D  filename
?? filename

ie,文件出现两次,一次为HEAD-vs-index = deleted状态(与工作树没有区别),然后再次为index-vs-work-tree = untracked状态(显示为两个问号)。

现在让我们进入grep-E参数提供了一个正则表达式,该正则表达式与三个备选方案之一匹配,所有这些备选方案都锚定在行的开头,因此我们始终查看两个git status字符。三种表达方式分别是:

  • .M:index-vs-work-tree状态为 modified 的任何内容。第一个点接受任何HEAD-vs-index状态。
  • [MA].:HEAD-vs-index状态为“ 已修改”或“ 已添加”的任何内容。第二个点接受任何index-vs-work-tree状态。
  • ??:未跟踪的文件。
  

我认为这可以通过例如git-diff-index ...

正如the documentation所说,

  

将树与工作树或索引进行比较

因此,假设HEAD是比较左侧的树,并且说明符将工作树用作右侧,则此操作与git status的前半部分。所以

git diff-index --cached HEAD [options]

git status的前半部分相同,并且可以获得与git status --short左侧相同的字母代码。

不过,要获得与git status --short一半相同的字母代码,您必须将索引本身(而不是树)与工作进行比较-树。 the git diff-files command就是这样做的。因此,您需要:

git diff-files [options]

因此,通过一些工作,有可能 获得“相同”的输出。我们想要:

  1. 工作树中 M 状态文件的名称:git diff-files --name-only --diff-filter=M

  2. 索引中的 A M 状态文件的名称:git diff-index --name-only --diff-filter=AM

  3. 未跟踪的文件。这不能使用两个git diff-*命令中的任何一个来完成,但是 可以使用git ls-files --other --exclude-standard来完成(您需要最后一个选项来使其遵循通常的git-ignore规则)

因此:

(
 git diff-files --name-only --diff-filter=M
 git diff-index --name-only --diff-filter=AM
 git ls-files --other --exclude-standard
)

作为一组三个命令,应该为您提供相同的文件名。这里的主要问题是,如果文件在索引中同时包含 A-或-M-in和 M -work-tree状态,然后按如下顺序运行这三个命令,您将看到文件两次。您可以通过使用sort -u的管道结束命令序列来解决该问题:

(...) | sort -u

但是请注意,git status --porcelain 确实会同时打印两次具有D索引并且未跟踪的文件名,因此,如果您想允许{{ 1}}处于索引状态,D的结果并不总是匹配的,因为您现在只能看到文件一次。 (然后,无论如何这可能还是更好的选择。)