practices - Compruebe si es necesario tirar en Git
git tag commits (22)
Si tienes una sucursal upstream
git fetch <remote>
git status
Si no tienes una sucursal upstream
Compara las dos ramas:
git fetch <remote>
git log <local_branch_name>..<remote_branch_name> --oneline
Por ejemplo:
git fetch origin
# See if there are any incoming changes
git log HEAD..origin/master --oneline
(Supongo que origin/master
es su rama de seguimiento remoto)
Si alguna confirmación aparece en la salida anterior, entonces tiene cambios entrantes, debe fusionar. Si no se enumeran las confirmaciones por git log
entonces no hay nada que fusionar.
Tenga en cuenta que esto funcionará incluso si se encuentra en una rama de función, que no tiene un control remoto de seguimiento, ya que si se refiere explícitamente a origin/master
lugar de utilizar implícitamente la rama ascendente recordada por Git.
¿Cómo verifico si el repositorio remoto ha cambiado y necesito extraer?
Ahora uso este simple script:
git pull --dry-run | grep -q -v ''Already up-to-date.'' && changed=1
Pero es bastante pesado.
¿Hay alguna manera mejor? La solución ideal sería verificar todas las sucursales remotas y devolver los nombres de las sucursales modificadas y el número de confirmaciones nuevas en cada una.
Aquí está mi versión de un script Bash que comprueba todos los repositorios en una carpeta predefinida:
https://gist.github.com/henryiii/5841984
Puede diferenciar entre situaciones comunes, como el tirón necesario y el empuje necesario, y está multiproceso, por lo que la recuperación se realiza de una vez. Tiene varios comandos, como pull y status.
Coloque un enlace simbólico (o la secuencia de comandos) en una carpeta en su ruta, entonces funciona como git all status
(, etc.). Solo admite origen / master, pero puede editarse o combinarse con otro método.
Aquí hay un Bash de una sola línea que compara el hash de confirmación HEAD de la sucursal actual con su sucursal ascendente remota, sin git pull --dry-run
una gran git fetch
git pull --dry-run
o git pull --dry-run
operaciones de git pull --dry-run
requeridas:
[ $(git rev-parse HEAD) = $(git ls-remote $(git rev-parse --abbrev-ref @{u} | /
sed ''s//// /g'') | cut -f1) ] && echo up to date || echo not up to date
Así es como se descompone esta línea un tanto densa:
- Los comandos se agrupan y anidan usando la sintaxis de command-substitution
$(x)
Bash. -
git rev-parse --abbrev-ref @{u}
devuelve una referencia ascendente abreviada (por ejemplo,origin/master
), que luego se convierte en campos separados por espacios mediante el comando pipedsed
, por ejemplo,origin master
. - Esta cadena se alimenta a
git ls-remote
que devuelve la confirmación de encabezado de la rama remota. Este comando se comunicará con el repositorio remoto. El comando decut
canalizado extrae solo el primer campo (el hash de confirmación), eliminando la cadena de referencia separada por tabulaciones. -
git rev-parse HEAD
devuelve el hash de confirmación local. - La sintaxis de Bash
[ a = b ] && x || y
[ a = b ] && x || y
completa una línea: esta es una string-comparison Bash=
dentro de una construcción de[ test ]
, seguida de las construcciones de lista y de or o lista&& true || false
&& true || false
Basé esta solución en los comentarios de @jberger.
if git checkout master &&
git fetch origin master &&
[ `git rev-list HEAD...origin/master --count` != 0 ] &&
git merge origin/master
then
echo ''Updated!''
else
echo ''Not updated.''
fi
Creo que la mejor manera de hacer esto sería:
git diff remotes/origin/HEAD
Suponiendo que tienes la refspec registrada. Debe hacerlo si ha clonado el repositorio, de lo contrario (es decir, si el repositorio se creó de novo localmente y se envió al control remoto), debe agregar el refspec explícitamente.
Después de leer muchas respuestas y varias publicaciones, y pasar medio día probando varias permutaciones, esto es lo que he encontrado.
Si está en Windows, puede ejecutar este script en Windows usando Git Bash proporcionado por Git para Windows (instalación o portátil).
Este script requiere argumentos.
- local path e.g. /d/source/project1 - Git URL e.g. https://[email protected]/username/project1.git - password if a password should not be entered on the command line in plain text, then modify the script to check if GITPASS is empty; do not replace and let Git prompt for a password
El guion sera
- Find the current branch
- Get the SHA1 of the remote on that branch
- Get the SHA1 of the local on that branch
- Compare them.
Si hay un cambio como lo imprime el script, entonces puede proceder a buscar o extraer. El guión puede no ser eficiente, pero hace el trabajo por mí.
Actualización - 2015-10-30: stderr a dev null para evitar la impresión de la URL con la contraseña en la consola.
#!/bin/bash
# Shell script to check if a Git pull is required.
LOCALPATH=$1
GITURL=$2
GITPASS=$3
cd $LOCALPATH
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
echo
echo git url = $GITURL
echo branch = $BRANCH
# Bash replace - replace @ with :password@ in the GIT URL
GITURL2="${GITURL/@/:$GITPASS@}"
FOO="$(git ls-remote $GITURL2 -h $BRANCH 2> /dev/null)"
if [ "$?" != "0" ]; then
echo cannot get remote status
exit 2
fi
FOO_ARRAY=($FOO)
BAR=${FOO_ARRAY[0]}
echo [$BAR]
LOCALBAR="$(git rev-parse HEAD)"
echo [$LOCALBAR]
echo
if [ "$BAR" == "$LOCALBAR" ]; then
#read -t10 -n1 -r -p ''Press any key in the next ten seconds...'' key
echo No changes
exit 0
else
#read -t10 -n1 -r -p ''Press any key in the next ten seconds...'' key
#echo pressed $key
echo There are changes between local and remote repositories.
exit 1
fi
Ejecute git fetch (remote)
para actualizar sus refs remotos, le mostrará las novedades. Luego, cuando compruebe su sucursal local, le mostrará si está detrás del flujo ascendente.
El comando
git ls-remote origin -h refs/heads/master
listará la cabecera actual en el control remoto; puede compararlo con un valor anterior o ver si tiene el SHA en su repositorio local.
El siguiente script funciona perfectamente.
changed=0
git remote update && git status -uno | grep -q ''Your branch is behind'' && changed=1
if [ $changed = 1 ]; then
git pull
echo "Updated successfully";
else
echo "Up-to-date"
fi
Haría el camino sugerido por brool. La siguiente secuencia de comandos de una línea toma el SHA1 de su última versión comprometida y la compara con la del origen remoto, y extrae cambios solo si son diferentes. Y es aún más ligero que las soluciones basadas en git pull
o git fetch
.
[ `git log --pretty=%H ...refs/heads/master^` != `git ls-remote origin
-h refs/heads/master |cut -f1` ] && git pull
Hay muchas respuestas muy ricas e ingeniosas ya. Para proporcionar algo de contraste, podría conformarme con una línea muy simple.
# Check return value to see if there are incoming updates.
if ! git diff --quiet remotes/origin/HEAD; then
# pull or whatever you want to do
fi
Le sugiero que vaya a ver el script https://github.com/badele/gitcheck . He codificado este script para verificar en una sola pasada todos los repositorios de Git, y muestra quién no se ha comprometido y quién no ha presionado / tirado.
Aquí un ejemplo de resultado:
Para los usuarios de Windows que terminan esta pregunta buscando esto, he modificado algunas de las respuestas en un script de powershell. Modifique según sea necesario, guárdelo en un archivo .ps1
y ejecútelo a pedido o programado si lo desea.
cd C:/<path to repo>
git remote update #update remote
$msg = git remote show origin #capture status
$update = $msg -like ''*local out of date*''
if($update.length -gt 0){ #if local needs update
Write-Host (''needs update'')
git pull
git reset --hard origin/master
Write-Host (''local updated'')
} else {
Write-Host (''no update needed'')
}
Primero use git remote update
para actualizar sus refs remotos. Entonces puedes hacer una de varias cosas, tales como:
git status -uno
le dirá si la sucursal que está rastreando está adelantada, atrasada o divergida. Si no dice nada, lo local y lo remoto son lo mismo.git show-branch *master
le mostrará las confirmaciones en todas las ramas cuyos nombres terminan en ''master'' (por ejemplo, master y origin / master ).
Si usa -v
con git remote update
( git remote -v update
) puede ver qué sucursales se actualizaron, por lo que no necesita más comandos.
Sin embargo, parece que desea hacer esto en un script o programa y terminar con un valor verdadero / falso. Si es así, hay formas de verificar la relación entre su compromiso HEAD actual y el jefe de la sucursal que está rastreando, aunque dado que hay cuatro resultados posibles, no puede reducirlo a una respuesta de sí / no. Sin embargo, si está preparado para realizar una pull --rebase
entonces puede tratar "local está detrás" y "local ha divergido" como "necesidad de tirar", y los otros dos como "no es necesario tirar".
Puede obtener el ID de confirmación de cualquier ref usando git rev-parse <ref>
, así que puede hacer esto para master y origin / master y compararlos. Si son iguales, las ramas son las mismas. Si son desiguales, quieres saber cuál está por delante de la otra. El uso de git merge-base master origin/master
te dirá el ancestro común de ambas ramas, y si no se han desviado, esto será lo mismo que una u otra. Si obtienes tres identificaciones diferentes, las ramas han divergido.
Para hacer esto correctamente, por ejemplo, en un script, debe poder referirse a la rama actual y a la rama remota que está rastreando. La función de configuración de solicitud de bash en /etc/bash_completion.d
tiene algún código útil para obtener los nombres de las sucursales. Sin embargo, probablemente no necesite obtener los nombres. Git tiene algunos métodos abreviados para referirse a ramas y confirmaciones (como se documenta en git rev-parse --help
). En particular, puede usar @
para la rama actual (suponiendo que no se encuentre en un estado de cabeza separada) y @{u}
para su rama ascendente (por ejemplo, origin/master
). Así que git merge-base @ @{u}
devolverá el (hash de) el commit en el cual la rama actual y su divergencia en sentido ascendente y git rev-parse @
y git rev-parse @{u}
le darán los hashes del dos consejos Esto se puede resumir en el siguiente script:
#!/bin/sh
UPSTREAM=${1:-''@{u}''}
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "$UPSTREAM")
BASE=$(git merge-base @ "$UPSTREAM")
if [ $LOCAL = $REMOTE ]; then
echo "Up-to-date"
elif [ $LOCAL = $BASE ]; then
echo "Need to pull"
elif [ $REMOTE = $BASE ]; then
echo "Need to push"
else
echo "Diverged"
fi
Nota: las versiones anteriores de git no permitían @
solo, por lo que es posible que tenga que usar @{0}
lugar.
La línea UPSTREAM=${1:-''@{u}''}
permite opcionalmente pasar una sucursal en sentido ascendente explícitamente, en caso de que desee verificar una sucursal remota diferente a la configurada para la rama actual. Esto normalmente sería de la forma remotename / branchname . Si no se proporciona ningún parámetro, el valor predeterminado es @{u}
.
El script asume que primero has realizado una git remote update
git fetch
o git remote update
, para actualizar las sucursales de seguimiento. No incorporé esto en el script porque es más flexible poder realizar la extracción y la comparación como operaciones separadas, por ejemplo, si desea comparar sin recuperar porque ya lo hizo recientemente.
Si ejecuta este script, probará si la rama actual necesita un git pull
:
#!/bin/bash
git fetch -v --dry-run 2>&1 |
grep -qE "/[up/s+to/s+date/]/s+$(
git branch 2>/dev/null |
sed -n ''/^/*/s/^/* //p'' |
sed -r ''s:(/+|/*|/$):///1:g''
)/s+" || {
echo >&2 "Current branch need a ''git pull'' before commit"
exit 1
}
Es muy conveniente ponerlo como un gancho previo de Git para evitar
Merge branch ''foobar'' of url:/path/to/git/foobar into foobar
Cuando te commit
antes de pulling
.
Para usar este código como un gancho, simplemente copie / pegue el script en
.git/hooks/pre-commit
y
chmod +x .git/hooks/pre-commit
Si esto es para un script, puedes usar:
git fetch
$(git rev-parse HEAD) == $(git rev-parse @{u})
(Nota: el beneficio de esto frente a las respuestas anteriores es que no necesita un comando por separado para obtener el nombre de la rama actual. "HEAD" y "@ {u}" (el canal actual de la rama actual) cuídelo. Consulte "git rev-parse --help" para más detalles.)
Solo quiero publicar esto como una publicación real, ya que es fácil perderse esto en los comentarios.
La respuesta correcta y mejor a esta pregunta fue dada por @Jake Berger, Muchas gracias, todos lo necesitan y todos lo extrañan en los comentarios. Así que para todos los que luchan con esto aquí es la respuesta correcta, solo use la salida de este comando para saber si necesita hacer un tirón de git. Si la salida es 0, obviamente no hay nada que actualizar.
@, dale campanas a este chico. Gracias @ Jake Berger
git rev-list HEAD...origin/master --count will give you the total number of "different" commits between the two. – Jake Berger Feb 5 ''13 at 19:23
Tal vez esto, si desea agregar tarea como crontab:
#!/bin/bash
dir="/path/to/root"
lock=/tmp/update.lock
msglog="/var/log/update.log"
log()
{
echo "$(date) ${1:-missing}" >> $msglog
}
if [ -f $lock ]; then
log "Already run, exiting..."
else
> $lock
git -C ~/$dir remote update &> /dev/null
checkgit=`git -C ~/$dir status`
if [[ ! "$checkgit" =~ "Your branch is up-to-date" ]]; then
log "-------------- Update ---------------"
git -C ~/$dir pull &>> $msglog
log "-------------------------------------"
fi
rm $lock
fi
exit 0
También puede encontrar un script de Phing que hace eso ahora.
Necesitaba una solución para actualizar mis entornos de producción automáticamente y estamos muy contentos gracias a este script que estoy compartiendo.
El script está escrito en XML y necesita Phing .
Usando regexp simple:
str=$(git status)
if [[ $str =~ .*Your/ branch/ is/ behind.*by.*commits,/ and/ can/ be/ fast-forwarded ]]; then
echo `date "+%Y-%m-%d %H:%M:%S"` "Needs pull"
else
echo "Code is up to date"
fi
Utilizo una versión de un script basado en la respuesta de Stephen Haberman:
if [ -n "$1" ]; then
gitbin="git -C $1"
else
gitbin="git"
fi
# Fetches from all the remotes, although --all can be replaced with origin
$gitbin fetch --all
if [ $($gitbin rev-parse HEAD) != $($gitbin rev-parse @{u}) ]; then
$gitbin rebase @{u} --preserve-merges
fi
Suponiendo que este script se llame git-fetch-and-rebase
, se puede invocar con un directory name
de directory name
argumentos opcional del repositorio Git local para realizar la operación. Si se llama al script sin argumentos, se supone que el directorio actual es parte del repositorio Git.
Ejemplos:
# Operates on /abc/def/my-git-repo-dir
git-fetch-and-rebase /abc/def/my-git-repo-dir
# Operates on the Git repository which the current working directory is part of
git-fetch-and-rebase
Está disponible here también.
git ls-remote | cut -f1 | git cat-file --batch-check >&-
listará todo lo que se menciona en cualquier control remoto que no esté en su repositorio. Para capturar ref ref los cambios a cosas que ya tenías (por ejemplo, restablecimientos a confirmaciones previas) toma un poco más:
git pack-refs --all
mine=`mktemp`
sed ''/^#/d;/^^/{G;s/./(.*/)/n.* /(.*/)//1 /2^{}/;};h'' .git/packed-refs | sort -k2 >$mine
for r in `git remote`; do
echo Checking $r ...
git ls-remote $r | sort -k2 | diff -b - $mine | grep ^/<
done