Видаліть гілки відстеження більше не на віддаленому


1171

Чи є простий спосіб видалити всі гілки відстеження, віддаленого еквівалента яких більше немає?

Приклад:

Відділення (місцеві та віддалені)

  • майстер
  • походження / майстер
  • походження / помилка-виправлення
  • походження / помилка-виправлення-b
  • походження / помилка-виправлення-c

Місцево я маю лише головну галузь. Тепер мені потрібно працювати над bug-fix-a , тому я перевіряю це, працюю над ним і натискаю зміни на пульт. Далі я те ж саме роблю з bug-fix-b .

Відділення (місцеві та віддалені)

  • майстер
  • помилка-виправлення-а
  • помилка-виправлення-б
  • походження / майстер
  • походження / помилка-виправлення
  • походження / помилка-виправлення-b
  • походження / помилка-виправлення-c

Тепер у мене є місцеві філії майстер , баг-фікс-а , баг-фікс-б . Підтримувач гілки Master об'єднає мої зміни в головний і видалить усі гілки, які він уже об'єднав.

Отже, теперішній стан зараз:

Відділення (місцеві та віддалені)

  • майстер
  • помилка-виправлення-а
  • помилка-виправлення-б
  • походження / майстер
  • походження / помилка-виправлення-c

Тепер я хотів би викликати якусь команду для видалення гілок (у цьому випадку bug-fix-a , bug-fix-b ), які більше не представлені у віддаленому сховищі.

Це було б щось на зразок існуючої команди git remote prune origin, але більше подібне git local prune origin.

Відповіді:


1280

git remote prune origin чорнослив відстежує гілки не на віддаленому.

git branch --merged перелічує гілки, які були об'єднані в поточну гілку.

xargs git branch -d видаляє гілки, вказані на стандартному вході.

Будьте уважні, видаляючи гілки, перелічені користувачем git branch --merged. Список може включати masterчи інші гілки, які ви не бажаєте видаляти.

Щоб дати собі можливість редагувати список перед видаленням гілок, ви можете зробити наступне в одному рядку:

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

17
Перший рядок об'єднаних гілок знаходиться * masterв моїй системі. Наступна команда працювала для мене:git branch -d $(git branch --merged |tail -n +2)
Trendfischer,

99
Якщо я на, developтоді git branch --mergedвключає master! Ви, ймовірно, (безумовно!) Не хочете видаляти це. Крім того, я думаю, що це має бути git branch -dтам, де малі -dзначить "безпечне видалення", наприклад, видалити лише якщо об'єднати.
thom_nic

11
Здається , покращене рішення забезпечується там .
Сергій Брунов

34
Видалене об’єднання корисно, але не те саме, що "видалити гілки не на віддаленому".
dlsso

11
Просто використовуйте grep, щоб виключити майстра:git branch --merged | grep -v "master" >/tmp/merged-branches && vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches
geniass

588

Після команди

git fetch -p

видаляє віддалені посилання під час запуску

git branch -vv

він відображатиметься як "пішов" у якості віддаленого стану. Наприклад,

$ 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

Таким чином, ви можете написати простий скрипт для видалення локальних гілок, які перейшли на віддалений:

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

Зауважте, що вище використовується команда "порцеляна" git branchдля отримання статусу вище.

Ще один спосіб отримати цей статус - використовувати команду "сантехніка" git for-each-refіз змінною інтерполяції %(upstream:track), яка буде [gone]точно так само, як вище.

Такий підхід є дещо безпечнішим, оскільки немає ризику випадкової відповідності частині повідомлення про виконання.

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 - не на Solaris та деяких BSD та деяких OS X :)
jww

4
Схоже, це також видалить будь-яку гілку, яка "пішла" в останньому повідомленні про фіксацію.
dlsso

11
@dlsso Якщо останнє повідомлення про фіксацію містить рядок ": gone]", так, воно також буде видалено. Ви можете зробити його більш надійним за рахунок простоти, маючи додатковий awk / gawk, щоб зняти повідомлення про фіксацію. git branch -vv | gawk '{print $1,$4}' | grep 'gone]' | gawk '{print $1}'
jason.rickman

2
У відповідь на мій попередній коментар (запитання) у поточної гілки є * як перше поле. Якщо це також є у списку "відійшли" гілок, $ 1 буде призначено * і буде інтерпретуватися як filepec з awk виплюненням імен файлів і папок. Я усунув вираз Grep і було AWK робити все фільтрації: awk '/: gone]/{if ($1!="*") print $1}'. Зараз це працює як очікувалося.
rhaben

5
@ahmedbhs, оскільки команда використовує єдину цитату "вам потрібно використовувати подвійну цитату" для оточення всієї команди. Також для початку потрібні псевдоніми git, які є командами оболонки (як ця), це працює для мене:test = "!git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done"
jason.rickman

306

Більшість із цих відповідей насправді не відповідають на початкове запитання. Я зробив купу копання, і це було найчистішим рішенням, яке я знайшов. Ось трохи більш ретельна версія цієї відповіді:

  1. Ознайомтеся з відділенням за замовчуванням. Зазвичайgit checkout master
  2. Біжи git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Пояснення:

Працює, обрізаючи гілки відстеження, потім видаляючи локальні, які показують, що вони "пішли" git branch -vv.

Примітки:

Якщо для вашої мови встановлено щось інше, ніж англійська, вам потрібно буде змінити goneвідповідне слово. Гілки, які є лише місцевими, не торкаються. Гілки, які були видалені віддалено, але не були об'єднані, покажуть сповіщення, але не будуть видалені на локальному рівні. Якщо ви хочете видалити їх, також змініть -dна -D.


2
для французької ОС, що пішла, треба змінити невідповідно
Mohamed EL HABIB

4
слід додати LANG = en_US перед гіткою git, щоб змусити англійську мову: git fetch --prune && LANG = en_US гілка git -vv | awk '/: пройшов] / {print $ 1}' | xargs git branch -d
Мохамед EL HABIB

3
Я додав би git checkout master && ...на початку команди.
iarroyo

2
Це небезпечно, коли ви знаходитесь на гілці, яку слід видалити - у цьому випадку перший стовпець - "*", який потім передається xargs. Щоб покращити це, додайте смужку символу '*' перед передачею виводу у awk: sed -e 's / ^ * //'
meeee

6
Обережно! Існує кращий випадок, який призведе до видалення всіх ваших локальних гілок: Якщо ви зараз перебуваєте на гілці, яку було видалено на віддаленому рахунку, то виведення git branch -vvпочнеться зірочкою, що в кінцевому підсумку призведе до виконання git branch -d *. Ось виправлена ​​версія, яка буде ігнорувати рядки зірочкою:git branch -vv | grep ': gone]'| grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d
kcm

209

Я б зазвичай не відповідав на запитання, на яке вже є 16 відповідей, але всі інші відповіді неправильні, а правильна відповідь така проста. Питання говорить: "Чи існує простий спосіб видалити всі гілки відстеження, віддаленого еквівалента яких більше немає?"

Якщо "просте" означає видалити їх усі за один раз, не тендітні, не небезпечні та без опори на інструменти, які не всі читачі матимуть, то правильна відповідь: ні.

Деякі відповіді прості, але вони не роблять того, що було запропоновано. Інші роблять те, що було запропоновано, але це не просто: усі покладаються на аналіз Git-виводу за допомогою команд для керування текстом або мов скриптів, які можуть бути присутніми не в кожній системі. Крім того, у більшості пропозицій використовуються порцелянові команди, вихід яких не розроблений для розбору сценарію ("фарфор" відноситься до команд, призначених для роботи з людиною; сценарії повинні використовувати команди "сантехніка" нижнього рівня).

Подальше читання:


Якщо ви хочете зробити це безпечно, для випадку використання у запитанні (сміття збору гілок відстеження, які були видалені на сервері, але все ще існують як локальні гілки) та лише з командами Git високого рівня, ви повинні

  • git fetch --prune(або git fetch -pпсевдонім або git prune remote originякий робить те ж саме без отримання даних, і це, мабуть, не те, чого ви хочете більшу частину часу).
  • Зверніть увагу на всі віддалені гілки, які повідомляються про видалені. Або, щоб знайти їх пізніше, git branch -v(будь-яка осиротіла гілка відстеження буде позначена "[пішла]").
  • git branch -d [branch_name] на кожній осиротілій гілці стеження

(що пропонують деякі інші відповіді).

Якщо ви хочете розробити сценарій рішення, то for-each-refце ваша відправна точка, як тут відповідь Марка Лонгайра і ця відповідь на інше запитання , але я не можу побачити спосіб його використання без написання циклу сценарію оболонки або використання xargs або чогось іншого .


Основне пояснення

Щоб зрозуміти, що відбувається, потрібно розуміти, що в ситуації відстеження гілок у вас не одна гілка, а три. (І нагадайте, що "гілка" означає просто вказівник на коміт.)

Враховуючи гілку відстеження feature/X, віддалений сховище (сервер) матиме цю гілку і називатиме її feature/X. У вашому локальному сховищі є гілка, remotes/origin/feature/Xщо означає: "Це те, що віддалений сказав мені, що його особливість / X-гілка була, коли ми говорили", і, нарешті, локальне сховище має гілку, feature/Xяка вказує на останню передачу даних і налаштована на "трек" remotes/origin/feature/X, що означає, що ви можете перетягувати та натискати, щоб їх вирівняти.

У якийсь момент хтось видалив feature/Xна пульті дистанційного керування. З цього моменту вам залишається місцевий feature/X(якого ви, мабуть, більше не хочете, оскільки робота над функцією X, імовірно, закінчена), і ваша remotes/origin/feature/X, безумовно, марна, оскільки її єдиною метою було запам’ятати стан гілки сервера .

І Git дозволить вам автоматично очистити зайве remotes/origin/feature/X- ось що і git fetch --pruneробиться, - але чомусь це не дозволяє автоматично видаляти власну feature/X... навіть якщо ваша інформація feature/Xвсе ще містить інформацію про відстеження сиріт, тому вона має цю інформацію визначити колишні гілки відстеження, які були повністю об'єднані. (Зрештою, він може дати вам інформацію, яка дозволяє вам робити операцію вручну.)


3
Це набагато безпечніша відповідь. Крім того, він буде працювати, якщо ви використовуєте робочий процес "Squash and Merge", на відміну від обраної відповіді.
Джейк Левітт

3
Це дійсно відповідає лише на просту частину питання "як знайти відійшли гілки" (і те з тією ж командою, яку вже написав jason.rickman у 2015 році), але потім говорить вам, щоб видалити всі гілки вручну, саме це і є ОП не хоче робити.
Voo

4
@Voo В цьому і полягає суть відповіді: не сказати вам, як це зробити (це просто бонус), а сказати вам, що відповідь полягає в тому, що не існує простого, простого способу зробити це. Яка правильна відповідь на питання, як було сформульовано.
Ендрю Спенсер

1
"Якщо ви хочете зробити це безпечно, для випадку використання у питанні .." - що заперечує більшість претензій першого абзацу. "[Git] може дати вам інформацію, яка дозволяє вам робити операцію вручну" - ми програмісти, тому дана інформація як список (який показаний) завдання все ще проста (наприклад. xargs). Існує також git aliasспростити ряд випадків.
користувач2864740

2
@ user2864740 Деякі читачі можуть вважати, що написання маленького скрипту bash підпадає під їх визначення "простий", і це ідеально захисна позиція, але я дав власну інтерпретацію "простого" у другому абзаці, а решта відповіді узгоджується з цим. І на захист моєї загальноприйнятої обмежувальної інтерпретації не всі користувачі Git мають xargsабо б'ють, і вони, мабуть, тут не шукають проблеми мікрокодування, настільки швидку відповідь вони можуть застосувати безпечно, не відволікаючись далі від своєї реальної мети .
Ендрю Спенсер

52

Тут я знайшов відповідь: Як я можу видалити всі гіти git, які були об'єднані?

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

Переконайтеся, що ми тримаємо господаря

Ви можете переконатися, що masterабо будь-яка інша гілка з цього питання не буде видалена, додавши ще одну grepпісля першої. У такому випадку ви підете:

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

Отже, якби ми хотіли зберегти master, developі, stagingнаприклад, ми пішли:

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

Зробіть це псевдонімом

Оскільки це трохи довго, ви можете додати псевдонім до свого .zshrcабо .bashrc. Шахта називається gbpurge(для 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'

Потім перезавантажте своє .bashrcабо .zshrc:

. ~/.bashrc

або

. ~/.zshrc

8
Корисно, але не те саме, що "видалити гілки не на віддалений"
dlsso

Зробіть відповідь @karlingen, я постійно отримую це як gbpurge у всіх моїх розробниках
Пітер Долан

50

Рішення для Windows

Для 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 $_}

Пояснення

git checkout master перемикається на головну гілку

git remote update origin --prune чорнослив віддалені гілки

git branch -vvотримує багатослівний вихід з усіх гілок ( git reference )

Select-String -Pattern ": gone]" отримує лише записи, з яких вони були видалені з віддаленого.

% { $_.toString().Trim().Split(" ")[0]} отримати назву філії

% {git branch -d $_} видаляє гілку


3
Дякую за це, хоча мені довелося додати .Trim()після .toString(), щоб видалити два пробіли перед назвою гілки.
Матвій

2
Дякую за цю команду. Мені довелося змінити git branch -dна git branch -Dінше, я отримую помилку, гілка не повністю об'єднана.
markieo

1
@markieo Ви , напевно , вже знаєте це, але для кого -то ще - git branch -Dце руйнівний і видалити локальну роботу , яку ви ще не натиснуті. -dзавжди в безпеці, просто будьте обережні -D.
Нейт Барбеттіні

33

Відповідність шаблону "відпала" у більшості інших рішень була для мене трохи страшною. Щоб бути більш безпечним, для цього використовується --formatпрапор, щоб витягнути стан відстеження кожної гілки .

Мені потрібна зручна для Windows версія, тому це видаляє всі гілки, які вказані як "пішли" за допомогою Powershell:

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

У першому рядку перелічено назву локальних гілок, гілка яких за течією "пішла". Наступний рядок видаляє порожні рядки (які виводяться для гілок, які не "пішли"), потім ім'я гілки передається команді для видалення гілки.


6
Цей --formatваріант видається досить новим; Мені потрібно було оновити git з 2.10.щось до 2.16.3, щоб отримати його. Це моя модифікація для 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
Це найкраще рішення поки що. Одне із пропозицій - використовувати refname:short. Тоді ви можете видалити рядок% { $_ -replace '^refs/heads/', '' }
ioudas

2
Це чудове рішення для вікон. Я користуюсь цим, як ця причина, це читабельніше git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | where { $_ -ne "" } | foreach { git branch -d $_ }Також, мабуть, хорошу ідею використовувати -dзамість -D. Видалення не повинно бути необхідним для гілок, які вже не є віддаленими.
Рубанов

31

Видаліть усі гілки, які були об'єднані в master, але не намагайтеся видалити сам master:

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

або додати псевдонім:

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

Пояснення:

git checkout master каси майстер відділення

git pull origin master переконайтеся, що місцева філія об'єднала всі віддалені зміни

git fetch -p видалити посилання на віддалені гілки, які були видалені

git branch -d $(git branch master --merged | grep master -v) видаліть усі гілки, які були об'єднані в головний, але не намагайтеся видалити сам master


3
Одне зауваження, це дуже корисно, але воно також видалить ті гілки, які ніколи не були натиснуті на пульт. Безпечніше лише перелічити відмінності, а потім скопіювати те, що ви насправді хочете видалити в git branch -Dкоманду
Zefiryn

Просто для уточнення, Зефірин має на увазі використання опції -D, яка не входить до складу одного вкладиша.
cs01

1
Або скопіюйте малі регістри, git branch -dякі повинні попередити про не натиснуті гілки.
акме

16
git fetch -p

Це обріже будь-які гілки, яких більше немає на віддаленому.


64
це видаляє віддалені посилання, але не самі локальні гілки. Хоча корисна команда, я не думаю, що це відповідає на питання ОП.
thataustin

Що? Це видаляє місцеві відділення для мене.
Алекс Холл

1
@AlexHall У віддаленому сховищі є відділення X; ти git checkout X; тепер у вашому сховищі є (локальна) гілка відстеження Xта віддалена гілка origin/X; віддалений сховище видаляється X; ти git fetch-p; у вашому локальному сховищі видалено не лише, origin/Xа й Xвидалено. Це те, що ти кажеш?
Ендрю Спенсер

16

Ще одна відповідь на купу, сильно виходячи з відповіді Патріка (що мені подобається, тому що, здається, усуває будь-яку неоднозначність щодо того, де gone]буде відповідати вgit branch результат), але додаючи зігнуту * nix.

У своєму найпростішому вигляді:

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

У мене це завершено у git-goneсценарії на моєму шляху:

#!/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 варіант здається досить новим; Мені потрібно було оновити git з 2.10.щось до 2.16.3, щоб отримати його.

EDIT: налаштовано, щоб включити пропозиції щодо refname:shortвід Benjamin W.

NB2 - Я протестував лише bash, отже, хешбанг, але, ймовірно, портативний sh.


Ви не можете пропустити sedчастину, використовуючи %(refname:short)в першому рядку?
Бенджамін В.

Здається, це працює для мене, і я маю це як акуратний псевдонім Git - найчистіше рішення з усіх них тут!
Бенджамін В.

2.13 введено --format. Ще один спосіб пропустити sedдеталь - це використовувати git update-ref -d. Зауважте, що це, ймовірно, дещо небезпечно, використання git for-each-refтут безпечніше (дано --shell).
gsnedders

Чудова відповідь, і це допомогло мені відповісти на мої власні запитання, коли дихати, --formatщоб зробити це самостійно. Лише одне питання: чому #!bash? Тут все схоже shна мене портативно .
Toby Speight

Це просто моя звичайна котельня, і ніде не тестувалася.
bxm

15

Можливо, буде корисним деякий, простий один рядок, щоб очистити всі локальні гілки, окрім майстра та розвитку

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

Дивовижна відповідь! Мені подобається, як можна легко пограти з 'git branch | grep -v "master" | grep -v "develop"подібними речами, перш ніж взяти на себе частину команди для видалення. 👏😊
finneycanhelp

Але це не дає відповіді на вищезазначене питання. Це видалить гілки, хоча віддалений все ще є.
Jim.B

13

Це видалить усі об'єднані локальні розгалуження, крім локальної основної посилання та тієї, що використовується зараз:

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

І це видалить усі гілки, які вже були вилучені з віддаленого сховища, на яке посилається " походження ", але все ще є локально доступними в " віддалених / вихідних ".

git remote prune origin

git branch -vv | grep 'gone]' | grep -v "\*" | awk '{print $1}' | xargs -r git branch -d Пояснення: Я вважаю за краще замінити на git branch --merged, git branch -vvщоб показати статус (пішов), тому що попередній git branch --mergedможе показати також майстер
jpmottin

13

Я не думаю, що для цього є вбудована команда, але безпечно робити наступне:

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

Під час використання -d, git відмовиться видаляти гілку, якщо вона повністю не об'єднана з HEADїї відділенням віддаленого відстеження за версією течії. Отже, ви завжди можете перевести цикл на вихід git for-each-refта спробувати видалити кожну гілку. Проблема з таким підходом полягає в тому, що я підозрюю, що ви, ймовірно, не хочете bug-fix-dвидаляти його лише тому, що origin/bug-fix-dмістить його історію. Натомість ви можете створити сценарій на зразок наступного:

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

Попередження: Я не перевіряв цей сценарій - використовуйте лише обережно ...


Я взяв на себе сміливість редагувати сценарій. Ще не даватиме гарантій, але він працює і, здається, працює зараз.
fwielstra

13

TL; DR:

Видаліть ВСІ локальні гілки, які не є віддаленими

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

Видаліть ВСІ локальні гілки, які не знаходяться на віддаленому І, які повністю об'єднані І, які не використовуються, як сказано в багатьох відповідях раніше.

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

Пояснення

  • git fetch -p обріже всі гілки, які вже не існують на віддалених
  • git branch -vv буде надрукувати місцеві гілки, а обрізана гілка буде позначена тегами gone
  • grep ': gone]' вибирає лише гілки, які вже немає
  • awk '{print $1}' відфільтруйте вихід, щоб відобразити лише назву гілок
  • xargs git branch -D переміститься над усіма лініями (гілками) і змусить видалити цю гілку

Чому git branch -Dі ні git branch -dіншого у вас буде для галузей, які не повністю злиті.

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

1
ти хотів сказати віддалений замість майстра?
тинос


7

На підставі інформації вище, це працювало для мене:

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

Він видаляє всі локальні гілки, які знаходяться ': gone] 'на віддаленому.


1
Схоже, це також видалить будь-яку гілку, яка "пішла" в останньому повідомленні про фіксацію.
dlsso

Він також видалить будь-яку гілку, яка має goneбудь-яку назву.
bfontaine

3
"git гілка -D git гілка -vv | grep ': пішла]' | awk '{print $ 1}' | xargs`" Це зробило роботу для мене.
Муту Ганапатій Натан

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

Наведена вище команда може бути використана для отримання гілок, які об'єднані та видалені у віддаленому режимі, та видаляє локальну гілку, яка більше не доступна у віддаленому


Краще рішення до сих пір, хоча я змінив його , grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -Dщоб змусити видалити всі
Shoaib

Небезпечно, якщо будь-яке з ваших імен філій міститиме підрядку в goneбудь-якому місці (наприклад usingonefunction).
Toby Speight

7

Нічого з цього насправді не підходило мені. Я хотів щось, що очистить усі локальні гілки, які відстежували віддалену гілку, у тому місці origin, де віддалена гілка була видалена ( gone). Я не хотів видаляти локальні гілки, які ніколи не були налаштовані для відстеження віддаленої гілки (тобто: мої місцеві гілки розробників). Також я хотів простий одноразовий лайнер, який просто використовує gitабо інші прості інструменти CLI, а не писати спеціальні сценарії. Я в кінцевому рахунку використовував трохи grepіawk зробити цю просту команду.

Це в кінцевому підсумку і закінчилося в моєму ~/.gitconfig:

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

Ось git config --global ...команда для того, щоб легко додати це як 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'

ПРИМІТКА. У команді config я використовую -dпараметр, git branchа не -Dяк у моїй фактичній конфігурації. Я використовую, -Dтому що я не хочу чути, як Git скаржиться на незанурені гілки. Можливо, ви хочете і цю функціональність. Якщо так, просто використовуйте -Dзамість -dцього кінця команду config.


Мені подобається підхід псевдоніма git. Швидке запитання: Навіщо робити git branch -vvі жабувати, : gone]а не робити git branch -vі грепатися [gone]?
lalibi

@lalibi, гарне запитання. Я не пам'ятаю. Я підозрюю, що щось не виявлялося лише з одним v. Якщо git branch -v | grep '[gone]'працює для вас, займіться цим. Це здається трохи чистішим.
Карл Вільбур

4

На основі Git Tip: Видалення старих локальних гілок , схоже на рішення Jason.rickman, я реалізував для цієї мети спеціальну команду під назвою git gone за допомогою 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 поєднує обрізку та перелічення "пропали" гілок:

$ 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

Потім ви можете натиснути на курок за допомогою git gone -dабо git gone -D.

Примітки

  • Регулярний вираз, який я використовував, є "$BRANCH/.*: gone]"там, де $BRANCHзазвичай було б origin. Це, ймовірно, не буде працювати, якщо ваш вихід Git локалізований на французькій мові тощо.
  • Себастьян Віснер також переніс його на Rust для користувачів Windows. Таку ще називають git gone .

Це, мабуть, має бути прийнятою відповіддю - git goneвиглядає як псевдонім git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d, який вирішує питання ОП.
alex

4

Маючи на увазі велику кількість низки інших відповідей , я закінчив наступне (я вважаю, git 2.13 і вище лише), який повинен працювати на будь-якій оболонці, схожій на UNIX:

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

Це, зокрема, використовує for-each-refзамість branch(як branchце "порцелянова" команда, призначена для читаного людиною результату, а не машинної обробки) і використовує його --shellаргумент, щоб отримати правильний вихід (це дозволяє нам не турбуватися про будь-який символ у назві посилання).


Це працює для мене, але було б також непогано, якби ви могли пояснити, що роблять інші кроки команди. Наприклад, я не знаю, що [ ! -z "$ref" ]означає. Я думаю, що мультилайн уже допоміг би. Але все ж дякую за ваш внесок!
KevinH

4

Ще одна відповідь, тому що жодне з рішень не відповідає моїм потребам щодо елегантності та крос-платформності:

Команда видалити локальні гілки не на віддаленому:

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

Щоб інтегрувати його з gitconfig, щоб його можна було запустити git branch-prune:

Баш

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'

(Потрібна допомога у пошуку універсальної команди для PowerShell та bash)

Чому ця відповідь найкраща?

  • Пропонує повне рішення: додає git branch-pruneкоманду до вашого git
  • Відмінно працює від Windows PowerShell
  • Основною ідеєю є куленепробивний метод @ jason.rickman з використаннямgit for-each-ref
  • Розбір і фільтрування проводяться --filterбез необхідності зовнішніх залежностей

Пояснення:

  • Додає новий псевдонім до вашого ~\.gitconfig. Після виконання цього ви можете просто зробитиgit branch-prune
  • Всередині цього псевдоніма:
    • Отримує гілки з --prune прапором, який "обрізає гілки віддаленого відстеження більше не на віддаленому"
    • Використання git for-each-refта --filter, щоб отримати список гілок, є [gone](без віддалених)
    • Прокручує цей список і безпечно видаляє гілку

1
Дякуємо, @ jerry-wu за те, що покращили дивовижність цього рішення до нескінченності.
Хімура

@ jerry-wu та остання редакція команди git config не працює в PowerShell ((
Himura

1

Я придумав цей сценарій баш. Вона завжди тримати гілки 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

Я використовую короткий метод, щоб зробити трюк, я рекомендую вам зробити так само, як це може заощадити кілька годин і дати вам більше видимості

Просто додайте наступний фрагмент у свій .bashrc (.bashprofile в macos).

git-cleaner() { git fetch --all --prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. Отримати всі віддалені
  2. Отримайте лише злиті гілки від git
  3. Видаліть із цього списку гілки "захищених / важливих"
  4. Видаліть решту (наприклад, чисті та злиті гілки)

Вам доведеться відредагувати grege regex, щоб відповідати вашим потребам (тут це запобігає видаленню master, prepredd та dmz)


git fetch --all --pruneзробив трюк. Дякую!
LeOn - Хан Лі

1

Це працювало для мене:

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

о, я двічі опублікував це на ... щойно побачив це під час перегляду дописів. Але ти справді міг би вказати на хлопця, який дав цю команду, замість того, щоб прийняти це як своє!
Dwza

Я отримую це з форуму, не намагайтеся бути розумним, просто прийміть відповідь або ігноруйте його
Fareed Alnamrouti

Навіть ніж ви могли ... все одно ... не приходили сюди, щоб обговорити це з вами. Як ви сказали .... Прийміть мій коментар або проігноруйте його;)
Dwza

0

Я не впевнений, як довго, але я зараз використовую git-up, який піклується про це.

Я це роблю, git upі він починає відслідковувати нові гілки та видаляє старі.

Просто для того, щоб зрозуміти, це не команда git команда поза https://github.com/aanand/git-up

До речі, він також приховує брудне дерево і робить знижки все ще просто git up.

Сподіваюся, це комусь стане в нагоді


2
Це не видаляє локальні гілки, які не існують на сервері.
Ешлі

0

Ось рішення, яке я використовую для рибної оболонки. Випробувано на Mac OS X 10.11.5, fish 2.3.0і 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

Кілька приміток:

Переконайтеся, що встановлено правильне base_branch. У цьому випадку я використовую developяк основну гілку, але це може бути що завгодно.

Ця частина дуже важлива: grep -v "\(master\|$base_branch\|\*\)". Це гарантує, що ви не видалите головний або основну гілку.

Я використовую git branch -d <branch>в якості додаткової обережності, щоб не видалити будь-яку гілку, яка не була повністю злита з поточною або поточною HEAD.

Простий спосіб перевірки - замінити git branch -d $fна echo "will delete $f".

Я припускаю, що я також повинен додати: ВИКОРИСТОВУЙТЕ ВАШ ВЛАСНИЙ РИЗИК!


0

Я написав сценарій Python за допомогою GitPython для видалення локальних гілок, які не існують на віддаленому.

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

Сподіваюся, хтось вважає це корисним, будь ласка, додайте коментарі, якщо я щось пропустив.


0

Якщо ви використовуєте zshоболонку з Oh My Zshвстановленим, найпростіший спосіб зробити це безпечно - використовувати вбудований автозаповнення.

Спочатку визначте, з якими гілками ви хочете видалити:

~ git branch --merged

  branch1
  branch2
  branch3
* master

це покаже вам список вже об’єднаних гілок

Після того, як ви дізнаєтесь декілька, які потрібно видалити, тоді введіть:

~ git branch -d 

Все, що вам потрібно зробити, це натиснути [вкладку], і він покаже вам список місцевих гілок. Скористайтеся вкладкою або просто натисніть [вкладку] ще раз, і ви можете перейти через них, щоб вибрати гілку за допомогою [enter].

Вкладка Виберіть гілки знову і знову, поки у вас не з’явиться список гілок, які ви бажаєте видалити:

~ git branch -d branch1 branch2 branch3

Тепер просто натисніть клавішу Enter, щоб видалити колекцію гілок.

Якщо ви не використовуєте zsh на своєму терміналі ... Отримайте його тут.


0

Мені подобається використовувати труби, тому що це полегшує читання команди.

Це моє рішення, якщо ви хочете видалити всі гілки, крім головного.

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

Щоб видалити інші гілки, які відповідають вашим критеріям, змініть перший і другий блоки.

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

-3

Ось простий відповідь, який працював на мене за допомогою клієнта git:

Видаліть сховище повністю зі свого комп'ютера, а потім перевірте ще раз.

Немає спілкування з ризикованими сценаріями.


2
А щодо галузей, над якими я зараз працюю: /
Mailo Světel

@ MailoSvětel, якщо вони знаходяться на пульті дистанційного керування, ви можете просто потягнути їх заново (пам'ятайте, щоб здійснити та
Juan Carlos Ospina Gonzalez

У моєму запитанні та в більшості відповідей ми намагаємось уникати зайвої роботи. Іншими словами: Що мені потрібно зробити, це просто видалити гілки, я більше не буду працювати. Видалити репо, клонувати його і почати відслідковувати гілки - для мене багато непотрібної роботи. Крім цього, деякі роботи можуть загубитися :(
Mailo Světel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.