Eliminar las ramas de seguimiento que ya no están en el control remoto


1173

¿Existe una manera simple de eliminar todas las ramas de seguimiento cuyo equivalente remoto ya no existe?

Ejemplo:

Sucursales (locales y remotas)

  • Maestro
  • origen / maestro
  • origen / corrección de errores-a
  • origen / corrección de errores-b
  • origen / corrección de errores-c

A nivel local, solo tengo una rama maestra. Ahora necesito trabajar en bug-fix-a , así que lo reviso, trabajo y empujo los cambios al control remoto. Luego hago lo mismo con bug-fix-b .

Sucursales (locales y remotas)

  • Maestro
  • bug-fix-a
  • bug-fix-b
  • origen / maestro
  • origen / corrección de errores-a
  • origen / corrección de errores-b
  • origen / corrección de errores-c

Ahora tengo las sucursales locales master , bug-fix-a , bug-fix-b . El mantenedor de la rama maestra fusionará mis cambios en maestro y eliminará todas las ramas que ya ha fusionado.

Entonces el estado actual es ahora:

Sucursales (locales y remotas)

  • Maestro
  • bug-fix-a
  • bug-fix-b
  • origen / maestro
  • origen / corrección de errores-c

Ahora me gustaría llamar a algún comando para eliminar ramas (en este caso, bug-fix-a , bug-fix-b ), que ya no están representadas en el repositorio remoto.

Sería algo así como el comando existente git remote prune origin, pero más parecido git local prune origin.

Respuestas:


1282

git remote prune origin ciruelas pasas que no se encuentran en el control remoto.

git branch --merged enumera las ramas que se han fusionado en la rama actual.

xargs git branch -d elimina las ramas enumeradas en la entrada estándar.

Tenga cuidado al eliminar las ramas enumeradas por git branch --merged. La lista podría incluir masteru otras ramas que preferiría no eliminar.

Para tener la oportunidad de editar la lista antes de eliminar ramas, puede hacer lo siguiente en una línea:

git branch --merged >/tmp/merged-branches && \
  vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches

17
La primera línea de las ramas fusionadas está * masteren mi sistema. El siguiente comando funcionó para mí:git branch -d $(git branch --merged |tail -n +2)
Trendfischer

99
Si estoy encendido, developentonces git branch --mergedincluye master! Probablemente (¡definitivamente!) No quieras eliminar eso. También creo que debería ser git branch -ddonde minúsculas -dsignifica "eliminación segura", por ejemplo, solo eliminar si se fusionó.
thom_nic

11
Parece que allí se proporciona una solución mejorada .
Sergey Brunov

34
Eliminar combinado es útil, pero no es lo mismo que "eliminar ramas que no están en remoto".
dlsso

11
Solo use grep para excluir al maestro:git branch --merged | grep -v "master" >/tmp/merged-branches && vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches
geniass

592

Después del comando

git fetch -p

elimina las referencias remotas, cuando ejecuta

git branch -vv

mostrará "ido" como el estado remoto. Por ejemplo,

$ git branch -vv
  master                 b900de9 [origin/master: behind 4] Fixed bug
  release/v3.8           fdd2f4e [origin/release/v3.8: behind 2] Fixed bug
  release/v3.9           0d680d0 [origin/release/v3.9: behind 2] Updated comments
  bug/1234               57379e4 [origin/bug/1234: gone] Fixed bug

Para que pueda escribir una secuencia de comandos simple para eliminar las ramas locales que se han convertido en controles remotos:

git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done

Tenga en cuenta que lo anterior utiliza el comando "porcelana" git branchpara obtener el estado ascendente.

Otra forma de obtener este estado es utilizar el comando "plomería" git for-each-refcon la variable de interpolación %(upstream:track), que será [gone]igual que la anterior.

Este enfoque es algo más seguro, porque no hay riesgo de coincidencia accidental en parte del mensaje de confirmación.

git fetch -p && for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do git branch -D $branch; done

8
@KrzysztofWende - no en Solaris y algunos BSD y algunos OS X :)
jww

44
Parece que esto también eliminará cualquier rama que se haya "ido" en el último mensaje de confirmación.
dlsso

11
@dlsso Si el último mensaje de confirmación contiene la cadena ": desaparecido]", sí, también se eliminará. Puede hacerlo más robusto a expensas de la simplicidad al tener un awk / gawk adicional para quitar el mensaje de confirmación. git branch -vv | gawk '{print $1,$4}' | grep 'gone]' | gawk '{print $1}'
jason.rickman

2
En respuesta a mi comentario anterior (pregunta), la rama actual tiene * como primer campo. Si resulta que también está en la lista de ramas "desaparecidas", se le asignará $ 1 * y se interpretará como una especificación de archivo con awk escupiendo nombres de archivos y carpetas. He eliminado la expresión grep y awk había hacer todo el filtrado: awk '/: gone]/{if ($1!="*") print $1}'. Esto ahora funciona como se esperaba.
rhaben

55
@ahmedbhs ya que el comando usa comillas simples 'necesita usar comillas dobles "para rodear todo el comando. Además, ¡los alias git que son comandos de shell (como este) requieren! al principio. Esto funciona para mí:test = "!git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done"
jason.rickman

307

La mayoría de estas respuestas no responden realmente la pregunta original. Hice un montón de excavación y esta fue la solución más limpia que encontré. Aquí hay una versión un poco más completa de esa respuesta:

  1. Echa un vistazo a tu sucursal predeterminada. Generalmentegit checkout master
  2. correr git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Explicación:

Funciona podando sus ramas de rastreo y luego eliminando las locales que muestran que se han "ido" git branch -vv.

Notas:

Si su idioma está configurado en algo diferente al inglés, deberá cambiar gonea la palabra apropiada. Las ramas que solo son locales no se tocarán. Las ramas que se han eliminado en remoto pero que no se fusionaron mostrarán una notificación pero no se eliminarán en local. Si desea eliminarlos, también cambie -da -D.


2
para el sistema operativo francés desaparecido tiene que ser cambiado por disparue
Mohamed EL HABIB

44
debería agregar LANG = en_US antes de git branch para forzar el inglés: git fetch --prune && LANG = en_US git branch -vv | awk '/: gone] / {print $ 1}' | xargs git branch -d
Mohamed EL HABIB

3
Agregaría git checkout master && ...al comienzo del comando.
iarroyo

2
Esto es peligroso cuando estás en una rama que se supone que debe eliminarse; en ese caso, la primera columna es '*', que luego se pasa a xargs. Para mejorar esto, agregue la tira del carácter '*' antes de pasar la salida a awk: sed -e 's / ^ * //'
meeee

66
¡Cuidado! Hay un caso extremo que dará como resultado la eliminación de todas sus sucursales locales: si actualmente se encuentra en una rama que se eliminó en forma remota, la salida de git branch -vvcomenzará con un asterisco que finalmente se ejecutará git branch -d *. Aquí hay una versión parcheada que ignorará las líneas con asterisco:git branch -vv | grep ': gone]'| grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d
kcm

210

Normalmente no respondería una pregunta que ya tiene 16 respuestas, pero todas las demás respuestas son incorrectas, y la respuesta correcta es muy simple. La pregunta dice: "¿Hay una manera simple de eliminar todas las ramas de seguimiento cuyo equivalente remoto ya no existe?"

Si "simple" significa eliminarlos de una vez, no son frágiles, no son peligrosos y no dependen de herramientas que no todos los lectores tendrán, entonces la respuesta correcta es: no.

Algunas respuestas son simples, pero no hacen lo que se les pidió. Otros hacen lo que se les pidió, pero no son simples: todos confían en analizar la salida de Git a través de comandos de manipulación de texto o lenguajes de script, que pueden no estar presentes en todos los sistemas. Además de eso, la mayoría de las sugerencias usan comandos de porcelana, cuya salida no está diseñada para ser analizada por script ("porcelain" se refiere a los comandos destinados a la operación humana; los scripts deben usar los comandos de "plomería" de nivel inferior).

Otras lecturas:


Si desea hacer esto de forma segura, para el caso de uso en la pregunta (ramas de seguimiento de recolección de basura que se han eliminado en el servidor pero que todavía existen como ramas locales) y solo con comandos Git de alto nivel, debe

  • git fetch --prune(o git fetch -p, que es un alias, o git prune remote originque hace lo mismo sin buscar, y probablemente no es lo que quieres la mayor parte del tiempo).
  • Tenga en cuenta las ramas remotas que se informan como eliminadas. O, para encontrarlos más adelante, git branch -v(cualquier rama de seguimiento huérfana se marcará "[desaparecido]").
  • git branch -d [branch_name] en cada rama de seguimiento huérfana

(que es lo que proponen algunas de las otras respuestas).

Si desea escribir una solución, entonces for-each-refes su punto de partida, como en la respuesta de Mark Longair aquí y esta respuesta a otra pregunta , pero no puedo ver una manera de explotarla sin escribir un bucle de script de shell, o usar xargs o algo .


Explicación de fondo

Para comprender lo que está sucediendo, debe apreciar que, en la situación de rastrear sucursales, no tiene una, sino tres. (Y recuerde que "rama" significa simplemente un puntero a una confirmación).

Dada una rama de seguimiento feature/X, el repositorio remoto (servidor) tendrá esta rama y la llamará feature/X. Su repositorio local tiene una rama remotes/origin/feature/Xque significa: "Esto es lo que el control remoto me dijo que era su característica / rama X, la última vez que hablamos", y finalmente, el repositorio local tiene una rama feature/Xque apunta a su último commit y está configurado para "seguimiento" remotes/origin/feature/X, lo que significa que puede tirar y empujar para mantenerlos alineados.

En algún momento, alguien ha eliminado el feature/Xen el control remoto. A partir de ese momento, te quedarás con tu local feature/X(que probablemente ya no quieras más, ya que presumiblemente el trabajo en la función X está presumiblemente terminado), y tu remotes/origin/feature/Xque sin duda es inútil porque su único propósito era recordar el estado de la rama del servidor .

Y Git le permitirá limpiar automáticamente el redundante remotes/origin/feature/X, eso es lo que git fetch --prunehace, pero por alguna razón, no le permite eliminar automáticamente el suyo feature/X... a pesar de que feature/Xtodavía contiene la información de seguimiento huérfana, por lo que tiene la información para identificar antiguas ramas de seguimiento que se han fusionado completamente. (Después de todo, se puede dar que la información que le permite hacer la operación por sí mismo la mano.)


3
Esta es una respuesta mucho más segura. Además, funcionará si usa un flujo de trabajo "Squash and Merge", a diferencia de la respuesta seleccionada.
Jake Levitt

3
Esto realmente solo responde a la parte fácil de la pregunta "cómo encontrar ramas desaparecidas" (y eso con el mismo comando que ya publicó jason.rickman en 2015), pero luego le dice que elimine todas las ramas manualmente, que es exactamente lo que el OP no quiere hacer
Voo

44
@Voo Ese es el punto de la respuesta: no decirle cómo hacerlo (eso es solo una ventaja) sino decirle que la respuesta es que no hay una manera fácil y sencilla de hacerlo. Cuál es la respuesta correcta a la pregunta tal como fue formulada.
Andrew Spencer

1
"Si desea hacer esto de manera segura, para el caso de uso en la pregunta ...", que niega la mayoría de las afirmaciones del primer párrafo. "[Git] puede proporcionarle la información que le permite realizar la operación usted mismo": somos programadores, por lo tanto, dada la información en forma de lista (que se muestra), la tarea sigue siendo simple (p. Ej. xargs). También hay git aliasque simplificar una serie de casos.
user2864740

2
@ user2864740 Algunos lectores pueden considerar que escribir un pequeño script bash está dentro de su definición de "simple", y esa es una posición perfectamente defendible, pero di mi propia interpretación de "simple" en el segundo párrafo y el resto de la respuesta es consistente con eso. Y en defensa de mi interpretación ciertamente restrictiva, no todos los usuarios de Git tienen xargso critican, y probablemente no están buscando un desafío de microcodificación, sino una respuesta rápida que pueden aplicar de manera segura sin distraerse más de su objetivo real. .
Andrew Spencer

52

Encontré la respuesta aquí: ¿Cómo puedo eliminar todas las ramas git que se han fusionado?

git branch --merged | grep -v "\*" | xargs -n 1 git branch -d

Asegúrate de seguir siendo maestro

Puede asegurarse de que master, o cualquier otra rama para el caso, no se elimine agregando otra grepdespués de la primera. En ese caso irías:

git branch --merged | grep -v "\*" | grep -v "YOUR_BRANCH_TO_KEEP" | xargs -n 1 git branch -d

Entonces, si quisiéramos mantener master, developy stagingpor ejemplo, iríamos:

git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d

Haz de esto un alias

Como es un poco largo, es posible que desee agregar un alias a su .zshrco .bashrc. El mío se llama gbpurge(para git branches purge):

alias gbpurge='git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d'

Luego recarga tu .bashrco .zshrc:

. ~/.bashrc

o

. ~/.zshrc

8
Útil, pero no es lo mismo que "eliminar ramas no en remoto"
dlsso

Gran respuesta @karlingen, tengo esto permanentemente como gbpurge en todos mis entornos de desarrollo
Peter Dolan

50

Solución de Windows

Para Microsoft Windows Powershell:

git checkout master; git remote update origin --prune; git branch -vv | Select-String -Pattern ": gone]" | % { $_.toString().Trim().Split(" ")[0]} | % {git branch -d $_}

Explicación

git checkout master cambia a la rama maestra

git remote update origin --prune ciruelas pasas ramas remotas

git branch -vvobtiene una salida detallada de todas las ramas ( referencia de git )

Select-String -Pattern ": gone]" obtiene solo los registros donde se han eliminado del control remoto.

% { $_.toString().Trim().Split(" ")[0]} obtener el nombre de la sucursal

% {git branch -d $_} elimina la rama


3
Gracias por esto, aunque tuve que agregar un .Trim()after .toString()para eliminar dos espacios antes del nombre de la rama.
Mateo

2
Gracias por este comando. Tuve que cambiarlo git branch -dpara git branch -Dobtener el error, la rama no está completamente fusionada.
markieo

1
@markieo Probablemente ya lo sepa, pero para cualquier otra persona, git branch -Des destructivo y eliminará el trabajo local que aún no ha presionado. -dsiempre es seguro, solo ten cuidado -D.
Nate Barbettini

33

La coincidencia de patrones para "desaparecido" en la mayoría de las otras soluciones me dio un poco de miedo. Para estar más seguro, esto usa la --formatbandera para extraer el estado de seguimiento ascendente de cada rama .

Necesitaba una versión compatible con Windows, por lo que esto elimina todas las ramas que se enumeran como "desaparecidas" usando Powershell:

git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | 
    ? { $_ -ne "" } | 
    % { git branch -D $_ }

La primera línea enumera el nombre de las sucursales locales cuya rama ascendente está "desaparecida". La siguiente línea elimina las líneas en blanco (que se muestran para las ramas que no están "desaparecidas"), luego el nombre de la rama se pasa al comando para eliminar la rama.


66
La --formatopción parece ser bastante nueva; Necesitaba actualizar git de 2.10.algo a 2.16.3 para obtenerlo. Esta es mi modificación para sistemas Linux-ish:git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)" | sed 's,^refs/heads/,,' | grep . | xargs git branch -D
bxm

2
Esta es la mejor solución hasta ahora. Una sugerencia es usar refname:short. Entonces puedes borrar la línea% { $_ -replace '^refs/heads/', '' }
ioudas

2
Esta es una gran solución para Windows. Lo estoy usando así porque es más legible. git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | where { $_ -ne "" } | foreach { git branch -d $_ }También es probablemente una buena idea usarlo en -dlugar de -D. La eliminación forzada no debería ser necesaria para las ramas que ya no están en remoto.
Rubanov

31

Elimine todas las ramas que se han fusionado en master, pero no intente eliminar el master en sí:

git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)

o agrega un alias:

alias gitcleanlocal="git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)"

Explicación:

git checkout master caja maestra de pago

git pull origin master asegúrese de que la sucursal local tenga todos los cambios remotos combinados

git fetch -p eliminar referencias a ramas remotas que se han eliminado

git branch -d $(git branch master --merged | grep master -v) elimine todas las ramas que se han fusionado en master, pero no intente eliminar el master


3
Una nota, esto es muy útil, pero también eliminará aquellas ramas que nunca han sido empujadas al control remoto. Es más seguro enumerar solo las diferencias y luego copiar lo que realmente desea eliminar en el git branch -Dcomando
Zefiryn

Solo para aclarar, Zefiryn se refiere al uso de la opción -D que no forma parte de la línea única.
cs01

1
O utilice las minúsculas, git branch -dque deberían emitir una advertencia sobre ramas no empujadas.
acme

16
git fetch -p

Esto eliminará cualquier rama que ya no exista en el control remoto.


64
Esto elimina las referencias remotas, pero no las ramas locales en sí. Si bien es un comando útil, no creo que esto responda la pregunta de OP.
thataustin

¿Qué? Esto elimina sucursales locales para mí.
Alex Hall

1
@AlexHall El repositorio remoto tiene una rama X; usted git checkout X; ahora su repositorio tiene una rama de seguimiento (local) Xy una rama remota origin/X; el repositorio remoto elimina X; usted git fetch-p; en su repositorio local, no solo origin/Xsino que también Xse han eliminado. ¿Eso es lo que estás diciendo?
Andrew Spencer

16

Sin embargo, otra respuesta para la pila, basada en gran medida en la respuesta de Patrick (que me gusta porque parece eliminar cualquier ambigüedad sobre dónde gone]coincidirá en la git branchsalida) pero agregando un * nix doblado.

En su forma más simple:

git branch --list --format \
  "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" \
  | xargs git branch -D

Tengo esto envuelto en un git-gonescript en mi camino:

#!/usr/bin/env bash

action() {
  ${DELETE} && xargs git branch -D || cat
}

get_gone() {
  git branch --list --format \
    "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)"
}

main() {
  DELETE=false
  while [ $# -gt 0 ] ; do
    case "${1}" in
      (-[dD] | --delete) DELETE=true ;;
    esac
    shift
  done
  get_gone | action
}

main "${@}"

NB: la --formatopción parece ser bastante nueva; Necesitaba actualizar git de 2.10.algo a 2.16.3 para obtenerlo.

EDIT: ajustado para incluir sugerencia sobre refname:shortde Benjamin W.

NB2: solo he probado bash, de ahí el hashbang, pero probablemente sea portátil sh.


¿No puedes saltarte la sedparte usando %(refname:short)la primera línea?
Benjamin W.

Parece funcionar para mí, y lo tengo como un buen alias de Git ahora: ¡la solución más limpia de todas aquí!
Benjamin W.

2.13 introducido --format. Otra forma de saltear la sedparte es usarla git update-ref -d. Tenga en cuenta que esto es probablemente algo inseguro, el uso git for-each-refes más seguro aquí (dado --shell).
gsnedders

Gran respuesta, y me ha ayudado a responder mis propias preguntas cuando me empeño --formaten hacerlo yo mismo. Solo una pregunta: ¿por qué #!bash? Todo aquí me parece portátil sh.
Toby Speight

Eso es solo mi repetitivo normal, y no lo había probado en otro lugar.
bxm

15

Puede ser útil para algunos, una línea simple para borrar todas las sucursales locales, excepto master y desarrollo

git branch | grep -v "master" | grep -v "develop" | xargs git branch -D

Respuesta impresionante! Me gusta cómo se puede jugar fácilmente con el 'git branch | grep -v "master" | grep -v "develop"tipo de cosas antes de comprometerse a agregar la parte de eliminación del comando. 👏😊
finneycanhelp

Pero esto no responde a la pregunta anterior. Esto eliminará ramas aunque el control remoto todavía esté allí.
Jim.B

13

Esto eliminará todas las ramificaciones locales fusionadas, excepto la referencia maestra local y la que se está utilizando actualmente:

git branch --merged | grep -v "*" | grep -v "master" | xargs git branch -d

Y esto eliminará todas las ramas que ya se han eliminado del repositorio remoto al que hace referencia " origen ", pero que todavía están disponibles localmente en " control remoto / origen ".

git remote prune origin

git branch -vv | grep 'gone]' | grep -v "\*" | awk '{print $1}' | xargs -r git branch -d Explicación: Prefiero reemplazar el git branch --mergedby git branch -vvpara mostrar el estado (desaparecido) porque el anterior git branch --mergedtambién puede mostrar el maestro
jpmottin

13

No creo que haya un comando incorporado para hacer esto, pero es seguro hacer lo siguiente:

git checkout master
git branch -d bug-fix-a

Cuando lo use -d, git se negará a eliminar la rama a menos que esté completamente fusionada HEADo en su rama de seguimiento remoto ascendente. Por lo tanto, siempre puede recorrer la salida git for-each-refe intentar eliminar cada rama. El problema con ese enfoque es que sospecho que probablemente no desee bug-fix-dser eliminado solo porque origin/bug-fix-dcontiene su historial. En su lugar, podría crear un script similar a lo siguiente:

#!/bin/sh

git checkout master &&
for r in $(git for-each-ref refs/heads --format='%(refname:short)')
do
  if [ x$(git merge-base master "$r") = x$(git rev-parse --verify "$r") ]
  then
    if [ "$r" != "master" ]
    then
      git branch -d "$r"
    fi
  fi
done

Advertencia: no he probado este script, úselo solo con cuidado ...


Me tomé la libertad de editar el guión. Todavía no dará ninguna garantía, pero funciona y parece funcionar ahora.
fwielstra

13

TL; DR:

Eliminar TODAS las sucursales locales que no están en remoto

git fetch -p && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -D

Elimine TODAS las ramas locales que no estén en el control remoto Y que estén completamente fusionadas Y que no se usen como se dijo anteriormente en muchas respuestas.

git fetch -p && git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -d

Explicación

  • git fetch -p podará todas las ramas que ya no existan en el control remoto
  • git branch -vv imprimirá sucursales locales y la rama podada se etiquetará con gone
  • grep ': gone]' selecciona solo la rama que se ha ido
  • awk '{print $1}' filtrar la salida para mostrar solo el nombre de las ramas
  • xargs git branch -D recorrerá todas las líneas (ramas) y forzará a eliminar esta rama

Por qué git branch -Dy no git branch -dmás tendrá para las ramas que no están completamente fusionadas.

error: The branch 'xxx' is not fully merged.

1
¿Querías decir remoto en lugar de maestro?
tinos

8

Podrías hacer esto:

git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d

7

Según la información anterior, esto funcionó para mí:

git br -d `git br -vv | grep ': gone] ' | awk '{print $1}' | xargs`

Elimina todas las sucursales locales que están ': gone] 'en remoto.


1
Parece que esto también eliminará cualquier rama que se haya "ido" en el último mensaje de confirmación.
dlsso

También eliminará cualquier rama que tenga algún gonelugar en su nombre.
bfontaine

3
"git branch -D git branch -vv | grep ': desaparecido]' | awk '{print $ 1}' | xargs`" Esto hizo el trabajo por mí.
Muthu Ganapathy Nathan

7
grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -d

El comando anterior se puede usar para buscar ramas que se fusionan y eliminan en remoto y elimina la rama local que ya no está disponible en remoto


La mejor solución hasta ahora, aunque la cambié grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -Dpara forzar la eliminación de todo
Shoaib

Peligroso si alguno de los nombres de sus sucursales contiene la subcadena goneen alguna parte de ellos (por ejemplo usingonefunction).
Toby Speight

7

Nada de esto fue realmente bueno para mí. Quería algo que purgara todas las sucursales locales que estaban rastreando una rama remota, en origin, donde se ha eliminado la rama remota ( gone). No quería eliminar ramas locales que nunca se configuraron para rastrear una rama remota (es decir, mis ramas de desarrollo local). También quería una línea simple que solo use git, u otras herramientas simples de CLI, en lugar de escribir scripts personalizados. Terminé usando un poco de grepy awkpara hacer este simple comando.

Esto es lo que finalmente terminó en mi ~/.gitconfig:

[alias]
  prune-branches = !git remote prune origin && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -D

Aquí hay un git config --global ...comando para agregar esto fácilmente como git prune-branches:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | xargs -r git branch -d'

NOTA: en el comando config, utilizo la -dopción en git branchlugar de hacerlo -D, como lo hago en mi configuración actual. Lo uso -Dporque no quiero escuchar a Git quejarse de ramas no fusionadas. Es posible que también desee esta funcionalidad. Si es así, simplemente use en -Dlugar de -dal final de ese comando de configuración.


Me gusta el enfoque de git alias. Pregunta rápida: ¿Por qué hacer git branch -vvy codiciar : gone]y no hacer git branch -vy codiciar [gone]?
lalibi

@lalibi, buena pregunta. No me acuerdo Sospecho que algo no aparecía con solo uno v. Si git branch -v | grep '[gone]'te funciona, hazlo. Parece un poco más limpio.
Karl Wilbur

4

Basado en el consejo de Git: Eliminando viejas sucursales locales , que se parece a la solución de jason.rickman . Implementé un comando personalizado para este propósito llamado git gone usando Bash:

$ git gone
usage: git gone [-pndD] [<branch>=origin]
OPTIONS
  -p  prune remote branch
  -n  dry run: list the gone branches
  -d  delete the gone branches
  -D  delete the gone branches forcefully

EXAMPLES
git gone -pn    prune and dry run
git gone -d     delete the gone branches

git gone -pn combina la poda y enumera las ramas "desaparecidas":

$ git gone -pn
  bport/fix-server-broadcast         b472d5d2b [origin/bport/fix-server-broadcast: gone] Bump modules
  fport/rangepos                     45c857d15 [origin/fport/rangepos: gone] Bump modules

Luego puede apretar el gatillo con git gone -do git gone -D.

Notas

  • La expresión regular que utilicé es "$BRANCH/.*: gone]"donde $BRANCHnormalmente estaría origin. Esto probablemente no funcionará si su salida de Git está localizada en francés, etc.
  • Sebastian Wiesner también lo portó a Rust para usuarios de Windows. Ese también se llama git gone .

Probablemente esta debería ser la respuesta aceptada, git goneparece un alias para git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d, que resuelve el problema del OP.
alex

4

Basándose en gran medida a partir de un número de otras respuestas aquí, he terminado con la siguiente (GIT 2.13 y superior, creo), que debe funcionar en cualquier tipo UNIX shell:

git for-each-ref --shell --format='ref=%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)' refs/heads | while read entry; do eval "$entry"; [ ! -z "$ref" ] && git update-ref -d "$ref" && echo "deleted $ref"; done

Esto usa notablemente en for-each-reflugar de branch(como branches un comando de "porcelana" diseñado para una salida legible por humanos, no un procesamiento de máquina) y usa su --shellargumento para obtener una salida con escape adecuado (esto nos permite no preocuparnos por ningún carácter en el nombre de referencia).


Funciona para mí, pero también sería bueno si pudieras explicar lo que están haciendo los otros pasos en el comando. Por ejemplo, no sé qué [ ! -z "$ref" ]significa. Creo que un revestimiento múltiple ya habría ayudado. ¡Pero aún así, gracias por tu aporte!
KevinH

4

Otra respuesta más, porque ninguna de las soluciones satisface mis necesidades de elegancia y multiplataforma:

Comando para eliminar ramas locales que no están en remoto:

for b in $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads); do git branch -d $b; done

Para integrarlo con gitconfig para que pueda ejecutarse con git branch-prune:

Golpetazo

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format='\''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'\'' refs/heads); do git branch -d $b; done'

Potencia Shell

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format=''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'' refs/heads); do git branch -d $b; done'

(Necesita ayuda para encontrar un comando universal para PowerShell y bash)

¿Por qué esta respuesta es la mejor?

  • Ofrece una solución completa: agrega un git branch-prunecomando a tu git
  • Funciona bien desde Windows PowerShell
  • La idea central es @ jason.rickman 's método de prueba de balas usandogit for-each-ref
  • El análisis y el filtrado se realizan --filtersin necesidad de dependencias externas.

Explicación:

  • Agrega un nuevo alias a tu ~\.gitconfig. Después de ejecutar esto, simplemente puedes hacergit branch-prune
  • Dentro de este alias:
    • Obtiene ramas con la --prunebandera, que "poda las ramas de seguimiento remoto que ya no están en el control remoto"
    • Los usos git for-each-refy --filter, para obtener una lista de las ramas son [gone](sin control remoto)
    • Recorre esta lista y elimina la rama de forma segura

1
Gracias, @ jerry-wu por mejorar la genialidad de esta solución hasta el infinito.
Himura

@ jerry-wu y su última edición del comando git config no funciona en PowerShell ((
Himura

1

Se me ocurrió este script bash. Siempre mantener las ramas develop, qa, master.

git-clear() {
  git pull -a > /dev/null

  local branches=$(git branch --merged | grep -v 'develop' | grep -v 'master' | grep -v 'qa' | sed 's/^\s*//')
  branches=(${branches//;/ })

  if [ -z $branches ]; then
    echo 'No branches to delete...'
    return;
  fi

  echo $branches

  echo 'Do you want to delete these merged branches? (y/n)'
  read yn
  case $yn in
      [^Yy]* ) return;;
  esac

  echo 'Deleting...'

  git remote prune origin
  echo $branches | xargs git branch -d
  git branch -vv
}

1

Utilizo un método corto para hacer el truco, te recomiendo que hagas lo mismo ya que podría ahorrarte algunas horas y darte más visibilidad

Simplemente agregue el siguiente fragmento en su .bashrc (.bashprofile en macos).

git-cleaner() { git fetch --all --prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. Obtener todos los controles remotos
  2. Obtenga solo ramas fusionadas de git
  3. Eliminar de esta lista las ramas "protegidas / importantes"
  4. Retire el resto (p. Ej., Ramas limpias y fusionadas)

Tendrá que editar el grep regex para que se ajuste a sus necesidades (aquí, evita que se eliminen master, preprod y dmz)


git fetch --all --pruneHizo el truco. ¡Gracias!
LeOn - Han Li

1

Esto funcionó para mí:

git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d

oh, publiqué esto dos veces ... acabo de ver esto al revisar publicaciones. ¡Pero realmente podrías señalar al tipo que proporcionó este comando en lugar de tomarlo como tuyo!
Dwza

Lo obtengo de un foro, no intentes ser inteligente, solo toma la respuesta o
ignórala

Incluso de lo que podrías ... de todos modos ... no viniste aquí para discutir esto contigo. Como dijiste ... toma mi comentario o
ignóralo

0

No estoy seguro de cuánto tiempo, pero uso git-up ahora, que se encarga de eso.

Lo hago git upy comienza a rastrear nuevas ramas y elimina las viejas.

Solo para que quede claro, no es un comando git listo para usar: https://github.com/aanand/git-up

Por cierto, también esconde árboles sucios y hace rebases con solo git up.

Espero que sea útil para alguien


2
Esto no elimina las sucursales locales que no existen en el servidor.
Ashley

0

Aquí hay una solución que uso para la concha de pescado. Probado en Mac OS X 10.11.5, fish 2.3.0y git 2.8.3.

function git_clean_branches
  set base_branch develop

  # work from our base branch
  git checkout $base_branch

  # remove local tracking branches where the remote branch is gone
  git fetch -p

  # find all local branches that have been merged into the base branch
  # and delete any without a corresponding remote branch
  set local
  for f in (git branch --merged $base_branch | grep -v "\(master\|$base_branch\|\*\)" | awk '/\s*\w*\s*/ {print $1}')
    set local $local $f
  end

  set remote
  for f in (git branch -r | xargs basename)
    set remote $remote $f
  end

  for f in $local
    echo $remote | grep --quiet "\s$f\s"
    if [ $status -gt 0 ]
      git branch -d $f
    end
  end
end

Algunas notas

Asegúrese de configurar lo correcto base_branch. En este caso lo uso developcomo la rama base, pero podría ser cualquier cosa.

Esta parte es muy importante: grep -v "\(master\|$base_branch\|\*\)". Asegura que no elimine el maestro o su rama base.

Lo uso git branch -d <branch>como precaución adicional, para no eliminar ninguna rama que no se haya fusionado completamente con HEAD ascendente o actual.

Una forma fácil de probar es reemplazar git branch -d $fcon echo "will delete $f".

Supongo que también debería agregar: ¡UTILICE BAJO SU PROPIO RIESGO!


0

He escrito un script de Python usando GitPython para eliminar ramas locales que no existen en remoto.

    import git
    import subprocess
    from git.exc import GitCommandError
    import os

    def delete_merged_branches():
        current_dir = input("Enter repository directory:")
        repo = git.Repo(current_dir)
        git_command = git.Git(current_dir)

        # fetch the remote with prune, this will delete the remote references in local.
        for remote in repo.remotes:
            remote.fetch(prune=True)

        local_branches = [branch.name for branch in repo.branches]
        deleted_branches = []

        # deleted_branches are the branches which are deleted on remote but exists on local.
        for branch in local_branches:
            try:
                remote_name = 'origin/'+ branch
                repo.git.checkout(remote_name)
            except GitCommandError:
            # if the remote reference is not present, it means the branch is deleted on remote.
                deleted_branches.append(branch)

        for branch in deleted_branches:
            print("Deleting branch:"+branch)
            git_command.execute(["git", "branch", "-D",branch])


        # clean up the work flow.
        repo.git.checkout('master')
        repo.git.pull()

    if __name__ == '__main__':
        delete_merged_branches()

Espero que alguien lo encuentre útil, agregue comentarios si me perdí algo.


0

Si está utilizando zshShell con Oh My Zshinstalado, la forma más fácil de hacerlo de forma segura es utilizar el autocompletado incorporado.

Primero determine con qué ramas desea eliminar:

~ git branch --merged

  branch1
  branch2
  branch3
* master

esto le mostrará una lista de ramas ya fusionadas

Después de saber algunos que desea eliminar, escriba:

~ git branch -d 

Todo lo que tiene que hacer es presionar [tab] y le mostrará una lista de sucursales locales. Use tab-complete o simplemente presione [tab] nuevamente y puede recorrerlos para seleccionar una rama con [enter].

Pestaña Seleccione las ramas una y otra vez hasta que tenga una lista de ramas que desea eliminar:

~ git branch -d branch1 branch2 branch3

Ahora solo presione enter para eliminar su colección de ramas.

Si no está usando zsh en su terminal ... Consíguelo aquí.


0

Me gusta usar tuberías porque hace que el comando sea más fácil de leer.

Esta es mi solución si desea eliminar todas las ramas excepto la maestra.

git branch | grep -v master | xargs -n 1 git branch -D

Para eliminar otras ramas que coincidan con sus criterios, modifique el primer y segundo bloque.

git branch --merged | grep feature_name | xargs -n 1 git branch -D

-3

Aquí está la respuesta simple que funcionó para mí usando un cliente git:

Elimine el repositorio por completo de su máquina y luego vuelva a pagar.

No perder el tiempo con guiones arriesgados.


2
¿Qué pasa con las sucursales en las que estoy trabajando actualmente: /
Mailo Světel

@ MailoSvětel si están en su control remoto, entonces puede simplemente tirar de ellos nuevamente (¡recuerde comprometerse y enviar su último trabajo a su control remoto!)
Juan Carlos Ospina Gonzalez

En mi pregunta y en la mayoría de las respuestas, estamos tratando de evitar el trabajo innecesario. En otras palabras: lo que tengo que hacer es eliminar las ramas, ya no trabajaré más. Eliminar el repositorio, clonarlo y comenzar a rastrear las ramas nuevamente, es mucho trabajo innecesario para mí. Además de eso, podría perderse algo de trabajo :(
Mailo Světel
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.