让 git status 显示未修改/未更改的跟踪文件?

问题描述 投票:0回答:8

这是一个简短的代码片段示例(您可以将其粘贴到 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 [email protected]
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
)不变。下面的代码片段演示了这一点,以及
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
不会显示,因为它显示
H
为所有跟踪文件的状态)。例如,我想获得类似伪代码的东西:

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
中的任何命令已经执行了类似的操作?

git version-control
8个回答
14
投票

适用于 Linux 的快速单行代码:

sort -uk 2bi <(git status -s) <(git ls-files | sed 's/^/ . /')


您可以将其包装在一个方便的别名中,该别名也接受可选的目录路径。因此,在您的

[alias]
中的
.gitconfig
下,您可以输入以下内容:

stt = "!f() { cd -- ${GIT_PREFIX}/${1:-.} ; sort -uk 2bi <(git status -s .) <(git ls-files . | sed 's/^/ . /') ; }; f"

然后你可以像这样使用它:

$ git stt
$ git stt src/foo
$ git stt ..

7
投票

谢谢@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

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


3
投票

受到 @RogerDueck 的回答的启发,我制作了一个脚本,该脚本仅执行

git ls-files
git status
各一次。在我的存储库上,它的运行速度大约快 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

0
投票

frnhr 的答案在 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;

0
投票

这是一个需要检查文件列表的版本,并为传入的每个文件输出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"

0
投票

仅显示未修改/未编辑的跟踪文件/路径:

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- )
  1. 通过管道 ls-files/ls-tree 到 grep

  2. 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]: 什么是文件描述符,简单解释一下?


-1
投票
git status -s | egrep -v '^\?\?'

这会过滤掉以

??
开头的行,即未跟踪的文件。

© www.soinside.com 2019 - 2024. All rights reserved.