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.
Files changed (89) hide show
  1. clevergit/__init__.py +13 -0
  2. clevergit/cli/__init__.py +1 -0
  3. clevergit/cli/app.py +186 -0
  4. clevergit/cli/branch_cmd.py +150 -0
  5. clevergit/cli/commit_cmd.py +120 -0
  6. clevergit/cli/remote_cmd.py +142 -0
  7. clevergit/cli/repo_cmd.py +118 -0
  8. clevergit/core/__init__.py +1 -0
  9. clevergit/core/blame.py +180 -0
  10. clevergit/core/branch.py +157 -0
  11. clevergit/core/cherry_pick.py +145 -0
  12. clevergit/core/commit.py +192 -0
  13. clevergit/core/conflict.py +306 -0
  14. clevergit/core/diff.py +567 -0
  15. clevergit/core/git_flow.py +403 -0
  16. clevergit/core/graph.py +199 -0
  17. clevergit/core/log.py +294 -0
  18. clevergit/core/merge.py +206 -0
  19. clevergit/core/remote.py +294 -0
  20. clevergit/core/repo.py +350 -0
  21. clevergit/core/revert.py +145 -0
  22. clevergit/core/stash.py +98 -0
  23. clevergit/core/status.py +49 -0
  24. clevergit/core/tag.py +204 -0
  25. clevergit/git/__init__.py +1 -0
  26. clevergit/git/client.py +1051 -0
  27. clevergit/git/errors.py +96 -0
  28. clevergit/integrations/__init__.py +15 -0
  29. clevergit/integrations/github.py +419 -0
  30. clevergit/integrations/gitlab.py +432 -0
  31. clevergit/models/__init__.py +1 -0
  32. clevergit/models/blame_info.py +43 -0
  33. clevergit/models/branch_comparison.py +57 -0
  34. clevergit/models/branch_info.py +69 -0
  35. clevergit/models/commit_info.py +70 -0
  36. clevergit/models/file_status.py +126 -0
  37. clevergit/models/stash_info.py +51 -0
  38. clevergit/models/tag_info.py +44 -0
  39. clevergit/plugins/__init__.py +18 -0
  40. clevergit/plugins/builtin/__init__.py +5 -0
  41. clevergit/plugins/builtin/example_plugin.py +65 -0
  42. clevergit/plugins/config.py +92 -0
  43. clevergit/plugins/interface.py +126 -0
  44. clevergit/plugins/loader.py +132 -0
  45. clevergit/plugins/manager.py +239 -0
  46. clevergit/ui/__init__.py +1 -0
  47. clevergit/ui/main.py +49 -0
  48. clevergit/ui/settings.py +224 -0
  49. clevergit/ui/shortcuts.py +284 -0
  50. clevergit/ui/themes/__init__.py +14 -0
  51. clevergit/ui/themes/base.py +205 -0
  52. clevergit/ui/themes/dark.py +53 -0
  53. clevergit/ui/themes/light.py +53 -0
  54. clevergit/ui/themes/manager.py +208 -0
  55. clevergit/ui/widgets/__init__.py +11 -0
  56. clevergit/ui/widgets/blame_view.py +190 -0
  57. clevergit/ui/widgets/branch_compare_dialog.py +375 -0
  58. clevergit/ui/widgets/branch_view.py +163 -0
  59. clevergit/ui/widgets/cherry_pick_dialog.py +197 -0
  60. clevergit/ui/widgets/clone_dialog.py +290 -0
  61. clevergit/ui/widgets/command_palette.py +400 -0
  62. clevergit/ui/widgets/commit_dialog.py +118 -0
  63. clevergit/ui/widgets/diff_viewer.py +914 -0
  64. clevergit/ui/widgets/git_flow_panel.py +637 -0
  65. clevergit/ui/widgets/github_panel.py +583 -0
  66. clevergit/ui/widgets/gitlab_panel.py +583 -0
  67. clevergit/ui/widgets/graph_view.py +288 -0
  68. clevergit/ui/widgets/log_view.py +49 -0
  69. clevergit/ui/widgets/merge_tool.py +454 -0
  70. clevergit/ui/widgets/repo_view.py +31 -0
  71. clevergit/ui/widgets/repository_tab.py +290 -0
  72. clevergit/ui/widgets/reset_dialog.py +329 -0
  73. clevergit/ui/widgets/revert_dialog.py +197 -0
  74. clevergit/ui/widgets/shortcuts_dialog.py +246 -0
  75. clevergit/ui/widgets/stash_view.py +253 -0
  76. clevergit/ui/widgets/status_view.py +62 -0
  77. clevergit/ui/widgets/tag_view.py +260 -0
  78. clevergit/ui/widgets/welcome_screen.py +57 -0
  79. clevergit/ui/windows/__init__.py +3 -0
  80. clevergit/ui/windows/main_window.py +1232 -0
  81. clevergit/utils/__init__.py +1 -0
  82. clevergit/utils/formatter.py +166 -0
  83. clevergit/utils/helpers.py +228 -0
  84. clevergit-0.1.0.dist-info/METADATA +376 -0
  85. clevergit-0.1.0.dist-info/RECORD +89 -0
  86. clevergit-0.1.0.dist-info/WHEEL +5 -0
  87. clevergit-0.1.0.dist-info/entry_points.txt +3 -0
  88. clevergit-0.1.0.dist-info/licenses/LICENSE +21 -0
  89. 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)