Rimuovere i tag git locali che non si trovano più nel repository remoto


468

Usiamo i tag in git come parte del nostro processo di distribuzione. Di tanto in tanto, vogliamo ripulire questi tag rimuovendoli dal nostro repository remoto.

Questo è piuttosto semplice. Un utente elimina il tag locale e il tag remoto in un set di comandi. Abbiamo un piccolo script di shell che combina entrambi i passaggi.

Il secondo utente (3 °, 4 °, ...) ora ha tag locali che non si riflettono più sul telecomando.

Sto cercando un comando simile a quello git remote prune originche pulisce localmente i rami di tracciamento per i quali il ramo remoto è stato eliminato.

In alternativa, è possibile utilizzare un semplice comando per elencare i tag remoti per confrontare i tag locali restituiti tramite git tag -l.


2
Ho proposto una nuova funzionalità in git per supportare i tag stantio della potatura: thread.gmane.org/gmane.comp.version-control.git/168833
Adam Monsen

1
Nota: con Git 2.17 (Q2 2018), un semplice git config fetch.pruneTags trueti farà git fetchfare quello che vuoi! Vedi la mia risposta a questa altra domanda .
VonC

2
Ripubblicare un commento da una delle risposte seguenti: almeno con git 2.18.0 si può anche usare questa sintassi: git fetch --prune --prune-tags origin
zutnop

Risposte:


71

Buona domanda. :) Non ho una risposta completa ...

Detto questo, puoi ottenere un elenco di tag remoti tramite git ls-remote. Per elencare i tag nel repository a cui fa riferimento origin, avresti eseguito:

git ls-remote --tags origin

Ciò restituisce un elenco di hash e nomi di tag descrittivi, come:

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

Potresti certamente mettere insieme uno script bash per confrontare i tag generati da questo elenco con i tag che hai localmente. Dai un'occhiata a git show-ref --tags, che genera i nomi dei tag nella stessa forma di git ls-remote).


A parte questo, git show-refha un'opzione che fa il contrario di quello che vorresti. Il comando seguente elenca tutti i tag sul ramo remoto che non sono disponibili localmente:

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

Grazie Mike. Farò scorrere il mio script bash usando ciascuna lista per il confronto.
kEND

11
La risposta di Richard W lo fa in modo molto più elegante, nel caso in cui non ti interessi una sceneggiatura complicata.
Kyle Heironimus,

1
La nota a margine sui tag non presenti localmente può essere espansa per controllare più telecomandi:git remote | xargs -L 1 git ls-remote --tags | git show-ref --tags --exclude-existing
Palec,

Vedi la prossima risposta per una soluzione più semplice
sfletche,

git supporta --prune-tags. Incerta su quale versione è stata introdotta. git-scm.com/docs/git-fetch#git-fetch---prune-tags
John Kloian

1054

Questa è un'ottima domanda, mi chiedevo la stessa cosa.

Non volevo scrivere una sceneggiatura, quindi ho cercato una soluzione diversa. La chiave sta scoprendo che è possibile eliminare un tag localmente, quindi utilizzare git fetch per "ripristinarlo" dal server remoto. Se il tag non esiste sul telecomando, rimarrà eliminato.

Pertanto è necessario digitare due righe nell'ordine:

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

Queste:

  1. Elimina tutti i tag dal repository locale. FWIW, xargs posiziona ciascun tag di output di "tag -l" sulla riga di comando per "tag -d". Senza questo, git non cancellerà nulla perché non legge stdin (sciocco git).

  2. Recupera tutti i tag attivi dal repository remoto.

Questo funziona anche su Windows.


57
Questa deve essere la mia risposta git preferita su StackOverflow. Unisce conoscenza, semplicità e inganno e spiega tutto. Ottimo
tymtam il

25
come indicato in una risposta separata, questo elimina TUTTI i tag locali e quelli che non si trovano nel repository remoto ovviamente non verranno ricreati
secondo

13
FWIW questo dovrebbe essere completamente inutile. Dovrebbe esserci un git tag prune origincomando.
void.pointer

9
Questo potrebbe non funzionare per tutti. Dovresti fare git fetch --tags per stare sul sicuro.
Adam Kurkiewicz,

5
Ho dovuto andare git tag -l | %{git tag -d $_}per farlo funzionare in PowerShell. Non sono sicuro di qualcun altro.
Alain,

244

Da Git v1.7.8 a v1.8.5.6, è possibile utilizzare questo:

git fetch <remote> --prune --tags

Aggiornare

Questo non funziona con le versioni più recenti di git (a partire dalla v1.9.0) a causa del commit e66ef7ae6f31f2 . Non voglio davvero eliminarlo dal momento che ha funzionato per alcune persone.

Come suggerito da "Chad Juliano", con tutte le versioni di Git dalla v1.7.8, puoi usare il seguente comando:

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

Potrebbe essere necessario racchiudere la parte dei tag tra virgolette (ad esempio su Windows) per evitare l'espansione dei caratteri jolly:

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

2
Mi riferisco alla documentazione fornita con Git per Windows 1.9.4-preview20140611 (e sospetto che tutte le versioni precedenti). Accedo a detta documentazione con "git fetch --help" [quote] I tag non sono soggetti a potatura se vengono recuperati solo a causa del tag predefinito auto-seguito o a causa di un'opzione --tags. [/ Quote]
Félix Cantournet

2
git fetch --prune <remote> +refs/tags/*:refs/tags/*non ha funzionato in ZSH ma funziona in BASH
Alex

3
@Alex Questo è solo perché zsh si espande *ma se citi una sola frase , dovrebbe andare bene.
NSF

3
@ v01pe - ora esiste un collegamento git --prune-tags disponibile da git 2.17.0 descritto nella documentazione nella sezione POTENZA : git-scm.com/docs/git-fetch/2.17.0 Dal documento: Il - L'opzione -prune-tags equivale ad avere refs / tags / *: refs / tags / * dichiarati nelle refspecs del telecomando. Equivalenti: 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-tagspotare sia i rami che i tag di monitoraggio remoto. controllato nella versione git 2.18.
Numero 945

158

Se desideri solo quei tag che esistono sul telecomando, elimina semplicemente tutti i tag locali:

$ git tag -d $(git tag)

E quindi recupera tutti i tag remoti:

$ git fetch --tags

1
impeccabile, stavo avendo problemi con quello xargs che non trova alcuni tag
Marcio Toshio,

3
@ocroquette, non sono sicuro di quanto sia più bello di xargs. Se hai più tag ARG_MAXo limitazioni simili, non funzionerà. Improbabile, ma possibile, ed è per questo che xargsè fantastico.
Paul Draper,

2
"bello" è una cosa soggettiva, ognuno farà la propria opinione. A proposito di ARG_MAX, è vero. Tuttavia, sui sistemi che utilizzo, ARG_MAX è molto più alto del numero di tag che ho in qualsiasi repository, quindi non mi dispiace la limitazione, così come non mi dispiace quando scrivo "ls * .jpg" .
ocroquette,

2
soluzione più pulita
mitsest

2
git config --global alias.prune-tags '!git tag -d $(git tag) && git fetch --tags'Comando alias obbligatorio. Godere. :-)
Karl Wilbur,

87

Sembra che le versioni recenti di Git (sono su git v2.20) permettano semplicemente di dire

git fetch --prune --prune-tags

Molto più pulito!

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

Puoi anche configurare git per eliminare sempre i tag durante il recupero:

git config fetch.pruneTags true

Se si desidera eliminare i tag solo durante il recupero da un telecomando specifico, è possibile utilizzare l' remote.<remote>.pruneTagsopzione Ad esempio, per eliminare sempre i tag durante il recupero dall'origine ma non da altri telecomandi,

git config remote.origin.pruneTags true


Eccellente! Ho incontrato il fallimento di git push con "git-shell morto del segnale 13". Nessun effetto anche con il http.postbuffer aumentato. Dopo aver abilitato GIT_TRACE_PACKET e GIT_TRACE, ho visto passare a ref / tag non validi, quindi usando "--prune-tags" risolverlo. Molte grazie!
Ivellios

78

Tutte le versioni di Git dalla v1.7.8 comprendono git fetchuna refspec, mentre dalla v1.9.0 l' --tagsopzione sovrascrive l' --pruneopzione. Per una soluzione di uso generale, prova questo:

$ git --version
git version 2.1.3

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

Per ulteriori informazioni su come il "--tags" con il comportamento "--prune" è cambiato in Git v1.9.0, vedere: https://github.com/git/git/commit/e66ef7ae6f31f246dead62f574cc2acb75fd001c


7
Questa dovrebbe essere la risposta migliore. È un singolo comando git, senza bash, senza pipe e senza xargs.
G. Sylvie Davies,

1
Sostituito origincon upstreame git corretto i miei tag locali in base all'upstream; successivamente git push origin :<deleted-tag-name>aggiornato il mio fork GitHub, rimuovendo il tag eliminato.
leanne,

3
Almeno con git 2.18.0 si può anche usare questa sintassi:git fetch --prune --prune-tags origin
Martin

3
A partire da git 2.17.0 - l'opzione --prune-tags è stata inclusa e descritta in dettaglio nella sezione PRUNING con comandi equivalenti nel seguente documento: git-scm.com/docs/git-fetch/2.17.0git fetch origin --prune --prune-tags OR git fetch origin --prune 'refs/tags/*:refs/tags/*' OR git fetch <url of origin> --prune --prune-tags ORgit fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
mkisaacs,

8

Git supporta nativamente la pulizia dei tag locali:

git fetch --tags --prune

Questo comando estrae i tag più recenti e rimuove tutti i tag eliminati.


Sembra che dovrebbe essere "--prune" invece di "--prune-tags", altrimenti è quello di cui avevo bisogno, grazie.
AnyDev,

Ho riscontrato un problema nell'albero dei sorgenti non è riuscito a spingere alcuni ref a ...: Funziona per me :) Grazie
mille


4

Mostra la differenza tra tag locali e tag remoti:

diff <(git tag | sort) <( git ls-remote --tags origin | cut -f2 | grep -v '\^' | sed 's#refs/tags/##' | sort)
  • git tag fornisce l'elenco dei tag locali
  • git ls-remote --tags fornisce l'elenco di percorsi completi per i tag remoti
  • cut -f2 | grep -v '\^' | sed 's#refs/tags/##' analizza solo il nome del tag dall'elenco dei percorsi dei tag remoti
  • Infine ordiniamo ciascuna delle due liste e le diff

Le righe che iniziano con "<" sono i tag locali che non si trovano più nel repository remoto. Se sono pochi, puoi rimuoverli manualmente uno per uno, se sono molti, fai più connessioni e tubazioni per automatizzarlo.


2
Ti consigliamo di aggiungere alcune spiegazioni al tuo codice. Ciò migliorerebbe definitivamente la qualità della tua risposta.
suonare il clacson il

Il comando completo per eliminare tutti i tag remoti non presenti localmente sarebbe quindi: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,

Se hai bisogno di fare una tale diff e visualizzare contemporaneamente l'hash di commit: diff <(git show-ref --tags | grep -v '{}' | awk '{print $1 " " $2}') <(git ls-remote --tags origin | grep -v '{}' | awk '{print $1 " " $2}')
piroux

Questo confronto era esattamente quello che stavo cercando, grazie. L'unica cosa di cui sono confuso è che genera anche un paio di righe che non iniziano con una freccia <, ma un numero seguito da una virgola e quindi quelli che sembrano i primi tre caratteri di un hash di commit (?), es. 7,8d4...
Kay,

3

Ho appena aggiunto un comando git sync-local-tags a pivotal_git_scripts Gem fork su GitHub:

https://github.com/kigster/git_scripts

Installa la gemma, quindi esegui "git sync-local-tags" nel tuo repository per eliminare i tag locali che non esistono sul telecomando.

In alternativa puoi semplicemente installare questo script qui sotto e chiamarlo "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

So di essere in ritardo alla festa, ma ora c'è una risposta rapida a questo:

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

Sì, ora è un'opzione da recuperare.

Se non vuoi recuperare e potare semplicemente:

git remote prune origin

1

Che ne dici di questo: rilasciare tutti i tag locali e quindi recuperare nuovamente? Considerando che il tuo repository potrebbe contenere sottomoduli:

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 può ora confrontare i tag.

Il registro sinistro è sul telecomando, il diritto è sul locale.

inserisci qui la descrizione dell'immagine

Utilizzando la funzione Confronta tag della finestra di dialogo Sincronizza:

inserisci qui la descrizione dell'immagine

Vedi anche TortoiseGit numero 2973


1

La stessa risposta di @Richard W ma per Windows (PowerShell)

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

1

Aggiungo il comando SourceTreecome Azione personalizzata sul mio MacOS.


Impostato Custom Actionsda Sourcetree-> Preferences...->Custom Actions


Script to rundeve essere il gitpercorso.

Uso git fetch --prune --prune-tags originper sincronizzare i tag da remoto a locale.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


0

Nella nuova versione di git (come v2.26.2)

-P, --prune-tags Prima di recuperare, rimuovere eventuali tag locali che non esistono più sul telecomando se --prune è abilitato. Questa opzione dovrebbe essere usata con più attenzione, diversamente da --prune rimuoverà qualsiasi riferimento locale (tag locale) che è stato creato. Questa opzione è una scorciatoia per fornire il tag esplicito refspec insieme a --prune, vedere la discussione al riguardo nella sua documentazione.

Quindi avresti bisogno di correre:

git fetch august --prune --prune-tags
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.