index.txt 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. .. _recipes:
  2. ========================
  3. Recipes for common tasks
  4. ========================
  5. How do I check out files with Dulwich?
  6. ======================================
  7. The answer depends on the exact meaning of "check out" that is
  8. intended. There are several common goals it could be describing, and
  9. correspondingly several options to achieve them.
  10. Make sure a working tree on disk matches a particular commit (like ``git checkout``)
  11. ------------------------------------------------------------------------------------
  12. :py:func:`dulwich.porcelain.checkout` is a very high-level function
  13. that operates on the working tree and behaves very similar to the
  14. ``git checkout`` command. It packages a lot of functionality into a
  15. single command, just as Git's porcelain does, which is useful when
  16. matching Git's CLI is the goal, but might be less desirable for
  17. programmatic access to a repository's contents.
  18. Retrieve a single file's contents at a particular commit
  19. --------------------------------------------------------
  20. :py:func:`dulwich.object_store.tree_lookup_path` can a retrieve the
  21. object SHA given its path and the SHA of a tree to look it up in. This
  22. makes it very easy to access a specific file as stored in the
  23. repo. Note that this function operates on *trees*, not *commits*
  24. (every commit contains a tree for its contents, but a commit's ID is
  25. not the same as its tree's ID).
  26. With the retrieved SHA it's possible to get a file's blob directly
  27. from the repository's object store, and thus its content bytes. It's
  28. also possible to write it out to disk, using
  29. :py:func:`dulwich.index.build_file_from_blob`, which takes care of
  30. things like symlinks and file permissions.
  31. .. code-block:: python
  32. from dulwich.repo import Repo
  33. from dulwich.objectspec import parse_commit
  34. from dulwich.object_store import tree_lookup_path
  35. repo = Repo("/path/to/some/repo")
  36. # parse_commit will understand most commonly-used types of Git refs, including
  37. # short SHAs, tag names, branch names, HEAD, etc.
  38. commit = parse_commit(repo, "v1.0.0")
  39. path = b"README.md"
  40. mode, sha = tree_lookup_path(repo.get_object, commit.tree, path)
  41. # Normalizer takes care of line ending conversion and applying smudge
  42. # filters during checkout. See the Git Book for more details:
  43. # https://git-scm.com/book/ms/v2/Customizing-Git-Git-Attributes
  44. blob = repo.get_blob_normalizer().checkout_normalize(repo[sha], path)
  45. print(f"The readme at {commit.id.decode('ascii')} is:")
  46. print(blob.data.decode("utf-8"))
  47. Retrieve all or a subset of files at a particular commit
  48. --------------------------------------------------------
  49. A dedicated helper function
  50. :py:func:`dulwich.object_store.iter_commit_contents` exists to
  51. simplify the common requirement of programmatically getting the
  52. contents of a repo as stored at a specific commit. Unlike
  53. :py:func:`!porcelain.checkout`, it is not tied to a working tree, or
  54. even files.
  55. When paired with :py:func:`dulwich.index.build_file_from_blob`, it's
  56. very easy to write out the retrieved files to an arbitrary location on
  57. disk, independent of any working trees. This makes it ideal for tasks
  58. such as retrieving a pristine copy of the contained files without any
  59. of Git's tracking information, for use in deployments, automation, and
  60. similar.
  61. .. code-block:: python
  62. import stat
  63. from pathlib import Path
  64. from dulwich.repo import Repo
  65. from dulwich.object_store import iter_commit_contents
  66. from dulwich.index import build_file_from_blob
  67. repo = Repo("/path/to/another/repo")
  68. normalize = repo.get_blob_normalizer().checkout_normalize
  69. commit = repo[repo.head()]
  70. encoding = commit.encoding or "utf-8"
  71. # Scan the repo at current HEAD. Retrieve all files marked as
  72. # executable under bin/ and write them to disk
  73. for entry in iter_commit_contents(repo, commit.id, include=[b"bin"]):
  74. if entry.mode & stat.S_IXUSR:
  75. # Strip the leading bin/ from returned paths, write to
  76. # current directory
  77. path = Path(entry.path.decode(encoding)).relative_to("bin/")
  78. # Make sure the target directory exists
  79. path.parent.mkdir(parents=True, exist_ok=True)
  80. blob = normalize(repo[entry.sha], entry.path)
  81. build_file_from_blob(
  82. blob, entry.mode,
  83. str(path)
  84. )
  85. print(f"Wrote executable {path}")