1#!/usr/bin/python 2# 3# Copyright 2015 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Gets list of revisions between two commits and their commit positions. 8 9Example usage: 10 ./fetchInterveningRevisions.py 343b531d31 7b43807df3 chromium 11 ./fetchInterveningRevisions.py 235eff9574 1e4681c33f v8 12 13Note: Another implementation of this functionality can be found in 14findit/common/gitRepository.py (https://goo.gl/Rr8j9O). 15""" 16 17import argparse 18import json 19import urllib2 20 21from bisect_lib import depot_map 22 23_GITILES_PADDING = ')]}\'\n' 24_URL_TEMPLATE = ('https://chromium.googlesource.com/%s/+log/%s..%s' 25 '?format=json&n=%d') 26 27# Gitiles paginates the list of commits; since we want to get all of the 28# commits at once, the page size should be larger than the largest revision 29# range that we expect to get. 30_PAGE_SIZE = 512 31 32def FetchInterveningRevisions(start, end, depot_name): 33 """Fetches a list of revision in between two commits. 34 35 Args: 36 start (str): A git commit hash in the Chromium src repository. 37 end (str): Another git commit hash, after start. 38 depot_name (str): A respository name. 39 40 Returns: 41 A list of pairs (commit hash, commit position), from earliest to latest, 42 for all commits in between the two given commits, not including either 43 of the given commits. 44 45 Raises: 46 urllib2.URLError: The request to gitiles failed. 47 ValueError: The response wasn't valid JSON. 48 KeyError: The JSON didn't contain the expected data. 49 """ 50 revisions = _FetchRangeFromGitiles(start, end, depot_name) 51 # The response from gitiles includes the end revision and is ordered 52 # from latest to earliest. 53 return [_CommitPair(r) for r in reversed(revisions[1:])] 54 55 56def _FetchRangeFromGitiles(start, end, depot_name): 57 """Fetches a list of revision dicts from gitiles. 58 59 Make multiple requests to get multiple pages, if necessary. 60 """ 61 revisions = [] 62 url = _URL_TEMPLATE % ( 63 depot_map.DEPOT_PATH_MAP[depot_name], start, end, _PAGE_SIZE) 64 current_page_url = url 65 while True: 66 response = urllib2.urlopen(current_page_url).read() 67 response_json = response[len(_GITILES_PADDING):] # Remove padding. 68 response_dict = json.loads(response_json) 69 revisions.extend(response_dict['log']) 70 if 'next' not in response_dict: 71 break 72 current_page_url = url + '&s=' + response_dict['next'] 73 return revisions 74 75 76def _CommitPair(commit_dict): 77 return (commit_dict['commit'], 78 _CommitPositionFromMessage(commit_dict['message'])) 79 80 81def _CommitPositionFromMessage(message): 82 for line in reversed(message.splitlines()): 83 if line.startswith('Cr-Commit-Position:'): 84 return line.split('#')[1].split('}')[0] 85 return None 86 87 88def main(): 89 parser = argparse.ArgumentParser() 90 parser.add_argument('start') 91 parser.add_argument('end') 92 parser.add_argument('depot', choices=list(depot_map.DEPOT_PATH_MAP)) 93 args = parser.parse_args() 94 revision_pairs = FetchInterveningRevisions(args.start, args.end, args.depot) 95 print json.dumps(revision_pairs) 96 97 98if __name__ == '__main__': 99 main() 100