git_common_test.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. #!/usr/bin/env python
  2. # Copyright 2013 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """Unit tests for git_common.py"""
  6. import binascii
  7. import collections
  8. import os
  9. import signal
  10. import sys
  11. import tempfile
  12. import time
  13. import unittest
  14. DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  15. sys.path.insert(0, DEPOT_TOOLS_ROOT)
  16. from testing_support import coverage_utils
  17. from testing_support import git_test_utils
  18. class GitCommonTestBase(unittest.TestCase):
  19. @classmethod
  20. def setUpClass(cls):
  21. super(GitCommonTestBase, cls).setUpClass()
  22. import git_common
  23. cls.gc = git_common
  24. cls.gc.TEST_MODE = True
  25. class Support(GitCommonTestBase):
  26. def _testMemoizeOneBody(self, threadsafe):
  27. calls = collections.defaultdict(int)
  28. def double_if_even(val):
  29. calls[val] += 1
  30. return val * 2 if val % 2 == 0 else None
  31. # Use this explicitly as a wrapper fn instead of a decorator. Otherwise
  32. # pylint crashes (!!)
  33. double_if_even = self.gc.memoize_one(threadsafe=threadsafe)(double_if_even)
  34. self.assertEqual(4, double_if_even(2))
  35. self.assertEqual(4, double_if_even(2))
  36. self.assertEqual(None, double_if_even(1))
  37. self.assertEqual(None, double_if_even(1))
  38. self.assertDictEqual({1: 2, 2: 1}, calls)
  39. double_if_even.set(10, 20)
  40. self.assertEqual(20, double_if_even(10))
  41. self.assertDictEqual({1: 2, 2: 1}, calls)
  42. double_if_even.clear()
  43. self.assertEqual(4, double_if_even(2))
  44. self.assertEqual(4, double_if_even(2))
  45. self.assertEqual(None, double_if_even(1))
  46. self.assertEqual(None, double_if_even(1))
  47. self.assertEqual(20, double_if_even(10))
  48. self.assertDictEqual({1: 4, 2: 2, 10: 1}, calls)
  49. def testMemoizeOne(self):
  50. self._testMemoizeOneBody(threadsafe=False)
  51. def testMemoizeOneThreadsafe(self):
  52. self._testMemoizeOneBody(threadsafe=True)
  53. def testOnce(self):
  54. testlist = []
  55. # This works around a bug in pylint
  56. once = self.gc.once
  57. @once
  58. def add_to_list():
  59. testlist.append('dog')
  60. add_to_list()
  61. add_to_list()
  62. add_to_list()
  63. add_to_list()
  64. self.assertEquals(testlist, ['dog'])
  65. def slow_square(i):
  66. """Helper for ScopedPoolTest.
  67. Must be global because non top-level functions aren't pickleable.
  68. """
  69. return i ** 2
  70. class ScopedPoolTest(GitCommonTestBase):
  71. CTRL_C = signal.CTRL_C_EVENT if sys.platform == 'win32' else signal.SIGINT
  72. def testThreads(self):
  73. result = []
  74. with self.gc.ScopedPool(kind='threads') as pool:
  75. result = list(pool.imap(slow_square, xrange(10)))
  76. self.assertEqual([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], result)
  77. def testThreadsCtrlC(self):
  78. result = []
  79. with self.assertRaises(KeyboardInterrupt):
  80. with self.gc.ScopedPool(kind='threads') as pool:
  81. # Make sure this pool is interrupted in mid-swing
  82. for i in pool.imap(slow_square, xrange(20)):
  83. if i > 32:
  84. os.kill(os.getpid(), self.CTRL_C)
  85. result.append(i)
  86. self.assertEqual([0, 1, 4, 9, 16, 25], result)
  87. def testProcs(self):
  88. result = []
  89. with self.gc.ScopedPool() as pool:
  90. result = list(pool.imap(slow_square, xrange(10)))
  91. self.assertEqual([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], result)
  92. def testProcsCtrlC(self):
  93. result = []
  94. with self.assertRaises(KeyboardInterrupt):
  95. with self.gc.ScopedPool() as pool:
  96. # Make sure this pool is interrupted in mid-swing
  97. for i in pool.imap(slow_square, xrange(20)):
  98. if i > 32:
  99. os.kill(os.getpid(), self.CTRL_C)
  100. result.append(i)
  101. self.assertEqual([0, 1, 4, 9, 16, 25], result)
  102. class ProgressPrinterTest(GitCommonTestBase):
  103. class FakeStream(object):
  104. def __init__(self):
  105. self.data = set()
  106. self.count = 0
  107. def write(self, line):
  108. self.data.add(line)
  109. def flush(self):
  110. self.count += 1
  111. @unittest.expectedFailure
  112. def testBasic(self):
  113. """This test is probably racy, but I don't have a better alternative."""
  114. fmt = '%(count)d/10'
  115. stream = self.FakeStream()
  116. pp = self.gc.ProgressPrinter(fmt, enabled=True, fout=stream, period=0.01)
  117. with pp as inc:
  118. for _ in xrange(10):
  119. time.sleep(0.02)
  120. inc()
  121. filtered = {x.strip() for x in stream.data}
  122. rslt = {fmt % {'count': i} for i in xrange(11)}
  123. self.assertSetEqual(filtered, rslt)
  124. self.assertGreaterEqual(stream.count, 10)
  125. class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase,
  126. GitCommonTestBase):
  127. REPO_SCHEMA = """
  128. A B C D
  129. B E D
  130. """
  131. COMMIT_A = {
  132. 'some/files/file1': {'data': 'file1'},
  133. 'some/files/file2': {'data': 'file2'},
  134. 'some/files/file3': {'data': 'file3'},
  135. 'some/other/file': {'data': 'otherfile'},
  136. }
  137. COMMIT_C = {
  138. 'some/files/file2': {
  139. 'mode': 0755,
  140. 'data': 'file2 - vanilla'},
  141. }
  142. COMMIT_E = {
  143. 'some/files/file2': {'data': 'file2 - merged'},
  144. }
  145. COMMIT_D = {
  146. 'some/files/file2': {'data': 'file2 - vanilla\nfile2 - merged'},
  147. }
  148. def testHashes(self):
  149. ret = self.repo.run(
  150. self.gc.hash_multi, *[
  151. 'master',
  152. 'master~3',
  153. self.repo['E']+'~',
  154. self.repo['D']+'^2',
  155. 'tag_C^{}',
  156. ]
  157. )
  158. self.assertEqual([
  159. self.repo['D'],
  160. self.repo['A'],
  161. self.repo['B'],
  162. self.repo['E'],
  163. self.repo['C'],
  164. ], ret)
  165. self.assertEquals(
  166. self.repo.run(self.gc.hash_one, 'branch_D'),
  167. self.repo['D']
  168. )
  169. def testStream(self):
  170. items = set(self.repo.commit_map.itervalues())
  171. def testfn():
  172. for line in self.gc.run_stream('log', '--format=%H').xreadlines():
  173. line = line.strip()
  174. self.assertIn(line, items)
  175. items.remove(line)
  176. self.repo.run(testfn)
  177. def testCurrentBranch(self):
  178. def cur_branch_out_of_git():
  179. os.chdir('..')
  180. return self.gc.current_branch()
  181. self.assertIsNone(self.repo.run(cur_branch_out_of_git))
  182. self.repo.git('checkout', 'branch_D')
  183. self.assertEqual(self.repo.run(self.gc.current_branch), 'branch_D')
  184. def testBranches(self):
  185. self.assertEqual(self.repo.run(set, self.gc.branches()),
  186. {'master', 'branch_D', 'root_A'})
  187. def testDormant(self):
  188. self.assertFalse(self.repo.run(self.gc.is_dormant, 'master'))
  189. self.repo.git('config', 'branch.master.dormant', 'true')
  190. self.assertTrue(self.repo.run(self.gc.is_dormant, 'master'))
  191. def testParseCommitrefs(self):
  192. ret = self.repo.run(
  193. self.gc.parse_commitrefs, *[
  194. 'master',
  195. 'master~3',
  196. self.repo['E']+'~',
  197. self.repo['D']+'^2',
  198. 'tag_C^{}',
  199. ]
  200. )
  201. self.assertEqual(ret, map(binascii.unhexlify, [
  202. self.repo['D'],
  203. self.repo['A'],
  204. self.repo['B'],
  205. self.repo['E'],
  206. self.repo['C'],
  207. ]))
  208. with self.assertRaisesRegexp(Exception, r"one of \('master', 'bananas'\)"):
  209. self.repo.run(self.gc.parse_commitrefs, 'master', 'bananas')
  210. def testTags(self):
  211. self.assertEqual(set(self.repo.run(self.gc.tags)),
  212. {'tag_'+l for l in 'ABCDE'})
  213. def testTree(self):
  214. tree = self.repo.run(self.gc.tree, 'master:some/files')
  215. file1 = self.COMMIT_A['some/files/file1']['data']
  216. file2 = self.COMMIT_D['some/files/file2']['data']
  217. file3 = self.COMMIT_A['some/files/file3']['data']
  218. self.assertEquals(
  219. tree['file1'],
  220. ('100644', 'blob', git_test_utils.git_hash_data(file1)))
  221. self.assertEquals(
  222. tree['file2'],
  223. ('100755', 'blob', git_test_utils.git_hash_data(file2)))
  224. self.assertEquals(
  225. tree['file3'],
  226. ('100644', 'blob', git_test_utils.git_hash_data(file3)))
  227. tree = self.repo.run(self.gc.tree, 'master:some')
  228. self.assertEquals(len(tree), 2)
  229. # Don't check the tree hash because we're lazy :)
  230. self.assertEquals(tree['files'][:2], ('040000', 'tree'))
  231. tree = self.repo.run(self.gc.tree, 'master:wat')
  232. self.assertEqual(tree, None)
  233. def testTreeRecursive(self):
  234. tree = self.repo.run(self.gc.tree, 'master:some', recurse=True)
  235. file1 = self.COMMIT_A['some/files/file1']['data']
  236. file2 = self.COMMIT_D['some/files/file2']['data']
  237. file3 = self.COMMIT_A['some/files/file3']['data']
  238. other = self.COMMIT_A['some/other/file']['data']
  239. self.assertEquals(
  240. tree['files/file1'],
  241. ('100644', 'blob', git_test_utils.git_hash_data(file1)))
  242. self.assertEquals(
  243. tree['files/file2'],
  244. ('100755', 'blob', git_test_utils.git_hash_data(file2)))
  245. self.assertEquals(
  246. tree['files/file3'],
  247. ('100644', 'blob', git_test_utils.git_hash_data(file3)))
  248. self.assertEquals(
  249. tree['other/file'],
  250. ('100644', 'blob', git_test_utils.git_hash_data(other)))
  251. class GitMutableFunctionsTest(git_test_utils.GitRepoReadWriteTestBase,
  252. GitCommonTestBase):
  253. REPO_SCHEMA = ''
  254. def _intern_data(self, data):
  255. with tempfile.TemporaryFile() as f:
  256. f.write(data)
  257. f.seek(0)
  258. return self.repo.run(self.gc.intern_f, f)
  259. def testInternF(self):
  260. data = 'CoolBobcatsBro'
  261. data_hash = self._intern_data(data)
  262. self.assertEquals(git_test_utils.git_hash_data(data), data_hash)
  263. self.assertEquals(data, self.repo.git('cat-file', 'blob', data_hash).stdout)
  264. def testMkTree(self):
  265. tree = {}
  266. for i in 1, 2, 3:
  267. name = 'file%d' % i
  268. tree[name] = ('100644', 'blob', self._intern_data(name))
  269. tree_hash = self.repo.run(self.gc.mktree, tree)
  270. self.assertEquals('37b61866d6e061c4ba478e7eb525be7b5752737d', tree_hash)
  271. def testConfig(self):
  272. self.repo.git('config', '--add', 'happy.derpies', 'food')
  273. self.assertEquals(self.repo.run(self.gc.config_list, 'happy.derpies'),
  274. ['food'])
  275. self.assertEquals(self.repo.run(self.gc.config_list, 'sad.derpies'), [])
  276. self.repo.git('config', '--add', 'happy.derpies', 'cat')
  277. self.assertEquals(self.repo.run(self.gc.config_list, 'happy.derpies'),
  278. ['food', 'cat'])
  279. self.assertEquals('cat', self.repo.run(self.gc.config, 'dude.bob', 'cat'))
  280. self.repo.run(self.gc.set_config, 'dude.bob', 'dog')
  281. self.assertEquals('dog', self.repo.run(self.gc.config, 'dude.bob', 'cat'))
  282. self.repo.run(self.gc.del_config, 'dude.bob')
  283. # This should work without raising an exception
  284. self.repo.run(self.gc.del_config, 'dude.bob')
  285. self.assertEquals('cat', self.repo.run(self.gc.config, 'dude.bob', 'cat'))
  286. self.assertEquals('origin/master', self.repo.run(self.gc.root))
  287. self.repo.git('config', 'depot-tools.upstream', 'catfood')
  288. self.assertEquals('catfood', self.repo.run(self.gc.root))
  289. def testUpstream(self):
  290. self.repo.git('commit', '--allow-empty', '-am', 'foooooo')
  291. self.assertEquals(self.repo.run(self.gc.upstream, 'bobly'), None)
  292. self.assertEquals(self.repo.run(self.gc.upstream, 'master'), None)
  293. self.repo.git('checkout', '-tb', 'happybranch', 'master')
  294. self.assertEquals(self.repo.run(self.gc.upstream, 'happybranch'),
  295. 'master')
  296. class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase,
  297. GitCommonTestBase):
  298. REPO_SCHEMA = """
  299. A B C D E F G
  300. B H I J K
  301. J L
  302. X Y Z
  303. CAT DOG
  304. """
  305. COMMIT_B = {'file': {'data': 'B'}}
  306. COMMIT_H = {'file': {'data': 'H'}}
  307. COMMIT_I = {'file': {'data': 'I'}}
  308. COMMIT_J = {'file': {'data': 'J'}}
  309. COMMIT_K = {'file': {'data': 'K'}}
  310. COMMIT_L = {'file': {'data': 'L'}}
  311. def setUp(self):
  312. super(GitMutableStructuredTest, self).setUp()
  313. self.repo.git('branch', '--set-upstream-to', 'root_X', 'branch_Z')
  314. self.repo.git('branch', '--set-upstream-to', 'branch_G', 'branch_K')
  315. self.repo.git('branch', '--set-upstream-to', 'branch_K', 'branch_L')
  316. self.repo.git('branch', '--set-upstream-to', 'root_A', 'branch_G')
  317. self.repo.git('branch', '--set-upstream-to', 'root_X', 'root_A')
  318. def testTooManyBranches(self):
  319. for i in xrange(30):
  320. self.repo.git('branch', 'a'*i)
  321. _, rslt = self.repo.capture_stdio(list, self.gc.branches())
  322. self.assertIn('too many branches (39/20)', rslt)
  323. self.repo.git('config', 'depot-tools.branch-limit', 'cat')
  324. _, rslt = self.repo.capture_stdio(list, self.gc.branches())
  325. self.assertIn('too many branches (39/20)', rslt)
  326. self.repo.git('config', 'depot-tools.branch-limit', '100')
  327. # should not raise
  328. self.assertEqual(38, len(self.repo.run(list, self.gc.branches())))
  329. def testMergeBase(self):
  330. self.repo.git('checkout', 'branch_K')
  331. self.assertEqual(
  332. self.repo['B'],
  333. self.repo.run(self.gc.get_or_create_merge_base, 'branch_K', 'branch_G')
  334. )
  335. self.assertEqual(
  336. self.repo['J'],
  337. self.repo.run(self.gc.get_or_create_merge_base, 'branch_L', 'branch_K')
  338. )
  339. self.assertEqual(
  340. self.repo['B'], self.repo.run(self.gc.config, 'branch.branch_K.base')
  341. )
  342. self.assertEqual(
  343. 'branch_G', self.repo.run(self.gc.config, 'branch.branch_K.base-upstream')
  344. )
  345. # deadbeef is a bad hash, so this will result in repo['B']
  346. self.repo.run(self.gc.manual_merge_base, 'branch_K', 'deadbeef', 'branch_G')
  347. self.assertEqual(
  348. self.repo['B'],
  349. self.repo.run(self.gc.get_or_create_merge_base, 'branch_K', 'branch_G')
  350. )
  351. # but if we pick a real ancestor, then it'll work
  352. self.repo.run(self.gc.manual_merge_base, 'branch_K', self.repo['I'],
  353. 'branch_G')
  354. self.assertEqual(
  355. self.repo['I'],
  356. self.repo.run(self.gc.get_or_create_merge_base, 'branch_K', 'branch_G')
  357. )
  358. self.assertEqual({'branch_K': self.repo['I'], 'branch_L': self.repo['J']},
  359. self.repo.run(self.gc.branch_config_map, 'base'))
  360. self.repo.run(self.gc.remove_merge_base, 'branch_K')
  361. self.repo.run(self.gc.remove_merge_base, 'branch_L')
  362. self.assertEqual(None,
  363. self.repo.run(self.gc.config, 'branch.branch_K.base'))
  364. self.assertEqual({}, self.repo.run(self.gc.branch_config_map, 'base'))
  365. # if it's too old, then it caps at merge-base
  366. self.repo.run(self.gc.manual_merge_base, 'branch_K', self.repo['A'],
  367. 'branch_G')
  368. self.assertEqual(
  369. self.repo['B'],
  370. self.repo.run(self.gc.get_or_create_merge_base, 'branch_K', 'branch_G')
  371. )
  372. # If the user does --set-upstream-to something else, then we discard the
  373. # base and recompute it.
  374. self.repo.run(self.gc.run, 'branch', '-u', 'root_A')
  375. self.assertEqual(
  376. self.repo['A'],
  377. self.repo.run(self.gc.get_or_create_merge_base, 'branch_K')
  378. )
  379. self.assertIsNone(
  380. self.repo.run(self.gc.get_or_create_merge_base, 'branch_DOG'))
  381. def testGetBranchTree(self):
  382. skipped, tree = self.repo.run(self.gc.get_branch_tree)
  383. self.assertEqual(skipped, {'master', 'root_X', 'branch_DOG', 'root_CAT'})
  384. self.assertEqual(tree, {
  385. 'branch_G': 'root_A',
  386. 'root_A': 'root_X',
  387. 'branch_K': 'branch_G',
  388. 'branch_L': 'branch_K',
  389. 'branch_Z': 'root_X'
  390. })
  391. topdown = list(self.gc.topo_iter(tree))
  392. bottomup = list(self.gc.topo_iter(tree, top_down=False))
  393. self.assertEqual(topdown, [
  394. ('branch_Z', 'root_X'),
  395. ('root_A', 'root_X'),
  396. ('branch_G', 'root_A'),
  397. ('branch_K', 'branch_G'),
  398. ('branch_L', 'branch_K'),
  399. ])
  400. self.assertEqual(bottomup, [
  401. ('branch_L', 'branch_K'),
  402. ('branch_Z', 'root_X'),
  403. ('branch_K', 'branch_G'),
  404. ('branch_G', 'root_A'),
  405. ('root_A', 'root_X'),
  406. ])
  407. def testSquashBranch(self):
  408. self.repo.git('checkout', 'branch_K')
  409. self.repo.run(self.gc.squash_current_branch, 'cool message')
  410. lines = ['cool message', '']
  411. for l in 'HIJK':
  412. lines.extend((self.repo[l], l, ''))
  413. lines.pop()
  414. msg = '\n'.join(lines)
  415. self.assertEquals(self.repo.run(self.gc.run, 'log', '-n1', '--format=%B'),
  416. msg)
  417. self.assertEquals(
  418. self.repo.git('cat-file', 'blob', 'branch_K:file').stdout,
  419. 'K'
  420. )
  421. def testRebase(self):
  422. self.assertSchema("""
  423. A B C D E F G
  424. B H I J K
  425. J L
  426. X Y Z
  427. CAT DOG
  428. """)
  429. rslt = self.repo.run(
  430. self.gc.rebase, 'branch_G', 'branch_K~4', 'branch_K')
  431. self.assertTrue(rslt.success)
  432. self.assertSchema("""
  433. A B C D E F G H I J K
  434. B H I J L
  435. X Y Z
  436. CAT DOG
  437. """)
  438. rslt = self.repo.run(
  439. self.gc.rebase, 'branch_K', 'branch_L~1', 'branch_L', abort=True)
  440. self.assertFalse(rslt.success)
  441. self.assertFalse(self.repo.run(self.gc.in_rebase))
  442. rslt = self.repo.run(
  443. self.gc.rebase, 'branch_K', 'branch_L~1', 'branch_L', abort=False)
  444. self.assertFalse(rslt.success)
  445. self.assertTrue(self.repo.run(self.gc.in_rebase))
  446. self.assertEqual(self.repo.git('status', '--porcelain').stdout, 'UU file\n')
  447. self.repo.git('checkout', '--theirs', 'file')
  448. self.repo.git('add', 'file')
  449. self.repo.git('rebase', '--continue')
  450. self.assertSchema("""
  451. A B C D E F G H I J K L
  452. X Y Z
  453. CAT DOG
  454. """)
  455. class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase):
  456. @classmethod
  457. def setUpClass(cls):
  458. super(GitFreezeThaw, cls).setUpClass()
  459. import git_common
  460. cls.gc = git_common
  461. cls.gc.TEST_MODE = True
  462. REPO_SCHEMA = """
  463. A B C D
  464. B E D
  465. """
  466. COMMIT_A = {
  467. 'some/files/file1': {'data': 'file1'},
  468. 'some/files/file2': {'data': 'file2'},
  469. 'some/files/file3': {'data': 'file3'},
  470. 'some/other/file': {'data': 'otherfile'},
  471. }
  472. COMMIT_C = {
  473. 'some/files/file2': {
  474. 'mode': 0755,
  475. 'data': 'file2 - vanilla'},
  476. }
  477. COMMIT_E = {
  478. 'some/files/file2': {'data': 'file2 - merged'},
  479. }
  480. COMMIT_D = {
  481. 'some/files/file2': {'data': 'file2 - vanilla\nfile2 - merged'},
  482. }
  483. def testNothing(self):
  484. self.assertIsNotNone(self.repo.run(self.gc.thaw)) # 'Nothing to thaw'
  485. self.assertIsNotNone(self.repo.run(self.gc.freeze)) # 'Nothing to freeze'
  486. def testAll(self):
  487. def inner():
  488. with open('some/files/file2', 'a') as f2:
  489. print >> f2, 'cool appended line'
  490. os.mkdir('some/other_files')
  491. with open('some/other_files/subdir_file', 'w') as f3:
  492. print >> f3, 'new file!'
  493. with open('some/files/file5', 'w') as f5:
  494. print >> f5, 'New file!1!one!'
  495. STATUS_1 = '\n'.join((
  496. ' M some/files/file2',
  497. 'A some/files/file5',
  498. '?? some/other_files/'
  499. )) + '\n'
  500. self.repo.git('add', 'some/files/file5')
  501. # Freeze group 1
  502. self.assertEquals(self.repo.git('status', '--porcelain').stdout, STATUS_1)
  503. self.assertIsNone(self.gc.freeze())
  504. self.assertEquals(self.repo.git('status', '--porcelain').stdout, '')
  505. # Freeze group 2
  506. with open('some/files/file2', 'a') as f2:
  507. print >> f2, 'new! appended line!'
  508. self.assertEquals(self.repo.git('status', '--porcelain').stdout,
  509. ' M some/files/file2\n')
  510. self.assertIsNone(self.gc.freeze())
  511. self.assertEquals(self.repo.git('status', '--porcelain').stdout, '')
  512. # Thaw it out!
  513. self.assertIsNone(self.gc.thaw())
  514. self.assertIsNotNone(self.gc.thaw()) # One thaw should thaw everything
  515. self.assertEquals(self.repo.git('status', '--porcelain').stdout, STATUS_1)
  516. self.repo.run(inner)
  517. if __name__ == '__main__':
  518. sys.exit(coverage_utils.covered_main(
  519. os.path.join(DEPOT_TOOLS_ROOT, 'git_common.py')
  520. ))