配置相关

配置基础配置

这类命令用于配置 Git 的全局 或 局部参数,如用户名、邮箱,是使用 Git 的第一步,确保提交记录归属正确。

1
2
3
git config
# 常用格式
git config [--global] key value

例如

1
2
3
4
5
6
7
8
# 配置全局用户名
git config --global user.name "Your Name"
#配置全局邮箱
git config --global user.email "your@email.com"
# 查看所有配置
git config --list
# 配置默认编辑器为 VSCode
git config --global core.editor "code --wait"

该命令没有什么输出,但是可能会弹出交互的填写内容

  • --global:全局配置,保存在用户目录下的 .gitconfig 文件(如 Windows 的 C:\Users\你的用户名\.gitconfig,macOS 的 ~/.gitconfig),对所有仓库生效。

  • 不加 --global:局部配置,保存在当前仓库的 .git/config 文件,只对当前仓库生效(优先级高于全局)。

查看配置项

有时候不需要查看所有配置,只需要确认某个具体配置的值:

1
2
3
4
5
6
# 查看全局用户名
git config --global user.name
# 查看全局邮箱
git config --global user.email
# 查看默认编辑器配置
git config --global core.editor

也就是不带值,只带参数,就是查看

取消某个配置项

如果配置错了需要删除,用 --unset 参数

1
2
3
4
# 删除全局邮箱配置
git config --global --unset user.email
# 删除局部(当前仓库)的用户名配置(不加 --global 就是局部)
git config --unset user.name

配置提交时的换行符

Windows 和 macOS/Linux 的换行符格式不同,这个配置能避免提交时出现不必要的换行符变更

1
2
3
4
5
6
# Windows 系统推荐配置(自动转换为 LF 存储,CRLF 检出)
git config --global core.autocrlf true
# macOS/Linux 系统推荐配置(拒绝转换,检出保持 LF)
git config --global core.autocrlf input
# 禁止 Git 警告换行符问题
git config --global core.safecrlf false

初始化

git init 命令用于在目录中创建新的 Git 仓库。 git init 命令只用于在一个空目录中创建新仓库。

1
2
# 在当前目录初始化仓库
git init

该命令没有什么输出

操作远程仓库

克隆远程仓库

克隆远端仓库,将远程服务器上的 Git 仓库完整地复制到本地电脑,包括文件和提交历史等信息

1
2
3
4
# 克隆远程仓库到本地
git clone
# 常用格式
git clone [仓库地址] [本地目录名]

默认情况下,git clone 只会克隆远程仓库的默认分支,克隆指定分支使用下面的命令

1
git clone -b 分支名 仓库地址

支持 HTTPS 和 SSH 地址

克隆后,本地仓库会自动关联远程仓库,默认远程仓库名是 origin,可以直接操作

查看已配置的远程仓库

git remote 用于管理本地仓库与远程仓库的关联关系

如下命令显示远程仓库的名称和对应的地址

一般情况下,fetch 是拉取地址、push 是推送地址

1
git remote -v

多数情况下,fetch 和 push 地址相同;如果是多人协作的仓库,可能出现 fetch 地址是公共仓库,push 地址是自己的 fork 仓库的情况。

image-20260115172853613

添加远程仓库

为本地已存在的 Git 仓库关联一个远程仓库

1
git remote add 远程仓库名 仓库链接

如果需要修改远程仓库的别名,可以使用

1
2
# origin→github
git remote rename origin github

一个本地仓库可以关联多个远程仓库

1
2
3
4
# 同时关联 GitHub 和 Gitee
# 这是多平台同步代码的核心技巧
git remote add github git@github.com:yourname/your-project.git
git remote add gitee git@gitee.com:yourname/your-project.git

重命名远程仓库别名

修改已配置的远程仓库别名,适合别名命名不规范或需要区分多个远程仓库的场景。

1
2
# 格式:git remote rename [旧别名] [新别名]
git remote rename origin github

重命名仅修改本地的别名映射

移除远程仓库

解除本地仓库与指定远程仓库的关联关系

1
git remote remove 远程仓库名

这种情况知识打断了映射,对本地和远程没有任何影响

查看详细信息

显示指定远程仓库的详细信息,包括 URL 和跟踪分支。

1
git remote show 远程仓库名

可以看到展示的比 git remote -v 更详细的信息展示

image-20260115173719003

一般情况的格式是这样,简单解读一下

1
2
3
4
5
6
7
8
9
10
11
12
13
* remote origin
Fetch URL: git@github.com:yourname/your-project.git
Push URL: git@github.com:yourname/your-project.git
HEAD branch: main # 远程仓库的默认分支
Remote branches:
main tracked # 本地跟踪了远程的 main 分支
dev tracked # 本地跟踪了远程的 dev 分支
Local branches configured for 'git pull':
main merges with remote main # git pull 时本地 main 合并远程 main
dev merges with remote dev # git pull 时本地 dev 合并远程 dev
Local refs configured for 'git push':
main pushes to main (up to date) # git push 时本地 main 推送到远程 main(已同步)
dev pushes to dev (local out of date) # 本地 dev 有未推送的更新

修改远程仓库地址

更新已关联的远程仓库的 URL

一般在远程仓库迁移、HTTPS 地址改 SSH 地址、仓库地址拼写错误经常用到

1
git remote set-url 远程仓库别名 替换的新的远程仓库地址

而且一个远程仓库可以配置不同的 fetch(拉取)和 push(推送)地址

开源协作场景几乎是必备的了

1
git remote set-url --[fetch/push] [别名] [地址]
1
2
3
4
5
6
# 示例:拉取从官方仓库,推送到自己的 fork 仓库
git remote set-url --fetch origin https://github.com/official/project.git
git remote set-url --push origin git@github.com:yourname/project.git
# 执行 git remote -v 会看到:
# origin https://github.com/official/project.git (fetch)
# origin git@github.com:yourname/project.git (push)

刷新远程分支列表

任何修改都建议手动刷新再次查看

1
git remote update 仓库别名

不加仓库别名, git remote update就是刷新所有远程仓库

为远程仓库添加新的关联链接

这种情况适用于多平台的远程仓库都需要进行维护的情况

Git 本身支持为同一个远程别名配置多个 push 地址,但 fetch 地址只能有一个

1
git remote set-url --add --push [别名] [第二个推送地址]

注意 Git 拉取代码(git pull/git fetch)时,只会从 originurl 字段(第一个地址)拉取

提交相关(工作区 暂存区相关)

这类命令用于管理文件的追踪状态,将文件从工作区提交到暂存区,或撤销修改,是日常开发最常用的命令组。

复习一下 Git 的三区

区域 作用 对应操作命令
工作区 你本地正在编辑的文件目录(看得见、改得到的文件) 日常编辑、保存文件
暂存区 临时存放待提交的修改,是“待提交清单”,是工作区和版本库的中间层 git add 写入、git reset 撤销
版本库 Git 本地仓库的核心(保存所有提交记录、分支、版本信息) git commit 提交到版本库

实际上,远程仓库是版本库的 “远程副本”,我们每次只不过是通过 git push 将本地版本库的提交推送到远程。

将文件从工作区添加到暂存区

git add 是连接工作区和暂存区的核心命令,将工作区的修改(新增、修改、删除)添加到暂存区,为后续提交做准备。

基础命令,添加全部文件

1
git add .

实际上这个命令的形式有很多

命令 作用
git add 文件名 添加指定单个文件到暂存区(比如 git add index.html
git add 目录名/ 添加指定目录下所有修改的文件到暂存区(比如 git add src/
git add -A / git add --all 添加整个仓库所有修改的文件(包括当前目录外的文件),等价于 git add . + git add -u
git add -u / git add --update 仅添加已追踪文件的修改 / 删除,不包含新增文件,到暂存区
git add -p / git add --patch 交互式添加:逐块选择文件的修改内容(比如一个文件改了 3 处,只提交其中 2 处)

关于文件追踪状态

  • 未追踪文件:新创建的文件,Git 从未记录过,git status 显示为 ??
  • 已追踪文件:曾经被 git add + git commit 过的文件,后续修改会被 Git 识别;

git add 的本质是 “将文件纳入 Git 追踪”(首次添加)或 “更新暂存区的文件快照”(追踪文件的修改)。

撤销工作区修改

撤销工作区修改的核心是,将工作区的文件恢复为 “暂存区 / 版本库” 中的快照,一般情况下,使用 git restore(新版推荐)和 git checkout(旧版兼容)。

1
2
3
4
5
6
7
8
9
10
11
# 撤销工作区未暂存的修改,也就是用暂存区的文件覆盖工作区
git restore <文件名>

# 撤销暂存区的修改,用版本库文件覆盖暂存区,也就是将暂存区修改退回工作区
git restore --staged <文件名>

# 撤销当前目录下所有文件的工作区修改
git restore .

# 等价于 git restore <文件名>
git checkout -- <文件名>

撤销的是 “未提交的工作区修改”,如果修改已提交到版本库,无法用此命令撤销(需用 git reset/git revert

撤销后工作区的修改会永久丢失,一般执行前先用 git stash 暂存修改

emmm,二编,再说一下git restore --staged,它是暂存区恢复为版本库最新状态,工作区的修改完全保留

查看三区状态

git status 用于查看工作区、暂存区的状态,是详细查看其中文件状态的命令

1
git status
image-20260116175917114
  • 解读一下其中的输出含义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    On branch main
    Your branch is up to date with 'origin/main'.

    Changes to be committed: # 暂存区的文件(待提交)
    (use "git restore --staged <file>..." to unstage)
    modified: README.md

    Changes not staged for commit: # 工作区的修改(未暂存)
    (use "git add <file>..." to update what will be committed)
    (use "git restore <file>..." to discard changes in working directory)
    modified: src/index.js

    Untracked files: # 未追踪文件(新创建)
    (use "git add <file>..." to include in what will be committed)
    docs/guide.md

简洁查看(-s=short),一行一个文件

1
git status -s
image-20260116175926443
  • 解读一下其中的输出含义

    1
    2
    3
    M  README.md      # M=暂存区修改(已追踪)
    M src/index.js # 前无标记=工作区修改(未暂存)
    ?? docs/guide.md # ??=未追踪文件

显示当前分支名加状态,git status -b -s要更常用一些

1
git status -b

git reset 恢复工作区的代码

首先,HEAD 指针是Git 的 “当前位置标记”,指向当前分支的最新提交,常用的HEAD~1 表示 “HEAD 指向的提交的上一个提交”

然后,git reset 的核心本质是,移动当前分支的 HEAD 指针到指定的提交版本,同时根据参数决定是否同步更新暂存区和工作区

1
git reset [参数] [目标版本]
  • --soft:仅移动 HEAD,不碰暂存区 / 工作区
  • --mixed默认,移动 HEAD + 同步暂存区
  • --hard:移动 HEAD + 同步暂存区 + 同步工作区

提交代码

将暂存区文件提交到版本库,是将暂存区的修改 “正式提交” 到本地版本库的命令,每个提交会生成一个唯一的 commit-id,代表一个版本。

1
2
3
4
5
6
7
8
9
10
11
# 基础命令,别用,会打开默认编辑器,输入提交信息后提交
git commit

# 直接在命令行指定提交信息,这个很熟悉了
git commit -m "提交信息"

# 跳过暂存区,直接将已追踪文件的修改提交(快速提交已追踪文件),也就是忽略了新增的文件
git commit -am "提交信息"

# 修正最后一次提交
git commit --amend

注意,–amend 会修改最后一次提交的 commit-id,已推送到远程的提交不建议用!

那么,比较常用的 修改提交信息 如何做呢?

  • 如果只是最后一次提交的信息写错了,用 git commit --amend 即可快速修改

    1
    git commit --amend -m "新的提交信息:feat: 修复用户登录验证逻辑"

    如果是 已推送到远程的提交,这时候就不建议修改了,因为修改后需要强制推送

  • 修改任意一次提交操作涉及到变基,谨慎,需要用 git rebase -i(交互式变基),这会重写提交历史

    绝对不要在公共分支(如 main/dev)上执行变基操作,会导致团队成员的提交历史混乱;

那么修正最后一次提交,添加文件呢?

  • git commit --amend 即可将漏加的文件补充到最后一次提交中,无需新建提交

    1
    2
    3
    4
    5
    6
    git add 漏加的文件  # 或 git add .(添加所有未提交的修改)

    # 方式1:仅补充文件,保留原提交信息
    git commit --amend --no-edit
    # 方式2:补充文件 + 修改提交信息
    git commit --amend -m "新的提交信息:feat: 新增个人中心(补加头像组件)"

git reset 撤销提交

然后再看如何撤销提交

如果提交只是 commit 了,还没有 push,可以用 git reset 撤销,不会影响任何人。

1
2
3
4
5
# 一般要查看最近提交,确认提交的哈希码
git log --oneline

# 撤销最后一次提交,保留工作区/暂存区修改
git reset --soft HEAD~1

git reset --soft是把提交的内容回退到暂存区,所以说暂存区,工作区的修改会完全保留,只是这次 commit 没了

那么,如果我希望清空暂存区,可以使用 git reset --mixed,而且--mixed 是默认参数,可省略

1
2
# 撤销最后一次提交,将提交的内容退回工作区,暂存区被清空
git reset HEAD~1 / git reset --mixed HEAD~1

那么,如果我希望最后一次提交的时候,彻底删除修改,也就是同时清空工作区和暂存区的所有修改,代码回到上一次提交的状态,可以使用git reset --hard

1
git reset --hard HEAD~1

git revert撤销已推送到远程的提交

这类提交已同步到远程仓库,直接用 git reset 会导致本地和远程提交历史不一致

一般情况下,使用git revert(反向提交),它不会删除原有提交,而是创建一个新提交抵消旧提交的修改,安全且不影响他人。

撤销最后一次远程提交

1
2
3
4
5
6
7
8
9
# 创建一个新的提交,该提交的内容是 “抵消最后一次提交的所有修改”,原有提交仍保留在历史中;
# 会打开编辑器让你写撤销的提交信息
git revert HEAD

# 当然也可以直接指定撤销的提交信息
git revert HEAD -m "revert: 撤销错误的分页功能提交"

# 最后推送
git push

如果要撤销的不是最后一次远程提交,需指定该提交的 commit-id,这个要慎用

1
git revert d4e5f6g -m "revert: 撤销错误的分页功能提交"

推送代码到远程仓库

git push 是将本地版本库的提交推送到远程仓库的命令,完成 “本地→远程” 的同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 基础命令,推送当前分支到远程跟踪分支
git push

# 指定远程和分支推送,就是天天写的 git push origin main
git push 远程别名 本地分支:远程分支

# 推送并建立跟踪关系,首次推送分支时用,后续可直接用 git push/git pull,这样就无需指定远程和分支
git push -u 远程别名 本地分支

# 强制推送,覆盖远程分支
git push --force / git push -f

# --force-with-lease,比 -f 更安全,因为使用此参数推送,如果远端有其他人推送了新的提交,那么推送将被拒绝
# 在使用 git push --force-with-lease 命令被拒绝时,你需要 fetch 仓库,然后确认其他人是否对此分支有新的修改,如果没有,你才可以继续强制推送。
git push --force-with-lease 远程别名 分支名

这个命令会输出提交的情况,建议使用 SSH 关联 Github 链接,要不然很容易网络问题传不上去

从Git追踪中删除

仅从Git追踪中删除,保留本地文件

1
git rm --cached <文件名/目录名>

也可以

1
git rm --cached -r .

批量停止追踪已加入 .gitignore 的文件

暂存工作区未提交的修改

这是开发中临时切换分支、处理紧急任务时的高频操作,而且是经常容易被忽视的很重要的一步

git stash 直译是 “储藏、暂存”,核心作用是:将工作区中未提交的修改(包括暂存区 + 工作区)临时保存到 Git 的 “储藏栈” 中,让工作区恢复到干净的状态(与 HEAD 提交一致)

什么意思,就是例如,我正在开发一个功能,此时我本地的工作区有未提交的修改,但是现在我需要临时切换分支处理一些bug,但是我为了不提交未完成的代码,使用了 git stash 暂存了我的本地修改,就能干净的切换分支

先明确一个前提,git stash 只会暂存已追踪文件的修改 + 暂存区的修改,未追踪文件(新创建、从未提交过的文件)默认不会被暂存,需加参数指定。

1
2
3
4
5
6
7
8
9
10
11
# 基础暂存
git stash

# 带备注的暂存:为暂存记录添加自定义备注
git stash save "备注信息"

# 暂存已追踪文件 + 未追踪文件
git stash -u / git stash --include-untracked

# 暂存所有修改,看忽略文件是否有被暂存的必要
git stash -a / git stash --all

暂存的修改会以 “栈” 的形式存储(先进后出,最新的在栈顶),用该命令查看所有暂存记录:

1
git stash list

当然也可以查看暂存记录的详细内容

1
2
3
4
5
# 查看暂存记录,默认显示的是精简的信息
git stash show stash@{0}

# 查看详细修改内容
git stash show -p stash@{0}

这里最后再讲一些有关的 git 知识

暂存的修改并不是存在 工作区 / 暂存区 / 版本库 中,而是存储在 Git 仓库的 .git/refs/stash 文件和 .git/objects 目录中,他们都属于 Git 的 引用存储,本质是一个特殊的提交对象(commit object),包含了工作区和暂存区的状态。

而且 stash 是全局的,不是绑定到某个分支,你在 dev 分支暂存的修改,可在 main 分支恢复

恢复暂存的修改

恢复暂存的修改到工作区,有两种核心方式:

1
2
3
4
5
# 恢复指定暂存记录到工作区,暂存记录仍保留在储藏栈中
git stash apply

# 恢复指定暂存记录到工作区,同时删除该暂存记录
git stash pop

删除暂存记录

1
2
3
4
5
# 1. 删除指定暂存记录
git stash drop stash@{1}

# 2. 清空所有暂存记录(确认不需要后再执行)
git stash clear

撤销 stash 的恢复操作

既然恢复后只是把 stash 里的修改放回工作区,直接重新暂存即可回到 “恢复前” 的干净状态:

1
2
# 恢复 stash 后想撤销 → 重新暂存这些修改(备注可和之前一致)
git stash save "撤销恢复:重新暂存用户列表开发中的修改"

执行后,工作区会回到干净状态,相当于 “撤销了恢复操作”。

删除单个文件

Git 命令删除

1
git rm <文件名>

Git 中删除的文件可通过版本库恢复,因为删除操作会被记录在提交历史中,但前提是你删除后提交了

1
2
3
git log --oneline

git restore --source=a1b2c3d src/被删除的文件.js

分支管理

先搞懂分支的本质

  • Git 的分支本质上是指向提交记录(commit)的可变指针,创建分支只是创建了一个新指针
  • 仓库默认的分支是 main(新版),每次提交,当前分支的指针会自动指向最新的提交记录。
  • HEAD 是一个特殊指针,指向当前正在工作的分支,也就是,我们现在在哪个分支上

一般情况下,一个分支代表一个状态,例如,以一个稳定的版本为根,我希望从中单独出来一个分支去做oauth2,我就开一个分支 feature/oauth2,然后团队的其他成员继续在 main 分支上进行维护,我的 oauth2 分支开发完成之后,就可以和 main 分支进行合并,然后跟上团队的进度,而且不会因团队的开发而影响自己的功能开发

查看分支

你在哪个分支查看的就是哪个分支的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看本地所有分支(当前分支前会有 * 标记)
git branch

# 查看本地+远程所有分支
git branch -a

# 查看分支的详细信息(最后一次提交、提交说明等)
git branch -v

# 查看已合并到当前分支的本地分支(可用于清理无用分支)
git branch --merged

# 查看未合并到当前分支的本地分支(这种分支谨慎删除)
git branch --no-merged

例如

image-20260118170144513
  • 远程分支在显示时会以 remotes/远程仓库名/分支名

创建新分支

1
2
3
4
5
6
7
8
9
10
11
# 基于当前分支创建新分支(仅创建,不切换到新分支)
git branch 新分支名

# 基于指定提交记录/标签/远程分支创建新分支
git branch 新分支名 提交ID/标签名/远程分支名

# 创建分支并直接切换到该分支(最常用)
git checkout -b 新分支名

# 新版 Git 推荐的创建+切换命令(替代 checkout)
git switch -c 新分支名

创建分支时,新分支会继承源分支的所有代码和提交记录。

删除分支

1
2
3
4
5
6
7
8
9
10
# 删除本地分支(仅能删除已合并的分支,未合并会报错)
git branch -d 分支名

# 强制删除本地分支(不管是否合并,谨慎使用)
git branch -D 分支名

# 删除远程分支(核心命令,注意格式)
git push 远程仓库名 --delete 分支名
# 简写形式(远程仓库默认是 origin),:分支名 和 --delete 分支名 等价
git push origin :分支名

-d 是 “安全删除”,会检查分支是否已合并,避免误删;-D 是强制删除,仅在确认分支代码无用时使用。

删除远程分支后,其他协作者需要执行 git fetch --prune 来同步远程分支的删除状态(否则本地仍会显示已删除的远程分支)。

切换分支

1
2
3
4
5
6
7
8
9
10
# 切换到已存在的本地分支
git checkout 分支名

# 新版 Git 推荐的切换命令(更语义化)
git switch 分支名

# 切换到上一个分支(快速在两个分支间切换)
git checkout -
# 或
git switch -

切换分支前,需确保当前分支的修改已提交 / 暂存,否则 Git 会拒绝切换,如果想强制切换(这样会放弃未提交修改),可加 -f 参数,谨慎使用。

重命名分支

1
2
3
4
5
6
7
8
# 重命名当前分支
git branch -m 新分支名

# 重命名指定分支
git branch -m 旧分支名 新分支名

# 强制重命名(如果新分支名已存在)
git branch -M 旧分支名 新分支名

重命名远程分支的流程:先重命名本地分支 → 删除远程旧分支 → 将新命名的本地分支推送到远程。

  • ```bash # 示例:重命名远程 dev 为 dev-v2 git branch -m dev dev-v2 # 本地重命名 git push origin –delete dev # 删除远程旧分支 git push origin dev-v2 # 推送新分支到远程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20



    ### 合并两个分支

    分支的最终目的是合并代码,这是分支操作的核心场景

    ![image-20260112103640409](常用Git命令与知识总结/image-20260112103640409.png)

    核心命令是`git merge`,但是合并肯定不止涉及到这样的一个命令

    ```bash
    # 切换到目标分支(如合并到 main)
    git switch main

    # 将 dev 分支合并到当前 main 分支
    git merge dev

    # 合并时禁用快进模式(保留分支合并记录,推荐)
    git merge --no-ff dev

如果目标分支是源分支的直接祖先,也就是没有产生分叉,Git 会直接将目标分支指针指向源分支的最新提交,不会创建新的合并提交。

合并时可能出现冲突,Git 会标记冲突文件,需手动解决后提交。

同步远程分支到本地

也就是更新本地对远程分支的认知情况,这么说太傻逼了,算了,也就是让本地 Git 知道远程仓库的最新分支状态,而非直接拉取代码

1
2
3
4
5
6
# 获取远程仓库的所有更新(分支、标签等),但不合并到本地分支
git fetch [远程仓库名]

# 同步远程信息 + 清理本地已失效的远程分支引用(推荐)
git fetch --prune origin
# 简写:git fetch -p origin

fetch 是 “只读操作”,只会下载远程的分支、提交记录等元数据到本地,不会修改你当前工作区的代码,也不会合并到本地分支,非常安全。和 pull 是完全两种的操作,因为 fetch是仅同步远程信息,不修改本地代码

团队协作中,若其他同事删除了远程分支(比如合并后的 feature 分支),你的本地仍会显示 remotes/origin/已删除分支,导致分支列表混乱。

--prune 会:

  • 删除本地那些 “远程已不存在” 的远程追踪分支引用;
  • 只保留远程仓库当前存在的分支信息。

关联本地分支和远程分支

一般我们习惯在创建本地分支的时候直接关联

1
2
3
4
# 基于远程 origin/dev 分支创建本地 dev 分支,并自动关联
git checkout -b dev origin/dev
# 新版 Git 推荐写法
git switch -c dev origin/dev

为已存在的本地分支关联远程分支

1
2
3
4
5
# 关联本地分支到指定远程分支
git branch --set-upstream-to=origin/远程分支名 本地分支名

# 简写版(效果同上)
git branch -u origin/远程分支名 本地分支名

查看分支的关联状态

1
2
# 查看所有本地分支的远程关联信息
git branch -vv

变基分支

变基,核心含义,就是改变基底,例如,图上有这样的两个分支 masterfeature,其中feature是在 master分支的提交点 B 处从 master 上拉出的新分支,feature上有 C,D 两个提交

image-20260117173326322

此时,如果切换到feature分支上,执行 rebase 命令进行变基,例如我们在自己的分支 feature 上开发了一段时间了,准备从主干 master 上用 git pull --rebase 拉一下最新改动,就会这样

可以看到,本来 feature 上的提交是基于 B 提交的,也就是根是 B,变基之后的C,D提交的根变成了 master 分支上的最新提交 M

image-20260117173402385

这也就是为什么,那么多人喜欢在 git pull 加个--rebase,避免 git 提交记录上存在一堆的 merge xxx 这种大量合并冲突产生的无效节点

那么,变基的核心是改变提交的 基础,也就是根节点,简单来说:

  • 变基会把一个分支的所有提交,“搬移” 到另一个分支的最新提交之后,形成一条线性的提交历史
  • 对比合并(Merge):
    • merge 是 “合并分支”,会创建一个新的合并提交,保留分支的分叉记录(提交历史呈 “分叉状”);
    • rebase 是 “重写提交”,把分支的提交重新基于目标分支的最新状态,提交历史呈 “线性”,更整洁。

基础变基就是本地分支变基到目标分支,这是最常用的场景,例如将当前功能分支变基到主分支的最新版本,避免合并时产生多余提交。

1
2
3
4
5
# 切换到功能分支
git switch feature

# 将 feature 分支变基到 main 分支(核心命令)
git rebase main

变基过程中,若提交和目标分支有冲突,Git 会暂停变基,提示你解决冲突:

1
2
3
4
5
6
7
8
9
10
# 解决完冲突了,git add 了,可以

# 继续变基流程
git rebase --continue

# 若想放弃本次变基,执行:
git rebase --abort

# 若想暂停变基(极少用),执行:
git rebase --quit

还有一些 其他实用变基命令

1
2
3
4
5
# 查看变基的帮助信息
git rebase --help

# 查看当前变基的状态
git rebase --status

那么,总结一下, 推荐使用变基的场景

  • 本地功能分支开发中:将功能分支变基到主分支的最新版本,保持提交历史线性,避免合并时产生大量合并提交;
  • 整理本地提交历史:用 git rebase -i 合并琐碎提交、修改提交注释,让提交记录更清晰;
  • 团队协作的功能分支:在合并到主分支前,先变基到主分支最新版,解决冲突后再合并(很多团队的规范)。

公共分支禁止使用变基:绝对不要对已推送到远程的公共分支执行变基!因为公共分支被多人使用,变基会重写提交历史,导致其他同事的本地分支和远程分支严重冲突;

变基可以背被恢复,如果变基出错,可通过 git reflog 找到变基前的 commit ID,执行 git reset --hard <变基前的ID> 恢复;

交互式变基

git rebase -i(interactive,交互式)是最强大的变基用法,可用于:

  • 合并多个提交为一个(简化提交历史);
  • 修改历史提交的注释;
  • 删除 / 重排历史提交;
  • 编辑历史提交的内容。

核心命令就是

1
git rebase -i  提交id等参数

例如

1
2
3
4
5
# 对最近 N 个提交进行交互式变基(N 为数字,比如 3)
git rebase -i HEAD~N

# 对指定 commit ID 之后的提交进行交互式变基(更精准)
git rebase -i <commit ID>

挑选单个/多个提交应用到当前分支

cherry-pick 是 Git 中 精准复用代码 的核心命令,能将其他分支的单个或多个提交 复制 到当前分支,而非合并整个分支

  • 复制提交的含义是原分支的提交仍保留,当前分支会生成一个 “内容相同但 commit ID 不同” 的新提交。

因为很多情况下,例如,我们会只把 hotfix 分支的一个 Bug 修复提交应用到 dev 分支

1
2
3
4
5
# 将指定 commit ID 的提交应用到当前分支
git cherry-pick <commit ID>

# 带注释版,应用提交时,保留原提交的注释(默认行为,可省略)
git cherry-pick -x <commit ID>

当日也可以挑选多个提交

1
2
3
4
5
6
7
# 方式1:挑选多个独立的提交(按顺序应用)
git cherry-pick <commit ID1> <commit ID2> <commit ID3>

# 方式2:挑选连续的提交(左开右闭,A 不包含,B 包含)
git cherry-pick <commit A>..<commit B>
# 包含 A 和 B 的连续提交
git cherry-pick <commit A>^..<commit B>

cherry-pick 的中断与恢复

1
2
3
4
5
6
7
8
# 应用提交时出现冲突,放弃本次 cherry-pick
git cherry-pick --abort

# 解决冲突后,继续完成 cherry-pick
git cherry-pick --continue

# 暂停 cherry-pick,极少用
git cherry-pick --quit

历史查看

这些命令不太推荐在git中直接查看,因为这些命令的查看效果远远不如现在的各种代码管理软件等效果好

查看提交历史

这是详细版本的查看提交历史,也就是不带参数的

1
git log

无参数的 git log 会按提交时间从新到旧显示所有提交,每条提交包含:

  • commit <commit ID>:唯一标识提交的哈希值(完整 40 位,可简写前 7 位);
  • Author:提交者(用户名 + 邮箱,对应 git config 配置);
  • Date:提交时间;
  • 提交说明(commit message)

一般情况下,我们会加这些参数使得变得更加方便查看

1
git log [--oneline/--graph/--stat]
参数 作用 示例
--oneline 精简输出:每行显示 7 位 commit ID + 提交说明(最常用) git log --oneline
--graph 图形化显示分支合并 / 分叉历史(配合 –oneline 效果最佳) git log --graph --oneline
--stat 显示每个提交修改的文件列表 + 增删行数统计(看修改范围) git log --stat
--patch/-p 显示每个提交的具体代码修改内容(diff) git log -p
-n <数字> 只显示最近 N 次提交(比如最近 3 次) git log -3 --oneline
--author 只显示指定作者的提交(筛选个人 / 同事提交) git log --author="Your Name"
--since/--until 按时间筛选(比如最近 7 天、指定日期前) git log --since="7 days ago"
--grep 按提交说明关键词筛选(比如找 “修复 bug” 的提交) git log --grep="修复bug"

查看指定文件的提交历史

1
2
3
4
5
6
7
8
9
10
11
# 核心命令:查看指定文件的所有提交历史
git log 文件名

# 示例
git log README.md

# 精简显示 + 显示该文件的具体修改内容
git log --oneline -p README.md

# 还可以加上分支
git log dev --oneline README.md

查看指定提交的详细修改内容

git log 是看 提交列表,git show 是看 单个提交的详情,核心用于深入分析某一次提交。

1
git show 提交ID

也可以带分支

1
git show 提交ID/分支名

也可以这样,查看指定提交中某个文件的修改

1
2
# 查看指定提交中某个文件的修改
git show 8a7b9c0:README.md

git show 会显示该提交的:

  • 基本信息(commit ID、作者、时间、提交说明);
  • 该提交的代码修改(diff),用 + 表示新增,- 表示删除。

查看操作日志

查看 Git 操作日志,也就是能看所有 Git 操作记录,是恢复误操作的核心命令

1
2
3
4
git reflog

# 精简查看(只显示关键信息)
git reflog --oneline

例如,误执行 git reset --hard 回滚了提交,用 reflog 恢复:

1
2
3
4
5
# 步骤1:找到重置前的 commit ID
git reflog

# 步骤2:强制重置回该 commit ID 的状态
git reset --hard <重置前的commit ID>

注意,git reflog 记录的是本地仓库的操作,不会同步到远程;而且操作日志默认保留 90 天,超过后会被清理;

一些Git的使用规范

在维护一个本地代码库,多个远端云代码库的情况下,Git 的使用规范极为重要

所以在最后,列出一些Git使用规范,和帮助我养成良好的 Git 使用习惯的一些记录

Atomic Commits 勤提交

也就是原子化提交,个人总结为勤提交

每个 commit 只做一件事,避免大杂烩式的提交

如果发现某个功能出错了,你可以精准地 revert(撤销)它,而不会波及其他功能。

Conventional Commits 规范提交信息

一个好的提交记录应该是 自解释 的。推荐采用目前主流的 Conventional Commits (约定式提交) 规范。

1
2
<type>(<scope>): <subject>
<类型>:(简短描述) :描述正文
类型 (Type) 说明
feat 新功能 (feature)
fix 修补 bug
docs 文档修改 (documentation)
style 格式修改(不影响代码运行的变动,如空格、分号等)
refactor 重构(既不是新增功能,也不是修改 bug 的代码变动)
perf 优化相关,比如提升性能、体验
test 增加测试
chore 构建过程或辅助工具的变动

分支管理策略

在处理多种分支情况时,建议遵循一套清晰的逻辑。

核心分支

  • main/master: 永远保持稳定,永远代表生产环境的代码,永远是高可用,少问题的稳定版本
  • develop: 开发主干,最新的特性都在这里汇合。

临时分支需要用完即删

  • feature/*: 从 develop 分出,用于开发新功能。命名如 feature/login-module
  • hotfix/*: 从 main 分出,用于紧急修复生产环境的 bug。命名如 hotfix/issue-102
  • release/*: 准备发布前的测试分支。

不要在 main 分支上直接写代码。

保持同步

保持同步才能最少的避免合并冲突的出现

每天开始工作前需要

1
2
git checkout main
git pull --rebase # 拉取最新代码(推荐用rebase代替merge)

功能开发中途同步:

1
2
git fetch origin    # 获取远程更新
git rebase origin/main # 变基到最新代码

多远端云代码库管理

协作项目,养成 Push 前先 Pull 的习惯。

使用 git pull --rebase 可以让你的提交历史保持线性,避免无谓的“Merge branch ‘xxx’ of …”这种干扰信息。

如果你需要同时将代码推送到不同的平台,例如 GitHub 备份和公司内部云效,可以参考以下技巧。

首先最重要的是给不同的远端命名,不要都叫 origin

1
2
git remote add github https://github.com/user/repo.git
git remote add gitlab https://gitlab.company.com/user/repo.git

可以为一个远程名称设置多个 URL,这样一次 git push 就能同步到指定的多个 URL 代码库,通常情况下,我们各个平台叫各个平台的远端仓库名,origin 则通过这种方式统一上述的推送地址

但是这种情况不是很推荐使用,尽管它很方便

1
2
git remote set-url --add --push origin https://github.com/user/repo.git
git remote set-url --add --push origin https://gitlab.company.com/user/repo.git

查看所有远端仓库git remote -v

不同分支对应不同远端是好习惯吗?

我不这么认为,因为分支不是这么用的

在 Git 中,分支应该代表功能 (Feature)发布版本 (Release)环境 (Environment),而不是代表“目的地”。

很简单,这样设想一下,如果 branch-A 只推送到 GitHub,branch-B 只推送到 GitLab,久而久之这两个分支的代码逻辑会越来越偏离。当你需要合并功能时,会面临大量的冲突。

分支与多远端的正确配合的思路应该是这样的

  • 确定一个远端作为主协作库
    • 在主库上进行 PR、Code Review 和 Issue 管理。
    • 其他远端仅作为只读镜像备份

不同远程仓库的配置文件如何妥善管理

当你维护一个私有库(公司)和一个公开库(GitHub)时,最怕把私有密钥或者不能被公开的配置文件中的明文信息推送到公开库,这样就爆炸了

  • 使用 .gitignore 屏蔽敏感文件,要确保包含敏感信息的配置文件本身不会被 Git 追踪,所以说,在敏感信息被首次提交之前,就是使用.gitignore 忽视掉

  • 创建模板文件,不在仓库中直接存放真实的配置文件,而且你填充好的,真实的配置文件需要被.gitignore忽略掉,然后创建一个配置模板文件,在仓库中流转的时候使用这个配置模板,模板中只保留配置项的 key,value 用占位符替代。

    1
    2
    3
    4
    5
    # 数据库配置模板
    db.username=YOUR_DB_USERNAME
    db.password=YOUR_DB_PASSWORD
    # API密钥模板
    api.secret=YOUR_API_SECRET
  • 如果团队够大,不建议开发者各自维护本地配置,可使用git-crypt这种加密工具统一管理敏感配置