Appearance
什么是 Git
1. 知识点大纲
1.1 概述
Git 是目前世界上最先进的分布式版本控制系统(Distributed Version Control System,简称 DVCS)。它由 Linus Torvalds(Linux 之父)于 2005 年创建,最初是为了更好地管理 Linux 内核开发而设计。
在软件开发过程中,我们经常面临这样的问题:
- 代码修改后发现问题,想回退到之前的版本
- 多人协作开发,代码容易冲突
- 需要同时维护多个版本(如开发版、稳定版)
Git 就像是一个"时光机器",它可以记录文件的每一次修改历史,让你随时可以回到过去某个时间点的状态。想象一下,你在写一篇重要的文档,每次修改前都复制一份备份,最终你会得到很多个版本:文档_v1.doc、文档_v2.doc、文档_最终版.doc、文档_最终版_改.doc...Git 就是帮你自动管理这些版本的工具,而且比手动复制更智能、更高效。
为什么选择 Git?
- 免费开源:任何人都可以免费使用和修改
- 速度快:几乎所有操作都在本地完成,无需联网
- 分布式:每个人都有完整的版本库,不依赖中央服务器
- 强大分支:创建和合并分支非常快速和简单
- 安全性高:使用 SHA-1 哈希算法确保数据完整性
1.2 基本概念
语法
Git 的基本命令语法格式:
bash
git <command> [<args>]常用命令一览:
| 命令 | 说明 | 示例 |
|---|---|---|
git init | 初始化仓库 | git init |
git clone | 克隆远程仓库 | git clone https://github.com/user/repo.git |
git add | 添加文件到暂存区 | git add filename.txt |
git commit | 提交更改 | git commit -m "提交说明" |
git status | 查看状态 | git status |
git log | 查看提交历史 | git log --oneline |
git diff | 查看差异 | git diff |
git branch | 分支管理 | git branch feature |
git checkout | 切换分支 | git checkout feature |
git merge | 合并分支 | git merge feature |
git pull | 拉取远程更新 | git pull origin main |
git push | 推送到远程 | git push origin main |
语义
版本控制(Version Control)
版本控制是一种记录文件内容变化的方式,让你能够查阅特定版本的修订情况。它不仅适用于代码,也适用于任何类型的文件。
分布式 vs 集中式
集中式版本控制系统(如 SVN)有一个单一的集中管理的服务器,保存所有文件的修订版本。团队成员通过客户端连接到这台服务器,获取最新的文件或提交更新。
分布式版本控制系统(如 Git)中,客户端不只是提取最新版本的文件快照,而是把原始代码仓库完整地镜像下来。这样任何一处协同工作用的服务器发生故障,都可以用任何一个镜像出来的本地仓库恢复。
集中式架构:
┌─────────────┐
│ 中央服务器 │
│ (唯一仓库) │
└──────┬──────┘
│
┌────┴────┐
│ │
┌─▼──┐ ┌──▼─┐
│客户端│ │客户端│
└────┘ └────┘
分布式架构:
┌────────┐ ┌────────┐
│ 远程仓库 │◄───►│ 本地仓库 │
└────────┘ └────┬───┘
│
┌─────┴─────┐
│ │
┌──▼──┐ ┌───▼──┐
│ 开发者A │ │ 开发者B │
└─────┘ └──────┘仓库(Repository)
仓库是 Git 管理项目的核心概念,它包含:
- 项目文件(代码、文档等)
- 完整的修改历史
- 配置信息
规范
提交信息规范
遵循约定式提交(Conventional Commits)规范:
bash
<type>(<scope>): <subject>
<body>
<footer>type 类型说明:
| 类型 | 说明 |
|---|---|
feat | 新功能 |
fix | 修复 bug |
docs | 文档变更 |
style | 代码格式(不影响代码运行的变动) |
refactor | 重构(既不是新增功能,也不是修改 bug) |
test | 增加测试 |
chore | 构建过程或辅助工具的变动 |
示例:
bash
feat(user): 添加用户登录功能
- 实现用户名密码登录
- 添加登录状态保持
- 集成第三方登录
Closes #123分支命名规范:
bash
feature/功能名称 # 新功能分支
bugfix/问题描述 # bug 修复分支
hotfix/紧急修复 # 紧急修复分支
release/版本号 # 发布分支1.3 原理深度解析
Git 的存储机制
Git 的核心是一个简单的键值对数据库。你可以向该数据库插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回内容。
Git 对象模型:
Git 有四种主要的对象类型:
Blob(二进制大对象)
- 存储文件内容
- 不包含文件名,只包含内容
Tree(树对象)
- 存储目录结构
- 包含文件名和权限信息
- 指向 blob 或其他 tree
Commit(提交对象)
- 存储提交信息
- 指向一个 tree(项目根目录)
- 包含父提交、作者、时间等信息
Tag(标签对象)
- 存储标签信息
- 指向一个 commit
Commit 对象
├── tree (根目录)
│ ├── blob (文件1内容)
│ ├── blob (文件2内容)
│ └── tree (子目录)
│ └── blob (文件3内容)
├── parent (父提交)
├── author (作者)
├── committer (提交者)
└── message (提交信息)SHA-1 哈希:
每个 Git 对象都有一个 40 位的 SHA-1 哈希值作为唯一标识:
bash
# 查看对象的哈希值
$ echo "Hello, Git" | git hash-object --stdin
8d01495a85a9f6e2f8e9bc4b5a5c5a5a5a5a5a5aGit 的三个工作区域
Git 有三个重要的工作区域,理解它们对于掌握 Git 至关重要:
┌─────────────────────────────────────────────────────────┐
│ Git 工作流程 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ git add ┌──────────┐ git commit │
│ │ 工作目录 │ ───────────► │ 暂存区 │ ───────────► │
│ │ (Working) │ │ (Staging)│ │
│ └──────────┘ └──────────┘ │
│ ▲ │
│ │ ┌──────────┐ │
│ │ git checkout │ 仓库 │ │
│ └────────────────────│(Repository)│◄─────────────┘
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────┘工作目录(Working Directory)
- 你实际编辑文件的地方
- 包含项目的当前状态
- 可以是新建、修改或删除的文件
暂存区(Staging Area / Index)
- 下次提交的文件快照
- 使用
git add命令将文件添加到这里 - 是一个文件,保存了即将提交的文件信息
仓库(Repository)
- 保存所有提交的历史记录
- 使用
git commit将暂存区内容保存到这里 - 每个提交都是一个完整的项目快照
Git 分支的本质
Git 的分支本质上仅仅是指向提交对象的可变指针。Git 默认分支名称是 main 或 master。
bash
# 分支只是指向某个 commit 的指针
main ──► commit A ──► commit B ──► commit C
▲
feature ──────────────────────────────┘创建分支就是创建一个新的指针:
bash
$ git branch feature
# 创建了一个名为 feature 的新指针,指向当前 commitHEAD 是一个特殊指针,指向当前所在的本地分支:
bash
$ git checkout feature
# HEAD 现在指向 feature 分支
HEAD ──► feature ──► commit C
main ──► commit C1.4 常见错误与踩坑点
错误 1:提交了敏感信息
错误表现:
bash
$ git log
commit abc123...
Author: user@example.com
Date: Mon Jan 1 10:00:00 2024 +0800
添加配置文件
包含数据库密码和 API 密钥产生原因:
- 不小心将包含密码、密钥等敏感信息的文件提交到仓库
- 没有使用
.gitignore文件过滤敏感文件
解决方案:
- 使用
.gitignore文件排除敏感文件:
bash
# .gitignore
.env
config/local.php
*.key
*.pem- 如果已经提交,需要从历史记录中彻底删除:
bash
# 使用 git filter-branch 删除敏感文件
$ git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch path/to/sensitive-file' \
--prune-empty --tag-name-filter cat -- --all
# 强制推送(谨慎使用)
$ git push origin --force --all- 更好的方法是使用
git-secrets或pre-commit钩子自动检测:
bash
# 安装 git-secrets
$ brew install git-secrets
# 在仓库中设置
$ git secrets --install
$ git secrets --register-aws错误 2:错误的提交信息
错误表现:
bash
$ git log --oneline
abc1234 修复 bug
def5678 更新
ghi9012 修改文件产生原因:
- 提交信息过于简单,无法理解修改内容
- 没有遵循提交信息规范
解决方案:
- 修改最后一次提交信息:
bash
$ git commit --amend -m "fix(user): 修复用户登录验证失败的问题
- 修复密码验证逻辑错误
- 添加输入验证
Closes #456"- 修改历史提交信息:
bash
# 使用交互式 rebase
$ git rebase -i HEAD~3
# 将要修改的 commit 前面的 pick 改为 reword
reword abc1234 修复 bug
pick def5678 更新
pick ghi9012 修改文件
# 保存后会打开编辑器修改每个提交信息错误 3:在错误的分支上工作
错误表现:
bash
$ git status
On branch main
Changes to be committed:
new file: feature.php产生原因:
- 忘记创建新分支就开始开发
- 忘记切换到正确的分支
解决方案:
- 如果还没有提交,可以轻松转移:
bash
# 创建新分支并转移修改
$ git checkout -b feature/new-feature- 如果已经提交,使用 cherry-pick:
bash
# 在正确的分支上应用提交
$ git checkout feature/new-feature
$ git cherry-pick abc1234
# 删除错误分支上的提交
$ git checkout main
$ git reset --hard HEAD~1错误 4:合并冲突处理不当
错误表现:
bash
$ git merge feature
Auto-merging config.php
CONFLICT (content): Merge conflict in config.php
Automatic merge failed; fix conflicts and then commit the result.产生原因:
- 多人修改同一文件的同一位置
- 分支差异过大
解决方案:
- 查看冲突文件:
bash
$ git status
Unmerged paths:
both modified: config.php- 手动解决冲突:
php
<?php
// config.php
<<<<<<< HEAD
$db_host = 'localhost';
$db_name = 'production';
=======
$db_host = '127.0.0.1';
$db_name = 'development';
>>>>>>> feature修改为:
php
<?php
// config.php
$db_host = 'localhost';
$db_name = 'production';- 标记为已解决:
bash
$ git add config.php
$ git commit -m "merge: 合并 feature 分支,解决配置文件冲突"错误 5:误删文件或提交
错误表现:
bash
$ git rm important.php
$ git commit -m "删除文件"
# 发现删错了产生原因:
- 操作失误
- 没有确认就执行删除操作
解决方案:
- 如果还没有提交:
bash
# 恢复误删的文件
$ git checkout -- important.php- 如果已经提交:
bash
# 查看历史找到删除前的提交
$ git log --oneline
# 恢复特定文件
$ git checkout abc1234 -- important.php
$ git commit -m "restore: 恢复误删的 important.php"- 使用
git reflog找回丢失的提交:
bash
$ git reflog
abc1234 HEAD@{0}: reset: moving to HEAD~1
def5678 HEAD@{1}: commit: 重要的提交
# 恢复到之前的状态
$ git reset --hard def56781.5 常见应用场景
场景 1:初始化新项目
场景描述: 开始一个全新的项目,需要使用 Git 进行版本控制。
使用方法:
- 创建项目目录
- 初始化 Git 仓库
- 创建初始文件
- 进行首次提交
示例代码:
bash
# 创建项目目录
$ mkdir my-project
$ cd my-project
# 初始化 Git 仓库
$ git init
Initialized empty Git repository in /path/my-project/.git/
# 创建 .gitignore 文件
$ cat > .gitignore << 'EOF'
/vendor/
/node_modules/
.env
.DS_Store
*.log
EOF
# 创建 README 文件
$ echo "# My Project" > README.md
# 添加所有文件到暂存区
$ git add .
# 查看状态
$ git status
On branch main
No commits yet
Changes to be committed:
new file: .gitignore
new file: README.md
# 进行首次提交
$ git commit -m "feat: 初始化项目
- 添加 .gitignore 配置
- 添加 README 文件"
[main (root-commit) abc1234] feat: 初始化项目
2 files changed, 10 insertions(+)
create mode 100644 .gitignore
create mode 100644 README.md
# 查看提交历史
$ git log --oneline
abc1234 feat: 初始化项目运行结果分析:
git init创建了.git目录,存储所有版本信息.gitignore文件排除了不需要版本控制的文件- 首次提交建立了项目的基础结构
场景 2:克隆远程仓库
场景描述: 参与已有项目开发,需要从远程服务器获取代码。
使用方法:
- 使用
git clone命令 - 配置个人信息
- 查看项目结构
示例代码:
bash
# 克隆远程仓库
$ git clone https://github.com/laravel/laravel.git my-laravel
Cloning into 'my-laravel'...
remote: Enumerating objects: 12345, done.
remote: Counting objects: 100% (12345/12345), done.
remote: Compressing objects: 100% (6789/6789), done.
remote: Total 12345 (delta 5678), reused 12345 (delta 5678), pack-reused 0
Receiving objects: 100% (12345/12345), 5.67 MiB | 1.23 MiB/s, done.
Resolving deltas: 100% (5678/5678), done.
# 进入项目目录
$ cd my-laravel
# 查看远程仓库信息
$ git remote -v
origin https://github.com/laravel/laravel.git (fetch)
origin https://github.com/laravel/laravel.git (push)
# 查看分支
$ git branch -a
* main
remotes/origin/HEAD -> origin/main
remotes/origin/main
remotes/origin/develop
# 配置个人信息
$ git config user.name "Your Name"
$ git config user.email "your.email@example.com"运行结果分析:
git clone自动创建远程仓库的完整副本- 自动设置了
origin远程仓库别名 - 可以直接开始开发工作
场景 3:日常开发流程
场景描述: 日常开发中最常见的工作流程:修改代码、提交更改、同步远程。
使用方法:
- 拉取最新代码
- 创建功能分支
- 修改代码
- 提交更改
- 推送到远程
示例代码:
bash
# 拉取最新代码
$ git pull origin main
From https://github.com/user/repo
* branch main -> FETCH_HEAD
Already up to date.
# 创建功能分支
$ git checkout -b feature/user-auth
Switched to a new branch 'feature/user-auth'
# 修改文件
$ echo "<?php // 用户认证模块" > Auth.php
# 查看修改状态
$ git status
On branch feature/user-auth
Untracked files:
Auth.php
# 查看具体修改
$ git diff
# 添加到暂存区
$ git add Auth.php
# 提交更改
$ git commit -m "feat(auth): 添加用户认证模块基础结构"
[feature/user-auth def5678] feat(auth): 添加用户认证模块基础结构
1 file changed, 1 insertion(+)
create mode 100644 Auth.php
# 推送到远程
$ git push -u origin feature/user-auth
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 345 bytes | 345.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote:
remote: Create a pull request for 'feature/user-auth' on GitHub by visiting:
remote: https://github.com/user/repo/pull/new/feature/user-auth
remote:
To https://github.com/user/repo.git
* [new branch] feature/user-auth -> feature/user-auth
Branch 'feature/user-auth' set up to track remote branch 'feature/user-auth' from 'origin'.运行结果分析:
- 使用分支隔离开发,不影响主分支
- 每次提交都是原子操作,便于回滚
- 推送后可以在 GitHub 创建 Pull Request
场景 4:查看项目历史
场景描述: 需要了解项目的开发历史,追踪某个功能的演进过程。
使用方法:
- 查看提交历史
- 查看文件修改历史
- 查看特定提交详情
示例代码:
bash
# 查看简洁的提交历史
$ git log --oneline
ghi9012 feat: 添加用户权限管理
def5678 fix: 修复登录验证 bug
abc1234 feat: 添加用户登录功能
xyz5678 docs: 更新 README
# 查看详细的提交历史
$ git log
commit ghi9012345678901234567890123456789012345
Author: Zhang San <zhangsan@example.com>
Date: Mon Jan 15 14:30:00 2024 +0800
feat: 添加用户权限管理
- 实现角色管理
- 实现权限分配
- 添加权限验证中间件
# 查看图形化历史
$ git log --oneline --graph --all
* ghi9012 Merge branch 'feature/permissions'
|\
| * def5678 feat: 添加权限管理
* | abc1234 feat: 添加用户登录功能
|/
* xyz5678 docs: 更新 README
# 查看某个文件的历史
$ git log --follow --oneline config/database.php
abc1234 feat: 添加数据库配置
xyz5678 chore: 初始化项目
# 查看某次提交的详细修改
$ git show abc1234
commit abc1234567890abcdef...
Author: Zhang San <zhangsan@example.com>
Date: Mon Jan 10 10:00:00 2024 +0800
feat: 添加用户登录功能
diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php
new file mode 100644
index 0000000..abc1234
--- /dev/null
+++ b/app/Http/Controllers/AuthController.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class AuthController extends Controller
+{
+ public function login()
+ {
+ // 登录逻辑
+ }
+}运行结果分析:
--oneline显示简洁的单行历史--graph显示分支合并图形--follow可以追踪文件重命名
场景 5:团队协作合并代码
场景描述: 多人协作开发,需要合并不同成员的代码更改。
使用方法:
- 获取远程更新
- 合并分支
- 解决冲突
- 推送合并结果
示例代码:
bash
# 切换到主分支
$ git checkout main
Switched to branch 'main'
# 拉取最新代码
$ git pull origin main
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 5 (delta 2), reused 5 (delta 2), pack-reused 0
Unpacking objects: 100% (5/5), done.
From https://github.com/user/repo
* branch main -> FETCH_HEAD
abc1234..def5678 main -> origin/main
Updating abc1234..def5678
Fast-forward
app/Models/User.php | 5 +++++
1 file changed, 5 insertions(+)
# 合并功能分支
$ git merge feature/user-auth
Updating def5678..ghi9012
Fast-forward
app/Http/Controllers/AuthController.php | 45 +++++++++++++++++++++++++
routes/web.php | 3 ++
2 files changed, 48 insertions(+)
create mode 100644 app/Http/Controllers/AuthController.php
# 如果有冲突,手动解决
$ git merge feature/user-profile
Auto-merging app/Models/User.php
CONFLICT (content): Merge conflict in app/Models/User.php
Automatic merge failed; fix conflicts and then commit the result.
# 查看冲突文件
$ git status
Unmerged paths:
both modified: app/Models/User.php
# 编辑冲突文件
$ vim app/Models/User.php
# 解决冲突后标记为已解决
$ git add app/Models/User.php
# 完成合并
$ git commit -m "merge: 合并用户资料功能分支"
[main 1234567] merge: 合并用户资料功能分支
# 推送到远程
$ git push origin main
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (8/8), 1.23 KiB | 1.23 MiB/s, done.
Total 8 (delta 4), reused 0 (delta 0), pack-reused 0
To https://github.com/user/repo.git
def5678..1234567 main -> main运行结果分析:
git pull先获取远程更新再合并- Fast-forward 合并是最简单的合并方式
- 冲突需要手动解决后才能完成合并
1.6 企业级进阶应用
Git Flow 工作流
Git Flow 是一种广泛使用的工作流模型,适合有计划发布周期的项目。
main ──────●──────●──────●──────►
│ │ │
release ───┼──────●──────┘
│ │
develop ───●──────●──────●──────●──────►
│ │ │
feature ───●─────────────● │
│ │
hotfix ────┼────────────────────●
│分支类型:
| 分支类型 | 命名规则 | 说明 |
|---|---|---|
| main | main/master | 生产环境代码 |
| develop | develop | 开发主分支 |
| feature | feature/* | 新功能开发 |
| release | release/* | 发布准备 |
| hotfix | hotfix/* | 紧急修复 |
实现示例:
bash
# 安装 git-flow
$ brew install git-flow
# 初始化 git-flow
$ git flow init
# 开始新功能
$ git flow feature start user-auth
Switched to a new branch 'feature/user-auth'
# 完成功能开发
$ git flow feature finish user-auth
Switched to branch 'develop'
Already up to date!
Deleted branch feature/user-auth (was abc1234).
# 开始发布
$ git flow release start v1.0.0
Switched to a new branch 'release/v1.0.0'
# 完成发布
$ git flow release finish v1.0.0
Switched to branch 'main'
Merge made by the 'recursive' strategy.
Switched to branch 'develop'
Merge made by the 'recursive' strategy.
Deleted branch release/v1.0.0 (was def5678).
# 紧急修复
$ git flow hotfix start critical-bug
$ git flow hotfix finish critical-bugGit Hooks 自动化
Git Hooks 可以在特定事件发生时自动执行脚本。
常用 Hooks:
| Hook | 触发时机 | 用途 |
|---|---|---|
| pre-commit | 提交前 | 代码检查、格式化 |
| commit-msg | 提交信息编辑后 | 验证提交信息格式 |
| pre-push | 推送前 | 运行测试 |
| post-merge | 合并后 | 安装依赖 |
pre-commit 示例:
bash
#!/bin/bash
# .git/hooks/pre-commit
# 检查 PHP 语法错误
echo "检查 PHP 语法..."
FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.php')
if [ -n "$FILES" ]; then
for FILE in $FILES; do
php -l "$FILE" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "PHP 语法错误: $FILE"
exit 1
fi
done
fi
# 检查代码风格
echo "检查代码风格..."
./vendor/bin/phpcs --standard=PSR12 $FILES
if [ $? -ne 0 ]; then
echo "代码风格检查失败,请修复后再提交"
exit 1
fi
echo "检查通过!"
exit 0commit-msg 示例:
bash
#!/bin/bash
# .git/hooks/commit-msg
# 获取提交信息
COMMIT_MSG=$(cat "$1")
# 验证提交信息格式
PATTERN="^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "提交信息格式错误!"
echo "正确格式: type(scope): subject"
echo "示例: feat(user): 添加用户登录功能"
exit 1
fiGit 大文件管理
对于大型二进制文件(如图片、视频),使用 Git LFS(Large File Storage)。
bash
# 安装 Git LFS
$ brew install git-lfs
# 初始化
$ git lfs install
# 追踪大文件类型
$ git lfs track "*.psd"
$ git lfs track "*.mp4"
$ git lfs track "*.zip"
# 查看追踪规则
$ git lfs track
Listing tracked patterns
*.psd (.gitattributes)
*.mp4 (.gitattributes)
*.zip (.gitattributes)
# 正常提交
$ git add .
$ git commit -m "feat: 添加设计稿"1.7 行业最佳实践
实践 1:频繁提交
实践内容: 每次完成一个小功能或修复一个 bug 就提交,而不是积累大量修改后一次性提交。
推荐理由:
- 每个提交都是原子操作,便于回滚和审查
- 提交历史更清晰,便于理解项目演进
- 减少代码冲突的风险
bash
# 好的做法:小步提交
$ git commit -m "feat(auth): 添加登录表单验证"
$ git commit -m "feat(auth): 实现登录 API 接口"
$ git commit -m "feat(auth): 添加登录状态保持"
# 不好的做法:大提交
$ git commit -m "feat: 完成整个用户系统(登录、注册、权限、个人中心)"实践 2:使用分支进行功能开发
实践内容: 每个新功能、bug 修复都在独立分支上进行,完成后合并到主分支。
推荐理由:
- 隔离开发,不影响主分支稳定性
- 便于代码审查和讨论
- 可以并行开发多个功能
bash
# 功能分支工作流
$ git checkout -b feature/payment
# ... 开发支付功能 ...
$ git push origin feature/payment
# 创建 Pull Request 进行代码审查
# 审查通过后合并到 main实践 3:编写清晰的提交信息
实践内容: 遵循约定式提交规范,编写清晰、详细的提交信息。
推荐理由:
- 便于理解每次提交的目的
- 自动生成变更日志
- 便于追踪问题
bash
# 好的提交信息
feat(order): 添加订单导出功能
- 支持 CSV 和 Excel 格式导出
- 支持按日期范围筛选
- 支持批量导出
Breaking Change: 导出接口参数格式变更
Closes #789
# 不好的提交信息
fix bug
update
修改实践 4:定期同步远程更新
实践内容: 每天开始工作前,先拉取远程仓库的最新更新。
推荐理由:
- 及时了解团队其他成员的进度
- 减少合并冲突
- 保持本地仓库与远程同步
bash
# 每日开始工作
$ git checkout main
$ git pull origin main
$ git checkout feature/my-feature
$ git rebase main # 或 merge实践 5:使用 .gitignore 过滤文件
实践内容: 创建完善的 .gitignore 文件,排除不需要版本控制的文件。
推荐理由:
- 保持仓库干净
- 减少仓库体积
- 避免提交敏感信息
bash
# PHP 项目 .gitignore 示例
/vendor/
/node_modules/
/.idea/
/.vscode/
/.env
/.env.local
*.log
*.cache
.DS_Store
Thumbs.db
# 编译输出
/build/
/dist/
# 测试覆盖率
/coverage/
/.phpunit.result.cache1.8 常见问题答疑(FAQ)
问题 1:Git 和 GitHub 有什么区别?
问题描述: 很多初学者混淆 Git 和 GitHub,认为它们是同一个东西。
回答内容:
Git 和 GitHub 是两个不同的概念:
Git 是一个分布式版本控制软件,安装在你的本地电脑上,用于管理代码版本。它是一个工具,就像 Word 是编辑文档的工具一样。
GitHub 是一个代码托管平台,运行在云端服务器上,提供 Git 仓库的远程存储服务。它是一个网站,就像百度网盘是存储文件的网站一样。
类比理解:
- Git 就像你电脑上的 Word 软件
- GitHub 就像 Google Docs 或石墨文档
- 你可以用 Word 本地编辑文档(用 Git 本地管理代码)
- 也可以把文档上传到 Google Docs 协作(把代码推送到 GitHub 协作)
其他 Git 托管平台:
- GitLab
- Bitbucket
- Gitee(码云)
- 自建 Git 服务器(如 Gogs、Gitea)
示例代码:
bash
# Git 是本地工具
$ git init # 本地创建仓库
$ git add . # 本地添加文件
$ git commit -m "message" # 本地提交
# GitHub 是远程平台
$ git remote add origin https://github.com/user/repo.git # 连接远程仓库
$ git push origin main # 推送到 GitHub
$ git pull origin main # 从 GitHub 拉取更新问题 2:什么时候用 merge,什么时候用 rebase?
问题描述: 合并分支时,不清楚应该选择 merge 还是 rebase。
回答内容:
Merge(合并):
- 创建一个新的合并提交
- 保留完整的分支历史
- 历史图会有分叉
Rebase(变基):
- 将提交移动到目标分支顶端
- 创建线性历史
- 历史图是一条直线
选择原则:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 合并公共分支到功能分支 | merge | 保留完整历史,便于追溯 |
| 更新功能分支与主分支同步 | rebase | 保持历史线性,便于理解 |
| 已推送到远程的分支 | merge | rebase 会改写历史,影响他人 |
| 本地未推送的分支 | rebase | 可以安全地改写历史 |
示例代码:
bash
# 使用 merge
$ git checkout feature
$ git merge main
# 结果:
# * Merge branch 'main' into feature
# |\
# | * commit on main
# * | commit on feature
# |/
# * common ancestor
# 使用 rebase
$ git checkout feature
$ git rebase main
# 结果:
# * commit on feature (rebased)
# * commit on feature (rebased)
# * commit on main
# * common ancestor重要警告: 永远不要对已推送到远程的提交执行 rebase!这会改写历史,导致其他开发者的仓库出现问题。
问题 3:如何撤销最近的提交?
问题描述: 提交后发现有问题,想要撤销这次提交。
回答内容:
根据不同情况,有三种撤销方式:
1. 保留修改,只撤销提交(推荐):
bash
$ git reset --soft HEAD~1
# 撤销最近一次提交,保留修改在暂存区
# 可以重新编辑后再次提交2. 保留修改,撤销提交和暂存:
bash
$ git reset --mixed HEAD~1
# 或简写为
$ git reset HEAD~1
# 撤销提交和暂存,保留修改在工作目录
# 需要重新 add 和 commit3. 完全撤销,丢弃所有修改(危险):
bash
$ git reset --hard HEAD~1
# 撤销提交,丢弃所有修改
# ⚠️ 警告:修改将永久丢失!如果已经推送到远程:
bash
# 创建一个反向提交(安全)
$ git revert HEAD
# 这会创建一个新的提交,撤销上一次提交的修改
# 适合已推送的情况,不会影响他人
# 推送到远程
$ git push origin main对比分析:
| 方式 | 提交历史 | 文件修改 | 适用场景 |
|---|---|---|---|
| reset --soft | 撤销 | 保留在暂存区 | 想修改提交信息 |
| reset --mixed | 撤销 | 保留在工作目录 | 想重新组织提交 |
| reset --hard | 撤销 | 丢弃 | 确认要放弃修改 |
| revert | 新增反向提交 | 撤销效果 | 已推送的提交 |
问题 4:如何解决 "fatal: refusing to merge unrelated histories" 错误?
问题描述: 合并两个没有共同祖先的分支时出现此错误。
回答内容:
这个错误发生在两个仓库没有共同的提交历史时,Git 默认拒绝合并以防止意外合并无关项目。
常见场景:
- 本地新建仓库后,想关联远程仓库
- 合并两个独立创建的仓库
解决方案:
bash
# 方法 1:允许合并不相关历史
$ git pull origin main --allow-unrelated-histories
# 或
$ git merge origin/main --allow-unrelated-histories
# 方法 2:推荐做法 - 先克隆再添加文件
$ git clone https://github.com/user/repo.git
$ cd repo
# 然后复制你的文件到这个目录
$ git add .
$ git commit -m "feat: 添加项目文件"
$ git push origin main
# 方法 3:如果本地已有项目,关联远程仓库
$ git remote add origin https://github.com/user/repo.git
$ git fetch origin
$ git merge origin/main --allow-unrelated-histories
$ git push -u origin main最佳实践: 建议使用方法 2,先克隆远程仓库,再添加本地文件,这样可以避免历史不相关的问题。
问题 5:如何只提交文件的部分修改?
问题描述: 一个文件有多处修改,只想提交其中一部分。
回答内容:
Git 提供了交互式暂存功能,可以选择性地暂存文件的修改片段。
使用方法:
bash
# 交互式添加
$ git add -p filename.php
# Git 会显示每个修改块,让你选择
# y - 暂存此块
# n - 不暂存此块
# q - 退出
# a - 暂存此文件所有剩余块
# d - 不暂存此文件所有剩余块
# s - 分割成更小的块
# e - 手动编辑
# ? - 帮助示例场景:
php
<?php
// 原文件 content.php
function oldFunction() {
return 'old';
}
// 修改后的文件
function newFunction() { // 修改 1:想提交
return 'new';
}
// TODO: 临时调试代码 // 修改 2:不想提交
$debug = true;操作步骤:
bash
$ git add -p content.php
diff --git a/content.php b/content.php
index abc1234..def5678 100644
--- a/content.php
+++ b/content.php
@@ -1,5 +1,8 @@
-function oldFunction() {
- return 'old';
+function newFunction() {
+ return 'new';
}
+
+// TODO: 临时调试代码
+$debug = true;
(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? s
# 分割成更小的块
Split into 2 hunks.
@@ -1,5 +1,5 @@
-function oldFunction() {
- return 'old';
+function newFunction() {
+ return 'new';
}
(1/2) Stage this hunk [y,n,q,a,d,e,?]? y
# 暂存第一个修改
@@ -3,3 +3,6 @@
}
+
+// TODO: 临时调试代码
+$debug = true;
(2/2) Stage this hunk [y,n,q,a,d,e,?]? n
# 不暂存第二个修改
$ git status
Changes to be committed:
modified: content.php # 只包含第一个修改
Changes not staged for commit:
modified: content.php # 包含第二个修改问题 6:如何查看某个文件的修改历史?
问题描述: 需要追踪某个文件是谁在什么时候修改的。
回答内容:
Git 提供了多种方式查看文件历史:
1. 查看文件的提交历史:
bash
$ git log --follow --oneline filename.php
abc1234 feat: 添加新功能
def5678 fix: 修复 bug
ghi9012 refactor: 重构代码
xyz5678 feat: 初始提交
# --follow 可以追踪文件重命名2. 查看每次提交的具体修改:
bash
$ git log -p filename.php
# 显示每次提交的详细差异3. 查看每行代码的最后修改信息:
bash
$ git blame filename.php
abc1234 (Zhang San 2024-01-15 10:00:00 +0800 1) <?php
def5678 (Li Si 2024-01-14 15:30:00 +0800 2)
ghi9012 (Wang Wu 2024-01-13 09:00:00 +0800 3) class UserService
abc1234 (Zhang San 2024-01-15 10:00:00 +0800 4) {
abc1234 (Zhang San 2024-01-15 10:00:00 +0800 5) public function login()
# 格式:提交哈希 (作者 时间 行号) 代码内容4. 查看特定行范围的修改:
bash
$ git blame -L 10,20 filename.php
# 只查看第 10-20 行的修改信息5. 查看文件在某个时间点的状态:
bash
$ git show abc1234:filename.php
# 查看某次提交时的文件内容
$ git show HEAD~3:filename.php
# 查看 3 次提交前的文件内容6. 使用图形化工具:
bash
$ gitk filename.php
# 打开图形化历史查看器1.9 实战练习
基础练习:创建第一个 Git 仓库
练习目标: 掌握 Git 仓库的创建、文件添加和提交操作。
解题思路:
- 创建项目目录
- 初始化 Git 仓库
- 创建文件
- 添加到暂存区
- 提交更改
常见误区:
- 忘记初始化仓库
- 没有添加文件就提交
- 提交信息过于简单
分步提示:
bash
# 步骤 1:创建项目目录
$ mkdir my-first-repo
$ cd my-first-repo
# 步骤 2:初始化 Git 仓库
# 提示:使用 git init 命令
# 步骤 3:创建 README.md 文件
# 提示:写入项目名称和简介
# 步骤 4:查看仓库状态
# 提示:使用 git status 命令
# 步骤 5:添加文件到暂存区
# 提示:使用 git add 命令
# 步骤 6:提交更改
# 提示:使用 git commit 命令,写一个有意义的提交信息
# 步骤 7:查看提交历史
# 提示:使用 git log 命令参考代码:
bash
# 创建项目目录
$ mkdir my-first-repo
$ cd my-first-repo
# 初始化 Git 仓库
$ git init
Initialized empty Git repository in /path/my-first-repo/.git/
# 创建 README.md
$ cat > README.md << 'EOF'
# My First Repository
这是我的第一个 Git 仓库项目。
## 功能
- 学习 Git 基础操作
- 练习版本控制
## 安装
```bash
git clone https://github.com/username/my-first-repo.gitEOF
查看状态
$ git status On branch main
No commits yet
Untracked files: README.md
添加到暂存区
$ git add README.md
再次查看状态
$ git status On branch main
No commits yet
Changes to be committed: new file: README.md
提交更改
$ git commit -m "feat: 初始化项目,添加 README 文件" [main (root-commit) abc1234] feat: 初始化项目,添加 README 文件 1 file changed, 15 insertions(+) create mode 100644 README.md
查看提交历史
$ git log commit abc1234567890abcdef1234567890abcdef12345678 Author: Your Name your.email@example.com Date: Mon Jan 15 10:00:00 2024 +0800
feat: 初始化项目,添加 README 文件
#### 进阶练习:分支管理实战
**练习目标:**
掌握分支创建、切换、合并和删除操作。
**解题思路:**
1. 创建主分支的初始提交
2. 创建并切换到功能分支
3. 在功能分支开发新功能
4. 切换回主分支
5. 合并功能分支
6. 删除功能分支
**常见误区:**
- 忘记切换分支就开始开发
- 合并前没有提交功能分支的更改
- 合并后忘记删除功能分支
**分步提示:**
```bash
# 步骤 1:创建初始项目
$ mkdir branch-practice
$ cd branch-practice
$ git init
$ echo "# Branch Practice" > README.md
$ git add .
$ git commit -m "feat: 初始化项目"
# 步骤 2:创建并切换到 feature/login 分支
# 提示:使用 git checkout -b 命令
# 步骤 3:在功能分支创建登录相关文件
# 提示:创建 LoginController.php
# 步骤 4:提交功能分支的更改
# 步骤 5:切换回 main 分支
# 提示:使用 git checkout 命令
# 步骤 6:合并 feature/login 分支
# 提示:使用 git merge 命令
# 步骤 7:删除功能分支
# 提示:使用 git branch -d 命令
# 步骤 8:查看分支历史
# 提示:使用 git log --oneline --graph参考代码:
bash
# 创建初始项目
$ mkdir branch-practice
$ cd branch-practice
$ git init
$ echo "# Branch Practice" > README.md
$ git add .
$ git commit -m "feat: 初始化项目"
[main (root-commit) abc1234] feat: 初始化项目
1 file changed, 1 insertion(+)
create mode 100644 README.md
# 创建并切换到功能分支
$ git checkout -b feature/login
Switched to a new branch 'feature/login'
# 创建登录控制器
$ mkdir -p app/Controllers
$ cat > app/Controllers/LoginController.php << 'EOF'
<?php
namespace App\Controllers;
class LoginController
{
public function index()
{
return 'Login Page';
}
public function authenticate()
{
// 认证逻辑
return 'Authenticating...';
}
}
EOF
# 提交功能分支的更改
$ git add .
$ git commit -m "feat(login): 添加登录控制器"
[feature/login def5678] feat(login): 添加登录控制器
1 file changed, 17 insertions(+)
create mode 100644 app/Controllers/LoginController.php
# 切换回 main 分支
$ git checkout main
Switched to branch 'main'
# 合并功能分支
$ git merge feature/login
Updating abc1234..def5678
Fast-forward
app/Controllers/LoginController.php | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 app/Controllers/LoginController.php
# 删除功能分支
$ git branch -d feature/login
Deleted branch feature/login (was def5678).
# 查看分支历史
$ git log --oneline --graph
* def5678 feat(login): 添加登录控制器
* abc1234 feat: 初始化项目
# 查看当前分支
$ git branch
* main挑战练习:解决复杂的合并冲突
练习目标: 模拟真实的多人协作场景,解决复杂的合并冲突。
解题思路:
- 创建模拟场景
- 在两个分支修改同一文件
- 尝试合并产生冲突
- 分析冲突内容
- 手动解决冲突
- 完成合并
常见误区:
- 不理解冲突标记的含义
- 解决冲突时丢失代码
- 解决冲突后忘记测试
分步提示:
bash
# 步骤 1:创建项目并初始化
$ mkdir conflict-practice
$ cd conflict-practice
$ git init
# 步骤 2:创建配置文件并提交
$ cat > config.php << 'EOF'
<?php
return [
'app_name' => 'My App',
'debug' => false,
'database' => [
'host' => 'localhost',
'name' => 'myapp',
],
];
EOF
$ git add .
$ git commit -m "feat: 添加配置文件"
# 步骤 3:创建 feature/database 分支并修改配置
$ git checkout -b feature/database
# 修改 database 配置
# 提交更改
# 步骤 4:切换回 main 分支并修改同一位置
$ git checkout main
# 修改 database 配置(不同的值)
# 提交更改
# 步骤 5:尝试合并 feature/database 分支
# 提示:观察冲突信息
# 步骤 6:查看冲突文件内容
# 提示:理解 <<<<<<< HEAD 和 ======= 标记
# 步骤 7:手动解决冲突
# 提示:保留正确的配置,删除冲突标记
# 步骤 8:标记为已解决并完成合并
# 提示:git add 和 git commit
# 步骤 9:验证最终结果参考代码:
bash
# 创建项目
$ mkdir conflict-practice
$ cd conflict-practice
$ git init
# 创建初始配置文件
$ cat > config.php << 'EOF'
<?php
return [
'app_name' => 'My App',
'debug' => false,
'database' => [
'host' => 'localhost',
'name' => 'myapp',
],
];
EOF
$ git add .
$ git commit -m "feat: 添加配置文件"
[main (root-commit) abc1234] feat: 添加配置文件
1 file changed, 10 insertions(+)
create mode 100644 config.php
# 创建功能分支并修改
$ git checkout -b feature/database
Switched to a new branch 'feature/database'
$ cat > config.php << 'EOF'
<?php
return [
'app_name' => 'My App',
'debug' => false,
'database' => [
'host' => 'db.example.com',
'name' => 'production_db',
'port' => 3306,
],
];
EOF
$ git add .
$ git commit -m "feat(database): 更新生产环境数据库配置"
[feature/database def5678] feat(database): 更新生产环境数据库配置
1 file changed, 3 insertions(+), 2 deletions(-)
# 切换回 main 分支并修改同一位置
$ git checkout main
Switched to branch 'main'
$ cat > config.php << 'EOF'
<?php
return [
'app_name' => 'My App',
'debug' => true,
'database' => [
'host' => '127.0.0.1',
'name' => 'development_db',
],
];
EOF
$ git add .
$ git commit -m "feat: 更新开发环境配置"
[main ghi9012] feat: 更新开发环境配置
1 file changed, 2 insertions(+), 2 deletions(-)
# 尝试合并
$ git merge feature/database
Auto-merging config.php
CONFLICT (content): Merge conflict in config.php
Automatic merge failed; fix conflicts and then commit the result.
# 查看冲突文件
$ cat config.php
<?php
return [
'app_name' => 'My App',
<<<<<<< HEAD
'debug' => true,
'database' => [
'host' => '127.0.0.1',
'name' => 'development_db',
=======
'debug' => false,
'database' => [
'host' => 'db.example.com',
'name' => 'production_db',
'port' => 3306,
>>>>>>> feature/database
],
];
# 手动解决冲突 - 保留最佳配置
$ cat > config.php << 'EOF'
<?php
return [
'app_name' => 'My App',
'debug' => env('APP_DEBUG', false),
'database' => [
'host' => env('DB_HOST', 'localhost'),
'name' => env('DB_NAME', 'myapp'),
'port' => env('DB_PORT', 3306),
],
];
EOF
# 标记为已解决
$ git add config.php
# 完成合并
$ git commit -m "merge: 合并数据库配置,使用环境变量
- 解决 feature/database 和 main 的配置冲突
- 使用 env() 函数支持多环境配置"
[main 1234567] merge: 合并数据库配置,使用环境变量
# 查看最终结果
$ cat config.php
<?php
return [
'app_name' => 'My App',
'debug' => env('APP_DEBUG', false),
'database' => [
'host' => env('DB_HOST', 'localhost'),
'name' => env('DB_NAME', 'myapp'),
'port' => env('DB_PORT', 3306),
],
];
# 查看合并历史
$ git log --oneline --graph
* 1234567 merge: 合并数据库配置,使用环境变量
|\
| * def5678 feat(database): 更新生产环境数据库配置
* | ghi9012 feat: 更新开发环境配置
|/
* abc1234 feat: 添加配置文件1.10 知识点总结
核心要点
Git 是分布式版本控制系统
- 每个开发者都有完整的仓库副本
- 不依赖中央服务器,支持离线工作
- 速度快、安全性高
三个工作区域
- 工作目录:实际编辑文件的地方
- 暂存区:下次提交的文件快照
- 仓库:保存所有提交历史
基本工作流程
- 修改文件 → git add → git commit → git push
- git pull → 解决冲突 → git push
分支是 Git 的核心特性
- 分支是指向提交的指针
- 创建和切换分支非常快速
- 支持并行开发
提交信息规范
- 遵循约定式提交格式
- 清晰描述修改内容
- 便于生成变更日志
易错点回顾
| 易错点 | 正确做法 |
|---|---|
| 提交敏感信息 | 使用 .gitignore 排除,使用钩子检测 |
| 提交信息不规范 | 遵循约定式提交规范 |
| 在错误分支工作 | 开发前确认当前分支 |
| 合并冲突处理不当 | 理解冲突标记,保留正确代码 |
| 误删文件或提交 | 使用 git reflog 恢复 |
| 对已推送的提交 rebase | 已推送的提交使用 merge 或 revert |
1.11 拓展参考资料
官方文档
- Git 官方文档
- Pro Git 电子书(强烈推荐)
- [GitHub 官方文档](https://docs.github.com)
进阶学习路径
Git 进阶命令
- git stash:暂存工作区修改
- git cherry-pick:选择性合并提交
- git bisect:二分查找问题提交
- git filter-branch:重写历史
Git 工作流
- Git Flow
- GitHub Flow
- GitLab Flow
- Trunk Based Development
Git 工具集成
- IDE 集成(VS Code、PhpStorm)
- GUI 工具(Sourcetree、GitKraken)
- CI/CD 集成(GitHub Actions、GitLab CI)
Git 服务器搭建
- GitLab CE/EE
- Gitea
- Gogs
推荐资源
- Learn Git Branching:交互式学习 Git 分支
- Git 简明指南
- GitHub Skills:GitHub 官方学习平台
- Atlassian Git Tutorials
学习建议: Git 是一个需要通过大量实践才能熟练掌握的工具。建议在学习过程中:
- 每天使用 Git 管理自己的代码
- 参与开源项目,体验真实的协作流程
- 遇到问题时,先尝试自己解决,再查阅文档
- 定期复习高级命令,提升效率
