从零配置 GitHub SSH 到一键推送脚本:完整实践记录

2026-09-20T12:52:26+08:00 | 10分钟阅读

Mizar
从零配置 GitHub SSH 到一键推送脚本:完整实践记录

从零配置 GitHub SSH 到一键推送脚本:完整实践

背景

日常开发中,需要频繁向 GitHub 推送多个项目的代码。之前一直用 HTTPS + Personal Access Token(PAT)的方式,每次推送都要输入 Token,且 Token 明文写在脚本里,既不安全也不方便。

后来改用 SSH 密钥认证,配合自定义的推送脚本,实现了:

  • 一次配置,永久免密
  • 多个项目各自独立推送
  • 双击即可完成 add → commit → push

本文记录整个配置过程,以及踩过的坑。


生成 SSH 密钥对

生成密钥

在任意目录打开命令行(CMD),执行:

ssh-keygen -t ed25519 -C "你的邮箱@example.com"

参数说明:

参数含义
-t ed25519使用 ed25519 算法(现代、安全、速度快,GitHub 推荐)
-C "邮箱"添加注释,仅用于辨识,不参与加密

执行后会依次提示:

Enter file in which to save the key (C:\Users\你\.ssh\id_ed25519):

直接回车,使用默认路径。

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

直接回车(不设密码),或输入密码(更安全但每次要输)。

生成结果

密钥默认保存在 C:\Users\<用户名>\.ssh\ 目录下:

文件说明是否外传
id_ed25519私钥❌ 绝对保密
id_ed25519.pub公钥✅ 添加到 GitHub

一句话记忆:带 .pub 的是公钥,可以公开;不带的是私钥,绝不能外传。


image-20260919182845171

把公钥添加到 GitHub

添加至GitHub

  1. 用记事本打开 C:\Users\<用户名>\.ssh\id_ed25519.pub

  2. 复制全部内容

  3. 登录 GitHub → 右上角头像 → Settings

  4. 左侧菜单 → SSH and GPG keysNew SSH key

  5. 粘贴内容,起个名字(如 MyPC),保存

    image-20260919181827257

    image-20260919181949108

    image-20260919182134662

    image-20260919182221114

验证连接

ssh -T git@github.com

第一次会提示是否信任主机,输入 yes。成功后显示:

Hi 用户名! You've successfully authenticated, but GitHub does not provide shell access.

看到这句话就说明 SSH 配置成功。


image-20260919182733755

image-20260919190118212

配置 SSH config 文件

如果只有一把id_ed25519密钥,可以不配置config文件。

需要 config的场景

默认情况下,SSH 会按固定顺序尝试 .ssh 下的默认命名密钥(id_rsaid_ed25519 等)。如果你有多把密钥,或者使用了非默认命名(如 id_ed25519_nopass),就需要用 config 文件告诉 SSH 用哪把。

创建 config

路径:C:\Users\<用户名>\.ssh\config无扩展名

内容:

config:

Host github.com
    HostName github.com
    User git
    IdentityFile C:/Users/<用户名>/.ssh/id_ed25519
    IdentitiesOnly yes

注意事项:

  • 文件名必须是 config,不能有 .txt 后缀
  • IdentityFile 路径用正斜杠 /,不要用反斜杠
  • 不要写行尾注释(如 IdentityFile xxx # 说明),SSH 会报 garbage at end of line
  • IdentitiesOnly yes 强制只用指定的密钥,避免尝试其他密钥导致失败

验证

ssh -vT git@github.com

在输出里找:

debug1: Trying private key: C:/Users/.../id_ed25519

能看到,就说明 config 生效了。


让 SSH 不再问密码(可选)

如果生成密钥时设了密码,每次推送都要输。两种解决方案:

方案 A:使用 ssh-agent(推荐)

以**管理员身份**运行 PowerShell:

Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
ssh-add C:\Users\<用户名>\.ssh\id_ed25519

输入一次密码后,后续就免输。

方案 B:生成无密码密钥

ssh-keygen -t ed25519 -C "邮箱" -f C:\Users\<用户名>\.ssh\id_ed25519_nopass

生成时两次回车跳过 passphrase。然后在 config 里把 IdentityFile 指向这个新密钥。

  • 安全性:方案 A 更安全(私钥有密码保护),方案 B 更省事(无密码)。
  • 个人电脑推荐 B,公共环境推荐 A。

修改项目的远程地址为 SSH

进入每个项目目录,执行:

git remote set-url origin git@github.com:用户名/仓库名.git

注意:

  • SSH 地址格式是 git@github.com:用户名/仓库名.git
  • 不是 https://github.com/用户名/仓库名.git
  • 只需设置一次,之后永久生效

验证:

git remote -v

应该显示:

origin  git@github.com:用户名/仓库名.git (fetch)
origin  git@github.com:用户名/仓库名.git (push)

编写日常推送脚本

基础版本(单项目)

新建 push.bat

@echo off
chcp 65001 >nul

cd /d E:\path\to\your\project

git add .
git commit -m "auto push %date% %time%"
git push

pause

说明:

命令作用
@echo off关闭命令回显,屏幕更干净
chcp 65001 >nul切换 UTF-8 编码,避免中文乱码(>nul 隐藏切换提示)
cd /d 路径进入项目目录(/d 支持跨盘符)
git add .暂存所有改动
git commit -m "..."提交,%date% %time% 自动填充时间
git push推送(无需写分支名,已建立过关联)
pause结束后暂停,防止窗口一闪而过

多项目菜单版

当项目多了之后,与其每个项目一个脚本,不如做成菜单式脚本

@echo off
chcp 65001 >nul
title 项目推送工具

:menu
cls
echo ============================================
echo            项目推送工具
echo ============================================
echo   1. ProjectA
echo   2. ProjectB
echo   3. ProjectC
echo --------------------------------------------
echo   0. 退出
echo ============================================
set /p choice=请输入项目编号并回车: 

if "%choice%"=="1" goto ProjectA
if "%choice%"=="2" goto ProjectB
if "%choice%"=="3" goto ProjectC
if "%choice%"=="0" exit
goto menu

:ProjectA
set "PROJECT=ProjectA"
set "TARGET=E:\path\to\ProjectA"
goto push

:ProjectB
set "PROJECT=ProjectB"
set "TARGET=E:\path\to\ProjectB"
goto push

:ProjectC
set "PROJECT=ProjectC"
set "TARGET=E:\path\to\ProjectC"
goto push

:push
cls
echo ============================================
echo   正在推送: %PROJECT%
echo ============================================
cd /d "%TARGET%"
git add .
git commit -m "auto push %date% %time%"
git push
echo.
echo ============================================
echo   推送完成: %PROJECT%
echo ============================================
pause
goto menu

设计要点:

  • 每个项目只定义 PROJECT(名字)和 TARGET(路径)
  • 推送逻辑统一抽到 :push 段,改一处等于改所有项目
  • 推完 goto menu 回到菜单,可连续推送多个项目

自动扫描版(进阶)

如果项目经常新增,硬编码列表每次都要改脚本,很麻烦。这时可以用自动扫描版:脚本运行时自动扫描指定根目录,找出所有 Git 仓库,动态生成菜单。

优点

  • 新增项目后自动出现,无需改脚本
  • 只维护两个配置项(扫描根目录路径)
  • 适合项目多、变动频繁的场景

完整脚本

@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
title 项目推送工具

:: ============================================
:: 配置:要扫描的根目录(可添加多个)
:: ============================================
set "ROOT1=E:\Informations\MSB\technical_information\git"
set "ROOT2=E:\Informations\DevelopmentProject\TraeCN\TraeCNProject"
:: ============================================

:menu
cls
echo ============================================
echo            项目推送工具
echo ============================================
echo 正在扫描 Git 项目...
echo.

set "INDEX=0"
set "LIST="

call :scan "%ROOT1%"
call :scan "%ROOT2%"

if "%INDEX%"=="0" (
    echo 未找到任何 Git 项目。
    echo 请检查根目录配置。
    pause
    exit /b
)

echo --------------------------------------------
echo   0. 退出
echo ============================================
set /p choice=请输入项目编号并回车: 

if "%choice%"=="0" exit /b

:: 根据编号取出对应路径
set "TARGET="
for /f "tokens=1,2 delims=|" %%a in ("!LIST!") do (
    if "%%a"=="%choice%" set "TARGET=%%b"
)

if "%TARGET%"=="" (
    echo 无效输入,请重新选择。
    timeout /t 2 >nul
    goto menu
)

goto push

:: ============================================
:: 扫描函数:递归查找含 .git 的目录
:: ============================================
:scan
if not exist "%~1" exit /b
for /d %%D in ("%~1\*") do (
    :: 第一层:检查是否本身是 Git 仓库
    if exist "%%D\.git\" (
        set /a INDEX+=1
        echo   !INDEX!. %%~nxD
        set "LIST=!LIST!!INDEX!|%%D|"
    )
    :: 第二层:再往下一层查找(适配 DigitalPortal\DigitalPortal 结构)
    for /d %%E in ("%%D\*") do (
        if exist "%%E\.git\" (
            set /a INDEX+=1
            echo   !INDEX!. %%~nxD / %%~nxE
            set "LIST=!LIST!!INDEX!|%%E|"
        )
    )
)
exit /b

:: ============================================
:: 推送逻辑(所有项目共用)
:: ============================================
:push
cls
echo ============================================
echo   正在推送: %TARGET%
echo ============================================
echo.

cd /d "%TARGET%"
if errorlevel 1 (
    echo [错误] 无法进入目录: %TARGET%
    echo 请检查路径是否正确。
    pause
    goto menu
)

git add .
git commit -m "auto push %date% %time%"
git push

echo.
echo ============================================
echo   推送完成
echo ============================================
pause
goto menu

工作原理

  1. 扫描阶段:遍历 ROOT1ROOT2 两个根目录
    • 第一层:检查每个子目录是否含 .git
    • 第二层:再深入一层,适配"外层目录 + 内层仓库"的嵌套结构
  2. 生成菜单:扫描时用 LIST 变量累积"编号|路径",动态打印
  3. 选择推送:按编号从 LIST 取出路径,执行统一推送逻辑
  4. 循环:推完 goto menu,回到菜单可继续选下一个项目

关键语法说明:

语法作用
setlocal enabledelayedexpansion启用延迟变量展开,让循环里的 !变量! 实时更新
!INDEX!延迟展开语法(普通 %INDEX% 在循环里不会实时更新)
for /d %%D in (...)遍历目录
if exist "%%D\.git\"判断该目录是否是 Git 仓库
%%~nxD取目录名(不含路径)
`for /f “tokens=1,2 delims=“`

使用注意:

  • 脚本必须用 UTF-8 编码保存(配合 chcp 65001),否则中文菜单乱码
  • 扫描深度为两层,如果项目嵌套更深需要再加一层循环
  • 如果两个根目录下有同名项目,菜单会显示两个一样的名字,需注意区分
  • 如某些目录不想被扫描(如备份),可在 if exist "%%D\.git\" 里再加排除判断

自动扫描版 vs 硬编码版

对比项硬编码版自动扫描版
项目列表手动写在脚本里自动扫描目录生成
新增项目需要改脚本无需改脚本,自动出现
路径写死否(由扫描得出)
可移植性换电脑要改只改 ROOT 路径即可
复杂度稍高,但一次配好

适用场景

  • 项目固定、很少新增 → 硬编码版(简单直观)
  • 项目经常新增、分散在多个根目录 → 自动扫描版(免维护)

批量全推版(进阶)

如果某天改动了多个项目,想一次性全部推上去,一个个选菜单太麻烦。这时可以用批量全推版:不需要菜单,运行后自动扫描并推送所有项目。

完整脚本

@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
title 全部项目批量推送

:: ============================================
:: 配置:要扫描的根目录(可添加多个)
:: ============================================
set "ROOT1=E:\Informations\MSB\technical_information\git"
set "ROOT2=E:\Informations\DevelopmentProject\TraeCN\TraeCNProject"
:: ============================================

echo ============================================
echo          全部项目批量推送
echo ============================================
echo.

call :push_all "%ROOT1%"
call :push_all "%ROOT2%"

echo.
echo ============================================
echo          全部推送完成
echo ============================================
pause
exit /b

:: ============================================
:: 扫描并推送指定根目录下所有 Git 项目
:: ============================================
:push_all
if not exist "%~1" exit /b
for /d %%D in ("%~1\*") do (
    :: 第一层
    if exist "%%D\.git\" (
        call :do_push "%%D"
    )
    :: 第二层
    for /d %%E in ("%%D\*") do (
        if exist "%%E\.git\" (
            call :do_push "%%E"
        )
    )
)
exit /b

:: ============================================
:: 单个项目推送
:: ============================================
:do_push
echo.
echo ===== 推送 %~nx1 =====
cd /d "%~1"
if errorlevel 1 (
    echo [错误] 无法进入目录: %~1
    exit /b
)
git add .
git commit -m "auto push %date% %time%"
git push
exit /b

批量全推 vs 自动扫描版(菜单)

对比项菜单版(7.3)批量版(7.4)
交互需要手动选择项目全自动,无需选择
推送范围只推选中的项目推所有扫描到的项目
适用场景只改了部分项目改了很多项目,想一次全推
输出一个项目一份结果多个项目连续输出

运行效果示例:

============================================
          全部项目批量推送
============================================

===== 推送 SubsTracker =====
nothing to commit, working tree clean
Everything up-to-date

===== 推送 MyraZ-blog =====
[main 3f2a1b0] auto push 2026/09/19 ...
 2 files changed, 5 insertions(+)
To github.com:...
   a1b2c3d..3f2a1b0  main -> main

===== 推送 DigitalPortal =====
nothing to commit, working tree clean
Everything up-to-date

============================================
          全部推送完成
============================================

优缺点

优点:

  • 一次运行,推完所有项目
  • 新增项目后自动纳入,无需改脚本
  • 适合"周末统一提交"或"换电脑前同步"等场景

缺点:

  • 没改动的项目也会跑一遍 git add / commit / push,屏幕输出较多
  • 如果某个项目 remote 配错,容易被淹没在大量输出里忽略
  • 运行时间较长(项目越多越慢)

适用场景

场景推荐
只改了一两个项目菜单版(6.3)
改了很多项目,想一次全推批量版(6.4)
定时任务/自动化脚本批量版(无交互,适合无人值守)

建议: 日常用菜单版,周期性(比如每周)用批量版统一提交一次,兼顾便利和可控。

脚本版本对比与选择建议

本文给出了四种脚本方案,从简单到复杂递进,可按需选择:

版本位置适用场景维护成本
基础版(单项目)6.1只有一两个项目低,每个项目一个脚本
菜单版(硬编码)6.2项目固定,很少新增中,新增项目要改脚本
菜单版(自动扫描)6.3项目较多、经常新增低,只维护 ROOT 路径
批量全推版6.4改了很多项目,一次全推低,只维护 ROOT 路径

我的实际使用方式:

  • 日常零散改动 → 用 6.3 菜单版,双击选项目,推完回菜单继续
  • 周期性统一提交(如每周一次)→ 用 6.4 批量版,一次推完所有项目
  • 新增项目 → 放到扫描根目录下,脚本自动识别,无需任何修改

一次配置,长期受益。 配好之后,日常推送只是"双击 → 选编号 → 完成”,不再需要手动敲命令、输密码、改 remote。

分工原则:

类型频率位置
SSH 密钥 + config一次~/.ssh/
remote 地址每个项目一次项目 .git/config
add / commit / push每次推送脚本

常见问题汇总

把配置过程中踩过的坑集中列出,方便快速排查:

报错原因解决
fatal: not a git repositorycd 没进到项目目录检查路径,cd /d 加引号
garbage at end of line; "#".ssh/config 写了行尾注释删掉 #,或注释单独一行
src refspec main does not match any本地是 master,脚本推 main本地改名 git branch -M main,或脚本改 master,或统一用 git push
Permission denied (publickey)SSH 认证失败ssh -vT git@github.com 排查
nothing to commit, working tree clean没有改动正常现象,不是错误
提交信息中文乱码.bat 编码与 CMD 不一致另存为 ANSI,或提交信息用英文
Everything up-to-date本地与远程一致正常现象,没有新内容可推
SSH 每次要输密码密钥设了 passphrase用 ssh-agent 缓存,或换无密码密钥

清理工作(可选)

配置成功后,可以清理旧的 HTTPS Token 和冗余密钥:

撤销 GitHub Token

最重要的一步,光删本地没用,必须去 GitHub 撤销:

  1. 登录 GitHub
  2. Settings → Developer settings
  3. Personal access tokens → Tokens (classic) 或 Fine-grained tokens
  4. 找到旧 Token → Delete / Revoke

检查各项目 remote

逐个项目确认 remote 是 SSH,没有残留HTTPS+Token:

cd /d 项目路径
git remote -v

显示 git@github.com:... 才正确。

清理 Windows 凭据管理器

Win + R,输入:

control /name Microsoft.CredentialManager

Windows 凭据普通凭据 里,找名字含 git:https://github.com 的条目,删除。

清理冗余 SSH 密钥

如果同时存在旧的有密码密钥和新无密码密钥,确认 config 里 IdentityFile 指向正在用的那个后,删掉另一个:

# 查看 config 指向哪个
type C:\Users\<用户名>\.ssh\config

IdentityFile 指向的绝对不能删,其余可清理。

关键:光删本地没用,必须去 GitHub 上撤销 Token,否则 Token 依然有效。

© 2024 - 2026 拾光舍

湘ICP备2025117227号-1 | 公安备案图标 湘公网安备43040002000293号
在线工具资源网站

在线工具官网

ProcessOn:👉ProcessOn在线作图

腾讯文档:👉腾讯文档

FlowUs 息流:👉FlowUs 息流

Mermaid Live Editor:👉在线流程图和图表编辑器 - 美人鱼实时编辑器

ME2在线工具(加密工具):👉ME2在线工具-在线开发学习生活工具箱

Textln:👉Textln文本智能云平台-通用文本解析

MinerU:👉MinerU数据提取工具

Crontab:👉Cron Expression Examples - Crontab.guru

正则表达式测试网站:👉regex101: build, test, and debug regex

技术资源网站

技术资源官网

Spring:👉Spring官网

MyBatis中文网 :👉MyBatis 中文网

Redis中文网:👉Redis中文网

Zookeeper:👉Apache ZooKeeper官网

Raft算法:👉Raft Consensus Algorithm官网

Hadoop:👉Apache Hadoop官网

Hive:👉Apache Hive官网

Phoenix:👉Apache Phoenix官网

Bootstrap中文网:👉Bootstrap中文网

Element:👉组件 | Element

Layui :👉Layui - 极简模块化前端 UI 组件库(官方文档)

FreeMarker :👉FreeMarker 中文官方参考手册

ztree树插件: 👉zTree – jQuery 树插件官网

x File Storage:👉x File Storage官网

社交链接

© 2024 - 2026 拾光舍

湘ICP备2025117227号-1 | 公安备案图标 湘公网安备43040002000293号

ESC

使用扫一扫,扫码加我