2
0

test_porcelain.py 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858
  1. # test_porcelain.py -- porcelain tests
  2. # Copyright (C) 2013 Jelmer Vernooij <jelmer@jelmer.uk>
  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, StringIO
  22. import os
  23. import shutil
  24. import tarfile
  25. import tempfile
  26. import time
  27. from dulwich import porcelain
  28. from dulwich.diff_tree import tree_changes
  29. from dulwich.objects import (
  30. Blob,
  31. Tag,
  32. Tree,
  33. ZERO_SHA,
  34. )
  35. from dulwich.repo import (
  36. NoIndexPresent,
  37. Repo,
  38. )
  39. from dulwich.tests import (
  40. TestCase,
  41. )
  42. from dulwich.tests.utils import (
  43. build_commit_graph,
  44. make_commit,
  45. make_object,
  46. )
  47. def flat_walk_dir(dir_to_walk):
  48. for dirpath, _, filenames in os.walk(dir_to_walk):
  49. rel_dirpath = os.path.relpath(dirpath, dir_to_walk)
  50. if not dirpath == dir_to_walk:
  51. yield rel_dirpath
  52. for filename in filenames:
  53. if dirpath == dir_to_walk:
  54. yield filename
  55. else:
  56. yield os.path.join(rel_dirpath, filename)
  57. class PorcelainTestCase(TestCase):
  58. def setUp(self):
  59. super(PorcelainTestCase, self).setUp()
  60. self.test_dir = tempfile.mkdtemp()
  61. self.addCleanup(shutil.rmtree, self.test_dir)
  62. self.repo_path = os.path.join(self.test_dir, 'repo')
  63. self.repo = Repo.init(self.repo_path, mkdir=True)
  64. self.addCleanup(self.repo.close)
  65. class ArchiveTests(PorcelainTestCase):
  66. """Tests for the archive command."""
  67. def test_simple(self):
  68. c1, c2, c3 = build_commit_graph(
  69. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  70. self.repo.refs[b"refs/heads/master"] = c3.id
  71. out = BytesIO()
  72. err = BytesIO()
  73. porcelain.archive(self.repo.path, b"refs/heads/master", outstream=out,
  74. errstream=err)
  75. self.assertEqual(b"", err.getvalue())
  76. tf = tarfile.TarFile(fileobj=out)
  77. self.addCleanup(tf.close)
  78. self.assertEqual([], tf.getnames())
  79. class UpdateServerInfoTests(PorcelainTestCase):
  80. def test_simple(self):
  81. c1, c2, c3 = build_commit_graph(
  82. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  83. self.repo.refs[b"refs/heads/foo"] = c3.id
  84. porcelain.update_server_info(self.repo.path)
  85. self.assertTrue(os.path.exists(
  86. os.path.join(self.repo.controldir(), 'info', 'refs')))
  87. class CommitTests(PorcelainTestCase):
  88. def test_custom_author(self):
  89. c1, c2, c3 = build_commit_graph(
  90. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  91. self.repo.refs[b"refs/heads/foo"] = c3.id
  92. sha = porcelain.commit(
  93. self.repo.path, message=b"Some message",
  94. author=b"Joe <joe@example.com>",
  95. committer=b"Bob <bob@example.com>")
  96. self.assertTrue(isinstance(sha, bytes))
  97. self.assertEqual(len(sha), 40)
  98. def test_unicode(self):
  99. c1, c2, c3 = build_commit_graph(
  100. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  101. self.repo.refs[b"refs/heads/foo"] = c3.id
  102. sha = porcelain.commit(
  103. self.repo.path, message="Some message",
  104. author="Joe <joe@example.com>",
  105. committer="Bob <bob@example.com>")
  106. self.assertTrue(isinstance(sha, bytes))
  107. self.assertEqual(len(sha), 40)
  108. class CleanTests(PorcelainTestCase):
  109. def put_files(self, tracked, ignored, untracked, empty_dirs):
  110. """Put the described files in the wd
  111. """
  112. all_files = tracked | ignored | untracked
  113. for file_path in all_files:
  114. abs_path = os.path.join(self.repo.path, file_path)
  115. # File may need to be written in a dir that doesn't exist yet, so
  116. # create the parent dir(s) as necessary
  117. parent_dir = os.path.dirname(abs_path)
  118. try:
  119. os.makedirs(parent_dir)
  120. except FileExistsError:
  121. pass
  122. with open(abs_path, 'w') as f:
  123. f.write('')
  124. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  125. f.writelines(ignored)
  126. for dir_path in empty_dirs:
  127. os.mkdir(os.path.join(self.repo.path, 'empty_dir'))
  128. files_to_add = [os.path.join(self.repo.path, t) for t in tracked]
  129. porcelain.add(repo=self.repo.path, paths=files_to_add)
  130. porcelain.commit(repo=self.repo.path, message="init commit")
  131. def assert_wd(self, expected_paths):
  132. """Assert paths of files and dirs in wd are same as expected_paths
  133. """
  134. control_dir_rel = os.path.relpath(
  135. self.repo._controldir, self.repo.path)
  136. # normalize paths to simplify comparison across platforms
  137. found_paths = {
  138. os.path.normpath(p)
  139. for p in flat_walk_dir(self.repo.path)
  140. if not p.split(os.sep)[0] == control_dir_rel}
  141. norm_expected_paths = {os.path.normpath(p) for p in expected_paths}
  142. self.assertEqual(found_paths, norm_expected_paths)
  143. def test_from_root(self):
  144. self.put_files(
  145. tracked={
  146. 'tracked_file',
  147. 'tracked_dir/tracked_file',
  148. '.gitignore'},
  149. ignored={
  150. 'ignored_file'},
  151. untracked={
  152. 'untracked_file',
  153. 'tracked_dir/untracked_dir/untracked_file',
  154. 'untracked_dir/untracked_dir/untracked_file'},
  155. empty_dirs={
  156. 'empty_dir'})
  157. porcelain.clean(repo=self.repo.path, target_dir=self.repo.path)
  158. self.assert_wd({
  159. 'tracked_file',
  160. 'tracked_dir/tracked_file',
  161. '.gitignore',
  162. 'ignored_file',
  163. 'tracked_dir'})
  164. def test_from_subdir(self):
  165. self.put_files(
  166. tracked={
  167. 'tracked_file',
  168. 'tracked_dir/tracked_file',
  169. '.gitignore'},
  170. ignored={
  171. 'ignored_file'},
  172. untracked={
  173. 'untracked_file',
  174. 'tracked_dir/untracked_dir/untracked_file',
  175. 'untracked_dir/untracked_dir/untracked_file'},
  176. empty_dirs={
  177. 'empty_dir'})
  178. porcelain.clean(
  179. repo=self.repo,
  180. target_dir=os.path.join(self.repo.path, 'untracked_dir'))
  181. self.assert_wd({
  182. 'tracked_file',
  183. 'tracked_dir/tracked_file',
  184. '.gitignore',
  185. 'ignored_file',
  186. 'untracked_file',
  187. 'tracked_dir/untracked_dir/untracked_file',
  188. 'empty_dir',
  189. 'untracked_dir',
  190. 'tracked_dir',
  191. 'tracked_dir/untracked_dir'})
  192. class CloneTests(PorcelainTestCase):
  193. def test_simple_local(self):
  194. f1_1 = make_object(Blob, data=b'f1')
  195. commit_spec = [[1], [2, 1], [3, 1, 2]]
  196. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  197. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  198. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  199. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  200. commit_spec, trees)
  201. self.repo.refs[b"refs/heads/master"] = c3.id
  202. self.repo.refs[b"refs/tags/foo"] = c3.id
  203. target_path = tempfile.mkdtemp()
  204. errstream = BytesIO()
  205. self.addCleanup(shutil.rmtree, target_path)
  206. r = porcelain.clone(self.repo.path, target_path,
  207. checkout=False, errstream=errstream)
  208. self.addCleanup(r.close)
  209. self.assertEqual(r.path, target_path)
  210. target_repo = Repo(target_path)
  211. self.assertEqual(0, len(target_repo.open_index()))
  212. self.assertEqual(c3.id, target_repo.refs[b'refs/tags/foo'])
  213. self.assertTrue(b'f1' not in os.listdir(target_path))
  214. self.assertTrue(b'f2' not in os.listdir(target_path))
  215. c = r.get_config()
  216. encoded_path = self.repo.path
  217. if not isinstance(encoded_path, bytes):
  218. encoded_path = encoded_path.encode('utf-8')
  219. self.assertEqual(encoded_path, c.get((b'remote', b'origin'), b'url'))
  220. self.assertEqual(
  221. b'+refs/heads/*:refs/remotes/origin/*',
  222. c.get((b'remote', b'origin'), b'fetch'))
  223. def test_simple_local_with_checkout(self):
  224. f1_1 = make_object(Blob, data=b'f1')
  225. commit_spec = [[1], [2, 1], [3, 1, 2]]
  226. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  227. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  228. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  229. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  230. commit_spec, trees)
  231. self.repo.refs[b"refs/heads/master"] = c3.id
  232. target_path = tempfile.mkdtemp()
  233. errstream = BytesIO()
  234. self.addCleanup(shutil.rmtree, target_path)
  235. with porcelain.clone(self.repo.path, target_path,
  236. checkout=True,
  237. errstream=errstream) as r:
  238. self.assertEqual(r.path, target_path)
  239. with Repo(target_path) as r:
  240. self.assertEqual(r.head(), c3.id)
  241. self.assertTrue('f1' in os.listdir(target_path))
  242. self.assertTrue('f2' in os.listdir(target_path))
  243. def test_bare_local_with_checkout(self):
  244. f1_1 = make_object(Blob, data=b'f1')
  245. commit_spec = [[1], [2, 1], [3, 1, 2]]
  246. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)],
  247. 2: [(b'f1', f1_1), (b'f2', f1_1)],
  248. 3: [(b'f1', f1_1), (b'f2', f1_1)], }
  249. c1, c2, c3 = build_commit_graph(self.repo.object_store,
  250. commit_spec, trees)
  251. self.repo.refs[b"refs/heads/master"] = c3.id
  252. target_path = tempfile.mkdtemp()
  253. errstream = BytesIO()
  254. self.addCleanup(shutil.rmtree, target_path)
  255. with porcelain.clone(
  256. self.repo.path, target_path, bare=True,
  257. errstream=errstream) as r:
  258. self.assertEqual(r.path, target_path)
  259. with Repo(target_path) as r:
  260. r.head()
  261. self.assertRaises(NoIndexPresent, r.open_index)
  262. self.assertFalse(b'f1' in os.listdir(target_path))
  263. self.assertFalse(b'f2' in os.listdir(target_path))
  264. def test_no_checkout_with_bare(self):
  265. f1_1 = make_object(Blob, data=b'f1')
  266. commit_spec = [[1]]
  267. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]}
  268. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  269. self.repo.refs[b"refs/heads/master"] = c1.id
  270. self.repo.refs[b"HEAD"] = c1.id
  271. target_path = tempfile.mkdtemp()
  272. errstream = BytesIO()
  273. self.addCleanup(shutil.rmtree, target_path)
  274. self.assertRaises(
  275. ValueError, porcelain.clone, self.repo.path,
  276. target_path, checkout=True, bare=True, errstream=errstream)
  277. def test_no_head_no_checkout(self):
  278. f1_1 = make_object(Blob, data=b'f1')
  279. commit_spec = [[1]]
  280. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]}
  281. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  282. self.repo.refs[b"refs/heads/master"] = c1.id
  283. target_path = tempfile.mkdtemp()
  284. self.addCleanup(shutil.rmtree, target_path)
  285. errstream = BytesIO()
  286. r = porcelain.clone(
  287. self.repo.path, target_path, checkout=True, errstream=errstream)
  288. r.close()
  289. def test_no_head_no_checkout_outstream_errstream_autofallback(self):
  290. f1_1 = make_object(Blob, data=b'f1')
  291. commit_spec = [[1]]
  292. trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]}
  293. (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees)
  294. self.repo.refs[b"refs/heads/master"] = c1.id
  295. target_path = tempfile.mkdtemp()
  296. self.addCleanup(shutil.rmtree, target_path)
  297. errstream = porcelain.NoneStream()
  298. r = porcelain.clone(
  299. self.repo.path, target_path, checkout=True, errstream=errstream)
  300. r.close()
  301. def test_source_broken(self):
  302. target_path = tempfile.mkdtemp()
  303. self.assertRaises(
  304. Exception, porcelain.clone, '/nonexistant/repo', target_path)
  305. self.assertFalse(os.path.exists(target_path))
  306. class InitTests(TestCase):
  307. def test_non_bare(self):
  308. repo_dir = tempfile.mkdtemp()
  309. self.addCleanup(shutil.rmtree, repo_dir)
  310. porcelain.init(repo_dir)
  311. def test_bare(self):
  312. repo_dir = tempfile.mkdtemp()
  313. self.addCleanup(shutil.rmtree, repo_dir)
  314. porcelain.init(repo_dir, bare=True)
  315. class AddTests(PorcelainTestCase):
  316. def test_add_default_paths(self):
  317. # create a file for initial commit
  318. fullpath = os.path.join(self.repo.path, 'blah')
  319. with open(fullpath, 'w') as f:
  320. f.write("\n")
  321. porcelain.add(repo=self.repo.path, paths=[fullpath])
  322. porcelain.commit(repo=self.repo.path, message=b'test',
  323. author=b'test <email>', committer=b'test <email>')
  324. # Add a second test file and a file in a directory
  325. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  326. f.write("\n")
  327. os.mkdir(os.path.join(self.repo.path, 'adir'))
  328. with open(os.path.join(self.repo.path, 'adir', 'afile'), 'w') as f:
  329. f.write("\n")
  330. cwd = os.getcwd()
  331. try:
  332. os.chdir(self.repo.path)
  333. self.assertEqual(
  334. set(['foo', 'blah', 'adir', '.git']),
  335. set(os.listdir('.')))
  336. self.assertEqual(
  337. (['foo', os.path.join('adir', 'afile')], set()),
  338. porcelain.add(self.repo.path))
  339. finally:
  340. os.chdir(cwd)
  341. # Check that foo was added and nothing in .git was modified
  342. index = self.repo.open_index()
  343. self.assertEqual(sorted(index), [b'adir/afile', b'blah', b'foo'])
  344. def test_add_default_paths_subdir(self):
  345. os.mkdir(os.path.join(self.repo.path, 'foo'))
  346. with open(os.path.join(self.repo.path, 'blah'), 'w') as f:
  347. f.write("\n")
  348. with open(os.path.join(self.repo.path, 'foo', 'blie'), 'w') as f:
  349. f.write("\n")
  350. cwd = os.getcwd()
  351. try:
  352. os.chdir(os.path.join(self.repo.path, 'foo'))
  353. porcelain.add(repo=self.repo.path)
  354. porcelain.commit(repo=self.repo.path, message=b'test',
  355. author=b'test <email>',
  356. committer=b'test <email>')
  357. finally:
  358. os.chdir(cwd)
  359. index = self.repo.open_index()
  360. self.assertEqual(sorted(index), [b'foo/blie'])
  361. def test_add_file(self):
  362. fullpath = os.path.join(self.repo.path, 'foo')
  363. with open(fullpath, 'w') as f:
  364. f.write("BAR")
  365. porcelain.add(self.repo.path, paths=[fullpath])
  366. self.assertIn(b"foo", self.repo.open_index())
  367. def test_add_ignored(self):
  368. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  369. f.write("foo")
  370. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  371. f.write("BAR")
  372. with open(os.path.join(self.repo.path, 'bar'), 'w') as f:
  373. f.write("BAR")
  374. (added, ignored) = porcelain.add(self.repo.path, paths=[
  375. os.path.join(self.repo.path, "foo"),
  376. os.path.join(self.repo.path, "bar")])
  377. self.assertIn(b"bar", self.repo.open_index())
  378. self.assertEqual(set(['bar']), set(added))
  379. self.assertEqual(set(['foo']), ignored)
  380. def test_add_file_absolute_path(self):
  381. # Absolute paths are (not yet) supported
  382. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  383. f.write("BAR")
  384. porcelain.add(self.repo, paths=[os.path.join(self.repo.path, "foo")])
  385. self.assertIn(b"foo", self.repo.open_index())
  386. def test_add_not_in_repo(self):
  387. with open(os.path.join(self.test_dir, 'foo'), 'w') as f:
  388. f.write("BAR")
  389. self.assertRaises(
  390. ValueError,
  391. porcelain.add, self.repo,
  392. paths=[os.path.join(self.test_dir, "foo")])
  393. self.assertRaises(
  394. (ValueError, FileNotFoundError),
  395. porcelain.add, self.repo,
  396. paths=["../foo"])
  397. self.assertEqual([], list(self.repo.open_index()))
  398. def test_add_file_clrf_conversion(self):
  399. # Set the right configuration to the repo
  400. c = self.repo.get_config()
  401. c.set("core", "autocrlf", "input")
  402. c.write_to_path()
  403. # Add a file with CRLF line-ending
  404. fullpath = os.path.join(self.repo.path, 'foo')
  405. with open(fullpath, 'wb') as f:
  406. f.write(b"line1\r\nline2")
  407. porcelain.add(self.repo.path, paths=[fullpath])
  408. # The line-endings should have been converted to LF
  409. index = self.repo.open_index()
  410. self.assertIn(b"foo", index)
  411. entry = index[b"foo"]
  412. blob = self.repo[entry.sha]
  413. self.assertEqual(blob.data, b"line1\nline2")
  414. class RemoveTests(PorcelainTestCase):
  415. def test_remove_file(self):
  416. fullpath = os.path.join(self.repo.path, 'foo')
  417. with open(fullpath, 'w') as f:
  418. f.write("BAR")
  419. porcelain.add(self.repo.path, paths=[fullpath])
  420. porcelain.commit(repo=self.repo, message=b'test',
  421. author=b'test <email>',
  422. committer=b'test <email>')
  423. self.assertTrue(os.path.exists(os.path.join(self.repo.path, 'foo')))
  424. cwd = os.getcwd()
  425. try:
  426. os.chdir(self.repo.path)
  427. porcelain.remove(self.repo.path, paths=["foo"])
  428. finally:
  429. os.chdir(cwd)
  430. self.assertFalse(os.path.exists(os.path.join(self.repo.path, 'foo')))
  431. def test_remove_file_staged(self):
  432. fullpath = os.path.join(self.repo.path, 'foo')
  433. with open(fullpath, 'w') as f:
  434. f.write("BAR")
  435. cwd = os.getcwd()
  436. try:
  437. os.chdir(self.repo.path)
  438. porcelain.add(self.repo.path, paths=[fullpath])
  439. self.assertRaises(Exception, porcelain.rm, self.repo.path,
  440. paths=["foo"])
  441. finally:
  442. os.chdir(cwd)
  443. class LogTests(PorcelainTestCase):
  444. def test_simple(self):
  445. c1, c2, c3 = build_commit_graph(
  446. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  447. self.repo.refs[b"HEAD"] = c3.id
  448. outstream = StringIO()
  449. porcelain.log(self.repo.path, outstream=outstream)
  450. self.assertEqual(3, outstream.getvalue().count("-" * 50))
  451. def test_max_entries(self):
  452. c1, c2, c3 = build_commit_graph(
  453. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  454. self.repo.refs[b"HEAD"] = c3.id
  455. outstream = StringIO()
  456. porcelain.log(self.repo.path, outstream=outstream, max_entries=1)
  457. self.assertEqual(1, outstream.getvalue().count("-" * 50))
  458. class ShowTests(PorcelainTestCase):
  459. def test_nolist(self):
  460. c1, c2, c3 = build_commit_graph(
  461. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  462. self.repo.refs[b"HEAD"] = c3.id
  463. outstream = StringIO()
  464. porcelain.show(self.repo.path, objects=c3.id, outstream=outstream)
  465. self.assertTrue(outstream.getvalue().startswith("-" * 50))
  466. def test_simple(self):
  467. c1, c2, c3 = build_commit_graph(
  468. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  469. self.repo.refs[b"HEAD"] = c3.id
  470. outstream = StringIO()
  471. porcelain.show(self.repo.path, objects=[c3.id], outstream=outstream)
  472. self.assertTrue(outstream.getvalue().startswith("-" * 50))
  473. def test_blob(self):
  474. b = Blob.from_string(b"The Foo\n")
  475. self.repo.object_store.add_object(b)
  476. outstream = StringIO()
  477. porcelain.show(self.repo.path, objects=[b.id], outstream=outstream)
  478. self.assertEqual(outstream.getvalue(), "The Foo\n")
  479. def test_commit_no_parent(self):
  480. a = Blob.from_string(b"The Foo\n")
  481. ta = Tree()
  482. ta.add(b"somename", 0o100644, a.id)
  483. ca = make_commit(tree=ta.id)
  484. self.repo.object_store.add_objects([(a, None), (ta, None), (ca, None)])
  485. outstream = StringIO()
  486. porcelain.show(self.repo.path, objects=[ca.id], outstream=outstream)
  487. self.assertMultiLineEqual(outstream.getvalue(), """\
  488. --------------------------------------------------
  489. commit: 344da06c1bb85901270b3e8875c988a027ec087d
  490. Author: Test Author <test@nodomain.com>
  491. Committer: Test Committer <test@nodomain.com>
  492. Date: Fri Jan 01 2010 00:00:00 +0000
  493. Test message.
  494. diff --git a/somename b/somename
  495. new file mode 100644
  496. index 0000000..ea5c7bf
  497. --- /dev/null
  498. +++ b/somename
  499. @@ -0,0 +1 @@
  500. +The Foo
  501. """)
  502. def test_tag(self):
  503. a = Blob.from_string(b"The Foo\n")
  504. ta = Tree()
  505. ta.add(b"somename", 0o100644, a.id)
  506. ca = make_commit(tree=ta.id)
  507. self.repo.object_store.add_objects([(a, None), (ta, None), (ca, None)])
  508. porcelain.tag_create(
  509. self.repo.path, b"tryme", b'foo <foo@bar.com>', b'bar',
  510. annotated=True, objectish=ca.id, tag_time=1552854211,
  511. tag_timezone=0)
  512. outstream = StringIO()
  513. porcelain.show(self.repo, objects=[b'refs/tags/tryme'],
  514. outstream=outstream)
  515. self.maxDiff = None
  516. self.assertMultiLineEqual(outstream.getvalue(), """\
  517. Tagger: foo <foo@bar.com>
  518. Date: Sun Mar 17 2019 20:23:31 +0000
  519. bar
  520. --------------------------------------------------
  521. commit: 344da06c1bb85901270b3e8875c988a027ec087d
  522. Author: Test Author <test@nodomain.com>
  523. Committer: Test Committer <test@nodomain.com>
  524. Date: Fri Jan 01 2010 00:00:00 +0000
  525. Test message.
  526. diff --git a/somename b/somename
  527. new file mode 100644
  528. index 0000000..ea5c7bf
  529. --- /dev/null
  530. +++ b/somename
  531. @@ -0,0 +1 @@
  532. +The Foo
  533. """)
  534. def test_commit_with_change(self):
  535. a = Blob.from_string(b"The Foo\n")
  536. ta = Tree()
  537. ta.add(b"somename", 0o100644, a.id)
  538. ca = make_commit(tree=ta.id)
  539. b = Blob.from_string(b"The Bar\n")
  540. tb = Tree()
  541. tb.add(b"somename", 0o100644, b.id)
  542. cb = make_commit(tree=tb.id, parents=[ca.id])
  543. self.repo.object_store.add_objects(
  544. [(a, None), (b, None), (ta, None), (tb, None),
  545. (ca, None), (cb, None)])
  546. outstream = StringIO()
  547. porcelain.show(self.repo.path, objects=[cb.id], outstream=outstream)
  548. self.assertMultiLineEqual(outstream.getvalue(), """\
  549. --------------------------------------------------
  550. commit: 2c6b6c9cb72c130956657e1fdae58e5b103744fa
  551. Author: Test Author <test@nodomain.com>
  552. Committer: Test Committer <test@nodomain.com>
  553. Date: Fri Jan 01 2010 00:00:00 +0000
  554. Test message.
  555. diff --git a/somename b/somename
  556. index ea5c7bf..fd38bcb 100644
  557. --- a/somename
  558. +++ b/somename
  559. @@ -1 +1 @@
  560. -The Foo
  561. +The Bar
  562. """)
  563. class SymbolicRefTests(PorcelainTestCase):
  564. def test_set_wrong_symbolic_ref(self):
  565. c1, c2, c3 = build_commit_graph(
  566. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  567. self.repo.refs[b"HEAD"] = c3.id
  568. self.assertRaises(ValueError, porcelain.symbolic_ref, self.repo.path,
  569. b'foobar')
  570. def test_set_force_wrong_symbolic_ref(self):
  571. c1, c2, c3 = build_commit_graph(
  572. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  573. self.repo.refs[b"HEAD"] = c3.id
  574. porcelain.symbolic_ref(self.repo.path, b'force_foobar', force=True)
  575. # test if we actually changed the file
  576. with self.repo.get_named_file('HEAD') as f:
  577. new_ref = f.read()
  578. self.assertEqual(new_ref, b'ref: refs/heads/force_foobar\n')
  579. def test_set_symbolic_ref(self):
  580. c1, c2, c3 = build_commit_graph(
  581. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  582. self.repo.refs[b"HEAD"] = c3.id
  583. porcelain.symbolic_ref(self.repo.path, b'master')
  584. def test_set_symbolic_ref_other_than_master(self):
  585. c1, c2, c3 = build_commit_graph(
  586. self.repo.object_store, [[1], [2, 1], [3, 1, 2]],
  587. attrs=dict(refs='develop'))
  588. self.repo.refs[b"HEAD"] = c3.id
  589. self.repo.refs[b"refs/heads/develop"] = c3.id
  590. porcelain.symbolic_ref(self.repo.path, b'develop')
  591. # test if we actually changed the file
  592. with self.repo.get_named_file('HEAD') as f:
  593. new_ref = f.read()
  594. self.assertEqual(new_ref, b'ref: refs/heads/develop\n')
  595. class DiffTreeTests(PorcelainTestCase):
  596. def test_empty(self):
  597. c1, c2, c3 = build_commit_graph(
  598. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  599. self.repo.refs[b"HEAD"] = c3.id
  600. outstream = BytesIO()
  601. porcelain.diff_tree(self.repo.path, c2.tree, c3.tree,
  602. outstream=outstream)
  603. self.assertEqual(outstream.getvalue(), b"")
  604. class CommitTreeTests(PorcelainTestCase):
  605. def test_simple(self):
  606. c1, c2, c3 = build_commit_graph(
  607. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  608. b = Blob()
  609. b.data = b"foo the bar"
  610. t = Tree()
  611. t.add(b"somename", 0o100644, b.id)
  612. self.repo.object_store.add_object(t)
  613. self.repo.object_store.add_object(b)
  614. sha = porcelain.commit_tree(
  615. self.repo.path, t.id, message=b"Withcommit.",
  616. author=b"Joe <joe@example.com>",
  617. committer=b"Jane <jane@example.com>")
  618. self.assertTrue(isinstance(sha, bytes))
  619. self.assertEqual(len(sha), 40)
  620. class RevListTests(PorcelainTestCase):
  621. def test_simple(self):
  622. c1, c2, c3 = build_commit_graph(
  623. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  624. outstream = BytesIO()
  625. porcelain.rev_list(
  626. self.repo.path, [c3.id], outstream=outstream)
  627. self.assertEqual(
  628. c3.id + b"\n" +
  629. c2.id + b"\n" +
  630. c1.id + b"\n",
  631. outstream.getvalue())
  632. class TagCreateTests(PorcelainTestCase):
  633. def test_annotated(self):
  634. c1, c2, c3 = build_commit_graph(
  635. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  636. self.repo.refs[b"HEAD"] = c3.id
  637. porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
  638. b'bar', annotated=True)
  639. tags = self.repo.refs.as_dict(b"refs/tags")
  640. self.assertEqual(list(tags.keys()), [b"tryme"])
  641. tag = self.repo[b'refs/tags/tryme']
  642. self.assertTrue(isinstance(tag, Tag))
  643. self.assertEqual(b"foo <foo@bar.com>", tag.tagger)
  644. self.assertEqual(b"bar", tag.message)
  645. self.assertLess(time.time() - tag.tag_time, 5)
  646. def test_unannotated(self):
  647. c1, c2, c3 = build_commit_graph(
  648. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  649. self.repo.refs[b"HEAD"] = c3.id
  650. porcelain.tag_create(self.repo.path, b"tryme", annotated=False)
  651. tags = self.repo.refs.as_dict(b"refs/tags")
  652. self.assertEqual(list(tags.keys()), [b"tryme"])
  653. self.repo[b'refs/tags/tryme']
  654. self.assertEqual(list(tags.values()), [self.repo.head()])
  655. def test_unannotated_unicode(self):
  656. c1, c2, c3 = build_commit_graph(
  657. self.repo.object_store, [[1], [2, 1], [3, 1, 2]])
  658. self.repo.refs[b"HEAD"] = c3.id
  659. porcelain.tag_create(self.repo.path, "tryme", annotated=False)
  660. tags = self.repo.refs.as_dict(b"refs/tags")
  661. self.assertEqual(list(tags.keys()), [b"tryme"])
  662. self.repo[b'refs/tags/tryme']
  663. self.assertEqual(list(tags.values()), [self.repo.head()])
  664. class TagListTests(PorcelainTestCase):
  665. def test_empty(self):
  666. tags = porcelain.tag_list(self.repo.path)
  667. self.assertEqual([], tags)
  668. def test_simple(self):
  669. self.repo.refs[b"refs/tags/foo"] = b"aa" * 20
  670. self.repo.refs[b"refs/tags/bar/bla"] = b"bb" * 20
  671. tags = porcelain.tag_list(self.repo.path)
  672. self.assertEqual([b"bar/bla", b"foo"], tags)
  673. class TagDeleteTests(PorcelainTestCase):
  674. def test_simple(self):
  675. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  676. self.repo[b"HEAD"] = c1.id
  677. porcelain.tag_create(self.repo, b'foo')
  678. self.assertTrue(b"foo" in porcelain.tag_list(self.repo))
  679. porcelain.tag_delete(self.repo, b'foo')
  680. self.assertFalse(b"foo" in porcelain.tag_list(self.repo))
  681. class ResetTests(PorcelainTestCase):
  682. def test_hard_head(self):
  683. fullpath = os.path.join(self.repo.path, 'foo')
  684. with open(fullpath, 'w') as f:
  685. f.write("BAR")
  686. porcelain.add(self.repo.path, paths=[fullpath])
  687. porcelain.commit(self.repo.path, message=b"Some message",
  688. committer=b"Jane <jane@example.com>",
  689. author=b"John <john@example.com>")
  690. with open(os.path.join(self.repo.path, 'foo'), 'wb') as f:
  691. f.write(b"OOH")
  692. porcelain.reset(self.repo, "hard", b"HEAD")
  693. index = self.repo.open_index()
  694. changes = list(tree_changes(self.repo,
  695. index.commit(self.repo.object_store),
  696. self.repo[b'HEAD'].tree))
  697. self.assertEqual([], changes)
  698. def test_hard_commit(self):
  699. fullpath = os.path.join(self.repo.path, 'foo')
  700. with open(fullpath, 'w') as f:
  701. f.write("BAR")
  702. porcelain.add(self.repo.path, paths=[fullpath])
  703. sha = porcelain.commit(self.repo.path, message=b"Some message",
  704. committer=b"Jane <jane@example.com>",
  705. author=b"John <john@example.com>")
  706. with open(fullpath, 'wb') as f:
  707. f.write(b"BAZ")
  708. porcelain.add(self.repo.path, paths=[fullpath])
  709. porcelain.commit(self.repo.path, message=b"Some other message",
  710. committer=b"Jane <jane@example.com>",
  711. author=b"John <john@example.com>")
  712. porcelain.reset(self.repo, "hard", sha)
  713. index = self.repo.open_index()
  714. changes = list(tree_changes(self.repo,
  715. index.commit(self.repo.object_store),
  716. self.repo[sha].tree))
  717. self.assertEqual([], changes)
  718. class PushTests(PorcelainTestCase):
  719. def test_simple(self):
  720. """
  721. Basic test of porcelain push where self.repo is the remote. First
  722. clone the remote, commit a file to the clone, then push the changes
  723. back to the remote.
  724. """
  725. outstream = BytesIO()
  726. errstream = BytesIO()
  727. porcelain.commit(repo=self.repo.path, message=b'init',
  728. author=b'author <email>',
  729. committer=b'committer <email>')
  730. # Setup target repo cloned from temp test repo
  731. clone_path = tempfile.mkdtemp()
  732. self.addCleanup(shutil.rmtree, clone_path)
  733. target_repo = porcelain.clone(self.repo.path, target=clone_path,
  734. errstream=errstream)
  735. try:
  736. self.assertEqual(target_repo[b'HEAD'], self.repo[b'HEAD'])
  737. finally:
  738. target_repo.close()
  739. # create a second file to be pushed back to origin
  740. handle, fullpath = tempfile.mkstemp(dir=clone_path)
  741. os.close(handle)
  742. porcelain.add(repo=clone_path, paths=[fullpath])
  743. porcelain.commit(repo=clone_path, message=b'push',
  744. author=b'author <email>',
  745. committer=b'committer <email>')
  746. # Setup a non-checked out branch in the remote
  747. refs_path = b"refs/heads/foo"
  748. new_id = self.repo[b'HEAD'].id
  749. self.assertNotEqual(new_id, ZERO_SHA)
  750. self.repo.refs[refs_path] = new_id
  751. # Push to the remote
  752. porcelain.push(clone_path, 'origin', b"HEAD:" + refs_path,
  753. outstream=outstream, errstream=errstream)
  754. self.assertEqual(
  755. target_repo.refs[b'refs/remotes/origin/foo'],
  756. target_repo.refs[b'HEAD'])
  757. # Check that the target and source
  758. with Repo(clone_path) as r_clone:
  759. self.assertEqual({
  760. b'HEAD': new_id,
  761. b'refs/heads/foo': r_clone[b'HEAD'].id,
  762. b'refs/heads/master': new_id,
  763. }, self.repo.get_refs())
  764. self.assertEqual(r_clone[b'HEAD'].id, self.repo[refs_path].id)
  765. # Get the change in the target repo corresponding to the add
  766. # this will be in the foo branch.
  767. change = list(tree_changes(self.repo, self.repo[b'HEAD'].tree,
  768. self.repo[b'refs/heads/foo'].tree))[0]
  769. self.assertEqual(os.path.basename(fullpath),
  770. change.new.path.decode('ascii'))
  771. def test_delete(self):
  772. """Basic test of porcelain push, removing a branch.
  773. """
  774. outstream = BytesIO()
  775. errstream = BytesIO()
  776. porcelain.commit(repo=self.repo.path, message=b'init',
  777. author=b'author <email>',
  778. committer=b'committer <email>')
  779. # Setup target repo cloned from temp test repo
  780. clone_path = tempfile.mkdtemp()
  781. self.addCleanup(shutil.rmtree, clone_path)
  782. target_repo = porcelain.clone(self.repo.path, target=clone_path,
  783. errstream=errstream)
  784. target_repo.close()
  785. # Setup a non-checked out branch in the remote
  786. refs_path = b"refs/heads/foo"
  787. new_id = self.repo[b'HEAD'].id
  788. self.assertNotEqual(new_id, ZERO_SHA)
  789. self.repo.refs[refs_path] = new_id
  790. # Push to the remote
  791. porcelain.push(clone_path, self.repo.path, b":" + refs_path,
  792. outstream=outstream, errstream=errstream)
  793. self.assertEqual({
  794. b'HEAD': new_id,
  795. b'refs/heads/master': new_id,
  796. }, self.repo.get_refs())
  797. class PullTests(PorcelainTestCase):
  798. def setUp(self):
  799. super(PullTests, self).setUp()
  800. # create a file for initial commit
  801. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  802. os.close(handle)
  803. porcelain.add(repo=self.repo.path, paths=fullpath)
  804. porcelain.commit(repo=self.repo.path, message=b'test',
  805. author=b'test <email>',
  806. committer=b'test <email>')
  807. # Setup target repo
  808. self.target_path = tempfile.mkdtemp()
  809. self.addCleanup(shutil.rmtree, self.target_path)
  810. target_repo = porcelain.clone(self.repo.path, target=self.target_path,
  811. errstream=BytesIO())
  812. target_repo.close()
  813. # create a second file to be pushed
  814. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  815. os.close(handle)
  816. porcelain.add(repo=self.repo.path, paths=fullpath)
  817. porcelain.commit(repo=self.repo.path, message=b'test2',
  818. author=b'test2 <email>',
  819. committer=b'test2 <email>')
  820. self.assertTrue(b'refs/heads/master' in self.repo.refs)
  821. self.assertTrue(b'refs/heads/master' in target_repo.refs)
  822. def test_simple(self):
  823. outstream = BytesIO()
  824. errstream = BytesIO()
  825. # Pull changes into the cloned repo
  826. porcelain.pull(self.target_path, self.repo.path, b'refs/heads/master',
  827. outstream=outstream, errstream=errstream)
  828. # Check the target repo for pushed changes
  829. with Repo(self.target_path) as r:
  830. self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id)
  831. def test_no_refspec(self):
  832. outstream = BytesIO()
  833. errstream = BytesIO()
  834. # Pull changes into the cloned repo
  835. porcelain.pull(self.target_path, self.repo.path, outstream=outstream,
  836. errstream=errstream)
  837. # Check the target repo for pushed changes
  838. with Repo(self.target_path) as r:
  839. self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id)
  840. def test_no_remote_location(self):
  841. outstream = BytesIO()
  842. errstream = BytesIO()
  843. # Pull changes into the cloned repo
  844. porcelain.pull(self.target_path, refspecs=b'refs/heads/master',
  845. outstream=outstream, errstream=errstream)
  846. # Check the target repo for pushed changes
  847. with Repo(self.target_path) as r:
  848. self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id)
  849. class StatusTests(PorcelainTestCase):
  850. def test_empty(self):
  851. results = porcelain.status(self.repo)
  852. self.assertEqual(
  853. {'add': [], 'delete': [], 'modify': []},
  854. results.staged)
  855. self.assertEqual([], results.unstaged)
  856. def test_status_base(self):
  857. """Integration test for `status` functionality."""
  858. # Commit a dummy file then modify it
  859. fullpath = os.path.join(self.repo.path, 'foo')
  860. with open(fullpath, 'w') as f:
  861. f.write('origstuff')
  862. porcelain.add(repo=self.repo.path, paths=[fullpath])
  863. porcelain.commit(repo=self.repo.path, message=b'test status',
  864. author=b'author <email>',
  865. committer=b'committer <email>')
  866. # modify access and modify time of path
  867. os.utime(fullpath, (0, 0))
  868. with open(fullpath, 'wb') as f:
  869. f.write(b'stuff')
  870. # Make a dummy file and stage it
  871. filename_add = 'bar'
  872. fullpath = os.path.join(self.repo.path, filename_add)
  873. with open(fullpath, 'w') as f:
  874. f.write('stuff')
  875. porcelain.add(repo=self.repo.path, paths=fullpath)
  876. results = porcelain.status(self.repo)
  877. self.assertEqual(results.staged['add'][0],
  878. filename_add.encode('ascii'))
  879. self.assertEqual(results.unstaged, [b'foo'])
  880. def test_status_all(self):
  881. del_path = os.path.join(self.repo.path, 'foo')
  882. mod_path = os.path.join(self.repo.path, 'bar')
  883. add_path = os.path.join(self.repo.path, 'baz')
  884. us_path = os.path.join(self.repo.path, 'blye')
  885. ut_path = os.path.join(self.repo.path, 'blyat')
  886. with open(del_path, 'w') as f:
  887. f.write('origstuff')
  888. with open(mod_path, 'w') as f:
  889. f.write('origstuff')
  890. with open(us_path, 'w') as f:
  891. f.write('origstuff')
  892. porcelain.add(repo=self.repo.path, paths=[del_path, mod_path, us_path])
  893. porcelain.commit(repo=self.repo.path, message=b'test status',
  894. author=b'author <email>',
  895. committer=b'committer <email>')
  896. porcelain.remove(self.repo.path, [del_path])
  897. with open(add_path, 'w') as f:
  898. f.write('origstuff')
  899. with open(mod_path, 'w') as f:
  900. f.write('more_origstuff')
  901. with open(us_path, 'w') as f:
  902. f.write('more_origstuff')
  903. porcelain.add(repo=self.repo.path, paths=[add_path, mod_path])
  904. with open(us_path, 'w') as f:
  905. f.write('\norigstuff')
  906. with open(ut_path, 'w') as f:
  907. f.write('origstuff')
  908. results = porcelain.status(self.repo.path)
  909. self.assertDictEqual(
  910. {'add': [b'baz'], 'delete': [b'foo'], 'modify': [b'bar']},
  911. results.staged)
  912. self.assertListEqual(results.unstaged, [b'blye'])
  913. self.assertListEqual(results.untracked, ['blyat'])
  914. def test_status_crlf_mismatch(self):
  915. # First make a commit as if the file has been added on a Linux system
  916. # or with core.autocrlf=True
  917. file_path = os.path.join(self.repo.path, 'crlf')
  918. with open(file_path, 'wb') as f:
  919. f.write(b'line1\nline2')
  920. porcelain.add(repo=self.repo.path, paths=[file_path])
  921. porcelain.commit(repo=self.repo.path, message=b'test status',
  922. author=b'author <email>',
  923. committer=b'committer <email>')
  924. # Then update the file as if it was created by CGit on a Windows
  925. # system with core.autocrlf=true
  926. with open(file_path, 'wb') as f:
  927. f.write(b'line1\r\nline2')
  928. results = porcelain.status(self.repo)
  929. self.assertDictEqual(
  930. {'add': [], 'delete': [], 'modify': []},
  931. results.staged)
  932. self.assertListEqual(results.unstaged, [b'crlf'])
  933. self.assertListEqual(results.untracked, [])
  934. def test_status_crlf_convert(self):
  935. # First make a commit as if the file has been added on a Linux system
  936. # or with core.autocrlf=True
  937. file_path = os.path.join(self.repo.path, 'crlf')
  938. with open(file_path, 'wb') as f:
  939. f.write(b'line1\nline2')
  940. porcelain.add(repo=self.repo.path, paths=[file_path])
  941. porcelain.commit(repo=self.repo.path, message=b'test status',
  942. author=b'author <email>',
  943. committer=b'committer <email>')
  944. # Then update the file as if it was created by CGit on a Windows
  945. # system with core.autocrlf=true
  946. with open(file_path, 'wb') as f:
  947. f.write(b'line1\r\nline2')
  948. # TODO: It should be set automatically by looking at the configuration
  949. c = self.repo.get_config()
  950. c.set("core", "autocrlf", True)
  951. c.write_to_path()
  952. results = porcelain.status(self.repo)
  953. self.assertDictEqual(
  954. {'add': [], 'delete': [], 'modify': []},
  955. results.staged)
  956. self.assertListEqual(results.unstaged, [])
  957. self.assertListEqual(results.untracked, [])
  958. def test_get_tree_changes_add(self):
  959. """Unit test for get_tree_changes add."""
  960. # Make a dummy file, stage
  961. filename = 'bar'
  962. fullpath = os.path.join(self.repo.path, filename)
  963. with open(fullpath, 'w') as f:
  964. f.write('stuff')
  965. porcelain.add(repo=self.repo.path, paths=fullpath)
  966. porcelain.commit(repo=self.repo.path, message=b'test status',
  967. author=b'author <email>',
  968. committer=b'committer <email>')
  969. filename = 'foo'
  970. fullpath = os.path.join(self.repo.path, filename)
  971. with open(fullpath, 'w') as f:
  972. f.write('stuff')
  973. porcelain.add(repo=self.repo.path, paths=fullpath)
  974. changes = porcelain.get_tree_changes(self.repo.path)
  975. self.assertEqual(changes['add'][0], filename.encode('ascii'))
  976. self.assertEqual(len(changes['add']), 1)
  977. self.assertEqual(len(changes['modify']), 0)
  978. self.assertEqual(len(changes['delete']), 0)
  979. def test_get_tree_changes_modify(self):
  980. """Unit test for get_tree_changes modify."""
  981. # Make a dummy file, stage, commit, modify
  982. filename = 'foo'
  983. fullpath = os.path.join(self.repo.path, filename)
  984. with open(fullpath, 'w') as f:
  985. f.write('stuff')
  986. porcelain.add(repo=self.repo.path, paths=fullpath)
  987. porcelain.commit(repo=self.repo.path, message=b'test status',
  988. author=b'author <email>',
  989. committer=b'committer <email>')
  990. with open(fullpath, 'w') as f:
  991. f.write('otherstuff')
  992. porcelain.add(repo=self.repo.path, paths=fullpath)
  993. changes = porcelain.get_tree_changes(self.repo.path)
  994. self.assertEqual(changes['modify'][0], filename.encode('ascii'))
  995. self.assertEqual(len(changes['add']), 0)
  996. self.assertEqual(len(changes['modify']), 1)
  997. self.assertEqual(len(changes['delete']), 0)
  998. def test_get_tree_changes_delete(self):
  999. """Unit test for get_tree_changes delete."""
  1000. # Make a dummy file, stage, commit, remove
  1001. filename = 'foo'
  1002. fullpath = os.path.join(self.repo.path, filename)
  1003. with open(fullpath, 'w') as f:
  1004. f.write('stuff')
  1005. porcelain.add(repo=self.repo.path, paths=fullpath)
  1006. porcelain.commit(repo=self.repo.path, message=b'test status',
  1007. author=b'author <email>',
  1008. committer=b'committer <email>')
  1009. cwd = os.getcwd()
  1010. try:
  1011. os.chdir(self.repo.path)
  1012. porcelain.remove(repo=self.repo.path, paths=[filename])
  1013. finally:
  1014. os.chdir(cwd)
  1015. changes = porcelain.get_tree_changes(self.repo.path)
  1016. self.assertEqual(changes['delete'][0], filename.encode('ascii'))
  1017. self.assertEqual(len(changes['add']), 0)
  1018. self.assertEqual(len(changes['modify']), 0)
  1019. self.assertEqual(len(changes['delete']), 1)
  1020. def test_get_untracked_paths(self):
  1021. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  1022. f.write('ignored\n')
  1023. with open(os.path.join(self.repo.path, 'ignored'), 'w') as f:
  1024. f.write('blah\n')
  1025. with open(os.path.join(self.repo.path, 'notignored'), 'w') as f:
  1026. f.write('blah\n')
  1027. self.assertEqual(
  1028. set(['ignored', 'notignored', '.gitignore']),
  1029. set(porcelain.get_untracked_paths(self.repo.path, self.repo.path,
  1030. self.repo.open_index())))
  1031. self.assertEqual(set(['.gitignore', 'notignored']),
  1032. set(porcelain.status(self.repo).untracked))
  1033. self.assertEqual(set(['.gitignore', 'notignored', 'ignored']),
  1034. set(porcelain.status(self.repo, ignored=True)
  1035. .untracked))
  1036. def test_get_untracked_paths_nested(self):
  1037. with open(os.path.join(self.repo.path, 'notignored'), 'w') as f:
  1038. f.write('blah\n')
  1039. subrepo = Repo.init(os.path.join(self.repo.path, 'nested'), mkdir=True)
  1040. with open(os.path.join(subrepo.path, 'another'), 'w') as f:
  1041. f.write('foo\n')
  1042. self.assertEqual(
  1043. set(['notignored']),
  1044. set(porcelain.get_untracked_paths(self.repo.path, self.repo.path,
  1045. self.repo.open_index())))
  1046. self.assertEqual(
  1047. set(['another']),
  1048. set(porcelain.get_untracked_paths(subrepo.path, subrepo.path,
  1049. subrepo.open_index())))
  1050. # TODO(jelmer): Add test for dulwich.porcelain.daemon
  1051. class UploadPackTests(PorcelainTestCase):
  1052. """Tests for upload_pack."""
  1053. def test_upload_pack(self):
  1054. outf = BytesIO()
  1055. exitcode = porcelain.upload_pack(
  1056. self.repo.path, BytesIO(b"0000"), outf)
  1057. outlines = outf.getvalue().splitlines()
  1058. self.assertEqual([b"0000"], outlines)
  1059. self.assertEqual(0, exitcode)
  1060. class ReceivePackTests(PorcelainTestCase):
  1061. """Tests for receive_pack."""
  1062. def test_receive_pack(self):
  1063. filename = 'foo'
  1064. fullpath = os.path.join(self.repo.path, filename)
  1065. with open(fullpath, 'w') as f:
  1066. f.write('stuff')
  1067. porcelain.add(repo=self.repo.path, paths=fullpath)
  1068. self.repo.do_commit(message=b'test status',
  1069. author=b'author <email>',
  1070. committer=b'committer <email>',
  1071. author_timestamp=1402354300,
  1072. commit_timestamp=1402354300, author_timezone=0,
  1073. commit_timezone=0)
  1074. outf = BytesIO()
  1075. exitcode = porcelain.receive_pack(
  1076. self.repo.path, BytesIO(b"0000"), outf)
  1077. outlines = outf.getvalue().splitlines()
  1078. self.assertEqual([
  1079. b'0091319b56ce3aee2d489f759736a79cc552c9bb86d9 HEAD\x00 report-status ' # noqa: E501
  1080. b'delete-refs quiet ofs-delta side-band-64k '
  1081. b'no-done symref=HEAD:refs/heads/master',
  1082. b'003f319b56ce3aee2d489f759736a79cc552c9bb86d9 refs/heads/master',
  1083. b'0000'], outlines)
  1084. self.assertEqual(0, exitcode)
  1085. class BranchListTests(PorcelainTestCase):
  1086. def test_standard(self):
  1087. self.assertEqual(set([]), set(porcelain.branch_list(self.repo)))
  1088. def test_new_branch(self):
  1089. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1090. self.repo[b"HEAD"] = c1.id
  1091. porcelain.branch_create(self.repo, b"foo")
  1092. self.assertEqual(
  1093. set([b"master", b"foo"]),
  1094. set(porcelain.branch_list(self.repo)))
  1095. class BranchCreateTests(PorcelainTestCase):
  1096. def test_branch_exists(self):
  1097. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1098. self.repo[b"HEAD"] = c1.id
  1099. porcelain.branch_create(self.repo, b"foo")
  1100. self.assertRaises(KeyError, porcelain.branch_create, self.repo, b"foo")
  1101. porcelain.branch_create(self.repo, b"foo", force=True)
  1102. def test_new_branch(self):
  1103. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1104. self.repo[b"HEAD"] = c1.id
  1105. porcelain.branch_create(self.repo, b"foo")
  1106. self.assertEqual(
  1107. set([b"master", b"foo"]),
  1108. set(porcelain.branch_list(self.repo)))
  1109. class BranchDeleteTests(PorcelainTestCase):
  1110. def test_simple(self):
  1111. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1112. self.repo[b"HEAD"] = c1.id
  1113. porcelain.branch_create(self.repo, b'foo')
  1114. self.assertTrue(b"foo" in porcelain.branch_list(self.repo))
  1115. porcelain.branch_delete(self.repo, b'foo')
  1116. self.assertFalse(b"foo" in porcelain.branch_list(self.repo))
  1117. def test_simple_unicode(self):
  1118. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1119. self.repo[b"HEAD"] = c1.id
  1120. porcelain.branch_create(self.repo, 'foo')
  1121. self.assertTrue(b"foo" in porcelain.branch_list(self.repo))
  1122. porcelain.branch_delete(self.repo, 'foo')
  1123. self.assertFalse(b"foo" in porcelain.branch_list(self.repo))
  1124. class FetchTests(PorcelainTestCase):
  1125. def test_simple(self):
  1126. outstream = BytesIO()
  1127. errstream = BytesIO()
  1128. # create a file for initial commit
  1129. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  1130. os.close(handle)
  1131. porcelain.add(repo=self.repo.path, paths=fullpath)
  1132. porcelain.commit(repo=self.repo.path, message=b'test',
  1133. author=b'test <email>',
  1134. committer=b'test <email>')
  1135. # Setup target repo
  1136. target_path = tempfile.mkdtemp()
  1137. self.addCleanup(shutil.rmtree, target_path)
  1138. target_repo = porcelain.clone(self.repo.path, target=target_path,
  1139. errstream=errstream)
  1140. # create a second file to be pushed
  1141. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  1142. os.close(handle)
  1143. porcelain.add(repo=self.repo.path, paths=fullpath)
  1144. porcelain.commit(repo=self.repo.path, message=b'test2',
  1145. author=b'test2 <email>',
  1146. committer=b'test2 <email>')
  1147. self.assertFalse(self.repo[b'HEAD'].id in target_repo)
  1148. target_repo.close()
  1149. # Fetch changes into the cloned repo
  1150. porcelain.fetch(target_path, 'origin',
  1151. outstream=outstream, errstream=errstream)
  1152. # Assert that fetch updated the local image of the remote
  1153. self.assert_correct_remote_refs(
  1154. target_repo.get_refs(), self.repo.get_refs())
  1155. # Check the target repo for pushed changes
  1156. with Repo(target_path) as r:
  1157. self.assertTrue(self.repo[b'HEAD'].id in r)
  1158. def test_with_remote_name(self):
  1159. remote_name = 'origin'
  1160. outstream = BytesIO()
  1161. errstream = BytesIO()
  1162. # create a file for initial commit
  1163. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  1164. os.close(handle)
  1165. porcelain.add(repo=self.repo.path, paths=fullpath)
  1166. porcelain.commit(repo=self.repo.path, message=b'test',
  1167. author=b'test <email>',
  1168. committer=b'test <email>')
  1169. # Setup target repo
  1170. target_path = tempfile.mkdtemp()
  1171. self.addCleanup(shutil.rmtree, target_path)
  1172. target_repo = porcelain.clone(self.repo.path, target=target_path,
  1173. errstream=errstream)
  1174. # Capture current refs
  1175. target_refs = target_repo.get_refs()
  1176. # create a second file to be pushed
  1177. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  1178. os.close(handle)
  1179. porcelain.add(repo=self.repo.path, paths=fullpath)
  1180. porcelain.commit(repo=self.repo.path, message=b'test2',
  1181. author=b'test2 <email>',
  1182. committer=b'test2 <email>')
  1183. self.assertFalse(self.repo[b'HEAD'].id in target_repo)
  1184. target_config = target_repo.get_config()
  1185. target_config.set(
  1186. (b'remote', remote_name.encode()), b'url', self.repo.path.encode())
  1187. target_repo.close()
  1188. # Fetch changes into the cloned repo
  1189. porcelain.fetch(target_path, remote_name,
  1190. outstream=outstream, errstream=errstream)
  1191. # Assert that fetch updated the local image of the remote
  1192. self.assert_correct_remote_refs(
  1193. target_repo.get_refs(), self.repo.get_refs())
  1194. # Check the target repo for pushed changes, as well as updates
  1195. # for the refs
  1196. with Repo(target_path) as r:
  1197. self.assertTrue(self.repo[b'HEAD'].id in r)
  1198. self.assertNotEqual(self.repo.get_refs(), target_refs)
  1199. def assert_correct_remote_refs(
  1200. self, local_refs, remote_refs, remote_name=b'origin'):
  1201. """Assert that known remote refs corresponds to actual remote refs."""
  1202. local_ref_prefix = b'refs/heads'
  1203. remote_ref_prefix = b'refs/remotes/' + remote_name
  1204. locally_known_remote_refs = {
  1205. k[len(remote_ref_prefix) + 1:]: v for k, v in local_refs.items()
  1206. if k.startswith(remote_ref_prefix)}
  1207. normalized_remote_refs = {
  1208. k[len(local_ref_prefix) + 1:]: v for k, v in remote_refs.items()
  1209. if k.startswith(local_ref_prefix)}
  1210. self.assertEqual(locally_known_remote_refs, normalized_remote_refs)
  1211. class RepackTests(PorcelainTestCase):
  1212. def test_empty(self):
  1213. porcelain.repack(self.repo)
  1214. def test_simple(self):
  1215. handle, fullpath = tempfile.mkstemp(dir=self.repo.path)
  1216. os.close(handle)
  1217. porcelain.add(repo=self.repo.path, paths=fullpath)
  1218. porcelain.repack(self.repo)
  1219. class LsTreeTests(PorcelainTestCase):
  1220. def test_empty(self):
  1221. porcelain.commit(repo=self.repo.path, message=b'test status',
  1222. author=b'author <email>',
  1223. committer=b'committer <email>')
  1224. f = StringIO()
  1225. porcelain.ls_tree(self.repo, b"HEAD", outstream=f)
  1226. self.assertEqual(f.getvalue(), "")
  1227. def test_simple(self):
  1228. # Commit a dummy file then modify it
  1229. fullpath = os.path.join(self.repo.path, 'foo')
  1230. with open(fullpath, 'w') as f:
  1231. f.write('origstuff')
  1232. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1233. porcelain.commit(repo=self.repo.path, message=b'test status',
  1234. author=b'author <email>',
  1235. committer=b'committer <email>')
  1236. f = StringIO()
  1237. porcelain.ls_tree(self.repo, b"HEAD", outstream=f)
  1238. self.assertEqual(
  1239. f.getvalue(),
  1240. '100644 blob 8b82634d7eae019850bb883f06abf428c58bc9aa\tfoo\n')
  1241. def test_recursive(self):
  1242. # Create a directory then write a dummy file in it
  1243. dirpath = os.path.join(self.repo.path, 'adir')
  1244. filepath = os.path.join(dirpath, 'afile')
  1245. os.mkdir(dirpath)
  1246. with open(filepath, 'w') as f:
  1247. f.write('origstuff')
  1248. porcelain.add(repo=self.repo.path, paths=[filepath])
  1249. porcelain.commit(repo=self.repo.path, message=b'test status',
  1250. author=b'author <email>',
  1251. committer=b'committer <email>')
  1252. f = StringIO()
  1253. porcelain.ls_tree(self.repo, b"HEAD", outstream=f)
  1254. self.assertEqual(
  1255. f.getvalue(),
  1256. '40000 tree b145cc69a5e17693e24d8a7be0016ed8075de66d\tadir\n')
  1257. f = StringIO()
  1258. porcelain.ls_tree(self.repo, b"HEAD", outstream=f, recursive=True)
  1259. self.assertEqual(
  1260. f.getvalue(),
  1261. '40000 tree b145cc69a5e17693e24d8a7be0016ed8075de66d\tadir\n'
  1262. '100644 blob 8b82634d7eae019850bb883f06abf428c58bc9aa\tadir'
  1263. '/afile\n')
  1264. class LsRemoteTests(PorcelainTestCase):
  1265. def test_empty(self):
  1266. self.assertEqual({}, porcelain.ls_remote(self.repo.path))
  1267. def test_some(self):
  1268. cid = porcelain.commit(repo=self.repo.path, message=b'test status',
  1269. author=b'author <email>',
  1270. committer=b'committer <email>')
  1271. self.assertEqual({
  1272. b'refs/heads/master': cid,
  1273. b'HEAD': cid},
  1274. porcelain.ls_remote(self.repo.path))
  1275. class LsFilesTests(PorcelainTestCase):
  1276. def test_empty(self):
  1277. self.assertEqual([], list(porcelain.ls_files(self.repo)))
  1278. def test_simple(self):
  1279. # Commit a dummy file then modify it
  1280. fullpath = os.path.join(self.repo.path, 'foo')
  1281. with open(fullpath, 'w') as f:
  1282. f.write('origstuff')
  1283. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1284. self.assertEqual([b'foo'], list(porcelain.ls_files(self.repo)))
  1285. class RemoteAddTests(PorcelainTestCase):
  1286. def test_new(self):
  1287. porcelain.remote_add(
  1288. self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich')
  1289. c = self.repo.get_config()
  1290. self.assertEqual(
  1291. c.get((b'remote', b'jelmer'), b'url'),
  1292. b'git://jelmer.uk/code/dulwich')
  1293. def test_exists(self):
  1294. porcelain.remote_add(
  1295. self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich')
  1296. self.assertRaises(porcelain.RemoteExists, porcelain.remote_add,
  1297. self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich')
  1298. class CheckIgnoreTests(PorcelainTestCase):
  1299. def test_check_ignored(self):
  1300. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  1301. f.write('foo')
  1302. foo_path = os.path.join(self.repo.path, 'foo')
  1303. with open(foo_path, 'w') as f:
  1304. f.write('BAR')
  1305. bar_path = os.path.join(self.repo.path, 'bar')
  1306. with open(bar_path, 'w') as f:
  1307. f.write('BAR')
  1308. self.assertEqual(
  1309. ['foo'],
  1310. list(porcelain.check_ignore(self.repo, [foo_path])))
  1311. self.assertEqual(
  1312. [], list(porcelain.check_ignore(self.repo, [bar_path])))
  1313. def test_check_added_abs(self):
  1314. path = os.path.join(self.repo.path, 'foo')
  1315. with open(path, 'w') as f:
  1316. f.write('BAR')
  1317. self.repo.stage(['foo'])
  1318. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  1319. f.write('foo\n')
  1320. self.assertEqual(
  1321. [], list(porcelain.check_ignore(self.repo, [path])))
  1322. self.assertEqual(
  1323. ['foo'],
  1324. list(porcelain.check_ignore(self.repo, [path], no_index=True)))
  1325. def test_check_added_rel(self):
  1326. with open(os.path.join(self.repo.path, 'foo'), 'w') as f:
  1327. f.write('BAR')
  1328. self.repo.stage(['foo'])
  1329. with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f:
  1330. f.write('foo\n')
  1331. cwd = os.getcwd()
  1332. os.mkdir(os.path.join(self.repo.path, 'bar'))
  1333. os.chdir(os.path.join(self.repo.path, 'bar'))
  1334. try:
  1335. self.assertEqual(
  1336. list(porcelain.check_ignore(self.repo, ['../foo'])), [])
  1337. self.assertEqual(['../foo'], list(
  1338. porcelain.check_ignore(self.repo, ['../foo'], no_index=True)))
  1339. finally:
  1340. os.chdir(cwd)
  1341. class UpdateHeadTests(PorcelainTestCase):
  1342. def test_set_to_branch(self):
  1343. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1344. self.repo.refs[b"refs/heads/blah"] = c1.id
  1345. porcelain.update_head(self.repo, "blah")
  1346. self.assertEqual(c1.id, self.repo.head())
  1347. self.assertEqual(b'ref: refs/heads/blah',
  1348. self.repo.refs.read_ref(b'HEAD'))
  1349. def test_set_to_branch_detached(self):
  1350. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1351. self.repo.refs[b"refs/heads/blah"] = c1.id
  1352. porcelain.update_head(self.repo, "blah", detached=True)
  1353. self.assertEqual(c1.id, self.repo.head())
  1354. self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD'))
  1355. def test_set_to_commit_detached(self):
  1356. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1357. self.repo.refs[b"refs/heads/blah"] = c1.id
  1358. porcelain.update_head(self.repo, c1.id, detached=True)
  1359. self.assertEqual(c1.id, self.repo.head())
  1360. self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD'))
  1361. def test_set_new_branch(self):
  1362. [c1] = build_commit_graph(self.repo.object_store, [[1]])
  1363. self.repo.refs[b"refs/heads/blah"] = c1.id
  1364. porcelain.update_head(self.repo, "blah", new_branch="bar")
  1365. self.assertEqual(c1.id, self.repo.head())
  1366. self.assertEqual(b'ref: refs/heads/bar',
  1367. self.repo.refs.read_ref(b'HEAD'))
  1368. class MailmapTests(PorcelainTestCase):
  1369. def test_no_mailmap(self):
  1370. self.assertEqual(
  1371. b'Jelmer Vernooij <jelmer@samba.org>',
  1372. porcelain.check_mailmap(
  1373. self.repo, b'Jelmer Vernooij <jelmer@samba.org>'))
  1374. def test_mailmap_lookup(self):
  1375. with open(os.path.join(self.repo.path, '.mailmap'), 'wb') as f:
  1376. f.write(b"""\
  1377. Jelmer Vernooij <jelmer@debian.org>
  1378. """)
  1379. self.assertEqual(
  1380. b'Jelmer Vernooij <jelmer@debian.org>',
  1381. porcelain.check_mailmap(
  1382. self.repo, b'Jelmer Vernooij <jelmer@samba.org>'))
  1383. class FsckTests(PorcelainTestCase):
  1384. def test_none(self):
  1385. self.assertEqual(
  1386. [],
  1387. list(porcelain.fsck(self.repo)))
  1388. def test_git_dir(self):
  1389. obj = Tree()
  1390. a = Blob()
  1391. a.data = b"foo"
  1392. obj.add(b".git", 0o100644, a.id)
  1393. self.repo.object_store.add_objects(
  1394. [(a, None), (obj, None)])
  1395. self.assertEqual(
  1396. [(obj.id, 'invalid name .git')],
  1397. [(sha, str(e)) for (sha, e) in porcelain.fsck(self.repo)])
  1398. class DescribeTests(PorcelainTestCase):
  1399. def test_no_commits(self):
  1400. self.assertRaises(KeyError, porcelain.describe, self.repo.path)
  1401. def test_single_commit(self):
  1402. fullpath = os.path.join(self.repo.path, 'foo')
  1403. with open(fullpath, 'w') as f:
  1404. f.write("BAR")
  1405. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1406. sha = porcelain.commit(
  1407. self.repo.path, message=b"Some message",
  1408. author=b"Joe <joe@example.com>",
  1409. committer=b"Bob <bob@example.com>")
  1410. self.assertEqual(
  1411. 'g{}'.format(sha[:7].decode('ascii')),
  1412. porcelain.describe(self.repo.path))
  1413. def test_tag(self):
  1414. fullpath = os.path.join(self.repo.path, 'foo')
  1415. with open(fullpath, 'w') as f:
  1416. f.write("BAR")
  1417. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1418. porcelain.commit(
  1419. self.repo.path, message=b"Some message",
  1420. author=b"Joe <joe@example.com>",
  1421. committer=b"Bob <bob@example.com>")
  1422. porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
  1423. b'bar', annotated=True)
  1424. self.assertEqual(
  1425. "tryme",
  1426. porcelain.describe(self.repo.path))
  1427. def test_tag_and_commit(self):
  1428. fullpath = os.path.join(self.repo.path, 'foo')
  1429. with open(fullpath, 'w') as f:
  1430. f.write("BAR")
  1431. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1432. porcelain.commit(
  1433. self.repo.path, message=b"Some message",
  1434. author=b"Joe <joe@example.com>",
  1435. committer=b"Bob <bob@example.com>")
  1436. porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
  1437. b'bar', annotated=True)
  1438. with open(fullpath, 'w') as f:
  1439. f.write("BAR2")
  1440. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1441. sha = porcelain.commit(
  1442. self.repo.path, message=b"Some message",
  1443. author=b"Joe <joe@example.com>",
  1444. committer=b"Bob <bob@example.com>")
  1445. self.assertEqual(
  1446. 'tryme-1-g{}'.format(sha[:7].decode('ascii')),
  1447. porcelain.describe(self.repo.path))
  1448. class PathToTreeTests(PorcelainTestCase):
  1449. def setUp(self):
  1450. super(PathToTreeTests, self).setUp()
  1451. self.fp = os.path.join(self.test_dir, 'bar')
  1452. with open(self.fp, 'w') as f:
  1453. f.write('something')
  1454. oldcwd = os.getcwd()
  1455. self.addCleanup(os.chdir, oldcwd)
  1456. os.chdir(self.test_dir)
  1457. def test_path_to_tree_path_base(self):
  1458. self.assertEqual(
  1459. b'bar', porcelain.path_to_tree_path(self.test_dir, self.fp))
  1460. self.assertEqual(b'bar', porcelain.path_to_tree_path('.', './bar'))
  1461. self.assertEqual(b'bar', porcelain.path_to_tree_path('.', 'bar'))
  1462. cwd = os.getcwd()
  1463. self.assertEqual(
  1464. b'bar', porcelain.path_to_tree_path('.', os.path.join(cwd, 'bar')))
  1465. self.assertEqual(b'bar', porcelain.path_to_tree_path(cwd, 'bar'))
  1466. def test_path_to_tree_path_syntax(self):
  1467. self.assertEqual(b'bar', porcelain.path_to_tree_path('.', './bar'))
  1468. def test_path_to_tree_path_error(self):
  1469. with self.assertRaises(ValueError):
  1470. with tempfile.TemporaryDirectory() as od:
  1471. porcelain.path_to_tree_path(od, self.fp)
  1472. def test_path_to_tree_path_rel(self):
  1473. cwd = os.getcwd()
  1474. os.mkdir(os.path.join(self.repo.path, 'foo'))
  1475. os.mkdir(os.path.join(self.repo.path, 'foo/bar'))
  1476. try:
  1477. os.chdir(os.path.join(self.repo.path, 'foo/bar'))
  1478. with open('baz', 'w') as f:
  1479. f.write('contents')
  1480. self.assertEqual(b'bar/baz', porcelain.path_to_tree_path(
  1481. '..', 'baz'))
  1482. self.assertEqual(b'bar/baz', porcelain.path_to_tree_path(
  1483. os.path.join(os.getcwd(), '..'),
  1484. os.path.join(os.getcwd(), 'baz')))
  1485. self.assertEqual(b'bar/baz', porcelain.path_to_tree_path(
  1486. '..', os.path.join(os.getcwd(), 'baz')))
  1487. self.assertEqual(b'bar/baz', porcelain.path_to_tree_path(
  1488. os.path.join(os.getcwd(), '..'), 'baz'))
  1489. finally:
  1490. os.chdir(cwd)
  1491. class GetObjectByPathTests(PorcelainTestCase):
  1492. def test_simple(self):
  1493. fullpath = os.path.join(self.repo.path, 'foo')
  1494. with open(fullpath, 'w') as f:
  1495. f.write("BAR")
  1496. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1497. porcelain.commit(
  1498. self.repo.path, message=b"Some message",
  1499. author=b"Joe <joe@example.com>",
  1500. committer=b"Bob <bob@example.com>")
  1501. self.assertEqual(
  1502. b"BAR",
  1503. porcelain.get_object_by_path(self.repo, 'foo').data)
  1504. self.assertEqual(
  1505. b"BAR",
  1506. porcelain.get_object_by_path(self.repo, b'foo').data)
  1507. def test_encoding(self):
  1508. fullpath = os.path.join(self.repo.path, 'foo')
  1509. with open(fullpath, 'w') as f:
  1510. f.write("BAR")
  1511. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1512. porcelain.commit(
  1513. self.repo.path, message=b"Some message",
  1514. author=b"Joe <joe@example.com>",
  1515. committer=b"Bob <bob@example.com>",
  1516. encoding=b"utf-8")
  1517. self.assertEqual(
  1518. b"BAR",
  1519. porcelain.get_object_by_path(self.repo, 'foo').data)
  1520. self.assertEqual(
  1521. b"BAR",
  1522. porcelain.get_object_by_path(self.repo, b'foo').data)
  1523. def test_missing(self):
  1524. self.assertRaises(
  1525. KeyError,
  1526. porcelain.get_object_by_path, self.repo, 'foo')
  1527. class WriteTreeTests(PorcelainTestCase):
  1528. def test_simple(self):
  1529. fullpath = os.path.join(self.repo.path, 'foo')
  1530. with open(fullpath, 'w') as f:
  1531. f.write("BAR")
  1532. porcelain.add(repo=self.repo.path, paths=[fullpath])
  1533. self.assertEqual(
  1534. b'd2092c8a9f311f0311083bf8d177f2ca0ab5b241',
  1535. porcelain.write_tree(self.repo))
  1536. class ActiveBranchTests(PorcelainTestCase):
  1537. def test_simple(self):
  1538. self.assertEqual(b'master', porcelain.active_branch(self.repo))