W pracy, głównie ze względu na administratorów i zarządzanie uprawnieniami, muszę korzystać z TFSa. Nie jestem entuzjastą tego narzędzia i zdecydowanie wolę pracę z Gitem, m. in. z powodów, które swego czasu wymienił Maciej Aniserowicz w poście W czym Git jest lepszy od TFS?.
Na całe szczęście istnieją narzędzia takie jak git-tfs i git-tf łagodzące ból zadany przez TFS. Z tego pierwszego korzystam już od dłuższego czasu i generalnie nie miałem z nim problemów. Niestety życie po raz kolejny potwierdziło, że nie ma rzeczy niezawodnych i git-tfs odmówił ostatnio współpracy przy jednym z projektów. Miałem problem z wrzucaniem zmian do zdalnego repozytorium po migracji na inny serwer. W skończonym czasie nie byłem w stanie obejść problemu więc postanowiłem skorzystać z alternatywy w postaci narzędzia git-tf.
Oba narzędzia pod względem zestawu komend i zaimplementowanych funkcjonalności są do siebie podobne i mają ten sam cel, integrację reporytorium TFS z lokalnym repozytorium Git. Komendy są podobne, funcjonalność jest podobna, a workflow? No właśnie, workflow też jest podobny, ale w kwestii wrzucania zmian na serwer jest dość istotna różnica.
Korzystając z git-tfs pracuję według tego schematu: Git workflows with git-tfs. Workflow w uproszczeniu jest następujący:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
git checkout master git tfs pull git checkout –b cool-feature #… pracujemy, pracujemy git add -A git commit –m “Tadam, wreszcie gotowe!” git checkout master git tfs pull git checkout cool-feature git rebase master git tfs checkin (lub rcheckin) git checkout master git pull git branch –d cool-feature # … i tak dalej |
Schemat jest dość popularny i , co najważniejsze, działa bez zarzutu. Jak on się ma natomiast w stosunku do git-tf? Przeanalizujmy następujący przykład:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
git checkout master git tf pull git checkout –b cool-feature # ... pracujemy, pracujemy git add -A git commit –m “Tadam, wreszcie gotowe!” git checkout master git rebase cool-feature git tf pull --rebase git tf checkin (lub checkin –-deep) git branch –d cool-feature # ... i tak dalej |
Jak widać model pracy z jednym i drugim narzędziem jest do pewnego momentu identyczny. Różnice zaczynają się po linijce 13.
W przypadku narzędzia git-tfs zmiany wrzucamy z gałęzi roboczej i do niej scalamy zmiany wprowadzone przez innych członków zespołu. Po przeniesieniu zmian na serwer synchronizujemy naszą gałęź master i usuwamy gałęź roboczą.
Natomiast korzystając z git-tf sprawa wygląda inaczej. Synchronizacja z repozytorium TFS odbywa się w całości przez gałęź master. Jeżeli spróbujemy wykonać komendę checkin z gałęzi roboczej (po wcześniejszej synchronizacji gałęzi master z TFSem i naniesieniu zmian na gałęź roboczą) program zwróci komunikat:
All files are up to date there is nothing to check in.
Niestety nie jestem w stanie powiedzieć jaka jest dokładna przyczyna takiego zachowania. Proszę o komentarze z pomysłami.
Idąc dalej, scalając zmiany z gałęzi roboczej do gałęzi głównej mamy dwie możliwości: merge
lub rebase
. Korzystając z merge
łatwiej będzie odkręcić zmiany jeżeli coś pójdzie nie tak. Z kolei rebase
pozwoli utrzymać liniową historię w repozytorium. Dlaczego liniowość ma znaczenie w konteście git-tf i dlaczego właśnie to podejście zastosowałem w przykładzie? Domyślnie komenda checkin wysyła wszystkie lokalne commity jako jedną zmianę. Nie zawsze może nam to odpowiadać. Ja wolę wrzucać pojedyncze, mniejsze commity. Żeby to osiągnąć trzeba wywołać komendę checkin
z przełącznikiem --deep
. To właśnie ten przełącznik był decydentem przy wyborze metody nanoszenia zmian. Powołując się na dokumentację wspomnianego przełącznika:
Creates a “deep” check-in, checking in a TFS changeset for each git commit since the latest TFS changeset (requires linear history or “–squash” or “–autosquash”) …
W nawiasie podano trzy warunki, z których jeden musi być spełniony żeby commity były wrzucone osobno. Jak widać jednym z nich jest liniowość lokalnego repozytorium, co uzasadnia wykorzystanie komendy rebase
. Plus jest taki, że zachowanie liniowości repozytorium kontroluję sam i nic nie jest dziełem przypadku. Pozostałe możliwości odrzuciłem ponieważ:
- W przypadku przełącznika
--squash
musimy podawać hash każdego commita więc wrzucenie n commitów jeden po drugim wymagałoby n komend. Bardzo nieefektywne rozwiązanie. - Przełącznik
-–autosquash
podczas wrzucania commitów rzekomo sam decyduje co zrobić w sytuacji, gdy commit ma dwóch rodziców (np. commit stworzony przez merge). Ja nie wiem jak to się zachowuje w praktyce i nie jestem na tyle odważny (albo głupi) żeby testować to rozwiązanie na firmowym repozytorium biorąc pod uwagę, że git-tf to dzieło Microsoftu :).
Narzędzia są do siebie bardzo podobne i spełniają swoje zadanie. Ja natomiast zawsze będę wybierał git-tfs wszędzie tam, gdzie będę miał taką możliwość. Model pracy z git-tfs najzwyczajniej w świecie bardziej mi odpowiada. Oczywiście jest to kwestia gustu, dlatego każdy powinien sam zdecydować przy wyborze pomiędzy git-tf, a git-tfs. Jeżeli ktoś nie wie co mu bardziej odpowiada, to proponuję zasięgnąć informacji jeszcze tutaj: Git-TFS czy Git-TF – który wybrać?.