-
1. Primeiros Passos
- 1.1 Sobre Controle de Versão
- 1.2 Uma Breve História do Git
- 1.3 O que é Git?
- 1.4 A Linha de Comando
- 1.5 Instalando o Git
- 1.6 Configuração inicial do Git
- 1.7 Obtendo Ajuda
- 1.8 Resumo
-
2. Fundamentos do Git
-
3. Ramificação (Branching) no Git
-
4. Git no Servidor
- 4.1 Os Protocolos
- 4.2 Colocando o Git em um Servidor
- 4.3 Gerando a sua Chave Pública SSH
- 4.4 Configurando o Servidor
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Opções de Hospedagem de Terceiros
- 4.10 Resumo
-
5. Git Distribuído
-
6. GitHub
-
7. Ferramentas do Git
- 7.1 Seleção de Revisão
- 7.2 Área de Stage Interativa
- 7.3 Fazendo Stash e Limpando
- 7.4 Assinando Seu Trabalho
- 7.5 Pesquisando (Searching)
- 7.6 Reescrevendo o Histórico
- 7.7 Reset Desmistificado
- 7.8 Merging Avançado
- 7.9 Rerere
- 7.10 Depurando com Git
- 7.11 Submódulos
- 7.12 Empacotando (Bundling)
- 7.13 Substituir (Replace)
- 7.14 Armazenamento de Credenciais
- 7.15 Resumo
-
8. Customizando o Git
- 8.1 Configuração do Git
- 8.2 Atributos do Git
- 8.3 Hooks do Git
- 8.4 Uma Política de Exemplo Imposta pelo Git
- 8.5 Resumo
-
9. Git e Outros Sistemas
- 9.1 Git como Cliente
- 9.2 Migrando para o Git
- 9.3 Resumo
-
10. Git Internals (Por Dentro do Git)
- 10.1 Encanamento e Porcelana (Plumbing and Porcelain)
- 10.2 Objetos do Git
- 10.3 Referências do Git
- 10.4 Arquivos de pacote (Packfiles)
- 10.5 Refspec
- 10.6 Protocolos de Transferência
- 10.7 Manutenção e Recuperação de Dados
- 10.8 Variáveis de Ambiente
- 10.9 Resumo
-
A1. Appendix A: O Git em Outros Ambientes
- A1.1 Interfaces Gráficas
- A1.2 Git no Visual Studio
- A1.3 Git no Visual Studio Code
- A1.4 Git no IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git no Sublime Text
- A1.6 Git no Bash
- A1.7 Git no Zsh
- A1.8 Git no PowerShell
- A1.9 Resumo
-
A2. Appendix B: Incorporando o Git nos seus Aplicativos
- A2.1 Linha de Comando do Git
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Appendix C: Comandos do Git
- A3.1 Configuração e Ajustes
- A3.2 Obtendo e Criando Projetos
- A3.3 Snapshots Básicos
- A3.4 Branches e Merges
- A3.5 Compartilhando e Atualizando Projetos
- A3.6 Inspeção e Comparação
- A3.7 Depuração
- A3.8 Patches
- A3.9 E-mail
- A3.10 Sistemas Externos
- A3.11 Administração
- A3.12 Comandos de Encanamento
8.2 Customizando o Git - Atributos do Git
Atributos do Git
Algumas dessas configurações também podem ser especificadas para um caminho, para que o Git aplique essas configurações apenas para um subdiretório ou subconjunto de arquivos.
Essas configurações específicas de caminho são chamadas de atributos do Git e são definidas em um arquivo .gitattributes em um dos seus diretórios (normalmente a raiz do projeto) ou no arquivo .git/info/attributes, caso você não queira incluir o arquivo de atributos nos commits do projeto.
Com os atributos, você pode, por exemplo, especificar estratégias de merge diferentes para arquivos ou diretórios individuais do projeto, dizer ao Git como calcular o diff de arquivos não textuais ou fazer o Git filtrar o conteúdo antes de armazená-lo ou extraí-lo. Nesta seção, você vai aprender sobre alguns dos atributos que você pode configurar nos caminhos do seu projeto Git e ver alguns exemplos do uso desse recurso na prática.
Arquivos Binários
Um truque legal para o qual você pode usar os atributos do Git é dizer a ele quais arquivos são binários (em casos onde ele não seria capaz de descobrir de outra forma) e dar a ele instruções especiais sobre como lidar com esses arquivos. Por exemplo, alguns arquivos de texto podem ser gerados por máquina e não sujeitos a diff, enquanto alguns arquivos binários podem passar por um diff. Você verá como dizer ao Git o que é o que.
Identificando Arquivos Binários
Alguns arquivos parecem com arquivos de texto, mas para todos os propósitos devem ser tratados como dados binários.
Por exemplo, os projetos do Xcode no macOS contêm um arquivo terminado em .pbxproj, que é basicamente um conjunto de dados JSON (formato de dados JavaScript em texto puro) gravado em disco pelo IDE, que registra as suas configurações de build e assim por diante.
Apesar de tecnicamente ser um arquivo de texto (por ser todo em UTF-8), você não vai querer tratá-lo como tal porque é, na verdade, um banco de dados leve – você não pode fazer um merge no conteúdo dele se duas pessoas o alterarem, e usar o diff geralmente não será de ajuda.
O arquivo deve ser consumido por uma máquina.
Em suma, você quer tratá-lo como um arquivo binário.
Para dizer ao Git para tratar todos os arquivos pbxproj como dados binários, adicione a seguinte linha ao seu arquivo .gitattributes:
*.pbxproj binary
Agora, o Git não tentará converter ou corrigir problemas de CRLF; nem tentará computar ou imprimir um diff para alterações nesse arquivo ao executar um git show ou um git diff no seu projeto.
Fazendo o Diff de Arquivos Binários
Você também pode usar a funcionalidade de atributos do Git para fazer o diff de arquivos binários com eficácia. Você faz isso dizendo ao Git como converter seus dados binários num formato de texto que pode ser comparado por meio do diff normal.
Primeiro, você usará essa técnica para resolver um dos problemas mais irritantes conhecidos pela humanidade: os documentos de controle de versão do Microsoft Word.
Se você deseja fazer controle de versão em documentos do Word, você pode colocá-los em um repositório Git e fazer o commit de vez em quando; mas de que serve isso?
Se você executar git diff normalmente, você verá apenas algo como isto:
$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 88839c4..4afcb7c 100644
Binary files a/chapter1.docx and b/chapter1.docx differ
Você não pode comparar as duas versões diretamente, a menos que as verifique e faça uma varredura manualmente, certo?
Acontece que você pode fazer isso muito bem utilizando os atributos do Git.
Coloque a linha a seguir no seu arquivo .gitattributes:
*.docx diff=word
Isso informa ao Git que qualquer arquivo correspondente a esse padrão (.docx) deve usar o filtro “word” quando você tentar visualizar um diff que contenha alterações.
O que é o filtro “word”?
Você terá que configurá-lo.
Aqui você configurará o Git para usar o programa docx2txt para converter documentos do Word em arquivos de texto legíveis, nos quais ele fará o diff da maneira correta.
Primeiro, você vai ter que instalar o docx2txt; é possível baixá-lo a partir de https://sourceforge.net/projects/docx2txt.
Siga as instruções presentes no arquivo INSTALL para colocá-lo em algum lugar que o seu shell possa achá-lo.
No próximo passo, você deve criar um script wrapper para converter a saída para o formato que o Git espera.
Crie um arquivo chamado docx2txt num local que fique visível no seu caminho, e adicione estes conteúdos:
#!/bin/bash
docx2txt.pl "$1" -
Não se esqueça de usar o comando chmod a+x no arquivo.
Finalmente, você pode configurar o Git para usar este script:
$ git config diff.word.textconv docx2txt
Agora o Git sabe que, ao calcular um diff entre dois snapshots, deve passar todos os arquivos terminados em .docx pelo filtro “word”, definido como o programa docx2txt.
Isto faz um trabalho eficaz elaborando ótimas versões em formato de texto para os seus arquivos do Word antes de tentar fazer um diff deles.
Aqui vai um exemplo: O Capítulo 1 deste livro foi convertido para o formato do Word e teve um commit num repositório Git.
Depois um novo parágrafo foi adicionado.
Veja o que o git diff irá mostrar:
$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 0b013ca..ba25db5 100644
--- a/chapter1.docx
+++ b/chapter1.docx
@@ -2,6 +2,7 @@
This chapter will be about getting started with Git. We will begin at the beginning by explaining some background on version control tools, then move on to how to get Git running on your system and finally how to get it setup to start working with. At the end of this chapter you should understand why Git is around, why you should use it and you should be all setup to do so.
1.1. About Version Control
What is "version control", and why should you care? Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. For the examples in this book you will use software source code as the files being version controlled, though in reality you can do this with nearly any type of file on a computer.
+Testing: 1, 2, 3.
If you are a graphic or web designer and want to keep every version of an image or layout (which you would most certainly want to), a Version Control System (VCS) is a very wise thing to use. It allows you to revert files back to a previous state, revert the entire project back to a previous state, compare changes over time, see who last modified something that might be causing a problem, who introduced an issue and when, and more. Using a VCS also generally means that if you screw things up or lose files, you can easily recover. In addition, you get all this for very little overhead.
1.1.1. Local Version Control Systems
Many people's version-control method of choice is to copy files into another directory (perhaps a time-stamped directory, if they're clever). This approach is very common because it is so simple, but it is also incredibly error prone. It is easy to forget which directory you're in and accidentally write to the wrong file or copy over files you don't mean to.
O Git nos diz, com sucesso e de forma sucinta, que nós adicionamos a string “Testing: 1, 2, 3.”, o que está correto. Não é algo perfeito – as mudanças de formatação não seriam apresentadas aqui – mas certamente isso funciona.
Outro problema interessante que você pode resolver dessa forma envolve comparar as diferenças entre arquivos de imagem.
Uma maneira de fazer isso é passar os arquivos de imagem por um filtro que extraia suas informações EXIF – metadados registrados na maioria dos formatos de imagem.
Se você baixar e instalar o programa exiftool, poderá usá-lo para converter suas imagens em texto sobre os metadados, de modo que pelo menos o diff mostre uma representação textual das alterações ocorridas.
Adicione a seguinte linha ao seu arquivo .gitattributes:
*.png diff=exif
Configure o Git para usar esta ferramenta:
$ git config diff.exif.textconv exiftool
Caso você substitua uma imagem no seu projeto e execute o git diff, você verá algo como isto:
diff --git a/image.png b/image.png
index 88839c4..4afcb7c 100644
--- a/image.png
+++ b/image.png
@@ -1,12 +1,12 @@
ExifTool Version Number : 7.74
-File Size : 70 kB
-File Modification Date/Time : 2009:04:21 07:02:45-07:00
+File Size : 94 kB
+File Modification Date/Time : 2009:04:21 07:02:43-07:00
File Type : PNG
MIME Type : image/png
-Image Width : 1058
-Image Height : 889
+Image Width : 1056
+Image Height : 827
Bit Depth : 8
Color Type : RGB with Alpha
É possível que você veja facilmente que o tamanho do arquivo e as dimensões da imagem foram alterados.
Expansão de Palavras-Chave
A expansão de palavras-chave no estilo do SVN ou CVS costuma ser solicitada por desenvolvedores habituados a esses sistemas. O principal problema disso no Git é que você não pode modificar um arquivo com informações sobre o commit depois de criá-lo, pois o Git calcula primeiro o checksum do arquivo. No entanto, você pode inserir texto em um arquivo quando ele passa por checkout e removê-lo novamente antes que seja adicionado a um commit. Os atributos do Git lhe oferecem duas maneiras para se fazer isso.
Primeiro, você pode inserir automaticamente o checksum SHA-1 de um blob em um campo $Id$ do arquivo.
Se você definir esse atributo para um arquivo ou conjunto de arquivos, na próxima vez que fizer checkout dessa branch o Git substituirá esse campo pelo SHA-1 do blob.
É importante observar que esse não é o SHA-1 do commit, mas o do próprio blob.
Coloque a linha a seguir no seu arquivo .gitattributes:
*.txt ident
Acrescente uma referência $Id$ a um arquivo de teste:
$ echo '$Id$' > test.txt
Da próxima vez que você fizer checkout nesse arquivo, o Git irá injetar o SHA-1 do blob:
$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt
$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
No entanto, esse resultado tem utilidade limitada. Se você já usou a substituição de palavras-chave no CVS ou no Subversion, sabe que é possível incluir uma marca de data – o SHA-1 não ajuda muito, pois é relativamente aleatório e, apenas olhando, não é possível dizer se um SHA-1 é mais antigo ou mais recente que outro.
Você pode criar seus próprios filtros para fazer substituições em arquivos durante o commit e o checkout.
Eles são os chamados filtros do tipo “clean” e “smudge”.
No arquivo .gitattributes, você pode definir um filtro para caminhos específicos e depois configurar scripts que processem os arquivos imediatamente antes do checkout (“smudge”, veja O filtro “smudge” é executado no checkout) e imediatamente antes de serem adicionados ao stage (“clean”, veja O filtro “clean” vai rodar na hora em que os arquivos forem pro stage).
Esses filtros podem ser configurados para fazer todo tipo de coisa interessante.
A mensagem de commit original desse recurso apresenta um exemplo simples que passa todo o código-fonte C pelo programa indent antes do commit.
Para configurá-lo, defina no arquivo .gitattributes o atributo filter dos arquivos \*.c como o filtro “indent”:
*.c filter=indent
Em seguida, informe ao Git o que o filtro “indent” deve fazer nas etapas smudge e clean:
$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat
Nesse caso, quando você fizer commit de arquivos que correspondam a *.c, o Git os passará pelo programa indent antes de adicioná-los ao stage e depois pelo programa cat antes de colocá-los novamente no disco.
O programa cat essencialmente não faz nada: ele devolve os mesmos dados que recebe.
Essa combinação efetivamente filtra todos os arquivos de código-fonte C por meio do indent antes do commit.
Outro exemplo interessante implementa a expansão da palavra-chave $Date$, no estilo do RCS.
Para fazer isso corretamente, você precisa de um pequeno script que receba o nome de um arquivo, descubra a data do último commit desse projeto e insira essa data no arquivo.
Este pequeno script Ruby faz isso:
#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
Tudo o que o script faz é obter a data do commit mais recente com o comando git log, inserir essa data em todas as strings $Date$ que encontrar na entrada padrão e imprimir os resultados – deve ser simples fazer isso na linguagem com a qual você se sente mais à vontade.
Você pode nomear esse arquivo como expand_date e colocá-lo no seu path.
Agora, você precisa configurar um filtro no Git (chame-o de dater) e instruí-lo a usar seu filtro expand_date para processar os arquivos no checkout.
Você usará uma expressão Perl para desfazer essa alteração no commit:
$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
Esse trecho de Perl remove tudo o que encontrar em uma string $Date$, retornando-a ao estado inicial.
Agora que seu filtro está pronto, você pode testá-lo configurando um atributo do Git para esse arquivo, que aciona o novo filtro, e criando um arquivo com a palavra-chave $Date$:
date*.txt filter=dater
$ echo '# $Date$' > date_test.txt
Se você fizer commit dessas alterações e executar o checkout do arquivo novamente, verá a palavra-chave devidamente substituída:
$ git add date_test.txt .gitattributes
$ git commit -m "Test date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$
Você pode perceber como essa técnica pode ser poderosa em aplicações personalizadas.
Você precisa ter cuidado, no entanto, porque o arquivo .gitattributes é commitado e repassado junto com o projeto, mas o driver (neste caso, dater) não é, então ele não funcionará em todos os lugares.
Ao projetar esses filtros, eles devem ser capazes de falhar com elegância e fazer com que o projeto ainda funcione corretamente.
Exportando Seu Repositório
Os dados de atributos do Git também permitem que você faça algumas coisas interessantes ao exportar um arquivo do seu projeto.
export-ignore
Você pode dizer ao Git para não exportar certos arquivos ou diretórios ao gerar um arquivo.
Se houver um subdiretório ou arquivo que você não queira incluir no arquivo compactado, mas queira manter no projeto, poderá identificá-lo com o atributo export-ignore.
Por exemplo, digamos que você tenha alguns arquivos de teste em um subdiretório test/, e não faça sentido incluí-los na exportação tarball do seu projeto.
Você pode adicionar a seguinte linha ao seu arquivo de atributos do Git:
test/ export-ignore
Agora, quando você executar o git archive para criar um tarball do seu projeto, esse diretório não será incluído no arquivo.
export-subst
Ao exportar arquivos para implantação, você pode aplicar a formatação e o processamento de expansão de palavra-chave do git log a partes selecionadas dos arquivos marcados com o atributo export-subst.
Por exemplo, se você quiser incluir um arquivo chamado LAST_COMMIT no seu projeto, e ter os metadados sobre o último commit injetados automaticamente nele quando o git archive for executado, você pode, por exemplo, configurar os seus arquivos .gitattributes e LAST_COMMIT assim:
LAST_COMMIT export-subst
$ echo 'Last commit date: $Format:%cd by %aN$' > LAST_COMMIT
$ git add LAST_COMMIT .gitattributes
$ git commit -am 'adding LAST_COMMIT file for archives'
Quando você executar o git archive, o conteúdo do arquivo arquivado ficará assim:
$ git archive HEAD | tar xCf ../deployment-testing -
$ cat ../deployment-testing/LAST_COMMIT
Last commit date: Tue Apr 21 08:38:48 2009 -0700 by Scott Chacon
As substituições podem incluir, por exemplo, a mensagem de commit e quaisquer git notes, e o git log pode fazer uma quebra de palavras simples:
$ echo '$Format:Last commit: %h by %aN at %cd%n%+w(76,6,9)%B$' > LAST_COMMIT
$ git commit -am 'export-subst uses git log'\''s custom formatter
git archive uses git log'\''s `pretty=format:` processor
directly, and strips the surrounding `$Format:` and `$`
markup from the output.
'
$ git archive @ | tar xfO - LAST_COMMIT
Last commit: 312ccc8 by Jim Hill at Fri May 8 09:14:04 2015 -0700
export-subst uses git log's custom formatter
git archive uses git log's `pretty=format:` processor directly, and
strips the surrounding `$Format:` and `$` markup from the output.
O arquivo resultante é adequado para trabalho de implantação, mas como qualquer arquivo exportado, ele não é adequado para um trabalho de desenvolvimento posterior.
Estratégias de Merge
Você também pode usar os atributos do Git para dizer ao Git para usar estratégias de merge diferentes para arquivos específicos no seu projeto. Uma opção muito útil é dizer ao Git para não tentar fazer o merge de arquivos específicos quando eles tiverem conflitos, mas sim usar o seu lado do merge em vez do de outra pessoa.
Isso é útil se uma branch no seu projeto tiver divergido ou for especializada, mas você quiser ser capaz de fazer o merge de alterações de volta dela, e você quiser ignorar certos arquivos.
Digamos que você tenha um arquivo de configurações de banco de dados chamado database.xml que é diferente em duas branches, e você deseja fazer o merge na sua outra branch sem bagunçar o arquivo do banco de dados.
Você pode configurar um atributo como este:
database.xml merge=ours
Em seguida, defina uma estratégia de merge ours fictícia com:
$ git config --global merge.ours.driver true
Se você fizer o merge na outra branch, em vez de ter conflitos de merge com o arquivo database.xml, você verá algo como isto:
$ git merge topic
Auto-merging database.xml
Merge made by recursive.
Neste caso, o database.xml permanece na versão que você tinha originalmente.