没有个人 Git 仓库时,怎么维护自己的内部小项目

有些小项目很难被正式归到某个业务仓库里。

它可能是一组给自己用的脚本,一个本地知识库,一套 AI 工具的 Skill,也可能是一份还没成熟到要进入团队工程体系的内部资料。它确实有价值,也确实需要长期维护,但它又不像正式业务项目那样天然有一个远端仓库可以放。

有获准的私有 Git 仓库时,可以直接用它保留提交历史、分支、作者信息和变更差异,也方便多设备同步。

但现实里经常会遇到两个限制:

  • 公司内网有 GitLab,但普通员工不能创建个人仓库。
  • 由于合规、保密或数据边界要求,项目不能放到公网 GitHub / Gitee,即使是私有仓库也不合适。

如果公司允许把这些资料存入企业飞书,可以在本地继续按项目维护,再把备份文件放到飞书云盘或文档附件里。存储位置是否获准、谁能访问,需要先确认;不能因为服务在公司内部使用,就默认所有资料都适合上传。

这条路线可以先解决备份和离线分发:电脑坏了能恢复,换设备能迁移,别人需要时能拿到一份明确版本,多次更新时也能沿用同一个远端文件位置。

先分清想保存什么

这类项目的关键不在于怎么压缩一个文件夹,而在于先判断:远端保存的是普通文件快照,还是一个 Git 仓库备份。

如果只是想把当前目录保存下来,普通 zip 就够。如果项目已经用 Git 管理,而且只需要交付当前版本,可以用 git archive。如果希望连提交历史一起保留,后续还能 clone 成一个可继续开发的仓库,就应该用 git bundle。

方案一:zip,最直接的目录压缩

zip 是最普通的压缩方式。

它的特点是简单:不要求项目本身是 Git 仓库,也不要求有提交历史。只要目录存在,就可以压缩。

在 macOS 或 Linux 上,可以这样生成:

zip -r project-backup.zip project-folder

也可以直接在 Finder 里右键目录,选择压缩。

zip 的优点是解压体验最好。拿到文件的人不需要知道 Git,直接解压就能看到目录。

它的缺点也很明显:默认会把目录里的内容都放进去。如果项目里有 node_modules、构建产物、缓存、大文件或临时日志,普通 zip 不会自动理解这些内容该不该排除。可以用 zip -x 写排除规则,但规则一多,就会变得不如 Git 自己的忽略机制清楚。

因此 zip 更适合这些情况:

  • 目录不是 Git 仓库。
  • 内容规模不大。
  • 解压后能直接使用比保留提交历史更重要。
  • 目录里没有太多需要排除的缓存和依赖目录。

如果只是备份一个本地资料夹、一个 Markdown 知识库、一个没有 Git 管理的小工具目录,zip 是成本最低的方案。

方案二:git archive,打包当前提交里的项目内容

如果项目已经用 Git 管理,但远端只需要一份「当前版本源码」,可以用 git archive。

常用命令是:

git archive --format=zip --output project-snapshot.zip HEAD

它打包的是 HEAD 提交里的文件,不会带上未跟踪的依赖目录、缓存或日志。但 .gitignore 不会替它过滤已经提交的文件:如果敏感文件早已进入提交,它仍可能被打包。需要排除已跟踪的交付外文件时,可以在 .gitattributes 中配置 export-ignore。

这里要注意一个细节:git archive 打包的是某个提交,不是当前工作区。已经修改但还没提交的内容不会进入 HEAD 生成的压缩包。打包前最好先看一眼:

git status --short

如果有需要进入快照的改动,先提交,再执行 git archive。

不过 git archive 生成的是源码快照,不包含 .git/,也不包含提交历史。别人解压后能看到文件,但不能继续查看原来的 commit、branch 和 author 记录。

它适合这些情况:

  • 项目已经是 Git 仓库。
  • 远端只需要当前版本,不需要历史。
  • 希望只交付指定提交里的文件,不带未跟踪内容。
  • 接收方更希望直接解压,而不是执行 git clone。

把它理解成「Git 管理下的干净 zip」会比较准确。

方案三:git bundle,把 Git 仓库本身打成一个文件

如果想保留 Git 提交历史,git bundle 更合适。

常用命令是:

git bundle create project-backup.bundle --all

--all 会纳入当前仓库的 refs 及其可达历史,包括分支和 tag。这个文件不是普通 zip,不能直接双击解压查看源码,但 Git 可以从中读取对象和历史。

和 git archive 一样,bundle 不保存未提交的工作区改动。本地配置和 hooks 也不在备份范围内;它不是整个 .git 目录的复制。Git 的 bundle 文档明确区分了引用、可达提交和其他本地状态。打包前先检查工作区,把需要保留的项目改动提交,不能为了备份顺手提交凭证或缓存。

生成后先检查文件:

git bundle verify project-backup.bundle

这会检查 bundle 的格式和前置提交要求。要确认自己真能恢复,还应在一个新的临时目录里 clone,检查文件、分支和 tag;不要在原项目目录上直接覆盖。

还原时用 git clone:

git clone project-backup.bundle project-folder

这样还原出来的是一个正常 Git 工作目录,可以继续看提交历史、创建新提交、切分支。

如果有同事协助开发,只要他们的提交已经进入这个仓库历史,bundle 里也会保留对应的 author 和 commit 信息。它保存的是 Git 历史,不只是文件结果。

git bundle 适合这些情况:

  • 项目已经用 Git 管理。
  • 提交历史有价值。
  • 未来可能要在另一台设备上继续开发。
  • 希望保留分支、tag、author 和 commit 记录。
  • 接收方可以接受用 git clone 还原。

如果希望在另一台机器继续维护已有 Git 历史,我会选 git bundle --all,同时另行处理确实需要迁移的本地配置。

三种方式怎么选

有没有 Git、要不要保留历史,决定了使用哪种文件:

方式 是否需要 Git 仓库 是否保留提交历史 接收方怎么还原 适合场景
zip 不需要 不保留 直接解压 普通目录、资料夹、小规模文件
git archive 需要 不保留 直接解压 干净源码快照、交付当前版本
git bundle 需要 保留 git clone 仓库备份、多设备迁移、保留历史

交付前也要说明文件代表的是目录快照、提交快照还是 Git 历史备份,避免接收方拿到 bundle 后不知道怎样打开。

同一个项目,三种备份保存的范围不同

远端放到哪里

压缩包生成后,还需要一个稳定的远端位置。

如果内容适合让多人看到,飞书文档是一个不错的入口。可以在相关说明文档里追加附件,把压缩包和使用说明放在一起。这样读者看到背景、规则和最新附件,就能理解这个项目为什么存在、怎么恢复、什么时候更新。

如果内容主要给自己备份,飞书云盘更轻。它默认更像个人文件存储,适合放一些还没有团队化的小项目、个人工具、草稿知识库和 AI 工作流资产。

两种放法的区别可以简单理解为:

  • 飞书文档:更适合多人共同查看,适合和说明、背景、维护规则放在一起。
  • 飞书云盘:更适合个人备份,适合自己频繁替换和整理。

如果这个目录只用来保存最新备份,可以按项目名和日期命名。例如:

project-name-2026-05-29.bundle
project-name-2026-05-29.zip

项目更新后,先生成并校验新文件,再更新远端。替换前还要确认旧文件属于这个项目、当前账号有权限操作,而且新备份已在本地保留。

需要恢复旧状态时,就不能只留最新文件。可以按实际恢复需要保留近期版本或周备份,不要把「目录整洁」放在可恢复性前面。

可以把生成过程脚本化

重复备份时,可以把生成和校验写进脚本。

比如一个 Git 管理的小项目,可以写一个本地备份脚本:

#!/usr/bin/env bash
set -euo pipefail

project_name="project-name"
stamp="$(date +%Y-%m-%d-%H%M%S)"
output_dir="$HOME/code"

backup_file="$output_dir/${project_name}-${stamp}.bundle"
if [[ -e "$backup_file" ]]; then
  printf '备份文件已存在:%s\n' "$backup_file" >&2
  exit 1
fi
git bundle create "$backup_file" --all
git bundle verify "$backup_file"

如果只是普通目录,也可以用 zip:

#!/usr/bin/env bash
set -euo pipefail

project_name="project-name"
stamp="$(date +%Y-%m-%d-%H%M%S)"
output_dir="$HOME/code"

backup_file="$output_dir/${project_name}-${stamp}.zip"
if [[ -e "$backup_file" ]]; then
  printf '备份文件已存在:%s\n' "$backup_file" >&2
  exit 1
fi
zip -r "$backup_file" "$project_name"
unzip -t "$backup_file"

bundle 脚本要从 Git 仓库内运行;zip 示例要从包含 project-name 的父目录运行。output_dir 需要事先存在,zip 校验还需要 unzip。

脚本先生成新文件,再校验,不删除旧文件;遇到已存在的目标文件就退出。时间到秒用于区分同一天的多次备份,定期清理仍需按项目和保留规则处理。生成或校验失败时,上一份备份应该仍在。

这些是手动单次运行的示例,不支持并发。存在性检查与写文件不是一个原子动作;要做并行备份,需要隔离各次输出目录并设计发布、保留和失败清理流程。

如果还想更进一步,可以把「上传到飞书云盘并替换旧文件」也工具化。这个步骤依赖当前账号、登录态和飞书页面能力,自动化前要确认权限边界和误删恢复机制。对于确定只保留最新备份的目录,删除旧文件再上传新文件是可以接受的;对于多人共用或不可恢复资料,就不应该默认自动删除。

AI 工作流资产的备份选择

AI 工具里的 Skill、规则、个人知识库就是这类问题的典型例子。

它们不是业务项目,但会越来越重要。比如一套文档整理 Skill、一组前端排障规则、一批 Markdown 知识库,可能在很多项目里反复提供帮助。

这些内容只留在本机,电脑坏了就难恢复;放到公网仓库,又可能不符合内部资料的存储要求。没有获准的个人仓库时,可以按前面的方法保存本地版本,再通过获准的文件存储入口备份。

这时可以这样处理:

  • 本地目录继续用 Git 管理,认真写 commit。
  • 定期用 git bundle create xxx.bundle --all 备份引用和提交历史,并检查恢复结果。
  • 把 bundle 上传到飞书云盘或附到飞书文档里。
  • 校验新 bundle 后更新远端;是否只留最新版,按前面的恢复需求决定。

如果只是想给别人一份当前资料,不需要历史,可以额外生成 git archive 版本。别人直接解压就能看 Markdown,不需要理解 bundle。

我会为自己保留 bundle,需要给别人一份可直接阅读的 Markdown 时,再生成 archive。

这个方案的边界

「压缩包 + 飞书」提供的是备份和离线分发,不是 Git 服务。接收方能恢复指定版本或历史,但附件不会替团队管理分支权限、合并冲突,也没有在线 review、issue 和 CI。

低频维护时,先做好本地版本管理、获准的远端存储和恢复检查。多人开始频繁交换改动后,就该申请正式团队仓库,不能继续靠替换同一个附件协调开发。