internet2/grouper/cherry-pick

Claude Code and agent skill by internet2/grouper

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Cherry-pick / merge git commits between Grouper branches (v4, v6, v7, v9). Lists commits by JIRA ID in the release-bounded range, checks which are already applied to the target branch, flags version-specific features that must not be backported, and cherry-picks in chronological order with per-JIRA approval. Use this skill whenever the user mentions cherry-picking, merging commits between branches, syncing branches, backporting, or bringing commits from one Grouper version to another. Also trig…

Install

npx vskill@latest install internet2/grouper/cherry-pick

SKILL.md

Cherry-pick / merge git commits for Grouper

Cherry-pick commits from one Grouper branch to another, identifying what needs to be merged by JIRA ticket and skipping already-applied or inapplicable changes.

Typical use is a backport: work lands on the newest branch (v7), and the maintenance branch (v6, v4) picks up the subset that applies, usually right before that branch's release.

Branch and repo layout

Work with one checkout per branch, so no branch switching is needed. A common layout:

  • $HOME/git/grouper_v4 -- GROUPER_4_BRANCH
  • $HOME/git/grouper_v6 -- GROUPER_6_BRANCH
  • $HOME/git/grouper_v7 -- GROUPER_7_BRANCH

Do NOT switch branches in the current working directory. Run the skill from the TARGET branch's checkout. Read the source branch with git log <branch> -- all branches are in the same repo, so a fetched clone can read any of them.

Fetch before starting:

bash
git fetch origin --tags

Release tags

Release tags follow the format GROUPER_RELEASE_a.b.c, e.g. GROUPER_RELEASE_6.3.0, GROUPER_RELEASE_7.4.0.

Workflow

1. Identify source and target

Ask if not clear:

  • Source branch: where commits are coming FROM (e.g. GROUPER_7_BRANCH)
  • Target branch: where commits are going TO (the current checkout's branch)
2. Find the release tags that bound the range

Two different tags matter -- do not confuse them:

  • Lower bound: the last release tag on the TARGET branch. This is where the

target left off, so it is the starting point for what still needs to come over.

  • Upper bound: the last release tag on the SOURCE branch. See step 3 -- the

range STOPS here, it does not run to the source branch HEAD.

bash
# last release on the target (lower bound), e.g. GROUPER_RELEASE_6.3.0
git tag --sort=-creatordate | grep "GROUPER_RELEASE_<target_prefix>" | head -1
# last release on the source (upper bound), e.g. GROUPER_RELEASE_7.4.0
git tag --sort=-creatordate | grep "GROUPER_RELEASE_<source_prefix>" | head -1
3. List commits in range -- ALWAYS cap at the SOURCE branch's last release

Never list to the source branch HEAD. Commits made on the source branch after its most recent release tag have not shipped in any release yet -- they are unreleased work still baking. Pulling them into the target would ship code in the target's release that the source has never released, which is backwards. Cap the range at the source branch's last release tag.

Use the tag itself as the revision argument (git log ... <SOURCE_TAG>), NOT <branch>. This also avoids a subtle trap: --after filters on AUTHOR date, and some commits land on the branch after the tag while carrying an earlier author date, so a date-only filter against the branch silently pulls post-release commits in. The tag argument bounds by ancestry, which is what you actually want.

For the lower bound, take the date of the TARGET's last release tag and subtract about a week. This catches commits made just before that release but not cherry-picked into it.

Include the commit timestamp and author in the output so they can be shown in the tables.

bash
# target's release tag date (lower bound)
git log -1 --format=%ai GROUPER_RELEASE_<target_version>
# source release tag and its commit (upper bound)
git log -1 --format="%h %ai %an%n%s" GROUPER_RELEASE_<source_version>
# commits, oldest first, bounded ABOVE by the source release tag
git log --reverse --after="<target tag date minus 7 days>" \
--format="%h|%ai|%an|%s" GROUPER_RELEASE_<source_version>

Show the user the source release tag and its commit before listing, and state how many commits sit above the cap and are therefore excluded:

bash
git log --reverse --format="%h|%ai|%an|%s" GROUPER_RELEASE_<source_version>..<source_branch>

If the user genuinely wants unreleased source-branch work too, they will say so -- ask before including it, never default to it.

4. Group by JIRA ID

Parse commit messages for JIRA IDs (GRP-XXXX) and group commits by ticket. Commits with no JIRA ID go in a separate list for review (they are usually mechanical, e.g. "wiki: sync", and usually skipped).

Multiple commits often share one JIRA ID (e.g. "GRP-1234 (commit 2)"). Keep them together and cherry-pick them in order.

5. Check which are already applied

For each JIRA ID, check whether it already exists on the target branch. Match by JIRA ID in the commit message, NOT by commit hash -- cherry-picks create new hashes.

bash
git log --oneline <target_branch> | grep "GRP-XXXX"

Mark each JIRA as:

  • Already applied -- skip. Note that some of these were done on the target

branch FIRST and forward-ported to the source, which is normal.

  • Not yet applied -- candidate.
  • Partially applied -- some commits for this JIRA exist on target but not all;

flag for review.

6. Flag version-specific features

Some features exist only on newer branches. Flag them so the user knows they are being skipped, but still list them.

Featurev4v6v7v9
Provisioningyesyesyesyes
Data field subjects/sourcesnoyesyesyes
Data fields for ABACnoyesyesyes
User lifecycle eventsnoyesyesyes
MCP / OAuth for MCPnonoyesyes
Sync-back / native-sync capturenonoyesyes
Compiled GSH templates (templateMode=compiled)nonoyesyes
Composite-in-place conversion (GRP-7187)nonoyesyes

Identify features by keywords in commit messages and changed file paths:

  • MCP: "mcp", "MCP", or files in MCP packages
  • Data fields / ABAC: "data field", "abac", "dataField"
  • User lifecycle: "lifecycle", "user lifecycle"
  • OAuth for MCP: "oauth", "OAuth"
  • Sync-back: "sync-back", "sync back", "syncBack", "native sync", or files

named *ProvisioningTargetNativeSync.java

  • Compiled GSH templates: "compiled", "compiledJava", "templateMode",

GshTemplateMode, GshTemplateCompile*, or config comments gated on showEl: "${scriptType == 'compiledJava'}"

Compiled GSH templates are v7+ only

v6 and v4 have the interpreted GshTemplateV2 API but NOT compiled mode. A commit that touches compiled daemon/change-log templates cannot be backported -- the templateMode / compiledJava config values and the GshTemplateMode enum do not exist on the older branches, so even a config-comment-only change edits a line that is not there. Skip it.

This one is easy to misjudge, because the GSH template classes with the same names DO exist on the target -- it is only compiled mode that is missing. Check the capability, not the class names:

bash
# each returns 0 on a branch without compiled mode
for t in templateMode compiledJava GshTemplateCompile "TemplateMode.compiled"; do
echo "$t: $(git grep -il "$t" <target_branch> -- 'grouper/src' 'grouper/conf' | wc -l)"
done
Composite-in-place conversion is v7+ only

Converting a group that already has members into a composite in place, without change log / PIT churn (GRP-7187: CompositeInPlaceConverter, plus the CompositeSave and Group changes that back it), stays on v7 and later. Do not backport it to v6 or v4, and do not treat it as an oversight -- it is a deliberate decision to keep that path off the maintenance branches, because it reaches into core Group / CompositeSave membership handling and bypasses the normal change log. Skip it and any follow-up commit that builds on it.

Sync-back is v7+ only -- ALWAYS skip it on v6 and earlier

Sync-back (the native-sync capture layer) does not exist on v6 or v4. Skip every sync-back commit when the target is v6 or earlier -- do not ask, just skip and say why. Porting it is a feature port, not a cherry-pick: it would mean creating the whole *ProvisioningTargetNativeSync.java family plus its grouper-loader.base.properties keys, and it conflicts heavily.

Verify against the repo rather than trusting this table -- both commands return nothing on a branch without the feature:

bash
git ls-tree -r --name-only <target_branch> | grep -iE "nativesync|syncback"
git grep -il -E "captureMembershipsFromCache|ProvisioningTargetNativeSync|syncBackCache|fullSyncFromSyncBack" \
<target_branch> -- 'grouper/src'

This was a deliberate decision, not an oversight -- the v6 branch carries an explicit divergence commit recording it:

GRP-7062: (commit 2) v4/v6 only: remove sync-back captureUserInsertFromCurrentProvisioner
call on the 409-link path; the native-sync capture layer does not exist on v6

Watch for sync-back riding along inside a commit that is otherwise applicable (e.g. a provisioner series where one commit adds sync-back support). Skip just that commit and keep the rest of the JIRA's commits.

New provisioners and external systems ARE backported

A brand-new provisioner or external system is cherry-picked back to v4 and v6, not treated as a v7-only feature. Do not auto-skip one just because every file it touches is new on the target -- "all files missing" is the normal shape of a new provisioner, and it is the one case where that heuristic gives the wrong answer. Recommend picking it.

The catch: a provisioner written on v7 often has sync-back woven into the very commit that creates it, so there is no clean commit to drop. Bring the provisioner over and then strip the sync-back parts on the target:

  • Delete the *ProvisioningTargetNativeSync.java file the commit adds.
  • Remove captureUserInsert* / captureMembership* / captureGroup* calls and

the native-sync imports from the target DAO.

  • Remove the provisioner's syncBack / nativeSync keys from

grouper-loader.base.properties and any matching externalized text.

  • Drop any native-sync test class, and any sync-back cases inside a kept test.

This is avoidable at authoring time -- see the "How to commit this work" section of the grouper-provisioner skill, which says to keep sync-back in its own commit so the backport is "skip commit N" instead. CCure did that and applied clean to v4 and v6; Jamf did not and needed the surgery below on both branches.

Do this as a modified pick: git cherry-pick -n <hash> applies without committing; make the removals in the working tree, then stage and commit it yourself. Say plainly that it is a modified pick and exactly what was stripped, so the divergence is deliberate and recorded -- the way GRP-7062 (commit 2) did.

Always inspect the changed files before asking

Even when a commit is not auto-flagged above, the code it touches may simply not exist on the target. For each candidate:

  1. git show <hash> --stat to see what files it touches.
  2. For non-trivial commits, grep the target branch for the key classes, methods,

JSPs, properties keys, or UI components the commit modifies.

  1. If the target does not have that code at all, auto-skip and say so -- do not

ask. Wasted confirmations on inapplicable commits erode trust.

  1. Only ask when the change genuinely applies.

A single JIRA can be half-applicable: some hunks touch shared code and some touch a class that exists only on the source branch. Symptom is a cherry-pick conflict spanning hundreds or thousands of lines, because git could not place the hunks and fell back to offering the whole region. Do not try to resolve that block by hand. Instead:

  1. Reset the conflicted file to the target's version: git show HEAD:<path> > <path>
  2. Re-read the source diff hunk by hunk (git show <hash> -- <path>).
  3. Apply only the hunks whose surrounding code exists on the target, checking that

every variable they reference is in scope there.

  1. Tell the user exactly which half you kept and which you dropped, and why.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
103
Repository forks
82
7-day trend score
0

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/internet2/grouper/cherry-pick/badge)](https://verified-skill.com/skills/internet2/grouper/cherry-pick)

Something wrong with this skill or its page? Tell us.