# Playing around with gitoxide - an implementation of git in Rust


## Background

There's `git` which is a CLI tool implementing `git` SCM which we all know and love.

Additionally, there's [libgit2](https://libgit2.org/) which is a reference
implementation of git "standard"/"protocol".  The focus of it, is primarily to
be used within applications that wish to integrate with git repositories and
provide programmatic access to git facilities.

Usually, languages like Python or Go use [libgit2](https://libgit2.org/) under
the hood to implement git bindings.  This is the case with e.g.
[pygit2](https://github.com/libgit2/pygit2) or
[git2go](https://github.com/libgit2/git2go) and many more.

Recently, I stumbled upon [gitoxide](https://github.com/Byron/gitoxide).  Which,
as I initially thought, would cultivate exactly the same approach, meaning it would
use `libgit2` as low level routines for git, but this doesn't seem to be the
case.

First thing that comes to mind is ... why?  Reading through project goals, the aspiration is to:

> ... be the go-to implementation for anyone who wants to solve problems around
> git, and become the alternative to GitPython and libgit2 in the process.

There's a whole slew of questions I might have behind this motivation, but I'm
gonna spare myself the effort.  Regardless of all of that I decided to give it,
at least, some sort of superficial valuation.


## Benchmarks

`gix` is not yet feature complete so there's a limited set of things that can
be tested.  Cloning a repository is supported though and its performance might
be compared between `git` and `gix` so, this is what I'm going to do.

For the purpose of this exercise I've clone [Linux
kernel](https://github.com/torvalds/linux) repo (to eliminate any networking
related aspects) like so:

    git clone --bare --progress https://github.com/torvalds/linux.git

Bare in mind as well that I'm using a relatively slow SATA SSD so the numbers
might seem high.

### git clone

Starting with `git` I get a bit of a weird results.

```bash
time git clone --bare linux linux_git.git
Cloning into bare repository 'linux_git.git'...
done.

real	0m0.079s
user	0m0.037s
sys	0m0.037s
```

This is too good to be true as it's physically impossible to copy ~5G worth of
data on my machine that quickly.  So, what has actually happened?

Turns out that `git` implements an optimisation when cloning a local repository
and instead of copying the data from `.git/objects` it just creates hard links.

Listing the contents of the new clone, sure enough, `ls` reports link count > 1
for majority of files.

```bash
2056:hermod linux_git.git 0 (BARE:master) $ ls -l .
total 116
drwxr-xr-x 2 tomasz tomasz  4096 Dec 27 13:30 branches
-rw-r--r-- 1 tomasz tomasz   123 Dec 27 13:30 config
-rw-r--r-- 1 tomasz tomasz    73 Dec 27 13:30 description
-rw-r--r-- 1 tomasz tomasz    23 Dec 27 13:30 HEAD
drwxr-xr-x 2 tomasz tomasz  4096 Dec 27 13:30 hooks
drwxr-xr-x 2 tomasz tomasz  4096 Dec 27 13:30 info
drwxr-xr-x 4 tomasz tomasz  4096 Dec 27 13:30 objects
-rw-r--r-- 1 tomasz tomasz 84343 Dec 27 13:30 packed-refs
drwxr-xr-x 4 tomasz tomasz  4096 Dec 27 13:30 refs
```

This can be further confirmed by inspecting the `inode` number of an arbitrarily
chosen object between two repositories:

```bash
stat linux{,_git}.git/objects/pack/pack-ff01ee2f2c6db77ba0e85e66b1fd277cf2545546.idx | grep Inode
Device: 254,2	Inode: 1058406     Links: 2
Device: 254,2	Inode: 1058406     Links: 2
```

As expected both point to the same `inode`.  This behaviour can be disabled
with `--no-hardlinks` flag.  Let's try that.

```bash
$ time git clone --no-hardlinks --bare linux.git linux_git2.git
Cloning into bare repository 'linux_git2.git'...
done.

real	0m11.134s
user	0m1.058s
sys	0m5.874s
```

This result is comparable with `cp -r` or `rsync -a` so, it seems to be more
reliable and useful as a baseline for comparison.

### gix clone

Installation of `gitoxide` is easy in my case as it is available in Arch so I need `pacman` to do it.

I will use version `0.32.0`.

Let's perform the same test as with `git`.

```bash
$ time gix clone --bare linux.git/ linux_gix.git
 13:48:44 indexing done 9.9M objects in 142.39s (69.3k objects/s)                                                                      
 13:48:44 decompressing done 12.4GB in 142.39s (87.4MB/s)                                                                              
 13:51:47     Resolving done 9.9M objects in 183.17s (53.9k objects/s)                                                                 
 13:51:47      Decoding done 129.9GB in 183.17s (709.3MB/s)                                                                            
 13:51:55 writing index file done 334.4MB in 1.36s (245.6MB/s)                                                                         
 13:51:55  create index file done 9.9M objects in 333.32s (29.6k objects/s)                                                            
 13:51:55          read pack done 5.0GB in 614.66s (8.1MB/s)                                                                           
HEAD:refs/remotes/origin/HEAD (implicit)
        fbafc3e621c3f4ded43720fdb1d6ce1728ec664e HEAD symref-target:refs/heads/master -> refs/remotes/origin/HEAD [new]                
+refs/heads/*:refs/remotes/origin/*                                                                                                    
	d566ec94e63702a1bf83f43804b021a8f478a27b refs/heads/dependabot/pip/drivers/gpu/drm/ci/xfails/pip-23.3 -> refs/remotes/origin/dependabot/pip/drivers/gpu/drm/ci/xfails/pip-23.3 [new]
	fbafc3e621c3f4ded43720fdb1d6ce1728ec664e refs/heads/master -> refs/remotes/origin/master [new]
refs/tags/*:refs/tags/* (implicit, due to auto-tag)
	5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11 object:c39ae07f393806ccf406ef966e9a15afc43cc36a -> refs/tags/v2.6.11 [new]
	5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree object:c39ae07f393806ccf406ef966e9a15afc43cc36a -> refs/tags/v2.6.11-tree [new]
    ...
	adab409b5eb1c5905c260f74c75725db3da46e38 refs/tags/v6.7-rc7 object:861deac3b092f37b2c5e6871732f3e11486f7082 -> refs/tags/v6.7-rc7 [new]

real	10m15.549s
user	14m13.188s
sys	0m10.899s
```

This is very disappointing.  We're talking about ~60x time difference.  Judging
by the log it seems like `gix` re-packaged all objects when cloning - this is
something that `git` probably doesn't do.

## Conclusion

I'm not sure if `gitoxide` will ever become a viable rust based alternative to
`libgit2`.  There's definitely a lot of effort and time required to bring the
project to maturity.  As of now, I'm happy to stick with `libgit2` rust
bindings like e.g. [git2-rs](https://github.com/rust-lang/git2-rs).  `gix` as a
CLI tool utilising `gitoxide` is definitely not yet there to be a true
competitor with `git`.

