ฉันจะทราบได้อย่างไรว่าโปรแกรมมีการเปลี่ยนแปลงที่ปราศจากข้อผูกมัดทางโปรแกรมหรือไม่


226

ใน Makefile ฉันต้องการดำเนินการบางอย่างหากมีการเปลี่ยนแปลงที่ไม่มีข้อผูกมัด (ในแผนผังการทำงานหรือดัชนี) อะไรคือวิธีที่สะอาดและมีประสิทธิภาพที่สุดในการทำเช่นนั้น? คำสั่งที่ออกโดยมีค่าส่งคืนเป็นศูนย์ในหนึ่งกรณีและไม่เป็นศูนย์ในอื่น ๆ จะเหมาะกับวัตถุประสงค์ของฉัน

ฉันสามารถรันgit statusและไพพ์เอาต์พุตผ่านgrepได้ แต่ฉันรู้สึกว่าต้องมีวิธีที่ดีกว่า


คำตอบ:


289

ปรับปรุง : OP Daniel Daniel Stutzbachชี้ให้เห็นในความคิดเห็นที่คำสั่งง่ายๆนี้git diff-indexทำงานสำหรับเขา:

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

( nornagonกล่าวถึงในความคิดเห็นว่าหากมีไฟล์ที่ถูกแตะต้อง แต่มีเนื้อหาเหมือนในดัชนีคุณจะต้องเรียกใช้git update-index --refreshก่อนgit diff-indexมิฉะนั้นdiff-indexจะรายงานว่าต้นไม้สกปรกอย่างไม่ถูกต้อง)

จากนั้นคุณจะเห็น " วิธีตรวจสอบว่าคำสั่งประสบความสำเร็จหรือไม่ " ถ้าคุณใช้มันในสคริปต์ทุบตี:

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

หมายเหตุ: ตามความเห็นของAnthony Sottile

git diff-index HEAD ...จะล้มเหลวในสาขาที่ไม่มีข้อผูกมัด (เช่นที่เก็บข้อมูลที่เริ่มต้นใหม่)
วิธีแก้ปัญหาหนึ่งที่ฉันพบคือgit diff-index $(git write-tree) ...

และharidsvจุดที่ออกในความคิดเห็นที่git diff-filesบนใหม่ไฟล์ไม่ได้ตรวจสอบว่ามันเป็น 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จริงๆแล้วเปลี่ยนเนื้อหาของ sandbox ซึ่งหมายถึงที่นี่.git/index)

ปัญหาการประทับเวลาเหล่านี้ยังสามารถเกิดขึ้นได้ถ้าคอมไพล์จะทำงานในนักเทียบท่า


คำตอบเดิม:

"โปรแกรม" หมายถึงไม่เคยพึ่งพาคำสั่งพอร์ซเลน
พึ่งพาคำสั่งการวางท่อเสมอ

ดูเพิ่มเติม "การตรวจสอบดัชนีสกปรกหรือไฟล์ที่ไม่ได้ติดตามด้วย Git " เพื่อหาทางเลือกอื่น (เช่นgit status --porcelain)

คุณสามารถรับแรงบันดาลใจจาก " require_clean_work_treeฟังก์ชั่น " ใหม่ซึ่งเขียนขึ้นเมื่อเราพูด ;) (ต้นเดือนตุลาคม 2010)

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: 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รัฐ: - พอร์ซเลน ให้ผลลัพธ์ในรูปแบบที่ง่ายต่อการแยกวิเคราะห์สำหรับสคริปต์ สิ่งนี้คล้ายกับเอาต์พุตสั้น ๆ แต่จะยังคงเสถียรในเวอร์ชัน Git และไม่ว่าผู้ใช้จะกำหนดค่าแบบใด ดูรายละเอียดด้านล่าง
Ed Randall

7
@VonC ที่ไม่มีเหตุผลจริงๆ วิธีนี้คุณสามารถบิดทุกอย่างกลับด้านได้ - พอร์ซเลนให้ความประทับใจแก่คุณว่าจะพังเร็ว ๆ นี้ หากไม่เป็นเช่นนั้นควรเรียกว่าท่อประปาไม่ใช่เครื่องเคลือบ การใช้ --porcelain ทำให้สคริปต์ของคุณไม่หยุดซึ่งทำให้สคริปต์ไม่เป็นลายคราม ;-) หากคุณต้องการให้สคริปต์ของคุณแตกคุณไม่ควรใช้ --porcelain !! ดังนั้นจึงไม่สามารถเข้าใจได้อย่างสมบูรณ์และทำให้ทุกคนหลุดพ้น
Xennex81

104

ในขณะที่วิธีแก้ปัญหาอื่นนั้นละเอียดถี่ถ้วนหากคุณต้องการบางสิ่งที่รวดเร็วและสกปรกจริงๆลองทำสิ่งนี้:

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

มันแค่ตรวจสอบว่ามีผลลัพธ์ใด ๆ ในการสรุปสถานะ


7
ทำงานได้สำหรับฉัน ใช้ -n สำหรับผกผัน (คุณมีการเปลี่ยนแปลง) เช่น `ถ้า [[-n $ (สถานะ git -s)]]; ถ้าอย่างนั้น ... fi`
aaron

วิธีนี้ใช้ได้ผล แต่คุณสามารถบอกได้ไหมว่า[[ ... ]]ไวยากรณ์กำลังทำอะไรอยู่ ฉันไม่เคยเห็นอะไรแบบนั้นมาก่อน
GMA

2
@EM รหัสส่งคืนของgit statusถูกละเว้นจริงในการทดสอบนี้ มันแค่ดูที่เอาต์พุต ตรวจสอบทุบตีหน้านี้ที่เกี่ยวข้องสำหรับข้อมูลเพิ่มเติมเกี่ยวกับ[, [[และวิธีการทดสอบการทำงานในทุบตี
Nepthar

2
นี่เป็นคำตอบที่ถูกต้อง แต่สำหรับสคริปต์การใช้--porcelainพารามิเตอร์ตามที่แสดงไว้ที่นี่
Mariusz Pawelski

2
คุณอาจต้องการใช้git status -s -uallเพื่อรวมไฟล์ที่ไม่ได้ติดตาม
barfuin

59

git diff --exit-codeจะส่งกลับค่าที่ไม่ใช่ศูนย์ถ้ามีการเปลี่ยนแปลงใด ๆ git diff --quietเหมือนกันโดยไม่มีเอาต์พุต เนื่องจากคุณต้องการตรวจสอบแผนผังการทำงานและดัชนีให้ใช้

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

หรือ

git diff --quiet HEAD

คนใดคนหนึ่งจะบอกคุณว่ามีการเปลี่ยนแปลงที่ไม่มีข้อผูกมัดที่จัดฉากหรือไม่


6
สิ่งเหล่านั้นไม่เท่ากัน คำสั่งgit diff --quite HEADเดียวเท่านั้นที่จะบอกคุณว่าต้นไม้การทำงานสะอาดหรือไม่ไม่ว่าดัชนีสะอาด ตัวอย่างเช่นหากfileมีการเปลี่ยนแปลงระหว่าง HEAD ~ และ HEAD หลังจากgit reset HEAD~ -- fileนั้นจะยังคงออกจาก 0 แม้ว่าจะมีการเปลี่ยนแปลงแบบ staged อยู่ในดัชนี (wt == HEAD แต่ index! = HEAD)
Chris Johnsen

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")แล้วในelseข้อgit add "$file"; git commit -m "your autocommit process" "$file"
toddkaufmann

ถ้าคุณทำที่แทนไดเรกทอรีขยะจะโผล่ขึ้นมาที่นี่ด้วยซึ่งจะมองไม่เห็น git status -sgit status --porcelain ; git clean -ndgit 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

ด้วย python และแพ็คเกจ 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เป็นคำสั่ง 'ลายคราม' อย่าใช้คำสั่งเครื่องเคลือบในสคริปต์เนื่องจากสามารถเปลี่ยนระหว่างเวอร์ชัน git ได้ ใช้คำสั่ง 'plumbing' แทน
spuder

3
ฉันคิดว่าถ้าคุณอัปเดตให้ใช้git status --porcelain(ซึ่งมีไว้สำหรับวัตถุประสงค์นี้ - รูปแบบที่เสถียรคุณสามารถแยกวิเคราะห์ในสคริปต์) อาจเป็นด้วย -z (คั่นด้วย null แทน newline?) คุณสามารถทำสิ่งที่มีประโยชน์กับความคิดนี้ . @ codyc4321 ดูstackoverflow.com/questions/6976473/…สำหรับรายละเอียด
msouth
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.