refs.py 32 KB

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