git
git 一个分布式版本控制系统,由Linus Torvalds(芬兰计算机科学家,linux之父呀)在2005创建,最初是为了帮助管理 Linux 内核的开发而设计
使用git可以协作并行工作,版本控制,可以创建分支轻松合并更改,代码保存在多个地方,防止数据丢失。
Git的核心概念
仓库 (Repository): 你的项目代码库,包含所有文件和历史记录。
- 本地仓库: 在你电脑上的仓库。
- 远程仓库: 在线托管的仓库,例如GitHub、GitLab、Gitee。
工作区 (Working Directory): 你在电脑上看到和编辑的实际文件。
暂存区 (Staging Area / Index): 一个临时区域,用于存放你准备提交(commit)的更改。你可以选择性地将文件添加到暂存区。
提交 (Commit): 将暂存区的更改永久保存到本地仓库中,并生成一个唯一的**提交ID (Commit ID)**。每次提交都像是一个项目状态的“快照”。
分支 (Branch): 从主线(通常是
main
或master
分支)分出来的独立开发路径。你可以在不影响主线代码的情况下进行新功能开发或修复bug。合并 (Merge): 将一个分支的更改合并到另一个分支。
拉取 (Pull): 从远程仓库下载最新的更改到本地仓库,并自动合并。
推送 (Push): 将本地仓库的更改上传到远程仓库。
Git安装与配置
1. Git 安装
- Windows:下载 Git for Windows 安装包 (https://git-scm.com/download/win),按照提示安装。
- macOS:
- 通过 Homebrew 安装:
brew install git
。 - 安装 Xcode Command Line Tools (会包含 Git):
xcode-select --install
。
- 通过 Homebrew 安装:
- Linux:使用包管理器安装,例如 Debian/Ubuntu:
sudo apt-get install git
;Fedora:sudo dnf install git
。 - 验证安装:安装完成后,在终端或命令行输入
git --version
检查 Git 是否成功安装并显示版本号。
安装完后验证安装:
1 | git --version |
- 初始配置Git
安装完成后,需要配置你的用户名和邮箱,在每次提交时显示,这对于团队协作和追溯历史非常重要。
1 | git config --global user.name "你的用户名" |
--global
参数表示这些配置是全局的,对电脑上所有的Git仓库都有效。如果想为某个特定仓库设置不同的用户名和邮箱,可以进入该仓库目录后不加--global
参数进行配置。- 配置默认编辑器(可选)
git config --global core.edit "vim" # 或"code --wait"(VS Code)
- 检查配置
可使用以下命令检查配置:
1 | git config --list |
2..gitconfig
文件的常见配置项详解
.gitconfig
文件是 Git 的配置文件,它存储了 Git 的各种设置,包括你的用户信息、默认编辑器、别名等。Git 会在三个不同的级别查找这些配置:
- **系统级配置 (System-level)**:
- 文件路径:通常位于
/etc/gitconfig
(Linux/macOS) 或 Git 安装目录下的etc/gitconfig
(Windows)。 - 作用范围:对系统上所有用户的所有 Git 仓库都有效。
- 命令:
git config --system
- 文件路径:通常位于
- **全局配置 (Global-level)**:
- 文件路径:通常位于用户主目录下的
~/.gitconfig
(Linux/macOS) 或C:\Users\<YourUsername>\.gitconfig
(Windows)。 - 作用范围:对当前用户的所有 Git 仓库都有效。这是最常用的配置级别。
- 命令:
git config --global
- 文件路径:通常位于用户主目录下的
- **仓库级配置 (Repository-level)**:
- 文件路径:位于每个 Git 仓库的
.git/config
目录下。 - 作用范围:仅对当前 Git 仓库有效。
- 命令:
git config --local
(默认,可以省略--local
)
- 文件路径:位于每个 Git 仓库的
优先级:仓库级配置 > 全局配置 > 系统级配置。这意味着如果同一个配置项在多个级别中都存在,Git 会优先使用最具体的(即仓库级)配置。
当你使用 git config
命令进行配置时,实际上就是修改这些 .gitconfig
文件。下面是一些常见的配置项及其作用:
1. [user]
用户信息
这是最基本也是最重要的配置,用于标识你的提交。
name
: 你的用户名,会显示在 Git 提交记录中。1
2[user]
name = 你的名字对应命令:
git config --global user.name "你的名字"
email
: 你的邮箱地址,也会显示在 Git 提交记录中。1
2[user]
email = 你的邮箱@example.com对应命令:
git config --global user.email "你的邮箱@example.com"
2. [core]
核心配置
包含了一些 Git 核心行为的设置。
editor
: Git 在需要你输入提交信息或合并信息时使用的默认文本编辑器。1
2[core]
editor = vim对应命令:
git config --global core.editor "vim"
(也可以是code --wait
for VS Code,nano
,subl -w
, etc.)autocrlf
: 处理行结束符(CRLF vs. LF)。在跨平台协作时非常有用。true
(Windows 推荐):提交时将 CRLF 转换为 LF,检出时将 LF 转换为 CRLF。input
(macOS/Linux 推荐):提交时将 CRLF 转换为 LF,检出时不转换。false
:不做任何转换。
1
2[core]
autocrlf = input对应命令:
git config --global core.autocrlf input
excludesfile
: 指定一个全局的.gitignore
文件路径,用于忽略所有仓库中不需要跟踪的文件。1
2[core]
excludesfile = ~/.gitignore_global对应命令:
git config --global core.excludesfile ~/.gitignore_global
3. [alias]
别名
你可以为常用的 Git 命令设置别名,以简化输入。
示例:
1
2
3
4
5
6
7[alias]
co = checkout
br = branch
ci = commit -m
st = status
unstage = restore --staged
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit对应命令:
git config --global alias.co checkout
git config --global alias.st status
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
4. [color]
颜色设置
配置 Git 输出的颜色,提高可读性。
示例:
1
2
3
4
5
6
7
8
9
10
11[color]
ui = auto
[color "branch"]
current = yellow reverse
local = yellow
remote = green
[color "diff"]
meta = yellow bold
frag = magenta bold
old = red bold
new = green bold对应命令:
git config --global color.ui auto
5. [init]
初始化配置
defaultBranch
: 设置新仓库初始化时默认分支的名称(Git 2.28+ 推荐使用main
而非master
)。1
2[init]
defaultBranch = main对应命令:
git config --global init.defaultBranch main
6. [push]
推送配置
default
: 设置git push
命令的默认行为。simple
(Git 2.0+ 推荐):只推送当前分支到其对应的上游分支。matching
(旧版默认):推送所有名称相同的本地分支到远程。
1
2[push]
default = simple对应命令:
git config --global push.default simple
3. 查看 .gitconfig
文件内容
你可以直接用文本编辑器打开这些文件来查看和编辑,但更推荐使用 git config
命令:
查看所有配置:
1
git config --list
查看某个级别的配置:
1
2
3git config --list --system
git config --list --global
git config --list --local查看特定配置项的值:
1
2git config user.name
git config alias.st
了解和配置 .gitconfig
文件能让你更好地定制 Git 的行为,提高工作效率。
4. 文件状态与暂存区
Git 文件状态:
- **Untracked (未跟踪)**:新创建的文件,Git 还没有开始跟踪它们。
- **Unmodified (未修改)**:文件自上次提交以来没有发生变化。
- **Modified (已修改)**:文件自上次提交以来已发生变化,但尚未添加到暂存区。
- **Staged (已暂存)**:文件已修改并被添加到暂存区,准备好进行下一次提交。
5. Git 的数据结构
Git 并不是存储文件的差异,而是将每个文件作为一个快照(Snapshot)来存储。它使用一种叫 SHA-1 的算法来计算每个快照的唯一标识符。一个提交(Commit)包含了:
- 一个唯一的 SHA-1 值。
- 一个或多个指向父提交的指针。
- 一个指向当前项目所有文件快照的指针。
- 作者、提交者、提交信息、时间等元数据。
这种快照模式使得 Git 的操作速度非常快,尤其是在创建分支和合并时。
Git核心工作流程:工作区、暂存区、本地仓库
核心工作流程图示:
1 | +----------------+ +-------------------+ +-------------------+ |
实际操作流程:
- 你在工作区修改文件。
- 使用
git add <文件名>
或git add .
将你希望跟踪的更改从工作区添加到暂存区。 - 使用
git commit -m "提交信息"
将暂存区的所有内容提交到本地仓库,形成一个新的版本。
Git基本操作
现在我们开始实践Git的基本操作。
1. 初始化一个新的Git仓库
如果有一个全新的项目,要被Git管理,需要初始化一个Git仓库。
1 | # 进入项目根目录 |
这会在你的项目目录下创建一个名为 .git
的隐藏文件夹,这就是你的Git仓库,它包含了Git所需的所有信息。
2. 添加文件到暂存区
对文件进行更改后,需要告诉Git希望跟踪这些更改。
1 | git add <文件名> # 添加单个文件 |
1 | # 取消暂存文件 |
3. 提交更改
将暂存区的更改提交到本地仓库。每次提交都应该有一个清晰的**提交信息 (Commit Message)**,描述你这次提交做了什么。
1 | git commit -m "你的提交信息" |
一个好的提交信息能够帮助你和团队成员快速理解每次更改的目的。例如:
1 | git commit -m "feat: 添加部署指南" |
小技巧:
提交信息建议使用动词开头,清晰明了。常见的提交信息前缀:
feat:
新功能 (feature)fix:
修复bug (bug fix)docs:
文档变更 (documentation)style:
代码格式化 (whitespace, formatting)refactor:
代码重构 (refactoring)test:
增加测试 (tests)chore:
构建过程或辅助工具的变动 (build process, auxiliary tools)
4. 查看仓库状态
git status
命令非常常用,它会显示你工作区和暂存区的状态,以及哪些文件被修改、哪些文件是未跟踪的。
1 | git status |
- 红色文件名: 未跟踪的文件(untracked files)或已修改但未添加到暂存区的文件。
- 绿色文件名: 已添加到暂存区的文件。
5. 查看提交历史
git log
命令可以查看提交历史,包括提交ID、作者、日期和提交信息。
1 | git log |
你也可以使用一些参数让输出更简洁:
1 | git log --oneline # 每条提交显示一行 |
通过状态与历史:git status
和git log
可随时了解仓库的健康状况和发展历程。
当有多条分支和合并操作时 --graph
会非常直观…
6. 撤销操作
Git 的四大撤销操作:
Git 的撤销操作主要作用于我们之前讲过的三个区域:工作区、暂存区、本地仓库。
1. git restore
:撤销工作区的修改
git restore
命令用于撤销工作区中尚未添加到暂存区的修改。
- 作用区域:工作区。
- 工作方式:它会将工作区的文件恢复到最近一次提交或暂存区时的状态。
你正在修改一个文件 file.txt
,但突然发现改错了,想回到修改前的状态。
1 | # 修改了文件 |
执行后,file.txt
的内容会恢复到你上次 git commit
或 git add
时的状态。
2. git reset
:撤销提交,并移动 HEAD 指针
git reset
是一个功能强大且危险的命令,它用于撤销提交,并根据不同的参数移动 HEAD
指针和文件。
- 作用区域:本地仓库、暂存区、工作区。
- 工作方式:它将
HEAD
指针(指向当前提交的指针)移动到指定的提交。
git reset
有三个主要的参数:
- **
git reset --soft <commit-hash>
**:- 作用:只移动
HEAD
指针到指定的提交,保留你所有的修改在工作区和暂存区。 - 适用场景:你提交了几个 commit,但突然发现它们应该合并成一个,或者你提交信息写错了。你可以用
soft
模式回到上一个 commit,然后重新commit
。
- 作用:只移动
- **
git reset --mixed <commit-hash>
**:- 作用:这是默认模式。它移动
HEAD
指针,并将修改放回到工作区。清空暂存区。 - 适用场景:你已经提交了,但想撤销这次提交,并且把文件恢复到未暂存的状态。
- 作用:这是默认模式。它移动
- **
git reset --hard <commit-hash>
**:- 作用:这是最危险的模式。它移动
HEAD
指针,并彻底删除所有工作区和暂存区中该提交之后的所有修改。 - 适用场景:你提交了一个错误的修改,而且你确定不需要这些修改,想彻底回到一个干净的历史版本。
- 作用:这是最危险的模式。它移动
1 | # 假设你提交了两次:C1 -> C2 -> C3 (HEAD) |
3. git revert
:反向提交
git revert
用于撤销一次提交,但它并不会删除提交历史,而是创建一个新的提交,来抵消之前提交的修改。
- 作用区域:本地仓库。
- 工作方式:它通过创建一个新的提交来“还原”指定的提交。
与 git reset
的区别:
revert
不会改写历史,它会增加新的历史记录。这使得它在团队协作和共享分支上非常安全。reset --hard
会改写历史,并删除提交。这在公共分支上非常危险,因为它会影响其他人的本地仓库。
你提交了一次 Bug 修复,但这个修复导致了更大的问题。你想撤销它。
1 | # 找到导致问题的提交 |
执行后,你的提交历史会增加一个新的提交,内容是撤销了 abcde123
的修改。
4. git clean
:清除未追踪的文件
git clean
用于清理工作区中未被 Git 追踪的文件。
- 作用区域:工作区。
- 工作方式:它会删除那些你从未执行过
git add
的文件。 - **
git clean -n
**:- 作用:预演,显示将被删除的文件,但不会实际删除。这是最安全的用法。
- **
git clean -f
**:- 作用:强制删除未追踪的文件。
- **
git clean -fd
**:- 作用:强制删除未追踪的文件和目录。
你编译了一些临时文件,如 .o
文件,或者生成了一些日志文件,它们都没有被 git add
。你想删除它们。
1 | # 检查将要被删除的文件(安全模式) |
总结与对比:
命令 | 作用区域 | 撤销对象 | 行为方式 | 场景 |
---|---|---|---|---|
git restore | 工作区 | 未暂存的修改 | 将文件恢复到暂存区或上次提交的状态。 | 撤销工作区的改动。 |
git reset | 本地仓库 | 提交和工作区/暂存区修改 | 移动 HEAD 指针,可选择性地删除修改。 | 撤销本地提交,但不推荐用于公共分支。 |
git revert | 本地仓库 | 提交 | 创建一个新的提交来抵消之前的修改。 | 安全地撤销公共分支上的提交。 |
git clean | 工作区 | 未追踪的文件 | 彻底删除未被 Git 管理的文件。 | 清理临时文件或编译产物。 |
在实际工作中,最常使用的可能是 git restore
和 git revert
。而 git reset --hard
则像一把双刃剑,使用时务必小心。
功能对比:
git checkout
- 切换分支:
git checkout main
- 查看提交(进入 detached HEAD):
git checkout <commit>
- 恢复文件:
git checkout -- file.txt
(在较新 Git 里推荐git restore
替代)
git reset
- 撤销暂存:
git reset file.txt
(相当于git restore --staged file.txt
) - 移动 HEAD 到某个提交:
git reset <commit>
- 根据模式不同(
--soft
/--mixed
/--hard
),可以控制是否同时修改 暂存区 和 工作区
7. 忽略文件:.gitignore
项目中,有些文件或目录不想被git跟踪,比如:临时文件,敏感信息,依赖包等,如果不忽略导致仓库臃肿且不必要。
在项目根目录下创建一个名为 .gitignore
的文件(注意文件名以点开头),然后将.gitignore
文件本身添加到git并提交。
1 | git add .gitignore |
7.1 .gitignore
语法
每行定义一个忽略规则。
- 空行 或以
#
开头的行都会被忽略。 - **
*
**:匹配零个或多个字符。 - **
?
**:匹配单个字符。 - **
[]
**:匹配括号内的任何一个字符(例如[abc]
匹配 ‘a’, ‘b’, 或 ‘c’)。 - **
**
:匹配任意目录层级。 /
结尾: 表示忽略目录,不包含其下的子文件。!
开头: 表示不忽略某个文件或目录(即使它被前面的规则忽略了)。
注意: 如果已经不小心把某个应该忽略的文件提交到了仓库,然后才添加到 .gitignore
,Git不会自动从历史中删除它。需要手动从Git的跟踪中移除它:
1 | git rm --cached <文件名> |
--cached
参数表示只从Git的暂存区和仓库中移除,但保留你工作区的实际文件。
与远程仓库协作
远程仓库通常托管在 GitHub、GitLab 或 Bitbucket 等平台上,它为你提供了一个集中存放代码的地方,供团队成员共享和同步。
当你 git clone
或 git checkout
一个远程分支时,Git 会自动为你设置追踪关系。
git branch --set-upstream-to=<remote>/<branch>
: 手动设置追踪关系。
1 | # 在一个新分支上进行开发 |
https关联远程仓库
首先,你需要将你的本地仓库和远程仓库建立连接。
- **
git remote add <name> <url>
**:添加一个新的远程仓库。name
通常是origin
,url
是远程仓库的地址(可以是 HTTPS 或 SSH)。 - **
git remote -v
**:查看当前本地仓库关联的所有远程仓库。
1 | # 在 GitHub 上创建一个新的仓库,然后复制它的 HTTPS 地址 |
执行后,你会看到类似 origin https://github.com/user/my-project.git (fetch)
和 (push)
的输出,表示成功关联。
如果用 SSH 方式连接远程 Git 仓库,而不是 HTTPS,这在私有仓库和频繁推送中很常用,因为免密码(通过 SSH Key)且安全。
ssh方式连接远程仓库
生成 SSH Key
如果你本机还没有 SSH Key,需要先生成:
1 | # 默认在 ~/.ssh/id_ed25519 |
-t ed25519
:推荐,安全性高,性能好-C "your_email@example.com"
:给 key 加注释,一般用邮箱- 会提示输入文件路径和密码,可以直接回车使用默认路径,密码可以留空或设置
生成后,会得到两个文件:
1 | ~/.ssh/id_ed25519 # 私钥,不要泄露 |
添加公钥到 Git 服务器
- 登录你的 Git 平台(GitHub / GitLab / Gitee / Bitbucket 等)
- 找到 SSH Keys 配置页面
- 将
id_ed25519.pub
内容复制进去,保存
1 | cat ~/.ssh/id_ed25519.pub |
测试 SSH 连接
1 | ssh -T git@github.com |
- 第一次会提示
Are you sure you want to continue connecting (yes/no)?
,输入yes
- 成功后会看到类似:
1 | Hi username! You've successfully authenticated, but GitHub does not provide shell access. |
用 SSH 克隆仓库
假设仓库地址:
- HTTPS:
https://github.com/username/repo.git
- SSH:
git@github.com:username/repo.git
1 | git clone git@github.com:username/repo.git |
已有仓库改为 SSH
如果你已经用 HTTPS 克隆,可以切换为 SSH:
1 | git remote set-url origin git@github.com:username/repo.git |
检查是否修改成功:
1 | git remote -v |
输出示例:
1 | origin git@github.com:username/repo.git (fetch) |
常用技巧
免输密码
可以用
ssh-agent
添加私钥:1
2eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
多账号管理
可以在
~/.ssh/config
配置:1
2
3
4Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
SSH 方式主要优点:免密码、安全、适合脚本/自动化部署 ,核心步骤:生成 SSH Key → 上传公钥 → 测试 → 克隆或切换远程地址
推送提交到远程仓库
当你完成了本地的开发并进行了提交后,你需要将这些提交推送到远程仓库,让团队成员能够看到你的工作。
- **
git push -u <remote> <branch>
**:将本地分支的提交推送到远程仓库。-u
选项(即--set-upstream
)是初次推送时常用的,它会建立本地分支和远程分支的关联,之后你就可以直接使用git push
了。
1 | # 将本地的 master 分支推送到远程仓库 origin |
如果你正在使用 main
分支,则命令是 git push -u origin main
。
从远程仓库拉取更新
在团队协作中,其他成员可能已经推送了新的代码。你需要将这些最新的代码拉取到你的本地仓库,以确保你的代码是最新的。
git fetch <remote>
:从远程仓库下载最新的提交,但不合并到你的本地分支。这些提交会保存在一个隐藏的远程分支中(如origin/master
)。git pull <remote> <branch>
:从远程仓库拉取更新,并自动合并到你当前所在的本地分支。git pull
实际上是git fetch
和git merge
的组合。
1 | # 从远程仓库 origin 下载最新提交 |
克隆远程仓库
如果你是新加入一个团队,或者想在另一台电脑上获取项目的完整代码,你需要克隆远程仓库。
- **
git clone <url>
**:克隆一个完整的远程仓库到本地。这个命令会自动完成git init
、git remote add
和git pull
的所有步骤。
1 | # 克隆一个远程仓库到当前目录 |
克隆完成后,就拥有了一个完整的本地仓库,并且与远程仓库建立了关联。
示例
1 | ➜ ~ mkdir beizi_blog |
-u
参数只需在第一次推送分支时使用。之后,可以直接使用 git push
。
如果已经设置过关联,也可以手动设置本地分支与远程分支的关联,例如:
1 | git branch --set-upstream-to=origin/main main |
前提:远程有beizi_blog仓库,并设置好ssh key或 gpg key 我已经设置好了。以下设置步骤
设置ssh key
1 | ➜ ~/beizi_blog ls ~/.ssh |
1 | ➜ ~/beizi_blog ssh-keygen -t rsa -b 4096 -C "test keys" #测试生成密钥对到当前目录 |
如果想让不同的密钥对访问不同主机,需要建立ssh config 文件
1 | # GitHub |
分支管理——Git的灵魂
分支 (Branch) 就像是 Git 提供的一条独立的开发路径。它允许你在不影响主线开发(通常是 main
或 master
分支)的情况下,进行新功能的开发、bug 修复或实验性尝试。
在 Git 中,分支的本质只是一个指向某次提交 (commit) 的指针。 默认情况下,Git 会创建一个名为 main
(或 master
) 的主分支。当你进行提交时,这个分支指针就会向前移动。
- 当你创建一个新的分支时,Git 只是在当前提交上新增了一个指向它的指针。这个过程几乎瞬间完成,这也是 Git 分支轻量化的原因。
- 默认情况下,当你初始化一个 Git 仓库时,会有一个名为
master
(或main
) 的主分支。
分支的常用命令
查看分支
git branch
:查看本地分支,当前分支前带*
git branch -a
:显示所有分支(本地 + 远程)git branch -r
:仅显示远程分支git branch -vv
:显示分支详细信息(跟踪远程分支、最新提交)
创建分支
git branch new-feature
:从当前 HEAD 创建分支new-feature
git branch new-feature commit-hash
:从指定 commit 创建分支
删除分支
git branch -d branch-name
:删除本地分支(已合并时可用)git branch -D branch-name
:强制删除本地分支(未合并也可删)
重命名分支
git branch -m old-name new-name
:将本地分支old-name
改名为new-name
git branch -m new-name
:将当前分支改名
设置/查看远程跟踪
git branch -u origin/main
:设置当前分支跟踪远程origin/main
git branch -vv
:查看分支与远程分支对应关系
检查分支合并情况
git branch --merged
:列出已合并到当前分支的分支git branch --no-merged
:列出未合并到当前分支的分支
清理远程分支引用
git fetch -p
:删除已经在远程不存在的分支引用
切换分支(补充)
- 传统:
git checkout branch-name
- 推荐:
git switch branch-name
或git switch -c new-branch
(创建并切换)
git switch
是 Git 2.23 之后推荐的新命令,更加直观安全,专门用来切换分支。git checkout
功能更多(切换分支 + 恢复文件),所以有时候容易误操作。
- 切换到现有分支
git checkout <branch-name>
git switch <branch-name>
- 创建并切换分支
git checkout -b <branch-name>
git switch -c <branch-name>
- 从旧版本创建分支
git checkout -b <branch-name> <commit-hash>
git switch -c <branch-name> <commit-hash>
1 | # 1. 创建一个名为 "feature-a" 的新分支,并切换过去 |
1 | git_test$ git branch |
合并分支
- 基本合并
git merge <branch>
:将指定分支合并到当前分支
- 创建合并提交
git merge --no-ff <branch>
:即使可以快进,也强制创建一个合并提交
- 不提交,只合并到暂存区
git merge --no-commit <branch>
:合并修改到暂存区,但不生成合并提交
- 不修改工作区
git merge --no-ff --no-commit <branch>
:将修改放到暂存区,手动检查后再提交
- 快速前进合并
git merge --ff <branch>
:允许快进合并(默认行为)
- 指定合并策略
git merge -s recursive <branch>
:默认策略,处理普通分支合并git merge -s ours <branch>
:保留当前分支的内容,忽略合并分支内容git merge -s theirs <branch>
:保留被合并分支内容(需配合-X
使用)
- 解决冲突策略
git merge -X theirs <branch>
:在冲突时优先使用被合并分支的内容git merge -X ours <branch>
:在冲突时优先使用当前分支的内容
- 其他常用
git merge --abort
:放弃合并,回到合并前状态git merge --edit
:允许编辑合并提交信息git merge --quiet
:合并时只显示错误,不输出普通信息
💡 :
- 平时日常开发最常用的命令就是:
git merge <branch>
(普通合并)git merge --no-ff <branch>
(保持合并记录)git merge --abort
(遇到冲突想放弃合并)
当你在一个分支上完成了功能开发后,你需要将它合并回主分支,让你的修改成为项目的一部分。
- **
git merge <branch-name>
**:将指定分支的提交合并到当前分支。
1 | # 1. 确保你在主分支(main)上 |
Git 的合并有两种方式:
- 快进式合并(Fast-Forward):如果主分支在你创建分支后没有任何新的提交,Git 会直接将主分支指针移动到新分支的最新提交上,合并过程非常快。
- 三方合并(Three-Way Merge):如果主分支在你创建分支后有了新的提交,Git 会找到两个分支的共同祖先,然后将两个分支的修改进行合并。
解决冲突
在合并分支时,如果两个分支修改了同一个文件的同一部分,Git 无法自动合并,就会产生冲突(Conflict)。
处理冲突的步骤:
Git 会在冲突文件中插入特殊标记,告诉你哪些部分存在冲突。
1
2
3
4
5<<<<<<< HEAD
这是主分支的代码
=======
这是你的分支的代码
>>>>>>> feature-a你需要手动编辑文件,选择你想要保留的代码,并删除所有 Git 标记。
编辑完成后,使用
git add <file>
将文件标记为已解决冲突。最后,使用
git commit
完成合并。
撤消合并
- 取消合并(回到合并前状态)
1 | git merge --abort |
- 只接受
main
分支的版本
1 | git checkout --ours a.txt |
- 只接受 被合并的分支的版本
1 | git checkout --theirs a.txt |
注意:和git merge -s ours/theirs
的区别:全局合并策略用
文件冲突解决用 git checkout --ours / --theirs
协作工作流:Forking 工作流
开源项目和许多大型团队中,最常见的协作模式是 Forking 工作流。它的核心思想是:不直接向主仓库推送代码,而是先复刻(Fork)主仓库到你自己的账户下,然后通过拉取请求(Pull Request)或合并请求(Merge Request)来贡献代码。
1 | git clone https://github.com/your-username/project.git |
Git 的核心思维
Git 远不止是一堆命令。它代表着一种高效、安全的工作模式。
- 提交是原子性的:每次提交只做一件事。
- 分支是廉价的:大胆创建分支,并行开发,隔离风险。
- 先拉后推:在推送代码前,先拉取最新代码,以减少冲突。
- Fork 和 Pull Request:这是现代开源协作的最佳实践。
Git 的高级知识点
git stash
临时存放改动
- 作用:把当前工作区和暂存区的改动“存放”到一个栈(stash stack)里,并把工作区恢复干净(回到 HEAD)。
- 典型场景:
- 你正在写代码,突然需要切换分支去修复一个 bug,但又不想提交未完成的代码。
- 用
stash
把改动临时存起来,等修完 bug 再回来stash pop
。
1 | git stash # 保存当前改动 |
git stash -p
👉 逐个 hunk(代码块)决定要不要放进 stash。
1 | git_test$ git stash -p |
表示当前是 1 个 hunk(第 1/1 个),你需要选择如何处理。
🔹 参数含义:
在 -p
模式下,有这些操作选项:
- y → yes,把这个 hunk 存到 stash
- n → no,不 stash 这个 hunk,保留在工作区
- q → quit,退出,不 stash 剩下的 hunks
- a → all,stash 所有 hunks(后续都自动 yes)
- d → do not stash this and remaining hunks(后续都自动 no)
- e → edit,手动编辑 hunk 内容,只 stash 你想要的部分
- ? → 显示帮助说明
多个 stash 的管理
1 | git_test$ git stash list |
stash@{0}
- 栈顶(最新存的 stash)
- 内容:基于 main 分支 的
a98b738
提交时,你执行了git stash
。 add: eee
是当时的提交信息(说明 stash 是从哪个 commit 派生的)。
stash@{1}
- 更早存的 stash
- 内容:基于 main 分支 的
02f466d
提交时保存的改动。
查看某条 stash 的改动:
1
2git stash show stash@{0} # 简略差异
git stash show -p stash@{0} # 详细 diff应用(恢复)某条 stash:
1
2git stash apply stash@{0} # 应用但不删除
git stash pop stash@{0} # 应用并删除删除某条 stash:
1
git stash drop stash@{1}
清空所有 stash:
1
git stash clear
🔹 使用小技巧
多个 stash 的管理
你可以 stash 很多次,每次都会编号,stash@{0}
永远是最新的。带名字的 stash
默认git stash
会生成 “WIP on : “,但你可以自己写名字:1
git stash push -m "修复用户登录表单未提交完成"
这样
git stash list
更好理解。
分支分叉(Divergence)
1. 什么是分叉(分支不一致)?
本地分支和远程分支各自有独立提交,形成了如下结构:
1 | A---B---C (origin/main) |
即:
- 远程分支
origin/main
有提交 C; - 本地分支
main
有提交 D 和 E; - 两者的共同祖先是 B。
2. 如何检测分叉状态?
1 | git log --oneline --graph --all --decorate |
或使用定向对比:
1 | git log origin/main..HEAD --oneline # 查看本地领先部分 |
二、变基(Rebase)
1. 什么是 rebase?
git rebase origin/main
会将你本地的提交 “摘下” 来,依次应用到远程分支的最新提交之后,保持提交历史线性。
2. 何时使用 git pull --rebase
?
当你 本地有提交,远程也有新提交,希望保持干净历史(避免 merge commit)时。
设置默认行为(推荐):
1 | git config --global pull.rebase true |
三、变基过程中的冲突处理
1. 检测当前是否正在变基
1 | git status |
输出类似:
1 | 交互式变基操作正在进行中... |
说明你正在 rebase
流程中,遇到冲突被暂停。
2. 如何解决冲突?
手动处理冲突文件中标记的内容:
1 | <<<<<<< HEAD |
如需保留本地版本:
1 | git checkout --ours <file> |
或保留远程版本:
1 | git checkout --theirs <file> |
然后:
1 | git add <file> |
四、冲突解决完成后的操作
1. 继续变基流程
1 | git rebase --continue |
如果你不想继续这个补丁:
1 | git rebase --skip |
中止整个 rebase:
1 | git rebase --abort |
五、变基后的推送
变基改变了提交历史,因此 必须使用强推:
1 | git push --force origin main |
⚠️ 注意:强推有风险,确保你理解自己的操作,并协作团队知情。
六、常用可视化与对比命令
目的 | 命令 |
---|---|
查看所有分支图谱 | git log --oneline --graph --all --decorate |
查看本地领先 | git log origin/main..HEAD --oneline |
查看远程领先 | git log HEAD..origin/main --oneline |
查看变基基础点 | git merge-base HEAD origin/main |
七、实际工作中建议的工作流(最佳实践)
每次开发前:
1
2git fetch origin
git rebase origin/main每次提交前,确保你是最新状态,不要与远程冲突。
推送前检查差异,避免不必要的冲突和强推。
延伸学习点
知识点 | 说明 |
---|---|
git cherry | 查看哪些提交在某分支但不在另一个 |
git reflog | 查看 HEAD 的变更历史(特别适合误操作回退) |
git rerere | 自动记住冲突解决历史,减少重复劳动 |
git rebase -i | 交互式变基:可以 squash、edit、drop 提交 |
git stash | 暂存未完成的工作,避免干扰变基 |