"Git pull --all" può aggiornare tutte le mie filiali locali?


473

Ho spesso almeno 3 filiali remote: master, stadiazione e produzione. Ho 3 filiali locali che tracciano quelle filiali remote.

Aggiornare tutte le mie filiali locali è noioso:

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

Mi piacerebbe poter fare solo un "git pull-all", ma non sono riuscito a farlo funzionare. Sembra fare un "recupero - tutti", quindi aggiorna (avanzamento veloce o unisce) il ramo di lavoro corrente, ma non gli altri rami locali.

Sono ancora bloccato a passare manualmente a ciascun ramo locale e l'aggiornamento.


8
Desideri l'aggiornamento automatico delle filiali di localizzazione locali solo in caso di avanzamento rapido? Sì, perché l'unione può avere un conflitto che dovresti risolvere ...
Jakub Narębski

34
Supponendo che $ 300 conservativi nel tempo di consulenza per risolvere questo problema, questo singolo problema è costato alle aziende $ 23.242.800 utilizzando un conteggio visualizzazioni di 77.476. Ora considera questa domanda stackoverflow.com/questions/179123/… e tutti gli altri. Wow.
Luke Puplett,

16
@Luke Sei la prima persona che ho sentito sottolineare come il tempo speso nel tentativo di far fare quello che vogliamo costa ai soldi delle aziende. Queste cose semplici dovrebbero essere automatiche e dovrebbero essere così semplici che non devo aprire un browser per leggere i forum, IMO.
Samuel,

13
@LukePuplett Ci sono circa 9 volte il numero di domande su git su SO rispetto a Mercurial, e la maggior parte delle prime sembra essere "come posso fare <operazione semplice> in git?". Ciò indica che git è mal progettato, scarsamente documentato, non intuitivo o tutti e tre.
Ian Kemp,

26
@IanKemp Non sono sicuro che sia sicuro fare questa affermazione senza conoscere i dati demografici di SO. Se Mercurial non è così comunemente usato qui, o se i suoi utenti usano altri forum per chiederlo, mi aspetto di vedere lo stesso risultato. :) Ci sono circa 51 volte il numero di domande su Javascript rispetto all'Assemblea - quindi potrebbe non essere sempre preciso giudicare gli strumenti solo da questo tipo di metriche.
danShumway

Risposte:


188

Il comportamento che descrivi pull --allè esattamente come previsto, anche se non necessariamente utile. L'opzione viene passata a git fetch, che quindi recupera tutti i ref da tutti i telecomandi, anziché solo quello necessario; pullquindi unisce (o nel tuo caso, ripristina) il singolo ramo appropriato.

Se vuoi dare un'occhiata ad altre filiali, dovrai controllarle. E sì, l'unione (e la riformulazione) richiedono assolutamente un albero di lavoro, quindi non possono essere eseguiti senza estrarre gli altri rami. Se lo desideri, potresti avvolgere i tuoi passaggi descritti in uno script / alias, anche se suggerirei di unire i comandi in &&modo che, nel caso in cui uno di essi fallisca, non proverà ad andare avanti.


2
Se dai un esempio di riga di comando, voterei. Ho questo problema su Github. Ho creato un ramo sull'interfaccia utente. Ora ho bisogno del mio locale per mostrare la filiale. git pull --all; git branch ... argh ... il comando: git branch -a
mariotti,

@mariotti Dipende da cosa stai cercando di fare e non è molto chiaro dal tuo commento. Potrebbe essere meglio fare una nuova domanda.
Cascabel,

1
Oppure @Jefromi .. fai un esempio. In realtà sono d'accordo con te.
Mariotti,

3
@mariotti Il punto di questa risposta è che i comandi integrati in realtà non fanno ciò che l'OP ha richiesto, quindi è necessaria la sequenza di passi che avevano. È possibile automatizzare questi passaggi (vedere ad esempio la risposta di John) ma devono essere eseguiti. Quindi, se quello che stai cercando di fare è esattamente lo stesso dell'OP, non c'è davvero un esempio da dare, e se stai cercando di fare qualcosa di diverso, allora dovresti fare una nuova domanda: ecco come funziona StackOverflow! (E il tuo commento non è chiaro, ma la mia ipotesi migliore è che tu voglia qualcosa di diverso dall'OP qui, quindi sì, nuova domanda.)
Cascabel,

Sì, qualcosa di diverso. Ma la tua risposta è stata semplicemente perfetta per il contesto. E potrei non aver più bisogno di chiedere solo per la tua risposta. Solo: la risposta accettata usa git-up, che è semplicemente un'interfaccia per la riga di comando di git (suppongo). Speravo che tu potessi renderlo esplicito in poche righe di comandi git. La risposta attuale NON è git.
Mariotti,

206

Uso il syncsottocomando di hub per automatizzare questo. Ho alias git=hubnel mio .bash_profile, quindi il comando che scrivo è:

git sync

Ciò aggiorna tutti i rami locali che hanno un ramo upstream corrispondente. Dalla pagina man:

  • Se la filiale locale non è aggiornata, avanzare rapidamente;
  • Se il ramo locale contiene lavori non compressi, avvisare;
  • Se il ramo sembra unito e il suo ramo a monte è stato eliminato, eliminarlo.

Gestisce anche modifiche non stashing / nonstashing sul ramo corrente.

Usavo uno strumento simile chiamato git-up , ma non è più mantenuto e git syncfa quasi esattamente la stessa cosa.


15
Che mi dici di Windows?
Violet Giraffe

6
Le date di commit di @ TrentonD.Adams e le date degli autori sono concetti diversi. Un rebase cambierà la data di commit ma non la data dell'autore (tranne nei conflitti, dove cambia anche la data dell'autore). La data dell'autore riflette quando è stato creato l'albero del commit e non dovrebbe cambiare durante un rebase non in conflitto. La data del commit cambia perché rebase crea sempre un nuovo commit. Quindi le date di commit saranno sempre nell'ordine corretto.
Dev

16
Per disattivare il comportamento di rebasing automatico di git-up, esegui git config --global git-up.rebase.auto false.
Dan Loewenherz,

18
@MaxYankov Rebasing della storia condivisa è generalmente da evitare, non c'è nulla di sbagliato nel riequilibrare gli commit locali durante un pull.
Dev

23
Reimpostare gli impegni locali è riscrivere la storia e renderla più semplice di quanto non sia in realtà. Con rebase, potresti ritrovarti con un codice che si è unito automaticamente ma non si compila, o peggio, si compila ma non funziona. L'unione riconosce il modo in cui hai lavorato: hai implementato le modifiche e le hai testate prima di incorporare le modifiche di altre persone, e unire il commit è un punto molto utile: è il luogo in cui ti assicuri che diversi chageset giochino bene insieme. Il rifacimento fa sembrare che questo processo non avvenga mai, il che non è semplicemente vero ed è una pratica molto pericolosa.
Max Yankov,

39

So che questa domanda ha quasi 3 anni, ma mi sono posto la stessa domanda e non ho trovato alcuna soluzione pronta. Quindi, ho creato uno script della shell dei comandi git personalizzato da solo.

Ecco qui, lo git-ffwd-updatescript fa il seguente ...

  1. rilascia un a git remote updateper recuperare i giri recenti
  2. quindi utilizza git remote showper ottenere un elenco di filiali locali che tengono traccia di una filiale remota (ad esempio filiali che possono essere utilizzate con git pull)
  3. quindi controlla con git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>quanti commit si trova la filiale locale dietro il telecomando (e avanti viceversa)
  4. se la filiale locale ha 1 o più commit in anticipo, NON può essere anticipata e deve essere unita o riformata a mano
  5. se la filiale locale è 0 si impegna in anticipo e 1 o più si impegna indietro, può essere fatta avanzare rapidamente di git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

lo script può essere chiamato come:

$ 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

Lo script completo, deve essere salvato come git-ffwd-updatee deve essere sul 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 $@

1
Grazie per questo script. È possibile che qualcuno possa convertire quello script in batch di Windows?
Saariko,

@Saariko perché non vuoi usare git su una normale shell di Windows? Se usi qualcosa come Cygwin questo script dovrebbe funzionare bene ... (anche se non l'ho provato)
muhqu,

@RyanWilcox grazie, lo sto usando come ogni giorno (di lavoro) ... ;-) potresti voler dare un'occhiata ai miei file dot per altri script e alias relativi a git: github.com/muhqu/dotfiles
muhqu,

@muhqu Sto cercando di usare la tua sceneggiatura e non so perché abbia funzionato la prima volta ma non funziona "come previsto" in questo momento. Ad esempio, dai un'occhiata a questo . Perché il master ha ancora 78 commit dopo che eseguo la tua sceneggiatura?
BPL,

1
@muhqu Nelle versioni git più recenti, -t e -l non devono essere usati insieme in una git branchchiamata. Ho rimosso il -l per cambiare la chiamata in git branch -f $LB -t $ARB >/dev/null;e ora lo script funziona come dovrebbe.
Radek Liska,

24

Non è così difficile automatizzare:

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

4
Probabilmente è meglio non usare gli alias negli script. Anche questo in realtà non recupera nulla, si limita a ripristinare il contenuto già recuperato. È necessario passare git rebase origin/$brancha git pull, in modo che venga recuperato dal ramo di tracciamento appropriato (presumibilmente sull'origine) e si fonda o si riformi come determinato dalla configurazione.
Cascabel,

@Jefromi: avevo dimenticato il fetch. Hanno modificato; funzioni / correzioni extra qualunque sia l'OP.
Fred Foo,

8
Penso ancora che potresti voler usare pull(o controllare branch.<branch>.rebase), in modo da non rifare accidentalmente un ramo che è impostato per tirare normalmente (unire).
Cascabel,

1
Prendi in considerazione l'utilizzo set -einvece di || exit 1rendere l'uscita dell'interprete al primo errore.
crishoj,

18

Questo non è ancora automatico, poiché vorrei che ci fosse un'opzione per - e ci dovrebbero essere alcuni controlli per assicurarsi che ciò possa avvenire solo per gli aggiornamenti di avanzamento rapido (motivo per cui eseguire manualmente un pull è molto più sicuro !!), ma a parte gli avvertimenti puoi:

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

per aggiornare la posizione della tua filiale locale senza doverla controllare.

Nota: perderai la posizione corrente del tuo ramo e la sposterai dove si trova il ramo dell'origine, il che significa che se devi unire perderai i dati!


1
Questa è esattamente la soluzione che stavo cercando. Di solito non ho cambiamenti non compressi su più rami e voglio solo aggiornare i miei vari rami locali per adattarli al telecomando. Questa soluzione è molto più bella del mio solito metodo di eliminazione / re-checkout!
Dave Knight,

1
combinati in un solo comando:git fetch origin other-branch:other-branch
fabb,

12

Ci sono molte risposte qui, ma nessuna che usa git-fetchper aggiornare direttamente il riferimento locale, che è molto più semplice che controllare i rami, e più sicura di git-update-ref.

Qui usiamo git-fetchper aggiornare i rami non correnti e git pull --ff-onlyper il ramo corrente. It:

  • Non richiede il controllo delle filiali
  • Aggiorna i rami solo se possono essere fatti avanzare rapidamente
  • Riferirà quando non è possibile avanzare rapidamente

ed eccolo qui:

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

Dalla manpage per 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.

Specificando git fetch <remote> <ref>:<ref>(senza alcuno +) otteniamo un recupero che aggiorna il riferimento locale solo quando può essere fatto avanzare rapidamente.

Nota : questo presuppone che i rami locali e remoti abbiano lo stesso nome (e che si desidera tenere traccia di tutti i rami), in realtà si dovrebbero usare le informazioni su quali rami locali si hanno e su cosa sono impostati per tenere traccia.


1
"Aggiorna i rami solo se possono essere fatti avanzare rapidamente" : qual è il significato dell'avanzamento rapido? Se voglio le ultime fonti in tutte le mie filiali, perché dovrei preoccuparmi di un avanzamento veloce o no? Sono cose come questa che mi fanno ridere di Git e dei suoi Fanboi. Non puoi farlo in un solo comando. Invece è necessario eseguire c*npassaggi (anziché 1), in cui cè presente un numero di comandi ripetuti ed nè il numero di rami.
JWW

@jww Non aiuta a "ridere di Git e dei suoi Fanboi" [sic] quando è il VCS che la maggior parte del mondo usa. Ma sto divagando ... Penso che nel contesto di questo tipo di script "pull globale" sia prudente non tentare di apportare modifiche ai rami non correnti se hanno conflitti di unione.
Ville,

Questo è stato utile, grazie. L'unica cosa che non mi piaceva era che ha creato una filiale a livello locale per ogni ramo remoto (compresi quelli in cui non mi interessa), così ho cambiato git branch -r | grep -v ' -> ' | while read remotebranchper git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchlimitarlo alle filiali che già ho a livello locale. Inoltre ho aggiunto git fetch --prunea all'inizio per aggiornare l'elenco dei rami remoti prima di fare qualsiasi cosa, il che evita alcuni avvisi.
Nate Cook,

11

Questo problema non è (ancora) risolto, almeno non facilmente / senza script: vedi questo post sulla mailing list git di Junio ​​C Hamano che spiega la situazione e fornisce una soluzione semplice.

Il ragionamento principale è che non dovresti averne bisogno:

Con git che non è antico (cioè v1.5.0 o più recente), non vi è alcun motivo per avere "dev" locali che seguano puramente il telecomando. Se vuoi solo andare a guardare e vedere, puoi controllare il ramo di tracciamento remoto direttamente su HEAD distaccato con " git checkout origin/dev".

Ciò significa che gli unici casi di cui abbiamo bisogno per renderlo conveniente per gli utenti sono gestire queste filiali locali che "tengono traccia" di quelle remote quando si hanno modifiche locali o quando si prevede di averne alcune.

Se hai delle modifiche locali su "dev" che è contrassegnato per tenere traccia della rimozione di "dev", e se ti trovi su un ramo diverso da "dev", non dovremmo fare nulla dopo " git fetch" aggiorna il "dev" di tracciamento remoto . Non avanzerà comunque comunque

La richiesta di una soluzione era quella di un'opzione o uno script esterno per eliminare le filiali locali che ora seguono filiali di tracciamento remoto, anziché tenerle aggiornate mediante avanzamento rapido, come richiesto dal poster originale.

Che ne dici di " git branch --prune --remote=<upstream>" che scorre sulle filiali locali e se

(1) non è il ramo corrente; e
(2) è contrassegnato per tracciare un ramo preso da <upstream>; e
(3) non ha alcun commit da solo;

quindi rimuovere quel ramo? " git remote --prune-local-forks <upstream>" va anche bene; Non mi interessa quale comando implementa la funzione così tanto.

Nota: da git 2.10 non esiste tale soluzione. Si noti che ilgit remote prunesottocomando egit fetch --pruneriguarda la rimozione del ramo di tracciamento remoto per il ramo che non esiste più sul telecomando, non la rimozione di un ramo locale che tiene traccia del ramo di tracciamento remoto (per il quale il ramo di tracciamento remoto è ramo a monte).


Piuttosto che pubblicare solo i collegamenti, si prega di pubblicare contenuti reali, utilizzando i collegamenti come riferimenti. Quel collegamento è ora morto. Peccato, sembrava promettente. (Mi rendo conto che questa risposta è stata del 2009, quindi questa è solo una nota per riferimento futuro.)
michael

grazie (e wow, risposta rapida dopo tanti anni). Ora vedo che questo thread è una " richiesta di una soluzione semplice" in contrapposizione al mio errore di lettura originale, " fornisce una soluzione semplice".
michael

@michael_n: espanso ... hmm, ora vedo che il post non era esattamente sulla soluzione richiesta, ma riguardava il problema (supponendo il caso del problema XY).
Jakub Narębski,

Hmm, sbirciare con la testa staccata dovrebbe essere reso più facile, in particolare dovrebbe mostrare informazioni utili sullo stato e consentire di avanzare rapidamente nell'area di lavoro con alcuni feedback (come i commit estratti). Quindi sarebbe un sostituto per le filiali locali di sola lettura.
Devo dire

9

Ci sono molte risposte accettabili qui, ma alcune delle tubature potrebbero essere un po 'opache per chi non lo sapesse. Ecco un esempio molto più semplice che può essere facilmente personalizzato:

$ 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 "$@"

Se aggiungi ~/bin/git al tuo PATH(supponendo che il file sia ~/bin/git/git-update-all), puoi semplicemente eseguire:

$ git update-all

Grazie! mi hai risparmiato un'ora di gioco con bash ...
8ctopus

5

Aggiungi questo script a .profilesu 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};
};

1
Questo non dovrebbe prima nascondere tutte le modifiche e poi ripristinarle?
Mel

5

Ecco una buona risposta: come recuperare tutti i rami git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all

Perché stai suggerendo di fare git fetche git pull, invece di limitarti a farlo git pull?
syntagma,

Grazie. Sembra che pull recuperi tutti i rami da tutti i telecomandi. Modificato
milkovsky,

8
Questo recupererà tutti i telecomandi, ma unirà solo il ramo corrente. Se disponi di 10 telecomandi, dovrai effettuare il checkout manuale di ognuno e unirli.
mpoisot,

In questo modo creerai tutte le filiali remote localmente con origin/prefisso
Yassine ElBadaoui,

3

Una sceneggiatura che ho scritto per il mio GitBash . Compie quanto segue:

  • Per impostazione predefinita, estrae dall'origine per tutti i rami impostati per tenere traccia dell'origine, se lo si desidera è possibile specificare un telecomando diverso.
  • Se il tuo ramo attuale è in uno stato sporco, interrompe le modifiche e tenterà di ripristinarle alla fine.
  • Per ogni filiale locale impostata per tenere traccia di una filiale remota:
    • git checkout branch
    • git pull origin
  • Infine, tornerai al ramo originale e ripristinerai lo stato.

** Lo uso ma non ho testato a fondo, uso a proprio rischio. Vedi un esempio di questo script in un file .bash_alias qui .

    # 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
    }

Potete fornire un file bat Windows equivalente?
Jaffy,

1
@Jaffy Non sono sicuro di quanto tempo ho a disposizione e non sono molto fluente in batch, ma posso provarlo. Pubblicherò i miei progressi qui , forse altri potranno intervenire e aiutare?
philosowaffle,

3

Se sei su Windows puoi usare PyGitUp che è un clone di git-upPython. Puoi installarlo usando pip con pip install --user git-upo tramite Scoop usandoscoop install git-up

[4]


3

Pubblicando solo una risposta aggiornata. git-upnon è più gestito e se leggi la documentazione, menzionano la funzionalità è ora disponibile in git .

A partire da Git 2.9, git pull --rebase --autostash fa sostanzialmente la stessa cosa.

Di conseguenza, se aggiorni a Git 2.9 o versioni successive, puoi usare questo alias invece di installare git-up:

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

Puoi anche impostarlo per ogni versione git pulldi Git 2.9 (grazie @VonC, vedi la sua risposta qui )

git config --global pull.rebase true
git config --global rebase.autoStash true

1
Non hai bisogno di un alias. A tirare semplice git è sufficiente, con la configurazione giusta: stackoverflow.com/a/40067353/6309
VonC

Grande chiamata grazie @VonC Ho aggiornato la mia risposta :) potrebbe anche inviare un PR alla git-updocumentazione perché non lo menzionano
agosto

Questo non aggiorna contemporaneamente tutte le filiali locali, motivo per cui l'ho usato principalmente git-up.
ray,

Documentation updated on git-up:)
Agosto

3

Mi sono imbattuto nello stesso numero di questa domanda ...

Mi chiedo, ho fatto una piccola funzione alias all'interno del mio .bashrcfile:

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

Ha funzionato per me (:


2

Se refs / heads / master può essere spostato rapidamente su refs / telecomandi / foo / master , l'output di

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

dovrebbe restituire l'id SHA1 a cui refs / head / master points. Con questo, puoi mettere insieme uno script che aggiorna automaticamente tutti i rami locali a cui non sono stati applicati commit di deviazione.

Questo piccolo script di shell (l'ho chiamato git-can-ff ) illustra come si può fare.

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

Cosa implica questo commento?
Hillu,

Io stesso non sono in grado di scrivere la sceneggiatura suggerita da hillu e non sono abbastanza sicuro della mia conoscenza git da usare git-merge-base.
Norman Ramsey,

2
Temo di non capire il modello abbastanza bene da sfruttare la sceneggiatura così gentilmente fornita. È abbastanza per indurre una persona a passare al mercurcial.
Norman Ramsey,

Personalmente ho trovato l'articolo di Tommi Virtanen "Git per informatici" molto utile per acquisire familiarità con il modello e la terminologia di Git.
hillu,

2

Per completare la risposta di Matt Connolly, questo è un modo più sicuro per aggiornare i riferimenti di filiali locali che possono essere fatti avanzare rapidamente, senza controllare la filiale. Non aggiorna i rami che non possono essere fatti avanzare rapidamente (cioè che sono divergenti) e non aggiorna il ramo che è attualmente estratto (perché anche la copia di lavoro dovrebbe essere aggiornata).

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

2

Uno script leggermente diverso che si dirama rapidamente solo i nomi dei nomi corrispondono al loro ramo a monte. Aggiorna anche il ramo corrente se è possibile l'avanzamento rapido.

Assicurati che tutti i rami a monte dei tuoi rami siano impostati correttamente eseguendo git branch -vv. Impostare il ramo a monte congit branch -u origin/yourbanchname

Copia e incolla in un file e 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;

2

Il seguente one-liner avanza rapidamente tutti i rami che hanno un ramo a monte, se possibile, e stampa un errore altrimenti:

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

Come funziona?

Utilizza un formato personalizzato con il git branchcomando. Per ogni ramo che ha un ramo a monte, stampa una linea con il modello seguente:

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

Questo può essere convogliato direttamente in sh(supponendo che i nomi dei rami siano ben formati). Ometti il| sh per vedere cosa sta facendo.

Avvertenze

Il one-liner non contatterà i telecomandi. Emettere un git fetcho git fetch --allprima di eseguirlo.

Il ramo attualmente estratto non verrà aggiornato con un messaggio simile

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

Per questo, puoi ricorrere a regolari git pull --ff-only.

Alias

Aggiungi quanto segue al tuo in .gitconfigmodo che git fftesegua questo comando:

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

Vedi anche il mio .gitconfig. L'alias è una scorciatoia per "tracciamento rapido (rami)".


Questa è una buona soluzione, anche se penso che userò la hubsoluzione proposta da @John per un output migliore.
Didier L

Questo è veloce, semplice e funziona davvero! Sono sconcertato dal fatto git pushche è totalmente opposto a quello che ti aspetteresti. Qual è il segreto?
BrandonL White,

@BrandonLWhite: non capisco la domanda. Cosa ti aspetti git push?
krlmlr

git pushha una semantica di upload - Ho alcuni commit a livello locale che voglio inviare a monte. git pullha una semantica di download - Voglio ottenere alcuni commit remoti a monte nella mia filiale locale. Dal momento che stiamo parlando di scaricare nuovi commit dal telecomando al locale, git pullè la scelta ovvia. Ma no, questo trucco usa git push. In che modo git pushrisulta apportare modifiche remote alla mia filiale locale ?!
BrandonLWhite

git pushpuò essere utilizzato anche per aggiornare le filiali locali, purché si tratti di un aggiornamento rapido.
krlmlr

1

Lo script di @larsmans è leggermente migliorato:

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

Questo, al termine, lascia la copia di lavoro estratta dallo stesso ramo in cui era prima che lo script fosse chiamato.

La git pullversione:

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

1

Sembra che molti altri abbiano contribuito con soluzioni simili, ma ho pensato di condividere ciò che mi è venuto in mente e di invitare altri a contribuire. Questa soluzione ha un output colorato, gestisce con grazia la tua directory di lavoro corrente ed è veloce perché non esegue alcun checkout e lascia intatta la tua directory di lavoro. Inoltre, è solo uno script di shell senza dipendenze oltre a git. (finora testato solo su OSX)

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

Mi dispiace anche che mi sia venuta in mente lo stesso nome dell'altro strumento sopra.


2
Sei tu quello che ha scritto questo? In tal caso, ti preghiamo di rivelare la tua affiliazione, ovvero di dirci come sei collegato ad essa. Si prega di leggere di più su questo per ulteriori informazioni. In particolare, non dirlo: mostra! ; Dicci quali parti della tua sceneggiatura e come / perché risolve il problema.
Keale,

1
Sì, l'ho scritto. Ho incluso la fonte sopra per un rapido copia-incolla nel tuo .bashrc o .zshrc.
Stimp

Questa è una buona soluzione e funziona bene. Nessuno se n'era accorto?
Ville,

1

Può essere fatto usando lo script qui sotto ... Prima recupererà tutti i rami e verificherà uno per uno e si aggiornerà da solo.

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

Aggiungi una spiegazione per ottenere il punteggio della tua risposta
idiota-dev

1

Non puoi farlo con un solo comando git ma puoi automatizzarlo con una riga bash.

Per aggiornare in sicurezza tutti i rami con una riga, ecco cosa faccio:

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
  • Se non è possibile avanzare rapidamente di un ramo o riscontrare un errore, si arresterà e ti lascerà in quel ramo in modo da poter riprendere il controllo e unirli manualmente.

  • Se tutti i rami possono essere fatti avanzare rapidamente, finirà con il ramo in cui ti trovavi attualmente, lasciandoti dove eri prima di aggiornare.

spiegazioni:

Per una migliore leggibilità, può essere suddiviso su più righe:

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 && ... => Recupera tutti i riferimenti da tutti i telecomandi e continua con il comando successivo se non si sono verificati errori.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Dall'output di git branch, sedprendi la linea con a *e spostala alla fine (in modo che il ramo corrente venga aggiornato per ultimo). Quindi trrimuovere semplicemente il *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Per ogni nome di ramo ottenuto dal comando precedente, controlla questo ramo e prova a unire con un avanzamento veloce. Se fallisce, breakviene chiamato e il comando si ferma qui.

Certo, puoi sostituirlo git merge --ff-onlycon git rebasese è quello che vuoi.

Infine, puoi inserirlo nel tuo bashrc come alias:

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'

O se hai paura di fare casini con 'e ", o semplicemente preferisci mantenere la leggibilità sintattica nel tuo editor, puoi dichiararlo come una funzione:

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:

Per coloro a cui piacerebbe la spiegazione da sed '/*/{$q;h;d};$G'parte:

  • /*/=> Cerca la riga con a *.

  • {$q => Se è nell'ultima riga, esci (non è necessario fare nulla perché il ramo corrente è già l'ultimo nell'elenco).

  • ;h;d} => In caso contrario, memorizzare la riga nel buffer di mantenimento ed eliminarla nella posizione corrente dell'elenco.

  • ;$G => Quando raggiunge l'ultima riga, aggiungi il contenuto del buffer di conservazione.


Puoi evitare tutta la follia di linee infinite e &&impostando set -enella parte superiore della sceneggiatura.
martedì

0

"Git pull --all" può aggiornare tutte le mie filiali locali?

No non può. Per l'avanzamento veloce, ho appena scritto un piccolo strumento per farlo. https://github.com/changyuheng/git-fast-forward-all

Vantaggi di questo strumento:

  1. Supporta più telecomandi in un repository. ( hub syncnon supporta più telecomandi al momento.)
  2. Supporta avere nomi diversi sulla filiale locale e sulla branche di tracciamento remoto corrispondente.
  3. Molto più veloce di altri script che recupera in remoto per ogni singolo ramo.
  4. Nessuna analisi / modifica regex soggetta a errori.

1
Puoi evitare di colpire la rete usando git fetch . refspec. Il .dice di recuperare dal repository corrente invece che da quello remoto.
hugomg

-1

A partire da git 2.9:

git pull --rebase --autostash

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

Crea automaticamente una scorta temporanea prima dell'inizio dell'operazione e applicala al termine dell'operazione. Ciò significa che è possibile eseguire rebase su un worktree sporco. Tuttavia, usare con cautela: l'applicazione di stash finale dopo un rebase riuscito potrebbe causare conflitti non banali.


-1

In effetti, con git version 1.8.3.1, funziona:

[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

Nel ramo master è possibile aggiornare tutti gli altri rami. @Cascabel

Non so quale versione romperlo / ripararlo, in 2.17 (che io uso), può funzionare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.