Git 入门:从零开始掌握本地版本管理 刚开始写项目时很多人都会用一种最直接的方式保存代码版本project project_new project_new2 project_final project_final2 project_final_really项目简单的时候似乎没什么问题但随着修改次数越来越多很快就会遇到一个麻烦到底哪个才是正确版本Git 解决的就是这个问题。它可以记录项目每一次有意义的修改让我们知道代码什么时候改过、改了什么也可以在修改出问题后恢复到之前的版本。这篇文章先只介绍Git 的本地使用流程。不涉及 GitHub、远程仓库和多人协作先把最基础、也是平时最常用的一套流程弄清楚。1. Git 到底是什么Git 是一个分布式版本控制系统。如果暂时不考虑“分布式”这个概念可以先把它理解成一个代码版本管理工具。例如一个 Linux C 项目可能经历初始工程 ↓ 加入鼠标输入 ↓ 完善 read() 错误处理 ↓ 加入 select() ↓ 加入 epoll()如果使用 Git每完成一个比较完整的功能都可以保存一个版本。这些版本被称为commit于是项目历史就可能变成a81c293 Add epoll event monitoring 76f201b Add select event monitoring 42c89ea Fix read return value handling 1df7842 Add mouse input support 933eb37 Initial commit这样不但知道项目经历过哪些修改还能查看每个版本具体改了什么。2. Git 和 GitHub 不是一回事这是刚接触 Git 时最容易混淆的一点。Git 是安装在本地电脑上的版本管理工具。例如git status git diff git add git commit git log这些操作全部可以在没有网络的情况下完成。GitHub 则是一个在线代码托管平台可以把本地 Git 仓库上传到服务器也可以进行多人协作。简单来说Git │ └── 本地版本管理 GitHub │ └── 在线托管 Git 仓库所以即使完全不使用 GitHub也可以正常使用 Git。3. 安装完成后先配置用户信息首先检查 Git 是否已经正确安装git --version例如git version 2.x.x.windows.x第一次使用 Git 时需要配置用户名和邮箱git config --global user.name Your Name git config --global user.email your_emailexample.com这里的名字和邮箱会记录在每一次 commit 中例如Author: Your Name your_emailexample.com它们不是 Git 的登录账号也不是用来接收邮件的只是提交记录中的作者信息。查看当前配置git config --global --list也可以分别查看git config --global user.name git config --global user.email如果以后会把代码推送到 GitHub通常建议使用 GitHub 账号中已经验证过的邮箱或者使用 GitHub 提供的noreply邮箱。4. 先理解 Git 的三个区域Git 的很多命令看起来比较零散但如果先理解三个区域后面的操作会清楚很多。Git 中最重要的是工作区 Working Tree 暂存区 Staging Area 本地仓库 Repository它们之间的关系是┌──────────────────────┐ │ Working Tree 工作区 │ │ │ │ 正在编辑的代码 │ └──────────┬───────────┘ │ git add ↓ ┌──────────────────────┐ │ Staging Area 暂存区 │ │ │ │ 准备提交的修改 │ └──────────┬───────────┘ │ git commit ↓ ┌──────────────────────┐ │ Repository 本地仓库 │ │ │ │ commit A │ │ commit B │ │ commit C │ └──────────────────────┘因此git add并不是正式保存一个版本。它只是告诉 Git这些修改准备放进下一次提交。真正生成版本的是git commit5. 初始化一个 Git 项目假设现在有一个项目mouse_project/ ├── main.c ├── Makefile └── README.md首先进入项目根目录cd mouse_project然后初始化 Gitgit init -b main执行成功后会看到类似Initialized empty Git repository in ...项目目录中还会出现一个隐藏目录.git/于是实际目录大致是mouse_project/ ├── .git/ ├── main.c ├── Makefile └── README.md.git里面保存着版本历史 分支信息 HEAD Git 对象 仓库配置所以.git是整个 Git 仓库最核心的部分。需要注意的是一个项目只需要执行一次git init。之后重新打开这个项目不需要重新初始化。6.git status先学会看状态初始化完成以后可以执行git status这个命令非常重要。它会告诉我们当前在哪个分支 哪些文件是新文件 哪些文件被修改过 哪些文件已经进入暂存区 哪些修改还没有暂存假设刚刚创建了一个main.c执行git status可能会看到Untracked files: main.cUntracked的意思是文件已经存在但是 Git 还没有开始跟踪它。7. 把文件加入暂存区如果只想添加一个文件git add main.c如果想把当前目录的修改一起加入git add .然后再执行git status可能会看到Changes to be committed: new file: main.c此时main.c已经进入暂存区。可以理解为工作区 │ git add ↓ 暂存区但此时还没有产生正式版本。8. 查看准备提交的内容在提交之前最好再检查一次。如果修改还没有执行git addgit diff可以看到工作区相对于当前版本发生了哪些变化。例如- printf(Hello Git!\n); printf(Hello Linux!\n);这里-表示旧内容表示新内容。如果已经执行git add .则应该使用git diff --staged查看暂存区中的内容。因此这两个命令的区别可以记成git diff查看还没有进入暂存区的修改。而git diff --staged查看已经进入暂存区、准备提交的修改。9. 创建第一次 commit确定暂存区内容没有问题以后git commit -m Initial commit这里-m表示直接指定提交说明。执行成功后Git 就正式保存了当前版本。现在项目已经拥有第一个 commit。10. 查看版本历史使用git log --oneline可以看到简洁的提交历史。例如933eb37 Initial commit前面的933eb37是 commit ID 的缩写。后面的Initial commit是提交信息。项目继续开发以后可能变成a81c293 Add epoll event monitoring 76f201b Add select event monitoring 42c89ea Fix read return value handling 1df7842 Add mouse input support 933eb37 Initial commit这样项目的开发过程就非常清楚了。11. 日常开发时怎么用 Git第一次初始化以后平时不需要再执行git init正常开发基本就是重复下面这套流程。先查看状态git status然后修改代码。修改完成git status看看哪些文件发生了变化。再执行git diff检查具体修改内容。然后编译、运行或者测试程序。确认代码没问题以后git add .接着git diff --staged最后再提交git commit -m Fix input event read handling整个流程可以简化成修改代码 ↓ git status ↓ git diff ↓ 编译 / 测试 ↓ git add . ↓ git diff --staged ↓ git commit这就是 Git 最核心的日常使用流程。12. 一个完整的 C 项目示例下面用一个简单的 C 程序实际走一遍。创建main.c内容#include stdio.h int main(void) { printf(Hello Git!\n); return 0; }初始化git init -b main查看状态git status此时main.c是未跟踪文件。加入暂存区git add main.c检查git diff --staged第一次提交git commit -m Initial commit查看git log --oneline得到933eb37 Initial commit13. 修改代码并创建第二个版本把printf(Hello Git!\n);改成printf(Hello Linux!\n);然后git status会看到modified: main.c检查修改git diff大概会显示- printf(Hello Git!\n); printf(Hello Linux!\n);如果程序测试正常git add main.c查看暂存区git diff --staged提交git commit -m Change greeting to Linux再查看历史git log --oneline此时a31cb92 Change greeting to Linux 933eb37 Initial commit项目已经有两个版本。14. commit 信息应该怎么写提交说明最好能直接描述这次修改做了什么。不太推荐git commit -m 修改也不推荐git commit -m 改一下这种提交记录多起来以后基本没有参考价值。更合适的是git commit -m Add mouse input support或者git commit -m Fix read return value handling中文也完全可以git commit -m 修复 read 返回值处理核心原则就是以后重新看到这条记录时能马上知道当时做了什么。15. 什么时候应该 commitcommit 不需要频繁到“改一行就提交一次”但也不应该整个项目做很久只提交一次。比较合适的方式是一个相对完整的小功能完成以后提交一次。例如初始化项目 ↓ commit 实现鼠标输入 ↓ commit 完善错误处理 ↓ commit 加入 select ↓ commit 加入 epoll ↓ commit最终历史Add epoll event monitoring Add select event monitoring Fix input error handling Add mouse input support Initial project这样的提交历史比较容易维护。16.git show查看某次提交查看最近一次提交git show如果想看某个具体 commitgit log --oneline先找到 ID例如42c89ea Fix read return value handling然后git show 42c89ea这样就可以看到这一次提交具体修改了哪些内容。17. 代码改坏了如何恢复Git 很重要的一个作用就是恢复代码。假设当前已经有一个正确 commit。之后又把int add(int a, int b) { return a b; }改坏了int add(int a, int b) { abcdefg; }但还没有 commit。先查看git diff如果确定这些修改全部不要了可以恢复git restore main.c这样main.c会恢复到最近一次 commit 中的状态。需要特别注意git restore main.c会丢弃这个文件尚未提交的修改。所以使用前最好先执行git diff确认当前修改确实不需要。18.git add错了怎么办假设执行了git add main.c但是后来发现这个文件暂时还不想提交。可以使用git restore --staged main.c这样只是把文件从暂存区拿出来。代码本身仍然保留。两个命令很容易混淆git restore --staged main.c表示撤销暂存。代码不会消失。而git restore main.c表示丢弃工作区修改恢复到最近一次 commit。后一个操作要谨慎很多。19. 多个文件不一定要一起提交假设当前修改了mouse.c mouse.h README.md其中mouse.c mouse.h属于一个功能修改。而README.md只是文档更新。可以分成两次 commitgit add mouse.c mouse.h git commit -m Fix mouse event handling再git add README.md git commit -m Update mouse usage documentation这样比全部塞进一个 commit 更清楚。所以一次 commit 最好只解决一件比较明确的事情。20..gitignore哪些文件不应该进 GitGit 一般适合保存源代码 头文件 脚本 配置文件 Makefile CMakeLists.txt README 文档例如.c .h .cpp .hpp .py .yml .yaml .json .md但很多编译产物、缓存文件和大型数据没有必要进入 Git。例如 C/C 项目*.o *.exe build/Python__pycache__/ *.pyc深度学习项目datasets/ outputs/ checkpoints/ *.pth *.pt这些可以写进.gitignore例如*.o *.exe build/ __pycache__/ *.pyc对于深度学习项目__pycache__/ *.pyc datasets/ outputs/ experiments/ checkpoints/ *.pth *.pt这样执行git add .时这些文件就不会被加入版本管理。21. 为什么最好一开始就写.gitignore假设项目目录project/ ├── datasets/ ├── checkpoints/ ├── outputs/ ├── train.py └── model.py其中datasets/可能有几十 GB模型权重model.pth也可能几百 MB。如果刚初始化仓库就直接git add .很容易把大量不应该跟踪的文件加入 Git。所以比较合理的顺序是创建项目 ↓ 创建 .gitignore ↓ git init ↓ git status ↓ git add . ↓ git commit22. 一个实际项目的完整 Git 流程假设现在有linux_input/ ├── input_test.c ├── Makefile └── README.md第一次初始化git init -b main查看git status加入git add .检查git diff --staged提交git commit -m Initial Linux input project现在历史是Initial Linux input project后来修复read()的返回值处理。修改完成git status查看git diff编译make测试正常以后git add input_test.c再次确认git diff --staged提交git commit -m Fix input event read handling历史变成Fix input event read handling Initial Linux input project之后加入select()。修改完成git status git diff编译测试make确认没问题git add . git diff --staged git commit -m Add select based input monitoring最终Add select based input monitoring Fix input event read handling Initial Linux input project这就是一个比较典型的 Git 使用方式。23. 几种常见状态怎么看UntrackedUntracked files表示新文件Git 目前没有跟踪。可以git add 文件名Modifiedmodified: main.c表示文件和最近一次 commit 相比发生了变化。查看差异git diffChanges to be committed表示修改已经进入暂存区下一次 commit 会包含这些内容。查看git diff --stagedworking tree cleannothing to commit, working tree clean表示当前工作区没有尚未提交的修改。这通常是开始开发下一个功能前比较理想的状态。24. 几个常见错误每次打开项目都重新git init没有必要。一个项目只需要初始化一次。以后直接git status即可。在子目录里初始化假设project/ ├── src/ ├── include/ └── Makefile应该在project/执行git init而不是在project/src/里面初始化。一修改完就直接 commit最好先看git status git diff再测试程序。确认没有问题以后git add . git diff --staged git commit没有.gitignore就直接git add .如果项目中包含build/ dataset/ output/ checkpoint/很容易把大量不必要文件加入 Git。把git add当成保存版本要记住git add只是放进暂存区。真正创建版本的是git commit随便执行git restore .这个命令会丢弃当前尚未提交的修改。最好先git diff确认之后再使用。25. 日常最常用的 Git 命令真正开始使用以后经常会用到的其实并不多。命令作用git init -b main初始化仓库git status查看仓库状态git diff查看未暂存修改git add .将当前修改加入暂存区git add file.c暂存指定文件git diff --staged查看暂存区内容git commit -m xxx创建 commitgit log --oneline查看提交历史git show查看最近一次提交git show ID查看指定提交git restore file.c丢弃文件当前修改git restore --staged file.c撤销暂存git config --global --list查看全局配置git --version查看 Git 版本26. 推荐形成的 Git 使用习惯每次开始开发git status修改代码。修改完成git status git diff然后编译或者测试。确认代码正常git add .最后检查git diff --staged提交git commit -m 描述本次修改偶尔查看历史git log --oneline所以整个 Git 本地工作流实际上可以浓缩成status ↓ 修改代码 ↓ diff ↓ 测试 ↓ add ↓ diff --staged ↓ commit27. 后续还需要学习什么掌握本文这些内容以后就已经可以用 Git 管理普通的个人项目了。接下来比较值得学习的是Git 本地基础 ↓ branch 分支 ↓ merge 合并 ↓ GitHub ↓ clone / push / pull ↓ 多人协作 ↓ 冲突处理其中分支非常重要。例如同一个项目需要同时尝试两个方案可以建立main ├── experiment-a └── experiment-b两个方案互不影响最后再决定是否合并。这部分适合在熟悉 commit 之后继续学习。总结Git 入门真正需要理解的其实并不复杂。首先记住三个区域工作区 ↓ git add 暂存区 ↓ git commit 本地仓库再掌握一条日常工作流git status git diff git add . git diff --staged git commit -m 修改说明 git log --oneline最后知道怎么撤销git restore --staged 文件名撤销暂存。git restore 文件名丢弃当前未提交修改。对于刚开始使用 Git 的人来说不需要一上来就记几十个命令。先把status diff add commit log restore这几个真正用熟就已经足够应付大部分日常开发。Git 的价值并不是“多学几个命令”而是让代码开发逐渐形成一个稳定的习惯每完成一个可靠的小阶段就留下一个清楚、可恢复的版本。