web-dev-qa-db-fra.com

Vérifiez si besoin de tirer dans Git

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.

583
takeshin

Commencez par utiliser git remote update , pour mettre à jour vos références à distance. Ensuite, vous pouvez faire l’une des choses suivantes:

  1. 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.

  2. 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.

795
Neil Mayhew

Si vous avez une branche en amont

git fetch <remote>
git status

Si vous n'avez pas de branche en amont

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. .

125
PlagueHammer

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.)

57
Stephen Haberman

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.

36
brool

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:

  • Les commandes sont regroupées et imbriquées à l'aide de la syntaxe $(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.
  • Cette chaîne est introduite dans 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.
  • La syntaxe Bash [ 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.
31
wjordan

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:

Enter image description here

20
Bruno Adelé

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
10
ma11hew28

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.

9
Jeet

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
9
thuovila

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
6
Harikrishna

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
6
Claudio Floreani

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
5
Gilles Quenot

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
5
diegeelvis_SA

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.

4
che

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.

2
Henry Schreiner
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
1
jthill

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
1
Tomas Kukis

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
0
RuntimeException

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')
}
0
Andrew Grothe

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
0
Digital Human

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
0
Altzone

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.

0
Tuxdude