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