diff --git a/svn_mirror/git_to_svn.py b/svn_mirror/git_to_svn.py index 5ad283b..060a160 100644 --- a/svn_mirror/git_to_svn.py +++ b/svn_mirror/git_to_svn.py @@ -133,9 +133,15 @@ def _ensure_wc(mirror: Mirror, svn_branch: str): try: _run_svn(["checkout", "--ignore-externals", svn_url, str(wc)], timeout=900, auth_args=auth_args) - except GitToSVNError: + except GitToSVNError as e: _cleanup_failed_wc(wc) - raise + if _is_svn_not_found(e): + _create_svn_branch(mirror, svn_branch, auth_args) + wc.mkdir(parents=True, exist_ok=True) + _run_svn(["checkout", "--ignore-externals", svn_url, str(wc)], + timeout=900, auth_args=auth_args) + else: + raise else: _try_revert(wc, auth_args) try: @@ -189,6 +195,40 @@ def _cleanup_failed_wc(wc: Path): logger.warning("Could not fully remove failed WC at %s", wc) +def _is_svn_not_found(e: GitToSVNError) -> bool: + return "E170000" in str(e) + + +def _create_svn_branch(mirror: Mirror, svn_branch: str, + auth_args: Optional[list]): + """Create an SVN branch by copying from trunk. + + Called when a Git branch has no corresponding SVN path yet. + """ + root = mirror.config.svn.url.rstrip("/") + trunk_url = f"{root}/{mirror.config.svn.trunk}" + branch_url = f"{root}/{svn_branch}" + branch_name = svn_branch.rsplit("/", 1)[-1] + msg = f"Create branch {branch_name} from {mirror.config.svn.trunk}" + logger.info("Creating SVN branch: %s …", branch_url) + result = _run_svn( + ["copy", trunk_url, branch_url, "-m", msg], + timeout=120, auth_args=auth_args, + ) + new_rev = _parse_commit_revision(result) + # Restore author revprop (svn copy uses buildbot as committer) + try: + _run_svn( + ["propset", "--revprop", "-r", str(new_rev), + "svn:author", mirror.config.svn.username or "buildbot", + root], + timeout=30, auth_args=auth_args, + ) + except GitToSVNError: + logger.debug("Could not set svn:author for branch creation r%d", new_rev) + logger.info("Created SVN branch %s (r%d)", branch_url, new_rev) + + # ─── Author mapping (reverse) ──────────────────────────────────