web-dev-qa-db-ja.com

Gitを使用したダーティインデックスまたは未追跡ファイルの確認

Gitリポジトリにコミットされていない変更があるかどうかを確認するにはどうすればよいですか:

  1. インデックスに追加されたがコミットされていない変更
  2. 追跡されていないファイル

スクリプトから?

git-statusは、gitバージョン1.6.4.2では常にゼロを返すようです。

235
Robert Munteanu

素晴らしいタイミング!数日前にまさにこれについてのブログ記事を書きました。そのとき、プロンプトにgitステータス情報を追加する方法を見つけました。

ここに私がやることがあります:

  1. ダーティステータスの場合:

    # Returns "*" if the current git branch is dirty.
    function evil_git_dirty {
      [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
    }
    
  2. 追跡されていないファイルの場合(--porcelainフラグがgit statusに通知されます。これにより、解析可能な出力が得られます):

    # Returns the number of untracked files
    
    function evil_git_num_untracked_files {
      expr `git status --porcelain 2>/dev/null| grep "^??" | wc -l` 
    }
    

git diff --shortstatの方が便利ですが、git status --porcelainを使用してダーティファイルを取得することもできます。

# Get number of files added to the index (but uncommitted)
expr $(git status --porcelain 2>/dev/null| grep "^M" | wc -l)

# Get number of files that are uncommitted and not added
expr $(git status --porcelain 2>/dev/null| grep "^ M" | wc -l)

# Get number of total uncommited files
expr $(git status --porcelain 2>/dev/null| egrep "^(M| M)" | wc -l)

注:2>/dev/nullはエラーメッセージを除外するため、これらのコマンドをgit以外のディレクトリで使用できます。 (ファイル数については、単に0を返します。)

編集

投稿は次のとおりです。

Gitステータス情報をターミナルプロンプトに追加する

Git対応シェルプロンプトの改善

166
0xfe

Gitを確実に「スクリプト化」するための鍵は、「配管」コマンドを使用することです。

開発者は、配管コマンドを変更するときに注意して、非常に安定したインターフェイスを提供するようにします(つまり、リポジトリ状態、stdin、コマンドラインオプション、引数などの特定の組み合わせは、コマンド/オプションが存在します)。配管コマンドの新しい出力バリエーションは、新しいオプションを介して導入できますが、古いバージョンに対して既に作成されたプログラムには問題を導入できません(新しいオプションは存在しなかったため、使用しませんでした(少なくとも、使用されていません)スクリプトの作成時)。

残念ながら、「毎日」のGitコマンドは「磁器」コマンドなので、ほとんどのGitユーザーは配管コマンドに慣れていない可能性があります。磁器と配管コマンドの区別は、メインで行われます git manpage高レベルコマンド(磁器)低レベルコマンド(配管) )


コミットされていない変更を見つけるには、おそらく git diff-index (他のツリー(たとえばHEAD)とインデックス(およびおそらく追跡された作業ツリーのビット)を比較します)、おそらく git diff-files (インデックスに対して作業ツリーを比較します)、そしておそらく git ls-files (ファイルをリストします。例えば、追跡されていない無視されないファイルをリストします).

(以下のコマンドでは、HEADの代わりにHEAD --が使用されることに注意してください。そうでない場合は、コマンド failsHEADという名前のファイルがある場合)

リポジトリが変更をステージングした(まだコミットされていない)かどうかを確認するには、次を使用します。

git diff-index --quiet --cached HEAD --
  • 0で終了する場合、違いはありませんでした(1は違いがあることを意味します)。

作業ツリーにステージングできる変更があるかどうかを確認するには:

git diff-files --quiet
  • 終了コードはgit diff-indexの場合と同じです(0 ==違いなし; 1 ==違い)。

インデックスと作業ツリー内の追跡対象ファイルの組み合わせにHEADに関して変更があるかどうかを確認するには:

git diff-index --quiet HEAD --
  • これは、前の2つの組み合わせのようなものです。主な違いの1つは、作業ツリーで「元に戻す」段階的な変更がある場合(「HEADにある内容に戻る」)、引き続き「違いなし」を報告することです。これと同じ状況で、2つの別々のコマンドは両方とも「差異が存在する」というレポートを返します。

追跡されていないファイルについても言及しました。 「追跡されず無視されない」ことを意味する場合もあれば、単なる「追跡されない」ことを意味する場合もあります(無視されたファイルを含む)。いずれにせよ、git ls-filesは仕事のためのツールです:

「未追跡」の場合(存在する場合、無視されたファイルが含まれます):

git ls-files --others

「追跡されず無視されない」場合:

git ls-files --exclude-standard --others

私の最初の考えは、これらのコマンドに出力があるかどうかを確認することです。

test -z "$(git ls-files --others)"
  • 0で終了する場合、追跡されていないファイルはありません。 1で終了する場合、追跡されていないファイルがあります。

これにより、git ls-filesからの異常終了が「追跡されていないファイルなし」レポートに変換される可能性がわずかにあります(両方とも上記のコマンドのゼロ以外の終了になります)。もう少し堅牢なバージョンは次のようになります。

u="$(git ls-files --others)" && test -z "$u"
  • 考え方は前のコマンドと同じですが、git ls-filesからの予期しないエラーが伝播する可能性があります。この場合、ゼロ以外の出口は「追跡されていないファイルがある」ことを意味するか、エラーが発生したことを意味します。代わりに「エラー」結果を「追跡されていないファイルなし」の結果と組み合わせたい場合は、test -n "$u"を使用します(0の終了は「追跡されていないファイル」を意味し、ゼロ以外はエラーまたは「追跡されていないファイルなし」を意味します)。

別のアイデアは、追跡されていないファイルがないときに--error-unmatchを使用してゼロ以外の終了を引き起こすことです。また、「追跡されていないファイルがない」(exit 1)と「エラーが発生しました」(ゼロ以外の終了、ただし128)を混同するリスクもあります。しかし、01対0以外の終了コードのチェックは、おそらくかなり堅牢です。

git ls-files --others --error-unmatch . >/dev/null 2>&1; ec=$?
if test "$ec" = 0; then
    echo some untracked files
Elif test "$ec" = 1; then
    echo no untracked files
else
    echo error from ls-files
fi

上記のgit ls-filesの例はいずれも、追跡されていない無視されていないファイルのみを検討する場合に--exclude-standardを使用できます。

386
Chris Johnsen

git 1.7.0以降を使用している場合...

このページのすべての回答といくつかの実験を読んだ後、正確さと簡潔さの正しい組み合わせを見つける方法は次のとおりだと思います。

test -n "$(git status --porcelain)"

Gitは、追跡、無視、追跡されないが無視されないなどの間で多くのニュアンスを可能にしますが、典型的なユースケースは、チェックアウトがクリーンでない場合にすべてを停止するビルドスクリプトを自動化することです。

その場合、プログラマが何をするかをシミュレートすることは理にかなっています:git statusと入力して出力を確認します。ただし、表示される特定の単語に依存したくないため、1.7.0で導入された--porcelainモードを使用します。有効にすると、クリーンなディレクトリは出力されません。

次に、test -nを使用して、出力があったかどうかを確認します。

このコマンドは、作業ディレクトリがクリーンな場合は1を返し、コミットする変更がある場合は0を返します。反対の場合は、-n-zに変更できます。これは、これをスクリプト内のコマンドにチェーンするのに便利です。例えば:

test -z "$(git status --porcelain)" || red-alert "UNCLEAN UNCLEAN"

これは、「変更する必要がないか、アラームをオフにする」という事実を効果的に示しています。書くスクリプトによっては、このワンライナーはifステートメントよりも望ましい場合があります。

133
benzado

VonC の答えからの実装:

if [[ -n $(git status --porcelain) ]]; then echo "repo is dirty"; fi
13
Dean Rather

これらの答えのいくつかを見てみました...(そして* nixとwindowsでさまざまな問題がありましたが、これは私が持っていた要件でした)...以下がうまくいくことがわかりました...

git diff --no-ext-diff --quiet --exit-code

* nixの終了コードを確認するには

echo $?   
#returns 1 if the repo has changes (0 if clean)

Window $の終了コードを確認するには

echo %errorlevel% 
#returns 1 if the repos has changes (0 if clean) 

ソース: https://github.com/sindresorhus/pure/issues/115 共有してくれた投稿の@paulirishに感謝

7
mlo55

次のようなスクリプトで 'git statusをカプセル化しない理由:

  • そのコマンドの出力を分析します
  • あなたが必要なものに基づいて適切なエラーコードを返します

そうすれば、スクリプトでその「拡張」ステータスを使用できます。


xfeexcellent answer で言及しているように、git status --porcelainはスクリプトベースのソリューションに役立つ

--porcelain

安定した、解析しやすいスクリプト形式で出力を提供します。
現在、これは--short outputと同じですが、将来変更されないことが保証されており、スクリプトに対して安全です。

4
VonC

xfe の提案に従うように更新された1つのDIYの可能性

#!/bin/sh
exit $(git status --porcelain | wc -l) 

Chris Johnsen で述べたように、これはGit 1.7.0以降でのみ機能します。

4
Robert Munteanu

実行の最後に変更された追跡ファイルまたは無視されない追跡されていないファイルがある場合、ビルドを失敗させる簡単な方法が非常に頻繁に必要でした。

これは、ビルドが残り物を生成するケースを回避するために非常に重要です。

これまでのところ、私が使用することになった最高のコマンドは次のようになります。

 test -z "$(git status --porcelain | tee /dev/fd/2)" || \
     {{ echo "ERROR: git unclean at the end, failing build." && return 1 }}

それは少し複雑に見えるかもしれませんし、誰かがこれが望ましい動作を維持する短絡バリアントを見つけたら感謝します:

  • 偶数が正常な場合、出力はなく、終了コードが成功します
  • 失敗した場合は終了コード1
  • 失敗した理由を説明するstderrのエラーメッセージ
  • 失敗の原因となっているファイルのリストを表示し、stderrを再度表示します。
2
sorin

@ eduard-wirchの回答は非常に完全でしたが、両方を同時にチェックしたかったので、ここに私の最終的なバリエーションがあります。

        set -eu

        u="$(git ls-files --others)"
        if ! git diff-index --name-only --quiet HEAD -- || [ -z "${u:-}" ]; then
            dirty="-dirty"
        fi

Set -eまたは同等の機能を使用して実行しない場合、代わりにu="$(git ls-files --others)" || exit 1を実行できます(または、これが使用された関数で機能する場合に戻ります)

したがって、untracked_filesは、コマンドが正常に成功した場合にのみ設定されます。

その後、両方のプロパティを確認し、変数(または何でも)を設定できます。

2
oliver

あなたもできる

git describe --dirty

。汚れた作業ツリーを検出した場合、Wordの末尾に「-dirty」が追加されます。 git-describe(1)によると:

   --dirty[=<mark>]
       Describe the working tree. It means describe HEAD and appends <mark> (-dirty by default) if
       the working tree is dirty.

。警告:追跡されていないファイルは「ダーティ」とは見なされません。これは、マンページに記載されているように、作業ツリーのみを考慮しているためです。

1
Linus Arver

これは、any追跡されていないファイルがリポジトリに存在するかどうかを調べるための、よりシェルに優しいバリエーションです。

# Works in bash and zsh
if [[ "$(git status --porcelain 2>/dev/null)" = *\?\?* ]]; then
  echo untracked files
fi

これは2番目のプロセスgrepを分岐せず、gitリポジトリにいるかどうかを確認する必要もありません。シェルプロンプトなどに便利です。

1
docwhat

このスレッドからの回答のより良い組み合わせがあるかもしれません..しかし、これは私のために動作します...あなたの.gitconfig[alias]セクションのために...

          # git untracked && echo "There are untracked files!"
untracked = ! git status --porcelain 2>/dev/null | grep -q "^??"
          # git unclean && echo "There are uncommited changes!"
  unclean = ! ! git diff --quiet --ignore-submodules HEAD > /dev/null 2>&1
          # git dirty && echo "There are uncommitted changes OR untracked files!"
    dirty = ! git untracked || git unclean
0
Alex Gray

dirty state =未追跡ファイルを含むすべての変更を検出するために使用する最も単純な自動テスト

git add --all
git diff-index --exit-code HEAD

注意:

  • add --alldiff-indexがなければ、追跡されていないファイルに気付きません。
  • 通常、エラーコードをテストした後にgit resetを実行して、すべてのステージングを解除します。
0
uvsmtid