Niestandardowa zmiana historii gita.

0

Mam sobie takie repo:
screenshot-20201221171852.png

Co bym chciał zrobić: wziąć zmiany które weszły w ostatnim commicie i dołączyć do któregoś z wczesniejszych commitów np do "pierwszy", a ten ostatni commit usunąć.
Robie więc:

git rebase -i 37f8

daje fixup i zmieniam kolejnosc tego ostatniego commita:

screenshot-20201221172345.png

Robie pusha z force i spoko, (prawie działa), mam dwa problemy:

Jeśli (a tak najprawdopodobniej będzie) w tym ostatnim commicie (który chce usunąć a zmiany z niego wcisnąć wcześniej) będą zmiany w plikach które tez będą w innych commitach to oczywiscie dostane konflikty. Ok, roziązuje konflikty notatnikiem (póki co), daje git rebase --continue i znowu mi wskakuje to okienko od vima i nazwą pierwszego commita, nie wiem co wtedy, po zapisie dalej mi sie pluje ze mam konflikt :/

Druga sprawa, chce to robic wszystko skryptem, tzn zrobic to z wiersza polecen nie uzywajac interaktywnego rebase. Czyli podac do skryptu 2 hashe commitów, jeden z ktorego zmiany będą doklejone do drugiego a ten pierwszy usunąć.

Podsumowująć, jak poradzic sobie z konfliktami (chce po prostu kolejny commit po tym moim "pierwszym" nadpisal zmiany w skonfliktowanych linijkach)? Dwa, jak to zrobic bez interaktywnego rebase?

0

roziązuje konflikty notatnikiem (póki co)

Do rozwiązania konfliktów polecam IntelliJ, emacsowy magit bądź vimdiff (w takiej kolejności) - rozwiązywanie konfliktów poprzez ręczne grzebanie w plikach jest na dłuższą metę nieopłacalne oraz błędogenne.

i znowu mi wskakuje to okienko od vima i nazwą pierwszego commita, nie wiem co wtedy, po zapisie dalej mi sie pluje ze mam konflikt :/

git add, git commit i dopiero potem git rebase --continue.

chce to robic wszystko skryptem

Brzmi niespodziewanie - co próbujesz zrobić, tak wysokopoziomowo?

1

W sensie chcesz zrobić squash ale już po fakcie? Czemu nie zrobisz reset mixed przed ten commit do którego chcesz dodać dalsze zmiany i potem commit wszystkich zmian + push force?
A jeszcze lepiej: rób zmiany w branchu i potem rób squash merge wracając do mastera...

0

@Patryk27: o wlasnie, czegos mi tu brakowalo.. zaraz sprawdze jak to wyglada.

Ogólnie to próbuje w CI podnosić wersje projektu na podstawie nazwy commita. Robie tak:
Przed buildem parsuje message commita, podnosze w csproju na odpowiedniej pozycji. Po buildzie i pomyslnych testach musze zakomitowac te zmiane wersji w zdalnym repo. Ale w miedzyczasie ktos inny moze zrobic commita do repo wiec nie chce zawsze ammendować do ostatniego commita tylko do tego od którego zaczął sie build (tak wiem, amendowac mozna tylko ostatni commit)

@Shalom nie wiem czy mnie dobrze zrozumiales, musze sobie chwile dać na przeanalizowanie co napisales :)

0

nie wiem czy mnie dobrze zrozumiales

Źle bo mogłeś od razu zacząć od tego twojego use-case ;) W takim wypadku ja bym robił coś w stylu: branch, podbicie wersji a po buildzie i testach merge tego brancha do mastera. Jeśli faktycznie zmieniasz tylko wersje to nie będzie konfliktów i po problemie.

0

@kzkzg: jak potrzebujesz --interactive w CI to możesz nadpisać zmienną GIT_SEQUENCE_EDITOR np. GIT_SEQUENCE_EDITOR='sed -i jakies_czary' git rebase -i branch

kzkzg napisał(a):

@Patryk27: o wlasnie, czegos mi tu brakowalo.. zaraz sprawdze jak to wyglada.

Ogólnie to próbuje w CI podnosić wersje projektu na podstawie nazwy commita. Robie tak:
Przed buildem parsuje message commita, podnosze w csproju na odpowiedniej pozycji. Po buildzie i pomyslnych testach musze zakomitowac te zmiane wersji w zdalnym repo. Ale w miedzyczasie ktos inny moze zrobic commita do repo wiec nie chce zawsze ammendować do ostatniego commita tylko do tego od którego zaczął sie build (tak wiem, amendowac mozna tylko ostatni commit)

A musisz tak robić, czy tak może wymyśliłeś? Jak chcesz łączyć wersjonowanie z gitem to najprościej i najczytelniej będzie użyć hash ostatniego commita np przy użyciu gitattributes https://stackoverflow.com/questions/1792838/how-do-i-enable-the-ident-string-for-a-git-repository

1

Zrób sobie od zera ćwiczebne repo

mkdir repo; cd repo; git init 

Dodaj np. 5 dummy-commitów

for I in {1..5}; do touch File_$I.txt; git add .; git commit -m"Add File_$I"; done

Dodaj jeszcze plik np. cherry.txt - to ten o który ci chodzi

touch cherry.txt; git add .; gi commit -m"add cherry"

masz gotowe do zabawy:

repo (master) $ git log --oneline
d06625e (HEAD -> master) add cherry
64ca0f7 Add File_5
884529b Add File_4
cd29f59 Add File_3
66796ec Add File_2
2899507 Add File_1

Bazą niech będzie commit z File_2, robisz wiszący - detached - checkout do niego i branch z niego, nazwijmy ją base

repo (master) $ git checkout 66796ec
repo ((HEAD detached at 66796ec)) $ git checkout -b base
Switched to a new branch 'base'

Teraz cherry-pick z interesującego cię commit w którym dodałeś plik cherry.txt i już masz cherry.txt w branch base

repo (base) $ git cherry-pick d06625e
repo (base) $ ls
cherry.txt  File_1.txt	File_2.txt

repo (base) $ git status -s
repo (base) $ 

repo (base) $ git checkout master 
Switched to branch 'master'

I teraz robisz albo reset --hard do commit z File_2 i merge base do master

repo (master) $ git log --oneline
d06625e (HEAD -> master) add cherry
64ca0f7 Add File_5
884529b Add File_4
cd29f59 Add File_3
66796ec Add File_2
2899507 Add File_1

repo (master) $ git reset --hard 66796ec
HEAD is now at 66796ec Add File_2
repo (master) $ git merge base 
Updating 66796ec..0489e6e
Fast-forward
 cherry.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 cherry.txt

repo (master) $ git log --oneline
0489e6e (HEAD -> master, base) add cherry
66796ec Add File_2
2899507 Add File_1

repo (master) $ ls
cherry.txt  File_1.txt	File_2.txt

I masz co chyba? chciałeś (File_1, File_2 cherry).

PS Zakładam, że tu dla uproszczenia master to w rzeczywistości jakiś branch jaki został stworzony .
Zaczynasz coś robić, to zaczynasz od checkout -b anotherfeature

0

@Shalom: ale jeśli podczas build dojdzie jakiś inny commit od innej osoby to potem ten merge będę miał do tego ostatniego commita a nie do tego od którego zaczął się Build. Chyba że da się zmergowac do tego pierwszego?

@BraVolt no chyba tak średnio te twoje rozwiązanie, a co plikami które zostały później dodane tj 3,4,5?

0

@kzkzg ale jaki sens miałoby takie mergowanie o którym piszesz? o_O Jeśli bardzo potrzebujesz zeby historia tak wyglądała to bez rebase się nie obędzie (a wtedy jakieś automatycznie rozwiązywanie konfliktów zawsze może coś zrypać), ale moim zdaniem robisz jakieś kombinacje alpejskie które absolutnie nie mają sensu.
Zresztą bierzesz pod uwagę sytuacje że który z tych kolejnych commitów nie będzie działać po tym jak ty zaaplikujesz te swoje zmiany? Juz nawet nie wspominam o race condition :D Co jak pójdą dwa commity i dwa buildy jedocześnie? I jeszcze ci się automatycznie zaaplikują w złej kolejnosci? :D
Czemu nie robisz jak normalny człowiek tagów albo jakiegoś specjalnego brancha pod wersje?

0

2 buildy równocześnie nie pójdą, drugi będzie czekał na zakończenie pierwszego

Widziałem u javowcow takie rozwiązanie, tam chyba też wersja jest po prostu w pliku przechowywana? Wersja się podbija na podstawie commita i jest ladna prosta historia repo, bez merge commitow. Tylko ze u nich jest to robione w pipelinie Jenkins owym, ją chce to zrobić w Build Cake.

0
kzkzg napisał(a):

co plikami które zostały później dodane tj 3,4,5?

Jak nie chcesz usuwać swoich plików z 3,4,5, to interactive rebase albo squash. Tylko nie wiem czy gra warta świeczki, jak pracuje się na stworzonym do tego branch. Jak to jakiś drobny fix to zrobisz na koniec squash i wszystko będzie w jednym commit . Albo zrobisz pull request a właściciel i tak robi squash bez pytania cię o to.

Dopóki pracujesz nad feature to nie zajmuj się kolejnością commitów bo po co? Skończyłeś, przeszło testy, to pewnie cały feature zasługuje na jeden commit żeby nie mącić hstorii.

0
kzkzg napisał(a):

Co bym chciał zrobić: wziąć zmiany które weszły w ostatnim commicie i dołączyć do któregoś z wczesniejszych commitów np do "pierwszy", a ten ostatni commit usunąć.

Dałbym po prostu git cherry-pick hashe kolejnych commitów

Jeśli już koniecznie trzeba jakiś commit połączyć z drugim to na nim zrobiłbym cherry-pick z flagą --no-commit i potem git amend, a potem dalsze cherry picki. Ale w praktyce bym to olał i nie łączył.

0

Jedna linijka w bash, podobna do poniższej, i można ćwiczyć do woli i kombinować na prostym repo (plus ze dwa razy użyć git reflog po nieudanym eksperymencie)

for I in {1..5}; do touch file_$I.txt; git add .; git commit -m"Add file_$I"; done

Albo ręcznie dłużej ale raz i klonować z githuba

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