2013-12-12 10 views
9

Durante il refactoring del codice sorgente, a volte è necessario spostare grandi blocchi di testo all'interno di un file, o anche in un nuovo file. Si crea un ramo refactored e si impegnano via:Dire a git di seguire il contenuto spostato (non semplicemente i file spostati)

$git checkout master 
$git branch refactored 
$git checkout refactored 
<move code around> 
$git commit -m "refactored code" 

Tuttavia, le persone possono impegnarsi in cima al vecchio ramo pre-refactoring, modificando il codice che è stato spostato:

$git checkout master 
<change code that was moved elsewhere on branch refactored> 
$git commit -m "bugfix" 

sul ramo refactored, è poi desidera incorporare le modifiche apportate in master:

$git checkout refactored 
$git merge master 
<giant merge conflict> 

Questo porta ad un grande conflitto di unione. Se ci fosse un modo per dire a Git che il contenuto è stato semplicemente spostato, dovrebbe essere possibile unire automaticamente.

La cosa peggiore è che, anche dopo la risoluzione del conflitto e commettere è, git ancora non è possibile utilizzare la risoluzione per capire altre unioni:

<fix conflicts> 
$git commit -m "merge master into refactored" 
$git checkout master 
<change more code> 
$git commit -m "bugfix2" 
$git checkout refactored 
$git merge master 
<another giant merge conflict> 

E` evitabile a tutti? Ho provato git rerere e non può risolvere i conflitti qui. C'è un modo in cui git può vedere lo spostamento di un blocco di testo come una mossa reale, invece di una cancellazione e inserimento? Se non è possibile, qual è l'approccio migliore per ridurre al minimo i conflitti di unione, se è necessario mantenere i due rami paralleli per un po '?

Mentre questo è abbastanza facile per moving the contents of a complete file, non riuscivo a trovare informazioni sullo spostamento solo parte di esso, o lo spostamento all'interno dello stesso file.

Inoltre, se esiste una soluzione per questo, quale sarebbe il comportamento di git blame sul codice refactored ? Indicherebbe il commit del refactoring o lo ignorerebbe? C'è un modo per raggiungere il dopo?

Nel caso in cui qualcuno è interessato, ho messo un Base64 codificato tar.gz del repository (molto minima) che sto utilizzando per il test on pastebin

potenziali soluzioni

Una possibile soluzione potrebbe essere eseguendo il Unisci applicando una patch (automaticamente) modificata con le modifiche nel ramo pre-refactored. C'è un software sviluppato per fare questo? Utilizzando questo approccio, suppongo che, poiché questo è trasparente per git, git blame farebbe riferimento al commit del refactoring.

Ho trovato the same question, applied to diff. Non c'è alcuna menzione di qualsiasi implementazione non proprietaria, ma c'è menzione ad un algoritmo che regola i movimenti del blocco

+0

vi consiglio di fare più commettere. Quando si modificano molti dati, maggiore è il commit, meno problemi si verificano durante l'unione del codice. – sensorario

+0

@sensorario è un buon suggerimento in generale. Non sono sicuro di come applicarlo al refactoring, tuttavia - spostare una sola funzione/piccoli blocchi alla volta non aiuterà il – goncalopp

+0

Se cambi file, hai due contenuti diversi. Sì, anche se si sposta una funzione da un file a un altro. Se vuoi minimizzare i conflitti, fai sempre più commit. Per favore prova. – sensorario

risposta

1

Purtroppo non è possibile sostituire il costruito nel strategie di git di unione, per quanto posso dire. Ciò significa che non è possibile interrompere i conflitti, tuttavia è possibile utilizzare uno strumento intelligente per risolverli.

Questo Semantic Merge sembra interessante, ma può anche essere used by git

+0

Ho provato SemanticMerge nel repository di esempio. Innanzitutto, su Linux, introduce 80MB di dipendenze: molti pacchetti da plasticscm e ciò che sembra essere mono. L'unione deve essere eseguita manualmente usando 'git mergetool', e lo strumento necessita di interazione (" Hit return to start ") e utilizza una GUI (non può eseguirlo da CLI, AFAICT). Infine, non è riuscito a fondere i conflitti nel mio esempio, con "errori di analisi sono stati trovati, gli alberi potrebbero essere incoerenti". Non sono sicuro che lo strumento stia effettivamente facendo qualcosa, dal momento che ricevo un errore "Riferimento dell'oggetto non impostato su un'istanza di un oggetto". – goncalopp

+0

Per correttezza, l'ho provato su Ubuntu 12.04 LTS (Precise Pangolin), che non è necessariamente supportato ('garantiamo il supporto solo per Debian 6.0, ma questo repository può essere indirizzato anche alle nuove versioni di Debian (come 7.0, per istanza) e distribuzioni Ubuntu (specialmente le versioni LTS) ') – goncalopp

0

Non è possibile evitare questo sforzo in più, ma poi di nuovo che è come Git dovrebbe funzionare:

L'altra fondamentale decisione di progettazione intelligente è come Git si fonde. Gli algoritmi di fusione sono intelligenti ma non cercano di essere troppo intelligenti. Le decisioni univoche vengono prese automaticamente, ma quando c'è il dubbio spetta all'utente decidere. Dovrebbe essere così. Non devi vuoi una macchina che prenda queste decisioni per te. Non lo vorresti mai. Questa è l'intuizione fondamentale nell'approccio alla fusione di Git: mentre ogni altro sistema di controllo delle versioni sta cercando di diventare più intelligente, Git è felicemente descritto come "stupido gestore di contenuti", ed è migliore per questo.

(da Wincent Colaiuta's blog)

Problemi correlati