agentsclimarketplace

Cross repo cherry pick

Skill OmarEltak/legacy-prod-survival-kit/skills/cross-repo-cherry-pick

Claude Code skills for the solo engineer who just inherited a 17-year-old production system. Audit, deploy, monitor, and report — without a DevOps team.

Install
npx -y skills add OmarEltak/legacy-prod-survival-kit --skill cross-repo-cherry-pick

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when you need to apply commits from one git repository to another whose history is unrelated. Common scenario; an old "dev" repo with N commits of undeployed work, and a fresh "prod-mirror" repo created from the live server. You want N-3 of those commits applied to the mirror without merging the histories. Handles the line-ending chaos (CRLF/LF) that occurs when source was on Windows and destination came from a Linux server. Includes a CRLF/LF detective sub-protocol.

SKILL.md

9.8 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it

Cross-repo cherry-pick (with line-ending intelligence)

When to use this

You have two git repositories with unrelated histories:

  • Source repo: an old development repository with commits you want to ship.
  • Target repo: a newer repository that was initialised from a different starting point (often: a live production server's current state).

You want to bring specific commits — or a contiguous range, or "everything since commit X" — from source into target. Crucially:

  • You do not want to merge the histories. They're unrelated; merging would be nonsense.
  • You do not want a single giant "initial sync" commit; you want the actual content of those specific commits.
  • You want the target's git history to make sense as "mirror of prod, plus these intentional changes."

This is harder than it sounds because:

  • git cherry-pick works only when commits live in the same DAG.
  • git format-patch + git am fails across repos because patches reference parent blob SHAs that don't exist in the target.
  • Working-tree files often have different line endings between the two repos (Windows checkout = CRLF; production pull = mixed CRLF/LF). Naive copy produces a "huge diff" that's 95% line-ending noise.

The right approach (chosen after trial-and-error)

  1. Identify the file set. What files were touched in the commit range you want?
  2. Detect line-ending convention in the target repo for those files (per-file).
  3. Extract content from source git blobs, not from the source working tree. Source git stores LF (typically); working tree may have CRLF after checkout.
  4. Write content to target with the target's line-ending convention (per-file match).
  5. Verify diff with --ignore-all-space to see the actual content delta vs the line-ending noise.
  6. Commit on a non-deployed branch with a message preserving the source commit summaries.

Concrete protocol

Step 1 — identify files in the commit range

In the source repo:

# range: from <start>^ (exclusive) to <end> (inclusive)
git diff --name-status <start>^..<end>

Output is M for modified, A for added, D for deleted, R for renamed. List of files is your work scope.

For a single commit:

git diff --name-status <commit>^..<commit>

Step 2 — line-ending detective

Don't trust assumptions. Check actual bytes per file in both repos.

# In source repo — what's in the git blob (typically LF)
git show HEAD:<file> | head -1 | od -c | head -1

# In source repo working tree — what's on disk (often CRLF after Windows checkout)
head -1 <file> | od -c | head -1

# In target repo — what's in HEAD
git -C <target> show HEAD:<file> | head -1 | od -c | head -1

You're looking for \r \n (CRLF) vs just \n (LF) at the end of the first line.

Common findings:

Source git blobSource working treeTarget git blobImplication
LFCRLF (autocrlf checkout)LFEasy — extract from blob, write to target
LFCRLFCRLFExtract from blob, convert LF→CRLF, write
LFCRLFMixed (some files LF, some CRLF)Per-file detection required

A 17-year-old PHP codebase that has been touched by both Windows and Linux editors will be inconsistent — some files LF, some CRLF, in the same repo.

Step 3 — extract from blobs (not working tree)

# Get the LF-correct content of the source file at HEAD (or any commit)
git -C <source> show HEAD:<file> > /tmp/blob-content

This gives you exactly what's in git, with no checkout-time conversion.

Step 4 — match target's convention

Per file, check target's line ending and convert if needed:

# Detect target's convention for a file
target_ending=$(git -C <target> show HEAD:<file> 2>/dev/null | head -c 4096 | grep -c $'\r')

# If $target_ending > 0, target uses CRLF for this file
if [ "$target_ending" -gt 0 ]; then
    sed 's/$/\r/' /tmp/blob-content > /tmp/final-content
else
    cp /tmp/blob-content /tmp/final-content
fi

cp /tmp/final-content <target>/<file>

Step 5 — verify the actual diff

cd <target>
git status -s              # files changed
git diff --stat            # raw diff (will look huge if lines differ)
git diff --stat --ignore-all-space   # real content delta

Expect a discrepancy: if a file had CRLF in source-git's blob (rare) but you wrote LF to target, every line will show as changed in the raw diff. The --ignore-all-space view is the source of truth for "what content actually changed."

If the --ignore-all-space numbers match what the source commits' git log --stat showed, you applied the changes correctly.

Step 6 — commit on a safe branch

cd <target>
git checkout -b dev   # if not already on a non-deployed branch
git add -A
git commit -m "$(cat <<'EOF'
feat: apply <feature-name> from <source-repo> to prod-mirror

Cherry-picks commits <range> from <source-repo>:

  <commit-sha-1> <message-1>
  <commit-sha-2> <message-2>
  ...

Files:
- <file-1>
- <file-2>

Note: line endings normalized to <LF|CRLF|mixed> to match target convention.
Real content delta (ignoring whitespace): +X / -Y lines.
EOF
)"

Push to a branch that does not auto-deploy. If your CI deploys on main push, push to dev or any other branch.

A more robust automated version (Bash one-liner)

For when you trust the protocol:

#!/usr/bin/env bash
# cross-repo-cherrypick.sh <source-repo-dir> <target-repo-dir> <range>
# e.g.: ./cross-repo-cherrypick.sh ~/old-dev ~/prod-mirror 6548e50^..HEAD
set -euo pipefail

SRC="$1"
DST="$2"
RANGE="$3"

# 1. List files
files=$(git -C "$SRC" diff --name-only "$RANGE")

# 2. For each file, extract from source blob, match target convention, write
while IFS= read -r f; do
    [ -z "$f" ] && continue
    mkdir -p "$DST/$(dirname "$f")"
    
    # Extract source content (blob — LF normalized)
    src_content=$(git -C "$SRC" show "HEAD:$f" 2>/dev/null) || {
        echo "MISSING in source HEAD: $f"
        continue
    }
    
    # Detect target convention (default to LF if file doesn't exist yet)
    target_uses_crlf="no"
    if [ -f "$DST/$f" ]; then
        if head -c 4096 "$DST/$f" | grep -q $'\r'; then
            target_uses_crlf="yes"
        fi
    fi
    
    # Write with appropriate line ending
    if [ "$target_uses_crlf" = "yes" ]; then
        printf '%s' "$src_content" | sed 's/$/\r/' > "$DST/$f"
    else
        printf '%s' "$src_content" > "$DST/$f"
    fi
    echo "OK $f"
done <<< "$files"

echo "Done. Run \`cd $DST && git diff --stat --ignore-all-space\` to verify."

CRLF / LF detective protocol (sub-skill)

When you see a "huge git diff that doesn't make sense":

  1. Check the file's blob in HEAD on both sides. Use git show <ref>:<file> | head -1 | od -c | head -1.
  2. Check the file on disk in both working trees. Use head -1 <file> | od -c | head -1.
  3. Compare. Find the mismatch.
  4. Decide which convention you want to standardize on for the target repo.
  5. Convert before committing. Use sed 's/\r$//' (CRLF→LF) or sed 's/$/\r/' (LF→CRLF).
  6. Re-check git diff --stat --ignore-all-space to verify actual content matches expectations.

The pattern: git diff output is very rarely lying about content. It IS often lying about how much you actually changed, because line endings inflate the apparent diff. Always cross-check with --ignore-all-space.

Why this skill exists

In one engagement, this exact problem ate ~45 minutes:

  1. Copied working-tree files from source to target. ❌ Broken — Windows checkout had CRLF, target had LF for some files.
  2. Converted all to LF. ❌ Broken — some target files were CRLF; converting introduced new diffs.
  3. Tried git am with patch files. ❌ Broken — blob SHAs don't exist across repos.
  4. Tried git apply --3way. ❌ Broken — same SHA problem.
  5. Finally: extracted blobs from source git (always LF), detected target convention per file, wrote with matching convention. ✅ Works.

The trial-and-error was costly because every attempt produced a "huge diff" that looked plausible at first glance — you have to dig in to realize it's line-ending noise. Skip the chain by using this protocol from step 1.

Common mistakes to avoid

  • Trusting cp to preserve line endings. It does (binary copy). But the source working tree itself may have CRLF that the source's git blob doesn't have. The blob is the truth.
  • Running dos2unix blindly across all files. Some prod files genuinely have CRLF (legacy Windows uploads). Don't normalize without checking the target's existing convention.
  • Using git am for cross-repo work. It needs SHAs. They don't exist. It will fail. Don't waste time.
  • Trusting the "huge diff" number. Always cross-check with --ignore-all-space. The real number is usually 5–10% of the displayed number.
  • Committing the result to main directly. If your target has auto-deploy, this ships untested code. Always use a branch that doesn't auto-deploy.

Output format

When you complete a cross-repo cherry-pick, produce a summary:

Source: <repo> <range>
Target: <repo> <branch>
Files: <count> (M: ..., A: ..., D: ...)
Raw diff:           +<X> / -<Y>
Content diff:       +<X'> / -<Y'>      (--ignore-all-space)
Line-ending noise:  <X-X'> + <Y-Y'>
Commit:             <sha>
Branch:             <branch> (push manually when ready)

This makes the actual scope of the change reviewable.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.