Usuń gałęzie śledzenia już nie na pilocie


1170

Czy istnieje prosty sposób usunięcia wszystkich gałęzi śledzenia, których zdalny odpowiednik już nie istnieje?

Przykład:

Oddziały (lokalne i zdalne)

  • mistrz
  • pochodzenie / mistrz
  • origin / bug-fix-a
  • origin / bug-fix-b
  • origin / bug-fix-c

Lokalnie mam tylko oddział główny. Teraz muszę pracować nad poprawką błędów , więc sprawdzam ją, pracuję nad nią i wypycham zmiany do pilota. Następnie robię to samo z bug-fix-b .

Oddziały (lokalne i zdalne)

  • mistrz
  • bug-fix-a
  • bug-fix-b
  • pochodzenie / mistrz
  • origin / bug-fix-a
  • origin / bug-fix-b
  • origin / bug-fix-c

Teraz mam master oddziałów lokalnych , bug-fix-a , bug-fix-b . Opiekun gałęzi Master scali moje zmiany w master i usunie wszystkie gałęzie, które już scalił.

Więc obecny stan jest teraz:

Oddziały (lokalne i zdalne)

  • mistrz
  • bug-fix-a
  • bug-fix-b
  • pochodzenie / mistrz
  • origin / bug-fix-c

Teraz chciałbym wywołać jakieś polecenie w celu usunięcia gałęzi (w tym przypadku bug-fix-a , bug-fix-b ), które nie są już reprezentowane w zdalnym repozytorium.

Byłoby to coś w rodzaju istniejącego polecenia git remote prune origin, ale bardziej podobne git local prune origin.

Odpowiedzi:


1278

git remote prune origin przycina gałęzie śledzące nie na pilocie.

git branch --merged wyświetla gałęzie, które zostały scalone z bieżącą gałęzią.

xargs git branch -d usuwa gałęzie wymienione na standardowym wejściu.

Ostrożnie usuwaj oddziały wymienione przez git branch --merged. Lista może zawierać masterlub inne gałęzie, których wolisz nie usuwać.

Aby dać sobie możliwość edycji listy przed usunięciem gałęzi, możesz wykonać następujące czynności w jednym wierszu:

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

17
Pierwszy wiersz połączonych gałęzi znajduje się * masterw moim systemie. Następujące polecenie działało dla mnie:git branch -d $(git branch --merged |tail -n +2)
Trendfischer

99
Jeśli jestem włączony, developto git branch --mergedobejmuje master! Prawdopodobnie (zdecydowanie!) Nie chcesz tego usuwać. Myślę też, że powinno być git branch -dtam, gdzie małe litery -doznaczają „bezpieczne usuwanie”, np. Usuwanie tylko w przypadku scalenia.
thom_nic

11
Wydaje ulepszony rozwiązanie jest tam .
Siergiej Brunow

34
Usunięto scalone jest przydatne, ale nie to samo, co „usuń gałęzie nie na zdalnym”.
dlsso

11
Wystarczy użyć grep, aby wykluczyć master:git branch --merged | grep -v "master" >/tmp/merged-branches && vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches
genial

588

Po poleceniu

git fetch -p

usuwa zdalne odwołania po uruchomieniu

git branch -vv

pokaże „zniknął” jako zdalny status. Na przykład,

$ 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

Możesz więc napisać prosty skrypt, aby usunąć lokalne oddziały, które zostały zdalne:

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

Zauważ, że powyższe używa polecenia „porcelana”, git branchaby uzyskać status upstream.

Innym sposobem uzyskania tego statusu jest użycie polecenia „instalacja wodno-kanalizacyjna” git for-each-refze zmienną interpolacyjną %(upstream:track), [gone]podobnie jak powyżej.

To podejście jest nieco bezpieczniejsze, ponieważ nie ma ryzyka przypadkowego dopasowania części komunikatu zatwierdzenia.

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 - nie na Solarisie i niektórych BSD i niektórych OS X :)
jww

4
Wygląda na to, że spowoduje to również usunięcie gałęzi, która „zniknęła” w ostatnim komunikacie zatwierdzenia.
dlsso

11
@dlsso Jeśli ostatni komunikat zatwierdzenia zawiera ciąg „: gone]”, to tak, również zostanie usunięty. Możesz uczynić go bardziej niezawodnym kosztem prostoty, dzięki dodatkowemu awk / gawk do usuwania komunikatu zatwierdzenia. git branch -vv | gawk '{print $1,$4}' | grep 'gone]' | gawk '{print $1}'
jason.rickman

2
W odpowiedzi na mój poprzedni komentarz (pytanie) bieżąca gałąź ma * jako pierwsze pole. Jeśli zdarzy się, że znajdzie się również na liście „nieistniejących” gałęzi, zostanie przypisany 1 $ * i będzie interpretowany jako specyfikacja plików z awk wypluwającymi nazwy plików i folderów. I wyeliminowane wyrażenie grep i awk miał zrobić wszystko filtrowania: awk '/: gone]/{if ($1!="*") print $1}'. To działa teraz zgodnie z oczekiwaniami.
raben

5
@ahmedbhs, ponieważ polecenie używa pojedynczego cudzysłowu „musisz otaczać całe polecenie podwójnym cudzysłowem. Także aliasy git, które są poleceniami powłoki (jak ta), wymagają na początku! To działa dla mnie:test = "!git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done"
jason.rickman

306

Większość tych odpowiedzi w rzeczywistości nie odpowiada na pierwotne pytanie. Zrobiłem sporo kopania i to było najczystsze rozwiązanie, jakie znalazłem. Oto nieco dokładniejsza wersja tej odpowiedzi:

  1. Sprawdź swój domyślny oddział. Zazwyczajgit checkout master
  2. Biegać git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Wyjaśnienie:

Działa poprzez przycinanie gałęzi śledzenia, a następnie usuwanie lokalnych, które pokazują, że już ich nie ma git branch -vv.

Uwagi:

Jeśli Twój język jest ustawiony na inny niż angielski, musisz zmienić goneodpowiednie słowo. Oddziały, które są tylko lokalne, nie zostaną dotknięte. Oddziały, które zostały usunięte na odległość, ale nie zostały scalone, pokażą powiadomienie, ale nie zostaną usunięte na poziomie lokalnym. Jeśli chcesz je również usunąć, zmień -dna -D.


2
dla francuskiego OS już nie ma - należy zmienić
Mohamed EL HABIB,

4
powinien dodać LANG = en_US przed oddziałem git, aby wymusić angielski: git fetch --prune && LANG = en_US oddział git -vv | awk '/: gone] / {print $ 1}' | xargs git branch -d
Mohamed EL HABIB

3
Dodałbym git checkout master && ...na początku polecenia.
iarroyo

2
Jest to niebezpieczne, gdy znajdujesz się w gałęzi, która ma zostać usunięta - w takim przypadku pierwszą kolumną jest „*”, która jest następnie przekazywana do xargs. Aby to poprawić, dodaj pasek „*” przed przekazaniem wyjścia do awk: sed -e 's / ^ * //'
meeee

6
Ostrożny! Istnieje przypadek krawędzi, który spowoduje usunięcie wszystkich lokalnych gałęzi: Jeśli aktualnie znajdujesz się w gałęzi, która została usunięta zdalnie, dane wyjściowe git branch -vvzaczynają się od gwiazdki, która ostatecznie zakończy się wykonaniem git branch -d *. Oto git branch -vv | grep ': gone]'| grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d
łatana

209

Normalnie nie odpowiedziałbym na pytanie, które ma już 16 odpowiedzi, ale wszystkie inne odpowiedzi są błędne, a właściwa odpowiedź jest tak prosta. Pytanie brzmi: „Czy istnieje prosty sposób usunięcia wszystkich gałęzi śledzenia, których zdalny odpowiednik już nie istnieje?”

Jeśli „prosty” oznacza usunięcie ich wszystkich za jednym razem, nie jest kruchy, nie jest niebezpieczny i bez polegania na narzędziach, które nie wszyscy czytelnicy będą mieli, wtedy właściwa odpowiedź brzmi: nie.

Niektóre odpowiedzi są proste, ale nie robią tego, o co pytano. Inni robią to, o co proszono, ale nie są proste: wszyscy polegają na analizie danych wyjściowych Gita za pomocą poleceń manipulacji tekstem lub języków skryptowych, które mogą nie być obecne w każdym systemie. Ponadto większość sugestii używa komend porcelanowych, których wynik nie jest przeznaczony do analizowania przez skrypt („porcelana” odnosi się do komend przeznaczonych do działania przez człowieka; skrypty powinny używać komend niższego poziomu).

Dalsza lektura:


Jeśli chcesz to zrobić bezpiecznie, w przypadku zastosowania w pytaniu (gałęzie śledzące odśmiecanie, które zostały usunięte na serwerze, ale nadal istnieją jako gałęzie lokalne) i tylko za pomocą poleceń Git wysokiego poziomu, musisz:

  • git fetch --prune(lub git fetch -p, który jest aliasem lub git prune remote originktóry robi to samo bez pobierania i prawdopodobnie nie jest tym, czego chcesz przez większość czasu).
  • Zanotuj wszystkie zdalne gałęzie, które są zgłaszane jako usunięte. Lub, aby je później znaleźć git branch -v(każda osierocona gałąź śledzenia będzie oznaczona „[odszedł]”).
  • git branch -d [branch_name] w każdej osieroconej gałęzi śledzenia

(co proponują niektóre inne odpowiedzi).

Jeśli chcesz napisać rozwiązanie, to for-each-refjest twój punkt wyjścia, jak w odpowiedzi Marka Longaira tutaj i tej odpowiedzi na inne pytanie , ale nie widzę sposobu na wykorzystanie go bez napisania pętli skryptu powłoki lub użycia xargs lub czegoś takiego .


Wyjaśnienie tła

Aby zrozumieć, co się dzieje, musisz zdawać sobie sprawę, że w sytuacji śledzenia oddziałów nie masz jednej gałęzi, ale trzy. (I przypomnij sobie, że „gałąź” oznacza po prostu wskaźnik do zatwierdzenia.)

Biorąc pod uwagę gałąź śledzenia feature/X, zdalne repozytorium (serwer) będzie miało tę gałąź i będzie ją wywoływać feature/X. Twoje lokalne repozytorium ma gałąź, remotes/origin/feature/Xco oznacza: „To, co zdalnie powiedział mi jego funkcja / gałąź X, kiedy ostatnio rozmawialiśmy”, a na koniec, lokalne repozytorium ma gałąź, feature/Xktóra wskazuje na twoje ostatnie zatwierdzenie i jest skonfigurowana do „śledzenie” remotes/origin/feature/X, co oznacza, że ​​możesz pociągać i pchać, aby je wyrównać.

W pewnym momencie ktoś usunął feature/Xzdalne sterowanie. Od tego momentu pozostaniesz z lokalnym feature/X(którego prawdopodobnie już nie chcesz, ponieważ praca nad funkcją X jest prawdopodobnie zakończona), a twój z remotes/origin/feature/Xpewnością jest bezużyteczny, ponieważ jego jedynym celem było zapamiętanie stanu gałęzi serwera .

A Git pozwoli ci automatycznie wyczyścić zbędne remotes/origin/feature/X- to właśnie git fetch --prunerobi - ale z jakiegoś powodu nie pozwala ci automatycznie usunąć własnego feature/X... nawet jeśli feature/Xnadal zawiera osierocone informacje o śledzeniu, więc ma informacje w celu zidentyfikowania wcześniejszych gałęzi śledzenia, które zostały całkowicie scalone. (Wszakże może dać Ci informacje, które pozwala wykonać operację ręcznie samemu).


3
To jest znacznie bezpieczniejsza odpowiedź. Ponadto zadziała, jeśli użyjesz przepływu pracy „Squash and Merge”, w przeciwieństwie do wybranej odpowiedzi.
Jake Levitt

3
To naprawdę odpowiada tylko na prostą część pytania „jak znaleźć zniknięte gałęzie” (i to za pomocą tego samego polecenia, które zostało już opublikowane przez jason.rickman w 2015 r.), Ale następnie nakazuje ręczne usunięcie wszystkich gałęzi, co jest dokładnie tym, co PO nie chce robić.
Voo

4
@ Voo To jest sedno odpowiedzi: nie mówić ci, jak to zrobić (to tylko bonus), ale powiedzieć, że odpowiedź jest taka, że ​​nie ma łatwego, prostego sposobu, aby to zrobić. Która jest poprawną odpowiedzią na sformułowane pytanie.
Andrew Spencer

1
„Jeśli chcesz to zrobić bezpiecznie, na przykład w pytaniu ..” - co neguje większość twierdzeń zawartych w akapicie pierwszym. „[Git] może dać ci informacje, które pozwalają ci wykonać operację ręcznie” - jesteśmy programistami, więc biorąc pod uwagę informacje jako listę (która jest pokazana) zadanie jest nadal proste (np. xargs). Należy również git aliasuprościć wiele spraw.
user2864740

2
@ user2864740 Niektórzy czytelnicy mogą uznać, że napisanie małego skryptu bash mieści się w ich definicji „prostego” i jest to pozycja całkowicie możliwa do obrony, ale podałem własną interpretację „prostego” w drugim akapicie, a reszta odpowiedzi jest spójna z tym. I w obronie mojej ograniczającej interpretacji interpretacji, nie wszyscy użytkownicy Git mają xargsbash lub bash, i prawdopodobnie nie szukają tutaj wyzwania w zakresie mikrokodowania, a jedynie szybkiej odpowiedzi, którą mogą bezpiecznie zastosować bez odwracania uwagi od prawdziwego celu .
Andrew Spencer,

52

Znalazłem odpowiedź tutaj: Jak mogę usunąć wszystkie gałęzie git, które zostały scalone?

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

Upewnij się, że utrzymujemy mistrza

Możesz upewnić się, że masterżadna inna gałąź w tym zakresie nie zostanie usunięta, dodając kolejną greppo pierwszej. W takim przypadku wybrałbyś:

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

Więc jeśli chcielibyśmy zachować master, developi stagingna przykład, poszlibyśmy:

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

Niech to będzie alias

Ponieważ jest trochę długi, możesz chcieć dodać alias do swojego .zshrclub .bashrc. Mój jest nazywany gbpurge(dla 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'

Następnie załaduj ponownie .bashrclub .zshrc:

. ~/.bashrc

lub

. ~/.zshrc

8
Przydatne, ale nie to samo, co „usuń gałęzie nie na odległość”
dlsso,

Greate answer @karlingen, Mam to na stałe jako gbpurge we wszystkich moich środowiskach deweloperów
Peter Dolan

50

Rozwiązanie Windows

W przypadku programu 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 $_}

Wyjaśnienie

git checkout master przechodzi do gałęzi master

git remote update origin --prune przycina odległe gałęzie

git branch -vvpobiera pełne wyjście wszystkich gałęzi ( referencja git )

Select-String -Pattern ": gone]" pobiera tylko te rekordy, w których zostały usunięte ze zdalnego.

% { $_.toString().Trim().Split(" ")[0]} uzyskać nazwę oddziału

% {git branch -d $_} usuwa gałąź


3
Dzięki za to, choć musiałem dodać .Trim()po .toString(), aby usunąć dwie spacje przed nazwą oddziału.
Matthew

2
Dzięki za to polecenie. Musiałem zmienić git branch -dna git branch -Dinny pojawia się błąd oddział nie jest w pełni połączone.
markieo

1
@markieo Prawdopodobnie już to wiesz, ale dla każdego innego - git branch -Djest destrukcyjny i spowoduje usunięcie lokalnej pracy, której jeszcze nie wysłałeś. -djest zawsze bezpieczny, po prostu bądź ostrożny -D.
Nate Barbettini

33

Dopasowanie wzorca do „zniknął” w większości innych rozwiązań było dla mnie trochę przerażające. Aby być bezpieczniejszym, wykorzystuje --formatflagę, aby wyciągnąć status śledzenia każdego odgałęzienia .

Potrzebowałem wersji przyjaznej dla systemu Windows, więc usuwa wszystkie gałęzie, które są wymienione jako „zniknęły” za pomocą programu Powershell:

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

W pierwszym wierszu znajduje się nazwa lokalnych oddziałów, których odgałęzienie „zniknęło”. Następny wiersz usuwa puste linie (które są wyprowadzane dla gałęzi, które nie „zniknęły”), a następnie nazwa gałęzi jest przekazywana do polecenia usunięcia gałęzi.


6
Ta --formatopcja wydaje się dość nowa; Musiałem zaktualizować git z 2.10. Coś do 2.16.3, aby go zdobyć. Oto moja modyfikacja systemów Linux:git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)" | sed 's,^refs/heads/,,' | grep . | xargs git branch -D
bxm

2
To jak dotąd najlepsze rozwiązanie. Jedną z sugestii jest użycie refname:short. Następnie możesz usunąć linię% { $_ -replace '^refs/heads/', '' }
ioudas

2
To świetne rozwiązanie dla systemu Windows. Używam go w ten sposób, ponieważ jest bardziej czytelny. git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | where { $_ -ne "" } | foreach { git branch -d $_ }Prawdopodobnie dobrym pomysłem jest użycie -dzamiast -D. Wymuszone usunięcie nie powinno być konieczne w przypadku oddziałów, które nie są już zdalne.
Rubanov

31

Usuń wszystkie gałęzie, które zostały scalone w Master, ale nie próbuj usuwać samego Master:

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

lub dodaj alias:

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

Wyjaśnienie:

git checkout master główny oddział kasy

git pull origin master upewnij się, że oddział lokalny scalił wszystkie zmiany zdalne

git fetch -p usuń odniesienia do zdalnych gałęzi, które zostały usunięte

git branch -d $(git branch master --merged | grep master -v) usuń wszystkie gałęzie, które zostały scalone w Master, ale nie próbuj usuwać samego Master


3
Jedna uwaga, jest to bardzo pomocne, ale usunie również te gałęzie, które nigdy nie zostały wypchnięte do pilota. Bezpieczniej jest tylko wymienić różnice, a następnie skopiować do git branch -Dpolecenia to, co naprawdę chcesz usunąć
Zefiryn

Aby to wyjaśnić, Zefiryn odnosi się do korzystania z opcji -D, która nie jest częścią jednowierszowej.
cs01,

1
Lub użyj małych liter, git branch -dktóre powinny wyświetlać ostrzeżenie o nieodepchniętych gałęziach.
acme

16
git fetch -p

Spowoduje to przycięcie wszystkich gałęzi, które już nie istnieją na pilocie.


64
usuwa to zdalne odwołania, ale nie same lokalne oddziały. Chociaż jest to przydatne polecenie, nie sądzę, że odpowiada na pytanie OP.
thataustin

Co? To dla mnie usuwa lokalne oddziały.
Alex Hall

1
@AlexHall Zdalne repozytorium ma gałąź X; ty git checkout X; teraz twoje repozytorium ma (lokalną) gałąź śledzenia Xi gałąź zdalną origin/X; zdalne repozytorium zostanie usunięte X; ty git fetch-p; w Twoim lokalnym repozytorium nie tylko zostały usunięte, origin/Xale również X. Czy tak mówisz?
Andrew Spencer,

16

Jeszcze jedna odpowiedź dla stosu, czerpiąca mocno z odpowiedzi Patricka (co mi się podoba, ponieważ wydaje się, że usuwa niejasności co do tego, gdzie gone]będzie pasować w git branchwyniku), ale dodając wygięcie * nix.

W najprostszej formie:

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

Mam to zawinięte w git-goneskrypt na mojej ścieżce:

#!/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 - The --format opcja wydaje się dość nowa; Musiałem zaktualizować git z 2.10. Coś do 2.16.3, aby go zdobyć.

EDYCJA: poprawione, aby zawierały sugestie dotyczące refname:shortod Benjamina W.

NB2 - testowałem tylko bash, stąd hashbang, ale prawdopodobnie przenośny sh.


Nie możesz pominąć sedczęści, używając %(refname:short)w pierwszym wierszu?
Benjamin W.

Wydaje się, że działa dla mnie i mam go teraz jako zgrabny alias Git - najczystsze rozwiązanie ze wszystkich tutaj!
Benjamin W.

2.13 wprowadzone --format. Innym sposobem na pominięcie sedczęści jest użycie git update-ref -d. Zauważ, że jest to prawdopodobnie nieco niebezpieczne, używanie git for-each-refjest tutaj bezpieczniejsze (dane --shell).
gsnedders

Świetna odpowiedź, która pomogła mi odpowiedzieć na moje własne pytania, kiedy --formatsamemu to zrobiłem. Tylko jedno pytanie: dlaczego #!bash? Wszystko tutaj wygląda mi na przenośne sh.
Toby Speight

To tylko moja normalna płyta kotłowa i nie testowałem jej gdzie indziej.
bxm

15

Może być przydatny w niektórych prostych liniach, aby wyczyścić wszystkie lokalne gałęzie z wyjątkiem master i develop

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

Świetna odpowiedź! Podoba mi się, jak łatwo można grać z tego 'git branch | grep -v "master" | grep -v "develop"rodzaju rzeczami, zanim przystąpię do dodawania części usuwania polecenia. 👏😊
finneycanhelp

Ale to nie odpowiada na powyższe pytanie. Spowoduje to usunięcie gałęzi, nawet jeśli zdalny nadal tam jest.
Jim.B

13

Spowoduje to usunięcie wszystkich scalonych lokalnych rozgałęzień oprócz lokalnego odniesienia głównego i aktualnie używanego:

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

Spowoduje to usunięcie wszystkich gałęzi, które zostały już usunięte ze zdalnego repozytorium, do którego odnosi się „ origin ”, ale nadal są lokalnie dostępne w „ pilotach / origin ”.

git remote prune origin

git branch -vv | grep 'gone]' | grep -v "\*" | awk '{print $1}' | xargs -r git branch -d Objaśnienie: Wolę zastąpić git branch --mergedprzez, git branch -vvaby pokazać status (odszedł), ponieważ poprzedni git branch --mergedmoże pokazywać również master
jpmottin

13

Nie sądzę, aby było to wbudowane polecenie, ale można bezpiecznie wykonać następujące czynności:

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

Kiedy używasz -d, git odmówi usunięcia gałęzi, chyba że zostanie ona całkowicie scalona z HEADgałęzią zdalnego śledzenia w górę. Tak więc zawsze możesz zapętlić dane wyjściowe git for-each-refi spróbować usunąć każdą gałąź. Problem z tym podejściem polega na tym, że podejrzewam, że prawdopodobnie nie chcesz bug-fix-dzostać usunięty tylko dlatego, że origin/bug-fix-dzawiera jego historię. Zamiast tego możesz utworzyć skrypt podobny do następującego:

#!/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

Ostrzeżenie: nie testowałem tego skryptu - używaj tylko ostrożnie ...


Pozwoliłem sobie na edycję skryptu. Nadal nie daje żadnych gwarancji, ale działa i wydaje się działać teraz.
fwielstra

13

TL; DR:

Usuń WSZYSTKIE lokalne oddziały, które nie są zdalne

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

Usuń WSZYSTKIE lokalne oddziały, które nie są zdalne ORAZ w pełni scalone ORAZ nie są używane, jak powiedziano w wielu odpowiedziach wcześniej.

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

Wyjaśnienie

  • git fetch -p będzie przycinać wszystkie gałęzie, które już nie istnieją na pilocie
  • git branch -vv wydrukuje lokalne oddziały, a przycięta gałąź zostanie oznaczona gone
  • grep ': gone]' wybiera tylko gałąź, która zniknęła
  • awk '{print $1}' filtruj dane wyjściowe, aby wyświetlić tylko nazwę gałęzi
  • xargs git branch -D zapętli wszystkie linie (gałęzie) i wymusi usunięcie tej gałęzi

Dlaczego git branch -Dnie git branch -d, będziesz miał dla oddziałów, które nie są w pełni połączone.

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

1
czy chciałeś powiedzieć zdalny zamiast mistrza?
tinos

8

Możesz to zrobić:

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

7

Na podstawie powyższych informacji zadziałało to dla mnie:

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

Usuwa wszystkie lokalne oddziały za ': gone] 'pomocą zdalnego.


1
Wygląda na to, że spowoduje to również usunięcie gałęzi, która „zniknęła” w ostatnim komunikacie zatwierdzenia.
dlsso

Usunie również każdy oddział, który ma gonegdziekolwiek w nazwie.
bfontaine

3
"git branch -D git branch -vv | grep ': gone]' | awk '{print $ 1}' | xargs`" To zadziałało dla mnie.
Muthu Ganapathy Nathan

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

Powyższe polecenie może służyć do pobierania gałęzi, które są scalane i usuwane w trybie zdalnym, a także usuwa gałąź lokalną, która nie jest już dostępna w trybie zdalnym


Najlepsze rozwiązanie do tej pory, chociaż zmieniłem to, grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -Daby wymusić usunięcie wszystkich
Shoaib

Niebezpieczne, jeśli którakolwiek z twoich nazw gałęzi zawiera podciąg w gonedowolnym miejscu (np usingonefunction.).
Toby Speight

7

Nic z tego nie było dla mnie odpowiednie. Chciałem czegoś, co wyczyści wszystkie lokalne gałęzie, które śledziły gałąź zdalną, w originktórej usunięto gałąź zdalną ( gone). Nie chciałem usuwać lokalnych gałęzi, które nigdy nie zostały skonfigurowane do śledzenia gałęzi zdalnej (tj. Moich lokalnych gałęzi programistów). Chciałem też mieć prostą liniówkę, która po prostu używa gitlub inne proste narzędzia CLI, zamiast pisać własne skrypty. Skończyło się na użyciu trochę grepiawk zrobić to proste polecenia.

To ostatecznie skończyło się na moim ~/.gitconfig:

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

Oto git config --global ...polecenie umożliwiające łatwe dodanie tego jako 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'

UWAGA: W poleceniu config używam tej -dopcji git branchzamiast -D, jak w mojej rzeczywistej konfiguracji. Używam, -Dponieważ nie chcę słyszeć, jak Git narzeka na nie połączone gałęzie. Możesz także chcieć tę funkcjonalność. Jeśli tak, po prostu użyj -Dzamiast -dna końcu tego polecenia config.


Lubię podejście alias git. Szybkie pytanie: po co robić git branch -vvi żałować, : gone]a czego nie robić git branch -vi nie żartować [gone]?
lalibi

@lalibi, dobre pytanie. Nie pamiętam Podejrzewam, że coś się nie pojawiło tylko z jednym v. Jeśli git branch -v | grep '[gone]'Ci odpowiada, idź. To wydaje się trochę czystsze.
Karl Wilbur

4

Na podstawie Git Tip: Usuwanie starych lokalnych gałęzi , które wygląda podobnie do rozwiązania jason.rickman Zaimplementowałem niestandardowe polecenie o nazwie git odszedł używając 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 łączy przycinanie i wyświetlanie „znikniętych” gałęzi:

$ 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

Następnie możesz pociągnąć za spust za pomocą git gone -dlubgit gone -D .

Notatki

  • Użyłem wyrażenia regularnego "$BRANCH/.*: gone]"tam $BRANCH, gdzie normalnie by to było origin. To prawdopodobnie nie zadziała, jeśli twoje wyjście Git jest zlokalizowane na francuski itp.
  • Sebastian Wiesner przeniósł go również do użytkowników Rust dla Windows. Ten jest również nazywany git odszedł .

To prawdopodobnie powinna być zaakceptowana odpowiedź - git gonewygląda na alias git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d, który rozwiązuje problem PO.
alex

4

Rysunek mocno z numerem z innymi odpowiedziami tutaj, ja skończyło się z następujących (git 2.13 i nowszych, wierzę), który powinien działać na każdym systemie UNIX-like powłoce:

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

To w szczególności używa for-each-refzamiast branch(jak branchto jest polecenie „porcelany” zaprojektowanej dla wyjścia czytelnego dla człowieka, a nie przetwarzania maszynowego) i używa swojego --shellargumentu, aby uzyskać odpowiednio uniknięte wyjście (pozwala to nam nie martwić się o żaden znak w nazwie ref).


Działa to dla mnie, ale byłoby miło, gdybyś mógł wyjaśnić, co robią pozostałe kroki polecenia. Na przykład nie wiem, co [ ! -z "$ref" ]to znaczy. Myślę, że multi-liner już by pomógł. Ale wciąż dziękuję za Twój wkład!
KevinH

4

Jeszcze jedna odpowiedź, ponieważ żadne z rozwiązań nie odpowiada moim potrzebom w zakresie elegancji i wieloplatformowości:

Polecenie usunięcia lokalnych oddziałów, które nie są zdalnie:

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

Aby zintegrować go z gitconfig, aby można go było uruchomić z git branch-prune:

Grzmotnąć

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'

PowerShell

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'

(Potrzebujesz pomocy w znalezieniu uniwersalnego polecenia dla PowerShell i bash)

Dlaczego ta odpowiedź jest najlepsza?

  • Oferuje kompletne rozwiązanie: dodaje git branch-prunepolecenie do git
  • Działa dobrze z Windows PowerShell
  • Główną ideą jest @ jason.rickman „s metoda kuloodporny korzystaniagit for-each-ref
  • Analizowanie i filtrowanie odbywa się --filterbez zewnętrznych zależności

Wyjaśnienie:

  • Dodaje nowy alias do twojego ~\.gitconfig. Po wykonaniu tego możesz po prostu zrobićgit branch-prune
  • Wewnątrz tego aliasu:
    • Pobiera gałęzie z --pruneflagą, która „przycina gałęzie do zdalnego śledzenia, które nie są już zdalne”
    • Używa git for-each-refi --filter, aby uzyskać listę oddziałów są [gone](nie zdalne)
    • Przechodzi przez tę listę i bezpiecznie usuwa gałąź

1
Dziękuję, @ jerry-wu za poprawę niesamowitości tego rozwiązania nieskończoności.
Himura

@ jerry-wu i Twoja ostatnia edycja polecenia git config nie działa w PowerShell ((
Himura

1

Wymyśliłem ten skrypt bash. To zawsze gałęzie 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

Używam krótkiej metody, aby zrobić lewę, zalecam zrobić to samo, ponieważ może to zaoszczędzić kilka godzin i dać ci lepszą widoczność

Wystarczy dodać następujący fragment kodu do pliku .bashrc (.bashprofile w systemie Macos).

git-cleaner() { git fetch --all --prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. Zbierz wszystkie piloty
  2. Uzyskaj tylko scalone gałęzie z git
  3. Usuń z tej listy gałęzie „chronione / ważne”
  4. Usuń resztę (np. Czyste i scalone gałęzie)

Będziesz musiał edytować wyrażenia regularne grep, aby dopasować je do swoich potrzeb (tutaj, zapobiega to usunięciu master, preprodprod i dmz)


git fetch --all --prunewykonał lewę. Dzięki!
LeOn - Han Li

1

To działało dla mnie:

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

och, podwójnie opublikowałem to na ... właśnie zobaczyłem to podczas przeglądania postów. Ale naprawdę możesz wskazać faceta, który dostarczył to polecenie, zamiast brać je za swoje!
Dwza

Dostaję to z forum, nie próbuj być mądry, po prostu weź odpowiedź lub zignoruj ​​ją
Fareed Alnamrouti

Nawet, jeśli nie ... w każdym razie ... nie przyszedłeś tutaj, aby z tobą o tym porozmawiać. Tak jak powiedziałeś ... weź mój komentarz lub zignoruj ​​go;)
Dwza

0

Nie jestem pewien, jak długo, ale teraz używam git-up, który się tym zajmuje.

ja robię git up i zaczyna śledzić nowe gałęzie i usuwa stare.

Żeby było jasne, nie jest to gotowe narzędzie git - https://github.com/aanand/git-up

BTW to także ukrywa brudne drzewo i sprawia, że ​​rebases wciąż są sprawiedliwe git up.

Mam nadzieję, że przyda się komuś


2
To nie usuwa lokalnych oddziałów, które nie istnieją na serwerze.
Ashley,

0

Oto rozwiązanie, którego używam do skorupki ryby. Testowane na Mac OS X 10.11.5, fish 2.3.0i 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

Kilka uwag:

Upewnij się, że ustawiłeś prawidłowy base_branch. W tym przypadku używam developjako gałęzi podstawowej, ale może to być wszystko.

Ta część jest bardzo ważna: grep -v "\(master\|$base_branch\|\*\)" . Zapewnia to, że nie usuniesz master ani gałęzi bazowej.

Używam git branch -d <branch>jako dodatkowego środka ostrożności, aby nie usuwać żadnych gałęzi, które nie zostały w pełni połączone z wcześniejszym lub obecnym HEAD.

Łatwym sposobem na przetestowanie jest zastąpienie git branch -d $fgo echo "will delete $f".

Powinienem też dodać: UŻYWAJ NA WŁASNE RYZYKO!


0

Napisałem skrypt Pythona za pomocą GitPython, aby usunąć lokalne gałęzie, które nie istnieją na zdalnym.

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

Mam nadzieję, że ktoś uzna to za przydatne, jeśli coś przeoczyłem, dodaj komentarze.


0

Jeśli używasz zshpowłoki zOh My Zsh zainstalowanym programem, najłatwiejszym sposobem na zrobienie tego bezpiecznie jest użycie wbudowanego autouzupełniania.

Najpierw określ, które gałęzie chcesz usunąć:

~ git branch --merged

  branch1
  branch2
  branch3
* master

wyświetli się lista już połączonych oddziałów

Po poznaniu kilku, które chcesz usunąć, wpisz:

~ git branch -d 

Wszystko, co musisz zrobić, to nacisnąć [tab], a wyświetli się lista lokalnych oddziałów. Użyj polecenia uzupełnij tabulatorem lub ponownie wciśnij klawisz [tab] i możesz je kolejno przełączać, aby wybrać gałąź za pomocą [enter].

Karta Wybierz gałęzie w kółko, aż pojawi się lista gałęzi, które chcesz usunąć:

~ git branch -d branch1 branch2 branch3

Teraz wystarczy nacisnąć Enter, aby usunąć kolekcję oddziałów.

Jeśli nie używasz zsh na swoim terminalu ... Pobierz go tutaj.


0

Lubię używać potoków, ponieważ ułatwia to odczytanie polecenia.

To jest moje rozwiązanie, jeśli chcesz usunąć wszystkie gałęzie oprócz master.

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

Aby usunąć inne gałęzie spełniające kryteria, zmodyfikuj pierwszy i drugi blok.

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

-3

Oto prosta odpowiedź, która zadziałała dla mnie przy użyciu klienta git:

Usuń repozytorium całkowicie ze swojego komputera, a następnie sprawdź ponownie.

Żadnych chowańców z ryzykownymi skryptami.


2
Co z oddziałami, nad którymi obecnie pracuję: /
Mailo Světel

@ MailoSvětel, jeśli są na twoim pilocie, możesz po prostu pociągnąć je ponownie (pamiętaj, aby zatwierdzić i popchnąć swoją najnowszą pracę do pilota!)
Juan Carlos Ospina Gonzalez,

W moim pytaniu i w większości odpowiedzi staramy się unikać niepotrzebnej pracy. Innymi słowy: muszę po prostu usunąć gałęzie, nie będę już pracował. Usunięcie repozytorium, klonowanie go i ponowne rozpoczęcie śledzenia gałęzi to dla mnie dużo niepotrzebnej pracy. Poza tym część pracy może się zgubić :(
Mailo Světel
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.