-
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
A2.2 Appendix B: Incorporando o Git nos seus Aplicativos - Libgit2
Libgit2
Outra opção à sua disposição é usar a Libgit2. A Libgit2 é uma implementação do Git sem dependências, com foco em oferecer uma boa API para uso dentro de outros programas. Você pode encontrá-la em https://libgit2.org.
Primeiro, vejamos como é a API em C. Eis um tour relâmpago:
// Open a repository
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");
// Dereference HEAD to a commit
git_object *head_commit;
error = git_revparse_single(&head_commit, repo, "HEAD^{commit}");
git_commit *commit = (git_commit*)head_commit;
// Print some of the commit's properties
printf("%s", git_commit_message(commit));
const git_signature *author = git_commit_author(commit);
printf("%s <%s>\n", author->name, author->email);
const git_oid *tree_id = git_commit_tree_id(commit);
// Cleanup
git_commit_free(commit);
git_repository_free(repo);
As primeiras linhas abrem um repositório Git.
O tipo git_repository representa um handle para um repositório com cache em memória.
Esse é o método mais simples, usado quando você conhece o caminho exato para o diretório de trabalho ou a pasta .git de um repositório.
Há também git_repository_open_ext, que inclui opções de busca; git_clone e funções relacionadas, para criar um clone local de um repositório remoto; e git_repository_init, para criar um repositório inteiramente novo.
O segundo trecho de código usa a sintaxe rev-parse (consulte Referências de Branch para saber mais) para obter o commit para o qual HEAD acaba apontando.
O tipo retornado é um ponteiro git_object, que representa algo existente no banco de dados de objetos Git de um repositório.
Na verdade, git_object é um tipo “parent” de vários tipos diferentes de objetos; o layout de memória de cada tipo “child” é igual ao de git_object, portanto você pode convertê-lo com segurança para o tipo correto.
Neste caso, git_object_type(commit) retornaria GIT_OBJ_COMMIT, portanto é seguro convertê-lo para um ponteiro git_commit.
O próximo pedaço de código mostra como acessar as propriedades do commit.
A última linha usa o tipo git_oid, que é a representação de um hash SHA-1 na Libgit2.
A partir deste exemplo, alguns padrões começam a surgir:
-
Se você declarar um ponteiro e passar uma referência a ele para uma chamada da Libgit2, essa chamada provavelmente retornará um código de erro inteiro. O valor
0indica sucesso; qualquer valor menor indica erro. -
Se a Libgit2 preencher um ponteiro para você, será sua responsabilidade liberá-lo.
-
Se a Libgit2 retornar um ponteiro
constde uma chamada, você não precisará liberá-lo, mas ele se tornará inválido quando o objeto ao qual pertence for liberado. -
Escrever em C é um pouco doloroso.
Esse último ponto significa que é pouco provável que você escreva em C ao usar a Libgit2. Felizmente, há vários bindings específicos de linguagem que facilitam bastante o trabalho com repositórios Git a partir da sua linguagem e do seu ambiente. Vejamos o exemplo anterior escrito com os bindings Ruby da Libgit2, chamados Rugged e disponíveis em https://github.com/libgit2/rugged.
repo = Rugged::Repository.new('path/to/repository')
commit = repo.head.target
puts commit.message
puts "#{commit.author[:name]} <#{commit.author[:email]}>"
tree = commit.tree
Como você pode ver, o código é muito menos carregado.
Primeiro, o Rugged usa exceções; ele pode lançar erros como ConfigError ou ObjectError para sinalizar condições de erro.
Segundo, não há liberação explícita de recursos, pois o Ruby possui coleta de lixo.
Vejamos um exemplo um pouco mais complexo: criar um commit do zero
blob_id = repo.write("Blob contents", :blob) # (1)
index = repo.index
index.read_tree(repo.head.target.tree)
index.add(:path => 'newfile.txt', :oid => blob_id) # (2)
sig = {
:email => "bob@example.com",
:name => "Bob User",
:time => Time.now,
}
commit_id = Rugged::Commit.create(repo,
:tree => index.write_tree(repo), # (3)
:author => sig,
:committer => sig, # (4)
:message => "Add newfile.txt", # (5)
:parents => repo.empty? ? [] : [ repo.head.target ].compact, # (6)
:update_ref => 'HEAD', # (7)
)
commit = repo.lookup(commit_id) # (8)
-
Crie um novo blob que contenha o conteúdo de um novo arquivo.
-
Preencha o index com a árvore do commit apontado por head e adicione o novo arquivo no caminho
newfile.txt. -
Isso cria uma nova árvore no ODB e a usa no novo commit.
-
Usamos a mesma assinatura nos campos de autor e committer.
-
A mensagem de commit.
-
Ao criar um commit, você precisa especificar os pais do novo commit. Isso usa a ponta de HEAD como o único pai.
-
O Rugged (e a Libgit2) pode, opcionalmente, atualizar uma referência ao criar um commit.
-
O valor de retorno é o hash SHA-1 de um novo objeto de commit, o qual você pode então usar para obter um objeto
Commit.
O código Ruby é simples e limpo, mas, como a Libgit2 faz o trabalho pesado, ele também será executado com bastante rapidez. Se você não programa em Ruby, abordamos outros bindings em Outros Bindings.
Funcionalidades Avançadas
O Libgit2 tem algumas capacidades que estão fora do escopo do núcleo do Git. Um exemplo é a plugabilidade: o Libgit2 permite que você providencie “backends” customizados para diversos tipos de operações, de modo que você possa armazenar as coisas de uma forma diferente daquela que o Git padrão faz. O Libgit2 permite backends customizados para a configuração, o armazenamento de referências, e a base de dados de objetos, entre outras coisas.
Vamos dar uma olhada em como isso funciona. O código abaixo foi emprestado a partir do conjunto de exemplos para backend providos pelo próprio time de atuantes no Libgit2 (os quais ali constam que podem ser achados em https://github.com/libgit2/libgit2-backends). Aqui está como um backend customizado para o banco de dados de objetos é configurado:
git_odb *odb;
int error = git_odb_new(&odb); // (1)
git_odb_backend *my_backend;
error = git_odb_backend_mine(&my_backend, /*…*/); // (2)
error = git_odb_add_backend(odb, my_backend, 1); // (3)
git_repository *repo;
error = git_repository_open(&repo, "some-path");
error = git_repository_set_odb(repo, odb); // (4)
Note que os erros são capturados, mas não tratados. Esperamos que o seu código seja melhor que o nosso.
-
Inicializa um “frontend,” vazio do banco de dados de objetos (ODB), que atuará como contêiner dos “backends” responsáveis pelo trabalho real.
-
Inicializa um backend de ODB customizado.
-
Adiciona o backend ao frontend.
-
Abre um repositório, e o configura para usar o nosso ODB para procurar por objetos.
But what is this git_odb_backend_mine thing?
Bem, esse é o construtor da sua própria implementação de ODB, e você pode fazer o que quiser nele, desde que preencha corretamente a estrutura git_odb_backend.
Esta é uma possível implementação:
typedef struct {
git_odb_backend parent;
// Some other stuff
void *custom_context;
} my_backend_struct;
int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/)
{
my_backend_struct *backend;
backend = calloc(1, sizeof (my_backend_struct));
backend->custom_context = …;
backend->parent.read = &my_backend__read;
backend->parent.read_prefix = &my_backend__read_prefix;
backend->parent.read_header = &my_backend__read_header;
// …
*backend_out = (git_odb_backend *) backend;
return GIT_SUCCESS;
}
A restrição mais sutil é que o primeiro membro de my_backend_struct` deve ser uma estrutura git_odb_backend; isso garante que a disposição da memória seja a esperada pelo código da Libgit2.
O restante é arbitrário; essa estrutura pode ser tão grande ou pequena quanto necessário.
A função de inicialização aloca um pouco de memória para a estrutura, configura o contexto customizado, e então preenche os membros da estrutura parent que ela suporta.
Dê uma olhada no arquivo include/git2/sys/odb_backend.h no código-fonte da Libgit2 para um conjunto completo de assinaturas de chamadas; seu caso de uso em particular ajudará a determinar quais dessas você irá querer suportar.
Outros Bindings
O Libgit2 possui bindings para muitas linguagens.
Aqui mostramos um pequeno exemplo usando alguns dos pacotes de bindings mais completos na data em que isto é escrito; bibliotecas existem para muitas outras linguagens, incluindo C++, Go, Node.js, Erlang, e a JVM, todas em vários estágios de maturidade.
A coleção oficial de bindings pode ser encontrada navegando pelos repositórios em https://github.com/libgit2.
O código que nós iremos escrever vai retornar a mensagem de commit a partir do commit que eventualmente seja apontado por HEAD (mais ou menos como no git log -1).
LibGit2Sharp
Se você estiver escrevendo um aplicativo de .NET ou Mono, LibGit2Sharp (https://github.com/libgit2/libgit2sharp) é o que você está procurando. Os bindings são escritos em C#, e foi tomado um grande cuidado para envolver (wrap) as chamadas brutas (raw) do Libgit2 com APIs de CLR com uma pegada bem nativa (native-feeling). Aqui temos como nosso programa de exemplo se parece:
new Repository(@"C:\path\to\repo").Head.Tip.Message;
Para aplicativos de Windows no desktop, existe até mesmo um pacote no NuGet que te ajudará a iniciar nisto tudo muito rapidamente.
objective-git
Se o seu aplicativo está rodando numa plataforma da Apple, é muito provável que você esteja usando o Objective-C enquanto sendo sua linguagem para implementação. Objective-Git (https://github.com/libgit2/objective-git) é o nome para os bindings da Libgit2 para este respectivo ambiente. O programa de exemplo se parece com isto:
GTRepository *repo =
[[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];
NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];
O Objective-git é totalmente interoperável com Swift, portanto não se preocupe se você já deixou o Objective-C para trás.
pygit2
Os bindings Python da Libgit2 são chamados Pygit2 e estão disponíveis em https://www.pygit2.org. Nosso programa de exemplo:
pygit2.Repository("/path/to/repo") # open repository
.head # get the current branch
.peel(pygit2.Commit) # walk down to the commit
.message # read the message
Leituras Adicionais
Naturalmente, uma abordagem completa dos recursos da Libgit2 está fora do escopo deste livro. Se quiser mais informações sobre a própria Libgit2, há documentação da API em https://libgit2.github.com/libgit2 e um conjunto de guias em https://libgit2.github.com/docs. Para os outros bindings, consulte o README e os testes incluídos; muitas vezes há pequenos tutoriais e indicações de leituras adicionais nesses arquivos.