Coverage for src / moai_adk / core / git / manager.py: 34.78%
46 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
1"""
2Git repository management built on GitPython.
4SPEC: .moai/specs/SPEC-CORE-GIT-001/spec.md
5"""
7from pathlib import Path
9from git import InvalidGitRepositoryError, Repo
11from moai_adk.core.git.conflict_detector import GitConflictDetector
14class GitManager:
15 """Manage interactions with a Git repository."""
17 def __init__(self, repo_path: str | Path = "."):
18 """
19 Initialize the GitManager.
21 Args:
22 repo_path: Path to the Git repository (default: current directory)
24 Raises:
25 InvalidGitRepositoryError: Raised when the path is not a Git repository.
26 """
27 self.repo = Repo(repo_path)
28 self.git = self.repo.git
29 self.repo_path = Path(repo_path).resolve()
30 self.conflict_detector = GitConflictDetector(self.repo_path)
32 def is_repo(self) -> bool:
33 """
34 Check whether the path points to a Git repository.
36 Returns:
37 True when the location is a Git repository, otherwise False.
39 Examples:
40 >>> manager = GitManager("/path/to/repo")
41 >>> manager.is_repo()
42 True
43 """
44 try:
45 _ = self.repo.git_dir
46 return True
47 except (InvalidGitRepositoryError, Exception):
48 return False
50 def current_branch(self) -> str:
51 """
52 Return the active branch name.
54 Returns:
55 Name of the currently checked-out branch.
57 Examples:
58 >>> manager = GitManager()
59 >>> manager.current_branch()
60 'main'
61 """
62 return self.repo.active_branch.name
64 def is_dirty(self) -> bool:
65 """
66 Check whether the working tree has uncommitted changes.
68 Returns:
69 True when the worktree is dirty, otherwise False.
71 Examples:
72 >>> manager = GitManager()
73 >>> manager.is_dirty()
74 False
75 """
76 return self.repo.is_dirty()
78 def create_branch(self, branch_name: str, from_branch: str | None = None) -> None:
79 """
80 Create and switch to a new branch.
82 Args:
83 branch_name: Name of the branch to create.
84 from_branch: Base branch (default: current branch).
86 Examples:
87 >>> manager = GitManager()
88 >>> manager.create_branch("feature/SPEC-AUTH-001")
89 >>> manager.current_branch()
90 'feature/SPEC-AUTH-001'
91 """
92 if from_branch:
93 self.git.checkout("-b", branch_name, from_branch)
94 else:
95 self.git.checkout("-b", branch_name)
97 def commit(self, message: str, files: list[str] | None = None) -> None:
98 """
99 Stage files and create a commit.
101 Args:
102 message: Commit message.
103 files: Optional list of files to commit (default: all changes).
105 Examples:
106 >>> manager = GitManager()
107 >>> manager.commit("feat: add authentication", files=["auth.py"])
108 """
109 if files:
110 self.repo.index.add(files)
111 else:
112 self.git.add(A=True)
114 self.repo.index.commit(message)
116 def push(self, branch: str | None = None, set_upstream: bool = False) -> None:
117 """
118 Push commits to the remote repository.
120 Args:
121 branch: Branch to push (default: current branch).
122 set_upstream: Whether to set the upstream tracking branch.
124 Examples:
125 >>> manager = GitManager()
126 >>> manager.push(set_upstream=True)
127 """
128 if set_upstream:
129 target_branch = branch or self.current_branch()
130 self.git.push("--set-upstream", "origin", target_branch)
131 else:
132 self.git.push()
134 def check_merge_conflicts(
135 self, feature_branch: str, base_branch: str
136 ) -> dict:
137 """
138 Check if merge is possible without conflicts.
140 Args:
141 feature_branch: Feature branch to merge from
142 base_branch: Base branch to merge into
144 Returns:
145 Dictionary with merge status and conflict information
147 Examples:
148 >>> manager = GitManager()
149 >>> result = manager.check_merge_conflicts("feature/auth", "develop")
150 >>> if result["can_merge"]:
151 ... print("Ready to merge")
152 ... else:
153 ... print(f"Conflicts: {result['conflicts']}")
154 """
155 return self.conflict_detector.can_merge(feature_branch, base_branch)
157 def has_merge_conflicts(self, feature_branch: str, base_branch: str) -> bool:
158 """
159 Quick check if merge would have conflicts.
161 Args:
162 feature_branch: Feature branch to merge from
163 base_branch: Base branch to merge into
165 Returns:
166 True if conflicts exist, False otherwise
168 Examples:
169 >>> manager = GitManager()
170 >>> if manager.has_merge_conflicts("feature/auth", "develop"):
171 ... print("Conflicts detected")
172 """
173 result = self.conflict_detector.can_merge(feature_branch, base_branch)
174 return not result.get("can_merge", False)
176 def get_conflict_summary(self, feature_branch: str, base_branch: str) -> str:
177 """
178 Get human-readable summary of merge conflicts.
180 Args:
181 feature_branch: Feature branch to merge from
182 base_branch: Base branch to merge into
184 Returns:
185 String summary of conflicts for user presentation
187 Examples:
188 >>> manager = GitManager()
189 >>> summary = manager.get_conflict_summary("feature/auth", "develop")
190 >>> print(summary)
191 """
192 result = self.conflict_detector.can_merge(feature_branch, base_branch)
193 conflicts = result.get("conflicts", [])
194 return self.conflict_detector.summarize_conflicts(conflicts)
196 def auto_resolve_safe_conflicts(self) -> bool:
197 """
198 Auto-resolve safe config file conflicts.
200 Returns:
201 True if auto-resolution succeeded, False otherwise
203 Examples:
204 >>> manager = GitManager()
205 >>> if manager.auto_resolve_safe_conflicts():
206 ... print("Safe conflicts resolved automatically")
207 """
208 return self.conflict_detector.auto_resolve_safe()
210 def abort_merge(self) -> None:
211 """
212 Abort an in-progress merge and clean up state.
214 Examples:
215 >>> manager = GitManager()
216 >>> manager.abort_merge()
217 """
218 self.conflict_detector.cleanup_merge_state()