ezgit-sync 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.
- autogit/__init__.py +1 -0
- autogit/__main__.py +4 -0
- autogit/autogit.py +139 -0
- autogit/cli.py +60 -0
- autogit/py.typed +1 -0
- autogit/utils.py +2 -0
- ezgit_sync-0.1.0.dist-info/METADATA +81 -0
- ezgit_sync-0.1.0.dist-info/RECORD +11 -0
- ezgit_sync-0.1.0.dist-info/WHEEL +4 -0
- ezgit_sync-0.1.0.dist-info/entry_points.txt +2 -0
- ezgit_sync-0.1.0.dist-info/licenses/LICENSE +21 -0
autogit/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Top-level package for auto_git."""
|
autogit/__main__.py
ADDED
autogit/autogit.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Main module for autogit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# ANSI Escape Sequences for Terminal Styling
|
|
10
|
+
COLOR_RESET = "\033[0m"
|
|
11
|
+
BOLD = "\033[1m"
|
|
12
|
+
DIM = "\033[2m"
|
|
13
|
+
|
|
14
|
+
COLOR_GREEN = "\033[92m"
|
|
15
|
+
COLOR_YELLOW = "\033[93m"
|
|
16
|
+
COLOR_RED = "\033[91m"
|
|
17
|
+
COLOR_CYAN = "\033[96m"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def log_success(message: str) -> None:
|
|
21
|
+
"""Prints a styled success message."""
|
|
22
|
+
print(f"{COLOR_GREEN}{BOLD}[SUCCESS]{COLOR_RESET} {message}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def log_warning(message: str) -> None:
|
|
26
|
+
"""Prints a styled warning message."""
|
|
27
|
+
print(f"{COLOR_YELLOW}{BOLD}[WARNING]{COLOR_RESET} {message}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def log_info(message: str) -> None:
|
|
31
|
+
"""Prints a styled info message."""
|
|
32
|
+
print(f"{COLOR_CYAN}{BOLD}[INFO]{COLOR_RESET} {message}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def log_error_with_reasons(title: str, reasons: list[str], raw_error: str | None = None) -> None:
|
|
36
|
+
"""Formats and prints error explanations with probable causes and solutions."""
|
|
37
|
+
sys.stderr.write(f"\n{COLOR_RED}{BOLD}[ERROR] {title}{COLOR_RESET}\n")
|
|
38
|
+
sys.stderr.write(f"{BOLD}Possible Reasons & Solutions:{COLOR_RESET}\n")
|
|
39
|
+
for reason in reasons:
|
|
40
|
+
sys.stderr.write(f" • {reason}\n")
|
|
41
|
+
|
|
42
|
+
if raw_error:
|
|
43
|
+
sys.stderr.write(f"\n{DIM}Raw Git Error Details:\n{raw_error.strip()}{COLOR_RESET}\n")
|
|
44
|
+
sys.stderr.write("\n")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run_git(args: list[str], repo_root: Path) -> str:
|
|
48
|
+
"""Executes a git command and returns its stripped stdout."""
|
|
49
|
+
result = subprocess.run(
|
|
50
|
+
["git", *args],
|
|
51
|
+
cwd=repo_root,
|
|
52
|
+
check=True,
|
|
53
|
+
capture_output=True,
|
|
54
|
+
text=True,
|
|
55
|
+
)
|
|
56
|
+
return result.stdout.strip()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def verify_git_repository(repo_root: Path) -> bool:
|
|
60
|
+
"""Checks if the current working directory is inside a valid Git repository."""
|
|
61
|
+
try:
|
|
62
|
+
run_git(["rev-parse", "--is-inside-work-tree"], repo_root)
|
|
63
|
+
return True
|
|
64
|
+
except subprocess.CalledProcessError:
|
|
65
|
+
log_error_with_reasons(
|
|
66
|
+
title=f"The directory '{repo_root.name}' is not a valid Git repository.",
|
|
67
|
+
reasons=[
|
|
68
|
+
"Git is not initialized in this directory. Run 'git init' to create a new repository.",
|
|
69
|
+
"You might be running the command in the wrong folder. Navigate to your project root.",
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_current_branch(repo_root: Path) -> str:
|
|
76
|
+
"""Retrieves the name of the currently active branch."""
|
|
77
|
+
try:
|
|
78
|
+
branch = run_git(["rev-parse", "--abbrev-ref", "HEAD"], repo_root)
|
|
79
|
+
if branch == "HEAD":
|
|
80
|
+
raise RuntimeError("DETACHED_HEAD")
|
|
81
|
+
return branch
|
|
82
|
+
except (subprocess.CalledProcessError, RuntimeError) as exc:
|
|
83
|
+
raw_err = exc.stderr if isinstance(exc, subprocess.CalledProcessError) else str(exc)
|
|
84
|
+
log_error_with_reasons(
|
|
85
|
+
title="Failed to determine active Git branch.",
|
|
86
|
+
reasons=[
|
|
87
|
+
"This repository might be completely empty (no commits yet). Create an initial commit first: 'git add . && git commit -m \"initial commit\"'",
|
|
88
|
+
"Your repository might be in a 'detached HEAD' state. Switch to a branch using 'git checkout <branch-name>'.",
|
|
89
|
+
],
|
|
90
|
+
raw_error=raw_err,
|
|
91
|
+
)
|
|
92
|
+
raise exc
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def pull_remote_changes(repo_root: Path, branch: str) -> bool:
|
|
96
|
+
"""Pulls recent changes from the remote repository."""
|
|
97
|
+
log_info(f"Pulling latest updates from origin/{branch}...")
|
|
98
|
+
try:
|
|
99
|
+
run_git(["pull", "origin", branch], repo_root)
|
|
100
|
+
return True
|
|
101
|
+
except subprocess.CalledProcessError as exc:
|
|
102
|
+
log_error_with_reasons(
|
|
103
|
+
title=f"Failed to pull changes from origin/{branch}.",
|
|
104
|
+
reasons=[
|
|
105
|
+
"There might be merge conflicts between local and remote files. Resolve conflicts manually.",
|
|
106
|
+
"The remote branch 'origin/{branch}' might not exist yet on the remote server.",
|
|
107
|
+
"You might be offline or lack permission to access the remote repository.",
|
|
108
|
+
],
|
|
109
|
+
raw_error=exc.stderr,
|
|
110
|
+
)
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def has_local_changes(repo_root: Path) -> bool:
|
|
115
|
+
"""Checks if there are uncommitted local modifications."""
|
|
116
|
+
status = run_git(["status", "--porcelain"], repo_root)
|
|
117
|
+
return bool(status)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def sync_local_changes(repo_root: Path, branch: str, commit_message: str) -> None:
|
|
121
|
+
"""Stages, commits, and pushes local changes to the remote repository."""
|
|
122
|
+
log_info("Staging and committing local changes...")
|
|
123
|
+
run_git(["add", "-A"], repo_root)
|
|
124
|
+
run_git(["commit", "-m", commit_message], repo_root)
|
|
125
|
+
|
|
126
|
+
log_info(f"Pushing updates to origin/{branch}...")
|
|
127
|
+
try:
|
|
128
|
+
run_git(["push", "origin", branch], repo_root)
|
|
129
|
+
except subprocess.CalledProcessError as exc:
|
|
130
|
+
log_error_with_reasons(
|
|
131
|
+
title=f"Failed to push changes to origin/{branch}.",
|
|
132
|
+
reasons=[
|
|
133
|
+
"Remote contains commits that you do not have locally. Try pulling first.",
|
|
134
|
+
"You may not have write permissions for this remote repository.",
|
|
135
|
+
"Your authentication credentials (SSH key or GitHub token) might be missing or expired.",
|
|
136
|
+
],
|
|
137
|
+
raw_error=exc.stderr,
|
|
138
|
+
)
|
|
139
|
+
raise exc
|
autogit/cli.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Console script for autogit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from autogit.autogit import (
|
|
10
|
+
get_current_branch,
|
|
11
|
+
has_local_changes,
|
|
12
|
+
log_error,
|
|
13
|
+
log_success,
|
|
14
|
+
log_warning,
|
|
15
|
+
pull_remote_changes,
|
|
16
|
+
sync_local_changes,
|
|
17
|
+
verify_git_repository,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main(argv: list[str] | None = None) -> int:
|
|
22
|
+
"""Console script for autogit."""
|
|
23
|
+
parser = argparse.ArgumentParser(
|
|
24
|
+
description="Pull remote updates, stage, commit, and push the current repository to GitHub."
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"-m",
|
|
28
|
+
"--message",
|
|
29
|
+
default="Sync updates",
|
|
30
|
+
help="Commit message to use when creating the commit.",
|
|
31
|
+
)
|
|
32
|
+
args = parser.parse_args(argv)
|
|
33
|
+
repo_root = Path.cwd()
|
|
34
|
+
|
|
35
|
+
if not verify_git_repository(repo_root):
|
|
36
|
+
log_error(f"The current directory '{repo_root}' is not a valid Git repository.")
|
|
37
|
+
return 1
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
branch = get_current_branch(repo_root)
|
|
41
|
+
|
|
42
|
+
if not pull_remote_changes(repo_root, branch):
|
|
43
|
+
return 1
|
|
44
|
+
|
|
45
|
+
if not has_local_changes(repo_root):
|
|
46
|
+
log_warning("No new local changes to commit after pulling updates.")
|
|
47
|
+
return 0
|
|
48
|
+
|
|
49
|
+
sync_local_changes(repo_root, branch, args.message)
|
|
50
|
+
|
|
51
|
+
log_success(f"Successfully synchronized repository with origin/{branch}.")
|
|
52
|
+
return 0
|
|
53
|
+
|
|
54
|
+
except Exception as exc:
|
|
55
|
+
log_error(f"An error occurred during synchronization: {exc}")
|
|
56
|
+
return 1
|
|
57
|
+
|
|
58
|
+
app = main
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
sys.exit(main())
|
autogit/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file for PEP 561
|
autogit/utils.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ezgit-sync
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: This package is a basic git automator for deep learning and machine learning tasks.
|
|
5
|
+
Project-URL: bugs, https://github.com/hasanaliozkan-dev/autogit/issues
|
|
6
|
+
Project-URL: changelog, https://github.com/hasanaliozkan-dev/autogit/releases
|
|
7
|
+
Project-URL: documentation, https://hasanaliozkan-dev.github.io/autogit/
|
|
8
|
+
Project-URL: homepage, https://github.com/hasanaliozkan-dev/autogit
|
|
9
|
+
Author-email: Hasan Ali Özkan <hasanaliozkan@mu.edu.tr>
|
|
10
|
+
Maintainer-email: Hasan Ali Özkan <hasanaliozkan@mu.edu.tr>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: rich
|
|
18
|
+
Requires-Dist: typer
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# auto_git
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
This package is a basic git automator for deep learning and machine learning tasks.
|
|
26
|
+
|
|
27
|
+
* [GitHub](https://github.com/hasanaliozkan-dev/autogit/) | [PyPI](https://pypi.org/project/autogit/) | [Documentation](https://hasanaliozkan-dev.github.io/autogit/)
|
|
28
|
+
* Created by [Hasan Ali Özkan](https://hasanaliozkan-dev.github.io/) | GitHub [@hasanaliozkan-dev](https://github.com/hasanaliozkan-dev) | PyPI [@hasanaliozkan](https://pypi.org/user/hasanaliozkan/)
|
|
29
|
+
* MIT License
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
* TODO
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Documentation is built with [Zensical](https://zensical.org/) and deployed to GitHub Pages.
|
|
38
|
+
|
|
39
|
+
* **Live site:** https://hasanaliozkan-dev.github.io/autogit/
|
|
40
|
+
* **Preview locally:** `just docs-serve` (serves at http://localhost:8000)
|
|
41
|
+
* **Build:** `just docs-build`
|
|
42
|
+
|
|
43
|
+
API documentation is auto-generated from docstrings using [mkdocstrings](https://mkdocstrings.github.io/).
|
|
44
|
+
|
|
45
|
+
Docs deploy automatically on push to `main` via GitHub Actions. To enable this, go to your repo's Settings > Pages and set the source to **GitHub Actions**.
|
|
46
|
+
|
|
47
|
+
## Development
|
|
48
|
+
|
|
49
|
+
To set up for local development:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
# Clone your fork
|
|
53
|
+
git clone git@github.com:your_username/autogit.git
|
|
54
|
+
cd autogit
|
|
55
|
+
|
|
56
|
+
# Install dependencies
|
|
57
|
+
uv sync
|
|
58
|
+
|
|
59
|
+
# Create a branch for development
|
|
60
|
+
git checkout -b feature/new-awesome-feature
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This installs the CLI globally but with live updates - any changes you make to the source code are immediately available when you run `autogit`.
|
|
64
|
+
|
|
65
|
+
Run tests:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv run pytest
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Run quality checks (format, lint, type check, test):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
just qa
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Author
|
|
78
|
+
|
|
79
|
+
auto_git was created in 2026 by Hasan Ali Özkan.
|
|
80
|
+
|
|
81
|
+
Built with [Cookiecutter](https://github.com/cookiecutter/cookiecutter) and the [audreyfeldroy/cookiecutter-pypackage](https://github.com/audreyfeldroy/cookiecutter-pypackage) project template.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
autogit/__init__.py,sha256=NnmvoOhzsrQhG-0n-m1mVAqsP8U5n7FATgJrPuVtiB0,38
|
|
2
|
+
autogit/__main__.py,sha256=Qd-f8z2Q2vpiEP2x6PBFsJrpACWDVxFKQk820MhFmHo,59
|
|
3
|
+
autogit/autogit.py,sha256=WVykX1qGJyCskO9Phs6-b7C1Wx_0p6BpUyESZXDcJvc,5194
|
|
4
|
+
autogit/cli.py,sha256=pKiWmBagVYzaPWWkOT60iu54mzj1x7V2LD7KU4zE2kc,1557
|
|
5
|
+
autogit/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
|
|
6
|
+
autogit/utils.py,sha256=HppGIYjTTn56wTOArW3kxDDc2bMnrGVBQB2DIaoLnI4,85
|
|
7
|
+
ezgit_sync-0.1.0.dist-info/METADATA,sha256=LlJ7xJX8rc9xk5_IECmemyVj5QubpMfiwg1UtXMgiks,2696
|
|
8
|
+
ezgit_sync-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
9
|
+
ezgit_sync-0.1.0.dist-info/entry_points.txt,sha256=f3gTyjE1mWQc45DlNd5ry0yEQnRRAwl1irLam9cGvTw,45
|
|
10
|
+
ezgit_sync-0.1.0.dist-info/licenses/LICENSE,sha256=OCptpIILuJZlFEE1yk_SKTrMfXvGHgTXSD07s-RM1bI,1074
|
|
11
|
+
ezgit_sync-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Hasan Ali Özkan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|