web-dev-qa-db-de.com

Überprüfen Sie, ob in Git Pull benötigt wird

Wie überprüfe ich, ob sich das Remote-Repository geändert hat und ich ziehen muss?

Jetzt benutze ich dieses einfache Skript:

git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1

Aber es ist ziemlich schwer.

Gibt es einen besseren Weg? Die ideale Lösung würde alle entfernten Zweige prüfen und die Namen der geänderten Zweige und die Anzahl der neuen Festschreibungen in jedem einzelnen zurückgeben.

583
takeshin

Verwenden Sie zuerst git remote update , um Ihre Fernbedienungsreferenzen auf den neuesten Stand zu bringen. Dann können Sie eine der folgenden Aktionen ausführen:

  1. _git status -uno_ zeigt an, ob der Zweig, den Sie verfolgen, vor, hinter oder auseinander gegangen ist. Wenn es nichts sagt, sind das lokale und das entfernte gleich.

  2. _git show-branch *master_ zeigt Ihnen die Commits in allen Zweigen an, deren Namen auf 'master' enden (zB master und Origin/master ).

Wenn Sie _-v_ mit _git remote update_ (_git remote -v update_) verwenden, können Sie sehen, welche Zweige aktualisiert wurden, sodass Sie eigentlich keine weiteren Befehle benötigen.

Es sieht jedoch so aus, als ob Sie dies in einem Skript oder Programm tun und am Ende einen True/False-Wert erhalten möchten. Wenn ja, gibt es Möglichkeiten, die Beziehung zwischen Ihrem aktuellen HEAD Commit und dem Kopf des Zweigs, den Sie verfolgen, zu überprüfen. Da es jedoch vier mögliche Ergebnisse gibt, können Sie t Reduzieren Sie es auf eine Ja/Nein-Antwort. Wenn Sie jedoch zu einem _pull --rebase_ bereit sind, können Sie "local is behind" und "local has diverged" als "need to pull" und die anderen beiden als "don't need to pull" behandeln. .

Sie können die Festschreibungs-ID eines beliebigen Verweises mit _git rev-parse <ref>_ abrufen. Dies ist also für master und Origin/master möglich. ) und vergleichen Sie sie. Wenn sie gleich sind, sind die Zweige gleich. Wenn sie ungleich sind, möchten Sie wissen, was dem anderen voraus ist. Wenn Sie _git merge-base master Origin/master_ verwenden, werden Sie über den gemeinsamen Vorfahren beider Zweige informiert. Wenn dieser nicht auseinander gegangen ist, stimmt dieser mit dem einen oder anderen Zweig überein. Wenn Sie drei verschiedene IDs erhalten, sind die Zweige auseinander gegangen.

Um dies richtig zu tun, z. B. in einem Skript, müssen Sie in der Lage sein, auf den aktuellen Zweig und den Remote-Zweig zu verweisen, den es verfolgt. Die Bash-Eingabeaufforderungsfunktion in _/etc/bash_completion.d_ enthält nützlichen Code zum Abrufen von Zweignamen. Allerdings müssen Sie die Namen wahrscheinlich nicht wirklich abrufen. Git hat einige nette Abkürzungen, um auf Zweige und Commits zu verweisen (wie in _git rev-parse --help_ dokumentiert). Insbesondere können Sie _@_ für den aktuellen Zweig (vorausgesetzt, Sie befinden sich nicht in einem Status mit getrenntem Kopf) und _@{u}_ für den vorgelagerten Zweig (z. B. _Origin/master_) verwenden. Also gibt _git merge-base @ @{u}_ den (Hash des) Commits zurück, bei dem der aktuelle Zweig und sein Upstream auseinander gehen, und _git rev-parse @_ und _git rev-parse @{u}_ geben Ihnen die Hashes der beiden Tipps. Dies kann in folgendem Skript zusammengefasst werden:

_#!/bin/sh

UPSTREAM=${1:-'@{u}'}
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "$UPSTREAM")
BASE=$(git merge-base @ "$UPSTREAM")

if [ $LOCAL = $REMOTE ]; then
    echo "Up-to-date"
Elif [ $LOCAL = $BASE ]; then
    echo "Need to pull"
Elif [ $REMOTE = $BASE ]; then
    echo "Need to Push"
else
    echo "Diverged"
fi
_

Hinweis: ältere Versionen von git haben _@_ nicht für sich allein zugelassen, daher müssen Sie möglicherweise _@{0}_ verwenden.

Mit der Zeile _UPSTREAM=${1:-'@{u}'}_ können Sie optional einen Upstream-Zweig explizit übergeben, falls Sie einen anderen Remote-Zweig als den für den aktuellen Zweig konfigurierten verwenden möchten. Dies hat normalerweise die Form remotename/branchname . Wenn kein Parameter angegeben wird, lautet der Standardwert _@{u}_.

Das Skript setzt voraus, dass Sie zuerst einen _git fetch_ oder _git remote update_ durchgeführt haben, um die Verfolgungszweige auf den neuesten Stand zu bringen. Ich habe dies nicht in das Skript eingebaut, da es flexibler ist, das Abrufen und Vergleichen als separate Operationen durchzuführen, zum Beispiel, wenn Sie ohne Abrufen vergleichen möchten, weil Sie es bereits kürzlich abgerufen haben.

795
Neil Mayhew

Wenn Sie eine vorgelagerte Niederlassung haben

git fetch <remote>
git status

Wenn Sie keine vorgelagerte Niederlassung haben

Vergleichen Sie die beiden Zweige:

git fetch <remote>
git log <local_branch_name>..<remote_branch_name> --oneline

Zum Beispiel:

git fetch Origin

# See if there are any incoming changes
git log HEAD..Origin/master --oneline

(Ich gehe davon aus, dass Origin/master Ihr Remote-Tracking-Zweig ist.)

Wenn in der obigen Ausgabe Commits aufgeführt sind, liegen eingehende Änderungen vor - Sie müssen sie zusammenführen. Wenn bei git log keine Commits aufgelistet sind, gibt es nichts zu verschmelzen.

Beachten Sie, dass dies auch dann funktioniert, wenn Sie sich in einem Feature-Zweig befinden - der über keine Verfolgungsfernbedienung verfügt, da explizit auf Origin/master verwiesen wird, anstatt implizit den von Git gespeicherten pstream-Zweig zu verwenden .

125
PlagueHammer

Wenn dies für ein Skript ist, können Sie Folgendes verwenden:

git fetch
$(git rev-parse HEAD) == $(git rev-parse @{u})

(Hinweis: Der Vorteil gegenüber früheren Antworten besteht darin, dass Sie keinen separaten Befehl benötigen, um den aktuellen Zweignamen abzurufen. "HEAD" und "@ {u}" (der aktuelle Zweig ist stromaufwärts) kümmern sich darum. Siehe "git rev-parse --help" für weitere Details.)

57

Der Befehl

git ls-remote Origin -h refs/heads/master

listet den aktuellen Kopf auf der Fernbedienung auf - Sie können ihn mit einem vorherigen Wert vergleichen oder sehen, ob Sie den SHA in Ihrem lokalen Repo haben.

36
brool

Hier ist ein Bash-Einzeiler, der den HEADCommit-Hash des aktuellen Zweigs mit dem entfernten Upstream-Zweig vergleicht, ohne dass schwere git fetch- oder git pull --dry-run -Operationen erforderlich sind:

[ $(git rev-parse HEAD) = $(git ls-remote $(git rev-parse --abbrev-ref @{u} | \
sed 's/\// /g') | cut -f1) ] && echo up to date || echo not up to date

So wird diese etwas dichte Linie aufgeteilt:

  • Befehle werden mit der Syntax $(x) Bash Befehlsersetzung gruppiert und verschachtelt.
  • git rev-parse --abbrev-ref @{u} gibt einen abgekürzten Upstream-Verweis (z. B. Origin/master) zurück, der dann durch den weitergeleiteten Befehl sed, z. Origin master.
  • Diese Zeichenfolge wird in git ls-remote eingespeist, das das Head-Commit des Remote-Zweigs zurückgibt. Dieser Befehl kommuniziert mit dem Remote-Repository. Der Befehl piped cut extrahiert nur das erste Feld (den Festschreibungs-Hash) und entfernt die durch Tabulatoren getrennte Referenzzeichenfolge.
  • git rev-parse HEAD gibt den lokalen Commit-Hash zurück.
  • Die Bash-Syntax [ a = b ] && x || y vervollständigt den Einzeiler: Dies ist ein Bash String-Vergleich= innerhalb eines Testkonstrukts [ test ], gefolgt von und-list und oder -list konstruiert && true || false.
31
wjordan

Ich schlage vor, Sie gehen das Skript sehen https://github.com/badele/gitcheck . Ich habe dieses Skript zum Einchecken in einem Durchgang für alle Ihre Git-Repositorys codiert und es zeigt, wer nicht festgeschrieben und wer nicht gepusht/gezogen hat.

Hier ein Beispielergebnis:

Enter image description here

20
Bruno Adelé

Ich habe diese Lösung auf die Kommentare von @jberger gestützt.

if git checkout master &&
    git fetch Origin master &&
    [ `git rev-list HEAD...Origin/master --count` != 0 ] &&
    git merge Origin/master
then
    echo 'Updated!'
else
    echo 'Not updated.'
fi
10
ma11hew28

Ich denke, der beste Weg, dies zu tun, wäre:

git diff remotes/Origin/HEAD

Vorausgesetzt, Sie haben diese Referenz registriert. Sie sollten, wenn Sie das Repository geklont haben, andernfalls (d. H. Wenn das Repository de novo lokal erstellt und auf die Remote-Site übertragen wurde), die Referenzspezifikation explizit hinzufügen.

9
Jeet

Es gibt bereits viele sehr funktionsreiche und geniale Antworten. Um einen gewissen Kontrast zu schaffen, könnte ich mit einer sehr einfachen Linie auskommen.

# Check return value to see if there are incoming updates.
if ! git diff --quiet remotes/Origin/HEAD; then
 # pull or whatever you want to do
fi
9
thuovila

Das folgende Skript funktioniert einwandfrei.

changed=0
git remote update && git status -uno | grep -q 'Your branch is behind' && changed=1
if [ $changed = 1 ]; then
    git pull
    echo "Updated successfully";
else
    echo "Up-to-date"
fi
6
Harikrishna

Ich würde den von Brool vorgeschlagenen Weg gehen. Das folgende einzeilige Skript verwendet den SHA1 Ihrer letzten festgeschriebenen Version und vergleicht ihn mit dem des fernen Origin. Änderungen werden nur dann übernommen, wenn sie sich unterscheiden. Und die Lösungen, die auf git pull oder git fetch basieren, sind noch leichter.

[ `git log --pretty=%H ...refs/heads/master^` != `git ls-remote Origin
-h refs/heads/master |cut -f1` ] && git pull
6

Wenn Sie dieses Skript ausführen, wird geprüft, ob der aktuelle Zweig einen git pull benötigt:

#!/bin/bash

git fetch -v --dry-run 2>&1 |
    grep -qE "\[up\s+to\s+date\]\s+$(
        git branch 2>/dev/null |
           sed -n '/^\*/s/^\* //p' |
                sed -r 's:(\+|\*|\$):\\\1:g'
    )\s+" || {
        echo >&2 "Current branch need a 'git pull' before commit"
        exit 1
}

Es ist sehr praktisch, es als Git-Hook-Pre-Commit zu speichern, um dies zu vermeiden

Merge branch 'foobar' of url:/path/to/git/foobar into foobar

wenn Sie commit vor pulling.

Um diesen Code als Hook zu verwenden, kopieren Sie einfach das Skript und fügen es ein

.git/hooks/pre-commit

und

chmod +x .git/hooks/pre-commit
5
Gilles Quenot

Ich möchte dies nur als tatsächlichen Beitrag veröffentlichen, da dies in den Kommentaren leicht zu übersehen ist.

Die richtige und beste Antwort auf diese Frage hat @Jake Berger gegeben. Vielen Dank, Alter, jeder braucht das und jeder vermisst das in den Kommentaren. Für alle, die hier mit dieser Frage zu kämpfen haben, ist die richtige Antwort. Verwenden Sie einfach die Ausgabe dieses Befehls, um zu wissen, ob Sie einen Git-Pull durchführen müssen. Wenn der Ausgang 0 ist, gibt es offensichtlich nichts zu aktualisieren.

@stackoverflow, gib diesem Typ eine Glocke. Danke @ Jake Berger

git rev-list HEAD...Origin/master --count will give you the total number of "different" commits between the two. – Jake Berger Feb 5 '13 at 19:23
5
diegeelvis_SA

Führen Sie git fetch (remote) aus, um Ihre Fernbedienungsreferenzen zu aktualisieren. Es zeigt Ihnen, was neu ist. Wenn Sie dann Ihre lokale Filiale auschecken, wird Ihnen angezeigt, ob sie sich hinter dem Upstream befindet.

4
che

Hier ist meine Version eines Bash-Skripts, das alle Repositorys in einem vordefinierten Ordner überprüft:

https://Gist.github.com/henryiii/5841984

Es kann zwischen häufigen Situationen wie Pull Needed und Push Needed unterscheiden und ist multithreading-fähig, sodass der Abruf auf einmal erfolgt. Es hat mehrere Befehle, wie Pull und Status.

Legen Sie einen Symlink (oder das Skript) in einen Ordner in Ihrem Pfad, dann funktioniert es als git all status (usw.). Es unterstützt nur Origin/Master, kann jedoch bearbeitet oder mit einer anderen Methode kombiniert werden.

2
Henry Schreiner
git ls-remote | cut -f1 | git cat-file --batch-check >&-

listet alles auf, auf das in einer Fernbedienung verwiesen wird, die sich nicht in Ihrem Repo befindet. Das Auffinden von Änderungen an bereits vorhandenen Dingen (z. B. Zurücksetzen auf vorherige Commits) per Fernzugriff erfordert etwas mehr:

git pack-refs --all
mine=`mktemp`
sed '/^#/d;/^^/{G;s/.\(.*\)\n.* \(.*\)/\1 \2^{}/;};h' .git/packed-refs | sort -k2 >$mine
for r in `git remote`; do 
    echo Checking $r ...
    git ls-remote $r | sort -k2 | diff -b - $mine | grep ^\<
done
1
jthill

Mit einfachen regulären Ausdrücken:

str=$(git status) 
if [[ $str =~ .*Your\ branch\ is\ behind.*by.*commits,\ and\ can\ be\ fast-forwarded ]]; then
    echo `date "+%Y-%m-%d %H:%M:%S"` "Needs pull"
else
    echo "Code is up to date"
fi
1
Tomas Kukis

Nachdem ich viele Antworten und mehrere Posts gelesen und einen halben Tag lang verschiedene Permutationen ausprobiert habe, habe ich mir das ausgedacht.

Wenn Sie unter Windows arbeiten, können Sie dieses Skript unter Windows mit Git Bash ausführen, das von Git for Windows bereitgestellt wird (Installation oder portabel).

Dieses Skript erfordert Argumente

 - lokaler Pfad, z.B. /d/source/project1[.____.₱- Git URL, z. https: //[email protected]/username/project1.git 
 - Passwort 
 
 Wenn in der Befehlszeile kein Passwort im Klartext eingegeben werden soll, 
 ändern Sie dann das Skript, um zu überprüfen, ob GITPASS leer ist; nicht ersetzen und Git nach einem Passwort fragen lassen

Das Skript wird

- Find the current branch
- Get the SHA1 of the remote on that branch
- Get the SHA1 of the local on that branch
- Compare them.

Wenn das Skript eine Änderung ausgibt, können Sie mit dem Abrufen oder Ziehen fortfahren. Das Skript ist möglicherweise nicht effizient, erledigt aber die Arbeit für mich.

Update - 2015-10-30: stderr to dev null, um zu verhindern, dass die URL mit dem Kennwort an die Konsole gesendet wird.

#!/bin/bash

# Shell script to check if a Git pull is required.

LOCALPATH=$1
GITURL=$2
GITPASS=$3

cd $LOCALPATH
BRANCH="$(git rev-parse --abbrev-ref HEAD)"

echo
echo git url = $GITURL
echo branch = $BRANCH

# Bash replace - replace @ with :[email protected] in the GIT URL
GITURL2="${GITURL/@/:[email protected]}"
FOO="$(git ls-remote $GITURL2 -h $BRANCH 2> /dev/null)"
if [ "$?" != "0" ]; then
  echo cannot get remote status
  exit 2
fi
FOO_ARRAY=($FOO)
BAR=${FOO_ARRAY[0]}
echo [$BAR]

LOCALBAR="$(git rev-parse HEAD)"
echo [$LOCALBAR]
echo

if [ "$BAR" == "$LOCALBAR" ]; then
  #read -t10 -n1 -r -p 'Press any key in the next ten seconds...' key
  echo No changes
  exit 0
else
  #read -t10 -n1 -r -p 'Press any key in the next ten seconds...' key
  #echo pressed $key
  echo There are changes between local and remote repositories.
  exit 1
fi
0

Für die Windows-Benutzer, die diese Frage suchen, habe ich einige Antworten in ein Powershell-Skript umgewandelt. Passen Sie die Einstellungen nach Bedarf an, speichern Sie sie in einer .ps1 -Datei und führen Sie sie bei Bedarf oder nach Zeitplan aus.

cd C:\<path to repo>
git remote update                           #update remote
$msg = git remote show Origin               #capture status
$update = $msg -like '*local out of date*'
if($update.length -gt 0){                   #if local needs update
    Write-Host ('needs update')
    git pull
    git reset --hard Origin/master
    Write-Host ('local updated')
} else {
    Write-Host ('no update needed')
}
0
Andrew Grothe

All diese komplexen Vorschläge, während die Lösung so kurz und einfach ist:

#!/bin/bash

BRANCH="<your branch name>"
LAST_UPDATE=`git show --no-notes --format=format:"%H" $BRANCH | head -n 1`
LAST_COMMIT=`git show --no-notes --format=format:"%H" Origin/$BRANCH | head -n 1`

if [ $LAST_COMMIT != $LAST_UPDATE ]; then
        echo "Updating your branch $BRANCH"
        git pull --no-edit
else
        echo "No updates available"
fi
0
Digital Human

Vielleicht ist dies, wenn Sie eine Aufgabe als crontab hinzufügen möchten:

#!/bin/bash
dir="/path/to/root"
lock=/tmp/update.lock
msglog="/var/log/update.log"

log()
{
        echo "$(date) ${1:-missing}" >> $msglog
}

if [ -f $lock ]; then
        log "Already run, exiting..."
else
        > $lock
        git -C ~/$dir remote update &> /dev/null
        checkgit=`git -C ~/$dir status`
        if [[ ! "$checkgit" =~ "Your branch is up-to-date" ]]; then
                log "-------------- Update ---------------"
                git -C ~/$dir pull &>> $msglog
                log "-------------------------------------"
        fi
        rm $lock

fi
exit 0
0
Altzone

Ich verwende eine Version eines Skripts, das auf Stephen Habermans Antwort basiert:

if [ -n "$1" ]; then
    gitbin="git -C $1"
else
    gitbin="git"
fi

# Fetches from all the remotes, although --all can be replaced with Origin
$gitbin fetch --all
if [ $($gitbin rev-parse HEAD) != $($gitbin rev-parse @{u}) ]; then
    $gitbin rebase @{u} --preserve-merges
fi

Angenommen, dieses Skript heißt git-fetch-and-rebase, kann es mit einem optionalen Argument directory name des lokalen Git-Repository aufgerufen werden, mit dem die Operation ausgeführt werden soll. Wenn das Skript ohne Argumente aufgerufen wird, wird davon ausgegangen, dass das aktuelle Verzeichnis Teil des Git-Repositorys ist.

Beispiele:

# Operates on /abc/def/my-git-repo-dir
git-fetch-and-rebase /abc/def/my-git-repo-dir

# Operates on the Git repository which the current working directory is part of
git-fetch-and-rebase

Es ist auch verfügbar hier .

0
Tuxdude