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".
'''
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
import os, re, time
import stat
@ -69,11 +69,11 @@ except:
from mercurial.version import version as v
hgversion = v.get_version()
try:
from mercurial.discovery import findcommonincoming
except:
def findcommonincoming(repo, remote):
return repo.findcommonincoming(remote)
# in Mercurial 1.9 the cmdutil.match and cmdutil.revpair moved to scmutil
if hgversion >= '1.9':
from mercurial import scmutil
else:
scmutil = cmdutil
oldMessage = """
The code review extension requires Mercurial 1.3 or newer.
@ -107,6 +107,22 @@ def promptyesno(ui, msg):
except AttributeError:
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:
# >>> 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'.
def default_to_utf8():
import sys
stdout, __stdout__ = sys.stdout, sys.__stdout__
reload(sys) # site.py deleted setdefaultencoding; get it back
sys.stdout, sys.__stdout__ = stdout, __stdout__
sys.setdefaultencoding('utf-8')
default_to_utf8()
@ -212,6 +230,7 @@ class CL(object):
self.copied_from = None # None means current user
self.mailed = False
self.private = False
self.lgtm = []
def DiskText(self):
cl = self
@ -264,6 +283,8 @@ class CL(object):
if cl.copied_from:
s += "\tAuthor: " + cl.copied_from + "\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 += "\tFiles:\n"
for f in cl.files:
@ -536,6 +557,13 @@ def LoadCL(ui, repo, name, web=True):
cl.url = server_url_base + name
cl.web = True
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)
return cl, ''
@ -713,14 +741,14 @@ _change_prolog = """# Change list.
# Get effective change nodes taking into account applied MQ patches
def effective_revpair(repo):
try:
return cmdutil.revpair(repo, ['qparent'])
return scmutil.revpair(repo, ['qparent'])
except:
return cmdutil.revpair(repo, None)
return scmutil.revpair(repo, None)
# Return list of changed files in repository that match pats.
# Warn about patterns that did not match.
def matchpats(ui, repo, pats, opts):
matcher = cmdutil.match(repo, pats, opts)
matcher = scmutil.match(repo, pats, opts)
node1, node2 = effective_revpair(repo)
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)
@ -802,10 +830,6 @@ def getremote(ui, repo, opts):
os.environ['http_proxy'] = proxy
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_msg = '''Your CL description appears not to use the standard form.
@ -827,7 +851,6 @@ Examples:
'''
def promptremove(ui, repo, f):
if promptyesno(ui, "hg remove %s (y/n)?" % (f,)):
@ -844,6 +867,18 @@ def EditCL(ui, repo, cl):
s = cl.EditorText()
while True:
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)
if 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
# in that CL.
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 = []
files = []
pats = pats or []
opts = opts or {}
for p in pats:
if p.startswith('@'):
taken.append(p)
clname = p[1:]
if not GoodCLName(clname):
raise util.Abort("invalid CL name " + clname)
cl, err = LoadCL(repo.ui, repo, clname, web=False)
if err != '':
raise util.Abort("loading CL " + clname + ": " + err)
if not cl.files:
raise util.Abort("no files in CL " + clname)
files = Add(files, cl.files)
if clname == "default":
files = DefaultFiles(global_ui, global_repo, [], opts)
else:
if not GoodCLName(clname):
raise util.Abort("invalid CL name " + clname)
cl, err = LoadCL(global_repo.ui, global_repo, clname, web=False)
if err != '':
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]
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):
n = len(cwd)
@ -1292,7 +1337,7 @@ def clpatch_or_undo(ui, repo, clname, opts, mode):
# sequence numbers get to be 7 digits long.
if re.match('^[0-9]{7,}$', clname):
found = False
matchfn = cmdutil.match(repo, [], {'rev': None})
matchfn = scmutil.match(repo, [], {'rev': None})
def prep(ctx, fns):
pass
for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev': None}, prep):
@ -1606,9 +1651,12 @@ def pending(ui, repo, *pats, **opts):
def reposetup(ui, repo):
global original_match
if original_match is None:
global global_repo, global_ui
global_repo = repo
global_ui = ui
start_status_thread()
original_match = cmdutil.match
cmdutil.match = ReplacementForCmdutilMatch
original_match = scmutil.match
scmutil.match = ReplacementForCmdutilMatch
RietveldSetup(ui, repo)
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.
set_mercurial_encoding_to_utf8()
other = getremote(ui, repo, opts)
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"
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")
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)
node = repo.commit(ustr(opts['message']), ustr(userline), opts.get('date'), m)
if not node:
@ -1732,7 +1787,6 @@ def submit(ui, repo, *pats, **opts):
# push changes to remote.
# if it works, we're committed.
# if not, roll back
other = getremote(ui, repo, opts)
r = repo.push(other, False, None)
if r == 0:
raise util.Abort("local repository out of date; must sync before submit")
@ -1828,7 +1882,7 @@ def sync_changes(ui, repo):
break
Rev(rev)
else:
matchfn = cmdutil.match(repo, [], {'rev': None})
matchfn = scmutil.match(repo, [], {'rev': None})
def prep(ctx, fns):
pass
for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev': None}, prep):
@ -3087,6 +3141,7 @@ class VersionControlSystem(object):
return False
return not mimetype.startswith("text/")
class FakeMercurialUI(object):
def __init__(self):
self.quiet = True
@ -3094,6 +3149,19 @@ class FakeMercurialUI(object):
def write(self, *args, **opts):
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
@ -3104,6 +3172,7 @@ class MercurialVCS(VersionControlSystem):
super(MercurialVCS, self).__init__(options)
self.ui = ui
self.repo = repo
self.status = None
# Absolute path to repository (we can be in a subdir)
self.repo_dir = os.path.normpath(repo.root)
# Compute the subdir
@ -3162,6 +3231,33 @@ class MercurialVCS(VersionControlSystem):
unknown_files.append(fn)
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):
set_status("inspecting " + filename)
# "hg status" and "hg cat" both take a path relative to the current subdir
@ -3171,20 +3267,7 @@ class MercurialVCS(VersionControlSystem):
new_content = None
is_binary = False
oldrelpath = relpath = self._GetRelPath(filename)
# "hg status -C" returns two lines for moved/copied files, one otherwise
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:]
out = self.get_hg_status(self.base_rev, relpath)
status, what = out[0].split(' ', 1)
if len(out) > 1 and status == "A" and what == relpath:
oldrelpath = out[1].strip()