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 ++++++++++++++++++--- objects/submodule/root.py | 20 ++++++++++++++------ 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'objects') 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 ##################################### diff --git a/objects/submodule/root.py b/objects/submodule/root.py index 753c6df4..b0dba08b 100644 --- a/objects/submodule/root.py +++ b/objects/submodule/root.py @@ -1,4 +1,4 @@ -from base import Submodule +from base import Submodule, UpdateProgress from util import ( find_first_remote_branch ) @@ -9,7 +9,7 @@ import sys __all__ = ["RootModule"] - + class RootModule(Submodule): """A (virtual) Root of all submodules in the given repository. It can be used to more easily traverse all submodules of the master repository""" @@ -38,7 +38,8 @@ class RootModule(Submodule): #{ Interface - def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, to_latest_revision=False): + def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, + to_latest_revision=False, progress=None, dry_run=False): """Update the submodules of this repository to the current HEAD commit. This method behaves smartly by determining changes of the path of a submodules repository, next to changes to the to-be-checked-out commit or the branch to be @@ -57,11 +58,18 @@ class RootModule(Submodule): :param init: If we encounter a new module which would need to be initialized, then do it. :param to_latest_revision: If True, instead of checking out the revision pointed to by this submodule's sha, the checked out tracking branch will be merged with the - newest remote branch fetched from the repository's origin""" + newest remote branch fetched from the repository's origin + :param progress: UpdateProgress instance or None if no progress should be sent + :param dry_run: if True, operations will not actually be performed. Progress messages + will change accordingly to indicate the WOULD DO state of the operation.""" if self.repo.bare: raise InvalidGitRepositoryError("Cannot update submodules in bare repositories") # END handle bare + if progress is None: + progress = UpdateProgress() + #END assure progress is set + repo = self.repo # HANDLE COMMITS @@ -125,7 +133,7 @@ class RootModule(Submodule): assert nn not in [r.name for r in rmts] smr = smm.create_remote(nn, sm.url) - smr.fetch() + smr.fetch(progress=progress) # If we have a tracking branch, it should be available # in the new remote as well. @@ -234,7 +242,7 @@ class RootModule(Submodule): ###################################### for sm in sms: # update the submodule using the default method - sm.update(recursive=False, init=init, to_latest_revision=to_latest_revision) + sm.update(recursive=False, init=init, to_latest_revision=to_latest_revision, progress=progress) # update recursively depth first - question is which inconsitent # state will be better in case it fails somewhere. Defective branch -- 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 +++++++++++++++++++++++---- objects/submodule/root.py | 65 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 86 insertions(+), 17 deletions(-) (limited to 'objects') 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 diff --git a/objects/submodule/root.py b/objects/submodule/root.py index b0dba08b..49fb1417 100644 --- a/objects/submodule/root.py +++ b/objects/submodule/root.py @@ -10,6 +10,20 @@ import sys __all__ = ["RootModule"] +class RootUpdateProgress(UpdateProgress): + """Utility class which adds more opcodes to the UpdateProgress""" + REMOVE, PATHCHANGE, BRANCHCHANGE, URLCHANGE = [1 << x for x in range(UpdateProgress._num_op_codes, UpdateProgress._num_op_codes+4)] + _num_op_codes = UpdateProgress._num_op_codes+4 + + __slots__ = tuple() + +BEGIN = RootUpdateProgress.BEGIN +END = RootUpdateProgress.END +REMOVE = RootUpdateProgress.REMOVE +BRANCHCHANGE = RootUpdateProgress.BRANCHCHANGE +URLCHANGE = RootUpdateProgress.URLCHANGE +PATHCHANGE = RootUpdateProgress.PATHCHANGE + class RootModule(Submodule): """A (virtual) Root of all submodules in the given repository. It can be used to more easily traverse all submodules of the master repository""" @@ -59,7 +73,7 @@ class RootModule(Submodule): :param to_latest_revision: If True, instead of checking out the revision pointed to by this submodule's sha, the checked out tracking branch will be merged with the newest remote branch fetched from the repository's origin - :param progress: UpdateProgress instance or None if no progress should be sent + :param progress: RootUpdateProgress instance or None if no progress should be sent :param dry_run: if True, operations will not actually be performed. Progress messages will change accordingly to indicate the WOULD DO state of the operation.""" if self.repo.bare: @@ -67,13 +81,13 @@ class RootModule(Submodule): # END handle bare if progress is None: - progress = UpdateProgress() + progress = RootUpdateProgress() #END assure progress is set repo = self.repo - # HANDLE COMMITS - ################## + # SETUP BASE COMMIT + ################### cur_commit = repo.head.commit if previous_commit is None: try: @@ -97,29 +111,50 @@ class RootModule(Submodule): # HANDLE REMOVALS ################### - for rsm in (spsms - ssms): + rrsm = (spsms - ssms) + len_rrsm = len(rrsm) + for i, rsm in enumerate(rrsm): + op = REMOVE + if i == 0: + op |= BEGIN + #END handle begin + # fake it into thinking its at the current commit to allow deletion # of previous module. Trigger the cache to be updated before that - #rsm.url + progress.update(op, i, len_rrsm, "Removing submodule %s at %s" % (rsm.name, rsm.abspath)) rsm._parent_commit = repo.head.commit rsm.remove(configuration=False, module=True, force=force_remove) + + if i == len_rrsm-1: + op |= END + #END handle end + progress.update(op, i, len_rrsm, "Done removing submodule %s" % rsm.name) # END for each removed submodule # HANDLE PATH RENAMES ##################### # url changes + branch changes - for csm in (spsms & ssms): + csms = (spsms & ssms) + len_csms = len(csms) + for i, csm in enumerate(csms): psm = psms[csm.name] sm = sms[csm.name] + #PATH CHANGES + ############## if sm.path != psm.path and psm.module_exists(): + progress.update(BEGIN|PATHCHANGE, i, len_csms, "Moving submodule's %s repository from %s to %s" % (sm.name, psm.abspath, sm.abspath)) # move the module to the new path psm.move(sm.path, module=True, configuration=False) + progress.update(END|PATHCHANGE, i, len_csms, "Done moving repository of submodule %s" % sm.name) # END handle path changes if sm.module_exists(): - # handle url change + # HANDLE URL CHANGE + ################### if sm.url != psm.url: + progress.update(BEGIN|URLCHANGE, i, len_csms, "Changing url of submodule %s from %s to %s" % (sm.name, psm.url, sm.url)) + # Add the new remote, remove the old one # This way, if the url just changes, the commits will not # have to be re-retrieved @@ -130,7 +165,6 @@ class RootModule(Submodule): # don't do anything if we already have the url we search in place if len([r for r in rmts if r.url == sm.url]) == 0: - assert nn not in [r.name for r in rmts] smr = smm.create_remote(nn, sm.url) smr.fetch(progress=progress) @@ -202,12 +236,16 @@ class RootModule(Submodule): #NOTE: All checkout is performed by the base implementation of update + progress.update(END|URLCHANGE, i, len_csms, "Done adjusting url of submodule %s" % (sm.name)) # END skip remote handling if new url already exists in module # END handle url + # HANDLE PATH CHANGES + ##################### if sm.branch_path != psm.branch_path: # finally, create a new tracking branch which tracks the # new remote branch + progress.update(BEGIN|BRANCHCHANGE, i, len_csms, "Changing branch of submodule %s from %s to %s" % (sm.name, psm.branch_path, sm.branch_path)) smm = sm.module() smmr = smm.remotes try: @@ -234,6 +272,7 @@ class RootModule(Submodule): #NOTE: All checkout is done in the base implementation of update + progress.update(END|BRANCHCHANGE, i, len_csms, "Done changing branch of submodule %s" % sm.name) #END handle branch #END handle # END for each common submodule @@ -242,15 +281,17 @@ class RootModule(Submodule): ###################################### for sm in sms: # update the submodule using the default method - sm.update(recursive=False, init=init, to_latest_revision=to_latest_revision, progress=progress) + sm.update(recursive=False, init=init, to_latest_revision=to_latest_revision, + progress=progress, dry_run=dry_run) # update recursively depth first - question is which inconsitent # state will be better in case it fails somewhere. Defective branch # or defective depth. The RootSubmodule type will never process itself, # which was done in the previous expression if recursive: - type(self)(sm.module()).update(recursive=True, force_remove=force_remove, - init=init, to_latest_revision=to_latest_revision) + type(self)(sm.module()).update( recursive=True, force_remove=force_remove, + init=init, to_latest_revision=to_latest_revision, + progress=progress, dry_run=dry_run) #END handle recursive # END for each submodule to update -- 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 +++++++++++++++---------- objects/submodule/root.py | 235 ++++++++++++++++++++++++---------------------- 2 files changed, 200 insertions(+), 162 deletions(-) (limited to 'objects') 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 diff --git a/objects/submodule/root.py b/objects/submodule/root.py index 49fb1417..36cd7209 100644 --- a/objects/submodule/root.py +++ b/objects/submodule/root.py @@ -7,7 +7,7 @@ import git import sys -__all__ = ["RootModule"] +__all__ = ["RootModule", "RootUpdateProgress"] class RootUpdateProgress(UpdateProgress): @@ -84,6 +84,10 @@ class RootModule(Submodule): progress = RootUpdateProgress() #END assure progress is set + prefix = '' + if dry_run: + prefix = 'DRY-RUN: ' + repo = self.repo # SETUP BASE COMMIT @@ -121,14 +125,16 @@ class RootModule(Submodule): # fake it into thinking its at the current commit to allow deletion # of previous module. Trigger the cache to be updated before that - progress.update(op, i, len_rrsm, "Removing submodule %s at %s" % (rsm.name, rsm.abspath)) + progress.update(op, i, len_rrsm, prefix+"Removing submodule %r at %s" % (rsm.name, rsm.abspath)) rsm._parent_commit = repo.head.commit - rsm.remove(configuration=False, module=True, force=force_remove) + if not dry_run: + rsm.remove(configuration=False, module=True, force=force_remove) + #END handle dry-run if i == len_rrsm-1: op |= END #END handle end - progress.update(op, i, len_rrsm, "Done removing submodule %s" % rsm.name) + progress.update(op, i, len_rrsm, prefix+"Done removing submodule %r" % rsm.name) # END for each removed submodule # HANDLE PATH RENAMES @@ -143,18 +149,18 @@ class RootModule(Submodule): #PATH CHANGES ############## if sm.path != psm.path and psm.module_exists(): - progress.update(BEGIN|PATHCHANGE, i, len_csms, "Moving submodule's %s repository from %s to %s" % (sm.name, psm.abspath, sm.abspath)) + progress.update(BEGIN|PATHCHANGE, i, len_csms, prefix+"Moving repository of submodule %r from %s to %s" % (sm.name, psm.abspath, sm.abspath)) # move the module to the new path - psm.move(sm.path, module=True, configuration=False) - progress.update(END|PATHCHANGE, i, len_csms, "Done moving repository of submodule %s" % sm.name) + if not dry_run: + psm.move(sm.path, module=True, configuration=False) + #END handle dry_run + progress.update(END|PATHCHANGE, i, len_csms, prefix+"Done moving repository of submodule %r" % sm.name) # END handle path changes if sm.module_exists(): # HANDLE URL CHANGE ################### if sm.url != psm.url: - progress.update(BEGIN|URLCHANGE, i, len_csms, "Changing url of submodule %s from %s to %s" % (sm.name, psm.url, sm.url)) - # Add the new remote, remove the old one # This way, if the url just changes, the commits will not # have to be re-retrieved @@ -164,79 +170,81 @@ class RootModule(Submodule): # don't do anything if we already have the url we search in place if len([r for r in rmts if r.url == sm.url]) == 0: + progress.update(BEGIN|URLCHANGE, i, len_csms, prefix+"Changing url of submodule %r from %s to %s" % (sm.name, psm.url, sm.url)) - assert nn not in [r.name for r in rmts] - smr = smm.create_remote(nn, sm.url) - smr.fetch(progress=progress) - - # If we have a tracking branch, it should be available - # in the new remote as well. - if len([r for r in smr.refs if r.remote_head == sm.branch_name]) == 0: - raise ValueError("Submodule branch named %r was not available in new submodule remote at %r" % (sm.branch_name, sm.url)) - # END head is not detached - - # now delete the changed one - rmt_for_deletion = None - for remote in rmts: - if remote.url == psm.url: - rmt_for_deletion = remote - break - # END if urls match - # END for each remote - - # if we didn't find a matching remote, but have exactly one, - # we can safely use this one - if rmt_for_deletion is None: - if len(rmts) == 1: - rmt_for_deletion = rmts[0] - else: - # if we have not found any remote with the original url - # we may not have a name. This is a special case, - # and its okay to fail here - # Alternatively we could just generate a unique name and leave all - # existing ones in place - raise InvalidGitRepositoryError("Couldn't find original remote-repo at url %r" % psm.url) - #END handle one single remote - # END handle check we found a remote - - orig_name = rmt_for_deletion.name - smm.delete_remote(rmt_for_deletion) - # NOTE: Currently we leave tags from the deleted remotes - # as well as separate tracking branches in the possibly totally - # changed repository ( someone could have changed the url to - # another project ). At some point, one might want to clean - # it up, but the danger is high to remove stuff the user - # has added explicitly - - # rename the new remote back to what it was - smr.rename(orig_name) - - # early on, we verified that the our current tracking branch - # exists in the remote. Now we have to assure that the - # sha we point to is still contained in the new remote - # tracking branch. - smsha = sm.binsha - found = False - rref = smr.refs[self.branch_name] - for c in rref.commit.traverse(): - if c.binsha == smsha: - found = True - break - # END traverse all commits in search for sha - # END for each commit - - if not found: - # adjust our internal binsha to use the one of the remote - # this way, it will be checked out in the next step - # This will change the submodule relative to us, so - # the user will be able to commit the change easily - print >> sys.stderr, "WARNING: Current sha %s was not contained in the tracking branch at the new remote, setting it the the remote's tracking branch" % sm.hexsha - sm.binsha = rref.commit.binsha - #END reset binsha - - #NOTE: All checkout is performed by the base implementation of update - - progress.update(END|URLCHANGE, i, len_csms, "Done adjusting url of submodule %s" % (sm.name)) + if not dry_run: + assert nn not in [r.name for r in rmts] + smr = smm.create_remote(nn, sm.url) + smr.fetch(progress=progress) + + # If we have a tracking branch, it should be available + # in the new remote as well. + if len([r for r in smr.refs if r.remote_head == sm.branch_name]) == 0: + raise ValueError("Submodule branch named %r was not available in new submodule remote at %r" % (sm.branch_name, sm.url)) + # END head is not detached + + # now delete the changed one + rmt_for_deletion = None + for remote in rmts: + if remote.url == psm.url: + rmt_for_deletion = remote + break + # END if urls match + # END for each remote + + # if we didn't find a matching remote, but have exactly one, + # we can safely use this one + if rmt_for_deletion is None: + if len(rmts) == 1: + rmt_for_deletion = rmts[0] + else: + # if we have not found any remote with the original url + # we may not have a name. This is a special case, + # and its okay to fail here + # Alternatively we could just generate a unique name and leave all + # existing ones in place + raise InvalidGitRepositoryError("Couldn't find original remote-repo at url %r" % psm.url) + #END handle one single remote + # END handle check we found a remote + + orig_name = rmt_for_deletion.name + smm.delete_remote(rmt_for_deletion) + # NOTE: Currently we leave tags from the deleted remotes + # as well as separate tracking branches in the possibly totally + # changed repository ( someone could have changed the url to + # another project ). At some point, one might want to clean + # it up, but the danger is high to remove stuff the user + # has added explicitly + + # rename the new remote back to what it was + smr.rename(orig_name) + + # early on, we verified that the our current tracking branch + # exists in the remote. Now we have to assure that the + # sha we point to is still contained in the new remote + # tracking branch. + smsha = sm.binsha + found = False + rref = smr.refs[self.branch_name] + for c in rref.commit.traverse(): + if c.binsha == smsha: + found = True + break + # END traverse all commits in search for sha + # END for each commit + + if not found: + # adjust our internal binsha to use the one of the remote + # this way, it will be checked out in the next step + # This will change the submodule relative to us, so + # the user will be able to commit the change easily + print >> sys.stderr, "WARNING: Current sha %s was not contained in the tracking branch at the new remote, setting it the the remote's tracking branch" % sm.hexsha + sm.binsha = rref.commit.binsha + #END reset binsha + + #NOTE: All checkout is performed by the base implementation of update + #END handle dry_run + progress.update(END|URLCHANGE, i, len_csms, prefix+"Done adjusting url of submodule %r" % (sm.name)) # END skip remote handling if new url already exists in module # END handle url @@ -245,34 +253,36 @@ class RootModule(Submodule): if sm.branch_path != psm.branch_path: # finally, create a new tracking branch which tracks the # new remote branch - progress.update(BEGIN|BRANCHCHANGE, i, len_csms, "Changing branch of submodule %s from %s to %s" % (sm.name, psm.branch_path, sm.branch_path)) - smm = sm.module() - smmr = smm.remotes - try: - tbr = git.Head.create(smm, sm.branch_name, logmsg='branch: Created from HEAD') - except OSError: - # ... or reuse the existing one - tbr = git.Head(smm, sm.branch_path) - #END assure tracking branch exists - - tbr.set_tracking_branch(find_first_remote_branch(smmr, sm.branch_name)) - # figure out whether the previous tracking branch contains - # new commits compared to the other one, if not we can - # delete it. - try: - tbr = find_first_remote_branch(smmr, psm.branch_name) - if len(smm.git.cherry(tbr, psm.branch)) == 0: - psm.branch.delete(smm, psm.branch) - #END delete original tracking branch if there are no changes - except InvalidGitRepositoryError: - # ignore it if the previous branch couldn't be found in the - # current remotes, this just means we can't handle it - pass - # END exception handling - - #NOTE: All checkout is done in the base implementation of update + progress.update(BEGIN|BRANCHCHANGE, i, len_csms, prefix+"Changing branch of submodule %r from %s to %s" % (sm.name, psm.branch_path, sm.branch_path)) + if not dry_run: + smm = sm.module() + smmr = smm.remotes + try: + tbr = git.Head.create(smm, sm.branch_name, logmsg='branch: Created from HEAD') + except OSError: + # ... or reuse the existing one + tbr = git.Head(smm, sm.branch_path) + #END assure tracking branch exists + + tbr.set_tracking_branch(find_first_remote_branch(smmr, sm.branch_name)) + # figure out whether the previous tracking branch contains + # new commits compared to the other one, if not we can + # delete it. + try: + tbr = find_first_remote_branch(smmr, psm.branch_name) + if len(smm.git.cherry(tbr, psm.branch)) == 0: + psm.branch.delete(smm, psm.branch) + #END delete original tracking branch if there are no changes + except InvalidGitRepositoryError: + # ignore it if the previous branch couldn't be found in the + # current remotes, this just means we can't handle it + pass + # END exception handling + + #NOTE: All checkout is done in the base implementation of update + #END handle dry_run - progress.update(END|BRANCHCHANGE, i, len_csms, "Done changing branch of submodule %s" % sm.name) + progress.update(END|BRANCHCHANGE, i, len_csms, prefix+"Done changing branch of submodule %r" % sm.name) #END handle branch #END handle # END for each common submodule @@ -289,9 +299,12 @@ class RootModule(Submodule): # or defective depth. The RootSubmodule type will never process itself, # which was done in the previous expression if recursive: - type(self)(sm.module()).update( recursive=True, force_remove=force_remove, - init=init, to_latest_revision=to_latest_revision, - progress=progress, dry_run=dry_run) + # the module would exist by now if we are not in dry_run mode + if sm.module_exists(): + type(self)(sm.module()).update( recursive=True, force_remove=force_remove, + init=init, to_latest_revision=to_latest_revision, + progress=progress, dry_run=dry_run) + #END handle dry_run #END handle recursive # END for each submodule to update -- cgit v1.2.3