引言:为什么要学 Git?

在开始正式内容之前,我想先问你几个问题:

  • 你是否曾把 论文_最终版.docx论文_最终版_v2.docx论文_真正最终版.docx论文_打死也不改了.docx 摆在同一个文件夹里?
  • 你是否曾在写代码时,因为改坏了一个文件想要找回昨天的版本,结果发现昨天没保存?
  • 你是否曾经和同学合作写一份报告,最后发现两个人的修改互相覆盖,谁的版本才是”最终”成了一个哲学问题?
  • 你是否曾在某次电脑蓝屏后,看着硬盘上仅存的那一份残缺文件,欲哭无泪?

如果以上任何一个场景戳中了你,那么恭喜你——你已经亲身体验过 「没有版本控制的痛苦」。而 Git,就是为彻底解决这些问题而生的工具。

Git 是什么?一句话回答

Git 是一个分布式版本控制系统

这句话里有两个关键词:

  • 版本控制:记录文件每次修改的历史,可以随时回到任意一个历史版本
  • 分布式:每个人的电脑上都有完整的项目历史,不依赖中央服务器也能工作

听起来很抽象?我们用一个类比来理解。

类比:Git 就像一个「游戏存档系统」

想象你在玩一个开放世界游戏。每到一个关键时刻,你可以按一下 F5(或 存档键),把当前的游戏状态保存下来。

  • 之后遇到 BOSS 打不过?→ 读档回到上一个存档点
  • 不小心掉下悬崖?→ 读档就行
  • 想试试不同的剧情走向?→ 可以在不同存档点之间反复横跳
  • 想和朋友联机?→ 把存档发给他,他接着玩

Git 就是你写文档、写代码时的「存档系统」。 而且它比游戏存档更强大:你能看到每一次存档之间具体改了什么、谁改的、什么时候改的、改动的目的是什么。

为什么不是别的工具?

你可能听过其他类似的工具,比如 SVNCVSPerforce 等。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

  1. 登录 GitHub,点击右上角头像 → Settings
  2. 左侧菜单选择 SSH and GPG keys
  3. 点击 New SSH key
  4. Title 随便填(比如 “My Laptop”),Key 粘贴 id_ed25519.pub 文件的完整内容
  5. 点击 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 分支指向 C6
  • feature 分支指向 C5
  • HEAD 是一个特殊的指针,指向 当前所在分支
  • 两个分支在 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

这一节的重点

  1. Git 把文件分成 三个区:工作区、暂存区、版本库
  2. 每个文件有 四种状态:未跟踪 / 已修改 / 已暂存 / 已提交
  3. Commit 是一次完整快照,不是差异
  4. Branch 是一个可移动指针,指向某个 commit
  5. 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'"
维度mergerebase
历史形态分叉后合并,呈”八卦图”线性历史,干净整洁
是否改写 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 拉出,完成后合并回 develop
  • release/*:发布前准备,从 develop 拉出,完成后同时合并到 maindevelop
  • hotfix/*:紧急修复,从 main 拉出,完成后同时合并到 maindevelop

优点:结构清晰、纪律严格、适合大型项目 缺点:流程复杂、对小项目过重

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(一人独享版)

  1. main 分支:稳定版
  2. 接到一个新需求:git switch -c feature/xxx
  3. 写完功能、commit、push
  4. 合并回 main,删掉功能分支

不用搞 developrelease 这些花哨的分支。一人项目,简单就是美。

分支命名建议

好的分支命名应该 一目了然。推荐格式:

<类型>/<简短描述>

常见类型:

  • feature/login —— 新功能:登录
  • fix/payment-bug —— 修复支付 Bug
  • docs/readme —— 文档:更新 README
  • refactor/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):bugenhancementdocumentation
  • 指派给某人(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,想帮忙修一下。

  1. 在项目页面点 Fork,把项目复制到你的账号下
  2. 把你账号下的项目 Clone 到本地
  3. 创建一个新分支:git switch -c fix/typo
  4. 改代码、commit、push 到你的 Fork
  5. 在你的 Fork 页面点 Compare & pull request
  6. 填写 PR 标题和描述,提交
  7. 等待项目维护者 review
  8. 根据反馈修改,或者被合并

第一次 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

解决步骤:

  1. 打开冲突文件,手动编辑
  2. 保留你需要的代码、删除对方的(或保留对方的、或两者都保留)
  3. 删除 <<<<<<<=======>>>>>>> 标记
  4. git add <file>
  5. 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
updatefix 作为 commit message写清楚改了什么、为什么改
对已推送的 commit 做 rebaserevert 来撤销
node_modules/ 提交进仓库加到 .gitignore
把密钥、密码 commit 进去本地 .env,永不提交
不写 .gitignore 就开始项目第一件事就是建 .gitignore
commitpush定期 push 备份到远程
main 分支上直接开发新功能拉个 feature/xxx 分支

与同类工具对比

Git vs SVN vs Mercurial

维度GitSVNMercurial
架构分布式集中式分布式
速度极快较慢
分支轻量、廉价沉重轻量
学习曲线较陡较平缓中等
社区生态最大较小
主流程度绝对主流老项目较多已式微

结论:除非你不得不维护老 SVN 仓库,否则 新项目请直接用 Git

GitHub vs GitLab vs Gitee vs Bitbucket

维度GitHubGitLabGiteeBitbucket
母公司MicrosoftGitLab Inc.开源中国Atlassian
中文支持一般一般原生一般
国内访问偶尔抽风偶尔抽风偶尔抽风
免费私有仓无限无限限制数量限制人数
CI/CDGitHub ActionsGitLab CIGitee GoBitbucket 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% 的问题,遇到不懂的随时查文档即可。


总结:行动清单

学完这篇文档,你应该掌握了:

  1. 装好 Git,配置好身份和 SSH Key——这是所有后续操作的基础
  2. 理解三个区、四个状态——这是 Git 设计的灵魂
  3. 熟悉 add / commit / push / pull 四大基本命令——覆盖 80% 日常需求
  4. 学会用 branch 和 merge——开始体验 Git 的强大
  5. 注册 GitHub,创建第一个仓库——进入协作世界
  6. 尝试 clone 一个开源项目到本地——感受真实的项目结构
  7. 发起你的第一个 PR——体验开源协作
  8. 为你的所有项目写 .gitignore——从一开始就养成好习惯
  9. 学用 stash 和 reflog——掌握”后悔药”
  10. 了解 Actions 和 Pages——把 GitHub 用到极致

相关资源

关于本文档

最后的最后

Git 不只是工具,而是现代软件开发的 协作语言

学 Git 不是为了”会一项技能”,而是为了 和全世界的开发者用同一种方式对话

一旦你习惯了 Git 的工作流,你就再也不想回到”论文_最终版_v2.docx”的时代了。

现在就打开终端,输入 git --version,开启你的版本控制之旅吧。