mac - ¿Puedo “git pull-all” actualizar todas mis sucursales locales?
git repository (20)
Agregue este script a .profile
en 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};
};
A menudo tengo al menos 3 sucursales remotas: master, 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 simplemente hacer un "git pull-all", pero no he podido hacerlo funcionar. Parece hacer un "fetch - all", luego actualiza (avance rápido o fusiona) la rama de trabajo actual, pero no las otras sucursales locales.
Todavía estoy atascado manualmente cambiando a cada sucursal local y actualizando.
Aquí hay una buena respuesta: cómo obtener todas las ramas de git
for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
El comportamiento que describe para " pull --all
es exactamente el esperado, aunque no es necesariamente útil. La opción se pasa a git fetch, que luego recupera todos los refs de todos los controles remotos, en lugar de solo el necesario; pull
luego fusiona (o en su caso, vuelve a basarse) la única rama apropiada.
Si desea revisar otras sucursales, tendrá que revisarlas. Y sí, la fusión (y la reorganización) requieren absolutamente un árbol de trabajo, por lo que no se pueden hacer sin revisar las otras sucursales. Si lo desea, podría resumir los pasos descritos en un script / alias, aunque sugeriría unir los comandos con &&
para que, si uno de ellos falla, no intente seguir adelante.
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, una vez que termina, deja la copia de trabajo desprotegida de la misma rama que estaba antes de que se llamara el script.
La versión git pull
:
#!/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
Este problema no está resuelto (todavía), al menos no fácilmente / sin scripting: vea esta publicación en la lista de correo de git de Junio C Hamano, que explica la situación y ofrece una solución simple.
El razonamiento principal es que no deberías necesitar esto:
Con git que no es antiguo (es decir, v1.5.0 o más reciente), no hay razón para tener un "dev" local que simplemente rastree el control remoto. Si solo desea ver 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 de "dev", y si está en una rama diferente de "dev", no debemos hacer nada después de que "
git fetch
" actualice el seguimiento remoto " dev ". No avanzará de todos modos
La solicitud de una solución fue para una opción o script externo para eliminar las sucursales locales que siguen a las sucursales de seguimiento remoto, en lugar de mantenerlas actualizadas mediante el envío rápido, como el póster original solicitado.
Entonces, ¿qué hay de "
git branch --prune --remote=<upstream>
" que itera sobre las sucursales locales, y si(1) no es la rama actual; y
(2) está marcado para rastrear alguna rama tomada del <upstream>; y
(3) no tiene ningún compromiso por sí mismo;entonces quitar esa rama? "
git remote --prune-local-forks <upstream>
" también está bien; No me importa qué comando implementa la característica tanto.
Nota: a partir de git 2.10 no existe tal solución. Tenga en cuenta que el subcomando git fetch --prune
y git fetch --prune
tratan sobre la eliminación de la rama de seguimiento remoto para la rama que ya no existe en el control remoto, no sobre la eliminación de la rama local que realiza el seguimiento de la rama de seguimiento remoto (para la cual la rama de seguimiento remoto está en sentido ascendente rama).
Esto aún no es automático, ya que me gustaría que hubiera una opción para, y debería haber algunas comprobaciones para asegurarse de que esto solo pueda suceder con las actualizaciones de avance rápido (¡por eso es que hacer un pull manualmente es mucho más seguro!), Pero advertencias aparte 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 revisarla.
Nota: perderá la posición actual de la rama y la moverá hacia donde se encuentra la rama de origen, lo que significa que si necesita fusionar perderá datos.
Hay muchas respuestas aceptables aquí, 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/git
a su PATH
(asumiendo que el archivo es ~/bin/git/git-update-all
), simplemente puede ejecutar:
$ git update-all
Hay muchas respuestas aquí, pero ninguna que use git-fetch
para actualizar directamente la referencia local, que es mucho más simple que consultar sucursales, y más segura que git-update-ref
.
Aquí utilizamos git-fetch
para actualizar las ramas no actuales y git pull --ff-only
para la rama actual. Eso:
- No requiere retirar las sucursales.
- Actualiza las sucursales solo si se pueden adelantar
- 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
De la página del 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 un fetch que actualiza la referencia local solo cuando se puede avanzar rápidamente.
Nota : esto supone que las sucursales locales y remotas tienen el mismo nombre (y que desea realizar un seguimiento de todas las sucursales), en realidad debería usar información sobre qué sucursales locales tiene y para qué están configuradas.
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
Para completar la respuesta de Matt Connolly, esta es una forma más segura de actualizar las referencias de sucursales locales que pueden ser enviadas rápidamente, sin consultar la sucursal. No actualiza las ramas que no pueden ser reenviadas rápidamente (es decir, que se han desviado) y no actualiza la rama que está actualmente activada (porque 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
Parece que muchos otros han contribuido con soluciones similares, pero pensé en compartir lo que se me ocurrió e invitar a otros a contribuir. Esta solución tiene una salida colorida y agradable, maneja con gracia su directorio de trabajo actual, y es rápida porque no realiza ninguna comprobación y deja su directorio de trabajo intacto. Además, es solo un script de shell sin dependencias que no sea 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, parece que también he encontrado el mismo nombre que la otra herramienta anterior.
Si refs / heads / master puede ser reenviado rápidamente a refs / remotes / foo / master , la salida de
git merge-base refs/heads/master refs/remotes/foo/master
debe devolver el ID de SHA1 al que refs / heads / master apunta. Con esto, puede armar una secuencia de comandos que actualiza automáticamente todas las sucursales locales a las que no se les ha aplicado ninguna confirmación 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
Solo publicando una respuesta actualizada. git-up
ya no se mantiene y si lees 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 pull
partir de Git 2.9 también (gracias @VonC, consulte su respuesta here )
git config --global pull.rebase true
git config --global rebase.autoStash true
Un guión que escribí para mi GitBash . Realiza lo siguiente:
- De manera predeterminada, las fuentes desde el origen para todas las sucursales 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, entonces esconde sus cambios e intentará restaurarlos al final.
- Para cada sucursal local configurada para rastrear una sucursal remota:
-
git checkout branch
-
git pull origin
-
- Finalmente, volverá a su estado original de bifurcación y restauración.
** Uso esto pero no lo he probado a fondo, uso bajo su propio riesgo. Vea un ejemplo de este script en un archivo .bash_alias here .
# 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
}
Un script ligeramente diferente que solo avanza rápidamente las ramas cuyos nombres coinciden con su rama ascendente. También actualiza la rama actual si es posible un avance rápido.
Asegúrese de que todas las sucursales de sus sucursales estén configuradas correctamente ejecutando git branch -vv
. Establezca la rama en sentido ascendente con git branch -u origin/yourbanchname
Copiar-pegar 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;
Uso el subcomando sync
de hub para automatizar esto. Hub tiene un alias como git
, por lo que el comando que escribo es:
git sync
Esto actualiza todas las sucursales locales que tienen una rama ascendente coincidente. De la página del manual:
- Si la sucursal local no está actualizada, avance rápidamente;
- Si la sucursal local contiene trabajo no pulido, adviértalo;
- Si la rama parece fusionada y su rama ascendente se eliminó, elimínela.
También maneja los cambios no comprometidos 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 sync
hace casi exactamente lo mismo.
Nota: Aunque publiqué mi propia solución, recomendaría el uso de git-up , que también es la respuesta aceptada.
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 yo mismo.
Aquí va, el git-ffwd-update
hace lo siguiente ...
- emite una
git remote update
para obtener las revoluciones de lates - luego usa
git remote show
para obtener una lista de sucursales locales que rastrean una sucursal remota (por ejemplo, sucursales que se pueden usar congit pull
) - luego verifica con
git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>
la cantidad de confirmaciones que la sucursal local está detrás del control remoto (y viceversa) - Si la sucursal local tiene 1 o más confirmaciones por adelantado, NO puede ser reenviada y necesita ser fusionada o rediseñada a mano.
- si la rama local tiene 0 confirmaciones por delante y 1 o más confirmaciones por detrás,
git branch -l -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>
El script se puede llamar como:
$ 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
El script completo, debe guardarse como git-ffwd-update
y 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 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 $@
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 área de trabajo sucia. Sin embargo, úselo con cuidado: la aplicación de alijo final después de una reconfiguración exitosa podría resultar en conflictos no triviales.
Se puede hacer usando el siguiente script ... Primero buscará todas las ramas y el proceso de pago uno por uno y se actualizará por sí mismo.
#!/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