Как удалить локальные ветви отслеживания, которые больше не существуют на удаленном

С 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 в качестве имени и т. Д.). Безопаснее всего эти шаги:

  1. git branch -vv | grep 'gone]' > stale_branches.txt
  2. просмотреть файл и удалить строки для веток, которые вы хотите сохранить (например, ветку master!); вам не нужно редактировать содержимое любой строки
  3. 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
Другие вопросы по тегам