Enumere todas las sucursales locales sin un control remoto

91

Problema: quiero una forma de eliminar todas las sucursales locales que tengo que no tienen control remoto. Es bastante fácil canalizar los nombres de las ramas en un git branch -D {branch_name}, pero ¿cómo obtengo esa lista en primer lugar?

Por ejemplo:

Creo una nueva rama sin control remoto:

$ git co -b no_upstream

Enumero todas mis sucursales, y solo hay una con control remoto

$ git branch -a
master
* no_upstream
remotes/origin/HEAD -> origin/master
remotes/origin/master

¿Qué comando puedo ejecutar para obtener no_upstreamuna respuesta?

Puedo correr git rev-parse --abbrev-ref --symbolic-full-name @{u}y eso mostrará que no tiene control remoto:

$ git rev-parse --abbrev-ref --symbolic-full-name @{u}
error: No upstream configured for branch 'no_upstream'
error: No upstream configured for branch 'no_upstream'
fatal: ambiguous argument '@{u}': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Pero como esto es un error, no me dejará usarlo o canalizarlo a otros comandos. Tengo la intención de usar esto como un alias de script de shell git-delete-unbranchedo tal vez hacer una gema súper simple comogit-branch-delete-orphans

JC Denton
fuente

Respuestas:

103

Recientemente descubrí git branch -vvcuál es la versión "muy detallada" del git branchcomando.

Genera las ramas junto con las ramas remotas si existen, en el siguiente formato:

  25-timeout-error-with-many-targets    206a5fa WIP: batch insert
  31-target-suggestions                 f5bdce6 [origin/31-target-suggestions] Create target suggestion for team and list on save
* 54-feedback-link                      b98e97c [origin/54-feedback-link] WIP: Feedback link in mail
  65-digest-double-publish              2de4150 WIP: publishing-state

Una vez que tenga esta salida bien formateada, es tan fácil como pasarla cuty awkobtener su lista:

git branch -vv | cut -c 3- | awk '$3 !~/\[/ { print $1 }'

Resultados en la siguiente salida:

25-timeout-error-with-many-targets
65-digest-double-publish

La cutparte simplemente normaliza los datos eliminando los dos primeros caracteres (incluidos *) de la salida antes de pasarlos a awk.

La awkparte imprime la primera columna si no hay corchetes en la tercera columna.

Bono: crea un alias

Facilite la ejecución creando un alias en su .gitconfigarchivo global (o donde sea):

[alias]
  local-branches = !git branch -vv | cut -c 3- | awk '$3 !~/\\[/ { print $1 }'

Importante: la barra invertida debe tener un escape en el alias o, de lo contrario, tendrá un archivo gitconfig no válido.

Bono: filtrado remoto

Si por alguna razón tiene varios controles remotos de seguimiento para diferentes sucursales, es bastante fácil especificar con qué control remoto desea verificar. Simplemente agregue el nombre remoto al patrón awk. En mi caso, es originpara que pueda hacer esto:

git branch -vv | cut -c 3- | awk '$3 !~/\[origin/ { print $1 }'
Jeremy Baker
fuente
¿ WIP:Qué es eso de todos modos ..? ¿No es eso lo que crea un comando de alijo ...?
igrek
@igrek Ese es solo un prefijo que usé en el mensaje de confirmación. Nada específico de git.
Jeremy Baker
cuando lo hago git branch -vv, incluso si existe una rama remota y coincide con mi rama local, no vería [origin/branch-name]el resultado junto con el. Tuve que hacer diffdos listas de sucursales para averiguar qué era solo local.
ryantuck
git branch -vv | grep -v origin/es suficiente para mí
no entre en pánico
2
¿No se pierden las ramas remotas marcadas gone?
Reid
36

git branch (sin opciones) enumera solo las sucursales locales, pero no sabe si están rastreando una sucursal remota o no.

Por lo general, esas ramas locales deben eliminarse una vez fusionadas master(como se ve en este número de git-sweep ):

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

Esto no es tan completo como quisiera, pero es un comienzo.

Con --merged, solo se enumerarán las ramas fusionadas en la confirmación con nombre (es decir, las ramas cuyas confirmaciones de sugerencias son accesibles desde la confirmación con nombre).

VonC
fuente
1
Para incluir también sucursales remotas sin sucursal local, utilice git branch -a --merged.
Acumenus
21

Tengo un problema similar. Quiero eliminar todas las sucursales locales que estaban rastreando sucursales remotas que ahora se eliminan. Me parece que eso git remote prune origines insuficiente para eliminar las ramas que quiero que se vayan. Una vez que se elimina el control remoto, quiero que el local también desaparezca. Esto es lo que funcionó para mí:

De 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 agregarlo 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 : Cambié -da -Den mi configuración actual, porque no quiero escuchar a Git quejarse de las ramas no fusionadas. Es posible que también desee esta funcionalidad. Si es así, simplemente use en -Dlugar de -dal final de ese comando.

Además, FWIW, su archivo de configuración global casi siempre será ~/.gitconfig.

(Corrección de OS X)

Como está escrito, esto no funciona en OS X debido al uso de xargs -r(gracias, Korny ).

El -res para evitar que se xargsejecute git branch -dsin un nombre de rama, lo que resultará en un mensaje de error " fatal: branch name required". Si no le importa el mensaje de error, simplemente puede eliminar el -rargumento xargsy listo.

Sin embargo, si no desea ver un mensaje de error (y realmente, quién podría culparlo), entonces necesitará algo más que busque una tubería vacía. Si es posible que pueda utilizar ifne de moreutils . Insertaría ifneantes xargs, lo que dejará xargsde ejecutarse con datos vacíos. NOTA : ifneconsidera que cualquier cosa no está vacía, esto incluye líneas en blanco, por lo que aún puede ver ese mensaje de error. No lo he probado en OS X.

Aquí está esa git configlínea con ifne:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | ifne xargs git branch -d'
Karl Wilbur
fuente
1
¿Es posible convertir esto en un comando "git config --global ..."? Desde el punto de vista de las instrucciones / cómo-wiki-para-tontos, es fácil de transmitir en lugar de decir "Encuentra tus archivos de configuración de git, usa el editor para editarlos y luego ejecuta los comandos"
Kannan Ekanath
Tenga en cuenta que xargs -r no funciona en OSX
Korny
Bueno saber. No sé cuál sería la opción "no ejecutar si está vacío" para OSX.
Karl Wilbur
18

Edición tardía:

Mejor es

git for-each-ref  --format='%(refname:short) %(upstream)'  refs/heads \
| awk '$2 !~/^refs\/remotes/'

En GNU / cualquier cosa

for b in `git branch|sed s,..,,`; do
    git config --get branch.$b.remote|sed Q1 && echo git branch -D $b
done

Si es probable que haya más de un puñado de ramas, habría mejores formas, utilizando comm -23en la salida de git branch|sed|sorty git config -l|sed|sort.

jthill
fuente
1
@nmr s / git. * Q1 / test $ (git config --get branch. $ b.remote | sed q | wc -1) = 1 /
jthill
15

Esto funciona para mi:

git branch -vv | grep -v origin

(si su control remoto tiene otro nombre que no sea el origen, sustitúyalo).

Esto mostrará una lista de todas las ramas que no están rastreando un control remoto, que suena como lo que está buscando.

ebr
fuente
2
Esto mostrará una lista de las ramas que solían tener un control remoto pero que ya no lo tienen incluso si ejecuta git fetch --prune.
RusinaRange
2
En mi opinión, es mejor buscar ": ido]" en lugar de filtrar "origen"
fiorentinoing
4

Sintetizo mi propio comando Git para obtener las origin/***: goneramas:

git remote prune origin && git branch -vv | cut -c 3- | grep ': gone]' | awk '{print $1}' | xargs -n1 -r echo git branch -d

git remote prune origin && git branch -vv imprimirá ramas en modo detallado.

cut -c 3- eliminará los primeros caracteres.

grep ': gone]'imprimirá solo las ramas remotas que se hayan ido .

awk '{print $1}' imprimirá el nombre de la rama.

xargs -n1 -r echo git branch -dimprimirá el git branch -dcomando para eliminar ramas (-n1 administrará un comando por vez, -r para evitar emitir un comando si no hay una rama presente).

SUGERENCIA: elimine el comando "echo" para ejecutar los comandos en lugar de imprimir solo, lo dejé en el comando para verificar los comandos antes de ejecutar git.

SUGERENCIA 2: emita git branch -Dsi y solo si está seguro de que desea eliminar las ramas no fusionadas

fiorentinoing
fuente
de esto hice mi git
fiorentinoing
1
Esta es, con mucho, la solución más limpia que he encontrado, ¡genial! Nunca he pasado el tiempo estudiando awk, no sabía que pudieras hacer eso.
Adamjc
2

Aquí hay algo que he usado en PowerShell con comentarios para explicar lo que está haciendo. En un intento por aclarar lo que está sucediendo, no he usado ningún comando abreviado de PowerShell (alias). Siéntase libre de comprimirlo al nivel deseado de críptica :)

$verboseList = @(git branch -vv)
foreach ($line in $verboseList)
{
    # Get the branch name
    $branch = [regex]::Match($line, "\s(\S+)\s").Captures.Groups[1].Value
    # Check if the line contains text to indicate if the remote is deleted
    $remoteGone = $line.Contains(": gone]")
    # Check if the line does not contain text to indicate it is a tracking branch (i.e., it's local)
    $isLocal = !($line.Contains("[origin/"))
    if ($remoteGone -or $isLocal)
    {
        # Print the branch name
        $branch
    }
}
Grahamesd
fuente
0

Combinando algunas de las respuestas existentes:

[alias]
        branch-local = !git branch -vv | cut -c 3- | egrep '([0-9a-f]{7} [^[])|(: gone\\])' | sed -E 's/(^.+[0-9a-f]{7} (\\[.+\\])?).*$/\\1/'

P.ej:

$ git branch-local
autogen_autoreconf      fcfb1c6 [github/autogen_autoreconf: gone]
autotools_documentation 72dc477 [github/autotools_documentation: gone]
cray-mpich-messy        2196f4b
epel-sphinx             cfda770
lammps                  07d96a7 [github/lammps: gone]
lark-error              9ab5d6c [github/lark-error: gone]
no-root2                c3894d1
openmpi-cray            69326cf [github/openmpi-cray: gone]
shellcheck-cleanup      40d90ec
travis-python36         cde072e
update_vagrant_default  4f69f47 [github/update_vagrant_default: gone]
web-docs                e73b4a8
Reid
fuente