test_porcelain.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. # test_porcelain.py -- porcelain tests
  2. # Copyright (C) 2013 Jelmer Vernooij <jelmer@samba.org>
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; version 2
  7. # of the License or (at your option) a 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,
  17. # MA 02110-1301, USA.
  18. """Tests for dulwich.porcelain."""
  19. from cStringIO import StringIO
  20. import os
  21. import shutil
  22. import tarfile
  23. import tempfile
  24. from dulwich import porcelain
  25. from dulwich.diff_tree import tree_changes
  26. from dulwich.objects import (
  27. Blob,
  28. Tree,
  29. )
  30. from dulwich.repo import Repo
  31. from dulwich.tests import (
  32. TestCase,
  33. )
  34. from dulwich.tests.utils import (
  35. build_commit_graph,
  36. make_object,
  37. )
  38. class PorcelainTestCase(TestCase):
  39. def setUp(self):
  40. super(TestCase, self).setUp()
  41. repo_dir = tempfile.mkdtemp()
  42. self.addCleanup(shutil.rmtree, repo_dir)
  43. self.repo = Repo.init(repo_dir)
  44. class ArchiveTests(PorcelainTestCase):
  45. """Tests for the archive command."""
  46. def test_simple(self):
  47. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  48. self.repo.refs["refs/heads/master"] = c3.id
  49. out = StringIO()
  50. err = StringIO()
  51. porcelain.archive(self.repo.path, "refs/heads/master", outstream=out,
  52. errstream=err)
  53. self.assertEquals("", err.getvalue())
  54. tf = tarfile.TarFile(fileobj=out)
  55. self.addCleanup(tf.close)
  56. self.assertEquals([], tf.getnames())
  57. class UpdateServerInfoTests(PorcelainTestCase):
  58. def test_simple(self):
  59. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  60. [3, 1, 2]])
  61. self.repo.refs["refs/heads/foo"] = c3.id
  62. porcelain.update_server_info(self.repo.path)
  63. self.assertTrue(os.path.exists(os.path.join(self.repo.controldir(),
  64. 'info', 'refs')))
  65. class CommitTests(PorcelainTestCase):
  66. def test_custom_author(self):
  67. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  68. [3, 1, 2]])
  69. self.repo.refs["refs/heads/foo"] = c3.id
  70. sha = porcelain.commit(self.repo.path, message="Some message",
  71. author="Joe <joe@example.com>", committer="Bob <bob@example.com>")
  72. self.assertTrue(type(sha) is str)
  73. self.assertEquals(len(sha), 40)
  74. class CloneTests(PorcelainTestCase):
  75. def test_simple_local(self):
  76. f1_1 = make_object(Blob, data='f1')
  77. commit_spec = [[1], [2, 1], [3, 1, 2]]
  78. trees = {1: [('f1', f1_1), ('f2', f1_1)],
  79. 2: [('f1', f1_1), ('f2', f1_1)],
  80. 3: [('f1', f1_1), ('f2', f1_1)], }
  81. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  82. commit_spec, trees)
  83. self.repo.refs["refs/heads/master"] = c3.id
  84. target_path = tempfile.mkdtemp()
  85. outstream = StringIO()
  86. self.addCleanup(shutil.rmtree, target_path)
  87. r = porcelain.clone(self.repo.path, target_path,
  88. checkout=False, outstream=outstream)
  89. self.assertEquals(r.path, target_path)
  90. self.assertEquals(Repo(target_path).head(), c3.id)
  91. self.assertTrue('f1' not in os.listdir(target_path))
  92. self.assertTrue('f2' not in os.listdir(target_path))
  93. def test_simple_local_with_checkout(self):
  94. f1_1 = make_object(Blob, data='f1')
  95. commit_spec = [[1], [2, 1], [3, 1, 2]]
  96. trees = {1: [('f1', f1_1), ('f2', f1_1)],
  97. 2: [('f1', f1_1), ('f2', f1_1)],
  98. 3: [('f1', f1_1), ('f2', f1_1)], }
  99. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  100. commit_spec, trees)
  101. self.repo.refs["refs/heads/master"] = c3.id
  102. target_path = tempfile.mkdtemp()
  103. outstream = StringIO()
  104. self.addCleanup(shutil.rmtree, target_path)
  105. r = porcelain.clone(self.repo.path, target_path,
  106. checkout=True, outstream=outstream)
  107. self.assertEquals(r.path, target_path)
  108. self.assertEquals(Repo(target_path).head(), c3.id)
  109. self.assertTrue('f1' in os.listdir(target_path))
  110. self.assertTrue('f2' in os.listdir(target_path))
  111. def test_bare_local_with_checkout(self):
  112. f1_1 = make_object(Blob, data='f1')
  113. commit_spec = [[1], [2, 1], [3, 1, 2]]
  114. trees = {1: [('f1', f1_1), ('f2', f1_1)],
  115. 2: [('f1', f1_1), ('f2', f1_1)],
  116. 3: [('f1', f1_1), ('f2', f1_1)], }
  117. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  118. commit_spec, trees)
  119. self.repo.refs["refs/heads/master"] = c3.id
  120. target_path = tempfile.mkdtemp()
  121. outstream = StringIO()
  122. self.addCleanup(shutil.rmtree, target_path)
  123. r = porcelain.clone(self.repo.path, target_path,
  124. bare=True, outstream=outstream)
  125. self.assertEquals(r.path, target_path)
  126. self.assertEquals(Repo(target_path).head(), c3.id)
  127. self.assertFalse('f1' in os.listdir(target_path))
  128. self.assertFalse('f2' in os.listdir(target_path))
  129. def test_no_checkout_with_bare(self):
  130. f1_1 = make_object(Blob, data='f1')
  131. commit_spec = [[1]]
  132. trees = {1: [('f1', f1_1), ('f2', f1_1)]}
  133. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  134. self.repo.refs["refs/heads/master"] = c1.id
  135. target_path = tempfile.mkdtemp()
  136. outstream = StringIO()
  137. self.addCleanup(shutil.rmtree, target_path)
  138. self.assertRaises(ValueError, porcelain.clone, self.repo.path,
  139. target_path, checkout=True, bare=True, outstream=outstream)
  140. class InitTests(TestCase):
  141. def test_non_bare(self):
  142. repo_dir = tempfile.mkdtemp()
  143. self.addCleanup(shutil.rmtree, repo_dir)
  144. porcelain.init(repo_dir)
  145. def test_bare(self):
  146. repo_dir = tempfile.mkdtemp()
  147. self.addCleanup(shutil.rmtree, repo_dir)
  148. porcelain.init(repo_dir, bare=True)
  149. class AddTests(PorcelainTestCase):
  150. def test_add_default_paths(self):
  151. # create a file for initial commit
  152. with open(os.path.join(self.repo.path, 'blah'), 'w') as f:
  153. f.write("\n")
  154. porcelain.add(repo=self.repo.path, paths=['blah'])
  155. porcelain.commit(repo=self.repo.path, message='test',
  156. author='test', committer='test')
  157. # Add a second test file and a file in a directory
  158. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  159. f.write("\n")
  160. os.mkdir(os.path.join(self.repo.path, 'adir'))
  161. with open(os.path.join(self.repo.path, 'adir', 'afile'), 'w') as f:
  162. f.write("\n")
  163. porcelain.add(self.repo.path)
  164. # Check that foo was added and nothing in .git was modified
  165. index = self.repo.open_index()
  166. self.assertEquals(list(index), ['blah', 'foo', 'adir/afile'])
  167. def test_add_file(self):
  168. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  169. f.write("BAR")
  170. porcelain.add(self.repo.path, paths=["foo"])
  171. class RemoveTests(PorcelainTestCase):
  172. def test_remove_file(self):
  173. f = open(os.path.join(self.repo.path, 'foo'), 'w')
  174. try:
  175. f.write("BAR")
  176. finally:
  177. f.close()
  178. porcelain.add(self.repo.path, paths=["foo"])
  179. porcelain.rm(self.repo.path, paths=["foo"])
  180. class LogTests(PorcelainTestCase):
  181. def test_simple(self):
  182. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  183. [3, 1, 2]])
  184. self.repo.refs["HEAD"] = c3.id
  185. outstream = StringIO()
  186. porcelain.log(self.repo.path, outstream=outstream)
  187. self.assertEquals(3, outstream.getvalue().count("-" * 50))
  188. def test_max_entries(self):
  189. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  190. [3, 1, 2]])
  191. self.repo.refs["HEAD"] = c3.id
  192. outstream = StringIO()
  193. porcelain.log(self.repo.path, outstream=outstream, max_entries=1)
  194. self.assertEquals(1, outstream.getvalue().count("-" * 50))
  195. class ShowTests(PorcelainTestCase):
  196. def test_nolist(self):
  197. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  198. [3, 1, 2]])
  199. self.repo.refs["HEAD"] = c3.id
  200. outstream = StringIO()
  201. porcelain.show(self.repo.path, objects=c3.id, outstream=outstream)
  202. self.assertTrue(outstream.getvalue().startswith("-" * 50))
  203. def test_simple(self):
  204. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  205. [3, 1, 2]])
  206. self.repo.refs["HEAD"] = c3.id
  207. outstream = StringIO()
  208. porcelain.show(self.repo.path, objects=[c3.id], outstream=outstream)
  209. self.assertTrue(outstream.getvalue().startswith("-" * 50))
  210. def test_blob(self):
  211. b = Blob.from_string("The Foo\n")
  212. self.repo.object_store.add_object(b)
  213. outstream = StringIO()
  214. porcelain.show(self.repo.path, objects=[b.id], outstream=outstream)
  215. self.assertEquals(outstream.getvalue(), "The Foo\n")
  216. class SymbolicRefTests(PorcelainTestCase):
  217. def test_set_wrong_symbolic_ref(self):
  218. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  219. [3, 1, 2]])
  220. self.repo.refs["HEAD"] = c3.id
  221. outstream = StringIO()
  222. self.assertRaises(ValueError, porcelain.symbolic_ref, self.repo.path, 'foobar')
  223. def test_set_force_wrong_symbolic_ref(self):
  224. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  225. [3, 1, 2]])
  226. self.repo.refs["HEAD"] = c3.id
  227. porcelain.symbolic_ref(self.repo.path, 'force_foobar', force=True)
  228. #test if we actually changed the file
  229. new_ref = self.repo.get_named_file('HEAD').read()
  230. self.assertEqual(new_ref, 'ref: refs/heads/force_foobar\n')
  231. def test_set_symbolic_ref(self):
  232. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  233. [3, 1, 2]])
  234. self.repo.refs["HEAD"] = c3.id
  235. porcelain.symbolic_ref(self.repo.path, 'master')
  236. def test_set_symbolic_ref_other_than_master(self):
  237. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  238. [3, 1, 2]], attrs=dict(refs='develop'))
  239. self.repo.refs["HEAD"] = c3.id
  240. self.repo.refs["refs/heads/develop"] = c3.id
  241. porcelain.symbolic_ref(self.repo.path, 'develop')
  242. #test if we actually changed the file
  243. new_ref = self.repo.get_named_file('HEAD').read()
  244. self.assertEqual(new_ref, 'ref: refs/heads/develop\n')
  245. class DiffTreeTests(PorcelainTestCase):
  246. def test_empty(self):
  247. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  248. [3, 1, 2]])
  249. self.repo.refs["HEAD"] = c3.id
  250. outstream = StringIO()
  251. porcelain.diff_tree(self.repo.path, c2.tree, c3.tree, outstream=outstream)
  252. self.assertEquals(outstream.getvalue(), "")
  253. class CommitTreeTests(PorcelainTestCase):
  254. def test_simple(self):
  255. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  256. [3, 1, 2]])
  257. b = Blob()
  258. b.data = "foo the bar"
  259. t = Tree()
  260. t.add("somename", 0100644, b.id)
  261. self.repo.object_store.add_object(t)
  262. self.repo.object_store.add_object(b)
  263. sha = porcelain.commit_tree(
  264. self.repo.path, t.id, message="Withcommit.",
  265. author="Joe <joe@example.com>",
  266. committer="Jane <jane@example.com>")
  267. self.assertTrue(type(sha) is str)
  268. self.assertEquals(len(sha), 40)
  269. class RevListTests(PorcelainTestCase):
  270. def test_simple(self):
  271. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  272. [3, 1, 2]])
  273. outstream = StringIO()
  274. porcelain.rev_list(
  275. self.repo.path, [c3.id], outstream=outstream)
  276. self.assertEquals(
  277. "%s\n%s\n%s\n" % (c3.id, c2.id, c1.id),
  278. outstream.getvalue())
  279. class TagTests(PorcelainTestCase):
  280. def test_simple(self):
  281. tag = 'tryme'
  282. author = 'foo'
  283. message = 'bar'
  284. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  285. [3, 1, 2]])
  286. self.repo.refs["HEAD"] = c3.id
  287. porcelain.tag(self.repo.path, tag, author, message)
  288. tags = self.repo.refs.as_dict("refs/tags")
  289. self.assertEquals(tags.keys()[0], tag)
  290. class ListTagsTests(PorcelainTestCase):
  291. def test_empty(self):
  292. tags = porcelain.list_tags(self.repo.path)
  293. self.assertEquals([], tags)
  294. def test_simple(self):
  295. self.repo.refs["refs/tags/foo"] = "aa" * 20
  296. self.repo.refs["refs/tags/bar/bla"] = "bb" * 20
  297. tags = porcelain.list_tags(self.repo.path)
  298. self.assertEquals(["bar/bla", "foo"], tags)
  299. class ResetTests(PorcelainTestCase):
  300. def test_hard_head(self):
  301. f = open(os.path.join(self.repo.path, 'foo'), 'w')
  302. try:
  303. f.write("BAR")
  304. finally:
  305. f.close()
  306. porcelain.add(self.repo.path, paths=["foo"])
  307. porcelain.commit(self.repo.path, message="Some message",
  308. committer="Jane <jane@example.com>",
  309. author="John <john@example.com>")
  310. f = open(os.path.join(self.repo.path, 'foo'), 'w')
  311. try:
  312. f.write("OOH")
  313. finally:
  314. f.close()
  315. porcelain.reset(self.repo, "hard", "HEAD")
  316. index = self.repo.open_index()
  317. changes = list(tree_changes(self.repo,
  318. index.commit(self.repo.object_store),
  319. self.repo['HEAD'].tree))
  320. self.assertEquals([], changes)
  321. class PushTests(PorcelainTestCase):
  322. def test_simple(self):
  323. """
  324. Basic test of porcelain push where self.repo is the remote. First
  325. clone the remote, commit a file to the clone, then push the changes
  326. back to the remote.
  327. """
  328. outstream = StringIO()
  329. errstream = StringIO()
  330. porcelain.commit(repo=self.repo.path, message='init',
  331. author='', committer='')
  332. # Setup target repo cloned from temp test repo
  333. clone_path = tempfile.mkdtemp()
  334. porcelain.clone(self.repo.path, target=clone_path, outstream=outstream)
  335. # create a second file to be pushed back to origin
  336. handle, fullpath = tempfile.mkstemp(dir=clone_path)
  337. porcelain.add(repo=clone_path, paths=[os.path.basename(fullpath)])
  338. porcelain.commit(repo=clone_path, message='push',
  339. author='', committer='')
  340. # Setup a non-checked out branch in the remote
  341. refs_path = os.path.join('refs', 'heads', 'foo')
  342. self.repo[refs_path] = self.repo['HEAD']
  343. # Push to the remote
  344. porcelain.push(clone_path, self.repo.path, refs_path, outstream=outstream,
  345. errstream=errstream)
  346. # Check that the target and source
  347. r_clone = Repo(clone_path)
  348. # Get the change in the target repo corresponding to the add
  349. # this will be in the foo branch.
  350. change = list(tree_changes(self.repo, self.repo['HEAD'].tree,
  351. self.repo['refs/heads/foo'].tree))[0]
  352. self.assertEquals(r_clone['HEAD'].id, self.repo[refs_path].id)
  353. self.assertEquals(os.path.basename(fullpath), change.new.path)
  354. class PullTests(PorcelainTestCase):
  355. def test_simple(self):
  356. outstream = StringIO()
  357. errstream = StringIO()
  358. # create a file for initial commit
  359. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  360. filename = os.path.basename(fullpath)
  361. porcelain.add(repo=self.repo.path, paths=filename)
  362. porcelain.commit(repo=self.repo.path, message='test',
  363. author='test', committer='test')
  364. # Setup target repo
  365. target_path = tempfile.mkdtemp()
  366. porcelain.clone(self.repo.path, target=target_path, outstream=outstream)
  367. # create a second file to be pushed
  368. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  369. filename = os.path.basename(fullpath)
  370. porcelain.add(repo=self.repo.path, paths=filename)
  371. porcelain.commit(repo=self.repo.path, message='test2',
  372. author='test2', committer='test2')
  373. # Pull changes into the cloned repo
  374. porcelain.pull(target_path, self.repo.path, 'refs/heads/master',
  375. outstream=outstream, errstream=errstream)
  376. # Check the target repo for pushed changes
  377. r = Repo(target_path)
  378. self.assertEquals(r['HEAD'].id, self.repo['HEAD'].id)