Exemple #1
0
    def test_git_work_tree_dotgit(self, rw_dir):
        """Check that we find .git as a worktree file and find the worktree
        based on it."""
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        branch = rw_master.create_head('aaaaaaaa')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        if Git.is_cygwin():
            worktree_path = cygpath(worktree_path)
        rw_master.git.worktree('add', worktree_path, branch.name)

        # this ensures that we can read the repo's gitdir correctly
        repo = Repo(worktree_path)
        self.assertIsInstance(repo, Repo)

        # this ensures we're able to actually read the refs in the tree, which
        # means we can read commondir correctly.
        commit = repo.head.commit
        self.assertIsInstance(commit, Object)

        # this ensures we can read the remotes, which confirms we're reading
        # the config correctly.
        origin = repo.remotes.origin
        self.assertIsInstance(origin, Remote)

        self.assertIsInstance(repo.heads['aaaaaaaa'], Head)
    def test_git_work_tree_dotgit(self, rw_dir):
        """Check that we find .git as a worktree file and find the worktree
        based on it."""
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        branch = rw_master.create_head('aaaaaaaa')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        if Git.is_cygwin():
            worktree_path = cygpath(worktree_path)
        rw_master.git.worktree('add', worktree_path, branch.name)

        # this ensures that we can read the repo's gitdir correctly
        repo = Repo(worktree_path)
        self.assertIsInstance(repo, Repo)

        # this ensures we're able to actually read the refs in the tree, which
        # means we can read commondir correctly.
        commit = repo.head.commit
        self.assertIsInstance(commit, Object)

        # this ensures we can read the remotes, which confirms we're reading
        # the config correctly.
        origin = repo.remotes.origin
        self.assertIsInstance(origin, Remote)

        self.assertIsInstance(repo.heads['aaaaaaaa'], Head)
Exemple #3
0
    def test_untracked_files(self):
        base = self.rorepo.working_tree_dir
        files = (join_path_native(base, "__test_myfile"),
                 join_path_native(base, "__test_other_file"))
        num_recently_untracked = 0
        try:
            for fpath in files:
                fd = open(fpath, "wb")
                fd.close()
            # END for each filename
            untracked_files = self.rorepo.untracked_files
            num_recently_untracked = len(untracked_files)

            # assure we have all names - they are relative to the git-dir
            num_test_untracked = 0
            for utfile in untracked_files:
                num_test_untracked += join_path_native(base, utfile) in files
            assert len(files) == num_test_untracked
        finally:
            for fpath in files:
                if os.path.isfile(fpath):
                    os.remove(fpath)
        # END handle files

        assert len(self.rorepo.untracked_files) == (num_recently_untracked -
                                                    len(files))
Exemple #4
0
    def test_git_work_tree_env(self, rw_dir):
        """Check that we yield to GIT_WORK_TREE"""
        # clone a repo
        # move .git directory to a subdirectory
        # set GIT_DIR and GIT_WORK_TREE appropriately
        # check that repo.working_tree_dir == rw_dir
        self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))

        repo_dir = join_path_native(rw_dir, 'master_repo')
        old_git_dir = join_path_native(repo_dir, '.git')
        new_subdir = join_path_native(repo_dir, 'gitdir')
        new_git_dir = join_path_native(new_subdir, 'git')
        os.mkdir(new_subdir)
        os.rename(old_git_dir, new_git_dir)

        oldenv = os.environ.copy()
        os.environ['GIT_DIR'] = new_git_dir
        os.environ['GIT_WORK_TREE'] = repo_dir

        try:
            r = Repo()
            self.assertEqual(r.working_tree_dir, repo_dir)
            self.assertEqual(r.working_dir, repo_dir)
        finally:
            os.environ = oldenv
Exemple #5
0
    def test_git_work_tree_env(self, rw_dir):
        """Check that we yield to GIT_WORK_TREE"""
        # clone a repo
        # move .git directory to a subdirectory
        # set GIT_DIR and GIT_WORK_TREE appropriately
        # check that repo.working_tree_dir == rw_dir
        self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))

        repo_dir = join_path_native(rw_dir, 'master_repo')
        old_git_dir = join_path_native(repo_dir, '.git')
        new_subdir = join_path_native(repo_dir, 'gitdir')
        new_git_dir = join_path_native(new_subdir, 'git')
        os.mkdir(new_subdir)
        os.rename(old_git_dir, new_git_dir)

        oldenv = os.environ.copy()
        os.environ['GIT_DIR'] = new_git_dir
        os.environ['GIT_WORK_TREE'] = repo_dir

        try:
            r = Repo()
            self.assertEqual(r.working_tree_dir, repo_dir)
            self.assertEqual(r.working_dir, repo_dir)
        finally:
            os.environ = oldenv
Exemple #6
0
    def test_untracked_files(self, rwrepo):
        for run, (repo_add, is_invoking_git) in enumerate((
                (rwrepo.index.add, False),
                (rwrepo.git.add, True),
        )):
            base = rwrepo.working_tree_dir
            files = (join_path_native(base, u"%i_test _myfile" % run),
                     join_path_native(base, "%i_test_other_file" % run),
                     join_path_native(base, u"%i__çava verböten" % run),
                     join_path_native(base, u"%i_çava-----verböten" % run))

            num_recently_untracked = 0
            for fpath in files:
                with open(fpath, "wb"):
                    pass
            untracked_files = rwrepo.untracked_files
            num_recently_untracked = len(untracked_files)

            # assure we have all names - they are relative to the git-dir
            num_test_untracked = 0
            for utfile in untracked_files:
                num_test_untracked += join_path_native(base, utfile) in files
            self.assertEqual(len(files), num_test_untracked)

            if is_win and not PY3 and is_invoking_git:
                ## On Windows, shell needed when passing unicode cmd-args.
                #
                repo_add = fnt.partial(repo_add, shell=True)
                untracked_files = [win_encode(f) for f in untracked_files]
            repo_add(untracked_files)
            self.assertEqual(len(rwrepo.untracked_files), (num_recently_untracked - len(files)))
Exemple #7
0
    def test_untracked_files(self, rwrepo):
        for (run, repo_add) in enumerate((rwrepo.index.add, rwrepo.git.add)):
            base = rwrepo.working_tree_dir
            files = (join_path_native(base, u"%i_test _myfile" % run),
                     join_path_native(base, "%i_test_other_file" % run),
                     join_path_native(base, u"%i__çava verböten" % run),
                     join_path_native(base, u"%i_çava-----verböten" % run))

            num_recently_untracked = 0
            for fpath in files:
                fd = open(fpath, "wb")
                fd.close()
            # END for each filename
            untracked_files = rwrepo.untracked_files
            num_recently_untracked = len(untracked_files)

            # assure we have all names - they are relative to the git-dir
            num_test_untracked = 0
            for utfile in untracked_files:
                num_test_untracked += join_path_native(base, utfile) in files
            assert len(files) == num_test_untracked

            repo_add(untracked_files)
            assert len(rwrepo.untracked_files) == (num_recently_untracked -
                                                   len(files))
Exemple #8
0
    def test_untracked_files(self, rwrepo):
        for (run, repo_add) in enumerate((rwrepo.index.add, rwrepo.git.add)):
            base = rwrepo.working_tree_dir
            files = (
                join_path_native(base, u"%i_test _myfile" % run),
                join_path_native(base, "%i_test_other_file" % run),
                join_path_native(base, u"%i__çava verböten" % run),
                join_path_native(base, u"%i_çava-----verböten" % run),
            )

            num_recently_untracked = 0
            for fpath in files:
                fd = open(fpath, "wb")
                fd.close()
            # END for each filename
            untracked_files = rwrepo.untracked_files
            num_recently_untracked = len(untracked_files)

            # assure we have all names - they are relative to the git-dir
            num_test_untracked = 0
            for utfile in untracked_files:
                num_test_untracked += join_path_native(base, utfile) in files
            assert len(files) == num_test_untracked

            repo_add(untracked_files)
            assert len(rwrepo.untracked_files) == (num_recently_untracked - len(files))
	def test_untracked_files(self):
		base = self.rorepo.working_tree_dir
		files = (	join_path_native(base, "__test_myfile"), 
					join_path_native(base, "__test_other_file") )
		num_recently_untracked = 0
		try:
			for fpath in files:
				fd = open(fpath,"wb")
				fd.close()
			# END for each filename
			untracked_files = self.rorepo.untracked_files
			num_recently_untracked = len(untracked_files)
			
			# assure we have all names - they are relative to the git-dir
			num_test_untracked = 0
			for utfile in untracked_files:
				num_test_untracked += join_path_native(base, utfile) in files
			assert len(files) == num_test_untracked
		finally:
			for fpath in files:
				if os.path.isfile(fpath):
					os.remove(fpath)
		# END handle files 
		
		assert len(self.rorepo.untracked_files) == (num_recently_untracked - len(files))
Exemple #10
0
    def test_untracked_files(self, rwrepo):
        for run, (repo_add, is_invoking_git) in enumerate((
            (rwrepo.index.add, False),
            (rwrepo.git.add, True),
        )):
            base = rwrepo.working_tree_dir
            files = (join_path_native(base, u"%i_test _myfile" % run),
                     join_path_native(base, "%i_test_other_file" % run),
                     join_path_native(base, u"%i__çava verböten" % run),
                     join_path_native(base, u"%i_çava-----verböten" % run))

            num_recently_untracked = 0
            for fpath in files:
                with open(fpath, "wb"):
                    pass
            untracked_files = rwrepo.untracked_files
            num_recently_untracked = len(untracked_files)

            # assure we have all names - they are relative to the git-dir
            num_test_untracked = 0
            for utfile in untracked_files:
                num_test_untracked += join_path_native(base, utfile) in files
            self.assertEqual(len(files), num_test_untracked)

            if is_win and not PY3 and is_invoking_git:
                ## On Windows, shell needed when passing unicode cmd-args.
                #
                repo_add = fnt.partial(repo_add, shell=True)
                untracked_files = [win_encode(f) for f in untracked_files]
            repo_add(untracked_files)
            self.assertEqual(len(rwrepo.untracked_files),
                             (num_recently_untracked - len(files)))
    def test_work_tree_unsupported(self, rw_dir):
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        rw_master.git.checkout('HEAD~10')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        rw_master.git.worktree('add', worktree_path, 'master')

        self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path)
Exemple #12
0
    def test_work_tree_unsupported(self, rw_dir):
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        rw_master.git.checkout('HEAD~10')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        rw_master.git.worktree('add', worktree_path, 'master')

        self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path)
    def test_git_file(self, rwrepo):
        # Move the .git directory to another location and create the .git file.
        real_path_abs = os.path.abspath(join_path_native(rwrepo.working_tree_dir, '.real'))
        os.rename(rwrepo.git_dir, real_path_abs)
        git_file_path = join_path_native(rwrepo.working_tree_dir, '.git')
        open(git_file_path, 'wb').write(fixture('git_file'))

        # Create a repo and make sure it's pointing to the relocated .git directory.
        git_file_repo = Repo(rwrepo.working_tree_dir)
        assert os.path.abspath(git_file_repo.git_dir) == real_path_abs

        # Test using an absolute gitdir path in the .git file.
        open(git_file_path, 'wb').write(('gitdir: %s\n' % real_path_abs).encode('ascii'))
        git_file_repo = Repo(rwrepo.working_tree_dir)
        assert os.path.abspath(git_file_repo.git_dir) == real_path_abs
Exemple #14
0
    def test_git_file(self, rwrepo):
        # Move the .git directory to another location and create the .git file.
        real_path_abs = os.path.abspath(join_path_native(rwrepo.working_tree_dir, '.real'))
        os.rename(rwrepo.git_dir, real_path_abs)
        git_file_path = join_path_native(rwrepo.working_tree_dir, '.git')
        open(git_file_path, 'wb').write(fixture('git_file'))

        # Create a repo and make sure it's pointing to the relocated .git directory.
        git_file_repo = Repo(rwrepo.working_tree_dir)
        assert os.path.abspath(git_file_repo.git_dir) == real_path_abs

        # Test using an absolute gitdir path in the .git file.
        open(git_file_path, 'wb').write(('gitdir: %s\n' % real_path_abs).encode('ascii'))
        git_file_repo = Repo(rwrepo.working_tree_dir)
        assert os.path.abspath(git_file_repo.git_dir) == real_path_abs
    def _iter_items(cls, repo, common_path=None):
        if common_path is None:
            common_path = cls._common_path_default
        rela_paths = set()

        # walk loose refs
        # Currently we do not follow links
        for root, dirs, files in os.walk(join_path_native(repo.common_dir, common_path)):
            if 'refs' not in root.split(os.sep):  # skip non-refs subfolders
                refs_id = [d for d in dirs if d == 'refs']
                if refs_id:
                    dirs[0:] = ['refs']
            # END prune non-refs folders

            for f in files:
                if f == 'packed-refs':
                    continue
                abs_path = to_native_path_linux(join_path(root, f))
                rela_paths.add(abs_path.replace(to_native_path_linux(repo.common_dir) + '/', ""))
            # END for each file in root directory
        # END for each directory to walk

        # read packed refs
        for _sha, rela_path in cls._iter_packed_refs(repo):
            if rela_path.startswith(common_path):
                rela_paths.add(rela_path)
            # END relative path matches common path
        # END packed refs reading

        # return paths in sorted order
        for path in sorted(rela_paths):
            try:
                yield cls.from_path(repo, path)
            except ValueError:
                continue
Exemple #16
0
    def test_rename(self, rwdir):
        parent = git.Repo.init(osp.join(rwdir, 'parent'))
        sm_name = 'mymodules/myname'
        sm = parent.create_submodule(sm_name,
                                     sm_name,
                                     url=self._small_repo_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 osp.isfile(osp.join(sm_mod.working_tree_dir,
                               '.git')) == sm._need_gitfile_submodules(
                                   parent.git):
            assert sm_mod.git_dir.endswith(
                join_path_native('.git', 'modules', new_sm_name))
Exemple #17
0
    def _iter_items(cls, repo, common_path=None):
        if common_path is None:
            common_path = cls._common_path_default
        rela_paths = set()

        # walk loose refs
        # Currently we do not follow links
        for root, dirs, files in os.walk(join_path_native(repo.git_dir, common_path)):
            if 'refs/' not in root:  # skip non-refs subfolders
                refs_id = [d for d in dirs if d == 'refs']
                if refs_id:
                    dirs[0:] = ['refs']
            # END prune non-refs folders

            for f in files:
                abs_path = to_native_path_linux(join_path(root, f))
                rela_paths.add(abs_path.replace(to_native_path_linux(repo.git_dir) + '/', ""))
            # END for each file in root directory
        # END for each directory to walk

        # read packed refs
        for sha, rela_path in cls._iter_packed_refs(repo):
            if rela_path.startswith(common_path):
                rela_paths.add(rela_path)
            # END relative path matches common path
        # END packed refs reading

        # return paths in sorted order
        for path in sorted(rela_paths):
            try:
                yield cls.from_path(repo, path)
            except ValueError:
                continue
Exemple #18
0
    def test_work_tree_unsupported(self, rw_dir):
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        rw_master.git.checkout('HEAD~10')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        if Git.is_cygwin():
            worktree_path = cygpath(worktree_path)
        try:
            rw_master.git.worktree('add', worktree_path, 'master')
        except Exception as ex:
            raise AssertionError(ex, "It's ok if TC not running from `master`.")

        self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path)
Exemple #19
0
    def test_work_tree_unsupported(self, rw_dir):
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        rw_master.git.checkout('HEAD~10')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        if Git.is_cygwin():
            worktree_path = cygpath(worktree_path)
        try:
            rw_master.git.worktree('add', worktree_path, 'master')
        except Exception as ex:
            raise AssertionError(ex, "It's ok if TC not running from `master`.")

        self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path)
Exemple #20
0
    def abspath(self):
        """
        :return:
            Absolute path to this index object in the file system ( as opposed to the
            .path field which is a path relative to the git repository ).

            The returned path will be native to the system and contains '\' on windows. """
        return join_path_native(self.repo.working_tree_dir, self.path)
Exemple #21
0
    def abspath(self):
        """
        :return:
            Absolute path to this index object in the file system ( as opposed to the
            .path field which is a path relative to the git repository ).

            The returned path will be native to the system and contains '\' on windows. """
        return join_path_native(self.repo.working_tree_dir, self.path)
Exemple #22
0
    def test_submodule_update(self, rwrepo):
        # fails in bare mode
        rwrepo._bare = True
        self.failUnlessRaises(InvalidGitRepositoryError, rwrepo.submodule_update)
        rwrepo._bare = False

        # test create submodule
        sm = rwrepo.submodules[0]
        sm = rwrepo.create_submodule("my_new_sub", "some_path", join_path_native(self.rorepo.working_tree_dir, sm.path))
        assert isinstance(sm, Submodule)
Exemple #23
0
    def test_submodule_update(self, rwrepo):
        # fails in bare mode
        rwrepo._bare = True
        self.failUnlessRaises(InvalidGitRepositoryError, rwrepo.submodule_update)
        rwrepo._bare = False

        # test create submodule
        sm = rwrepo.submodules[0]
        sm = rwrepo.create_submodule("my_new_sub", "some_path", join_path_native(self.rorepo.working_tree_dir, sm.path))
        self.assertIsInstance(sm, Submodule)
Exemple #24
0
    def abspath(self) -> PathLike:
        """
        :return:
            Absolute path to this index object in the file system ( as opposed to the
            .path field which is a path relative to the git repository ).

            The returned path will be native to the system and contains '\' on windows. """
        if self.repo.working_tree_dir is not None:
            return join_path_native(self.repo.working_tree_dir, self.path)
        else:
            raise WorkTreeRepositoryUnsupported("Working_tree_dir was None or empty")
Exemple #25
0
    def test_linked_worktree_traversal(self, rw_dir):
        """Check that we can identify a linked worktree based on a .git file"""
        git = Git(rw_dir)
        if git.version_info[:3] < (2, 5, 1):
            raise SkipTest("worktree feature unsupported")

        rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
        branch = rw_master.create_head('aaaaaaaa')
        worktree_path = join_path_native(rw_dir, 'worktree_repo')
        if Git.is_cygwin():
            worktree_path = cygpath(worktree_path)
        rw_master.git.worktree('add', worktree_path, branch.name)

        dotgit = osp.join(worktree_path, ".git")
        statbuf = stat(dotgit)
        self.assertTrue(statbuf.st_mode & S_IFREG)

        gitdir = find_worktree_git_dir(dotgit)
        self.assertIsNotNone(gitdir)
        statbuf = stat(gitdir)
        self.assertTrue(statbuf.st_mode & S_IFDIR)
Exemple #26
0
    def test_untracked_files(self, rwrepo):
        for run, repo_add in enumerate((rwrepo.index.add, rwrepo.git.add)):
            base = rwrepo.working_tree_dir
            files = (join_path_native(base, u"%i_test _myfile" % run),
                     join_path_native(base, "%i_test_other_file" % run),
                     join_path_native(base, u"%i__çava verböten" % run),
                     join_path_native(base, u"%i_çava-----verböten" % run))

            num_recently_untracked = 0
            for fpath in files:
                with open(fpath, "wb"):
                    pass
            untracked_files = rwrepo.untracked_files
            num_recently_untracked = len(untracked_files)

            # assure we have all names - they are relative to the git-dir
            num_test_untracked = 0
            for utfile in untracked_files:
                num_test_untracked += join_path_native(base, utfile) in files
            self.assertEqual(len(files), num_test_untracked)

            repo_add(untracked_files)
            self.assertEqual(len(rwrepo.untracked_files), (num_recently_untracked - len(files)))
Exemple #27
0
	def test_submodule_update(self, rwrepo):
		# fails in bare mode
		rwrepo._bare = True
		# special handling: there are repo implementations which have a bare attribute. IN that case, set it directly
		if not rwrepo.bare:
			rwrepo.bare = True
		self.failUnlessRaises(InvalidGitRepositoryError, rwrepo.submodule_update)
		rwrepo._bare = False
		if rwrepo.bare:
			rwrepo.bare = False
		#END special repo handling

		# test create submodule
		sm = rwrepo.submodules[0]
		sm = rwrepo.create_submodule("my_new_sub", "some_path", join_path_native(self.rorepo.working_tree_dir, sm.path))
		assert isinstance(sm, Submodule)
Exemple #28
0
    def test_submodule_update(self, rwrepo):
        # fails in bare mode
        rwrepo._bare = True
        # special handling: there are repo implementations which have a bare attribute. IN that case, set it directly
        if not rwrepo.bare:
            rwrepo.bare = True
        self.failUnlessRaises(InvalidGitRepositoryError, rwrepo.submodule_update)
        rwrepo._bare = False
        if rwrepo.bare:
            rwrepo.bare = False
        # END special repo handling

        # test create submodule
        sm = rwrepo.submodules[0]
        sm = rwrepo.create_submodule("my_new_sub", "some_path", join_path_native(self.rorepo.working_tree_dir, sm.path))
        assert isinstance(sm, Submodule)
Exemple #29
0
	def abspath(self):
		"""
		:return:
			Absolute path to this index object in the file system ( as opposed to the 
			.path field which is a path relative to the git repository ).
			
			The returned path will be native to the system and contains '\' on windows.
		:raise UnsupportedOperation: if underlying odb does not support the required method to obtain a working dir"""
		# TODO: Here we suddenly need something better than a plain object database
		# which indicates our odb should better be named repo !
		root = ''
		if isinstance(self.odb, RepositoryPathsMixin):
			root = self.odb.working_tree_dir
		else:
			raise UnsupportedOperation("Cannot provide absolute path from a database without Repository path support")
		#END handle odb type
		return join_path_native(root, self.path)
Exemple #30
0
    def abspath(self):
        """
        :return:
            Absolute path to this index object in the file system ( as opposed to the 
            .path field which is a path relative to the git repository ).

            The returned path will be native to the system and contains '\' on windows.
        :raise UnsupportedOperation: if underlying odb does not support the required method to obtain a working dir"""
        # TODO: Here we suddenly need something better than a plain object database
        # which indicates our odb should better be named repo !
        root = ''
        if isinstance(self.odb, RepositoryPathsMixin):
            root = self.odb.working_tree_dir
        else:
            raise UnsupportedOperation(
                "Cannot provide absolute path from a database without Repository path support"
            )
        # END handle odb type
        return join_path_native(root, self.path)
    def test_rename(self, rwdir):
        parent = git.Repo.init(osp.join(rwdir, 'parent'))
        sm_name = 'mymodules/myname'
        sm = parent.create_submodule(sm_name, sm_name, url=self._small_repo_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 osp.isfile(osp.join(sm_mod.working_tree_dir, '.git')) == sm._need_gitfile_submodules(parent.git):
            assert sm_mod.git_dir.endswith(join_path_native('.git', 'modules', new_sm_name))
Exemple #32
0
 def _generate_async_local_path(self):
     return to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, 'git/ext/async'))
Exemple #33
0
 def _index_path(self):
     return join_path_native(self.repo.git_dir, "index")
Exemple #34
0
    def from_tree(cls, repo, *treeish, **kwargs):
        """Merge the given treeish revisions into a new index which is returned.
        The original index will remain unaltered

        :param repo:
            The repository treeish are located in.

        :param treeish:
            One, two or three Tree Objects, Commits or 40 byte hexshas. The result
            changes according to the amount of trees.
            If 1 Tree is given, it will just be read into a new index
            If 2 Trees are given, they will be merged into a new index using a
             two way merge algorithm. Tree 1 is the 'current' tree, tree 2 is the 'other'
             one. It behaves like a fast-forward.
             If 3 Trees are given, a 3-way merge will be performed with the first tree
             being the common ancestor of tree 2 and tree 3. Tree 2 is the 'current' tree,
             tree 3 is the 'other' one

        :param kwargs:
            Additional arguments passed to git-read-tree

        :return:
            New IndexFile instance. It will point to a temporary index location which
            does not exist anymore. If you intend to write such a merged Index, supply
            an alternate file_path to its 'write' method.

        :note:
            In the three-way merge case, --aggressive will be specified to automatically
            resolve more cases in a commonly correct manner. Specify trivial=True as kwarg
            to override that.

            As the underlying git-read-tree command takes into account the current index,
            it will be temporarily moved out of the way to assure there are no unsuspected
            interferences."""
        if len(treeish) == 0 or len(treeish) > 3:
            raise ValueError("Please specify between 1 and 3 treeish, got %i" %
                             len(treeish))

        arg_list = list()
        # ignore that working tree and index possibly are out of date
        if len(treeish) > 1:
            # drop unmerged entries when reading our index and merging
            arg_list.append("--reset")
            # handle non-trivial cases the way a real merge does
            arg_list.append("--aggressive")
        # END merge handling

        # tmp file created in git home directory to be sure renaming
        # works - /tmp/ dirs could be on another device
        tmp_index = tempfile.mktemp('', '', repo.git_dir)
        arg_list.append("--index-output=%s" % tmp_index)
        arg_list.extend(treeish)

        # move current index out of the way - otherwise the merge may fail
        # as it considers existing entries. moving it essentially clears the index.
        # Unfortunately there is no 'soft' way to do it.
        # The TemporaryFileSwap assure the original file get put back
        index_handler = TemporaryFileSwap(
            join_path_native(repo.git_dir, 'index'))
        try:
            repo.git.read_tree(*arg_list, **kwargs)
            index = cls(repo, tmp_index)
            index.entries  # force it to read the file as we will delete the temp-file
            del (index_handler)  # release as soon as possible
        finally:
            if os.path.exists(tmp_index):
                os.remove(tmp_index)
        # END index merge handling

        return index
Exemple #35
0
 def _generate_async_local_path(self):
     return to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, "git/ext/async"))
Exemple #36
0
 def abspath(self):
     return join_path_native(self.repo.git_dir, self.path)
Exemple #37
0
    def move(self, module_path, configuration=True, module=True):
        """Move the submodule to a another module path. This involves physically moving
        the repository at our current path, changing the configuration, as well as
        adjusting our index entry accordingly.

        :param module_path: the path to which to move our module, given as
            repository-relative path. Intermediate directories will be created
            accordingly. If the path already exists, it must be empty.
            Trailling (back)slashes are removed automatically
        :param configuration: if True, the configuration will be adjusted to let
            the submodule point to the given path.
        :param module: if True, the repository managed by this submodule
            will be moved, not the configuration. This will effectively
            leave your repository in an inconsistent state unless the configuration
            and index already point to the target location.
        :return: self
        :raise ValueError: if the module path existed and was not empty, or was a file
        :note: Currently the method is not atomic, and it could leave the repository
            in an inconsistent state if a sub-step fails for some reason
        """
        if module + configuration < 1:
            raise ValueError(
                "You must specify to move at least the module or the configuration of the submodule"
            )
        #END handle input

        module_path = to_native_path_linux(module_path)
        if module_path.endswith('/'):
            module_path = module_path[:-1]
        # END handle trailing slash

        # VERIFY DESTINATION
        if module_path == self.path:
            return self
        #END handle no change

        dest_path = join_path_native(self.repo.working_tree_dir, module_path)
        if os.path.isfile(dest_path):
            raise ValueError("Cannot move repository onto a file: %s" %
                             dest_path)
        # END handle target files

        index = self.repo.index
        tekey = index.entry_key(module_path, 0)
        # if the target item already exists, fail
        if configuration and tekey in index.entries:
            raise ValueError("Index entry for target path did alredy exist")
        #END handle index key already there

        # remove existing destination
        if module:
            if os.path.exists(dest_path):
                if len(os.listdir(dest_path)):
                    raise ValueError(
                        "Destination module directory was not empty")
                #END handle non-emptyness

                if os.path.islink(dest_path):
                    os.remove(dest_path)
                else:
                    os.rmdir(dest_path)
                #END handle link
            else:
                # recreate parent directories
                # NOTE: renames() does that now
                pass
            #END handle existance
        # END handle module

        # move the module into place if possible
        cur_path = self.abspath
        renamed_module = False
        if module and os.path.exists(cur_path):
            os.renames(cur_path, dest_path)
            renamed_module = True
        #END move physical module

        # rename the index entry - have to manipulate the index directly as
        # git-mv cannot be used on submodules ... yeah
        try:
            if configuration:
                try:
                    ekey = index.entry_key(self.path, 0)
                    entry = index.entries[ekey]
                    del (index.entries[ekey])
                    nentry = git.IndexEntry(entry[:3] + (module_path, ) +
                                            entry[4:])
                    index.entries[tekey] = nentry
                except KeyError:
                    raise InvalidGitRepositoryError(
                        "Submodule's entry at %r did not exist" % (self.path))
                #END handle submodule doesn't exist

                # update configuration
                writer = self.config_writer(index=index)  # auto-write
                writer.set_value('path', module_path)
                self.path = module_path
                del (writer)
            # END handle configuration flag
        except Exception:
            if renamed_module:
                os.renames(dest_path, cur_path)
            # END undo module renaming
            raise
        #END handle undo rename

        return self
 def abspath(self):
     return join_path_native(_git_dir(self.repo, self.path), self.path)
Exemple #39
0
    def test_root_module(self, rwrepo):
        # Can query everything without problems
        rm = RootModule(self.rorepo)
        assert rm.module() is self.rorepo

        # try attributes
        rm.binsha
        rm.mode
        rm.path
        assert rm.name == rm.k_root_name
        assert rm.parent_commit == self.rorepo.head.commit
        rm.url
        rm.branch

        assert len(rm.list_items(rm.module())) == 1
        rm.config_reader()
        rm.config_writer()

        # deep traversal gitdb / async
        rsmsp = [sm.path for sm in rm.traverse()]
        assert len(rsmsp) == 2  # gitdb and async, async being a child of gitdb

        # cannot set the parent commit as root module's path didn't exist
        self.failUnlessRaises(ValueError, rm.set_parent_commit, 'HEAD')

        # TEST UPDATE
        #############
        # setup commit which remove existing, add new and modify existing submodules
        rm = RootModule(rwrepo)
        assert len(rm.children()) == 1

        # modify path without modifying the index entry
        # ( which is what the move method would do properly )
        #==================================================
        sm = rm.children()[0]
        pp = "path/prefix"
        fp = join_path_native(pp, sm.path)
        prep = sm.path
        assert not sm.module_exists()  # was never updated after rwrepo's clone

        # assure we clone from a local source
        sm.config_writer().set_value(
            'url',
            to_native_path_linux(
                join_path_native(self.rorepo.working_tree_dir, sm.path)))

        # dry-run does nothing
        sm.update(recursive=False, dry_run=True, progress=prog)
        assert not sm.module_exists()

        sm.update(recursive=False)
        assert sm.module_exists()
        sm.config_writer().set_value(
            'path',
            fp)  # change path to something with prefix AFTER url change

        # update fails as list_items in such a situations cannot work, as it cannot
        # find the entry at the changed path
        self.failUnlessRaises(InvalidGitRepositoryError,
                              rm.update,
                              recursive=False)

        # move it properly - doesn't work as it its path currently points to an indexentry
        # which doesn't exist ( move it to some path, it doesn't matter here )
        self.failUnlessRaises(InvalidGitRepositoryError, sm.move, pp)
        # reset the path(cache) to where it was, now it works
        sm.path = prep
        sm.move(fp, module=False)  # leave it at the old location

        assert not sm.module_exists()
        cpathchange = rwrepo.index.commit(
            "changed sm path")  # finally we can commit

        # update puts the module into place
        rm.update(recursive=False, progress=prog)
        sm.set_parent_commit(cpathchange)
        assert sm.module_exists()

        # add submodule
        #================
        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)
        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()
        # in our case, the module should not exist, which happens if we update a parent
        # repo and a new submodule comes into life
        nsm.remove(configuration=False, module=True)
        assert not nsm.module_exists() and nsm.exists()

        # dry-run does nothing
        rm.update(recursive=False, dry_run=True, progress=prog)

        # otherwise it will work
        rm.update(recursive=False, progress=prog)
        assert nsm.module_exists()

        # remove submodule - the previous one
        #====================================
        sm.set_parent_commit(csmadded)
        smp = sm.abspath
        assert not sm.remove(module=False).exists()
        assert os.path.isdir(smp)  # module still exists
        csmremoved = rwrepo.index.commit("Removed submodule")

        # an update will remove the module
        # not in dry_run
        rm.update(recursive=False, dry_run=True)
        assert os.path.isdir(smp)

        rm.update(recursive=False)
        assert not os.path.isdir(smp)

        # 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]))
        nsm.config_writer().set_value('url', nsmurl)
        csmpathchange = rwrepo.index.commit("changed url")
        nsm.set_parent_commit(csmpathchange)

        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)
        assert nsm.module().remotes.origin.url == nsmurl
        # head changed, as the remote url and its commit changed
        assert prev_commit != nsm.module().head.commit

        # add the submodule's changed commit to the index, which is what the
        # user would do
        # beforehand, update our instance's binsha with the new one
        nsm.binsha = nsm.module().head.commit.binsha
        rwrepo.index.add([nsm])

        # change branch
        #=================
        # we only have one branch, so we switch to a virtual one, and back
        # to the current one to trigger the difference
        cur_branch = nsm.branch
        nsmm = nsm.module()
        prev_commit = nsmm.head.commit
        for branch in ("some_virtual_branch", cur_branch.name):
            nsm.config_writer().set_value(Submodule.k_head_option,
                                          git.Head.to_full_path(branch))
            csmbranchchange = rwrepo.index.commit("changed branch to %s" %
                                                  branch)
            nsm.set_parent_commit(csmbranchchange)
        # END for each branch to change

        # Lets remove our tracking branch to simulate some changes
        nsmmh = nsmm.head
        assert nsmmh.ref.tracking_branch() is None  # never set it up until now
        assert not nsmmh.is_detached

        #dry run does nothing
        rm.update(recursive=False, dry_run=True, progress=prog)
        assert nsmmh.ref.tracking_branch() is None

        # the real thing does
        rm.update(recursive=False, progress=prog)

        assert nsmmh.ref.tracking_branch() is not None
        assert not nsmmh.is_detached

        # recursive update
        # =================
        # finally we recursively update a module, just to run the code at least once
        # remove the module so that it has more work
        assert len(nsm.children()) == 1
        assert nsm.exists() and nsm.module_exists() and len(
            nsm.children()) == 1
        # assure we pull locally only
        nsmc = nsm.children()[0]
        nsmc.config_writer().set_value('url', async_url)
        rm.update(recursive=True, progress=prog,
                  dry_run=True)  # just to run the code
        rm.update(recursive=True, progress=prog)

        assert len(nsm.children()) == 1 and nsmc.module_exists()
	def _do_base_tests(self, rwrepo):
		"""Perform all tests in the given repository, it may be bare or nonbare"""
		# manual instantiation
		smm = Submodule(rwrepo, "\0"*20)
		# name needs to be set in advance
		self.failUnlessRaises(AttributeError, getattr, smm, 'name') 
		
		# iterate - 1 submodule
		sms = Submodule.list_items(rwrepo, self.k_subm_current)
		assert len(sms) == 1
		sm = sms[0]
		
		# at a different time, there is None
		assert len(Submodule.list_items(rwrepo, self.k_no_subm_tag)) == 0
		
		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.branch_path == 'refs/heads/master'			# the default ...
		assert sm.branch_name == 'master'
		assert sm.parent_commit == rwrepo.head.commit
		# size is always 0
		assert sm.size == 0
		# the module is not checked-out yet
		self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
		
		# which is why we can't get the branch either - it points into the module() repository
		self.failUnlessRaises(InvalidGitRepositoryError, getattr, sm, 'branch')
		
		# branch_path works, as its just a string
		assert isinstance(sm.branch_path, basestring)
		
		# some commits earlier we still have a submodule, but its at a different commit
		smold = Submodule.iter_items(rwrepo, self.k_subm_changed).next()
		assert smold.binsha != sm.binsha
		assert smold != sm					# the name changed
		
		# force it to reread its information
		del(smold._url)
		smold.url == sm.url
		
		# test config_reader/writer methods
		sm.config_reader()
		new_smclone_path = None				# keep custom paths for later 
		new_csmclone_path = None				# 
		if rwrepo.bare:
			self.failUnlessRaises(InvalidGitRepositoryError, sm.config_writer)
		else:
			writer = sm.config_writer()
			# for faster checkout, set the url to the local path
			new_smclone_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path))
			writer.set_value('url', new_smclone_path)
			del(writer)
			assert sm.config_reader().get_value('url') == new_smclone_path
			assert sm.url == new_smclone_path
		# END handle bare repo
		smold.config_reader()
		
		# cannot get a writer on historical submodules
		if not rwrepo.bare:
			self.failUnlessRaises(ValueError, smold.config_writer)
		# END handle bare repo
		
		# make the old into a new - this doesn't work as the name changed
		prev_parent_commit = smold.parent_commit
		self.failUnlessRaises(ValueError, smold.set_parent_commit, self.k_subm_current)
		# the sha is properly updated
		smold.set_parent_commit(self.k_subm_changed+"~1")
		assert smold.binsha != sm.binsha
		
		# raises if the sm didn't exist in new parent - it keeps its 
		# parent_commit unchanged
		self.failUnlessRaises(ValueError, smold.set_parent_commit, self.k_no_subm_tag)
		
		# TEST TODO: if a path in the gitmodules file, but not in the index, it raises
		
		# TEST UPDATE
		##############
		# module retrieval is not always possible
		if rwrepo.bare:
			self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
			self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)
			self.failUnlessRaises(InvalidGitRepositoryError, sm.add, rwrepo, 'here', 'there')
		else:
			# its not checked out in our case
			self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
			assert not sm.module_exists()
			
			# currently there is only one submodule
			assert len(list(rwrepo.iter_submodules())) == 1
			assert sm.binsha != "\0"*20
			
			# TEST ADD
			###########
			# preliminary tests
			# adding existing returns exactly the existing
			sma = Submodule.add(rwrepo, sm.name, sm.path)
			assert sma.path == sm.path
			
			# no url and no module at path fails
			self.failUnlessRaises(ValueError, Submodule.add, rwrepo, "newsubm", "pathtorepo", url=None)
			
			# CONTINUE UPDATE
			#################
			
			# lets update it - its a recursive one too
			newdir = os.path.join(sm.abspath, 'dir')
			os.makedirs(newdir)
			
			# update fails if the path already exists non-empty
			self.failUnlessRaises(OSError, sm.update)
			os.rmdir(newdir)
			
			# dry-run does nothing
			sm.update(dry_run=True, progress=prog)
			assert not sm.module_exists()
			
			assert sm.update() is sm
			sm_repopath = sm.path				# cache for later
			assert sm.module_exists()
			assert isinstance(sm.module(), git.Repo)
			assert sm.module().working_tree_dir == sm.abspath
			
			# INTERLEAVE ADD TEST
			#####################
			# url must match the one in the existing repository ( if submodule name suggests a new one )
			# or we raise
			self.failUnlessRaises(ValueError, Submodule.add, rwrepo, "newsubm", sm.path, "git://someurl/repo.git")
			
			
			# CONTINUE UPDATE
			#################
			# we should have setup a tracking branch, which is also active
			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
			# dry-run does nothing
			sm.update(dry_run=True, recursive=False, progress=prog)
			assert len(sm.children()) == 0
			
			sm.update(recursive=False)
			assert len(list(rwrepo.iter_submodules())) == 2
			assert len(sm.children()) == 1			# its not checked out yet
			csm = sm.children()[0]
			assert not csm.module_exists()
			csm_repopath = csm.path
			
			# adjust the path of the submodules module to point to the local destination
			new_csmclone_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path, csm.path))
			csm.config_writer().set_value('url', new_csmclone_path)
			assert csm.url == new_csmclone_path
			
			# dry-run does nothing
			assert not csm.module_exists()
			sm.update(recursive=True, dry_run=True, progress=prog)
			assert not csm.module_exists()
			
			# update recursively again
			sm.update(recursive=True)
			assert csm.module_exists()
			
			# tracking branch once again
			csm.module().head.ref.tracking_branch() is not None
			
			# this flushed in a sub-submodule
			assert len(list(rwrepo.iter_submodules())) == 2
			
			
			# reset both heads to the previous version, verify that to_latest_revision works
			smods = (sm.module(), csm.module())
			for repo in smods:
				repo.head.reset('HEAD~2', working_tree=1)
			# END for each repo to reset
			
			# dry run does nothing 
			sm.update(recursive=True, dry_run=True, progress=prog)
			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)
			for repo in smods:
				assert repo.head.commit == repo.head.ref.tracking_branch().commit
			# END for each repo to check
			del(smods)
			
			# if the head is detached, it still works ( but warns )
			smref = sm.module().head.ref
			sm.module().head.ref = 'HEAD~1'
			# if there is no tracking branch, we get a warning as well
			csm_tracking_branch = csm.module().head.ref.tracking_branch()
			csm.module().head.ref.set_tracking_branch(None)
			sm.update(recursive=True, to_latest_revision=True)
			
			# to_latest_revision changes the child submodule's commit, it needs an
			# update now
			csm.set_parent_commit(csm.repo.head.commit)
			
			# undo the changes
			sm.module().head.ref = smref
			csm.module().head.ref.set_tracking_branch(csm_tracking_branch)
			
			# REMOVAL OF REPOSITOTRY
			########################
			# must delete something
			self.failUnlessRaises(ValueError, csm.remove, module=False, configuration=False)
			# We have modified the configuration, hence the index is dirty, and the
			# deletion will fail
			# NOTE: As we did  a few updates in the meanwhile, the indices were reset
			# Hence we create some changes
			csm.set_parent_commit(csm.repo.head.commit)
			sm.config_writer().set_value("somekey", "somevalue")
			csm.config_writer().set_value("okey", "ovalue")
			self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)
			# if we remove the dirty index, it would work
			sm.module().index.reset()
			# still, we have the file modified
			self.failUnlessRaises(InvalidGitRepositoryError, sm.remove, dry_run=True)
			sm.module().index.reset(working_tree=True)
			
			# enforce the submodule to be checked out at the right spot as well.
			csm.update()
			
			# this would work
			assert sm.remove(dry_run=True) is sm
			assert sm.module_exists()
			sm.remove(force=True, dry_run=True)
			assert sm.module_exists()
			
			# but ... we have untracked files in the child submodule
			fn = join_path_native(csm.module().working_tree_dir, "newfile")
			open(fn, 'w').write("hi")
			self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)
			
			# forcibly delete the child repository
			prev_count = len(sm.children())
			assert csm.remove(force=True) is csm
			assert not csm.exists()
			assert not csm.module_exists()
			assert len(sm.children()) == prev_count - 1
			# 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)
			assert sm.exists()
			assert not sm.module_exists()
			assert sm.config_reader().get_value('url')
			
			# delete the rest
			sm.remove()
			assert not sm.exists()
			assert not sm.module_exists()
			
			assert len(rwrepo.submodules) == 0
			
			# ADD NEW SUBMODULE
			###################
			# add a simple remote repo - trailing slashes are no problem
			smid = "newsub"
			osmid = "othersub"
			nsm = Submodule.add(rwrepo, smid, sm_repopath, new_smclone_path+"/", None, no_checkout=True)
			assert nsm.name == smid
			assert nsm.module_exists()
			assert nsm.exists()
			# its not checked out
			assert not os.path.isfile(join_path_native(nsm.module().working_tree_dir, Submodule.k_modules_file))
			assert len(rwrepo.submodules) == 1
			
			# add another submodule, but into the root, not as submodule
			osm = Submodule.add(rwrepo, osmid, csm_repopath, new_csmclone_path, Submodule.k_head_default)
			assert osm != nsm
			assert osm.module_exists()
			assert osm.exists()
			assert os.path.isfile(join_path_native(osm.module().working_tree_dir, 'setup.py'))
			
			assert len(rwrepo.submodules) == 2
			
			# commit the changes, just to finalize the operation
			rwrepo.index.commit("my submod commit")
			assert len(rwrepo.submodules) == 2
			
			# needs update as the head changed, it thinks its in the history 
			# of the repo otherwise
			nsm.set_parent_commit(rwrepo.head.commit)
			osm.set_parent_commit(rwrepo.head.commit)
			
			# MOVE MODULE
			#############
			# invalid inptu
			self.failUnlessRaises(ValueError, nsm.move, 'doesntmatter', module=False, configuration=False)
			
			# renaming to the same path does nothing
			assert nsm.move(sm.path) is nsm
			
			# rename a module
			nmp = join_path_native("new", "module", "dir") + "/" # new module path
			pmp = nsm.path
			abspmp = nsm.abspath
			assert nsm.move(nmp) is nsm
			nmp = nmp[:-1]			# cut last /
			nmpl = to_native_path_linux(nmp)
			assert nsm.path == nmpl
			assert rwrepo.submodules[0].path == nmpl
			
			mpath = 'newsubmodule'
			absmpath = join_path_native(rwrepo.working_tree_dir, mpath)
			open(absmpath, 'w').write('')
			self.failUnlessRaises(ValueError, nsm.move, mpath)
			os.remove(absmpath)
			
			# now it works, as we just move it back
			nsm.move(pmp)
			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
			osmod = osm.module()
			
			osm.remove(module=False)
			for remote in osmod.remotes:
				remote.remove(osmod, remote.name)
			assert not osm.exists()
			self.failUnlessRaises(ValueError, Submodule.add, rwrepo, osmid, csm_repopath, url=None)   
		# END handle bare mode
		
		# Error if there is no submodule file here
		self.failUnlessRaises(IOError, Submodule._config_parser, rwrepo, rwrepo.commit(self.k_no_subm_tag), True)
Exemple #41
0
	def move(self, module_path, configuration=True, module=True):
		"""Move the submodule to a another module path. This involves physically moving
		the repository at our current path, changing the configuration, as well as
		adjusting our index entry accordingly.
		
		:param module_path: the path to which to move our module, given as
			repository-relative path. Intermediate directories will be created
			accordingly. If the path already exists, it must be empty.
			Trailling (back)slashes are removed automatically
		:param configuration: if True, the configuration will be adjusted to let 
			the submodule point to the given path.
		:param module: if True, the repository managed by this submodule
			will be moved, not the configuration. This will effectively 
			leave your repository in an inconsistent state unless the configuration
			and index already point to the target location.
		:return: self
		:raise ValueError: if the module path existed and was not empty, or was a file
		:note: Currently the method is not atomic, and it could leave the repository
			in an inconsistent state if a sub-step fails for some reason
		"""
		if module + configuration < 1:
			raise ValueError("You must specify to move at least the module or the configuration of the submodule")
		#END handle input
		
		module_path = to_native_path_linux(module_path)
		if module_path.endswith('/'):
			module_path = module_path[:-1]
		# END handle trailing slash
		
		# VERIFY DESTINATION
		if module_path == self.path:
			return self
		#END handle no change
		
		dest_path = join_path_native(self.repo.working_tree_dir, module_path)
		if os.path.isfile(dest_path):
			raise ValueError("Cannot move repository onto a file: %s" % dest_path)
		# END handle target files
		
		index = self.repo.index
		tekey = index.entry_key(module_path, 0)
		# if the target item already exists, fail
		if configuration and tekey in index.entries:
			raise ValueError("Index entry for target path did alredy exist")
		#END handle index key already there
		
		# remove existing destination
		if module:
			if os.path.exists(dest_path):
				if len(os.listdir(dest_path)):
					raise ValueError("Destination module directory was not empty")
				#END handle non-emptyness
				
				if os.path.islink(dest_path):
					os.remove(dest_path)
				else:
					os.rmdir(dest_path)
				#END handle link
			else:
				# recreate parent directories
				# NOTE: renames() does that now
				pass
			#END handle existance
		# END handle module
		
		# move the module into place if possible
		cur_path = self.abspath
		renamed_module = False
		if module and os.path.exists(cur_path):
			os.renames(cur_path, dest_path)
			renamed_module = True
		#END move physical module
		
		
		# rename the index entry - have to manipulate the index directly as 
		# git-mv cannot be used on submodules ... yeah
		try:
			if configuration:
				try:
					ekey = index.entry_key(self.path, 0)
					entry = index.entries[ekey]
					del(index.entries[ekey])
					nentry = git.IndexEntry(entry[:3]+(module_path,)+entry[4:])
					index.entries[tekey] = nentry
				except KeyError:
					raise InvalidGitRepositoryError("Submodule's entry at %r did not exist" % (self.path))
				#END handle submodule doesn't exist
				
				# update configuration
				writer = self.config_writer(index=index)		# auto-write
				writer.set_value('path', module_path)
				self.path = module_path
				del(writer)
			# END handle configuration flag
		except Exception:
			if renamed_module:
				os.renames(dest_path, cur_path)
			# END undo module renaming
			raise
		#END handle undo rename
		
		return self
Exemple #42
0
	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.
		
		:param recursive: if True, we will operate recursively and update child-
			modules as well.
		:param init: if True, the module repository will be cloned into place if necessary
		:param to_latest_revision: if True, the submodule's sha will be ignored during checkout.
			Instead, the remote will be fetched, and the local tracking branch updated.
			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
		: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"""
		if self.repo.bare:
			return self
		#END pass in bare mode
		
		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
		#####################################
		try:
			mrepo = self.module()
			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, prefix+"Fetching remote %s of submodule %r" % (remote, self.name))
				#===============================
				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, prefix+"Done fetching remote of submodule %r" % self.name)
			#END fetch new data
		except InvalidGitRepositoryError:
			if not init:
				return self
			# END early abort if init is not allowed
			
			# there is no git-repository yet - but delete empty paths
			module_path = join_path_native(self.repo.working_tree_dir, self.path)
			if not dry_run and os.path.isdir(module_path):
				try:
					os.rmdir(module_path)
				except OSError:
					raise OSError("Module directory at %r does already exist and is non-empty" % module_path)
				# END handle OSError
			# END handle directory removal
			
			# 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, prefix+"Cloning %s to %s in submodule %r" % (self.url, module_path, self.name))
			if not dry_run:
				mrepo = type(self.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)
			
			
			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
		
		
		# DETERMINE SHAS TO CHECKOUT
		############################
		binsha = self.binsha
		hexsha = self.hexsha
		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 mrepo is not None 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()
				if rref is not None:
					rcommit = rref.commit
					binsha = rcommit.binsha
					hexsha = rcommit.hexsha
				else:
					print >> sys.stderr, "%s a tracking branch was not set for local branch '%s'" % (msg_base, mrepo.head.ref) 
				# END handle remote ref
			else:
				print >> sys.stderr, "%s there was no local tracking branch" % msg_base
			# END handle detached head
		# END handle to_latest_revision option
		
		# update the working tree
		# 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 to revision %s" % (self.path, self.name, hexsha))
			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:
			# 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
Exemple #43
0
 def abspath(self):
     return join_path_native(_git_dir(self.repo, self.path), self.path)
Exemple #44
0
	def _index_path(self):
		return join_path_native(self.repo.git_dir, "index")
Exemple #45
0
 def abspath(self):
     return join_path_native(self.repo.git_dir, self.path)
Exemple #46
0
	def from_tree(cls, repo, *treeish, **kwargs):
		"""Merge the given treeish revisions into a new index which is returned.
		The original index will remain unaltered

		:param repo:
			The repository treeish are located in.

		:param treeish:
			One, two or three Tree Objects, Commits or 40 byte hexshas. The result
			changes according to the amount of trees.
			If 1 Tree is given, it will just be read into a new index
			If 2 Trees are given, they will be merged into a new index using a
			 two way merge algorithm. Tree 1 is the 'current' tree, tree 2 is the 'other'
			 one. It behaves like a fast-forward.
			 If 3 Trees are given, a 3-way merge will be performed with the first tree
			 being the common ancestor of tree 2 and tree 3. Tree 2 is the 'current' tree,
			 tree 3 is the 'other' one

		:param kwargs:
			Additional arguments passed to git-read-tree

		:return:
			New IndexFile instance. It will point to a temporary index location which
			does not exist anymore. If you intend to write such a merged Index, supply
			an alternate file_path to its 'write' method.

		:note:
			In the three-way merge case, --aggressive will be specified to automatically
			resolve more cases in a commonly correct manner. Specify trivial=True as kwarg
			to override that.

			As the underlying git-read-tree command takes into account the current index,
			it will be temporarily moved out of the way to assure there are no unsuspected
			interferences."""
		if len(treeish) == 0 or len(treeish) > 3:
			raise ValueError("Please specify between 1 and 3 treeish, got %i" % len(treeish))

		arg_list = list()
		# ignore that working tree and index possibly are out of date
		if len(treeish)>1:
			# drop unmerged entries when reading our index and merging
			arg_list.append("--reset")
			# handle non-trivial cases the way a real merge does
			arg_list.append("--aggressive")
		# END merge handling

		# tmp file created in git home directory to be sure renaming
		# works - /tmp/ dirs could be on another device
		tmp_index = tempfile.mktemp('','',repo.git_dir)
		arg_list.append("--index-output=%s" % tmp_index)
		arg_list.extend(treeish)

		# move current index out of the way - otherwise the merge may fail
		# as it considers existing entries. moving it essentially clears the index.
		# Unfortunately there is no 'soft' way to do it.
		# The TemporaryFileSwap assure the original file get put back
		index_handler = TemporaryFileSwap(join_path_native(repo.git_dir, 'index'))
		try:
			repo.git.read_tree(*arg_list, **kwargs)
			index = cls(repo, tmp_index)
			index.entries		# force it to read the file as we will delete the temp-file
			del(index_handler)	# release as soon as possible
		finally:
			if os.path.exists(tmp_index):
				os.remove(tmp_index)
		# END index merge handling

		return index
    def test_root_module(self, rwrepo):
        # Can query everything without problems
        rm = RootModule(self.rorepo)
        assert rm.module() is self.rorepo

        # try attributes
        rm.binsha
        rm.mode
        rm.path
        assert rm.name == rm.k_root_name
        assert rm.parent_commit == self.rorepo.head.commit
        rm.url
        rm.branch

        assert len(rm.list_items(rm.module())) == 1
        rm.config_reader()
        with rm.config_writer():
            pass

        # deep traversal gitdb / async
        rsmsp = [sm.path for sm in rm.traverse()]
        assert len(rsmsp) >= 2          # gitdb and async [and smmap], async being a child of gitdb

        # cannot set the parent commit as root module's path didn't exist
        self.failUnlessRaises(ValueError, rm.set_parent_commit, 'HEAD')

        # TEST UPDATE
        #############
        # setup commit which remove existing, add new and modify existing submodules
        rm = RootModule(rwrepo)
        assert len(rm.children()) == 1

        # modify path without modifying the index entry
        # ( which is what the move method would do properly )
        #==================================================
        sm = rm.children()[0]
        pp = "path/prefix"
        fp = join_path_native(pp, sm.path)
        prep = sm.path
        assert not sm.module_exists()               # was never updated after rwrepo's clone

        # assure we clone from a local source
        with sm.config_writer() as writer:
            writer.set_value('url', Git.polish_url(osp.join(self.rorepo.working_tree_dir, sm.path)))

        # dry-run does nothing
        sm.update(recursive=False, dry_run=True, progress=prog)
        assert not sm.module_exists()

        sm.update(recursive=False)
        assert sm.module_exists()
        with sm.config_writer() as writer:
            writer.set_value('path', fp)    # change path to something with prefix AFTER url change

        # update fails as list_items in such a situations cannot work, as it cannot
        # find the entry at the changed path
        self.failUnlessRaises(InvalidGitRepositoryError, rm.update, recursive=False)

        # move it properly - doesn't work as it its path currently points to an indexentry
        # which doesn't exist ( move it to some path, it doesn't matter here )
        self.failUnlessRaises(InvalidGitRepositoryError, sm.move, pp)
        # reset the path(cache) to where it was, now it works
        sm.path = prep
        sm.move(fp, module=False)       # leave it at the old location

        assert not sm.module_exists()
        cpathchange = rwrepo.index.commit("changed sm path")  # finally we can commit

        # update puts the module into place
        rm.update(recursive=False, progress=prog)
        sm.set_parent_commit(cpathchange)
        assert sm.module_exists()

        # add submodule
        #================
        nsmn = "newsubmodule"
        nsmp = "submrepo"
        subrepo_url = Git.polish_url(osp.join(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()
        # in our case, the module should not exist, which happens if we update a parent
        # repo and a new submodule comes into life
        nsm.remove(configuration=False, module=True)
        assert not nsm.module_exists() and nsm.exists()

        # dry-run does nothing
        rm.update(recursive=False, dry_run=True, progress=prog)

        # otherwise it will work
        rm.update(recursive=False, progress=prog)
        assert nsm.module_exists()

        # remove submodule - the previous one
        #====================================
        sm.set_parent_commit(csmadded)
        smp = sm.abspath
        assert not sm.remove(module=False).exists()
        assert osp.isdir(smp)           # module still exists
        csmremoved = rwrepo.index.commit("Removed submodule")

        # an update will remove the module
        # not in dry_run
        rm.update(recursive=False, dry_run=True, force_remove=True)
        assert osp.isdir(smp)

        # 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 osp.isdir(smp)

        # '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(osp.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 = Git.polish_url(osp.join(self.rorepo.working_tree_dir, rsmsp[0]))
        with nsm.config_writer() as writer:
            writer.set_value('url', nsmurl)
        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, force_reset=True)
        assert nsm.module().remotes.origin.url == nsmurl
        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
        # beforehand, update our instance's binsha with the new one
        nsm.binsha = nsm.module().head.commit.binsha
        rwrepo.index.add([nsm])

        # change branch
        #=================
        # we only have one branch, so we switch to a virtual one, and back
        # to the current one to trigger the difference
        cur_branch = nsm.branch
        nsmm = nsm.module()
        prev_commit = nsmm.head.commit
        for branch in ("some_virtual_branch", cur_branch.name):
            with nsm.config_writer() as writer:
                writer.set_value(Submodule.k_head_option, git.Head.to_full_path(branch))
            csmbranchchange = rwrepo.index.commit("changed branch to %s" % branch)
            nsm.set_parent_commit(csmbranchchange)
        # END for each branch to change

        # Lets remove our tracking branch to simulate some changes
        nsmmh = nsmm.head
        assert nsmmh.ref.tracking_branch() is None                  # never set it up until now
        assert not nsmmh.is_detached

        # dry run does nothing
        rm.update(recursive=False, dry_run=True, progress=prog)
        assert nsmmh.ref.tracking_branch() is None

        # the real thing does
        rm.update(recursive=False, progress=prog)

        assert nsmmh.ref.tracking_branch() is not None
        assert not nsmmh.is_detached

        # recursive update
        # =================
        # finally we recursively update a module, just to run the code at least once
        # remove the module so that it has more work
        assert len(nsm.children()) >= 1  # could include smmap
        assert nsm.exists() and nsm.module_exists() and len(nsm.children()) >= 1
        # assure we pull locally only
        nsmc = nsm.children()[0]
        with nsmc.config_writer() as writer:
            writer.set_value('url', subrepo_url)
        rm.update(recursive=True, progress=prog, dry_run=True)      # just to run the code
        rm.update(recursive=True, progress=prog)

        # gitdb: has either 1 or 2 submodules depending on the version
        assert len(nsm.children()) >= 1 and nsmc.module_exists()
Exemple #48
0
    def _do_base_tests(self, rwrepo):
        """Perform all tests in the given repository, it may be bare or nonbare"""
        # manual instantiation
        smm = Submodule(rwrepo, "\0" * 20)
        # name needs to be set in advance
        self.failUnlessRaises(AttributeError, getattr, smm, 'name')

        # iterate - 1 submodule
        sms = Submodule.list_items(rwrepo, self.k_subm_current)
        assert len(sms) == 1
        sm = sms[0]

        # at a different time, there is None
        assert len(Submodule.list_items(rwrepo, self.k_no_subm_tag)) == 0

        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.branch_path == 'refs/heads/master'  # the default ...
        assert sm.branch_name == 'master'
        assert sm.parent_commit == rwrepo.head.commit
        # size is always 0
        assert sm.size == 0
        # the module is not checked-out yet
        self.failUnlessRaises(InvalidGitRepositoryError, sm.module)

        # which is why we can't get the branch either - it points into the module() repository
        self.failUnlessRaises(InvalidGitRepositoryError, getattr, sm, 'branch')

        # branch_path works, as its just a string
        assert isinstance(sm.branch_path, basestring)

        # some commits earlier we still have a submodule, but its at a different commit
        smold = Submodule.iter_items(rwrepo, self.k_subm_changed).next()
        assert smold.binsha != sm.binsha
        assert smold != sm  # the name changed

        # force it to reread its information
        del (smold._url)
        smold.url == sm.url

        # test config_reader/writer methods
        sm.config_reader()
        new_smclone_path = None  # keep custom paths for later
        new_csmclone_path = None  #
        if rwrepo.bare:
            self.failUnlessRaises(InvalidGitRepositoryError, sm.config_writer)
        else:
            writer = sm.config_writer()
            # for faster checkout, set the url to the local path
            new_smclone_path = to_native_path_linux(
                join_path_native(self.rorepo.working_tree_dir, sm.path))
            writer.set_value('url', new_smclone_path)
            del (writer)
            assert sm.config_reader().get_value('url') == new_smclone_path
            assert sm.url == new_smclone_path
        # END handle bare repo
        smold.config_reader()

        # cannot get a writer on historical submodules
        if not rwrepo.bare:
            self.failUnlessRaises(ValueError, smold.config_writer)
        # END handle bare repo

        # make the old into a new - this doesn't work as the name changed
        prev_parent_commit = smold.parent_commit
        self.failUnlessRaises(ValueError, smold.set_parent_commit,
                              self.k_subm_current)
        # the sha is properly updated
        smold.set_parent_commit(self.k_subm_changed + "~1")
        assert smold.binsha != sm.binsha

        # raises if the sm didn't exist in new parent - it keeps its
        # parent_commit unchanged
        self.failUnlessRaises(ValueError, smold.set_parent_commit,
                              self.k_no_subm_tag)

        # TEST TODO: if a path in the gitmodules file, but not in the index, it raises

        # TEST UPDATE
        ##############
        # module retrieval is not always possible
        if rwrepo.bare:
            self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
            self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)
            self.failUnlessRaises(InvalidGitRepositoryError, sm.add, rwrepo,
                                  'here', 'there')
        else:
            # its not checked out in our case
            self.failUnlessRaises(InvalidGitRepositoryError, sm.module)
            assert not sm.module_exists()

            # currently there is only one submodule
            assert len(list(rwrepo.iter_submodules())) == 1
            assert sm.binsha != "\0" * 20

            # TEST ADD
            ###########
            # preliminary tests
            # adding existing returns exactly the existing
            sma = Submodule.add(rwrepo, sm.name, sm.path)
            assert sma.path == sm.path

            # no url and no module at path fails
            self.failUnlessRaises(ValueError,
                                  Submodule.add,
                                  rwrepo,
                                  "newsubm",
                                  "pathtorepo",
                                  url=None)

            # CONTINUE UPDATE
            #################

            # lets update it - its a recursive one too
            newdir = os.path.join(sm.abspath, 'dir')
            os.makedirs(newdir)

            # update fails if the path already exists non-empty
            self.failUnlessRaises(OSError, sm.update)
            os.rmdir(newdir)

            # dry-run does nothing
            sm.update(dry_run=True, progress=prog)
            assert not sm.module_exists()

            assert sm.update() is sm
            sm_repopath = sm.path  # cache for later
            assert sm.module_exists()
            assert isinstance(sm.module(), git.Repo)
            assert sm.module().working_tree_dir == sm.abspath

            # INTERLEAVE ADD TEST
            #####################
            # url must match the one in the existing repository ( if submodule name suggests a new one )
            # or we raise
            self.failUnlessRaises(ValueError, Submodule.add, rwrepo, "newsubm",
                                  sm.path, "git://someurl/repo.git")

            # CONTINUE UPDATE
            #################
            # we should have setup a tracking branch, which is also active
            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
            # dry-run does nothing
            sm.update(dry_run=True, recursive=False, progress=prog)
            assert len(sm.children()) == 0

            sm.update(recursive=False)
            assert len(list(rwrepo.iter_submodules())) == 2
            assert len(sm.children()) == 1  # its not checked out yet
            csm = sm.children()[0]
            assert not csm.module_exists()
            csm_repopath = csm.path

            # adjust the path of the submodules module to point to the local destination
            new_csmclone_path = to_native_path_linux(
                join_path_native(self.rorepo.working_tree_dir, sm.path,
                                 csm.path))
            csm.config_writer().set_value('url', new_csmclone_path)
            assert csm.url == new_csmclone_path

            # dry-run does nothing
            assert not csm.module_exists()
            sm.update(recursive=True, dry_run=True, progress=prog)
            assert not csm.module_exists()

            # update recursively again
            sm.update(recursive=True)
            assert csm.module_exists()

            # tracking branch once again
            csm.module().head.ref.tracking_branch() is not None

            # this flushed in a sub-submodule
            assert len(list(rwrepo.iter_submodules())) == 2

            # reset both heads to the previous version, verify that to_latest_revision works
            smods = (sm.module(), csm.module())
            for repo in smods:
                repo.head.reset('HEAD~2', working_tree=1)
            # END for each repo to reset

            # dry run does nothing
            sm.update(recursive=True, dry_run=True, progress=prog)
            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)
            for repo in smods:
                assert repo.head.commit == repo.head.ref.tracking_branch(
                ).commit
            # END for each repo to check
            del (smods)

            # if the head is detached, it still works ( but warns )
            smref = sm.module().head.ref
            sm.module().head.ref = 'HEAD~1'
            # if there is no tracking branch, we get a warning as well
            csm_tracking_branch = csm.module().head.ref.tracking_branch()
            csm.module().head.ref.set_tracking_branch(None)
            sm.update(recursive=True, to_latest_revision=True)

            # to_latest_revision changes the child submodule's commit, it needs an
            # update now
            csm.set_parent_commit(csm.repo.head.commit)

            # undo the changes
            sm.module().head.ref = smref
            csm.module().head.ref.set_tracking_branch(csm_tracking_branch)

            # REMOVAL OF REPOSITOTRY
            ########################
            # must delete something
            self.failUnlessRaises(ValueError,
                                  csm.remove,
                                  module=False,
                                  configuration=False)
            # We have modified the configuration, hence the index is dirty, and the
            # deletion will fail
            # NOTE: As we did  a few updates in the meanwhile, the indices were reset
            # Hence we create some changes
            csm.set_parent_commit(csm.repo.head.commit)
            sm.config_writer().set_value("somekey", "somevalue")
            csm.config_writer().set_value("okey", "ovalue")
            self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)
            # if we remove the dirty index, it would work
            sm.module().index.reset()
            # still, we have the file modified
            self.failUnlessRaises(InvalidGitRepositoryError,
                                  sm.remove,
                                  dry_run=True)
            sm.module().index.reset(working_tree=True)

            # this would work
            assert sm.remove(dry_run=True) is sm
            assert sm.module_exists()
            sm.remove(force=True, dry_run=True)
            assert sm.module_exists()

            # but ... we have untracked files in the child submodule
            fn = join_path_native(csm.module().working_tree_dir, "newfile")
            open(fn, 'w').write("hi")
            self.failUnlessRaises(InvalidGitRepositoryError, sm.remove)

            # forcibly delete the child repository
            assert csm.remove(force=True) is csm
            assert not csm.exists()
            assert not csm.module_exists()
            assert len(sm.children()) == 0
            # 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)
            assert sm.exists()
            assert not sm.module_exists()
            assert sm.config_reader().get_value('url')

            # delete the rest
            sm.remove()
            assert not sm.exists()
            assert not sm.module_exists()

            assert len(rwrepo.submodules) == 0

            # ADD NEW SUBMODULE
            ###################
            # add a simple remote repo - trailing slashes are no problem
            smid = "newsub"
            osmid = "othersub"
            nsm = Submodule.add(rwrepo,
                                smid,
                                sm_repopath,
                                new_smclone_path + "/",
                                None,
                                no_checkout=True)
            assert nsm.name == smid
            assert nsm.module_exists()
            assert nsm.exists()
            # its not checked out
            assert not os.path.isfile(
                join_path_native(nsm.module().working_tree_dir,
                                 Submodule.k_modules_file))
            assert len(rwrepo.submodules) == 1

            # add another submodule, but into the root, not as submodule
            osm = Submodule.add(rwrepo, osmid, csm_repopath, new_csmclone_path,
                                Submodule.k_head_default)
            assert osm != nsm
            assert osm.module_exists()
            assert osm.exists()
            assert os.path.isfile(
                join_path_native(osm.module().working_tree_dir, 'setup.py'))

            assert len(rwrepo.submodules) == 2

            # commit the changes, just to finalize the operation
            rwrepo.index.commit("my submod commit")
            assert len(rwrepo.submodules) == 2

            # needs update as the head changed, it thinks its in the history
            # of the repo otherwise
            nsm.set_parent_commit(rwrepo.head.commit)
            osm.set_parent_commit(rwrepo.head.commit)

            # MOVE MODULE
            #############
            # invalid inptu
            self.failUnlessRaises(ValueError,
                                  nsm.move,
                                  'doesntmatter',
                                  module=False,
                                  configuration=False)

            # renaming to the same path does nothing
            assert nsm.move(sm.path) is nsm

            # rename a module
            nmp = join_path_native("new", "module",
                                   "dir") + "/"  # new module path
            pmp = nsm.path
            abspmp = nsm.abspath
            assert nsm.move(nmp) is nsm
            nmp = nmp[:-1]  # cut last /
            assert nsm.path == nmp
            assert rwrepo.submodules[0].path == nmp

            mpath = 'newsubmodule'
            absmpath = join_path_native(rwrepo.working_tree_dir, mpath)
            open(absmpath, 'w').write('')
            self.failUnlessRaises(ValueError, nsm.move, mpath)
            os.remove(absmpath)

            # now it works, as we just move it back
            nsm.move(pmp)
            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
            osmod = osm.module()

            osm.remove(module=False)
            for remote in osmod.remotes:
                remote.remove(osmod, remote.name)
            assert not osm.exists()
            self.failUnlessRaises(ValueError,
                                  Submodule.add,
                                  rwrepo,
                                  osmid,
                                  csm_repopath,
                                  url=None)
        # END handle bare mode

        # Error if there is no submodule file here
        self.failUnlessRaises(IOError, Submodule._config_parser, rwrepo,
                              rwrepo.commit(self.k_no_subm_tag), True)
Exemple #49
0
    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.

        :param recursive: if True, we will operate recursively and update child-
            modules as well.
        :param init: if True, the module repository will be cloned into place if necessary
        :param to_latest_revision: if True, the submodule's sha will be ignored during checkout.
            Instead, the remote will be fetched, and the local tracking branch updated.
            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
        :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"""
        if self.repo.bare:
            return self
        #END pass in bare mode

        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
        #####################################
        try:
            mrepo = self.module()
            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, prefix +
                    "Fetching remote %s of submodule %r" % (remote, self.name))
                #===============================
                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, prefix +
                    "Done fetching remote of submodule %r" % self.name)
            #END fetch new data
        except InvalidGitRepositoryError:
            if not init:
                return self
            # END early abort if init is not allowed
            import git

            # there is no git-repository yet - but delete empty paths
            module_path = join_path_native(self.repo.working_tree_dir,
                                           self.path)
            if not dry_run and os.path.isdir(module_path):
                try:
                    os.rmdir(module_path)
                except OSError:
                    raise OSError(
                        "Module directory at %r does already exist and is non-empty"
                        % module_path)
                # END handle OSError
            # END handle directory removal

            # 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,
                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)

            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

        # DETERMINE SHAS TO CHECKOUT
        ############################
        binsha = self.binsha
        hexsha = self.hexsha
        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 mrepo is not None 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()
                if rref is not None:
                    rcommit = rref.commit
                    binsha = rcommit.binsha
                    hexsha = rcommit.hexsha
                else:
                    print >> sys.stderr, "%s a tracking branch was not set for local branch '%s'" % (
                        msg_base, mrepo.head.ref)
                # END handle remote ref
            else:
                print >> sys.stderr, "%s there was no local tracking branch" % msg_base
            # END handle detached head
        # END handle to_latest_revision option

        # update the working tree
        # 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 to revision %s" %
                (self.path, self.name, hexsha))
            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:
            # 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
	def test_root_module(self, rwrepo):
		# Can query everything without problems
		rm = RootModule(self.rorepo)
		assert rm.module() is self.rorepo
		
		# try attributes
		rm.binsha
		rm.mode
		rm.path
		assert rm.name == rm.k_root_name
		assert rm.parent_commit == self.rorepo.head.commit
		rm.url
		rm.branch
		
		assert len(rm.list_items(rm.module())) == 1
		rm.config_reader()
		rm.config_writer()
		
		# deep traversal gitdb / async
		rsmsp = [sm.path for sm in rm.traverse()]
		assert len(rsmsp) >= 2			# gitdb and async [and smmap], async being a child of gitdb
		
		# cannot set the parent commit as root module's path didn't exist
		self.failUnlessRaises(ValueError, rm.set_parent_commit, 'HEAD')
		
		# TEST UPDATE
		#############
		# setup commit which remove existing, add new and modify existing submodules
		rm = RootModule(rwrepo)
		assert len(rm.children()) == 1
		
		# modify path without modifying the index entry
		# ( which is what the move method would do properly )
		#==================================================
		sm = rm.children()[0]
		pp = "path/prefix"
		fp = join_path_native(pp, sm.path)
		prep = sm.path
		assert not sm.module_exists()				# was never updated after rwrepo's clone
		
		# assure we clone from a local source 
		sm.config_writer().set_value('url', to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path)))
		
		# dry-run does nothing
		sm.update(recursive=False, dry_run=True, progress=prog)
		assert not sm.module_exists()
		
		sm.update(recursive=False)
		assert sm.module_exists()
		sm.config_writer().set_value('path', fp)	# change path to something with prefix AFTER url change
		
		# update fails as list_items in such a situations cannot work, as it cannot
		# find the entry at the changed path
		self.failUnlessRaises(InvalidGitRepositoryError, rm.update, recursive=False)
		
		# move it properly - doesn't work as it its path currently points to an indexentry
		# which doesn't exist ( move it to some path, it doesn't matter here )
		self.failUnlessRaises(InvalidGitRepositoryError, sm.move, pp)
		# reset the path(cache) to where it was, now it works
		sm.path = prep
		sm.move(fp, module=False)		# leave it at the old location
		
		assert not sm.module_exists()
		cpathchange = rwrepo.index.commit("changed sm path") # finally we can commit
		
		# update puts the module into place
		rm.update(recursive=False, progress=prog)
		sm.set_parent_commit(cpathchange)
		assert sm.module_exists()
		
		# add submodule
		#================
		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)
		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()
		# in our case, the module should not exist, which happens if we update a parent
		# repo and a new submodule comes into life
		nsm.remove(configuration=False, module=True)
		assert not nsm.module_exists() and nsm.exists()
		
		
		# dry-run does nothing
		rm.update(recursive=False, dry_run=True, progress=prog)
		
		# otherwise it will work
		rm.update(recursive=False, progress=prog)
		assert nsm.module_exists()
		
		
		
		# remove submodule - the previous one
		#====================================
		sm.set_parent_commit(csmadded)
		smp = sm.abspath
		assert not sm.remove(module=False).exists()
		assert os.path.isdir(smp)			# module still exists
		csmremoved = rwrepo.index.commit("Removed submodule")
		
		# an update will remove the module
		# not in dry_run
		rm.update(recursive=False, dry_run=True)
		assert os.path.isdir(smp)
		
		rm.update(recursive=False)
		assert not os.path.isdir(smp)
		
		
		# 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]))
		nsm.config_writer().set_value('url', nsmurl)
		csmpathchange = rwrepo.index.commit("changed url")
		nsm.set_parent_commit(csmpathchange)
		
		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)
		assert nsm.module().remotes.origin.url == nsmurl
		# head changed, as the remote url and its commit changed
		assert prev_commit != nsm.module().head.commit
		
		# add the submodule's changed commit to the index, which is what the
		# user would do
		# beforehand, update our instance's binsha with the new one
		nsm.binsha = nsm.module().head.commit.binsha
		rwrepo.index.add([nsm])
		
		# change branch
		#=================
		# we only have one branch, so we switch to a virtual one, and back 
		# to the current one to trigger the difference
		cur_branch = nsm.branch
		nsmm = nsm.module()
		prev_commit = nsmm.head.commit
		for branch in ("some_virtual_branch", cur_branch.name):
			nsm.config_writer().set_value(Submodule.k_head_option, git.Head.to_full_path(branch))
			csmbranchchange = rwrepo.index.commit("changed branch to %s" % branch)
			nsm.set_parent_commit(csmbranchchange)
		# END for each branch to change
		
		# Lets remove our tracking branch to simulate some changes
		nsmmh = nsmm.head
		assert nsmmh.ref.tracking_branch() is None					# never set it up until now
		assert not nsmmh.is_detached
		
		#dry run does nothing
		rm.update(recursive=False, dry_run=True, progress=prog)
		assert nsmmh.ref.tracking_branch() is None
		
		# the real thing does
		rm.update(recursive=False, progress=prog)
		
		assert nsmmh.ref.tracking_branch() is not None
		assert not nsmmh.is_detached
		
		# recursive update
		# =================
		# finally we recursively update a module, just to run the code at least once
		# remove the module so that it has more work
		assert len(nsm.children()) >= 1 # could include smmap
		assert nsm.exists() and nsm.module_exists() and len(nsm.children()) >= 1
		# assure we pull locally only
		nsmc = nsm.children()[0] 
		nsmc.config_writer().set_value('url', async_url)
		rm.update(recursive=True, progress=prog, dry_run=True)		# just to run the code
		rm.update(recursive=True, progress=prog)
		
		# gitdb: has either 1 or 2 submodules depending on the version
		assert len(nsm.children()) >= 1 and nsmc.module_exists()
Exemple #51
0
    def test_root_module(self, rwrepo):
        # Can query everything without problems
        rm = RootModule(self.rorepo)
        assert rm.module() is self.rorepo

        # try attributes
        rm.binsha
        rm.mode
        rm.path
        assert rm.name == rm.k_root_name
        assert rm.parent_commit == self.rorepo.head.commit
        rm.url
        rm.branch

        assert len(rm.list_items(rm.module())) == 1
        rm.config_reader()
        with rm.config_writer():
            pass

        # deep traversal gitdb / async
        rsmsp = [sm.path for sm in rm.traverse()]
        assert len(
            rsmsp
        ) >= 2  # gitdb and async [and smmap], async being a child of gitdb

        # cannot set the parent commit as root module's path didn't exist
        self.failUnlessRaises(ValueError, rm.set_parent_commit, 'HEAD')

        # TEST UPDATE
        #############
        # setup commit which remove existing, add new and modify existing submodules
        rm = RootModule(rwrepo)
        assert len(rm.children()) == 1

        # modify path without modifying the index entry
        # ( which is what the move method would do properly )
        #==================================================
        sm = rm.children()[0]
        pp = "path/prefix"
        fp = join_path_native(pp, sm.path)
        prep = sm.path
        assert not sm.module_exists()  # was never updated after rwrepo's clone

        # assure we clone from a local source
        with sm.config_writer() as writer:
            writer.set_value(
                'url',
                Git.polish_url(osp.join(self.rorepo.working_tree_dir,
                                        sm.path)))

        # dry-run does nothing
        sm.update(recursive=False, dry_run=True, progress=prog)
        assert not sm.module_exists()

        sm.update(recursive=False)
        assert sm.module_exists()
        with sm.config_writer() as writer:
            writer.set_value(
                'path',
                fp)  # change path to something with prefix AFTER url change

        # update doesn't fail, because list_items ignores the wrong path in such situations.
        rm.update(recursive=False)

        # move it properly - doesn't work as it its path currently points to an indexentry
        # which doesn't exist ( move it to some path, it doesn't matter here )
        self.failUnlessRaises(InvalidGitRepositoryError, sm.move, pp)
        # reset the path(cache) to where it was, now it works
        sm.path = prep
        sm.move(fp, module=False)  # leave it at the old location

        assert not sm.module_exists()
        cpathchange = rwrepo.index.commit(
            "changed sm path")  # finally we can commit

        # update puts the module into place
        rm.update(recursive=False, progress=prog)
        sm.set_parent_commit(cpathchange)
        assert sm.module_exists()

        # add submodule
        #================
        nsmn = "newsubmodule"
        nsmp = "submrepo"
        subrepo_url = Git.polish_url(
            osp.join(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()
        # in our case, the module should not exist, which happens if we update a parent
        # repo and a new submodule comes into life
        nsm.remove(configuration=False, module=True)
        assert not nsm.module_exists() and nsm.exists()

        # dry-run does nothing
        rm.update(recursive=False, dry_run=True, progress=prog)

        # otherwise it will work
        rm.update(recursive=False, progress=prog)
        assert nsm.module_exists()

        # remove submodule - the previous one
        #====================================
        sm.set_parent_commit(csmadded)
        smp = sm.abspath
        assert not sm.remove(module=False).exists()
        assert osp.isdir(smp)  # module still exists
        csmremoved = rwrepo.index.commit("Removed submodule")

        # an update will remove the module
        # not in dry_run
        rm.update(recursive=False, dry_run=True, force_remove=True)
        assert osp.isdir(smp)

        # 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 osp.isdir(smp)

        # '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(osp.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 = Git.polish_url(
            osp.join(self.rorepo.working_tree_dir, rsmsp[0]))
        with nsm.config_writer() as writer:
            writer.set_value('url', nsmurl)
        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, force_reset=True)
        assert nsm.module().remotes.origin.url == nsmurl
        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
        # beforehand, update our instance's binsha with the new one
        nsm.binsha = nsm.module().head.commit.binsha
        rwrepo.index.add([nsm])

        # change branch
        #=================
        # we only have one branch, so we switch to a virtual one, and back
        # to the current one to trigger the difference
        cur_branch = nsm.branch
        nsmm = nsm.module()
        prev_commit = nsmm.head.commit
        for branch in ("some_virtual_branch", cur_branch.name):
            with nsm.config_writer() as writer:
                writer.set_value(Submodule.k_head_option,
                                 git.Head.to_full_path(branch))
            csmbranchchange = rwrepo.index.commit("changed branch to %s" %
                                                  branch)
            nsm.set_parent_commit(csmbranchchange)
        # END for each branch to change

        # Lets remove our tracking branch to simulate some changes
        nsmmh = nsmm.head
        assert nsmmh.ref.tracking_branch() is None  # never set it up until now
        assert not nsmmh.is_detached

        # dry run does nothing
        rm.update(recursive=False, dry_run=True, progress=prog)
        assert nsmmh.ref.tracking_branch() is None

        # the real thing does
        rm.update(recursive=False, progress=prog)

        assert nsmmh.ref.tracking_branch() is not None
        assert not nsmmh.is_detached

        # recursive update
        # =================
        # finally we recursively update a module, just to run the code at least once
        # remove the module so that it has more work
        assert len(nsm.children()) >= 1  # could include smmap
        assert nsm.exists() and nsm.module_exists() and len(
            nsm.children()) >= 1
        # assure we pull locally only
        nsmc = nsm.children()[0]
        with nsmc.config_writer() as writer:
            writer.set_value('url', subrepo_url)
        rm.update(recursive=True, progress=prog,
                  dry_run=True)  # just to run the code
        rm.update(recursive=True, progress=prog)

        # gitdb: has either 1 or 2 submodules depending on the version
        assert len(nsm.children()) >= 1 and nsmc.module_exists()
    def test_git_submodule_compatibility(self, rwdir):
        parent = git.Repo.init(osp.join(rwdir, 'parent'))
        sm_path = join_path_native('submodules', 'intermediate', 'one')
        sm = parent.create_submodule('mymodules/myname', sm_path, url=self._small_repo_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 = osp.join(sm.module().working_tree_dir, '.git')
        assert module_repo_path.startswith(osp.join(parent.working_tree_dir, sm_path))
        if not sm._need_gitfile_submodules(parent.git):
            assert osp.isdir(module_repo_path)
            assert not sm.module().has_separate_working_tree()
        else:
            assert osp.isfile(module_repo_path)
            assert sm.module().has_separate_working_tree()
            assert find_submodule_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 = join_path_native('submodules', 'one')
        sm.move(new_sm_path)
        assert_exists(sm)

        # Add additional submodule level
        csm = sm.module().create_submodule('nested-submodule', join_path_native('nested-submodule', 'working-tree'),
                                           url=self._small_repo_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)
        with csm.config_writer().set_value('url', 'bar'):
            pass
        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 osp.isdir(sm_module_path) == dry_run
Exemple #53
0
    def test_git_submodule_compatibility(self, rwdir):
        parent = git.Repo.init(osp.join(rwdir, 'parent'))
        sm_path = join_path_native('submodules', 'intermediate', 'one')
        sm = parent.create_submodule('mymodules/myname',
                                     sm_path,
                                     url=self._small_repo_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 = osp.join(sm.module().working_tree_dir, '.git')
        assert module_repo_path.startswith(
            osp.join(parent.working_tree_dir, sm_path))
        if not sm._need_gitfile_submodules(parent.git):
            assert osp.isdir(module_repo_path)
            assert not sm.module().has_separate_working_tree()
        else:
            assert osp.isfile(module_repo_path)
            assert sm.module().has_separate_working_tree()
            assert find_submodule_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 = join_path_native('submodules', 'one')
        sm.move(new_sm_path)
        assert_exists(sm)

        # Add additional submodule level
        csm = sm.module().create_submodule('nested-submodule',
                                           join_path_native(
                                               'nested-submodule',
                                               'working-tree'),
                                           url=self._small_repo_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)
        with csm.config_writer().set_value('url', 'bar'):
            pass
        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 osp.isdir(sm_module_path) == dry_run