Видаліть локальні теги git, яких більше немає у віддаленому сховищі


468

Ми використовуємо теги в 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-теги походження
zutop

Відповіді:


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
Відповідь Річарда W робить це набагато елегантніше, якщо ви не зацікавлені у складному сценарії.
Kyle Heironimus

1
git remote | xargs -L 1 git ls-remote --tags | git show-ref --tags --exclude-existing
Бічну

Дивіться наступну відповідь на більш просте рішення
sfletche

git підтримує --prune-теги. Невідомо, яка версія була введена. git-scm.com/docs/git-fetch#git-fetch---prune-tags
Джон Клоян

1052

Це велике запитання, мені було цікаво те саме.

Я не хотів писати сценарій, тому шукав іншого рішення. Ключовим моментом є виявлення того, що ви можете видалити тег локально, а потім скористатися 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. Він поєднує в собі знання, простоту і хитрість і пояснює все. Великий
tymtam

25
як зазначається в окремій відповіді, це видаляє ВСІ локальні теги, і ті, які не знаходяться у віддаленому репо, очевидно, не будуть створені повторно
друге

13
FWIW це має бути абсолютно непотрібним. Повинна бути git tag prune originкоманда.
void.pointer

9
Це може працювати не для всіх. Ви повинні зробити git fetch - теги, щоб бути на безпечній стороні.
Адам Куркевич

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) через виконувати e66ef7ae6f31f2 . Я не хочу його видаляти, хоча він працював для деяких людей.

Як запропонував "Чад Джуліано", для всієї версії Git з версії 1.7 можна використовувати таку команду:

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" [цитата] Теги не підлягають обрізанню, якщо вони отримані лише через автоматичне наступне тег за замовчуванням або через опцію --tags. [/
Quote

2
git fetch --prune <remote> +refs/tags/*:refs/tags/*не працював у ZSH, однак він працює в БАШ
Олексій

3
@ Алекс Це лише тому, що zsh розширюється, *але якщо ви одразу цитуєте, це має бути добре.
NSF

3
@ v01pe - тепер є ярлик для git --prune-теги доступні з git 2.17.0, описаного в документації в розділі PRUNING : git-scm.com/docs/git-fetch/2.17.0 З документа: The - Параметр -prune-tags еквівалентний тому, що refs / tags / *: refs / tags / * оголошено в рефлексах віддаленого пристрою. Еквіваленти: 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.
Число945

158

Якщо ви хочете лише ті теги, які існують на пульті, просто видаліть усі локальні теги:

$ git tag -d $(git tag)

А потім отримайте всі віддалені теги:

$ git fetch --tags

1
бездоганно, у мене були проблеми з xargs, у якому не знайдено тегів
Marcio Toshio,

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

Чудовий. Я адаптував це для публікації на ДУ -> ¿Cómo puedo eliminar las etiquetas de Git que solo tengo en local? .
fedorqui 'ТАК перестаньте шкодити'

Відмінно! Я зустрів провал git push з "git-shell помер від сигналу 13". Немає ефекту навіть при збільшенні http.postbuffer. Після ввімкнення GIT_TRACE_PACKET та GIT_TRACE, я побачив, що підштовхують до недійсних реф / тегів. Дуже дякую!
Івелліос

78

Всі версії Git починаючи з версії v1.7.8 розуміються git fetchз респектом, тоді як з 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, ні дуд і xargs.
Г. Сільві Девіс

1
Замінено originз upstreamі мерзотником виправив мої локальні мітки на основі вгору по течії; наступного git push origin :<deleted-tag-name>разу оновив мою вилку GitHub, видаливши видалений тег.
leanne

3
Принаймні, з git 2.18.0 можна також використовувати цей синтаксис:git fetch --prune --prune-tags origin
Мартін

3
Починаючи з git 2.17.0 - параметр --prune-tags був включений та детально описаний у розділі PRUNING з еквівалентними командами у наступному документі: git-scm.com/docs/git-fetch/2.17.0git 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-теги", інакше це те, що мені було потрібно, дякую.
AnyDev

У мене виникає проблема у вихідному дереві, не вдалося підштовхнути деякі відгуки до ...: Це працює для мене :) Дякую
Абхішек Тапліял,


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
Будь ласка, подумайте, щоб додати до свого коду пояснення Це остаточно поліпшить якість вашої відповіді.
ганком

Повна команда для видалення всіх віддалених тегів, які не є локальними, буде: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/{}
Daniel Gehriger

Якщо вам потрібно зробити таку diff <(git show-ref --tags | grep -v '{}' | awk '{print $1 " " $2}') <(git ls-remote --tags origin | grep -v '{}' | awk '{print $1 " " $2}')
фіксування

Це порівняння було саме те, що я шукав, дякую. Єдине, що мене бентежить, це те, що він також виводить пару рядків, які не починаються зі стрілки <, а число, за яким йде кома, а потім те, що схоже на перші три символи хеша комітету (?), наприклад 7,8d4...
Кей

3

Щойно додана команда git sync-local-tags до pivotal_git_scripts Gem fork на 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-теги Перед завантаженням видаліть локальні теги, які більше не існують на пульті, якщо --prune увімкнено. Цю опцію слід використовувати більш обережно, на відміну від --prune вона видалить усі створені локальні посилання (локальні теги). Цей параметр є скороченням для надання явного перегляду тегів разом з --prune, див. Обговорення про це в його документації.

Тож вам потрібно запустити:

git fetch august --prune --prune-tags
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.