codereview: update from go

R=rsc
http://codereview.appspot.com/5340059
This commit is contained in:
Russ Cox 2011-11-08 09:03:05 -05:00
parent db60da46b3
commit 338bd4bb79

View file

@ -38,7 +38,7 @@ For example, if change 123456 contains the files x.go and y.go,
"hg diff @123456" is equivalent to"hg diff x.go y.go". "hg diff @123456" is equivalent to"hg diff x.go y.go".
''' '''
from mercurial import cmdutil, commands, hg, util, error, match from mercurial import cmdutil, commands, hg, util, error, match, discovery
from mercurial.node import nullrev, hex, nullid, short from mercurial.node import nullrev, hex, nullid, short
import os, re, time import os, re, time
import stat import stat
@ -69,11 +69,11 @@ except:
from mercurial.version import version as v from mercurial.version import version as v
hgversion = v.get_version() hgversion = v.get_version()
try: # in Mercurial 1.9 the cmdutil.match and cmdutil.revpair moved to scmutil
from mercurial.discovery import findcommonincoming if hgversion >= '1.9':
except: from mercurial import scmutil
def findcommonincoming(repo, remote): else:
return repo.findcommonincoming(remote) scmutil = cmdutil
oldMessage = """ oldMessage = """
The code review extension requires Mercurial 1.3 or newer. The code review extension requires Mercurial 1.3 or newer.
@ -107,6 +107,22 @@ def promptyesno(ui, msg):
except AttributeError: except AttributeError:
return ui.prompt(msg, ["&yes", "&no"], "y") != "n" return ui.prompt(msg, ["&yes", "&no"], "y") != "n"
def incoming(repo, other):
fui = FakeMercurialUI()
ret = commands.incoming(fui, repo, *[other.path], **{'bundle': '', 'force': False})
if ret and ret != 1:
raise util.Abort(ret)
out = fui.output
return out
def outgoing(repo):
fui = FakeMercurialUI()
ret = commands.outgoing(fui, repo, *[], **{})
if ret and ret != 1:
raise util.Abort(ret)
out = fui.output
return out
# 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 = ".")
@ -176,7 +192,9 @@ set_mercurial_encoding_to_utf8()
# encoding for all of Python to 'utf-8', not 'ascii'. # encoding for all of Python to 'utf-8', not 'ascii'.
def default_to_utf8(): def default_to_utf8():
import sys import sys
stdout, __stdout__ = sys.stdout, sys.__stdout__
reload(sys) # site.py deleted setdefaultencoding; get it back reload(sys) # site.py deleted setdefaultencoding; get it back
sys.stdout, sys.__stdout__ = stdout, __stdout__
sys.setdefaultencoding('utf-8') sys.setdefaultencoding('utf-8')
default_to_utf8() default_to_utf8()
@ -212,6 +230,7 @@ class CL(object):
self.copied_from = None # None means current user self.copied_from = None # None means current user
self.mailed = False self.mailed = False
self.private = False self.private = False
self.lgtm = []
def DiskText(self): def DiskText(self):
cl = self cl = self
@ -264,6 +283,8 @@ class CL(object):
if cl.copied_from: if cl.copied_from:
s += "\tAuthor: " + cl.copied_from + "\n" s += "\tAuthor: " + cl.copied_from + "\n"
s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n" s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n"
for (who, line) in cl.lgtm:
s += "\t\t" + who + ": " + line + "\n"
s += "\tCC: " + JoinComma(cl.cc) + "\n" s += "\tCC: " + JoinComma(cl.cc) + "\n"
s += "\tFiles:\n" s += "\tFiles:\n"
for f in cl.files: for f in cl.files:
@ -536,6 +557,13 @@ def LoadCL(ui, repo, name, web=True):
cl.url = server_url_base + name cl.url = server_url_base + name
cl.web = True cl.web = True
cl.private = d.get('private', False) != False cl.private = d.get('private', False) != False
cl.lgtm = []
for m in d.get('messages', []):
if m.get('approval', False) == True:
who = re.sub('@.*', '', m.get('sender', ''))
text = re.sub("\n(.|\n)*", '', m.get('text', ''))
cl.lgtm.append((who, text))
set_status("loaded CL " + name) set_status("loaded CL " + name)
return cl, '' return cl, ''
@ -713,14 +741,14 @@ _change_prolog = """# Change list.
# Get effective change nodes taking into account applied MQ patches # Get effective change nodes taking into account applied MQ patches
def effective_revpair(repo): def effective_revpair(repo):
try: try:
return cmdutil.revpair(repo, ['qparent']) return scmutil.revpair(repo, ['qparent'])
except: except:
return cmdutil.revpair(repo, None) return scmutil.revpair(repo, None)
# Return list of changed files in repository that match pats. # Return list of changed files in repository that match pats.
# Warn about patterns that did not match. # Warn about patterns that did not match.
def matchpats(ui, repo, pats, opts): def matchpats(ui, repo, pats, opts):
matcher = cmdutil.match(repo, pats, opts) matcher = scmutil.match(repo, pats, opts)
node1, node2 = effective_revpair(repo) node1, node2 = effective_revpair(repo)
modified, added, removed, deleted, unknown, ignored, clean = repo.status(node1, node2, matcher, ignored=True, clean=True, unknown=True) modified, added, removed, deleted, unknown, ignored, clean = repo.status(node1, node2, matcher, ignored=True, clean=True, unknown=True)
return (modified, added, removed, deleted, unknown, ignored, clean) return (modified, added, removed, deleted, unknown, ignored, clean)
@ -802,10 +830,6 @@ def getremote(ui, repo, opts):
os.environ['http_proxy'] = proxy os.environ['http_proxy'] = proxy
return other return other
def Incoming(ui, repo, opts):
_, incoming, _ = findcommonincoming(repo, getremote(ui, repo, opts))
return incoming
desc_re = '^(.+: |(tag )?(release|weekly)\.|fix build|undo CL)' desc_re = '^(.+: |(tag )?(release|weekly)\.|fix build|undo CL)'
desc_msg = '''Your CL description appears not to use the standard form. desc_msg = '''Your CL description appears not to use the standard form.
@ -827,7 +851,6 @@ Examples:
''' '''
def promptremove(ui, repo, f): def promptremove(ui, repo, f):
if promptyesno(ui, "hg remove %s (y/n)?" % (f,)): if promptyesno(ui, "hg remove %s (y/n)?" % (f,)):
@ -844,6 +867,18 @@ def EditCL(ui, repo, cl):
s = cl.EditorText() s = cl.EditorText()
while True: while True:
s = ui.edit(s, ui.username()) s = ui.edit(s, ui.username())
# We can't trust Mercurial + Python not to die before making the change,
# so, by popular demand, just scribble the most recent CL edit into
# $(hg root)/last-change so that if Mercurial does die, people
# can look there for their work.
try:
f = open(repo.root+"/last-change", "w")
f.write(s)
f.close()
except:
pass
clx, line, err = ParseCL(s, cl.name) clx, line, err = ParseCL(s, cl.name)
if err != '': if err != '':
if not promptyesno(ui, "error parsing change list: line %d: %s\nre-edit (y/n)?" % (line, err)): if not promptyesno(ui, "error parsing change list: line %d: %s\nre-edit (y/n)?" % (line, err)):
@ -941,25 +976,35 @@ def CommandLineCL(ui, repo, pats, opts, defaultcc=None):
# which expands the syntax @clnumber to mean the files # which expands the syntax @clnumber to mean the files
# in that CL. # in that CL.
original_match = None original_match = None
def ReplacementForCmdutilMatch(repo, pats=None, opts=None, globbed=False, default='relpath'): global_repo = None
global_ui = None
def ReplacementForCmdutilMatch(ctx, pats=None, opts=None, globbed=False, default='relpath'):
taken = [] taken = []
files = [] files = []
pats = pats or [] pats = pats or []
opts = opts or {} opts = opts or {}
for p in pats: for p in pats:
if p.startswith('@'): if p.startswith('@'):
taken.append(p) taken.append(p)
clname = p[1:] clname = p[1:]
if not GoodCLName(clname): if clname == "default":
raise util.Abort("invalid CL name " + clname) files = DefaultFiles(global_ui, global_repo, [], opts)
cl, err = LoadCL(repo.ui, repo, clname, web=False) else:
if err != '': if not GoodCLName(clname):
raise util.Abort("loading CL " + clname + ": " + err) raise util.Abort("invalid CL name " + clname)
if not cl.files: cl, err = LoadCL(global_repo.ui, global_repo, clname, web=False)
raise util.Abort("no files in CL " + clname) if err != '':
files = Add(files, cl.files) raise util.Abort("loading CL " + clname + ": " + err)
if not cl.files:
raise util.Abort("no files in CL " + clname)
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)
# work-around for http://selenic.com/hg/rev/785bbc8634f8
if hgversion >= '1.9' and not hasattr(ctx, 'match'):
ctx = ctx[None]
return original_match(ctx, pats=pats, opts=opts, globbed=globbed, default=default)
def RelativePath(path, cwd): def RelativePath(path, cwd):
n = len(cwd) n = len(cwd)
@ -1292,7 +1337,7 @@ def clpatch_or_undo(ui, repo, clname, opts, mode):
# sequence numbers get to be 7 digits long. # sequence numbers get to be 7 digits long.
if re.match('^[0-9]{7,}$', clname): if re.match('^[0-9]{7,}$', clname):
found = False found = False
matchfn = cmdutil.match(repo, [], {'rev': None}) matchfn = scmutil.match(repo, [], {'rev': None})
def prep(ctx, fns): def prep(ctx, fns):
pass pass
for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev': None}, prep): for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev': None}, prep):
@ -1606,9 +1651,12 @@ def pending(ui, repo, *pats, **opts):
def reposetup(ui, repo): def reposetup(ui, repo):
global original_match global original_match
if original_match is None: if original_match is None:
global global_repo, global_ui
global_repo = repo
global_ui = ui
start_status_thread() start_status_thread()
original_match = cmdutil.match original_match = scmutil.match
cmdutil.match = ReplacementForCmdutilMatch scmutil.match = ReplacementForCmdutilMatch
RietveldSetup(ui, repo) RietveldSetup(ui, repo)
def CheckContributor(ui, repo, user=None): def CheckContributor(ui, repo, user=None):
@ -1648,8 +1696,9 @@ def submit(ui, repo, *pats, **opts):
# We already called this on startup but sometimes Mercurial forgets. # We already called this on startup but sometimes Mercurial forgets.
set_mercurial_encoding_to_utf8() set_mercurial_encoding_to_utf8()
other = getremote(ui, repo, opts)
repo.ui.quiet = True repo.ui.quiet = True
if not opts["no_incoming"] and Incoming(ui, repo, opts): if not opts["no_incoming"] and incoming(repo, other):
return "local repository out of date; must sync before submit" return "local repository out of date; must sync before submit"
cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc) cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc)
@ -1712,6 +1761,12 @@ def submit(ui, repo, *pats, **opts):
print Indent('\n'.join(cl.files), "\t") print Indent('\n'.join(cl.files), "\t")
return "dry run; not submitted" return "dry run; not submitted"
set_status("pushing " + cl.name + " to remote server")
other = getremote(ui, repo, opts)
if outgoing(repo):
raise util.Abort("local repository corrupt or out-of-phase with remote: found outgoing changes")
m = match.exact(repo.root, repo.getcwd(), cl.files) m = match.exact(repo.root, repo.getcwd(), cl.files)
node = repo.commit(ustr(opts['message']), ustr(userline), opts.get('date'), m) node = repo.commit(ustr(opts['message']), ustr(userline), opts.get('date'), m)
if not node: if not node:
@ -1732,7 +1787,6 @@ def submit(ui, repo, *pats, **opts):
# push changes to remote. # push changes to remote.
# if it works, we're committed. # if it works, we're committed.
# if not, roll back # if not, roll back
other = getremote(ui, repo, opts)
r = repo.push(other, False, None) r = repo.push(other, False, None)
if r == 0: if r == 0:
raise util.Abort("local repository out of date; must sync before submit") raise util.Abort("local repository out of date; must sync before submit")
@ -1828,7 +1882,7 @@ def sync_changes(ui, repo):
break break
Rev(rev) Rev(rev)
else: else:
matchfn = cmdutil.match(repo, [], {'rev': None}) matchfn = scmutil.match(repo, [], {'rev': None})
def prep(ctx, fns): def prep(ctx, fns):
pass pass
for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev': None}, prep): for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev': None}, prep):
@ -3087,6 +3141,7 @@ class VersionControlSystem(object):
return False return False
return not mimetype.startswith("text/") return not mimetype.startswith("text/")
class FakeMercurialUI(object): class FakeMercurialUI(object):
def __init__(self): def __init__(self):
self.quiet = True self.quiet = True
@ -3094,6 +3149,19 @@ class FakeMercurialUI(object):
def write(self, *args, **opts): def write(self, *args, **opts):
self.output += ' '.join(args) self.output += ' '.join(args)
def copy(self):
return self
def status(self, *args, **opts):
pass
def readconfig(self, *args, **opts):
pass
def expandpath(self, *args, **opts):
return global_ui.expandpath(*args, **opts)
def configitems(self, *args, **opts):
return global_ui.configitems(*args, **opts)
def config(self, *args, **opts):
return global_ui.config(*args, **opts)
use_hg_shell = False # set to True to shell out to hg always; slower use_hg_shell = False # set to True to shell out to hg always; slower
@ -3104,6 +3172,7 @@ class MercurialVCS(VersionControlSystem):
super(MercurialVCS, self).__init__(options) super(MercurialVCS, self).__init__(options)
self.ui = ui self.ui = ui
self.repo = repo self.repo = repo
self.status = None
# Absolute path to repository (we can be in a subdir) # Absolute path to repository (we can be in a subdir)
self.repo_dir = os.path.normpath(repo.root) self.repo_dir = os.path.normpath(repo.root)
# Compute the subdir # Compute the subdir
@ -3162,6 +3231,33 @@ class MercurialVCS(VersionControlSystem):
unknown_files.append(fn) unknown_files.append(fn)
return unknown_files return unknown_files
def get_hg_status(self, rev, path):
# We'd like to use 'hg status -C path', but that is buggy
# (see http://mercurial.selenic.com/bts/issue3023).
# Instead, run 'hg status -C' without a path
# and skim the output for the path we want.
if self.status is None:
if use_hg_shell:
out = RunShell(["hg", "status", "-C", "--rev", rev])
else:
fui = FakeMercurialUI()
ret = commands.status(fui, self.repo, *[], **{'rev': [rev], 'copies': True})
if ret:
raise util.Abort(ret)
out = fui.output
self.status = out.splitlines()
for i in range(len(self.status)):
# line is
# A path
# M path
# etc
line = self.status[i].replace('\\', '/')
if line[2:] == path:
if i+1 < len(self.status) and self.status[i+1][:2] == ' ':
return self.status[i:i+2]
return self.status[i:i+1]
raise util.Abort("no status for " + path)
def GetBaseFile(self, filename): def GetBaseFile(self, filename):
set_status("inspecting " + filename) set_status("inspecting " + filename)
# "hg status" and "hg cat" both take a path relative to the current subdir # "hg status" and "hg cat" both take a path relative to the current subdir
@ -3171,20 +3267,7 @@ class MercurialVCS(VersionControlSystem):
new_content = None new_content = None
is_binary = False is_binary = False
oldrelpath = relpath = self._GetRelPath(filename) oldrelpath = relpath = self._GetRelPath(filename)
# "hg status -C" returns two lines for moved/copied files, one otherwise out = self.get_hg_status(self.base_rev, relpath)
if use_hg_shell:
out = RunShell(["hg", "status", "-C", "--rev", self.base_rev, relpath])
else:
fui = FakeMercurialUI()
ret = commands.status(fui, self.repo, *[relpath], **{'rev': [self.base_rev], 'copies': True})
if ret:
raise util.Abort(ret)
out = fui.output
out = out.splitlines()
# HACK: strip error message about missing file/directory if it isn't in
# the working copy
if out[0].startswith('%s: ' % relpath):
out = out[1:]
status, what = out[0].split(' ', 1) status, what = out[0].split(' ', 1)
if len(out) > 1 and status == "A" and what == relpath: if len(out) > 1 and status == "A" and what == relpath:
oldrelpath = out[1].strip() oldrelpath = out[1].strip()