git filter-repo – co to jest + praktyczny przykład

Opublikowane przez Tomasz Prasołek w dniu

Delorean

Jeśli próbowałeś kiedyś zrobić naraz większe zmiany w historii swojego projektu np. usunąć jakieś pliki z commitów lub usunąć hasła z kodu, to pewnie natknąłeś się na narzędzie git filter-branch. Do czego jest git filter-repo? Do tego samego 🙂

Jak jest do tego samego, to po co o tym piszę…. ? Ponieważ teraz ludzie tworzący gita zalecają używanie filter-repo, dodatkowo jest łatwiejsze w użyciu i co ważniejsze – wydajniejsze.

Git filter-repo

Pojawiło się w wersji Gita 2.24 (3 listopad 2019 – obecnie jest wersja 2.25.1). Do działania wymaga pythona w wersji 3.5 lub nowszej. To w sumie logiczne, bo narzędzie jest własnie napisane w pythonie 🙂

To narzędzie to po prostu skrypt napisany w Pythonie. Jak go zainstalować znajdziesz tutaj: https://github.com/newren/git-filter-repo/blob/master/INSTALL.md.

UWAGA!!!
To polecenie przepisuje historię, czyli wszystkie zmienione commity będą miały inne SHA-1.

Co to potrafi:

  • Usuwanie dużych plików i dużych katalogów.
  • Usuwanie niechcianych plików według ścieżki.
  • Wydobywanie poszukiwanych ścieżek i ich historii (usuwanie wszystkiego innego).
  • Restrukturyzacja układu plików (np. przeniesienie wszystkich plików do podkatalogu w ramach przygotowań do połączenia z innym repozytorium, uczynienie z podkatalogu nowego katalogu najwyższego poziomu lub scalenie dwóch katalogów z niezależnymi nazwami plików w jednym katalogu).
  • Zmiana nazw tagów (również często w ramach przygotowań do połączenia z innym repozytorium).
  • Zastępowanie lub usuwanie poufnego tekstu, takiego jak hasła.
  • Modyfikacje nazw użytkowników lub e-maili.
  • Modyfikacje commit messages (wolę niektóre zwroty pisać w oryginale niż np. komunikaty zatwierdzania 🙂 ).

I wiele więcej… Do czego tego można jeszcze użyć? Mi przyszło do głowy takie coś.

Problem

W pracy jako systemu do tasków korzystamy z Azure DevOps. Tam każdy task ma swój numer, który jest liczbą. Pierwszy task był oznaczony nr 1 itd., teraz już chyba dochodzimy do numeru 6000.

Robiąc commit staram się łączyć go z taskiem. Najwygodniejszy sposób na takie połączenie to na końcu commit message wpisać nr taska poprzedony znakiem # czyli:

Poprawka bug-a #1

Po wrzuceniu kod na serwer ten commit będzie połączony z taskiem. Będzie to widać w portalu:

I wszystko spoko, ale czasami nie skończymy zadania i musimy na chwilę przesiąść się na naprawę czegoś innego, za pewne bardziej pilnego 🙂 Wtedy pushujemy kod na serwer (jako backup) i wracamy do tego później.

To później jest wtedy jak już na gałęzi develop (lub innej do której scalamy nasz kod, przeważnie master) są nowe rzeczy i przed przystąpieniem pracy robimy git rebase develop. Wtedy nasz commit ma inny identyfikator. Po dokończeniu pracy i ponownym wrzuceniu na serwer, task już ma przypisane 2 commity:

W większości przypadków kod w tych commitach będzie identyczny, ale będą inne identyfikatory. W tym temacie są również zgłoszenia (o tutaj -> https://developercommunity.visualstudio.com/idea/764354/auto-link-commit-to-work-item-for-specificmaster-b.html) do Microsoftu, aby może łączyć linki tylko jeśli commit jest na głównym branchu (master/develop). Skoro nic się z tym nie da zrobić, to trzeba coś wymyślić samemu.

I tutaj z pomocą przychodzi właśnie git filter-repo.

Jak ma to nam pomóc?

Wymyśliłem to tak:

  • Podczas tworzenia commita daję na końcu nr commita bez symbolu # np. Poprawka bug-a 546
  • 546 to numer naszego taska w Azure DevOps.
  • W tym momencie jak wrzucę kod na serwer, to task nie zostanie połączony z commitem.
  • Przed scalaniem pracy z główną gałęzią uruchamiam git filter-repo, które w moich commitach doda symbol # przed nr commita.
  • Wiadomość commita nie będzie wyglądała tak: Poprawka bug-a 546, tylko tak: Poprawka bug-a #546

Skrypt

Napisałem do tego skrypt, żeby wykonywać to szybko i żebym nie musiał za każdym razem tego pisać 🙂 . Kod w Pythonie wykonujący tą robotę wygląda tak:

git filter-repo -f --refs master..$currentBranchName --message-callback '
		lastWord = message.split()[-1]
		def RepresentsInt(s):
		    try: 
		        int(s)
		        return True
		    except ValueError:
		        return False
		if RepresentsInt(lastWord):
		    return message.replace(lastWord, b"#" + lastWord)
		else:
		    return message'

O co w nim chodzi… Po kolei:

  • git filter-repo – wywołanie polecenia
  • -f – force
  • --refs develop..$currentBranchName – jakie commity na wziąć pod uwagę do edycji. To polecenie weźmie commity znajdujące się na naszym feature branchu, a nie ma ich na branchu develop.
    UWAGA! Jeśli scalasz kod do innego brancha niż develop, zmień to w skrypcie.
  • --message-callback + kod w Pythonie – określenie, której opcji git filter-repo użyć + kod który ma być wykonany dla każdego commita. Po prostu sprawdzam czy ostatnie słowo w commit message jest liczbą. Jeśli tak to doklejam do niego # na początku. Reszta commit message pozostaje bez zmian. Jeśli commit nie posiada na końcu numeru taska, to nic się nie dzieje. Znaczy.. nie się nie dzieje z commit message, ale i tam polecenie “przemieli” tego commita i jego sha-1 będzie inne.

Cały skrypt jest dostępny na moim Githubie:

Nie znam pythona (na co dzień proramuje w C#), to co widać wyżej to efekt googlowania. Resztę skryptu postanowiłem napisać w bashu, bo już go poznałem wcześniej podczas pisania innych skryptów.

Jak taki skrypt “zainstalować” u siebie na komputerze opisał w tym wpisie: https://poznajgita.pl/jak-napisac-wlasne-polecenie-w-gicie/#podstawy

Podsumowanie

W ten oto prosty sposób – przy użyciu prostego skryptu w Pythonie – rozwiązaliśmy problem zduplikowanych połączeń tasków do commitów z kodem 🙂

git filter-repo jest narzędziem, którego można używać do wielu rzeczy. Ja pokazałem tylko jedną z nich, która może się przydać (prawie) codziennie.


Źródła:
https://git-scm.com/docs/git-filter-branch
https://github.com/newren/git-filter-repo
https://developercommunity.visualstudio.com/idea/764354/auto-link-commit-to-work-item-for-specificmaster-b.html

Zdjęcie w nagłówku pochodzi z serwisu unsplash.com. Jego autorem jest: unsplash-logoJason Leung


0 Komentarzy

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *