git-rewrite 0.5.0__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.
- git_rewrite/__init__.py +32 -0
- git_rewrite/__main__.py +32 -0
- git_rewrite/argbuilder.py +1424 -0
- git_rewrite/cli.py +415 -0
- git_rewrite/common/__init__.py +103 -0
- git_rewrite/common/config.py +65 -0
- git_rewrite/common/repo.py +353 -0
- git_rewrite/common/stream.py +125 -0
- git_rewrite/compose/__init__.py +20 -0
- git_rewrite/compose/run.py +105 -0
- git_rewrite/flatten/__init__.py +26 -0
- git_rewrite/flatten/rewriter.py +524 -0
- git_rewrite/flatten/run.py +98 -0
- git_rewrite/flatten/tree.py +213 -0
- git_rewrite/py.typed +0 -0
- git_rewrite/remap/__init__.py +21 -0
- git_rewrite/remap/remapper.py +361 -0
- git_rewrite/remap/run.py +85 -0
- git_rewrite/sanitise/__init__.py +38 -0
- git_rewrite/sanitise/config.py +93 -0
- git_rewrite/sanitise/patterns.py +112 -0
- git_rewrite/sanitise/rewriter.py +357 -0
- git_rewrite/sanitise/run.py +145 -0
- git_rewrite/scan/__init__.py +21 -0
- git_rewrite/scan/run.py +100 -0
- git_rewrite/scan/scanner.py +192 -0
- git_rewrite/version.py +36 -0
- git_rewrite-0.5.0.dist-info/METADATA +178 -0
- git_rewrite-0.5.0.dist-info/RECORD +32 -0
- git_rewrite-0.5.0.dist-info/WHEEL +4 -0
- git_rewrite-0.5.0.dist-info/entry_points.txt +2 -0
- git_rewrite-0.5.0.dist-info/licenses/LICENSE +21 -0
git_rewrite/cli.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# cli.py
|
|
3
|
+
# ──────
|
|
4
|
+
#
|
|
5
|
+
# Main CLI for git-rewrite with subcommand dispatch.
|
|
6
|
+
#
|
|
7
|
+
# (c) 2026 Cyber Assessment Labs — MIT License; see LICENSE in the project root.
|
|
8
|
+
#
|
|
9
|
+
# Authors
|
|
10
|
+
# ───────
|
|
11
|
+
# bena (via Claude)
|
|
12
|
+
#
|
|
13
|
+
# Version History
|
|
14
|
+
# ───────────────
|
|
15
|
+
# Jan 2026 - Created
|
|
16
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
# Imports
|
|
20
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
import traceback
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from .argbuilder import ArgsParser
|
|
26
|
+
from .argbuilder import Namespace
|
|
27
|
+
from .version import VERSION_STR
|
|
28
|
+
|
|
29
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
# Descriptions
|
|
31
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
SCAN_DESCRIPTION = """\
|
|
34
|
+
Scan a git repository for sensitive words without modifying anything.
|
|
35
|
+
|
|
36
|
+
This command streams through all commits and blobs using git fast-export,
|
|
37
|
+
searching for occurrences of words defined in your config file.
|
|
38
|
+
Use this to audit a repository before sanitising it.
|
|
39
|
+
|
|
40
|
+
The config file should be JSON with a "words" array:
|
|
41
|
+
{"words": ["secretword", "internalname"]}
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
git-rewrite scan -r ./my-repo -c dirty-words.json
|
|
45
|
+
git-rewrite scan -r ./my-repo -c dirty-words.json -v # verbose
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
SANITISE_DESCRIPTION = """\
|
|
49
|
+
Rewrite git history to remove or replace sensitive words.
|
|
50
|
+
|
|
51
|
+
Creates a NEW repository with sanitised history. The original is never modified.
|
|
52
|
+
Uses git fast-export/fast-import for efficient streaming.
|
|
53
|
+
|
|
54
|
+
Features:
|
|
55
|
+
- Replace words in file contents, commit messages, author/committer names
|
|
56
|
+
- Map specific words to specific replacements (or use default "REDACTED")
|
|
57
|
+
- Map author names to specific email addresses
|
|
58
|
+
- Exclude specific files from the output (e.g., lock files)
|
|
59
|
+
- Remap submodule commit references if you've sanitised submodules too
|
|
60
|
+
|
|
61
|
+
Config file (JSON):
|
|
62
|
+
- words: list of words to find/replace
|
|
63
|
+
- word_mapping: dict mapping words to replacements
|
|
64
|
+
- email_mapping: dict mapping author names to emails
|
|
65
|
+
- exclude_files: list of files to exclude
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
git-rewrite sanitise -r ./dirty-repo -o ./clean-repo -c config.json
|
|
69
|
+
git-rewrite sanitise --sample-config # print example config
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
FLATTEN_DESCRIPTION = """\
|
|
73
|
+
Flatten submodules by inlining their contents into the main repository.
|
|
74
|
+
|
|
75
|
+
Creates a NEW repository where submodule references (gitlinks) are replaced
|
|
76
|
+
with the actual file contents from those submodules at each commit.
|
|
77
|
+
Useful for merging submodule history into a monorepo.
|
|
78
|
+
|
|
79
|
+
The command will:
|
|
80
|
+
1. Scan history to find all submodule paths and URLs
|
|
81
|
+
2. Clone or use existing checkouts of submodule repositories
|
|
82
|
+
3. Stream history, replacing gitlinks with actual file trees
|
|
83
|
+
4. Write a commit mapping file (old SHA -> new SHA)
|
|
84
|
+
|
|
85
|
+
Use --scan-only to just list submodules without flattening.
|
|
86
|
+
|
|
87
|
+
Example:
|
|
88
|
+
git-rewrite flatten -r ./repo-with-submodules -o ./flat-repo
|
|
89
|
+
git-rewrite flatten -r ./repo --scan-only # list submodules
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
REMAP_DESCRIPTION = """\
|
|
93
|
+
Remap submodule commit references using a mapping file.
|
|
94
|
+
|
|
95
|
+
When you sanitise or rewrite a submodule repository, its commit SHAs change.
|
|
96
|
+
If you have a parent repository that references those submodules, the gitlink
|
|
97
|
+
references become invalid. This command updates those references.
|
|
98
|
+
|
|
99
|
+
The mapping file format is: old_sha new_sha (one per line)
|
|
100
|
+
This is the output from a previous sanitise operation.
|
|
101
|
+
|
|
102
|
+
Optionally rewrite submodule URLs in .gitmodules with --url-rewrite.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
git-rewrite remap -r ./parent -o ./remapped -m mapping.txt
|
|
106
|
+
git-rewrite remap -r ./repo -o ./out -m map.txt \\
|
|
107
|
+
--url-rewrite ../old-submodule.git ../new-submodule.git
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
COMPOSE_DESCRIPTION = """\
|
|
111
|
+
Compose multiple commit mapping files into one.
|
|
112
|
+
|
|
113
|
+
When chaining operations (e.g., flatten -> sanitise), each produces a mapping
|
|
114
|
+
file. This command composes them: given A->B and B->C mappings, produces A->C.
|
|
115
|
+
|
|
116
|
+
Mapping files are processed in order, following the chain of transformations.
|
|
117
|
+
The format is: old_sha new_sha (one per line).
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
git-rewrite compose -m flatten.txt -m sanitise.txt -o combined.txt
|
|
121
|
+
git-rewrite compose -m a.txt -m b.txt -m c.txt -o final.txt
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
125
|
+
# Argument Parsing
|
|
126
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
def create_parser() -> ArgsParser:
|
|
131
|
+
"""Create the main argument parser with subcommands."""
|
|
132
|
+
parser = ArgsParser(
|
|
133
|
+
prog="git-rewrite",
|
|
134
|
+
description=(
|
|
135
|
+
"Git history rewriting tools for sanitising, flattening, and remapping."
|
|
136
|
+
),
|
|
137
|
+
version=f"git-rewrite {VERSION_STR}",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# =========== Common Options ===========
|
|
141
|
+
|
|
142
|
+
common = parser.create_common_collection()
|
|
143
|
+
common.add_argument(
|
|
144
|
+
"-v",
|
|
145
|
+
"--verbose",
|
|
146
|
+
action="store_true",
|
|
147
|
+
help="Show verbose output",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# =========== Scan Command ===========
|
|
151
|
+
|
|
152
|
+
scan_cmd = parser.add_command(
|
|
153
|
+
"scan",
|
|
154
|
+
help="Scan repository for sensitive words (read-only)",
|
|
155
|
+
description=SCAN_DESCRIPTION,
|
|
156
|
+
)
|
|
157
|
+
scan_cmd.add_argument(
|
|
158
|
+
"-r",
|
|
159
|
+
"--repo",
|
|
160
|
+
type=Path,
|
|
161
|
+
required=True,
|
|
162
|
+
help="Path to the git repository",
|
|
163
|
+
)
|
|
164
|
+
scan_cmd.add_argument(
|
|
165
|
+
"-c",
|
|
166
|
+
"--config",
|
|
167
|
+
type=Path,
|
|
168
|
+
required=True,
|
|
169
|
+
help="Path to JSON config file",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# =========== Sanitise Command ===========
|
|
173
|
+
|
|
174
|
+
sanitise_cmd = parser.add_command(
|
|
175
|
+
"sanitise",
|
|
176
|
+
help="Rewrite history to remove sensitive words",
|
|
177
|
+
description=SANITISE_DESCRIPTION,
|
|
178
|
+
)
|
|
179
|
+
sanitise_cmd.add_argument(
|
|
180
|
+
"--sample-config",
|
|
181
|
+
action="store_true",
|
|
182
|
+
help="Print sample config and exit",
|
|
183
|
+
)
|
|
184
|
+
sanitise_cmd.add_argument(
|
|
185
|
+
"-r",
|
|
186
|
+
"--repo",
|
|
187
|
+
type=Path,
|
|
188
|
+
help="Path to source repository",
|
|
189
|
+
)
|
|
190
|
+
sanitise_cmd.add_argument(
|
|
191
|
+
"-o",
|
|
192
|
+
"--output",
|
|
193
|
+
type=Path,
|
|
194
|
+
help="Path for output repository",
|
|
195
|
+
)
|
|
196
|
+
sanitise_cmd.add_argument(
|
|
197
|
+
"-c",
|
|
198
|
+
"--config",
|
|
199
|
+
type=Path,
|
|
200
|
+
help="Path to JSON config file",
|
|
201
|
+
)
|
|
202
|
+
sanitise_cmd.add_argument(
|
|
203
|
+
"--delete",
|
|
204
|
+
action="store_true",
|
|
205
|
+
help="Delete output directory if exists",
|
|
206
|
+
)
|
|
207
|
+
sanitise_cmd.add_argument(
|
|
208
|
+
"-d",
|
|
209
|
+
"--default",
|
|
210
|
+
default="REDACTED",
|
|
211
|
+
help="Default replacement word (default: REDACTED)",
|
|
212
|
+
)
|
|
213
|
+
sanitise_cmd.add_argument(
|
|
214
|
+
"-m",
|
|
215
|
+
"--commit-map",
|
|
216
|
+
type=Path,
|
|
217
|
+
help="Path to write commit mapping file",
|
|
218
|
+
)
|
|
219
|
+
sanitise_cmd.add_argument(
|
|
220
|
+
"-s",
|
|
221
|
+
"--submodule-map",
|
|
222
|
+
type=Path,
|
|
223
|
+
help="Path to submodule commit mapping file",
|
|
224
|
+
)
|
|
225
|
+
sanitise_cmd.add_argument(
|
|
226
|
+
"--copy-remotes",
|
|
227
|
+
action="store_true",
|
|
228
|
+
help="Copy git remotes from source to output repository",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# =========== Flatten Command ===========
|
|
232
|
+
|
|
233
|
+
flatten_cmd = parser.add_command(
|
|
234
|
+
"flatten",
|
|
235
|
+
help="Flatten submodules into main repository",
|
|
236
|
+
description=FLATTEN_DESCRIPTION,
|
|
237
|
+
)
|
|
238
|
+
flatten_cmd.add_argument(
|
|
239
|
+
"-r",
|
|
240
|
+
"--repo",
|
|
241
|
+
type=Path,
|
|
242
|
+
required=True,
|
|
243
|
+
help="Path to source repository",
|
|
244
|
+
)
|
|
245
|
+
flatten_cmd.add_argument(
|
|
246
|
+
"-o",
|
|
247
|
+
"--output",
|
|
248
|
+
type=Path,
|
|
249
|
+
help="Path for output repository",
|
|
250
|
+
)
|
|
251
|
+
flatten_cmd.add_argument(
|
|
252
|
+
"-m",
|
|
253
|
+
"--commit-map",
|
|
254
|
+
type=Path,
|
|
255
|
+
help="Path to write commit mapping file",
|
|
256
|
+
)
|
|
257
|
+
flatten_cmd.add_argument(
|
|
258
|
+
"--delete",
|
|
259
|
+
action="store_true",
|
|
260
|
+
help="Delete output directory if exists",
|
|
261
|
+
)
|
|
262
|
+
flatten_cmd.add_argument(
|
|
263
|
+
"--scan-only",
|
|
264
|
+
action="store_true",
|
|
265
|
+
help="Only scan and list submodules",
|
|
266
|
+
)
|
|
267
|
+
flatten_cmd.add_argument(
|
|
268
|
+
"--copy-remotes",
|
|
269
|
+
action="store_true",
|
|
270
|
+
help="Copy git remotes from source to output repository",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# =========== Remap Command ===========
|
|
274
|
+
|
|
275
|
+
remap_cmd = parser.add_command(
|
|
276
|
+
"remap",
|
|
277
|
+
help="Remap submodule commit references",
|
|
278
|
+
description=REMAP_DESCRIPTION,
|
|
279
|
+
)
|
|
280
|
+
remap_cmd.add_argument(
|
|
281
|
+
"-r",
|
|
282
|
+
"--repo",
|
|
283
|
+
type=Path,
|
|
284
|
+
required=True,
|
|
285
|
+
help="Path to source repository",
|
|
286
|
+
)
|
|
287
|
+
remap_cmd.add_argument(
|
|
288
|
+
"-o",
|
|
289
|
+
"--output",
|
|
290
|
+
type=Path,
|
|
291
|
+
required=True,
|
|
292
|
+
help="Path for output repository",
|
|
293
|
+
)
|
|
294
|
+
remap_cmd.add_argument(
|
|
295
|
+
"-m",
|
|
296
|
+
"--commit-map",
|
|
297
|
+
type=Path,
|
|
298
|
+
required=True,
|
|
299
|
+
help="Path to commit mapping file (old_sha new_sha per line)",
|
|
300
|
+
)
|
|
301
|
+
remap_cmd.add_argument(
|
|
302
|
+
"--submodule-path",
|
|
303
|
+
type=str,
|
|
304
|
+
help="Specific submodule path to remap (for gitlink filtering)",
|
|
305
|
+
)
|
|
306
|
+
remap_cmd.add_argument(
|
|
307
|
+
"--url-rewrite",
|
|
308
|
+
nargs=2,
|
|
309
|
+
action="append",
|
|
310
|
+
metavar=("OLD_URL", "NEW_URL"),
|
|
311
|
+
help="Rewrite URL in .gitmodules (can be specified multiple times)",
|
|
312
|
+
)
|
|
313
|
+
remap_cmd.add_argument(
|
|
314
|
+
"--delete",
|
|
315
|
+
action="store_true",
|
|
316
|
+
help="Delete output directory if exists",
|
|
317
|
+
)
|
|
318
|
+
remap_cmd.add_argument(
|
|
319
|
+
"--copy-remotes",
|
|
320
|
+
action="store_true",
|
|
321
|
+
help="Copy git remotes from source to output repository",
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# =========== Compose Command ===========
|
|
325
|
+
|
|
326
|
+
compose_cmd = parser.add_command(
|
|
327
|
+
"compose",
|
|
328
|
+
help="Compose multiple commit mapping files",
|
|
329
|
+
description=COMPOSE_DESCRIPTION,
|
|
330
|
+
)
|
|
331
|
+
compose_cmd.add_argument(
|
|
332
|
+
"-m",
|
|
333
|
+
"--mapping",
|
|
334
|
+
type=Path,
|
|
335
|
+
action="append",
|
|
336
|
+
dest="mappings",
|
|
337
|
+
required=True,
|
|
338
|
+
help="Mapping file to include (can be specified multiple times, in order)",
|
|
339
|
+
)
|
|
340
|
+
compose_cmd.add_argument(
|
|
341
|
+
"-o",
|
|
342
|
+
"--output",
|
|
343
|
+
type=Path,
|
|
344
|
+
required=True,
|
|
345
|
+
help="Path for output mapping file",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
return parser
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
352
|
+
# Main Entry Point
|
|
353
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
357
|
+
def main(argv: list[str] | None = None) -> int:
|
|
358
|
+
"""
|
|
359
|
+
Main entry point for git-rewrite CLI.
|
|
360
|
+
|
|
361
|
+
Parameters:
|
|
362
|
+
argv: Command line arguments (without program name). If None, uses sys.argv[1:].
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Exit code (0 for success, non-zero for error)
|
|
366
|
+
"""
|
|
367
|
+
if argv is None:
|
|
368
|
+
argv = sys.argv[1:]
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
return _main_inner(argv)
|
|
372
|
+
except KeyboardInterrupt:
|
|
373
|
+
print()
|
|
374
|
+
print("---- Manually Terminated ----")
|
|
375
|
+
print()
|
|
376
|
+
return 1
|
|
377
|
+
except SystemExit:
|
|
378
|
+
raise
|
|
379
|
+
except BaseException as e:
|
|
380
|
+
t = "-----------------------------------------------------------------------------\n"
|
|
381
|
+
t += "UNHANDLED EXCEPTION OCCURRED!!\n"
|
|
382
|
+
t += "\n"
|
|
383
|
+
t += traceback.format_exc()
|
|
384
|
+
t += "\n"
|
|
385
|
+
t += f"EXCEPTION: {type(e)} {e}\n"
|
|
386
|
+
t += "-----------------------------------------------------------------------------\n"
|
|
387
|
+
t += "\n"
|
|
388
|
+
print(t, file=sys.stderr)
|
|
389
|
+
return 1
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
393
|
+
def _main_inner(argv: list[str]) -> int:
|
|
394
|
+
"""Inner main function that does the actual work."""
|
|
395
|
+
from . import compose
|
|
396
|
+
from . import flatten
|
|
397
|
+
from . import remap
|
|
398
|
+
from . import sanitise
|
|
399
|
+
from . import scan
|
|
400
|
+
|
|
401
|
+
parser = create_parser()
|
|
402
|
+
args: Namespace = parser.parse(argv)
|
|
403
|
+
|
|
404
|
+
if not args.command:
|
|
405
|
+
return 0 # Help was shown by ArgsParser
|
|
406
|
+
|
|
407
|
+
commands = {
|
|
408
|
+
"scan": scan.run,
|
|
409
|
+
"sanitise": sanitise.run,
|
|
410
|
+
"flatten": flatten.run,
|
|
411
|
+
"remap": remap.run,
|
|
412
|
+
"compose": compose.run,
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return commands[args.command](args)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# common/__init__.py
|
|
3
|
+
# ───────────────────
|
|
4
|
+
#
|
|
5
|
+
# Common utilities for git-rewrite.
|
|
6
|
+
#
|
|
7
|
+
# (c) 2026 Cyber Assessment Labs — MIT License; see LICENSE in the project root.
|
|
8
|
+
#
|
|
9
|
+
# Authors
|
|
10
|
+
# ───────
|
|
11
|
+
# bena (via Claude)
|
|
12
|
+
#
|
|
13
|
+
# Version History
|
|
14
|
+
# ───────────────
|
|
15
|
+
# Jan 2026 - Created
|
|
16
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
from .config import BLOB_MODE
|
|
19
|
+
from .config import EXEC_MODE
|
|
20
|
+
from .config import GITLINK_MODE
|
|
21
|
+
from .config import TREE_MODE
|
|
22
|
+
from .config import CommitInfo
|
|
23
|
+
from .config import SubmoduleInfo
|
|
24
|
+
from .config import TreeEntry
|
|
25
|
+
from .repo import clone_repo
|
|
26
|
+
from .repo import commit_tree
|
|
27
|
+
from .repo import copy_remotes
|
|
28
|
+
from .repo import get_all_branches
|
|
29
|
+
from .repo import get_all_commits
|
|
30
|
+
from .repo import get_all_tags
|
|
31
|
+
from .repo import get_branch_commit
|
|
32
|
+
from .repo import get_commit_info
|
|
33
|
+
from .repo import get_gitmodules_at_commit
|
|
34
|
+
from .repo import get_remotes
|
|
35
|
+
from .repo import get_tag_commit
|
|
36
|
+
from .repo import get_tree_sha
|
|
37
|
+
from .repo import git
|
|
38
|
+
from .repo import git_bytes
|
|
39
|
+
from .repo import hash_object
|
|
40
|
+
from .repo import init_repo
|
|
41
|
+
from .repo import ls_tree
|
|
42
|
+
from .repo import mktree
|
|
43
|
+
from .repo import object_exists
|
|
44
|
+
from .repo import parse_gitmodules
|
|
45
|
+
from .repo import read_blob
|
|
46
|
+
from .repo import update_ref
|
|
47
|
+
from .stream import COMMIT_PATTERN
|
|
48
|
+
from .stream import D_PATTERN
|
|
49
|
+
from .stream import FROM_PATTERN
|
|
50
|
+
from .stream import M_PATTERN
|
|
51
|
+
from .stream import MARK_PATTERN
|
|
52
|
+
from .stream import MERGE_PATTERN
|
|
53
|
+
from .stream import checkout_default_branch
|
|
54
|
+
from .stream import create_stream_writer
|
|
55
|
+
from .stream import finalise_stream
|
|
56
|
+
from .stream import start_fast_export
|
|
57
|
+
from .stream import start_fast_import
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
# config
|
|
61
|
+
"GITLINK_MODE",
|
|
62
|
+
"TREE_MODE",
|
|
63
|
+
"BLOB_MODE",
|
|
64
|
+
"EXEC_MODE",
|
|
65
|
+
"SubmoduleInfo",
|
|
66
|
+
"CommitInfo",
|
|
67
|
+
"TreeEntry",
|
|
68
|
+
# repo
|
|
69
|
+
"git",
|
|
70
|
+
"git_bytes",
|
|
71
|
+
"init_repo",
|
|
72
|
+
"clone_repo",
|
|
73
|
+
"get_all_branches",
|
|
74
|
+
"get_all_tags",
|
|
75
|
+
"get_tag_commit",
|
|
76
|
+
"get_all_commits",
|
|
77
|
+
"get_commit_info",
|
|
78
|
+
"get_branch_commit",
|
|
79
|
+
"ls_tree",
|
|
80
|
+
"read_blob",
|
|
81
|
+
"get_tree_sha",
|
|
82
|
+
"mktree",
|
|
83
|
+
"hash_object",
|
|
84
|
+
"commit_tree",
|
|
85
|
+
"update_ref",
|
|
86
|
+
"get_gitmodules_at_commit",
|
|
87
|
+
"parse_gitmodules",
|
|
88
|
+
"object_exists",
|
|
89
|
+
"get_remotes",
|
|
90
|
+
"copy_remotes",
|
|
91
|
+
# stream
|
|
92
|
+
"MARK_PATTERN",
|
|
93
|
+
"COMMIT_PATTERN",
|
|
94
|
+
"FROM_PATTERN",
|
|
95
|
+
"MERGE_PATTERN",
|
|
96
|
+
"M_PATTERN",
|
|
97
|
+
"D_PATTERN",
|
|
98
|
+
"start_fast_export",
|
|
99
|
+
"start_fast_import",
|
|
100
|
+
"create_stream_writer",
|
|
101
|
+
"finalise_stream",
|
|
102
|
+
"checkout_default_branch",
|
|
103
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# config.py
|
|
3
|
+
# ─────────
|
|
4
|
+
#
|
|
5
|
+
# Shared configuration types and constants for git-rewrite.
|
|
6
|
+
#
|
|
7
|
+
# (c) 2026 Cyber Assessment Labs — MIT License; see LICENSE in the project root.
|
|
8
|
+
#
|
|
9
|
+
# Authors
|
|
10
|
+
# ───────
|
|
11
|
+
# bena (via Claude)
|
|
12
|
+
#
|
|
13
|
+
# Version History
|
|
14
|
+
# ───────────────
|
|
15
|
+
# Jan 2026 - Created
|
|
16
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
# Imports
|
|
20
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
from typing import TypedDict
|
|
23
|
+
|
|
24
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
# Constants
|
|
26
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
GITLINK_MODE = "160000"
|
|
29
|
+
TREE_MODE = "040000"
|
|
30
|
+
BLOB_MODE = "100644"
|
|
31
|
+
EXEC_MODE = "100755"
|
|
32
|
+
|
|
33
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
# Types
|
|
35
|
+
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SubmoduleInfo(TypedDict):
|
|
39
|
+
"""Information about a submodule from .gitmodules."""
|
|
40
|
+
|
|
41
|
+
path: str
|
|
42
|
+
url: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CommitInfo(TypedDict):
|
|
46
|
+
"""Information about a git commit."""
|
|
47
|
+
|
|
48
|
+
tree: str
|
|
49
|
+
parents: list[str]
|
|
50
|
+
author_name: str
|
|
51
|
+
author_email: str
|
|
52
|
+
author_date: str
|
|
53
|
+
committer_name: str
|
|
54
|
+
committer_email: str
|
|
55
|
+
committer_date: str
|
|
56
|
+
message: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class TreeEntry(TypedDict):
|
|
60
|
+
"""A single entry in a git tree."""
|
|
61
|
+
|
|
62
|
+
mode: str
|
|
63
|
+
type: str
|
|
64
|
+
sha: str
|
|
65
|
+
name: str
|