Настройка целевых ветвей для pull request

Службы Azure DevOps

По умолчанию Azure DevOps предлагает создание новых pull-реквестов в отношении ветви по умолчанию. В репозитории с несколькими ветками, используемыми для pull-запросов, владельцы репозитория могут настроить список целевых веток для pull-запросов, чтобы эти рекомендации выбирали соответствующую целевую ветку.

Чтобы включить эту функцию, создайте файл с именем .azuredevops/pull_request_targets.yml в ветвь по умолчанию репозитория. Этот файл YAML должен содержать один список, озаглавленный pull_request_targets, содержащий имена веток или префиксы, соответствующие кандидатским веткам.

Например, рассмотрим следующее содержимое:

pull_request_targets:
  - main
  - release/*
  - feature/*

Этот список потенциальных целевых объектов указывает main как первую ветвь для выбора, но если более подходящей является ветвь, начинающаяся с release/ или feature/, то выбирается эта ветвь.

Для получения дополнительных рекомендаций по запросам на вытягивание и управлению ими см. раздел "Сведения о запросах на вытягивание".

Предпосылки

Категория Требования
доступ к проекту Член проекта .
Разрешения — Просмотр кода в частных проектах: по крайней мере базовый доступ.
— Клонирование или участие в работе с кодом в частных проектах: член группы безопасности Contributors или наличие соответствующих разрешений в проекте.
— Задайте разрешения ветви или репозитория: управление разрешениями для ветви или репозитория.
— Чтобы задать политики ветви, проверки состояния или изменить ветвь по умолчанию, требуется разрешение для репозитория или ветви Изменение политик либо членство в группе безопасности Project Administrators.
— Импорт репозитория: член группы безопасности администраторов проектов или уровня проекта Git разрешения на Разрешить. Дополнительные сведения см. в разделе "Настройка разрешений репозитория Git".
Services Repos включено.
Инструменты Необязательно. Используйте az repos команды: Azure DevOps CLI.
Категория Требования
доступ к проекту Член проекта .
Разрешения — Просмотр кода: доступ уровня Basic хотя бы .
— Клонирование или работа с кодом: член группы безопасности участников или имеющий соответствующие разрешения в проекте.
Services Repos включено.

Когда используется эта конфигурация?

Существует несколько точек входа для использования динамической целевой ветви.

  • Предложения по pull-реквесту. Когда пользователь отправляет ветвь в Azure DevOps, его следующий визит на страницу Repos может предложить создать pull request из этой ветви. Эта кнопка "Создать pull request" динамически выбирает целевую ветвь.

  • URL-адрес запроса на вытягивание. Когда пользователь переходит непосредственно на страницу создания запроса на вытягивание с помощью sourceRef параметра, но пропускает targetRef параметр, Azure DevOps выбирает целевую ветвь на основе этого динамического выбора.

  • Раскрывающийся список веток. Когда пользователь открывает выпадающий список веток в Azure DevOps, ветки, указанные в целях пулл-реквестов, будут отображаться в разделе "Цели" между разделами "Мои" и "Все".

    Снимок экрана: раскрывающийся список ветви с разделом

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

Какие хорошие кандидаты для целевых объектов филиалов?

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

Как Azure DevOps выбирает ветвь?

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

Среди возможных целевых ветвей Azure DevOps выбирает ту ветвь, чья история первой родительской ветви наиболее пересекается с историей первой родительской ветви исходной ветви.

Пример: Нет фиксаций объединения

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

  ,-E---F <-- release/2024-September
 /
A---B---C---D <--- main
     \
      `-G---H <--- feature/targets
         \
          `-I <--- topic

С этой историей и примером pull_request_targets списка, используемого ранее, у нас есть три целевых ветви кандидата в порядке приоритета:

  • main
  • release/2024-September
  • feature/targets

Исходная ветвь topic, затем сравнивается с этими ветвями.

  • main пересекается с topic в B, оставляя G,I в topic и не в main.
  • release/2024-September пересекается с topic на A, оставляя B,G,I в topic, а не в release/2024-September.
  • feature/targets пересекается с topic в G, оставляя I в topic и не в feature/targets.

Таким образом, в этом примере ветвь feature/targets выбирается в качестве целевой ветви для pull request, а ветвь topic используется в качестве исходной.

Пример: коммиты слияния

В более сложном примере, где ветвь feature/targets была объединена в main, а затем main был объединен сам с собой, журнал фиксации содержит больше случаев, которые нужно учитывать.

  ,-E---F <-- release/2024-September
 /
A---B---C---D---J---K <--- main
     \    _/     \
      \  /        \
       `G---H---L--\--M <--- feature/targets
         \          \/
          \
           `I <--- topic

Здесь коммит D в main представляет момент, когда feature/targets был объединен с main. Фиксация M означает момент, когда main был объединен с feature/targets. Связь между фиксациями M и J рисуется таким образом, чтобы подчеркнуть, что J является вторым родителем M, в то время как L является первым родителем.

В этом случае, если рассмотреть полную историю коммитов, main и feature/targets обе пересекаются с историей topic в G. Однако первая родительская история по-прежнему демонстрирует предпочтение feature/targets.

Разрыв связей

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

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

          ,-E---F <-- release/2024-October
         /
A---B---C---D <--- main
     \
      \
       `G <--- topic

В этом примере ветвь release/2024-October была создана от ветви main после того, как topic была выделена от main. Хотя это интуитивно понятно для человека, порядок категорий main и release/* в списке pull_request_targets указывает предпочтительный порядок для Azure DevOps.

Что делать, если Azure DevOps выбирает неправильную целевую ветвь?

Страница создания pull request содержит селектор для изменения целевой ветви, если динамический выбор не соответствует ожиданиям. Целевая ветвь также может быть изменена после создания пулл-реквеста.

Более важно понять, почему эвристика может выбирать "неправильную" целевую ветвь.

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

  • Целевые ветви не защищены политиками пул-реквеста. Если целевые ветви можно произвольно перенести, то история первого родителя не является надежным индикатором предыдущего местоположения этой ветви.

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

  • Исходная ветвь была обновлена с помощью команд git commit и git merge. Такие команды, как git reset --hard или git rebase, могут изменять историю ветви непредсказуемыми способами.

Если вы не согласны с целевой ветвью, выбранной этой эвристической, рассмотрите возможность обновления выбранного варианта.git rebase --onto <new-target> <old-target> <source> Команда git rebase перезаписывает историю первого родителя, чтобы заставить эвристику выбрать новую цель.

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

Как протестировать это решение локально?

Эвристика, используемая Azure DevOps, была включена в основной клиент Git и доступна в Git версии 2.47.0 и более поздних версий.

Чтобы проверить эту логику в собственном репозитории, сначала запустите git fetch origin , чтобы убедиться, что у вас установлена последняя версия целевых ветвей. Затем выполните следующую git for-each-ref команду, подстроив её под ваш список ветвей-кандидатов.

$ git for-each-ref --format="%(is-base:HEAD) %(refname)" \
           refs/remotes/origin/main \
           "refs/remotes/origin/release/*" \
           "refs/remotes/origin/feature/*"
 refs/remotes/origin/main
 refs/remotes/origin/release/2024-September
(HEAD) refs/remotes/origin/feature/targets

В этой команде фиксация HEAD используется в качестве источника и сравнивает первую родительскую историю целевых ветвей так же. Хотя каждая кандидатная ветвь указана в выходных данных, строка (HEAD) указывает, какая ветвь должна использоваться как целевая ветвь.

Следующие шаги