让 git status 显示未修改/未更改的跟踪文件?
Posted
技术标签:
【中文标题】让 git status 显示未修改/未更改的跟踪文件?【英文标题】:Make git status show unmodified/unchanged tracked files? 【发布时间】:2013-04-24 19:09:32 【问题描述】:这是一个简短的 sn-p 示例(您可以将其粘贴到您的 Linux 终端中),创建一个新的 git
存储库并向其中添加一些文件(使用 git 版本 1.7.9.5):
cd /tmp/
mkdir myrepo_git
cd myrepo_git/
git init
git config user.name "Your Name"
git config user.email you@example.com
echo "test" > file_tracked_unchanged.txt
echo "test" > file_tracked_changed.txt
echo "test" > file_untracked.txt
git add file_tracked_unchanged.txt
git add file_tracked_changed.txt
git commit -m "initial commit"
现在,在初始提交之后,我想更改 file_tracked_changed.txt
文件,并在下一次提交时保持其他文件(这里只有 file_tracked_unchanged.txt
)不变。下面是一个证明了这一点的 sn-p,以及 git status
与 git ls-files
的不同输出(git
shell 输出以 #
为前缀):
echo "test more" >> file_tracked_changed.txt
git status -uno
# # On branch master
# # Changes not staged for commit:
# # (use "git add <file>..." to update what will be committed)
# # (use "git checkout -- <file>..." to discard changes in working directory)
# #
# # modified: file_tracked_changed.txt
# #
# no changes added to commit (use "git add" and/or "git commit -a")
git status
# # On branch master
# # Changes not staged for commit:
# # (use "git add <file>..." to update what will be committed)
# # (use "git checkout -- <file>..." to discard changes in working directory)
# #
# # modified: file_tracked_changed.txt
# #
# # Untracked files:
# # (use "git add <file>..." to include in what will be committed)
# #
# # file_untracked.txt
# no changes added to commit (use "git add" and/or "git commit -a")
git status -uno --short
# M file_tracked_changed.txt
git status --short
# M file_tracked_changed.txt
# ?? file_untracked.txt
git ls-files -v
# H file_tracked_changed.txt
# H file_tracked_unchanged.txt
git add file_tracked_changed.txt
git status -uno
# # On branch master
# # Changes to be committed:
# # (use "git reset HEAD <file>..." to unstage)
# #
# # modified: file_tracked_changed.txt
# #
# # Untracked files not listed (use -u option to show untracked files)
git status
# # On branch master
# # Changes to be committed:
# # (use "git reset HEAD <file>..." to unstage)
# #
# # modified: file_tracked_changed.txt
# #
# # Untracked files:
# # (use "git add <file>..." to include in what will be committed)
# #
# # file_untracked.txt
git status -uno --short
# M file_tracked_changed.txt
git status --short
# M file_tracked_changed.txt
# ?? file_untracked.txt
git ls-files -v
# H file_tracked_changed.txt
# H file_tracked_unchanged.txt
我正在寻找的是一个命令,它将显示目录中的所有跟踪文件(git ls-files -v
会显示)以及它们准确的存储库状态(git ls-files
不会显示,因为它显示了@987654332 @ 作为所有跟踪文件的状态)。例如,我想获得类似伪代码的东西:
git status-tracked
# M file_tracked_changed.txt
# . file_tracked_unchanged.txt
... 点 .
是一个符号,表示已跟踪但未更改的文件(如果我没记错的话,SVN 可能会使用 U
字符来表示这些文件)。
最后,我还想显示目录中所有文件的状态,如伪代码所示:
git status-tracked-and-untracked
# M file_tracked_changed.txt
# . file_tracked_unchanged.txt
# ?? file_untracked.txt
...但对我来说更重要的是获得所有跟踪文件的状态,如上面的伪git status-tracked
。
git
中的任何命令已经执行了这样的操作?
【问题讨论】:
非常感谢@Kartik 的评论 - 但该命令显示已跟踪修改+未跟踪;我正在寻找一个显示跟踪修改+跟踪未修改的命令。干杯! 【参考方案1】:适用于 Linux 的快速单线:
sort -uk 2bi <(git status -s) <(git ls-files | sed 's/^/ . /')
【讨论】:
非常简单,非常快速。在我测试的两个大型存储库中,与我的答案相比,第一次运行速度提高了 10 倍以上,随后运行速度提高了 100 倍以上。【参考方案2】:感谢@sdaau。我进行了一些更改,使其运行速度更快,并以与git status
相同的格式提供结果:
git ls-files | while read -r line;
do
st=$(git status -s "$line");
if [ -n "$st" ]; then
echo "$st";
else
echo " $line";
fi;
done
【讨论】:
^ 非常有用,但还是有点慢,因为它为每个文件运行一个 git_status。 我对此进行了修改以接受传递给git ls-files
的文件列表,但对于深度忽略的文件,它仍然不打印任何内容,而不是 '??'
查看以下@hashstat 的答案 (***.com/a/59722589/1488762) 以获得 IMO 的最佳答案。【参考方案3】:
感谢@Andomar,提供git ls-tree
提示;这就是它所显示的:
git ls-tree --name-status HEAD
# file_tracked_changed.txt
# file_tracked_unchanged.txt
...但我想要状态:)
好的,这是一个解决方案,同时调用ls-files
和status
,并用一些bash
解析将它们交错:
git ls-files -cdmoskt --abbrev=8 | while read -r line; do \
fn=$(echo "$line" | sed 's/.*\s\(\w\+\)/\1/'); \
st=$(git status -s "$fn" | printf "%-02s " $(sed 's/\([[:print:]]\+\)\s.*/\1/')); \
echo "$st- $line"; \
done
如果你像在 OP 示例中那样运行它,你会得到:
git ls-files -cdmoskt --abbrev=8 | while read -r line; do fn=$(echo "$line" | sed 's/.*\s\(\w\+\)/\1/'); st=$(git status -s "$fn" | printf "%-02s " $(sed 's/\([[:print:]]\+\)\s.*/\1/')); echo "$st- $line"; done
# ?? - ? file_untracked.txt
# M - H 100644 52e7a08e 0 file_tracked_changed.txt
# - H 100644 9daeafb9 0 file_tracked_unchanged.txt
...这基本上是我想要的。 (如果我有幸将其转换为 git
别名,我会在此处发帖)。
编辑:这里是git
别名(对于~/.gitconfig
):
ls-fstatus = "! cd $PWD/$GIT_PREFIX; git ls-files -cdmoskt --abbrev=8 | while read -r line; do \
fn=$(echo \"$line\" | sed \"s/.*\\s\\([[:print:]]\\+\\)/\\1/\"); \
st=$(git status -s "$fn" | printf \"%-02s \" $(sed \"s/\\([[:print:]]\\+\\)\\s.*/\\1/\")); \
echo \"$st- $line\"; \
done "
...所以可以在给定的 git repo 子目录中调用git ls-fstatus
。
【讨论】:
这不处理带空格的路径。通过在 cd 的参数周围添加引号,它可以工作:cd \"$PWD/$GIT_PREFIX\";
【参考方案4】:
受@RogerDueck's answer 的启发,我制作了一个脚本,每个脚本只执行一次git ls-files
和git status
。它在我的 repo 上运行大约 15 倍,大约 1700 个文件,不到 2 秒。
编辑:添加了一些修复和一些单元测试,移至 GitHub:https://github.com/frnhr/git-fullstatus
示例输出:
M some/file
D another/file
D more/files/blahblah
A this/is/an/added/file/i/think
an/unchanged_file
another/unchanged_file
【讨论】:
太棒了!为什么file=`echo "$line:3" | xargs`
而不仅仅是file="$line:3"
?【参考方案5】:
frnhr's answer 在 macOS 上对我不起作用,因为它有旧版本的 bash。 perl 中也有类似的想法。而不是 qsort,它只是合并两个已经排序的列表(除了最后被忽略的文件)。
#!/usr/bin/env perl
use strict;
# --ignored means something different to ls-files than to status
my @ls_args = grep( ! m/^(-i|--ignored)/, @ARGV );
my @files = split( /\n/, `git ls-files @ls_args` );
my @status = split( /\n/, `git status -s @ARGV` );
# merge the two sorted lists
while (@files and @status)
$status[0] =~ m/^.. (.*)/;
my $cmp = $1 cmp $files[0];
if ($cmp <= 0)
print shift @status, "\n";
if ($cmp == 0)
shift @files;
else
print " ", shift @files, "\n";
# print remainder
print map " $_\n" @files;
print map "$_\n" @status;
【讨论】:
【参考方案6】:这是一个需要检查文件列表的版本,并为传入的每个文件输出 something -- 已修改、未修改 (' ') 或忽略 ('??')。
#!/bin/bash
sorted=$(printf '%s\n' $(realpath --relative-to . "$@") | sort)
for f in $sorted; do
st=$(git status -s "$f");
lsf=$(git ls-files "$f");
if [ -n "$st" ]; then
echo "$st";
elif [ -n "$lsf" ]; then
echo " $lsf";
else
echo "?? $f"
fi;
done
以及同一事物的单行 git 别名:
[alias]
file-status="!f() sorted=$(printf '%s\\n' $(realpath --relative-to . \"$@\") | sort); for f in $sorted; do st=$(git status -s \"$f\"); lsf=$(git ls-files \"$f\"); if [ -n \"$st\" ]; then echo \"$st\"; elif [ -n \"$lsf\" ]; then echo \" $lsf\"; else echo \"?? $f\"; fi; done ; f"
【讨论】:
【参考方案7】:仅显示未修改/未编辑的跟踪文件/路径:
bash/zsh
$ git ls-files | grep -vf <( git status -s | grep '^[^?]' | cut -c4- )
$ git ls-tree --name-status HEAD |
grep -vf <( git status -s | grep '^[^?]' | cut -c4- )
管道 ls-files/ls-tree 到 grep
grep -v -f
-v
排除/过滤掉
-f
读取git status -s | grep '^[^?]' | cut -c4-
的输出作为[文件描述符][1],以<(
开头,以)
结尾
[^?]
grep 第一个字符不能是 ?
cut -c4-
剪切第四个字符直到行尾
$ time zsh -c " git ls-files |
grep -v -f <( git status -s | grep '^[^?]' | cut -c4- )"
fileA
fileB
dirA/file1
zsh -c "git ls-files| grep -v -f <( git status -s | grep '^[^?]' | cut -c4- )" 0.02s user 0.03s system 69% cpu 0.073 total
$ time zsh -c "git ls-tree --name-status HEAD |
grep -v -f <( git status -s | grep '^[^?]' | cut -c4- )"
fileA
fileB
zsh -c 0.02s user 0.03s system 93% cpu 0.057 total
(注意:在|
之后不转义换行符很好,但如果首选样式是转义,我很乐意编辑)
[1]:What are file descriptors, explained in simple terms?
【讨论】:
【参考方案8】:git status -s | egrep -v '^\?\?'
这会过滤掉以??
开头的行,即未跟踪的文件。
【讨论】:
感谢@Andomar 的回答-但不幸的是,该命令没有显示跟踪但未修改的文件,这就是我要问的。干杯!git ls-tree --name-status HEAD
应该做你想做的事,但它会默默地忽略-status
部分:)
非常感谢,@Andomar - 很棒的提示,我不知道 ls-tree
,但正如你所说,状态没有显示 - 所以我刚刚发布了 an answer,它“交错”了状态来自git
s status
和ls-files
。干杯!以上是关于让 git status 显示未修改/未更改的跟踪文件?的主要内容,如果未能解决你的问题,请参考以下文章