ฉันจะตรวจสอบสคริปต์ Bash ว่าที่เก็บ Git ในพื้นที่ของฉันมีการเปลี่ยนแปลงได้อย่างไร


176

มีสคริปต์บางตัวที่ทำงานไม่ถูกต้องหากตรวจสอบการเปลี่ยนแปลง

ฉันลองแบบนี้:

VN=$(git describe --abbrev=7 HEAD 2>/dev/null)

git update-index -q --refresh
CHANGED=$(git diff-index --name-only HEAD --)
if [ ! -z $CHANGED ];
    then VN="$VN-mod"
fi

มีการตรวจสอบบูลีนบางประเภทหรือไม่ว่ามีการเปลี่ยนแปลงตั้งแต่การคอมมิชชันล่าสุดหรือไม่ฉันจะทดสอบได้อย่างไรว่ามีการเปลี่ยนแปลงใหม่กับที่เก็บในเครื่องของฉันหรือไม่?

ฉันทำทั้งหมดนี้เพื่อสร้างสคริปต์เวอร์ชัน (ที่ฉันพบที่นี่)


3
มีอะไรผิดปกติกับgit status?
karlphillip

4
@karlphillip: การประมวลผลจำนวนมากที่คุณไม่ต้องการจริงๆ
Cascabel

1
@karlphillip เป็นคำสั่ง "Porcelain" ซึ่งหมายความว่า: ไม่เหมาะสำหรับใช้ในสคริปต์เนื่องจากผลลัพธ์ถูกออกแบบมาเพื่อให้มนุษย์อ่านและอาจเปลี่ยนแปลงได้ (ระหว่างเวอร์ชันหรือเนื่องจากการแปล)
Andrew Spencer

คำตอบ:


201

สิ่งที่คุณทำเกือบจะใช้ได้: คุณควรอ้างอิง$CHANGEDในกรณีที่ว่างเปล่าและ-zการทดสอบว่างเปล่าซึ่งหมายถึงไม่มีการเปลี่ยนแปลง สิ่งที่คุณหมายถึงคือ:

if [ -n "$CHANGED" ]; then
    VN="$VN-mod"
fi

ข้อความจาก Git's GIT-VERSION-GEN:

git update-index -q --refresh
test -z "$(git diff-index --name-only HEAD --)" ||
VN="$VN-dirty"

ดูเหมือนว่าคุณกำลังคัดลอกสิ่งนั้น แต่คุณลืมรายละเอียดการอ้างถึง

แน่นอนคุณสามารถทำสิ่งนี้ได้เช่นกัน:

if git diff-index --quiet HEAD --; then
    # No changes
else
    # Changes
fi

หรือถ้าคุณสนใจเฉพาะกรณี "บางอย่างเปลี่ยนไป":

if ! git diff-index --quiet HEAD --; then
    VN="$VN-mod"
fi

การใช้--quietมีประโยชน์ที่ Git สามารถหยุดการประมวลผลทันทีที่พบความแตกต่างเดียวดังนั้นจึงไม่จำเป็นต้องตรวจสอบแผนผังการทำงานทั้งหมดของคุณ


2
สวัสดีนั่นเป็นหนึ่งในคำตอบที่ดีที่สุดสำหรับคำถามคุณเพียงแค่ให้ข้อมูลทั้งหมดที่ฉันต้องการกับคนอื่น ๆ ที่ฉันต้องการเช่นกัน :) ฉันแค่ต้องการอันสุดท้าย "บางสิ่งเปลี่ยนไป" :) และคุณพูดถูกฉันคัดลอกมัน
kmindi

9
สคริปต์การเติมเต็มของ bash ที่น่าทึ่งดูเหมือนจะใช้git diff --no-ext-diff --quiet --exit-codeเพื่อกำหนดสถานะสกปรก
mjs

3
@mjs: นั่นเป็นสถานที่ที่ยอดเยี่ยมสำหรับการค้นหาสิ่งนี้! --no-ext-diffตัวเลือกที่ดีสำหรับความปลอดภัย (ในกรณีคนที่มีการกำหนดค่าโปรแกรมควบคุม diff ภายนอก) แต่ไม่ควรจะเป็นสิ่งที่จำเป็นเพราะมันส่อให้เห็นถึง--exit-code --quiet
Cascabel

4
สิ่งนี้ไม่ทำงานสำหรับฉันเนื่องจากไม่ได้รายงานไฟล์ที่ไม่ได้ติดตาม
Cookie

1
git diff-indexรายงานการเปลี่ยนแปลงแม้ว่าเวลาการแก้ไขไฟล์จะเปลี่ยนแปลงเท่านั้น (และไม่ใช่เนื้อหา) หากคุณtouchไฟล์มันจะรายงานการดัดแปลงที่ทำงานอยู่git statusจะรีเซ็ต ใช้git statusเป็นด้านล่างจะดีกว่า
Sampo

303

การใช้git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi

15
คำตอบที่ดีที่สุดน่าละอายคำตอบจาก 'สถาปนิกเชลล์และคอมไพล์' ได้รับการโหวตมากขึ้น
jwg

4
นี่เป็นเรื่องที่ยอดเยี่ยมเพราะมันคำนึงถึงไฟล์ที่ไม่มีเวอร์ชันและการใช้เครื่องเคลือบดินเผามันควรจะเข้ากันได้กับ git เวอร์ชั่นต่าง ๆ มากกว่า
Jayd

25
หากต้องการละเว้นไฟล์ที่ไม่ได้ติดตาม: ถ้า [[ git status --porcelain --untracked-files=no]]; จากนั้น
storm_m2138

4
สำหรับการตรวจสอบการเปลี่ยนแปลงในท้องถิ่น -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Carlos Saltos

3
โค้ดนี้ทำอะไร:ดำเนินการgit status --porcelainดูว่าเอาต์พุตไม่ว่างเปล่า ถ้าเป็นเช่นนั้นหมายความว่ามีการเปลี่ยนแปลงในปัจจุบัน
bhathiya-perera

18

แม้ว่าคำตอบของ Jefromi นั้นดี แต่ฉันโพสต์สิ่งนี้ไว้เพื่ออ้างอิงเท่านั้น

จากซอร์สโค้ด Git มีshสคริปต์ซึ่งรวมถึงต่อไปนี้

require_clean_work_tree () {
    git rev-parse --verify HEAD >/dev/null || exit 1
    git update-index -q --ignore-submodules --refresh
    err=0

    if ! git diff-files --quiet --ignore-submodules
    then
        echo >&2 "Cannot $1: You have unstaged changes."
        err=1
    fi

    if ! git diff-index --cached --quiet --ignore-submodules HEAD --
    then
        if [ $err = 0 ]
        then
            echo >&2 "Cannot $1: Your index contains uncommitted changes."
        else
            echo >&2 "Additionally, your index contains uncommitted changes."
        fi
        err=1
    fi

    if [ $err = 1 ]
    then
        test -n "$2" && echo >&2 "$2"
        exit 1
    fi
}

สำหรับการอ้างอิงและถ้าฉันอ่านถูกต้อง$1สตริงการตั้งชื่องานที่คุณต้องการเรียกใช้และ$2เป็นสตริงที่มีข้อความแสดงข้อผิดพลาดที่กำหนดเองเมื่อล้มเหลว ตัวอย่างเช่นเรียกมันว่าrequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella

6

ฉันมีปัญหาที่คล้ายกัน แต่ฉันต้องตรวจสอบไฟล์เพิ่มเติมด้วย ดังนั้นฉันทำต่อไปนี้:

cd /local/repo
RUN=0
git diff --no-ext-diff --quiet --exit-code || RUN=1
if [ $RUN = 0 ]; then
    RUN=`git ls-files --exclude-standard --others| wc -l`
fi

if [ $RUN = 0 ]; then
    exit 0
fi

5

git status เป็นเพื่อนของคุณ

เปลี่ยนเป็นไดเรกทอรี Git เพื่อgit statusให้ทำงาน:

cd c:/path/to/.git

ตั้งค่าตัวแปรเพื่อตั้งค่าแผนผังงานเพื่อให้คุณไม่ได้รับข้อผิดพลาด 'การดำเนินการนี้ต้องทำงานในแผนผังต้นไม้':

WORKTREE=c:/path/to/worktree

จับgit statusเอาท์พุทในตัวแปร Bash

ใช้--porcelainสิ่งที่รับประกันว่าจะอยู่ในรูปแบบมาตรฐานและสามารถแยกวิเคราะห์ได้:

CHANGED=$(git --work-tree=${WORKTREE} status --porcelain)

ถ้า -n (ไม่ใช่ null) เรามีการเปลี่ยนแปลง

if [ -n "${CHANGED}" ]; then
  echo 'changed';

else
  echo 'not changed';
fi

1
สิ่งนี้มีประโยชน์ในการตรวจจับไฟล์ที่ไม่ได้ติดตามและ
Luiz C.

2

วิธีนี้ใช้ได้เช่นกัน:

if [ $(git status --porcelain | wc -l) -eq "0" ]; then
  echo "  🟢 Git repo is clean."
else
  echo "  🔴 Git repo dirty. Quit."
  exit 1
fi

1

มันใช้งานได้ดี นอกจากนี้ยังจะแสดงรายการไฟล์ที่ได้รับผลกระทบ:

if git diff-index --name-status --exit-code HEAD;
then
    echo Git working copy is clean...;
else
    echo ;
    echo ERROR: Git working copy is dirty!;
    echo Commit your changes and try again.;
fi;

1
หากคุณไม่ต้องการเห็นความแตกต่างgit diff --no-ext-diff --quiet --exit-codeก็ใช้งานได้เช่นกัน
อเล็กซ์

1

นี่เป็นชุดที่ดีของฟังก์ชั่นสคริปต์ Bash ที่ตรวจสอบว่ามีความแตกต่างพิมพ์ไปยังผู้ใช้และแจ้งให้ผู้ใช้หากพวกเขาต้องการที่จะยอมรับการเปลี่ยนแปลงก่อนการปรับใช้ มันถูกสร้างขึ้นสำหรับแอพพลิเคชั่น Heroku และ Python แต่มันต้องการการเปลี่ยนแปลงเล็กน้อยสำหรับแอพพลิเคชั่นอื่น ๆ

commit(){
    echo "Please enter a commit message..."
    read msg
    git add . --all
    git commit -am $msg
}

check_commit(){
    echo ========== CHECKING FOR CHANGES ========
    changes=$(git diff)
    if [ -n "$changes" ]; then
        echo ""
        echo "*** CHANGES FOUND ***"
        echo "$changes"
        echo ""
        echo "You have uncomitted changes."
        echo "Would you like to commit them (y/n)?"
        read n
        case $n in
            "y") commit;;
            "n") echo "Changes will not be included...";;
            *) echo "invalid option";;
        esac
    else
        echo "... No changes found"
    fi
}

deploy(){
    check_commit
    echo ========== DEPLOYING TO HEROKU ========
    git push heroku master
    heroku run python manage.py syncdb
}

คุณสามารถคัดลอกจาก Gists ได้ที่: https://gist.github.com/sshadmand/f33afe7c9071bb725105


1

คำถามของ OP มีอายุมากกว่า 9 ปีแล้ว ฉันไม่รู้ว่าจะman git-statusพูดอย่างไร แต่นี่คือสิ่งที่พูดตอนนี้:

--porcelain[=<version>]  
Give the output in an easy-to-parse format for scripts. This is similar to the 
short output, but will remain stable across Git versions and regardless of user 
configuration. See below for details.  

The version parameter is used to specify the format version. This is optional and 
defaults to the original version v1 format.  

สิ่งนี้ชี้ให้เห็นว่าการ--porcelainโต้แย้งนั้นเหมาะสมกับการทดสอบสถานะของธุรกรรมซื้อคืนสำหรับการเปลี่ยนแปลง

เขียนคำถามของ OP ว่า"มีบูลีนตรวจสอบบ้างไหมว่ามีการเปลี่ยนแปลงตั้งแต่การคอมมิทครั้งล่าสุดหรือจะทดสอบได้อย่างไรว่ามีการเปลี่ยนแปลงใหม่ในที่เก็บโลคอลของฉันหรือไม่"

ฉันไม่คิดว่าbashมีประเภทข้อมูลบูลีนต่อ seแต่อาจใกล้พอ:

[ -z "`git status --porcelain`" ] && echo "NULL-NO DIFFS" || echo "DIFFS EXIST"

นี้จะเป็นอีกครั้งหล่อเป็นif-then-elseรูปแบบสำหรับสคริปต์หรือดำเนินการตามที่เป็น FM CLI ในขณะที่ในคอมไพล์ repo โฟลเดอร์ มิฉะนั้นให้ใช้-Cตัวเลือกที่มีข้อมูลจำเพาะของเส้นทางไปยัง repo ที่น่าสนใจ:

git -C ~/path/to/MyGitRepo status --porcelain 

ภาคผนวก:

  1. บางคนแนะนำให้ใช้-u, --untracked-fileตัวเลือกในการหลีกเลี่ยงสถานะการรายงานในไฟล์ที่ต้องการละเว้น หมายเหตุสิ่งนี้มาพร้อมกับผลข้างเคียงที่โชคร้าย: ไฟล์ที่เพิ่งเพิ่มเข้ามาจะไม่ได้รับสถานะเช่นกัน ตัวเลือกมีประโยชน์ในบางสถานการณ์แต่ควรพิจารณาอย่างรอบคอบก่อนใช้

0

นี่คือวิธีที่ฉันทำ ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi

3
ฉันชอบใช้สถานะ git แต่จะดีกว่าถ้าใช้ - จำกัด ในสคริปต์และเปรียบเทียบผลลัพธ์กับสตริงว่างโดยไม่มีการเปลี่ยนแปลงเนื่องจากรับประกันว่าจะไม่เปลี่ยนแปลงในลักษณะที่เข้ากันไม่ได้ในทุกเวอร์ชัน
Paul Chernoch

0
nano checker_git.sh

วางสิ่งนี้

#!/bin/bash

echo "First arg: $1"

cd $1

bob="Already up-to-date."
echo $bob

echo $(git pull) > s.txt
cat s.txt
if [ "$(cat s.txt)" == "$bob" ]
then
echo "up"
else
echo "not up"

fi
rm -rf st.txt

วิ่ง sh checker_git.sh gitpath


0

ขึ้นอยู่กับความคิดเห็น @ storm_m2138 เพื่อ @ คำตอบของ RyanMoon ( ลิงค์ ) Powershellผมใช้ต่อไปนี้ใน

function hasChanges($dir="."){ $null -ne (iex "git -C $dir status --porcelain --untracked-files=no") }

gci -Directory | ?{ hasChanges $_ } | %{ Write-Host "$_ has changes" }
gci -Directory | ?{ hasChanges $_ } | %{ iex "git -C $_ add -u"; iex "git -C $_ commit -m"Somemthing" }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.