refs.py 25 KB

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