test_porcelain.py 68 KB

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