refs.py 25 KB

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