Как удалить локальные ветви отслеживания, которые больше не существуют на удаленном
С git remote prune origin
Я могу удалить локальные ветки, которых больше нет на пульте.
Но я также хочу удалить локальные ветви, которые были созданы из этих удаленных ветвей (было бы неплохо проверить, не были ли они объединены).
Как я могу это сделать?
41 ответ
После сокращения вы можете получить список удаленных веток с git branch -r
, Список ветвей с их удаленной веткой отслеживания можно получить с помощью git branch -vv
, Таким образом, используя эти два списка, вы можете найти удаленные ветви отслеживания, которых нет в списке удаленных.
Эта строка должна сделать свое дело (требует bash
или же zsh
, не будет работать со стандартной оболочкой Bourne):
git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d
Эта строка получает список удаленных веток и передает его в egrep
через стандартный ввод. И фильтрует ветви, которые имеют удаленную ветвь отслеживания (используя git branch -vv
и фильтрация для тех, которые имеют origin
) затем получить первый столбец этого вывода, который будет именем ветви. Наконец, передав все имена ветвей в команду delete branch.
Так как он использует -d
Опция не удалит ветви, которые не были объединены с той ветвью, в которой вы работали при выполнении этой команды.
Также помните, что вам нужно будет запустить git fetch --prune
во-первых, иначе git branch -r
все равно увидим удаленные ветки.
Если вы хотите удалить все локальные ветви, которые уже объединены в master, вы можете использовать следующую команду:
git branch --merged master | grep -v '^[ *]*master$' | xargs git branch -d
На фоне информации, представленной git help fetch
есть этот маленький пункт:
-p, --prune
After fetching, remove any remote-tracking branches which no longer exist on the remote.
Так что, возможно, git fetch -p
это то, что вы ищете?
РЕДАКТИРОВАТЬ: Хорошо, для тех, кто все еще обсуждает этот ответ через 3 года после факта, вот немного больше информации о том, почему я представил этот ответ...
Во-первых, OP говорит, что они хотят "удалить также те локальные ветви, которые были созданы из тех удаленных ветвей [которые больше не находятся на удаленных]". Это не однозначно возможно в git
, Вот пример.
Допустим, у меня есть репо на центральном сервере, и у него есть две ветви, называемые A
а также B
, Если я клонирую этот репо в мою локальную систему, мой клон будет называться локальными ссылками (пока не актуальными) origin/A
а также origin/B
, Теперь допустим, я делаю следующее:
git checkout -b A origin/A
git checkout -b Z origin/B
git checkout -b C <some hash>
Уместные факты: я по какой-то причине решил создать ветку в моем локальном репо, имя которой отличается от его источника, и у меня также есть локальная ветвь, которая (пока) не существует в исходном репо.
Теперь скажем, я удаляю оба A
а также B
ветки на удаленном репо и обновление моего локального репо (git fetch
какой-то формы), что вызывает мои местные ссылки origin/A
а также origin/B
исчезнуть. Теперь у моего локального репо еще есть три филиала, A
, Z
, а также C
, Ни у одного из них нет соответствующей ветки на удаленном репо. Два из них были "созданы из... удаленных веток", но даже если я знаю, что раньше была ветка с именем B
о происхождении, я не могу знать, что Z
был создан из B
потому что он был переименован в процессе, вероятно, по уважительной причине. Таким образом, на самом деле, без какого-либо внешнего метаданных происхождения ветви записи процесса или человека, который знает историю, невозможно определить, какая из трех ветвей, если таковая имеется, предназначена для удаления ОП. Без какой-либо внешней информации, которая git
не поддерживает автоматически для вас, git fetch -p
is about as close as you can get, and any automatic method for literally attempting what the OP asked runs the risk of either deleting too many branches, or missing some that the OP would otherwise want deleted.
There are other scenarios, as well, such as if I create three separate branches off origin/A
to test three different approaches to something, and then origin/A
уходит. Now I have three branches, which obviously can't all match name-wise, but they were created from origin/A
, and so a literal interpretation of the OPs question would require removing all three. However, that may not be desirable, if you could even find a reliable way to match them...
Есть отличный пакет NPM, который делает это для вас (и он должен работать кроссплатформенно).
Установите его с помощью: npm install -g git-removed-branches
А потом git removed-branches
покажет вам все несвежие местные филиалы, и git removed-branches --prune
на самом деле удалить их.
Это удалит локальные ветви, для которых удаленные ветви отслеживания были удалены. (Убедитесь, что вы на master
ветка!)
git checkout master
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -d
Подробности:
git branch -vv
отображает "ушел" для локальных веток, что удаленный был удален.mybranch abc1234 [origin/mybranch: gone] commit comments
-d
проверим, был ли он объединен (-D
удалим его независимо)error: The branch 'mybranch' is not fully merged.
Windows Solution
Для Microsoft Windows Powershell:
git checkout master; git remote update origin --prune; git branch -vv | Select-String -Pattern ": gone]" | % { $_.toString().Split(" ")[0]} | % {git branch -d $_}
Explaination
git checkout master
переключается на главную ветку
git remote update origin --prune
чернослив удаленных веток
git branch -vv
получает подробный вывод всех ветвей ( ссылка на git)
Select-String -Pattern ": gone]"
получает только записи, где они были удалены из удаленного.
% { $_.toString().Split(" ")[0]}
получить название филиала
% {git branch -d $_}
удаляет ветку
Можно настроить Git для автоматического удаления ссылок на удаленные удаленные ветви при извлечении:
git config --global fetch.prune true
При звонке git fetch
или же git pull
после этого ссылки на удаленные удаленные ветви удаляются автоматически.
Как отмечает @tzacks... для этого есть пакет npm. Просто делать:
npx git-removed-branches --prune
(Я бы прокомментировал, но репутации недостаточно)
В нем будут перечислены локальные ветви, удаленная ветвь отслеживания которых удалена
$ git remote prune origin --dry-run
Если вы хотите отменить ссылку на эти локальные ветви из местных, которые не отслеживаются
$ git remote prune origin
Если вы используете Windows и Powershell, вы можете использовать следующее для удаления всех локальных веток, которые были объединены с веткой, извлеченной в данный момент:
git branch --merged | ? {$_[0] -ne '*'} | % {$_.trim()} | % {git branch -d $_}
объяснение
- Перечисляет текущую ветку и ветви, которые были объединены в нее
- Отфильтровывает текущую ветку
- Убирает все начальные или конечные пробелы из
git
вывод для каждого оставшегося имени ветви - Удаляет объединенные локальные филиалы
Стоит бежать git branch --merged
сначала для того, чтобы убедиться, что он только удалит то, что вы ожидаете.
(Портировано / автоматизировано с http://railsware.com/blog/2014/08/11/git-housekeeping-tutorial-clean-up-outdated-branches-in-local-and-remote-repositories/.)
Я хотел что-то, что очистило бы все локальные ветви, которые отслеживали удаленную ветку, на 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'
ПРИМЕЧАНИЕ: использование -D
флаг для git branch
может быть очень опасным. Итак, в приведенной выше команде config я использую -d
возможность git branch
скорее, чем -D
; я использую -D
в моем реальном конфиге. я использую -D
потому что я не хочу, чтобы Гит жаловался на незакрепленные ветви, я просто хочу, чтобы они ушли. Вы можете также хотеть эту функциональность. Если это так, просто используйте -D
вместо -d
в конце этой команды конфигурации.
Еще короче и безопаснее однострочник:
git branch -d $(git branch --merged | cut -c 3-)
Обязательно извлеките ветку, которая еще не объединена, прежде чем запускать ее. Потому что вы не можете удалить ветку, в которой вы сейчас зарегистрированы.
Вот мое решение:
git fetch -p
git branch -vv | grep ": gone" | awk '{print $1}' | xargs git branch -d
-p - удалить все ссылки удаленного отслеживания, которые больше не существуют на пульте дистанционного управления. Итак, первым шагом будет удаление ссылок на удаленные ветки.
-vv - показывает sha1 и строку темы коммита для каждого заголовка, а также связь с восходящей веткой (если есть). 2-й шаг получит все локальные ветки, а команда grep отфильтрует ветки, которые были удалены.
Кажется, что не существует безопасной однострочной строки, слишком много крайних случаев (например, ветвь с именем master в качестве имени и т. Д.). Безопаснее всего эти шаги:
git branch -vv | grep 'gone]' > stale_branches.txt
- просмотреть файл и удалить строки для веток, которые вы хотите сохранить (например, ветку master!); вам не нужно редактировать содержимое любой строки
awk '{print $1}' stale_branches.txt | xargs git branch -d
Удалить удаленные ветки
$git remote prune origin
Чтобы удалить локальные ветви, которые уже объединены
$git branch -D $(git branch --merged)
Эта команда и приведенный ниже сценарий работают в Linux и Windows с Git Bash (MinGW).
Лучше всего использовать внутренние команды git, чтобы комментарии или имена случайно не совпадали и не удаляли ветку, которую вы не хотите удалять. Есть много внутренних «атомов», которые можно использовать с параметром формата 's для вывода желаемой информации. Таким образом, нам не нужно полагаться на конвейер для
awk
или же
grep
для проверки регулярного выражения на выходе, которое может содержать ненужную информацию.
Команда ниже использует только
git for-each-ref
Внутренние низкоуровневые команды для отображения только потерянных локальных ветвей. Когда у вас есть это, вы можете подключиться к
git branch -D
. Кроме того, не забудьте сначала обрезать и получить удаленные ссылки, иначе он не найдет совпадений:
git fetch -p
git for-each-ref --format '%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' 'refs/heads/**' | xargs -r git branch -D
Вот разбивка:
git fetch -p
- обрезать удаленные ссылки и получить новые с удаленного сервера
git for-each-ref --format
- перечисляет все ссылки, используя определенный выходной формат.
%(if:equals=[gone])%(upstream:track)
- вывод только в том случае, если восходящая ветвь отслеживания "[ушла]".
%(then)%(refname:short)%(end)
- вывести название ветки (когда трекинг пропал).
refs/heads/**
- ограничение на количество ссылок в заголовке (для эффективности).
| xargs -r git branch -D
- вывод трубы в качестве параметров для удаления. В
-r
указывает на игнорирование пустого ввода.
Само по себе это решение длинное, его смешно набирать, и его трудно запомнить. К счастью, добавлять собственные команды в git несложно. Ниже приведен сценарий, который использует ту же команду, что и выше, но позволяет пользователю видеть, какие ветки будут выбраны с помощью
--dry-run
вариант.
я назвал свой файл
git-prune-local
и бросил его в папку, которая была включена в мой
PATH
. Также требуются разрешения на выполнение (
chmod 755 git-prune-local
).
Git автоматически ищет исполняемые файлы, такие как
git-[command]
. При этом вам нужно только ввести
git prune-local
для удаления правильных ветвей.
git-prune-local
#!/bin/sh
if [ $# -gt 1 ] || ([ ! -z $1 ] && [ $1 != "--dry-run" ])
then
echo "Usage: git prune-local [--dry-run]"
exit
fi
git fetch -p --quiet
branchesToDelete=$(git for-each-ref --format '%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' 'refs/heads/**')
while read -r branch
do
if [ ! -z $branch ]
then
if [ ! -z $1 ]
then
echo $branch
else
git branch -D $branch
fi
fi
done <<< "$branchesToDelete"
Поздно к празднику, но вы можете использовать параметр --no-contains, чтобы избежать удаления вашей основной ветки при использовании ветки --merged
git branch --no-contains master --merged master | xargs git branch -d
В Powershell:
git branch -D (git branch --merged |% { $_.trim() } )
Основываясь на ответах выше, я использую этот более короткий вкладыш:
git remote prune origin | grep pruned | awk 'BEGIN{FS="/"} ; { print $2 }' | xargs git branch -d
В OS X, которая поддерживаетcut -w
git branch -d $(git branch -vv | grep ': gone]' | cut -w -f 2 )
объяснение
удалить следующие ветки
git branch -d ...
показать удаленные ветки
$ git branch -vv | grep ': gone]'
chore/TECH-456 a4bdac8ac8 [origin/TECH-456: gone] chore: syntax error
feature/TECH-678 0342f8e277 [origin/feature/TECH-678: gone] Added IsGross in new Income Details
сохранить название ветки
$ git branch -vv | grep ': gone]' | cut -w -f 2
chore/TECH-456
feature/TECH-678
Не уверен, как это сделать все сразу, но мерзавец git branch -d <branchname>
удалит локальную ветку ТОЛЬКО если она полностью объединена. Обратите внимание на строчную букву d.
git branch -D <branchname>
(обратите внимание на заглавную D) удалит местное отделение независимо от его объединенного статуса.
Сделать это можно с помощью нескольких простых действий:
- Выведите все ваши ветки во временный файл:
git branch > branches.tmp
- Откройте файл и удалите ветки, чтобы исключить их удаление из вашего локального (ветки, такие как
develop
/master
/main
/...
) - Передать имя ветки в
xargs
кcat
команда и удалить ветки:
cat branches.tmp | xargs git branch -D
Вариант Шлейса у меня не работает (Ubuntu 12.04), поэтому позвольте мне предложить мои (ясные и блестящие:) варианты:
Вариант 1 (я бы предпочел этот вариант):
git for-each-ref --format='%(refname:short) %(upstream)' refs/heads/ | awk '$2 !~/^refs\/remotes/' | xargs git branch -D
Вариант 2:
а. Пробный прогон:
comm -23 <( git branch | grep -v "/" | grep -v "*" | sort ) <( git br -r | awk -F '/' '{print $2}' | sort ) | awk '{print "git branch -D " $1}'
б. Удалить ветки:
comm -23 <( git branch | grep -v "/" | grep -v "*" | sort ) <( git br -r | awk -F '/' '{print $2}' | sort ) | xargs git branch -D
Это работает для меня с использованием git 2.21.0 - он удаляет локальные ветки отслеживания, которые объединены в HEAD
где я раньше --set-upstream
при нажатии (я использую push.default=upstream
потому что он лучше всего работает с несколькими пультами дистанционного управления), и эта восходящая ветвь с тех пор была удалена fetch --prune
(или неявно, если fetch.prune=true
в конфигурации git):
git branch -vv --merged | grep ': gone]' | awk '{print $1}' | xargs git branch -d
Использование --merged
а также -d
сделайте это очень "безопасным" удалением. Более агрессивная версия может отбросить--merged
и использовать -D
Вы можете использовать эту команду:
git branch --merged master | grep -v "\* master" | xargs -n 1 git branch -d
Git Clean: удаление уже слитых веток, включая разделение команд
В Windows с Powershell, чтобы удалить все локальные ветки, которые были удалены на удаленном компьютере, независимо от статуса слияния, это поможет:
git fetch -p
git branch --v | ? {$_.contains('[gone]')} | % {$_.trim().split()[0].trim()} | % {git branch -D $_}
-
git branch --v
Предоставляет подробный список ваших филиалов, включая статус. -
? {$_.contains('[gone]')}
Правда, если[gone]
настоящее -
% {$_.trim().split()[0].trim()}
Дает нам только имя ветки -
% {git branch -D $_}
Принудительное удаление локальной ветки, даже если она не объединена
Если вы хотите избежать изменения несвязанных ветвей-D
к-d
и обрабатывать их вручную.
Используя вариант ответа @wisbucky, я добавил в качестве псевдонима следующее ~/.gitconfig
файл:
pruneitgood = "!f() { \
git remote prune origin; \
git branch -vv | perl -nae 'system(qw(git branch -d), $F[0]) if $F[3] eq q{gone]}'; \
}; f"
С этим простым git pruneitgood
очистит как локальные, так и удаленные ветви, которые больше не нужны после слияний.
Ниже приводится адаптация ответа @wisbucky для пользователей Windows:
for /f "tokens=1" %i in ('git branch -vv ^| findstr ": gone]"') DO git branch %i -d
Я использую шикарный мерзавец и, к сожалению, PS не любит голых for
поэтому я создал простой командный скрипт с именем PruneOrphanBranches.cmd:
@ECHO OFF
for /f "tokens=1" %%i in ('git branch -vv ^| findstr ": gone]"') DO CALL :ProcessBranch %%i %1
GOTO :EOF
:ProcessBranch
IF /I "%2%"=="-d" (
git branch %1 %2
) ELSE (
CALL :OutputMessage %1
)
GOTO :EOF
:OutputMessage
ECHO Will delete branch [%1]
GOTO :EOF
:EOF
Вызовите его без параметров, чтобы увидеть список, а затем вызовите его с помощью "-d" для фактического удаления или "-D" для любых ветвей, которые не полностью объединены, но которые вы все равно хотите удалить.
Версия Powershell git branch --merged master | grep -v '^[ *]*master$' | xargs git branch -d
git branch --merged master | %{ if($_ -notmatch '\*.*master'){ git branch -d "$($_.Trim())" }}
Это удалит все локальные ветви, которые были объединены в master, пока вы находитесь в master ветке.
git checkout master
переключать.
Основываясь на ответах выше, я пришел с этим решением в одну строку:
git remote prune origin; git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d