Git
Chapters ▾ 2nd Edition

6.2 GitHub - Contribuindo em um projeto

Contribuindo em um projeto

Agora que a sua conta está configurada, vamos analisar alguns detalhes que podem te ajudar a contribuir em um projeto existente.

Fazendo Fork de projetos

Se você deseja contribuir em um projeto existente no qual você ainda não tem acesso de push, você pode dar um “fork” no projeto. Quando você faz “fork” de um projeto, o GitHub faz uma cópia do projeto que é inteiramente sua; ele vive no seu namespace e você pode dar push nela.

Note

Historicamente, o termo “fork” tinha uma conotação negativa, significando que alguém levou um projeto open source para uma direção diferente, às vezes criando um projeto competidor e dividindo os contribuintes. No GitHub, um “fork” é simplesmente o mesmo projeto no seu namespace, permitindo que você faça alterações publicamente em um projeto como uma forma mais aberta de contribuir.

Dessa forma, projetos não precisam se preocupar em adicionar colaboradores para dar acesso ao push. Qualquer um pode dar fork no projeto, fazer push e mandar suas alterações de volta para o repositório original criando o chamado Pull Request, o qual falaremos a seguir. Isso abre uma thread de disscusão para revisar o código e para que o proprietário e o contribuinte possam se comunicar sobre as mudanças até que o proprietário esteja feliz com isso, nesse instante o proprietário pode fazer merge do código.

Para fazer fork de um projeto, visite a página do projeto e clique no botão “Fork” na parte superior direita do site.

The ``Fork'' button.
Figure 89. O botão de “Fork”

Depois de alguns segundos, você vai para a página do seu novo projeto, com sua própria cópia alterável do código.

O fluxo do GitHub

O GitHub é desenhado ao redor de uma fluxo particular de colaboção, centrado nas Pull Requests. Este fluxo funciona se você está colaborando com um time integrado em uma único repositório compartilhado, com uma empresa distribuída globalmente ou com uma rede de estranhos contribuindo para o projeto com dúzias de forks. É centrado no fluxo Topic Branches abordado em [ch03-git-branching].

Aqui está como geralmente funciona:

  1. Faça fork do projeto.

  2. Crie um branch para alterações no master.

  3. Faça alguns commits para aprimorar o projeto.

  4. Faça push dessa branch para seu projeto no GitHub

  5. Abra um Pull Request no GitHub.

  6. Discusta e opicionalmente continue commitando.

  7. O proprietário do projeto faz merge ou fecha o Pull Request.

Isto é basicamente o fluxo de trabalho de um Integration Manager abordado em Integration-Manager Workflow, mas em vez de usar o email para se comunicarem e revisarem mudanças, os times usam ferramentas do GitHub basedas na web.

Vamos observar um exemplo de como propor uma alteração para um projeto open source hospedado no GitHub usando esse fluxo.

Criando um Pull Request

Tony está procurando algum código para rodar no seu microcontrolador programável em Arduino e ele achou um ótimo arquivo de programa no GitHub em https://github.com/schacon/blink.

The project we want to contribute to.
Figure 90. O projeto que queremos contribuir

O único problema é que o intervalo para o LED piscar é muito curto, nós achamos que seria muito melhor aguardar 3 segundos ao invés de apenas 1 entre cada mudança de estado. Então vamos aprimorar o programa e retornar para o projeto como uma proposta de mudança.

Primeiro, clicamos no botão de Fork como mencionado mais cedo para pegar nossa própria cópia do projeto. Nosso nome de usuário é “tonychacon” então nossa cópia desse projeto está em https://github.com/tonychacon/blink e é aí onde podemos editá-lo. Vamos cloná-lo localmente, criar um tópico de branch, fazer alterações no código e finalmente dar push nas mudanças para o GitHub.

$ git clone https://github.com/tonychacon/blink (1)
Cloning into 'blink'...

$ cd blink
$ git checkout -b slow-blink (2)
Switched to a new branch 'slow-blink'

$ sed -i '' 's/1000/3000/' blink.ino (3)

$ git diff --word-diff (4)
diff --git a/blink.ino b/blink.ino
index 15b9911..a6cc5a5 100644
--- a/blink.ino
+++ b/blink.ino
@@ -18,7 +18,7 @@ void setup() {
// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  [-delay(1000);-]{+delay(3000);+}               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  [-delay(1000);-]{+delay(3000);+}               // wait for a second
}

$ git commit -a -m 'three seconds is better' (5)
[slow-blink 5ca509d] three seconds is better
 1 file changed, 2 insertions(+), 2 deletions(-)

$ git push origin slow-blink (6)
Username for 'https://github.com': tonychacon
Password for 'https://tonychacon@github.com':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/tonychacon/blink
 * [new branch]      slow-blink -> slow-blink
  1. Clone seu fork do projeto localmente

  2. Crie um tópico de branch descritivo

  3. Faça suas alterações no código

  4. Confira se suas alterações são boas

  5. Faça commit de suas alterações no tópico de branch

  6. Faça push do seu novo tópico de branch de volta para seu fork no GitHub.

Agora se você voltar para seu fork no GitHub, podemos ver que o GitHub percebeu que fizemos push de um novo tópico de branch e nos presenteia com um grande botão verde para conferir nossas alterações e abrir um Pull Request para o projeto original.

Você pode alternativamente ir para página de “Branches” em https://github.com/<user>/<project>/branches para localizar seu branch e abrir um Pull Request de lá.

Pull Request button
Figure 91. Botão de Pull Request

Se clicarmos no botão verde, veremos uma tela que nos pede para dar um título e uma descrição para nosso Pull Request. Quase sempre vale a pena colocar um pouco de esforço nisso, já que uma boa descriçaõo ajuda o proprietário do projeto original a determinar o que você está tentando fazer, se suas alterações propostas estão corretas e se aceitar suas alterações vai melhorar o projeto original.

Também vemos uma lista de commits no nosso tópico de branch que estão “ahead” no branch master (nessa caso apenas um) e uma diff unificada de todas as alterações que deveriam ser feitas nesse branch para que o proprietário do projeto faça merge.

Pull Request creation
Figure 92. Página de criação de Pull Request

Quando você aperta no botão Create pull request na tela, o proprietário do projeto que você fez fork vai receber uma notificação de que alguém está sugerindo uma alteração e um link para a página onde está toda a informação sobre isso.

Note

Embora os Pull Requests sejam normalmente usados para projetos públicos nos quais o contribuinte tem uma alteração pronta para ser feita, também é comum em projetos internos no começo do ciclo de desenvolvimento. Uma vez que você continua fazendo push no tópico de branch mesmo depois que o Pull Request está aberto, normalmente é aberto cedo e usado como uma forma de interagir no trabalho como time com um contexto, em vez de aberto perto do fim do processo.

Interagindo em um Pull Request

Nesse ponto, o proprietário do projeto pode olhar para a alteração sugerida e fazer merge dela, rejeitá-la ou comentá-la. Digamos que ele goste da ideia, mas prefira um pouco mais de tempo para pensar no assunto.

Essa conversa pode ocorrer por meio de email com os workflows apresentados em [ch05-distributed-git], ou de forma online com o GitHub. O proprietário do projeto pode revisar o diff unificado e deixar um comentário clicando em qualquer linha.

PR line comment
Figure 93. Comente em uma linha específica de código no Pull Request

Uma vez que o mantenedor faça este comentário, a pessoa que abriu o Pull Request (na verdade qualquer um que estiver observando o repositório) receberá uma notificação. Vamos customizar isso depois, mas se ele tivesse ativado suas configurações de email, Tony receberia um email como este:

Email notification
Figure 94. Comentários enviados como notificações pelo email

Qualquer um pode deixar um comentário geral no Pull Request. Em Página de discussão de um Pull Request podemos ver um exemplo no qual o proprietário do projeto comenta uma linha de código e deixa um comentário geral na seção de discussão. Você pode ver que os comentários aparecem na discussão também.

PR discussion page
Figure 95. Página de discussão de um Pull Request

Agora o contribuinte pode ver o que ele precisa fazer para ter sua alteração aceita. Felizmente isso é muito simples. Além do email, onde você tem que fazer re-roll das suas series e reenviar para a lista de email, com o GitHub você simplesmente commita no tópico de branch de novo e faz um push que automaticamente atualiza o Pull Request. Em Pull Request final você também pode ver que o comentário do código antigo foi colapsado no Pull Request atualizado, desde que seja de uma linha que foi alterada.

Adicionar commits para um Pull Request não ativa uma notificação, então uma vez que Tony fez push de suas correções ele decide deixar um comentário para informar ao proprietário do projeto que ele fez as alterações requeridas.

PR final
Figure 96. Pull Request final

Uma coisa interessante para se notar é que se você clicar na aba “Files Changed” nesse Pull Request, você vai ter o diff “unificado” — isto é, o agregado total das diferenças que seriam introduzidas no seu branch principal se este tópico de branch levasse merge. O git diff basicamente mostra automaticamente git master...<branch> para o branch no qual este Pull Request se baseia. Veja Determining What Is Introduced para mais informação sobre este tipo de diff.

A outra coisa de você vai notar é que o GitHub confere se o Pull Request fez um merge válido e te fornece um botão para fazer merge no servidor. Esse botão apenas mostra se você tem acesso de escrita no repositório e um merge trivial se possível. Se você clicar nele, o GitHub fornecerá um merge “no-fast-forward”, significando que mesmo se pudesse ser um fast-forward, será criado um commit de merge.

Se preferir, você pode simplesmente fazer pull da branch e dar merge localmente. Se você fizer merge deste branch na branch master e dar push para o GitHub, o Pull Request será automaticamente fechado.

Este é o workflow que a maioria dos projetos no GitHub usa. Tópicos de branch são criados, Pull Requests são abertos neles, uma discussão começa, possivelmente mais trabalho é feito na branch e eventualmente a requisição é fechada ou sofre merge.

Note
Não só Forks

É importante notar que você também pode abrir um Pull Request entre duas branches no mesmo repositório. Se você está trabalhando em um recurso com alguém e vocês têm acesso de escrita no projeto, você pode fazer push de um tópico de branch para o repositório e abrir um Pull Request da branch master do mesmo projeto para iniciar um processo de discussão e revisão do código. Sem a necessidade de forks.

Pull Requests Avançados

Agora que cobrimos o básico sobre contribuição de um projeto no GitHub, vamos abordar algumas dicas e truques interessantes sobre Pull Requests e então você pode ser mais eficaz usando eles.

Pull Requests como Patches

É importante entender que muitos projetos não pensam realmente em Pull Requests como filas de patches perfeitos que deveriam claramente ser aplicados em ordem, assim como a maioria dos projetos baseados em listas de emails pensa em séries de patches de contribuição. A maior parte dos projetos no GitHub encara branches de Pull Requests como conversações interativas sobre uma alteração proposta, culminando em um diff unificado que é aplicado por meio de merging.

Esta é um distinção importante, porque normalmente a alteração é sugerida antes do código atingir a perfeição, o que é muito mais raro em séries de patches de contribuições baseadas em listas de emails. Isso possibilita uma conversação mais cedo com os mantenedores para chegar a uma solução adequada por meio da dedicação da comunidade. Quando código é proposto com um Pull Request e os mantenedores ou a comunidade sugerem uma alteração, a série de patches normalmente não é recarregado, mas sim a diferença sofre push como um novo commit na branch, movendo a conversação para frente com o contexto do trabalho anterior preservado.

Por exemplo, se você voltar e olhar de novo em Pull Request final, você vai notar que o contribuinte não deu rebase no seu commit e enviou outro Pull Request. Em vez disso ele adicionou novos commits e fez push deles para uma branch existente. Desta forma se você voltar e olhar esse Pull Request no futuro, você poderá facilmente encontrar todo o contexto do motivo de cada decisão. Pressionar o botão “Merge” no site intencionalmente cria commit de merge que referencia o Pull Request e torna mais fácil voltar e pesquisar a conversação original caso necessário.

Mantendo-se no Upstream

Se o seu Pull Request ficar desatualizado ou de qualquer modo não for um merge válido, você vai querer consertá-lo para que o mantenedor possa facilmente fazer merge dele. O GitHub vai testar isso para você e informar na parte inferior de cada Pull Request se o merge é trivial ou não.

PR merge failure
Figure 97. Pull Request que não é um merge válido

Se você ver algo como Pull Request que não é um merge válido, você vai querer consertar seu branch para que ele fique verde e o para que mantenedor não tenha trabalho extra.

Você tem duas opções principais para lidar com isso. Você pode fazer rebase da sua branch no topo ou em qualquer branch alvo (que normalmente é a branch master do repositório que você deu fork), ou você pode fazer merge da branch alvo na sua branch.

A maioria dos desenvolvedores no GitHub vai escolher a última opção, pelas mesmas razões que passamos por cima na seção anterior. O que importa é o histórico e o merge final, então fazer rebase não será nada mais do que olhar um histórico limpo e passar longe de mais dificuldade e de propensão a erros.

Se você quiser dar merge em uma branch específica, você deve adicionar o repositório original como um novo remoto, fazer fetch, e dar merge da branch principal deste repositório para seu tópico de branch, consertando quaisquer erros e finalmente fazendo push para a mesma branch na qual você abriu o Pull Request.

Por exemplo, digamos que no exemplo do “tonychacon” que nós usamos antes, o autor original fez uma alteração que criaria um conflito no Pull Request. Vamos dar uma olhada nesses passos.

$ git remote add upstream https://github.com/schacon/blink (1)

$ git fetch upstream (2)
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From https://github.com/schacon/blink
 * [new branch]      master     -> upstream/master

$ git merge upstream/master (3)
Auto-merging blink.ino
CONFLICT (content): Merge conflict in blink.ino
Automatic merge failed; fix conflicts and then commit the result.

$ vim blink.ino (4)
$ git add blink.ino
$ git commit
[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \
    into slower-blink

$ git push origin slow-blink (5)
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/tonychacon/blink
   ef4725c..3c8d735  slower-blink -> slow-blink
  1. Adicione um repositório remoto original nomeado como “upstream”

  2. Faça fetch do trabalho mais recente para este repositório

  3. Dê merge da branch principal para seu tópico de branch

  4. Conserte o conflito que ocorreu

  5. Faça push de volta para o mesmo tópico de branch

Uma vez que você fez isso, o Pull Request será automaticamnete atualizado e re-avaliado para ver se é um merge válido.

PR fixed
Figure 98. O Pull Request agora é válído

Um dos grandes feitos do Git é que você pode fazer isso continuamente. Se você tem um projeto muito grande, você pode facilmente fazer merge de uma branch específica de novo e de novo se preocupando apenas com conflitos que sugiram desde da última vez que você deu merge, tornando o projeto muito mais gerenciável.

Se você realmente quer fazer rebase da branch para limpá-la, você até pode fazê-lo, mas uma atitude muito mais encorajada é não forçar uma branch em um Pull Request que já está aberto. Se outra pessoa deu push e trabalhou mais no projeto, você pode conferir todos os erros destacados em The Perils of Rebasing. Em vez disso, faça push da branch rebaseada para uma nova branch no GitHub e abra um novo Pull Request referenciando o antigo, e então feche o original.

Referências

Sua próxima pergunta deve ser “Como eu referencio um Pull Request antigo?”. Na verdade existem muitas, muitas formas de referenciar quase tudo que você pode escrever no GitHub.

Vamos começar com como referenciar outro Pull Request ou Issue. Todos os Pull Requests e Issues são números atribuídos e eles são únicos dentro do projeto. Por exemplo, você não pode ter um Pull Request 3 e uma Issue #3. Se você quiser referenciar qualquer Pull Request ou Issue de outro repositório, você pode simplesmente #<num> em qualquer comentário ou descrição. Você também pode ser mais específico quanto se a Issue ou o Pull Request está ativo em outro lugar; escreva username<num> se você está referenciando uma Issue ou um Pull Request em algum fork do repositório em que você está, ou usernam/repo#<num> para referenciar algo em outro repositório.

Vamos olhar um exemplo. Digamos que rebaseamos a branch no exemplo anterior, criamos um novo pull request para ele e agora queremos referenciar um antigo pull request para o novo. Nós também queremos referenciar um problema no fork do repositório e um problema em um projeto completamente diferente. Podemos preencher a descrição como em Referências cruzadas em um Pull Request..

PR references
Figure 99. Referências cruzadas em um Pull Request.

Quando fazemos submit deste pull request, veremos tudo renderizado como em Referências cruzadas renderizadas em um Pull Request..

PR references rendered
Figure 100. Referências cruzadas renderizadas em um Pull Request.

Note que a URL completa que colocamos no GitHub foi encurtada para a informação necessária.

Agora se Tony voltar e fechar o Pull Request original, poderemos ver isso sendo mencionado no novo, o GitHub automaticamente cria um evento de trackback na timeline do Pull Request. Isso significa que qualquer um que visite este Pull Request e veja que está fechado pode facilmente seguir o link para o que o sucedeu. O link parecerá algo parecido com Referências cruzadas renderizadas em um Pull Request..

PR closed
Figure 101. Referências cruzadas renderizadas em um Pull Request.

Além de números de issues, você também pode referenciar um commit específico pelo SHA-1. Você precisa especificar uma SHA-1 de 40 caracteres, mas se o GitHub vê-lo em um comentário, ele vai linkar diretamente para o commit. De novo, você pode referenciar commits em outros forks ou repositórios da mesma forma que você fez com issues.

Markdown aprimorado do GitHub

Fazer link com outras Issues é só o começo das coisas interessantes que você pode fazer com quase qualquer caixa de texto no GitHub. Em uma Issue e em descrições de Pull Request, comentários, comentários de código e outros, você pode usar o chamado “Markdown aprimorado do GitHub”. Markdown é similar a um texto comum mas renderizado de forma rica.

Veja Um exemplo escrito e renderizado do Markdown melhorado do GitHub. para um exemplo de como comentários ou textos podem ser escritos e renderizados usando Markdown.

Example Markdown
Figure 102. Um exemplo escrito e renderizado do Markdown melhorado do GitHub.

O Markdown do GitHub adiciona mais coisas para você fazer além da sintaxe básica do Markdown. Elas podem ser realmente úteis quando for criar um comentário ou uma descrição para um Pull Request ou para uma Issue.

Task Lists

O primeiro recurso realmente útil do Markdown específico do GitHub, especialmente para usar em Pull Requests, é a Task List. Uma task list é uma lista de checkboxes das coisas que você precisa fazer. Colocar elas em uma Issue ou em um Pull Request normalmente indica o que você quer fazer antes de considerar o item completo.

Você pode criar uma task list como essa:

- [X] Write the code
- [ ] Write all the tests
- [ ] Document the code

Se incluírmos isso na descrição do nosso Pull Request ou Issue, nós veremos ele ser renderizado como em Task lists renderizadas em um comentário de Markdown.

Example Task List
Figure 103. Task lists renderizadas em um comentário de Markdown.

Isso é normalmente usado em Pull Requests para indicar tudo aquilo que você gostaria de fazer em uma branch antes do Pull Request estar pronto para merge. A parte realmente legal é que você pode simplesmente clicar nas checkboxes para atualizar o comentário — você não precisa editar o Markdown diretamente para marcar suas tasks.

Não só isso, o GitHub vai procurar task lists nas suas Issues e Pull Requests e vai mostrá-las como metadata em uma páginas que as lista. Por exemplo, se você tem um Pull Request com tasks e você olha na página de resumo de todos os Pull Requests, você pode ver o quão longe isso vai. Isso ajuda as pessoas a dividir Pull Requests em subtasks e ajudar outras pessoas a acompanhar o progresso da branch. Você pode ver um exemplo disso em Resumo das Task lists na lista de Pull Requests..

Example Task List
Figure 104. Resumo das Task lists na lista de Pull Requests.

Esses recursos são incrivelmente úteis quando você abre um Pull Request cedo e usa ele para traçar seu progresso por meio de implementações no recurso.

Trechos de código

Você também pode adicionar um trecho de código nos comentários. Isso é muito útil se você quer apresentar algo que poderia tentar fazer antes de realmente implementar como um commit na branch. Isso também é muito usado para adicionar um código de exemplo do que não está funcionando ou do que este Pull Request poderia implementar.

Para adicionar um trecho de código, você precisa cercá-lo com acentos graves.

```java
for(int i=0 ; i < 5 ; i++)
{
   System.out.println("i is : " + i);
}
```

Se você adicionar um nome de linguagem como fizemos com java, o GitHub também vai tentar destacar o trecho. No caso do exemplo acima, o código vai ser renderizado como em Um exemplo de código cercado renderizado..

Rendered fenced code
Figure 105. Um exemplo de código cercado renderizado.

Citando

Se você está respondendo uma pequena parte de um comentário longo, você pode citá-la colocando o caractere > antes das linhas de código. De fato, isso é tão comum e útil que há uma atalho para isso. Se você destacar o texto em um comentário que você quer responder e aperta a tecla r, ele vai citar esse texto em uma caixa de comentário para você.

A citação vai parecer algo como isto:

> Whether 'tis Nobler in the mind to suffer
> The Slings and Arrows of outrageous Fortune,

How big are these slings and in particular, these arrows?

Uma vez renderizado, o comentário vai se parecer com Exemplo de citação renderizada.

Rendered quoting
Figure 106. Exemplo de citação renderizada

Emoji

Finalmente, você também pode usar emoji nos seus comentários. Na verdade isso é usado extensivamente em comentários que você encontra em várias Issues e Pull Requests no GitHub. Também há um auxiliador de emoji no GitHub. Se você está digitando um comentário e começa com o caractere :, um autocompletador vai te ajudar a achar o que você está procurando.

Emoji autocompleter
Figure 107. Autocompletador de emoji em ação.

Emojis têm a forma de :<nome>: em um comentário. Por exemplo, você poderia escrever algo como isto:

I :eyes: that :bug: and I :cold_sweat:.

:trophy: for :microscope: it.

:+1: and :sparkles: on this :ship:, it's :fire::poop:!

:clap::tada::panda_face:

Quando renderizado, deveria se parecer com Comentando que nem louco com emoji.

Emoji
Figure 108. Comentando que nem louco com emoji

Note que isso é incrivelmente útil, mas adiciona um elemento de diversão e emoção em um meio no qual é difícil transmitir emoção.

Note

Na verdade, há um grande número de serviços web que usam caracteres emoji esses dias. Um grande cola de referências para encontrar o emoji que expressa o que você quer dizer pode ser encontrada em:

Imagens

Isso tecnicamente não é o Markdown aprimorado do GitHub, mas é incrivelmente útil. Além de adicionar links Markdown de images nos comentários, o que pode ser bem difícil de encontrar e inserir as URLs, o GitHub permite arrastar e soltar imagens em áreas de texto para incorporá-las..

Drag and drop images
Figure 109. Arraste e solte imagens para carregá-las e incorporá-las.

Se você olhar em Arraste e solte imagens para carregá-las e incorporá-las., você pode ver um pequeno aviso “Ver como Markdown” acima da área de texto. Clicando nele gera uma cola de tudo que você pode fazer com o Markdown no GitHub.