Usuń lokalne tagi git, które nie są już w zdalnym repozytorium


468

Używamy tagów w git jako część naszego procesu wdrażania. Od czasu do czasu chcemy wyczyścić te tagi, usuwając je z naszego zdalnego repozytorium.

To jest całkiem proste. Jeden użytkownik usuwa tag lokalny i tag zdalny w jednym zestawie poleceń. Mamy mały skrypt powłoki, który łączy oba kroki.

Drugi (trzeci, czwarty, ...) użytkownik ma teraz tagi lokalne, które nie są już widoczne na pilocie.

Szukam polecenia podobnego do tego, git remote prune originktóre usuwa lokalnie śledzące gałęzie, dla których gałąź zdalna została usunięta.

Alternatywnie, można użyć prostej komendy do wyświetlenia zdalnych tagów w celu porównania z lokalnymi tagami zwróconymi przez git tag -l.


2
Zaproponowałem nową funkcję w git do obsługi przycinania starych
Adam Monsen

1
Uwaga: z Git 2.17 (Q2 2018), proste git config fetch.pruneTags truesprawią, że będziesz git fetchrobić, co chcesz! Zobacz moją odpowiedź na to drugie pytanie .
VonC

2
Przesłanie komentarza z jednej z poniższych odpowiedzi: Przynajmniej w git 2.18.0 można również użyć tej składni: git fetch --prune --prune-tags origin
zutnop

Odpowiedzi:


71

Dobre pytanie. :) Nie mam pełnej odpowiedzi ...

To powiedziawszy, możesz uzyskać listę zdalnych tagów za pośrednictwem git ls-remote. Aby wyświetlić listę tagów w repozytorium, do którego się odwołujesz origin, uruchom:

git ls-remote --tags origin

Zwraca listę skrótów i przyjaznych nazw znaczników, takich jak:

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

Z pewnością możesz stworzyć skrypt bash, aby porównać tagi generowane przez tę listę z tagami, które masz lokalnie. Spójrz na git show-ref --tags, która generuje nazwy znaczników w takiej samej formie jak git ls-remote).


Nawiasem mówiąc, git show-refma opcję, która robi coś przeciwnego do tego, co chcesz. Następujące polecenie wyświetli wszystkie tagi w zdalnej gałęzi, których nie masz lokalnie:

git ls-remote --tags origin | git show-ref --tags --exclude-existing

Dzięki Mike. Rzucę własny skrypt bash, używając każdej listy do porównania.
kEND

11
Odpowiedź Richarda W. robi to znacznie bardziej elegancko, na wypadek, gdybyś nie był zainteresowany skomplikowanym scenariuszem.
Kyle Heironimus

1
Uwaga boczna na temat tagów nieobecnych lokalnie może zostać rozszerzona, aby sprawdzić więcej pilotów:git remote | xargs -L 1 git ls-remote --tags | git show-ref --tags --exclude-existing
Palec

Zobacz następną odpowiedź na prostsze rozwiązanie
sfletche

git obsługuje --prune-tags. Nie wiadomo, która wersja została wprowadzona. git-scm.com/docs/git-fetch#git-fetch---prune-tags
John Kloian

1052

To świetne pytanie, zastanawiałem się nad tym samym.

Nie chciałem pisać skryptu, więc szukałem innego rozwiązania. Kluczem jest odkrycie, że możesz usunąć tag lokalnie, a następnie użyć git fetch, aby „odzyskać” ze zdalnego serwera. Jeśli tag nie istnieje na pilocie, pozostanie usunięty.

Dlatego musisz wpisać dwie linie w kolejności:

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

Te:

  1. Usuń wszystkie tagi z lokalnego repozytorium. FWIW, xargs umieszcza dane wyjściowe każdego znacznika przez „tag -l” w wierszu poleceń dla „tag -d”. Bez tego git niczego nie usunie, ponieważ nie czyta stdin (głupie git).

  2. Pobierz wszystkie aktywne tagi ze zdalnego repozytorium.

Działa to nawet w systemie Windows.


57
To musi być moja ulubiona odpowiedź git na StackOverflow. Łączy wiedzę, prostotę i podstęp, a wszystko wyjaśnia. Świetnie
tymtam

25
jak zaznaczono w osobnej odpowiedzi, usuwa WSZYSTKIE lokalne tagi, a te, które nie znajdują się w zdalnym repo, oczywiście nie zostaną ponownie utworzone
drugi

13
FWIW powinno to być całkowicie niepotrzebne. Powinno być git tag prune originpolecenie.
void.pointer

9
To może nie działać dla wszystkich. Powinieneś zrobić git fetch - tagi, aby być po bezpiecznej stronie.
Adam Kurkiewicz

5
Musiałem iść, git tag -l | %{git tag -d $_}aby to działało w PowerShell. Nie jestem pewien nikogo innego.
Alain,

244

Z Git v1.7.8 do v1.8.5.6 możesz użyć tego:

git fetch <remote> --prune --tags

Aktualizacja

Nie działa to na nowszych wersjach git (począwszy od wersji 1.9.0 ) z powodu zatwierdzenia e66ef7ae6f31f2 . Jednak tak naprawdę nie chcę tego usuwać, ponieważ działało to u niektórych osób.

Jak sugeruje „Chad Juliano”, we wszystkich wersjach Gita od wersji 1.7.8 można użyć następującego polecenia:

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

Konieczne może być dołączenie części tagów do cudzysłowów (na przykład w systemie Windows), aby uniknąć rozwijania symboli zastępczych:

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

2
Odnoszę się do dokumentacji dołączonej do Git dla Windows 1.9.4-Preview20140611 (i podejrzewam, że wszystkie poprzednie wersje). Dostęp do wspomnianej dokumentacji uzyskuję za pomocą polecenia „git fetch --help” [quote] Tagi nie podlegają przycinaniu, jeśli są pobierane tylko z powodu domyślnego automatycznego śledzenia tagów lub opcji --tags. [/ Quote]
Félix Cantournet

2
git fetch --prune <remote> +refs/tags/*:refs/tags/*nie działał w ZSH, ale działa w BASH
Alex

3
@Alex To tylko dlatego, że zsh rozwija się, *ale jeśli zacytujesz pojedynczy cytat, powinno być dobrze.
NSF,

3
@ v01pe - dostępny jest teraz skrót git - tagi-przycinania od wersji 2.17.0 git opisanej w dokumentacji w sekcji PRUNING : git-scm.com/docs/git-fetch/2.17.0 Z dokumentu: The - Opcja -prune-tags jest odpowiednikiem zadeklarowania refs / tags / *: refs / tags / * w refspecs zdalnego. Odpowiedniki: git fetch origin --prune --prune-tagsOR git fetch origin --prune 'refs/tags/*:refs/tags/*'OR git fetch <url of origin> --prune --prune-tagsORgit fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
mkisaacs

3
git fetch origin --prune --prune-tagsprzycinaj zarówno zdalne gałęzie śledzenia, jak i tagi. sprawdzone w wersji git 2.18.
Number945

158

Jeśli chcesz tylko tych tagów, które istnieją na pilocie, po prostu usuń wszystkie tagi lokalne:

$ git tag -d $(git tag)

A następnie pobierz wszystkie zdalne tagi:

$ git fetch --tags

1
bezbłędnie, miałem problem z Xargsem, w którym nie znaleziono żadnych tagów
Marcio Toshio

3
@ocroquette, nie jestem pewien, jak to jest ładniejsze niż xargs. Jeśli masz więcej tagów ARG_MAXlub podobne ograniczenia, to nie zadziała. Mało prawdopodobne, ale możliwe, i dlatego xargsjest świetne.
Paul Draper,

2
„miły” to subiektywna rzecz, każdy wyrazi swoją opinię. O ARG_MAX to prawda. Jednak w systemach, których używam, ARG_MAX jest znacznie wyższy niż liczba znaczników, które mam w dowolnym repozytorium, więc nie przeszkadza mi to ograniczenie, podobnie jak nie przeszkadza mi to, gdy piszę „ls * .jpg” .
ocroquette

2
najczystsze rozwiązanie
mitsest

2
git config --global alias.prune-tags '!git tag -d $(git tag) && git fetch --tags'Obowiązkowe polecenie aliasu. Cieszyć się. :-)
Karl Wilbur

87

Wygląda na to, że najnowsze wersje Gita (jestem na git v2.20) pozwalają po prostu powiedzieć

git fetch --prune --prune-tags

Dużo czystsze!

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

Możesz także skonfigurować git, aby zawsze przycinał tagi podczas pobierania:

git config fetch.pruneTags true

Jeśli chcesz przycinać tagi tylko podczas pobierania z określonego pilota, możesz użyć tej remote.<remote>.pruneTagsopcji. Na przykład, aby zawsze przycinać tagi podczas pobierania z miejsca pochodzenia, ale nie innych pilotów,

git config remote.origin.pruneTags true


Świetny! Spotkałem błąd git push z „git-shell zmarł na sygnał 13”. Brak efektu nawet przy zwiększonym buforze http.postbuffer. Po włączeniu GIT_TRACE_PACKET i GIT_TRACE zobaczyłem wypychanie do niepoprawnych odnośników / tagów, więc użycie „--prune-tags” rozwiązuje to. Wielkie dzięki!
Ivellios

78

Wszystkie wersje Gita od wersji 1.7.8 rozumieją git fetchz refspec, natomiast od wersji 1.1.0 --tagsopcja zastępuje --pruneopcję. Aby uzyskać rozwiązanie ogólnego zastosowania, spróbuj:

$ git --version
git version 2.1.3

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

Więcej informacji o tym, jak zmieniło się zachowanie „--tags” z „--prune” w Git v1.9.0, patrz: https://github.com/git/git/commit/e66ef7ae6f31f246dead62f574cc2acb75fd001c


7
To powinna być najlepsza odpowiedź. To pojedyncze polecenie git, bez bash, bez potoków i bez xargs.
G. Sylvie Davies,

1
Zastąpiono originz upstreami git skorygowane moim lokalnym tagów na podstawie Upstream; następnie git push origin :<deleted-tag-name>zaktualizowałem mój widelec GitHub, usuwając usunięty tag.
leanne

3
Przynajmniej w przypadku gita 2.18.0 można również użyć tej składni:git fetch --prune --prune-tags origin
Martin

3
Począwszy od git 2.17.0 - włączono opcję --prune-tags i opisano ją szczegółowo w sekcji PRUNING odpowiednimi komendami w następującym dokumencie: git-scm.com/docs/git-fetch/2.17.0 git fetch origin --prune --prune-tags LUB git fetch origin --prune 'refs/tags/*:refs/tags/*' LUB git fetch <url of origin> --prune --prune-tags LUBgit fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
mkisaacs

8

Git natywnie obsługuje czyszczenie lokalnych tagów:

git fetch --tags --prune

To polecenie pobiera najnowsze tagi i usuwa wszystkie usunięte tagi.


Wygląda na to, że powinno to być „--prune” zamiast „--prune-tags”, w przeciwnym razie to było to, czego potrzebowałem, dzięki.
AnyDev,

Mam problem z drzewem źródłowym, który nie
wypchnął niektórych odnośników


4

Pokaż różnicę między tagami lokalnymi i zdalnymi:

diff <(git tag | sort) <( git ls-remote --tags origin | cut -f2 | grep -v '\^' | sed 's#refs/tags/##' | sort)
  • git tag podaje listę lokalnych tagów
  • git ls-remote --tags daje listę pełnych ścieżek do zdalnych tagów
  • cut -f2 | grep -v '\^' | sed 's#refs/tags/##' analizuje tylko nazwę znacznika z listy zdalnych ścieżek znaczników
  • Na koniec sortujemy każdą z dwóch list i różnicujemy je

Wiersze zaczynające się od „<” to tagi lokalne, których już nie ma w repozytorium zdalnym. Jeśli jest ich niewiele, możesz je usuwać ręcznie jeden po drugim, jeśli jest ich wiele, wykonuj więcej grepowania i orurowania, aby je zautomatyzować.


2
Proszę rozważyć dodanie wyjaśnienia do kodu. To zdecydowanie poprawi jakość twojej odpowiedzi.
honk

Kompletne polecenie, aby usunąć wszystkie zdalne tagi nieobecne lokalnie, to: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

Jeśli musisz zrobić taki diff i wyświetlać hash zatwierdzenia w tym samym czasie: diff <(git show-ref --tags | grep -v '{}' | awk '{print $1 " " $2}') <(git ls-remote --tags origin | grep -v '{}' | awk '{print $1 " " $2}')
piroux

To porównanie było dokładnie tym, czego szukałem, dziękuję. Jedyne, o czym się mylę, to to, że wypisuje również kilka wierszy, które nie zaczynają się od strzałki <, ale liczba po której następuje przecinek, a następnie coś, co wygląda jak pierwsze trzy znaki skrótu zatwierdzenia (?), np 7,8d4...
Kay

3

Właśnie dodałem polecenie git sync-local-tags do pivotal_git_scripts Gem fork na GitHub:

https://github.com/kigster/git_scripts

Zainstaluj klejnot, a następnie uruchom „git sync-local-tags” w swoim repozytorium, aby usunąć tagi lokalne, które nie istnieją na pilocie.

Możesz też po prostu zainstalować poniższy skrypt i nazwać go „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

Wiem, że jestem spóźniony na przyjęcie, ale teraz jest na to szybka odpowiedź:

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

Tak, teraz można pobrać.

Jeśli nie chcesz pobierać i po prostu przyciąć:

git remote prune origin

1

Co powiesz na to - upuść wszystkie lokalne tagi, a następnie ponownie pobierz? Biorąc pod uwagę, że Twoje repozytorium może zawierać submoduły:

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 może teraz porównywać tagi.

Lewy dziennik jest zdalny, prawy lokalny.

wprowadź opis zdjęcia tutaj

Korzystanie z funkcji Porównaj tagi w oknie dialogowym Synchronizacja:

wprowadź opis zdjęcia tutaj

Zobacz także TortoiseGit numer 2973


1

Ta sama odpowiedź, co @Richard W, ale dla Windows (PowerShell)

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

1

Dodaję to polecenie SourceTreejako akcję niestandardową na moim MacOS.


Ustawianie Custom Actionswedług Sourcetree-> Preferences...->Custom Actions


Script to runmusi być gitścieżką.

Używam git fetch --prune --prune-tags origindo synchronizacji tagów ze zdalnego do lokalnego.

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj


0

W nowej wersji git (jak v2.26.2)

-P, --prune-tags Przed pobraniem usuń wszystkie lokalne tagi, które nie istnieją już na pilocie, jeśli --prune jest włączony. Z tej opcji należy korzystać ostrożniej, w przeciwieństwie do opcji - przycinanie spowoduje usunięcie wszystkich utworzonych lokalnych odwołań (lokalnych znaczników). Ta opcja jest skrótem od podania jawnego tagu refspec wraz z --prune, zobacz dyskusję na ten temat w jego dokumentacji.

Więc musisz uruchomić:

git fetch august --prune --prune-tags
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.