Zmiana nazwy commita

0

Pomoże ktoś w walce z gitem? Problem niby prosty, a męczę się z nim od wczoraj..

Załóżmy, że mamy taką sytuację:

  1. Inicjalizuję nowe repozytorium i przechodzę do brancha master
  2. Dodaję nowy plik file, oraz commituję zmiany
  3. Przechodzę do brancha branch1
  4. Usuwam plik file, a następnie commituję zmiany
  5. Przechodzę do brancha master
  6. Modyfikuję plik file a następnie commituję zmiany
  7. Merguję branch branch1 w wyniku czego otrzymuję konflikt, ale rozwiązuję go - chcę zachować plik, więc commituję zmiany

Zrobiłem więc to:

git init
touch file
git add file
git commit -m 'add file'
git checkout -b 'branch1'
rm file
git add file
git commit -m 'delete file'
git checkout master
echo 'content' > file
git add file
git commit -m 'edit file'
git merge 'branch1'
git add file
git commit -m 'solve conflict'

Historia gita wygląda następująco:

38a9e5a (HEAD -> master) resolve conflict
4d07d5f edit file
8f7a049 (branch1) delete file
40db0ce add file

Teraz, chciałbym zmienić wiadomość commita 40db0ce. W internecie przeczytałem, że można w tym celu wykorzystać polecenie git rebase. W takim razie uruchamiam:

git rebase -i --root

w wyniku czego otrzymuję:

pick 40db0ce add file
pick 4d07d5f edit file
pick 8f7a049 delete file

zmieniam to na:

reword 40db0ce add file
pick 4d07d5f edit file
pick 8f7a049 delete file

Zatwierdzam, wprowadzam nową wiadomość commita i znowu zatwierdzam. Otrzymuję błąd:

error: could not apply 8f7a049... delete file
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 8f7a049... delete file
CONFLICT (modify/delete): file deleted in 8f7a049 (delete file) and modified in HEAD. Version HEAD of file left in tree.

Czytam internet i okazuje się, że domyślnie git rebase nie wie w jaki sposób rozwiązać konflikty. Można uruchomić polecenie git rerere, by git wiedział, by przy występowaniu konfliktów w rebase rozwiązać je tak, jak były rozwiązane wcześniej. Uruchamiam więc w konsoli:

git config --global rerere.enabled 1

Próbuję jeszcze raz wykonać zmianę wiadomości commita, ale otrzymuję ten sam błąd. Czytam internet i okazuje się, że tak skonfigurowane rerere zadziała dla przyszłych commitów. Aby zadziałało dla starych, należy uruchomić skrypt, który "nauczy" gita w jaki sposób zostały rozwiązane przeszłe konflikty. Uruchamiam więc skrypt rerere-train.sh:

./rerere-train.sh --all

Ponownie próbuję zmienić wiadomość commita, ale otrzymuję ten sam błąd. Zadałem pytanie na stackoverflow i otrzymałem odpowiedź, że konflikt polegający na modyfikacji czy usunięciu to inny typ konfliktu i git rerere sobie z nim nie poradzi. No ja pierd***, to jak mam w końcu zmienić nazwę tego commita?

Nie bardzo też rozumiem jak to ma w praktyce działać - załóżmy, że orientujemy się, że musimy zmienić nazwę commita, który został wrzucony rok temu. Rerere nie poradzi sobie z rozwiązywaniem konfliktów modyfikacji, więc jeszcze raz trzeba rozwiązywać wszystkie konflikty sprzed roku?

7
  1. Po rebase musisz zrobić push force bo przecież właśnie zmieniłeś historie repozytorium i chcesz nadpisać remote swoim lokalnym stanem.
  2. Nikt normalny nie bawi sie w zmienianie nazwy commita i w ogóle generalnie "zmiana historii" to bardzo zły pomysł i w 99% przypadków w ogóle nie mozesz czegoś takiego zrobić bo nie da się zrobić push force na mastera bo będzie protected, wiec całe to twoje ćwiczenie nie ma żadnego sensu.
  3. Problem w tym że nie ma czegoś takiego jak zmiana nazwy commita. A zmiana samego commita sprzed roku to jednak hardkor, bo co się niby ma stać ze wszystkimi zmianami które były bazowane na tym co właśnie zmieniłeś? :D Jak by to miało niby działać?

W praktyce robi sie tak:

  1. Developujesz jakims swoim branchu, robisz z nim co chcesz, push force itd
  2. Jak juz jest skończony to robisz squash tego brancha i mergujesz do mastera jeden zbiorczy commit z "ficzerem"
  3. Profit!
1

@Shalom:

  1. Ale mi ten rebase się w ogóle nie udaje
  2. No dobra, to zamiast nazwy commita weźmy inny przykład - zespół się orientuje, że 2 lata temu ktoś zacommitował hasło. Trzeba się tego commita pozbyć. Robimy rebase i rozwiązujemy konflikty z 2 lat?
  3. Chodzi o wiadomość commita :P Ogólnie dziwi mnie to, że to takie skomplikowane. Przecież ta wiadomość to nie żaden identyfikator, tylko zwykły string
1
  1. Nie dziwota, bo nie do końca rozumiesz co robisz. Zacznijmy od tego że rebase służy doładnie do tego co się dzieje - służy do zaaplikowana wszystkich commitów z historii danego brancha, jeden po drugim, na górę innego brancha (nasza nowa baza). Czyli np. odbiłeś brancha od mastera i zrobiłeś w nim jakieś zmiany, ale w międzyczasie ktos pushnął coś do mastera i teraz u ciebie w lokalnym repo po commicie X z mastera jest twój commit Y, a na remote masterze po commicie X z mastera jest commit Z kolegi z biurka obok. Trzeba to jakoś "wyprostować" i jedna z opcji to właśnie rebase, gdzie chcesz zmienić bazę z której wystartował twój branch. Zamiast startować z X chcesz zeby startował z Z i chcesz zaaplikować wszystkie commity ze swojego brancha (czyli commit Y) na nową bazę (czyli na stan repo gdzie jest commit X i Z).
  2. W ogóle nie należy tak robić. Zmieniacie to hasło i żyjecie dalej. Próba usunięcia tego commita z historii nie tylko będzie potencjalnie ciężka ale też w ogóle nie ma sensu. Po pierwsze co z ludźmi którzy w ciągu ostatnich 2 lat klonowali to repo? Oni wszyscy to hasło mają w swojej lokalnej kopii. Ba, jest nawet jeszcze gorzej! Bo to że usuniesz commit z jakiegoś brancha wcale nie oznacza ze ten commit znika! Po prostu nie ma do niego żadnych "odwołań" ale w repo jak najbardziej nadal się znajduje i można go tam spokojnie znaleźć za pomocą np. jakiegoś refloga. Ale żeby wyjaśnić ci gdzie popełniasz błąd myślowy: Co jeśli ten commit który chcesz usunąć dodaje jakiś plik który następnie jest bazą dla setek kolejnych commitów? Co sie niby ma automatycznie stać? :D Nie oglądałeś nigdy jakiegoś filmu o podróżach w czasie? Bo to co robisz to jest dokładnie to: chcesz się cofnać w czasie i zmienić historie :)
  3. Git jest tak zaprojektowany że ten commit msg nie jest jakąś zewnętrzną metadaną którą możesz sobie zmodyfikować. To jest część zmian w danym commicie :)
1

Wiadomosc comita mozesz sobie zmienic jak na lokalu cos zacomitowales i jeszcze nie wypchnales do repo, bo np nie wiem jakas literowke zrobiles czy cos, jak zrobisz push i jeszcze dodatkowo (jezeli robice) juz po mergu do brancza glownego to dupa zimna. Zrobice soibe githooki jezeli jakis syntax comita macie miec

0

Są w zasadzie 3 podejścia do prowadzenia git loga.

  • commitowanie byle gówna pod nazwą commita (zawsze "small changes"), nie używanie squasha i rebase,
  • używanie popularnych konwencji nazywania commitów, squashowanie i robienie rebaseów,
  • modyfikowanie gitloga w taki sposób, żeby odzwierciedlał faktycznie scrumowy proces dodawania nowych ficzerów.

Kiedyś bym powiedział, że nie modyfikuje się gitloga bo coś tam. Ostatnio był tutaj wątek o branchless git i zmieniłem zdanie. W zależności od konwencji w zespole możesz chcieć jednak zmodyfikować historię.

I teraz rozwiązanie problemu. Modyfikujesz pierwszy commit i w tym przypadku zrobienie rebase'a jest trochę bardziej skomplikowane. Kiedyś to robiłem. Z tego co sobie przypomniałem po lekturze: https://stackoverflow.com/a/2322798/3628952 tagujesz rootowy commit, przepinasz się na niego i robisz nowego brancha, amendujesz rootowy commit, przepinasz się na mastera, git rebase --onto new-root root - to podmienia tego rootowego commita.

0

W tym przypadku problemem jest git rebase (i cherry-pick) odtwarzający zmiany, mimo, że co do zasady git operuje stanami.

Tu zadziała git filter-repo.
Jeśli repozytorium nazywa się repo, to przepisanie historii wygląda następująco:

mkdir ../kopia-repo
cd ../kopia-repo
git clone ../repo
cd repo
echo 'add==>create' > ../reword.txt
git filter-repo --replace-message ../reword.txt
git log --oneline -p
*   39c25fd (HEAD -> master) resolve
|\  
| * af1044b (br1) delete
| | diff --git a/file b/file
| | deleted file mode 100644
| | index 7898192..0000000
| | --- a/file
| | +++ /dev/null
| | @@ -1 +0,0 @@
| | -a
* | 9a79a99 edit
|/  
|   diff --git a/file b/file
|   index 7898192..6178079 100644
|   --- a/file
|   +++ b/file
|   @@ -1 +1 @@
|   -a
|   +b
* 4ef0783 create
  diff --git a/file b/file
  new file mode 100644
  index 0000000..7898192
  --- /dev/null
  +++ b/file
  @@ -0,0 +1 @@
  +a

1 użytkowników online, w tym zalogowanych: 0, gości: 1