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.
- git_warp-0.1.0/LICENSE.md +8 -0
- git_warp-0.1.0/PKG-INFO +53 -0
- git_warp-0.1.0/README.md +32 -0
- git_warp-0.1.0/git_warp/__init__.py +130 -0
- git_warp-0.1.0/git_warp/__main__.py +3 -0
- git_warp-0.1.0/git_warp/backend.py +1161 -0
- git_warp-0.1.0/git_warp/editor.py +41 -0
- git_warp-0.1.0/git_warp/rest_api.py +142 -0
- git_warp-0.1.0/git_warp/static/app.js +809 -0
- git_warp-0.1.0/git_warp/static/favicon.svg +32 -0
- git_warp-0.1.0/git_warp/static/fixup.png +0 -0
- git_warp-0.1.0/git_warp/static/index.html +118 -0
- git_warp-0.1.0/git_warp/static/logo-dark.svg +110 -0
- git_warp-0.1.0/git_warp/static/logo-light.svg +46 -0
- git_warp-0.1.0/git_warp/static/manual.html +108 -0
- git_warp-0.1.0/git_warp/static/style.css +190 -0
- git_warp-0.1.0/git_warp.egg-info/PKG-INFO +53 -0
- git_warp-0.1.0/git_warp.egg-info/SOURCES.txt +28 -0
- git_warp-0.1.0/git_warp.egg-info/dependency_links.txt +1 -0
- git_warp-0.1.0/git_warp.egg-info/entry_points.txt +2 -0
- git_warp-0.1.0/git_warp.egg-info/requires.txt +1 -0
- git_warp-0.1.0/git_warp.egg-info/top_level.txt +1 -0
- git_warp-0.1.0/pyproject.toml +40 -0
- git_warp-0.1.0/setup.cfg +4 -0
- git_warp-0.1.0/tests/test_challenging.py +1412 -0
- git_warp-0.1.0/tests/test_cli.py +165 -0
- git_warp-0.1.0/tests/test_rest_api.py +2042 -0
- git_warp-0.1.0/tests/test_submodule.py +997 -0
- git_warp-0.1.0/tests/test_ui.py +507 -0
- git_warp-0.1.0/tests/test_worktree.py +275 -0
git_warp-0.1.0/PKG-INFO
ADDED
|
@@ -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).
|
git_warp-0.1.0/README.md
ADDED
|
@@ -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()
|