codereview: import latest from go

R=rsc
http://codereview.appspot.com/1922042
This commit is contained in:
Russ Cox 2010-08-04 17:36:17 -07:00
parent 860d327c8f
commit a06877afa9

View file

@ -54,6 +54,12 @@ except:
from mercurial.version import version as v from mercurial.version import version as v
hgversion = v.get_version() hgversion = v.get_version()
try:
from mercurial.discovery import findcommonincoming
except:
def findcommonincoming(repo, remote):
return repo.findcommonincoming(remote)
oldMessage = """ oldMessage = """
The code review extension requires Mercurial 1.3 or newer. The code review extension requires Mercurial 1.3 or newer.
@ -77,6 +83,15 @@ if hgversion < '1.3':
msg += linuxMessage msg += linuxMessage
raise util.Abort(msg) raise util.Abort(msg)
def promptyesno(ui, msg):
# Arguments to ui.prompt changed between 1.3 and 1.3.1.
# Even so, some 1.3.1 distributions seem to have the old prompt!?!?
# What a terrible way to maintain software.
try:
return ui.promptchoice(msg, ["&yes", "&no"], 0) == 0
except AttributeError:
return ui.prompt(msg, ["&yes", "&no"], "y") != "n"
# To experiment with Mercurial in the python interpreter: # To experiment with Mercurial in the python interpreter:
# >>> repo = hg.repository(ui.ui(), path = ".") # >>> repo = hg.repository(ui.ui(), path = ".")
@ -92,6 +107,7 @@ if __name__ == "__main__":
server = "codereview.appspot.com" server = "codereview.appspot.com"
server_url_base = None server_url_base = None
defaultcc = None defaultcc = None
contributors = {}
####################################################################### #######################################################################
# Change list parsing. # Change list parsing.
@ -331,7 +347,7 @@ def ParseCL(text, name):
if sections['Mailed'] != 'False': if sections['Mailed'] != 'False':
# Odd default, but avoids spurious mailings when # Odd default, but avoids spurious mailings when
# reading old CLs that do not have a Mailed: line. # reading old CLs that do not have a Mailed: line.
# CLs created with this update will always have # CLs created with this update will always have
# Mailed: False on disk. # Mailed: False on disk.
cl.mailed = True cl.mailed = True
if cl.desc == '<enter description here>': if cl.desc == '<enter description here>':
@ -339,7 +355,10 @@ def ParseCL(text, name):
return cl, 0, '' return cl, 0, ''
def SplitCommaSpace(s): def SplitCommaSpace(s):
return re.sub(", *", ",", s).split(",") s = s.strip()
if s == "":
return []
return re.split(", *", s)
def CutDomain(s): def CutDomain(s):
i = s.find('@') i = s.find('@')
@ -521,11 +540,18 @@ _change_prolog = """# Change list.
####################################################################### #######################################################################
# Mercurial helper functions # Mercurial helper functions
# Get effective change nodes taking into account applied MQ patches
def effective_revpair(repo):
try:
return cmdutil.revpair(repo, ['qparent'])
except:
return cmdutil.revpair(repo, None)
# Return list of changed files in repository that match pats. # Return list of changed files in repository that match pats.
def ChangedFiles(ui, repo, pats, opts): def ChangedFiles(ui, repo, pats, opts):
# Find list of files being operated on. # Find list of files being operated on.
matcher = cmdutil.match(repo, pats, opts) matcher = cmdutil.match(repo, pats, opts)
node1, node2 = cmdutil.revpair(repo, None) node1, node2 = effective_revpair(repo)
modified, added, removed = repo.status(node1, node2, matcher)[:3] modified, added, removed = repo.status(node1, node2, matcher)[:3]
l = modified + added + removed l = modified + added + removed
l.sort() l.sort()
@ -534,7 +560,7 @@ def ChangedFiles(ui, repo, pats, opts):
# Return list of changed files in repository that match pats and still exist. # Return list of changed files in repository that match pats and still exist.
def ChangedExistingFiles(ui, repo, pats, opts): def ChangedExistingFiles(ui, repo, pats, opts):
matcher = cmdutil.match(repo, pats, opts) matcher = cmdutil.match(repo, pats, opts)
node1, node2 = cmdutil.revpair(repo, None) node1, node2 = effective_revpair(repo)
modified, added, _ = repo.status(node1, node2, matcher)[:3] modified, added, _ = repo.status(node1, node2, matcher)[:3]
l = modified + added l = modified + added
l.sort() l.sort()
@ -571,14 +597,18 @@ def getremote(ui, repo, opts):
# save $http_proxy; creating the HTTP repo object will # save $http_proxy; creating the HTTP repo object will
# delete it in an attempt to "help" # delete it in an attempt to "help"
proxy = os.environ.get('http_proxy') proxy = os.environ.get('http_proxy')
source, _, _ = hg.parseurl(ui.expandpath("default"), None) source = hg.parseurl(ui.expandpath("default"), None)[0]
other = hg.repository(cmdutil.remoteui(repo, opts), source) try:
remoteui = hg.remoteui # hg 1.6
except:
remoteui = cmdutil.remoteui
other = hg.repository(remoteui(repo, opts), source)
if proxy is not None: if proxy is not None:
os.environ['http_proxy'] = proxy os.environ['http_proxy'] = proxy
return other return other
def Incoming(ui, repo, opts): def Incoming(ui, repo, opts):
_, incoming, _ = repo.findcommonincoming(getremote(ui, repo, opts)) _, incoming, _ = findcommonincoming(repo, getremote(ui, repo, opts))
return incoming return incoming
def EditCL(ui, repo, cl): def EditCL(ui, repo, cl):
@ -587,7 +617,7 @@ def EditCL(ui, repo, cl):
s = ui.edit(s, ui.username()) s = ui.edit(s, ui.username())
clx, line, err = ParseCL(s, cl.name) clx, line, err = ParseCL(s, cl.name)
if err != '': if err != '':
if ui.prompt("error parsing change list: line %d: %s\nre-edit (y/n)?" % (line, err), ["&yes", "&no"], "y") == "n": if not promptyesno(ui, "error parsing change list: line %d: %s\nre-edit (y/n)?" % (line, err)):
return "change list not modified" return "change list not modified"
continue continue
cl.desc = clx.desc; cl.desc = clx.desc;
@ -595,7 +625,7 @@ def EditCL(ui, repo, cl):
cl.cc = clx.cc cl.cc = clx.cc
cl.files = clx.files cl.files = clx.files
if cl.desc == '': if cl.desc == '':
if ui.prompt("change list should have description\nre-edit (y/n)?", ["&yes", "&no"], "y") != "n": if promptyesno(ui, "change list should have description\nre-edit (y/n)?"):
continue continue
break break
return "" return ""
@ -640,6 +670,7 @@ original_match = None
def ReplacementForCmdutilMatch(repo, pats=[], opts={}, globbed=False, default='relpath'): def ReplacementForCmdutilMatch(repo, pats=[], opts={}, globbed=False, default='relpath'):
taken = [] taken = []
files = [] files = []
pats = pats or []
for p in pats: for p in pats:
if p.startswith('@'): if p.startswith('@'):
taken.append(p) taken.append(p)
@ -652,7 +683,7 @@ def ReplacementForCmdutilMatch(repo, pats=[], opts={}, globbed=False, default='r
if cl.files == None: if cl.files == None:
raise util.Abort("no files in CL " + clname) raise util.Abort("no files in CL " + clname)
files = Add(files, cl.files) files = Add(files, cl.files)
pats = Sub(pats, taken) + ['path:'+f for f in files] pats = Sub(pats, taken) + ['path:'+f for f in files]
return original_match(repo, pats=pats, opts=opts, globbed=globbed, default=default) return original_match(repo, pats=pats, opts=opts, globbed=globbed, default=default)
def RelativePath(path, cwd): def RelativePath(path, cwd):
@ -669,6 +700,8 @@ def CheckGofmt(ui, repo, files, just_warn=False):
cwd = os.getcwd() cwd = os.getcwd()
files = [RelativePath(repo.root + '/' + f, cwd) for f in files] files = [RelativePath(repo.root + '/' + f, cwd) for f in files]
files = [f for f in files if os.access(f, 0)] files = [f for f in files if os.access(f, 0)]
if not files:
return
try: try:
cmd = subprocess.Popen(["gofmt", "-l"] + files, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) cmd = subprocess.Popen(["gofmt", "-l"] + files, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
cmd.stdin.close() cmd.stdin.close()
@ -700,9 +733,9 @@ def CheckGofmt(ui, repo, files, just_warn=False):
# #
def change(ui, repo, *pats, **opts): def change(ui, repo, *pats, **opts):
"""create or edit a change list """create, edit or delete a change list
Create or edit a change list. Create, edit or delete a change list.
A change list is a group of files to be reviewed and submitted together, A change list is a group of files to be reviewed and submitted together,
plus a textual description of the change. plus a textual description of the change.
Change lists are referred to by simple alphanumeric names. Change lists are referred to by simple alphanumeric names.
@ -818,6 +851,8 @@ def clpatch(ui, repo, clname, **opts):
""" """
cl, patch, err = DownloadCL(ui, repo, clname) cl, patch, err = DownloadCL(ui, repo, clname)
argv = ["hgpatch"] argv = ["hgpatch"]
if opts["fuzzy"]:
argv += ["--fuzzy"]
if opts["no_incoming"]: if opts["no_incoming"]:
argv += ["--checksync=false"] argv += ["--checksync=false"]
if err != "": if err != "":
@ -948,8 +983,16 @@ def mail(ui, repo, *pats, **opts):
if err != "": if err != "":
return err return err
cl.Upload(ui, repo, gofmt_just_warn=True) cl.Upload(ui, repo, gofmt_just_warn=True)
if not cl.reviewer and not cl.cc: if not cl.reviewer:
return "no reviewers listed in CL" # If no reviewer is listed, assign the review to defaultcc.
# This makes sure that it appears in the
# codereview.appspot.com/user/defaultcc
# page, so that it doesn't get dropped on the floor.
if not defaultcc:
return "no reviewers listed in CL"
cl.cc = Sub(cl.cc, defaultcc)
cl.reviewer = defaultcc
cl.Flush(ui, repo)
cl.Mail(ui, repo) cl.Mail(ui, repo)
def nocommit(ui, repo, *pats, **opts): def nocommit(ui, repo, *pats, **opts):
@ -993,22 +1036,17 @@ def CheckContributor(ui, repo, user=None):
return userline return userline
def FindContributor(ui, repo, user, warn=True): def FindContributor(ui, repo, user, warn=True):
try: m = re.match(r".*<(.*)>", user)
f = open(repo.root + '/CONTRIBUTORS', 'r') if m:
except: user = m.group(1).lower()
raise util.Abort("cannot open %s: %s" % (repo.root+'/CONTRIBUTORS', ExceptionDetail()))
for line in f.readlines(): if user not in contributors:
line = line.rstrip() if warn:
if line.startswith('#'): ui.warn("warning: cannot find %s in CONTRIBUTORS\n" % (user,))
continue return None, None
match = re.match(r"(.*) <(.*)>", line)
if not match: user, email = contributors[user]
continue return email, "%s <%s>" % (user, email)
if line == user or match.group(2).lower() == user.lower():
return match.group(2), line
if warn:
ui.warn("warning: cannot find %s in CONTRIBUTORS\n" % (user,))
return None, None
def submit(ui, repo, *pats, **opts): def submit(ui, repo, *pats, **opts):
"""submit change to remote repository """submit change to remote repository
@ -1246,6 +1284,7 @@ cmdtable = {
[ [
('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'),
('', 'no_incoming', None, 'disable check for incoming changes'), ('', 'no_incoming', None, 'disable check for incoming changes'),
('', 'fuzzy', None, 'attempt to adjust patch line numbers'),
], ],
"change#" "change#"
), ),
@ -1572,7 +1611,7 @@ def PostMessage(ui, issue, message, reviewers=None, cc=None, send_mail=True, sub
if subject is not None: if subject is not None:
form_fields['subject'] = subject form_fields['subject'] = subject
form_fields['message'] = message form_fields['message'] = message
form_fields['message_only'] = '1' # Don't include draft comments form_fields['message_only'] = '1' # Don't include draft comments
if reviewers is not None or cc is not None: if reviewers is not None or cc is not None:
form_fields['message_only'] = '' # Must set '' in order to override cc/reviewer form_fields['message_only'] = '' # Must set '' in order to override cc/reviewer
@ -1587,7 +1626,7 @@ class opt(object):
pass pass
def RietveldSetup(ui, repo): def RietveldSetup(ui, repo):
global defaultcc, upload_options, rpc, server, server_url_base, force_google_account, verbosity global defaultcc, upload_options, rpc, server, server_url_base, force_google_account, verbosity, contributors
# Read repository-specific options from lib/codereview/codereview.cfg # Read repository-specific options from lib/codereview/codereview.cfg
try: try:
@ -1596,7 +1635,31 @@ def RietveldSetup(ui, repo):
if line.startswith('defaultcc: '): if line.startswith('defaultcc: '):
defaultcc = SplitCommaSpace(line[10:]) defaultcc = SplitCommaSpace(line[10:])
except: except:
pass # If there are no options, chances are good this is not
# a code review repository; stop now before we foul
# things up even worse. Might also be that repo doesn't
# even have a root. See issue 959.
return
try:
f = open(repo.root + '/CONTRIBUTORS', 'r')
except:
raise util.Abort("cannot open %s: %s" % (repo.root+'/CONTRIBUTORS', ExceptionDetail()))
for line in f:
# CONTRIBUTORS is a list of lines like:
# Person <email>
# Person <email> <alt-email>
# The first email address is the one used in commit logs.
if line.startswith('#'):
continue
m = re.match(r"([^<>]+\S)\s+(<[^<>\s]+>)((\s+<[^<>\s]+>)*)\s*$", line)
if m:
name = m.group(1)
email = m.group(2)[1:-1]
contributors[email.lower()] = (name, email)
for extra in m.group(3).split():
contributors[extra[1:-1].lower()] = (name, email)
# TODO(rsc): If the repository config has no codereview section, # TODO(rsc): If the repository config has no codereview section,
# do not enable the extension. This allows users to # do not enable the extension. This allows users to
@ -2808,8 +2871,11 @@ class MercurialVCS(VersionControlSystem):
if self.options.revision: if self.options.revision:
self.base_rev = self.options.revision self.base_rev = self.options.revision
else: else:
self.base_rev = RunShell(["hg", "parent", "-q"]).split(':')[1].strip() mqparent, err = RunShellWithReturnCode(['hg', 'log', '--rev', 'qparent', '--template={node}'])
if not err:
self.base_rev = mqparent
else:
self.base_rev = RunShell(["hg", "parents", "-q"]).split(':')[1].strip()
def _GetRelPath(self, filename): def _GetRelPath(self, filename):
"""Get relative path of a file according to the current directory, """Get relative path of a file according to the current directory,
given its logical path in the repo.""" given its logical path in the repo."""
@ -2869,13 +2935,10 @@ class MercurialVCS(VersionControlSystem):
# the working copy # the working copy
if out[0].startswith('%s: ' % relpath): if out[0].startswith('%s: ' % relpath):
out = out[1:] out = out[1:]
if len(out) > 1: status, what = out[0].split(' ', 1)
# Moved/copied => considered as modified, use old filename to if len(out) > 1 and status == "A" and what == relpath:
# retrieve base contents
oldrelpath = out[1].strip() oldrelpath = out[1].strip()
status = "M" status = "M"
else:
status, _ = out[0].split(' ', 1)
if ":" in self.base_rev: if ":" in self.base_rev:
base_rev = self.base_rev.split(":", 1)[0] base_rev = self.base_rev.split(":", 1)[0]
else: else: