Git 2.18 (Q2 2018) migliorerà considerevolmente l' --preserve-merge
opzione aggiungendo una nuova opzione.
" git rebase
" imparato " --rebase-merges
" per trapiantare l'intera topologia del grafico di commit altrove .
(Nota: Git 2.22, Q2 2019, in realtà si deprezza --preserve-merge
, e Git 2.25, Q1 2020, smette di pubblicizzarlo git rebase --help
nell'output " " )
Vedere commettere 25cff9f , commettere 7543f6f , commettere 1131ec9 , commettere 7ccdf65 , commettere 537e7d6 , commettere a9be29c , commettere 8f6aed7 , commettere 1644c73 , commettere d1e8b01 , commettere 4c68e7d , commettere 9055e40 , commettere cb5206e , commettere a01c2a5 , commettere 2f6b1d1 , commettere bf5c057 (25 Apr 2018) di Johannes Schindelin ( dscho
) .
Vedi commit f431d73 (25 apr 2018) di Stefan Beller ( stefanbeller
) .
Vedi commit 2429335 (25 aprile 2018) di Phillip Wood ( phillipwood
) .
(Unita da Junio C Hamano - gitster
- in commit 2c18e6a , 23 maggio 2018)
pull
: accetta --rebase-merges
di ricreare la topologia del ramo
Simile alla preserve
modalità che passa semplicemente l' --preserve-merges
opzione al rebase
comando, la merges
modalità passa semplicemente l'
--rebase-merges
opzione.
Ciò consentirà agli utenti di riformulare convenientemente topologie di commit non banali quando eseguono il pull di nuove commit, senza appiattirle.
git rebase
La pagina man ora ha una sezione completa dedicata alla riassegnazione della storia con le fusioni .
Estratto:
Esistono motivi legittimi per cui uno sviluppatore potrebbe voler ricreare i commit di merge: mantenere la struttura del ramo (o "commettere topologia") quando si lavora su più rami collegati.
Nell'esempio seguente, lo sviluppatore lavora su un ramo di argomento che riformula la modalità di definizione dei pulsanti e su un altro ramo di argomento che utilizza tale refactoring per implementare un pulsante "Segnala un bug".
L'output di git log --graph --format=%s -5
potrebbe essere simile al seguente:
* Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one
Lo sviluppatore potrebbe voler riassegnare questi commit a un più nuovo master
mantenendo la topologia del ramo, ad esempio quando si prevede che il primo ramo dell'argomento sarà integrato in master
molto prima del secondo, per esempio, per risolvere i conflitti di unione con le modifiche alla
DownloadButton
classe che ha reso in master
.
Questo rebase può essere eseguito usando l' --rebase-merges
opzione.
Vedi commit 1644c73 per un piccolo esempio:
rebase-helper
--make-script
: introduce una bandiera per rifare le fusioni
Il sequencer ha appena appreso nuovi comandi destinati a ricreare la struttura del ramo ( simile nello spirito a --preserve-merges
, ma con un design sostanzialmente meno rotto ).
Consentiamo di rebase--helper
generare liste di todo facendo uso di questi comandi, attivati dalla nuova --rebase-merges
opzione.
Per una topologia di commit come questa (dove HEAD punta a C):
- A - B - C (HEAD)
\ /
D
l'elenco di todo generato sarebbe simile al seguente:
# branch D
pick 0123 A
label branch-point
pick 1234 D
label D
reset branch-point
pick 2345 B
merge -C 3456 D # C
Qual è la differenza con --preserve-merge
?
Commit 8f6aed7 spiega:
C'era una volta questo sviluppatore che pensava: non sarebbe bello se, diciamo, le patch di Git per Windows sulla parte superiore del core Git potessero essere rappresentate come un boschetto di rami ed essere ridisegnate sulla parte superiore del core Git per mantenere una serie selezionabile di serie di patch?
Il tentativo originale di rispondere a questa era: git rebase --preserve-merges
.
Tuttavia, quell'esperimento non è mai stato inteso come un'opzione interattiva e si è solo appoggiato git rebase --interactive
perché l'implementazione di quel comando sembrava già molto, molto familiare: è stata progettata dalla stessa persona che ha progettato --preserve-merges
: il tuo veramente.
E per "il tuo vero", l'autore si riferisce a se stesso: Johannes Schindelin ( dscho
) , che è la ragione principale (con alcuni altri eroi - Hannes, Steffen, Sebastian, ...) che abbiamo Git For Windows (anche se indietro nel tempo - 2009 - non è stato facile ).
Lavora in Microsoft da settembre 2015 , il che ha senso considerando che Microsoft ora utilizza pesantemente Git e ha bisogno dei suoi servizi.
Quella tendenza è iniziata nel 2013 in realtà, con TFS . Da allora, Microsoft gestisce il più grande repository Git del pianeta ! E, dall'ottobre 2018, Microsoft ha acquisito GitHub .
Puoi vedere Johannes parlare in questo video per Git Merge 2018 nell'aprile 2018.
Qualche tempo dopo, qualche altro sviluppatore (ti sto guardando, Andreas! ;-)) ha deciso che sarebbe una buona idea consentire --preserve-merges
di combinare --interactive
(con avvertenze!) E il manutentore Git (beh, il manutentore temporaneo di Git durante l'assenza di Junio, cioè) d'accordo, e fu allora che il glamour del --preserve-merges
design iniziò a sfaldarsi piuttosto rapidamente e senza afflizione.
Qui Jonathan parla di Andreas Schwab di Suse.
Puoi vedere alcune delle loro discussioni nel 2012 .
La ragione? Nella --preserve-merges
modalità, i genitori di un commit di unione (o del resto, di qualsiasi commit) non venivano dichiarati esplicitamente, ma erano
impliciti dal nome del commit passato al pick
comando .
Ciò ha reso impossibile, ad esempio, riordinare i commit .
Per non parlare di spostare gli impegni tra le filiali o, vietare la divinità, di dividere in due le sezioni dell'argomento.
Purtroppo, queste carenze hanno anche impedito a quella modalità (il cui scopo originale era soddisfare le esigenze di Git per Windows, con l'ulteriore speranza che potesse essere utile anche ad altri) dal soddisfare le esigenze di Git per Windows.
Cinque anni dopo, quando divenne davvero insostenibile avere una serie di patch hodge-podge ingombranti e ingombranti di patch parzialmente correlate, parzialmente non correlate in Git per Windows che di tanto in tanto venivano ridisegnate sui tag core di Git (guadagnando l'ira immeritata dello sviluppatore della git-remote-hg
serie sfortunata
che per prima ha obsoleto l'approccio concorrenziale di Git per Windows, per poi essere abbandonato senza un manutentore in seguito) era davvero insostenibile, nacquero le " cesoie da giardino Git " : una sceneggiatura, appoggiata sulle spalle del rebase interattivo, che determinerebbe dapprima la topologia del ramo delle patch da rielaborare, creerebbe un elenco di pseudo todo per ulteriori modifiche, trasformerebbe il risultato in un vero elenco di todo (facendo un uso pesante delexec
per "implementare" i comandi mancanti dell'elenco todo) e infine ricreare le serie di patch in cima al nuovo commit di base.
(Lo script di cesoie Git Garden fa riferimento a questa patch in commit 9055e40 )
Era il 2013.
E ci sono volute circa tre settimane per elaborare il progetto e implementarlo come uno script fuori dall'albero. Inutile dire che l'implementazione ha richiesto alcuni anni per stabilizzarsi, mentre il design stesso si è dimostrato solido.
Con questa patch, la bontà delle cesoie da giardino Git arriva a git
rebase -i
se stessa .
Passare l' --rebase-merges
opzione genererà un elenco di cose da fare che può essere compreso facilmente e dove è ovvio come riordinare i commit .
Nuovi rami possono essere introdotti inserendo label
comandi e chiamando merge <label>
.
E una volta che questa modalità sarà diventata stabile e universalmente accettata, possiamo deprecare l'errore di progettazione che è stato--preserve-merges
.
Git 2.19 (Q3 2018) migliora la nuova --rebase-merges
opzione facendola funzionare --exec
.
L '" --exec
" opzione per " git rebase --rebase-merges
" ha posto i comandi exec in punti sbagliati, che è stato corretto.
Vedi commit 1ace63b (09 agosto 2018) e commit f0880f7 (06 agosto 2018) di Johannes Schindelin ( dscho
) .
(Unito da Junio C Hamano - gitster
- in commit 750eb11 , 20 ago 2018)
rebase --exec
: fallo funzionare con --rebase-merges
L'idea --exec
è di aggiungere una exec
chiamata dopo ciascuna pick
.
Dall'introduzione di fixup!
/ s quash!
commit, questa idea è stata estesa per applicarsi a "pick, eventualmente seguito da una catena fixup / squash", cioè un exec non verrebbe inserito tra una pick
e nessuna delle sue corrispondenti
fixup
o squash
linee.
L'implementazione corrente usa un trucco sporco per raggiungere questo obiettivo: presuppone che ci siano solo comandi pick / fixup / squash, quindi
inserisce le exec
righe prima di qualsiasi pick
tranne la prima e ne aggiunge una finale.
Con i todo list generati da git rebase --rebase-merges
questa semplice spettacoli di implementazione i suoi problemi: produce la cosa esatta sbagliato quando ci sono label
, reset
e merge
comandi.
Modifichiamo l'implementazione per fare esattamente ciò che vogliamo: cercare le
pick
linee, saltare le catene di correzione / squash e quindi inserire la exec
linea . Raccogliere, sciacquare, ripetere.
Nota: facciamo tutto il possibile per inserire prima delle righe di commento ogni volta che è possibile, poiché i commit vuoti sono rappresentati dalle righe di pick commentate (e vogliamo inserire una riga exec di una pick precedente prima di tale riga, non successivamente).
Mentre ci sei , aggiungi anche exec
righe dopo i merge
comandi, perché sono simili nello spirito ai pick
comandi: aggiungono nuovi commit.
Git 2.22 (Q2 2019) corregge l'uso della refs / rewritten / gerarchy per memorizzare uno stato intermedio rebase, il che rende intrinsecamente la gerarchia per worktree.
Vedi commit b9317d5 , commit 90d31ff , commit 09e6564 (07 mar 2019) di Nguyễn Thái Ngọc Duy ( pclouds
) .
(Unita da Junio C Hamano - gitster
- in commit 917f2cd , 09 apr 2019)
Assicurati che refs / rewritten / sia per-worktree
a9be29c (sequencer: make refs generato dal label
comando worktree-local, 25-04-2018, Git 2.19) aggiunge refs/rewritten/
come spazio di riferimento per ogni worktree.
Sfortunatamente (il mio male) ci sono un paio di posti che devono essere aggiornati per assicurarsi che sia davvero per-worktree.
- add_per_worktree_entries_to_dir()
viene aggiornato per accertarsi che l'elenco di riferimenti guardi per-worktree refs/rewritten/
anziché per-repo one.
common_list[]
viene aggiornato in modo da git_path()
restituire la posizione corretta. Questo include " rev-parse --git-path
".
Questo pasticcio è stato creato da me.
Ho iniziato a provare a risolverlo con l'introduzione di refs/worktree,
dove tutti gli arbitri saranno per-worktree senza trattamenti speciali.
I ref sfavorevoli / riscritti sono venuti prima dei ref / worktree quindi questo è tutto ciò che possiamo fare.
Con Git 2.24 (Q4 2019), " git rebase --rebase-merges
" ha imparato a guidare diverse strategie di unione e passare loro opzioni specifiche di strategia.
Vedi commit 476998d (04 set 2019) di Elijah Newren ( newren
) .
Vedere commettere e1fac53 , commettere a63f990 , commettere 5dcdd74 , commettere e145d99 , commettere 4e6023b , commettere f67336d , commettere a9c7107 , commettere b8c6f24 , commettere d51b771 , commettere c248d32 , commettere 8c1e240 , commettere 5efed0e , commettere 68b54f6 , commettere 2e7bbac , commettere 6180b20 , commettere d5b581f (31 Lug 2019) diJohannes Schindelin ( dscho
) .
(Unita da Junio C Hamano - gitster
- in commit 917a319 , 18 set 2019)
Con Git 2.25 (Q1 2020), la logica utilizzata per distinguere i riferimenti globali locali e di repository di worktree è fissa, per facilitare l'unione di preservazione.
Vedi commit f45f88b , commit c72fc40 , commit 8a64881 , commit 7cb8c92 , commit e536b1f (21 ott 2019) di SZEDER Gábor ( szeder
) .
(Unita da Junio C Hamano - gitster
- in commit db806d7 , 10 nov 2019)
path.c
: non chiamare la match
funzione senza valore intrie_find()
Firmato-fuori-da: SZEDER Gábor
'logs / refs' non è un percorso specifico dell'albero funzionante, ma poiché commit b9317d55a3 (Assicurarsi che refs / riscritto / sia per-worktree, 2019-03-07, v2.22.0-rc0) ' git rev-parse --git-path
' ha restituito un percorso fasullo se /
è presente un " " finale :
$ git -C WT/ rev-parse --git-path logs/refs --git-path logs/refs/
/home/szeder/src/git/.git/logs/refs
/home/szeder/src/git/.git/worktrees/WT/logs/refs/
Utilizziamo una trie
struttura di dati per decidere in modo efficiente se un percorso appartiene alla directory comune o funziona in modo specifico per l'albero.
Come accade, b9317d55a3 ha attivato un bug vecchio quanto l' trie
implementazione stessa, aggiunto in 4e09cf2acf (" path
: ottimizzare il controllo della directory comune", 2015-08-31, Git v2.7.0-rc0 - unione elencata nel batch # 2 ).
Secondo il commento che descrive trie_find()
, dovrebbe chiamare la funzione di corrispondenza "fn" solo per un prefisso "/ -or- \ 0-terminato della chiave per la quale il trie contiene un valore".
Questo non è vero: ci sono tre posti in cui trie_find () chiama la funzione match, ma in uno di questi manca il controllo dell'esistenza del valore.
b9317d55a3 ha aggiunto due nuove chiavi a trie
:
- '
logs/refs/rewritten
' e
- '
logs/refs/worktree
', accanto al già esistente ' logs/refs/bisect
'.
Ciò ha comportato un trie
nodo con il percorso ' logs/refs/
', che non esisteva prima e che non ha un valore associato.
Una query per " logs/refs/
" trova questo nodo e quindi colpisce quel sito di chiamata della match
funzione che non verifica l'esistenza del valore, e quindi invoca la match
funzione NULL
come valore.
Quando la match
funzione check_common()
viene invocata con un NULL
valore, restituisce 0, che indica che il percorso richiesto non appartiene alla directory comune, risultando in definitiva il percorso fasullo mostrato sopra.
Aggiungi la condizione mancante a in trie_find()
modo che non invocherà mai la funzione di corrispondenza con un valore inesistente.
check_common()
non sarà più necessario verificare che abbia ottenuto un valore diverso da NULL, quindi rimuovere tale condizione.
Credo che non ci siano altri percorsi che potrebbero causare risultati fasulli simili.
AFAICT l'unica altra chiave che porta alla chiamata della funzione match con un NULL
valore è ' co
' (a causa dei tasti ' common
' e ' config
').
Tuttavia, poiché non si trovano in una directory appartenente alla directory comune, è previsto il percorso specifico dell'albero di lavoro risultante.
git --rebase-merges
alla fine sostituirà il vecchiogit --preserve-merges
. Vedi la mia risposta di seguito