Bagaimana saya bisa mengecek skrip Bash jika repositori Git lokal saya mengalami perubahan?

176

Ada beberapa skrip yang tidak berfungsi dengan benar jika mereka memeriksa perubahan.

Saya mencobanya seperti ini:

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

Apakah ada semacam pemeriksaan boolean jika ada perubahan sejak komit terakhir, atau bagaimana saya bisa benar-benar menguji apakah ada perubahan baru pada repositori lokal saya?

Saya melakukan semua ini untuk skrip pembuatan versi (yang saya temukan di suatu tempat di sini).

kmindi
sumber
3
Ada apa dengan ini git status?
karlphillip
4
@karlphillip: Ini melakukan banyak pemrosesan yang sebenarnya tidak Anda butuhkan.
Cascabel
1
@karlphillip itu adalah perintah "porselen", artinya: tidak sesuai untuk digunakan dalam skrip karena output dirancang untuk dibaca oleh manusia dan dapat berubah (antara versi atau karena lokalisasi)
Andrew Spencer

Jawaban:

201

Apa yang Anda lakukan hampir akan berhasil: Anda harus mengutip $CHANGEDjika itu kosong, dan -ztes untuk kosong, yang berarti tidak ada perubahan. Yang Anda maksud adalah:

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

Kutipan dari Git GIT-VERSION-GEN:

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

Sepertinya Anda menyalinnya, tetapi Anda hanya lupa detail kutipan itu.

Tentu saja, Anda juga bisa melakukan ini:

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

Atau jika Anda hanya peduli dengan kasus "sesuatu telah berubah":

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

Menggunakan --quietmemiliki manfaat bahwa Git dapat berhenti memproses segera setelah bertemu dengan satu perbedaan, jadi mungkin tidak perlu memeriksa seluruh pohon kerja Anda.

Cascabel
sumber
2
Hai, itu adalah salah satu jawaban terbaik untuk sebuah pertanyaan, Anda hanya memberi saya semua informasi yang saya butuhkan dan yang lain juga saya butuhkan :). Saya hanya perlu yang terakhir, "sesuatu telah berubah" :) dan Anda benar, saya menyalinnya.
kmindi
9
Skrip penyelesaian bash yang menakjubkan tampaknya digunakan git diff --no-ext-diff --quiet --exit-codeuntuk menentukan kondisi kotor.
mjs
3
@ mjs: Itu memang tempat yang tepat untuk mencari hal-hal seperti ini! The --no-ext-diffpilihan adalah baik untuk keselamatan (dalam hal seseorang telah dikonfigurasi driver diff eksternal), meskipun --exit-codeseharusnya tidak perlu, karena itu tersirat oleh --quiet.
Cascabel
4
Ini tidak berfungsi untuk saya karena tidak melaporkan file yang tidak dilacak
Cookie
1
git diff-indexlaporan berubah bahkan jika hanya waktu modifikasi file yang berubah (dan bukan isinya). Jika Anda touchfile, itu akan melaporkan modifikasi bahwa menjalankan git statusakan mengatur ulang. Menggunakan git statusseperti di bawah ini lebih baik.
Sampo
303

Menggunakan git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi
ryanmoon
sumber
15
Jawaban terbaik, memalukan jawaban dari 'arsitek shell dan git' lebih banyak dipilih.
jwg
4
Ini bagus karena memperhitungkan file tidak berversi dan juga menggunakan porselen, itu harus lebih kompatibel dengan versi git yang berbeda.
Jayd
25
Untuk mengabaikan file yang tidak dilacak: if [[ git status --porcelain --untracked-files=no]]; kemudian
storm_m2138
4
Untuk memeriksa perubahan lokal -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Carlos Saltos
3
Apa yang dilakukan kode ini: Jalankan git status --porcelainmelihat apakah output tidak kosong. Jika demikian itu berarti ada perubahan yang ada.
bhathiya-perera
18

Meskipun jawaban Jefromi bagus, saya memposting ini hanya untuk referensi.

Dari kode sumber Git ada shskrip yang mencakup yang berikut ini.

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
}
Arrowmaster
sumber
Sebagai referensi, dan jika saya membaca ini dengan benar, $1adalah string yang menamai tugas yang ingin Anda jalankan dan $2adalah string yang secara opsional berisi pesan kesalahan khusus pada kegagalan. Misalnya, sebut sepertirequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella
6

Saya memiliki masalah yang sama, tetapi saya harus memeriksa file yang ditambahkan juga. Jadi saya melakukan yang berikut:

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
Leon Waldman
sumber
5

git status adalah temanmu

Ubah ke direktori Git git statusagar berfungsi:

cd c:/path/to/.git

Tetapkan variabel untuk mengatur pohon kerja sehingga Anda tidak mendapatkan kesalahan 'Operasi ini harus dijalankan dalam pohon kerja':

WORKTREE=c:/path/to/worktree

Abadikan git statusoutput dalam variabel Bash

Gunakan --porcelainjaminan yang dalam format standar dan dapat diuraikan:

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

Jika -n (bukan null), kami memiliki perubahan.

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

else
  echo 'not changed';
fi
AndrewD
sumber
1
Ini memiliki manfaat mendeteksi file yang tidak terlacak juga.
Luiz C.
2

Ini juga berfungsi:

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

Ini bekerja dengan baik. Ini juga akan mencantumkan file yang terpengaruh:

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;
robertberrington
sumber
1
jika Anda tidak ingin melihat diff git diff --no-ext-diff --quiet --exit-codejuga berfungsi.
Alex
1

Berikut adalah serangkaian fungsi skrip Bash yang bagus yang memeriksa apakah ada perbedaan, mencetaknya kepada pengguna dan meminta pengguna jika mereka ingin melakukan perubahan sebelum penyebaran. Itu dibangun untuk aplikasi Heroku dan Python, tetapi perlu sedikit perubahan untuk aplikasi lain.

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
}

Anda dapat menyalin dari Gists di: https://gist.github.com/sshadmand/f33afe7c9071bb725105

Sean
sumber
1

Pertanyaan OP sudah lebih dari 9 tahun sekarang. Saya tidak tahu apa yang man git-statusdikatakan saat itu, tetapi inilah yang dikatakan sekarang:

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

Ini menunjukkan bahwa --porcelainargumen tersebut cocok untuk menguji status repo untuk perubahan.

Ketahui pertanyaan OP, "Apakah ada semacam pemeriksaan boolean jika ada perubahan sejak komit terakhir, atau bagaimana saya bisa benar-benar menguji apakah ada perubahan baru pada repositori lokal saya?"

Saya tidak berpikir itu bashmemiliki tipe data boolean per se , tetapi ini mungkin cukup dekat:

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

Ini dapat dicetak ulang sebagai if-then-elseformulir untuk skrip, atau dieksekusi apa adanya CLI saat berada di folder git repo . Jika tidak, gunakan -Copsi dengan spec path ke repo yang menarik:

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

Tambahan:

  1. Beberapa menyarankan menggunakan -u, --untracked-fileopsi untuk menghindari melaporkan status pada file yang ingin diabaikan. Catatan ini disertai dengan efek samping yang tidak menguntungkan : file yang baru ditambahkan juga tidak berstatus. Opsi ini berguna dalam beberapa situasi , tetapi pertimbangkan baik-baik sebelum menggunakannya.
Seamus
sumber
0

Begini cara saya melakukannya ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi
Jader Feijo
sumber
3
Saya suka menggunakan status git, tetapi lebih baik menggunakan --porcelain dalam skrip dan membandingkan hasilnya dengan string kosong tanpa perubahan, karena dijamin tidak akan berubah dengan cara yang tidak kompatibel lintas versi.
Paul Chernoch
0
nano checker_git.sh

rekatkan ini

#!/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

Lari sh checker_git.sh gitpath

Robert A
sumber
0

Berdasarkan pada @ storm_m2138 komentar untuk jawaban @ RyanMoon ( tautan ) Saya menggunakan yang berikut ini di 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" }
Dennis
sumber