empty for now, we'll start by adding a new file::
>>> from dulwich.objects import Blob
- >>> blob = Blob.from_string("My file content\n")
- >>> blob.id
- 'c55063a4d5d37aa1af2b2dad3a70aa34dae54dc6'
+ >>> blob = Blob.from_string(b"My file content\n")
+ >>> print(blob.id.decode('ascii'))
+ c55063a4d5d37aa1af2b2dad3a70aa34dae54dc6
Of course you could create a blob from an existing file using ``from_file``
instead.
>>> from dulwich.objects import Tree
>>> tree = Tree()
- >>> tree.add("spam", 0100644, blob.id)
+ >>> tree.add(b"spam", 0o100644, blob.id)
-Note that "0100644" is the octal form for a regular file with common
+Note that "0o100644" is the octal form for a regular file with common
permissions. You can hardcode them or you can use the ``stat`` module.
The tree state of our repository still needs to be placed in time. That's the
>>> from time import time
>>> commit = Commit()
>>> commit.tree = tree.id
- >>> author = "Your Name <your.email@example.com>"
+ >>> author = b"Your Name <your.email@example.com>"
>>> commit.author = commit.committer = author
>>> commit.commit_time = commit.author_time = int(time())
- >>> tz = parse_timezone('-0200')[0]
+ >>> tz = parse_timezone(b'-0200')[0]
>>> commit.commit_timezone = commit.author_timezone = tz
- >>> commit.encoding = "UTF-8"
- >>> commit.message = "Initial commit"
+ >>> commit.encoding = b"UTF-8"
+ >>> commit.message = b"Initial commit"
Note that the initial commit has no parents.
Now the physical repository contains three objects but still has no branch.
Let's create the master branch like Git would::
- >>> repo.refs['refs/heads/master'] = commit.id
+ >>> repo.refs[b'refs/heads/master'] = commit.id
The master branch now has a commit where to start. When we commit to master, we
are also moving HEAD, which is Git's currently checked out branch:
- >>> head = repo.refs['HEAD']
+ >>> head = repo.refs[b'HEAD']
>>> head == commit.id
True
- >>> head == repo.refs['refs/heads/master']
+ >>> head == repo.refs[b'refs/heads/master']
True
How did that work? As it turns out, HEAD is a special kind of ref called a
symbolic ref, and it points at master. Most functions on the refs container
work transparently with symbolic refs, but we can also take a peek inside HEAD:
- >>> repo.refs.read_ref('HEAD')
- 'ref: refs/heads/master'
+ >>> import sys
+ >>> print(repo.refs.read_ref(b'HEAD').decode(sys.getfilesystemencoding()))
+ ref: refs/heads/master
Normally, you won't need to use read_ref. If you want to change what ref HEAD
points to, in order to check out another branch, just use set_symbolic_ref.
Let's first build the blob::
>>> from dulwich.objects import Blob
- >>> spam = Blob.from_string("My new file content\n")
- >>> spam.id
- '16ee2682887a962f854ebd25a61db16ef4efe49f'
+ >>> spam = Blob.from_string(b"My new file content\n")
+ >>> print(spam.id.decode('ascii'))
+ 16ee2682887a962f854ebd25a61db16ef4efe49f
An alternative is to alter the previously constructed blob object::
- >>> blob.data = "My new file content\n"
- >>> blob.id
- '16ee2682887a962f854ebd25a61db16ef4efe49f'
+ >>> blob.data = b"My new file content\n"
+ >>> print(blob.id.decode('ascii'))
+ 16ee2682887a962f854ebd25a61db16ef4efe49f
In any case, update the blob id known as "spam". You also have the
opportunity of changing its mode::
- >>> tree["spam"] = (0100644, spam.id)
+ >>> tree[b"spam"] = (0o100644, spam.id)
Now let's record the change::
>>> c2 = Commit()
>>> c2.tree = tree.id
>>> c2.parents = [commit.id]
- >>> c2.author = c2.committer = "John Doe <john@example.com>"
+ >>> c2.author = c2.committer = b"John Doe <john@example.com>"
>>> c2.commit_time = c2.author_time = int(time())
>>> c2.commit_timezone = c2.author_timezone = 0
- >>> c2.encoding = "UTF-8"
- >>> c2.message = 'Changing "spam"'
+ >>> c2.encoding = b"UTF-8"
+ >>> c2.message = b'Changing "spam"'
In this new commit we record the changed tree id, and most important, the
previous commit as the parent. Parents are actually a list because a commit
You won't see it using git log because the head is still the previous
commit. It's easy to remedy::
- >>> repo.refs['refs/heads/master'] = c2.id
+ >>> repo.refs[b'refs/heads/master'] = c2.id
Now all git tools will work as expected.
>>> from dulwich.repo import Repo
>>> from dulwich.server import DictBackend, TCPGitServer
>>> import threading
- >>> repo = Repo.init("remote", mkdir=True)
+ >>> repo = Repo.init(b"remote", mkdir=True)
>>> cid = repo.do_commit(b"message", committer=b"Jelmer <jelmer@samba.org>")
>>> backend = DictBackend({b'/': repo})
- >>> dul_server = TCPGitServer(backend, 'localhost', 0)
+ >>> dul_server = TCPGitServer(backend, b'localhost', 0)
>>> threading.Thread(target=dul_server.serve).start()
- >>> server_address, server_port = dul_server.socket.getsockname()
+ >>> server_address, server_port=dul_server.socket.getsockname()
Remote repositories
===================
one manually::
>>> from dulwich.client import TCPGitClient
- >>> client = TCPGitClient(server_address, server_port)
+ >>> client = TCPGitClient(server_address.encode('ascii'), server_port)
Retrieving raw pack files
-------------------------
>>> class DummyGraphWalker(object):
... def ack(self, sha): pass
... def next(self): pass
+ ... def __next__(self): pass
With the ``determine_wants`` function in place, we can now fetch a pack,
which we will write to a ``BytesIO`` object::
``f`` will now contain a full pack file::
- >>> f.getvalue()[:4]
- 'PACK'
+ >>> print(f.getvalue()[:4].decode('ascii'))
+ PACK
Fetching objects into a local repository
----------------------------------------
importing the received pack file into the local repository::
>>> from dulwich.repo import Repo
- >>> local = Repo.init("local", mkdir=True)
+ >>> local = Repo.init(b"local", mkdir=True)
>>> remote_refs = client.fetch(b"/", local)
Let's shut down the server now that all tests have been run::
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
call::
>>> index = repo.open_index()
- >>> repr(index).replace('\\\\', '/')
- "Index('myrepo/.git/index')"
+ >>> print(index.path.decode(sys.getfilesystemencoding()))
+ myrepo/.git/index
Since the repository was just created, the index will be empty::
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', 'w')
- >>> f.write("monty")
+ >>> f = open('myrepo/foo', 'wb')
+ >>> _ = f.write(b"monty")
>>> f.close()
- >>> repo.stage(["foo"])
+ >>> repo.stage([b"foo"])
It will now show up in the index::
- >>> list(repo.open_index())
- ['foo']
+ >>> print(",".join([f.decode(sys.getfilesystemencoding()) for f in repo.open_index()]))
+ foo
Creating new commits
repository configuration or global configuration if they are not specified::
>>> commit_id = repo.do_commit(
- ... "The first commit", committer="Jelmer Vernooij <jelmer@samba.org>")
+ ... b"The first commit", committer=b"Jelmer Vernooij <jelmer@samba.org>")
``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::