{"id":50339937,"url":"https://github.com/abdulrahman1s/github-fs","last_synced_at":"2026-06-01T19:00:32.809Z","repository":{"id":360544405,"uuid":"1249675936","full_name":"abdulrahman1s/github-fs","owner":"abdulrahman1s","description":"Mount your entire GitHub as a Linux filesystem.","archived":false,"fork":false,"pushed_at":"2026-05-31T01:49:42.000Z","size":441,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-31T18:25:56.734Z","etag":null,"topics":["cli","filesystem","fs","fuse","fuse-filesystem","fuse3","git","github","github-api","linux","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abdulrahman1s.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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},"funding":{"ko_fi":"abdulrahman1s"}},"created_at":"2026-05-26T00:16:41.000Z","updated_at":"2026-05-31T01:46:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/abdulrahman1s/github-fs","commit_stats":null,"previous_names":["abdulrahman1s/github-fs"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/abdulrahman1s/github-fs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abdulrahman1s%2Fgithub-fs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abdulrahman1s%2Fgithub-fs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abdulrahman1s%2Fgithub-fs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abdulrahman1s%2Fgithub-fs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abdulrahman1s","download_url":"https://codeload.github.com/abdulrahman1s/github-fs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abdulrahman1s%2Fgithub-fs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33789013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["cli","filesystem","fs","fuse","fuse-filesystem","fuse3","git","github","github-api","linux","rust"],"created_at":"2026-05-29T16:01:25.812Z","updated_at":"2026-06-01T19:00:32.791Z","avatar_url":"https://github.com/abdulrahman1s.png","language":"Rust","funding_links":["https://ko-fi.com/abdulrahman1s"],"categories":[],"sub_categories":[],"readme":"# Github FS\n\n**Your entire GitHub, as a folder.**\n\n`ghfs` mounts every repository your token can see as a single\nfilesystem on Linux. Browse it with `ls`, open files in your editor,\nread what you need on demand. Every tool that takes a path just works,\nacross every repo you can reach. When you actually want to change something,\n`ghfs promote` flips one repo into a real on-disk git clone in-place,\nso `vim`, `git commit`, and `git push` flow straight through the mount.\n\n```text\n~/ghfs/\n  abdulrahman1s/\n    github-fs/\n      Cargo.toml\n      README.md\n      src/main.rs\n  rust-lang/\n    rust/\n      ...\n  torvalds/\n    linux/\n      ...\n```\n\n## See for yourself\n\n```sh\n# Find every service in your org with a Dockerfile, in one command.\nfd Dockerfile ~/ghfs/myorg\n\n# Read a file from any repo, without cloning it.\ncat ~/ghfs/torvalds/linux/MAINTAINERS\n\n# Open a repo in your editor straight from the mount.\ncode ~/ghfs/rust-lang/rust\n\n# Spot a bug? Promote in place, edit, commit, push.\nghfs promote ~/ghfs/myorg/api\n$EDITOR ~/ghfs/myorg/api/src/server.rs\ncd ~/ghfs/myorg/api \u0026\u0026 git commit -am 'fix it' \u0026\u0026 git push\n```\n\nThe last block is the trick that sets `ghfs` apart.\n`~/ghfs/myorg/api` is the **same path** before and after `ghfs\npromote`: same shell `cwd`, same open editor buffers, same inode. It\njust becomes writable, backed by a real git checkout.\n\n## Who it's for\n\n- **You work across a lot of repos.** An org with dozens of services,\n  a personal account with years of side projects, or just open source\n  you keep cloning into `~/code` and forgetting about.\n- **You live in a terminal.** `fd`, `fzf`, `vim`/`nvim`, `bat`,\n  anything that consumes paths is now a multi-repo tool. (Avoid tools\n  that bulk-read file contents across the mount — every uncached file\n  is a GitHub API round-trip; reach for `ghfs promote` first if you\n  want to grep a whole repo.)\n- **You want to read code without ceremony.** Skim a dependency's\n  source, look up how an upstream project handles something, share a\n  path with a colleague. No \"let me clone it first.\"\n- **You want one path for the whole workflow.** Read, realize you\n  need to fix it, edit and commit, without ever changing directories\n  or re-cloning.\n\n## What you get\n\n- **One mount, every repo.** No per-repo `git clone`, no remembering\n  which checkout lives where. Repos land under `\u003cmount\u003e/\u003cowner\u003e/\u003crepo\u003e/`.\n- **First read fetches; the rest is local.** Files are cached on disk\n  after first access and re-validated with ETags, so re-reads don't\n  burn your GitHub rate limit. Wipe `~/.cache/ghfs/` any time to start\n  fresh; nothing is lost.\n- **Read-only by default; writable where it matters.** Edits return\n  `EROFS` everywhere except inside a repo you've `ghfs promote`'d.\n  Inside that repo, ops pass through to a real working tree, so `vim`,\n  `git status`, `git commit`, and `git push` all work through the\n  mount.\n- **One branch per repo dir** (the GitHub default by default). Swap\n  per-repo with `ghfs branch \u003cpath\u003e \u003cother\u003e`. Promoted repos clone\n  every branch, configure `origin`, and track upstream branches — `cd`\n  in and `git checkout \u003cother\u003e` to switch what the mount serves.\n- **Filter what shows up.** Yourself, everything visible, or an owner\n  allowlist. Hide forks; show only private or only public. See\n  [DOCS.md](DOCS.md#filtering-which-repos-appear).\n- **Clone-on-demand.** Let `ghfs` auto-promote repos the first time\n  you touch them, no manual step. See [DOCS.md](DOCS.md#clone-on-demand).\n\n## Install\n\nInstall the latest release to `~/.local/bin/ghfs`:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/abdulrahman1s/github-fs/master/install.sh | sh\n```\n\nThe installer also drops bash/zsh/fish completions into the standard\nXDG paths (opt out with `--no-completions`).\n\nSee [DOCS.md](DOCS.md#install) for installer flags (`--yes`,\n`--no-modify-rc`, `--no-completions`, per-shell PATH-export overrides)\nand other install methods.\n\nBuild from source:\n\n```sh\ngit clone https://github.com/abdulrahman1s/github-fs.git\ncd github-fs\ncargo build --release\ninstall -m 0755 target/release/ghfs ~/.local/bin/ghfs\n```\n\nYou will also need `fusermount3` and the kernel FUSE module. Debian/Ubuntu:\n`sudo apt install fuse3`. Fedora/Arch/Alpine: package `fuse3`.\n\n\u003cdetails\u003e\n\u003csummary\u003eNixOS users\u003c/summary\u003e\n\nUse the flake instead of a manual install:\n\n```sh\nnix run github:abdulrahman1s/github-fs#ghfs -- --help\nnix profile install github:abdulrahman1s/github-fs#ghfs\n```\n\nFor the NixOS module (with optional systemd user-service for auto-mount)\nand the prebuilt-release option, see [DOCS.md](DOCS.md#nix-flakes).\n\n\u003c/details\u003e\n\n## Quickstart\n\n```sh\n# point ghfs at a GitHub personal access token\nexport GHFS_TOKEN=ghp_xxx\n# or\nmkdir -p ~/.config/ghfs\necho 'token = \"ghp_xxx\"' \u003e ~/.config/ghfs/config.toml\n\n# smoke-test auth\nghfs whoami\n\n# mount\nmkdir -p ~/ghfs\nghfs mount ~/ghfs\n\n# in another shell:\nls ~/ghfs\nls ~/ghfs/\u003cowner\u003e\nls ~/ghfs/\u003cowner\u003e/\u003csome-repo\u003e\ncat ~/ghfs/\u003cowner\u003e/\u003csome-repo\u003e/README.md\n\n# switch which branch \u003csome-repo\u003e shows (takes effect on next mount)\nghfs branch ~/ghfs/\u003cowner\u003e/\u003csome-repo\u003e dev\n\n# Ctrl-C in the mount terminal to unmount, or from another shell:\nghfs unmount ~/ghfs\n\n# list active ghfs mounts\nghfs status\n\n# force-refresh the cached repo list and show added/removed repos\n# — also signals every running mount via SIGUSR1 to pick up the change in place\nghfs refresh\n```\n\nToken scopes: `repo` for private repos, none for public ones.\n\n## Subcommands\n\n| Command | What it does |\n| ------- | ------------ |\n| `ghfs whoami` | Print the authenticated GitHub user. Smoke-tests auth. |\n| `ghfs mount \u003cpath\u003e` | Mount the GitHub filesystem at `\u003cpath\u003e` (foreground). |\n| `ghfs unmount \u003cpath\u003e [--strict]` | Unmount via `fusermount3 -uz` (lazy by default — detaches a busy mount and frees it once the last reference drops). Pass `--strict` to refuse on busy and surface the holder PIDs instead. |\n| `ghfs status` | List active ghfs mounts (scans `/proc/mounts`). |\n| `ghfs refresh` | Re-fetch the cached repo list and show added/removed repos. |\n| `ghfs info \u003cpath\u003e` | Print repo metadata (URL, description, visibility, fork flag, default/effective branch) for the repo at `\u003cpath\u003e` inside an active mount. |\n| `ghfs promote \u003cpath\u003e [--branch B]` | Manually clone a repo into a local working copy (`origin` configured, every branch fetched, `--branch` initially checked out). Works regardless of `[clone] trigger`. `\u003cpath\u003e` is a path inside an active mount, e.g. `~/ghfs/\u003cowner\u003e/\u003crepo\u003e`. |\n| `ghfs branch \u003cpath\u003e \u003cB\u003e` | Set which branch the mount surfaces under `\u003cmount\u003e/\u003cowner\u003e/\u003crepo\u003e/`. `\u003cpath\u003e` is a path inside an active mount. Persistent; applies on next mount. Pass `--default` to clear. |\n| `ghfs completions \u003cshell\u003e` | Print a shell-completion script (`bash`, `zsh`, `fish`, `elvish`, `powershell`) to stdout. Redirect into the location your shell expects. |\n\nSee [DOCS.md](DOCS.md) for the full layout, mount semantics, configuration,\nerrno mapping, systemd auto-mount, caching internals, and development workflows.\n\n## Mount semantics\n\n* **Read-only by default; writable under materialized repos.** Writes\n  outside a materialized repo return `EROFS`. Inside one, ops pass\n  through to the on-disk working tree.\n* **Two-level layout.** Repos live under `\u003cmount\u003e/\u003cowner\u003e/\u003crepo\u003e/`.\n* **One branch per repo dir.** `~/ghfs/\u003cowner\u003e/\u003crepo\u003e/` is the repo's\n  effective branch (override from `ghfs branch`, falling back to the\n  GitHub default). Override changes take effect at the next mount.\n* **Symlinks** (`mode 120000`) are surfaced as real symlinks.\n* **Hard links.** `link(2)` works inside a single materialized repo+branch;\n  crossing worktrees returns `EXDEV`, linking into or out of a virtual\n  path returns `EROFS`. Each name gets its own FUSE inode number, so\n  `st_nlink` is accurate but `st_ino`-based dedup (`du`, `tar -l`,\n  `rsync -H`) doesn't recognize the link.\n* **Submodules** (`mode 160000`) show as empty directories; gitlinks\n  aren't followed.\n* **Truncated trees** (\u003e~100k entries or \u003e7 MB) log a warning and may\n  omit some entries; promote the repo to read it in full.\n\n## Errors\n\nGitHub errors are translated to errnos at the FUSE boundary:\n\n| Cause | errno |\n|---|---|\n| 401 Unauthorized / 403 Forbidden (no rate-limit) | `EACCES` |\n| 403 with `X-RateLimit-Remaining: 0` | `EAGAIN` |\n| 404 Not Found | `ENOENT` |\n| Network / 5xx / decode failure | `EIO` |\n\nRun with `RUST_LOG=ghfs=debug` for verbose op tracing.\n\n## Documentation\n\nSee [DOCS.md](DOCS.md) for installation variants, configuration, mount\nsemantics, caching internals, systemd auto-mount, privacy/security notes,\nand development workflows.\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabdulrahman1s%2Fgithub-fs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabdulrahman1s%2Fgithub-fs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabdulrahman1s%2Fgithub-fs/lists"}