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


469

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

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

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

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

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


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/*"

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

1
безупречно, у меня были проблемы с XARGS, он не находит некоторые теги
Марсио Тошио

3
@ocroquette, я не уверен, как это лучше, чем xargs. Если у вас больше тегов ARG_MAXили подобных ограничений, это не сработает. Маловероятно, но возможно, и поэтому xargsэто здорово.
Пол Дрейпер

2
«приятно» - субъективная вещь, каждый будет высказывать свое мнение. Насчет ARG_MAX это правда. Тем не менее, в системах, которые я использую, ARG_MAX намного больше, чем количество тегов, которые у меня есть в любом хранилище, поэтому я не против ограничения, так же как я не против этого, когда пишу "ls * .jpg" ,
Окрокет


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/##' анализирует только имя тега из списка путей удаленных тегов
  • Наконец, мы сортируем каждый из двух списков и различаем их

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


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

1

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

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

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

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

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

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


1

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

git tag | foreach-object -process { git tag -d $_ }
git fetch -t

1

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


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


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

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

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


0

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

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

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

git fetch august --prune --prune-tags
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.