unityflow 0.3.4__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.
- unityflow/__init__.py +167 -0
- unityflow/asset_resolver.py +636 -0
- unityflow/asset_tracker.py +1687 -0
- unityflow/cli.py +2317 -0
- unityflow/data/__init__.py +1 -0
- unityflow/data/class_ids.json +336 -0
- unityflow/diff.py +234 -0
- unityflow/fast_parser.py +676 -0
- unityflow/formats.py +1558 -0
- unityflow/git_utils.py +307 -0
- unityflow/hierarchy.py +1672 -0
- unityflow/merge.py +226 -0
- unityflow/meta_generator.py +1291 -0
- unityflow/normalizer.py +529 -0
- unityflow/parser.py +698 -0
- unityflow/query.py +406 -0
- unityflow/script_parser.py +717 -0
- unityflow/sprite.py +378 -0
- unityflow/validator.py +783 -0
- unityflow-0.3.4.dist-info/METADATA +293 -0
- unityflow-0.3.4.dist-info/RECORD +25 -0
- unityflow-0.3.4.dist-info/WHEEL +5 -0
- unityflow-0.3.4.dist-info/entry_points.txt +2 -0
- unityflow-0.3.4.dist-info/licenses/LICENSE +21 -0
- unityflow-0.3.4.dist-info/top_level.txt +1 -0
unityflow/git_utils.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"""Git utilities for incremental normalization.
|
|
2
|
+
|
|
3
|
+
Provides functions to detect changed Unity files based on git status or commit history.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# Unity YAML file extensions that can be normalized
|
|
13
|
+
# Core assets
|
|
14
|
+
UNITY_CORE_EXTENSIONS = {
|
|
15
|
+
".prefab", # Prefab files
|
|
16
|
+
".unity", # Scene files
|
|
17
|
+
".asset", # ScriptableObject and generic assets
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Animation & control
|
|
21
|
+
UNITY_ANIMATION_EXTENSIONS = {
|
|
22
|
+
".anim", # Animation clips
|
|
23
|
+
".controller", # Animator Controller
|
|
24
|
+
".overrideController", # Animator Override Controller
|
|
25
|
+
".playable", # Playable assets (Timeline, etc.)
|
|
26
|
+
".mask", # Avatar masks
|
|
27
|
+
".signal", # Timeline Signal assets
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Materials & rendering
|
|
31
|
+
UNITY_RENDERING_EXTENSIONS = {
|
|
32
|
+
".mat", # Materials
|
|
33
|
+
".renderTexture", # Render Textures
|
|
34
|
+
".flare", # Lens flare assets
|
|
35
|
+
".shadervariants", # Shader variant collections
|
|
36
|
+
".spriteatlas", # Sprite atlases
|
|
37
|
+
".cubemap", # Cubemap assets
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Physics
|
|
41
|
+
UNITY_PHYSICS_EXTENSIONS = {
|
|
42
|
+
".physicMaterial", # 3D Physics materials
|
|
43
|
+
".physicsMaterial2D", # 2D Physics materials
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Terrain
|
|
47
|
+
UNITY_TERRAIN_EXTENSIONS = {
|
|
48
|
+
".terrainlayer", # Terrain layer assets
|
|
49
|
+
".brush", # Terrain brush assets
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Audio
|
|
53
|
+
UNITY_AUDIO_EXTENSIONS = {
|
|
54
|
+
".mixer", # Audio Mixer assets
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# UI & Editor
|
|
58
|
+
UNITY_UI_EXTENSIONS = {
|
|
59
|
+
".guiskin", # GUI Skin assets
|
|
60
|
+
".fontsettings", # Font settings
|
|
61
|
+
".preset", # Presets
|
|
62
|
+
".giparams", # Global Illumination parameters
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# All Unity YAML extensions combined
|
|
66
|
+
UNITY_EXTENSIONS = (
|
|
67
|
+
UNITY_CORE_EXTENSIONS
|
|
68
|
+
| UNITY_ANIMATION_EXTENSIONS
|
|
69
|
+
| UNITY_RENDERING_EXTENSIONS
|
|
70
|
+
| UNITY_PHYSICS_EXTENSIONS
|
|
71
|
+
| UNITY_TERRAIN_EXTENSIONS
|
|
72
|
+
| UNITY_AUDIO_EXTENSIONS
|
|
73
|
+
| UNITY_UI_EXTENSIONS
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_repo_root(path: Path | None = None) -> Path | None:
|
|
78
|
+
"""Get the root directory of the git repository.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
path: Starting path to search from (default: current directory)
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Path to repository root, or None if not in a git repository
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
result = subprocess.run(
|
|
88
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
89
|
+
cwd=path or Path.cwd(),
|
|
90
|
+
capture_output=True,
|
|
91
|
+
text=True,
|
|
92
|
+
check=True,
|
|
93
|
+
)
|
|
94
|
+
return Path(result.stdout.strip())
|
|
95
|
+
except subprocess.CalledProcessError:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_git_repository(path: Path | None = None) -> bool:
|
|
100
|
+
"""Check if the given path is inside a git repository.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
path: Path to check (default: current directory)
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
True if inside a git repository
|
|
107
|
+
"""
|
|
108
|
+
return get_repo_root(path) is not None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_changed_files(
|
|
112
|
+
extensions: Sequence[str] | None = None,
|
|
113
|
+
staged_only: bool = False,
|
|
114
|
+
include_untracked: bool = True,
|
|
115
|
+
cwd: Path | None = None,
|
|
116
|
+
) -> list[Path]:
|
|
117
|
+
"""Get list of changed files from git status.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
extensions: File extensions to filter (default: Unity extensions)
|
|
121
|
+
staged_only: Only include staged files
|
|
122
|
+
include_untracked: Include untracked files
|
|
123
|
+
cwd: Working directory (default: current directory)
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
List of paths to changed files
|
|
127
|
+
"""
|
|
128
|
+
if extensions is None:
|
|
129
|
+
extensions = list(UNITY_EXTENSIONS)
|
|
130
|
+
|
|
131
|
+
repo_root = get_repo_root(cwd)
|
|
132
|
+
if repo_root is None:
|
|
133
|
+
return []
|
|
134
|
+
|
|
135
|
+
changed_files: list[Path] = []
|
|
136
|
+
|
|
137
|
+
# Get staged and unstaged changes
|
|
138
|
+
# --porcelain=v1 gives stable, parseable output
|
|
139
|
+
# -uall shows individual files in untracked directories (not just directory names)
|
|
140
|
+
cmd = ["git", "status", "--porcelain=v1"]
|
|
141
|
+
if include_untracked:
|
|
142
|
+
cmd.append("-uall")
|
|
143
|
+
else:
|
|
144
|
+
cmd.append("--untracked-files=no")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
result = subprocess.run(
|
|
148
|
+
cmd,
|
|
149
|
+
cwd=cwd or Path.cwd(),
|
|
150
|
+
capture_output=True,
|
|
151
|
+
text=True,
|
|
152
|
+
check=True,
|
|
153
|
+
)
|
|
154
|
+
except subprocess.CalledProcessError:
|
|
155
|
+
return []
|
|
156
|
+
|
|
157
|
+
for line in result.stdout.split("\n"):
|
|
158
|
+
if not line or len(line) < 4:
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
# Porcelain format: XY filename (XY = 2 chars, space, then filename)
|
|
162
|
+
# X = index status, Y = worktree status
|
|
163
|
+
status_index = line[0]
|
|
164
|
+
status_worktree = line[1]
|
|
165
|
+
# Skip the space at position 2, filename starts at position 3
|
|
166
|
+
filepath = line[3:]
|
|
167
|
+
|
|
168
|
+
# Handle renames: "R old -> new"
|
|
169
|
+
if " -> " in filepath:
|
|
170
|
+
filepath = filepath.split(" -> ")[1]
|
|
171
|
+
|
|
172
|
+
# Filter by staged_only
|
|
173
|
+
if staged_only:
|
|
174
|
+
# Only include if index has changes (X is not ' ' or '?')
|
|
175
|
+
if status_index in (" ", "?"):
|
|
176
|
+
continue
|
|
177
|
+
else:
|
|
178
|
+
# Include both staged and unstaged, but not deleted
|
|
179
|
+
if status_index == "D" or status_worktree == "D":
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
file_path = repo_root / filepath
|
|
183
|
+
|
|
184
|
+
# Filter by extension
|
|
185
|
+
if file_path.suffix.lower() in extensions:
|
|
186
|
+
if file_path.exists():
|
|
187
|
+
changed_files.append(file_path)
|
|
188
|
+
|
|
189
|
+
return changed_files
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_files_changed_since(
|
|
193
|
+
ref: str,
|
|
194
|
+
extensions: Sequence[str] | None = None,
|
|
195
|
+
cwd: Path | None = None,
|
|
196
|
+
) -> list[Path]:
|
|
197
|
+
"""Get list of files changed since a git reference (commit, tag, branch).
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
ref: Git reference (e.g., "HEAD~5", "main", "v1.0.0")
|
|
201
|
+
extensions: File extensions to filter (default: Unity extensions)
|
|
202
|
+
cwd: Working directory (default: current directory)
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
List of paths to changed files
|
|
206
|
+
"""
|
|
207
|
+
if extensions is None:
|
|
208
|
+
extensions = list(UNITY_EXTENSIONS)
|
|
209
|
+
|
|
210
|
+
repo_root = get_repo_root(cwd)
|
|
211
|
+
if repo_root is None:
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
# Get files changed between ref and HEAD
|
|
215
|
+
try:
|
|
216
|
+
result = subprocess.run(
|
|
217
|
+
["git", "diff", "--name-only", ref, "HEAD"],
|
|
218
|
+
cwd=cwd or Path.cwd(),
|
|
219
|
+
capture_output=True,
|
|
220
|
+
text=True,
|
|
221
|
+
check=True,
|
|
222
|
+
)
|
|
223
|
+
except subprocess.CalledProcessError:
|
|
224
|
+
return []
|
|
225
|
+
|
|
226
|
+
changed_files: list[Path] = []
|
|
227
|
+
|
|
228
|
+
for line in result.stdout.strip().split("\n"):
|
|
229
|
+
if not line:
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
file_path = repo_root / line
|
|
233
|
+
|
|
234
|
+
# Filter by extension
|
|
235
|
+
if file_path.suffix.lower() in extensions:
|
|
236
|
+
if file_path.exists():
|
|
237
|
+
changed_files.append(file_path)
|
|
238
|
+
|
|
239
|
+
return changed_files
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def get_files_in_commit(
|
|
243
|
+
commit: str,
|
|
244
|
+
extensions: Sequence[str] | None = None,
|
|
245
|
+
cwd: Path | None = None,
|
|
246
|
+
) -> list[Path]:
|
|
247
|
+
"""Get list of files changed in a specific commit.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
commit: Git commit hash or reference
|
|
251
|
+
extensions: File extensions to filter (default: Unity extensions)
|
|
252
|
+
cwd: Working directory (default: current directory)
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
List of paths to changed files
|
|
256
|
+
"""
|
|
257
|
+
if extensions is None:
|
|
258
|
+
extensions = list(UNITY_EXTENSIONS)
|
|
259
|
+
|
|
260
|
+
repo_root = get_repo_root(cwd)
|
|
261
|
+
if repo_root is None:
|
|
262
|
+
return []
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
result = subprocess.run(
|
|
266
|
+
["git", "diff-tree", "--no-commit-id", "--name-only", "-r", commit],
|
|
267
|
+
cwd=cwd or Path.cwd(),
|
|
268
|
+
capture_output=True,
|
|
269
|
+
text=True,
|
|
270
|
+
check=True,
|
|
271
|
+
)
|
|
272
|
+
except subprocess.CalledProcessError:
|
|
273
|
+
return []
|
|
274
|
+
|
|
275
|
+
changed_files: list[Path] = []
|
|
276
|
+
|
|
277
|
+
for line in result.stdout.strip().split("\n"):
|
|
278
|
+
if not line:
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
file_path = repo_root / line
|
|
282
|
+
|
|
283
|
+
# Filter by extension
|
|
284
|
+
if file_path.suffix.lower() in extensions:
|
|
285
|
+
if file_path.exists():
|
|
286
|
+
changed_files.append(file_path)
|
|
287
|
+
|
|
288
|
+
return changed_files
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def filter_unity_files(
|
|
292
|
+
paths: Sequence[Path],
|
|
293
|
+
extensions: Sequence[str] | None = None,
|
|
294
|
+
) -> list[Path]:
|
|
295
|
+
"""Filter paths to only include Unity YAML files.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
paths: List of paths to filter
|
|
299
|
+
extensions: File extensions to include (default: Unity extensions)
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Filtered list of paths
|
|
303
|
+
"""
|
|
304
|
+
if extensions is None:
|
|
305
|
+
extensions = list(UNITY_EXTENSIONS)
|
|
306
|
+
|
|
307
|
+
return [p for p in paths if p.suffix.lower() in extensions and p.exists()]
|