stop scanning at the base of a branch

By default, stop scanning when we hit the version that is at the base of
a branch.

Change-Id: I4eaa03d15bafa8dc3bdbdae3707295040482ec04
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2016-10-10 14:04:04 -04:00
parent e2fe07d13f
commit 6f6e7addfb
3 changed files with 251 additions and 3 deletions

View File

@ -0,0 +1,7 @@
---
features:
- Automatically stop scanning branches at the point where they
diverge from master. This avoids having release notes from older
versions, that appear on master before the branch, from showing up
in the versions from the branch. This logic is only applied to
branches created from master.

View File

@ -78,6 +78,64 @@ def _get_unique_id(filename):
return uniqueid
def _get_branch_base(reporoot, branch):
"Return the tag at base of the branch."
# Based on
# http://stackoverflow.com/questions/1527234/finding-a-branch-point-with-git
# git rev-list $(git rev-list --first-parent \
# ^origin/stable/newton master | tail -n1)^^!
#
# Determine the list of commits accessible from the branch we are
# supposed to be scanning, but not on master.
cmd = [
'git',
'rev-list',
'--first-parent',
branch, # on the branch
'^master', # not on master
]
try:
LOG.debug(' '.join(cmd))
parents = utils.check_output(cmd, cwd=reporoot).strip()
if not parents:
# There are no commits on the branch, yet, so we can use
# our current-version logic.
return _get_current_version(reporoot, branch)
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
parent = parents.splitlines()[-1]
LOG.debug('parent = %r', parent)
# Now get the previous commit, which should be the one we tagged
# to create the branch.
cmd = [
'git',
'rev-list',
'{}^^!'.format(parent),
]
try:
sha = utils.check_output(cmd, cwd=reporoot).strip()
LOG.debug('sha = %r', sha)
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
# Now get the tag for that commit.
cmd = [
'git',
'describe',
'--abbrev=0',
sha,
]
try:
return utils.check_output(cmd, cwd=reporoot).strip()
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
# The git log output from _get_tags_on_branch() looks like this sample
# from the openstack/nova repository for git 1.9.1:
#
@ -164,9 +222,26 @@ def get_notes_by_version(conf):
reporoot = conf.reporoot
notesdir = conf.notespath
branch = conf.branch
earliest_version = conf.earliest_version
collapse_pre_releases = conf.collapse_pre_releases
LOG.debug('scanning %s/%s (branch=%s)' % (reporoot, notesdir, branch))
# If the user has not told us where to stop, try to work it out
# for ourselves. If branch is set and is not "master", then we
# want to stop at the base of the branch.
if (not earliest_version) and branch and (branch != 'master'):
LOG.debug('determining earliest_version from branch')
earliest_version = _get_branch_base(reporoot, branch)
if earliest_version and collapse_pre_releases:
if PRE_RELEASE_RE.search(earliest_version):
# The earliest version won't actually be the pre-release
# that might have been tagged when the branch was created,
# but the final version. Strip the pre-release portion of
# the version number.
earliest_version = '.'.join(earliest_version.split('.')[:-1])
LOG.debug('using earliest_version = %r', earliest_version)
# Determine all of the tags known on the branch, in their date
# order. We scan the commit history in topological order to ensure
# we have the commits in the right version, so we might encounter
@ -319,7 +394,7 @@ def get_notes_by_version(conf):
# Combine pre-releases into the final release, if we are told to
# and the final release exists.
if conf.collapse_pre_releases:
if collapse_pre_releases:
collapsing = files_and_tags
files_and_tags = collections.OrderedDict()
for ov in versions_by_date:
@ -365,7 +440,7 @@ def get_notes_by_version(conf):
trimmed[ov] = sorted(files_and_tags[ov])
# If we have been told to stop at a version, we can do that
# now.
if conf.earliest_version and ov == conf.earliest_version:
if earliest_version and ov == earliest_version:
break
LOG.debug('[reno] found %d versions and %d files',

View File

@ -815,13 +815,179 @@ class BranchTest(Base):
}
self.assertEqual(
{
'1.0.0': [self.f1],
'2.0.0': [self.f2],
'2.0.0-1': [f21],
},
results,
)
def test_pre_release_branch_no_collapse(self):
f4 = self._add_notes_file('slug4')
self._run_git('tag', '-s', '-m', 'pre-release', '4.0.0.0rc1')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self._run_git('checkout', '4.0.0.0rc1')
self._run_git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
log_text = self._run_git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self._run_git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
collapse_pre_releases=False,
)
raw_results = scanner.get_notes_by_version(self.c)
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0.0rc1': [f4],
'4.0.0.0rc1-1': [f41],
},
results,
)
def test_pre_release_branch_collapse(self):
f4 = self._add_notes_file('slug4')
self._run_git('tag', '-s', '-m', 'pre-release', '4.0.0.0rc1')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self._run_git('checkout', '4.0.0.0rc1')
self._run_git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
self._run_git('tag', '-s', '-m', 'release', '4.0.0')
log_text = self._run_git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self._run_git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
collapse_pre_releases=True,
)
raw_results = scanner.get_notes_by_version(self.c)
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4, f41],
},
results,
)
def test_full_release_branch(self):
f4 = self._add_notes_file('slug4')
self._run_git('tag', '-s', '-m', 'release', '4.0.0')
# Add a commit on master after the tag
self._add_notes_file('slug5')
# Move back to the tag and create the branch
self._run_git('checkout', '4.0.0')
self._run_git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
log_text = self._run_git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self._run_git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
)
raw_results = scanner.get_notes_by_version(self.c)
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4],
'4.0.0-1': [f41],
},
results,
)
def test_branch_tip_of_master(self):
# We have branched from master, but not added any commits to
# master.
f4 = self._add_notes_file('slug4')
self._run_git('tag', '-s', '-m', 'release', '4.0.0')
self._run_git('checkout', '-b', 'stable/4')
# Create a commit on the branch
f41 = self._add_notes_file('slug41')
f42 = self._add_notes_file('slug42')
log_text = self._run_git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self._run_git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
)
raw_results = scanner.get_notes_by_version(self.c)
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4],
'4.0.0-2': [f41, f42],
},
results,
)
def test_branch_no_more_commits(self):
# We have branched from master, but not added any commits to
# our branch or to master.
f4 = self._add_notes_file('slug4')
self._run_git('tag', '-s', '-m', 'release', '4.0.0')
self._run_git('checkout', '-b', 'stable/4')
# Create a commit on the branch
log_text = self._run_git(
'log', '--pretty=%x00%H %d', '--name-only', '--graph',
'--all', '--decorate',
)
self.addDetail('git log', text_content(log_text))
rev_list = self._run_git('rev-list', '--first-parent',
'^stable/4', 'master')
self.addDetail('rev-list', text_content(rev_list))
self.c.override(
branch='stable/4',
)
raw_results = scanner.get_notes_by_version(self.c)
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
}
self.assertEqual(
{
'4.0.0': [f4],
},
results,
)
class GetTagsParseTest(base.TestCase):