2013-06-20 14 views
7

Sto utilizzando Git per un lungo periodo, ma di recente ho dovuto affrontare il trucco interessante che consente di annullare la cronologia delle modifiche del file durante l'unione. Ecco i passaggi per riprodurre è:Git consente di perdere la cronologia delle modifiche del file durante l'unione con conflitti

Ho il repository git con due file e un commit:

$ git branch 
    * master 
$ git log --oneline 
    80c8d5a Initial commit 
$ git log --oneline -- README 
    80c8d5a Initial commit 
$ ls 
    README conflict_file 

sto creando il nuovo ramo sperimentale e la modifica di file README e conflict_file:

$ checkout -b experiment 
    Switched to branch 'experiment' 
$ vim README 
$ vim conflict_file 
$ git commit -am "Experimental changes" 
    [experiment cdbc988] Experimental changes 
    2 files changed, 2 insertions(+) 
$ git log --oneline -- README 
    cdbc988 Experimental changes 
    80c8d5a Initial commit 

Tornando al ramo principale e modificare conflict_file (per avere il conflitto durante il merge):

$ git checkout master 
    Switched to branch 'master' 
$ vim conflict_file 
$ git commit -am "Master changes" 
    [master ad8b68e] Master changes 
    1 file changed, 1 insertion(+) 

Lo stato del mio repository nei seguenti:

$ git log --oneline --decorate --graph --all 
    * ad8b68e (HEAD, master) Master changes 
    | * cdbc988 (experiment) Experimental changes 
    |/ 
    * 80c8d5a Initial commit 

Cercando di fondersi con il ramo sperimentale:

$ git merge experiment 
    Auto-merging conflict_file 
    CONFLICT (content): Merge conflict in conflict_file 
    Automatic merge failed; fix conflicts and then commit the result. 
$ git mergetool 
    <merging conflict in conflict_file> 

Ecco il trucco:

$ git reset HEAD README 
    Unstaged changes after reset: 
    M  README 
$ git commit 
    [master 909139f] Merge branch 'experiment' 
$ git log --oneline -- README 
    80c8d5a Initial commit 

ho perso le modifiche e cronologia del file README introdotto nel ramo sperimentaleModifiche sperimentali commit. Qualcuno potrebbe commentare come è correlato all'idea che Git conosca tutti i cambiamenti? È possibile evitare questo scenario? Potrebbe diventare un problema nella nostra azienda, perché l'operazione di unione è consentita per gli sviluppatori, ma potrebbero rimuovere per errore le modifiche di qualcuno.

+1

Prova l'opzione '--full-history' a' git log'. Leggi anche la pagina man relativa a _history simplification_: "Modalità predefinita ... la storia più semplice che spiega lo stato finale dell'albero." – chirlu

+0

Perché sei sorpreso che le modifiche al file 'README' siano finite? hai richiesto esplicitamente che si verifichi con 'git reset HEAD README'. – Chronial

+0

Invocazione di 'git log --follow - ' helpen me – erkfel

risposta

7

Nessuna cronologia è stata persa: la modifica a README eseguita sul ramo sperimentale è ancora contenuta nel commit 210fdc1.Il fatto che non faccia parte del commit di unione 909139f è dovuto al fatto che l'hai annullato in modo esplicito prima di finalizzare l'unione.

Non hai detto cosa ti aspettavi che succedesse, quindi posso solo indovinare cosa ti ha sorpreso esattamente qui. Indicherò solo due probabili candidati.

  1. Un merge commit non si limita a modificare solo i file toccato in quanto la base di fusione, o anche solo i file che hanno modifiche in conflitto. In effetti, può essere completamente diverso da tutti i suoi genitori.

    "Completamente diverso", naturalmente, non è quello che si consiglia, perché spezzerebbe le ipotesi di tutti su cosa sia una fusione. Tuttavia, può essere perfettamente logico estendere i file di documentazione in un commit di unione e la risoluzione di determinati conflitti di unione potrebbe richiedere la modifica di file aggiuntivi.

  2. Quando si chiama git log -- some_path, viene richiamata una procedura denominata semplificazione della cronologia. Come la pagina man lo descrive, git log proverà a visualizzare solo quei commit "che sono sufficienti per spiegare in che modo i file che corrispondono ai percorsi specificati sono diventati". Nel tuo caso, il commit iniziale è sufficiente per spiegare lo stato corrente di README (perché lo README ha ancora, o ancora, lo stesso contenuto), quindi è tutto ciò che viene mostrato.

    È possibile utilizzare --full-history, eventualmente insieme a --simplify-merges, o alcune altre opzioni di semplificazione della cronologia. Loro, così come le regole esatte per includere i commit (che sono più complicati di quelli impliciti sopra), sono described in the git log man page, con un esempio esteso.

+0

Ho basato la mia aspettativa sull'idea che ogni cambiamento dovrebbe essere tracciato e salvato nella cronologia delle modifiche del file, la mia aspettativa era di avere 3 modifiche nel file README nel 909139f. Lo stesso comportamento se si sta usando un branche e quando si apportano alcune modifiche e quindi si annullano queste modifiche, ci saranno 2 commit diversi senza usare '--amend' e saranno mostrati per voi in' git log'. – erkfel

+0

Stiamo provando a introdurre Git nella nostra organizzazione, gli sviluppatori utilizzano IDE per commit/merge/push e Gerrit/GitHub per accedere al codice tramite interfaccia utente, abbiamo politiche di utilizzo Git come disabilitare force push, feature branch, ecc. Git e riceviamo il lamentarsi che è molto facile perdere le modifiche. – erkfel

+0

Dopo le indagini scopriamo che è il risultato di unioni errate come nel mio esempio, quando le persone che fanno qualcosa di sbagliato durante i conflitti si uniscono e accidentalmente ripristinano (e quindi rimuovono) le modifiche. Quando menzionano la scomparsa di alcune modifiche vanno a Gerrit/GitHub, controllare la cronologia dei file e non vedere che le modifiche non sono state introdotte. Questo è molto confuso. Solo indagini approfondite aiutano a trovare l'unione non corretta. – erkfel

1

non si è effettivamente perso le modifiche, è solo che non hanno controllato nei cambiamenti al file README ancora, quindi non sono in alcun commit dimostrato dai vostri git log comandi:

* 9b8ede8 (HEAD, master) Merge branch 'experiment' 
|\ 
| * 210fdc1 (experiment) Experimental 
* | d0c1637 Master changes 
|/ 
* b558d42 Initial commit 

(il mio SHA di sono ovviamente diversi) e:

$ git status 
# On branch master 
# Changes not staged for commit: 
# (use "git add <file>..." to update what will be committed) 
# (use "git checkout -- <file>..." to discard changes in working directory) 
# 
# modified: README 
# 
# Untracked files: 
# (use "git add <file>..." to include in what will be committed) 
# 
# conflict_file.orig 
no changes added to commit (use "git add" and/or "git commit -a") 

Come @Chronial notato nei commenti, è detto esplicitamente git per fare un git reset --mixed (--mixed è il default) del file README all'ora HEAD, che era (nel tuo caso) ad8b68e.

Had si esegue git status nel bel mezzo della la stampa conflittuale (prima git mergetool), avreste visto questo:

$ git status 
# On branch master 
# You have unmerged paths. 
# (fix conflicts and run "git commit") 
# 
# Changes to be committed: 
# 
# modified: README 
# 
# Unmerged paths: 
# (use "git add <file>..." to mark resolution) 
# 
# both modified:  conflict_file 
# 

Il passo git mergetool risolve il conflitto, lo spostamento conflict_file nella stessa ready-to-commit stato come README sopra - ma dopo il reset --mixed, README si passa da "modifiche da impegnare" a "modifiche non pianificate per il commit".

+0

Grazie per la risposta, si prega di controllare i miei commenti alla risposta "chirlu". – erkfel

Problemi correlati