Git
Z Wikibooks, biblioteki wolnych podręczników.
GIT to system plików zaprojektowany jako system kontroli wersji. Stworzył go Linus Torvalds jako narzędzie wspomagające rozwój jądra systemu operacyjnego Linux. GIT stanowi wolne oprogramowanie i został opublikowany pod ochroną licencji GNU GPL.
Git jest dobry do obsługi dużych projektów jak Linux, które mają po kilkanaście tysięcy plików, setki łat dziennie. Nazywany jest "stupid content tracker" ponieważ ma bardzo proste algorytmy scalania, prostsze niż w innych systemach kontroli wersji. Ale dzięki temu jest kilkukrotnie szybszy. Jeśli chcemy się dowiedzieć czym się różnią dane wersje jądra, odpowie w mig. Jeżeli jednak będziemy chcieli znać historię zmian danego pliku, potrwa to trochę dłużej. Wynika to z tego że git nie pracuje na poziomie plików ale zestawów plików.
Wspomaga także zdecentralizowany rozwój - umożliwia każdemu utworzenie własnego repozytorium z czyjegoś repozytorium oraz śledzenie zmian między nimi.
Porównanie git i svn według komend
Przypadki użycia
Konfiguracja
Git - Koncepcje
- Zdecentralizowane
- Każde repozytorium jest gałęzią
- Semantyczne
- Drzewo reprezentuje katalog
- Zatwierdzenie reprezentuje drzewo, poprzednie zatwierdzenie(a) i wiadomość
- tag jest aliasem dla commit
- Zatwierdzenie jest zaprojektowane z uwzględnieniem sum kontrolnych
- Zatwierdzenie może mieć kilkunastu przodków
- Reprezentuje scalenie dwóch linii rozwojowych
Ograniczenia
Git nie śledzi węzłów urządzeń (ang. device nodes) oraz zmian uid i gid.
Aby wyświetlić w katalogu roboczym pliki (np. węzły urządzeń), które nie będą śledzone, należy wykonać komendę:
find / ! -type f ! -type l ! -type d -ls
Porównanie svn i git
| svn | git |
|---|---|
| svn commit | git commit |
| svn add / rm / mv / mkdir | git add / rm / mv / mkdir |
| svn status / log / diff | git status / log / diff |
| svn import | git clone |
| svn update | git pull |
| svn merge | git merge / rebase |
| svn switch | git checkout |
| svn cp <trunk> <tag> | git tag |
| svn cp <trunk> <gałąź> | git branch |
- trunk = master
- lokalne repozytorium jest gałęzią
- git clone
- klonuje zdalne repozytorium
- Wyjątek: zdalne master = lokalne origin
- ~ svn checkout
- git pull
- Pobiera uaktualnienia ze zdalnego repozytorium
- ~ svn update
- git push
- Wysyła obiekty do zdalnego repozytorium
- ~ svn commit
Podręcznik
Git ma wbudowany podręcznik. Listę poleceń otrzymamy po wpisanie samego git. Jeśli chcemy dowiedzieć się o jakimś poleceniu, np commit, to wpisujemy
git commit --help # wyświetli stronę manuala git commit -h # skrócona pomoc, nie działa dla wszystkich poleceń
Podstawowa obsługa
Obsługa Gita może być nieco zagmatwana. I tak:
- Tworzenie gałęzi to "git checkout -b".
"git branch" powinno być używane tylko do wylistowania i usuwania gałęzi.
- Współdzielisz swoją pracę przez "git fetch" (pobranie) i "git push" (wysłanie). To są przeciwieństwa.
- "git pull" może również zrobić "git fetch" ale to jest opcjonalne.
- Większość operacji wykonuje się przez "git polecenie".
Aby uzyskać pomoc na temat jakiegoś polecenia wpisujemy "git help polecenie" lub "git --help".
Reprezentacja historii w Git
Istnieją cztery typy obiektów w obiektowej bazie danych Gita. Wszystkie z nich są globalnie unikalnymi 40-znakowymi szesnastkowymi nazwami powstałymi w wyniku obliczenia sumy haszującej ich typu i zawartości.
- Obiekty blob zapisują zawartość pliku.
- Obiekty drzewa zapisują zawartość katalogu; zawierają nazwy plików, uprawnienia i powiązane nazwy obiektów drzew i blob.
- Obiekty tagi to wskaźniki do innych obiektów, które można współdzielić; generalnie są używane do przechowywania podpisów cyfrowych.
- I wreszcie doszliśmy do obiektów zatwierdzeń (commit). Każde zatwierdzenie wskazuje na (zawiera nazwę) powiązanego obiektu drzewo, który zapisuje stan kodu źródłowego w czasie zatwierdzenia oraz pewne dane opisowe (czas, autor, zatwierdzający, komentarz zatwierdzenia) o zatwierdzeniu.
I najważniejsze, zawiera listę "zatwierdzeń przodkowych", starszych zatwierdzeń z których to jedno się wywodzi. Te wskaźniki są tym co tworzy graf historii.
Zwykle tylko jedno zatwierdzenie (inicjacyjne) ma zero przodków. Możliwe jest mieć więcej niż jeden takich zatwierdzeń (jeśli złączysz dwa projekty z różną historią), ale to nie jest typowe.
Wiele zatwierdzeń ma tylko jednego przodka. Te są tworzone zwykłe zatwierdzenie po edycji.
W wreszcie są zatwierdzenia które mają wielu przodków. Dwa jest najbardziej spotykane, ale git umożliwia dużo więcej. (Istnieje limit szesnastu w kodzie źródłowym, a najwięcej ktokolwiek użył w rzeczywistości to 12 i było to uważane za przesadę).
Na końcu są referencje, przechowywane w katalogu .git/refs. To są nazwy odczytywalne dla człowieka powiązane z zatwierdzeniami oraz "zestaw root", od którego wszystkie inne zatwierdzenia powinny być osiągalne.
Te referencje są generalnie podzielone na dwa typy, mimo że nie ma fundamentalnej różnicy:
- Tagi są referencjami w zamierzeniu niezmienialnymi. Tag "v1.2" jest historycznym zapisem. Tag może wskazywać do obiektu tag (który będzie zawierał podpis) lub po prostu bezpośrednio na zatwierdzenie. Ten drugi nie jest uwierzytelniany kryptograficznie, ale działa całkiem dobrze w codziennym użyciu.
- Głowice (head) to referencje przeznaczone do aktualizacji. Głowica to aktualnie synonim gałęzi, jednak pierwszy bardziej jest podpowiedzią, podczas gdy drugi kieruje twoją uwagę na całkowitą ścieżkę, która tu doprowadziła.
Czy tak czy tak, są po prostu 41-bajtowym plikiem, który zawiera po prostu 40-bajtowy szesnastkowy identyfikator obiektu, plus znak nowej linii. Tagi są przechowywane w .git/refs/tags, a głowice są przechowywane w git/refs/geads. Tworzenie nowej gałęzi to po prostu wybranie nazwy pliku i zapisanie do niego identyfikatora istniejącego zatwierdzenia.
Polecenia git wymuszają niezmienialność tagów, ale to jest dodatek bezpieczeństwa, nie cecha fundamentalna. Możesz zmienić tag w głowicę i oszaleć.
Jedyne ograniczenie gałęzi to bałagan. Wiele poleceń gita umie operować na "wszystkich głowicach" i jeśli masz zbyt dużo, to może stać się nieznośne. Jeśli nie używasz gałęzi, usuń ją, lub przenieś gdzieś (na przykład do katalogu tags) gdzie nie będą zaśmiecać listę aktywnych aktualnie głowic.
(Zauważ, że CVS nie ma domyślnie włączonego operowania na wszystkich głowicach, więc ludzie zwykle używają dłuższych nazw gałęzi i pozostawiają je po tym jak zostały złączone z aktywną gałęzią (trunk). Stare repozytoria CVS przekształcone do gita zwykle wymagają wyczyszczenia starego rozgałęziania.
Inną rzeczą wartą wspomnienia jest to, że nazwy głowic i tagów mogą zawierać ukośniki: np. możesz tworzyć podkatalogi w katalogach .git/refs/heads i .git/refs/tags. Pełny opis legalnych nazw na stronie podręcznika man git-check-ref-format.
Nazywanie wersji
CVS zachęca do tagowania jak szalony, ponieważ jedynym sposobem, aby znaleźć daną wersję jest wyszukiwanie po dacie. Git czyni to dużo łatwiejszym, tak więc większość wersji nie potrzebuje nazw.
Możesz znaleźć pełen opis na stronie man git-rev-parse, ale poniżej jest streszczenie.
Po pierwsze, każde zatwierdzenie posiada globalną unikalną nazwę, swój 40-cyfrowy szesnastkowy identyfikator obiektu. Jest trochę za długi i dziwaczny, ale zawsze działa. To jest przydatne do omawiania szczególnego zatwierdzenia na liście dyskusyjnej. Możesz podać unikalny przedrostek, większość ludzi uważa 8 cyfr za wystarczające.
(Subversion jest nawet prostsze, ponieważ przypisuje kolejny numer do każdego zatwierdzenia. Jakkolwiek nie jest to możliwe w rozproszonym systemie kontroli wersji, jakim jest git.)
Po drugie, możesz odnieść się do nazwy głowicy lub taga. Git szuka głowicy w następujących miejscach: 1) .git 2) .git/refs 3) .git/refs/heads 4) .git/refs/tags
Powinieneś unikać takich samych nazw dla głowicy i taga, ale jeśli to robisz, możesz określić je np. jako heads/foo i tags/foo.
Po trzecie, możesz określić zatwierdzenie względem innego. Najprostszy jest "przodek", określany przez dodanie znaku karetki ^ do nazwy. Np. HEAD^ lub deadbeef^. Jeśli jest kilku przodków, wtedy ^ jest takie samo jak ^1, a inne są ^2, ^3, itd.
Tak więc ostanie kilka zatwierdzeń które zrobiłeś to HEAD, HEAD^, HEAD^^, HEAD^^^, itd. Po chwili liczenie karetek staje się uciążliwe, tak więc możesz skrócić ^^^^ do ~4. Zauważ, że to umożliwia określenie tylko pierwszego przodka. Jeśli chcesz podążyć szerokością gałęzi, musisz napisać coś takiego jak "master~305^2~22".
Przekształcanie nazw
Git ma dwóch pomocników (programy zaprojektowane głównie do użycia w skryptach powłoki) do przekształcania pomiędzy globalnym identyfikatorem obiektu i odczytywalnymi dla człowieka nazwami.
Pierwszy to git-rev-parse. To jest generalnie skrypt powłoki, który sprawdza poprawność polecenia i przekształca nazwy obiektów do absolutnych identyfikatorów obiektów. Więcej na stronie podręcznika man git-rev-parse.
Drugi to git-name-rev, który przekształca w drugą stronę. Jest szczególnie użyteczny do pokazywania pomiędzy którymi tagami nie udało się zatwierdzenie.
Operowanie gałęziami, proste przypadki
Według konwencji, lokalny aktualny katalog roboczy w gicie jest nazywany "master". To jest po prostu nazwa gałęzi, którą git tworzy, kiedy zaczniesz puste repozytorium. Możesz ją usunąć jeśli nie podoba Ci się nazwa.
Jeśli tworzysz swoje repozytorium przez klonowanie czyjegoś repozytorium, zdalna gałąź "master" jest kopiowana na lokalną gałąź nazwaną "origin". Możesz mieć swoją własną gałąź "master", która nie jest przywiązana do zdalnego repozytorium.
Istnieje zawsze aktualna głowica, zwana HEAD. (Aktualnie jest to link symboliczny, .git/HEAD, do pliku takiego jak refs/heads/master.) Git wymaga aby to zawsze wskazywało do katalogu refs/heads.
Pomniejsze szczegóły techniczne:
1) HEAD zwykł być Uniksowym symlinkiem, i nadal może być tak postrzegany, ale aby obsługiwać Microsoft, to jest teraz tym co jest nazywane "symboliczna referencja" lub symref i jest prostym plikiem zawierającym "ref: refs/heads/master". Git traktuje to tak jak symlink. Istnieje pomocnik git-update-ref, który je zapisuje.
2) Podczas gdy HEAD musi wskazywać na refs/heads, legalnym jest wskazywanie na nieistniejący plik. To właśnie się dzieje przed pierwszym zatwierdzeniem w całkowicie nowym repozytorium.
Kiedy wykonujesz "git commit", nowy obiekt zatwierdzenia jest tworzony ze starym HEAD jako przodkiem, a nowe zatwierdzenie jest zapisywane do aktualnej głowicy (wskazywanej przez HEAD).
Trzy użycia "git checkout"
Git checkout może zrobić trzy oddzielne rzeczy:
Przełączenie na inną głowicę
git checkout [-f|-m] <galąź>
To czyni <gałąź> nową GŁOWICĄ, i kopiuje jej stan do indeksu i katalogu roboczego.
Jeśli plik ma nie zapisane zmiany w katalogu roboczym, to stara się zachować je. To jest pojedyncza próba i wymaga aby zmienione pliki nie zostały zmienione pomiędzy starymi i nowymi GŁOWICAMI. W takim wypadku, wersja katalogu roboczego pozostaje nie tknięta.
Bardziej agresywną opcją jest -m, która będzie próbowała zrobić 3-kierunkowe (międzyplikowe) scalenie. To może się nie udać, zostawiając niescalone pliki w indeksie.
Alternatywą jest użycie -f, co nadpisze niezapisane zmiany w katalogu roboczym. Ta opcja może być użyta bez określenia opcji <gałąź> (domyślnie GŁOWICA) aby cofnąć lokalne zmiany.
Cofnięcie zmiany dla małej liczby plików
git checkout [<wersja>] [--] <ścieżki>
skopiuje wersję <ścieżki> z indeksu do katalogu roboczego. Jeśli jest podana <wersja>, index dla tych ścieżek będzie uaktualniony z danej wersji przed kopiowaniem z indeksu do drzewa roboczego.
Inaczej niż z wersją bez określenia <ścieżki>, to NIE uaktualni GŁOWICY, nawet gdy <ścieżki> to ".".
Stworzenie gałęzi
git checkout [-f|-m] -b <gałąź> [wersja]
stworzy i przełączy do nowej gałęzi o podanej nazwie. To jest odpowiednik do
git branch <gałąź> [<wersja>] git checkout [-f|-m] <gałąź>
Jeśli <wersja> jest pominięta, domyślnie jest GŁOWICA, a w takim wypadku żadne pliki katalogu roboczego nie będą zmienione.
To jest typowy sposób wypuszczenia wersji, która nie posiada istniejącej głowicy wskazującej na nią.
Kasowanie gałęzi
"git branch -d <głowica>" jest bezpieczne. Kasuje podaną <głowicę>, ale najpierw sprawdza, czy zatwierdzenie jest osiągalne w inny sposób. To znaczy scaliłeś gdzieś gałąź lub nigdy nie robiłeś edycji na tej gałęzi.
To dobry sposób na stworzenie "tematycznej gałęzi" kiedy pracujesz nad czymś większym niż jednolinijkowiec, ale powinno się usuwać je gdy skończysz. Nadal pozostaje w historii.
Brudna robota z głowicami: git reset
Jeśli potrzebujesz nadpisać aktualną GŁOWICĘ z pewnego powodu, narzędzie aby to zrobić to "git reset". Istnieją trzy poziomy resetu:
git reset --soft <głowica>
To nadpisuje aktualną GŁOWICĘ zawartością <głowica>.
Jeśli pominiesz <głowica>, domyślnie jest GŁOWICA, więc niczego nie zrobi.
git reset [<głowica>] git reset --mixed [<głowica>]
To nadpisuje aktualną GŁOWICĘ i kopiuje ją do indeksu, cofając jakiekolwiek polecenia git-update-index, które mogłeś wykonać.
Jeśli pominiesz <głowica>, domyślnie jest GŁOWICA, więc nie będzie zmiany w aktualnej gałęzi, ale wszystkie indeksy zostaną cofnięte.
git reset --hard [<głowica>]
To robi wszystko wspomniane wyżej oraz uaktualnia katalog roboczy. Wyrzuca wszystkie twoje edycje w-toku i daje ci czystą kopię. To jest zwykle używane bez określenia <głowica>, a w takim wypadku aktualna GŁOWICA jest użyta.
Systemy śledzenia błędów
Jeśli chcesz używać systemu śledzenia błędów w połączeniu z gitem, zobacz na tą stronę:
http://en.wikipedia.org/wiki/Comparison_of_issue_tracking_systems
Interesuje nas kolumna "Source code revision control system integration".

