批量 git add, commit, push 多个文件,支持重试 Args: filenames: 要提交的文件名列表 max_retries: 最大重试次数(默认 3 次) sync_codespace: 是否在 push 后同步 Codespace(默认 True) 如果为 False,需要调用者稍后在持有 submit_lock 时调用 sync Returns:
(self, filenames: List[str], max_retries: int = 3, sync_codespace: bool = True)
| 469 | return written_files |
| 470 | |
| 471 | async def batch_push(self, filenames: List[str], max_retries: int = 3, sync_codespace: bool = True) -> bool: |
| 472 | """ |
| 473 | 批量 git add, commit, push 多个文件,支持重试 |
| 474 | |
| 475 | Args: |
| 476 | filenames: 要提交的文件名列表 |
| 477 | max_retries: 最大重试次数(默认 3 次) |
| 478 | sync_codespace: 是否在 push 后同步 Codespace(默认 True) |
| 479 | 如果为 False,需要调用者稍后在持有 submit_lock 时调用 sync |
| 480 | |
| 481 | Returns: |
| 482 | 是否成功 push |
| 483 | """ |
| 484 | if not filenames: |
| 485 | return True |
| 486 | |
| 487 | logger.info(f"| 📦 Batch pushing {len(filenames)} files...") |
| 488 | |
| 489 | # 先完成 git add 和 commit(这部分不需要重试) |
| 490 | try: |
| 491 | # Configure git user |
| 492 | await self._run_git_command(['config', 'user.name', self.username], self.repo_path) |
| 493 | await self._run_git_command(['config', 'user.email', f'{self.username}@users.noreply.github.com'], self.repo_path) |
| 494 | |
| 495 | # Git add all files |
| 496 | for filename in filenames: |
| 497 | await self._run_git_command(['add', filename], self.repo_path) |
| 498 | |
| 499 | # Check if there are changes to commit |
| 500 | status_out = await self._run_git_command(['status', '--porcelain'], self.repo_path) |
| 501 | |
| 502 | if status_out.strip(): |
| 503 | # Git commit with batch message |
| 504 | commit_msg = f'Batch add {len(filenames)} solutions: {", ".join(f[:20] for f in filenames[:3])}...' |
| 505 | await self._run_git_command(['commit', '-m', commit_msg], self.repo_path) |
| 506 | else: |
| 507 | logger.info("| 🔍 No changes to commit (files unchanged)") |
| 508 | return True |
| 509 | |
| 510 | except Exception as e: |
| 511 | logger.error(f"| ❌ Git add/commit failed: {e}") |
| 512 | return False |
| 513 | |
| 514 | # Git push 部分支持重试 |
| 515 | for attempt in range(1, max_retries + 1): |
| 516 | try: |
| 517 | logger.info(f"| 🔄 Push attempt {attempt}/{max_retries}...") |
| 518 | await self._run_git_command(['push'], self.repo_path) |
| 519 | logger.info(f"| ✅ Successfully batch pushed {len(filenames)} files to GitHub") |
| 520 | |
| 521 | # Wait for GitHub to sync |
| 522 | await asyncio.sleep(3) |
| 523 | |
| 524 | # Pull in Codespace (only if sync_codespace=True and we have submit_lock) |
| 525 | # Note: _sync_codespace_repo() operates the browser, so it must be called |
| 526 | # when the caller holds submit_lock to avoid conflicts with browser operations |
| 527 | if sync_codespace: |
| 528 | await self._sync_codespace_repo() |
no test coverage detected