summaryrefslogtreecommitdiff
path: root/test/git/test_index.py
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2009-10-26 23:24:56 +0100
committerSebastian Thiel <byronimo@gmail.com>2009-10-26 23:24:56 +0100
commit2792e534dd55fe03bca302f87a3ea638a7278bf1 (patch)
tree28d8f1cc81d8d121a9976204ee10be22996f6e2d /test/git/test_index.py
parent1b89f39432cdb395f5fbb9553b56595d29e2b773 (diff)
parent0ef1f89abe5b2334705ee8f1a6da231b0b6c9a50 (diff)
downloadgitpython-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.py243
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])
+