From 1fa87ada553ec8b9d4e520fe715faa3f9d391a5c Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 26 Sep 2011 18:32:16 -0400 Subject: [PATCH] codereview: extra repo sanity check Also work around Mercurial issue 3023. If anyone has local changes in their repo (due to patch queues or whatever) stop them from leaking into the main repository. R=golang-dev, r CC=golang-dev https://golang.org/cl/5144043 --- lib/codereview/codereview.py | 54 +++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py index 761476371b9..22b08ad0d60 100644 --- a/lib/codereview/codereview.py +++ b/lib/codereview/codereview.py @@ -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 @@ -71,9 +71,12 @@ except: try: from mercurial.discovery import findcommonincoming + from mercurial.discovery import findoutgoing except: def findcommonincoming(repo, remote): return repo.findcommonincoming(remote) + def findoutgoing(repo, remote): + return repo.findoutgoing(remote) # in Mercurial 1.9 the cmdutil.match and cmdutil.revpair moved to scmutil if hgversion >= '1.9': @@ -1738,6 +1741,11 @@ def submit(ui, repo, *pats, **opts): return "dry run; not submitted" set_status("pushing " + cl.name + " to remote server") + + other = getremote(ui, repo, opts) + if findoutgoing(repo, other): + 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: @@ -1758,7 +1766,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") @@ -3130,6 +3137,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 @@ -3188,6 +3196,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] + 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 @@ -3197,20 +3232,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()