refs.py 30 KB

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