2013-07-25 12 views
9

Lo chiedo solo per curiosità. Ci sono altri modi per affrontare questo tipo di situazione nella vita reale, ma trovo il seguente comportamento di git un po 'strano.Rifondazione dallo stash, risultato strano

Sommario: Sommario: lo stivaggio crea, dietro la tenda, due commit, uno contiene l'indice e l'altro le modifiche non aggiunte. Se eseguiamo il checkout di quest'ultimo e proviamo a rebase, in qualche modo otteniamo solo i cambiamenti dall'indice. Perché?

esempio dettagliata segue:

Prima facciamo un pronti contro termine con un commesso, poi un altro di modifica che viene aggiunto all'indice, poi un altro di modifica che non viene aggiunto all'indice, e poi riporre:

git init 
echo 1 > a.txt 
git add a.txt 
git commit -m"First commit" 
echo 2 >> a.txt 
git add a.txt 
echo 3 >> a.txt 
git stash 
git log --all --graph --oneline 

    * 5c00fc0 WIP on master: c8af537 First commit 
    |\ 
    | * 965c986 index on master: c8af537 First commit 
    |/ 
    * c8af537 First commit 

Quindi git stash sembra salvare sia l'indice che la modifica non aggiunta come commit con i propri hash (nel mio caso, 965c986 per l'indice e 5c00fc0 per le modifiche non aggiunte).

Ora modificare un nuovo file e si impegnano:

echo x >> b.txt 
git add b.txt 
git commit -m"Second commit" 

Così tutti i commit apparire così:

git log --all --graph --oneline 

    * b589f50 Second commit 
    | * 5c00fc0 WIP on master: c8af537 First commit 
    | |\ 
    |// 
    | * 965c986 index on master: c8af537 First commit 
    |/ 
    * c8af537 First commit 

Dire, ora vogliono prendere le modifiche stashed e combinarle con la secondo impegno. Ci sono altri modi per fare questo (come git stash apply, ma cosa succede se avevamo già ripulito la scorta, e poi scavato il commit dalla reflog), ma diciamo basta provare con:

git checkout 5c00fc0 
[warning message here] 
cat a.txt 
    1 
    2 
    3 
git rebase master 
    First, rewinding head to replay your work on top of it... 
    Applying: index on master: c8af537 First commit 

Ma ora, il file risultante a.txt è solo:

cat a.txt 
    1 
    2 

questo è l'intero grafo:

git log --all --graph --oneline 
    * 5fc3ade index on master: c8af537 First commit 
    * b589f50 Second commit 
    | * 5c00fc0 WIP on master: c8af537 First commit 
    | |\ 
    |// 
    | * 965c986 index on master: c8af537 First commit 
    |/ 
    * c8af537 First commit 

Quindi sembra che, anche se abbiamo controllato commit 5c00fc0, l'unica rebase ap applicato i cambiamenti dal commit 965c986, cioè le modifiche che erano nell'indice quando abbiamo nascosto. Ma qualunque cosa fosse in 5c00fc0 è stata ignorata.

Domanda: Perché è? C'è qualche spiegazione ragionevole per questo comportamento? O dovrebbe essere considerato un bug?

risposta

1

Viene fuori, git solo (per impostazione predefinita) ignora i commit di unione quando si effettua il rebasing. E la memorizzazione crea il commit WIP e il commit dell'indice e il commit WIP è un'unione, poiché ha sia il commit dell'indice che il c8af537 come genitori.

Niente a che vedere con la conservazione come tale.

+0

Questo non spiega perché ignorerebbe il commit dell'indice. Il rebasing di solito rimuove i commit di unione, ma ciò non significa che rimuova il codice che è stato unito. – Ilion

+0

Non ignora il commit dell'indice. –

-1

Modifica massiva alla mia risposta originale.

Ho fatto un po 'di immersioni nel codice e alcuni test dei miei e ho trovato il problema. Quando esegui il rebase sul tuo ramo senza testa, rebase il master. Il comando rebase verifica il capo del ramo che stai ridefinendo, che in questo caso non include la scorta. Prova a modificare:

git checkout -b stashbranch 5c00fc0 
git rebase stashbranch 
git log --oneline --decorate --all 

Si dovrebbe vedere qualcosa di simile:

b589f50 (HEAD, refs/stash, stashbranch) Second commit 

Nota: Se si è in uno stato di testa staccata ma esegue git rebase master sarà, secondo i miei test, impostare la testa a capo maestro Avresti potuto fare git rebase 5c00fc0 e ottenere gli stessi risultati ottenuti con la ramificazione.

+0

Sembra che tu non abbia capito che (sopra) i commit 5c00fc0 e 965c986 sono creati dal comando stash. –

+0

Lo capisco. Stai ancora cercando di aggirare il normale comportamento del comando stash. – Ilion

+0

Sì, il caso di cui sopra è forse un abuso, o almeno un uso non molto normale. Ma IMHO non è intuitivo che se provo a rebase di '5c00fc0', quindi' 5c00fc0' è ** non ** rebased, ma '965c986' è. In che senso è questo protettivo? Se avessi voluto rebase '965c986', avrei potuto rebase da' 965c986', no? –

0

Mi sono imbattuto in questo problema stamattina dopo aver lasciato cadere una sequenza per caso in un'applicazione GUI. All'inizio ho provato a ridiscendere come altri hanno fatto, perché è la cosa naturale da fare. Poi ho usato duckduckgo per cercare e ho trovato questo, ma nessuna soluzione reale. Così ho provato un po 'più difficile e arrivato fino a questo:

Supponendo che abbiamo i seguenti rami/arbitri:

  • droppedStash che punta a un merge artificiale impegnarsi con la desiderata contenuti
  • parentOfStash che punta al commit originale di base per questo ramo ex-stash

Th en possiamo semplicemente fare:

  1. git checkout parentOfStash
  2. git cherry-pick -m 1 droppedStash

Dove -m 1 dice git di prendere in considerazione uno degli antenati di essere mainline. Ta da! Divertiti :-)