Git – это распределенная система контроля версий - каждый разработчик создает на своем компьютере отдельный, полноценный репозиторий. Git поддерживают несколько крупных репозиториев – GitHub, SourceForge, BitBucket - удобно использовать один из них в качестве основного хранилища для корпоративных проектов.
После создания удалённого репозитория для проекта на одном из вышеперечисленных сервисов (или ином соответствующим образом настроенном сервере), каждый разработчик должен получить свою локальную копию содержимого удалённого репозитория. Для этого:
1. Создаем на локальной машине новую директорию для проекта project_name, переходим в нее.
2. Выполняем следующую команду:

$ git clone git@github.com:devlabuser/sharp.git ./

“./” в конце команды означает, что создать репозиторий нужно в текущей директории. Если не указать эти символы, внутри текущей папки будет создана директория c именем репозитория, в которой и будет находиться содержимое склонированного проекта.

Создание коммитов

После того, как внесены нужные изменения в файлы или код, список всех измененных и добавленных файлов можно просмотреть командой:

$ git status

Чтобы подготовить файлы к фиксации в коммите (говоря на языке Git - проиндексировать изменения), добавляем в него файлы командой:
$ git add -A

Чтобы проиндексировать изменения одного конкретного файла, нужно указать его имя:
$ git add EDITEDFILE

Выполняем коммит:
$ git commit -m "Комментарий к коммиту"

Заливаем изменения, зафиксированные последним коммитом, на сервер:
$ git push

Если над проектом работает кто-то помимо Вас, и он успел загрузить на сервер свои коммиты до Вас, то перед тем, как использовать команду git push, Вам необходимо использовать команду git pull, чтобы привести состояние вашего локального репозитория в соответствие с удалённым (иначе команда git push при выполнении выдаст ошибку).
В ходе этого процесса слияния Ваших локальных и удалённых данных могут возникнуть противоречия между ними (например, если Вы и Ваш коллега изменили одну и ту же часть одного и того же файла по-разному) - система git сама уведомит Вас, в каких файлах произошли неразрешимые для самой системы конфликты, и Вы сможете их надлежащим образом отредактировать.
Но что если конфликтный файл не подлежит редактированию - например, если он бинарный? Чтобы решить такой конфликт, надо просто выбрать – какая версия файла будет использоваться - ваша или из вливаемых данных. Чтобы использовать свой вариант файла, вводим команды:
$ git checkout --ours binary.dat
$ git add binary.dat

Если мы выбираем версию из вливаемой ветки:
$ git checkout --theirs binary.dat
$ git add binary.dat

“ours” – от английского “наш”, “theirs” – от английского “их”.
Вышесказанное приводит нас к выводу - коммиты следует делать по возможности чаще, регулярно сверяясь с тем, как протекает работа у коллег - если коммиты будут содержать минимум изменений, локализовывать и устранять ошибки и конфликты придётся в меньшем количестве кода и файлов.

Просмотр изменений

git log - просмотр логов. Выводимый результат:

$ git log
commit 9a452d9cdbdb57e7e4f2b09f8ce2f776cd56657a
Author: DevLab User user@mail.ru
Date:   Wed Jul 31 18:35:47 2013 +0400
 	first commit
 commit d528335724dfc15461996ed9d44d74f23ce6a075
Author: DevLab User user@mail.ru
Date:   Wed Jul 31 06:24:57 2013 -0700
 	Initial commit

Вывод данных о каждом коммите в одну строку:
$ git log --pretty=oneline
9a452d9cdbdb57e7e4f2b09f8ce2f776cd56657a first commit
d528335724dfc15461996ed9d44d74f23ce6a075 Initial commit

Поиск по ключевому слову в комментариях к коммиту:
$ git log | grep -e "first"
    first commit

Вывести сгруппированные по авторам коммиты:
$ git shortlog
Desperado (5):
	aaaaa
	safddav
	safdgdf
	index
	sas

Статистика изменения файлов, вроде числа измененных файлов, внесенных в них строк, удаленных файлов вызывается ключом --stat:
$ git log --stat
commit b1c953d9e214980a487e4a0fb5389d45fef0d90e
Author: Mihail Dzantiev
Date:   Thu May 5 13:51:00 2016 +0300
	keywords
 app/controllers/ControllerBase.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
commit 4a215611a8bb9d4dcb46f4edb0d2c0bbea4c41e3
Author: Mihail Dzantiev
Date:   Thu May 5 11:28:11 2016 +0300
	local changes
 app/controllers/ControllerBase.php |   2 +-
 app/views/about/index.volt         |  12 +---
 app/views/inc/about.volt           |   2 +-
 app/views/inc/about_index.volt     |   2 +-
 app/views/index/index.volt         |   2 +-
 element                            |   2 +-
 public/html/css/about.css          |   2 +-
 public/html/css/main-block.css     |   2 +-
 public/html/css/style.css          |   2 +-
 public/html/scss/about.scss        |   2 +-
 public/html/scss/main-block.scss   |   2 +-
 public/sitemap.xml                 | 252 +++++++++++++++++++++++++++++++++++++++++++----------------------------------
 18 files changed, 154 insertions(+), 130 deletions(-)
commit 741cd866d76b081bd43441fd84a7c5209c0bf5e8
Author: root
Date:   Thu May 5 11:04:12 2016 +0300
	server side
 app/views/blog/post.volt                         |   2 ++
 element                                          |   2 +-

Команда git show позволяет просмотреть, какие именно изменения произошли в указанном коммите:
$ git show 99452d955bdb57e7e4f2b09f8ce2fbb6cd56377a
commit 99452d955bdb57e7e4f2b09f8ce2fbb6cd56377a
Author: DevLab User
Date:   Wed Jul 31 18:35:47 2013 +0400
    first commit
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..4add785
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1 @@
+Text
\ No newline at end of file

Чтобы увидеть, что же вы изменили, но пока не проиндексировали, наберите git diff (эта команда сравнивает содержимое вашего рабочего каталога с содержимым индекса):
$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
 Please include a nice description of your changes when you submit your PR;
 if we have to read the whole diff to figure out why you're contributing
 in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if you patch is
+longer than a dozen lines.

Если вы хотите посмотреть, что вы проиндексировали и что войдёт в следующий коммит, вы можете выполнить git diff --staged (эта команда сравнивает ваши индексированные изменения с последним коммитом):
$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project

Также можно просмотреть разницу между любыми двумя коммитами (git diff commit1 commit2):
$ git diff b1c953d 4a21561
diff --git a/app/controllers/ControllerBase.php b/app/controllers/ControllerBase.php
index 84eacea..117c1dc 100755
--- a/app/controllers/ControllerBase.php
+++ b/app/controllers/ControllerBase.php
@@ -54,7 +54,7 @@ class ControllerBase extends Controller
        // SEO
        $this->view->setVar('title','Hot Pie – Осетинские пироги');
        $this->view->setVar('description',
        'Заказ Осетинских пирогов с доставкой по Москве');
-               $this->view->setVar('keywords',
	'осетинские пироги Москва');
+       $this->view->setVar('keywords',
	'осетинские пироги доставка');
        // Статичные страницы
        // всегда контент

Показать, что за файлы менялись в коммите:
$ git diff-tree --no-commit-id --name-only -r commit1
.DS_Store
app/views/about/index.volt
element
public/html/css/about.css
public/html/css/style.css

Ветки

Любой проект предусматривает множество контекстов, в которых ведётся работа; каждый компонент, исправление ошибки, эксперимент или альтернатива вашему продукту — это своего рода контекст сам по себе. Его можно рассматривать как отдельный «тематический раздел», чётко отграниченный от других таких разделов. Если такие тематические разделы не отделять друг от друга с помощью веток, неизбежно повышается риск появления проблем. Перемешивание тематических разделов в одном контексте:
1. Усложняет контроль над ними — а при большом количестве тематических разделов он становится почти невозможным;
2. Усложняет откат какого-либо действия, которое привело к ошибке, так как оно уже смешалось со слишком большим количеством других вещей;
3. Не вдохновляет людей на эксперименты, так как после смешивания экспериментального кода с основным кодом проекта становится очень трудно извлечь его из репозитория.
Чтобы создать новую ветку на основании текущего состояния, нужно выбрать имя и выполнить одну команду в командной строке. Допустим, мы хотим начать работу над новой версией контактной формы, следовательно создадим новую ветку под названием «contact-form»:

$ git branch contact-form

Если выполнить команду git branch не указывая имя ветки, мы получим список всех веток, которые есть на локальном репозитории (а ключ -v предоставит немного дополнительной информации):
$ git branch -v
  contact-form b1c953d keywords
* master       b1c953d [ahead 1, behind 1] keywords

Маленькая звёздочка напротив ветки под названием «master» обозначает ветку, которая на данный момент является активной. Итак, перед тем, как начать работу над нашей контактной формой, нужно сделать её активным контекстом:
$ git checkout contact-form

Теперь эта ветка - наш текущий рабочий контекст (на языке Git это называется «HEAD branch»). Все изменения и любой коммит, совершённые с текущего момента, будут влиять только на этот контекст — остальные контексты останутся нетронутыми. Чтобы переключить контекст на другую ветку, мы просто снова используем команду git checkout.
Чтобы добавить изменения из одной ветки в другую, можно выполнить их «слияние» в текущий рабочий контекст. Например, если мы некоторое время работали над нашим компонентом «contact-form», а теперь желаем добавить эти изменения в ветку «master», то нам нужно переключиться на эту ветку и выполнить git merge:
$ git checkout master
$ git merge contact-form

Полезная возможность для выяснения состояния веток состоит в том, чтобы составить список только тех веток, которые Вы слили (или не слили) в текущую ветку. Для этих целей в Git'е есть опции --merged и --no-merged. Чтобы посмотреть те ветки, которые Вы уже слили с текущей, выполните команду git branch --merged:
$ git branch --merged
  iss53
* master

Вывод всех локальных и удалённых веток:
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

Получить имя текущей ветки:
$ git rev-parse --abbrev-ref HEAD
master

Переименовать текущую ветку:
git branch -m new-branch-name

Одну конкретную ветку можно склонировать из удалённого репозитория:
git clone -b branch-name --single-branch https://github.com/user/repo.git

"Черновики" изменений

Часто возникает такая ситуация, что пока Вы работаете над частью своего проекта, всё находится в беспорядочном состоянии, а Вам нужно переключить ветки, чтобы немного поработать над чем-то другим. Проблема в том, что Вы не хотите делать коммит с наполовину доделанной работой только для того, чтобы позже можно было вернуться в это же состояние. Ответ на эту проблему — команда git stash.
Эта команда поглощает грязное состояние рабочего каталога, то есть изменённые отслеживаемые файлы и изменения в индексе, и сохраняет их в стек незавершённых изменений, которые потом в любое время можно снова применить:

$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")

Тепеь Ваш рабочий каталог чист:
$ git status
On branch master
nothing to commit, working directory clean

В данный момент Вы легко можете переключить ветки и поработать где-то ещё - ваши изменения сохранены в стеке. Чтобы посмотреть, что у Вас есть припрятанного, выполните git stash list:
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051... Revert "added file_size"
stash@{2}: WIP on master: 21d80a5... added number to log

В нашем случае две "заначки" были сделаны ранее, так что у вас теперь три разных припрятанных работы. Вы можете снова применить ту, которую только что спрятали, с помощью команды, показанной в справке в выводе первоначальной команды stash: git stash apply. Если вы хотите применить одну из старых заначек, можете сделать это, указав её имя так: git stash apply stash@{2}. Если не указывать конкретный стек, Git будет подразумевать, что вы хотите применить последнюю спрятанную работу:
$ git stash apply
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
	modified:   index.html
    modified:   lib/simplegit.rb

Как видите, Git восстановил изменения в файлах, которые былиы отменены командой stash. В нашем случае у Вас был чистый рабочий каталог, когда вы восстанавливали спрятанные изменения, и к тому же вы делали это на той же ветке, на которой находились во время прятанья. Но наличие чистого рабочего каталога и применение на той же ветке не обязательны для git stash apply. Вы можете спрятать изменения на одной ветке, переключиться позже на другую ветку и попытаться восстановить изменения. У вас в рабочем каталоге также могут быть изменённые и недокоммиченные файлы во время применения спрятанного — Git выдаст вам конфликты слияния, если что-то уже не может быть применено чисто.
Изменения в файлах были восстановлены, но проиндексированные файлы — нет. Чтобы воостановить и их, выполнить команду git stash apply с опцией --index.
Всё, что делает опция apply — это пытается применить спрятанную работу — то, что вы спрятали, всё ещё будет находиться в стеке. Чтобы удалить спрятанное, выполните git stash drop с именем "заначки", которую нужно удалить:
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051... Revert "added file_size"
stash@{2}: WIP on master: 21d80a5... added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Также можно выполнить git stash pop, чтобы применить спрятанные изменения и сразу же удалить их из стека, а просто для удаления всех "черновиков" - git stash clear.
При некоторых сценариях использования, может понадобиться применить спрятанные изменения, поработать, а потом отменить изменения, внесённые командой stash apply - это делается так:
$ git stash show -p stash@{0} | git apply -R

Опять же, если не указывать параметр для stash, Git подразумевает то, что было спрятано последним:
$ git stash show -p | git apply -R

Исправление изменений

Очень легко забыть указать какое-либо изменение или допустить опечатку в комментарии к коммиту. С помощью ключа —amend к команде git commit Git даёт вам возможность изменить самый последний коммит - например, если вы забыли добавить какое-либо изменение и допустили опечатку в названии коммита, это можно легко исправить:

$ git add some-changed-files
$ git commit --amend -m "Комментарий, в этот раз без опечаток"

Помните: коммит, который уже был отослан в удалённый репозиторий, изменять нельзя!
Изменения, для которых не был совершён коммит, называются «локальными». Сброс этих изменений может потребоваться, когда текущий результат вашей работы оказался… скажем, хуже чем то, что было раньше. В Git можно отменить локальные изменения и начать сначала с последней версии вашего проекта, для которой был совершён коммит.
Если вы хотите восстановить один файл, можете использовать команду git checkout (команда нам уже знакома, но в текущем случае она выполняет иные функции):
$ git checkout -- restore-file

Если день совсем не задался, и требуется сбросить все локальные изменения и восстановить весь проект целиком, то делаем так (это заменит все файлы в вашей рабочей директории на последнюю редакцию, для которой был совершён коммит):
$ git reset --hard HEAD

Будьте осторожны с этими операциями: поскольку локальные изменения не были зарегистрированы в репозитории, после сброса восстановить их будет невозможно!
Отменить можно не только локальные изменения, но и некоторые коммиты, когда это необходимо — например, если они привели к ошибке в коде.
Команда git reset позволяет буквально вернуться в прошлое - Вы сообщаете к какой версии вы хотите вернуться, и она восстанавливает именно это состояние — отменяя все изменения, которые были осуществлены после этой временной точки. Нужно указать ей коммит, к которому вы хотите вернуться:
$ git reset --hard 2be18d9

Чтобы восстановить просто самый свежий коммит:
$ git reset --hard HEAD

Опция -—hard — это наиболее простой и чистый способ, однако она также удаляет все локальные изменения в вашей рабочей директории. Так что перед её применением, убедитесь, что у Вас нет локальных изменений, которые лучше было бы сохранить.
Команда git revert используется в другой ситуации. Представьте, что у Вас есть коммит, от которого Вы бы хотели избавиться, однако коммиты, совершённые после него, все ещё актуальны. В таком случае нельзя использовать команду git reset, потому что она отменит и эти, более поздние коммиты.
Команда revert позволяет откатить последствия от одного конкретного коммита. Она не удаляет какие-либо коммиты, в отличие от git reset - вместо этого она вносит изменения, противоположные изменениям в коммите, который подлежит откату. Например, если вы удалили строку кода, revert добавит точно такую же строку.
Чтобы откатить коммит 2be18d9:
$ git revert 2be18d9

Чтобы откатить самый свежий коммит:
	$ git revert HEAD
	

Поиск ошибок

Часто возникает ситуация вида - Вы уверены, что два дня назад всё работало, а теперь - не работает - например, мы знаем, что текущая версия (с меткой «2.0») приведена в негодность, также мы знаем, что несколько коммитов назад (в версии «1.9»), всё было нормально.
Этой информации уже достаточно, чтобы начать охоту на ошибки с помощью git bisect:

$ git bisect start
$ git bisect bad
$ git bisect good v1.9

После начала процесса мы сообщили Git, что наш текущий коммит содержит ошибку и обозначили его как «плохой» («bad»). Затем мы сообщили Git какой из предыдущих коммитов точно был рабочим (в качестве параметра для команды git bisect good).
Git после этого восстанавливает проект посередине между заведомо хорошим и плохим состоянием:

Теперь мы проводим тестирование этой версии, чтобы определить является ли это состояние рабочим или же уже содержит ошибку. Когда мы это выясним, снова сообщаем Git о результате с помощью git bisect bad или git bisect good.
Предположим, что мы определили этот коммит тоже как «плохой» - значит, ошибка была допущена раньше — и Git опять сократит количество коммитов под вопросом:

Таким образом Вы очень быстро определите, где именно была допущена ошибка. Когда это произойдёт, выполните git bisect reset, чтобы закончить охоту за ошибкой и восстановить исходное состояние проекта.

Share with friends:

Facebook Twitter Vkontakte