The repository

After this introduction, let’s start directly with code:

>>> from dulwich.repo import Repo

The access to a repository is through the Repo object. You can open an existing repository or you can create a new one. There are two types of Git repositories:

Regular Repositories – They are the ones you create using git init and you daily use. They contain a .git folder.

Bare Repositories – There is no “.git” folder. The top-level folder contains itself the “branches”, “hooks”… folders. These are used for published repositories (mirrors). They do not have a working tree.

Creating a repository

Let’s create a folder and turn it into a repository, like git init would:

>>> from os import mkdir
>>> import sys
>>> mkdir("myrepo")
>>> repo = Repo.init("myrepo")
>>> repo
<Repo at 'myrepo'>

You can already look at the structure of the “myrepo/.git” folder, though it is mostly empty for now.

Opening an existing repository

To reopen an existing repository, simply pass its path to the constructor of Repo:

>>> repo = Repo("myrepo")
>>> repo
<Repo at 'myrepo'>

Opening the index

The index is used as a staging area. Once you do a commit, the files tracked in the index will be recorded as the contents of the new commit. As mentioned earlier, only non-bare repositories have a working tree, so only non-bare repositories will have an index, too. To open the index, simply call:

>>> index = repo.open_index()
>>> print(index.path)

Since the repository was just created, the index will be empty:

>>> list(index)

Staging new files

The repository allows “staging” files. Only files can be staged - directories aren’t tracked explicitly by git. Let’s create a simple text file and stage it:

>>> f = open('myrepo/foo', 'wb')
>>> _ = f.write(b"monty")
>>> f.close()

>>> repo.stage([b"foo"])

It will now show up in the index:

>>> print(",".join([f.decode(sys.getfilesystemencoding()) for f in repo.open_index()]))

Creating new commits

Now that we have staged a change, we can commit it. The easiest way to do this is by using Repo.do_commit. It is also possible to manipulate the lower-level objects involved in this, but we’ll leave that for a separate chapter of the tutorial.

To create a simple commit on the current branch, it is only necessary to specify the message. The committer and author will be retrieved from the repository configuration or global configuration if they are not specified:

>>> commit_id = repo.do_commit(
...     b"The first commit", committer=b"Jelmer Vernooij <>")

do_commit returns the SHA1 of the commit. Since the commit was to the default branch, the repository’s head will now be set to that commit:

>>> repo.head() == commit_id