Zainstaluj paczkę git.
- Instructions on git-scm.com
http://git-scm.com/download/win
Paczka msysgit zawiera także bash-a z podstawowymi poleceniami — w ten sposób znacznie łatwiej korzystać z gita i dodatkowych skryptów.
- Downloads page on git-scm.com
- windows-owy klient GitHub-a można używać nie tylko do repozytoriów tam hostowanych
Gdy brakuje Ci jakiegoś polecenia w bashu, np. do jakiegoś hooka, dobre kompilacje znajdziesz na http://gnuwin32.sourceforge.net/packages.html
grep zawarty w msysgit nie obsługuje opcji -P/--perl-regexp (włączenie perl-owych, bardziej zaawansowanych wyrażeń regularnych).
Sprawdź, czy twój grep ma tą opcję:
$ grep --help | grep -- --perl-regexpJeśli jej brakuje, pobierz kompilację grepa z http://gnuwin32.sourceforge.net/packages/grep.htm i nadpisz plik grep.exe w katalogu bin/.
man [grep](http://linux.die.net/man/1/grep)
https://code.google.com/p/conemu-maximus5/
Edytor używany jest w gicie m.i. do wpisywania komentarzy do komitów.
Wybrany edytor musi działać w następujący sposób:
gitodpala edytor i czeka, aż polecenie skończy się wykonywać,- zamykacie edytor i "zwracacie kontrolę"
git-owi.
Także IDE odpadają. nano, czy pico dają radę. Można też użyć SublimeText dodając parametr -w
Zasadniczo dowolny edytor, którego da się przekonać do poprawnego kodowania i znaków końca linii. Ustawienia dokomujemy w sposób nastepujący:
$ git config --global core.editor "editor command" man [git-config](http://git-scm.com/docs/git-config)
$ echo >> ~/.bashrc
$ wget -qO- https://gist.github.com/marek-saji/5090416/raw/.bashrc >> ~/.bashrc
# jeśli nie ma polecenia "wget":
$ curl -s https://gist.github.com/marek-saji/5090416/raw/.bashrc >> ~/.bashrcPo ponownym włączeniu konsoli, przed znakiem zachęty pojawi się nazwa aktualnego tag-a (jeśli nie istnieje to branch-a) w nawiasach. Jeśli komuś nie podoba się kolor, można skorzystać z tego skryptu, żeby wyświetlić listę dostępnych kolorów: https://gist.github.com/marek-saji/5090435
man [bash](http://linux.die.net/man/1/bash) | less -p^PROMPTING- modularny PS1, zawiera moduły do git-a
Wyświetlając diff-y git wyświetla kontekst, w którym zaszła zmiana. Jeśli zamiast funkcji/metody wyświetla się klasa, można to naprawić:
echo '*.php diff=php' >> ~/.gitattributes
git config --global core.attributesfile "~/.gitattributes"Do repozytorium git można odwoływać się przez wiele protokołów:
ssh://— dostęp przez sshhttp://ihttps://— dostęp via HTTPfile://— dostęp przez lokalną ścieżkę do repozytorium. Można pominąćfile://i po prostu podać ścieżkęgit://— git-owy demon działający na dedykowanym porcie
Naturalnie ze względów bezpieczeństwa najlepiej korzystać przez ssh. Żeby za każdym razem nie trzeba było wpisywać hasła, zwykle ustawia się klucze publiczne.
Generowanie klucza:
# wchodzimy do katalogu konfiguracji ssh, może już istnieć.
$ mkdir ~/.ssh
$ cd ~/.ssh
# jeśli klucz istnieje, jesteśmy szczęśliwi:
$ ls id_rsa.pub
# jeśli nie, należy go wygenerować (bez hasła):
$ ssh-keygen -t rsa -C "twój.email`example.com"Tak wygenerowany klucz wklejamy w ustawieniach w GitHubie, Bitbucket’cie, bądź przekazujemy komuś, kto ma moce to ustawić.
- github:help — Generating SSH Keys
man [ssh-keygen](http://linux.die.net/man/1/ssh-keygen)
$ cd ~/SOMEWHERE/crls/
$ git clone ssh://[email protected]/szp src/
# wejdźmy do katalogu z repozytorium
$ cd src/BAM! (W końcu) Masz repozytorium. Nie przestawaj czytać. {;
man [git-clone](http://git-scm.com/docs/git-clone)
Możliwe, że sklonowane repozytorium zawiera submoduły. Jest to nic innego, jak inne repozytoria git "osadzone" w tym repozytorium. Polecenie git clone nie pobiera ich automatycznie, należy zrobić to oddzielnie:
$ git submodule initman [git-submodule](http://git-scm.com/docs/git-submodule)
Git pozwala wam ustawić jak chcecie się przedstawić w swoich commitach:
$ git config --add user.name "Imię Nazwisko"
$ git config --add user.email "[email protected]"Dodanie parametru --global spowoduje zapisanie tych ustawień w pliku globalnej konfiguracji (~/.gitconfig), zamiast wewnątrz repozytorium (.git/config).
man [git-config](http://git-scm.com/docs/git-config)
Ustawienia gita nie są wysyłane na serwer. Jednak jeśli jest taka potrzeba (dzielenie się aliasami, o których za chwilę, wewnątrz zespołu jest sympatyczną praktyką), można to zrobić. Na repozytorium wrzucamy plik ze spólnymi ustawieniami, np. do .gitconfig. Następnie każdy, kto sklonował repozytorium musi dodać do swojego .git/config:
[include]
path = ../.gitconfigOk, sklonowałeś repozytorium. Oznacza to, że jest na nim wszystko to, co znajduje się na upstream-owym serwerze. W razie wojny nuklearnej projekt jest bezpieczny.
Rozejrzyjmy się, co właściwie dostaliśmy.
$ git remote
originIstnieje jeden remote, domyślnie nazwany origin. To nasz upstream, z którego klonowaliśmy repozytorium i na który będziemy wysyłać rzeczy. Przyjrzyjmy się bliżej:
$ git remote -v
origin git.holonglobe.com:szp (fetch)
origin git.holonglobe.com:szp (push)Fun fact: URL, z którego pobieramy zmiany w ramach danego remote-a wcale nie musi być taki sam, jak ten, na który wysyłamy rzeczy.
Patrzmy dalej.
$ git branch
* masterZnajdujemy się na jedynym lokalnym branchu. master jest zwyczajową nazwą dla głównej, stabilnej gałęzi projektu. Wszelkie zmiany na nim powinny być wprowadzane ze szczególną ostrożnością.
$ git branch -vv
* master 5959cb9 [origin/master] Make two report generating scripts act more consistent.Wygląda na to, że nasz lokalny branch master śledzi zdalny branch master (nazwy nie muszą wcale być takie same) z remote-a origin.
$ git branch -a
* master
remotes/origin/converter
remotes/origin/devel
remotes/origin/master
remotes/origin/next
remotes/origin/story-3319Istnieje kilka zdalnych branch-y, z których dwa, które nas interesują, nie mają jeszcze lokalnych odpowiedników.
$ git checkout -b next --track origin/next
$ git checkout -b devel --track origin/devel
$ git branch
* devel
master
next
story-3319Jesteśmy teraz na branch-u devel. Tutaj głównie toczą się prace nad aktualnym sprintem. Jeśli historyjka, bądź kierat mają być długotrwałe, bądź prace nad nimi mogą przeszkadzać innym użytkownikom repozytorium, będziemy tworzyć branch dla tego issue-a (tutaj: story-3319 dla zlecenia o numerze #3319).
man [git-remote](http://git-scm.com/docs/git-remote)man [git-branch](http://git-scm.com/docs/git-branch)man [git-checkout](http://git-scm.com/docs/git-checkout)
Aliasy to nic innego jak skróty do dłuższych, a często wykonywanych poleceń. Definiuje się je w pliku konfiguracyjnym. Jeśli alias nie zaczyna się od wykrzyknika (!), to traktowany jest jako pod-polecenie git-a (np. alias 1log ustawiony na log --oneline --decorate wykona git log --oneline --decorate); poprzedzenie aliasy wykrzyknikiem pozwala na wykonanie dowolnego polecenia (np. alias test ustawiony na !echo test). Jeśli polecenie jest długie, dobrym zwyczajem jest umieścić je w skrypcie w katalogu .git/bin/ i w aliasie wywołać skrypt.
Wszystkie parametry podane przy wywołaniu aliasu są przekazywane, więc np. 1log z powyższego przykładu możemy wywołać jako git 1log --graph. Możemy odwoływać się do konkretnych parametrów aliasu tak jak w bashu ($1, $2…); jeśli chcemy, żeby parametry nie zostały przekazane do zaliasowanego polecenia, można na jego końcu dodać znak komentarza (#), który wykomentuje dodatkowe argumenty. Tak więc alias test2 ustawiony na !echo $2 # wyświetli tylko drugi parametr.
Podanie parametru --help przy wywołaniu aliasu wyświetli zaliasowane polecenie..
Trochę przydatnych aliasów (można dodać do globalnie, w ~/gitconfig):
[alias]
; slim log
lg = log --graph --pretty=slim --abbrev-commit --date=relative
; lg date: sorted by date and with absolute dates
lgd = log --graph --pretty=slim --abbrev-commit --date=local --date-order
; lg upstream: also show upstream branch
lgu = !git lg $( git rev-parse --symbolic @{u} ) HEAD
; lg date, upstream
lgdu = !git lgd $( git rev-parse --symbolic @{u} ) HEAD
; lg me: my commits from all branches, by date
lgme = !git lgd --author=\"$( git config --get user.name )\" --all
; grep with some context (and a header)
hgrep = grep --heading -B3 -A3
; diff by word
diffword = diff --word-diff --word-diff-regex='\\w+|[^[:space:]]'
; diff by character
diffchar = diff --word-diff --word-diff-regex='.'
; checkout files with only whitespace changes
whitespacecheckout = "!git status --porcelain | grep '^.M' | cut -b4- | while read file ; do if test -z "$( git diff -w "$file" )"; then git checkout -- "$file" ; fi; done"
; rebase not-yet-pushed commits
rebaselocal = "!REMOTE=$( git rev-parse --abbrev-ref HEAD@{u} ) ; if [ -n "$REMOTE" ]; then git rebase -i $REMOTE ; else echo "ERROR: Unable to determine remote branch" ; fi"
[pretty]
slim = "%C(red)%h%C(yellow)%d%C(reset) %s %C(green)(%cd) %C(bold blue)<%an>%C(reset)"Sekcja [pretty] definiuje ładniejszy, jednolinijkowy format dla git-log.
man [git-config](http://git-scm.com/docs/git-config) | less -p'alias.*'
Hooki to rzeczy, które uruchamiają się w reakcji na jakieś zdarzenie. Można ich użyć do sprawdzenia, czy komitowany przez nas kod jest dobrze sformatowany, nie ma w nim pozostałości z debug-owania etc.
Zestaw przydatnych hook-ów:
# wchodzimy do katalogu, w którym znajduą się lokalne hook-i
$ cd .git/hooks/
# usuwamy to, co tam teraz jest (przykładowe rzeczy)
$ rm *
# klonujemy gist-a hookami:
# (git-in-a-git, fancy that!)
$ git clone https://gist.github.com/0b3e22847a2d5b732373.git .Po nazwach można się łatwo domyślić co robi każdy z nich:
common: wspólne funkcje etcpre-commit,post-checkout: hooki uruchamiane przed komitowaniem (mogą powstrzymać commit) i po checkout-cie (np. zmiana brancha)pre-commit-*,post-checkout-*: sprawdzanie/robienie poszczególnych rzeczy w ramach tych hooków.
Jeśli nie chcemy używać jakiegoś hooka, można go jednorazowo wyłączyć:
# wyłączenie sprawdzania białych znaków
$ git -c hooks.whitespace=false commit
# wyłączenie hooków w ogóle
$ git -c hooks.all=false checkoutMożna też wyłączyć hook na stałe, np. większość z was zechce wyłączyć swap-ctags:
$ git config --add hooks.swap-ctags falsePodobnie jak w przypadku konfiguracji miło jest dzielić się hookami z resztą zespołu. Można to zrobić trzymając je na w repozytorium, np. w git-hooks i podlinkować go do wewnątrz .git:
cd .git
ln -sfn ../git-hooks hooksman [githooks](http://git-scm.com/docs/githooks)
Zanim zaczniemy pracę ważne jest zrozumienie podstawowych konceptów związanych z git-em.
Kod (bądź ogólnie "treści") mogą znajdować się w jednej z następujących przestrzeni:
- stash Przestrzeń służąca do "odkładania" na bok rzeczy z katalogu roboczego.
- workspace / working directory
Katalog roboczy — wszystko to, co leży poza katalogiem
.git. Nad tym pracujemy. - index / staging area Służy do komponowania komitów.
- local repository Nasze lokalne repozytorium. Póki nie wypchniemy z niego komitów, możemy je dowolnie zmieniać, usuwać, przestawiać. Tutaj istnieją również lokalne branch-e, które wcale nie muszą nigdy zostać wypchnięte nigdzie wyżej (mogą zostać usunięte, bądź zmerge-owane do innego branch-a, który zostanie wypchnięty).
- upstream repository Repozytorium, przez które zachodzi synchronizacja, między ludźmi pracującymi w projekcie.
- Interactive Git Cheatsheet Dobrze pokazuje relacje między "przestrzeniami", a poszczególnymi poleceniami.
Jak być może zauważyliście rozglądając się, repozytorium posiada wiele branch-y, jednak w katalogu roboczym widzimy tylko jeden z nich. Możemy przełączać branch, nad którym pracujemy:
$ git branch
* devel
master
next
story-3319
# Przełączenie z devel na next
$ git checkout next
$ git branch
devel
master
* next
story-3319
# Powrót do ostatniego branch-a -- analogicznie do `cd -`
$ git checkout -
* devel
master
next
story-3319Poleceniem można również "wgrać" stan z wybranego branch-a, bądź innego REF dla wybranych plików:
# Wycofanie lokalnych zmian
# (wgranie stanu z ostatniego commit-a)
$ git checkout .
# To samo, co powyżej: `HEAD` określa commit "na którym jesteśmy",
# natomiast `--` oddziela REF od
$ git checkout HEAD -- .
# Możemy też użyć trybu interaktywnego, i wybrać, co chcemy
# wyrzucić po kawałku:
$ git checkout -p .
# Możemy też np. zcheckout-ować kawałek
# wybranego pliku z innego branch-a.
$ git checkout -p devel -- public/index.php… acz do ostatniego prawdopodobnie lepiej użyć git-cherry-pick służący do przenoszenia pojedynczych commitów pomiędzy branchami.
man [git-checkout](http://git-scm.com/docs/git-checkout)man [git-cherry-pick](http://git-scm.com/docs/git-cherry-pick)
Komitując zapisujemy zmiany w lokalnym repozytorium.
# status plików w repo (zmodyfikowane, nie śledzone, usunięte, zmieniona nazwa)
$ git status
# dodanie plików
$ git add PLIK PLIK PLIK
# interaktywne dodawanie plików po kawałku
$ git add -p PLIK PLIK
# commit
$ git commit
# możemy też od razu podać commit msg:
$ git commit -m "Komentarz"
# obejrzenie ostatniego commit-a, czy wszystko jest w porządku
$ git showWywołanie git commit bez opcji -m spowoduje użycie ustawionego edytora
W odróżnieniu od subversion, skomitowanie nie równa się wysłaniu zmian na zdalny serwer. Patrz rozdział "Synchronizowanie z upstream-em".
man [git-status](http://git-scm.com/docs/git-status)man [git-add](http://git-scm.com/docs/git-add)man [git-commit](http://git-scm.com/docs/git-commit)man [git-show](http://git-scm.com/docs/git-show)
Wpisując commit message należy przyjąć konwencję, że pierwsza linijka jest krótkim opisem (najlepiej do 70 znaków). Po niej może nastąpić pusta linijka przerwy i dłuższy opis (linie do 70 znaków).
Należy się tego trzymać, bo git lg, git log --oneline, czy git merge --log używają tylko tej pierwszej linijki.
- Odniesienia do issue-ów poprzedzamy słowami:
- "fixes", jeśli commit naprawia błąd,
- "closes", jeśli commit zamyka issue innego typu,
- "refs", jeśli commit jest powiązany w inny sposób.
np.
fixes #42, refs #13
- Opisujemy, czym jest commit, a nie jaki był motyw jego wprowadzenia:
- "Redirect to main page, when removing status. fixes #42",
- a nie: "Fix weird bug",
- czy nawet nie: "fixes #42: Weird thing happens, when removing status",
- i brońboże nie samo: "fixes #42".
- Używamy formy:
- "Change property visiblity",
- a nie "Changes property visibility".
Przykład:
Make two report generating scripts more consistent. refs #4596, #5167
- Generated files start with "report-" and contain date and time of generation.
- Both script and generated files names start with "report-".
- Make passing year to report-error.log.sh optional.
Literówka w commit msg? Zapomniany plik? W łatwy sposób można dodać coś do ostatniego commita:
# jeśli zapomnieliśmy jakiegoś pliku dodajemy go do staging area
$ git add PLIK
$ git commit --amend
# uruchomi się nam edytor pozwalający na zmianę commit msg-a.Nie należy tego robić, jeśli już spushowaliśmy ostatni commit.
man [git-commit](http://git-scm.com/docs/git-commit)
Nie podając nazwy remote-a, będziemy pracować na domyślnym (zwykle origin; zwykle używamy tylko jednego).
Mając lokalne commity możemy wypchnąć je na serwer:
# spójrzmy, co chcemy wysłać (zamiast $BRANCH nazwa obecnego branch-a)
$ git lgu
# jeśli wygląda w porządku:
$ git pushman [git-push](http://git-scm.com/docs/git-push)
Ściągnięcie zmian z upstream-u:
$ git pull
# Jeśli mieliśmy niespushowane commity, może pojawić się
# rozgałęzienie w kodzie i dodatkowy merge-ujacy commit;
$ git log
* 163ee1f Merge branch 'devel' of ssh://example.com/fooer into devel (Mon Mar 25 12:51:52 2013) <Gallus Anonymus>
|\
| * 5660b07 Always cast report filters to array. refs #4399 (Mon Mar 25 12:37:57 2013) <Other Guy>
* | 0cd6b6c fix recursive directory removing, fixes #5418 (Mon Mar 25 12:51:32 2013) <Gallus Anonymus>
|/
* adecdab Fix legacy invocation of getEntity(). refs #5131 (Mon Mar 25 11:25:36 2013) <Other Guy>
* 5b666c1 Properly filter RDashboard, when upgrading order_number. refs #5390 (Mon Mar 25 10:43:55 2013) <Other Guy>
…
# Jeśli chcemy tego uniknąć, możemy poprosić pull, żeby
# użył rebase-owania, zamiast merge-owania:
$ git pull --rebase
# można też zrobić po kolei
$ git fetch
$ git rebase # bądź `git merge`git pull --rebase różni się pod kilkoma względami:
- niezpushowane commity zmienią swój hash (ponieważ zmieni im się rodzic)
- w przypadku konfliktów będziemy musieli je rozwiązywać dla każdego ze swoich commitów z osobna — tak, aby dały się zaaplikować na swoich nowych rodziców
man [git-pull](http://git-scm.com/docs/git-pull)
Tworzenie branch-a dla każdej niebanalnej zmiany jest dobrym pomysłem. Dzięki temu:
- nie blokujemy wyrzutów z branch-a: Zwłaszcza istotne na branch-ach powyzej deweloperskiego: tam najlepiej nawet drobne naprawy robić na osobnych branch-ach i oddawać je komuś do spojrzenia.
- wygodniej przeprowadzić code review: wystarczy przejrzeć commit-y z branch-a,
- nie musimy dodawać
refs #STORY_ISSUEdo każdego commit-a: wystarczy dodać do commit-a merge-ującego do głównej gałęzi, - łatwiej dołączyć kogoś do realizacji historyjki: w odróżnieniu od grzebania w niej na nieskomitowanych plikach: "Chcesz pomóc? Ok.. tylko to ukryję to, skomituję, później będziesz musiał w konfigu sobie lokalnie....."
- łatwiej pracować nad kilkoma rzeczami równolegle: Niespushowane commity z historyjki nie przeszkadzają w niczym, żeby zabrać się za krytyczny błąd na głównym branchu,
- zen and stuff.
$ git branch story-1337-kittens
$ git checkout story-1337-kittens
# bądź jedym poleceniem
$ git checkout -b story-1337-kittensTo utworzy branch na podstawie tego, na którym jesteśmy aktualnie zcheckoutowani.Możemy utworzyć branch z innego commit-a podając dodatkowy parametr:
git branch story/1337-kittens 57ed6b3
git branch story/1337-kittens next
git branch story/1337-kittens HEAD~man [git-checkout](http://git-scm.com/docs/git-checkout)man [gitrevisions](http://git-scm.com/docs/gitrevisions)
Pracowałeś nad czymś przez chwilę, skomitowałeś ze dwie rzeczy i doszedłeś do wniosku, że może powinno to się znaleźć na dedykowanym branchu. Jak to zrobić?
☠ Jeśli nie czujesz się pewnie, zbackupuj w tym momencie swoje repozytorium.
# tworzymy lokalny branch z aktualnego HEAD-a
$ git branch story-1337-kittens
# szukamy ostatniego commit-a, który nie dotyczył story/1337-kittens
$ git lg
# przesuwamy branch bazowego na ten commit:
$ git reset --hard $COMMIT
# zobacz, czy wszystko wygląda, jak należy
$ git lg
# przechodzimy na nowy branch i piszemy dalej / pushujemy
$ git checkout story-1337-kittensKiedy chcemy opublikować pracę (z kimś współpracujemy, bądź chcemy oddać historyjkę do testów).
Najlepiej robić to będąc na lokalnym branch-u, którego nie ma jeszcze w upstream-ie. Próbujemy go zpushować, co się nie uda, ale git podpowie nam co pewnie chcemy zrobić:
(bug-1337) $ git push
fatal: The current branch fo has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin bug-1337
(bug-1337) $ git push --set-upstream origin bug-1337Kiedy nasz branch jest gotowy do połączenia z głównym, możemy go zmerge-ować i usunąć zarówno lokalny, jak i zdalny branch:
git checkout devel
git merge --no-ff --edit story/1337-kittens
git branch -d story/1337-kittens
git push origin :story/1337-kittensOpcja -d nie pozowli na usunięcie niezmerge-owanego brancha — niezmerge-owane commity zostały by wtedy "oderwane od drzewa". Można to wymusić używając -D.
Opcje --no-ff --edit pozwoli na zmodyfikowanie commit message-a: można tutaj wpisać refs #1337 tak, aby commit pojawił się na stronie issue-a.
Ostatnie polecenie wygląda dość dziwnie — od publikowania różni się tylko głupim dwukropkiem. Wynika to z tego, że pełna postać polecenia git-push to:
git push REMOTE LOCAL_BRANCH:REMOTE_BRANCH… a usunięcie zdalnego branch-a, to tak jakby wypchnięcie w niego nicości.
man [git-branch](http://git-scm.com/docs/git-branch)man [git-push](http://git-scm.com/docs/git-push)
Submoduły pozwalają na podpięcie upstream-owych projektów, bądź na rozbicie naszego własnego projektu na mniejsze, logiczne, reużywalne części. W odróżnieniu od svn-owych externals submoduły pozwalają na określenie rewizji, w której ma zostać użyty dany submoduł. Chroni nas to przed sytuacją, w której autorzy któregoś z submodułów zmienią API, co popsuje naszą aplikację. Zmiana rewizji submodułu jest zawsze akcją świadomą.
Polecenie wywołujemy zarówno po sklonowaniu repozytorium, jak i kiedy ktoś doda nowe submoduły (pojawiają się w komunikacie przy git pull).
# zarejestrowanie nowych submodułów w .git/config
$ git submodule init
# sklonowanie ich
$ git submodule update$ git submodule add git://github.com/HolonGlobe/HoloGram.git library/HolonGlobeTak dodany moduł zostanie dodany do staging area w oczekiwaniu na skomitowanie.
Jak było wspominane, submoduły "ustawione" są na konkretny commit. Co robimy kiedy pojawia się np. nowa wersja upstreamowej biblioteki?:
# wchodzimy do katalogu submodułu
$ cd library/HolonGlobe
# checkout-ujemy inną wersję
$ git checkout v1337
# wracamy do katalogu głównego repozytorium
$ cd -
# tutaj widzimy, że katalog z submodułem jest oznaczony jako zmieniony
$ git status
# więc możemy go skomitować
$ git add library/HolonGlobe
$ git commitTu sprawa jest mniej banalna. Patrząc na manual od git-submodule okazuje się, że nie ma tam operacji uzuwania. Trzeba ręcznie wytrzeć wszelkie ślady istnienia modułu:
- usunąć sekcję z
.gitmodules - usunąc sekcję z
.git/config - usunąć katalog z git-a:
git rm --cached path_to_submodule(no trailing slash). - commit
- usunąć już nieśledzone pliki submodułu
man [git-submodule](http://git-scm.com/docs/git-submodule)- Git Submodule Tutorial at official git's wiki
- Git Tools — Submodules chapter in the Git Book
Do zarządzania repozytoriami można używać z gitosis. Konfiugorwanie polega na sklonowaniu [email protected]:gitosis-admin edycji plików konfiguracji i pushowaniu.
Na serwerze wszystko leży zwykle w /home/git (~git).
.gitosis.conf → ~git/repositories/gitosis-admin.git/gitosis.confObowiązująca konfiguracja.repositoriesZałożone repozytoria oraz specjalnegitosis-admin.git..ssh/authorized_keysKlucze publiczne autoryzowane do dostępu.
Należy ograniczyć ręczną konfigurację, bo może zostać nadpisana przez zpush-owanie na gitosis-admin.
UWAGA Wszystkie polecenia zakłądają, że działamy jako użytkownik git! Robienie jako root może łatwo popsuć uprawnienia i odciąć ludziom dostęp do repo (patrz pierwszy trouble).
$ ssh [email protected] -t sudo -u git -iJeśli sklonujemy gitosis-admin nie przez ssh, tylko system plików i tak zpushujemy, to w repozytorium powstaną pliki z uprawnienami dla tego użytkownika. Jeśli był nim root, to psujemy uprawnienia dla wszystkich innych użytkowników. Wystarczy przywrócić uprawnienia do katalogu domowego użytkownika git:
$ sudo chown -R git:git ~gitMożliwe, że link do hook-a jest znów popsuty (nowa wersja python-a?):
$ cd ~/repositories/gitosis-admin.git/hooks/
$ ls -l post-updateZdarzyło mi się, że chciałem założyć repozytorium, ale był popsuty hook. Naprawiłem go, ale nawet jak wycofałem commit dodający repo i znów go pchnąłem, repozytorium mi się nie utworzyło. Można to zrobić ręcznie:
$ cd ~/repositories/
$ git init --bare repo_name.gitRepozytoria w ~/repositories są takie same jak te, na których pracujemy. Jedyną różnicą jest to, że posiadają tylko zawartość katalogu .git. Można na nich wykonywać wszystkie polecenia dodając opcję --bare:
$ cd ~/repositories/repo_name.git
$ git --bare log
# nie mamy aliasu `lg`, to jest jest podobne (bez dat)
$ git --bare log --graph --oneline --decorate --date --branches
# lista branchy z ostatnimi komitami
$ git --bare branch -v
# lista tagów
$ git --bare tagNie trzeba chyba dodawać, że komitowanie/pushowanie/pullowanie tutaj jest bardzo złym pomysłem.
Dzięki git-svn migracja odbywa się bardzo gładko.
Zasadniczo wystarczy pobrać repozytorium via git-svn i zpushować je do nowoutworzonego repozytorium git.
# klonujemy repozytorium svn przez git-svn
$ git svn clone …
# zapisz ignorowane pliki w .gitignore
$ git svn show-ignore >> .gitignore
$ git add .gitignore
$ git commit -m 'Convert svn:ignore properties to .gitignore.'
# dobrze jest w tym momencie zrobić backup
$ cp -ra . ../svn-git-migration-backup
# porządkujemy repozytorium:
# - można usunąć niepotrzebne branche
# - poustawiać tagi
# - naprawić autorów komitów (o tym poniżej)
$ …
# dodajemy remote z pustym repozytorium git
$ git remote add git-migration git.holonglobecom:repo_name.git
$ git push --all git-migrationTeraz dobrze na nowo sklonować repozytorium, tym razem z git-a, żeby pozbyć się git-svn-owych śmieci.
git-svn pozwala na mapowanie autorów svn-owych na pełniejszą formę git-ową (--autors-file w git-svn / svn.authorsfile w konfigu). Skorzystamy z tego pliku, żeby zmodyfikować autorów w samym repozytorium, przed zpushowaniem go.
Teraz możemy zmodyfikować komity w lokalnym repozytorium używając skryptu z https://gist.github.com/marek-saji/5232629. Znajduje się tam też instrukcja jak utworzyć plik plik authorsfile dla repozytorium SVN.
- pytanie How do I change the author of a commit in git? na StackOverflow
- blogpost Converting a Subversion repository to Git
man [git-svn](http://git-scm.com/docs/git-svn)
Damn! Byłem na złym branchu, jak to (z)robiłem!
git log # i szukasz hash-a commita
git checkout next
git cherry-pick TEN_HASHJeśli trzeba, użyj git rebase -i, żeby interesujący nas commit był ostatni.
git checkout INNY_BRANCH
git cherry-pick TAMTEN_BRANCH
git checkout INNY_BRANCH
git rebase -i HEAD~~
# i usuń z listy commit (inaczej zostanie na TAMTEN_BRANCH)git stash save 'możesz podać komentarz, ale nie musisz'
git checkout next
git stash poptylko niektórych:
# -p włączy interaktywne wybieranie kawałków
git stash save -p 'to, co tutaj ma zostać'
git stash save 'to, co ma iść gdzie indziej'
git checkout GDZIE_INDZIEJ
git stash pop
git commit -a
git svn dcommit
git checkout - # btw. to wraca na poprzedni branch, na którym się było
git stash pop- stashin'
git stash save "opcjonalna nazwa"
git stash
* listin'
`git stash list`
* oglądanie ostatniej zestashowanej zmiany
`git stash show -p`
* oglądanie _nie_ ostatniej zestashowanej zmiany
```sh
git stash listh # i szukamy identyfikatora (stash@{LICZBA})
git stash show -p stash@{LICZBA}
- więcej:
- git-stash(1)(git-stash man page)
- Stashing chapter in the Git Book
git revert SOME_COMMIT_HASHUtworzy to nowy commit, który cofnie (założy odwrotne zmiany) wskazany commit.
- Wycofanie wszystkich zmian w workingcopy (tylko w śledzonych plikach) do stanu z ostatniego commit-a:
git reset --hard - Wycofanie ostatniego nieskomitowanego(!!) commit-a do workingcopy:
git reset HEAD~
Podstawowe opcje git reset:
- nie podanie żadnego ref zostanie użyte
HEAD --hardusunie wszelkie zmiany, jakie mamy w workingcopy, podczas gdy pominięcie go pozostawi je nienaruszone.--softprzywróci zmiany nie do workingcopy, ale do staging area.
Więcej w git-reset(1)
Nie można zrobić tam wszystkiego tego, co z konsoli, ale czasem jednak wygodniej przegląda się logi, czy wybiera pliki do skomitowania przy pomocy graficznego interfejsu.
- TortoiseGit ssie, z tego co mi wiadomo.
- gitk
Najwyraźniej na windowsie odpalany poleceniem:
git gui - gitg
apt-get install gitgŁadniejszy, niż gitk, ale nie ma na windowsy. - GitHub for Windows http://windows.github.com Z tego co wiem, działa nie tylko z repozytoriami z GitHub-a. Używałeś? Napisz, czy warto.
Przy czystym working copy (można użyć git stash).
# szukasz ostatniego commita, jaki jest na repo
git lg
git rebase -i TEN_COMMITOstatnie polecenie wyświetli listę z:
- co zrobić z tym commitem (
pick,reword,fixup,squashetc) Pod listą opisane jest co znaczą poszczególne rzeczy. - hash
- commit msg
Można zmieniać kolejność oraz zmienić pierwszą kolumnę.
UWAGA
- rebase-owanie z udziałem comitów, które są już na repo to bardzo zły pomysł
- usunięcie linijki z komitem spowoduje usunięcie samego commita
Więcej:
- git-rebase(1)
- Rebasing chapter in the Git Book

