github2gerrit 0.1.15__py3-none-any.whl → 0.1.16__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.
- github2gerrit/cli.py +50 -8
- github2gerrit/core.py +257 -1
- {github2gerrit-0.1.15.dist-info → github2gerrit-0.1.16.dist-info}/METADATA +1 -1
- {github2gerrit-0.1.15.dist-info → github2gerrit-0.1.16.dist-info}/RECORD +7 -7
- {github2gerrit-0.1.15.dist-info → github2gerrit-0.1.16.dist-info}/WHEEL +0 -0
- {github2gerrit-0.1.15.dist-info → github2gerrit-0.1.16.dist-info}/entry_points.txt +0 -0
- {github2gerrit-0.1.15.dist-info → github2gerrit-0.1.16.dist-info}/licenses/LICENSE +0 -0
github2gerrit/cli.py
CHANGED
@@ -36,6 +36,7 @@ from .github_api import get_pr_title_body
|
|
36
36
|
from .github_api import get_pull
|
37
37
|
from .github_api import get_repo_from_env
|
38
38
|
from .github_api import iter_open_pulls
|
39
|
+
from .gitutils import CommandError
|
39
40
|
from .gitutils import run_cmd
|
40
41
|
from .models import GitHubContext
|
41
42
|
from .models import Inputs
|
@@ -246,7 +247,7 @@ if "--help" in sys.argv or _is_github_actions_context():
|
|
246
247
|
|
247
248
|
app: typer.Typer = typer.Typer(
|
248
249
|
add_completion=False,
|
249
|
-
no_args_is_help=
|
250
|
+
no_args_is_help=True,
|
250
251
|
cls=cast(Any, _SingleUsageGroup),
|
251
252
|
rich_markup_mode="rich",
|
252
253
|
help="Tool to convert GitHub pull requests into Gerrit changes",
|
@@ -990,15 +991,56 @@ def _process_single(
|
|
990
991
|
else:
|
991
992
|
progress_tracker.change_updated()
|
992
993
|
except Exception as exc:
|
993
|
-
|
994
|
-
|
994
|
+
# Enhanced error handling for CommandError to show git command
|
995
|
+
# details
|
996
|
+
if isinstance(exc, CommandError):
|
997
|
+
# Always show the basic error message
|
998
|
+
cmd_str = " ".join(exc.cmd) if exc.cmd else "unknown command"
|
999
|
+
basic_error = f"Git command failed: {cmd_str}"
|
1000
|
+
if exc.returncode is not None:
|
1001
|
+
basic_error += f" (exit code: {exc.returncode})"
|
1002
|
+
|
1003
|
+
# In verbose mode, show detailed stdout/stderr
|
1004
|
+
if is_verbose_mode():
|
1005
|
+
detailed_msg = basic_error
|
1006
|
+
if exc.stdout and exc.stdout.strip():
|
1007
|
+
detailed_msg += f"\nGit stdout: {exc.stdout.strip()}"
|
1008
|
+
if exc.stderr and exc.stderr.strip():
|
1009
|
+
detailed_msg += f"\nGit stderr: {exc.stderr.strip()}"
|
1010
|
+
|
1011
|
+
# Show debugging suggestion for merge failures
|
1012
|
+
if "merge --squash" in " ".join(exc.cmd or []):
|
1013
|
+
detailed_msg += (
|
1014
|
+
"\n💡 For local debugging: python "
|
1015
|
+
"test_merge_failure.py --verbose"
|
1016
|
+
)
|
995
1017
|
|
996
|
-
|
997
|
-
|
998
|
-
|
1018
|
+
safe_console_print(f"❌ {detailed_msg}", style="red")
|
1019
|
+
if progress_tracker:
|
1020
|
+
progress_tracker.add_error(basic_error)
|
1021
|
+
if exc.stderr and exc.stderr.strip():
|
1022
|
+
progress_tracker.add_error(
|
1023
|
+
f"Details: {exc.stderr.strip()}"
|
1024
|
+
)
|
1025
|
+
else:
|
1026
|
+
# In non-verbose mode, show basic error with hint to enable
|
1027
|
+
# verbose
|
1028
|
+
hint_msg = (
|
1029
|
+
basic_error
|
1030
|
+
+ "\n💡 Run with VERBOSE=true for detailed git output"
|
1031
|
+
)
|
1032
|
+
safe_console_print(f"❌ {hint_msg}", style="red")
|
1033
|
+
if progress_tracker:
|
1034
|
+
progress_tracker.add_error(basic_error)
|
999
1035
|
else:
|
1000
|
-
#
|
1001
|
-
|
1036
|
+
# For other exceptions, use original handling
|
1037
|
+
error_msg = str(exc)
|
1038
|
+
if progress_tracker:
|
1039
|
+
progress_tracker.add_error(f"Execution failed: {error_msg}")
|
1040
|
+
else:
|
1041
|
+
safe_console_print(f"❌ Error: {error_msg}", style="red")
|
1042
|
+
|
1043
|
+
log.debug("Execution failed; continuing to write outputs: %s", exc)
|
1002
1044
|
|
1003
1045
|
# In verbose mode, also log the full exception with traceback
|
1004
1046
|
if is_verbose_mode():
|
github2gerrit/core.py
CHANGED
@@ -2028,10 +2028,99 @@ class Orchestrator:
|
|
2028
2028
|
# Create temp branch from base and merge-squash PR head
|
2029
2029
|
tmp_branch = f"g2g_tmp_{gh.pr_number or 'pr'!s}_{os.getpid()}"
|
2030
2030
|
os.environ["G2G_TMP_BRANCH"] = tmp_branch
|
2031
|
+
|
2032
|
+
log.debug(
|
2033
|
+
"Git merge preparation: base_sha=%s, head_sha=%s, tmp_branch=%s",
|
2034
|
+
base_sha,
|
2035
|
+
head_sha,
|
2036
|
+
tmp_branch,
|
2037
|
+
)
|
2038
|
+
|
2039
|
+
# Check if we have any commits to merge
|
2040
|
+
try:
|
2041
|
+
merge_base = run_cmd(
|
2042
|
+
["git", "merge-base", base_sha, head_sha], cwd=self.workspace
|
2043
|
+
).stdout.strip()
|
2044
|
+
log.debug("Merge base: %s", merge_base)
|
2045
|
+
|
2046
|
+
# Check if there are any commits between base and head
|
2047
|
+
commits_to_merge = run_cmd(
|
2048
|
+
["git", "rev-list", f"{base_sha}..{head_sha}"],
|
2049
|
+
cwd=self.workspace,
|
2050
|
+
).stdout.strip()
|
2051
|
+
if not commits_to_merge:
|
2052
|
+
log.warning(
|
2053
|
+
"No commits found between base (%s) and head (%s)",
|
2054
|
+
base_sha,
|
2055
|
+
head_sha,
|
2056
|
+
)
|
2057
|
+
else:
|
2058
|
+
commit_count = len(commits_to_merge.splitlines())
|
2059
|
+
log.debug("Found %d commits to merge", commit_count)
|
2060
|
+
|
2061
|
+
except Exception as debug_exc:
|
2062
|
+
log.warning("Failed to analyze merge situation: %s", debug_exc)
|
2063
|
+
|
2031
2064
|
run_cmd(
|
2032
2065
|
["git", "checkout", "-b", tmp_branch, base_sha], cwd=self.workspace
|
2033
2066
|
)
|
2034
|
-
|
2067
|
+
|
2068
|
+
# Show git status before attempting merge
|
2069
|
+
try:
|
2070
|
+
status_output = run_cmd(
|
2071
|
+
["git", "status", "--porcelain"], cwd=self.workspace
|
2072
|
+
).stdout
|
2073
|
+
if status_output.strip():
|
2074
|
+
log.debug(
|
2075
|
+
"Git status before merge (modified files detected):\n%s",
|
2076
|
+
status_output,
|
2077
|
+
)
|
2078
|
+
else:
|
2079
|
+
log.debug("Git status before merge: working directory clean")
|
2080
|
+
|
2081
|
+
# Show current branch
|
2082
|
+
current_branch = run_cmd(
|
2083
|
+
["git", "branch", "--show-current"], cwd=self.workspace
|
2084
|
+
).stdout.strip()
|
2085
|
+
log.debug("Current branch before merge: %s", current_branch)
|
2086
|
+
|
2087
|
+
except Exception as status_exc:
|
2088
|
+
log.warning("Failed to get git status before merge: %s", status_exc)
|
2089
|
+
|
2090
|
+
log.debug("About to run: git merge --squash %s", head_sha)
|
2091
|
+
try:
|
2092
|
+
run_cmd(["git", "merge", "--squash", head_sha], cwd=self.workspace)
|
2093
|
+
except CommandError as merge_exc:
|
2094
|
+
# Enhanced error handling for git merge failures
|
2095
|
+
error_details = self._analyze_merge_failure(
|
2096
|
+
merge_exc, base_sha, head_sha
|
2097
|
+
)
|
2098
|
+
|
2099
|
+
# Try to provide recovery suggestions
|
2100
|
+
recovery_msg = self._suggest_merge_recovery(
|
2101
|
+
merge_exc, base_sha, head_sha
|
2102
|
+
)
|
2103
|
+
|
2104
|
+
# Log detailed error information
|
2105
|
+
log.exception("Git merge --squash failed: %s", error_details)
|
2106
|
+
if recovery_msg:
|
2107
|
+
log.exception("Suggested recovery: %s", recovery_msg)
|
2108
|
+
|
2109
|
+
# Enhanced debugging if verbose mode is enabled
|
2110
|
+
from .utils import is_verbose_mode
|
2111
|
+
|
2112
|
+
if is_verbose_mode():
|
2113
|
+
self._debug_merge_failure_context(base_sha, head_sha)
|
2114
|
+
|
2115
|
+
# Re-raise with enhanced context
|
2116
|
+
raise OrchestratorError(
|
2117
|
+
f"Failed to merge PR commits: {error_details}"
|
2118
|
+
+ (
|
2119
|
+
f"\nSuggested recovery: {recovery_msg}"
|
2120
|
+
if recovery_msg
|
2121
|
+
else ""
|
2122
|
+
)
|
2123
|
+
) from merge_exc
|
2035
2124
|
|
2036
2125
|
def _collect_log_lines() -> list[str]:
|
2037
2126
|
body = run_cmd(
|
@@ -4260,6 +4349,173 @@ class Orchestrator:
|
|
4260
4349
|
except Exception as exc:
|
4261
4350
|
log.debug("File validation failed (non-critical): %s", exc)
|
4262
4351
|
|
4352
|
+
def _analyze_merge_failure(
|
4353
|
+
self, merge_exc: CommandError, base_sha: str, head_sha: str
|
4354
|
+
) -> str:
|
4355
|
+
"""Analyze git merge failure and provide detailed error information."""
|
4356
|
+
error_parts = []
|
4357
|
+
|
4358
|
+
# Include basic command info
|
4359
|
+
if merge_exc.cmd:
|
4360
|
+
error_parts.append(f"Command: {' '.join(merge_exc.cmd)}")
|
4361
|
+
if merge_exc.returncode is not None:
|
4362
|
+
error_parts.append(f"Exit code: {merge_exc.returncode}")
|
4363
|
+
|
4364
|
+
# Analyze stderr for common patterns
|
4365
|
+
stderr = merge_exc.stderr or ""
|
4366
|
+
if "conflict" in stderr.lower():
|
4367
|
+
error_parts.append("Merge conflicts detected")
|
4368
|
+
if "abort" in stderr.lower():
|
4369
|
+
error_parts.append("Merge was aborted")
|
4370
|
+
if "fatal" in stderr.lower():
|
4371
|
+
error_parts.append("Fatal git error occurred")
|
4372
|
+
|
4373
|
+
# Include actual git output
|
4374
|
+
if merge_exc.stdout and merge_exc.stdout.strip():
|
4375
|
+
error_parts.append(f"Git output: {merge_exc.stdout.strip()}")
|
4376
|
+
if stderr and stderr.strip():
|
4377
|
+
error_parts.append(f"Git error: {stderr.strip()}")
|
4378
|
+
|
4379
|
+
return (
|
4380
|
+
"; ".join(error_parts) if error_parts else "Unknown merge failure"
|
4381
|
+
)
|
4382
|
+
|
4383
|
+
def _suggest_merge_recovery(
|
4384
|
+
self, merge_exc: CommandError, base_sha: str, head_sha: str
|
4385
|
+
) -> str:
|
4386
|
+
"""Suggest recovery actions based on merge failure analysis."""
|
4387
|
+
stderr = (merge_exc.stderr or "").lower()
|
4388
|
+
|
4389
|
+
if "conflict" in stderr:
|
4390
|
+
return "Check for merge conflicts in the PR files and resolve them"
|
4391
|
+
elif "fatal: refusing to merge unrelated histories" in stderr:
|
4392
|
+
return (
|
4393
|
+
"The branches have unrelated histories - check if the PR "
|
4394
|
+
"branch is based on the correct target"
|
4395
|
+
)
|
4396
|
+
elif "nothing to commit" in stderr:
|
4397
|
+
return (
|
4398
|
+
"No changes to merge - the PR may already be merged or have "
|
4399
|
+
"no differences"
|
4400
|
+
)
|
4401
|
+
elif "abort" in stderr:
|
4402
|
+
return (
|
4403
|
+
"Previous merge operation may have been interrupted - check "
|
4404
|
+
"repository state"
|
4405
|
+
)
|
4406
|
+
|
4407
|
+
# Try to provide generic guidance
|
4408
|
+
try:
|
4409
|
+
# Check if commits exist between base and head
|
4410
|
+
commits_cmd = ["git", "rev-list", f"{base_sha}..{head_sha}"]
|
4411
|
+
commits_result = run_cmd(
|
4412
|
+
commits_cmd, cwd=self.workspace, check=False
|
4413
|
+
)
|
4414
|
+
if (
|
4415
|
+
commits_result.returncode == 0
|
4416
|
+
and not commits_result.stdout.strip()
|
4417
|
+
):
|
4418
|
+
return (
|
4419
|
+
"No commits found between base and head - PR may be empty "
|
4420
|
+
"or already merged"
|
4421
|
+
)
|
4422
|
+
except Exception as e:
|
4423
|
+
log.debug(
|
4424
|
+
"Failed to check commit range for recovery suggestion: %s", e
|
4425
|
+
)
|
4426
|
+
|
4427
|
+
return (
|
4428
|
+
"Review git repository state and ensure PR branch is properly "
|
4429
|
+
"synchronized with target"
|
4430
|
+
)
|
4431
|
+
|
4432
|
+
def _debug_merge_failure_context(
|
4433
|
+
self, base_sha: str, head_sha: str
|
4434
|
+
) -> None:
|
4435
|
+
"""Provide extensive debugging context for merge failures when verbose mode is enabled.""" # noqa: E501
|
4436
|
+
log.error("=== VERBOSE MODE: Extended merge failure analysis ===")
|
4437
|
+
|
4438
|
+
try:
|
4439
|
+
# Show detailed git log between base and head
|
4440
|
+
log_result = run_cmd(
|
4441
|
+
[
|
4442
|
+
"git",
|
4443
|
+
"log",
|
4444
|
+
"--oneline",
|
4445
|
+
"--graph",
|
4446
|
+
f"{base_sha}..{head_sha}",
|
4447
|
+
],
|
4448
|
+
cwd=self.workspace,
|
4449
|
+
check=False,
|
4450
|
+
)
|
4451
|
+
if log_result.returncode == 0:
|
4452
|
+
log.error("Commits to be merged:\n%s", log_result.stdout)
|
4453
|
+
else:
|
4454
|
+
log.error("Failed to get commit log: %s", log_result.stderr)
|
4455
|
+
|
4456
|
+
# Show file differences
|
4457
|
+
diff_result = run_cmd(
|
4458
|
+
["git", "diff", "--name-status", base_sha, head_sha],
|
4459
|
+
cwd=self.workspace,
|
4460
|
+
check=False,
|
4461
|
+
)
|
4462
|
+
if diff_result.returncode == 0:
|
4463
|
+
log.error(
|
4464
|
+
"Files changed between base and head:\n%s",
|
4465
|
+
diff_result.stdout,
|
4466
|
+
)
|
4467
|
+
|
4468
|
+
# Show merge-base information
|
4469
|
+
merge_base_result = run_cmd(
|
4470
|
+
["git", "merge-base", "--is-ancestor", base_sha, head_sha],
|
4471
|
+
cwd=self.workspace,
|
4472
|
+
check=False,
|
4473
|
+
)
|
4474
|
+
if merge_base_result.returncode == 0:
|
4475
|
+
log.error(
|
4476
|
+
"Base SHA %s is an ancestor of head SHA %s",
|
4477
|
+
base_sha[:8],
|
4478
|
+
head_sha[:8],
|
4479
|
+
)
|
4480
|
+
else:
|
4481
|
+
log.error(
|
4482
|
+
"Base SHA %s is NOT an ancestor of head SHA %s",
|
4483
|
+
base_sha[:8],
|
4484
|
+
head_sha[:8],
|
4485
|
+
)
|
4486
|
+
|
4487
|
+
# Show current repository state
|
4488
|
+
status_result = run_cmd(
|
4489
|
+
["git", "status", "--porcelain"],
|
4490
|
+
cwd=self.workspace,
|
4491
|
+
check=False,
|
4492
|
+
)
|
4493
|
+
if status_result.stdout.strip():
|
4494
|
+
log.error(
|
4495
|
+
"Repository has uncommitted changes:\n%s",
|
4496
|
+
status_result.stdout,
|
4497
|
+
)
|
4498
|
+
|
4499
|
+
# Show current branch and HEAD
|
4500
|
+
branch_result = run_cmd(
|
4501
|
+
["git", "branch", "--show-current"],
|
4502
|
+
cwd=self.workspace,
|
4503
|
+
check=False,
|
4504
|
+
)
|
4505
|
+
if branch_result.returncode == 0:
|
4506
|
+
log.error("Current branch: %s", branch_result.stdout.strip())
|
4507
|
+
|
4508
|
+
head_result = run_cmd(
|
4509
|
+
["git", "rev-parse", "HEAD"], cwd=self.workspace, check=False
|
4510
|
+
)
|
4511
|
+
if head_result.returncode == 0:
|
4512
|
+
log.error("Current HEAD: %s", head_result.stdout.strip())
|
4513
|
+
|
4514
|
+
except Exception:
|
4515
|
+
log.exception("Failed to gather debug context")
|
4516
|
+
|
4517
|
+
log.error("=== End verbose merge failure analysis ===")
|
4518
|
+
|
4263
4519
|
|
4264
4520
|
# ---------------------
|
4265
4521
|
# Utility functions
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: github2gerrit
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.16
|
4
4
|
Summary: Submit a GitHub pull request to a Gerrit repository.
|
5
5
|
Project-URL: Homepage, https://github.com/lfreleng-actions/github2gerrit
|
6
6
|
Project-URL: Repository, https://github.com/lfreleng-actions/github2gerrit
|
@@ -1,8 +1,8 @@
|
|
1
1
|
github2gerrit/__init__.py,sha256=N1Vj1HJ28LKCJLAynQdm5jFGQQAz9YSMzZhEfvbBgow,886
|
2
|
-
github2gerrit/cli.py,sha256=
|
2
|
+
github2gerrit/cli.py,sha256=edQGODSo0A1RfvO0C7zAjWwIZCnt4RT67BnJwgL0MAY,67843
|
3
3
|
github2gerrit/commit_normalization.py,sha256=5HqUJ7p3WQzUYNTkganIsu82aW1wZrh7J7ivQvdCYXw,17033
|
4
4
|
github2gerrit/config.py,sha256=khuXAfmOc2wqO4r1Cpp8PYk04AO-JpmnJvSWQZ3fdL8,22315
|
5
|
-
github2gerrit/core.py,sha256=
|
5
|
+
github2gerrit/core.py,sha256=hf7XDZ83PD_dYu79u-9u_pboDqu_iL9TpdIaa64_q-0,172360
|
6
6
|
github2gerrit/duplicate_detection.py,sha256=HSL1IpyfPk0yLkzCKA8mWadYB3qENR6Ar3AfiH59lCI,28766
|
7
7
|
github2gerrit/external_api.py,sha256=9483kkgIs1ECOl_f0lcGb8GrJQF9IfYmWfBQwUJT9hk,18480
|
8
8
|
github2gerrit/gerrit_query.py,sha256=7AR9UEwZxtTb9V-l17UDdtWvlvROlnIeJVMa57ilf6s,8343
|
@@ -24,8 +24,8 @@ github2gerrit/trailers.py,sha256=9w0vIxPNBNQp56sIy-MF62d22Rm6vY-msh9ao1lX0rQ,838
|
|
24
24
|
github2gerrit/utils.py,sha256=1CKTsQo_FO3eyVjzNUT3XyFnyObIxyEFLeSmVKzavVo,3397
|
25
25
|
github2gerrit/orchestrator/__init__.py,sha256=HAEcdCAHOFr8LsdIwAdcIcFZn_ayMbX9rdVUULp8410,864
|
26
26
|
github2gerrit/orchestrator/reconciliation.py,sha256=qLhmjnIblSa_PVmBblWJ1gSvJ6Gto2AIVOVdYvT6zbk,18364
|
27
|
-
github2gerrit-0.1.
|
28
|
-
github2gerrit-0.1.
|
29
|
-
github2gerrit-0.1.
|
30
|
-
github2gerrit-0.1.
|
31
|
-
github2gerrit-0.1.
|
27
|
+
github2gerrit-0.1.16.dist-info/METADATA,sha256=02ry4GJBvmh4lqF27joxLA_s9be7UqOv95-89plq_fQ,33833
|
28
|
+
github2gerrit-0.1.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
29
|
+
github2gerrit-0.1.16.dist-info/entry_points.txt,sha256=MxN2_liIKo3-xJwtAulAeS5GcOS6JS96nvwOQIkP3W8,56
|
30
|
+
github2gerrit-0.1.16.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
31
|
+
github2gerrit-0.1.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|