Tentukan apakah direktori kerja Git bersih dari skrip

84

Saya memiliki skrip yang berjalan rsyncdengan direktori kerja Git sebagai tujuan. Saya ingin skrip memiliki perilaku yang berbeda tergantung pada apakah direktori kerja bersih (tidak ada perubahan untuk dikomit), atau tidak. Misalnya, jika outputnya git statusseperti di bawah ini, saya ingin skrip keluar:

git status
Already up-to-date.
# On branch master
nothing to commit (working directory clean)
Everything up-to-date

Jika direktori tidak bersih maka saya ingin menjalankan beberapa perintah lagi.

Bagaimana saya bisa memeriksa output seperti di atas dalam skrip shell?

brentwpeterson
sumber
Apakah memeriksa status dari perintah terakhir akan membantu di sini? ($?)
UVV
Bisakah Anda memberikan detail lebih lanjut? Apa ide utama untuk skrip Anda?
tachomi
@tachomi Saya menambahkan konteks di edit
brentwpeterson
Anda bisa berasumsi itu tidak bersih dan lakukan git reset --hard origin/branchjika itu yang Anda inginkan ... seperti jika Anda mencoba untuk membersihkan setelah mengkompilasi sesuatu, dll.
SnakeDoc
1
@SnakeDoc Anda bisa, tapi saya berasumsi bahwa kasus terbalik akan lebih umum, yaitu keluar jika direktori kerja kotor untuk menghindari perubahan lokal yang membingungkan. Mempertimbangkan kedua kasus akan membuat pertanyaan lebih bermanfaat bagi pembaca masa depan.
Thomas Nyman

Jawaban:

135

Mengurai output git statusadalah ide yang buruk karena output dimaksudkan agar dapat dibaca oleh manusia, tidak dapat dibaca mesin. Tidak ada jaminan bahwa output akan tetap sama di versi Git yang akan datang atau di lingkungan yang dikonfigurasi secara berbeda.

Komentar UVV ada di jalur yang benar, tetapi sayangnya kode pengembalian git statustidak berubah ketika ada perubahan yang tidak dikomit. Namun, ia menyediakan --porcelainopsi, yang menyebabkan output git status --porcelaindiformat dalam format skrip yang mudah diurai, dan akan tetap stabil di seluruh versi Git dan terlepas dari konfigurasi pengguna.

Kita dapat menggunakan output kosong git status --porcelainsebagai indikator bahwa tidak ada perubahan yang dilakukan:

if [ -z "$(git status --porcelain)" ]; then 
  # Working directory clean
else 
  # Uncommitted changes
fi

Jika kami tidak peduli dengan file yang tidak dilacak dalam direktori kerja, kami dapat menggunakan --untracked-files=noopsi untuk mengabaikannya:

if [ -z "$(git status --untracked-files=no --porcelain)" ]; then 
  # Working directory clean excluding untracked files
else 
  # Uncommitted changes in tracked files
fi

Untuk menjadikan ini lebih kuat terhadap kondisi yang sebenarnya menyebabkan git statuskegagalan tanpa hasil stdout, kami dapat mempersempit pemeriksaan ke:

if output=$(git status --porcelain) && [ -z "$output" ]; then
  # Working directory clean
else 
  # Uncommitted changes
fi

Ini juga diperhatikan bahwa, meskipun git statustidak memberikan kode keluar berarti ketika direktori kerja najis, git diffmemberikan --exit-codepilihan, yang membuatnya berperilaku mirip dengan diff utilitas, yaitu, keluar dengan statusnya 1ketika ada perbedaan dan 0ketika tidak ditemukan.

Dengan ini, kita dapat memeriksa perubahan yang tidak dipentaskan dengan:

git diff --exit-code

dan dipentaskan, tetapi tidak melakukan perubahan dengan:

git diff --cached --exit-code

Meskipun git diffdapat melaporkan file yang tidak dilacak dalam submodul melalui argumen yang sesuai --ignore-submodules, sayangnya tampaknya tidak ada cara untuk melaporkannya pada file yang tidak dilacak dalam direktori kerja yang sebenarnya. Jika file yang tidak dilacak dalam direktori kerja relevan, git status --porcelainmungkin itu yang terbaik.

Thomas Nyman
sumber
4
ughhh git status --porcelainakan keluar dengan kode 0 bahkan jika ada perubahan tidak dilakukan untuk file komit dan tidak terlacak.
Alexander Mills
Saya tertarik untuk menentukan sebelumnya apakah git stashakan melakukan sesuatu (itu tidak menghasilkan kode pengembalian yang berguna). Saya harus menambahkan --ignore-submoduleskarena jika tidak git statusakan menunjukkan perubahan submodule yang git stashmengabaikan.
Devin Lane
1
@AlexanderMills: Saya mengamati hal yang sama. Tetapi kemudian memeriksa apa yang if [ -zsedang dilakukan. The -zberarti bahwa jika string berikut kosong, jika mengevaluasi ke true. Dengan kata lain, jika ini tidak git status --porcelainmenghasilkan string, repo bersih. Jika tidak, daftar file yang diubah / ditambahkan / dihapus dan tidak lagi menjadi string kosong. The ifkemudian mengevaluasi ke false.
Adeynack
19

Menggunakan:

git diff-index --quiet HEAD

Kode kembali mencerminkan keadaan direktori kerja (0 = bersih, 1 = kotor). File yang tidak dilacak diabaikan.

André van Herk
sumber
6
Mengembalikan 0 ketika ada file yang tidak dilacak dalam direktori saat ini.
Adam Parkin
2
Jika file telah disentuh / ditimpa tetapi sebaliknya identik dengan indeks, Anda harus terlebih dahulu dijalankan git update-index --refreshsebelumnya git diff-index HEAD. Info lebih lanjut: stackoverflow.com/q/34807971/1407170
sffc
@ AdamParkin Saya baru saja menambahkan semua file dengan git add .sebelum menerbitkannya. Biasanya ini cara menggunakannya dalam naskah
ceztko
Ini bagus. Perhatikan bahwa kode kembali / keluar yang bukan nol juga ditafsirkan sebagai 'kesalahan', yang jika Anda menggunakan skrip dengan set -e maka skrip Anda akan keluar jika 'kotor'. Ini dapat dihindari dengan melakukan set +esebelum panggilan ke git, dan menambahkan set -elagi setelah Anda mengevaluasi $?.
orion elenzil
1

Ekstensi kecil untuk jawaban André yang luar biasa .

Ini adalah salah satu cara untuk mengevaluasi hasil dan juga menghindari jebakan jika Anda berada dalam skrip yang sebelumnya diterbitkan set -e .

File yang tidak dilacak diabaikan.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
orion elenzil
sumber