| #!/usr/bin/python |
| # |
| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Gets list of revisions between two commits and their commit positions. |
| |
| Example usage: |
| ./fetchInterveningRevisions.py 343b531d31 7b43807df3 chromium |
| ./fetchInterveningRevisions.py 235eff9574 1e4681c33f v8 |
| |
| Note: Another implementation of this functionality can be found in |
| findit/common/gitRepository.py (https://goo.gl/Rr8j9O). |
| """ |
| |
| import argparse |
| import json |
| import urllib2 |
| |
| from bisect_lib import depot_map |
| |
| _GITILES_PADDING = ')]}\'\n' |
| _URL_TEMPLATE = ('https://chromium.googlesource.com/%s/+log/%s..%s' |
| '?format=json&n=%d') |
| |
| # Gitiles paginates the list of commits; since we want to get all of the |
| # commits at once, the page size should be larger than the largest revision |
| # range that we expect to get. |
| _PAGE_SIZE = 512 |
| |
| def FetchInterveningRevisions(start, end, depot_name): |
| """Fetches a list of revision in between two commits. |
| |
| Args: |
| start (str): A git commit hash in the Chromium src repository. |
| end (str): Another git commit hash, after start. |
| depot_name (str): A respository name. |
| |
| Returns: |
| A list of pairs (commit hash, commit position), from earliest to latest, |
| for all commits in between the two given commits, not including either |
| of the given commits. |
| |
| Raises: |
| urllib2.URLError: The request to gitiles failed. |
| ValueError: The response wasn't valid JSON. |
| KeyError: The JSON didn't contain the expected data. |
| """ |
| revisions = _FetchRangeFromGitiles(start, end, depot_name) |
| # The response from gitiles includes the end revision and is ordered |
| # from latest to earliest. |
| return [_CommitPair(r) for r in reversed(revisions[1:])] |
| |
| |
| def _FetchRangeFromGitiles(start, end, depot_name): |
| """Fetches a list of revision dicts from gitiles. |
| |
| Make multiple requests to get multiple pages, if necessary. |
| """ |
| revisions = [] |
| url = _URL_TEMPLATE % ( |
| depot_map.DEPOT_PATH_MAP[depot_name], start, end, _PAGE_SIZE) |
| current_page_url = url |
| while True: |
| response = urllib2.urlopen(current_page_url).read() |
| response_json = response[len(_GITILES_PADDING):] # Remove padding. |
| response_dict = json.loads(response_json) |
| revisions.extend(response_dict['log']) |
| if 'next' not in response_dict: |
| break |
| current_page_url = url + '&s=' + response_dict['next'] |
| return revisions |
| |
| |
| def _CommitPair(commit_dict): |
| return (commit_dict['commit'], |
| _CommitPositionFromMessage(commit_dict['message'])) |
| |
| |
| def _CommitPositionFromMessage(message): |
| for line in reversed(message.splitlines()): |
| if line.startswith('Cr-Commit-Position:'): |
| return line.split('#')[1].split('}')[0] |
| return None |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('start') |
| parser.add_argument('end') |
| parser.add_argument('depot', choices=list(depot_map.DEPOT_PATH_MAP)) |
| args = parser.parse_args() |
| revision_pairs = FetchInterveningRevisions(args.start, args.end, args.depot) |
| print json.dumps(revision_pairs) |
| |
| |
| if __name__ == '__main__': |
| main() |