@@ -121,13 +121,20 @@ def title(self) -> str:
121121 return f"{ self .title_prefix } { self .release .tag_name } "
122122
123123 @property
124- def branch (self ) -> str :
124+ def template_branch (self ) -> str :
125+ """Branch name in the forked repo that tracks template updates (stay the same across versions)"""
125126 # as of v0.5.0 (new template sync), the branch name does not contain the release-tag anymore
126127 return f"{ self .branch_prefix } { self .repo_id } "
127128
129+ @property
130+ def pr_branch (self ) -> str :
131+ """Name of the branch that is used to create the pull-request. A new branch is created for each version."""
132+ return f"{ self .template_branch } -{ self .release .tag_name } "
133+
128134 @property
129135 def namespaced_head (self ) -> str :
130- return f"{ self .con .login } :{ self .branch } "
136+ """Branch used to crate the pull request, including repo namespace"""
137+ return f"{ self .con .login } :{ self .pr_branch } "
131138
132139 @property
133140 def body (self ) -> str :
@@ -141,9 +148,9 @@ def matches_prefix(self, pr: PullRequest) -> bool:
141148 # Don’t compare title prefix, people might rename PRs
142149 return pr .head .ref .startswith (self .branch_prefix ) and pr .user .id == self .con .user .id
143150
144- def matches_exact_branch_name (self , pr : PullRequest ) -> bool :
151+ def matches_current_version (self , pr : PullRequest ) -> bool :
145152 """Check if `pr` is a template update PR for the current version"""
146- return pr .head .ref == self .branch and pr .user .id == self .con .user .id
153+ return pr .head .ref == self .pr_branch and pr .user .id == self .con .user .id
147154
148155
149156class RepoInfo (TypedDict ):
@@ -382,6 +389,7 @@ def template_update( # noqa: PLR0913, (= too many function arguments)
382389 forked_repo : GHRepo ,
383390 original_repo : GHRepo ,
384391 template_branch_name : str ,
392+ versioned_branch_name : str ,
385393 tag_name : str ,
386394 cruft_log_file : Path ,
387395 dry_run : bool ,
@@ -400,6 +408,8 @@ def template_update( # noqa: PLR0913, (= too many function arguments)
400408 4) Use `cruft create` to instantiate the template into a separate directory
401409 5) sync the changes from the separate directory into the `template-branch`
402410 6) commit
411+ 7) check out commit into a version-specific branch used for making the pull request. See #396 for why this is
412+ necessary.
403413
404414 --> From this commit, we can make a pull-request to the original repo including the latest template-changes.
405415
@@ -411,6 +421,8 @@ def template_update( # noqa: PLR0913, (= too many function arguments)
411421 The repo forked in scverse-bot namespace
412422 template_branch_name
413423 branch name to use for the template in the forked repo
424+ versioned_branch_name
425+ version-specific branch name (will be created off the template branch)
414426 original_repo
415427 The original (upstream) repo
416428 tag_name
@@ -446,17 +458,19 @@ def template_update( # noqa: PLR0913, (= too many function arguments)
446458 tmp_config = json .load (f )
447459 exclude_files = tmp_config ["context" ]["cookiecutter" ].get ("_exclude_on_template_update" , [])
448460
449- if _commit_update (
450- clone ,
451- exclude_files = exclude_files ,
452- commit_msg = f"Automated template update to { tag_name } " ,
453- commit_author = f"{ con .sig .name } <{ con .sig .email } >" ,
454- ):
455- if not dry_run :
456- clone .git .push ("origin" , template_branch_name )
457- return True
458- else :
459- return False
461+ if (
462+ updated := _commit_update (
463+ clone ,
464+ exclude_files = exclude_files ,
465+ commit_msg = f"Automated template update to { tag_name } " ,
466+ commit_author = f"{ con .sig .name } <{ con .sig .email } >" ,
467+ )
468+ ) and not dry_run :
469+ clone .git .switch (versioned_branch_name , template_branch_name , C = True )
470+ clone .git .push ("origin" , template_branch_name )
471+ clone .git .push ("origin" , versioned_branch_name )
472+
473+ return updated
460474
461475
462476def make_pr (con : GitHubConnection , release : GHRelease , repo_url : str , * , log_dir : Path , dry_run : bool = False ) -> None :
@@ -490,27 +504,33 @@ def make_pr(con: GitHubConnection, release: GHRelease, repo_url: str, *, log_dir
490504 con ,
491505 forked_repo = forked_repo ,
492506 original_repo = original_repo ,
493- template_branch_name = pr .branch ,
507+ template_branch_name = pr .template_branch ,
508+ versioned_branch_name = pr .pr_branch ,
494509 tag_name = release .tag_name ,
495- cruft_log_file = log_dir / f"{ pr .branch } .log" ,
510+ cruft_log_file = log_dir / f"{ pr .template_branch } .log" ,
496511 dry_run = dry_run ,
497512 )
498513 if dry_run :
499514 log .info ("Skipping PR because in dry-run mode" )
500515 return
501516 if updated :
502- if old_pr := next ((p for p in original_repo .get_pulls ("open" ) if pr .matches_exact_branch_name (p )), None ):
517+ if old_pr := next ((p for p in original_repo .get_pulls ("open" ) if pr .matches_current_version (p )), None ):
503518 log .info (f"PR already exists: #{ old_pr .number } with branch name `{ old_pr .head .ref } `. Skipping PR creation." )
504- else :
505- log .info (f"Creating PR of { pr .namespaced_head } against { original_repo .default_branch } " )
506- new_pr = original_repo .create_pull (
507- title = pr .title ,
508- body = pr .body ,
509- base = original_repo .default_branch ,
510- head = pr .namespaced_head ,
511- maintainer_can_modify = True ,
512- )
513- log .info (f"Created PR #{ new_pr .number } with branch name `{ new_pr .head .ref } `." )
519+ return
520+
521+ if old_pr := next ((p for p in original_repo .get_pulls ("open" ) if pr .matches_prefix (p )), None ):
522+ log .info (f"Closing old PR #{ old_pr .number } with branch name `{ old_pr .head .ref } `." )
523+ old_pr .edit (state = "closed" )
524+
525+ log .info (f"Creating PR of { pr .namespaced_head } against { original_repo .default_branch } " )
526+ new_pr = original_repo .create_pull (
527+ title = pr .title ,
528+ body = pr .body ,
529+ base = original_repo .default_branch ,
530+ head = pr .namespaced_head ,
531+ maintainer_can_modify = True ,
532+ )
533+ log .info (f"Created PR #{ new_pr .number } with branch name `{ new_pr .head .ref } `." )
514534
515535
516536cli = App ()
0 commit comments