diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2009-10-26 23:24:56 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2009-10-26 23:24:56 +0100 |
commit | 2792e534dd55fe03bca302f87a3ea638a7278bf1 (patch) | |
tree | 28d8f1cc81d8d121a9976204ee10be22996f6e2d /test/git/test_index.py | |
parent | 1b89f39432cdb395f5fbb9553b56595d29e2b773 (diff) | |
parent | 0ef1f89abe5b2334705ee8f1a6da231b0b6c9a50 (diff) | |
download | gitpython-2792e534dd55fe03bca302f87a3ea638a7278bf1.tar.gz |
Merge branch 'index' into improvements
* index:
index.add: Finished implemenation including through tests
When parsing trees, we now store the originan type bits as well, previously we dropped it
cmd.wait: AutoKill wrapped process will automatically raise on errors to unify error handling amongst clients using the process directly. It might be needed to add a flag allowing to easily override that
added head kwarg to reset and commit method, allowing to automatically change the head to the given commit, which makes the methods more versatile
refs.SymoblicRef: implemented direcft setting of the symbolic references commit, which possibly dereferences to the respective head
index.commit: implemented initial version, but in fact some more changes are required to have a nice API. Tests are not yet fully done either
actor: added __eq__, __ne__ and __hash__ methods including simple test
index.remove implemented including throrough test
Implemented index.reset method including test
IndexEntry is now based on a 'minimal' version that is suitable to be fed into UpdateIndex. The Inode and device information is only needed to quickly compare the index against the working tree for changes, hence it should not be that dominant in the API either. More changes to come
Added notes about git-update-ref
Refs can now set the reference they are pointing to in a controlled fashion by writing their ref file directly
Added TagRefernce creation and deletion including tests
Implemented head methods: create, delete, rename, including tests
refs: added create, delete and rename methods where appropriate. Tests are marked, implementation is needed for most of them
Added frame for IndexFile add/remove/commit methods and respective test markers
Added repo.index property including simple test, and additional ideas in the TODO list
Renamed Index to IndexFile, adjusted tests, it will only operate on physical files, not on streams, as Indices are not streamed by any git command ( at least not in raw format )
Diffstat (limited to 'test/git/test_index.py')
-rw-r--r-- | test/git/test_index.py | 243 |
1 files changed, 226 insertions, 17 deletions
diff --git a/test/git/test_index.py b/test/git/test_index.py index 7bc2ad7e..3312abe1 100644 --- a/test/git/test_index.py +++ b/test/git/test_index.py @@ -8,13 +8,16 @@ from test.testlib import * from git import * import inspect import os +import sys import tempfile +import glob +from stat import * class TestTree(TestBase): - def test_base(self): + def test_index_file_base(self): # read from file - index = Index.from_file(self.rorepo, fixture_path("index")) + index = IndexFile(self.rorepo, fixture_path("index")) assert index.entries assert index.version > 0 @@ -26,21 +29,22 @@ class TestTree(TestBase): val = getattr(entry, attr) # END for each method + # test update + entries = index.entries + assert isinstance(index.update(), IndexFile) + assert entries is not index.entries + # test stage - index_merge = Index.from_file(self.rorepo, fixture_path("index_merge")) + index_merge = IndexFile(self.rorepo, fixture_path("index_merge")) assert len(index_merge.entries) == 106 assert len(list(e for e in index_merge.entries.itervalues() if e.stage != 0 )) # write the data - it must match the original - index_output = os.tmpfile() - index_merge.write(index_output) - - index_output.seek(0) - assert index_output.read() == fixture("index_merge") - tmpfile = tempfile.mktemp() - Index.to_file(index_merge, tmpfile) - assert os.path.isfile(tmpfile) + index_merge.write(tmpfile) + fp = open(tmpfile, 'r') + assert fp.read() == fixture("index_merge") + fp.close() os.remove(tmpfile) def _cmp_tree_index(self, tree, index): @@ -55,23 +59,23 @@ class TestTree(TestBase): # END for each blob in tree assert num_blobs == len(index.entries) - def test_from_tree(self): + def test_index_file_from_tree(self): common_ancestor_sha = "5117c9c8a4d3af19a9958677e45cda9269de1541" cur_sha = "4b43ca7ff72d5f535134241e7c797ddc9c7a3573" other_sha = "39f85c4358b7346fee22169da9cad93901ea9eb9" # simple index from tree - base_index = Index.from_tree(self.rorepo, common_ancestor_sha) + base_index = IndexFile.from_tree(self.rorepo, common_ancestor_sha) assert base_index.entries self._cmp_tree_index(common_ancestor_sha, base_index) # merge two trees - its like a fast-forward - two_way_index = Index.from_tree(self.rorepo, common_ancestor_sha, cur_sha) + two_way_index = IndexFile.from_tree(self.rorepo, common_ancestor_sha, cur_sha) assert two_way_index.entries self._cmp_tree_index(cur_sha, two_way_index) # merge three trees - here we have a merge conflict - three_way_index = Index.from_tree(self.rorepo, common_ancestor_sha, cur_sha, other_sha) + three_way_index = IndexFile.from_tree(self.rorepo, common_ancestor_sha, cur_sha, other_sha) assert len(list(e for e in three_way_index.entries.values() if e.stage != 0)) @@ -102,9 +106,9 @@ class TestTree(TestBase): assert num_blobs == len(three_way_index.entries) @with_rw_repo('0.1.6') - def test_from_index_and_diff(self, rw_repo): + def test_index_file_diffing(self, rw_repo): # default Index instance points to our index - index = Index(rw_repo) + index = IndexFile(rw_repo) assert index.path is not None assert len(index.entries) @@ -144,3 +148,208 @@ class TestTree(TestBase): # against something unusual self.failUnlessRaises(ValueError, index.diff, int) + + # adjust the index to match an old revision + cur_branch = rw_repo.active_branch + cur_commit = cur_branch.commit + rev_head_parent = 'HEAD~1' + assert index.reset(rev_head_parent) is index + + assert cur_branch == rw_repo.active_branch + assert cur_commit == rw_repo.head.commit + + # there must be differences towards the working tree which is in the 'future' + assert index.diff(None) + + # reset the working copy as well to current head,to pull 'back' as well + new_data = "will be reverted" + file_path = os.path.join(rw_repo.git.git_dir, "CHANGES") + fp = open(file_path, "w") + fp.write(new_data) + fp.close() + index.reset(rev_head_parent, working_tree=True) + assert not index.diff(None) + assert cur_branch == rw_repo.active_branch + assert cur_commit == rw_repo.head.commit + fp = open(file_path) + try: + assert fp.read() != new_data + finally: + fp.close() + + # test full checkout + test_file = os.path.join(rw_repo.git.git_dir, "CHANGES") + os.remove(test_file) + index.checkout(None, force=True) + assert os.path.isfile(test_file) + + os.remove(test_file) + index.checkout(None, force=False) + assert os.path.isfile(test_file) + + # individual file + os.remove(test_file) + index.checkout(test_file) + assert os.path.exists(test_file) + + + + # currently it ignore non-existing paths + index.checkout(paths=["doesnt/exist"]) + + + def _count_existing(self, repo, files): + existing = 0 + basedir = repo.git.git_dir + for f in files: + existing += os.path.isfile(os.path.join(basedir, f)) + # END for each deleted file + return existing + # END num existing helper + + + def _make_file(self, rela_path, data, repo=None): + """ + Create a file at the given path relative to our repository, filled + with the given data. Returns absolute path to created file. + """ + repo = repo or self.rorepo + abs_path = os.path.join(repo.git.git_dir, rela_path) + fp = open(abs_path, "w") + fp.write(data) + fp.close() + return abs_path + + @with_rw_repo('0.1.6') + def test_index_mutation(self, rw_repo): + index = rw_repo.index + num_entries = len(index.entries) + cur_head = rw_repo.head + + # remove all of the files, provide a wild mix of paths, BaseIndexEntries, + # IndexEntries + def mixed_iterator(): + count = 0 + for entry in index.entries.itervalues(): + type_id = count % 4 + if type_id == 0: # path + yield entry.path + elif type_id == 1: # blob + yield Blob(rw_repo, entry.sha, entry.mode, entry.path) + elif type_id == 2: # BaseIndexEntry + yield BaseIndexEntry(entry[:4]) + elif type_id == 3: # IndexEntry + yield entry + else: + raise AssertionError("Invalid Type") + count += 1 + # END for each entry + # END mixed iterator + deleted_files = index.remove(mixed_iterator(), working_tree=False) + assert deleted_files + assert self._count_existing(rw_repo, deleted_files) == len(deleted_files) + assert len(index.entries) == 0 + + # reset the index to undo our changes + index.reset() + assert len(index.entries) == num_entries + + # remove with working copy + deleted_files = index.remove(mixed_iterator(), working_tree=True) + assert deleted_files + assert self._count_existing(rw_repo, deleted_files) == 0 + + # reset everything + index.reset(working_tree=True) + assert self._count_existing(rw_repo, deleted_files) == len(deleted_files) + + # invalid type + self.failUnlessRaises(TypeError, index.remove, [1]) + + # absolute path + deleted_files = index.remove([os.path.join(rw_repo.git.git_dir,"lib")], r=True) + assert len(deleted_files) > 1 + self.failUnlessRaises(ValueError, index.remove, ["/doesnt/exists"]) + + # TEST COMMITTING + # commit changed index + cur_commit = cur_head.commit + commit_message = "commit default head" + + new_commit = index.commit(commit_message, head=False) + assert new_commit.message == commit_message + assert new_commit.parents[0] == cur_commit + assert len(new_commit.parents) == 1 + assert cur_head.commit == cur_commit + + # same index, no parents + commit_message = "index without parents" + commit_no_parents = index.commit(commit_message, parent_commits=list(), head=True) + assert commit_no_parents.message == commit_message + assert len(commit_no_parents.parents) == 0 + assert cur_head.commit == commit_no_parents + + # same index, multiple parents + commit_message = "Index with multiple parents\n commit with another line" + commit_multi_parent = index.commit(commit_message,parent_commits=(commit_no_parents, new_commit)) + assert commit_multi_parent.message == commit_message + assert len(commit_multi_parent.parents) == 2 + assert commit_multi_parent.parents[0] == commit_no_parents + assert commit_multi_parent.parents[1] == new_commit + assert cur_head.commit == commit_multi_parent + + # re-add all files in lib + # get the lib folder back on disk, but get an index without it + index.reset(new_commit.parents[0], working_tree=True).reset(new_commit, working_tree=False) + lib_file_path = "lib/git/__init__.py" + assert (lib_file_path, 0) not in index.entries + assert os.path.isfile(os.path.join(rw_repo.git.git_dir, lib_file_path)) + + # directory + entries = index.add(['lib']) + assert len(entries)>1 + + # glob + entries = index.reset(new_commit).add(['lib/*.py']) + assert len(entries) == 14 + + # missing path + self.failUnlessRaises(GitCommandError, index.reset(new_commit).add, ['doesnt/exist/must/raise']) + + # blob from older revision overrides current index revision + old_blob = new_commit.parents[0].tree.blobs[0] + entries = index.reset(new_commit).add([old_blob]) + assert index.entries[(old_blob.path,0)].sha == old_blob.id and len(entries) == 1 + + # mode 0 not allowed + null_sha = "0"*40 + self.failUnlessRaises(ValueError, index.reset(new_commit).add, [BaseIndexEntry((0, null_sha,0,"doesntmatter"))]) + + # add new file + new_file_relapath = "my_new_file" + new_file_path = self._make_file(new_file_relapath, "hello world", rw_repo) + entries = index.reset(new_commit).add([BaseIndexEntry((010644, null_sha, 0, new_file_relapath))]) + assert len(entries) == 1 and entries[0].sha != null_sha + + # add symlink + if sys.platform != "win32": + link_file = os.path.join(rw_repo.git.git_dir, "my_real_symlink") + os.symlink("/etc/that", link_file) + entries = index.reset(new_commit).add([link_file]) + assert len(entries) == 1 and S_ISLNK(entries[0].mode) + print "%o" % entries[0].mode + # END real symlink test + + # add fake symlink and assure it checks-our as symlink + fake_symlink_relapath = "my_fake_symlink" + fake_symlink_path = self._make_file(fake_symlink_relapath, "/etc/that", rw_repo) + fake_entry = BaseIndexEntry((0120000, null_sha, 0, fake_symlink_relapath)) + entries = index.reset(new_commit).add([fake_entry]) + assert len(entries) == 1 and S_ISLNK(entries[0].mode) + + # checkout the fakelink, should be a link then + assert not S_ISLNK(os.stat(fake_symlink_path)[ST_MODE]) + os.remove(fake_symlink_path) + index.checkout(fake_symlink_path) + assert S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE]) + |