Skip to content

Commit

Permalink
Update /book/B-embedding-git/sections/
Browse files Browse the repository at this point in the history
  • Loading branch information
git-em authored and sergiocabral committed Sep 8, 2021
1 parent f6838a3 commit 246f72e
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 29 deletions.
44 changes: 44 additions & 0 deletions book/B-embedding-git/sections/dulwich.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
=== Dulwich

(((Dulwich)))(((Python)))
There is also a pure-Python Git implementation - Dulwich.
The project is hosted under https://www.dulwich.io/
It aims to provide an interface to git repositories (both local and remote) that doesn't call out to git directly but instead uses pure Python.
It has an optional C extensions though, that significantly improve the performance.

Dulwich follows git design and separate two basic levels of API: plumbing and porcelain.

Here is an example of using the lower level API to access the commit message of the last commit:

[source, python]
----
from dulwich.repo import Repo
r = Repo('.')
r.head()
# '57fbe010446356833a6ad1600059d80b1e731e15'
c = r[r.head()]
c
# <Commit 015fc1267258458901a94d228e39f0a378370466>
c.message
# 'Add note about encoding.\n'
----

To print a commit log using high-level porcelain API, one can use:

[source, python]
----
from dulwich import porcelain
porcelain.log('.', max_entries=1)
#commit: 57fbe010446356833a6ad1600059d80b1e731e15
#Author: Jelmer Vernooij <[email protected]>
#Date: Sat Apr 29 2017 23:57:34 +0000
----


==== Further Reading

* The official API documentation is available at https://www.dulwich.io/docs/api/[].
* Official tutorial at https://www.dulwich.io/docs/tutorial[] has many examples of how to do specific tasks with Dulwich.
83 changes: 83 additions & 0 deletions book/B-embedding-git/sections/go-git.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
=== go-git

(((go-git)))(((Go)))
In case you want to integrate Git into a service written in Golang, there also is a pure Go library implementation.
This implementation does not have any native dependencies and thus is not prone to manual memory management errors.
It is also transparent for the standard Golang performance analysis tooling like CPU, Memory profilers, race detector, etc.

go-git is focused on extensibility, compatibility and supports most of the plumbing APIs, which is documented at https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md[].

Here is a basic example of using Go APIs:

[source, go]
----
import "github.com/go-git/go-git/v5"
r, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{
URL: "https://github.com/go-git/go-git",
Progress: os.Stdout,
})
----

As soon as you have a `Repository` instance, you can access information and perform mutations on it:

[source, go]
----
// retrieves the branch pointed by HEAD
ref, err := r.Head()
// get the commit object, pointed by ref
commit, err := r.CommitObject(ref.Hash())
// retrieves the commit history
history, err := commit.History()
// iterates over the commits and print each
for _, c := range history {
fmt.Println(c)
}
----

==== Advanced Functionality

go-git has few notable advanced features, one of which is a pluggable storage system, which is similar to Libgit2 backends.
The default implementation is in-memory storage, which is very fast.

[source, go]
----
r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
URL: "https://github.com/go-git/go-git",
})
----

Pluggable storage provides many interesting options.
For instance, https://github.com/go-git/go-git/tree/master/_examples/storage[] allows you to store references, objects, and configuration in an Aerospike database.

Another feature is a flexible filesystem abstraction.
Using https://pkg.go.dev/github.com/go-git/go-billy/v5?tab=doc#Filesystem[] it is easy to store all the files in different way i.e by packing all of them to a single archive on disk or by keeping them all in-memory.

Another advanced use-case includes a fine-tunable HTTP client, such as the one found at https://github.com/go-git/go-git/blob/master/_examples/custom_http/main.go[].

[source, go]
----
customClient := &http.Client{
Transport: &http.Transport{ // accept any certificate (might be useful for testing)
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: 15 * time.Second, // 15 second timeout
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // don't follow redirect
},
}
// Override http(s) default protocol to use our custom client
client.InstallProtocol("https", githttp.NewClient(customClient))
// Clone repository using the new client if the protocol is https://
r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: url})
----

==== Further Reading

A full treatment of go-git's capabilities is outside the scope of this book.
If you want more information on go-git, there's API documentation at https://pkg.go.dev/github.com/go-git/go-git/v5[], and a set of usage examples at https://github.com/go-git/go-git/tree/master/_examples[].
16 changes: 8 additions & 8 deletions book/B-embedding-git/sections/jgit.asc
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
=== JGit

(((jgit)))(((java)))
(((jgit)))(((Java)))
If you want to use Git from within a Java program, there is a fully featured Git library called JGit.
JGit is a relatively full-featured implementation of Git written natively in Java, and is widely used in the Java community.
The JGit project is under the Eclipse umbrella, and its home can be found at http://www.eclipse.org/jgit[].
The JGit project is under the Eclipse umbrella, and its home can be found at https://www.eclipse.org/jgit/[].

==== Getting Set Up

Expand All @@ -19,10 +19,10 @@ Probably the easiest is to use Maven – the integration is accomplished by addi
</dependency>
----

The `version` will most likely have advanced by the time you read this; check http://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[] for updated repository information.
The `version` will most likely have advanced by the time you read this; check https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[] for updated repository information.
Once this step is done, Maven will automatically acquire and use the JGit libraries that you'll need.

If you would rather manage the binary dependencies yourself, pre-built JGit binaries are available from http://www.eclipse.org/jgit/download[].
If you would rather manage the binary dependencies yourself, pre-built JGit binaries are available from https://www.eclipse.org/jgit/download[].
You can build them into your project by running a command like this:

[source,console]
Expand Down Expand Up @@ -91,13 +91,13 @@ String name = cfg.getString("user", null, "name");
There's quite a bit going on here, so let's go through it one section at a time.

The first line gets a pointer to the `master` reference.
JGit automatically grabs the _actual_ master ref, which lives at `refs/heads/master`, and returns an object that lets you fetch information about the reference.
JGit automatically grabs the _actual_ `master` ref, which lives at `refs/heads/master`, and returns an object that lets you fetch information about the reference.
You can get the name (`.getName()`), and either the target object of a direct reference (`.getObjectId()`) or the reference pointed to by a symbolic ref (`.getTarget()`).
Ref objects are also used to represent tag refs and objects, so you can ask if the tag is ``peeled,'' meaning that it points to the final target of a (potentially long) string of tag objects.
Ref objects are also used to represent tag refs and objects, so you can ask if the tag is "`peeled,`" meaning that it points to the final target of a (potentially long) string of tag objects.

The second line gets the target of the `master` reference, which is returned as an ObjectId instance.
ObjectId represents the SHA-1 hash of an object, which might or might not exist in Git's object database.
The third line is similar, but shows how JGit handles the rev-parse syntax (for more on this, see <<ch07-git-tools#r_branch_references>>); you can pass any object specifier that Git understands, and JGit will return either a valid ObjectId for that object, or `null`.
The third line is similar, but shows how JGit handles the rev-parse syntax (for more on this, see <<ch07-git-tools#_branch_references>>); you can pass any object specifier that Git understands, and JGit will return either a valid ObjectId for that object, or `null`.

The next two lines show how to load the raw contents of an object.
In this example, we call `ObjectLoader.copyTo()` to stream the contents of the object directly to stdout, but ObjectLoader also has methods to read the type and size of an object, as well as return it as a byte array.
Expand Down Expand Up @@ -128,7 +128,7 @@ Git git = new Git(repo);
----

The Git class has a nice set of high-level _builder_-style methods that can be used to construct some pretty complex behavior.
Let's take a look at an example doing something like `git ls-remote`:
Let's take a look at an example -- doing something like `git ls-remote`:

[source,java]
----
Expand Down
38 changes: 17 additions & 21 deletions book/B-embedding-git/sections/libgit2.asc
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
(((libgit2)))((("C")))
Another option at your disposal is to use Libgit2.
Libgit2 is a dependency-free implementation of Git, with a focus on having a nice API for use within other programs.
You can find it at http://libgit2.github.com[].
You can find it at https://libgit2.org[].

First, let's take a look at what the C API looks like.
Here's a whirlwind tour:

[source,c]
-----
----
// Open a repository
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");
Expand All @@ -28,16 +28,16 @@ const git_oid *tree_id = git_commit_tree_id(commit);
// Cleanup
git_commit_free(commit);
git_repository_free(repo);
-----
----

The first couple of lines open a Git repository.
The `git_repository` type represents a handle to a repository with a cache in memory.
This is the simplest method, for when you know the exact path to a repository's working directory or `.git` folder.
There's also the `git_repository_open_ext` which includes options for searching, `git_clone` and friends for making a local clone of a remote repository, and `git_repository_init` for creating an entirely new repository.

The second chunk of code uses rev-parse syntax (see <<ch07-git-tools#r_branch_references>> for more on this) to get the commit that HEAD eventually points to.
The second chunk of code uses rev-parse syntax (see <<ch07-git-tools#_branch_references>> for more on this) to get the commit that HEAD eventually points to.
The type returned is a `git_object` pointer, which represents something that exists in the Git object database for a repository.
`git_object` is actually a ``parent'' type for several different kinds of objects; the memory layout for each of the ``child'' types is the same as for `git_object`, so you can safely cast to the right one.
`git_object` is actually a "`parent`" type for several different kinds of objects; the memory layout for each of the "`child`" types is the same as for `git_object`, so you can safely cast to the right one.
In this case, `git_object_type(commit)` would return `GIT_OBJ_COMMIT`, so it's safe to cast to a `git_commit` pointer.

The next chunk shows how to access the commit's properties.
Expand Down Expand Up @@ -66,7 +66,7 @@ tree = commit.tree
----

As you can see, the code is much less cluttered.
Firstly, Rugged uses exceptions; it can raise things like `ConfigError` or `ObjectError` to signal error conditions.
Firstly, Rugged uses exceptions; it can raise things like `ConfigError` or `ObjectError` to signal error conditions.
Secondly, there's no explicit freeing of resources, since Ruby is garbage-collected.
Let's take a look at a slightly more complicated example: crafting a commit from scratch

Expand Down Expand Up @@ -106,13 +106,12 @@ commit = repo.lookup(commit_id) # <8>
<8> The return value is the SHA-1 hash of a new commit object, which you can then use to get a `Commit` object.

The Ruby code is nice and clean, but since Libgit2 is doing the heavy lifting, this code will run pretty fast, too.
If you're not a rubyist, we touch on some other bindings in <<r_libgit2_bindings>>.

If you're not a rubyist, we touch on some other bindings in <<_libgit2_bindings>>.

==== Advanced Functionality

Libgit2 has a couple of capabilities that are outside the scope of core Git.
One example is pluggability: Libgit2 allows you to provide custom ``backends'' for several types of operation, so you can store things in a different way than stock Git does.
One example is pluggability: Libgit2 allows you to provide custom "`backends`" for several types of operation, so you can store things in a different way than stock Git does.
Libgit2 allows custom backends for configuration, ref storage, and the object database, among other things.

Let's take a look at how this works.
Expand All @@ -131,12 +130,12 @@ 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(odb); // <4>
error = git_repository_set_odb(repo, odb); // <4>
----

_(Note that errors are captured, but not handled. We hope your code is better than ours.)_
_Note that errors are captured, but not handled. We hope your code is better than ours._

<1> Initialize an empty object database (ODB) ``frontend,'' which will act as a container for the ``backends'' which are the ones doing the real work.
<1> Initialize an empty object database (ODB) "`frontend,`" which will act as a container for the "`backends`" which are the ones doing the real work.
<2> Initialize a custom ODB backend.
<3> Add the backend to the frontend.
<4> Open a repository, and set it to use our ODB to look up objects.
Expand Down Expand Up @@ -179,15 +178,14 @@ The rest of it is arbitrary; this structure can be as large or small as you need
The initialization function allocates some memory for the structure, sets up the custom context, and then fills in the members of the `parent` structure that it supports.
Take a look at the `include/git2/sys/odb_backend.h` file in the Libgit2 source for a complete set of call signatures; your particular use case will help determine which of these you'll want to support.

[[r_libgit2_bindings]]
[[_libgit2_bindings]]
==== Other Bindings

Libgit2 has bindings for many languages.
Here we show a small example using a few of the more complete bindings packages as of this writing; libraries exist for many other languages, including C++, Go, Node.js, Erlang, and the JVM, all in various stages of maturity.
The official collection of bindings can be found by browsing the repositories at https://github.com/libgit2[].
The code we'll write will return the commit message from the commit eventually pointed to by HEAD (sort of like `git log -1`).


===== LibGit2Sharp

(((.NET)))(((C#)))(((Mono)))
Expand All @@ -196,9 +194,9 @@ The bindings are written in C#, and great care has been taken to wrap the raw Li
Here's what our example program looks like:

[source,csharp]
-----
----
new Repository(@"C:\path\to\repo").Head.Tip.Message;
-----
----

For desktop Windows applications, there's even a NuGet package that will help you get started quickly.

Expand All @@ -210,19 +208,18 @@ Objective-Git (https://github.com/libgit2/objective-git[]) is the name of the Li
The example program looks like this:

[source,objc]
-----
----
GTRepository *repo =
[[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];
NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];
-----
----

Objective-git is fully interoperable with Swift, so don't fear if you've left Objective-C behind.


===== pygit2

(((Python)))
The bindings for Libgit2 in Python are called Pygit2, and can be found at http://www.pygit2.org/[].
The bindings for Libgit2 in Python are called Pygit2, and can be found at https://www.pygit2.org[].
Our example program:

[source,python]
Expand All @@ -233,7 +230,6 @@ pygit2.Repository("/path/to/repo") # open repository
.message # read the message
----


==== Further Reading

Of course, a full treatment of Libgit2's capabilities is outside the scope of this book.
Expand Down

0 comments on commit 246f72e

Please sign in to comment.