Skip to content

Instantly share code, notes, and snippets.

@neonxp
Created January 15, 2012 18:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neonxp/1616744 to your computer and use it in GitHub Desktop.
Save neonxp/1616744 to your computer and use it in GitHub Desktop.
GIT Wizardy
1 Введение
В своей прошлой заметке я постарался осветить в общих чертах стиль работы с
распределенной системой контроля версий git и указать на отличия по сравнению с
классическими централизованными СКВ. Целью было прежде всего обобщение опыта
работы с системой без упоминания тонкостей синтаксиса отдельных команд.
Данный же топик задумывался как непосредственное введение в работу с git, нечто
среднее между tutorial и обобщенной справкой, до которого все же рекомендуется
прочитать упомянутое выше введение. Сознательно избегаются технические
подробности работы git, употребляются только общие для СКВ термины и
ограничивается список упоминаемых команд.
2 Работа с локальным репозитарием
Сила любых распределенных систем — в наличии у каждого разработчика локального
репозитария, в котором возможно организовывать произвольную личную схему
разработки. В git есть несколько основных команды для ведения работы на месте и
множество вспомогательных.
2.1 Базовые команды
Базовые команды — те, без которых невозможно обойтись в разработке.
2.1.1 git init — создание репозитария
Команда git init создает в директории пустой репозитарий в виде директория
.git, где и будет в дальнейшем храниться вся информация об истории коммитов,
тегах — ходе разработки проекта:
mkdir project-dir
cd project-dir
git init
Другой способ создать репозитарий — команда git clone, но о ней чуть позже.
2.1.2 git add и git rm — индексация изменений
Следующее, что нужно знать — команда git add. Она позволяет внести в индекс — временное хранилище — изменения, которые затем войдут в коммит. Примеры
использования:
git add EDITEDFILE — индексация измененного файла, либо оповещение о
создании нового.
git add. — внести в индекс все изменения, включая новые файлы.
Из индекса и дерева одновременно проекта файл можно удалить командой git rm:
git rm FILE1 FILE2 — отдельные файлы
git rm Documentation/\*.txt — хороший пример удаления из документации к git,
удаляются сразу все файлы txt из папки.
Сбросить весь индекс или удалить из него изменения определенного файла можно
командой git reset:
git reset — сбросить нафиг весь индекс.
git reset — EDITEDFILE — удалить из индекса конкретный файл.
Команда git reset используется не только для сбрасывания индекса, поэтому дальше
ей будет уделено гораздо больше внимания.
2.1.3 git status — состояние проекта, измененные и не добавленные файлы, индексированные файлы
Команда git status, пожалуй, можно считать самой часто используемой наряду с
командами коммита и индексации. Она выводит информацию обо всех изменениях,
внесенных в дерево директорий проекта по сравнению с последним коммитом рабочей
ветки; отдельно выводятся внесенные в индекс и неиндексированные
файлы. Использовать ее крайне просто:
git status
Кроме того, git status указывает файлы с неразрешенными конфликтами слияния и
файлы, игнорируемые git.
2.1.4 git commit — совершение коммита
Коммиты — базовое понятие во всех системах контроля версий, поэтому совершатся
он должен легко и по возможности быстро. В самом своем простом виде достаточно
после индексации набрать:
git commit
Если индекс не пустой, то на его основе будет совершен коммит, после чего
пользователя попросят прокомментировать вносимые изменения вызовом команды
edit(например, в Ubuntu обычно вызывается простенький текстовый редактор nano, у
меня же — emacs). Сохраняемся, и вуала! Коммит готов.
Есть несколько ключей, упрощающих работу с git commit:
git commit -a — совершит коммит, автоматически индексируя изменения в файлах
проекта. Новые файлы при этом индексироваться не будут! Удаление же файлов
будет учтено.
git commit -m «commit comment» — комментируем коммит прямо из командной строки
вместо текстового редактора.
git commit FILENAME — внесет в индекс и создаст коммит на основе изменений
единственного файла.
2.1.5 git reset — возврат к определенному коммиту, откат изменений, «жесткий» или «мягкий»
Помимо работы с индексом (см. выше), git reset позволяет сбросить состояние
проекта до какого-либо коммита в истории. В git данное действие может быть двух
видов: «мягкого»(soft reset) и «жесткого» (hard reset).
«Мягкий» (с ключом "--soft") резет оставит нетронутыми ваши индекс и все дерево
файлов и директорий проекта, вернется к работе с указанным коммитом. Иными
словами, если вы обнаруживаете ошибку в только что совершенном коммите или
комментарии к нему, то легко можно исправить ситуацию:
git commit… — некорректный коммит;
git reset --soft HEAD^ — переходим к работе над уже совершенным коммитом,
сохраняя все состояние проекта и проиндексированные файлы
edit WRONGFILE
edit ANOTHERWRONGFILE
add.
6.1. git commit -c ORIG_HEAD — вернутся к последнему коммиту, будет предложено
редактировать его сообщение. Если сообщение оставить прежним, то
достаточно изменить регистр ключа -с:
6.2. git commit -C ORIG_HEAD
Обратите внимание на обозначение HEAD^, оно означает «обратиться к предку
последнего коммита». Подробней описан синтаксис такой относительной адресации
будет ниже, в разделе «Хэши, тэги, относительная адресация». Соответственно,
HEAD — ссылка на последний коммит. Ссылка ORIG_HEAD после «мягкого» резета
указывает на оригинальный коммит.
Естественно, можно вернуться и на большую глубину коммитов,
«Жесткий» резет (ключ --hard) — команда, которую следует использовать с
осторожностью. Git reset --hard вернет дерево проекта и индекс в состояние,
соответствующее указанному коммиту, удалив изменения последующих коммитов:
git add.
git commit -m «destined to death»
git reset --hard HEAD~1 — больше никто и никогда не увидит этот позорный коммит.
git reset --hard HEAD~3 — вернее, три последних коммита. Никто. Никогда.
Если команда достигнет точки ветвления, удаления коммита не произойдет.
Для команд слияния или выкачивания последних изменений с удаленного репозитария
примеры резета будут приведены в соответствующих разделах.
2.1.6 git revert — отмена изменений, произведенных в прошлом отдельным коммитом
Возможна ситуация, в которой требуется отменить изменения, внесенные отдельным
коммитом. Git revert создает новый коммит, накладывающий обратные изменения:
git revert config-modify-tag — отменяем коммит, помеченный тегом.
git revert 12abacd — отменяем коммит, используя его хэш.
Для использования команды необходимо, чтобы состояние проекта не отличалось от
состояния, зафиксированного последним коммитом.
2.1.7 git log — разнообразная информация о коммитах в целом, по отдельным файлам и
различной глубины погружения в историю
Иногда требуется получить информацию об истории коммитов, коммитах, изменивших
отдельный файл; коммитах за определенный отрезок времени и так далее. Для этих
целей используется команда git log.
Простейший пример использования, в котором приводится короткая справка по всем
коммитам, коснувшимся активной в настоящий момент ветки (о ветках и ветвлении
подробно узнать можно ниже, в разделе «Ветвления и слияния»):
git log
Получить подробную информацию о каждом в виде патчей по файлам из коммитов
можно, добавив ключ -p (или -u):
git log -p
Статистика изменения файлов, вроде числа измененных файлов, внесенных в них
строк, удаленных файлов вызывается ключом --stat:
git log --stat
За информацию по созданиям, переименованиям и правам доступа файлов отвечает ключ
--summary:
git log --summary
Для исследования истории отдельного файла достаточно указать в виде параметра
его имя (кстати, в моей старой версии git этот способ не срабатывает,
обязательно добавлять " — " перед «README»):
git log README
или, если версия git не совсем свежая:
git log — README
Далее будет приводится только более современный вариант синтаксиса. Возможно
указывать время, начиная в определенного момента («weeks», «days», «hours», «s»
и так далее):
git log --since=«1 day 2 hours» README
git log --since=«2 hours» README
git log --since=«2 hours» dir/ — изменения, касающиеся отдельной папки.
Можно отталкиваться от тегов:
git log v1… — все коммиты, начиная с тега v1.
git log v1… README — все коммиты, включающие изменения файла README, начиная с
тега v1.
git log v1..v2 README — все коммиты, включающие изменения файла README, начиная с
тега v1 и заканчивая тегом v2.
Создание, выведение списка, назначение тегов будет приведено в соответствующем
разделе ниже.
Интересные возможности по формату вывода команды предоставляет ключ --pretty:
git log --pretty=oneline — выведет на каждый из коммитов по строчке, состоящей из хэша
(здесь — уникального идентификатора каждого коммита, подробней — дальше).
git log --pretty=short — лаконичная информация о коммитах, приводятся только
автор и комментарий
git log --pretty=full/fuller — более полная информация о коммитах, с именем
автора, комментарием, датой создания и внесения коммита
В принципе, формат вывода можно определить самостоятельно:
git log --pretty=format:'FORMAT'
Определение формата можно поискать в разделе по git log из Git Community Book
или справке. Красивый ASCII-граф коммитов выводится с использованием ключа
--graph.
2.1.8 git diff — отличия между деревьями проекта; коммитами; состоянием индекса и каким-либо коммитом.
Своего рода подмножеством команды git log можно считать команду git diff,
определяющую изменения между объектами в проекте: деревьями (файлов и
директорий):
git diff — покажет изменения, не внесенные в индекс.
git diff --cached — изменения, внесенные в индекс.
git diff HEAD — изменения в проекте по сравнению с последним коммитом
git diff HEAD^ — предпоследним коммитом
Можно сравнивать «головы» веток:
git diff master..experimental
Ну или активную ветку с какой-либо:
git diff experimental
2.1.9 git show — показать изменения, внесенные отдельным коммитом
Посмотреть изменения, внесенные любым коммитом в истории можно командой git
show:
git show COMMIT_TAG
2.1.10 git blame и git annotate — вспомогательные команды, помогающие отслеживать изменения файлов
При работе в команде часто требуется выяснить, кто именно написал конкретный
код. Удобно использовать команду git blame, выводящую построчную информацию о
последнем коммите, коснувшемся строки, имя автора и хэш коммита:
git blame README
Можно указать и конкретные строки для отображения:
git blame -L 2,+3 README — выведет информацию по трем строкам, начиная со второй.
Аналогично работает команда git annotate, выводящая и строки, и информацию о
коммитах, их коснувшихся:
git annotate README
2.1.11 git grep — поиск слов по проекту, состоянию проекта в прошлом
git grep, в целом, просто дублирует функционал знаменитой юниксовой
команды. Однако, он позволяет слова и их сочетания искать в прошлом проекта, что
бывает очень полезно:
git grep tst — поиск слова tst в проекте.
git grep -с tst — подсчитать число упоминаний tst в проекте.
git grep tst v1 — поиск в старой версии проекта.
Команда позволяет использовать логическое И и ИЛИ:
git grep -e 'first' --and -e 'another' — найти строки, где упоминаются и первое
слово, и второе.
git grep --all-match -e 'first' -e 'second' — найти строки, где встречается хотя
бы одно из слов.
2.2 Ветвление
Операции ветвления и слияния — сердце и душа git, именно эти возможности делают такой
удобной работу с системой.
2.2.1 git branch — создание, перечисление и удаление веток
Работа с ветками — очень легкая процедура в git, все необходимые механизмы
сконцентрированы в одной команде:
git branch — просто перечислит существующие ветки, отметив активную.
git branch new-branch — создаст новую ветку new-branch.
git branch -d new-branch — удалит ветку, если та была залита (merged) с
разрешением возможных конфликтов в текущую.
git branch -D new-branch — удалит ветку в любом случае.
git branch -m new-name-branch — переименует ветку.
git branch --contains v1.2 — покажет те ветки, среди предков которых есть
определенный коммит.
2.2.2 git checkout — переключение между ветками, извлечение отдельных файлов из истории коммитов
Команда git checkout позволяет переключаться между последними коммитами (если
упрощенно) веток:
checkout some-other-branch
checkout -b some-other-new-branch — создаст ветку, в которую и произойдет
переключение.
Если в текущей ветке были какие-то изменения по сравнению с последним коммитом в
ветке(HEAD), то команда откажется производить переключение, дабы не потерять
произведенную работу. Проигнорировать этот факт позволяет ключ -f:
checkout -f some-other-branch
В случае, когда изменения надо все же сохранить, используют ключ -m. Тогда команда
перед переключением попробует залить изменения в текущую ветку и, после
разрешения возможных конфликтов, переключиться в новую:
checkout -m some-other-branch
Вернуть файл (или просто вытащить из прошлого коммита) позволяет команда вида:
git checkout somefile — вернуть somefile к состоянию последнего коммита
git checkout HEAD~2 somefile — вернуть somefile к состоянию на два коммита назад по ветке.
2.2.3 git merge — слияние веток (разрешение возможных конфликтов).
Слияние веток, в отличие от обычной практики централизованных систем, в git
происходит практически каждый день. Естественно, что имеется удобный интерфейс к
популярной операции:
git merge new-feature — попробует объединить текующую ветку и ветку new-feature.
В случае возникновения конфликтов коммита не происходит, а по проблемным файлам
расставляются специальные метки а ля svn; сами же файлы отмечаются в индексе как
«не соединенные» (unmerged). До тех пор пока проблемы не будут решены, коммит совершить
будет нельзя.
Например, конфликт возник в файле TROUBLE, что можно увидеть в git status:
git merge experiment — произошла неудачная попытка слияния.
git status — смотрим на проблемные места.
edit TROUBLE — разрешаем проблемы.
git add. — индексируем наши изменения, тем самым снимая метки.
git commit — совершаем коммит слияния.
Вот и все, ничего сложного. Если в процессе разрешения вы передумали разрешать
конфликт, достаточно набрать:
git reset --hard HEAD — это вернет обе ветки в исходные состояния.
Если же коммит слияния был совершен, используем команду:
git reset --hard ORIG_HEAD
2.2.4 git rebase — построение ровной линии коммитов
Предположим, разработчик завел дополнительную ветку для разработки отдельной
возможности и совершил в ней несколько коммитов. Одновременно по какой-либо
причине в основной ветке также были совершены коммиты: например, в нее были
залиты изменения с удаленного сервера; либо сам разработчик совершал в ней
коммиты.
В принципе, можно обойтись обычным git merge. Но тогда усложняется сама линия
разработки, что бывает нежелательно в слишком больших проектах, где участвует
множество разработчиков.
Предположим, имеется две ветки, master и топик, в каждой из которых было совершенно несколько коммитов начиная с момента ветвления.
Команда git rebase берет коммиты из ветки topic и накладывает их на последний коммит ветки
master:
git-rebase master topic — вариант, в котором явно указывается, что и куда
прикладывается.
git-rebase master — на master накладывается активная в настоящий момент
ветка.
После использования команды история становится линейной. При возникновении
конфликтов при поочередном накладывании коммитов
работа команды будет останавливаться, а в проблемные местах файлов появятся
соответствующие метки. После редактирования — разрешения конфликтов — файлы
следует внести в индекс командой git add и продолжить наложение следующих
коммитов командой git rebase --continue. Альтернативными выходами будут команды
git rebase --skip (пропустить наложение коммита и перейти к следующему) или git
rebase --abort (отмена работы команды и всех внесенных изменений).
С ключом -i (--interactive) команда будет работать в интерактивном
режиме. Пользователю будет предоставлена возможность определить порядок внесения
изменений, автоматически будет вызывать редактор для разрешения конфликтов и так
далее.
2.2.5 git cherry-pick — применение к дереву проекта изменений, внесенных отдельным коммитом
Если ведется сложная история разработки, с несколькими длинными ветками
разработками, может возникнуть необходимость в применении изменений, внесенных
отдельным коммитом одной ветки, к дереву другой (активной в настоящий момент).
git cherry-pick BUG_FIX_TAG — изменения, внесенные указанным коммитом будут
применены к дереву, автоматически проиндексированы и станут коммитом в активной
ветке.
git cherry-pick BUG_FIX_TAG -n — ключ "-n" показывает, что изменения надо
просто применить к дереву проекта без индексации и создания коммита.
2.3 Прочие команды и необходимые возможности
Для удобства работы с git было введено дополнительное понятие: тэг. Кроме того
дальше будет пояснена необходимость в хэшах, и его применение; показан способ
обращаться к коммитам при помощи относительной адресации.
2.3.1 Хэш — уникальная идентификация объектов
В git для идентификации любых объектов используется уникальный (то есть с
огромной вероятностью уникальный) хэш из 40 символов, который определяется
хэшируюшей функцией на основе содержимого объекта. Объекты — это все: коммиты,
файлы, тэги, деревья. Поскольку хэш уникален для содержимого, например, файла,
то и сравнивать такие файлы очень легко — достаточно просто сравнить две строки
в сорок символов.
Больше всего нас интересует тот факт, что хэши идентифицируют коммиты. В этом
смысле хэш — продвинутый аналог ревизий Subversion. Несколько примеров
использования хэшей в качестве способа адресации:
git diff f292ef5d2b2f6312bc45ae49c2dc14588eef8da2 — найти разницу текущего
состояния проекта и коммита за номером… Ну сами видите, каким.
git diff f292ef5 — то же самое, но оставляем только шесть первых символов. Git
поймет, о каком коммите идет речь, если не существует другого коммита с таким
началом хэша.
git diff f292 — иногда хватает и четырех символов.
git log febc32...f292 — читаем лог с коммита по коммит.
Разумеется, человеку пользоваться хэшами не так удобно, как машине, именно поэтому были
введены другие объекты — тэги.
2.3.2 git tag — тэги как способ пометить уникальный коммит
Тэг (tag) — это объект, связанный с коммитом; хранящий ссылку на сам коммит, имя
автора, собственное имя и некоторый комментарий. Кроме того, разработчик может
оставлять на таких тегах собственную цифровую подпись.
Кроме этого в git представленные так называемые «легковесные тэги» («lightweight
tags»), состоящие только из имени и ссылки на коммит. Такие тэги, как правило,
используются для упрощения навигации по дереву истории; создать их очень легко:
git tag stable-1 — создать «легковесный» тэг, связанный с последним
коммитом. Если тэг уже есть, то еще один создан не будет.
git tag stable-2 f292ef5 — пометить определенный коммит.
git tag -d stable-2 — удалить тег.
git tag -l — перечислить тэги.
git tag -f stable-1.1 — создать тэг для последнего коммита, заменить
существующий, если таковой уже был.
После создания тэга его имя можно использовать вместо хэша в любых командах
вроде git diff, git log и так далее:
git diff stable-1.1...stable-1
Обычные тэги имеет смысл использовать для приложения к коммиту какой-либо
информации, вроде номера версии и комментария к нему. Иными словами, если в
комментарии к коммиту пишешь «исправил такой-то баг», то в комментарии к тэгу по
имени «v1.0» будет что-то вроде «стабильная версия, готовая к использованию»:
git tag -a stable — создать обычный тэг для последнего коммита; будет вызван
текстовый редактор для составления комментария.
git tag -a stable -m «production version» — создать обычный тэг, сразу указав в
качестве аргумента комментарий.
Команды перечисления, удаления, перезаписи для обычных тэгов не отличаются от
команд для «легковесных» тэгов.
2.3.3 Относительная адресация
Вместо ревизий и тэгов в качестве имени коммита можно опираться на еще один
механизм — относительную адресацию. Например, можно обратиться прямо к предку
последнего коммита ветки master:
git diff master^
Если после «птички» поставить цифру, то можно адресоваться по нескольким предкам
коммитов слияния:
git diff HEAD^2 — найти изменения по сравнению со вторым предком последнего
коммита в master. HEAD здесь — указатель на последний коммит активной ветки.
Аналогично, тильдой можно просто указывать, насколько глубоко в историю ветки
нужно погрузиться:
git diff master^^ — что привнес «дедушка» нынешнего коммита.
git diff master~2 — то же самое.
Обозначения можно объединять, чтобы добраться до нужного коммита:
git diff master~3^~2
git diff master~6
2.3.4 файл .gitignore — объясняем git, какие файлы следует игнорировать
Иногда по директориям проекта встречаются файлы, которые не хочется постоянно
видеть в сводке git status. Например, вспомогательные файлы текстовых
редакторов, временные файлы и прочий мусор.
Заставить git status игнорировать можно, создав в корне или глубже по дереву
(если ограничения должны быть только в определенных директория) файл
.gitignore. В этих файлах можно описывать шаблоны игнорируемых файлов
определенного формата.
Пример содержимого такого файла:
>>>>>>>Начало файла
#комментарий к файлу .gitignore
#игнорируем сам .gitignore
.gitignore
#все html-файлы…
*.html
#… кроме определенного
!special.html
#не нужны объектники и архивы
*.[ao]
>>>>>>>>Конец файла
Существуют и другие способы указания игнорируемых файлов, о которых можно узнать
из справки git help gitignore.
3 «Вместе мы — сила», или основы работы с удаленным репозитарием
Естественно, что большая часть проектов все-таки подразумевает работу по крайней
мере двух разработчиков, которым требуется обмениваться кодом. Далее будут
перечислены команды, требующиеся для совместной — возможно удаленной — работы.
3.1 Удаленные ветки (remote tracking branches)
Новое понятие здесь — удаленные ветки. Удаленные ветки соответствуют какой-либо
ветке (чаще master) на удаленном сервере. Одна такая создается автоматически при
создании копии удаленного репозитария; все команды, связанные с удаленной
работой, будут по умолчанию использовать именно эту удаленную ветку (обычно
называется «origin»).
Рассмотрим эти команды.
3.2 git clone — создание копии (удаленного) репозитария
Для начала работы с центральным репозитарием, следует создать копию
оригинального проекта со всей его историей локально:
git clone /home/username/project myrepo — клонируем репозитарий с той же машины
в директорию myrepo.
git clone ssh://user@somehost:port/~user/repository — клонируем репозитарий,
используя безопасный протокол ssh (для чего требуется завести у себя на машине
эккаунт ssh).
git clone git://user@somehost:port/~user/repository/project.git/ — у git имеется
и собственный протокол.
3.3 git fetch и git pull — забираем изменения из центрального репозитария (из удаленной ветки)
Для синхронизации текущей ветки с репозитарием используются команды git fetch и
git pull.
git fetch — забрать изменения удаленной ветки из репозитария по умолчания,
основной ветки; той, которая была использована при клонировании
репозитария. Изменения обновят удаленную ветку (remote tracking branch), после
чего надо будет провести слияние с локальной ветку командой git merge.
git fetch /home/username/project — забрать изменения из определенного
репозитария.
Возможно также использовать синонимы для адресов, создаваемые командой git remote:
git remote add username-project /home/username/project
git fetch username-project — забрать изменения по адресу, определяемому
синонимом.
Естественно, что после оценки изменений, например, командой git diff, из надо
создать коммит слияния с основной:
git merge username-project/master
Команда git pull сразу забирает изменения и проводит слияние с активной веткой:
git pull — забрать из репозитария, для которого были созданы удаленные ветки по
умолчанию.
git pull username-project — забрать изменения из определенного репозитария.
Как правило, используется сразу команда git pull.
3.4 git push — вносим изменения в удаленный репозитарий (удаленную ветку)
После проведения работы в экспериментальной ветке, слияния с основной,
необходимо обновить удаленный репозитарий (удаленную ветку). Для этого
используется команда git push:
git push — отправить свои изменения в удаленную ветку, созданную при
клонировании по умолчанию.
git push ssh://yourserver.com/~you/proj.git master:experimental — отправить изменения
из ветки master в ветку experimental удаленного репозитария.
git push origin :experimental — в удаленном репозитарии origin удалить ветку experimental.
git push origin master:master — в удаленную ветку master репозитария origin (синоним
репозитария по умолчанию) ветки локальной ветки master.
4 git-о-день
В этом разделе будут показаны и разобраны подробно несколько обычных и чуть
меньше необычных для работы с git ситуаций.
4.1 Обычный workflow при работе с локальным репозитарием
Git обладает необычайной легкостью в использовании не только как распределенная
система контроля версий, но и в работе с локальными проектами. Давайте разберем
обычный цикл — начиная с создания репозитария — работы разработчика git над
собственным персональным проектом:
mkdir git-demo
cd git-demo
git init
git add.
git commit -m «initial commit»
git branch new-feature
git checkout new-feature
git add.
git commit -m «Done with the new feature»
git checkout master
git diff HEAD new-feature
git merge new-feature
git branch -d new-feature
git log --since=«1 day»
Разберем каждое из действий. 1-2 — просто создаем рабочую директорию
проекта. 3 — создаем репозитарий в директории. 4 — индексируем все существующие
файлы проекта (если, конечно, они вообще были). 5 — создаем инициализирующий
коммит. 6 — новая ветка, 7 — переключение в нее (можно сделать в один шаг
командой git checkout -b new-feature). Далее, после непосредственной работы с
кодом, индексируем внесенные изменения(8), совершаем коммит(9). Переключаемся в
основную ветку(10), смотрим отличия между последним коммитом активной ветки и
последним коммитом экспериментальной (11). Проводим слияние (12) и, если не было
никаких конфликтов, удаляем ненужную больше ветку (13). Ну и на всякий случай
оценим проведенную за последний день работу (14).
Почему именно так? Зачем отказываться от линейной модели? Хотя бы даже потому,
что у программиста появляется дополнительная гибкость: он может переключаться
между задачами (ветками); под рукой всегда остается «чистовик» — ветка
master; коммиты становятся мельче и точнее.
4.2 Workflow при работе с удаленным репозитарием
Предположим, что вы и несколько ваших напарников создали общественный
репозитарий, чтобы заняться неким общим проектом. Как выглядит самая
распространенная для git модель общей работы?
git clone http://yourserver.com/~you/proj.git
… возможно, прошло некоторое время.
git pull
git diff HEAD^
git checkout -b bad-feature
… работаем некоторое время.
git commit -a -m «Created a bad feature»
git checkout master
git pull
git merge bad-feature
git commit -a
git diff HEAD^
… запускаем тесты проекта, обнаруживаем, что где-то произошла ошибка. Упс.
git reset --hard ORIG_HEAD
git checkout bad-feature
… исправляем ошибку.
git -m bad-feature good-feature
git commit -a -m «Better feature»
git checkout master
git pull
git merge good-feature
git push
git branch -d good-feature
Итак, первым делом создаем (1) создаем копию удаленного репозитария (по
умолчанию команды вроде git pull и git push будут работать с ним). «Вытягиваем»
последние обновления (2); смотрим, что же изменилось(3); создаем новую ветвь и
переключаемся в нее (4); индексируем все изменения и одновременно создаем из них
коммит (5); переключаемся в главную ветвь (6), обновляем ее (7); проводим
слияние с веткой bad-feature(8) и, обнаружив и разрешив конфликт, делаем коммит
слияния (9).
После совершения коммита отслеживаем изменения(10), запускаем, например,
юнит-тесты и с ужасом обнаруживаем, что после слияния проект валится на большей
части тестов.
В принципе, тесты можно было прогнать и до коммита, в момент
слияния (между пунктами 8 и 9); тогда бы хватило «мягкого» резета.
Таким образом, приходится совершить «жесткий» (11) сброс произошедшего слияния,
ветки вернулись в исходное до состояние. После чего переключаемся в неудачную
ветку (12), вносим необходимые изменения и переименовываем ветку (13). Совершаем
коммит (14); переходим в главную ветку(15), опять ее обновляем (16). На этот раз
бесконфликтно делаем слияние (17), закидываем изменения в удаленный репозитарий
(18) и удаляем ненужную теперь ветку (19). Закрываем ноутбук, одеваемся и идем
домой под утро.
5 Заключение
В топике не рассмотрено несколько важных вопросов, вроде администрирования
общественного репозитария, интеграции с текстовыми редакторами или IDE,
использования SSH под Линукс и Windows; приветствуются замечания, и особенно
дополнения в раздел «git-о-день».
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment