Stash, branching и как элегантно бороться с merge conflicts

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

Предположим, что вы являетесь не единственным QAA на проекте. Ну и чисто “теоретически” вы со своим коллегой(-ми) можете периодически работать над одинаковыми участками кода одновременно.

Рассмотрим сей процесс более детально на примере Atlassian Stash, который может стать идеальным 10$ решением для небольших команд. Построен Stash на базе Git. Имеет интуитивно понятный и приятный интерфейс, по функциональности чем-то напоминающий GitHub. Одним из плюсов с точки зрения секьюрности является полная независимость от облачных сервисов. Т.е. вы свободно можете установить пробную up to 90-дневную версию на свой собственный сервер для детального изучения всех фич Stash'а.

Я специально создал тестовый репозиторий. К слову, так выглядит главное окно проекта:

Слева находится основная рабочая панель. И первым же делом новоиспеченный юзер, получивший access, наверняка захочет создать Fork (персональную remote копию master репозитория). Тут же нам предложат задать доп. настройки, включая автоматическую синхронизацию c основным репозиторием.

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

Внешне ваш личный форк ничем не отличается от мастера. В Settings вы сможете настроить permissions, ключи, навесить различные хуки, а также (что очень важно) гибко кастомизировать workflow.

К примеру, на мастер репозиторий можно навесить ограничение по минимальному числу апруверов. Т.е. ваш pull request не сможет быть смерджен с мастером, пока не получите, к примеру, 2 апрува от членов команды в итоге ревью.

Итак, предположим, оба юзера создали форки. Теперь бы нужно склонировать репозиторий к себе на тачку. По дефолту, после клика на Clone вам будет доступен лишь HTTP URL.

Конечно это не совсем по феншую, посему нам нужно будет предварительно создать SSH ключ в системе, а затем добавить его нашему stash-юзеру в личное пользование.

Либо на уровне репозитория:

После этого при клонировании у нас появится доп. SSH опция - как раз то, что нам надо.

Любители SourceTree смогут воспользоваться 1-кликовым решением. Я же предпочитаю все делать напрямую через IntelliJ IDEA. Вставляем SSH линк в идею и клонимируем наш форк локально:

Предположим, что один из юзеров захотел теперь изменить кое-что в коде:

Самое время залить изменения в наш форк: Ctrl + kCommit and Push.

Шагаем в наш форк, а именно в меню Commits. Видим только что внесенные изменения.

И с полной уверенностью в завтрашнем дне создаем pull request в мастер репозиторий, воспользовавшись соответствующей кнопкой:

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

На следующем шаге добавляем description / своего лучшего друга в ревьюверы, и нажимаем заветную кнопку Create.

Попав на страницу PR’а, мы видим предупреждение о том, что он должен быть заапрувлен хотя бы 1 человеком, прежде чем появится возможность мерджа (выше упомянутое ограничение по workflow). А так хотелось проскользнуть незамеченным! :disappointed:

К слову, в Stash мною был специально подключен MailCatcher SMTP для демонстрации того, что ваш друг получил запрос на code review. Открыв почту, он увидит приблизительно следующее приглашение на “чай”:

Помимо этого, Stash будет характерно напоминать, что у вас есть pending PR:

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

К сожалению, в правах на мердж наш коллега был ограничен. А чего уж там? У нас ведь они есть!

С еще большим восторгом делаем Merge и идем пить пиво.

Итак, надеюсь вы все внимательно запомнили и записали?! Потому что так делать НЕЛЬЗЯ! :stuck_out_tongue_winking_eye:

Представим, что тем временем, еще один друг и коллега активно трудился над тем же кодом, что и мы. А затем создал PR следующего содержания:

Конечно такой простой кейс совсем маловероятен. Но на практике вы могли бы оба “зацепить”, к примеру, какой-то “проходной” PageObject. Anyway…

К глубочайшему сожалению, ваш друг был беспощадно унижен характерным предупреждением Stash'а о том, что его PR не может быть смерджен по причине конфликта в файле, над которым была проделана огромная работа:

Конечно же ему будет предложен хитроумный workflow для решения проблемы:

Конечно же он может пойти по другому пути: задискардить все изменения из меню синхронизации (перезатерев свой бранч мастером), сделать soft reset своего коммита, или попробовать после пула из мастера разрулить конфликт из IDE.

Конечно же он также может сделать приблизительно то же самое через SourceTree.

Но после условно многочасовой борьбы над резолвом конфликта, будет известно только одно - сегодня у вас будут активно гореть уши, а завтра ждет беспощадная расправа за совершенное преступление!

Итак, приближаясь к кульминации, может возникнуть логичный вопрос: а можно ли было всего этого избежать? Ответ - конечно! Welcome to branches world!

Теперь рассмотрим валидный workflow. Для этого придется вернуться к моменту после создания форка. Так вот, перед тем, как начинать какую-либо полезную деятельность в вашей любимой IDE, необходимо создать бранч!

Делается это предельно просто средствами самого Stash'а.

Более того, он даже предложит вам выбрать один из адекватных существующих префиксов (типов) для имени вашего будущего бранча.

К примеру, новые тесты или API фреймворка могут быть отмаркированы, как Feature. А фиксы локаторов - как Hotfix.

Касательно имени… Используя Stash, вам наверняка захочется интегрировать его с вашей любимой Jira. Локально конечно я ее не ставил, но возможность такая конечно же есть. Собственно в таком случае лучше всего помечать ваш бранч (его имя) идентификатором Story, которую вы как раз только взяли в оборот. Так будет легче трекать прогресс, да и ID будут автоматически преобразованы в линки, что позволит более гибко перемещаться к описанию задачи.

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

Создаем бранч из мастера. Теперь на странице сорсов мы можем легко переключаться между имеющимися бранчами и анализировать любые изменения.

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

Ваша задача теперь сделать checkout ремоутного бранча в качестве нового локального.

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

Для начала создадим новый changelist. “Теоретически” вы можете одновременно работать над несколькими фичами. Либо же у вас есть какая-то сырая незаконченная наработка, которую вы не хотели бы пока пушить. Так вот, все ваши изменения будут закреплены за активным чендж листом. При этом, в случае чего вы всегда сможете легким движением руки перемещать файлы между имеющимися списками.

Назовем его по идентификатору нашей user story. Вносим какие-нибудь фиксы и пытаемся сделать push.

Тут же мы видим, что коммит осуществляется из CIR-1 чендж листа. На этот раз мы стараемся ввести адекватный commit message, характеризиющий не то, что мы поменяли, а зачем мы это сделали.

Также в окне пуша мы сможем заметить, что работа осуществляется уже не с master бранчем, а с нашим кастомным:

Далее повторяем процедуру создания PR, выбирая уже наш собственный бранч:

При этом, стоит также отметить, что PR создается из кастомного бранча нашего форка в мастер бранч оригинального репозитория.

На этот раз наш друг оказался более любопытным и пооставлял комменты:

Конечно же потом он поставил свой approve, после чего PR был смерджен.

В этот момент наш коллега все так же трудился над тем же участком кода, но уже в своем собственном бранче CIR-2, и даже создал соответствующий PR. Но вот незадача, опять мы видим конфликт!

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

Итак, идем в нашу любимую IDE и переключаемся на master branch:

Тут мы по-прежнему видим старый код, потому нам нужен pull для апдейта проекта:

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

В списке изменений мы видим 1 затронутый файл, и сразу же вспоминаем, что именно он является конфликтным для осуществления мерджа.

Теперь переключаемся на наш бранч (это важно!), и делаем Merge из мастера в наш кастомный бранч.

Тут же наша умная IDE поймет, что назрел конфликт и предложит вам принять очень важное решение, что же с ним делать:

Конечно вы можете устроить подставу вашему обидчику, организовавшему конфликт, приняв наши личные ченджи, что перетрет изменения из upstream’а. Но мы ведь интеллигентные люди! Конечно выберем опцию Merge.

По доброте душевной мы даже решим оставить оба изменения:

Сохраняем. Теперь мы готовы обновить наш PR для разрешения конфликта.

Теперь в окне пуша, помимо нашего коммита, мы видим информацию о всех изменениях подтянутых из мастера.

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

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

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

7 лайков

Монументальный труд. Утащил в корпоративный блог не читая (пока нет времени) :slight_smile: Со ссылкой на первоисточник конечно!

1 лайк

Только пусть читают от начала и до конца, т.к. тут использовался некий “литературный” подвох - обучение на примере, как делать не нужно, с последующими детальными разъяснениями, как делать правильно. :smile: Так что любители читать по-диагонали могут не оценить. :blush:

3 лайка

спасибо! реально нужная информация

1 лайк