Fast-forward.
[cursogit.git] / Merge.mdwn
1 # Merge
2
3 Branches permitem experimentar com o repositório, e separar o
4 desenvolvimento de novas funcionalidades e correções. Também permite que
5 várias pessoas trabalhem no mesmo projeto, através de branches remotos.
6
7 Em algum momento, os vários branches devem ser reunidos em uma nova
8 versão. Há várias formas de fazê-lo. Uma das ferramentas que o permite é
9 o merge. Mas antes, revisaremos e veremos alguns conceitos.
10
11 ## Branches
12
13 Discutimos como criar branches na seção [[Branches]] e como obter
14 branches remotos na seção [[Remotes]]. Vejamos, no entanto, porque levam
15 esse nome.
16
17 A imagem abaixo mostra um repositório com poucos commits. Com exceção do
18 primeiro commit, todos os commits em Git possuem um ou mais genitores.
19 Isso faz com que a relação entre os commits constitua um grafo acíclico
20 dirigido, uma estrutura similar àquela associada a uma árvore
21 genealógica.
22
23 O exemplo abaixo mostra commits com apenas um genitor. Note, no entanto,
24 que há um ramo, representado pelo branch devel. Isto é o que chamamos de
25 histórico não-linear. Há formas de linearizar históricos e veremos mais
26 adiante as razões para fazê-lo, quando não fazê-lo, e como fazê-lo.
27
28 [[!img branches.png]]
29
30 ## Commit IDs
31
32 Vimos em [[Git_log]] que um commit é identificado por um ID. Este ID é
33 um hash SHA-1 do objeto do commit. O objeto é composto pelo log, autor,
34 data, a árvore de arquivos, outros metadados, e a lista de genitores.
35 Dada a natureza de um hash criptográfico como SHA-1, pequenas mudanças
36 no commit, seja no sumário, em algum arquivo, genitor, ou outros, causa
37 uma mudança no commit ID.
38
39 Vejamos como alterar um commit usando a opção --amend do comando git
40 commit. Para tanto, criaremos um novo branch a partir do master e
41 alteraremos nosso novo HEAD.
42
43         ~/project$ git checkout -b amend master
44         Switched to a new branch 'amend'
45         ~/project$ 
46
47 Veja como fica o nosso grafo após o novo branch ser criado na imagem
48 abaixo. Note que o grafo continua o mesmo e o branch amend é apenas uma
49 referência ao mesmo commit ao qual o branch master se refere.
50
51 [[!img branch.png]]
52
53 Neste exemplo, alteramos apenas a mensagem de log, usando a opção
54 --amend.
55
56         ~/project$ git commit --amend
57         [amend 571a638] Acrescenta lista de arquivos a serem ignorados.
58          1 file changed, 2 insertions(+)
59          create mode 100644 .gitignore
60         ~/project$ 
61
62 Note o resultado na figura abaixo. O commit foi alterado, mudando seu
63 ID. Ele ainda aponta para o mesmo genitor, mas é um commit diferente
64 daquele referenciado pelo branch master.
65
66 [[!img amend.png]]
67
68 ## Histórico
69
70 A sequência de commits que vemos em um branch é também chamada de
71 histórico. Como é utilizado um hash criptográfico e um link entre os
72 commits utilizando o ID, e esse faz parte do conteúdo criptografado, não
73 é possível alterar este histórico.
74
75 Quando alteramos um commit, como feito acima, o histórico original é
76 mantido enquanto ainda existir alguma referência a ele, através de
77 branches, por exemplo. Se algum commit tiver o commit alterado como seu
78 genitor, sua referência ainda será ao commit genitor original, não à
79 nova versão do commit. Sendo assim, para aproveitar os commits já
80 realizados, é necessário reescrevê-los para que apontem, em cadeia, às
81 novas versões dos commits reescritos. Isso se chama reescrevendo a
82 história, e pode ser feito com o comando git rebase, entre outros
83 comandos.
84
85 Veremos mais logo quando e como reescrever a história. Agora, veremos
86 quando não se deve reescrevê-la.
87
88 ## Fast-foward
89
90 Quando realizamos um push em um repositório, enviamos nossas
91 atualizações. No entanto, existe uma condição para que o Git aceite esta
92 atualização sem emitir um aviso. O novo commit enviado deve conter todos
93 os commits já presentes no branch sendo atualizado. Significa que o novo
94 commit deve ter o velho commit como ancestral em seu grafo de histórico.
95
96 No exemplo anterior em que fizemos o amend, tanto o branch amend quanto
97 o branch devel não têm o branch master como ancestral. Isto significa
98 que a atualização do branch master para um destes commits não avançaria
99 o histórico. Não seria o que chamamos de fast-forward.
100
101 Fazer uma atualização que não é fast-forward seria como apagar o
102 histórico que já ocorreu. Substituir um ramo por outro. É o que muitos
103 chamam de fork. Fazer fork utilizando diferentes repositórios por
104 diferentes pessoas, ou diferentes branches com o propósito de realizar
105 merges é saudável pra uma comunidade. É o que permite o envolvimento de
106 mais pessoas em um projeto, e a experimentação. Mas substituir um mesmo
107 branch público por algo que apaga parte de seu histórico pode introduzir
108 vários problemas no fluxo de trabalho de uma comunidade, como merges
109 duplicados e desnecessários, reescritas de histórico em toda a cadeia de
110 branches e forks, criando um efeito cascata.
111
112 Portanto, não publique branches que não pretenda manter de forma
113 fast-forward, a não ser que fique claro no workflow como aquele branch
114 será consumido em outro branch fast-forward, ou deixando claro que o
115 branch será reescrito, e não deve ser utilizado como base a não ser que
116 o contribuidor esteja preparado para lidar com as reescritas.
117
118 ## Merge
119
120 Integração de código e histórico.
121
122         ~/project$ git merge devel
123         Faz o merge de testes.
124         Merge made by the 'recursive' strategy.
125          Makefile | 3 +++
126          1 file changed, 3 insertions(+)
127         ~/project$ 
128
129 [[!img merge.png]]