Lezione 21 · Storia e integrazione
Merge versus rebase: scegli con un grafico
Esegui l'unione e il rebase da due copie identiche della stessa divergenza, confronta i due grafici, i genitori e gli alberi dei file e impara l'unica regola che decide tra loro: chi altro ha già i tuoi commit.
- Livello: Intermedio
- 10 minuti di lettura
- 6 minuti di video
- Aggiornato
- Prima di questa lezione: Unisci rami con cronologia divergente, Comprendere le unioni veloci, Ribasare il lavoro inedito su una nuova base
Avanzamenti salvati in questo browser.
La riproduzione carica il video da YouTube (modalità ottimizzata per la privacy).
Cosa imparerai
- Inizia l'unione e il rebase da copie identiche, quindi il confronto è giusto.
- Legge i due genitori di un commit di unione e il nuovo genitore singolo di un commit ribasato.
- Spiega perché due copie possono terminare con gli stessi file e avere comunque storie diverse.
- Scegli con una regola: riscrivi solo i commit che nessun altro ha.
Prima di iniziare
Hai bisogno di due copie separate della divergenza della lezione 20, denominate gittree-merge e gittree-rebase. In ogni copia il ramo feature/signup-form contiene uno dei tuoi commit (chiamiamolo S), il ramo updated-main contiene un commit diverso (chiamiamolo N), entrambi iniziati dallo stesso commit M e rescue/signup-before-rebase punta a S. Il ramo dell'argomento è stato estratto e l'albero di lavoro è pulito. Non creare la seconda copia dal risultato della prima.
Qui tutto è locale e con il piano gratuito (GitTree ti chiede di accedere): nessun account di hosting, nessun provider, niente Internet. Ogni identità è fittizia (esempio.com) e non dovresti mai eseguire questa lezione all'interno di un progetto reale.
Apporta le modifiche in GitTree o con i comandi Git nativi, non entrambi nella stessa copia. Leggere il risultato successivamente è sempre sicuro. Le etichette dei menu provengono dalle stringhe proprie di GitTree 2.0; i comandi nativi sono stati eseguiti con Git 2.43 in copie usa e getta. I passaggi GitTree non sono stati eseguiti su macOS, Windows o Linux; le scorciatoie dell'app utilizzano Comando su macOS dove questa pagina scrive Ctrl.
Effetti del concetto e dello stato
Un confronto è giusto solo quando entrambe le copie iniziano dagli stessi riferimenti e dagli stessi file. Se prima unisci e poi ribasi il risultato, stai ponendo una domanda diversa: il replay ora ha un commit di unione nel suo intervallo.
Merge mantiene entrambe le linee di lavoro. Aggiunge un nuovo commit, K, con due genitori: prima S, poi N. S e N mantengono i loro ID.
Rebase riproduce il tuo commit sopra N. Il risultato, S′, ha la stessa modifica di S ma un nuovo genitore e l'ID di un commit include il suo genitore, quindi S′ ha un nuovo ID. La vecchia S sopravvive solo perché la filiale di soccorso la indica ancora.
Quando nulla è in conflitto, entrambe le copie terminano con lo stesso albero di file. Alberi uguali non significano storia uguale: un grafico mantiene un diamante, l'altro una linea retta e gli ID di commit differiscono.
Quale sia quello giusto non è una questione di gusti. Se altre persone hanno già i tuoi commit, riprodurli interrompe le loro copie, quindi unisci. Se i commit sono privati, un rebase è sicuro e fornisce una cronologia lineare se il tuo team ne preferisce una. Né migliora il codice.
In ogni copia si sposta solo il ramo dell'argomento. Il server, updated-main e quello principale originale non vengono toccati.
Preparare lo stato esatto del progetto
Fai una divergenza, poi copiala due volte. Inizia da una copia ordinaria separata del progetto così come lo ha lasciato la lezione 18 (il principale è il merge commit M). Lascia intatto il tuo gittree-features originale e non eseguirlo mai in un checkout di origine. L'identità demo è GitTree Demo, demo@example.com, con la firma disattivata solo in questo repository.
Completare gli input del file
Risparmia solo nella fase denominata di seguito. Questi sono corpi UTF-8/LF completi con un ritorno a capo finale; non incollare mai le recinzioni Markdown. I file eseguibili non modificati ereditano l'input denominato. Le versioni in varianti separate non devono sovrascrivere l'originale.
index.html — ISCRIVITI
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GitTree Features — course demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<a href="#main">GitTree Features</a>
</header>
<main id="main" tabindex="-1">
<section class="hero" aria-labelledby="hero-title">
<p class="eyebrow">GitTree Learn demo project</p>
<h1 id="hero-title">See your work clearly.</h1>
<p id="hero-copy">Choose one change, then check what Git saved.</p>
</section>
<section class="features" aria-labelledby="features-title">
<h2 id="features-title">Follow your work</h2>
<div class="cards">
<article class="feature-card">
<h3>History</h3>
<p>Read commits and branches together.</p>
</article>
<article class="feature-card">
<h3>Changes</h3>
<p>Review the exact content selected for a commit.</p>
</article>
<article class="feature-card">
<h3>Recovery</h3>
<p>Inspect the scope of a recovery point before using it.</p>
</article>
</div>
</section>
<section aria-labelledby="plans-title">
<h2 id="plans-title">Choose the workflow you need</h2>
<div class="cards">
<article class="feature-card">
<h3>Local Git</h3>
<p>Local Git workflows work without signing in.</p>
</article>
<article class="feature-card">
<h3>Connected workflows</h3>
<p>Provider integrations and Nour require Pro or an applicable trial.</p>
</article>
</div>
</section>
<section aria-labelledby="signup-title">
<h2 id="signup-title">Try the demo form</h2>
<form id="signup-form" novalidate>
<label for="demo-email">Demo email</label>
<input id="demo-email" name="email" type="email" autocomplete="off" aria-describedby="signup-hint">
<p id="signup-hint">Use a fictional example.com address. Nothing is sent.</p>
<button type="submit">Check the address locally</button>
<p id="signup-result" role="status"></p>
</form>
</section>
</main>
<footer><p>Built by a fictional team for GitTree Learn. Review one change at a time.</p></footer>
<script type="module" src="site.js"></script>
</body>
</html>docs/integration-note.md — DOC_MERGE
# Integration review
This note was written on main while signup work was private.Tra GitTree
Apri la tavolozza con Comando+Maiusc+P (Ctrl+Maiusc+P su Ubuntu), digita il nome esatto del comando e selezionalo. Le azioni su un ramo con nome utilizzano il menu di scelta rapida di quel ramo.
- Apri gittree-merge. Quindi scegli New Tab e apri gittree-rebase dal suo elenco Recent. Ogni scheda è intitolata con il nome della cartella, quindi puoi distinguerle.
- Nella scheda gittree-merge mantieni
feature/signup-formcontrollato. Fai clic con il pulsante destro del mouse suupdated-maine scegli Merge updated-main into feature/signup-form. - Sotto Record it as scegli Always a merge commit, quindi scegli Merge una volta. Il nuovo commit ha due genitori: prima S, poi N.
- Passa alla scheda gittree-rebase. È ancora a S e N, esattamente come l'hai salvato. Non utilizzare la copia unita.
- Fai clic con il pulsante destro del mouse su
updated-maine scegli Rebase feature/signup-form onto updated-main. Leggi Options for this rebase e lascia entrambe le opzioni disattivate, quindi scegli Rebase feature/signup-form una volta. - Seleziona il commit riprodotto. Il suo genitore è N e il suo ID è nuovo. La vecchia S porta ancora il nome
rescue/signup-before-rebase. - In ogni scheda apri Show Files →
index.htmledocs/integration-note.md. Sono la stessa pagina e la stessa nota in entrambe le copie. Quindi ritorna con Back to Graph. - Confronta i due grafici, quindi indica quale regola decide la scelta per questo ramo prima di scegliere un risultato. Conserva entrambe le copie così come sono.
Per ogni commit ordinario nel corso: Summary è l'oggetto dichiarato, Description è vuoto e le opzioni Amend Previous Commit, Skip Hooks, Add Signed-off-by, Different Author, Push After Commit e Allow Empty Commit rimangono disattivate. Questa lezione non prevede un commit ordinario.
Nel terminale
Costruisci la divergenza in una copia separata e pulita del risultato della lezione 18 (il lavoro che hai svolto nell'app, scritto come comandi nativi):
git switch --create feature/signup-formSalva l'intera pagina di ISCRIZIONE come index.html, quindi:
git add -- index.html
git commit --quiet -m "feat: add the local signup demonstration"
git branch rescue/signup-before-rebase
BASE_SHA="$(git rev-parse HEAD~1)"
git switch --quiet --create updated-main "$BASE_SHA"Il giorno updated-main salva l'intera nota DOC_MERGE come docs/integration-note.md, quindi:
git add -- docs/integration-note.md
git commit --quiet -m "docs: add the integration review"
git switch --quiet feature/signup-formBASE_SHA è il commit M da cui crescono entrambe le linee. Ora crea le due copie indipendenti da questo stato esatto (cd .. && cp -R gittree-features gittree-merge && cp -R gittree-features gittree-rebase). Nella copia unita:
git merge --quiet --no-ff --no-edit updated-main
git rev-list --parents -n 1 HEAD
git rev-parse 'HEAD^{tree}'
git status --shortNella copia rebase intatta:
git rebase --quiet --no-autostash --no-update-refs --no-autosquash updated-main
git rev-list --parents -n 1 HEAD
git rev-parse 'HEAD^{tree}'
git status --shortgit rev-list --parents -n 1 HEAD stampa l'ID del commit seguito dai suoi genitori: tre ID nella copia di unione, due nella copia di rebase. Le due righe HEAD^{tree} hanno lo stesso ID e git status --short non stampa nulla in entrambe. I due ID commit HEAD sono diversi. I tuoi ID saranno diversi da quelli di chiunque altro; confronta i tuoi tra loro, non con questa pagina.
Se l'argomento non avesse un commit proprio e updated-main fosse semplicemente in vantaggio rispetto a esso, git merge avanzerebbe velocemente per impostazione predefinita e non creerebbe alcun commit di unione. Questo è un input diverso e un risultato diverso; sceglilo apposta, non fingere mai una fusione tra due genitori.
Risultato
Where you end: la copia unificata ha un nuovo commit K con genitori S e N; la copia rebase ha S′ con genitore N, e la vecchia S è conservata dal ramo di salvataggio. Gli alberi dei file sono uguali, entrambi gli stati sono puliti e quello principale originale, M e il server non sono stati modificati.
Errori comuni
- Supponendo che un grafico lineare significhi che il vecchio ID di commit è sopravvissuto. Un rebase crea sempre nuovi commit.
- Avvio del rebase dal risultato dell'unione. Questo è un esperimento diverso e la riproduzione può includere il commit dell'unione.
- Confrontando i due ID HEAD e aspettandosi che siano d'accordo. Confronta invece gli alberi.
- Ripetendo l'unione o il rebase dopo che è già terminato.
Se qualcosa va storto
Non scartare nessuno dei risultati per forzarli a corrispondere. Se gli alberi differiscono, confronta i riferimenti iniziali, qualsiasi risoluzione dei conflitti e qualsiasi commit aggiuntivo, quindi ricomincia dalla divergenza salvata. Un ramo di salvataggio conserva solo la cronologia impegnata; non mantiene i file non salvati.
Controlla la tua comprensione
Rispondi a ciascuna domanda, quindi leggi il motivo. Le domande contrassegnate come "Obbligatorio" contano ai fini del completamento della lezione.
0 di 6 corretto
Rispondi correttamente alle domande richieste per completare la lezione.
Controllato in questo browser.
Pratica
Riporta i due elenchi principali, i due ID HEAD e i due ID dell'albero. Spiegare perché il risultato atteso è ID di albero uguali accanto a ID HEAD diversi. Quindi prevedere, senza farlo, cosa fa un'unione predefinita quando l'argomento non ha un proprio commit.
La tua conferma: il sito non può vedere il tuo computer.
Per completare la lezione rispondere correttamente alle domande richieste e confermare l'esercizio pratico.
Riepilogo
- Stesso inizio, storia diversa.
- Alberi uguali non significano storia uguale.
- Unisci quando gli altri hanno i tuoi commit. Rebase solo quelli privati.
Relativo
Termini del glossario introdotti
Rebase — إعادة تأسيس; ripetere: إعادة تطبيق; ramo di salvataggio — فرع حفظ.
Trascrizione video
La narrazione del video, spunto per spunto.
Mostra la trascrizione
- Unire o riorganizzare? Lo stesso lavoro può andarsene due storie diverse.
- Correremo entrambi dallo stesso inizio puntare e confrontare.
- Il tuo ramo ha un commit. L'aggiornato il ramo ne ha un altro.
- Entrambi sono cresciuti dallo stesso impegno, quindi nessuno dei due contiene l'altro.
- Per prima cosa prepara la divergenza e creane due copie identiche.
- Un test corretto richiede gli stessi riferimenti e file in entrambe le copie.
- Ecco la prima copia. Ci uniremo questo.
- E questa è la seconda copia. Lo teniamo intatto per il rebase.
- Fare clic con il pulsante destro del mouse su aggiornato-principale e sceglierlo Unisci comando.
- In Registra come scegli Sempre un'unione impegnarsi.
- Scegli Unisci una volta e attendi il vero risultato.
- Il nuovo commit ha due genitori: your commit, quindi aggiornato-main.
- Ora la seconda copia. Ha ancora i due impegni originali.
- Fare di nuovo clic con il pulsante destro del mouse su aggiornato-principale e scegliere Ribase questa volta.
- La finestra di dialogo nomina il ramo e il file bersaglio. Lascia entrambe le opzioni disattivate.
- Ribasare una volta. Git riproduce il tuo commit top di aggiornato-main.
- Il commit riprodotto ha un nuovo genitore, quindi ottiene un nuovo ID.
- Il vecchio impegno non è andato perduto: il salvataggio il ramo lo indica ancora.
- Ora confronta i file. La pagina è la stesso in entrambe le copie.
- Così è la nota di integrazione. Stessi file, storia diversa.
- Guarda i due grafici: un diamante con a unisci commit e una linea retta.
- Quindi quale dovresti usare? Chiedi a chi altro ha i tuoi impegni.
- Se altri li hanno, uniscili. Un rebase lo farebbe riscrivere la loro storia.
- Se i commit sono privati, rebase lo è sicuro e dà una linea retta.
- Né migliora il tuo codice. La regola riguarda la proprietà.
- Ora dimostralo nel terminale. Innanzitutto, il genitori di ogni punta.
- Tre ID per la copia unificata: il commit, e i suoi due genitori.
- Due ID per la copia rebase: il commit, ed è un genitore.
- Poi gli alberi dei file: un ID, stampato due volte. Stessi file.
- Ma i due ID di commit differiscono. Uguale alberi, storie diverse.
- Cambia l'input: supponi che il tuo ramo avesse nessun impegno proprio.
- Un'unione predefinita avanzerebbe quindi rapidamente, senza commit di unione.
- Non riprogrammare mai un ramo di qualcun altro ha già.
- Se gli alberi differiscono, confronta l'inizio prima gli arbitri.
- Non eliminare un risultato per forzarne i due partita.
- Pratica: segnalare i genitori, i documenti d'identità e gli alberi dalle tue copie.
- Successivamente, riordina, schiaccia e riformula il tuo impegni locali.