Skip to main content
摘要:本文聚焦「Git 极客生存指南:从命令行到私有云搭建」,梳理核心概念、关键方法与落地实践。
面向对象: 程序员 (Programmers)。 目标: 掌握 Git 的“黑魔法”,处理复杂的分支管理、冲突解决,并学会搭建团队的私有代码仓库。

1. 常用命令速查 (The Cheat Sheet)

1.1 基础操作

git init                    # 初始化仓库
git clone <url>             # 克隆远程仓库
git status                  # 查看当前状态 (必用!)
git add .                   # 添加所有修改到暂存区
git commit -m "feat: xxx"   # 提交
git pull                    # 拉取更新 (相当于 fetch + merge)
git push                    # 推送修改

1.2 分支操作 (Branching)

git branch                  # 列出本地分支
git branch -a               # 列出所有分支 (含远程)
git checkout -b feature/A   # 创建并切换到 feature/A 分支
git checkout develop        # 切换回 develop
git merge feature/A         # 把 feature/A 合并进当前分支
git branch -d feature/A     # 删除分支

1.3 后悔药 (Undo)

git checkout -- file.cs     # 丢弃工作区的修改 (还没 add)
git reset HEAD file.cs      # 把暂存区的修改撤回工作区 (add 了但没 commit)
git reset --soft HEAD^      # 撤销最近一次 commit (代码保留在暂存区)
git reset --hard HEAD^      # 彻底回退到上个版本 (代码全部丢弃,慎用!)
git commit --amend          # 修改最近一次 commit 的注释

1.4 暂存现场 (Stash)

当你正在修 Bug,突然老板让你切分支去改另一个紧急 Bug:
git stash                   # 把当前未提交的修改“藏”起来
git checkout hotfix/001     # 切分支去修 Bug...
## ... 修完回来 ...
git checkout develop
git stash pop               # 把“藏”起来的代码还原回来

2. 进阶技巧 (Advanced Skills)

2.1 Rebase (变基) vs Merge

  • Merge: 保留真实的历史记录,会有 “Merge branch ‘xxx’” 的提交。适合公共分支合并。
  • Rebase: 把你的提交“接”在目标分支的最新提交后面。历史记录是一条直线,非常干净。
    • git pull --rebase: 拉取代码时自动变基 (推荐配置)。
    • git rebase develop: 在 feature 分支上,把 develop 的最新代码垫在下面。

2.2 Cherry-pick (摘樱桃)

只想要某个分支里的某一次提交,而不是整个分支?
git log                     # 找到那个 commit 的 hash (例如 a1b2c3d)
git cherry-pick a1b2c3d     # 把这个 commit 复制到当前分支

2.3 解决冲突 (Conflict Resolution)

A. 文本文件冲突

  1. 定位: 打开冲突文件,找到 <<<<<<<, =======, >>>>>>> 标记。
  2. 修改: 决定保留哪部分代码(或者都保留),删除标记符号。
  3. 提交: git add + git commit

B. 二进制文件冲突 (Binary Conflict) - 关键!

图片、模型、DLL 无法合并内容,必须二选一 命令行方案:
  • 保留我的 (Mine): 我改了图,我要覆盖服务器的。
    git checkout --ours path/to/image.png
    git add path/to/image.png
    
  • 保留他的 (Theirs): 别人的图是对的,我放弃我的修改。
    git checkout --theirs path/to/image.png
    git add path/to/image.png
    
  • 注意:在 git merge 时,--ours 是指当前分支,--theirs 是指要合并进来的分支。但在 git rebase 时,逻辑是反的!务必先备份。
GUI 方案 (Sourcetree / TortoiseGit):
  1. 在冲突文件上右键。
  2. 选择 Resolve using 'Mine' (使用我的版本) 或 Resolve using 'Theirs' (使用远程版本)。
  3. 工具会自动执行上述命令并标记为已解决。
终极方案: 锁定 (Locking) 为了避免二进制冲突,最好的办法是不要让冲突发生
  • 使用 LFS 的锁定功能: git lfs lock image.png
  • 这样当你在修改时,别人无法推送这个文件,直到你 unlock

3. 搭建私有 Git 服务器 (Self-Hosted Git)

对于不想把代码放在 GitHub/Gitee 的团队,推荐搭建 Gitea (轻量级) 或 GitLab (功能全)。

3.1 方案 A: Gitea (推荐,极轻量)

适合小团队,一个二进制文件搞定,内存占用极低。 搭建步骤 (Windows/Linux):
  1. 下载: 去 Gitea 官网 下载对应系统的可执行文件。
  2. 运行: 直接双击运行 (会启动 Web 服务器,默认端口 3000)。
  3. 配置: 浏览器访问 localhost:3000,首次运行会进入安装向导。
    • 数据库选 SQLite3 (最简单,无需安装 MySQL)。
    • 设置管理员账号。
  4. 局域网访问: 确保防火墙开放 3000 端口。队友可以通过 http://192.168.x.x:3000 访问。

3.2 方案 B: 局域网裸仓库 (Bare Repo)

最原始的方法,不需要任何 Web 界面。
  1. 服务器端 (找台电脑做主机):
    mkdir vampirefall.git
    cd vampirefall.git
    git init --bare  # 初始化裸仓库 (没有工作区,只有数据库)
    
  2. 共享: 将 vampirefall.git 文件夹设置为网络共享文件夹 (Windows SMB)。
  3. 客户端:
    git clone //SERVER_IP/Shared/vampirefall.git
    

4. Unity 项目的 .gitignore (必抄)

## Unity folders
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Mm]emoryCaptures/

## Visual Studio / JetBrains
.vs/
.idea/
*.sln
*.csproj
*.unityproj

## OS
.DS_Store
Thumbs.db

5. LFS 配置 (大文件存储)

对于大于 100MB 的文件 (PSD, FBX),必须用 LFS。
  1. 安装: git lfs install
  2. 配置:
    git lfs track "*.psd"
    git lfs track "*.fbx"
    git lfs track "*.wav"
    
  3. 提交: 这会生成一个 .gitattributes 文件,务必把它提交上去。

一句话忠告: 永远不要在主分支 (master/develop) 上直接写代码。 Commit 早,Commit 勤。

🐙 Git 版本管理与 Commit Log 规范

核心理念: Commit Log 是写给人看的,不是写给机器看的。 一个好的 Commit Log 应该能回答三个问题:
  1. 改了什么? (What)
  2. 为什么改? (Why)
  3. 怎么改的? (How - 可选,如果是复杂逻辑)

1. Commit Message 格式规范

采用业界标准的 Angular Commit Convention,结构如下:
<type>(<scope>): <subject>

<body>

<footer>

1.1 Type (必填)

用一个词描述改动的性质:
  • feat: 新功能 (Feature)。
  • fix: 修补 Bug。
  • docs: 仅修改了文档 (如 README)。
  • style: 格式修改 (不影响代码运行的变动,如空格、缩进)。
  • refactor: 重构 (即不是新增功能,也不是修改 bug 的代码变动)。
  • perf: 性能优化。
  • test: 增加测试或修改测试。
  • chore: 构建过程或辅助工具的变动 (如 .gitignore, package.json)。
  • art: 美术资源提交 (贴图、模型、预制体)。

1.2 Scope (选填)

用括号说明影响的范围 (模块/功能):
  • feat(Tower): 塔防模块。
  • fix(UI): 界面模块。
  • art(VFX): 特效资源。

1.3 Subject (必填)

简短的描述,不超过 50 个字符。
  • 原则: 动词开头,使用祈使句。
  • Good: “Add double jump mechanic” (添加二段跳机制)
  • Bad: “Fixed some bugs” (修了一些bug -> 修了啥??)

1.4 Body (选填,但推荐)

详细描述。
  • 解释为什么要做这个修改?
  • 解释之前是怎么样的,现在是怎么样的?
  • 如果是修复 Bug,描述复现步骤根因
  • 关联的 Issue 或任务 ID。
  • Closes #123
  • BREAKING CHANGE: 如果有破坏性更新(如改了存档格式),必须大写注明!

2. 📝 标准 Commit Log Demo (抄作业区)

请团队成员直接复制以下模板修改。

场景 A: 修复了一个 Bug

fix(Combat): 修复箭塔攻速过快导致伤害丢失的问题

原因: 
之前的攻击冷却计时器使用 Time.deltaTime 累加,在低帧率下会有浮点误差。

修改:
改用 Time.time 时间戳进行冷却判定。

Closes #405

场景 B: 开发了一个新功能

feat(Roguelike): 新增天赋 "火焰精通"

效果:
所有造成物理伤害的防御塔,现在有 30% 概率附加点燃效果。

技术细节:
1. 在 DamageCalculator 中新增了 ElementCheck 逻辑。
2. 新增了 Buff_Ignite 脚本。

场景 C: 提交美术资源

art(Enemy): 提交 Level 3 精英怪 "石头人" 资源

包含:
1. 模型: Golem_L3.fbx (带 LOD)
2. 贴图: T_Golem_D/N/M_01.png (ASTC 压缩)
3. 动画: Anim_Golem_Walk/Attack/Die

注意:
材质球使用了新的 Toon Shader,请程序确认是否支持 GPU Instancing。

场景 D: 性能优化

perf(Pathfinding): 优化大量单位寻路时的 CPU 占用

之前使用 NavMeshAgent.SetDestination 每帧调用,导致主线程卡顿。
现在改为每 10 帧 (0.2s) 更新一次路径,并启用了 Job System 进行距离计算。

性能提升:
同屏 500 单位时,Update 耗时从 8ms 降至 1.5ms。

3. 分支管理策略 (Branching Strategy)

3.1 分支命名

  • master / main: 随时可发布的稳定版本。绝对禁止直接 Push
  • develop: 开发主分支。所有 Feature 分支合入这里。
  • feat/xxx: 功能分支。如 feat/login_system
  • fix/xxx: 修复分支。如 fix/crash_on_start
  • art/xxx: 美术资源分支。

3.2 工作流 (Workflow)

  1. 接到任务 “开发登录系统”。
  2. 基于 develop 切出 feat/login
  3. 开发… 提交… (多次 Commit)。
  4. 开发完毕,推送到远程。
  5. 发起 Pull Request (PR) 合入 develop
  6. Code Review: 同事检查代码,确认无误后 Approve。
  7. 合并。

4. 工具强制约束 (Enforcement)

为了防止人为偷懒,建议部署 Git Hooks

4.1 commit-msg Hook

.git/hooks/commit-msg 中添加脚本,使用正则表达式检查 Commit Message 格式。如果不符合 <type>(<scope>): <subject> 格式,直接拒绝提交。

4.2 pre-commit Hook

在提交前自动运行:
  • 代码格式化 (CSharpier / Format)。
  • 简单的静态检查 (如有无带 Debug.Log 的代码)。

最后通牒: “Update”, “Fix bug”, “Backup”, ”…” 这种 Commit Message 一经发现,请请全组喝奶茶

📚 扩展阅读与参考标准 (References)

🌍 行业标准

  • Conventional Commits
    • 本文档基于此规范。它是目前最流行的 Commit Message 标准,被 Angular, React, Electron 等数万个开源项目采用。
  • Semantic Versioning 2.0.0 (语义化版本控制)
    • 解释了为什么 BREAKING CHANGE 会导致大版本号 +1 (v1.0.0 -> v2.0.0)。

🔧 自动化工具

  • Husky
    • 最流行的 Git Hooks 工具。可以用它在 git commit 之前自动运行 Lint 检查。
  • Commitlint
    • 一个命令行工具,用来检查 Commit Message 是否符合 Conventional Commits 规范。建议集成到 CI/CD 流程中。

📖 深度文章

  • How to Write a Git Commit Message (Chris Beams)
    • 这篇博客被无数人引用,详细解释了“为什么要用祈使句”、“为什么首行不能超过50个字符”。

🐙 GitHub 工作流与 PR 最佳实践

核心理念: 主分支 (main/develop) 是神圣不可侵犯的。任何代码想要进入主分支,必须经过至少一双眼睛的检查 (Code Review)。这个过程就叫 Pull Request (PR)

1. 标准工作流 (The Flow)

1.1 Fork vs Branch

  • 开源模式 (Fork): 你没有原仓库的写权限。你把仓库 Fork 到你自己名下,改完后向原仓库发起 PR。
  • 团队模式 (Branch): 你有写权限。你直接在原仓库里切一个 feature/xxx 分支,改完后向 develop 发起 PR。
  • Vampirefall 推荐: 团队模式。效率更高。

1.2 完整生命周期

  1. 新建分支: 基于最新 develop 创建 feat/tower_fire
  2. 提交代码: 在 feat/tower_fire 上 commit。
  3. 发起 PR: 在 GitHub/Gitea 网页上点击 “New Pull Request”。
    • Source: feat/tower_fire
    • Target: develop
  4. Code Review: 你的同事收到通知,进来检查代码,写评论。
  5. 修改反馈: 根据同事的建议,继续在 feat/tower_fire 上提交修改。
  6. 合并 (Merge): 同事点赞 (Approve) 后,点击 “Squash and Merge”。
  7. 删除分支: 完事后删掉 feat/tower_fire

2. 如何写一个优秀的 PR 描述?

PR 的描述决定了 Reviewer 的心情和审核速度。

2.1 标题 (Title)

  • 格式: <Type>: <Subject> (同 Commit Message)
  • 例子: feat: 实现火焰塔的燃烧逻辑

2.2 模板 (Template)

建议在仓库根目录建一个 .github/PULL_REQUEST_TEMPLATE.md,内容如下:
## 📝 改动摘要
实现了火焰塔的基础逻辑,包括 DoT 伤害和视觉特效。

## 📸 截图/GIF (选填)
[这里放一张火焰塔攻击怪物的 GIF,胜过千言万语]

## 🔗 关联 Issue
Closes #102

## ✅ 自测清单
- [x] 塔能正常攻击
- [x] 燃烧伤害数值正确
- [x] 怪物死亡后特效消失
- [x] 没有产生 GC Alloc

3. Code Review 礼仪与标准

3.1 Reviewer (审核者) 的职责

  • 看逻辑: 代码是否实现了需求?有没有明显的 Bug?
  • 看规范: 变量名是否规范?有没有写注释?
  • 看性能: 有没有在 Update 里 new List?有没有死循环风险?
  • 语气: 对事不对人
    • Bad: “你这代码写得太烂了。”
    • Good: “这里可能会产生 GC,建议改用对象池。“

3.2 Submitter (提交者) 的心态

  • 不要玻璃心: 别人指出的问题是为了项目好,不是针对你。
  • 解释: 如果你不认同 Reviewer 的意见,请在评论里解释你的理由,或者线下沟通。
  • 及时响应: 别发了 PR 就不管了,别人提了意见赶紧改。

4. Merge 策略:Squash vs Merge

点击 Merge 按钮时,有三种选项:

4.1 Create a merge commit (普通合并)

  • 保留所有历史记录。如果你的分支上有 100 个 “fix typo” 的垃圾提交,它们都会进入主分支。
  • 评价: ❌ 。不推荐。

4.2 Squash and merge (压缩合并) - 推荐

  • 把你分支上的 100 个提交压缩成 1 个提交,合入主分支。
  • 评价: ✅ 干净。主分支的历史记录非常清晰,一个功能对应一个 Commit。

4.3 Rebase and merge (变基合并)

  • 把你的提交直接接到主分支后面,像从来没分叉过一样。
  • 评价: ⚠️ 高风险。如果不仅保留了垃圾提交,还没有 Merge 节点,出问题很难回退。

5. 常见问题

  • PR 冲突了怎么办?:
    • 在本地 git pull origin develop (把主分支最新代码拉下来)。
    • 本地解冲突。
    • git push 更新你的 PR 分支。GitHub 会自动更新状态。
  • PR 太大了怎么办?:
    • 如果一个 PR 改了 50 个文件,没人愿意看。
    • 拆分: 先提一个 feat/tower_base (只有基类),合入后再提 feat/tower_fire

一句话总结: PR 是代码质量的守门员。 没有 Review 的代码,就是埋在项目里的雷。

🐢 SVN vs 🐙 Git:深度对比与极简上手指南

写在前面: 很多团队(尤其是美术同学)习惯了 SVN 的“直观”,对 Git 感到恐惧。其实 Git 并没有那么难,只是逻辑变了。 核心区别: SVN 是集中式的 (服务器坏了大家都得停工),Git 是分布式的 (每个人电脑里都有一份完整的版本库)。

1. 深度对比分析 (Analysis)

1.1 为什么美术喜欢 SVN?

  • 文件锁定 (Locking): SVN 可以在你编辑 Hero.psd 时“锁住”它,别人就不能改了。这对二进制文件(图片、模型)至关重要,防止冲突。Git 默认没有锁(需要 LFS 插件)。
  • 局部检出: SVN 可以只下载项目的 Art/Characters 文件夹。Git 必须把整个仓库(包括你不关心的代码和文档)全拉下来。
  • 权限控制: SVN 可以精确控制“美术只能读写 Art 目录,不能碰 Code 目录”。Git 通常是仓库级别的权限(要么都能读写,要么都不能)。

1.2 为什么程序喜欢 Git?

  • 分支 (Branching): Git 切分支是秒级的。每个功能一个分支,互不影响。SVN 切分支很重,而且合并代码简直是噩梦。
  • 离线工作: Git 可以在没网的时候提交代码(Commit 到本地),等有网了再推送到服务器。SVN 没网就废了。
  • 安全性: Git 每个人的电脑里都有完整备份。服务器炸了,随便找台电脑就能恢复。SVN 服务器炸了且没备份,项目就没了。

1.3 结论:Vampirefall 该选谁?

  • 代码/配置: 必须用 Git。分支管理和合并是多人协作的刚需。
  • 美术大资源 (PSD/MAX):
    • 方案 A: 继续用 SVN 管理源文件,导出后的资源 (FBX/PNG) 进 Git。
    • 方案 B: 全面转 Git,但必须开启 Git LFS (Large File Storage) 并配置文件锁。

2. 🐙 Git 极简上手指南 (美术/策划专用版)

忘掉命令行! 我们推荐使用 SourcetreeGitHub DesktopTortoiseGit (小乌龟,长得跟 SVN 很像)。

2.1 核心概念对应表 (SVN -> Git)

你在 SVN 做的操作在 Git 里的对应操作区别
Update (更新)Pull (拉取)没区别,都是把服务器的东西拉下来。
Commit (提交)Commit (提交) + Push (推送)这是最大的坑!
SVN 提交就完事了。
Git 提交只是存到你自己电脑里,必须再点一下 Push 才能传到服务器给别人看。
Revert (还原)Discard / Reset放弃修改,还原到上次提交的状态。
Lock (锁定)LFS Lock需要专门配置 LFS 才能用。

2.2 傻瓜式工作流 (Daily Workflow)

假设你使用 TortoiseGit (因为它和 SVN 操作最像):

第一步:早上开工 (Pull)

  1. 在项目文件夹上右键 -> TortoiseGit -> Pull
  2. OK
  3. 目的: 确保你拿到的是最新版本,防止和别人冲突。

第二步:干活 (Work)

  • 改 Excel,画图,改场景… 随便弄。

第三步:下班提交 (Commit + Push)

  1. 右键 -> Git Commit -> "master"
  2. 勾选你修改的文件。
  3. 在 Message 框里写:art: 修改了吸血鬼主角的模型贴图
  4. 点击 Commit
  5. 关键动作: 此时弹窗左下角会有一个 Push 按钮,一定要点一下! (或者 Commit 完单独右键 -> Push)。
  6. 目的: 只有 Push 成功了,你的东西才算真正提交了。

2.3 遇到冲突怎么办? (Conflict)

  • 现象: Push 失败,提示 Updates were rejected
  • 原因: 你改了 Data.xlsx,小王也改了 Data.xlsx,而且他比你先 Push。
  • 解决:
    1. 先点 Pull。Git 会试图合并。
    2. 如果合并失败,文件上会有个感叹号。
    3. 策划/美术: 别慌!直接找程序帮忙,或者备份你的文件,还原 (Revert),拉取最新 (Pull),再把你的改动覆盖上去。
    4. 程序: 使用 Merge Tool 解决冲突。

3. 🚀 给主程的建议:如何平滑迁移?

  1. 保留 SVN 习惯: 给美术装 TortoiseGit,因为右键菜单的操作习惯和 SVN 几乎一样,学习成本最低。
  2. 忽略文件配置 (.gitignore):
    • 务必把 Library/, Temp/, Logs/, .vs/ 屏蔽掉。SVN 以前可能把这些垃圾都传上去了,Git 绝对不行。
  3. LFS 强制开启:
    • 配置 .gitattributes,把 *.psd, *.fbx, *.png, *.wav 全部走 LFS。否则 1个月后你的 Git 仓库会大到拉不下来。

一句话总结: Git = SVN + “本地仓库”。 以前是 写完 -> 上传。 现在是 写完 -> 存本地 (Commit) -> 上传 (Push)。 多了一步,但更安全。