fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
5 minut

Upojne chwile z git filter-branch


12.07.2012

Git nie przestaje zaskakiwać. Zawsze gdy pomyślę “fajnie by było, gdyby się dało [cokolwiek]” okazuje się, że w Gicie.. się DA!

Ostatnio eksperymentowałem z jakąś biblioteką i nie chciałem tymi zabawami śmiecić w firmowym TFSie. Utworzyłem więc oczywiście lokalne repozytorium Gita i – jazda! Po kilku dniach okazało się jednak, że wykluło się z tego coś pożytecznego i mimo wszystko fajnie by było efekty pracy w tymże TFSie mieć, w jednym z firmowych projektów. Można było pójść na skróty i po prostu skopiować finalną postać mojego kodu, ale ja preferowałem wrzucić do TFSa całą historię, commit po commicie. W niniejszym poście poruszę dwie sprawy: przygotowanie mojego lokalnego repo do tej czynności oraz sam proces łączenia repozytoriów.

Note: kiedyś pisałem już o modyfikacji historii w Gicie; ten post idzie jeszcze dalej.

Git filter-branch – oh jea!

Moje lokalne repozytorium miało strukturę przygotowaną pod ten konkretny projekt, a więc w roocie siedział sobie katalog /lib, obok katalog /src z kodem. Repozytorium w TFS przechowuje jednak więcej informacji i docelowo wszystkie moje commity, zamiast w katalogu /, powinny lądować w katalogu, dajmy na to, /Dev/Main/MAPlayground. Natknąłem się na wielce imponującą ze wszech miar komendę filter-branch. Polecam zapoznanie się z dokumentacją, chociaż, jak to w Gicie, i bez tego ciężko jest cokolwiek poważnie zepsuć. Warto jednak popatrzeć co jeszcze, oprócz tego co tutaj przedstawiam, oferuje ta komenda. A oferuje BARDZO wiele.

Najpierw w swoim repozytorium stworzyłem osobną gałąź do takich eksperymentów z Gitem, właśnie po to żeby nic nie napsocić i w razie emergency móc spokojnie wrócić do mastera i zacząć od nowa:

git checkout -b filtered

Zanim jednak zacznę bawić się mieszaniem w historii na poziomie katalogów, czyli przepisywaniem wszystkich commitów pod względem położenia plików na dysku, chciałbym do każdej commit message dodać prefix. Komentarze w tym repo dotyczą oczywiście wyłącznie tego co w nim robiłem i chciałbym później w TFS móc w prosty sposób zgrupować commity, które zamierzam tam właśnie pchnąć. Prefix “[ma-playground]” przed każdą commit msg wydał mi się dobrym pomysłem (oczywiście tak naprawdę brzmiał trochę inaczej;) ). Po kilku chwilach zabawy doszedłem do takiej komendy:

git filter-branch –msg-filter \
‘read m;
echo “[ma-playground] $m” ‘

i już, szybkie zerknięcie w logi wywołuje uśmiech na mordzie: każdy mój commit jest teraz poprzedzony wybranym przeze mnie prefixem. Słitaśnie.

Pora na zmierzenie się z katalogami. Każdy commit powinien się teraz znaleźć we wspomnianym wyżej folderze. Kolejne kilka (no dobra… trochę więcej:) ) minut i oto rozwiązanie:

git filter-branch  –tree-filter \
‘mkdir Dev && mkdir Dev/Main && mkdir Dev/Main/MAPlayground
ls -A | grep -v Dev | while read filename
do
mv $filename Dev/Main/MAPlayground
done’

Windowsowym UI-klikaczom może wydać się to trochę zamotane, ale polecam zagłębienie się w ów miniskrypt i przeanalizowanie wykorzystanych w nim komend. Niejednokrotnie potrafią uprościć życie, zdecydowanie warto je znać.

Na stronie dokumentacji komendy znajduje się alternatywny sposób poradzenia sobie z tym zadaniem (operujący bezpośrednio na indexie), ale jest mocniej skomplikowany niż moja propozycja (chociaż moja propozycja jest skrojona dokładnie na miarę właśnie moich konkretnych wymagań i pewnie znajdą się przypadki kiedy nie zadziała).

Nowy git remote i rebase

Z tak przygotowanym repo czas na operacje na moim docelowym repozytorium, podłączonym do TFSa oczywiście poprzez git-tfs. Oba siedzą sobie bezpiecznie w katalogu c:\dev. Najsampierw wchodzę bashem do katalogu zawierającego repo TFSowe i dodaję link do repozytorium, które dopiero co tak pięknie obrobiłem:

git remote add ma ../MaPlayground

Teraz ściągam jego zawartość…

git fetch ma

… i ponownie tworzę gałąź “roboczą”, tym razem bazując na branchu przygotowanym do operacji łączenia repo:

git checkout -b br-ma ma/filtered

Jedyne co chcę zrobić to “nałożyć” moje commity z podlinkowanego repo do repozytorium na wierzch aktualnej historii (na mastera). Jak wiemy ze wspomnianych wcześniej postów, posłużyć nam może polecenie rebase:

git rebase master

A potem już tylko:

git tfs rcheckin
git checkout master
git tfs pull
git remote rm ma
git branch -d br-ma

… i jesteśmy w domu.

Piękne, czyż nie? Ależ TAK, jak najbardziej! Zachęcam do dalszych zabaw z tym poleceniem, możliwości wydają się nieograniczone.

0 0 votes
Article Rating
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Paweł Srebniak
11 years ago

Gratuluję bardzo dobry wpis! Widzę, że nie tylko mnie git zaskakuje niemalże każdego dnia.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również