Taahhüt edilmemiş değişiklikler olup olmadığını programlı olarak nasıl belirlerim?


227

Makefile'de, taahhüt edilmemiş değişiklikler varsa (çalışma ağacında veya dizinde) belirli eylemleri gerçekleştirmek istiyorum. Bunu yapmanın en temiz ve verimli yolu nedir? Bir durumda sıfır, diğerinde sıfır olmayan bir dönüş değeri ile çıkan bir komut, amacım için uygun olacaktır.

Ben çalıştırabilir git statusve boru içinden çıktı grep, ancak daha iyi bir yolu olmalı gibi hissediyorum.


4
Git
jb

Yanıtlar:


290

GÜNCELLEME : OP Daniel Stutzbach işaret yorumlarda bu basit bir komut olduğunu git diff-indexonun için çalıştı:

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

( Nornagon bahseder yorumlarda içerikleri endeksinde aynıdır dokundu edilmiş dosyalar, ancak varsa, çalıştırmak gerekir git update-index --refreshönce git diff-index, aksi takdirde diff-indexyanlış ağaç kirli olduğunu bildirir)

Daha sonra bash komut dosyasında kullanıyorsanız " Bir komutun başarılı olup olmadığını nasıl kontrol edersiniz ? "

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

Not: Anthony Sottile tarafından yorumlandığı gibi

git diff-index HEAD ...taahhütleri olmayan bir dalda başarısız olur (yeni başlatılan bir depo gibi).
Bulduğum bir geçici çözümgit diff-index $(git write-tree) ...

Ve haridsvPuan yorumlarda olduğu git diff-filesbir üzerine yeni dosyanın bir fark olarak algılamaz.
Daha güvenli yaklaşım git add, önce dosya spec üzerinde çalışıyor ve daha sonra git diff-indexçalıştırmadan önce dizine bir şey eklenip eklenmediğini görmek için kullanılıyor git commit.

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

Ve 6502 yorumlarda raporlar:

Ben çarptım bir sorun git diff-index, gerçekten zaman damgaları dışında hiçbiri olmadığında farklılıklar olduğunu söyleyecektir. Bir kez
koşmak git diffsorunu çözer (şaşırtıcı bir şekilde git diff, sanal alanın içeriğini değiştirir, yani burada .git/index)

Bu zaman damgası sorunları, git docker'da çalışıyorsa da oluşabilir .


Orijinal cevap:

"Programlı" hiçbir zaman porselen komutlarına güvenmemek anlamına gelir .
Daima tesisat komutlarına güvenin .

Alternatifler için " Git ile kirli bir dizini veya izlenmemiş dosyaları kontrol etme " konusuna da bakın (örneğin git status --porcelain)

Konuştuğumuz gibi yazılan yeni " require_clean_work_treeişlev " den ilham alabilirsiniz ;) (Ekim 2010 başı)

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
"Senaryo için sıhhi tesisat vs porselen" ilkesi Jakub Narębski'nin bana defalarca bahsettiği bir derstir : " Git'teki mevcut proje için tüm günlük nasıl listelenir? ", " Git: günden güne değişiklik ", ...
VonC

18
Önerdiğiniz bağlantılardan bazılarını tıkladıktan sonra, ben aradığımı buldum: git diff-index --quiet HEAD.
Daniel Stutzbach

11
@DanielStutzbach: HEADÇalışma dizininde bir dosyanız varsa bu başarısız olabilir . Daha iyi kullanım git diff-index --quiet HEAD --.
David Ongaro

7
Ve yine de git status --helpdevletlerde: --porselen Çıktıya komut dosyaları için ayrıştırılması kolay bir
Ed Randall

7
@VonC gerçekten mantıklı değil. Bu şekilde her şeyi tersine çevirebilirsiniz. - porselen yakında kırılacağı izlenimini veriyor. Değilse, porselen değil, sıhhi tesisat olarak adlandırılmalıdır. --Porcelain kullanmak betiğinizin bozulmamasına neden olur; Senaryonuzun kırılmasını istiyorsanız --porselen kullanmamalısınız !!. Yani bu tamamen anlaşılmaz ve herkesi atıyor.
Xennex81

104

Diğer çözümler çok kapsamlı olsa da, gerçekten hızlı ve kirli bir şey istiyorsanız, böyle bir şey deneyin:

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

Sadece durum özetinde çıktı olup olmadığını kontrol eder.


7
benim için çalışıyor. tersi için -n kullanın (değişiklikleriniz vardır) örneğin `` if [[-n $ (git status -s)]]; sonra ... fi`
aaron

Bu işe yarar, ancak [[ ... ]]sözdiziminin gerçekte ne yaptığını söyleyebilir misiniz ? Daha önce hiç böyle bir şey görmemiştim.
GMA

2
@EM, git statusbu testte dönüş kodu aslında yok sayılır. Sadece çıktıya bakar. Check out bu bash ilgili sayfayı daha üzerinde bilgi için [, [[bash eserlerini test ve nasıl.
Nepthar

2
Bu neredeyse doğru cevap, ancak komut dosyası için burada--porcelain gösterildiği gibi parametre kullanmak daha iyidir
Mariusz Pawelski

2
git status -s -uallİzlenmemiş dosyaları dahil etmek için kullanmak isteyebilirsiniz .
barfuin

59

git diff --exit-codeherhangi bir değişiklik varsa sıfırdan farklı bir değerle döner; git diff --quietçıktı olmadan aynıdır. Çalışma ağacını ve dizini kontrol etmek istediğiniz için şunu kullanın:

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

Veya

git diff --quiet HEAD

Her ikisi de size aşamalı olarak kaydedilen taahhüt edilmemiş değişiklikler olup olmadığını söyleyecektir.


6
Bunlar eşdeğer değil. Tek komut git diff --quite HEAD, dizinin temiz olup olmadığını değil, yalnızca çalışma ağacının temiz olup olmadığını söyleyecektir. Örneğin, fileHEAD ~ ve HEAD arasında değiştirildiyse, daha sonra git reset HEAD~ -- file, dizinde aşamalı değişiklikler olsa bile yine de 0'dan çıkacaktır (wt == HEAD, ancak index! = HEAD).
Chris Johnsen

2
Uyarı, bu, git rm, AFAICS ile hazırlama alanından kaldırılan dosyaları yakalamaz.
nmr

24
Yeni (izlenmeyen) dosyalar tarafından algılanmıyor git diff --quiet && git diff --cached --quiet.
4LegsDrivenCat

17

@ Nepthar'ın cevabına genişleyen:

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

1
Bu iyi; Bunu tek bir dosyayı test ederek $(git status -s "$file")ve sonra elsemaddede git add "$file"; git commit -m "your autocommit process" "$file"
otomatik olarak tamamlamak

Bunu yaparsanız git status -sbir git status --porcelain ; git clean -ndyerine, önemsiz dizinleri de burada ortaya edilecektir görünmez olan git status.
ecmanaut

4

Diğer cevapta belirtildiği gibi, bu komut kadar basit:

git diff-index --quiet HEAD --

Son iki tireyi atlarsanız, adlı bir dosyanız varsa komut başarısız olur HEAD.

Misal:

#!/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...

Dikkat kelimesi: Bu komut izlenmeyen dosyaları yok sayar.


2
Bu cevaba yapılan yorumlarda belirtildiği gibi, bu yeni eklenen dosyaları algılamaz
minexew

Hayır, dizin dosyalarına yeni eklenenleri algılar. Yeni kontrol edildi.
sanmai

Soruya bakın. İzlenmeyen dosyalar değişiklik değildir . git addve git cleankurtarmaya
sanmai

4

İşaretlenmemiş ve aşamalı dosyaları listelemek için bazı kullanışlı git takma adları oluşturdum:

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

Sonra kolayca aşağıdakileri yapabilirsiniz:

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

Eğer bir komut bir yere oluşturarak daha okunaklı yapabilirsiniz PATHdenilen git-has:

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

Şimdi yukarıdaki örnekler basitleştirilebilir:

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

Tamlık için burada izlenmeyen ve yok sayılan dosyalar için benzer takma adlar bulunur:

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

2

Python ve GitPython paketi ile:

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

Depo temiz değilse True döndürür


Bu, diğer yorumlarda bahsedilen "zaman damgası" sorunlarından bazılarını önledi
Jason

1
GitPython'un sadece git CLI'yi kullandığını unutmayın. Ayarlarsanız LOGLEVEL=DEBUGgöreceğiniz tüm POPEN çalıştırmak için kullanarak bunu komutlarınıgit diff
Jason

-3

İşte en iyi, en temiz yol.

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
Hayır, bu en iyisi değil. git statusbir 'porselen' komutudur. Git sürümleri arasında geçiş yapabildikleri için komut dosyalarında porselen komutları kullanmayın. Bunun yerine 'sıhhi tesisat' komutlarını kullanın.
spuder

3
Bunu kullanmak için güncellediyseniz git status --porcelain(bu amaç için - bir komut dosyasında ayrıştırabileceğiniz kararlı bir biçim), muhtemelen -z (satırsonu yerine null-ayrılmış?) . @ codyc4321 ayrıntılar için stackoverflow.com/questions/6976473/… adresine bakın
msouth
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.