Kann "git pull --all" alle meine lokalen Filialen aktualisieren?

473

Ich habe oft mindestens 3 entfernte Niederlassungen: Master, Inszenierung und Produktion. Ich habe 3 lokale Zweige, die diese entfernten Zweige verfolgen.

Das Aktualisieren aller meiner lokalen Niederlassungen ist mühsam:

git fetch --all
git rebase origin/master
git checkout staging
git rebase origin/staging
git checkout production
git rebase origin/production

Ich würde gerne in der Lage sein, einfach ein "Git Pull-All" zu machen, aber ich habe es nicht geschafft, es zum Laufen zu bringen. Es scheint ein "Fetch - All" zu machen und dann den aktuellen Arbeitszweig zu aktualisieren (schnell vorzuspulen oder zusammenzuführen), aber nicht die anderen lokalen Zweige.

Ich bin immer noch nicht in der Lage, manuell zu jedem lokalen Zweig zu wechseln und zu aktualisieren.

mpoisot
quelle
8
Möchten Sie die automatische Aktualisierung lokaler Tracking-Zweige nur im Schnellvorlauf durchführen? Ypu sollte, weil Zusammenführung Konflikte haben kann, die Sie lösen müssten ...
Jakub Narębski
34
Unter der Annahme einer konservativen Beratungszeit von 300 US-Dollar, um damit herumzuspielen, hat diese einzelne Ausgabe Unternehmen 23.242.800 US-Dollar gekostet, bei einer Anzahl von 77.476. Betrachten Sie nun diese Frage stackoverflow.com/questions/179123/… und alle anderen. Beeindruckend.
Luke Puplett
16
@ Luke Du bist die erste Person, von der ich gehört habe, dass die Zeit, die ich damit verbracht habe, Git dazu zu bringen, das zu tun, was wir wollen, Unternehmen Geld kostet. Diese einfachen Dinge sollten automatisch und so einfach sein, dass ich keinen Browser öffnen muss, um die Foren zu lesen, IMO.
Samuel
13
@LukePuplett Es gibt fast 9-mal so viele Fragen zu Git auf SO im Vergleich zu Mercurial, und die Mehrheit der ersteren scheint zu lauten: "Wie mache ich <einfache Operation> in Git?". Das zeigt an, dass Git entweder schlecht designt, schlecht dokumentiert, nicht intuitiv oder alle drei ist.
Ian Kemp
26
@ IanKemp Ich bin mir nicht sicher, ob es sicher ist, diese Behauptung aufzustellen, ohne die Demografie von SO zu kennen. Wenn Mercurial hier nicht so häufig verwendet wird oder wenn seine Benutzer andere Foren verwenden, um danach zu fragen, würde ich dasselbe Ergebnis erwarten. :) Es gibt ~ 51-mal so viele Fragen zu Javascript im Vergleich zu Assembly - daher ist es möglicherweise nicht immer korrekt, Tools nur anhand dieser Art von Metriken zu beurteilen.
DanShumway

Antworten:

188

Das Verhalten, für das Sie beschreiben, pull --allist genau wie erwartet, jedoch nicht unbedingt nützlich. Die Option wird an git fetch weitergegeben, der dann alle Refs von allen Fernbedienungen abruft, anstatt nur die benötigte. pullführt dann den entsprechenden einzelnen Zweig zusammen (oder in Ihrem Fall neu).

Wenn Sie andere Filialen auschecken möchten, müssen Sie sie auschecken. Und ja, das Zusammenführen (und erneutes Basieren) erfordert unbedingt einen Arbeitsbaum, sodass sie nicht ohne Auschecken der anderen Zweige durchgeführt werden können. Sie können Ihre beschriebenen Schritte in ein Skript / einen Alias ​​einbinden, wenn Sie möchten. Ich würde jedoch empfehlen, die Befehle mit zu &&verknüpfen, damit bei einem Ausfall eines dieser Befehle nicht versucht wird, weiterzumachen.

Cascabel
quelle
2
Wenn Sie ein Beispiel für eine Befehlszeile angeben, würde ich abstimmen. Ich habe dieses Problem auf Github. Ich habe einen Zweig auf der Benutzeroberfläche erstellt. Jetzt brauche ich meinen Einheimischen, um die Filiale zu zeigen. git pull --all; Git Branch ... Argh ... der Befehl: Git Branch -a
Mariotti
@mariotti Hängt davon ab, was Sie versuchen, und es ist nicht wirklich klar aus Ihrem Kommentar. Am besten stellen Sie eine neue Frage.
Cascabel
1
Oder @Jefromi .. geben Sie ein Beispiel. Ich habe dir tatsächlich zugestimmt.
Mariotti
3
@mariotti Der Punkt dieser Antwort ist, dass die eingebauten Befehle nicht das tun, was das OP verlangt, daher ist die Reihenfolge der Schritte, die sie hatten, notwendig. Es ist möglich, diese Schritte zu automatisieren (siehe zum Beispiel Johns Antwort), aber sie müssen ausgeführt werden. Wenn das, was Sie versuchen, genau das gleiche wie das OP ist, gibt es kein wirkliches Beispiel, und wenn Sie versuchen, etwas anderes zu tun, sollten Sie eine neue Frage stellen - so funktioniert StackOverflow! (Und Ihr Kommentar ist unklar, aber meine beste Vermutung ist, dass Sie hier etwas anderes als das OP wollen, also ja, neue Frage.)
Cascabel
Ja, etwas anderes. Aber Ihre Antwort war einfach perfekt für den Kontext. Und ich muss vielleicht nicht mehr nur wegen Ihrer Antwort fragen. Nur: Die akzeptierte Antwort verwendet git-up, eine Schnittstelle zur git-Befehlszeile (nehme ich an). Ich hatte gehofft, Sie könnten es in wenigen Zeilen von Git-Befehlen explizit machen. Die aktuelle Antwort lautet NICHT git.
Mariotti
206

Ich benutze den syncUnterbefehl von Hub, um dies zu automatisieren. Ich habe alias git=hubin meinem .bash_profile, also lautet der Befehl, den ich eingebe:

git sync

Dadurch werden alle lokalen Zweige aktualisiert, die einen passenden Upstream-Zweig haben. Von der Manpage:

  • Wenn die lokale Niederlassung veraltet ist, spulen Sie sie vor.
  • Wenn der lokale Zweig nicht gepolsterte Arbeit enthält, warnen Sie davor.
  • Wenn der Zweig zusammengeführt zu sein scheint und sein vorgelagerter Zweig gelöscht wurde, löschen Sie ihn.

Es behandelt auch das Verstecken / Aufheben von nicht festgeschriebenen Änderungen im aktuellen Zweig.

Früher habe ich ein ähnliches Tool namens git-up verwendet , aber es wird nicht mehr gewartet und git syncmacht fast genau das Gleiche.

John
quelle
15
Was ist mit Windows?
Violette Giraffe
6
@ TrentonD.Adams Commit-Daten und Autorendaten sind unterschiedliche Konzepte. Eine Rebase ändert das Festschreibungsdatum, jedoch nicht das Autorendatum (außer in Konflikten, in denen sich auch das Autorendatum ändert). Das Autorendatum gibt an, wann der Commit-Baum erstellt wurde und sollte sich während einer nicht in Konflikt stehenden Rebase nicht ändern. Das Festschreibungsdatum ändert sich, da bei der erneuten Basis immer ein neues Festschreiben erstellt wird. Die Festschreibungsdaten sind also immer in der richtigen Reihenfolge.
Dev
16
Führen Sie run aus, um das automatische Rebasing-Verhalten von git-up auszuschalten git config --global git-up.rebase.auto false.
Dan Loewenherz
18
@MaxYankov Das Wiederherstellen des gemeinsam genutzten Verlaufs ist im Allgemeinen zu vermeiden. Es ist nichts Falsches daran, lokale Commits während eines Pulls neu zu gründen.
Dev
23
Durch das Wiederherstellen lokaler Commits wird der Verlauf neu geschrieben und einfacher als er tatsächlich ist. Mit rebase finden Sie sich möglicherweise mit Code wieder, der automatisch zusammengeführt wird, aber nicht kompiliert oder, schlimmer noch, kompiliert wird, aber nicht funktioniert. Durch das Zusammenführen wird Ihre Arbeitsweise bestätigt: Sie haben die Änderungen implementiert und getestet, bevor Sie die Änderungen anderer Personen übernommen haben, und das Zusammenführen von Commits ist ein sehr nützlicher Punkt: Hier stellen Sie sicher, dass verschiedene Chagesets gut zusammenspielen. Beim erneuten Basieren sieht es so aus, als würde dieser Prozess niemals stattfinden, was einfach nicht stimmt und eine sehr gefährliche Praxis ist.
Max Yankov
39

Ich weiß, dass diese Frage fast 3 Jahre alt ist, aber ich habe mir dieselbe Frage gestellt und keine fertige Lösung gefunden. Also habe ich selbst ein benutzerdefiniertes Git-Befehls-Shell-Skript erstellt.

Hier geht es, das git-ffwd-updateSkript macht folgendes ...

  1. es gibt ein aus git remote update, um die letzten Umdrehungen abzurufen
  2. verwendet dann git remote show, um eine Liste lokaler Zweige abzurufen, die einen entfernten Zweig verfolgen (z. B. Zweige, mit denen verwendet werden kann git pull).
  3. Anschließend wird überprüft, mit git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>wie vielen Commits sich der lokale Zweig hinter der Fernbedienung befindet (und umgekehrt).
  4. Wenn der lokale Zweig 1 oder mehr Commits voraus ist, kann er NICHT schnell weitergeleitet werden und muss von Hand zusammengeführt oder neu basiert werden
  5. Wenn der lokale Zweig 0 Commits voraus und 1 oder mehr Commits hinter sich hat, kann er von schnell vorgespult werden git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

Das Skript kann wie folgt aufgerufen werden:

$ git ffwd-update
Fetching origin
 branch bigcouch was 10 commit(s) behind of origin/bigcouch. resetting local branch to remote
 branch develop was 3 commit(s) behind of origin/develop. resetting local branch to remote
 branch master is 6 commit(s) behind and 1 commit(s) ahead of origin/master. could not be fast-forwarded

Das vollständige Skript sollte als gespeichert werden git-ffwd-updateund muss sich auf dem befinden PATH.

#!/bin/bash

main() {
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  CLB=$(git rev-parse --abbrev-ref HEAD);
  echo "$REMOTES" | while read REMOTE; do
    git remote update $REMOTE
    git remote show $REMOTE -n \
    | awk '/merges with remote/{print $5" "$1}' \
    | while read RB LB; do
      ARB="refs/remotes/$REMOTE/$RB";
      ALB="refs/heads/$LB";
      NBEHIND=$(( $(git rev-list --count $ALB..$ARB 2>/dev/null) +0));
      NAHEAD=$(( $(git rev-list --count $ARB..$ALB 2>/dev/null) +0));
      if [ "$NBEHIND" -gt 0 ]; then
        if [ "$NAHEAD" -gt 0 ]; then
          echo " branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $REMOTE/$RB. could not be fast-forwarded";
        elif [ "$LB" = "$CLB" ]; then
          echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. fast-forward merge";
          git merge -q $ARB;
        else
          echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. resetting local branch to remote";
          git branch -f $LB -t $ARB >/dev/null;
        fi
      fi
    done
  done
}

main $@
muhqu
quelle
1
Vielen Dank für dieses Skript. Ist es möglich, dass jemand dieses Skript in Windows Batch konvertieren kann?
Saariko
@ Saariko, warum willst du Git nicht für eine normale Windows-Shell verwenden? Wenn Sie so etwas wie Cygwin verwenden, sollte dieses Skript gut funktionieren ... (obwohl ich es nicht getestet habe)
Muhqu
@ RyanWilcox danke, ich benutze es wie jeden (Arbeits-) Tag ... ;-) Vielleicht möchten Sie in meinen Punktedateien nach weiteren Git-bezogenen Skripten und Aliasen suchen: github.com/muhqu/dotfiles
Muhqu
@muhqu Ich versuche, Ihr Skript zu verwenden, und ich weiß nicht, warum es beim ersten Mal funktioniert hat, aber es funktioniert momentan nicht "wie erwartet". Nehmen Sie zum Beispiel einen Blick auf diese . Warum liegt der Master immer noch 78 Commits zurück, nachdem ich Ihr Skript ausgeführt habe?
BPL
1
@muhqu Bei neueren Git-Versionen sollten -t und -l nicht zusammen in einem git branchAufruf verwendet werden. Ich habe das -l entfernt, um den Aufruf in zu ändern, git branch -f $LB -t $ARB >/dev/null;und jetzt funktioniert das Skript wie es sollte.
Radek Liska
24

Es ist nicht so schwer zu automatisieren:

#!/bin/sh
# Usage: fetchall.sh branch ...

set -x
git fetch --all
for branch in "$@"; do
    git checkout "$branch"      || exit 1
    git rebase "origin/$branch" || exit 1
done
Fred Foo
quelle
4
Es ist wahrscheinlich am besten, keine Aliase in Skripten zu verwenden. Dies ruft auch eigentlich nichts ab, sondern basiert nur auf dem bereits abgerufenen Inhalt. Sie sollten ändern git rebase origin/$branchzu git pull, so dass es aus dem entsprechenden Tracking - Zweig (vermutlich auf Herkunft) holen wird und entweder merge oder Fütterungsmaterial , wie durch die Konfiguration bestimmt.
Cascabel
@Jefromi: Ich hatte das vergessen fetch. Habe bearbeitet; zusätzliche Funktionen / Korrekturen, was auch immer bis zum OP ist.
Fred Foo
8
Ich denke immer noch, dass Sie möglicherweise verwenden pull(oder überprüfen branch.<branch>.rebase) möchten , damit Sie nicht versehentlich einen Zweig neu gründen, der so eingerichtet ist, dass er normal gezogen wird (Zusammenführen).
Cascabel
1
Verwenden Sie set -estatt || exit 1, um den Interpreter beim ersten Fehler zu beenden.
Crishoj
18

Dies ist immer noch nicht automatisch, da ich wünschte, es gäbe eine Option für - und es sollte überprüft werden, ob dies nur für Schnellvorlauf-Updates möglich ist (weshalb das manuelle Ausführen eines Pulls weitaus sicherer ist !!). aber Vorbehalte beiseite können Sie:

git fetch origin
git update-ref refs/heads/other-branch origin/other-branch

um die Position Ihrer lokalen Niederlassung zu aktualisieren, ohne sie überprüfen zu müssen.

Hinweis: Sie verlieren Ihre aktuelle Zweigposition und verschieben sie an die Stelle, an der sich der Zweig des Ursprungs befindet. Wenn Sie also zusammenführen müssen, gehen Daten verloren!

Matt Connolly
quelle
1
Dies ist genau die Lösung, nach der ich gesucht habe. Ich habe normalerweise keine ungezwungenen Änderungen an mehreren Zweigen und möchte nur meine verschiedenen lokalen Zweige aktualisieren, um sie an die Fernbedienung anzupassen. Diese Lösung ist viel besser als meine übliche Methode zum Löschen / erneuten Auschecken!
Dave Knight
1
kombiniert zu einem Befehl:git fetch origin other-branch:other-branch
fabb
12

Hier gibt es viele Antworten, aber keine, git-fetchmit denen die lokale Referenz direkt aktualisiert wird. Dies ist viel einfacher als das Auschecken von Zweigen und sicherer als git-update-ref.

Hier git-fetchaktualisieren wir nicht aktuelle Zweige und git pull --ff-onlyfür den aktuellen Zweig. Es:

  • Das Auschecken von Filialen ist nicht erforderlich
  • Aktualisiert Zweige nur, wenn sie schnell weitergeleitet werden können
  • Wird gemeldet, wenn kein schneller Vorlauf möglich ist

und hier ist es:

#!/bin/bash
currentbranchref="$(git symbolic-ref HEAD 2>&-)"
git branch -r | grep -v ' -> ' | while read remotebranch
do
    # Split <remote>/<branch> into remote and branchref parts
    remote="${remotebranch%%/*}"
    branchref="refs/heads/${remotebranch#*/}"

    if [ "$branchref" == "$currentbranchref" ]
    then
        echo "Updating current branch $branchref from $remote..."
        git pull --ff-only
    else
        echo "Updating non-current ref $branchref from $remote..."
        git fetch "$remote" "$branchref:$branchref"
    fi
done

Aus der Manpage für git-fetch:

   <refspec>
       The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>,
       followed by a colon :, followed by the destination ref <dst>.

       The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref
       that matches it is fast-forwarded using <src>. If the optional plus + is used, the local ref is
       updated even if it does not result in a fast-forward update.

Durch Angabe git fetch <remote> <ref>:<ref>(ohne +) erhalten wir einen Abruf, der die lokale Referenz nur aktualisiert, wenn sie schnell weitergeleitet werden kann.

Hinweis : Dies setzt voraus, dass die lokalen und Remote-Zweige den gleichen Namen haben (und dass Sie alle Zweige verfolgen möchten). Es sollten wirklich Informationen darüber verwendet werden, welche lokalen Zweige Sie haben und was sie für die Verfolgung eingerichtet sind.

Quornian
quelle
1
"Aktualisiert Zweige nur, wenn sie schnell vorgespult werden können" - welche Bedeutung hat der schnelle Vorlauf? Wenn ich die neuesten Quellen in allen meinen Filialen haben möchte, warum sollte ich mich dann für den schnellen Vorlauf interessieren oder nicht? Es sind solche Dinge, die mich über Git und seine Fanboi zum Lachen bringen. Sie können dies nicht mit nur einem Befehl tun. Stattdessen müssen Sie c*nSchritte ausführen (anstelle von 1), wobei ceinige Anzahl wiederholter Befehle und ndie Anzahl der Zweige angegeben sind.
JWW
@jww Es hilft nicht, "über Git und seine Fanboi zu lachen", wenn es das VCS ist, das die meisten der Welt verwenden. Aber ich schweife ab ... Ich denke, im Kontext dieser Art von "Global Pull" -Skript ist es ratsam, nicht zu versuchen, Änderungen an nicht aktuellen Zweigen vorzunehmen, wenn diese Zusammenführungskonflikte haben.
Ville
Das war hilfreich, danke. Das Einzige , was ich nicht mochte war , dass es eine Niederlassung vor Ort für jeden Remote - Zweig erstellt (darunter auch solche , in denen ich nicht interessiert bin), so dass ich geändert , git branch -r | grep -v ' -> ' | while read remotebranchum git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranches zu Verzweigungen zu begrenzen bereits lokal ich habe. Außerdem habe ich git fetch --pruneam Anfang ein hinzugefügt , um die Liste der Remote-Zweige zu aktualisieren, bevor etwas unternommen wird, wodurch einige Warnungen vermieden werden.
Nate Cook
11

Dieses Problem ist (noch) nicht gelöst, zumindest nicht einfach / ohne Skripterstellung: In diesem Beitrag auf der Git-Mailingliste von Junio ​​C Hamano wird die Situation erläutert und eine einfache Lösung gefordert.

Der Hauptgrund ist, dass Sie dies nicht brauchen sollten:

Mit Git, das nicht alt ist (dh v1.5.0 oder neuer), gibt es keinen Grund mehr, einen lokalen "Entwickler" zu haben, der nur noch die Fernbedienung verfolgt. Wenn Sie nur nachsehen möchten, können Sie den Zweig für die Fernverfolgung direkt auf einem abgetrennten HEAD mit " git checkout origin/dev" überprüfen .

Dies bedeutet, dass die einzigen Fälle, die wir für Benutzer bequemer machen müssen, darin bestehen, diese lokalen Zweige zu bearbeiten, die entfernte Zweige "verfolgen", wenn Sie lokale Änderungen haben oder wenn Sie planen, einige zu haben.

Wenn Sie lokale Änderungen an "dev" haben, die markiert sind, um das Entfernen von "dev" zu verfolgen, und wenn Sie sich in einem Zweig befinden, der sich von "dev" unterscheidet, sollten wir nichts tun, nachdem " git fetch" das Remote-Tracking "dev" aktualisiert hat. . Es wird sowieso nicht schnell vorspulen

Die Forderung nach einer Lösung bestand darin, eine Option oder ein externes Skript zu verwenden, um lokale Zweige, die jetzt auf Fernverfolgungszweige folgen, zu beschneiden , anstatt sie durch Schnellvorlauf auf dem neuesten Stand zu halten, wie es das Originalplakat verlangt.

Wie wäre es also mit " git branch --prune --remote=<upstream>", das über lokale Zweige iteriert, und wenn

(1) es ist nicht der aktuelle Zweig; und
(2) es ist markiert, um einen Zweig zu verfolgen, der aus dem <upstream> entnommen wurde; und
(3) es hat keine eigenen Verpflichtungen;

dann diesen Zweig entfernen? " git remote --prune-local-forks <upstream>" ist auch gut; Es ist mir egal, welcher Befehl die Funktion so stark implementiert.

Hinweis: Ab Git 2.10 gibt es keine solche Lösung. Beachten Sie, dass dergit remote pruneUnterbefehl und dasgit fetch --pruneEntfernen des Remote-Tracking-Zweigs für einen Zweig, der nicht mehr auf dem Remote vorhanden ist, und nicht das Entfernen des lokalen Zweigs, der den Remote-Tracking-Zweig verfolgt (für den der Remote-Tracking-Zweig der Upstream-Zweig ist).

Jakub Narębski
quelle
Anstatt nur Links zu veröffentlichen, veröffentlichen Sie bitte den tatsächlichen Inhalt und verwenden Sie Links als Referenz. Dieser Link ist jetzt tot. Schade, klang vielversprechend. (Mir ist klar, dass diese Antwort aus dem Jahr 2009 stammt, daher ist dies nur ein Hinweis für zukünftige Referenzzwecke.)
Michael
Danke (und wow, schnelle Antwort nach so vielen Jahren). Ich sehe jetzt, dass dieser Thread ein " Aufruf für eine einfache Lösung" ist, im Gegensatz zu meiner ursprünglichen Fehlinterpretation, " bietet eine einfache Lösung".
Michael
@michael_n: erweitert ... hmm, jetzt sehe ich, dass es in dem Beitrag nicht genau um die angeforderte Lösung ging, sondern um das Problem (unter der Annahme eines XY-Problemfalls).
Jakub Narębski
Hmm, das Spähen mit abgenommenem Kopf sollte erleichtert werden, insbesondere sollte es nützliche Informationen im Status anzeigen und es ermöglichen, den Arbeitsbereich mit einigen Rückmeldungen (wie den gezogenen Commits) schnell vorzuspulen. Dann wäre es ein Ersatz für schreibgeschützte lokale Filialen.
eckes
9

Hier gibt es viele akzeptable Antworten, aber einige der Klempnerarbeiten sind für Uneingeweihte möglicherweise etwas undurchsichtig. Hier ist ein viel einfacheres Beispiel, das leicht angepasst werden kann:

$ cat ~/bin/git/git-update-all
#!/bin/bash
# Update all local branches, checking out each branch in succession.
# Eventually returns to the original branch. Use "-n" for dry-run.
git_update_all() {
  local run br
  br=$(git name-rev --name-only HEAD 2>/dev/null)
  [ "$1" = "-n" ] && shift && run=echo

  for x in $( git branch | cut -c3- ) ; do
     $run git checkout $x && $run git pull --ff-only || return 2
  done

  [ ${#br} -gt 0 ] && $run git checkout "$br"
}

git_update_all "$@"

Wenn Sie ~/bin/gitzu Ihrer hinzufügen PATH(vorausgesetzt, die Datei ist ~/bin/git/git-update-all), können Sie einfach Folgendes ausführen:

$ git update-all
Michael
quelle
Vielen Dank! Du hast mir eine Stunde Spielzeit
erspart
5

Fügen Sie dieses Skript zu .profileauf Mac OS X:

# Usage:
#   `git-pull-all` to pull all your local branches from origin
#   `git-pull-all remote` to pull all your local branches from a named remote

function git-pull-all() {
    START=$(git symbolic-ref --short -q HEAD);
    for branch in $(git branch | sed 's/^.//'); do
        git checkout $branch;
        git pull ${1:-origin} $branch || break;
    done;
    git checkout $START;
};

function git-push-all() {
    git push --all ${1:-origin};
};
Mike
quelle
1
Sollte dies nicht zuerst alle Änderungen speichern und dann wiederherstellen?
Mel
5

Hier ist eine gute Antwort: Wie man alle Git-Zweige abruft

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
melkowski
quelle
Warum schlagen Sie vor, git fetchund git pullstatt nur zu tun git pull?
Syntagma
Vielen Dank. Es scheint, dass Pull alle Zweige von allen Fernbedienungen abruft. Geändert
Milkovsky
8
Dadurch werden alle Fernbedienungen abgerufen, aber nur der aktuelle Zweig zusammengeführt. Wenn Sie 10 Fernbedienungen haben, müssen Sie jede manuell auschecken und zusammenführen.
Mpoisot
Dadurch wird alle Remote - Niederlassungen vor Ort mit erstellen origin/Präfix
Yassine ElBadaoui
3

Ein Skript, das ich für meinen GitBash geschrieben habe . Erreicht Folgendes:

  • Standardmäßig können Sie beim Abrufen des Ursprungs für alle Zweige, die zum Verfolgen des Ursprungs eingerichtet sind, bei Bedarf eine andere Fernbedienung angeben.
  • Wenn sich Ihr aktueller Zweig in einem fehlerhaften Zustand befindet, werden Ihre Änderungen gespeichert und am Ende wird versucht, diese Änderungen wiederherzustellen.
  • Für jeden lokalen Zweig, der zum Verfolgen eines Remote-Zweigs eingerichtet ist, gilt Folgendes:
    • git checkout branch
    • git pull origin
  • Schließlich kehren Sie zu Ihrem ursprünglichen Zweig zurück und stellen den Status wieder her.

** Ich benutze dies, habe es aber nicht gründlich getestet, benutze es auf eigenes Risiko. Sehen Sie ein Beispiel dieses Skript in einer .bash_alias Datei hier .

    # Do a pull on all branches that are tracking a remote branches, will from origin by default.
    # If current branch is dirty, will stash changes and reply after pull.
    # Usage: pullall [remoteName]
    alias pullall=pullAll
    function pullAll (){
     # if -h then show help
     if [[ $1 == '-h' ]]
    then
      echo "Description: Pulls new changes from upstream on all branches that are tracking remotes."
      echo 
      echo "Usage: "
      echo "- Default: pullall"
      echo "- Specify upstream to pull from: pullall [upstreamName]"
      echo "- Help: pull-all -h"
    else

     # default remote to origin
     remote="origin"
     if [ $1 != "" ]
     then
       remote=$1
     fi

     # list all branches that are tracking remote
     # git branch -vv : list branches with their upstreams
     # grep origin : keep only items that have upstream of origin
     # sed "s/^.."... : remove leading *
     # sed "s/^"..... : remove leading white spaces
     # cut -d" "..... : cut on spaces, take first item
     # cut -d splits on space, -f1 grabs first item
     branches=($(git branch -vv | grep $remote | sed "s/^[ *]*//" | sed "s/^[ /t]*//" | cut -d" " -f1))

     # get starting branch name
     startingBranch=$(git rev-parse --abbrev-ref HEAD)

     # get starting stash size
     startingStashSize=$(git stash list | wc -l)

     echo "Saving starting branch state: $startingBranch"
     git stash

     # get the new stash size
     newStashSize=$(git stash list | wc -l)

     # for each branch in the array of remote tracking branches
     for branch in ${branches[*]}
     do
       echo "Switching to $branch"
       git checkout $branch

       echo "Pulling $remote"
       git pull $remote

     done

     echo "Switching back to $startingBranch"
     git checkout $startingBranch

     # compare before and after stash size to see if anything was stashed
     if [ "$startingStashSize" -lt "$newStashSize" ]
     then
       echo "Restoring branch state"
       git stash pop
     fi
    fi
    }
philosowaffle
quelle
Können Sie eine entsprechende Windows-Bat-Datei bereitstellen?
Jaffy
1
@Jaffy Ich bin mir nicht sicher, wie viel Zeit ich zur Verfügung habe und ich bin nicht besonders fließend im Batch, aber ich kann es versuchen. Ich werde meine Fortschritte hier veröffentlichen , vielleicht können andere eingreifen und helfen?
Philosowaffle
3

Wenn Sie unter Windows arbeiten, können Sie PyGitUp verwenden , einen Klon git-upfür Python. Sie können es mit pip mit pip install --user git-upoder über Scoop mit installierenscoop install git-up

[4]

Simon Hartcher
quelle
3

Einfach eine aktualisierte Antwort posten. git-upwird nicht mehr gepflegt und wenn Sie die Dokumentation lesen, erwähnen sie, dass die Funktionalität jetzt in git verfügbar ist .

Ab Git 2.9 macht git pull --rebase --autostash im Grunde das Gleiche.

Wenn Sie auf Git 2.9 oder höher aktualisieren, können Sie diesen Alias ​​verwenden, anstatt git-up zu installieren:

git config --global alias.up 'pull --rebase --autostash'

Sie können dies auch für jeden git pullab Git 2.9 einstellen (danke @VonC, siehe seine Antwort hier )

git config --global pull.rebase true
git config --global rebase.autoStash true
aug
quelle
1
Du brauchst keinen Alias. Ein einfacher Git Pull ist genug, mit der richtigen Konfiguration: stackoverflow.com/a/40067353/6309
VonC
Großer Anruf aus Dank @VonC ich meine Antwort aktualisiert :) auch eine PR zur vorlegen könnte git-upDokumentation , weil sie nicht erwähnen , dass
August
Dadurch werden nicht alle lokalen Zweige gleichzeitig aktualisiert, weshalb ich hauptsächlich verwendet habe git-up.
Ray
Dokumentation aktualisiert git-up:)
August
3

Ich bin auf das gleiche Problem dieser Frage gestoßen ...

Ich wunderte mich darüber und führte eine kleine Alias-Funktion in meiner .bashrcDatei aus:

gitPullAll() {
    for branch in `git branch | sed -E 's/^\*/ /' | awk '{print $1}'`; do
        git checkout $branch
        git pull -p
        printf "\n"
    done
    echo "Done"
}

Arbeitete für mich (:

Francisco Neto
quelle
2

Wenn refs / Heads / Master schnell zu refs / remotes / foo / master vorgespult werden kann , wird die Ausgabe von

git merge-base refs/heads/master refs/remotes/foo/master

sollte die SHA1-ID zurückgeben, auf die refs / Heads / Master zeigt. Auf diese Weise können Sie ein Skript zusammenstellen, das automatisch alle lokalen Zweige aktualisiert, auf die keine Umleitungs-Commits angewendet wurden.

Dieses kleine Shell-Skript (ich nannte es git-can-ff ) zeigt, wie es gemacht werden kann.

#!/bin/sh

set -x

usage() {
    echo "usage: $(basename $0) <from-ref> <to-ref>" >&2
    exit 2
}

[ $# -ne 2 ] && usage

FROM_REF=$1
TO_REF=$2

FROM_HASH=$(git show-ref --hash $FROM_REF)
TO_HASH=$(git show-ref --hash $TO_REF)
BASE_HASH=$(git merge-base $FROM_REF $TO_REF)

if [ "$BASE_HASH" = "$FROM_HASH" -o \
     "$BASE_HASH" = "$FROM_REF" ]; then
    exit 0
else
    exit 1
fi
Hillu
quelle
Was implizieren Sie mit diesem Kommentar?
Hillu
Ich selbst bin nicht in der Lage, das von Hillu vorgeschlagene Skript zu schreiben, und ich bin nicht sicher genug von meinem Git-Wissen, um Git-Merge-Base zu verwenden.
Norman Ramsey
2
Ich fürchte, ich verstehe das Modell nicht gut genug, um das so freundliche Skript auszunutzen. Es ist genug, um eine Person dazu zu bringen, zu mercurcial zu wechseln.
Norman Ramsey
Ich persönlich fand Tommi Virtanens Artikel "Git für Informatiker" sehr hilfreich, um mich mit dem Modell und der Terminologie von git vertraut zu machen.
Hillu
2

Um die Antwort von Matt Connolly zu vervollständigen, ist dies eine sicherere Möglichkeit, lokale Zweigstellenreferenzen zu aktualisieren, die schnell weitergeleitet werden können, ohne den Zweig auszuchecken. Zweige, die nicht schnell weitergeleitet werden können (dh die auseinander gegangen sind), werden nicht aktualisiert, und der aktuell ausgecheckte Zweig wird nicht aktualisiert (da dann auch die Arbeitskopie aktualisiert werden sollte).

git fetch

head="$(git symbolic-ref HEAD)"
git for-each-ref --format="%(refname) %(upstream)" refs/heads | while read ref up; do
    if [ -n "$up" -a "$ref" != "$head" ]; then
        mine="$(git rev-parse "$ref")"
        theirs="$(git rev-parse "$up")"
        base="$(git merge-base "$ref" "$up")"
        if [ "$mine" != "$theirs" -a "$mine" == "$base" ]; then
            git update-ref "$ref" "$theirs"
        fi
    fi
done
Bruno De Fraine
quelle
2

Ein etwas anderes Skript, das nur Zweige vorspult, deren Namen mit ihrem vorgelagerten Zweig übereinstimmen. Außerdem wird der aktuelle Zweig aktualisiert, wenn ein schneller Vorlauf möglich ist.

Stellen Sie sicher, dass alle Upstream-Zweige Ihrer Zweige korrekt eingestellt sind, indem Sie ausführen git branch -vv. Stellen Sie den Upstream-Zweig mit eingit branch -u origin/yourbanchname

Kopieren-Einfügen in eine Datei und chmod 755:

#!/bin/sh

curbranch=$(git rev-parse --abbrev-ref HEAD)

for branch in $(git for-each-ref refs/heads --format="%(refname:short)"); do
        upbranch=$(git config --get branch.$branch.merge | sed 's:refs/heads/::');
        if [ "$branch" = "$upbranch" ]; then
                if [ "$branch" = "$curbranch" ]; then
                        echo Fast forwarding current branch $curbranch
                        git merge --ff-only origin/$upbranch
                else
                        echo Fast forwarding $branch with origin/$upbranch
                        git fetch . origin/$upbranch:$branch
                fi
        fi
done;
qwertzguy
quelle
2

Der folgende Einzeiler spult nach Möglichkeit alle Zweige vor, die einen vorgelagerten Zweig haben, und gibt ansonsten einen Fehler aus:

git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

Wie funktioniert es?

Es verwendet ein benutzerdefiniertes Format mit dem git branchBefehl. Für jeden Zweig, der einen vorgelagerten Zweig hat, wird eine Zeile mit dem folgenden Muster gedruckt:

git push . <remote-ref>:<branch>

Dies kann direkt in geleitet werden sh(vorausgesetzt, die Zweignamen sind wohlgeformt). Lassen Sie das aus, um | shzu sehen, was es tut.

Vorsichtsmaßnahmen

Der Einzeiler wird Ihre Fernbedienungen nicht kontaktieren. Geben Sie ein git fetchoder git fetch --allvor dem Ausführen aus.

Der aktuell ausgecheckte Zweig wird nicht mit einer Meldung wie aktualisiert

! [remote rejected] origin/master -> master (branch is currently checked out)

Hierfür können Sie auf regelmäßige zurückgreifen git pull --ff-only.

Alias

Fügen Sie Folgendes hinzu, .gitconfigdamit git fftdieser Befehl ausgeführt wird:

[alias]
        fft = !sh -c 'git branch --format \"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)\" | sh' -

Siehe auch meine .gitconfig. Der Alias ​​ist eine Abkürzung für "Fast-Forward-Tracking (Zweige)".

krlmlr
quelle
Dies ist eine schöne Lösung, obwohl ich denke, dass ich die von hub@John vorgeschlagene Lösung für eine bessere Ausgabe verwenden werde.
Didier L
Das ist schnell, einfach und funktioniert tatsächlich! Ich bin allerdings verblüfft über das git push, was völlig anders ist als das, was man erwarten würde. Was ist das Geheimnis?
BrandonLWhite
@BrandonLWhite: Ich verstehe die Frage nicht. Was erwartest du von git push?
krlmlr
git pushhat Upload-Semantik - Ich habe einige Commits lokal, die ich Upstream senden möchte. git pullhat Download-Semantik - Ich möchte einige Upstream-Remote-Commits in meine lokale Niederlassung bringen. Da es sich um das Herunterladen neuer Commits von der Remote-Datei auf die lokale Ebene handelt, git pullist dies die naheliegende Wahl. Aber nein, dieser Trick verwendet git push. Wie führt git pushdies dazu, dass Remote-Änderungen an meiner lokalen Niederlassung vorgenommen werden?!
BrandonLWhite
git pushkann auch zum Aktualisieren lokaler Zweige verwendet werden, sofern es sich um ein Schnellvorlauf-Update handelt.
krlmlr
1

Das Skript von @larsmans, etwas verbessert:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git rebase "origin/$branch" || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git rebase "origin/$CURRENT" || exit 1

Nach Abschluss des Vorgangs wird die Arbeitskopie aus demselben Zweig ausgecheckt wie vor dem Aufruf des Skripts.

Die git pullVersion:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git pull || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Saran
quelle
1

Es sieht so aus, als hätten viele andere ähnliche Lösungen beigesteuert, aber ich dachte, ich würde teilen, was ich mir ausgedacht habe, und andere einladen, einen Beitrag zu leisten. Diese Lösung hat eine schöne, farbenfrohe Ausgabe, verwaltet Ihr aktuelles Arbeitsverzeichnis elegant und ist schnell, da sie keine Kassen durchführt und Ihr Arbeitsverzeichnis intakt lässt. Außerdem ist es nur ein Shell-Skript ohne andere Abhängigkeiten als git. (bisher nur unter OSX getestet)

#!/usr/bin/env bash

gitup(){    
RED='\033[33;31m'
YELLO='\033[33;33m'
GREEN='\033[33;32m'
NC='\033[0m' # No Color

HEAD=$(git rev-parse HEAD)
CHANGED=$(git status --porcelain | wc -l)

echo "Fetching..."
git fetch --all --prune &>/dev/null
for branch in `git for-each-ref --format='%(refname:short)' refs/heads`; do

    LOCAL=$(git rev-parse --quiet --verify $branch)
    if [ "$HEAD" = "$LOCAL" ] && [ $CHANGED -gt 0 ]; then
        echo -e "${YELLO}WORKING${NC}\t\t$branch"
    elif git rev-parse --verify --quiet $branch@{u}&>/dev/null; then
        REMOTE=$(git rev-parse --quiet --verify $branch@{u})
        BASE=$(git merge-base $branch $branch@{u})

        if [ "$LOCAL" = "$REMOTE" ]; then
           echo -e "${GREEN}OK${NC}\t\t$branch" 
        elif [ "$LOCAL" = "$BASE" ]; then
            if [ "$HEAD" = "$LOCAL" ]; then
                git merge $REMOTE&>/dev/null
            else
                git branch -f $branch $REMOTE
            fi
            echo -e "${GREEN}UPDATED${NC}\t\t$branch"
        elif [ "$REMOTE" = "$BASE" ]; then
            echo -e "${RED}AHEAD${NC}\t\t$branch"
        else
            echo -e "${RED}DIVERGED${NC}\t\t$branch"
        fi
    else
        echo -e "${RED}NO REMOTE${NC}\t$branch"
    fi
done
}

https://github.com/davestimpert/gitup

Entschuldigung, ich habe anscheinend auch den gleichen Namen wie das andere Tool oben gefunden.

Stimp
quelle
2
Bist du derjenige, der das geschrieben hat? Wenn ja, geben Sie bitte Ihre Zugehörigkeit an, dh teilen Sie uns mit, wie Sie damit verbunden sind. Bitte lesen Sie mehr dazu für weitere Informationen. Speziell nicht erzählen - zeigen! ;; Sagen Sie uns, welche Teile Ihres Skripts und wie / warum es das Problem löst.
Keale
1
Ja, ich habe es geschrieben. Ich habe die Quelle oben für ein schnelles Kopieren und Einfügen in Ihre .bashrc oder .zshrc aufgenommen.
Stimp
Dies ist eine schöne Lösung und funktioniert gut. Niemand hatte es bemerkt?
Ville
1

Dies kann mit dem folgenden Skript durchgeführt werden ... Es werden zuerst alle Zweige abgerufen und einzeln ausgecheckt und von selbst aktualisiert.

#!/bin/bash
git branch -r | grep -v '\->' | while read remote; do git branch --track 
"${remote#origin/}" "$remote"; done

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
branch_name=$(git branch | awk '{print $1" "}' | grep -v '*' | xargs)
for branch in $branch_name; do
   git checkout "$branch" || exit 1
   git rebase "origin/$branch" || exit 1
   git pull origin $branch|| exit 1
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Vikash Singh
quelle
Fügen Sie eine Erklärung für die Bewertung Ihrer Antwort hinzu
Narr-Entwickler
1

Sie können es nicht mit nur einem Git-Befehl tun, aber Sie können es mit einer Bash-Zeile automatisieren.

Um alle Zweige sicher mit einer Zeile zu aktualisieren, gehe ich wie folgt vor:

git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
  • Wenn ein Zweig nicht vorgespult werden kann oder ein Fehler auftritt, wird er angehalten und Sie bleiben in diesem Zweig, sodass Sie die Kontrolle zurückerobern und manuell zusammenführen können.

  • Wenn alle Zweige schnell vorgespult werden können, endet sie mit dem Zweig, in dem Sie sich gerade befanden, und lässt Sie dort, wo Sie sich vor dem Update befanden.

Erklärungen:

Zur besseren Lesbarkeit kann es auf mehrere Zeilen aufgeteilt werden:

git fetch --all && \
for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*')
    do git checkout $branch && \
    git merge --ff-only || break
done
  1. git fetch --all && ... => Ruft alle Refs von allen Fernbedienungen ab und fährt mit dem nächsten Befehl fort, wenn kein Fehler aufgetreten ist.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Aus dem Ausgang git branch, sednehmen Sie die Linie mit einem *und es bis zum Ende bewegen (so dass der aktuelle Zweig zuletzt aktualisiert). Dann trentfernen Sie einfach die *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Überprüfen Sie für jeden aus dem vorherigen Befehl erhaltenen Zweignamen diesen Zweig und versuchen Sie, ihn mit einem Schnellvorlauf zusammenzuführen. Wenn es fehlschlägt, breakwird aufgerufen und der Befehl stoppt hier.

Natürlich können Sie ersetzen git merge --ff-onlymit , git rebasewenn es ist , was Sie wollen.

Schließlich können Sie es als Alias in Ihr Bashrc einfügen :

alias git-pull-all='git fetch --all && for branch in $(git branch | sed '\''/*/{$q;h;d};$G'\'' | tr -d "*") ; do git checkout $branch && git merge --ff-only || break ; done'

Oder wenn Sie Angst haben, das 'und' durcheinander zu bringen, oder einfach lieber die syntaktische Lesbarkeit in Ihrem Editor beibehalten möchten, können Sie dies als Funktion deklarieren:

git-pull-all()
{
    git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
}

Bonus:

Für diejenigen, die die Erklärung des sed '/*/{$q;h;d};$G'Teils wünschen :

  • /*/=> Suche nach der Zeile mit a *.

  • {$q => Wenn es in der letzten Zeile steht, beenden Sie (wir müssen nichts tun, da der aktuelle Zweig bereits der letzte in der Liste ist).

  • ;h;d} => Andernfalls speichern Sie die Zeile im Haltepuffer und löschen Sie sie an der aktuellen Listenposition.

  • ;$G => Wenn die letzte Zeile erreicht ist, hängen Sie den Inhalt des Haltepuffers an.

Bouty
quelle
Sie können die Verrücktheit endloser Zeilen vermeiden, &&indem Sie set -eoben im Skript festlegen.
Mcepl
0

Kann "git pull --all" alle meine lokalen Filialen aktualisieren?

Nein, ich kann nicht. Für den schnellen Vorlauf habe ich dazu ein kleines Tool geschrieben. https://github.com/changyuheng/git-fast-forward-all

Vorteile dieses Tools:

  1. Unterstützt mehrere Fernbedienungen in einem Repository. ( hub syncUnterstützt derzeit nicht mehrere Fernbedienungen.)
  2. Unterstützt unterschiedliche Namen in der lokalen Niederlassung und die entsprechende Fernverfolgungsbranche.
  3. Viel schneller als andere Skripte, die Remote für jeden einzelnen Zweig abrufen.
  4. Kein fehleranfälliges Regex-Parsen / Bearbeiten.
changyuheng
quelle
1
Sie können vermeiden, das Netzwerk mit zu treffen git fetch . refspec. Das .sagt, dass aus dem aktuellen Repository anstatt aus dem Remote-Repository abgerufen werden soll.
Hugomg
-1

Ab Git 2.9:

git pull --rebase --autostash

Siehe https://git-scm.com/docs/git-rebase

Erstellen Sie automatisch einen temporären Stash, bevor der Vorgang beginnt, und wenden Sie ihn an, nachdem der Vorgang beendet wurde. Dies bedeutet, dass Sie Rebase auf einem schmutzigen Arbeitsbaum ausführen können. Seien Sie jedoch vorsichtig: Die endgültige Stash-Anwendung nach einer erfolgreichen Basis kann zu nicht trivialen Konflikten führen.

nha
quelle
-1

In der Tat version 1.8.3.1funktioniert es mit git :

[root@test test]# git br
* master
  release/0.1
  update
[root@test test]# git pull --rebase
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From http://xxx/scm/csdx/test-git
   d32ca6d..2caa393  release/0.1 -> origin/release/0.1
Current branch master is up to date.
[root@test test]# git --version
git version 1.8.3.1

Im Hauptzweig können Sie alle anderen Zweige aktualisieren. @Cascabel

Ich weiß nicht, welche Version es kaputt macht / repariert, in 2.17 (welche ich benutze) kann es funktionieren.

daixiang0
quelle