fancygit 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.
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: fancygit
3
+ Version: 1.0.0
4
+ Summary: A smart CLI tool that provides intelligent recommendations and helps solve merge conflicts
5
+ Home-page: https://github.com/Youssif-Ashmawy/Fancy_Git
6
+ Author: Youssif Ashmawy
7
+ Author-email: ashmawyyoussif@gmail.com
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Topic :: Software Development :: Version Control :: Git
14
+ Requires-Python: >=3.6
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: requests>=2.25.0
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: requires-dist
24
+ Dynamic: requires-python
25
+ Dynamic: summary
26
+
27
+ # Fancy Git
28
+
29
+ ![FancyGit Overview](images/overview.png)
30
+
31
+ A smart CLI tool that provides intelligent recommendations and helps solve merge conflicts by analyzing git command outputs.
32
+
33
+ ## Email Notifications Test
34
+ *Testing email notification system - this change should trigger an email to the commit author.*
35
+
36
+ ## Overview
37
+
38
+ FancyGit is a CLI wrapper around git commands that:
39
+ - Runs git commands and captures their output
40
+ - Detects warnings and errors in real-time
41
+ - Provides user confirmation for clean operations
42
+ - Prepares for AI-powered recommendations (future phase)
43
+
44
+ ## Current Implementation (Phase 1)
45
+
46
+ ### ✅ Features Implemented
47
+ - **CLI Interface**: `fancygit` command works system-wide
48
+ - **Git Commands**: Full support for `add`, `commit`, `push`, and `pull` with argument passing
49
+ - **Warning/Error Detection**: Regex-based pattern matching for common git issues
50
+ - **User Confirmation**: Prompts for confirmation when no issues detected
51
+ - **Cross-platform**: Works on macOS, Linux, and Windows
52
+
53
+ ### 🔍 Detection Patterns
54
+
55
+ **Error Patterns:**
56
+ - `error:`, `fatal:`, `failed`, `rejected`
57
+ - `conflict`, `merge conflict`, `unable to`
58
+
59
+ **Warning Patterns:**
60
+ - `warning:`, `WARNING:`, `behind`, `ahead`, `diverged`
61
+
62
+ ## Installation
63
+
64
+ ### Quick Setup
65
+
66
+ Run the automated cross-platform launcher script:
67
+ ```bash
68
+ python3 launcher.py
69
+ ```
70
+
71
+ This script will:
72
+ - Automatically detect your operating system (Linux, macOS, or Windows)
73
+ - Run the appropriate installation script for your system
74
+ - Check Python 3.6+ and Git installation
75
+ - Make fancygit.py executable
76
+ - Create system-wide symlink (Unix-like systems) or add to PATH (Windows)
77
+ - Test the installation
78
+
79
+ ## Usage
80
+
81
+ ### Basic Commands
82
+ ```bash
83
+ # Add files
84
+ fancygit add .
85
+ fancygit add filename.txt
86
+
87
+ # Commit changes
88
+ fancygit commit -m "Your commit message"
89
+ fancygit commit --amend
90
+
91
+ # Push to remote
92
+ fancygit push origin main
93
+
94
+ # Pull from remote
95
+ fancygit pull origin main
96
+
97
+ # With additional arguments
98
+ fancygit push origin main --force-with-lease
99
+ fancygit pull origin main --rebase
100
+ ```
101
+
102
+ ### How It Works
103
+
104
+ 1. **Command Execution**: Runs the actual git command
105
+ 2. **Output Analysis**: Scans stdout/stderr for warning/error patterns
106
+ 3. **User Feedback**:
107
+ - If issues detected: Shows detailed error/warning messages
108
+ - If no issues: Prompts for confirmation with "enter 'y' to run the command"
109
+ 4. **Action**: Executes or cancels based on user input
110
+
111
+ ### Example Output
112
+
113
+ **No Issues:**
114
+ ```
115
+ Running: git push origin main
116
+ ✅ No warnings or errors detected
117
+ No warning messages - enter 'y' to run the command: y
118
+ Command executed successfully!
119
+ ```
120
+
121
+ **Issues Detected:**
122
+ ```
123
+ Running: git pull origin main
124
+ ❌ Errors detected:
125
+ error: couldn't find remote ref refs/heads/main
126
+ ```
127
+
128
+ ## Architecture
129
+
130
+ ```
131
+ FancyGit CLI → Git Commands → Collect Output → Pattern Matching → User Interface (CLI)
132
+ ```
133
+
134
+ ## Future Roadmap
135
+
136
+ ### Phase 2: AI Pipeline
137
+ - Implement AI model for parsing warnings/errors
138
+ - Generate intelligent recommendations
139
+ - Add confidence scoring (backend only)
140
+
141
+ ### Phase 3: Smart Resolution
142
+ - Automatic conflict resolution suggestions
143
+ - Context-aware recommendations
144
+ - Integration with popular Git workflows
145
+
146
+ ## File Structure
147
+
148
+ ```
149
+ Fancy_Git/
150
+ ├── fancygit.py # Main CLI application
151
+ ├── launcher.py # Cross-platform launcher script
152
+ ├── README.md # This file
153
+ ├── images/ # Images and documentation
154
+ │ └── overview.png # Project overview image
155
+ └── .git/ # Git repository
156
+ ```
157
+
158
+ *Note: Platform-specific installation scripts (`start.sh`, `start.bat`) are included but automatically managed by `launcher.py`.*
159
+ THIS SHOULD BE UPDATED ASAP
160
+ ## Requirements
161
+
162
+ - Python 3.6+
163
+ - Git installed and configured
164
+ - System permissions for symlink creation
165
+ - Note: No external dependecies for now so no environment setup required
166
+
167
+ ## License
168
+
169
+ MIT License (For now as it is still a private project)
@@ -0,0 +1,29 @@
1
+ src/__init__.py,sha256=9kebFj2X3VcOAcKAMsbUy_BY0odMs6JDpR6XZd-UBMo,53
2
+ src/colors.py,sha256=v5GogeZzJJdchim5kbenlQno350kld_4HkTojLvGbog,7810
3
+ src/git_error.py,sha256=tb-RbWWQZYqEcqP2PxI3snjwlfuEp96baZiONFulDm0,1852
4
+ src/git_error_parser.py,sha256=aWhVpb0eXx0tF8y8npWovvuKMYQTv-vgHnNh7o5rzh4,1499
5
+ src/git_insights.py,sha256=2R4Qb6iZ3HRdMNFUV03FWxZtbaYMCMIQ_lTXePhbC7w,13196
6
+ src/git_runner.py,sha256=ogQntBlV55uPH1dHlyQtEJI3OOL9WnvdzyxK-7qUaVE,544
7
+ src/loading_animation.py,sha256=Ei5oXCmNxLCbLPsLuST8ipxQN0vjzYBaHWfkbCASttw,5365
8
+ src/merge_conflict.py,sha256=OZ5_WkbEIycAtg0fazEB_LulhibfT8A4vpUwuD_NN1s,559
9
+ src/mermaid_export.py,sha256=HdFWpDSsiKIYehZ24mEQa62mMEiPOecD6LdDHZoc7w0,17157
10
+ src/ollama_client.py,sha256=2aB0dwrJNX1LagKpMJAgNyX7gL9bhdCbeDjXCklFU6I,5229
11
+ src/output_colorizer.py,sha256=yBDF0-ddoGH5A-44Ht9qNKFdamAfenJeTgyxMwOFuS0,14161
12
+ src/repo_state.py,sha256=QaDKEBYk3J6L6aZVeJdJlKz6aci3K3VK5eEHF43TuzU,665
13
+ src/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ tests/README.md,sha256=x_5bZLmVpTvobP8YQc_qVDwKlRk6CjbVqD-cN0WNfpI,4377
15
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ tests/conftest.py,sha256=z80AuePwW-ZczGfa9YQpuAKKG2iaJ8qzx3DU-qXTVV0,2095
17
+ tests/test_conflict_parser_integration.py,sha256=vWCHNBcxlHDxjk5c7kk8dlktcbUcF0sGUF47Iwvru_w,2721
18
+ tests/test_fancygit_advanced.py,sha256=SWEZJBrEbqFg-hzvEkqV150DFIdpkSigovH9cM-D1Dc,22920
19
+ tests/test_fancygit_commands.py,sha256=FNNAd4kLyd_tXlNXQBG0cCt5yOH3_v3Xy8B0M4M9LKM,22414
20
+ tests/test_fancygit_integration.py,sha256=EQxjw-wlUoWBRhtCVvSERM-ikx-P73M7-R_bJqtP8oM,7506
21
+ tests/test_fancygit_workflows.py,sha256=CPGpmRIgFlwf95fAx-I_sb2ZpelOZPIfhqTTj4use8Q,21309
22
+ tests/test_git_error.py,sha256=lJcFlVwa1beP74fnTbn7pD8XKFLGgAL59XMYW3CF8W0,2618
23
+ tests/test_git_error_parser.py,sha256=UuRwVN5awD5F9G7fg9dJLowRFMt-tYfXwOyedukO1-U,6008
24
+ tests/test_git_runner.py,sha256=pVIaz2P2qbpIrcYBW-jCYA6HUD6uq6Ye7RDviDvoyyU,4365
25
+ fancygit-1.0.0.dist-info/METADATA,sha256=wHhxrZOmQ0VFpzQHDuQmrYpolhUF_9KkuqS8BdvLlmg,4802
26
+ fancygit-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
27
+ fancygit-1.0.0.dist-info/entry_points.txt,sha256=kUhjnub9IlB7qbE3w9KKKhKb8-QMdgLfAsTrD4Jhe_w,43
28
+ fancygit-1.0.0.dist-info/top_level.txt,sha256=KW3xgkz9NLMTcmmzgKvW8RFpCFkRIQ085qzq2diFf68,10
29
+ fancygit-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ fancygit = fancygit:main
@@ -0,0 +1,2 @@
1
+ src
2
+ tests
src/__init__.py ADDED
@@ -0,0 +1 @@
1
+ # This file makes the src directory a Python package
src/colors.py ADDED
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ FancyGit Color System
4
+ Provides vibrant colors and styling for the CLI interface
5
+ """
6
+
7
+ class Colors:
8
+ """ANSI color codes for vibrant terminal output"""
9
+
10
+ # Reset
11
+ RESET = '\033[0m'
12
+
13
+ # Check if terminal supports colors
14
+ SUPPORTS_COLOR = True
15
+
16
+ @classmethod
17
+ def check_color_support(cls):
18
+ """Check if terminal supports colors"""
19
+ import os
20
+ # Check if NO_COLOR environment variable is set
21
+ if os.environ.get('NO_COLOR'):
22
+ cls.SUPPORTS_COLOR = False
23
+ return False
24
+
25
+ # Check if TERM environment variable suggests no color support
26
+ term = os.environ.get('TERM', '').lower()
27
+ no_color_terms = ['dumb', 'unknown', 'unknown']
28
+ if any(no_color_term in term for no_color_term in no_color_terms):
29
+ cls.SUPPORTS_COLOR = False
30
+ return False
31
+
32
+ # Check if we're not in a TTY
33
+ import sys
34
+ if not sys.stdout.isatty():
35
+ cls.SUPPORTS_COLOR = False
36
+ return False
37
+
38
+ cls.SUPPORTS_COLOR = True
39
+ return True
40
+
41
+ # Regular colors
42
+ BLACK = '\033[30m'
43
+ RED = '\033[31m'
44
+ GREEN = '\033[32m'
45
+ YELLOW = '\033[33m'
46
+ BLUE = '\033[34m'
47
+ MAGENTA = '\033[35m'
48
+ CYAN = '\033[36m'
49
+ WHITE = '\033[37m'
50
+
51
+ # Bright colors
52
+ BRIGHT_BLACK = '\033[90m'
53
+ BRIGHT_RED = '\033[91m'
54
+ BRIGHT_GREEN = '\033[92m'
55
+ BRIGHT_YELLOW = '\033[93m'
56
+ BRIGHT_BLUE = '\033[94m'
57
+ BRIGHT_MAGENTA = '\033[95m'
58
+ BRIGHT_CYAN = '\033[96m'
59
+ BRIGHT_WHITE = '\033[97m'
60
+
61
+ # Background colors
62
+ BG_BLACK = '\033[40m'
63
+ BG_RED = '\033[41m'
64
+ BG_GREEN = '\033[42m'
65
+ BG_YELLOW = '\033[43m'
66
+ BG_BLUE = '\033[44m'
67
+ BG_MAGENTA = '\033[45m'
68
+ BG_CYAN = '\033[46m'
69
+ BG_WHITE = '\033[47m'
70
+
71
+ # Bright background colors
72
+ BG_BRIGHT_BLACK = '\033[100m'
73
+ BG_BRIGHT_RED = '\033[101m'
74
+ BG_BRIGHT_GREEN = '\033[102m'
75
+ BG_BRIGHT_YELLOW = '\033[103m'
76
+ BG_BRIGHT_BLUE = '\033[104m'
77
+ BG_BRIGHT_MAGENTA = '\033[105m'
78
+ BG_BRIGHT_CYAN = '\033[106m'
79
+ BG_BRIGHT_WHITE = '\033[107m'
80
+
81
+ # Text styles
82
+ BOLD = '\033[1m'
83
+ DIM = '\033[2m'
84
+ ITALIC = '\033[3m'
85
+ UNDERLINE = '\033[4m'
86
+ BLINK = '\033[5m'
87
+ REVERSE = '\033[7m'
88
+ STRIKETHROUGH = '\033[9m'
89
+
90
+ # FancyGit theme colors
91
+ PRIMARY = BRIGHT_BLUE
92
+ SECONDARY = BRIGHT_CYAN
93
+ ACCENT = BRIGHT_YELLOW
94
+ SUCCESS = BRIGHT_GREEN
95
+ WARNING = BRIGHT_YELLOW
96
+ ERROR = BRIGHT_RED
97
+ INFO = BRIGHT_MAGENTA
98
+ MUTED = BRIGHT_BLACK
99
+
100
+ # Git-specific colors
101
+ GIT_ADD = SUCCESS
102
+ GIT_MODIFY = WARNING
103
+ GIT_DELETE = ERROR
104
+ GIT_RENAME = INFO
105
+ GIT_COPY = SECONDARY
106
+ GIT_CONFLICT = BG_BRIGHT_RED + WHITE + BOLD
107
+
108
+ # Status indicators
109
+ STATUS_SUCCESS = '✅'
110
+ STATUS_ERROR = '❌'
111
+ STATUS_WARNING = '⚠️'
112
+ STATUS_INFO = 'ℹ️'
113
+ STATUS_LOADING = '🔄'
114
+ STATUS_AI = '🤖'
115
+ STATUS_BRAIN = '🧠'
116
+
117
+ @classmethod
118
+ def colorize(cls, text: str, color: str, style: str = '') -> str:
119
+ """Apply color and style to text"""
120
+ if not cls.SUPPORTS_COLOR:
121
+ return text
122
+ return f"{style}{color}{text}{cls.RESET}"
123
+
124
+ @classmethod
125
+ def command(cls, text: str) -> str:
126
+ """Color for command names"""
127
+ return cls.colorize(text, cls.PRIMARY, cls.BOLD)
128
+
129
+ @classmethod
130
+ def success(cls, text: str) -> str:
131
+ """Color for success messages"""
132
+ return cls.colorize(text, cls.SUCCESS)
133
+
134
+ @classmethod
135
+ def error(cls, text: str) -> str:
136
+ """Color for error messages"""
137
+ return cls.colorize(text, cls.ERROR, cls.BOLD)
138
+
139
+ @classmethod
140
+ def warning(cls, text: str) -> str:
141
+ """Color for warning messages"""
142
+ return cls.colorize(text, cls.WARNING)
143
+
144
+ @classmethod
145
+ def info(cls, text: str) -> str:
146
+ """Color for info messages"""
147
+ return cls.colorize(text, cls.INFO)
148
+
149
+ @classmethod
150
+ def ai_response(cls, text: str) -> str:
151
+ """Color for AI responses"""
152
+ return cls.colorize(text, cls.SECONDARY, cls.ITALIC)
153
+
154
+ @classmethod
155
+ def highlight(cls, text: str) -> str:
156
+ """Color for highlighted text"""
157
+ return cls.colorize(text, cls.ACCENT, cls.BOLD)
158
+
159
+ @classmethod
160
+ def muted(cls, text: str) -> str:
161
+ """Color for muted text"""
162
+ return cls.colorize(text, cls.MUTED)
163
+
164
+ @classmethod
165
+ def git_status(cls, status: str) -> str:
166
+ """Color for git status indicators"""
167
+ status_colors = {
168
+ 'A': cls.GIT_ADD, # Added
169
+ 'M': cls.GIT_MODIFY, # Modified
170
+ 'D': cls.GIT_DELETE, # Deleted
171
+ 'R': cls.GIT_RENAME, # Renamed
172
+ 'C': cls.GIT_COPY, # Copied
173
+ '??': cls.MUTED, # Untracked
174
+ 'UU': cls.GIT_CONFLICT # Conflict
175
+ }
176
+ color = status_colors.get(status, cls.WHITE)
177
+ return cls.colorize(status, color, cls.BOLD)
178
+
179
+ @classmethod
180
+ def branch_name(cls, branch: str, is_current: bool = False) -> str:
181
+ """Color for branch names"""
182
+ if is_current:
183
+ return cls.colorize(branch, cls.BRIGHT_GREEN, cls.BOLD)
184
+ return cls.colorize(branch, cls.PRIMARY)
185
+
186
+ @classmethod
187
+ def file_path(cls, path: str) -> str:
188
+ """Color for file paths"""
189
+ return cls.colorize(path, cls.BRIGHT_CYAN)
190
+
191
+ @classmethod
192
+ def header(cls, text: str) -> str:
193
+ """Color for headers"""
194
+ return cls.colorize(text, cls.PRIMARY, cls.BOLD)
195
+
196
+ @classmethod
197
+ def subheader(cls, text: str) -> str:
198
+ """Color for subheaders"""
199
+ return cls.colorize(text, cls.SECONDARY)
200
+
201
+ @classmethod
202
+ def divider(cls, char: str = '=', length: int = 60) -> str:
203
+ """Create a colored divider"""
204
+ return cls.colorize(char * length, cls.MUTED)
205
+
206
+ @classmethod
207
+ def progress_bar(cls, current: int, total: int, width: int = 20) -> str:
208
+ """Create a colored progress bar"""
209
+ filled = int(width * current / total)
210
+ bar = '█' * filled + '░' * (width - filled)
211
+ return cls.colorize(f'[{bar}] {current}/{total}', cls.SUCCESS)
212
+
213
+ @classmethod
214
+ def rainbow_text(cls, text: str) -> str:
215
+ """Create rainbow-colored text"""
216
+ colors = [cls.RED, cls.BRIGHT_YELLOW, cls.GREEN, cls.CYAN, cls.BLUE, cls.MAGENTA]
217
+ result = []
218
+ for i, char in enumerate(text):
219
+ color = colors[i % len(colors)]
220
+ result.append(f"{color}{char}")
221
+ return ''.join(result) + cls.RESET
222
+
223
+ @classmethod
224
+ def gradient_text(cls, text: str, start_color: str, end_color: str) -> str:
225
+ """Create gradient-colored text (simplified version)"""
226
+ # This is a simplified gradient - in a full implementation,
227
+ # you'd interpolate between RGB values
228
+ mid_point = len(text) // 2
229
+ first_half = text[:mid_point]
230
+ second_half = text[mid_point:]
231
+
232
+ return f"{start_color}{first_half}{end_color}{second_half}{cls.RESET}"
233
+
234
+ # Convenience functions for common color operations
235
+ def color_command(text: str) -> str:
236
+ return Colors.command(text)
237
+
238
+ def color_success(text: str) -> str:
239
+ return Colors.success(text)
240
+
241
+ def color_error(text: str) -> str:
242
+ return Colors.error(text)
243
+
244
+ def color_warning(text: str) -> str:
245
+ return Colors.warning(text)
246
+
247
+ def color_info(text: str) -> str:
248
+ return Colors.info(text)
249
+
250
+ def color_ai(text: str) -> str:
251
+ return Colors.ai_response(text)
252
+
253
+ def color_header(text: str) -> str:
254
+ return Colors.header(text)
255
+
256
+ def color_file(text: str) -> str:
257
+ return Colors.file_path(text)
258
+
259
+ def color_branch(text: str, current: bool = False) -> str:
260
+ return Colors.branch_name(text, current)
src/git_error.py ADDED
@@ -0,0 +1,55 @@
1
+ # this class will be an immutable dataclass
2
+ # it will represent like the format of the data so we can then
3
+ # pass it to the ai models so they can be fed a consistent and organized
4
+ # json format that is both readable and can be easily extracted and
5
+ # worked on
6
+
7
+ # dataclasses are suitable here because we will primarily use it to store the
8
+ # data and represent it
9
+ # moreover to make it look cleaner without having to write an init method
10
+
11
+
12
+ # the format will be like the following
13
+ # {
14
+ # "type": "MERGE_CONFLICT",
15
+ # "source" : stdout || stderr
16
+ # "message": "error: Merge conflict in README.md",
17
+ # "severity": "error" || "warning",
18
+ # "file": "README.md",
19
+ # "line": null
20
+ # }
21
+ from dataclasses import dataclass
22
+ from src.colors import Colors, color_error, color_warning, color_info, color_file
23
+
24
+ @dataclass(frozen = True) # we freeze it to make it immutable
25
+ class GitError:
26
+ source: str
27
+ message: str
28
+ severity: str = "unknown"
29
+
30
+ type: str | None = None
31
+ file: str | None = None
32
+ line: int | None = None
33
+
34
+ def __str__(self) -> str:
35
+ severity_color = {
36
+ 'error': color_error,
37
+ 'warning': color_warning,
38
+ 'info': color_info,
39
+ 'unknown': lambda x: x
40
+ }.get(self.severity, lambda x: x)
41
+
42
+ type_str = str(self.type) if self.type is not None else 'None'
43
+ file_str = str(self.file) if self.file is not None else 'N/A'
44
+ line_str = str(self.line) if self.line is not None else 'N/A'
45
+
46
+ return (
47
+ f"{Colors.divider('=', 60)}\n"
48
+ f"type: {type_str}\n"
49
+ f"source: {str(self.source)}\n"
50
+ f"message: {str(self.message)}\n"
51
+ f"severity: {str(self.severity)}\n"
52
+ f"file: {file_str}\n"
53
+ f"line: {line_str}\n"
54
+ f"{Colors.divider('=', 60)}"
55
+ )
@@ -0,0 +1,43 @@
1
+ import re
2
+ from src.git_error import GitError
3
+ class GitErrorParser:
4
+ def __init__(self) -> None:
5
+ pass
6
+
7
+ def detect_warnings_errors(self, stdout, stderr):
8
+ """Detect warnings and errors in git output"""
9
+ messages = []
10
+
11
+ def scan_patterns(patterns, text, type, source):
12
+ for pattern in patterns:
13
+ for match in re.findall(f'{pattern}.*', text, re.IGNORECASE | re.MULTILINE):
14
+ messages.append(GitError(severity = type, source=source, message=match.strip()))
15
+
16
+ # Common git error patterns (more specific to avoid false positives)
17
+ error_patterns = [
18
+ r'fatal:',
19
+ r'error:',
20
+ r'\bfailed\b',
21
+ r'\brejected\b',
22
+ r'merge conflict',
23
+ r'\bconflict\b',
24
+ r'unable to',
25
+ r'\.git.*error:',
26
+ r'^(?!.*error:).*pathspec.*did not match'
27
+ ]
28
+
29
+ # Common git warning patterns (more specific to avoid false positives)
30
+ warning_patterns = [
31
+ r'warning:',
32
+ r'WARNING:',
33
+ r'.*behind.*by\s+\d+.*',
34
+ r'.*ahead.*by\s+\d+.*',
35
+ r'\bdiverged\b'
36
+ ]
37
+
38
+ scan_patterns(error_patterns, stdout, "error", "stdout")
39
+ scan_patterns(warning_patterns, stdout, "warning", "stdout")
40
+ scan_patterns(error_patterns, stderr, "error", "stderr")
41
+ scan_patterns(warning_patterns, stderr, "warning", "stderr")
42
+
43
+ return messages