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>

Joel Holdsworth committed Apr 1, 2022 at 15:24 UTC adf159b44183537538e78c2e69997f9f605b92d4
1 file changed +107
git-p4.py
+107
@@ -59,6 +59,7 @@ p4_access_checked = False
59 re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$')
60 re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$')
61
62 +
63 def format_size_human_readable(num):
64 """ Returns a number of units (typically bytes) formatted as a human-readable
65 string.
@@ -71,6 +72,7 @@ def format_size_human_readable(num):
72 return "{:3.1f} {}B".format(num, unit)
73 return "{:.1f} YiB".format(num)
74
75 +
76 def p4_build_cmd(cmd):
77 """Build a suitable p4 command line.
78
@@ -118,6 +120,7 @@ def p4_build_cmd(cmd):
120
121 return real_cmd
122
123 +
124 def git_dir(path):
125 """ Return TRUE if the given path is a git directory (/path/to/dir/.git).
126 This won't automatically add ".git" to a directory.
@@ -128,6 +131,7 @@ def git_dir(path):
131 else:
132 return d
133
134 +
135 def chdir(path, is_client_path=False):
136 """Do chdir to the given path, and set the PWD environment
137 variable for use by P4. It does not look at getcwd() output.
@@ -150,6 +154,7 @@ def chdir(path, is_client_path=False):
154 path = os.getcwd()
155 os.environ['PWD'] = path
156
157 +
158 def calcDiskFree():
159 """Return free space in bytes on the disk of the given dirname."""
160 if platform.system() == 'Windows':
@@ -160,6 +165,7 @@ def calcDiskFree():
165 st = os.statvfs(os.getcwd())
166 return st.f_bavail * st.f_frsize
167
168 +
169 def die(msg):
170 """ Terminate execution. Make sure that any running child processes have been wait()ed for before
171 calling this.
@@ -170,6 +176,7 @@ def die(msg):
176 sys.stderr.write(msg + "\n")
177 sys.exit(1)
178
179 +
180 def prompt(prompt_text):
181 """ Prompt the user to choose one of the choices
182
@@ -188,21 +195,25 @@ def prompt(prompt_text):
195 if response in choices:
196 return response
197
198 +
199 # We need different encoding/decoding strategies for text data being passed
200 # around in pipes depending on python version
201 if bytes is not str:
202 # For python3, always encode and decode as appropriate
203 def decode_text_stream(s):
204 return s.decode() if isinstance(s, bytes) else s
205 +
206 def encode_text_stream(s):
207 return s.encode() if isinstance(s, str) else s
208 else:
209 # For python2.7, pass read strings as-is, but also allow writing unicode
210 def decode_text_stream(s):
211 return s
212 +
213 def encode_text_stream(s):
214 return s.encode('utf_8') if isinstance(s, unicode) else s
215
216 +
217 def decode_path(path):
218 """Decode a given string (bytes or otherwise) using configured path encoding options
219 """
@@ -218,6 +229,7 @@ def decode_path(path):
229 print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path))
230 return path
231
232 +
233 def run_git_hook(cmd, param=[]):
234 """Execute a hook if the hook exists."""
235 args = ['git', 'hook', 'run', '--ignore-missing', cmd]
@@ -227,6 +239,7 @@ def run_git_hook(cmd, param=[]):
239 args.append(p)
240 return subprocess.call(args) == 0
241
242 +
243 def write_pipe(c, stdin, *k, **kw):
244 if verbose:
245 sys.stderr.write('Writing pipe: {}\n'.format(' '.join(c)))
@@ -240,12 +253,14 @@ def write_pipe(c, stdin, *k, **kw):
253
254 return val
255
256 +
257 def p4_write_pipe(c, stdin, *k, **kw):
258 real_cmd = p4_build_cmd(c)
259 if bytes is not str and isinstance(stdin, str):
260 stdin = encode_text_stream(stdin)
261 return write_pipe(real_cmd, stdin, *k, **kw)
262
263 +
264 def read_pipe_full(c, *k, **kw):
265 """ Read output from command. Returns a tuple
266 of the return status, stdout text and stderr
@@ -259,6 +274,7 @@ def read_pipe_full(c, *k, **kw):
274 (out, err) = p.communicate()
275 return (p.returncode, out, decode_text_stream(err))
276
277 +
278 def read_pipe(c, ignore_error=False, raw=False, *k, **kw):
279 """ Read output from command. Returns the output text on
280 success. On failure, terminates execution, unless
@@ -276,6 +292,7 @@ def read_pipe(c, ignore_error=False, raw=False, *k, **kw):
292 out = decode_text_stream(out)
293 return out
294
295 +
296 def read_pipe_text(c, *k, **kw):
297 """ Read output from a command with trailing whitespace stripped.
298 On error, returns None.
@@ -286,10 +303,12 @@ def read_pipe_text(c, *k, **kw):
303 else:
304 return decode_text_stream(out).rstrip()
305
306 +
307 def p4_read_pipe(c, ignore_error=False, raw=False, *k, **kw):
308 real_cmd = p4_build_cmd(c)
309 return read_pipe(real_cmd, ignore_error, raw=raw, *k, **kw)
310
311 +
312 def read_pipe_lines(c, raw=False, *k, **kw):
313 if verbose:
314 sys.stderr.write('Reading pipe: {}\n'.format(' '.join(c)))
@@ -303,11 +322,13 @@ def read_pipe_lines(c, raw=False, *k, **kw):
322 die('Command failed: {}'.format(' '.join(c)))
323 return lines
324
325 +
326 def p4_read_pipe_lines(c, *k, **kw):
327 """Specifically invoke p4 on the command supplied. """
328 real_cmd = p4_build_cmd(c)
329 return read_pipe_lines(real_cmd, *k, **kw)
330
331 +
332 def p4_has_command(cmd):
333 """Ask p4 for help on this command. If it returns an error, the
334 command does not exist in this version of p4."""
@@ -317,6 +338,7 @@ def p4_has_command(cmd):
338 p.communicate()
339 return p.returncode == 0
340
341 +
342 def p4_has_move_command():
343 """See if the move command exists, that it supports -k, and that
344 it has not been administratively disabled. The arguments
@@ -337,6 +359,7 @@ def p4_has_move_command():
359 # assume it failed because @... was invalid changelist
360 return True
361
362 +
363 def system(cmd, ignore_error=False, *k, **kw):
364 if verbose:
365 sys.stderr.write("executing {}\n".format(
@@ -347,6 +370,7 @@ def system(cmd, ignore_error=False, *k, **kw):
370
371 return retcode
372
373 +
374 def p4_system(cmd, *k, **kw):
375 """Specifically invoke p4 as the system command. """
376 real_cmd = p4_build_cmd(cmd)
@@ -354,9 +378,11 @@ def p4_system(cmd, *k, **kw):
378 if retcode:
379 raise subprocess.CalledProcessError(retcode, real_cmd)
380
381 +
382 def die_bad_access(s):
383 die("failure accessing depot: {0}".format(s.rstrip()))
384
385 +
386 def p4_check_access(min_expiration=1):
387 """ Check if we can access Perforce - account still logged in
388 """
@@ -402,7 +428,10 @@ def p4_check_access(min_expiration=1):
428 else:
429 die_bad_access("unknown error code {0}".format(code))
430
431 +
432 _p4_version_string = None
433 +
434 +
435 def p4_version_string():
436 """Read the version string, showing just the last line, which
437 hopefully is the interesting version bit.
@@ -418,12 +447,15 @@ def p4_version_string():
447 _p4_version_string = a[-1].rstrip()
448 return _p4_version_string
449
450 +
451 def p4_integrate(src, dest):
452 p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
453
454 +
455 def p4_sync(f, *options):
456 p4_system(["sync"] + list(options) + [wildcard_encode(f)])
457
458 +
459 def p4_add(f):
460 # forcibly add file names with wildcards
461 if wildcard_present(f):
@@ -431,29 +463,37 @@ def p4_add(f):
463 else:
464 p4_system(["add", f])
465
466 +
467 def p4_delete(f):
468 p4_system(["delete", wildcard_encode(f)])
469
470 +
471 def p4_edit(f, *options):
472 p4_system(["edit"] + list(options) + [wildcard_encode(f)])
473
474 +
475 def p4_revert(f):
476 p4_system(["revert", wildcard_encode(f)])
477
478 +
479 def p4_reopen(type, f):
480 p4_system(["reopen", "-t", type, wildcard_encode(f)])
481
482 +
483 def p4_reopen_in_change(changelist, files):
484 cmd = ["reopen", "-c", str(changelist)] + files
485 p4_system(cmd)
486
487 +
488 def p4_move(src, dest):
489 p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
490
491 +
492 def p4_last_change():
493 results = p4CmdList(["changes", "-m", "1"], skip_info=True)
494 return int(results[0]['change'])
495
496 +
497 def p4_describe(change, shelved=False):
498 """Make sure it returns a valid result by checking for
499 the presence of field "time". Return a dict of the
@@ -482,6 +522,7 @@ def p4_describe(change, shelved=False):
522
523 return d
524
525 +
526 #
527 # Canonicalize the p4 type and return a tuple of the
528 # base type, plus any modifiers. See "p4 help filetypes"
@@ -517,6 +558,7 @@ def split_p4_type(p4type):
558 mods = s[1]
559 return (base, mods)
560
561 +
562 #
563 # return the raw p4 type of a file (text, text+ko, etc)
564 #
@@ -524,6 +566,7 @@ def p4_type(f):
566 results = p4CmdList(["fstat", "-T", "headType", wildcard_encode(f)])
567 return results[0]['headType']
568
569 +
570 #
571 # Given a type base and modifier, return a regexp matching
572 # the keywords that can be expanded in the file
@@ -539,6 +582,7 @@ def p4_keywords_regexp_for_type(base, type_mods):
582 else:
583 return None
584
585 +
586 #
587 # Given a file, return a regexp matching the possible
588 # RCS keywords that will be expanded, or None for files
@@ -551,6 +595,7 @@ def p4_keywords_regexp_for_file(file):
595 (type_base, type_mods) = split_p4_type(p4_type(file))
596 return p4_keywords_regexp_for_type(type_base, type_mods)
597
598 +
599 def setP4ExecBit(file, mode):
600 # Reopens an already open file and changes the execute bit to match
601 # the execute bit setting in the passed in mode.
@@ -566,6 +611,7 @@ def setP4ExecBit(file, mode):
611
612 p4_reopen(p4Type, file)
613
614 +
615 def getP4OpenedType(file):
616 # Returns the perforce file type for the given file.
617
@@ -576,6 +622,7 @@ def getP4OpenedType(file):
622 else:
623 die("Could not determine file type for %s (result: '%s')" % (file, result))
624
625 +
626 # Return the set of all p4 labels
627 def getP4Labels(depotPaths):
628 labels = set()
@@ -588,6 +635,7 @@ def getP4Labels(depotPaths):
635
636 return labels
637
638 +
639 # Return the set of all git tags
640 def getGitTags():
641 gitTags = set()
@@ -596,8 +644,10 @@ def getGitTags():
644 gitTags.add(tag)
645 return gitTags
646
647 +
648 _diff_tree_pattern = None
649
650 +
651 def parseDiffTreeEntry(entry):
652 """Parses a single diff tree entry into its component elements.
653
@@ -635,41 +685,52 @@ def parseDiffTreeEntry(entry):
685 }
686 return None
687
688 +
689 def isModeExec(mode):
690 # Returns True if the given git mode represents an executable file,
691 # otherwise False.
692 return mode[-3:] == "755"
693
694 +
695 class P4Exception(Exception):
696 """ Base class for exceptions from the p4 client """
697 +
698 def __init__(self, exit_code):
699 self.p4ExitCode = exit_code
700
701 +
702 class P4ServerException(P4Exception):
703 """ Base class for exceptions where we get some kind of marshalled up result from the server """
704 +
705 def __init__(self, exit_code, p4_result):
706 super(P4ServerException, self).__init__(exit_code)
707 self.p4_result = p4_result
708 self.code = p4_result[0]['code']
709 self.data = p4_result[0]['data']
710
711 +
712 class P4RequestSizeException(P4ServerException):
713 """ One of the maxresults or maxscanrows errors """
714 +
715 def __init__(self, exit_code, p4_result, limit):
716 super(P4RequestSizeException, self).__init__(exit_code, p4_result)
717 self.limit = limit
718
719 +
720 class P4CommandException(P4Exception):
721 """ Something went wrong calling p4 which means we have to give up """
722 +
723 def __init__(self, msg):
724 self.msg = msg
725
726 def __str__(self):
727 return self.msg
728
729 +
730 def isModeExecChanged(src_mode, dst_mode):
731 return isModeExec(src_mode) != isModeExec(dst_mode)
732
733 +
734 def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
735 errors_as_exceptions=False, *k, **kw):
736
@@ -746,6 +807,7 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
807
808 return result
809
810 +
811 def p4Cmd(cmd, *k, **kw):
812 list = p4CmdList(cmd, *k, **kw)
813 result = {}
@@ -753,6 +815,7 @@ def p4Cmd(cmd, *k, **kw):
815 result.update(entry)
816 return result;
817
818 +
819 def p4Where(depotPath):
820 if not depotPath.endswith("/"):
821 depotPath += "/"
@@ -789,20 +852,25 @@ def p4Where(depotPath):
852 clientPath = clientPath[:-3]
853 return clientPath
854
855 +
856 def currentGitBranch():
857 return read_pipe_text(["git", "symbolic-ref", "--short", "-q", "HEAD"])
858
859 +
860 def isValidGitDir(path):
861 return git_dir(path) != None
862
863 +
864 def parseRevision(ref):
865 return read_pipe(["git", "rev-parse", ref]).strip()
866
867 +
868 def branchExists(ref):
869 rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
870 ignore_error=True)
871 return len(rev) > 0
872
873 +
874 def extractLogMessageFromGitCommit(commit):
875 logMessage = ""
876
@@ -817,6 +885,7 @@ def extractLogMessageFromGitCommit(commit):
885 logMessage += log
886 return logMessage
887
888 +
889 def extractSettingsGitLog(log):
890 values = {}
891 for line in log.split("\n"):
@@ -842,19 +911,24 @@ def extractSettingsGitLog(log):
911 values['depot-paths'] = paths.split(',')
912 return values
913
914 +
915 def gitBranchExists(branch):
916 proc = subprocess.Popen(["git", "rev-parse", branch],
917 stderr=subprocess.PIPE, stdout=subprocess.PIPE);
918 return proc.wait() == 0;
919
920 +
921 def gitUpdateRef(ref, newvalue):
922 subprocess.check_call(["git", "update-ref", ref, newvalue])
923
924 +
925 def gitDeleteRef(ref):
926 subprocess.check_call(["git", "update-ref", "-d", ref])
927
928 +
929 _gitConfig = {}
930
931 +
932 def gitConfig(key, typeSpecifier=None):
933 if key not in _gitConfig:
934 cmd = [ "git", "config" ]
@@ -865,6 +939,7 @@ def gitConfig(key, typeSpecifier=None):
939 _gitConfig[key] = s.strip()
940 return _gitConfig[key]
941
942 +
943 def gitConfigBool(key):
944 """Return a bool, using git config --bool. It is True only if the
945 variable is set to true, and False if set to false or not present
@@ -874,6 +949,7 @@ def gitConfigBool(key):
949 _gitConfig[key] = gitConfig(key, '--bool') == "true"
950 return _gitConfig[key]
951
952 +
953 def gitConfigInt(key):
954 if key not in _gitConfig:
955 cmd = [ "git", "config", "--int", key ]
@@ -885,6 +961,7 @@ def gitConfigInt(key):
961 _gitConfig[key] = None
962 return _gitConfig[key]
963
964 +
965 def gitConfigList(key):
966 if key not in _gitConfig:
967 s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
@@ -893,6 +970,7 @@ def gitConfigList(key):
970 _gitConfig[key] = []
971 return _gitConfig[key]
972
973 +
974 def p4BranchesInGit(branchesAreInRemotes=True):
975 """Find all the branches whose names start with "p4/", looking
976 in remotes or heads as specified by the argument. Return
@@ -925,6 +1003,7 @@ def p4BranchesInGit(branchesAreInRemotes=True):
1003
1004 return branches
1005
1006 +
1007 def branch_exists(branch):
1008 """Make sure that the given ref name really exists."""
1009
@@ -937,6 +1016,7 @@ def branch_exists(branch):
1016 # expect exactly one line of output: the branch name
1017 return out.rstrip() == branch
1018
1019 +
1020 def findUpstreamBranchPoint(head = "HEAD"):
1021 branches = p4BranchesInGit()
1022 # map from depot-path to branch name
@@ -964,6 +1044,7 @@ def findUpstreamBranchPoint(head = "HEAD"):
1044
1045 return ["", settings]
1046
1047 +
1048 def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
1049 if not silent:
1050 print("Creating/updating branch(es) in %s based on origin branch(es)"
@@ -1011,6 +1092,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
1092 if update:
1093 system(["git", "update-ref", remoteHead, originHead])
1094
1095 +
1096 def originP4BranchesExist():
1097 return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
1098
@@ -1024,12 +1106,14 @@ def p4ParseNumericChangeRange(parts):
1106
1107 return (changeStart, changeEnd)
1108
1109 +
1110 def chooseBlockSize(blockSize):
1111 if blockSize:
1112 return blockSize
1113 else:
1114 return defaultBlockSize
1115
1116 +
1117 def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
1118 assert depotPaths
1119
@@ -1107,6 +1191,7 @@ def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
1191 changes = sorted(changes)
1192 return changes
1193
1194 +
1195 def p4PathStartsWith(path, prefix):
1196 # This method tries to remedy a potential mixed-case issue:
1197 #
@@ -1119,6 +1204,7 @@ def p4PathStartsWith(path, prefix):
1204 return path.lower().startswith(prefix.lower())
1205 return path.startswith(prefix)
1206
1207 +
1208 def getClientSpec():
1209 """Look at the p4 client spec, create a View() object that contains
1210 all the mappings, and return it."""
@@ -1149,6 +1235,7 @@ def getClientSpec():
1235
1236 return view
1237
1238 +
1239 def getClientRoot():
1240 """Grab the client directory."""
1241
@@ -1162,6 +1249,7 @@ def getClientRoot():
1249
1250 return entry["Root"]
1251
1252 +
1253 #
1254 # P4 wildcards are not allowed in filenames. P4 complains
1255 # if you simply add them, but you can force it with "-f", in
@@ -1179,6 +1267,7 @@ def wildcard_decode(path):
1267 .replace("%25", "%")
1268 return path
1269
1270 +
1271 def wildcard_encode(path):
1272 # do % first to avoid double-encoding the %s introduced here
1273 path = path.replace("%", "%25") \
@@ -1187,10 +1276,12 @@ def wildcard_encode(path):
1276 .replace("@", "%40")
1277 return path
1278
1279 +
1280 def wildcard_present(path):
1281 m = re.search("[*#@%]", path)
1282 return m is not None
1283
1284 +
1285 class LargeFileSystem(object):
1286 """Base class for large file system support."""
1287
@@ -1272,6 +1363,7 @@ class LargeFileSystem(object):
1363 sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
1364 return (git_mode, contents)
1365
1366 +
1367 class MockLFS(LargeFileSystem):
1368 """Mock large file system for testing."""
1369
@@ -1295,6 +1387,7 @@ class MockLFS(LargeFileSystem):
1387 os.makedirs(remotePath)
1388 shutil.copyfile(localLargeFile, os.path.join(remotePath, os.path.basename(localLargeFile)))
1389
1390 +
1391 class GitLFS(LargeFileSystem):
1392 """Git LFS as backend for the git-p4 large file system.
1393 See https://git-lfs.github.com/ for details."""
@@ -1383,6 +1476,7 @@ class GitLFS(LargeFileSystem):
1476 else:
1477 return LargeFileSystem.processContent(self, git_mode, relPath, contents)
1478
1479 +
1480 class Command:
1481 delete_actions = ( "delete", "move/delete", "purge" )
1482 add_actions = ( "add", "branch", "move/add" )
@@ -1398,6 +1492,7 @@ class Command:
1492 setattr(self, attr, value)
1493 return getattr(self, attr)
1494
1495 +
1496 class P4UserMap:
1497 def __init__(self):
1498 self.userMapFromPerforceServer = False
@@ -1468,6 +1563,7 @@ class P4UserMap:
1563 except IOError:
1564 self.getUserMapFromPerforceServer()
1565
1566 +
1567 class P4Submit(Command, P4UserMap):
1568
1569 conflict_behavior_choices = ("ask", "skip", "quit")
@@ -2473,6 +2569,7 @@ class P4Submit(Command, P4UserMap):
2569
2570 return True
2571
2572 +
2573 class View(object):
2574 """Represent a p4 view ("p4 help views"), and map files in a
2575 repo according to the view."""
@@ -2580,11 +2677,13 @@ class View(object):
2677 die( "Error: %s is not found in client spec path" % depot_path )
2678 return ""
2679
2680 +
2681 def cloneExcludeCallback(option, opt_str, value, parser):
2682 # prepend "/" because the first "/" was consumed as part of the option itself.
2683 # ("-//depot/A/..." becomes "/depot/A/..." after option parsing)
2684 parser.values.cloneExclude += ["/" + re.sub(r"\.\.\.$", "", value)]
2685
2686 +
2687 class P4Sync(Command, P4UserMap):
2688
2689 def __init__(self):
@@ -3942,6 +4041,7 @@ class P4Sync(Command, P4UserMap):
4041
4042 return True
4043
4044 +
4045 class P4Rebase(Command):
4046 def __init__(self):
4047 Command.__init__(self)
@@ -3979,6 +4079,7 @@ class P4Rebase(Command):
4079 "HEAD", "--"])
4080 return True
4081
4082 +
4083 class P4Clone(P4Sync):
4084 def __init__(self):
4085 P4Sync.__init__(self)
@@ -4057,6 +4158,7 @@ class P4Clone(P4Sync):
4158
4159 return True
4160
4161 +
4162 class P4Unshelve(Command):
4163 def __init__(self):
4164 Command.__init__(self)
@@ -4172,6 +4274,7 @@ class P4Unshelve(Command):
4274
4275 return True
4276
4277 +
4278 class P4Branches(Command):
4279 def __init__(self):
4280 Command.__init__(self)
@@ -4197,6 +4300,7 @@ class P4Branches(Command):
4300 print("%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"]))
4301 return True
4302
4303 +
4304 class HelpFormatter(optparse.IndentedHelpFormatter):
4305 def __init__(self):
4306 optparse.IndentedHelpFormatter.__init__(self)
@@ -4207,6 +4311,7 @@ class HelpFormatter(optparse.IndentedHelpFormatter):
4311 else:
4312 return ""
4313
4314 +
4315 def printUsage(commands):
4316 print("usage: %s <command> [options]" % sys.argv[0])
4317 print("")
@@ -4215,6 +4320,7 @@ def printUsage(commands):
4320 print("Try %s <command> --help for command specific help." % sys.argv[0])
4321 print("")
4322
4323 +
4324 commands = {
4325 "submit" : P4Submit,
4326 "commit" : P4Submit,
@@ -4225,6 +4331,7 @@ commands = {
4331 "unshelve" : P4Unshelve,
4332 }
4333
4334 +
4335 def main():
4336 if len(sys.argv[1:]) == 0:
4337 printUsage(commands.keys())