是否可以在没有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
答案 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 --stage
或git diff-files -1
和git diff-files -2
。 (我还没有尝试过git diff-files -digit
。)
但是,如果我们可以忽略此未合并索引的情况,则两个字符为:
HEAD
与索引,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]
因此,通过一些工作,有可能 获得“相同”的输出。我们想要:
工作树中 M 状态文件的名称:git diff-files --name-only --diff-filter=M
。
索引中的 A 或 M 状态文件的名称:git diff-index --name-only --diff-filter=AM
。
未跟踪的文件。这不能使用两个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
的结果并不总是匹配的,因为您现在只能看到文件一次。 (然后,无论如何这可能还是更好的选择。)