2
0

repo.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357
  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 in ('HEAD', 'refs/stash'):
  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 InfoRefsContainer(RefsContainer):
  317. """Refs container that reads refs from a info/refs file."""
  318. def __init__(self, f):
  319. self._refs = {}
  320. self._peeled = {}
  321. for l in f.readlines():
  322. sha, name = l.rstrip("\n").split("\t")
  323. if name.endswith("^{}"):
  324. name = name[:-3]
  325. if not check_ref_format(name):
  326. raise ValueError("invalid ref name '%s'" % name)
  327. self._peeled[name] = sha
  328. else:
  329. if not check_ref_format(name):
  330. raise ValueError("invalid ref name '%s'" % name)
  331. self._refs[name] = sha
  332. def allkeys(self):
  333. return self._refs.keys()
  334. def read_loose_ref(self, name):
  335. return self._refs.get(name, None)
  336. def get_packed_refs(self):
  337. return {}
  338. def get_peeled(self, name):
  339. try:
  340. return self._peeled[name]
  341. except KeyError:
  342. return self._refs[name]
  343. class DiskRefsContainer(RefsContainer):
  344. """Refs container that reads refs from disk."""
  345. def __init__(self, path):
  346. self.path = path
  347. self._packed_refs = None
  348. self._peeled_refs = None
  349. def __repr__(self):
  350. return "%s(%r)" % (self.__class__.__name__, self.path)
  351. def subkeys(self, base):
  352. keys = set()
  353. path = self.refpath(base)
  354. for root, dirs, files in os.walk(path):
  355. dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/")
  356. for filename in files:
  357. refname = ("%s/%s" % (dir, filename)).strip("/")
  358. # check_ref_format requires at least one /, so we prepend the
  359. # base before calling it.
  360. if check_ref_format("%s/%s" % (base, refname)):
  361. keys.add(refname)
  362. for key in self.get_packed_refs():
  363. if key.startswith(base):
  364. keys.add(key[len(base):].strip("/"))
  365. return keys
  366. def allkeys(self):
  367. keys = set()
  368. if os.path.exists(self.refpath("HEAD")):
  369. keys.add("HEAD")
  370. path = self.refpath("")
  371. for root, dirs, files in os.walk(self.refpath("refs")):
  372. dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/")
  373. for filename in files:
  374. refname = ("%s/%s" % (dir, filename)).strip("/")
  375. if check_ref_format(refname):
  376. keys.add(refname)
  377. keys.update(self.get_packed_refs())
  378. return keys
  379. def refpath(self, name):
  380. """Return the disk path of a ref.
  381. """
  382. if os.path.sep != "/":
  383. name = name.replace("/", os.path.sep)
  384. return os.path.join(self.path, name)
  385. def get_packed_refs(self):
  386. """Get contents of the packed-refs file.
  387. :return: Dictionary mapping ref names to SHA1s
  388. :note: Will return an empty dictionary when no packed-refs file is
  389. present.
  390. """
  391. # TODO: invalidate the cache on repacking
  392. if self._packed_refs is None:
  393. # set both to empty because we want _peeled_refs to be
  394. # None if and only if _packed_refs is also None.
  395. self._packed_refs = {}
  396. self._peeled_refs = {}
  397. path = os.path.join(self.path, 'packed-refs')
  398. try:
  399. f = GitFile(path, 'rb')
  400. except IOError, e:
  401. if e.errno == errno.ENOENT:
  402. return {}
  403. raise
  404. try:
  405. first_line = iter(f).next().rstrip()
  406. if (first_line.startswith("# pack-refs") and " peeled" in
  407. first_line):
  408. for sha, name, peeled in read_packed_refs_with_peeled(f):
  409. self._packed_refs[name] = sha
  410. if peeled:
  411. self._peeled_refs[name] = peeled
  412. else:
  413. f.seek(0)
  414. for sha, name in read_packed_refs(f):
  415. self._packed_refs[name] = sha
  416. finally:
  417. f.close()
  418. return self._packed_refs
  419. def get_peeled(self, name):
  420. """Return the cached peeled value of a ref, if available.
  421. :param name: Name of the ref to peel
  422. :return: The peeled value of the ref. If the ref is known not point to a
  423. tag, this will be the SHA the ref refers to. If the ref may point to
  424. a tag, but no cached information is available, None is returned.
  425. """
  426. self.get_packed_refs()
  427. if self._peeled_refs is None or name not in self._packed_refs:
  428. # No cache: no peeled refs were read, or this ref is loose
  429. return None
  430. if name in self._peeled_refs:
  431. return self._peeled_refs[name]
  432. else:
  433. # Known not peelable
  434. return self[name]
  435. def read_loose_ref(self, name):
  436. """Read a reference file and return its contents.
  437. If the reference file a symbolic reference, only read the first line of
  438. the file. Otherwise, only read the first 40 bytes.
  439. :param name: the refname to read, relative to refpath
  440. :return: The contents of the ref file, or None if the file does not
  441. exist.
  442. :raises IOError: if any other error occurs
  443. """
  444. filename = self.refpath(name)
  445. try:
  446. f = GitFile(filename, 'rb')
  447. try:
  448. header = f.read(len(SYMREF))
  449. if header == SYMREF:
  450. # Read only the first line
  451. return header + iter(f).next().rstrip("\r\n")
  452. else:
  453. # Read only the first 40 bytes
  454. return header + f.read(40-len(SYMREF))
  455. finally:
  456. f.close()
  457. except IOError, e:
  458. if e.errno == errno.ENOENT:
  459. return None
  460. raise
  461. def _remove_packed_ref(self, name):
  462. if self._packed_refs is None:
  463. return
  464. filename = os.path.join(self.path, 'packed-refs')
  465. # reread cached refs from disk, while holding the lock
  466. f = GitFile(filename, 'wb')
  467. try:
  468. self._packed_refs = None
  469. self.get_packed_refs()
  470. if name not in self._packed_refs:
  471. return
  472. del self._packed_refs[name]
  473. if name in self._peeled_refs:
  474. del self._peeled_refs[name]
  475. write_packed_refs(f, self._packed_refs, self._peeled_refs)
  476. f.close()
  477. finally:
  478. f.abort()
  479. def set_symbolic_ref(self, name, other):
  480. """Make a ref point at another ref.
  481. :param name: Name of the ref to set
  482. :param other: Name of the ref to point at
  483. """
  484. self._check_refname(name)
  485. self._check_refname(other)
  486. filename = self.refpath(name)
  487. try:
  488. f = GitFile(filename, 'wb')
  489. try:
  490. f.write(SYMREF + other + '\n')
  491. except (IOError, OSError):
  492. f.abort()
  493. raise
  494. finally:
  495. f.close()
  496. def set_if_equals(self, name, old_ref, new_ref):
  497. """Set a refname to new_ref only if it currently equals old_ref.
  498. This method follows all symbolic references, and can be used to perform
  499. an atomic compare-and-swap operation.
  500. :param name: The refname to set.
  501. :param old_ref: The old sha the refname must refer to, or None to set
  502. unconditionally.
  503. :param new_ref: The new sha the refname will refer to.
  504. :return: True if the set was successful, False otherwise.
  505. """
  506. self._check_refname(name)
  507. try:
  508. realname, _ = self._follow(name)
  509. except KeyError:
  510. realname = name
  511. filename = self.refpath(realname)
  512. ensure_dir_exists(os.path.dirname(filename))
  513. f = GitFile(filename, 'wb')
  514. try:
  515. if old_ref is not None:
  516. try:
  517. # read again while holding the lock
  518. orig_ref = self.read_loose_ref(realname)
  519. if orig_ref is None:
  520. orig_ref = self.get_packed_refs().get(realname, None)
  521. if orig_ref != old_ref:
  522. f.abort()
  523. return False
  524. except (OSError, IOError):
  525. f.abort()
  526. raise
  527. try:
  528. f.write(new_ref+"\n")
  529. except (OSError, IOError):
  530. f.abort()
  531. raise
  532. finally:
  533. f.close()
  534. return True
  535. def add_if_new(self, name, ref):
  536. """Add a new reference only if it does not already exist.
  537. This method follows symrefs, and only ensures that the last ref in the
  538. chain does not exist.
  539. :param name: The refname to set.
  540. :param ref: The new sha the refname will refer to.
  541. :return: True if the add was successful, False otherwise.
  542. """
  543. try:
  544. realname, contents = self._follow(name)
  545. if contents is not None:
  546. return False
  547. except KeyError:
  548. realname = name
  549. self._check_refname(realname)
  550. filename = self.refpath(realname)
  551. ensure_dir_exists(os.path.dirname(filename))
  552. f = GitFile(filename, 'wb')
  553. try:
  554. if os.path.exists(filename) or name in self.get_packed_refs():
  555. f.abort()
  556. return False
  557. try:
  558. f.write(ref+"\n")
  559. except (OSError, IOError):
  560. f.abort()
  561. raise
  562. finally:
  563. f.close()
  564. return True
  565. def remove_if_equals(self, name, old_ref):
  566. """Remove a refname only if it currently equals old_ref.
  567. This method does not follow symbolic references. It can be used to
  568. perform an atomic compare-and-delete operation.
  569. :param name: The refname to delete.
  570. :param old_ref: The old sha the refname must refer to, or None to delete
  571. unconditionally.
  572. :return: True if the delete was successful, False otherwise.
  573. """
  574. self._check_refname(name)
  575. filename = self.refpath(name)
  576. ensure_dir_exists(os.path.dirname(filename))
  577. f = GitFile(filename, 'wb')
  578. try:
  579. if old_ref is not None:
  580. orig_ref = self.read_loose_ref(name)
  581. if orig_ref is None:
  582. orig_ref = self.get_packed_refs().get(name, None)
  583. if orig_ref != old_ref:
  584. return False
  585. # may only be packed
  586. try:
  587. os.remove(filename)
  588. except OSError, e:
  589. if e.errno != errno.ENOENT:
  590. raise
  591. self._remove_packed_ref(name)
  592. finally:
  593. # never write, we just wanted the lock
  594. f.abort()
  595. return True
  596. def _split_ref_line(line):
  597. """Split a single ref line into a tuple of SHA1 and name."""
  598. fields = line.rstrip("\n").split(" ")
  599. if len(fields) != 2:
  600. raise PackedRefsException("invalid ref line '%s'" % line)
  601. sha, name = fields
  602. try:
  603. hex_to_sha(sha)
  604. except (AssertionError, TypeError), e:
  605. raise PackedRefsException(e)
  606. if not check_ref_format(name):
  607. raise PackedRefsException("invalid ref name '%s'" % name)
  608. return (sha, name)
  609. def read_packed_refs(f):
  610. """Read a packed refs file.
  611. :param f: file-like object to read from
  612. :return: Iterator over tuples with SHA1s and ref names.
  613. """
  614. for l in f:
  615. if l[0] == "#":
  616. # Comment
  617. continue
  618. if l[0] == "^":
  619. raise PackedRefsException(
  620. "found peeled ref in packed-refs without peeled")
  621. yield _split_ref_line(l)
  622. def read_packed_refs_with_peeled(f):
  623. """Read a packed refs file including peeled refs.
  624. Assumes the "# pack-refs with: peeled" line was already read. Yields tuples
  625. with ref names, SHA1s, and peeled SHA1s (or None).
  626. :param f: file-like object to read from, seek'ed to the second line
  627. """
  628. last = None
  629. for l in f:
  630. if l[0] == "#":
  631. continue
  632. l = l.rstrip("\r\n")
  633. if l[0] == "^":
  634. if not last:
  635. raise PackedRefsException("unexpected peeled ref line")
  636. try:
  637. hex_to_sha(l[1:])
  638. except (AssertionError, TypeError), e:
  639. raise PackedRefsException(e)
  640. sha, name = _split_ref_line(last)
  641. last = None
  642. yield (sha, name, l[1:])
  643. else:
  644. if last:
  645. sha, name = _split_ref_line(last)
  646. yield (sha, name, None)
  647. last = l
  648. if last:
  649. sha, name = _split_ref_line(last)
  650. yield (sha, name, None)
  651. def write_packed_refs(f, packed_refs, peeled_refs=None):
  652. """Write a packed refs file.
  653. :param f: empty file-like object to write to
  654. :param packed_refs: dict of refname to sha of packed refs to write
  655. :param peeled_refs: dict of refname to peeled value of sha
  656. """
  657. if peeled_refs is None:
  658. peeled_refs = {}
  659. else:
  660. f.write('# pack-refs with: peeled\n')
  661. for refname in sorted(packed_refs.iterkeys()):
  662. f.write('%s %s\n' % (packed_refs[refname], refname))
  663. if refname in peeled_refs:
  664. f.write('^%s\n' % peeled_refs[refname])
  665. class BaseRepo(object):
  666. """Base class for a git repository.
  667. :ivar object_store: Dictionary-like object for accessing
  668. the objects
  669. :ivar refs: Dictionary-like object with the refs in this repository
  670. """
  671. def __init__(self, object_store, refs):
  672. self.object_store = object_store
  673. self.refs = refs
  674. def _init_files(self, bare):
  675. """Initialize a default set of named files."""
  676. from dulwich.config import ConfigFile
  677. self._put_named_file('description', "Unnamed repository")
  678. f = StringIO()
  679. cf = ConfigFile()
  680. cf.set("core.repositoryformatversion", "0")
  681. cf.set("core.filemode", "true")
  682. cf.set("core.bare", str(bare).lower())
  683. cf.set("core.logallrefupdates", "true")
  684. cf.write_to_file(f)
  685. self._put_named_file('config', f.getvalue())
  686. self._put_named_file(os.path.join('info', 'exclude'), '')
  687. def get_named_file(self, path):
  688. """Get a file from the control dir with a specific name.
  689. Although the filename should be interpreted as a filename relative to
  690. the control dir in a disk-based Repo, the object returned need not be
  691. pointing to a file in that location.
  692. :param path: The path to the file, relative to the control dir.
  693. :return: An open file object, or None if the file does not exist.
  694. """
  695. raise NotImplementedError(self.get_named_file)
  696. def _put_named_file(self, path, contents):
  697. """Write a file to the control dir with the given name and contents.
  698. :param path: The path to the file, relative to the control dir.
  699. :param contents: A string to write to the file.
  700. """
  701. raise NotImplementedError(self._put_named_file)
  702. def open_index(self):
  703. """Open the index for this repository.
  704. :raises NoIndexPresent: If no index is present
  705. :return: Index instance
  706. """
  707. raise NotImplementedError(self.open_index)
  708. def fetch(self, target, determine_wants=None, progress=None):
  709. """Fetch objects into another repository.
  710. :param target: The target repository
  711. :param determine_wants: Optional function to determine what refs to
  712. fetch.
  713. :param progress: Optional progress function
  714. """
  715. if determine_wants is None:
  716. determine_wants = lambda heads: heads.values()
  717. target.object_store.add_objects(
  718. self.fetch_objects(determine_wants, target.get_graph_walker(),
  719. progress))
  720. return self.get_refs()
  721. def fetch_objects(self, determine_wants, graph_walker, progress,
  722. get_tagged=None):
  723. """Fetch the missing objects required for a set of revisions.
  724. :param determine_wants: Function that takes a dictionary with heads
  725. and returns the list of heads to fetch.
  726. :param graph_walker: Object that can iterate over the list of revisions
  727. to fetch and has an "ack" method that will be called to acknowledge
  728. that a revision is present.
  729. :param progress: Simple progress function that will be called with
  730. updated progress strings.
  731. :param get_tagged: Function that returns a dict of pointed-to sha -> tag
  732. sha for including tags.
  733. :return: iterator over objects, with __len__ implemented
  734. """
  735. wants = determine_wants(self.get_refs())
  736. if wants is None:
  737. # TODO(dborowitz): find a way to short-circuit that doesn't change
  738. # this interface.
  739. return None
  740. haves = self.object_store.find_common_revisions(graph_walker)
  741. return self.object_store.iter_shas(
  742. self.object_store.find_missing_objects(haves, wants, progress,
  743. get_tagged))
  744. def get_graph_walker(self, heads=None):
  745. if heads is None:
  746. heads = self.refs.as_dict('refs/heads').values()
  747. return self.object_store.get_graph_walker(heads)
  748. def ref(self, name):
  749. """Return the SHA1 a ref is pointing to."""
  750. return self.refs[name]
  751. def get_refs(self):
  752. """Get dictionary with all refs."""
  753. return self.refs.as_dict()
  754. def head(self):
  755. """Return the SHA1 pointed at by HEAD."""
  756. return self.refs['HEAD']
  757. def _get_object(self, sha, cls):
  758. assert len(sha) in (20, 40)
  759. ret = self.get_object(sha)
  760. if not isinstance(ret, cls):
  761. if cls is Commit:
  762. raise NotCommitError(ret)
  763. elif cls is Blob:
  764. raise NotBlobError(ret)
  765. elif cls is Tree:
  766. raise NotTreeError(ret)
  767. elif cls is Tag:
  768. raise NotTagError(ret)
  769. else:
  770. raise Exception("Type invalid: %r != %r" % (
  771. ret.type_name, cls.type_name))
  772. return ret
  773. def get_object(self, sha):
  774. return self.object_store[sha]
  775. def get_parents(self, sha):
  776. return self.commit(sha).parents
  777. def get_config(self):
  778. from dulwich.config import ConfigFile
  779. try:
  780. p = ConfigFile.from_path(os.path.join(self._controldir, 'config'))
  781. except (IOError, OSError), e:
  782. if e.errno == errno.ENOENT:
  783. return ConfigFile()
  784. raise
  785. return dict((section, dict(p.items(section)))
  786. for section in p.sections())
  787. def commit(self, sha):
  788. """Retrieve the commit with a particular SHA.
  789. :param sha: SHA of the commit to retrieve
  790. :raise NotCommitError: If the SHA provided doesn't point at a Commit
  791. :raise KeyError: If the SHA provided didn't exist
  792. :return: A `Commit` object
  793. """
  794. warnings.warn("Repo.commit(sha) is deprecated. Use Repo[sha] instead.",
  795. category=DeprecationWarning, stacklevel=2)
  796. return self._get_object(sha, Commit)
  797. def tree(self, sha):
  798. """Retrieve the tree with a particular SHA.
  799. :param sha: SHA of the tree to retrieve
  800. :raise NotTreeError: If the SHA provided doesn't point at a Tree
  801. :raise KeyError: If the SHA provided didn't exist
  802. :return: A `Tree` object
  803. """
  804. warnings.warn("Repo.tree(sha) is deprecated. Use Repo[sha] instead.",
  805. category=DeprecationWarning, stacklevel=2)
  806. return self._get_object(sha, Tree)
  807. def tag(self, sha):
  808. """Retrieve the tag with a particular SHA.
  809. :param sha: SHA of the tag to retrieve
  810. :raise NotTagError: If the SHA provided doesn't point at a Tag
  811. :raise KeyError: If the SHA provided didn't exist
  812. :return: A `Tag` object
  813. """
  814. warnings.warn("Repo.tag(sha) is deprecated. Use Repo[sha] instead.",
  815. category=DeprecationWarning, stacklevel=2)
  816. return self._get_object(sha, Tag)
  817. def get_blob(self, sha):
  818. """Retrieve the blob with a particular SHA.
  819. :param sha: SHA of the blob to retrieve
  820. :raise NotBlobError: If the SHA provided doesn't point at a Blob
  821. :raise KeyError: If the SHA provided didn't exist
  822. :return: A `Blob` object
  823. """
  824. warnings.warn("Repo.get_blob(sha) is deprecated. Use Repo[sha] "
  825. "instead.", category=DeprecationWarning, stacklevel=2)
  826. return self._get_object(sha, Blob)
  827. def get_peeled(self, ref):
  828. """Get the peeled value of a ref.
  829. :param ref: The refname to peel.
  830. :return: The fully-peeled SHA1 of a tag object, after peeling all
  831. intermediate tags; if the original ref does not point to a tag, this
  832. will equal the original SHA1.
  833. """
  834. cached = self.refs.get_peeled(ref)
  835. if cached is not None:
  836. return cached
  837. return self.object_store.peel_sha(self.refs[ref]).id
  838. def get_walker(self, include=None, *args, **kwargs):
  839. """Obtain a walker for this repository.
  840. :param include: Iterable of SHAs of commits to include along with their
  841. ancestors. Defaults to [HEAD]
  842. :param exclude: Iterable of SHAs of commits to exclude along with their
  843. ancestors, overriding includes.
  844. :param order: ORDER_* constant specifying the order of results. Anything
  845. other than ORDER_DATE may result in O(n) memory usage.
  846. :param reverse: If True, reverse the order of output, requiring O(n)
  847. memory.
  848. :param max_entries: The maximum number of entries to yield, or None for
  849. no limit.
  850. :param paths: Iterable of file or subtree paths to show entries for.
  851. :param rename_detector: diff.RenameDetector object for detecting
  852. renames.
  853. :param follow: If True, follow path across renames/copies. Forces a
  854. default rename_detector.
  855. :param since: Timestamp to list commits after.
  856. :param until: Timestamp to list commits before.
  857. :param queue_cls: A class to use for a queue of commits, supporting the
  858. iterator protocol. The constructor takes a single argument, the
  859. Walker.
  860. """
  861. from dulwich.walk import Walker
  862. if include is None:
  863. include = [self.head()]
  864. return Walker(self.object_store, include, *args, **kwargs)
  865. def revision_history(self, head):
  866. """Returns a list of the commits reachable from head.
  867. :param head: The SHA of the head to list revision history for.
  868. :return: A list of commit objects reachable from head, starting with
  869. head itself, in descending commit time order.
  870. :raise MissingCommitError: if any missing commits are referenced,
  871. including if the head parameter isn't the SHA of a commit.
  872. """
  873. warnings.warn("Repo.revision_history() is deprecated."
  874. "Use dulwich.walker.Walker(repo) instead.",
  875. category=DeprecationWarning, stacklevel=2)
  876. return [e.commit for e in self.get_walker(include=[head])]
  877. def __getitem__(self, name):
  878. if len(name) in (20, 40):
  879. try:
  880. return self.object_store[name]
  881. except KeyError:
  882. pass
  883. try:
  884. return self.object_store[self.refs[name]]
  885. except RefFormatError:
  886. raise KeyError(name)
  887. def __iter__(self):
  888. raise NotImplementedError(self.__iter__)
  889. def __contains__(self, name):
  890. if len(name) in (20, 40):
  891. return name in self.object_store or name in self.refs
  892. else:
  893. return name in self.refs
  894. def __setitem__(self, name, value):
  895. if name.startswith("refs/") or name == "HEAD":
  896. if isinstance(value, ShaFile):
  897. self.refs[name] = value.id
  898. elif isinstance(value, str):
  899. self.refs[name] = value
  900. else:
  901. raise TypeError(value)
  902. else:
  903. raise ValueError(name)
  904. def __delitem__(self, name):
  905. if name.startswith("refs") or name == "HEAD":
  906. del self.refs[name]
  907. else:
  908. raise ValueError(name)
  909. def do_commit(self, message=None, committer=None,
  910. author=None, commit_timestamp=None,
  911. commit_timezone=None, author_timestamp=None,
  912. author_timezone=None, tree=None, encoding=None,
  913. ref='HEAD', merge_heads=None):
  914. """Create a new commit.
  915. :param message: Commit message
  916. :param committer: Committer fullname
  917. :param author: Author fullname (defaults to committer)
  918. :param commit_timestamp: Commit timestamp (defaults to now)
  919. :param commit_timezone: Commit timestamp timezone (defaults to GMT)
  920. :param author_timestamp: Author timestamp (defaults to commit timestamp)
  921. :param author_timezone: Author timestamp timezone
  922. (defaults to commit timestamp timezone)
  923. :param tree: SHA1 of the tree root to use (if not specified the
  924. current index will be committed).
  925. :param encoding: Encoding
  926. :param ref: Optional ref to commit to (defaults to current branch)
  927. :param merge_heads: Merge heads (defaults to .git/MERGE_HEADS)
  928. :return: New commit SHA1
  929. """
  930. import time
  931. c = Commit()
  932. if tree is None:
  933. index = self.open_index()
  934. c.tree = index.commit(self.object_store)
  935. else:
  936. if len(tree) != 40:
  937. raise ValueError("tree must be a 40-byte hex sha string")
  938. c.tree = tree
  939. if merge_heads is None:
  940. # FIXME: Read merge heads from .git/MERGE_HEADS
  941. merge_heads = []
  942. # TODO: Allow username to be missing, and get it from .git/config
  943. if committer is None:
  944. raise ValueError("committer not set")
  945. c.committer = committer
  946. if commit_timestamp is None:
  947. commit_timestamp = time.time()
  948. c.commit_time = int(commit_timestamp)
  949. if commit_timezone is None:
  950. # FIXME: Use current user timezone rather than UTC
  951. commit_timezone = 0
  952. c.commit_timezone = commit_timezone
  953. if author is None:
  954. author = committer
  955. c.author = author
  956. if author_timestamp is None:
  957. author_timestamp = commit_timestamp
  958. c.author_time = int(author_timestamp)
  959. if author_timezone is None:
  960. author_timezone = commit_timezone
  961. c.author_timezone = author_timezone
  962. if encoding is not None:
  963. c.encoding = encoding
  964. if message is None:
  965. # FIXME: Try to read commit message from .git/MERGE_MSG
  966. raise ValueError("No commit message specified")
  967. c.message = message
  968. try:
  969. old_head = self.refs[ref]
  970. c.parents = [old_head] + merge_heads
  971. self.object_store.add_object(c)
  972. ok = self.refs.set_if_equals(ref, old_head, c.id)
  973. except KeyError:
  974. c.parents = merge_heads
  975. self.object_store.add_object(c)
  976. ok = self.refs.add_if_new(ref, c.id)
  977. if not ok:
  978. # Fail if the atomic compare-and-swap failed, leaving the commit and
  979. # all its objects as garbage.
  980. raise CommitError("%s changed during commit" % (ref,))
  981. return c.id
  982. class Repo(BaseRepo):
  983. """A git repository backed by local disk."""
  984. def __init__(self, root):
  985. if os.path.isdir(os.path.join(root, ".git", OBJECTDIR)):
  986. self.bare = False
  987. self._controldir = os.path.join(root, ".git")
  988. elif (os.path.isdir(os.path.join(root, OBJECTDIR)) and
  989. os.path.isdir(os.path.join(root, REFSDIR))):
  990. self.bare = True
  991. self._controldir = root
  992. else:
  993. raise NotGitRepository(root)
  994. self.path = root
  995. object_store = DiskObjectStore(os.path.join(self.controldir(),
  996. OBJECTDIR))
  997. refs = DiskRefsContainer(self.controldir())
  998. BaseRepo.__init__(self, object_store, refs)
  999. def controldir(self):
  1000. """Return the path of the control directory."""
  1001. return self._controldir
  1002. def _put_named_file(self, path, contents):
  1003. """Write a file to the control dir with the given name and contents.
  1004. :param path: The path to the file, relative to the control dir.
  1005. :param contents: A string to write to the file.
  1006. """
  1007. path = path.lstrip(os.path.sep)
  1008. f = GitFile(os.path.join(self.controldir(), path), 'wb')
  1009. try:
  1010. f.write(contents)
  1011. finally:
  1012. f.close()
  1013. def get_named_file(self, path):
  1014. """Get a file from the control dir with a specific name.
  1015. Although the filename should be interpreted as a filename relative to
  1016. the control dir in a disk-based Repo, the object returned need not be
  1017. pointing to a file in that location.
  1018. :param path: The path to the file, relative to the control dir.
  1019. :return: An open file object, or None if the file does not exist.
  1020. """
  1021. # TODO(dborowitz): sanitize filenames, since this is used directly by
  1022. # the dumb web serving code.
  1023. path = path.lstrip(os.path.sep)
  1024. try:
  1025. return open(os.path.join(self.controldir(), path), 'rb')
  1026. except (IOError, OSError), e:
  1027. if e.errno == errno.ENOENT:
  1028. return None
  1029. raise
  1030. def index_path(self):
  1031. """Return path to the index file."""
  1032. return os.path.join(self.controldir(), INDEX_FILENAME)
  1033. def open_index(self):
  1034. """Open the index for this repository."""
  1035. from dulwich.index import Index
  1036. if not self.has_index():
  1037. raise NoIndexPresent()
  1038. return Index(self.index_path())
  1039. def has_index(self):
  1040. """Check if an index is present."""
  1041. # Bare repos must never have index files; non-bare repos may have a
  1042. # missing index file, which is treated as empty.
  1043. return not self.bare
  1044. def stage(self, paths):
  1045. """Stage a set of paths.
  1046. :param paths: List of paths, relative to the repository path
  1047. """
  1048. from dulwich.index import cleanup_mode
  1049. index = self.open_index()
  1050. for path in paths:
  1051. full_path = os.path.join(self.path, path)
  1052. blob = Blob()
  1053. try:
  1054. st = os.stat(full_path)
  1055. except OSError:
  1056. # File no longer exists
  1057. try:
  1058. del index[path]
  1059. except KeyError:
  1060. pass # Doesn't exist in the index either
  1061. else:
  1062. f = open(full_path, 'rb')
  1063. try:
  1064. blob.data = f.read()
  1065. finally:
  1066. f.close()
  1067. self.object_store.add_object(blob)
  1068. # XXX: Cleanup some of the other file properties as well?
  1069. index[path] = (st.st_ctime, st.st_mtime, st.st_dev, st.st_ino,
  1070. cleanup_mode(st.st_mode), st.st_uid, st.st_gid, st.st_size,
  1071. blob.id, 0)
  1072. index.write()
  1073. def clone(self, target_path, mkdir=True, bare=False, origin="origin"):
  1074. """Clone this repository.
  1075. :param target_path: Target path
  1076. :param mkdir: Create the target directory
  1077. :param bare: Whether to create a bare repository
  1078. :return: Created repository
  1079. """
  1080. if not bare:
  1081. target = self.init(target_path, mkdir=mkdir)
  1082. else:
  1083. target = self.init_bare(target_path)
  1084. self.fetch(target)
  1085. target.refs.import_refs(
  1086. 'refs/remotes/'+origin, self.refs.as_dict('refs/heads'))
  1087. target.refs.import_refs(
  1088. 'refs/tags', self.refs.as_dict('refs/tags'))
  1089. try:
  1090. target.refs.add_if_new(
  1091. 'refs/heads/master',
  1092. self.refs['refs/heads/master'])
  1093. except KeyError:
  1094. pass
  1095. return target
  1096. def __repr__(self):
  1097. return "<Repo at %r>" % self.path
  1098. @classmethod
  1099. def _init_maybe_bare(cls, path, bare):
  1100. for d in BASE_DIRECTORIES:
  1101. os.mkdir(os.path.join(path, *d))
  1102. DiskObjectStore.init(os.path.join(path, OBJECTDIR))
  1103. ret = cls(path)
  1104. ret.refs.set_symbolic_ref("HEAD", "refs/heads/master")
  1105. ret._init_files(bare)
  1106. return ret
  1107. @classmethod
  1108. def init(cls, path, mkdir=False):
  1109. if mkdir:
  1110. os.mkdir(path)
  1111. controldir = os.path.join(path, ".git")
  1112. os.mkdir(controldir)
  1113. cls._init_maybe_bare(controldir, False)
  1114. return cls(path)
  1115. @classmethod
  1116. def init_bare(cls, path):
  1117. return cls._init_maybe_bare(path, True)
  1118. create = init_bare
  1119. class MemoryRepo(BaseRepo):
  1120. """Repo that stores refs, objects, and named files in memory.
  1121. MemoryRepos are always bare: they have no working tree and no index, since
  1122. those have a stronger dependency on the filesystem.
  1123. """
  1124. def __init__(self):
  1125. BaseRepo.__init__(self, MemoryObjectStore(), DictRefsContainer({}))
  1126. self._named_files = {}
  1127. self.bare = True
  1128. def _put_named_file(self, path, contents):
  1129. """Write a file to the control dir with the given name and contents.
  1130. :param path: The path to the file, relative to the control dir.
  1131. :param contents: A string to write to the file.
  1132. """
  1133. self._named_files[path] = contents
  1134. def get_named_file(self, path):
  1135. """Get a file from the control dir with a specific name.
  1136. Although the filename should be interpreted as a filename relative to
  1137. the control dir in a disk-baked Repo, the object returned need not be
  1138. pointing to a file in that location.
  1139. :param path: The path to the file, relative to the control dir.
  1140. :return: An open file object, or None if the file does not exist.
  1141. """
  1142. contents = self._named_files.get(path, None)
  1143. if contents is None:
  1144. return None
  1145. return StringIO(contents)
  1146. def open_index(self):
  1147. """Fail to open index for this repo, since it is bare."""
  1148. raise NoIndexPresent()
  1149. @classmethod
  1150. def init_bare(cls, objects, refs):
  1151. ret = cls()
  1152. for obj in objects:
  1153. ret.object_store.add_object(obj)
  1154. for refname, sha in refs.iteritems():
  1155. ret.refs[refname] = sha
  1156. ret._init_files(bare=True)
  1157. return ret