GitHub import

Paste a public GitHub repository URL into the import panel and press Fetch, and FileConcat fetches the file tree on demand. No GitHub account needed, and no servers in between.

Supported URL shapes

The GitHub adapter at packages/core/src/sources/adapters/github.ts parses three shapes:

https://github.com/owner/repo
https://github.com/owner/repo/tree/branch
https://github.com/owner/repo/tree/branch/subdirectory

A trailing .git is tolerated and stripped. The optional branch defaults to the repository's default branch when omitted. When a subdirectory is included, only files at or under that path are imported.

A branch name containing a slash does not work. The branch segment stops at the first slash, so tree/release/2.0 is read as the branch release with the subdirectory 2.0. The URL itself is ambiguous and GitHub resolves it on its own servers; FileConcat does not. If a branch named release also exists you get that branch's 2.0 directory instead of an error. Import the default branch instead. Bitbucket import is the one source here that handles slashed branch names, because it resolves the ref to a commit before it lists anything.

What one import costs in API requests

Two requests to api.github.com for a typical import: one to read the repository's default branch, and one for the recursive file tree. Include the branch in the URL and the first is skipped.

File contents do not go through that API at all. They stream from raw.githubusercontent.com, one request per file, twelve at a time (DEFAULT_DOWNLOAD_CONCURRENCY in packages/core/src/sources/pooled-map.ts).

GitHub's documented primary rate limit for unauthenticated REST requests is 60 per hour, per IP address (GitHub, Rate limits for the REST API, read September 2026). Because the per-file downloads are served by a different host, the number of files in a repository is not what exhausts that budget. Repeated imports are.

When a request is denied, the message in the import panel comes from classifyResponseError (packages/core/src/sources/adapters/_errors.ts). Status 401, 403 and 429 each carry their own wording, so "rate limited" reads differently from "private repository", and a rate-limit message carries the reset time GitHub sent back.

If you need higher quotas or access to private repositories today, the supported workaround is to download the project locally and use the drag-and-drop path instead. Token-authenticated imports are tracked separately and are not shipped at the time of writing.

Large repositories fall back to an archive

GitHub caps a recursive tree at 100,000 entries and 7 MB, and sets truncated on the response when the listing it returned is incomplete (GitHub, REST API endpoints for Git trees, read September 2026).

Rather than bundle a partial repository, FileConcat switches to a single archive request, /zipball/{branch}, and unzips it in the browser. The import panel says it is downloading the full archive while that runs. It costs one more API request in place of one request per file, and a subdirectory URL still narrows the result the same way.

When a download fails

A file that fails to download does not fail the import. Each failure is recorded with its path and reason and the rest of the bundle is assembled without it, so compare the file count in the result against what you expected.

A 429 whose Retry-After hint fits inside 30 seconds is retried once, automatically. Anything longer surfaces as an error rather than a long silent wait.

Raw URLs

Raw file URLs like https://raw.githubusercontent.com/owner/repo/... are not handled by the GitHub adapter. They go to the URL fallback adapter instead, which downloads the single file you point it at and shows it in the result screen. Use this for "give me one file from GitHub" cases.

Gists

GitHub Gists are handled by a separate adapter that lives next to the others. URL shape:

https://gist.github.com/owner/gistId

The owner segment is required by the regex; bare gist URLs without the owner part are not matched. Gists are flat (no subdirectories); every file in the gist comes in together.

The same adapter also matches GitLab Snippet URLs; see GitLab import for those.

See also