pyjallib 0.1.13__py3-none-any.whl → 0.1.15__py3-none-any.whl
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.
- pyjallib/__init__.py +1 -1
- pyjallib/max/__init__.py +4 -0
- pyjallib/max/header.py +2 -0
- pyjallib/max/progress.py +40 -0
- pyjallib/max/skeleton.py +192 -0
- pyjallib/perforce.py +137 -4
- {pyjallib-0.1.13.dist-info → pyjallib-0.1.15.dist-info}/METADATA +1 -1
- {pyjallib-0.1.13.dist-info → pyjallib-0.1.15.dist-info}/RECORD +9 -7
- {pyjallib-0.1.13.dist-info → pyjallib-0.1.15.dist-info}/WHEEL +0 -0
pyjallib/__init__.py
CHANGED
pyjallib/max/__init__.py
CHANGED
@@ -24,6 +24,7 @@ from pyjallib.max.link import Link
|
|
24
24
|
|
25
25
|
from pyjallib.max.bip import Bip
|
26
26
|
from pyjallib.max.skin import Skin
|
27
|
+
from pyjallib.max.skeleton import Skeleton
|
27
28
|
from pyjallib.max.morph import Morph
|
28
29
|
|
29
30
|
from pyjallib.max.boneChain import BoneChain
|
@@ -39,6 +40,7 @@ from pyjallib.max.rootMotion import RootMotion
|
|
39
40
|
|
40
41
|
from pyjallib.max.fbxHandler import FBXHandler
|
41
42
|
from pyjallib.max.toolManager import ToolManager
|
43
|
+
from pyjallib.max.progress import Progress
|
42
44
|
|
43
45
|
from pyjallib.max.ui.Container import Container
|
44
46
|
|
@@ -57,6 +59,7 @@ __all__ = [
|
|
57
59
|
'Link',
|
58
60
|
'Bip',
|
59
61
|
'Skin',
|
62
|
+
'Skeleton',
|
60
63
|
'Morph',
|
61
64
|
'BoneChain',
|
62
65
|
'TwistBone',
|
@@ -68,5 +71,6 @@ __all__ = [
|
|
68
71
|
'RootMotion',
|
69
72
|
'FBXHandler',
|
70
73
|
'ToolManager',
|
74
|
+
'Progress',
|
71
75
|
'Container'
|
72
76
|
]
|
pyjallib/max/header.py
CHANGED
@@ -23,6 +23,7 @@ from .link import Link
|
|
23
23
|
|
24
24
|
from .bip import Bip
|
25
25
|
from .skin import Skin
|
26
|
+
from .skeleton import Skeleton
|
26
27
|
|
27
28
|
from .twistBone import TwistBone
|
28
29
|
from .autoClavicle import AutoClavicle
|
@@ -74,6 +75,7 @@ class Header:
|
|
74
75
|
|
75
76
|
self.bip = Bip(animService=self.anim, nameService=self.name, boneService=self.bone)
|
76
77
|
self.skin = Skin()
|
78
|
+
self.skeleton = Skeleton(animService=self.anim, nameService=self.name, boneService=self.bone, bipService=self.bip, layerService=self.layer)
|
77
79
|
|
78
80
|
self.twistBone = TwistBone(nameService=self.name, animService=self.anim, constraintService=self.constraint, bipService=self.bip, boneService=self.bone)
|
79
81
|
self.groinBone = GroinBone(nameService=self.name, animService=self.anim, constraintService=self.constraint, boneService=self.bone, helperService=self.helper)
|
pyjallib/max/progress.py
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
Progress 모듈 - 3ds Max 작업 진행 상황 표시 관련 기능 제공
|
6
|
+
"""
|
7
|
+
|
8
|
+
from pymxs import runtime as rt
|
9
|
+
|
10
|
+
class Progress:
|
11
|
+
"""
|
12
|
+
3ds Max 작업 진행 상황 표시 관련 기능을 제공하는 클래스.
|
13
|
+
"""
|
14
|
+
def __init__(self, inTaskName: str, inTotalSteps: int = 0):
|
15
|
+
"""
|
16
|
+
클래스 초기화
|
17
|
+
"""
|
18
|
+
self.taskName = inTaskName
|
19
|
+
self.currentStep = 0
|
20
|
+
self.totalSteps = inTotalSteps
|
21
|
+
self.currentPercent = 0
|
22
|
+
|
23
|
+
def update(self, inCurrentStep: int) -> int:
|
24
|
+
"""
|
25
|
+
현재 진행율(%)을 반환합니다.
|
26
|
+
"""
|
27
|
+
if self.totalSteps == 0:
|
28
|
+
self.currentStep = inCurrentStep % 100
|
29
|
+
self.currentPercent = int(self.currentStep)
|
30
|
+
else:
|
31
|
+
self.currentStep = inCurrentStep
|
32
|
+
self.currentPercent = int((self.currentStep / self.totalSteps) * 100)
|
33
|
+
return self.currentPercent
|
34
|
+
|
35
|
+
def reset(self):
|
36
|
+
"""
|
37
|
+
진행 상태를 초기화합니다.
|
38
|
+
"""
|
39
|
+
self.currentStep = 0
|
40
|
+
self.currentPercent = 0
|
pyjallib/max/skeleton.py
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
스켈레톤 모듈 - 3ds Max용 스켈레톤 관련 기능 제공
|
6
|
+
원본 MAXScript의 skeleton.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현됨
|
7
|
+
"""
|
8
|
+
|
9
|
+
from pymxs import runtime as rt
|
10
|
+
|
11
|
+
# Import necessary service classes for default initialization
|
12
|
+
from .anim import Anim
|
13
|
+
from .name import Name
|
14
|
+
from .bone import Bone
|
15
|
+
from .bip import Bip
|
16
|
+
from .layer import Layer
|
17
|
+
|
18
|
+
from .progress import Progress
|
19
|
+
|
20
|
+
|
21
|
+
class Skeleton:
|
22
|
+
"""
|
23
|
+
스켈레톤 관련 기능을 제공하는 클래스.
|
24
|
+
MAXScript의 _Skeleton 구조체 개념을 Python으로 재구현한 클래스이며,
|
25
|
+
3ds Max의 기능들을 pymxs API를 통해 제어합니다.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, animService=None, nameService=None, boneService=None, bipService=None, layerService=None):
|
29
|
+
"""
|
30
|
+
클래스 초기화
|
31
|
+
|
32
|
+
Args:
|
33
|
+
animService: Anim 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
34
|
+
nameService: Name 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
35
|
+
boneService: Bone 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
36
|
+
bipService: Bip 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
37
|
+
layerService: Layer 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
38
|
+
"""
|
39
|
+
self.anim = animService if animService else Anim()
|
40
|
+
self.name = nameService if nameService else Name()
|
41
|
+
self.bone = boneService if boneService else Bone(nameService=self.name, animService=self.anim)
|
42
|
+
self.bip = bipService if bipService else Bip(animService=self.anim, nameService=self.name, boneService=self.bone)
|
43
|
+
self.layer = layerService if layerService else Layer()
|
44
|
+
|
45
|
+
# def get_dependencies(self, inObj):
|
46
|
+
# """
|
47
|
+
# 객체의 의존성을 가져옴
|
48
|
+
|
49
|
+
# Args:
|
50
|
+
# inObj: 의존성을 확인할 객체 또는 객체 배열
|
51
|
+
|
52
|
+
# Returns:
|
53
|
+
# 의존성 노드 배열
|
54
|
+
# """
|
55
|
+
# # core - 단일 객체인 경우 배열로 변환
|
56
|
+
# if rt.classOf(inObj) != rt.Array:
|
57
|
+
# inObj = [inObj]
|
58
|
+
# else:
|
59
|
+
# # rt.Array를 Python 리스트로 변환
|
60
|
+
# inObj = list(inObj)
|
61
|
+
|
62
|
+
# nodeArray = []
|
63
|
+
# res = inObj.copy()
|
64
|
+
|
65
|
+
# for each in inObj:
|
66
|
+
# # Biped 객체인 경우 건너뛰기
|
67
|
+
# if self.bip.is_biped_object(each):
|
68
|
+
# continue
|
69
|
+
|
70
|
+
# # 컨트롤러의 의존성 추가
|
71
|
+
# controller_deps = rt.refs.dependson(each.controller)
|
72
|
+
# if controller_deps:
|
73
|
+
# res.extend(controller_deps)
|
74
|
+
|
75
|
+
# # Skin 속성이 있는 경우 Skin의 의존성 추가
|
76
|
+
# if rt.isProperty(each, rt.name("Skin")):
|
77
|
+
# skin_deps = rt.refs.dependson(each.skin)
|
78
|
+
# if skin_deps:
|
79
|
+
# res.extend(skin_deps)
|
80
|
+
|
81
|
+
# # 의존성 배열을 순회하면서 추가 의존성 확인
|
82
|
+
# i = 0
|
83
|
+
# while i < len(res):
|
84
|
+
# # 노드가 아닌 경우 추가 의존성 확인
|
85
|
+
# if rt.superClassOf(res[i]) != rt.node:
|
86
|
+
# additional_deps = rt.refs.dependson(res[i])
|
87
|
+
# if additional_deps:
|
88
|
+
# res.extend(additional_deps)
|
89
|
+
# # 유효한 노드인 경우
|
90
|
+
# if rt.isValidNode(res[i]):
|
91
|
+
# # 노드 배열에 추가
|
92
|
+
# if res[i] not in nodeArray:
|
93
|
+
# nodeArray.append(res[i])
|
94
|
+
|
95
|
+
# # 부모 노드 확인 및 추가
|
96
|
+
# parentNode = res[i].parent
|
97
|
+
# if rt.isValidNode(parentNode) and parentNode not in nodeArray:
|
98
|
+
# res.append(parentNode)
|
99
|
+
# nodeArray.append(parentNode)
|
100
|
+
|
101
|
+
# i += 1
|
102
|
+
|
103
|
+
# return nodeArray
|
104
|
+
|
105
|
+
def get_dependencies(self, inObjs):
|
106
|
+
targetObjs = rt.Array()
|
107
|
+
for item in inObjs:
|
108
|
+
if rt.isValidNode(item):
|
109
|
+
rt.append(targetObjs, item)
|
110
|
+
|
111
|
+
maxcriptCode = ""
|
112
|
+
maxcriptCode += "fn pyjallib_max_skeleton_get_dependencies obj = \n"
|
113
|
+
maxcriptCode += "(\n"
|
114
|
+
maxcriptCode += " node_array = #()\n"
|
115
|
+
maxcriptCode += " res = deepCopy obj\n"
|
116
|
+
maxcriptCode += " \n"
|
117
|
+
maxcriptCode += " for each in obj do \n"
|
118
|
+
maxcriptCode += " (\n"
|
119
|
+
maxcriptCode += " isBipedObj = (classOf each.controller == BipSlave_control) or (classOf each.controller == Footsteps) or (classOf each.controller == Vertical_Horizontal_Turn)\n"
|
120
|
+
maxcriptCode += " if isBipedObj then continue\n"
|
121
|
+
maxcriptCode += "\n"
|
122
|
+
maxcriptCode += " join res (refs.dependson each.controller)\n"
|
123
|
+
maxcriptCode += "\n"
|
124
|
+
maxcriptCode += " if isproperty each #skin do ( join res (refs.dependson each.skin) )\n"
|
125
|
+
maxcriptCode += "\n"
|
126
|
+
maxcriptCode += " i = 0\n"
|
127
|
+
maxcriptCode += " while i < res.count do \n"
|
128
|
+
maxcriptCode += " (\n"
|
129
|
+
maxcriptCode += " i += 1\n"
|
130
|
+
maxcriptCode += " if classof (superclassof res[i]) != node then \n"
|
131
|
+
maxcriptCode += " join res (refs.dependson res[i])\n"
|
132
|
+
maxcriptCode += "\n"
|
133
|
+
maxcriptCode += " else if isvalidnode res[i] do\n"
|
134
|
+
maxcriptCode += " (\n"
|
135
|
+
maxcriptCode += " appendifunique node_array res[i]\n"
|
136
|
+
maxcriptCode += "\n"
|
137
|
+
maxcriptCode += " parent_node=res[i].parent\n"
|
138
|
+
maxcriptCode += " if isValidNode parent_node and findItem node_array parent_node == 0 do\n"
|
139
|
+
maxcriptCode += " ( \n"
|
140
|
+
maxcriptCode += " appendIfUnique res parent_node\n"
|
141
|
+
maxcriptCode += " appendIfUnique node_array parent_node\n"
|
142
|
+
maxcriptCode += " )\n"
|
143
|
+
maxcriptCode += " )\n"
|
144
|
+
maxcriptCode += " )\n"
|
145
|
+
maxcriptCode += " )\n"
|
146
|
+
maxcriptCode += " \n"
|
147
|
+
maxcriptCode += " return node_array\n"
|
148
|
+
maxcriptCode += ")\n"
|
149
|
+
|
150
|
+
rt.execute(maxcriptCode)
|
151
|
+
return rt.pyjallib_max_skeleton_get_dependencies(targetObjs)
|
152
|
+
|
153
|
+
def get_all_dependencies(self, inObjs, inAddonLayerName="Rig_Addon"):
|
154
|
+
"""
|
155
|
+
객체의 모든 의존성을 가져옴 (애드온 레이어 포함)
|
156
|
+
|
157
|
+
Args:
|
158
|
+
inObjs: 의존성을 확인할 객체 배열
|
159
|
+
inAddonLayerName: 애드온 레이어 이름 (기본값: "Rig_Addon")
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
모든 의존성 노드 배열 (중복 제거됨)
|
163
|
+
"""
|
164
|
+
returnArray = []
|
165
|
+
nodeArray = self.get_dependencies(inObjs)
|
166
|
+
|
167
|
+
|
168
|
+
# 애드온 레이어의 노드들만 필터링
|
169
|
+
addOnArray = []
|
170
|
+
for item in nodeArray:
|
171
|
+
if item.layer.name == inAddonLayerName:
|
172
|
+
addOnArray.append(item)
|
173
|
+
|
174
|
+
# 애드온 노드들의 의존성 가져오기
|
175
|
+
addOnRefArray = []
|
176
|
+
progress = Progress("Get All Dependencies", inTotalSteps=len(addOnArray))
|
177
|
+
for i,item in enumerate(addOnArray):
|
178
|
+
refs = self.get_dependencies(item)
|
179
|
+
progress.update(i)
|
180
|
+
|
181
|
+
addOnRefArray.extend(refs)
|
182
|
+
|
183
|
+
# 모든 노드들을 returnArray에 추가 (중복 없이)
|
184
|
+
for item in nodeArray:
|
185
|
+
if item not in returnArray:
|
186
|
+
returnArray.append(item)
|
187
|
+
|
188
|
+
for item in addOnRefArray:
|
189
|
+
if item not in returnArray:
|
190
|
+
returnArray.append(item)
|
191
|
+
|
192
|
+
return returnArray
|
pyjallib/perforce.py
CHANGED
@@ -16,10 +16,14 @@ from pathlib import Path
|
|
16
16
|
|
17
17
|
# 로깅 설정
|
18
18
|
logger = logging.getLogger(__name__)
|
19
|
-
|
19
|
+
|
20
|
+
# 기본 로그 레벨은 ERROR로 설정 (디버그 모드는 생성자에서 설정)
|
21
|
+
logger.setLevel(logging.ERROR)
|
22
|
+
|
20
23
|
# 사용자 문서 폴더 내 로그 파일 저장
|
21
24
|
log_path = os.path.join(Path.home() / "Documents", 'Perforce.log')
|
22
25
|
file_handler = logging.FileHandler(log_path, encoding='utf-8')
|
26
|
+
file_handler.setLevel(logging.ERROR) # 기본적으로 ERROR 레벨만 기록
|
23
27
|
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
|
24
28
|
logger.addHandler(file_handler)
|
25
29
|
|
@@ -27,8 +31,19 @@ logger.addHandler(file_handler)
|
|
27
31
|
class Perforce:
|
28
32
|
"""P4Python을 사용하여 Perforce 작업을 수행하는 클래스."""
|
29
33
|
|
30
|
-
def __init__(self):
|
31
|
-
"""Perforce 인스턴스를 초기화합니다.
|
34
|
+
def __init__(self, debug_mode: bool = False):
|
35
|
+
"""Perforce 인스턴스를 초기화합니다.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
debug_mode (bool): True로 설정하면 DEBUG 레벨 로그를 활성화합니다.
|
39
|
+
기본값은 False (ERROR 레벨만 기록)
|
40
|
+
"""
|
41
|
+
# 디버그 모드에 따라 로그 레벨 설정
|
42
|
+
if debug_mode:
|
43
|
+
logger.setLevel(logging.DEBUG)
|
44
|
+
file_handler.setLevel(logging.DEBUG)
|
45
|
+
logger.debug("디버그 모드가 활성화되었습니다.")
|
46
|
+
|
32
47
|
self.p4 = P4()
|
33
48
|
self.connected = False
|
34
49
|
self.workspaceRoot = r""
|
@@ -575,11 +590,13 @@ class Perforce:
|
|
575
590
|
|
576
591
|
return all_success
|
577
592
|
|
578
|
-
def submit_change_list(self, change_list_number: int) -> bool:
|
593
|
+
def submit_change_list(self, change_list_number: int, auto_revert_unchanged: bool = True) -> bool:
|
579
594
|
"""체인지 리스트를 제출합니다.
|
580
595
|
|
581
596
|
Args:
|
582
597
|
change_list_number (int): 제출할 체인지 리스트 번호
|
598
|
+
auto_revert_unchanged (bool, optional): 제출 후 변경사항이 없는 체크아웃된 파일들을
|
599
|
+
자동으로 리버트할지 여부. 기본값 True
|
583
600
|
|
584
601
|
Returns:
|
585
602
|
bool: 제출 성공 시 True, 실패 시 False
|
@@ -590,6 +607,11 @@ class Perforce:
|
|
590
607
|
try:
|
591
608
|
self.p4.run_submit("-c", change_list_number)
|
592
609
|
logger.info(f"체인지 리스트 {change_list_number} 제출 성공.")
|
610
|
+
|
611
|
+
# 제출 후 변경사항이 없는 체크아웃된 파일들을 자동으로 리버트
|
612
|
+
if auto_revert_unchanged:
|
613
|
+
self._auto_revert_unchanged_files(change_list_number)
|
614
|
+
|
593
615
|
return True
|
594
616
|
except P4Exception as e:
|
595
617
|
self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 제출")
|
@@ -597,6 +619,117 @@ class Perforce:
|
|
597
619
|
logger.warning(f"체인지 리스트 {change_list_number}에 제출할 파일이 없습니다.")
|
598
620
|
return False
|
599
621
|
|
622
|
+
def _auto_revert_unchanged_files(self, change_list_number: int) -> None:
|
623
|
+
"""제출 후 변경사항이 없는 체크아웃된 파일들을 자동으로 리버트합니다.
|
624
|
+
|
625
|
+
Args:
|
626
|
+
change_list_number (int): 체인지 리스트 번호
|
627
|
+
"""
|
628
|
+
logger.debug(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일들 자동 리버트 시도...")
|
629
|
+
try:
|
630
|
+
# 체인지 리스트에서 체크아웃된 파일들 가져오기
|
631
|
+
opened_files = self.p4.run_opened("-c", change_list_number)
|
632
|
+
|
633
|
+
if not opened_files:
|
634
|
+
logger.debug(f"체인지 리스트 {change_list_number}에 체크아웃된 파일이 없습니다.")
|
635
|
+
return
|
636
|
+
|
637
|
+
unchanged_files = []
|
638
|
+
for file_info in opened_files:
|
639
|
+
file_path = file_info.get('clientFile', '')
|
640
|
+
action = file_info.get('action', '')
|
641
|
+
|
642
|
+
# edit 액션의 파일만 확인 (add, delete는 변경사항이 있음)
|
643
|
+
if action == 'edit':
|
644
|
+
try:
|
645
|
+
# p4 diff 명령으로 파일의 변경사항 확인
|
646
|
+
diff_result = self.p4.run_diff("-sa", file_path)
|
647
|
+
|
648
|
+
# diff 결과가 비어있으면 변경사항이 없음
|
649
|
+
if not diff_result:
|
650
|
+
unchanged_files.append(file_path)
|
651
|
+
logger.debug(f"파일 '{file_path}'에 변경사항이 없어 리버트 대상으로 추가")
|
652
|
+
else:
|
653
|
+
logger.debug(f"파일 '{file_path}'에 변경사항이 있어 리버트하지 않음")
|
654
|
+
|
655
|
+
except P4Exception as e:
|
656
|
+
# diff 명령 실패 시에도 리버트 대상으로 추가 (안전하게 처리)
|
657
|
+
unchanged_files.append(file_path)
|
658
|
+
logger.debug(f"파일 '{file_path}' diff 확인 실패, 리버트 대상으로 추가: {e}")
|
659
|
+
else:
|
660
|
+
logger.debug(f"파일 '{file_path}'는 {action} 액션이므로 리버트하지 않음")
|
661
|
+
|
662
|
+
# 변경사항이 없는 파일들을 리버트
|
663
|
+
if unchanged_files:
|
664
|
+
logger.info(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 시도...")
|
665
|
+
for file_path in unchanged_files:
|
666
|
+
try:
|
667
|
+
self.p4.run_revert("-c", change_list_number, file_path)
|
668
|
+
logger.info(f"파일 '{file_path}' 자동 리버트 완료")
|
669
|
+
except P4Exception as e:
|
670
|
+
self._handle_p4_exception(e, f"파일 '{file_path}' 자동 리버트")
|
671
|
+
logger.info(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 완료")
|
672
|
+
else:
|
673
|
+
logger.debug(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일이 없습니다.")
|
674
|
+
|
675
|
+
# default change list에서도 변경사항이 없는 파일들 처리
|
676
|
+
self._auto_revert_unchanged_files_in_default_changelist()
|
677
|
+
|
678
|
+
except P4Exception as e:
|
679
|
+
self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 자동 리버트 처리")
|
680
|
+
|
681
|
+
def _auto_revert_unchanged_files_in_default_changelist(self) -> None:
|
682
|
+
"""default change list에서 변경사항이 없는 체크아웃된 파일들을 자동으로 리버트합니다."""
|
683
|
+
logger.debug("default change list에서 변경사항이 없는 파일들 자동 리버트 시도...")
|
684
|
+
try:
|
685
|
+
# default change list에서 체크아웃된 파일들 가져오기
|
686
|
+
opened_files = self.p4.run_opened("-c", "default")
|
687
|
+
|
688
|
+
if not opened_files:
|
689
|
+
logger.debug("default change list에 체크아웃된 파일이 없습니다.")
|
690
|
+
return
|
691
|
+
|
692
|
+
unchanged_files = []
|
693
|
+
for file_info in opened_files:
|
694
|
+
file_path = file_info.get('clientFile', '')
|
695
|
+
action = file_info.get('action', '')
|
696
|
+
|
697
|
+
# edit 액션의 파일만 확인 (add, delete는 변경사항이 있음)
|
698
|
+
if action == 'edit':
|
699
|
+
try:
|
700
|
+
# p4 diff 명령으로 파일의 변경사항 확인
|
701
|
+
diff_result = self.p4.run_diff("-sa", file_path)
|
702
|
+
|
703
|
+
# diff 결과가 비어있으면 변경사항이 없음
|
704
|
+
if not diff_result:
|
705
|
+
unchanged_files.append(file_path)
|
706
|
+
logger.debug(f"default change list의 파일 '{file_path}'에 변경사항이 없어 리버트 대상으로 추가")
|
707
|
+
else:
|
708
|
+
logger.debug(f"default change list의 파일 '{file_path}'에 변경사항이 있어 리버트하지 않음")
|
709
|
+
|
710
|
+
except P4Exception as e:
|
711
|
+
# diff 명령 실패 시에도 리버트 대상으로 추가 (안전하게 처리)
|
712
|
+
unchanged_files.append(file_path)
|
713
|
+
logger.debug(f"default change list의 파일 '{file_path}' diff 확인 실패, 리버트 대상으로 추가: {e}")
|
714
|
+
else:
|
715
|
+
logger.debug(f"default change list의 파일 '{file_path}'는 {action} 액션이므로 리버트하지 않음")
|
716
|
+
|
717
|
+
# 변경사항이 없는 파일들을 리버트
|
718
|
+
if unchanged_files:
|
719
|
+
logger.info(f"default change list에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 시도...")
|
720
|
+
for file_path in unchanged_files:
|
721
|
+
try:
|
722
|
+
self.p4.run_revert(file_path)
|
723
|
+
logger.info(f"default change list의 파일 '{file_path}' 자동 리버트 완료")
|
724
|
+
except P4Exception as e:
|
725
|
+
self._handle_p4_exception(e, f"default change list의 파일 '{file_path}' 자동 리버트")
|
726
|
+
logger.info(f"default change list에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 완료")
|
727
|
+
else:
|
728
|
+
logger.debug("default change list에서 변경사항이 없는 파일이 없습니다.")
|
729
|
+
|
730
|
+
except P4Exception as e:
|
731
|
+
self._handle_p4_exception(e, "default change list 자동 리버트 처리")
|
732
|
+
|
600
733
|
def revert_change_list(self, change_list_number: int) -> bool:
|
601
734
|
"""체인지 리스트를 되돌리고 삭제합니다.
|
602
735
|
|
@@ -1,13 +1,13 @@
|
|
1
|
-
pyjallib/__init__.py,sha256=
|
1
|
+
pyjallib/__init__.py,sha256=jf7VzQoweVj0AiRCD4CeHLUjlvrcoxyKp5rNDL22Drc,509
|
2
2
|
pyjallib/namePart.py,sha256=lKIiOVkWrtAW-D3nuv--vHmdAnlQeVPaXLYUDhcr8QU,24177
|
3
3
|
pyjallib/nameToPath.py,sha256=aBeezepLYdpv3VYxnQ2c4ZWzz2WjticXjkdbAIlVa1k,4676
|
4
4
|
pyjallib/naming.py,sha256=b2C-P9VWV4Q2StqkizEwABblYOC5g6sXHzN0KpOZ_Ys,37419
|
5
5
|
pyjallib/namingConfig.py,sha256=QGpK5mCnRiclKqNKz3GJ2PeJO8fbVitAEdqWwnwo8oA,34127
|
6
|
-
pyjallib/perforce.py,sha256=
|
6
|
+
pyjallib/perforce.py,sha256=pkCDaso4TAs-au0E8X1JwXPJ-TVbO2Fjz4k_Wh3z8bc,56078
|
7
7
|
pyjallib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
pyjallib/reloadModules.py,sha256=RAEG3IxzJ0TlsjvnZwJt56JOkc2j8voqAnRbfQuZ44g,1151
|
9
9
|
pyjallib/ConfigFiles/namingConfig.json,sha256=Ov4bbVJb6qodPaooU63e11YUMGXXPWFAA4AQq1sLBYU,1486
|
10
|
-
pyjallib/max/__init__.py,sha256=
|
10
|
+
pyjallib/max/__init__.py,sha256=D5yORjy_n4b2MPq6suG3Wj7oq7hROr8jWUoUu8JSCtU,1771
|
11
11
|
pyjallib/max/align.py,sha256=HKjCViQCuicGmtvHB6xxVv4BEGEBGtV2gO3NvR_6R2A,5183
|
12
12
|
pyjallib/max/anim.py,sha256=scfxLSSXfFAlxgFi_qePrbONYc4mpKB8mXIxtYtFtl0,29032
|
13
13
|
pyjallib/max/autoClavicle.py,sha256=m5rs313-qkaHfs9TUr98HYRVFdB25E_fW-YiY4o9zhE,9559
|
@@ -17,7 +17,7 @@ pyjallib/max/boneChain.py,sha256=qTvbnJVuBchOdOBhi8wPxPClQ-5Wbkhc7Y2H47nUjCc,574
|
|
17
17
|
pyjallib/max/constraint.py,sha256=93g-X0aZHtZMKXVKr8xMjIhbKou61yc2b3ubQKJquBs,40589
|
18
18
|
pyjallib/max/fbxHandler.py,sha256=rVcnxZh5_Cu012wKIFgiGO_XvZF07Oy3jezxUhIpmSo,8703
|
19
19
|
pyjallib/max/groinBone.py,sha256=yBexwDTrnXViP8DRACIMnnpJWErbn9RIA61_d3iADCc,9193
|
20
|
-
pyjallib/max/header.py,sha256=
|
20
|
+
pyjallib/max/header.py,sha256=qqiaSLACsa0nv15cKiqwhgyHrPYI4MYHFzFZpfzY5pw,4380
|
21
21
|
pyjallib/max/helper.py,sha256=Na3jFRwLsjHh4rz0Tk_r_CwHQxOA6n8LhDRA9x5xcSk,18018
|
22
22
|
pyjallib/max/hip.py,sha256=RavoZgK7zP2sXDa4A8CXEbHB6g8MQ-604XryZbStx0E,12684
|
23
23
|
pyjallib/max/kneeBone.py,sha256=cs5bCZtHxgLf6u80er1rV_PBF_SizqRgcTjYmy1q3IM,25316
|
@@ -27,8 +27,10 @@ pyjallib/max/mirror.py,sha256=TcbfZXSk-VJQstNqAmD6VGCqYBF9bMuJtFTg-6SiGdQ,14505
|
|
27
27
|
pyjallib/max/mocap.py,sha256=tD0yhan8GBYLBZtdpGBlYC-DzdQkT4gsbqeo5lpyQXU,12544
|
28
28
|
pyjallib/max/morph.py,sha256=I8HRYx4NznL6GZL4CbT9iTv05SeaBW_mJJ4PzMxCBkw,12664
|
29
29
|
pyjallib/max/name.py,sha256=DcJt2td-N7vfUGyWazdGTD4-0JW-noa7z5nwc6SHm6I,15337
|
30
|
+
pyjallib/max/progress.py,sha256=0_ry9NiYJZGqwBtvR-oGwV40Z57AtOBG0RcuQwwOeq4,1162
|
30
31
|
pyjallib/max/rootMotion.py,sha256=H9kvh9dx4zLLBYuigGyN0XZsdGAF5d7YRBdaokZmCiw,31399
|
31
32
|
pyjallib/max/select.py,sha256=HMJD2WNX3zVBEeYrj0UX2YXM3fHNItfw6UtQSItNsoU,9487
|
33
|
+
pyjallib/max/skeleton.py,sha256=4U4N2jA0GX4HqX1UOuUM1Yp3eud5ghqSRIO7FV-6poA,8138
|
32
34
|
pyjallib/max/skin.py,sha256=5mBzG2wSUxoGlkFeb9Ys8uUxOwuZRGeqUMTI9LiWWZU,41937
|
33
35
|
pyjallib/max/toolManager.py,sha256=ya6beAGzk1_culw4H7lBdr7klnS-Yl8mVGg13A41Q8A,3185
|
34
36
|
pyjallib/max/twistBone.py,sha256=VPZLDE5V6KEW-i5gAberggEeR2YRzLwsFfFQBT4AB1U,17295
|
@@ -42,6 +44,6 @@ pyjallib/max/macro/jal_macro_helper.py,sha256=hd8e5x56aq7Qt0g-hP5bY0p-njVy8ja77_
|
|
42
44
|
pyjallib/max/macro/jal_macro_link.py,sha256=E8i3z2xsrQiGDEz4Qoxc75hkpalzS95mOMcIic0J-Fc,1193
|
43
45
|
pyjallib/max/macro/jal_macro_select.py,sha256=jeSFR_mqqudTTrZE1rU6qifJ4g441cYxXWcHPTWh1CU,2289
|
44
46
|
pyjallib/max/ui/Container.py,sha256=QSk3oCqhfiR4aglSSkherRGAyPFqMRUt83L-0ENBz-s,5571
|
45
|
-
pyjallib-0.1.
|
46
|
-
pyjallib-0.1.
|
47
|
-
pyjallib-0.1.
|
47
|
+
pyjallib-0.1.15.dist-info/METADATA,sha256=syGZwRd9A7gdeiOYxG9QpXz-o7qkMZhrRsvwEaEyCYo,870
|
48
|
+
pyjallib-0.1.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
49
|
+
pyjallib-0.1.15.dist-info/RECORD,,
|
File without changes
|