Comment vérifier si le référentiel distant a changé et que je dois extraire?
Maintenant, j'utilise ce script simple:
git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1
Mais c'est plutôt lourd.
Y a-t-il un meilleur moyen? La solution idéale consisterait à vérifier toutes les branches distantes et à renvoyer les noms des branches modifiées et le nombre de nouveaux commits dans chacune.
Commencez par utiliser git remote update
, pour mettre à jour vos références à distance. Ensuite, vous pouvez faire l’une des choses suivantes:
git status -uno
vous dira si la branche que vous suivez est en avance, en retard ou si elle a divergé. Si cela ne dit rien, le local et la télécommande sont les mêmes.
git show-branch *master
vous montrera les commits de toutes les branches dont le nom se termine par 'maître' (par exemple maître et Origine/master ).
Si vous utilisez -v
avec git remote update
(git remote -v update
), vous pouvez voir quelles branches ont été mises à jour et vous n'avez donc plus besoin de commandes supplémentaires.
Cependant, il semble que vous souhaitiez faire cela dans un script ou un programme et que vous obteniez une valeur true/false. Si tel est le cas, il existe des moyens de vérifier la relation entre votre commit actuel HEAD et le responsable de la branche que vous suivez, bien qu'il y en ait Quatre résultats possibles, vous ne pouvez pas le réduire à une réponse oui/non. Toutefois, si vous êtes prêt à faire un pull --rebase
, vous pouvez traiter "local est en retard" et "local a divergé" comme étant "besoin de tirer" et les deux autres comme "n'ayant pas besoin de tirer".
Vous pouvez obtenir l'ID de validation de n'importe quelle référence à l'aide de git rev-parse <ref>
. Vous pourrez ainsi le faire pour maître et Origine/maître et comparez-les. S'ils sont égaux, les branches sont les mêmes. S'ils sont inégaux, vous voulez savoir ce qui est en avance sur les autres. Utiliser git merge-base master Origin/master
vous indiquera l’ancêtre commun des deux branches, et si elles n’ont pas divergé, ce sera la même chose que l’une ou l’autre. Si vous obtenez trois identifiants différents, les branches ont divergé.
Pour le faire correctement, par exemple, dans un script, vous devez pouvoir faire référence à la branche actuelle et à la branche distante qu’elle suit. La fonction bash prompt-setting de /etc/bash_completion.d
contient un code utile pour obtenir les noms de branche. Cependant, vous n'avez probablement pas besoin d'obtenir les noms. Git dispose de quelques raccourcis pour faire référence aux branches et aux commits (comme décrit dans git rev-parse --help
). En particulier, vous pouvez utiliser @
pour la branche actuelle (en supposant que vous n'êtes pas dans un état isolé) et @{u}
pour sa branche en amont (par exemple Origin/master
). Donc, git merge-base @ @{u}
renverra le (hachage) du commit auquel la branche actuelle et ses divergences en amont et git rev-parse @
et git rev-parse @{u}
vous donneront les hachages des deux astuces. Ceci peut être résumé dans le script suivant:
#!/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
Remarque: Les anciennes versions de git n'autorisaient pas @
par elles-mêmes. Il est donc possible que vous deviez utiliser @{0}
à la place.
La ligne UPSTREAM=${1:-'@{u}'}
vous permet éventuellement de passer explicitement une branche amont, si vous souhaitez effectuer une vérification sur une branche distante différente de celle configurée pour la branche actuelle. Cela prend généralement la forme nom_extérieur/nom_ branchement . Si aucun paramètre n'est fourni, la valeur par défaut est @{u}
.
Le script suppose que vous avez d’abord créé git fetch
ou git remote update
pour mettre à jour les branches de suivi. Je n'ai pas intégré cela dans le script, car il est plus flexible de pouvoir effectuer l'extraction et la comparaison en tant qu'opérations distinctes, par exemple si vous souhaitez effectuer une comparaison sans extraire, car vous l'avez déjà extraite récemment.
git fetch <remote>
git status
Comparez les deux branches:
git fetch <remote>
git log <local_branch_name>..<remote_branch_name> --oneline
Par exemple:
git fetch Origin
# See if there are any incoming changes
git log HEAD..Origin/master --oneline
(Je suppose que Origin/master
est votre branche de suivi à distance)
Si des validations sont répertoriées dans le résultat ci-dessus, vous devez effectuer des modifications entrantes. Vous devez les fusionner. Si aucun commet n'est répertorié par git log
alors il n'y a rien à fusionner.
Notez que cela fonctionnera même si vous êtes sur une branche de fonctionnalité - qui n'a pas de suivi distant, car si se réfère explicitement à Origin/master
au lieu d'utiliser implicitement le branche en amont retenu par Git. .
S'il s'agit d'un script, vous pouvez utiliser:
git fetch
$(git rev-parse HEAD) == $(git rev-parse @{u})
(Remarque: l'avantage de cette réponse par rapport aux réponses précédentes est que vous n'avez pas besoin d'une commande distincte pour obtenir le nom de la branche actuelle. "HEAD" et "@ {u}" (la branche actuelle en amont) s'en occupent. Voir "git rev-parse --help" pour plus de détails.)
La commande
git ls-remote Origin -h refs/heads/master
listera la tête actuelle sur la télécommande - vous pouvez la comparer à une valeur précédente ou voir si vous avez le SHA dans votre référentiel local.
Voici un Bash one-liner qui compare le hachage de validation de la branche HEAD à sa branche amont distante. Aucune opération lourde git fetch
ou git pull --dry-run
n'est requise:
[ $(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
Voici comment cette ligne assez dense est décomposée:
$(x)
Bash substitution de commande .git rev-parse --abbrev-ref @{u}
renvoie une référence abrégée en amont (par exemple Origin/master
), qui est ensuite convertie en champs séparés par un espace par la commande sed
canalisée, par exemple. Origin master
.git ls-remote
qui renvoie le commit en-tête de la branche distante. Cette commande communiquera avec le référentiel distant. La commande piped cut
extrait uniquement le premier champ (le hachage de validation), en supprimant la chaîne de référence séparée par des tabulations.git rev-parse HEAD
renvoie le hachage de validation local.[ a = b ] && x || y
complète le one-line: il s'agit d'un Bash comparaison de chaînes=
dans une construction de test [ test ]
, suivi de and-list et or -list construit && true || false
.Je vous suggère d'aller voir le script https://github.com/badele/gitcheck . J'ai codé ce script pour vérifier en un seul passage tous vos référentiels Git, et il indique qui n'a pas commis et qui n'a pas poussé/tiré.
Voici un exemple de résultat:
J'ai basé cette solution sur les commentaires 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
Je pense que la meilleure façon de faire serait:
git diff remotes/Origin/HEAD
En supposant que vous avez enregistré cette référence. Si vous avez cloné le référentiel, vous devriez le faire (autrement dit, si le référentiel a été créé de novo localement et poussé vers la télécommande), vous devez ajouter explicitement le refspec.
Il existe déjà de nombreuses réponses ingénieuses et riches en fonctionnalités. Pour faire contraste, je pourrais me contenter d’une ligne très 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 script ci-dessous fonctionne parfaitement.
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
Je ferais le chemin suggéré par Brool. Le script d'une ligne suivant utilise la valeur SHA1 de votre dernière version validée et la compare à celle de l'origine distante, et extrait les modifications uniquement si elles diffèrent. Et c'est encore plus léger des solutions basées sur git pull
ou git fetch
.
[ `git log --pretty=%H ...refs/heads/master^` != `git ls-remote Origin
-h refs/heads/master |cut -f1` ] && git pull
Si vous exécutez ce script, il vérifiera si la branche actuelle a besoin d'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
}
C'est très pratique de le mettre comme un crochet pré-engagé pour éviter
Merge branch 'foobar' of url:/path/to/git/foobar into foobar
quand vous commit
avant pulling
.
Pour utiliser ce code en tant que hook, il suffit de copier/coller le script dans
.git/hooks/pre-commit
et
chmod +x .git/hooks/pre-commit
Je veux juste poster ceci comme un post réel car il est facile de rater ça dans les commentaires.
La réponse correcte et la meilleure pour cette question a été donnée par @Jake Berger. Merci beaucoup mec, tout le monde a besoin de ça et tout le monde le manque dans les commentaires. Donc, pour tous ceux qui ont des problèmes avec ceci, voici la bonne réponse, utilisez simplement le résultat de cette commande pour savoir si vous avez besoin de tirer un git pull. si la sortie est 0, il n'y a évidemment rien à mettre à jour.
@stackoverflow, donne une cloche à ce gars. Merci @ 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
Exécutez git fetch (remote)
pour mettre à jour vos références à distance, cela vous montrera les nouveautés. Ensuite, lorsque vous passez à la caisse de votre succursale locale, elle vous indiquera si elle est située en amont.
Voici ma version d'un script Bash qui vérifie tous les référentiels dans un dossier prédéfini:
https://Gist.github.com/henryiii/5841984
Il peut faire la différence entre des situations courantes, telles que tirer nécessaire et pousser nécessaire, et il est multithread, de sorte que l'extraction s'effectue en une fois. Il a plusieurs commandes, telles que pull et status.
Placez un lien symbolique (ou le script) dans un dossier de votre chemin, il fonctionnera alors comme git all status
(, etc.). Il ne supporte que Origin/master, mais il peut être édité ou combiné avec une autre méthode.
git ls-remote | cut -f1 | git cat-file --batch-check >&-
listera tout ce qui est référencé dans une télécommande qui n'est pas dans votre repo. Pour récupérer les modifications de référence à distance apportées à ce que vous aviez déjà (par exemple, réinitialisation à des commits précédents), prenez un peu plus:
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
En utilisant une expression rationnelle 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
Après avoir lu de nombreuses réponses et de nombreux messages, et passé une demi-journée à essayer diverses permutations, voici ce que j’ai trouvé.
Si vous êtes sous Windows, vous pouvez exécuter ce script sous Windows en utilisant Git Bash fourni par Git pour Windows (installation ou portable).
Ce script nécessite des arguments
- chemin local, par exemple /d/source/project1 - Git URL p. ex. https: //[email protected]/username/project1.git - mot de passe si un mot de passe ne doit pas être entré sur la ligne de commande en texte brut, puis modifiez le script pour vérifier si GITPASS est vide; ne pas remplacer et laisser Git demander un mot de passe
Le script va
- Find the current branch
- Get the SHA1 of the remote on that branch
- Get the SHA1 of the local on that branch
- Compare them.
S'il y a un changement tel qu'imprimé par le script, vous pouvez alors procéder à l'extraction ou à l'extraction. Le script peut ne pas être efficace, mais le travail est fait pour moi.
Mise à jour - 2015-10-30: stderr to dev null pour empêcher l'impression de l'URL avec le mot de passe sur la console.
#!/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
Pour les utilisateurs de Windows qui finissent par chercher cette question, j'ai modifié une partie de la réponse en script PowerShell. Ajustez si nécessaire, enregistrez dans un fichier .ps1
et exécutez-le à la demande ou planifié si vous le souhaitez.
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')
}
Toutes ces suggestions complexes alors que la solution est si courte et simple:
#!/bin/bash
BRANCH="<your branch name>"
LAST_UPDATE=`git show --no-notes --format=format:"%H" $BRANCH | head -n 1`
LAST_COMMIT=`git show --no-notes --format=format:"%H" Origin/$BRANCH | head -n 1`
if [ $LAST_COMMIT != $LAST_UPDATE ]; then
echo "Updating your branch $BRANCH"
git pull --no-edit
else
echo "No updates available"
fi
Peut-être ceci, si vous voulez ajouter une tâche en tant que 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
J'utilise une version d'un script basé sur la réponse 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
En supposant que ce script s'appelle git-fetch-and-rebase
, vous pouvez l'invoquer avec un argument facultatif directory name
du référentiel Git local sur lequel vous souhaitez effectuer l'opération. Si le script est appelé sans argument, il suppose que le répertoire en cours fait partie du référentiel Git.
Exemples:
# 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
Il est disponible ici aussi.