Skip to content

Commit c73eeb5

Browse files
author
LocalIdentity
committed
stat order diff stripping
1 parent 38a0cfa commit c73eeb5

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

.gitattributes

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,17 @@
6666
# Executable files - preserve execution permissions on Unix systems
6767
# (https://git-scm.com/docs/gitattributes#_executable)
6868
###############################################################################
69-
runtime/*.exe binary
69+
runtime/*.exe binary
70+
71+
###############################################################################
72+
# Hide statOrder churn inside specific exported data files when diffing
73+
###############################################################################
74+
src/Data/Mod*.lua diff=nostatorder
75+
src/Data/Crucible.lua diff=nostatorder
76+
src/Data/Uniques/Special/*.lua diff=nostatorder
77+
src/Data/ClusterJewels.lua diff=nostatorder
78+
src/Data/TattooPassives.lua diff=nostatorder
79+
src/Data/TimelessJewelData/LegionPassives.lua diff=nostatorder
80+
src/Data/StatDescriptions/*.lua diff=nostatorder
81+
src/Data/BeastCraft.lua diff=nostatorder
82+
src/Data/StatDescriptions/*/*.lua diff=nostatorder

CONTRIBUTING.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ Feature requests are always welcome. Note that not all requests will receive an
5353
`-- This file is automatically generated, do not edit!`. To change these, instead change the scripts in the `./src/Export` directory and rerun the exporter.
5454
For your PR, please include all relevant changes to both the scripts and data files.
5555

56+
### Keeping mod diffs readable (Fork, CLI, etc.)
57+
The exporter stores `statOrder` values in every generated mod entry so we can reference them at runtime, but those numbers churn between game updates and make diffs noisy.
58+
Hook Git’s textconv into the repo after cloning so any diff-capable client that respects `.gitattributes` (the CLI, Fork, Sourcetree, etc.) hides that churn automatically:
59+
60+
```
61+
git config diff.nostatorder.textconv "python tools/strip_statorder_for_diff.py"
62+
git config diff.nostatorder.cachetextconv true
63+
```
64+
65+
Git now runs the helper in `tools/strip_statorder_for_diff.py` whenever you diff the noisy exporter outputs (`src/Data/Mod*.lua`, `src/Data/Crucible.lua`, `src/Data/Uniques/Special/*.lua`, `src/Data/TattooPassives.lua`, `src/Data/TimelessJewelData/LegionPassives.lua`, `src/Data/ClusterJewels.lua`), replacing every `statOrder` payload with a stable placeholder before diffing.
66+
Fork.dev, Sourcetree and other GUIs that honor textconv immediately pick up the cleaner diff without extra per-file ignore settings.
67+
5668
## Setting up a development installation
5769
Note: This tutorial assumes that you are already familiar with Git.
5870

tools/strip_statorder_for_diff.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python3
2+
"""Text conversion helper that hides statOrder churn in diffs."""
3+
4+
from __future__ import annotations
5+
6+
import io
7+
import re
8+
import sys
9+
from pathlib import Path
10+
from typing import Final
11+
12+
_RE_TABLE: Final = re.compile(r"(statOrder\s*=\s*\{)([^}]*)(\})", re.MULTILINE)
13+
_RE_SCALAR: Final = re.compile(r"(statOrder\s*=\s*)(?!\{)([^,\s}]+)", re.MULTILINE)
14+
_RE_BRACKET_KEY: Final = re.compile(r'(\["statOrder"\]\s*=\s*)([^,\s}]+)', re.MULTILINE)
15+
_RE_NOTABLE_ENTRY: Final = re.compile(r'(\["[^"]+"\]\s*=\s*)(\d+)', re.MULTILINE)
16+
_RE_STATDESC_BLOCK: Final = re.compile(r"(?m)(^\t)\[(\d+)\](\s*=\s*\{)")
17+
_RE_STATDESC_TAIL: Final = re.compile(r'(\["[^"]+"\]\s*=\s*)(\d+)(,?)$', re.MULTILINE)
18+
_STATDESC_SUFFIX = "_stat_descriptions.lua"
19+
_PLACEHOLDER: Final = "0"
20+
21+
22+
def _replace_scalar(match: re.Match[str]) -> str:
23+
return f"{match.group(1)}{_PLACEHOLDER}"
24+
25+
26+
def _replace_table(match: re.Match[str]) -> str:
27+
return f"{match.group(1)} {_PLACEHOLDER} {match.group(3)}"
28+
29+
30+
def _normalize(contents: str, file_hint: str | None) -> str:
31+
contents = _RE_TABLE.sub(_replace_table, contents)
32+
contents = _RE_SCALAR.sub(_replace_scalar, contents)
33+
contents = _RE_BRACKET_KEY.sub(_replace_scalar, contents)
34+
35+
file_name = Path(file_hint).name if file_hint else ""
36+
37+
if file_name == "ClusterJewels.lua" or "NotableSortOrder" in contents:
38+
contents = _RE_NOTABLE_ENTRY.sub(_replace_scalar, contents)
39+
if file_hint and "LegionPassives.lua" in file_hint:
40+
contents = re.sub(r'(\["oidx"\]\s*=\s*)(\d+)', _replace_scalar, contents)
41+
if file_name == "stat_descriptions.lua" or file_name.endswith(_STATDESC_SUFFIX):
42+
contents = _RE_STATDESC_BLOCK.sub(lambda m: f"{m.group(1)}[0]{m.group(3)}", contents)
43+
contents = _RE_STATDESC_TAIL.sub(lambda m: f'{m.group(1)}{_PLACEHOLDER}{m.group(3)}', contents)
44+
45+
return contents
46+
47+
48+
def _read_source(path: str | None) -> str:
49+
if path:
50+
with io.open(path, "r", encoding="utf-8", errors="ignore") as handle:
51+
return handle.read()
52+
return sys.stdin.read()
53+
54+
55+
def main() -> None:
56+
path = sys.argv[1] if len(sys.argv) > 1 else None
57+
sys.stdout.write(_normalize(_read_source(path), path))
58+
59+
60+
if __name__ == "__main__":
61+
main()

0 commit comments

Comments
 (0)