Jak zmodyfikować określony commit w git?

Zazwyczaj wysyłam listę commitów do przeglądu. Jeśli mam:

  • HEAD
  • Commit3
  • Commit2
  • Commit1

Wiem, że mogę modyfikować head commit za pomocą git commit --amend, ale jak mogę modyfikować Commit1, biorąc pod uwagę, że nie jest to HEAD zaangażować?

Author: Chris Martin, 2009-07-27

12 answers

Możesz użyć git rebase, na przykład, jeśli chcesz zmodyfikować z powrotem do commit bbc643cd, Uruchom

$ git rebase --interactive 'bbc643cd^'

W domyślnym edytorze zmodyfikuj pick na edit w linii, której commit chcesz zmodyfikować. Wprowadź zmiany, a następnie zatwierdź je tą samą wiadomością, którą miałeś wcześniej:

$ git commit --all --amend --no-edit

Aby zmodyfikować commit, a następnie

$ git rebase --continue

Aby powrócić do poprzedniego Head commit.

WARNING: zauważ, że spowoduje to zmianę SHA-1 tego commita , jak również wszystkich dzieci -- innymi słowy, to przepisuje historię od tego momentu do przodu. możesz złamać repos robiąc to jeśli wciśniesz polecenie git push --force

 2294
Author: ZelluX,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-14 14:14:16

Użyj niesamowitego interaktywnego rebase:

git rebase -i @~9   # Show the last 9 commits in a text editor

Znajdź odpowiedni commit, Zmień pick na e (edit), i zapisz i zamknij plik. Git będzie przewijał do tego commita, pozwalając na:

  • użyj git commit --amend do wprowadzania zmian lub
  • użyj git reset @~, aby odrzucić ostatni commit, ale nie zmiany w plikach (tzn. przeniesie Cię do punktu, w którym edytowałeś pliki, ale jeszcze nie zatwierdziłeś).

Ten ostatni jest przydatny do wykonywania bardziej złożonych takie rzeczy jak dzielenie na wiele commitów.

Następnie uruchom git rebase --continue, a Git odtworzy kolejne zmiany na Twoim zmodyfikowanym commicie. Możesz zostać poproszony o naprawienie niektórych konfliktów scalania.

Uwaga: @ jest skrótem od HEAD, a {[10] } jest zatwierdzeniem przed podanym zatwierdzeniem.

Przeczytaj więcej o przepisywaniu historii w dokumentacji Git.


Nie bój się rebase

ProTip™: nie bój się eksperymentowanie z" niebezpiecznymi " poleceniami, które przepisują historię* - Git domyślnie nie usuwa Twoich commitów przez 90 dni; możesz je znaleźć w reflogu:

$ 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

* uważaj jednak na opcje takie jak --hard i --force - mogą one odrzucić dane.
* nie zapisuj również historii w żadnych gałęziach, nad którymi współpracujesz.



Na wielu systemach, git rebase -i domyślnie otworzy Vima. Vim nie działa jak większość nowoczesnych edytorów tekstu, więc zobacz jak rebase używać Vim . Jeśli wolisz używać innego edytora, zmień go za pomocą git config --global core.editor your-favorite-text-editor.

 319
Author: Zaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:24

Interactive rebase with --autosquash jest czymś, czego często używam, gdy muszę naprawić poprzednie commity głębiej w historii. Zasadniczo przyspiesza proces, który ilustruje odpowiedź ZelluX, i jest szczególnie przydatny, gdy masz więcej niż jeden commit, który musisz edytować.

Z dokumentacji:

--autosquash

Gdy komunikat dziennika zmian zaczyna się od " squash! ... "(lub "fixup! ... "), a istnieje commit, którego tytuł zaczyna się od tego samego …​, automatycznie Modyfikuj listę todo rebase-i tak, aby commit oznaczony do squashowania pojawił się zaraz po commicie, który ma zostać zmodyfikowany

Załóżmy, że masz historię, która wygląda tak:

$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1

I masz zmiany, które chcesz zmienić w commit 2, a następnie zatwierdź zmiany za pomocą

$ git commit -m "fixup! Commit2"

Alternatywnie możesz użyć commit-sha zamiast komunikatu commit, więc "fixup! e8adec4 lub nawet tylko prefiks komunikatu commit.

Następnie zainicjuj interaktywną rebazę na commit przed

$ git rebase e8adec4^ -i --autosquash

Twój edytor otworzy się z poprawnym zatwierdzeniem

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

Wszystko, co musisz zrobić, to zapisać i zakończyć

 60
Author: thrau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-29 17:59:45

Run:

$ git rebase --interactive commit_hash^

Każdy ^ wskazuje, ile commitów chcesz edytować, jeśli jest to tylko jeden (hash commitów, który podałeś), to po prostu dodajesz jeden ^.

Używając Vima zmieniasz słowa pick na reword dla commitów, które chcesz zmienić, Zapisz i zakończ(:wq). Następnie git wyświetli monit z każdym zatwierdzeniem, które zostało oznaczone jako przeredagowanie, abyś mógł zmienić wiadomość zatwierdzania.

Każda wiadomość commit musisz zapisać i zamknąć (:wq) aby przejść do następnego commit message

Jeśli chcesz wyjść bez stosowania zmian, naciśnij :q!

EDIT: aby nawigować w vim, używasz j, aby przejść do góry, k, aby przejść do dołu, h, aby przejść do lewej i l, aby przejść do trybu NORMAL). Aby edytować tekst, naciśnij i, aby przejść do trybu INSERT, w którym wstawiasz tekst. Naciśnij ESC, aby wrócić do trybu NORMAL:)

UPDATE : oto świetny link z github listing jak cofnąć (prawie) wszystko z git

 31
Author: betoharres,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 19:45:40

Jeśli z jakiegoś powodu nie lubisz edytorów interaktywnych, możesz użyć git rebase --onto.

Powiedz, że chcesz zmodyfikować Commit1. Najpierw gałąź z przed Commit1:

git checkout -b amending [commit before Commit1]

Drugi, chwyć Commit1 z cherry-pick:

git cherry-pick Commit1

Teraz popraw swoje zmiany, tworząc Commit1':

git add ...
git commit --amend -m "new message for Commit1"

I na koniec, po ukryciu wszelkich innych zmian, przenieś resztę commitów do master na wierzch nowy commit:

git rebase --onto amending Commit1 master

Powinno być: "rebase, na gałęzi amending, wszystkie committs between Commit1 (non-inclusive) and master (inclusive)". Oznacza to, że commit 2 I commit 3, całkowicie odcinając Stary commit 1. Możesz je po prostu wybrać, ale ten sposób jest łatwiejszy.

Pamiętaj, aby oczyścić swoje gałęzie!

git branch -d amending
 13
Author: FeepingCreature,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-22 12:19:44

Przyszedł do tego podejścia (i jest to prawdopodobnie dokładnie to samo, co za pomocą interaktywnego rebase), ale dla mnie jest to dość proste.

Uwaga: przedstawiam to podejście ze względu na ilustrację tego, co można zrobić, a nie codzienną alternatywę. Ponieważ ma wiele kroków (i ewentualnie pewne zastrzeżenia.)

Powiedz, że chcesz zmienić commit 0 i jesteś aktualnie na feature-branch

some-commit---0---1---2---(feature-branch)HEAD

Sprawdź ten commit i utwórz quick-branch. Możesz również sklonować swoją funkcję gałąź jako punkt regeneracji (przed rozpoczęciem).

?(git checkout -b feature-branch-backup)
git checkout 0
git checkout -b quick-branch

Teraz będziesz miał coś takiego:

0(quick-branch)HEAD---1---2---(feature-branch)

Zmiany sceny, Schowaj Wszystko inne.

git add ./example.txt
git stash

Zatwierdź zmiany i Kasuj z powrotem do feature-branch

git commit --amend
git checkout feature-branch

Teraz będziesz miał coś takiego:

some-commit---0---1---2---(feature-branch)HEAD
           \
             ---0'(quick-branch)

Rebase feature-branch na quick-branch (rozwiąż wszelkie konflikty po drodze). Apply stash and remove quick-branch.

git rebase quick-branch
git stash pop
git branch -D quick-branch

I kończy się na:

some-commit---0'---1'---2'---HEAD(feature-branch)

Git nie powieli (chociaż I nie mogę powiedzieć w jakim stopniu) 0 commit podczas rebasingu.

Uwaga: wszystkie skróty zmian są zmieniane począwszy od zmiany, którą pierwotnie zamierzaliśmy zmienić.

 6
Author: Olga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-23 16:51:41

Całkowicie nieinteraktywne polecenie(1)

Pomyślałem, że podzielę się pseudonimem, którego do tego używam. Opiera się na nieinteraktywnej interaktywnej rebase. Aby dodać go do swojego Gita, uruchom to polecenie (Wyjaśnienie podane poniżej):

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'

Największą zaletą tego polecenia jest fakt, że jest to no-vim .


(1)biorąc pod uwagę, że nie ma konfliktów podczas rebase, oczywiście

Użycie

git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111

The Nazwa amend-to wydaje się odpowiednia IMHO. Porównaj przepływ z --amend:

git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>

Wyjaśnienie

  • git config --global alias.<NAME> '!<COMMAND>' - tworzy globalny alias git o nazwie <NAME>, który wykona polecenie inne niż git <COMMAND>
  • f() { <BODY> }; f - "anonimowa" funkcja bash.
  • SHA=`git rev-parse "$1"`; - Konwertuje argument do git revision i przypisuje wynik do zmiennej SHA
  • git commit --fixup "$SHA" - fixup-commit dla SHA. Zobacz też git-commit docs
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^" część została pokryta innymi odpowiedziami.
    • --autosquash jest to, co jest używane w połączeniu z git commit --fixup, zobacz git-rebase docs aby uzyskać więcej informacji
    • To sprawia, że całość nie jest interaktywna. Tego hacka nauczyłem się Z tego posta na blogu .
 6
Author: Dethariel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-27 01:47:51

Aby uzyskać polecenie nieinteraktywne, umieść skrypt z tą zawartością w ścieżce:

#!/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"

Użyj go przez inscenizację zmian (za pomocą git add), a następnie uruchom git fixup <commit-to-modify>. Oczywiście nadal będzie interaktywny, jeśli pojawią się konflikty.

 5
Author: Pelle Nilsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-16 15:27:09

Na podstawie dokumentacji

Zmiana wiadomości starszych lub wielu komunikatów commit

git rebase -i HEAD~3 

Powyższe wyświetla listę ostatnich 3 commitów w bieżącej gałęzi, Zmień 3 na coś innego, jeśli chcesz więcej. Lista będzie wyglądać podobnie do:

pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

Zastąp pick z przeredaguj przed każdą wiadomością commit, którą chcesz zmienić. Powiedzmy, że zmienisz drugi commit na liście, Twój plik będzie wyglądał jak "po": {]}

pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

Zapisz i zamknij plik listy zatwierdzeń, pojawi się nowy edytor, aby zmienić wiadomość zatwierdzania, zmienić wiadomość zatwierdzania i zapisać.

Ostatecznie Wymuś-wciśnij zmienione commity.

git push --force
 2
Author: justMe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-17 08:38:57

Dla mnie to było za usunięcie niektórych poświadczeń z repo. Próbowałem rebasingu i napotkałem mnóstwo pozornie niepowiązanych konfliktów po drodze, próbując rebase -- Kontynuuj. Nie trudź się próbując rebase siebie, użyj narzędzia o nazwie BFG (brew install bfg) na mac.

 1
Author: Pellet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-07 07:24:20

I solved this,

1) tworząc nowy commit ze zmianami, które chcę..

r8gs4r commit 0

2) wiem, który commit muszę z nim połączyć. czyli commit 3.

Więc git rebase -i HEAD~4 # 4 reprezentuje ostatnie 4 commit (tutaj commit 3 jest na 4 miejscu)

3) w interaktywnym rebase Ostatnie zatwierdzenie będzie umieszczone na dole. będzie wyglądać podobnie,

pick q6ade6 commit 3
pick vr43de commit 2
pick ac123d commit 1
pick r8gs4r commit 0

4) tutaj musimy zmienić commit, jeśli chcemy połączyć się z konkretnym. powinno być tak:

parent
|_child

pick q6ade6 commit 3
f r8gs4r commit 0
pick vr43de commit 2
pick ac123d commit 1

Po przestawianiu Ciebie trzeba wymienić p pick z f (fixup połączy się bez komunikatu commit) lub s (squash merge with commit message can change in run time)

A następnie zapisać swoje drzewo.

Teraz połącz z istniejącym commitem.

Uwaga: nie jest to preferowana metoda, chyba że utrzymujesz ją samodzielnie. jeśli masz duży rozmiar zespołu to nie jest akceptowalna metoda przepisywania Gita drzewo skończy w konfliktach, które znasz inne zwyczaje. jeśli chcesz na utrzymanie drzewa czyste z mniej commitów może spróbować tego i jeśli jego mały zespół w przeciwnym razie nie jest preferowany.....

 1
Author: Mohideen ibn Mohammed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-05 18:35:50

Automatyczna, interaktywna edycja rebase, po której następuje commit revert gotowy do powtórzenia

Znalazłem się naprawiając poprzedni commit na tyle często, że napisałem do niego skrypt.

Oto przebieg pracy:

  1. git commit-edit <commit-hash>
    

    Spowoduje to usunięcie zmian, które chcesz edytować.

  2. Popraw i ustaw commit tak, jak chcesz, aby był na pierwszym miejscu.

    (możesz użyć git stash save, Aby zachować pliki, których nie jesteś committing)

  3. Powtórz commit za pomocą --amend, np:

    git commit --amend
    
  4. Uzupełnij rebase:

    git rebase --continue
    

Aby powyższy skrypt zadziałał, umieść poniższy skrypt w pliku wykonywalnym o nazwie git-commit-edit gdzieś w twoim $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
 1
Author: Tom Hale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-14 04:41:11