aboutsummaryrefslogtreecommitdiff
path: root/git/test
diff options
context:
space:
mode:
Diffstat (limited to 'git/test')
-rw-r--r--git/test/test_config.py14
-rw-r--r--git/test/test_docs.py26
-rw-r--r--git/test/test_index.py3
-rw-r--r--git/test/test_repo.py6
-rw-r--r--git/test/test_submodule.py295
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