¿Cuáles son las diferencias entre git prune remoto, git prune, git fetch --prune, etc.

358

Mi situación es esta ... alguien que trabaja en el mismo repositorio ha eliminado una rama de su repositorio local y remoto ...

La mayoría de las personas que han preguntado sobre este tipo de problema en Stack Overflow u otros sitios tienen el problema de que las sucursales todavía se muestran en su lista de sucursales de seguimiento remoto git branch -aen la parte inferior:

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

Sin embargo, en MI situación, la rama que no debería estar allí, es local:

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

Cuando hago algo de lo siguiente, no se elimina localmente:

$ git prune

También probé:

$ git remote prune origin
$ git fetch --prune

Más información útil: cuando verifico git remote show originasí es como se ve:

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

Tenga en cuenta que solo está en la sección titulada Local branches configured for 'git pull':

¿Por qué?

gogogadgetinternet
fuente
git branch -d the_local_branch
krsteeve
1
Gracias, pero tengo curiosidad por saber por qué pudo haber ocurrido.
gogogadgetinternet
Hubo una sutil diferencia al tratar con la jerarquía de ramas ( x/y): se ha solucionado (vea mi respuesta a continuación )
VonC

Respuestas:

665

No te culpo por frustrarte por esto. La mejor manera de mirar es esto. Hay potencialmente tres versiones de cada rama remota:

  1. La rama real en el repositorio remoto
    (por ejemplo, repositorio remoto en https://example.com/repo.git , refs/heads/master)
  2. Su instantánea de esa rama localmente (almacenada en refs/remotes/...)
    (por ejemplo, repositorio local, refs/remotes/origin/master)
  3. Y una sucursal local que podría estar rastreando la sucursal remota
    (por ejemplo, repositorio local, refs/heads/master)

Vamos a empezar con git prune. Esto elimina objetos a los que ya no se hace referencia, no elimina referencias. En su caso, tiene una sucursal local. Eso significa que hay una referencia nombrada random_branch_I_want_deletedque se refiere a algunos objetos que representan el historial de esa rama. Entonces, por definición, git pruneno se eliminará random_branch_I_want_deleted. Realmente, git prunees una forma de eliminar datos que se han acumulado en Git pero a los que no se hace referencia. En general, no afecta su vista de ninguna rama.

git remote prune originy git fetch --pruneambos operan con referencias en refs/remotes/...(me referiré a ellas como referencias remotas). No afecta a las sucursales locales. La git remoteversión es útil si solo desea eliminar referencias remotas en un control remoto en particular. De lo contrario, los dos hacen exactamente lo mismo. En resumen, git remote pruney git fetch --pruneoperar en el número 2 anterior. Por ejemplo, si eliminó una rama utilizando la GUI web de git y no desea que aparezca más en su lista de sucursales locales ( git branch -r), entonces este es el comando que debe usar.

Para eliminar una sucursal local, debe usarla git branch -d(o -Dsi no está fusionada en ninguna parte). FWIW, no hay un comando git para eliminar automáticamente las ramas de seguimiento locales si desaparece una rama remota.

John Szakmeister
fuente
22
Esto hace un mejor trabajo al abordar la cuestión general al explicar las diferencias pertinentes. También responde preguntas adicionales que tuve de la anterior.
gogogadgetinternet
14
Este comando mostrará una lista de todas las ramas locales que no tienen una rama remota correspondiente. Usted podría tubería que esto xargs git branch -D, pero tenga en cuenta que las nuevas ramas que ha creado, pero nunca inserta en el servidor o será borrada, por lo que la banda de rodamiento con cuidado: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton
44
@ Seed No, no lo hace. :-( Solo elimina las referencias de seguimiento remoto local. Acabo de verificar esto con la versión 2.7.0.
John Szakmeister
1
@ BlueRaja-DannyPflughoeft Tenga cuidado con ese enfoque. Por ejemplo, dependiendo de cómo haga sus ramas estables, pueden parecer fusionadas en la rama maestra, y usted terminaría eliminándolas. No es una gran pérdida aquí, ya que no los está eliminando del servidor, pero si tenía alguna configuración especial que configuró, se perdería cuando se elimine la rama.
John Szakmeister
1
@Cloud No es del todo cierto. Las referencias se pueden empaquetar (ver el packed-refsarchivo en el .gitárea), por lo que no es necesariamente una simple cuestión de eliminarlas a través del explorador de archivos. Es mejor usar los comandos para asegurarse de que ambos se cuiden correctamente.
John Szakmeister
55

git remote pruney git fetch --prunehaz lo mismo: eliminando las referencias a las ramas que no existen en el control remoto, como dijiste. El segundo comando se conecta al control remoto y obtiene sus ramas actuales antes de la poda.

Sin embargo, no toca las sucursales locales que ha desprotegido, que simplemente puede eliminar con

git branch -d  random_branch_I_want_deleted

Reemplace -dpor -Dsi la rama no se fusiona en otro lugar

git prune hace algo diferente, purga objetos inalcanzables, esas confirmaciones a las que no se puede acceder en ninguna rama o etiqueta, y por lo tanto ya no son necesarias.

CharlesB
fuente
1
Sé que parece obvio, pero git pruneno solo busca ramas y etiquetas, sino también todas las demás referencias.
Entonces, en mi caso, ¿por qué no funcionaría git podar? ¿Porque no le importan las sucursales locales, sino las referencias remotas? Gracias por la información concisa.
gogogadgetinternet
@hvd ¿qué tipo de referencias hay además de ramas y etiquetas?
CharlesB
@gogogadgetinternet sí exactamente. (suponiendo que git remote prune
quisieras
44
OMI, la convención de nomenclatura de git de usar "podar" tanto para la recolección de objetos como para la limpieza de referencias es donde se establece la confusión. Pero esa es solo una de las muchas perplejas de la interfaz de usuario, en git. :-)
torek
14

En caso de que alguien esté interesado. Aquí hay un script de shell rápido que eliminará todas las ramas locales que no se rastrean de forma remota. Una advertencia: esto eliminará cualquier rama que no se rastree de forma remota, independientemente de si se fusionó o no.

Si ven algún problema con esto, háganmelo saber y lo solucionaré (etc., etc.)

Guárdelo en un archivo llamado git-rm-ntb(llámelo como quiera) PATHy ejecute:

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
fuente
¡Gracias! Buen toque con la salida de color ;-)
BVengerov
2
¿No sería más seguro $ (git branch -d $ i) para eliminar solo las ramas fusionadas?
user2012677
Muy útil muchas gracias !!!
Robin Hartland
Las opciones más seguras se analizan en stackoverflow.com/questions/7726949/…
Michael Freidgeim
13

Tenga en cuenta que una diferencia entre git remote --pruney git fetch --prunese está arreglando, con commit 10a6cc8 , por Tom Miller ( tmiller) (para git 1.9 / 2.0, Q1 2014):

Cuando tenemos una rama de seguimiento remoto llamada " frotz/nitfol" de una búsqueda anterior, y el flujo ascendente ahora tiene una rama llamada "** frotz " **, fetchno se eliminaría " frotz/nitfol" con un " git fetch --prune" del flujo ascendente.
git informaría al usuario que use " git remote prune" para solucionar el problema.

Entonces: cuando un repositorio ascendente tiene una sucursal ("frotz") con el mismo nombre que una jerarquía de sucursal ("frotz / xxx", una posible convención de nomenclatura de sucursal ), git remote --prunetuvo éxito (en la limpieza de la sucursal de seguimiento remoto de su repositorio) , pero git fetch --pruneestaba fallando.

Ya no:

Cambie la forma en que " fetch --prune" funciona moviendo la operación de poda antes de la operación de recuperación.
De esta manera, en lugar de advertir al usuario de un conflicto, lo corrige automáticamente.

VonC
fuente