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.
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:
_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.
_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.
git fetch <remote>
git status
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 .
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.)
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.
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:
$(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
.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.[ 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
.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:
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
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.
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
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
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
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
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
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.
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.
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
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
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
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')
}
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
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
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 .