123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- .. _recipes:
- ========================
- Recipes for common tasks
- ========================
- How do I check out files with Dulwich?
- ======================================
- The answer depends on the exact meaning of "check out" that is
- intended. There are several common goals it could be describing, and
- correspondingly several options to achieve them.
- Make sure a working tree on disk matches a particular commit (like ``git checkout``)
- ------------------------------------------------------------------------------------
- :py:func:`dulwich.porcelain.checkout` is a very high-level function
- that operates on the working tree and behaves very similar to the
- ``git checkout`` command. It packages a lot of functionality into a
- single command, just as Git's porcelain does, which is useful when
- matching Git's CLI is the goal, but might be less desirable for
- programmatic access to a repository's contents.
- Retrieve a single file's contents at a particular commit
- --------------------------------------------------------
- :py:func:`dulwich.object_store.tree_lookup_path` can a retrieve the
- object SHA given its path and the SHA of a tree to look it up in. This
- makes it very easy to access a specific file as stored in the
- repo. Note that this function operates on *trees*, not *commits*
- (every commit contains a tree for its contents, but a commit's ID is
- not the same as its tree's ID).
- With the retrieved SHA it's possible to get a file's blob directly
- from the repository's object store, and thus its content bytes. It's
- also possible to write it out to disk, using
- :py:func:`dulwich.index.build_file_from_blob`, which takes care of
- things like symlinks and file permissions.
- .. code-block:: python
- from dulwich.repo import Repo
- from dulwich.objectspec import parse_commit
- from dulwich.object_store import tree_lookup_path
- repo = Repo("/path/to/some/repo")
- # parse_commit will understand most commonly-used types of Git refs, including
- # short SHAs, tag names, branch names, HEAD, etc.
- commit = parse_commit(repo, "v1.0.0")
- path = b"README.md"
- mode, sha = tree_lookup_path(repo.get_object, commit.tree, path)
- # Normalizer takes care of line ending conversion and applying smudge
- # filters during checkout. See the Git Book for more details:
- # https://git-scm.com/book/ms/v2/Customizing-Git-Git-Attributes
- blob = repo.get_blob_normalizer().checkout_normalize(repo[sha], path)
- print(f"The readme at {commit.id.decode('ascii')} is:")
- print(blob.data.decode("utf-8"))
- Retrieve all or a subset of files at a particular commit
- --------------------------------------------------------
- A dedicated helper function
- :py:func:`dulwich.object_store.iter_commit_contents` exists to
- simplify the common requirement of programmatically getting the
- contents of a repo as stored at a specific commit. Unlike
- :py:func:`!porcelain.checkout`, it is not tied to a working tree, or
- even files.
- When paired with :py:func:`dulwich.index.build_file_from_blob`, it's
- very easy to write out the retrieved files to an arbitrary location on
- disk, independent of any working trees. This makes it ideal for tasks
- such as retrieving a pristine copy of the contained files without any
- of Git's tracking information, for use in deployments, automation, and
- similar.
- .. code-block:: python
- import stat
- from pathlib import Path
- from dulwich.repo import Repo
- from dulwich.object_store import iter_commit_contents
- from dulwich.index import build_file_from_blob
- repo = Repo("/path/to/another/repo")
- normalize = repo.get_blob_normalizer().checkout_normalize
- commit = repo[repo.head()]
- encoding = commit.encoding or "utf-8"
- # Scan the repo at current HEAD. Retrieve all files marked as
- # executable under bin/ and write them to disk
- for entry in iter_commit_contents(repo, commit.id, include=[b"bin"]):
- if entry.mode & stat.S_IXUSR:
- # Strip the leading bin/ from returned paths, write to
- # current directory
- path = Path(entry.path.decode(encoding)).relative_to("bin/")
- # Make sure the target directory exists
- path.parent.mkdir(parents=True, exist_ok=True)
- blob = normalize(repo[entry.sha], entry.path)
- build_file_from_blob(
- blob, entry.mode,
- str(path)
- )
- print(f"Wrote executable {path}")
|