refs.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. # refs.py -- For dealing with git refs
  2. # Copyright (C) 2008-2013 Jelmer Vernooij <jelmer@samba.org>
  3. #
  4. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  5. # General Public License as public by the Free Software Foundation; version 2.0
  6. # or (at your option) any later version. You can redistribute it and/or
  7. # modify it under the terms of either of these two licenses.
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. # You should have received a copy of the licenses; if not, see
  16. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  17. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  18. # License, Version 2.0.
  19. #
  20. """Ref handling.
  21. """
  22. import errno
  23. import os
  24. import sys
  25. from dulwich.errors import (
  26. PackedRefsException,
  27. RefFormatError,
  28. )
  29. from dulwich.objects import (
  30. git_line,
  31. valid_hexsha,
  32. ZERO_SHA,
  33. )
  34. from dulwich.file import (
  35. GitFile,
  36. ensure_dir_exists,
  37. )
  38. SYMREF = b'ref: '
  39. LOCAL_BRANCH_PREFIX = b'refs/heads/'
  40. BAD_REF_CHARS = set(b'\177 ~^:?*[')
  41. ANNOTATED_TAG_SUFFIX = b'^{}'
  42. def check_ref_format(refname):
  43. """Check if a refname is correctly formatted.
  44. Implements all the same rules as git-check-ref-format[1].
  45. [1] http://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
  46. :param refname: The refname to check
  47. :return: True if refname is valid, False otherwise
  48. """
  49. # These could be combined into one big expression, but are listed separately
  50. # to parallel [1].
  51. if b'/.' in refname or refname.startswith(b'.'):
  52. return False
  53. if b'/' not in refname:
  54. return False
  55. if b'..' in refname:
  56. return False
  57. for i, c in enumerate(refname):
  58. if ord(refname[i:i+1]) < 0o40 or c in BAD_REF_CHARS:
  59. return False
  60. if refname[-1] in b'/.':
  61. return False
  62. if refname.endswith(b'.lock'):
  63. return False
  64. if b'@{' in refname:
  65. return False
  66. if b'\\' in refname:
  67. return False
  68. return True
  69. class RefsContainer(object):
  70. """A container for refs."""
  71. def set_symbolic_ref(self, name, other):
  72. """Make a ref point at another ref.
  73. :param name: Name of the ref to set
  74. :param other: Name of the ref to point at
  75. """
  76. raise NotImplementedError(self.set_symbolic_ref)
  77. def get_packed_refs(self):
  78. """Get contents of the packed-refs file.
  79. :return: Dictionary mapping ref names to SHA1s
  80. :note: Will return an empty dictionary when no packed-refs file is
  81. present.
  82. """
  83. raise NotImplementedError(self.get_packed_refs)
  84. def get_peeled(self, name):
  85. """Return the cached peeled value of a ref, if available.
  86. :param name: Name of the ref to peel
  87. :return: The peeled value of the ref. If the ref is known not point to a
  88. tag, this will be the SHA the ref refers to. If the ref may point to
  89. a tag, but no cached information is available, None is returned.
  90. """
  91. return None
  92. def import_refs(self, base, other):
  93. for name, value in other.items():
  94. self[b'/'.join((base, name))] = value
  95. def allkeys(self):
  96. """All refs present in this container."""
  97. raise NotImplementedError(self.allkeys)
  98. def keys(self, base=None):
  99. """Refs present in this container.
  100. :param base: An optional base to return refs under.
  101. :return: An unsorted set of valid refs in this container, including
  102. packed refs.
  103. """
  104. if base is not None:
  105. return self.subkeys(base)
  106. else:
  107. return self.allkeys()
  108. def subkeys(self, base):
  109. """Refs present in this container under a base.
  110. :param base: The base to return refs under.
  111. :return: A set of valid refs in this container under the base; the base
  112. prefix is stripped from the ref names returned.
  113. """
  114. keys = set()
  115. base_len = len(base) + 1
  116. for refname in self.allkeys():
  117. if refname.startswith(base):
  118. keys.add(refname[base_len:])
  119. return keys
  120. def as_dict(self, base=None):
  121. """Return the contents of this container as a dictionary.
  122. """
  123. ret = {}
  124. keys = self.keys(base)
  125. if base is None:
  126. base = b''
  127. else:
  128. base = base.rstrip(b'/')
  129. for key in keys:
  130. try:
  131. ret[key] = self[(base + b'/' + key).strip(b'/')]
  132. except KeyError:
  133. continue # Unable to resolve
  134. return ret
  135. def _check_refname(self, name):
  136. """Ensure a refname is valid and lives in refs or is HEAD.
  137. HEAD is not a valid refname according to git-check-ref-format, but this
  138. class needs to be able to touch HEAD. Also, check_ref_format expects
  139. refnames without the leading 'refs/', but this class requires that
  140. so it cannot touch anything outside the refs dir (or HEAD).
  141. :param name: The name of the reference.
  142. :raises KeyError: if a refname is not HEAD or is otherwise not valid.
  143. """
  144. if name in (b'HEAD', b'refs/stash'):
  145. return
  146. if not name.startswith(b'refs/') or not check_ref_format(name[5:]):
  147. raise RefFormatError(name)
  148. def read_ref(self, refname):
  149. """Read a reference without following any references.
  150. :param refname: The name of the reference
  151. :return: The contents of the ref file, or None if it does
  152. not exist.
  153. """
  154. contents = self.read_loose_ref(refname)
  155. if not contents:
  156. contents = self.get_packed_refs().get(refname, None)
  157. return contents
  158. def read_loose_ref(self, name):
  159. """Read a loose reference and return its contents.
  160. :param name: the refname to read
  161. :return: The contents of the ref file, or None if it does
  162. not exist.
  163. """
  164. raise NotImplementedError(self.read_loose_ref)
  165. def follow(self, name):
  166. """Follow a reference name.
  167. :return: a tuple of (refnames, sha), wheres refnames are the names of
  168. references in the chain
  169. """
  170. contents = SYMREF + name
  171. depth = 0
  172. refnames = []
  173. while contents.startswith(SYMREF):
  174. refname = contents[len(SYMREF):]
  175. refnames.append(refname)
  176. contents = self.read_ref(refname)
  177. if not contents:
  178. break
  179. depth += 1
  180. if depth > 5:
  181. raise KeyError(name)
  182. return refnames, contents
  183. def _follow(self, name):
  184. import warnings
  185. warnings.warn(
  186. "RefsContainer._follow is deprecated. Use RefsContainer.follow instead.",
  187. DeprecationWarning)
  188. refnames, contents = self.follow(name)
  189. if not refnames:
  190. return (None, contents)
  191. return (refnames[-1], contents)
  192. def __contains__(self, refname):
  193. if self.read_ref(refname):
  194. return True
  195. return False
  196. def __getitem__(self, name):
  197. """Get the SHA1 for a reference name.
  198. This method follows all symbolic references.
  199. """
  200. _, sha = self.follow(name)
  201. if sha is None:
  202. raise KeyError(name)
  203. return sha
  204. def set_if_equals(self, name, old_ref, new_ref):
  205. """Set a refname to new_ref only if it currently equals old_ref.
  206. This method follows all symbolic references if applicable for the
  207. subclass, and can be used to perform an atomic compare-and-swap
  208. operation.
  209. :param name: The refname to set.
  210. :param old_ref: The old sha the refname must refer to, or None to set
  211. unconditionally.
  212. :param new_ref: The new sha the refname will refer to.
  213. :return: True if the set was successful, False otherwise.
  214. """
  215. raise NotImplementedError(self.set_if_equals)
  216. def add_if_new(self, name, ref):
  217. """Add a new reference only if it does not already exist."""
  218. raise NotImplementedError(self.add_if_new)
  219. def __setitem__(self, name, ref):
  220. """Set a reference name to point to the given SHA1.
  221. This method follows all symbolic references if applicable for the
  222. subclass.
  223. :note: This method unconditionally overwrites the contents of a
  224. reference. To update atomically only if the reference has not
  225. changed, use set_if_equals().
  226. :param name: The refname to set.
  227. :param ref: The new sha the refname will refer to.
  228. """
  229. self.set_if_equals(name, None, ref)
  230. def remove_if_equals(self, name, old_ref):
  231. """Remove a refname only if it currently equals old_ref.
  232. This method does not follow symbolic references, even if applicable for
  233. the subclass. It can be used to perform an atomic compare-and-delete
  234. operation.
  235. :param name: The refname to delete.
  236. :param old_ref: The old sha the refname must refer to, or None to delete
  237. unconditionally.
  238. :return: True if the delete was successful, False otherwise.
  239. """
  240. raise NotImplementedError(self.remove_if_equals)
  241. def __delitem__(self, name):
  242. """Remove a refname.
  243. This method does not follow symbolic references, even if applicable for
  244. the subclass.
  245. :note: This method unconditionally deletes the contents of a reference.
  246. To delete atomically only if the reference has not changed, use
  247. remove_if_equals().
  248. :param name: The refname to delete.
  249. """
  250. self.remove_if_equals(name, None)
  251. class DictRefsContainer(RefsContainer):
  252. """RefsContainer backed by a simple dict.
  253. This container does not support symbolic or packed references and is not
  254. threadsafe.
  255. """
  256. def __init__(self, refs):
  257. self._refs = refs
  258. self._peeled = {}
  259. def allkeys(self):
  260. return self._refs.keys()
  261. def read_loose_ref(self, name):
  262. return self._refs.get(name, None)
  263. def get_packed_refs(self):
  264. return {}
  265. def set_symbolic_ref(self, name, other):
  266. self._refs[name] = SYMREF + other
  267. def set_if_equals(self, name, old_ref, new_ref):
  268. if old_ref is not None and self._refs.get(name, ZERO_SHA) != old_ref:
  269. return False
  270. realnames, _ = self.follow(name)
  271. for realname in realnames:
  272. self._check_refname(realname)
  273. self._refs[realname] = new_ref
  274. return True
  275. def add_if_new(self, name, ref):
  276. if name in self._refs:
  277. return False
  278. self._refs[name] = ref
  279. return True
  280. def remove_if_equals(self, name, old_ref):
  281. if old_ref is not None and self._refs.get(name, ZERO_SHA) != old_ref:
  282. return False
  283. try:
  284. del self._refs[name]
  285. except KeyError:
  286. pass
  287. return True
  288. def get_peeled(self, name):
  289. return self._peeled.get(name)
  290. def _update(self, refs):
  291. """Update multiple refs; intended only for testing."""
  292. # TODO(dborowitz): replace this with a public function that uses
  293. # set_if_equal.
  294. self._refs.update(refs)
  295. def _update_peeled(self, peeled):
  296. """Update cached peeled refs; intended only for testing."""
  297. self._peeled.update(peeled)
  298. class InfoRefsContainer(RefsContainer):
  299. """Refs container that reads refs from a info/refs file."""
  300. def __init__(self, f):
  301. self._refs = {}
  302. self._peeled = {}
  303. for l in f.readlines():
  304. sha, name = l.rstrip(b'\n').split(b'\t')
  305. if name.endswith(ANNOTATED_TAG_SUFFIX):
  306. name = name[:-3]
  307. if not check_ref_format(name):
  308. raise ValueError("invalid ref name %r" % name)
  309. self._peeled[name] = sha
  310. else:
  311. if not check_ref_format(name):
  312. raise ValueError("invalid ref name %r" % name)
  313. self._refs[name] = sha
  314. def allkeys(self):
  315. return self._refs.keys()
  316. def read_loose_ref(self, name):
  317. return self._refs.get(name, None)
  318. def get_packed_refs(self):
  319. return {}
  320. def get_peeled(self, name):
  321. try:
  322. return self._peeled[name]
  323. except KeyError:
  324. return self._refs[name]
  325. class DiskRefsContainer(RefsContainer):
  326. """Refs container that reads refs from disk."""
  327. def __init__(self, path, worktree_path=None):
  328. self.path = path
  329. self.worktree_path = worktree_path or path
  330. self._packed_refs = None
  331. self._peeled_refs = None
  332. def __repr__(self):
  333. return "%s(%r)" % (self.__class__.__name__, self.path)
  334. def subkeys(self, base):
  335. subkeys = set()
  336. path = self.refpath(base)
  337. for root, dirs, files in os.walk(path):
  338. dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/")
  339. for filename in files:
  340. refname = (("%s/%s" % (dir, filename))
  341. .strip("/").encode(sys.getfilesystemencoding()))
  342. # check_ref_format requires at least one /, so we prepend the
  343. # base before calling it.
  344. if check_ref_format(base + b'/' + refname):
  345. subkeys.add(refname)
  346. for key in self.get_packed_refs():
  347. if key.startswith(base):
  348. subkeys.add(key[len(base):].strip(b'/'))
  349. return subkeys
  350. def allkeys(self):
  351. allkeys = set()
  352. if os.path.exists(self.refpath(b'HEAD')):
  353. allkeys.add(b'HEAD')
  354. path = self.refpath(b'')
  355. for root, dirs, files in os.walk(self.refpath(b'refs')):
  356. dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/")
  357. for filename in files:
  358. refname = ("%s/%s" % (dir, filename)).encode(sys.getfilesystemencoding())
  359. if check_ref_format(refname):
  360. allkeys.add(refname)
  361. allkeys.update(self.get_packed_refs())
  362. return allkeys
  363. def refpath(self, name):
  364. """Return the disk path of a ref.
  365. """
  366. if getattr(self.path, "encode", None) and getattr(name, "decode", None):
  367. name = name.decode(sys.getfilesystemencoding())
  368. if os.path.sep != "/":
  369. name = name.replace("/", os.path.sep)
  370. # TODO: as the 'HEAD' reference is working tree specific, it
  371. # should actually not be a part of RefsContainer
  372. if name == 'HEAD':
  373. return os.path.join(self.worktree_path, name)
  374. else:
  375. return os.path.join(self.path, name)
  376. def get_packed_refs(self):
  377. """Get contents of the packed-refs file.
  378. :return: Dictionary mapping ref names to SHA1s
  379. :note: Will return an empty dictionary when no packed-refs file is
  380. present.
  381. """
  382. # TODO: invalidate the cache on repacking
  383. if self._packed_refs is None:
  384. # set both to empty because we want _peeled_refs to be
  385. # None if and only if _packed_refs is also None.
  386. self._packed_refs = {}
  387. self._peeled_refs = {}
  388. path = os.path.join(self.path, 'packed-refs')
  389. try:
  390. f = GitFile(path, 'rb')
  391. except IOError as e:
  392. if e.errno == errno.ENOENT:
  393. return {}
  394. raise
  395. with f:
  396. first_line = next(iter(f)).rstrip()
  397. if (first_line.startswith(b'# pack-refs') and b' peeled' in
  398. first_line):
  399. for sha, name, peeled in read_packed_refs_with_peeled(f):
  400. self._packed_refs[name] = sha
  401. if peeled:
  402. self._peeled_refs[name] = peeled
  403. else:
  404. f.seek(0)
  405. for sha, name in read_packed_refs(f):
  406. self._packed_refs[name] = sha
  407. return self._packed_refs
  408. def get_peeled(self, name):
  409. """Return the cached peeled value of a ref, if available.
  410. :param name: Name of the ref to peel
  411. :return: The peeled value of the ref. If the ref is known not point to a
  412. tag, this will be the SHA the ref refers to. If the ref may point to
  413. a tag, but no cached information is available, None is returned.
  414. """
  415. self.get_packed_refs()
  416. if self._peeled_refs is None or name not in self._packed_refs:
  417. # No cache: no peeled refs were read, or this ref is loose
  418. return None
  419. if name in self._peeled_refs:
  420. return self._peeled_refs[name]
  421. else:
  422. # Known not peelable
  423. return self[name]
  424. def read_loose_ref(self, name):
  425. """Read a reference file and return its contents.
  426. If the reference file a symbolic reference, only read the first line of
  427. the file. Otherwise, only read the first 40 bytes.
  428. :param name: the refname to read, relative to refpath
  429. :return: The contents of the ref file, or None if the file does not
  430. exist.
  431. :raises IOError: if any other error occurs
  432. """
  433. filename = self.refpath(name)
  434. try:
  435. with GitFile(filename, 'rb') as f:
  436. header = f.read(len(SYMREF))
  437. if header == SYMREF:
  438. # Read only the first line
  439. return header + next(iter(f)).rstrip(b'\r\n')
  440. else:
  441. # Read only the first 40 bytes
  442. return header + f.read(40 - len(SYMREF))
  443. except IOError as e:
  444. if e.errno == errno.ENOENT:
  445. return None
  446. raise
  447. def _remove_packed_ref(self, name):
  448. if self._packed_refs is None:
  449. return
  450. filename = os.path.join(self.path, 'packed-refs')
  451. # reread cached refs from disk, while holding the lock
  452. f = GitFile(filename, 'wb')
  453. try:
  454. self._packed_refs = None
  455. self.get_packed_refs()
  456. if name not in self._packed_refs:
  457. return
  458. del self._packed_refs[name]
  459. if name in self._peeled_refs:
  460. del self._peeled_refs[name]
  461. write_packed_refs(f, self._packed_refs, self._peeled_refs)
  462. f.close()
  463. finally:
  464. f.abort()
  465. def set_symbolic_ref(self, name, other):
  466. """Make a ref point at another ref.
  467. :param name: Name of the ref to set
  468. :param other: Name of the ref to point at
  469. """
  470. self._check_refname(name)
  471. self._check_refname(other)
  472. filename = self.refpath(name)
  473. try:
  474. f = GitFile(filename, 'wb')
  475. try:
  476. f.write(SYMREF + other + b'\n')
  477. except (IOError, OSError):
  478. f.abort()
  479. raise
  480. finally:
  481. f.close()
  482. def set_if_equals(self, name, old_ref, new_ref):
  483. """Set a refname to new_ref only if it currently equals old_ref.
  484. This method follows all symbolic references, and can be used to perform
  485. an atomic compare-and-swap operation.
  486. :param name: The refname to set.
  487. :param old_ref: The old sha the refname must refer to, or None to set
  488. unconditionally.
  489. :param new_ref: The new sha the refname will refer to.
  490. :return: True if the set was successful, False otherwise.
  491. """
  492. self._check_refname(name)
  493. try:
  494. realnames, _ = self.follow(name)
  495. realname = realnames[-1]
  496. except (KeyError, IndexError):
  497. realname = name
  498. filename = self.refpath(realname)
  499. ensure_dir_exists(os.path.dirname(filename))
  500. with GitFile(filename, 'wb') as f:
  501. if old_ref is not None:
  502. try:
  503. # read again while holding the lock
  504. orig_ref = self.read_loose_ref(realname)
  505. if orig_ref is None:
  506. orig_ref = self.get_packed_refs().get(realname, ZERO_SHA)
  507. if orig_ref != old_ref:
  508. f.abort()
  509. return False
  510. except (OSError, IOError):
  511. f.abort()
  512. raise
  513. try:
  514. f.write(new_ref + b'\n')
  515. except (OSError, IOError):
  516. f.abort()
  517. raise
  518. return True
  519. def add_if_new(self, name, ref):
  520. """Add a new reference only if it does not already exist.
  521. This method follows symrefs, and only ensures that the last ref in the
  522. chain does not exist.
  523. :param name: The refname to set.
  524. :param ref: The new sha the refname will refer to.
  525. :return: True if the add was successful, False otherwise.
  526. """
  527. try:
  528. realnames, contents = self.follow(name)
  529. if contents is not None:
  530. return False
  531. realname = realnames[-1]
  532. except (KeyError, IndexError):
  533. realname = name
  534. self._check_refname(realname)
  535. filename = self.refpath(realname)
  536. ensure_dir_exists(os.path.dirname(filename))
  537. with GitFile(filename, 'wb') as f:
  538. if os.path.exists(filename) or name in self.get_packed_refs():
  539. f.abort()
  540. return False
  541. try:
  542. f.write(ref + b'\n')
  543. except (OSError, IOError):
  544. f.abort()
  545. raise
  546. return True
  547. def remove_if_equals(self, name, old_ref):
  548. """Remove a refname only if it currently equals old_ref.
  549. This method does not follow symbolic references. It can be used to
  550. perform an atomic compare-and-delete operation.
  551. :param name: The refname to delete.
  552. :param old_ref: The old sha the refname must refer to, or None to delete
  553. unconditionally.
  554. :return: True if the delete was successful, False otherwise.
  555. """
  556. self._check_refname(name)
  557. filename = self.refpath(name)
  558. ensure_dir_exists(os.path.dirname(filename))
  559. f = GitFile(filename, 'wb')
  560. try:
  561. if old_ref is not None:
  562. orig_ref = self.read_loose_ref(name)
  563. if orig_ref is None:
  564. orig_ref = self.get_packed_refs().get(name, ZERO_SHA)
  565. if orig_ref != old_ref:
  566. return False
  567. # may only be packed
  568. try:
  569. os.remove(filename)
  570. except OSError as e:
  571. if e.errno != errno.ENOENT:
  572. raise
  573. self._remove_packed_ref(name)
  574. finally:
  575. # never write, we just wanted the lock
  576. f.abort()
  577. return True
  578. def _split_ref_line(line):
  579. """Split a single ref line into a tuple of SHA1 and name."""
  580. fields = line.rstrip(b'\n\r').split(b' ')
  581. if len(fields) != 2:
  582. raise PackedRefsException("invalid ref line %r" % line)
  583. sha, name = fields
  584. if not valid_hexsha(sha):
  585. raise PackedRefsException("Invalid hex sha %r" % sha)
  586. if not check_ref_format(name):
  587. raise PackedRefsException("invalid ref name %r" % name)
  588. return (sha, name)
  589. def read_packed_refs(f):
  590. """Read a packed refs file.
  591. :param f: file-like object to read from
  592. :return: Iterator over tuples with SHA1s and ref names.
  593. """
  594. for l in f:
  595. if l.startswith(b'#'):
  596. # Comment
  597. continue
  598. if l.startswith(b'^'):
  599. raise PackedRefsException(
  600. "found peeled ref in packed-refs without peeled")
  601. yield _split_ref_line(l)
  602. def read_packed_refs_with_peeled(f):
  603. """Read a packed refs file including peeled refs.
  604. Assumes the "# pack-refs with: peeled" line was already read. Yields tuples
  605. with ref names, SHA1s, and peeled SHA1s (or None).
  606. :param f: file-like object to read from, seek'ed to the second line
  607. """
  608. last = None
  609. for l in f:
  610. if l[0] == b'#':
  611. continue
  612. l = l.rstrip(b'\r\n')
  613. if l.startswith(b'^'):
  614. if not last:
  615. raise PackedRefsException("unexpected peeled ref line")
  616. if not valid_hexsha(l[1:]):
  617. raise PackedRefsException("Invalid hex sha %r" % l[1:])
  618. sha, name = _split_ref_line(last)
  619. last = None
  620. yield (sha, name, l[1:])
  621. else:
  622. if last:
  623. sha, name = _split_ref_line(last)
  624. yield (sha, name, None)
  625. last = l
  626. if last:
  627. sha, name = _split_ref_line(last)
  628. yield (sha, name, None)
  629. def write_packed_refs(f, packed_refs, peeled_refs=None):
  630. """Write a packed refs file.
  631. :param f: empty file-like object to write to
  632. :param packed_refs: dict of refname to sha of packed refs to write
  633. :param peeled_refs: dict of refname to peeled value of sha
  634. """
  635. if peeled_refs is None:
  636. peeled_refs = {}
  637. else:
  638. f.write(b'# pack-refs with: peeled\n')
  639. for refname in sorted(packed_refs.keys()):
  640. f.write(git_line(packed_refs[refname], refname))
  641. if refname in peeled_refs:
  642. f.write(b'^' + peeled_refs[refname] + b'\n')
  643. def read_info_refs(f):
  644. ret = {}
  645. for l in f.readlines():
  646. (sha, name) = l.rstrip(b"\r\n").split(b"\t", 1)
  647. ret[name] = sha
  648. return ret
  649. def write_info_refs(refs, store):
  650. """Generate info refs."""
  651. for name, sha in sorted(refs.items()):
  652. # get_refs() includes HEAD as a special case, but we don't want to
  653. # advertise it
  654. if name == b'HEAD':
  655. continue
  656. try:
  657. o = store[sha]
  658. except KeyError:
  659. continue
  660. peeled = store.peel_sha(sha)
  661. yield o.id + b'\t' + name + b'\n'
  662. if o.id != peeled.id:
  663. yield peeled.id + b'\t' + name + ANNOTATED_TAG_SUFFIX + b'\n'
  664. is_local_branch = lambda x: x.startswith(b'refs/heads/')