Jak przesłać zmodyfikowane zatwierdzenie do zdalnego repozytorium Git?


662

Kiedy trochę popracowałem z moim kodem źródłowym, zrobiłem swoje zwykłe zatwierdzenie, a następnie wypchnąłem do zdalnego repozytorium. Ale potem zauważyłem, że zapomniałem zorganizować import w kodzie źródłowym. Więc wykonuję polecenie zmiany, aby zastąpić poprzednie zatwierdzenie:

> git commit --amend

Niestety zatwierdzenia nie można wypchnąć z powrotem do repozytorium. Jest odrzucany w następujący sposób:

> git push origin
To //my.remote.repo.com/stuff.git/
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '//my.remote.repo.com/stuff.git/'

Co powinienem zrobić? (Mogę uzyskać dostęp do zdalnego repozytorium).


Co jeśli mój --amend miał tylko zmienić komunikat zatwierdzenia? Jakikolwiek sposób edycji ostatniej wiadomości zatwierdzenia, jeśli została już przekazana do zdalnego? Zrobiłem to na Github i otrzymałem tę samą wiadomość o nie przewijaniu do przodu. Następnie zastosowałem rozwiązanie poniżej, ale scalanie właśnie dodało więcej komunikatów zatwierdzania na górze ..

7
@faB: Myślę, że to jest FAQ. Komunikat zatwierdzenia jest mieszany wraz z zatwierdzeniem, więc jego zmiana zmienia revid (skrót). Jeśli nie jest jasne: nie, nie możesz. IIRC może przechowywać informacje poza pasmem w notatkach (dzięki czemu można dodawać adnotacje do istniejących zatwierdzeń bez ich zmieniania). Aby oznaczyć konkretne zatwierdzenia, użyj tagów
patrz

1
Wkrótce (git1.8.5, IV kwartał 2013 r.) Będziesz mógł robić to git push -forceostrożniej .
VonC

3
Oto styl kowbojski. Nie ucz się więcej ani nie szukaj sposobów cofnięcia poprzedniej poprawki git. Po prostu dodaj kod zastępczy, dodaj komentarz, Oczyść trochę kodu lub po prostu dodaj myślnik myślnik myślnik ... Teraz dokonaj prawdziwego zatwierdzenia i wypchnij go na odległość. Gotowy !
nehem

@ user58777 Jeśli -amend miał tylko zmienić komunikat zatwierdzenia i od tego czasu nie dokonałeś żadnych dodatkowych zatwierdzeń lokalnych, możesz zresetować swój oddział lokalny do zdalnego zatwierdzenia, które wypchnąłeś przed zmianą komunikatu zatwierdzenia.
Scott Ahten

Odpowiedzi:


504

Właściwie kiedyś naciskałem --forcei .gitrepozytorium i zostałem skarcony przez Linusa DUŻY CZAS . Zasadniczo spowoduje to wiele problemów dla innych ludzi. Prosta odpowiedź brzmi „nie rób tego”.

W każdym razie widzę, że inni podali taki przepis, więc nie będę ich tutaj powtarzał. Ale oto wskazówka, jak wyjść z sytuacji po wypchnięciu zmienionego zatwierdzenia za pomocą --force (lub + master).

  1. Użyj, git reflogaby znaleźć stare zatwierdzenie, które zmieniłeś (zadzwoń old, a my wywołamy nowe zatwierdzenie, które utworzyłeś, zmieniając new).
  2. Utwórz połączenie między oldi new, rejestrując drzewo new, lubić git checkout new && git merge -s ours old.
  3. Połącz to ze swoim mistrzem git merge master
  4. Zaktualizuj swojego mistrza o wynik za pomocą git push . HEAD:master
  5. Wypchnij wynik.

Wtedy ludzie, którzy niefortunne wystarczy mieć opiera swoją pracę na zatwierdzenie ty zatarte poprzez zmianę i zmuszając push ujrzy otrzymany seryjnej będzie widać, że faworyzuje newsię old. Ich późniejsze fuzje nie spowodują konfliktów między nimi oldi newwynikają z twoich poprawek, więc nie będą musiały cierpieć.


17
Doskonale zdaję sobie sprawę z tego, co się dzieje, gdy zmuszasz się do wprowadzenia poprawionego zatwierdzenia (niszcząc historię). Na szczęście byłem jedynym deweloperem w projekcie, ponieważ zdalne repozytorium znajdowało się na dysku sieciowym, więc nie było to nic wielkiego. Nigdy nie myślałem o połączeniu zatwierdzenia zmian, więc będę to głosować.
Spoike

61
W naszej firmie naciskamy dość regularnie ... na gałęzie funkcji opracowane przez osoby prywatne.
Ondra Žižka

2
Karcenie Linusa było spowodowane tym, że wymazałeś historię opcją siły, a nie dlatego, że nie powinieneś tego robić. Rozwiązanie GabrielleV działa dobrze, ponieważ nie zmienia historii.
user411279,

2
Proszę, ponieważ autor (gitster) tej odpowiedzi wydaje się już nie istnieć, czy ktoś mógłby pomóc wyjaśnić pozycję nr 1: znajdź stare zatwierdzenie. Jeśli nie masz kopii zapasowej, gdzie ją znajdziesz? Poprawiaj i zmuszaj do popchnięcia, czy nie zniszczyłbyś go? Może chodzi mu o to, aby zdobyć go od znajomego / współpracownika, który wciąż ma go na drzewie?
Dr Beco,

2
Dr Breco, za pomocą którego możesz git refloggo znaleźć
Simon Zyx,

269

Widzisz funkcję bezpieczeństwa Git. Git nie chce zaktualizować gałęzi zdalnej za pomocą gałęzi, ponieważ zatwierdzenie główne gałęzi nie jest bezpośrednim potomkiem bieżącego zatwierdzenia głównego gałęzi, do której użytkownik naciska.

Gdyby tak nie było, dwie osoby pchające do tego samego repozytorium mniej więcej w tym samym czasie nie wiedziałyby, że nadchodzi nowy zatwierdzenie w tym samym czasie i ktokolwiek nacisnąłby ostatni, straciłby pracę poprzedniego popychacza bez żadnego z zdają sobie z tego sprawę.

Jeśli wiesz, że jesteś jedyną osobą, która pcha i chcesz wypchnąć zmienione zatwierdzenie lub wypchnąć zatwierdzenie, które wycofuje gałąź, możesz „zmusić” Gita do zaktualizowania zdalnej gałęzi za pomocą -f przełącznika.

git push -f origin master

Nawet to może nie działać, ponieważ Git zezwala zdalnym repozytoriom na odrzucanie wypychania na drugim końcu za pomocą zmiennej konfiguracyjnej receive.denynonfastforwards. W takim przypadku przyczyna odrzucenia będzie wyglądać następująco (zwróć uwagę na część „odrzucenie zdalne”):

 ! [remote rejected] master -> master (non-fast forward)

Aby obejść ten problem, musisz zmienić konfigurację zdalnego repozytorium lub jako brudny hack możesz usunąć i ponownie utworzyć gałąź w ten sposób:

git push origin :master
git push origin master

Zasadniczo ostatni parametr, który git pushkorzysta z formatu <local_ref>:<remote_ref>, gdzie local_refjest nazwą gałęzi w lokalnym repozytorium i remote_refnazwą gałęzi w zdalnym repozytorium. Ta para poleceń używa dwóch skrótów. :masterma zerowy parametr local_ref, co oznacza wypchnięcie gałęzi null na stronę zdalną master, tj. usunięcie gałęzi zdalnej. Nazwa oddziału w żadnym :wypadku nie wypycha gałęzi lokalnej o podanej nazwie do oddziału zdalnego o tej samej nazwie. masterw tej sytuacji jest skrótem master:master.


2
to nie działało z githubem, to dało mi następujący komunikat: [zdalne odrzucenie] master (usunięcie bieżącego oddziału zabronione)
wedang

Nie chciałem wymuszać push (co wiedziałem, że rozwiąże problem), ale teraz chyba nie mam wyboru.
wedang

1
To jedyne rozwiązanie, które zadziałało w przypadku mojego repozytorium hostowanego przez assemblela.
Justin

1
usunięcie zdalnej gałęzi głównej zwolni miejsce w zdalnym repozytorium?
Mr_and_Mrs_D

1
@Mr_and_Mrs_D: Nie od razu, ale po upływie git gcczasu ponownego rejestrowania stare obiekty zostaną przycięte. Nikt, kto klonuje repozytorium, nie otrzyma żadnych obiektów, które nie są już dostępne, gdy tylko gałąź zostanie zaktualizowana.
CB Bailey,

211

Szybki rant: fakt, że nikt nie opublikował tutaj prostej odpowiedzi, świadczy o desperackiej wrogości wobec użytkownika przejawianej przez interfejs Git CLI.

W każdym razie „oczywistym” sposobem na zrobienie tego, zakładając, że nie próbowałeś wymusić nacisku, jest pociągnięcie w pierwszej kolejności. To pociąga za sobą zmianę, którą poprawiłeś (a więc już nie masz), abyś miał ją ponownie.

Po rozwiązaniu konfliktów możesz ponownie naciskać.

Więc:

git pull

Jeśli podczas ściągania wystąpią błędy, być może coś jest nie tak z konfiguracją twojego lokalnego repozytorium (miałem złe referencje w sekcji gałęzi .git / config).

I po

git push

Może dostaniesz dodatkowe zatwierdzenie z tematem opowiadającym o „Trywialnym scaleniu”.


2
Tak, napisałem o tym, patrz stackoverflow.com/questions/253055/… ;)
Spoike

10
To nie działa tak, jak się spodziewałem. Tworzy dwa nowe zatwierdzenia. Jest to replika starej, ale ze zmienionymi zmianami. I jedno połączenie zatwierdzenia z pustym plikiem różnicowym. Wciąż pozostawiam stare zatwierdzenie bez zmian, ujawniając potencjalnie wrażliwe dane, które starałem się zmienić. Wierzę git push -flub git resetjest to jedyna droga, aby tu dotrzeć.
thnee

40
Technicznie odpowiadając na problem, tak naprawdę nie rozwiązuje problemu. Jak powiedziałeś, wygeneruje dodatkowe zatwierdzenie, ale głównym powodem, dla którego ludzie zmieniają zatwierdzenie, jest unikanie tworzenia nowego. Gdyby więc plakat postępował zgodnie z instrukcjami, nie uzyskałby pożądanego rezultatu. Byłoby tak samo rozsądne, aby nie zmieniać zatwierdzenia w pierwszej kolejności.
Dan Jones

102

Krótka odpowiedź: Nie wypychaj poprawionych zmian do publicznego repozytorium.

Długa odpowiedź: kilka poleceń Gita, takich jak git commit --amendigit rebase , faktycznie przepisuję wykres historii. Jest to w porządku, o ile nie opublikowałeś swoich zmian, ale kiedy już to zrobisz, naprawdę nie powinieneś grzebać w historii, ponieważ jeśli ktoś już dostał twoje zmiany, to kiedy spróbują je ponownie pobrać, może się nie powieść . Zamiast zmieniać zatwierdzenie, powinieneś po prostu dokonać nowego zatwierdzenia ze zmianami.

Jednakże, jeśli naprawdę naprawdę chcesz wcisnąć zmienione zatwierdzenie, możesz to zrobić w następujący sposób:

$ git push origin +master:master

+Znak wiodący zmusi do wypchnięcia, nawet jeśli nie spowoduje zatwierdzenia „przewijania do przodu”. (Szybkie zatwierdzanie do przodu występuje, gdy zmiany, które przesyłasz, są bezpośrednimi potomkami zmian już zawartych w publicznym repozytorium).


5
Czym różni się to (lepsze lub gorsze) od git push -f? Dzięki!
Bentford

11
@ Bentford: Zasadniczo to samo git push -f.
mipadi

54

Oto bardzo prosty i czysty sposób na wprowadzenie zmian po dokonaniu commit --amend:

git reset --soft HEAD^
git stash
git push -f origin master
git stash pop
git commit -a
git push origin master

Który wykonuje następujące czynności:

  • Zresetuj głowę oddziału do zatwierdzenia rodzica.
  • Ukryj to ostatnie zatwierdzenie.
  • Force push to remote. Pilot nie ma teraz ostatniego zatwierdzenia.
  • Pop swoją skrytkę.
  • Zobowiązuj się czysto.
  • Naciśnij na pilocie.

Pamiętaj, aby zmienić „origin” i „master”, jeśli zastosujesz to do innej gałęzi lub pilota.


3
2 uwagi: - pamiętaj, aby zmienić nazwę oddziału, jeśli pracujesz nad innym - musiałem użyć git addprzed zatwierdzeniem, aby uwzględnić zmiany.
SylvainB,

1
W systemie Windows CMD, pierwsza komenda powinna być uciekł: git reset --soft "HEAD^". Reszta działa dobrze.
MrMister

2
„bardzo prosty i czysty sposób…” cit. Ta procedura obejmuje wymuszone wypychanie. W świetle wszystkich powyższych uwag w powyższych odpowiedziach nie jestem pewien, czy procedura ta jest rzeczywiście czysta.
Na13-c

24

Rozwiązałem go, odrzucając moje zmienione lokalne zatwierdzenie i dodając nowe zmiany na górze:

# Rewind to commit before conflicting
git reset --soft HEAD~1

# Pull the remote version
git pull

# Add the new commit on top
git add ...
git commit
git push

2
To jest najprostsza wersja!
mknaf

Dodanie kolejnego zatwierdzenia „zmiana” jest lepsze niż bałagan w historii przepisywania. Zgadzam się z @mknaf
sdkks

8

Miałem ten sam problem.

  • Przypadkowo zmieniono ostatnie zatwierdzenie, które zostało już wypchnięte
  • Dokonano wielu zmian lokalnie, popełniono około pięć razy
  • Próbowałem wypchnąć, dostałem błąd, spanikowałem, połączyłem zdalnie, dostałem wiele nie-moich plików, wypchnąłem, nie udało się itp.

Jako początkujący Git myślałem, że to kompletny FUBAR .

Rozwiązanie: W pewnym sensie @bara zasugerował + utworzenie lokalnego oddziału kopii zapasowych

# Rewind to commit just before the pushed-and-amended one.
# Replace <hash> with the needed hash.
# --soft means: leave all the changes there, so nothing is lost.
git reset --soft <hash>

# Create new branch, just for a backup, still having all changes in it.
# The branch was feature/1234, new one - feature/1234-gone-bad
git checkout -b feature/1234-gone-bad

# Commit all the changes (all the mess) not to lose it & not to carry around
git commit -a -m "feature/1234 backup"

# Switch back to the original branch
git checkout feature/1234

# Pull the from remote (named 'origin'), thus 'repairing' our main problem
git pull origin/feature/1234

# Now you have a clean-and-non-diverged branch and a backup of the local changes.
# Check the needed files from the backup branch
git checkout feature/1234-gone-bad -- the/path/to/file.php

Może nie jest to szybkie i czyste rozwiązanie i straciłem historię (1 zatwierdzenie zamiast 5), ale zaoszczędziłem dzień pracy.


6

Jeśli nie przekazałeś kodu do zdalnej gałęzi (GitHub / Bitbucket), możesz zmienić komunikat zatwierdzenia w wierszu poleceń, jak poniżej.

 git commit --amend -m "Your new message"

Jeśli pracujesz nad konkretnym oddziałem, wykonaj następujące czynności:

git commit --amend -m "BRANCH-NAME: new message"

Jeśli kod został już przesłany z niewłaściwą wiadomością, należy zachować ostrożność podczas zmiany wiadomości. tzn. po zmianie komunikatu zatwierdzenia i ponownym wypchnięciu go, masz problemy. Aby zapewnić płynność, wykonaj następujące czynności.

Przeczytaj całą odpowiedź, zanim to zrobisz

git commit --amend -m "BRANCH-NAME : your new message"

git push -f origin BRANCH-NAME                # Not a best practice. Read below why?

Ważna uwaga: użycie bezpośredniego wymuszania może spowodować problemy z kodem, które inni programiści pracują nad tym samym oddziałem. Aby uniknąć tych konfliktów, musisz wyciągnąć kod z gałęzi, zanim wymusisz wypchnięcie :

 git commit --amend -m "BRANCH-NAME : your new message"
 git pull origin BRANCH-NAME
 git push -f origin BRANCH-NAME

Jest to najlepsza praktyka przy zmianie komunikatu zatwierdzenia, jeśli został już wypchnięty.


1
Jeśli udało ci się wycofać zatwierdzenie w ostatnim przykładzie, dlaczego musisz wymusić push? Czy standardowe wypychanie nie wystarczyłoby? Dzięki
Thomas

Pytanie zadane przez Thomasa jest w rzeczywistości bardzo ważne. Sam nie musiałem zmuszać do popychania po pociągnięciu.
Na13-c

Nie nazywaj tego „najlepszą praktyką”, ponieważ istnieje sposób na obejście --force, zobacz przyjętą odpowiedź
Farid

5

Jeśli wiesz, że nikt nie wyciągnął twojego niezmienionego zatwierdzenia, skorzystaj z --force-with-leaseopcji git push.

W TortoiseGit możesz zrobić to samo w opcjach „Push ...” „Force: May discard” i zaznaczając „znane zmiany”.

Wymuś (może odrzucić znane zmiany) pozwala zdalnemu repozytorium zaakceptować bezpieczniejsze przesunięcie do przodu. Może to spowodować utratę zatwierdzeń przez zdalne repozytorium; używaj go ostrożnie. Może to zapobiec utracie nieznanych zmian przez inne osoby na pilocie. Sprawdza, czy gałąź serwera wskazuje to samo zatwierdzenie, co gałąź zdalnego śledzenia (znane zmiany). Jeśli tak, zostanie przeprowadzone wymuszenie. W przeciwnym razie zostanie odrzucony. Ponieważ git nie ma tagów zdalnego śledzenia, tagi nie mogą zostać zastąpione przy użyciu tej opcji.


4

Otrzymujesz ten błąd, ponieważ zdalny Git ma już te pliki zatwierdzeń. Musisz wymusić wypchnięcie gałęzi, aby to zadziałało:

git push -f origin branch_name

Upewnij się również, że ściągnąłeś kod zdalnie, ponieważ ktoś z twojego zespołu mógł pchnąć do tego samego oddziału.

git pull origin branch_name

Jest to jeden z przypadków, w których musimy wymusić wypchnięcie zatwierdzenia na odległość.


Dlaczego ta odpowiedź nie uwzględnia głównych uwag poruszonych w poprzednich odpowiedziach?
Na13-c

2

Oto bardzo prosty i czysty sposób na wprowadzenie zmian po dokonaniu już git add "your files"i git commit --amend:

git push origin master -f

lub:

git push origin master --force

Słyszę, że to źle i jestem tego pewien. Jest (dobry) powód domyślnego niepowodzenia gita (i wymaga --force), jestem pewien.
Rolf,

1

Musiałem rozwiązać ten problem podczas pobierania ze zdalnego repo i radzenia sobie z konfliktami scalania, które powstały, zatwierdzenia, a następnie wypchnięcia. Ale wydaje mi się, że istnieje lepszy sposób.


Nie całkiem. Problemem może być to, że nie zaktualizowałeś swojej lokalnej kopii ze zdalnego repozytorium. Git nie chce tego robić, ponieważ być może będziesz musiał poradzić sobie z połączeniami ręcznie. W mojej drugiej odpowiedzi mam polecenie (i wyjaśnienie), które wymusi push - ale uwaga, która może usunąć zmiany w pilocie.
mipadi

1

Po prostu robiłem to, co Git mi powiedział. Więc:

  • Nie można wypychać z powodu zmienionego zatwierdzenia.
  • Wykonuję pociągnięcie zgodnie z sugestią.
  • Scalanie kończy się niepowodzeniem. więc naprawiam to ręcznie.
  • Utwórz nowe zatwierdzenie (oznaczone jako „scal”) i wciśnij je.
  • Wydaje się, że działa!

Uwaga: Zmienione zatwierdzenie było najnowsze.


1
Oddałbym głos, gdybym miał więcej punktów reputacji, więc po prostu grzecznie zapytam tutaj, który z was cierpiał? Ten, który zmienił? Ten, który ciągnął i pracował nad oddziałem ze zmienionym zatwierdzeniem? Przed poprawką, czy po niej? Właśnie wyczyściłem każdą moją modyfikację, ponieważ źle cię zrozumiałem ... Na szczęście nie było wiele ...
Bartis Áron

1

Poniższe działało dla mnie przy zmianie autora i osoby zatwierdzającej zatwierdzenie.

git push -f origin master

Git był na tyle sprytny, by stwierdzić, że były to zatwierdzone identyczne delty, które różniły się tylko sekcją metainformacyjną.

Zarówno lokalni, jak i zdalni szefowie wskazywali na przedmiotowe zobowiązania.



0

Tutaj, jak naprawiłem edycję w poprzednim zatwierdzeniu:

  1. Jak dotąd zachowaj swoją pracę.
  2. Ukryj na razie zmiany, jeśli zostały wprowadzone: git stashteraz kopia robocza jest czysta w stanie ostatniego zatwierdzenia.
  3. Wprowadź zmiany i poprawki.
  4. Zatwierdź zmiany w trybie „popraw” :git commit --all --amend
  5. Twój edytor pojawi się z prośbą o komunikat dziennika (domyślnie stary komunikat dziennika). Zapisz i zamknij edytor, gdy będziesz z niego zadowolony.

    Nowe zmiany są dodawane do starego zatwierdzenia. Przekonaj się za pomocą git logigit diff HEAD^

  6. Ponownie zastosuj ukryte zmiany, jeśli zostały wprowadzone: git stash apply

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.