git笔记之撤销、回退、reset方面的笔记

code review!

文章目录

1.git 已经commit了,还没push,如何撤销到初始状态

git reset 是 Git 中的一个强大命令,用于撤销本地的提交。git reset 可以通过不同的参数来指定撤销的方式,比如 --soft--mixed(默认),以及 --hard。下面是每个参数的详解以及一个比较表格。

git reset --soft HEAD~1

  • --soft 参数将 HEAD 移动到指定的提交,但不会改变索引(暂存区)和工作目录。换句话说,它撤销了最近的提交,但保留了文件的更改,并且这些更改已经在暂存区准备好了下一个提交。
  • 适用场景:当你想要撤销提交但保留更改并立即进行新的提交时。
  • 将当前分支的HEAD移动到前一个提交(即撤销最后一次提交),但不更改暂存区(index)和工作目录。
  • 撤销后,所有的更改都会被保留在暂存区中,就好像你刚刚执行了git add将它们暂存起来一样
  • 这个命令适合于当你想要修改最后的提交信息或者合并多个提交到一个提交的场景。

git reset HEAD~1(等同于 git reset --mixed HEAD~1

  • --mixed 是默认的重置模式。这个参数会将 HEAD 移动到指定的提交,并且重置索引,但不会触及工作目录的文件。
  • 撤销了最近的提交,并将那次提交的所有更改放回工作目录。
  • 就像你完成了修改但还没有执行 git add 的状态
  • 适用场景:当你想要撤销提交并重新审查或修改更改,但不想完全放弃这些更改时。

git reset --hard HEAD~1

  • --hard 参数将 HEAD、索引和工作目录全都重置到指定的提交。
  • 这意味着所有的更改——包括已经暂存的和未暂存的更改——都会被丢弃。使用这个选项要非常小心,因为它会丢失所有未提交的更改。
  • 适用场景:当你想彻底撤销最近的提交以及所有更改,回到之前的状态时。
  • ⚠️ 警告: 使用 --hard 选项会丢失最后一次提交的所有更改。请确保你不需要这些更改,或者已经对它们做了备份。

下面是一个表格,比较这三种 git reset 命令:

命令 HEAD 移动 索引(暂存区)影响 工作目录影响 适用场景
git reset --soft HEAD~1 保留更改 无影响 撤销提交,保留更改,准备新提交
git reset HEAD~1 取消暂存更改 保留更改 撤销提交,重新审查/修改更改
git reset --hard HEAD~1 取消暂存更改 丢弃更改 彻底撤销提交和更改,回到之前的状态

重要提醒: 使用 git reset --hard 时务必要小心,因为一旦执行,未提交的更改将无法恢复。在任何可能会丢失数据的操作之前,建议先做好备份。

2.git 已经commit了,还没push,修改最后的commit信息

如果你已经做了一个commit,但还没有将其push到远程仓库,并且希望修改这次commit的信息,你可以使用git commit --amend命令进行操作。下面是一步一步的指导:

  1. 打开你的命令行工具。

  2. 使用git log查看最近的commit,确保你要修改的是最近一次的commit信息。

  3. 输入以下命令来修改最后一次的commit信息:

git commit --amend
  1. 这将打开你的默认文本编辑器,允许你修改commit信息。在编辑器中修改commit信息,然后保存并关闭编辑器。

  2. 如果你想直接在命令行中修改commit信息,而不是打开文本编辑器,可以使用以下命令(其中new commit message是你的新commit信息):

git commit --amend -m "new commit message"
  1. 完成修改后,你可以使用git log再次检查以确保commit信息已更新。

  2. 现在,你可以像往常一样推送你的commit到远程仓库:

git push origin your-branch-name

请注意,如果你的commit已经被推送到了远程仓库,并且其他人可能已经基于这个commit进行了工作,那么使用--amend操作可能会导致问题。在这种情况下,你应该谨慎使用这个命令,并且可能需要与你的团队协调来决定是否要修改远程仓库中的commit信息。

3.git 已经commit了,也push了,发现远程没合,发现还有没改完,如何操作

如果你已经提交(commit)并推送(push)了代码到远程仓库,并且发现还有未完成的修改需要添加,你可以按照以下步骤操作:

3.1. 在本地进行修改

首先,在你的本地代码库中进行必要的修改。

3.2. 添加并提交修改

修改完成后,将这些更改添加到暂存区并创建一个新的提交:

git add .
git commit -m "说明你的修改"

如果修改较小,你也可以选择将这些修改添加到上一个提交中,使用 --amend 选项:

git add .
git commit --amend --no-edit  # 保持上次提交的信息不变
# 如果你想修改提交信息,可以去掉 --no-edit

3.3. 推送到远程仓库

如果你没有修改上一次的提交(即第2步中没有使用 --amend),你可以正常推送到远程仓库:

git push origin <branch-name>

如果你使用了 --amend 修改了上一个提交,你需要强制推送,因为历史已经改变:

git push origin <branch-name> --force
# 或者使用更安全的 --force-with-lease 选项
git push origin <branch-name> --force-with-lease

注意:强制推送会重写远程仓库的历史。在共享的仓库中进行强制推送可能会给其他协作者带来问题。在强制推送之前,最好跟团队成员沟通一下。

–force-with-lease 详解

--force-with-leasegit push 命令的一个选项,它是 --force 的一个更安全的替代。强制推送(使用 --force)会覆盖远程仓库的分支,而不管其他人是否已经推送了他们的提交。这可能会导致他人的工作丢失,因为你的本地历史会覆盖远程历史。

相比之下,--force-with-lease 提供了一种保护机制,确保你不会意外覆盖其他人的更改。它会在强制推送之前检查远程分支的当前状态,确保远程分支的当前状态与你上次获取时的状态一致。如果有差异,说明可能有其他人已经推送了新的提交,那么 --force-with-lease 会阻止你的推送操作。

这里是如何操作的:

  • 当你执行 git push --force-with-lease 时,Git 会检查远程分支的当前引用是否匹配你本地的引用。
  • 如果它们匹配,说明自从你上次同步以来,没有人更新远程分支,你的强制推送将会执行。
  • 如果它们不匹配,说明有人在你之后推送了他们的更改,你的推送将会被拒绝。这可以防止你不小心覆盖别人的工作。

--force-with-lease 的好处在于,它提供了强制推送的能力,同时减少了意外覆盖同事代码的风险。它是一种更加谨慎的做法,尤其适用于多人协作的项目中。

这个选项还可以接受额外的参数来指定要检查的分支和引用:

git push --force-with-lease=<branch-name>:<expected-value>

这里,<branch-name> 是你想要推送的远程分支名,<expected-value> 是你期望远程分支引用的值。如果远程分支的实际引用与 <expected-value> 不同,Git 将阻止推送。

使用 --force-with-lease 是一个更安全的做法,因为它有助于防止数据丢失。不过,即使是这种安全的强制推送,也应该谨慎使用,并且最好在推送之前与团队成员沟通。

3.4. 如果有必要,打开一个新的合并请求(Merge Request)或拉取请求(Pull Request)

如果初始的合并请求还没有被处理,你可以继续在原来的合并请求中添加信息说明有新的提交添加。如果合并请求已经关闭,或者你需要创建一个新的合并请求,根据你使用的平台(GitHub, GitLab, Bitbucket等)的规则进行操作。

请注意,如果你的仓库设置了分支保护规则,禁止了强制推送,那么你可能需要联系仓库管理员或者使用别的方法(比如创建一个新的提交来修复问题,而不是修改已有提交)。

根据你的具体情况和团队的工作流程,这些步骤可能会有所不同。

4.使本地与远程保持一致

重置本地分支到远程分支的状态

如果你想要放弃本地分支上所有的更改,并使其与远程分支完全一致,可以使用 git reset 命令配合 --hard 选项。这样会使你的本地分支(比如 master)完全回退到远程分支的状态:

git fetch origin
git reset --hard origin/<branch_name>

⚠️ 警告: 使用 --hard 选项会丢失所有未提交的更改和本地提交。在执行这个命令之前,请确保你不需要这些更改,或者已经对它们做了备份。

更新本地分支列表

有时,远程仓库的分支可能会被删除或重命名,你也需要更新你本地的分支列表以反映这些更改:

git fetch --prune

这个命令会从你的本地仓库中删除那些已经在远程仓库中被删除的追踪分支。

对于所有本地分支,跟踪远程分支

如果你有多个分支需要同步,你可以对每个分支重复上述的拉取或推送操作。此外,确保本地分支正确地跟踪对应的远程分支,可以使用:

git branch -u origin/<branch_name>

或者,在推送本地分支时设置上游(远程跟踪)分支:

git push -u origin <branch_name>

通过这些基本操作,你可以保持本地仓库与远程仓库的一致性。记得在执行可能会丢失数据的操作如 git reset --hard 前,总是确保备份你的工作。

5.git stash的作用

git stash 是一个非常有用的 Git 命令,它可以帮助你临时保存你的工作目录中的更改,而不是做一个提交。这在以下情况下非常有用:

  1. 当你正在进行一项工作但需要切换到另一个分支进行另一项工作时,而你的当前更改又不足以做一个完整的提交。
  2. 当你需要快速保存当前的工作状态,以便稍后再回来继续工作。

git stash 通常与几个选项一起使用:

  • git stash save "message":保存当前的工作进度。你可以提供一个消息作为参数,以帮助记住这个stash中保存了什么。
  • git stash list:列出所有的 stash。
  • git stash apply:重新应用最近保存的 stash。你可以指定一个 stash 来应用,如 git stash apply stash@{2}
  • git stash pop:应用最近的或指定的 stash,并从 stash 列表中移除它。
  • git stash drop:删除最近的或指定的 stash。
  • git stash clear:删除所有的 stashes。

当你执行 git stash(等同于 git stash push)时,Git 会执行以下操作:

  1. 将你的暂存区和工作目录中的更改保存起来。
  2. 回复暂存区和工作目录到最近的提交状态(HEAD),这样你就有了一个干净的工作状态。

这使得你可以切换分支并开始不同的工作,或者保持当前分支的一个干净状态。当你准备好继续之前的工作时,你可以用 git stash applygit stash pop 来恢复你之前保存的更改。

请注意,git stash 不会保存未追踪的文件(比如新添加的文件,还没有用 git add 添加到暂存区的文件),除非你使用 git stash -u 或者 git stash --include-untracked。同样,git stash -agit stash --all 会保存所有的更改,包括未追踪的文件和忽略的文件。

6.某个分支在Git中进行了就地合并,并且已经推送了更改到远程,想要撤销这次合并

如果你在Git中进行了就地合并(即使用了git merge --no-ff或直接git merge并且没有创建新的分支),已经将合并后的代码推送到远程仓库,你需要先撤销本地更改,然后再强制推送回远程仓库。

  • 可以查看reflog来找到之前的提交ID,然后通过reset回到那个状态。

    git reflog
    

    这会列出最近所有的更新记录,包括那些已经被删除的提交。找到你想恢复的那个提交的哈希值。

  • 然后用reset回到那个点,即撤销本地更改。

    git reset --hard <commit-hash>
    
  • 然后强制推送到远程仓库:

    git push --force
    

7.git add 后发现多add 了一个文件

如果在使用 git add 后发现多添加了一个文件,你可以用以下命令将其从暂存区移除:

git reset <file-name>

如果想移除所有已添加但未提交的文件,可以使用:

git reset

这会将所有文件从暂存区中移除,但文件的修改仍然保留在工作目录中。

8.git 向远程push和merge了几个commit,现在后悔了,想让远程最新处于之前的状态

有几种方法可以回退远程仓库的状态,取决于具体需求。

方法一:git revert(推荐,安全)

这种方式会创建新的 commit 来撤销之前的更改,不会改写历史,适合多人协作的仓库。

# 撤销最近一个 commit
git revert HEAD

# 撤销最近多个 commit(比如最近 3 个)
git revert HEAD~2..HEAD

# 然后推送
git push origin <branch>

方法二:git reset + 强制推送(谨慎使用)

这种方式会直接改写历史,如果其他人已经拉取了这些 commit,会造成问题。

# 查看 commit 历史,找到想回退到的 commit hash
git log --oneline

# 回退到指定 commit(保留工作区修改)
git reset --soft <commit-hash>

# 或者完全丢弃修改
git reset --hard <commit-hash>

# 强制推送到远程
git push origin <branch> --force

方法三:git push --force-with-lease(相对安全的强推)

git reset --hard <commit-hash>
git push origin <branch> --force-with-lease

这个命令会在推送前检查远程分支是否被其他人更新过,如果有则拒绝推送,避免覆盖别人的工作。

9.gitlab 如何 把多个 merge commit 都cherry pick 到一个分支上并提起一个merge请求?

# 先创建工作分支
git checkout target-branch
git pull origin target-branch
git checkout -b feature/cherry-pick-mixed

# 普通 commit
git cherry-pick abc1234

# merge commit(加 -m 1)
git cherry-pick -m 1 def5678

# 普通 commit
git cherry-pick ghi9012

# 继续按需操作...

# 推送到远程
git push origin feature/cherry-pick-mixed

10.vscode 处理git 冲突 accept current、 incoming、both 区别

Current 与 Incoming

  • Current/HEAD:当前分支、本地原始内容,位于 <<<<<<< HEAD======= 之间。
  • Incoming:要合入的分支或远程内容,位于 =======>>>>>>> branch-name 之间。

三个按钮

  • Accept Current Change:丢弃 Incoming,只保留 Current,并移除冲突标记。适合确认本地正确时。
  • Accept Incoming Change:丢弃 Current,只保留 Incoming。适合本地过时或需要同步远程时。
  • Accept Both Changes:上下各保留一份(先 Current 后 Incoming),并移除标记;常需再手动调整逻辑或顺序,避免重复定义/语法问题。

快速示例

function calculate() {
<<<<<<< HEAD
    const result = 10 + 20; // Current
=======
    const result = 50 * 2;  // Incoming
>>>>>>> feature-branch
    return result;
}
  • 选 Current:只留 10 + 20
  • 选 Incoming:只留 50 * 2
  • 选 Both:两段代码叠在一起,需手动收拾,避免重复声明

Compare Changes

  • 打开左右分屏对比:左侧 Current,右侧 Incoming。
  • 适合冲突行较多或逻辑复杂时先对比再决定。

实操流程(含灰色圆圈)

  1. 选择解决方案:Current / Incoming / Both。
  2. 保存文件(灰色圆圈消失,内容写入磁盘)。
  3. 暂存文件。
  4. 提交合并。

记忆:选择方案 → 保存 → 暂存 → 提交。

附:官网给出的三向合并编辑器的介绍
在这里插入图片描述

11.vscode中的GitLens和Git Graph

GitLensGit Graph 是 VS Code 中最受欢迎的两个 Git 插件,但它们的侧重点完全不同

简单来说:GitLens 是显微镜(Micro),Git Graph 是地图(Macro)。

以下是详细的对比分析:

1. 核心定位对比

特性 GitLens Git Graph
核心隐喻 增强视力的眼镜 可视化的地铁线路图
主要功能 代码行级别的历史追溯、Blame 信息、文件历史深度分析。 提交历史树(Commit Tree)的可视化、分支管理、合并操作。
使用频率 高频(几乎每写一行代码都在用)。 中频(需要查看历史、合并分支、Rebase 时使用)。
界面表现 嵌入在代码编辑器行尾、侧边栏详细视图。 独立的 Webview 标签页,图形化界面。

2. 功能深度对比

GitLens:代码考古学家

GitLens 的强项在于**“谁?什么时候?改了什么?为什么?”**。

  • 行内 Blame (Inline Blame): 这是它的杀手级功能。光标点到哪一行,行尾就会显示这行代码是谁、在多久以前、哪个 Commit 提交的。
  • 文件热力图 (File Heatmap): 在侧边栏可以看到文件被修改的频率,红色越深改动越频繁(通常意味着 Bug 高发区)。
  • Code Lens: 在函数或类定义的上方显示“3 authors, last change 2 days ago”等元数据。
  • Hovers (悬浮窗): 鼠标悬停在 Blame 信息上,可以查看详细的 Commit Message 和 Diff。
  • Worktrees 支持: 对 Git Worktrees 的支持非常完善。
  • 缺点: 功能极多,配置项极其复杂,默认开启的功能可能会让编辑器显得有点“吵”(信息过载)。
Git Graph:分支指挥官

Git Graph 的强项在于**“分支结构长什么样?我们现在在哪里?”**。

  • 图形化历史 (Graph View): 它是 VS Code 里最接近 SourceTree 或 TortoiseGit 体验的插件。它画出的分支线非常漂亮、清晰。
  • 鼠标操作: 你可以在图表上右键点击某个 Commit,直接执行 Checkout, Cherry-pick, Merge, Rebase, Reset, Tag 等操作。
  • 对比功能: 按住 Ctrl 点击两个不同的 Commit,它会直接列出这两个 Commit 之间的所有文件差异。
  • 轻量级: 界面简洁,专注于展示“树”的结构,没有太多花哨的背景分析功能。
  • 缺点: 它主要是一个“查看器”和“操作器”,不具备代码行级别的深入分析能力。

3. 实际场景应用

场景 A:你在阅读同事写的代码,发现一行逻辑很奇怪。
  • GitLens: 你只需要把光标移上去,行尾立刻告诉你这是 ZhangSan3个月前 为了 修复登录Bug 提交的。你点击那个 Commit ID,还能看到当时他同时改了哪些文件。
  • Git Graph: 你需要打开 Graph 视图,搜索这个文件,找到相关的 Commit,效率较低。
  • 胜出: GitLens
场景 B:你需要把 feature 分支合并到 dev,但你想先看看这两个分支偏离了多远。
  • GitLens: 虽然也能看,但在侧边栏的树状列表中看分支差异并不直观。
  • Git Graph: 打开视图,一眼就能看到两条地铁线是在哪里分叉的,中间差了多少个点。你可以右键点击 feature 分支直接选择 “Merge into current branch”。
  • 胜出: Git Graph
场景 C:你想查看上一个问题中提到的 git reset 后的效果。
  • GitLens: 在侧边栏的 “Commits” 或 “Stashes” 里能看到,但不够直观。
  • Git Graph: 顶部直接出现一个显眼的 “Uncommitted Changes” 节点,连在最新的 Commit 上,非常符合直觉。
  • 胜出: Git Graph

4. 结论与建议

不要二选一,请同时安装。

它们是互补的,而不是竞争的。

  • 日常编码时: 依赖 GitLens 的行内提示(如果不喜欢太吵,可以在设置里把 Current Line Blame 设为只有 toggle 时才显示)。
  • 处理版本管理时: 当你需要合并代码、查看版本树、或者像你之前问的那样查看 Reset 后的差异时,打开 Git Graph

最佳实践配置:

  1. 安装 GitLens,但建议去设置里把 Code Lens 关掉(因为它会把代码行距撑大),保留 Current Line Blame
  2. 安装 Git Graph,并将其图标固定在底部状态栏,随时点开查看“地图”。

在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐