您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

1205 行
53KB

  1. # need a dict to set bloody .name field
  2. from io import BytesIO
  3. import logging
  4. import os
  5. import stat
  6. from unittest import SkipTest
  7. import uuid
  8. import git
  9. from git.cmd import Git
  10. from git.compat import (
  11. string_types,
  12. defenc,
  13. is_win,
  14. )
  15. from git.config import (
  16. SectionConstraint,
  17. GitConfigParser,
  18. cp
  19. )
  20. from git.exc import (
  21. InvalidGitRepositoryError,
  22. NoSuchPathError,
  23. RepositoryDirtyError
  24. )
  25. from git.objects.base import IndexObject, Object
  26. from git.objects.util import Traversable
  27. from git.util import (
  28. Iterable,
  29. join_path_native,
  30. to_native_path_linux,
  31. RemoteProgress,
  32. rmtree,
  33. unbare_repo
  34. )
  35. from git.util import HIDE_WINDOWS_KNOWN_ERRORS
  36. import os.path as osp
  37. from .util import (
  38. mkhead,
  39. sm_name,
  40. sm_section,
  41. SubmoduleConfigParser,
  42. find_first_remote_branch
  43. )
  44. __all__ = ["Submodule", "UpdateProgress"]
  45. log = logging.getLogger('git.objects.submodule.base')
  46. log.addHandler(logging.NullHandler())
  47. class UpdateProgress(RemoteProgress):
  48. """Class providing detailed progress information to the caller who should
  49. derive from it and implement the ``update(...)`` message"""
  50. CLONE, FETCH, UPDWKTREE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes + 3)]
  51. _num_op_codes = RemoteProgress._num_op_codes + 3
  52. __slots__ = ()
  53. BEGIN = UpdateProgress.BEGIN
  54. END = UpdateProgress.END
  55. CLONE = UpdateProgress.CLONE
  56. FETCH = UpdateProgress.FETCH
  57. UPDWKTREE = UpdateProgress.UPDWKTREE
  58. # IndexObject comes via util module, its a 'hacky' fix thanks to pythons import
  59. # mechanism which cause plenty of trouble of the only reason for packages and
  60. # modules is refactoring - subpackages shouldn't depend on parent packages
  61. class Submodule(IndexObject, Iterable, Traversable):
  62. """Implements access to a git submodule. They are special in that their sha
  63. represents a commit in the submodule's repository which is to be checked out
  64. at the path of this instance.
  65. The submodule type does not have a string type associated with it, as it exists
  66. solely as a marker in the tree and index.
  67. All methods work in bare and non-bare repositories."""
  68. _id_attribute_ = "name"
  69. k_modules_file = '.gitmodules'
  70. k_head_option = 'branch'
  71. k_head_default = 'master'
  72. k_default_mode = stat.S_IFDIR | stat.S_IFLNK # submodules are directories with link-status
  73. # this is a bogus type for base class compatibility
  74. type = 'submodule'
  75. __slots__ = ('_parent_commit', '_url', '_branch_path', '_name', '__weakref__')
  76. _cache_attrs = ('path', '_url', '_branch_path')
  77. def __init__(self, repo, binsha, mode=None, path=None, name=None, parent_commit=None, url=None, branch_path=None):
  78. """Initialize this instance with its attributes. We only document the ones
  79. that differ from ``IndexObject``
  80. :param repo: Our parent repository
  81. :param binsha: binary sha referring to a commit in the remote repository, see url parameter
  82. :param parent_commit: see set_parent_commit()
  83. :param url: The url to the remote repository which is the submodule
  84. :param branch_path: full (relative) path to ref to checkout when cloning the remote repository"""
  85. super(Submodule, self).__init__(repo, binsha, mode, path)
  86. self.size = 0
  87. self._parent_commit = parent_commit
  88. if url is not None:
  89. self._url = url
  90. if branch_path is not None:
  91. assert isinstance(branch_path, string_types)
  92. self._branch_path = branch_path
  93. if name is not None:
  94. self._name = name
  95. def _set_cache_(self, attr):
  96. if attr in ('path', '_url', '_branch_path'):
  97. reader = self.config_reader()
  98. # default submodule values
  99. try:
  100. self.path = reader.get('path')
  101. except cp.NoSectionError:
  102. raise ValueError("This submodule instance does not exist anymore in '%s' file"
  103. % osp.join(self.repo.working_tree_dir, '.gitmodules'))
  104. # end
  105. self._url = reader.get('url')
  106. # git-python extension values - optional
  107. self._branch_path = reader.get_value(self.k_head_option, git.Head.to_full_path(self.k_head_default))
  108. elif attr == '_name':
  109. raise AttributeError("Cannot retrieve the name of a submodule if it was not set initially")
  110. else:
  111. super(Submodule, self)._set_cache_(attr)
  112. # END handle attribute name
  113. def _get_intermediate_items(self, item):
  114. """:return: all the submodules of our module repository"""
  115. try:
  116. return type(self).list_items(item.module())
  117. except InvalidGitRepositoryError:
  118. return []
  119. # END handle intermediate items
  120. @classmethod
  121. def _need_gitfile_submodules(cls, git):
  122. return git.version_info[:3] >= (1, 7, 5)
  123. def __eq__(self, other):
  124. """Compare with another submodule"""
  125. # we may only compare by name as this should be the ID they are hashed with
  126. # Otherwise this type wouldn't be hashable
  127. # return self.path == other.path and self.url == other.url and super(Submodule, self).__eq__(other)
  128. return self._name == other._name
  129. def __ne__(self, other):
  130. """Compare with another submodule for inequality"""
  131. return not (self == other)
  132. def __hash__(self):
  133. """Hash this instance using its logical id, not the sha"""
  134. return hash(self._name)
  135. def __str__(self):
  136. return self._name
  137. def __repr__(self):
  138. return "git.%s(name=%s, path=%s, url=%s, branch_path=%s)"\
  139. % (type(self).__name__, self._name, self.path, self.url, self.branch_path)
  140. @classmethod
  141. def _config_parser(cls, repo, parent_commit, read_only):
  142. """:return: Config Parser constrained to our submodule in read or write mode
  143. :raise IOError: If the .gitmodules file cannot be found, either locally or in the repository
  144. at the given parent commit. Otherwise the exception would be delayed until the first
  145. access of the config parser"""
  146. parent_matches_head = True
  147. if parent_commit is not None:
  148. try:
  149. parent_matches_head = repo.head.commit == parent_commit
  150. except ValueError:
  151. # We are most likely in an empty repository, so the HEAD doesn't point to a valid ref
  152. pass
  153. # end handle parent_commit
  154. if not repo.bare and parent_matches_head:
  155. fp_module = osp.join(repo.working_tree_dir, cls.k_modules_file)
  156. else:
  157. assert parent_commit is not None, "need valid parent_commit in bare repositories"
  158. try:
  159. fp_module = cls._sio_modules(parent_commit)
  160. except KeyError:
  161. raise IOError("Could not find %s file in the tree of parent commit %s" %
  162. (cls.k_modules_file, parent_commit))
  163. # END handle exceptions
  164. # END handle non-bare working tree
  165. if not read_only and (repo.bare or not parent_matches_head):
  166. raise ValueError("Cannot write blobs of 'historical' submodule configurations")
  167. # END handle writes of historical submodules
  168. return SubmoduleConfigParser(fp_module, read_only=read_only)
  169. def _clear_cache(self):
  170. # clear the possibly changed values
  171. for name in self._cache_attrs:
  172. try:
  173. delattr(self, name)
  174. except AttributeError:
  175. pass
  176. # END try attr deletion
  177. # END for each name to delete
  178. @classmethod
  179. def _sio_modules(cls, parent_commit):
  180. """:return: Configuration file as BytesIO - we only access it through the respective blob's data"""
  181. sio = BytesIO(parent_commit.tree[cls.k_modules_file].data_stream.read())
  182. sio.name = cls.k_modules_file
  183. return sio
  184. def _config_parser_constrained(self, read_only):
  185. """:return: Config Parser constrained to our submodule in read or write mode"""
  186. try:
  187. pc = self.parent_commit
  188. except ValueError:
  189. pc = None
  190. # end handle empty parent repository
  191. parser = self._config_parser(self.repo, pc, read_only)
  192. parser.set_submodule(self)
  193. return SectionConstraint(parser, sm_section(self.name))
  194. @classmethod
  195. def _module_abspath(cls, parent_repo, path, name):
  196. if cls._need_gitfile_submodules(parent_repo.git):
  197. return osp.join(parent_repo.git_dir, 'modules', name)
  198. else:
  199. return osp.join(parent_repo.working_tree_dir, path)
  200. # end
  201. @classmethod
  202. def _clone_repo(cls, repo, url, path, name, **kwargs):
  203. """:return: Repo instance of newly cloned repository
  204. :param repo: our parent repository
  205. :param url: url to clone from
  206. :param path: repository-relative path to the submodule checkout location
  207. :param name: canonical of the submodule
  208. :param kwrags: additinoal arguments given to git.clone"""
  209. module_abspath = cls._module_abspath(repo, path, name)
  210. module_checkout_path = module_abspath
  211. if cls._need_gitfile_submodules(repo.git):
  212. kwargs['separate_git_dir'] = module_abspath
  213. module_abspath_dir = osp.dirname(module_abspath)
  214. if not osp.isdir(module_abspath_dir):
  215. os.makedirs(module_abspath_dir)
  216. module_checkout_path = osp.join(repo.working_tree_dir, path)
  217. # end
  218. clone = git.Repo.clone_from(url, module_checkout_path, **kwargs)
  219. if cls._need_gitfile_submodules(repo.git):
  220. cls._write_git_file_and_module_config(module_checkout_path, module_abspath)
  221. # end
  222. return clone
  223. @classmethod
  224. def _to_relative_path(cls, parent_repo, path):
  225. """:return: a path guaranteed to be relative to the given parent-repository
  226. :raise ValueError: if path is not contained in the parent repository's working tree"""
  227. path = to_native_path_linux(path)
  228. if path.endswith('/'):
  229. path = path[:-1]
  230. # END handle trailing slash
  231. if osp.isabs(path):
  232. working_tree_linux = to_native_path_linux(parent_repo.working_tree_dir)
  233. if not path.startswith(working_tree_linux):
  234. raise ValueError("Submodule checkout path '%s' needs to be within the parents repository at '%s'"
  235. % (working_tree_linux, path))
  236. path = path[len(working_tree_linux.rstrip('/')) + 1:]
  237. if not path:
  238. raise ValueError("Absolute submodule path '%s' didn't yield a valid relative path" % path)
  239. # end verify converted relative path makes sense
  240. # end convert to a relative path
  241. return path
  242. @classmethod
  243. def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath):
  244. """Writes a .git file containing a (preferably) relative path to the actual git module repository.
  245. It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir
  246. :note: will overwrite existing files !
  247. :note: as we rewrite both the git file as well as the module configuration, we might fail on the configuration
  248. and will not roll back changes done to the git file. This should be a non-issue, but may easily be fixed
  249. if it becomes one
  250. :param working_tree_dir: directory to write the .git file into
  251. :param module_abspath: absolute path to the bare repository
  252. """
  253. git_file = osp.join(working_tree_dir, '.git')
  254. rela_path = osp.relpath(module_abspath, start=working_tree_dir)
  255. if is_win:
  256. if osp.isfile(git_file):
  257. os.remove(git_file)
  258. with open(git_file, 'wb') as fp:
  259. fp.write(("gitdir: %s" % rela_path).encode(defenc))
  260. with GitConfigParser(osp.join(module_abspath, 'config'),
  261. read_only=False, merge_includes=False) as writer:
  262. writer.set_value('core', 'worktree',
  263. to_native_path_linux(osp.relpath(working_tree_dir, start=module_abspath)))
  264. #{ Edit Interface
  265. @classmethod
  266. def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
  267. """Add a new submodule to the given repository. This will alter the index
  268. as well as the .gitmodules file, but will not create a new commit.
  269. If the submodule already exists, no matter if the configuration differs
  270. from the one provided, the existing submodule will be returned.
  271. :param repo: Repository instance which should receive the submodule
  272. :param name: The name/identifier for the submodule
  273. :param path: repository-relative or absolute path at which the submodule
  274. should be located
  275. It will be created as required during the repository initialization.
  276. :param url: git-clone compatible URL, see git-clone reference for more information
  277. If None, the repository is assumed to exist, and the url of the first
  278. remote is taken instead. This is useful if you want to make an existing
  279. repository a submodule of anotherone.
  280. :param branch: name of branch at which the submodule should (later) be checked out.
  281. The given branch must exist in the remote repository, and will be checked
  282. out locally as a tracking branch.
  283. It will only be written into the configuration if it not None, which is
  284. when the checked out branch will be the one the remote HEAD pointed to.
  285. The result you get in these situation is somewhat fuzzy, and it is recommended
  286. to specify at least 'master' here.
  287. Examples are 'master' or 'feature/new'
  288. :param no_checkout: if True, and if the repository has to be cloned manually,
  289. no checkout will be performed
  290. :return: The newly created submodule instance
  291. :note: works atomically, such that no change will be done if the repository
  292. update fails for instance"""
  293. if repo.bare:
  294. raise InvalidGitRepositoryError("Cannot add submodules to bare repositories")
  295. # END handle bare repos
  296. path = cls._to_relative_path(repo, path)
  297. # assure we never put backslashes into the url, as some operating systems
  298. # like it ...
  299. if url is not None:
  300. url = to_native_path_linux(url)
  301. # END assure url correctness
  302. # INSTANTIATE INTERMEDIATE SM
  303. sm = cls(repo, cls.NULL_BIN_SHA, cls.k_default_mode, path, name, url='invalid-temporary')
  304. if sm.exists():
  305. # reretrieve submodule from tree
  306. try:
  307. sm = repo.head.commit.tree[path]
  308. sm._name = name
  309. return sm
  310. except KeyError:
  311. # could only be in index
  312. index = repo.index
  313. entry = index.entries[index.entry_key(path, 0)]
  314. sm.binsha = entry.binsha
  315. return sm
  316. # END handle exceptions
  317. # END handle existing
  318. # fake-repo - we only need the functionality on the branch instance
  319. br = git.Head(repo, git.Head.to_full_path(str(branch) or cls.k_head_default))
  320. has_module = sm.module_exists()
  321. branch_is_default = branch is None
  322. if has_module and url is not None:
  323. if url not in [r.url for r in sm.module().remotes]:
  324. raise ValueError(
  325. "Specified URL '%s' does not match any remote url of the repository at '%s'" % (url, sm.abspath))
  326. # END check url
  327. # END verify urls match
  328. mrepo = None
  329. if url is None:
  330. if not has_module:
  331. raise ValueError("A URL was not given and existing repository did not exsit at %s" % path)
  332. # END check url
  333. mrepo = sm.module()
  334. urls = [r.url for r in mrepo.remotes]
  335. if not urls:
  336. raise ValueError("Didn't find any remote url in repository at %s" % sm.abspath)
  337. # END verify we have url
  338. url = urls[0]
  339. else:
  340. # clone new repo
  341. kwargs = {'n': no_checkout}
  342. if not branch_is_default:
  343. kwargs['b'] = br.name
  344. # END setup checkout-branch
  345. # _clone_repo(cls, repo, url, path, name, **kwargs):
  346. mrepo = cls._clone_repo(repo, url, path, name, **kwargs)
  347. # END verify url
  348. ## See #525 for ensuring git urls in config-files valid under Windows.
  349. url = Git.polish_url(url)
  350. # It's important to add the URL to the parent config, to let `git submodule` know.
  351. # otherwise there is a '-' character in front of the submodule listing
  352. # a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8)
  353. # -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one
  354. with sm.repo.config_writer() as writer:
  355. writer.set_value(sm_section(name), 'url', url)
  356. # update configuration and index
  357. index = sm.repo.index
  358. with sm.config_writer(index=index, write=False) as writer:
  359. writer.set_value('url', url)
  360. writer.set_value('path', path)
  361. sm._url = url
  362. if not branch_is_default:
  363. # store full path
  364. writer.set_value(cls.k_head_option, br.path)
  365. sm._branch_path = br.path
  366. # we deliberately assume that our head matches our index !
  367. sm.binsha = mrepo.head.commit.binsha
  368. index.add([sm], write=True)
  369. return sm
  370. def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False,
  371. force=False, keep_going=False):
  372. """Update the repository of this submodule to point to the checkout
  373. we point at with the binsha of this instance.
  374. :param recursive: if True, we will operate recursively and update child-
  375. modules as well.
  376. :param init: if True, the module repository will be cloned into place if necessary
  377. :param to_latest_revision: if True, the submodule's sha will be ignored during checkout.
  378. Instead, the remote will be fetched, and the local tracking branch updated.
  379. This only works if we have a local tracking branch, which is the case
  380. if the remote repository had a master branch, or of the 'branch' option
  381. was specified for this submodule and the branch existed remotely
  382. :param progress: UpdateProgress instance or None if no progress should be shown
  383. :param dry_run: if True, the operation will only be simulated, but not performed.
  384. All performed operations are read-only
  385. :param force:
  386. If True, we may reset heads even if the repository in question is dirty. Additinoally we will be allowed
  387. to set a tracking branch which is ahead of its remote branch back into the past or the location of the
  388. remote branch. This will essentially 'forget' commits.
  389. If False, local tracking branches that are in the future of their respective remote branches will simply
  390. not be moved.
  391. :param keep_going: if True, we will ignore but log all errors, and keep going recursively.
  392. Unless dry_run is set as well, keep_going could cause subsequent/inherited errors you wouldn't see
  393. otherwise.
  394. In conjunction with dry_run, it can be useful to anticipate all errors when updating submodules
  395. :note: does nothing in bare repositories
  396. :note: method is definitely not atomic if recurisve is True
  397. :return: self"""
  398. if self.repo.bare:
  399. return self
  400. # END pass in bare mode
  401. if progress is None:
  402. progress = UpdateProgress()
  403. # END handle progress
  404. prefix = ''
  405. if dry_run:
  406. prefix = "DRY-RUN: "
  407. # END handle prefix
  408. # to keep things plausible in dry-run mode
  409. if dry_run:
  410. mrepo = None
  411. # END init mrepo
  412. try:
  413. # ASSURE REPO IS PRESENT AND UPTODATE
  414. #####################################
  415. try:
  416. mrepo = self.module()
  417. rmts = mrepo.remotes
  418. len_rmts = len(rmts)
  419. for i, remote in enumerate(rmts):
  420. op = FETCH
  421. if i == 0:
  422. op |= BEGIN
  423. # END handle start
  424. progress.update(op, i, len_rmts, prefix + "Fetching remote %s of submodule %r"
  425. % (remote, self.name))
  426. #===============================
  427. if not dry_run:
  428. remote.fetch(progress=progress)
  429. # END handle dry-run
  430. #===============================
  431. if i == len_rmts - 1:
  432. op |= END
  433. # END handle end
  434. progress.update(op, i, len_rmts, prefix + "Done fetching remote of submodule %r" % self.name)
  435. # END fetch new data
  436. except InvalidGitRepositoryError:
  437. if not init:
  438. return self
  439. # END early abort if init is not allowed
  440. # there is no git-repository yet - but delete empty paths
  441. checkout_module_abspath = self.abspath
  442. if not dry_run and osp.isdir(checkout_module_abspath):
  443. try:
  444. os.rmdir(checkout_module_abspath)
  445. except OSError:
  446. raise OSError("Module directory at %r does already exist and is non-empty"
  447. % checkout_module_abspath)
  448. # END handle OSError
  449. # END handle directory removal
  450. # don't check it out at first - nonetheless it will create a local
  451. # branch according to the remote-HEAD if possible
  452. progress.update(BEGIN | CLONE, 0, 1, prefix + "Cloning url '%s' to '%s' in submodule %r" %
  453. (self.url, checkout_module_abspath, self.name))
  454. if not dry_run:
  455. mrepo = self._clone_repo(self.repo, self.url, self.path, self.name, n=True)
  456. # END handle dry-run
  457. progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath)
  458. if not dry_run:
  459. # see whether we have a valid branch to checkout
  460. try:
  461. # find a remote which has our branch - we try to be flexible
  462. remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name)
  463. local_branch = mkhead(mrepo, self.branch_path)
  464. # have a valid branch, but no checkout - make sure we can figure
  465. # that out by marking the commit with a null_sha
  466. local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA))
  467. # END initial checkout + branch creation
  468. # make sure HEAD is not detached
  469. mrepo.head.set_reference(local_branch, logmsg="submodule: attaching head to %s" % local_branch)
  470. mrepo.head.ref.set_tracking_branch(remote_branch)
  471. except (IndexError, InvalidGitRepositoryError):
  472. log.warn("Failed to checkout tracking branch %s", self.branch_path)
  473. # END handle tracking branch
  474. # NOTE: Have to write the repo config file as well, otherwise
  475. # the default implementation will be offended and not update the repository
  476. # Maybe this is a good way to assure it doesn't get into our way, but
  477. # we want to stay backwards compatible too ... . Its so redundant !
  478. with self.repo.config_writer() as writer:
  479. writer.set_value(sm_section(self.name), 'url', self.url)
  480. # END handle dry_run
  481. # END handle initialization
  482. # DETERMINE SHAS TO CHECKOUT
  483. ############################
  484. binsha = self.binsha
  485. hexsha = self.hexsha
  486. if mrepo is not None:
  487. # mrepo is only set if we are not in dry-run mode or if the module existed
  488. is_detached = mrepo.head.is_detached
  489. # END handle dry_run
  490. if mrepo is not None and to_latest_revision:
  491. msg_base = "Cannot update to latest revision in repository at %r as " % mrepo.working_dir
  492. if not is_detached:
  493. rref = mrepo.head.ref.tracking_branch()
  494. if rref is not None:
  495. rcommit = rref.commit
  496. binsha = rcommit.binsha
  497. hexsha = rcommit.hexsha
  498. else:
  499. log.error("%s a tracking branch was not set for local branch '%s'", msg_base, mrepo.head.ref)
  500. # END handle remote ref
  501. else:
  502. log.error("%s there was no local tracking branch", msg_base)
  503. # END handle detached head
  504. # END handle to_latest_revision option
  505. # update the working tree
  506. # handles dry_run
  507. if mrepo is not None and mrepo.head.commit.binsha != binsha:
  508. # We must assure that our destination sha (the one to point to) is in the future of our current head.
  509. # Otherwise, we will reset changes that might have been done on the submodule, but were not yet pushed
  510. # We also handle the case that history has been rewritten, leaving no merge-base. In that case
  511. # we behave conservatively, protecting possible changes the user had done
  512. may_reset = True
  513. if mrepo.head.commit.binsha != self.NULL_BIN_SHA:
  514. base_commit = mrepo.merge_base(mrepo.head.commit, hexsha)
  515. if len(base_commit) == 0 or base_commit[0].hexsha == hexsha:
  516. if force:
  517. msg = "Will force checkout or reset on local branch that is possibly in the future of"
  518. msg += "the commit it will be checked out to, effectively 'forgetting' new commits"
  519. log.debug(msg)
  520. else:
  521. msg = "Skipping %s on branch '%s' of submodule repo '%s' as it contains un-pushed commits"
  522. msg %= (is_detached and "checkout" or "reset", mrepo.head, mrepo)
  523. log.info(msg)
  524. may_reset = False
  525. # end handle force
  526. # end handle if we are in the future
  527. if may_reset and not force and mrepo.is_dirty(index=True, working_tree=True, untracked_files=True):
  528. raise RepositoryDirtyError(mrepo, "Cannot reset a dirty repository")
  529. # end handle force and dirty state
  530. # end handle empty repo
  531. # end verify future/past
  532. progress.update(BEGIN | UPDWKTREE, 0, 1, prefix +
  533. "Updating working tree at %s for submodule %r to revision %s"
  534. % (self.path, self.name, hexsha))
  535. if not dry_run and may_reset:
  536. if is_detached:
  537. # NOTE: for now we force, the user is no supposed to change detached
  538. # submodules anyway. Maybe at some point this becomes an option, to
  539. # properly handle user modifications - see below for future options
  540. # regarding rebase and merge.
  541. mrepo.git.checkout(hexsha, force=force)
  542. else:
  543. mrepo.head.reset(hexsha, index=True, working_tree=True)
  544. # END handle checkout
  545. # if we may reset/checkout
  546. progress.update(END | UPDWKTREE, 0, 1, prefix + "Done updating working tree for submodule %r"
  547. % self.name)
  548. # END update to new commit only if needed
  549. except Exception as err:
  550. if not keep_going:
  551. raise
  552. log.error(str(err))
  553. # end handle keep_going
  554. # HANDLE RECURSION
  555. ##################
  556. if recursive:
  557. # in dry_run mode, the module might not exist
  558. if mrepo is not None:
  559. for submodule in self.iter_items(self.module()):
  560. submodule.update(recursive, init, to_latest_revision, progress=progress, dry_run=dry_run,
  561. force=force, keep_going=keep_going)
  562. # END handle recursive update
  563. # END handle dry run
  564. # END for each submodule
  565. return self
  566. @unbare_repo
  567. def move(self, module_path, configuration=True, module=True):
  568. """Move the submodule to a another module path. This involves physically moving
  569. the repository at our current path, changing the configuration, as well as
  570. adjusting our index entry accordingly.
  571. :param module_path: the path to which to move our module in the parent repostory's working tree,
  572. given as repository-relative or absolute path. Intermediate directories will be created
  573. accordingly. If the path already exists, it must be empty.
  574. Trailing (back)slashes are removed automatically
  575. :param configuration: if True, the configuration will be adjusted to let
  576. the submodule point to the given path.
  577. :param module: if True, the repository managed by this submodule
  578. will be moved as well. If False, we don't move the submodule's checkout, which may leave
  579. the parent repository in an inconsistent state.
  580. :return: self
  581. :raise ValueError: if the module path existed and was not empty, or was a file
  582. :note: Currently the method is not atomic, and it could leave the repository
  583. in an inconsistent state if a sub-step fails for some reason
  584. """
  585. if module + configuration < 1:
  586. raise ValueError("You must specify to move at least the module or the configuration of the submodule")
  587. # END handle input
  588. module_checkout_path = self._to_relative_path(self.repo, module_path)
  589. # VERIFY DESTINATION
  590. if module_checkout_path == self.path:
  591. return self
  592. # END handle no change
  593. module_checkout_abspath = join_path_native(self.repo.working_tree_dir, module_checkout_path)
  594. if osp.isfile(module_checkout_abspath):
  595. raise ValueError("Cannot move repository onto a file: %s" % module_checkout_abspath)
  596. # END handle target files
  597. index = self.repo.index
  598. tekey = index.entry_key(module_checkout_path, 0)
  599. # if the target item already exists, fail
  600. if configuration and tekey in index.entries:
  601. raise ValueError("Index entry for target path did already exist")
  602. # END handle index key already there
  603. # remove existing destination
  604. if module:
  605. if osp.exists(module_checkout_abspath):
  606. if len(os.listdir(module_checkout_abspath)):
  607. raise ValueError("Destination module directory was not empty")
  608. # END handle non-emptiness
  609. if osp.islink(module_checkout_abspath):
  610. os.remove(module_checkout_abspath)
  611. else:
  612. os.rmdir(module_checkout_abspath)
  613. # END handle link
  614. else:
  615. # recreate parent directories
  616. # NOTE: renames() does that now
  617. pass
  618. # END handle existence
  619. # END handle module
  620. # move the module into place if possible
  621. cur_path = self.abspath
  622. renamed_module = False
  623. if module and osp.exists(cur_path):
  624. os.renames(cur_path, module_checkout_abspath)
  625. renamed_module = True
  626. if osp.isfile(osp.join(module_checkout_abspath, '.git')):
  627. module_abspath = self._module_abspath(self.repo, self.path, self.name)
  628. self._write_git_file_and_module_config(module_checkout_abspath, module_abspath)
  629. # end handle git file rewrite
  630. # END move physical module
  631. # rename the index entry - have to manipulate the index directly as
  632. # git-mv cannot be used on submodules ... yeah
  633. previous_sm_path = self.path
  634. try:
  635. if configuration:
  636. try:
  637. ekey = index.entry_key(self.path, 0)
  638. entry = index.entries[ekey]
  639. del(index.entries[ekey])
  640. nentry = git.IndexEntry(entry[:3] + (module_checkout_path,) + entry[4:])
  641. index.entries[tekey] = nentry
  642. except KeyError:
  643. raise InvalidGitRepositoryError("Submodule's entry at %r did not exist" % (self.path))
  644. # END handle submodule doesn't exist
  645. # update configuration
  646. with self.config_writer(index=index) as writer: # auto-write
  647. writer.set_value('path', module_checkout_path)
  648. self.path = module_checkout_path
  649. # END handle configuration flag
  650. except Exception:
  651. if renamed_module:
  652. os.renames(module_checkout_abspath, cur_path)
  653. # END undo module renaming
  654. raise
  655. # END handle undo rename
  656. # Auto-rename submodule if it's name was 'default', that is, the checkout directory
  657. if previous_sm_path == self.name:
  658. self.rename(module_checkout_path)
  659. # end
  660. return self
  661. @unbare_repo
  662. def remove(self, module=True, force=False, configuration=True, dry_run=False):
  663. """Remove this submodule from the repository. This will remove our entry
  664. from the .gitmodules file and the entry in the .git/config file.
  665. :param module: If True, the module checkout we point to will be deleted
  666. as well. If the module is currently on a commit which is not part
  667. of any branch in the remote, if the currently checked out branch
  668. working tree, or untracked files,
  669. is ahead of its tracking branch, if you have modifications in the
  670. In case the removal of the repository fails for these reasons, the
  671. submodule status will not have been altered.
  672. If this submodule has child-modules on its own, these will be deleted
  673. prior to touching the own module.
  674. :param force: Enforces the deletion of the module even though it contains
  675. modifications. This basically enforces a brute-force file system based
  676. deletion.
  677. :param configuration: if True, the submodule is deleted from the configuration,
  678. otherwise it isn't. Although this should be enabled most of the times,
  679. this flag enables you to safely delete the repository of your submodule.
  680. :param dry_run: if True, we will not actually do anything, but throw the errors
  681. we would usually throw
  682. :return: self
  683. :note: doesn't work in bare repositories
  684. :note: doesn't work atomically, as failure to remove any part of the submodule will leave
  685. an inconsistent state
  686. :raise InvalidGitRepositoryError: thrown if the repository cannot be deleted
  687. :raise OSError: if directories or files could not be removed"""
  688. if not (module or configuration):
  689. raise ValueError("Need to specify to delete at least the module, or the configuration")
  690. # END handle parameters
  691. # Recursively remove children of this submodule
  692. nc = 0
  693. for csm in self.children():
  694. nc += 1
  695. csm.remove(module, force, configuration, dry_run)
  696. del(csm)
  697. # end
  698. if configuration and not dry_run and nc > 0:
  699. # Assure we don't leave the parent repository in a dirty state, and commit our changes
  700. # It's important for recursive, unforced, deletions to work as expected
  701. self.module().index.commit("Removed at least one of child-modules of '%s'" % self.name)
  702. # end handle recursion
  703. # DELETE REPOSITORY WORKING TREE
  704. ################################
  705. if module and self.module_exists():
  706. mod = self.module()
  707. git_dir = mod.git_dir
  708. if force:
  709. # take the fast lane and just delete everything in our module path
  710. # TODO: If we run into permission problems, we have a highly inconsistent
  711. # state. Delete the .git folders last, start with the submodules first
  712. mp = self.abspath
  713. method = None
  714. if osp.islink(mp):
  715. method = os.remove
  716. elif osp.isdir(mp):
  717. method = rmtree
  718. elif osp.exists(mp):
  719. raise AssertionError("Cannot forcibly delete repository as it was neither a link, nor a directory")
  720. # END handle brutal deletion
  721. if not dry_run:
  722. assert method
  723. method(mp)
  724. # END apply deletion method
  725. else:
  726. # verify we may delete our module
  727. if mod.is_dirty(index=True, working_tree=True, untracked_files=True):
  728. raise InvalidGitRepositoryError(
  729. "Cannot delete module at %s with any modifications, unless force is specified"
  730. % mod.working_tree_dir)
  731. # END check for dirt
  732. # figure out whether we have new commits compared to the remotes
  733. # NOTE: If the user pulled all the time, the remote heads might
  734. # not have been updated, so commits coming from the remote look
  735. # as if they come from us. But we stay strictly read-only and
  736. # don't fetch beforehand.
  737. for remote in mod.remotes:
  738. num_branches_with_new_commits = 0
  739. rrefs = remote.refs
  740. for rref in rrefs:
  741. num_branches_with_new_commits += len(mod.git.cherry(rref)) != 0
  742. # END for each remote ref
  743. # not a single remote branch contained all our commits
  744. if len(rrefs) and num_branches_with_new_commits == len(rrefs):
  745. raise InvalidGitRepositoryError(
  746. "Cannot delete module at %s as there are new commits" % mod.working_tree_dir)
  747. # END handle new commits
  748. # have to manually delete references as python's scoping is
  749. # not existing, they could keep handles open ( on windows this is a problem )
  750. if len(rrefs):
  751. del(rref)
  752. # END handle remotes
  753. del(rrefs)
  754. del(remote)
  755. # END for each remote
  756. # finally delete our own submodule
  757. if not dry_run:
  758. self._clear_cache()
  759. wtd = mod.working_tree_dir
  760. del(mod) # release file-handles (windows)
  761. import gc
  762. gc.collect()
  763. try:
  764. rmtree(wtd)
  765. except Exception as ex:
  766. if HIDE_WINDOWS_KNOWN_ERRORS:
  767. raise SkipTest("FIXME: fails with: PermissionError\n %s", ex)
  768. else:
  769. raise
  770. # END delete tree if possible
  771. # END handle force
  772. if not dry_run and osp.isdir(git_dir):
  773. self._clear_cache()
  774. try:
  775. rmtree(git_dir)
  776. except Exception as ex:
  777. if HIDE_WINDOWS_KNOWN_ERRORS:
  778. raise SkipTest("FIXME: fails with: PermissionError\n %s", ex)
  779. else:
  780. raise
  781. # end handle separate bare repository
  782. # END handle module deletion
  783. # void our data not to delay invalid access
  784. if not dry_run:
  785. self._clear_cache()
  786. # DELETE CONFIGURATION
  787. ######################
  788. if configuration and not dry_run:
  789. # first the index-entry
  790. parent_index = self.repo.index
  791. try:
  792. del(parent_index.entries[parent_index.entry_key(self.path, 0)])
  793. except KeyError:
  794. pass
  795. # END delete entry
  796. parent_index.write()
  797. # now git config - need the config intact, otherwise we can't query
  798. # information anymore
  799. with self.repo.config_writer() as writer:
  800. writer.remove_section(sm_section(self.name))
  801. with self.config_writer() as writer:
  802. writer.remove_section()
  803. # END delete configuration
  804. return self
  805. def set_parent_commit(self, commit, check=True):
  806. """Set this instance to use the given commit whose tree is supposed to
  807. contain the .gitmodules blob.
  808. :param commit:
  809. Commit'ish reference pointing at the root_tree, or None to always point to the
  810. most recent commit
  811. :param check:
  812. if True, relatively expensive checks will be performed to verify
  813. validity of the submodule.
  814. :raise ValueError: if the commit's tree didn't contain the .gitmodules blob.
  815. :raise ValueError:
  816. if the parent commit didn't store this submodule under the current path
  817. :return: self"""
  818. if commit is None:
  819. self._parent_commit = None
  820. return self
  821. # end handle None
  822. pcommit = self.repo.commit(commit)
  823. pctree = pcommit.tree
  824. if self.k_modules_file not in pctree:
  825. raise ValueError("Tree of commit %s did not contain the %s file" % (commit, self.k_modules_file))
  826. # END handle exceptions
  827. prev_pc = self._parent_commit
  828. self._parent_commit = pcommit
  829. if check:
  830. parser = self._config_parser(self.repo, self._parent_commit, read_only=True)
  831. if not parser.has_section(sm_section(self.name)):
  832. self._parent_commit = prev_pc
  833. raise ValueError("Submodule at path %r did not exist in parent commit %s" % (self.path, commit))
  834. # END handle submodule did not exist
  835. # END handle checking mode
  836. # update our sha, it could have changed
  837. # If check is False, we might see a parent-commit that doesn't even contain the submodule anymore.
  838. # in that case, mark our sha as being NULL
  839. try:
  840. self.binsha = pctree[self.path].binsha
  841. except KeyError:
  842. self.binsha = self.NULL_BIN_SHA
  843. # end
  844. self._clear_cache()
  845. return self
  846. @unbare_repo
  847. def config_writer(self, index=None, write=True):
  848. """:return: a config writer instance allowing you to read and write the data
  849. belonging to this submodule into the .gitmodules file.
  850. :param index: if not None, an IndexFile instance which should be written.
  851. defaults to the index of the Submodule's parent repository.
  852. :param write: if True, the index will be written each time a configuration
  853. value changes.
  854. :note: the parameters allow for a more efficient writing of the index,
  855. as you can pass in a modified index on your own, prevent automatic writing,
  856. and write yourself once the whole operation is complete
  857. :raise ValueError: if trying to get a writer on a parent_commit which does not
  858. match the current head commit
  859. :raise IOError: If the .gitmodules file/blob could not be read"""
  860. writer = self._config_parser_constrained(read_only=False)
  861. if index is not None:
  862. writer.config._index = index
  863. writer.config._auto_write = write
  864. return writer
  865. @unbare_repo
  866. def rename(self, new_name):
  867. """Rename this submodule
  868. :note: This method takes care of renaming the submodule in various places, such as
  869. * $parent_git_dir/config
  870. * $working_tree_dir/.gitmodules
  871. * (git >=v1.8.0: move submodule repository to new name)
  872. As .gitmodules will be changed, you would need to make a commit afterwards. The changed .gitmodules file
  873. will already be added to the index
  874. :return: this submodule instance
  875. """
  876. if self.name == new_name:
  877. return self
  878. # .git/config
  879. with self.repo.config_writer() as pw:
  880. # As we ourselves didn't write anything about submodules into the parent .git/config,
  881. # we will not require it to exist, and just ignore missing entries.
  882. if pw.has_section(sm_section(self.name)):
  883. pw.rename_section(sm_section(self.name), sm_section(new_name))
  884. # .gitmodules
  885. with self.config_writer(write=True).config as cw:
  886. cw.rename_section(sm_section(self.name), sm_section(new_name))
  887. self._name = new_name
  888. # .git/modules
  889. mod = self.module()
  890. if mod.has_separate_working_tree():
  891. destination_module_abspath = self._module_abspath(self.repo, self.path, new_name)
  892. source_dir = mod.git_dir
  893. # Let's be sure the submodule name is not so obviously tied to a directory
  894. if destination_module_abspath.startswith(mod.git_dir):
  895. tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4()))
  896. os.renames(source_dir, tmp_dir)
  897. source_dir = tmp_dir
  898. # end handle self-containment
  899. os.renames(source_dir, destination_module_abspath)
  900. self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath)
  901. # end move separate git repository
  902. return self
  903. #} END edit interface
  904. #{ Query Interface
  905. @unbare_repo
  906. def module(self):
  907. """:return: Repo instance initialized from the repository at our submodule path
  908. :raise InvalidGitRepositoryError: if a repository was not available. This could
  909. also mean that it was not yet initialized"""
  910. # late import to workaround circular dependencies
  911. module_checkout_abspath = self.abspath
  912. try:
  913. repo = git.Repo(module_checkout_abspath)
  914. if repo != self.repo:
  915. return repo
  916. # END handle repo uninitialized
  917. except (InvalidGitRepositoryError, NoSuchPathError):
  918. raise InvalidGitRepositoryError("No valid repository at %s" % module_checkout_abspath)
  919. else:
  920. raise InvalidGitRepositoryError("Repository at %r was not yet checked out" % module_checkout_abspath)
  921. # END handle exceptions
  922. def module_exists(self):
  923. """:return: True if our module exists and is a valid git repository. See module() method"""
  924. try:
  925. self.module()
  926. return True
  927. except Exception:
  928. return False
  929. # END handle exception
  930. def exists(self):
  931. """
  932. :return: True if the submodule exists, False otherwise. Please note that
  933. a submodule may exist (in the .gitmodules file) even though its module
  934. doesn't exist on disk"""
  935. # keep attributes for later, and restore them if we have no valid data
  936. # this way we do not actually alter the state of the object
  937. loc = locals()
  938. for attr in self._cache_attrs:
  939. try:
  940. if hasattr(self, attr):
  941. loc[attr] = getattr(self, attr)
  942. # END if we have the attribute cache
  943. except (cp.NoSectionError, ValueError):
  944. # on PY3, this can happen apparently ... don't know why this doesn't happen on PY2
  945. pass
  946. # END for each attr
  947. self._clear_cache()
  948. try:
  949. try:
  950. self.path
  951. return True
  952. except Exception:
  953. return False
  954. # END handle exceptions
  955. finally:
  956. for attr in self._cache_attrs:
  957. if attr in loc:
  958. setattr(self, attr, loc[attr])
  959. # END if we have a cache
  960. # END reapply each attribute
  961. # END handle object state consistency
  962. @property
  963. def branch(self):
  964. """:return: The branch instance that we are to checkout
  965. :raise InvalidGitRepositoryError: if our module is not yet checked out"""
  966. return mkhead(self.module(), self._branch_path)
  967. @property
  968. def branch_path(self):
  969. """
  970. :return: full (relative) path as string to the branch we would checkout
  971. from the remote and track"""
  972. return self._branch_path
  973. @property
  974. def branch_name(self):
  975. """:return: the name of the branch, which is the shortest possible branch name"""
  976. # use an instance method, for this we create a temporary Head instance
  977. # which uses a repository that is available at least ( it makes no difference )
  978. return git.Head(self.repo, self._branch_path).name
  979. @property
  980. def url(self):
  981. """:return: The url to the repository which our module-repository refers to"""
  982. return self._url
  983. @property
  984. def parent_commit(self):
  985. """:return: Commit instance with the tree containing the .gitmodules file
  986. :note: will always point to the current head's commit if it was not set explicitly"""
  987. if self._parent_commit is None:
  988. return self.repo.commit()
  989. return self._parent_commit
  990. @property
  991. def name(self):
  992. """:return: The name of this submodule. It is used to identify it within the
  993. .gitmodules file.
  994. :note: by default, the name is the path at which to find the submodule, but
  995. in git-python it should be a unique identifier similar to the identifiers
  996. used for remotes, which allows to change the path of the submodule
  997. easily
  998. """
  999. return self._name
  1000. def config_reader(self):
  1001. """
  1002. :return: ConfigReader instance which allows you to qurey the configuration values
  1003. of this submodule, as provided by the .gitmodules file
  1004. :note: The config reader will actually read the data directly from the repository
  1005. and thus does not need nor care about your working tree.
  1006. :note: Should be cached by the caller and only kept as long as needed
  1007. :raise IOError: If the .gitmodules file/blob could not be read"""
  1008. return self._config_parser_constrained(read_only=True)
  1009. def children(self):
  1010. """
  1011. :return: IterableList(Submodule, ...) an iterable list of submodules instances
  1012. which are children of this submodule or 0 if the submodule is not checked out"""
  1013. return self._get_intermediate_items(self)
  1014. #} END query interface
  1015. #{ Iterable Interface
  1016. @classmethod
  1017. def iter_items(cls, repo, parent_commit='HEAD'):
  1018. """:return: iterator yielding Submodule instances available in the given repository"""
  1019. pc = repo.commit(parent_commit) # parent commit instance
  1020. try:
  1021. parser = cls._config_parser(repo, pc, read_only=True)
  1022. except IOError:
  1023. return
  1024. # END handle empty iterator
  1025. rt = pc.tree # root tree
  1026. for sms in parser.sections():
  1027. n = sm_name(sms)
  1028. p = parser.get(sms, 'path')
  1029. u = parser.get(sms, 'url')
  1030. b = cls.k_head_default
  1031. if parser.has_option(sms, cls.k_head_option):
  1032. b = str(parser.get(sms, cls.k_head_option))
  1033. # END handle optional information
  1034. # get the binsha
  1035. index = repo.index
  1036. try:
  1037. sm = rt[p]
  1038. except KeyError:
  1039. # try the index, maybe it was just added
  1040. try:
  1041. entry = index.entries[index.entry_key(p, 0)]
  1042. sm = Submodule(repo, entry.binsha, entry.mode, entry.path)
  1043. except KeyError:
  1044. # The submodule doesn't exist, probably it wasn't
  1045. # removed from the .gitmodules file.
  1046. continue
  1047. # END handle keyerror
  1048. # END handle critical error
  1049. # fill in remaining info - saves time as it doesn't have to be parsed again
  1050. sm._name = n
  1051. if pc != repo.commit():
  1052. sm._parent_commit = pc
  1053. # end set only if not most recent !
  1054. sm._branch_path = git.Head.to_full_path(b)
  1055. sm._url = u
  1056. yield sm
  1057. # END for each section
  1058. #} END iterable interface