Skip to content
Back to skills

Upstream Fork

ASecurity

Identify commits/changes in a locally-checked-out source repo that should

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 20, 2026
documentationgobashgitdocumentation

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add tstapler/dotfiles --skill upstream-fork --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Upstream Fork?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Upstream Fork
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tstapler-upstream-fork/badge)](https://www.skillsdirectory.com/skills/tstapler-upstream-fork)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: upstream-fork
description: Identify commits/changes in a locally-checked-out source repo that should
  be upstreamed to another locally-checked-out target repo, then create a PR. Replaces
  rsync-based syncing with a git-native, Claude-analyzed workflow. Use when contributing
  local changes back to an upstream project.
---

# Upstream Fork Changes (Local-to-Local)

Both repos are checked out locally. No rsync. Use `git format-patch` / `git am` to move changes between them, with Claude filtering out personal/private content before anything is committed.

## Step 1: Establish Source and Target

```bash
SOURCE_REPO=/path/to/source     # e.g. /Users/tylerstapler/Documents/personal-wiki
TARGET_REPO=/path/to/target     # e.g. /Users/tylerstapler/IdeaProjects/markdown-confluence
TARGET_BRANCH=main
```

Common setup for this project:
```
SOURCE: /Users/tylerstapler/Documents/personal-wiki
TARGET: /Users/tylerstapler/IdeaProjects/markdown-confluence
```

## Step 2: Determine Repo Relationship

First check whether the repos share git history (one is a fork of the other) or are completely independent:

```bash
cd $SOURCE_REPO
git remote add target-local $TARGET_REPO 2>/dev/null || true
git fetch target-local

# Check for shared history
git merge-base HEAD target-local/$TARGET_BRANCH 2>/dev/null && echo "SHARED HISTORY" || echo "UNRELATED REPOS"
```

- **Shared history** → use [Path A: commit-based sync](#path-a-commit-based-sync-shared-history)
- **Unrelated repos** → use [Path B: file-based sync](#path-b-file-based-sync-unrelated-repos) ← most common

---

## Path A: Commit-Based Sync (Shared History)

For true forks where one repo was branched from the other.

```bash
cd $SOURCE_REPO

# Commits in source not yet in target
git log target-local/$TARGET_BRANCH..HEAD --oneline

# Export as patch files (one per commit)
git format-patch target-local/$TARGET_BRANCH --output-directory /tmp/upstream-patches/
```

Classify patch files with Claude (see classification criteria below), then apply:

```bash
cd $TARGET_REPO
git checkout -b upstream-$(date +%Y%m%d) $TARGET_BRANCH

for patch in /tmp/upstream-patches/0001-fix-*.patch; do
    git am "$patch"
done
```

---

## Path B: File-Based Sync (Unrelated Repos)

For repos with diverged/independent histories (e.g., personal-wiki → markdown-confluence). **This is the common case.**

### Identify Which Paths to Sync

First, decide which top-level paths in the source are relevant to the target. Read the source repo's directory structure:

```bash
ls $SOURCE_REPO
```

Typical paths worth upstreaming to a tool repo:
- `.claude/skills/` — Claude skills developed in personal context
- `scripts/` — utility scripts
- `src/` — source code improvements
- `tests/` — new test cases

### Generate File-Specific Diffs

```bash
cd $SOURCE_REPO

# Diff specific paths between source and target (works across unrelated histories)
git diff target-local/$TARGET_BRANCH -- .claude/skills/ > /tmp/skills.patch
git diff target-local/$TARGET_BRANCH -- scripts/ > /tmp/scripts.patch
git diff target-local/$TARGET_BRANCH -- src/ > /tmp/src.patch
```

### Classify with Claude

Read each patch file and classify every changed file. Keep only files that are:

**Upstreamable:**
- Bug fixes and feature code in `src/`
- New or improved tests
- Claude skills/commands for the shared tool
- CI/workflow improvements
- Documentation relevant to the public project

**Personal/Private — exclude:**
- Journal entries, personal notes (`logseq/`, `pages/`, date-named `.md` files)
- Personal config (`.claude/settings.local.json`, `.idea/`, `.env`, `settings.local.*`)
- Pages about private/work-only projects
- Sensitive credentials or internal URLs
- Scratch files and work-in-progress notes

### Apply the Filtered Diff

Use a **git worktree** to work in an isolated directory — no branch switching, no risk of polluting your main checkout:

```bash
# Create a worktree with a new branch (clean isolated environment)
git -C $TARGET_REPO worktree add /tmp/upstream-work-$(date +%Y%m%d) -b upstream-$(date +%Y%m%d)
WORK=/tmp/upstream-work-$(date +%Y%m%d)

# Apply a specific patch
git -C $WORK apply /tmp/skills.patch

# Or copy specific files directly (simplest for small sets)
cp -r $SOURCE_REPO/.claude/skills/my-new-skill $WORK/.claude/skills/
cp $SOURCE_REPO/src/some/new/file.py $WORK/src/some/new/file.py

# Stage and commit
git -C $WORK add .
git -C $WORK diff --staged --stat    # review before committing
git -C $WORK commit -m "upstream: add X from personal development"

# Clean up worktree after PR is merged
git -C $TARGET_REPO worktree remove /tmp/upstream-work-$(date +%Y%m%d)
```

**Why worktrees**: Applying patches or copying files is a multi-step operation. A worktree keeps your main checkout clean if something goes wrong mid-way, and lets you work on the upstream PR while continuing other work in the main checkout.

## Step 6: Review and Clean Up

```bash
cd $TARGET_REPO

git log main..HEAD --oneline          # confirm what's included
git diff main..HEAD --stat            # confirm no personal files snuck in
git diff main..HEAD -- "*.md" | head  # spot-check any markdown
```

Remove any personal files that were accidentally included:
```bash
git rm --cached path/to/personal/file.md
echo "path/to/personal/file.md" >> .gitignore
git commit --amend --no-edit
```

## Step 7: Create the PR

```bash
cd $TARGET_REPO

git push -u origin $(git branch --show-current)

gh pr create \
  --title "upstream: [description]" \
  --body "$(cat <<'EOF'
## Summary
[Claude-generated summary of what was upstreamed]

## Changes
- `src/...`: [what changed]

## Excluded
Personal content (journal entries, local config, private notes) was filtered out.
EOF
)"
```

## Step 8: Tag the Sync Point in Source Repo

After the PR merges, tag the source repo so future runs know where the last sync was:

```bash
cd $SOURCE_REPO
git tag last-upstream-sync-$(date +%Y%m%d)
git push origin --tags
```

## Key Rules

- **No rsync** — `git format-patch` + `git am` transfers only the diff, not raw files. No bulk file copy = no DLP trigger.
- **Local remotes are safe** — `git remote add target-local /path/to/repo` reads the git objects directly; nothing goes over the network.
- **Classify before applying** — read ALL patches first, then apply only the clean ones. One bad patch can poison the branch.
- **Tag after merging** — without a sync tag, every run must manually figure out where the last sync ended.

## Quick Reference

```bash
# Full workflow in one block
SOURCE=/Users/tylerstapler/Documents/personal-wiki
TARGET=/Users/tylerstapler/IdeaProjects/markdown-confluence

cd $SOURCE
git remote add target-local $TARGET 2>/dev/null; git fetch target-local
git format-patch target-local/main --output-directory /tmp/upstream-patches/

# [Claude classifies patches — edit INCLUDE list below]
INCLUDE=( "0001-..." "0003-..." )

cd $TARGET
git checkout -b upstream-$(date +%Y%m%d) main
for p in "${INCLUDE[@]}"; do git am "/tmp/upstream-patches/$p"; done
git push -u origin HEAD
gh pr create --title "upstream: ..."
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…