repo.py 54 KB

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