Java八股文——Git

Git 使用熟练吗?

面试官您好,是的,我非常熟练地使用Git。在我的所有项目中,Git都是我们进行代码版本控制和团队协作的核心工具。我对Git的工作原理和日常协作流程都非常熟悉。

1. 我对Git工作原理的理解

我认为,理解Git的四个工作区域是掌握它的关键。

  • 工作区 (Workspace):就是我在本地电脑上能直接看到的、正在进行编辑的项目目录。
  • 暂存区 (Index / Stage):这是一个非常重要的中间区域。它像一个“购物车”,我可以通过 git add 命令,将工作区中想要提交的修改,有选择性地、一部分一部分地放进去。
  • 本地仓库 (Repository):这是我本地的代码库,它保存了项目所有的提交历史。当我执行 git commit 时,暂存区里的所有内容,就会被打包成一个“快照”(一个commit),并永久地记录在本地仓库中。
  • 远程仓库 (Remote):通常是像GitHub, GitLab, Gitee这样的服务器,用于团队成员之间共享和同步代码。

2. 我的日常开发工作流 (Git Flow)

在团队协作中,我们通常遵循一套规范的Git工作流程,以保证代码的整洁和协作的顺畅。

  1. 开始新任务前,保持同步

    • 首先,切换到主开发分支(比如 develop):git checkout develop
    • 然后,拉取远程仓库的最新代码,确保我的本地分支是最新的:git pull origin develop
  2. 创建特性分支

    • 为我即将开发的新功能或修复的Bug,创建一个新的、独立的特性分支。我会遵循一个清晰的命名规范,比如 feature/user-loginfix/order-bug-123
    • git checkout -b feature/user-login
    • 在自己的分支上进行开发,可以保证我的工作不会影响到主分支和其他同事。
  3. 开发与提交

    • 在完成一部分有意义的代码改动后,我会执行:
      • git add . (或者 git add [具体文件]):将修改添加到暂存区
      • git commit -m "feat: implement user login functionality":将暂存区的内容提交到我的本地仓库。我会遵循规范的commit message格式(如Angular规范),让提交历史清晰可读。
    • 我会在开发过程中进行多次commit,形成一个逻辑清晰的开发记录。
  4. 推送到远程仓库

    • 当特性开发完成,并且在本地测试通过后,我会将我的特性分支推送到远程仓库:
      git push origin feature/user-login
  5. 发起合并请求 (Pull Request / Merge Request)

    • 推送到远程后,我会在GitHub或GitLab上,创建一个从我的feature/user-login分支到develop主分支的合并请求(PR/MR)
    • 在PR中,我会详细描述我所做的工作、实现的功能以及需要注意的事项。
  6. 代码审查与合并 (Code Review & Merge)

    • 团队的其他成员会对我的PR进行代码审查,提出修改意见。
    • 在我根据意见修改并再次推送后,如果审查通过,项目负责人或我自己会将这个PR合并develop分支中。
    • 合并后,我会删除这个远程的特性分支,并回到第一步,开始新的任务。

3. 常用的“后悔药”操作

在开发过程中,难免会犯错,熟练使用Git的撤销命令非常重要。

  • 撤销工作区的修改

    • git checkout -- [文件名]: 丢弃某个文件的所有本地修改,恢复到最近一次commit或add的状态。
  • 撤销 git add

    • git reset HEAD [文件名]: 将某个文件从暂存区“拿出来”,放回到工作区,修改内容依然保留。
  • 撤销 git commit

    • git reset --soft HEAD^: 这是我最常用的方式。它会撤销上一次的commit,但保留所有的代码改动在暂存区。这非常适合在我发现commit message写错了,或者想把几个小commit合并成一个时使用。
    • git reset --mixed HEAD^ (默认): 它会撤销commit和add,将代码改动保留在工作区
    • git commit --amend: 如果我只是想修改上一次commit的注释信息,这个命令更方便。
  • 撤销 git push

    • 这是一个需要非常谨慎的操作,因为它会修改远程仓库的历史。
    • 前提:必须确保被撤销的commit,还没有被团队其他成员拉取和依赖。
    • 步骤
      1. 首先,在本地使用 git reset 将本地分支回退到正确的版本。
      2. 然后,使用强制推送来覆盖远程分支:
        git push origin [分支名] --force (或者更安全的 --force-with-lease)
    • 在团队协作中,如果非要进行此操作,我一定会先和团队成员充分沟通

通过这套规范的流程和对常用命令的熟练掌握,我能够高效、可靠地进行代码管理和团队协作。


git rebase和merge的区别

面试官您好,git rebasegit merge 是我们在Git中用来整合不同分支代码的两种核心命令。它们虽然都能达到合并代码的最终目的,但它们的实现原理和对提交历史(commit history) 的影响是截然不同的。

简单来说,我的理解是:

  • merge 是一个 “忠实的记录者”。它会诚实地记录下“两个分支在这里进行了合并”这个事实。
  • rebase 则像一个 “历史的整理师”。它会“伪造”历史,把你的提交历史变得像一条直线一样,干净整洁。

下面我从工作原理、提交历史的变化、以及优缺点和适用场景三个方面来详细对比它们。

假设我们有这样一个场景:从main分支切出了一个feature分支进行开发,期间main分支也有了新的提交。

      A---B---C   <-- feature
     /
D---E---F---G     <-- main

现在,我们想把main分支的最新改动(F, G)同步到feature分支上。

1. git merge main (在feature分支上执行)

  • 工作原理

    • merge会找到featuremain两个分支的共同祖先(commit E)。
    • 然后,它会创建一个全新的、特殊的合并提交(Merge Commit),这个提交有两个父节点,分别指向feature的最新提交(C)和main的最新提交(G)。
    • 这个合并提交,将两个分支的历史“汇合”到了一起。
  • 提交历史的变化

          A---B---C
         /         \
    D---E---F---G---H   <-- feature, main (合并后)
    
    • H就是那个新的合并提交。
    • 优点:它完整地、真实地保留了所有分支的开发历史。我们可以清晰地看到feature分支是什么时候从main分叉出去的,又是什么时候合并回来的。整个历史是一个有向无环图(DAG)
    • 缺点:如果团队协作频繁,大量的分支合并会导致提交历史变得非常复杂和混乱,充满了各种分叉和合并线,像一个“意大利面条”,难以阅读和追溯。

2. git rebase main (在feature分支上执行)

  • 工作原理

    • rebase(变基)会先找到两个分支的共同祖先(commit E)。
    • 然后,它会 “拔起”feature分支上在共同祖先之后的所有提交(A, B, C)。
    • 接着,它将feature分支的“起点”移动到main分支的最新提交(G) 上。
    • 最后,它将之前“拔起”的那些提交(A, B, C),像打补丁一样,逐一地、按顺序地重新应用(replay)在新的起点(G)之后,形成新的提交(A’, B’, C’)。
    • 注意:A’和A虽然改动内容相同,但它们的commit ID是不同的,因为它们的父节点变了。
  • 提交历史的变化

                        A'--B'--C'  <-- feature (变基后)
                       /
    D---E---F---G     <-- main
    
    • 优点:它创造了一个非常线性的、干净的提交历史。feature分支看起来就像是直接从main分支的最新位置开发出来的一样,没有多余的合并分叉,非常清爽。
    • 缺点:它重写(或者说“伪造”)了提交历史。原始的提交(A, B, C)被丢弃了。

总结对比与适用场景

特性/维度git mergegit rebase
核心操作创建一个新的“合并提交”来汇合两个分支将一个分支的提交“平移”并重新应用到另一个分支的顶端
提交历史保留真实历史,产生分叉和合并记录重写历史,形成一条线性的、干净的历史
优点真实、可追溯,对团队协作安全提交历史清晰、整洁
缺点历史记录可能变得复杂混乱(“意大利面条”)修改了历史,如果分支已被共享,会给他人带来麻烦
我的使用原则:
  1. 从公共分支(如main, develop)拉取更新到我的个人特性分支时:我优先使用 git rebase

    • git pull --rebase
    • 这可以使我的特性分支始终保持与主干的同步,并且提交历史非常干净,便于后续发起合并请求(Pull Request)。
  2. 将我的个人特性分支合并回公共分支时:我永远使用 git merge (通常是通过GitHub/GitLab的合并请求按钮)。

    • 这会在主分支上留下一个清晰的“合并了xx特性”的记录,保留了特性开发的完整上下文。
  3. 黄金法则永远不要对一个已经被推送到远程、并且可能被团队其他成员拉取和依赖的公共分支(如main)执行rebase操作。因为重写公共历史,会给所有人的本地仓库带来巨大的混乱和冲突。只在自己的、未被共享的本地分支上进行rebase


如何使用 git 命令合并两个分支,发生冲突如何解决

面试官您好,在Git中合并两个分支,以及处理可能发生的冲突,是我日常开发工作中非常常见的操作。

1. 如何合并两个分支

假设我当前在develop分支,想要将一个已经开发完成的feature/user-login分支合并进来。我会执行以下步骤:

  1. 切换到目标分支:首先,确保我当前位于要接收合并的目标分支上。
    git checkout develop
    
  2. 拉取最新代码:在合并前,养成一个好习惯,先拉取远程develop分支的最新代码,确保我的本地develop分支是最新的。
    git pull origin develop
    
  3. 执行合并命令:使用git merge命令,将feature分支合并到当前的develop分支。
    git merge feature/user-login
    
    • Fast-forward (快速合并):如果develop分支在feature分支切出去之后,没有任何新的提交,那么Git会执行一次“快速合并”。它只是简单地将develop分支的指针,直接移动到feature分支的最新提交上,不会产生新的合并提交。
    • Three-way Merge (三方合并):如果develop分支也有了新的提交,Git会自动创建一个新的合并提交(Merge Commit),将两个分支的历史汇合在一起。

2. 当发生冲突时,如何解决?

如果Git在合并时发现,两个分支对同一个文件的同一块区域都进行了修改,它就无法自动决定应该保留哪个版本。这时,Git会暂停合并,并在文件中标记出冲突区域,等待我来手动解决

我解决冲突的流程,严格遵循以下四步:

第一步:识别冲突 (Identify the Conflicts)
  • git merge命令执行后,如果发生冲突,终端会明确地提示 CONFLICT (content): Merge conflict in [文件名]
  • 我会使用git status命令,来清晰地看到哪些文件当前正处于冲突状态(Unmerged paths)
第二步:手动解决冲突 (Resolve the Conflicts)

这是最关键的一步。我会打开每一个冲突的文件。在文件中,Git会用特殊的标记来标识出冲突的区域:

<<<<<<< HEAD
// 这部分是当前分支(develop)的代码
// (也即是“我的”改动)
=======
// 这部分是要被合并进来的分支(feature/user-login)的代码
// (也即是“别人的”改动)
>>>>>>> feature/user-login
  • 我的任务

    1. 仔细阅读并理解<<<<<<< HEAD=======>>>>>>>这三个标记所包围的代码块。
    2. 与相关的同事(如果是团队协作)进行沟通,或者根据业务逻辑,来决定最终应该保留哪部分代码,或者如何将两部分代码进行整合
    3. 手动编辑这个文件,删除掉所有Git的特殊标记 (<<<, ===, >>>),并保留下最终正确的代码。
  • 使用工具:对于复杂的冲突,我也会使用像VS Code、IntelliJ IDEA等IDE内置的可视化合并工具,它们能以三栏视图(你的、别人的、结果)来辅助解决冲突,更直观、更不容易出错。

第三步:标记为已解决 (Stage the Resolution)

当我确认一个文件中的所有冲突都已经被手动解决后,我需要告诉Git:“这个文件我已经处理好了”。

  • 命令:使用git add命令,将已解决的文件添加到暂存区。
    git add [已解决的文件名]
    # 或者,如果解决了多个文件
    git add .
    
    git add在这里的作用,就是将文件从“冲突状态”标记为“已解决状态”。
第四步:完成合并提交 (Complete the Merge)

当所有冲突文件都通过git add标记为已解决后,git status会显示All conflicts fixed but you are still merging.

  • 命令:此时,我只需要执行一次git commit,来完成这次合并。
    git commit
    
    • 执行这个命令后,Git会自动弹出一个预设好的commit message,通常是 Merge branch 'feature/user-login' into develop。我可以直接保存并退出,也可以补充一些关于如何解决冲突的说明。
  • 这次commit操作,会生成那个特殊的“合并提交”,标志着整个合并过程,包括冲突解决,都已成功完成。

意外情况:如何中止合并?

如果在解决冲突的过程中,发现情况太复杂,想放弃本次合并,回到合并前的状态,可以使用以下命令:

git merge --abort

这个命令会安全地中止合并过程,并将工作区恢复到执行git merge之前的状态。

通过这套清晰的流程,我就能自信、从容地处理任何分支合并和冲突解决的任务。

参考小林 coding

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xumistore

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值