Quelles sont les différences entre git remote prune, git prune, git fetch --prune, etc.

359

Ma situation est la suivante ... quelqu'un travaillant sur le même référentiel a supprimé une branche de son référentiel local et distant ...

La plupart des personnes qui ont posé des questions sur ce type de problème sur Stack Overflow ou sur d'autres sites ont le problème des branches toujours affichées dans leur liste de branches de suivi à distance git branch -aen bas:

* master
  develop
  feature_blah
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah
  remotes/origin/random_branch_I_want_deleted

Cependant, dans MA situation, la branche qui ne devrait pas être là est locale:

* master
  develop
  feature_blah
  random_branch_I_want_deleted
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah

Lorsque je fais l'une des actions suivantes, elle n'est pas supprimée localement:

$ git prune

J'ai aussi essayé:

$ git remote prune origin
$ git fetch --prune

Plus d'informations utiles: Lorsque je vérifie, git remote show originvoici à quoi cela ressemble:

* remote origin
Fetch URL: utilities:homeconnections_ui.git
Push  URL: utilities:homeconnections_ui.git
HEAD branch: master
Remote branches:
 master                        tracked
 develop                       tracked
 feature_blah                  tracked
 other123                      tracked
 other444                      tracked
 other999                      tracked
Local branches configured for 'git pull':
 develop                      merges with remote develop
 feature_blah                 merges with remote other999
 master                       merges with remote master
 random_branch_I_want_deleted merges with remote random_branch_I_want_deleted
Local refs configured for 'git push':
 develop         pushes to develop     (local out of date)
 master          pushes to master      (up to date)
 feature_blah    pushes to feature_blah(up to date)

Notez que ce n'est que dans la section intitulée Local branches configured for 'git pull':

Pourquoi?

gogogadgetinternet
la source
git branch -d the_local_branch
krsteeve
1
Merci, mais je suis simplement curieux de savoir pourquoi cela a pu se produire.
gogogadgetinternet
Il y avait une différence subtile dans le traitement de la hiérarchie des branches ( x/y): elle a été corrigée (voir ma réponse ci-dessous )
VonC

Réponses:

665

Je ne vous blâme pas d'être frustré à ce sujet. La meilleure façon de voir les choses est la suivante. Il existe potentiellement trois versions de chaque branche distante:

  1. La branche réelle sur le référentiel distant
    (par exemple, le dépôt à distance sur https://example.com/repo.git , refs/heads/master)
  2. Votre instantané de cette branche localement (stocké sous refs/remotes/...)
    (par exemple, repo local, refs/remotes/origin/master)
  3. Et une branche locale qui pourrait suivre la branche distante
    (par exemple, repo local, refs/heads/master)

Commençons par git prune. Cela supprime les objets qui ne sont plus référencés, il ne supprime pas les références. Dans votre cas, vous avez une succursale locale. Cela signifie qu'il y a une référence nommée random_branch_I_want_deletedqui se réfère à certains objets qui représentent l'histoire de cette branche. Donc, par définition, git prunene supprimera pas random_branch_I_want_deleted. Vraiment, git prunec'est un moyen de supprimer les données qui se sont accumulées dans Git mais qui ne sont référencées par rien. En général, cela n'affecte pas votre vision des branches.

git remote prune originet les git fetch --prunedeux opèrent sur des références sous refs/remotes/...(je les appellerai références distantes). Cela n'affecte pas les succursales locales. La git remoteversion est utile si vous souhaitez uniquement supprimer les références distantes sous une télécommande particulière. Sinon, les deux font exactement la même chose. Donc, en bref, git remote pruneet git fetch --pruneopérez sur le numéro 2 ci-dessus. Par exemple, si vous avez supprimé une branche à l'aide de l'interface graphique Web de git et que vous ne voulez plus qu'elle apparaisse dans votre liste de branches locale ( git branch -r), alors c'est la commande que vous devez utiliser.

Pour supprimer une branche locale, vous devez utiliser git branch -d(ou -Dsi elle n'est fusionnée nulle part). FWIW, il n'y a pas de commande git pour supprimer automatiquement les branches de suivi locales si une branche distante disparaît.

John Szakmeister
la source
22
Cela permet de mieux répondre à la question globale en expliquant les différences pertinentes. Il répond également aux questions supplémentaires que j'avais de celle ci-dessus.
gogogadgetinternet
14
Cette commande affichera une liste de toutes les branches locales qui n'ont pas de branche distante correspondante. Vous pouvez diriger cela vers xargs git branch -D, mais notez que toutes les nouvelles branches que vous avez créées mais jamais poussées vers le serveur seront supprimées, alors soyez prudent: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton
4
@Seed Non, ce n'est pas le cas. :-( Il ne supprime que les références de suivi à distance local. J'ai juste
revérifié
1
@ BlueRaja-DannyPflughoeft Soyez prudent avec cette approche. Par exemple, selon la façon dont vous faites vos branches stables, elles peuvent sembler fusionnées dans la branche principale, et vous finirez par les supprimer. Ce n'est pas une grosse perte ici car vous ne les supprimez pas du serveur, mais si vous aviez une configuration spéciale que vous avez définie pour cela, cela serait perdu lorsque la branche est supprimée.
John Szakmeister
1
@Cloud Pas tout à fait vrai. Les références peuvent être compressées (voir le packed-refsfichier dans la .gitzone), il n'est donc pas nécessairement simple de les supprimer via l'explorateur de fichiers. Mieux vaut utiliser les commandes pour vous assurer que les deux sont correctement pris en charge.
John Szakmeister
55

git remote pruneet git fetch --prunefaites la même chose: supprimer les références aux branches qui n'existent pas sur la télécommande, comme vous l'avez dit. La deuxième commande se connecte à la télécommande et récupère ses branches actuelles avant l'élagage.

Cependant, il ne touche pas les succursales locales que vous avez consultées, que vous pouvez simplement supprimer avec

git branch -d  random_branch_I_want_deleted

Remplacer -dpar -Dsi la branche n'est pas fusionnée ailleurs

git prune fait quelque chose de différent, il purge les objets inaccessibles, les validations qui ne sont accessibles dans aucune branche ou balise, et qui ne sont donc plus nécessaires.

CharlesB
la source
1
Je sais que cela semble évident, mais git prunene recherche pas seulement les branches et les balises, mais aussi toutes les autres références.
Dans mon cas, pourquoi git prune ne fonctionnerait-il pas? Parce qu'il ne se soucie pas des succursales locales, mais des références distantes? Merci pour les informations concises.
gogogadgetinternet
@hvd, quel type de références existe-t-il à part les branches et les balises?
CharlesB
@gogogadgetinternet oui exactement. (en supposant que vous vouliez dire git remote prune)
CharlesB
4
IMO, la convention de dénomination git de l'utilisation de "prune" pour la collecte d'objets et le nettoyage de références est le point de départ de la confusion. Mais ce n'est là qu'un des nombreux problèmes d'interface utilisateur, dans git. :-)
torek
14

Dans le cas où quelqu'un serait intéressé. Voici un script shell rapide qui supprimera toutes les branches locales qui ne sont pas suivies à distance. Un mot d'avertissement: cela supprimera toute branche qui n'est pas suivie à distance, qu'elle ait été fusionnée ou non.

Si vous voyez des problèmes avec ceci, faites-le moi savoir et je le corrigerai (etc. etc.)

Enregistrez-le dans un fichier appelé git-rm-ntb(appelez-le n'importe quoi) sur PATHet exécutez:

git-rm-ntb <remote1:optional> <remote2:optional> ...

clean()
{
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  RBRANCHES=()
  while read REMOTE; do
    CURRBRANCHES=($(git ls-remote $REMOTE | awk '{print $2}' | grep 'refs/heads/' | sed 's:refs/heads/::'))
    RBRANCHES=("${CURRBRANCHES[@]}" "${RBRANCHES[@]}")
  done < <(echo "$REMOTES" )
  [[ $RBRANCHES ]] || exit
  LBRANCHES=($(git branch | sed 's:\*::' | awk '{print $1}'))
  for i in "${LBRANCHES[@]}"; do
    skip=
    for j in "${RBRANCHES[@]}"; do
      [[ $i == $j ]] && { skip=1; echo -e "\033[32m Keeping $i \033[0m"; break; }
    done
    [[ -n $skip ]] || { echo -e "\033[31m $(git branch -D $i) \033[0m"; }
  done
}

clean $@
D.Mill
la source
Merci! Belle touche avec la sortie colorée ;-)
BVengerov
2
$ (Git branch -d $ i) ne serait-il pas plus sûr de ne supprimer que les branches fusionnées?
user2012677
Tellement utile merci beaucoup !!!
Robin Hartland
Les options les plus sûres sont discutées dans stackoverflow.com/questions/7726949/…
Michael Freidgeim
13

Notez qu'une différence entre git remote --pruneet git fetch --pruneest en cours de correction, avec commit 10a6cc8 , par Tom Miller ( tmiller) (pour git 1.9 / 2.0, T1 2014):

Lorsque nous avons une branche de suivi à distance nommée " frotz/nitfol" à partir d'une extraction précédente, et que l'amont a maintenant une branche nommée "** frotz " **, fetchne parviendrait pas à supprimer " frotz/nitfol" avec un " git fetch --prune" de l'amont.
git informerait l'utilisateur d'utiliser " git remote prune" pour résoudre le problème.

Donc: lorsqu'un référentiel en amont a une branche ("frotz") avec le même nom qu'une hiérarchie de branche ("frotz / xxx", une convention de dénomination de branche possible ), git remote --pruneréussissait (à nettoyer la branche de suivi à distance de votre référentiel) , mais git fetch --pruneéchouait.

Plus maintenant:

Modifiez le mode de fonctionnement de " fetch --prune" en déplaçant l'opération d'élagage avant l'opération de récupération.
De cette façon, au lieu d'avertir l'utilisateur d'un conflit, il le corrige automatiquement.

VonC
la source