def test_commit_msg_hook_fail(self, rw_repo): index = rw_repo.index hp = hook_path('commit-msg', index.repo.git_dir) hpd = osp.dirname(hp) if not osp.isdir(hpd): os.mkdir(hpd) with open(hp, "wt") as fp: fp.write( "#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1") os.chmod(hp, 0o744) try: index.commit("This should fail") except HookExecutionError as err: if is_win: self.assertIsInstance(err.status, OSError) self.assertEqual(err.command, [hp]) self.assertEqual(err.stdout, '') self.assertEqual(err.stderr, '') assert str(err) else: self.assertEqual(err.status, 1) self.assertEqual(err.command, [hp]) self.assertEqual(err.stdout, "\n stdout: 'stdout\n'") self.assertEqual(err.stderr, "\n stderr: 'stderr\n'") assert str(err) else: raise AssertionError("Should have cought a HookExecutionError")
def test_pre_commit_hook_success(self, rw_repo): index = rw_repo.index hp = hook_path('pre-commit', index.repo.git_dir) hpd = osp.dirname(hp) if not osp.isdir(hpd): os.mkdir(hpd) with open(hp, "wt") as fp: fp.write("#!/usr/bin/env sh\nexit 0") os.chmod(hp, 0o744) index.commit("This should not fail")
def _make_hook(git_dir, name, content, make_exec=True): """A helper to create a hook""" hp = hook_path(name, git_dir) hpd = osp.dirname(hp) if not osp.isdir(hpd): os.mkdir(hpd) with open(hp, "wt") as fp: fp.write(HOOKS_SHEBANG + content) if make_exec: os.chmod(hp, 0o744) return hp
def test_commit_msg_hook_success(self, rw_repo): index = rw_repo.index commit_message = u"commit default head by Frèderic Çaufl€" from_hook_message = u"from commit-msg" hp = hook_path('commit-msg', index.repo.git_dir) hpd = osp.dirname(hp) if not osp.isdir(hpd): os.mkdir(hpd) with open(hp, "wt") as fp: fp.write('#!/usr/bin/env sh\necho -n " {}" >> "$1"'.format( from_hook_message)) os.chmod(hp, 0o744) new_commit = index.commit(commit_message) self.assertEqual(new_commit.message, u"{} {}".format(commit_message, from_hook_message))
def test_index_mutation(self, rw_repo): index = rw_repo.index num_entries = len(index.entries) cur_head = rw_repo.head uname = u"Thomas Müller" umail = "*****@*****.**" writer = rw_repo.config_writer() writer.set_value("user", "name", uname) writer.set_value("user", "email", umail) writer.release() assert writer.get_value("user", "name") == uname # remove all of the files, provide a wild mix of paths, BaseIndexEntries, # IndexEntries def mixed_iterator(): count = 0 for entry in index.entries.values(): type_id = count % 4 if type_id == 0: # path yield entry.path elif type_id == 1: # blob yield Blob(rw_repo, entry.binsha, 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.working_tree_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 = u"commit default head by Frèderic Çaufl€" new_commit = index.commit(commit_message, head=False) assert cur_commit != new_commit assert new_commit.author.name == uname assert new_commit.author.email == umail assert new_commit.committer.name == uname assert new_commit.committer.email == umail 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 # commit with other actor cur_commit = cur_head.commit my_author = Actor(u"Frèderic Çaufl€", "*****@*****.**") my_committer = Actor(u"Committing Frèderic Çaufl€", "*****@*****.**") commit_actor = index.commit(commit_message, author=my_author, committer=my_committer) assert cur_commit != commit_actor assert commit_actor.author.name == u"Frèderic Çaufl€" assert commit_actor.author.email == "*****@*****.**" assert commit_actor.committer.name == u"Committing Frèderic Çaufl€" assert commit_actor.committer.email == "*****@*****.**" assert commit_actor.message == commit_message assert commit_actor.parents[0] == cur_commit assert len(new_commit.parents) == 1 assert cur_head.commit == commit_actor assert cur_head.log()[-1].actor == my_committer # commit with author_date and commit_date cur_commit = cur_head.commit commit_message = u"commit with dates by Avinash Sajjanshetty" new_commit = index.commit(commit_message, author_date="2006-04-07T22:13:13", commit_date="2005-04-07T22:13:13") assert cur_commit != new_commit print(new_commit.authored_date, new_commit.committed_date) assert new_commit.message == commit_message assert new_commit.authored_date == 1144447993 assert new_commit.committed_date == 1112911993 # 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 = os.path.join("lib", "git", "__init__.py") assert (lib_file_path, 0) not in index.entries assert os.path.isfile( os.path.join(rw_repo.working_tree_dir, lib_file_path)) # directory entries = index.add(['lib'], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) > 1 # glob entries = index.reset(new_commit).add( [os.path.join('lib', 'git', '*.py')], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) == 14 # same file entries = index.reset(new_commit).add( [os.path.join(rw_repo.working_tree_dir, 'lib', 'git', 'head.py')] * 2, fprogress=self._fprogress_add) self._assert_entries(entries) assert entries[0].mode & 0o644 == 0o644 # would fail, test is too primitive to handle this case # self._assert_fprogress(entries) self._reset_progress() assert len(entries) == 2 # missing path self.failUnlessRaises(OSError, 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], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert index.entries[( old_blob.path, 0)].hexsha == old_blob.hexsha and len(entries) == 1 # mode 0 not allowed null_hex_sha = Diff.NULL_HEX_SHA null_bin_sha = b"\0" * 20 self.failUnlessRaises( ValueError, index.reset(new_commit).add, [BaseIndexEntry((0, null_bin_sha, 0, "doesntmatter"))]) # add new file new_file_relapath = "my_new_file" self._make_file(new_file_relapath, "hello world", rw_repo) entries = index.reset(new_commit).add( [BaseIndexEntry((0o10644, null_bin_sha, 0, new_file_relapath))], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) == 1 and entries[0].hexsha != null_hex_sha # add symlink if sys.platform != "win32": for target in ('/etc/nonexisting', '/etc/passwd', '/etc'): basename = "my_real_symlink" link_file = os.path.join(rw_repo.working_tree_dir, basename) os.symlink(target, link_file) entries = index.reset(new_commit).add( [link_file], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) == 1 and S_ISLNK(entries[0].mode) assert S_ISLNK(index.entries[index.entry_key( "my_real_symlink", 0)].mode) # we expect only the target to be written assert index.repo.odb.stream( entries[0].binsha).read().decode('ascii') == target os.remove(link_file) # end for each target # END real symlink test # add fake symlink and assure it checks-our as symlink fake_symlink_relapath = "my_fake_symlink" link_target = "/etc/that" fake_symlink_path = self._make_file(fake_symlink_relapath, link_target, rw_repo) fake_entry = BaseIndexEntry( (0o120000, null_bin_sha, 0, fake_symlink_relapath)) entries = index.reset(new_commit).add([fake_entry], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert entries[0].hexsha != null_hex_sha assert len(entries) == 1 and S_ISLNK(entries[0].mode) # assure this also works with an alternate method full_index_entry = IndexEntry.from_base( BaseIndexEntry((0o120000, entries[0].binsha, 0, entries[0].path))) entry_key = index.entry_key(full_index_entry) index.reset(new_commit) assert entry_key not in index.entries index.entries[entry_key] = full_index_entry index.write() index.update() # force reread of entries new_entry = index.entries[entry_key] assert S_ISLNK(new_entry.mode) # a tree created from this should contain the symlink tree = index.write_tree() assert fake_symlink_relapath in tree index.write() # flush our changes for the checkout # 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) # on windows we will never get symlinks if os.name == 'nt': # simlinks should contain the link as text ( which is what a # symlink actually is ) open(fake_symlink_path, 'rb').read() == link_target else: assert S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE]) # TEST RENAMING def assert_mv_rval(rval): for source, dest in rval: assert not os.path.exists(source) and os.path.exists(dest) # END for each renamed item # END move assertion utility self.failUnlessRaises(ValueError, index.move, ['just_one_path']) # file onto existing file files = ['AUTHORS', 'LICENSE'] self.failUnlessRaises(GitCommandError, index.move, files) # again, with force assert_mv_rval(index.move(files, f=True)) # files into directory - dry run paths = ['LICENSE', 'VERSION', 'doc'] rval = index.move(paths, dry_run=True) assert len(rval) == 2 assert os.path.exists(paths[0]) # again, no dry run rval = index.move(paths) assert_mv_rval(rval) # dir into dir rval = index.move(['doc', 'test']) assert_mv_rval(rval) # TEST PATH REWRITING ###################### count = [0] def rewriter(entry): rval = str(count[0]) count[0] += 1 return rval # END rewriter def make_paths(): # two existing ones, one new one yield 'CHANGES' yield 'ez_setup.py' yield index.entries[index.entry_key('README', 0)] yield index.entries[index.entry_key('.gitignore', 0)] for fid in range(3): fname = 'newfile%i' % fid open(fname, 'wb').write(b"abcd") yield Blob(rw_repo, Blob.NULL_BIN_SHA, 0o100644, fname) # END for each new file # END path producer paths = list(make_paths()) self._assert_entries(index.add(paths, path_rewriter=rewriter)) for filenum in range(len(paths)): assert index.entry_key(str(filenum), 0) in index.entries # TEST RESET ON PATHS ###################### arela = "aa" brela = "bb" afile = self._make_file(arela, "adata", rw_repo) bfile = self._make_file(brela, "bdata", rw_repo) akey = index.entry_key(arela, 0) bkey = index.entry_key(brela, 0) keys = (akey, bkey) absfiles = (afile, bfile) files = (arela, brela) for fkey in keys: assert fkey not in index.entries index.add(files, write=True) if os.name != 'nt': hp = hook_path('pre-commit', index.repo.git_dir) with open(hp, "wt") as fp: fp.write( "#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1") # end os.chmod(hp, 0o544) try: index.commit("This should fail") except HookExecutionError as err: assert err.status == 1 assert err.command == hp assert err.stdout == 'stdout\n' assert err.stderr == 'stderr\n' assert str(err) else: raise AssertionError("Should have cought a HookExecutionError") # end exception handling os.remove(hp) # end hook testing nc = index.commit("2 files committed", head=False) for fkey in keys: assert fkey in index.entries # just the index index.reset(paths=(arela, afile)) assert akey not in index.entries assert bkey in index.entries # now with working tree - files on disk as well as entries must be recreated rw_repo.head.commit = nc for absfile in absfiles: os.remove(absfile) index.reset(working_tree=True, paths=files) for fkey in keys: assert fkey in index.entries for absfile in absfiles: assert os.path.isfile(absfile)
def test_index_mutation(self, rw_repo): index = rw_repo.index num_entries = len(index.entries) cur_head = rw_repo.head uname = u"Thomas Müller" umail = "*****@*****.**" with rw_repo.config_writer() as writer: writer.set_value("user", "name", uname) writer.set_value("user", "email", umail) self.assertEqual(writer.get_value("user", "name"), uname) # remove all of the files, provide a wild mix of paths, BaseIndexEntries, # IndexEntries def mixed_iterator(): count = 0 for entry in index.entries.values(): type_id = count % 4 if type_id == 0: # path yield entry.path elif type_id == 1: # blob yield Blob(rw_repo, entry.binsha, 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 self.assertEqual(self._count_existing(rw_repo, deleted_files), len(deleted_files)) self.assertEqual(len(index.entries), 0) # reset the index to undo our changes index.reset() self.assertEqual(len(index.entries), num_entries) # remove with working copy deleted_files = index.remove(mixed_iterator(), working_tree=True) assert deleted_files self.assertEqual(self._count_existing(rw_repo, deleted_files), 0) # reset everything index.reset(working_tree=True) self.assertEqual(self._count_existing(rw_repo, deleted_files), len(deleted_files)) # invalid type self.failUnlessRaises(TypeError, index.remove, [1]) # absolute path deleted_files = index.remove([osp.join(rw_repo.working_tree_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 = u"commit default head by Frèderic Çaufl€" new_commit = index.commit(commit_message, head=False) assert cur_commit != new_commit self.assertEqual(new_commit.author.name, uname) self.assertEqual(new_commit.author.email, umail) self.assertEqual(new_commit.committer.name, uname) self.assertEqual(new_commit.committer.email, umail) self.assertEqual(new_commit.message, commit_message) self.assertEqual(new_commit.parents[0], cur_commit) self.assertEqual(len(new_commit.parents), 1) self.assertEqual(cur_head.commit, cur_commit) # commit with other actor cur_commit = cur_head.commit my_author = Actor(u"Frèderic Çaufl€", "*****@*****.**") my_committer = Actor(u"Committing Frèderic Çaufl€", "*****@*****.**") commit_actor = index.commit(commit_message, author=my_author, committer=my_committer) assert cur_commit != commit_actor self.assertEqual(commit_actor.author.name, u"Frèderic Çaufl€") self.assertEqual(commit_actor.author.email, "*****@*****.**") self.assertEqual(commit_actor.committer.name, u"Committing Frèderic Çaufl€") self.assertEqual(commit_actor.committer.email, "*****@*****.**") self.assertEqual(commit_actor.message, commit_message) self.assertEqual(commit_actor.parents[0], cur_commit) self.assertEqual(len(new_commit.parents), 1) self.assertEqual(cur_head.commit, commit_actor) self.assertEqual(cur_head.log()[-1].actor, my_committer) # commit with author_date and commit_date cur_commit = cur_head.commit commit_message = u"commit with dates by Avinash Sajjanshetty" new_commit = index.commit(commit_message, author_date="2006-04-07T22:13:13", commit_date="2005-04-07T22:13:13") assert cur_commit != new_commit print(new_commit.authored_date, new_commit.committed_date) self.assertEqual(new_commit.message, commit_message) self.assertEqual(new_commit.authored_date, 1144447993) self.assertEqual(new_commit.committed_date, 1112911993) # same index, no parents commit_message = "index without parents" commit_no_parents = index.commit(commit_message, parent_commits=list(), head=True) self.assertEqual(commit_no_parents.message, commit_message) self.assertEqual(len(commit_no_parents.parents), 0) self.assertEqual(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)) self.assertEqual(commit_multi_parent.message, commit_message) self.assertEqual(len(commit_multi_parent.parents), 2) self.assertEqual(commit_multi_parent.parents[0], commit_no_parents) self.assertEqual(commit_multi_parent.parents[1], new_commit) self.assertEqual(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 = osp.join("lib", "git", "__init__.py") assert (lib_file_path, 0) not in index.entries assert osp.isfile(osp.join(rw_repo.working_tree_dir, lib_file_path)) # directory entries = index.add(['lib'], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) > 1 # glob entries = index.reset(new_commit).add([osp.join('lib', 'git', '*.py')], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) self.assertEqual(len(entries), 14) # same file entries = index.reset(new_commit).add( [osp.join(rw_repo.working_tree_dir, 'lib', 'git', 'head.py')] * 2, fprogress=self._fprogress_add) self._assert_entries(entries) self.assertEqual(entries[0].mode & 0o644, 0o644) # would fail, test is too primitive to handle this case # self._assert_fprogress(entries) self._reset_progress() self.assertEqual(len(entries), 2) # missing path self.failUnlessRaises(OSError, 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], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) self.assertEqual(index.entries[(old_blob.path, 0)].hexsha, old_blob.hexsha) self.assertEqual(len(entries), 1) # mode 0 not allowed null_hex_sha = Diff.NULL_HEX_SHA null_bin_sha = b"\0" * 20 self.failUnlessRaises(ValueError, index.reset( new_commit).add, [BaseIndexEntry((0, null_bin_sha, 0, "doesntmatter"))]) # add new file new_file_relapath = "my_new_file" self._make_file(new_file_relapath, "hello world", rw_repo) entries = index.reset(new_commit).add( [BaseIndexEntry((0o10644, null_bin_sha, 0, new_file_relapath))], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) self.assertEqual(len(entries), 1) self.assertNotEquals(entries[0].hexsha, null_hex_sha) # add symlink if not is_win: for target in ('/etc/nonexisting', '/etc/passwd', '/etc'): basename = "my_real_symlink" link_file = osp.join(rw_repo.working_tree_dir, basename) os.symlink(target, link_file) entries = index.reset(new_commit).add([link_file], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) self.assertEqual(len(entries), 1) self.assertTrue(S_ISLNK(entries[0].mode)) self.assertTrue(S_ISLNK(index.entries[index.entry_key("my_real_symlink", 0)].mode)) # we expect only the target to be written self.assertEqual(index.repo.odb.stream(entries[0].binsha).read().decode('ascii'), target) os.remove(link_file) # end for each target # END real symlink test # add fake symlink and assure it checks-our as symlink fake_symlink_relapath = "my_fake_symlink" link_target = "/etc/that" fake_symlink_path = self._make_file(fake_symlink_relapath, link_target, rw_repo) fake_entry = BaseIndexEntry((0o120000, null_bin_sha, 0, fake_symlink_relapath)) entries = index.reset(new_commit).add([fake_entry], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert entries[0].hexsha != null_hex_sha self.assertEqual(len(entries), 1) self.assertTrue(S_ISLNK(entries[0].mode)) # assure this also works with an alternate method full_index_entry = IndexEntry.from_base(BaseIndexEntry((0o120000, entries[0].binsha, 0, entries[0].path))) entry_key = index.entry_key(full_index_entry) index.reset(new_commit) assert entry_key not in index.entries index.entries[entry_key] = full_index_entry index.write() index.update() # force reread of entries new_entry = index.entries[entry_key] assert S_ISLNK(new_entry.mode) # a tree created from this should contain the symlink tree = index.write_tree() assert fake_symlink_relapath in tree index.write() # flush our changes for the checkout # 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) # on windows we will never get symlinks if is_win: # simlinks should contain the link as text ( which is what a # symlink actually is ) with open(fake_symlink_path, 'rt') as fd: self.assertEqual(fd.read(), link_target) else: self.assertTrue(S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE])) # TEST RENAMING def assert_mv_rval(rval): for source, dest in rval: assert not osp.exists(source) and osp.exists(dest) # END for each renamed item # END move assertion utility self.failUnlessRaises(ValueError, index.move, ['just_one_path']) # file onto existing file files = ['AUTHORS', 'LICENSE'] self.failUnlessRaises(GitCommandError, index.move, files) # again, with force assert_mv_rval(index.move(files, f=True)) # files into directory - dry run paths = ['LICENSE', 'VERSION', 'doc'] rval = index.move(paths, dry_run=True) self.assertEqual(len(rval), 2) assert osp.exists(paths[0]) # again, no dry run rval = index.move(paths) assert_mv_rval(rval) # dir into dir rval = index.move(['doc', 'test']) assert_mv_rval(rval) # TEST PATH REWRITING ###################### count = [0] def rewriter(entry): rval = str(count[0]) count[0] += 1 return rval # END rewriter def make_paths(): # two existing ones, one new one yield 'CHANGES' yield 'ez_setup.py' yield index.entries[index.entry_key('README', 0)] yield index.entries[index.entry_key('.gitignore', 0)] for fid in range(3): fname = 'newfile%i' % fid with open(fname, 'wb') as fd: fd.write(b"abcd") yield Blob(rw_repo, Blob.NULL_BIN_SHA, 0o100644, fname) # END for each new file # END path producer paths = list(make_paths()) self._assert_entries(index.add(paths, path_rewriter=rewriter)) for filenum in range(len(paths)): assert index.entry_key(str(filenum), 0) in index.entries # TEST RESET ON PATHS ###################### arela = "aa" brela = "bb" afile = self._make_file(arela, "adata", rw_repo) bfile = self._make_file(brela, "bdata", rw_repo) akey = index.entry_key(arela, 0) bkey = index.entry_key(brela, 0) keys = (akey, bkey) absfiles = (afile, bfile) files = (arela, brela) for fkey in keys: assert fkey not in index.entries index.add(files, write=True) if is_win: hp = hook_path('pre-commit', index.repo.git_dir) hpd = osp.dirname(hp) if not osp.isdir(hpd): os.mkdir(hpd) with open(hp, "wt") as fp: fp.write("#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1") # end os.chmod(hp, 0o744) try: index.commit("This should fail") except HookExecutionError as err: if is_win: self.assertIsInstance(err.status, OSError) self.assertEqual(err.command, [hp]) self.assertEqual(err.stdout, '') self.assertEqual(err.stderr, '') assert str(err) else: self.assertEqual(err.status, 1) self.assertEqual(err.command, hp) self.assertEqual(err.stdout, 'stdout\n') self.assertEqual(err.stderr, 'stderr\n') assert str(err) else: raise AssertionError("Should have cought a HookExecutionError") # end exception handling os.remove(hp) # end hook testing nc = index.commit("2 files committed", head=False) for fkey in keys: assert fkey in index.entries # just the index index.reset(paths=(arela, afile)) assert akey not in index.entries assert bkey in index.entries # now with working tree - files on disk as well as entries must be recreated rw_repo.head.commit = nc for absfile in absfiles: os.remove(absfile) index.reset(working_tree=True, paths=files) for fkey in keys: assert fkey in index.entries for absfile in absfiles: assert osp.isfile(absfile)
def test_index_mutation(self, rw_repo): index = rw_repo.index num_entries = len(index.entries) cur_head = rw_repo.head uname = u"Thomas Müller" umail = "*****@*****.**" writer = rw_repo.config_writer() writer.set_value("user", "name", uname) writer.set_value("user", "email", umail) writer.release() assert writer.get_value("user", "name") == uname # remove all of the files, provide a wild mix of paths, BaseIndexEntries, # IndexEntries def mixed_iterator(): count = 0 for entry in index.entries.values(): type_id = count % 4 if type_id == 0: # path yield entry.path elif type_id == 1: # blob yield Blob(rw_repo, entry.binsha, 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.working_tree_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 = u"commit default head by Frèderic Çaufl€" new_commit = index.commit(commit_message, head=False) assert cur_commit != new_commit assert new_commit.author.name == uname assert new_commit.author.email == umail assert new_commit.committer.name == uname assert new_commit.committer.email == umail 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 # commit with other actor cur_commit = cur_head.commit my_author = Actor(u"Frèderic Çaufl€", "*****@*****.**") my_committer = Actor(u"Committing Frèderic Çaufl€", "*****@*****.**") commit_actor = index.commit(commit_message, author=my_author, committer=my_committer) assert cur_commit != commit_actor assert commit_actor.author.name == u"Frèderic Çaufl€" assert commit_actor.author.email == "*****@*****.**" assert commit_actor.committer.name == u"Committing Frèderic Çaufl€" assert commit_actor.committer.email == "*****@*****.**" assert commit_actor.message == commit_message assert commit_actor.parents[0] == cur_commit assert len(new_commit.parents) == 1 assert cur_head.commit == commit_actor assert cur_head.log()[-1].actor == my_committer # 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 = os.path.join("lib", "git", "__init__.py") assert (lib_file_path, 0) not in index.entries assert os.path.isfile(os.path.join(rw_repo.working_tree_dir, lib_file_path)) # directory entries = index.add(["lib"], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) > 1 # glob entries = index.reset(new_commit).add([os.path.join("lib", "git", "*.py")], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) == 14 # same file entries = index.reset(new_commit).add( [os.path.join(rw_repo.working_tree_dir, "lib", "git", "head.py")] * 2, fprogress=self._fprogress_add ) self._assert_entries(entries) assert entries[0].mode & 0o644 == 0o644 # would fail, test is too primitive to handle this case # self._assert_fprogress(entries) self._reset_progress() assert len(entries) == 2 # missing path self.failUnlessRaises(OSError, 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], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert index.entries[(old_blob.path, 0)].hexsha == old_blob.hexsha and len(entries) == 1 # mode 0 not allowed null_hex_sha = Diff.NULL_HEX_SHA null_bin_sha = b"\0" * 20 self.failUnlessRaises( ValueError, index.reset(new_commit).add, [BaseIndexEntry((0, null_bin_sha, 0, "doesntmatter"))] ) # add new file new_file_relapath = "my_new_file" self._make_file(new_file_relapath, "hello world", rw_repo) entries = index.reset(new_commit).add( [BaseIndexEntry((0o10644, null_bin_sha, 0, new_file_relapath))], fprogress=self._fprogress_add ) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) == 1 and entries[0].hexsha != null_hex_sha # add symlink if sys.platform != "win32": for target in ("/etc/nonexisting", "/etc/passwd", "/etc"): basename = "my_real_symlink" link_file = os.path.join(rw_repo.working_tree_dir, basename) os.symlink(target, link_file) entries = index.reset(new_commit).add([link_file], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert len(entries) == 1 and S_ISLNK(entries[0].mode) assert S_ISLNK(index.entries[index.entry_key("my_real_symlink", 0)].mode) # we expect only the target to be written assert index.repo.odb.stream(entries[0].binsha).read().decode("ascii") == target os.remove(link_file) # end for each target # END real symlink test # add fake symlink and assure it checks-our as symlink fake_symlink_relapath = "my_fake_symlink" link_target = "/etc/that" fake_symlink_path = self._make_file(fake_symlink_relapath, link_target, rw_repo) fake_entry = BaseIndexEntry((0o120000, null_bin_sha, 0, fake_symlink_relapath)) entries = index.reset(new_commit).add([fake_entry], fprogress=self._fprogress_add) self._assert_entries(entries) self._assert_fprogress(entries) assert entries[0].hexsha != null_hex_sha assert len(entries) == 1 and S_ISLNK(entries[0].mode) # assure this also works with an alternate method full_index_entry = IndexEntry.from_base(BaseIndexEntry((0o120000, entries[0].binsha, 0, entries[0].path))) entry_key = index.entry_key(full_index_entry) index.reset(new_commit) assert entry_key not in index.entries index.entries[entry_key] = full_index_entry index.write() index.update() # force reread of entries new_entry = index.entries[entry_key] assert S_ISLNK(new_entry.mode) # a tree created from this should contain the symlink tree = index.write_tree() assert fake_symlink_relapath in tree index.write() # flush our changes for the checkout # 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) # on windows we will never get symlinks if os.name == "nt": # simlinks should contain the link as text ( which is what a # symlink actually is ) open(fake_symlink_path, "rb").read() == link_target else: assert S_ISLNK(os.lstat(fake_symlink_path)[ST_MODE]) # TEST RENAMING def assert_mv_rval(rval): for source, dest in rval: assert not os.path.exists(source) and os.path.exists(dest) # END for each renamed item # END move assertion utility self.failUnlessRaises(ValueError, index.move, ["just_one_path"]) # file onto existing file files = ["AUTHORS", "LICENSE"] self.failUnlessRaises(GitCommandError, index.move, files) # again, with force assert_mv_rval(index.move(files, f=True)) # files into directory - dry run paths = ["LICENSE", "VERSION", "doc"] rval = index.move(paths, dry_run=True) assert len(rval) == 2 assert os.path.exists(paths[0]) # again, no dry run rval = index.move(paths) assert_mv_rval(rval) # dir into dir rval = index.move(["doc", "test"]) assert_mv_rval(rval) # TEST PATH REWRITING ###################### count = [0] def rewriter(entry): rval = str(count[0]) count[0] += 1 return rval # END rewriter def make_paths(): # two existing ones, one new one yield "CHANGES" yield "ez_setup.py" yield index.entries[index.entry_key("README", 0)] yield index.entries[index.entry_key(".gitignore", 0)] for fid in range(3): fname = "newfile%i" % fid open(fname, "wb").write(b"abcd") yield Blob(rw_repo, Blob.NULL_BIN_SHA, 0o100644, fname) # END for each new file # END path producer paths = list(make_paths()) self._assert_entries(index.add(paths, path_rewriter=rewriter)) for filenum in range(len(paths)): assert index.entry_key(str(filenum), 0) in index.entries # TEST RESET ON PATHS ###################### arela = "aa" brela = "bb" afile = self._make_file(arela, "adata", rw_repo) bfile = self._make_file(brela, "bdata", rw_repo) akey = index.entry_key(arela, 0) bkey = index.entry_key(brela, 0) keys = (akey, bkey) absfiles = (afile, bfile) files = (arela, brela) for fkey in keys: assert fkey not in index.entries index.add(files, write=True) if os.name != "nt": hp = hook_path("pre-commit", index.repo.git_dir) with open(hp, "wt") as fp: fp.write("#!/usr/bin/env sh\necho stdout; echo stderr 1>&2; exit 1") # end os.chmod(hp, 0o544) try: index.commit("This should fail") except HookExecutionError as err: assert err.status == 1 assert err.command == hp assert err.stdout == "stdout\n" assert err.stderr == "stderr\n" assert str(err) else: raise AssertionError("Should have cought a HookExecutionError") # end exception handling os.remove(hp) # end hook testing nc = index.commit("2 files committed", head=False) for fkey in keys: assert fkey in index.entries # just the index index.reset(paths=(arela, afile)) assert akey not in index.entries assert bkey in index.entries # now with working tree - files on disk as well as entries must be recreated rw_repo.head.commit = nc for absfile in absfiles: os.remove(absfile) index.reset(working_tree=True, paths=files) for fkey in keys: assert fkey in index.entries for absfile in absfiles: assert os.path.isfile(absfile)