Come faccio a inviare il commit modificato al repository Git remoto?


663

Quando ho lavorato un po 'con il mio codice sorgente, ho fatto il mio solito impegno e poi ho spinto in un repository remoto. Ma poi ho notato che ho dimenticato di organizzare le mie importazioni nel codice sorgente. Quindi eseguo il comando di modifica per sostituire il commit precedente:

> git commit --amend

Sfortunatamente il commit non può essere rinviato al repository. Viene rifiutato in questo modo:

> 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/'

Cosa dovrei fare? (Posso accedere al repository remoto.)


E se il mio --amend fosse solo per cambiare il messaggio di commit? Un modo per modificare l'ultimo messaggio di commit da solo, se è stato già inviato al telecomando? L'ho fatto su Github e ho ricevuto lo stesso messaggio sull'avanzamento non veloce. Quindi ho applicato una soluzione di seguito, ma l'unione ha appena aggiunto altri messaggi di commit in cima ..

7
@faB: penso che sia una FAQ. Un messaggio di commit viene assegnata con il commit, quindi chaning si cambia il revid (hash). Se non è chiaro: no non puoi. IIRC può memorizzare informazioni fuori banda nelle note (in modo da poter annotare i commit esistenti senza modificarli). Per etichettare specifici commit, usa i tag
vedi il

1
Presto (git1.8.5, Q4 2013) sarai in grado di fare git push -forcepiù attentamente .
VonC,

3
Ecco lo stile da cowboy. Non imparare oltre o non cercare modi per annullare la modifica git precedente. Basta aggiungere un po 'di segnaposto, voglio dire, aggiungere un commento, ripulire un po' di codice o semplicemente aggiungere qualche trattino trattino trattino .... Ora fai un vero commit e spingilo sul telecomando. Fatto !
Nehem,

@ user58777 Se -amend doveva solo modificare il messaggio di commit e da allora non hai effettuato ulteriori commit locali, puoi ripristinare il tuo ramo locale sul commit remoto che hai inviato prima di modificare il messaggio di commit.
Scott Ahten,

Risposte:


504

In realtà una volta ho spinto --forcee .gitrepository e sono stato sgridato da Linus BIG TIME . In generale, ciò creerà molti problemi per altre persone. Una semplice risposta è "Non farlo".

Vedo che gli altri hanno dato la ricetta per farlo comunque, quindi non li ripeterò qui. Ma ecco un suggerimento per riprendersi dalla situazione dopo aver eliminato il commit modificato con --force (o + master).

  1. Usa git reflogper trovare il vecchio commit che hai modificato (chiamalo olde chiameremo il nuovo commit che hai creato modificando new).
  2. Crea una fusione tra olde new, registrando l'albero di new, come git checkout new && git merge -s ours old.
  3. Uniscilo al tuo padrone con git merge master
  4. Aggiorna il tuo master con il risultato con git push . HEAD:master
  5. Sposta il risultato.

Poi le persone che hanno avuto la sfortuna di avere basato il loro lavoro sulla commit si cancellato dalla modifica e costringendo una spinta vedrà l'unione risultante vedrà che sei a favore newsopra old. Le loro successive fusioni non vedranno i conflitti tra olde newrisultanti dalla tua modifica, quindi non devono soffrire.


17
Sono ben consapevole di ciò che accade quando si forza il push di un commit modificato (distruggendo la cronologia). Fortunatamente, ero l'unico sviluppatore del progetto con il repository remoto su un'unità di rete, quindi non era un grosso problema. Non ho mai pensato di unire un commit di modifica, quindi voterò questo.
Spoike,

62
Nella nostra azienda, spingiamo forzatamente abbastanza regolarmente ... sui rami delle funzionalità sviluppati dagli individui.
Ondra Žižka,

2
Il rimprovero di Linus era perché hai cancellato la storia con l'opzione force, non perché non dovresti farlo. La soluzione di GabrielleV funziona bene, perché non cambia la storia.
user411279,

2
Per favore, poiché l'autore (gitster) di questa risposta sembra non esistere più, qualcuno potrebbe aiutare a chiarire l'articolo numero 1: trovare il vecchio commit. Se non si dispone di un backup, dove lo troveresti? La modifica e la spinta forzata non l'avrebbero distrutta? Forse si sta riferendo per ottenerlo da un amico / collaboratore che lo ha ancora nell'albero?
Dr Beco,

2
Dr. Breco, puoi usarlo git reflogper trovarlo
Simon Zyx,

269

Stai vedendo una funzione di sicurezza Git. Git si rifiuta di aggiornare il ramo remoto con il proprio ramo, poiché il commit principale del proprio ramo non è un diretto discendente dell'attuale head commit del ramo verso cui si sta spingendo.

Se così non fosse, allora due persone che spingono nello stesso repository nello stesso momento non saprebbero che ci sarebbe stato un nuovo commit in arrivo allo stesso tempo e chiunque avesse spinto per ultimo avrebbe perso il lavoro del precedente spintore senza nessuno dei due realizzando questo.

Se sai di essere l'unica persona che spinge e desideri inviare un commit modificato o inviare un commit che riavvolge il ramo, puoi "forzare" Git ad aggiornare il ramo remoto usando l' -finterruttore.

git push -f origin master

Anche questo potrebbe non funzionare poiché Git consente ai repository remoti di rifiutare spinte non veloci dall'altra parte usando la variabile di configurazione receive.denynonfastforwards. In questo caso, il motivo del rifiuto sarà simile al seguente (notare la parte "rifiutato a distanza"):

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

Per ovviare a questo, è necessario modificare la configurazione del repository remoto o come hack sporco è possibile eliminare e ricreare il ramo in questo modo:

git push origin :master
git push origin master

In generale, l'ultimo parametro git pushutilizza il formato <local_ref>:<remote_ref>, dove local_refè il nome del ramo nel repository locale ed remote_refè il nome del ramo nel repository remoto. Questa coppia di comandi utilizza due shorthands. :masterha un local_ref nullo che significa che spinge un ramo null sul lato remoto master, cioè cancella il ramo remoto. Un nome di ramo senza :mezzi spinge il ramo locale con il nome dato al ramo remoto con lo stesso nome. masterin questa situazione è l'abbreviazione di master:master.


2
questo non ha funzionato con github, mi ha dato il seguente messaggio: [remoto rifiutato] master (cancellazione del ramo corrente proibita)
vedang

Non volevo forzare la spinta (che sapevo avrebbe risolto il problema), ma ora credo di non avere scelta.
vedang

1
Questa è l'unica soluzione che ha funzionato per il mio repository ospitato con assembla.
Justin

1
l'eliminazione del ramo master remoto libererà lo spazio nel repository remoto?
Mr_and_Mrs_D

1
@Mr_and_Mrs_D: non immediatamente, ma dopo una git gcvolta scaduti i reflog verranno eliminati i vecchi oggetti. Nessuno che clona il repository otterrà oggetti che non sono più raggiungibili non appena il ramo è stato aggiornato.
CB Bailey,

211

Rapidità: il fatto che nessuno abbia pubblicato qui la semplice risposta dimostra l'ostilità disperata dell'utente mostrata dalla CLI di Git.

Ad ogni modo, il modo "ovvio" per farlo, supponendo che tu non abbia provato a forzare la spinta, è quello di tirare per primo. Ciò estrae la modifica che hai modificato (e quindi non è più presente) in modo da averla nuovamente.

Dopo aver risolto eventuali conflitti, puoi spingere di nuovo.

Così:

git pull

Se ricevi errori in pull, forse qualcosa non va nella configurazione del tuo repository locale (ho avuto un riferimento sbagliato nella sezione del ramo .git / config).

E dopo

git push

Forse riceverai un ulteriore impegno con il soggetto che parla di una "unione triviale".


2
Sì, ho scritto su questo, vedere stackoverflow.com/questions/253055/... ;)
Spoike

10
Questo non funziona davvero come mi aspettavo. Crea due nuovi commit. Uno che è una replica di quello vecchio, ma con le modifiche modificate. E una fusione commette con un diff vuoto. Continuando a lasciare invariato il vecchio commit, rivelando i possibili dati sensibili che stavo cercando di modificare. Credo git push -fo git resetè l'unico modo per andare qui.
thnee

40
Mentre risponde tecnicamente al problema, non risolve davvero il problema. Come hai detto, genererà un commit aggiuntivo, ma il motivo principale per cui le persone modificano un commit è evitare di crearne uno nuovo. Quindi se il poster dovesse seguire le tue istruzioni, non otterrebbe il risultato desiderato. Avrebbe altrettanto senso non modificare il commit in primo luogo.
Dan Jones,

102

Risposta breve: non inviare commit modificati a un repository pubblico.

Risposta lunga: alcuni comandi Git, come git commit --amende git rebase, in realtà riscrivono il grafico cronologico. Questo va bene fino a quando non hai pubblicato le modifiche, ma una volta fatto, non dovresti davvero andare in giro con la cronologia, perché se qualcuno ha già le tue modifiche, quindi quando provano a tirare di nuovo, potrebbe fallire . Invece di modificare un commit, dovresti semplicemente effettuare un nuovo commit con le modifiche.

Tuttavia, se vuoi davvero inviare un commit modificato, puoi farlo in questo modo:

$ git push origin +master:master

Il +segno principale forzerà la spinta, anche se non si traduce in un commit "avanzamento rapido". (Un commit ad avanzamento rapido si verifica quando le modifiche che si stanno spingendo sono un diretto discendente delle modifiche già presenti nel repository pubblico.)


5
In che modo è diverso (migliore o peggiore) di git push -f? Grazie!
Bentford,

11
@bentford: è praticamente la stessa cosa di git push -f.
mipadi,

54

Ecco un modo molto semplice e pulito per inviare le modifiche dopo aver già effettuato una commit --amend:

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

Che fa quanto segue:

  • Ripristina la diramazione sul commit principale.
  • Stash questo ultimo commit.
  • Forza la spinta verso il telecomando. Il telecomando ora non ha l'ultimo commit.
  • Fai scoppiare la tua scorta.
  • Impegnati in modo pulito.
  • Premere sul telecomando.

Ricordarsi di cambiare "origine" e "master" se si applica questo a un ramo o telecomando diverso.


3
2 commenti: - assicurati di cambiare il nome del ramo se stai lavorando su un altro - ho dovuto usare git addprima del mio commit per includere le modifiche.
SylvainB,

1
In Windows CMD, il primo comando deve essere sfuggito: git reset --soft "HEAD^". Il resto funziona bene.
Mr Mister,

2
"un modo molto semplice e pulito .." cit. Questa procedura include la spinta forzata. Alla luce di tutte le critiche di cui sopra in Risposte, non sono sicuro che questa procedura sia effettivamente chiara.
Na13-c,

24

Ho risolto scartando il mio commit modificato locale e aggiungendo le nuove modifiche in cima:

# 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
Questa è la versione più semplice!
mknaf,

Aggiungere un altro commit "modifica" è meglio che fare casini con la cronologia della riscrittura. Sono d'accordo con @mknaf
sdkks il

8

Ho avuto lo stesso problema.

  • Modifica accidentale dell'ultimo commit che è stato già sottoposto a push
  • Ha apportato molte modifiche a livello locale, impegnate circa cinque volte
  • Ho provato a spingere, ho ricevuto un errore, sono andato nel panico, hanno unito il telecomando, ho ricevuto molti file non personali, ho spinto, fallito, ecc.

Come novizio Git, ho pensato che fosse FUBAR completo .

Soluzione: un po 'come suggerito da @bara + è stato creato un ramo di backup locale

# 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

Forse non è una soluzione rapida e pulita e ho perso la mia storia (1 commit invece di 5), ma mi ha salvato un giorno di lavoro.


6

Se non hai inviato il codice al tuo ramo remoto (GitHub / Bitbucket) puoi modificare il messaggio di commit sulla riga di comando come di seguito.

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

Se stai lavorando su un ramo specifico, fai come segue:

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

Se hai già inviato il codice con un messaggio sbagliato, devi fare attenzione quando cambi il messaggio. cioè dopo aver modificato il messaggio di commit e provare a spingerlo di nuovo, si verificano problemi. Per renderlo uniforme, attenersi alla seguente procedura.

Si prega di leggere l'intera risposta prima di farlo

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

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

Nota importante: quando si utilizza la forza forzata direttamente, si potrebbero riscontrare problemi di codice che altri sviluppatori stanno lavorando sullo stesso ramo. Quindi, per evitare quei conflitti, devi estrarre il codice dal tuo ramo prima di far spingere la forza :

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

Questa è la procedura consigliata quando si modifica il messaggio di commit, se è stato già inviato.


1
Se hai eseguito correttamente il commit del tuo ultimo esempio, perché devi forzare push? Una spinta standard non sarebbe sufficiente? Grazie
Thomas

La domanda posta da Thomas è in effetti molto valida. Non avevo bisogno di forzare la spinta dopo il tiro.
Na13-c,

Per favore, non chiamarla "la migliore pratica" perché c'è un modo per aggirare --force, vedi la risposta accettata
Farid

5

Se sai che nessuno ha eseguito il commit non modificato, utilizza l' --force-with-leaseopzione di git push.

In TortoiseGit, puoi fare la stessa cosa sotto le opzioni "Push ..." "Forza: può scartare" e controllare "modifiche conosciute".

Force (può scartare le modifiche note) consente al repository remoto di accettare una spinta più sicura non in avanti veloce. Ciò può causare la perdita di commit del repository remoto; usalo con cura. Ciò può impedire di perdere modifiche sconosciute da altre persone sul telecomando. Verifica se il ramo del server punta allo stesso commit del ramo di tracciamento remoto (modifiche note). Se sì, verrà eseguita una spinta forzata. Altrimenti verrà rifiutato. Poiché git non ha tag di tracciamento remoto, i tag non possono essere sovrascritti usando questa opzione.


4

Stai ricevendo questo errore perché il telecomando Git ha già questi file di commit. Devi forzare a spingere il ramo affinché questo funzioni:

git push -f origin branch_name

Assicurati anche di estrarre il codice dal telecomando poiché qualcun altro nella tua squadra potrebbe aver spinto nello stesso ramo.

git pull origin branch_name

Questo è uno dei casi in cui dobbiamo forzare il push del commit in remoto.


Perché questa risposta non tiene conto dei principali commenti sollevati nelle risposte precedenti?
Na13-c,

2

Ecco un modo molto semplice e pulito per inviare le modifiche dopo aver già effettuato un git add "your files"e git commit --amend:

git push origin master -f

o:

git push origin master --force

Ho sentito che è male, e sono sicuro che lo è. C'è una (buona) ragione per cui git fallisce di default (e richiede --force), ne sono sicuro.
Rolf,


1

Ho dovuto risolvere questo problema estraendo dal repository remoto e gestendo i conflitti di unione che sono sorti, impegnati e quindi inviati. Ma sento che esiste un modo migliore.


Non proprio. Il problema potrebbe essere che non hai aggiornato la tua copia locale dal repository remoto. Git non ci spingerà perché potresti dover gestire le fusioni manualmente. Nella mia altra risposta, ho un comando (e una spiegazione) che forzerà un push - ma attenzione che è possibile eliminare le modifiche nel telecomando.
mipadi,

1

Ho continuato a fare quello che Git mi ha detto di fare. Così:

  • Impossibile eseguire il push a causa del commit modificato.
  • Faccio un tiro come suggerito.
  • L'unione fallisce. quindi lo aggiusto manualmente.
  • Crea un nuovo commit (etichettato "unisci") e invialo.
  • Sembra funzionare!

Nota: il commit modificato è stato l'ultimo.


1
Sottovaluterei, se avessi più punti reputazione, quindi chiederò cortesemente qui quale dei tuoi malati sei stato tu? Quello che ha emendato? Chi ha tirato e lavorato su un ramo con commit modificato? Prima della modifica o dopo? Ho appena cancellato ogni mia modifica perché ti avevo frainteso ... Fortunatamente non c'era molto ...
Bartis Áron,

1

Quanto segue ha funzionato per me quando ho cambiato Autore e Committer di un commit.

git push -f origin master

Git è stato abbastanza intelligente da capire che si trattava di delta identici che differivano solo nella sezione delle meta informazioni.

Sia i capi locali che quelli remoti hanno indicato gli impegni in questione.


0

Ecco, come ho risolto una modifica in un precedente commit:

  1. Salva il tuo lavoro finora.
  2. Metti da parte le modifiche per ora, se apportate: git stashora la tua copia di lavoro è pulita allo stato del tuo ultimo commit.
  3. Apporta le modifiche e le correzioni.
  4. Conferma le modifiche in modalità "modifica" :git commit --all --amend
  5. Il tuo editor verrà a chiedere un messaggio di registro (per impostazione predefinita, il vecchio messaggio di registro). Salva ed esci dall'editor quando sei soddisfatto.

    Le nuove modifiche vengono aggiunte al vecchio commit. Guarda tu stesso con git logegit diff HEAD^

  6. Riapplica le modifiche nascoste, se apportate: git stash apply

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.