Git/ru

From JazzTeamWiki
Jump to: navigation, search

Установка

Для установки git нужно выполнить команду:

  * sudo apt-get install git-core            ; установка пакета git-core

Полезные ссылки

Статьи

Видео (Tech Talk: Linus Torvalds on git (rus))

Хорошая книжка про гит [1]

Принцип работы GIT в сравнении с SVN

Работа с репозиторием

  • SVN Полный репозиторий со всей историей находится на сервере. У каждого разработчика только последние изменения. В каждой папке и подпапках проекта есть папка .svn
  • GIT У каждого разработчика лежит копия всего репозитория со всей историей комитов. В папке проекта есть подпапка .git, которая хранит в себе всю историю изменений с самого начала проекта. На сервере лежит такой же репозиторий со всей историей. Принцип работы - синхронизация между различными репозиториями (git-распределённая система).

Комиты

  • SVN При комите изменения сразу попадают на сервер. Комиты нельзя делать локально (исключение - если используется локальный svn)
  • GIT Изменения комитаются в локальный репозиторий (поэтому комиты можно делать локально).

Отправка изменений на сервер

  • SVN Изменения отправляются при каждом комите
  • GIT Изменения (некоторое количество локальных комитов) отправляются только после специальной команды push

Ветки

  • SVN При создании ветки выполняется копия всех файлов. Если мержить ветки, нужно мержить весь проект.
  • GIT Находится общая точка для двух веток (последний комит). Мержинг выполняется относительно этой общей точки (в итоге мержится не всё, а только комиты двух веток после общей точки). За день разработчик может создать несколько бренчей и под конец работы слить их всех в одну. Основная бренч называется master.

Quickstart

1. Коифигурация enviroment-а: для windows [2] для linux [3]

2. Устанавливаем плагин для Eclipse (egit)

Костя: пользовался плагином только для просмотре истории, клонирования удалённых репозиториев, иногда через него комитал. В остальном - консоль лучше и понятнее (имхо).

Основные команды работы с GIT

  • Клонирование репозитория (скачиваем репозиторий с удалённого сервера)
 * git clone http://myrepo.com

Перед комитом изменения нужно проиндексировать: 1. Делаем изменения 2. Индексируем изменения для включения в коммит (можно проиндексировать не все, а только часть и закомитать соответственно только эту часть) 3. Выполняем комит

  • Настройка имени пользователя и почтового адреса (между прочим, это хороший тон):
 * git config user.name "FirstName LastName"  
 * git config user.email "user@example.com"
  • Просмотр статуса репозитория
 * git status (отображает непроиндексированные изменения и бренч, на которой мы сейчас находимся)
  • Добавление изменений
 * git add . (добавить все существующие, но изменйнные файлы)
 * git add -u (добавить все новый файлы)
 * git add org/jazzteam/myproject/model* (добавить все файлы, начинающиеся с org/jazzteam/myproject/model)
 * git add org/jazzteam/myproject/model/User.java (Добавить один файл)
 * git add -i (включает интерактивный редактор, где можно цифрами указать, какие файлы добавлять, какие - нет. Удобная штука, рекомендую.)
  • Комит (в локальный репозиторий)
 * git commit (откроется редактор, в котором нужно будет набрать комментарий к коммиту)
 * git commit -m "комментарий к коммиту" (указать комментарий сразу)
 * git commit --amend (Не создавать новый коммит, а добавить изменения к предыдущему коммиту. Обычно если мы забыли закомитать пару файлов, то не нужно создавать новый коммит, нужно докомитать в прежний. Актуально если в проекте настроена система code review.)
 * git commit -a (= git add . + git commit)
  • Отправка изменений на сервер
 * git push
  • Получение изменений с сервера
 * git pull (использует merge для объединения ваших изменений и изменений с сервера)
 * git pull --rebase (использует rebase  для объединения ваших изменений и изменений с сервера)

Работа с ветками

Главная ветка называется master. В неё сливаются изменения с сервера и с неё заливаются на сервер.

В гите ветки легковесные, не нужно бояться создавать их для каких-то локальных изменений, потом сливать в master. Например вы делаете таск, создаём для него бренч и в ней работаем. Потом пришёл срочный багфикс, переключаемся в master и фиксаем его например там (можно конечно для него создать ветку). Пофиксали, переключаемся в ветку с таском, доделываем его, сливаем с master.

  • Клонирование ветки
 * git clone -b <branch> <remote_repo>
  • Создание ветки
 * git branch my_branch
  • Переключение на другую ветку
 * git checkout my_branch
  • Создание ветки и переключение на неё
 * git checkout -b my_branch
  • Просмотр веток
 * git branch
  • Просмотр веток с последним коммитом
 * git branch -v
  • Чтобы оставить в списке только те ветки, которые вы слили (или не слили) в ветку, на которой сейчас находитесь в Git'е есть опции --merged и --no-merged
 * git branch --merged ( покажет те ветки которые вы слили в ветку ) 
 * git branch --no-merged ( покажет ветки которые вы ещё не слили в ветку )
  • Удаление ветки
 * git branch -D my_branch
  • Слияние веток
 * git merge my_branch (выполняет мерж ветки my_branch в master)
 * git rebase my_branch (выполняет rebase ветки my_branch в master)

Так же можно работать и с удалёнными ветками (которые находятся на сервере), просматривать, переключаться, и т.д.

  • Различия между merge и rebase

Merge Не трогает sha1-хеши коммитов. Дупликаты коммитов не будут отправлены на сервер. Если нам нужно слить одну ветвь в другую, создаётся дополнительный merge-коммит.

Rebase Меняет хеши коммитов (Создаёт новые коммиты с новыми хешами. Старые ещё остаются!). Нужно быть осторожным с этой командой т.к. дубликаты коммитов могут залиться на сервер. Если делаем rebase для веток А и В: (A с коммитами A1, A2, A3 и B с коммитами B1, B2, B3), rebase сначала берёт коммиты с A, потом применяет коммиты с B (пользователь разрешает конфликты мануально). История коммитов будет выглядеть так: A1, A2, A3, B1', B2', B3'.

Сервер может обладать дополнительными фишками для предотвращения дубликатов. Например вводится параметр Change-Id, который пишется в последней строке сообщения о коммите (Change-Id используется gerrit'ом)

Merge-конфликты

Если в двух ветках были внесены изменения в одну и ту же часть файла, то при попытке смержить эти ветки возникнет конфликт. Git не создаст новый коммит для слияния. Он приостановит этот процесс до тех пор, пока вы не разрешите конфликт. Выполнив команду git status можно посмотреть, какие файлы не прошли слияние.

Git добавляет стандартные маркеры в файл, в котором случился конфликт, так что вы можете открыть файл вручную и разрешить этот конфликт. Ваш файл содержит секцию, которая выглядит примерно так:

 <<<<<<< HEAD:'имя_файла'
 =======
 >>>>>>> my_branch:'имя_файла' 

В верхней части блока (всё что выше =======) версия участка файла из ветки в которую осуществляеться слияние, всё, что находится в нижней части — версия участка файла из ветки которую сливаем. Чтобы разрешить конфликт, вы должны либо выбрать одну из этих частей, либо как-то объединить содержимое по своему усмотрению.

После того как вы разобрались с каждой из таких секций в каждом из конфликтных файлов, выполните git add для каждого конфликтного файла. Индексирование будет означать для Git'а, что все конфликты в файле теперь разрешены.

Если вы хотите использовать графические инструменты для разрешения конфликтов, можете выполнить команду git mergetool, которая запустит соответствующий графический инструмент и покажет конфликтные ситуации.

Работа с тегами

Как правило, кроме веток разработчики используют теги – чтобы запомнить состояние кода в какой-то момент. Тег – это своеобразный слепок, точно идентифицирующий состояние кода. Гит умеет работать с подписанными GPG тегами и с неподписанными. Здесь я рассмотрю только неподписанные теги. Просмотр тегов:

git tag

Для создания анатинованного тега необходимо выполнить команду:

git tag -a %tag_name% -m "%comment%"

Чтобы убрать тег необходимо выполнить:

git tag -d

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

git push --tags

Чтобы получить версию с конкретного тега необходимо создать от него локальную ветку и расчекаутить эту ветку:

git fetch origin tag
git branch
git checkout

Разумеется, в будущем эту ветку можно будет сделать глобальной и выслать в удаленной репозиторий.

Перезапись истории коммитов

Иногда бывает необходимо слить несколько маленьких коммитов в большой, либо отредактировать сообщение в каком-то коммите. Бывает, нужно докомитать какие-то изменения в какой-то коммит в истории. Для этого используется команда rebase.

  • Перезапись истории
 * git rebase -i HEAD~3 (открыть редактор для последних 3 коммитов)

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

  • pick - ничего не делать
  • squash - слить с предыдущим
  • edit - внести изменения в коммит
  • reword - поменять сообщение коммита

Далее гит проходит по всем коммитам и в зависимости от действия даёт возможность что-то поменять.

Так же для этого можно использовать git commit --amend

Отмена изменений

  • Отмена последнего коммита, все изменения становятся непроиндексированными
 * git reset
 * git reset HEAD~3 (отмена последних 3 коммитов)
  • Полностью стереть последний коммит
 * git reset --hard
 * git reset --hard HEAD~3 (последних 3 коммита)
  • Отмена локальных комитов
 * git clean

В команде reset так же можно указывать и хеш коммита, до которого выполнить сброс (проверить!)

Полезные фишки

  • Перетянуть комит из другой ветки, либо поднять его из истории и положить наверх
 * git cherry-pick 6hgjnd8s (6hgjnd8s - начало хеша комита, хеши можно посмотреть командой git log)

Работа с "заначками"

  • В любой момент можно спрятать не добавленные в индекс изменения в "заначку". Потом можно их оттуда достать в любой другой ветке, либо после других коммитов.
  • Спрятать изменения (При этом изменения прячутся. Состояние файловой системы будет эквивалентно последним проиндексированным изменениям!)
 * git stash (Сохраняет изменения. Они хранятся в виде стека, где последние изменения будут извлечены первыми.)
 * git stash save my_changes (сохраняет изменения с меткой my_changes)
  • Достать и применить изменения
 * git stash apply 
 * git stash apply my_changes (достаёт изменения с меткой my_changes)
  • Просмотр "заначек"
 * git stash list
  • Очистка списка "заначек"
 * git stash clear

Просмотр истории

  • Просмотр истории комитов
 * git log
  • Просмотр изменений
 * git diff
  • Просмотр истории коммитов в графической оболочке
 * gitk (у гита есть графическая среда, поставляется вместе с гитом)

Flows по работе с git

Во всех примерах работы с git считается, что ветка master - это ветка, которая синхронизируется с удаленной веткой. С этой ветки мы может заливать изменения в удаленный репозиторий. Все другие ветки используются как локальные и не предоставляют возможности работы с удаленным репозиторием.

Создание локального репозитория

Локальный репозиторий - это обычная папка, в которой есть подпапка .git. Поэтому для создания локального репозитория нужно сделать:

  - создать папку (например, git-repo)
  - перейти в созданную папку
  - выполнить команду git init

В итоге в папке git-repo появиться подпапка .git. Все, локальный репозиторий готов. Можно копировать в эту папку проекты и управлять версиями с помощью git'a.

Сливаем проект с сервера

Git поддерживает работу по трем протоколам: ssh, http и git. Использование ssh и http предоставляет read/write права, а использование протокола git - только read only.

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

  * git clone ssh://[username]@[host]:[port]/[projectname]
  * git clone http(s)://[host]/[username]/[projectname]
  * git clone git://[host]/[username]/[projectname]

Заливаем изменения на удаленный сервер

Мы внесли изменения в проект и требуется их залить на удаленный сервер. Чтобы отправить изменения в удаленный репозиторий сначала нужно закоммитать эти изменения в локальный репозиторий.

  * git add .                    ; добавляем все сделанные изменения в индекс
  * git commit                   ; коммитаем локально наши изменения (при выполнении команды откроется консольный редактор, где нужно будет написать сообщение к коммиту)
  * git push                     ; отправляем изменения в удаленный репозиторий

Сливаем обновления в проекте с сервера

У нас есть проект в локальном репозитории, кто-то внес изменения в проект и залил их на удаленный сервер. Нам нужно слить эти изменения в свой локальный репозиторий:

  * git pull --rebase            ; все изменения в удаленном репозитории будут перенесены в наш локальный. Опция --rebase позволяет сохранить прямолинейную историю коммитов

Нужно начать делать таск. Почему для него лучше создать ветку, а потом смержить ее в master

Создание отдельной ветки для каждого таска позволяет:

  - легко переключаться в работу над разными тасками;
  - выполнение одного таска не зависит от другого; 
  - master branch всегда содержит самые последние изменения в проекте и проект из этой ветки всегда живой.

Представим такую ситуацию, что вам нужно сделать новый функционал. Вы начинаете вносить изменения в код, находясь в ветке master. Вы сделали один коммит, потом второй для нового функционала, но не заливали эти коммиты в удаленный репозиторий. Потом вам нужно быстро добавить еще что-то и вы делаете еще один коммит поверх 2-х с новым функционалом. Уже в одной ветке смешалось два разных функционала. Последний коммит нужно отправить в удаленный репозиторий. Получается, что каким-то образом вы должны выкинуть два предыдущих коммита и отправить только последний. Без другой ветки не обойтись! Поэтому лучше для каждого функционала делать новую ветку, т.к. потом вы сможете переключаться между ними и делать одновременно разные функционалы и отправлять в удаленный репозиторий только нужный из функционалов, а другие еще дорабатывать.

Допустим у вас есть ветка master и ветка с новым функционалом feature-branch. Feature-branch - это ответвеление от ветки master. Для того, чтобы перенести коммиты с новым функционалом из feature-branch в ветку master нужно сделать:

  * git checkout featute-branch    ; переключаемя на ветку feature-branch
  * git rebase master              ; делаем перемещение всех новых коммитов из ветки master в текущую ветку. Это нужно чтобы история коммитов в обоих ветках была одинаковой. Коммиты, который мы сделали в текущей 
                                     ветке будут лежать наверху истории
  * git checkout master            ; переключаемся на ветку master    
  * git merge feature-branch       ; производим мерж feature-branch с веткой master. Т.к. до этой команды мы сделали, чтобы у обоих веток была одинковая история, то при мержинге произойдет копирование в ветку 
                                     master только тех коммитов, которых в ней нет, но они есть в feature-branch, т.е. это и будут наши коммиты с новым функционалом

Нужно закоммитать в отдельную ветку на удаленном репозитории

Допустим что в удаленном репозитории была создана ветка для проверки каких-либо фишек. Эта ветка называется feature-branch. Нам нужно закоммитать свои фишки в эту ветку:

  * git push origin HEAD:refs/for/feature-branch

Просмотр истории коммитов

Чтобы просмотреть историю коммитов нужно использовать команду

  * git log

Клавиши для работы с историей коммитов:

  * "стрелки вверх/вниз" - навигация по истории
  * "q" - выход из просмотра истории

Как откатиться на какой-то коммит

Предположим у вас 3 коммита и вам нужно откатиться на самый первый их них. Откат можно сделать с потерей первых двух коммитов и без потери.

Откат с потерей коммитов означает их удаление:

  * git reset --hard HEAD~2           ; удаление последних двух коммитов

Откат без потери коммитов можно сделать следующим образом:

  * git branch current-branch-copy    ; создаем ветку от текущей ветки, чтобы сохранить в ней удаляемые коммиты
  * git reset --hard HEAD~2           ; удаление последних двух коммитов из текущей ветки. Удаленные коммиты легко возвратить из созданной ветки current-branch-copy.

Git на сервере

Настройка git на сервере.

Установка ssh сервера и клиента