test_porcelain.py 66 KB

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