Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 47ac37b

Browse files
committed
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 gitpython-developers#49
1 parent bb0f3d7 commit 47ac37b

File tree

5 files changed

+77
-30
lines changed

5 files changed

+77
-30
lines changed

git/objects/submodule/base.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -411,21 +411,9 @@ def add(cls, repo, name, path, url=None, branch=None, no_checkout=False):
411411
del(writer)
412412

413413
# we deliberatly assume that our head matches our index !
414-
415-
try:
416-
repo.head.commit
417-
parent_repo_is_empty = False
418-
except ValueError:
419-
parent_repo_is_empty = True
420-
# Can't set this yet, if the parent repo is empty.
421-
# end
422414
sm.binsha = mrepo.head.commit.binsha
423415
index.add([sm], write=True)
424416

425-
if parent_repo_is_empty:
426-
log.debug("Will not set _parent_commit now as the parent repository has no commit yet.")
427-
# end
428-
429417
return sm
430418

431419
def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False,

git/objects/submodule/root.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
287287
if not dry_run:
288288
smm = sm.module()
289289
smmr = smm.remotes
290+
# As the branch might not exist yet, we will have to fetch all remotes to be sure ... .
291+
for remote in smmr:
292+
remote.fetch(progress=progress)
293+
# end for each remote
294+
290295
try:
291296
tbr = git.Head.create(smm, sm.branch_name, logmsg='branch: Created from HEAD')
292297
except OSError:
@@ -295,21 +300,11 @@ def update(self, previous_commit=None, recursive=True, force_remove=False, init=
295300
# END assure tracking branch exists
296301

297302
tbr.set_tracking_branch(find_first_remote_branch(smmr, sm.branch_name))
298-
# figure out whether the previous tracking branch contains
299-
# new commits compared to the other one, if not we can
300-
# delete it.
301-
try:
302-
tbr = find_first_remote_branch(smmr, psm.branch_name)
303-
if len(smm.git.cherry(tbr, psm.branch)) == 0:
304-
psm.branch.delete(smm, psm.branch)
305-
# END delete original tracking branch if there are no changes
306-
except InvalidGitRepositoryError:
307-
# ignore it if the previous branch couldn't be found in the
308-
# current remotes, this just means we can't handle it
309-
pass
310-
# END exception handling
311-
312-
# NOTE: All checkout is done in the base implementation of update
303+
# NOTE: All head-resetting is done in the base implementation of update
304+
# but we will have to checkout the new branch here. As it still points to the currently
305+
# checkout out commit, we don't do any harm.
306+
# As we don't want to update working-tree or index, changing the ref is all there is to do
307+
smm.head.reference = tbr
313308
# END handle dry_run
314309

315310
progress.update(

git/objects/submodule/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def find_first_remote_branch(remotes, branch_name):
4949
continue
5050
# END exception handling
5151
# END for remote
52-
raise InvalidGitRepositoryError("Didn't find remote branch %r in any of the given remotes", branch_name)
52+
raise InvalidGitRepositoryError("Didn't find remote branch '%r' in any of the given remotes" % branch_name)
5353

5454
#} END utilities
5555

git/repo/fun.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
def touch(filename):
2727
fp = open(filename, "ab")
2828
fp.close()
29+
return filename
2930

3031

3132
def is_git_dir(d):

git/test/test_submodule.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,10 +529,9 @@ def test_root_module(self, rwrepo):
529529

530530
# 'apply work' to the nested submodule and assure this is not removed/altered during updates
531531
# Need to commit first, otherwise submodule.update wouldn't have a reason to change the head
532-
nsm_file = os.path.join(nsm.module().working_tree_dir, 'new-file')
532+
touch(os.path.join(nsm.module().working_tree_dir, 'new-file'))
533533
# We cannot expect is_dirty to even run as we wouldn't reset a head to the same location
534534
assert nsm.module().head.commit.hexsha == nsm.hexsha
535-
touch(nsm_file)
536535
nsm.module().index.add([nsm])
537536
nsm.module().index.commit("added new file")
538537
rm.update(recursive=False, dry_run=True, progress=prog) # would not change head, and thus doens't fail
@@ -778,3 +777,67 @@ def test_rename(self, rwdir):
778777
if os.path.isfile(os.path.join(sm_mod.working_tree_dir, '.git')) == sm._need_gitfile_submodules(parent.git):
779778
assert sm_mod.git_dir.endswith(".git/modules/" + new_sm_name)
780779
# end
780+
781+
@with_rw_directory
782+
def test_branch_renames(self, rw_dir):
783+
# Setup initial sandbox:
784+
# parent repo has one submodule, which has all the latest changes
785+
source_url = self._submodule_url()
786+
sm_source_repo = git.Repo.clone_from(source_url, os.path.join(rw_dir, 'sm-source'))
787+
parent_repo = git.Repo.init(os.path.join(rw_dir, 'parent'))
788+
sm = parent_repo.create_submodule('mysubmodule', 'subdir/submodule',
789+
sm_source_repo.working_tree_dir, branch='master')
790+
parent_repo.index.commit('added submodule')
791+
assert sm.exists()
792+
793+
# Create feature branch with one new commit in submodule source
794+
sm_fb = sm_source_repo.create_head('feature')
795+
sm_fb.checkout()
796+
new_file = touch(os.path.join(sm_source_repo.working_tree_dir, 'new-file'))
797+
sm_source_repo.index.add([new_file])
798+
sm.repo.index.commit("added new file")
799+
800+
# change designated submodule checkout branch to the new upstream feature branch
801+
smcw = sm.config_writer()
802+
smcw.set_value('branch', sm_fb.name)
803+
smcw.release()
804+
assert sm.repo.is_dirty(index=True, working_tree=False)
805+
sm.repo.index.commit("changed submodule branch to '%s'" % sm_fb)
806+
807+
# verify submodule update with feature branch that leaves currently checked out branch in it's past
808+
sm_mod = sm.module()
809+
prev_commit = sm_mod.commit()
810+
assert sm_mod.head.ref.name == 'master'
811+
assert parent_repo.submodule_update()
812+
assert sm_mod.head.ref.name == sm_fb.name
813+
assert sm_mod.commit() == prev_commit, "Without to_latest_revision, we don't change the commit"
814+
815+
assert parent_repo.submodule_update(to_latest_revision=True)
816+
assert sm_mod.head.ref.name == sm_fb.name
817+
assert sm_mod.commit() == sm_fb.commit
818+
819+
# Create new branch which is in our past, and thus seemingly unrelated to the currently checked out one
820+
# To make it even 'harder', we shall fork and create a new commit
821+
sm_pfb = sm_source_repo.create_head('past-feature', commit='HEAD~20')
822+
sm_pfb.checkout()
823+
sm_source_repo.index.add([touch(os.path.join(sm_source_repo.working_tree_dir, 'new-file'))])
824+
sm_source_repo.index.commit("new file added, to past of '%r'" % sm_fb)
825+
826+
# Change designated submodule checkout branch to a new commit in its own past
827+
smcw = sm.config_writer()
828+
smcw.set_value('branch', sm_pfb.path)
829+
smcw.release()
830+
sm.repo.index.commit("changed submodule branch to '%s'" % sm_pfb)
831+
832+
# Test submodule updates - must fail if submodule is dirty
833+
touch(os.path.join(sm_mod.working_tree_dir, 'unstaged file'))
834+
# This doesn't fail as our own submodule binsha didn't change, and the reset is only triggered if
835+
# to latest revision is True.
836+
parent_repo.submodule_update(to_latest_revision=False)
837+
sm_mod.head.ref.name == sm_pfb.name, "should have been switched to past head"
838+
sm_mod.commit() == sm_fb.commit, "Head wasn't reset"
839+
840+
self.failUnlessRaises(RepositoryDirtyError, parent_repo.submodule_update, to_latest_revision=True)
841+
parent_repo.submodule_update(to_latest_revision=True, force_reset=True)
842+
assert sm_mod.commit() == sm_pfb.commit, "Now head should have been reset"
843+
assert sm_mod.head.ref.name == sm_pfb.name

0 commit comments

Comments
 (0)