Mamy repozytorium Gita z ponad 400 zatwierdzeniami, z których pierwsze kilkadziesiąt to wiele prób i błędów. Chcemy uporządkować te zatwierdzenia, zgniatając wiele w jednym zatwierdzeniu. Oczywiście git-rebase wydaje się właściwą drogą. Mój problem polega na tym, że kończy się to konfliktami scalania, a te konflikty nie są łatwe do rozwiązania. Nie rozumiem, dlaczego w ogóle powinny istnieć jakieś konflikty, ponieważ po prostu zgniatam zatwierdzenia (nie usuwam ani nie zmieniam kolejności). Bardzo prawdopodobne, że to pokazuje, że nie do końca rozumiem, w jaki sposób git-rebase robi swoje squashy.
Oto zmodyfikowana wersja skryptów, których używam:
repo_squash.sh (to jest skrypt, który jest faktycznie uruchomiony):
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
repo_squash_helper.sh (ten skrypt jest używany tylko przez repo_squash.sh):
if grep -q "pick " $1
then
# cp $1 ../repo_squash_history.txt
# emacs -nw $1
sed -f ../repo_squash_list.txt < $1 > $1.tmp
mv $1.tmp $1
else
if grep -q "initial import" $1
then
cp ../repo_squash_new_message1.txt $1
elif grep -q "fixing bad import" $1
then
cp ../repo_squash_new_message2.txt $1
else
emacs -nw $1
fi
fi
repo_squash_list.txt: (ten plik jest używany tylko przez repo_squash_helper.sh)
# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g
Treść „nowej wiadomości” pozostawię waszej wyobraźni. Początkowo zrobiłem to bez opcji "--strategy theirs" (tj. Używając domyślnej strategii, która, jeśli dobrze rozumiem dokumentację, jest rekurencyjna, ale nie jestem pewien, która strategia rekurencyjna jest używana), i też nie. t działa. Powinienem również zwrócić uwagę, że używając zakomentowanego kodu w repo_squash_helper.sh, zapisałem oryginalny plik, na którym działa skrypt seda, i uruchomiłem skrypt seda, aby upewnić się, że robi to, co chciałem ( to było). Ponownie, nie wiem nawet, dlaczego miałby nastąpić konflikt, więc nie wydaje się, aby miało to duże znaczenie, która strategia zostanie zastosowana. Wszelkie rady lub spostrzeżenia byłyby pomocne, ale przede wszystkim chcę, aby to zgniatanie działało.
Zaktualizowano o dodatkowe informacje z dyskusji z Jefromi:
Przed rozpoczęciem pracy nad naszym ogromnym „prawdziwym” repozytorium użyłem podobnych skryptów w repozytorium testowym. To było bardzo proste repozytorium, a test działał bezproblemowo.
Komunikat, który otrzymuję, gdy się nie powiedzie, to:
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
To jest pierwszy wybór po pierwszym zatwierdzeniu squasha. Uruchomienie git status
daje czysty katalog roboczy. Jeśli następnie zrobię git rebase --continue
, po kilku kolejnych zatwierdzeniach otrzymam bardzo podobny komunikat. Jeśli zrobię to ponownie, po kilkudziesięciu zatwierdzeniach otrzymam kolejną bardzo podobną wiadomość. Jeśli zrobię to jeszcze raz, tym razem przejdzie przez około sto zatwierdzeń i wyświetli następujący komunikat:
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental
Jeśli potem ucieknę git status
, otrzymam:
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: repo/file_A.cpp
# modified: repo/file_B.cpp
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: repo/file_X.cpp
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: repo/file_Z.imp
„Oba zmodyfikowane” bit brzmi dla mnie dziwnie, ponieważ był to po prostu rezultat wybrania. Warto też zauważyć, że jeśli spojrzę na „konflikt”, to sprowadza się on do jednej linii, w której jedna wersja zaczyna się znakiem [tabulatora], a druga czterema spacjami. Brzmiało to tak, jakby mógł to być problem z konfiguracją mojego pliku konfiguracyjnego, ale nie ma w tym nic takiego. (Zauważyłem, że core.ignorecase jest ustawiony na true, ale najwyraźniej git-clone zrobił to automatycznie. Nie jestem tym całkowicie zaskoczony, biorąc pod uwagę, że oryginalne źródło znajdowało się na komputerze z systemem Windows).
Jeśli ręcznie naprawię plik_X.cpp, to niedługo potem nie powiedzie się z innym konfliktem, tym razem między plikiem (CMakeLists.txt), który według jednej wersji powinien istnieć, a jedną wersją nie powinien. Jeśli naprawię ten konflikt, mówiąc, że chcę tego pliku (co robię), kilka zatwierdzeń później pojawia się inny konflikt (w tym samym pliku), w którym teraz są raczej nietrywialne zmiany. To wciąż tylko około 25% drogi przez konflikty.
Powinienem również zwrócić uwagę, ponieważ może to być bardzo ważne, że projekt ten zaczynał się w repozytorium svn. Ta początkowa historia została najprawdopodobniej zaimportowana z repozytorium svn.
Aktualizacja nr 2:
Na próżno (pod wpływem komentarzy Jefromi) zdecydowałem się zmienić mój repo_squash.sh na:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
A potem po prostu zaakceptowałem oryginalne wpisy, tak jak jest. To znaczy, „rebase” nie powinno było niczego zmienić. Skończyło się na tych samych wynikach, które opisaliśmy wcześniej.
Aktualizacja nr 3:
Alternatywnie, jeśli pominę strategię i zamieniam ostatnie polecenie na:
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Nie mam już problemów z rebase typu „nic do popełnienia”, ale nadal pozostają mi inne konflikty.
Zaktualizuj za pomocą repozytorium zabawek, które odtwarza problem:
test_squash.sh (to jest plik, który faktycznie uruchamiasz):
#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================
#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt
git add test_file.txt
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..
#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================
#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
#========================================================
#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================
test_squash_helper.sh (używany przez test_sqash.sh):
# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
echo "Created two line file" > $1
fi
PS: Tak, wiem, że niektórzy z was wzdrygają się, kiedy widzicie, jak używam emacsa jako pomocniczego edytora.
PPS: Wiemy, że po rebase będziemy musieli usunąć wszystkie nasze klony z istniejącego repozytorium. (Zgodnie z wersetem „nie będziesz przebudowywać repozytorium po jego opublikowaniu”).
PPPS: Czy ktoś może mi powiedzieć, jak dodać do tego nagrodę? Nie widzę opcji nigdzie na tym ekranie, niezależnie od tego, czy jestem w trybie edycji, czy widoku.
źródło
rebase -p
)rebase --interactive
- jest to rodzaj listy działań, które ma wykonać git. Miałem nadzieję, że uda ci się zredukować to do pojedynczego squasha, który powoduje konflikty, i uniknąć całej dodatkowej złożoności skryptów pomocniczych. Inną brakującą informacją jest sytuacja, w której występują konflikty - kiedy git nakłada łaty, aby utworzyć squash, czy też gdy próbuje przejść dalej, poza squasha i nałożyć kolejną łatkę? (I czy na pewno nic złego się nie dzieje z twoją kludge GIT_EDITOR? Kolejny głos na prosty przypadek testowy.)Odpowiedzi:
W porządku, jestem na tyle pewny, że mogę rzucić odpowiedź. Może będę musiał to edytować, ale wydaje mi się, że wiem, na czym polega twój problem.
Twój przypadek testowy repozytorium zabawek zawiera scalenie - co gorsza, ma scalenie z konfliktami. A ty zbierasz się ponownie po scaleniu. Bez
-p
(co nie do końca działa-i
) scalenia są ignorowane. Oznacza to, że cokolwiek zrobiłeś w rozwiązywaniu konfliktów jest nie tam , kiedy próbuje do rebase cherry-pick następny popełnić, więc jego łata nie może stosować. (Uważam, że jest to pokazane jako konflikt scalania, ponieważgit cherry-pick
można zastosować łatkę, wykonując trójstronne scalenie między oryginalnym zatwierdzeniem, bieżącym zatwierdzeniem i wspólnym przodkiem).Niestety, jak zauważyliśmy w komentarzach,
-i
i-p
(zachowaj połączenia) nie dogadują się zbyt dobrze. Wiem, że edycja / przeredagowanie działa, a zmiana kolejności nie działa. Ja jednak wierzę , że to działa dobrze z kabaczki. Nie jest to udokumentowane, ale zadziałało w przypadku przypadków testowych, które opisuję poniżej. Jeśli twoja sprawa jest bardziej skomplikowana, możesz mieć wiele problemów z robieniem tego, co chcesz, chociaż nadal będzie to możliwe. (Morał tej historii: posprzątajrebase -i
przed połączeniem.)Załóżmy więc, że mamy bardzo prosty przypadek, w którym chcemy zgnieść razem A, B i C:
Teraz, jak powiedziałem, gdyby nie było konfliktów w X,
git rebase -i -p
działa zgodnie z oczekiwaniami.W przypadku konfliktów sprawy stają się nieco trudniejsze. Zrobi dobrze zgniatanie, ale kiedy spróbuje odtworzyć scalenie, konflikty pojawią się ponownie. Będziesz musiał rozwiązać je ponownie, dodać je do indeksu, a następnie użyć,
git rebase --continue
aby przejść dalej. (Oczywiście możesz rozwiązać je ponownie, sprawdzając wersję z oryginalnego zatwierdzenia scalania).Jeśli zdarzy się, że
rerere
włączone w Twojej repo (rerere.enabled
zestaw do true), będzie to sposób łatwiej - git będzie mógł ponownie skorzystać z ponownego Przewodowy ponowne rozwiązanie od kiedy pierwotnie miał konfliktów, a wszystko co musisz zrobić, to zbadaj aby upewnić się, że wszystko działa prawidłowo, dodaj pliki do indeksu i kontynuuj. (Możesz nawet pójść o krok dalej, włączającrerere.autoupdate
, a to doda je za Ciebie, więc scalenie nawet się nie powiedzie). Domyślam się jednak, że nigdy nie włączyłeś rerere, więc będziesz musiał sam rozwiązać konflikt. ** Lub możesz wypróbować
rerere-train.sh
skrypt z git-contrib, który próbuje "Prime [the] rerere bazy danych z istniejących merge commits" - w zasadzie sprawdza wszystkie merge commits, próbuje je scalić, a jeśli scalenie się nie powiedzie, przechwytuje wyniki i pokazuje jegit-rerere
. Może to być czasochłonne i nigdy z niego nie korzystałem, ale może być bardzo pomocne.źródło
-p
opcji.git commit -a -m"Some message"
igit rebase --continue
, i będzie to kontynuowane. Działa to nawet bez-p
opcji, ale działa jeszcze lepiej z tą-p
opcją (ponieważ nie robię żadnej zmiany kolejności, wydaje się, że-p
jest w porządku). Tak czy inaczej, będę cię informować.git config --global rerere.enabled true
igit config --global rerere.autoupdate true
przed uruchomieniem przykładu testowego rozwiązuje podstawowy problem. Co ciekawe, nie zachowuje on jednak scaleń, nawet przy określaniu--preserve-merges
. Jeśli jednak nie mam tych ustawionych i piszęgit commit -a -m"Meaningful commit"
igit rebase --continue
podczas określania--preserve-merges
, zachowuje to scalenia. W każdym razie dziękuję za pomoc w rozwiązaniu tego problemu!Jeśli nie masz nic przeciwko utworzeniu nowego oddziału, oto jak poradziłem sobie z problemem:
Będąc na głównym:
źródło
git diff new_clean_branch..original_messy_branch
powinieneś to zrobić, czy powinny być takie same? Dla mnie są różne.Szukałem podobnego wymagania, tj. Odrzucenia pośrednich zatwierdzeń mojej gałęzi programistycznej, zauważyłem, że ta procedura działa dla mnie.
na mojej gałęzi roboczej
viola połączyliśmy najnowsze zatwierdzenie z zatwierdzeniem początkowym gałęzi! Brak problemów z konfliktami scalania! W mojej praktyce uczenia się doszedłem na tym etapie do następującego wniosku: Czy istnieje lepsze podejście do tego celu.
źródło
Opierając się na świetnej odpowiedzi @ hlidka powyżej, która minimalizuje ręczną interwencję, chciałem dodać wersję, która zachowuje wszystkie nowe zatwierdzenia na serwerze głównym, których nie ma w gałęzi, do squasha.
Uważam, że można je łatwo utracić w
git reset
kroku w tym przykładzie.źródło
Zauważ, że
-X
opcje i strategii zostały zignorowane, gdy zostały użyte w interaktywnej rebase.Zobacz commit db2b3b820e2b28da268cc88adff076b396392dfe (lipiec 2013, git 1.8.4+),
Oznacza to, że
-X
strategia i strategia działają teraz z interaktywnym rebase, a także ze zwykłym rebase, a Twój początkowy skrypt może teraz działać lepiej.źródło
Miałem prostszy, ale podobny problem, w którym 1) rozwiązałem konflikt scalania w lokalnej gałęzi, 2) kontynuowałem pracę, dodając dużo więcej małych zatwierdzeń, 3) chciałem zmienić bazę i trafić w konflikty scalania.
U mnie
git rebase -p -i master
zadziałało. Zachował pierwotne zobowiązanie do rozwiązania konfliktu i pozwolił mi zmiażdżyć innych na szczycie.Mam nadzieję, że to komuś pomoże!
źródło