Я использовал ответ Адама в течение многих лет. Тем не менее, есть некоторые случаи, когда он не вел себя так, как я ожидал:
- ветви, содержащие слово «master», игнорировались, например, «notmaster» или «masterful», а не только основная ветвь
- ветви, содержащие слово «dev», игнорировались, например, «dev-test», а не только ветка dev
- удаление веток, которые доступны из HEAD текущей ветки (то есть не обязательно master)
- в отключенном состоянии HEAD, удаляя каждую ветвь, достижимую из текущего коммита
1 и 2 были просты для решения, просто с изменением в регулярном выражении. 3 зависит от контекста того, что вы хотите (т.е. удаляете только те ветки, которые не были объединены в master или с вашей текущей веткой). 4 может привести к катастрофическим последствиям (хотя их можно будет восстановить git reflog
), если вы непреднамеренно запустили это в отключенном состоянии HEAD.
Наконец, я хотел, чтобы все было в одной строке, для которой не требовался отдельный скрипт (Bash | Ruby | Python).
TL; DR
Создайте псевдоним git «sweep», который принимает необязательный -f
флаг:
git config --global alias.sweep '!git branch --merged $([[ $1 != "-f" ]] \
&& git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \
| xargs git branch -d'
и вызвать его с помощью:
git sweep
или:
git sweep -f
Длинный, подробный ответ
Мне было проще создать пример git repo с некоторыми ветками и коммитами для проверки правильности поведения:
Создать новый репозиторий Git с одним коммитом
mkdir sweep-test && cd sweep-test && git init
echo "hello" > hello
git add . && git commit -am "initial commit"
Создайте несколько новых веток
git branch foo && git branch bar && git branch develop && git branch notmaster && git branch masterful
git branch --list
bar
develop
foo
* master
masterful
notmaster
Желаемое поведение: выберите все объединенные ветви, кроме: master, development или current
Оригинальное регулярное выражение пропускает ветки "masterful" и "notmaster":
git checkout foo
git branch --merged | egrep -v "(^\*|master|dev)"
bar
С обновленным регулярным выражением (которое теперь исключает «развернуть», а не «dev»):
git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
masterful
notmaster
Переключитесь на ветку foo, сделайте новый коммит, затем извлеките новую ветку foobar на основе foo:
echo "foo" > foo
git add . && git commit -am "foo"
git checkout -b foobar
echo "foobar" > foobar
git add . && git commit -am "foobar"
Моя текущая ветка - это foobar, и если я перезапущу приведенную выше команду для вывода списка веток, которые я хочу удалить, ветка "foo" будет включена, даже если она не была объединена с master:
git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
foo
masterful
notmaster
Однако, если я запускаю ту же команду на master, ветка "foo" не включается:
git checkout master && git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
masterful
notmaster
И это просто потому, что по git branch --merged
умолчанию используется заголовок текущей ветви, если не указано иное. По крайней мере, для моего рабочего процесса я не хочу удалять локальные ветви, если они не были объединены с master, поэтому я предпочитаю следующий вариант:
git checkout foobar
git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)"
bar
masterful
notmaster
Отделенное состояние HEAD
Опора на поведение по умолчанию git branch --merged
имеет еще более существенные последствия в отключенном состоянии HEAD:
git checkout foobar
git checkout HEAD~0
git branch --merged | egrep -v "(^\*|^\s*(master|develop)$)"
bar
foo
foobar
masterful
notmaster
Это удалило бы ветку, на которой я только что находился, «foobar» вместе с «foo», что почти наверняка не является желаемым результатом. С нашей пересмотренной командой, однако:
git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)"
bar
masterful
notmaster
Одна строка, включая фактическое удаление
git branch --merged $(git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" | xargs git branch -d
Все закутано в псевдоним git "sweep":
git config --global alias.sweep '!git branch --merged $([[ $1 != "-f" ]] \
&& git rev-parse master) | egrep -v "(^\*|^\s*(master|develop)$)" \
| xargs git branch -d'
Псевдоним принимает необязательный -f
флаг. Поведение по умолчанию - удаление только тех веток, которые были объединены с главной, но -f
флаг удалит ветки, которые были объединены с текущей веткой.
git sweep
Deleted branch bar (was 9a56952).
Deleted branch masterful (was 9a56952).
Deleted branch notmaster (was 9a56952).
git sweep -f
Deleted branch foo (was 2cea1ab).
git branch -D
удаляет любую ветку, независимо от того, была ли она объединена или нет.