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.
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.)
rebase -p
)