Русский
Русский
English
Статистика
Реклама

Rebase

Переписывание истории репозитория кода, или почему иногда можно git push -f

22.09.2020 16:11:47 | Автор: admin


Одно из первых наставлений, которое молодой падаван получает вместе с доступом к git-репозиториям, звучит так: никогда не ешь жёлтый снег делай git push -f. Поскольку это одна из сотен максим, которые нужно усвоить начинающему инженеру-разработчику ПО, никто не тратит время на уточнение, почему именно так нельзя делать. Это как младенцы и огонь: спички детям не игрушки и баста. Но мы растём и развиваемся как люди и как профессионалы, и однажды вопрос а почему, собственно? встаёт в полный рост. Эта статья написана по мотивам нашего внутреннего митапа, на тему: Когда можно и нужно переписывать историю коммитов.



Я слышал, что умение ответить на этот вопрос на собеседовании в некоторых компаниях является критерием прохождения собеседования на сеньорские позиции. Но чтобы лучше понять ответ на него, нужно разобраться, почему вообще плохо переписывание истории?

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

На самом низком уровне git-репо представляет собой набор объектов и указателей на них. Каждый объект имеет свой уникальный 40-значный хэш (20 байт в 16-ричной системе), который вычисляется на основе содержимого объекта.



Иллюстрация взята из The Git Community Book

Основные типы объектов это blob (просто содержимое файла), tree (набор указателей на blobs и другие trees) и commit. Объект типа commit представляет собой только указатель на tree, на предыдущий коммит и служебную информацию: дата/время, автор и комментарий.

Где здесь ветки и тэги, которыми мы привыкли оперировать? А они не являются объектами, они являются просто указателями: ветка указывает на последний коммит в ней, тэг на произвольный коммит в репо. То есть когда мы в IDE или GUI-клиенте видим красиво нарисованные веточки с кружочками-коммитами на них они строятся на лету, пробегая по цепочкам коммитов от концов веток вниз к корню. Самый первый коммит в репо не имеет предыдущего, вместо указателя там null.

Важный для понимания момент: один и тот же коммит может фигурировать в нескольких ветках одновременно. Коммиты не копируются при создании новой ветки, она просто начинает расти с того места, где был HEAD в момент отдачи команды git checkout -b <branch-name>.

Итак, почему же переписывание истории репозитория вредно?



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

Почему-то мало кто знает, что довольно давно у команды git push существует безопасный ключ --force-with-lease, который заставляет команду завершиться с ошибкой, если в удалённом репозитории есть коммиты, добавленные другими пользователями. Я всегда рекомендую использовать его вместо -f/--force.

Вторая причина, по которой команда git push -f считается вредной, заключается в том, что при попытке слияния (merge) ветки с переписанной историей с ветками, где она сохранилась (точнее, сохранились коммиты, удалённые из переписанной истории), мы получим адское число конфликтов (по числу коммитов, собственно). На это есть простой ответ: если аккуратно соблюдать Gitflow или Gitlab Flow, то такие ситуации, скорее всего, даже не возникнут.

И наконец есть неприятная побочка переписывания истории: те коммиты, которые как бы удаляются при этом из ветки, на самом деле, никуда не исчезают и просто остаются навечно висеть в репо. Мелочь, но неприятно. К счастью, эту проблему разработчики git тоже предусмотрели, введя команду сборки мусора git gc --prune. Большинство git-хостингов, как минимум GitHub и GitLab, время от времени,
производят эту операцию в фоне.

Итак, развеяв опасения перед изменением истории репозитория, можно, наконец, перейти к главному вопросу: зачем оно нужно и когда оправдано?

На самом деле, я уверен, что практически каждый из более-менее активных пользователей git хоть раз, да изменял историю, когда вдруг оказывалось, что в последнем коммите что-то пошло не так: вкралась досадная опечатка в код, сделал коммит не от того пользователя (с личного e-mail вместо рабочего или наоборот), забыл добавить новый файл (если вы, как я, любите пользоваться git commit -a). Даже изменение описания коммита приводит к необходимости его перезаписи, ведь хэш считается и от описания тоже!

Но это тривиальный случай. Давайте рассмотрим более интересные.

Допустим, вы сделали большую фичу, которую пилили несколько дней, отсылая ежедневно результаты работы в репозиторий на сервере (4-5 коммитов), и отправили свои изменения на ревью. Двое-трое неутомимых ревьюверов закидали вас крупными и мелкими рекомендациями правок, а то и вовсе нашли косяки (ещё 4-5 коммитов). Затем QA нашли несколько краевых случаев, тоже требующих исправлений (ещё 2-3 коммита). И наконец при интеграции выяснились какие-то несовместимости или попадали автотесты, которые тоже надо пофиксить.

Если теперь нажать, не глядя, кнопку Merge, то в главную ветку (у многих она по старинке называется master) вольются полтора десятка коммитов типа My feature, day 1, Day 2, Fix tests, Fix review и т.д. От этого, конечно, помогает режим squash, который сейчас есть и в GitHub, и в GitLab, но с ним надо быть осторожными: во-первых, он может заменить описание коммита на что-то непредсказуемое, а во-вторых заменить автора фичи на того, кто нажал кнопку Merge (у нас это вообще робот, помогающий релиз-инженеру собрать сегодняшний деплой). Поэтому самым простым будет перед окончательной интеграцией в релиз схлопнуть все коммиты ветки в один при помощи git rebase.

Но бывает также, что к код-ревью вы уже подошли с историей репо, напоминающей салат Оливье. Такое бывает, если фича пилилась несколько недель, ибо была плохо декомпозирована или, хотя за это в приличных коллективах бьют канделябром, требования изменились в процессе разработки. Вот, например, реальный merge request, который приехал ко мне на ревью две недели назад:



У меня рука машинально потянулась к кнопке Report abuse, потому что как ещё можно охарактеризовать реквест из 50 коммитов с почти 2000 изменённых строк? И как его, спрашивается, ревьюить?

Честно говоря, у меня ушло два дня просто на то, чтобы заставить себя приступить к этому ревью. И это нормальная реакция для инженера; кто-то в подобной ситуации, просто не глядя, жмёт Approve, понимая, что за разумное время всё равно не сможет сделать работу по обзору этого изменения с достаточным качеством.

Но есть способ облегчить жизнь товарищу. Помимо предварительной работы по лучшей декомпозиции задачи, уже после завершения написания основного кода можно привести историю его написания в более логичный вид, разбив на атомарные коммиты с зелёными тестами в каждом: создал новый сервис и транспортный уровень для него, построил модели и написал проверку инвариантов, добавил валидацию и обработку исключений, написал тесты.
Каждый из таких коммитов можно ревьюить по отдельности (и GitHub, и GitLab это умеют) и делать это набегами в моменты переключения между своими задачами или в перерывах.

Сделать это всё нам поможет всё тот же git rebase с ключом --interactive. В качестве параметра надо передать ему хэш коммита, начиная с которого нужно будет переписать историю. Если речь о последних 50 коммитах, как в примере на картинке, можно написать git rebase --interactive HEAD~50 (подставьте вместо 50 вашу цифру).

Кстати, если вы в процессе работы над задачей подливали к себе ветку master, то сначала надо будет сделать rebase на эту ветку, чтобы merge-коммиты и коммиты из мастера не путались у вас под ногами.

Вооружившись знаниями о внутреннем устройстве git-репозитория, понять принцип действия rebase на master будет несложно. Эта команда берёт все коммиты в нашей ветке и меняет родителя первого из них на последний коммит в ветке master. См. схему:




Иллюстрации взяты из книги Pro Git

Если изменения в C4 и C3 конфликтуют, то после разрешения конфликтов коммит C4 изменит своё содержание, поэтому он переименован на второй схеме в C4.

Таким образом, вы получите ветку, состоящую только из ваших изменений, и растущую из вершины master. Само собой, master должен быть актуальным. Можно просто использовать версию с сервера: git pull --rebase origin/master (как известно, git pull равносилен git fetch && git merge, а ключ --rebase заставит git сделать rebase вместо merge).

Вернёмся наконец к git rebase --interactive. Его делали программисты для программистов, и понимая, какой стресс люди будут испытывать в процессе, постарались максимально сохранить нервы пользователя и избавить его от необходимости чрезмерно напрягаться. Вот что вы увидите на экране:


Это репозиторий популярного пакета Guzzle. Похоже, что rebase ему не помешал бы

В текстовом редакторе открывается сформированный файл. Внизу вас ожидает подробная справка о том, что тут вообще делать. Далее в режиме простого редактирования вы решаете, что делать с коммитами в вашей ветке. Всё просто, как палка: pick оставить как есть, reword поменять описание коммита, squash слить воедино с предыдущим (процесс работает снизу вверх, то есть предыдущий это который строчкой ниже), drop вообще удалить, edit и это самое интересное остановиться и замереть. После того, как git встретит команду edit, он встанет в позицию, когда изменения в коммите уже добавлены в режим staged. Вы можете поменять всё, что угодно в этом коммите, добавить поверх него ещё несколько, и после этого скомандовать git rebase --continue, чтобы продолжить процесс rebase.

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

Если вы запутались и кажется, что всё пропало, у вас есть кнопка аварийного катапультирования git rebase --abort, которая немедленно вернёт всё как было.

Вы можете повторять rebase несколько раз, затрагивая только части истории, и оставляя остальные нетронутыми при помощи pick, придавая своей истории всё более и более законченный вид, как гончар кувшину. Хорошим тоном, как я уже написал выше, будет сделать так, что тесты в каждом коммите будут зелёными (для этого отлично помогает edit и на следующем проходе squash).

Ещё одна фигура высшего пилотажа, полезная в случае, если надо несколько изменений в одном и том же файле разложить по разным коммитам git add --patch. Она бывает полезна и сама по себе, но в сочетании с директивой edit она позволит вам разделить один коммит на несколько, причём сделать это на уровне отдельных строк, чего не позволяет, если я не ошибаюсь, ни один GUI-клиент и ни одна IDE.

Убедившись ещё раз, что всё в порядке, вы наконец можете со спокойной душой сделать то, с чего начался этот туториал: git push --force. Ой, то есть, разумеется, --force-with-lease!



Поначалу вы, скорее всего, будете тратить на этот процесс (включая первоначальный rebase на master) час, а то и два, если фича реально развесистая. Но даже это намного лучше, чем ждать два дня, когда ревьювер заставит себя наконец взяться за ваш реквест, и ещё пару дней, пока он сквозь него продерётся. В будущем же вы, скорее всего, будете укладываться в 30-40 минут. Особенно помогают в этом продукты линейки IntelliJ со встроенным инструментом разрешения конфликтов (full disclosure: компания FunCorp оплачивает эти продукяы своим сотрудникам).

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

Спасибо за внимание всем, кто дочитал до конца! Надеюсь, что статья будет полезна не только вам, но и коллегам, которым ваш код попадает на ревью. Если у вас есть клёвые хаки для git делитесь ими в комментариях!
Подробнее..

Перевод Поддержание аккуратной истории в Git с помощью интерактивного rebase

12.01.2021 10:14:24 | Автор: admin

Прим. перев.: эта статья была написана автором Git-клиента Tower, Tobias Gnther, и опубликована в блоге GitLab. В ней просто и наглядно рассказывается об основных возможностях интерактивного rebase'а, что может стать отличным введением для тех, кто только начинает им пользоваться.

Interactive rebase один из самых универсальных инструментов Git'а. В этой статье мы поговорим о том, как с его помощью корректировать сообщения при коммитах, исправлять ошибки, и о многом другом.

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

Обратите внимание на слово локальной: rebase следует использовать только для очистки локальной истории коммитов (например, перед включением одной из ваших локальных feature-веток в общую ветку команды). И наоборот, этот мощный инструмент НЕ следует использовать для исправления коммитов в ветке, которая уже загружена и открыта для совместной работы в удаленном репозитории. Интерактивный rebase инструмент для переписывания истории Git, и его не следует использовать для редактирования коммитов, которые уже открыты для других.

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

Примечание: для визуализации сценариев и последовательностей операций для некоторых скриншотов я использовал GUI к Git под названием Tower.

Редактирование сообщения в старом коммите

Иногда вы замечаете опечатку в старом коммите или вспоминаете, что забыли упомянуть нечто важное в его описании. Если бы речь шла о самом последнем коммите, можно было бы воспользоваться опцией --amend команды git commit. Но в случае более старых коммитов придется воспользоваться интерактивным rebaseом.

Вот пример описания к коммиту, которое мы будем исправлять:

Плохое сообщение к коммиту, которое мы будем исправлятьПлохое сообщение к коммиту, которое мы будем исправлять

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

Начинаем с родительского коммитаНачинаем с родительского коммита

Теперь нужно скормить хэш родительского коммита команде:

$ git rebase -i 0023cddd

Откроется окно редактора со списком коммитов для изменения. Не удивляйтесь тому, что они приведены в обратном порядке: в рамках интерактивного rebaseа Git будет повторно применять прошлые коммиты один за другим. Другими словами, с точки зрения Git коммиты выстроены в правильном порядке.

Окно редактора со списком выбранных коммитовОкно редактора со списком выбранных коммитов

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

Редактирование описания старого коммитаРедактирование описания старого коммита

Сохраните изменения и закройте окно. Поздравляю сессия интерактивного rebaseа завершена, сообщение к коммиту успешно отредактировано!

Объединение нескольких коммитов в один

Rebase также можно использовать для объединения нескольких старых коммитов в один. При этом, конечно, актуальным остается золотое правило систем управления версиями: в большинстве случаев лучше создавать множество мелких коммитов, нежели несколько крупных. Однако, как и во всем остальном, мы можем внезапно обнаружить, что несколько перестарались со следованием этому правилу, и решить, что было бы хорошо объединить несколько старых коммитов в один.

Давайте предположим, что нужно объединить следующие выбранные коммиты в один:

Объединяем несколько коммитов в одинОбъединяем несколько коммитов в один

Как и в первом случае, процесс начинается с запуска сессии интерактивного rebaseа на коммите-предшественнике тех, что мы хотим изменить.

$ git rebase -i 2b504bee

Снова откроется окно редактора с историей коммитов, которые мы хотим объединить:

Помечаем нужные строки кодовым словом squashПомечаем нужные строки кодовым словом squash

Действию, которое мы собираемся произвести над коммитами, соответствует кодовое слово squash. В данном случае следует помнить лишь об одной тонкости: строка, помеченная squash, будет объединена со строкой, которая находится выше нее. Именно поэтому на скриншоте выше я пометил словом squash строку 2 (она будет объединена со строкой 1).

Сохраните изменения и закройте окно редактора. Как и в первом случае, появится новое окно с просьбой ввести сообщение для нового, объединенного коммита:

Вводим сообщение для нового коммитаВводим сообщение для нового коммита

Сохраните сообщение и закройте окно. Будет создан новый коммит, содержащий изменения обоих старых коммитов. Вуаля!

Исправление ошибок

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

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

Именно для таких случаев и предназначен fixup. Этот инструмент берет коммит с быстрым исправлением, применяет его изменения к оригинальном коммиту (тем самым исправляя его), и удаляет корректирующий коммит.

Как работает fixupКак работает fixup

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

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

Следующий шаг сделать коммит этих изменений в репозиторий, но с небольшой добавкой: делать коммит надо с флагом --fixup, попутно указав хэш плохого коммита:

$ git add corrections.txt

$ git commit --fixup 2b504bee

Если теперь посмотреть на историю, вы увидите, что был создан ничем не примечательный коммит (разве вы ожидали чего-то иного?). Но при более внимательном взгляде становятся заметны некоторые особенности: к новому коммиту были автоматически добавлены пометка fixup! и описание старого плохого коммита:

Оригинальный коммит и корректирующий коммит (fixup)Оригинальный коммит и корректирующий коммит (fixup)

Теперь пора запускать interactive rebase. Опять же, в качестве отправной точки выбираем коммит, предшествующий плохому:

$ git rebase -i 0023cddd autosquash

А вторым ингредиентом нашего секретного соуса выступает флаг --autosquash. Он позволяет не вносить дополнительных правок в открывшемся окне редактора. Внимательно посмотрите на скриншот:

Корректирующий коммит помечен как fixup и размещен в правильном порядкеКорректирующий коммит помечен как fixup и размещен в правильном порядке

Git автоматически сделал две вещи:

  1. Он пометил новый коммит как fixup.

  2. И переупорядочил строки так, чтобы fixup-коммит оказался непосредственно под плохим коммитом. Дело в том, что fixup работает в точности как squash: он объединяет выделенный коммит с коммитом выше.

Таким образом, нам ничего делать не надо. Сохраните изменения и закройте окно редактора.

Давайте еще раз взглянем на историю коммитов:

Счастливый финал!Счастливый финал!

Мало того, что оригинальный коммит теперь содержит правки из вспомогательного, но и некрасивый вспомогательный коммит (с исправлениями) исчез из истории. Все красиво, словно никогда и не было никаких проблем!

Откройте для себя возможности interactive rebase

Существует множество различных вариантов использования интерактивного rebaseа: большинство из них связаны с исправлением ошибок. Подробнее узнать о других способах можно в бесплатном (англоязычном) курсе First Aid Kit for Git коллекции коротких видео (по 2-3 минуты на эпизод).

Примечание оригинального редактора: забавно, но мне пришлось воспользоваться interactive rebase при редактировании этой статьи! Один из коммитов включал изображение, размер которого превышал 1 Мб (что противоречит правилам сайта GitLab). Пришлось вернуться к этому коммиту и включить в него изображение подходящего размера. Спасибо за урок, Вселенная! ?

P.S. от переводчика

Читайте также в нашем блоге:

Подробнее..

Перевод Продвинутые функции гита, о которых вы, возможно, не знали

04.03.2021 18:06:42 | Автор: admin

Git очень мощный инструмент, который практически каждый разработчик должен использовать ежедневно, но для большинства из нас git сводится к нескольким командам: pull commit push. Однако, чтобы быть эффективным, продуктивным и обладать всей мощью git, необходимо знать ещё несколько команд и трюков. Итак, в этой статье мы исследуем функции git, которые просто запомнить, применять и настроить, но которые могут сделать ваше время с git гораздо более приятным.


Прокачиваем базовый рабочий процесс

Прежде чем мы воспользуемся даже самыми базовыми командами pull, commit и push, необходимо выяснить, что происходит с нашими ветками и изменёнными файлами. Для этого можно воспользоваться git log довольно известной командой, хотя не все знают, как сделать его вывод на самом деле читабельным и красивым:

Дерево git log.Дерево git log.

Такой граф даст хороший обзор, однако часто нужно копать немного глубже. Например, посмотреть историю (эволюцию) определённых файлов или даже отдельных функций; в этом поможет git log с флагом -L::).

git log для функции.git log для функции.

Теперь, когда мы немного представляем происходящее в репозитории, мы, возможно, захотим проверить различия между обновлёнными файлами и последним коммитом. Здесь можно воспользоваться git diff; опять же ничего нового здесь нет, но у diff есть кое-какие опции и флаги, о которых вы, возможно, не знаете. Например, можно сравнить две ветки: git diff branch -a branch -b, или даже конкретные файлы в разных ветках: `git diff <commit-a> <commit-b> -- <пути>`.

Иногда чтение git diff становится трудной задачей. Можно попробовать прописать игнорирующий все пробельные символы (white-space) флаг -w, и этим немного заспамить diff, или флаг --word-diff и работать вместо строк с раскрашенными словами.

Если простой статичный вывод в оболочке вас не устраивает, можно запустить difftool, вот так: git difftool=vimdiff, команда откроет файлы diff внутри vim в два окна слева и справа. Очевидно, что Vim не единственный вариант; можно запустить git difftool --tool-help, чтобы увидеть список всех инструментов, которые можно использовать вместе с diff.

Мы уже видели, как просматривать историю конкретных частей или строк в файла с помощью git log. Было бы удобно делать нечто подобное, например, стейджинг частей файлов, правда? И такое легко делается в в IDE, например, в IntelliJ; то же самое уже сложнее в git CLI, но, конечно же, по-прежнему возможно: в git add пропишите опцию --patch:

Команда открывает редактор, в котором отображается один "hunk" [кусок], представляющий собой кусок кода с несколькими отличающимися друг от друга строками в нём. Можно много чего сделать с этим куском, но самые важные опции это y принять изменения (делает стейджинг), n не принимать (не делать стейджинг) и e отредактировать кусок перед стейджингом (полный список опций здесь).

Когда закончите с интерактивным стейджингом, вы можете запустить git status, и увидите, что файл с частичным стейджингом находится в разделах "Changes to be committed:" и "Changes not staged for commit:". Кроме того, можно запустить git add -i (интерактивный стейджинг), а затем воспользоваться командой s (статус), которая покажет вам, какие строки находятся на стейджинге, а какие нет.

Исправление распространённых ошибок

Закончив со стейджингом, я (слишком) часто осознаю, что добавил то, чего добавлять не хотел. Однако на этот случай у git для файлов нет команды un-stage. Чтобы обойти ограничение, можно сбросить репозиторий командой git reset --soft HEAD somefile.txt. Вы также можете включить в git reset флаг -p, который покажет вам тот же UI, что и у git-add -p. Также не забудьте добавить туда флаг --soft, иначе вы сотрёте ваши локальные изменения!

Поменьше грубой силы

Теперь, когда мы закончили стейджинг, всё, что осталось, commit и push. Но что, если мы забыли что-то добавить или совершили ошибку и хотим исправить уже запушенные коммиты? Есть простое решение, использующее git commit -a и git push --force, но оно может быть довольно опасным, если мы работаем над общей веткой, например, master. Таким образом, чтобы избежать риска перезаписи чужой работы из-за того, что мы решили проблему грубой силой, мы можем воспользоваться флагом --force-with-lease. Этот флаг в отличие от --force запушит на изменения только в том случае, если за время работы никто не добавил никаких изменений в ветку. Если ветка была изменялась, код не будет отправлен, и этот факт сам по себе указывает на то, что перед отправкой кода мы должны выполнить git pull.

Правильное слияние веток

Если вы работаете над репозиторием, в котором участвует более одного разработчика, можно с уверенностью предположить, что вы работаете в отдельной ветке, а не в мастере. Это также означает, что рано или поздно вам придётся включить свой код в кодовую базу (главную ветку). Вполне вероятно, что, пока вы работали над своей веткой, кто-то другой уже добавил свой код в мастер, из-за чего ветка вашей функциональности отстаёт на несколько коммитов. Можно пойти напролом и выполнить слияние вашего кода в мастер с помощью git merge, но команда создаст дополнительный комммит слияния, а также, без необходимости на то, затруднит чтение истории и сделает её сложнее:

История с ветвлением.История с ветвлением.

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

Но как нам сделать такой rebase? Можно выполнить rebase в его базовой форме с помощью git rebase master feature_branch, чего часто бывает достаточно (за этим следует push --force). Однако, чтобы получить от git rebase максимальную отдачу, также следует включить флаг -i, чтобы rebase был интерактивным. Интерактивный rebase удобный инструмент, чтобы, например, переформулировать, сжать или вообще очистить ваши коммиты и всю ветку. В качестве небольшой демонстрации мы можем даже сделать rebase ветки на саму себя:

Приём выше позволяет нам повторно применять последние 4 коммита и изменить их, получив полезный результат, например сжать одни коммиты и переформулировать другие:

Выше показан пример сеанса rebase. В верхней части показывается ветка перед перезагрузкой. Вторая часть фрагмента это список коммитов, представленных после запуска git rebase каждый из них можно выбрать, чтобы включить в работу (pick). Мы можем изменить действие для каждого из них, а также полностью переупорядочить коммиты. Как показано в третьем разделе примера, некоторые допустимые действия переформулирование (оно говорит git открыть редактор сообщений о коммите), сжатие коммита (объединяет коммиты в предыдущий) и исправление коммита: (исправление работает как сжатие, но при этом сбрасывает сообщение о коммите). После того как мы применим эти изменения и переформулируем изменённые коммиты, мы получим историю, которая показана на скриншоте выше, в его нижней части.

Если во время rebase вы столкнулись с каким-либо конфликтом, чтобы разрешить его, вы можете запустить git mergetool --tool=vimdiff, а затем продолжить rebase с помощью git rebase --continue. git mergetool может быть вам не знаком, на первый взгляд он может показаться пугающим. В действительности же это то же самое, что IDE вроде IntelliJ, просто в стиле Vim. Если вы не знаете хотя бы несколько сочетаний клавиш Vim, то, как и в случае с любым другим использующим этот редактор инструментом, вам, может быть, трудно даже понять, на что на самом деле вы смотрите. Если вам нужна помощь, я рекомендую прочитать эту исчерпывающую статью.

Если всё это кажется слишком сложным или вы просто боитесь работать с rebase, в качестве альтернативы создайте пул реквест на GitHub и нажмите кнопку Rebase and merge, чтобы сделать, по крайней мере, простые и быстрые rebase и merge с быстрой перемоткой.

Главное эффективность

Я думаю, что примеры выше показали несколько изящных советов и хитростей, но всё это может быть довольно сложно запомнить, особенно когда дело касается команд вроде git log. К счастью, чтобы разрешить эти трудности, можно воспользоваться глобальной конфигурацией git. Она находится в ~/.gitconfig и обновляется каждый раз, когда вы запускаете git config --global. Даже если вы не настраивали этот файл, он, вероятно, содержит кое-какие базовые вещи, такие как раздел [user], но можно добавить много других разделов:

Выше приведён пример некоторых из доступных опций конфигурации. Примечательно, что длинная команда git log это только псевдоним git graph. Автокоррекция установлена 10: такое значение включает её и заставляет ждать 1 секунду, прежде чем выполнить правильную команду, в которой была опечатка, и, наконец, последний раздел подписывание коммита GPG (подробнее об этом читайте ниже).

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

Автозавершение команд это инструмент не менее продуктивный, чем псевдонимы, и он просто устанавливается:

Extras

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

  • git delta список файлов, которые в другой ветке отличаются.

  • git show-tree древовидное представление коммитов всех ветвей, похожее на показанный ранее git log.

  • git pull-request пул-реквест в командной строке.

  • git changelog генерирует журнал изменений (changelog) из тегов и сообщений в коммитах.

Конечно, это не единственный крутой плагин. Например, есть ещё один удобный инструмент, позволяющий открыть репозиторий в браузере прямо из командной строки. Кроме того, в приглашении терминала можно настроить статус репозитория, это делается с помощью zsh или bash-it.

Подписываем коммиты

Даже если вы никогда не вкладывались в какой-либо проект Open Source, вы, вероятно, прокручивали историю коммитов такого проекта. В ней вы, скорее всего, видели значок подтверждённого (sign-off знак о правах на ПО), проверенного или подписанного коммита. Что это такое и зачем?

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

Сверху видно, что в git commit с опцией --sign-off в конце сообщения о коммите автоматически добавляется строка Signed-off-by: , которая формируется на основе вашего имени пользователя в конфигурации git.

Что касается значка signed/verified, который вы, вероятно, заметили в некоторых репозиториях, он существует, потому что на GitHub довольно легко выдавать себя за других пользователей. Всё, что вам нужно сделать, изменить имя сделавшего коммит человека и электронную почту в вашей конфигурации и отправить изменения. Чтобы предупредить ситуацию, вы можете подписывать коммиты с помощью ключей GPG, подтверждающих, что автор коммита и отправитель изменений на самом деле является тем, за кого он себя выдаёт. Подпись коммита более распространена, чем подтверждение прав, поскольку важно знать, кто на самом деле внёс код.

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

Сначала вы генерируете пару ключей GPG (если у вас её ещё нет), затем устанавливаете ключи при помощи git config и, наконец, добавляете опцию -S, когда делаете коммит. Затем, посмотрев на информацию о коммите на GitHub, вы увидите значок, как на картинке ниже.

Подписанный непроверенный коммит.Подписанный непроверенный коммит.

Однако, как видно на изображении, подпись не проверена, потому что GitHub не знает, что ключ GPG принадлежит вам. Чтобы это исправить, открытый ключ из нашей пары ключей нужно отправить на GitHub. Для этого экспортируем ключ командой gpg --export, как здесь:

Затем скопируйте этот ключ и вставите его в поле https://github.com/settings/gpg/new. Если вы проверите ранее подписанный коммит после добавления ключа, то увидите, что коммит теперь проверен (verified). Здесь предполагаем, что вы добавили на GitHub именно тот ключ, которым подписывали коммит:

Подписанный проверенный коммит.Подписанный проверенный коммит.

Заключение

Git очень мощный инструмент, у которого слишком много подкоманд и опций, чтобы в одной статье описать их все. Если вы хотите глубже погрузиться в некоторые связанные с Git темы, я бы порекомендовал прочитать Debugging with Git, чтобы узнать больше о blame, bisect или Getting solid at Git rebase vs. merge, чтобы глубже понять rebase и merge. Помимо множества полезных статей в Интернете часто при поиске информации о некоторых тонкостях git лучший выбор это мануал, который выводится опцией --help, или версия в сети.


Узнайте подробности, как получить Level Up по навыкам и зарплате или востребованную профессию с нуля, пройдя онлайн-курсы SkillFactory со скидкой 40% и промокодомHABR, который даст еще +10% скидки на обучение.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru