1# Copyright (C) 2007 Canonical Ltd 2# Copyright (C) 2007-2018 Jelmer Vernooij <jelmer@jelmer.uk> 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 2 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program; if not, write to the Free Software 16# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 18"""Tests for interfacing with a Git Repository""" 19 20import dulwich 21from dulwich.repo import ( 22 Repo as GitRepo, 23 ) 24import os 25 26from ... import ( 27 config, 28 errors, 29 revision, 30 ) 31from ...repository import ( 32 InterRepository, 33 Repository, 34 ) 35 36from .. import ( 37 dir, 38 repository, 39 tests, 40 ) 41from ..mapping import ( 42 default_mapping, 43 ) 44from ..object_store import ( 45 BazaarObjectStore, 46 ) 47from ..push import ( 48 MissingObjectsIterator, 49 ) 50 51 52class TestGitRepositoryFeatures(tests.TestCaseInTempDir): 53 """Feature tests for GitRepository.""" 54 55 def _do_commit(self): 56 builder = tests.GitBranchBuilder() 57 builder.set_file(b'a', b'text for a\n', False) 58 commit_handle = builder.commit(b'Joe Foo <joe@foo.com>', b'message') 59 mapping = builder.finish() 60 return mapping[commit_handle] 61 62 def test_open_existing(self): 63 GitRepo.init(self.test_dir) 64 65 repo = Repository.open('.') 66 self.assertIsInstance(repo, repository.GitRepository) 67 68 def test_has_git_repo(self): 69 GitRepo.init(self.test_dir) 70 71 repo = Repository.open('.') 72 self.assertIsInstance(repo._git, dulwich.repo.BaseRepo) 73 74 def test_has_revision(self): 75 GitRepo.init(self.test_dir) 76 commit_id = self._do_commit() 77 repo = Repository.open('.') 78 self.assertFalse(repo.has_revision(b'foobar')) 79 revid = default_mapping.revision_id_foreign_to_bzr(commit_id) 80 self.assertTrue(repo.has_revision(revid)) 81 82 def test_has_revisions(self): 83 GitRepo.init(self.test_dir) 84 commit_id = self._do_commit() 85 repo = Repository.open('.') 86 self.assertEqual(set(), repo.has_revisions([b'foobar'])) 87 revid = default_mapping.revision_id_foreign_to_bzr(commit_id) 88 self.assertEqual(set([revid]), repo.has_revisions([b'foobar', revid])) 89 90 def test_get_revision(self): 91 # GitRepository.get_revision gives a Revision object. 92 93 # Create a git repository with a revision. 94 GitRepo.init(self.test_dir) 95 commit_id = self._do_commit() 96 97 # Get the corresponding Revision object. 98 revid = default_mapping.revision_id_foreign_to_bzr(commit_id) 99 repo = Repository.open('.') 100 rev = repo.get_revision(revid) 101 self.assertIsInstance(rev, revision.Revision) 102 103 def test_get_revision_unknown(self): 104 GitRepo.init(self.test_dir) 105 106 repo = Repository.open('.') 107 self.assertRaises(errors.NoSuchRevision, repo.get_revision, b"bla") 108 109 def simple_commit(self): 110 # Create a git repository with some interesting files in a revision. 111 GitRepo.init(self.test_dir) 112 builder = tests.GitBranchBuilder() 113 builder.set_file(b'data', b'text\n', False) 114 builder.set_file(b'executable', b'content', True) 115 builder.set_symlink(b'link', b'broken') 116 builder.set_file(b'subdir/subfile', b'subdir text\n', False) 117 commit_handle = builder.commit(b'Joe Foo <joe@foo.com>', b'message', 118 timestamp=1205433193) 119 mapping = builder.finish() 120 return mapping[commit_handle] 121 122 def test_pack(self): 123 commit_id = self.simple_commit() 124 repo = Repository.open('.') 125 repo.pack() 126 127 def test_revision_tree(self): 128 commit_id = self.simple_commit() 129 revid = default_mapping.revision_id_foreign_to_bzr(commit_id) 130 repo = Repository.open('.') 131 tree = repo.revision_tree(revid) 132 self.assertEqual(tree.get_revision_id(), revid) 133 self.assertEqual(b"text\n", tree.get_file_text("data")) 134 135 136class TestGitRepository(tests.TestCaseWithTransport): 137 138 def _do_commit(self): 139 builder = tests.GitBranchBuilder() 140 builder.set_file(b'a', b'text for a\n', False) 141 commit_handle = builder.commit(b'Joe Foo <joe@foo.com>', b'message') 142 mapping = builder.finish() 143 return mapping[commit_handle] 144 145 def setUp(self): 146 tests.TestCaseWithTransport.setUp(self) 147 dulwich.repo.Repo.create(self.test_dir) 148 self.git_repo = Repository.open(self.test_dir) 149 150 def test_supports_rich_root(self): 151 repo = self.git_repo 152 self.assertEqual(repo.supports_rich_root(), True) 153 154 def test_get_signature_text(self): 155 self.assertRaises( 156 errors.NoSuchRevision, self.git_repo.get_signature_text, revision.NULL_REVISION) 157 158 def test_has_signature_for_revision_id(self): 159 self.assertEqual(False, self.git_repo.has_signature_for_revision_id( 160 revision.NULL_REVISION)) 161 162 def test_all_revision_ids_none(self): 163 self.assertEqual([], self.git_repo.all_revision_ids()) 164 165 def test_get_known_graph_ancestry(self): 166 cid = self._do_commit() 167 revid = default_mapping.revision_id_foreign_to_bzr(cid) 168 g = self.git_repo.get_known_graph_ancestry([revid]) 169 self.assertEqual(frozenset([revid]), 170 g.heads([revid])) 171 self.assertEqual([(revid, 0, (1,), True)], 172 [(n.key, n.merge_depth, n.revno, n.end_of_merge) 173 for n in g.merge_sort(revid)]) 174 175 def test_all_revision_ids(self): 176 commit_id = self._do_commit() 177 self.assertEqual( 178 [default_mapping.revision_id_foreign_to_bzr(commit_id)], 179 self.git_repo.all_revision_ids()) 180 181 def assertIsNullInventory(self, inv): 182 self.assertEqual(inv.root, None) 183 self.assertEqual(inv.revision_id, revision.NULL_REVISION) 184 self.assertEqual(list(inv.iter_entries()), []) 185 186 def test_revision_tree_none(self): 187 # GitRepository.revision_tree(None) returns the null tree. 188 repo = self.git_repo 189 tree = repo.revision_tree(revision.NULL_REVISION) 190 self.assertEqual(tree.get_revision_id(), revision.NULL_REVISION) 191 self.assertIs(None, tree.path2id('')) 192 193 def test_get_parent_map_null(self): 194 self.assertEqual({revision.NULL_REVISION: ()}, 195 self.git_repo.get_parent_map([revision.NULL_REVISION])) 196 197 198class SigningGitRepository(tests.TestCaseWithTransport): 199 200 def test_signed_commit(self): 201 import breezy.gpg 202 oldstrategy = breezy.gpg.GPGStrategy 203 wt = self.make_branch_and_tree('.', format='git') 204 branch = wt.branch 205 revid = wt.commit("base", allow_pointless=True) 206 self.assertFalse( 207 branch.repository.has_signature_for_revision_id(revid)) 208 try: 209 breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy 210 conf = config.MemoryStack(b''' 211create_signatures=always 212''') 213 revid2 = wt.commit(config=conf, message="base", 214 allow_pointless=True) 215 216 def sign(text): 217 return breezy.gpg.LoopbackGPGStrategy(None).sign(text) 218 self.assertIsInstance( 219 branch.repository.get_signature_text(revid2), bytes) 220 finally: 221 breezy.gpg.GPGStrategy = oldstrategy 222 223 224class RevpropsRepository(tests.TestCaseWithTransport): 225 226 def test_author(self): 227 wt = self.make_branch_and_tree('.', format='git') 228 revid = wt.commit( 229 "base", allow_pointless=True, 230 revprops={'author': 'Joe Example <joe@example.com>'}) 231 rev = wt.branch.repository.get_revision(revid) 232 r = dulwich.repo.Repo('.') 233 self.assertEqual(b'Joe Example <joe@example.com>', r[r.head()].author) 234 235 def test_authors(self): 236 wt = self.make_branch_and_tree('.', format='git') 237 revid = wt.commit( 238 "base", allow_pointless=True, 239 revprops={'authors': 'Joe Example <joe@example.com>'}) 240 rev = wt.branch.repository.get_revision(revid) 241 r = dulwich.repo.Repo('.') 242 self.assertEqual(b'Joe Example <joe@example.com>', r[r.head()].author) 243 244 def test_multiple_authors(self): 245 wt = self.make_branch_and_tree('.', format='git') 246 self.assertRaises( 247 Exception, wt.commit, "base", allow_pointless=True, 248 revprops={'authors': 'Joe Example <joe@example.com>\n' 249 'Jane Doe <jane@example.com\n>'}) 250 251 def test_bugs(self): 252 wt = self.make_branch_and_tree('.', format='git') 253 revid = wt.commit( 254 "base", allow_pointless=True, 255 revprops={ 256 'bugs': 'https://github.com/jelmer/dulwich/issues/123 fixed\n' 257 }) 258 rev = wt.branch.repository.get_revision(revid) 259 r = dulwich.repo.Repo('.') 260 self.assertEqual( 261 b'base\n' 262 b'Fixes: https://github.com/jelmer/dulwich/issues/123\n', 263 r[r.head()].message) 264 265 266class GitRepositoryFormat(tests.TestCase): 267 268 def setUp(self): 269 super(GitRepositoryFormat, self).setUp() 270 self.format = repository.GitRepositoryFormat() 271 272 def test_get_format_description(self): 273 self.assertEqual("Git Repository", 274 self.format.get_format_description()) 275 276 277class RevisionGistImportTests(tests.TestCaseWithTransport): 278 279 def setUp(self): 280 tests.TestCaseWithTransport.setUp(self) 281 self.git_path = os.path.join(self.test_dir, "git") 282 os.mkdir(self.git_path) 283 dulwich.repo.Repo.create(self.git_path) 284 self.git_repo = Repository.open(self.git_path) 285 self.bzr_tree = self.make_branch_and_tree("bzr") 286 287 def get_inter(self): 288 return InterRepository.get(self.bzr_tree.branch.repository, 289 self.git_repo) 290 291 def object_iter(self): 292 store = BazaarObjectStore( 293 self.bzr_tree.branch.repository, default_mapping) 294 store_iterator = MissingObjectsIterator( 295 store, self.bzr_tree.branch.repository) 296 return store, store_iterator 297 298 def import_rev(self, revid, parent_lookup=None): 299 store, store_iter = self.object_iter() 300 store._cache.idmap.start_write_group() 301 try: 302 return store_iter.import_revision(revid, lossy=True) 303 except: 304 store._cache.idmap.abort_write_group() 305 raise 306 else: 307 store._cache.idmap.commit_write_group() 308 309 def test_pointless(self): 310 revid = self.bzr_tree.commit("pointless", timestamp=1205433193, 311 timezone=0, committer="Jelmer Vernooij <jelmer@samba.org>") 312 self.assertEqual(b"2caa8094a5b794961cd9bf582e3e2bb090db0b14", 313 self.import_rev(revid)) 314 self.assertEqual(b"2caa8094a5b794961cd9bf582e3e2bb090db0b14", 315 self.import_rev(revid)) 316 317 318class ForeignTestsRepositoryFactory(object): 319 320 def make_repository(self, transport): 321 return dir.LocalGitControlDirFormat().initialize_on_transport(transport).open_repository() 322