Git workflow e rebase vs unisci domande


971

Uso Git da un paio di mesi su un progetto con un altro sviluppatore. Ho diversi anni di esperienza con SVN , quindi credo di portare molti bagagli nella relazione.

Ho sentito che Git è eccellente per ramificarsi e fondersi, e finora non lo vedo. Certo, la ramificazione è morta in modo semplice, ma quando provo a fondermi, tutto va all'inferno. Ora, sono abituato a questo da SVN, ma mi sembra di aver appena scambiato un sistema di versioning inferiore a un altro.

La mia compagna mi dice che i miei problemi derivano dal mio desiderio di unirmi volenti o nolenti e che dovrei usare rebase invece di unirmi in molte situazioni. Ad esempio, ecco il flusso di lavoro che ha definito:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

In sostanza, creare un ramo di funzionalità, SEMPRE reimpostare dal master al ramo e unire nuovamente dal ramo al master. È importante notare che il ramo rimane sempre locale.

Ecco il flusso di lavoro con cui ho iniziato

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

Ci sono due differenze essenziali (credo): uso sempre la fusione anziché la ridisposizione e spingo il ramo della mia funzione (e il mio ramo della funzione si impegna) nel repository remoto.

Il mio ragionamento per il ramo remoto è che voglio che il mio lavoro sia sottoposto a backup mentre sto lavorando. Il nostro repository viene automaticamente sottoposto a backup e può essere ripristinato se qualcosa va storto. Il mio laptop non lo è o non è così completo. Pertanto, odio avere un codice sul mio laptop che non si rispecchi da qualche altra parte.

Il mio ragionamento per l'unione invece di rebase è che l'unione sembra essere standard e rebase sembra essere una funzionalità avanzata. La mia sensazione è che ciò che sto cercando di fare non sia una configurazione avanzata, quindi rebase non dovrebbe essere necessario. Ho anche sfogliato il nuovo libro di Programmazione pragmatica su Git, e coprono l'unione ampiamente e menzionano a malapena rebase.

Ad ogni modo, stavo seguendo il mio flusso di lavoro su un ramo recente e quando ho provato a fonderlo con il master, tutto è andato all'inferno. Ci sono stati molti conflitti con cose che non avrebbero dovuto avere importanza. I conflitti non avevano senso per me. Mi ci è voluto un giorno per sistemare tutto, e alla fine è culminato in una spinta forzata al master remoto, dal momento che il mio master locale ha risolto tutti i conflitti, ma quello remoto non era ancora felice.

Qual è il flusso di lavoro "corretto" per qualcosa del genere? Si suppone che Git renda le ramificazioni e le fusioni super facili, e non le vedo proprio.

Aggiornamento 15-04-2011

Questa sembra essere una domanda molto popolare, quindi ho pensato di aggiornare con la mia esperienza di due anni dalla prima volta che l'ho chiesto.

Si scopre che il flusso di lavoro originale è corretto, almeno nel nostro caso. In altre parole, questo è ciò che facciamo e funziona:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

In effetti, il nostro flusso di lavoro è leggermente diverso, poiché tendiamo a fare fusioni di squash invece di fusioni grezze. ( Nota: questo è controverso, vedi sotto. ) Questo ci consente di trasformare l'intero ramo delle caratteristiche in un unico commit sul master. Quindi eliminiamo il nostro ramo di funzionalità. Questo ci consente di strutturare logicamente i nostri commit sul master, anche se sono un po 'disordinati sui nostri rami. Quindi, questo è quello che facciamo:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

Controversia di fusione delle squash - Come sottolineato da numerosi commentatori, la fusione delle squash getterà via tutta la cronologia del ramo delle funzionalità. Come suggerisce il nome, riduce tutti i commit in uno solo. Per le piccole funzionalità, questo ha senso in quanto lo condensa in un unico pacchetto. Per funzionalità più grandi, probabilmente non è una grande idea, soprattutto se i tuoi singoli commit sono già atomici. Dipende davvero dalle preferenze personali.

Github e Bitbucket (altri?) Richieste pull - Nel caso in cui ti stia chiedendo in che modo l'unione / rebase si collega alle richieste pull, ti consiglio di seguire tutti i passaggi precedenti fino a quando non sei pronto per ricollegarti al master. Invece di unirti manualmente a git, accetti semplicemente il PR. Nota che questo non farà una fusione di squash (almeno non di default), ma non-squash, non-fast-forward è la convenzione di unione accettata nella community di Pull Request (per quanto ne so). In particolare, funziona così:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin

Ho imparato ad amare Git e non voglio tornare a SVN. Se stai lottando, resta su di esso e alla fine vedrai la luce alla fine del tunnel.


31
Sfortunatamente, il nuovo libro di Programmazione Pragmstic è principalmente scritto dall'uso di Git mentre continua a pensare in SVN, e in questo caso ti ha ingannato. In Git, rebase rende le cose semplici quando possono essere. La tua esperienza potrebbe dirti che il tuo flusso di lavoro non funziona in Git, non che Git non funziona.
Paul,

18
Non consiglierei l'unione di squash in questo caso, in quanto non salva informazioni su ciò che viene unito (proprio come svn, ma non mergeinfo qui).
Marius K,

7
Adoro la nota in fondo, ho avuto un'esperienza simile di lotta con Git, ma ora faccio fatica a immaginare di non usarla. Grazie anche per la spiegazione finale, mi ha aiutato molto nella rebasecomprensione
Jon Phenow l'

6
Dopo aver terminato la funzione, non dovresti rifare l'ultima volta prima di unire new_feature a master?
softarn,

17
Il tuo flusso di lavoro perde tutta la cronologia di commit dal ramo eliminato :(
Max Nanasy il

Risposte:


372

"Conflitti" significano "evoluzioni parallele di uno stesso contenuto". Quindi, se va "tutto all'inferno" durante un'unione, significa che hai enormi evoluzioni sullo stesso set di file.

Il motivo per cui un rebase è quindi migliore di una fusione è che:

  • riscrivi la tua cronologia di commit locale con quella del master (e quindi riapplica il tuo lavoro, risolvendo quindi eventuali conflitti)
  • la fusione finale sarà sicuramente una "avanzamento veloce", perché avrà tutta la cronologia di commit del master, più solo le tue modifiche per riapplicare.

Confermo che il flusso di lavoro corretto in quel caso (evoluzioni su un set comune di file) viene prima riformulato, quindi unito .

Tuttavia, ciò significa che, se si preme il proprio ramo locale (per motivi di backup), quel ramo non deve essere estratto (o almeno utilizzato) da chiunque altro (poiché la cronologia del commit verrà riscritta dal rebase successivo).


Su quell'argomento (rebase quindi unisci flusso di lavoro), barraponto menziona nei commenti due post interessanti, entrambi da randyfay.com :

Usando questa tecnica, il tuo lavoro va sempre al di sopra del ramo pubblico come una patch aggiornata con l'attuale HEAD.

( esiste una tecnica simile per il bazar )


27
Per una tecnica che consente il rebasing e la condivisione, vedere softwareswirl.blogspot.com/2009/04/…
mhagger

2
randyfay.com/node/91 e randyfay.com/node/89 sono letture meravigliose. questi articoli mi hanno fatto capire cosa era logorato dal mio flusso di lavoro e quale sarebbe stato un flusso di lavoro ideale.
Capi Etheriel,

giusto per dirlo, rifare dal ramo principale al tuo locale è fondamentalmente aggiornare qualsiasi cronologia che il tuo locale potrebbe aver perso di cui il maestro è a conoscenza dopo un qualche tipo di fusione?
Hellatan,

@dtan quello che descrivo qui è il riequilibrare locale in cima al maestro. Non stai aggiornando esattamente la cronologia locale, ma piuttosto riapplicando la cronologia locale in cima al master per risolvere qualsiasi conflitto all'interno della filiale locale.
VonC

386

TL; DR

Un flusso di lavoro di git rebase non ti protegge da persone che sono cattive nella risoluzione dei conflitti o da persone che sono abituate a un flusso di lavoro SVN, come suggerito in Come evitare disastri di Git: una storia di Gory . Rende solo noiosa la risoluzione dei conflitti e rende più difficile riprendersi da una cattiva risoluzione dei conflitti. Invece, usa diff3 in modo che non sia così difficile in primo luogo.


Il rifacimento del flusso di lavoro non è migliore per la risoluzione dei conflitti!

Sono molto pro-rebase per ripulire la storia. Tuttavia, se mai avessi raggiunto un conflitto, interrompo immediatamente il rebase e faccio invece un'unione! Mi uccide davvero il fatto che le persone stiano raccomandando un flusso di lavoro rebase come una migliore alternativa a un flusso di lavoro di unione per la risoluzione dei conflitti (che è esattamente ciò di cui trattava questa domanda).

Se va "tutto all'inferno" durante una fusione, andrà "tutto all'inferno" durante un rebase, e potenzialmente anche molto più inferno! Ecco perché:

Motivo n. 1: risolvere i conflitti una volta, anziché una volta per ciascun commit

Quando si effettua il rebase anziché l'unione, sarà necessario eseguire la risoluzione dei conflitti fino a quante volte si è impegnati a rebase, per lo stesso conflitto!

Scenario reale

Mi dirigo dal maestro per refactoring un metodo complicato in un ramo. Il mio lavoro di refactoring comprende un totale di 15 commit mentre lavoro per refactoring e ottenere revisioni del codice. Parte del mio refactoring comporta la correzione delle schede e degli spazi misti che erano presenti in precedenza in master. Ciò è necessario, ma sfortunatamente entrerà in conflitto con qualsiasi modifica apportata successivamente a questo metodo in master. Abbastanza sicuro, mentre sto lavorando su questo metodo, qualcuno apporta una modifica semplice e legittima allo stesso metodo nel ramo master che dovrebbe essere unito alle mie modifiche.

Quando è il momento di ricollegare il mio ramo con il master, ho due opzioni:

git merge: ho un conflitto. Vedo il cambiamento che hanno fatto per padroneggiare e fonderlo con (il prodotto finale) del mio ramo. Fatto.

git rebase: ho un conflitto con il mio primo commit. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio secondo commit. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio terzo commit. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio quarto commit. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio quinto commit. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio sesto impegno. Risolvo il conflitto e continuo il rebase. Ho un conflitto con il mio settimocommettere. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio ottavo impegno. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio nono commit. Risolvo il conflitto e continuo il rebase. Ho un conflitto con il mio decimo impegno. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio undicesimo impegno. Risolvo il conflitto e continuo il rebase. Ho un conflitto con il mio dodicesimo impegno. Risolvo il conflitto e continuo il rebase. Ho un conflitto con il mio tredicesimo impegno. Risolvo il conflitto e continuo il rebase. Ho un conflitto con il mio quattordicesimocommettere. Risolvo il conflitto e continuo il rebase. Ottengo un conflitto con il mio quindicesimo impegno. Risolvo il conflitto e continuo il rebase.

Mi stai prendendo in giro se questo è il tuo flusso di lavoro preferito. Tutto ciò che serve è una correzione degli spazi bianchi che è in conflitto con una modifica apportata al master e ogni commit sarà in conflitto e deve essere risolto. E questo è uno scenario semplice con solo un conflitto di spazi bianchi. Il cielo non voglia si ha un vero e proprio conflitto che coinvolge le principali modifiche al codice attraverso file e devono risolvere che più volte.

Con tutta la risoluzione extra dei conflitti che devi fare, aumenta solo la possibilità che tu commetta un errore . Ma gli errori vanno bene perché puoi annullare, giusto? Tranne ovviamente ...

Motivo n. 2: con rebase, non è possibile annullare!

Penso che possiamo essere tutti d'accordo sul fatto che la risoluzione dei conflitti può essere difficile, e anche che alcune persone sono pessime. Può essere molto soggetto a errori, motivo per cui è così grande che Git rende facile annullare!

Quando unisci un ramo, git crea un commit di unione che può essere scartato o modificato se la risoluzione del conflitto non va a buon fine. Anche se hai già inviato il commit di unione errato al repository pubblico / autorevole, puoi utilizzare git revertper annullare le modifiche introdotte dall'unione e ripetere correttamente l'unione in un nuovo commit di unione.

Quando rinnovi un ramo, nel probabile caso in cui la risoluzione dei conflitti sia sbagliata, sei fregato. Ogni commit ora contiene la cattiva unione e non puoi semplicemente ripetere il rebase *. Nel migliore dei casi, è necessario tornare indietro e modificare ciascuno dei commit interessati. Non è divertente.

Dopo un rebase, è impossibile determinare ciò che originariamente faceva parte dei commit e cosa è stato introdotto a seguito di una cattiva risoluzione dei conflitti.

* Può essere possibile annullare un rebase se è possibile estrarre i vecchi ref dai log interni di git o se si crea un terzo ramo che punta all'ultimo commit prima del rebasing.

Allontanati dalla risoluzione dei conflitti: usa diff3

Prendi questo conflitto per esempio:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Osservando il conflitto, è impossibile dire che cosa è cambiato ogni ramo o quale fosse il suo intento. Questo è il motivo principale secondo me perché la risoluzione dei conflitti è difficile e confusa.

diff3 in soccorso!

git config --global merge.conflictstyle diff3

Quando usi diff3, ogni nuovo conflitto avrà una terza sezione, l'antenato comune unito.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Prima esamina l'antenato comune unito. Quindi confrontare ciascun lato per determinare l'intento di ciascun ramo. Puoi vedere che HEAD ha cambiato EmailMessage in TextMessage. Il suo intento è cambiare la classe utilizzata in TextMessage, passando gli stessi parametri. Puoi anche vedere che l'intento di feature-branch è passare false invece che true per l'opzione: include_timestamp. Per unire questi cambiamenti, combina l'intento di entrambi:

TextMessage.send(:include_timestamp => false)

In generale:

  1. Confronta l'antenato comune con ogni ramo e determina quale ramo ha la modifica più semplice
  2. Applica quella semplice modifica alla versione dell'altro ramo del codice, in modo che contenga sia la modifica più semplice che quella più complessa
  3. Rimuovi tutte le sezioni del codice di conflitto diverse da quella in cui hai appena unito le modifiche

Alternativo: risolvere applicando manualmente le modifiche del ramo

Infine, alcuni conflitti sono terribili da capire anche con diff3. Ciò accade soprattutto quando diff trova linee in comune che non sono semanticamente comuni (ad es. Entrambi i rami hanno una linea vuota nello stesso posto!). Ad esempio, un ramo modifica il rientro del corpo di una classe o riordina metodi simili. In questi casi, una strategia di risoluzione migliore può essere quella di esaminare la modifica da entrambi i lati dell'unione e applicare manualmente il diff all'altro file.

Diamo un'occhiata a come si potrebbe risolvere un conflitto in uno scenario in cui la fusione origin/feature1in cui lib/message.rbi conflitti.

  1. Decidi se il nostro ramo attualmente estratto ( HEADo --ours) o il ramo che stiamo unendo ( origin/feature1o --theirs) è una modifica più semplice da applicare. L'uso di diff con il punto triplo ( git diff a...b) mostra i cambiamenti avvenuti bdall'ultima divergenza ao, in altre parole, confrontare l'antenato comune di aeb con b.

    git diff HEAD...origin/feature1 -- lib/message.rb # show the change in feature1
    git diff origin/feature1...HEAD -- lib/message.rb # show the change in our branch
    
  2. Scopri la versione più complicata del file. Ciò rimuoverà tutti gli indicatori di conflitto e utilizzerà la parte scelta.

    git checkout --ours -- lib/message.rb   # if our branch's change is more complicated
    git checkout --theirs -- lib/message.rb # if origin/feature1's change is more complicated
    
  3. Con la modifica complicata verificata, apri la differenza della modifica più semplice (vedi passaggio 1). Applica ogni modifica da questa diff al file in conflitto.


4
In che modo la fusione di tutti i conflitti in una sola volta funzionerebbe meglio di quanto i singoli impegni? Ho già problemi a fondere singoli commit (specialmente da persone che non li dividono in porzioni logiche E forniscono test sufficienti per la verifica). Inoltre, rebase non è peggio che unire quando si tratta di opzioni di backup, l'uso intelligente di rebase interattivo e strumenti come tortoisegit (che consente la selezione di quali commit includere) aiuteranno molto.
prusswan,

8
Mi sento come se avessi affrontato il motivo in # 1. Se i singoli commit non sono logicamente coerenti, una ragione in più per unire il ramo logicamente coerente, in modo da poter effettivamente dare un senso al conflitto. Se il commit 1 è errato e il commit 2 lo risolve, la fusione del commit 1 sarà fonte di confusione. Ci sono motivi legittimi per cui potresti avere 15 conflitti di fila, come quello che ho descritto sopra. Anche il tuo argomento per il ribasso non è in qualche modo infondato. Rebase mescola le cattive fusioni nelle commit originali buone e non lascia le commit buone per farti riprovare. Unisci.
Edward Anderson,

6
Sono completamente d'accordo con te nilbus. Ottimo post; che chiarisce alcune cose. Mi chiedo se rerere sarebbe di aiuto qui. Inoltre, grazie per il suggerimento sull'uso di diff3, lo accenderò sicuramente adesso.
derick,

45
+1 per avermi raccontato della sola diff3 - quanto spesso guardava un conflitto incomprensibile che maledisse chiunque fosse responsabile di non dirmi cosa aveva da dire l'antenato comune. Grazie mille.
Giovanni

4
Questa avrebbe dovuto essere la risposta accettata. Il flusso di lavoro rebase è anche orribile perché nasconde il fatto che ci fosse un'enorme divergenza nella base di codice a un certo punto nel tempo, che potrebbe essere utile sapere se vuoi capire come è stato scritto il codice che stai guardando. Solo i piccoli rami che non sono in conflitto dovrebbero essere riassegnati al maestro.
Robert Rüger,

32

Nel mio flusso di lavoro, rifaccio il più possibile (e provo a farlo spesso. Non lasciare che le discrepanze si accumulino drasticamente riduce la quantità e la gravità delle collisioni tra i rami).

Tuttavia, anche in un flusso di lavoro principalmente basato su rebase, esiste un posto per le fusioni.

Ricordiamo che l'unione crea effettivamente un nodo che ha due genitori. Consideriamo ora la seguente situazione: ho due marchi indipendenti di funzionalità A e B, e ora voglio sviluppare elementi sul ramo di funzionalità C che dipende sia da A che da B, mentre A e B vengono sottoposti a revisione.

Quello che faccio quindi è il seguente:

  1. Crea (e verifica) il ramo C sopra A.
  2. Uniscilo con B

Ora il ramo C include modifiche sia da A che da B, e posso continuare a svilupparle. Se faccio qualche modifica ad A, ricostruisco il grafico dei rami nel modo seguente:

  1. crea il ramo T nella nuova cima di A
  2. unire T con B
  3. rifare C su T
  4. cancella ramo T

In questo modo posso effettivamente mantenere grafici arbitrari di rami, ma fare qualcosa di più complesso della situazione sopra descritta è già troppo complesso, dato che non esiste uno strumento automatico per fare il rebasing quando il genitore cambia.


1
Potresti ottenere lo stesso con solo ribattezze. La fusione non è in realtà necessaria qui (tranne se non si desidera duplicare i commit - ma difficilmente lo vedo come argomento).
odwl

1
In effetti non voglio duplicare gli commit. Vorrei mantenere la struttura in volo del mio lavoro il più pulita possibile. Ma questa è una questione di gusti personali e non è necessariamente adatta a tutti.
Alex Gontmakher,

Sono d'accordo al 100% con il primo paragrafo. (La risposta di @ Edward funziona dove non è così, ma preferirei che tutti i progetti al mondo funzionino come suggerisci tu). Il resto della risposta sembra un po 'inverosimile nel senso che lavorare su C mentre A e B sono in corso è già piuttosto rischioso (almeno nella misura in cui dipende davvero da A e B), e anche alla fine tu probabilmente non manterrebbe le fusioni (la C verrebbe ridisegnata in cima all'ultima e alla più grande).
Alois Mahdal,

23

NON utilizzare git push origin --mirror SOTTO QUASI QUALUNQUE CIRCOSTANZA.

Non ti chiede se sei sicuro di volerlo fare, e faresti meglio ad esserlo, perché cancellerà tutti i tuoi rami remoti che non si trovano nella tua casella locale.

http://twitter.com/dysinger/status/1273652486


6
O non fare cose che non sei sicuro di quale sarà il risultato? Nel Instructions to this machine may lead to unintended consequences, loss of work/data, or even death (at the hands of the sysad). Remember that you are solely responsible for the consequences of your actions MOTD c'era una macchina che usavo come amministratore.
Richo,

usalo se hai un repository con mirroring (anche se nel mio caso ora viene eseguito da un utente speciale nel repository di origine con l'hook post-ricezione)
prusswan,

14

Ho una domanda dopo aver letto la tua spiegazione: potrebbe essere che tu non l'abbia mai fatto

git checkout master
git pull origin
git checkout my_new_feature

prima di fare il 'git rebase / merge master' nel tuo ramo delle caratteristiche?

Perché il tuo ramo principale non si aggiorna automaticamente dal repository del tuo amico. Devi farlo con il git pull origin. Cioè forse ti rifaresti sempre da un ramo master locale in continua evoluzione? E poi arriva il momento push, stai spingendo in un repository che ha impegni (locali) che non hai mai visto e quindi la spinta fallisce.


13

Nella tua situazione penso che il tuo partner sia corretto. La cosa bella del rebasing è che per gli estranei le tue modifiche sembrano essere avvenute tutte in una sequenza pulita da sole. Questo significa

  • le tue modifiche sono molto facili da rivedere
  • puoi continuare a fare piccoli e piacevoli commit e tuttavia puoi rendere pubblici tutti i set di tali commit (unendoti al master) contemporaneamente
  • quando guardi il ramo principale pubblico vedrai diverse serie di commit per diverse funzionalità da parte di sviluppatori diversi ma non saranno tutti mescolati

Puoi continuare a inviare il tuo ramo di sviluppo privato al repository remoto per motivi di backup, ma altri non dovrebbero considerarlo come un ramo "pubblico" dal momento che eseguirai il rebasing. A proposito, un comando facile per farlo è git push --mirror origin.

L'articolo Il software di packaging che utilizza Git fa un lavoro abbastanza carino che spiega i compromessi tra fusione e rebasing. È un contesto un po 'diverso ma i principi sono gli stessi - in pratica dipende dal fatto che le filiali siano pubbliche o private e da come intendi integrarle nella linea principale.


1
Il collegamento al software di packaging che utilizza git non funziona più. Non sono riuscito a trovare un buon link per modificare la risposta originale.
Chetan,

Non dovresti eseguire il mirroring origin, dovresti eseguire il mirroring su un terzo repository di backup dedicato.
Miral,

12

Ad ogni modo, stavo seguendo il mio flusso di lavoro su un ramo recente e quando ho provato a fonderlo con il master, tutto è andato all'inferno. Ci sono stati molti conflitti con cose che non avrebbero dovuto avere importanza. I conflitti non avevano senso per me. Mi ci è voluto un giorno per sistemare tutto, e alla fine è culminato in una spinta forzata al master remoto, dal momento che il mio master locale ha risolto tutti i conflitti, ma quello remoto non era ancora felice.

Né nei flussi di lavoro suggeriti dal partner né nei flussi di lavoro suggeriti si sarebbero verificati conflitti che non avevano senso. Anche se lo hai fatto, se stai seguendo i flussi di lavoro suggeriti, dopo la risoluzione non dovrebbe essere richiesta una spinta 'forzata'. Suggerisce che non hai effettivamente unito il ramo a cui stavi spingendo, ma hai dovuto spingere un ramo che non era un discendente della punta remota.

Penso che devi guardare attentamente cosa è successo. Qualcun altro potrebbe aver (deliberatamente o meno) riavvolto il ramo principale remoto tra la creazione del ramo locale e il punto in cui hai tentato di fonderlo nel ramo locale?

Rispetto a molti altri sistemi di controllo della versione, ho scoperto che l'uso di Git comporta una lotta minore allo strumento e consente di lavorare sui problemi fondamentali per i flussi di origine. Git non esegue la magia, quindi cambiamenti in conflitto causano conflitti, ma dovrebbe rendere più semplice fare la cosa di scrittura monitorando la parentela di commit.


Stai insinuando che il PO ha qualche ribasso o errore non scoperto nel suo processo, giusto?
krosenvold,

8

"Anche se sei un singolo sviluppatore con solo pochi rami, vale la pena prendere l'abitudine di usare rebase e fondersi correttamente. Il modello di lavoro di base sarà simile a:

  • Crea un nuovo ramo B dal ramo A esistente

  • Aggiungi / esegui il commit delle modifiche nel ramo B

  • Sostituisci gli aggiornamenti dal ramo A

  • Unisci le modifiche dal ramo B al ramo A "

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/


7

Da quello che ho osservato, git merge tende a mantenere separati i rami anche dopo l'unione, mentre rebase quindi si fonde li combina in un singolo ramo. Il secondo risulta molto più pulito, mentre nel primo sarebbe più facile scoprire quali commit appartengono a quale ramo anche dopo la fusione.


4

Con Git non esiste un flusso di lavoro "corretto". Usa qualunque cosa galleggi la tua barca. Tuttavia, se si verificano costantemente conflitti quando si uniscono le filiali, forse è necessario coordinare meglio i propri sforzi con gli altri sviluppatori? Sembra che voi due continuiate a modificare gli stessi file. Inoltre, fai attenzione alle parole chiave spazi bianchi e sovversione (ad esempio, "$ Id $" e altri).


0

Uso solo il flusso di lavoro rebase, perché è visivamente più chiaro (non solo in GitKraken, ma anche in Intellij e in gitk, ma raccomando di più il primo): hai un ramo, ha origine dal master e torna al master . Quando il diagramma è pulito e bello, saprai che nulla va all'inferno, mai .

inserisci qui la descrizione dell'immagine

Il mio flusso di lavoro è quasi lo stesso del tuo, ma con solo una piccola differenza: mi squashimpegno in uno nel mio ramo locale prima del rebasemio ramo sulle ultime modifiche master, perché:

rebasefunziona sulla base di ciascun commit

il che significa che se hai 15 commit che cambiano la stessa linea master, devi controllare 15 volte se non schiacci, ma ciò che conta è il risultato finale, giusto?

Quindi, l'intero flusso di lavoro è:

  1. Fai il checkout mastere tira per assicurarti di avere l'ultima versione

  2. Da lì, crea un nuovo ramo

  3. Fai il tuo lavoro lì, puoi impegnarti liberamente più volte e spingere in remoto, senza preoccupazioni, perché è il tuo ramo.

  4. Se qualcuno ti dice "hey, il mio PR / MR è approvato, ora è unito al master", puoi recuperarli / estrarli. Puoi farlo in qualsiasi momento o al passaggio 6.

  5. Dopo aver fatto tutto il tuo lavoro, commettili e, se hai diversi commit, schiacciali (sono tutti i tuoi lavori e quante volte cambi una riga di codice non importa; l'unica cosa importante è la versione finale). Spingilo o no, non importa.

  6. Fai il checkout a master, di pullnuovo per assicurarti di avere le ultime novità masterin locale. Il diagramma dovrebbe essere simile a questo:

inserisci qui la descrizione dell'immagine

Come puoi vedere, sei nella tua filiale locale, che ha origine da uno stato obsoleto master, mentre master(sia locale che remoto) si è spostato in avanti con le modifiche del tuo collega.

  1. Torna alla tua filiale e torna al master. Ora avrai un solo commit, quindi risolvi i conflitti solo una volta . (E in GitKraken, devi solo trascinare il tuo ramo su mastere scegliere "Rebase"; un altro motivo per cui mi piace.) Dopodiché, sarai piace:

inserisci qui la descrizione dell'immagine

  1. Quindi ora hai tutte le modifiche più recenti master, combinate con le modifiche sul tuo ramo. Ora puoi spingere sul tuo telecomando e, se hai già spinto prima, dovrai forzare la spinta; Git ti dirà che non puoi semplicemente avanzare rapidamente. È normale, a causa del rebase, hai cambiato il punto iniziale del tuo ramo. Ma non dovresti temere: usa la forza, saggiamente . Alla fine, il telecomando è anche il tuo ramo, quindi non influisci masteranche se fai qualcosa di sbagliato.

  2. Crea PR / MR e attendi fino all'approvazione, quindi masteravrai il tuo contributo. Congratulazioni! Quindi ora puoi effettuare il checkout master, estrarre le modifiche ed eliminare il tuo ramo locale per ripulire il diagramma. Anche il ramo remoto deve essere eliminato, se ciò non viene fatto quando lo si unisce al master.

Lo schema finale è di nuovo pulito e chiaro:

inserisci qui la descrizione dell'immagine

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.