需要深入理解的内容
一、git本地仓库
下面是Git本地仓库目录结构
.git/
├── objects/ # 对象存储目录
├── refs/
│ ├── heads/ # 本地分支
│ │ ├── main
│ │ └── feature
│ ├── remotes/ # 远程跟踪分支
│ │ └── origin/
│ │ ├── main
│ │ └── dev
│ └── tags/ # 标签
└── index # 暂存区
1、 暂存区 (staging Area / Index)
对应.git/index文件(一个二进制文件),记录了文件的路径、权限、以及指向对象存储区中对应blob 对象的哈希值
记录了即将提交的文件快照的元数据,包括:
文件名
文件模式(权限)
文件内容的SHA-1哈希值(指向.git/bject/中的,blob对象)
文件时间戳等缓存信息(用于性能优化)
列出暂存区中的所有文件及其状态
git ls-files --stage
查看暂存区与工作区的差异
git status
2、对象存储目录
对应.git/objects/,存放所有 Git 对象 (blob、tree、commit、tag)。
(1) 提交对象 (commit):记录作者、时间、提交信息,以及指向树对象的指针。
(2) 树对象 (tree):记录目录结构和文件列表,每个文件对应一个数据对象。
(3) 数据对象 (blob):存储文件的具体内容。
当执行 git add 时,Git 会将文件内容压缩成一个 blob 对象,并存入 .git/objects/ 中,同时更新 .git/index 文件(暂存区)来指向这个新对象
当执行 git commit 时,Git 会创建 tree 对象和 commit 对象,也存入 .git/objects/。
查看对象存储区内容
git rev-list --objects --all
3、远程跟踪分支 (Remote-tracking branches)
存储路径:.git/refs/remotes/<远程仓库名>/<分支名>
例如,远程仓库名为 origin,分支名为 main,则存储在:
.git/refs/remotes/origin/main
作用:记录远程仓库中对应分支的最新提交哈希值,用于跟踪远程仓库的状态。
特点:
只读 (不能直接通过 git checkout 切换并修改)。
通过 git fetch 或 git pull 自动更新。
属于本地仓库的一部分,但不会影响工作区。
4、本地分支 (Local branches)
存储路径:.git/refs/heads/<分支名>
例如,当前工作分支名为 main,则存储在:
.git/refs/heads/main
作用:记录本地分支的最新提交哈希值,指向通过 git commit 创建的本地提交。简单来说,本地分支是指向对象存储中某个提交对象的指针。
当切换到某个分支时,Git 会读取该分支指向的提交对象,然后递归地找到对应的树对象和数据对象,最终将文件内容还原到工作区。
特点:
可读写(可以通过 git checkout、git merge 等操作修改)。
通过 git commit、git merge、git rebase 等命令更新。
与工作区直接关联(git checkout 切换分支时,工作区文件会同步更新)。
二、git 中 revert、 restore、 reset、 rebase 的详细用法介绍
1、revert、 restore、 reset、 rebase的主要区别
git revert
操作内容:撤销某次提交的更改,并创建一个新的提交
作用范围:工作区、暂存区、历史记录
是否危险:否(安全)
使用场景:撤销已推送到远程仓库的错误提交。
git restore
操作内容:恢复工作区或暂存区的文件到某个历史状态
作用范围:工作区、暂存区
是否危险:否(安全)
使用场景:放弃工作区的修改,或从暂存区取消暂存文件。
git reset
操作内容:将当前分支的 HEAD 指针移动到指定的提交,并可选择地重置暂存区和工作区
作用范围:历史记录、暂存区、工作区
是否危险:是(危险)
使用场景:撤销本地的、尚未推送的提交,或彻底清理工作区。
git rebase
操作内容:将一个分支的提交"移植"到另一个分支的顶端,或合并/修改历史提交
作用范围:历史记录
是否危险:是(危险)
使用场景:保持线性历史,合并分支前整理提交,或修改最近的提交信息。
2、revert、 restore、 reset、 rebase的各自用法介绍
2.1、git revert (安全撤销)
核心思想:
创建一个新的提交来抵消之前某个提交的更改。它不会删除或修改历史记录中的任何提交,因此是最安全的撤销方式,尤其适用于已经推送到远程仓库的提交。
用法:
撤销指定的单个提交
git revert <commit-hash>
撤销一系列连续的提交(会为每个提交创建一个新的 revert 提交)
git revert <oldest-commit-hash>..<newest-commit-hash>
撤销提交,但不自动创建新的提交(用于手动合并多个 revert 提交)
git revert -n <commit-hash>
示例:
假设提交历史为 A -> B -> C -> D,你想撤销提交 B。
git revert B
执行后,历史变为 A -> B -> C -> D -> E,其中 E 是一个新的提交,其内容与 B 提交的更改完全相反。
2.2、git restore (文件级恢复)
核心思想:
专注于文件级别的操作,用于恢复工作区或暂存区中的文件到某个状态。它不操作提交历史,因此非常安全。
用法:
1. 恢复工作区的文件到暂存区(或 HEAD)的状态(放弃工作区的修改)
git restore <file-name>
2. 恢复工作区的文件到指定提交的状态
git restore --source=<commit-hash> <file-name>
3. 将暂存区的文件取消暂存(恢复到工作区)
git restore --staged <file-name>
4. 同时恢复暂存区和工作区到 HEAD 的状态
git restore --source=HEAD --staged --worktree <file-name>
示例:
修改了 index.html,但想放弃修改:git restore index.html
执行了 git add index.html,但想取消暂存:git restore --staged index.html
2.3、git reset (指针移动与状态重置)
核心思想:
将当前分支的 HEAD 指针移动到指定的提交,并可选择地重置暂存区和工作区。它会重写历史,因此非常危险,通常只用于本地未推送的分支。
用法:
模式一:--soft (最温和) # 移动 HEAD 指针,暂存区和工作区保持不变
git reset --soft <commit-hash>
模式二:--mixed (默认模式) # 移动 HEAD 指针,并重置暂存区,但工作区保持不变
git reset --mixed <commit-hash>
模式三:--hard (最危险) # 移动 HEAD 指针,并重置暂存区和工作区,所有未提交的更改都将丢失
git reset --hard <commit-hash>
示例:
假设提交历史为 A -> B -> C -> D,当前 HEAD 在 D。
git reset --soft B:HEAD 指针移动,暂存区和工作区仍包含 C 和 D 的更改。你可以重新提交它们。
git reset --mixed B:HEAD 指针移动,暂存区被清空,但工作区仍包含 C 和 D 的更改(显示为未暂存的修改)。
git reset --hard B:HEAD 指针移动,暂存区和工作区都被重置为 B 的状态。C 和 D 的更改以及所有未提交的修改将永久丢失。
2.4、git rebase (变基与历史整理)
核心思想:
将一个分支上的所有提交"移植"到另一个分支的顶端,或者交互式地修改、合并、删除历史提交。
它会重写历史,因此非常危险,主要用于本地分支或团队约定好的协作流程中。
用法:
(1)将当前分支变基到目标分支的顶端
git rebase <target-branch>
(2)交互式变基,用于修改最近的 N 个提交
git rebase -i HEAD~N
交互式变基 (-i) 是最强大的功能,它允许你对提交执行以下操作:
pick: 使用该提交
reword: 修改提交信息
edit: 修改提交内容
squash: 将该提交合并到上一个提交中
fixup: 类似 squash,但丢弃该提交的提交信息
drop: 删除该提交
示例:
假设你在 feature 分支上开发,main 分支有了新的提交。你想让 feature 分支的历史看起来像是在最新的 main 上开发的。
git checkout feature
git rebase main
这会将 feature 分支上所有与 main 分支不同的提交,依次应用到 main 分支的最新提交之上。
三、git 中Cherry Pick介绍
Cherry Pick 是 git 中的一个非常有用的命令,用于将某个分支上的一个或多个特定提交(commit)复制到当前分支。它允许你只选择性地合并某些提交,而不是合并整个分支。
选择性合并
当你不想合并整个分支(例如,该分支包含未完成的或不需要的功能),但需要其中的某个特定提交(比如一个重要的 bug 修复)时,可以使用 Cherry Pick。
修复移植:在维护多个版本(如发布版和开发版)时,将一个分支上的修复补丁应用到另一个分支。
基本用法
git cherry-pick commit-hash>
<commit-hash>:你想要复制的提交的哈希值(可以通过 git log 查看)。
示例场景
假设有两个分支:main(主分支)和 feature(功能分支)。
在 feature 分支上,提交了一个重要的 bug 修复,哈希值为 a1b2c3d。
当前在 main 分支上,但不想合并整个 feature 分支(因为还有其他未完成的功能)。
执行命令:
git checkout main git cherry-pick a1b2c3d
现在,main 分支上就有了 feature 分支中那个特定的 bug 修复提交,而其他提交不会被合并。
注意事项
冲突处理:如果被复制的提交与当前分支的代码有冲突,Git 会暂停并提示手动解决冲突。解决后,使用 git cherry-pick --continue 继续,或使用 git cherry-pick --abort 取消操作。
提交信息:Cherry Pick 会保留原提交的提交信息,但会生成一个新的提交哈希值(因为提交的上下文不同)。
避免滥用:频繁使用 Cherry Pick 可能导致分支历史变得混乱,增加后续合并的复杂性。通常建议在确实需要"精准移植"时使用。
常用选项
git cherry-pick -n:只应用更改到工作区,但不自动创建新提交(方便你修改后再提交)。
git cherry-pick <start-commit>..<end-commit>:复制一个范围内的多个连续提交
(注意:范围是左开右闭,即不包含 start-commit)。
git cherry-pick -m 1 <merge-commit>:当需要 Cherry Pick 一个合并提交时,使用 -m 指定父提交编号(通常为 1 表示主分支)。
四、git 中cherry pick与merge的区别
git merge
■目的
将整个分支的完整历史记录和所有更改合并到当前分支。
■作用范围
分支级别。它关注的是两个分支之间的差异和共同祖先。
■典型场景
功能开发完成后,将 feature 分支合并回 main 分支;定期同步主分支的更新到开发分支。
git cherry-pick
■目的
将一个或多个特定的提交(commit)复制到当前分支。
■作用范围
提交级别。它只关注你指定的那一个或几个提交。
■典型场景
紧急修复一个 Bug,只想把修复提交从 dev 分支移植到 release 分支,而不想引入 dev 上的其他未完成
常见问题
一、git reset --hard origin/develop中origin/develop指的是远程develop分支吗?
origin/develop 在 Git 中指的是本地存储的远程跟踪分支(remote-tracking branch),而不是直接操作远程仓库上的 develop 分支。
git reset --hard origin/develop 的作用:
这条命令将当前本地分支的 HEAD 指针重置到 origin/develop 指向的提交,并强制更新工作目录和暂存区。
它不会对远程仓库的 develop 分支做任何修改。
二、git fetch origin develop 为什么不写成 git fetch origin/develop ? git reset --hard origin/develop为什么不写成 git fetch origin develop ?
核心原因:git fetch 的参数是"源"和"目标",而 git reset 的参数是"引用"
1、git fetch 的语法
git fetch <远程仓库名> <远程分支名>
第一个参数:远程仓库的名称(如 origin),告诉 Git 去哪个远程服务器拉取数据。
第二个参数:远程仓库上的分支名(如 develop),告诉 Git 只拉取该分支的最新数据。
结果:Git 会连接到远程服务器,下载 develop 分支的新提交,然后更新本地的远程跟踪分支 origin/develop。
2、git reset 的语法
git reset <提交引用>
参数:一个本地可访问的提交引用,可以是哈希值、分支名、标签,或者远程跟踪分支(如 origin/develop)。
结果:Git 直接读取本地存储的 origin/develop 这个引用指向的提交,然后移动当前本地分支的 HEAD。
三、本地分支和标签是什么关系?
1、分支(Branch)
动态指针:每次提交后,分支指针自动前移到最新提交。
可变性:可以 reset、rebase、merge 等操作改变其指向。
典型用途:
●开发新功能(feature-x)
●修复 bug(bugfix-y)
●实验性开发(experiment)
示例:
git switch -c feature-login # 创建并切换
git commit -m "Add login" # 分支自动前移
git commit -m "Fix login" # 继续前移
2、标签(Tag)
静态指针:创建后永远指向同一个提交,不会随新提交移动。
不可变性:默认不可修改(除非使用 -f 强制移动)。
典型用途:
●标记发布版本(v1.0.0、v2.3.1)
●标记重要里程碑(release-candidate)
●标记历史关键点(before-refactor)
示例:
git tag v1.0.0 # 创建轻量标签
git tag -a v1.0.0 -m "Release 1.0" # 创建附注标签
git checkout v1.0.0 # 切换到标签(分离头指针)
3、共同点
都指向某个提交(commit)。
都可以通过 git checkout 或 git switch 检出(但标签检出会进入分离头指针状态)。
都可以推送到远程仓库(git push origin <branch> 或 git push origin <tag>)。
4、不同点
创建
分支:git branch <name>
标签:git tag <name>
删除
分支:git branch -d <name>
标签:git tag -d <name>
推送
分支:git push origin <name>
标签:git push origin <name>
查看
分支:git branch
标签:git tag
移动
分支:自动随提交移动
标签:固定不动(除非强制)
四、在 IntelliJIDEA中,Git 的Rollback功能通常对应的是什么Git 命令
1.在 Local Changes(本地更改)视图中
场景: 修改了某个文件,但还没有git add(未暂存)
IDEA操作: 右键点击文件 -> Rollback。
对应的 Git命令:
git checkout <file>
或
(Git 2.23+推荐)
git restore <file>
效果: 撒销工作区中对该文件的修改,恢复到最近一次提交的状态
2.在commit视图中(已git add但未提交)
场景: 已经 git add了某个文件(暂存区有更改),但尚未提交
IDEA操作: 在 Commit 窗口的 changes列表中,右键点击文件 -> Rollback
对应的 Git 命令:
git reset HEAD <file>
或
(Git2.23+推荐)
git restore --staged <file>
效果: 将文件从暂存区移出,但保留工作区的修改(即文件内容不会变,只是不再处于暂存状态)。
3.在Git Log(版本历史)视图中
场景: 想撤销某次已经提交的更改(例如最近一次提交)
IDEA操作: 右键点某次提交 -> Undo Commit或Revert Commit
Undo Commit: 相当于git reset --soft HEAD~1(保留更改,但撤销提交)
Revert Commit: 相当于 git revert <commit-hash> (创建一个新的反向提交)
注意: IDEA中没有直接叫 Rollback 的按钮来撒销已提交的更改,而是用Undo Commit或Revert Comit来实现.