如果你已经有了一台 SSH 服务器,你就不需要 GitHub、GitLab 等第三方服务来托管你的 Git 仓库。
你的 SSH 服务器本身就已经是一个功能齐全的 Git 服务器。
1. 核心技术点:理解“裸仓库” (Bare Repository)#
这是实现一切的基础。要理解它,得先搞清楚一个普通的 Git 仓库包含什么。
当你运行 git init 时,你得到的是一个 “工作仓库” (Working Repository) 。它包含两部分:
- 工作区 (Working Directory): 你实际看到和编辑的代码文件,比如
main.js,README.md等。 - .git 目录: 一个隐藏目录,包含了 Git 的所有“内脏”——提交历史、分支、标签、对象数据等。
而 裸仓库(Bare Repository) 则完全不同。当你运行 git init --bare 时,你得到的仓库没有工作区。它只包含 .git 目录里的内容。你只能看到 HEAD, config, objects, refs 这些 Git 的内部文件。
为什么服务器需要裸仓库?#
这是关键。服务器扮演的是一个“中心枢纽”的角色,只负责接收和分发提交历史,从不干涉其它。
想象一下,如果你试图 push 代码到一个“工作仓库”(即有工作区的普通仓库),而这个仓库刚好 checkout 在 main 分支。你的 push 会尝试更新 main 分支,这会导致服务器上的工作区文件和 Git 历史记录产生冲突和混乱。Git 会阻止这种操作。
而裸仓库没有工作区,因此它没有任何被 checkout 的分支。它就像一个纯粹的“数据库”,可以安全地接收来自任何人的 git push,是作为服务器仓库的完美形态。
2. 创建自己的 Git 服务器#
你只需要:一台你能 SSH 登录的服务器。
第一步:在服务器上初始化裸仓库#
你可以使用任意 SSH 工具链接你的服务器,我这里使用的是 Termius(界面漂亮但收费),你可以使用 FinalShell、XShell、HexHub等等一系列免费的GUI工具,当然你也可以直接使用电脑自带的终端。
终端命令:
ssh your_user@your_serverbash输入你的密码(不会显示但已经输入),进入你的服务器你会看到:

然后,我们来创建一个存放仓库的目录。使用 .git 作为目录后缀,以明确表示这是一个裸仓库。
mkdir you_project_name.gitbash进入这个目录,并初始化它为裸仓库:
cd you_project_name.git
git init --barebash你会看到:Initialized empty Git repository in /home/your_user/you_project_name.git/

至此服务器部分已经全部完成。
第二步:从本地仓库克隆#
现在,回到你本地电脑上,打开终端,使用 git clone 命令(前提是已安装Git)。
上图可以看到,我开了两个本地终端,分别是 Local Terminal ONE 和 Local Terminal TWO,两个终端在不同目录下,来模仿两个不同的电脑。
首先来到 Local Terminal ONE。
关键在于 URL 的写法。Git 原生支持 SSH 协议。语法是: user@host:/path/to/repo.git
-
user@host:就是你 SSH 登录服务器的用户名和地址。 -
:后面是服务器上裸仓库的绝对路径或相对路径。
例如,如果 you_project_name.git 在你的服务器主目录 (~) 下,你可以使用相对路径:
git clone your_user@your-server:you_project_name.gitbash如果它在 /opt/git/ 目录下,你就需要使用绝对路径:
git clone your_user@your-server:/opt/git/you_project_name.gitbash如果你和我的操作相同的话,那么此刻 you_project_name.git 在你的服务器主目录(~)下,我们在本地终端选择使用相对路径。
执行命令后,Git 会通过 SSH 连接服务器,下载仓库数据。你会得到一个 Cloning into 'you_project_name'... 的提示,以及一个 warning: You appear to have cloned an empty repository. 的警告。这很正常,因为我们的仓库刚初始化,什么都还没有。

第三步:推送你的第一个提交#
进入本地克隆下来的仓库
cd you_project_namebash创建一个文件并提交
# 文件内容随意
echo "Hello World" > README.md
git add README.md
git commit -m "Initial commit"bash推送到服务器
git push origin mainbash执行 git push 时,Git 会再次通过 SSH 连接到服务器,进行身份验证(可能会提示你输入 SSH 密钥密码),然后将你的提交数据安全地推送到服务器的裸仓库中。

至此,你已经拥有了一个 100% 属于你的私有 Git 服务器。
第四步:验证是否推送成功#
你会想,我 push 到服务器了,但是文件在服务器哪里可以看到呢?怎么验证是否真的成功了呢?
答案是,你无法在服务器上像查看普通文件夹一样直接看到你推送的(比如 README.md)文件。
为什么会这样?
因为你创建的是一个裸仓库 (git init --bare)。
-
没有工作区: 裸仓库没有“工作区”。它不会把你的文件解压并平铺在文件夹里。
-
只有 Git 数据库: 它只包含 Git 的内部数据库(即
.git文件夹里的内容)。
你推送的文件内容被 Git 压缩后,存储为“对象 (objects)”,放在了你仓库目录的 objects 文件夹里(例如 /you_project_name.git/objects/)。这些文件是以 Git 的内部格式存储的,文件名是哈希值(比如 fa/49b0779c13d8...),无法直接阅读。
那么,该如何查看文件?
两种方式:
方式一:在另一台电脑上
这个服务器现在是你的“中央枢纽”。查看文件的正确方式是从另一台电脑上把它 clone 下来:
此时我来到我的 Local Terminal TWO,执行 git clone。

结果很显然,README.md 文件确实成功的 push 到了服务器上。
方式二:在服务器上查看
来到服务器的 you_project_name.git 仓库下。
查看提交历史: 用 git log 查看刚才的 push 记录:
git logbash你会看到 fatal: your current branch 'master' does not have any commits yet 错误,这是因为服务器上这个裸仓库的 HEAD 文件(它相当于一个“默认分支”的指针)指向了错误的地方。
因为 HEAD 默认指向 master,但 master 分支上什么都没有。
我们上面推送的是 main 分支。
我们应该告诉这个裸仓库,main 才是它的默认分支。
git symbolic-ref HEAD refs/heads/mainbash这个命令做了什么? 它会编辑你的 HEAD 文件,让它从指向 refs/heads/master (一个不存在的分支) 改为指向 refs/heads/main (刚刚推送的分支)。
此时我们再试 git log ,就能正常工作了。
查看最新提交的文件列表: 使用 ls-tree 命令可以列出某个提交(这里用 HEAD 代表最新)包含的文件:
git ls-tree HEADbash查看特定文件的内容: 查看某个文件的内容(比如 README.md),可以使用 git show:
git show HEAD:README.mdbash
3.Git、SSH 与 GitHub 的联系#
git push 到底是怎么通过 SSH 工作的?
当执行 git push 或 git pull 时,Git 客户端在底层实际上是作为 SSH 客户端运行的。它大致做了以下几件事:
- 建立连接: 通过 SSH 连接到
your_user@your-server。 - 执行命令: 在远程服务器上执行
git-receive-pack(用于 push)或git-upload-pack(用于 pull/fetch)命令。 - 数据传输: 本地和远程的这两个
git进程通过 SSH 建立的这个安全通道(stdin/stdout)来协商和传输 Git 对象数据(即提交、文件等)。
所以,Git 本身就是被设计为去中心化的,并且原生支持 SSH 协议。我们所做的,只是利用了它最基本、最核心的功能。
而这也正是 GitHub 的工作原理。
你仔细看过 GitHub 仓库的 SSH URL会发现 git@github.com:your-name/your-repo.git:
git:是 GitHub 用来处理 Git 操作的通用 SSH 用户名。github.com:是 GitHub 的 SSH 服务器地址。your-name/your-repo.git:是存储在 GitHub 服务器上,属于你的那个裸仓库的路径。
可以在本地终端试试这个命令: ssh git@github.com
你会收到 GitHub 服务器的回复:Hi your_github_username! You've successfully authenticated, but GitHub does not provide shell access.
这行字说明:
- 你成功地通过 SSH 密钥认证了。
github.com就是一台 SSH 服务器。- 它只是禁用了你的 shell 登录权限,只允许你执行
git-receive-pack和git-upload-pack等 Git 相关的命令。
GitHub、GitLab 在它们的核心,就是一套非常庞大和复杂的系统,用于管理海量的裸仓库和 SSH 公钥,并在上层套了一个 Web 界面来处理 Pull Request 和 Issues。
4.优势与局限性#
不用 GitHub 而改用 SSH 服务器意味着什么?
它的巨大优势在于:
- 避免了第三方服务的痛点: 如私有库收费、网络限制、对数据失去完全控制等。
- 绝对安全: 你的身份验证和授权完全由 SSH 负责。你可以通过管理服务器的
~/.ssh/authorized_keys文件来添加或删除协作者。没有 SSH 密钥的人,绝对无法访问你的仓库。 - 完全控制: 数据 100% 在你自己的服务器上,你可以随时备份、迁移、删除。
- 零配置,零依赖: 只要有 SSH 和 Git,一切就绪。
- 轻量高效: 资源占用极低,几乎为零。
- 完美的多人协作: 任何拥有该服务器 SSH 访问权限的团队成员,都可以通过相同的
git clone和git push命令来进行协作。
但同时,你会失去:
- Web 界面: 没有地方可以在浏览器里看代码。
- Pull Requests: 没有 PR 功能。协作模式回到了最原始的“发邮件讨论 Patch”。
- Issues 和 CI/CD: 这些高级功能统统没有。
总结
对于许多私有项目、代码备份、或小型团队的内部协作来说,这个方案远比维护一个庞大的 GitLab 实例,或为私有库向 GitHub 付费要简单、高效得多。
