コミットされていない変更があるかどうかをプログラムで判断するにはどうすればよいですか?


226

Makefileで、コミットされていない変更(作業ツリーまたはインデックスのいずれか)がある場合、特定のアクションを実行したいと思います。それを行うための最もクリーンで最も効率的な方法は何ですか?ある場合はゼロの戻り値で終了し、他の場合はゼロ以外の値で終了するコマンドは、私の目的に適しています。

git status出力を実行してにパイプすることができますgrepが、もっと良い方法があるはずだと感じています。


4
Git
jbを

回答:


289

更新:OP Daniel Stutzbach は、この簡単なコマンドgit diff-indexが彼のために機能したことをコメントで指摘しています

git update-index --refresh 
git diff-index --quiet HEAD --

nornagonコメントで、触れられたファイルがあり、その内容がインデックスと同じである場合、git update-index --refresh前に実行する必要があることを述べています。git diff-indexそうしないdiff-indexと、ツリーがダーティであると誤って報告されます)

その後、bashスクリプトで使用している場合は、「コマンドが成功したかどうかを確認する方法」を確認できます。

git diff-index --quiet HEAD -- || echo "untracked"; // do something about it

注:Anthony Sottileによるコメント

git diff-index HEAD ...コミットされていないブランチ(新しく初期化されたリポジトリなど)では失敗します。
私が見つけた1つの回避策はgit diff-index $(git write-tree) ...

そして、コメントharidsv指摘するように、新しいファイルでは差分として検出されません。 より安全なアプローチは、最初にファイル仕様で実行し、次に実行前にインデックスに何かが追加されたかどうかを確認することです。git diff-files
git addgit diff-indexgit commit

git add ${file_args} && \
git diff-index --cached --quiet HEAD || git commit -m '${commit_msg}'

そして6502はコメントで報告します:

私がぶつかった1つの問題は、git diff-index実際にはファイルのタイムスタンプ以外に違いがない場合に、違いがあることがわかるということです。一度
実行git diffすると問題が解決します(驚くほど十分に、git diff実際にはサンドボックスの内容が変更されます。つまり、ここで意味します.git/index)。

これらのタイムスタンプの問題は、gitがdocker実行されている場合にも発生する可能性があります。


元の答え:

「プログラム的に」とは、磁器のコマンドに決して依存しないことを意味します
常に配管コマンドに依存します

代替手段(など)については、「Gitを使用したダーティインデックスまたは追跡されていないファイルの確認」も参照してください。git status --porcelain

たちが話すにつれて書かれた新しい「require_clean_work_tree機能」からインスピレーション得ることができます ;)(2010年10月初旬)

require_clean_work_tree () {
    # Update the index
    git update-index -q --ignore-submodules --refresh
    err=0

    # Disallow unstaged changes in the working tree
    if ! git diff-files --quiet --ignore-submodules --
    then
        echo >&2 "cannot $1: you have unstaged changes."
        git diff-files --name-status -r --ignore-submodules -- >&2
        err=1
    fi

    # Disallow uncommitted changes in the index
    if ! git diff-index --cached --quiet HEAD --ignore-submodules --
    then
        echo >&2 "cannot $1: your index contains uncommitted changes."
        git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
        err=1
    fi

    if [ $err = 1 ]
    then
        echo >&2 "Please commit or stash them."
        exit 1
    fi
}

12
「スクリプト用の配管と磁器」の原則は、JakubNarębskiが繰り返し私に言ったレッスンです:「現在のプロジェクトのすべてのログをgitで一覧表示する方法は?」、「git:日
ごとの変更

18
あなたが提案したリンクのいくつかをクリックした後、私は探していたものを見つけました:git diff-index --quiet HEAD
Daniel Stutzbach、2010年

11
@DanielStutzbach:HEAD作業ディレクトリにファイルが呼び出されていると、失敗する可能性があります。よく使うgit diff-index --quiet HEAD --
David Ongaro 2014年

7
さらに、マニュアルにgit status --helpは次のよう に記載されています 。--porcelain スクリプトの解析しやすい形式で出力を提供します。これは短い出力に似ていますが、ユーザー設定に関係なく、Gitバージョン全体で安定したままです。詳細については、以下を参照してください。
Ed Randall

7
@VonCは本当に意味がありません。このようにして、すべてを逆にねじることができます。-磁器はすぐに壊れそうな印象を与えます。そうでない場合は、磁器ではなく配管と呼ばれる必要があります。--porcelainを使用すると、スクリプトが壊れないため、磁器スクリプトではありません;-)。スクリプトを壊したい場合は、-porcelain !!を使用しないでください。したがって、これは完全に理解できず、全員を捨てます。
Xennex81

104

他の解決策は非常に徹底していますが、本当に迅速で汚いものが必要な場合は、次のような方法を試してください。

[[ -z $(git status -s) ]]

ステータスの概要に出力があるかどうかを確認するだけです。


7
私のために働く。逆の場合は-nを使用します(変更があります)。たとえば、 `if [[-n $(git status -s)]]; その後... fi`
aaron

これは機能し[[ ... ]]ますが、構文が実際に何をしているのかわかりますか?私はそのようなものを見たことがありません。
GMA、2014年

2
@EMの戻りコードはgit status、このテストでは実際には無視されます。出力のみを確認します。の詳細とbashでのテストの仕組みについては、このbash関連ページをご覧ください。[[[
Nepthar 2016年

2
これはほとんど正しい答えですが、スクリプトのためにそれを使用する方が良いでしょう--porcelain示すように、パラメータここで
マリウシュPawelski

2
を使用git status -s -uallして、追跡されていないファイルを含めることができます。
バルフイン

59

git diff --exit-code変更がある場合はゼロ以外を返します。git diff --quiet出力なしで同じです。作業ツリーとインデックスを確認したいので、

git diff --quiet && git diff --cached --quiet

または

git diff --quiet HEAD

ステージングされているかどうかにかかわらず、コミットされていない変更があるかどうかがわかります。


6
それらは同等ではありません。単一のコマンドgit diff --quite HEADは、作業ツリーがクリーンであるかどうかを通知するだけで、インデックスがクリーンであるかどうかは通知しません。たとえば、fileHEAD〜とHEADの間で変更された場合、の後git reset HEAD~ -- file、インデックスに段階的な変更が存在していても(wt == HEAD、ただしインデックス!= HEAD)、0を終了します。
Chris Johnsen、

2
警告、これはgit rm、AFAICSでステージング領域から削除されたファイルをキャッチしません。
nmr 2013

24
新しい(追跡されていない)ファイルはによって検出されませんgit diff --quiet && git diff --cached --quiet
4LegsDrivenCat 2015年

17

@Neptharの答えを拡張する:

if [[ -z $(git status -s) ]]
then
  echo "tree is clean"
else
  echo "tree is dirty, please commit changes before running this"
  exit
fi

1
これはいい; 私はそれを使用して、テストによって単一のファイルを自動コミットしてから$(git status -s "$file")else節の中で使用しますgit add "$file"; git commit -m "your autocommit process" "$file"
toddkaufmann

あなたはことを確認した場合、代わりに、ジャンクディレクトリがここにも浮上するだろうに見えないです。git status -sgit status --porcelain ; git clean -ndgit status
ecmanaut

4

他の答えで指摘されているように、そのようなコマンドで十分です:

git diff-index --quiet HEAD --

最後の2つのダッシュを省略すると、という名前のファイルがある場合、コマンドは失敗しますHEAD

例:

#!/bin/bash
set -e
echo -n "Checking if there are uncommited changes... "
trap 'echo -e "\033[0;31mFAILED\033[0m"' ERR
git diff-index --quiet HEAD --
trap - ERR
echo -e "\033[0;32mAll set!\033[0m"

# continue as planned...

注意:このコマンドは、追跡されていないファイルを無視します。


2
その答えへのコメントで指摘されているように、これは新しく追加されたファイルを検出しません
minexew

いいえ、新しく追加されたインデックスファイルを検出します。ちょうどチェックしました。
sanmai 2017年

質問を参照してください。追跡されていないファイルは変更されません。git addそしてgit clean救助に
-sanmai

4

いくつかの便利なgitエイリアスを作成して、ステージングされていないファイルとステージングされたファイルをリストします。

git config --global alias.unstaged 'diff --name-only'
git config --global alias.staged 'diff --name-only --cached'

その後、次のようなことを簡単に行うことができます。

[[ -n "$(git unstaged)" ]] && echo unstaged files || echo NO unstaged files
[[ -n "$(git staged)" ]] && echo staged files || echo NO staged files

PATH呼び出し先のどこかにスクリプトを作成して、読みやすくすることができますgit-has

#!/bin/bash
[[ $(git "$@" | wc -c) -ne 0 ]]

上記の例は、次のように簡略化できます。

git has unstaged && echo unstaged files || echo NO unstaged files
git has staged && echo staged files || echo NO staged files

完全を期すために、追跡されず無視されたファイルの同様のエイリアスを次に示します。

git config --global alias.untracked 'ls-files --exclude-standard --others'
git config --global alias.ignored 'ls-files --exclude-standard --others --ignored'

2

PythonとGitPythonパッケージの場合:

import git
git.Repo(path).is_dirty(untracked_files=True)

リポジトリがクリーンでない場合はTrueを返します


これにより、他のコメントで言及されている「タイムスタンプ」の問題の一部が回避されました
Jason

1
GitPythonはgit CLIも使用していることに注意してください。設定LOGLEVEL=DEBUGすると、実行に使用するすべてのPopenコマンドが表示されますgit diff
Jason

-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
}

4
いいえ、これは最善ではありません。git status「磁器」コマンドです。磁器のコマンドはgitのバージョン間で変わる可能性があるため、スクリプトでは使用しないでください。代わりに、「配管」コマンドを使用してください。
spuder 2017年

3
私がそれを使用するように更新した場合git status --porcelain(これはこの目的のために意味します-スクリプトで解析できる安定したフォーマット)、おそらく-z(改行の代わりにnullで区切られていますか?) 。@ codyc4321詳細については、stackoverflow.com / questions / 6976473 /… を参照
msouth
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.