Git
ribasamento
Ricerca…
Sintassi
-
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
-
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-
git rebase --continue | --skip | --abort | --edit-todo
Parametri
Parametro | Dettagli |
---|---|
--Continua | Riavvia il processo di rebasing dopo aver risolto un conflitto di unione. |
--abort | Interrompere l'operazione di rebase e reimpostare HEAD sul ramo originale. Se è stato fornito un ramo quando è stata avviata l'operazione di rebase, HEAD verrà reimpostato su branch. In caso contrario, HEAD verrà reimpostato sul punto in cui si trovava quando è stata avviata l'operazione di rebase. |
--keep-vuoto | Mantenere il commit che non cambia nulla dai suoi genitori nel risultato. |
--Salta | Riavvia il processo di rebasing saltando la patch corrente. |
-m, --merge | Utilizzare le strategie di fusione per rebase. Quando viene utilizzata la strategia di unione ricorsiva (predefinita), ciò consente a rebase di essere a conoscenza delle ridenominazioni sul lato upstream. Si noti che un'unione di rebase funziona riproducendo ogni commit dal ramo di lavoro in cima al ramo upstream. Per questo motivo, quando si verifica un conflitto di merge, la parte riportata come nostra è la serie a lungo termine, a partire da monte, e il loro è il ramo di lavoro. In altre parole, i lati vengono scambiati. |
--statistica | Mostra una differenza tra ciò che è cambiato a monte dall'ultimo rebase. Il diffstat è anche controllato dall'opzione di configurazione rebase.stat. |
-x, command --exec | Esegui rebase interattivo, fermandosi tra ogni command commit ed esecuzione |
Osservazioni
Si prega di tenere presente che rebase riscrive in modo efficace la cronologia del repository.
Il commit rebasing esistente nel repository remoto potrebbe riscrivere i nodi di repository utilizzati da altri sviluppatori come nodo di base per i loro sviluppi. A meno che tu non sappia veramente cosa stai facendo, è buona norma effettuare il rebase prima di applicare le modifiche.
Rebasing del ramo locale
La ridefinizione riapplica una serie di commit su un altro commit.
Per rebase
un ramo, controlla il ramo e poi rebase
sopra un altro ramo.
git checkout topic
git rebase master # rebase current branch onto master branch
Ciò causerebbe:
A---B---C topic
/
D---E---F---G master
Diventare:
A'--B'--C' topic
/
D---E---F---G master
Queste operazioni possono essere combinate in un unico comando che controlla il ramo e lo ricolloca immediatamente:
git rebase master topic # rebase topic branch onto master branch
Importante: dopo il rebase, i commit applicati avranno un hash diverso. Non dovresti rebase i commit che hai già inviato a un host remoto. Una conseguenza potrebbe essere l'incapacità di git push
il proprio ramo locale ribaltabile su un host remoto, lasciando l'unica opzione per git push --force
.
Rebase: nostro e loro, locale e remoto
Un rebase cambia il significato di "nostro" e "loro":
git checkout topic
git rebase master # rebase topic branch on top of master branch
Qualunque cosa HEAD indichi è "nostra"
La prima cosa che fa rebase è il reset del HEAD
da master
; prima che il cherry-picking commetta dal vecchio topic
ramo a uno nuovo (ogni commit nel ramo topic
precedente verrà riscritto e verrà identificato da un diverso hash).
Per quanto riguarda le terminologie utilizzate dagli strumenti di unione (da non confondere con riferimento locale o riferimento remoto )
=> local is master ("ours"),
=> remote is topic ("theirs")
Ciò significa che uno strumento di fusione / diff presenterà il ramo upstream come local
( master
: il ramo su cui si sta ridefinendo), e il ramo di lavoro come remote
( topic
: il ramo che viene ridefinito)
+-----------------------------------------+
| LOCAL:master | BASE | REMOTE:topic |
+-----------------------------------------+
| MERGED |
+-----------------------------------------+
Inversione illustrata
In una fusione:
c--c--x--x--x(*) <- current branch topic ('*'=HEAD)
\
\
\--y--y--y <- other branch to merge
Non cambiamo l' topic
corrente, quindi quello che abbiamo è ancora quello su cui stavamo lavorando (e ci uniamo da un altro ramo)
c--c--x--x--x---------o(*) MERGE, still on branch topic
\ ^ /
\ ours /
\ /
--y--y--y--/
^
theirs
Su un rebase:
Ma su un rebase cambiamo i lati perché la prima cosa che fa un rebase è il checkout del ramo upstream per replicare il commit corrente su di esso!
c--c--x--x--x(*) <- current branch topic ('*'=HEAD)
\
\
\--y--y--y <- upstream branch
Un git rebase upstream
imposta innanzitutto HEAD
al ramo upstream, da qui il passaggio di "nostro" e "loro" rispetto al precedente ramo di lavoro "attuale".
c--c--x--x--x <- former "current" branch, new "theirs"
\
\
\--y--y--y(*) <- set HEAD to this commit, to replay x's on it
^ this will be the new "ours"
|
upstream
Il rebase poi ripetere 'loro' si impegna sul nuovo 'nostro' topic
filiale:
c--c..x..x..x <- old "theirs" commits, now "ghosts", available through "reflogs"
\
\
\--y--y--y--x'--x'--x'(*) <- topic once all x's are replayed,
^ point branch topic to this commit
|
upstream branch
Rebase interattivo
Questo esempio mira a descrivere come si può utilizzare git rebase
in modalità interattiva. Ci si aspetta che si abbia una comprensione di base di cosa sia git rebase
e cosa fa.
Il rebase interattivo viene avviato usando il seguente comando:
git rebase -i
L'opzione -i
riferisce alla modalità interattiva . Utilizzando rebase interattivo, l'utente può modificare i messaggi di commit, nonché riordinare, dividere e / o commettere lo squash (combinarli in uno).
Dì che vuoi riorganizzare i tuoi ultimi tre commit. Per fare ciò puoi eseguire:
git rebase -i HEAD~3
Dopo aver eseguito le istruzioni di cui sopra, verrà aperto un file nel tuo editor di testo in cui sarai in grado di selezionare il modo in cui i tuoi commit saranno ridepositati. Ai fini di questo esempio, è sufficiente modificare l'ordine dei commit, salvare il file e chiudere l'editor. Ciò avvierà un rebase con l'ordine che hai applicato. Se controlli git log
, vedrai il tuo commit nel nuovo ordine che hai specificato.
Risconto dei messaggi di commit
Ora, hai deciso che uno dei messaggi di commit è vago e vuoi che sia più descrittivo. Esaminiamo gli ultimi tre commit usando lo stesso comando.
git rebase -i HEAD~3
Invece di riorganizzare l'ordine, i commit verranno ridefiniti, questa volta cambieremo pick
, il valore predefinito, per reword
un commit in cui si desidera modificare il messaggio.
Quando chiudi l'editor, il rebase verrà avviato e si fermerà al messaggio di commit specifico che desideri riformulare. Questo ti permetterà di cambiare il messaggio di commit a seconda di quello che desideri. Dopo aver modificato il messaggio, basta chiudere l'editor per procedere.
Modifica del contenuto di un commit
Oltre a cambiare il messaggio di commit puoi anche adattare le modifiche fatte dal commit. Per farlo basta cambiare il pick
per edit
un commit. Git si fermerà quando arriverà a quel commit e fornirà le modifiche originali del commit nell'area di staging. Ora puoi adattare queste modifiche disattivandole o aggiungendo nuove modifiche.
Non appena l'area di staging contiene tutte le modifiche desiderate in quel commit, commetti le modifiche. Il vecchio messaggio di commit verrà mostrato e può essere adattato per riflettere il nuovo commit.
Divisione di un singolo commit in più
Supponiamo che tu abbia fatto un commit ma in un secondo momento abbia deciso che questo commit potrebbe essere diviso in due o più commit. Usando lo stesso comando di prima, sostituisci invece pick
con edit
e premi invio.
Ora, git si fermerà al commit che hai contrassegnato per la modifica e inserirà tutto il suo contenuto nell'area di staging. Da quel punto puoi eseguire git reset HEAD^
per posizionare il commit nella tua directory di lavoro. Quindi, puoi aggiungere e trasferire i tuoi file in una sequenza diversa, dividendo infine un singolo commit in n commit.
Schiacciare più commit in uno solo
Di 'che hai fatto un po' di lavoro e hai più commit che a tuo avviso potrebbero essere un singolo commit. Per questo puoi eseguire git rebase -i HEAD~3
, sostituendo 3
con una quantità appropriata di commit.
Questa volta sostituisci invece il pick
con lo squash
. Durante il rebase, il commit che hai indicato di essere schiacciato sarà schiacciato sopra il commit precedente; invece di trasformarli in un singolo commit.
Abortire un Rebase interattivo
Hai avviato un rebase interattivo. Nell'editor in cui scegli i tuoi commit, decidi che qualcosa va storto (ad esempio manca un commit, o hai scelto la destinazione errata di rebase) e vuoi abortire il rebase.
Per fare ciò, basta cancellare tutti i commit e le azioni (cioè tutte le linee che non iniziano con il segno #
) e il rebase verrà annullato!
Il testo della guida nell'editor fornisce effettivamente questo suggerimento:
# Rebase 36d15de..612f2f7 onto 36d15de (3 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Note that empty commits are commented out
Spingendo dopo un rebase
A volte hai bisogno di riscrivere la cronologia con un rebase, ma git push
lamenta di farlo perché hai riscritto la cronologia.
Questo può essere risolto con un git push --force
, ma si consideri git push --force-with-lease
, che indica che si desidera che la push fallisca se il ramo locale di localizzazione remota differisce dal ramo sul telecomando, ad esempio, qualcuno altrimenti spinto al telecomando dopo l'ultimo recupero. Ciò evita di sovrascrivere inavvertitamente la spinta recente di qualcun altro.
Nota : git push --force
- e anche - --force-with-lease
per quella materia - può essere un comando pericoloso perché riscrive la cronologia del ramo. Se un'altra persona ha tirato il ramo prima della spinta forzata, la sua git pull
o git fetch
avrà errori perché la cronologia locale e la cronologia remota sono divergenti. Ciò potrebbe causare errori imprevisti della persona. Con un sufficiente esame dei diagrammi, è possibile recuperare il lavoro degli altri utenti, ma può portare a molto tempo sprecato. Se devi fare una spinta forzata a un ramo con altri contributori, prova a coordinarti con loro in modo che non debbano affrontare errori.
Rebase fino al commit iniziale
Dal momento che Git 1.7.12 è possibile rebase fino al commit di root. Il commit radice è il primo commit mai fatto in un repository e normalmente non può essere modificato. Usa il seguente comando:
git rebase -i --root
Rifondazione prima di una revisione del codice
Sommario
Questo obiettivo è riorganizzare tutti i tuoi commit sparsi in commit più significativi per revisioni più semplici del codice. Se ci sono troppi livelli di modifiche su troppi file contemporaneamente, è più difficile eseguire una revisione del codice. Se riesci a riorganizzare i commit cronologicamente creati in commit topici, allora il processo di revisione del codice è più semplice (e probabilmente meno errori passano attraverso il processo di revisione del codice).
Questo esempio troppo semplificato non è l'unica strategia per usare git per fare revisioni migliori del codice. È il modo in cui lo faccio, ed è qualcosa per ispirare gli altri a considerare come rendere le recensioni del codice e la cronologia git più semplici / migliori.
Questo dimostra anche pedagogicamente il potere di rebase in generale.
Questo esempio presume che tu sappia del rebasing interattivo.
assumendo:
- stai lavorando su un ramo di funzione fuori dal master
- la tua funzione ha tre livelli principali: front-end, back-end, DB
- hai fatto molti commit mentre lavoravi su un ramo di funzionalità. Ogni commit tocca più layer contemporaneamente
- vuoi (alla fine) solo tre commit nel tuo ramo
- uno contenente tutte le modifiche front-end
- uno contenente tutte le modifiche di back-end
- uno contenente tutte le modifiche del DB
Strategia:
- cambieremo i nostri commit cronologici in commit "topici".
- per prima cosa, suddividi tutti i commit in commit multipli, più piccoli - ognuno contenente un solo argomento alla volta (nel nostro esempio, gli argomenti sono front-end, back-end, modifiche DB)
- Quindi riordina i nostri commit attuali e li "squash" in singoli commit attuali
Esempio:
$ git log --oneline master..
975430b db adding works: db.sql logic.rb
3702650 trying to allow adding todo items: page.html logic.rb
43b075a first draft: page.html and db.sql
$ git rebase -i master
Questo verrà mostrato nell'editor di testo:
pick 43b075a first draft: page.html and db.sql
pick 3702650 trying to allow adding todo items: page.html logic.rb
pick 975430b db adding works: db.sql logic.rb
Cambiarlo in questo:
e 43b075a first draft: page.html and db.sql
e 3702650 trying to allow adding todo items: page.html logic.rb
e 975430b db adding works: db.sql logic.rb
Quindi git applicherà un commit alla volta. Dopo ogni commit, verrà visualizzato un prompt, quindi è possibile effettuare le seguenti operazioni:
Stopped at 43b075a92a952faf999e76c4e4d7fa0f44576579... first draft: page.html and db.sql
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
$ git status
rebase in progress; onto 4975ae9
You are currently editing a commit while rebasing branch 'feature' on '4975ae9'.
(use "git commit --amend" to amend the current commit)
(use "git rebase --continue" once you are satisfied with your changes)
nothing to commit, working directory clean
$ git reset HEAD^ #This 'uncommits' all the changes in this commit.
$ git status -s
M db.sql
M page.html
$ git add db.sql #now we will create the smaller topical commits
$ git commit -m "first draft: db.sql"
$ git add page.html
$ git commit -m "first draft: page.html"
$ git rebase --continue
Quindi ripeterai i passaggi per ogni commit. Alla fine, hai questo:
$ git log --oneline
0309336 db adding works: logic.rb
06f81c9 db adding works: db.sql
3264de2 adding todo items: page.html
675a02b adding todo items: logic.rb
272c674 first draft: page.html
08c275d first draft: db.sql
Ora eseguiamo rebase un'altra volta per riordinare e squash:
$ git rebase -i master
Questo verrà mostrato nell'editor di testo:
pick 08c275d first draft: db.sql
pick 272c674 first draft: page.html
pick 675a02b adding todo items: logic.rb
pick 3264de2 adding todo items: page.html
pick 06f81c9 db adding works: db.sql
pick 0309336 db adding works: logic.rb
Cambiarlo in questo:
pick 08c275d first draft: db.sql
s 06f81c9 db adding works: db.sql
pick 675a02b adding todo items: logic.rb
s 0309336 db adding works: logic.rb
pick 272c674 first draft: page.html
s 3264de2 adding todo items: page.html
AVVISO: assicurati di dire a git rebase di applicare / schiacciare i commit topici più piccoli nell'ordine in cui sono stati commessi cronologicamente . Altrimenti potresti avere falsi, inutili conflitti di fusione da affrontare.
Quando tutto ciò che è detto e fatto, questo rebase interattivo, ottieni questo:
$ git log --oneline master..
74bdd5f adding todos: GUI layer
e8d8f7e adding todos: business logic layer
121c578 adding todos: DB layer
Ricapitolare
Ora hai ridefinito i tuoi commit cronologici in commit attuali. Nella vita reale, potresti non aver bisogno di farlo ogni volta, ma quando vuoi o devi farlo, ora puoi farlo. Inoltre, si spera che tu abbia imparato di più su git rebase.
Imposta git-pull per eseguire automaticamente un rebase anziché un'unione
Se il tuo team sta seguendo un flusso di lavoro basato su rebase, può essere vantaggioso configurare git in modo che ogni ramo appena creato esegua un'operazione di rebase, invece di un'operazione di unione, durante un git pull
.
Per impostare automaticamente ogni nuovo ramo su rebase, aggiungi quanto segue a .gitconfig
o .git/config
:
[branch]
autosetuprebase = always
git config [--global] branch.autosetuprebase always
comando: git config [--global] branch.autosetuprebase always
In alternativa, puoi impostare il comando git pull
per comportarti sempre come se l'opzione --rebase
fosse passata:
[pull]
rebase = true
git config [--global] pull.rebase true
comando: git config [--global] pull.rebase true
Test di tutti i commit durante rebase
Prima di effettuare una richiesta di pull, è utile assicurarsi che la compilazione abbia esito positivo e che i test passino per ogni commit nel ramo. Possiamo farlo automaticamente usando il parametro -x
.
Per esempio:
git rebase -i -x make
eseguirà il rebase interattivo e si fermerà dopo ogni commit per eseguire make
. Nel caso in cui make
fallisca, git si fermerà per darti l'opportunità di risolvere i problemi e di modificare il commit prima di procedere con il successivo.
Configurazione di autostash
L'autostampa è un'opzione di configurazione molto utile quando si utilizza rebase per le modifiche locali. Spesso, potrebbe essere necessario inserire commit dal ramo upstream, ma non sono ancora pronti a impegnarsi.
Tuttavia, Git non consente l'avvio di un rebase se la directory di lavoro non è pulita. Autostash per il salvataggio:
git config --global rebase.autostash # one time configuration
git rebase @{u} # example rebase on upstream branch
L'autostash verrà applicato ogni volta che il rebase è finito. Non importa se il rebase finisce correttamente o se viene interrotto. In entrambi i casi, verrà applicato l'autostash. Se il rebase ha avuto successo e quindi il commit di base è stato modificato, potrebbe verificarsi un conflitto tra l'autostash e i nuovi commit. In questo caso, dovrai risolvere i conflitti prima di impegnarti. Questo non è diverso da quello che avresti se fosse stato nascosto manualmente, e poi applicato, quindi non c'è nessun svantaggio nel farlo automaticamente.