diff --git a/docs/manpages/gbp-rpm-ch.xml b/docs/manpages/gbp-rpm-ch.xml index e0f0d9d7..b7cb47ce 100644 --- a/docs/manpages/gbp-rpm-ch.xml +++ b/docs/manpages/gbp-rpm-ch.xml @@ -28,7 +28,12 @@ DIRECTORY FILEPATH FILEPATH - COMMITISH + + + MESSAGE + COMMITISH + + META_TAGS @@ -38,6 +43,13 @@ GIT-LOG-OPTIONS EDITOR + + MSG-FORMAT + + + MSG-FORMAT + + GPG-KEYID CUSTOMIZATION-FILE [PATH1 PATH2] @@ -151,6 +163,16 @@ + + + + + + Use all commits from the Git history, overrides + . + + + COMMITTISH @@ -161,6 +183,19 @@ + + META_TAGS + + + + Meta tags in the commit messages that are interpreted as bug tracking + system related references. The recognized bts references are added in + the generated changelog entries. See the META TAGS section below for + more information. The bts meta tag tracking feature can be disabled + by defining an empty string. + + + @@ -263,6 +298,91 @@ + + + + + + Text to use for new changelog entries. Git history and the commit + messages, including and + options are ignored in this case. + + + + + + + + + Commit changes to git after modifying changelog. Importantly, in + addition to the changelog modifications all other staged changes are + committed, too, making it possible to update other files in the same + commit. + + + + + MSG-FORMAT + + + + Format string for the commit message when committing changes + (when is given). + + + + + + + + + Commit the changes and create a packaging (release) tag. Similarly to + , all staged changes are committed to git + before creating the tag. This option makes it possible to create a + release and correctly document the the tag name in the rpm changelog + (by using %(tagname)s in the + string). + + + + + + + + + Don't fail tag operations if a tag with the same version already + exists, but, overwrite the existing tag, instead. + + + + + MSG-FORMAT + + + + Format string for the tag message + (when is given). + + + + + + + + + GPG sign all created tags. + + + + + GPG-KEYID + + + + Use this keyid for gpg signing tags. + + + CUSTOMIZATION-FILE @@ -310,6 +430,8 @@ Indicate in the changelog entry that bug BUGNUMBER was addressed in this commit. + The bts meta tags recognized by &gbp-rpm-ch; is actually defined by + the option. diff --git a/gbp/config.py b/gbp/config.py index 76a850de..b062248d 100644 --- a/gbp/config.py +++ b/gbp/config.py @@ -798,6 +798,7 @@ class GbpOptionParserRpm(GbpOptionParser): 'changelog-revision': '', 'spawn-editor': 'always', 'editor-cmd': 'vim', + 'meta-bts': '(Close|Closes|Fixes|Fix)', 'spec-vcs-tag': '', }) @@ -860,6 +861,8 @@ class GbpOptionParserRpm(GbpOptionParser): 'git-author': "Use name and email from git-config for the changelog header, " "default is '%(git-author)s'", + 'meta-bts': + "Meta tags for the bts commands, default is '%(meta-bts)s'", 'spec-vcs-tag': "Set/update the 'VCS:' tag in the spec file, empty value " "removes the tag entirely, default is '%(spec-vcs-tag)s'", diff --git a/gbp/git/repository.py b/gbp/git/repository.py index 6cc3cd9f..ae62b5cb 100644 --- a/gbp/git/repository.py +++ b/gbp/git/repository.py @@ -171,7 +171,7 @@ def _git_getoutput(self, command, args=[], extra_env=None, cwd=None): return output, popen.returncode def _git_inout(self, command, args, input=None, extra_env=None, cwd=None, - capture_stderr=False, config_args=None): + capture_stderr=False, capture_stdout=True, config_args=None): """ Run a git command with input and return output @@ -192,10 +192,12 @@ def _git_inout(self, command, args, input=None, extra_env=None, cwd=None, """ if not cwd: cwd = self.path - return self.__git_inout(command, args, input, extra_env, cwd, capture_stderr, config_args) + return self.__git_inout(command, args, input, extra_env, cwd, capture_stderr, + capture_stdout, config_args) @classmethod - def __git_inout(cls, command, args, input, extra_env, cwd, capture_stderr, config_args=None): + def __git_inout(cls, command, args, input, extra_env, cwd, capture_stderr, + capture_stdout, config_args=None): """ As _git_inout but can be used without an instance """ @@ -206,13 +208,14 @@ def __git_inout(cls, command, args, input, extra_env, cwd, capture_stderr, confi cmd = ['git'] + config_opts + [command] + args env = cls.__build_env(extra_env) + stdout_arg = subprocess.PIPE if capture_stdout else None stderr_arg = subprocess.PIPE if capture_stderr else None stdin_arg = subprocess.PIPE if input is not None else None log.debug(cmd) popen = subprocess.Popen(cmd, stdin=stdin_arg, - stdout=subprocess.PIPE, + stdout=stdout_arg, stderr=stderr_arg, env=env, close_fds=True, @@ -220,7 +223,7 @@ def __git_inout(cls, command, args, input, extra_env, cwd, capture_stderr, confi (stdout, stderr) = popen.communicate(input) return stdout, stderr, popen.returncode - def _git_command(self, command, args=[], extra_env=None): + def _git_command(self, command, args=[], extra_env=None, interactive=False): """ Execute git command with arguments args and environment env at path. @@ -232,12 +235,14 @@ def _git_command(self, command, args=[], extra_env=None): @param extra_env: extra environment variables to set when running command @type extra_env: C{dict} """ + capture_stdout = not interactive try: stdout, stderr, ret = self._git_inout(command=command, args=args, input=None, extra_env=extra_env, - capture_stderr=True) + capture_stderr=True, + capture_stdout=capture_stdout) except Exception as excobj: raise GitRepositoryError("Error running git %s: %s" % (command, excobj)) if ret: @@ -1450,11 +1455,17 @@ def rename_file(self, old, new): #{ Comitting - def _commit(self, msg, args=[], author_info=None): + def _commit(self, msg, args=[], author_info=None, + committer_info=None, edit=False): extra_env = author_info.get_author_env() if author_info else None - self._git_command("commit", ['-q', '-m', msg] + args, extra_env=extra_env) + if committer_info: + extra_env.update(committer_info.get_committer_env()) + default_args = ['-q', '-m', msg] + (['--edit'] if edit else []) + self._git_command("commit", default_args + args, extra_env=extra_env, + interactive=edit) - def commit_staged(self, msg, author_info=None, edit=False): + def commit_staged(self, msg, author_info=None, edit=False, + committer_info=None): """ Commit currently staged files to the repository @@ -1464,10 +1475,11 @@ def commit_staged(self, msg, author_info=None, edit=False): @type author_info: L{GitModifier} @param edit: whether to spawn an editor to edit the commit info @type edit: C{bool} + @param committer_info: committer information + @type committer_info: L{GitModifier} """ - args = GitArgs() - args.add_true(edit, '--edit') - self._commit(msg=msg, args=args.args, author_info=author_info) + self._commit(msg=msg, author_info=author_info, + committer_info=committer_info, edit=edit) def commit_all(self, msg, author_info=None, edit=False): """ @@ -1477,11 +1489,9 @@ def commit_all(self, msg, author_info=None, edit=False): @param author_info: authorship information @type author_info: L{GitModifier} """ - args = GitArgs('-a') - args.add_true(edit, '--edit') - self._commit(msg=msg, args=args.args, author_info=author_info) + self._commit(msg=msg, args=['-a'], author_info=author_info, edit=edit) - def commit_files(self, files, msg, author_info=None): + def commit_files(self, files, msg, author_info=None, committer_info=None): """ Commit the given files to the repository @@ -1491,10 +1501,13 @@ def commit_files(self, files, msg, author_info=None): @type msg: C{str} @param author_info: authorship information @type author_info: L{GitModifier} + @param committer_info: committer information + @type committer_info: L{GitModifier} """ if isinstance(files, str): files = [files] - self._commit(msg=msg, args=files, author_info=author_info) + self._commit(msg=msg, args=files, author_info=author_info, + committer_info=committer_info) def commit_dir(self, unpack_dir, msg, branch, other_parents=None, author={}, committer={}, create_missing_branch=False): @@ -2007,7 +2020,8 @@ def create(cls, path, description=None, bare=False): input=None, extra_env=None, cwd=abspath, - capture_stderr=True) + capture_stderr=True, + capture_stdout=True) except Exception as excobj: raise GitRepositoryError("Error running git init: %s" % excobj) if ret: @@ -2073,7 +2087,8 @@ def clone(cls, path, remote, depth=0, recursive=False, mirror=False, input=None, extra_env=None, cwd=abspath, - capture_stderr=True) + capture_stderr=True, + capture_stdout=True) except Exception as excobj: raise GitRepositoryError("Error running git clone: %s" % excobj) if ret: diff --git a/gbp/rpm/policy.py b/gbp/rpm/policy.py index a027ed99..791b25b1 100644 --- a/gbp/rpm/policy.py +++ b/gbp/rpm/policy.py @@ -94,8 +94,6 @@ class ChangelogEntryFormatter(object): # Maximum length for a changelog entry line max_entry_line_length = 76 - # Bug tracking system related meta tags recognized from git commit msg - bts_meta_tags = ("Close", "Closes", "Fixes", "Fix") # Regexp for matching bug tracking system ids (e.g. "bgo#123") bug_id_re = r'[A-Za-z0-9#_\-]+' @@ -107,15 +105,18 @@ def _parse_bts_tags(cls, lines, meta_tags): @param lines: commit message @type lines: C{list} of C{str} - @param meta_tags: meta tags to look for - @type meta_tags: C{tuple} of C{str} + @param meta_tags: meta tags (regexp) to look for + @type meta_tags: C{str} @return: bts-ids per meta tag and the non-mathced lines @rtype: (C{dict}, C{list} of C{str}) """ + if not meta_tags: + return ({}, lines[:]) + tags = {} other_lines = [] - bts_re = re.compile(r'^(?P%s):\s*(?P.*)' % - ('|'.join(meta_tags)), re.I) + bts_re = re.compile(r'^(?P%s):\s*(?P.*)' % meta_tags, + re.I) bug_id_re = re.compile(cls.bug_id_re) for line in lines: match = bts_re.match(line) @@ -172,7 +173,7 @@ def compose(cls, commit_info, **kwargs): return None # Parse and filter out bts-related meta-tags - bts_tags, body = cls._parse_bts_tags(body, cls.bts_meta_tags) + bts_tags, body = cls._parse_bts_tags(body, kwargs['meta_bts']) # Additional filtering body = cls._extra_filter(body, kwargs['ignore_re']) @@ -191,7 +192,7 @@ def compose(cls, commit_info, **kwargs): text.extend([" " + line for line in body if line.strip()]) # Add bts tags and ids in the end - for tag, ids in bts_tags.items(): + for tag, ids in sorted(bts_tags.items()): bts_msg = " (%s: %s)" % (tag, ', '.join(ids)) if len(text[-1]) + len(bts_msg) >= cls.max_entry_line_length: text.append(" ") diff --git a/gbp/scripts/rpm_ch.py b/gbp/scripts/rpm_ch.py index 9212f717..a60ba96f 100644 --- a/gbp/scripts/rpm_ch.py +++ b/gbp/scripts/rpm_ch.py @@ -29,11 +29,13 @@ import gbp.log from gbp.config import GbpOptionParserRpm, GbpOptionGroup from gbp.errors import GbpError +from gbp.git.modifier import GitModifier from gbp.rpm import (guess_spec, NoSpecError, SpecFile, split_version_str, compose_version_str) from gbp.rpm.changelog import Changelog, ChangelogParser, ChangelogError from gbp.rpm.git import GitRepositoryError, RpmGitRepository from gbp.rpm.policy import RpmPkgPolicy +from gbp.scripts.buildpackage_rpm import packaging_tag_data from gbp.scripts.common import ExitCodes from gbp.tmpfile import init_tmpdir, del_tmpdir @@ -122,8 +124,9 @@ def determine_editor(options): return 'vi' -def check_branch(repo, options): - """Check the current git branch""" +def check_repo_state(repo, options): + """Check that the repository is in good state""" + # Check branch try: branch = repo.get_branch() except GitRepositoryError: @@ -133,6 +136,18 @@ def check_branch(repo, options): (options.packaging_branch, branch)) raise GbpError("Use --ignore-branch to ignore or " "--packaging-branch to set the branch name.") + # Check unstaged changes + if options.commit: + unstaged = [] + status = repo.status() + for group, files in status.items(): + if group != '??' and group[1] != ' ': + unstaged.extend([f.decode() for f in files]) + if unstaged: + gbp.log.err("Unstaged changes in:\n %s" % + '\n '.join(unstaged)) + raise GbpError("Please commit or stage your changes before using " + "the --commit or --tag option") def parse_spec_file(repo, options): @@ -223,7 +238,9 @@ def guess_commit(section, repo, options): def get_start_commit(changelog, repo, options): """Get the start commit from which to generate new entries""" - if options.since: + if options.all: + since = None + elif options.since: since = options.since else: if changelog.sections: @@ -232,7 +249,7 @@ def get_start_commit(changelog, repo, options): since = None if not since: raise GbpError("Couldn't determine starting point from " - "changelog, please use the '--since' option") + "changelog, please use the '--since' or '--all'") gbp.log.info("Continuing from commit '%s'" % since) return since @@ -269,23 +286,65 @@ def entries_from_commits(changelog, repo, commits, options): info = repo.get_commit_info(commit) entry_text = ChangelogEntryFormatter.compose(info, full=options.full, ignore_re=options.ignore_regex, - id_len=options.idlen) + id_len=options.idlen, + meta_bts=options.meta_bts) if entry_text: entries.append(changelog.create_entry(author=info['author'].name, text=entry_text)) return entries +def entries_from_text(changelog, text, author): + """Generate a list of changelog entries from a string""" + entries = [] + # Use current user as the author for all entries + for line in text.splitlines(): + if line.strip(): + entry_text = "- %s" % line.strip() + entries.append(changelog.create_entry(author=author, + text=entry_text)) + return entries + + +def generate_new_entries(changelog, repo, options, args): + """Generate new entries to be appended to changelog""" + if options.message: + author = get_author(repo, options.git_author)[0] + entries = entries_from_text(changelog, options.message, author) + else: + # Get range of commits from where to generate changes + since = get_start_commit(changelog, repo, options) + if args: + gbp.log.info("Only looking for changes in '%s'" % ", ".join(args)) + commits = repo.get_commits(since=since, until='HEAD', paths=args, + options=options.git_log.split(" ")) + commits.reverse() + if not commits: + gbp.log.info("No changes detected from %s to %s." % (since, 'HEAD')) + entries = entries_from_commits(changelog, repo, commits, options) + return entries + + def update_changelog(changelog, entries, repo, spec, options): """Update the changelog with a range of commits""" # Get info for section header now = datetime.now() name, email = get_author(repo, options.git_author) + author = None + committer = None rev_str_fields = dict(spec.version, version=compose_version_str(spec.version), - vendor=options.vendor, - tagname=repo.describe('HEAD', longfmt=True, - always=True)) + vendor=options.vendor) + if options.tag: + # Get fake information for the to-be-created git commit + author = committer = GitModifier(date=now) + tag, msg = packaging_tag_data(repo, 'HEAD', spec.name, spec.version, + options) + else: + tag = repo.describe('HEAD', longfmt=True, always=True) + msg = None + rev_str_fields['tagname'] = tag + try: revision = options.changelog_revision % rev_str_fields except KeyError as err: @@ -305,6 +364,27 @@ def update_changelog(changelog, entries, repo, spec, options): # Add new entries to the topmost section for entry in entries: top_section.append_entry(entry) + return (tag, msg, author, committer) + + +def create_commit_message(spec, options): + """Generate commit message""" + fields = spec.version + fields['version'] = compose_version_str(spec.version) + fields['vendor'] = options.vendor + fields['pkg'] = spec.name + try: + return options.commit_msg % fields + except KeyError as err: + raise GbpError("Unknown key %s in commit-msg string, " + "only %s are accepted" % (err, fields.keys())) + + +def commit_changelog(repo, changelog, message, author, committer, edit): + """Commit changelog to Git""" + repo.add_files(changelog.path) + repo.commit_staged(message, author_info=author, committer_info=committer, + edit=edit) def build_parser(name): @@ -322,9 +402,12 @@ def build_parser(name): "how to format the changelog entries") naming_grp = GbpOptionGroup(parser, "naming", "branch names, tag formats, directory and file naming") + commit_grp = GbpOptionGroup(parser, "commit", + "automatic committing and tagging") parser.add_option_group(range_grp) parser.add_option_group(format_grp) parser.add_option_group(naming_grp) + parser.add_option_group(commit_grp) # Non-grouped options parser.add_option("-v", "--verbose", action="store_true", dest="verbose", @@ -351,6 +434,8 @@ def build_parser(name): dest="packaging_branch") naming_grp.add_config_file_option(option_name="packaging-tag", dest="packaging_tag") + naming_grp.add_config_file_option(option_name="packaging-tag-msg", + dest="packaging_tag_msg") naming_grp.add_config_file_option(option_name="packaging-dir", dest="packaging_dir") naming_grp.add_config_file_option(option_name="changelog-file", @@ -359,7 +444,11 @@ def build_parser(name): # Range group options range_grp.add_option("-s", "--since", dest="since", help="commit to start from (e.g. HEAD^^^, release/0.1.2)") + range_grp.add_option("--all", action="store_true", + help="use all commits from the Git history, overrides " + "--since") # Formatting group options + format_grp.add_config_file_option(option_name="meta-bts", dest="meta_bts") format_grp.add_option("--no-release", action="store_false", default=True, dest="release", help="no release, just update the last changelog section") @@ -380,6 +469,22 @@ def build_parser(name): dest="spawn_editor") format_grp.add_config_file_option(option_name="editor-cmd", dest="editor_cmd") + format_grp.add_option("-m", '--message', + help="text to use as new changelog entries - git commit " + "messages and the --since are ignored in this case") + # Commit/tag group options + commit_grp.add_option("-c", "--commit", action="store_true", + help="commit changes") + commit_grp.add_config_file_option(option_name="commit-msg", + dest="commit_msg") + commit_grp.add_option("--tag", action="store_true", + help="commit the changes and create a" + "packaging/release tag") + commit_grp.add_option("--retag", action="store_true", + help="Overwrite packaging tag if it already exists") + commit_grp.add_boolean_config_file_option(option_name="sign-tags", + dest="sign_tags") + commit_grp.add_config_file_option(option_name="keyid", dest="keyid") return parser @@ -391,6 +496,8 @@ def parse_args(argv): options, args = parser.parse_args(argv[1:]) + if options.tag: + options.commit = True if not options.changelog_revision: options.changelog_revision = RpmPkgPolicy.Changelog.header_rev_format @@ -412,35 +519,37 @@ def main(argv): editor_cmd = determine_editor(options) repo = RpmGitRepository('.') - check_branch(repo, options) + check_repo_state(repo, options) # Find and parse spec file spec = parse_spec_file(repo, options) # Find and parse changelog file ch_file = parse_changelog_file(repo, spec, options) - since = get_start_commit(ch_file.changelog, repo, options) - # Get range of commits from where to generate changes - if args: - gbp.log.info("Only looking for changes in '%s'" % ", ".join(args)) - commits = repo.get_commits(since=since, until='HEAD', paths=args, - options=options.git_log.split(" ")) - commits.reverse() - if not commits: - gbp.log.info("No changes detected from %s to %s." % (since, 'HEAD')) + # Get new entries + entries = generate_new_entries(ch_file.changelog, repo, options, args) # Do the actual update - entries = entries_from_commits(ch_file.changelog, repo, commits, - options) - update_changelog(ch_file.changelog, entries, repo, spec, options) - + tag, tag_msg, author, committer = update_changelog(ch_file.changelog, + entries, repo, spec, + options) # Write to file ch_file.write() - if editor_cmd: + if editor_cmd and not options.message: gbpc.Command(editor_cmd, [ch_file.path])() + if options.commit: + edit = True if editor_cmd else False + msg = create_commit_message(spec, options) + commit_changelog(repo, ch_file, msg, author, committer, edit) + if options.tag: + if options.retag and repo.has_tag(tag): + repo.delete_tag(tag) + repo.create_tag(tag, tag_msg, 'HEAD', options.sign_tags, + options.keyid) + except (GbpError, GitRepositoryError, ChangelogError, NoSpecError) as err: if len(err.__str__()): gbp.log.err(err) diff --git a/tests/component/rpm/test_rpm_ch.py b/tests/component/rpm/test_rpm_ch.py index 97f5d7e6..197f6db9 100644 --- a/tests/component/rpm/test_rpm_ch.py +++ b/tests/component/rpm/test_rpm_ch.py @@ -109,6 +109,16 @@ def test_create_changes_file(self): # Should contain 3 lines (header, 1 entry and an empty line) eq_(len(content), 3) + def test_option_all(self): + """Test the --all cmdline option""" + repo = self.init_test_repo('gbp-test2') + + eq_(mock_ch(['--changelog-file=CHANGES', '--all']), 0) + content = self.read_file('packaging/gbp-test2.changes') + # Should contain N+2 lines (header, N commits and an empty line) + commit_cnt = len(repo.get_commits(since=None, until='master')) + eq_(len(content), commit_cnt + 2) + def test_option_changelog_file(self): """Test the --changelog-file cmdline option""" repo = self.init_test_repo('gbp-test-native') @@ -163,6 +173,26 @@ def test_branch_options(self): eq_(mock_ch(['--packaging-branch=foo', '--ignore-branch']), 0) + def test_option_meta_bts(self): + """Test parsing of the bts meta tags""" + repo = self.init_test_repo('gbp-test-native') + + # Create a dummy commit that references bts + with open('new-file', 'w') as fobj: + fobj.write('foobar\n') + repo.add_files('new-file') + repo.commit_all('Fix\n\nCloses: #123\nFixes: #456\n Fixes: #789') + + eq_(mock_ch(['--since=HEAD^']), 0) + content = self.read_file('packaging/gbp-test-native.changes') + # rpm-ch shouldn't have picked the ref with leading whitespace + eq_(content[1], '- Fix (Closes: #123) (Fixes: #456)\n') + + # Check the --meta-bts option + eq_(mock_ch(['--since=HEAD^', '--meta-bts=Fixes']), 0) + content = self.read_file('packaging/gbp-test-native.changes') + eq_(content[1], '- Fix (Fixes: #456)\n') + def test_option_no_release(self): """Test the --no-release cmdline option""" self.init_test_repo('gbp-test-native') @@ -255,6 +285,81 @@ def test_option_changelog_revision(self): header = self.read_file('packaging/gbp-test-native.changes')[0] ok_(re.match(r'.+ foobar$', header)) + def test_option_commit(self): + """Test the --commit cmdline option""" + repo = self.init_test_repo('gbp-test') + + # Check unclean repo + with open('untracked-file', 'w') as fobj: + fobj.write('this file is not tracked\n') + with open('foo.txt', 'a') as fobj: + fobj.write('new stuff\n') + + # Unstaged file (foo.txt) -> failure + eq_(mock_ch(['--commit', '--since=HEAD^']), 1) + self._check_log(-1, 'gbp:error: Please commit or stage your changes') + + # Add file, update and commit, untracked file should be ignored + repo.add_files('foo.txt') + sha = repo.rev_parse('HEAD') + eq_(mock_ch(['--commit', '--since=HEAD^']), 0) + eq_(sha, repo.rev_parse('HEAD^')) + eq_(repo.get_commit_info('HEAD')['files'], + {'M': [b'foo.txt', b'gbp-test.spec']}) + + def test_option_commit_msg(self): + """Test the --commit-msg cmdline option""" + repo = self.init_test_repo('gbp-test2') + + eq_(mock_ch(['--commit', '--since=HEAD^', '--commit-msg=Foo']), 0) + eq_(repo.get_commit_info('HEAD')['subject'], 'Foo') + + # Unknown key in format string causes failure + eq_(mock_ch(['--commit', '--since=HEAD^', '--commit-msg=%(foo)s']), 1) + self._check_log(-1, "gbp:error: Unknown key 'foo' in commit-msg string") + + def test_tagging(self): + """Test commiting/tagging""" + repo = self.init_test_repo('gbp-test-native') + + # Update and commit+tag + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 0) + ok_(repo.has_tag('new-tag')) + sha = repo.rev_parse('HEAD') + eq_(sha, repo.rev_parse('new-tag^0')) + + # Should fail if the tag already exists + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 1) + + # Update and commit+tag + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^', + '--retag']), 0) + ok_(repo.has_tag('new-tag')) + sha2 = repo.rev_parse('HEAD') + ok_(sha2 != sha) + eq_(sha2, repo.rev_parse('new-tag^0')) + + def test_tagging2(self): + """Test commiting/tagging spec file""" + repo = self.init_test_repo('gbp-test2') + + # Check unclean repo + with open('untracked-file', 'w') as fobj: + fobj.write('this file is not tracked\n') + with open('README', 'a') as fobj: + fobj.write('some new content\n') + + # Unstaged file (README) -> failure + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 1) + self._check_log(-1, 'gbp:error: Please commit or stage your changes') + + # Add file, update and commit+tag, untracked file should be ignored + repo.add_files('README') + eq_(mock_ch(['--tag', '--packaging-tag=new-tag', '--since=HEAD^']), 0) + ok_(repo.has_tag('new-tag')) + sha = repo.rev_parse('HEAD') + eq_(sha, repo.rev_parse('new-tag^0')) + def test_option_editor_cmd(self): """Test the --editor-cmd and --spawn-editor cmdline options""" repo = self.init_test_repo('gbp-test-native') @@ -268,6 +373,17 @@ def test_option_editor_cmd(self): eq_(mock_ch(['--spawn-editor=always', '--editor-cmd=']), 0) + def test_option_message(self): + """Test the --message cmdline option""" + self.init_test_repo('gbp-test-native') + orig_content = self.read_file('packaging/gbp-test-native.changes') + + eq_(mock_ch(['--message', 'my entry\nanother entry']), 0) + content = self.read_file('packaging/gbp-test-native.changes') + # Added header, two entries and a blank line + eq_(len(content), len(orig_content) + 4) + eq_(content[2], '- another entry\n') + def test_user_customizations(self): """Test the user customizations""" repo = self.init_test_repo('gbp-test-native')