Uma Referência Visual do Git

Outros Idiomas:

Esta página fornece uma breve referência visual para os comandos mais comuns do git. Uma vez que você saiba um pouco sobre como o git funciona, esta página pode consolidar o seu entendimento. Se você está interessado em saber como esta página foi criada, veja o meu repositório no GitHub.

Conteúdo

  1. Uso Básico
  2. Convenções
  3. Comandos em Detalhe
    1. Diff
    2. Commit
    3. Checkout
    4. Comitando com um HEAD detachado
    5. Reset
    6. Merge
    7. Cherry Pick
    8. Rebase
  4. Observações Técnicas

Uso Básico

Os quatro comandos acima copiam arquivos entre o diretório de trabalho, o stage (também chamado de índice), e o histórico (na forma de commits).

Você pode usar git reset -p, git checkout -p, ou git add -p em vez de (ou além de) especificar arquivos para selecionar interativamente partes de arquivos para copiar.

Também é possível passar por cima do stage e copiar (checkout) arquivos diretamente do histórico ou de commits sem copiar o aquivo para o stage.

Convenções

No restante deste documento, nós vamos usar gráficos no seguinte formato.

commits são mostrados em verde com uma identidade de 5 caracteres, e eles apontam para os seus pais (parents). Os ramos (branches) são mostrados em laranja, e eles apontam para commits específicos. O ramo atual é identificado pela referência especial HEAD, que está unida àquele ramo. Nessa imagem, os últimos cinco commits são mostrados, sendo ed489 o mais recente. main (o ramo atual) aponta para esse commit, enquanto stable (outro ramo) aponta para um ancestral do commit do main.

Comandos em Detalhe

Diff

Existem várias formas de ver as diferenças entre commits. Abaixo estão alguns exemplos comuns. Qualquer desses comandos pode opcionalmente receber nomes de arquivos como argumentos que restringem as diferenças a esses arquivos.

Commit

Quando você comita, o git cria um novo commit usando os arquivos do stage e define os pais como o commit atual. Então ele aponta o ramo atual para esse novo commit. Na figura abaixo, o ramo atual é o main. Antes do comando ser executado, main apontava para ed489. Após, um novo commit, f0cec, foi criado, com ancestral ed489, e então main foi movido para o novo commit.

Esse mesmo processo ocorre também quando o ramo atual é um ancestral de outro ramo. Abaixo, um commit ocorre no ramo stable, o qual era um ancestral de main, resultando em 1800b. Após, stable não é mais um ancestral de main. Para juntar as duas histórias, um merge (ou rebase) será necessário.

As vezes um engano ocorre em um commit, mas isso é fácil de corrigir com git commit --amend. Quando você usa esse comando, o git cria um novo commit com os mesmos pais do commit atual. (O commit antigo será descartado se nada fizer referência a ele.)

Um quarto caso é comitar com o HEAD detachado, como explicado mais tarde.

Checkout

O comando checkout é usado para copiar arquivos do histórico, ou stage, para o diretório de trabalho, e opcionalmente mudar de ramo.

Quando um nome de arquivo (e/ou -p) é fornecido, o git copia esse arquivo do commit para o stage e para o diretório de trabalho. Por exemplo, git checkout HEAD~ foo.c copia o arquivo foo.c do commit chamado HEAD~ (os pais do commit atual) para o diretório de trabalho, e também para o stage. (Se nenhum commit é fornecido, os arquivos são copiados do stage.) Note que não há mudança no ramo.

Quando um nome de arquivo não é fornecido mas a referência é um ramo (local), HEAD é movido para aquele ramo (isto é, nós passamos para aquele ramo), e então o stage e o diretório de trabalho são modificados para coincidir com o conteúdo daquele commit. Qualquer arquivo que existe no novo commit (a47c3 abaixo) é copiado; qualquer arquivo que existe no antigo commit (ed489) mas não no novo commit é excluído; e qualquer arquivo que não existe em ambos é ignorado.

Quando um nome de arquivo não é fornecido e a referência não é um ramo (local) — por exemplo, é uma etiqueta (tag), um ramo remoto, uma identidade SHA-1, ou algo como main~3 — nós obtemos um ramo anônimo, chamado HEAD detachado. Isso é útil para se mover ao longo do histórico. Por exemplo, suponha que você queira compilar a versão 1.6.6.1 do git. Você poder executar git checkout v1.6.6.1 (que é uma etiqueta, não um ramo), compilar, instalar, e então passar de volta para outro ramo, por exemplo executado git checkout main. Todavia, efetuar um commit funciona um pouco diferente em um HEAD detachado; isso é discutido abaixo.

Comitando com um HEAD detachado

Quando o HEAD está detachado, comitar funciona da maneira usual, exceto que nenhum ramo com nome é modificado. (Você pode pensar nisso como um ramo anônimo.)

Uma vez que você fizer um checkout de alguma coisa, por exemplo main, o commit (presumivelmente) não recebe outra referência, e acaba excluído. Note que após o comando, não há nada fazendo referência para 2eecb.

Se, por outro lado, você quiser salvar esse estado, você pode criar um novo ramo com nome usando git checkout -b nome.

Reset

O comando reset move o ramo atual para uma nova posição, e opcionalmente atualiza o stage e o diretório de trabalho. Ele também é usado para copiar arquivos do histórico para o stage sem alterar o diretório de trabalho.

Se um commit é realizado sem um nome de arquivo, o ramo atual é movido para aquele commit, e então o stage é atualizado para coincidir com esse commit. Se --hard é fornecido, o diretório de trabalho também é atualizado. Se --soft é fornecido, nem o stage nem o diretório de trabalho são atualizados.

Se um commit não é fornecido, será usado o HEAD. Nesse caso, o ramo não é alterado, mas o stage, e opcionalmente o diretório de trabalho se --hard é fornecido, são atualizados com o conteúdo do último commit.

Se um nome de arquivo (e/ou -p) é fornecido, então o comando funciona similarmente ao comando checkout com um nome de arquivo, exceto que apenas o stage (e não o diretório de trabalho) é atualizado. (Você pode também especificar o commit a partir do qual copiar os arquivos, em vez de HEAD.)

Merge

Um merge cria um novo commit que incorpora mudanças de outros commits. Antes de executar um merge, o stage deve estar igual ao último commit. O caso trivial é quando o outro commit é um ancestral do commit atual; em tal caso nada ocorre. O próximo caso mais simples é quando o commit atual é um ancestral do outro commit. Isso resulta em um merge fast-forward. A referência é simplesmente movida, e então é efetuado um checkout do novo commit.

Caso contrário, um merge "real" deve ocorrer. Você pode selecionar outras estratégias, mas o padrão é efetuar um merge "recursivo", o qual basicamente considera o commit atual (ed489 abaixo), o outro commit (33104), e o ancestral comum a ambos (b325c), e efetua um merge three-way. O resultado é salvo no diretório de trabalho e no stage, e então um commit é executado, com um ancestral adicional (33104) para o novo commit.

Cherry Pick

O comando cherry-pick "copia" um commit, criando um novo commit no ramo atual com a mesma mensagem e modificações de outro commit.

Rebase

Um rebase é uma alternativa a um merge para combinar vários ramos. Enquanto um merge cria um único commit com dois pais, gerando um histórico não-linear, um rebase efetua os commits do ramo atual em outro ramo, gerando um histórico linear. Em essência, isso é uma forma automática de executar vários cherry-picks em sequência.

O comando acima considera todos os commits que existem em topic mas não em main (a saber 169a6 e 2c33a), executa esses commits em main, e então move o HEAD para o novo commit. Note que os commits antigos serão descartados se nada mais fizer referência a eles.

Para limitar quanto se quer ir para trás, use a opção --onto. O seguinte comando executa em main os commits mais recentes do ramo atual desde 169a6 (exclusivamente), a saber 2c33a.

Existe também o comando git rebase --interactive, o qual permite fazer coisas mais complicadas do que simplesmente executar novamente commits, a saber, remover, reordenar, modificar, e "amassar" commits (squashing). Não existe uma figura clara para representar isso; veja git-rebase(1) para mais detalhes.

Observações técnicas

O conteúdo dos arquivos não é na verdade armazenado no index (.git/index) ou em objetos commit. Em vez disso, cada arquivo é armazenado na base-de-dados de objetos (.git/objects) como um blob, identificado pelo seu código hash SHA-1. O arquivo index lista os nomes de arquivos juntamente com o identificador do blob associado, bem como alguns outros dados. Para commits, existe um tipo de dado adicional, uma árvore, também identificado pelo seu código hash. Árvores correspondem aos diretórios no diretório de trabalho, e contém uma lista das árvores e blobs correspondentes a cada nome de arquivo naquele diretório. Cada commit armazena o identificador da sua árvore, que por sua vez contém todos os blobs e outras árvores associadas àquele commit.

Se você realiza um commit usando um HEAD detachado, o último commit é na verdade referenciado por algo: o reflog para o HEAD. Todavia, esse possui data de validade, logo em algum momento posterior o commit será finalmente excluído, de forma similar aos commits excluídos com git commit --amend ou git rebase.


Copyright © 2010, Mark Lodato. Portuguese translation © 2014, Gustavo de Oliveira

Este trabalho está sob a licença Atribuição-NãoComercial-CompartilhaIgual 3.0 Estados Unidos.

Gostaria de traduzir para outro idioma?