git-warp 0.1.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.
@@ -0,0 +1,8 @@
1
+ # License
2
+
3
+ Copyright (C) 2026 Helgesen Software AS
4
+
5
+ This project is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).
6
+
7
+ License terms for integration with a given product may be negotiated.
8
+
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: git-warp
3
+ Version: 0.1.0
4
+ Summary: Edit git commit history with full undo
5
+ License: # License
6
+
7
+ Copyright (C) 2026 Helgesen Software AS
8
+
9
+ This project is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).
10
+
11
+ License terms for integration with a given product may be negotiated.
12
+
13
+
14
+ Project-URL: Homepage, http://helgesen.software
15
+ Project-URL: Repository, https://github.com/HelgesenSoftware/git-warp
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE.md
19
+ Requires-Dist: flask>=3.0
20
+ Dynamic: license-file
21
+
22
+ # git-warp
23
+
24
+ Rewrite git history in your browser — drag to reorder, squash, fixup, reword, and split commits, with unlimited undo.
25
+
26
+ A command-line tool, run from inside any git repository. It starts a local HTTP server bound to `127.0.0.1`, opens a browser, and presents a single-page UI for rewriting git history. All git operations run server-side via `subprocess`; the browser is pure UI. It refuses mutating operations on a dirty working tree; commits are shown with short hashes.
27
+
28
+ Design goal: minimal, reliable code with no avoidable failure modes. Dependencies: Flask, stdlib. No build step or JS bundler.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install git+https://github.com/HelgesenSoftware/git-warp
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ Run from inside any git repository:
39
+
40
+ ```bash
41
+ git-warp
42
+ ```
43
+
44
+ This opens your browser to a local page where you can view and rewrite your commit history. From there, open the manual for details.
45
+
46
+ ## Requirements
47
+
48
+ - Python >= 3.10
49
+ - Git >= 2.26
50
+
51
+ ## License
52
+
53
+ GPL-3.0 — see [LICENSE.md](LICENSE.md).
@@ -0,0 +1,32 @@
1
+ # git-warp
2
+
3
+ Rewrite git history in your browser — drag to reorder, squash, fixup, reword, and split commits, with unlimited undo.
4
+
5
+ A command-line tool, run from inside any git repository. It starts a local HTTP server bound to `127.0.0.1`, opens a browser, and presents a single-page UI for rewriting git history. All git operations run server-side via `subprocess`; the browser is pure UI. It refuses mutating operations on a dirty working tree; commits are shown with short hashes.
6
+
7
+ Design goal: minimal, reliable code with no avoidable failure modes. Dependencies: Flask, stdlib. No build step or JS bundler.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install git+https://github.com/HelgesenSoftware/git-warp
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ Run from inside any git repository:
18
+
19
+ ```bash
20
+ git-warp
21
+ ```
22
+
23
+ This opens your browser to a local page where you can view and rewrite your commit history. From there, open the manual for details.
24
+
25
+ ## Requirements
26
+
27
+ - Python >= 3.10
28
+ - Git >= 2.26
29
+
30
+ ## License
31
+
32
+ GPL-3.0 — see [LICENSE.md](LICENSE.md).
@@ -0,0 +1,130 @@
1
+ import argparse
2
+ import logging
3
+ import os
4
+ import secrets
5
+ import subprocess
6
+ import sys
7
+ import webbrowser
8
+
9
+ import flask.cli
10
+ from werkzeug.serving import make_server
11
+
12
+ import git_warp.backend
13
+ from git_warp.rest_api import create_app
14
+ from git_warp.backend import GitWarp, GitError, GitWarpError, WarpStateError
15
+
16
+
17
+ def _get_history_index(gh):
18
+ """Get undo stack and current HEAD index. Exits on error."""
19
+ try:
20
+ return gh.get_history_state()
21
+ except WarpStateError as e:
22
+ print(f"error: {e}", file=sys.stderr)
23
+ sys.exit(1)
24
+
25
+
26
+ def _set_history_index(gh, history, target_idx):
27
+ """Move HEAD to a specific history index. Exits on error."""
28
+ if target_idx < 0 or target_idx >= len(history):
29
+ print(f"error: target index {target_idx} out of range (0–{len(history)-1})", file=sys.stderr)
30
+ sys.exit(1)
31
+ try:
32
+ gh.reset(history[target_idx].commit_hash)
33
+ except GitWarpError as e:
34
+ print(f"error: {e.code}: {e.message}", file=sys.stderr)
35
+ sys.exit(1)
36
+ except GitError as e:
37
+ print(f"error: {e}", file=sys.stderr)
38
+ sys.exit(1)
39
+ print(f"At history index {target_idx}.")
40
+
41
+
42
+ def main():
43
+ parser = argparse.ArgumentParser(
44
+ description="Rewrite branch history with unlimited undo.",
45
+ formatter_class=argparse.RawDescriptionHelpFormatter,
46
+ )
47
+ parser.add_argument("--port", type=int, default=0,
48
+ help="TCP port for the server (default: auto-assigned)")
49
+ parser.add_argument("--clear-log", action="store_true",
50
+ help="delete the log file and exit")
51
+ _color_scheme = parser.add_mutually_exclusive_group()
52
+ _color_scheme.add_argument("--dark", action="store_true",
53
+ help="force dark mode (default: follow OS)")
54
+ _color_scheme.add_argument("--light", action="store_true",
55
+ help="force light mode (default: follow OS)")
56
+ _undo_redo = parser.add_mutually_exclusive_group()
57
+ _undo_redo.add_argument("--undo", nargs="?", const=1, type=int, metavar="N",
58
+ help="undo the last N operations (default: 1)")
59
+ _undo_redo.add_argument("--redo", nargs="?", const=1, type=int, metavar="N",
60
+ help="redo the next N operations (default: 1)")
61
+ args = parser.parse_args()
62
+
63
+ if args.clear_log:
64
+ log_path, existed = git_warp.backend.clear_log()
65
+ if existed:
66
+ print(f"Deleted {log_path}")
67
+ else:
68
+ print(f"No log file at {log_path}")
69
+ sys.exit(0)
70
+
71
+ # Require git >= 2.26.
72
+ try:
73
+ r = subprocess.run(["git", "--version"], capture_output=True, text=True, check=False)
74
+ parts = r.stdout.strip().split() # "git version X.Y.Z"
75
+ version_parts = parts[2].split(".")
76
+ major, minor = int(version_parts[0]), int(version_parts[1])
77
+ if (major, minor) < (2, 26):
78
+ raise ValueError("version too old")
79
+ except (FileNotFoundError, IndexError, ValueError):
80
+ # Git not found, unknown version, or version too old.
81
+ print("fatal: git >= 2.26 required", file=sys.stderr)
82
+ sys.exit(1)
83
+
84
+ # Run as command line tool for undo/redo
85
+ if args.undo is not None:
86
+ try:
87
+ gh = GitWarp(os.getcwd())
88
+ except WarpStateError as e:
89
+ print(f"fatal: {e}", file=sys.stderr)
90
+ sys.exit(1)
91
+ history, idx = _get_history_index(gh)
92
+ _set_history_index(gh, history, idx + args.undo)
93
+ sys.exit(0)
94
+ if args.redo is not None:
95
+ try:
96
+ gh = GitWarp(os.getcwd())
97
+ except WarpStateError as e:
98
+ print(f"fatal: {e}", file=sys.stderr)
99
+ sys.exit(1)
100
+ history, idx = _get_history_index(gh)
101
+ _set_history_index(gh, history, idx - args.redo)
102
+ sys.exit(0)
103
+ # Run web server UI
104
+ token = secrets.token_urlsafe(24)
105
+ repo_path = os.getcwd()
106
+
107
+ try:
108
+ app = create_app(repo_path, token)
109
+ except WarpStateError as e:
110
+ print(f"fatal: {e}", file=sys.stderr)
111
+ sys.exit(1)
112
+
113
+ logging.getLogger("werkzeug").setLevel(logging.ERROR)
114
+ flask.cli.show_server_banner = lambda *_: None
115
+
116
+ httpd = make_server("127.0.0.1", args.port, app, threaded=False)
117
+ port = httpd.server_port
118
+
119
+ url = f"http://127.0.0.1:{port}/?t={token}"
120
+ if args.dark:
121
+ url += "&dark=1"
122
+ elif args.light:
123
+ url += "&light=1"
124
+ print(f"git-warp running at {url} — Ctrl+C to quit")
125
+ webbrowser.open(url)
126
+ httpd.serve_forever()
127
+
128
+
129
+ if __name__ == "__main__":
130
+ main()
@@ -0,0 +1,3 @@
1
+ """Git history CLI entry point."""
2
+ from git_warp import main
3
+ main()