test_porcelain.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  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 contextlib import closing
  20. from io import BytesIO
  21. import os
  22. import shutil
  23. import tarfile
  24. import tempfile
  25. from dulwich import porcelain
  26. from dulwich.diff_tree import tree_changes
  27. from dulwich.objects import (
  28. Blob,
  29. Tag,
  30. Tree,
  31. )
  32. from dulwich.repo import Repo
  33. from dulwich.tests import (
  34. TestCase,
  35. )
  36. from dulwich.tests.compat.utils import require_git_version
  37. from dulwich.tests.utils import (
  38. build_commit_graph,
  39. make_object,
  40. )
  41. class PorcelainTestCase(TestCase):
  42. def setUp(self):
  43. super(PorcelainTestCase, self).setUp()
  44. repo_dir = tempfile.mkdtemp()
  45. self.addCleanup(shutil.rmtree, repo_dir)
  46. self.repo = Repo.init(repo_dir)
  47. def tearDown(self):
  48. super(PorcelainTestCase, self).tearDown()
  49. self.repo.close()
  50. class ArchiveTests(PorcelainTestCase):
  51. """Tests for the archive command."""
  52. def test_simple(self):
  53. # TODO(jelmer): Remove this once dulwich has its own implementation of archive.
  54. require_git_version((1, 5, 0))
  55. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  56. self.repo.refs[b"refs/heads/master"] = c3.id
  57. out = BytesIO()
  58. err = BytesIO()
  59. porcelain.archive(self.repo.path, b"refs/heads/master", outstream=out,
  60. errstream=err)
  61. self.assertEqual(b"", err.getvalue())
  62. tf = tarfile.TarFile(fileobj=out)
  63. self.addCleanup(tf.close)
  64. self.assertEqual([], tf.getnames())
  65. class UpdateServerInfoTests(PorcelainTestCase):
  66. def test_simple(self):
  67. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  68. [3, 1, 2]])
  69. self.repo.refs[b"refs/heads/foo"] = c3.id
  70. porcelain.update_server_info(self.repo.path)
  71. self.assertTrue(os.path.exists(os.path.join(self.repo.controldir(),
  72. 'info', 'refs')))
  73. class CommitTests(PorcelainTestCase):
  74. def test_custom_author(self):
  75. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  76. [3, 1, 2]])
  77. self.repo.refs[b"refs/heads/foo"] = c3.id
  78. sha = porcelain.commit(self.repo.path, message=b"Some message",
  79. author=b"Joe <joe@example.com>", committer=b"Bob <bob@example.com>")
  80. self.assertTrue(isinstance(sha, bytes))
  81. self.assertEqual(len(sha), 40)
  82. class CloneTests(PorcelainTestCase):
  83. def test_simple_local(self):
  84. f1_1 = make_object(Blob, data=b'f1')
  85. commit_spec = [[1], [2, 1], [3, 1, 2]]
  86. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  87. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  88. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  89. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  90. commit_spec, trees)
  91. self.repo.refs[b"refs/heads/master"] = c3.id
  92. target_path = tempfile.mkdtemp()
  93. errstream = BytesIO()
  94. self.addCleanup(shutil.rmtree, target_path)
  95. r = porcelain.clone(self.repo.path, target_path,
  96. checkout=False, errstream=errstream)
  97. self.assertEqual(r.path, target_path)
  98. self.assertEqual(Repo(target_path).head(), c3.id)
  99. self.assertTrue(b'f1' not in os.listdir(target_path))
  100. self.assertTrue(b'f2' not in os.listdir(target_path))
  101. def test_simple_local_with_checkout(self):
  102. f1_1 = make_object(Blob, data=b'f1')
  103. commit_spec = [[1], [2, 1], [3, 1, 2]]
  104. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  105. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  106. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  107. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  108. commit_spec, trees)
  109. self.repo.refs[b"refs/heads/master"] = c3.id
  110. target_path = tempfile.mkdtemp()
  111. errstream = BytesIO()
  112. self.addCleanup(shutil.rmtree, target_path)
  113. with closing(porcelain.clone(self.repo.path, target_path,
  114. checkout=True,
  115. errstream=errstream)) as r:
  116. self.assertEqual(r.path, target_path)
  117. with closing(Repo(target_path)) as r:
  118. self.assertEqual(r.head(), c3.id)
  119. self.assertTrue('f1' in os.listdir(target_path))
  120. self.assertTrue('f2' in os.listdir(target_path))
  121. def test_bare_local_with_checkout(self):
  122. f1_1 = make_object(Blob, data=b'f1')
  123. commit_spec = [[1], [2, 1], [3, 1, 2]]
  124. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  125. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  126. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  127. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  128. commit_spec, trees)
  129. self.repo.refs[b"refs/heads/master"] = c3.id
  130. target_path = tempfile.mkdtemp()
  131. errstream = BytesIO()
  132. self.addCleanup(shutil.rmtree, target_path)
  133. r = porcelain.clone(self.repo.path, target_path,
  134. bare=True, errstream=errstream)
  135. self.assertEqual(r.path, target_path)
  136. self.assertEqual(Repo(target_path).head(), c3.id)
  137. self.assertFalse(b'f1' in os.listdir(target_path))
  138. self.assertFalse(b'f2' in os.listdir(target_path))
  139. def test_no_checkout_with_bare(self):
  140. f1_1 = make_object(Blob, data=b'f1')
  141. commit_spec = [[1]]
  142. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]}
  143. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  144. self.repo.refs[b"refs/heads/master"] = c1.id
  145. target_path = tempfile.mkdtemp()
  146. errstream = BytesIO()
  147. self.addCleanup(shutil.rmtree, target_path)
  148. self.assertRaises(ValueError, porcelain.clone, self.repo.path,
  149. target_path, checkout=True, bare=True, errstream=errstream)
  150. class InitTests(TestCase):
  151. def test_non_bare(self):
  152. repo_dir = tempfile.mkdtemp()
  153. self.addCleanup(shutil.rmtree, repo_dir)
  154. porcelain.init(repo_dir)
  155. def test_bare(self):
  156. repo_dir = tempfile.mkdtemp()
  157. self.addCleanup(shutil.rmtree, repo_dir)
  158. porcelain.init(repo_dir, bare=True)
  159. class AddTests(PorcelainTestCase):
  160. def test_add_default_paths(self):
  161. # create a file for initial commit
  162. with open(os.path.join(self.repo.path, 'blah'), 'w') as f:
  163. f.write("\n")
  164. porcelain.add(repo=self.repo.path, paths=['blah'])
  165. porcelain.commit(repo=self.repo.path, message=b'test',
  166. author=b'test', committer=b'test')
  167. # Add a second test file and a file in a directory
  168. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  169. f.write("\n")
  170. os.mkdir(os.path.join(self.repo.path, 'adir'))
  171. with open(os.path.join(self.repo.path, 'adir', 'afile'), 'w') as f:
  172. f.write("\n")
  173. porcelain.add(self.repo.path)
  174. # Check that foo was added and nothing in .git was modified
  175. index = self.repo.open_index()
  176. self.assertEqual(sorted(index), [b'adir/afile', b'blah', b'foo'])
  177. def test_add_file(self):
  178. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  179. f.write("BAR")
  180. porcelain.add(self.repo.path, paths=["foo"])
  181. class RemoveTests(PorcelainTestCase):
  182. def test_remove_file(self):
  183. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  184. f.write("BAR")
  185. porcelain.add(self.repo.path, paths=["foo"])
  186. porcelain.rm(self.repo.path, paths=["foo"])
  187. class LogTests(PorcelainTestCase):
  188. def test_simple(self):
  189. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  190. [3, 1, 2]])
  191. self.repo.refs[b"HEAD"] = c3.id
  192. outstream = BytesIO()
  193. porcelain.log(self.repo.path, outstream=outstream)
  194. self.assertEqual(3, outstream.getvalue().count(b"-" * 50))
  195. def test_max_entries(self):
  196. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  197. [3, 1, 2]])
  198. self.repo.refs[b"HEAD"] = c3.id
  199. outstream = BytesIO()
  200. porcelain.log(self.repo.path, outstream=outstream, max_entries=1)
  201. self.assertEqual(1, outstream.getvalue().count(b"-" * 50))
  202. class ShowTests(PorcelainTestCase):
  203. def test_nolist(self):
  204. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  205. [3, 1, 2]])
  206. self.repo.refs[b"HEAD"] = c3.id
  207. outstream = BytesIO()
  208. porcelain.show(self.repo.path, objects=c3.id, outstream=outstream)
  209. self.assertTrue(outstream.getvalue().startswith(b"-" * 50))
  210. def test_simple(self):
  211. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  212. [3, 1, 2]])
  213. self.repo.refs[b"HEAD"] = c3.id
  214. outstream = BytesIO()
  215. porcelain.show(self.repo.path, objects=[c3.id], outstream=outstream)
  216. self.assertTrue(outstream.getvalue().startswith(b"-" * 50))
  217. def test_blob(self):
  218. b = Blob.from_string(b"The Foo\n")
  219. self.repo.object_store.add_object(b)
  220. outstream = BytesIO()
  221. porcelain.show(self.repo.path, objects=[b.id], outstream=outstream)
  222. self.assertEqual(outstream.getvalue(), b"The Foo\n")
  223. class SymbolicRefTests(PorcelainTestCase):
  224. def test_set_wrong_symbolic_ref(self):
  225. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  226. [3, 1, 2]])
  227. self.repo.refs[b"HEAD"] = c3.id
  228. self.assertRaises(ValueError, porcelain.symbolic_ref, self.repo.path, b'foobar')
  229. def test_set_force_wrong_symbolic_ref(self):
  230. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  231. [3, 1, 2]])
  232. self.repo.refs[b"HEAD"] = c3.id
  233. porcelain.symbolic_ref(self.repo.path, b'force_foobar', force=True)
  234. #test if we actually changed the file
  235. with self.repo.get_named_file('HEAD') as f:
  236. new_ref = f.read()
  237. self.assertEqual(new_ref, b'ref: refs/heads/force_foobar\n')
  238. def test_set_symbolic_ref(self):
  239. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  240. [3, 1, 2]])
  241. self.repo.refs[b"HEAD"] = c3.id
  242. porcelain.symbolic_ref(self.repo.path, b'master')
  243. def test_set_symbolic_ref_other_than_master(self):
  244. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  245. [3, 1, 2]], attrs=dict(refs='develop'))
  246. self.repo.refs[b"HEAD"] = c3.id
  247. self.repo.refs[b"refs/heads/develop"] = c3.id
  248. porcelain.symbolic_ref(self.repo.path, b'develop')
  249. #test if we actually changed the file
  250. with self.repo.get_named_file('HEAD') as f:
  251. new_ref = f.read()
  252. self.assertEqual(new_ref, b'ref: refs/heads/develop\n')
  253. class DiffTreeTests(PorcelainTestCase):
  254. def test_empty(self):
  255. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  256. [3, 1, 2]])
  257. self.repo.refs[b"HEAD"] = c3.id
  258. outstream = BytesIO()
  259. porcelain.diff_tree(self.repo.path, c2.tree, c3.tree, outstream=outstream)
  260. self.assertEqual(outstream.getvalue(), b"")
  261. class CommitTreeTests(PorcelainTestCase):
  262. def test_simple(self):
  263. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  264. [3, 1, 2]])
  265. b = Blob()
  266. b.data = b"foo the bar"
  267. t = Tree()
  268. t.add(b"somename", 0o100644, b.id)
  269. self.repo.object_store.add_object(t)
  270. self.repo.object_store.add_object(b)
  271. sha = porcelain.commit_tree(
  272. self.repo.path, t.id, message=b"Withcommit.",
  273. author=b"Joe <joe@example.com>",
  274. committer=b"Jane <jane@example.com>")
  275. self.assertTrue(isinstance(sha, bytes))
  276. self.assertEqual(len(sha), 40)
  277. class RevListTests(PorcelainTestCase):
  278. def test_simple(self):
  279. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  280. [3, 1, 2]])
  281. outstream = BytesIO()
  282. porcelain.rev_list(
  283. self.repo.path, [c3.id], outstream=outstream)
  284. self.assertEqual(
  285. c3.id + b"\n" +
  286. c2.id + b"\n" +
  287. c1.id + b"\n",
  288. outstream.getvalue())
  289. class TagCreateTests(PorcelainTestCase):
  290. def test_annotated(self):
  291. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  292. [3, 1, 2]])
  293. self.repo.refs[b"HEAD"] = c3.id
  294. porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
  295. b'bar', annotated=True)
  296. tags = self.repo.refs.as_dict(b"refs/tags")
  297. self.assertEqual(list(tags.keys()), [b"tryme"])
  298. tag = self.repo[b'refs/tags/tryme']
  299. self.assertTrue(isinstance(tag, Tag))
  300. self.assertEqual(b"foo <foo@bar.com>", tag.tagger)
  301. self.assertEqual(b"bar", tag.message)
  302. def test_unannotated(self):
  303. c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1],
  304. [3, 1, 2]])
  305. self.repo.refs[b"HEAD"] = c3.id
  306. porcelain.tag_create(self.repo.path, b"tryme", annotated=False)
  307. tags = self.repo.refs.as_dict(b"refs/tags")
  308. self.assertEqual(list(tags.keys()), [b"tryme"])
  309. self.repo[b'refs/tags/tryme']
  310. self.assertEqual(list(tags.values()), [self.repo.head()])
  311. class TagListTests(PorcelainTestCase):
  312. def test_empty(self):
  313. tags = porcelain.tag_list(self.repo.path)
  314. self.assertEqual([], tags)
  315. def test_simple(self):
  316. self.repo.refs[b"refs/tags/foo"] = b"aa" * 20
  317. self.repo.refs[b"refs/tags/bar/bla"] = b"bb" * 20
  318. tags = porcelain.tag_list(self.repo.path)
  319. self.assertEqual([b"bar/bla", b"foo"], tags)
  320. class TagDeleteTests(PorcelainTestCase):
  321. def test_simple(self):
  322. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  323. self.repo[b"HEAD"] = c1.id
  324. porcelain.tag_create(self.repo, b'foo')
  325. self.assertTrue(b"foo" in porcelain.tag_list(self.repo))
  326. porcelain.tag_delete(self.repo, b'foo')
  327. self.assertFalse(b"foo" in porcelain.tag_list(self.repo))
  328. class ResetTests(PorcelainTestCase):
  329. def test_hard_head(self):
  330. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  331. f.write("BAR")
  332. porcelain.add(self.repo.path, paths=["foo"])
  333. porcelain.commit(self.repo.path, message=b"Some message",
  334. committer=b"Jane <jane@example.com>",
  335. author=b"John <john@example.com>")
  336. with open(os.path.join(self.repo.path, 'foo'), 'wb') as f:
  337. f.write(b"OOH")
  338. porcelain.reset(self.repo, "hard", b"HEAD")
  339. index = self.repo.open_index()
  340. changes = list(tree_changes(self.repo,
  341. index.commit(self.repo.object_store),
  342. self.repo[b'HEAD'].tree))
  343. self.assertEqual([], changes)
  344. class PushTests(PorcelainTestCase):
  345. def test_simple(self):
  346. """
  347. Basic test of porcelain push where self.repo is the remote. First
  348. clone the remote, commit a file to the clone, then push the changes
  349. back to the remote.
  350. """
  351. outstream = BytesIO()
  352. errstream = BytesIO()
  353. porcelain.commit(repo=self.repo.path, message=b'init',
  354. author=b'', committer=b'')
  355. # Setup target repo cloned from temp test repo
  356. clone_path = tempfile.mkdtemp()
  357. target_repo = porcelain.clone(self.repo.path, target=clone_path, errstream=errstream)
  358. target_repo.close()
  359. # create a second file to be pushed back to origin
  360. handle, fullpath = tempfile.mkstemp(dir=clone_path)
  361. os.close(handle)
  362. porcelain.add(repo=clone_path, paths=[os.path.basename(fullpath)])
  363. porcelain.commit(repo=clone_path, message=b'push',
  364. author=b'', committer=b'')
  365. # Setup a non-checked out branch in the remote
  366. refs_path = b"refs/heads/foo"
  367. self.repo[refs_path] = self.repo[b'HEAD']
  368. # Push to the remote
  369. porcelain.push(clone_path, self.repo.path, refs_path, outstream=outstream,
  370. errstream=errstream)
  371. # Check that the target and source
  372. with closing(Repo(clone_path)) as r_clone:
  373. # Get the change in the target repo corresponding to the add
  374. # this will be in the foo branch.
  375. change = list(tree_changes(self.repo, self.repo[b'HEAD'].tree,
  376. self.repo[b'refs/heads/foo'].tree))[0]
  377. self.assertEqual(r_clone[b'HEAD'].id, self.repo[refs_path].id)
  378. self.assertEqual(os.path.basename(fullpath), change.new.path.decode('ascii'))
  379. class PullTests(PorcelainTestCase):
  380. def test_simple(self):
  381. outstream = BytesIO()
  382. errstream = BytesIO()
  383. # create a file for initial commit
  384. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  385. os.close(handle)
  386. filename = os.path.basename(fullpath)
  387. porcelain.add(repo=self.repo.path, paths=filename)
  388. porcelain.commit(repo=self.repo.path, message=b'test',
  389. author=b'test', committer=b'test')
  390. # Setup target repo
  391. target_path = tempfile.mkdtemp()
  392. target_repo = porcelain.clone(self.repo.path, target=target_path,
  393. errstream=errstream)
  394. target_repo.close()
  395. # create a second file to be pushed
  396. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  397. os.close(handle)
  398. filename = os.path.basename(fullpath)
  399. porcelain.add(repo=self.repo.path, paths=filename)
  400. porcelain.commit(repo=self.repo.path, message=b'test2',
  401. author=b'test2', committer=b'test2')
  402. # Pull changes into the cloned repo
  403. porcelain.pull(target_path, self.repo.path, b'refs/heads/master',
  404. outstream=outstream, errstream=errstream)
  405. # Check the target repo for pushed changes
  406. with closing(Repo(target_path)) as r:
  407. self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id)
  408. class StatusTests(PorcelainTestCase):
  409. def test_status(self):
  410. """Integration test for `status` functionality."""
  411. # Commit a dummy file then modify it
  412. fullpath = os.path.join(self.repo.path, 'foo')
  413. with open(fullpath, 'w') as f:
  414. f.write('origstuff')
  415. porcelain.add(repo=self.repo.path, paths=['foo'])
  416. porcelain.commit(repo=self.repo.path, message=b'test status',
  417. author=b'', committer=b'')
  418. # modify access and modify time of path
  419. os.utime(fullpath, (0, 0))
  420. with open(fullpath, 'wb') as f:
  421. f.write(b'stuff')
  422. # Make a dummy file and stage it
  423. filename_add = 'bar'
  424. fullpath = os.path.join(self.repo.path, filename_add)
  425. with open(fullpath, 'w') as f:
  426. f.write('stuff')
  427. porcelain.add(repo=self.repo.path, paths=filename_add)
  428. results = porcelain.status(self.repo)
  429. self.assertEqual(results.staged['add'][0], filename_add.encode('ascii'))
  430. self.assertEqual(results.unstaged, [b'foo'])
  431. def test_get_tree_changes_add(self):
  432. """Unit test for get_tree_changes add."""
  433. # Make a dummy file, stage
  434. filename = 'bar'
  435. with open(os.path.join(self.repo.path, filename), 'w') as f:
  436. f.write('stuff')
  437. porcelain.add(repo=self.repo.path, paths=filename)
  438. porcelain.commit(repo=self.repo.path, message=b'test status',
  439. author=b'', committer=b'')
  440. filename = 'foo'
  441. with open(os.path.join(self.repo.path, filename), 'w') as f:
  442. f.write('stuff')
  443. porcelain.add(repo=self.repo.path, paths=filename)
  444. changes = porcelain.get_tree_changes(self.repo.path)
  445. self.assertEqual(changes['add'][0], filename.encode('ascii'))
  446. self.assertEqual(len(changes['add']), 1)
  447. self.assertEqual(len(changes['modify']), 0)
  448. self.assertEqual(len(changes['delete']), 0)
  449. def test_get_tree_changes_modify(self):
  450. """Unit test for get_tree_changes modify."""
  451. # Make a dummy file, stage, commit, modify
  452. filename = 'foo'
  453. fullpath = os.path.join(self.repo.path, filename)
  454. with open(fullpath, 'w') as f:
  455. f.write('stuff')
  456. porcelain.add(repo=self.repo.path, paths=filename)
  457. porcelain.commit(repo=self.repo.path, message=b'test status',
  458. author=b'', committer=b'')
  459. with open(fullpath, 'w') as f:
  460. f.write('otherstuff')
  461. porcelain.add(repo=self.repo.path, paths=filename)
  462. changes = porcelain.get_tree_changes(self.repo.path)
  463. self.assertEqual(changes['modify'][0], filename.encode('ascii'))
  464. self.assertEqual(len(changes['add']), 0)
  465. self.assertEqual(len(changes['modify']), 1)
  466. self.assertEqual(len(changes['delete']), 0)
  467. def test_get_tree_changes_delete(self):
  468. """Unit test for get_tree_changes delete."""
  469. # Make a dummy file, stage, commit, remove
  470. filename = 'foo'
  471. with open(os.path.join(self.repo.path, filename), 'w') as f:
  472. f.write('stuff')
  473. porcelain.add(repo=self.repo.path, paths=filename)
  474. porcelain.commit(repo=self.repo.path, message=b'test status',
  475. author=b'', committer=b'')
  476. porcelain.rm(repo=self.repo.path, paths=[filename])
  477. changes = porcelain.get_tree_changes(self.repo.path)
  478. self.assertEqual(changes['delete'][0], filename.encode('ascii'))
  479. self.assertEqual(len(changes['add']), 0)
  480. self.assertEqual(len(changes['modify']), 0)
  481. self.assertEqual(len(changes['delete']), 1)
  482. # TODO(jelmer): Add test for dulwich.porcelain.daemon
  483. class UploadPackTests(PorcelainTestCase):
  484. """Tests for upload_pack."""
  485. def test_upload_pack(self):
  486. outf = BytesIO()
  487. exitcode = porcelain.upload_pack(self.repo.path, BytesIO(b"0000"), outf)
  488. outlines = outf.getvalue().splitlines()
  489. self.assertEqual([b"0000"], outlines)
  490. self.assertEqual(0, exitcode)
  491. class ReceivePackTests(PorcelainTestCase):
  492. """Tests for receive_pack."""
  493. def test_receive_pack(self):
  494. filename = 'foo'
  495. with open(os.path.join(self.repo.path, filename), 'w') as f:
  496. f.write('stuff')
  497. porcelain.add(repo=self.repo.path, paths=filename)
  498. self.repo.do_commit(message=b'test status',
  499. author=b'', committer=b'', author_timestamp=1402354300,
  500. commit_timestamp=1402354300, author_timezone=0, commit_timezone=0)
  501. outf = BytesIO()
  502. exitcode = porcelain.receive_pack(self.repo.path, BytesIO(b"0000"), outf)
  503. outlines = outf.getvalue().splitlines()
  504. self.assertEqual([
  505. b'005a9e65bdcf4a22cdd4f3700604a275cd2aaf146b23 HEAD\x00report-status '
  506. b'delete-refs side-band-64k',
  507. b'003f9e65bdcf4a22cdd4f3700604a275cd2aaf146b23 refs/heads/master',
  508. b'0000'], outlines)
  509. self.assertEqual(0, exitcode)
  510. class BranchListTests(PorcelainTestCase):
  511. def test_standard(self):
  512. self.assertEqual(set([]), set(porcelain.branch_list(self.repo)))
  513. def test_new_branch(self):
  514. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  515. self.repo[b"HEAD"] = c1.id
  516. porcelain.branch_create(self.repo, b"foo")
  517. self.assertEqual(
  518. set([b"master", b"foo"]),
  519. set(porcelain.branch_list(self.repo)))
  520. class BranchCreateTests(PorcelainTestCase):
  521. def test_branch_exists(self):
  522. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  523. self.repo[b"HEAD"] = c1.id
  524. porcelain.branch_create(self.repo, b"foo")
  525. self.assertRaises(KeyError, porcelain.branch_create, self.repo, b"foo")
  526. porcelain.branch_create(self.repo, b"foo", force=True)
  527. def test_new_branch(self):
  528. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  529. self.repo[b"HEAD"] = c1.id
  530. porcelain.branch_create(self.repo, b"foo")
  531. self.assertEqual(
  532. set([b"master", b"foo"]),
  533. set(porcelain.branch_list(self.repo)))
  534. class BranchDeleteTests(PorcelainTestCase):
  535. def test_simple(self):
  536. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  537. self.repo[b"HEAD"] = c1.id
  538. porcelain.branch_create(self.repo, b'foo')
  539. self.assertTrue(b"foo" in porcelain.branch_list(self.repo))
  540. porcelain.branch_delete(self.repo, b'foo')
  541. self.assertFalse(b"foo" in porcelain.branch_list(self.repo))
  542. class FetchTests(PorcelainTestCase):
  543. def test_simple(self):
  544. outstream = BytesIO()
  545. errstream = BytesIO()
  546. # create a file for initial commit
  547. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  548. os.close(handle)
  549. filename = os.path.basename(fullpath)
  550. porcelain.add(repo=self.repo.path, paths=filename)
  551. porcelain.commit(repo=self.repo.path, message=b'test',
  552. author=b'test', committer=b'test')
  553. # Setup target repo
  554. target_path = tempfile.mkdtemp()
  555. target_repo = porcelain.clone(self.repo.path, target=target_path,
  556. errstream=errstream)
  557. # create a second file to be pushed
  558. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  559. os.close(handle)
  560. filename = os.path.basename(fullpath)
  561. porcelain.add(repo=self.repo.path, paths=filename)
  562. porcelain.commit(repo=self.repo.path, message=b'test2',
  563. author=b'test2', committer=b'test2')
  564. self.assertFalse(self.repo[b'HEAD'].id in target_repo)
  565. target_repo.close()
  566. # Fetch changes into the cloned repo
  567. porcelain.fetch(target_path, self.repo.path, outstream=outstream,
  568. errstream=errstream)
  569. # Check the target repo for pushed changes
  570. with closing(Repo(target_path)) as r:
  571. self.assertTrue(self.repo[b'HEAD'].id in r)