Eustáquio Rangel

Desenvolvedor, pai, metalhead, ciclista

Pull requests em modo raiz

Publicado em Developer


Nas últimas semanas está havendo bastante discussão sobre merging de pull requests, mas em relação ao nível das pessoas envolvidas (jr, sênior, etc), construções das linguagens utilizadas (if, else, for, etc). Mas já pensaram no que basicamente é um pull request?

No Git em si mesmo, não existe o pull request! Esse termo foi criado em cima de recursos de sites como o Github, Gitlab e Bitbucket para permitir que as pessoas possam enviar as suas alterações no código, mesmo sem ter acesso de escrita ao repositório original, de forma com que outra pessoa possa avaliar, aceitar ou rejeitar as alterações propostas, geralmente através de forma visual, cada um implementando do jeito que achar melhor a interface.

Um problema que vejo nesse caso é a dependência que esse tipo de ferramenta cria. Será que conseguiríamos fazer essas mesmas operações que o o pull request faz, sem algum dos sites/produtos envolvidos ali? Git é diferente de Github. Será que uma pessoa que desenvolve software conhece os caminhos alternativos para não ficar preso à um determinado produto? Convenhamos, todos os citados ali são produtos, imaginem - ok, é meio difícil - se somem todos. E a boa notícia é que dá sim para fazer algo similar de forma bem mais básica.

Vamos fazer uma pequena e bem simples demonstração. Imaginemos que temos o repositório repo1 onde a pessoa A mantém o seu código. Temos a pessoa B, que tem acesso de leitura ao repo1. A pessoa B clona o repositório para um diretório chamado repo2. Dentro de repositório, só temos um arquivo fonte, test.rb (Ruby, tinha que ser), com uma classe chamada Test:

Aí a pessoa B altera esse código, criando um método construtor na classe e faz um novo commit com a mensagem "Construtor":

E agora a pessoa B quer enviar suas alterações para a pessoa A. Ela poderia fazer um pull request baseado no seu fork direto pela ferramenta da sua escolha, geralmente através da interface gráfica. Mas vamos nos lembrar que não vamos utilizar essas ferramentas, então como fazer?

A pessoa B pode fazer um patch do que foi alterado e enviar para a pessoa A. Levando em conta que vamos considerar que as alterações estão no último commit feito pela pessoa B (poderia estar em quantos forem necessários), podemos fazer:

$ git format-patch -o /tmp HEAD^
/tmp/0001-Construtor.patch

Ali temos como saída o diretório /tmp e especificamos o último commit com HEAD^ (ei, eu falo sobre isso no meu livro de Git) e como podemos ver, foi criado o arquivo /tmp/0001-Construtor.patch. Vamos dar uma olhada nele:

Podemos reparar em 2 coisas interessantes: as 4 primeiras linhas tem um formato bem conhecido para cabeçalhos de e-mails. As outras é que existem linhas com marcações verdes e vermelhas parecidas com essas:

2023-12-21-16-23

A imagem acima é de um pull request.

O interessante do arquivo de patch (que é uma coisa muito antiga em desenvolvimento de software!) criado é que ele tem todas as instruções para alterar o repositório destino igual o repositório local e podemos abrir o arquivo e conferir facilmente e na hora as alterações com um editor de texto com uma boa sintaxe colorida. Para pegar essa imagem acima, eu tive que entrar no repósito e dar pelo menos uns 4 cliques e esperar o conteúdo carregar. Ok, nada de desesperador, mas pegaram a idéia. Agora que temos o arquivo do patch, como enviar para a pessoa A? Vamos convencionar que é por e-mail, um padrão que utilizamos quase todos dias e está aí faz tempo, para ficar. Tem alguns jeitos de automatizar isso, mas aqui vai o jeito cru de fazer a coisa:

git send-email --smtp-encryption=tls --smtp-server=smtp.gmail.com --smtp-user=eustaquiorangel@gmail.com --smtp-server-port=587 --to eustaquiorangel@gmail.com /tmp/0001-Construtor.patch

Lógico que se quisermos copiar as linhas logo abaixo do cabeçalho do patch e enviar direto compondo um e-mail na ferramenta da sua escolha, não tem problema algum. O e-mail vai chegar tipo assim para a pessoa A:

2023-12-21-16-36

A pessoa A nesse ponto pode salvar o conteúdo do texto do e-mail em um arquivo, ou até pedir para ver o texto original do e-mail (clicando em "Mostrar original", no caso do Gmail) e salvar, digamos, em um arquivo chamado pessoa_b.patch, cujo conteúdo será similar ao conteúdo de /tmp/0001-Construtor.patch que vimos acima, apenas com mais alguns cabeçalhos específicos do e-mail. Como já pudemos inspecionar as alterações sugeridas, se tudo estiver ok, podemos aplicar o patch completo em repo1 com o seguinte comando:

$ git am /tmp/pessoa_b.patch
Applying: Construtor

E agora temos todos os arquivos de repo1 (no caso, somente 1) refletindo os de repo2. E se por acaso quisermos inspecionar arquivo por arquivo? Sem problema. Vamos pegar um bom editor de texto, como Vim/Neovim ou qualquer outro da preferência que tenha suporte bom para patches e podemos, de posse do arquivo de patch, especificando as opções set diffopt=internal,filler,closeoff,vertical (nem precisa, só coloquei para abrir sempre na vertical), abrir test.rb e digitar o seguinte comando:

:diffpatch /tmp/pessoa_b.patch

O que vai produzir algo como:

2023-12-21-16-51

Ficando muito fácil conferir as diferenças. Se estiver utilizando um plugin como o vim-fugitive, fica muito bom de ir até fazendo cherry picking nas diferenças, indo para a janela do código enviado, navegando para a próxima com ]c e aplicando, se assim desejado, com dp. Para quem interessar no plugin, aqui tem um Vimcast sobre como resolver conflitos com ele.

E para dar um pouco mais de fé nos "pull requests raiz": esse é o fluxo em que o kernel do Linux, que é um projeto gigante, é desenvolvido, por gente muito competente. Alguns links sobre isso:

Enfim, nada contra utilizar as ferramentas (sim, eu utilizo o Github) que no final são produtos, mas importante lembrar e aprender as bases da coisa toda para não ficar na mão ou até simplificar (porque não?) o seu fluxo de trabalho.




0 comentário - Comente esse artigo!

Artigos anteriores