{"id":47703483,"url":"https://github.com/dris1153/upstream-sync","last_synced_at":"2026-04-02T17:47:19.949Z","repository":{"id":347699995,"uuid":"1194954151","full_name":"dris1153/upstream-sync","owner":"dris1153","description":"Safely sync upstream changes from any Git repository into your customized fork — without breaking your custom code","archived":false,"fork":false,"pushed_at":"2026-03-29T04:27:37.000Z","size":45,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-29T06:27:07.859Z","etag":null,"topics":["ai-agent","cherry-pick","claude","claude-code","claude-code-skill","claude-code-skills","claude-skill","claude-skills","fork","git","github","integration","synchronization","upstream","upstream-sync"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dris1153.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-29T02:53:42.000Z","updated_at":"2026-03-29T04:27:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dris1153/upstream-sync","commit_stats":null,"previous_names":["dris1153/upstream-sync"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/dris1153/upstream-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dris1153%2Fupstream-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dris1153%2Fupstream-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dris1153%2Fupstream-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dris1153%2Fupstream-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dris1153","download_url":"https://codeload.github.com/dris1153/upstream-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dris1153%2Fupstream-sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","cherry-pick","claude","claude-code","claude-code-skill","claude-code-skills","claude-skill","claude-skills","fork","git","github","integration","synchronization","upstream","upstream-sync"],"created_at":"2026-04-02T17:47:17.972Z","updated_at":"2026-04-02T17:47:19.940Z","avatar_url":"https://github.com/dris1153.png","language":"JavaScript","readme":"# Upstream Sync\n\nA Claude Code skill that syncs your fork/clone with the original open source repository. Instead of merging commits directly, Claude **reads upstream diffs, evaluates each change, and edits your files directly** — giving you full control over what gets applied and how.\n\n## Problem\n\nWhen you fork/clone an open source project and build custom features on top, the original repo keeps receiving new commits. Syncing those changes is typically:\n\n- **Risky** — `git merge` can break your customized features\n- **Noisy** — merge commits pollute your history\n- **All-or-nothing** — hard to selectively apply only the changes you want\n- **Context-lost** — merge conflicts don't explain *why* code diverged\n\n## How This Skill Works\n\nInstead of running `git merge`/`rebase`/`cherry-pick`, this skill takes a different approach:\n\n1. **Extract** — fetch upstream and extract per-file diffs since last sync\n2. **Evaluate** — Claude reads each diff, compares with your local code, and decides: apply as-is, adapt, partial apply, or skip\n3. **Edit** — Claude applies changes by directly editing your files (Edit/Write tools), preserving your customizations\n4. **Track** — saves the last synced commit so the next sync only shows new changes\n5. **Report** — presents a clear summary of what was updated, added, skipped, and why\n\n## Installation\n\n### Option 1: Copy into project\n\n```bash\ncp -r upstream-sync/ /path/to/your-project/.claude/skills/upstream-sync/\n```\n\n### Option 2: Symlink (recommended for multi-project use)\n\n```bash\n# Linux/macOS\nln -s /path/to/upstream-sync /path/to/your-project/.claude/skills/upstream-sync\n\n# Windows (PowerShell, run as admin)\nNew-Item -ItemType Junction -Path \"C:\\path\\to\\your-project\\.claude\\skills\\upstream-sync\" -Target \"C:\\path\\to\\upstream-sync\"\n```\n\n## Setup\n\n### Configure upstream URL\n\n**Option A: Via `.env` (recommended)** — the skill auto-creates the remote:\n\n```env\n# Create in scripts/.env or project root .env\nUPSTREAM_URL=https://github.com/original-author/original-repo.git\n```\n\n**Option B: Manual** — add the remote yourself:\n\n```bash\ngit remote add upstream https://github.com/original-author/original-repo.git\n```\n\n### Optional `.env` settings\n\n```env\nUPSTREAM_REMOTE=upstream   # Remote name (default: upstream)\nUPSTREAM_BRANCH=main       # Branch to sync from (default: main)\n```\n\n## Usage with Claude Code\n\n### Trigger phrases\n\n```\n\"Sync upstream changes\"\n\"Check what's new in the original repo\"\n\"Review new commits from upstream\"\n\"Integrate upstream into my project\"\n```\n\n### What Claude does\n\n```\nYou: \"Sync upstream changes\"\n\nClaude:\n1. Runs upstream-status.js → \"12 new upstream commits, 6 files changed\"\n2. Runs upstream-diff.js  → extracts per-file diffs\n3. For each changed file:\n   - Reads the upstream diff to understand intent\n   - Reads your local version\n   - Decides: apply / adapt / skip\n   - Edits the file directly if applying\n4. Runs sync-state.js save → marks this sync point\n5. Runs tests/build to verify\n6. Presents a sync report:\n\n   ## Upstream Sync Report\n   **Source**: upstream/main (commits a1b2c3d...f4e5d6c)\n   **Upstream commits reviewed**: 12\n\n   ### Applied Changes\n   - **src/parser.js**: Applied upstream bug fix for memory leak\n   - **src/utils.js**: Updated helper to support new API format\n\n   ### New Files Added\n   - **src/dark-mode.js**: New dark mode feature from upstream\n\n   ### Skipped Changes\n   - **src/config.js**: Conflicts with our custom config — kept local version\n\n   ### Notes\n   - New dependency `@lib/theme` added in upstream — run `npm install`\n```\n\n### Real-world scenarios\n\n**Scenario: Only want bug fixes**\n\n```\nYou: \"Only sync bug fixes from upstream, skip new features\"\n\nClaude:\n1. Lists upstream commits, filters by type (fix, bug, patch)\n2. Extracts diffs only for bug fix commits using --commit \u003chash\u003e\n3. Applies each fix by editing the relevant files\n4. Skips all feature commits with reasons in the report\n```\n\n**Scenario: Large upstream update (50+ commits)**\n\n```\nYou: \"Sync upstream changes\"\n\nClaude:\n1. Runs --files-only first to get overview\n2. Uses conflict-preview.js to identify risky overlaps\n3. Processes files in dependency order (utils → features)\n4. For heavily customized files: reads both versions carefully,\n   combines upstream improvements with local changes\n5. For new upstream files: creates them locally, adapts imports\n```\n\n**Scenario: Second sync (incremental)**\n\n```\nYou: \"Sync upstream again\"\n\nClaude:\n1. Reads .upstream-sync.json → last synced at commit abc123\n2. Only shows changes AFTER abc123 (not everything since fork)\n3. \"5 new commits since last sync, 3 files changed\"\n4. Applies changes, saves new sync point\n```\n\n## Sync State Tracking\n\nThe skill tracks which upstream commit was last synced in `.upstream-sync.json`:\n\n```json\n{\n  \"lastSyncedCommit\": \"a1b2c3d4e5f6...\",\n  \"lastSyncDate\": \"2026-03-29T10:30:00.000Z\",\n  \"remote\": \"upstream\",\n  \"branch\": \"main\"\n}\n```\n\n**This file is committed to git**, so the sync state persists across machines and clones. If you clone the project on a new machine, the next sync continues from the correct point.\n\n- **First sync**: uses `git merge-base` as starting point (all changes since fork)\n- **Subsequent syncs**: uses saved state (only new changes since last sync)\n- **Reset**: run `sync-state.js reset` to start over from merge-base\n\n## Scripts Reference\n\n### upstream-status.js — List new upstream commits\n\n```bash\nnode scripts/upstream-status.js                          # Text output\nnode scripts/upstream-status.js --format json            # JSON output\nnode scripts/upstream-status.js --since 2024-06-01       # Filter by date\nnode scripts/upstream-status.js --limit 20               # Limit commits\nnode scripts/upstream-status.js --remote origin --branch develop\n```\n\n### upstream-diff.js — Extract per-file diffs\n\n```bash\nnode scripts/upstream-diff.js                            # All diffs since last sync\nnode scripts/upstream-diff.js --files-only               # Just file list, no diffs\nnode scripts/upstream-diff.js --file src/parser.js       # Specific file only\nnode scripts/upstream-diff.js --commit abc123            # Specific commit only\nnode scripts/upstream-diff.js --context 10 --format json # More context lines\n```\n\n### conflict-preview.js — Detect overlapping changes\n\n```bash\nnode scripts/conflict-preview.js                         # Preview overlaps\nnode scripts/conflict-preview.js --format json           # JSON output\n```\n\n### sync-state.js — Manage sync tracking\n\n```bash\nnode scripts/sync-state.js show                          # View current state\nnode scripts/sync-state.js save                          # Save current upstream HEAD\nnode scripts/sync-state.js save --commit abc123          # Save specific commit\nnode scripts/sync-state.js reset                         # Reset (next sync from merge-base)\n```\n\nAll scripts support `--remote \u003cname\u003e` and `--branch \u003cbranch\u003e` overrides.\n\n## Non-Fork Setup (Clone → Remove .git)\n\nIf you cloned an open source repo, **removed `.git`**, and initialized a new git repo, the history is disconnected from upstream. The skill relies on shared git history to detect new commits (`HEAD..upstream/main`), so without it, **all upstream commits** will appear as \"new\".\n\n### Fix: Reconnect history with `git replace --graft`\n\n```bash\n# 1. Find your first commit (the \"big bang\" commit)\ngit rev-list --max-parents=0 HEAD\n# → e.g. 4510b44\n\n# 2. Add upstream remote and fetch\ngit remote add upstream https://github.com/original-author/original-repo.git\ngit fetch upstream\n\n# 3. Find the upstream commit closest to when you cloned\n#    Option A: by date (if you remember when you cloned)\ngit log upstream/main --oneline --before=\"2025-01-15\" -1\n\n#    Option B: by comparing content (most accurate)\n#    Pick a few upstream commits and compare — least diff = best match\ngit diff \u003cupstream-commit\u003e \u003cyour-first-commit\u003e --stat | tail -1\n\n# 4. Graft: tell git your first commit's parent is the upstream commit\ngit replace --graft \u003cyour-first-commit\u003e \u003cupstream-commit\u003e\n\n# 5. Verify — should only show genuinely new upstream commits\ngit log --oneline HEAD..upstream/main\n```\n\n### Fixing a wrong graft\n\n```bash\n# Remove and redo\ngit replace -d \u003cyour-first-commit\u003e\ngit replace --graft \u003cyour-first-commit\u003e \u003ccorrect-upstream-commit\u003e\n\n# Or overwrite directly\ngit replace --graft -f \u003cyour-first-commit\u003e \u003ccorrect-upstream-commit\u003e\n```\n\nAfter grafting, the skill works identically to a normal fork — `HEAD..upstream/main` only returns commits you haven't integrated yet.\n\n## Configuration\n\nCreate a `.env` file in the `scripts/` directory or project root:\n\n```env\n# URL of the original repo (auto-creates remote if not configured)\nUPSTREAM_URL=https://github.com/original-author/original-repo.git\n\n# Remote name and branch (optional, defaults shown)\nUPSTREAM_REMOTE=upstream\nUPSTREAM_BRANCH=main\n```\n\nPriority order: `process.env` \u003e `scripts/.env` \u003e parent `.env` \u003e grandparent `.env`\n\n## Project Structure\n\n```\nupstream-sync/\n├── SKILL.md                       # Skill metadata + workflow for Claude Code\n├── README.md\n├── .upstream-sync.json            # Sync state tracking (committed to git)\n├── scripts/\n│   ├── git-utils.js               # Shared git utilities + sync state functions\n│   ├── upstream-status.js         # Check upstream, list new commits\n│   ├── upstream-diff.js           # Extract per-file diffs for review\n│   ├── conflict-preview.js        # Detect overlapping changes\n│   ├── sync-state.js              # Manage sync state (show/save/reset)\n│   ├── package.json\n│   ├── .env.example\n│   └── __tests__/                 # 52 tests (Node.js built-in test runner)\n│       ├── git-utils.test.js\n│       ├── upstream-status.test.js\n│       ├── upstream-diff.test.js\n│       ├── conflict-preview.test.js\n│       └── sync-state.test.js\n└── references/\n    ├── sync-workflow.md           # Full 7-phase sync workflow\n    ├── review-guide.md            # Change evaluation methodology\n    └── conflict-resolution.md     # Manual change application guide\n```\n\n## Running Tests\n\n```bash\ncd scripts\nnpm test\n```\n\nRequires Node.js \u003e= 18 (uses built-in test runner, zero npm dependencies).\n\n## Why Not Just `git merge`?\n\n| | `git merge` | This skill |\n|---|---|---|\n| **Control** | All-or-nothing | Per-file, per-hunk decisions |\n| **History** | Merge commits, upstream history mixed in | Clean local commits only |\n| **Conflicts** | Cryptic conflict markers | Claude understands intent and combines intelligently |\n| **Customizations** | Can be overwritten silently | Explicitly preserved, upstream adapted to fit |\n| **Tracking** | Merge-base advances automatically | Explicit sync state, committed to git |\n| **Cross-machine** | N/A (git handles it) | Sync state persists via `.upstream-sync.json` |\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdris1153%2Fupstream-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdris1153%2Fupstream-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdris1153%2Fupstream-sync/lists"}