- 文档
- 教程
【免费下载链接】learnxinyminutes-docs
Code documentation written as code! How novel and totally my idea!
本篇技术指南以 zh-cn/git.md 为核心骨架,系统讲解 Git 分布式版本控制的底层架构(版本库、.git目录、索引、分支与头指针)以及从初始化、提交到合并、回滚的完整命令体系。读完本文,你将掌握 Git 的核心概念模型与 20+ 个高频命令的准确用法,并能在 learnxinyminutes-docs 这类以「代码即文档」为协作模式的开源仓库中,直接运用 Git 完成克隆、分支、提交与协作流程。
版本控制基础:为什么需要 Git
什么是版本控制
版本控制系统(Version Control System,VCS)是一种按照时间记录一个或多个文件更改情况的系统。它允许你回溯任意历史时刻的文件内容,回答「这个文件在什么时候、被谁、改了什么」等问题,是软件开发协作的基石。
集中式版本控制 vs 分布式版本控制
| 维度 | 集中式版本控制 | 分布式版本控制 |
|---|---|---|
| 核心功能 | 同步(synchronizing)、跟踪(tracking)、备份(backing up)文件 | 共享更改(sharing changes) |
| 更改标识 | 依赖中心服务器统一管理 | 每一次更改都有唯一的标识 ID |
| 结构约束 | 有预定的集中式结构 | 没有预定的结构,可灵活组织 |
分布式系统没有预定的结构:你可以用 Git 很轻松地实现 SVN 风格的集中式版本控制(即所有成员都向同一个远端仓库推送),也可以构建完全去中心化的协作网络。
为什么要使用 Git
- 可以离线工作:几乎所有操作(提交、分支、历史查看)都在本地完成,无需连接服务器;
- 与别人协同工作变得简单:通过 clone、pull、push 即可分享更改;
- 分支很轻松:创建、切换、删除分支的成本极低;
- 合并很容易:Git 提供强大的三方合并与冲突解决机制;
- 速度快且灵活:绝大多数操作基于本地文件系统与内容寻址,性能出色。
Git 架构:理解五个核心概念
在动手敲命令之前,先理解 Git 的对象模型。Git 的整个工作流都建立在这几个概念之上。
版本库(Repository)
版本库是一系列文件、目录、历史记录、提交记录和头指针(heads)的集合。可以把它视作一个「每个源代码文件都带有历史记录属性」的数据结构。一个 Git 版本库由.git目录和工作目录(working tree)两部分组成。
.git 目录(版本库的一部分)
.git目录包含所有的配置(config)、日志(logs)、分支信息(refs)、头指针(HEAD)等内容。它是版本库的元数据中枢,所有历史与状态信息都存放在这里。
注:
.git目录中的objects/存放 Git 对象(blob、tree、commit、tag),refs/存放分支与标签指针,HEAD文件记录当前活动分支。动手验证:在 learnxinyminutes-docs 仓库根目录执行ls -la .git即可观察这些目录。
工作目录(版本库的一部分)
工作目录就是版本库中的目录和文件,也就是你日常编辑代码时所在的目录。它是从版本库检出的、可被修改的实体文件集合。
索引(Index,属于 .git 目录)
索引就是 Git 中的暂存区(staging area)。它是把工作目录与 Git 版本库分割开的一层缓冲区:你在工作目录中的修改,只有先git add进入索引,才会被纳入下一次提交。这给开发者更大的灵活度,决定「究竟把什么内容送入版本库」。
提交(Commit)
一次 Git 提交就是对一组更改(或对工作目录操作)的快照。例如你添加了 5 个文件、删除了 2 个文件,这些变化会被写入同一个提交(快照)中;这个提交之后可以被决定是否推送到其他版本库。
分支(Branch)
分支本质上是一个指向你最后一次提交的指针。随着你不断提交,这个指针会自动更新,指向最新的提交。分支让并行开发、实验性功能与主线隔离成为可能。
标签(Tag)
标签是对历史中某个特定点的标记。通常人们用它标记发布版本(如 v1.0、v2.0)。与分支不同,标签一旦创建通常指向固定的提交,不随新提交移动。
头指针 HEAD 与头 head
- HEAD(头指针):指向当前分支的指针。一个版本库只有一个活动的HEAD。
- head(头):指向版本库中任意一个提交的指针。一个版本库可以有任意数量的 head。
简言之:HEAD 标识「你在哪个分支上」,head 则泛指「指向某提交的引用」。
Git 文件的三种状态
原文档虽未单独列出,但它是理解索引的关键补充,这里一并给出:
- 已修改(Modified):文件已被修改,但尚未提交到 Git 数据库;
- 已暂存(Staged):修改过的文件被标记,准备进入下一次提交快照;
- 已提交(Committed):文件已被安全地保存到 Git 数据库。
对应的工作流即:工作目录修改 → git add 暂存 → git commit 提交。
命令实战:从初始化到日常协作
以下命令均以 bash 演示,$为提示符,不参与实际执行。命令覆盖初始化、配置、帮助、状态查看、暂存、分支、远程协作、历史检索与回滚等完整生命周期。
初始化:git init
创建一个新的 Git 版本库。这个版本库的配置、存储等信息会被保存到.git文件夹中。
$ git init配置:git config
更改设置,可以是版本库级别的(不带--global)、系统级的或全局的(全局配置文件为~/.gitconfig)。提交前务必配置用户名与邮箱,它们会被写入每一次提交的作者信息。
# 输出、设置基本的全局变量 $ git config --global user.email $ git config --global user.name $ git config --global user.email "MyEmail@Zoho.com" $ git config --global user.name "My Name"补充常用配置:设置 GPG 签名密钥git config --global user.signingkey <KEY>(配合git commit -S使用);设置 grep 输出行号git config --global grep.lineNumber true。
帮助:git help
Git 内置了对每个命令非常详细的解释,可供快速查阅。
# 查找可用命令 $ git help # 查找所有可用命令 $ git help -a # 在文档中查找特定命令(git help <命令>) $ git help add $ git help commit $ git help init # 等价写法:git <命令> --help $ git add --help $ git commit --help $ git init --help忽略文件:.gitignore
有意让某些文件(或文件夹)不被 Git 跟踪,通常用于私有文件与临时文件,避免被提交到共享版本库。在 learnxinyminutes-docs 仓库根目录就存在一个 .gitignore 文件,其中列出了**/*~、**/*.swp、**/*.bak、**/.DS_Store等编辑器临时文件与系统文件模式:
$ echo "temp/" >> .gitignore $ echo "private_key" >> .gitignore查看状态:git status
显示索引文件(也就是当前工作空间)和当前 HEAD 指向的提交之间的不同。
# 显示分支、未跟踪文件、更改和其他不同 $ git status # 查看其他 git status 的用法 $ git help status暂存:git add
添加文件到暂存区(索引)。如果你不使用git add将文件添加进去,那么这些文件也不会被包含进之后的提交之中!注意:git add只是把文件加入暂存区,并不会提交到版本库。
# 添加当前工作目录中的一个文件 $ git add HelloWorld.java # 添加一个子目录中的文件 $ git add /path/to/file/HelloWorld.c # 支持正则表达式(通配符) $ git add ./*.java # 添加工作目录中的所有更改到暂存区 $ git add -A分支管理:git branch
管理分支,可以通过下列命令对分支进行增删改查。
# 查看所有的分支和远程分支 $ git branch -a # 创建一个新的分支 $ git branch myNewBranch # 删除一个分支 $ git branch -d myBranch # 重命名分支(git branch -m <旧名称> <新名称>) $ git branch -m myBranchName myNewBranchName # 编辑分支的介绍 $ git branch myBranchName --edit-description标签管理:git tag
管理标签(通常用于标记发布版本):
# 列出所有标签 $ git tag # 创建带注释的标签(-m 指定标签信息并随标签存储; # 若不指定,Git 会启动编辑器供你输入) $ git tag -a v2.0 -m 'my version 2.0' # 显示标签信息(含打标签者、打标签日期、注释信息及对应提交) $ git show v2.0 # 推送单个标签到远端 $ git push origin v2.0 # 推送所有标签到远端 $ git push origin --tags检出:git checkout
将当前工作空间更新到索引所标识的或某一特定的工作空间。
# 检出一个版本库(默认更新到 master 分支) $ git checkout # 检出到一个特定的分支 $ git checkout branchName # 新建一个分支并且切换过去,相当于 "git branch <名字>; git checkout <名字>" $ git checkout -b newBranch克隆:git clone
将已有的版本库拷贝到另一个目录中,同时为该版本库中的每个分支创建远程跟踪分支(remote-tracking branches),这样你就可以在新版本库中推送到远程分支。
# 克隆 learnxinyminutes-docs 仓库 $ git clone https://github.com/adambard/learnxinyminutes-docs.git # 浅克隆:只拉取最新快照,克隆更快 $ git clone --depth 1 https://github.com/adambard/learnxinyminutes-docs.git # 只克隆特定分支 $ git clone -b master-cn https://github.com/adambard/learnxinyminutes-docs.git --single-branch结合本仓库的协作实践:CONTRIBUTING.md 展示了用 Git 搭建本地站点的完整流程——先git clone站点仓库,再把本文档仓库克隆到站点源码目录下,然后安装依赖并运行本地服务器。这正是 clone + 协作型仓库的典型真实用例。
提交:git commit
将当前索引的更改保存为一个新的提交,这个提交包含用户做出的更改与提交信息。
# 提交时附带提交信息 $ git commit -m "Added multiplyNumbers() function to HelloWorld.c" # 使用 GPG 签名提交(需预先通过 git config --global user.signingkey 设置密钥, # 例如 git config --global user.signingkey 5173AAD5) $ git commit -S -m "signed commit message" # 自动暂存已修改或已删除的文件(不包括新文件)后提交 $ git commit -a -m "Modified foo.php and removed bar.php" # 修改上一次提交(用一条全新提交替换之前的提交) $ git commit --amend -m "Correct message"差异比较:git diff
显示工作目录、索引和提交之间某个文件的差异。
# 显示工作目录和索引的不同 $ git diff # 显示索引和最近一次提交的不同 $ git diff --cached # 显示工作目录和最近一次提交的不同 $ git diff HEAD快速检索:git grep
在版本库中快速查找内容(基于正则表达式搜索历史与当前内容)。可选配置:
# 在 grep 搜索结果中显示行号 $ git config --global grep.lineNumber true # 使搜索结果可读性更好(分组、标题、行号) $ git config --global alias.g "grep --break --heading --line-number"# 在所有 java 文件中查找 "variableName" $ git grep 'variableName' -- '*.java' # 搜索同时包含 "arrayListName" 且包含 "add" 或 "remove" 的所有行 $ git grep -e 'arrayListName' --and \( -e add -e remove \)历史记录:git log
显示这个版本库的所有提交。
# 显示所有提交 $ git log # 仅显示提交信息与引用(单行显示) $ git log --oneline # 仅显示某几条提交信息 $ git log -n 10 # 仅显示合并提交 $ git log --merges # 以 ASCII 图形显示所有提交的分支拓扑 $ git log --graph合并:git merge
将外部的提交合并到当前分支中。
# 将其他分支合并到当前分支 $ git merge branchName # 合并时总是生成一个新的合并提交(--no-ff) $ git merge --no-ff branchName移动与重命名:git mv
重命名或移动一个文件。
# 重命名文件 $ git mv HelloWorld.c HelloNewWorld.c # 移动文件 $ git mv HelloWorld.c ./new/path/HelloWorld.c # 强制重命名或移动(目标文件已存在,将被覆盖) $ git mv -f myFile existingFile拉取:git pull
从远端版本库拉取更改并合并到当前分支。
# 从远端 origin 的 master 分支更新版本库 # git pull <远端> <分支> $ git pull origin master # 默认情况下,git pull 会将当前分支与其远程跟踪分支的新更改合并 $ git pull # 拉取远端分支更改后用 rebase 方式重放本地分支提交 # 等价于 "git fetch <远端> <分支>; git rebase <远端>/<分支>" $ git pull origin master --rebase推送:git push
把本地更改推送到远端版本库并合并到远端分支。
# 把本地分支更新到远端 origin 的 master 分支上 # git push <远端> <分支> # git push 相当于 git push origin master $ git push origin master # 默认情况下,git push 会把当前分支推送到其远程跟踪分支 $ git push # 用 -u 把当前本地分支与远端分支关联起来 $ git push -u origin master # 之后从同一本地分支推送时,直接使用快捷命令: $ git push暂存工作:git stash
stash 会把工作目录的脏状态(未提交的修改)保存到一个未完成更改的栈上,你可以在任何时候重新应用它。典型场景:你在本地做了修改,但想git pull拉取远端更新——由于存在未提交更改无法直接 pull,此时先 stash 保存更改,pull 完成后再恢复。
$ git stash Saved working directory and index state \ "WIP on master: 049d078 added the index file" HEAD is now at 049d078 added the index file (To restore them type "git stash apply")现在可以放心 pull 了:
$ git pull确认一切正常:
$ git status # On branch master nothing to commit, working directory clean查看已暂存的改动块(以栈方式存储,LIFO,最近的改动在最上面):
$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log把改动从栈中弹出恢复:
$ git stash pop # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: index.html # modified: lib/simplegit.rbgit stash apply与git stash pop效果相同(前者不弹出栈顶条目)。恢复后即可继续工作。
变基:git rebase(谨慎使用)
将一个分支上所有的提交历史都应用到另一个分支上。不要在一个已经公开的远端分支上使用 rebase——因为 rebase 会重写提交历史,导致远端与本地历史不一致,影响协作者。
# 将 experimentBranch 应用到 master 上面 # git rebase <basebranch> <topicbranch> $ git rebase master experimentBranch重置:git reset(谨慎使用)
将当前 HEAD 复位到一个特定的状态。这样可以撤销 merge、pull、commits、add 等操作。这是个很强大的命令,但使用时一定要清楚其所产生的后果。
# 使暂存区恢复到上次提交时的状态,不改变现在的工作目录 $ git reset # 使暂存区恢复到上次提交时的状态,并覆盖现在的工作目录 $ git reset --hard # 将当前分支恢复到某次提交,不改变现在的工作目录 #(工作目录中所有的改变仍然存在) $ git reset 31f2bb1 # 将当前分支恢复到某次提交,覆盖现在的工作目录, # 并删除所有未提交的改变和该提交之后的所有提交 $ git reset --hard 31f2bb1引用日志:git reflog(谨慎使用)
reflog 会列出你在给定时间段内(默认 90 天)执行过的大部分 Git 命令记录。这给了你撤销任何出错的 Git 命令的机会(例如一次 rebase 弄坏了你的应用):
# 列出 rebase 相关的所有 Git 命令记录 $ git reflog 38b323f HEAD@{0}: rebase -i (finish): returning to refs/heads/feature/add_git_reflog 38b323f HEAD@{1}: rebase -i (pick): Clarify inc/dec operators 4fff859 HEAD@{2}: rebase -i (pick): Update java.html.markdown 34ed963 HEAD@{3}: rebase -i (pick): [yaml/en] Add more resources (#1666) ed8ddf2 HEAD@{4}: rebase -i (pick): pythonstatcomp spanish translation (#1748) 2e6c386 HEAD@{5}: rebase -i (start): checkout 02fb96d选择要重置到的位置(上例中为2e6c386或HEAD@{5}),执行git reset --hard HEAD@{5}将仓库重置到该引用处,之后可以重新开始 rebase 或保持现状。
撤销提交:git revert
revert 用于撤销某次提交。注意不要与 reset 混淆:reset 是把项目状态恢复到之前的某个时间点,而 revert 会新增一个与被撤销提交相反的提交,从而在保留历史的前提下撤销该提交的效果——这是回滚已推送提交的安全方式。
# 撤销指定的提交 $ git revert <commit>移除文件:git rm
与 git add 相反,git rm 从工作空间中移除某个文件(同时从索引中删除)。
# 移除 HelloWorld.c $ git rm HelloWorld.c # 移除子目录中的文件 $ git rm /pather/to/the/file/HelloWorld.c追溯作者:git blame
查看代码特定部分的修改历史,找出某一行最后一次被谁修改。
# 找出某文件每行的最后修改作者 $ git blame google_python_style.vim b88c6a1b (Google Python team 2019-12-30 13:45:23 -0800 12) " See the License for the specific language governing permissions and b88c6a1b (Google Python team 2019-12-30 13:45:23 -0800 13) " limitations under the License. 222e6da8 (mshields@google.com 2010-11-29 20:32:06 +0000 15) " Indent Python in the Google way. 222e6da8 (mshields@google.com 2010-11-29 20:32:06 +0000 17) setlocal indentexpr=GetGooglePythonIndent(v:lnum)在 learnxinyminutes-docs 中的实战演练
将以上知识落到本仓库的真实场景中:
- 克隆仓库:
git clone https://github.com/adambard/learnxinyminutes-docs.git(需要贡献时,fork 后克隆你自己的副本)。 - 查看状态与分支:
git status查看工作区是否干净;git branch -a查看本地与远端分支。 - 忽略临时文件:仓库根目录的 .gitignore 已屏蔽编辑器临时文件与系统文件,提交前用
git status确认没有误跟踪文件。 - 检查历史:
git log --oneline --graph查看提交拓扑,git blame定位某行文档的贡献者。 - 本地构建验证:按 CONTRIBUTING.md 的流程,用
git clone将站点仓库与本文档仓库组合,运行python build.py后本地预览,再通过 push 提交你的翻译或文档改进。
小结
本文从版本控制的概念出发,完整梳理了 Git 的版本库、.git目录、工作目录、索引、提交、分支、标签与 HEAD 架构,并逐一讲解了 init、config、help、status、add、branch、tag、checkout、clone、commit、diff、grep、log、merge、mv、pull、push、stash、rebase、reset、reflog、revert、rm、blame 等命令的语法与使用场景。建议配合 zh-cn/git.md 与英文原版 git.md 对照阅读,在 learnxinyminutes-docs 仓库中亲手运行一遍上述命令,即可将 Git 从「概念」转化为「肌肉记忆」。
- 文档
- 教程
【免费下载链接】learnxinyminutes-docs
Code documentation written as code! How novel and totally my idea!
相关推荐
Git 分布式版本控制实战指南:从 learnxinyminutes-docs 仓库掌握版本管理与日常命令
Git 分布式版本控制实战指南:从 learnxinyminutes docs 仓库掌握版本管理与日常命令 本指南基于 learnxinyminutes doc
文档教程learnxinyminutes-docs 中的 Git 完全指南:分布式版本控制原理与常用命令实战
learnxinyminutes docs 中的 Git 完全指南:分布式版本控制原理与常用命令实战 Git 是一种分布式版本控制与源代码管理系统,通过为项目保
文档教程learnxinyminutes-docs 中的 Git 全指南:分布式版本控制概念、架构与命令行实战
learnxinyminutes docs 中的 Git 全指南:分布式版本控制概念、架构与命令行实战 Git 是当今最主流的分布式版本控制与源码管理系统,本仓
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考