diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2015-01-21 08:52:23 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2015-01-21 08:53:14 +0100 |
commit | 47ac37be2e0e14e958ad24dc8cba1fa4b7f78700 (patch) | |
tree | c7544cb324a1141628e281828bc4f9bda295ae5c /git | |
parent | bb0f3d78d6980a1d43f05cb17a8da57a196a34f3 (diff) | |
download | gitpython-47ac37be2e0e14e958ad24dc8cba1fa4b7f78700.tar.gz |
Assured that branch changes are properly handled.
Previously we could try to remove the branch we are on.
Of course, we have a test-case elaborate enough to verify we don't
destroy changes in submodules accidentally. Therefore I am confident
that this implementation is correct.
Fixes #49
Diffstat (limited to 'git')
-rw-r--r-- | git/objects/submodule/base.py | 12 | ||||
-rw-r--r-- | git/objects/submodule/root.py | 25 | ||||
-rw-r--r-- | git/objects/submodule/util.py | 2 | ||||
-rw-r--r-- | git/repo/fun.py | 1 | ||||
-rw-r--r-- | git/test/test_submodule.py | 67 |
5 files changed, 77 insertions, 30 deletions
diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 4b2fc0c0..ebb66495 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -411,21 +411,9 @@ class Submodule(util.IndexObject, Iterable, Traversable): del(writer) # we deliberatly assume that our head matches our index ! - - try: - repo.head.commit - parent_repo_is_empty = False - except ValueError: - parent_repo_is_empty = True - # Can't set this yet, if the parent repo is empty. - # end sm.binsha = mrepo.head.commit.binsha index.add([sm], write=True) - if parent_repo_is_empty: - log.debug("Will not set _parent_commit now as the parent repository has no commit yet.") - # end - return sm def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False, diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index 7042e99c..1c863f6f 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -287,6 +287,11 @@ class RootModule(Submodule): if not dry_run: smm = sm.module() smmr = smm.remotes + # As the branch might not exist yet, we will have to fetch all remotes to be sure ... . + for remote in smmr: + remote.fetch(progress=progress) + # end for each remote + try: tbr = git.Head.create(smm, sm.branch_name, logmsg='branch: Created from HEAD') except OSError: @@ -295,21 +300,11 @@ class RootModule(Submodule): # 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 + # NOTE: All head-resetting is done in the base implementation of update + # but we will have to checkout the new branch here. As it still points to the currently + # checkout out commit, we don't do any harm. + # As we don't want to update working-tree or index, changing the ref is all there is to do + smm.head.reference = tbr # END handle dry_run progress.update( diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 5604dec7..8b9873fc 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -49,7 +49,7 @@ def find_first_remote_branch(remotes, branch_name): continue # END exception handling # END for remote - raise InvalidGitRepositoryError("Didn't find remote branch %r in any of the given remotes", branch_name) + raise InvalidGitRepositoryError("Didn't find remote branch '%r' in any of the given remotes" % branch_name) #} END utilities diff --git a/git/repo/fun.py b/git/repo/fun.py index 2a1270be..2321dbc8 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -26,6 +26,7 @@ __all__ = ('rev_parse', 'is_git_dir', 'touch', 'find_git_dir', 'name_to_object', def touch(filename): fp = open(filename, "ab") fp.close() + return filename def is_git_dir(d): diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index 6a03fe26..49ab2586 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -529,10 +529,9 @@ class TestSubmodule(TestBase): # 'apply work' to the nested submodule and assure this is not removed/altered during updates # Need to commit first, otherwise submodule.update wouldn't have a reason to change the head - nsm_file = os.path.join(nsm.module().working_tree_dir, 'new-file') + touch(os.path.join(nsm.module().working_tree_dir, 'new-file')) # We cannot expect is_dirty to even run as we wouldn't reset a head to the same location assert nsm.module().head.commit.hexsha == nsm.hexsha - touch(nsm_file) nsm.module().index.add([nsm]) nsm.module().index.commit("added new file") rm.update(recursive=False, dry_run=True, progress=prog) # would not change head, and thus doens't fail @@ -778,3 +777,67 @@ class TestSubmodule(TestBase): if os.path.isfile(os.path.join(sm_mod.working_tree_dir, '.git')) == sm._need_gitfile_submodules(parent.git): assert sm_mod.git_dir.endswith(".git/modules/" + new_sm_name) # end + + @with_rw_directory + def test_branch_renames(self, rw_dir): + # Setup initial sandbox: + # parent repo has one submodule, which has all the latest changes + source_url = self._submodule_url() + sm_source_repo = git.Repo.clone_from(source_url, os.path.join(rw_dir, 'sm-source')) + parent_repo = git.Repo.init(os.path.join(rw_dir, 'parent')) + sm = parent_repo.create_submodule('mysubmodule', 'subdir/submodule', + sm_source_repo.working_tree_dir, branch='master') + parent_repo.index.commit('added submodule') + assert sm.exists() + + # Create feature branch with one new commit in submodule source + sm_fb = sm_source_repo.create_head('feature') + sm_fb.checkout() + new_file = touch(os.path.join(sm_source_repo.working_tree_dir, 'new-file')) + sm_source_repo.index.add([new_file]) + sm.repo.index.commit("added new file") + + # change designated submodule checkout branch to the new upstream feature branch + smcw = sm.config_writer() + smcw.set_value('branch', sm_fb.name) + smcw.release() + assert sm.repo.is_dirty(index=True, working_tree=False) + sm.repo.index.commit("changed submodule branch to '%s'" % sm_fb) + + # verify submodule update with feature branch that leaves currently checked out branch in it's past + sm_mod = sm.module() + prev_commit = sm_mod.commit() + assert sm_mod.head.ref.name == 'master' + assert parent_repo.submodule_update() + assert sm_mod.head.ref.name == sm_fb.name + assert sm_mod.commit() == prev_commit, "Without to_latest_revision, we don't change the commit" + + assert parent_repo.submodule_update(to_latest_revision=True) + assert sm_mod.head.ref.name == sm_fb.name + assert sm_mod.commit() == sm_fb.commit + + # Create new branch which is in our past, and thus seemingly unrelated to the currently checked out one + # To make it even 'harder', we shall fork and create a new commit + sm_pfb = sm_source_repo.create_head('past-feature', commit='HEAD~20') + sm_pfb.checkout() + sm_source_repo.index.add([touch(os.path.join(sm_source_repo.working_tree_dir, 'new-file'))]) + sm_source_repo.index.commit("new file added, to past of '%r'" % sm_fb) + + # Change designated submodule checkout branch to a new commit in its own past + smcw = sm.config_writer() + smcw.set_value('branch', sm_pfb.path) + smcw.release() + sm.repo.index.commit("changed submodule branch to '%s'" % sm_pfb) + + # Test submodule updates - must fail if submodule is dirty + touch(os.path.join(sm_mod.working_tree_dir, 'unstaged file')) + # This doesn't fail as our own submodule binsha didn't change, and the reset is only triggered if + # to latest revision is True. + parent_repo.submodule_update(to_latest_revision=False) + sm_mod.head.ref.name == sm_pfb.name, "should have been switched to past head" + sm_mod.commit() == sm_fb.commit, "Head wasn't reset" + + self.failUnlessRaises(RepositoryDirtyError, parent_repo.submodule_update, to_latest_revision=True) + parent_repo.submodule_update(to_latest_revision=True, force_reset=True) + assert sm_mod.commit() == sm_pfb.commit, "Now head should have been reset" + assert sm_mod.head.ref.name == sm_pfb.name |