15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# coding=utf-8 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# (The line above is necessary so that I can use 世界 in the 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# *comment* below without Python getting all bent out of shape.) 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2007-2009 Google Inc. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License"); 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License. 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# http://www.apache.org/licenses/LICENSE-2.0 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS, 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License. 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)'''Mercurial interface to codereview.appspot.com. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)To configure, set the following options in 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)your repository's .hg/hgrc file. 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [extensions] 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) codereview = /path/to/codereview.py 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [codereview] 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) server = codereview.appspot.com 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The server should be running Rietveld; see http://code.google.com/p/rietveld/. 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)In addition to the new commands, this extension introduces 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the file pattern syntax @nnnnnn, where nnnnnn is a change list 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)number, to mean the files included in that change list, which 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)must be associated with the current client. 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)For example, if change 123456 contains the files x.go and y.go, 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"hg diff @123456" is equivalent to"hg diff x.go y.go". 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)''' 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == "__main__": 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "This is a Mercurial extension and should not be invoked directly." 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(2) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We require Python 2.6 for the json package. 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if sys.version < '2.6': 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "The codereview extension requires Python 2.6 or newer." 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "You are running Python " + sys.version 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(2) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import json 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import stat 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import threading 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial import commands as hg_commands 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial import util as hg_util 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)defaultcc = None 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)codereview_disabled = None 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)real_rollback = None 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)releaseBranch = None 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)server = "codereview.appspot.com" 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)server_url_base = None 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Normally I would split this into multiple files, but it simplifies 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# import path headaches to keep it all in one file. Sorry. 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The different parts of the file are separated by banners like this one. 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Helpers 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RelativePath(path, cwd): 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) n = len(cwd) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if path.startswith(cwd) and path[n] == '/': 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return path[n+1:] 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return path 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Sub(l1, l2): 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [l for l in l1 if l not in l2] 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Add(l1, l2): 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l = l1 + Sub(l2, l1) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l.sort() 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Intersect(l1, l2): 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [l for l in l1 if l in l2] 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# RE: UNICODE STRING HANDLING 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Python distinguishes between the str (string of bytes) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# and unicode (string of code points) types. Most operations 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# work on either one just fine, but some (like regexp matching) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# require unicode, and others (like write) require str. 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# As befits the language, Python hides the distinction between 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# unicode and str by converting between them silently, but 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# *only* if all the bytes/code points involved are 7-bit ASCII. 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This means that if you're not careful, your program works 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# fine on "hello, world" and fails on "hello, 世界". And of course, 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the obvious way to be careful - use static types - is unavailable. 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# So the only way is trial and error to find where to put explicit 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# conversions. 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Because more functions do implicit conversion to str (string of bytes) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# than do implicit conversion to unicode (string of code points), 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the convention in this module is to represent all text as str, 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# converting to unicode only when calling a unicode-only function 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# and then converting back to str as soon as possible. 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def typecheck(s, t): 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(s) != t: 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("type check failed: %s has type %s != %s" % (repr(s), type(s), t)) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If we have to pass unicode instead of str, ustr does that conversion clearly. 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ustr(s): 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s.decode("utf-8") 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Even with those, Mercurial still sometimes turns unicode into str 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# and then tries to use it as ascii. Change Mercurial's default. 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def set_mercurial_encoding_to_utf8(): 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from mercurial import encoding 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) encoding.encoding = 'utf-8' 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set_mercurial_encoding_to_utf8() 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Even with those we still run into problems. 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# I tried to do things by the book but could not convince 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Mercurial to let me check in a change with UTF-8 in the 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# CL description or author field, no matter how many conversions 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# between str and unicode I inserted and despite changing the 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# default encoding. I'm tired of this game, so set the default 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# encoding for all of Python to 'utf-8', not 'ascii'. 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def default_to_utf8(): 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import sys 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stdout, __stdout__ = sys.stdout, sys.__stdout__ 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reload(sys) # site.py deleted setdefaultencoding; get it back 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.stdout, sys.__stdout__ = stdout, __stdout__ 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.setdefaultencoding('utf-8') 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)default_to_utf8() 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Status printer for long-running commands 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)global_status = None 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def set_status(s): 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # print >>sys.stderr, "\t", time.asctime(), s 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global global_status 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global_status = s 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class StatusThread(threading.Thread): 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self): 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) threading.Thread.__init__(self) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def run(self): 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # pause a reasonable amount of time before 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # starting to display status messages, so that 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # most hg commands won't ever see them. 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time.sleep(30) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # now show status every 15 seconds 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while True: 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time.sleep(15 - time.time() % 15) 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = global_status 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if s is None: 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if s == "": 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = "(unknown status)" 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, time.asctime(), s 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def start_status_thread(): 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = StatusThread() 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.setDaemon(True) # allowed to exit if t is still running 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.start() 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Change list parsing. 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Change lists are stored in .hg/codereview/cl.nnnnnn 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# where nnnnnn is the number assigned by the code review server. 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Most data about a change list is stored on the code review server 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# too: the description, reviewer, and cc list are all stored there. 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The only thing in the cl.nnnnnn file is the list of relevant files. 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Also, the existence of the cl.nnnnnn file marks this repository 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# as the one where the change list lives. 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)emptydiff = """Index: ~rietveld~placeholder~ 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)=================================================================== 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)diff --git a/~rietveld~placeholder~ b/~rietveld~placeholder~ 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)new file mode 100644 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class CL(object): 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, name): 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(name, str) 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.name = name 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.desc = '' 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.files = [] 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.reviewer = [] 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cc = [] 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.url = '' 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.local = False 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.web = False 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.copied_from = None # None means current user 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.mailed = False 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.private = False 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.lgtm = [] 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def DiskText(self): 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = self 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = "" 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.copied_from: 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Author: " + cl.copied_from + "\n\n" 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.private: 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Private: " + str(self.private) + "\n" 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Mailed: " + str(self.mailed) + "\n" 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Description:\n" 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += Indent(cl.desc, "\t") 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Files:\n" 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in cl.files: 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\t" + f + "\n" 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def EditorText(self): 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = self 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = _change_prolog 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\n" 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.copied_from: 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Author: " + cl.copied_from + "\n" 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.url != '': 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += 'URL: ' + cl.url + ' # cannot edit\n\n' 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.private: 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Private: True\n" 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Reviewer: " + JoinComma(cl.reviewer) + "\n" 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "CC: " + JoinComma(cl.cc) + "\n" 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\n" 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Description:\n" 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.desc == '': 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\t<enter description here>\n" 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += Indent(cl.desc, "\t") 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\n" 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.local or cl.name == "new": 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "Files:\n" 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in cl.files: 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\t" + f + "\n" 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\n" 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def PendingText(self, quick=False): 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = self 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = cl.name + ":" + "\n" 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += Indent(cl.desc, "\t") 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\n" 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.copied_from: 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\tAuthor: " + cl.copied_from + "\n" 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not quick: 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n" 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (who, line) in cl.lgtm: 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\t\t" + who + ": " + line + "\n" 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\tCC: " + JoinComma(cl.cc) + "\n" 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\tFiles:\n" 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in cl.files: 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\t\t" + f + "\n" 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Flush(self, ui, repo): 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.name == "new": 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Upload(ui, repo, gofmt_just_warn=True, creating=True) 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dir = CodeReviewDir(ui, repo) 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path = dir + '/cl.' + self.name 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = open(path+'!', "w") 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.write(self.DiskText()) 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.close() 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sys.platform == "win32" and os.path.isfile(path): 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.remove(path) 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.rename(path+'!', path) 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.web and not self.copied_from: 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EditDesc(self.name, desc=self.desc, 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reviewers=JoinComma(self.reviewer), cc=JoinComma(self.cc), 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private=self.private) 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Delete(self, ui, repo): 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dir = CodeReviewDir(ui, repo) 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.unlink(dir + "/cl." + self.name) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Subject(self): 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = line1(self.desc) 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(s) > 60: 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = s[0:55] + "..." 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.name != "new": 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = "code review %s: %s" % (self.name, s) 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=False, creating=False, quiet=False): 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not self.files and not creating: 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("no files in change list\n") 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ui.configbool("codereview", "force_gofmt", True) and gofmt: 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CheckFormat(ui, repo, self.files, just_warn=gofmt_just_warn) 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading CL metadata + diffs") 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.chdir(repo.root) 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields = [ 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("content_upload", "1"), 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("reviewers", JoinComma(self.reviewer)), 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("cc", JoinComma(self.cc)), 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("description", self.desc), 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("base_hashes", ""), 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ] 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.name != "new": 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("issue", self.name)) 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vcs = None 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # We do not include files when creating the issue, 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # because we want the patch sets to record the repository 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # and base revision they are diffs against. We use the patch 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # set message for that purpose, but there is no message with 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # the first patch set. Instead the message gets used as the 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # new CL's overall subject. So omit the diffs when creating 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # and then we'll run an immediate upload. 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # This has the effect that every CL begins with an empty "Patch set 1". 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.files and not creating: 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vcs = MercurialVCS(upload_options, ui, repo) 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data = vcs.GenerateDiff(self.files) 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = vcs.GetBaseFiles(data) 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(data) > MAX_UPLOAD_SIZE: 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uploaded_diff_file = [] 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("separate_patches", "1")) 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uploaded_diff_file = [("data", "data.diff", data)] 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uploaded_diff_file = [("data", "data.diff", emptydiff)] 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if vcs and self.name != "new": 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("subject", "diff -r " + vcs.base_rev + " " + ui.expandpath("default"))) 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # First upload sets the subject for the CL itself. 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("subject", self.Subject())) 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file) 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_body = MySend("/upload", body, content_type=ctype) 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patchset = None 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = response_body 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines = msg.splitlines() 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(lines) >= 2: 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = lines[0] 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patchset = lines[1].strip() 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches = [x.split(" ", 1) for x in lines[2:]] 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if response_body.startswith("Issue updated.") and quiet: 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status(msg + "\n") 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploaded CL metadata + diffs") 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not response_body.startswith("Issue created.") and not response_body.startswith("Issue updated."): 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("failed to update issue: " + response_body) 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) issue = msg[msg.rfind("/")+1:] 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.name = issue 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not self.url: 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.url = server_url_base + self.name 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not uploaded_diff_file: 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading patches") 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches = UploadSeparatePatches(issue, rpc, patchset, data, upload_options) 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if vcs: 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading base files") 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vcs.UploadBaseFiles(issue, rpc, patches, patchset, upload_options, files) 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if send_mail: 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("sending mail") 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MySend("/" + issue + "/mail", payload="") 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.web = True 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("flushing changes to disk") 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Flush(ui, repo) 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Mail(self, ui, repo): 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg = "Hello " + JoinComma(self.reviewer) 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.cc: 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg += " (cc: %s)" % (', '.join(self.cc),) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg += ",\n" 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg += "\n" 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repourl = ui.expandpath("default") 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not self.mailed: 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg += "I'd like you to review this change to\n" + repourl + "\n" 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg += "Please take another look.\n" 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(pmsg, str) 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PostMessage(ui, self.name, pmsg, subject=self.Subject()) 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.mailed = True 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Flush(ui, repo) 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GoodCLName(name): 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(name, str) 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return re.match("^[0-9]+$", name) 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ParseCL(text, name): 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(text, str) 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(name, str) 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sname = None 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineno = 0 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sections = { 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Author': '', 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Description': '', 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Files': '', 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'URL': '', 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Reviewer': '', 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'CC': '', 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Mailed': '', 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Private': '', 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in text.split('\n'): 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineno += 1 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line.rstrip() 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line != '' and line[0] == '#': 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line == '' or line[0] == ' ' or line[0] == '\t': 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sname == None and line != '': 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, lineno, 'text outside section' 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sname != None: 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sections[sname] += line + '\n' 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p = line.find(':') 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if p >= 0: 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s, val = line[:p].strip(), line[p+1:].strip() 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if s in sections: 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sname = s 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if val != '': 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sections[sname] += val + '\n' 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, lineno, 'malformed section header' 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k in sections: 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sections[k] = StripCommon(sections[k]).rstrip() 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = CL(name) 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sections['Author']: 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.copied_from = sections['Author'] 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = sections['Description'] 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in sections['Files'].split('\n'): 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i = line.find('#') 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if i >= 0: 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line[0:i].rstrip() 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line.strip() 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line == '': 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files.append(line) 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = SplitCommaSpace(sections['Reviewer']) 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = SplitCommaSpace(sections['CC']) 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.url = sections['URL'] 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sections['Mailed'] != 'False': 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Odd default, but avoids spurious mailings when 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # reading old CLs that do not have a Mailed: line. 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # CLs created with this update will always have 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Mailed: False on disk. 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.mailed = True 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sections['Private'] in ('True', 'true', 'Yes', 'yes'): 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.private = True 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.desc == '<enter description here>': 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = '' 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cl, 0, '' 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def SplitCommaSpace(s): 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = s.strip() 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if s == "": 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [] 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return re.split(", *", s) 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CutDomain(s): 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i = s.find('@') 4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if i >= 0: 4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = s[0:i] 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def JoinComma(l): 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for s in l: 4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(s, str) 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ", ".join(l) 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ExceptionDetail(): 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = str(sys.exc_info()[0]) 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if s.startswith("<type '") and s.endswith("'>"): 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = s[7:-2] 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif s.startswith("<class '") and s.endswith("'>"): 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = s[8:-2] 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arg = str(sys.exc_info()[1]) 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(arg) > 0: 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += ": " + arg 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IsLocalCL(ui, repo, name): 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GoodCLName(name) and os.access(CodeReviewDir(ui, repo) + "/cl." + name, 0) 5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Load CL from disk and/or the web. 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def LoadCL(ui, repo, name, web=True): 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(name, str) 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("loading CL " + name) 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not GoodCLName(name): 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "invalid CL name" 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dir = CodeReviewDir(ui, repo) 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path = dir + "cl." + name 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.access(path, 0): 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ff = open(path) 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) text = ff.read() 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ff.close() 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, lineno, err = ParseCL(text, name) 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "malformed CL data: "+err 5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.local = True 5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = CL(name) 5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if web: 5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("getting issue metadata from web") 5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d = JSONGet(ui, "/api/" + name + "?messages=true") 5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status(None) 5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if d is None: 5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "cannot load CL %s from server" % (name,) 5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if 'owner_email' not in d or 'issue' not in d or str(d['issue']) != name: 5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "malformed response loading CL data from code review server" 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.dict = d 5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = d.get('reviewers', []) 5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = d.get('cc', []) 5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.local and cl.copied_from and cl.desc: 5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # local copy of CL written by someone else 5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # and we saved a description. use that one, 5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # so that committers can edit the description 5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # before doing hg submit. 5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = d.get('description', "") 5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.url = server_url_base + name 5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.web = True 5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.private = d.get('private', False) != False 5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.lgtm = [] 5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for m in d.get('messages', []): 5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m.get('approval', False) == True: 5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) who = re.sub('@.*', '', m.get('sender', '')) 5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) text = re.sub("\n(.|\n)*", '', m.get('text', '')) 5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.lgtm.append((who, text)) 5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("loaded CL " + name) 5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cl, '' 5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LoadCLThread(threading.Thread): 5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, ui, repo, dir, f, web): 5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) threading.Thread.__init__(self) 5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.ui = ui 5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.repo = repo 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.dir = dir 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.f = f 5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.web = web 5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cl = None 5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def run(self): 5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(self.ui, self.repo, self.f[3:], web=self.web) 5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.ui.warn("loading "+self.dir+self.f+": " + err + "\n") 5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cl = cl 5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Load all the CLs from this repository. 5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def LoadAllCL(ui, repo, web=True): 5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dir = CodeReviewDir(ui, repo) 5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = {} 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [f for f in os.listdir(dir) if f.startswith('cl.')] 5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return m 5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) active = [] 5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) first = True 5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in files: 5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = LoadCLThread(ui, repo, dir, f, web) 5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.start() 5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if web and first: 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # first request: wait in case it needs to authenticate 5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # otherwise we get lots of user/password prompts 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # running in parallel. 5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.join() 5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if t.cl: 5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m[t.cl.name] = t.cl 5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) first = False 5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) active.append(t) 5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for t in active: 5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.join() 5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if t.cl: 5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m[t.cl.name] = t.cl 5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return m 5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Find repository root. On error, ui.warn and return None 6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RepoDir(ui, repo): 6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = repo.url(); 6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not url.startswith('file:'): 6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("repository %s is not in local file system\n" % (url,)) 6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = url[5:] 6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if url.endswith('/'): 6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = url[:-1] 6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(url, str) 6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return url 6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Find (or make) code review directory. On error, ui.warn and return None 6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CodeReviewDir(ui, repo): 6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dir = RepoDir(ui, repo) 6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if dir == None: 6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dir += '/.hg/codereview/' 6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not os.path.isdir(dir): 6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.mkdir(dir, 0700) 6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn('cannot mkdir %s: %s\n' % (dir, ExceptionDetail())) 6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(dir, str) 6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return dir 6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Turn leading tabs into spaces, so that the common white space 6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# prefix doesn't get confused when people's editors write out 6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# some lines with spaces, some with tabs. Only a heuristic 6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# (some editors don't use 8 spaces either) but a useful one. 6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def TabsToSpaces(line): 6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i = 0 6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while i < len(line) and line[i] == '\t': 6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i += 1 6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ' '*(8*i) + line[i:] 6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Strip maximal common leading white space prefix from text 6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def StripCommon(text): 6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(text, str) 6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ws = None 6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in text.split('\n'): 6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line.rstrip() 6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line == '': 6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = TabsToSpaces(line) 6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) white = line[:len(line)-len(line.lstrip())] 6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ws == None: 6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ws = white 6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) common = '' 6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(min(len(white), len(ws))+1): 6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if white[0:i] == ws[0:i]: 6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) common = white[0:i] 6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ws = common 6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ws == '': 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ws == None: 6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return text 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = '' 6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in text.split('\n'): 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line.rstrip() 6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = TabsToSpaces(line) 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith(ws): 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = line[len(ws):] 6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line == '' and t == '': 6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t += line + '\n' 6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while len(t) >= 2 and t[-2:] == '\n\n': 6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = t[:-1] 6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(t, str) 6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return t 6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Indent text with indent. 6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Indent(text, indent): 6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(text, str) 6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(indent, str) 6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = '' 6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in text.split('\n'): 6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t += indent + line + '\n' 6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(t, str) 6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return t 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return the first line of l 6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def line1(text): 6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(text, str) 6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return text.split('\n')[0] 6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_change_prolog = """# Change list. 6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Lines beginning with # are ignored. 6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Multi-line values should be indented. 6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)desc_re = '^(.+: |(tag )?(release|weekly)\.|fix build|undo CL)' 6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)desc_msg = '''Your CL description appears not to use the standard form. 6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The first line of your change description is conventionally a 6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)one-line summary of the change, prefixed by the primary affected package, 6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)and is used as the subject for code review mail; the rest of the description 6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elaborates. 7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Examples: 7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) encoding/rot13: new package 7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) math: add IsInf, IsNaN 7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net: fix cname in LookupHost 7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unicode: update to Unicode 5.0.2 7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)''' 7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def promptyesno(ui, msg): 7141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if hgversion >= "2.7": 7151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ui.promptchoice(msg + " $$ &yes $$ &no", 0) == 0 7161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci else: 7171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ui.promptchoice(msg, ["&yes", "&no"], 0) == 0 7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def promptremove(ui, repo, f): 7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if promptyesno(ui, "hg remove %s (y/n)?" % (f,)): 7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if hg_commands.remove(ui, repo, 'path:'+f) != 0: 7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("error removing %s" % (f,)) 7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def promptadd(ui, repo, f): 7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if promptyesno(ui, "hg add %s (y/n)?" % (f,)): 7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if hg_commands.add(ui, repo, 'path:'+f) != 0: 7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("error adding %s" % (f,)) 7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def EditCL(ui, repo, cl): 7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status(None) # do not show status 7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = cl.EditorText() 7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while True: 7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = ui.edit(s, ui.username()) 7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # We can't trust Mercurial + Python not to die before making the change, 7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # so, by popular demand, just scribble the most recent CL edit into 7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # $(hg root)/last-change so that if Mercurial does die, people 7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # can look there for their work. 7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = open(repo.root+"/last-change", "w") 7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.write(s) 7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.close() 7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clx, line, err = ParseCL(s, cl.name) 7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not promptyesno(ui, "error parsing change list: line %d: %s\nre-edit (y/n)?" % (line, err)): 7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "change list not modified" 7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check description. 7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clx.desc == '': 7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if promptyesno(ui, "change list should have a description\nre-edit (y/n)?"): 7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif re.search('<enter reason for undo>', clx.desc): 7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if promptyesno(ui, "change list description omits reason for undo\nre-edit (y/n)?"): 7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif not re.match(desc_re, clx.desc.split('\n')[0]): 7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if promptyesno(ui, desc_msg + "re-edit (y/n)?"): 7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check file list for files that need to be hg added or hg removed 7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # or simply aren't understood. 7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pats = ['path:'+f for f in clx.files] 7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) changed = hg_matchPattern(ui, repo, *pats, modified=True, added=True, removed=True) 7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) deleted = hg_matchPattern(ui, repo, *pats, deleted=True) 7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unknown = hg_matchPattern(ui, repo, *pats, unknown=True) 7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ignored = hg_matchPattern(ui, repo, *pats, ignored=True) 7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clean = hg_matchPattern(ui, repo, *pats, clean=True) 7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [] 7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in clx.files: 7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in changed: 7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files.append(f) 7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in deleted: 7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) promptremove(ui, repo, f) 7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files.append(f) 7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in unknown: 7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) promptadd(ui, repo, f) 7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files.append(f) 7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in ignored: 7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("error: %s is excluded by .hgignore; omitting\n" % (f,)) 7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in clean: 7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: %s is listed in the CL but unchanged\n" % (f,)) 7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files.append(f) 7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p = repo.root + '/' + f 7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.path.isfile(p): 7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: %s is a file but not known to hg\n" % (f,)) 7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files.append(f) 7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.path.isdir(p): 7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("error: %s is a directory, not a file; omitting\n" % (f,)) 7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("error: %s does not exist; omitting\n" % (f,)) 8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clx.files = files 8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = clx.desc 8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = clx.reviewer 8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = clx.cc 8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = clx.files 8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.private = clx.private 8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "" 8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# For use by submit, etc. (NOT by change) 8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Get change list number or list of files from command line. 8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If files are given, make a new change list. 8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CommandLineCL(ui, repo, pats, opts, defaultcc=None): 8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(pats) > 0 and GoodCLName(pats[0]): 8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(pats) != 1: 8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "cannot specify change number and file names" 8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts.get('message'): 8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "cannot use -m with existing CL" 8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(ui, repo, pats[0], web=True) 8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, err 8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = CL("new") 8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.local = True 8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = ChangedFiles(ui, repo, pats, taken=Taken(ui, repo)) 8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.files: 8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "no files changed" 8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts.get('reviewer'): 8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = Add(cl.reviewer, SplitCommaSpace(opts.get('reviewer'))) 8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts.get('cc'): 8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = Add(cl.cc, SplitCommaSpace(opts.get('cc'))) 8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if defaultcc: 8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = Add(cl.cc, defaultcc) 8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.name == "new": 8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts.get('message'): 8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = opts.get('message') 8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = EditCL(ui, repo, cl) 8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, err 8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cl, "" 8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Change list file management 8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return list of changed files in repository that match pats. 8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The patterns came from the command line, so we warn 8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# if they have no effect or cannot be understood. 8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ChangedFiles(ui, repo, pats, taken=None): 8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) taken = taken or {} 8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Run each pattern separately so that we can warn about 8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # patterns that didn't do anything useful. 8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for p in pats: 8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in hg_matchPattern(ui, repo, p, unknown=True): 8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) promptadd(ui, repo, f) 8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in hg_matchPattern(ui, repo, p, removed=True): 8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) promptremove(ui, repo, f) 8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = hg_matchPattern(ui, repo, p, modified=True, added=True, removed=True) 8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in files: 8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in taken: 8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: %s already in CL %s\n" % (f, taken[f].name)) 8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: %s did not match any modified files\n" % (p,)) 8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Again, all at once (eliminates duplicates) 8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l = hg_matchPattern(ui, repo, *pats, modified=True, added=True, removed=True) 8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l.sort() 8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if taken: 8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l = Sub(l, taken.keys()) 8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l 8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return list of changed files in repository that match pats and still exist. 8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ChangedExistingFiles(ui, repo, pats, opts): 8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l = hg_matchPattern(ui, repo, *pats, modified=True, added=True) 8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l.sort() 8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l 8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return list of files claimed by existing CLs 8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Taken(ui, repo): 8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) all = LoadAllCL(ui, repo, web=False) 8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) taken = {} 8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for _, cl in all.items(): 8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in cl.files: 8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) taken[f] = cl 8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return taken 8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return list of changed files that are not claimed by other CLs 8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def DefaultFiles(ui, repo, pats): 8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ChangedFiles(ui, repo, pats, taken=Taken(ui, repo)) 8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# File format checking. 8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CheckFormat(ui, repo, files, just_warn=False): 8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("running gofmt") 8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CheckGofmt(ui, repo, files, just_warn) 8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CheckTabfmt(ui, repo, files, just_warn) 8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Check that gofmt run on the list of files does not change them 9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CheckGofmt(ui, repo, files, just_warn): 9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = gofmt_required(files) 9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cwd = os.getcwd() 9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [RelativePath(repo.root + '/' + f, cwd) for f in files] 9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [f for f in files if os.access(f, 0)] 9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd = subprocess.Popen(["gofmt", "-l"] + files, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=sys.platform != "win32") 9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd.stdin.close() 9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("gofmt: " + ExceptionDetail()) 9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data = cmd.stdout.read() 9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors = cmd.stderr.read() 9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd.wait() 9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("done with gofmt") 9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(errors) > 0: 9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("gofmt errors:\n" + errors.rstrip() + "\n") 9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(data) > 0: 9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = "gofmt needs to format these files (run hg gofmt):\n" + Indent(data, "\t").rstrip() 9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if just_warn: 9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: " + msg + "\n") 9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(msg) 9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Check that *.[chys] files indent using tabs. 9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CheckTabfmt(ui, repo, files, just_warn): 9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [f for f in files if f.startswith('src/') and re.search(r"\.[chys]$", f) and not re.search(r"\.tab\.[ch]$", f)] 9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cwd = os.getcwd() 9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [RelativePath(repo.root + '/' + f, cwd) for f in files] 9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [f for f in files if os.access(f, 0)] 9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) badfiles = [] 9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in files: 9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in open(f, 'r'): 9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Four leading spaces is enough to complain about, 9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # except that some Plan 9 code uses four spaces as the label indent, 9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # so allow that. 9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith(' ') and not re.match(' [A-Za-z0-9_]+:', line): 9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) badfiles.append(f) 9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # ignore cannot open file, etc. 9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(badfiles) > 0: 9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = "these files use spaces for indentation (use tabs instead):\n\t" + "\n\t".join(badfiles) 9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if just_warn: 9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: " + msg + "\n") 9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(msg) 9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# CONTRIBUTORS file parsing 9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)contributorsCache = None 9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)contributorsURL = None 9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ReadContributors(ui, repo): 9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global contributorsCache 9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if contributorsCache is not None: 9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return contributorsCache 9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if contributorsURL is not None: 9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opening = contributorsURL 9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = urllib2.urlopen(contributorsURL) 9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opening = repo.root + '/CONTRIBUTORS' 9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = open(repo.root + '/CONTRIBUTORS', 'r') 9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write("warning: cannot open %s: %s\n" % (opening, ExceptionDetail())) 9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) contributors = {} 9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in f: 9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # CONTRIBUTORS is a list of lines like: 9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Person <email> 9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Person <email> <alt-email> 9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # The first email address is the one used in commit logs. 9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('#'): 9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match(r"([^<>]+\S)\s+(<[^<>\s]+>)((\s+<[^<>\s]+>)*)\s*$", line) 9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m: 9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name = m.group(1) 9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = m.group(2)[1:-1] 9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) contributors[email.lower()] = (name, email) 9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for extra in m.group(3).split(): 9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) contributors[extra[1:-1].lower()] = (name, email) 9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) contributorsCache = contributors 9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return contributors 9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CheckContributor(ui, repo, user=None): 10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("checking CONTRIBUTORS file") 10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user, userline = FindContributor(ui, repo, user, warn=False) 10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not userline: 10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("cannot find %s in CONTRIBUTORS" % (user,)) 10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return userline 10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def FindContributor(ui, repo, user=None, warn=True): 10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not user: 10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user = ui.config("ui", "username") 10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not user: 10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("[ui] username is not configured in .hgrc") 10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user = user.lower() 10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match(r".*<(.*)>", user) 10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m: 10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user = m.group(1) 10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) contributors = ReadContributors(ui, repo) 10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if user not in contributors: 10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if warn: 10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: cannot find %s in CONTRIBUTORS\n" % (user,)) 10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return user, None 10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user, email = contributors[user] 10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return email, "%s <%s>" % (user, email) 10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Mercurial helper functions. 10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Read http://mercurial.selenic.com/wiki/MercurialApi before writing any of these. 10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We use the ui.pushbuffer/ui.popbuffer + hg_commands.xxx tricks for all interaction 10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# with Mercurial. It has proved the most stable as they make changes. 10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)hgversion = hg_util.version() 10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We require Mercurial 1.9 and suggest Mercurial 2.0. 10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The details of the scmutil package changed then, 10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# so allowing earlier versions would require extra band-aids below. 10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Ubuntu 11.10 ships with Mercurial 1.9.1 as the default version. 10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)hg_required = "1.9" 10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)hg_suggested = "2.0" 10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)old_message = """ 10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The code review extension requires Mercurial """+hg_required+""" or newer. 10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)You are using Mercurial """+hgversion+""". 10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)To install a new Mercurial, use 10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sudo easy_install mercurial=="""+hg_suggested+""" 10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)or visit http://mercurial.selenic.com/downloads/. 10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)linux_message = """ 10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)You may need to clear your current Mercurial installation by running: 10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sudo apt-get remove mercurial mercurial-common 10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sudo rm -rf /etc/mercurial 10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if hgversion < hg_required: 10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = old_message 10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.access("/etc/mercurial", 0): 10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg += linux_message 10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(msg) 10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial.hg import clean as hg_clean 10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial import cmdutil as hg_cmdutil 10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial import error as hg_error 10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial import match as hg_match 10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from mercurial import node as hg_node 10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class uiwrap(object): 10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, ui): 10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.ui = ui 10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.pushbuffer() 10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.oldQuiet = ui.quiet 10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.quiet = True 10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.oldVerbose = ui.verbose 10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.verbose = False 10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def output(self): 10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui = self.ui 10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.quiet = self.oldQuiet 10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.verbose = self.oldVerbose 10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ui.popbuffer() 10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def to_slash(path): 10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sys.platform == "win32": 10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return path.replace('\\', '/') 10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return path 10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_matchPattern(ui, repo, *pats, **opts): 10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg_commands.status(ui, repo, *pats, **opts) 10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) text = w.output() 10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret = [] 10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prefix = to_slash(os.path.realpath(repo.root))+'/' 10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in text.split('\n'): 10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = line.split() 10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(f) > 1: 10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(pats) > 0: 11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Given patterns, Mercurial shows relative to cwd 11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p = to_slash(os.path.realpath(f[1])) 11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not p.startswith(prefix): 11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "File %s not in repo root %s.\n" % (p, prefix) 11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret.append(p[len(prefix):]) 11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Without patterns, Mercurial shows relative to root (what we want) 11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret.append(to_slash(f[1])) 11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ret 11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_heads(ui, repo): 11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg_commands.heads(ui, repo) 11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w.output() 11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)noise = [ 11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "", 11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "resolving manifests", 11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "searching for changes", 11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "couldn't find merge tool hgmerge", 11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "adding changesets", 11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "adding manifests", 11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "adding file changes", 11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "all local heads known remotely", 11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)] 11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def isNoise(line): 11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = str(line) 11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for x in noise: 11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line == x: 11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return True 11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_incoming(ui, repo): 11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret = hg_commands.incoming(ui, repo, force=False, bundle="") 11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ret and ret != 1: 11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(ret) 11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w.output() 11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_log(ui, repo, **opts): 11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k in ['date', 'keyword', 'rev', 'user']: 11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not opts.has_key(k): 11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opts[k] = "" 11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret = hg_commands.log(ui, repo, **opts) 11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ret: 11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(ret) 11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w.output() 11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_outgoing(ui, repo, **opts): 11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret = hg_commands.outgoing(ui, repo, **opts) 11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ret and ret != 1: 11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(ret) 11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w.output() 11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_pull(ui, repo, **opts): 11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.quiet = False 11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.verbose = True # for file list 11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = hg_commands.pull(ui, repo, **opts) 11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in w.output().split('\n'): 11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if isNoise(line): 11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('moving '): 11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = 'mv ' + line[len('moving '):] 11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('getting ') and line.find(' to ') >= 0: 11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = 'mv ' + line[len('getting '):] 11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('getting '): 11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = '+ ' + line[len('getting '):] 11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('removing '): 11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = '- ' + line[len('removing '):] 11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(line + '\n') 11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_push(ui, repo, **opts): 11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w = uiwrap(ui) 11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.quiet = False 11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.verbose = True 11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = hg_commands.push(ui, repo, **opts) 11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in w.output().split('\n'): 11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not isNoise(line): 11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(line + '\n') 11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hg_commit(ui, repo, *pats, **opts): 11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return hg_commands.commit(ui, repo, *pats, **opts) 11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Mercurial precommit hook to disable commit except through this interface. 11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)commit_okay = False 11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def precommithook(ui, repo, **opts): 11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if commit_okay: 11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False # False means okay. 11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write("\ncodereview extension enabled; use mail, upload, or submit instead of commit\n\n") 11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return True 12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# @clnumber file pattern support 12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We replace scmutil.match with the MatchAt wrapper to add the @clnumber pattern. 12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)match_repo = None 12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)match_ui = None 12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)match_orig = None 12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def InstallMatch(ui, repo): 12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global match_repo 12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global match_ui 12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global match_orig 12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) match_ui = ui 12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) match_repo = repo 12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from mercurial import scmutil 12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) match_orig = scmutil.match 12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scmutil.match = MatchAt 12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MatchAt(ctx, pats=None, opts=None, globbed=False, default='relpath'): 12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) taken = [] 12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [] 12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pats = pats or [] 12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opts = opts or {} 12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for p in pats: 12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if p.startswith('@'): 12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) taken.append(p) 12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clname = p[1:] 12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clname == "default": 12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = DefaultFiles(match_ui, match_repo, []) 12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not GoodCLName(clname): 12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("invalid CL name " + clname) 12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(match_repo.ui, match_repo, clname, web=False) 12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("loading CL " + clname + ": " + err) 12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.files: 12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("no files in CL " + clname) 12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = Add(files, cl.files) 12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pats = Sub(pats, taken) + ['path:'+f for f in files] 12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # work-around for http://selenic.com/hg/rev/785bbc8634f8 12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not hasattr(ctx, 'match'): 12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx = ctx[None] 12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return match_orig(ctx, pats=pats, opts=opts, globbed=globbed, default=default) 12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Commands added by code review extension. 12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# As of Mercurial 2.1 the commands are all required to return integer 12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# exit codes, whereas earlier versions allowed returning arbitrary strings 12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to be printed as errors. We wrap the old functions to make sure we 12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# always return integer exit codes now. Otherwise Mercurial dies 12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# with a TypeError traceback (unsupported operand type(s) for &: 'str' and 'int'). 12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Introduce a Python decorator to convert old functions to the new 12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# stricter convention. 12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def hgcommand(f): 12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def wrapped(ui, repo, *pats, **opts): 12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = f(ui, repo, *pats, **opts) 12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(err) is int: 12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not err: 12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0 12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(err) 12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wrapped.__doc__ = f.__doc__ 12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return wrapped 12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg change 12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def change(ui, repo, *pats, **opts): 12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """create, edit or delete a change list 12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Create, edit or delete a change list. 12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A change list is a group of files to be reviewed and submitted together, 12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) plus a textual description of the change. 12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Change lists are referred to by simple alphanumeric names. 12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Changes must be reviewed before they can be submitted. 12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) In the absence of options, the change command opens the 12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) change list for editing in the default editor. 12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Deleting a change with the -d or -D flag does not affect 12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the contents of the files listed in that change. To revert 12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the files listed in a change, use 12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg revert @123456 12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) before running hg change -d 123456. 12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty = {} 13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(pats) > 0 and GoodCLName(pats[0]): 13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name = pats[0] 13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(pats) != 1: 13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot specify CL name and file patterns" 13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pats = pats[1:] 13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(ui, repo, name, web=True) 13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.local and (opts["stdin"] or not opts["stdout"]): 13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot change non-local CL " + name 13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name = "new" 13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = CL("new") 13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if repo[None].branch() != "default": 13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot create CL outside default branch; switch with 'hg update default'" 13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = ChangedFiles(ui, repo, pats, taken=Taken(ui, repo)) 13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["delete"] or opts["deletelocal"]: 13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["delete"] and opts["deletelocal"]: 13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot use -d and -D together" 13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) flag = "-d" 13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["deletelocal"]: 13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) flag = "-D" 13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if name == "new": 13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot use "+flag+" with file patterns" 13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["stdin"] or opts["stdout"]: 13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot use "+flag+" with -i or -o" 13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.local: 13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot change non-local CL " + name 13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["delete"]: 13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.copied_from: 13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "original author must delete CL; hg change -D will remove locally" 13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PostMessage(ui, cl.name, "*** Abandoned ***", send_mail=cl.mailed) 13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EditDesc(cl.name, closed=True, private=cl.private) 13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Delete(ui, repo) 13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["stdin"]: 13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = sys.stdin.read() 13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clx, line, err = ParseCL(s, name) 13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "error parsing change list: line %d: %s" % (line, err) 13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clx.desc is not None: 13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = clx.desc; 13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clx.reviewer is not None: 13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = clx.reviewer 13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clx.cc is not None: 13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = clx.cc 13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clx.files is not None: 13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = clx.files 13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clx.private != cl.private: 13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.private = clx.private 13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not opts["stdin"] and not opts["stdout"]: 13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if name == "new": 13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = files 13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = EditCL(ui, repo, cl) 13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for d, _ in dirty.items(): 13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name = d.name 13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d.Flush(ui, repo) 13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if name == "new": 13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d.Upload(ui, repo, quiet=True) 13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["stdout"]: 13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(cl.EditorText()) 13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif opts["pending"]: 13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(cl.PendingText()) 13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif name == "new": 13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ui.quiet: 13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(cl.name) 13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write("CL created: " + cl.url + "\n") 13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg code-login (broken?) 13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def code_login(ui, repo, **opts): 13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """log in to code review server 13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Logs in to the code review server, saving a cookie in 13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) a file in your home directory. 13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MySend(None) 14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg clpatch / undo / release-apply / download 14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# All concerned with applying or unapplying patches to the repository. 14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def clpatch(ui, repo, clname, **opts): 14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """import a patch from the code review server 14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Imports a patch from the code review server into the local client. 14105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) If the local client has already modified any of the files that the 14115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patch modifies, this command will refuse to apply the patch. 14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Submitting an imported patch will keep the original author's 14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name as the Author: line but add your own name to a Committer: line. 14155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 14165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if repo[None].branch() != "default": 14175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot run hg clpatch outside default branch" 14185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return clpatch_or_undo(ui, repo, clname, opts, mode="clpatch") 14195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 14215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def undo(ui, repo, clname, **opts): 14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """undo the effect of a CL 14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Creates a new CL that undoes an earlier CL. 14255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) After creating the CL, opens the CL text for editing so that 14265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) you can add the reason for the undo to the description. 14275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 14285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if repo[None].branch() != "default": 14295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot run hg undo outside default branch" 14305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return clpatch_or_undo(ui, repo, clname, opts, mode="undo") 14315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 14335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def release_apply(ui, repo, clname, **opts): 14345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """apply a CL to the release branch 14355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Creates a new CL copying a previously committed change 14375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from the main branch to the release branch. 14385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The current client must either be clean or already be in 14395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the release branch. 14405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The release branch must be created by starting with a 14425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clean client, disabling the code review plugin, and running: 14435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg update weekly.YYYY-MM-DD 14455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg branch release-branch.rNN 14465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg commit -m 'create release-branch.rNN' 14475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg push --new-branch 14485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Then re-enable the code review plugin. 14505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) People can test the release branch by running 14525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg update release-branch.rNN 14545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) in a clean client. To return to the normal tree, 14565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg update default 14585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Move changes since the weekly into the release branch 14605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) using hg release-apply followed by the usual code review 14615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) process and hg submit. 14625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) When it comes time to tag the release, record the 14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) final long-form tag of the release-branch.rNN 14655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) in the *default* branch's .hgtags file. That is, run 14665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg update default 14685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) and then edit .hgtags as you would for a weekly. 14705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 14725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c = repo[None] 14735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not releaseBranch: 14745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "no active release branches" 14755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if c.branch() != releaseBranch: 14765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if c.modified() or c.added() or c.removed(): 14775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("uncommitted local changes - cannot switch branches") 14785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = hg_clean(repo, releaseBranch) 14795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err: 14805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 14815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 14825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = clpatch_or_undo(ui, repo, clname, opts, mode="backport") 14835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err: 14845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(err) 14855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except Exception, e: 14865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg_clean(repo, "default") 14875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise e 14885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 14895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def rev2clname(rev): 14915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Extract CL name from revision description. 14925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # The last line in the description that is a codereview URL is the real one. 14935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Earlier lines might be part of the user-written description. 14945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) all = re.findall('(?m)^http://codereview.appspot.com/([0-9]+)$', rev.description()) 14955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(all) > 0: 14965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return all[-1] 14975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "" 14985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 14995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)undoHeader = """undo CL %s / %s 15005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<enter reason for undo> 15025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)««« original CL description 15045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 15055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)undoFooter = """ 15075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)»»» 15085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 15095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)backportHeader = """[%s] %s 15115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)««« CL %s / %s 15135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 15145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)backportFooter = """ 15165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)»»» 15175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 15185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Implementation of clpatch/undo. 15205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def clpatch_or_undo(ui, repo, clname, opts, mode): 15215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 15225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 15235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if mode == "undo" or mode == "backport": 15255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find revision in Mercurial repository. 15265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Assume CL number is 7+ decimal digits. 15275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Otherwise is either change log sequence number (fewer decimal digits), 15285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # hexadecimal hash, or tag name. 15295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Mercurial will fall over long before the change log 15305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # sequence numbers get to be 7 digits long. 15315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if re.match('^[0-9]{7,}$', clname): 15325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) found = False 15335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for r in hg_log(ui, repo, keyword="codereview.appspot.com/"+clname, limit=100, template="{node}\n").split(): 15345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rev = repo[r] 15355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Last line with a code review URL is the actual review URL. 15365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Earlier ones might be part of the CL description. 15375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) n = rev2clname(rev) 15385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if n == clname: 15395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) found = True 15405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 15415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not found: 15425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot find CL %s in local repository" % clname 15435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 15445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rev = repo[clname] 15455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not rev: 15465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "unknown revision %s" % clname 15475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clname = rev2clname(rev) 15485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if clname == "": 15495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot find CL name in revision description" 15505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Create fresh CL and start with patch that would reverse the change. 15525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vers = hg_node.short(rev.node()) 15535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = CL("new") 15545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) desc = str(rev.description()) 15555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if mode == "undo": 15565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = (undoHeader % (clname, vers)) + desc + undoFooter 15575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 15585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.desc = (backportHeader % (releaseBranch, line1(desc), clname, vers)) + desc + undoFooter 15595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) v1 = vers 15605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) v0 = hg_node.short(rev.parents()[0].node()) 15615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if mode == "undo": 15625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arg = v1 + ":" + v0 15635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 15645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vers = v0 15655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arg = v0 + ":" + v1 15665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patch = RunShell(["hg", "diff", "--git", "-r", arg]) 15675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: # clpatch 15695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, vers, patch, err = DownloadCL(ui, repo, clname) 15705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 15715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 15725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if patch == emptydiff: 15735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "codereview issue %s has no diff" % clname 15745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # find current hg version (hg identify) 15765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx = repo[None] 15775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parents = ctx.parents() 15785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) id = '+'.join([hg_node.short(p.node()) for p in parents]) 15795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # if version does not match the patch version, 15815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # try to update the patch line numbers. 15825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if vers != "" and id != vers: 15835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # "vers in repo" gives the wrong answer 15845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # on some versions of Mercurial. Instead, do the actual 15855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # lookup and catch the exception. 15865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 15875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repo[vers].description() 15885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 15895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "local repository is out of date; sync to get %s" % (vers) 15905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patch1, err = portPatch(repo, patch, vers, id) 15915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 15925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not opts["ignore_hgpatch_failure"]: 15935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "codereview issue %s is out of date: %s (%s->%s)" % (clname, err, vers, id) 15945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 15955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patch = patch1 15965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv = ["hgpatch"] 15975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["no_incoming"] or mode == "backport": 15985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv += ["--checksync=false"] 15995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 16005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32") 16015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 16025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "hgpatch: " + ExceptionDetail() + "\nInstall hgpatch with:\n$ go get code.google.com/p/go.codereview/cmd/hgpatch\n" 16035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out, err = cmd.communicate(patch) 16055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cmd.returncode != 0 and not opts["ignore_hgpatch_failure"]: 16065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "hgpatch failed" 16075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.local = True 16085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = out.strip().split() 16095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.files and not opts["ignore_hgpatch_failure"]: 16105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "codereview issue %s has no changed files" % clname 16115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = ChangedFiles(ui, repo, []) 16125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra = Sub(cl.files, files) 16135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if extra: 16145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("warning: these files were listed in the patch but not changed:\n\t" + "\n\t".join(extra) + "\n") 16155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Flush(ui, repo) 16165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if mode == "undo": 16175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = EditCL(ui, repo, cl) 16185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 16195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "CL created, but error editing: " + err 16205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Flush(ui, repo) 16215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 16225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(cl.PendingText() + "\n") 16235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# portPatch rewrites patch from being a patch against 16255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# oldver to being a patch against newver. 16265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def portPatch(repo, patch, oldver, newver): 16275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines = patch.splitlines(True) # True = keep \n 16285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delta = None 16295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(lines)): 16305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = lines[i] 16315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('--- a/'): 16325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file = line[6:-1] 16335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delta = fileDeltas(repo, file, oldver, newver) 16345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not delta or not line.startswith('@@ '): 16355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 16365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # @@ -x,y +z,w @@ means the patch chunk replaces 16375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # the original file's line numbers x up to x+y with the 16385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # line numbers z up to z+w in the new file. 16395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find the delta from x in the original to the same 16405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # line in the current version and add that delta to both 16415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # x and z. 16425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line) 16435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not m: 16445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, "error parsing patch line numbers" 16455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) 16465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d, err = lineDelta(delta, n1, len1) 16475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 16485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "", err 16495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) n1 += d 16505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) n2 += d 16515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines[i] = "@@ -%d,%d +%d,%d @@\n" % (n1, len1, n2, len2) 16525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) newpatch = ''.join(lines) 16545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return newpatch, "" 16555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# fileDelta returns the line number deltas for the given file's 16575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# changes from oldver to newver. 16585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The deltas are a list of (n, len, newdelta) triples that say 16595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# lines [n, n+len) were modified, and after that range the 16605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# line numbers are +newdelta from what they were before. 16615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def fileDeltas(repo, file, oldver, newver): 16625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd = ["hg", "diff", "--git", "-r", oldver + ":" + newver, "path:" + file] 16635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data = RunShell(cmd, silent_ok=True) 16645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) deltas = [] 16655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in data.splitlines(): 16665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line) 16675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not m: 16685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 16695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) 16705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) deltas.append((n1, len1, n2+len2-(n1+len1))) 16715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return deltas 16725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# lineDelta finds the appropriate line number delta to apply to the lines [n, n+len). 16745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# It returns an error if those lines were rewritten by the patch. 16755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def lineDelta(deltas, n, len): 16765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d = 0 16775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (old, oldlen, newdelta) in deltas: 16785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if old >= n+len: 16795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 16805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if old+len > n: 16815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0, "patch and recent changes conflict" 16825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d = newdelta 16835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return d, "" 16845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 16865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def download(ui, repo, clname, **opts): 16875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """download a change from the code review server 16885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Download prints a description of the given change list 16905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) followed by its diff, downloaded from the code review server. 16915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 16925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 16935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 16945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, vers, patch, err = DownloadCL(ui, repo, clname) 16965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 16975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 16985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(cl.EditorText() + "\n") 16995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(patch + "\n") 17005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 17015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 17035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg file 17045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 17065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def file(ui, repo, clname, pat, *pats, **opts): 17075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """assign files to or remove files from a change list 17085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Assign files to or (with -d) remove files from a change list. 17105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The -d option only removes files from the change list. 17125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) It does not edit them or remove them from the repository. 17135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 17145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 17155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 17165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pats = tuple([pat] + list(pats)) 17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not GoodCLName(clname): 17195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "invalid CL name " + clname 17205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty = {} 17225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(ui, repo, clname, web=False) 17235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != '': 17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.local: 17265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot change non-local CL " + clname 17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = ChangedFiles(ui, repo, pats) 17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts["delete"]: 17315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) oldfiles = Intersect(files, cl.files) 17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if oldfiles: 17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not ui.quiet: 17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("# Removing files from CL. To undo:\n") 17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("# cd %s\n" % (repo.root)) 17365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in oldfiles: 17375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("# hg file %s %s\n" % (cl.name, f)) 17385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = Sub(cl.files, oldfiles) 17395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Flush(ui, repo) 17405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 17415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("no such files in CL") 17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 17435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 17455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "no such modified files" 17465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = Sub(files, cl.files) 17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) taken = Taken(ui, repo) 17495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) warned = False 17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in files: 17515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if f in taken: 17525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not warned and not ui.quiet: 17535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("# Taking files from other CLs. To undo:\n") 17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("# cd %s\n" % (repo.root)) 17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) warned = True 17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ocl = taken[f] 17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not ui.quiet: 17585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.status("# hg file %s %s\n" % (ocl.name, f)) 17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ocl not in dirty: 17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ocl.files = Sub(ocl.files, files) 17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[ocl] = True 17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = Add(cl.files, files) 17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirty[cl] = True 17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for d, _ in dirty.items(): 17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d.Flush(ui, repo) 17665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 17675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 17695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg gofmt 17705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 17725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def gofmt(ui, repo, *pats, **opts): 17735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """apply gofmt to modified files 17745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Applies gofmt to the modified files in the repository that match 17765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the given patterns. 17775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 17785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 17795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 17805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = ChangedExistingFiles(ui, repo, pats, opts) 17825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = gofmt_required(files) 17835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not files: 17845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "no modified go files" 17855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cwd = os.getcwd() 17865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [RelativePath(repo.root + '/' + f, cwd) for f in files] 17875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 17885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd = ["gofmt", "-l"] 17895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not opts["list"]: 17905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd += ["-w"] 17915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.spawnvp(os.P_WAIT, "gofmt", cmd + files) != 0: 17925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("gofmt did not exit cleanly") 17935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except hg_error.Abort, e: 17945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 17955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 17965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("gofmt: " + ExceptionDetail()) 17975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 17985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def gofmt_required(files): 18005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [f for f in files if (not f.startswith('test/') or f.startswith('test/bench/')) and f.endswith('.go')] 18015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 18035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg mail 18045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 18065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def mail(ui, repo, *pats, **opts): 18075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """mail a change for review 18085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Uploads a patch to the code review server and then sends mail 18105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) to the reviewer and CC list asking for a review. 18115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 18125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 18135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 18145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc) 18165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 18175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 18185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Upload(ui, repo, gofmt_just_warn=True) 18195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.reviewer: 18205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If no reviewer is listed, assign the review to defaultcc. 18215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # This makes sure that it appears in the 18225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # codereview.appspot.com/user/defaultcc 18235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # page, so that it doesn't get dropped on the floor. 18245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not defaultcc: 18255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "no reviewers listed in CL" 18265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.cc = Sub(cl.cc, defaultcc) 18275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = defaultcc 18285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Flush(ui, repo) 18295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.files == []: 18315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "no changed files, not sending mail" 18325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Mail(ui, repo) 18345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 18365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg p / hg pq / hg ps / hg pending 18375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 18395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ps(ui, repo, *pats, **opts): 18405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """alias for hg p --short 18415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 18425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opts['short'] = True 18435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return pending(ui, repo, *pats, **opts) 18445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 18465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def pq(ui, repo, *pats, **opts): 18475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """alias for hg p --quick 18485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 18495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opts['quick'] = True 18505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return pending(ui, repo, *pats, **opts) 18515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 18535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def pending(ui, repo, *pats, **opts): 18545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """show pending changes 18555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Lists pending changes followed by a list of unassigned but modified files. 18575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 18585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 18595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 18605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) quick = opts.get('quick', False) 18625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) short = opts.get('short', False) 18635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = LoadAllCL(ui, repo, web=not quick and not short) 18645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) names = m.keys() 18655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) names.sort() 18665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for name in names: 18675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl = m[name] 18685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if short: 18695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(name + "\t" + line1(cl.desc) + "\n") 18705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 18715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(cl.PendingText(quick=quick) + "\n") 18725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if short: 18745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 18755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = DefaultFiles(ui, repo, []) 18765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(files) > 0: 18775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s = "Changed files not in any CL:\n" 18785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in files: 18795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s += "\t" + f + "\n" 18805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write(s) 18815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 18835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg submit 18845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def need_sync(): 18865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("local repository out of date; must sync before submit") 18875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 18895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def submit(ui, repo, *pats, **opts): 18905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """submit change to remote repository 18915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Submits change to remote repository. 18935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Bails out if the local repository is not in sync with the remote one. 18945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 18955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 18965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 18975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 18985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # We already called this on startup but sometimes Mercurial forgets. 18995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_mercurial_encoding_to_utf8() 19005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not opts["no_incoming"] and hg_incoming(ui, repo): 19025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) need_sync() 19035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc) 19055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 19065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 19075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user = None 19095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.copied_from: 19105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user = cl.copied_from 19115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) userline = CheckContributor(ui, repo, user) 19125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(userline, str) 19135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) about = "" 19155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.reviewer: 19165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) about += "R=" + JoinComma([CutDomain(s) for s in cl.reviewer]) + "\n" 19175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if opts.get('tbr'): 19185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tbr = SplitCommaSpace(opts.get('tbr')) 19195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.reviewer = Add(cl.reviewer, tbr) 19205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) about += "TBR=" + JoinComma([CutDomain(s) for s in tbr]) + "\n" 19215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.cc: 19225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) about += "CC=" + JoinComma([CutDomain(s) for s in cl.cc]) + "\n" 19235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.reviewer: 19255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "no reviewers listed in CL" 19265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.local: 19285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot submit non-local CL" 19295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # upload, to sync current patch and also get change number if CL is new. 19315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.copied_from: 19325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Upload(ui, repo, gofmt_just_warn=True) 19335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # check gofmt for real; allowed upload to warn in order to save CL. 19355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Flush(ui, repo) 19365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CheckFormat(ui, repo, cl.files) 19375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) about += "%s%s\n" % (server_url_base, cl.name) 19395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cl.copied_from: 19415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) about += "\nCommitter: " + CheckContributor(ui, repo, None) + "\n" 19425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(about, str) 19435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.mailed and not cl.copied_from: # in case this is TBR 19455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Mail(ui, repo) 19465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # submit changes locally 19485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) message = cl.desc.rstrip() + "\n\n" + about 19495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(message, str) 19505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("pushing " + cl.name + " to remote server") 19525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if hg_outgoing(ui, repo): 19545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("local repository corrupt or out-of-phase with remote: found outgoing changes") 19555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) old_heads = len(hg_heads(ui, repo).split()) 19575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global commit_okay 19595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) commit_okay = True 19605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret = hg_commit(ui, repo, *['path:'+f for f in cl.files], message=message, user=userline) 19615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) commit_okay = False 19625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ret: 19635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "nothing changed" 19645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node = repo["-1"].node() 19655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # push to remote; if it fails for any reason, roll back 19665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 19675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_heads = len(hg_heads(ui, repo).split()) 19685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if old_heads != new_heads and not (old_heads == 0 and new_heads == 1): 19695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Created new head, so we weren't up to date. 19705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) need_sync() 19715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Push changes to remote. If it works, we're committed. If not, roll back. 19735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 19745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hg_push(ui, repo) 19755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except hg_error.Abort, e: 19765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.message.find("push creates new heads") >= 0: 19775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Remote repository had changes we missed. 19785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) need_sync() 19795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 19805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 19815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) real_rollback() 19825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 19835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 19845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # We're committed. Upload final patch, close review, add commit message. 19855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) changeURL = hg_node.short(node) 19865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = ui.expandpath("default") 19875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match("(^https?://([^@/]+@)?([^.]+)\.googlecode\.com/hg/?)" + "|" + 19885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "(^https?://([^@/]+@)?code\.google\.com/p/([^/.]+)(\.[^./]+)?/?)", url) 19895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m: 19905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m.group(1): # prj.googlecode.com/hg/ case 19915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(3), changeURL) 19925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif m.group(4) and m.group(7): # code.google.com/p/prj.subrepo/ case 19935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) changeURL = "http://code.google.com/p/%s/source/detail?r=%s&repo=%s" % (m.group(6), changeURL, m.group(7)[1:]) 19945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif m.group(4): # code.google.com/p/prj/ case 19955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(6), changeURL) 19965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 19975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "URL: ", url 19985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 19995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "URL: ", url 20005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pmsg = "*** Submitted as " + changeURL + " ***\n\n" + message 20015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # When posting, move reviewers to CC line, 20035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # so that the issue stops showing up in their "My Issues" page. 20045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PostMessage(ui, cl.name, pmsg, reviewers="", cc=JoinComma(cl.reviewer+cl.cc)) 20055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.copied_from: 20075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EditDesc(cl.name, closed=True, private=cl.private) 20085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Delete(ui, repo) 20095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c = repo[None] 20115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if c.branch() == releaseBranch and not c.modified() and not c.added() and not c.removed(): 20125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.write("switching from %s to default branch.\n" % releaseBranch) 20135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = hg_clean(repo, "default") 20145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err: 20155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 20165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 20175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 20195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg sync 20205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 20225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def sync(ui, repo, **opts): 20235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """synchronize with remote repository 20245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Incorporates recent changes from the remote repository 20265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) into the local repository. 20275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 20285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 20295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 20305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not opts["local"]: 20325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) err = hg_pull(ui, repo, update=True) 20335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err: 20345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 20355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sync_changes(ui, repo) 20365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def sync_changes(ui, repo): 20385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Look through recent change log descriptions to find 20395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # potential references to http://.*/our-CL-number. 20405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Double-check them by looking at the Rietveld log. 20415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for rev in hg_log(ui, repo, limit=100, template="{node}\n").split(): 20425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) desc = repo[rev].description().strip() 20435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for clname in re.findall('(?m)^http://(?:[^\n]+)/([0-9]+)$', desc): 20445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if IsLocalCL(ui, repo, clname) and IsRietveldSubmitted(ui, clname, repo[rev].hex()): 20455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("CL %s submitted as %s; closing\n" % (clname, repo[rev])) 20465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(ui, repo, clname, web=False) 20475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 20485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("loading CL %s: %s\n" % (clname, err)) 20495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 20505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.copied_from: 20515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EditDesc(cl.name, closed=True, private=cl.private) 20525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Delete(ui, repo) 20535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Remove files that are not modified from the CLs in which they appear. 20555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) all = LoadAllCL(ui, repo, web=False) 20565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) changed = ChangedFiles(ui, repo, []) 20575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for cl in all.values(): 20585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra = Sub(cl.files, changed) 20595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if extra: 20605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("Removing unmodified files from CL %s:\n" % (cl.name,)) 20615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for f in extra: 20625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("\t%s\n" % (f,)) 20635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.files = Sub(cl.files, extra) 20645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Flush(ui, repo) 20655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.files: 20665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.copied_from: 20675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("CL %s has no files; delete (abandon) with hg change -d %s\n" % (cl.name, cl.name)) 20685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 20695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("CL %s has no files; delete locally with hg change -D %s\n" % (cl.name, cl.name)) 20705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 20715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 20735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# hg upload 20745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@hgcommand 20765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def upload(ui, repo, name, **opts): 20775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """upload diffs to the code review server 20785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Uploads the current modifications for a given change to the server. 20805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 20815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_disabled: 20825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return codereview_disabled 20835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repo.ui.quiet = True 20855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(ui, repo, name, web=True) 20865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 20875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err 20885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not cl.local: 20895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "cannot upload non-local change" 20905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.Upload(ui, repo) 20915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print "%s%s\n" % (server_url_base, cl.name) 20925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 20935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 20955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Table of commands, supplied to Mercurial for installation. 20965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 20975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)review_opts = [ 20985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('r', 'reviewer', '', 'add reviewer'), 20995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'cc', '', 'add cc'), 21005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'tbr', '', 'add future reviewer'), 21015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('m', 'message', '', 'change description (for new change)'), 21025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)] 21035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 21045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cmdtable = { 21055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # The ^ means to show this command in the help text that 21065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # is printed when running hg with no arguments. 21075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^change": ( 21085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) change, 21095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('d', 'delete', None, 'delete existing change list'), 21115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('D', 'deletelocal', None, 'delete locally, but do not change CL on server'), 21125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('i', 'stdin', None, 'read change list from standard input'), 21135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('o', 'stdout', None, 'print change list to standard output'), 21145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('p', 'pending', None, 'print pending summary to standard output'), 21155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[-d | -D] [-i] [-o] change# or FILE ..." 21175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^clpatch": ( 21195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) clpatch, 21205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), 21225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'no_incoming', None, 'disable check for incoming changes'), 21235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "change#" 21255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Would prefer to call this codereview-login, but then 21275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # hg help codereview prints the help for this command 21285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # instead of the help for the extension. 21295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "code-login": ( 21305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) code_login, 21315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [], 21325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "", 21335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^download": ( 21355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) download, 21365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [], 21375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "change#" 21385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^file": ( 21405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file, 21415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('d', 'delete', None, 'delete files from change list (but not repository)'), 21435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[-d] change# FILE ..." 21455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^gofmt": ( 21475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gofmt, 21485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('l', 'list', None, 'list files that would change, but do not edit them'), 21505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "FILE ..." 21525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^pending|p": ( 21545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending, 21555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('s', 'short', False, 'show short result form'), 21575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'quick', False, 'do not consult codereview server'), 21585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[FILE ...]" 21605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^ps": ( 21625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ps, 21635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [], 21645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[FILE ...]" 21655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^pq": ( 21675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pq, 21685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [], 21695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[FILE ...]" 21705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^mail": ( 21725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mail, 21735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) review_opts + [ 21745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ] + hg_commands.walkopts, 21755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[-r reviewer] [--cc cc] [change# | file ...]" 21765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^release-apply": ( 21785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) release_apply, 21795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), 21815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'no_incoming', None, 'disable check for incoming changes'), 21825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "change#" 21845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO: release-start, release-tag, weekly-tag 21865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^submit": ( 21875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) submit, 21885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) review_opts + [ 21895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'no_incoming', None, 'disable initial incoming check (for testing)'), 21905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ] + hg_commands.walkopts + hg_commands.commitopts + hg_commands.commitopts2, 21915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[-r reviewer] [--cc cc] [change# | file ...]" 21925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 21935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^sync": ( 21945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sync, 21955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 21965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'local', None, 'do not pull changes from remote repository') 21975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 21985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[--local]", 21995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 22005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^undo": ( 22015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) undo, 22025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ 22035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), 22045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ('', 'no_incoming', None, 'disable check for incoming changes'), 22055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ], 22065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "change#" 22075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 22085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "^upload": ( 22095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload, 22105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [], 22115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "change#" 22125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ), 22135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 22145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 22165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Mercurial extension initialization 22175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def norollback(*pats, **opts): 22195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """(disabled when using this extension)""" 22205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("codereview extension enabled; use undo instead of rollback") 22215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)codereview_init = False 22235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def reposetup(ui, repo): 22255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global codereview_disabled 22265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global defaultcc 22275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # reposetup gets called both for the local repository 22295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # and also for any repository we are pulling or pushing to. 22305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Only initialize the first time. 22315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global codereview_init 22325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if codereview_init: 22335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 22345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) codereview_init = True 22355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Read repository-specific options from lib/codereview/codereview.cfg or codereview.cfg. 22375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) root = '' 22385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 22395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) root = repo.root 22405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 22415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Yes, repo might not have root; see issue 959. 22425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) codereview_disabled = 'codereview disabled: repository has no root' 22435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 22445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repo_config_path = '' 22465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p1 = root + '/lib/codereview/codereview.cfg' 22475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p2 = root + '/codereview.cfg' 22485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.access(p1, os.F_OK): 22495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repo_config_path = p1 22505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 22515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repo_config_path = p2 22525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 22535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = open(repo_config_path) 22545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in f: 22555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('defaultcc:'): 22565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) defaultcc = SplitCommaSpace(line[len('defaultcc:'):]) 22575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('contributors:'): 22585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global contributorsURL 22595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) contributorsURL = line[len('contributors:'):].strip() 22605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 22615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) codereview_disabled = 'codereview disabled: cannot open ' + repo_config_path 22625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 22635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) remote = ui.config("paths", "default", "") 22655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if remote.find("://") < 0: 22665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("codereview: default path '%s' is not a URL" % (remote,)) 22675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InstallMatch(ui, repo) 22695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) RietveldSetup(ui, repo) 22705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Disable the Mercurial commands that might change the repository. 22725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Only commands in this extension are supposed to do that. 22735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.setconfig("hooks", "precommit.codereview", precommithook) 22745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Rollback removes an existing commit. Don't do that either. 22765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global real_rollback 22775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) real_rollback = repo.rollback 22785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) repo.rollback = norollback 22795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 22825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Wrappers around upload.py for interacting with Rietveld 22835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from HTMLParser import HTMLParser 22855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 22865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# HTML form parser 22875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class FormParser(HTMLParser): 22885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self): 22895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.map = {} 22905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curtag = None 22915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curdata = None 22925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HTMLParser.__init__(self) 22935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def handle_starttag(self, tag, attrs): 22945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if tag == "input": 22955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) key = None 22965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) value = '' 22975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for a in attrs: 22985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if a[0] == 'name': 22995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) key = a[1] 23005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if a[0] == 'value': 23015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) value = a[1] 23025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if key is not None: 23035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.map[key] = value 23045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if tag == "textarea": 23055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) key = None 23065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for a in attrs: 23075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if a[0] == 'name': 23085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) key = a[1] 23095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if key is not None: 23105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curtag = key 23115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curdata = '' 23125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def handle_endtag(self, tag): 23135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if tag == "textarea" and self.curtag is not None: 23145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.map[self.curtag] = self.curdata 23155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curtag = None 23165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curdata = None 23175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def handle_charref(self, name): 23185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.handle_data(unichr(int(name))) 23195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def handle_entityref(self, name): 23205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import htmlentitydefs 23215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if name in htmlentitydefs.entitydefs: 23225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.handle_data(htmlentitydefs.entitydefs[name]) 23235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 23245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.handle_data("&" + name + ";") 23255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def handle_data(self, data): 23265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.curdata is not None: 23275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.curdata += data 23285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def JSONGet(ui, path): 23305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 23315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data = MySend(path, force_auth=False) 23325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(data, str) 23335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) d = fix_json(json.loads(data)) 23345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except: 23355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui.warn("JSONGet %s: %s\n" % (path, ExceptionDetail())) 23365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 23375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return d 23385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Clean up json parser output to match our expectations: 23405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# * all strings are UTF-8-encoded str, not unicode. 23415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# * missing fields are missing, not None, 23425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# so that d.get("foo", defaultvalue) works. 23435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def fix_json(x): 23445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(x) in [str, int, float, bool, type(None)]: 23455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 23465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif type(x) is unicode: 23475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x = x.encode("utf-8") 23485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif type(x) is list: 23495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(x)): 23505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x[i] = fix_json(x[i]) 23515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif type(x) is dict: 23525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) todel = [] 23535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k in x: 23545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if x[k] is None: 23555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) todel.append(k) 23565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 23575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x[k] = fix_json(x[k]) 23585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k in todel: 23595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) del x[k] 23605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 23615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("unknown type " + str(type(x)) + " in fix_json") 23625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(x) is str: 23635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x = x.replace('\r\n', '\n') 23645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return x 23655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IsRietveldSubmitted(ui, clname, hex): 23675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dict = JSONGet(ui, "/api/" + clname + "?messages=true") 23685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if dict is None: 23695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 23705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for msg in dict.get("messages", []): 23715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) text = msg.get("text", "") 23725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match('\*\*\* Submitted as [^*]*?([0-9a-f]+) \*\*\*', text) 23735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m is not None and len(m.group(1)) >= 8 and hex.startswith(m.group(1)): 23745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return True 23755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 23765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IsRietveldMailed(cl): 23785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for msg in cl.dict.get("messages", []): 23795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if msg.get("text", "").find("I'd like you to review this change") >= 0: 23805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return True 23815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 23825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def DownloadCL(ui, repo, clname): 23845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("downloading CL " + clname) 23855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl, err = LoadCL(ui, repo, clname, web=True) 23865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if err != "": 23875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, None, None, "error loading CL %s: %s" % (clname, err) 23885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find most recent diff 23905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diffs = cl.dict.get("patchsets", []) 23915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not diffs: 23925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, None, None, "CL has no patch sets" 23935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patchid = diffs[-1] 23945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patchset = JSONGet(ui, "/api/" + clname + "/" + str(patchid)) 23965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if patchset is None: 23975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, None, None, "error loading CL patchset %s/%d" % (clname, patchid) 23985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if patchset.get("patchset", 0) != patchid: 23995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, None, None, "malformed patchset information" 24005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vers = "" 24025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = patchset.get("message", "").split() 24035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(msg) >= 3 and msg[0] == "diff" and msg[1] == "-r": 24045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) vers = msg[2] 24055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diff = "/download/issue" + clname + "_" + str(patchid) + ".diff" 24065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diffdata = MySend(diff, force_auth=False) 24085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Print warning if email is not in CONTRIBUTORS file. 24105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = cl.dict.get("owner_email", "") 24115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not email: 24125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None, None, None, "cannot find owner for %s" % (clname) 24135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) him = FindContributor(ui, repo, email) 24145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) me = FindContributor(ui, repo, None) 24155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if him == me: 24165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.mailed = IsRietveldMailed(cl) 24175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 24185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cl.copied_from = email 24195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cl, vers, diffdata, "" 24215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MySend(request_path, payload=None, 24235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content_type="application/octet-stream", 24245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timeout=None, force_auth=True, 24255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) **kwargs): 24265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Run MySend1 maybe twice, because Rietveld is unreliable.""" 24275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 24285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) 24295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except Exception, e: 24305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(e) != urllib2.HTTPError or e.code != 500: # only retry on HTTP 500 error 24315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 24325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "Loading "+request_path+": "+ExceptionDetail()+"; trying again in 2 seconds." 24335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time.sleep(2) 24345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) 24355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Like upload.py Send but only authenticates when the 24375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# redirect is to www.google.com/accounts. This keeps 24385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# unnecessary redirects from happening during testing. 24395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MySend1(request_path, payload=None, 24405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content_type="application/octet-stream", 24415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timeout=None, force_auth=True, 24425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) **kwargs): 24435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Sends an RPC and returns the response. 24445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 24465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_path: The path to send the request to, eg /api/appversion/create. 24475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) payload: The body of the request, or None to send an empty request. 24485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content_type: The Content-Type header to use. 24495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timeout: timeout in seconds; default None i.e. no timeout. 24505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (Note: for large requests on OS X, the timeout doesn't work right.) 24515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kwargs: Any keyword arguments are converted into query string parameters. 24525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 24545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The response body, as a string. 24555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 24565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO: Don't require authentication. Let the server say 24575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # whether it is necessary. 24585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global rpc 24595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if rpc == None: 24605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rpc = GetRpcServer(upload_options) 24615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self = rpc 24625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not self.authenticated and force_auth: 24635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._Authenticate() 24645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if request_path is None: 24655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 24665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) old_timeout = socket.getdefaulttimeout() 24685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) socket.setdefaulttimeout(timeout) 24695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 24705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tries = 0 24715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while True: 24725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tries += 1 24735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args = dict(kwargs) 24745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = "http://%s%s" % (self.host, request_path) 24755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if args: 24765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url += "?" + urllib.urlencode(args) 24775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req = self._CreateRequest(url=url, data=payload) 24785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req.add_header("Content-Type", content_type) 24795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 24805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = self.opener.open(req) 24815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = f.read() 24825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.close() 24835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Translate \r\n into \n, because Rietveld doesn't. 24845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = response.replace('\r\n', '\n') 24855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # who knows what urllib will give us 24865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(response) == unicode: 24875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = response.encode("utf-8") 24885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(response, str) 24895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return response 24905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except urllib2.HTTPError, e: 24915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if tries > 3: 24925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 24935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif e.code == 401: 24945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._Authenticate() 24955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif e.code == 302: 24965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) loc = e.info()["location"] 24975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not loc.startswith('https://www.google.com/a') or loc.find('/ServiceLogin') < 0: 24985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return '' 24995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._Authenticate() 25005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 25015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 25025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finally: 25035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) socket.setdefaulttimeout(old_timeout) 25045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetForm(url): 25065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = FormParser() 25075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.feed(ustr(MySend(url))) # f.feed wants unicode 25085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.close() 25095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # convert back to utf-8 to restore sanity 25105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = {} 25115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k,v in f.map.items(): 25125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m[k.encode("utf-8")] = v.replace("\r\n", "\n").encode("utf-8") 25135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return m 25145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def EditDesc(issue, subject=None, desc=None, reviewers=None, cc=None, closed=False, private=False): 25165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading change to description") 25175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields = GetForm("/" + issue + "/edit") 25185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if subject is not None: 25195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['subject'] = subject 25205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if desc is not None: 25215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['description'] = desc 25225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if reviewers is not None: 25235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['reviewers'] = reviewers 25245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cc is not None: 25255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['cc'] = cc 25265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if closed: 25275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['closed'] = "checked" 25285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if private: 25295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['private'] = "checked" 25305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctype, body = EncodeMultipartFormData(form_fields.items(), []) 25315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = MySend("/" + issue + "/edit", body, content_type=ctype) 25325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if response != "": 25335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "Error editing description:\n" + "Sent form: \n", form_fields, "\n", response 25345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(2) 25355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def PostMessage(ui, issue, message, reviewers=None, cc=None, send_mail=True, subject=None): 25375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading message") 25385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields = GetForm("/" + issue + "/publish") 25395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if reviewers is not None: 25405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['reviewers'] = reviewers 25415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cc is not None: 25425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['cc'] = cc 25435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if send_mail: 25445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['send_mail'] = "checked" 25455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 25465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) del form_fields['send_mail'] 25475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if subject is not None: 25485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['subject'] = subject 25495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['message'] = message 25505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['message_only'] = '1' # Don't include draft comments 25525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if reviewers is not None or cc is not None: 25535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields['message_only'] = '' # Must set '' in order to override cc/reviewer 25545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctype = "applications/x-www-form-urlencoded" 25555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body = urllib.urlencode(form_fields) 25565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = MySend("/" + issue + "/publish", body, content_type=ctype) 25575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if response != "": 25585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print response 25595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(2) 25605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class opt(object): 25625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 25635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RietveldSetup(ui, repo): 25655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global force_google_account 25665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global rpc 25675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global server 25685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global server_url_base 25695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global upload_options 25705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global verbosity 25715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not ui.verbose: 25735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) verbosity = 0 25745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Config options. 25765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x = ui.config("codereview", "server") 25775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if x is not None: 25785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) server = x 25795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO(rsc): Take from ui.username? 25815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = None 25825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x = ui.config("codereview", "email") 25835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if x is not None: 25845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = x 25855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) server_url_base = "http://" + server + "/" 25875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) testing = ui.config("codereview", "testing") 25895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) force_google_account = ui.configbool("codereview", "force_google_account", False) 25905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options = opt() 25925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.email = email 25935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.host = None 25945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.verbose = 0 25955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.description = None 25965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.description_file = None 25975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.reviewers = None 25985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.cc = None 25995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.message = None 26005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.issue = None 26015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.download_base = False 26025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.revision = None 26035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.send_mail = False 26045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.vcs = None 26055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.server = server 26065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.save_cookies = True 26075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if testing: 26095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.save_cookies = False 26105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_options.email = "test@example.com" 26115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rpc = None 26135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global releaseBranch 26151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci tags = repo.branchmap().keys() 26165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if 'release-branch.go10' in tags: 26175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # NOTE(rsc): This tags.sort is going to get the wrong 26185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # answer when comparing release-branch.go9 with 26195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # release-branch.go10. It will be a while before we care. 26205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort('tags.sort needs to be fixed for release-branch.go10') 26215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tags.sort() 26225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for t in tags: 26235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if t.startswith('release-branch.go'): 26245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) releaseBranch = t 26255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)####################################################################### 26275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# http://codereview.appspot.com/static/upload.py, heavily edited. 26285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python 26305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 26315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2007 Google Inc. 26325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 26335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License"); 26345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License. 26355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at 26365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 26375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# http://www.apache.org/licenses/LICENSE-2.0 26385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 26395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software 26405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS, 26415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and 26435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License. 26445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Tool for uploading diffs from a version control system to the codereview app. 26465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Usage summary: upload.py [options] [-- diff_options] 26485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Diff options are passed to the diff command of the underlying system. 26505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Supported version control systems: 26525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Git 26535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Mercurial 26545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Subversion 26555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)It is important for Git/Mercurial users to specify a tree/node/branch to diff 26575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)against by using the '--rev' option. 26585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 26595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This code is derived from appcfg.py in the App Engine SDK (open source), 26605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# and from ASPN recipe #146306. 26615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import cookielib 26635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import getpass 26645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging 26655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import mimetypes 26665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse 26675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 26685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re 26695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import socket 26705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess 26715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys 26725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urllib 26735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urllib2 26745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urlparse 26755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The md5 module was deprecated in Python 2.5. 26775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)try: 26785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from hashlib import md5 26795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)except ImportError: 26805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from md5 import md5 26815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)try: 26835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import readline 26845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)except ImportError: 26855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 26865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The logging verbosity: 26885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 0: Errors only. 26895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 1: Status messages. 26905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 2: Info logs. 26915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 3: Debug logs. 26925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)verbosity = 1 26935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Max size of patch or base file. 26955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MAX_UPLOAD_SIZE = 900 * 1024 26965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 26975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# whitelist for non-binary filetypes which do not start with "text/" 26985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# .mm (Objective-C) shows up as application/x-freemind on my Linux box. 26995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEXT_MIMETYPES = [ 27005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'application/javascript', 27015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'application/x-javascript', 27025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'application/x-freemind' 27035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)] 27045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetEmail(prompt): 27065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Prompts the user for their email address and returns it. 27075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The last used email address is saved to a file and offered up as a suggestion 27095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) to the user. If the user presses enter without typing in anything the last 27105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) used email address is used. If the user enters a new address, it is saved 27115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for next time we prompt. 27125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 27145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email_file_name = os.path.expanduser("~/.last_codereview_email_address") 27155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email = "" 27165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.path.exists(last_email_file_name): 27175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 27185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email_file = open(last_email_file_name, "r") 27195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email = last_email_file.readline().strip("\n") 27205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email_file.close() 27215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prompt += " [%s]" % last_email 27225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except IOError, e: 27235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 27245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = raw_input(prompt + ": ").strip() 27255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if email: 27265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 27275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email_file = open(last_email_file_name, "w") 27285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email_file.write(email) 27295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_email_file.close() 27305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except IOError, e: 27315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 27325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 27335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = last_email 27345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return email 27355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def StatusUpdate(msg): 27385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Print a status message to stdout. 27395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) If 'verbosity' is greater than 0, print the message. 27415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 27435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg: The string to print. 27445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 27455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if verbosity > 0: 27465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print msg 27475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ErrorExit(msg): 27505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Print an error message to stderr and exit.""" 27515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, msg 27525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(1) 27535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ClientLoginError(urllib2.HTTPError): 27565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Raised to indicate there was an error authenticating with ClientLogin.""" 27575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, url, code, msg, headers, args): 27595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) urllib2.HTTPError.__init__(self, url, code, msg, headers, None) 27605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.args = args 27615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.reason = args["Error"] 27625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class AbstractRpcServer(object): 27655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Provides a common interface for a simple RPC server.""" 27665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, host, auth_function, host_override=None, extra_headers={}, save_cookies=False): 27685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Creates a new HttpRpcServer. 27695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 27715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host: The host to send requests to. 27725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auth_function: A function that takes no arguments and returns an 27735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (email, password) tuple when called. Will be called if authentication 27745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is required. 27755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host_override: The host header to send to the server (defaults to host). 27765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra_headers: A dict of extra headers to append to every request. 27775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) save_cookies: If True, save the authentication cookies to local disk. 27785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) If False, use an in-memory cookiejar instead. Subclasses must 27795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) implement this functionality. Defaults to False. 27805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 27815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.host = host 27825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.host_override = host_override 27835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.auth_function = auth_function 27845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.authenticated = False 27855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.extra_headers = extra_headers 27865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.save_cookies = save_cookies 27875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.opener = self._GetOpener() 27885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.host_override: 27895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info("Server: %s; Host: %s", self.host, self.host_override) 27905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 27915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info("Server: %s", self.host) 27925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _GetOpener(self): 27945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns an OpenerDirector for making HTTP requests. 27955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 27975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A urllib2.OpenerDirector object. 27985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 27995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise NotImplementedError() 28005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _CreateRequest(self, url, data=None): 28025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Creates a new urllib request.""" 28035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug("Creating request for: '%s' with payload:\n%s", url, data) 28045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req = urllib2.Request(url, data=data) 28055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.host_override: 28065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req.add_header("Host", self.host_override) 28075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for key, value in self.extra_headers.iteritems(): 28085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req.add_header(key, value) 28095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return req 28105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _GetAuthToken(self, email, password): 28125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Uses ClientLogin to authenticate the user, returning an auth token. 28135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 28155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email: The user's email address 28165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password: The user's password 28175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Raises: 28195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ClientLoginError: If there was an error authenticating with ClientLogin. 28205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HTTPError: If there was some other form of HTTP error. 28215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 28235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The authentication token returned by ClientLogin. 28245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 28255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) account_type = "GOOGLE" 28265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.host.endswith(".google.com") and not force_google_account: 28275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Needed for use inside Google. 28285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) account_type = "HOSTED" 28295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req = self._CreateRequest( 28305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url="https://www.google.com/accounts/ClientLogin", 28315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data=urllib.urlencode({ 28325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Email": email, 28335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Passwd": password, 28345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "service": "ah", 28355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "source": "rietveld-codereview-upload", 28365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "accountType": account_type, 28375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }), 28385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ) 28395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 28405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = self.opener.open(req) 28415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_body = response.read() 28425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_dict = dict(x.split("=") for x in response_body.split("\n") if x) 28435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return response_dict["Auth"] 28445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except urllib2.HTTPError, e: 28455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.code == 403: 28465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body = e.read() 28475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_dict = dict(x.split("=", 1) for x in body.split("\n") if x) 28485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise ClientLoginError(req.get_full_url(), e.code, e.msg, e.headers, response_dict) 28495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 28505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 28515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _GetAuthCookie(self, auth_token): 28535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Fetches authentication cookies for an authentication token. 28545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 28565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auth_token: The authentication token returned by ClientLogin. 28575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Raises: 28595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HTTPError: If there was an error fetching the authentication cookies. 28605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 28615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # This is a dummy value to allow us to identify when we're successful. 28625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue_location = "http://localhost/" 28635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args = {"continue": continue_location, "auth": auth_token} 28645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req = self._CreateRequest("http://%s/_ah/login?%s" % (self.host, urllib.urlencode(args))) 28655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 28665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = self.opener.open(req) 28675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except urllib2.HTTPError, e: 28685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = e 28695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (response.code != 302 or 28705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.info()["location"] != continue_location): 28715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, response.headers, response.fp) 28725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.authenticated = True 28735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _Authenticate(self): 28755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Authenticates the user. 28765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The authentication process works as follows: 28785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1) We get a username and password from the user 28795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2) We use ClientLogin to obtain an AUTH token for the user 28805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). 28815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3) We pass the auth token to /_ah/login on the server to obtain an 28825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) authentication cookie. If login was successful, it tries to redirect 28835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) us to the URL we provided. 28845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) If we attempt to access the upload API without first obtaining an 28865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) authentication cookie, it returns a 401 response (or a 302) and 28875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) directs us to authenticate ourselves with ClientLogin. 28885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 28895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(3): 28905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) credentials = self.auth_function() 28915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 28925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auth_token = self._GetAuthToken(credentials[0], credentials[1]) 28935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except ClientLoginError, e: 28945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "BadAuthentication": 28955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "Invalid username or password." 28965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 28975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "CaptchaRequired": 28985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, ( 28995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Please go to\n" 29005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "https://www.google.com/accounts/DisplayUnlockCaptcha\n" 29015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "and verify you are a human. Then try again.") 29025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "NotVerified": 29045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "Account not verified." 29055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "TermsNotAgreed": 29075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "User has not agreed to TOS." 29085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "AccountDeleted": 29105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "The user account has been deleted." 29115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "AccountDisabled": 29135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "The user account has been disabled." 29145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "ServiceDisabled": 29165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "The user's access to the service has been disabled." 29175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if e.reason == "ServiceUnavailable": 29195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, "The service is not available; try again later." 29205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 29215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 29225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._GetAuthCookie(auth_token) 29235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 29245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Send(self, request_path, payload=None, 29265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content_type="application/octet-stream", 29275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timeout=None, 29285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) **kwargs): 29295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Sends an RPC and returns the response. 29305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 29325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_path: The path to send the request to, eg /api/appversion/create. 29335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) payload: The body of the request, or None to send an empty request. 29345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content_type: The Content-Type header to use. 29355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timeout: timeout in seconds; default None i.e. no timeout. 29365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (Note: for large requests on OS X, the timeout doesn't work right.) 29375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kwargs: Any keyword arguments are converted into query string parameters. 29385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 29405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The response body, as a string. 29415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 29425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO: Don't require authentication. Let the server say 29435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # whether it is necessary. 29445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not self.authenticated: 29455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._Authenticate() 29465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) old_timeout = socket.getdefaulttimeout() 29485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) socket.setdefaulttimeout(timeout) 29495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 29505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tries = 0 29515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while True: 29525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tries += 1 29535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args = dict(kwargs) 29545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = "http://%s%s" % (self.host, request_path) 29555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if args: 29565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url += "?" + urllib.urlencode(args) 29575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req = self._CreateRequest(url=url, data=payload) 29585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req.add_header("Content-Type", content_type) 29595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 29605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = self.opener.open(req) 29615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = f.read() 29625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.close() 29635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return response 29645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except urllib2.HTTPError, e: 29655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if tries > 3: 29665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 29675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif e.code == 401 or e.code == 302: 29685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._Authenticate() 29695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 29705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise 29715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finally: 29725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) socket.setdefaulttimeout(old_timeout) 29735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HttpRpcServer(AbstractRpcServer): 29765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Provides a simplified RPC-style interface for HTTP requests.""" 29775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _Authenticate(self): 29795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Save the cookie jar after authentication.""" 29805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) super(HttpRpcServer, self)._Authenticate() 29815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.save_cookies: 29825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) 29835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cookie_jar.save() 29845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _GetOpener(self): 29865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns an OpenerDirector that supports cookies and ignores redirects. 29875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 29895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A urllib2.OpenerDirector object. 29905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 29915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener = urllib2.OpenerDirector() 29925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.ProxyHandler()) 29935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.UnknownHandler()) 29945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.HTTPHandler()) 29955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.HTTPDefaultErrorHandler()) 29965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.HTTPSHandler()) 29975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.HTTPErrorProcessor()) 29985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.save_cookies: 29995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies_" + server) 30005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) 30015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.path.exists(self.cookie_file): 30025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 30035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cookie_jar.load() 30045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.authenticated = True 30055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StatusUpdate("Loaded authentication cookies from %s" % self.cookie_file) 30065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except (cookielib.LoadError, IOError): 30075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Failed to load cookies - just ignore them. 30085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 30095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 30105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Create an empty cookie file with mode 600 30115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fd = os.open(self.cookie_file, os.O_CREAT, 0600) 30125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.close(fd) 30135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Always chmod the cookie file 30145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.chmod(self.cookie_file, 0600) 30155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 30165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Don't save cookies across runs of update.py. 30175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cookie_jar = cookielib.CookieJar() 30185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) 30195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return opener 30205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetRpcServer(options): 30235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns an instance of an AbstractRpcServer. 30245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 30265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A new AbstractRpcServer, on which RPC calls can be made. 30275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 30285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rpc_server_class = HttpRpcServer 30305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetUserCredentials(): 30325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Prompts the user for a username and password.""" 30335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Disable status prints so they don't obscure the password prompt. 30345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global global_status 30355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) st = global_status 30365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global_status = None 30375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = options.email 30395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if email is None: 30405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = GetEmail("Email (login for uploading to %s)" % options.server) 30415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password = getpass.getpass("Password for %s: " % email) 30425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Put status back. 30445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global_status = st 30455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (email, password) 30465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If this is the dev_appserver, use fake authentication. 30485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host = (options.host or options.server).lower() 30495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if host == "localhost" or host.startswith("localhost:"): 30505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = options.email 30515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if email is None: 30525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) email = "test@example.com" 30535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info("Using debug user %s. Override with --email" % email) 30545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) server = rpc_server_class( 30555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) options.server, 30565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lambda: (email, "password"), 30575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host_override=options.host, 30585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra_headers={"Cookie": 'dev_appserver_login="%s:False"' % email}, 30595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) save_cookies=options.save_cookies) 30605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Don't try to talk to ClientLogin. 30615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) server.authenticated = True 30625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return server 30635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return rpc_server_class(options.server, GetUserCredentials, 30655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host_override=options.host, save_cookies=options.save_cookies) 30665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def EncodeMultipartFormData(fields, files): 30695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Encode form fields for multipart/form-data. 30705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 30725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fields: A sequence of (name, value) elements for regular form fields. 30735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files: A sequence of (name, filename, value) elements for data to be 30745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uploaded as files. 30755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 30765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (content_type, body) ready for httplib.HTTP instance. 30775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 30785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Source: 30795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 30805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 30815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-' 30825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CRLF = '\r\n' 30835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines = [] 30845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (key, value) in fields: 30855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(key, str) 30865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(value, str) 30875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('--' + BOUNDARY) 30885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('Content-Disposition: form-data; name="%s"' % key) 30895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('') 30905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append(value) 30915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (key, filename, value) in files: 30925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(key, str) 30935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(filename, str) 30945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typecheck(value, str) 30955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('--' + BOUNDARY) 30965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) 30975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('Content-Type: %s' % GetContentType(filename)) 30985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('') 30995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append(value) 31005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('--' + BOUNDARY + '--') 31015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines.append('') 31025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body = CRLF.join(lines) 31035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content_type = 'multipart/form-data; boundary=%s' % BOUNDARY 31045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return content_type, body 31055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetContentType(filename): 31085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Helper to guess the content-type from the filename.""" 31095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return mimetypes.guess_type(filename)[0] or 'application/octet-stream' 31105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use a shell for subcommands on Windows to get a PATH search. 31135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)use_shell = sys.platform.startswith("win") 31145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RunShellWithReturnCode(command, print_output=False, 31165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) universal_newlines=True, env=os.environ): 31175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Executes a command and returns the output from stdout and the return code. 31185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 31205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) command: Command to execute. 31215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print_output: If True, the output is printed to stdout. 31225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) If False, both stdout and stderr are ignored. 31235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) universal_newlines: Use universal_newlines flag (default: True). 31245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 31265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Tuple (output, return code) 31275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 31285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info("Running %s", command) 31295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 31305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) shell=use_shell, universal_newlines=universal_newlines, env=env) 31315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if print_output: 31325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) output_array = [] 31335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while True: 31345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = p.stdout.readline() 31355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not line: 31365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 31375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print line.strip("\n") 31385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) output_array.append(line) 31395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) output = "".join(output_array) 31405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 31415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) output = p.stdout.read() 31425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p.wait() 31435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errout = p.stderr.read() 31445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if print_output and errout: 31455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print >>sys.stderr, errout 31465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p.stdout.close() 31475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p.stderr.close() 31485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return output, p.returncode 31495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RunShell(command, silent_ok=False, universal_newlines=True, 31525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print_output=False, env=os.environ): 31535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data, retcode = RunShellWithReturnCode(command, print_output, universal_newlines, env) 31545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if retcode: 31555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ErrorExit("Got error status from %s:\n%s" % (command, data)) 31565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not silent_ok and not data: 31575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ErrorExit("No output from %s" % command) 31585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return data 31595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class VersionControlSystem(object): 31625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Abstract base class providing an interface to the VCS.""" 31635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, options): 31655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Constructor. 31665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 31685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) options: Command line options. 31695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 31705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.options = options 31715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GenerateDiff(self, args): 31735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Return the current diff as a string. 31745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 31765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args: Extra arguments to pass to the diff command. 31775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 31785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise NotImplementedError( 31795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "abstract method -- subclass %s must override" % self.__class__) 31805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetUnknownFiles(self): 31825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Return a list of files unknown to the VCS.""" 31835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise NotImplementedError( 31845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "abstract method -- subclass %s must override" % self.__class__) 31855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def CheckForUnknownFiles(self): 31875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Show an "are you sure?" prompt if there are unknown files.""" 31885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unknown_files = self.GetUnknownFiles() 31895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if unknown_files: 31905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print "The following files are not added to version control:" 31915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in unknown_files: 31925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print line 31935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prompt = "Are you sure to continue?(y/N) " 31945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) answer = raw_input(prompt).strip() 31955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if answer != "y": 31965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ErrorExit("User aborted") 31975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 31985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetBaseFile(self, filename): 31995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Get the content of the upstream version of a file. 32005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 32025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A tuple (base_content, new_content, is_binary, status) 32035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content: The contents of the base file. 32045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_content: For text files, this is empty. For binary files, this is 32055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the contents of the new file, since the diff output won't contain 32065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) information to reconstruct the current file. 32075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_binary: True iff the file is binary. 32085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) status: The status of the file. 32095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 32105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise NotImplementedError( 32125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "abstract method -- subclass %s must override" % self.__class__) 32135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetBaseFiles(self, diff): 32165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Helper that calls GetBase file for each file in the patch. 32175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 32195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A dictionary that maps from filename to GetBaseFile's tuple. Filenames 32205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) are retrieved based on lines that start with "Index:" or 32215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Property changes on:". 32225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 32235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = {} 32245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in diff.splitlines(True): 32255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('Index:') or line.startswith('Property changes on:'): 32265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unused, filename = line.split(':', 1) 32275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # On Windows if a file has property changes its filename uses '\' 32285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # instead of '/'. 32295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename = to_slash(filename.strip()) 32305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files[filename] = self.GetBaseFile(filename) 32315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return files 32325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, 32355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files): 32365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Uploads the base files (and if necessary, the current ones as well).""" 32375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def UploadFile(filename, file_id, content, is_binary, status, is_base): 32395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Uploads a file to the server.""" 32405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading " + filename) 32415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_too_large = False 32425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if is_base: 32435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) type = "base" 32445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 32455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) type = "current" 32465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(content) > MAX_UPLOAD_SIZE: 32475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print ("Not uploading the %s file for %s because it's too large." % 32485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (type, filename)) 32495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_too_large = True 32505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content = "" 32515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) checksum = md5(content).hexdigest() 32525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if options.verbose > 0 and not file_too_large: 32535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print "Uploading %s file for %s" % (type, filename) 32545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id) 32555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields = [ 32565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("filename", filename), 32575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("status", status), 32585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("checksum", checksum), 32595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("is_binary", str(is_binary)), 32605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("is_current", str(not is_base)), 32615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ] 32625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if file_too_large: 32635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("file_too_large", "1")) 32645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if options.email: 32655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("user", options.email)) 32665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctype, body = EncodeMultipartFormData(form_fields, [("data", filename, content)]) 32675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_body = rpc_server.Send(url, body, content_type=ctype) 32685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not response_body.startswith("OK"): 32695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StatusUpdate(" --> %s" % response_body) 32705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(1) 32715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Don't want to spawn too many threads, nor do we want to 32735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # hit Rietveld too hard, or it will start serving 500 errors. 32745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # When 8 works, it's no better than 4, and sometimes 8 is 32755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # too many for Rietveld to handle. 32765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MAX_PARALLEL_UPLOADS = 4 32775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sema = threading.BoundedSemaphore(MAX_PARALLEL_UPLOADS) 32795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_threads = [] 32805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finished_upload_threads = [] 32815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class UploadFileThread(threading.Thread): 32835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, args): 32845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) threading.Thread.__init__(self) 32855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.args = args 32865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def run(self): 32875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UploadFile(*self.args) 32885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finished_upload_threads.append(self) 32895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sema.release() 32905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def StartUploadFile(*args): 32925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sema.acquire() 32935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while len(finished_upload_threads) > 0: 32945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = finished_upload_threads.pop() 32955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_threads.remove(t) 32965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.join() 32975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = UploadFileThread(args) 32985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upload_threads.append(t) 32995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.start() 33005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def WaitForUploads(): 33025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for t in upload_threads: 33035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t.join() 33045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches = dict() 33065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [patches.setdefault(v, k) for k, v in patch_list] 33075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for filename in patches.keys(): 33085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content, new_content, is_binary, status = files[filename] 33095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_id_str = patches.get(filename) 33105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if file_id_str.find("nobase") != -1: 33115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content = None 33125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_id_str = file_id_str[file_id_str.rfind("_") + 1:] 33135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file_id = int(file_id_str) 33145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if base_content != None: 33155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StartUploadFile(filename, file_id, base_content, is_binary, status, True) 33165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if new_content != None: 33175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StartUploadFile(filename, file_id, new_content, is_binary, status, False) 33185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WaitForUploads() 33195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def IsImage(self, filename): 33215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns true if the filename has an image extension.""" 33225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mimetype = mimetypes.guess_type(filename)[0] 33235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not mimetype: 33245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 33255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return mimetype.startswith("image/") 33265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def IsBinary(self, filename): 33285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns true if the guessed mimetyped isnt't in text group.""" 33295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mimetype = mimetypes.guess_type(filename)[0] 33305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not mimetype: 33315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False # e.g. README, "real" binaries usually have an extension 33325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # special case for text files which don't start with text/ 33335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if mimetype in TEXT_MIMETYPES: 33345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 33355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return not mimetype.startswith("text/") 33365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class FakeMercurialUI(object): 33395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self): 33405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.quiet = True 33415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.output = '' 33425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def write(self, *args, **opts): 33445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.output += ' '.join(args) 33455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def copy(self): 33465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 33475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def status(self, *args, **opts): 33485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 33495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def formatter(self, topic, opts): 33515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from mercurial.formatter import plainformatter 33525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return plainformatter(self, topic, opts) 33535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def readconfig(self, *args, **opts): 33555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 33565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def expandpath(self, *args, **opts): 33575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return global_ui.expandpath(*args, **opts) 33585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def configitems(self, *args, **opts): 33595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return global_ui.configitems(*args, **opts) 33605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def config(self, *args, **opts): 33615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return global_ui.config(*args, **opts) 33625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)use_hg_shell = False # set to True to shell out to hg always; slower 33645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MercurialVCS(VersionControlSystem): 33665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Implementation of the VersionControlSystem interface for Mercurial.""" 33675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, options, ui, repo): 33695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) super(MercurialVCS, self).__init__(options) 33705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.ui = ui 33715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.repo = repo 33725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.status = None 33735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Absolute path to repository (we can be in a subdir) 33745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.repo_dir = os.path.normpath(repo.root) 33755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Compute the subdir 33765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cwd = os.path.normpath(os.getcwd()) 33775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert cwd.startswith(self.repo_dir) 33785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/") 33795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.options.revision: 33805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.base_rev = self.options.revision 33815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 33825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mqparent, err = RunShellWithReturnCode(['hg', 'log', '--rev', 'qparent', '--template={node}']) 33835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not err and mqparent != "": 33845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.base_rev = mqparent 33855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 33865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out = RunShell(["hg", "parents", "-q"], silent_ok=True).strip() 33875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not out: 33885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # No revisions; use 0 to mean a repository with nothing. 33895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out = "0:0" 33905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.base_rev = out.split(':')[1].strip() 33915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _GetRelPath(self, filename): 33925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Get relative path of a file according to the current directory, 33935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) given its logical path in the repo.""" 33945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert filename.startswith(self.subdir), (filename, self.subdir) 33955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return filename[len(self.subdir):].lstrip(r"\/") 33965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 33975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GenerateDiff(self, extra_args): 33985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If no file specified, restrict to the current subdir 33995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra_args = extra_args or ["."] 34005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args 34015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data = RunShell(cmd, silent_ok=True) 34025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) svndiff = [] 34035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filecount = 0 34045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in data.splitlines(): 34055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m = re.match("diff --git a/(\S+) b/(\S+)", line) 34065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if m: 34075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Modify line to make it look like as it comes from svn diff. 34085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # With this modification no changes on the server side are required 34095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # to make upload.py work with Mercurial repos. 34105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # NOTE: for proper handling of moved/copied files, we have to use 34115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # the second filename. 34125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename = m.group(2) 34135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) svndiff.append("Index: %s" % filename) 34145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) svndiff.append("=" * 67) 34155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filecount += 1 34165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info(line) 34175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 34185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) svndiff.append(line) 34195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not filecount: 34205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ErrorExit("No valid patches found in output from hg diff") 34215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "\n".join(svndiff) + "\n" 34225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 34235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetUnknownFiles(self): 34245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Return a list of files unknown to the VCS.""" 34255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args = [] 34265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], 34275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) silent_ok=True) 34285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unknown_files = [] 34295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in status.splitlines(): 34305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) st, fn = line.split(" ", 1) 34315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if st == "?": 34325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unknown_files.append(fn) 34335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return unknown_files 34345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 34355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def get_hg_status(self, rev, path): 34365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # We'd like to use 'hg status -C path', but that is buggy 34375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # (see http://mercurial.selenic.com/bts/issue3023). 34385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Instead, run 'hg status -C' without a path 34395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # and skim the output for the path we want. 34405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.status is None: 34415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if use_hg_shell: 34425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out = RunShell(["hg", "status", "-C", "--rev", rev]) 34435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 34445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fui = FakeMercurialUI() 34455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ret = hg_commands.status(fui, self.repo, *[], **{'rev': [rev], 'copies': True}) 34465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ret: 34475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort(ret) 34485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out = fui.output 34495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.status = out.splitlines() 34505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(self.status)): 34515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # line is 34525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # A path 34535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # M path 34545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # etc 34555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line = to_slash(self.status[i]) 34565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line[2:] == path: 34575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if i+1 < len(self.status) and self.status[i+1][:2] == ' ': 34585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.status[i:i+2] 34595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.status[i:i+1] 34605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise hg_util.Abort("no status for " + path) 34615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 34625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetBaseFile(self, filename): 34635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("inspecting " + filename) 34645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # "hg status" and "hg cat" both take a path relative to the current subdir 34655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # rather than to the repo root, but "hg diff" has given us the full path 34665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # to the repo root. 34675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content = "" 34685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_content = None 34695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_binary = False 34705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) oldrelpath = relpath = self._GetRelPath(filename) 34715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out = self.get_hg_status(self.base_rev, relpath) 34725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) status, what = out[0].split(' ', 1) 34735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(out) > 1 and status == "A" and what == relpath: 34745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) oldrelpath = out[1].strip() 34755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) status = "M" 34765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ":" in self.base_rev: 34775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_rev = self.base_rev.split(":", 1)[0] 34785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 34795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_rev = self.base_rev 34805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if status != "A": 34815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if use_hg_shell: 34825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], silent_ok=True) 34835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 34845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content = str(self.repo[base_rev][oldrelpath].data()) 34855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_binary = "\0" in base_content # Mercurial's heuristic 34865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if status != "R": 34875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_content = open(relpath, "rb").read() 34885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_binary = is_binary or "\0" in new_content 34895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if is_binary and base_content and use_hg_shell: 34905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Fetch again without converting newlines 34915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], 34925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) silent_ok=True, universal_newlines=False) 34935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not is_binary or not self.IsImage(relpath): 34945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_content = None 34955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base_content, new_content, is_binary, status 34965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 34975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 34985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync. 34995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def SplitPatch(data): 35005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Splits a patch into separate pieces for each file. 35015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 35035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data: A string containing the output of svn diff. 35045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 35065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A list of 2-tuple (filename, text) where text is the svn diff output 35075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pertaining to filename. 35085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 35095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches = [] 35105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename = None 35115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diff = [] 35125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in data.splitlines(True): 35135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_filename = None 35145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if line.startswith('Index:'): 35155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unused, new_filename = line.split(':', 1) 35165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_filename = new_filename.strip() 35175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif line.startswith('Property changes on:'): 35185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unused, temp_filename = line.split(':', 1) 35195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # When a file is modified, paths use '/' between directories, however 35205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # when a property is modified '\' is used on Windows. Make them the same 35215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # otherwise the file shows up twice. 35225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) temp_filename = to_slash(temp_filename.strip()) 35235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if temp_filename != filename: 35245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # File has property changes but no modifications, create a new diff. 35255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_filename = temp_filename 35265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if new_filename: 35275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if filename and diff: 35285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches.append((filename, ''.join(diff))) 35295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename = new_filename 35305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diff = [line] 35315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 35325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if diff is not None: 35335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) diff.append(line) 35345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if filename and diff: 35355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches.append((filename, ''.join(diff))) 35365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return patches 35375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def UploadSeparatePatches(issue, rpc_server, patchset, data, options): 35405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Uploads a separate patch for each file in the diff output. 35415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns a list of [patch_key, filename] for each file. 35435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 35445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) patches = SplitPatch(data) 35455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rv = [] 35465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for patch in patches: 35475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_status("uploading patch for " + patch[0]) 35485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(patch[1]) > MAX_UPLOAD_SIZE: 35495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print ("Not uploading the patch for " + patch[0] + 35505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " because the file is too large.") 35515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 35525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields = [("filename", patch[0])] 35535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not options.download_base: 35545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) form_fields.append(("content_upload", "1")) 35555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = [("data", "data.diff", patch[1])] 35565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctype, body = EncodeMultipartFormData(form_fields, files) 35575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) 35585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print "Uploading patch for " + patch[0] 35595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_body = rpc_server.Send(url, body, content_type=ctype) 35605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines = response_body.splitlines() 35615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not lines or lines[0] != "OK": 35625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StatusUpdate(" --> %s" % response_body) 35635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(1) 35645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rv.append([lines[1], patch[0]]) 35655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return rv 3566