引言:为什么要学 Git?
在开始正式内容之前,我想先问你几个问题:
- 你是否曾把
论文_最终版.docx、论文_最终版_v2.docx、论文_真正最终版.docx、论文_打死也不改了.docx摆在同一个文件夹里? - 你是否曾在写代码时,因为改坏了一个文件想要找回昨天的版本,结果发现昨天没保存?
- 你是否曾经和同学合作写一份报告,最后发现两个人的修改互相覆盖,谁的版本才是”最终”成了一个哲学问题?
- 你是否曾在某次电脑蓝屏后,看着硬盘上仅存的那一份残缺文件,欲哭无泪?
如果以上任何一个场景戳中了你,那么恭喜你——你已经亲身体验过 「没有版本控制的痛苦」。而 Git,就是为彻底解决这些问题而生的工具。
Git 是什么?一句话回答
Git 是一个分布式版本控制系统。
这句话里有两个关键词:
- 版本控制:记录文件每次修改的历史,可以随时回到任意一个历史版本
- 分布式:每个人的电脑上都有完整的项目历史,不依赖中央服务器也能工作
听起来很抽象?我们用一个类比来理解。
类比:Git 就像一个「游戏存档系统」
想象你在玩一个开放世界游戏。每到一个关键时刻,你可以按一下
F5(或存档键),把当前的游戏状态保存下来。
- 之后遇到 BOSS 打不过?→ 读档回到上一个存档点
- 不小心掉下悬崖?→ 读档就行
- 想试试不同的剧情走向?→ 可以在不同存档点之间反复横跳
- 想和朋友联机?→ 把存档发给他,他接着玩
Git 就是你写文档、写代码时的「存档系统」。 而且它比游戏存档更强大:你能看到每一次存档之间具体改了什么、谁改的、什么时候改的、改动的目的是什么。
为什么不是别的工具?
你可能听过其他类似的工具,比如 SVN、CVS、Perforce 等。Git 相比它们,最大的优势是 分布式架构。
graph LR
A[用户 A] -->|集中式<br>中心服务器| SVNCenter[中心服务器]
B[用户 B] -->|集中式<br>中心服务器| SVNCenter
C[用户 C] -->|集中式<br>中心服务器| SVNCenter
A2[用户 A<br>完整历史] <-->|分布式| B2[用户 B<br>完整历史]
B2 <-->|分布式| C2[用户 C<br>完整历史]
C2 <-->|分布式| A2
- 集中式(SVN):所有历史只存在中央服务器上,离线就抓瞎
- 分布式(Git):每个人的电脑都有完整历史,离线也能提交、查日志、回滚
Git 的诞生:传奇的 10 天
2005 年,Linux 内核的开发者们(由 Linus Torvalds 带领)和当时使用的商业版本控制工具 BitKeeper 的版权方闹翻了,对方收回了免费使用权。Linus 一怒之下,决定自己写一个版本控制工具。10 天后,Git 诞生了。
更传奇的是:这个 10 天写出来的工具,到 20 年后的今天,依然是全世界最主流的版本控制系统。从 Linux 内核到 Windows 源码,从谷歌到微软,从个人开发者到大公司,几乎所有人都在用 Git。
一句话总结:Git 不只是程序员的工具,而是 任何需要管理「文件历史」的人的必备工具。
安装与配置:第一次亲密接触
下载安装
Git 是跨平台的,三大主流操作系统都支持:
| 操作系统 | 安装方式 |
|---|---|
| Windows | 下载 Git for Windows 安装包:https://git-scm.com/download/win ,双击运行,全程「Next」即可 |
| macOS | 方式一:安装 Xcode Command Line Tools(终端输入 xcode-select --install)方式二:使用 Homebrew: brew install git |
| Linux (Debian/Ubuntu) | sudo apt update && sudo apt install git |
| Linux (Fedora/RedHat) | sudo dnf install git |
安装完成后,打开终端(Windows 用户打开 Git Bash),输入:
$ git --version
git version 2.42.0.windows.2
看到类似上面的输出,就说明安装成功啦。
第一次必做:配置你的身份
在你能用 Git 提交任何东西之前,必须先告诉 Git「你是谁」。这一步非常重要,因为 Git 的每一次提交都会记录作者信息。
# 设置全局用户名
$ git config --global user.name "Lenny Li"
# 设置全局邮箱(用你常用的邮箱)
$ git config --global user.email "lennyli663@icloud.com"
# 查看配置是否生效
$ git config --list
注意:
--global表示这是「全局配置」,对你这台电脑上的所有 Git 仓库都生效- 如果你想为某个特定项目用不同的身份,进入项目目录后用
git config user.name "xxx"(不带--global)即可- 邮箱不必是真实邮箱,但 建议用真实邮箱——因为很多平台(特别是 GitHub)会用邮箱匹配你的头像
配置 SSH Key:免密码推送的关键
当你以后要把本地代码推送到 GitHub 这样的平台时,每次都输密码会非常烦。配置 SSH Key 可以让你”一次配置,长期免密”。
步骤一:生成 SSH 密钥对
$ ssh-keygen -t ed25519 -C "lennyli663@icloud.com"
# 一直按回车,使用默认路径和空密码
执行成功后,会在 ~/.ssh/ 目录下生成两个文件:
id_ed25519:私钥(绝对不能泄露!)id_ed25519.pub:公钥(可以安全地放到网上)
步骤二:把公钥添加到 GitHub
- 登录 GitHub,点击右上角头像 →
Settings - 左侧菜单选择
SSH and GPG keys - 点击
New SSH key Title随便填(比如 “My Laptop”),Key粘贴id_ed25519.pub文件的完整内容- 点击
Add SSH key
步骤三:测试连接
$ ssh -T git@github.com
Hi Lenny-lab! You've successfully authenticated, but GitHub does not provide shell access.
看到类似 "Hi <你的用户名>!" 的输出,说明配置成功!
常用配置文件 .gitconfig
除了用户名和邮箱,你还可以个性化很多 Git 行为。编辑 ~/.gitconfig 文件:
[user]
name = Lenny Li
email = lennyli663@icloud.com
[core]
editor = vim
autocrlf = input # Windows 用户推荐设置
[alias]
# 命令别名,让 Git 更好用
st = status
co = checkout
br = branch
ci = commit
unstage = reset HEAD --
last = log -1
visual = log --graph --oneline --all
lola = log --graph --decorate --oneline --all
[pull]
rebase = false # 拉取时是否自动 rebase
小技巧:让你的 Git 颜值飙升
试试这个命令,看看你项目历史的可视化图谱:
git log --graph --oneline --all --decorate是不是感觉像在看电影里的黑客终端?配上
alias.visual = "log --graph --oneline --all"这个别名,你以后就能用git visual一键召唤它。
核心概念:理解 Git 的灵魂
这一节是全文的 最重要部分。如果你只想读一节,就读这一节。
三个区:工作区 / 暂存区 / 版本库
Git 之所以强大且令人困惑,根本原因就是它把文件的存储分成了 三个区。
graph LR
A[工作区<br>Working Directory] -->|git add| B[暂存区<br>Staging Area]
B -->|git commit| C[版本库<br>Repository]
C -.->|git checkout| A
用写论文来类比三个区:
- 工作区:你正在 Word 里写的草稿,随时在改
- 暂存区:你把这次要发给导师的章节”勾选”出来,放进一个”待提交列表”里
- 版本库:导师确认后,这部分内容”正式归档”,成为论文历史的一部分
为什么要搞这么复杂?一个”保存”按钮不就够了吗?
因为 暂存区给了你精细控制的能力。比如你改了一个文件里的 3 个 Bug,但只希望把第 1 个和第 3 个 Bug 的修复打包成一个 commit,第 2 个 Bug 等修完再单独提交——这时候暂存区就派上用场了。
文件的四种状态
站在三个区的视角看,每个文件可以处于以下 四种状态:
| 状态 | 所在区域 | 说明 |
|---|---|---|
| 未跟踪 (Untracked) | 仅有工作区 | 新创建的文件,Git 还没”认识”它 |
| 已修改 (Modified) | 工作区(暂存区有旧版本) | 文件被改了,但还没放到暂存区 |
| 已暂存 (Staged) | 暂存区 | 文件已经”勾选”了,等待提交 |
| 已提交 (Committed) | 版本库 | 文件已经”归档”到历史里了 |
你可以用 git status 命令查看当前所有文件的状态:
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: README.md
new file: index.html
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: style.css
读到 Changes to be committed 就说明文件已经在暂存区了;读到 Changes not staged for commit 则说明文件改了但还没 add。
Commit:一次快照,不是一次增量
很多初学者以为 commit 是”保存当前修改的差异”。这个理解是错的。
Commit 是一次完整的快照(snapshot)。每次你 git commit,Git 都会把所有文件的当前状态完整地拍一张”照片”,存进版本库。
graph LR
C1[C1<br>初始版本<br>完整快照] --> C2[C2<br>改了 3 个文件<br>完整快照]
C2 --> C3[C3<br>加了 2 个文件<br>完整快照]
C3 --> C4[C4<br>删除 1 个文件<br>完整快照]
C4 --> C5[C5<br>改了 1 个文件<br>完整快照]
每个 commit 都有一个唯一的 SHA-1 哈希值(比如 a1b2c3d4e5...),用来标识这个”快照”。这也是为什么 Git 经常用一长串字母数字来代表某个版本。
Branch:可移动的指针
如果你只能一条直线地提交下去,那 Git 也没什么神奇的。Git 真正强大的地方在于 分支(Branch)。
一个 commit 是一个快照,一个 branch 是一个指向某个 commit 的可移动指针。
就这么简单。
gitGraph
commit
commit
commit id: "C3"
commit id: "C4"
branch feature
commit id: "C5"
checkout main
commit id: "C6"
在图中:
main分支指向C6feature分支指向C5HEAD是一个特殊的指针,指向 当前所在分支- 两个分支在
C3处分叉,之后各自演进
分支的本质就是”在某个节点上分出一条新的时间线”。这意味着你可以:
- 拉一个新分支专门修复 Bug,主分支继续开发新功能
- 拉一个新分支试一个大胆的 idea,搞砸了直接删掉分支,主分支毫发无损
- 多人协作时,每人拉一个分支,最后再合并到一起
HEAD:当前所在的位置
HEAD 是 Git 里另一个常被提及的概念。简单来说:
HEAD通常指向当前所在的分支(即”我在哪个分支上工作”)- 当你
git checkout C3时,HEAD会变成”分离”状态,直接指向C3这个 commit(不推荐新手这么做)
每次你新建文件、修改代码,产生的修改都属于 HEAD 指向的那个分支。理解了这一点,很多看似神秘的 Git 行为就都顺理成章了。
一次完整提交流程的”流水线”
完整工作流示例:
# 1. 假设你刚改了 README.md 和 index.html
$ git status
On branch main
Changes not staged for commit:
modified: README.md
Untracked files:
index.html
# 2. 把 README.md 加入暂存区
$ git add README.md
# 3. 暂存区现在有 README.md,但 index.html 还在工作区
$ git status
Changes to be committed:
modified: README.md
Untracked files:
index.html
# 4. 提交暂存区的内容
$ git commit -m "更新了 README"
# 5. 提交完成,暂存区清空
$ git status
Untracked files:
index.html
这一节的重点
- Git 把文件分成 三个区:工作区、暂存区、版本库
- 每个文件有 四种状态:未跟踪 / 已修改 / 已暂存 / 已提交
- Commit 是一次完整快照,不是差异
- Branch 是一个可移动指针,指向某个 commit
- HEAD 指向当前所在分支
掌握了这五个概念,你就掌握了 Git 80% 的核心。
常用命令详解:从入门到日常使用
这一节列出 Git 的高频命令,并配上实际例子。建议作为速查表收藏,遇到问题时翻一翻。
初始化与克隆
创建一个新的 Git 仓库(从零开始一个项目):
$ cd my-project
$ git init
Initialized empty Git repository in /path/to/my-project/.git/
克隆一个已有的仓库(比如从 GitHub 复制一个项目到本地):
# 克隆自己的仓库(HTTPS 方式)
$ git clone https://github.com/username/repo.git
# 克隆自己的仓库(SSH 方式,推荐)
$ git clone git@github.com:username/repo.git
# 克隆到指定目录
$ git clone https://github.com/username/repo.git my-folder
查看状态与历史
$ git status # 查看工作区和暂存区的状态
$ git log # 查看提交历史(详细)
$ git log --oneline # 简洁版历史(一行一个 commit)
$ git log --graph # 图形化显示分支
$ git log --all # 显示所有分支的提交
$ git diff # 工作区 vs 暂存区的差异
$ git diff --staged # 暂存区 vs 最新 commit 的差异
一个非常好用的组合:
$ git log --graph --oneline --all --decorate
* a1b2c3d (HEAD -> main, origin/main) 修复首页加载慢的问题
* d4e5f6g 添加用户登录功能
| * h7i8j9k (feature/cart) 实现了购物车逻辑
|/
* l0m1n2o 初始化项目结构
添加与提交
$ git add <file> # 添加指定文件到暂存区
$ git add . # 添加当前目录所有修改
$ git add -A # 添加所有修改(包括删除)
$ git add -p # 交互式 add(推荐新手试试)
$ git commit -m "提交说明" # 提交暂存区到版本库
$ git commit -am "说明" # add + commit 一步(只对已跟踪文件有效)
$ git commit --amend # 修改上一次提交(合并、修改说明)
提交信息(commit message)的写法
不要用
"update"、"fix"、"改了一点"这种含糊的说明。 好的提交信息应该能 让人一眼看出这次改了什么、为什么改。推荐格式(Conventional Commits):
<类型>(<范围>): <简短说明>例如:
feat: 添加用户登录功能fix: 修复首页 500 报错docs: 更新 README 安装步骤style: 统一代码缩进为 4 空格refactor: 重构数据处理模块
撤销操作:最危险也最有用的部分
| 想做什么 | 用这个命令 | 说明 |
|---|---|---|
| 撤销工作区的修改 | git restore <file> | 放弃工作区的修改,回到暂存区(或最新 commit)的状态 |
| 撤销暂存(取消 add) | git restore --staged <file> | 把文件从暂存区”拿出来”,回到工作区 |
| 修改上一次的提交 | git commit --amend | 把暂存区内容合并进上一次 commit |
| 撤销某次 commit(保留修改) | git reset HEAD~1 | 回退到上一次 commit,修改还在工作区 |
| 撤销某次 commit(彻底删除) | git reset --hard HEAD~1 | 危险! 工作区修改也会被删除 |
| 生成反向 commit(不删历史) | git revert <commit> | 新增一个 commit 来”撤销”指定 commit,适合已推送的 commit |
危险!—hard 选项
git reset --hard会 彻底删除 工作区的修改。一旦执行,被删除的内容理论上还能从reflog里找回来(但有保质期)。黄金法则:
- 永远不要对 已推送到远程的 commit 用
--hard- 撤销已推送的 commit,用
revert才是正确做法
分支管理
$ git branch # 列出所有本地分支
$ git branch -a # 列出所有分支(包括远程)
$ git branch <name> # 创建新分支
$ git checkout <name> # 切换到指定分支
$ git switch <name> # 同上(Git 2.23+ 新命令,更直观)
$ git switch -c <name> # 创建并切换到新分支
$ git branch -d <name> # 删除已合并的分支
$ git branch -D <name> # 强制删除分支(即使没合并)
$ git merge <branch> # 把指定分支合并到当前分支
$ git rebase <branch> # 把当前分支的提交"重新播放"到指定分支上
远程操作:与 GitHub 互动
$ git remote -v # 查看远程仓库地址
$ git remote add origin <url> # 添加远程仓库(origin 是默认名)
$ git push -u origin main # 第一次推送 main 分支
$ git push # 之后每次推送就用 git push
$ git pull # 拉取远程最新代码并合并
$ git fetch # 只拉取,不自动合并
$ git fetch --all # 拉取所有远程分支的更新
pull vs fetch 的区别:
fetch:把远程的最新状态下载下来,但 不自动合并 到你的工作区pull:先fetch,然后 自动 merge 到当前分支
如果你想先看看远程有什么新东西再决定合不合并,用 fetch;如果你信任远程的代码,直接 pull 即可。
合并 vs 变基:最大的坑
这是 Git 里最让人迷惑的部分。我们用图来对比:
merge(合并):保留分支历史,创建一个 merge commit
gitGraph
commit id: "C1"
commit id: "C2"
commit id: "C3"
branch feature
commit id: "C4"
checkout main
commit id: "C5"
checkout feature
commit id: "C6"
checkout main
merge feature id: "M"
rebase(变基):把分支的提交”移植”到目标分支之后,历史变成一条直线
gitGraph
commit id: "C1"
commit id: "C2"
commit id: "C3"
commit id: "C4"
commit id: "C5'"
commit id: "C6'"
| 维度 | merge | rebase |
|---|---|---|
| 历史形态 | 分叉后合并,呈”八卦图” | 线性历史,干净整洁 |
| 是否改写 commit | 不改写 | 改写(生成新哈希值) |
| 适合场景 | 公共分支、已推送的 commit | 本地分支、想保持线性历史 |
| 冲突处理 | 一次性合并,所有冲突一起解决 | 逐个 commit 重新应用,可能多次解决冲突 |
| 何时 不要 用 | — | 已推送到远程的 commit |
黄金法则
永远不要对已经推送到远程的 commit 做 rebase。
因为 rebase 会改写 commit 的哈希值,导致本地和远程历史不一致,给协作者带来混乱。 如果你想把远程的更新同步到本地,用
merge;如果你想整理本地未推送的 commit,用rebase。
储藏(Stash):临时保存工作进度
有时候你正在改代码,但突然需要切到别的分支处理紧急 Bug,这时候可以用 stash 把当前修改”暂存”起来。
$ git stash # 把当前工作区和暂存区的修改保存起来
$ git stash list # 查看所有储藏
$ git stash pop # 恢复最近一次储藏
$ git stash apply stash@{0} # 恢复指定储藏
$ git stash drop stash@{0} # 删除指定储藏
$ git stash clear # 清空所有储藏
标签(Tag):给重要时刻打标记
当项目发布 v1.0、v2.0 这种重要版本时,可以打一个 tag:
$ git tag # 列出所有标签
$ git tag v1.0 # 给当前 commit 打轻量标签
$ git tag -a v1.0 -m "发布说明" # 给当前 commit 打附注标签(推荐)
$ git tag v0.9 <commit-hash> # 给指定 commit 打标签
$ git push origin v1.0 # 推送标签到远程
$ git push origin --tags # 推送所有标签
分支策略:团队协作的规则
当你一个人写项目时,分支的意义不大——但当你和团队一起工作时,分支策略决定了协作效率。
为什么需要分支策略?
想象一个 5 人团队:
- 小张在写登录功能
- 小李在修支付 Bug
- 小王在做性能优化
- 小赵在写一个新模块
- 小孙在改一个老 Bug
如果所有人都在 main 分支上工作,会出现:
- 谁改了哪行代码很难追溯
- 一个不成熟的改动可能拖垮整个项目
- 紧急 Bug 修复和正常功能开发互相冲突
分支策略就是用来解决这些问题的:约定好大家如何创建分支、命名分支、合并分支、回滚分支。
三大主流分支模型
Git Flow
由 Vincent Driessen 在 2010 年提出,是最经典、最完整的分支模型,适合 有明确发布周期 的传统软件项目。
主要分支:
main:主分支,随时可发布的稳定版本develop:开发分支,所有功能开发完成后合并到这里
辅助分支:
feature/*:开发新功能,从develop拉出,完成后合并回developrelease/*:发布前准备,从develop拉出,完成后同时合并到main和develophotfix/*:紧急修复,从main拉出,完成后同时合并到main和develop
优点:结构清晰、纪律严格、适合大型项目 缺点:流程复杂、对小项目过重
GitHub Flow
GitHub 官方推荐的轻量级流程,适合 持续部署 的 Web 项目。
graph LR
A[1. 创建分支] --> B[2. 提交修改]
B --> C[3. 发起 PR]
C --> D[4. 审查合并]
D --> E[5. 自动部署]
核心规则:
main分支永远是可部署的- 任何新功能都在自己的分支上开发
- 通过 Pull Request 合并回
main - 合并后自动部署
优点:简单清晰、适合 CI/CD 缺点:对多版本并行支持较弱
Trunk-based Development
最激进的策略:所有开发都在一个分支(main / trunk)上完成。
- 不长期持有功能分支
- 通过 Feature Flag 控制新功能的发布
- 频繁合并(每天至少一次)
- 适合 极强工程能力 的团队(Google、Facebook)
个人项目适合哪种?
推荐:简化版 GitHub Flow(一人独享版)
main分支:稳定版- 接到一个新需求:
git switch -c feature/xxx - 写完功能、commit、push
- 合并回
main,删掉功能分支
不用搞 develop、release 这些花哨的分支。一人项目,简单就是美。
分支命名建议
好的分支命名应该 一目了然。推荐格式:
<类型>/<简短描述>常见类型:
feature/login—— 新功能:登录fix/payment-bug—— 修复支付 Bugdocs/readme—— 文档:更新 READMErefactor/user-module—— 重构用户模块experiment/new-ui—— 实验性:新 UI
GitHub 全景:不只是代码托管
很多新手以为 GitHub 就是”放代码的网盘”,这是对 GitHub 最大的误解。
GitHub 是一个基于 Git 的协作社交平台,它的功能远不止”托管代码”那么简单。
Repository:仓库
仓库里不只有代码,还有:
- 完整的 Git 历史
- Issue 列表(任务、Bug 跟踪)
- Pull Requests(协作讨论)
- Wiki(项目文档)
- Projects(看板)
- Releases(发布版本)
- Actions(自动化流水线)
Star / Watch / Fork 三大按钮
每个 GitHub 仓库右上角都有这三个按钮,含义分别是:
| 按钮 | 作用 | 使用场景 |
|---|---|---|
| ⭐ Star | 给项目点赞(收藏) | 看到一个喜欢的项目,希望以后能找到 |
| 👁 Watch | 关注项目动态 | 想第一时间知道这个项目更新了什么 |
| ⑂ Fork | 把项目复制到自己的账号下 | 想基于这个项目二次开发,或者提 PR |
Issue:免费的任务跟踪系统
Issue 翻译过来是”问题”的意思,但在 GitHub 里它的功能远不止”报错”——任何需要讨论、跟踪、解决的事项都可以开一个 Issue。
- 报告 Bug:“运行时报错 XXX”
- 提需求:“希望能加个深色模式”
- 提问:“这个功能怎么用?”
- 任务管理:“本周要完成 XXX”
每个 Issue 可以:
- 打标签(Label):
bug、enhancement、documentation等 - 指派给某人(Assignee)
- 关联到某个项目(Project)
- 引用其他 Issue 或 PR:
#123 - 关闭后会自动留下完整讨论历史
Pull Request:协作的灵魂
如果说 Git 让代码管理变得可能,那 Pull Request(PR)就是 GitHub 让 协作 变得优雅的关键发明。
Pull Request 的本质:告诉项目维护者”我改了点东西,请求你拉(pull)我的修改”。
典型 PR 流程:
graph LR
A[1. Fork<br>到自己的账号] --> B[2. Clone 改 Push]
B --> C[3. 发 PR]
C --> D[4. Code Review]
D --> E[5. 同步 / 合并]
一个好的 PR 应该:
- 标题清晰,说明这次改了什么
- 描述详细,说明为什么改、怎么改的、有哪些影响
- 关联到对应的 Issue(如果修复了某个 Bug)
- 改动尽量小而专注,一个 PR 只做一件事
Actions:自动化流水线
GitHub Actions 是 GitHub 内置的 CI/CD 工具。你可以在 .github/workflows/ 目录下写 YAML 配置文件,让 GitHub 在特定事件发生时自动执行任务。
典型场景:
- 每次 push 代码时,自动跑测试
- 创建 PR 时,自动检查代码风格
- 合并到 main 时,自动部署到服务器
- 每天定时跑一次数据备份
示例:自动跑测试的 workflow
# .github/workflows/test.yml
name: Run Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest
Pages:免费静态网站托管
GitHub Pages 是一项 免费 功能,可以把你的静态网站(HTML/CSS/JS)直接托管到 GitHub 上。
典型用法:
- 个人博客
- 项目文档站
- 个人主页 / 简历页
事实上,你正在看的 lennyli663.com 这个个人站,就是基于 Astro 框架构建,部署在 GitHub Pages 上的。
Profile:打造你的个人品牌
GitHub 还允许你创建一个 特殊的仓库 来定制你的个人主页:
- 创建一个与你的用户名同名的仓库
- 在里面放一个
README.md - 这个 README 会显示在你的个人主页上
实战工作流:从零到协作
个人项目从零起步
场景:你想开始一个新项目(比如写一个计算器)。
# 1. 在 GitHub 上点 "New repository",创建空仓库
# 比如 https://github.com/Lenny-lab/calculator
# 2. 本地初始化项目
$ mkdir calculator && cd calculator
$ echo "# 计算器" > README.md
$ git init
$ git add .
$ git commit -m "Initial commit"
# 3. 关联远程仓库并推送
$ git remote add origin git@github.com:Lenny-lab/calculator.git
$ git push -u origin main
# 4. 之后每次修改的工作流
$ # ... 改代码 ...
$ git add .
$ git commit -m "实现了加法功能"
$ git push
团队协作:Fork + PR 模式
场景:你和同学一起做一个项目,同学是项目 owner,你是贡献者。
sequenceDiagram
participant 你
participant 同学
participant 主仓库
participant 你的Fork
你->>你的Fork: 1. Fork
你的Fork->>你: 2. Clone 改 Push
你->>主仓库: 3. 发 PR
同学->>主仓库: 4. Code Review
同学->>主仓库: 5. 合并
你的Fork->>主仓库: 4. 同步
第一次给开源项目提 PR
场景:你在 GitHub 上看到一个开源项目有个小 Bug,想帮忙修一下。
- 在项目页面点 Fork,把项目复制到你的账号下
- 把你账号下的项目 Clone 到本地
- 创建一个新分支:
git switch -c fix/typo - 改代码、commit、push 到你的 Fork
- 在你的 Fork 页面点 Compare & pull request
- 填写 PR 标题和描述,提交
- 等待项目维护者 review
- 根据反馈修改,或者被合并
第一次 PR 的小贴士
- 从小的改动开始:错别字、文档改进、注释翻译都是不错的第一 PR
- 先看 CONTRIBUTING.md:大多数项目都有关于如何贡献的指南
- 不要大改:一个 PR 只做一件事,方便 review
- 保持礼貌和专业:开源是协作,不要命令式地要求作者接受
真实案例:Astro 项目的协作方式
我自己的个人站(你正在访问的这个)就是基于 Astro 框架的,它的协作模式值得参考:
- 主仓库 withastro/astro:Astro 官方仓库
- 用户提 Issue 报告 Bug 或提建议
- 贡献者 Fork 仓库、修改后发 PR
- 维护者 review 代码、跑 CI 测试、合并
- 每个 PR 都关联到具体的 Issue
- 文档和代码改动用不同的 Label 区分
这就是典型的 开源协作模式,也是 GitHub 最核心的价值所在。
避坑指南:血泪教训总结
这一节列出了我见过的、踩过的、听过的各种”翻车现场”,希望你看到之后能绕开。
常见翻车现场
提交了不该提交的东西
# 错误示范:把 .env 文件加进去了
$ git add .env
$ git commit -m "添加配置文件"
$ git push
# 此时 .env 文件里的 API Key / 密码已经推上去了!
正确做法: .env 之类的敏感文件应该加到 .gitignore 里,永远不要提交。
# 依赖
node_modules/
# 构建产物
dist/
build/
# 环境变量
.env
.env.local
# IDE 配置
.vscode/
.idea/
# 系统文件
.DS_Store
Thumbs.db
# 日志
*.log
已经提交了敏感信息怎么办?
改密码 + 让密钥失效——因为 Git 历史是公开的,即使你删掉了文件,别人也能从历史里找回来。 完整的”清理 Git 历史”流程见 GitHub 官方文档。
提交了巨大文件
$ git add video.mp4 # 1.2 GB
$ git commit -m "添加视频"
$ git push
# 推送时卡死……克隆时极慢……
正确做法: 视频、数据集等大文件应该用 Git LFS(Large File Storage)管理,或放在对象存储(S3、OSS)里。
# 安装 Git LFS
$ git lfs install
# 标记大文件类型
$ git lfs track "*.mp4"
$ git lfs track "*.psd"
# 之后的 add、commit、push 流程不变
$ git add video.mp4
$ git commit -m "添加视频"
$ git push
在错误的分支上工作
你兴冲冲改了半天代码,准备 commit 的时候才发现——你在 main 分支上!但其实你应该在 feature/xxx 分支上。
解决方法:
# 1. 把当前修改暂存起来
$ git stash
# 2. 切到正确的分支
$ git switch feature/xxx
# 3. 把修改取出来
$ git stash pop
冲突了怎么办?
当你和同事改了同一个文件的同一行,merge 或 pull 时会出现冲突:
<<<<<<< HEAD
你的代码
=======
同事的代码
>>>>>>> branch-name
解决步骤:
- 打开冲突文件,手动编辑
- 保留你需要的代码、删除对方的(或保留对方的、或两者都保留)
- 删除
<<<<<<<、=======、>>>>>>>标记 git add <file>git commit
避免冲突的技巧:
- 频繁
pull,避免本地和远程差距太大 - 不同人改不同文件
- 同一个文件的修改尽量分工明确
reflog:你的后悔药
几乎所有”误删”操作,都能用 git reflog 找回来。
# 假设你不小心 reset --hard 把代码删了
$ git reset --hard HEAD~5 # 删了 5 个 commit
# 别慌!用 reflog 找回
$ git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~5
d4e5f6g HEAD@{1}: commit: 添加了重要功能 <-- 这个就是要找的
h7i8j9k HEAD@{2}: commit: ...
# 找回那个 commit
$ git reset --hard d4e5f6g
实用避坑清单
| 不要做 | 应该做 |
|---|---|
git add . 一次提交所有修改 | 按功能分多次 add + commit |
用 update、fix 作为 commit message | 写清楚改了什么、为什么改 |
对已推送的 commit 做 rebase | 用 revert 来撤销 |
把 node_modules/ 提交进仓库 | 加到 .gitignore |
| 把密钥、密码 commit 进去 | 本地 .env,永不提交 |
不写 .gitignore 就开始项目 | 第一件事就是建 .gitignore |
只 commit 不 push | 定期 push 备份到远程 |
在 main 分支上直接开发新功能 | 拉个 feature/xxx 分支 |
与同类工具对比
Git vs SVN vs Mercurial
| 维度 | Git | SVN | Mercurial |
|---|---|---|---|
| 架构 | 分布式 | 集中式 | 分布式 |
| 速度 | 极快 | 较慢 | 快 |
| 分支 | 轻量、廉价 | 沉重 | 轻量 |
| 学习曲线 | 较陡 | 较平缓 | 中等 |
| 社区生态 | 最大 | 较小 | 小 |
| 主流程度 | 绝对主流 | 老项目较多 | 已式微 |
结论:除非你不得不维护老 SVN 仓库,否则 新项目请直接用 Git。
GitHub vs GitLab vs Gitee vs Bitbucket
| 维度 | GitHub | GitLab | Gitee | Bitbucket |
|---|---|---|---|---|
| 母公司 | Microsoft | GitLab Inc. | 开源中国 | Atlassian |
| 中文支持 | 一般 | 一般 | 原生 | 一般 |
| 国内访问 | 偶尔抽风 | 偶尔抽风 | 快 | 偶尔抽风 |
| 免费私有仓 | 无限 | 无限 | 限制数量 | 限制人数 |
| CI/CD | GitHub Actions | GitLab CI | Gitee Go | Bitbucket Pipelines |
| 生态 | 最大最活跃 | 中大 | 国内活跃 | 中等 |
| 推荐场景 | 开源/个人 | 企业自建 | 国内协作 | Jira 用户 |
个人建议:
- 个人项目 / 开源贡献:GitHub(最大社区、最多学习资源)
- 国内团队 / 私有项目:Gitee(访问快、中文好)
- 企业自建 / 数据敏感:GitLab(可私有化部署)
常见问题解答
Q1:我完全不懂编程,能学 Git 吗?
完全可以。 Git 最初是为管理 Linux 内核代码设计的,但它的应用范围远不止代码——任何你想保留历史版本的文件(论文、合同、博客、读书笔记)都可以用 Git 管理。建议先从个人项目开始,把 Git 当成”高级网盘 + 时光机”使用,不需要一开始就接触所有高级功能。
Q2:Git 和 GitHub 是什么关系?我可以只用其中一个吗?
Git 是工具,GitHub 是基于 Git 的平台。理论上你完全可以只用 Git 不上 GitHub——比如本地自娱自乐管理自己的笔记。但 GitHub 提供了很多 Git 本身没有的能力(PR review、Issue tracking、CI/CD 等),所以 两者配合使用 才是最佳实践。类比:Git 像是 Word 文档,GitHub 像是腾讯文档——后者在前者基础上加了协作功能。
Q3:我应该什么时候开始用 Git?
越早越好。 很多人觉得”等我写出像样的项目再用 Git”——这是本末倒置。Git 的价值在于 从第一天就记录你的开发过程。哪怕只是一个 5 行的 Python 脚本,也值得用 Git 管理。建议:从你下一个项目(哪怕只是一个简单的实验代码)开始,就初始化一个 Git 仓库。
Q4:commit 写错了 message,能改吗?
能。 用 git commit --amend -m "新的 message"。注意:
- 只对 最后一次 commit 有效
- 如果 commit 已经推送过,amend 后需要
git push --force,这会 改写远程历史(小心使用)
Q5:我误操作把代码删了,能恢复吗?
99% 的情况下能。 使用 git reflog 查看所有操作历史,找到误操作之前的 commit 哈希,然后用 git reset --hard <hash> 恢复。只有一种情况救不回来:你 git add 后还没 git commit,并且 git stash 也没保留——这时修改还在工作区,但 Git 没有历史记录。所以重要修改一定要 commit,这是 Git 救命的底线。
Q6:GitHub 上传代码安全吗?会被别人偷走吗?
公有仓库(Public)的代码任何人都能看到——这是开源的本质。私有仓库(Private)的代码只有你授权的人能看到,GitHub 承诺不会泄露。建议:
- 包含商业机密或个人隐私的项目,用 Private 仓库
- 想分享但又有点担心,可以加 开源许可证(MIT、Apache 2.0 等),明确授权方式
- 不要把 SSH Key、API Token 等敏感信息写在代码里
Q7:怎么才算”精通” Git?
三个层次:
- 基础:
add/commit/push/pull/log/status/branch/switch/merge—— 80% 的日常需求 - 进阶:
rebase/cherry-pick/stash/reflog/reset/revert/tag—— 处理复杂情况 - 高级:
rebase -i(交互式 rebase)、bisect(二分查找 Bug)、worktree(多工作区)、submodule(子模块)
对大多数用户来说,掌握”基础”已经能解决 90% 的问题,遇到不懂的随时查文档即可。
总结:行动清单
学完这篇文档,你应该掌握了:
- 装好 Git,配置好身份和 SSH Key——这是所有后续操作的基础
- 理解三个区、四个状态——这是 Git 设计的灵魂
- 熟悉 add / commit / push / pull 四大基本命令——覆盖 80% 日常需求
- 学会用 branch 和 merge——开始体验 Git 的强大
- 注册 GitHub,创建第一个仓库——进入协作世界
- 尝试 clone 一个开源项目到本地——感受真实的项目结构
- 发起你的第一个 PR——体验开源协作
- 为你的所有项目写
.gitignore——从一开始就养成好习惯 - 学用 stash 和 reflog——掌握”后悔药”
- 了解 Actions 和 Pages——把 GitHub 用到极致
相关资源
- Git 官方文档:https://git-scm.com/doc
- Pro Git 中文版:https://git-scm.com/book/zh/v2
- GitHub 官方教程:https://docs.github.com/zh
- Learn Git Branching(交互式学习):https://learngitbranching.js.org/
- Oh Shit, Git!?!(救场指南):https://ohshitgit.com/
关于本文档
- 作者:Lenny Li
- 邮箱:lennyli663@icloud.com
- 个人站:lennyli663.com
最后的最后
Git 不只是工具,而是现代软件开发的 协作语言。
学 Git 不是为了”会一项技能”,而是为了 和全世界的开发者用同一种方式对话。
一旦你习惯了 Git 的工作流,你就再也不想回到”论文_最终版_v2.docx”的时代了。
现在就打开终端,输入
git --version,开启你的版本控制之旅吧。