Limpiar viejas ramas remotas de git

694

Trabajo desde dos computadoras diferentes (A y B) y almaceno un control remoto git común en el directorio de Dropbox.

Digamos que tengo dos ramas, master y devel. Ambos están rastreando sus contrapartes remotas origin / master y origin / devel.

Ahora, mientras estoy en la computadora A, elimino el desarrollo de sucursales, en local y remoto.

git push origin :heads/devel
git branch -d devel

Ejecutando git branch -aen la computadora A, obtengo la siguiente lista de ramas.

  • Maestro
  • origen / CABEZA
  • origen / maestro

Ejecutando git fetchen la computadora B, puedo eliminar la rama de desarrollo local con git branch -d devel, pero no puedo eliminar la rama de desarrollo remoto.

git push origin :heads/devel devuelve los siguientes mensajes de error.

error: no se puede enviar a un destino no calificado: heads / proxy3d
La especificación de referencia de destino no coincide con una referencia existente en el control remoto ni comienza con referencias /, y no podemos adivinar un prefijo basado en la referencia de origen.
fatal: el extremo remoto colgó inesperadamente

git branch -a todavía enumera origen / desarrollo en las ramas remotas.

¿Cómo puedo limpiar las ramas remotas de la computadora B?

Jayesh
fuente
44
Alguien que lo intentó me dijo que los repositorios de git en las carpetas de Dropbox son un poco frágiles (pero sin detalles adicionales).
Thorbjørn Ravn Andersen
55
@ ThorbjørnRavnAndersen probablemente porque tienes que esperar para asegurarte de que se sincronice completamente cada vez que confirmes, antes de que puedas estar seguro de que es seguro usarlo en la otra máquina (y se requiere otra sincronización incluso en ese momento).
ataulm

Respuestas:

1240

Primero, ¿cuál es el resultado de la git branch -amáquina B?

En segundo lugar, ya ha eliminado heads/develel origin, así que por eso no puede eliminarla de la máquina B.

Tratar

git branch -r -d origin/devel

o

git remote prune origin

o

git fetch origin --prune

y siéntase libre de agregar --dry-runal final de su gitdeclaración para ver el resultado de ejecutarlo sin ejecutarlo realmente.

Documentos para git remote pruney git branch.

Jakub Narębski
fuente
50
git remote prune originfuncionó para mí => * [pruned] origin/my-old-branch
Hari Karam Singh
126
git remote prune origin --dry-runmuestra lo que se eliminaría sin hacerlo realmente.
Wolfram Arnold
30
git fetch origin --prunefue perfecto para eliminar ramas eliminadas
Sébastien Barbieri
10
Si tiene una sucursal local que rastrea un control remoto que se ha ido, esto no eliminará nada. Para aquellos, parece git branch -vvseguido por git branch -D branchnamey finalmente la ciruela es la mejor manera.
Roman Starkov
77
Puede configurar la poda para que se realice automáticamente en extracción / recuperación configurando la siguiente opción:git config remote.origin.prune true
Patrick James McDougle, el
100

Considera correr:

git fetch --prune

De forma regular en cada repositorio para eliminar sucursales locales que han estado rastreando una sucursal remota que se elimina (ya no existe en el repositorio GIT remoto).

Esto se puede simplificar aún más mediante

git config remote.origin.prune true

esto es un per-repo configuración que hará que cualquier futuro sea git fetch or git pullautomáticamente pode .

Para configurar esto para su usuario, también puede editar el archivo .gitconfig global y agregar

[fetch]
    prune = true

Sin embargo, se recomienda que esto se haga usando el siguiente comando:

git config --global fetch.prune true

o para aplicarlo en todo el sistema (no solo para el usuario)

git config --system fetch.prune true
Arif
fuente
14

Aquí hay un script bash que puede hacerlo por usted. Es la versión modificada del http://snippets.freerobby.com/post/491644841/remove-merged-branches-in-git script. Mi modificación le permite admitir diferentes ubicaciones remotas.

#!/bin/bash

current_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
if [ "$current_branch" != "master" ]; then
  echo "WARNING: You are on branch $current_branch, NOT master."
fi
echo -e "Fetching merged branches...\n"

git remote update --prune
remote_branches=$(git branch -r --merged | grep -v '/master$' | grep -v "/$current_branch$")
local_branches=$(git branch --merged | grep -v 'master$' | grep -v "$current_branch$")
if [ -z "$remote_branches" ] && [ -z "$local_branches" ]; then
  echo "No existing branches have been merged into $current_branch."
else
  echo "This will remove the following branches:"
  if [ -n "$remote_branches" ]; then
echo "$remote_branches"
  fi
  if [ -n "$local_branches" ]; then
echo "$local_branches"
  fi
  read -p "Continue? (y/n): " -n 1 choice
  echo
  if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then
    remotes=`echo "$remote_branches" | sed 's/\(.*\)\/\(.*\)/\1/g' | sort -u`
# Remove remote branches
for remote in $remotes
do
        branches=`echo "$remote_branches" | grep "$remote/" | sed 's/\(.*\)\/\(.*\)/:\2 /g' | tr -d '\n'`
        git push $remote $branches 
done

# Remove local branches
git branch -d `git branch --merged | grep -v 'master$' | grep -v "$current_branch$" | sed 's/origin\///g' | tr -d '\n'`
  else
echo "No branches removed."
  fi
fi
Alex Amiryan
fuente
1
Esto se rompió en una rama llamada "origen / característica / mybranch", no estoy seguro de por qué.
Stavros Korokithakis
El problema está en los dos seds. Reemplazar sed 's/\(.*\)\/\(.*\)/\1/g'porsed 's/\([^/]*\)\/\(.*\)/\1/g'
Benoît Latinier
14

Este comando "ejecutará en seco" eliminará todas las originramas combinadas remotas ( ), aparte de master. Puede cambiar eso o agregar ramas adicionales después del maestro:grep -v for-example-your-branch-here |

git branch -r --merged | 
  grep origin | 
  grep -v '>' | 
  grep -v master | 
  xargs -L1 | 
  awk '{sub(/origin\//,"");print}'| 
  xargs git push origin --delete --dry-run

Si se ve bien, retire el --dry-run. Además, es posible que desee probar esto en una bifurcación primero.

weston
fuente
Agradable. Mi ajuste usando perl en lugar del primer xargs + awk: git branch -r --merged | origen grep | grep -v '>' | grep -v master | perl -lpe '($ basura, $ _) = división (/ \ //, $ _, 2)' | xargs git push origin --delete
Andrew
6

Si git branch -rmuestra muchas ramas de seguimiento remoto que no le interesan y desea eliminarlas solo de local, use el siguiente comando:

git branch -r | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd

Una versión más segura sería eliminar solo las fusionadas:

git branch -r --merged | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd

Esto podría ser útil para proyectos grandes, donde no necesita las ramas de características de otros compañeros de equipo, pero hay muchas ramas de seguimiento remoto obtenidas en el clon inicial.

Sin embargo, este paso por sí solo no es suficiente, porque esas ramas de seguimiento remoto eliminadas volverían a aparecer a continuación git fetch.

Para dejar de buscar esas ramas de seguimiento remoto, debe especificar explícitamente las referencias para recuperar en .git / config :

[remote "origin"]
  # fetch = +refs/heads/*:refs/remotes/origin/*    ### don't fetch everything
  fetch = +refs/heads/master:refs/remotes/origin/master
  fetch = +refs/heads/develop:refs/remotes/origin/develop
  fetch = +refs/heads/release/*:refs/remotes/origin/release/*

En el ejemplo anterior, sólo recuperamos master, developy liberamos ramas, sentir la libertad de adaptar como sea necesario.

revs ryenus
fuente
1
Gracias, el primer comando funcionó para mí. También puede buscar solo una rama con git fetch origin branchnameo crear un alias ft = "!f() { git fetch origin $(git rev-parse --abbrev-ref HEAD);}; f"para recuperar solo la rama actual (mi favorito personal). Salud.
GiovanyMoreno
Quizás OT pero ¿qué significa la -rbandera? Veo que xargstiene una -Rdiscusión pero no encontré nada al respecto -r. Quiero decir, en teoría, si recuerdo correctamente cómo xargsfunciona, incluso sin -rque debería funcionar.
Aldo 'xoen' Giambelluca
Me gusta esta respuesta Un par de notas (indicando lo obvio realmente). Es posible "obtener una vista previa" de las ramas que se eliminarán eliminando la última tubería y el último comando | xargs git branch -d. Además, al eliminar la -ropción ( --remotes) de git branch -d, solo limpiamos las ramas locales (lo cual puede ser suficiente teniendo en cuenta que, de forma predeterminada git branch, no muestra ramas remotas de todos modos).
Aldo 'xoen' Giambelluca
¡Gracias! Esto era exactamente lo que necesitaba. Otras soluciones sugieren empujar ramas locales podadas al control remoto para eliminarlas allí también, pero eso no ayuda cuando el control remoto se ha eliminado o ya no existe. Este enfoque eliminó las referencias locales a las ramas remotas a las que ya no tengo acceso.
precaución bug
@aldo, acerca de xargs -r, es decir --no-run-if-empty, es una extensión GNU significa, aquí hay una buena explicación .
ryenus
4

¡La eliminación siempre es una tarea desafiante y puede ser peligrosa ! Por lo tanto, primero ejecute el siguiente comando para ver qué sucederá:

git push --all --prune --dry-run

Al hacerlo como lo anterior, git le proporcionará una lista de lo que sucedería si se ejecuta el siguiente comando.

Luego ejecute el siguiente comando para eliminar todas las ramas del repositorio remoto que no están en su repositorio local:

git push --all --prune
Timothy LJ Stewart
fuente
10
Este comando parece ser peligroso ... Logró eliminar lo que quería (y no pudo hacer con al menos cuatro de las respuestas anteriores). Pero también eliminó otras cuatro ramas de desarrollo. Git chupa absolutamente ...
JWW
2
Esas sucursales no deben haber estado en su local. Sin embargo, no todo está perdido. Git commit, Git reflog y luego git reset --hard <checksum>. Literalmente puede hacer cualquier commit (también conocido como guardar) y otros con esto. Branch es solo una etiqueta, al eliminar la etiqueta no se elimina el guardado ... siempre tendrá una suma de comprobación. Avíseme si puedo ayudar
Timothy LJ Stewart
3
Por supuesto, querrás recuperarlos bastante rápido ya que el recolector de basura de git eventualmente eliminará los commits a los que no hacen referencia las ramas.
Andrew
1
Es bueno agregar -n(ejecución en seco) a este comando para que pueda obtener una vista previa de los cambios y luego, si todo está bien, vuelva a llamarlo sin -n.
mati865
1
De acuerdo con @GuiWeinmann: esto es demasiado peligroso, especialmente sin una llamada específica para al menos ejecutar esto en seco primero.
Vivek M. Chawla
2

Aquí se explica cómo hacerlo con SourceTree (v2.3.1):
1. Haga clic en Recuperar
2. Marque "Podar ramas de seguimiento ..."
3. Presione OK
4. 😀

ingrese la descripción de la imagen aquí

Coco
fuente
1

Tendré que agregar una respuesta aquí, porque las otras respuestas no cubren mi caso o son innecesariamente complicadas. Uso github con otros desarrolladores y solo quiero que todas las sucursales locales cuyos controles remotos fueron (posiblemente fusionados y) eliminados de un github PR se eliminen de una vez de mi máquina. No, cosas comogit branch -r --merged no cubren las ramas que no se fusionaron localmente, o las que no se fusionaron (abandonaron), etc., por lo que se necesita una solución diferente.

De todos modos, el primer paso lo obtuve de otras respuestas:

git fetch --prune

Una carrera en seco de git remote prune originParecía que haría lo mismo en mi caso, así que elegí la versión más corta para que sea simple.

Ahora, a git branch -vdebería marcar las ramas cuyos controles remotos se eliminan como [gone]. Por lo tanto, todo lo que necesito hacer es:

git branch -v|grep \\[gone\\]|awk '{print $1}'|xargs -I{} git branch -D {}

Tan simple como eso, elimina todo lo que quiero para el escenario anterior.

La xargssintaxis menos común es que también funciona en Mac y BSD además de Linux. Cuidado, este comando no es una ejecución en seco, por lo que forzará la eliminación de todas las ramas marcadas como [gone]. Obviamente, al ser esto, nada se ha ido para siempre, si ve que se eliminan las ramas que recuerda que quería mantener, siempre puede recuperarlas (el comando anterior habrá enumerado su hash en la eliminación, por lo que es muy simple) git checkout -b <branch> <hash>.

Ecuador
fuente
0
# First use prune --dry-run to filter+delete the local branches
git remote prune origin --dry-run \
  | grep origin/ \
  | sed 's,.*origin/,,g' \
  | xargs git branch -D

# Second delete the remote refs without --dry-run
git remote prune origin

Pode las mismas ramas de referencias locales y remotas (en mi ejemplo de origin).

Jan Winter
fuente