Perché git non unisce linee adiacenti senza conflitti?


25

Di recente ho imparato che quando si uniscono due rami in git, se ci sono cambiamenti su due linee adiacenti , git lo dichiara un conflitto. Ad esempio, se il file test.txtha questo contenuto:

Line 1: A
Line 2: B
Line 3: C
Line 4: D

e in ramo masterlo cambiamo in

Line 1: A
Line 2: B1
Line 3: C
Line 4: D

mentre nel ramo testinglo cambiamo in

Line 1: A
Line 2: B
Line 3: C1
Line 4: D

e quindi tenta di unirsi testingin master, git dichiara un conflitto di unione. La mia ingenua aspettativa era che la fusione sarebbe avvenuta senza conflitti e avrebbe prodotto questo:

Line 1: A
Line 2: B1
Line 3: C1
Line 4: D

Sono sicuro che c'è una buona ragione per cui git non si fonde in questo modo. Qualcuno può spiegare questo motivo?


Ehi, ho appena notato anche questa settimana scorsa. Forse stavamo facendo lo stesso tutorial.
detly

5
Le capacità di fusione di git sono in realtà piuttosto scarse, IMO
James

@James hai provato a utilizzare l'algoritmo di pazienza? Trovo di ottenere risultati migliori con esso, in particolare quando ho a che fare con la divisione degli hunk (ad esempio, afferrando un corpo di funzione anziché due). Se non ti piacciono i git, puoi anche usarne uno tuo (vedi blog.wuwon.id.au/2010/09/… per un esempio).
deterb

1
La causa principale è che git prova a fare l'unione stessa, invece di fattorizzarla su uno strumento specializzato. Non interamente la filosofia Unix. Per i file di origine, puoi effettivamente utilizzare la grammatica della lingua per determinare in modo affidabile le differenze.
MSalters,

L'unico contesto comune è A e D, quindi perché A / C1 / B1 / D non è l'unione corretta?
Izkata

Risposte:


13

Supponendo che questo frammento di codice

x=0
x+=1 if foo
x+=1 if bar
return x

è stato modificato in un ramo in questo

x=0
x+=1 if foo && xyzzy
x+=1 if bar
return x

e in un altro ramo in questo

x=0
x+=1 if foo
x+=1 if bar && xyzzy
return x

allora non vorrei che Git lo fondesse in questo

x=0
x+=1 if foo && xyzzy
x+=1 if bar && xyzzy
return x

senza allarmarmi.

Al fine di evitare di causare tali problemi, git di solito si rifiuta di unire automaticamente le modifiche toccando le linee vicine. Ti dà la possibilità di verificare se la logica del programma sarebbe rotta o meno.

Questo esempio è banale, ma quando si uniscono enormi rami il rischio di conflitti "logici" simili è molto più grande. A volte mi piacerebbe persino che il contesto fosse ancora più grande di quello che è attualmente.


5
Ciò non ha nulla a che fare con esso, tuttavia, è sufficiente aggiungere una linea immutabile tra quei due e improvvisamente git li unisce senza problemi.
Darkhogg,

Sì, non ho questa risposta. È possibile utilizzare la stessa logica per evitare tutte le fusioni automatiche.
Mehrdad,

Deve esserci una linea tracciata tra sicurezza e utilità. Le fusioni automatiche comportano il rischio di corrompere il flusso del programma - come stavo cercando di mostrare nella risposta -, ma se Git si rifiutasse di unire qualcosa, diventerebbe inutile. I creatori di git hanno appena deciso un certo contesto, che prenderà la maggior parte dei casi "pericolosi". Aggiungendo una linea invariata (come ha scoperto Darkhogg), gli idioti credono che l'unione potrebbe essere sicura da eseguire.
Arsen7,

11

Questo comportamento è solo git?

Dopo una discussione con un collega, ho appena provato, e SVN lo gestisce senza problemi: ottieni le 2 righe modificate.

Le funzionalità di unione di diversi VCS sono testate qui per bazar, darc, git e mercurial : https://github.com/mndrix/merge-this

Sembra che solo i darc abbiano unito con successo il caso "linee adiacenti".

L'applicazione di modifiche adiacenti ai file non è un problema difficile. Penso davvero che questo comportamento sia stato scelto apposta.

Perché qualcuno dovrebbe decidere che la modifica delle linee adiacenti produce un conflitto?

Penso che questo sia per costringerti a guardarlo .

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif numero 1, sul master:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif numero 2, unito da un ramo:

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Dopo l'unione, non vuoi che:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Vedendo questo comportamento come una caratteristica

È possibile trasformare il comportamento di fusione git in un vantaggio. Quando è necessario mantenere coerenti 2 righe ma non è possibile rilevarle (al momento della compilazione, all'inizio dei test o altro), è possibile provare a unirsi a esse.

Riscrivi questo ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    // Need to do something else
    do_something_else(r);

...a questa:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    do_something_else(r); // Need to do something else

Quindi quando unisci Modif 1 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i)/2; // we need only the half
    do_something_else(r); // Need to do something else

... con Modif 2 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    if(r < 0) // do_stuff can return an error
        handle_error(r);
    do_something_else(r/2); // Need to do something else

..., git produrrà un conflitto e ti costringerai a guardarlo.


2
Sto solo andando avanti e dirò che penso che la tua risposta sia perfettamente ragionevole, ma a seconda dell'interazione intricata e definita dall'implementazione tra il tuo codice e il controllo del codice sorgente per il controllo della sanità mentale è una strada veloce verso thedailywtf.com. La fusione cieca di codice senza un parser di linguaggio è SEMPRE il miglior sforzo in ogni caso, e ho avuto alcuni casi in cui git ha automatizzato qualcosa che non avrebbe dovuto avere e prodotto codice che non sarebbe nemmeno stato compilato.
Wug

5

Sto indovinando principalmente, ma penso che abbia a che fare con la linea 2 utilizzata come contesto per il cambio di linea 3.

Git non può semplicemente dire che "La linea con C è diventata una linea con C1" perché potrebbe esserci un'altra linea con "C", quindi dice "La linea con C, che è subito dopo l'inizio del file, la linea con A, e la linea con B, è ora C1 "

Se "la linea con B" non è più presente, allora parte del contesto viene persa e git può solo dire approssimativamente dove deve andare la nuova linea.


5
è anche molto probabile che C dipenda da B, quindi un'ingenua fusione può essere problematica anche se git "sa come" farlo
Lucina

Credetemi Git solo "pensa di sapere". Git è un cimitero di concetti sbagliati, che cerca di raddrizzarsi!
user3833732

2

Le altre risposte qui sono tutte valide, ma per me questo mi è sempre sembrato un limite inutile.

Come altri hanno già detto, in questi casi non vorrai assolutamente che Git unisca le linee senza preavviso.

Ma volevo ancora l'opzione di farlo automaticamente, dopo essere stato avvertito. Così ho scritto un driver di unione git personalizzato che potrebbe unire i conflitti su linee adiacenti (o individuali) in modo interattivo:

inserisci qui la descrizione dell'immagine

Mi fa risparmiare un sacco di tempo, poiché gestisco un progetto in cui le persone lavorano spesso sugli stessi file e refactoring di un sacco di codice.

Lo script è disponibile su GitHub con una licenza GPLv3 +. Forse lo troverai utile:

https://github.com/paulaltin/git-subline-merge


4
Qualcuno potrebbe spiegare perché questo è stato sottoposto a downgrade? Sono abbastanza nuovo qui, quindi se ho fatto qualcosa di sbagliato mi piacerebbe sapere cosa fosse in modo da poterlo evitare in futuro. Mi rendo conto che il mio post non risponde esattamente alla domanda, ma è comunque pertinente e penso che la maggior parte delle persone che vengono qui vorrebbero sapere non solo perché Git fa questo, ma anche cosa possono fare al riguardo (come ho fatto io quando prima ho raggiunto questa domanda da una ricerca su Google).
deltacrux,

Non l'ho ancora provato, ma sono venuto alla ricerca di un modo per automatizzare questo, ed è stato felice di trovarlo qui. Grazie :) sei fantastico.
Mehrdad,

1
Nessun problema! Spero che lo troviate utile e non esitate a lasciare un feedback su Github in caso di problemi o se avete suggerimenti per migliorare. Grazie!
deltacrux,
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.