-
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
7.1 Ferramentas do Git - Seleção de Revisão
A esta altura, você aprendeu a maioria dos comandos e fluxos de trabalho do dia a dia de que precisa para gerenciar ou manter um repositório Git para o seu controle de código-fonte. Você concluiu as tarefas básicas de rastreamento e commit de arquivos, e utilizou o poder da área de stage, do branch de tópicos leve (lightweight topic branching) e merge.
Agora você explorará várias coisas muito poderosas que o Git pode fazer, as quais você pode não usar necessariamente no dia a dia, mas pode precisar em algum momento.
Seleção de Revisão
O Git permite que você se refira a um único commit, conjunto de commits ou intervalo (range) de commits de várias maneiras. Elas não são necessariamente óbvias, mas são úteis de se conhecer.
Revisões Únicas
Você pode obviamente se referir a qualquer commit único pelo seu hash SHA-1 completo de 40 caracteres, mas também existem formas mais amigáveis aos humanos de se referir a commits. Esta seção descreve as várias maneiras pelas quais você pode se referir a qualquer commit.
SHA-1 Curto
O Git é inteligente o suficiente para descobrir a qual commit você está se referindo se você fornecer os primeiros caracteres do hash SHA-1, desde que esse hash parcial tenha pelo menos quatro caracteres e seja inequívoco; ou seja, nenhum outro objeto no banco de dados de objetos pode ter um hash que comece com o mesmo prefixo.
Por exemplo, para examinar um commit específico onde você sabe que adicionou certa funcionalidade, você pode primeiro executar o comando git log para localizar o commit:
$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
Fix refs handling, add gc auto, update tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
Add some blame and merge stuff
Neste caso, digamos que você esteja interessado no commit cujo hash começa com 1c002dd….
Você pode inspecionar esse commit com qualquer uma das seguintes variações de git show (supondo que as versões mais curtas sejam inequívocas):
$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d
O Git pode descobrir uma abreviação curta e única para os seus valores SHA-1.
Se você passar --abbrev-commit para o comando git log, a saída usará valores mais curtos, mas os manterá únicos; o padrão é usar sete caracteres, mas os torna mais longos se necessário para manter o SHA-1 inequívoco:
$ git log --abbrev-commit --pretty=oneline
ca82a6d Change the version number
085bb3b Remove unnecessary test code
a11bef0 Initial commit
Geralmente, oito a dez caracteres são mais do que suficientes para serem únicos em um projeto. Por exemplo, a partir de fevereiro de 2019, o kernel do Linux (que é um projeto bastante considerável) tem mais de 875.000 commits e quase sete milhões de objetos no seu banco de dados de objetos, sem que dois objetos tenham SHA-1 idênticos nos primeiros 12 caracteres.
|
Note
|
UMA BREVE NOTA SOBRE O SHA-1
Muitas pessoas começam a se preocupar em algum momento de que elas, por acaso, terão dois objetos distintos em seu repositório com o mesmo valor de hash SHA-1. E aí? Se você por acaso comitar um objeto que tenha o mesmo valor de hash SHA-1 que um objeto anterior diferente no seu repositório, o Git verá o objeto anterior já no seu banco de dados do Git, assumirá que ele já foi gravado e simplesmente o reutilizará. Se você tentar fazer o checkout desse objeto novamente em algum momento, você sempre obterá os dados do primeiro objeto. No entanto, você deve estar ciente de quão ridiculamente improvável é esse cenário.
O digest SHA-1 tem 20 bytes ou 160 bits.
O número de objetos com hash aleatório necessários para garantir uma probabilidade de 50% de uma única colisão é cerca de 280 (a fórmula para determinar a probabilidade de colisão é Aqui está um exemplo para se ter uma ideia do que seria necessário para obter uma colisão SHA-1. Se todos os 6,5 bilhões de humanos na Terra estivessem programando, e a cada segundo, cada um estivesse produzindo código que fosse o equivalente a toda a história do kernel do Linux (6,5 milhões de objetos Git) e enviando-o para um enorme repositório Git, demoraria cerca de 2 anos até que o repositório contivesse objetos suficientes para ter 50% de probabilidade de uma única colisão de objeto SHA-1. Assim, uma colisão orgânica do SHA-1 é menos provável do que todos os membros da sua equipe de programação serem atacados e mortos por lobos em incidentes não relacionados na mesma noite. Se você dedicar milhares de dólares em poder computacional a isso, é possível sintetizar dois arquivos com o mesmo hash, como comprovado em https://shattered.io/ em fevereiro de 2017. O Git está caminhando para o uso do SHA256 como algoritmo de hashing padrão, o qual é muito mais resiliente a ataques de colisão, e tem código no lugar para ajudar a mitigar este ataque (embora não possa eliminá-lo completamente). |
Referências de Branch
Uma maneira direta de se referir a um commit em particular é se ele for o commit na ponta (tip) de um branch; nesse caso, você pode simplesmente usar o nome do branch em qualquer comando do Git que espere uma referência a um commit.
Por exemplo, se você quiser examinar o último objeto de commit em um branch, os seguintes comandos são equivalentes, assumindo que o branch topic1 aponte para o commit ca82a6d…:
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1
Se você quiser ver para qual SHA-1 específico um branch aponta, ou se você quiser ver o que qualquer um desses exemplos se resume em termos de SHA-1s, você pode usar uma ferramenta de encanamento (plumbing) do Git chamada rev-parse.
Você pode ver Git Internals (Por Dentro do Git) para mais informações sobre ferramentas de plumbing; basicamente, rev-parse existe para operações de nível mais baixo e não foi projetado para ser usado em operações do dia a dia.
No entanto, às vezes pode ser útil quando você precisar ver o que realmente está acontecendo.
Aqui você pode rodar o rev-parse no seu branch.
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949
Nomes Curtos do RefLog
Uma das coisas que o Git faz em segundo plano enquanto você está trabalhando é manter um “reflog” — um log de onde o seu HEAD e referências de branch estiveram nos últimos meses.
Você pode ver o seu reflog usando git reflog:
$ git reflog
734713b HEAD@{0}: commit: Fix refs handling, add gc auto, update tests
d921970 HEAD@{1}: merge phedders/rdocs: Merge made by the 'recursive' strategy.
1c002dd HEAD@{2}: commit: Add some blame and merge stuff
1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
95df984 HEAD@{4}: commit: # This is a combination of two commits.
1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD
Toda vez que a ponta do seu branch é atualizada por qualquer motivo, o Git armazena essa informação para você neste histórico temporário.
Você também pode usar seus dados do reflog para se referir a commits mais antigos.
Por exemplo, se você quiser ver o quinto valor anterior do HEAD do seu repositório, você pode usar a referência @{5} que você vê na saída do reflog:
$ git show HEAD@{5}
Você também pode usar essa sintaxe para ver onde um branch estava há um tempo específico.
Por exemplo, para ver onde o seu branch master estava ontem, você pode digitar:
$ git show master@{yesterday}
Isso mostraria a você onde a ponta do seu branch master estava ontem.
Esta técnica só funciona para dados que ainda estão no seu reflog, então você não pode usá-la para procurar commits mais antigos que alguns meses.
Para ver as informações do reflog formatadas como a saída do git log, você pode executar git log -g:
$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: commit: Fix refs handling, add gc auto, update tests
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
Fix refs handling, add gc auto, update tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
É importante notar que as informações do reflog são estritamente locais — é um log apenas do que você fez no seu repositório.
As referências não serão as mesmas na cópia do repositório de outra pessoa; além disso, logo após você clonar um repositório inicialmente, você terá um reflog vazio, já que nenhuma atividade ocorreu ainda no seu repositório.
Rodar git show HEAD@{2.months.ago} mostrará o commit correspondente apenas se você clonou o projeto há pelo menos dois meses — se você clonou mais recentemente do que isso, verá apenas seu primeiro commit local.
|
Tip
|
Pense no reflog como a versão do Git do histórico do shell
Se você tem experiência em UNIX ou Linux, pode pensar no reflog como a versão do Git do histórico do shell, o que enfatiza que o que está lá é claramente relevante apenas para você e sua “sessão”, e não tem nada a ver com qualquer outra pessoa que possa estar trabalhando na mesma máquina. |
|
Note
|
Escapando chaves no PowerShell
Ao usar o PowerShell, as chaves como
|
Referências de Ascendência (Ancestry)
A outra maneira principal de especificar um commit é por meio de sua ascendência.
Se você colocar um ^ (acento circunflexo) no final de uma referência, o Git resolverá isso para significar o pai daquele commit.
Suponha que você observe o histórico do seu projeto:
$ git log --pretty=format:'%h %s' --graph
* 734713b Fix refs handling, add gc auto, update tests
* d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd Add some blame and merge stuff
|/
* 1c36188 Ignore *.gem
* 9b29157 Add open3_detach to gemspec file list
Então, você pode ver o commit anterior especificando HEAD^, que significa “o pai do HEAD”:
$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
|
Note
|
Escapando o acento circunflexo no Windows
No Windows em
|
Você também pode especificar um número após o ^ para identificar qual pai você deseja; por exemplo, d921970^2 significa “o segundo pai de d921970”.
Esta sintaxe é útil apenas para commits de merge, que têm mais de um pai — o primeiro pai de um commit de merge é do branch em que você estava quando fez o merge (frequentemente master), enquanto o segundo pai de um commit de merge é do branch que foi mesclado (digamos, topic):
$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
Add some blame and merge stuff
$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date: Wed Dec 10 22:22:03 2008 +0000
Some rdoc changes
A outra principal especificação de ascendência é o ~ (til).
Isto também se refere ao primeiro pai, então HEAD~ e HEAD^ são equivalentes.
A diferença torna-se aparente quando você especifica um número.
HEAD~2 significa “o primeiro pai do primeiro pai” ou “o avô” — ele percorre os primeiros pais o número de vezes que você especificar.
Por exemplo, no histórico listado anteriormente, HEAD~3 seria:
$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
Ignore *.gem
Isso também pode ser escrito como HEAD~~~, que novamente é o primeiro pai do primeiro pai do primeiro pai:
$ git show HEAD~~~
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
Ignore *.gem
Você também pode combinar essas sintaxes — você pode obter o segundo pai da referência anterior (supondo que tenha sido um commit de merge) usando HEAD~3^2, e assim por diante.
Intervalos (Ranges) de Commits
Agora que você pode especificar commits individuais, vamos ver como especificar intervalos de commits. Isso é particularmente útil para gerenciar seus branches — se você tem muitos branches, pode usar as especificações de intervalo para responder a perguntas como: “Qual trabalho está neste branch que eu ainda não mesclei (merged) no meu branch principal?”
Ponto Duplo (Double Dot)
A especificação de intervalo mais comum é a sintaxe de ponto duplo. Isto basicamente pede ao Git para resolver um intervalo de commits que são alcançáveis a partir de um commit, mas não são alcançáveis a partir de outro. Por exemplo, digamos que você tenha um histórico de commits parecido com Exemplo de histórico para seleção de intervalo.
Digamos que você queira ver o que está no seu branch experiment que ainda não foi mesclado (merged) no seu branch master.
Você pode pedir ao Git para mostrar um log de apenas esses commits com master..experiment — isso significa “todos os commits alcançáveis a partir de experiment que não são alcançáveis a partir de master.”
Por uma questão de brevidade e clareza nesses exemplos, as letras dos objetos de commit do diagrama são usadas no lugar da saída de log real, na ordem em que seriam exibidas:
$ git log master..experiment
D
C
Se, por outro lado, você quiser ver o oposto — todos os commits no master que não estão no experiment — você pode inverter os nomes dos branches.
experiment..master mostra tudo o que está no master e não é alcançável a partir do experiment:
$ git log experiment..master
F
E
Isso é útil se você quiser manter o branch experiment atualizado e visualizar o que está prestes a mesclar.
Outro uso frequente dessa sintaxe é ver o que você está prestes a fazer push (enviar) para um repositório remoto:
$ git log origin/master..HEAD
Este comando mostra quaisquer commits no seu branch atual que não estão no branch master no seu repositório remoto origin.
Se você executar um git push e o seu branch atual estiver rastreando origin/master, os commits listados por git log origin/master..HEAD são os commits que serão transferidos para o servidor.
Você também pode omitir um lado da sintaxe para fazer com que o Git assuma HEAD.
Por exemplo, você pode obter os mesmos resultados do exemplo anterior digitando git log origin/master.. — o Git substitui HEAD se um dos lados estiver faltando.
Múltiplos Pontos
A sintaxe de ponto duplo é útil como um atalho, mas talvez você queira especificar mais do que dois branches para indicar a sua revisão, como ver quais commits estão em qualquer um de vários branches que não estão no branch em que você se encontra atualmente.
O Git permite que você faça isso usando o caractere ^ ou --not antes de qualquer referência da qual você não deseja ver os commits alcançáveis.
Assim, os três comandos a seguir são equivalentes:
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA
Isso é legal porque com essa sintaxe você pode especificar mais de duas referências na sua consulta, o que não pode fazer com a sintaxe de ponto duplo.
Por exemplo, se você quiser ver todos os commits que são alcançáveis a partir do refA ou refB mas não do refC, você pode usar qualquer um destes:
$ git log refA refB ^refC
$ git log refA refB --not refC
Isso cria um sistema de consulta de revisão muito poderoso que deve ajudá-lo a descobrir o que há nos seus branches.
Ponto Triplo (Triple Dot)
A última grande sintaxe de seleção de intervalo é a sintaxe de ponto triplo, que especifica todos os commits que são alcançáveis por qualquer uma das duas referências, mas não por ambas.
Veja novamente o histórico de commits de exemplo em Exemplo de histórico para seleção de intervalo.
Se você quiser ver o que está no master ou experiment, mas sem quaisquer referências comuns, você pode executar:
$ git log master...experiment
F
E
D
C
Mais uma vez, isso fornece a saída normal de log, mas mostra apenas as informações de commit para aqueles quatro commits, aparecendo na ordem tradicional de data de commit.
Um switch comum para usar com o comando log neste caso é o --left-right, que mostra em qual lado do intervalo cada commit está.
Isso ajuda a tornar a saída mais útil:
$ git log --left-right master...experiment
< F
< E
> D
> C
Com essas ferramentas, você pode informar ao Git de forma muito mais fácil qual ou quais commits você deseja inspecionar.