Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!

!pip3 install pygit2
Defaulting to user installation because normal site-packages is not writeable
Collecting pygit2
  Downloading pygit2-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.3 kB)
Requirement already satisfied: cffi>=1.16.0 in /usr/local/lib/python3.10/dist-packages (from pygit2) (1.16.0)
Requirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.16.0->pygit2) (2.22)
Downloading pygit2-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.1 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.1/5.1 MB 20.5 MB/s eta 0:00:0000:0100:01
Installing collected packages: pygit2
Successfully installed pygit2-1.15.1

[notice] A new release of pip is available: 24.0 -> 24.2
[notice] To update, run: python3 -m pip install --upgrade pip
import pygit2
pygit2.InvalidSpecError
_pygit2.InvalidSpecError

Clone a Repository

url = "https://github.com/dclong/docker-ubuntu_b.git"
dir_local = "/tmp/test_pygit2"
!ls {dir_local}
build.sh  Dockerfile  LICENSE  readme.md  scripts
!rm -rf {dir_local}
!ls {dir_local}
ls: cannot access '/tmp/test_pygit2': No such file or directory
repo = pygit2.clone_repository(url, dir_local)
!ls {dir_local}
build.sh  Dockerfile  LICENSE  readme.md  scripts
url2 = "https://github.com/dclong/docker-ubuntu_b"
dir_local2 = "/tmp/test_pygit2_2"
pygit2.clone_repository(url, dir_local2)
pygit2.Repository('/tmp/test_pygit2_2/.git/')
ls /tmp/test_pygit2_2/
build.sh*  Dockerfile  LICENSE  readme.md  scripts/

Methods of pygit2.Repository

[m for m in dir(repo) if not m.startswith("_")]
['TreeBuilder', 'add_submodule', 'add_worktree', 'ahead_behind', 'amend_commit', 'applies', 'apply', 'blame', 'branches', 'checkout', 'checkout_head', 'checkout_index', 'checkout_tree', 'cherrypick', 'compress_references', 'config', 'config_snapshot', 'create_blob', 'create_blob_fromdisk', 'create_blob_fromiobase', 'create_blob_fromworkdir', 'create_branch', 'create_commit', 'create_note', 'create_reference', 'create_reference_direct', 'create_reference_symbolic', 'create_remote', 'create_tag', 'default_signature', 'descendant_of', 'describe', 'diff', 'expand_id', 'free', 'get', 'get_attr', 'git_object_lookup_prefix', 'head', 'head_is_detached', 'head_is_unborn', 'ident', 'index', 'init_submodules', 'is_bare', 'is_empty', 'is_shallow', 'list_worktrees', 'listall_branches', 'listall_reference_objects', 'listall_references', 'listall_stashes', 'listall_submodules', 'lookup_branch', 'lookup_note', 'lookup_reference', 'lookup_reference_dwim', 'lookup_submodule', 'lookup_worktree', 'merge', 'merge_analysis', 'merge_base', 'merge_base_many', 'merge_base_octopus', 'merge_commits', 'merge_file_from_index', 'merge_trees', 'notes', 'odb', 'pack', 'path', 'path_is_ignored', 'raw_listall_branches', 'raw_listall_references', 'read', 'refdb', 'references', 'remotes', 'reset', 'resolve_refish', 'revert_commit', 'revparse', 'revparse_ext', 'revparse_single', 'set_head', 'set_ident', 'set_odb', 'set_refdb', 'stash', 'stash_apply', 'stash_drop', 'stash_pop', 'state_cleanup', 'status', 'status_file', 'update_submodules', 'walk', 'workdir', 'write', 'write_archive']

Branches

repo.listall_branches()
['dev']
set(repo.branches)
{'dev', 'main', 'origin/22.04', 'origin/HEAD', 'origin/centos7', 'origin/debian', 'origin/dev', 'origin/main'}
repo.branches
<pygit2.repository.Branches at 0x7f91e5e8b340>
[m for m in dir(repo.branches) if not m.startswith("_")]
['create', 'delete', 'get', 'local', 'remote', 'with_commit']
repo.branches.get("non_exist_branch") is None
True
br = repo.branches.get("dev")
br
<_pygit2.Branch at 0x7f9223593bf0>
repo.branches.get("origin/main")
<_pygit2.Branch at 0x7f9223679490>

Create a Branch

  1. Unlike the --force parameter of git checkout -b, repo.create_branch(..., force=True) does not throw away changes in the current directory. Notice that it does overwrite an existing branch.

repo.create_branch(
    "centos7", repo.references["refs/remotes/origin/centos7"].peel(), True
)
<_pygit2.Branch at 0x7f92236e8830>

References

repo.references
<pygit2.repository.References at 0x7f91e60d76a0>
repo.listall_references()
['refs/heads/centos7', 'refs/heads/dev', 'refs/heads/main', 'refs/remotes/origin/22.04', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/centos7', 'refs/remotes/origin/debian', 'refs/remotes/origin/dev', 'refs/remotes/origin/main', 'refs/stash']
set(repo.references)
{'refs/heads/centos7', 'refs/heads/dev', 'refs/heads/main', 'refs/remotes/origin/22.04', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/centos7', 'refs/remotes/origin/debian', 'refs/remotes/origin/dev', 'refs/remotes/origin/main', 'refs/stash'}
repo.lookup_reference("centos7")
---------------------------------------------------------------------------
InvalidSpecError                          Traceback (most recent call last)
Input In [72], in <cell line: 1>()
----> 1 repo.lookup_reference("centos7")

InvalidSpecError: centos7: the given reference name 'centos7' is not valid
repo.lookup_reference("refs/remotes/origin/centos7")
<_pygit2.Reference at 0x7f9223679b50>
repo.references.get("refs/remotes/origin/centos7")
<_pygit2.Reference at 0x7f92234c0910>

Checkout a Reference

!git -C {dir_local} status
On branch dev
Your branch is up to date with 'origin/dev'.

nothing to commit, working tree clean
repo.checkout("nima")
---------------------------------------------------------------------------
InvalidSpecError                          Traceback (most recent call last)
Input In [71], in <cell line: 1>()
----> 1 repo.checkout("nima")

File ~/.local/lib/python3.8/site-packages/pygit2/repository.py:422, in BaseRepository.checkout(self, refname, **kwargs)
    420     refname = refname.name
    421 else:
--> 422     reference = self.lookup_reference(refname)
    424 oid = reference.resolve().target
    425 treeish = self[oid]

InvalidSpecError: nima: the given reference name 'nima' is not valid
repo.checkout("refs/heads/dev")
repo.checkout("refs/heads/main")
!git -C {dir_local} status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
repo.checkout("refs/remotes/origin/main")
!git -C {dir_local} status
HEAD detached at origin/main
nothing to commit, working tree clean
!git -C {dir_local} branch
* (HEAD detached at origin/main)
  dev
repo.listall_references()
['refs/heads/centos7', 'refs/heads/dev', 'refs/heads/main', 'refs/remotes/origin/22.04', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/centos7', 'refs/remotes/origin/debian', 'refs/remotes/origin/dev', 'refs/remotes/origin/main', 'refs/stash']
!git -C {dir_local} branch
  centos7
* dev
  main
repo.checkout(repo.references["refs/heads/centos7"])
!git -C {dir_local} branch
* centos7
  dev
  main

Status of the Repository

repo.status()
{'Dockerfile': 256}

Stash Changes

repo.default_signature
pygit2.Signature('Benjamin Du', 'longendu@yahoo.com', 1651127956, 4294966876, 'utf-8')
repo.stash(repo.default_signature)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Input In [87], in <cell line: 1>()
----> 1 repo.stash(repo.default_signature)

File ~/.local/lib/python3.8/site-packages/pygit2/repository.py:1091, in BaseRepository.stash(self, stasher, message, keep_index, include_untracked, include_ignored)
   1089 coid = ffi.new('git_oid *')
   1090 err = C.git_stash_save(coid, self._repo, stasher_cptr[0], stash_msg, flags)
-> 1091 check_error(err)
   1093 return Oid(raw=bytes(ffi.buffer(coid)[:]))

File ~/.local/lib/python3.8/site-packages/pygit2/errors.py:56, in check_error(err, io)
     53     if io:
     54         raise IOError(message)
---> 56     raise KeyError(message)
     58 if err == C.GIT_EINVALIDSPEC:
     59     raise ValueError(message)

KeyError: 'cannot stash changes - there is nothing to stash.'

Create a Commit

?repo.create_commit
Docstring: create_commit(reference_name: str, author: Signature, committer: Signature, message: bytes | str, tree: Oid, parents: list[Oid][, encoding: str]) -> Oid Create a new commit object, return its oid. Type: builtin_function_or_method

Index

index = repo.index
index
<pygit2.index.Index at 0x7f92234a9b50>
[m for m in dir(index) if not m.startswith("_")]
['add', 'add_all', 'clear', 'conflicts', 'diff_to_tree', 'diff_to_workdir', 'from_c', 'read', 'read_tree', 'remove', 'remove_all', 'write', 'write_tree']

git reset / Repository.reset

Reset current HEAD to the specified state.

!git -C {dir_local} status
On branch dev
Your branch is up to date with 'origin/dev'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   Dockerfile

no changes added to commit (use "git add" and/or "git commit -a")
repo.reset(repo.head.peel().oid, pygit2.GIT_RESET_HARD)
repo.reset(repo.head.peel().id, pygit2.GIT_RESET_HARD)
!git -C {dir_local} status
On branch dev
Your branch is up to date with 'origin/dev'.

nothing to commit, working tree clean

git diff / Repository.diff

repo.listall_references()
['refs/heads/dev', 'refs/heads/main', 'refs/remotes/origin/22.04', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/centos7', 'refs/remotes/origin/debian', 'refs/remotes/origin/dev', 'refs/remotes/origin/main']
diff = repo.diff("refs/heads/dev", "refs/heads/main")
diff
<_pygit2.Diff at 0x7f581bc368d0>
not any(True for _ in diff.deltas)
True
not any(True for _ in repo.diff("refs/heads/main", "refs/heads/dev").deltas)
True
diff = repo.diff("refs/heads/dev", "refs/heads/centos7")
diff
<_pygit2.Diff at 0x7f581954a4b0>
deltas = list(diff.deltas)
deltas
[<_pygit2.DiffDelta at 0x7f58195d4d50>, <_pygit2.DiffDelta at 0x7f58195d4510>, <_pygit2.DiffDelta at 0x7f58195d4f30>, <_pygit2.DiffDelta at 0x7f58195d4180>]
deltas[0].old_file.path
'.github/workflows/create_pull_request.yml'
deltas[0].new_file.path
'.github/workflows/create_pull_request.yml'
deltas[1].old_file.path
'Dockerfile'
deltas[1].new_file.path
'Dockerfile'