mirror of
https://chromium.googlesource.com/chromium/tools/depot_tools.git
synced 2026-01-11 18:51:29 +00:00
[stacked_changes] Create cherry-picked commit.
Bug:b/265929888 Change-Id: I4277474c1f09e4ac6ea6ebb5d9d340f22365f542 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4178924 Reviewed-by: Gavin Mak <gavinmak@google.com> Reviewed-by: Josip Sokcevic <sokcevic@chromium.org> Commit-Queue: Joanna Wang <jojwang@chromium.org>
This commit is contained in:
41
git_cl.py
41
git_cl.py
@@ -999,6 +999,12 @@ _CommentSummary = collections.namedtuple(
|
|||||||
'approval', 'disapproval'])
|
'approval', 'disapproval'])
|
||||||
|
|
||||||
|
|
||||||
|
_NewUpload = collections.namedtuple('NewUpload', [
|
||||||
|
'reviewers', 'ccs', 'commit_to_push', 'new_last_uploaded_commit',
|
||||||
|
'change_desc'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class Changelist(object):
|
class Changelist(object):
|
||||||
"""Changelist works with one changelist in local branch.
|
"""Changelist works with one changelist in local branch.
|
||||||
|
|
||||||
@@ -1569,6 +1575,41 @@ class Changelist(object):
|
|||||||
return title
|
return title
|
||||||
return user_title or title
|
return user_title or title
|
||||||
|
|
||||||
|
def PrepareCherryPickSquashedCommit(self, options):
|
||||||
|
# type: (optparse.Values) -> _NewUpload()
|
||||||
|
"""Create a commit cherry-picked on parent to push."""
|
||||||
|
|
||||||
|
parent = self.GetCommonAncestorWithUpstream()
|
||||||
|
reviewers, ccs, change_desc = self._PrepareChange(options, parent,
|
||||||
|
self.branchref)
|
||||||
|
|
||||||
|
new_upload_hash = RunGit(['rev-parse', self.branchref]).strip()
|
||||||
|
latest_tree = RunGit(['rev-parse', self.branchref + ':']).strip()
|
||||||
|
with gclient_utils.temporary_file() as desc_tempfile:
|
||||||
|
gclient_utils.FileWrite(desc_tempfile, change_desc.description)
|
||||||
|
commit_to_cp = RunGit(
|
||||||
|
['commit-tree', latest_tree, '-p', parent, '-F',
|
||||||
|
desc_tempfile]).strip()
|
||||||
|
|
||||||
|
_, upstream_branch_ref = self.FetchUpstreamTuple(self.GetBranch())
|
||||||
|
|
||||||
|
upstream_branch = scm.GIT.ShortBranchName(upstream_branch_ref)
|
||||||
|
upstream_squashed_upload = scm.GIT.GetBranchConfig(
|
||||||
|
settings.GetRoot(), upstream_branch, GERRIT_SQUASH_HASH_CONFIG_KEY)
|
||||||
|
|
||||||
|
RunGit(['checkout', '-q', upstream_squashed_upload])
|
||||||
|
ret, _out = RunGitWithCode(['cherry-pick', commit_to_cp])
|
||||||
|
if ret:
|
||||||
|
RunGit(['cherry-pick', '--abort'])
|
||||||
|
RunGit(['checkout', '-q', self.branch])
|
||||||
|
DieWithError('Could not cleanly cherry-pick')
|
||||||
|
|
||||||
|
commit_to_push = RunGit(['rev-parse', 'HEAD'])
|
||||||
|
RunGit(['checkout', '-q', self.branch])
|
||||||
|
|
||||||
|
return _NewUpload(reviewers, ccs, commit_to_push, new_upload_hash,
|
||||||
|
change_desc)
|
||||||
|
|
||||||
def _PrepareChange(self, options, parent, end_commit):
|
def _PrepareChange(self, options, parent, end_commit):
|
||||||
# type: (optparse.Values, str, str) ->
|
# type: (optparse.Values, str, str) ->
|
||||||
# Tuple[Sequence[str], Sequence[str], ChangeDescription]
|
# Tuple[Sequence[str], Sequence[str], ChangeDescription]
|
||||||
|
|||||||
@@ -3210,8 +3210,12 @@ class ChangelistTest(unittest.TestCase):
|
|||||||
mock.patch('subprocess2.Popen').start()
|
mock.patch('subprocess2.Popen').start()
|
||||||
mock.patch(
|
mock.patch(
|
||||||
'git_cl.Changelist.GetGerritProject', return_value='project').start()
|
'git_cl.Changelist.GetGerritProject', return_value='project').start()
|
||||||
|
mock.patch('sys.exit', side_effect=SystemExitMock).start()
|
||||||
|
|
||||||
self.addCleanup(mock.patch.stopall)
|
self.addCleanup(mock.patch.stopall)
|
||||||
self.temp_count = 0
|
self.temp_count = 0
|
||||||
|
self.mockGit = GitMocks()
|
||||||
|
mock.patch('scm.GIT.GetConfig', self.mockGit.GetConfig).start()
|
||||||
|
|
||||||
def testRunHook(self):
|
def testRunHook(self):
|
||||||
expected_results = {
|
expected_results = {
|
||||||
@@ -3555,6 +3559,79 @@ class ChangelistTest(unittest.TestCase):
|
|||||||
for user_title in ['not empty', 'yes', 'YES']:
|
for user_title in ['not empty', 'yes', 'YES']:
|
||||||
self.assertEqual(cl._GetTitleForUpload(options), user_title)
|
self.assertEqual(cl._GetTitleForUpload(options), user_title)
|
||||||
|
|
||||||
|
@mock.patch('git_cl.Settings.GetRoot', return_value='')
|
||||||
|
@mock.patch('git_cl.Changelist.FetchUpstreamTuple')
|
||||||
|
@mock.patch('git_cl.RunGitWithCode')
|
||||||
|
@mock.patch('git_cl.RunGit')
|
||||||
|
@mock.patch('git_cl.Changelist._PrepareChange')
|
||||||
|
@mock.patch('git_cl.Changelist.GetCommonAncestorWithUpstream')
|
||||||
|
def testPrepareCherryPickSquashedCommit(self,
|
||||||
|
mockGetCommonAncestorWithUpstream,
|
||||||
|
mockPrepareChange, mockRunGit,
|
||||||
|
mockRunGitWithCode,
|
||||||
|
mockFetchUpstreamTuple, *_mocks):
|
||||||
|
parent_hash = '1a2bparentcommit'
|
||||||
|
mockGetCommonAncestorWithUpstream.return_value = parent_hash
|
||||||
|
|
||||||
|
change_desc = git_cl.ChangeDescription('BOO!')
|
||||||
|
ccs = ['cc@review.cl']
|
||||||
|
reviewers = ['reviewer@review.cl']
|
||||||
|
mockPrepareChange.return_value = (reviewers, ccs, change_desc)
|
||||||
|
|
||||||
|
branchref = 'refs/heads/current-branch'
|
||||||
|
cl = git_cl.Changelist(branchref=branchref)
|
||||||
|
options = optparse.Values()
|
||||||
|
|
||||||
|
mockFetchUpstreamTuple.return_value = ('', 'refs/heads/upstream')
|
||||||
|
|
||||||
|
upstream_gerrit_hash = 'upstream-gerrit-hash'
|
||||||
|
self.mockGit.config['branch.upstream.%s' %
|
||||||
|
git_cl.GERRIT_SQUASH_HASH_CONFIG_KEY] = (
|
||||||
|
upstream_gerrit_hash)
|
||||||
|
|
||||||
|
latest_tree_hash = 'tree-hash'
|
||||||
|
hash_to_cp = 'squashed-hash'
|
||||||
|
hash_to_push = 'hash-to-push'
|
||||||
|
hash_to_save_as_last_upload = 'last-upload'
|
||||||
|
|
||||||
|
def mock_run_git(commands):
|
||||||
|
if commands == ['rev-parse', branchref]:
|
||||||
|
return hash_to_save_as_last_upload
|
||||||
|
if commands == ['rev-parse', branchref + ':']:
|
||||||
|
return latest_tree_hash
|
||||||
|
if {'commit-tree', latest_tree_hash, '-p', parent_hash,
|
||||||
|
'-F'}.issubset(set(commands)):
|
||||||
|
return hash_to_cp
|
||||||
|
if commands == ['rev-parse', 'HEAD']:
|
||||||
|
return hash_to_push
|
||||||
|
|
||||||
|
mockRunGit.side_effect = mock_run_git
|
||||||
|
|
||||||
|
def mock_run_git_with_code(commands):
|
||||||
|
if commands == ['cherry-pick', hash_to_cp]:
|
||||||
|
return 0, ''
|
||||||
|
|
||||||
|
mockRunGitWithCode.side_effect = mock_run_git_with_code
|
||||||
|
|
||||||
|
new_upload = cl.PrepareCherryPickSquashedCommit(options)
|
||||||
|
self.assertEqual(new_upload.reviewers, reviewers)
|
||||||
|
self.assertEqual(new_upload.ccs, ccs)
|
||||||
|
self.assertEqual(new_upload.commit_to_push, hash_to_push)
|
||||||
|
self.assertEqual(new_upload.new_last_uploaded_commit,
|
||||||
|
hash_to_save_as_last_upload)
|
||||||
|
self.assertEqual(new_upload.change_desc, change_desc)
|
||||||
|
|
||||||
|
# Test failed cherry-pick
|
||||||
|
|
||||||
|
def mock_run_git_with_code(commands):
|
||||||
|
if commands == ['cherry-pick', hash_to_cp]:
|
||||||
|
return 1, ''
|
||||||
|
|
||||||
|
mockRunGitWithCode.side_effect = mock_run_git_with_code
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExitMock):
|
||||||
|
cl.PrepareCherryPickSquashedCommit(options)
|
||||||
|
|
||||||
@mock.patch('git_cl.Changelist.GetAffectedFiles', return_value=[])
|
@mock.patch('git_cl.Changelist.GetAffectedFiles', return_value=[])
|
||||||
@mock.patch('git_cl.GenerateGerritChangeId', return_value='1a2b3c')
|
@mock.patch('git_cl.GenerateGerritChangeId', return_value='1a2b3c')
|
||||||
@mock.patch('git_cl.Changelist.GetIssue', return_value=None)
|
@mock.patch('git_cl.Changelist.GetIssue', return_value=None)
|
||||||
|
|||||||
Reference in New Issue
Block a user