Удалите локальные теги git, которых больше нет в удаленном хранилище.

469

Мы используем теги в git как часть нашего процесса развертывания. Время от времени мы хотим очистить эти теги, удалив их из нашего удаленного хранилища.

Это довольно просто. Один пользователь удаляет локальный тег и удаленный тег в одном наборе команд. У нас есть небольшой сценарий оболочки, который объединяет оба этапа.

2-й (3-й, 4-й, ...) пользователь теперь имеет локальные теги, которые больше не отображаются на пульте.

Я ищу команду, подобную git remote prune originкоторой очищает локально отслеживающие ветви, для которых удаленная ветвь была удалена.

В качестве альтернативы можно использовать простую команду для отображения удаленных тегов для сравнения с локальными тегами, возвращаемыми через git tag -l.

Kend
источник
2
Я предложил новую функцию в git для поддержки удаления
Адам Монсен,
1
Примечание: с Git 2.17 (Q2 2018) простое git config fetch.pruneTags trueзаставит вас git fetchделать то, что вы хотите! Смотрите мой ответ на этот другой вопрос .
VonC
2
Повторное размещение комментария к одному из ответов ниже: По крайней мере, с git 2.18.0 можно также использовать этот синтаксис: git fetch --prune --prune-tags origin
zutnop

Ответы:

71

Хороший вопрос. :) У меня нет полного ответа ...

Тем не менее, вы можете получить список удаленных тегов через git ls-remote. Чтобы вывести список тегов в репозитории, на который ссылаются origin, вы должны выполнить:

git ls-remote --tags origin

Это возвращает список хэшей и понятных имен тегов, например:

94bf6de8315d9a7b22385e86e1f5add9183bcb3c        refs/tags/v0.1.3
cc047da6604bdd9a0e5ecbba3375ba6f09eed09d        refs/tags/v0.1.4
...
2f2e45bedf67dedb8d1dc0d02612345ee5c893f2        refs/tags/v0.5.4

Вы, конечно, могли бы собрать bash-скрипт, чтобы сравнить теги, сгенерированные этим списком, с тегами, которые вы используете локально. Посмотрите на git show-ref --tags, который генерирует имена тегов в той же форме, что и git ls-remote).


Кроме того, git show-refесть вариант, который противоположен тому, что вы хотели бы. Следующая команда выведет список всех тегов в удаленной ветке, которых у вас нет локально:

git ls-remote --tags origin | git show-ref --tags --exclude-existing
Майк Вест
источник
Спасибо майк Я сверну свой собственный скрипт bash, используя каждый список для сравнения.
kEND
11
Ответ Ричарда В. делает это намного элегантнее, если вас не интересует сложный сценарий.
Кайл Хейронимус
1
Дополнительное примечание о тегах, отсутствующих локально, может быть расширено для проверки большего количества пультов:git remote | xargs -L 1 git ls-remote --tags | git show-ref --tags --exclude-existing
Palec
Смотрите следующий ответ для более простого решения
sfletche
Git поддерживает --prune-tags. Не знаю, какая версия была представлена. git-scm.com/docs/git-fetch#git-fetch---prune-tags
Джон Клоян
1056

Это отличный вопрос, мне было интересно то же самое.

Я не хотел писать сценарий, поэтому искал другое решение. Ключ обнаруживает, что вы можете удалить тег локально, а затем использовать git fetch, чтобы «вернуть его» с удаленного сервера. Если тег не существует на удаленном компьютере, он останется удаленным.

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

git tag -l | xargs git tag -d
git fetch --tags

Эти:

  1. Удалить все теги из локального репо. FWIW, xargs помещает каждый вывод тега с помощью «tag -l» в командную строку для «tag -d». Без этого git ничего не удалит, потому что не читает stdin (глупый git).

  2. Получить все активные теги из удаленного репо.

Это даже работает на Windows.

Ричард В
источник
57
Это, должно быть, мой любимый git-ответ на StackOverflow. Он сочетает в себе знания, простоту и хитрость и объясняет все. Великий
тымтам
25
как отмечено в отдельном ответе, это удаляет ВСЕ локальные теги, и те, которые не находятся в удаленном репо, очевидно, не будут воссозданы
второе
13
ПОЭТОМУ это должно быть совершенно ненужным. Там должна быть git tag prune originкоманда.
void.pointer
9
Это может не работать для всех. Вы должны сделать git fetch --tags, чтобы быть в безопасности.
Адам Куркевич
5
Я должен был пойти, git tag -l | %{git tag -d $_}чтобы заставить это работать в PowerShell. Не уверен ни в ком другом.
Ален
244

От Git v1.7.8 до v1.8.5.6 вы можете использовать это:

git fetch <remote> --prune --tags

Обновить

Это не работает на более новых версиях git (начиная с v1.9.0) из-за commit e66ef7ae6f31f2 . Я действительно не хочу удалять это, так как это действительно работало для некоторых людей.

Как предлагает "Chad Juliano", со всей версией Git начиная с v1.7.8, вы можете использовать следующую команду:

git fetch --prune <remote> +refs/tags/*:refs/tags/*

Вам может потребоваться заключить часть тегов в кавычки (например, в Windows), чтобы избежать расширения по шаблону:

git fetch --prune <remote> "+refs/tags/*:refs/tags/*"
loganfsmyth
источник
2
Я ссылаюсь на документацию, которая поставляется с Git для Windows 1.9.4-preview20140611 (и я подозреваю, что все предыдущие версии). Я получаю доступ к указанной документации с помощью "git fetch --help" [quote] Теги не подлежат сокращению, если они выбираются только из-за автоматического следования тега по умолчанию или из-за опции --tags. [/
Quote
2
git fetch --prune <remote> +refs/tags/*:refs/tags/*не работал в ZSH, однако он работает в BASH
Alex
3
@Alex Это только потому, что zsh расширяется, *но если вы пишете одинарную кавычку, все будет хорошо.
НФС
3
@ v01pe - теперь существует ярлык git --prune-tags, доступный начиная с git 2.17.0, описанный в документации по разделу PRUNING : git-scm.com/docs/git-fetch/2.17.0 Из документа: - Опция -prune-tags эквивалентна объявлению refs / tags / *: refs / tags / * в refspecs пульта. Эквиваленты: git fetch origin --prune --prune-tagsИЛИ git fetch origin --prune 'refs/tags/*:refs/tags/*'ИЛИ git fetch <url of origin> --prune --prune-tagsИЛИgit fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
mkisaacs
3
git fetch origin --prune --prune-tagsудалить как удаленные отслеживания ветки и теги. проверил в версии git 2.18.
Number945
158

Если вам нужны только те теги, которые существуют на пульте, просто удалите все свои локальные теги:

$ git tag -d $(git tag)

А затем получить все удаленные теги:

$ git fetch --tags
newmangt
источник
1
безупречно, у меня были проблемы с XARGS, он не находит некоторые теги
Марсио Тошио
3
@ocroquette, я не уверен, как это лучше, чем xargs. Если у вас больше тегов ARG_MAXили подобных ограничений, это не сработает. Маловероятно, но возможно, и поэтому xargsэто здорово.
Пол Дрейпер
2
«приятно» - субъективная вещь, каждый будет высказывать свое мнение. Насчет ARG_MAX это правда. Тем не менее, в системах, которые я использую, ARG_MAX намного больше, чем количество тегов, которые у меня есть в любом хранилище, поэтому я не против ограничения, так же как я не против этого, когда пишу "ls * .jpg" ,
Окрокет
2
самое
2
git config --global alias.prune-tags '!git tag -d $(git tag) && git fetch --tags'Обязательный псевдоним команды. Наслаждаться. :-)
Карл Уилбур
87

Похоже, последние версии Git (я на git v2.20) позволяют просто сказать

git fetch --prune --prune-tags

Гораздо чище!

https://git-scm.com/docs/git-fetch#_pruning

Вы также можете настроить git так, чтобы он всегда удалял теги при получении:

git config fetch.pruneTags true

Если вы хотите удалять теги только при извлечении с определенного пульта, вы можете использовать эту remote.<remote>.pruneTagsопцию. Например, чтобы всегда удалять теги при извлечении из источника, но не с других пультов,

git config remote.origin.pruneTags true
Николас Кэри
источник
Великий. Я адаптировал его для публикации на SOes -> ó Cómo puedo delear las etiquetas de Git que solo tengo en local? ,
Федорки 'ТАК прекрати вредить'
Отлично! Я встретил ошибку git push с «git-shell умер от сигнала 13». Нет эффекта даже при увеличенном http.postbuffer. После включения GIT_TRACE_PACKET и GIT_TRACE я увидел переход к недействительным ссылкам / тегам, поэтому использование «--prune-tags» разрешило его. Большое спасибо!
Ивеллиос
78

Все версии Git начиная с v1.7.8 понимают git fetchс refspec, тогда как начиная с v1.9.0 --tagsопция переопределяет эту --pruneопцию. Для решения общего назначения попробуйте это:

$ git --version
git version 2.1.3

$ git fetch --prune origin "+refs/tags/*:refs/tags/*"
From ssh://xxx
 x [deleted]         (none)     -> rel_test

Подробнее о том, как изменилось поведение «--tags» с «--prune» в Git v1.9.0, смотрите: https://github.com/git/git/commit/e66ef7ae6f31f246dead62f574cc2acb75fd001c

Чад Джулиано
источник
7
Это должен быть главный ответ. Это одна команда git, без bash, pipe и xargs.
Дж. Сильви Дэвис
1
Заменены originна upstreamи git исправил мои локальные теги, основанные на восходящем потоке; Далее git push origin :<deleted-tag-name>обновил мой GitHub форк, удалив удаленный тег.
Линне
3
По крайней мере, с git 2.18.0 можно также использовать этот синтаксис:git fetch --prune --prune-tags origin
Martin
3
Начиная с git 2.17.0 - опция --prune-tags была включена и подробно описана в разделе PRUNING с помощью эквивалентных команд в следующем документе: git-scm.com/docs/git-fetch/2.17.0 git fetch origin --prune --prune-tags ИЛИ git fetch origin --prune 'refs/tags/*:refs/tags/*' ИЛИ git fetch <url of origin> --prune --prune-tags ИЛИgit fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
mkisaacs
8

Git изначально поддерживает очистку локальных тегов:

git fetch --tags --prune

Эта команда извлекает последние теги и удаляет все удаленные теги.

Нирав шах
источник
Кажется, это должен быть "--prune" вместо "--prune-tags", иначе это то, что мне нужно, спасибо.
AnyDev
У меня проблема с исходным деревом, и мне не удалось выдвинуть некоторые ссылки на ...: Это работает для меня :) Большое спасибо
Abhishek Thapliyal
4

Показать разницу между локальными и удаленными тегами:

diff <(git tag | sort) <( git ls-remote --tags origin | cut -f2 | grep -v '\^' | sed 's#refs/tags/##' | sort)
  • git tag дает список локальных тегов
  • git ls-remote --tags дает список полных путей к удаленным тегам
  • cut -f2 | grep -v '\^' | sed 's#refs/tags/##' анализирует только имя тега из списка путей удаленных тегов
  • Наконец, мы сортируем каждый из двух списков и различаем их

Строки, начинающиеся с «<», - это ваши локальные теги, которых больше нет в удаленном репо. Если их немного, вы можете удалить их вручную один за другим, если их много, вы сделаете больше операций по нарезке и обвязке для их автоматизации.

dotstaraj
источник
2
Пожалуйста, рассмотрите возможность добавления пояснения к вашему коду. Это определенно улучшит качество вашего ответа.
Honk
Полная команда для удаления всех удаленных тегов, отсутствующих локально, будет:diff <(git tag | sort) <( git ls-remote --tags origin | cut -f2 | grep -v '\^' | sed 's#refs/tags/##' | sort) | grep ">" | cut -c3- | xargs -I{} git push origin :refs/tags/{}
Даниэль Геригер,
Если вам нужно сделать такой diff и отобразить хеш коммита одновременно: diff <(git show-ref --tags | grep -v '{}' | awk '{print $1 " " $2}') <(git ls-remote --tags origin | grep -v '{}' | awk '{print $1 " " $2}')
piroux
Это сравнение было именно то, что я искал, спасибо. Единственное, что меня смущает, это то, что он также выводит пару строк, которые начинаются не со стрелки <, а с числа, за которым следует запятая, а затем то, что выглядит как первые три символа хеша коммита (?), например 7,8d4...
Кей
3

Просто добавили команду git sync-local-tags в вилку Gem pivotal_git_scripts на GitHub:

https://github.com/kigster/git_scripts

Установите гем, затем запустите «git sync-local-tags» в своем хранилище, чтобы удалить локальные теги, которые не существуют на удаленном компьютере.

В качестве альтернативы вы можете просто установить этот скрипт ниже и назвать его «git-sync-local-tags»:


#!/usr/bin/env ruby

# Delete tags from the local Git repository, which are not found on 
# a remote origin
#
# Usage: git sync-local-tags [-n]
#        if -n is passed, just print the tag to be deleted, but do not 
#        actually delete it.
#
# Author: Konstantin Gredeskoul (http://tektastic.com)
#
#######################################################################

class TagSynchronizer
  def self.local_tags
    `git show-ref --tags | awk '{print $2}'`.split(/\n/)
  end

  def self.remote_tags
    `git ls-remote --tags origin | awk '{print $2}'`.split(/\n/)
  end

  def self.orphaned_tags
    self.local_tags - self.remote_tags
  end

  def self.remove_unused_tags(print_only = false)
    self.orphaned_tags.each do |ref|
      tag = ref.gsub /refs\/tags\//, ''
      puts "deleting local tag #{tag}"
      `git tag -d #{tag}` unless print_only
    end
  end
end

unless File.exists?(".git")
  puts "This doesn't look like a git repository."
  exit 1
end

print_only = ARGV.include?("-n")
TagSynchronizer.remove_unused_tags(print_only)
Константин Гредескул
источник
3

Я знаю, что опоздал на вечеринку, но теперь есть быстрый ответ на это:

git fetch --prune --prune-tags # or just git fetch -p -P

Да, теперь есть возможность получить.

Если вы не хотите получать и просто обрезать:

git remote prune origin
джокер
источник
1

Как насчет этого - отбросить все локальные теги, а затем повторно получить? Учитывая, что ваш репо может содержать подмодули:

git submodule foreach --recursive  'git tag | xargs git tag -d'
(alternatively, "for i in `find .git  -type d -name '*tags*'`; do rm -f $i/*;  done")
git fetch -t
git submodule foreach --recursive git fetch -t
tetzu0
источник
1

TortoiseGit теперь может сравнивать теги.

Левый журнал находится на удаленном, правый на локальном.

введите описание изображения здесь

Использование функции сравнения тегов в диалоге синхронизации:

введите описание изображения здесь

Также смотрите TortoiseGit выпуск 2973

Юэ Лин Хо
источник
1

Ответ тот же, что и у @Richard W, но для Windows (PowerShell)

git tag | foreach-object -process { git tag -d $_ }
git fetch -t
Danon
источник
1

Я добавляю команду SourceTree в качестве пользовательского действия на моем MacOS.


Установка с Custom Actionsпомощью Sourcetree-> Preferences...->Custom Actions


Script to run должен быть git путь.

Я использую git fetch --prune --prune-tags originдля синхронизации тегов от удаленного к локальному.

введите описание изображения здесь введите описание изображения здесь

AechoLiu
источник
0

В новой версии git (как v2.26.2)

-P, --prune-tags Перед извлечением удалите все локальные теги, которые больше не существуют на удаленном компьютере, если --prune включен. Эту опцию следует использовать более осторожно, в отличие от --prune она удалит все созданные локальные ссылки (локальные теги). Эта опция является сокращением для предоставления явного тега refspec вместе с --prune, см. Обсуждение этого в его документации.

Так что вам нужно будет запустить:

git fetch august --prune --prune-tags
Ela Dute
источник