Przejdź to tekstu

Krótka lekcja gita

Kategoria: Artykuły, etykiety: diff, git, github, meld, repozytorium, vim

Dodany przez: morfik, 2015-02-17 21:21 (zmodyfikowany: 2015-02-17 21:27)

Wyświetleń: 22188

Od paru dni eksperymentuję sobie z gitem i postanowiłem zrobić drobną ściągawkę na temat jego obsługi z poziomu linii poleceń. Konfigurowanie systemu jak i samego githuba tak by korzystać z kluczy SSH jak i GPG zdążyłem już opisać i nie będę tego poruszał w tym tekście. Oczywiście te dwie rzeczy nie są wymagane do pracy z gitem ale przydają się. W każdym razie, tutaj zajmę się podstawowymi operacjami jakie zwykle człowiek będzie dokonywał gdy przyjdzie mu korzystać z gita.

Zdaję sobie sprawę, że są różne nakładki graficzne na gita ale jak się okaże po przeczytaniu tego artykułu, cmd nie jest taki straszny, choć sama dokumentacja może przerazić, bo jest tego dość sporo i raczej wszystkiego od razu nie ogarniemy.

Na sam początek doinstalujmy paczkę git , bo to w niej znajdziemy narzędzia, przy pomocy których będziemy dokonywać zmian w repozytorium. Pakiet jest dostępny w debianie, także sama instalacja nie powinna nam przysporzyć problemów.

1. Konfiguracja gita

Gita konfiguruje się za pomocą polecenia git config . Tutaj jest obszerny opis poszczególnych opcji. Dodatkowe informacje można znaleźć również tutaj.

Konfiguracji można dokonywać na dwa sposoby -- przy pomocy powyższego narzędzia, albo też przez ręczne uzupełnianie plików konfiguracyjnych, a te są czytane z określonych lokalizacji i w określonej kolejności. Pierwszym z nich jest plik systemowy (dodanie --system do git config) i ten jest zlokalizowany w /etc/gitconfig -- zwykle nie jest obecny w systemie. Drugi w kolejności jest plik użytkownika (dodane --global do git config) i jest to ~/.gitconfig (ew. ~/.config/git/config , jeśli istnieje). To zwykle na nim będziemy operować dodając nowe opcje lub zmieniając stare. Ostatnie miejsce gdzie git zagląda w poszukiwaniu konfiguracji, to lokalizacja repozytorium. Po wydaniu git clone lub git init , plik konfiguracyjny będzie dostępny w repo/.git/config . To jest lokalna konfiguracja, z której będzie korzystało określone repozytorium i jeśli mamy jakieś niestandardowe opcje, które chcemy zaaplikować tylko do tego danego repo, to właśnie w tym pliku musimy je umieścić. Można także określić swój własny plik przy pomocy opcji -f dopisanej do git config . Nam jednak wystarczy plik użytkownika.

Jeśli chodzi o zapis opcji konfiguracyjnych, to jest trochę inaczej. Pierw jest czytany plik konfiguracyjny repozytorium, następnie plik użytkownika i na końcu konfiguracja systemowa. Dlatego też by wstępnie skonfigurować sobie gita, trzeba pamiętać o dopisaniu do git config opcji --global .

Dość tego gadania, pora przejść do działania. Na początek skonfigurujmy sobie kilka podstawowych parametrów:

$ git config --global user.name "Mikhail Morfikov"
$ git config --global user.email morfik@nsa.com
$ git config --global gpg.program gpg
$ git config --global user.signingkey 0x72F3A416B820057A
$ git config --global push.default simple
$ git config --global color.ui always
$ git config --global core.whitespace blank-at-eol,blank-at-eof,space-before-tab,tabwidth=4
$ git config --global merge.tool meld
$ git config --global diff.tool meld
$ git config --global core.editor vim
$ git config --global core.pager less
$ git config --global core.excludesfile ~/.config/git/.gitignore_global
$ git config --global core.ignorecase false
$ git config --global core.eol lf
$ git config --global core.autocrlf input
$ git config --global web.browser firefox
$ git config --global browser.firefox.path /opt/firefox/firefox
$ git config --global browser.firefox.cmd 'firefox -P default'
$ git config --global log.date iso8601
$ git config --global log.decorate full

Powyższe opcje mówią raczej same za siebie i nie trzeba ich dodatkowo wyjaśniać. Oczywiście nie musimy wszystkiego ręcznie wpisywać i ustawiać -- można dociągnąć paczkę git-gui , odpalić wersję gui via git gui i wejść tam w opcje i pozmieniać wedle uznania. To powinno wygenerować dość przyzwoity plik konfiguracyjny, z tym, że nie wszystko idzie ustawić w ten sposób. Poza tym, graficzne aplikacje ustawiają swoje własne opcje i ignorują przy tym niektóre parametry wpisane w pliku konfiguracyjnym.

2. Tworzenie repozytorium

Mamy z głowy zatem wstępną konfigurację gita. Przydałoby się teraz poćwiczyć umiejętność operowania na repozytorium. Pierw musimy się o jakieś postarać. Tworzymy zatem dedykowany katalog, przechodzimy do niego i robimy repozytorium:

morfik:~$ mkdir repozytoria/practice-makes-perfect/

morfik:~$ cd repozytoria/practice-makes-perfect/

morfik:~/repozytoria/practice-makes-perfect$ git init
Initialized empty Git repository in /home/morfik/repozytoria/practice-makes-perfect/.git/

morfik:~/repozytoria/practice-makes-perfect$ ls -al
total 12K
drwxr-xr-x 3 morfik morfik 4.0K 2015-02-17 11:20:51 ./
drwxr-xr-x 3 morfik morfik 4.0K 2015-02-17 11:20:01 ../
drwxr-xr-x 7 morfik morfik 4.0K 2015-02-17 11:20:51 .git/

Został utworzony katalog .git , w którym to znajdować się będą wszystkie informacje na temat przeprowadzanych zmian w repozytorium. Póki co, nic tam jeszcze zbytnio nie ma:

morfik:~/repozytoria/practice-makes-perfect$ tree .git/
.git/
├── HEAD
├── branches
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
├── heads
└── tags

9 directories, 13 files

Jak widać, mamy tam kilka przykładowych skryptów (pliki .sample), i to co nas bardziej interesuje, czyli pliki HEAD, description i config .

W pliku description trzymany jest opis repozytorium. Dobrze jest edytować ten plik i dopisać krótki opis.

Plik config już po części omówiliśmy, z tym, że trzeba wspomnieć o jednej ważnej rzeczy -- ustawienia z plików konfiguracyjnych się sumują, czyli jeśli w tym pliki są jakieś ustawienia, których nie zdefiniowaliśmy w dwóch pozostałych plikach konfiguracyjnych -- zostaną one uwzględnione. Podobnie jest z hierarchią plików i ustawione tutaj opcje, które już istnieją w pozostałych plikach konfiguracyjnych, będą mieć pierwszeństwo.

By połapać się jak wygląda konfiguracja dla jakiegoś określonego repozytorium, przechodzimy do jego głównego katalogu i wydajemy poniższe polecenie:

$ git config -l
...
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true

Powyższe opcje odpowiadają dokładnie tym określonym w pliku konfiguracyjnym config repozytorium.

Ostatni plik to HEAD . Standardowo wygląda on tak:

morfik:~/repozytoria/practice-makes-perfect$ cat .git/HEAD
ref: refs/heads/master

Są to informacje na temat gałęzi, w której aktualnie się znajdujemy -- więcej o nim będzie w dalszej części artykułu.

3. Plik licencji i README

Pliki w repozytorium powinny mieć licencję. Jeśli nie zdefiniujemy licencji, wtedy wszystkie udostępniane pliki będą na domyślnej licencji ustalonej prawnie, czyli brak kopiowania/zmieniania plików. W debianie pliki z licencjami są zlokalizowane w /usr/share/common-licenses/ . Mamy tam do wyboru:

$ ls -al /usr/share/common-licenses
total 220K
drwxr-xr-x   2 root root 4.0K 2014-12-06 18:54:52 ./
drwxr-xr-x 335 root root  12K 2015-02-17 10:15:18 ../
-rw-r--r--   1 root root  12K 2004-12-19 21:30:25 Apache-2.0
-rw-r--r--   1 root root 6.0K 1996-12-16 03:58:50 Artistic
-rw-r--r--   1 root root 1.5K 1999-08-26 14:06:20 BSD
lrwxrwxrwx   1 root root    8 2014-11-30 13:37:40 GFDL -> GFDL-1.3
-rw-r--r--   1 root root  20K 2010-03-24 00:34:05 GFDL-1.2
-rw-r--r--   1 root root  23K 2008-11-03 17:47:07 GFDL-1.3
lrwxrwxrwx   1 root root    5 2014-11-30 13:37:40 GPL -> GPL-3
-rw-r--r--   1 root root  13K 2010-03-24 00:34:05 GPL-1
-rw-r--r--   1 root root  18K 2010-03-24 00:34:05 GPL-2
-rw-r--r--   1 root root  35K 2007-07-02 00:55:35 GPL-3
lrwxrwxrwx   1 root root    6 2014-11-30 13:37:40 LGPL -> LGPL-3
-rw-r--r--   1 root root  25K 2010-03-25 18:29:55 LGPL-2
-rw-r--r--   1 root root  26K 2010-03-24 00:34:05 LGPL-2.1
-rw-r--r--   1 root root 7.5K 2010-03-24 00:34:01 LGPL-3

Pliki w tym repo będą na licencji GPL-2. Kopiujemy zatem plik do głównego katalogu repozytorium:

morfik:~/repozytoria/practice-makes-perfect$ cp /usr/share/common-licenses/GPL-2 ./LICENSE
morfik:~/repozytoria/practice-makes-perfect$ ls -al
total 32K
drwxr-xr-x 3 morfik morfik 4.0K 2015-02-17 12:01:59 ./
drwxr-xr-x 3 morfik morfik 4.0K 2015-02-17 11:20:01 ../
drwxr-xr-x 7 morfik morfik 4.0K 2015-02-17 11:53:48 .git/
-rw-r--r-- 1 morfik morfik  18K 2015-02-17 12:01:59 LICENSE

Przydałoby się także stworzyć plik README.md , który to będzie wyświetlany na głównej stronie repozytorium na githubie.

morfik:~/repozytoria/practice-makes-perfect$ touch README.md
morfik:~/repozytoria/practice-makes-perfect$ echo "This is a testing repository for learning purposes" > README.md

4. Wprowadzanie zmian w repozytorium

To przygotowaliśmy wstępnie repozytorium. Musimy jeszcze uwzględnić te zmiany, tak by git je zapamiętał. Najpierw sprawdzamy co tak naprawdę git zarejestrował:

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master

Initial commit

Untracked files:
(use "git add <file>..." to include in what will be committed)

LICENSE
README.md

nothing added to commit but untracked files present (use "git add" to track)

Oba pliki zostały wykryte ale jeszcze ich stan nie podlega monitorowaniu. Zgodnie z informacją, dodajemy je:

morfik:~/repozytoria/practice-makes-perfect$ git add \*

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master

Initial commit

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file:   LICENSE
new file:   README.md

Teraz już tylko wystarczy zaaplikować zmiany:

morfik:~/repozytoria/practice-makes-perfect$ git commit -a -S -m 'Repository setup'

You need a passphrase to unlock the secret key for
user: "Mikhail Morfikov <morfik@nsa.com>"
4096-bit RSA key, ID 0x72F3A416B820057A, created 2013-11-13

[master (root-commit) 4de0d7f] Repository setup
2 files changed, 340 insertions(+)
create mode 100644 LICENSE
create mode 100644 README.md

Wyżej zostało wypisane to jakie pliki zostały zmienione wraz z usuniętymi i dodanymi linijkami. W tym przypadku tworzyliśmy pliki, zatem zostały dodane nowe linijki (+).

Jeśli nie korzystamy z kluczy GPG, nie podajemy opcji -S . Z kolei parametr -a odpowiada za uwzględnienie wszystkich plików, które zostały zmodyfikowane lub usunięte, za wyjątkiem tych nowych, których nie chcemy włączać póki co do repozytorium. Opcja -m odpowiada za treść wiadomości, która zostanie uwzględniona w logu.

Zmienił się także nieco wygląd drzewa katalogów repozytorium:

morfik:~/repozytoria/practice-makes-perfect$ tree -a
.
├── .git
│   ├── COMMIT_EDITMSG
│   ├── HEAD
│   ├── branches
│   ├── config
│   ├── description
│   ├── hooks
│   │   ├── applypatch-msg.sample
│   │   ├── commit-msg.sample
│   │   ├── post-update.sample
│   │   ├── pre-applypatch.sample
│   │   ├── pre-commit.sample
│   │   ├── pre-push.sample
│   │   ├── pre-rebase.sample
│   │   ├── prepare-commit-msg.sample
│   │   └── update.sample
│   ├── index
│   ├── info
│   │   └── exclude
│   ├── logs
│   │   ├── HEAD
│   │   └── refs
│   │       └── heads
│   │           └── master
│   ├── objects
│   │   ├── 22
│   │   │   └── fb3a8ab3ec2e800d1e22255204ec8d735d3d43
│   │   ├── 4d
│   │   │   └── e0d7f07def79578ed908669c9bc0da4383a3b7
│   │   ├── d1
│   │   │   └── 59169d1050894d3ea3b98e1c965c4058208fe1
│   │   ├── e4
│   │   │   └── 67acb33a14fd0f651500057a6364403c1ebd38
│   │   ├── info
│   │   └── pack
│   └── refs
│       ├── heads
│       │   └── master
│       └── tags
├── LICENSE
└── README.md

17 directories, 24 files

Zostało dodanych kilka obiektów, oraz został utworzony log.

5. Log

Sam log można przeglądać przy pomocy poniższego polecenia:

morfik:~/repozytoria/practice-makes-perfect$ git log
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Log jest póki co krótki, bo zrobiliśmy tylko jeden commit. W przypadku gdy wprowadzamy wiele zmian, powyższe polecenie może nam zwrócić sporo informacji.

Oczywiście opcji, które można sprecyzować, by odpowiednio formatować logi, jest całe mnóstwo. Te częściej używane są z kolei przystępnie opisane tutaj.

Generalnie rzecz biorąc, na początek przydadzą nam się poniższe parametry.

Uwzględnianie zmian w commitach:

morfik:~/repozytoria/practice-makes-perfect$ git log -p
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
...

Zwracanie kilka ostatnich pozycji, w tym też i ostatniego commita:

morfik:~/repozytoria/practice-makes-perfect$ git log -1
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Zwracanie wybranego commita:

morfik:~/repozytoria/practice-makes-perfect$ git log 4de0d7f07def79578ed908669c9bc0da4383a3b7
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Powyżej został zwrócony commit w oparciu o hash. Przydatne jeśli chcemy wyświetlić informacje, a dysponujemy jedynie tym ciągiem.

Zwracanie commitów z określonego przedziału czasowego:

morfik:~/repozytoria/practice-makes-perfect$ git log --since=2.weeks --until=now
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Można także używać dat w postaci: "2015-02-16 12:00:02", przykładowo:

morfik:~/repozytoria/practice-makes-perfect$ git log --since=yesterday --until="2015-02-17 12:22:00"
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Przy czym nie są obowiązkowe obie pozycje --since i --until , możemy równie dobrze podać tylko jeden z nich.

Wyświetlanie commitów określonego autora:

morfik:~/repozytoria/practice-makes-perfect$ git log --author=morfik
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Co ciekawe, wpisanie morfik zwraca commita, mimo, że nie ma tam takiego autora. W manie można wyczytać, że to jest wzór a nie autor, czyli wszystkie commity zawierające w polu Author morfik, zostaną zwrócone.

Statystyki commitów:

morfik:~/repozytoria/practice-makes-perfect$ git log --stat
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

LICENSE   | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
README.md |   1 +
2 files changed, 340 insertions(+)

Dostosowywanie wyglądu loga:

morfik:~/repozytoria/practice-makes-perfect$ git log --pretty=format:"%H - %an, %ad : %s"
4de0d7f07def79578ed908669c9bc0da4383a3b7 - Mikhail Morfikov, 2015-02-17 12:21:27 +0100 : Repository setup

Powyższe opcje możemy łączyć, dostając bardziej przefiltrowany log.

6. Zdalna kopia repozytorium

Pora przesłać kopię repozytorium na githuba. Pierw jednak musimy odwiedzić stronę githuba i utworzyć tam repozytorium -- nie da rady inaczej stworzyć repo na githubie, zwłaszcza jeśli ma się włączone uwierzytelnianie dwuskładnikowe.

Jako, że wcześniej stworzyliśmy plik README.md i LICENCE, nie musimy ich tutaj definiować -- zostaną przesłane przy synchronizacji.

Po pomyślnym utworzeniu repozytorium, powinno zostać wyświetlone podsumowanie:

Jak widać, mamy tam między innymi linki potrzebne do synchronizacji -- zarówno po https jak i ssh. Dodatkowo jest też instrukcja jak stworzyć lokalne repo (to już zrobiliśmy) i jak dokonać synchronizacji.

Wracamy zatem do terminala i dodajemy zdalne ścieżki:

morfik:~/repozytoria/practice-makes-perfect$ git remote add origin git@github.com:morfikov/practice-makes-perfect.git

morfik:~/repozytoria/practice-makes-perfect$ git remote -v
origin  git@github.com:morfikov/practice-makes-perfect.git (fetch)
origin  git@github.com:morfikov/practice-makes-perfect.git (push)

Jeśli w tej chwili spróbowalibyśmy dać push , dostaniemy poniższy komunikat:

morfik:~/repozytoria/practice-makes-perfect$ git push
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use

git push --set-upstream origin master

Zgodnie z instrukcją wydajemy poniższe polecenie:

morfik:~/repozytoria/practice-makes-perfect$ git push --set-upstream origin master
Counting objects: 4, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 7.57 KiB | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To git@github.com:morfikov/practice-makes-perfect.git
* [new branch]      master -> master
Branch master set up to track remote branch master from origin.

W tej chwili jeśli odwiedzimy repozytorium na githubie powinniśmy ujrzeć już pliki:

7. Cofanie zmian

Musimy także wiedzieć jak cofać/odrzucać zmiany wprowadzane do repozytorium, a te z kolei dzielimy z grubsza na dwie kategorie. Pierwsza z nich to zmiany przed dokonaniem commita, drugi zaś po dokonaniu.

Stwórzmy zatem nowy plik w repozytorium:

morfik:~/repozytoria/practice-makes-perfect$ touch test_file

morfik:~/repozytoria/practice-makes-perfect$ echo "some random text" > test_file

morfik:~/repozytoria/practice-makes-perfect$ git add .

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file:   test_file

Nie możemy teraz sobie od tak tego pliku usunąć, tj. przy pomocy rm lub wciskając delete na klawiaturze -- to jest traktowane jako kolejna zmiana:

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file:   test_file

Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

deleted:    test_file

Zgodnie z wypisanymi instrukcjami, by cofnąć zmiany, wpisujemy:

morfik:~/repozytoria/practice-makes-perfect$ git reset --soft HEAD test_file

Jeśli teraz sprawdzimy status gita, zobaczymy, że plik aktualnie nie jest śledzony:

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)

test_file

nothing added to commit but untracked files present (use "git add" to track)

Teraz już można spokojnie usunąć plik z gita:

morfik:~/repozytoria/practice-makes-perfect$ rm test_file

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

A jak sprawa wygląda w przypadku gdy dokonamy commita? Stwórzmy zatem jeszcze jeden plik testowy:

morfik:~/repozytoria/practice-makes-perfect$ touch test_file

morfik:~/repozytoria/practice-makes-perfect$ echo "some random text" > test_file

morfik:~/repozytoria/practice-makes-perfect$ git add .

morfik:~/repozytoria/practice-makes-perfect$ git commit -S -a -m "mistake"

You need a passphrase to unlock the secret key for
user: "Mikhail Morfikov <morfik@nsa.com>"
4096-bit RSA key, ID 0x72F3A416B820057A, created 2013-11-13

[master e175de8] mistake
1 file changed, 1 insertion(+)
create mode 100644 test_file

Możemy pójść nawet o krok dalej i przesłać zmiany na githuba:

morfik:~/repozytoria/practice-makes-perfect$ git push
Counting objects: 3, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 964 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:morfikov/practice-makes-perfect.git
4de0d7f..e175de8  master -> master

Sprawdzając log możemy dostrzec nowy commit:

morfik:~/repozytoria/practice-makes-perfect$ git log
commit e175de8b05df16c5f2b7d2c2c23a932a1c9efeaa (HEAD, refs/remotes/origin/master, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 16:59:49 +0100

mistake

commit 4de0d7f07def79578ed908669c9bc0da4383a3b7
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Spróbujmy się tego jakoś pozbyć. Najpierw musimy ustalić gdzie jest HEAD. Zwykle będzie na ostatnim commicie ale zakładając, że nie wiemy gdzie dokładnie jest HEAD -- sprawdzamy:

morfik:~/repozytoria/practice-makes-perfect$ cat .git/HEAD
ref: refs/heads/master

morfik:~/repozytoria/practice-makes-perfect$ cat .git/refs/heads/master
e175de8b05df16c5f2b7d2c2c23a932a1c9efeaa

morfik:~/repozytoria/practice-makes-perfect$ git show e175de8b05df16c5f2b7d2c2c23a932a1c9efeaa
commit e175de8b05df16c5f2b7d2c2c23a932a1c9efeaa (HEAD, refs/remotes/origin/master, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 16:59:49 +0100

mistake

diff --git a/test_file b/test_file
new file mode 100644
index 0000000..4ab08b3
--- /dev/null
+++ b/test_file
@@ -0,0 +1 @@
+some random text

Czyli tak jak podejrzewaliśmy, jest na ostatnim. Jeśli chcemy cofnąć zmiany tylko z tego ostatniego commita, to musimy zmienić położenie HEAD na przedostatni commit:

morfik:~/repozytoria/practice-makes-perfect$ git log
commit e175de8b05df16c5f2b7d2c2c23a932a1c9efeaa (HEAD, refs/remotes/origin/master, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 16:59:49 +0100

mistake

commit 4de0d7f07def79578ed908669c9bc0da4383a3b7
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

morfik:~/repozytoria/practice-makes-perfect$ git reset --soft HEAD~1

morfik:~/repozytoria/practice-makes-perfect$ git log
commit 4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master)
Author: Mikhail Morfikov <morfik@nsa.com>
Date:   2015-02-17 12:21:27 +0100

Repository setup

Jeśli zmian by było więcej i nie chciałoby nam się liczyć ile commitów trzeba wrócić aby doprowadzić repozytorium do stanu używalności, możemy skorzystać z hashów i zresetować repo w oparciu o nie, przykładowo:

morfik:~/repozytoria/practice-makes-perfect$ git log --format=oneline
e175de8b05df16c5f2b7d2c2c23a932a1c9efeaa (HEAD, refs/remotes/origin/master, refs/heads/master) mistake
4de0d7f07def79578ed908669c9bc0da4383a3b7 Repository setup

morfik:~/repozytoria/practice-makes-perfect$ git reset --soft 4de0d7f07def79578ed908669c9bc0da4383a3b7

morfik:~/repozytoria/practice-makes-perfect$  git log --format=oneline
4de0d7f07def79578ed908669c9bc0da4383a3b7 (HEAD, refs/heads/master) Repository setup

Zatem lokalnie cofnęliśmy zmiany ostatniego commita:

morfik:~/repozytoria/practice-makes-perfect$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file:   test_file

Jak widzimy wyżej, git uważa, że nasze lokalne repozytorium jest ździebko nieaktualne i według niego musimy zrobić git pull by dokonać aktualizacji. Problem w tym, że właśnie chcemy czegoś odwrotnego, czyli cofnąć zmiany wprowadzone z zdalnym repo githuba.

Co się stanie jeśli damy push w tym momencie? Sprawdźmy:

morfik:~/repozytoria/practice-makes-perfect$ git push
To git@github.com:morfikov/practice-makes-perfect.git
! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:morfikov/practice-makes-perfect.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Można się było tego spodziewać -- git odrzucił proponowane zmiany, bo nasze repo jest "nieaktualne". Musimy skorzystać z opcji -f (force):

morfik:~/repozytoria/practice-makes-perfect$ git push -f
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:morfikov/practice-makes-perfect.git
+ e175de8...4de0d7f master -> master (forced update)

8. Przywrócenie stanu

Jest tylko jeden problem -- to powyższe działanie wymazuje kompletnie ślad na temat usunięcia zmian, co nie zawsze jest pożądane. Musimy jeszcze wymyśleć jak zapisać w logu taką informację.

Tworzymy zatem kolejny plik testowy, robimy commita i przesyłamy zmiany do zdalnego gita:

morfik:~/repozytoria/practice-makes-perfect$ touch test_file

morfik:~/repozytoria/practice-makes-perfect$ echo "some random text" > test_file

morfik:~/repozytoria/practice-makes-perfect$ git add .

morfik:~/repozytoria/practice-makes-perfect$ git commit -S -a -m "mistake"
You need a passphrase to unlock the secret key for
user: "Mikhail Morfikov <morfik@nsa.com>"
4096-bit RSA key, ID 0x72F3A416B820057A, created 2013-11-13

[master 49f8b65] mistake
1 file changed, 1 insertion(+)
create mode 100644 test_file

morfik:~/repozytoria/practice-makes-perfect$ git push
Counting objects: 3, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 964 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:morfikov/practice-makes-perfect.git
4de0d7f..49f8b65  master -> master

morfik:~/repozytoria/practice-makes-perfect$ git log --format=oneline
49f8b65ed0d2ae0c7f17fd0003af832f61c7885a (HEAD, refs/remotes/origin/master, refs/heads/master) mistake
4de0d7f07def79578ed908669c9bc0da4383a3b7 Repository setup

By cofnąć zmiany i zachować informacje o tej o tej czynności, wydajemy poniższe polecenie:

morfik:~/repozytoria/practice-makes-perfect$ git revert 49f8b65ed0d2ae0c7f17fd0003af832f61c7885a
[master 172d0fd] Revert "mistake"
1 file changed, 1 deletion(-)
delete mode 100644 test_file

Sprawdźmy zatem log:

morfik:~/repozytoria/practice-makes-perfect$ git log --format=oneline
172d0fd7634ff0dd1140ca50f0f37a9211223251 (HEAD, refs/heads/master) Revert "mistake"
49f8b65ed0d2ae0c7f17fd0003af832f61c7885a (refs/remotes/origin/master) mistake
4de0d7f07def79578ed908669c9bc0da4383a3b7 Repository setup

Został utworzony nowy commit, który cofnął zmiany tego wskazanego przy pomocy hasha.

Teraz tylko wystarczy zsynchronizować zdalne repo:

morfik:~/repozytoria/practice-makes-perfect$ git push
Counting objects: 2, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 246 bytes | 0 bytes/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To git@github.com:morfikov/practice-makes-perfect.git
49f8b65..172d0fd  master -> master

Sprawdźmy co zarejestrował github:

9. Odpowiednie narzędzia

W pliku konfiguracyjnym zdefiniowaliśmy trzy opcje, które pomogą nam w pracach z gitem -- mowa o zewnętrznych narzędziach: merge.tool meld , diff.tool meld oraz core.editor vim . To z jakich narzędzi możemy korzystać w przypadku merge.tool i diff.tool można ustalić przez:

morfik:~/repozytoria/practice-makes-perfect$ git difftool --tool-help
'git difftool --tool=<tool>' may be set to one of the following:
    araxis
    meld
    vimdiff
    vimdiff2
    vimdiff3

The following tools are valid, but not currently available:
    bc3
    codecompare
    deltawalker
    diffmerge
    diffuse
    ecmerge
    emerge
    gvimdiff
    gvimdiff2
    gvimdiff3
    kdiff3
    kompare
    opendiff
    p4merge
    tkdiff
    xxdiff

Some of the tools listed above only work in a windowed
environment. If run in a terminal-only session, they will fail.

Jeśli chodzi o edytor tekstu, czy w ogóle o tekstowe narzędzia, to w ich przypadku, będziemy mieli do dyspozycji kolorowanie wyjścia.

Tak wygląda vim podczas robienia commita (git commit):

A tu przykład diffa (git diff):

Choć ja tam wolę graficznego melda i niezbyt przepadam za tekstowymi diffami (git difftool):

Jest jeszcze git merge i git mergetool, odpowiednio tekstowy i GUI ale póki co jeszcze nie było dane mi z nich skorzystać, także nic na temat tej funkcjonalności nie napiszę, może kiedyś.

No i to w sumie by było na tyle -- pozostaje nam tworzyć nowe pliki, aktualizować/usuwać stare, robić commity i pushować wszystko na githuba. Więcej informacji zawsze można znaleźć w helpie githuba , lokalnie w manach lub też na https://www.kernel.org/pub/software/scm/git/docs/ .

OSnews Wykop Blip Flaker Kciuk Śledzik Facebook Identi.ca Twitter del.icio.us Google Bookmarks