ตรวจสอบว่าไดเรกทอรีทำงาน Git สะอาดจากสคริปต์


82

ฉันมีสคริปต์ที่ทำงานrsyncกับไดเรกทอรีการทำงานของ Git เป็นปลายทาง ฉันต้องการให้สคริปต์มีพฤติกรรมที่แตกต่างกันไปขึ้นอยู่กับว่าไดเรกทอรีทำงานนั้นสะอาด (ไม่มีการเปลี่ยนแปลงที่จะกระทำ) หรือไม่ ตัวอย่างเช่นหากผลลัพธ์ของgit statusด้านล่างเป็นฉันต้องการสคริปต์เพื่อออก:

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

หากไดเรกทอรีไม่สะอาดฉันก็อยากให้มันรันคำสั่งเพิ่มเติม

ฉันจะตรวจสอบเอาต์พุตเช่นด้านบนในเชลล์สคริปต์ได้อย่างไร


การตรวจสอบสถานะจากคำสั่งล่าสุดช่วยที่นี่หรือไม่ ($?)
UVV

กรุณาให้รายละเอียดเพิ่มเติมได้ไหม แนวคิดหลักของสคริปต์ของคุณคืออะไร?
tachomi

@tachomi ฉันได้เพิ่มบริบทในการแก้ไข
brentwpeterson

คุณก็อาจคิดว่ามันไม่ได้ทำความสะอาดและทำgit reset --hard origin/branchถ้าเป็นสิ่งที่คุณจะไป ... เหมือนถ้าคุณกำลังพยายามที่จะทำความสะอาดหลังจากรวบรวมบางสิ่ง, ฯลฯ
SnakeDoc

1
@SnakeDoc คุณทำได้ แต่ฉันคิดว่ากรณีผกผันจะเป็นเรื่องธรรมดามากขึ้นเช่นออกถ้าไดเรกทอรีการทำงานสกปรกเพื่อหลีกเลี่ยงการเปลี่ยนแปลงในเครื่อง การพิจารณาทั้งสองกรณีจะทำให้คำถามมีประโยชน์มากขึ้นสำหรับผู้อ่านในอนาคต
Thomas Nyman

คำตอบ:


133

การแยกเอาท์พุทของgit statusเป็นความคิดที่ไม่ดีเพราะเอาท์พุทมีวัตถุประสงค์เพื่อให้มนุษย์อ่านได้ไม่ใช่เครื่องอ่านได้ ไม่มีการรับประกันว่าผลลัพธ์จะยังคงเหมือนเดิมใน Git รุ่นอนาคตหรือในสภาพแวดล้อมที่กำหนดค่าแตกต่างกัน

ความคิดเห็นของ UVVอยู่ในเส้นทางที่ถูกต้อง แต่น่าเสียดายที่โค้ดส่งคืนของgit statusไม่เปลี่ยนแปลงเมื่อมีการเปลี่ยนแปลงที่ไม่ได้รับการยอมรับ อย่างไรก็ตามมี--porcelainตัวเลือกซึ่งทำให้การgit status --porcelainจัดรูปแบบผลลัพธ์ในรูปแบบที่ง่ายต่อการแยกวิเคราะห์สำหรับสคริปต์และจะยังคงมีเสถียรภาพในเวอร์ชัน Git และไม่คำนึงถึงการกำหนดค่าของผู้ใช้

เราสามารถใช้เอาท์พุทเปล่าgit status --porcelainเป็นตัวบ่งชี้ว่าไม่มีการเปลี่ยนแปลงที่จะเกิดขึ้น:

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

หากเราไม่สนใจเกี่ยวกับไฟล์ที่ไม่ได้ติดตามในไดเรกทอรีทำงานเราสามารถใช้--untracked-files=noตัวเลือกเพื่อเพิกเฉยต่อไฟล์เหล่านั้น:

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

ในการทำให้สิ่งนี้มีความทนทานต่อสภาวะที่เป็นจริงซึ่งทำให้เกิดความgit statusล้มเหลวโดยไม่ต้องออกไปstdoutเราสามารถปรับแต่งการตรวจสอบไปที่:

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

นอกจากนี้ยังเป็นที่น่าสังเกตว่าถึงแม้ว่าgit statusจะไม่ให้รหัสออกที่มีความหมายเมื่อไดเรกทอรีทำงานไม่สะอาดก็git diffมี--exit-codeตัวเลือกซึ่งทำให้มันทำงานคล้ายกับยูทิลิตีdiffซึ่งก็คือการออกจากสถานะ1เมื่อมีความแตกต่างและ0เมื่อไม่พบ

เมื่อใช้สิ่งนี้เราสามารถตรวจสอบการเปลี่ยนแปลงที่ไม่มีการจัดการด้วย:

git diff --exit-code

และเป็นฉาก แต่ไม่ได้ทำการเปลี่ยนแปลงด้วย:

git diff --cached --exit-code

แม้ว่าgit diffสามารถรายงานไฟล์ที่ไม่ได้ติดตามใน submodules ผ่านการขัดแย้งที่เหมาะสม--ignore-submodulesแต่น่าเสียดายที่ไม่มีรายงานว่าไฟล์ที่ไม่ได้ติดตามในไดเรกทอรีการทำงานจริง หากไฟล์ที่ไม่ได้ติดตามในไดเรกทอรีการทำงานมีความเกี่ยวข้องgit status --porcelainน่าจะเป็นทางออกที่ดีที่สุด


4
ughhh git status --porcelainจะออกด้วยรหัส 0 แม้ว่าจะมีการเปลี่ยนแปลงที่ไม่ได้จัดเตรียมไว้สำหรับไฟล์คอมมิทและไฟล์ที่ไม่ได้ติดตาม
Alexander Mills

ฉันสนใจที่จะกำหนดล่วงหน้าว่าgit stashจะทำอะไร (ไม่ส่งคืนรหัสส่งคืนที่มีประโยชน์) ฉันต้องเพิ่ม--ignore-submodulesเป็นอย่างอื่นgit statusจะบ่งบอกถึงการเปลี่ยนแปลง submodule ซึ่งgit stashไม่สนใจ
Devin Lane

1
@AlexanderMills: ฉันสังเกตเห็นเหมือนกัน แต่จากนั้นตรวจสอบสิ่งที่if [ -zกำลังทำ หมายความว่าถ้าสตริงต่อไปนี้เป็นที่ว่างเปล่าหากการประเมิน-z trueกล่าวอีกนัยหนึ่งถ้าgit status --porcelainผลลัพธ์นี้ไม่มีสตริง repo จะสะอาด มิฉะนั้นจะแสดงรายการไฟล์ที่ถูกแก้ไข / เพิ่ม / ลบออกและจะไม่เป็นสตริงว่างอีกต่อไป แล้วประเมินif false
Adeynack

19

ใช้:

git diff-index --quiet HEAD

โค้ดส่งคืนสะท้อนถึงสถานะของไดเร็กทอรีการทำงาน (0 = clean, 1 = dirty) ไฟล์ที่ไม่ได้ติดตามจะถูกละเว้น


6
ส่งคืน 0 เมื่อมีไฟล์ที่ไม่ได้ติดตามในไดเรกทอรีปัจจุบัน
Adam Parkin

2
ถ้าแฟ้มได้รับการสัมผัส / เขียนทับ แต่เป็นอย่างอื่นเหมือนกันกับดัชนีคุณต้องทำงานครั้งแรกก่อนgit update-index --refresh git diff-index HEADข้อมูลเพิ่มเติม: stackoverflow.com/q/34807971/1407170
sffc

@ AdamParkin ฉันเพิ่งเพิ่มไฟล์ทั้งหมดด้วยgit add .ก่อนที่จะออก โดยปกติแล้วจะเป็นวิธีการใช้งานในสคริปต์
ceztko

มันเยี่ยมมาก โปรดทราบว่าโค้ดส่งคืน / ออกที่ไม่ใช่ศูนย์จะถูกตีความว่าเป็น 'ข้อผิดพลาด' ซึ่งหากคุณอยู่ในสคริปต์ที่มีset -eสคริปต์ของคุณจะออกหาก 'สกปรก' นี้สามารถหลีกเลี่ยงได้โดยการทำset +eก่อนที่จะเรียกร้องให้gitและการเพิ่มอีกครั้งหลังจากที่คุณได้รับการประเมินset -e $?
orion elenzil

1

ขยายไมเนอร์อังเดรดีเยี่ยมคำตอบ

นี้เป็นวิธีหนึ่งในการประเมินผลและยังหลีกเลี่ยงอันตรายถ้าคุณอยู่ในสคริปต์ซึ่งก่อนหน้านี้ได้ออกตั้ง -e

ไฟล์ที่ไม่ได้ติดตามจะถูกละเว้น

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.