|
|
|
@ -102,22 +102,18 @@ VCS_CVS = "CVS"
|
|
|
|
|
VCS_UNKNOWN = "Unknown"
|
|
|
|
|
|
|
|
|
|
VCS = [
|
|
|
|
|
{
|
|
|
|
|
'name': VCS_MERCURIAL,
|
|
|
|
|
'aliases': ['hg', 'mercurial'],
|
|
|
|
|
}, {
|
|
|
|
|
'name': VCS_SUBVERSION,
|
|
|
|
|
'aliases': ['svn', 'subversion'],
|
|
|
|
|
}, {
|
|
|
|
|
'name': VCS_PERFORCE,
|
|
|
|
|
'aliases': ['p4', 'perforce'],
|
|
|
|
|
}, {
|
|
|
|
|
'name': VCS_GIT,
|
|
|
|
|
'aliases': ['git'],
|
|
|
|
|
}, {
|
|
|
|
|
'name': VCS_CVS,
|
|
|
|
|
'aliases': ['cvs'],
|
|
|
|
|
}]
|
|
|
|
|
{'name': VCS_MERCURIAL,
|
|
|
|
|
'aliases': ['hg', 'mercurial']},
|
|
|
|
|
{'name': VCS_SUBVERSION,
|
|
|
|
|
'aliases': ['svn', 'subversion'],},
|
|
|
|
|
{'name': VCS_PERFORCE,
|
|
|
|
|
'aliases': ['p4', 'perforce']},
|
|
|
|
|
{'name': VCS_GIT,
|
|
|
|
|
'aliases': ['git']},
|
|
|
|
|
{'name': VCS_CVS,
|
|
|
|
|
'aliases': ['cvs']},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VCS_SHORT_NAMES = [] # hg, svn, ...
|
|
|
|
|
VCS_ABBREVIATIONS = {} # alias: name, ...
|
|
|
|
@ -217,7 +213,7 @@ def StatusUpdate(msg):
|
|
|
|
|
|
|
|
|
|
def ErrorExit(msg):
|
|
|
|
|
"""Print an error message to stderr and exit."""
|
|
|
|
|
print >>sys.stderr, msg
|
|
|
|
|
print >> sys.stderr, msg
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -380,41 +376,41 @@ class AbstractRpcServer(object):
|
|
|
|
|
try:
|
|
|
|
|
auth_token = self._GetAuthToken(credentials[0], credentials[1])
|
|
|
|
|
except ClientLoginError, e:
|
|
|
|
|
print >>sys.stderr, ''
|
|
|
|
|
print >> sys.stderr, ''
|
|
|
|
|
if e.reason == "BadAuthentication":
|
|
|
|
|
if e.info == "InvalidSecondFactor":
|
|
|
|
|
print >>sys.stderr, (
|
|
|
|
|
print >> sys.stderr, (
|
|
|
|
|
"Use an application-specific password instead "
|
|
|
|
|
"of your regular account password.\n"
|
|
|
|
|
"See http://www.google.com/"
|
|
|
|
|
"support/accounts/bin/answer.py?answer=185833")
|
|
|
|
|
else:
|
|
|
|
|
print >>sys.stderr, "Invalid username or password."
|
|
|
|
|
print >> sys.stderr, "Invalid username or password."
|
|
|
|
|
elif e.reason == "CaptchaRequired":
|
|
|
|
|
print >>sys.stderr, (
|
|
|
|
|
print >> sys.stderr, (
|
|
|
|
|
"Please go to\n"
|
|
|
|
|
"https://www.google.com/accounts/DisplayUnlockCaptcha\n"
|
|
|
|
|
"and verify you are a human. Then try again.\n"
|
|
|
|
|
"If you are using a Google Apps account the URL is:\n"
|
|
|
|
|
"https://www.google.com/a/yourdomain.com/UnlockCaptcha")
|
|
|
|
|
elif e.reason == "NotVerified":
|
|
|
|
|
print >>sys.stderr, "Account not verified."
|
|
|
|
|
print >> sys.stderr, "Account not verified."
|
|
|
|
|
elif e.reason == "TermsNotAgreed":
|
|
|
|
|
print >>sys.stderr, "User has not agreed to TOS."
|
|
|
|
|
print >> sys.stderr, "User has not agreed to TOS."
|
|
|
|
|
elif e.reason == "AccountDeleted":
|
|
|
|
|
print >>sys.stderr, "The user account has been deleted."
|
|
|
|
|
print >> sys.stderr, "The user account has been deleted."
|
|
|
|
|
elif e.reason == "AccountDisabled":
|
|
|
|
|
print >>sys.stderr, "The user account has been disabled."
|
|
|
|
|
print >> sys.stderr, "The user account has been disabled."
|
|
|
|
|
break
|
|
|
|
|
elif e.reason == "ServiceDisabled":
|
|
|
|
|
print >>sys.stderr, ("The user's access to the service has been "
|
|
|
|
|
print >> sys.stderr, ("The user's access to the service has been "
|
|
|
|
|
"disabled.")
|
|
|
|
|
elif e.reason == "ServiceUnavailable":
|
|
|
|
|
print >>sys.stderr, "The service is not available; try again later."
|
|
|
|
|
print >> sys.stderr, "The service is not available; try again later."
|
|
|
|
|
else:
|
|
|
|
|
# Unknown error.
|
|
|
|
|
raise
|
|
|
|
|
print >>sys.stderr, ''
|
|
|
|
|
print >> sys.stderr, ''
|
|
|
|
|
continue
|
|
|
|
|
self._GetAuthCookie(auth_token)
|
|
|
|
|
return
|
|
|
|
@ -461,7 +457,7 @@ class AbstractRpcServer(object):
|
|
|
|
|
for header, value in extra_headers.items():
|
|
|
|
|
req.add_header(header, value)
|
|
|
|
|
try:
|
|
|
|
|
f = self.opener.open(req)
|
|
|
|
|
f = self.opener.open(req, timeout=70)
|
|
|
|
|
response = f.read()
|
|
|
|
|
f.close()
|
|
|
|
|
return response
|
|
|
|
@ -542,37 +538,37 @@ class HttpRpcServer(AbstractRpcServer):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CondensedHelpFormatter(optparse.IndentedHelpFormatter):
|
|
|
|
|
"""Frees more horizontal space by removing indentation from group
|
|
|
|
|
options and collapsing arguments between short and long, e.g.
|
|
|
|
|
'-o ARG, --opt=ARG' to -o --opt ARG"""
|
|
|
|
|
|
|
|
|
|
def format_heading(self, heading):
|
|
|
|
|
return "%s:\n" % heading
|
|
|
|
|
|
|
|
|
|
def format_option(self, option):
|
|
|
|
|
self.dedent()
|
|
|
|
|
res = optparse.HelpFormatter.format_option(self, option)
|
|
|
|
|
self.indent()
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
def format_option_strings(self, option):
|
|
|
|
|
self.set_long_opt_delimiter(" ")
|
|
|
|
|
optstr = optparse.HelpFormatter.format_option_strings(self, option)
|
|
|
|
|
optlist = optstr.split(", ")
|
|
|
|
|
if len(optlist) > 1:
|
|
|
|
|
if option.takes_value():
|
|
|
|
|
# strip METAVAR from all but the last option
|
|
|
|
|
optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:]
|
|
|
|
|
optstr = " ".join(optlist)
|
|
|
|
|
return optstr
|
|
|
|
|
"""Frees more horizontal space by removing indentation from group
|
|
|
|
|
options and collapsing arguments between short and long, e.g.
|
|
|
|
|
'-o ARG, --opt=ARG' to -o --opt ARG"""
|
|
|
|
|
|
|
|
|
|
def format_heading(self, heading):
|
|
|
|
|
return "%s:\n" % heading
|
|
|
|
|
|
|
|
|
|
def format_option(self, option):
|
|
|
|
|
self.dedent()
|
|
|
|
|
res = optparse.HelpFormatter.format_option(self, option)
|
|
|
|
|
self.indent()
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
def format_option_strings(self, option):
|
|
|
|
|
self.set_long_opt_delimiter(" ")
|
|
|
|
|
optstr = optparse.HelpFormatter.format_option_strings(self, option)
|
|
|
|
|
optlist = optstr.split(", ")
|
|
|
|
|
if len(optlist) > 1:
|
|
|
|
|
if option.takes_value():
|
|
|
|
|
# strip METAVAR from all but the last option
|
|
|
|
|
optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:]
|
|
|
|
|
optstr = " ".join(optlist)
|
|
|
|
|
return optstr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parser = optparse.OptionParser(
|
|
|
|
|
usage=("%prog [options] [-- diff_options] [path...]\n"
|
|
|
|
|
"See also: http://code.google.com/p/rietveld/wiki/UploadPyUsage"),
|
|
|
|
|
add_help_option=False,
|
|
|
|
|
formatter=CondensedHelpFormatter()
|
|
|
|
|
)
|
|
|
|
|
usage=("%prog [options] [-- diff_options] [path...]\n"
|
|
|
|
|
"See also: http://code.google.com/p/rietveld/wiki/UploadPyUsage"),
|
|
|
|
|
add_help_option=False,
|
|
|
|
|
formatter=CondensedHelpFormatter()
|
|
|
|
|
)
|
|
|
|
|
parser.add_option("-h", "--help", action="store_true",
|
|
|
|
|
help="Show this help message and exit.")
|
|
|
|
|
parser.add_option("-y", "--assume_yes", action="store_true",
|
|
|
|
@ -680,8 +676,11 @@ group.add_option("--emulate_svn_auto_props", action="store_true",
|
|
|
|
|
group = parser.add_option_group("Git-specific options")
|
|
|
|
|
group.add_option("--git_similarity", action="store", dest="git_similarity",
|
|
|
|
|
metavar="SIM", type="int", default=50,
|
|
|
|
|
help=("Set the minimum similarity index for detecting renames "
|
|
|
|
|
"and copies. See `git diff -C`. (default 50)."))
|
|
|
|
|
help=("Set the minimum similarity percentage for detecting "
|
|
|
|
|
"renames and copies. See `git diff -C`. (default 50)."))
|
|
|
|
|
group.add_option("--git_only_search_patch", action="store_false", default=True,
|
|
|
|
|
dest='git_find_copies_harder',
|
|
|
|
|
help="Removes --find-copies-harder when seaching for copies")
|
|
|
|
|
group.add_option("--git_no_find_copies", action="store_false", default=True,
|
|
|
|
|
dest="git_find_copies",
|
|
|
|
|
help=("Prevents git from looking for copies (default off)."))
|
|
|
|
@ -1009,7 +1008,7 @@ use_shell = sys.platform.startswith("win")
|
|
|
|
|
def RunShellWithReturnCodeAndStderr(command, print_output=False,
|
|
|
|
|
universal_newlines=True,
|
|
|
|
|
env=os.environ):
|
|
|
|
|
"""Executes a command and returns the output from stdout, stderr and the return code.
|
|
|
|
|
"""Run a command and return output from stdout, stderr and the return code.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
command: Command to execute.
|
|
|
|
@ -1040,7 +1039,7 @@ def RunShellWithReturnCodeAndStderr(command, print_output=False,
|
|
|
|
|
p.wait()
|
|
|
|
|
errout = p.stderr.read()
|
|
|
|
|
if print_output and errout:
|
|
|
|
|
print >>sys.stderr, errout
|
|
|
|
|
print >> sys.stderr, errout
|
|
|
|
|
p.stdout.close()
|
|
|
|
|
p.stderr.close()
|
|
|
|
|
return output, errout, p.returncode
|
|
|
|
@ -1048,7 +1047,7 @@ def RunShellWithReturnCodeAndStderr(command, print_output=False,
|
|
|
|
|
def RunShellWithReturnCode(command, print_output=False,
|
|
|
|
|
universal_newlines=True,
|
|
|
|
|
env=os.environ):
|
|
|
|
|
"""Executes a command and returns the output from stdout and the return code."""
|
|
|
|
|
"""Run a command and return output from stdout and the return code."""
|
|
|
|
|
out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output,
|
|
|
|
|
universal_newlines, env)
|
|
|
|
|
return out, retcode
|
|
|
|
@ -1223,7 +1222,8 @@ class VersionControlSystem(object):
|
|
|
|
|
mimetype = mimetypes.guess_type(filename)[0]
|
|
|
|
|
if not mimetype:
|
|
|
|
|
return False
|
|
|
|
|
return mimetype.startswith("image/") and not mimetype.startswith("image/svg")
|
|
|
|
|
return (mimetype.startswith("image/") and
|
|
|
|
|
not mimetype.startswith("image/svg"))
|
|
|
|
|
|
|
|
|
|
def IsBinaryData(self, data):
|
|
|
|
|
"""Returns true if data contains a null byte."""
|
|
|
|
@ -1269,21 +1269,21 @@ class SubversionVCS(VersionControlSystem):
|
|
|
|
|
"""
|
|
|
|
|
url = self._GetInfo("URL")
|
|
|
|
|
if url:
|
|
|
|
|
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
|
|
|
|
|
guess = ""
|
|
|
|
|
# TODO(anatoli) - repository specific hacks should be handled by server
|
|
|
|
|
if netloc == "svn.python.org" and scheme == "svn+ssh":
|
|
|
|
|
path = "projects" + path
|
|
|
|
|
scheme = "http"
|
|
|
|
|
guess = "Python "
|
|
|
|
|
elif netloc.endswith(".googlecode.com"):
|
|
|
|
|
scheme = "http"
|
|
|
|
|
guess = "Google Code "
|
|
|
|
|
path = path + "/"
|
|
|
|
|
base = urlparse.urlunparse((scheme, netloc, path, params,
|
|
|
|
|
query, fragment))
|
|
|
|
|
LOGGER.info("Guessed %sbase = %s", guess, base)
|
|
|
|
|
return base
|
|
|
|
|
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
|
|
|
|
|
guess = ""
|
|
|
|
|
# TODO(anatoli) - repository specific hacks should be handled by server
|
|
|
|
|
if netloc == "svn.python.org" and scheme == "svn+ssh":
|
|
|
|
|
path = "projects" + path
|
|
|
|
|
scheme = "http"
|
|
|
|
|
guess = "Python "
|
|
|
|
|
elif netloc.endswith(".googlecode.com"):
|
|
|
|
|
scheme = "http"
|
|
|
|
|
guess = "Google Code "
|
|
|
|
|
path = path + "/"
|
|
|
|
|
base = urlparse.urlunparse((scheme, netloc, path, params,
|
|
|
|
|
query, fragment))
|
|
|
|
|
LOGGER.info("Guessed %sbase = %s", guess, base)
|
|
|
|
|
return base
|
|
|
|
|
if required:
|
|
|
|
|
ErrorExit("Can't find URL in output from svn info")
|
|
|
|
|
return None
|
|
|
|
@ -1338,9 +1338,10 @@ class SubversionVCS(VersionControlSystem):
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def repl(m):
|
|
|
|
|
if m.group(2):
|
|
|
|
|
return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
|
|
|
|
|
return "$%s$" % m.group(1)
|
|
|
|
|
if m.group(2):
|
|
|
|
|
return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
|
|
|
|
|
return "$%s$" % m.group(1)
|
|
|
|
|
|
|
|
|
|
keywords = [keyword
|
|
|
|
|
for name in keyword_str.split(" ")
|
|
|
|
|
for keyword in svn_keywords.get(name, [])]
|
|
|
|
@ -1393,7 +1394,8 @@ class SubversionVCS(VersionControlSystem):
|
|
|
|
|
if returncode:
|
|
|
|
|
# Directory might not yet exist at start revison
|
|
|
|
|
# svn: Unable to find repository location for 'abc' in revision nnn
|
|
|
|
|
if re.match('^svn: Unable to find repository location for .+ in revision \d+', err):
|
|
|
|
|
if re.match('^svn: Unable to find repository location '
|
|
|
|
|
'for .+ in revision \d+', err):
|
|
|
|
|
old_files = ()
|
|
|
|
|
else:
|
|
|
|
|
ErrorExit("Failed to get status for %s:\n%s" % (filename, err))
|
|
|
|
@ -1615,16 +1617,18 @@ class GitVCS(VersionControlSystem):
|
|
|
|
|
# append a diff (with rename detection), without deletes.
|
|
|
|
|
cmd = [
|
|
|
|
|
"git", "diff", "--no-color", "--no-ext-diff", "--full-index",
|
|
|
|
|
"--ignore-submodules",
|
|
|
|
|
"--ignore-submodules", "--src-prefix=a/", "--dst-prefix=b/",
|
|
|
|
|
]
|
|
|
|
|
diff = RunShell(
|
|
|
|
|
cmd + ["--no-renames", "--diff-filter=D"] + extra_args,
|
|
|
|
|
env=env, silent_ok=True)
|
|
|
|
|
assert 0 <= self.options.git_similarity <= 100
|
|
|
|
|
if self.options.git_find_copies:
|
|
|
|
|
similarity_options = ["--find-copies-harder", "-l100000",
|
|
|
|
|
"-C%s" % self.options.git_similarity ]
|
|
|
|
|
similarity_options = ["-l100000", "-C%d%%" % self.options.git_similarity]
|
|
|
|
|
if self.options.git_find_copies_harder:
|
|
|
|
|
similarity_options.append("--find-copies-harder")
|
|
|
|
|
else:
|
|
|
|
|
similarity_options = ["-M%s" % self.options.git_similarity ]
|
|
|
|
|
similarity_options = ["-M%d%%" % self.options.git_similarity ]
|
|
|
|
|
diff += RunShell(
|
|
|
|
|
cmd + ["--diff-filter=AMCRT"] + similarity_options + extra_args,
|
|
|
|
|
env=env, silent_ok=True)
|
|
|
|
@ -2085,7 +2089,7 @@ class PerforceVCS(VersionControlSystem):
|
|
|
|
|
line_count = len(diffData.file_body.splitlines())
|
|
|
|
|
diffData.change_summary = "@@ -0,0 +1"
|
|
|
|
|
if line_count > 1:
|
|
|
|
|
diffData.change_summary += ",%d" % line_count
|
|
|
|
|
diffData.change_summary += ",%d" % line_count
|
|
|
|
|
diffData.change_summary += " @@"
|
|
|
|
|
diffData.prefix = "+"
|
|
|
|
|
return diffData
|
|
|
|
|