Di solito invio un elenco di commit per la revisione. Se ho i seguenti commit:
HEAD
Commit3
Commit2
Commit1
... So che posso modificare head commit con git commit --amend
. Ma come posso modificare Commit1
, dato che non è il HEAD
commit?
Di solito invio un elenco di commit per la revisione. Se ho i seguenti commit:
HEAD
Commit3
Commit2
Commit1
... So che posso modificare head commit con git commit --amend
. Ma come posso modificare Commit1
, dato che non è il HEAD
commit?
Risposte:
Puoi usare git rebase . Ad esempio, se si desidera modificare il commit bbc643cd
, eseguire
$ git rebase --interactive 'bbc643cd^'
Si prega di notare il punto di inserimento ^
alla fine del comando, poiché è necessario tornare a commit prima di quello che si desidera modificare .
Nel editor predefinito, modificare pick
a edit
nella linea di nota 'bbc643cd'.
Salvare il file ed uscire: git interpreterà ed eseguirà automaticamente i comandi nel file. Ti ritroverai nella situazione precedente in cui hai appena creato il commit bbc643cd
.
A questo punto, bbc643cd
è il tuo ultimo commit e puoi facilmente modificarlo : apporta le tue modifiche e poi commettile con il comando:
$ git commit --all --amend --no-edit
Successivamente, digita:
$ git rebase --continue
per tornare al precedente commit HEAD.
ATTENZIONE : Nota che questo cambierà lo SHA-1 di quel commit così come tutti i bambini - in altre parole, riscrive la storia da quel punto in avanti. È possibile interrompere i repository in questo modo se si preme usando il comandogit push --force
reword
azione in git rebase -i
invece di edit
(apre automaticamente l'editor e continua con il resto dei passaggi di rebase; questo evita l'uso di git commit --ammend
e git rebase --continue
quando è necessario modificare solo il messaggio di commit e non il contenuto ).
git stash
prima git rebase
e git stash pop
dopo, in caso di modifiche in sospeso.
git commit --all --amend --no-edit
qui. Tutto quello che dovevo fare dopo git rebase -i ...
era git commit --amend
normalmente allora git rebase --continue
.
git rebase -i @~9 # Show the last 9 commits in a text editor
Trova il commit desiderato, modifica pick
in e
( edit
), salva e chiudi il file. Git tornerà indietro a quel commit, permettendoti di:
git commit --amend
per apportare modifiche ogit reset @~
per scartare l'ultimo commit, ma non le modifiche ai file (ovvero portarti al punto in cui ti trovavi quando avevi modificato i file, ma non hai ancora eseguito il commit).Quest'ultimo è utile per fare cose più complesse come la suddivisione in più commit.
Quindi, esegui git rebase --continue
e Git riprodurrà le modifiche successive in aggiunta al commit modificato. È possibile che ti venga chiesto di correggere alcuni conflitti di unione.
Nota: @
è una scorciatoia per HEAD
ed ~
è il commit prima del commit specificato.
Maggiori informazioni sulla riscrittura della cronologia nei documenti Git.
ProTip ™: non aver paura di sperimentare comandi "pericolosi" che riscrivono la cronologia * - Git non cancella i tuoi commit per 90 giorni di default; li puoi trovare nel reflog:
$ git reset @~3 # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started
* Fai attenzione a opzioni come --hard
e --force
sebbene: possono scartare i dati.
* Inoltre, non riscrivere la cronologia su nessun ramo su cui stai collaborando.
Su molti sistemi, git rebase -i
aprirà Vim per impostazione predefinita. Vim non funziona come la maggior parte degli editor di testo moderni, quindi dai un'occhiata a come riutilizzare Vim . Se preferisci utilizzare un editor diverso, modificalo con git config --global core.editor your-favorite-text-editor
.
@
come stenografia per HEAD
. Grazie per aver pubblicato questo
git reset @~
esattamente quello che volevo fare dopo aver scelto commit with git rebase ...
. Sei il mio eroe)
Rifacimento interattivo con--autosquash
è qualcosa che uso frequentemente quando ho bisogno di sistemare i commit precedenti più in profondità nella storia. In sostanza, accelera il processo illustrato dalla risposta di ZelluX ed è particolarmente utile quando hai più di un commit che devi modificare.
Dalla documentazione:
--autosquash
Quando il messaggio di registro di commit inizia con "squash! ..." (o "fixup! ...") e c'è un commit il cui titolo inizia con lo stesso ..., modifica automaticamente la todo list di rebase -i in modo che il commit contrassegnato per la compressione viene subito dopo la modifica del commit
Supponiamo di avere una storia simile a questa:
$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1
e hai delle modifiche che desideri modificare in Commit2, quindi esegui il commit delle modifiche utilizzando
$ git commit -m "fixup! Commit2"
in alternativa puoi usare commit-sha invece del messaggio di commit, quindi "fixup! e8adec4
o anche solo un prefisso del messaggio di commit.
Quindi avviare un rebase interattivo sul commit prima
$ git rebase e8adec4^ -i --autosquash
il tuo editor si aprirà con i commit già correttamente ordinati
pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3
tutto ciò che devi fare è salvare ed uscire
git commit --fixup=@~
posto di git commit -m "fixup! Commit2"
. Ciò è particolarmente utile quando i tuoi messaggi di commit sono più lunghi e sarebbe una seccatura scrivere tutto.
Correre:
$ git rebase --interactive commit_hash^
ognuno ^
indica quanti commit si desidera modificare, se è solo uno (l'hash di commit specificato), quindi basta aggiungerne uno ^
.
Con Vim puoi cambiare le parole pick
in reword
per i commit che vuoi cambiare, salvare ed uscire (:wq
). Quindi git ti chiederà ogni commit che hai contrassegnato come reword in modo da poter cambiare il messaggio di commit.
Ogni messaggio di commit è necessario salvare ed uscire ( :wq
) per passare al messaggio di commit successivo
Se si desidera uscire senza applicare le modifiche, premere :q!
MODIFICA : per navigare in vim
te usa j
per salire, k
per scendere, h
per andare a sinistra e l
per andare a destra (tutto questo in NORMAL
modalità, premi ESC
per passare alla NORMAL
modalità). Per modificare un testo, premere in i
modo da accedere alla INSERT
modalità in cui inserire il testo. Premere ESC
per tornare alla NORMAL
modalità :)
AGGIORNAMENTO : Ecco un ottimo link dalla lista di github Come annullare (quasi) qualsiasi cosa con git
git push --force
?
git push --force
fa è sovrascrivere i commit dei telecomandi con i tuoi commit locali. Questo non è il caso di questo argomento :)
Se per qualche motivo non ti piacciono gli editor interattivi, puoi usarli git rebase --onto
.
Di 'che vuoi modificare Commit1
. In primo luogo, ramo da prima Commit1
:
git checkout -b amending [commit before Commit1]
Secondo, prendi Commit1
con cherry-pick
:
git cherry-pick Commit1
Ora modifica le modifiche, creando Commit1'
:
git add ...
git commit --amend -m "new message for Commit1"
E infine, dopo aver nascosto tutte le altre modifiche, trapianta il resto dei tuoi commit fino a completare master
il nuovo commit:
git rebase --onto amending Commit1 master
Leggi: "rebase, sul ramo amending
, tutto si impegna tra Commit1
(non inclusivo) e master
(compreso)". Cioè, Commit2 e Commit3, eliminando completamente il vecchio Commit1. Potresti semplicemente selezionarli, ma in questo modo è più facile.
Ricorda di ripulire i tuoi rami!
git branch -d amending
git checkout -b amending Commit1~1
per ottenere il commit precedente
git checkout -b amending Commit1
?
Basato sulla documentazione
Modifica del messaggio di messaggi di commit più vecchi o multipli
git rebase -i HEAD~3
Quanto sopra mostra un elenco degli ultimi 3 commit sul ramo corrente, cambia 3 in qualcos'altro se vuoi di più. L'elenco sarà simile al seguente:
pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
Sostituisci pick con reword prima di ogni messaggio di commit che desideri modificare. Supponiamo che tu modifichi il secondo commit nell'elenco, il tuo file sarà simile al seguente:
pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
Salvare e chiudere il file dell'elenco di commit, verrà visualizzato un nuovo editor per modificare il messaggio di commit, modificare il messaggio di commit e salvare.
Finaly Forza-spingi gli commit modificati.
git push --force
Ho pensato di condividere un alias che sto usando per questo. Si basa su rebase interattivo non interattivo. Per aggiungerlo a Git, eseguire questo comando (spiegazione fornita di seguito):
git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'
Il più grande vantaggio di questo comando è il fatto che è no-vim .
(1) dato che non ci sono conflitti durante il rebase, ovviamente
git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111
Il nome amend-to
sembra appropriato IMHO. Confronta il flusso con --amend
:
git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>
git config --global alias.<NAME> '!<COMMAND>'
- crea un alias git globale denominato <NAME>
che eseguirà il comando non git<COMMAND>
f() { <BODY> }; f
- una funzione bash "anonima".SHA=`git rev-parse "$1"`;
- converte l'argomento in git revision e assegna il risultato a variabile SHA
git commit --fixup "$SHA"
- fixup-commit per SHA
. Vedi git-commit
documentiGIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
la parte è stata coperta da altre risposte.--autosquash
è ciò che viene utilizzato insieme a git commit --fixup
, vedere git-rebase
documenti per ulteriori informazioniGIT_SEQUENCE_EDITOR=true
è ciò che rende il tutto non interattivo. Questo trucco che ho imparato da questo post sul blog .amend-to
gestisca i file non messi in scena: git config --global alias.amend-to '!f() { SHA=
git rev-parse "$ 1"; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
Mi sono ritrovato a correggere un impegno passato abbastanza frequentemente da aver scritto una sceneggiatura per questo.
Ecco il flusso di lavoro:
git commit-edit <commit-hash>
Questo ti lascerà al commit che vuoi modificare.
Correggi e metti in scena il commit come vorresti fosse stato in primo luogo.
(Potresti voler usare git stash save
per conservare tutti i file che non stai commettendo)
Ripeti il commit con --amend
, ad esempio:
git commit --amend
Completa il rebase:
git rebase --continue
Affinché quanto sopra funzioni, inserisci lo script seguente in un file eseguibile chiamato git-commit-edit
da qualche parte nel tuo $PATH
:
#!/bin/bash
set -euo pipefail
script_name=${0##*/}
warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }
[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"
# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
message=$(git log -1 --format='%h %s' "$commit")
if [[ $OSTYPE =~ ^darwin ]]; then
sed_inplace=(sed -Ei "")
else
sed_inplace=(sed -Ei)
fi
export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \\1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)" # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty # Commit an empty commit so that that cache diffs are un-reversed
echo
echo "Editing commit: $message" >&2
echo
È venuto a questo approccio (ed è probabilmente esattamente lo stesso che usare il rebase interattivo) ma per me è un po 'semplice.
Nota: vi presento questo approccio a scopo illustrativo di ciò che potete fare piuttosto che un'alternativa quotidiana. Dal momento che ha molti passaggi (e forse alcuni avvertimenti).
Supponi di voler modificare il commit 0
e che sei attualmente attivofeature-branch
some-commit---0---1---2---(feature-branch)HEAD
Vai a questo commit e crea un quick-branch
. È inoltre possibile clonare il ramo della funzione come punto di ripristino (prima di iniziare).
?(git checkout -b feature-branch-backup)
git checkout 0
git checkout -b quick-branch
Ora avrai qualcosa del genere:
0(quick-branch)HEAD---1---2---(feature-branch)
Cambiamenti di scena, metti tutto il resto.
git add ./example.txt
git stash
Salva le modifiche e torna a feature-branch
git commit --amend
git checkout feature-branch
Ora avrai qualcosa del genere:
some-commit---0---1---2---(feature-branch)HEAD
\
---0'(quick-branch)
Rebase feature-branch
su quick-branch
(risolvi eventuali conflitti lungo il percorso). Applicare stash e rimuovere quick-branch
.
git rebase quick-branch
git stash pop
git branch -D quick-branch
E finisci con:
some-commit---0'---1'---2'---HEAD(feature-branch)
Git non duplicherà (anche se non posso davvero dire fino a che punto) lo 0 commette durante il rebasing.
Nota: tutti gli hash di commit vengono modificati a partire dal commit che inizialmente intendevamo modificare.
Per ottenere un comando non interattivo, inserisci uno script con questo contenuto nel PERCORSO:
#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse $1)
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"
Usalo mettendo in scena le modifiche (con git add
) e quindi esegui git fixup <commit-to-modify>
. Naturalmente, sarà comunque interattivo se si verificano conflitti.
git stash
+ rebase
automazione
Per quando ho bisogno di modificare un vecchio commit molte volte per le recensioni di Gerrit, ho fatto:
git-amend-old() (
# Stash, apply to past commit, and rebase the current branch on to of the result.
current_branch="$(git rev-parse --abbrev-ref HEAD)"
apply_to="$1"
git stash
git checkout "$apply_to"
git stash apply
git add -u
git commit --amend --no-edit
new_sha="$(git log --format="%H" -n 1)"
git checkout "$current_branch"
git rebase --onto "$new_sha" "$apply_to"
)
Uso:
git add
se già in repositorygit-amend-old $old_sha
Mi piace questo in --autosquash
quanto non schiaccia altre correzioni non correlate.
git amend
per applicare le modifiche a un commit specifico con l'utilizzo della scorta corrente, molto intelligente!
Ho risolto questo
1) creando un nuovo commit con le modifiche che voglio ..
r8gs4r commit 0
2) So quale impegno devo unirmi a esso. che è commit 3.
quindi, git rebase -i HEAD~4
# 4 rappresenta il recente commit 4 (qui il commit 3 è al 4 ° posto)
3) in rebase interattivo il recente commit si troverà in fondo. sarà simile,
pick q6ade6 commit 3
pick vr43de commit 2
pick ac123d commit 1
pick r8gs4r commit 0
4) qui è necessario riorganizzare il commit se si desidera unire uno specifico. dovrebbe essere come
parent
|_child
pick q6ade6 commit 3
f r8gs4r commit 0
pick vr43de commit 2
pick ac123d commit 1
dopo riorganizzare è necessario sostituire p
pick
con f
( fixup si unirà senza messaggio di commit) o s
( squash merge con il messaggio di commit può cambiare in fase di esecuzione)
e poi salva il tuo albero.
ora unione completata con commit esistente.
Nota: il suo metodo non è preferibile a meno che tu non lo mantenga da solo. se hai una grande squadra non è un metodo accettabile per riscrivere git tree finirà in conflitti che non conosci. se vuoi mantenere l'albero pulito con meno commit puoi provare questo e se la sua piccola squadra non è preferibile .....
L'opzione migliore è utilizzare "Comando rebase interattivo" .
Il
git rebase
comando è incredibilmente potente. Ti consente di modificare i messaggi di commit , combinare i commit, riordinarli ... ecc.Ogni volta che si ribadisce un commit, verrà creato un nuovo SHA per ciascun commit, indipendentemente dal fatto che il contenuto venga modificato o meno! Dovresti stare attento quando usare questo comando perché potrebbe avere conseguenze drastiche soprattutto se lavori in collaborazione con altri sviluppatori. Potrebbero iniziare a lavorare con il tuo commit mentre ne stai riformulando un po '. Dopo aver forzato a spingere i commit saranno fuori sincrono e potresti scoprirlo in seguito in una situazione disordinata. Quindi sii attento!
Si consiglia di creare una
backup
succursale prima di effettuare la rebasing, quindi ogni volta che si trovano cose fuori controllo è possibile tornare allo stato precedente.
git rebase -i <base>
-i
indicare "interattivo" . Si noti che è possibile eseguire un rebase in modalità non interattiva. ex:
#interactivly rebase the n commits from the current position, n is a given number(2,3 ...etc)
git rebase -i HEAD~n
HEAD
indica la posizione corrente (può anche essere il nome della filiale o il commit SHA). Il ~n
mezzo "n beforeé, così HEAD~n
sarà la lista di 'n' commit prima di quello che si sta attualmente.
git rebase
ha un comando diverso come:
p
o pick
continuare a impegnarsi così com'è.r
oppure reword
: per mantenere il contenuto del commit ma modificare il messaggio di commit.s
oppure squash
: per combinare le modifiche di questo commit nel commit precedente (il commit sopra di esso nell'elenco).... eccetera.
Nota: è meglio far funzionare Git con l'editor di codice per semplificare le cose. Ad esempio, se si utilizza il codice visivo, è possibile aggiungere in questo modo git config --global core.editor "code --wait"
. Oppure puoi cercare in Google come associare il tuo editor di codice preferito a GIT.
git rebase
Volevo cambiare gli ultimi 2 commit che ho fatto, quindi ho elaborato in questo modo:
#This to show all the commits on one line
$git log --oneline
4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"
4d95e08 docs: Add created date and project title"
eaf7978 (origin/master , origin/HEAD, master) Inital commit
46a5819 Create README.md
Ora uso git rebase
per modificare gli ultimi 2 messaggi di commit:
$git rebase -i HEAD~2
apre l'editor di codice e mostra questo:
pick 4d95e08 docs: Add created date and project title
pick 4f3d0c8 docs: Add project description and included files
# Rebase eaf7978..4f3d0c8 onto eaf7978 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
...
Dal momento che voglio cambiare il messaggio di commit per questo 2 commit. Quindi scriverò r
o reword
al posto di pick
. Quindi salvare il file e chiudere la scheda. Si noti che rebase
viene eseguito in un processo in più passaggi, quindi il passaggio successivo è aggiornare i messaggi. Si noti inoltre che i commit vengono visualizzati in ordine cronologico inverso, quindi l'ultimo commit viene visualizzato in quello e il primo commit nella prima riga e così via.
Aggiorna i messaggi: aggiorna il primo messaggio:
docs: Add created date and project title to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
salva e chiudi Modifica il secondo messaggio
docs: Add project description and included files to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
salva e chiudi.
Riceverai un messaggio come questo entro la fine del rebase: il Successfully rebased and updated refs/heads/documentation
che significa che hai successo. È possibile visualizzare le modifiche:
5dff827 (HEAD -> documentation) docs: Add project description and included files to the documentation "README.md"
4585c68 docs: Add created date and project title to the documentation "README.md"
eaf7978 (origin/master, origin/HEAD, master) Inital commit
46a5819 Create README.md
Vorrei che ciò potesse aiutare i nuovi utenti :).
Per me è stato per rimuovere alcune credenziali da un repository. Ho provato a riformare e ho incontrato un sacco di conflitti apparentemente non correlati lungo la strada quando provavo a ribattere: continuare. Non preoccuparti di tentare di rifare te stesso, usa lo strumento chiamato BFG (brew install bfg) su mac.
Se non hai già eseguito il push dei commit, puoi tornare a un commit precedente usando git reset HEAD^[1,2,3,4...]
Per esempio
git commit <file1> -m "Updated files 1 and 2"
git commit <file3> -m "Updated file 3"
Spiacenti, ho dimenticato di aggiungere file2 al primo commit ...
git reset HEAD^1 // because I only need to go back 1 commit
git add <file2>
Ciò aggiungerà file2 al primo commit.
Bene, questa soluzione potrebbe sembrare molto sciocca, ma può salvarti in determinate condizioni.
Un mio amico si è appena imbattuto accidentalmente in alcuni file enormi (quattro file generati automaticamente che vanno da 3 GB a 5 GB ciascuno) e poi ha fatto alcuni commit di codice aggiuntivo prima di rendersi conto del problema che git push
non funzionava più!
I file erano stati elencati .gitignore
ma dopo aver rinominato la cartella del contenitore, sono stati esposti e impegnati! E ora c'erano ancora alcuni commit del codice, ma push
funzionava per sempre (cercando di caricare GB di dati!) E alla fine falliva a causa dei limiti di dimensione del file di Github .
Il problema con rebase interattivo o qualcosa di simile era che avrebbero avuto a che fare con la ricerca di questi file enormi e che avrebbero impiegato un'eternità a fare qualsiasi cosa. Tuttavia, dopo aver trascorso quasi un'ora nella CLI, non eravamo sicuri che i file (e i delta) fossero effettivamente rimossi dalla cronologia o semplicemente non inclusi negli commit correnti. Neanche la spinta funzionava e il mio amico era davvero bloccato.
Quindi, la soluzione che ho trovato è stata:
~/Project-old
.~/Project
). cp -r
i file dalla ~/Project-old
cartella a~/Project
. mv
e inclusi.gitignore
correttamente. .git
cartella nella clonata di recente~/Project
di da quella precedente. Ecco dove vivono i registri della storia problematica!push
"redatte".Il problema più grande con questa soluzione è che si occupa della copia manuale di alcuni file e unisce tutti i recenti commit in uno (ovviamente con un nuovo commit-hash.) B
I grandi vantaggi sono che, è molto chiaro in ogni fase, funziona benissimo per file di grandi dimensioni (oltre a quelli sensibili) e non lascia alcuna traccia nella storia!