notes.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # notes.py -- Porcelain-like interface for Git notes
  2. # Copyright (C) 2013 Jelmer Vernooij <jelmer@jelmer.uk>
  3. #
  4. # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  5. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  6. # General Public License as published by the Free Software Foundation; version 2.0
  7. # or (at your option) any later version. You can redistribute it and/or
  8. # modify it under the terms of either of these two licenses.
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. # You should have received a copy of the licenses; if not, see
  17. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  18. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  19. # License, Version 2.0.
  20. #
  21. """Porcelain-like interface for Git notes."""
  22. from typing import TYPE_CHECKING
  23. from dulwich.objects import ObjectID
  24. from dulwich.objectspec import parse_object
  25. from ..refs import LOCAL_NOTES_PREFIX
  26. if TYPE_CHECKING:
  27. from . import RepoPath
  28. def _make_notes_ref(name: bytes) -> bytes:
  29. """Make a notes ref name."""
  30. if name.startswith(b"refs/notes/"):
  31. return name
  32. return LOCAL_NOTES_PREFIX + name
  33. def notes_add(
  34. repo: "RepoPath",
  35. object_sha: bytes,
  36. note: bytes,
  37. ref: bytes = b"commits",
  38. author: bytes | None = None,
  39. committer: bytes | None = None,
  40. message: bytes | None = None,
  41. ) -> bytes:
  42. """Add or update a note for an object.
  43. Args:
  44. repo: Path to repository
  45. object_sha: SHA of the object to annotate
  46. note: Note content
  47. ref: Notes ref to use (defaults to "commits" for refs/notes/commits)
  48. author: Author identity (defaults to committer)
  49. committer: Committer identity (defaults to config)
  50. message: Commit message for the notes update
  51. Returns:
  52. SHA of the new notes commit
  53. """
  54. from . import DEFAULT_ENCODING, open_repo_closing
  55. with open_repo_closing(repo) as r:
  56. # Parse the object to get its SHA
  57. obj = parse_object(r, object_sha)
  58. object_sha = obj.id
  59. if isinstance(note, str):
  60. note = note.encode(DEFAULT_ENCODING)
  61. if isinstance(ref, str):
  62. ref = ref.encode(DEFAULT_ENCODING)
  63. notes_ref = _make_notes_ref(ref)
  64. config = r.get_config_stack()
  65. return r.notes.set_note(
  66. object_sha,
  67. note,
  68. notes_ref,
  69. author=author,
  70. committer=committer,
  71. message=message,
  72. config=config,
  73. )
  74. def notes_remove(
  75. repo: "RepoPath",
  76. object_sha: bytes,
  77. ref: bytes = b"commits",
  78. author: bytes | None = None,
  79. committer: bytes | None = None,
  80. message: bytes | None = None,
  81. ) -> bytes | None:
  82. """Remove a note for an object.
  83. Args:
  84. repo: Path to repository
  85. object_sha: SHA of the object to remove notes from
  86. ref: Notes ref to use (defaults to "commits" for refs/notes/commits)
  87. author: Author identity (defaults to committer)
  88. committer: Committer identity (defaults to config)
  89. message: Commit message for the notes removal
  90. Returns:
  91. SHA of the new notes commit, or None if no note existed
  92. """
  93. from . import DEFAULT_ENCODING, open_repo_closing
  94. with open_repo_closing(repo) as r:
  95. # Parse the object to get its SHA
  96. obj = parse_object(r, object_sha)
  97. object_sha = obj.id
  98. if isinstance(ref, str):
  99. ref = ref.encode(DEFAULT_ENCODING)
  100. notes_ref = _make_notes_ref(ref)
  101. config = r.get_config_stack()
  102. return r.notes.remove_note(
  103. object_sha,
  104. notes_ref,
  105. author=author,
  106. committer=committer,
  107. message=message,
  108. config=config,
  109. )
  110. def notes_show(
  111. repo: "RepoPath", object_sha: bytes, ref: bytes = b"commits"
  112. ) -> bytes | None:
  113. """Show the note for an object.
  114. Args:
  115. repo: Path to repository
  116. object_sha: SHA of the object
  117. ref: Notes ref to use (defaults to "commits" for refs/notes/commits)
  118. Returns:
  119. Note content as bytes, or None if no note exists
  120. """
  121. from . import DEFAULT_ENCODING, open_repo_closing
  122. with open_repo_closing(repo) as r:
  123. # Parse the object to get its SHA
  124. obj = parse_object(r, object_sha)
  125. object_sha = obj.id
  126. if isinstance(ref, str):
  127. ref = ref.encode(DEFAULT_ENCODING)
  128. notes_ref = _make_notes_ref(ref)
  129. config = r.get_config_stack()
  130. return r.notes.get_note(object_sha, notes_ref, config=config)
  131. def notes_list(
  132. repo: "RepoPath", ref: bytes = b"commits"
  133. ) -> list[tuple[ObjectID, bytes]]:
  134. """List all notes in a notes ref.
  135. Args:
  136. repo: Path to repository
  137. ref: Notes ref to use (defaults to "commits" for refs/notes/commits)
  138. Returns:
  139. List of tuples of (object_sha, note_content)
  140. """
  141. from . import DEFAULT_ENCODING, open_repo_closing
  142. with open_repo_closing(repo) as r:
  143. if isinstance(ref, str):
  144. ref = ref.encode(DEFAULT_ENCODING)
  145. notes_ref = _make_notes_ref(ref)
  146. config = r.get_config_stack()
  147. return r.notes.list_notes(notes_ref, config=config)