Как пользоваться git для начинающих

История коммитов в Git

Git хранит данные в виде набора легковесных «снимков», известных как коммиты. Они хранят состояние файловой системы в определённый момент времени, а также указатель на предыдущий(-ие) коммит(-ы). Каждый коммит содержит уникальную контрольную сумму — идентификатор, который Git использует, чтобы ссылаться на коммит. Чтобы отслеживать историю, Git хранит указатель HEAD, который указывает на первый коммит (мы следуем по цепочке коммитов в обратном порядке, чтобы попасть к предыдущим коммитам).

Мы можем ссылаться на коммит либо через его контрольную сумму, либо через его позицию относительно HEAD, например ссылается на коммит, который находится 4 коммитами ранее HEAD.

Работа в команде, часть 2

Опять. Это опять произошло. Стоило вам только запушить свою feature-ветку для превью, как спустя минуту Патрик сделал то же самое и тем самым перезаписал содержимое staging. #$@! Третий раз за сегодня!

Идея! Используем Slack для оповещений о развертываниях, чтобы никто не пушил новые изменения, пока старые не закончили развертываться.

Оповещения Slack

Настроить оповещения Slack несложно. Надо лишь взять из Slack URL входящего вебхука…

… и передать его в Settings > Services > Slack вместе с вашим логином Slack:

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

Просмотр изменений в истории

Для просмотра истории предыдущих коммитов в обратном хронологическом порядке можно использовать команду . Ей можно передать разные опции:

показывает изменения в каждом коммите;

показывает сокращённую статистику для коммитов, например изменённые файлы и количество добавленных/удалённых строк в каждом их них;

показывает n последних коммитов;

и позволяет отфильтровать коммиты по промежутку времени, например покажет коммиты с 1 января 2019 года;

позволяет указать формат логов (например, ), также можно использовать для большей кастомизации, например ;

и фильтруют коммиты с сообщениями/изменениями кода, которые содержат указанную строку, например,  позволяет посмотреть добавление/удаление функции;

пропускает коммиты со слиянием веток;

позволяет посмотреть, какие коммиты из ветки 2 не находятся в ветке 1 (полезно при слиянии веток)

Например,  покажет, каких коммитов из ветки test нет в master (о ветках поговорим чуть позже).

показывает коммиты, которые есть либо в ветке 1, либо в ветке 2, но не в обеих; знак обозначает коммиты из , а — из .Обратите внимание: используется три точки, а не две;

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

позволяет ограничить эту команду заданными строками. Это можно использовать, например, для выяснения того, какой коммит привёл к определённому багу (чтобы можно было его откатить)

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

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

Примечание Не путайте с ! Первый ищет по файлам среди коммитов, а последний смотрит на сообщения логов.

Сопровождающие исходного проекта могут пушить в форк (LIBRE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD)

Распределение рабочего процесса по форкам — частое явление в проектах с открытым исходным кодом, таких как GitLab; при этом мерж-реквесты отправляются из форка в исходный проект.

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

Ранее это было невозможно, поскольку сопровождающие исходного проекта не получали автоматических разрешений на запись в форки. Теперь же, если у автора мерж-реквеста есть доступ на запись в исходную ветку, он может выдать сопровождающим право на запись в ветку мерж-реквеста, выбрав Allow edits from maintainers на его странице. После подключения этой опции пользователи с разрешением на мерж ветки исходного проекта смогут проводить пуш в ветку мерж-реквеста. По умолчанию эта опция отключена.

Исходная позиция: что имеется и чего хочется

Имеем:

репозиторий в GitLab.

Хотим:

  • автоматическую сборку и тестирование для каждого merge request,
  • сборку пакетов для каждого merge request и пуша в мастер при условии наличия в сообщении коммита определённой строки,
  • отправку собранных пакетов в приватный фид в Azure DevOps,
  • сборку документации и публикацию в GitLab Pages,
  • бейджики!11

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

  • Этап 1 — сборка
  • Этап 2 — тестирование
  • Этап 3 — отправка
    • Задача 1 — собираем nuget-пакет и отправляем в Azure DevOps
    • Задача 2 — собираем сайт из xmldoc в исходном коде и публикуем в GitLab Pages

Приступим!

Работа в большой команде

Со временем ваш сайт стал очень популярным, а ваша команда выросла с двух до восьми человек. Разработка происходит параллельно, и людям все чаще приходится ждать в очереди для превью на Staging. Подход “Проводите развертывание каждой ветки на Staging” больше не работает.

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

Для добавления этой функциональности нужно внести лишь небольшие изменения в файл :

становится

Разработчики проводят мерж своих feature-веток перед превью на Staging

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

Непредвиденные обстоятельства

Невозможно все контролировать, и неприятности имеют свойство случаться. К примеру, кто-то неправильно смержил ветки и запушил результат прямо в production как раз когда ваш сайт находился в топе HackerNews. В результате тысячи человек увидели кривую версию сайта вместо вашей шикарной главной страницы.

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

Rollback перезапускает более раннюю задачу, порожденную в прошлом каким-то другим коммитом

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

Для того, чтобы запустить развертывание вручную, перейдите на вкладку Pipelines > Builds и нажмите на вот эту кнопку:

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

Ревью приложений

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

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

А так будет выглядеть код, замещающий задачу :

Стоит объяснить откуда у нас появилась переменная — из списка , которые вы можете использовать для любой своей задачи.

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

Визуальная интерпретация такой конфигурации:

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

Реальные проекты, как правило, значительно сложнее, чем наш пример с сайтом на статическом HTML. К примеру, поскольку инстансы временные, это сильно усложняет их автоматическую загрузку со всеми требуемыми сервисами и софтом “на лету”. Однако это выполнимо, особенно, если вы используете Docker или хотя бы Chef или Ansible.

Про развертывание при помощи Docker будет рассказано в другой статье. Честно говоря, я чувствую себя немного виноватым за то, что упростил процесс развартывания до простого копирования HTML-файлов, совершенно упуская более хардкорные сценарии. Если вам это интересно, рекомендую почитать статью «Building an Elixir Release into a Docker image using GitLab CI».

А пока что давайте обсудим еще одну, последнюю проблему.

Развертывание на различные платформы

В реальности мы не ограничены S3 и GitLab Pages; приложения разворачиваются на различные сервисы.

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

В приведенных в этой статье примерах мы использовали в качестве инструмента для доставки кода на сервис Amazon S3

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

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

Задача для развертывания в production с использованием dpl будет выглядеть вот так:

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

Не меняйте порядок коммитов с помощью rebase

Git позволяет вам сделать ребейз feature-ветки на , в результате чего коммиты этой ветки оказываются в истории после коммитов в . Это позволяет сделать мерж без мерж-коммита и в результате у вас получается простая линейная история. Но здесь действует то же правило, что и с объединением коммитов: не трогайте то, что уже попало в удалённый репозиторий. Мы рекомендуем не ребейзить даже промежуточные результаты вашей работы, отданные на ревью через мерж-реквест.

Использование вынуждает вас многократно разрешать одни и те же конфликты. В некоторых случах это можно сделать командой (reuse recorded resolutions). Но ещё проще — вовсе не ребейзить и разрешать конфликты всего один раз, при мерже. Чем с меньшим количеством мерж-конфликтов вы сталкиваетесь — тем лучше.

Чтобы избежать лишних конфликтов, нужно не слишком часто мержить в feature-ветки. Давайте разберём три возможных причины мержа куда-либо ещё: «подтягивание кода» (leveraging code), мерж-конфликты и долгоживущие ветки.

Если вам нужно «подтянуть» изменения из в feature-ветку — обычно можно обойтись вытаскиванием (cherry-pick) одного нужного коммита.

Конфликт при мерже feature-ветки обычно разрешается с помощью создания мерж-коммита. Если строки вашего файла могут находиться в произвольном порядке, то можно избежать некоторых конфликтов с помощью настройки gitattributes. Например, в файле репозитория GitLab есть строка , и это позволяет мержить список изменений автоматически.

Последняя ситуация, когда необходимо мержить куда-то ещё — это использование долгоживущих веток, которые периодически нужно обновлять до актуального состояния. Мартин Фаулер в своей статье о feature-ветках рассуждает о практике непрерывной интеграции (continuous integration, CI). Мы в GitLab немного путаем CI с тестированием веток.

Цитируя Фаулера: «Я знаю людей, которые утверждают, что практикуют CI, потому что выполняют сборку каждой ветки и каждого коммита, и даже могут при этом использовать CI-сервер. То, что они делают, называется непрерывной сборкой (continuous building). Это тоже благородное дело, но интеграции-то нет, а значит, нет и «непрерывной интеграции».»

Решение заключается в том, что feature-ветки должны существовать недолго и быстро мержиться. Можно ориентироваться на срок в один рабочий день. Если разработчик держит ветку для реализации задачи более одного дня, подумайте о том, чтобы раздробить задачу на более мелкие части. В качестве альтернативы можно использовать «переключатели фич» (feature toggles).

Для работы с долгоживущими ветками есть две стратегии:

  • Стратегия непрерывной интеграции предполагает, что вы мержите в долгоживущую ветку в начале каждого дня,
    чтобы предотвратить более сложные мержи в будущем.
  • Стратегия «точки синхронизации» (synchronization point strategy) разрешает мержить только строго определённые коммиты,
    например отмеченые тегом релизы. Линус Торвальдс рекомендует именно такой способ, потому что код релизных версий лучше изучен.

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

Перед принятием мерж-реквеста выберите опцию .

GitLab попытается сделать rebase перед мержем. Если rebase без конфликтов невозможен, будет выполнен обычный мерж.

В заключение хотелось бы сказать следующее: старайтесь делать меньше мерж-коммитов, но не исключайте их вовсе. Ваш код должен быть чистым, но его история должна быть достоверной. Разработка ПО происходит небольшими и не всегда красивыми шагами. То, что они сохранятся в истории кода — нормально. А ребейз делает историю недостоверной, после чего никакие инструменты не покажут вам действительную историю, потому что они не могут узнать идентификаторы коммитов, которые были до ребейза.

CI/CD изнутри

Процесс непрерывной интеграции/внедрения описан в файле .gitlab-ci.yml в корне репозитория, он состоит из 4 стадий: загрузка зависимостей, phpunit-тестирование, сборка, развёртывание.

Загрузка зависимостей

На данном этапе производится попытка установить все зависимости приложения через .

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

PHPUnit-тестирование

Перед запуском собственно , создаются переменные окружения для работы приложения Symfony. Если какие-то значения переменных в testing-окружении должны отличаться значений во всех остальных окружениях — нужно создать такие переменные в настройках репозитория GitLab с суффиксом (например, ). Тогда её значение перекроет дефолтное из файла .

Сборка

Здесь сборка для php-проекта — это создание docker-образов для контейнеров nginx и php, и выкладывание подготовленных образов в GitLab Container Registry.

Здесь, задача создаёт образы PHP-приложения для staging-сайта (и для тестового сайта разработчика); а задача — для production-сайта. Для каждой задачи значения переменных из настроек pipeline с суффиксом или перекрывают дефолтные значения из файла . Аналогичным образом описаны задачи по сборке образов (см. задачи и ).

Также на этом этапе создаётся файл , который на следующем этапе будет скопирован на удалённый сервер (см. задачи и ). Сформированный файл содержит все переменные окружения, необходимые для запуска приложения. В секции все контейнеры будут создаваться только на основе готовых образов docker.

Развёртывание

На данном этапе docker-образы приложения готовы и загружены в Container Registry. Осталось обновить приложения.

На удалённых серверах ceрвис отсутствует; дополнительно к сервису добавлен абсолютно такой же сервис ; а в конфигурации вместо одного сервера в прописано два. Использование двух одинаковых сервисов позволило добиться практически нулевого deployment downtime.

Алгоритм развёртывания следующий:

  • Копируем сформированный на этапе файл
  • Загружаем новые образы из Container Registry
  • Обновляем контейнер
  • Обновляем статичные файлы для , производим миграцию БД
  • Обновляем контейнер
  • Обновляем контейнеры и (в боевых условиях — это не обязательно)

Во время обновления контейнеров или , через несколько секунд недоступности одного из них переключается на следующий доступный в . Т.е. приложение работает правильно для 100% HTTP-запросов, но иногда с задержкой.

Во время выполнения миграции БД первая половина HTTP-запросов идёт в контейнер , который может работать со старой структурой БД, а вторая половина — в контейнер , который может работать с только с новой структурой. Т.е. в обоих контейнерах возможны сбои в работе во время миграции БД. Но если допустить, что внесение изменений в структуру БД не такое уж и частое явление, то можно считать это вполне приемлемым.

Во время обновления контейнеров и , сайт недоступен вообще. Эти сервисы обновляются очень редко, обновление вообще можно производить вручную «ночью». Проверка возможности обновлений для этих контейнеров длится около 5 секунд, что примерно 80-90% от всего deployment downtime.

Файл .gitlab-ci.yml

В каждом репозитории GitLab CI ищет файл .gitlab-ci.yml, чтобы понять, как тестировать код. В импортированном репозитории уже есть этот файл.

Кликните по файлу .gitlab-ci.yml в интерфейсе GitLab. Файл должен содержать такой код:

Файл использует синтаксис YAML конфигурации GitLab CI для определения действий, порядка, условий и ресурсов, необходимых для выполнения каждой задачи. При написании собственных файлов GitLab CI вы можете использовать /ci/lint в интерфейсе GitLab, чтобы проверить формат файла.

Конфигурационный файл начинается с объявления образа Docker, который нужно использовать для запуска тестов. Поскольку Hapi – это фреймворк Node.js, Docker будет использовать последний образ Node.js:

Далее определяются этапы непрерывной интеграции:

Вы можете назвать этапы интеграции как угодно, но порядок менять нельзя: иначе вы измените порядок выполнения последующих этапов. Этапы – это теги, которые можно применять к отдельным заданиям. GitLab будет параллельно запускать задания одного и того же этапа и ждать, пока все задания на текущем этапе не будут завершены. Если в файле этапы не определены, GitLab будет использовать три стандартных этапа — build, test и deploy, — и по умолчанию присваивать все задания этапу test.

После этапов в конфигурации находится определение кэша:

Здесь указаны файлы или каталоги, которые можно кэшировать между прогонами или этапами. Это может уменьшить время, затрачиваемое на выполнение заданий. В данном случае кэшируется каталог node_modules, в котором npm будет устанавливать зависимости.

Первая задача (job) называется install_dependencies:

Задачи можно называть как угодно, но поскольку имена будут использоваться в интерфейсе GitLab, лучше выбирать описательные имена. Обычно npm install можно комбинировать с другими этапами тестирования, но в руководстве он выделе в отдельный этап, чтобы продемонстрировать взаимодействие между этапами.

Директива stage содержит метку этапа build. Затем нужно указать команды в директиве script. Чтобы указать несколько команд, добавьте в раздел script новые строки.

Подраздел artifacts указывает пути к файлам или каталогам, которые нужно сохранять между этапами. Поскольку команда npm install устанавливает зависимости проекта, следующей задаче нужен доступ к загруженным файлам. Путь node_modules предоставляет такой доступ. Также файлы можно просмотреть или загрузить в пользовательском интерфейсе GitLab после тестирования. Если вы хотите сохранить все, что было создано на определенном этапе, замените весь раздел paths строкой:

Вторая задача называется test_with_lab и объявляет задачу, которая запустит тест.

Она присваивается этапу test. Этот этап имеет доступ ко всем файлам, сгенерированным на этапе build (в данном случае это зависимости проекта). Раздел script представлен однострочным синтаксисом YAML, который можно использовать, когда имеется только один элемент.

Панель управления безопасностью в рамках группы

(ULTIMATE, GOLD)

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

В релизе GitLab 11.5 мы представляем первую версию новой панели управления безопасностью, доступной на уровне группы. Она собирает в одном месте выявленные SAST уязвимости по всем проектам данной группы и список доступных действий для их исправления. Например, вы можете создать задачу с предлагаемым решением, или просто скрыть уведомление, если считаете, что это ошибочное срабатывание. В будущих релизах будет добавлена поддержка других тестов — Dependency Scanning, Container Scanning, DAST.

Обратите внимание, что панель безопасности группы требует использования нового синтаксиса для отчетов и версию GitLab Runner 11.5 или выше для отображения результатов. Поддержка Auto DevOps будет добавлена в следующем релизе

Документация по новой панели безопасности и оригинальный тикет.

Продвинутое использование: правим историю

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

Вы можете поменять порядок коммитов, изменив порядок, в котором они перечислены.

Измененяем сообщение коммита/разбиваем коммиты

Для указания коммита, который вы хотите изменить, используется команда . Затем, когда Git будет проводить перемещение, он остановится на этом коммите. После этого вы можете использовать , чтобы изменить сообщение или подготовить забытые файлы. Если вы хотите разделить коммит, после остановки введите (в результате HEAD будет перемещён на один коммит назад и все изменённые в этом коммите файлы перейдут в статус неподготовленных). Затем вы сможете зафиксировать файлы в отдельных коммитах обычным образом.

После завершения редактирования введите .

Перезапись нескольких коммитов

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

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

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

Переносим отдельный коммит

Кроме слияния/перемещения всех коммитов в тематической ветке, вас может интересовать только определённый коммит. Допустим, у вас есть локальная ветка drafts, где вы работаете над несколькими потенциальными статьями, но хотите опубликовать только одну из них. Для этого можно использовать команду . Чтобы получить определённые коммиты, из которых мы хотим выбирать, можно использовать .

Обратите внимание, что таким образом создаётся новый коммит, который только повторяет diff выбранного коммита (то есть разницу между этим коммитом и предыдущим), но не его состояние

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector