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())