4141 - task : _fetch-patches
4242 - task : _validate-patches
4343 - task : _import-patches
44- - task : _duplicate -patches
44+ - task : _octopus -patches
4545 - task : _verify-patches
4646
4747 check :
@@ -73,9 +73,13 @@ tasks:
7373 internal : true
7474 requires :
7575 vars : [PATCH_BRANCHES]
76+ env :
77+ DECLARED_PATCH_BRANCHES : ' {{.PATCH_BRANCHES}}'
7678 cmds :
77- - for : { var: PATCH_BRANCHES }
78- cmd : git check-ref-format --branch '{{.ITEM}}' >/dev/null
79+ - |
80+ while IFS= read -r branch; do
81+ git check-ref-format --branch "${branch}" >/dev/null
82+ done <<< "${DECLARED_PATCH_BRANCHES}"
7983
8084 _clean-auth :
8185 internal : true
@@ -101,27 +105,32 @@ tasks:
101105 env :
102106 GIT_ASKPASS : ' {{.PATCH_AUTH_DIR}}/git-askpass'
103107 GIT_TERMINAL_PROMPT : ' 0'
108+ PATCHES_FETCH_URL : ' {{.PATCHES_REMOTE_URL}}'
109+ DECLARED_PATCH_BRANCHES : ' {{.PATCH_BRANCHES}}'
104110 cmds :
105- - for : { var: PATCH_BRANCHES }
106- cmd : >-
107- git fetch --no-tags '{{.PATCHES_REMOTE_URL}}'
108- '+refs/heads/{{.ITEM}}:refs/remotes/patches/{{.ITEM}}'
111+ - |
112+ refspecs=()
113+ while IFS= read -r branch; do
114+ refspecs+=("+refs/heads/${branch}:refs/remotes/patches/${branch}")
115+ done <<< "${DECLARED_PATCH_BRANCHES}"
116+ git fetch --no-tags "${PATCHES_FETCH_URL}" "${refspecs[@]}"
109117
110118 _validate-patches :
111119 internal : true
120+ env :
121+ DECLARED_PATCH_BRANCHES : ' {{.PATCH_BRANCHES}}'
112122 cmds :
113- - for : { var: PATCH_BRANCHES }
114- task : _validate-patch
115- vars :
116- PATCH_BRANCH : ' {{.ITEM}}'
117-
118- _validate-patch :
119- internal : true
120- requires :
121- vars : [PATCH_BRANCH]
122- preconditions :
123- - sh : test "$(git show -s --format='%P' 'refs/remotes/patches/{{.PATCH_BRANCH}}' | wc -w)" -eq 1
124- msg : " commercial patch branch must point to one non-merge commit: {{.PATCH_BRANCH}}"
123+ - |
124+ # Patch branches carry real multi-commit history plus sync-merge
125+ # commits, so no commit-shape rule applies; each branch only has to
126+ # share history with the target so the octopus has a merge base.
127+ base=$(git rev-parse HEAD)
128+ while IFS= read -r branch; do
129+ if ! git merge-base "refs/remotes/patches/${branch}" "${base}" >/dev/null; then
130+ echo "commercial patch branch shares no history with the target: ${branch}" >&2
131+ exit 1
132+ fi
133+ done <<< "${DECLARED_PATCH_BRANCHES}"
125134
126135 _import-patches :
127136 internal : true
@@ -135,34 +144,42 @@ tasks:
135144 - test -d .jj
136145 cmd : jj git init --colocate >/dev/null
137146
138- _duplicate -patches :
147+ _octopus -patches :
139148 internal : true
140- vars :
141- PATCH_REVSET :
142- sh : |
143- printf '%s\n' '{{.PATCH_BRANCHES}}' | while IFS= read -r branch; do
144- git rev-parse "refs/remotes/patches/${branch}^{commit}"
145- done | paste -sd '|' -
146- PATCHSET_ID :
147- sh : printf '%s' '{{.PATCH_REVSET}}' | git hash-object --stdin
148- PATCHSET_DESCRIPTION : ' release patches {{.PATCHSET_ID}}'
149+ env :
150+ DECLARED_PATCH_BRANCHES : ' {{.PATCH_BRANCHES}}'
149151 status :
150- - test "$(jj log -r @ --no-graph -T 'description.first_line()')" = '{{.PATCHSET_DESCRIPTION}}'
152+ - |
153+ patch_revset=$(printf '%s\n' "${DECLARED_PATCH_BRANCHES}" | while IFS= read -r branch; do
154+ git rev-parse "refs/remotes/patches/${branch}^{commit}"
155+ done | paste -sd '|' -)
156+ patchset_id=$(printf '%s' "${patch_revset}" | git hash-object --stdin)
157+ test "$(jj log -r @ --no-graph -T 'description.first_line()')" = "release patches ${patchset_id}"
151158 cmds :
152159 - |
160+ patch_revset=$(printf '%s\n' "${DECLARED_PATCH_BRANCHES}" | while IFS= read -r branch; do
161+ git rev-parse "refs/remotes/patches/${branch}^{commit}"
162+ done | paste -sd '|' -)
163+ patchset_id=$(printf '%s' "${patch_revset}" | git hash-object --stdin)
153164 base=$(jj log -r @ --no-graph -T commit_id)
154- jj duplicate '({{.PATCH_REVSET}})' --onto @
155- jj new "heads(${base}::) ~ ${base}" -m '{{.PATCHSET_DESCRIPTION}}'
165+ # Octopus of the fetched tips onto the target. heads() drops
166+ # redundant parents (a stacked branch already contains its parent's
167+ # tip, and a tip may already contain the base).
168+ jj new "heads(${base}|${patch_revset})" -m "release patches ${patchset_id}"
156169
157170 _verify-patches :
158171 internal : true
172+ env :
173+ DECLARED_PATCH_BRANCHES : ' {{.PATCH_BRANCHES}}'
159174 preconditions :
160175 - sh : test -z "$(jj log -r '::@ & conflicts()' --no-graph -T commit_id)"
161176 msg : " commercial patches did not apply cleanly; run 'jj resolve --list'"
162177 cmds :
163178 - echo "Applied commercial patches from {{.PATCHES_SERIES}}:"
164- - for : { var: PATCH_BRANCHES }
165- cmd : echo ' {{.ITEM}}'
179+ - |
180+ while IFS= read -r branch; do
181+ echo " ${branch}"
182+ done <<< "${DECLARED_PATCH_BRANCHES}"
166183
167184 _clean-checkout :
168185 internal : true
0 commit comments