%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later %% # commit 相对引用 | | 说明 | 示例 | | ------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `<commit/refs>^` | 引用该提交的 "**父提交**" | `HEAD^`: 引用 HEAD 的上一个提交 | | `<commit/refs>^n` | 当其有**多个父提交**时,指示第 n 个父提交 <br>(例如合并提交) | - `commit^1`:第一个父提交(通常是当前分支的末端)<br>- `commit^2`:第二个父提交(通常是合并进来的分支的末端)<br> | | `<commit/resf>~[n]` | 引用该提交的 "**第 n 个祖先提交**" | - `HEAD~0` 即 HEAD 本身 <br>- `HEAD~` 等价于 `HEAD~1` 等价于 `HEAD^` <br>- `HEAD~2`:HEAD 之前的第 2 个提交,等价于 `HEAD^^` | > [!NOTE] `^` 与 `~` 可作用于某一具体 commit(ID),也可用于各类 "引用" 如 HEAD、分支名——本质上仍是作用于引用所指向的提交 <br><br> # commit 范围区间 | | 说明 | 示例 | | ------- | --------------------------------------------------- | --------------------------- | | `A..B` | 引用 "**B 不同于 A 的所有提交(==不包括 A==)**,等价于 `B --not A` | - `git log A..B`:<br> <br> | | `A...B` | 引用 **A 和 B 的共同祖先之外的所有提交**——即 **A 和 B 的 "不同提交" 的并集** | - `git log A...B` | > [!NOTE] 要选取 `A` 到 `B` 之间的所有提交,包括 A,可用 `A^..B`。 > [!example] `A..B` 示例 > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-985C7FF2C663AD8260A9F7E57ABEE1A8.png|571]] > [!example] `A...B` 示例 > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-5EB6A2D809561FB206E4A9852A77129E.png]] > <br><br><br> # 回退至特定提交——git reset ⭐ > 参见:[Git - 重置揭密](https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E7%BD%AE%E6%8F%AD%E5%AF%86#_git_reset)(生动形象的图例说明)以及 [Git Reset 三种模式 - 简书](https://www.jianshu.com/p/c2ec5f06cf1a) ![[Excalidraw/Excalidraw-Solution/git commit 管理_0.excalidraw.svg|447]] %%[[Excalidraw/Excalidraw-Solution/git commit 管理_0.excalidraw.md|🖋 Edit in Excalidraw]]%% 版本回退:**令 ==HEAD== 指向当前分支上==某一历史提交==**,并 **==丢弃其之后的所有提交==** 。 有三种方式,差异在于回退时对**工作区**和**暂存区**的处理: | | 方式 | 说明 | | ------------------------------ | ---------------- | ----------------------------------------------------------------------- | | `git reset [--mixed] <commit>` | **混合重置**<br>(默认) | 仅**清空==暂存区==**,保持**当前工作区不变** | | `git reset --soft <commit>` | **软重置** | **==不重置/不改变==** 暂存区和工作区(仅回退 HEAD 指向而**保留当前所有更改,可供重新提交**) | | `git reset --hard <commit>` | **硬重置** | 同时**重置==暂存区&工作区==** ——即 **==丢弃==当前==所有修改==**,**工作区重置为与 HEAD 新指向的提交匹配**。 | > [!danger] 若当前不存在任何历史 commit,则 `git reset --hard` 将==删除整个工作区中所有已跟踪文件== ![[Excalidraw/Excalidraw-Solution/git commit 管理.excalidraw.svg|435]] %%[[Excalidraw/Excalidraw-Solution/git commit 管理.excalidraw.md|🖋 Edit in Excalidraw]]%% ## 示例说明 - `git reset HEAD^` 或 `git reset HEAD~1`:**回退当前分支到上一个提交**,**清空暂存区**,但**保留工作区内容** - `git reset --hard HEAD~2`:**回退两个提交**(**撤销这两个提交中的所有变更**) > [!example] 示例说明 [^2] > > > 初始时: > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-FC6A51104D87CE77A599BD81A57F30CC.png|345]] > > (1)执行 `git reset HEAD^` (混合重置/**默认选项**) > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-C22A5A1D84570E45B3FEE9B4CCCD9F0A.png|337]] > > > (2)执行 `git reset --soft HEAD^`(软重置) > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-E0F22720D2226A1F6B0862B524AC830A.png|338]] > > > (3)执行 `git reset --hard HEAD^` (硬重置) > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-28997508731808D12FED1AD04F4969D5.png|338]] > > [!example] 示例:使用 `git reset soft` "压缩/合并" 多个提交 > > 初始时: > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-161D000D6DB22F3B5370C83124115837.png|356]] > > > 执行 `git reset --soft HEAD~2` (保留工作区/暂存区不变) > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-9ED0083841A5F6698266E9346C59DD04.png|382]] > > 重新提交(原先两次提交中的更改被压缩/合并成了这一新提交) > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-E6E236A2DD01CEB839F8CBC054F7844F.png|375]] > <br><br><br> # 撤销既有提交——git revert `git revert` 用于**撤销 "指定提交" ==所引入的修改==**,其会**产生一个新的提交**并在该提交中**消除了 "指定提交" 所引入的变更**。 `git revert` 并不会影响其他版本的修改,仅仅**针对指定的提交做了一个 undo 操作**。 `git revert [option] <commit>...` > [!NOTE] 执行 `git revert` 时要求工作区&暂存区干净,若存在 "未暂存的修改或未提交的暂存" 时将会报错 > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-D48BE6B6C521259F387124A97420DB5C.png]] > > <br><br><br> # 拣选复制特定提交——git cherry-pick ⭐ `git cherry-pick` 用于 **==拣选一个或多个特定提交==** 应用到**当前分支**,**为每个拣选的提交==依次产生一个对应的新提交==**。 `git cherry-pick [options] <commit>...`:将**指定提交**应用到**当前分支**——**产生新的提交** - `-e| --edit`:编辑**提交信息** - `-x`:在**提交信息末尾自动添加一行** "**cherry picked from commit ...**" 内容(便于查看该提交是如何产生的) - `-n | --no-commit`:拣选**应用于==工作区&暂存区==**,**不产生提交**(可用于**拣选合并多个提交的内容**,而后手动进行一次提交) - `<commit>...` :支持**指定单个、多个不连续的 commit 或者范围形式**如 `<commit1>..<commit2>` - `-X <strategy>`:指定**产生合并冲突**时的策略,可选项为 - `ours`:选择当前分支的更改。 - `theirs`:选择 cherry-pick 提交中的更改。 > [!example] 常见使用场景——**合并多个修复到当前分支** > > 在 `Feature` 分支做了几个重要的修复 or 特性,并希望**将这些修复应用到当前分支**: > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-0FD51CE432235ED288A3C1F38FB74276.png|775]] > [!example] > > ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-AF33E427E3DEEA01B36C747D8E1606A0.png|917]] > > > ### 冲突处理 在 cherry-pick 应用过程中,当**某一 commit 存在==合并冲突==** 时,可应用下列命令进行处理: - `git status`:检查**当前合并状态,冲突文件**; - `git cherry-pick --continue`:需**手动消除冲突**后**通过 git add 添加到暂存区**,再通过该命令**指示继续进行**。 - `git cherry-pick --skip`:**直接跳过当前冲突的 commit**; - `git cherry-pick --abort`:**中止拣选过程,恢复到 rebase 前的状态**; <br><br><br> # 检出特定提交——git checkout > `git checkout` 是一个多功能命令,可用于**切换分支、检出文件或恢复文件到某个特定状态**。 > 在较新 Git 版本中, `checkout` 的部分功能**已有等价的 `git switch` 和 `git restore` 命令实现**。 `git checkout [OPTION] <commit> [<pathsepc>...]` :检出**特定的提交、标签或分支**,**恢复到工作区** 使用示例: ```shell # 撤销工作区中未暂存的修改, 恢复至暂存区或上一版提交 git checkout -- [file] # 将指定分支下指定文件的最新版本应用/覆盖至当前"工作区"&"暂存区"; git checkout [source_branch] -- [file] # 等价的git restore 命令(Git 2.23+版本) git restore --source [source_branch] -- [file] # 将指定分支下指定文件的最新版本应用/覆盖至仅当前"工作区" git checkout [source_branch] -- [file] && git reset # 等价命令: git show [source_branch]:[file] > [file] # 打印指定分支的文件内容并覆盖当前文件 # 检出特定提交 git checkout adb1234 # 检出特定tag git checkout v0.1 # 切换到branch分支 git checkout branch # 创建并切换到新分支brach git checkout -b branch # 检出远程跟踪分支, 创建同名的本地分支作为跟踪分支 git --track origin/some_branch ``` > [!NOTE] > > 检出一个特定提交(而不是分支)会使仓库进入 **分离头指针状态**(Detached HEAD)。 > 在该状态下,可**浏览历史提交**或**在==某个特定提交的基础上创建新的分支==**。 ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-6810128840ECBDB460597293BE0BDAB0.png|725]] # 待定——git reflot #TODO ![[_attachment/05-工具/git 使用/git commit 管理.assets/IMG-git commit 管理-160410F50D6BDA10AEF42432B37C3792.png|661]] <br><br><br> # commit 管理总结 🌠 | 需求 | 相应命令 | | ------------------------------------------ | -------------------------------- | | **回退**到**某一历史提交**,**丢弃其后所有提交** | `git reset --hard` | | **合并从某一提交起至今的所有提交** | `git reset` 或 `git reset --soft` | | | | | **合并**提交历史 "**中间的某一段提交**",同时**保留其后所有提交历史** | `git rebase -i` | | **修改**提交历史中**某一次 commit 的提交消息** | `git rebase -i` | #### ❓修改历史 commit 内容 > 参考: https://segmentfault.com/a/1190000022926064 仅修改历史 commit 的**提交信息**: ```shell # 修改最近n次commit git rebase -i HEAD~n # 在编辑器中将提交ID前的pick改为r(reword), 关闭编辑器 # 在新打开的编辑器中, 修改commit的提交信息, 关闭编辑器 # (完成, 不需要rebase continue, 此时仍位于最新的commit状态) ``` **修改并重新提交**某个历史 commit: ```shell # 修改最近n次commit git rebase -i HEAD~n # 1. 在编辑器中将待修改的commit ID前的pick改为e(edit) # 2. 关闭编辑器, 进行源码调整等处理 # 3. 执行以下命令: git commit --amend -m "..." git rebase --continue ``` <br> %% #### 合并 commit 合并几个 commit——`git rebase -i <commit>` ,其中 `<commit>-id` 是**需要合并的提交中的最早的 commit** 的“**父commit**" > 除了使用--no-commit 在合并以前就取消提交以外,如果你已经合并了,但是不想提交,可以先 git commit 然后 git log 找到上一个版本 commit id(下文用 xxxx 指代) git reset xxxx 然后再 git add . > git commit --amend 就可以把合并进来的内容,放进之前的 commit 中了 %% # 参考资料 # Footnotes