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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow