From db800afb4e7b46df67feba70cda683f34110619b Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Mon, 24 Feb 2014 01:21:48 -0500 Subject: codereview: sync from Go. LGTM=rsc R=rsc https://codereview.appspot.com/67820044 --- lib/codereview/codereview.py | 297 +++++++++++++++++++++++++++---------------- 1 file changed, 185 insertions(+), 112 deletions(-) (limited to 'lib') diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py index d26df2a5..12b000c1 100644 --- a/lib/codereview/codereview.py +++ b/lib/codereview/codereview.py @@ -61,6 +61,14 @@ import time from mercurial import commands as hg_commands from mercurial import util as hg_util +# bind Plan 9 preferred dotfile location +if os.sys.platform == 'plan9': + try: + import plan9 + n = plan9.bind(os.path.expanduser("~/lib"), os.path.expanduser("~"), plan9.MBEFORE|plan9.MCREATE) + except ImportError: + pass + defaultcc = None codereview_disabled = None real_rollback = None @@ -155,7 +163,8 @@ default_to_utf8() global_status = None def set_status(s): - # print >>sys.stderr, "\t", time.asctime(), s + if verbosity > 0: + print >>sys.stderr, time.asctime(), s global global_status global_status = s @@ -268,7 +277,7 @@ class CL(object): s += "\tAuthor: " + cl.copied_from + "\n" if not quick: s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n" - for (who, line) in cl.lgtm: + for (who, line, _) in cl.lgtm: s += "\t\t" + who + ": " + line + "\n" s += "\tCC: " + JoinComma(cl.cc) + "\n" s += "\tFiles:\n" @@ -358,6 +367,8 @@ class CL(object): msg = lines[0] patchset = lines[1].strip() patches = [x.split(" ", 1) for x in lines[2:]] + else: + print >>sys.stderr, "Server says there is nothing to upload (probably wrong):\n" + msg if response_body.startswith("Issue updated.") and quiet: pass else: @@ -484,9 +495,15 @@ def CutDomain(s): return s def JoinComma(l): + seen = {} + uniq = [] for s in l: typecheck(s, str) - return ", ".join(l) + if s not in seen: + seen[s] = True + uniq.append(s) + + return ", ".join(uniq) def ExceptionDetail(): s = str(sys.exc_info()[0]) @@ -544,10 +561,10 @@ def LoadCL(ui, repo, name, web=True): cl.private = d.get('private', False) != False cl.lgtm = [] for m in d.get('messages', []): - if m.get('approval', False) == True: + if m.get('approval', False) == True or m.get('disapproval', False) == True: who = re.sub('@.*', '', m.get('sender', '')) text = re.sub("\n(.|\n)*", '', m.get('text', '')) - cl.lgtm.append((who, text)) + cl.lgtm.append((who, text, m.get('approval', False))) set_status("loaded CL " + name) return cl, '' @@ -711,7 +728,10 @@ Examples: ''' def promptyesno(ui, msg): - return ui.promptchoice(msg, ["&yes", "&no"], 0) == 0 + if hgversion >= "2.7": + return ui.promptchoice(msg + " $$ &yes $$ &no", 0) == 0 + else: + return ui.promptchoice(msg, ["&yes", "&no"], 0) == 0 def promptremove(ui, repo, f): if promptyesno(ui, "hg remove %s (y/n)?" % (f,)): @@ -807,7 +827,7 @@ def EditCL(ui, repo, cl): # For use by submit, etc. (NOT by change) # Get change list number or list of files from command line. # If files are given, make a new change list. -def CommandLineCL(ui, repo, pats, opts, defaultcc=None): +def CommandLineCL(ui, repo, pats, opts, op="verb", defaultcc=None): if len(pats) > 0 and GoodCLName(pats[0]): if len(pats) != 1: return None, "cannot specify change number and file names" @@ -821,7 +841,7 @@ def CommandLineCL(ui, repo, pats, opts, defaultcc=None): cl.local = True cl.files = ChangedFiles(ui, repo, pats, taken=Taken(ui, repo)) if not cl.files: - return None, "no files changed" + return None, "no files changed (use hg %s to use existing CL)" % op if opts.get('reviewer'): cl.reviewer = Add(cl.reviewer, SplitCommaSpace(opts.get('reviewer'))) if opts.get('cc'): @@ -972,7 +992,7 @@ def ReadContributors(ui, repo): f = open(repo.root + '/CONTRIBUTORS', 'r') except: ui.write("warning: cannot open %s: %s\n" % (opening, ExceptionDetail())) - return + return {} contributors = {} for line in f: @@ -1027,23 +1047,19 @@ def FindContributor(ui, repo, user=None, warn=True): hgversion = hg_util.version() -# We require Mercurial 1.9 and suggest Mercurial 2.0. +# We require Mercurial 1.9 and suggest Mercurial 2.1. # The details of the scmutil package changed then, # so allowing earlier versions would require extra band-aids below. # Ubuntu 11.10 ships with Mercurial 1.9.1 as the default version. hg_required = "1.9" -hg_suggested = "2.0" +hg_suggested = "2.1" old_message = """ The code review extension requires Mercurial """+hg_required+""" or newer. You are using Mercurial """+hgversion+""". -To install a new Mercurial, use - - sudo easy_install mercurial=="""+hg_suggested+""" - -or visit http://mercurial.selenic.com/downloads/. +To install a new Mercurial, visit http://mercurial.selenic.com/downloads/. """ linux_message = """ @@ -1171,6 +1187,25 @@ def hg_pull(ui, repo, **opts): ui.write(line + '\n') return err +def hg_update(ui, repo, **opts): + w = uiwrap(ui) + ui.quiet = False + ui.verbose = True # for file list + err = hg_commands.update(ui, repo, **opts) + for line in w.output().split('\n'): + if isNoise(line): + continue + if line.startswith('moving '): + line = 'mv ' + line[len('moving '):] + if line.startswith('getting ') and line.find(' to ') >= 0: + line = 'mv ' + line[len('getting '):] + if line.startswith('getting '): + line = '+ ' + line[len('getting '):] + if line.startswith('removing '): + line = '- ' + line[len('removing '):] + ui.write(line + '\n') + return err + def hg_push(ui, repo, **opts): w = uiwrap(ui) ui.quiet = False @@ -1190,6 +1225,10 @@ def hg_commit(ui, repo, *pats, **opts): commit_okay = False def precommithook(ui, repo, **opts): + if hgversion >= "2.1": + from mercurial import phases + if repo.ui.config('phases', 'new-commit') >= phases.secret: + return False if commit_okay: return False # False means okay. ui.write("\ncodereview extension enabled; use mail, upload, or submit instead of commit\n\n") @@ -1247,24 +1286,8 @@ def MatchAt(ctx, pats=None, opts=None, globbed=False, default='relpath'): ####################################################################### # Commands added by code review extension. -# As of Mercurial 2.1 the commands are all required to return integer -# exit codes, whereas earlier versions allowed returning arbitrary strings -# to be printed as errors. We wrap the old functions to make sure we -# always return integer exit codes now. Otherwise Mercurial dies -# with a TypeError traceback (unsupported operand type(s) for &: 'str' and 'int'). -# Introduce a Python decorator to convert old functions to the new -# stricter convention. - def hgcommand(f): - def wrapped(ui, repo, *pats, **opts): - err = f(ui, repo, *pats, **opts) - if type(err) is int: - return err - if not err: - return 0 - raise hg_util.Abort(err) - wrapped.__doc__ = f.__doc__ - return wrapped + return f ####################################################################### # hg change @@ -1293,42 +1316,42 @@ def change(ui, repo, *pats, **opts): """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) dirty = {} if len(pats) > 0 and GoodCLName(pats[0]): name = pats[0] if len(pats) != 1: - return "cannot specify CL name and file patterns" + raise hg_util.Abort("cannot specify CL name and file patterns") pats = pats[1:] cl, err = LoadCL(ui, repo, name, web=True) if err != '': - return err + raise hg_util.Abort(err) if not cl.local and (opts["stdin"] or not opts["stdout"]): - return "cannot change non-local CL " + name + raise hg_util.Abort("cannot change non-local CL " + name) else: name = "new" cl = CL("new") if repo[None].branch() != "default": - return "cannot create CL outside default branch; switch with 'hg update default'" + raise hg_util.Abort("cannot create CL outside default branch; switch with 'hg update default'") dirty[cl] = True files = ChangedFiles(ui, repo, pats, taken=Taken(ui, repo)) if opts["delete"] or opts["deletelocal"]: if opts["delete"] and opts["deletelocal"]: - return "cannot use -d and -D together" + raise hg_util.Abort("cannot use -d and -D together") flag = "-d" if opts["deletelocal"]: flag = "-D" if name == "new": - return "cannot use "+flag+" with file patterns" + raise hg_util.Abort("cannot use "+flag+" with file patterns") if opts["stdin"] or opts["stdout"]: - return "cannot use "+flag+" with -i or -o" + raise hg_util.Abort("cannot use "+flag+" with -i or -o") if not cl.local: - return "cannot change non-local CL " + name + raise hg_util.Abort("cannot change non-local CL " + name) if opts["delete"]: if cl.copied_from: - return "original author must delete CL; hg change -D will remove locally" + raise hg_util.Abort("original author must delete CL; hg change -D will remove locally") PostMessage(ui, cl.name, "*** Abandoned ***", send_mail=cl.mailed) EditDesc(cl.name, closed=True, private=cl.private) cl.Delete(ui, repo) @@ -1338,7 +1361,7 @@ def change(ui, repo, *pats, **opts): s = sys.stdin.read() clx, line, err = ParseCL(s, name) if err != '': - return "error parsing change list: line %d: %s" % (line, err) + raise hg_util.Abort("error parsing change list: line %d: %s" % (line, err)) if clx.desc is not None: cl.desc = clx.desc; dirty[cl] = True @@ -1360,7 +1383,7 @@ def change(ui, repo, *pats, **opts): cl.files = files err = EditCL(ui, repo, cl) if err != "": - return err + raise hg_util.Abort(err) dirty[cl] = True for d, _ in dirty.items(): @@ -1391,7 +1414,7 @@ def code_login(ui, repo, **opts): a file in your home directory. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) MySend(None) @@ -1411,8 +1434,10 @@ def clpatch(ui, repo, clname, **opts): name as the Author: line but add your own name to a Committer: line. """ if repo[None].branch() != "default": - return "cannot run hg clpatch outside default branch" - return clpatch_or_undo(ui, repo, clname, opts, mode="clpatch") + raise hg_util.Abort("cannot run hg clpatch outside default branch") + err = clpatch_or_undo(ui, repo, clname, opts, mode="clpatch") + if err: + raise hg_util.Abort(err) @hgcommand def undo(ui, repo, clname, **opts): @@ -1423,8 +1448,10 @@ def undo(ui, repo, clname, **opts): you can add the reason for the undo to the description. """ if repo[None].branch() != "default": - return "cannot run hg undo outside default branch" - return clpatch_or_undo(ui, repo, clname, opts, mode="undo") + raise hg_util.Abort("cannot run hg undo outside default branch") + err = clpatch_or_undo(ui, repo, clname, opts, mode="undo") + if err: + raise hg_util.Abort(err) @hgcommand def release_apply(ui, repo, clname, **opts): @@ -1468,13 +1495,13 @@ def release_apply(ui, repo, clname, **opts): """ c = repo[None] if not releaseBranch: - return "no active release branches" + raise hg_util.Abort("no active release branches") if c.branch() != releaseBranch: if c.modified() or c.added() or c.removed(): raise hg_util.Abort("uncommitted local changes - cannot switch branches") err = hg_clean(repo, releaseBranch) if err: - return err + raise hg_util.Abort(err) try: err = clpatch_or_undo(ui, repo, clname, opts, mode="backport") if err: @@ -1482,13 +1509,12 @@ def release_apply(ui, repo, clname, **opts): except Exception, e: hg_clean(repo, "default") raise e - return None def rev2clname(rev): # Extract CL name from revision description. # The last line in the description that is a codereview URL is the real one. # Earlier lines might be part of the user-written description. - all = re.findall('(?m)^http://codereview.appspot.com/([0-9]+)$', rev.description()) + all = re.findall('(?m)^https?://codereview.appspot.com/([0-9]+)$', rev.description()) if len(all) > 0: return all[-1] return "" @@ -1586,24 +1612,24 @@ def clpatch_or_undo(ui, repo, clname, opts, mode): return "local repository is out of date; sync to get %s" % (vers) patch1, err = portPatch(repo, patch, vers, id) if err != "": - if not opts["ignore_hgpatch_failure"]: + if not opts["ignore_hgapplydiff_failure"]: return "codereview issue %s is out of date: %s (%s->%s)" % (clname, err, vers, id) else: patch = patch1 - argv = ["hgpatch"] + argv = ["hgapplydiff"] if opts["no_incoming"] or mode == "backport": argv += ["--checksync=false"] try: cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32") except: - return "hgpatch: " + ExceptionDetail() + "\nInstall hgpatch with:\n$ go get code.google.com/p/go.codereview/cmd/hgpatch\n" + return "hgapplydiff: " + ExceptionDetail() + "\nInstall hgapplydiff with:\n$ go get code.google.com/p/go.codereview/cmd/hgapplydiff\n" out, err = cmd.communicate(patch) - if cmd.returncode != 0 and not opts["ignore_hgpatch_failure"]: - return "hgpatch failed" + if cmd.returncode != 0 and not opts["ignore_hgapplydiff_failure"]: + return "hgapplydiff failed" cl.local = True cl.files = out.strip().split() - if not cl.files and not opts["ignore_hgpatch_failure"]: + if not cl.files and not opts["ignore_hgapplydiff_failure"]: return "codereview issue %s has no changed files" % clname files = ChangedFiles(ui, repo, []) extra = Sub(cl.files, files) @@ -1618,6 +1644,17 @@ def clpatch_or_undo(ui, repo, clname, opts, mode): else: ui.write(cl.PendingText() + "\n") + # warn if clpatch will modify file already in another CL (it's unsafe to submit them) + if mode == "clpatch": + msgs = [] + cls = LoadAllCL(ui, repo, web=False) + for k, v in cls.iteritems(): + isec = Intersect(v.files, cl.files) + if isec and k != clname: + msgs.append("CL " + k + ", because it also modifies " + ", ".join(isec) + ".") + if msgs: + ui.warn("warning: please double check before submitting this CL and:\n\t" + "\n\t".join(msgs) + "\n") + # portPatch rewrites patch from being a patch against # oldver to being a patch against newver. def portPatch(repo, patch, oldver, newver): @@ -1687,7 +1724,7 @@ def download(ui, repo, clname, **opts): followed by its diff, downloaded from the code review server. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) cl, vers, patch, err = DownloadCL(ui, repo, clname) if err != "": @@ -1709,7 +1746,7 @@ def file(ui, repo, clname, pat, *pats, **opts): It does not edit them or remove them from the repository. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) pats = tuple([pat] + list(pats)) if not GoodCLName(clname): @@ -1773,19 +1810,20 @@ def gofmt(ui, repo, *pats, **opts): the given patterns. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) files = ChangedExistingFiles(ui, repo, pats, opts) files = gofmt_required(files) if not files: - return "no modified go files" + ui.status("no modified go files\n") + return cwd = os.getcwd() files = [RelativePath(repo.root + '/' + f, cwd) for f in files] try: cmd = ["gofmt", "-l"] if not opts["list"]: cmd += ["-w"] - if os.spawnvp(os.P_WAIT, "gofmt", cmd + files) != 0: + if subprocess.call(cmd + files) != 0: raise hg_util.Abort("gofmt did not exit cleanly") except hg_error.Abort, e: raise @@ -1807,11 +1845,11 @@ def mail(ui, repo, *pats, **opts): to the reviewer and CC list asking for a review. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) - cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc) + cl, err = CommandLineCL(ui, repo, pats, opts, op="mail", defaultcc=defaultcc) if err != "": - return err + raise hg_util.Abort(err) cl.Upload(ui, repo, gofmt_just_warn=True) if not cl.reviewer: # If no reviewer is listed, assign the review to defaultcc. @@ -1819,15 +1857,15 @@ def mail(ui, repo, *pats, **opts): # 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" + raise hg_util.Abort("no reviewers listed in CL") cl.cc = Sub(cl.cc, defaultcc) cl.reviewer = defaultcc cl.Flush(ui, repo) if cl.files == []: - return "no changed files, not sending mail" + raise hg_util.Abort("no changed files, not sending mail") - cl.Mail(ui, repo) + cl.Mail(ui, repo) ####################################################################### # hg p / hg pq / hg ps / hg pending @@ -1853,7 +1891,7 @@ def pending(ui, repo, *pats, **opts): Lists pending changes followed by a list of unassigned but modified files. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) quick = opts.get('quick', False) short = opts.get('short', False) @@ -1868,7 +1906,7 @@ def pending(ui, repo, *pats, **opts): ui.write(cl.PendingText(quick=quick) + "\n") if short: - return + return 0 files = DefaultFiles(ui, repo, []) if len(files) > 0: s = "Changed files not in any CL:\n" @@ -1890,7 +1928,7 @@ def submit(ui, repo, *pats, **opts): Bails out if the local repository is not in sync with the remote one. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) # We already called this on startup but sometimes Mercurial forgets. set_mercurial_encoding_to_utf8() @@ -1898,9 +1936,9 @@ def submit(ui, repo, *pats, **opts): if not opts["no_incoming"] and hg_incoming(ui, repo): need_sync() - cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc) + cl, err = CommandLineCL(ui, repo, pats, opts, op="submit", defaultcc=defaultcc) if err != "": - return err + raise hg_util.Abort(err) user = None if cl.copied_from: @@ -1909,20 +1947,29 @@ def submit(ui, repo, *pats, **opts): typecheck(userline, str) about = "" - if cl.reviewer: - about += "R=" + JoinComma([CutDomain(s) for s in cl.reviewer]) + "\n" + + if not cl.lgtm and not opts.get('tbr') and not isAddca(cl): + raise hg_util.Abort("this CL has not been LGTM'ed") + if cl.lgtm: + about += "LGTM=" + JoinComma([CutDomain(who) for (who, line, approval) in cl.lgtm if approval]) + "\n" + reviewer = cl.reviewer if opts.get('tbr'): tbr = SplitCommaSpace(opts.get('tbr')) + for name in tbr: + if name.startswith('golang-'): + raise hg_util.Abort("--tbr requires a person, not a mailing list") cl.reviewer = Add(cl.reviewer, tbr) about += "TBR=" + JoinComma([CutDomain(s) for s in tbr]) + "\n" + if reviewer: + about += "R=" + JoinComma([CutDomain(s) for s in reviewer]) + "\n" if cl.cc: about += "CC=" + JoinComma([CutDomain(s) for s in cl.cc]) + "\n" if not cl.reviewer: - return "no reviewers listed in CL" + raise hg_util.Abort("no reviewers listed in CL") if not cl.local: - return "cannot submit non-local CL" + raise hg_util.Abort("cannot submit non-local CL") # upload, to sync current patch and also get change number if CL is new. if not cl.copied_from: @@ -1957,7 +2004,7 @@ def submit(ui, repo, *pats, **opts): ret = hg_commit(ui, repo, *['path:'+f for f in cl.files], message=message, user=userline) commit_okay = False if ret: - return "nothing changed" + raise hg_util.Abort("nothing changed") node = repo["-1"].node() # push to remote; if it fails for any reason, roll back try: @@ -1968,12 +2015,16 @@ def submit(ui, repo, *pats, **opts): # Push changes to remote. If it works, we're committed. If not, roll back. try: - hg_push(ui, repo) + if hg_push(ui, repo): + raise hg_util.Abort("push error") except hg_error.Abort, e: if e.message.find("push creates new heads") >= 0: # Remote repository had changes we missed. need_sync() raise + except urllib2.HTTPError, e: + print >>sys.stderr, "pushing to remote server failed; do you have commit permissions?" + raise except: real_rollback() raise @@ -1985,11 +2036,11 @@ def submit(ui, repo, *pats, **opts): "(^https?://([^@/]+@)?code\.google\.com/p/([^/.]+)(\.[^./]+)?/?)", url) if m: if m.group(1): # prj.googlecode.com/hg/ case - changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(3), changeURL) + changeURL = "https://code.google.com/p/%s/source/detail?r=%s" % (m.group(3), changeURL) elif m.group(4) and m.group(7): # code.google.com/p/prj.subrepo/ case - changeURL = "http://code.google.com/p/%s/source/detail?r=%s&repo=%s" % (m.group(6), changeURL, m.group(7)[1:]) + changeURL = "https://code.google.com/p/%s/source/detail?r=%s&repo=%s" % (m.group(6), changeURL, m.group(7)[1:]) elif m.group(4): # code.google.com/p/prj/ case - changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(6), changeURL) + changeURL = "https://code.google.com/p/%s/source/detail?r=%s" % (m.group(6), changeURL) else: print >>sys.stderr, "URL: ", url else: @@ -2010,7 +2061,12 @@ def submit(ui, repo, *pats, **opts): err = hg_clean(repo, "default") if err: return err - return None + return 0 + +def isAddca(cl): + rev = cl.reviewer + isGobot = 'gobot' in rev or 'gobot@swtch.com' in rev or 'gobot@golang.org' in rev + return cl.desc.startswith('A+C:') and 'Generated by addca.' in cl.desc and isGobot ####################################################################### # hg sync @@ -2023,10 +2079,22 @@ def sync(ui, repo, **opts): into the local repository. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) if not opts["local"]: - err = hg_pull(ui, repo, update=True) + # If there are incoming CLs, pull -u will do the update. + # If there are no incoming CLs, do hg update to make sure + # that an update always happens regardless. This is less + # surprising than update depending on incoming CLs. + # It is important not to do both hg pull -u and hg update + # in the same command, because the hg update will end + # up marking resolve conflicts from the hg pull -u as resolved, + # causing files with <<< >>> markers to not show up in + # hg resolve -l. Yay Mercurial. + if hg_incoming(ui, repo): + err = hg_pull(ui, repo, update=True) + else: + err = hg_update(ui, repo) if err: return err sync_changes(ui, repo) @@ -2037,7 +2105,7 @@ def sync_changes(ui, repo): # Double-check them by looking at the Rietveld log. for rev in hg_log(ui, repo, limit=100, template="{node}\n").split(): desc = repo[rev].description().strip() - for clname in re.findall('(?m)^http://(?:[^\n]+)/([0-9]+)$', desc): + for clname in re.findall('(?m)^https?://(?:[^\n]+)/([0-9]+)$', desc): if IsLocalCL(ui, repo, clname) and IsRietveldSubmitted(ui, clname, repo[rev].hex()): ui.warn("CL %s submitted as %s; closing\n" % (clname, repo[rev])) cl, err = LoadCL(ui, repo, clname, web=False) @@ -2064,7 +2132,7 @@ def sync_changes(ui, repo): ui.warn("CL %s has no files; delete (abandon) with hg change -d %s\n" % (cl.name, cl.name)) else: ui.warn("CL %s has no files; delete locally with hg change -D %s\n" % (cl.name, cl.name)) - return + return 0 ####################################################################### # hg upload @@ -2076,17 +2144,17 @@ def upload(ui, repo, name, **opts): Uploads the current modifications for a given change to the server. """ if codereview_disabled: - return codereview_disabled + raise hg_util.Abort(codereview_disabled) repo.ui.quiet = True cl, err = LoadCL(ui, repo, name, web=True) if err != "": - return err + raise hg_util.Abort(err) if not cl.local: - return "cannot upload non-local change" + raise hg_util.Abort("cannot upload non-local change") cl.Upload(ui, repo) print "%s%s\n" % (server_url_base, cl.name) - return + return 0 ####################################################################### # Table of commands, supplied to Mercurial for installation. @@ -2115,7 +2183,7 @@ cmdtable = { "^clpatch": ( clpatch, [ - ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), + ('', 'ignore_hgapplydiff_failure', None, 'create CL metadata even if hgapplydiff fails'), ('', 'no_incoming', None, 'disable check for incoming changes'), ], "change#" @@ -2174,7 +2242,7 @@ cmdtable = { "^release-apply": ( release_apply, [ - ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), + ('', 'ignore_hgapplydiff_failure', None, 'create CL metadata even if hgapplydiff fails'), ('', 'no_incoming', None, 'disable check for incoming changes'), ], "change#" @@ -2197,7 +2265,7 @@ cmdtable = { "^undo": ( undo, [ - ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'), + ('', 'ignore_hgapplydiff_failure', None, 'create CL metadata even if hgapplydiff fails'), ('', 'no_incoming', None, 'disable check for incoming changes'), ], "change#" @@ -2229,6 +2297,7 @@ def reposetup(ui, repo): if codereview_init: return codereview_init = True + start_status_thread() # Read repository-specific options from lib/codereview/codereview.cfg or codereview.cfg. root = '' @@ -2366,7 +2435,7 @@ def IsRietveldSubmitted(ui, clname, hex): return False for msg in dict.get("messages", []): text = msg.get("text", "") - m = re.match('\*\*\* Submitted as [^*]*?([0-9a-f]+) \*\*\*', text) + m = re.match('\*\*\* Submitted as [^*]*?r=([0-9a-f]+)[^ ]* \*\*\*', text) if m is not None and len(m.group(1)) >= 8 and hex.startswith(m.group(1)): return True return False @@ -2460,6 +2529,8 @@ def MySend1(request_path, payload=None, self._Authenticate() if request_path is None: return + if timeout is None: + timeout = 30 # seconds old_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(timeout) @@ -2468,7 +2539,7 @@ def MySend1(request_path, payload=None, while True: tries += 1 args = dict(kwargs) - url = "http://%s%s" % (self.host, request_path) + url = "https://%s%s" % (self.host, request_path) if args: url += "?" + urllib.urlencode(args) req = self._CreateRequest(url=url, data=payload) @@ -2580,7 +2651,7 @@ def RietveldSetup(ui, repo): if x is not None: email = x - server_url_base = "http://" + server + "/" + server_url_base = "https://" + server + "/" testing = ui.config("codereview", "testing") force_google_account = ui.configbool("codereview", "force_google_account", False) @@ -2609,7 +2680,7 @@ def RietveldSetup(ui, repo): rpc = None global releaseBranch - tags = repo.branchtags().keys() + tags = repo.branchmap().keys() if 'release-branch.go10' in tags: # NOTE(rsc): This tags.sort is going to get the wrong # answer when comparing release-branch.go9 with @@ -2755,7 +2826,9 @@ class ClientLoginError(urllib2.HTTPError): def __init__(self, url, code, msg, headers, args): urllib2.HTTPError.__init__(self, url, code, msg, headers, None) self.args = args - self.reason = args["Error"] + # .reason is now a read-only property based on .msg + # this means we ignore 'msg', but that seems to work fine. + self.msg = args["Error"] class AbstractRpcServer(object): @@ -2858,7 +2931,7 @@ class AbstractRpcServer(object): # This is a dummy value to allow us to identify when we're successful. continue_location = "http://localhost/" args = {"continue": continue_location, "auth": auth_token} - req = self._CreateRequest("http://%s/_ah/login?%s" % (self.host, urllib.urlencode(args))) + req = self._CreateRequest("https://%s/_ah/login?%s" % (self.host, urllib.urlencode(args))) try: response = self.opener.open(req) except urllib2.HTTPError, e: @@ -2888,31 +2961,31 @@ class AbstractRpcServer(object): try: auth_token = self._GetAuthToken(credentials[0], credentials[1]) except ClientLoginError, e: - if e.reason == "BadAuthentication": + if e.msg == "BadAuthentication": print >>sys.stderr, "Invalid username or password." continue - if e.reason == "CaptchaRequired": + if e.msg == "CaptchaRequired": print >>sys.stderr, ( "Please go to\n" "https://www.google.com/accounts/DisplayUnlockCaptcha\n" "and verify you are a human. Then try again.") break - if e.reason == "NotVerified": + if e.msg == "NotVerified": print >>sys.stderr, "Account not verified." break - if e.reason == "TermsNotAgreed": + if e.msg == "TermsNotAgreed": print >>sys.stderr, "User has not agreed to TOS." break - if e.reason == "AccountDeleted": + if e.msg == "AccountDeleted": print >>sys.stderr, "The user account has been deleted." break - if e.reason == "AccountDisabled": + if e.msg == "AccountDisabled": print >>sys.stderr, "The user account has been disabled." break - if e.reason == "ServiceDisabled": + if e.msg == "ServiceDisabled": print >>sys.stderr, "The user's access to the service has been disabled." break - if e.reason == "ServiceUnavailable": + if e.msg == "ServiceUnavailable": print >>sys.stderr, "The service is not available; try again later." break raise @@ -2948,7 +3021,7 @@ class AbstractRpcServer(object): while True: tries += 1 args = dict(kwargs) - url = "http://%s%s" % (self.host, request_path) + url = "https://%s%s" % (self.host, request_path) if args: url += "?" + urllib.urlencode(args) req = self._CreateRequest(url=url, data=payload) -- cgit v1.2.3