git-p4: add blank lines between functions and class definitions

In the PEP8 style guidelines, top-level functions and class definitions
should be separated by two blank lines. Methods should be surrounded by
a single blank line.

This guideline is described here in the "Blank Lines" section:
https://www.python.org/dev/peps/pep-0008/#blank-lines

Signed-off-by: Joel Holdsworth <jholdsworth@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Joel Holdsworth 2022-04-01 15:24:43 +01:00 коммит произвёл Junio C Hamano
Родитель 2b9c120970
Коммит adf159b441
1 изменённых файлов: 107 добавлений и 0 удалений

107
git-p4.py
Просмотреть файл

@ -59,6 +59,7 @@ p4_access_checked = False
re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$') re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$')
re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$') re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$')
def format_size_human_readable(num): def format_size_human_readable(num):
""" Returns a number of units (typically bytes) formatted as a human-readable """ Returns a number of units (typically bytes) formatted as a human-readable
string. string.
@ -71,6 +72,7 @@ def format_size_human_readable(num):
return "{:3.1f} {}B".format(num, unit) return "{:3.1f} {}B".format(num, unit)
return "{:.1f} YiB".format(num) return "{:.1f} YiB".format(num)
def p4_build_cmd(cmd): def p4_build_cmd(cmd):
"""Build a suitable p4 command line. """Build a suitable p4 command line.
@ -118,6 +120,7 @@ def p4_build_cmd(cmd):
return real_cmd return real_cmd
def git_dir(path): def git_dir(path):
""" Return TRUE if the given path is a git directory (/path/to/dir/.git). """ Return TRUE if the given path is a git directory (/path/to/dir/.git).
This won't automatically add ".git" to a directory. This won't automatically add ".git" to a directory.
@ -128,6 +131,7 @@ def git_dir(path):
else: else:
return d return d
def chdir(path, is_client_path=False): def chdir(path, is_client_path=False):
"""Do chdir to the given path, and set the PWD environment """Do chdir to the given path, and set the PWD environment
variable for use by P4. It does not look at getcwd() output. variable for use by P4. It does not look at getcwd() output.
@ -150,6 +154,7 @@ def chdir(path, is_client_path=False):
path = os.getcwd() path = os.getcwd()
os.environ['PWD'] = path os.environ['PWD'] = path
def calcDiskFree(): def calcDiskFree():
"""Return free space in bytes on the disk of the given dirname.""" """Return free space in bytes on the disk of the given dirname."""
if platform.system() == 'Windows': if platform.system() == 'Windows':
@ -160,6 +165,7 @@ def calcDiskFree():
st = os.statvfs(os.getcwd()) st = os.statvfs(os.getcwd())
return st.f_bavail * st.f_frsize return st.f_bavail * st.f_frsize
def die(msg): def die(msg):
""" Terminate execution. Make sure that any running child processes have been wait()ed for before """ Terminate execution. Make sure that any running child processes have been wait()ed for before
calling this. calling this.
@ -170,6 +176,7 @@ def die(msg):
sys.stderr.write(msg + "\n") sys.stderr.write(msg + "\n")
sys.exit(1) sys.exit(1)
def prompt(prompt_text): def prompt(prompt_text):
""" Prompt the user to choose one of the choices """ Prompt the user to choose one of the choices
@ -188,21 +195,25 @@ def prompt(prompt_text):
if response in choices: if response in choices:
return response return response
# We need different encoding/decoding strategies for text data being passed # We need different encoding/decoding strategies for text data being passed
# around in pipes depending on python version # around in pipes depending on python version
if bytes is not str: if bytes is not str:
# For python3, always encode and decode as appropriate # For python3, always encode and decode as appropriate
def decode_text_stream(s): def decode_text_stream(s):
return s.decode() if isinstance(s, bytes) else s return s.decode() if isinstance(s, bytes) else s
def encode_text_stream(s): def encode_text_stream(s):
return s.encode() if isinstance(s, str) else s return s.encode() if isinstance(s, str) else s
else: else:
# For python2.7, pass read strings as-is, but also allow writing unicode # For python2.7, pass read strings as-is, but also allow writing unicode
def decode_text_stream(s): def decode_text_stream(s):
return s return s
def encode_text_stream(s): def encode_text_stream(s):
return s.encode('utf_8') if isinstance(s, unicode) else s return s.encode('utf_8') if isinstance(s, unicode) else s
def decode_path(path): def decode_path(path):
"""Decode a given string (bytes or otherwise) using configured path encoding options """Decode a given string (bytes or otherwise) using configured path encoding options
""" """
@ -218,6 +229,7 @@ def decode_path(path):
print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path)) print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path))
return path return path
def run_git_hook(cmd, param=[]): def run_git_hook(cmd, param=[]):
"""Execute a hook if the hook exists.""" """Execute a hook if the hook exists."""
args = ['git', 'hook', 'run', '--ignore-missing', cmd] args = ['git', 'hook', 'run', '--ignore-missing', cmd]
@ -227,6 +239,7 @@ def run_git_hook(cmd, param=[]):
args.append(p) args.append(p)
return subprocess.call(args) == 0 return subprocess.call(args) == 0
def write_pipe(c, stdin, *k, **kw): def write_pipe(c, stdin, *k, **kw):
if verbose: if verbose:
sys.stderr.write('Writing pipe: {}\n'.format(' '.join(c))) sys.stderr.write('Writing pipe: {}\n'.format(' '.join(c)))
@ -240,12 +253,14 @@ def write_pipe(c, stdin, *k, **kw):
return val return val
def p4_write_pipe(c, stdin, *k, **kw): def p4_write_pipe(c, stdin, *k, **kw):
real_cmd = p4_build_cmd(c) real_cmd = p4_build_cmd(c)
if bytes is not str and isinstance(stdin, str): if bytes is not str and isinstance(stdin, str):
stdin = encode_text_stream(stdin) stdin = encode_text_stream(stdin)
return write_pipe(real_cmd, stdin, *k, **kw) return write_pipe(real_cmd, stdin, *k, **kw)
def read_pipe_full(c, *k, **kw): def read_pipe_full(c, *k, **kw):
""" Read output from command. Returns a tuple """ Read output from command. Returns a tuple
of the return status, stdout text and stderr of the return status, stdout text and stderr
@ -259,6 +274,7 @@ def read_pipe_full(c, *k, **kw):
(out, err) = p.communicate() (out, err) = p.communicate()
return (p.returncode, out, decode_text_stream(err)) return (p.returncode, out, decode_text_stream(err))
def read_pipe(c, ignore_error=False, raw=False, *k, **kw): def read_pipe(c, ignore_error=False, raw=False, *k, **kw):
""" Read output from command. Returns the output text on """ Read output from command. Returns the output text on
success. On failure, terminates execution, unless success. On failure, terminates execution, unless
@ -276,6 +292,7 @@ def read_pipe(c, ignore_error=False, raw=False, *k, **kw):
out = decode_text_stream(out) out = decode_text_stream(out)
return out return out
def read_pipe_text(c, *k, **kw): def read_pipe_text(c, *k, **kw):
""" Read output from a command with trailing whitespace stripped. """ Read output from a command with trailing whitespace stripped.
On error, returns None. On error, returns None.
@ -286,10 +303,12 @@ def read_pipe_text(c, *k, **kw):
else: else:
return decode_text_stream(out).rstrip() return decode_text_stream(out).rstrip()
def p4_read_pipe(c, ignore_error=False, raw=False, *k, **kw): def p4_read_pipe(c, ignore_error=False, raw=False, *k, **kw):
real_cmd = p4_build_cmd(c) real_cmd = p4_build_cmd(c)
return read_pipe(real_cmd, ignore_error, raw=raw, *k, **kw) return read_pipe(real_cmd, ignore_error, raw=raw, *k, **kw)
def read_pipe_lines(c, raw=False, *k, **kw): def read_pipe_lines(c, raw=False, *k, **kw):
if verbose: if verbose:
sys.stderr.write('Reading pipe: {}\n'.format(' '.join(c))) sys.stderr.write('Reading pipe: {}\n'.format(' '.join(c)))
@ -303,11 +322,13 @@ def read_pipe_lines(c, raw=False, *k, **kw):
die('Command failed: {}'.format(' '.join(c))) die('Command failed: {}'.format(' '.join(c)))
return lines return lines
def p4_read_pipe_lines(c, *k, **kw): def p4_read_pipe_lines(c, *k, **kw):
"""Specifically invoke p4 on the command supplied. """ """Specifically invoke p4 on the command supplied. """
real_cmd = p4_build_cmd(c) real_cmd = p4_build_cmd(c)
return read_pipe_lines(real_cmd, *k, **kw) return read_pipe_lines(real_cmd, *k, **kw)
def p4_has_command(cmd): def p4_has_command(cmd):
"""Ask p4 for help on this command. If it returns an error, the """Ask p4 for help on this command. If it returns an error, the
command does not exist in this version of p4.""" command does not exist in this version of p4."""
@ -317,6 +338,7 @@ def p4_has_command(cmd):
p.communicate() p.communicate()
return p.returncode == 0 return p.returncode == 0
def p4_has_move_command(): def p4_has_move_command():
"""See if the move command exists, that it supports -k, and that """See if the move command exists, that it supports -k, and that
it has not been administratively disabled. The arguments it has not been administratively disabled. The arguments
@ -337,6 +359,7 @@ def p4_has_move_command():
# assume it failed because @... was invalid changelist # assume it failed because @... was invalid changelist
return True return True
def system(cmd, ignore_error=False, *k, **kw): def system(cmd, ignore_error=False, *k, **kw):
if verbose: if verbose:
sys.stderr.write("executing {}\n".format( sys.stderr.write("executing {}\n".format(
@ -347,6 +370,7 @@ def system(cmd, ignore_error=False, *k, **kw):
return retcode return retcode
def p4_system(cmd, *k, **kw): def p4_system(cmd, *k, **kw):
"""Specifically invoke p4 as the system command. """ """Specifically invoke p4 as the system command. """
real_cmd = p4_build_cmd(cmd) real_cmd = p4_build_cmd(cmd)
@ -354,9 +378,11 @@ def p4_system(cmd, *k, **kw):
if retcode: if retcode:
raise subprocess.CalledProcessError(retcode, real_cmd) raise subprocess.CalledProcessError(retcode, real_cmd)
def die_bad_access(s): def die_bad_access(s):
die("failure accessing depot: {0}".format(s.rstrip())) die("failure accessing depot: {0}".format(s.rstrip()))
def p4_check_access(min_expiration=1): def p4_check_access(min_expiration=1):
""" Check if we can access Perforce - account still logged in """ Check if we can access Perforce - account still logged in
""" """
@ -402,7 +428,10 @@ def p4_check_access(min_expiration=1):
else: else:
die_bad_access("unknown error code {0}".format(code)) die_bad_access("unknown error code {0}".format(code))
_p4_version_string = None _p4_version_string = None
def p4_version_string(): def p4_version_string():
"""Read the version string, showing just the last line, which """Read the version string, showing just the last line, which
hopefully is the interesting version bit. hopefully is the interesting version bit.
@ -418,12 +447,15 @@ def p4_version_string():
_p4_version_string = a[-1].rstrip() _p4_version_string = a[-1].rstrip()
return _p4_version_string return _p4_version_string
def p4_integrate(src, dest): def p4_integrate(src, dest):
p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)]) p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
def p4_sync(f, *options): def p4_sync(f, *options):
p4_system(["sync"] + list(options) + [wildcard_encode(f)]) p4_system(["sync"] + list(options) + [wildcard_encode(f)])
def p4_add(f): def p4_add(f):
# forcibly add file names with wildcards # forcibly add file names with wildcards
if wildcard_present(f): if wildcard_present(f):
@ -431,29 +463,37 @@ def p4_add(f):
else: else:
p4_system(["add", f]) p4_system(["add", f])
def p4_delete(f): def p4_delete(f):
p4_system(["delete", wildcard_encode(f)]) p4_system(["delete", wildcard_encode(f)])
def p4_edit(f, *options): def p4_edit(f, *options):
p4_system(["edit"] + list(options) + [wildcard_encode(f)]) p4_system(["edit"] + list(options) + [wildcard_encode(f)])
def p4_revert(f): def p4_revert(f):
p4_system(["revert", wildcard_encode(f)]) p4_system(["revert", wildcard_encode(f)])
def p4_reopen(type, f): def p4_reopen(type, f):
p4_system(["reopen", "-t", type, wildcard_encode(f)]) p4_system(["reopen", "-t", type, wildcard_encode(f)])
def p4_reopen_in_change(changelist, files): def p4_reopen_in_change(changelist, files):
cmd = ["reopen", "-c", str(changelist)] + files cmd = ["reopen", "-c", str(changelist)] + files
p4_system(cmd) p4_system(cmd)
def p4_move(src, dest): def p4_move(src, dest):
p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)]) p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
def p4_last_change(): def p4_last_change():
results = p4CmdList(["changes", "-m", "1"], skip_info=True) results = p4CmdList(["changes", "-m", "1"], skip_info=True)
return int(results[0]['change']) return int(results[0]['change'])
def p4_describe(change, shelved=False): def p4_describe(change, shelved=False):
"""Make sure it returns a valid result by checking for """Make sure it returns a valid result by checking for
the presence of field "time". Return a dict of the the presence of field "time". Return a dict of the
@ -482,6 +522,7 @@ def p4_describe(change, shelved=False):
return d return d
# #
# Canonicalize the p4 type and return a tuple of the # Canonicalize the p4 type and return a tuple of the
# base type, plus any modifiers. See "p4 help filetypes" # base type, plus any modifiers. See "p4 help filetypes"
@ -517,6 +558,7 @@ def split_p4_type(p4type):
mods = s[1] mods = s[1]
return (base, mods) return (base, mods)
# #
# return the raw p4 type of a file (text, text+ko, etc) # return the raw p4 type of a file (text, text+ko, etc)
# #
@ -524,6 +566,7 @@ def p4_type(f):
results = p4CmdList(["fstat", "-T", "headType", wildcard_encode(f)]) results = p4CmdList(["fstat", "-T", "headType", wildcard_encode(f)])
return results[0]['headType'] return results[0]['headType']
# #
# Given a type base and modifier, return a regexp matching # Given a type base and modifier, return a regexp matching
# the keywords that can be expanded in the file # the keywords that can be expanded in the file
@ -539,6 +582,7 @@ def p4_keywords_regexp_for_type(base, type_mods):
else: else:
return None return None
# #
# Given a file, return a regexp matching the possible # Given a file, return a regexp matching the possible
# RCS keywords that will be expanded, or None for files # RCS keywords that will be expanded, or None for files
@ -551,6 +595,7 @@ def p4_keywords_regexp_for_file(file):
(type_base, type_mods) = split_p4_type(p4_type(file)) (type_base, type_mods) = split_p4_type(p4_type(file))
return p4_keywords_regexp_for_type(type_base, type_mods) return p4_keywords_regexp_for_type(type_base, type_mods)
def setP4ExecBit(file, mode): def setP4ExecBit(file, mode):
# Reopens an already open file and changes the execute bit to match # Reopens an already open file and changes the execute bit to match
# the execute bit setting in the passed in mode. # the execute bit setting in the passed in mode.
@ -566,6 +611,7 @@ def setP4ExecBit(file, mode):
p4_reopen(p4Type, file) p4_reopen(p4Type, file)
def getP4OpenedType(file): def getP4OpenedType(file):
# Returns the perforce file type for the given file. # Returns the perforce file type for the given file.
@ -576,6 +622,7 @@ def getP4OpenedType(file):
else: else:
die("Could not determine file type for %s (result: '%s')" % (file, result)) die("Could not determine file type for %s (result: '%s')" % (file, result))
# Return the set of all p4 labels # Return the set of all p4 labels
def getP4Labels(depotPaths): def getP4Labels(depotPaths):
labels = set() labels = set()
@ -588,6 +635,7 @@ def getP4Labels(depotPaths):
return labels return labels
# Return the set of all git tags # Return the set of all git tags
def getGitTags(): def getGitTags():
gitTags = set() gitTags = set()
@ -596,8 +644,10 @@ def getGitTags():
gitTags.add(tag) gitTags.add(tag)
return gitTags return gitTags
_diff_tree_pattern = None _diff_tree_pattern = None
def parseDiffTreeEntry(entry): def parseDiffTreeEntry(entry):
"""Parses a single diff tree entry into its component elements. """Parses a single diff tree entry into its component elements.
@ -635,41 +685,52 @@ def parseDiffTreeEntry(entry):
} }
return None return None
def isModeExec(mode): def isModeExec(mode):
# Returns True if the given git mode represents an executable file, # Returns True if the given git mode represents an executable file,
# otherwise False. # otherwise False.
return mode[-3:] == "755" return mode[-3:] == "755"
class P4Exception(Exception): class P4Exception(Exception):
""" Base class for exceptions from the p4 client """ """ Base class for exceptions from the p4 client """
def __init__(self, exit_code): def __init__(self, exit_code):
self.p4ExitCode = exit_code self.p4ExitCode = exit_code
class P4ServerException(P4Exception): class P4ServerException(P4Exception):
""" Base class for exceptions where we get some kind of marshalled up result from the server """ """ Base class for exceptions where we get some kind of marshalled up result from the server """
def __init__(self, exit_code, p4_result): def __init__(self, exit_code, p4_result):
super(P4ServerException, self).__init__(exit_code) super(P4ServerException, self).__init__(exit_code)
self.p4_result = p4_result self.p4_result = p4_result
self.code = p4_result[0]['code'] self.code = p4_result[0]['code']
self.data = p4_result[0]['data'] self.data = p4_result[0]['data']
class P4RequestSizeException(P4ServerException): class P4RequestSizeException(P4ServerException):
""" One of the maxresults or maxscanrows errors """ """ One of the maxresults or maxscanrows errors """
def __init__(self, exit_code, p4_result, limit): def __init__(self, exit_code, p4_result, limit):
super(P4RequestSizeException, self).__init__(exit_code, p4_result) super(P4RequestSizeException, self).__init__(exit_code, p4_result)
self.limit = limit self.limit = limit
class P4CommandException(P4Exception): class P4CommandException(P4Exception):
""" Something went wrong calling p4 which means we have to give up """ """ Something went wrong calling p4 which means we have to give up """
def __init__(self, msg): def __init__(self, msg):
self.msg = msg self.msg = msg
def __str__(self): def __str__(self):
return self.msg return self.msg
def isModeExecChanged(src_mode, dst_mode): def isModeExecChanged(src_mode, dst_mode):
return isModeExec(src_mode) != isModeExec(dst_mode) return isModeExec(src_mode) != isModeExec(dst_mode)
def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False, def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
errors_as_exceptions=False, *k, **kw): errors_as_exceptions=False, *k, **kw):
@ -746,6 +807,7 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
return result return result
def p4Cmd(cmd, *k, **kw): def p4Cmd(cmd, *k, **kw):
list = p4CmdList(cmd, *k, **kw) list = p4CmdList(cmd, *k, **kw)
result = {} result = {}
@ -753,6 +815,7 @@ def p4Cmd(cmd, *k, **kw):
result.update(entry) result.update(entry)
return result; return result;
def p4Where(depotPath): def p4Where(depotPath):
if not depotPath.endswith("/"): if not depotPath.endswith("/"):
depotPath += "/" depotPath += "/"
@ -789,20 +852,25 @@ def p4Where(depotPath):
clientPath = clientPath[:-3] clientPath = clientPath[:-3]
return clientPath return clientPath
def currentGitBranch(): def currentGitBranch():
return read_pipe_text(["git", "symbolic-ref", "--short", "-q", "HEAD"]) return read_pipe_text(["git", "symbolic-ref", "--short", "-q", "HEAD"])
def isValidGitDir(path): def isValidGitDir(path):
return git_dir(path) != None return git_dir(path) != None
def parseRevision(ref): def parseRevision(ref):
return read_pipe(["git", "rev-parse", ref]).strip() return read_pipe(["git", "rev-parse", ref]).strip()
def branchExists(ref): def branchExists(ref):
rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref], rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
ignore_error=True) ignore_error=True)
return len(rev) > 0 return len(rev) > 0
def extractLogMessageFromGitCommit(commit): def extractLogMessageFromGitCommit(commit):
logMessage = "" logMessage = ""
@ -817,6 +885,7 @@ def extractLogMessageFromGitCommit(commit):
logMessage += log logMessage += log
return logMessage return logMessage
def extractSettingsGitLog(log): def extractSettingsGitLog(log):
values = {} values = {}
for line in log.split("\n"): for line in log.split("\n"):
@ -842,19 +911,24 @@ def extractSettingsGitLog(log):
values['depot-paths'] = paths.split(',') values['depot-paths'] = paths.split(',')
return values return values
def gitBranchExists(branch): def gitBranchExists(branch):
proc = subprocess.Popen(["git", "rev-parse", branch], proc = subprocess.Popen(["git", "rev-parse", branch],
stderr=subprocess.PIPE, stdout=subprocess.PIPE); stderr=subprocess.PIPE, stdout=subprocess.PIPE);
return proc.wait() == 0; return proc.wait() == 0;
def gitUpdateRef(ref, newvalue): def gitUpdateRef(ref, newvalue):
subprocess.check_call(["git", "update-ref", ref, newvalue]) subprocess.check_call(["git", "update-ref", ref, newvalue])
def gitDeleteRef(ref): def gitDeleteRef(ref):
subprocess.check_call(["git", "update-ref", "-d", ref]) subprocess.check_call(["git", "update-ref", "-d", ref])
_gitConfig = {} _gitConfig = {}
def gitConfig(key, typeSpecifier=None): def gitConfig(key, typeSpecifier=None):
if key not in _gitConfig: if key not in _gitConfig:
cmd = [ "git", "config" ] cmd = [ "git", "config" ]
@ -865,6 +939,7 @@ def gitConfig(key, typeSpecifier=None):
_gitConfig[key] = s.strip() _gitConfig[key] = s.strip()
return _gitConfig[key] return _gitConfig[key]
def gitConfigBool(key): def gitConfigBool(key):
"""Return a bool, using git config --bool. It is True only if the """Return a bool, using git config --bool. It is True only if the
variable is set to true, and False if set to false or not present variable is set to true, and False if set to false or not present
@ -874,6 +949,7 @@ def gitConfigBool(key):
_gitConfig[key] = gitConfig(key, '--bool') == "true" _gitConfig[key] = gitConfig(key, '--bool') == "true"
return _gitConfig[key] return _gitConfig[key]
def gitConfigInt(key): def gitConfigInt(key):
if key not in _gitConfig: if key not in _gitConfig:
cmd = [ "git", "config", "--int", key ] cmd = [ "git", "config", "--int", key ]
@ -885,6 +961,7 @@ def gitConfigInt(key):
_gitConfig[key] = None _gitConfig[key] = None
return _gitConfig[key] return _gitConfig[key]
def gitConfigList(key): def gitConfigList(key):
if key not in _gitConfig: if key not in _gitConfig:
s = read_pipe(["git", "config", "--get-all", key], ignore_error=True) s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
@ -893,6 +970,7 @@ def gitConfigList(key):
_gitConfig[key] = [] _gitConfig[key] = []
return _gitConfig[key] return _gitConfig[key]
def p4BranchesInGit(branchesAreInRemotes=True): def p4BranchesInGit(branchesAreInRemotes=True):
"""Find all the branches whose names start with "p4/", looking """Find all the branches whose names start with "p4/", looking
in remotes or heads as specified by the argument. Return in remotes or heads as specified by the argument. Return
@ -925,6 +1003,7 @@ def p4BranchesInGit(branchesAreInRemotes=True):
return branches return branches
def branch_exists(branch): def branch_exists(branch):
"""Make sure that the given ref name really exists.""" """Make sure that the given ref name really exists."""
@ -937,6 +1016,7 @@ def branch_exists(branch):
# expect exactly one line of output: the branch name # expect exactly one line of output: the branch name
return out.rstrip() == branch return out.rstrip() == branch
def findUpstreamBranchPoint(head = "HEAD"): def findUpstreamBranchPoint(head = "HEAD"):
branches = p4BranchesInGit() branches = p4BranchesInGit()
# map from depot-path to branch name # map from depot-path to branch name
@ -964,6 +1044,7 @@ def findUpstreamBranchPoint(head = "HEAD"):
return ["", settings] return ["", settings]
def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True): def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
if not silent: if not silent:
print("Creating/updating branch(es) in %s based on origin branch(es)" print("Creating/updating branch(es) in %s based on origin branch(es)"
@ -1011,6 +1092,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
if update: if update:
system(["git", "update-ref", remoteHead, originHead]) system(["git", "update-ref", remoteHead, originHead])
def originP4BranchesExist(): def originP4BranchesExist():
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
@ -1024,12 +1106,14 @@ def p4ParseNumericChangeRange(parts):
return (changeStart, changeEnd) return (changeStart, changeEnd)
def chooseBlockSize(blockSize): def chooseBlockSize(blockSize):
if blockSize: if blockSize:
return blockSize return blockSize
else: else:
return defaultBlockSize return defaultBlockSize
def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize): def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
assert depotPaths assert depotPaths
@ -1107,6 +1191,7 @@ def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
changes = sorted(changes) changes = sorted(changes)
return changes return changes
def p4PathStartsWith(path, prefix): def p4PathStartsWith(path, prefix):
# This method tries to remedy a potential mixed-case issue: # This method tries to remedy a potential mixed-case issue:
# #
@ -1119,6 +1204,7 @@ def p4PathStartsWith(path, prefix):
return path.lower().startswith(prefix.lower()) return path.lower().startswith(prefix.lower())
return path.startswith(prefix) return path.startswith(prefix)
def getClientSpec(): def getClientSpec():
"""Look at the p4 client spec, create a View() object that contains """Look at the p4 client spec, create a View() object that contains
all the mappings, and return it.""" all the mappings, and return it."""
@ -1149,6 +1235,7 @@ def getClientSpec():
return view return view
def getClientRoot(): def getClientRoot():
"""Grab the client directory.""" """Grab the client directory."""
@ -1162,6 +1249,7 @@ def getClientRoot():
return entry["Root"] return entry["Root"]
# #
# P4 wildcards are not allowed in filenames. P4 complains # P4 wildcards are not allowed in filenames. P4 complains
# if you simply add them, but you can force it with "-f", in # if you simply add them, but you can force it with "-f", in
@ -1179,6 +1267,7 @@ def wildcard_decode(path):
.replace("%25", "%") .replace("%25", "%")
return path return path
def wildcard_encode(path): def wildcard_encode(path):
# do % first to avoid double-encoding the %s introduced here # do % first to avoid double-encoding the %s introduced here
path = path.replace("%", "%25") \ path = path.replace("%", "%25") \
@ -1187,10 +1276,12 @@ def wildcard_encode(path):
.replace("@", "%40") .replace("@", "%40")
return path return path
def wildcard_present(path): def wildcard_present(path):
m = re.search("[*#@%]", path) m = re.search("[*#@%]", path)
return m is not None return m is not None
class LargeFileSystem(object): class LargeFileSystem(object):
"""Base class for large file system support.""" """Base class for large file system support."""
@ -1272,6 +1363,7 @@ class LargeFileSystem(object):
sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile)) sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
return (git_mode, contents) return (git_mode, contents)
class MockLFS(LargeFileSystem): class MockLFS(LargeFileSystem):
"""Mock large file system for testing.""" """Mock large file system for testing."""
@ -1295,6 +1387,7 @@ class MockLFS(LargeFileSystem):
os.makedirs(remotePath) os.makedirs(remotePath)
shutil.copyfile(localLargeFile, os.path.join(remotePath, os.path.basename(localLargeFile))) shutil.copyfile(localLargeFile, os.path.join(remotePath, os.path.basename(localLargeFile)))
class GitLFS(LargeFileSystem): class GitLFS(LargeFileSystem):
"""Git LFS as backend for the git-p4 large file system. """Git LFS as backend for the git-p4 large file system.
See https://git-lfs.github.com/ for details.""" See https://git-lfs.github.com/ for details."""
@ -1383,6 +1476,7 @@ class GitLFS(LargeFileSystem):
else: else:
return LargeFileSystem.processContent(self, git_mode, relPath, contents) return LargeFileSystem.processContent(self, git_mode, relPath, contents)
class Command: class Command:
delete_actions = ( "delete", "move/delete", "purge" ) delete_actions = ( "delete", "move/delete", "purge" )
add_actions = ( "add", "branch", "move/add" ) add_actions = ( "add", "branch", "move/add" )
@ -1398,6 +1492,7 @@ class Command:
setattr(self, attr, value) setattr(self, attr, value)
return getattr(self, attr) return getattr(self, attr)
class P4UserMap: class P4UserMap:
def __init__(self): def __init__(self):
self.userMapFromPerforceServer = False self.userMapFromPerforceServer = False
@ -1468,6 +1563,7 @@ class P4UserMap:
except IOError: except IOError:
self.getUserMapFromPerforceServer() self.getUserMapFromPerforceServer()
class P4Submit(Command, P4UserMap): class P4Submit(Command, P4UserMap):
conflict_behavior_choices = ("ask", "skip", "quit") conflict_behavior_choices = ("ask", "skip", "quit")
@ -2473,6 +2569,7 @@ class P4Submit(Command, P4UserMap):
return True return True
class View(object): class View(object):
"""Represent a p4 view ("p4 help views"), and map files in a """Represent a p4 view ("p4 help views"), and map files in a
repo according to the view.""" repo according to the view."""
@ -2580,11 +2677,13 @@ class View(object):
die( "Error: %s is not found in client spec path" % depot_path ) die( "Error: %s is not found in client spec path" % depot_path )
return "" return ""
def cloneExcludeCallback(option, opt_str, value, parser): def cloneExcludeCallback(option, opt_str, value, parser):
# prepend "/" because the first "/" was consumed as part of the option itself. # prepend "/" because the first "/" was consumed as part of the option itself.
# ("-//depot/A/..." becomes "/depot/A/..." after option parsing) # ("-//depot/A/..." becomes "/depot/A/..." after option parsing)
parser.values.cloneExclude += ["/" + re.sub(r"\.\.\.$", "", value)] parser.values.cloneExclude += ["/" + re.sub(r"\.\.\.$", "", value)]
class P4Sync(Command, P4UserMap): class P4Sync(Command, P4UserMap):
def __init__(self): def __init__(self):
@ -3942,6 +4041,7 @@ class P4Sync(Command, P4UserMap):
return True return True
class P4Rebase(Command): class P4Rebase(Command):
def __init__(self): def __init__(self):
Command.__init__(self) Command.__init__(self)
@ -3979,6 +4079,7 @@ class P4Rebase(Command):
"HEAD", "--"]) "HEAD", "--"])
return True return True
class P4Clone(P4Sync): class P4Clone(P4Sync):
def __init__(self): def __init__(self):
P4Sync.__init__(self) P4Sync.__init__(self)
@ -4057,6 +4158,7 @@ class P4Clone(P4Sync):
return True return True
class P4Unshelve(Command): class P4Unshelve(Command):
def __init__(self): def __init__(self):
Command.__init__(self) Command.__init__(self)
@ -4172,6 +4274,7 @@ class P4Unshelve(Command):
return True return True
class P4Branches(Command): class P4Branches(Command):
def __init__(self): def __init__(self):
Command.__init__(self) Command.__init__(self)
@ -4197,6 +4300,7 @@ class P4Branches(Command):
print("%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])) print("%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"]))
return True return True
class HelpFormatter(optparse.IndentedHelpFormatter): class HelpFormatter(optparse.IndentedHelpFormatter):
def __init__(self): def __init__(self):
optparse.IndentedHelpFormatter.__init__(self) optparse.IndentedHelpFormatter.__init__(self)
@ -4207,6 +4311,7 @@ class HelpFormatter(optparse.IndentedHelpFormatter):
else: else:
return "" return ""
def printUsage(commands): def printUsage(commands):
print("usage: %s <command> [options]" % sys.argv[0]) print("usage: %s <command> [options]" % sys.argv[0])
print("") print("")
@ -4215,6 +4320,7 @@ def printUsage(commands):
print("Try %s <command> --help for command specific help." % sys.argv[0]) print("Try %s <command> --help for command specific help." % sys.argv[0])
print("") print("")
commands = { commands = {
"submit" : P4Submit, "submit" : P4Submit,
"commit" : P4Submit, "commit" : P4Submit,
@ -4225,6 +4331,7 @@ commands = {
"unshelve" : P4Unshelve, "unshelve" : P4Unshelve,
} }
def main(): def main():
if len(sys.argv[1:]) == 0: if len(sys.argv[1:]) == 0:
printUsage(commands.keys()) printUsage(commands.keys())