Po pierwsze must-have alias w systemie (~/.bashrc
lub ~/.zshrc
)
alias g="git"
Kolejny must-have to completions do gita (google: "git [bash|zsh] completions")
Pozwala to TAB-em uzupełniać nazwy subkomend, parametrów, remote'ów, branchy itd, podobnie jak TAB-em uzupełnia się nazwy plików i katalogów w konsoli.
Kilka opcjonalnych aliasów, przyspieszających najczęstsze akcje
alias gp="git push"
alias gpl="git pull"
alias gs="git status"
alias gd="git diff"
alias gf="git fetch"
Przyspiesza to pracę
gp p master
gf p
gd Gemfile
Bardziej rozbudowane aliasy można zdefiniować w ~/.gitconfig, najbardziej wg mnie przydatne:
ci = commit
co = checkout
br = branch
nb = checkout -b # create new branch
remote = remote -v # show remotes verbosely by default
hard = reset --hard # reset all changes
undo = reset --soft HEAD^ # go back before last commit, with files in uncommitted state
append = commit --amend -C HEAD # append changes to previous commit
Szczególnie git append
to dla mnie duży time-saver, bo często pracując nad daną funkcjonalnością tworzę commit, a potem jeszcze testuję kod lokalnie lub na stagingu. Zamiast ewentualne fixy dodawać jako kolejne commity i robić w ten sposób bałagan w historii robię po prostu:
g add [files]
g append
W ten sposób dodaję kolejne zmiany do poprzedniego commita w bardzo szybki sposób (git nie pyta nawet o commit message).
Aby powrócić do poprzedniego brancha bez konieczności wpisywania jego często długiej nazwy warto korzystać z git checkout -
, w którym -
jest aliasem na ostatnio odwiedzonego brancha.
[master] g co feature/branch
[feature/branch] g co -
[master] # jesteśmy z powrotem na masterze
[master] g co -
[feature/branch] # i znowu na feature/branch'u
Zresztą wszystkie komendy gita, które działają na branchach obsługują -
, np. git merge -
, git rebase -
.
Swoją drogą, systemowy cd -
działa analogicznie.
Aby przejrzeć historię bez przechodzenia na githuba, bitbucketa lub inne wizualne narzędzie, służy git log
. Domyślnie pokazuje on paginowaną listę wszystkich commitów w obecnym branchu.
Wyświetlenie tylko kilku ostatnich commitów
g log -5
Wyświetlenie commitów wraz z diff'ami (-p
oznacza --patch
)
g log -5 -p
Wyświetlenie historii zmian wybranego pliku
g log path/to/file
Można też zobaczyć historię zmian tylko dla wybranej części pliku, np. zakresu linii zawierającego definicję wybranej metody. Git radzi sobie tu nawet gdy wybrany fragment pliku w różnych momentach przesuwał się w górę lub dół (gdy dodawano lub usuwano linie ponad nim), kurczył lub rozszerzał (gdy dodawano lub usuwano linie w środku bloku).
Np. gdy chcemy zobaczyć jak zmieniały się linie 10-20 w modelu User:
g log -L 10,20:app/models/user.rb
Aby zobaczyć lub odtworzyć starą wersję danego pliku należy podać sha tej rewizji:
g show ff1267ec2:path/to/file # printuje na stdout
g show ff1267ec2:path/to/file > new_file # printuje do pliku
W wyjątkowych przypadkach może się okazać, że chcemy odzyskać plik lub commit, którego nie ma już w żadnym branchu, np. gdy skasowaliśmy branch lub sam commit. Jest to możliwe, ponieważ git w swojej bazie trackuje wszystkie kiedykolwiek utworzone obiekty (commity, stash'e itd) oraz historię akcji, które powodują zmianę rewizji/sha (np. checkout, pull). Dzięki temu można się cofać w czasie, a także wyciągać te obiekty z historii.
Wyświetlenie historii wykonuje się przez git reflog
. Otrzymując w ten sposób sha interesujących nas rewizji, możemy np. dostać się do indywidualnych plików przez git show
lub wyciągnąć np. cały stary commit do obecnego brancha przez git cherry-pick [sha]
Oczywista oczywistość - warto tworzyć feature branche przy rozwijaniu większych funkcjonalności.
Branche za pierwszym razem pushujemy do remote'a z opcją -u
(lub --set-upstream
) co automatycznie ustawia tracking pomiędzy lokalnym i zdalnym branchem, dzięki czemu potem nie trzeba explicite podawać nazwy remote'a i brancha przy pullowaniu i pushowaniu.
# Pierwszy push z ustawieniem trackowania
[feature/branch] g push -u origin feature/branch
# Potem wystarczy tylko
[feature/branch] g push
[feature/branch] g pull
Mergowanie feature brancha do mastera powinno się robić jako non-fast-forward (--no-ff
), nawet jeśli od punktu odgałęzienia od mastera nie doszły do niego żadne nowe commity. Dzięki temu po pierwsze tworzy się merge-commit i w historii jasno widać co się działo, a po drugie w razie potrzeby wycofania całej funkcjonalności wystarczy zrevertować ten merge-commit.
Można takie zachowanie ustawić na sztywno w gitconfig'u. W tym przypadku taka strategia mergowania jest ustawiona dla brancha master i staging.
[branch "master"]
mergeoptions = --no-ff --no-edit
[branch "staging"]
mergeoptions = --no-ff --no-edit
Z kolei podczas mergowania lokalnego brancha ze zmianami z remote'a (czyli np. przy git pull
) należy merge-commitów unikać, bo tworzą one tylko bałagan w historii. Tu najlepiej dbać o możliwie liniową historię commitów. Czyli tutaj idealnie powinniśmy używać strategii fast-forward (--ff
), jednak nie zawsze jest to możliwe - konkretnie w przypadku gdy nasz lokalny branch zawiera commity, których nie spushowaliśmy do remote'a oraz remote zawiera commity, których nie jeszcze spullowaliśmy. Pozostaje nam rebase, czyli nałożenie naszych lokalnych zmian na zmiany z remote'a, aby zachować liniową historię.
[master] g rebase origin/master
Lub szybciej
[master] g pull --rebase
Lub jeszcze szybciej - ustawiając w gitconfigu, aby domyślnie mergował z remote branchem lub rebase'ował.
[branch]
autosetupmerge = true
autosetuprebase = always
Wtedy zawsze możemy standardowo korzystać po prostu z git pull
i nie zastanawiać się co się dzieje pod spodem.
Małym minusem jest to, że trzeba umieć sobie poradzić kiedy podczas rebase pojawią się konflikty.
Będąc przy temacie konfliktów - czasem zdarza się, że te same konflikty musimy rozwiązać wielokrotnie (np. przy mergowaniu zmian do kilku branchy lub ponawiając wcześniej przerwany rebase). Git potrafi zapamiętać już raz rozwiązane konflikty i potem automatycznie je rozwiązywać samemu. Wystarczy w gitconfigu włączyć rerere
:
[rerere]
# Remember my merges
# http://gitfu.wordpress.com/2008/04/20/git-rerere-rereremember-what-you-did-last-time/
enabled = true
Polecam nazywać remote'y heroku krótkimi nazwami, wskazującymi na środowisko aplikacji.
git remote add p [email protected]:production-app.git
git remote add s [email protected]:staging-app.git
Dzięki temu potem można szybciej wykonywać komendy oraz deployować kod.
heroku run console -r p
git push p master
Aby podejrzeć kod źródłowy gema, używanego w aplikacji
bundle open [gem]
Gem otwierany jest w domyślnie ustawionym w systemie edytorze (przez zmienną $EDITOR
), ale można też sobie to nadpisać przez ustawienie zmiennej $BUNDLER_EDITOR
. Np. export BUNDLER_EDITOR="atom"
.
Pretty-formatowanie JSON'a w konsoli przez stworzenie aliasu w ~/.bashrc
lub ~/.zshrc
:
alias jsonp="ruby -r json -e 'puts JSON.pretty_generate(JSON.parse(readlines.join))'"
Można go potem używać jak standardowej unixowej komendy, która działa na stdin/stdout tak jak powinna:
curl api.com/file.json | jsonp
jsonp < file.json > pretty_file.json
Dobrym pomysłem jest też stworzenie sobie pliku z różnymi komendami (Unix / Git / Rails / Postgres), które np. trudno zapamiętać i zaaliasować sobie ten plik w taki sposób:
Dzięki temu jedną literą w command linie jesteśmy w stanie przejść do zapisanych komend.
Wyszukiwanie komend można robić w less-ie przez / albo w shellu przez np.
c | grep <string>