clevergit 0.1.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.
- clevergit/__init__.py +13 -0
- clevergit/cli/__init__.py +1 -0
- clevergit/cli/app.py +186 -0
- clevergit/cli/branch_cmd.py +150 -0
- clevergit/cli/commit_cmd.py +120 -0
- clevergit/cli/remote_cmd.py +142 -0
- clevergit/cli/repo_cmd.py +118 -0
- clevergit/core/__init__.py +1 -0
- clevergit/core/blame.py +180 -0
- clevergit/core/branch.py +157 -0
- clevergit/core/cherry_pick.py +145 -0
- clevergit/core/commit.py +192 -0
- clevergit/core/conflict.py +306 -0
- clevergit/core/diff.py +567 -0
- clevergit/core/git_flow.py +403 -0
- clevergit/core/graph.py +199 -0
- clevergit/core/log.py +294 -0
- clevergit/core/merge.py +206 -0
- clevergit/core/remote.py +294 -0
- clevergit/core/repo.py +350 -0
- clevergit/core/revert.py +145 -0
- clevergit/core/stash.py +98 -0
- clevergit/core/status.py +49 -0
- clevergit/core/tag.py +204 -0
- clevergit/git/__init__.py +1 -0
- clevergit/git/client.py +1051 -0
- clevergit/git/errors.py +96 -0
- clevergit/integrations/__init__.py +15 -0
- clevergit/integrations/github.py +419 -0
- clevergit/integrations/gitlab.py +432 -0
- clevergit/models/__init__.py +1 -0
- clevergit/models/blame_info.py +43 -0
- clevergit/models/branch_comparison.py +57 -0
- clevergit/models/branch_info.py +69 -0
- clevergit/models/commit_info.py +70 -0
- clevergit/models/file_status.py +126 -0
- clevergit/models/stash_info.py +51 -0
- clevergit/models/tag_info.py +44 -0
- clevergit/plugins/__init__.py +18 -0
- clevergit/plugins/builtin/__init__.py +5 -0
- clevergit/plugins/builtin/example_plugin.py +65 -0
- clevergit/plugins/config.py +92 -0
- clevergit/plugins/interface.py +126 -0
- clevergit/plugins/loader.py +132 -0
- clevergit/plugins/manager.py +239 -0
- clevergit/ui/__init__.py +1 -0
- clevergit/ui/main.py +49 -0
- clevergit/ui/settings.py +224 -0
- clevergit/ui/shortcuts.py +284 -0
- clevergit/ui/themes/__init__.py +14 -0
- clevergit/ui/themes/base.py +205 -0
- clevergit/ui/themes/dark.py +53 -0
- clevergit/ui/themes/light.py +53 -0
- clevergit/ui/themes/manager.py +208 -0
- clevergit/ui/widgets/__init__.py +11 -0
- clevergit/ui/widgets/blame_view.py +190 -0
- clevergit/ui/widgets/branch_compare_dialog.py +375 -0
- clevergit/ui/widgets/branch_view.py +163 -0
- clevergit/ui/widgets/cherry_pick_dialog.py +197 -0
- clevergit/ui/widgets/clone_dialog.py +290 -0
- clevergit/ui/widgets/command_palette.py +400 -0
- clevergit/ui/widgets/commit_dialog.py +118 -0
- clevergit/ui/widgets/diff_viewer.py +914 -0
- clevergit/ui/widgets/git_flow_panel.py +637 -0
- clevergit/ui/widgets/github_panel.py +583 -0
- clevergit/ui/widgets/gitlab_panel.py +583 -0
- clevergit/ui/widgets/graph_view.py +288 -0
- clevergit/ui/widgets/log_view.py +49 -0
- clevergit/ui/widgets/merge_tool.py +454 -0
- clevergit/ui/widgets/repo_view.py +31 -0
- clevergit/ui/widgets/repository_tab.py +290 -0
- clevergit/ui/widgets/reset_dialog.py +329 -0
- clevergit/ui/widgets/revert_dialog.py +197 -0
- clevergit/ui/widgets/shortcuts_dialog.py +246 -0
- clevergit/ui/widgets/stash_view.py +253 -0
- clevergit/ui/widgets/status_view.py +62 -0
- clevergit/ui/widgets/tag_view.py +260 -0
- clevergit/ui/widgets/welcome_screen.py +57 -0
- clevergit/ui/windows/__init__.py +3 -0
- clevergit/ui/windows/main_window.py +1232 -0
- clevergit/utils/__init__.py +1 -0
- clevergit/utils/formatter.py +166 -0
- clevergit/utils/helpers.py +228 -0
- clevergit-0.1.0.dist-info/METADATA +376 -0
- clevergit-0.1.0.dist-info/RECORD +89 -0
- clevergit-0.1.0.dist-info/WHEEL +5 -0
- clevergit-0.1.0.dist-info/entry_points.txt +3 -0
- clevergit-0.1.0.dist-info/licenses/LICENSE +21 -0
- clevergit-0.1.0.dist-info/top_level.txt +1 -0
clevergit/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CleverGit - A high-level Git client library for Python.
|
|
3
|
+
|
|
4
|
+
This library provides a more intuitive and Pythonic way to interact with Git,
|
|
5
|
+
abstracting away complex command-line operations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
from clevergit.core.repo import Repo
|
|
11
|
+
from clevergit.plugins import Plugin, PluginManager, PluginMetadata
|
|
12
|
+
|
|
13
|
+
__all__ = ["Repo", "Plugin", "PluginManager", "PluginMetadata"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI layer initialization."""
|
clevergit/cli/app.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Main CLI application entry point."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from clevergit.cli import repo_cmd, commit_cmd, branch_cmd, remote_cmd
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(
|
|
10
|
+
name="clevergit",
|
|
11
|
+
help="CleverGit - Smart Git operations made simple",
|
|
12
|
+
no_args_is_help=True
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Register subcommands
|
|
16
|
+
app.add_typer(repo_cmd.app, name="repo", help="Repository operations")
|
|
17
|
+
app.add_typer(commit_cmd.app, name="commit", help="Commit operations")
|
|
18
|
+
app.add_typer(branch_cmd.app, name="branch", help="Branch operations")
|
|
19
|
+
app.add_typer(remote_cmd.app, name="remote", help="Remote operations")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.command()
|
|
23
|
+
def version():
|
|
24
|
+
"""Show CleverGit version."""
|
|
25
|
+
typer.echo("CleverGit v0.1.0")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@app.command()
|
|
29
|
+
def status(
|
|
30
|
+
path: Optional[Path] = typer.Option(
|
|
31
|
+
None,
|
|
32
|
+
"--path",
|
|
33
|
+
"-p",
|
|
34
|
+
help="Path to repository (default: current directory)"
|
|
35
|
+
)
|
|
36
|
+
):
|
|
37
|
+
"""Show repository status."""
|
|
38
|
+
from clevergit.core.repo import Repo
|
|
39
|
+
from clevergit.utils.formatter import format_status
|
|
40
|
+
|
|
41
|
+
repo_path = path or Path.cwd()
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
repo = Repo.open(repo_path)
|
|
45
|
+
status_list = repo.status()
|
|
46
|
+
|
|
47
|
+
output = format_status(status_list)
|
|
48
|
+
typer.echo(output)
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command()
|
|
56
|
+
def log(
|
|
57
|
+
path: Optional[Path] = typer.Option(
|
|
58
|
+
None,
|
|
59
|
+
"--path",
|
|
60
|
+
"-p",
|
|
61
|
+
help="Path to repository (default: current directory)"
|
|
62
|
+
),
|
|
63
|
+
max_count: int = typer.Option(
|
|
64
|
+
10,
|
|
65
|
+
"--max",
|
|
66
|
+
"-n",
|
|
67
|
+
help="Maximum number of commits to show"
|
|
68
|
+
),
|
|
69
|
+
oneline: bool = typer.Option(
|
|
70
|
+
False,
|
|
71
|
+
"--oneline",
|
|
72
|
+
help="Show compact one-line format"
|
|
73
|
+
)
|
|
74
|
+
):
|
|
75
|
+
"""Show commit history."""
|
|
76
|
+
from clevergit.core.repo import Repo
|
|
77
|
+
from clevergit.utils.formatter import format_log
|
|
78
|
+
|
|
79
|
+
repo_path = path or Path.cwd()
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
repo = Repo.open(repo_path)
|
|
83
|
+
commits = repo.log(max_count=max_count)
|
|
84
|
+
|
|
85
|
+
output = format_log(commits, oneline=oneline)
|
|
86
|
+
typer.echo(output)
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
90
|
+
raise typer.Exit(1)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@app.command()
|
|
94
|
+
def fetch(
|
|
95
|
+
remote: str = typer.Argument(
|
|
96
|
+
"origin",
|
|
97
|
+
help="Remote name to fetch from"
|
|
98
|
+
),
|
|
99
|
+
prune: bool = typer.Option(
|
|
100
|
+
False,
|
|
101
|
+
"--prune",
|
|
102
|
+
"-p",
|
|
103
|
+
help="Remove deleted remote branches"
|
|
104
|
+
),
|
|
105
|
+
path: Optional[Path] = typer.Option(
|
|
106
|
+
None,
|
|
107
|
+
"--path",
|
|
108
|
+
help="Path to repository (default: current directory)"
|
|
109
|
+
)
|
|
110
|
+
):
|
|
111
|
+
"""Fetch updates from a remote repository."""
|
|
112
|
+
from clevergit.cli.remote_cmd import fetch_remote
|
|
113
|
+
fetch_remote(remote=remote, prune=prune, path=path)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@app.command()
|
|
117
|
+
def pull(
|
|
118
|
+
remote: str = typer.Argument(
|
|
119
|
+
"origin",
|
|
120
|
+
help="Remote name to pull from"
|
|
121
|
+
),
|
|
122
|
+
branch: Optional[str] = typer.Option(
|
|
123
|
+
None,
|
|
124
|
+
"--branch",
|
|
125
|
+
"-b",
|
|
126
|
+
help="Branch to pull (default: current branch)"
|
|
127
|
+
),
|
|
128
|
+
rebase: bool = typer.Option(
|
|
129
|
+
False,
|
|
130
|
+
"--rebase",
|
|
131
|
+
"-r",
|
|
132
|
+
help="Use rebase instead of merge"
|
|
133
|
+
),
|
|
134
|
+
path: Optional[Path] = typer.Option(
|
|
135
|
+
None,
|
|
136
|
+
"--path",
|
|
137
|
+
help="Path to repository (default: current directory)"
|
|
138
|
+
)
|
|
139
|
+
):
|
|
140
|
+
"""Pull updates from a remote repository."""
|
|
141
|
+
from clevergit.cli.remote_cmd import pull_remote
|
|
142
|
+
pull_remote(remote=remote, branch=branch, rebase=rebase, path=path)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@app.command()
|
|
146
|
+
def push(
|
|
147
|
+
remote: str = typer.Argument(
|
|
148
|
+
"origin",
|
|
149
|
+
help="Remote name to push to"
|
|
150
|
+
),
|
|
151
|
+
branch: Optional[str] = typer.Option(
|
|
152
|
+
None,
|
|
153
|
+
"--branch",
|
|
154
|
+
"-b",
|
|
155
|
+
help="Branch to push (default: current branch)"
|
|
156
|
+
),
|
|
157
|
+
force: bool = typer.Option(
|
|
158
|
+
False,
|
|
159
|
+
"--force",
|
|
160
|
+
"-f",
|
|
161
|
+
help="Force push (use with caution!)"
|
|
162
|
+
),
|
|
163
|
+
set_upstream: bool = typer.Option(
|
|
164
|
+
False,
|
|
165
|
+
"--set-upstream",
|
|
166
|
+
"-u",
|
|
167
|
+
help="Set upstream tracking branch"
|
|
168
|
+
),
|
|
169
|
+
path: Optional[Path] = typer.Option(
|
|
170
|
+
None,
|
|
171
|
+
"--path",
|
|
172
|
+
help="Path to repository (default: current directory)"
|
|
173
|
+
)
|
|
174
|
+
):
|
|
175
|
+
"""Push commits to a remote repository."""
|
|
176
|
+
from clevergit.cli.remote_cmd import push_remote
|
|
177
|
+
push_remote(remote=remote, branch=branch, force=force, set_upstream=set_upstream, path=path)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def main():
|
|
181
|
+
"""Main entry point."""
|
|
182
|
+
app()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
main()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Branch commands."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
app = typer.Typer()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@app.command("create")
|
|
11
|
+
def create_branch(
|
|
12
|
+
name: str = typer.Argument(..., help="Branch name"),
|
|
13
|
+
checkout: bool = typer.Option(False, "--checkout", "-c", help="Checkout after creating"),
|
|
14
|
+
path: Optional[Path] = typer.Option(
|
|
15
|
+
None,
|
|
16
|
+
"--path",
|
|
17
|
+
"-p",
|
|
18
|
+
help="Path to repository (default: current directory)"
|
|
19
|
+
)
|
|
20
|
+
):
|
|
21
|
+
"""Create a new branch."""
|
|
22
|
+
from clevergit.core.repo import Repo
|
|
23
|
+
|
|
24
|
+
repo_path = path or Path.cwd()
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
repo = Repo.open(repo_path)
|
|
28
|
+
branch = repo.create_branch(name, checkout=checkout)
|
|
29
|
+
|
|
30
|
+
if checkout:
|
|
31
|
+
typer.secho(f"✓ Created and checked out branch '{name}'", fg=typer.colors.GREEN)
|
|
32
|
+
else:
|
|
33
|
+
typer.secho(f"✓ Created branch '{name}'", fg=typer.colors.GREEN)
|
|
34
|
+
|
|
35
|
+
except Exception as e:
|
|
36
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
37
|
+
raise typer.Exit(1)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.command("delete")
|
|
41
|
+
def delete_branch(
|
|
42
|
+
name: str = typer.Argument(..., help="Branch name"),
|
|
43
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force delete"),
|
|
44
|
+
path: Optional[Path] = typer.Option(
|
|
45
|
+
None,
|
|
46
|
+
"--path",
|
|
47
|
+
"-p",
|
|
48
|
+
help="Path to repository (default: current directory)"
|
|
49
|
+
)
|
|
50
|
+
):
|
|
51
|
+
"""Delete a branch."""
|
|
52
|
+
from clevergit.core.repo import Repo
|
|
53
|
+
|
|
54
|
+
repo_path = path or Path.cwd()
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
repo = Repo.open(repo_path)
|
|
58
|
+
repo.delete_branch(name, force=force)
|
|
59
|
+
typer.secho(f"✓ Deleted branch '{name}'", fg=typer.colors.GREEN)
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
63
|
+
raise typer.Exit(1)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@app.command("list")
|
|
67
|
+
def list_branches(
|
|
68
|
+
all: bool = typer.Option(False, "--all", "-a", help="Include remote branches"),
|
|
69
|
+
path: Optional[Path] = typer.Option(
|
|
70
|
+
None,
|
|
71
|
+
"--path",
|
|
72
|
+
"-p",
|
|
73
|
+
help="Path to repository (default: current directory)"
|
|
74
|
+
)
|
|
75
|
+
):
|
|
76
|
+
"""List all branches."""
|
|
77
|
+
from clevergit.core.repo import Repo
|
|
78
|
+
from clevergit.utils.formatter import format_branches
|
|
79
|
+
|
|
80
|
+
repo_path = path or Path.cwd()
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
repo = Repo.open(repo_path)
|
|
84
|
+
branches = repo.list_branches(remote=all)
|
|
85
|
+
|
|
86
|
+
output = format_branches(branches)
|
|
87
|
+
typer.echo(output)
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
91
|
+
raise typer.Exit(1)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@app.command("switch")
|
|
95
|
+
def switch_branch(
|
|
96
|
+
name: str = typer.Argument(..., help="Branch name"),
|
|
97
|
+
create: bool = typer.Option(False, "--create", "-c", help="Create if doesn't exist"),
|
|
98
|
+
path: Optional[Path] = typer.Option(
|
|
99
|
+
None,
|
|
100
|
+
"--path",
|
|
101
|
+
"-p",
|
|
102
|
+
help="Path to repository (default: current directory)"
|
|
103
|
+
)
|
|
104
|
+
):
|
|
105
|
+
"""Switch to a different branch."""
|
|
106
|
+
from clevergit.core.repo import Repo
|
|
107
|
+
|
|
108
|
+
repo_path = path or Path.cwd()
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
repo = Repo.open(repo_path)
|
|
112
|
+
|
|
113
|
+
if create:
|
|
114
|
+
repo.create_branch(name, checkout=True)
|
|
115
|
+
typer.secho(f"✓ Created and switched to branch '{name}'", fg=typer.colors.GREEN)
|
|
116
|
+
else:
|
|
117
|
+
repo.checkout(name)
|
|
118
|
+
typer.secho(f"✓ Switched to branch '{name}'", fg=typer.colors.GREEN)
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
122
|
+
raise typer.Exit(1)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@app.command("merge")
|
|
126
|
+
def merge_branch(
|
|
127
|
+
name: str = typer.Argument(..., help="Branch to merge"),
|
|
128
|
+
no_ff: bool = typer.Option(False, "--no-ff", help="Force merge commit"),
|
|
129
|
+
message: Optional[str] = typer.Option(None, "--message", "-m", help="Merge commit message"),
|
|
130
|
+
path: Optional[Path] = typer.Option(
|
|
131
|
+
None,
|
|
132
|
+
"--path",
|
|
133
|
+
"-p",
|
|
134
|
+
help="Path to repository (default: current directory)"
|
|
135
|
+
)
|
|
136
|
+
):
|
|
137
|
+
"""Merge a branch into current branch."""
|
|
138
|
+
from clevergit.core.repo import Repo
|
|
139
|
+
from clevergit.core import merge as merge_ops
|
|
140
|
+
|
|
141
|
+
repo_path = path or Path.cwd()
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
repo = Repo.open(repo_path)
|
|
145
|
+
merge_ops.merge_branch(repo.path, name, no_ff=no_ff, message=message)
|
|
146
|
+
typer.secho(f"✓ Merged branch '{name}'", fg=typer.colors.GREEN)
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
150
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Commit commands."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, List
|
|
6
|
+
|
|
7
|
+
app = typer.Typer()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@app.command("create")
|
|
11
|
+
def create_commit(
|
|
12
|
+
message: str = typer.Option(..., "--message", "-m", help="Commit message"),
|
|
13
|
+
all: bool = typer.Option(False, "--all", "-a", help="Commit all tracked files"),
|
|
14
|
+
files: Optional[List[str]] = typer.Argument(None, help="Specific files to commit"),
|
|
15
|
+
path: Optional[Path] = typer.Option(
|
|
16
|
+
None,
|
|
17
|
+
"--path",
|
|
18
|
+
"-p",
|
|
19
|
+
help="Path to repository (default: current directory)"
|
|
20
|
+
)
|
|
21
|
+
):
|
|
22
|
+
"""Create a new commit."""
|
|
23
|
+
from clevergit.core.repo import Repo
|
|
24
|
+
|
|
25
|
+
repo_path = path or Path.cwd()
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
repo = Repo.open(repo_path)
|
|
29
|
+
|
|
30
|
+
if all:
|
|
31
|
+
commit = repo.commit_all(message)
|
|
32
|
+
elif files:
|
|
33
|
+
commit = repo.commit_files(files, message)
|
|
34
|
+
else:
|
|
35
|
+
typer.secho(
|
|
36
|
+
"Error: Specify --all or provide file names",
|
|
37
|
+
fg=typer.colors.RED,
|
|
38
|
+
err=True
|
|
39
|
+
)
|
|
40
|
+
raise typer.Exit(1)
|
|
41
|
+
|
|
42
|
+
typer.secho(f"✓ Created commit {commit.short_sha}", fg=typer.colors.GREEN)
|
|
43
|
+
typer.echo(f" {commit.subject}")
|
|
44
|
+
|
|
45
|
+
except Exception as e:
|
|
46
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
47
|
+
raise typer.Exit(1)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.command("amend")
|
|
51
|
+
def amend_commit(
|
|
52
|
+
message: Optional[str] = typer.Option(None, "--message", "-m", help="New commit message"),
|
|
53
|
+
no_edit: bool = typer.Option(False, "--no-edit", help="Keep existing message"),
|
|
54
|
+
path: Optional[Path] = typer.Option(
|
|
55
|
+
None,
|
|
56
|
+
"--path",
|
|
57
|
+
"-p",
|
|
58
|
+
help="Path to repository (default: current directory)"
|
|
59
|
+
)
|
|
60
|
+
):
|
|
61
|
+
"""Amend the last commit."""
|
|
62
|
+
from clevergit.core.repo import Repo
|
|
63
|
+
from clevergit.git.client import GitClient
|
|
64
|
+
|
|
65
|
+
repo_path = path or Path.cwd()
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
repo = Repo.open(repo_path)
|
|
69
|
+
client = GitClient(repo.path)
|
|
70
|
+
|
|
71
|
+
cmd = ['commit', '--amend']
|
|
72
|
+
if no_edit:
|
|
73
|
+
cmd.append('--no-edit')
|
|
74
|
+
elif message:
|
|
75
|
+
cmd.extend(['-m', message])
|
|
76
|
+
|
|
77
|
+
result = client.run_command(cmd)
|
|
78
|
+
|
|
79
|
+
if result.success:
|
|
80
|
+
typer.secho("✓ Amended commit", fg=typer.colors.GREEN)
|
|
81
|
+
else:
|
|
82
|
+
raise Exception(result.error)
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
86
|
+
raise typer.Exit(1)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.command("undo")
|
|
90
|
+
def undo_commit(
|
|
91
|
+
soft: bool = typer.Option(True, "--soft", help="Keep changes staged"),
|
|
92
|
+
hard: bool = typer.Option(False, "--hard", help="Discard all changes"),
|
|
93
|
+
path: Optional[Path] = typer.Option(
|
|
94
|
+
None,
|
|
95
|
+
"--path",
|
|
96
|
+
"-p",
|
|
97
|
+
help="Path to repository (default: current directory)"
|
|
98
|
+
)
|
|
99
|
+
):
|
|
100
|
+
"""Undo the last commit."""
|
|
101
|
+
from clevergit.core.repo import Repo
|
|
102
|
+
from clevergit.git.client import GitClient
|
|
103
|
+
|
|
104
|
+
repo_path = path or Path.cwd()
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
repo = Repo.open(repo_path)
|
|
108
|
+
client = GitClient(repo.path)
|
|
109
|
+
|
|
110
|
+
reset_type = '--hard' if hard else '--soft'
|
|
111
|
+
result = client.run_command(['reset', reset_type, 'HEAD~1'])
|
|
112
|
+
|
|
113
|
+
if result.success:
|
|
114
|
+
typer.secho("✓ Undid last commit", fg=typer.colors.GREEN)
|
|
115
|
+
else:
|
|
116
|
+
raise Exception(result.error)
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
120
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Remote operations commands."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
app = typer.Typer()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@app.command("fetch")
|
|
11
|
+
def fetch_remote(
|
|
12
|
+
remote: str = typer.Argument(
|
|
13
|
+
"origin",
|
|
14
|
+
help="Remote name to fetch from"
|
|
15
|
+
),
|
|
16
|
+
prune: bool = typer.Option(
|
|
17
|
+
False,
|
|
18
|
+
"--prune",
|
|
19
|
+
"-p",
|
|
20
|
+
help="Remove deleted remote branches"
|
|
21
|
+
),
|
|
22
|
+
path: Optional[Path] = typer.Option(
|
|
23
|
+
None,
|
|
24
|
+
"--path",
|
|
25
|
+
help="Path to repository (default: current directory)"
|
|
26
|
+
)
|
|
27
|
+
):
|
|
28
|
+
"""Fetch updates from a remote repository."""
|
|
29
|
+
from clevergit.core.repo import Repo
|
|
30
|
+
|
|
31
|
+
repo_path = path or Path.cwd()
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
repo = Repo.open(repo_path)
|
|
35
|
+
repo.fetch(remote=remote, prune=prune)
|
|
36
|
+
typer.secho(
|
|
37
|
+
f"✓ Fetched updates from '{remote}'",
|
|
38
|
+
fg=typer.colors.GREEN
|
|
39
|
+
)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
42
|
+
raise typer.Exit(1)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@app.command("pull")
|
|
46
|
+
def pull_remote(
|
|
47
|
+
remote: str = typer.Argument(
|
|
48
|
+
"origin",
|
|
49
|
+
help="Remote name to pull from"
|
|
50
|
+
),
|
|
51
|
+
branch: Optional[str] = typer.Option(
|
|
52
|
+
None,
|
|
53
|
+
"--branch",
|
|
54
|
+
"-b",
|
|
55
|
+
help="Branch to pull (default: current branch)"
|
|
56
|
+
),
|
|
57
|
+
rebase: bool = typer.Option(
|
|
58
|
+
False,
|
|
59
|
+
"--rebase",
|
|
60
|
+
"-r",
|
|
61
|
+
help="Use rebase instead of merge"
|
|
62
|
+
),
|
|
63
|
+
path: Optional[Path] = typer.Option(
|
|
64
|
+
None,
|
|
65
|
+
"--path",
|
|
66
|
+
help="Path to repository (default: current directory)"
|
|
67
|
+
)
|
|
68
|
+
):
|
|
69
|
+
"""Pull updates from a remote repository."""
|
|
70
|
+
from clevergit.core.repo import Repo
|
|
71
|
+
|
|
72
|
+
repo_path = path or Path.cwd()
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
repo = Repo.open(repo_path)
|
|
76
|
+
repo.pull(remote=remote, branch=branch, rebase=rebase)
|
|
77
|
+
typer.secho(
|
|
78
|
+
f"✓ Pulled updates from '{remote}'",
|
|
79
|
+
fg=typer.colors.GREEN
|
|
80
|
+
)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
83
|
+
raise typer.Exit(1)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@app.command("push")
|
|
87
|
+
def push_remote(
|
|
88
|
+
remote: str = typer.Argument(
|
|
89
|
+
"origin",
|
|
90
|
+
help="Remote name to push to"
|
|
91
|
+
),
|
|
92
|
+
branch: Optional[str] = typer.Option(
|
|
93
|
+
None,
|
|
94
|
+
"--branch",
|
|
95
|
+
"-b",
|
|
96
|
+
help="Branch to push (default: current branch)"
|
|
97
|
+
),
|
|
98
|
+
force: bool = typer.Option(
|
|
99
|
+
False,
|
|
100
|
+
"--force",
|
|
101
|
+
"-f",
|
|
102
|
+
help="Force push (use with caution!)"
|
|
103
|
+
),
|
|
104
|
+
set_upstream: bool = typer.Option(
|
|
105
|
+
False,
|
|
106
|
+
"--set-upstream",
|
|
107
|
+
"-u",
|
|
108
|
+
help="Set upstream tracking branch"
|
|
109
|
+
),
|
|
110
|
+
path: Optional[Path] = typer.Option(
|
|
111
|
+
None,
|
|
112
|
+
"--path",
|
|
113
|
+
help="Path to repository (default: current directory)"
|
|
114
|
+
)
|
|
115
|
+
):
|
|
116
|
+
"""Push commits to a remote repository."""
|
|
117
|
+
from clevergit.core.repo import Repo
|
|
118
|
+
|
|
119
|
+
repo_path = path or Path.cwd()
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
repo = Repo.open(repo_path)
|
|
123
|
+
|
|
124
|
+
# Show warning for force push
|
|
125
|
+
if force:
|
|
126
|
+
typer.secho(
|
|
127
|
+
"⚠ Warning: Force push can overwrite remote history!",
|
|
128
|
+
fg=typer.colors.YELLOW
|
|
129
|
+
)
|
|
130
|
+
confirm = typer.confirm("Are you sure you want to force push?")
|
|
131
|
+
if not confirm:
|
|
132
|
+
typer.echo("Push cancelled.")
|
|
133
|
+
raise typer.Exit(0)
|
|
134
|
+
|
|
135
|
+
repo.push(remote=remote, branch=branch, force=force, set_upstream=set_upstream)
|
|
136
|
+
typer.secho(
|
|
137
|
+
f"✓ Pushed commits to '{remote}'",
|
|
138
|
+
fg=typer.colors.GREEN
|
|
139
|
+
)
|
|
140
|
+
except Exception as e:
|
|
141
|
+
typer.secho(f"Error: {e}", fg=typer.colors.RED, err=True)
|
|
142
|
+
raise typer.Exit(1)
|