commitflow 1.0.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.
- commitflow-1.0.0.dist-info/METADATA +216 -0
- commitflow-1.0.0.dist-info/RECORD +19 -0
- commitflow-1.0.0.dist-info/WHEEL +5 -0
- commitflow-1.0.0.dist-info/entry_points.txt +2 -0
- commitflow-1.0.0.dist-info/licenses/LICENSE +21 -0
- commitflow-1.0.0.dist-info/top_level.txt +1 -0
- daily_git_assistant/__init__.py +1 -0
- daily_git_assistant/config.py +126 -0
- daily_git_assistant/git_utils.py +165 -0
- daily_git_assistant/logger.py +105 -0
- daily_git_assistant/main.py +108 -0
- daily_git_assistant/modes/__init__.py +0 -0
- daily_git_assistant/modes/auto.py +118 -0
- daily_git_assistant/modes/interactive.py +172 -0
- daily_git_assistant/modes/quick.py +143 -0
- daily_git_assistant/modes/setup.py +90 -0
- daily_git_assistant/repo_scanner.py +127 -0
- daily_git_assistant/scheduler.py +259 -0
- daily_git_assistant/ui.py +149 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: commitflow
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: CLI tool for maintaining consistent Git commits automatically
|
|
5
|
+
Author: Abhinav Kumar Singh
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: colorama
|
|
10
|
+
Dynamic: author
|
|
11
|
+
Dynamic: description
|
|
12
|
+
Dynamic: description-content-type
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
Dynamic: requires-dist
|
|
15
|
+
Dynamic: requires-python
|
|
16
|
+
Dynamic: summary
|
|
17
|
+
|
|
18
|
+
# CommitFlow
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+

|
|
22
|
+

|
|
23
|
+
<!--
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
-->
|
|
27
|
+
CommitFlow is a lightweight command-line tool that helps developers maintain consistent Git commits automatically without interrupting their workflow .
|
|
28
|
+
It provides a simple interface to commit progress, automate daily commits and keep your GitHub contribution graph active while you focus on actual development.
|
|
29
|
+
|
|
30
|
+
## Why CommitFlow?
|
|
31
|
+
> ### CommitFlow is built to simplify a common developer habit: committing progress regularly.
|
|
32
|
+
Developers often forget to commit progress regularly while working across multiple projects. CommitFlow simplifies this by providing a clean command-line workflow that helps automate and manage daily commits. It is designed to be simple, practical and useful for everyday development workflows.
|
|
33
|
+
> ### If this tool helps your workflow, feel free to star the repository ! It means a lot !!
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
- Automatic repository detection
|
|
37
|
+
- Commit preview before committing
|
|
38
|
+
- Interactive commit workflow
|
|
39
|
+
- Quick commit mode for frequent commits
|
|
40
|
+
- Automatic daily commits (Scheduled runs)
|
|
41
|
+
- Windows Task Scheduler support
|
|
42
|
+
- Linux cron support
|
|
43
|
+
- Configuration file support
|
|
44
|
+
- Logging system
|
|
45
|
+
- Cross-platform terminal color support
|
|
46
|
+
|
|
47
|
+
<!--
|
|
48
|
+
## Usage
|
|
49
|
+
commitflow
|
|
50
|
+
commitflow --quick
|
|
51
|
+
commitflow --auto
|
|
52
|
+
commitflow --setup
|
|
53
|
+
commitflow --schedule
|
|
54
|
+
---
|
|
55
|
+
-->
|
|
56
|
+
## Installation
|
|
57
|
+
Install CommitFlow directly from PyPI:
|
|
58
|
+
```bash
|
|
59
|
+
pip install commitflow
|
|
60
|
+
```
|
|
61
|
+
Verify installation:
|
|
62
|
+
```bash
|
|
63
|
+
commitflow --version
|
|
64
|
+
```
|
|
65
|
+
## Quick Start
|
|
66
|
+
Run CommitFlow in interactive mode. This mode guides you through the commit process step by step.
|
|
67
|
+
```bash
|
|
68
|
+
commitflow
|
|
69
|
+
```
|
|
70
|
+
You will be prompted for:
|
|
71
|
+
- repository path
|
|
72
|
+
- files to add
|
|
73
|
+
- commit message
|
|
74
|
+
- push confirmation
|
|
75
|
+
|
|
76
|
+
CommitFlow shows a preview before committing.
|
|
77
|
+
|
|
78
|
+
## CLI Commands
|
|
79
|
+
### Interactive Mode (Default)
|
|
80
|
+
Run the standard interactive workflow:
|
|
81
|
+
```bash
|
|
82
|
+
commitflow
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Quick Mode
|
|
86
|
+
Use repository settings from your configuration file.
|
|
87
|
+
``` bash
|
|
88
|
+
commitflow --quick
|
|
89
|
+
```
|
|
90
|
+
Quick mode skips repository selection and speeds up the commit process.
|
|
91
|
+
|
|
92
|
+
### Auto Mode
|
|
93
|
+
Fully automatic commit process:
|
|
94
|
+
``` bash
|
|
95
|
+
commitflow --auto
|
|
96
|
+
```
|
|
97
|
+
This mode:
|
|
98
|
+
- adds files automatically
|
|
99
|
+
- commits with a default message
|
|
100
|
+
- optionally pushes to remote
|
|
101
|
+
|
|
102
|
+
Auto mode is mainly used by schedulers.
|
|
103
|
+
|
|
104
|
+
### Setup Wizard
|
|
105
|
+
Run the configuration wizard:
|
|
106
|
+
``` bash
|
|
107
|
+
commitflow --setup
|
|
108
|
+
```
|
|
109
|
+
The setup wizard asks for:
|
|
110
|
+
- repository path
|
|
111
|
+
- automatic push preference
|
|
112
|
+
- default commit message
|
|
113
|
+
- daily schedule time
|
|
114
|
+
|
|
115
|
+
It can also configure the system scheduler automatically.
|
|
116
|
+
|
|
117
|
+
### Scheduler Setup
|
|
118
|
+
Create an automated daily commit task:
|
|
119
|
+
```bash
|
|
120
|
+
commitflow --schedule
|
|
121
|
+
```
|
|
122
|
+
CommitFlow supports:
|
|
123
|
+
- Windows Task Scheduler
|
|
124
|
+
- Linux/macOS cron
|
|
125
|
+
|
|
126
|
+
Scheduler settings such as power conditions and retry behavior can be customized during setup.
|
|
127
|
+
|
|
128
|
+
### Example Workflow
|
|
129
|
+
Typical setup:
|
|
130
|
+
``` bash
|
|
131
|
+
commitflow --setup
|
|
132
|
+
```
|
|
133
|
+
After configuration, CommitFlow can run automatic commits daily.
|
|
134
|
+
|
|
135
|
+
Manual commits can also be executed anytime:
|
|
136
|
+
``` bash
|
|
137
|
+
commitflow --quick
|
|
138
|
+
```
|
|
139
|
+
### Configuration File
|
|
140
|
+
CommitFlow stores configuration in:
|
|
141
|
+
``` bash
|
|
142
|
+
~/.commitflow_config.json
|
|
143
|
+
```
|
|
144
|
+
Example configuration:
|
|
145
|
+
```bash
|
|
146
|
+
{
|
|
147
|
+
"repo": "/projects/my-project",
|
|
148
|
+
"auto_push": true,
|
|
149
|
+
"auto_add": ".",
|
|
150
|
+
"commit_message": "daily progress update"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
### Logs
|
|
154
|
+
CommitFlow keeps logs for debugging and activity tracking.
|
|
155
|
+
Default log location:
|
|
156
|
+
``` bash
|
|
157
|
+
~/.commitflow.log
|
|
158
|
+
```
|
|
159
|
+
Log entries include:
|
|
160
|
+
- commit operations
|
|
161
|
+
- warnings
|
|
162
|
+
- errors
|
|
163
|
+
- scheduler events
|
|
164
|
+
|
|
165
|
+
### Supported Platforms
|
|
166
|
+
- Windows
|
|
167
|
+
- Linux
|
|
168
|
+
<!--
|
|
169
|
+
Schedulers supported:
|
|
170
|
+
- Windows Task Scheduler
|
|
171
|
+
- cron
|
|
172
|
+
-->
|
|
173
|
+
<!--
|
|
174
|
+
Running Tests
|
|
175
|
+
|
|
176
|
+
Install development dependencies:
|
|
177
|
+
|
|
178
|
+
pip install pytest
|
|
179
|
+
|
|
180
|
+
Run tests:
|
|
181
|
+
|
|
182
|
+
pytest
|
|
183
|
+
|
|
184
|
+
GitHub Actions automatically runs tests on each push.
|
|
185
|
+
|
|
186
|
+
Project Structure
|
|
187
|
+
commitflow
|
|
188
|
+
│
|
|
189
|
+
├── daily_git_assistant
|
|
190
|
+
│ ├── main.py
|
|
191
|
+
│ ├── config.py
|
|
192
|
+
│ ├── git_utils.py
|
|
193
|
+
│ ├── logger.py
|
|
194
|
+
│ ├── repo_scanner.py
|
|
195
|
+
│ ├── scheduler.py
|
|
196
|
+
│ ├── ui.py
|
|
197
|
+
│ └── modes
|
|
198
|
+
│
|
|
199
|
+
├── tests
|
|
200
|
+
├── setup.py
|
|
201
|
+
└── README.md
|
|
202
|
+
-->
|
|
203
|
+
|
|
204
|
+
### Contributing
|
|
205
|
+
Contributions are welcome. If you would like to improve CommitFlow:
|
|
206
|
+
- Fork the repository
|
|
207
|
+
- Create a feature branch
|
|
208
|
+
- Submit a pull request
|
|
209
|
+
|
|
210
|
+
Bug reports and feature suggestions are appreciated.
|
|
211
|
+
|
|
212
|
+
### License
|
|
213
|
+
This project is licensed under the MIT License.
|
|
214
|
+
|
|
215
|
+
### Author
|
|
216
|
+
Abhinav Kumar Singh
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
commitflow-1.0.0.dist-info/licenses/LICENSE,sha256=xc-pONOWUH4mK8fw4GjVCS3-4_PvclWW0gsrFOVsPcA,1097
|
|
2
|
+
daily_git_assistant/__init__.py,sha256=Aj77VL1d5Mdku7sgCgKQmPuYavPpAHuZuJcy6bygQZE,21
|
|
3
|
+
daily_git_assistant/config.py,sha256=Nn1FTVziG9eZM4vAnxCPW9E0Hi_RZOUrwn3Wpxcv3Oc,2543
|
|
4
|
+
daily_git_assistant/git_utils.py,sha256=_5hMXH12qpdJDC-z5Dju39X4necC5do5pHfcXvzNJi8,3177
|
|
5
|
+
daily_git_assistant/logger.py,sha256=xVmf-e1FFs4eMCXu4ZR1IE1RB322uvdLtMfKGS_CGIk,1997
|
|
6
|
+
daily_git_assistant/main.py,sha256=tR1huAl0EFe4Jb5ZoK3gFqHcPl1VKYhSIalQ0RBKs0M,2127
|
|
7
|
+
daily_git_assistant/repo_scanner.py,sha256=ad4wXWPcmLmxzPBfOx4ProYu2I0EPqYrihIHiYUy-hU,2581
|
|
8
|
+
daily_git_assistant/scheduler.py,sha256=yipEOEoUbuIjLW2eh6SjrOzE3XXlxyAVu9FC-OIdX2Y,6590
|
|
9
|
+
daily_git_assistant/ui.py,sha256=6KdOQcUa1XfpvbYCYpPDiPpVyAgs9bf0GrEDDgS0WAU,3383
|
|
10
|
+
daily_git_assistant/modes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
daily_git_assistant/modes/auto.py,sha256=qn0z8U14HsrIev_Uw_uaBf_Fr4c1OxtLnX8lZEFPgM8,2650
|
|
12
|
+
daily_git_assistant/modes/interactive.py,sha256=zv1ADexjpnJQjhARJavmAfyfNpDwKfJ8iYXhMZi4hls,3759
|
|
13
|
+
daily_git_assistant/modes/quick.py,sha256=Fli3U4iKPvRarTrlAk0zL8BSrex0-UxKHq6ZtmCuaRw,2961
|
|
14
|
+
daily_git_assistant/modes/setup.py,sha256=02Fs3h9z78I0KvvIXuhHKlq5Px3b4EiSrt4ZpFnaEdk,2214
|
|
15
|
+
commitflow-1.0.0.dist-info/METADATA,sha256=BiJJlNTrF6QjHF8bsnrj9TSg-Dk3REopclefAeQRcbA,5208
|
|
16
|
+
commitflow-1.0.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
17
|
+
commitflow-1.0.0.dist-info/entry_points.txt,sha256=XnAqmSVYYmxDwOR9m3KIoisLIMBpl7i97FLQd_dWMvc,61
|
|
18
|
+
commitflow-1.0.0.dist-info/top_level.txt,sha256=1hLlmrsma5OW_nRh4oaaAjf3DFK1837HWHU0FlL0HMw,20
|
|
19
|
+
commitflow-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhinav Kumar Singh
|
|
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
daily_git_assistant
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
# Default config file location
|
|
6
|
+
CONFIG_PATH = os.path.join(Path.home(), ".commitflow_config.json")
|
|
7
|
+
|
|
8
|
+
# Default configuration values
|
|
9
|
+
DEFAULT_CONFIG = {
|
|
10
|
+
"repo": "",
|
|
11
|
+
"auto_push": False,
|
|
12
|
+
"auto_add": ".",
|
|
13
|
+
"commit_message": "Daily progress update",
|
|
14
|
+
"schedule_time": "21:00"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def create_default_config():
|
|
19
|
+
"""
|
|
20
|
+
Create a config file with default values if it does not exist.
|
|
21
|
+
"""
|
|
22
|
+
if not os.path.exists(CONFIG_PATH):
|
|
23
|
+
try:
|
|
24
|
+
with open(CONFIG_PATH, "w") as f:
|
|
25
|
+
json.dump(DEFAULT_CONFIG, f, indent=4)
|
|
26
|
+
|
|
27
|
+
return DEFAULT_CONFIG
|
|
28
|
+
|
|
29
|
+
except Exception as e:
|
|
30
|
+
raise RuntimeError(f"Failed to create config file: {e}")
|
|
31
|
+
|
|
32
|
+
return load_config()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_config():
|
|
36
|
+
"""
|
|
37
|
+
Load configuration from config file.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
with open(CONFIG_PATH, "r") as f:
|
|
41
|
+
config = json.load(f)
|
|
42
|
+
|
|
43
|
+
return config
|
|
44
|
+
|
|
45
|
+
except FileNotFoundError:
|
|
46
|
+
|
|
47
|
+
return create_default_config()
|
|
48
|
+
|
|
49
|
+
except json.JSONDecodeError:
|
|
50
|
+
|
|
51
|
+
raise RuntimeError(
|
|
52
|
+
"Config file corrupted. Delete ~/.commitflow_config.json and run setup again."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def save_config(config_data):
|
|
57
|
+
"""
|
|
58
|
+
Save updated configuration.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
|
|
63
|
+
with open(CONFIG_PATH, "w") as f:
|
|
64
|
+
json.dump(config_data, f, indent=4)
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
|
|
68
|
+
raise RuntimeError(f"Failed to save config: {e}")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def update_config(key, value):
|
|
72
|
+
"""
|
|
73
|
+
Update a single configuration field.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
config = load_config()
|
|
77
|
+
|
|
78
|
+
config[key] = value
|
|
79
|
+
|
|
80
|
+
save_config(config)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate_repo_path(repo_path):
|
|
84
|
+
"""
|
|
85
|
+
Ensure repository path exists and is valid.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
if not repo_path:
|
|
89
|
+
raise ValueError("Repository path not set in config")
|
|
90
|
+
|
|
91
|
+
if not os.path.exists(repo_path):
|
|
92
|
+
raise ValueError(f"Repository path does not exist: {repo_path}")
|
|
93
|
+
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_repo():
|
|
98
|
+
"""
|
|
99
|
+
Retrieve repository path from config.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
config = load_config()
|
|
103
|
+
|
|
104
|
+
repo = config.get("repo", "")
|
|
105
|
+
|
|
106
|
+
validate_repo_path(repo)
|
|
107
|
+
|
|
108
|
+
return repo
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def reset_config():
|
|
112
|
+
"""
|
|
113
|
+
Reset configuration to default values.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
|
|
118
|
+
with open(CONFIG_PATH, "w") as f:
|
|
119
|
+
|
|
120
|
+
json.dump(DEFAULT_CONFIG, f, indent=4)
|
|
121
|
+
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
except Exception as e:
|
|
125
|
+
|
|
126
|
+
raise RuntimeError(f"Failed to reset config: {e}")
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def run_git_command(cmd, repo_path):
|
|
6
|
+
"""
|
|
7
|
+
Execute a git command safely.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
|
|
12
|
+
result = subprocess.run(
|
|
13
|
+
cmd,
|
|
14
|
+
cwd=repo_path,
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
return result
|
|
20
|
+
|
|
21
|
+
except Exception as e:
|
|
22
|
+
|
|
23
|
+
raise RuntimeError(f"Git command failed: {e}")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_git_repo(repo_path):
|
|
27
|
+
"""
|
|
28
|
+
Validate whether a directory is a git repository.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
if not os.path.exists(repo_path):
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
result = run_git_command(["git", "rev-parse", "--is-inside-work-tree"], repo_path)
|
|
35
|
+
|
|
36
|
+
return result.returncode == 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_repo_name(repo_path):
|
|
40
|
+
"""
|
|
41
|
+
Get repository folder name.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
return os.path.basename(os.path.abspath(repo_path))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_branch(repo_path):
|
|
48
|
+
"""
|
|
49
|
+
Get current git branch.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
result = run_git_command(["git", "branch", "--show-current"], repo_path)
|
|
53
|
+
|
|
54
|
+
if result.returncode != 0:
|
|
55
|
+
return "unknown"
|
|
56
|
+
|
|
57
|
+
return result.stdout.strip()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_remote_url(repo_path):
|
|
61
|
+
"""
|
|
62
|
+
Retrieve repository remote URL.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
result = run_git_command(
|
|
66
|
+
["git", "config", "--get", "remote.origin.url"],
|
|
67
|
+
repo_path
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if result.returncode != 0:
|
|
71
|
+
return "unknown"
|
|
72
|
+
|
|
73
|
+
return result.stdout.strip()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_status(repo_path):
|
|
77
|
+
"""
|
|
78
|
+
Show git status.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
result = run_git_command(["git", "status"], repo_path)
|
|
82
|
+
|
|
83
|
+
return result.stdout
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def add_files(repo_path, files):
|
|
87
|
+
"""
|
|
88
|
+
Stage files for commit.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
result = run_git_command(["git", "add", files], repo_path)
|
|
92
|
+
|
|
93
|
+
if result.returncode != 0:
|
|
94
|
+
raise RuntimeError("Failed to add files")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def preview_staged_changes(repo_path):
|
|
98
|
+
"""
|
|
99
|
+
Preview staged changes before committing.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
result = run_git_command(["git", "diff", "--cached"], repo_path)
|
|
103
|
+
|
|
104
|
+
return result.stdout
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def has_changes_to_commit(repo_path):
|
|
108
|
+
"""
|
|
109
|
+
Detect if there are staged changes.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
result = run_git_command(
|
|
113
|
+
["git", "diff", "--cached", "--quiet"],
|
|
114
|
+
repo_path
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# returncode 0 means no changes
|
|
118
|
+
return result.returncode != 0
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def commit_changes(repo_path, message):
|
|
122
|
+
"""
|
|
123
|
+
Commit staged changes.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
result = run_git_command(
|
|
127
|
+
["git", "commit", "-m", message],
|
|
128
|
+
repo_path
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if result.returncode != 0:
|
|
132
|
+
raise RuntimeError("Commit failed")
|
|
133
|
+
|
|
134
|
+
return result.stdout
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def push_changes(repo_path):
|
|
138
|
+
"""
|
|
139
|
+
Push commit to remote repository.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
result = run_git_command(["git", "push"], repo_path)
|
|
143
|
+
|
|
144
|
+
if result.returncode != 0:
|
|
145
|
+
raise RuntimeError("Push failed")
|
|
146
|
+
|
|
147
|
+
return result.stdout
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def count_staged_files(repo_path):
|
|
151
|
+
"""
|
|
152
|
+
Count number of staged files.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
result = run_git_command(
|
|
156
|
+
["git", "diff", "--name-only", "--cached"],
|
|
157
|
+
repo_path
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if result.returncode != 0:
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
files = result.stdout.strip().splitlines()
|
|
164
|
+
|
|
165
|
+
return len(files)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Log file location
|
|
7
|
+
LOG_PATH = os.path.join(Path.home(), ".commitflow.log")
|
|
8
|
+
|
|
9
|
+
# Ensure log directory exists (future-proof safety)
|
|
10
|
+
try:
|
|
11
|
+
log_dir = os.path.dirname(LOG_PATH)
|
|
12
|
+
if log_dir:
|
|
13
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
14
|
+
except Exception:
|
|
15
|
+
# Logging should never break the application
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _write_log(level, message):
|
|
20
|
+
"""
|
|
21
|
+
Internal function to write log messages.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
25
|
+
|
|
26
|
+
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
with open(LOG_PATH, "a", encoding="utf-8") as log_file:
|
|
30
|
+
log_file.write(log_entry)
|
|
31
|
+
|
|
32
|
+
except Exception:
|
|
33
|
+
# Logging should never crash the application
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def log_info(message):
|
|
38
|
+
"""
|
|
39
|
+
Log informational message.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
_write_log("INFO", message)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def log_warning(message):
|
|
46
|
+
"""
|
|
47
|
+
Log warning message.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
_write_log("WARNING", message)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def log_error(message):
|
|
54
|
+
"""
|
|
55
|
+
Log error message.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
_write_log("ERROR", message)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def log_commit(repo, branch, files, message, pushed):
|
|
62
|
+
"""
|
|
63
|
+
Specialized log for commit operations.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
commit_log = (
|
|
67
|
+
f"repo={repo} "
|
|
68
|
+
f"branch={branch} "
|
|
69
|
+
f"files={files} "
|
|
70
|
+
f"message='{message}' "
|
|
71
|
+
f"pushed={pushed}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
_write_log("COMMIT", commit_log)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def read_logs(lines=20):
|
|
78
|
+
"""
|
|
79
|
+
Read last N log entries.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
if not os.path.exists(LOG_PATH):
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
with open(LOG_PATH, "r", encoding="utf-8") as f:
|
|
87
|
+
logs = f.readlines()
|
|
88
|
+
|
|
89
|
+
return logs[-lines:]
|
|
90
|
+
|
|
91
|
+
except Exception:
|
|
92
|
+
return []
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def clear_logs():
|
|
96
|
+
"""
|
|
97
|
+
Clear log file.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
if os.path.exists(LOG_PATH):
|
|
102
|
+
open(LOG_PATH, "w").close()
|
|
103
|
+
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|