pygitgo 1.4.0__tar.gz → 1.5.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.
Files changed (42) hide show
  1. {pygitgo-1.4.0/src/pygitgo.egg-info → pygitgo-1.5.0}/PKG-INFO +4 -1
  2. {pygitgo-1.4.0 → pygitgo-1.5.0}/README.md +2 -0
  3. {pygitgo-1.4.0 → pygitgo-1.5.0}/pyproject.toml +2 -1
  4. pygitgo-1.5.0/src/pygitgo/commands/staging.py +50 -0
  5. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/main.py +48 -16
  6. {pygitgo-1.4.0 → pygitgo-1.5.0/src/pygitgo.egg-info}/PKG-INFO +4 -1
  7. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo.egg-info/SOURCES.txt +2 -0
  8. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo.egg-info/requires.txt +1 -0
  9. pygitgo-1.5.0/tests/test_staging.py +127 -0
  10. {pygitgo-1.4.0 → pygitgo-1.5.0}/LICENSE +0 -0
  11. {pygitgo-1.4.0 → pygitgo-1.5.0}/setup.cfg +0 -0
  12. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/__init__.py +0 -0
  13. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/auth/__init__.py +0 -0
  14. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/auth/account.py +0 -0
  15. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/auth/manager.py +0 -0
  16. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/auth/ssh_utils.py +0 -0
  17. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/commands/__init__.py +0 -0
  18. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/commands/git_operations.py +0 -0
  19. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/commands/jump.py +0 -0
  20. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/commands/pull.py +0 -0
  21. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/commands/state.py +0 -0
  22. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/commands/undo.py +0 -0
  23. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/exceptions.py +0 -0
  24. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/utils/__init__.py +0 -0
  25. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/utils/colors.py +0 -0
  26. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/utils/config.py +0 -0
  27. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/utils/executor.py +0 -0
  28. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/utils/platform_utils.py +0 -0
  29. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo/utils/setup.py +0 -0
  30. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo.egg-info/dependency_links.txt +0 -0
  31. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo.egg-info/entry_points.txt +0 -0
  32. {pygitgo-1.4.0 → pygitgo-1.5.0}/src/pygitgo.egg-info/top_level.txt +0 -0
  33. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_config.py +0 -0
  34. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_git_operations.py +0 -0
  35. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_jump.py +0 -0
  36. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_main.py +0 -0
  37. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_platform.py +0 -0
  38. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_pull.py +0 -0
  39. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_setup.py +0 -0
  40. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_state.py +0 -0
  41. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_undo.py +0 -0
  42. {pygitgo-1.4.0 → pygitgo-1.5.0}/tests/test_url_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygitgo
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: GitGo CLI - Your Fast Git Companion. Simplifies git push, link, stash, and user management.
5
5
  Author: Huerte
6
6
  License: GPL-3.0-or-later
@@ -16,6 +16,7 @@ Requires-Python: >=3.8
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: yaspin>=3.0
19
+ Requires-Dist: pick>=2.0
19
20
  Provides-Extra: dev
20
21
  Requires-Dist: pytest>=7.0; extra == "dev"
21
22
  Requires-Dist: pytest-mock>=3.10; extra == "dev"
@@ -182,6 +183,7 @@ Stages all changes, commits, and pushes in one command.
182
183
  ```bash
183
184
  gitgo push [branch] [message]
184
185
  gitgo push -n [branch] [message] # create new branch first
186
+ gitgo push -s [branch] [message] # interactively select files to stage
185
187
  ```
186
188
 
187
189
  > [!TIP]
@@ -190,6 +192,7 @@ gitgo push -n [branch] [message] # create new branch first
190
192
  | Flag | Description |
191
193
  |------|-------------|
192
194
  | `-n`, `--new` | Create a new branch before pushing |
195
+ | `-s`, `--select` | Interactively select which files you want to include in this push |
193
196
 
194
197
  If there are no new changes but unpushed commits exist, GitGo detects this and pushes without creating an empty commit.
195
198
 
@@ -159,6 +159,7 @@ Stages all changes, commits, and pushes in one command.
159
159
  ```bash
160
160
  gitgo push [branch] [message]
161
161
  gitgo push -n [branch] [message] # create new branch first
162
+ gitgo push -s [branch] [message] # interactively select files to stage
162
163
  ```
163
164
 
164
165
  > [!TIP]
@@ -167,6 +168,7 @@ gitgo push -n [branch] [message] # create new branch first
167
168
  | Flag | Description |
168
169
  |------|-------------|
169
170
  | `-n`, `--new` | Create a new branch before pushing |
171
+ | `-s`, `--select` | Interactively select which files you want to include in this push |
170
172
 
171
173
  If there are no new changes but unpushed commits exist, GitGo detects this and pushes without creating an empty commit.
172
174
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pygitgo"
7
- version = "1.4.0"
7
+ version = "1.5.0"
8
8
  description = "GitGo CLI - Your Fast Git Companion. Simplifies git push, link, stash, and user management."
9
9
  readme = "README.md"
10
10
  license = {text = "GPL-3.0-or-later"}
@@ -22,6 +22,7 @@ classifiers = [
22
22
  ]
23
23
  dependencies = [
24
24
  "yaspin>=3.0",
25
+ "pick>=2.0",
25
26
  ]
26
27
 
27
28
  [project.optional-dependencies]
@@ -0,0 +1,50 @@
1
+ from pygitgo.utils.colors import success, warning, error
2
+ from pygitgo.utils.executor import run_command
3
+ from pick import pick
4
+ import subprocess
5
+ import sys
6
+
7
+
8
+ STATUS_LABELS = {
9
+ "M": "modified",
10
+ "A": "added",
11
+ "D": "deleted",
12
+ "R": "renamed",
13
+ "??": "new file",
14
+ }
15
+
16
+
17
+ def get_changed_files():
18
+ status = run_command(["git", "status", "--porcelain"], allow_fail=True)
19
+ if isinstance(status, subprocess.CalledProcessError) or not status.strip():
20
+ return []
21
+
22
+ files = []
23
+ for line in status.strip().splitlines():
24
+ if len(line) < 3:
25
+ continue
26
+ status_code = line[:2].strip()
27
+ filepath = line[3:].strip()
28
+ label = STATUS_LABELS.get(status_code, "changed")
29
+ files.append({"status": status_code, "label": label, "path": filepath})
30
+
31
+ return files
32
+
33
+
34
+ def display_file_picker(files):
35
+ options = [f"({f['label']}) {f['path']}" for f in files]
36
+
37
+ selected = pick(
38
+ options,
39
+ "Select files to stage (SPACE to toggle, ENTER to confirm):",
40
+ multiselect=True,
41
+ min_selection_count=0
42
+ )
43
+
44
+ return [files[idx]["path"] for _, idx in selected]
45
+
46
+
47
+ def selective_stage(selected_files):
48
+ for filepath in selected_files:
49
+ run_command(["git", "add", "--", filepath])
50
+ success(f"\n{len(selected_files)} file(s) staged for commit.")
@@ -2,6 +2,7 @@ from pygitgo.commands.git_operations import (
2
2
  git_new_branch, git_commit, git_init, add_remote_origin,
3
3
  confirm_remote_link, git_push, get_current_branch, is_branch_exist
4
4
  )
5
+ from pygitgo.commands.staging import get_changed_files, display_file_picker, selective_stage
5
6
  from pygitgo.utils.colors import info, success, warning, error
6
7
  from pygitgo.utils.config import get_config, config_operation
7
8
  from pygitgo.exceptions import GitCommandError, GitGoError
@@ -100,6 +101,7 @@ def link_operation(args):
100
101
  def push_operation(args):
101
102
  branch = args.branch
102
103
  message = args.message
104
+ select = args.select if hasattr(args, 'select') else False
103
105
 
104
106
  if args.new:
105
107
  if not branch:
@@ -129,24 +131,52 @@ def push_operation(args):
129
131
  message = get_config("default-message", "New Project Update")
130
132
  info(f"No commit message provided. Using default: '{message}'\n")
131
133
 
132
- commit_made = git_commit(message)
133
-
134
- if commit_made:
134
+ if select:
135
+ files = get_changed_files()
136
+ if not files:
137
+ info("\nWorking tree is clean. Nothing to select.")
138
+ warning("Make some changes first before using GitGo to commit and push.")
139
+ return
140
+
141
+ selected = display_file_picker(files)
142
+ if not selected:
143
+ info("\nNo files selected. Push aborted.\n")
144
+ return
145
+
146
+ selective_stage(selected)
147
+ clean_message = message.strip('"\'')
148
+ run_command(["git", "commit", "-m", clean_message], loading_msg="Commiting changes...")
135
149
  git_push(branch)
150
+
151
+ leftover_files = [f for f in files if f["path"] not in selected]
152
+ if leftover_files:
153
+ print()
154
+ warning(f"You have {len(leftover_files)} uncommitted file(s) left in your working directory:")
155
+ for f in leftover_files:
156
+ info(f" - {f['path']}")
157
+ print()
158
+ save_choice = input("Would you like to save these leftovers to a GitGo State for later? (y/n): ").lower()
159
+ if save_choice == 'y':
160
+ state_operations(Namespace(action="save", identifier=f"Leftovers from: {clean_message}"))
136
161
  else:
137
- try:
138
- unpushed = run_command(["git", "log", "--oneline", f"origin/{branch}..HEAD"], allow_fail=True, loading_msg="Checking for unpushed commits...")
139
- if not isinstance(unpushed, subprocess.CalledProcessError) and unpushed.strip():
140
- warning("\nNo changes to commit, but found unpushed commits. Pushing to remote...")
141
- git_push(branch)
142
- else:
143
- info("\nWorking tree is clean and everything is up to date.")
144
- warning("Make some changes first before using GitGo to commit and push.")
162
+ commit_made = git_commit(message)
163
+
164
+ if commit_made:
165
+ git_push(branch)
166
+ else:
167
+ try:
168
+ unpushed = run_command(["git", "log", "--oneline", f"origin/{branch}..HEAD"], allow_fail=True, loading_msg="Checking for unpushed commits...")
169
+ if not isinstance(unpushed, subprocess.CalledProcessError) and unpushed.strip():
170
+ warning("\nNo changes to commit, but found unpushed commits. Pushing to remote...")
171
+ git_push(branch)
172
+ else:
173
+ info("\nWorking tree is clean and everything is up to date.")
174
+ warning("Make some changes first before using GitGo to commit and push.")
175
+ return
176
+ except (GitCommandError, Exception):
177
+ warning("\nNo changes to commit. Cannot verify remote status.")
178
+ warning("Make some changes first or check your git remote configuration.")
145
179
  return
146
- except (GitCommandError, Exception):
147
- warning("\nNo changes to commit. Cannot verify remote status.")
148
- warning("Make some changes first or check your git remote configuration.")
149
- return
150
180
 
151
181
  print("\n" + ("=" * 90))
152
182
  success("MISSION COMPLETE — NO CASUALTIES. ALL TARGETS NEUTRALIZED.\nAWAITING FOR YOUR NEXT ORDERS.\n\n")
@@ -216,11 +246,13 @@ def main():
216
246
  " gitgo push Push current branch with default message\n"
217
247
  " gitgo push main 'fix auth bug' Push to main with a custom message\n"
218
248
  " gitgo push 'fix auth bug' Push current branch with a custom message\n"
219
- " gitgo push -n feature/login 'add login' Create new branch and push"
249
+ " gitgo push -n feature/login 'add login' Create new branch and push\n"
250
+ " gitgo push -s main 'fix bug' Pick which files to include before pushing"
220
251
  ),
221
252
  formatter_class=argparse.RawDescriptionHelpFormatter
222
253
  )
223
254
  push_parser.add_argument("-n", "--new", action="store_true", help="Create a new branch before pushing")
255
+ push_parser.add_argument("-s", "--select", action="store_true", help="Interactively select which files to stage")
224
256
  push_parser.add_argument("branch", nargs="?", default=None, help="Branch to push to (default: current branch)")
225
257
  push_parser.add_argument("message", nargs="?", default=None, help="Commit message")
226
258
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygitgo
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: GitGo CLI - Your Fast Git Companion. Simplifies git push, link, stash, and user management.
5
5
  Author: Huerte
6
6
  License: GPL-3.0-or-later
@@ -16,6 +16,7 @@ Requires-Python: >=3.8
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: yaspin>=3.0
19
+ Requires-Dist: pick>=2.0
19
20
  Provides-Extra: dev
20
21
  Requires-Dist: pytest>=7.0; extra == "dev"
21
22
  Requires-Dist: pytest-mock>=3.10; extra == "dev"
@@ -182,6 +183,7 @@ Stages all changes, commits, and pushes in one command.
182
183
  ```bash
183
184
  gitgo push [branch] [message]
184
185
  gitgo push -n [branch] [message] # create new branch first
186
+ gitgo push -s [branch] [message] # interactively select files to stage
185
187
  ```
186
188
 
187
189
  > [!TIP]
@@ -190,6 +192,7 @@ gitgo push -n [branch] [message] # create new branch first
190
192
  | Flag | Description |
191
193
  |------|-------------|
192
194
  | `-n`, `--new` | Create a new branch before pushing |
195
+ | `-s`, `--select` | Interactively select which files you want to include in this push |
193
196
 
194
197
  If there are no new changes but unpushed commits exist, GitGo detects this and pushes without creating an empty commit.
195
198
 
@@ -18,6 +18,7 @@ src/pygitgo/commands/__init__.py
18
18
  src/pygitgo/commands/git_operations.py
19
19
  src/pygitgo/commands/jump.py
20
20
  src/pygitgo/commands/pull.py
21
+ src/pygitgo/commands/staging.py
21
22
  src/pygitgo/commands/state.py
22
23
  src/pygitgo/commands/undo.py
23
24
  src/pygitgo/utils/__init__.py
@@ -33,6 +34,7 @@ tests/test_main.py
33
34
  tests/test_platform.py
34
35
  tests/test_pull.py
35
36
  tests/test_setup.py
37
+ tests/test_staging.py
36
38
  tests/test_state.py
37
39
  tests/test_undo.py
38
40
  tests/test_url_validator.py
@@ -1,4 +1,5 @@
1
1
  yaspin>=3.0
2
+ pick>=2.0
2
3
 
3
4
  [dev]
4
5
  pytest>=7.0
@@ -0,0 +1,127 @@
1
+ import pytest
2
+ from unittest.mock import patch, MagicMock
3
+ from pygitgo.commands.staging import get_changed_files, display_file_picker, selective_stage
4
+ from argparse import Namespace
5
+ import subprocess
6
+
7
+ @patch("pygitgo.commands.staging.run_command")
8
+ def test_get_changed_files_with_changes(mock_run_command):
9
+ mock_run_command.return_value = "M src/main.py\n?? new_file.txt\nM README.md"
10
+
11
+ files = get_changed_files()
12
+
13
+ assert len(files) == 3
14
+ assert files[0]["path"] == "src/main.py"
15
+ assert files[0]["label"] == "modified"
16
+ assert files[1]["path"] == "new_file.txt"
17
+ assert files[1]["label"] == "new file"
18
+ assert files[2]["path"] == "README.md"
19
+ assert files[2]["label"] == "modified"
20
+
21
+
22
+ @patch("pygitgo.commands.staging.run_command")
23
+ def test_get_changed_files_no_changes(mock_run_command):
24
+ mock_run_command.return_value = subprocess.CalledProcessError(1, ["git", "status"])
25
+
26
+ files = get_changed_files()
27
+
28
+ assert files == []
29
+
30
+
31
+ @patch("pygitgo.commands.staging.run_command")
32
+ def test_get_changed_files_empty_status(mock_run_command):
33
+ mock_run_command.return_value = ""
34
+
35
+ files = get_changed_files()
36
+
37
+ assert files == []
38
+
39
+
40
+ @patch("pygitgo.commands.staging.run_command")
41
+ def test_get_changed_files_deleted_file(mock_run_command):
42
+ mock_run_command.return_value = "D old_file.py"
43
+
44
+ files = get_changed_files()
45
+
46
+ assert len(files) == 1
47
+ assert files[0]["path"] == "old_file.py"
48
+ assert files[0]["label"] == "deleted"
49
+
50
+
51
+ @patch("pygitgo.commands.staging.pick")
52
+ def test_display_file_picker_select_specific(mock_pick):
53
+ mock_pick.return_value = [("(modified) src/main.py", 0), ("(modified) README.md", 2)]
54
+
55
+ files = [
56
+ {"status": "M", "label": "modified", "path": "src/main.py"},
57
+ {"status": "??", "label": "new file", "path": "new_file.txt"},
58
+ {"status": "M", "label": "modified", "path": "README.md"},
59
+ ]
60
+
61
+ selected = display_file_picker(files)
62
+
63
+ assert selected == ["src/main.py", "README.md"]
64
+ mock_pick.assert_called_once()
65
+
66
+
67
+ @patch("pygitgo.commands.staging.pick")
68
+ def test_display_file_picker_select_all(mock_pick):
69
+ mock_pick.return_value = [("(modified) src/main.py", 0), ("(new file) new_file.txt", 1)]
70
+
71
+ files = [
72
+ {"status": "M", "label": "modified", "path": "src/main.py"},
73
+ {"status": "??", "label": "new file", "path": "new_file.txt"},
74
+ ]
75
+
76
+ selected = display_file_picker(files)
77
+
78
+ assert selected == ["src/main.py", "new_file.txt"]
79
+
80
+
81
+ @patch("pygitgo.commands.staging.pick")
82
+ def test_display_file_picker_cancel(mock_pick):
83
+ mock_pick.return_value = []
84
+
85
+ files = [
86
+ {"status": "M", "label": "modified", "path": "src/main.py"},
87
+ ]
88
+
89
+ selected = display_file_picker(files)
90
+
91
+ assert selected == []
92
+
93
+
94
+ @patch("pygitgo.commands.staging.pick")
95
+ def test_display_file_picker_select_single(mock_pick):
96
+ mock_pick.return_value = [("(new file) new_file.txt", 1)]
97
+
98
+ files = [
99
+ {"status": "M", "label": "modified", "path": "src/main.py"},
100
+ {"status": "??", "label": "new file", "path": "new_file.txt"},
101
+ ]
102
+
103
+ selected = display_file_picker(files)
104
+
105
+ assert selected == ["new_file.txt"]
106
+
107
+
108
+ @patch("pygitgo.commands.staging.run_command")
109
+ @patch("pygitgo.commands.staging.success")
110
+ def test_selective_stage(mock_success, mock_run_command):
111
+ selected_files = ["src/main.py", "README.md"]
112
+
113
+ selective_stage(selected_files)
114
+
115
+ assert mock_run_command.call_count == 2
116
+ mock_run_command.assert_any_call(["git", "add", "--", "src/main.py"])
117
+ mock_run_command.assert_any_call(["git", "add", "--", "README.md"])
118
+ mock_success.assert_called_once()
119
+
120
+
121
+ @patch("pygitgo.commands.staging.run_command")
122
+ @patch("pygitgo.commands.staging.success")
123
+ def test_selective_stage_single_file(mock_success, mock_run_command):
124
+ selective_stage(["README.md"])
125
+
126
+ mock_run_command.assert_called_once_with(["git", "add", "--", "README.md"])
127
+ mock_success.assert_called_once()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes