Note: dulwich is not feature complete yet and the development of the project is extremely slow. It is suggested that you use other Python packages instead. For more discussions, please refer to Git Implementations and Bindings in Python .
Tips and Traps¶
- The
git
command (and thus Dulwich) accepts URLs both with and without the trailing.git
.
!pip3 install dulwich
import dulwich.repo
from dulwich.repo import Repo
import dulwich.porcelain
url = "https://github.com/dclong/test_dulwich"
dir_local = "/tmp/test_dulwich"
!rm -rf {dir_local}
git clone¶
repo = dulwich.porcelain.clone(url, dir_local)
repo
!ls {dir_local}
Clone the local repository to another location (which is not very useful as you can directly copy the directory to the new location).
?repo.clone
dulwich.repo.Repo¶
type(repo)
Create a repo from a local directory.
repo2 = Repo("/tmp/test_dulwich")
repo2
dir(repo)
git status¶
s = dulwich.porcelain.status(repo)
dir(s)
s.staged
s.unstaged
s.untracked
!git -C {dir_local} status
dulwich.porcelain.get_tree_changes(repo)
!git -C {dir_local} diff
repo[b"head"]
dir(repo)
repo.head
repo.head()
repo[repo.head()]
repo.object_store
import sys
from dulwich.patch import write_tree_diff
outstream = getattr(sys.stdout, 'buffer', sys.stdout)
write_tree_diff(outstream, repo.object_store, repo[repo.head()], commit.tree)
import sys
from dulwich.patch import write_tree_diff
from dulwich.repo import Repo
repo_path = "."
commit_id = b"a6602654997420bcfd0bee2a0563d9416afe34b4"
r = Repo(repo_path)
commit = r[commit_id]
parent_commit = r[commit.parents[0]]
outstream = getattr(sys.stdout, 'buffer', sys.stdout)
write_tree_diff(outstream, r.object_store, parent_commit.tree, commit.tree)
git add¶
!touch /tmp/test_dulwich/abc2
!ls /tmp/test_dulwich/
!git -C /tmp/test_dulwich/ status
?dulwich.porcelain.add
dulwich.porcelain.add("/tmp/test_dulwich", paths="/tmp/test_dulwich/abc")
By default, dulwich adds all files in the current working directory, which is not the right behavior! I have submitted a ticket to fix the issue.
dulwich.porcelain.add("/tmp/test_dulwich")
dulwich.porcelain.add("/tmp/test_dulwich", paths="/tmp/test_dulwich/.")
status = dulwich.porcelain.status("/tmp/test_dulwich")
status
status.unstaged
status.untracked
git commit¶
?dulwich.porcelain.commit
dulwich.porcelain.commit("/tmp/test_dulwich/", message="add abc")
!git -C /tmp/test_dulwich/ status
!git -C /tmp/test_dulwich/ log | head
ConfigFile¶
ConfigFile inherits ConfigDict which means that you operate on a ConfiFile like a dict
.
config = repo.get_config()
config
config.keys()
config.values()
config[(b"remote", b"origin")]
config.get((b"remote", b"origin"), b"url")
dict(config)
git remote -v¶
[m for m in dir(dulwich.porcelain) if 'remote' in m]
dulwich.porcelain.get_branch_remote(repo)
dulwich.porcelain.get_remote_repo(repo)
dulwich.porcelain.ls_remote(url)
[key[1].decode() for key in config.keys() if key[0] == b"remote"]
dulwich.porcelain.ls_remote¶
dulwich.porcelain.ls_remote(url)
dulwich.objects.Commit¶
repo.head()
commit = repo[repo.head()]
commit
type(commit)
commit.message
dir(commit)
main = heads.main
main
Get the commit pointed to by head called master.
main.commit
main.rename("main2")
Verify that the main
branch has been renamed to main2
.
!cd {dir_local} && git branch
Get the Active Branch¶
[m for m in dir(dulwich.porcelain) if "br" in m]
dulwich.porcelain.active_branch(repo)
Get All Branches¶
dulwich.porcelain.branch_list(repo)
Changed Files¶
Update a file.
!echo "# add a line of comment" >> {dir_local}/build.sh
Staged Files¶
The file build.sh
is now staged.
Commit the change.
Push the Commits¶
Push the local main2
branch to the remote main2
branch.
The above is equivalent to the following more detailed specification.
Push the local main2
branch to the remote main
branch.
git pull¶
!ls {dir_local}
dulwich.porcelain.pull(repo, refspecs="main")
!ls {dir_local}
git checkout¶
[m for m in dir(dulwich.porcelain) if 'checkout' in m]
!git -C {dir_local} status
!git -C {dir_local} branch
dulwich.porcelain.checkout_branch(repo, "main")
!git -C {dir_local} branch
git tag¶
List all tags.
!git -C {dir_local} tag
[m for m in dir(dulwich.porcelain) if "tag" in m]
dulwich.porcelain.tag_list(repo)
git tag tag_name¶
Create a new tag.
dulwich.porcelain.tag_create(repo, "v2.0.0")
dulwich.porcelain.push(repo, refspecs="v2.0.0")
git diff¶
help(repo.refs[4].commit.diff)
url = "https://github.com/dclong/docker-ubuntu_b.git"
dir_local = "/tmp/" + url[(url.rindex("/") + 1) :]
!rm -rf {dir_local}
repo = git.Repo.clone_from(url, dir_local, branch="main")
repo
repo.refs
diffs = repo.refs[4].commit.diff(repo.refs[3].commit)
diffs
diffs = repo.refs[4].commit.diff(repo.refs[2].commit)
diffs
str(diffs[0])
repo.refs[5].name
print(repo.git.status())
repo.git.checkout("debian", force=True)
repo.git.checkout(b="a_new_branch", force=True)
nima = repo.refs[4].checkout(force=True, b="nima")
nima
diffs = nima.commit.diff(repo.refs[-1].commit)
diffs[0].diff
Diff the dev
and the main
branch,
which is equivalent to the Git command
git diff dev..main
.
repo.refs[2].commit.diff(repo.refs[1].commit)
diffs = repo.refs[2].commit.diff(repo.refs[0].commit)
diffs
diffs[0]
diffs = repo.refs[6].commit.diff(repo.refs[7].commit)
diffs
diffs = repo.refs[4].commit.diff(repo.refs[7].commit)
diffs
diffs[0].diff
diffs = repo.refs[7].commit.diff(repo.refs[4].commit)
diffs
diffs[0].diff
any(ele for ele in [""])
repo.branches[0].name
for branch in repo.branches:
branch.
commit = repo.head.commit
commit
type(repo.branches[0])
repo.refs[4].commit.diff(repo.refs[2].commit)
repo.refs[4].commit.diff(repo.refs[3].commit)
help(repo.git.branch)
repo.heads
Diff the debian
and the main
branches but limit diff to specified paths
(via the paths
parameter).
diffs = repo.refs[4].commit.diff(repo.refs[2].commit, paths=["build.sh", "scripts"])
diffs