尧图精选

Termux 上用 Git 提交 GitHub 的 SSH 配置与避坑

🕒 发布时间:2026/10/1 16:19:36 📁 来源:尧图网络
1. 这套组合真正解决的是离开电脑还能提交的问题在 Termux 里用 git 把项目提交到 GitHub刚听起来像是硬把两件不相干的东西凑一起一个是安卓手机上的终端环境一个是分布式版本控制系统。但用过一段时间就会发现这组合恰好命中了一个很具体的场景——想法突然冒出来、手边只有手机。改一个配置文件、修一处脚本 bug、给仓库补个 README掏出手机就能 add、commit、push不用等回到电脑前。它不是要替代桌面开发而是把记录一次改动这件事的成本压到了最低。我最初动这个念头是因为好几次在地铁上想到某个仓库里有个明显错误等晚上打开电脑时已经把细节忘干净了。后来把仓库在 Termux 里 clone 一份发现问题就地改、就地提交虽然一次只能处理很小的改动但积少成多反而比攒到晚上一起做效率高。这篇就把整套流程从头到尾讲清楚环境怎么准备、凭据怎么配才不用每次输密码、首次推送的完整命令链、日常提交的节奏以及我在手机端真正踩过的几个坑。适合读这篇的人大致分两类一类是已经装了 Termux 但 git 一提交就报错的另一类是还没开始、想先搞明白手机上做版本控制到底靠不靠谱的。下面所有命令都在 Termux 里直接可用涉及取舍的地方我会把理由讲透方便你按自己的设备情况调整。2. 环境准备从包索引更新到 git 真正可执行2.1 先更新包索引别急着装东西很多人拿到 Termux 第一件事就是pkg install git然后卡在各种 404 或者依赖冲突上。原因很简单Termux 的软件源地址会随版本变化旧版 App 里内置的源地址大概率已经失效。pkg update pkg upgrade -ypkg是 Termux 对 apt 的一层包装它会帮你处理源的写法差异和部分交互提示本质上还是 apt。这两条命令的区别要分清pkg update只是刷新索引pkg upgrade才会真正升级已装的包。建议分开执行中间如果看到一堆待升级的包列表确认一下有没有正在用的关键工具再执行 upgrade。升级过程中如果弹出配置界面通常是询问是否保留旧配置文件一路回车用默认即可。千万不要在中途按 CtrlC 强行中断dpkg 会停在半配置状态之后再装任何东西都报错。真遇到了不用卸载重装执行dpkg --configure -a让它把没做完的事做完就行。还有一个容易被忽略的前提如果你的设备系统版本很老比如安卓 5.x能从应用商店装到的 Termux 版本同样很老配套的源大多已经不再维护pkg update就会直接失败。这种情况下正确的做法是去官方发布渠道拿一份与系统版本匹配的安装包而不是反复重试。2.2 存储权限与仓库放哪儿这个关键决策Termux 默认只能访问自己的私有目录$HOME也就是/data/data/com.termux/files/home。要访问手机共享存储得先授权termux-setup-storage执行后会弹出系统权限对话框同意之后~/storage下会出现 shared、downloads、dcim 等几个软链接分别指向共享存储里的对应位置。接下来这个决策比想象中重要仓库建在$HOME还是共享存储里我的答案很明确建在$HOME。对比项$HOME私有目录共享存储/sdcard文件权限位正常支持被文件系统忽略符号链接支持受限或不可用小文件读写速度快明显偏慢其他 App 能否直接看到不能能卸载 Termux 后是否保留一起被清掉保留共享存储为了兼容各种设备往往是通过一层模拟层挂载的导致权限位信息丢失。表现出来就是你什么都没改git status却告诉你一堆文件被修改了因为你改的是权限位而不是内容。这是后面要讲的core.filemode配置存在的根本原因。所以正确的姿势是仓库放$HOME需要让别的 App 看到成果时用rsync或cp单向导出一份过去而不是直接在共享存储上操作仓库。2.3 git 与配套工具一次性装齐pkg install git openssh nano rsync -y四个包各自解决什么git是主角openssh提供ssh-keygen和 ssh 客户端这是后面走 SSH 方式推送的前提虽然 git 的依赖里通常已经带了相关组件但显式装一次能避免命令找不到的迷惑nano是那个手机上至少能用的编辑器底部一直显示按键提示比 vim 的模式切换友好得多rsync用于把产物同步到共享存储增量同步比直接cp -r靠谱。如果打算用剪贴板直接传公钥还可以装termux-api包但它需要额外安装对应的配套 App 才能生效否则命令会返回空结果容易让人以为是自己写错了。2.4 验证安装三条命令确认环境可用装完别急着开始先确认环境是活的git --version ssh -V git config --list --show-origin第三条特别值得养成习惯。--show-origin会告诉你每一项配置来自哪个文件比如/data/data/com.termux/files/home/.gitconfig是全局配置仓库/.git/config是仓库级配置。当你遇到我明明设了某某选项却不起作用的时候十有八九是被另一层配置覆盖了这条命令直接给你答案省掉大量猜测时间。3. 身份与凭据为什么我把 HTTPS 换成了 SSH3.1 user.name 和 user.email 到底写什么这两个是写进 commit 对象的署名字段不是登录凭据。写错不会导致推送失败只会让提交记录看起来很奇怪。git config --global user.name Your Name git config --global user.email youexample.comGitHub 依靠邮箱把 commit 关联到你的账号首页前提是这个邮箱已经加到账号里。如果不想暴露真实邮箱可以在账号的邮箱设置里找到 GitHub 提供的匿名邮箱地址格式大致是一串数字加用户名用它作为 email 值commit 依然能正确关联。想区分工作和个人身份时在具体仓库里用不带--global的命令再设一次仓库级配置优先级更高。已经提交了才发现写错最近一次可以用git commit --amend --reset-author修正更早的记录要改就得重写历史手机上不建议动历史署名不准确不至于是致命问题。3.2 HTTPS 加访问令牌的三个麻烦GitHub 早就停止接受账号密码推送走 HTTPS 就必须配访问令牌。它在电脑上还好在手机上麻烦有三一是令牌有有效期某天会毫无预兆地推送失败并返回权限错误而你大概率已经忘了这回事二是如果用凭据缓存令牌会以明文形式存在用户目录下的凭据文件里三是换设备或重装 Termux 后要重新配一遍每次都要去网页上重新生成。SSH 密钥就没有这些问题生成一次可以长期使用私钥始终留在设备本地认证过程不存在把凭据发出去的环节。所以只要不是有特殊限制的环境我建议直接在手机端用 SSH。3.3 ssh-keygen 生成密钥的每个参数ssh-keygen -t ed25519 -C termux-phone-t指定算法ed25519 生成的密钥串短、生成速度快在同类算法里安全性也足够好。极少数环境不支持时退回到-t rsa -b 4096。-C只是给公钥加一段注释会附在公钥末尾作用是你日后在一堆密钥里能一眼认出这把是手机上的写个可识别的名字就够了。交互过程有两步。第一步问保存路径直接回车用默认的~/.ssh/id_ed25519别改路径改了后面所有命令都得跟着改。第二步问 passphrase也就是给私钥加一个口令。这里有个取舍手机丢失的概率客观上比电脑高加一个口令更稳妥代价是每次推送都要输入。我的做法是设一个短口令然后用 ssh-agent 缓存一次会话内就不用反复输eval $(ssh-agent -s) ssh-add ~/.ssh/id_ed25519另外顺手确认权限从别处拷过来的密钥文件权限往往是放宽的ssh 会因此拒绝使用chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519 chmod 644 ~/.ssh/id_ed25519.pub3.4 把公钥贴进 GitHub 的完整路径cat ~/.ssh/id_ed25519.pub输出的是一行文本以算法名开头中间一长串编码末尾是你的注释。整行一次性复制不能有换行、不能多空格、不能少字符。手机上复制可以长按屏幕选复制如果装了配套的剪贴板工具也可以直接把文件内容塞进系统剪贴板比手动选择精准得多。网页那侧的操作顺序是右上角头像进入设置左侧找到 SSH 相关页面点击新增标题随便写个能识别的名字密钥类型选认证用途那一项把刚复制的一整行粘进内容框保存。部分账号会要求再输一次密码确认正常走完即可。3.5 ssh -T 验证与报错的逐层定位ssh -T gitgithub.com第一次连接会问是否信任这个主机的指纹输入 yes 回车。成功时返回的内容大意是你好某某某认证成功但本服务不提供 shell 访问。最后那句不提供 shell 访问不是错误恰恰说明认证这一关已经过了别被它误导。失败时最有效的做法不是删密钥重来而是按层排查ssh -vT gitgithub.com在输出里找实际使用的身份文件路径确认是不是你想用的那把ssh-add -l看 agent 里到底有没有加载到密钥ssh-keygen -lf ~/.ssh/id_ed25519.pub拿到本地指纹和网页上显示的指纹逐位比对检查~/.ssh是 700、私钥是 600确认 config 里设置了只使用指定密钥。这套顺序的意义在于它能把公钥没贴对密钥认错权限不对网络根本没通这几种完全不同的问题区分开而不是盲目重试。顺带一个容易忽略的细节从终端复制的公钥很容易漏掉首尾字符或者在编辑框里被自动折成两行。可以用wc -c看一下长度ed25519 的公钥整行大概八十到一百个字符明显偏短就是复制时漏了。3.6 ~/.ssh/config 让长命令变短也避开一个隐蔽坑mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/.ssh/config EOF Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes ServerAliveInterval 20 ServerAliveCountMax 3 EOF chmod 600 ~/.ssh/config这里面IdentitiesOnly yes是隐藏的关键项。手机上如果你有好几把密钥不指定的话 ssh 会把每一把依次拿去试服务端在尝试次数过多之后会直接拒绝表现出来就是我明明贴对了公钥还是认证失败。这个坑我实实在在地花了一个多小时才想明白。ServerAliveInterval那两行是给移动网络准备的推送数据量稍大时中间链路可能因为长时间没有数据往来而断开定期发个保活包能避免半路掉线。如果你有两个账号要分别管理就在 config 里写两个不同的 Host 别名推送地址用别名互不干扰。4. 首次推送把一个本地目录变成 GitHub 仓库4.1 init 还是 clone先想清楚再动手两种起点对应两种操作本地已经有一堆文件要上传用git init从零建仓库GitHub 上已经有仓库要往手机里拉用git clone。最麻烦的是第三种情况——两边都有内容。如果本地 init 完直接添加远端再推会报远端包含你本地没有的提交或者拒绝合并不相关的历史。正确的处理顺序是先在手机上git clone把远端仓库拉下来再把本地的文件拷进这个目录最后统一提交推送。不要试图用允许不相关历史的选项去强行合并手机上解决冲突的体验非常糟糕软键盘下看冲突标记简直是煎熬。多花两分钟重新组织文件比事后处理一个混乱的仓库划算得多。4.2 .gitignore 在移动端要写得更狠一点一份起点配置__pycache__/ *.py[cod] .venv/ node_modules/ npm-debug.log* *.log .env *.tmp .DS_Store Thumbs.db移动端和桌面端最大的差别在于手机上跑的脚本经常会产生体积很大的临时产物——录音文件、截图、导出的文本、下载的安装包。一次手快的git add .就可能把几十兆的二进制塞进仓库之后想清理就得重写历史那是手机端最难干的一类活。所以我在手机上用的忽略规则通常比电脑上写得更激进凡是机器生成的东西一律先排除。还要记住.gitignore只对未被跟踪的文件生效。已经提交过的文件得先把它从索引里移出来忽略规则才会起作用git rm --cached 文件名这个命令只影响索引本地文件不会被删别被rm这个词吓到。4.3 三个必须提前改的全局配置git config --global core.filemode false git config --global core.quotepath false git config --global init.defaultBranch main git config --global core.autocrlf false git config --global core.editor nanocore.filemode false是最重要的一个。它让 git 不再把文件权限位的变化当成内容改动。前面说过共享存储会把权限位信息抹掉不关掉这个开关你的git status会永远显示一批被修改的文件而你逐字节比对都找不出差别那感觉非常折磨。core.quotepath false解决的是中文文件名显示成八进制转义的问题。默认情况下非 ASCII 字符会被转义你根本认不出哪个文件是哪个改成 false 之后中文文件名原样显示。init.defaultBranch main让每次git init直接得到 main 分支不用再手动重命名。core.autocrlf false是因为 Termux 是类 Unix 环境不需要换行符自动转换开着反而可能让文件内容在提交时被悄悄改写。core.editor nano是给不带参数的提交准备的默认编辑器有时候会指向一个你根本没装的程序commit 一执行就报错退出。4.4 remote 地址组织方式与写错后的补救git remote add origin gitgithub.com:用户名/仓库名.git git remote -vorigin只是个约定俗成的名字叫什么都能用但脚本和习惯都围绕它别给自己找麻烦。git remote -v会输出两行fetch 和 push 各一行要确认两行地址一致。地址写错不用删库重建一条命令改掉git remote set-url origin gitgithub.com:用户名/仓库名.git如果连名字都写错了就先git remote remove origin再重新添加。这里最常见的错误是把用户名和仓库名的位置写反或者用了网页上的 HTTPS 地址却想走 SSH 认证——地址以git开头、以.git结尾才是 SSH 形式。4.5 首次推送的完整命令链逐条拆解cd ~/projects/myrepo git init git add . git status git commit -m chore: 初始化仓库 git branch -M main git remote add origin gitgithub.com:用户名/仓库名.git git push -u origin main逐条说清楚每一步在干什么git init在当前目录创建一个.git子目录所有版本信息都存在里面git add .把当前目录下所有未被忽略的改动放进暂存区这一步之前最好先扫一眼有哪些文件会被加进去git status千万不要省它是出门前的最后一次检查暂存区里有什么一眼看清git commit把暂存区的内容固化成一个提交对象git branch -M main强制把当前分支重命名为 main如果前面已经配好了默认分支名这一步可以跳过git remote add记录远端地址最后git push -u origin main里的-u是建立跟踪关系设置之后以后在这个分支上直接敲git push就行不用再带参数。推送成功时会看到分支从本地指向远端、以及新分支之类的提示。如果返回的是一切都是最新的而你确定自己改过东西检查两个点是不是忘了 commit改动还在工作区或者提交到了别的分支用git branch看一下星号在哪。5. 日常提交的节奏把 commit 拆小才是真的省事5.1 status 与 diff 的分工以及小屏的取舍git status -s git diff --stat git diff git diff --staged git log --oneline --graph --decorate -10-s是短格式屏幕小的设备上比完整输出清爽得多两列字母就能说明暂存和未暂存的状态。git diff看的是工作区与暂存区的差异git diff --staged看的是暂存区与最后一次提交的差异这两个范围别搞混否则你会觉得我暂存了怎么 diff 不出来。手机上还有一个实际体验问题git diff的完整输出在窄屏上会疯狂折行很难读。我的习惯是先跑git diff --stat它只列出改动的文件和增删行数心里有数之后再决定要不要看具体某几个文件的完整 diff。5.2 提交信息写给三个月后的自己第一行控制在五十个字符以内说清改了什么需要解释为什么就空一行再写正文fix: 修正脚本在无网络时误报成功的问题 原来的判断只看退出码请求失败时退出码依然是 0 改成先检查返回内容里是否包含预期字段再决定成败。手机上打字慢反而更值得写清楚。三个月后你翻git log看到更新一下这种信息是完全无从下手的。真写错了只要还没推送用git commit --amend -m 新信息就能改最近一条。已经推送到远端之后再改就需要强制推送如果仓库有其他人协作会造成对方历史错乱慎用。5.3 add -p、amend、reset 的安全边界git add -p是逐块选择要不要暂存交互键在手机上按起来挺别扭但有一种情况下它很值在共享存储目录里操作时误加了一堆临时文件用它逐块排除比事后清理容易。git commit --amend只能改最近一次提交而且只适合还没推送出去的情形。git reset --soft HEAD~1撤销最近一次提交但把改动留在暂存区这是我最常用的纠错手段因为它不会丢东西。对应的危险操作是--hard版本它会连工作区的改动一起丢掉。手机上我基本不用这个参数因为一旦误操作那些没提交过的改动就找不回来了而在手机上重建这些内容比在电脑上痛苦得多。5.4 手机上的编辑器选择与多行提交信息不带-m执行 commit 会调用配置的编辑器。nano 底部一直有按键提示保存和退出都有明确显示配合 Termux 的额外按键行可以按出 Ctrl 组合键。不过说实话在手机上写长提交信息体验并不好更实用的做法是用多个-m参数git commit -m 标题一句话 -m 正文说明可以写长一点不用管换行git 会自动在两者之间插入空行效果和用编辑器写的多段信息一样还省掉了进出编辑器的步骤。这个技巧用在手机上特别顺手。6. 踩坑排查实录四个折腾最久的问题6.1 报不是一个 git 仓库的三种来源这个报错看着直白实际有三种完全不同的成因。第一种是当前工作目录确实不在仓库范围内或者你在子目录里而上层根本没有.git用pwd加git rev-parse --show-toplevel就能确认 git 认为的仓库根在哪。第二种是仓库位置记错了你以为在某个项目目录下实际在另一个路径。第三种最隐蔽.git目录被清理工具删掉了或者因为把仓库放在了共享存储里、被系统的存储管理清掉了数据。排查的时候ls -a看当前目录有没有.git是最直接的判断。如果确定建过仓库却找不到了回忆一下当时的路径是不是在~/storage下面的软链接里——软链接解析之后落在共享存储上而这个位置对系统清理来说就是普通缓存目录被清掉是没有提示的。6.2 认证失败这类问题的完整排查链这个问题的值得花时间的原因是它看起来只有一个症状背后却可能是一堆原因。我最后固化成一套固定顺序按顺序走基本十分钟内能定位用带详细输出的连接命令从日志里找出实际尝试使用的私钥文件路径确认是不是你预期的那把查看 agent 里当前加载了哪些密钥用密钥指纹命令打印本地公钥指纹和网页上显示的那一串逐位对照检查目录和文件的权限位是否符合要求回到 config 文件确认是否限制了只使用指定密钥。绝大多数情况下问题出在第 1 步和第 5 步手里有好几把密钥ssh 挨个尝试被服务端拒绝。剩下的情况就是公钥复制不全。这里有一个特别实用的自查方法直接量一下公钥文件的字符数ed25519 生成的公钥整行长度大约在八十到一百之间如果测出来只有几十说明复制时被截断了。这比反复删掉密钥重新生成快得多。现象大概率原因先做什么认证被拒公钥未添加或复制不全量公钥长度与网页指纹比对认证被拒但公钥已添加多把密钥依次尝试被拒在 config 中限制只使用指定密钥连接卡住不动移动网络链路中断加详细输出看握手到哪一步提示主机指纹校验失败本地记录与当前主机不匹配清掉旧记录后重新连接确认6.3 在共享存储上操作必然遇到的连环问题我认真试过在共享存储里直接跑 git结论是能用但每一步都在跟你作对。第一个问题是权限位被忽略git 把文件权限统一读成一组固定值于是多次修改权限位之后状态里全是模式已改变的记录哪怕你配了忽略权限位的开关某些操作路径下它还会冒出来。第二个问题是锁文件。git 执行时会创建一个索引锁文件防止并发写入而在共享存储上写入慢加上后台进程随时可能被系统回收一旦命令被中途打断锁文件就留在那儿了。此后所有命令都会报无法创建锁文件文件已存在。修复方式很简单确认没有正在运行的 git 进程之后删掉它rm -f .git/index.lock第三个问题是大小写与符号链接的处理差异。共享存储对文件名大小写的处理方式和管理方式都与普通文件系统不同只改大小写的重命名操作 git 可能完全察觉不到符号链接则基本不可用。想通这三点的根源就会理解为什么我反复强调仓库放私有目录、共享存储只做导出。这不是洁癖是省事。6.4 推送中断之后的处理顺序推送中断的表现形式有好几种卡住不动、提示远端意外挂断、提示传输层错误。很多人第一反应是怀疑仓库坏了其实推送中断不会损坏本地历史因为本地的提交对象在 commit 那一刻就已经写好了。处理顺序是这样先跑一次状态检查确认本地完整然后直接重试推送git 会跳过已经确认过的对象只传剩下的部分所以第二次往往能过如果反复失败把这次提交拆成更小的多次提交分次推送单次传输体积下降之后成功率明显提高这是我实测下来最有效的一招。如果是某个大文件引起的先用把文件移出索引的写法把它从本次提交里拿掉加进忽略规则再提交一次。至于已经在历史里的大文件需要在历史中清理这一步在手机上非常折腾我建议老老实实放到电脑上做。另外长时间推送之前可以先获取一次唤醒锁避免 Termux 被系统挂起导致进程中断推送结束后记得释放长期持有唤醒锁会明显增加耗电。7. 把重复劳动压缩掉别名、脚本与定时任务7.1 六个真正省事的别名git config --global alias.st status -s git config --global alias.lg log --oneline --graph --decorate -15 git config --global alias.last log -1 --stat git config --global alias.unstage restore --staged git config --global alias.br branch git config --global alias.undo reset --soft HEAD~1手机上的输入成本比电脑高得多把一个两段命令压成一个短词累积下来省的时间很可观。这里有一个机制上的注意点普通别名只是在原命令后面追加参数调用时不能再补参数。如果想在别名里做更复杂的事需要用叹号开头的形式把它交给 shell 执行。undo这个别名我用得最多对应前面说的软重置出错时敲三个字母就能退回上一步比记完整命令轻松太多。7.2 一个 gpush 脚本覆盖八成场景mkdir -p ~/bin cat ~/bin/gpush EOF #!/data/data/com.termux/files/usr/bin/bash set -e msg${1:-update: $(date %Y-%m-%d %H:%M)} git add -A if git diff --cached --quiet; then echo 没有需要提交的改动 exit 0 fi git commit -m $msg git push EOF chmod x ~/bin/gpush几个细节值得说明。set -e让脚本在任何一步失败时立刻停下避免在出错状态下继续往下跑产生更难收拾的局面。中间那个判断是在检查暂存区是否为空空的时候直接退出否则会得到一个内容为空的提交这种提交除了污染历史没有任何用处。第一个参数作为提交信息不传时用时间戳兜底。最关键的一点往往被忽略脚本第一行的路径必须写成 Termux 里 bash 的真实位置。安卓系统根目录下并没有常规 Linux 的路径结构写成常见的那种简洁写法脚本会直接执行失败。这是我写第一个脚本时踩的坑排查了半天才发现问题在这。最后把它加进命令搜索路径echo export PATH$HOME/bin:$PATH ~/.bashrc source ~/.bashrc之后在任何仓库目录下敲gpush 提交说明就能一次完成添加、提交、推送三件事。7.3 定时自动推送值不值得做Termux 里有任务调度工具可以把脚本注册成周期任务配合网络可用的条件触发。听起来很美好实际用起来有几个现实问题自动提交的信息只能写时间戳历史变得不可读半成品代码在你不注意的时候被推上去任务触发时如果网络刚好不可用脚本会静默失败而你根本不知道当天没同步成功。我的取舍是分仓库对待笔记、日记这类内容提交信息本身不重要开定时任务没问题代码仓库一律手动推送因为每一次提交都是一次我确认这次改动是有意义的表态。更轻量的替代方案是每次打开 Termux 时手动跑一遍那个脚本一条命令解决不依赖后台调度也不会出现静默失败。8. 用了一段时间之后我留下的几个习惯如果让我总结在手机上做版本控制最重要的经验排第一的不是某个命令而是把改动切小。手机屏幕小、输入慢、后台随时可能被回收任何一次长时间的操作都有中断风险。一次只改一个文件、只做一件事、只提交一次看起来效率低实际上是你唯一能保证不返工的方式。第二是别把凭据塞进命令里。见过不少人在推送地址里直接写上用户名和令牌图一时方便结果是令牌留在历史记录里还得去后台把它作废重发。SSH 密钥配置一次后面所有操作都是干净的。第三是在手机上永远不用会丢数据的命令。软重置可以用硬重置不要碰改历史的事留给电脑做.git目录里的任何文件除了那个残留的锁文件其他一律不要手动删。手机上重建一个仓库的心理成本比电脑高太多了。最后一个很小但很有用的习惯在$HOME下建一个固定的项目目录所有仓库都放在那里路径短、好记、和共享存储彻底隔离。我一开始随便放结果每次cd都要翻半天路径后来统一之后cd ~/projects加两个 Tab 键就能进到任何仓库这种小事累积起来决定了你到底愿不愿意随手把改动提交上去。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →