Git 学习笔记
引言
Git 是目前最流行的分布式版本控制系统,最初由 Linus Torvalds 开发,用于管理 Linux 内核代码。Git 可以高效地处理从小型到大型的项目版本管理,为开发者提供了强大的工具来跟踪代码变化、协同工作和维护项目历史。
Git 的基本概念
什么是 Git
- Git 是一个开源的、免费的、分布式的版本控制系统。
- 它能够跟踪文件的变化,记录历史版本,允许多人协作开发。
集中式 vs 分布式版本控制系统
-
集中式版本控制系统(CVCS):
- 例子:SVN、CVS。
- 所有版本数据都存储在中央服务器,开发者从服务器检出代码并提交修改。
- 缺点:中央服务器的单点故障风险,网络依赖性高。
-
分布式版本控制系统(DVCS):
- 例子:Git、Mercurial。
- 每个开发者的本地仓库都是完整的,包含了项目的所有历史版本。
- 优点:本地操作速度快,离线也能进行提交、查看历史等操作。
Git 的优势
- 快速:本地操作,无需联网,速度极快。
- 简单设计:容易上手,功能强大。
- 完全分布式:每个开发者都有完整的版本库,便于协作和备份。
- 强大的分支管理:创建、切换、合并、删除分支都非常快捷。
Git 的工作原理
Git 的数据存储模型
-
快照(Snapshot)模型:
- Git 并不记录文件之间的差异(增量),而是对文件的完整内容进行快照。
- 每次提交时,Git 会对所有文件拍摄一次快照,存储当前状态。
- 如果文件未修改,Git 不会再次存储该文件,而是引用之前的文件。
Git 的三个区域
Git 的操作主要涉及三个区域:
工作目录(Working Directory)
- 开发者实际进行代码编辑的地方。
- 包含了项目的所有文件。
暂存区(Staging Area/Index)
- 一个临时保存更改的区域,保存将要提交的快照。
- 开发者可以选择哪些修改被提交。
本地仓库(Repository)
- 存储所有的提交历史和对象,是一个完整的数据库。
- 包含了所有的提交对象、树对象、文件对象等。
工作流程示意图:
- 工作目录:修改文件。
git add
:将修改添加到暂存区。git commit
:将暂存区的内容提交到本地仓库。
Git 对象详解
Git 使用四种对象来存储数据:
Blob(文件对象)
- Blob:存储文件的内容,不包含文件名和其他元数据。
- 每个 Blob 对象都有一个唯一的 SHA-1 哈希值。
Tree(树对象)
- Tree:表示目录结构,存储文件名、文件权限以及对 Blob 和子 Tree 的引用。
- 通过 Tree 对象,Git 能够还原文件的目录结构。
Commit(提交对象)
- Commit:表示一次提交操作,包含以下信息:
- 指向一个 Tree 对象,代表项目在提交时的快照。
- 父提交对象的引用(一个或多个)。
- 提交者信息(姓名、邮箱)。
- 提交时间戳。
- 提交信息(日志)。
Tag(标签对象)
- Tag:为特定的提交打标签,用于标记重要的版本(如发布版本)。
- 标签可以是轻量标签或附注标签。
Git 基本操作
初始化仓库
git init
- 在当前目录下创建一个新的 Git 仓库,生成
.git
目录。
克隆仓库
git clone <repository-url> [directory]
- 从远程仓库克隆代码到本地。
- 可指定目录名称。
查看状态
git status
- 显示当前工作目录的状态,包括修改、暂存和未跟踪的文件。
添加文件到暂存区
git add <file>
git add .
- 将文件的修改添加到暂存区。
git add .
添加当前目录下的所有更改。
提交修改
git commit -m "提交信息"
git commit -a -m "提交信息"
- 将暂存区的内容提交到本地仓库。
-a
选项自动添加所有已跟踪文件的修改。
查看历史记录
git log
git log --oneline --graph --all
- 显示提交历史。
--oneline
:简洁模式。--graph
:以图形方式显示分支和合并历史。
查看差异
git diff
git diff <branch1> <branch2>
git diff <commit1> <commit2>
- 比较工作目录和暂存区的差异。
- 比较两个分支或提交之间的差异。
Git 分支管理
创建与切换分支
-
创建分支:
git branch <branch-name>
-
切换分支:
git checkout <branch-name>
-
创建并切换到新分支:
git checkout -b <branch-name>
合并分支
git merge <branch-name>
-
将指定分支的修改合并到当前分支。
-
合并策略:
- Fast-forward(快进合并):当当前分支没有新提交时,直接将指针移动到目标分支。
- 三路合并(3-way merge):当两个分支都有新的提交时,Git 会创建一个新的合并提交。
解决合并冲突
- 当 Git 无法自动合并文件时,会提示冲突,需要手动解决。
解决步骤:
- 查看冲突文件:
git status
。 - 编辑冲突文件:找到标记
<<<<<<
,======
,>>>>>>
的冲突区域。 - 手动修改:保留需要的代码,删除冲突标记。
- 添加到暂存区:
git add <file>
。 - 提交合并结果:
git commit
。
删除分支
-
删除本地分支:
git branch -d <branch-name>
-
强制删除未合并的分支:
git branch -D <branch-name>
Git 标签管理
标签的类型
-
轻量标签(Lightweight Tag):
- 只是对某个提交的引用,不包含额外信息。
- 类似于分支,但不会移动。
-
附注标签(Annotated Tag):
- 是一个完整的对象,包含标签名、邮箱、日期、消息等元数据。
- 存储在 Git 数据库中,可以被推送到远程。
创建标签
-
创建附注标签:
git tag -a <tag-name> -m "标签信息"
-
创建轻量标签:
git tag <tag-name>
-
为特定提交打标签:
git tag -a <tag-name> <commit-hash> -m "标签信息"
查看与操作标签
-
列出所有标签:
git tag
-
查看标签信息:
git show <tag-name>
-
推送标签到远程:
git push origin <tag-name> git push origin --tags # 推送所有标签
-
删除本地标签:
git tag -d <tag-name>
-
删除远程标签:
git push origin --delete <tag-name>
Git 远程仓库操作
添加远程仓库
git remote add <name> <url>
- 添加一个新的远程仓库,通常默认名称为
origin
。
查看远程仓库信息
git remote -v
- 显示远程仓库的详细信息(名称和 URL)。
推送与拉取
-
推送本地分支到远程仓库:
git push <remote-name> <branch-name>
-
拉取远程分支并合并到本地:
git pull <remote-name> <branch-name>
-
仅获取远程更新(不合并):
git fetch <remote-name>
远程分支管理
-
查看远程分支:
git branch -r
-
创建跟踪远程分支的本地分支:
git checkout -b <local-branch> <remote-name>/<remote-branch>
-
删除远程分支:
git push <remote-name> --delete <branch-name>
Git 的回滚与恢复
撤销未提交的修改
-
撤销工作目录的修改:
git checkout -- <file>
- 将文件恢复到暂存区或最后一次提交的状态。
-
撤销暂存区的修改:
git reset HEAD <file>
- 将文件从暂存区移除,但保留在工作目录中。
撤销已提交的修改
-
回退到上一个提交(保留修改):
git reset --soft HEAD~1
-
回退到指定提交(丢弃修改):
git reset --hard <commit-hash>
-
注意:
--hard
选项会丢弃所有未提交的修改,慎用。
使用 reflog 恢复
-
查看操作记录:
git reflog
- 记录了所有的 HEAD 变动,包括被删除的分支和回退的提交。
-
恢复误删的提交或分支:
git reset --hard <reflog-id>
Git 的工作流程与最佳实践
GitFlow 工作流程
-
GitFlow 是一种基于 Git 的工作流程,适用于大型项目。
-
主要分支:
- master:存储正式发布的版本。
- develop:集成了所有开发者的最新开发代码。
-
辅助分支:
- feature/:功能分支,从 develop 分支创建,用于开发新功能。
- release/:发布分支,用于准备新的生产发布版本。
- hotfix/:热修复分支,从 master 分支创建,用于修复紧急 Bug。
提交信息的编写
-
良好的提交信息有助于项目的维护和协作。
-
提交信息格式建议:
类型(范围): 简要说明 详细描述(可选) 关联的 Issue 或任务(可选)
-
类型(常用动词):
- feat:新增功能
- fix:修复 Bug
- docs:文档更新
- style:代码格式调整(不影响功能)
- refactor:代码重构(既不是新增功能,也不是修 Bug)
- test:增加或修改测试
- chore:构建过程或辅助工具的变动
使用 .gitignore
-
.gitignore 文件用于告诉 Git 忽略哪些文件或目录。
-
常见的忽略规则:
- 操作系统文件:如
.DS_Store
、Thumbs.db
。 - 编译输出:如
*.class
、*.o
、*.exe
。 - 日志文件:如
*.log
。 - 临时文件:如
*.tmp
、*.swp
。 - 第三方库:如
node_modules/
、vendor/
。
- 操作系统文件:如
-
示例:
# 忽略所有的 .log 文件 *.log # 忽略临时文件 *.tmp *.swp # 忽略 node_modules 目录 node_modules/ # 忽略环境配置文件 .env
常见问题与注意事项
-
频繁提交:
- 小步提交,便于跟踪和回溯。
- 每次提交只包含相关的修改。
-
提交前更新:
- 在开始新功能开发前,先拉取远程仓库的最新代码,避免冲突。
-
处理合并冲突:
- 及时解决冲突,确保代码的正确性。
- 使用合适的工具(如
git mergetool
)辅助解决冲突。
-
避免提交大文件或敏感信息:
- 使用
.gitignore
忽略不需要的文件。 - 如果误提交了敏感信息,及时删除并更改相关密码或密钥。
- 使用
-
使用分支进行开发:
- 不要在主分支(master/main)上直接开发。
- 为每个新功能或修复创建独立的分支。
-
慎用破坏性命令:
- 如
git reset --hard
、git clean
,可能导致数据丢失。 - 在执行前确保没有重要的未提交修改。
- 如
附录:常用 Git 命令速查
-
仓库初始化与克隆
git init # 初始化仓库 git clone <url> # 克隆远程仓库
-
状态与日志
git status # 查看状态 git log # 查看提交日志 git log --oneline # 简洁的提交日志 git reflog # 查看操作记录
-
添加与提交
git add <file> # 添加文件到暂存区 git add . # 添加所有修改 git commit -m "消息" # 提交暂存区的修改 git commit -am "消息" # 添加并提交已跟踪文件的修改
-
分支操作
git branch # 列出本地分支 git branch -r # 列出远程分支 git branch <branch> # 创建新分支 git checkout <branch> # 切换分支 git checkout -b <branch> # 创建并切换到新分支 git merge <branch> # 合并指定分支到当前分支 git branch -d <branch> # 删除本地分支 git push origin --delete <branch> # 删除远程分支
-
远程操作
git remote -v # 查看远程仓库 git remote add <name> <url> # 添加远程仓库 git fetch <remote> # 获取远程更新 git pull <remote> <branch> # 拉取并合并远程分支 git push <remote> <branch> # 推送本地分支到远程 git push --tags # 推送所有标签
-
标签管理
git tag # 列出所有标签 git tag -a <tag> -m "信息" # 创建附注标签 git show <tag> # 查看标签信息 git push origin <tag> # 推送标签到远程 git tag -d <tag> # 删除本地标签 git push origin --delete <tag> # 删除远程标签
-
撤销与恢复
git checkout -- <file> # 撤销工作目录的修改 git reset HEAD <file> # 取消暂存区的修改 git reset --soft HEAD~1 # 回退提交,保留修改 git reset --hard <commit> # 回退到指定提交,丢弃修改 git revert <commit> # 反转指定提交
总结:
通过以上详细的笔记,对 Git 的工作原理、基本操作、分支管理、标签管理、远程操作以及最佳实践有了深入的了解。掌握 Git,不仅可以提高个人的开发效率,还能在团队协作中如鱼得水。建议在实践中多多使用这些命令,遇到问题时及时查阅文档或求助,以巩固和深化对 Git 的理解。