2009-10-06 21 views
8

Ho un commit in un ramo remoto + locale e voglio buttarlo fuori dalla cronologia e metterne alcuni in un proprio ramo.git: modo migliore per git revert senza commit revertato aggiuntivo

In sostanza, in questo momento ho:

  D---E---F---G master 

E voglio:

   E---G topic 
      /
      D master 

che dovrebbero essere sia in mio locale e nel (c'è solo uno, chiamato origine) repository remoto .

Qual è il modo più pulito per farlo?

Inoltre, ci sono anche altre persone che hanno clonato quel repository e che hanno controllato il ramo principale. Se facessi un tale cambiamento nel repository remoto, farebbe in modo che "git pull" lavori anche per loro nello stesso stato?

risposta

7

Se hai pubblicato, hai ragione che non vuoi riscrivere la cronologia di master. Quello che vuoi è pubblicare un commit to master che lo riporta allo stato in cui era allo D conservando la sua cronologia corrente in modo che altri utenti possano unire o rebase facilmente il loro lavoro.

Se state pensando ad un certo punto in futuro di fondere topic in master allora quello che probabilmente anche vuole fare è fare una nuova base comune tra master e topic, in modo che quando si fa successivamente unire topic, si don' t perdere i commit che sono stati ripristinati in master. Il modo più semplice per farlo è eseguire un commit "redo" in cima al commit "undo" che reimposta lo stato master allo stato originale e basando il nuovo ramo topic su quello.

# checkout master branch (currently at G) 
git checkout master 

# Reset the index to how we want master to look like 
git reset D 

# Move the branch pointer back to where it should be, leaving the index 
# looking like D 
git reset --soft [email protected]{1} 

# Make a commit (D') for the head of the master branch 
git commit -m "Temporarily revert E, F and G" 

# Create the new topic branch based on master. 
# We're going to make it on top of master and the 'undo' 
# commit to ensure that subsequent merges of master->topic 
# or topic->master don't merge in the undo. 
git checkout -b topic 

# Revert the undo commit, making a redo commit (G'). 
git revert HEAD 

In alternativa si potrebbe avere fatto commette E 'F' e G 'rifare ogni parte separatamente, ma come E, F e G sono già nella vostra storia pubblicata è probabilmente più comprensibile se si fa riferimento alla' annullare 'commit e dire che il commit è stato annullato. Questo è ciò che lo fa git revert.

In sostanza quello che sai è questo.

D -- E -- F -- G -- D'  <-- master 
        \ 
         \ 
         G' <-- topic 

Le cose importanti sono di non aver riscritto la storia e la tema si basa su master in modo unioni non si applicano accidentalmente qualsiasi 'annullare' commette. Ora puoi tranquillamente spingere sia master e topic nel tuo repository remoto.

+0

Grazie, questo è esattamente quello che ho fatto ora (anche quello che ho suggerito in alcuni dei commenti a Jefromi). La storia è un po 'brutta ora a causa di questo annullamento commit, ma non posso farci niente. – Albert

+0

2 settimane git utente qui. Non sarebbe più semplice dapprima diramare G a G ', e quindi 'git revert' G a D, risultando in D'? In questo modo non devi annullare/ripetere nulla, il che mi sembra molto più sicuro. (Ho già perso incessantemente delle modifiche a causa di problemi con 'git reset'.) – bart

+0

@bart, dai un'occhiata a' git reflog'. Cose che sono state fatte e successivamente rovinate dovrebbero sempre essere recuperabili attraverso il reflog, almeno per alcuni giorni. Tuttavia, è possibile che inavvertitamente perdano le modifiche che non è stato possibile commettere. – dubiousjim

4

È possibile riscrivere la cronologia se lo si desidera, ma è una cattiva idea se qualcun altro ha copie della cronologia. In questo caso, probabilmente utilizzerai il rebase interattivo: git rebase -i master topic. Questo ti darà una lista di commit da un master all'altro, con suggerimenti su come giocare con loro. Dovresti solo rimuovere la riga contenente il commit che vuoi rimuovere.

Detto questo, devo sottolineare che è irresponsabile farlo se qualcun altro ha questa storia. Dovresti forzare-spingerlo al tuo repository centrale, e tutti gli altri dovrebbero risolvere i loro repository in modo che corrispondano, possono essere relativamente semplici o complessi a seconda delle circostanze.

C'è una bella sezione chiamata "recupero da rebase upstream" nel git-rebase man page discutendo su come affrontare questo, se davvero si decide di.

Edit:

Per la storia semplice, uno scenario comune sarebbe, dopo aver costretto una spinta non fastforward al repo centrale (push -f), altri sviluppatori:

  • indietro il loro vecchio maestro: git branch -m master master_old
  • ottenere aggiornamenti e ricreare maestro di origine: git remote update origin; git branch master origin/master
  • rebase tutti i rami argomento su nuovi padrone: git rebase --onto master master_old topic

Se hanno il lavoro in loro ramo padrone, che non è di origine ancora, dovrete ottenere più elaborato, rebasing questo lavoro e tutte le filiali argomento sulla nuova posizione del maestro ... questo dovrebbe darvi un'idea del perché sia ​​così orribile riscrivere la storia che hanno gli altri. In realtà, una volta che qualcosa è passato nel repository pubblico, dovresti considerarlo una storia registrata dura e veloce, non un work in progress.

+0

Ok, grazie mille per quelle informazioni. Quindi questa non è davvero una soluzione, allora perché non voglio fare una tale spinta forzata. Quale sarebbe la soluzione più pulita per ottenere un risultato simile? Quello che penserei ora è di ripristinare E, F, G nel master, quindi creare un nuovo ramo da quello stato e selezionare i caratteri E e G. È questo ciò che si farebbe naturalmente? – Albert

+0

Non c'è * nessun modo * per rimuovere un commit dalla cronologia senza causare questo tipo di problemi. Qualunque cosa tu faccia, finirai per puntare il tuo riferimento principale su un commit che non ha la posizione precedente del master nella sua storia. – Cascabel

+0

Questo è in gran parte affrontato nella mia risposta alla tua nuova versione di questa domanda, ma per rispondere al tuo commento: non importa come entri in uno stato in cui hai riscritto la storia del master. Lo avresti ancora riscritto. Se fai tutte queste cose * localmente *, va bene. Questo è chiamato cleanup. Se li fai per lavori pubblicati, stai facendo confusione, creando un repo pubblico di cui nessuno può fidarsi, ecc. – Cascabel

-3

trovo git stash è piuttosto utile

Basta riporre via e mai guardare di nuovo.

+2

Non è possibile memorizzare le modifiche che hai già commesso. –

+0

usa 'git reset HEAD ^' per salvare l'impegno senza perdere le modifiche. – twig