git cherry-pick dice "... 38c74d è un'unione ma non è stata data alcuna opzione -m"


519

Ho apportato alcune modifiche al mio ramo principale e voglio portarle a monte. quando seleziono i seguenti commit, tuttavia mi blocco su fd9f578 dove git dice:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

Cosa sta cercando di dirmi Git ed è Cherry-Pick la cosa giusta da usare qui? Il ramo master include modifiche ai file che sono stati modificati nel ramo upstream, quindi sono sicuro che ci saranno alcuni conflitti di unione, ma questi non sono poi così male da raddrizzare. So quali cambiamenti sono necessari dove.

Questi sono i commit che voglio portare a monte.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...

Risposte:


607

Il modo in cui funziona una scelta della ciliegia è prendendo il diff che un changeset rappresenta (la differenza tra l'albero di lavoro in quel punto e l'albero di lavoro del suo genitore) e applicandolo al ramo corrente.

Quindi, se un commit ha due o più genitori, rappresenta anche due o più differenze: quale dovrebbe essere applicato?

Stai cercando di scegliere una ciliegia fd9f578, che è stata un'unione con due genitori. Quindi devi dire al comando cherry-pick quale tra cui dovrebbe essere calcolato il diff, usando l' -mopzione. Ad esempio, git cherry-pick -m 1 fd9f578per utilizzare il genitore 1 come base.

Non posso dirlo con certezza per la tua situazione particolare, ma l'utilizzo al git mergeposto di git cherry-pickè generalmente consigliabile. Quando selezioni un commit di unione, comprime tutte le modifiche apportate nel genitore che non hai specificato -min quell'unico commit . Si perde tutta la loro storia e si radunano tutte le loro differenze. La tua chiamata.


3
@wufoo Probabilmente dovresti anche saperne di più git rebase: è come un'unione, ma invece di integrare due rami trapianta l'uno per sedersi sopra l'altro.
Borealid,

92
come conosci il numero genitore?
Anentropico

66
@Anentropic 1 è il "primo genitore", 2 è il "secondo genitore" e così via. L'ordine è quello in cui sono elencati nel commit (come visto da git showe simili).
Borealid,

2
@lkraav Potresti anche aver appena fatto un git reset --hard HEAD@{1}per recuperare il tuo commit mancante. git resetnon si limita a spostarsi "indietro" nella storia. git checkout -b mybranch HEAD@{1}funzionerebbe anche.
Borealid,

4
ATTENZIONE: git mergepotrebbe avere conseguenze indesiderate. Tale comando aggiungerà tutti gli altri commit (precedenti) presenti sul ramo padre. Di solito le persone scelgono di scegliere la ciliegia perché non vogliono che gli altri si impegnino. Assicurati di ricontrollare che stai implementando solo le modifiche che desideri!
Kay V

52

-m indica il numero principale.

Dal documento git:

Di solito non è possibile selezionare una fusione perché non si conosce quale parte della fusione debba essere considerata la linea principale. Questa opzione specifica il numero principale (a partire da 1) della linea principale e consente a cherry-pick di riprodurre la modifica relativa al padre specificato.

Ad esempio, se l'albero di commit è come di seguito:

- A - D - E - F -   master
   \     /
    B - C           branch one

quindi git cherry-pick Eprodurrà il problema riscontrato.

git cherry-pick E -m 1significa usare D-E, mentre git cherry-pick E -m 2significa usare B-C-E.


32

La risposta di @ Borealid è corretta, ma supponiamo che non ti interessi conservare l'esatta storia di fusione di un ramo e desideri semplicemente scegliere una versione linearizzata di esso. Ecco un modo semplice e sicuro per farlo:

Stato di partenza: sei sul ramo Xe vuoi selezionare i commit Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (opzionale) git branch -D tempZ

Ciò che fa è creare un ramo tempZbasato su Z, ma con la storia da Ylinearizzare in poi, e poi selezionarlo su una copia di Xchiamato newX. (È più sicuro farlo su un nuovo ramo piuttosto che mutare X.) Naturalmente potrebbero esserci dei conflitti nel passaggio 4, che dovrai risolvere nel solito modo ( cherry-pickfunziona in modo molto simile rebasea questo riguardo). Alla fine cancella il tempZramo temporaneo .

Se il passaggio 2 riporta il messaggio "Current branch tempZ è aggiornato", allora Y..Zera già lineare, quindi ignora quel messaggio e procedi con i passaggi da 3 in poi.

Quindi rivedi newXe vedi se ha fatto quello che volevi.

(Nota: questo non è lo stesso di un semplice git rebase Xramo Z, perché non dipende in alcun modo dalla relazione tra Xe Y; potrebbero esserci dei commit tra l'antenato comune e Yquello che non volevi.)


1
git rebase YdiceCurrent branch tempZ is up to date
Basilevs,

Penso che ciò significhi che Y..Zera già lineare. Quindi puoi ignorare quel messaggio e procedere con i passaggi 3 e 4.
Daira Hopwood,

1
Idea interessante, ho dovuto disegnarlo su carta per apprezzare appieno quello che stava succedendo = D
Chris

2
Brillante. git cherry-pick per un'intera gamma si è lamentato del fatto che l'opzione -m mancava o che fosse fornita. La tua soluzione è stata d'oro. (Un suggerimento: elimina il ramo tempZ dopo)
Otheus

1
è fantastico! Ho avuto problemi con la scelta della ciliegia e solo questo aveva senso. Ho avuto un po 'di problemi con le lettere raccolte (alcuni rami e commit sono lettere maiuscole e alcuni rami sono minuscoli)
pcarvalho

19

Semplificare. Cherry-pick the commit. Non scegliere la fusione.

Ecco una riscrittura della risposta accettata che chiarisce idealmente i vantaggi / i rischi di possibili approcci:

Stai cercando di scegliere cherry fd9f578, che è stata una fusione con due genitori.

Invece di scegliere una fusione in ciliegio, la cosa più semplice è scegliere ciliegie i commit effettivamente desiderati da ciascun ramo della fusione.

Dal momento che hai già unito, è probabile che tutti i tuoi commit desiderati siano nella tua lista. Cherry-selezionali direttamente e non è necessario pasticciare con il commit di unione.

spiegazione

Il modo in cui funziona una selezione di ciliegie è prendendo il diff che rappresenta un changeset (la differenza tra l'albero di lavoro in quel punto e l'albero di lavoro del suo genitore) e applicando il changeset al ramo corrente.

Se un commit ha due o più genitori, come nel caso di un'unione, anche questo commit rappresenta due o più differenze. L'errore si verifica a causa dell'incertezza su quale diff dovrebbe essere applicato.

alternative

Se si determina che è necessario includere la fusione tra la selezione della ciliegia e i relativi commit, sono disponibili due opzioni:

  1. (Più complicato e oscuro; scarta anche la storia) puoi indicare quale genitore dovrebbe applicare.

    • Utilizzare l' -mopzione per farlo. Ad esempio, git cherry-pick -m 1 fd9f578utilizzerà il primo genitore elencato nell'unione come base.

    • Considera anche che quando selezioni un commit di unione, comprime tutte le modifiche apportate al genitore che non hai specificato -min quell'unico commit . Si perde tutta la loro storia e si radunano tutte le loro differenze. La tua chiamata.

  2. (Più semplice e familiare; conserva la cronologia) che puoi utilizzare git mergeinvece di git cherry-pick.

    • Come al solito git merge, tenterà di applicare tutti i commit esistenti sul ramo che si sta unendo ed elencarli singolarmente nel proprio registro git.

2

Semplificazione del metodo @Daira Hopwood per la selezione di un singolo commit. Non sono necessarie filiali temporanee.

Nel caso dell'autore:

  • Z è voluto commit (fd9f578)
  • Y è commesso prima di esso
  • X ramo di lavoro attuale

Quindi fa:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit

2
Questo ovviamente perde i metadati associati al commit originale. Immagino sia una questione di opinione se sia più semplice. Lo uso a volte quando voglio perdere i metadati e mantenere solo le modifiche generali del codice. Nota che funziona anche se Y non è un genitore immediato di Z (nel qual caso le modifiche verranno schiacciate).
Daira Hopwood,
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.