The short answer
Use Git from your phone by SSHing into the machine that already has the repo, and running Git there. A VPS, a dev box at the office, your Mac at home on a tailnet — anywhere the clone, the deploy keys, the toolchain and the CI config already live. The phone contributes a keyboard and a screen; it does not become a second workstation you have to keep in sync.
This is the opposite of what most “Git on iPhone” articles suggest, and the reason is boring: the hard part of Git isn’t Git, it’s everything around the clone. Credentials, .env files, node_modules, a Docker daemon, a test runner, the LFS objects. Cloning to a handset means reproducing all of that on a device with a sandboxed filesystem, no package manager worth the name, and an OS that suspends your app in half a minute.
The two things you genuinely need to get right are covered below: how the remote box authenticates to GitHub (deploy key, machine key, or a carefully-scoped agent forward — not ForwardAgent yes), and which Git commands are worth typing on a narrow screen (a lot of -s, --oneline and --no-pager; almost no interactive rebase).
If you’d rather have the clone on the phone itself, that’s a legitimate choice for notes repos, static sites and Obsidian vaults — Working Copy on iOS is genuinely excellent at it. It’s the wrong shape for shipping application code.
Two approaches, and which one to pick
| Git on a remote box (SSH) | Git on the phone | |
|---|---|---|
| What runs where | Repo, keys, toolchain all remote; phone is a terminal | Full clone on the device |
| Credentials to manage | One key, on the server | A second set, on the phone, on GitHub |
| Build/test after a change | Yes — it’s a real machine | No |
| Works offline | No | Yes |
| Merge conflict resolution | Whatever editor is on the box | The app’s diff UI (often better than a phone vim) |
| Cost | Whatever the box costs | Working Copy pro unlock is a one-off $35.99; Termux is free |
| Good for | Application code, hotfixes, ops, deploys | Notes, blogs, static sites, config repos, Obsidian vaults |
| Falls apart when | You have no box and no network | The repo needs a toolchain, secrets, or LFS |
Concretely, the on-device options in 2026:
- Working Copy (iOS/iPadOS) — the serious one. Free download; pushing to a remote and managing more than five repositories require the pro unlock, a one-off in-app purchase of $35.99 with a 10-day trial, and it’s free for verified students via GitHub Student. Real SSH key generation, a proper diff/merge UI, and a Files provider so other editors can work in the checkout.
- a-Shell (iOS) — ships
lg2, a libgit2-based mini-Git. Clone, commit and push work; anything past that (rebase, submodules, hooks) doesn’t exist. Free. - Termux (Android) —
pkg install gitgives you actual upstream Git plus OpenSSH, which is why it’s still the answer for on-device Android Git. It’s also a Linux userland you now maintain on your phone; see Termux alternatives for when that’s the wrong trade. - iSH (iOS) — Alpine under x86 emulation.
apk add gitworks. It is slow enough that cloning any real repo is an exercise in patience.
Everything below assumes the SSH approach.
Setting it up: repo on a box, phone on SSH
Four pieces, once.
1. A box that’s reachable. If it’s a public VPS, you already have this. If it’s your desktop or a home server behind NAT, put it and the phone on a tailnet — that’s the whole reason TermAI ships Tailscale in-app rather than making you juggle two apps and one VPN slot. Setup is in Tailscale SSH from a phone.
2. A key on the phone, in the phone’s secure storage, on the server’s authorized_keys. Ed25519, generated on-device, never copied anywhere (create an SSH key on iPhone, ed25519 vs RSA). Restrict it in authorized_keys — a phone key should be a scalpel:
restrict,pty,from="100.64.0.0/10" ssh-ed25519 AAAAC3Nz… chen-iphone-2026-08
restrict (added in OpenSSH 7.2) turns off port forwarding, agent forwarding, X11 and pty allocation in one word; pty puts the terminal back. The from= limits the key to tailnet addresses. More of this reasoning in mobile SSH production safety.
3. A host entry so connecting is one tap. In the client’s saved-host UI, or in ~/.ssh/config if your client reads one (SSH config file):
Host dev
HostName dev.tail1234.ts.net
User chen
IdentityFile ~/.ssh/id_ed25519_phone
ServerAliveInterval 30
ServerAliveCountMax 3
ServerAliveInterval 30 matters more on mobile than anywhere else. Cellular NAT tables drop idle mappings aggressively, and without keepalives you’ll discover the session is dead only when you press Enter on a git push. Same fix as SSH broken pipe.
4. Land in tmux, not in a bare shell. This is the single highest-value habit for mobile Git:
tmux new -A -s dev
-A attaches to the session named dev if it exists and creates it otherwise, so the same command works every time. Now a git push of a large repo, a git clone, or a rebase you’re halfway through survives you walking into a lift. Without it, a dropped connection during git push leaves you guessing whether the objects made it. Details in keeping a process running after SSH.
Getting the server authenticated to GitHub
This is where people wire it up wrong. You are on your phone, SSHed into dev, and dev now needs to push to GitHub. Four options, in the order you should consider them:
1. A deploy key — best when the box serves one repo. A key registered on a single repository rather than on your account. Read-only by default; write access is a checkbox you tick deliberately. Generate on the server, paste the public half into Settings → Deploy keys on that repo:
ssh-keygen -t ed25519 -C "dev-box deploy key" -f ~/.ssh/id_ed25519_gh
cat ~/.ssh/id_ed25519_gh.pub
The blast radius is one repo. If the box is compromised, that’s the whole loss. A deploy key can be used on only one repository at a time across your account, which is the constraint people hit first.
2. A user key on the box — pragmatic for a personal dev box. Same ssh-keygen, but you add the public key under Settings → SSH and GPG keys on your account. Now the box can act as you on everything you can reach. Fine for a machine only you touch; not fine for anything shared.
3. A machine user — the right answer for shared or team boxes. A dedicated GitHub account with its own key, invited to exactly the repos it needs. More setup, correct isolation.
4. Agent forwarding — powerful, and the one to be careful with. ssh -A dev exports your phone’s agent socket to the server, so the server can authenticate to GitHub as you without ever holding a key. That’s genuinely attractive: no key material on the box at all. The catch is well documented — anyone who can bypass file permissions on that server (root, or a compromised process running as you) can use the forwarded socket to authenticate as you to anything your key opens for as long as the session lives. They can’t extract the key; they don’t need to. OpenSSH’s own documentation flags this, and the practical rule is to make it a per-session decision:
ssh -A dev # yes, for this one session
…and never this:
Host *
ForwardAgent yes # don't
There’s precedent beyond theory: CVE-2023-38408, disclosed July 2023, let a malicious server run arbitrary code on the client via the forwarded agent in OpenSSH 5.5 through 9.3p1. Forwarding is an attack surface, not just a convenience.
Whichever you pick, verify it before you need it. From the server:
ssh -T [email protected]
Success looks like this:
Hi chen! You've successfully authenticated, but GitHub does not provide shell access.
That is the success message, not an error. GitHub accepts Git protocol commands on that endpoint and nothing else, so it closes the session — and, unhelpfully, exits with status 1 even when everything worked. Read the sentence, not the exit code.
Then point the remote at SSH rather than HTTPS, since GitHub removed password authentication for Git over HTTPS in August 2021 and HTTPS now means juggling a personal access token:
git remote set-url origin [email protected]:you/repo.git
Verifying GitHub’s host key from a phone
The first Git command over SSH on a fresh box asks you to trust GitHub’s host key. On a laptop you’d compare against the docs page. On a phone, opening a browser to check a base64 string is exactly the moment people give up and type yes blind.
GitHub’s current fingerprints (docs):
| Key type | SHA256 fingerprint |
|---|---|
| Ed25519 | SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU |
| RSA | SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s |
The better move on a phone is not to compare by eye at all. Since 18 January 2022 GitHub publishes its host keys in the API, so you can populate known_hosts from the source over TLS:
curl -s https://api.github.com/meta \
| jq -r '.ssh_keys[] | "github.com " + .' >> ~/.ssh/known_hosts
One line, no transcription errors, no browser.
If you get REMOTE HOST IDENTIFICATION HAS CHANGED for github.com specifically, the likely cause is history rather than an attack: GitHub replaced its RSA host key on 24 March 2023 at around 05:00 UTC after discovering that the private key had been briefly exposed in a public repository (GitHub blog). Machines with a known_hosts older than that still carry the retired key. Remove the stale entry, then re-add from the API:
ssh-keygen -R github.com
General case: host key verification failed.
When port 22 is blocked
Hotel Wi-Fi, corporate guest networks and a fair number of captive portals block outbound 22. Your SSH connection to your own box is the first casualty — a different problem, usually solved by running sshd on 443 or 2222. But once you’re on the box and it can’t reach github.com:22, GitHub has an official second door.
The hostname changes, not just the port. It’s ssh.github.com, not github.com (docs):
ssh -T -p 443 [email protected]
Make it permanent in ~/.ssh/config so every existing remote keeps working unchanged:
Host github.com
Hostname ssh.github.com
Port 443
User git
The Ed25519 host key fingerprint for ssh.github.com on 443 is the same SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU. Note this is plain SSH on port 443, not SSH tunnelled inside TLS — a proxy doing deep packet inspection can still tell the difference and block it.
Git commands that survive a phone screen
A phone in portrait is roughly 40 columns. Git’s default output assumes 80+ and a pager you can scroll with a mouse. Three configuration changes and a handful of habits make the difference between usable and miserable.
Kill the pager for short output. less on a touchscreen is a bad time — you get a full-screen mode you then have to remember how to quit.
git config --global core.pager 'less -FX'
-F quits immediately if the output fits on one screen; -X stops it clearing the screen on exit, so the result stays in your scrollback where you can select and copy it. For a one-off, git --no-pager log ….
Use the short forms. They’re not just shorter to type, they’re narrower to read:
| Instead of | Type | Why |
|---|---|---|
git status | git status -sb | Two-column porcelain plus branch line; ~10 lines instead of 30 |
git log | git log --oneline -10 | Fits 40 columns; one commit per line |
git diff | git diff --stat | Which files changed, before you commit to reading hunks |
git branch -a | git branch --sort=-committerdate | head | Most recent branches first, not alphabetical |
git show HEAD | git show --stat HEAD | Summary first; drill down only if needed |
Set aliases on the server once. You’re typing on glass; every saved character counts.
git config --global alias.s 'status -sb'
git config --global alias.l 'log --oneline -12'
git config --global alias.d 'diff --stat'
git config --global alias.p 'pull --ff-only'
pull --ff-only as the default p is deliberate: it refuses rather than creating a surprise merge commit when the branch has diverged. Discovering divergence via an error message beats discovering it via a merge you didn’t intend and now have to unpick on a phone.
Expect Unicode breakage. Git’s status output, --graph lines and many shell prompts use box-drawing and Powerline glyphs. If yours render as tofu boxes, that’s a font problem on the client, not a Git problem — see Nerd Fonts over mobile SSH.
Use the AI assistant for the commands you can’t remember. This is the honest use case for AI in a mobile terminal: not “run my deploy”, but “what’s the command to undo the last commit but keep the changes staged” → git reset --soft HEAD~1, shown to you, run only after you tap confirm. Git’s CLI is famously full of commands whose names don’t match what they do, and recalling the difference between reset --soft, reset --hard and restore --staged on a phone at 2am is exactly the wrong test of memory. TermAI’s assistant proposes; you approve. That distinction is the whole argument in assistant vs agent, and it matters more for Git than for almost anything else, because git reset --hard has no undo button a beginner can find.
Committing without a real editor
git commit with no -m opens $EDITOR. On a phone that means vim or nano over SSH, on a keyboard with no Escape key and no Ctrl, inside a session iOS may suspend. It’s survivable but it isn’t where you want to be.
Always pass -m. For a multi-paragraph message, repeat the flag — each -m becomes a paragraph:
git commit -m "fix: reject empty webhook payloads" -m "Returned 500 instead of 400 when body was empty. Closes #412."
Add --no-edit to the commands that ambush you with an editor:
git pull --no-edit # if a merge commit is unavoidable
git merge --no-edit feature
git commit --amend --no-edit # amend content, keep the message
Set a sane editor anyway, for when you can’t avoid it. nano is far kinder than vim on a phone — Ctrl-O and Ctrl-X are printed on screen, and most mobile SSH clients put a Ctrl key on their accessory bar even when the system keyboard has none:
git config --global core.editor nano
Staging is the real friction, not the message. git add -p is an interactive loop of single-key answers (y, n, s, q) — actually one of the better mobile Git experiences, since every answer is one character. git add -e, which opens the patch itself in an editor, is not. Prefer git add -p or explicit paths; skip -e.
Two habits worth keeping: check what you’re about to commit with git diff --cached --stat before you push, and accept that your commit message quality will drop on a phone. If the change deserves a paragraph and you can’t face typing it, commit with a short message and amend it later from a laptop — git commit --amend before pushing costs nothing.
Longer-form work — actually writing the code, not just committing it — is a different question, and increasingly answered by running an AI coding tool on the box: Claude Code from your phone and Codex CLI from your phone both cover that pattern, and both are the same tmux-over-SSH shape as this.
Signing commits from a phone
If your repo enforces signed commits, a phone-driven workflow has to sign too, and GPG on a mobile-accessed box is unpleasant. SSH signing is the better path.
Git 2.34 (released 15 November 2021) added the ability to sign objects with an SSH key instead of PGP, and GitHub has verified those signatures since 23 August 2022 (changelog). On the box you SSH into:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519_gh.pub
git config --global commit.gpgsign true
Then upload that same public key to GitHub a second time, as a Signing Key rather than an Authentication Key — they’re separate entries in SSH and GPG keys even for an identical key, and there’s no limit on how many signing keys you add.
Two things that bite:
ssh-keygenfrom OpenSSH 8.7 has broken signing support. The Git 2.34 release notes say so explicitly. Use 8.8 or later — check withssh -Von the box.- Local verification needs an allowed-signers file. Without
gpg.ssh.allowedSignersFile,git log --show-signaturecan’t tell you who signed anything; GitHub’s web UI will still show Verified.
Can the signing key live on the phone instead? Yes — a Secure Enclave–backed ecdsa-sk key, gated by Face ID, forwarded to the box via agent forwarding, so no signing key exists on the server at all. It’s the most defensible version of this and also the one with the most moving parts, and it means turning agent forwarding on, with the caveats above. How Secure Enclave SSH keys actually work covers what that key can and can’t do.
What’s safe to do from a phone, and what isn’t
The constraint isn’t Git’s capability. It’s that a mobile session is interruptible — iOS suspends a backgrounded app in roughly 20–30 seconds, cellular hands off, the lift doors close — and that recovering from a half-finished Git operation is more typing than the operation itself.
| Operation | On a phone | Why |
|---|---|---|
git pull --ff-only, git log, git diff, git show | ✅ Fine | Read-only, idempotent, no half-states |
git commit -m, git push | ✅ Fine | Atomic; a dropped push either landed or didn’t, and re-running is safe |
git checkout -b, git switch | ✅ Fine | Local, reversible |
git stash / git stash pop | ⚠️ Careful | Easy to strand work in the stash and forget it exists |
git merge with conflicts | ⚠️ Careful | Conflict resolution is editor work on a 40-column screen |
git rebase -i | ❌ Avoid | Editor-driven, multi-step, leaves the repo mid-operation if the session dies |
git push --force | ❌ Avoid | Use --force-with-lease, or wait for a laptop |
git reset --hard | ❌ Avoid | The one Git command with no easy undo. Never on a small screen at speed |
git clean -fdx | ❌ Avoid | Deletes untracked files including .env. Nothing recovers it |
Two rules that make the difference:
Run everything in tmux. Then a suspended app is an inconvenience, not an abandoned rebase. Reattach with tmux a and the operation is exactly where you left it.
Prefer --force-with-lease to --force, always, and doubly so from a phone. --force-with-lease refuses the push if the remote moved since you last fetched — precisely the situation you can’t see coming on a device where you haven’t been watching the repo all day.
And the meta-rule from mobile SSH production safety: a phone is for the small, urgent, well-understood change. Hotfix a config value, cherry-pick a revert, tag a release, check why CI failed. It’s not where you refactor. If the thing you’re about to do would make you nervous on a laptop, it’s a bad idea on glass.
Error messages, decoded
| What you see | What it means | Fix |
|---|---|---|
Hi user! You've successfully authenticated, but GitHub does not provide shell access. | Success. Your key works | Nothing. Ignore the exit code of 1 |
[email protected]: Permission denied (publickey). | The server has no key GitHub accepts | Add a deploy or user key on the box, or ssh -A for that session |
REMOTE HOST IDENTIFICATION HAS CHANGED for github.com | Usually the March 2023 RSA rotation, not an attack | ssh-keygen -R github.com, re-add from api.github.com/meta |
ssh: connect to host github.com port 22: Connection timed out | Outbound 22 blocked on that network | ssh.github.com on port 443 |
ERROR: The deploy key is read-only | The deploy key was added without write access | Tick “Allow write access” on the repo’s deploy key |
error: failed to push some refs … fetch first | The remote moved on | git pull --ff-only, then push. Never reach for --force here |
fatal: Need to specify how to reconcile divergent branches | Git ≥ 2.27 refusing to guess your pull strategy | git config --global pull.ff only |
Push hangs, then Connection closed by remote host | Mobile session dropped mid-transfer | Run in tmux; add ServerAliveInterval 30 |
Agent admitted failure to sign using the key | The forwarded agent isn’t reachable from the server | Confirm ssh -A, and that the key’s authorized_keys line isn’t restricted |
error: gpg failed to sign the data with gpg.format ssh | ssh-keygen too old or broken (8.7) | ssh -V; upgrade to OpenSSH 8.8+ |
If the SSH connection to your own box is what’s failing rather than the Git operation, start at connection timed out or permission denied instead — Git’s errors are downstream of those and will mislead you.
FAQ
Can you use Git from a phone over SSH?
Yes, and the version that works well is running Git on a remote machine rather than on the phone. You SSH into a VPS, a dev box or your desktop and run git there — the repo, the SSH keys, the language toolchain and the CI config are already on that machine, so nothing has to be replicated onto a handset. The phone is a terminal, not a workstation. Running Git locally on the phone (Working Copy on iOS, Termux on Android) is a real option, but it turns the phone into a full clone that then needs its own credentials, its own merge conflicts and its own disk.
How do I authenticate to GitHub from a server I SSH into from my phone?
Generate a key on that server and register it with GitHub — don’t forward your agent by default. For a box that serves one repo, a deploy key is the tightest fit: scoped to a single repository and read-only unless you explicitly grant write access. For a general dev box you push from across many repos, register the server’s key as a user key, or use a dedicated machine-user account for anything shared. Agent forwarding works and keeps key material off the box entirely, but anyone with root on that server can use the forwarded socket to authenticate as you elsewhere while the session is open — so keep it to per-session ssh -A rather than ForwardAgent yes in your config.
Is [email protected]’s “does not provide shell access” message an error?
No — it’s the success message. On a correctly configured machine, ssh -T [email protected] prints Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access. That sentence means your key worked. GitHub accepts Git protocol commands on that endpoint and never an interactive shell, so it closes the session. Confusingly the command still exits with status 1, which trips up scripts that check the exit code instead of the output.
What are GitHub’s SSH host key fingerprints?
The Ed25519 fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU and the RSA one is SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s. GitHub rotated the RSA host key on 24 March 2023 after finding the private key had been briefly exposed in a public repository, which is why an old known_hosts file can produce a host key verification failure for github.com. Since January 2022 the current keys are also published at api.github.com/meta under ssh_keys, so on a phone you should fetch and append them with curl rather than transcribing a fingerprint from a docs page.
Can I sign commits from my phone?
Yes, with SSH signatures. Git 2.34 (November 2021) added signing with an SSH key via gpg.format ssh, and GitHub has verified those signatures since 23 August 2022. On the remote machine, set user.signingkey to a key and commit.gpgsign to true, then upload that public key to GitHub a second time as a Signing Key — authentication and signing keys are separate entries even for the same key. If you want the signing key itself to live on the phone behind Face ID, that means a Secure Enclave ecdsa-sk key plus agent forwarding, which trades one risk for another. Note that the SSH signing support in ssh-keygen from OpenSSH 8.7 is broken; use 8.8 or later.
Should I run git rebase -i or force-push from a phone?
Avoid it. Interactive rebase is editor-driven and multi-step, inside a session iOS may suspend within about 30 seconds of backgrounding, on a keyboard with no comfortable Escape or Ctrl key — and an interrupted rebase leaves the repo mid-operation, which is more typing to recover from than the rebase was worth. If you must, run it inside tmux so a dropped connection doesn’t abandon it, and use --force-with-lease rather than --force so a stale view of the remote can’t silently overwrite someone else’s push.
What about Git LFS, submodules and hooks?
They’re the strongest argument for the SSH approach. LFS needs its own credential handling and pulls large binaries you don’t want on a metered phone connection; submodules multiply the credential problem by the number of submodules; and pre-commit hooks routinely shell out to linters and test runners that don’t exist on iOS at all. On a remote box all three already work, because it’s the same environment your CI runs in.
Quick facts
- Run Git on the box, not the phone. SSH in, work inside
tmux new -A -s dev, so a dropped connection never abandons an operation. ssh -T [email protected]printing “does not provide shell access” is success — and it still exits 1.- Deploy key for one repo (read-only by default), user key for a personal box, machine user for anything shared.
- Agent forwarding: per-session
ssh -Aonly. Root on the remote can use your forwarded agent for the life of the session; CVE-2023-38408 (OpenSSH 5.5–9.3p1) made that concrete. - GitHub fingerprints: Ed25519
SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU, RSASHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s. Populateknown_hostsfromapi.github.com/metainstead of typing them. - RSA host key rotated 24 March 2023 after brief exposure — the usual cause of
HOST IDENTIFICATION HAS CHANGEDon github.com. - Port 22 blocked?
ssh.github.comon port 443 — the hostname changes too, not just the port. core.pager 'less -FX',git status -sb,git log --oneline,--no-edit,commit -m— the config that makes a 40-column screen workable.- SSH commit signing: Git 2.34+ with
gpg.format ssh; GitHub verifies since 23 Aug 2022;ssh-keygen8.8+ (8.7 is broken). - Never on a phone:
git rebase -i,git push --force,git reset --hard,git clean -fdx. Use--force-with-leaseif you must push over something. - On-device Git if you want it: Working Copy (iOS, $35.99 pro unlock for push and >5 repos, free for students), Termux (Android, free, real Git), a-Shell’s
lg2(iOS, minimal), iSH (slow).
Free on iOS and Android. 5 AI requests/day on the free tier, plus unlimited SSH/SFTP and built-in Tailscale.