Branches

Branches are a key feature of many VCS systems. They allow users to work in parallel without making changes that step on each other's toes.

The branching model in Oxen is inspired by git meaning branches are lightweight and quick to create. When creating a branch, we are never copying any of the raw datasets in the repository. Under the hood, a branch is really just a named reference to a commit. Creating a new branch simply creates a new named reference.

#![allow(unused)]
fn main() {
pub struct Branch {
    pub name: String,
    pub commit_id: String,
}
}

On the first commit of a repository, a default branch called main is created and points to the initial commit.

Refs

To see how this works in practice, let's look at how branches are stored on disk. All of the branches within a repository are stored in a key-value rocksdb database. This database can be found in the .oxen/refs directory.

Let's inspect this database with our oxen db list command.

$ oxen db list .oxen/refs

main	c719c887cc250784

This shows us that there is a single branch, main, that points to the commit id c719c887cc250784.

If we create a new branch, say foo, it will also be stored in the database with the same commit id as the current branch you are on.

$ oxen checkout -b foo
$ oxen db list .oxen/refs

main	c719c887cc250784
foo	c719c887cc250784

To see the list of current branches as well as which one you currently have checked out, you can use the oxen branch command.

$ oxen branch

* foo
  main

The * indicates the foo branch is currently checked out. The way we store the current branch is by creating a HEAD file in the .oxen directory.

This file contains the name of the branch or commit id that is currently checked out.

$ cat .oxen/HEAD

foo

Let's make a commit and see how the branches stored on disk change.

$ echo "foo" > foo.txt
$ oxen add foo.txt
$ oxen commit -m "foo commit"

Committing with message: foo commit
Commit 9ef4176b1b4422a7 done.

We now have a new commit id 9ef4176b1b4422a7. If we look at the refs database, we can see that the foo branch has been updated to point to the new commit id.

$ oxen db list .oxen/refs

foo	9ef4176b1b4422a7
main	c719c887cc250784

If we look at oxen log we will see that the foo branch is now the most recent commit.

commit 9ef4176b1b4422a7

Author: Ox Bot
Date:   Thursday, 30 May 2024 04:04:53 +00

    foo commit

commit c719c887cc250784

Author: Ox Bot
Date:   Tuesday, 28 May 2024 03:03:49 +00

    adding questions.jsonl

You can checkout a specific commit by using the oxen checkout command with the commit id.

$ oxen checkout c719c887cc250784

This will update the HEAD file to point to the commit id instead of the branch name.

$ cat .oxen/HEAD

c719c887cc250784

You will notice that our foo.txt file is no longer present in the working directory. If you perform a oxen status you will see that we are now in a "detached HEAD" state. This means that we are no longer on a branch and are instead on an individual commit.

Don't worry, the file foo.txt is still alive and well in the .oxen/versions directory, and can be restored by checking out the foo branch again.

$ oxen checkout foo

That's it! The relationship between branches, commits, and the HEAD commit is really that simple. Branches are just a named reference to a commit id that make it easier to find a particular chain of commits.

You can progress a branch as many commits as you want without affecting the main branch. When you are ready to merge your branch into the main branch, you can use the oxen merge command which will be covered later.

Next Up: Files & Directories

Now that you know the basic data structures for branches and commits, let's dive into how branches and commits are tied to a set of files and directories with the Merkle Tree data structure.

Next Up: Merkle Trees