{"id":45993633,"url":"https://github.com/mgalgs/git-ephemera","last_synced_at":"2026-02-28T21:00:24.138Z","repository":{"id":334464117,"uuid":"1141472101","full_name":"mgalgs/git-ephemera","owner":"mgalgs","description":"Attach ephemeral development files to git commits via git notes.","archived":false,"fork":false,"pushed_at":"2026-01-27T07:27:57.000Z","size":69,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-27T20:22:17.688Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","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/mgalgs.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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-24T22:35:34.000Z","updated_at":"2026-01-27T07:28:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mgalgs/git-ephemera","commit_stats":null,"previous_names":["mgalgs/git-notestash","mgalgs/git-ephemera"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mgalgs/git-ephemera","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgalgs%2Fgit-ephemera","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgalgs%2Fgit-ephemera/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgalgs%2Fgit-ephemera/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgalgs%2Fgit-ephemera/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mgalgs","download_url":"https://codeload.github.com/mgalgs/git-ephemera/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgalgs%2Fgit-ephemera/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29951672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T18:42:55.706Z","status":"ssl_error","status_checked_at":"2026-02-28T18:42:48.811Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":[],"created_at":"2026-02-28T21:00:09.003Z","updated_at":"2026-02-28T21:00:24.125Z","avatar_url":"https://github.com/mgalgs.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# git-ephemera\n\nAttach ephemeral development files to git commits via git notes.\n\n## Overview\n\n`git ephemera` captures ephemeral development files (PRDs, plans, specs, scratch notes, etc.) and stores them on the relevant git commit using `git notes`. This keeps development artifacts versioned and attributable to commits without polluting your repository tree or branch history.\n\nFor example, an AI-assisted development flow often results in accumulated\ncontext files, e.g.:\n\n```\n.ai/PRD.md\n.ai/PROMPT.md\n.ai/PLAN.md\n.ai/specs/*.md\n```\n\nIt's often helpful to save these files for future reference, but typically not desirable to have them in the main source worktree since they become out-of-date quickly, polluting code search results and cluttering agent context. They mainly make sense in the context of the commit that they were used to create.\n\n`git ephemera` solves this by attaching them to commits as git notes.\n\n## Installation\n\nPlace `git-ephemera` on your PATH:\n\n```bash\n# Symlink (recommended for development)\nln -s /path/to/git-ephemera ~/.local/bin/git-ephemera\n\n# Or copy\ncp git-ephemera ~/.local/bin/\n```\n\nGit automatically discovers executables named `git-*` as subcommands.\n\n## Quickstart\n\nOne-time (recommended) repo setup:\n\n```bash\n# 1) Make notes follow rebases/amends (otherwise notes stay on old SHAs)\ngit config notes.rewriteRef refs/notes/ephemera\n\n# 2) Make normal `git fetch` also fetch the ephemera notes ref\ngit ephemera setup-remote\ngit ephemera setup-remote torvalds   # for remote other than \"origin\"\n\n# 3) Install the post-rewrite hook to track rebase/amend history in notes\n#    (Rewrites only your notes, doesn't touch your commits.)\ngit ephemera install-hooks\n```\n\nTypical day-to-day flow:\n\n```bash\ngit commit -m \"...\"\n\n# Attach ephemeral files to the commit as a note (updates notes immediately)\ngit ephemera add .ai/\n\n# Attach ephemera to a commit other than HEAD\ngit ephemera add .ai/ --commit \u003crev\u003e\n\n# Share notes with collaborators/other machines\ngit ephemera push\n```\n\nOn a fresh clone / other machine:\n\n```bash\ngit ephemera setup-remote\ngit fetch     # now also fetches refs/notes/ephemera\ngit ephemera install-hooks  # Ensure rewrites (rebase, amend) also fix note references\n\n# Restore ephemera\ngit ephemera restore\n\n# Restore ephemera from a commit other than HEAD\ngit ephemera restore --commit \u003crev\u003e\n```\n\n## Usage\n\n```\nUsage: git ephemera \u003ccommand\u003e [options]\n\nCommands:\n  add \u003cpath\u003e...          Add ephemeral files to a commit\n  restore                Extract files from a commit's note\n  show                   Display note header/metadata\n  list                   List archived filenames without extracting\n  list-commits           List commits that have ephemera attached\n  push [\u003cremote\u003e]        Push ephemera notes ref (default: origin)\n  fetch [\u003cremote\u003e]       Fetch ephemera notes ref (default: origin)\n  setup-remote [\u003cremote\u003e]  Configure remote fetch refspec for notes (default: origin)\n  install-hooks          Install post-rewrite hook for commit tracking\n  record-rewrite         Record commit rewrite history (used by post-rewrite hook)\n\nOptions for 'add':\n  --commit \u003crev\u003e     Target commit (default: HEAD)\n  --message \u003ctext\u003e   Optional message stored in header\n  --strict           Fail if any path doesn't exist\n\nOptions for 'restore':\n  --commit \u003crev\u003e     Source commit (default: HEAD)\n  --dest \u003cdir\u003e       Extraction directory (default: `.`)\n  --force            Overwrite existing files\n  --dry-run          List files without extracting\n\nOptions for 'show':\n  --commit \u003crev\u003e     Source commit (default: HEAD)\n  --header           Print only the header\n  --payload          Print only the base64 payload\n\nOptions for 'list':\n  --commit \u003crev\u003e     Source commit (default: HEAD)\n\nOptions for 'list-commits':\n  --format \u003cfmt\u003e     Output format: oneline (default), short, sha\n\nOptions for 'push':\n  \u003cremote\u003e          Remote name (default: origin)\n\nOptions for 'fetch':\n  \u003cremote\u003e          Remote name (default: origin)\n\nOptions for 'setup-remote':\n  \u003cremote\u003e          Remote name (default: origin)\n\nOptions for 'install-hooks':\n  --force           Overwrite existing post-rewrite hook\n\nOptions for 'record-rewrite':\n  --ref \u003cnotes\u003e     Notes ref to update (default: ephemera)\n\nExamples:\n  git ephemera add .ai/\n  git ephemera add PRD.md PLAN.md\n  git ephemera restore --commit abc123\n  git ephemera list\n  git ephemera list-commits\n  git ephemera push\n  git ephemera fetch\n  git ephemera setup-remote\n  git ephemera install-hooks\n```\n\n### Add files to a commit\n\n```bash\n# Add files (updates notes for HEAD immediately)\ngit ephemera add .ai/\ngit ephemera add PRD.md PLAN.md\ngit ephemera add 'specs/*.md'\n\n# Add more files later\ngit ephemera add .ai/*.md\n\n# Update an existing file\ngit ephemera add .ai/PROMPT.md\n\n# Add ephemera to another commit\ngit ephemera add /tmp/old_file.txt --commit HEAD~5\n```\n\nUnlike `git add`, `git ephemera add` adds/updates the file(s) in the notes immediately (there's no staging index). If the commit already has ephemera, the new files are merged with the existing ones; existing files are overwritten with the new content.\n\n### Restore files from a commit\n\n```bash\ngit ephemera restore\ngit ephemera restore --commit abc123\ngit ephemera restore --dest /tmp/restored\n```\n\n### List archived files\n\n```bash\ngit ephemera list\ngit ephemera list --commit abc123\n```\n\n### Show note metadata\n\n```bash\ngit ephemera show\ngit ephemera show --commit abc123\n```\n\n### Full example workflow using the [Ralph Wiggum technique](https://github.com/ClaytonFarr/ralph-playbook)\n\n#### Step 1: Start work on a new feature by creating `.ai/{PRD,PLAN,PROMPT}.md` (or similar)\n\n(typically with the help of an LLM)\n\n#### Step 2: Once those are ready, iterate on the implementation\n\n```\nclaude \u003c .ai/PROMPT.md  # or in a loop for full ralph wiggum goodness\n```\n\n#### Step 3: Commit work and stash ephemeral files in the commit notes\n\n```\ngit add ...affected source files...  # _not_ the ephemeral files! i.e. nothing under .ai/\ngit commit -m \"...\"\n# Now stash the .ai/ files on the newly created commit for tracking\ngit ephemera add .ai/\n```\n\nIf you need to continue iterating, goto Step 2.\n\n(In a fully automated setup these commands are part of the prompt/plan).\n\n#### Step 4: Add more ephemeral files later\n\n```\n# You can add more files at any time\ngit ephemera add .ai/analysis.md\ngit ephemera add .ai/updates/\n```\n\n#### Push work and notes and clean up the ephemeral files\n\n```\ngit push  # push your actual code changes\ngit ephemera push  # push your ephemera\nrm -rf .ai/*  # clean up notes in worktree to make room for the next task\n```\n\n## Commands\n\n### `add \u003cpath\u003e... [--commit REV]`\n\nAdd ephemeral files to a commit.\n\n| Option             | Description                       |\n|--------------------|-----------------------------------|\n| `--commit \u003crev\u003e`   | Target commit (default: `HEAD`)   |\n| `--message \u003ctext\u003e` | Optional message stored in header |\n| `--strict`         | Fail if any path doesn't exist    |\n\nUnlike `git add`, `git ephemera add` adds/updates the file(s) in the notes immediately (there's no staging index).\n\n```bash\n# Add files (updates notes for HEAD immediately)\ngit ephemera add .ai/PROMPT.md\ngit ephemera add .ai/specs/\n\n# Add more files later\ngit ephemera add .ai/*.md\n\n# Update an existing file\ngit ephemera add .ai/PROMPT.md\n\n# Add ephemera to another commit\ngit ephemera add /tmp/old_file.txt --commit HEAD~5\n```\n\n### `restore`\n\nExtract files from a commit's note.\n\n| Option           | Description                         |\n|------------------|-------------------------------------|\n| `--commit \u003crev\u003e` | Source commit (default: `HEAD`)     |\n| `--dest \u003cdir\u003e`   | Extraction directory (default: `.`) |\n| `--force`        | Overwrite existing files            |\n| `--dry-run`      | List files without extracting       |\n\n### `list`\n\nList archived filenames without extracting.\n\n| Option           | Description                     |\n|------------------|---------------------------------|\n| `--commit \u003crev\u003e` | Source commit (default: `HEAD`) |\n\n### `list-commits`\n\nList all commits that have ephemera notes attached.\n\n| Option            | Description                                        |\n|-------------------|----------------------------------------------------|\n| `--format \u003cfmt\u003e`  | Output format: `oneline` (default), `short`, `sha` |\n\nFormats:\n- **oneline** — `\u003cshort-sha\u003e \u003csubject\u003e` (default)\n- **short** — like oneline, followed by the list of attached files\n- **sha** — full commit SHA only (useful for scripting)\n\n```bash\ngit ephemera list-commits\ngit ephemera list-commits --format short\ngit ephemera list-commits --format sha | xargs -I{} git ephemera restore --commit {} --dest /tmp/{}\n```\n\n### `show`\n\nDisplay note header/metadata.\n\n| Option           | Description                     |\n|------------------|---------------------------------|\n| `--commit \u003crev\u003e` | Source commit (default: `HEAD`) |\n| `--header`       | Print only the header           |\n| `--payload`      | Print only the base64 payload   |\n\n### `push [\u003cremote\u003e]`\n\nPush the `refs/notes/ephemera` ref to a remote (default: `origin`).\n\n### `fetch [\u003cremote\u003e]`\n\nFetch the `refs/notes/ephemera` ref from a remote (default: `origin`).\n\n### `setup-remote [\u003cremote\u003e]`\n\nConfigure a remote so that a regular `git fetch` will also fetch `refs/notes/ephemera` (default: `origin`).\n\n### `install-hooks`\n\nInstall the `post-rewrite` git hook to automatically track commit rewrites. This ensures that when you run `git rebase` or `git commit --amend`, the rewritten commits' notes get updated with their commit history.\n\n| Option      | Description                              |\n|-------------|------------------------------------------|\n| `--force`   | Overwrite existing `post-rewrite` hook  |\n\nThe hook calls `git ephemera record-rewrite` internally when rewrites occur. If a `post-rewrite` hook already exists, use `--force` to overwrite it, or manually add the `git ephemera record-rewrite` call to your existing hook.\n\n### `record-rewrite`\n\nRecord commit rewrite history in ephemera notes. This is typically called by the `post-rewrite` hook.\n\n| Option            | Description                              |\n|-------------------|------------------------------------------|\n| `--ref \u003cnotes\u003e`   | Notes ref to update (default: `ephemera`) |\n\nThis command reads `old_sha new_sha` pairs from stdin (the format provided by Git's `post-rewrite` hook) and updates the note on `new_sha` to include `old_sha` in its `commitHistory` field.\n\n## How It Works\n\nGit notes are attached directly to commits but stored in a separate ref namespace. `git ephemera` uses `refs/notes/ephemera`, keeping your stashed files separate from other git notes.\n\n## Syncing Notes\n\n### Push notes to remote\n\n```bash\ngit ephemera push        # defaults to origin\n# or: git ephemera push \u003cremote\u003e\n```\n\n### Fetch notes from remote\n\n```bash\ngit ephemera fetch       # defaults to origin\n# or: git ephemera fetch \u003cremote\u003e\n```\n\n### Auto-fetch notes\n\nConfigure the remote so that a regular `git fetch` will also fetch `refs/notes/ephemera`:\n\n```bash\ngit ephemera setup-remote        # defaults to origin\n# or: git ephemera setup-remote \u003cremote\u003e\n```\n\n## Preserving Notes on Rebase/Amend\n\nGit notes are attached to commit SHAs. When you rebase or amend, git creates new commits with new SHAs, leaving your notes on the old (orphaned) commits.\n\nConfigure git to copy notes to rewritten commits:\n\n```bash\ngit config notes.rewriteRef refs/notes/ephemera\n```\n\nThis enables note preservation for both `git rebase` and `git commit --amend`.\n\n## Viewing Notes in Git Log\n\n```bash\ngit log --show-notes=ephemera\n```\n\n## Data Format\n\nNotes are stored as text with a YAML-like header followed by a base64-encoded tar.gz payload:\n\n```\nephemeraVersion: 1\nencoding: tar+gzip+base64\ncreatedAt: 2026-01-24T12:34:56Z\ncommit: abc123...\npaths:\n  - PRD.md\n  - PLAN.md\n---\nH4sIAAAAA...\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgalgs%2Fgit-ephemera","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmgalgs%2Fgit-ephemera","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgalgs%2Fgit-ephemera/lists"}