xref: /netbsd-src/external/gpl3/gcc/dist/contrib/gcc-changelog/git_update_version.py (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1fb8a8121Smrg#!/usr/bin/env python3
2fb8a8121Smrg#
3fb8a8121Smrg# This file is part of GCC.
4fb8a8121Smrg#
5fb8a8121Smrg# GCC is free software; you can redistribute it and/or modify it under
6fb8a8121Smrg# the terms of the GNU General Public License as published by the Free
7fb8a8121Smrg# Software Foundation; either version 3, or (at your option) any later
8fb8a8121Smrg# version.
9fb8a8121Smrg#
10fb8a8121Smrg# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11fb8a8121Smrg# WARRANTY; without even the implied warranty of MERCHANTABILITY or
12fb8a8121Smrg# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13fb8a8121Smrg# for more details.
14fb8a8121Smrg#
15fb8a8121Smrg# You should have received a copy of the GNU General Public License
16fb8a8121Smrg# along with GCC; see the file COPYING3.  If not see
17fb8a8121Smrg# <http://www.gnu.org/licenses/>.  */
18fb8a8121Smrg
19fb8a8121Smrgimport argparse
20fb8a8121Smrgimport datetime
21fb8a8121Smrgimport os
22fb8a8121Smrg
23fb8a8121Smrgfrom git import Repo
24fb8a8121Smrg
25fb8a8121Smrgfrom git_repository import parse_git_revisions
26fb8a8121Smrg
27fb8a8121Smrgcurrent_timestamp = datetime.datetime.now().strftime('%Y%m%d\n')
28fb8a8121Smrg
29a448f87cSmrg# Skip the following commits, they cannot be correctly processed
30a448f87cSmrgIGNORED_COMMITS = (
31a448f87cSmrg        'c2be82058fb40f3ae891c68d185ff53e07f14f45',
32a448f87cSmrg        '04a040d907a83af54e0a98bdba5bfabc0ef4f700',
33*b1e83836Smrg        '2e96b5f14e4025691b57d2301d71aa6092ed44bc',
34*b1e83836Smrg        '3ab5c8cd03d92bf4ec41e351820349d92fbc40c4',
35*b1e83836Smrg        '86d8e0c0652ef5236a460b75c25e4f7093cc0651')
36a448f87cSmrg
37fb8a8121Smrg
38fb8a8121Smrgdef read_timestamp(path):
39fb8a8121Smrg    with open(path) as f:
40fb8a8121Smrg        return f.read()
41fb8a8121Smrg
42fb8a8121Smrg
43fb8a8121Smrgdef prepend_to_changelog_files(repo, folder, git_commit, add_to_git):
44fb8a8121Smrg    if not git_commit.success:
45fb8a8121Smrg        for error in git_commit.errors:
46fb8a8121Smrg            print(error)
47fb8a8121Smrg        raise AssertionError()
48fb8a8121Smrg    for entry, output in git_commit.to_changelog_entries(use_commit_ts=True):
49fb8a8121Smrg        full_path = os.path.join(folder, entry, 'ChangeLog')
50fb8a8121Smrg        print('writing to %s' % full_path)
51fb8a8121Smrg        if os.path.exists(full_path):
52fb8a8121Smrg            with open(full_path) as f:
53fb8a8121Smrg                content = f.read()
54fb8a8121Smrg        else:
55fb8a8121Smrg            content = ''
56fb8a8121Smrg        with open(full_path, 'w+') as f:
57fb8a8121Smrg            f.write(output)
58fb8a8121Smrg            if content:
59fb8a8121Smrg                f.write('\n\n')
60fb8a8121Smrg                f.write(content)
61fb8a8121Smrg        if add_to_git:
62fb8a8121Smrg            repo.git.add(full_path)
63fb8a8121Smrg
64fb8a8121Smrg
65a448f87cSmrgactive_refs = ['master', 'releases/gcc-9', 'releases/gcc-10',
66a448f87cSmrg               'releases/gcc-11']
67fb8a8121Smrg
68fb8a8121Smrgparser = argparse.ArgumentParser(description='Update DATESTAMP and generate '
69fb8a8121Smrg                                 'ChangeLog entries')
70fb8a8121Smrgparser.add_argument('-g', '--git-path', default='.',
71fb8a8121Smrg                    help='Path to git repository')
72fb8a8121Smrgparser.add_argument('-p', '--push', action='store_true',
73fb8a8121Smrg                    help='Push updated active branches')
74fb8a8121Smrgparser.add_argument('-d', '--dry-mode',
75fb8a8121Smrg                    help='Generate patch for ChangeLog entries and do it'
76fb8a8121Smrg                         ' even if DATESTAMP is unchanged; folder argument'
77fb8a8121Smrg                         ' is expected')
78fb8a8121Smrgparser.add_argument('-c', '--current', action='store_true',
79fb8a8121Smrg                    help='Modify current branch (--push argument is ignored)')
80fb8a8121Smrgargs = parser.parse_args()
81fb8a8121Smrg
82fb8a8121Smrgrepo = Repo(args.git_path)
83fb8a8121Smrgorigin = repo.remotes['origin']
84fb8a8121Smrg
85fb8a8121Smrg
86a448f87cSmrgdef update_current_branch(ref_name):
87fb8a8121Smrg    commit = repo.head.commit
88fb8a8121Smrg    commit_count = 1
89fb8a8121Smrg    while commit:
90fb8a8121Smrg        if (commit.author.email == 'gccadmin@gcc.gnu.org'
91fb8a8121Smrg                and commit.message.strip() == 'Daily bump.'):
92fb8a8121Smrg            break
93fb8a8121Smrg        # We support merge commits but only with 2 parensts
94fb8a8121Smrg        assert len(commit.parents) <= 2
95fb8a8121Smrg        commit = commit.parents[-1]
96fb8a8121Smrg        commit_count += 1
97fb8a8121Smrg
98fb8a8121Smrg    print('%d revisions since last Daily bump' % commit_count)
99fb8a8121Smrg    datestamp_path = os.path.join(args.git_path, 'gcc/DATESTAMP')
100fb8a8121Smrg    if (read_timestamp(datestamp_path) != current_timestamp
101fb8a8121Smrg            or args.dry_mode or args.current):
102fb8a8121Smrg        head = repo.head.commit
103fb8a8121Smrg        # if HEAD is a merge commit, start with second parent
104fb8a8121Smrg        # (branched that is being merged into the current one)
105fb8a8121Smrg        assert len(head.parents) <= 2
106fb8a8121Smrg        if len(head.parents) == 2:
107fb8a8121Smrg            head = head.parents[1]
108fb8a8121Smrg        commits = parse_git_revisions(args.git_path, '%s..%s'
109a448f87cSmrg                                      % (commit.hexsha, head.hexsha), ref_name)
110a448f87cSmrg        commits = [c for c in commits if c.info.hexsha not in IGNORED_COMMITS]
111fb8a8121Smrg        for git_commit in reversed(commits):
112fb8a8121Smrg            prepend_to_changelog_files(repo, args.git_path, git_commit,
113fb8a8121Smrg                                       not args.dry_mode)
114fb8a8121Smrg        if args.dry_mode:
115fb8a8121Smrg            diff = repo.git.diff('HEAD')
116fb8a8121Smrg            patch = os.path.join(args.dry_mode,
117fb8a8121Smrg                                 branch.name.split('/')[-1] + '.patch')
118fb8a8121Smrg            with open(patch, 'w+') as f:
119fb8a8121Smrg                f.write(diff)
120fb8a8121Smrg            print('branch diff written to %s' % patch)
121fb8a8121Smrg            repo.git.checkout(force=True)
122fb8a8121Smrg        else:
123fb8a8121Smrg            # update timestamp
124fb8a8121Smrg            print('DATESTAMP will be changed:')
125fb8a8121Smrg            with open(datestamp_path, 'w+') as f:
126fb8a8121Smrg                f.write(current_timestamp)
127fb8a8121Smrg            repo.git.add(datestamp_path)
128fb8a8121Smrg            if not args.current:
129fb8a8121Smrg                repo.index.commit('Daily bump.')
130fb8a8121Smrg                if args.push:
131fb8a8121Smrg                    repo.git.push('origin', branch)
132fb8a8121Smrg                    print('branch is pushed')
133fb8a8121Smrg    else:
134fb8a8121Smrg        print('DATESTAMP unchanged')
135fb8a8121Smrg
136fb8a8121Smrg
137fb8a8121Smrgif args.current:
138fb8a8121Smrg    print('=== Working on the current branch ===', flush=True)
139fb8a8121Smrg    update_current_branch()
140fb8a8121Smrgelse:
141fb8a8121Smrg    for ref in origin.refs:
142fb8a8121Smrg        assert ref.name.startswith('origin/')
143fb8a8121Smrg        name = ref.name[len('origin/'):]
144fb8a8121Smrg        if name in active_refs:
145fb8a8121Smrg            if name in repo.branches:
146fb8a8121Smrg                branch = repo.branches[name]
147fb8a8121Smrg            else:
148fb8a8121Smrg                branch = repo.create_head(name, ref).set_tracking_branch(ref)
149fb8a8121Smrg            print('=== Working on: %s ===' % branch, flush=True)
150fb8a8121Smrg            branch.checkout()
151fb8a8121Smrg            origin.pull(rebase=True)
152fb8a8121Smrg            print('branch pulled and checked out')
153a448f87cSmrg            update_current_branch(name)
154fb8a8121Smrg            assert not repo.index.diff(None)
155fb8a8121Smrg            print('branch is done\n', flush=True)
156