diff options
Diffstat (limited to 'git/test')
| -rw-r--r-- | git/test/test_config.py | 14 | ||||
| -rw-r--r-- | git/test/test_docs.py | 26 | ||||
| -rw-r--r-- | git/test/test_index.py | 3 | ||||
| -rw-r--r-- | git/test/test_repo.py | 6 | ||||
| -rw-r--r-- | git/test/test_submodule.py | 295 |
5 files changed, 307 insertions, 37 deletions
diff --git a/git/test/test_config.py b/git/test/test_config.py index 9a44d9e3..fc2b87b6 100644 --- a/git/test/test_config.py +++ b/git/test/test_config.py @@ -73,6 +73,7 @@ class TestBase(TestCase): assert r_config.has_section(sname) assert r_config.has_option(sname, oname) assert r_config.get(sname, oname) == val + w_config.release() # END for each filename def test_multi_line_config(self): @@ -193,3 +194,16 @@ class TestBase(TestCase): cr = GitConfigParser(fpa, read_only=True) check_test_value(cr, tv) cr.release() + + def test_rename(self): + file_obj = self._to_memcache(fixture_path('git_config')) + cw = GitConfigParser(file_obj, read_only=False, merge_includes=False) + + self.failUnlessRaises(ValueError, cw.rename_section, "doesntexist", "foo") + self.failUnlessRaises(ValueError, cw.rename_section, "core", "include") + + nn = "bee" + assert cw.rename_section('core', nn) is cw + assert not cw.has_section('core') + assert len(cw.items(nn)) == 4 + cw.release() diff --git a/git/test/test_docs.py b/git/test/test_docs.py new file mode 100644 index 00000000..5ebae513 --- /dev/null +++ b/git/test/test_docs.py @@ -0,0 +1,26 @@ +#-*-coding:utf-8-*- +# test_git.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +import os + +import git +from git.test.lib import TestBase +from gitdb.test.lib import with_rw_directory +from git.repo.fun import touch + + +class TestGit(TestBase): + + @with_rw_directory + def test_add_file_and_commit(self, rw_dir): + repo_dir = os.path.join(rw_dir, 'my-new-repo') + file_name = os.path.join(repo_dir, 'new-file') + + r = git.Repo.init(repo_dir) + # This function just creates an empty file ... + touch(file_name) + r.index.add([file_name]) + r.index.commit("initial commit") diff --git a/git/test/test_index.py b/git/test/test_index.py index 2be776cd..0569f40f 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -381,12 +381,13 @@ class TestIndex(TestBase): num_entries = len(index.entries) cur_head = rw_repo.head - uname = "Some Developer" + uname = u"Thomas Müller" umail = "sd@company.com" writer = rw_repo.config_writer() writer.set_value("user", "name", uname) writer.set_value("user", "email", umail) writer.release() + assert writer.get_value("user", "name") == uname # remove all of the files, provide a wild mix of paths, BaseIndexEntries, # IndexEntries diff --git a/git/test/test_repo.py b/git/test/test_repo.py index 226c1d26..c32dbdbf 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -164,6 +164,7 @@ class TestRepo(TestBase): r = Repo.init(path=path, bare=True) assert isinstance(r, Repo) assert r.bare is True + assert not r.has_separate_working_tree() assert os.path.isdir(r.git_dir) self._assert_empty_repo(r) @@ -200,6 +201,7 @@ class TestRepo(TestBase): os.chdir(git_dir_rela) r = Repo.init(bare=False) assert r.bare is False + assert not r.has_separate_working_tree() self._assert_empty_repo(r) finally: @@ -589,8 +591,8 @@ class TestRepo(TestBase): commit = rev_parse(first_rev) assert len(commit.parents) == 0 assert commit.hexsha == first_rev - self.failUnlessRaises(BadObject, rev_parse, first_rev + "~") - self.failUnlessRaises(BadObject, rev_parse, first_rev + "^") + self.failUnlessRaises(BadName, rev_parse, first_rev + "~") + self.failUnlessRaises(BadName, rev_parse, first_rev + "^") # short SHA1 commit2 = rev_parse(first_rev[:20]) diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index 3e3a97d8..49ab2586 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -1,6 +1,5 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import shutil import sys import os @@ -11,13 +10,18 @@ from git.test.lib import ( with_rw_repo ) from gitdb.test.lib import with_rw_directory -from git.exc import InvalidGitRepositoryError +from git.exc import ( + InvalidGitRepositoryError, + RepositoryDirtyError +) from git.objects.submodule.base import Submodule from git.objects.submodule.root import RootModule, RootUpdateProgress from git.util import to_native_path_linux, join_path_native from git.compat import string_types - -from nose import SkipTest +from git.repo.fun import ( + find_git_dir, + touch +) # Change the configuration if possible to prevent the underlying memory manager # to keep file handles open. On windows we get problems as they are not properly @@ -37,15 +41,15 @@ class TestRootProgress(RootUpdateProgress): """Just prints messages, for now without checking the correctness of the states""" - def update(self, op, index, max_count, message=''): - print(message) + def update(self, op, cur_count, max_count, message=''): + print(op, cur_count, max_count, message) prog = TestRootProgress() class TestSubmodule(TestBase): - k_subm_current = "468cad66ff1f80ddaeee4123c24e4d53a032c00d" + k_subm_current = "c15a6e1923a14bc760851913858a3942a4193cdb" k_subm_changed = "394ed7006ee5dc8bddfd132b64001d5dfc0ffdd3" k_no_subm_tag = "0.1.6" @@ -66,7 +70,7 @@ class TestSubmodule(TestBase): assert sm.path == 'git/ext/gitdb' assert sm.path != sm.name # in our case, we have ids there, which don't equal the path - assert sm.url == 'git://github.com/gitpython-developers/gitdb.git' + assert sm.url.endswith('github.com/gitpython-developers/gitdb.git') assert sm.branch_path == 'refs/heads/master' # the default ... assert sm.branch_name == 'master' assert sm.parent_commit == rwrepo.head.commit @@ -183,7 +187,9 @@ class TestSubmodule(TestBase): assert sm.module().head.ref.tracking_branch() is not None # delete the whole directory and re-initialize - shutil.rmtree(sm.abspath) + assert len(sm.children()) != 0 + # shutil.rmtree(sm.abspath) + sm.remove(force=True, configuration=False) assert len(sm.children()) == 0 # dry-run does nothing sm.update(dry_run=True, recursive=False, progress=prog) @@ -225,12 +231,14 @@ class TestSubmodule(TestBase): # END for each repo to reset # dry run does nothing - sm.update(recursive=True, dry_run=True, progress=prog) + self.failUnlessRaises(RepositoryDirtyError, sm.update, recursive=True, dry_run=True, progress=prog) + sm.update(recursive=True, dry_run=True, progress=prog, force=True) for repo in smods: assert repo.head.commit != repo.head.ref.tracking_branch().commit # END for each repo to check - sm.update(recursive=True, to_latest_revision=True) + self.failUnlessRaises(RepositoryDirtyError, sm.update, recursive=True, to_latest_revision=True) + sm.update(recursive=True, to_latest_revision=True, force=True) for repo in smods: assert repo.head.commit == repo.head.ref.tracking_branch().commit # END for each repo to check @@ -276,9 +284,12 @@ class TestSubmodule(TestBase): # enforce the submodule to be checked out at the right spot as well. csm.update() + assert csm.module_exists() + assert csm.exists() + assert os.path.isdir(csm.module().working_tree_dir) # this would work - assert sm.remove(dry_run=True) is sm + assert sm.remove(force=True, dry_run=True) is sm assert sm.module_exists() sm.remove(force=True, dry_run=True) assert sm.module_exists() @@ -290,25 +301,30 @@ class TestSubmodule(TestBase): # forcibly delete the child repository prev_count = len(sm.children()) - assert csm.remove(force=True) is csm + self.failUnlessRaises(ValueError, csm.remove, force=True) + # We removed sm, which removed all submodules. Howver, the instance we have + # still points to the commit prior to that, where it still existed + csm.set_parent_commit(csm.repo.commit(), check=False) assert not csm.exists() assert not csm.module_exists() - assert len(sm.children()) == prev_count - 1 + assert len(sm.children()) == prev_count # now we have a changed index, as configuration was altered. # fix this sm.module().index.reset(working_tree=True) # now delete only the module of the main submodule assert sm.module_exists() - sm.remove(configuration=False) + sm.remove(configuration=False, force=True) assert sm.exists() assert not sm.module_exists() assert sm.config_reader().get_value('url') # delete the rest + sm_path = sm.path sm.remove() assert not sm.exists() assert not sm.module_exists() + self.failUnlessRaises(ValueError, getattr, sm, 'path') assert len(rwrepo.submodules) == 0 @@ -345,11 +361,11 @@ class TestSubmodule(TestBase): # MOVE MODULE ############# - # invalid inptu + # invalid input self.failUnlessRaises(ValueError, nsm.move, 'doesntmatter', module=False, configuration=False) # renaming to the same path does nothing - assert nsm.move(sm.path) is nsm + assert nsm.move(sm_path) is nsm # rename a module nmp = join_path_native("new", "module", "dir") + "/" # new module path @@ -371,8 +387,6 @@ class TestSubmodule(TestBase): assert nsm.path == pmp assert rwrepo.submodules[0].path == pmp - # TODO lowprio: test remaining exceptions ... for now its okay, the code looks right - # REMOVE 'EM ALL ################ # if a submodule's repo has no remotes, it can't be added without an explicit url @@ -390,7 +404,6 @@ class TestSubmodule(TestBase): @with_rw_repo(k_subm_current) def test_base_rw(self, rwrepo): - raise SkipTest("Disabled as long as it fails and submodule support wasn't overhauled") self._do_base_tests(rwrepo) @with_rw_repo(k_subm_current, bare=True) @@ -477,8 +490,8 @@ class TestSubmodule(TestBase): #================ nsmn = "newsubmodule" nsmp = "submrepo" - async_url = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, rsmsp[0], rsmsp[1])) - nsm = Submodule.add(rwrepo, nsmn, nsmp, url=async_url) + subrepo_url = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, rsmsp[0], rsmsp[1])) + nsm = Submodule.add(rwrepo, nsmn, nsmp, url=subrepo_url) csmadded = rwrepo.index.commit("Added submodule").hexsha # make sure we don't keep the repo reference nsm.set_parent_commit(csmadded) assert nsm.module_exists() @@ -504,15 +517,38 @@ class TestSubmodule(TestBase): # an update will remove the module # not in dry_run - rm.update(recursive=False, dry_run=True) + rm.update(recursive=False, dry_run=True, force_remove=True) assert os.path.isdir(smp) - rm.update(recursive=False) + # when removing submodules, we may get new commits as nested submodules are auto-committing changes + # to allow deletions without force, as the index would be dirty otherwise. + # QUESTION: Why does this seem to work in test_git_submodule_compatibility() ? + self.failUnlessRaises(InvalidGitRepositoryError, rm.update, recursive=False, force_remove=False) + rm.update(recursive=False, force_remove=True) assert not os.path.isdir(smp) - # change url - #============= - # to the first repository, this way we have a fast checkout, and a completely different + # '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 + 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 + 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 + # Everything we can do from now on will trigger the 'future' check, so no is_dirty() check will even run + # This would only run if our local branch is in the past and we have uncommitted changes + + prev_commit = nsm.module().head.commit + rm.update(recursive=False, dry_run=False, progress=prog) + assert prev_commit == nsm.module().head.commit, "head shouldn't change, as it is in future of remote branch" + + # this kills the new file + rm.update(recursive=True, progress=prog, force_reset=True) + assert prev_commit != nsm.module().head.commit, "head changed, as the remote url and its commit changed" + + # change url ... + #=============== + # ... to the first repository, this way we have a fast checkout, and a completely different # repository at the different url nsm.set_parent_commit(csmremoved) nsmurl = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, rsmsp[0])) @@ -522,15 +558,17 @@ class TestSubmodule(TestBase): csmpathchange = rwrepo.index.commit("changed url") nsm.set_parent_commit(csmpathchange) + # Now nsm head is in the future of the tracked remote branch prev_commit = nsm.module().head.commit # dry-run does nothing rm.update(recursive=False, dry_run=True, progress=prog) assert nsm.module().remotes.origin.url != nsmurl - rm.update(recursive=False, progress=prog) + rm.update(recursive=False, progress=prog, force_reset=True) assert nsm.module().remotes.origin.url == nsmurl - # head changed, as the remote url and its commit changed - assert prev_commit != nsm.module().head.commit + assert prev_commit != nsm.module().head.commit, "Should now point to gitdb" + assert len(rwrepo.submodules) == 1 + assert not rwrepo.submodules[0].children()[0].module_exists(), "nested submodule should not be checked out" # add the submodule's changed commit to the index, which is what the # user would do @@ -577,7 +615,7 @@ class TestSubmodule(TestBase): # assure we pull locally only nsmc = nsm.children()[0] writer = nsmc.config_writer() - writer.set_value('url', async_url) + writer.set_value('url', subrepo_url) writer.release() rm.update(recursive=True, progress=prog, dry_run=True) # just to run the code rm.update(recursive=True, progress=prog) @@ -602,11 +640,9 @@ class TestSubmodule(TestBase): @with_rw_directory def test_add_empty_repo(self, rwdir): - parent_dir = os.path.join(rwdir, 'parent') - os.mkdir(parent_dir) empty_repo_dir = os.path.join(rwdir, 'empty-repo') - parent = git.Repo.init(parent_dir) + parent = git.Repo.init(os.path.join(rwdir, 'parent')) git.Repo.init(empty_repo_dir) for checkout_mode in range(2): @@ -614,3 +650,194 @@ class TestSubmodule(TestBase): self.failUnlessRaises(ValueError, parent.create_submodule, name, name, url=empty_repo_dir, no_checkout=checkout_mode and True or False) # end for each checkout mode + + def _submodule_url(self): + return os.path.join(self.rorepo.working_tree_dir, 'git/ext/gitdb/gitdb/ext/smmap') + + @with_rw_directory + def test_git_submodules(self, rwdir): + parent = git.Repo.init(os.path.join(rwdir, 'parent')) + parent.git.submodule('add', self._submodule_url(), 'module') + parent.index.commit("added submodule") + + assert len(parent.submodules) == 1 + sm = parent.submodules[0] + + assert sm.exists() and sm.module_exists() + + clone = git.Repo.clone_from(self._submodule_url(), + os.path.join(parent.working_tree_dir, 'existing-subrepository')) + sm2 = parent.create_submodule('nongit-file-submodule', clone.working_tree_dir) + assert len(parent.submodules) == 2 + + for _ in range(2): + for init in (False, True): + sm.update(init=init) + sm2.update(init=init) + # end for each init state + # end for each iteration + + sm.move(sm.path + '_moved') + sm2.move(sm2.path + '_moved') + + @with_rw_directory + def test_git_submodule_compatibility(self, rwdir): + parent = git.Repo.init(os.path.join(rwdir, 'parent')) + sm_path = 'submodules/intermediate/one' + sm = parent.create_submodule('mymodules/myname', sm_path, url=self._submodule_url()) + parent.index.commit("added submodule") + + def assert_exists(sm, value=True): + assert sm.exists() == value + assert sm.module_exists() == value + # end + + # As git is backwards compatible itself, it would still recognize what we do here ... unless we really + # muss it up. That's the only reason why the test is still here ... . + assert len(parent.git.submodule().splitlines()) == 1 + + module_repo_path = os.path.join(sm.module().working_tree_dir, '.git') + assert module_repo_path.startswith(os.path.join(parent.working_tree_dir, sm_path)) + if not sm._need_gitfile_submodules(parent.git): + assert os.path.isdir(module_repo_path) + assert not sm.module().has_separate_working_tree() + else: + assert os.path.isfile(module_repo_path) + assert sm.module().has_separate_working_tree() + assert find_git_dir(module_repo_path) is not None, "module pointed to by .git file must be valid" + # end verify submodule 'style' + + # test move + new_sm_path = 'submodules/one' + sm.move(new_sm_path) + assert_exists(sm) + + # Add additional submodule level + csm = sm.module().create_submodule('nested-submodule', 'nested-submodule/working-tree', + url=self._submodule_url()) + sm.module().index.commit("added nested submodule") + sm_head_commit = sm.module().commit() + assert_exists(csm) + + # Fails because there are new commits, compared to the remote we cloned from + self.failUnlessRaises(InvalidGitRepositoryError, sm.remove, dry_run=True) + assert_exists(sm) + assert sm.module().commit() == sm_head_commit + assert_exists(csm) + + # rename nested submodule + # This name would move itself one level deeper - needs special handling internally + new_name = csm.name + '/mine' + assert csm.rename(new_name).name == new_name + assert_exists(csm) + assert csm.repo.is_dirty(index=True, working_tree=False), "index must contain changed .gitmodules file" + csm.repo.index.commit("renamed module") + + # keep_going evaluation + rsm = parent.submodule_update() + assert_exists(sm) + assert_exists(csm) + csm_writer = csm.config_writer().set_value('url', 'bar') + csm_writer.release() + csm.repo.index.commit("Have to commit submodule change for algorithm to pick it up") + assert csm.url == 'bar' + + self.failUnlessRaises(Exception, rsm.update, recursive=True, to_latest_revision=True, progress=prog) + assert_exists(csm) + rsm.update(recursive=True, to_latest_revision=True, progress=prog, keep_going=True) + + # remove + sm_module_path = sm.module().git_dir + + for dry_run in (True, False): + sm.remove(dry_run=dry_run, force=True) + assert_exists(sm, value=dry_run) + assert os.path.isdir(sm_module_path) == dry_run + # end for each dry-run mode + + @with_rw_directory + def test_rename(self, rwdir): + parent = git.Repo.init(os.path.join(rwdir, 'parent')) + sm_name = 'mymodules/myname' + sm = parent.create_submodule(sm_name, sm_name, url=self._submodule_url()) + parent.index.commit("Added submodule") + + assert sm.rename(sm_name) is sm and sm.name == sm_name + assert not sm.repo.is_dirty(index=True, working_tree=False, untracked_files=False) + + new_path = 'renamed/myname' + assert sm.move(new_path).name == new_path + + new_sm_name = "shortname" + assert sm.rename(new_sm_name) is sm + assert sm.repo.is_dirty(index=True, working_tree=False, untracked_files=False) + assert sm.exists() + + sm_mod = sm.module() + 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 |
