В компании, в которой я работаю в настоящее время, мы используем при
разработке довольно распространённую модель ветвления, а в качестве
инструмента для стандартизации работы по этой схеме git-flow. Так же мы
дополнили эту схему ещё одной веткой — staging. Это временная ветка, в
которую сливаются feature branches, и на основе неё делаются
промежуточные релизы на тестовый сервер. Естественно, всё, что
происходит в этой ветке, нужно для предварительного просмотра и
тестирования выполненных задач и никак не должно попадать ни в основную
ветку разработки, ни в master, то есть в итоговый релиз. В принципе,
схема работы достаточно удачная. Она упрощает взаимодействие и работу
большой команды с разным навыком работы с git, и нештатные ситуации
случаются достаточно редко, но зато, если случаются, то это запоминается
надолго.
Началось всё с того, что мы стали замечать, что в master или в develop
ветку начинают попадать коммиты из ветки staging. Самые распространённые
из них — это мержи веток с задачами. Первый раз решили, что кто-то по
невнимательности примержил стейджинг в ветку с задачей и потом вылил,
разбираться точно как и кто не стали, просто всем ещё раз рассказали как
нужно вести разработку и посоветовали внимательнее следить за тем, что
они делают.
Затем история повторилась, но уже в другой команде с другими людьми. Стали копать, выяснили, что и раньше такое было, просто никто не замечал лишних коммитов в истории, потому что в принципе код работал, тесты проходили и ладно.
Разгадка проста и понятна сразу, кто-то примержил staging в ветку со
своей задачей, а потом через некоторое время, когда она вышла в релиз,
все нежелательные коммиты и попали в develop, а потом — в master. Но
как? Если первый раз нам удалось найти злополучный коммит вида Merge
branch staging into 'feature/fourth', то в последний раз во всей истории
всё было чисто. Каждый участник команды говорил, что он всё делал по
инструкции и по модели ветвления.
Те кто знаком с тем, как происходит мерж веток в git, наверное, уже догадались почему так. А остальным я продемонстрирую на примере.
Мы имеем новый репозиторий с несколькими коммитами в master ветку и
одним в develop
![[]](/images/content/gitfuckup/1.png)
Далее создадим новую задачу(first), закоммитим в неё первый коммит и
опубликуем.
1 2 3 4 5 | |
То же самое сделаем для second
1 2 3 4 5 | |
![[]](/images/content/gitfuckup/2.png)
Теперь менеджер попросил нас вылить вторую задачу на тестовый сервер:
1 2 3 4 | |
Но, предположим, у нас произошла ошибка в приложении, связанная только с тестовым сервером и другого программиста попросили её исправить:
1 2 3 4 | |
![[]](/images/content/gitfuckup/3.png)
Далее нас попросили вылить на тестовый сервер первую задачу. Для этого
мы перешли в staging, обновили его и влили в него первую задачу:
1 2 3 4 5 6 7 | |
![[]](/images/content/gitfuckup/4.png)
Пока всё верно. От ветки develop сделаны две задачи. В ветку staging влиты обе ветки с задачами и есть ещё один коммит нужный только для неё.
Мы возвращаемся к разработке второй задачи, но нас кто-то отвлёк и случайно получается, что “на автомате из истории” мы вполняем такую последовательность комманд:
1 2 | |
И ничего вроде бы страшного не произошло, в дальнейшей рутине мы можем ничего не заметить. Так называемый мерж коммит не создался, потому что мерж прошёл в режиме fast-forward.
История, если бы в ней не было коммита Staging fixed, тоже сильно не
изменилась.
1 2 3 4 5 6 7 8 9 | |
Но кто смотрит историю после перехода в ветку? Поэтому вы продолжаете работу. Делаем ещё один коммит в первую задачу (хотя в реале их может быть гораздо больше) и публикуем её.
1 2 3 4 | |
Никаких подозрительных сообщений в консоли. Всё вроде бы нормально. Поэтому мы продолжаем спокойно спать по ночам, а остальная комманда работать над проектом, потому что по сути только наша ветка содержит сейчас стейджинг.
![[]](/images/content/gitfuckup/5.png)
Хотя уже по графику видно, что произошёл некоторый факап, но когда веток и людей много этого незаметно, да и кто смотрит графики.
Затем нас просят выпустить первую задачу и сделать релиз, что мы и делаем:
1 2 3 4 5 | |
![[]](/images/content/gitfuckup/6.png)
Ну вот и всё. Полный фарш. В релиз попало то, что было в staging. В истории всё чисто с точки зрения того, что нет мерж коммита:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Если посмотреть reflog, то вот те строчки, которые были сгенерированы во время выполнения ошибочной команды:
1 2 3 | |
Тоже ничего необычного.
Причина такого поведения — это fast-forward мерж. При таком слиянии в случае, если это возможно, указатель ветки просто перемещается на то же место, куда указывает ветка, которая вливается:
1 2 | |

Как исправить получившуюся ситуацию? Никак. Можно попробовать найти
ветку, в которую влит staging и попробовать откатить её слияние в
develop,
а также удалить коммиты с релизом и почистить все ветки, но это долго и
сложно, особенно если веток с задачами много.
Что делать чтобы такое не произошло?
Что делать, чтобы такое не произошло?
- Внимательно следить за исполняемыми командами.
- Если используется git-flow, то использовать его команды для обновления и публикации веток.
- Использовать либо полный синтаксис команды
git push/pull, либо полностью сокращённый синтаксис без указания веток. Потому что в ситуации из приведённого примера не указывается, в какую ветку слить изменения, поэтому так и получилось, что информация изstagingпопала в ветку с задачей.