Detect cherry-picked changes without Change-Id
Support detecting simple cherry-picking of changes to support use with repositories that are not using Gerrit/git-review. Refactor the searcher list method to emulate git-rebase behaviour in looking at a single side of the changes between two revisions and using '--cherry-pick' to drop commits with the same patch id. Change-Id: I75b0086203ea614c4c58873292c8dbf9eca00f64
This commit is contained in:
@@ -126,11 +126,13 @@ class Searcher(GitMixin):
|
|||||||
return mergecommit, ["^%s" % ip
|
return mergecommit, ["^%s" % ip
|
||||||
for ip in mergecommit.parents if ip != parent]
|
for ip in mergecommit.parents if ip != parent]
|
||||||
|
|
||||||
def list(self):
|
def list(self, upstream=None):
|
||||||
"""
|
"""
|
||||||
Returns a list of Commit objects, between the '<commitish>' revision
|
Returns a list of Commit objects, between the '<commitish>' revision
|
||||||
given in the constructor, and the commit object returned by the find()
|
given in the constructor, and the commit object returned by the find()
|
||||||
method.
|
method. If given an upstream branch, uses --cherry-pick/--left-only to
|
||||||
|
exclude commits that are identical to those already on the upstream
|
||||||
|
branch.
|
||||||
"""
|
"""
|
||||||
if not self.commit:
|
if not self.commit:
|
||||||
self.find()
|
self.find()
|
||||||
@@ -182,34 +184,52 @@ class Searcher(GitMixin):
|
|||||||
# the tip of the head to avoid inversion where older commits
|
# the tip of the head to avoid inversion where older commits
|
||||||
# started before the previous import merge and approved afterwards
|
# started before the previous import merge and approved afterwards
|
||||||
# are not sorted by 'rev-list' predictably.
|
# are not sorted by 'rev-list' predictably.
|
||||||
if previous_import:
|
|
||||||
search_list = [
|
|
||||||
(previous_import, self.branch),
|
|
||||||
(self.commit, previous_import),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
search_list = [(self.commit, self.branch)]
|
|
||||||
|
|
||||||
commit_list = []
|
commit_list = []
|
||||||
extra_args.append('--')
|
if upstream is None:
|
||||||
for start, end in search_list:
|
if previous_import:
|
||||||
revision_spec = "{0}..{1}".format(start, end)
|
search_list = [
|
||||||
|
(previous_import, self.branch, None),
|
||||||
|
(self.commit, previous_import, None),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
search_list = [(self.commit, self.branch, None)]
|
||||||
|
rev_spec = "{0}..{1}"
|
||||||
|
git_args = {}
|
||||||
|
else:
|
||||||
|
if previous_import:
|
||||||
|
search_list = [
|
||||||
|
(self.branch, upstream, "^%s" % previous_import),
|
||||||
|
(previous_import, upstream, "^%s~1" % previous_import)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
search_list = [(self.branch, upstream, None)]
|
||||||
|
rev_spec = "{0}...{1}"
|
||||||
|
git_args = {'cherry_pick': True, 'left_only': True}
|
||||||
|
extra_args.append("^%s" % self.commit)
|
||||||
|
|
||||||
|
for start, end, exclude in search_list:
|
||||||
|
extra = list(extra_args)
|
||||||
|
if exclude:
|
||||||
|
extra.append(exclude)
|
||||||
|
extra.append("--")
|
||||||
|
revision_spec = rev_spec.format(start, end)
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"""
|
"""
|
||||||
Walking the changes between found commit and target, excluding
|
Walking the changes between found commit and target, excluding
|
||||||
those behind the previous import or merged as an additional
|
those behind the previous import or merged as an additional
|
||||||
branch during the previous import
|
branch during the previous import
|
||||||
git rev-list --topo-order %s %s
|
git rev-list --topo-order %s %s %s
|
||||||
""", revision_spec, " ".join(extra_args))
|
""", self.git.transform_kwargs(git_args), revision_spec,
|
||||||
|
" ".join(extra))
|
||||||
|
|
||||||
commit_list.append(
|
commit_list.append(
|
||||||
Commit._iter_from_process_or_stream(
|
Commit._iter_from_process_or_stream(
|
||||||
self.repo,
|
self.repo,
|
||||||
self.git.rev_list(revision_spec,
|
self.git.rev_list(revision_spec,
|
||||||
*extra_args,
|
*extra,
|
||||||
as_process=True,
|
as_process=True,
|
||||||
topo_order=True)))
|
topo_order=True,
|
||||||
|
**git_args)))
|
||||||
|
|
||||||
# chain the filters as generators so that we don't need to allocate new
|
# chain the filters as generators so that we don't need to allocate new
|
||||||
# lists for each step in the filter chain.
|
# lists for each step in the filter chain.
|
||||||
@@ -444,7 +464,7 @@ class CommitMessageSearcher(LogDedentMixin, Searcher):
|
|||||||
|
|
||||||
return self.commit.hexsha
|
return self.commit.hexsha
|
||||||
|
|
||||||
def list(self, include=True):
|
def list(self, upstream=None, include=True):
|
||||||
"""
|
"""
|
||||||
Override parent implementation to permit inclusion of the found commit
|
Override parent implementation to permit inclusion of the found commit
|
||||||
to be returned in the list of changes. This will help in cases where
|
to be returned in the list of changes. This will help in cases where
|
||||||
@@ -452,7 +472,7 @@ class CommitMessageSearcher(LogDedentMixin, Searcher):
|
|||||||
branches that would be returned by the generic upstream searcher.
|
branches that would be returned by the generic upstream searcher.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
commits = super(CommitMessageSearcher, self).list()
|
commits = super(CommitMessageSearcher, self).list(upstream)
|
||||||
if include:
|
if include:
|
||||||
commits.append(self.commit)
|
commits.append(self.commit)
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class LocateChangesStrategy(GitMixin, Sequence):
|
|||||||
|
|
||||||
def _popdata(self):
|
def _popdata(self):
|
||||||
"""Should return the list of commits from the searcher object"""
|
"""Should return the list of commits from the searcher object"""
|
||||||
return self.searcher.list()
|
return self.searcher.list(upstream=self.upstream)
|
||||||
|
|
||||||
|
|
||||||
class LocateChangesWalk(LocateChangesStrategy):
|
class LocateChangesWalk(LocateChangesStrategy):
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2016 Hewlett Packard Enterprise Development LP
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
---
|
||||||
|
- desc: |
|
||||||
|
Test if searcher handles simple cherry picks
|
||||||
|
|
||||||
|
Checks that importing an upstream where all local changes have
|
||||||
|
gone upstream, will spot that the commits are identical using
|
||||||
|
cherry-pick mechanism of git rev-list.
|
||||||
|
|
||||||
|
C---D local/master
|
||||||
|
/
|
||||||
|
A---B---E---C1--D1 upstream/master
|
||||||
|
|
||||||
|
tree:
|
||||||
|
- [A, []]
|
||||||
|
- [B, [A]]
|
||||||
|
- [C, [B]]
|
||||||
|
- [D, [C]]
|
||||||
|
- [C1, [E]]
|
||||||
|
- [D1, [C1]]
|
||||||
|
- [E, [B]]
|
||||||
|
|
||||||
|
branches:
|
||||||
|
head: [master, D]
|
||||||
|
upstream: [upstream/master, D1]
|
||||||
|
|
||||||
|
expected_changes: []
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2016 Hewlett Packard Enterprise Development LP
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
---
|
||||||
|
- desc: |
|
||||||
|
Test cherry-picked changes found with complex layout
|
||||||
|
|
||||||
|
Construct a complex repo layout which contains additional include branches
|
||||||
|
and a previous import from upstream. Test that cherry-picked changes are
|
||||||
|
correctly spotted having been upstreamed and removed from the set to be
|
||||||
|
rebased.
|
||||||
|
|
||||||
|
Repository layout being tested
|
||||||
|
|
||||||
|
B--
|
||||||
|
\ \
|
||||||
|
\ \ L----------------
|
||||||
|
\ \ / \
|
||||||
|
C---D---E-------I---J---K1--M---N master
|
||||||
|
/ \ /
|
||||||
|
/ --H---D1--E1
|
||||||
|
/ /
|
||||||
|
A---F---G---D2--E2--K upstream/master
|
||||||
|
|
||||||
|
tree:
|
||||||
|
- [A, []]
|
||||||
|
- [B, []]
|
||||||
|
- [C, [A, B]]
|
||||||
|
- [D, [C]]
|
||||||
|
- [D1, [H]]
|
||||||
|
- [D2, [G]]
|
||||||
|
- [E, [D]]
|
||||||
|
- [E1, [D1]]
|
||||||
|
- [E2, [D2]]
|
||||||
|
- [F, [A]]
|
||||||
|
- [G, [F]]
|
||||||
|
- [H, [G, B]]
|
||||||
|
- [I, [E, =E1]]
|
||||||
|
- [J, [I]]
|
||||||
|
- [K, [E2]]
|
||||||
|
- [K1, [J]]
|
||||||
|
- [L, [E]]
|
||||||
|
- [M, [K1, L]]
|
||||||
|
- [N, [M]]
|
||||||
|
|
||||||
|
branches:
|
||||||
|
head: [master, N]
|
||||||
|
upstream: [upstream/master, K]
|
||||||
|
|
||||||
|
expected_changes: [H, I, J, L, M, N]
|
||||||
@@ -52,4 +52,4 @@ class TestUpstreamMergeBaseSearcher(TestWithScenarios, BaseTestCase):
|
|||||||
patterns=pattern, repo=self.repo)
|
patterns=pattern, repo=self.repo)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.gittree._commits_from_nodes(reversed(self.expected_changes)),
|
self.gittree._commits_from_nodes(reversed(self.expected_changes)),
|
||||||
searcher.list())
|
searcher.list(self.branches['upstream'][0]))
|
||||||
|
|||||||
Reference in New Issue
Block a user