Błąd „tag już istnieje w zdalnym” po ponownym utworzeniu tagu git


142

Po wykonaniu poniższych czynności pojawia się następujący błąd:

To git@provider.com:username/repo-name.git
 ! [rejected]        dev -> dev (already exists)
error: failed to push some refs to 'git@provider.com:username/repo-name.git'
hint: Updates were rejected because the tag already exists in the remote.
  1. Utworzono repozytorium
  2. Sklonowano repozytorium na komputerze lokalnym.
  3. Zmodyfikował plik README, zatwierdził zmiany i przekazał zatwierdzenie.
  4. Utworzony tag dev:git tag dev
  5. Przesłane tagi: git push --tags
  6. Zmodyfikował plik README, zatwierdził zmiany i przekazał zatwierdzenie.
  7. Usunięto tag dev, utworzyłem go ponownie i przekazałem tagi:

    git tag -d dev
    git tag dev
    git push --tags
    

Dlaczego to się dzieje?

Jestem na Macu. Moi znajomi, którzy używają Linuksa (Ubuntu), nie mają tego problemu. Wiem, że mogę git push --tags -fwymusić aktualizację tagu, ale jest to niebezpieczne (np. Przepisanie popełnionego przez pomyłkę zatwierdzenia tylko w tagu, a nie w gałęzi).


1
Zatwierdzenia nie są wykonywane „w tagach” ani „w gałęziach” (chociaż wydaje się, że to drugie). W rzeczywistości nazwy tagów i gałęzi po prostu wskazują na (jedno, pojedyncze) zatwierdzenie. Zobacz odpowiedź poniżej.
torek

8
to działało dla mnie git pull --tagswtedygit push origin --tags
zobaczyłem

Odpowiedzi:


175

Edytuj, 24 listopada 2016 r .: ta odpowiedź jest najwyraźniej popularna, więc dodaję tutaj notatkę. Jeśli zastąpić tag na centralnym serwerze, kto ma stary Tag-dowolny klon tego repozytorium centralnego serwera, który ma już tag-mogła zachować swój stary znacznik . Więc chociaż mówi ci, jak to zrobić, upewnij się, że chcesz to zrobić. Będziesz musiał poprosić wszystkich, którzy mają już „zły” tag, aby usunął swój „zły tag” i zastąpił go nowym „właściwym tagiem”.

Testowanie w Git 2.10 / 2.11 pokazuje, że zachowanie starego tagu jest domyślnym zachowaniem dla działających klientów git fetch, a aktualizacja jest domyślnym zachowaniem dla działających klientówgit fetch --tags .

(Oryginalna odpowiedź jest następująca.)


Kiedy prosisz o wypchnięcie tagów, git push --tagswysyła (wraz z wszelkimi zatwierdzeniami i innymi potrzebnymi obiektami oraz innymi aktualizacjami referencyjnymi z ustawień wypychania) do pilota żądanie aktualizacji formularza . (Cóż, wysyła dowolną liczbę: po jednym dla każdego tagu.)new-sha1 refs/tags/name

Żądanie aktualizacji jest modyfikowane przez pilota w celu dodania old-sha1(lub ponownie jednego dla każdego znacznika), a następnie dostarczane do zaczepów przed odbiorem i / lub aktualizacją (niezależnie od tego, które z zaczepów istnieją na pilocie). Te haki mogą decydować, czy zezwolić, czy odrzucić tworzenie / usuwanie / aktualizację znacznika.

old-sha1Wartość jest all-Zera „null” SHA-1, jeśli znacznik jest tworzony. To new-sha1jest zerowy SHA-1, jeśli tag jest usuwany. W przeciwnym razie obie wartości SHA-1 są rzeczywistymi, prawidłowymi wartościami.

Nawet bez haków istnieje rodzaj „wbudowanego haka”, który jest również uruchamiany: pilot odmówi przeniesienia taga, chyba że użyjesz flagi „wymuś” (chociaż „wbudowany zaczep” jest zawsze OK z obydwoma „dodaj” i „usuń”). Komunikat o odrzuceniu, który widzisz, pochodzi z tego wbudowanego hooka. (Nawiasem mówiąc, ten sam wbudowany punkt zaczepienia odrzuca również aktualizacje gałęzi, które nie są szybkie do przodu.) 1

Ale - oto jeden z kluczy do zrozumienia, co się dzieje - git pushkrok nie ma pojęcia, czy pilot ma teraz ten znacznik, a jeśli tak, jaką ma wartość SHA-1. Mówi tylko „oto moja pełna lista tagów, wraz z ich wartościami SHA-1”. Pilot porównuje wartości i jeśli są dodatki i / lub zmiany, uruchamia na nich przechwyty. (W przypadku tagów, które są takie same, w ogóle nic nie robi. W przypadku tagów, których nie masz, to one również nie robią!)

Jeśli usuniesz tag lokalnie, to pushnaciśnięcie po prostu nie przenosi tagu. Pilot zakłada, że ​​nie należy wprowadzać żadnych zmian.

Jeśli usuniesz tag lokalnie, a następnie utwórz go wskazując nowe miejsce, wtedy pushtwoje naciśnięcie przenosi tag, a pilot postrzega to jako zmianę tagu i odrzuca zmianę, chyba że jest to wymuszenie.

Masz więc dwie możliwości:

  • zrobić pchnięcie siłowe lub
  • usuń znacznik na pilocie.

To ostatnie jest możliwe za pomocą git push2, mimo że lokalne usunięcie taga i pushnie ma żadnego efektu. Zakładając, że nazwa pilota to origin, a tag, który chcesz usunąć, to dev:

git push origin :refs/tags/dev

To prosi pilota o usunięcie tagu. Obecność lub brak tagu devw lokalnym repozytorium nie ma znaczenia; ten rodzaj push, jako refspec, jest wypychaniem czystego usuwania.:remoteref

Pilot może, ale nie musi, pozwolić na usunięcie tagu (w zależności od dodanych dodatkowych haków). Jeśli pozwoli to na usunięcie, tag zniknie, a po git push --tagschwili, gdy masz lokalny devznacznik wskazujący na jakiś obiekt repozytorium tagów zatwierdzonych lub z adnotacjami, wyślij nowy devtag. Na pilocie devbędzie teraz nowo utworzony tag, więc pilot prawdopodobnie pozwoli na wypchnięcie (znowu zależy to od dodanych dodatkowych haków).

Pchnięcie siły jest prostsze. Jeśli chcesz mieć pewność, nie do niczego aktualizacji innych niż tagu, po prostu powiedz git pushnaciskać tylko jeden refspec:

git push --force origin refs/tags/dev:refs/tags/dev

(uwaga: nie musisz tego robić, --tagsjeśli jawnie wypychasz tylko jeden tag ref-spec).


1 Oczywiście powodem tego wbudowanego punktu zaczepienia jest pomoc w egzekwowaniu zachowania, którego oczekują inni użytkownicy tego samego zdalnego repozytorium: gałęzie nie są przewijane, a tagi nie poruszają się. Jeśli naciskasz na siłę, powinieneś poinformować innych użytkowników, że to robisz, aby mogli to poprawić. Zwróć uwagę, że „tagi w ogóle się nie poruszają” zostało na nowo wymuszone przez Git 1.8.2; poprzednie wersje umożliwiały znacznikowi „przesuwanie do przodu” na wykresie zmian, podobnie jak w przypadku nazw gałęzi. Zobacz informacje o wydaniu git 1.8.2 .

2 To trywialne, jeśli możesz zalogować się na pilocie. Po prostu przejdź do repozytorium Git i uruchom git tag -d dev. Zwróć uwagę, że w każdym przypadku - usuwając znacznik na pilocie lub używając go git pushdo usunięcia - istnieje okres, w którym każdy, kto uzyska dostęp do pilota, stwierdzi, że devbrakuje tagu. (Będą one nadal mają swój stary tag, jeśli już masz, a może nawet przesunąć swój stary tag z powrotem w górę, zanim będzie można wcisnąć nowy).


Czy dzieje się to tylko w nowych wersjach gita? Mam 1.7.9.5i nie mam tego problemu ...
Ionică Bizău

2
Probalby - mam mgliste wspomnienie git push --tagsautomatycznej zmiany tagu w starszych wersjach gita, bez --force. Przetestowałem to jednak pod 1.8.4 i potrzebujesz --force, lub dwuetapowej techniki aktualizacji.
torek

2
@John ツ: aktualizacja: zgodnie z informacjami o wydaniu to nowe zachowanie od 1.8.2 . Zmienię to również w przypisie 1.
torek

Nie wiem, jak znalazłem się w tej sytuacji, ale ten tag został usunięty i odtworzony w mgnieniu oka.
RiggsFolly

4
Jak wykonać pchnięcie siłowe, jeśli nie jesteś Jedi?
Fonix

54

W Mac SourceTree tylko odznacz pole wyboru Push all tags :

wprowadź opis obrazu tutaj


3
hahahah, taki prosty człowieku, czytałem zaakceptowaną odpowiedź i pomyślałem, że tak
sfałszuję

10
Ma to na celu przezwyciężenie tego bez faktycznego rozwiązywania problemu. Nie rozwiązuje to problemu braku dopasowania nazwy tagu na zdalnym i lokalnym.
amalBit

1
działa również w wersji dla Windows! dzięki za uratowanie nas przed przeczytaniem długo akceptowanej odpowiedzi, która pomija użytkowników drzewa źródłowego, którzy nie dbają o to, co się dzieje w wierszu poleceń :)
schlingel

19

Jest to dość proste, jeśli używasz SourceTree .

wprowadź opis obrazu tutaj Zasadniczo wystarczy usunąć i ponownie dodać kolidujący tag:

  1. Przejdź do zakładki Repozytorium -> Tag -> Usuń tag
  2. Wybierz nazwę tagu powodującego konflikt
  3. Zaznacz Usuń tag ze wszystkich pilotów
  4. Naciśnij Usuń
  5. Utwórz nowy tag o tej samej nazwie do właściwego zatwierdzenia
  6. Pamiętaj, aby zaznaczyć opcję Przekaż wszystkie tagi podczas przesyłania zmian do pilota

16

Jeśli chcesz ZAKTUALIZOWAĆ tag, powiedzmy to1.0.0

  1. git checkout 1.0.0
  2. wprowadź zmiany
  3. git ci -am 'modify some content'
  4. git tag -f 1.0.0
  5. usuń zdalny tag na github: git push origin --delete 1.0.0
  6. git push origin 1.0.0

GOTOWE


12

Wygląda na to, że spóźniłem się z tą kwestią i / lub została już udzielona, ​​ale co można zrobić to: (w moim przypadku miałem tylko jeden tag lokalnie, więc ... Usunąłem stary tag i ponownie oznaczyłem go tagiem :

git tag -d v1.0
git tag -a v1.0 -m "My commit message"

Następnie:

git push --tags -f

To zaktualizuje wszystko tagi na pilocie.

Może być niebezpieczne! Używaj na własne ryzyko.


1
Zrobiło to dla mnie! Tagi były tylko lokalnie, a nie zdalnie :)
pgarciacamou

4

Powód, dla którego jesteś odrzucany jest utrata synchronizacji tagu z wersją zdalną. To samo dotyczy gałęzi.

zsynchronizuj z tagiem z pilota przez, git pull --rebase <repo_url> +refs/tags/<TAG>a po synchronizacji musisz zarządzać konfliktami . Jeśli masz zainstalowany diftool (np. Meld), git mergetool meldużyj go do synchronizacji pilota i zachowania zmian.

Powodem, dla którego ciągniesz z flagą --rebase, jest to, że chcesz umieścić swoją pracę na zdalnym, aby uniknąć innych konfliktów.

Nie rozumiem też, dlaczego miałbyś usunąć devtag i ponownie go utworzyć? Tagi służą do określania wersji oprogramowania lub punktów kontrolnych. Przykłady znaczników git v0.1dev, v0.0.1alpha, v2.3-cr(Cr - uwalnianie kandydat), itd ..


Innym sposobem rozwiązania tego problemu jest problem a git reflogi przejście do momentu, w którym wcisnąłeś devtag na pilocie. Skopiuj identyfikator zatwierdzenia, a dzięki git reset --mixed <commmit_id_from_reflog>temu będziesz mieć pewność, że tag był zsynchronizowany z pilotem w momencie jego naciśnięcia i nie pojawią się żadne konflikty.


Na przykład, jeśli chcesz oznaczyć zatwierdzenie, które jest obecnie w produkcji. Czy musiałbyś wtedy usunąć stary znacznik produkcyjny z określonego zatwierdzenia oraz utworzyć i wypchnąć nowy znacznik dla zatwierdzenia po nowej wersji produkcyjnej?
Ville Miekk-oja


0

Kilka dobrych odpowiedzi. Szczególnie ten autorstwa @torek . Pomyślałem, że dodam to obejście z małym wyjaśnieniem dla tych, którzy się spieszą.

Podsumowując, dzieje się tak, że gdy przenosisz znacznik lokalnie, zmienia on znacznik z wartości zatwierdzenia innej niż Null na inną wartość. Jednakże, ponieważ git (jako domyślne zachowanie) nie pozwala na zmianę zdalnych tagów innych niż Null, nie możesz wypchnąć zmiany.

Rozwiązaniem jest usunięcie tagu (i zaznaczenie opcji usuń wszystkie piloty). Następnie utwórz ten sam tag i wciśnij.

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.