Acrescenta um índice com outras páginas.
[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 Na prática, fast-forward facilita a leitura do histórico, evita
119 problemas com push e merges, e facilita reescrita de históricos, nas
120 situações em que estes devem ser feitos, em branches ainda não
121 publicados.
122
123 ## Merge
124
125 Como podemos, então, produzir um commit fast-forward que integre o
126 trabalho feito no branch devel? É possível criar um commit que seja
127 fast-forward tanto do branch master quanto do branch devel? Ou de
128 quaisquer dois commits?
129
130 Lembre-se que dissemos que um commit pode ter um ou mais genitores. Se
131 pudermos criar um commit que tenha os dois commits apontados pelos
132 branches master e devel como genitores, teremos um commit que permite
133 fast-forward para ambos.
134
135 Esse é um dos papéis do merge. O comando git merge permite criar um
136 commit com mais de um genitor. Mas ele faz mais que isso. Ele combina as
137 árvores de arquivos dos commits que farão parte do merge. A estratégia
138 utilizada para fazê-lo pode ser configurada e o padrão depende de
139 quantos commits fazem parte do merge. No caso comum, dois commits fazem
140 parte do merge, e a estratégia de combinar as duas árvores é um 3-way
141 merge recursivo, que compara as árvores dos dois commits com um
142 ancestral comum, e aplica as mudanças encontradas em ambas as árvores
143 dos dois commits.
144
145 O resultado é um novo commit com dois genitores, e uma árvore com
146 mudanças realizadas nos dois branches.
147
148         ~/project$ git merge devel
149         Faz o merge de testes.
150         Merge made by the 'recursive' strategy.
151          Makefile | 3 +++
152          1 file changed, 3 insertions(+)
153         ~/project$ 
154
155 [[!img merge.png]]