piątek, 21 października 2011

Git i (współ)praca z innymi systemami kontroli wersji

Dzisiaj będzie bardziej konkretny post. Jako, że sam nie tak dawno zmieniałem pracę, w której niestety programiści używali cvsa i nie mieli ochoty tego zmieniać (zgodnie z zasadą po co coś zmieniać skoro działa) to postanowiłem opisać jak sobie poradzić w takiej sytuacji.


Po co?
Pytanie jak najbardziej zasadne. Odpowiedź jest prosta, po to aby móc korzystać z wszystkich zalet gita nie robiąc rewolucji wśród reszty zespołu. Jakie to zalety? Jeżeli ktoś nie wie do odsyłam do link lub do link. Mnie cieszy najbardziej to, że mam całe sklonowane repo tylko dla siebie i mogę sobie commitować ile mi się podoba. Nie bez znaczenia jest też łatwość tworzenia branchy i ich niemal automatyczne mergowanie.

Krok 1 - tworzenie centralnego repozytorium gita tylko do odczytu
Na początku musimy stworzyć repozytorium, które będzie klonem tego głównego (czy to cvs czy svn). Najlepiej postawić je na maszynie, do której wszyscy będą mieli łatwy dostęp. Natomiast, co ważne to repozytorium będzie tylko ściągało zmiany z oryginalnego, nie będziemy żadnych zmian bezpośrednio do niego wrzucać!

W przypadku cvsa robimy to tak:
Przed importem należy ustawić zmienną CVSROOT w ten sposób:
$ CVSROOT=:method:user@host:/path/to/CVS/root cvs login
Później tworzymy sobie jakiś katalog, w którym będzie znajdowało się repozytorium i przechodzimy do niego. I wreszcie importujemy wszystko z cvsa:
$ git-cvsimport -p x -v -d :method:user@host:/path/to/CVS/root/someproject
method oznacza typ serwera, np. pserver. Możemy wprowadzić kilka drobnych udogodnień:
$ git config cvsimport.module someproject
$ git config cvsimport.r cvs
$ git config cvsimport.d $CVSROOT
W ten sposób przy aktualizowaniu repozytorium nie będziemy musieli prawie nic podawać.

A w svnie tak:
Na początku tworzymy sobie katalog, gdzie chcemy trzymać repozytorium i przechodzimy do niego. Później wywołujemy:
$ git-svn init svn://host/trunk/someproject
$ git-svn fetch
$ git-svn rebase

Zaznaczam w tym miejscu, że jeżeli mamy jedno repozytorium z wieloma projektami to warto zrobić kilka repozytoriów gitowych - każdy per projekt. Stąd someproject w podanych wyżej wywołaniach.

Krok 2: sklonowanie repozytorium gita
Teraz klonujemy sobie świeżo utworzone repozytorium, na którym będziemy pracować:
$ git clone /path/to/git dir

UWAGA: pracujemy tylko na branchach. Zaraz po sklonowaniu warto utworzyć sobie jakiegoś brancha aby nie pracować na branchu master. Jest to ważne przy późniejszym przerzucaniu zmian. Brancha możemy utworzyć w ten sposób:
$ git checkout -b nazwa_brancha
Utworzymy w ten sposób brancha bazującego na branchu master.

Krok 3: wrzucanie zmian
Jak już popracowaliśmy to pora na commit. Robi się go w ten sposób:
$ git add zmienione_pliki
$ git commit
Listę zmienionych plików możemy podejrzeć w ten sposób:
$ git status
Ewentualnie aby nie dodawać do commita każdego pliku z osobna możemy użyć:
$ git commit -a

Krok 4: formatowanie patcha
OK, mamy wrzucone zmiany do lokalnego repo. Jeżeli zrobiliśmy tylko jednego commita to sprawa jest banalna:
$ git format-patch -1
W ten sposób git utworzy nam plik o nazwie 0001-commit_message.patch
Jeżeli zrobiliśmy więcej commitów to mamy dwa wyjścia:
  • jeżeli chcemy mieć w oryginalnym repozytorium widoczne wszystkie commity jakie zrobiliśmy w gicie to wywołujemy:
    $ git format-patch origin..HEAD
    To polecenie utworzy nam serię patchy, każdy per commit
  • jeżeli natomiast chcemy mieć jeden zbiorczy commit to musimy sobie zmergować zmiany. W tym celu wracamy na chwilę do brancha master:
    $ git checkout master
    Tworzymy sobie tymczasowego brancha:
    $ git checkout -b merge_branch
    I mergujemy:
    $ git merge --squash nazwa_brancha_ze_zmianami
    Opcja --squash powodują złączenie wszystkich commitów w jeden.
    Commitujemy:
    $ git commit
    Po czym tworzymy patcha:
    $ git format-patch -1
    Może teraz przejść na innego brancha i usunąć ten tymczasowy:
    $ git checkout master && git branch -d merge_branch

Krok 5: wrzucanie zmian do oryginalnego repozytorium
Gdy mamy już patcha lub patche to przenosimy je do katalogu z checkoutem czy to cvsa czy svna. Po czym wywołujemy:
$ patch -p1 < plik_patcha
Przeglądamy sobie czy wszystko jest OK i commitujemy jak zwykle.
Jest też sposób automatyczny, którego nie polecam. Lepiej mieć nad wszystkim kontrolę. Jeżeli ktoś chce to niech poszuka informacji o git-svn dcommit lub git cvsexportcommit.

Krok 6: aktualizacja centralnego repo gita
Przechodzimy do katalogu z naszym centralnym repo gita i wywołujemy:

Dla cvsa:
$ git cvsimport -a -p -x

Dla svna:
$ git-svn fetch
$ git-svn rebase

Krok 7: aktualizacja lokalnego repo gita
W katalogu z naszym lokalnym repo wykonujemy (na każdym branchu, który chcemy zaktualizować):
$ git pull origin master
Należy pamiętać, że aby uniknąć konfliktów przy nakładaniu patcha należy zawsze przed utworzeniem brancha tymczasowego zaktualizować brancha master jak również checkout originalnego repo.

I tak w kółko :)
Wydaje się, że to dużo roboty. Powiem tak, jak już się raz to wszystko zrobi to potem nie ma jej w cale więcej niż dotychczas (oczywiście poza przerzucaniem zmian). Na przykład aktualizowanie repozytoriów można zautomatyzować jakimś prostym skryptem. Korzyści płynące z pracy z gitem są bezcenne. Naprawdę warto spróbować.

Brak komentarzy:

Prześlij komentarz