diff options
Diffstat (limited to 'contrib/gcc-changelog/git_update_version.py')
-rwxr-xr-x | contrib/gcc-changelog/git_update_version.py | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/contrib/gcc-changelog/git_update_version.py b/contrib/gcc-changelog/git_update_version.py new file mode 100755 index 0000000..d2cadb8 --- /dev/null +++ b/contrib/gcc-changelog/git_update_version.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 3, or (at your option) any later +# version. +# +# GCC is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. */ + +import argparse +import datetime +import os + +from git import Repo + +from git_repository import parse_git_revisions + +current_timestamp = datetime.datetime.now().strftime('%Y%m%d\n') + + +def read_timestamp(path): + with open(path) as f: + return f.read() + + +def prepend_to_changelog_files(repo, folder, git_commit, add_to_git): + if not git_commit.success: + for error in git_commit.errors: + print(error) + raise AssertionError() + for entry, output in git_commit.to_changelog_entries(use_commit_ts=True): + full_path = os.path.join(folder, entry, 'ChangeLog') + print('writing to %s' % full_path) + if os.path.exists(full_path): + with open(full_path) as f: + content = f.read() + else: + content = '' + with open(full_path, 'w+') as f: + f.write(output) + if content: + f.write('\n\n') + f.write(content) + if add_to_git: + repo.git.add(full_path) + + +active_refs = ['master', 'releases/gcc-8', 'releases/gcc-9', 'releases/gcc-10'] + +parser = argparse.ArgumentParser(description='Update DATESTAMP and generate ' + 'ChangeLog entries') +parser.add_argument('-g', '--git-path', default='.', + help='Path to git repository') +parser.add_argument('-p', '--push', action='store_true', + help='Push updated active branches') +parser.add_argument('-d', '--dry-mode', + help='Generate patch for ChangeLog entries and do it' + ' even if DATESTAMP is unchanged; folder argument' + ' is expected') +parser.add_argument('-c', '--current', action='store_true', + help='Modify current branch (--push argument is ignored)') +args = parser.parse_args() + +repo = Repo(args.git_path) +origin = repo.remotes['origin'] + + +def update_current_branch(): + commit = repo.head.commit + commit_count = 1 + while commit: + if (commit.author.email == 'gccadmin@gcc.gnu.org' + and commit.message.strip() == 'Daily bump.'): + break + # We support merge commits but only with 2 parensts + assert len(commit.parents) <= 2 + commit = commit.parents[-1] + commit_count += 1 + + print('%d revisions since last Daily bump' % commit_count) + datestamp_path = os.path.join(args.git_path, 'gcc/DATESTAMP') + if (read_timestamp(datestamp_path) != current_timestamp + or args.dry_mode or args.current): + head = repo.head.commit + # if HEAD is a merge commit, start with second parent + # (branched that is being merged into the current one) + assert len(head.parents) <= 2 + if len(head.parents) == 2: + head = head.parents[1] + commits = parse_git_revisions(args.git_path, '%s..%s' + % (commit.hexsha, head.hexsha)) + for git_commit in reversed(commits): + prepend_to_changelog_files(repo, args.git_path, git_commit, + not args.dry_mode) + if args.dry_mode: + diff = repo.git.diff('HEAD') + patch = os.path.join(args.dry_mode, + branch.name.split('/')[-1] + '.patch') + with open(patch, 'w+') as f: + f.write(diff) + print('branch diff written to %s' % patch) + repo.git.checkout(force=True) + else: + # update timestamp + print('DATESTAMP will be changed:') + with open(datestamp_path, 'w+') as f: + f.write(current_timestamp) + repo.git.add(datestamp_path) + if not args.current: + repo.index.commit('Daily bump.') + if args.push: + repo.git.push('origin', branch) + print('branch is pushed') + else: + print('DATESTAMP unchanged') + + +if args.current: + print('=== Working on the current branch ===', flush=True) + update_current_branch() +else: + for ref in origin.refs: + assert ref.name.startswith('origin/') + name = ref.name[len('origin/'):] + if name in active_refs: + if name in repo.branches: + branch = repo.branches[name] + else: + branch = repo.create_head(name, ref).set_tracking_branch(ref) + print('=== Working on: %s ===' % branch, flush=True) + branch.checkout() + origin.pull(rebase=True) + print('branch pulled and checked out') + update_current_branch() + assert not repo.index.diff(None) + print('branch is done\n', flush=True) |