2
0

repo.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312
  1. # repo.py -- For dealing with git repositories.
  2. # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
  3. # Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; version 2
  8. # of the License or (at your option) any later version of
  9. # the License.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. # MA 02110-1301, USA.
  20. """Repository access."""
  21. from cStringIO import StringIO
  22. import errno
  23. import os
  24. from dulwich.errors import (
  25. NoIndexPresent,
  26. NotBlobError,
  27. NotCommitError,
  28. NotGitRepository,
  29. NotTreeError,
  30. NotTagError,
  31. PackedRefsException,
  32. CommitError,
  33. RefFormatError,
  34. )
  35. from dulwich.file import (
  36. ensure_dir_exists,
  37. GitFile,
  38. )
  39. from dulwich.object_store import (
  40. DiskObjectStore,
  41. MemoryObjectStore,
  42. )
  43. from dulwich.objects import (
  44. Blob,
  45. Commit,
  46. ShaFile,
  47. Tag,
  48. Tree,
  49. hex_to_sha,
  50. )
  51. import warnings
  52. OBJECTDIR = 'objects'
  53. SYMREF = 'ref: '
  54. REFSDIR = 'refs'
  55. REFSDIR_TAGS = 'tags'
  56. REFSDIR_HEADS = 'heads'
  57. INDEX_FILENAME = "index"
  58. BASE_DIRECTORIES = [
  59. ["branches"],
  60. [REFSDIR],
  61. [REFSDIR, REFSDIR_TAGS],
  62. [REFSDIR, REFSDIR_HEADS],
  63. ["hooks"],
  64. ["info"]
  65. ]
  66. def read_info_refs(f):
  67. ret = {}
  68. for l in f.readlines():
  69. (sha, name) = l.rstrip("\r\n").split("\t", 1)
  70. ret[name] = sha
  71. return ret
  72. def check_ref_format(refname):
  73. """Check if a refname is correctly formatted.
  74. Implements all the same rules as git-check-ref-format[1].
  75. [1] http://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
  76. :param refname: The refname to check
  77. :return: True if refname is valid, False otherwise
  78. """
  79. # These could be combined into one big expression, but are listed separately
  80. # to parallel [1].
  81. if '/.' in refname or refname.startswith('.'):
  82. return False
  83. if '/' not in refname:
  84. return False
  85. if '..' in refname:
  86. return False
  87. for c in refname:
  88. if ord(c) < 040 or c in '\177 ~^:?*[':
  89. return False
  90. if refname[-1] in '/.':
  91. return False
  92. if refname.endswith('.lock'):
  93. return False
  94. if '@{' in refname:
  95. return False
  96. if '\\' in refname:
  97. return False
  98. return True
  99. class RefsContainer(object):
  100. """A container for refs."""
  101. def set_ref(self, name, other):
  102. warnings.warn("RefsContainer.set_ref() is deprecated."
  103. "Use set_symblic_ref instead.",
  104. category=DeprecationWarning, stacklevel=2)
  105. return self.set_symbolic_ref(name, other)
  106. def set_symbolic_ref(self, name, other):
  107. """Make a ref point at another ref.
  108. :param name: Name of the ref to set
  109. :param other: Name of the ref to point at
  110. """
  111. raise NotImplementedError(self.set_symbolic_ref)
  112. def get_packed_refs(self):
  113. """Get contents of the packed-refs file.
  114. :return: Dictionary mapping ref names to SHA1s
  115. :note: Will return an empty dictionary when no packed-refs file is
  116. present.
  117. """
  118. raise NotImplementedError(self.get_packed_refs)
  119. def get_peeled(self, name):
  120. """Return the cached peeled value of a ref, if available.
  121. :param name: Name of the ref to peel
  122. :return: The peeled value of the ref. If the ref is known not point to a
  123. tag, this will be the SHA the ref refers to. If the ref may point to
  124. a tag, but no cached information is available, None is returned.
  125. """
  126. return None
  127. def import_refs(self, base, other):
  128. for name, value in other.iteritems():
  129. self["%s/%s" % (base, name)] = value
  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 = ""
  162. for key in keys:
  163. try:
  164. ret[key] = self[("%s/%s" % (base, key)).strip("/")]
  165. except KeyError:
  166. continue # Unable to resolve
  167. return ret
  168. def _check_refname(self, name):
  169. """Ensure a refname is valid and lives in refs or is HEAD.
  170. HEAD is not a valid refname according to git-check-ref-format, but this
  171. class needs to be able to touch HEAD. Also, check_ref_format expects
  172. refnames without the leading 'refs/', but this class requires that
  173. so it cannot touch anything outside the refs dir (or HEAD).
  174. :param name: The name of the reference.
  175. :raises KeyError: if a refname is not HEAD or is otherwise not valid.
  176. """
  177. if name == 'HEAD':
  178. return
  179. if not name.startswith('refs/') or not check_ref_format(name[5:]):
  180. raise RefFormatError(name)
  181. def read_ref(self, refname):
  182. """Read a reference without following any references.
  183. :param refname: The name of the reference
  184. :return: The contents of the ref file, or None if it does
  185. not exist.
  186. """
  187. contents = self.read_loose_ref(refname)
  188. if not contents:
  189. contents = self.get_packed_refs().get(refname, None)
  190. return contents
  191. def read_loose_ref(self, name):
  192. """Read a loose reference and return its contents.
  193. :param name: the refname to read
  194. :return: The contents of the ref file, or None if it does
  195. not exist.
  196. """
  197. raise NotImplementedError(self.read_loose_ref)
  198. def _follow(self, name):
  199. """Follow a reference name.
  200. :return: a tuple of (refname, sha), where refname is the name of the
  201. last reference in the symbolic reference chain
  202. """
  203. contents = SYMREF + name
  204. depth = 0
  205. while contents.startswith(SYMREF):
  206. refname = contents[len(SYMREF):]
  207. contents = self.read_ref(refname)
  208. if not contents:
  209. break
  210. depth += 1
  211. if depth > 5:
  212. raise KeyError(name)
  213. return refname, contents
  214. def __contains__(self, refname):
  215. if self.read_ref(refname):
  216. return True
  217. return False
  218. def __getitem__(self, name):
  219. """Get the SHA1 for a reference name.
  220. This method follows all symbolic references.
  221. """
  222. _, sha = self._follow(name)
  223. if sha is None:
  224. raise KeyError(name)
  225. return sha
  226. def set_if_equals(self, name, old_ref, new_ref):
  227. """Set a refname to new_ref only if it currently equals old_ref.
  228. This method follows all symbolic references if applicable for the
  229. subclass, and can be used to perform an atomic compare-and-swap
  230. operation.
  231. :param name: The refname to set.
  232. :param old_ref: The old sha the refname must refer to, or None to set
  233. unconditionally.
  234. :param new_ref: The new sha the refname will refer to.
  235. :return: True if the set was successful, False otherwise.
  236. """
  237. raise NotImplementedError(self.set_if_equals)
  238. def add_if_new(self, name, ref):
  239. """Add a new reference only if it does not already exist."""
  240. raise NotImplementedError(self.add_if_new)
  241. def __setitem__(self, name, ref):
  242. """Set a reference name to point to the given SHA1.
  243. This method follows all symbolic references if applicable for the
  244. subclass.
  245. :note: This method unconditionally overwrites the contents of a
  246. reference. To update atomically only if the reference has not
  247. changed, use set_if_equals().
  248. :param name: The refname to set.
  249. :param ref: The new sha the refname will refer to.
  250. """
  251. self.set_if_equals(name, None, ref)
  252. def remove_if_equals(self, name, old_ref):
  253. """Remove a refname only if it currently equals old_ref.
  254. This method does not follow symbolic references, even if applicable for
  255. the subclass. It can be used to perform an atomic compare-and-delete
  256. operation.
  257. :param name: The refname to delete.
  258. :param old_ref: The old sha the refname must refer to, or None to delete
  259. unconditionally.
  260. :return: True if the delete was successful, False otherwise.
  261. """
  262. raise NotImplementedError(self.remove_if_equals)
  263. def __delitem__(self, name):
  264. """Remove a refname.
  265. This method does not follow symbolic references, even if applicable for
  266. the subclass.
  267. :note: This method unconditionally deletes the contents of a reference.
  268. To delete atomically only if the reference has not changed, use
  269. remove_if_equals().
  270. :param name: The refname to delete.
  271. """
  272. self.remove_if_equals(name, None)
  273. class DictRefsContainer(RefsContainer):
  274. """RefsContainer backed by a simple dict.
  275. This container does not support symbolic or packed references and is not
  276. threadsafe.
  277. """
  278. def __init__(self, refs):
  279. self._refs = refs
  280. self._peeled = {}
  281. def allkeys(self):
  282. return self._refs.keys()
  283. def read_loose_ref(self, name):
  284. return self._refs.get(name, None)
  285. def get_packed_refs(self):
  286. return {}
  287. def set_symbolic_ref(self, name, other):
  288. self._refs[name] = SYMREF + other
  289. def set_if_equals(self, name, old_ref, new_ref):
  290. if old_ref is not None and self._refs.get(name, None) != old_ref:
  291. return False
  292. realname, _ = self._follow(name)
  293. self._check_refname(realname)
  294. self._refs[realname] = new_ref
  295. return True
  296. def add_if_new(self, name, ref):
  297. if name in self._refs:
  298. return False
  299. self._refs[name] = ref
  300. return True
  301. def remove_if_equals(self, name, old_ref):
  302. if old_ref is not None and self._refs.get(name, None) != old_ref:
  303. return False
  304. del self._refs[name]
  305. return True
  306. def get_peeled(self, name):
  307. return self._peeled.get(name)
  308. def _update(self, refs):
  309. """Update multiple refs; intended only for testing."""
  310. # TODO(dborowitz): replace this with a public function that uses
  311. # set_if_equal.
  312. self._refs.update(refs)
  313. def _update_peeled(self, peeled):
  314. """Update cached peeled refs; intended only for testing."""
  315. self._peeled.update(peeled)
  316. class DiskRefsContainer(RefsContainer):
  317. """Refs container that reads refs from disk."""
  318. def __init__(self, path):
  319. self.path = path
  320. self._packed_refs = None
  321. self._peeled_refs = None
  322. def __repr__(self):
  323. return "%s(%r)" % (self.__class__.__name__, self.path)
  324. def subkeys(self, base):
  325. keys = set()
  326. path = self.refpath(base)
  327. for root, dirs, files in os.walk(path):
  328. dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/")
  329. for filename in files:
  330. refname = ("%s/%s" % (dir, filename)).strip("/")
  331. # check_ref_format requires at least one /, so we prepend the
  332. # base before calling it.
  333. if check_ref_format("%s/%s" % (base, refname)):
  334. keys.add(refname)
  335. for key in self.get_packed_refs():
  336. if key.startswith(base):
  337. keys.add(key[len(base):].strip("/"))
  338. return keys
  339. def allkeys(self):
  340. keys = set()
  341. if os.path.exists(self.refpath("HEAD")):
  342. keys.add("HEAD")
  343. path = self.refpath("")
  344. for root, dirs, files in os.walk(self.refpath("refs")):
  345. dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/")
  346. for filename in files:
  347. refname = ("%s/%s" % (dir, filename)).strip("/")
  348. if check_ref_format(refname):
  349. keys.add(refname)
  350. keys.update(self.get_packed_refs())
  351. return keys
  352. def refpath(self, name):
  353. """Return the disk path of a ref.
  354. """
  355. if os.path.sep != "/":
  356. name = name.replace("/", os.path.sep)
  357. return os.path.join(self.path, name)
  358. def get_packed_refs(self):
  359. """Get contents of the packed-refs file.
  360. :return: Dictionary mapping ref names to SHA1s
  361. :note: Will return an empty dictionary when no packed-refs file is
  362. present.
  363. """
  364. # TODO: invalidate the cache on repacking
  365. if self._packed_refs is None:
  366. # set both to empty because we want _peeled_refs to be
  367. # None if and only if _packed_refs is also None.
  368. self._packed_refs = {}
  369. self._peeled_refs = {}
  370. path = os.path.join(self.path, 'packed-refs')
  371. try:
  372. f = GitFile(path, 'rb')
  373. except IOError, e:
  374. if e.errno == errno.ENOENT:
  375. return {}
  376. raise
  377. try:
  378. first_line = iter(f).next().rstrip()
  379. if (first_line.startswith("# pack-refs") and " peeled" in
  380. first_line):
  381. for sha, name, peeled in read_packed_refs_with_peeled(f):
  382. self._packed_refs[name] = sha
  383. if peeled:
  384. self._peeled_refs[name] = peeled
  385. else:
  386. f.seek(0)
  387. for sha, name in read_packed_refs(f):
  388. self._packed_refs[name] = sha
  389. finally:
  390. f.close()
  391. return self._packed_refs
  392. def get_peeled(self, name):
  393. """Return the cached peeled value of a ref, if available.
  394. :param name: Name of the ref to peel
  395. :return: The peeled value of the ref. If the ref is known not point to a
  396. tag, this will be the SHA the ref refers to. If the ref may point to
  397. a tag, but no cached information is available, None is returned.
  398. """
  399. self.get_packed_refs()
  400. if self._peeled_refs is None or name not in self._packed_refs:
  401. # No cache: no peeled refs were read, or this ref is loose
  402. return None
  403. if name in self._peeled_refs:
  404. return self._peeled_refs[name]
  405. else:
  406. # Known not peelable
  407. return self[name]
  408. def read_loose_ref(self, name):
  409. """Read a reference file and return its contents.
  410. If the reference file a symbolic reference, only read the first line of
  411. the file. Otherwise, only read the first 40 bytes.
  412. :param name: the refname to read, relative to refpath
  413. :return: The contents of the ref file, or None if the file does not
  414. exist.
  415. :raises IOError: if any other error occurs
  416. """
  417. filename = self.refpath(name)
  418. try:
  419. f = GitFile(filename, 'rb')
  420. try:
  421. header = f.read(len(SYMREF))
  422. if header == SYMREF:
  423. # Read only the first line
  424. return header + iter(f).next().rstrip("\r\n")
  425. else:
  426. # Read only the first 40 bytes
  427. return header + f.read(40-len(SYMREF))
  428. finally:
  429. f.close()
  430. except IOError, e:
  431. if e.errno == errno.ENOENT:
  432. return None
  433. raise
  434. def _remove_packed_ref(self, name):
  435. if self._packed_refs is None:
  436. return
  437. filename = os.path.join(self.path, 'packed-refs')
  438. # reread cached refs from disk, while holding the lock
  439. f = GitFile(filename, 'wb')
  440. try:
  441. self._packed_refs = None
  442. self.get_packed_refs()
  443. if name not in self._packed_refs:
  444. return
  445. del self._packed_refs[name]
  446. if name in self._peeled_refs:
  447. del self._peeled_refs[name]
  448. write_packed_refs(f, self._packed_refs, self._peeled_refs)
  449. f.close()
  450. finally:
  451. f.abort()
  452. def set_symbolic_ref(self, name, other):
  453. """Make a ref point at another ref.
  454. :param name: Name of the ref to set
  455. :param other: Name of the ref to point at
  456. """
  457. self._check_refname(name)
  458. self._check_refname(other)
  459. filename = self.refpath(name)
  460. try:
  461. f = GitFile(filename, 'wb')
  462. try:
  463. f.write(SYMREF + other + '\n')
  464. except (IOError, OSError):
  465. f.abort()
  466. raise
  467. finally:
  468. f.close()
  469. def set_if_equals(self, name, old_ref, new_ref):
  470. """Set a refname to new_ref only if it currently equals old_ref.
  471. This method follows all symbolic references, and can be used to perform
  472. an atomic compare-and-swap operation.
  473. :param name: The refname to set.
  474. :param old_ref: The old sha the refname must refer to, or None to set
  475. unconditionally.
  476. :param new_ref: The new sha the refname will refer to.
  477. :return: True if the set was successful, False otherwise.
  478. """
  479. self._check_refname(name)
  480. try:
  481. realname, _ = self._follow(name)
  482. except KeyError:
  483. realname = name
  484. filename = self.refpath(realname)
  485. ensure_dir_exists(os.path.dirname(filename))
  486. f = GitFile(filename, 'wb')
  487. try:
  488. if old_ref is not None:
  489. try:
  490. # read again while holding the lock
  491. orig_ref = self.read_loose_ref(realname)
  492. if orig_ref is None:
  493. orig_ref = self.get_packed_refs().get(realname, None)
  494. if orig_ref != old_ref:
  495. f.abort()
  496. return False
  497. except (OSError, IOError):
  498. f.abort()
  499. raise
  500. try:
  501. f.write(new_ref+"\n")
  502. except (OSError, IOError):
  503. f.abort()
  504. raise
  505. finally:
  506. f.close()
  507. return True
  508. def add_if_new(self, name, ref):
  509. """Add a new reference only if it does not already exist.
  510. This method follows symrefs, and only ensures that the last ref in the
  511. chain does not exist.
  512. :param name: The refname to set.
  513. :param ref: The new sha the refname will refer to.
  514. :return: True if the add was successful, False otherwise.
  515. """
  516. try:
  517. realname, contents = self._follow(name)
  518. if contents is not None:
  519. return False
  520. except KeyError:
  521. realname = name
  522. self._check_refname(realname)
  523. filename = self.refpath(realname)
  524. ensure_dir_exists(os.path.dirname(filename))
  525. f = GitFile(filename, 'wb')
  526. try:
  527. if os.path.exists(filename) or name in self.get_packed_refs():
  528. f.abort()
  529. return False
  530. try:
  531. f.write(ref+"\n")
  532. except (OSError, IOError):
  533. f.abort()
  534. raise
  535. finally:
  536. f.close()
  537. return True
  538. def remove_if_equals(self, name, old_ref):
  539. """Remove a refname only if it currently equals old_ref.
  540. This method does not follow symbolic references. It can be used to
  541. perform an atomic compare-and-delete operation.
  542. :param name: The refname to delete.
  543. :param old_ref: The old sha the refname must refer to, or None to delete
  544. unconditionally.
  545. :return: True if the delete was successful, False otherwise.
  546. """
  547. self._check_refname(name)
  548. filename = self.refpath(name)
  549. ensure_dir_exists(os.path.dirname(filename))
  550. f = GitFile(filename, 'wb')
  551. try:
  552. if old_ref is not None:
  553. orig_ref = self.read_loose_ref(name)
  554. if orig_ref is None:
  555. orig_ref = self.get_packed_refs().get(name, None)
  556. if orig_ref != old_ref:
  557. return False
  558. # may only be packed
  559. try:
  560. os.remove(filename)
  561. except OSError, e:
  562. if e.errno != errno.ENOENT:
  563. raise
  564. self._remove_packed_ref(name)
  565. finally:
  566. # never write, we just wanted the lock
  567. f.abort()
  568. return True
  569. def _split_ref_line(line):
  570. """Split a single ref line into a tuple of SHA1 and name."""
  571. fields = line.rstrip("\n").split(" ")
  572. if len(fields) != 2:
  573. raise PackedRefsException("invalid ref line '%s'" % line)
  574. sha, name = fields
  575. try:
  576. hex_to_sha(sha)
  577. except (AssertionError, TypeError), e:
  578. raise PackedRefsException(e)
  579. if not check_ref_format(name):
  580. raise PackedRefsException("invalid ref name '%s'" % name)
  581. return (sha, name)
  582. def read_packed_refs(f):
  583. """Read a packed refs file.
  584. :param f: file-like object to read from
  585. :return: Iterator over tuples with SHA1s and ref names.
  586. """
  587. for l in f:
  588. if l[0] == "#":
  589. # Comment
  590. continue
  591. if l[0] == "^":
  592. raise PackedRefsException(
  593. "found peeled ref in packed-refs without peeled")
  594. yield _split_ref_line(l)
  595. def read_packed_refs_with_peeled(f):
  596. """Read a packed refs file including peeled refs.
  597. Assumes the "# pack-refs with: peeled" line was already read. Yields tuples
  598. with ref names, SHA1s, and peeled SHA1s (or None).
  599. :param f: file-like object to read from, seek'ed to the second line
  600. """
  601. last = None
  602. for l in f:
  603. if l[0] == "#":
  604. continue
  605. l = l.rstrip("\r\n")
  606. if l[0] == "^":
  607. if not last:
  608. raise PackedRefsException("unexpected peeled ref line")
  609. try:
  610. hex_to_sha(l[1:])
  611. except (AssertionError, TypeError), e:
  612. raise PackedRefsException(e)
  613. sha, name = _split_ref_line(last)
  614. last = None
  615. yield (sha, name, l[1:])
  616. else:
  617. if last:
  618. sha, name = _split_ref_line(last)
  619. yield (sha, name, None)
  620. last = l
  621. if last:
  622. sha, name = _split_ref_line(last)
  623. yield (sha, name, None)
  624. def write_packed_refs(f, packed_refs, peeled_refs=None):
  625. """Write a packed refs file.
  626. :param f: empty file-like object to write to
  627. :param packed_refs: dict of refname to sha of packed refs to write
  628. :param peeled_refs: dict of refname to peeled value of sha
  629. """
  630. if peeled_refs is None:
  631. peeled_refs = {}
  632. else:
  633. f.write('# pack-refs with: peeled\n')
  634. for refname in sorted(packed_refs.iterkeys()):
  635. f.write('%s %s\n' % (packed_refs[refname], refname))
  636. if refname in peeled_refs:
  637. f.write('^%s\n' % peeled_refs[refname])
  638. class BaseRepo(object):
  639. """Base class for a git repository.
  640. :ivar object_store: Dictionary-like object for accessing
  641. the objects
  642. :ivar refs: Dictionary-like object with the refs in this repository
  643. """
  644. def __init__(self, object_store, refs):
  645. self.object_store = object_store
  646. self.refs = refs
  647. def _init_files(self, bare):
  648. """Initialize a default set of named files."""
  649. self._put_named_file('description', "Unnamed repository")
  650. self._put_named_file('config', ('[core]\n'
  651. 'repositoryformatversion = 0\n'
  652. 'filemode = true\n'
  653. 'bare = ' + str(bare).lower() + '\n'
  654. 'logallrefupdates = true\n'))
  655. self._put_named_file(os.path.join('info', 'exclude'), '')
  656. def get_named_file(self, path):
  657. """Get a file from the control dir with a specific name.
  658. Although the filename should be interpreted as a filename relative to
  659. the control dir in a disk-based Repo, the object returned need not be
  660. pointing to a file in that location.
  661. :param path: The path to the file, relative to the control dir.
  662. :return: An open file object, or None if the file does not exist.
  663. """
  664. raise NotImplementedError(self.get_named_file)
  665. def _put_named_file(self, path, contents):
  666. """Write a file to the control dir with the given name and contents.
  667. :param path: The path to the file, relative to the control dir.
  668. :param contents: A string to write to the file.
  669. """
  670. raise NotImplementedError(self._put_named_file)
  671. def open_index(self):
  672. """Open the index for this repository.
  673. :raises NoIndexPresent: If no index is present
  674. :return: Index instance
  675. """
  676. raise NotImplementedError(self.open_index)
  677. def fetch(self, target, determine_wants=None, progress=None):
  678. """Fetch objects into another repository.
  679. :param target: The target repository
  680. :param determine_wants: Optional function to determine what refs to
  681. fetch.
  682. :param progress: Optional progress function
  683. """
  684. if determine_wants is None:
  685. determine_wants = lambda heads: heads.values()
  686. target.object_store.add_objects(
  687. self.fetch_objects(determine_wants, target.get_graph_walker(),
  688. progress))
  689. return self.get_refs()
  690. def fetch_objects(self, determine_wants, graph_walker, progress,
  691. get_tagged=None):
  692. """Fetch the missing objects required for a set of revisions.
  693. :param determine_wants: Function that takes a dictionary with heads
  694. and returns the list of heads to fetch.
  695. :param graph_walker: Object that can iterate over the list of revisions
  696. to fetch and has an "ack" method that will be called to acknowledge
  697. that a revision is present.
  698. :param progress: Simple progress function that will be called with
  699. updated progress strings.
  700. :param get_tagged: Function that returns a dict of pointed-to sha -> tag
  701. sha for including tags.
  702. :return: iterator over objects, with __len__ implemented
  703. """
  704. wants = determine_wants(self.get_refs())
  705. if wants is None:
  706. # TODO(dborowitz): find a way to short-circuit that doesn't change
  707. # this interface.
  708. return None
  709. haves = self.object_store.find_common_revisions(graph_walker)
  710. return self.object_store.iter_shas(
  711. self.object_store.find_missing_objects(haves, wants, progress,
  712. get_tagged))
  713. def get_graph_walker(self, heads=None):
  714. if heads is None:
  715. heads = self.refs.as_dict('refs/heads').values()
  716. return self.object_store.get_graph_walker(heads)
  717. def ref(self, name):
  718. """Return the SHA1 a ref is pointing to."""
  719. return self.refs[name]
  720. def get_refs(self):
  721. """Get dictionary with all refs."""
  722. return self.refs.as_dict()
  723. def head(self):
  724. """Return the SHA1 pointed at by HEAD."""
  725. return self.refs['HEAD']
  726. def _get_object(self, sha, cls):
  727. assert len(sha) in (20, 40)
  728. ret = self.get_object(sha)
  729. if not isinstance(ret, cls):
  730. if cls is Commit:
  731. raise NotCommitError(ret)
  732. elif cls is Blob:
  733. raise NotBlobError(ret)
  734. elif cls is Tree:
  735. raise NotTreeError(ret)
  736. elif cls is Tag:
  737. raise NotTagError(ret)
  738. else:
  739. raise Exception("Type invalid: %r != %r" % (
  740. ret.type_name, cls.type_name))
  741. return ret
  742. def get_object(self, sha):
  743. return self.object_store[sha]
  744. def get_parents(self, sha):
  745. return self.commit(sha).parents
  746. def get_config(self):
  747. import ConfigParser
  748. p = ConfigParser.RawConfigParser()
  749. p.read(os.path.join(self._controldir, 'config'))
  750. return dict((section, dict(p.items(section)))
  751. for section in p.sections())
  752. def commit(self, sha):
  753. """Retrieve the commit with a particular SHA.
  754. :param sha: SHA of the commit to retrieve
  755. :raise NotCommitError: If the SHA provided doesn't point at a Commit
  756. :raise KeyError: If the SHA provided didn't exist
  757. :return: A `Commit` object
  758. """
  759. warnings.warn("Repo.commit(sha) is deprecated. Use Repo[sha] instead.",
  760. category=DeprecationWarning, stacklevel=2)
  761. return self._get_object(sha, Commit)
  762. def tree(self, sha):
  763. """Retrieve the tree with a particular SHA.
  764. :param sha: SHA of the tree to retrieve
  765. :raise NotTreeError: If the SHA provided doesn't point at a Tree
  766. :raise KeyError: If the SHA provided didn't exist
  767. :return: A `Tree` object
  768. """
  769. warnings.warn("Repo.tree(sha) is deprecated. Use Repo[sha] instead.",
  770. category=DeprecationWarning, stacklevel=2)
  771. return self._get_object(sha, Tree)
  772. def tag(self, sha):
  773. """Retrieve the tag with a particular SHA.
  774. :param sha: SHA of the tag to retrieve
  775. :raise NotTagError: If the SHA provided doesn't point at a Tag
  776. :raise KeyError: If the SHA provided didn't exist
  777. :return: A `Tag` object
  778. """
  779. warnings.warn("Repo.tag(sha) is deprecated. Use Repo[sha] instead.",
  780. category=DeprecationWarning, stacklevel=2)
  781. return self._get_object(sha, Tag)
  782. def get_blob(self, sha):
  783. """Retrieve the blob with a particular SHA.
  784. :param sha: SHA of the blob to retrieve
  785. :raise NotBlobError: If the SHA provided doesn't point at a Blob
  786. :raise KeyError: If the SHA provided didn't exist
  787. :return: A `Blob` object
  788. """
  789. warnings.warn("Repo.get_blob(sha) is deprecated. Use Repo[sha] "
  790. "instead.", category=DeprecationWarning, stacklevel=2)
  791. return self._get_object(sha, Blob)
  792. def get_peeled(self, ref):
  793. """Get the peeled value of a ref.
  794. :param ref: The refname to peel.
  795. :return: The fully-peeled SHA1 of a tag object, after peeling all
  796. intermediate tags; if the original ref does not point to a tag, this
  797. will equal the original SHA1.
  798. """
  799. cached = self.refs.get_peeled(ref)
  800. if cached is not None:
  801. return cached
  802. return self.object_store.peel_sha(self.refs[ref]).id
  803. def get_walker(self, include=None, *args, **kwargs):
  804. """Obtain a walker for this repository.
  805. :param include: Iterable of SHAs of commits to include along with their
  806. ancestors. Defaults to [HEAD]
  807. :param exclude: Iterable of SHAs of commits to exclude along with their
  808. ancestors, overriding includes.
  809. :param order: ORDER_* constant specifying the order of results. Anything
  810. other than ORDER_DATE may result in O(n) memory usage.
  811. :param reverse: If True, reverse the order of output, requiring O(n)
  812. memory.
  813. :param max_entries: The maximum number of entries to yield, or None for
  814. no limit.
  815. :param paths: Iterable of file or subtree paths to show entries for.
  816. :param rename_detector: diff.RenameDetector object for detecting
  817. renames.
  818. :param follow: If True, follow path across renames/copies. Forces a
  819. default rename_detector.
  820. :param since: Timestamp to list commits after.
  821. :param until: Timestamp to list commits before.
  822. :param queue_cls: A class to use for a queue of commits, supporting the
  823. iterator protocol. The constructor takes a single argument, the
  824. Walker.
  825. """
  826. from dulwich.walk import Walker
  827. if include is None:
  828. include = [self.head()]
  829. return Walker(self.object_store, include, *args, **kwargs)
  830. def revision_history(self, head):
  831. """Returns a list of the commits reachable from head.
  832. :param head: The SHA of the head to list revision history for.
  833. :return: A list of commit objects reachable from head, starting with
  834. head itself, in descending commit time order.
  835. :raise MissingCommitError: if any missing commits are referenced,
  836. including if the head parameter isn't the SHA of a commit.
  837. """
  838. warnings.warn("Repo.revision_history() is deprecated."
  839. "Use dulwich.walker.Walker(repo) instead.",
  840. category=DeprecationWarning, stacklevel=2)
  841. return [e.commit for e in self.get_walker(include=[head])]
  842. def __getitem__(self, name):
  843. if len(name) in (20, 40):
  844. try:
  845. return self.object_store[name]
  846. except KeyError:
  847. pass
  848. try:
  849. return self.object_store[self.refs[name]]
  850. except RefFormatError:
  851. raise KeyError(name)
  852. def __contains__(self, name):
  853. if len(name) in (20, 40):
  854. return name in self.object_store or name in self.refs
  855. else:
  856. return name in self.refs
  857. def __setitem__(self, name, value):
  858. if name.startswith("refs/") or name == "HEAD":
  859. if isinstance(value, ShaFile):
  860. self.refs[name] = value.id
  861. elif isinstance(value, str):
  862. self.refs[name] = value
  863. else:
  864. raise TypeError(value)
  865. else:
  866. raise ValueError(name)
  867. def __delitem__(self, name):
  868. if name.startswith("refs") or name == "HEAD":
  869. del self.refs[name]
  870. else:
  871. raise ValueError(name)
  872. def do_commit(self, message=None, committer=None,
  873. author=None, commit_timestamp=None,
  874. commit_timezone=None, author_timestamp=None,
  875. author_timezone=None, tree=None, encoding=None,
  876. ref='HEAD', merge_heads=None):
  877. """Create a new commit.
  878. :param message: Commit message
  879. :param committer: Committer fullname
  880. :param author: Author fullname (defaults to committer)
  881. :param commit_timestamp: Commit timestamp (defaults to now)
  882. :param commit_timezone: Commit timestamp timezone (defaults to GMT)
  883. :param author_timestamp: Author timestamp (defaults to commit timestamp)
  884. :param author_timezone: Author timestamp timezone
  885. (defaults to commit timestamp timezone)
  886. :param tree: SHA1 of the tree root to use (if not specified the
  887. current index will be committed).
  888. :param encoding: Encoding
  889. :param ref: Optional ref to commit to (defaults to current branch)
  890. :param merge_heads: Merge heads (defaults to .git/MERGE_HEADS)
  891. :return: New commit SHA1
  892. """
  893. import time
  894. c = Commit()
  895. if tree is None:
  896. index = self.open_index()
  897. c.tree = index.commit(self.object_store)
  898. else:
  899. if len(tree) != 40:
  900. raise ValueError("tree must be a 40-byte hex sha string")
  901. c.tree = tree
  902. if merge_heads is None:
  903. # FIXME: Read merge heads from .git/MERGE_HEADS
  904. merge_heads = []
  905. # TODO: Allow username to be missing, and get it from .git/config
  906. if committer is None:
  907. raise ValueError("committer not set")
  908. c.committer = committer
  909. if commit_timestamp is None:
  910. commit_timestamp = time.time()
  911. c.commit_time = int(commit_timestamp)
  912. if commit_timezone is None:
  913. # FIXME: Use current user timezone rather than UTC
  914. commit_timezone = 0
  915. c.commit_timezone = commit_timezone
  916. if author is None:
  917. author = committer
  918. c.author = author
  919. if author_timestamp is None:
  920. author_timestamp = commit_timestamp
  921. c.author_time = int(author_timestamp)
  922. if author_timezone is None:
  923. author_timezone = commit_timezone
  924. c.author_timezone = author_timezone
  925. if encoding is not None:
  926. c.encoding = encoding
  927. if message is None:
  928. # FIXME: Try to read commit message from .git/MERGE_MSG
  929. raise ValueError("No commit message specified")
  930. c.message = message
  931. try:
  932. old_head = self.refs[ref]
  933. c.parents = [old_head] + merge_heads
  934. self.object_store.add_object(c)
  935. ok = self.refs.set_if_equals(ref, old_head, c.id)
  936. except KeyError:
  937. c.parents = merge_heads
  938. self.object_store.add_object(c)
  939. ok = self.refs.add_if_new(ref, c.id)
  940. if not ok:
  941. # Fail if the atomic compare-and-swap failed, leaving the commit and
  942. # all its objects as garbage.
  943. raise CommitError("%s changed during commit" % (ref,))
  944. return c.id
  945. class Repo(BaseRepo):
  946. """A git repository backed by local disk."""
  947. def __init__(self, root):
  948. if os.path.isdir(os.path.join(root, ".git", OBJECTDIR)):
  949. self.bare = False
  950. self._controldir = os.path.join(root, ".git")
  951. elif (os.path.isdir(os.path.join(root, OBJECTDIR)) and
  952. os.path.isdir(os.path.join(root, REFSDIR))):
  953. self.bare = True
  954. self._controldir = root
  955. else:
  956. raise NotGitRepository(root)
  957. self.path = root
  958. object_store = DiskObjectStore(os.path.join(self.controldir(),
  959. OBJECTDIR))
  960. refs = DiskRefsContainer(self.controldir())
  961. BaseRepo.__init__(self, object_store, refs)
  962. def controldir(self):
  963. """Return the path of the control directory."""
  964. return self._controldir
  965. def _put_named_file(self, path, contents):
  966. """Write a file to the control dir with the given name and contents.
  967. :param path: The path to the file, relative to the control dir.
  968. :param contents: A string to write to the file.
  969. """
  970. path = path.lstrip(os.path.sep)
  971. f = GitFile(os.path.join(self.controldir(), path), 'wb')
  972. try:
  973. f.write(contents)
  974. finally:
  975. f.close()
  976. def get_named_file(self, path):
  977. """Get a file from the control dir with a specific name.
  978. Although the filename should be interpreted as a filename relative to
  979. the control dir in a disk-based Repo, the object returned need not be
  980. pointing to a file in that location.
  981. :param path: The path to the file, relative to the control dir.
  982. :return: An open file object, or None if the file does not exist.
  983. """
  984. # TODO(dborowitz): sanitize filenames, since this is used directly by
  985. # the dumb web serving code.
  986. path = path.lstrip(os.path.sep)
  987. try:
  988. return open(os.path.join(self.controldir(), path), 'rb')
  989. except (IOError, OSError), e:
  990. if e.errno == errno.ENOENT:
  991. return None
  992. raise
  993. def index_path(self):
  994. """Return path to the index file."""
  995. return os.path.join(self.controldir(), INDEX_FILENAME)
  996. def open_index(self):
  997. """Open the index for this repository."""
  998. from dulwich.index import Index
  999. if not self.has_index():
  1000. raise NoIndexPresent()
  1001. return Index(self.index_path())
  1002. def has_index(self):
  1003. """Check if an index is present."""
  1004. # Bare repos must never have index files; non-bare repos may have a
  1005. # missing index file, which is treated as empty.
  1006. return not self.bare
  1007. def stage(self, paths):
  1008. """Stage a set of paths.
  1009. :param paths: List of paths, relative to the repository path
  1010. """
  1011. from dulwich.index import cleanup_mode
  1012. index = self.open_index()
  1013. for path in paths:
  1014. full_path = os.path.join(self.path, path)
  1015. blob = Blob()
  1016. try:
  1017. st = os.stat(full_path)
  1018. except OSError:
  1019. # File no longer exists
  1020. try:
  1021. del index[path]
  1022. except KeyError:
  1023. pass # Doesn't exist in the index either
  1024. else:
  1025. f = open(full_path, 'rb')
  1026. try:
  1027. blob.data = f.read()
  1028. finally:
  1029. f.close()
  1030. self.object_store.add_object(blob)
  1031. # XXX: Cleanup some of the other file properties as well?
  1032. index[path] = (st.st_ctime, st.st_mtime, st.st_dev, st.st_ino,
  1033. cleanup_mode(st.st_mode), st.st_uid, st.st_gid, st.st_size,
  1034. blob.id, 0)
  1035. index.write()
  1036. def clone(self, target_path, mkdir=True, bare=False, origin="origin"):
  1037. """Clone this repository.
  1038. :param target_path: Target path
  1039. :param mkdir: Create the target directory
  1040. :param bare: Whether to create a bare repository
  1041. :return: Created repository
  1042. """
  1043. if not bare:
  1044. target = self.init(target_path, mkdir=mkdir)
  1045. else:
  1046. target = self.init_bare(target_path)
  1047. self.fetch(target)
  1048. target.refs.import_refs(
  1049. 'refs/remotes/'+origin, self.refs.as_dict('refs/heads'))
  1050. target.refs.import_refs(
  1051. 'refs/tags', self.refs.as_dict('refs/tags'))
  1052. try:
  1053. target.refs.add_if_new(
  1054. 'refs/heads/master',
  1055. self.refs['refs/heads/master'])
  1056. except KeyError:
  1057. pass
  1058. return target
  1059. def __repr__(self):
  1060. return "<Repo at %r>" % self.path
  1061. @classmethod
  1062. def _init_maybe_bare(cls, path, bare):
  1063. for d in BASE_DIRECTORIES:
  1064. os.mkdir(os.path.join(path, *d))
  1065. DiskObjectStore.init(os.path.join(path, OBJECTDIR))
  1066. ret = cls(path)
  1067. ret.refs.set_symbolic_ref("HEAD", "refs/heads/master")
  1068. ret._init_files(bare)
  1069. return ret
  1070. @classmethod
  1071. def init(cls, path, mkdir=False):
  1072. if mkdir:
  1073. os.mkdir(path)
  1074. controldir = os.path.join(path, ".git")
  1075. os.mkdir(controldir)
  1076. cls._init_maybe_bare(controldir, False)
  1077. return cls(path)
  1078. @classmethod
  1079. def init_bare(cls, path):
  1080. return cls._init_maybe_bare(path, True)
  1081. create = init_bare
  1082. class MemoryRepo(BaseRepo):
  1083. """Repo that stores refs, objects, and named files in memory.
  1084. MemoryRepos are always bare: they have no working tree and no index, since
  1085. those have a stronger dependency on the filesystem.
  1086. """
  1087. def __init__(self):
  1088. BaseRepo.__init__(self, MemoryObjectStore(), DictRefsContainer({}))
  1089. self._named_files = {}
  1090. self.bare = True
  1091. def _put_named_file(self, path, contents):
  1092. """Write a file to the control dir with the given name and contents.
  1093. :param path: The path to the file, relative to the control dir.
  1094. :param contents: A string to write to the file.
  1095. """
  1096. self._named_files[path] = contents
  1097. def get_named_file(self, path):
  1098. """Get a file from the control dir with a specific name.
  1099. Although the filename should be interpreted as a filename relative to
  1100. the control dir in a disk-baked Repo, the object returned need not be
  1101. pointing to a file in that location.
  1102. :param path: The path to the file, relative to the control dir.
  1103. :return: An open file object, or None if the file does not exist.
  1104. """
  1105. contents = self._named_files.get(path, None)
  1106. if contents is None:
  1107. return None
  1108. return StringIO(contents)
  1109. def open_index(self):
  1110. """Fail to open index for this repo, since it is bare."""
  1111. raise NoIndexPresent()
  1112. @classmethod
  1113. def init_bare(cls, objects, refs):
  1114. ret = cls()
  1115. for obj in objects:
  1116. ret.object_store.add_object(obj)
  1117. for refname, sha in refs.iteritems():
  1118. ret.refs[refname] = sha
  1119. ret._init_files(bare=True)
  1120. return ret