p4cmd 2.6.0__tar.gz → 2.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {p4cmd-2.6.0 → p4cmd-2.8.0}/PKG-INFO +1 -1
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/__init__.py +1 -1
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/p4cmd.py +120 -2
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd.egg-info/PKG-INFO +1 -1
- {p4cmd-2.6.0 → p4cmd-2.8.0}/LICENSE +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/README.md +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/p4errors.py +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/p4file.py +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/__init__.py +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_DEPOT_ONLY.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_MOVED.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_NEED_SYNC.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_OPEN_FOR_ADD.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_OPEN_FOR_DELETE.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_OPEN_FOR_EDIT.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_UNKNOWN.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_UNTRACKED.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/resources/perforce_UP_TO_DATE.png +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd/utils.py +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd.egg-info/SOURCES.txt +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd.egg-info/dependency_links.txt +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/p4cmd.egg-info/top_level.txt +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/setup.cfg +0 -0
- {p4cmd-2.6.0 → p4cmd-2.8.0}/setup.py +0 -0
|
@@ -306,7 +306,73 @@ class P4Client(object):
|
|
|
306
306
|
if len(changelists):
|
|
307
307
|
return True
|
|
308
308
|
return False
|
|
309
|
+
|
|
310
|
+
def update_changelist_description(self, changelist, new_description):
|
|
311
|
+
"""
|
|
312
|
+
Updates the description of an existing changelist.
|
|
309
313
|
|
|
314
|
+
:param changelist: *string* or *int* changelist number
|
|
315
|
+
:param new_description: *string* new description for the changelist
|
|
316
|
+
:return: *bool* True if successful, False otherwise
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
# ... not super elegant, but it works
|
|
320
|
+
try:
|
|
321
|
+
changelist = self.__ensure_changelist(changelist)
|
|
322
|
+
|
|
323
|
+
# do some formatting so that we have the proper indentation
|
|
324
|
+
formatted_description = []
|
|
325
|
+
for line in new_description.split('\n'):
|
|
326
|
+
formatted_description.append(f"\t{line}")
|
|
327
|
+
formatted_description = '\n'.join(formatted_description)
|
|
328
|
+
|
|
329
|
+
output = subprocess.check_output(f'p4 change -o {changelist}', shell=True).decode()
|
|
330
|
+
|
|
331
|
+
# Split the output into sections based on field names
|
|
332
|
+
sections = []
|
|
333
|
+
current_section = []
|
|
334
|
+
current_field = None
|
|
335
|
+
|
|
336
|
+
# take the output and start parsing it
|
|
337
|
+
for line in output.split('\n'):
|
|
338
|
+
# check if it's a new field
|
|
339
|
+
if line.strip() and ':' in line and not line.startswith('\t'):
|
|
340
|
+
# if it is, save the previous section
|
|
341
|
+
if current_field:
|
|
342
|
+
sections.append((current_field, current_section))
|
|
343
|
+
# now start a new section
|
|
344
|
+
current_field = line.split(':', 1)[0].strip()
|
|
345
|
+
current_section = [line]
|
|
346
|
+
else:
|
|
347
|
+
# if it's not a new field, add to the current section
|
|
348
|
+
current_section.append(line)
|
|
349
|
+
|
|
350
|
+
# add the last section
|
|
351
|
+
if current_field:
|
|
352
|
+
sections.append((current_field, current_section))
|
|
353
|
+
|
|
354
|
+
# new remake the Description field with the update description
|
|
355
|
+
new_output = []
|
|
356
|
+
for field, section_lines in sections:
|
|
357
|
+
if field == "Description":
|
|
358
|
+
# replace the description section
|
|
359
|
+
new_output.append(section_lines[0]) # keep the "Description:" line because perforce needs this
|
|
360
|
+
new_output.append(formatted_description)
|
|
361
|
+
else:
|
|
362
|
+
# keep everything else as-is
|
|
363
|
+
new_output.extend(section_lines)
|
|
364
|
+
|
|
365
|
+
updated_output = '\n'.join(new_output)
|
|
366
|
+
|
|
367
|
+
# TODO: modify run_cmd to allow input so that commands like this can also run through it.
|
|
368
|
+
result = subprocess.check_output(f'p4 change -i', input=updated_output.encode(), shell=True).decode()
|
|
369
|
+
|
|
370
|
+
return True
|
|
371
|
+
except Exception as e:
|
|
372
|
+
if not self.silent:
|
|
373
|
+
logging.error(f"Failed to update changelist description: {e}")
|
|
374
|
+
return False
|
|
375
|
+
|
|
310
376
|
@validate_not_empty
|
|
311
377
|
def move_files_to_changelist(self, file_list, changelist="default"):
|
|
312
378
|
"""
|
|
@@ -563,16 +629,26 @@ class P4Client(object):
|
|
|
563
629
|
info_dicts = self.run_cmd("delete", args=["-c", changelist], file_list=file_list)
|
|
564
630
|
return info_dicts
|
|
565
631
|
|
|
566
|
-
def delete_changelist(self, changelist
|
|
632
|
+
def delete_changelist(self, changelist, perfect_match_only=False, case_sensitive=False, delete_shelved_files=False, obliterate=False):
|
|
567
633
|
"""
|
|
568
|
-
Deletes a changelist via changelist number or description
|
|
634
|
+
Deletes a changelist via changelist number or description. Will fail if any files are checked out or shelved files exist.
|
|
569
635
|
:param changelist: *string* or *int* change list number
|
|
570
636
|
:param perfect_match_only: bool* only delete if there's a perfect match of the changelist description
|
|
571
637
|
:param case_sensitive: *bool*
|
|
638
|
+
:param delete_shelved_files: *bool* If True, will delete all shelved files and then delete the CL.
|
|
639
|
+
:param obliterate: *bool* If True, will revert all files, deleted shelved files and then delete the CL
|
|
572
640
|
"""
|
|
573
641
|
info_dicts = []
|
|
574
642
|
cl_num = self.get_pending_changelists(changelist, perfect_match_only, case_sensitive)
|
|
575
643
|
for cl in cl_num:
|
|
644
|
+
# revert all files, then make sure shelved files are also deleted
|
|
645
|
+
if obliterate:
|
|
646
|
+
info_dicts.append(self.revert_changelist(changelist=cl))
|
|
647
|
+
delete_shelved_files = True
|
|
648
|
+
|
|
649
|
+
if delete_shelved_files:
|
|
650
|
+
info_dicts.append(self.delete_shelf(cl))
|
|
651
|
+
|
|
576
652
|
info_dicts.append(self.run_cmd('change', args=['-d', cl]))
|
|
577
653
|
# TODO: Break down info dicts and look for errors
|
|
578
654
|
return info_dicts
|
|
@@ -707,6 +783,48 @@ class P4Client(object):
|
|
|
707
783
|
|
|
708
784
|
return info_dicts
|
|
709
785
|
|
|
786
|
+
def delete_shelf(self, changelist):
|
|
787
|
+
"""
|
|
788
|
+
Deletes all shelved files in a changelist without deleting the changelist itself.
|
|
789
|
+
|
|
790
|
+
:param changelist: *string* or *int* changelist number
|
|
791
|
+
:return: *list* of info dictionaries
|
|
792
|
+
"""
|
|
793
|
+
try:
|
|
794
|
+
# Ensure the changelist exists and is a number
|
|
795
|
+
changelist = self.__ensure_changelist(changelist)
|
|
796
|
+
|
|
797
|
+
# Don't allow deleting shelves from the default changelist
|
|
798
|
+
if changelist == "default":
|
|
799
|
+
if not self.silent:
|
|
800
|
+
logging.error("Cannot delete shelved files from the default changelist")
|
|
801
|
+
return [{"code": "error", "data": "Cannot delete shelved files from the default changelist"}]
|
|
802
|
+
|
|
803
|
+
# Check if the changelist has shelved files
|
|
804
|
+
has_shelved_files = False
|
|
805
|
+
info_dicts = self.run_cmd("describe", args=["-S", str(changelist)])
|
|
806
|
+
for info_dict in info_dicts:
|
|
807
|
+
for key in info_dict.keys():
|
|
808
|
+
if b"depotFile" in key:
|
|
809
|
+
has_shelved_files = True
|
|
810
|
+
break
|
|
811
|
+
if has_shelved_files:
|
|
812
|
+
break
|
|
813
|
+
|
|
814
|
+
if not has_shelved_files:
|
|
815
|
+
if not self.silent:
|
|
816
|
+
logging.warning(f"No shelved files found in changelist {changelist}")
|
|
817
|
+
return [{"code": "info", "data": f"No shelved files found in changelist {changelist}"}]
|
|
818
|
+
|
|
819
|
+
# Delete all shelved files in the changelist
|
|
820
|
+
info_dicts = self.run_cmd("shelve", args=["-d", "-c", str(changelist)])
|
|
821
|
+
return info_dicts
|
|
822
|
+
|
|
823
|
+
except Exception as e:
|
|
824
|
+
if not self.silent:
|
|
825
|
+
logging.error(f"Failed to delete shelved files: {e}")
|
|
826
|
+
return [{"code": "error", "data": str(e)}]
|
|
827
|
+
|
|
710
828
|
@validate_not_empty
|
|
711
829
|
def add_or_edit_folders(self, folders, include_subfolders=True, changelist="default"):
|
|
712
830
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|