Git ile kirli bir dizini veya izlenmeyen dosyaları kontrol etme


243

Git depomda taahhüt edilmemiş değişiklik olup olmadığını nasıl kontrol edebilirim:

  1. Dizine eklenen, ancak kaydedilmeyen değişiklikler
  2. İzlenmeyen dosyalar

bir senaryodan?

git-status git sürüm 1.6.4.2 ile her zaman sıfır gibi görünüyor.


3
işaretlenmemiş değiştirilmiş dosyalar varsa git durumu 1 değerini döndürür. Ancak genel olarak, git araçlarının dönüş durumu ile özellikle ayrıntılı olmadığını düşünüyorum. EG git diff, fark olsa da olmasa da 0 değerini döndürür.
2010'da

11
@intuited: Eğer gerekiyorsa difffarklılıklardan ziyade komuta çalışan başarılı varlığını veya yokluğunu gösteren o zaman kullanıma gerek --exit-codeya --quiet. git komutları genellikle komutun başarılı olduğunu göstermek için sıfır veya sıfır olmayan bir çıkış kodu döndürmeyle çok tutarlıdır.
CB Bailey

1
@Charles Bailey: Hey, harika, bu seçeneği kaçırdım. Teşekkürler! Sanırım bunu yapmak için gerçekten hiç ihtiyacım olmadı, ya da muhtemelen manpage'i bulacağım. Beni düzeltilmiş sevindim :)
intuited

1
robert - bu topluluk için çok kafa karıştırıcı bir konudur ('porselen' in farklı bağlamlarda farklı şekilde kullanılmasına yardımcı olmaz). Seçtiğiniz cevap, daha sağlam olan 2,5 kat daha fazla oy kullanan bir cevabı yok sayar ve git tasarımını takip eder. @ChrisJ'in cevabını gördün mü?
mike

1
@Robert - Umarım kabul ettiğiniz cevabı değiştirmeyi düşünebilirsiniz. Seçtiğiniz, daha kırılgan git 'porselen' komutlarına dayanır; gerçek doğru cevap (ayrıca 2x upvotes ile) doğru git 'plumbing' komutlarına dayanır. Bu doğru cevap başlangıçta Chris Johnsen'di. Ben bugün bu sayfayı birine başvurmak zorunda 3. kez olduğu gibi bu getirmek, ama ben sadece cevap işaret edemez, ben kabul edilen cevap neden alt-optimal / sınırda yanlış olduğunu açıklamak var. Teşekkür ederim!
mike

Yanıtlar:


173

Harika zamanlama! Birkaç gün önce tam olarak bunun hakkında bir blog yazısı yazdım, istemime git durum bilgilerinin nasıl ekleneceğini anladım.

İşte yaptığım şey:

  1. Kirli durum için:

    # Returns "*" if the current git branch is dirty.
    function evil_git_dirty {
      [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
    }
  2. İzlenmeyen dosyalar için ( Size güzel ayrıştırılabilir çıktı veren --porcelainbayrağa dikkat edin 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 --shortstatDaha kullanışlı olmasına rağmen , git status --porcelainkirli dosyaları almak için de kullanabilirsiniz :

# 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)

Not: 2>/dev/nullGit olmayan dizinlerde bu komutları kullanabilmeniz için hata iletilerini filtreler. (Sadece 0dosya sayıları için geri dönerler .)

Düzenle :

İşte gönderiler:

Git İstemi Bilgilerini Terminal İsteminize Ekleme

Geliştirilmiş Git özellikli Kabuk İstemi


8
Git bash tamamlamanın, isteminizle ne yaptığınızı hemen hemen yapmak için bir kabuk işlevi ile geldiğini belirtmek gerekir __git_ps1. Rebase, am-Apply, merge veya bisect sürecindeyseniz, özel muamele de dahil olmak üzere şube adlarını gösterir. Ortam değişkenini GIT_PS1_SHOWDIRTYSTATE, değişmemiş değişiklikler ve aşamalı değişiklikler için yıldız işareti alacak şekilde ayarlayabilirsiniz . (Ayrıca izlenmemiş dosyaları belirtmek ve size git-describeçıktı vermek için alabilirsiniz )
Cascabel

8
Uyarı: git diff --shortstatdeğişiklikler dizinde zaten varsa yanlış negatif verir.
Marko Topolnik

4
git status --porcelaingit diff --shortstatyeni oluşturulan boş dosyaları yakalamayacağı için tercih edilir . Herhangi bir temiz çalışma ağacında deneyebilirsiniz:touch foo && git diff --shortstat
Campadrenalin

7
HAYIR - porselen, çıktının insanlar için olduğu ve kolayca kırıldığı anlamına gelir ! kararlı, komut dosyası dostu seçenekleri doğru kullanan @ChrisJohnsen'in cevabına bakın.
mike

7
Git durumu durumu sayfasından porselen seçeneği hakkında: "Çıktıya komut dosyaları için ayrıştırılması kolay bir biçimde verin. Bu kısa çıktıya benzer, ancak Git sürümlerinde ve kullanıcı yapılandırmasına bakılmaksızın sabit kalır. "
itsadok

399

Git'i güvenilir bir şekilde “betiklemenin” anahtarı 'sıhhi tesisat' komutlarını kullanmaktır.

Geliştiriciler, tesisat komutlarını değiştirirken çok kararlı arabirimler sağladıklarından emin olmak için dikkat ederler (örneğin, belirli bir depo durumu, stdin, komut satırı seçenekleri, argümanlar vb. Kombinasyonu, komutun / seçeneği var). Sıhhi tesisat komutlarındaki yeni çıktı varyasyonları yeni seçeneklerle tanıtılabilir, ancak bu eski sürümlere karşı yazılmış olan programlar için herhangi bir sorun yaratamaz (mevcut olmadıklarından (veya en azından kullanılmaz).

Ne yazık ki 'günlük' Git komutları 'porselen' komutlarıdır, bu nedenle Git kullanıcılarının çoğu sıhhi tesisat komutlarına aşina olmayabilir. Porselen ve sıhhi tesisat komutu arasındaki ayrım ana git man sayfasında yapılır ( Yüksek seviye komutları (porselen) ve Düşük seviye komutları (tesisat) başlıklı alt bölümlere bakın .


İstenmeyen değişiklikleri öğrenmek için, muhtemelen git diff-index(diğer ağaç ağaçlarıyla (örneğin HEAD) çalışma ağacının dizinini (ve belki de izlenen çalışma ağaç parçalarını ) git diff-fileskarşılaştırmanız ), belki (çalışma ağacını dizine göre karşılaştır) ve muhtemelen git ls-files(liste dosyalarını; örneğin izlenmeyenleri listele) , imzalanmamış dosyalar).

(Aşağıdaki komutlarda HEAD --bunun yerine kullanıldığından, HEADaksi halde adlandırılmış bir dosya varsa komutun başarısız olduğunu unutmayın HEAD.)

Bir havuzun (henüz işlenmemiş) değişiklikler sahnelediğini kontrol etmek için şunu kullanın:

git diff-index --quiet --cached HEAD --
  • Eğer çıkarsa 0, hiçbir fark yoktu ( 1farklılıklar olduğu anlamına gelir).

Çalışan bir ağacın aşamalandırılabilecek değişiklikler olup olmadığını kontrol etmek için:

git diff-files --quiet
  • Çıkış kodu git diff-index( 0== fark yok; 1== fark) ile aynıdır .

Çalışma ağacındaki dizin ve izlenen dosyaların kombinasyonunun aşağıdakilere göre değişiklik gösterip göstermediğini kontrol etmek için HEAD:

git diff-index --quiet HEAD --
  • Bu, önceki ikisinin bir kombinasyonu gibidir. Bir asıl fark, çalışma ağacında “geri aldığınız” aşamalı bir değişikliğiniz varsa (içinde bulunan içeriğe geri döndüğünüzde) “fark yok” rapor etmesidir HEAD. Aynı durumda, iki ayrı komutun her ikisi de “mevcut farklılıklar” raporlarını döndürür.

İzlenmeyen dosyalardan da bahsettiniz. "İzlenmemiş ve işaretsiz" anlamına gelebilir ya da yalnızca "izlenmemiş" (gözardı edilen dosyalar dahil) anlamına gelebilirsiniz. Her iki durumda da git ls-files, işin aracı:

"İzlenmemiş" için (varsa yok sayılan dosyaları içerir):

git ls-files --others

"İzlenmemiş ve işaretsiz" için:

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

İlk düşüncem, bu komutların çıktısı olup olmadığını kontrol etmektir:

test -z "$(git ls-files --others)"
  • 0İle çıkarsa izlenmemiş dosya yoktur. 1İle çıkarsa izlenmemiş dosyalar vardır.

Bunun anormal çıkışları git ls-files“izlenmeyen dosya yok” raporlarına dönüştürmesi için küçük bir şans vardır (her ikisi de yukarıdaki komutun sıfırdan farklı çıkışlarıyla sonuçlanır). Biraz daha sağlam bir sürüm şöyle görünebilir:

u="$(git ls-files --others)" && test -z "$u"
  • Fikir önceki komutla aynıdır, ancak beklenmeyen hataların git ls-filesyayılmasına izin verir . Bu durumda sıfır dışında bir çıkış “izlenmeyen dosyalar var” anlamına gelebilir veya bir hata oluştuğu anlamına gelebilir. Bunun yerine "hata" sonuçlarının "izlenmeyen dosya yok" sonucuyla birleştirilmesini istiyorsanız, test -n "$u"(burada çıkış 0"bazı izlenmemiş dosyalar" anlamına gelir ve sıfırdan farklı olması ise hata veya "izlenmeyen dosyalar" anlamına gelir).

Başka bir fikir, --error-unmatchizlenmemiş dosya olmadığında sıfırdan farklı bir çıkışa neden olmaktır. Bu aynı zamanda “izlenmeyen dosya yok” (çıkış 1) ile “hata oluştu” (sıfırdan çıkma, ancak büyük olasılıkla çıkış ) riskiyle de karşılaşır 128. Ama kontrol 0vs 1vs sıfır olmayan çıkış kodları muhtemelen oldukça sağlamdır:

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

Yalnızca izlenmeyen ve imzalanmamış dosyaları dikkate git ls-filesalmak --exclude-standardistiyorsanız yukarıdaki örneklerden herhangi biri geçerli olabilir .


5
Kabul edilen cevap git deposunun altında olan tüm izlenmemiş dosyaları verirken, yerel izlenmemiş dosyaları git ls-files --othersveren noktaya işaret etmek istiyorum . Bunlardan hangisinin orijinal posterden istediği değilim, ama ikisi arasındaki fark ilginç. git status --porcelain
Eric O Lebigot

1
@phunehehe: ile bir pathspec sağlamanız gerekir --error-unmatch. Deneyin (örneğin) git ls-files --other --error-unmatch --exclude-standard .(sondaki noktaya dikkat edin, cwd'ye başvurur; bunu çalışma ağacının üst düzey dizininden çalıştırın).
Chris Johnsen

8
@phs: stat (2) bilgilerinin uyuşmamasından kaynaklanan bazı “yanlış pozitif” durumlardan kaçınmak için git update-index -q --refreshönce yapmanız gerekebilir diff-index.
Chris Johnsen

1
Ben git update-indexihtiyaç tarafından ısırıldı ! Bir şey değişiklik yapmadan dosyalara dokunuyorsa bu önemlidir.
Nakedible

2
@RobertSiemer tarafından "Yerel" Ben senin altındaki dosyaları geliyordu geçerli dizinde olabilir, aşağıda ana git depo. --porcelainTüm izlenmeyen dosyaları bulunan çözüm listeleri tüm git deposunda (eksi git-gözardı dosyaları), onun alt dizinleri birinin içinde olsalar bile.
Eric O Lebigot

139

Git 1.7.0 veya üzeri bir sürümde olduğunuzu varsayarsak ...

Bu sayfadaki tüm cevapları ve bazı denemeleri okuduktan sonra, doğru doğruluk ve kısalık doğru kombinasyonunu vuran yöntemin olduğunu düşünüyorum:

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

Git, izlenen, yoksayılan, izlenmeyen ancak imzalanmamışlar arasında çok fazla nüansa izin verirken, tipik kullanım durumunun, kasanız temiz değilse her şeyi durdurmak istediğiniz yapı komut dosyalarını otomatikleştirmek için olduğuna inanıyorum.

Bu durumda, programcının ne yapacağını simüle etmek mantıklıdır: git statusçıktıyı yazın ve bakın. Ama ortaya çıkan belirli kelimelere güvenmek istemiyoruz, bu yüzden --porcelain1.7.0'da sunulan modu kullanıyoruz ; etkinleştirildiğinde, temiz bir dizin çıktı almaz.

Sonra test -nherhangi bir çıktı olup olmadığını görmek için kullanırız .

Bu komut, çalışma dizini temizse 1 ve yapılacak değişiklikler varsa 0 değerini döndürür. Eğer tersini istiyorsanız -nbir 'i değiştirebilirsiniz -z. Bu, bunu komut dosyasındaki bir komuta zincirlemek için kullanışlıdır. Örneğin:

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

Bu etkin bir şekilde "bir değişiklik yapılması veya alarmın başlatılması" anlamına gelir; bu tek satırlık, yazdığınız komut dosyasına bağlı olarak bir if ifadesine tercih edilebilir.


1
Benim için diğer tüm komutlar Linux ve pencereler arasında aynı temsilci üzerinde farklı sonuçlar veriyordu. Bu komut bana her ikisinde de aynı çıktıyı verdi.
Adarsha

6
Soruyu cevapladığınız ve sürekli açık bir şekilde cevap vermediğiniz, asla net bir cevap vermediğiniz için teşekkür ederiz.
NateS

El ile bir dağıtım komut dosyası için, dağıtımda test -n "$(git diff origin/$branch)"yerel taahhütlere izin verilmesini önlemek için bunu birleştirme
Erik Aronesty


8

Bu cevaplardan birkaçına bir göz attım ... (ve * nix ve pencereler üzerinde çeşitli sorunlar yaşadım, ki bu bir gereklilikti) ... aşağıdakileri iyi buldum ...

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

* Nix içindeki çıkış kodunu kontrol etmek için

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

$ Penceresindeki çıkış kodunu kontrol etmek için

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

Https://github.com/sindresorhus/pure/issues/115 kaynaklıdır Paylaşım için bu yayındaki @pairirish'e teşekkürler


4

Neden 'encapsulate değil git statushangi bir komut dosyası ile:

  • bu komutun çıktısını analiz eder
  • İhtiyacınıza göre uygun hata kodunu döndürür

Bu şekilde, betiğinizdeki bu 'gelişmiş' durumu kullanabilirsiniz.


As 0xFE onun içinde bahseder mükemmel cevap , git status --porcelainherhangi bir komut dosyası tabanlı bir çözüm vesile olan

--porcelain

Çıktıya komut dosyaları için sabit, ayrıştırılması kolay bir format verin.
Şu anda bu aynıdır --short output, ancak gelecekte değişmemesi garanti edilir ve komut dosyaları için güvenli hale gelir.


Çünkü tembelim muhtemelen. Oldukça sık karşılaşılan bir kullanım durumu gibi göründüğünden bunun için yerleşik bir şey olduğunu düşündüm.
Robert Munteanu

Ben son derece memnun olmasam da, öneri dayalı bir çözüm gönderdi.
Robert Munteanu

4

Bir DIY olasılığı, 0xfe'nin önerisini takip etmek için güncellendi

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

Chris Johnsen tarafından belirtildiği gibi , bu sadece Git 1.7.0 veya daha yeni sürümlerde çalışır.


6
Bununla ilgili sorun, gelecek sürümlerde 'çalışma dizini temiz' dizesini güvenilir bir şekilde bekleyemezsiniz. --Porcelain bayrağı ayrıştırma amaçlıydı, bu yüzden daha iyi bir çözüm şu olurdu: çıkış $ (git status --porcelain | wc -l)
0xfe

@ 0xfe - --porcelainBayrağın ne zaman eklendiğini biliyor musunuz ? 1.6.4.2 ile çalışmaz.
Robert Munteanu

@ Robert: dene git status --shorto zaman.
VonC

3
git status --porcelainve git status --shorther ikisi de 1.7.0'da tanıtıldı. Gelecekte biçimini --porcelaindeğiştirmeye izin vermek git status --shortiçin özel olarak tanıtıldı . Yani, git status --shortaynı problemle karşı karşıya kalacaktır git status(çıktı bir 'sıhhi tesisat' komutu olmadığı için herhangi bir zamanda değişebilir).
Chris Johnsen

@Chris, arka plan bilgileri için teşekkürler. Cevabı Git 1.7.0'dan itibaren bunu yapmanın en iyi yolunu yansıtacak şekilde güncelledim.
Robert Munteanu

2

Yürütme sonunda değiştirilmiş izlenen dosyalar veya göz ardı edilmeyen herhangi bir izlenmemiş dosya varsa, bir derleme başarısız oldukça basit bir yol gerekiyordu.

Bu, yapıların artıkları ürettiği durumdan kaçınmak için oldukça önemlidir.

Şimdiye kadar kullandığım en iyi komut şuna benziyor:

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

Biraz karmaşık görünebilir ve kimse bu istenen davranışı korur kısa devre bir varyant bulursa takdir:

  • her şey yolundaysa çıkış yok ve çıkış kodu yok
  • başarısız olursa çıkış kodu 1
  • stderr'da neden başarısız olduğunu açıklayan hata mesajı
  • hataya neden olan dosyaların listesini görüntüler, stderr tekrar.

2

@ eduard-wirch cevabı oldukça eksiksizdi, ama ikisini de aynı anda kontrol etmek istediğim gibi, işte son varyantım.

        set -eu

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

Set -e veya dengi kullanılarak yürütülmediğinde, bunun yerine bir u="$(git ls-files --others)" || exit 1(veya kullanılmış bir işlev için çalışıyorsa geri dönebilir) yapabiliriz

Yani untracked_files, yalnızca komut düzgün bir şekilde başarılı olursa ayarlanır.

bundan sonra, her iki özelliği de kontrol edebilir ve bir değişken (veya her ne olursa olsun) ayarlayabiliriz.


1

Bu olmadığını bulmaya yönelik daha kabuk dost varyasyon herhangi izlenmeyen dosyaları Depoda:

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

Bu ikinci bir işlemi grepkesmez ve git deposunda olup olmadığınızı kontrol etmenize gerek yoktur. Kabuk istemleri vb. İçin kullanışlıdır.


1

Ayrıca yapabilirsin

git describe --dirty

. Kirli bir çalışma ağacı algılarsa, sonuna "-deri" kelimesini ekler. Göre 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.

. Uyarı: izlenmeyen dosyalar "kirli" olarak kabul edilmez, çünkü manpage'in belirttiği gibi, yalnızca çalışma ağacını önemser.


0

Orada bu iplikten cevapların daha iyi bir kombinasyonu olabilir .. ama benim için bu eserler ... olabilir .gitconfig'ın [alias]bölümünde ...

          # 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

Kirli durumu tespit etmek için kullandığım en basit otomatik test = izlenmeyen dosyalar da dahil olmak üzere herhangi bir değişiklik :

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

NOT:

  • Olmadan add --all diff-indexizlenmeyen dosyaları fark etmez.
  • Normalde koşarım git reset her şeyi bozmak için hata kodunu test ettikten sonra .

Soru özellikle "bir senaryodan" ... sadece kirli durumu test etmek için dizini değiştirmek iyi bir fikir değil.
ScottJ

@ScottJ, sorunu çözdüğünde, herkes endeksin değiştirilip değiştirilemeyeceğine dair kesin değildir. Sürüm numarası ile otomatik yama kaynakları ile ilgili otomatik bir iş düşünün ve bir etiket yapın - emin olmanız gereken tek şey, başka hiçbir yerel değişikliğin (dizinde olup olmadıklarına bakılmaksızın) kesinlikle kalmamasıdır. Şimdiye kadar, bu izlenmemiş dosyalar da dahil olmak üzere herhangi bir değişiklik için güvenilir bir testti .
uvsmtid

-3

İşte en iyi, en temiz yol. Seçilen cevap bir nedenden dolayı benim için işe yaramadı, taahhüt edilmeyen yeni dosyalar olan aşamalı değişiklikleri almadı.

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