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


243

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

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

スクリプトから?

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


3
ステージングされていない変更済みファイルがある場合、git statusは1を返します。しかし、一般的には、gitツールはリターンステータスに関して特に完全ではないことがわかりました。EG git diffは、違いがあるかどうかにかかわらず0を返します。
2010

11
@intuited:あなたが必要な場合diffの違いではなく、コマンドが正常に実行の有無を示すために、あなたは使用する必要があります--exit-code--quiet。gitコマンドは一般に、コマンドの成功を示すゼロまたはゼロ以外の終了コードを返すことと非常に一貫しています。
CBベイリー

1
@Charles Bailey:いいね、そのオプションを逃した。ありがとう!私はそれをするために本当にそれを必要としたことがなかったと思います、またはおそらくそれのためにマンページを精査したでしょう。:)
2010

1
robert-これはコミュニティにとって非常に紛らわしいトピックです(「磁器」が異なるコンテキストで異なって使用されることは助けにはなりません)。選択した回答は、2.5倍高い投票数の回答を無視して、より堅牢になり、git設計に従います。@ChrisJの答えを見ましたか?
2016年

1
@ロバート-あなたがあなたの受け入れられた答えを変えることを検討するかもしれないことを望んでいます。選択したものは、より壊れやすいgit 'porcelain'コマンドに依存しています。実際の正解(2倍の賛成投票も使用)は、正しいgit 'plumbing'コマンドに依存しています。その正解は、元々Chris Johnsenによるものでした。今日私がこのページを誰かに紹介しなければならなかったのは3回目なので、これを取り上げますが、答えを指摘するだけでなく、受け入れられた答えが次善/境界線が間違っている理由を説明しました。ありがとうございました!
マイク

回答:


173

素晴らしいタイミング!数日前に、ブログの投稿を書いて、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対応のシェルプロンプト


8
git bashの完了には、プロンプトで実行しているほとんどのことを実行するためのシェル関数が付属していることに注意してください__git_ps1。ブランチ名が表示されます。リベース、am-apply、merge、またはbisectの処理中の場合の特別な扱いも含まれます。また、環境変数GIT_PS1_SHOWDIRTYSTATEを設定して、ステージングされていない変更にはアスタリスクを、ステージングされた変更にはアスタリスクを付けることができます。(私は、追跡されていないファイルを示し、いくつかのgit-describe出力を与えることもできます)
Cascabel

8
警告:git diff --shortstat変更がすでにインデックスにある場合、偽陰性を与えます。
Marko Topolnik 2013年

4
git status --porcelaingit diff --shortstat新しく作成された空のファイルをキャッチしないため、推奨されます。あなたはそれをどんなきれいな作業木でも試すことができます:touch foo && git diff --shortstat
カンパドレナリン

7
いいえ-磁器は、出力が人間用であり、壊れやすいことを意味します!! @ChrisJohnsenの回答を参照してください。これは、スクリプトに適した安定したオプションを正しく使用しています。
マイク

7
磁器オプションに関するgit-statusのmanページの@mike:「出力をスクリプトの解析しやすい形式で提供します。これは短い出力に似ていますが、Gitのバージョン全体で、ユーザー設定に関係なく安定したままです。 」
itsadok 2015

399

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

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

残念ながら、「日常的な」Gitコマンドは「磁器」コマンドなので、ほとんどのGitユーザーは配管コマンドに慣れていない可能性があります。磁器と配管コマンドの区別は、メインのgitマンページで行われます(高レベルコマンド(磁器)および低レベルコマンド(配管)というタイトルのサブセクションを参照


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

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

リポジトリに段階的な変更(まだコミットされていない)があるかどうかを確認するには、次のコマンドを使用します。

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

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

git diff-files --quiet
  • 終了コードはgit diff-index0==違いなし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"(exit 0は「追跡されていないファイル」を意味し、ゼロ以外はエラーまたは「追跡されていないファイル」を意味します)。

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

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使用できます。


5
それgit ls-files --othersローカルの追跡されていないファイルを与えることを指摘したいのですgit status --porcelainが、受け入れられた答えはgitリポジトリの下にあるすべての追跡されていないファイルを与えます。私は元のポスターがどちらを望んでいたかではありませんが、両方の違いは興味深いです。
Eric O Lebigot、2010

1
@phunehehe:でpathspecを指定する必要があり--error-unmatchます。(例)git ls-files --other --error-unmatch --exclude-standard .(最後のピリオドに注意してください。これはcwdを指します。これを作業ツリーの最上位ディレクトリから実行してください)。
Chris Johnsen 2013

8
@phs:stat(2)情報の不一致によって引き起こされるいくつかの「誤検知」を回避するためgit update-index -q --refreshに、前に行う必要がある場合がありdiff-indexます。
Chris Johnsen 2013年

1
git update-index必要に噛まれました!これは、何かを変更せずにファイルに触れている場合に重要です。
Nakedible

2
@RobertSiemer「ローカル」とは、現在のディレクトリの下にあるファイルを意味します。これは、メインのgitリポジトリのにある可能性があります。--porcelainすべての人跡未踏のファイルがで見つかったソリューションリスト全体gitリポジトリ(マイナスのgit-無視されたファイル)は、あなたがそのサブディレクトリの1の内側にある場合でも。
Eric O Lebigot 2016

139

git 1.7.0以降を使用していると仮定します...

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

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

gitは追跡、無視、追跡されないが無視されないなどの間で多くのニュアンスを許容しますが、典型的な使用例はビルドスクリプトを自動化することであり、チェックアウトがクリーンでない場合はすべてを停止したいと考えています。

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

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

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

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

これは事実上、「変更する必要がないか、アラームを発することはありません」と述べています。このワンライナーは、記述しているスクリプトによっては、ifステートメントよりも望ましい場合があります。


1
私にとって、他のすべてのコマンドは、LinuxとWindowsの間で同じ担当者に対して異なる結果を出していました。このコマンドを実行すると、両方で同じ出力が得られました。
Adarsha 2016年

6
質問に答えていただき、何度もとりとめないでください。明確な答えは提供しないでください。
NateS

手動展開スクリプトの場合は、これを test -n "$(git diff origin/$branch)"ローカルコミットが許可されないようにします
Erik Aronesty


8

これらの回答のいくつかを確認しました...(そして* nixとウィンドウでさまざまな問題がありましたが、これは私が持っていた要件でした)...次はうまく機能することがわかりました...

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に感謝


4

なぜ次のgit statusようなスクリプトでカプセル化しないでください。

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

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


0xFEのは、彼の中で言及優秀な答えはgit status --porcelain任意のスクリプトベースのソリューションに尽力しています

--porcelain

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


たぶん私は怠惰だからです。かなり頻繁に遭遇するユースケースのように、これにはビルトインがあると思いました。
ロバートムンテアヌ

私はあなたの提案に基づいて解決策を投稿しましたが、私はそれにあまり満足していません。
Robert Munteanu

4

1つのDIYの可能性、0xfeの提案に従うように更新

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

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


6
これの問題は、将来のバージョンで文字列「working directory clean」を確実に期待できないことです。--porcelainフラグは解析用であったため、より良い解決策は、exit $(git status --porcelain | wc -l)
0xfe

@ 0xfe- --porcelainフラグがいつ追加されたか知っていますか?1.6.4.2では機能しません。
ロバート・ムンテアヌ2010

@ロバート:やってみてgit status --short
VonC、2010

3
git status --porcelainそしてgit status --short両方の1.7.0で導入されました。--porcelainこれはgit status --short、将来的にフォーマットを変更できるようにするために特別に導入されました。したがって、git status --shortと同じ問題が発生しますgit status(「配管」コマンドではないため、出力はいつでも変更される可能性があります)。
クリスジョンセン2010

@Chris、背景情報をありがとう。Git 1.7.0の時点でこれを行う最良の方法を反映するように、回答を更新しました。
ロバートムンテアヌ2010

2

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

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

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

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

それは少し複雑に見えるかもしれませんが、これが望ましい動作を維持するショートされたバリアントを誰かが見つけたら幸いです:

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

2

@ 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は、コマンドが正しく成功した場合にのみ設定されます。

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


1

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

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

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


1

あなたもすることができます

git describe --dirty

。ダーティな作業ツリーを検出すると、末尾に「-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.

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


0

このスレッドからの回答のより良い組み合わせがあるかもしれません..しかし、これは私にとってはうまくいきます...あなた.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

ダーティな状態を検出するために使用する最も簡単な自動テスト= 追跡されていないファイルを含むすべての変更

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

注意:

  • なし add --all diff-indexは追跡されていないファイルに気づきません。
  • 通常、git resetエラーコードをテストした後に実行して、すべてを元に戻します。

問題は具体的には「スクリプトから」です...ダーティな状態をテストするためだけにインデックスを変更することはお勧めできません。
ScottJ 2017

@ScottJ、それが問題を解決するとき、すべての人がインデックスを変更できるかどうかに必ずしも厳密ではありません。バージョン番号付きのソースに自動パッチを適用する自動ジョブの場合を考えて、タグを作成します。確認する必要があるのは、他のローカル変更が絶対に邪魔にならないことです(インデックスにあるかどうかに関係なく)。これまでのところ、これは追跡されていないファイルを含むすべての変更に対する信頼できるテストでした。
uvsmtid 2017年

-3

これが最良で最もクリーンな方法です。選択された回答が何らかの理由で機能しませんでした。ステージングされた、コミットされていない新しいファイルである変更が反映されませんでした。

function git_dirty {
    text=$(git status)
    changed_text="Changes to be committed"
    untracked_files="Untracked files"

    dirty=false

    if [[ ${text} = *"$changed_text"* ]];then
        dirty=true
    fi

    if [[ ${text} = *"$untracked_files"* ]];then
        dirty=true
    fi

    echo $dirty
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.