Configurar ramificações de destino para solicitações de pull

Serviços do Azure DevOps

Por padrão, o Azure DevOps sugere a criação de novas solicitações de pull contra o ramificação padrão. Em um repositório com várias ramificações usadas para solicitações de pull, os proprietários do repositório podem configurar a lista de ramificações de destino da solicitação de pull para que essas sugestões selecionem a ramificação de destino adequada.

Para habilitar esse recurso, crie um arquivo com nome .azuredevops/pull_request_targets.yml no branch padrão do repositório. Esse arquivo YAML deve conter uma única lista, intitulada pull_request_targets, contendo os nomes ou prefixos de ramificação que correspondem às ramificações candidatas.

Por exemplo, considere estes conteúdos:

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

Essa lista de destinos potenciais especifica main como o branch de destino a ser selecionado primeiro, mas se um branch começando com release/ ou feature/ for uma opção melhor, esse branch será escolhido.

Para obter mais diretrizes de solicitação de pull e considerações de gerenciamento, consulte Sobre solicitações de pull.

Pré-requisitos

Categoria Requirements
Acesso ao projeto Membro de um projeto.
Permissões - Exibir código em projetos privados: pelo menos acesso básico .
- Clonar ou contribuir para o código em projetos privados: membro do grupo de segurança Colaboradores ou permissões correspondentes no projeto.
- Definir permissões de branch ou repositório: gerenciar permissões para o branch ou repositório.
- Defina políticas de ramificação, verificações de status ou altere a ramificação padrão: permissão Editar políticas para o repositório ou a ramificação, ou associação ao grupo de segurança Administradores do Projeto.
- Importar um repositório: membro do grupo de segurança Administradores do Projeto ou da permissão Criar repositório no nível do projeto do Git definida como Permitir. Para obter mais informações, consulte Definir permissões de repositório Git.
Serviços Repositórios habilitados.
Tools Optional. Use os comandos az repos: CLI do Azure DevOps.
Categoria Requirements
Acesso ao projeto Membro de um projeto.
Permissões - Exibir código: no mínimo acesso básico.
- Clonar ou contribuir com o código: membro do grupo de segurança Colaboradores ou permissões correspondentes no projeto.
Serviços Repositórios habilitados.

Quando essa configuração é usada?

Há vários pontos de entrada para usar uma ramificação de destino dinâmica.

  • Sugestões de solicitação de pull. Quando um usuário faz push de um ramificação para o Azure DevOps, sua próxima visita à página do Repositório pode sugerir a criação de uma solicitação de pull a partir desse ramificação. Este botão "Criar nova solicitação de pull" escolhe o branch de destino dinamicamente.

  • URL da solicitação de pull. Quando um usuário navega diretamente para a página de criação de solicitação de pull usando um sourceRef parâmetro, mas omitindo o targetRef parâmetro, o Azure DevOps seleciona um branch de destino com base nessa escolha dinâmica.

  • Menu suspenso de ramificações. Quando um usuário abre o menu suspenso de ramificações no Azure DevOps, as ramificações especificadas nos destinos da solicitação de pull aparecerão em uma seção chamada "Destinos", entre as seções "Minhas" e "Todas".

    Captura de tela dos menus suspensos de ramificações com a seção Destinos.

Há uma capacidade para as ferramentas de cliente criarem solicitações de pull usando essa opção dinâmica, mas esses clientes precisam adicionar um sinal opcional de que o usuário não especificou um branch de destino. Verifique a ferramenta do cliente de sua escolha para ver se a opção está ativada.

Quais são os bons candidatos para destinos de ramificação?

Recomendamos que a lista configurada de ramificações candidatas inclua apenas ramificações protegidas por políticas de pull request. Essas ramificações provavelmente só são alterados com a conclusão de solicitações de pull, o que garante que a posição anterior do ramificação esteja na história do primeiro pai do commit de ponta. Se uma estratégia de mesclagem for usada, o segundo pai representará os commits que estão sendo introduzidos no ramificação de destino concluindo uma solicitação de pull e o primeiro pai será a dica anterior.

Como o Azure DevOps escolhe um branch?

O Git não rastreia metadados em torno da criação de uma ramificação. Não há uma maneira exata de determinar qual ramificação foi usado ao criar um ramificação de tópico. Em vez disso, o Azure DevOps usa uma heurística baseada no histórico do primeiro pai das ramificações.

Entre os possíveis ramificações de destino, o Azure DevOps seleciona o ramificação cujo histórico do primeiro pai se cruza mais com o histórico do primeiro pai do ramificação de origem.

Exemplo: sem commits de mesclagem

Considere a seguinte estrutura do ramificação, que é simplificada mais do que o normal, pois não há commits de mesclagem. Neste exemplo, todo o histórico é representado pelo histórico do primeiro pai.

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

Com esse histórico e a lista de amostras pull_request_targets usada anteriormente, temos três ramificações-alvo candidatas, em ordem de prioridade:

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

O ramo de origem, topic, é então comparado a esses ramos.

  • main cruza com topic at B, deixando G,I em topic e não em main.
  • release/2024-September cruza com topic em A deixando B,G,I em topic e não em release/2024-September.
  • feature/targets cruza com topic at G, deixando I em topic e não em feature/targets.

Portanto, neste exemplo, o feature/targets branch é escolhido como o branch de destino para uma solicitação de pull com topic o branch de origem.

Exemplo: commits de mesclagem

Em um exemplo mais complicado, onde o ramificação feature/targets foi mesclado em main e main foi mesclada em si mesma, o histórico de commits tem mais casos a serem considerados:

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

Aqui, o commit D em main representa um momento em que feature/targets foi mesclado em main. Commit M representa um momento em que main foi mesclado em feature/targets. A ligação entre commits M e J é desenhada de forma a enfatizar que J é o segundo pai de M enquanto L é o primeiro pai.

Nesse caso, quando você considera o histórico completo de commit, main e feature/targets ambos cruzam o histórico de topic em G. No entanto, o primeiro histórico pai ainda demonstra uma preferência por feature/targets.

Rompendo laços

Se duas ramificações tiverem a mesma interseção no histórico do primeiro pai, o Azure DevOps selecionará a ramificação que aparece primeiro na lista pull_request_targets. Se vários ramificações ainda estiverem empatados com base na lista pull_request_targets devido a uma correspondência de prefixo, a primeira em ordem alfabética vencerá.

Esses tipos de vínculos estão presentes com mais frequência quando novos ramificações de candidatos são criados, como o início de um novo ramificação de funcionalidade ou a bifurcação de um ramificação de lançamento.

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

Neste exemplo, a ramificação release/2024-October foi criada a partir da ramificação main depois que topic foi ramificada a partir de main. Embora isso seja intuitivo para um leitor humano, a ordem das categorias main e release/* na lista pull_request_targets indica a ordem preferencial no Azure DevOps.

E se o Azure DevOps escolher o branch de destino errado?

A página de criação de pull request possui um seletor para ajustar o ramo de destino se a escolha dinâmica não atender às expectativas. A ramificação de destino também pode ser ajustada após a criação da solicitação de pull.

Mais importante, pode ser valioso entender por que a heurística pode estar selecionando o ramo-alvo "errado".

Essa heurística se baseia em algumas suposições sobre como as ramificações de destino e a ramificação de origem foram criadas. Aqui estão algumas razões potenciais pelas quais a heurística não funciona:

  • Os ramificações de destino não são protegidos por políticas de solicitação de pull. Se os ramificações de destino puderem ser enviados arbitrariamente, o histórico do primeiro pai não será um indicador confiável do local anterior desse ramificação.

  • O ramificação de origem foi criado a partir de uma dica anterior de um ramificação candidato. Se o ramificação de origem escolheu um commit arbitrário no histórico, não há garantia sobre o histórico do primeiro pai do qual ele dependeu.

  • A ramificação de origem foi avançada usando os comandos git commit e git merge. Comandos como git reset --hard ou git rebase podem alterar o histórico da ramificação de maneiras imprevisíveis.

Se você discordar do branch de destino escolhido por essa heurística, considere atualizar a opção usando git rebase --onto <new-target> <old-target> <source>. O git rebase comando reescreve o histórico do primeiro pai para fazer com que a heurística escolha o novo destino.

Um erro comum que os usuários cometem ao perceber que estão baseados no ramo errado é usar git merge para trazer o ramo certo para seu histórico. A mesclagem não altera o histórico do primeiro pai e, portanto, não altera a escolha do ramificação de destino.

Como posso testar essa decisão localmente?

A heurística usada pelo Azure DevOps foi contribuída para o cliente Git principal e está disponível nas versões 2.47.0 e posteriores do Git.

Para testar essa lógica em seu próprio repositório, primeiro execute git fetch origin para assegurar que você possua a versão mais recente das ramificações de destino. Em seguida, execute o seguinte git for-each-ref comando, ajustado para corresponder à sua lista de ramificações candidatas:

$ 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

Nesse comando, o commit HEAD é usado como origem e compara o histórico do primeiro pai dos ramificações de destino da mesma forma. Embora cada ramificação candidata seja listada na saída, a string (HEAD) indica qual das ramificações deve ser usada como ramificação de destino.

Próximas etapas