From 2c12fef1b1971ba7a50e7e5c497caf51e0f68479 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 24 Nov 2010 23:55:51 +0100 Subject: Submodule: Added dry_run and progress parameter to the update method. It is copatible to the RemoteProgress and should satisfy all progress needs. Dryrun will be useful in conjunction with the progress to verify the changes to be done --- objects/submodule/base.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'objects/submodule/base.py') diff --git a/objects/submodule/base.py b/objects/submodule/base.py index 36b48d78..85350e66 100644 --- a/objects/submodule/base.py +++ b/objects/submodule/base.py @@ -12,7 +12,8 @@ from StringIO import StringIO # need a dict to set bloody .name field from git.util import ( Iterable, join_path_native, - to_native_path_linux + to_native_path_linux, + RemoteProgress ) from git.config import SectionConstraint @@ -20,6 +21,7 @@ from git.exc import ( InvalidGitRepositoryError, NoSuchPathError ) + import stat import git @@ -29,7 +31,16 @@ import time import shutil -__all__ = ["Submodule"] +__all__ = ["Submodule", "UpdateProgress"] + + +class UpdateProgress(RemoteProgress): + """Class providing detailed progress information to the caller who should + derive from it and implement the ``update(...)`` message""" + ADD, REMOVE, UPDATE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes+3)] + + __slots__ = tuple() + # IndexObject comes via util module, its a 'hacky' fix thanks to pythons import @@ -285,7 +296,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): return sm - def update(self, recursive=False, init=True, to_latest_revision=False): + def update(self, recursive=False, init=True, to_latest_revision=False, progress=None): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -297,6 +308,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): This only works if we have a local tracking branch, which is the case if the remote repository had a master branch, or of the 'branch' option was specified for this submodule and the branch existed remotely + :param progress: UpdateProgress instance or None of no progress should be shown :note: does nothing in bare repositories :note: method is definitely not atomic if recurisve is True :return: self""" @@ -304,6 +316,9 @@ class Submodule(util.IndexObject, Iterable, Traversable): return self #END pass in bare mode + if progress is None: + progress = UpdateProgress() + #END handle progress # ASSURE REPO IS PRESENT AND UPTODATE ##################################### -- cgit v1.2.3 From d810f2700f54146063ca2cb6bb1cf03c36744a2c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 25 Nov 2010 09:56:36 +0100 Subject: Integrated progress updates into all submodule.update methods. Submodule.update now supports dry_run as well, which is still to be implemented in all cases --- objects/submodule/base.py | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'objects/submodule/base.py') diff --git a/objects/submodule/base.py b/objects/submodule/base.py index 85350e66..c47e2bfc 100644 --- a/objects/submodule/base.py +++ b/objects/submodule/base.py @@ -37,10 +37,17 @@ __all__ = ["Submodule", "UpdateProgress"] class UpdateProgress(RemoteProgress): """Class providing detailed progress information to the caller who should derive from it and implement the ``update(...)`` message""" - ADD, REMOVE, UPDATE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes+3)] + CLONE, FETCH, UPDWKTREE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes+3)] + _num_op_codes = RemoteProgress._num_op_codes + 3 __slots__ = tuple() + +BEGIN = UpdateProgress.BEGIN +END = UpdateProgress.END +CLONE = UpdateProgress.CLONE +FETCH = UpdateProgress.FETCH +UPDWKTREE = UpdateProgress.UPDWKTREE # IndexObject comes via util module, its a 'hacky' fix thanks to pythons import @@ -296,7 +303,8 @@ class Submodule(util.IndexObject, Iterable, Traversable): return sm - def update(self, recursive=False, init=True, to_latest_revision=False, progress=None): + def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, + dry_run=False): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -309,6 +317,8 @@ class Submodule(util.IndexObject, Iterable, Traversable): if the remote repository had a master branch, or of the 'branch' option was specified for this submodule and the branch existed remotely :param progress: UpdateProgress instance or None of no progress should be shown + :param dry_run: if True, the operation will only be simulated, but not performed. + All performed operations are read-only :note: does nothing in bare repositories :note: method is definitely not atomic if recurisve is True :return: self""" @@ -324,8 +334,22 @@ class Submodule(util.IndexObject, Iterable, Traversable): ##################################### try: mrepo = self.module() - for remote in mrepo.remotes: - remote.fetch() + rmts = mrepo.remotes + len_rmts = len(rmts) + for i, remote in enumerate(rmts): + op = FETCH + if i == 0: + op |= BEGIN + #END handle start + + progress.update(op, i, len_rmts, "Fetching remote %s" % remote) + #=============================== + remote.fetch(progress=progress) + #=============================== + if i == len_rmts-1: + op |= END + #END handle end + progress.update(op, i, len_rmts, "Done fetching remote %s" % remote) #END fetch new data except InvalidGitRepositoryError: if not init: @@ -345,7 +369,9 @@ class Submodule(util.IndexObject, Iterable, Traversable): # don't check it out at first - nonetheless it will create a local # branch according to the remote-HEAD if possible + progress.update(BEGIN|CLONE, 0, 1, "Cloning %s to %s" % (self.url, module_path)) mrepo = git.Repo.clone_from(self.url, module_path, n=True) + progress.update(END|CLONE, 0, 1, "Done cloning to %s" % module_path) # see whether we have a valid branch to checkout try: @@ -396,6 +422,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): # update the working tree if mrepo.head.commit.binsha != binsha: + progress.update(BEGIN|UPDWKTREE, 0, 1, "Updating working tree at %s" % self.path) if is_detached: # NOTE: for now we force, the user is no supposed to change detached # submodules anyway. Maybe at some point this becomes an option, to @@ -408,13 +435,14 @@ class Submodule(util.IndexObject, Iterable, Traversable): # branch - this should be prevented when setting the branch option mrepo.head.reset(hexsha, index=True, working_tree=True) # END handle checkout + progress.update(END|UPDWKTREE, 0, 1, "Done updating working tree at %s" % self.path) # END update to new commit only if needed # HANDLE RECURSION ################## if recursive: for submodule in self.iter_items(self.module()): - submodule.update(recursive, init, to_latest_revision) + submodule.update(recursive, init, to_latest_revision, progress=progress) # END handle recursive update # END for each submodule -- cgit v1.2.3 From 523fb313f77717a12086319429f13723fe95f85e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 25 Nov 2010 11:23:57 +0100 Subject: Implemented dry_run mode including tests for RootModule.update and Submodule --- objects/submodule/base.py | 127 +++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 51 deletions(-) (limited to 'objects/submodule/base.py') diff --git a/objects/submodule/base.py b/objects/submodule/base.py index c47e2bfc..88cc42da 100644 --- a/objects/submodule/base.py +++ b/objects/submodule/base.py @@ -329,6 +329,15 @@ class Submodule(util.IndexObject, Iterable, Traversable): if progress is None: progress = UpdateProgress() #END handle progress + prefix = '' + if dry_run: + prefix = "DRY-RUN: " + #END handle prefix + + # to keep things plausible in dry-run mode + if dry_run: + mrepo = None + #END init mrepo # ASSURE REPO IS PRESENT AND UPTODATE ##################################### @@ -342,14 +351,16 @@ class Submodule(util.IndexObject, Iterable, Traversable): op |= BEGIN #END handle start - progress.update(op, i, len_rmts, "Fetching remote %s" % remote) + progress.update(op, i, len_rmts, prefix+"Fetching remote %s of submodule %r" % (remote, self.name)) #=============================== - remote.fetch(progress=progress) + if not dry_run: + remote.fetch(progress=progress) + #END handle dry-run #=============================== if i == len_rmts-1: op |= END #END handle end - progress.update(op, i, len_rmts, "Done fetching remote %s" % remote) + progress.update(op, i, len_rmts, prefix+"Done fetching remote of submodule %r" % self.name) #END fetch new data except InvalidGitRepositoryError: if not init: @@ -359,7 +370,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): # there is no git-repository yet - but delete empty paths module_path = join_path_native(self.repo.working_tree_dir, self.path) - if os.path.isdir(module_path): + if not dry_run and os.path.isdir(module_path): try: os.rmdir(module_path) except OSError: @@ -369,33 +380,38 @@ class Submodule(util.IndexObject, Iterable, Traversable): # don't check it out at first - nonetheless it will create a local # branch according to the remote-HEAD if possible - progress.update(BEGIN|CLONE, 0, 1, "Cloning %s to %s" % (self.url, module_path)) - mrepo = git.Repo.clone_from(self.url, module_path, n=True) - progress.update(END|CLONE, 0, 1, "Done cloning to %s" % module_path) + progress.update(BEGIN|CLONE, 0, 1, prefix+"Cloning %s to %s in submodule %r" % (self.url, module_path, self.name)) + if not dry_run: + mrepo = git.Repo.clone_from(self.url, module_path, n=True) + #END handle dry-run + progress.update(END|CLONE, 0, 1, prefix+"Done cloning to %s" % module_path) - # see whether we have a valid branch to checkout - try: - # find a remote which has our branch - we try to be flexible - remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) - local_branch = mkhead(mrepo, self.branch_path) - - # have a valid branch, but no checkout - make sure we can figure - # that out by marking the commit with a null_sha - local_branch.set_object(util.Object(mrepo, self.NULL_BIN_SHA)) - # END initial checkout + branch creation - - # make sure HEAD is not detached - mrepo.head.set_reference(local_branch, logmsg="submodule: attaching head to %s" % local_branch) - mrepo.head.ref.set_tracking_branch(remote_branch) - except IndexError: - print >> sys.stderr, "Warning: Failed to checkout tracking branch %s" % self.branch_path - #END handle tracking branch - # NOTE: Have to write the repo config file as well, otherwise - # the default implementation will be offended and not update the repository - # Maybe this is a good way to assure it doesn't get into our way, but - # we want to stay backwards compatible too ... . Its so redundant ! - self.repo.config_writer().set_value(sm_section(self.name), 'url', self.url) + if not dry_run: + # see whether we have a valid branch to checkout + try: + # find a remote which has our branch - we try to be flexible + remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) + local_branch = mkhead(mrepo, self.branch_path) + + # have a valid branch, but no checkout - make sure we can figure + # that out by marking the commit with a null_sha + local_branch.set_object(util.Object(mrepo, self.NULL_BIN_SHA)) + # END initial checkout + branch creation + + # make sure HEAD is not detached + mrepo.head.set_reference(local_branch, logmsg="submodule: attaching head to %s" % local_branch) + mrepo.head.ref.set_tracking_branch(remote_branch) + except IndexError: + print >> sys.stderr, "Warning: Failed to checkout tracking branch %s" % self.branch_path + #END handle tracking branch + + # NOTE: Have to write the repo config file as well, otherwise + # the default implementation will be offended and not update the repository + # Maybe this is a good way to assure it doesn't get into our way, but + # we want to stay backwards compatible too ... . Its so redundant ! + self.repo.config_writer().set_value(sm_section(self.name), 'url', self.url) + #END handle dry_run #END handle initalization @@ -403,8 +419,12 @@ class Submodule(util.IndexObject, Iterable, Traversable): ############################ binsha = self.binsha hexsha = self.hexsha - is_detached = mrepo.head.is_detached - if to_latest_revision: + if mrepo is not None: + # mrepo is only set if we are not in dry-run mode or if the module existed + is_detached = mrepo.head.is_detached + #END handle dry_run + + if not dry_run and to_latest_revision: msg_base = "Cannot update to latest revision in repository at %r as " % mrepo.working_dir if not is_detached: rref = mrepo.head.ref.tracking_branch() @@ -421,29 +441,35 @@ class Submodule(util.IndexObject, Iterable, Traversable): # END handle to_latest_revision option # update the working tree - if mrepo.head.commit.binsha != binsha: - progress.update(BEGIN|UPDWKTREE, 0, 1, "Updating working tree at %s" % self.path) - if is_detached: - # NOTE: for now we force, the user is no supposed to change detached - # submodules anyway. Maybe at some point this becomes an option, to - # properly handle user modifications - see below for future options - # regarding rebase and merge. - mrepo.git.checkout(hexsha, force=True) - else: - # TODO: allow to specify a rebase, merge, or reset - # TODO: Warn if the hexsha forces the tracking branch off the remote - # branch - this should be prevented when setting the branch option - mrepo.head.reset(hexsha, index=True, working_tree=True) - # END handle checkout - progress.update(END|UPDWKTREE, 0, 1, "Done updating working tree at %s" % self.path) + # handles dry_run + if mrepo is not None and mrepo.head.commit.binsha != binsha: + progress.update(BEGIN|UPDWKTREE, 0, 1, prefix+"Updating working tree at %s for submodule %r" % (self.path, self.name)) + if not dry_run: + if is_detached: + # NOTE: for now we force, the user is no supposed to change detached + # submodules anyway. Maybe at some point this becomes an option, to + # properly handle user modifications - see below for future options + # regarding rebase and merge. + mrepo.git.checkout(hexsha, force=True) + else: + # TODO: allow to specify a rebase, merge, or reset + # TODO: Warn if the hexsha forces the tracking branch off the remote + # branch - this should be prevented when setting the branch option + mrepo.head.reset(hexsha, index=True, working_tree=True) + # END handle checkout + #END handle dry_run + progress.update(END|UPDWKTREE, 0, 1, prefix+"Done updating working tree for submodule %r" % self.name) # END update to new commit only if needed # HANDLE RECURSION ################## if recursive: - for submodule in self.iter_items(self.module()): - submodule.update(recursive, init, to_latest_revision, progress=progress) - # END handle recursive update + # in dry_run mode, the module might not exist + if mrepo is not None: + for submodule in self.iter_items(self.module()): + submodule.update(recursive, init, to_latest_revision, progress=progress, dry_run=dry_run) + # END handle recursive update + #END handle dry run # END for each submodule return self @@ -843,8 +869,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): def children(self): """ :return: IterableList(Submodule, ...) an iterable list of submodules instances - which are children of this submodule - :raise InvalidGitRepositoryError: if the submodule is not checked-out""" + which are children of this submodule or 0 if the submodule is not checked out""" return self._get_intermediate_items(self) #} END query interface -- cgit v1.2.3