test_porcelain.py 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. # test_porcelain.py -- porcelain tests
  2. # Copyright (C) 2013 Jelmer Vernooij <jelmer@samba.org>
  3. #
  4. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  5. # General Public License as public by the Free Software Foundation; version 2.0
  6. # or (at your option) any later version. You can redistribute it and/or
  7. # modify it under the terms of either of these two licenses.
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. # You should have received a copy of the licenses; if not, see
  16. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  17. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  18. # License, Version 2.0.
  19. #
  20. """Tests for dulwich.porcelain."""
  21. from io import BytesIO
  22. try:
  23. from StringIO import StringIO
  24. except ImportError:
  25. from io import StringIO
  26. import os
  27. import shutil
  28. import tarfile
  29. import tempfile
  30. import time
  31. from dulwich import porcelain
  32. from dulwich.diff_tree import tree_changes
  33. from dulwich.objects import (
  34. Blob,
  35. Tag,
  36. Tree,
  37. ZERO_SHA,
  38. )
  39. from dulwich.repo import (
  40. NoIndexPresent,
  41. Repo,
  42. )
  43. from dulwich.tests import (
  44. TestCase,
  45. )
  46. from dulwich.tests.utils import (
  47. build_commit_graph,
  48. make_commit,
  49. make_object,
  50. )
  51. class PorcelainTestCase(TestCase):
  52. def setUp(self):
  53. super(PorcelainTestCase, self).setUp()
  54. self.test_dir = tempfile.mkdtemp()
  55. self.addCleanup(shutil.rmtree, self.test_dir)
  56. self.repo = Repo.init(os.path.join(self.test_dir, 'repo'), mkdir=True)
  57. self.addCleanup(self.repo.close)
  58. class ArchiveTests(PorcelainTestCase):
  59. """Tests for the archive command."""
  60. def test_simple(self):
  61. c1, c2, c3 = build_commit_graph(
  62. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  63. self.repo.refs[b"refs/heads/master"] = c3.id
  64. out = BytesIO()
  65. err = BytesIO()
  66. porcelain.archive(self.repo.path, b"refs/heads/master", outstream=out,
  67. errstream=err)
  68. self.assertEqual(b"", err.getvalue())
  69. tf = tarfile.TarFile(fileobj=out)
  70. self.addCleanup(tf.close)
  71. self.assertEqual([], tf.getnames())
  72. class UpdateServerInfoTests(PorcelainTestCase):
  73. def test_simple(self):
  74. c1, c2, c3 = build_commit_graph(
  75. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  76. self.repo.refs[b"refs/heads/foo"] = c3.id
  77. porcelain.update_server_info(self.repo.path)
  78. self.assertTrue(os.path.exists(
  79. os.path.join(self.repo.controldir(), 'info', 'refs')))
  80. class CommitTests(PorcelainTestCase):
  81. def test_custom_author(self):
  82. c1, c2, c3 = build_commit_graph(
  83. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  84. self.repo.refs[b"refs/heads/foo"] = c3.id
  85. sha = porcelain.commit(
  86. self.repo.path, message=b"Some message",
  87. author=b"Joe <joe@example.com>",
  88. committer=b"Bob <bob@example.com>")
  89. self.assertTrue(isinstance(sha, bytes))
  90. self.assertEqual(len(sha), 40)
  91. def test_unicode(self):
  92. c1, c2, c3 = build_commit_graph(
  93. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  94. self.repo.refs[b"refs/heads/foo"] = c3.id
  95. sha = porcelain.commit(
  96. self.repo.path, message="Some message",
  97. author="Joe <joe@example.com>",
  98. committer="Bob <bob@example.com>")
  99. self.assertTrue(isinstance(sha, bytes))
  100. self.assertEqual(len(sha), 40)
  101. class CloneTests(PorcelainTestCase):
  102. def test_simple_local(self):
  103. f1_1 = make_object(Blob, data=b'f1')
  104. commit_spec = [[1], [2, 1], [3, 1, 2]]
  105. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  106. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  107. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  108. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  109. commit_spec, trees)
  110. self.repo.refs[b"refs/heads/master"] = c3.id
  111. self.repo.refs[b"refs/tags/foo"] = c3.id
  112. target_path = tempfile.mkdtemp()
  113. errstream = BytesIO()
  114. self.addCleanup(shutil.rmtree, target_path)
  115. r = porcelain.clone(self.repo.path, target_path,
  116. checkout=False, errstream=errstream)
  117. self.assertEqual(r.path, target_path)
  118. target_repo = Repo(target_path)
  119. self.assertEqual(0, len(target_repo.open_index()))
  120. self.assertEqual(c3.id, target_repo.refs[b'refs/tags/foo'])
  121. self.assertTrue(b'f1' not in os.listdir(target_path))
  122. self.assertTrue(b'f2' not in os.listdir(target_path))
  123. c = r.get_config()
  124. encoded_path = self.repo.path
  125. if not isinstance(encoded_path, bytes):
  126. encoded_path = encoded_path.encode('utf-8')
  127. self.assertEqual(encoded_path, c.get((b'remote', b'origin'), b'url'))
  128. self.assertEqual(
  129. b'+refs/heads/*:refs/remotes/origin/*',
  130. c.get((b'remote', b'origin'), b'fetch'))
  131. def test_simple_local_with_checkout(self):
  132. f1_1 = make_object(Blob, data=b'f1')
  133. commit_spec = [[1], [2, 1], [3, 1, 2]]
  134. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  135. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  136. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  137. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  138. commit_spec, trees)
  139. self.repo.refs[b"refs/heads/master"] = c3.id
  140. target_path = tempfile.mkdtemp()
  141. errstream = BytesIO()
  142. self.addCleanup(shutil.rmtree, target_path)
  143. with porcelain.clone(self.repo.path, target_path,
  144. checkout=True,
  145. errstream=errstream) as r:
  146. self.assertEqual(r.path, target_path)
  147. with Repo(target_path) as r:
  148. self.assertEqual(r.head(), c3.id)
  149. self.assertTrue('f1' in os.listdir(target_path))
  150. self.assertTrue('f2' in os.listdir(target_path))
  151. def test_bare_local_with_checkout(self):
  152. f1_1 = make_object(Blob, data=b'f1')
  153. commit_spec = [[1], [2, 1], [3, 1, 2]]
  154. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  155. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  156. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  157. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  158. commit_spec, trees)
  159. self.repo.refs[b"refs/heads/master"] = c3.id
  160. target_path = tempfile.mkdtemp()
  161. errstream = BytesIO()
  162. self.addCleanup(shutil.rmtree, target_path)
  163. with porcelain.clone(
  164. self.repo.path, target_path, bare=True,
  165. errstream=errstream) as r:
  166. self.assertEqual(r.path, target_path)
  167. with Repo(target_path) as r:
  168. r.head()
  169. self.assertRaises(NoIndexPresent, r.open_index)
  170. self.assertFalse(b'f1' in os.listdir(target_path))
  171. self.assertFalse(b'f2' in os.listdir(target_path))
  172. def test_no_checkout_with_bare(self):
  173. f1_1 = make_object(Blob, data=b'f1')
  174. commit_spec = [[1]]
  175. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]}
  176. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  177. self.repo.refs[b"refs/heads/master"] = c1.id
  178. self.repo.refs[b"HEAD"] = c1.id
  179. target_path = tempfile.mkdtemp()
  180. errstream = BytesIO()
  181. self.addCleanup(shutil.rmtree, target_path)
  182. self.assertRaises(
  183. ValueError, porcelain.clone, self.repo.path,
  184. target_path, checkout=True, bare=True, errstream=errstream)
  185. def test_no_head_no_checkout(self):
  186. f1_1 = make_object(Blob, data=b'f1')
  187. commit_spec = [[1]]
  188. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]}
  189. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  190. self.repo.refs[b"refs/heads/master"] = c1.id
  191. target_path = tempfile.mkdtemp()
  192. self.addCleanup(shutil.rmtree, target_path)
  193. errstream = BytesIO()
  194. r = porcelain.clone(
  195. self.repo.path, target_path, checkout=True, errstream=errstream)
  196. r.close()
  197. class InitTests(TestCase):
  198. def test_non_bare(self):
  199. repo_dir = tempfile.mkdtemp()
  200. self.addCleanup(shutil.rmtree, repo_dir)
  201. porcelain.init(repo_dir)
  202. def test_bare(self):
  203. repo_dir = tempfile.mkdtemp()
  204. self.addCleanup(shutil.rmtree, repo_dir)
  205. porcelain.init(repo_dir, bare=True)
  206. class AddTests(PorcelainTestCase):
  207. def test_add_default_paths(self):
  208. # create a file for initial commit
  209. fullpath = os.path.join(self.repo.path, 'blah')
  210. with open(fullpath, 'w') as f:
  211. f.write("\n")
  212. porcelain.add(repo=self.repo.path, paths=[fullpath])
  213. porcelain.commit(repo=self.repo.path, message=b'test',
  214. author=b'test <email>', committer=b'test <email>')
  215. # Add a second test file and a file in a directory
  216. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  217. f.write("\n")
  218. os.mkdir(os.path.join(self.repo.path, 'adir'))
  219. with open(os.path.join(self.repo.path, 'adir', 'afile'), 'w') as f:
  220. f.write("\n")
  221. cwd = os.getcwd()
  222. try:
  223. os.chdir(self.repo.path)
  224. porcelain.add(self.repo.path)
  225. finally:
  226. os.chdir(cwd)
  227. # Check that foo was added and nothing in .git was modified
  228. index = self.repo.open_index()
  229. self.assertEqual(sorted(index), [b'adir/afile', b'blah', b'foo'])
  230. def test_add_default_paths_subdir(self):
  231. os.mkdir(os.path.join(self.repo.path, 'foo'))
  232. with open(os.path.join(self.repo.path, 'blah'), 'w') as f:
  233. f.write("\n")
  234. with open(os.path.join(self.repo.path, 'foo', 'blie'), 'w') as f:
  235. f.write("\n")
  236. cwd = os.getcwd()
  237. try:
  238. os.chdir(os.path.join(self.repo.path, 'foo'))
  239. porcelain.add(repo=self.repo.path)
  240. porcelain.commit(repo=self.repo.path, message=b'test',
  241. author=b'test <email>',
  242. committer=b'test <email>')
  243. finally:
  244. os.chdir(cwd)
  245. index = self.repo.open_index()
  246. self.assertEqual(sorted(index), [b'foo/blie'])
  247. def test_add_file(self):
  248. fullpath = os.path.join(self.repo.path, 'foo')
  249. with open(fullpath, 'w') as f:
  250. f.write("BAR")
  251. porcelain.add(self.repo.path, paths=[fullpath])
  252. self.assertIn(b"foo", self.repo.open_index())
  253. def test_add_ignored(self):
  254. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  255. f.write("foo")
  256. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  257. f.write("BAR")
  258. with open(os.path.join(self.repo.path, 'bar'), 'w') as f:
  259. f.write("BAR")
  260. (added, ignored) = porcelain.add(self.repo.path, paths=[
  261. os.path.join(self.repo.path, "foo"),
  262. os.path.join(self.repo.path, "bar")])
  263. self.assertIn(b"bar", self.repo.open_index())
  264. self.assertEqual(set(['bar']), set(added))
  265. self.assertEqual(set(['foo']), ignored)
  266. def test_add_file_absolute_path(self):
  267. # Absolute paths are (not yet) supported
  268. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  269. f.write("BAR")
  270. porcelain.add(self.repo, paths=[os.path.join(self.repo.path, "foo")])
  271. self.assertIn(b"foo", self.repo.open_index())
  272. def test_add_not_in_repo(self):
  273. with open(os.path.join(self.test_dir, 'foo'), 'w') as f:
  274. f.write("BAR")
  275. self.assertRaises(
  276. ValueError,
  277. porcelain.add, self.repo,
  278. paths=[os.path.join(self.test_dir, "foo")])
  279. self.assertRaises(
  280. ValueError,
  281. porcelain.add, self.repo,
  282. paths=["../foo"])
  283. self.assertEqual([], list(self.repo.open_index()))
  284. class RemoveTests(PorcelainTestCase):
  285. def test_remove_file(self):
  286. fullpath = os.path.join(self.repo.path, 'foo')
  287. with open(fullpath, 'w') as f:
  288. f.write("BAR")
  289. porcelain.add(self.repo.path, paths=[fullpath])
  290. porcelain.commit(repo=self.repo, message=b'test',
  291. author=b'test <email>',
  292. committer=b'test <email>')
  293. self.assertTrue(os.path.exists(os.path.join(self.repo.path, 'foo')))
  294. cwd = os.getcwd()
  295. try:
  296. os.chdir(self.repo.path)
  297. porcelain.remove(self.repo.path, paths=["foo"])
  298. finally:
  299. os.chdir(cwd)
  300. self.assertFalse(os.path.exists(os.path.join(self.repo.path, 'foo')))
  301. def test_remove_file_staged(self):
  302. fullpath = os.path.join(self.repo.path, 'foo')
  303. with open(fullpath, 'w') as f:
  304. f.write("BAR")
  305. cwd = os.getcwd()
  306. try:
  307. os.chdir(self.repo.path)
  308. porcelain.add(self.repo.path, paths=[fullpath])
  309. self.assertRaises(Exception, porcelain.rm, self.repo.path,
  310. paths=["foo"])
  311. finally:
  312. os.chdir(cwd)
  313. class LogTests(PorcelainTestCase):
  314. def test_simple(self):
  315. c1, c2, c3 = build_commit_graph(
  316. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  317. self.repo.refs[b"HEAD"] = c3.id
  318. outstream = StringIO()
  319. porcelain.log(self.repo.path, outstream=outstream)
  320. self.assertEqual(3, outstream.getvalue().count("-" * 50))
  321. def test_max_entries(self):
  322. c1, c2, c3 = build_commit_graph(
  323. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  324. self.repo.refs[b"HEAD"] = c3.id
  325. outstream = StringIO()
  326. porcelain.log(self.repo.path, outstream=outstream, max_entries=1)
  327. self.assertEqual(1, outstream.getvalue().count("-" * 50))
  328. class ShowTests(PorcelainTestCase):
  329. def test_nolist(self):
  330. c1, c2, c3 = build_commit_graph(
  331. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  332. self.repo.refs[b"HEAD"] = c3.id
  333. outstream = StringIO()
  334. porcelain.show(self.repo.path, objects=c3.id, outstream=outstream)
  335. self.assertTrue(outstream.getvalue().startswith("-" * 50))
  336. def test_simple(self):
  337. c1, c2, c3 = build_commit_graph(
  338. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  339. self.repo.refs[b"HEAD"] = c3.id
  340. outstream = StringIO()
  341. porcelain.show(self.repo.path, objects=[c3.id], outstream=outstream)
  342. self.assertTrue(outstream.getvalue().startswith("-" * 50))
  343. def test_blob(self):
  344. b = Blob.from_string(b"The Foo\n")
  345. self.repo.object_store.add_object(b)
  346. outstream = StringIO()
  347. porcelain.show(self.repo.path, objects=[b.id], outstream=outstream)
  348. self.assertEqual(outstream.getvalue(), "The Foo\n")
  349. def test_commit_no_parent(self):
  350. a = Blob.from_string(b"The Foo\n")
  351. ta = Tree()
  352. ta.add(b"somename", 0o100644, a.id)
  353. ca = make_commit(tree=ta.id)
  354. self.repo.object_store.add_objects([(a, None), (ta, None), (ca, None)])
  355. outstream = StringIO()
  356. porcelain.show(self.repo.path, objects=[ca.id], outstream=outstream)
  357. self.assertMultiLineEqual(outstream.getvalue(), """\
  358. --------------------------------------------------
  359. commit: 344da06c1bb85901270b3e8875c988a027ec087d
  360. Author: Test Author <test@nodomain.com>
  361. Committer: Test Committer <test@nodomain.com>
  362. Date: Fri Jan 01 2010 00:00:00 +0000
  363. Test message.
  364. diff --git /dev/null b/somename
  365. new mode 100644
  366. index 0000000..ea5c7bf 100644
  367. --- /dev/null
  368. +++ b/somename
  369. @@ -0,0 +1 @@
  370. +The Foo
  371. """)
  372. def test_commit_with_change(self):
  373. a = Blob.from_string(b"The Foo\n")
  374. ta = Tree()
  375. ta.add(b"somename", 0o100644, a.id)
  376. ca = make_commit(tree=ta.id)
  377. b = Blob.from_string(b"The Bar\n")
  378. tb = Tree()
  379. tb.add(b"somename", 0o100644, b.id)
  380. cb = make_commit(tree=tb.id, parents=[ca.id])
  381. self.repo.object_store.add_objects(
  382. [(a, None), (b, None), (ta, None), (tb, None),
  383. (ca, None), (cb, None)])
  384. outstream = StringIO()
  385. porcelain.show(self.repo.path, objects=[cb.id], outstream=outstream)
  386. self.assertMultiLineEqual(outstream.getvalue(), """\
  387. --------------------------------------------------
  388. commit: 2c6b6c9cb72c130956657e1fdae58e5b103744fa
  389. Author: Test Author <test@nodomain.com>
  390. Committer: Test Committer <test@nodomain.com>
  391. Date: Fri Jan 01 2010 00:00:00 +0000
  392. Test message.
  393. diff --git a/somename b/somename
  394. index ea5c7bf..fd38bcb 100644
  395. --- a/somename
  396. +++ b/somename
  397. @@ -1 +1 @@
  398. -The Foo
  399. +The Bar
  400. """)
  401. class SymbolicRefTests(PorcelainTestCase):
  402. def test_set_wrong_symbolic_ref(self):
  403. c1, c2, c3 = build_commit_graph(
  404. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  405. self.repo.refs[b"HEAD"] = c3.id
  406. self.assertRaises(ValueError, porcelain.symbolic_ref, self.repo.path,
  407. b'foobar')
  408. def test_set_force_wrong_symbolic_ref(self):
  409. c1, c2, c3 = build_commit_graph(
  410. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  411. self.repo.refs[b"HEAD"] = c3.id
  412. porcelain.symbolic_ref(self.repo.path, b'force_foobar', force=True)
  413. # test if we actually changed the file
  414. with self.repo.get_named_file('HEAD') as f:
  415. new_ref = f.read()
  416. self.assertEqual(new_ref, b'ref: refs/heads/force_foobar\n')
  417. def test_set_symbolic_ref(self):
  418. c1, c2, c3 = build_commit_graph(
  419. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  420. self.repo.refs[b"HEAD"] = c3.id
  421. porcelain.symbolic_ref(self.repo.path, b'master')
  422. def test_set_symbolic_ref_other_than_master(self):
  423. c1, c2, c3 = build_commit_graph(
  424. self.repo.object_store, [[1], [2, 1], [3, 1, 2]],
  425. attrs=dict(refs='develop'))
  426. self.repo.refs[b"HEAD"] = c3.id
  427. self.repo.refs[b"refs/heads/develop"] = c3.id
  428. porcelain.symbolic_ref(self.repo.path, b'develop')
  429. # test if we actually changed the file
  430. with self.repo.get_named_file('HEAD') as f:
  431. new_ref = f.read()
  432. self.assertEqual(new_ref, b'ref: refs/heads/develop\n')
  433. class DiffTreeTests(PorcelainTestCase):
  434. def test_empty(self):
  435. c1, c2, c3 = build_commit_graph(
  436. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  437. self.repo.refs[b"HEAD"] = c3.id
  438. outstream = BytesIO()
  439. porcelain.diff_tree(self.repo.path, c2.tree, c3.tree,
  440. outstream=outstream)
  441. self.assertEqual(outstream.getvalue(), b"")
  442. class CommitTreeTests(PorcelainTestCase):
  443. def test_simple(self):
  444. c1, c2, c3 = build_commit_graph(
  445. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  446. b = Blob()
  447. b.data = b"foo the bar"
  448. t = Tree()
  449. t.add(b"somename", 0o100644, b.id)
  450. self.repo.object_store.add_object(t)
  451. self.repo.object_store.add_object(b)
  452. sha = porcelain.commit_tree(
  453. self.repo.path, t.id, message=b"Withcommit.",
  454. author=b"Joe <joe@example.com>",
  455. committer=b"Jane <jane@example.com>")
  456. self.assertTrue(isinstance(sha, bytes))
  457. self.assertEqual(len(sha), 40)
  458. class RevListTests(PorcelainTestCase):
  459. def test_simple(self):
  460. c1, c2, c3 = build_commit_graph(
  461. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  462. outstream = BytesIO()
  463. porcelain.rev_list(
  464. self.repo.path, [c3.id], outstream=outstream)
  465. self.assertEqual(
  466. c3.id + b"\n" +
  467. c2.id + b"\n" +
  468. c1.id + b"\n",
  469. outstream.getvalue())
  470. class TagCreateTests(PorcelainTestCase):
  471. def test_annotated(self):
  472. c1, c2, c3 = build_commit_graph(
  473. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  474. self.repo.refs[b"HEAD"] = c3.id
  475. porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
  476. b'bar', annotated=True)
  477. tags = self.repo.refs.as_dict(b"refs/tags")
  478. self.assertEqual(list(tags.keys()), [b"tryme"])
  479. tag = self.repo[b'refs/tags/tryme']
  480. self.assertTrue(isinstance(tag, Tag))
  481. self.assertEqual(b"foo <foo@bar.com>", tag.tagger)
  482. self.assertEqual(b"bar", tag.message)
  483. self.assertLess(time.time() - tag.tag_time, 5)
  484. def test_unannotated(self):
  485. c1, c2, c3 = build_commit_graph(
  486. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  487. self.repo.refs[b"HEAD"] = c3.id
  488. porcelain.tag_create(self.repo.path, b"tryme", annotated=False)
  489. tags = self.repo.refs.as_dict(b"refs/tags")
  490. self.assertEqual(list(tags.keys()), [b"tryme"])
  491. self.repo[b'refs/tags/tryme']
  492. self.assertEqual(list(tags.values()), [self.repo.head()])
  493. def test_unannotated_unicode(self):
  494. c1, c2, c3 = build_commit_graph(
  495. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  496. self.repo.refs[b"HEAD"] = c3.id
  497. porcelain.tag_create(self.repo.path, "tryme", annotated=False)
  498. tags = self.repo.refs.as_dict(b"refs/tags")
  499. self.assertEqual(list(tags.keys()), [b"tryme"])
  500. self.repo[b'refs/tags/tryme']
  501. self.assertEqual(list(tags.values()), [self.repo.head()])
  502. class TagListTests(PorcelainTestCase):
  503. def test_empty(self):
  504. tags = porcelain.tag_list(self.repo.path)
  505. self.assertEqual([], tags)
  506. def test_simple(self):
  507. self.repo.refs[b"refs/tags/foo"] = b"aa" * 20
  508. self.repo.refs[b"refs/tags/bar/bla"] = b"bb" * 20
  509. tags = porcelain.tag_list(self.repo.path)
  510. self.assertEqual([b"bar/bla", b"foo"], tags)
  511. class TagDeleteTests(PorcelainTestCase):
  512. def test_simple(self):
  513. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  514. self.repo[b"HEAD"] = c1.id
  515. porcelain.tag_create(self.repo, b'foo')
  516. self.assertTrue(b"foo" in porcelain.tag_list(self.repo))
  517. porcelain.tag_delete(self.repo, b'foo')
  518. self.assertFalse(b"foo" in porcelain.tag_list(self.repo))
  519. class ResetTests(PorcelainTestCase):
  520. def test_hard_head(self):
  521. fullpath = os.path.join(self.repo.path, 'foo')
  522. with open(fullpath, 'w') as f:
  523. f.write("BAR")
  524. porcelain.add(self.repo.path, paths=[fullpath])
  525. porcelain.commit(self.repo.path, message=b"Some message",
  526. committer=b"Jane <jane@example.com>",
  527. author=b"John <john@example.com>")
  528. with open(os.path.join(self.repo.path, 'foo'), 'wb') as f:
  529. f.write(b"OOH")
  530. porcelain.reset(self.repo, "hard", b"HEAD")
  531. index = self.repo.open_index()
  532. changes = list(tree_changes(self.repo,
  533. index.commit(self.repo.object_store),
  534. self.repo[b'HEAD'].tree))
  535. self.assertEqual([], changes)
  536. def test_hard_commit(self):
  537. fullpath = os.path.join(self.repo.path, 'foo')
  538. with open(fullpath, 'w') as f:
  539. f.write("BAR")
  540. porcelain.add(self.repo.path, paths=[fullpath])
  541. sha = porcelain.commit(self.repo.path, message=b"Some message",
  542. committer=b"Jane <jane@example.com>",
  543. author=b"John <john@example.com>")
  544. with open(fullpath, 'wb') as f:
  545. f.write(b"BAZ")
  546. porcelain.add(self.repo.path, paths=[fullpath])
  547. porcelain.commit(self.repo.path, message=b"Some other message",
  548. committer=b"Jane <jane@example.com>",
  549. author=b"John <john@example.com>")
  550. porcelain.reset(self.repo, "hard", sha)
  551. index = self.repo.open_index()
  552. changes = list(tree_changes(self.repo,
  553. index.commit(self.repo.object_store),
  554. self.repo[sha].tree))
  555. self.assertEqual([], changes)
  556. class PushTests(PorcelainTestCase):
  557. def test_simple(self):
  558. """
  559. Basic test of porcelain push where self.repo is the remote. First
  560. clone the remote, commit a file to the clone, then push the changes
  561. back to the remote.
  562. """
  563. outstream = BytesIO()
  564. errstream = BytesIO()
  565. porcelain.commit(repo=self.repo.path, message=b'init',
  566. author=b'author <email>',
  567. committer=b'committer <email>')
  568. # Setup target repo cloned from temp test repo
  569. clone_path = tempfile.mkdtemp()
  570. self.addCleanup(shutil.rmtree, clone_path)
  571. target_repo = porcelain.clone(self.repo.path, target=clone_path,
  572. errstream=errstream)
  573. try:
  574. self.assertEqual(target_repo[b'HEAD'], self.repo[b'HEAD'])
  575. finally:
  576. target_repo.close()
  577. # create a second file to be pushed back to origin
  578. handle, fullpath = tempfile.mkstemp(dir=clone_path)
  579. os.close(handle)
  580. porcelain.add(repo=clone_path, paths=[fullpath])
  581. porcelain.commit(repo=clone_path, message=b'push',
  582. author=b'author <email>',
  583. committer=b'committer <email>')
  584. # Setup a non-checked out branch in the remote
  585. refs_path = b"refs/heads/foo"
  586. new_id = self.repo[b'HEAD'].id
  587. self.assertNotEqual(new_id, ZERO_SHA)
  588. self.repo.refs[refs_path] = new_id
  589. # Push to the remote
  590. porcelain.push(clone_path, self.repo.path, b"HEAD:" + refs_path,
  591. outstream=outstream, errstream=errstream)
  592. # Check that the target and source
  593. with Repo(clone_path) as r_clone:
  594. self.assertEqual({
  595. b'HEAD': new_id,
  596. b'refs/heads/foo': r_clone[b'HEAD'].id,
  597. b'refs/heads/master': new_id,
  598. }, self.repo.get_refs())
  599. self.assertEqual(r_clone[b'HEAD'].id, self.repo[refs_path].id)
  600. # Get the change in the target repo corresponding to the add
  601. # this will be in the foo branch.
  602. change = list(tree_changes(self.repo, self.repo[b'HEAD'].tree,
  603. self.repo[b'refs/heads/foo'].tree))[0]
  604. self.assertEqual(os.path.basename(fullpath),
  605. change.new.path.decode('ascii'))
  606. def test_delete(self):
  607. """Basic test of porcelain push, removing a branch.
  608. """
  609. outstream = BytesIO()
  610. errstream = BytesIO()
  611. porcelain.commit(repo=self.repo.path, message=b'init',
  612. author=b'author <email>',
  613. committer=b'committer <email>')
  614. # Setup target repo cloned from temp test repo
  615. clone_path = tempfile.mkdtemp()
  616. self.addCleanup(shutil.rmtree, clone_path)
  617. target_repo = porcelain.clone(self.repo.path, target=clone_path,
  618. errstream=errstream)
  619. target_repo.close()
  620. # Setup a non-checked out branch in the remote
  621. refs_path = b"refs/heads/foo"
  622. new_id = self.repo[b'HEAD'].id
  623. self.assertNotEqual(new_id, ZERO_SHA)
  624. self.repo.refs[refs_path] = new_id
  625. # Push to the remote
  626. porcelain.push(clone_path, self.repo.path, b":" + refs_path,
  627. outstream=outstream, errstream=errstream)
  628. self.assertEqual({
  629. b'HEAD': new_id,
  630. b'refs/heads/master': new_id,
  631. }, self.repo.get_refs())
  632. class PullTests(PorcelainTestCase):
  633. def setUp(self):
  634. super(PullTests, self).setUp()
  635. # create a file for initial commit
  636. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  637. os.close(handle)
  638. porcelain.add(repo=self.repo.path, paths=fullpath)
  639. porcelain.commit(repo=self.repo.path, message=b'test',
  640. author=b'test <email>',
  641. committer=b'test <email>')
  642. # Setup target repo
  643. self.target_path = tempfile.mkdtemp()
  644. self.addCleanup(shutil.rmtree, self.target_path)
  645. target_repo = porcelain.clone(self.repo.path, target=self.target_path,
  646. errstream=BytesIO())
  647. target_repo.close()
  648. # create a second file to be pushed
  649. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  650. os.close(handle)
  651. porcelain.add(repo=self.repo.path, paths=fullpath)
  652. porcelain.commit(repo=self.repo.path, message=b'test2',
  653. author=b'test2 <email>',
  654. committer=b'test2 <email>')
  655. self.assertTrue(b'refs/heads/master' in self.repo.refs)
  656. self.assertTrue(b'refs/heads/master' in target_repo.refs)
  657. def test_simple(self):
  658. outstream = BytesIO()
  659. errstream = BytesIO()
  660. # Pull changes into the cloned repo
  661. porcelain.pull(self.target_path, self.repo.path, b'refs/heads/master',
  662. outstream=outstream, errstream=errstream)
  663. # Check the target repo for pushed changes
  664. with Repo(self.target_path) as r:
  665. self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id)
  666. def test_no_refspec(self):
  667. outstream = BytesIO()
  668. errstream = BytesIO()
  669. # Pull changes into the cloned repo
  670. porcelain.pull(self.target_path, self.repo.path, outstream=outstream,
  671. errstream=errstream)
  672. # Check the target repo for pushed changes
  673. with Repo(self.target_path) as r:
  674. self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id)
  675. class StatusTests(PorcelainTestCase):
  676. def test_empty(self):
  677. results = porcelain.status(self.repo)
  678. self.assertEqual(
  679. {'add': [], 'delete': [], 'modify': []},
  680. results.staged)
  681. self.assertEqual([], results.unstaged)
  682. def test_status(self):
  683. """Integration test for `status` functionality."""
  684. # Commit a dummy file then modify it
  685. fullpath = os.path.join(self.repo.path, 'foo')
  686. with open(fullpath, 'w') as f:
  687. f.write('origstuff')
  688. porcelain.add(repo=self.repo.path, paths=[fullpath])
  689. porcelain.commit(repo=self.repo.path, message=b'test status',
  690. author=b'author <email>',
  691. committer=b'committer <email>')
  692. # modify access and modify time of path
  693. os.utime(fullpath, (0, 0))
  694. with open(fullpath, 'wb') as f:
  695. f.write(b'stuff')
  696. # Make a dummy file and stage it
  697. filename_add = 'bar'
  698. fullpath = os.path.join(self.repo.path, filename_add)
  699. with open(fullpath, 'w') as f:
  700. f.write('stuff')
  701. porcelain.add(repo=self.repo.path, paths=fullpath)
  702. results = porcelain.status(self.repo)
  703. self.assertEqual(results.staged['add'][0],
  704. filename_add.encode('ascii'))
  705. self.assertEqual(results.unstaged, [b'foo'])
  706. def test_get_tree_changes_add(self):
  707. """Unit test for get_tree_changes add."""
  708. # Make a dummy file, stage
  709. filename = 'bar'
  710. fullpath = os.path.join(self.repo.path, filename)
  711. with open(fullpath, 'w') as f:
  712. f.write('stuff')
  713. porcelain.add(repo=self.repo.path, paths=fullpath)
  714. porcelain.commit(repo=self.repo.path, message=b'test status',
  715. author=b'author <email>',
  716. committer=b'committer <email>')
  717. filename = 'foo'
  718. fullpath = os.path.join(self.repo.path, filename)
  719. with open(fullpath, 'w') as f:
  720. f.write('stuff')
  721. porcelain.add(repo=self.repo.path, paths=fullpath)
  722. changes = porcelain.get_tree_changes(self.repo.path)
  723. self.assertEqual(changes['add'][0], filename.encode('ascii'))
  724. self.assertEqual(len(changes['add']), 1)
  725. self.assertEqual(len(changes['modify']), 0)
  726. self.assertEqual(len(changes['delete']), 0)
  727. def test_get_tree_changes_modify(self):
  728. """Unit test for get_tree_changes modify."""
  729. # Make a dummy file, stage, commit, modify
  730. filename = 'foo'
  731. fullpath = os.path.join(self.repo.path, filename)
  732. with open(fullpath, 'w') as f:
  733. f.write('stuff')
  734. porcelain.add(repo=self.repo.path, paths=fullpath)
  735. porcelain.commit(repo=self.repo.path, message=b'test status',
  736. author=b'author <email>',
  737. committer=b'committer <email>')
  738. with open(fullpath, 'w') as f:
  739. f.write('otherstuff')
  740. porcelain.add(repo=self.repo.path, paths=fullpath)
  741. changes = porcelain.get_tree_changes(self.repo.path)
  742. self.assertEqual(changes['modify'][0], filename.encode('ascii'))
  743. self.assertEqual(len(changes['add']), 0)
  744. self.assertEqual(len(changes['modify']), 1)
  745. self.assertEqual(len(changes['delete']), 0)
  746. def test_get_tree_changes_delete(self):
  747. """Unit test for get_tree_changes delete."""
  748. # Make a dummy file, stage, commit, remove
  749. filename = 'foo'
  750. fullpath = os.path.join(self.repo.path, filename)
  751. with open(fullpath, 'w') as f:
  752. f.write('stuff')
  753. porcelain.add(repo=self.repo.path, paths=fullpath)
  754. porcelain.commit(repo=self.repo.path, message=b'test status',
  755. author=b'author <email>',
  756. committer=b'committer <email>')
  757. cwd = os.getcwd()
  758. try:
  759. os.chdir(self.repo.path)
  760. porcelain.remove(repo=self.repo.path, paths=[filename])
  761. finally:
  762. os.chdir(cwd)
  763. changes = porcelain.get_tree_changes(self.repo.path)
  764. self.assertEqual(changes['delete'][0], filename.encode('ascii'))
  765. self.assertEqual(len(changes['add']), 0)
  766. self.assertEqual(len(changes['modify']), 0)
  767. self.assertEqual(len(changes['delete']), 1)
  768. def test_get_untracked_paths(self):
  769. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  770. f.write('ignored\n')
  771. with open(os.path.join(self.repo.path, 'ignored'), 'w') as f:
  772. f.write('blah\n')
  773. with open(os.path.join(self.repo.path, 'notignored'), 'w') as f:
  774. f.write('blah\n')
  775. self.assertEqual(
  776. set(['ignored', 'notignored', '.gitignore']),
  777. set(porcelain.get_untracked_paths(self.repo.path, self.repo.path,
  778. self.repo.open_index())))
  779. self.assertEqual(set(['.gitignore', 'notignored']),
  780. set(porcelain.status(self.repo).untracked))
  781. self.assertEqual(set(['.gitignore', 'notignored', 'ignored']),
  782. set(porcelain.status(self.repo, ignored=True)
  783. .untracked))
  784. def test_get_untracked_paths_nested(self):
  785. with open(os.path.join(self.repo.path, 'notignored'), 'w') as f:
  786. f.write('blah\n')
  787. subrepo = Repo.init(os.path.join(self.repo.path, 'nested'), mkdir=True)
  788. with open(os.path.join(subrepo.path, 'another'), 'w') as f:
  789. f.write('foo\n')
  790. self.assertEqual(
  791. set(['notignored']),
  792. set(porcelain.get_untracked_paths(self.repo.path, self.repo.path,
  793. self.repo.open_index())))
  794. self.assertEqual(
  795. set(['another']),
  796. set(porcelain.get_untracked_paths(subrepo.path, subrepo.path,
  797. subrepo.open_index())))
  798. # TODO(jelmer): Add test for dulwich.porcelain.daemon
  799. class UploadPackTests(PorcelainTestCase):
  800. """Tests for upload_pack."""
  801. def test_upload_pack(self):
  802. outf = BytesIO()
  803. exitcode = porcelain.upload_pack(
  804. self.repo.path, BytesIO(b"0000"), outf)
  805. outlines = outf.getvalue().splitlines()
  806. self.assertEqual([b"0000"], outlines)
  807. self.assertEqual(0, exitcode)
  808. class ReceivePackTests(PorcelainTestCase):
  809. """Tests for receive_pack."""
  810. def test_receive_pack(self):
  811. filename = 'foo'
  812. fullpath = os.path.join(self.repo.path, filename)
  813. with open(fullpath, 'w') as f:
  814. f.write('stuff')
  815. porcelain.add(repo=self.repo.path, paths=fullpath)
  816. self.repo.do_commit(message=b'test status',
  817. author=b'author <email>',
  818. committer=b'committer <email>',
  819. author_timestamp=1402354300,
  820. commit_timestamp=1402354300, author_timezone=0,
  821. commit_timezone=0)
  822. outf = BytesIO()
  823. exitcode = porcelain.receive_pack(
  824. self.repo.path, BytesIO(b"0000"), outf)
  825. outlines = outf.getvalue().splitlines()
  826. self.assertEqual([
  827. b'0091319b56ce3aee2d489f759736a79cc552c9bb86d9 HEAD\x00 report-status ' # noqa: E501
  828. b'delete-refs quiet ofs-delta side-band-64k '
  829. b'no-done symref=HEAD:refs/heads/master',
  830. b'003f319b56ce3aee2d489f759736a79cc552c9bb86d9 refs/heads/master',
  831. b'0000'], outlines)
  832. self.assertEqual(0, exitcode)
  833. class BranchListTests(PorcelainTestCase):
  834. def test_standard(self):
  835. self.assertEqual(set([]), set(porcelain.branch_list(self.repo)))
  836. def test_new_branch(self):
  837. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  838. self.repo[b"HEAD"] = c1.id
  839. porcelain.branch_create(self.repo, b"foo")
  840. self.assertEqual(
  841. set([b"master", b"foo"]),
  842. set(porcelain.branch_list(self.repo)))
  843. class BranchCreateTests(PorcelainTestCase):
  844. def test_branch_exists(self):
  845. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  846. self.repo[b"HEAD"] = c1.id
  847. porcelain.branch_create(self.repo, b"foo")
  848. self.assertRaises(KeyError, porcelain.branch_create, self.repo, b"foo")
  849. porcelain.branch_create(self.repo, b"foo", force=True)
  850. def test_new_branch(self):
  851. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  852. self.repo[b"HEAD"] = c1.id
  853. porcelain.branch_create(self.repo, b"foo")
  854. self.assertEqual(
  855. set([b"master", b"foo"]),
  856. set(porcelain.branch_list(self.repo)))
  857. class BranchDeleteTests(PorcelainTestCase):
  858. def test_simple(self):
  859. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  860. self.repo[b"HEAD"] = c1.id
  861. porcelain.branch_create(self.repo, b'foo')
  862. self.assertTrue(b"foo" in porcelain.branch_list(self.repo))
  863. porcelain.branch_delete(self.repo, b'foo')
  864. self.assertFalse(b"foo" in porcelain.branch_list(self.repo))
  865. def test_simple_unicode(self):
  866. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  867. self.repo[b"HEAD"] = c1.id
  868. porcelain.branch_create(self.repo, 'foo')
  869. self.assertTrue(b"foo" in porcelain.branch_list(self.repo))
  870. porcelain.branch_delete(self.repo, 'foo')
  871. self.assertFalse(b"foo" in porcelain.branch_list(self.repo))
  872. class FetchTests(PorcelainTestCase):
  873. def test_simple(self):
  874. outstream = BytesIO()
  875. errstream = BytesIO()
  876. # create a file for initial commit
  877. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  878. os.close(handle)
  879. porcelain.add(repo=self.repo.path, paths=fullpath)
  880. porcelain.commit(repo=self.repo.path, message=b'test',
  881. author=b'test <email>',
  882. committer=b'test <email>')
  883. # Setup target repo
  884. target_path = tempfile.mkdtemp()
  885. self.addCleanup(shutil.rmtree, target_path)
  886. target_repo = porcelain.clone(self.repo.path, target=target_path,
  887. errstream=errstream)
  888. # create a second file to be pushed
  889. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  890. os.close(handle)
  891. porcelain.add(repo=self.repo.path, paths=fullpath)
  892. porcelain.commit(repo=self.repo.path, message=b'test2',
  893. author=b'test2 <email>',
  894. committer=b'test2 <email>')
  895. self.assertFalse(self.repo[b'HEAD'].id in target_repo)
  896. target_repo.close()
  897. # Fetch changes into the cloned repo
  898. porcelain.fetch(target_path, self.repo.path,
  899. outstream=outstream, errstream=errstream)
  900. # Check the target repo for pushed changes
  901. with Repo(target_path) as r:
  902. self.assertTrue(self.repo[b'HEAD'].id in r)
  903. def test_with_remote_name(self):
  904. remote_name = b'origin'
  905. outstream = BytesIO()
  906. errstream = BytesIO()
  907. # create a file for initial commit
  908. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  909. os.close(handle)
  910. porcelain.add(repo=self.repo.path, paths=fullpath)
  911. porcelain.commit(repo=self.repo.path, message=b'test',
  912. author=b'test <email>',
  913. committer=b'test <email>')
  914. # Setup target repo
  915. target_path = tempfile.mkdtemp()
  916. self.addCleanup(shutil.rmtree, target_path)
  917. target_repo = porcelain.clone(self.repo.path, target=target_path,
  918. errstream=errstream)
  919. # Capture current refs
  920. target_refs = target_repo.get_refs()
  921. # create a second file to be pushed
  922. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  923. os.close(handle)
  924. porcelain.add(repo=self.repo.path, paths=fullpath)
  925. porcelain.commit(repo=self.repo.path, message=b'test2',
  926. author=b'test2 <email>',
  927. committer=b'test2 <email>')
  928. self.assertFalse(self.repo[b'HEAD'].id in target_repo)
  929. target_repo.close()
  930. # Fetch changes into the cloned repo
  931. porcelain.fetch(target_path, self.repo.path, remote_name=remote_name,
  932. outstream=outstream, errstream=errstream)
  933. # Check the target repo for pushed changes, as well as updates
  934. # for the refs
  935. with Repo(target_path) as r:
  936. self.assertTrue(self.repo[b'HEAD'].id in r)
  937. self.assertNotEqual(self.repo.get_refs(), target_refs)
  938. class RepackTests(PorcelainTestCase):
  939. def test_empty(self):
  940. porcelain.repack(self.repo)
  941. def test_simple(self):
  942. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  943. os.close(handle)
  944. porcelain.add(repo=self.repo.path, paths=fullpath)
  945. porcelain.repack(self.repo)
  946. class LsTreeTests(PorcelainTestCase):
  947. def test_empty(self):
  948. porcelain.commit(repo=self.repo.path, message=b'test status',
  949. author=b'author <email>',
  950. committer=b'committer <email>')
  951. f = StringIO()
  952. porcelain.ls_tree(self.repo, b"HEAD", outstream=f)
  953. self.assertEqual(f.getvalue(), "")
  954. def test_simple(self):
  955. # Commit a dummy file then modify it
  956. fullpath = os.path.join(self.repo.path, 'foo')
  957. with open(fullpath, 'w') as f:
  958. f.write('origstuff')
  959. porcelain.add(repo=self.repo.path, paths=[fullpath])
  960. porcelain.commit(repo=self.repo.path, message=b'test status',
  961. author=b'author <email>',
  962. committer=b'committer <email>')
  963. f = StringIO()
  964. porcelain.ls_tree(self.repo, b"HEAD", outstream=f)
  965. self.assertEqual(
  966. f.getvalue(),
  967. '100644 blob 8b82634d7eae019850bb883f06abf428c58bc9aa\tfoo\n')
  968. class LsRemoteTests(PorcelainTestCase):
  969. def test_empty(self):
  970. self.assertEqual({}, porcelain.ls_remote(self.repo.path))
  971. def test_some(self):
  972. cid = porcelain.commit(repo=self.repo.path, message=b'test status',
  973. author=b'author <email>',
  974. committer=b'committer <email>')
  975. self.assertEqual({
  976. b'refs/heads/master': cid,
  977. b'HEAD': cid},
  978. porcelain.ls_remote(self.repo.path))
  979. class RemoteAddTests(PorcelainTestCase):
  980. def test_new(self):
  981. porcelain.remote_add(
  982. self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich')
  983. c = self.repo.get_config()
  984. self.assertEqual(
  985. c.get((b'remote', b'jelmer'), b'url'),
  986. b'git://jelmer.uk/code/dulwich')
  987. def test_exists(self):
  988. porcelain.remote_add(
  989. self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich')
  990. self.assertRaises(porcelain.RemoteExists, porcelain.remote_add,
  991. self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich')
  992. class CheckIgnoreTests(PorcelainTestCase):
  993. def test_check_ignored(self):
  994. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  995. f.write("foo")
  996. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  997. f.write("BAR")
  998. with open(os.path.join(self.repo.path, 'bar'), 'w') as f:
  999. f.write("BAR")
  1000. self.assertEqual(
  1001. ['foo'],
  1002. list(porcelain.check_ignore(self.repo, ['foo'])))
  1003. self.assertEqual([], list(porcelain.check_ignore(self.repo, ['bar'])))
  1004. def test_check_added(self):
  1005. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  1006. f.write("BAR")
  1007. self.repo.stage(['foo'])
  1008. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  1009. f.write("foo\n")
  1010. self.assertEqual(
  1011. [], list(porcelain.check_ignore(self.repo, ['foo'])))
  1012. self.assertEqual(
  1013. ['foo'],
  1014. list(porcelain.check_ignore(self.repo, ['foo'], no_index=True)))
  1015. class UpdateHeadTests(PorcelainTestCase):
  1016. def test_set_to_branch(self):
  1017. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1018. self.repo.refs[b"refs/heads/blah"] = c1.id
  1019. porcelain.update_head(self.repo, "blah")
  1020. self.assertEqual(c1.id, self.repo.head())
  1021. self.assertEqual(b'ref: refs/heads/blah',
  1022. self.repo.refs.read_ref('HEAD'))
  1023. def test_set_to_branch_detached(self):
  1024. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1025. self.repo.refs[b"refs/heads/blah"] = c1.id
  1026. porcelain.update_head(self.repo, "blah", detached=True)
  1027. self.assertEqual(c1.id, self.repo.head())
  1028. self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD'))
  1029. def test_set_to_commit_detached(self):
  1030. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1031. self.repo.refs[b"refs/heads/blah"] = c1.id
  1032. porcelain.update_head(self.repo, c1.id, detached=True)
  1033. self.assertEqual(c1.id, self.repo.head())
  1034. self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD'))
  1035. def test_set_new_branch(self):
  1036. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1037. self.repo.refs[b"refs/heads/blah"] = c1.id
  1038. porcelain.update_head(self.repo, "blah", new_branch="bar")
  1039. self.assertEqual(c1.id, self.repo.head())
  1040. self.assertEqual(b'ref: refs/heads/bar',
  1041. self.repo.refs.read_ref(b'HEAD'))
  1042. class MailmapTests(PorcelainTestCase):
  1043. def test_no_mailmap(self):
  1044. self.assertEqual(
  1045. 'Jelmer Vernooij <jelmer@samba.org>',
  1046. porcelain.check_mailmap(
  1047. self.repo, 'Jelmer Vernooij <jelmer@samba.org>'))
  1048. def test_mailmap_lookup(self):
  1049. with open(os.path.join(self.repo.path, '.mailmap'), 'w') as f:
  1050. f.write("""\
  1051. Jelmer Vernooij <jelmer@debian.org>
  1052. """)
  1053. self.assertEqual(
  1054. 'Jelmer Vernooij <jelmer@debian.org>',
  1055. porcelain.check_mailmap(
  1056. self.repo, 'Jelmer Vernooij <jelmer@samba.org>'))