Merge "ignore null-merges"
This commit is contained in:
commit
3b2b36fede
|
@ -284,6 +284,22 @@ The following options are configurable:
|
|||
order in which the final report will be generated. A prelude section will
|
||||
always be automatically inserted before the first element of this list.
|
||||
|
||||
`ignore_null_merges`
|
||||
|
||||
OpenStack used to use null-merges to bring final release tags from
|
||||
stable branches back into the master branch. This confuses the
|
||||
regular traversal because it makes that stable branch appear to be
|
||||
part of master and/or the later stable branch. This option allows us
|
||||
to ignore those.
|
||||
|
||||
When this option is set to True, any merge commits with no changes
|
||||
and in which the second or later parent is tagged are considered
|
||||
"null-merges" that bring the tag information into the current branch
|
||||
but nothing else.
|
||||
|
||||
Defaults to ``True``.
|
||||
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
By default, reno now ignores "null" merge commits that bring in
|
||||
tags from other threads. The new configuration option
|
||||
``ignore_null_merges`` controls this behavior. Setting the flag to
|
||||
False restores the previous behavior in which the null-merge
|
||||
commits were traversed like any other merge commit.
|
||||
upgrade:
|
||||
- |
|
||||
The new configuration option ``ignore_null_merges`` causes the
|
||||
scanner to ignore merge commits with no changes when one of the
|
||||
parents being merged in has a release tag on it.
|
||||
fixes:
|
||||
- |
|
||||
This release fixes a problem with the scanner that may have caused
|
||||
it to stop scanning a branch prematurely when the tag from another
|
||||
branch had been merged into the history.
|
|
@ -155,6 +155,18 @@ class Config(object):
|
|||
['fixes', 'Bug Fixes'],
|
||||
['other', 'Other Notes'],
|
||||
],
|
||||
|
||||
# When this option is set to True, any merge commits with no
|
||||
# changes and in which the second or later parent is tagged
|
||||
# are considered "null-merges" that bring the tag information
|
||||
# into the current branch but nothing else.
|
||||
#
|
||||
# OpenStack used to use null-merges to bring final release
|
||||
# tags from stable branches back into the master branch. This
|
||||
# confuses the regular traversal because it makes that stable
|
||||
# branch appear to be part of master and/or the later stable
|
||||
# branch. This option allows us to ignore those.
|
||||
'ignore_null_merges': True,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -685,9 +685,55 @@ class Scanner(object):
|
|||
todo = collections.deque()
|
||||
todo.appendleft(head)
|
||||
|
||||
ignore_null_merges = self.conf.ignore_null_merges
|
||||
if ignore_null_merges:
|
||||
LOG.debug('ignoring null-merge commits')
|
||||
|
||||
while todo:
|
||||
sha = todo.popleft()
|
||||
entry = all[sha]
|
||||
null_merge = False
|
||||
|
||||
# OpenStack used to use null-merges to bring final release
|
||||
# tags from stable branches back into the master
|
||||
# branch. This confuses the regular traversal because it
|
||||
# makes that stable branch appear to be part of master
|
||||
# and/or the later stable branch. When we hit one of those
|
||||
# tags, skip it and take the first parent.
|
||||
if ignore_null_merges and len(entry.commit.parents) > 1:
|
||||
# Look for tags on the 2nd and later parents. The
|
||||
# first parent is part of the branch we were
|
||||
# originally trying to traverse, and any tags on it
|
||||
# need to be kept.
|
||||
for p in entry.commit.parents[1:]:
|
||||
t = self._get_valid_tags_on_commit(p)
|
||||
# If we have a tag being merged in, we need to
|
||||
# include a check to verify that this is actually
|
||||
# a null-merge (there are no changes).
|
||||
if t and not entry.changes():
|
||||
LOG.debug(
|
||||
'treating %s as a null-merge because '
|
||||
'parent %s has tag(s) %s',
|
||||
sha, p, t,
|
||||
)
|
||||
null_merge = True
|
||||
break
|
||||
if null_merge:
|
||||
# Make it look like the parent entries that we're
|
||||
# going to skip have been emitted so the
|
||||
# bookkeeping for children works properly and we
|
||||
# can continue past the merge.
|
||||
emitted.update(set(entry.commit.parents[1:]))
|
||||
# Make it look like the current entry was emitted
|
||||
# so the bookkeeping for children works properly
|
||||
# and we can continue past the merge.
|
||||
emitted.add(sha)
|
||||
# Now set up the first parent so it is processed
|
||||
# later.
|
||||
first_parent = entry.commit.parents[0]
|
||||
if first_parent not in todo:
|
||||
todo.appendleft(first_parent)
|
||||
continue
|
||||
|
||||
# If a node has multiple children, it is the start point
|
||||
# for a branch that was merged back into the rest of the
|
||||
|
|
|
@ -1010,6 +1010,83 @@ class MergeCommitTest(Base):
|
|||
)
|
||||
|
||||
|
||||
class NullMergeTest(Base):
|
||||
|
||||
def setUp(self):
|
||||
super(NullMergeTest, self).setUp()
|
||||
self.repo.add_file('ignore-0.txt')
|
||||
self.n1 = self._add_notes_file()
|
||||
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
|
||||
|
||||
# Create a branch, add a note, and tag it.
|
||||
self.repo.git('checkout', '-b', 'test_ignore_null_merge')
|
||||
self.n2 = self._add_notes_file()
|
||||
self.repo.git('tag', '-s', '-m', 'second tag', '2.0.0')
|
||||
|
||||
# Move back to master and advance it.
|
||||
self.repo.git('checkout', 'master')
|
||||
self.repo.add_file('ignore-1.txt')
|
||||
self.n3 = self._add_notes_file()
|
||||
|
||||
# Merge only the tag from the first branch back into master.
|
||||
self.repo.git(
|
||||
'merge', '--no-ff', '--strategy', 'ours', '2.0.0',
|
||||
)
|
||||
|
||||
# Add another note file.
|
||||
self.n4 = self._add_notes_file()
|
||||
self.repo.git('tag', '-s', '-m', 'third tag', '3.0.0')
|
||||
|
||||
self.repo.git('log', '--decorate', '--oneline', '--graph', '--all')
|
||||
# The results should look like:
|
||||
#
|
||||
# * afea344 (HEAD -> master, tag: 3.0.0) add slug-0000000000000004.yaml
|
||||
# * 7bb295c Merge tag '2.0.0'
|
||||
# |\
|
||||
# | * 260c80b (tag: 2.0.0, test_ignore_null_merge) add slug-0000000000000002.yaml # noqa
|
||||
# * | 5981ae3 add slug-0000000000000003.yaml
|
||||
# * | 00f9376 add ignore-1.txt
|
||||
# |/
|
||||
# * d24faf9 (tag: 1.0.0) add slug-0000000000000001.yaml
|
||||
# * 6c221cd add ignore-0.txt
|
||||
|
||||
def test_ignore(self):
|
||||
# The scanner should skip over the null-merge and include the
|
||||
# notes that come before the version being merged in, up to
|
||||
# the base of the previous branch.
|
||||
self.scanner = scanner.Scanner(self.c)
|
||||
raw_results = self.scanner.get_notes_by_version()
|
||||
results = {
|
||||
k: [f for (f, n) in v]
|
||||
for (k, v) in raw_results.items()
|
||||
}
|
||||
self.assertEqual(
|
||||
{'1.0.0': [self.n1],
|
||||
'3.0.0': [self.n3, self.n4]},
|
||||
results,
|
||||
)
|
||||
|
||||
def test_follow(self):
|
||||
# The scanner should not skip over the null-merge. The output
|
||||
# should include the 2.0.0 tag that was merged in, as well as
|
||||
# the earlier 1.0.0 version.
|
||||
self.c.override(
|
||||
ignore_null_merges=False,
|
||||
)
|
||||
self.scanner = scanner.Scanner(self.c)
|
||||
raw_results = self.scanner.get_notes_by_version()
|
||||
results = {
|
||||
k: [f for (f, n) in v]
|
||||
for (k, v) in raw_results.items()
|
||||
}
|
||||
self.assertEqual(
|
||||
{'1.0.0': [self.n1],
|
||||
'2.0.0': [self.n2, self.n3],
|
||||
'3.0.0': [self.n4]},
|
||||
results,
|
||||
)
|
||||
|
||||
|
||||
class UniqueIdTest(Base):
|
||||
|
||||
def test_legacy(self):
|
||||
|
|
Loading…
Reference in New Issue