pygitgo 1.4.0__tar.gz → 1.6.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.
- {pygitgo-1.4.0/src/pygitgo.egg-info → pygitgo-1.6.0}/PKG-INFO +19 -1
- {pygitgo-1.4.0 → pygitgo-1.6.0}/README.md +17 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/pyproject.toml +2 -1
- pygitgo-1.6.0/src/pygitgo/commands/staging.py +50 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/main.py +51 -17
- pygitgo-1.6.0/src/pygitgo/utils/update_checker.py +97 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0/src/pygitgo.egg-info}/PKG-INFO +19 -1
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo.egg-info/SOURCES.txt +4 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo.egg-info/requires.txt +1 -0
- pygitgo-1.6.0/tests/test_staging.py +127 -0
- pygitgo-1.6.0/tests/test_update_checker.py +143 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/LICENSE +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/setup.cfg +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/__init__.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/auth/__init__.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/auth/account.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/auth/manager.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/auth/ssh_utils.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/commands/__init__.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/commands/git_operations.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/commands/jump.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/commands/pull.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/commands/state.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/commands/undo.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/exceptions.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/utils/__init__.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/utils/colors.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/utils/config.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/utils/executor.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/utils/platform_utils.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo/utils/setup.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo.egg-info/dependency_links.txt +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo.egg-info/entry_points.txt +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/src/pygitgo.egg-info/top_level.txt +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_config.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_git_operations.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_jump.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_main.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_platform.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_pull.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_setup.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_state.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.0}/tests/test_undo.py +0 -0
- {pygitgo-1.4.0 → pygitgo-1.6.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.
|
|
3
|
+
Version: 1.6.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"
|
|
@@ -30,6 +31,7 @@ Dynamic: license-file
|
|
|
30
31
|
[](https://pypi.org/project/pygitgo)
|
|
31
32
|
[](https://www.python.org/downloads/)
|
|
32
33
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
34
|
+
[](https://ko-fi.com/huerte)
|
|
33
35
|
[](https://github.com/Huerte/GitGo)
|
|
34
36
|
|
|
35
37
|
**Stop typing the same five Git commands. Run one instead.**
|
|
@@ -80,6 +82,7 @@ GitGo provides a CLI environment designed for faster and simpler Git workflows.
|
|
|
80
82
|
- **Smart Branch Hopping:** Safely traverse branches with `jump`. Auto-stashes messy code and prevents merge conflict disasters with a Try-And-Revert safety engine.
|
|
81
83
|
- **State Management:** A human-readable interface over `git stash`. States are named and listed by index so you never have to remember cryptic stash references.
|
|
82
84
|
- **Custom Defaults:** Save your preferred branch name and commit message locally so you never have to type them again.
|
|
85
|
+
- **Auto-Update Checker:** Silently checks for newer versions in the background and notifies you, without slowing down your commands.
|
|
83
86
|
- **SSH Auto-Setup:** Generates an SSH key, adds it to `ssh-agent`, and opens your GitHub settings automatically.
|
|
84
87
|
- **HTTPS to SSH Conversion:** Silently converts the remote to SSH before pushing if your SSH is configured.
|
|
85
88
|
- **Termux Compatibility:** Works natively on Android natively handling common issues like dubious ownership errors.
|
|
@@ -182,6 +185,7 @@ Stages all changes, commits, and pushes in one command.
|
|
|
182
185
|
```bash
|
|
183
186
|
gitgo push [branch] [message]
|
|
184
187
|
gitgo push -n [branch] [message] # create new branch first
|
|
188
|
+
gitgo push -s [branch] [message] # interactively select files to stage
|
|
185
189
|
```
|
|
186
190
|
|
|
187
191
|
> [!TIP]
|
|
@@ -190,6 +194,7 @@ gitgo push -n [branch] [message] # create new branch first
|
|
|
190
194
|
| Flag | Description |
|
|
191
195
|
|------|-------------|
|
|
192
196
|
| `-n`, `--new` | Create a new branch before pushing |
|
|
197
|
+
| `-s`, `--select` | Interactively select which files you want to include in this push |
|
|
193
198
|
|
|
194
199
|
If there are no new changes but unpushed commits exist, GitGo detects this and pushes without creating an empty commit.
|
|
195
200
|
|
|
@@ -278,6 +283,7 @@ gitgo -r # verify GitGo is ready
|
|
|
278
283
|
|
|
279
284
|
- **SSH Auto-Setup:** `gitgo user login` generates an `ed25519` SSH key, adds it to `ssh-agent`, prints the public key, and opens `github.com/settings/ssh/new`.
|
|
280
285
|
- **HTTPS to SSH Conversion:** If your remote is set to HTTPS, GitGo converts the remote to SSH before pushing if SSH is configured. No `git remote set-url` is required.
|
|
286
|
+
- **Auto-Update Checker:** Spawns a non-blocking background thread on startup to query PyPI for newer versions. Results are cached locally for 7 days to prevent unnecessary network requests.
|
|
281
287
|
- **Termux Compatibility:** Detects Termux via environment variables, adjusts binary locations (`$PREFIX/bin`), uses `termux-open` for browser actions, and natively handles the `detected dubious ownership` Git error.
|
|
282
288
|
- **State Management:** `gitgo state` wraps `git stash` with named saves, indexed listing, and confirmation prompts.
|
|
283
289
|
|
|
@@ -308,6 +314,18 @@ Contributions are welcome and appreciated!
|
|
|
308
314
|
|
|
309
315
|
---
|
|
310
316
|
|
|
317
|
+
## Support
|
|
318
|
+
|
|
319
|
+
If GitGo saves you time, consider buying me a coffee. It helps keep the project going.
|
|
320
|
+
|
|
321
|
+
<div align="center">
|
|
322
|
+
<a href="https://ko-fi.com/huerte">
|
|
323
|
+
<img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Support on Ko-fi" />
|
|
324
|
+
</a>
|
|
325
|
+
</div>
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
311
329
|
## License
|
|
312
330
|
|
|
313
331
|
Distributed under the **GPLv3** License. See [`LICENSE`](LICENSE) for details.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://pypi.org/project/pygitgo)
|
|
8
8
|
[](https://www.python.org/downloads/)
|
|
9
9
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
10
|
+
[](https://ko-fi.com/huerte)
|
|
10
11
|
[](https://github.com/Huerte/GitGo)
|
|
11
12
|
|
|
12
13
|
**Stop typing the same five Git commands. Run one instead.**
|
|
@@ -57,6 +58,7 @@ GitGo provides a CLI environment designed for faster and simpler Git workflows.
|
|
|
57
58
|
- **Smart Branch Hopping:** Safely traverse branches with `jump`. Auto-stashes messy code and prevents merge conflict disasters with a Try-And-Revert safety engine.
|
|
58
59
|
- **State Management:** A human-readable interface over `git stash`. States are named and listed by index so you never have to remember cryptic stash references.
|
|
59
60
|
- **Custom Defaults:** Save your preferred branch name and commit message locally so you never have to type them again.
|
|
61
|
+
- **Auto-Update Checker:** Silently checks for newer versions in the background and notifies you, without slowing down your commands.
|
|
60
62
|
- **SSH Auto-Setup:** Generates an SSH key, adds it to `ssh-agent`, and opens your GitHub settings automatically.
|
|
61
63
|
- **HTTPS to SSH Conversion:** Silently converts the remote to SSH before pushing if your SSH is configured.
|
|
62
64
|
- **Termux Compatibility:** Works natively on Android natively handling common issues like dubious ownership errors.
|
|
@@ -159,6 +161,7 @@ Stages all changes, commits, and pushes in one command.
|
|
|
159
161
|
```bash
|
|
160
162
|
gitgo push [branch] [message]
|
|
161
163
|
gitgo push -n [branch] [message] # create new branch first
|
|
164
|
+
gitgo push -s [branch] [message] # interactively select files to stage
|
|
162
165
|
```
|
|
163
166
|
|
|
164
167
|
> [!TIP]
|
|
@@ -167,6 +170,7 @@ gitgo push -n [branch] [message] # create new branch first
|
|
|
167
170
|
| Flag | Description |
|
|
168
171
|
|------|-------------|
|
|
169
172
|
| `-n`, `--new` | Create a new branch before pushing |
|
|
173
|
+
| `-s`, `--select` | Interactively select which files you want to include in this push |
|
|
170
174
|
|
|
171
175
|
If there are no new changes but unpushed commits exist, GitGo detects this and pushes without creating an empty commit.
|
|
172
176
|
|
|
@@ -255,6 +259,7 @@ gitgo -r # verify GitGo is ready
|
|
|
255
259
|
|
|
256
260
|
- **SSH Auto-Setup:** `gitgo user login` generates an `ed25519` SSH key, adds it to `ssh-agent`, prints the public key, and opens `github.com/settings/ssh/new`.
|
|
257
261
|
- **HTTPS to SSH Conversion:** If your remote is set to HTTPS, GitGo converts the remote to SSH before pushing if SSH is configured. No `git remote set-url` is required.
|
|
262
|
+
- **Auto-Update Checker:** Spawns a non-blocking background thread on startup to query PyPI for newer versions. Results are cached locally for 7 days to prevent unnecessary network requests.
|
|
258
263
|
- **Termux Compatibility:** Detects Termux via environment variables, adjusts binary locations (`$PREFIX/bin`), uses `termux-open` for browser actions, and natively handles the `detected dubious ownership` Git error.
|
|
259
264
|
- **State Management:** `gitgo state` wraps `git stash` with named saves, indexed listing, and confirmation prompts.
|
|
260
265
|
|
|
@@ -285,6 +290,18 @@ Contributions are welcome and appreciated!
|
|
|
285
290
|
|
|
286
291
|
---
|
|
287
292
|
|
|
293
|
+
## Support
|
|
294
|
+
|
|
295
|
+
If GitGo saves you time, consider buying me a coffee. It helps keep the project going.
|
|
296
|
+
|
|
297
|
+
<div align="center">
|
|
298
|
+
<a href="https://ko-fi.com/huerte">
|
|
299
|
+
<img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Support on Ko-fi" />
|
|
300
|
+
</a>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
288
305
|
## License
|
|
289
306
|
|
|
290
307
|
Distributed under the **GPLv3** License. See [`LICENSE`](LICENSE) for details.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pygitgo"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.6.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,9 +2,11 @@ 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
|
|
9
|
+
from pygitgo.utils.update_checker import check_for_updates_background
|
|
8
10
|
from pygitgo.utils.setup import ensure_first_run_setup
|
|
9
11
|
from pygitgo.commands.state import state_operations
|
|
10
12
|
from pygitgo.commands.undo import undo_operations
|
|
@@ -100,6 +102,7 @@ def link_operation(args):
|
|
|
100
102
|
def push_operation(args):
|
|
101
103
|
branch = args.branch
|
|
102
104
|
message = args.message
|
|
105
|
+
select = args.select if hasattr(args, 'select') else False
|
|
103
106
|
|
|
104
107
|
if args.new:
|
|
105
108
|
if not branch:
|
|
@@ -129,24 +132,52 @@ def push_operation(args):
|
|
|
129
132
|
message = get_config("default-message", "New Project Update")
|
|
130
133
|
info(f"No commit message provided. Using default: '{message}'\n")
|
|
131
134
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
if select:
|
|
136
|
+
files = get_changed_files()
|
|
137
|
+
if not files:
|
|
138
|
+
info("\nWorking tree is clean. Nothing to select.")
|
|
139
|
+
warning("Make some changes first before using GitGo to commit and push.")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
selected = display_file_picker(files)
|
|
143
|
+
if not selected:
|
|
144
|
+
info("\nNo files selected. Push aborted.\n")
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
selective_stage(selected)
|
|
148
|
+
clean_message = message.strip('"\'')
|
|
149
|
+
run_command(["git", "commit", "-m", clean_message], loading_msg="Commiting changes...")
|
|
135
150
|
git_push(branch)
|
|
151
|
+
|
|
152
|
+
leftover_files = [f for f in files if f["path"] not in selected]
|
|
153
|
+
if leftover_files:
|
|
154
|
+
print()
|
|
155
|
+
warning(f"You have {len(leftover_files)} uncommitted file(s) left in your working directory:")
|
|
156
|
+
for f in leftover_files:
|
|
157
|
+
info(f" - {f['path']}")
|
|
158
|
+
print()
|
|
159
|
+
save_choice = input("Would you like to save these leftovers to a GitGo State for later? (y/n): ").lower()
|
|
160
|
+
if save_choice == 'y':
|
|
161
|
+
state_operations(Namespace(action="save", identifier=f"Leftovers from: {clean_message}"))
|
|
136
162
|
else:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
163
|
+
commit_made = git_commit(message)
|
|
164
|
+
|
|
165
|
+
if commit_made:
|
|
166
|
+
git_push(branch)
|
|
167
|
+
else:
|
|
168
|
+
try:
|
|
169
|
+
unpushed = run_command(["git", "log", "--oneline", f"origin/{branch}..HEAD"], allow_fail=True, loading_msg="Checking for unpushed commits...")
|
|
170
|
+
if not isinstance(unpushed, subprocess.CalledProcessError) and unpushed.strip():
|
|
171
|
+
warning("\nNo changes to commit, but found unpushed commits. Pushing to remote...")
|
|
172
|
+
git_push(branch)
|
|
173
|
+
else:
|
|
174
|
+
info("\nWorking tree is clean and everything is up to date.")
|
|
175
|
+
warning("Make some changes first before using GitGo to commit and push.")
|
|
176
|
+
return
|
|
177
|
+
except (GitCommandError, Exception):
|
|
178
|
+
warning("\nNo changes to commit. Cannot verify remote status.")
|
|
179
|
+
warning("Make some changes first or check your git remote configuration.")
|
|
145
180
|
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
181
|
|
|
151
182
|
print("\n" + ("=" * 90))
|
|
152
183
|
success("MISSION COMPLETE — NO CASUALTIES. ALL TARGETS NEUTRALIZED.\nAWAITING FOR YOUR NEXT ORDERS.\n\n")
|
|
@@ -195,7 +226,7 @@ def main():
|
|
|
195
226
|
epilog="Use 'gitgo <command> -h' for help on a specific command."
|
|
196
227
|
)
|
|
197
228
|
|
|
198
|
-
parser.add_argument("-v", "-V", "--version", action="version", version=f"GitGo {get_version()}")
|
|
229
|
+
parser.add_argument("-v", "-V", "--version", action="version", version=f"GitGo {get_version()}\nSupport GitGo: https://ko-fi.com/huerte")
|
|
199
230
|
parser.add_argument("-r", "--ready", action="store_true", help="Check tool readiness")
|
|
200
231
|
|
|
201
232
|
subparsers = parser.add_subparsers(title="Commands", dest="command")
|
|
@@ -216,11 +247,13 @@ def main():
|
|
|
216
247
|
" gitgo push Push current branch with default message\n"
|
|
217
248
|
" gitgo push main 'fix auth bug' Push to main with a custom message\n"
|
|
218
249
|
" 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"
|
|
250
|
+
" gitgo push -n feature/login 'add login' Create new branch and push\n"
|
|
251
|
+
" gitgo push -s main 'fix bug' Pick which files to include before pushing"
|
|
220
252
|
),
|
|
221
253
|
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
222
254
|
)
|
|
223
255
|
push_parser.add_argument("-n", "--new", action="store_true", help="Create a new branch before pushing")
|
|
256
|
+
push_parser.add_argument("-s", "--select", action="store_true", help="Interactively select which files to stage")
|
|
224
257
|
push_parser.add_argument("branch", nargs="?", default=None, help="Branch to push to (default: current branch)")
|
|
225
258
|
push_parser.add_argument("message", nargs="?", default=None, help="Commit message")
|
|
226
259
|
|
|
@@ -305,6 +338,7 @@ def main():
|
|
|
305
338
|
sys.exit(0)
|
|
306
339
|
|
|
307
340
|
ensure_first_run_setup()
|
|
341
|
+
check_for_updates_background(get_version())
|
|
308
342
|
|
|
309
343
|
try:
|
|
310
344
|
if args.command == "jump":
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import threading
|
|
3
|
+
import urllib.request
|
|
4
|
+
import urllib.error
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from pygitgo.utils.colors import info, warning
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
CACHE_DIR = Path.home() / ".gitgo"
|
|
12
|
+
CACHE_FILE = CACHE_DIR / "update_check.json"
|
|
13
|
+
PYPI_URL = "https://pypi.org/pypi/pygitgo/json"
|
|
14
|
+
CHECK_INTERVAL = timedelta(days=7)
|
|
15
|
+
REQUEST_TIMEOUT = 2
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def read_cache():
|
|
19
|
+
if not CACHE_FILE.exists():
|
|
20
|
+
return {}
|
|
21
|
+
try:
|
|
22
|
+
with open(CACHE_FILE, "r") as f:
|
|
23
|
+
return json.load(f)
|
|
24
|
+
except (json.JSONDecodeError, IOError):
|
|
25
|
+
return {}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def write_cache(data):
|
|
29
|
+
try:
|
|
30
|
+
CACHE_DIR.mkdir(exist_ok=True)
|
|
31
|
+
with open(CACHE_FILE, "w") as f:
|
|
32
|
+
json.dump(data, f)
|
|
33
|
+
except IOError:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def should_check():
|
|
38
|
+
cache = read_cache()
|
|
39
|
+
last_check = cache.get("last_check")
|
|
40
|
+
|
|
41
|
+
if not last_check:
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
last_check_time = datetime.fromisoformat(last_check)
|
|
46
|
+
return (datetime.now() - last_check_time) > CHECK_INTERVAL
|
|
47
|
+
except ValueError:
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_latest_version():
|
|
52
|
+
try:
|
|
53
|
+
with urllib.request.urlopen(PYPI_URL, timeout=REQUEST_TIMEOUT) as response:
|
|
54
|
+
data = json.loads(response.read().decode("utf-8"))
|
|
55
|
+
return data["info"]["version"]
|
|
56
|
+
except Exception:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def parse_version(version_string):
|
|
61
|
+
parts = [int(x) for x in version_string.split(".")]
|
|
62
|
+
while len(parts) < 3:
|
|
63
|
+
parts.append(0)
|
|
64
|
+
return parts
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def check_for_updates(current_version):
|
|
68
|
+
if not should_check():
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
cache = read_cache()
|
|
72
|
+
cache["last_check"] = datetime.now().isoformat()
|
|
73
|
+
write_cache(cache)
|
|
74
|
+
|
|
75
|
+
latest_version = get_latest_version()
|
|
76
|
+
if not latest_version or latest_version == current_version:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
current_parts = parse_version(current_version)
|
|
81
|
+
latest_parts = parse_version(latest_version)
|
|
82
|
+
|
|
83
|
+
if latest_parts > current_parts:
|
|
84
|
+
print()
|
|
85
|
+
warning(f"GitGo update available: {current_version} -> {latest_version}")
|
|
86
|
+
info("Run: pip install --upgrade pygitgo")
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
cache["notified_version"] = latest_version
|
|
90
|
+
write_cache(cache)
|
|
91
|
+
except (ValueError, AttributeError):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def check_for_updates_background(current_version):
|
|
96
|
+
thread = threading.Thread(target=check_for_updates, args=(current_version,), daemon=True)
|
|
97
|
+
thread.start()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygitgo
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.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"
|
|
@@ -30,6 +31,7 @@ Dynamic: license-file
|
|
|
30
31
|
[](https://pypi.org/project/pygitgo)
|
|
31
32
|
[](https://www.python.org/downloads/)
|
|
32
33
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
34
|
+
[](https://ko-fi.com/huerte)
|
|
33
35
|
[](https://github.com/Huerte/GitGo)
|
|
34
36
|
|
|
35
37
|
**Stop typing the same five Git commands. Run one instead.**
|
|
@@ -80,6 +82,7 @@ GitGo provides a CLI environment designed for faster and simpler Git workflows.
|
|
|
80
82
|
- **Smart Branch Hopping:** Safely traverse branches with `jump`. Auto-stashes messy code and prevents merge conflict disasters with a Try-And-Revert safety engine.
|
|
81
83
|
- **State Management:** A human-readable interface over `git stash`. States are named and listed by index so you never have to remember cryptic stash references.
|
|
82
84
|
- **Custom Defaults:** Save your preferred branch name and commit message locally so you never have to type them again.
|
|
85
|
+
- **Auto-Update Checker:** Silently checks for newer versions in the background and notifies you, without slowing down your commands.
|
|
83
86
|
- **SSH Auto-Setup:** Generates an SSH key, adds it to `ssh-agent`, and opens your GitHub settings automatically.
|
|
84
87
|
- **HTTPS to SSH Conversion:** Silently converts the remote to SSH before pushing if your SSH is configured.
|
|
85
88
|
- **Termux Compatibility:** Works natively on Android natively handling common issues like dubious ownership errors.
|
|
@@ -182,6 +185,7 @@ Stages all changes, commits, and pushes in one command.
|
|
|
182
185
|
```bash
|
|
183
186
|
gitgo push [branch] [message]
|
|
184
187
|
gitgo push -n [branch] [message] # create new branch first
|
|
188
|
+
gitgo push -s [branch] [message] # interactively select files to stage
|
|
185
189
|
```
|
|
186
190
|
|
|
187
191
|
> [!TIP]
|
|
@@ -190,6 +194,7 @@ gitgo push -n [branch] [message] # create new branch first
|
|
|
190
194
|
| Flag | Description |
|
|
191
195
|
|------|-------------|
|
|
192
196
|
| `-n`, `--new` | Create a new branch before pushing |
|
|
197
|
+
| `-s`, `--select` | Interactively select which files you want to include in this push |
|
|
193
198
|
|
|
194
199
|
If there are no new changes but unpushed commits exist, GitGo detects this and pushes without creating an empty commit.
|
|
195
200
|
|
|
@@ -278,6 +283,7 @@ gitgo -r # verify GitGo is ready
|
|
|
278
283
|
|
|
279
284
|
- **SSH Auto-Setup:** `gitgo user login` generates an `ed25519` SSH key, adds it to `ssh-agent`, prints the public key, and opens `github.com/settings/ssh/new`.
|
|
280
285
|
- **HTTPS to SSH Conversion:** If your remote is set to HTTPS, GitGo converts the remote to SSH before pushing if SSH is configured. No `git remote set-url` is required.
|
|
286
|
+
- **Auto-Update Checker:** Spawns a non-blocking background thread on startup to query PyPI for newer versions. Results are cached locally for 7 days to prevent unnecessary network requests.
|
|
281
287
|
- **Termux Compatibility:** Detects Termux via environment variables, adjusts binary locations (`$PREFIX/bin`), uses `termux-open` for browser actions, and natively handles the `detected dubious ownership` Git error.
|
|
282
288
|
- **State Management:** `gitgo state` wraps `git stash` with named saves, indexed listing, and confirmation prompts.
|
|
283
289
|
|
|
@@ -308,6 +314,18 @@ Contributions are welcome and appreciated!
|
|
|
308
314
|
|
|
309
315
|
---
|
|
310
316
|
|
|
317
|
+
## Support
|
|
318
|
+
|
|
319
|
+
If GitGo saves you time, consider buying me a coffee. It helps keep the project going.
|
|
320
|
+
|
|
321
|
+
<div align="center">
|
|
322
|
+
<a href="https://ko-fi.com/huerte">
|
|
323
|
+
<img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Support on Ko-fi" />
|
|
324
|
+
</a>
|
|
325
|
+
</div>
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
311
329
|
## License
|
|
312
330
|
|
|
313
331
|
Distributed under the **GPLv3** License. See [`LICENSE`](LICENSE) for details.
|
|
@@ -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
|
|
@@ -26,6 +27,7 @@ src/pygitgo/utils/config.py
|
|
|
26
27
|
src/pygitgo/utils/executor.py
|
|
27
28
|
src/pygitgo/utils/platform_utils.py
|
|
28
29
|
src/pygitgo/utils/setup.py
|
|
30
|
+
src/pygitgo/utils/update_checker.py
|
|
29
31
|
tests/test_config.py
|
|
30
32
|
tests/test_git_operations.py
|
|
31
33
|
tests/test_jump.py
|
|
@@ -33,6 +35,8 @@ tests/test_main.py
|
|
|
33
35
|
tests/test_platform.py
|
|
34
36
|
tests/test_pull.py
|
|
35
37
|
tests/test_setup.py
|
|
38
|
+
tests/test_staging.py
|
|
36
39
|
tests/test_state.py
|
|
37
40
|
tests/test_undo.py
|
|
41
|
+
tests/test_update_checker.py
|
|
38
42
|
tests/test_url_validator.py
|
|
@@ -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()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from pygitgo.utils.update_checker import (
|
|
2
|
+
read_cache, write_cache, should_check, get_latest_version,
|
|
3
|
+
parse_version, check_for_updates, check_for_updates_background,
|
|
4
|
+
CACHE_FILE
|
|
5
|
+
)
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
from unittest.mock import patch, MagicMock
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_read_cache_empty(tmp_path, mocker):
|
|
12
|
+
mocker.patch("pygitgo.utils.update_checker.CACHE_FILE", tmp_path / "missing.json")
|
|
13
|
+
assert read_cache() == {}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_read_cache_valid(tmp_path, mocker):
|
|
17
|
+
cache_file = tmp_path / "update_check.json"
|
|
18
|
+
cache_file.write_text(json.dumps({"last_check": "2026-01-01T00:00:00"}))
|
|
19
|
+
mocker.patch("pygitgo.utils.update_checker.CACHE_FILE", cache_file)
|
|
20
|
+
result = read_cache()
|
|
21
|
+
assert result["last_check"] == "2026-01-01T00:00:00"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_read_cache_corrupted(tmp_path, mocker):
|
|
25
|
+
cache_file = tmp_path / "update_check.json"
|
|
26
|
+
cache_file.write_text("not json")
|
|
27
|
+
mocker.patch("pygitgo.utils.update_checker.CACHE_FILE", cache_file)
|
|
28
|
+
assert read_cache() == {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_write_cache(tmp_path, mocker):
|
|
32
|
+
cache_file = tmp_path / "update_check.json"
|
|
33
|
+
mocker.patch("pygitgo.utils.update_checker.CACHE_DIR", tmp_path)
|
|
34
|
+
mocker.patch("pygitgo.utils.update_checker.CACHE_FILE", cache_file)
|
|
35
|
+
write_cache({"last_check": "2026-01-01T00:00:00"})
|
|
36
|
+
result = json.loads(cache_file.read_text())
|
|
37
|
+
assert result["last_check"] == "2026-01-01T00:00:00"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_should_check_no_cache(mocker):
|
|
41
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={})
|
|
42
|
+
assert should_check() is True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_should_check_expired(mocker):
|
|
46
|
+
old_date = (datetime.now() - timedelta(days=8)).isoformat()
|
|
47
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={"last_check": old_date})
|
|
48
|
+
assert should_check() is True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_should_check_still_valid(mocker):
|
|
52
|
+
recent_date = (datetime.now() - timedelta(days=1)).isoformat()
|
|
53
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={"last_check": recent_date})
|
|
54
|
+
assert should_check() is False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_should_check_invalid_date(mocker):
|
|
58
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={"last_check": "not-a-date"})
|
|
59
|
+
assert should_check() is True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_parse_version_standard():
|
|
63
|
+
assert parse_version("1.5.0") == [1, 5, 0]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_parse_version_short():
|
|
67
|
+
assert parse_version("2.0") == [2, 0, 0]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_parse_version_comparison():
|
|
71
|
+
assert parse_version("1.6.0") > parse_version("1.5.0")
|
|
72
|
+
assert parse_version("2.0.0") > parse_version("1.9.9")
|
|
73
|
+
assert parse_version("1.5.0") == parse_version("1.5.0")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_get_latest_version_success(mocker):
|
|
77
|
+
fake_response = MagicMock()
|
|
78
|
+
fake_response.read.return_value = json.dumps({"info": {"version": "1.6.0"}}).encode("utf-8")
|
|
79
|
+
fake_response.__enter__ = lambda s: s
|
|
80
|
+
fake_response.__exit__ = MagicMock(return_value=False)
|
|
81
|
+
mocker.patch("pygitgo.utils.update_checker.urllib.request.urlopen", return_value=fake_response)
|
|
82
|
+
assert get_latest_version() == "1.6.0"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_get_latest_version_network_failure(mocker):
|
|
86
|
+
mocker.patch("pygitgo.utils.update_checker.urllib.request.urlopen", side_effect=Exception("timeout"))
|
|
87
|
+
assert get_latest_version() is None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_check_for_updates_skips_when_cached(mocker):
|
|
91
|
+
mocker.patch("pygitgo.utils.update_checker.should_check", return_value=False)
|
|
92
|
+
fake_get = mocker.patch("pygitgo.utils.update_checker.get_latest_version")
|
|
93
|
+
check_for_updates("1.5.0")
|
|
94
|
+
fake_get.assert_not_called()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_check_for_updates_no_update_available(mocker):
|
|
98
|
+
mocker.patch("pygitgo.utils.update_checker.should_check", return_value=True)
|
|
99
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={})
|
|
100
|
+
mocker.patch("pygitgo.utils.update_checker.write_cache")
|
|
101
|
+
mocker.patch("pygitgo.utils.update_checker.get_latest_version", return_value="1.5.0")
|
|
102
|
+
fake_warning = mocker.patch("pygitgo.utils.update_checker.warning")
|
|
103
|
+
check_for_updates("1.5.0")
|
|
104
|
+
fake_warning.assert_not_called()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_check_for_updates_newer_version_available(mocker):
|
|
108
|
+
mocker.patch("pygitgo.utils.update_checker.should_check", return_value=True)
|
|
109
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={})
|
|
110
|
+
mocker.patch("pygitgo.utils.update_checker.write_cache")
|
|
111
|
+
mocker.patch("pygitgo.utils.update_checker.get_latest_version", return_value="2.0.0")
|
|
112
|
+
fake_warning = mocker.patch("pygitgo.utils.update_checker.warning")
|
|
113
|
+
fake_info = mocker.patch("pygitgo.utils.update_checker.info")
|
|
114
|
+
check_for_updates("1.5.0")
|
|
115
|
+
fake_warning.assert_called_with("GitGo update available: 1.5.0 -> 2.0.0")
|
|
116
|
+
fake_info.assert_called_with("Run: pip install --upgrade pygitgo")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_check_for_updates_older_version_on_pypi(mocker):
|
|
120
|
+
mocker.patch("pygitgo.utils.update_checker.should_check", return_value=True)
|
|
121
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={})
|
|
122
|
+
mocker.patch("pygitgo.utils.update_checker.write_cache")
|
|
123
|
+
mocker.patch("pygitgo.utils.update_checker.get_latest_version", return_value="1.4.0")
|
|
124
|
+
fake_warning = mocker.patch("pygitgo.utils.update_checker.warning")
|
|
125
|
+
check_for_updates("1.5.0")
|
|
126
|
+
fake_warning.assert_not_called()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_check_for_updates_pypi_returns_none(mocker):
|
|
130
|
+
mocker.patch("pygitgo.utils.update_checker.should_check", return_value=True)
|
|
131
|
+
mocker.patch("pygitgo.utils.update_checker.read_cache", return_value={})
|
|
132
|
+
mocker.patch("pygitgo.utils.update_checker.write_cache")
|
|
133
|
+
mocker.patch("pygitgo.utils.update_checker.get_latest_version", return_value=None)
|
|
134
|
+
fake_warning = mocker.patch("pygitgo.utils.update_checker.warning")
|
|
135
|
+
check_for_updates("1.5.0")
|
|
136
|
+
fake_warning.assert_not_called()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_check_for_updates_background_starts_thread(mocker):
|
|
140
|
+
fake_thread = MagicMock()
|
|
141
|
+
mocker.patch("pygitgo.utils.update_checker.threading.Thread", return_value=fake_thread)
|
|
142
|
+
check_for_updates_background("1.5.0")
|
|
143
|
+
fake_thread.start.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
|
|
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
|
|
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
|