Возможности, которые привнесли в работу программиста системы контроля версий, подняли ее на совершенно иной уровень. И если поначалу, особенно на простых проектах, новый багаж необходимых для этого знаний и навыков несколько пугает — да что там говорить, в первое время заметно замедляет работу,— то со временем с переходом на более сложные задачи становится уже не просто необычным средством бэкапа, а одним из основных инструментов, сохраняющим огромное количество времени, нервов и, в конечном итоге, денег.

И тут главное выбрать рабочий процесс, который бы максимально удовлетворял запросам компании. Об одном из таких шаблонов я бы хотел рассказать. Я сам познакомился с GIT сравнительно недавно, а вплотную использую его не более полугода, поэтому мое описание вряд ли будет претендовать на полноту. Однако, возможно, именно такое объяснение на пальцах поможет правильно понять необходимые основы.

Цепочка применения изменений в нашем случае будет выглядеть как «Feature» -> «Dev» -> «Production». Между «dev» и «production» еще может возникать «stage», но это уже зависит от конкретных особенностей проекта, в том числе финансовых.

То есть изменения, внесенные в проект разработчиком, переносятся на тестовый сайт, где происходит необходимое тестирование. После принятия решения о пригодности изменений они заливаются на stage, равный во всех отношениях «боевой» площадке и далее уже копируются на нее.

Как это выглядит на пальцах?

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

Почему именно так? Какими бы не были схожими ваши ветки, они все разные, причем иногда разные кардинально - начиная от прав доступа к файлам и заканчивая порой совершенно неопределимыми различиями, исток которых, по всей видимости, кроется чуть ли не в рознящихся контрольных суммах. Однако в конечном итоге любые изменения будут заливаться в основную ветку, на которой работает production. И стоит при создании новой ветки по невнимательности отнаследоваться от другой — и долгие часы разруливания конфликтов вам обеспечены.

То есть что бы ни происходило в нашей голове, мы должны сделать следующие шаги:

Переключаемся на основную ветку:

git checkout master

Обновляем ее:

git pull -p

В общих чертах pull состоит из двух компонентов: команды fetch, которая подтягивает изменения с удаленного репозитория, и команды merge, которая совмещает эти изменения с вашей локальной веткой.

Параметр «-p» слабо документирован, однако очень приятен в работе. Формально он никак не относится к самой команде pull, потому вы вряд ли найдете упоминание о нем в ее описании. Однако он передается в fetch, что приводит к удалению всех локальных веток, которые ранее существовали, но были удалены из удаленного репозитория. По сути это избавляет вас от исторического мусора, который со временем начинает доставлять боль при выводе списка веток, не говоря уже о невозможности пользоваться атодополнением.

Создаем feature-ветку

git checkout -b [название-ветки]

Давайте договоримся, что далее будем ее назвать, например «task-name»

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

Интересное начинается позже. Пока мы пишем код, тем же занимаются и наши коллеги. Да, порой это трудно признать, но помимо вас тоже есть люди, которые работают. И в основную ветку начинают заливаться изменения, которые все далее и далее отдаляют ее от вашей. Простейший алгоритм слияния изменений, который используется гитом (включение или кода между соседними одинаковыми строками) уже не сработает - структура master изменилась. То есть если мы закончим нашу работу, тщательно протестируем все локально и начнем сливать все в master, то увидим кучу незнакомого кода, который, как кажется, специально написан в самых неподходящих местах, чтобы испортить нашу жизнь.

Как избежать этого? Если честно, то, к сожалению, практически никак. Рано или поздно нам придется исправлять возникающие конфликты. Вопрос — когда и сколько сразу. Здесь есть два подхода.

Если коротко, то первый состоит в том, чтобы сделать это в самом конце, когда будем сливать изменения в вышестоящую ветку (в нашем случае это ветка, соответствующая stage-сайту). У этого метода есть свои преимущества, но все равно будет много боли, причем сразу. При этом психологически будет сложнее, потому что в уме вы свою работу уже сделали и хочется поскорее про нее забыть, как про страшный сон.

Вторая состоит в использовании rebase. Что это за зверь такой, надо сказать, я не сразу понял. Каждый раз, когда я в чтении мануалов доходил до этого момента, глаза застилала пелена и с мыслью о том, что уж это-то мне точно не пригодится еще пару лет, я шел прокрастинировать дальше.

На самом деле все достаточно просто. Rebase - это процесс подтягивания изменений с основной ветки (вообще, с любой, но мы сейчас не об этом) в вашу. Иначе говоря, каждый божий день, перед работой и перед отправкой изменений на удаленный репозиторий вы заливаете все последние изменения с той ветки, от которой отнаследовались при создании вашей (конечно же, от master) и исправляете небольшую порцию конфликтов, если они есть.

Далее мы предполагаем, что вы находитесь на своей feature-ветке.

Ребейз можно делать на локальную ветку и тогда это будет выглядеть так:

git rebase master

Но на самом деле это не сильно удобно, потому что локальная ветка имеет все шансы быть неактуальной (а регулярно переключаться на нее и делать git pull просто так на всякий случай - занятие, как бы это сказать, из области психиатрии). Поэтому мы сделаем иначе:

git pull -r origin master

То есть все примерно то же самое, но мы ребейзим не на локальный, а на удаленный, оригинальный master, как бы далеко он не ушел от локального.

В процессе ребейза все ваши коммиты последовательно сливаются в master.

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

git add -u

и продолжаем процесс ребейза:

git rebase --continue

Исправление текущего конфликта в редких случаях можно пропустить:

git rebase --skip

Или полностью откатить весь процесс:

git rebase --abort

Небольшая неожиданность возникнет при попытке залить отребейзенную ветку на удаленный репозиторий. Обычный git push уже не сработает, потому что с точки зрения репозитория ваша локальная и удаленная ветки расходятся и не могут быть слиты, что совершенно правильно.

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

git push -f

И здесь кроется небольшой недостаток метода, так как история изменений при этом теряется, и если вы накосячили с ребейзом, исправить содеянное будет уже не так легко. Поэтому если изменений много и они существенные, то перед началом ребейза не лишним будет подстраховаться и сделать копию ветки:

git checkout -b task-name-backup

И не забудьте переключиться назад перед ребейзом и дальнейшей работой:

git checkout task-name

И обязательно, в самом конце, когда все и везде протестировано и уже есть добро на перенос вашей работы на рабочий сайт, снова сделать ребейз, чтобы merge в master не вызвал неожиданностей.

Но это чуть позже. Сейчас мы уверены, что все хорошо и можно отдавать работу на тестирование. Мы делаем мерж в ветку, на которой работает дев-сайт. Могу поспорить, что она так и называется — dev.

Ребейзим ветку на master (не обязательно, но не помешает, к тому же должно уменьшить расхождение между dev и master):

git pull -r origin master

Переключаемся на dev и актуализируем ее:

git checkout dev
git pull

Внимание - короткая дорога:

git checkout -B dev origin/dev

Так мы одномоментно переключимся и сбросим ветку до состояния origin - меньше телодвижений всяко приятней.

Сливаем ветки:

git merge task-name

С большой степенью вероятности получаем конфликты, количество которых зависит от нашей кармы.

Смотрим список того, что конфликтует:

git status

Проводим некоторое время над разрешением конфликтов. Все конфликты git отмечает примерно следующим образом:

<<<<<<< HEAD
    <link type="text/css" rel="stylesheet" media="all" href="style.css" />
=======
    <!-- no style -->
>>>>>>> task-name

Собственно, по этим многочисленным угловым скобкам и можно провести поиск в каждом конфликтующем файле.

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

Учтите, неизвестный вам код в большинстве случаев не нужно удалять - его потом другие люди искать будут.

Как и вслучае с ребейзом, процесс мержа можно откатить:

git merge --abort

Когда все конфликты разрешены, а соответствующее им обрамление удалено, делаем git add исправленных файлов. Впрочем, делаем в большинстве случаев добавление всех измененных файлов:

git add -u

Создаем коммит с указанием того, что решали конфликты слияния (далее дева этот коммит не пойдет и карму нам не испортит)

git commit -m "Task-name - fixing merge conflicts"

И заливаем все это на удаленный репозиторий.

git push

Далее специально обученные люди решают, насколько ваше творение соответствует общественным ожиданиям, однако рано или поздно будет принято решение мержить ветку в production.

И тут еще одна тонкость. Я бы сказал - жирность: dev и master сайты находятся в этой цепочке не последовательно, а параллельно. Иными словами, мы не переносим изменения из dev в master. Feature-ветка мержится в master напрямую. Во многом именно поэтому мы наследуемся именно от мастера и регулярно на этот мастер ребейзим.

Конечно, такой процесс неизбежно приведет к росту расхождений между рабочим и тестовым сайтом, но по мере необходимости последний можно актуализировать, попросту делая push --force мастера в дев.

Какие могут быть проблемы и как их избежать

Никак. Если проект большой, то так или иначе вы будете сталкиваться с необходимостью при решении конфликтов разбираться не только в своем, но и в чужом коде. И здесь важно не только обеспечить работоспособность своего функционала, но и не сломать чужой. Для этого нужно при необходимости тесно контактировать с другими разработчиками и никогда не действовать по принципу «что это тут за лишний код понапоявлялся... скопирую-ка я в этой файл содержимое своей ветки». Иначе отдельный котел вам будет обеспечен, и хорошо, если в иной жизни, а не в этой, причем вечером и в пятницу.

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

Ну и, безусловно, основная доля серьезных проблем возникает, если вы или кто-то из сотрудников нарушает основные правила работы с репозиторием - начинают ветку не от master или даже работают непосредственно в master.

Итого

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

Алексей Зубенко

Добавить комментарий