repo.py 52 KB

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