Git

Z Wikibooks, biblioteki wolnych podręczników.

Przejdź do: nawigacji, wyszukiwania

Image:git-logo.svg

Wikipedia, nasz siostrzany projekt, zawiera artykuł na temat GIT.

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.

Spis treści

[edytuj] Szybki start

Obsługa git w edytorze vim

Uwaga! git nie dodaje pustych katalogów do repozytorium. To wynika z koncepcji, że git śledzi zawartość plików a nie pliki.

mkdir git_repo   # tworzymy katalog w którym będzie nasze repozytorium plików, możemy nazwać jak chcemy
cd git_repo      # przechodzimy do tego katalogu
git init         # inicjalizujemy bazę repozytorium (katalog .git)

Teraz wgrywamy do tego katalogu pliki lub tworzymy nowe.

git add .        # dodajemy wszystkie pliki do śledzenia z aktualnego katalogu, kropka oznacza właśnie katalog aktualny
git status       # sprawdzamy status naszego repozytorium, to polecenie pokaże które pliki zmienione, a które nowe
git commit -a    # zatwierdzamy zmiany czyli wysyłamy pliki do bazy repozytorium

Samo polecenie "git commit" zapisze tylko pliki które zostały dodane poleceniem "git add". Dlatego dodajemy -a, żeby nie dodawać ręcznie każdego zmienionego pliku. Nowe pliki trzeba jednak dodać przez "git add".

git log          # sprawdzamy historię zatwierdzeń
git whatchanged -p # historia zmian razem z diff
git whatchanged --pretty=oneline # wyświetla tylko nazwy zmienionych plików

Jeśli chcemy możemy rozgałęziać nasz projekt tworząc gałęzie. Domyślnie nowy projekt jest w gałęzi master. Wszystkie gałęzie można wylistować poleceniem

git branch

Znak * wskazuje aktualną gałąź.

Jeśli chcemy graficznie zobaczyć nasze drzewo kodu, to trzeba użyć polecenia "gitk" (w gentoo "gitview")

[edytuj] Dalsze operacje

Wycofanie nie zapisanych zmian (dwa polecenia)

git checkout file-to-revert
git reset HEAD <file>

Cofnięcie wszystkich lokalnych zmian

git checkout -f

[edytuj] Cofanie zmian

Poniższe polecenia działają na plikach już śledzonych. Jeśli chcemy usunąć także pliki nie śledzone, trzeba to zrobić ręcznie. Można najpierw usunąć wszystkie pliki poleceniem

rm -Rf *

To nie usunie katalogów o nazwach zaczynających się na kropkę czyli repozytorium .git.

git reset HEAD^         # nie ruszaj plików roboczych - pozostaw zmiany
git reset --hard HEAD^  # zresetuj również pliki robocze
git reset --hard HEAD~5 # cofnij się o 5 zapisów
git reset --hard origin # wyczyść wszystkie zmiany i zacznij od nowa
git-checkout -- nazwa_pliku # Przywróć ostatnią wersję pliku z repozytorium dla aktualnej gałęzi
git-checkout revision nazwa_pliku # Przywróć plik w wersji revision

Uwaga!

git-checkout revision #utworzy nam oderwaną gałąź, co nie koniecznie jest oczekiwane


git reset --hard revision # uaktualni HEAD i aktualną gałąź do
punktu określonego przez revision, i uaktualni drzewo robocze
aby odzwierciedlało nowy index.

Jeśli git reset --hard HEAD^ nie zadziała to trzeba trzeba inaczej

git reflog

wyświetli nam się lista operacji i znajdujemy numer tej operacji do której chcemy wrócić.

git reset --hard HEAD@{1}

[1]

[edytuj] Konfiguracja

plik konfiguracyjny może być

  • systemowy $(prefix)/etc/gitconfig (zwykle /etc/gitconfig)
  • global ~/.gitconfig (zwykle /home/nazwa_uzytkownika/.gitconfig)
  • lub bez określenia - dla repozytorium $GIT_DIR/config (zwykle repozytorium/.git/config)

[edytuj] Kolorowanie diff włączone dla jednego użytkownika

git config --global color.diff true

[edytuj] Zmiana domyślnego edytora dla git-commit

Według man git-commit edytor jest ustalany w kolejności: zmienna $GIT_EDITOR, zmienna core.editor, zmienna $VISUAL lub $EDITOR. Ale u mnie na gentoo działa tylko przy ustawieniu $EDITOR w pliku /etc/rc.conf.

EDITOR="vim"

[edytuj] Kolorowanie przy edytowaniu notki dla vima

Ze strony [2] ściągamy git.tar i rozpakowujemy go do katalogu domowego. Potem dodajemy jeszcze plik $HOME/.vim/filetype.vim z zawartością

au BufRead,BufNewFile COMMIT_EDITMSG setf git

Od teraz mamy ładne kolorowanie.

[edytuj] Automatyczne dzielenie okna

Do pliku .vim/ftplugin/git.vim dodajemy wpis

if !exists("g:git_diff_spawn_mode")
    let g:git_diff_spawn_mode = 1
endif

W tym samym pliku g:git_diff_opts = "-C -C" należy zamienieć na g:git_diff_opts = "-C -C --no-color" inaczej nie będzie działało kolorowanie.

Nie działa natomiast dzielenie okna przez mapowanie. Błąd w pliku .vim/ftplugin/git.vim. Funkcja wywoływana z dwoma argumentami zamiast trzema. Czyli powinno być:

noremap <buffer> <Leader>gd :call Git_diff_windows(0, 0, g:git_diff_opts)<cr>
noremap <buffer> <Leader>ghd :call Git_diff_windows(0, 0, g:git_diff_opts)<cr>
noremap <buffer> <Leader>gvd :call Git_diff_windows(1, 0, g:git_diff_opts)<cr>

Jednak nadal nie działa przez wywołanie <Leader>gd lub inne mapowania. <Leader> ustawia się przez :let mapleader =",". Wypisanie aktualnej wartości przez :echo mapleader.

Więcej: [3] [4] [5]

[edytuj] Ignorowanie plików

Możemy ustawić ignorowanie śledzenia zmian dla pewnych plików czy katalogów. Te pliki lub katalogi nie mogą być jednak zapisane w repozytorium. Jeśli są już w repozytorium, to trzeba je usunąć i zatwierdzić zmiany przez git-commit -a.

Ignorowanie możemy określić w kilku plikach w systemie: ~/.gitignore, .git/info/exclude. .gitignore w dowolnym lokalnym katalogu objętym śledzeniem. Dodatkowo plik .gitignore możemy włączyć do śledzenia zmian ponieważ znajduje sie w śledzonym katalogu. Poniższy kod może być umieszczony w dowolnym z tych trzech plików.

# Linie zaczynające się od '#' są traktowane jako komentarze.
# Ignoruj każdy plik o nazwie foo.txt.
foo.txt
# Ignoruj (wygenerowane) pliki html,
*.html
# oprócz foo.html który jest zarządzany ręcznie.
!foo.html
# Ignoruj obiekty i archiwa.
*.[oa]
#ukośnij określa najwyższy poziom w repozytorium
/katalog

Więcej informacji na stronie man gitignore

[edytuj] 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

[edytuj] 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

[edytuj] 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

[edytuj] 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ń

[edytuj] 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".

[edytuj] Reprezentacja historii w Gicie

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.

[edytuj] 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".

[edytuj] 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.

[edytuj] 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).

[edytuj] Trzy użycia "git checkout"

Git checkout może zrobić trzy oddzielne rzeczy:

[edytuj] 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.

[edytuj] 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 ".".

[edytuj] 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ą.

[edytuj] 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.

[edytuj] 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.

[edytuj] 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".

Dla autorów

w innych językach