Può “git pull –all” aggiornare tutte le mie filiali locali?

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

L’aggiornamento di 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 essere in grado di fare solo un “git pull -all”, ma non sono stato in grado di farlo funzionare. Sembra fare un “fetch –all”, quindi aggiorna (avanza velocemente o fonde) il ramo di lavoro corrente, ma non gli altri rami locali.

Sono ancora bloccato manualmente passando a ciascuna filiale locale e aggiornando.

Il comportamento che descrivi per pull --all è esattamente come previsto, sebbene non sia necessariamente utile. L’opzione viene passata a git fetch, che recupera tutti i riferimenti da tutti i telecomandi, invece di quello necessario; pull quindi unisce (o nel tuo caso, ribalta) il ramo singolo appropriato.

Se vuoi controllare altri rami, dovrai controllarli. E sì, la fusione (e la ridefinizione) richiedono assolutamente un albero di lavoro, quindi non possono essere eseguiti senza controllare gli altri rami. Potresti concludere i passaggi descritti in uno script / alias, se preferisci, anche se suggerirei di unire i comandi con && modo che se uno di essi dovesse fallire, non tenterebbe di farlo.

Io uso il sottocomando sync dell’hub per automatizzarlo. L’hub è con alias come git , quindi il comando che scrivo è:

 git sync 

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

  • Se il ramo locale è obsoleto, avanza rapidamente;
  • Se il ramo locale contiene un lavoro non spinto, avvisalo;
  • Se il ramo sembra unificato e il suo ramo a monte è stato cancellato, cancellalo.

Gestisce anche la memorizzazione / rimozione di modifiche non impegnate sul ramo corrente.

Ho usato uno strumento simile chiamato git-up , ma non è più mantenuto, e git sync fa quasi la stessa cosa.

Nota: anche se ho pubblicato la mia soluzione, raccomanderei l’uso di git-up , che è anche la risposta accettata.


So che questa domanda ha quasi 3 anni, ma mi sono posta la stessa domanda e non ho trovato nessuna soluzione pronta. Così, ho creato uno script personalizzato per la shell di comando git.

Ecco qui, lo script git-ffwd-update fa il seguente …

  1. rilascia un git remote update per recuperare i giri rev
  2. quindi usa git remote show per ottenere un elenco di rami locali che tracciano un ramo remoto (es. rami che possono essere usati con git pull )
  3. poi controlla con git rev-list --count .. quanti commit il ramo locale è dietro il remoto (e avanti viceversa)
  4. se il ramo locale è pari a 1 o più, non può essere inoltrato rapidamente e deve essere unito o ribasato a mano
  5. se il ramo locale è 0 commette in avanti e uno o più commit dietro, può essere inoltrato rapidamente da git branch -l -f -t

lo script può essere chiamato come:

 $ git ffwd-update Fetching origin branch bigcouch was 10 commit(s) behind of origin/bigcouch. reseting local branch to remote branch develop was 3 commit(s) behind of origin/develop. reseting 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-update e deve essere sul PATH .

 #!/bin/bash main() { REMOTES="[email protected]"; if [ -z "$REMOTES" ]; then REMOTES=$(git remote); fi REMOTES=$(echo "$REMOTES" | xargs -n1 echo) CLB=$(git branch -l|awk '/^\*/{print $2}'); echo "$REMOTES" | while read REMOTE; do git remote update $REMOTE git remote show $REMOTE -n \ | awk '/merges with remote/{print $5" "$1}' \ | while read line; do RB=$(echo "$line"|cut -f1 -d" "); ARB="refs/remotes/$REMOTE/$RB"; LB=$(echo "$line"|cut -f2 -d" "); 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. reseting local branch to remote"; git branch -l -f $LB -t $ARB >/dev/null; fi fi done done } main [email protected] 

Non è così difficile da automatizzare:

 #!/bin/sh # Usage: fetchall.sh branch ... set -x git fetch --all for branch in "[email protected]"; do git checkout "$branch" || exit 1 git rebase "origin/$branch" || exit 1 done 

Questo non è ancora automatico, come vorrei che ci fosse un’opzione per – e ci dovrebbe essere qualche controllo per assicurarsi che questo possa accadere solo per gli aggiornamenti rapidi (ecco perché fare manualmente un pull è molto più sicuro !!), ma a parte questo puoi:

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

per aggiornare la posizione della filiale locale senza doverlo verificare.

Nota: perderai la posizione attuale della tua diramazione e la sposterai dove si trova il ramo di origine, il che significa che se hai bisogno di unire perderai i tuoi dati!

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

Il principale ragionamento è che non dovresti aver bisogno di questo:

Con git che non è antico (cioè v1.5.0 o più recente), non vi è alcun motivo per avere “dev” locale che segua semplicemente il telecomando. Se vuoi solo andare a vedere e vedere, puoi controllare il ramo di monitoraggio remoto direttamente su un HEAD distaccato con ” git checkout origin/dev “.

Ciò significa che gli unici casi che dobbiamo rendere conveniente per gli utenti sono gestire questi rami locali che “tracciano” quelli remoti quando si hanno delle modifiche locali o quando si pianifica di averne.

Se hai modifiche locali su “dev” che è contrassegnato per tenere traccia della rimozione “dev”, e se sei su un ramo diverso da “dev”, allora non dovremmo fare nulla dopo che ” git fetch ” aggiorna il tracciamento remoto ” dev”. In ogni caso, non avanza velocemente

La richiesta di una soluzione prevedeva un’opzione o uno script esterno per eliminare i rami locali che seguono ora i rami di localizzazione remota, piuttosto che tenerli aggiornati tramite l’inoltro veloce, come richiesto dal poster originale.

Che ne dici di ” git branch --prune --remote= ” che scorre su rami locali, e se

(1) non è il ramo attuale; e
(2) è contrassegnato per tracciare qualche ramo preso da ; e
(3) non ha alcun commit da solo;

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

Nota: a partire da git 2.10 non esiste una soluzione del genere. Si noti che il sottocomando git remote prune e git fetch --prune riguardano la rimozione del ramo di tracciamento remoto per ramo che non esiste più sul remoto, non sulla rimozione del ramo locale che tiene traccia del ramo di tracciamento remoto (per il quale il ramo di localizzazione remota è a monte ramo).

Ci sono molte risposte qui ma nessuna che usa git-fetch per aggiornare direttamente il ref locale, che è molto più semplice del check-out dei rami e più sicuro di git-update-ref .

Qui usiamo git-fetch per aggiornare i rami non correnti e git pull --ff-only per il ramo corrente. It:

  • Non richiede il controllo di rami
  • Aggiorna i rami solo se possono essere inoltrati rapidamente
  • Segnalerà quando non può avanzare velocemente

ed eccolo qui:

 #!/bin/bash currentbranchref="$(git symbolic-ref HEAD 2>&-)" git branch -r | grep -v ' -> ' | while read remotebranch do # Split / 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 pagina di manuale di git-fetch :

   The format of a  parameter is an optional plus +, followed by the source ref , followed by a colon :, followed by the destination ref . The remote ref that matches  is fetched, and if  is not empty string, the local ref that matches it is fast-forwarded using . 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 : (senza alcun + ) otteniamo un fetch che aggiorna il ref locale solo quando può essere inoltrato rapidamente.

Nota : questo presuppone che i rami locali e remoti abbiano lo stesso nome (e che tu voglia tenere traccia di tutti i rami), in realtà dovrebbe usare le informazioni su quali rami locali hai e su cosa sono impostati per tracciare.

Ci sono molte risposte accettabili qui, ma alcune delle tubature potrebbero essere un po ‘opache per i non iniziati. 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 "[email protected]" 

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

 $ git update-all 

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 

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

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

  • Per impostazione predefinita, le derivazioni dall’origine per tutti i rami impostati per tracciare l’origine consentono di specificare un telecomando diverso se lo si desidera.
  • Se il tuo ramo attuale è in uno stato sporco, esso nasconde le tue modifiche e tenterà di ripristinare queste modifiche alla fine.
  • Per ogni ramo locale impostato per tracciare un ramo remoto:
    • git checkout branch
    • git pull origin
  • Infine, tornerai al tuo ramo originale e ripristinerai lo stato.

** Io uso questo ma non ho testato a fondo, utilizzare 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 } 

Se sei su Windows puoi usare PyGitUp che è un clone di git-up per Python. Puoi installarlo usando pip con pip install --user git-up o tramite Scoop usando scoop install git-up

[ 4]

Pubblicando semplicemente una risposta aggiornata. git-up non è più mantenuto e se leggi la documentazione, loro menzionano la funzionalità è ora disponibile in git .

A partire da Git 2.9, git pull –rebase –autostash fa praticamente la stessa cosa.

Di conseguenza, se si aggiorna a Git 2.9 o versioni successive, è ansible utilizzare questo alias anziché installare git-up:

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

Puoi anche impostarlo per ogni git pull partire da Git 2.9 (grazie a @VonC vedi la sua risposta qui )

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

Se refs / heads / master possono essere inoltrati rapidamente a refs / remotes / foo / master , l’output di

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

dovrebbe restituire l’ID SHA1 a cui fa riferimento ref / heads / master . Con questo, è ansible creare uno script che aggiorni 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 può essere fatto.

 #!/bin/sh set -x usage() { echo "usage: $(basename $0)  " >&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 

Per completare la risposta di Matt Connolly, questo è un modo più sicuro per aggiornare i riferimenti alle filiali locali che possono essere inoltrati rapidamente, senza verificare il ramo. Non aggiorna i rami che non possono essere inoltrati rapidamente (ovvero che sono stati divergenti) e non aggiorna il ramo 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 

Lo script di @larsmans, un po ‘migliorato:

 #!/bin/sh set -x CURRENT=`git rev-parse --abbrev-ref HEAD` git fetch --all for branch in "[email protected]"; 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 come era prima che venisse chiamato lo script.

La versione git pull :

 #!/bin/sh set -x CURRENT=`git rev-parse --abbrev-ref HEAD` git fetch --all for branch in "[email protected]"; 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 

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

 #!/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 [email protected]{u}&>/dev/null; then REMOTE=$(git rev-parse --quiet --verify [email protected]{u}) BASE=$(git merge-base $branch [email protected]{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

Spiacente, mi sembra che sia venuto fuori lo stesso nome dell’altro strumento sopra.

Può essere fatto usando sotto lo script … Prima recupererà tutti i rami e verificherà uno per uno e 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 

Uno script leggermente diverso che solo i rami in rapida successione i cui nomi corrispondono al loro ramo upstream. Aggiorna anche il ramo corrente se è ansible l’avanzamento rapido.

Assicurati che tutti i rami upstream dei tuoi rami siano impostati correttamente eseguendo git branch -vv . Imposta il ramo upstream con git branch -u origin/yourbanchname

Copia-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; 

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 è ansible eseguire rebase su un worktree sporco. Tuttavia, utilizzare con caucanvas: l’applicazione di stash finale dopo un rebase di successo potrebbe causare conflitti non banali.