¿Puede "git pull --all" actualizar todas mis sucursales locales?


473

A menudo tengo al menos 3 sucursales remotas: maestra, puesta en escena y producción. Tengo 3 sucursales locales que rastrean esas sucursales remotas.

Actualizar todas mis sucursales locales es tedioso:

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

Me encantaría poder hacer un "git pull-all", pero no he podido hacerlo funcionar. Parece hacer una "búsqueda --todos", luego actualiza (adelanta o combina) la rama de trabajo actual, pero no las otras ramas locales.

Todavía estoy atascado cambiando manualmente a cada rama local y actualizando.


8
¿Desea actualizar automáticamente las sucursales de rastreo locales solo en caso de avance rápido? Deberías, porque la fusión puede tener un conflicto que deberías resolver ...
Jakub Narębski

34
Suponiendo un conservador tiempo de consultoría de $ 300 para jugar con esto, este único problema ha costado a las empresas $ 23,242,800 con un recuento de 77,476. Ahora considere esta pregunta stackoverflow.com/questions/179123/… y todas las demás. Guau.
Luke Puplett

16
@Luke Eres la primera persona que escucho señalar cómo el tiempo dedicado a hacer que git haga lo que queremos cuesta dinero a las empresas. Estas cosas simples deberían ser automáticas y deberían ser tan simples que no tenga que abrir un navegador para leer los foros, en mi opinión.
Samuel

13
@LukePuplett Hay casi ~ 9 veces más preguntas sobre git en SO en comparación con Mercurial, y la mayoría de las primeras parecen ser "¿cómo hago <operación simple> en git?". Eso indica que git está mal diseñado, mal documentado, poco intuitivo o los tres.
Ian Kemp

26
@IanKemp No estoy seguro de que sea seguro hacer esa afirmación sin conocer la demografía de SO. Si Mercurial no se usa tan comúnmente aquí, o si sus usuarios usan otros foros para preguntar al respecto, esperaría ver el mismo resultado. :) Hay ~ 51 veces más preguntas en Javascript en comparación con Assembly, por lo que no siempre es preciso juzgar las herramientas solo por este tipo de métricas.
danShumway

Respuestas:


188

El comportamiento que describe pull --alles exactamente el esperado, aunque no necesariamente útil. La opción se pasa a git fetch, que luego obtiene todas las referencias de todos los controles remotos, en lugar de solo la necesaria; pullluego fusiona (o en su caso, vuelve a crear) la rama individual apropiada.

Si desea ver otras sucursales, tendrá que revisarlas. Y sí, la fusión (y el rebase) requieren absolutamente un árbol de trabajo, por lo que no se pueden hacer sin verificar las otras ramas. Si lo desea, puede resumir sus pasos descritos en un guión / alias, aunque sugeriría unir los comandos &&para que, en caso de que uno de ellos falle, no intente continuar.


2
Si da una línea de comando de ejemplo, votaría. Tengo este problema en github. Creé una rama en la interfaz de usuario. Ahora necesito mi local para mostrar la sucursal. git pull --todos; git branch ... argh ... el comando: git branch -a
mariotti

@mariotti Depende de lo que intente hacer, y su comentario no lo aclara realmente. Es mejor que hagas una nueva pregunta.
Cascabel

1
O @Jefromi .. da un ejemplo. De hecho estuve de acuerdo contigo.
mariotti el

3
@mariotti El objetivo de esta respuesta es que los comandos integrados no hacen realmente lo que el OP solicitó, por lo que la secuencia de pasos que tenían es necesaria. Es posible automatizar esos pasos (ver, por ejemplo, la respuesta de John) pero deben hacerse. Entonces, si lo que está tratando de hacer es exactamente lo mismo que el OP, no hay realmente un ejemplo para dar, y si está tratando de hacer algo diferente, entonces debe hacer una nueva pregunta: ¡así es como funciona StackOverflow! (Y su comentario no está claro, pero mi mejor suposición es que quiere algo diferente del OP aquí, así que sí, una nueva pregunta).
Cascabel

Sí, algo algo diferente. Pero tu respuesta fue perfecta para el contexto. Y es posible que ya no necesite preguntar solo por su respuesta. Simplemente: la respuesta aceptada usa git-up, que es simplemente una interfaz para la línea de comando git (supongo). Esperaba que pudieras hacerlo explícito en pocas líneas de comandos git. La respuesta actual NO es git.
mariotti

206

Utilizo el syncsubcomando de hub para automatizar esto. Tengo alias git=huben mi .bash_profile, así que el comando que escribo es:

git sync

Esto actualiza todas las sucursales locales que tienen una rama ascendente coincidente. Desde la página del manual:

  • Si la sucursal local está desactualizada, avance rápidamente;
  • Si la sucursal local contiene trabajo no apresurado, advierta al respecto;
  • Si la rama parece fusionada y se eliminó su rama ascendente, elimínela.

También maneja los cambios no confirmados de ocultación / desactivación en la rama actual.

Solía ​​usar una herramienta similar llamada git-up , pero ya no se mantiene y git synchace casi exactamente lo mismo.


15
¿Qué hay de Windows?
Violet Giraffe

66
@ TrentonD. Las fechas de confirmación de Adams y las fechas de autor son conceptos diferentes. Una nueva versión cambiará la fecha de confirmación pero no la fecha del autor (excepto en conflictos, donde la fecha del autor también cambia). La fecha del autor refleja cuándo se creó el árbol commit y no debe cambiar durante un rebase sin conflicto. La fecha de confirmación cambia porque rebase siempre crea una nueva confirmación. Por lo tanto, las fechas de confirmación siempre estarán en el orden correcto.
Dev

16
Para desactivar el comportamiento de rebase automático de git-up, ejecute git config --global git-up.rebase.auto false.
Dan Loewenherz

18
@MaxYankov Rebasar el historial compartido generalmente se debe evitar, no hay nada de malo en rebajar los compromisos locales durante un tirón.
Dev

23
Rebasar los commits locales es reescribir el historial y hacerlo más simple de lo que realmente es. Con rebase, puede encontrarse con un código que se fusionó automáticamente pero que no compila, o peor aún, compila pero no funciona. La fusión reconoce la forma en que trabajó: implementó los cambios y los probó antes de incorporar los cambios de otras personas, y fusionar commit es un punto muy útil: ese es el lugar donde se asegura de que los diferentes chagesets jueguen bien juntos. Rebasar hace que parezca que este proceso nunca sucede, lo cual simplemente no es cierto y es una práctica muy peligrosa.
Max Yankov

39

Sé que esta pregunta tiene casi 3 años, pero me hice la misma pregunta y no encontré ninguna solución preparada. Entonces, creé un script de shell de comando git personalizado.

Aquí va, el git-ffwd-updatescript hace lo siguiente ...

  1. emite una git remote updatepara buscar las últimas revoluciones
  2. luego se usa git remote showpara obtener una lista de sucursales locales que rastrean una sucursal remota (por ejemplo, sucursales que se pueden usar con git pull)
  3. luego comprueba con git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>cuántos commit está la rama local detrás del remoto (y viceversa)
  4. si la sucursal local tiene 1 o más confirmaciones por delante, NO se puede reenviar rápidamente y debe fusionarse o rehacerse manualmente
  5. si la rama local tiene 0 confirmaciones por delante y 1 o más confirmaciones por detrás, puede reenviarse rápidamente mediante git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

el script se puede llamar así:

$ 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

El script completo debe guardarse como git-ffwd-updatey debe estar en el 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
Gracias por este guion. ¿Es posible que alguien pueda convertir ese script a lote de Windows?
Saariko

@Saariko, ¿por qué no quieres usar git en un shell normal de Windows? Si usa algo como cygwin, este script debería funcionar bien ... (aunque no lo he probado)
muhqu

@RyanWilcox gracias, lo estoy usando como todos los días (de trabajo) ... ;-) es posible que desee echar un vistazo a mis archivos de puntos para obtener más scripts y alias relacionados con git: github.com/muhqu/dotfiles
muhqu

@muhqu Estoy tratando de usar su script y no sé por qué funcionó la primera vez, pero no funciona "como se esperaba" en este momento. Por ejemplo, eche un vistazo a esto . ¿Por qué el maestro todavía tiene 78 commits detrás después de ejecutar su script?
BPL

1
@muhqu En las versiones más nuevas de git, -t y -l no se deben usar juntas dentro de una git branchllamada. Eliminé -l para cambiar la llamada git branch -f $LB -t $ARB >/dev/null;y ahora el script funciona como debería.
Radek Liska

24

No es tan difícil automatizar:

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

44
Probablemente sea mejor no usar alias en los scripts. En realidad, esto tampoco busca nada, solo vuelve a crear el contenido ya obtenido. Debe cambiar git rebase origin/$brancha git pull, de modo que se obtenga de la rama de seguimiento adecuada (presumiblemente en el origen) y se combine o rebase según lo determine la configuración.
Cascabel

@Jefromi: me había olvidado de la fetch. Han editado; características / correcciones adicionales, lo que sea que dependa del OP.
Fred Foo

8
Todavía creo que es posible que desee usar pull(o verificar branch.<branch>.rebase), para no volver a crear accidentalmente una rama que está configurada para extraer normalmente (fusionar).
Cascabel

1
Considere usar en set -elugar de || exit 1hacer que el intérprete salga en el primer error.
crishoj

18

Esto todavía no es automático, ya que desearía que hubiera una opción, y debería haber alguna verificación para asegurarse de que esto solo pueda suceder para las actualizaciones de avance rápido (¡¡por eso es que hacer un tirón manualmente es mucho más seguro !!), pero aparte de las advertencias puedes:

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

para actualizar la posición de su sucursal local sin tener que verificarlo.

Nota: perderá su posición de sucursal actual y la moverá a donde está la sucursal del origen, lo que significa que si necesita fusionarse, ¡perderá datos!


1
Esta es exactamente la solución que estaba buscando. Por lo general, no tengo cambios imprevistos en varias sucursales, y solo quiero actualizar mis diversas sucursales locales para que coincidan con el control remoto. ¡Esta solución es mucho mejor que mi método habitual de borrar / volver a pagar!
Dave Knight

1
combinado en un comando:git fetch origin other-branch:other-branch
fabb

12

Aquí hay muchas respuestas, pero ninguna que se use git-fetchpara actualizar el árbitro local directamente, lo cual es mucho más simple que retirar sucursales y más seguro quegit-update-ref .

Aquí usamos git-fetchpara actualizar ramas no actuales y git pull --ff-onlypara la rama actual. Eso:

  • No requiere retirar sucursales
  • Actualiza las sucursales solo si pueden reenviarse rápidamente
  • Informará cuando no pueda avanzar rápidamente

Y aquí está:

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

Desde la página de manual para 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.

Al especificar git fetch <remote> <ref>:<ref>(sin ningún+ ) obtenemos una búsqueda que actualiza la referencia local solo cuando se puede reenviar rápidamente.

Nota : esto supone que las ramas locales y remotas tienen el mismo nombre (y que desea rastrear todas las ramas), realmente debería usar información sobre qué ramas locales tiene y qué están configuradas para rastrear.


1
"Actualiza las ramas solo si pueden reenviarse rápidamente" : ¿cuál es el significado del avance rápido? Si quiero las últimas fuentes en todas mis sucursales, ¿por qué debería importarme el reenvío rápido o no? Son cosas como esta que me hacen reír de Git y sus Fanboi. No puede hacer esto simplemente con un comando. En su lugar, debe realizar c*npasos (en lugar de 1), donde chay un número de comandos repetidos y nes el número de ramas.
jww

@jww No ayuda "reírse de Git y su Fanboi's" [sic] cuando es el VCS que usa la mayoría del mundo. Pero estoy divagando ... Creo que en el contexto de este tipo de script de "atracción global" es prudente no intentar hacer cambios en las ramas no actuales si tienen conflictos de fusión.
Ville

Esto fue útil, gracias. Lo único que no me gustó fue que se creó una rama local de cada rama remota (incluyendo aquellos en los que no me interesa), así que cambié git branch -r | grep -v ' -> ' | while read remotebrancha git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchlimitarlo a las ramas que ya tengo localmente. También agregué un git fetch --pruneal principio para actualizar la lista de ramas remotas antes de hacer algo, lo que evita algunas advertencias.
Nate Cook

11

Este problema no está resuelto (todavía), al menos no fácilmente / sin secuencias de comandos: vea esta publicación en la lista de correo git de Junio ​​C Hamano explicando la situación y solicitando una solución simple.

El razonamiento principal es que no debería necesitar esto:

Con git que no es antiguo (es decir, v1.5.0 o más reciente), no hay ninguna razón para tener un "dev" local que solo rastree el control remoto. Si solo desea mirar y ver, puede consultar la rama de seguimiento remoto directamente en un HEAD separado con " git checkout origin/dev".

Lo que significa que los únicos casos que necesitamos para que sea conveniente para los usuarios son manejar estas sucursales locales que "rastrean" las remotas cuando tiene cambios locales, o cuando planea tener algunos.

Si tiene cambios locales en "dev" que están marcados para rastrear la eliminación "dev", y si está en una rama diferente de "dev", entonces no deberíamos hacer nada después de " git fetch" actualiza el seguimiento remoto "dev" . No avanzará de todos modos

La llamada para una solución era para una opción o script externo para podar las sucursales locales que siguen ahora las sucursales de seguimiento remoto, en lugar de mantenerlas actualizadas mediante el avance rápido, como se solicitó en el póster original.

Entonces, ¿qué tal " git branch --prune --remote=<upstream>" que itera sobre las ramas locales, y si

(1) no es la rama actual; y
(2) está marcado para rastrear alguna rama tomada de <upstream>; y
(3) no tiene ningún compromiso por sí solo;

entonces quitar esa rama? " git remote --prune-local-forks <upstream>" también está bien; No me importa qué comando implemente tanto la función.

Nota: a partir de git 2.10 no existe tal solución. Tenga en cuenta que elgit remote prunesubcomando, ygit fetch --prunese trata de eliminar la bifurcación de seguimiento remoto para la bifurcación que ya no existe en la remota, no se trata de eliminar la bifurcación local que rastrea la bifurcación de seguimiento remoto (para la cual la bifurcación de seguimiento remoto es la bifurcación ascendente).


En lugar de sólo enlaces publicación de anuncios, por favor, publicar contenido real, utilizando enlaces como referencias. Ese enlace ahora está muerto. Lástima, sonaba prometedor. (Me doy cuenta de que esta respuesta era a partir de 2009, así que esto es sólo una nota para futuras referencias.)
Michael

gracias (y wow, respuesta rápida después de tantos años). Ahora veo que este hilo es un " llamado a una solución simple" en lugar de mi lectura errónea original, " proporciona una solución simple".
michael

@michael_n: expandido ... hmm, ahora veo que la publicación no se refería exactamente a la solución solicitada, sino al problema (suponiendo que el problema sea XY).
Jakub Narębski

Hmm, echar un vistazo con la cabeza separada debería hacerse más fácil, especialmente debería mostrar información útil sobre el estado y permitir avanzar rápidamente el espacio de trabajo con algunos comentarios (como los commits extraídos). Entonces sería un reemplazo para las sucursales locales de solo lectura.
eckes

9

Aquí hay muchas respuestas aceptables, pero algunas de las tuberías pueden ser un poco opacas para los no iniciados. Aquí hay un ejemplo mucho más simple que se puede personalizar fácilmente:

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

Si agrega ~/bin/gita su PATH(suponiendo que el archivo es ~/bin/git/git-update-all), puede ejecutar:

$ git update-all

¡Gracias! me
ahorraste

5

Agregue este script a .profileen 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
¿No debería esto esconder todos los cambios primero y luego restaurarlos?
Mel

5

Aquí hay una buena respuesta: cómo obtener todas las ramas git

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

¿Por qué sugieres hacer git fetchy git pull, en lugar de solo git pull?
sintagma

Gracias. Parece que pull obtiene todas las ramas de todos los controles remotos. Lo cambió
milkovsky

8
Esto buscará todos los controles remotos, pero solo fusionará la rama actual. Si tiene 10 controles remotos, deberá verificar manualmente cada uno y fusionarlos.
mpoisot

Hacer esto creará todas las ramas remotas localmente con origin/prefijo
Yassine ElBadaoui

3

Un guión que escribí para mi GitBash . Logra lo siguiente:

  • De manera predeterminada, el origen de todas las ramas que están configuradas para rastrear el origen le permite especificar un control remoto diferente si lo desea.
  • Si su rama actual está en un estado sucio, oculta sus cambios e intentará restaurarlos al final.
  • Para cada sucursal local que esté configurada para rastrear una sucursal remota:
    • git checkout branch
    • git pull origin
  • Finalmente, lo regresará a su rama original y restaurará el estado.

** Lo uso pero no lo he probado completamente, uso bajo su propio riesgo. Vea un ejemplo de este script en un archivo .bash_alias aquí .

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

¿Puedes proporcionar un archivo bat de Windows equivalente?
Jaffy

1
@Jaffy No estoy seguro de cuánto tiempo tengo en mis manos y no soy muy fluido en el lote, pero puedo intentarlo. Publicaré mi progreso aquí , ¿tal vez otros puedan intervenir y ayudar?
philosowaffle

3

Si está en Windows, puede usar PyGitUp, que es un clon de git-upPython. Puede instalarlo usando pip con pip install --user git-upo a través de Scoop usandoscoop install git-up

[4]


3

Solo publicando una respuesta actualizada. git-upya no se mantiene y si lee la documentación, mencionan que la funcionalidad ahora está disponible en git .

A partir de Git 2.9, git pull --rebase --autostash hace básicamente lo mismo.

En consecuencia, si actualiza a Git 2.9 o posterior, puede usar este alias en lugar de instalar git-up:

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

También puede configurar esto para cada git pullGit 2.9 también (gracias @VonC, consulte su respuesta aquí )

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

1
No necesitas un alias. Un simple git pull es suficiente, con la configuración correcta: stackoverflow.com/a/40067353/6309
VonC

Gran llamada gracias @VonC Actualicé mi respuesta :) también podría enviar un RP a la git-updocumentación porque no mencionan eso
agosto

Esto no actualiza todas las sucursales locales a la vez, razón por la cual lo uso principalmente git-up.
Ray

Documentación actualizada el git-up:)
agosto

3

Me encontré con el mismo problema de esta pregunta ...

Preguntándome al respecto, hice una pequeña función de alias dentro de mi .bashrcarchivo:

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

Trabajó para mi (:


2

Si las referencias / cabezas / maestro se pueden reenviar rápidamente a referencias / controles remotos / foo / maestro , la salida de

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

debería devolver la identificación SHA1 a la que hace referencia las referencias / cabezas / puntos maestros . Con esto, puede armar una secuencia de comandos que actualice automáticamente todas las sucursales locales a las que no se les han aplicado confirmaciones de desvío.

Este pequeño script de shell (lo llamé git-can-ff ) ilustra cómo se puede hacer.

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

¿Qué implicas con ese comentario?
hillu

Yo mismo no soy capaz de escribir el guión que sugiere hillu, y no estoy lo suficientemente seguro de mi conocimiento de git para usar git-merge-base.
Norman Ramsey

2
Me temo que no entiendo el modelo lo suficientemente bien como para explotar el script tan amablemente proporcionado. Es suficiente para hacer que una persona quiera cambiar a mercurcial.
Norman Ramsey

Personalmente encontré el artículo de Tommi Virtanen "Git para informáticos" bastante útil para familiarizarse con el modelo y la terminología de git.
hillu

2

Para completar la respuesta de Matt Connolly, esta es una forma más segura de actualizar las referencias de sucursales locales que pueden reenviarse rápidamente, sin consultar la sucursal. No actualiza las ramas que no pueden reenviarse rápidamente (es decir, que han divergido), y no actualiza la rama que está actualmente desprotegida (porque entonces la copia de trabajo también debe actualizarse).

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

Un script ligeramente diferente que solo adelanta rápidamente las ramas cuyos nombres coinciden con su rama ascendente. También actualiza la rama actual si es posible el avance rápido.

Asegúrese de que todas las ramas aguas arriba de sus ramas estén configuradas correctamente al ejecutarlas git branch -vv. Establecer la rama aguas arriba congit branch -u origin/yourbanchname

Copie y pegue en un archivo y 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

El siguiente one-liner avanza rápidamente todas las ramas que tienen una rama ascendente si es posible, e imprime un error de lo contrario:

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

¿Como funciona?

Utiliza un formato personalizado con el git branchcomando. Para cada rama que tiene una rama ascendente, imprime una línea con el siguiente patrón:

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

Esto se puede canalizar directamente sh(suponiendo que los nombres de las ramas estén bien formados). Omitir el| sh para ver lo que está haciendo.

Advertencias

El one-liner no contactará con sus controles remotos. Emita uno git fetcho git fetch --allantes de ejecutarlo.

La rama actualmente desprotegida no se actualizará con un mensaje como

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

Para esto, puede recurrir a regular git pull --ff-only.

Alias

Agregue lo siguiente a su .gitconfigpara que git fftrealice este comando:

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

Ver también mi .gitconfig. El alias es una forma abreviada de "seguimiento rápido (ramas)".


Esta es una buena solución, aunque creo que usaré la hubsolución propuesta por @John para obtener una mejor salida.
Didier L

¡Esto es rápido, simple y realmente funciona! Sin embargo, estoy desconcertado git pushporque es totalmente opuesto a lo que esperarías. Cual es el secreto
BrandonLWhite

@BrandonLWhite: No entiendo la pregunta. ¿Qué espera de git push?
krlmlr

git pushtiene semántica de carga: tengo algunas confirmaciones locales que quiero enviar en sentido ascendente. git pulltiene semántica de descarga: quiero obtener algunas confirmaciones remotas ascendentes en mi sucursal local. Como estamos hablando de descargar nuevos commits desde el remoto al local, git pulles la opción obvia. Pero no, este truco lo usa git push. ¿Cómo se git pushtraduce en cambios remotos a mi sucursal local?
BrandonLWhite

git pushtambién se puede usar para actualizar sucursales locales, siempre que sea una actualización rápida.
krlmlr

1

El script de @larsmans, un poco mejorado:

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

Esto, después de que termina, deja la copia de trabajo desprotegida de la misma rama que estaba antes de que se llamara al script.

La git pullversión:

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

Parece que muchos otros han aportado soluciones similares, pero pensé en compartir lo que se me ocurrió e invitar a otros a contribuir. Esta solución tiene una salida agradable y colorida, maneja con gracia su directorio de trabajo actual, y es rápido porque no hace ningún pago y deja su directorio de trabajo intacto. Además, es solo un script de shell sin dependencias distintas de git. (Solo probado en OSX hasta ahora)

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

Lo siento, también parece que se me ocurrió el mismo nombre que la otra herramienta anterior.


2
¿Eres tú quien escribió esto? Si es así, por favor revele su afiliación, es decir, cuéntenos cómo está relacionado con ella. Por favor leer más sobre este para obtener más información. Específicamente, no lo digas, ¡muéstralo! ; Cuéntanos qué partes de tu script y cómo / por qué resuelve el problema.
Keale

1
Sí lo escribí. He incluido la fuente anterior para copiar y pegar rápidamente en su .bashrc o .zshrc.
Stimp

Esta es una buena solución y funciona bien. ¿Nadie se había dado cuenta?
Ville

1

Se puede hacer usando el siguiente script ... Primero buscará todas las ramas y pagará una por una y se actualizará 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

Agregue alguna explicación para obtener su respuesta
tonto-desarrollador

1

No puede hacerlo con un solo comando git, pero puede automatizarlo con una línea bash.

Para actualizar de forma segura todas las ramas con una línea, esto es lo que hago:

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
  • Si no puede avanzar rápidamente una rama o encontrar un error, se detendrá y lo dejará en esa rama para que pueda recuperar el control y fusionarse manualmente.

  • Si todas las ramas se pueden reenviar rápidamente, finalizará con la rama en la que se encontraba actualmente, dejándolo donde estaba antes de la actualización.

Explicaciones:

Para una mejor legibilidad, se puede dividir en varias líneas:

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 && ... => Obtiene todas las referencias de todos los controles remotos y continúa con el siguiente comando si no ha habido ningún error.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Desde la salida de git branch, sedtome la línea con a *y muévala hasta el final (para que la rama actual se actualice por última vez). Luego trsimplemente quite el *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Para cada nombre de rama obtenido del comando anterior, desproteja esta rama e intente fusionarse con un avance rápido. Si falla, breakse llama y el comando se detiene aquí.

Por supuesto, puede reemplazar git merge --ff-onlycon git rebasesi es lo que desea.

Finalmente, puedes ponerlo en tu bashrc como un 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 si tiene miedo de equivocarse con 'y', o simplemente prefiere mantener la legibilidad sintáctica en su editor, puede declararlo como una función:

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
}

Prima:

Para aquellos que deseen la explicación por sed '/*/{$q;h;d};$G'parte:

  • /*/=> Busque la línea con a *.

  • {$q => Si está en la última línea, salga (no necesitamos hacer nada porque la rama actual ya es la última en la lista).

  • ;h;d} => De lo contrario, almacene la línea en el búfer de retención y elimínela en la posición actual de la lista.

  • ;$G => Cuando llegue a la última línea, agregue el contenido del búfer de retención.


Puede evitar toda la locura de las líneas interminables y &&colocando set -een la parte superior de la secuencia de comandos.
mcepl

0

¿Puede "git pull --all" actualizar todas mis sucursales locales?

No, no puede. Para el avance rápido, acabo de escribir una pequeña herramienta para hacerlo. https://github.com/changyuheng/git-fast-forward-all

Ventajas de esta herramienta:

  1. Admite múltiples controles remotos en un repositorio. ( hub syncno admite múltiples controles remotos por el momento).
  2. Admite tener diferentes nombres en la sucursal local y la correspondiente sucursal de seguimiento remoto.
  3. Mucho más rápido que otros scripts que recuperan remotamente para cada rama.
  4. Sin análisis / edición de expresiones regulares propensas a errores.

1
Puede evitar golpear la red utilizando git fetch . refspec. El .dice buscar desde el repositorio actual en lugar de desde el remoto.
Hugomg

-1

A partir de git 2.9:

git pull --rebase --autostash

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

Cree automáticamente un alijo temporal antes de que comience la operación y aplíquelo después de que finalice la operación. Esto significa que puede ejecutar rebase en un árbol de trabajo sucio. Sin embargo, utilice con cuidado: la aplicación de almacenamiento final después de un cambio de versión exitoso puede dar lugar a conflictos no triviales.


-1

De hecho, con git version 1.8.3.1, funciona:

[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

En la rama maestra, puede actualizar todas las demás ramas. @Cascabel

No sé qué versión romperlo / arreglarlo, en 2.17 (que uso), puede funcionar.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.