An open API service indexing awesome lists of open source software.

https://github.com/abdulrahman1s/github-fs

Mount your entire GitHub as a Linux filesystem.
https://github.com/abdulrahman1s/github-fs

cli filesystem fs fuse fuse-filesystem fuse3 git github github-api linux rust

Last synced: 1 day ago
JSON representation

Mount your entire GitHub as a Linux filesystem.

Awesome Lists containing this project

README

          

# Github FS

**Your entire GitHub, as a folder.**

`ghfs` mounts every repository your token can see as a single
filesystem on Linux. Browse it with `ls`, open files in your editor,
read what you need on demand. Every tool that takes a path just works,
across every repo you can reach. When you actually want to change something,
`ghfs promote` flips one repo into a real on-disk git clone in-place,
so `vim`, `git commit`, and `git push` flow straight through the mount.

```text
~/ghfs/
abdulrahman1s/
github-fs/
Cargo.toml
README.md
src/main.rs
rust-lang/
rust/
...
torvalds/
linux/
...
```

## See for yourself

```sh
# Find every service in your org with a Dockerfile, in one command.
fd Dockerfile ~/ghfs/myorg

# Read a file from any repo, without cloning it.
cat ~/ghfs/torvalds/linux/MAINTAINERS

# Open a repo in your editor straight from the mount.
code ~/ghfs/rust-lang/rust

# Spot a bug? Promote in place, edit, commit, push.
ghfs promote ~/ghfs/myorg/api
$EDITOR ~/ghfs/myorg/api/src/server.rs
cd ~/ghfs/myorg/api && git commit -am 'fix it' && git push
```

The last block is the trick that sets `ghfs` apart.
`~/ghfs/myorg/api` is the **same path** before and after `ghfs
promote`: same shell `cwd`, same open editor buffers, same inode. It
just becomes writable, backed by a real git checkout.

## Who it's for

- **You work across a lot of repos.** An org with dozens of services,
a personal account with years of side projects, or just open source
you keep cloning into `~/code` and forgetting about.
- **You live in a terminal.** `fd`, `fzf`, `vim`/`nvim`, `bat`,
anything that consumes paths is now a multi-repo tool. (Avoid tools
that bulk-read file contents across the mount — every uncached file
is a GitHub API round-trip; reach for `ghfs promote` first if you
want to grep a whole repo.)
- **You want to read code without ceremony.** Skim a dependency's
source, look up how an upstream project handles something, share a
path with a colleague. No "let me clone it first."
- **You want one path for the whole workflow.** Read, realize you
need to fix it, edit and commit, without ever changing directories
or re-cloning.

## What you get

- **One mount, every repo.** No per-repo `git clone`, no remembering
which checkout lives where. Repos land under `///`.
- **First read fetches; the rest is local.** Files are cached on disk
after first access and re-validated with ETags, so re-reads don't
burn your GitHub rate limit. Wipe `~/.cache/ghfs/` any time to start
fresh; nothing is lost.
- **Read-only by default; writable where it matters.** Edits return
`EROFS` everywhere except inside a repo you've `ghfs promote`'d.
Inside that repo, ops pass through to a real working tree, so `vim`,
`git status`, `git commit`, and `git push` all work through the
mount.
- **One branch per repo dir** (the GitHub default by default). Swap
per-repo with `ghfs branch `. Promoted repos clone
every branch, configure `origin`, and track upstream branches — `cd`
in and `git checkout ` to switch what the mount serves.
- **Filter what shows up.** Yourself, everything visible, or an owner
allowlist. Hide forks; show only private or only public. See
[DOCS.md](DOCS.md#filtering-which-repos-appear).
- **Clone-on-demand.** Let `ghfs` auto-promote repos the first time
you touch them, no manual step. See [DOCS.md](DOCS.md#clone-on-demand).

## Install

Install the latest release to `~/.local/bin/ghfs`:

```sh
curl -fsSL https://raw.githubusercontent.com/abdulrahman1s/github-fs/master/install.sh | sh
```

The installer also drops bash/zsh/fish completions into the standard
XDG paths (opt out with `--no-completions`).

See [DOCS.md](DOCS.md#install) for installer flags (`--yes`,
`--no-modify-rc`, `--no-completions`, per-shell PATH-export overrides)
and other install methods.

Build from source:

```sh
git clone https://github.com/abdulrahman1s/github-fs.git
cd github-fs
cargo build --release
install -m 0755 target/release/ghfs ~/.local/bin/ghfs
```

You will also need `fusermount3` and the kernel FUSE module. Debian/Ubuntu:
`sudo apt install fuse3`. Fedora/Arch/Alpine: package `fuse3`.

NixOS users

Use the flake instead of a manual install:

```sh
nix run github:abdulrahman1s/github-fs#ghfs -- --help
nix profile install github:abdulrahman1s/github-fs#ghfs
```

For the NixOS module (with optional systemd user-service for auto-mount)
and the prebuilt-release option, see [DOCS.md](DOCS.md#nix-flakes).

## Quickstart

```sh
# point ghfs at a GitHub personal access token
export GHFS_TOKEN=ghp_xxx
# or
mkdir -p ~/.config/ghfs
echo 'token = "ghp_xxx"' > ~/.config/ghfs/config.toml

# smoke-test auth
ghfs whoami

# mount
mkdir -p ~/ghfs
ghfs mount ~/ghfs

# in another shell:
ls ~/ghfs
ls ~/ghfs/
ls ~/ghfs//
cat ~/ghfs///README.md

# switch which branch shows (takes effect on next mount)
ghfs branch ~/ghfs// dev

# Ctrl-C in the mount terminal to unmount, or from another shell:
ghfs unmount ~/ghfs

# list active ghfs mounts
ghfs status

# force-refresh the cached repo list and show added/removed repos
# — also signals every running mount via SIGUSR1 to pick up the change in place
ghfs refresh
```

Token scopes: `repo` for private repos, none for public ones.

## Subcommands

| Command | What it does |
| ------- | ------------ |
| `ghfs whoami` | Print the authenticated GitHub user. Smoke-tests auth. |
| `ghfs mount ` | Mount the GitHub filesystem at `` (foreground). |
| `ghfs unmount [--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. |
| `ghfs status` | List active ghfs mounts (scans `/proc/mounts`). |
| `ghfs refresh` | Re-fetch the cached repo list and show added/removed repos. |
| `ghfs info ` | Print repo metadata (URL, description, visibility, fork flag, default/effective branch) for the repo at `` inside an active mount. |
| `ghfs promote [--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`. `` is a path inside an active mount, e.g. `~/ghfs//`. |
| `ghfs branch ` | Set which branch the mount surfaces under `///`. `` is a path inside an active mount. Persistent; applies on next mount. Pass `--default` to clear. |
| `ghfs completions ` | Print a shell-completion script (`bash`, `zsh`, `fish`, `elvish`, `powershell`) to stdout. Redirect into the location your shell expects. |

See [DOCS.md](DOCS.md) for the full layout, mount semantics, configuration,
errno mapping, systemd auto-mount, caching internals, and development workflows.

## Mount semantics

* **Read-only by default; writable under materialized repos.** Writes
outside a materialized repo return `EROFS`. Inside one, ops pass
through to the on-disk working tree.
* **Two-level layout.** Repos live under `///`.
* **One branch per repo dir.** `~/ghfs///` is the repo's
effective branch (override from `ghfs branch`, falling back to the
GitHub default). Override changes take effect at the next mount.
* **Symlinks** (`mode 120000`) are surfaced as real symlinks.
* **Hard links.** `link(2)` works inside a single materialized repo+branch;
crossing worktrees returns `EXDEV`, linking into or out of a virtual
path returns `EROFS`. Each name gets its own FUSE inode number, so
`st_nlink` is accurate but `st_ino`-based dedup (`du`, `tar -l`,
`rsync -H`) doesn't recognize the link.
* **Submodules** (`mode 160000`) show as empty directories; gitlinks
aren't followed.
* **Truncated trees** (>~100k entries or >7 MB) log a warning and may
omit some entries; promote the repo to read it in full.

## Errors

GitHub errors are translated to errnos at the FUSE boundary:

| Cause | errno |
|---|---|
| 401 Unauthorized / 403 Forbidden (no rate-limit) | `EACCES` |
| 403 with `X-RateLimit-Remaining: 0` | `EAGAIN` |
| 404 Not Found | `ENOENT` |
| Network / 5xx / decode failure | `EIO` |

Run with `RUST_LOG=ghfs=debug` for verbose op tracing.

## Documentation

See [DOCS.md](DOCS.md) for installation variants, configuration, mount
semantics, caching internals, systemd auto-mount, privacy/security notes,
and development workflows.

## License

MIT.