anchor.diff:        }).first;
       }
   
  -    final commitInfo = closestTag['commit'] as Map<String, dynamic>;
  -    print('Getting the date of the tagged commit for ${closestTag['name']}...');
  +    final commitInfo = closestTag[_commitKey] as Map<String, dynamic>;
  +    print(
  +      'Getting the date of the tagged commit for ${closestTag[_nameKey]}...',
  +    );
       final taggedCommit = jsonDecode((await http.get(Uri.https(
         '${auth}api.github.com',
  -      '/repos/flutter/devtools/commits/${commitInfo['sha']}',
  +      '/repos/flutter/devtools/commits/${commitInfo[_shaKey]}',
       )))
           .body);
   
  -    final commitDate = taggedCommit['commit']['author']['date'];
  +    final commitDate = taggedCommit[_commitKey][_authorKey][_dateKey];
       print(
  -        'Adding entries to the changelog for all commits since $commitDate...');
  -
  -    // TODO(kenz): handle cases where there are more than 100 commits.
  -    final uri = Uri.https(
  -      '${auth}api.github.com',
  -      '/repos/flutter/devtools/commits',
  -      {
  -        'since': commitDate,
  -        'per_page': '100',
  -      },
  +      'Adding entries to the changelog for all commits since $commitDate...',
       );
  -    final commits = jsonDecode((await http.get(uri)).body);
  +
       final changes = <String>[];
  -    for (var commit in commits) {
  -      if (commit['sha'] == taggedCommit['sha']) {
  -        print(
  -            'Skipping commit ${commit['sha']} because this is the commit of the previous tag');
  -        continue;
  -      }
  -      final message = commit['commit']['message'];
  -      if (_shouldSkip(commit['commit']['message'])) {
  -        print('Skipping commit marked to be ignored: $message');
  -        continue;
  -      }
  -      final entry = '* ' + _sanitize(commit['commit']['message']);
  -      changes.add(entry);
  +
  +    // TODO(kenz): consider traversing pages properly instead of using a while
  +    // loop.
  +    // See https://docs.github.com/en/rest/guides/traversing-with-pagination
  +    const maxPerPage = 100;
  +    var requestPage = 0;
  +    bool lastPageReceived = false;
  +    while (!lastPageReceived) {
  +      final uri = Uri.https(
  +        '${auth}api.github.com',
  +        '/repos/flutter/devtools/commits',
  +        {
  +          'since': commitDate,
  +          'per_page': '$maxPerPage',
  +          'page': '$requestPage',
  +        },
  +      );
  +      final githubResponse = await http.get(uri);
  +      final commits = jsonDecode(githubResponse.body);
  +      for (final commit in commits) {
  +        if (commit[_shaKey] == taggedCommit[_shaKey]) {
  +          print(
  +            'Skipping commit ${commit[_shaKey]} because this is the commit of '
  +            'the previous tag',
  +          );
  +          continue;
  +        }
  +        final message = commit[_commitKey][_messageKey];
  +        if (_shouldSkip(commit[_commitKey][_messageKey])) {
  +          print('Skipping commit marked to be ignored: $message');
  +          continue;
  +        }
  +        final entry = '* ' + _sanitize(commit[_commitKey][_messageKey]);
  +        changes.add(entry);
  +      }
  +
  +      requestPage++;
  +      if (commits.length < maxPerPage) {
  +        lastPageReceived = true;
  +      }
       }
   
       if (nextVersion != null) {
  
positive.code:       );
    }
  
    // You can authorize your access if you run into a github rate limit.
    // Don't check in your passwords or auth tokens.
    static const auth = '';
  
negative.code: 
      bool isDevBuild(String tagName) => tagName.contains('dev');
      bool isCherryPickRelease(String tagName) => tagName.contains('+');
      String nameOf(tag) => tag[_nameKey];
  
      Map<String, dynamic>? closestTag;
      if (sinceTag != null) {
  