Git: come tornare a un commit specifico?


156

Vorrei fare riferimento a un commit specifico, non a un HEAD dell'altro ramo:

A --- B --- C          master
 \
  \-- D                topic

per

A --- B --- C          master
       \
        \-- D          topic

invece di

A --- B --- C          master
             \
              \-- D    topic

Come posso raggiungerlo?


4
Hai provato a farlo git checkout Bprima di correre git rebase?
Peter-Paul van Gemerden,

No, dovrebbe essere d'aiuto? Immagino che solo i riferimenti al rebasecomando siano importanti.
Ondra Žižka,

Risposte:


98

Puoi evitare di usare il parametro --onto creando un ramo temporaneo sul commit che ti piace e poi usa rebase nella sua forma semplice:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp

5
Mi piace di più questo approccio tipo RISC :) Ci proverò. Grazie.
Ondra Žižka,

10
Mi chiedo perché non funzioni per me, in uno scenario leggermente diverso . Voglio che il gruppo salti il pep8 e sia basato sul master . git rebase temp(quando nel gruppo ) si arrende con "I gruppi di filiali correnti sono aggiornati".
Alois Mahdal,

4
Questa soluzione non funzionerà per lo scenario in cui l'argomento è già stato ridisegnato in master, ma si desidera ridisegnarlo su un antenato da padroneggiare. In tal caso, è necessario utilizzare in git rebase --onto <target> <from> <to>modo da poter specificare il commit <from>.
mirzmaster,

Questa è di gran lunga l'opzione più semplice se si desidera rifarsi sullo stesso commit su cui si basa il ramo.
Ash,

1
Sembra che funzioni, ma GitLab dice qualcos'altro. Se avessi 10 commit dietro, 5 commit in anticipo, la mia aspettativa era di avere 8 commit dietro, 5 commit in avanti dopo aver ricevuto 2 commit. Ma invece aggiunge ulteriori impegni a quei 5.
ROMANIA_engineer

68

Puoi anche adottare un approccio diretto:

git checkout topic
git rebase <commitB>

6
Per me, questo non fa effettivamente ciò che è previsto. Per quanto ne so, cerca di rifare l '"ultimo antenato comune" di topice commitB.
Dan Lenski,

2
@DanLenski, non è così che funziona rebase. Citando i documenti , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. ho provato di nuovo ora e sembrava funzionare bene.
r0hitsharma,

1
Super facile e funziona! Ora ho: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek,

Non ha funzionato per me. Prima di questo, GitLab ha detto "n si impegna in anticipo". E ora, dice "m si impegna in anticipo" dove m > n.
ROMANIA_engineer

48

Utilizzare l'opzione "onto":

git rebase --onto master^ D^ D

2
De D^sarebbe l'hash dell'ultimo e l'ultimo penultimo commit di "topic"?
Ondra Žižka,

39
La sintassi è come git rebase --onto <new-parent> <old-parent>. Vedi Impostazione del puntatore genitore git su un genitore diverso . Nel tuo caso, <nuovo-parente> è B, e <nuovo-parente> è A.
jsz

7
Uso sempre i 3 argomenti: desitnation, start e end of commit per rebase.
Adam Dymitruk,

14
Questo ha funzionato per me:git rebase --onto <commit-ID> master

4
Il commento di @ jsz è corretto, contrariamente a quello di Simon South, è il contrario: git rebase --onto master <commit-ID-of-old-parent>e per OP git rebase --onto B A.
gaborous

19

Il commento di jsz sopra mi ha risparmiato un sacco di dolore, quindi ecco un destinatario passo-passo basato su di esso che ho usato per riformulare / spostare qualsiasi commit in cima a qualsiasi altro commit:

  1. Trova un precedente punto di diramazione del ramo da riordinare (spostato) - chiamalo vecchio genitore. Nell'esempio sopra è A
  2. Trova commit in cima al quale vuoi spostare il ramo - chiamalo nuovo genitore. Nell'esempio è B
  3. Devi essere sul tuo ramo (quello che sposti):
  4. Applica il tuo rebase: git rebase --onto <new parent> <old parent>

Nell'esempio sopra è semplice come:

   git checkout topic
   git rebase --onto B A

6
Questa dovrebbe essere la risposta corretta. Tranne che uso git rebase --onto B master, vedere la mia risposta per una spiegazione più approfondita.
Zack Morris,

Non ha funzionato bene per me. Ho scelto 2 commit consecutivi (l'ultimo dal master che era nel ramo corrente e il primo dal master che non era nel ramo corrente). Ho iniziato con 100 dietro - 10 davanti e invece di 99 dietro - 10 davanti, ora ho 105 dietro - 13 davanti.
ROMANIA_engineer

Mi dispiace sapere che non ha funzionato. Sembra che i tuoi rami siano divergenti abbastanza - suggerirei di schiacciarli prima di provare a riformulare i rami con queste molte differenze.
Nestor Milyaev,

11

Soluzione per argomento

Il comando corretto per rispondere alla domanda pubblicata potrebbe essere uno dei seguenti (supponendo che il ramo topicsia già stato estratto):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Se topicnon è stato estratto, è sufficiente aggiungere topical comando (tranne l'ultimo) in questo modo:

git rebase --onto B master topic

In alternativa, controlla prima la filiale con:

git checkout topic

Sostituisci qualsiasi stringa di commit a un commit target

La forma base del comando di cui abbiamo bisogno, paralizzata dalla documentazione, è:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>è facoltativo e tutto ciò che fa è estrarre il ramo specificato prima di eseguire il resto del comando. Se hai già verificato il ramo che desideri modificare, non ti serve. Nota che devi aver specificato <Upstream>per specificare <Branch>o git penserà che stai specificando <Upstream>.

<Target>è il commit a cui assoceremo la nostra serie di commit. Quando si fornisce un nome di ramo, si specifica semplicemente il commit principale di quel ramo. <Target>può essere qualsiasi commit che non sarà contenuto nella stringa di commit che viene spostata. Per esempio:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Per spostare l'intero ramo di caratteristica, non è possibile selezionare X, Y, Zo featurecome <Target>in quanto tali sono tutti i commit all'interno del gruppo di essere spostati.

<Upstream>è speciale perché può significare due cose diverse. Se si tratta di un commit che è un antenato del ramo estratto, funge da punto di taglio. Nell'esempio ho fornito, questo sarebbe tutto ciò che non è C, Do master. Tutti i commit vengono eseguiti dopo che <Upstream>il capo del ramo estratto è quello che verrà spostato.

Tuttavia, se <Upstream>non è un antenato, git esegue il backup della catena dal commit specificato fino a quando non trova un antenato comune con il ramo estratto (e si interrompe se non riesce a trovarne uno). Nel nostro caso, una <Upstream>delle B, C, Do mastersaranno tutti risultati commit Bservire come punto di taglio. <Upstream>è esso stesso un comando facoltativo e se non è specificato, git osserva il genitore del ramo estratto che equivale a immettere master.

Ora che git ha selezionato i commit che taglia e si sposta, li applica per <Target>evitare di saltare quelli già applicati al target.

Esempi e risultati interessanti

Utilizzando questo punto di partenza:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Si applicherà commit B, C, X, Y, Zdi impegnarsi De finiscono per saltare Be Cperché già sono state applicate.

  • git rebase --onto C X feature
    Applicherà commit Ye Zcommit C, eliminando efficacemente il commitX


4

Una soluzione più semplice è git rebase <SHA1 of B> topic. Funziona indipendentemente da dove ti HEADtrovi.

Possiamo confermare questo comportamento dal doc git rebase

<upstream>Ramo a monte da confrontare con. Può essere un commit valido , non solo un nome di ramo esistente. Il valore predefinito è l'upstream configurato per il ramo corrente.


Potresti pensare che cosa accadrà se menzionassi anche SHA1 topicnel comando sopra?

git rebase <SHA1 of B> <SHA1 of topic>

Funzionerà anche questo, ma rebase non farà Topicriferimento a un nuovo ramo così creato e HEADsarà in stato distaccato. Quindi da qui devi eliminare manualmente il vecchioTopic e creare un nuovo riferimento di ramo sul nuovo ramo creato da rebase.


3

Ho usato una miscela di soluzioni sopra descritte:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

Ho trovato molto più facile da leggere e capire. La soluzione accettata mi porta a un conflitto di unione (troppo pigro per essere risolto a mano):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

1
stesso qui, diversi file hanno avuto conflitti quando ho usato le 2 risposte più popolari (cioè da r0hitsharma e Dymitruk)
Oliver

3

Poiché il rebasing è così fondamentale, ecco un'espansione della risposta di Nestor Milyaev . Combinando i commenti di jsz e Simon South dalla risposta di Adam Dymitruk si ottiene questo comando che funziona sul topicramo indipendentemente dal fatto che si ramifichi dal mastercommit del ramo Ao C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Si noti che è richiesto l'ultimo argomento (altrimenti riavvolge il ramo per eseguire il commit B).

Esempi:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Quindi l'ultimo comando è quello che di solito uso.


-2

C'è un altro modo di farlo o se desideri tornare a più di un solo commit.

Ecco un esempio per tornare al nnumero di commit:

git branch topic master~n

Per il bene di questa domanda, questo può anche essere fatto:

git branch topic master~1

Il comando funziona perfettamente git version 2.7.4. Non l'ho provato su nessuna altra versione.


Hai frainteso questo come una domanda sulla ramificazione? Questa in realtà è una domanda sul rebasing.
NetherGranite,
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.