Really flatten a git merge
Jest kilka pytań o "spłaszczenie merge" w StackOverflow, a odpowiedź zwykle brzmi "Git rebase". Te odpowiedzi choć brakuje jednego kluczowego punktu-kolejność commitów.
Załóżmy, że istnieje gałąź A z commitami z 1 czerwca i 1 sierpnia oraz gałąź B z commitem z 1 lipca (UPDATE w celu przywrócenia bazy opisanej poniżej: gałęzie są w pełni niezależne i nie mają wspólnego przodka, na przykład pochodzą z 2 różnych repozytoriów). Podczas łączenia B W A, będzie następująca historia (na git log):
Merged branch 'B'
Aug 1
Jul 1
Jun 1
Teraz szukam sposobu na uzyskanie tego samego wyniku, ale bez commitów scalających(a więc z bazową historią liniową w tej kolejności, i tak, to oznacza ponowne rodzicielstwo commitów). Git rebase nie pomaga tutaj, ponieważ z nim, otrzymasz następujące historie:
Jul 1
Aug 1
Jun 1
Lub
Aug 1
Jun 1
Jul 1
Innymi słowy, Git rebase zawsze układa jedną gałąź na drugiej, podczas gdy ja szukam rozwiązania, które przeplata commity posortowane według daty zatwierdzenia autora.
Najwyraźniej, w prostych przypadkach, potrzebny układ można osiągnąć poprzez ręczne postprocesowanie git rebase za pomocą git rebase-i, ale to nie jest praktyczne dla dużych historii, więc szukałbym zautomatyzowanych poleceń / skryptów.
Usecase? Jeśli A i B reprezentują różne części tego samego projektu, które zdarzały się być w różnych repozytoriach i nadszedł czas, aby to naprawić, łącząc je ze sobą, to naturalne jest, aby liniowa historia rozwijała się w rzeczywista kolejność rozwoju.
4 answers
Po pewnym namyśle, wymyśliłem jak zrobić Jak uruchomić git rebase -- interactive w sposób nieinteraktywny?, który również zapewnia całkowicie Skryptowe rozwiązanie tego pytania.
1. przenoszenie 2 gałęzi z różnych repozytoriów do jednego repozytorium (git remote add + git fetch)
2. Rebase (nieinteraktywnie) jedna gałąź na drugiej (kolejność ma znaczenie, rozważ pierwszy commit z której gałęzi chcesz mieć jako pierwszy commit oddział skonsolidowany).
3. przygotuj następujący skrypt (rebase-reoder-by-date
):
#!/bin/sh
awk '
/^pick/ {
printf "%s %s ", $1, $2;
system("echo -n `git show --format='%ai' -s " $2 "`");
for (i = 3; i <= NF; i++) printf " %s", $i; printf "\n";
}
' $1 | sort -k3 > $1.tmp
mv $1.tmp $1
4. Run: GIT_SEQUENCE_EDITOR=./rebase-reoder-by-date git rebase -i <initial commit>
Zastrzeżenie: Wszystkie te operacje powinny mieć miejsce na kopiach oryginalnych repozytoriów, przejrzyj/sprawdź / przetestuj połączoną gałąź, aby upewnić się, że jest to, czego oczekujesz i Zawiera to, czego oczekujesz, trzymaj kopie zapasowe pod ręką.
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:06:46
Jaki jest problem z pozostawieniem osobnego rozwoju w osobnych liniach do czasu ich połączenia? Jeśli byli oddzieleni, to byli oddzieleni.
Istnieje wiele sposobów, aby wyświetlić historię w porządku chronologicznym bez hakowania historii, jak próbujesz. Próbowałeś git log --pretty --date-order
?
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
2012-09-05 05:30:26
[zobacz moją inną odpowiedź na całkowicie zautomatyzowane rozwiązanie. Zostawiłbym to jako przykład ścieżki, która doprowadziła do ostatecznego rozwiązania, na wypadek, gdyby ktoś stanął przed podobnym nie tak oczywistym zadaniem.]
Ok, to nie jest prawdziwa odpowiedź na pytanie (w pełni Skryptowe, zautomatyzowane rozwiązanie), ale myślenie i przykład jak (interaktywne rebase oparte) przetwarzanie może być zautomatyzowane.
Cóż, po pierwsze, dla ostatecznego rozwiązania git filter-branch --parent-filter
wygląda dokładnie to, co jest potrzebne. Poza tym, że mój git-fu nie pozwala mi pisać, 1 -, 2-lub 3-liner, a podejście do pisania samodzielnego skryptu do parsowania wszystkich wersji nie jest fajne i bardziej wymagające niż rebase-i.
Więc, rebase-i może być użyty efektywnie, jeśli daty autora commit były widoczne. Moją pierwszą myślą było tymczasowe łatanie komunikatów commit, aby zacząć od daty autora używając git filter-branch --msg-filter
, Uruchamianie rebase-i, a następnie rozpakowywanie wiadomości z powrotem.
Druga myśl brzmiała: po co się męczyć, lepiej łatać listę zmian rebase jako używany przez rebase-i. tak więc proces będzie:
- Jak zwykle łącz gałęzie A i B z różnych repo do jednego repo.
- Rebase (nieinteraktywnie) jedna gałąź na drugiej. Zastanów się, która gałąź musi zostać przeniesiona do której, aby mieć pierwotne prawo zatwierdzenia (którego nie można łatwo przepisać za pomocą rebase).
- Start
git rebase -i
- w innej konsoli przejdź do $REPO/.git / rebase-merge/
- Run:
awk '/^pick/ {printf "%s %s ", $1, $2; system("echo -n
git show --format='%ai' -s " $2 "
"); for (I = 3; i git-rebase-todo.nowy; mv git-rebase-todo.nowy git-rebase-todo - to wydaje się właściwe miejsce/sposób na zmianę kolejności commitów:
sort -k3 git-rebase-todo >git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo
- przełącz się na oryginalną konsolę i przeładuj plik git-rebase-todo w edytorze, a następnie zakończ edytor.
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:06:46
Właściwie, jeśli dobrze rozumiem, możesz to łatwo osiągnąć za pomocą git-stitch-repo .
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-03 17:14:47