커밋되지 않은 변경 사항이 있는지 프로그래밍 방식으로 확인하려면 어떻게합니까?


226

Makefile에서 커밋되지 않은 변경 사항 (작업 트리 또는 인덱스)이있는 경우 특정 작업을 수행하고 싶습니다. 가장 깨끗하고 효율적인 방법은 무엇입니까? 어떤 경우에는 반환 값 0으로 종료하고 다른 경우에는 0이 아닌 명령이 내 목적에 적합합니다.

git status출력을 통해 실행 하고 파이프 할 수는 grep있지만 더 나은 방법이 있어야한다고 생각합니다.


4
Git
jb

답변:


289

UPDATE : 영업 다니엘 Stutzbach는 지적 코멘트에 이 간단한 명령이 있음을 git diff-index그를 위해 일했다 :

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

( 노르 나곤 은 주석에 언급 되었지만, 만졌지만 색인과 내용이 동일한 파일이있는 경우 git update-index --refresh이전 에 실행 해야 git diff-index하며 그렇지 않으면 diff-index트리가 더럽다고 잘못보고한다고 언급합니다)

bash 스크립트에서 명령 을 사용하는 경우 " 명령이 성공했는지 확인하는 방법? " 을 참조하십시오 .

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

참고 : 같이 주석 에 의해 안토니 Sottile

git diff-index HEAD ...커밋이없는 분기 (예 : 새로 초기화 된 리포지토리)에서는 실패합니다.
내가 찾은 한 가지 해결 방법은git diff-index $(git write-tree) ...

그리고 haridsv밖으로 점 코멘트에git diff-filesA의 새로운 파일은 DIFF로 감지하지 않습니다.
보다 안전한 방법은 git add파일 스펙에서 먼저 실행 한 다음 git diff-index실행하기 전에 색인에 추가 된 항목이 있는지 확인하는 것 git commit입니다.

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

그리고 의견에 6502 보고서 :

내가 부딪친 한 가지 문제는 git diff-index실제로 파일의 타임 스탬프를 제외하고는 실제로 차이가 없다는 것을 알 수 있다는 것입니다.
실행 git diff을 해결해 번 문제 (놀랍게도 것은, git diff실제로 여기서 의미하는 샌드 박스의 내용을 변경 않습니다 .git/index)

이 타임 스탬프 문제는 git이 docker에서 실행되는 경우에도 발생할 수 있습니다 .


원래 답변 :

"프로그래밍 방식으로"는 절대로 도자기 명령에 의존하지 않습니다 .
항상 배관 명령 에 의존하십시오 .

또한 "을 참조하십시오 망할 놈의 더러운 지수 또는 비 추적 파일을 확인 (같은 대안" 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
"스크립트에 대한 배관 대 도자기"원칙은 Jakub Narębski가 반복해서 나에게 언급 한 교훈입니다 . " git에서 현재 프로젝트에 대한 모든 로그를 나열하는 방법은 무엇입니까? ", " git : changelog day by day ", ...
VonC

18
당신이 제안하는 링크 중 일부를 클릭 한 후, 내가 찾던 것을 발견 git diff-index --quiet HEAD했습니다..
Daniel Stutzbach

11
@DanielStutzbach : HEAD작업 디렉토리에 파일이 있으면 실패 할 수 있습니다 . 더 나은 사용 git diff-index --quiet HEAD --.
David Ongaro

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

2
@EM의 리턴 코드 git status는 실제로이 테스트에서 무시됩니다. 출력 만 봅니다. 확인 이 bash는 관련 페이지를 더에 대한 정보를 원하시면 [, [[bash는 작품을 테스트하는 방법에 대해 설명합니다.
Nepthar

2
이것은 거의 정답이지만 스크립트의 경우 여기에--porcelain 표시된대로 매개 변수 를 사용하는 것이 좋습니다.
Mariusz Pawelski

2
git status -s -uall추적되지 않은 파일을 포함하는 데 사용할 수 있습니다 .
barfuin

59

git diff --exit-code변경 사항이 있으면 0이 아닌 값을 반환합니다. 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 8

2
경고, 이것은 git rm, AFAICS를 사용하여 스테이징 영역에서 제거 된 파일을 포착하지 않습니다.
nmr

24
에 의해 새로운 (추적되지 않은) 파일이 감지되지 않습니다 git diff --quiet && git diff --cached --quiet.
4LegsDrivenCat

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")에 다음과 elsegit add "$file"; git commit -m "your autocommit process" "$file"
toddkaufmann

당신이 있는지 확인하면 git status -sgit status --porcelain ; git clean -nd대신, 정크 디렉토리가 너무 여기 표면화 될 것이다 보이지있는 git status.
ecmanaut

4

다른 답변에서 지적했듯이 그러한 명령으로 간단합니다.

git diff-index --quiet HEAD --

마지막 두 개의 대시를 생략하면 파일 이름이이면 명령이 실패합니다 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

질문을 참조하십시오. 추적되지 않은 파일은 변경 되지 않습니다 . 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

파이썬과 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'porcelain'명령입니다. git 버전간에 변경 될 수 있으므로 스크립트에서 도자기 명령을 사용하지 마십시오. 대신 '배관'명령을 사용하십시오.
spuder

3
나는 당신이 그것을 사용하도록 업데이트했다면 git status --porcelain(이 목적을 위해-스크립트에서 파싱 할 수있는 안정적인 형식을 의미합니다), 아마도 -z (줄 바꿈 대신 null로 구분되어 있습니까?)를 사용 하여이 아이디어로 유용한 것을 할 수 있다고 생각합니다 . @ codyc4321 자세한 내용은 stackoverflow.com/questions/6976473/… 을 참조하십시오
msouth
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.