blob: dc6dfc0aecd19269b5ee8832a1040fd3edb4060c [file] [log] [blame]
Edward Lemura877ee62019-09-03 20:23:17 +00001#!/usr/bin/env vpython3
steveblock@chromium.org93567042012-02-15 01:02:26 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
maruel@chromium.orgba551772010-02-03 18:21:42 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
msb@chromium.orge28e4982009-09-25 20:51:45 +00005
6"""Unit tests for gclient_scm.py."""
7
maruel@chromium.org428342a2011-11-10 15:46:33 +00008# pylint: disable=E1103
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +00009
Edward Lesmese79107e2019-10-25 22:47:33 +000010from __future__ import unicode_literals
11
12
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000013from subprocess import Popen, PIPE, STDOUT
maruel@chromium.org428342a2011-11-10 15:46:33 +000014
John Budorick0f7b2002018-01-19 15:46:17 -080015import json
maruel@chromium.org428342a2011-11-10 15:46:33 +000016import logging
17import os
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000018import re
maruel@chromium.org428342a2011-11-10 15:46:33 +000019import sys
msb@chromium.orge28e4982009-09-25 20:51:45 +000020import tempfile
maruel@chromium.org389d6de2010-09-09 14:14:37 +000021import unittest
msb@chromium.orge28e4982009-09-25 20:51:45 +000022
Edward Lemur979fa782019-08-13 22:44:05 +000023if sys.version_info.major == 2:
24 from cStringIO import StringIO
Edward Lemura8145022020-01-06 18:47:54 +000025 import mock
Edward Lemur979fa782019-08-13 22:44:05 +000026else:
27 from io import StringIO
Edward Lemura8145022020-01-06 18:47:54 +000028 from unittest import mock
Edward Lemur979fa782019-08-13 22:44:05 +000029
maruel@chromium.org428342a2011-11-10 15:46:33 +000030sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
maruel@chromium.orgba551772010-02-03 18:21:42 +000031
Edward Lemurd64781e2018-07-11 23:09:55 +000032from testing_support import fake_repos
Edward Lemur9cafbf42019-08-15 22:03:35 +000033from testing_support import test_case_utils
maruel@chromium.org428342a2011-11-10 15:46:33 +000034
msb@chromium.orge28e4982009-09-25 20:51:45 +000035import gclient_scm
Edward Lesmese79107e2019-10-25 22:47:33 +000036import gclient_utils
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000037import git_cache
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000038import subprocess2
maruel@chromium.org96913eb2010-06-01 16:22:47 +000039
Edward Lesmese79107e2019-10-25 22:47:33 +000040
41GIT = 'git' if sys.platform != 'win32' else 'git.bat'
42
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000043# Disable global git cache
44git_cache.Mirror.SetCachePath(None)
45
maruel@chromium.org795a8c12010-10-05 19:54:29 +000046# Shortcut since this function is used often
47join = gclient_scm.os.path.join
48
Raul Tambrea79f0e52019-09-21 07:27:39 +000049TIMESTAMP_RE = re.compile(r'\[[0-9]{1,2}:[0-9]{2}:[0-9]{2}\] (.*)', re.DOTALL)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000050def strip_timestamps(value):
51 lines = value.splitlines(True)
Edward Lemur979fa782019-08-13 22:44:05 +000052 for i in range(len(lines)):
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000053 m = TIMESTAMP_RE.match(lines[i])
54 if m:
55 lines[i] = m.group(1)
56 return ''.join(lines)
maruel@chromium.org96913eb2010-06-01 16:22:47 +000057
maruel@chromium.orgd579fcf2011-12-13 20:36:03 +000058
Edward Lemur979fa782019-08-13 22:44:05 +000059class BasicTests(unittest.TestCase):
60 @mock.patch('gclient_scm.scm.GIT.Capture')
61 def testGetFirstRemoteUrl(self, mockCapture):
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000062 REMOTE_STRINGS = [('remote.origin.url E:\\foo\\bar', 'E:\\foo\\bar'),
63 ('remote.origin.url /b/foo/bar', '/b/foo/bar'),
64 ('remote.origin.url https://foo/bar', 'https://foo/bar'),
65 ('remote.origin.url E:\\Fo Bar\\bax', 'E:\\Fo Bar\\bax'),
66 ('remote.origin.url git://what/"do', 'git://what/"do')]
67 FAKE_PATH = '/fake/path'
Edward Lemur979fa782019-08-13 22:44:05 +000068 mockCapture.side_effect = [question for question, _ in REMOTE_STRINGS]
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000069
70 for _, answer in REMOTE_STRINGS:
Edward Lemur979fa782019-08-13 22:44:05 +000071 self.assertEqual(
72 gclient_scm.SCMWrapper._get_first_remote_url(FAKE_PATH), answer)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000073
Edward Lemur979fa782019-08-13 22:44:05 +000074 expected_calls = [
75 mock.call(['config', '--local', '--get-regexp', r'remote.*.url'],
76 cwd=FAKE_PATH)
77 for _ in REMOTE_STRINGS
78 ]
79 self.assertEqual(mockCapture.mock_calls, expected_calls)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000080
81
Edward Lemur9cafbf42019-08-15 22:03:35 +000082class BaseGitWrapperTestCase(unittest.TestCase, test_case_utils.TestCaseUtils):
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000083 """This class doesn't use pymox."""
msb@chromium.orge28e4982009-09-25 20:51:45 +000084 class OptionsObject(object):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +000085 def __init__(self, verbose=False, revision=None):
dnj@chromium.org5b23e872015-02-20 21:25:57 +000086 self.auto_rebase = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000087 self.verbose = verbose
88 self.revision = revision
msb@chromium.orge28e4982009-09-25 20:51:45 +000089 self.deps_os = None
90 self.force = False
davemoore@chromium.org8bf27312010-02-19 17:29:44 +000091 self.reset = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000092 self.nohooks = False
primiano@chromium.org5439ea52014-08-06 17:18:18 +000093 self.no_history = False
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +000094 self.upstream = False
iannucci@chromium.org53456aa2013-07-03 19:38:34 +000095 self.cache_dir = None
bauerb@chromium.org4dd09372011-07-22 14:41:51 +000096 self.merge = False
bratell@opera.com18fa4542013-05-21 13:30:46 +000097 self.jobs = 1
iannucci@chromium.org30a07982016-04-07 21:35:19 +000098 self.break_repo_locks = False
steveblock@chromium.org98e69452012-02-16 16:36:43 +000099 self.delete_unversioned_trees = False
Edward Lesmesc621b212018-03-21 20:26:56 -0400100 self.patch_ref = None
101 self.patch_repo = None
102 self.rebase_patch_ref = True
Edward Lemurca7d8812018-07-24 17:42:45 +0000103 self.reset_patch_ref = True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000104
105 sample_git_import = """blob
106mark :1
107data 6
108Hello
109
110blob
111mark :2
112data 4
113Bye
114
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000115reset refs/heads/main
116commit refs/heads/main
msb@chromium.orge28e4982009-09-25 20:51:45 +0000117mark :3
118author Bob <bob@example.com> 1253744361 -0700
119committer Bob <bob@example.com> 1253744361 -0700
120data 8
121A and B
122M 100644 :1 a
123M 100644 :2 b
124
125blob
126mark :4
127data 10
128Hello
129You
130
131blob
132mark :5
133data 8
134Bye
135You
136
137commit refs/heads/origin
138mark :6
139author Alice <alice@example.com> 1253744424 -0700
140committer Alice <alice@example.com> 1253744424 -0700
141data 13
142Personalized
143from :3
144M 100644 :4 a
145M 100644 :5 b
146
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000147blob
148mark :7
149data 5
150Mooh
151
152commit refs/heads/feature
153mark :8
154author Bob <bob@example.com> 1390311986 -0000
155committer Bob <bob@example.com> 1390311986 -0000
156data 6
157Add C
158from :3
159M 100644 :7 c
160
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000161reset refs/heads/main
msb@chromium.orge28e4982009-09-25 20:51:45 +0000162from :3
163"""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000164 def Options(self, *args, **kwargs):
maruel@chromium.org8071c282010-09-20 19:44:19 +0000165 return self.OptionsObject(*args, **kwargs)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000166
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000167 def checkstdout(self, expected):
Aravind Vasudevanc5f0cbb2022-01-24 23:56:57 +0000168 # pylint: disable=no-member
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000169 value = sys.stdout.getvalue()
170 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000171 # Check that the expected output appears.
Erik Chene16ffff2019-10-14 20:35:53 +0000172 self.assertIn(expected, strip_timestamps(value))
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000173
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000174 @staticmethod
175 def CreateGitRepo(git_import, path):
maruel@chromium.orgd5800f12009-11-12 20:03:43 +0000176 """Do it for real."""
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000177 try:
Edward Lesmese79107e2019-10-25 22:47:33 +0000178 Popen([GIT, 'init', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000179 cwd=path).communicate()
180 except OSError:
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000181 # git is not available, skip this test.
182 return False
Edward Lesmese79107e2019-10-25 22:47:33 +0000183 Popen([GIT, 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
Edward Lemur979fa782019-08-13 22:44:05 +0000184 stderr=STDOUT, cwd=path).communicate(input=git_import.encode())
Edward Lesmese79107e2019-10-25 22:47:33 +0000185 Popen([GIT, 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000186 cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000187 Popen([GIT, 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000188 stderr=STDOUT, cwd=path).communicate()
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000189 Popen([GIT, 'checkout', '-b', 'new', 'origin/main', '-q'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000190 stderr=STDOUT, cwd=path).communicate()
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000191 Popen([GIT, 'push', 'origin', 'origin/origin:origin/main', '-q'],
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000192 stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000193 Popen([GIT, 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000194 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000195 Popen([GIT, 'config', 'user.email', 'someuser@chromium.org'], stdout=PIPE,
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000196 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000197 Popen([GIT, 'config', 'user.name', 'Some User'], stdout=PIPE,
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000198 stderr=STDOUT, cwd=path).communicate()
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000199 # Set HEAD back to main
200 Popen([GIT, 'checkout', 'main', '-q'],
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000201 stdout=PIPE,
202 stderr=STDOUT,
203 cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000204 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000205
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000206 def _GetAskForDataCallback(self, expected_prompt, return_value):
207 def AskForData(prompt, options):
Edward Lemur979fa782019-08-13 22:44:05 +0000208 self.assertEqual(prompt, expected_prompt)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000209 return return_value
210 return AskForData
211
msb@chromium.orge28e4982009-09-25 20:51:45 +0000212 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000213 unittest.TestCase.setUp(self)
Edward Lemur9cafbf42019-08-15 22:03:35 +0000214 test_case_utils.TestCaseUtils.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000215 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000216 # The .git suffix allows gclient_scm to recognize the dir as a git repo
217 # when cloning it locally
218 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000219 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000220 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000221 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000222 mock.patch('sys.stdout', StringIO()).start()
223 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000224 self.addCleanup(gclient_utils.rmtree, self.root_dir)
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000225
msb@chromium.orge28e4982009-09-25 20:51:45 +0000226
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000227class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000228
Joanna Wang1a977bd2022-06-02 21:51:17 +0000229 @mock.patch('gclient_scm.GitWrapper._IsCog')
230 @mock.patch('gclient_scm.GitWrapper._Run', return_value=True)
231 @mock.patch('gclient_scm.GitWrapper._SetFetchConfig')
232 @mock.patch('gclient_scm.GitWrapper._GetCurrentBranch')
233 def testCloneInCog(self, mockGetCurrentBranch, mockSetFetchConfig, mockRun,
234 _mockIsCog):
235 """Test that we call the correct commands when in a cog workspace."""
236 if not self.enabled:
237 return
238 options = self.Options()
239 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
240 scm._Clone('123123ab', self.url, options)
Joanna Wanga9467d82022-06-09 22:05:43 +0000241 mockRun.assert_called_once_with(
242 ['citc', 'clone-repo', self.url, scm.checkout_path, '123123ab'],
243 options,
244 cwd=scm._root_dir,
245 retry=True,
246 print_stdout=False,
247 filter_fn=scm.filter)
Joanna Wang1a977bd2022-06-02 21:51:17 +0000248 mockSetFetchConfig.assert_called_once()
249 mockGetCurrentBranch.assert_called_once()
250
msb@chromium.orge28e4982009-09-25 20:51:45 +0000251 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000252 if not self.enabled:
253 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000254 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000255 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800256 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
257 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000258 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000259 scm.update(options, None, file_list)
260 gclient_scm.os.remove(file_path)
261 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000262 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000263 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000264 file_list = []
265 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000266 self.assertEqual(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000267 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000268
269 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000270 if not self.enabled:
271 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000272 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800273 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
274 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000275 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000276 scm.update(options, None, file_list)
277 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000278 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000279 self.assertEqual(file_list, [])
280 self.assertEqual(scm.revinfo(options, self.args, None),
281 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000282 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000283
284 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000285 if not self.enabled:
286 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000287 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800288 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
289 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000290 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000291 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000292 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000293 with open(file_path, 'a') as f:
294 f.writelines('touched\n')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000295 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000296 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000297 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000298 file_list = []
299 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000300 self.assertEqual(file_list, [])
301 self.assertEqual(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000302 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000303 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000304
305 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000306 if not self.enabled:
307 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000308 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800309 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
310 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000311 file_list = []
312 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000313 file_path = join(self.base_path, 'c')
Edward Lemur979fa782019-08-13 22:44:05 +0000314 with open(file_path, 'w') as f:
315 f.writelines('new\n')
Edward Lesmese79107e2019-10-25 22:47:33 +0000316 Popen([GIT, 'add', 'c'], stdout=PIPE,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000317 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000318 file_list = []
319 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000320 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000321 file_list = []
322 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000323 self.assertEqual(file_list, [])
324 self.assertEqual(scm.revinfo(options, self.args, None),
325 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000326 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000327
Joanna Wanga654ff32023-07-18 23:25:19 +0000328 def testStatusRef(self):
329 if not self.enabled:
330 return
331 options = self.Options()
332 file_paths = [join(self.base_path, 'a')]
333 with open(file_paths[0], 'a') as f:
334 f.writelines('touched\n')
335 scm = gclient_scm.GitWrapper(self.url + '@refs/heads/feature',
336 self.root_dir, self.relpath)
337 file_paths.append(join(self.base_path, 'c')) # feature branch touches c
338 file_list = []
339 scm.status(options, self.args, file_list)
340 self.assertEqual(file_list, file_paths)
341 self.checkstdout(
342 ('\n________ running \'git -c core.quotePath=false diff --name-status '
343 'refs/remotes/origin/feature\' in \'%s\'\n\nM\ta\n') %
344 join(self.root_dir, '.'))
345
msb@chromium.orge28e4982009-09-25 20:51:45 +0000346 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000347 if not self.enabled:
348 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000349 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000350 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000351 with open(file_path, 'a') as f:
352 f.writelines('touched\n')
Anthony Politobb457342019-11-15 22:26:01 +0000353 scm = gclient_scm.GitWrapper(
354 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
355 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000356 file_list = []
357 scm.status(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000358 self.assertEqual(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000359 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800360 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000361 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000362 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000363
Anthony Politobb457342019-11-15 22:26:01 +0000364
365 def testStatusNewNoBaseRev(self):
366 if not self.enabled:
367 return
368 options = self.Options()
369 file_path = join(self.base_path, 'a')
370 with open(file_path, 'a') as f:
371 f.writelines('touched\n')
372 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
373 file_list = []
374 scm.status(options, self.args, file_list)
375 self.assertEqual(file_list, [file_path])
376 self.checkstdout(
377 ('\n________ running \'git -c core.quotePath=false diff --name-status'
378 '\' in \'%s\'\n\nM\ta\n') % join(self.root_dir, '.'))
379
msb@chromium.orge28e4982009-09-25 20:51:45 +0000380 def testStatus2New(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000381 if not self.enabled:
382 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000383 options = self.Options()
384 expected_file_list = []
385 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000386 file_path = join(self.base_path, f)
Edward Lemur979fa782019-08-13 22:44:05 +0000387 with open(file_path, 'a') as f:
388 f.writelines('touched\n')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000389 expected_file_list.extend([file_path])
Anthony Politobb457342019-11-15 22:26:01 +0000390 scm = gclient_scm.GitWrapper(
391 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
392 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000393 file_list = []
394 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000395 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
Edward Lemur979fa782019-08-13 22:44:05 +0000396 self.assertEqual(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000397 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800398 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000399 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\nM\tb\n')
400 % join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000401
msb@chromium.orge28e4982009-09-25 20:51:45 +0000402 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000403 if not self.enabled:
404 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000405 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000406 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800407 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
408 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000409 file_list = []
Joanna Wang4e6c1072023-08-17 18:46:24 +0000410 scm._Capture(['config', 'diff.ignoreSubmodules', 'all'])
411
msb@chromium.orge28e4982009-09-25 20:51:45 +0000412 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000413 self.assertEqual(file_list, expected_file_list)
414 self.assertEqual(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000415 'a7142dc9f0009350b96a11f372b6ea658592aa95')
Joanna Wang4e6c1072023-08-17 18:46:24 +0000416 with self.assertRaises(Exception):
417 scm._Capture(['config', '--get', 'diff.ignoreSubmodules'])
Josip Sokcevic84c0bba2023-08-10 00:52:15 +0000418 self.assertEqual(
419 scm._Capture(['config', '--get', 'fetch.recurseSubmodules']), 'off')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000420 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000421
Joanna Wang4e6c1072023-08-17 18:46:24 +0000422 def testUpdateUpdate_only_unset_ignoresubmodules_all(self):
423 if not self.enabled:
424 return
425 options = self.Options()
426 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
427 file_list = []
428 scm._Capture(['config', 'diff.ignoreSubmodules', 'none'])
429
430 scm.update(options, (), file_list)
431 self.assertEqual(scm._Capture(['config', '--get', 'diff.ignoreSubmodules']),
432 'none')
433 sys.stdout.close()
434
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000435 def testUpdateMerge(self):
436 if not self.enabled:
437 return
438 options = self.Options()
439 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800440 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
441 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000442 scm._Run(['checkout', '-q', 'feature'], options)
443 rev = scm.revinfo(options, (), None)
444 file_list = []
445 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000446 self.assertEqual(file_list, [join(self.base_path, x)
447 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000448 # The actual commit that is created is unstable, so we verify its tree and
449 # parents instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000450 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
451 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000452 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
453 self.assertEqual(scm._Capture(['rev-parse', parent + '1']), rev)
454 self.assertEqual(scm._Capture(['rev-parse', parent + '2']),
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000455 scm._Capture(['rev-parse', 'origin/main']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000456 sys.stdout.close()
457
458 def testUpdateRebase(self):
459 if not self.enabled:
460 return
461 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800462 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
463 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000464 scm._Run(['checkout', '-q', 'feature'], options)
465 file_list = []
466 # Fake a 'y' key press.
467 scm._AskForData = self._GetAskForDataCallback(
468 'Cannot fast-forward merge, attempt to rebase? '
469 '(y)es / (q)uit / (s)kip : ', 'y')
470 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000471 self.assertEqual(file_list, [join(self.base_path, x)
472 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000473 # The actual commit that is created is unstable, so we verify its tree and
474 # parent instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000475 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
476 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000477 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
478 self.assertEqual(scm._Capture(['rev-parse', parent + '1']),
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000479 scm._Capture(['rev-parse', 'origin/main']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000480 sys.stdout.close()
481
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000482 def testUpdateReset(self):
483 if not self.enabled:
484 return
485 options = self.Options()
486 options.reset = True
487
488 dir_path = join(self.base_path, 'c')
489 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000490 with open(join(dir_path, 'nested'), 'w') as f:
491 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000492
493 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000494 with open(file_path, 'w') as f:
495 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000496
John Budorick0f7b2002018-01-19 15:46:17 -0800497 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
498 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000499 file_list = []
500 scm.update(options, (), file_list)
501 self.assert_(gclient_scm.os.path.isdir(dir_path))
502 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000503 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000504
Edward Lemur579c9862018-07-13 23:17:51 +0000505 def testUpdateResetUnsetsFetchConfig(self):
506 if not self.enabled:
507 return
508 options = self.Options()
509 options.reset = True
510
511 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
512 self.relpath)
513 scm._Run(['config', 'remote.origin.fetch',
514 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
515
516 file_list = []
517 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000518 self.assertEqual(scm.revinfo(options, (), None),
519 '069c602044c5388d2d15c3f875b057c852003458')
Edward Lemur579c9862018-07-13 23:17:51 +0000520 sys.stdout.close()
521
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000522 def testUpdateResetDeleteUnversionedTrees(self):
523 if not self.enabled:
524 return
525 options = self.Options()
526 options.reset = True
527 options.delete_unversioned_trees = True
528
529 dir_path = join(self.base_path, 'dir')
530 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000531 with open(join(dir_path, 'nested'), 'w') as f:
532 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000533
534 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000535 with open(file_path, 'w') as f:
536 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000537
John Budorick0f7b2002018-01-19 15:46:17 -0800538 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
539 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000540 file_list = []
541 scm.update(options, (), file_list)
542 self.assert_(not gclient_scm.os.path.isdir(dir_path))
543 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000544 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000545
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000546 def testUpdateUnstagedConflict(self):
547 if not self.enabled:
548 return
549 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800550 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
551 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000552 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000553 with open(file_path, 'w') as f:
554 f.writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000555 try:
556 scm.update(options, (), [])
557 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000558 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000559 # The exact exception text varies across git versions so it's not worth
560 # verifying it. It's fine as long as it throws.
561 pass
562 # Manually flush stdout since we can't verify it's content accurately across
563 # git versions.
564 sys.stdout.getvalue()
565 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000566
Mike Stipicevice992b612016-12-02 15:32:55 -0800567 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000568 def testUpdateLocked(self):
569 if not self.enabled:
570 return
571 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800572 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
573 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000574 file_path = join(self.base_path, '.git', 'index.lock')
575 with open(file_path, 'w'):
576 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800577 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000578 scm.update(options, (), [])
579 sys.stdout.close()
580
581 def testUpdateLockedBreak(self):
582 if not self.enabled:
583 return
584 options = self.Options()
585 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800586 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
587 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000588 file_path = join(self.base_path, '.git', 'index.lock')
589 with open(file_path, 'w'):
590 pass
591 scm.update(options, (), [])
592 self.assertRegexpMatches(sys.stdout.getvalue(),
Raul Tambrea79f0e52019-09-21 07:27:39 +0000593 r'breaking lock.*\.git[/|\\]index\.lock')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000594 self.assertFalse(os.path.exists(file_path))
595 sys.stdout.close()
596
msb@chromium.org5bde4852009-12-14 16:47:12 +0000597 def testUpdateConflict(self):
598 if not self.enabled:
599 return
600 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800601 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
602 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000603 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000604 with open(file_path, 'w') as f:
605 f.writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000606 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000607 scm._AskForData = self._GetAskForDataCallback(
608 'Cannot fast-forward merge, attempt to rebase? '
609 '(y)es / (q)uit / (s)kip : ', 'y')
Edward Lemur979fa782019-08-13 22:44:05 +0000610
611 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
612 scm.update(options, (), [])
613 self.assertEqual(
614 e.exception.args[0],
615 'Conflict while rebasing this branch.\n'
616 'Fix the conflict and run gclient again.\n'
617 'See \'man git-rebase\' for details.\n')
618
619 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
620 scm.update(options, (), [])
621 self.assertEqual(
622 e.exception.args[0],
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000623 '\n____ . at refs/remotes/origin/main\n'
Edward Lemur979fa782019-08-13 22:44:05 +0000624 '\tYou have unstaged changes.\n'
625 '\tPlease commit, stash, or reset.\n')
626
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000627 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000628
msb@chromium.org0f282062009-11-06 20:14:02 +0000629 def testRevinfo(self):
630 if not self.enabled:
631 return
632 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800633 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
634 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000635 rev_info = scm.revinfo(options, (), None)
Edward Lemur979fa782019-08-13 22:44:05 +0000636 self.assertEqual(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
msb@chromium.org0f282062009-11-06 20:14:02 +0000637
msb@chromium.orge28e4982009-09-25 20:51:45 +0000638
Edward Lemur979fa782019-08-13 22:44:05 +0000639class ManagedGitWrapperTestCaseMock(unittest.TestCase):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000640 class OptionsObject(object):
641 def __init__(self, verbose=False, revision=None, force=False):
642 self.verbose = verbose
643 self.revision = revision
644 self.deps_os = None
645 self.force = force
646 self.reset = False
647 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000648 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000649 # TODO(maruel): Test --jobs > 1.
650 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400651 self.patch_ref = None
652 self.patch_repo = None
653 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000654
655 def Options(self, *args, **kwargs):
656 return self.OptionsObject(*args, **kwargs)
657
borenet@google.comb09097a2014-04-09 19:09:08 +0000658 def checkstdout(self, expected):
Aravind Vasudevanc5f0cbb2022-01-24 23:56:57 +0000659 # pylint: disable=no-member
borenet@google.comb09097a2014-04-09 19:09:08 +0000660 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000661 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000662 # Check that the expected output appears.
Erik Chene16ffff2019-10-14 20:35:53 +0000663 self.assertIn(expected, strip_timestamps(value))
borenet@google.comb09097a2014-04-09 19:09:08 +0000664
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000665 def setUp(self):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000666 self.fake_hash_1 = 't0ta11yf4k3'
667 self.fake_hash_2 = '3v3nf4k3r'
668 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000669 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000670 self.relpath = 'fake'
671 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000672 self.backup_base_path = os.path.join(self.root_dir,
673 'old_%s.git' % self.relpath)
Edward Lemur979fa782019-08-13 22:44:05 +0000674 mock.patch('gclient_scm.scm.GIT.ApplyEnvVars').start()
675 mock.patch('gclient_scm.GitWrapper._CheckMinVersion').start()
676 mock.patch('gclient_scm.GitWrapper._Fetch').start()
677 mock.patch('gclient_scm.GitWrapper._DeleteOrMove').start()
678 mock.patch('sys.stdout', StringIO()).start()
679 self.addCleanup(mock.patch.stopall)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000680
Josip Sokcevic7958e302023-03-01 23:02:21 +0000681 @mock.patch('scm.GIT.IsValidRevision')
Edward Lemur979fa782019-08-13 22:44:05 +0000682 @mock.patch('os.path.isdir', lambda _: True)
683 def testGetUsableRevGit(self, mockIsValidRevision):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800684 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700685 options = self.Options(verbose=True)
686
Edward Lemur979fa782019-08-13 22:44:05 +0000687 mockIsValidRevision.side_effect = lambda cwd, rev: rev != '1'
smutae7ea312016-07-18 11:59:41 -0700688
John Budorick0f7b2002018-01-19 15:46:17 -0800689 git_scm = gclient_scm.GitWrapper(self.url, self.root_dir,
690 self.relpath)
smutae7ea312016-07-18 11:59:41 -0700691 # A [fake] git sha1 with a git repo should work (this is in the case that
692 # the LKGR gets flipped to git sha1's some day).
Edward Lemur979fa782019-08-13 22:44:05 +0000693 self.assertEqual(git_scm.GetUsableRev(self.fake_hash_1, options),
694 self.fake_hash_1)
smutae7ea312016-07-18 11:59:41 -0700695 # An SVN rev with an existing purely git repo should raise an exception.
696 self.assertRaises(gclient_scm.gclient_utils.Error,
697 git_scm.GetUsableRev, '1', options)
698
Edward Lemur979fa782019-08-13 22:44:05 +0000699 @mock.patch('gclient_scm.GitWrapper._Clone')
700 @mock.patch('os.path.isdir')
701 @mock.patch('os.path.exists')
702 @mock.patch('subprocess2.check_output')
703 def testUpdateNoDotGit(
704 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
705 mockIsdir.side_effect = lambda path: path == self.base_path
706 mockExists.side_effect = lambda path: path == self.base_path
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000707 mockCheckOutput.side_effect = [b'refs/remotes/origin/main', b'', b'']
Edward Lemur979fa782019-08-13 22:44:05 +0000708
borenet@google.comb09097a2014-04-09 19:09:08 +0000709 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000710 scm = gclient_scm.GitWrapper(
711 self.url, self.root_dir, self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000712 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000713
714 env = gclient_scm.scm.GIT.ApplyEnvVars({})
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000715 self.assertEqual(mockCheckOutput.mock_calls, [
716 mock.call(['git', 'symbolic-ref', 'refs/remotes/origin/HEAD'],
717 cwd=self.base_path,
718 env=env,
719 stderr=-1),
720 mock.call(['git', '-c', 'core.quotePath=false', 'ls-files'],
721 cwd=self.base_path,
722 env=env,
723 stderr=-1),
724 mock.call(['git', 'rev-parse', '--verify', 'HEAD'],
725 cwd=self.base_path,
726 env=env,
727 stderr=-1),
728 ])
729 mockClone.assert_called_with('refs/remotes/origin/main', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000730 self.checkstdout('\n')
731
Edward Lemur979fa782019-08-13 22:44:05 +0000732 @mock.patch('gclient_scm.GitWrapper._Clone')
733 @mock.patch('os.path.isdir')
734 @mock.patch('os.path.exists')
735 @mock.patch('subprocess2.check_output')
736 def testUpdateConflict(
737 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
738 mockIsdir.side_effect = lambda path: path == self.base_path
739 mockExists.side_effect = lambda path: path == self.base_path
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000740 mockCheckOutput.side_effect = [b'refs/remotes/origin/main', b'', b'']
Edward Lemur979fa782019-08-13 22:44:05 +0000741 mockClone.side_effect = [
742 gclient_scm.subprocess2.CalledProcessError(
743 None, None, None, None, None),
744 None,
745 ]
746
borenet@google.com90fe58b2014-05-01 18:22:00 +0000747 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000748 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
John Budorick0f7b2002018-01-19 15:46:17 -0800749 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000750 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000751
752 env = gclient_scm.scm.GIT.ApplyEnvVars({})
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000753 self.assertEqual(mockCheckOutput.mock_calls, [
754 mock.call(['git', 'symbolic-ref', 'refs/remotes/origin/HEAD'],
755 cwd=self.base_path,
756 env=env,
757 stderr=-1),
758 mock.call(['git', '-c', 'core.quotePath=false', 'ls-files'],
759 cwd=self.base_path,
760 env=env,
761 stderr=-1),
762 mock.call(['git', 'rev-parse', '--verify', 'HEAD'],
763 cwd=self.base_path,
764 env=env,
765 stderr=-1),
766 ])
767 mockClone.assert_called_with('refs/remotes/origin/main', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000768 self.checkstdout('\n')
769
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000770
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000771class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000772 def checkInStdout(self, expected):
Aravind Vasudevanc5f0cbb2022-01-24 23:56:57 +0000773 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000774 value = sys.stdout.getvalue()
775 sys.stdout.close()
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000776 self.assertIn(expected, value)
777
778 def checkNotInStdout(self, expected):
Aravind Vasudevanc5f0cbb2022-01-24 23:56:57 +0000779 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000780 value = sys.stdout.getvalue()
781 sys.stdout.close()
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000782 self.assertNotIn(expected, value)
783
smut@google.com27c9c8a2014-09-11 19:57:55 +0000784 def getCurrentBranch(self):
785 # Returns name of current branch or HEAD for detached HEAD
786 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
787 cwd=self.base_path)
788 if branch == 'HEAD':
789 return None
790 return branch
791
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000792 def testUpdateClone(self):
793 if not self.enabled:
794 return
795 options = self.Options()
796
797 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000798 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
799
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000800 self.root_dir = tempfile.mkdtemp()
801 self.relpath = '.'
802 self.base_path = join(self.root_dir, self.relpath)
803
John Budorick0f7b2002018-01-19 15:46:17 -0800804 scm = gclient_scm.GitWrapper(origin_root_dir,
805 self.root_dir,
806 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000807
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000808 expected_file_list = [join(self.base_path, "a"),
809 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000810 file_list = []
811 options.revision = 'unmanaged'
812 scm.update(options, (), file_list)
813
Edward Lemur979fa782019-08-13 22:44:05 +0000814 self.assertEqual(file_list, expected_file_list)
815 self.assertEqual(scm.revinfo(options, (), None),
816 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000817 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000818 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000819 self.checkInStdout(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +0000820 'Checked out refs/remotes/origin/main to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000821
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000822
823 def testUpdateCloneOnCommit(self):
824 if not self.enabled:
825 return
826 options = self.Options()
827
828 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000829 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
830
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000831 self.root_dir = tempfile.mkdtemp()
832 self.relpath = '.'
833 self.base_path = join(self.root_dir, self.relpath)
834 url_with_commit_ref = origin_root_dir +\
835 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
836
John Budorick0f7b2002018-01-19 15:46:17 -0800837 scm = gclient_scm.GitWrapper(url_with_commit_ref,
838 self.root_dir,
839 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000840
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000841 expected_file_list = [join(self.base_path, "a"),
842 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000843 file_list = []
844 options.revision = 'unmanaged'
845 scm.update(options, (), file_list)
846
Edward Lemur979fa782019-08-13 22:44:05 +0000847 self.assertEqual(file_list, expected_file_list)
848 self.assertEqual(scm.revinfo(options, (), None),
849 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000850 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000851 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000852 self.checkInStdout(
853 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000854
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000855 def testUpdateCloneOnBranch(self):
856 if not self.enabled:
857 return
858 options = self.Options()
859
860 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000861 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
862
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000863 self.root_dir = tempfile.mkdtemp()
864 self.relpath = '.'
865 self.base_path = join(self.root_dir, self.relpath)
866 url_with_branch_ref = origin_root_dir + '@feature'
867
John Budorick0f7b2002018-01-19 15:46:17 -0800868 scm = gclient_scm.GitWrapper(url_with_branch_ref,
869 self.root_dir,
870 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000871
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000872 expected_file_list = [join(self.base_path, "a"),
873 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000874 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000875 file_list = []
876 options.revision = 'unmanaged'
877 scm.update(options, (), file_list)
878
Edward Lemur979fa782019-08-13 22:44:05 +0000879 self.assertEqual(file_list, expected_file_list)
880 self.assertEqual(scm.revinfo(options, (), None),
881 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200882 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000883 self.assertEqual(self.getCurrentBranch(), None)
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200884 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000885 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200886 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000887
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000888 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000889 if not self.enabled:
890 return
891 options = self.Options()
892
893 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000894 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
895
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000896 self.root_dir = tempfile.mkdtemp()
897 self.relpath = '.'
898 self.base_path = join(self.root_dir, self.relpath)
899 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
900
John Budorick0f7b2002018-01-19 15:46:17 -0800901 scm = gclient_scm.GitWrapper(url_with_branch_ref,
902 self.root_dir,
903 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000904
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000905 expected_file_list = [join(self.base_path, "a"),
906 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000907 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000908 file_list = []
909 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000910 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000911
Edward Lemur979fa782019-08-13 22:44:05 +0000912 self.assertEqual(file_list, expected_file_list)
913 self.assertEqual(scm.revinfo(options, (), None),
914 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000915 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000916 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000917 self.checkInStdout(
918 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000919
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000920 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000921 if not self.enabled:
922 return
923 options = self.Options()
924
925 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000926 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
927
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000928 self.root_dir = tempfile.mkdtemp()
929 self.relpath = '.'
930 self.base_path = join(self.root_dir, self.relpath)
931 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
932
John Budorick0f7b2002018-01-19 15:46:17 -0800933 scm = gclient_scm.GitWrapper(url_with_branch_ref,
934 self.root_dir,
935 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000936
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000937 expected_file_list = [join(self.base_path, "a"),
938 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000939 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000940 file_list = []
941 options.revision = 'unmanaged'
942 scm.update(options, (), file_list)
943
Edward Lemur979fa782019-08-13 22:44:05 +0000944 self.assertEqual(file_list, expected_file_list)
945 self.assertEqual(scm.revinfo(options, (), None),
946 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
John Budorick882c91e2018-07-12 22:11:41 +0000947 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000948 # should be treated as such by gclient.
949 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
950 # as they are known in the upstream repo, since the mapping into the local
951 # repo can be modified by users (or we might even want to change the gclient
952 # defaults at some point). But that will take more work to stop using
953 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
954 # always resolve locally, like when passing them to show-ref or rev-list).
Edward Lemur979fa782019-08-13 22:44:05 +0000955 self.assertEqual(self.getCurrentBranch(), None)
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000956 self.checkInStdout(
957 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000958
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000959 def testUpdateUpdate(self):
960 if not self.enabled:
961 return
962 options = self.Options()
963 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800964 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
965 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000966 file_list = []
967 options.revision = 'unmanaged'
968 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000969 self.assertEqual(file_list, expected_file_list)
970 self.assertEqual(scm.revinfo(options, (), None),
971 '069c602044c5388d2d15c3f875b057c852003458')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000972 self.checkstdout('________ unmanaged solution; skipping .\n')
973
974
Edward Lemur979fa782019-08-13 22:44:05 +0000975class CipdWrapperTestCase(unittest.TestCase):
John Budorick0f7b2002018-01-19 15:46:17 -0800976
977 def setUp(self):
978 # Create this before setting up mocks.
979 self._cipd_root_dir = tempfile.mkdtemp()
980 self._workdir = tempfile.mkdtemp()
John Budorick0f7b2002018-01-19 15:46:17 -0800981
982 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
983 self._cipd_root = gclient_scm.CipdRoot(
984 self._cipd_root_dir,
985 self._cipd_instance_url)
986 self._cipd_packages = [
987 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
988 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
989 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
990 ]
Edward Lemur979fa782019-08-13 22:44:05 +0000991 mock.patch('tempfile.mkdtemp', lambda: self._workdir).start()
992 mock.patch('gclient_scm.CipdRoot.add_package').start()
993 mock.patch('gclient_scm.CipdRoot.clobber').start()
Dan Le Febvre456d0852023-05-24 23:43:40 +0000994 mock.patch('gclient_scm.CipdRoot.ensure_file_resolve').start()
Edward Lemur979fa782019-08-13 22:44:05 +0000995 mock.patch('gclient_scm.CipdRoot.ensure').start()
996 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000997 self.addCleanup(gclient_utils.rmtree, self._cipd_root_dir)
998 self.addCleanup(gclient_utils.rmtree, self._workdir)
John Budorick0f7b2002018-01-19 15:46:17 -0800999
1000 def createScmWithPackageThatSatisfies(self, condition):
1001 return gclient_scm.CipdWrapper(
1002 url=self._cipd_instance_url,
1003 root_dir=self._cipd_root_dir,
1004 relpath='fake_relpath',
1005 root=self._cipd_root,
1006 package=self.getPackageThatSatisfies(condition))
1007
1008 def getPackageThatSatisfies(self, condition):
1009 for p in self._cipd_packages:
1010 if condition(p):
1011 return p
1012
1013 self.fail('Unable to find a satisfactory package.')
1014
John Budorick0f7b2002018-01-19 15:46:17 -08001015 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -07001016 """Checks that revert does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +00001017 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -08001018 scm.revert(None, (), [])
1019
Edward Lemur979fa782019-08-13 22:44:05 +00001020 @mock.patch('gclient_scm.gclient_utils.CheckCallAndFilter')
1021 @mock.patch('gclient_scm.gclient_utils.rmtree')
1022 def testRevinfo(self, mockRmtree, mockCheckCallAndFilter):
John Budorick0f7b2002018-01-19 15:46:17 -08001023 """Checks that revinfo uses the JSON from cipd describe."""
1024 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
1025
1026 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
1027 json_contents = {
1028 'result': {
1029 'pin': {
1030 'instance_id': expected_revinfo,
1031 }
1032 }
1033 }
1034 describe_json_path = join(self._workdir, 'describe.json')
1035 with open(describe_json_path, 'w') as describe_json:
1036 json.dump(json_contents, describe_json)
1037
Edward Lemur979fa782019-08-13 22:44:05 +00001038 revinfo = scm.revinfo(None, (), [])
1039 self.assertEqual(revinfo, expected_revinfo)
1040
1041 mockRmtree.assert_called_with(self._workdir)
1042 mockCheckCallAndFilter.assert_called_with([
John Budorick0f7b2002018-01-19 15:46:17 -08001043 'cipd', 'describe', 'foo_package',
1044 '-log-level', 'error',
1045 '-version', 'foo_version',
1046 '-json-output', describe_json_path,
Edward Lemur979fa782019-08-13 22:44:05 +00001047 ])
John Budorick0f7b2002018-01-19 15:46:17 -08001048
1049 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -07001050 """Checks that update does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +00001051 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -08001052 scm.update(None, (), [])
1053
1054
Edward Lesmes8073a502020-04-15 02:11:14 +00001055class BranchHeadsFakeRepo(fake_repos.FakeReposBase):
1056 def populateGit(self):
1057 # Creates a tree that looks like this:
1058 #
1059 # 5 refs/branch-heads/5
1060 # |
1061 # 4
1062 # |
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001063 # 1--2--3 refs/heads/main
Edward Lesmes8073a502020-04-15 02:11:14 +00001064 self._commit_git('repo_1', {'commit 1': 'touched'})
1065 self._commit_git('repo_1', {'commit 2': 'touched'})
1066 self._commit_git('repo_1', {'commit 3': 'touched'})
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001067 self._create_ref('repo_1', 'refs/heads/main', 3)
Edward Lesmes8073a502020-04-15 02:11:14 +00001068
1069 self._commit_git('repo_1', {'commit 4': 'touched'}, base=2)
1070 self._commit_git('repo_1', {'commit 5': 'touched'}, base=2)
1071 self._create_ref('repo_1', 'refs/branch-heads/5', 5)
1072
1073
1074class BranchHeadsTest(fake_repos.FakeReposTestBase):
1075 FAKE_REPOS_CLASS = BranchHeadsFakeRepo
1076
1077 def setUp(self):
1078 super(BranchHeadsTest, self).setUp()
1079 self.enabled = self.FAKE_REPOS.set_up_git()
1080 self.options = BaseGitWrapperTestCase.OptionsObject()
1081 self.url = self.git_base + 'repo_1'
1082 self.mirror = None
Ben Pastened410c662020-08-26 17:07:03 +00001083 mock.patch('sys.stdout', StringIO()).start()
Edward Lesmes8073a502020-04-15 02:11:14 +00001084 self.addCleanup(mock.patch.stopall)
1085
1086 def setUpMirror(self):
1087 self.mirror = tempfile.mkdtemp('mirror')
1088 git_cache.Mirror.SetCachePath(self.mirror)
1089 self.addCleanup(gclient_utils.rmtree, self.mirror)
1090 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1091
1092 def testCheckoutBranchHeads(self):
1093 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1094 file_list = []
1095
1096 self.options.revision = 'refs/branch-heads/5'
1097 scm.update(self.options, None, file_list)
1098 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1099
1100 def testCheckoutUpdatedBranchHeads(self):
1101 # Travel back in time, and set refs/branch-heads/5 to its parent.
1102 subprocess2.check_call(
1103 ['git', 'update-ref', 'refs/branch-heads/5', self.githash('repo_1', 4)],
1104 cwd=self.url)
1105
1106 # Sync to refs/branch-heads/5
1107 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1108 self.options.revision = 'refs/branch-heads/5'
1109 scm.update(self.options, None, [])
1110
1111 # Set refs/branch-heads/5 back to its original value.
1112 subprocess2.check_call(
1113 ['git', 'update-ref', 'refs/branch-heads/5', self.githash('repo_1', 5)],
1114 cwd=self.url)
1115
1116 # Attempt to sync to refs/branch-heads/5 again.
1117 self.testCheckoutBranchHeads()
1118
1119 def testCheckoutBranchHeadsMirror(self):
1120 self.setUpMirror()
1121 self.testCheckoutBranchHeads()
1122
1123 def testCheckoutUpdatedBranchHeadsMirror(self):
1124 self.setUpMirror()
1125 self.testCheckoutUpdatedBranchHeads()
1126
1127
Edward Lemurd64781e2018-07-11 23:09:55 +00001128class GerritChangesFakeRepo(fake_repos.FakeReposBase):
1129 def populateGit(self):
1130 # Creates a tree that looks like this:
1131 #
Edward Lemurca7d8812018-07-24 17:42:45 +00001132 # 6 refs/changes/35/1235/1
1133 # |
1134 # 5 refs/changes/34/1234/1
1135 # |
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001136 # 1--2--3--4 refs/heads/main
Edward Lemurca7d8812018-07-24 17:42:45 +00001137 # | |
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001138 # | 11(5)--12 refs/heads/main-with-5
Edward Lemurca7d8812018-07-24 17:42:45 +00001139 # |
1140 # 7--8--9 refs/heads/feature
1141 # |
1142 # 10 refs/changes/36/1236/1
1143 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001144
1145 self._commit_git('repo_1', {'commit 1': 'touched'})
1146 self._commit_git('repo_1', {'commit 2': 'touched'})
1147 self._commit_git('repo_1', {'commit 3': 'touched'})
1148 self._commit_git('repo_1', {'commit 4': 'touched'})
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001149 self._create_ref('repo_1', 'refs/heads/main', 4)
Edward Lemurd64781e2018-07-11 23:09:55 +00001150
1151 # Create a change on top of commit 3 that consists of two commits.
1152 self._commit_git('repo_1',
1153 {'commit 5': 'touched',
1154 'change': '1234'},
1155 base=3)
1156 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1157 self._commit_git('repo_1',
1158 {'commit 6': 'touched',
1159 'change': '1235'})
1160 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1161
Edward Lemurca7d8812018-07-24 17:42:45 +00001162 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1163 # commits.
1164 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1165 self._commit_git('repo_1', {'commit 8': 'touched'})
1166 self._commit_git('repo_1', {'commit 9': 'touched'})
1167 self._create_ref('repo_1', 'refs/heads/feature', 9)
1168
1169 # Create a change of top of commit 8.
1170 self._commit_git('repo_1',
1171 {'commit 10': 'touched',
1172 'change': '1236'},
1173 base=8)
1174 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1175
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001176 # Create a refs/heads/main-with-5 on top of commit 3 which is a branch
Edward Lemurca7d8812018-07-24 17:42:45 +00001177 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1178 self._commit_git('repo_1',
1179 # This is really commit 11, but has the changes of commit 5
1180 {'commit 5': 'touched',
1181 'change': '1234'},
1182 base=3)
1183 self._commit_git('repo_1', {'commit 12': 'touched'})
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001184 self._create_ref('repo_1', 'refs/heads/main-with-5', 12)
Edward Lemurca7d8812018-07-24 17:42:45 +00001185
Edward Lemurd64781e2018-07-11 23:09:55 +00001186
1187class GerritChangesTest(fake_repos.FakeReposTestBase):
1188 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1189
1190 def setUp(self):
1191 super(GerritChangesTest, self).setUp()
1192 self.enabled = self.FAKE_REPOS.set_up_git()
1193 self.options = BaseGitWrapperTestCase.OptionsObject()
1194 self.url = self.git_base + 'repo_1'
1195 self.mirror = None
Ben Pastened410c662020-08-26 17:07:03 +00001196 mock.patch('sys.stdout', StringIO()).start()
Edward Lesmese79107e2019-10-25 22:47:33 +00001197 self.addCleanup(mock.patch.stopall)
Edward Lemurd64781e2018-07-11 23:09:55 +00001198
1199 def setUpMirror(self):
1200 self.mirror = tempfile.mkdtemp()
1201 git_cache.Mirror.SetCachePath(self.mirror)
Edward Lesmese79107e2019-10-25 22:47:33 +00001202 self.addCleanup(gclient_utils.rmtree, self.mirror)
Edward Lemurd64781e2018-07-11 23:09:55 +00001203 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1204
Edward Lemurca7d8812018-07-24 17:42:45 +00001205 def assertCommits(self, commits):
1206 """Check that all, and only |commits| are present in the current checkout.
1207 """
1208 for i in commits:
1209 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001210 self.assertTrue(os.path.exists(name), 'Commit not found: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001211
1212 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1213 for i in all_commits - set(commits):
1214 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001215 self.assertFalse(os.path.exists(name), 'Unexpected commit: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001216
Edward Lemurd64781e2018-07-11 23:09:55 +00001217 def testCanCloneGerritChange(self):
1218 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1219 file_list = []
1220
1221 self.options.revision = 'refs/changes/35/1235/1'
1222 scm.update(self.options, None, file_list)
1223 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1224
1225 def testCanSyncToGerritChange(self):
1226 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1227 file_list = []
1228
1229 self.options.revision = self.githash('repo_1', 1)
1230 scm.update(self.options, None, file_list)
1231 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1232
1233 self.options.revision = 'refs/changes/35/1235/1'
1234 scm.update(self.options, None, file_list)
1235 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1236
1237 def testCanCloneGerritChangeMirror(self):
1238 self.setUpMirror()
1239 self.testCanCloneGerritChange()
1240
1241 def testCanSyncToGerritChangeMirror(self):
1242 self.setUpMirror()
1243 self.testCanSyncToGerritChange()
1244
Edward Lesmese79107e2019-10-25 22:47:33 +00001245 def testMirrorPushUrl(self):
1246 self.setUpMirror()
1247
1248 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1249 file_list = []
1250 self.assertIsNotNone(scm._GetMirror(self.url, self.options))
1251
1252 scm.update(self.options, None, file_list)
1253
1254 fetch_url = scm._Capture(['remote', 'get-url', 'origin'])
1255 self.assertTrue(
1256 fetch_url.startswith(self.mirror),
1257 msg='\n'.join([
1258 'Repository fetch url should be in the git cache mirror directory.',
1259 ' fetch_url: %s' % fetch_url,
1260 ' mirror: %s' % self.mirror]))
1261 push_url = scm._Capture(['remote', 'get-url', '--push', 'origin'])
1262 self.assertEqual(push_url, self.url)
1263
Edward Lemurca7d8812018-07-24 17:42:45 +00001264 def testAppliesPatchOnTopOfMasterByDefault(self):
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001265 """Test the default case, where we apply a patch on top of main."""
Edward Lemurca7d8812018-07-24 17:42:45 +00001266 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1267 file_list = []
1268
1269 # Make sure we don't specify a revision.
1270 self.options.revision = None
1271 scm.update(self.options, None, file_list)
1272 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1273
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001274 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001275 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001276 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001277
1278 self.assertCommits([1, 2, 3, 4, 5, 6])
1279 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1280
1281 def testCheckoutOlderThanPatchBase(self):
1282 """Test applying a patch on an old checkout.
1283
1284 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1285 contains commits 5 and 6, and is based on top of commit 3.
1286 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1287 """
1288 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1289 file_list = []
1290
1291 # Sync to commit 1
1292 self.options.revision = self.githash('repo_1', 1)
1293 scm.update(self.options, None, file_list)
1294 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1295
1296 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001297 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001298 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001299 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001300
1301 self.assertCommits([1, 5, 6])
1302 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1303
1304 def testCheckoutOriginFeature(self):
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001305 """Tests that we can apply a patch on a branch other than main."""
Edward Lemurca7d8812018-07-24 17:42:45 +00001306 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1307 file_list = []
1308
Edward Lemur8c665652019-05-08 20:23:33 +00001309 # Sync to remote's refs/heads/feature
1310 self.options.revision = 'refs/heads/feature'
Edward Lemurca7d8812018-07-24 17:42:45 +00001311 scm.update(self.options, None, file_list)
1312 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1313
1314 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001315 scm.apply_patch_ref(
1316 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1317 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001318
1319 self.assertCommits([1, 2, 7, 8, 9, 10])
1320 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1321
1322 def testCheckoutOriginFeatureOnOldRevision(self):
1323 """Tests that we can apply a patch on an old checkout, on a branch other
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001324 than main."""
Edward Lemurca7d8812018-07-24 17:42:45 +00001325 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1326 file_list = []
1327
Edward Lemur8c665652019-05-08 20:23:33 +00001328 # Sync to remote's refs/heads/feature on an old revision
Edward Lemurca7d8812018-07-24 17:42:45 +00001329 self.options.revision = self.githash('repo_1', 7)
1330 scm.update(self.options, None, file_list)
1331 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1332
1333 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001334 scm.apply_patch_ref(
1335 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1336 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001337
1338 # We shouldn't have rebased on top of 2 (which is the merge base between
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001339 # remote's main branch and the change) but on top of 7 (which is the
Edward Lemur8c665652019-05-08 20:23:33 +00001340 # merge base between remote's feature branch and the change).
Edward Lemurca7d8812018-07-24 17:42:45 +00001341 self.assertCommits([1, 2, 7, 10])
1342 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1343
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001344 def testCheckoutOriginFeaturePatchBranch(self):
1345 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1346 file_list = []
1347
Edward Lemur8c665652019-05-08 20:23:33 +00001348 # Sync to the hash instead of remote's refs/heads/feature.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001349 self.options.revision = self.githash('repo_1', 9)
1350 scm.update(self.options, None, file_list)
1351 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1352
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001353 # Apply refs/changes/34/1234/1, created for remote's main branch on top of
Edward Lemur8c665652019-05-08 20:23:33 +00001354 # remote's feature branch.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001355 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001356 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001357 file_list)
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001358
1359 # Commits 5 and 6 are part of the patch, and commits 1, 2, 7, 8 and 9 are
Edward Lemur8c665652019-05-08 20:23:33 +00001360 # part of remote's feature branch.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001361 self.assertCommits([1, 2, 5, 6, 7, 8, 9])
1362 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001363
1364 def testDoesntRebasePatchMaster(self):
1365 """Tests that we can apply a patch without rebasing it.
1366 """
1367 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1368 file_list = []
1369
1370 self.options.rebase_patch_ref = False
1371 scm.update(self.options, None, file_list)
1372 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1373
1374 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001375 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001376 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001377 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001378
1379 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001380 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001381
1382 def testDoesntRebasePatchOldCheckout(self):
1383 """Tests that we can apply a patch without rebasing it on an old checkout.
1384 """
1385 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1386 file_list = []
1387
1388 # Sync to commit 1
1389 self.options.revision = self.githash('repo_1', 1)
1390 self.options.rebase_patch_ref = False
1391 scm.update(self.options, None, file_list)
1392 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1393
1394 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001395 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001396 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001397 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001398
1399 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001400 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001401
1402 def testDoesntSoftResetIfNotAskedTo(self):
1403 """Test that we can apply a patch without doing a soft reset."""
1404 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1405 file_list = []
1406
1407 self.options.reset_patch_ref = False
1408 scm.update(self.options, None, file_list)
1409 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1410
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001411 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001412 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001413 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001414
1415 self.assertCommits([1, 2, 3, 4, 5, 6])
1416 # The commit hash after cherry-picking is not known, but it must be
1417 # different from what the repo was synced at before patching.
1418 self.assertNotEqual(self.githash('repo_1', 4),
1419 self.gitrevparse(self.root_dir))
1420
Ravi Mistryecda7822022-02-28 16:22:20 +00001421 @mock.patch('gerrit_util.GetChange', return_value={'topic': 'test_topic'})
1422 @mock.patch('gerrit_util.QueryChanges', return_value=[
1423 {'_number': 1234},
1424 {'_number': 1235, 'current_revision': 'abc',
1425 'revisions': {'abc': {'ref': 'refs/changes/35/1235/1'}}}])
1426 def testDownloadTopics(self, query_changes_mock, get_change_mock):
1427 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1428 file_list = []
1429
1430 self.options.revision = 'refs/changes/34/1234/1'
1431 scm.update(self.options, None, file_list)
1432 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1433
1434 # pylint: disable=attribute-defined-outside-init
1435 self.options.download_topics = True
Ravi Mistryc848a4e2022-03-10 18:19:59 +00001436 scm.url = 'https://test-repo.googlesource.com/repo_1.git'
Ravi Mistryecda7822022-02-28 16:22:20 +00001437 scm.apply_patch_ref(
1438 self.url, 'refs/changes/34/1234/1', 'refs/heads/main', self.options,
1439 file_list)
1440
1441 get_change_mock.assert_called_once_with(
1442 mock.ANY, '1234')
1443 query_changes_mock.assert_called_once_with(
1444 mock.ANY,
1445 [('topic', 'test_topic'), ('status', 'open'), ('repo', 'repo_1')],
1446 o_params=['ALL_REVISIONS'])
1447
1448 self.assertCommits([1, 2, 3, 5, 6])
1449 # The commit hash after the two cherry-picks is not known, but it must be
1450 # different from what the repo was synced at before patching.
1451 self.assertNotEqual(self.githash('repo_1', 4),
1452 self.gitrevparse(self.root_dir))
1453
Edward Lemurca7d8812018-07-24 17:42:45 +00001454 def testRecoversAfterPatchFailure(self):
1455 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1456 file_list = []
1457
1458 self.options.revision = 'refs/changes/34/1234/1'
1459 scm.update(self.options, None, file_list)
1460 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1461
1462 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1463 # patch 'refs/changes/36/1236/1' creates a patch failure.
1464 with self.assertRaises(subprocess2.CalledProcessError) as cm:
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001465 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001466 self.url, 'refs/changes/36/1236/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001467 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001468 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
Edward Lemur979fa782019-08-13 22:44:05 +00001469 self.assertIn(b'error: could not apply', cm.exception.stderr)
Edward Lemurca7d8812018-07-24 17:42:45 +00001470
1471 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1472 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001473 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001474 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001475 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001476 self.assertCommits([1, 2, 3, 5, 6])
1477 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1478
1479 def testIgnoresAlreadyMergedCommits(self):
1480 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1481 file_list = []
1482
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001483 self.options.revision = 'refs/heads/main-with-5'
Edward Lemurca7d8812018-07-24 17:42:45 +00001484 scm.update(self.options, None, file_list)
1485 self.assertEqual(self.githash('repo_1', 12),
1486 self.gitrevparse(self.root_dir))
1487
1488 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1489 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1490 # already present in the tree as commit 11.
1491 # Make sure we deal with this gracefully.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001492 scm.apply_patch_ref(
1493 self.url, 'refs/changes/35/1235/1', 'refs/heads/feature', self.options,
1494 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001495 self.assertCommits([1, 2, 3, 5, 6, 12])
1496 self.assertEqual(self.githash('repo_1', 12),
1497 self.gitrevparse(self.root_dir))
1498
1499 def testRecoversFromExistingCherryPick(self):
1500 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1501 file_list = []
1502
1503 self.options.revision = 'refs/changes/34/1234/1'
1504 scm.update(self.options, None, file_list)
1505 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1506
1507 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1508 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1509 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1510 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1511 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1512 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1513
1514 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1515 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001516 scm.apply_patch_ref(
Josip Sokcevic7e133ff2021-07-13 17:44:53 +00001517 self.url, 'refs/changes/35/1235/1', 'refs/heads/main', self.options,
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001518 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001519 self.assertCommits([1, 2, 3, 5, 6])
1520 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1521
Edward Lemurd64781e2018-07-11 23:09:55 +00001522
Joanna Wang5a7c8242022-07-01 19:09:00 +00001523class DepsChangesFakeRepo(fake_repos.FakeReposBase):
1524 def populateGit(self):
1525 self._commit_git('repo_1', {'DEPS': 'versionA', 'doesnotmatter': 'B'})
1526 self._commit_git('repo_1', {'DEPS': 'versionA', 'doesnotmatter': 'C'})
1527
1528 self._commit_git('repo_1', {'DEPS': 'versionB'})
1529 self._commit_git('repo_1', {'DEPS': 'versionA', 'doesnotmatter': 'C'})
1530 self._create_ref('repo_1', 'refs/heads/main', 4)
1531
1532
1533class CheckDiffTest(fake_repos.FakeReposTestBase):
1534 FAKE_REPOS_CLASS = DepsChangesFakeRepo
1535
1536 def setUp(self):
1537 super(CheckDiffTest, self).setUp()
1538 self.enabled = self.FAKE_REPOS.set_up_git()
1539 self.options = BaseGitWrapperTestCase.OptionsObject()
1540 self.url = self.git_base + 'repo_1'
1541 self.mirror = None
1542 mock.patch('sys.stdout', StringIO()).start()
1543 self.addCleanup(mock.patch.stopall)
1544
1545 def setUpMirror(self):
1546 self.mirror = tempfile.mkdtemp()
1547 git_cache.Mirror.SetCachePath(self.mirror)
1548 self.addCleanup(gclient_utils.rmtree, self.mirror)
1549 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1550
1551 def testCheckDiff(self):
1552 """Correctly check for diffs."""
1553 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1554 file_list = []
1555
1556 # Make sure we don't specify a revision.
1557 self.options.revision = None
1558 scm.update(self.options, None, file_list)
1559 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1560
1561 self.assertFalse(scm.check_diff(self.githash('repo_1', 1), files=['DEPS']))
1562 self.assertTrue(scm.check_diff(self.githash('repo_1', 1)))
1563 self.assertTrue(scm.check_diff(self.githash('repo_1', 3), files=['DEPS']))
1564
1565 self.assertFalse(
1566 scm.check_diff(self.githash('repo_1', 2),
1567 files=['DEPS', 'doesnotmatter']))
1568 self.assertFalse(scm.check_diff(self.githash('repo_1', 2)))
1569
1570
Joanna Wang1a977bd2022-06-02 21:51:17 +00001571if 'unittest.util' in __import__('sys').modules:
1572 # Show full diff in self.assertEqual.
1573 __import__('sys').modules['unittest.util']._MAX_LENGTH = 999999999
1574
msb@chromium.orge28e4982009-09-25 20:51:45 +00001575if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001576 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1577 logging.basicConfig(
1578 level=level,
1579 format='%(asctime).19s %(levelname)s %(filename)s:'
1580 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001581 unittest.main()
1582
1583# vim: ts=2:sw=2:tw=80:et: