当前位置: 首页 > news >正文

Git 的进阶功能和技巧

1、分支的概念和使用

1.1、什么是分支?

分支(Branch)是在版本控制中非常重要的概念。几乎所有版本控制系统都支持某种形式的分支。在 Git 中,分支是 Git 强大功能之一,它允许我们从主开发线分离出来,在不影响主分支的情况下并行地进行开发 (Git 分支管理 | 菜鸟教程)。你可以把分支想象成代码开发的平行宇宙:在一个分支上进行的修改,不会立即出现在另一个分支上。开发者可以利用分支来尝试新功能、修复 Bug 或进行实验,而无需担心弄乱主分支(通常是用于发布的稳定分支)。完成工作后,再把分支的改动合并回主分支,这样主分支就得到了更新,而整个过程主分支始终保持可用和稳定。

简而言之,使用分支可以让团队并行开发成为可能:每个人或每个新功能都在自己的分支上进行,互不干扰。等到需要整合时,再将分支合并。下面我们将介绍 Git 中有关分支操作的常用命令:创建分支、切换分支以及合并分支。

1.2、创建和切换分支(git branch, git checkout)

在 Git 中,新建分支使用命令 git branch,切换分支使用命令 git checkout(在较新的 Git 版本中,也可以使用 git switch 切换分支)。我们先来看如何创建一个分支:

  • git branch <分支名>:创建一个新的分支。此命令将在当前所在提交的基础上创建出一个分支指针。注意,仅执行 git branch 命令不会切换到新分支,只是新建了一个分支。

例如,如果我们当前在主分支 main 上,希望创建一个新分支来开发登陆功能,可以执行:

git branch login-feature

此操作会在当前提交的基础上建立一个名为 login-feature 的新分支。但是此时我们仍停留在 main 分支上。通过 git branch 不带参数可以列出所有本地分支,* 会标识当前所在的分支。

  • git checkout <分支名>:切换到指定的分支。切换分支会更改工作区的文件内容,以匹配该分支最近一次提交的状态。执行切换前,确保当前工作区没有未提交的修改,否则 Git 会拒绝切换(或者要求先 stash 暂存修改)以防止未提交的更改被覆盖。

继续上面的例子,我们创建了 login-feature 分支后,可以切换过去:

git checkout login-feature

执行后,Git 会提示已经切换到 login-feature 分支。现在开始的所有提交都会在 login-feature 分支上,而对 main 分支没有影响。

一个常用的快捷方式是将创建和切换合为一步完成:git checkout -b <分支名>。例如:

git checkout -b login-feature

如果 login-feature 不存在,这条命令会创建该分支并马上切换过去,相当于依次执行了 git branchgit checkout。这样更方便一些。

成功切换到新分支后,你可以像往常一样编辑代码、添加和提交。所有这些提交将属于 login-feature 分支,与主分支main的提交历史分离开。你可以随时使用 git checkout main 切换回主分支,如果此时打开项目文件,会发现那些在 login-feature 分支上做的改动不见了(因为此刻看到的是主分支上的内容)。别担心,这些改动仍然安全地保存在 login-feature 分支,只是当前未合并到主分支而已。

小贴士:在多人协作中,通常每个功能或修复都会新建一个分支进行开发,分支命名应简洁明了,如 feature/login-uibugfix/issue-101 等。主分支一般用来保存已经稳定并准备发布的代码。

1.3、合并分支(git merge)

当一个分支上的开发告一段落,需要将其成果合并回主分支(或其他分支)时,就可以使用 git merge 命令。git merge 用于将另一条分支的修改合并到当前分支上。

最常见的场景是将功能分支合并回主分支。例如,我们在 login-feature 分支上完成了登陆功能的开发和测试,现在希望把它合并到 main。步骤如下:

  1. 首先,切换到目标分支(即我们希望将改动合并到的分支)。这里是主分支 main

    git checkout main
    
  2. 然后执行合并命令,将功能分支合并过来:

    git merge login-feature
    

    这条命令表示:“把分支 login-feature 合并到当前分支(main)”。

执行 git merge 后,可能出现两种情况:

  • 快速前进(Fast-forward)合并: 如果主分支在功能开发期间没有新的提交,那么主分支实际上停留在功能分支的起点上。在这种情况下,Git 不需要创建新的合并提交,而只会简单地将主分支指向功能分支的最新提交。这种合并被称为 “快进”,表现出来就是主分支的历史直接向前推进,包含了功能分支的提交记录。

  • 非快进合并: 如果主分支在功能分支开发期间有自己的新提交,那么两个分支的历史出现了分叉。Git 会进行一次“三方合并”(three-way merge),自动创建一个新的合并提交来把分叉的历史合并起来。合并提交包含两个父提交,一个来自主分支原来的末尾,一个来自功能分支的末尾。合并提交的默认说明一般是“Merge branch 'login-feature' into main”。

在大多数情况下,如果没有冲突,Git 会自动完成以上两类合并。合并后,可以使用 git log --graph --oneline 来查看历史,分支的提交记录现在都出现在主分支的历史中了。完成合并后,功能分支的代码已经融合进主分支,通常我们可以选择删除那个已完成的功能分支以保持仓库整洁。删除本地分支的命令是:

git branch -d login-feature

-d 选项表示删除分支(该操作不会影响已经合并到的内容)。如果分支尚未合并就尝试删除,Git 会发出警告并拒绝删除,以免丢失未合并的修改。若确定要强行删除,可以用 -D(不推荐轻易使用)。

注意:合并前请确保将待合并的分支最新内容拉取下来(如果是多人协作且远程有更新的话)。比如在合并远程分支时,先 git pull origin login-feature 更新本地的 login-feature,再切换到 main 合并。这样可以减少合并冲突的概率。

2、合并冲突及解决方法

当两个分支都有各自的提交,并且修改了同一个文件的同一部分时,Git 在合并它们时就会遇到合并冲突(merge conflict)。简单来说,Git 无法自动判断以哪个分支的修改为准,需要人工介入决策。冲突最常发生于多人修改了相同代码的情形,或自己在不同分支对同一文件做了不同改动。

**冲突发生时的现象:**执行 git merge 后,终端会输出类似“Automatic merge failed; fix conflicts and commit the result.”的信息。这表示自动合并未能完成,需要手动解决冲突。此时,可以运行 git status 查看哪些文件存在冲突,输出中会列出冲突文件并标记为“both modified”。

解决合并冲突的一般步骤:

  1. 定位冲突区域: 用文本编辑器打开存在冲突的文件。你会看到特殊的冲突标记。例如,假设 config.txt 文件在两个分支有冲突,打开后可能出现这样的内容:

    <<<<<<< HEAD
    timeout=30
    =======
    timeout=60
    >>>>>>> login-feature
    

    上述标记表示:HEAD(当前分支,即 main 分支)的内容是 timeout=30,而要合并进来的 login-feature 分支的内容是 timeout=60。冲突区域被 <<<<<<<=======>>>>>>> 分隔成两部分,开发者需要决定哪一侧(或如何合并两者)才是正确的。

  2. 手动合并修改: 编辑冲突标记所在位置,根据需要保留或修改代码。例如,如果确定以 login-feature 分支的设置为准,就把 timeout=60 保留下来,删去其他冲突标记。如果两侧的修改都需要,可以手动合并成一个新的内容。总之,要编辑成我们想要的最终结果,并删除所有 <<<<<<, ======, >>>>>> 标记,使文件恢复正常的代码格式。

  3. 标记冲突已解决并暂存: 修改完所有冲突文件后,保存文件并退出编辑器。然后使用 git add <文件> 将刚刚解决冲突的文件添加到暂存区。标记为已解决意味着告诉 Git:“这文件的冲突我已经处理好了,可以纳入下一次提交”。

  4. 完成合并提交: 当所有冲突都解决并 git add 后,再次执行 git status 检查确认没有剩余未解决的冲突。接下来,运行 git commit 提交合并结果。值得注意的是,如果当前正处于合并过程,Git 在提交时会自动带上合并的默认提交信息(也可以自行编辑提交说明)。提交成功后,合并冲突就算解决完成,仓库历史中会出现这次合并提交。

如果在处理冲突过程中发现合并的方向有误或者想放弃此次合并,可以使用:

git merge --abort

该命令会中止合并操作,将分支恢复到合并开始前的状态,就像从未执行过合并一样。然后你可以重新尝试合并或者做其他处理。

小提示:合并冲突虽然听起来可怕,但有些图形化工具可以帮助解决。例如,使用 git mergetool 可以调起图形化冲突解决工具(如 KDiff3、P4Merge 等)对比冲突双方的差异,辅助你选择保留方案。另外,在团队协作中,减少冲突的最好办法是勤奋地沟通和同步:经常 git pull 同步他人修改,及时分享自己的改动。当冲突真的发生时,也尽量和相关开发者商量确认最终修改方案。

3、使用 git stash 暂存变更

在日常开发中,有时我们会遇到这样一种情况:正在一个分支上开发新功能,代码写到一半,突然接到任务需要立即切换到另一分支去修复紧急问题。但当前分支的修改尚未完成也不适合提交,这时该怎么办?直接切换分支会被 Git 拒绝(因为有未提交修改),把不完整的工作硬提交显然也不妥当。git stash 就是为了解决这种场景而设计的。

git stash 命令可以将当前工作目录和暂存区的修改暂时储藏起来,恢复到干净状态,好让你切换到别的分支去处理其他事务。等需要恢复时,再将暂存的修改取出,继续先前的工作。git stash 类似于一个堆栈(stack),可以储存多组未完成的修改。

常用的 stash 操作为:

  • 保存当前修改: 在有未提交改动的情况下运行:

    git stash
    

    这会把所有未提交的修改(包含已经 git add 暂存的和尚未暂存的)保存到一个匿名的储藏当中。Git 会输出类似 “Saved working directory and index state WIP on branchName...” 的信息,并将工作区恢复到最近一次提交的状态。你也可以加说明信息,例如 git stash save "message",但较新的 Git 版本中直接 git stash 也可以自动保存。

  • 查看暂存列表: 可以运行:

    git stash list
    

    来查看所有储藏的记录。最新的储藏在最上面,每条记录都有一个索引(如 stash@{0})和描述(包含当时所在的分支名和提交信息的摘要)。

  • 恢复暂存的修改: 当你要继续之前的工作时,在相应的分支上运行:

    git stash pop
    

    这条命令会将最近一次保存的修改取出应用到当前工作目录,同时从储藏列表中移除该记录。假如你想应用但保留储藏记录,可以使用 git stash apply stash@{n}(其中 n 是编号)来应用某个特定储藏但不删除它。

举个例子:你在 feature-X 分支上开发,写了一半代码。这时需要切换到 main 分支修复紧急 Bug。你可以执行 git stashfeature-X 的修改暂存起来,然后 git checkout main 切换分支(现在不会有未提交内容的阻碍)。修复完 Bug 并提交推送后,回到 feature-X 分支,用 git stash pop 将之前的工作恢复出来,继续完成剩下的开发。整个过程就像把工作“按暂停”,处理完其他事情后再“恢复播放”。

git stash 非常适合处理工作中断、上下文切换的场景。需要注意的是,stash 默认会储存未提交的所有内容(包括已暂存和未暂存的部分)。如果你只想暂存尚未暂存的修改而保留已暂存部分,可以使用 git stash -k (keep index)。另外,git stash 也能暂存新建但未追踪的文件(加 -u 参数),不过初学者一开始用默认行为即可。

最后,当某个储藏的修改不再需要时,可以用 git stash drop stash@{n} 删除,或者 git stash clear 清空所有暂存记录。合理地使用 stash,可以帮助你保持仓库提交历史的整洁,不会因为临时切换任务而到处留下零碎的“WIP”提交。

4、git reset 与 git revert 的区别

在使用 Git 的过程中,难免会遇到需要“撤销”更改的时候。Git 提供了多个命令来撤销或回退提交,其中最常见的是 git resetgit revert。它们都能达到“让项目回到之前状态”的目的,但原理和使用场景有所不同。理解两者区别有助于我们在不同情况下选择正确的方法去撤销修改。

4.1、git reset —— 重置提交历史

git reset 通常用于“回退”本地仓库的HEAD到某个旧的提交。通过 reset,我们可以舍弃最近的一些提交,使仓库退回到指定的历史节点。根据参数不同,git reset 既可以影响提交历史,也可以影响暂存区和工作区。

git reset [模式] <提交> 其中常用的模式有三种:

  • --mixed(默认模式):重置 HEAD 到指定提交,同时保留工作区改动但清除暂存区。简言之,就是把选定提交之后的修改全部撤出提交历史,放回工作区未暂存的状态。

  • --soft:更温和的重置,只重置 HEAD 提交,暂存区和工作区都保留这些改动。也就是说撤销最近的提交,但改动仍然保持在暂存区,几乎相当于把提交“取消”回暂存状态。

  • --hard:强制重置,提交历史、暂存区和工作区都同步到指定提交的状态。所有在那个提交之后的改动都会被彻底丢弃,从当前目录中消失(无法通过 Git 恢复)。

一个常见的用法是撤销最近一次提交。例如你发现刚才的提交有问题,不想要了,可以使用:

git reset HEAD~1

这条命令将仓库重置到上一个提交(即舍弃了最新提交)。默认模式下(mixed),最新提交的更改内容会留在工作区,这样你可以修改后重新提交,或者也可以放弃掉。相当于“退一步,但保留现场改动,允许你修正后再提交”。

如果你完全确定最近的提交是错误的且不需要其中的改动,可以用:

git reset --hard HEAD~1

警告:--hard 非常危险,它会抹掉工作区中自上一个提交以来的所有改动!除非你在其他地方还有这些改动的备份,否则此操作不可逆。因此务必谨慎使用。

git reset 更多地被视为一种本地操作,设计用于调整尚未公开(push)的历史。例如,你提交了一些临时代码想回退,或者想将上一个 commit 拆分/合并,这些都可以通过 reset 在本地实现,然后重新整理 commit,再推送。但是如果你已经把提交推送到远程仓库,其他人也基于它展开了工作,贸然 reset 自己的分支并强制推送(force push)会导致团队其他人的仓库历史不一致。因此,git reset 一般不应用于已发布到公用仓库的提交

除了回退整个提交,git reset 还可以用于取消暂存(unstage)。比如不小心 git add 了不想提交的文件,可以用 git reset HEAD <文件> 将其从暂存区移除,回到未暂存状态。

4.2、git revert —— 还原提交

与 reset 不同,git revert 是通过创建一个新的提交抵消某个历史提交的影响,从而达到“撤销更改”的目的。git revert <提交> 会根据指定的提交,产生一个内容相反的新提交,应用这个新提交后,仓库状态看起来就像撤回了指定的提交一样。但是,原先的提交记录依然保留在历史中,只是后面附加了一个“反向提交”来消除它的效果。

git revert 常用于已经推送到远程仓库并分享给他人的提交的撤销。因为 revert 不会重写历史,它是往历史里添加记录,所以对团队其他成员不会产生破坏性的影响。实际上,revert 后大家同步代码时,会拿到一个新的提交,该提交做了撤销更改的操作。

举例来说,假如在主分支有一个错误的提交 C 已经推送,现在想撤销它。如果用 reset,我们必须强制修改历史,会给协作者带来困扰;但使用 revert:

git revert <提交C的哈希>

Git 会自动创建一个新提交 C'(内容为将提交 C 引入的改动反向修改回去),提交消息会注明它是一次 revert。例如 “Revert "添加XX功能"”。推送这个新提交后,其他人拉取更新,他们的仓库会保留提交 CC' 两条记录,但最终文件内容与 C 未发生前一致了。

git revert 的语法通常是指定具体的提交哈希,也可以使用诸如 HEADHEAD~2 这类引用来表示相对位置。还可以连续 revert 多个提交(需要逐个执行或使用...范围)。需要注意,如果要 revert 的提交不是最新的,或者涉及文件改动与后续提交有重叠,可能也会出现冲突,处理方式和合并冲突类似:修改冲突文件然后继续 git revert --continue 完成操作(revert 一个提交序列的情况)。

小结比较:git revertgit reset 的根本区别在于是否保留历史git revert 是用一次新的 commit 来回滚之前的 commit,而 git reset 是直接删除指定的 commit 。换句话说:

  • 使用 git reset,仓库会“丢弃”某段历史,就好像那些提交从未发生过。而使用 git revert,仓库历史会完整保留每一次改变,只是附加记录某次改动被撤销了。

  • git reset 改变了当前分支的 commit 链(HEAD 向后移动),而 git revert 则让 HEAD 继续前进,只是新增的那个提交的内容与被还原的提交相反,从效果上抵消了它 。

  • 从协作角度,reset 要求所有协作者都配合修改历史(否则会出现分歧),而 revert 则是一个正常的提交,其他人只需拉取即可。因此,撤销公开发布的提交建议使用 git revert ;仅在本地调整尚未发布的提交时可以使用 git reset

使用场景建议:

  • 当你提交后立刻发现错误,且该提交尚未推送,想彻底抹除这次错误提交,可以考虑 git reset 将 HEAD 回退(或者用 git commit --amend 更正提交,这也是改历史的方法之一)。但如果该提交已经分享出去,不要用 reset 撤销。

  • 当某次提交已经推送并被他人拉取,如果后来证明需要撤销,就用 git revert 来安全地回滚。这样所有人的历史都会保持一致,只是多了一条修正记录。

  • 如果需要清除多个无用的提交(比如试验性质的提交),而这些提交尚未推送,可以用 git reset 一次性回退。不过Git还有更高级的工具如交互式 rebase 可以用于整理多个提交(见下一节)。

总之,谨记:不要在公共仓库的分支上使用 git reset --hard 等破坏性命令;撤销公开提交请选择 git revert。两者各有用途:reset 修剪历史,revert 保持历史连续性地进行撤销。

5、使用 git rebase 优化提交历史

在多人协作和长期开发的项目中,提交历史很容易变得复杂。频繁的合并会产生很多分支交叉点和合并提交,而且每个人的提交粒度和风格也不尽相同。Git 提供的 git rebase 命令可以在一定程度上优化和整理提交历史,使之更加线性和简洁。不过,rebase 修改历史的特性也需要我们小心使用。

5.1、变基(Rebase)简介

git rebase 的直译是“变基”。它的作用是改变提交的基底。具体来说,就是将一系列提交“剪下来”,然后重新应用(Replay)在另一位置。结果就是提交历史发生了重排或重构。在实际操作中,rebase 常用于两种情况:

  1. 在分支合并前,更新分支以基于最新的主分支: 假设我们有一个功能分支 feature 从主分支 main 分出。一段时间后,main 上有了新的提交,而 feature 也有自己的提交。这时,我们可以在将 feature 合并回 main 之前,先切到 feature 分支执行 git rebase main。这会把 feature 分支上的提交转移到 main 分支最新提交之后,仿佛 feature 是从当前最新的 main 开始开发的一样。这样处理后,当我们再合并 feature 到 main 时,可以避免产生额外的合并提交,使历史呈现为一条直线。

    例如:最初 main 有 A 提交,feature 分支从 A 分出做了提交 B;期间 main 上又有了提交 C。此时 feature 分支执行 git rebase main 后,feature 上的 B 提交会被移到 C 的后面,形成一个新的提交 B'。现在 main 的历史是 A - C,feature 的历史是 A - C - B'。随后如果 fast-forward 合并,主分支将线性包含 A - C - B',看起来就像 B'是在 C 之后提交的一样,消除了分叉记录。

    Rebase 后由于提交被重新应用,其 SHA-1 哈希值会发生变化(B 变成 B'),所以这个过程重写了分支的历史。Git 会逐个应用 feature 分支的每个 commit,因此如果这些提交与 main 的更改有冲突,会暂停并要求解决冲突(类似前面合并冲突的过程,但命令变为 git rebase --continue 继续下一个,或者 git rebase --abort 放弃变基)。

  2. 整理本地的多次碎片提交: 在开发一个功能时,可能我们会进行很多次临时的提交(比如调试信息、实验尝试等)。在提交到主分支前,团队往往希望这些历史被整理得整洁一些,例如合并相关的改动、编辑规范提交消息等。git rebase -i(交互式变基)可以帮助实现这一点。通过交互式 rebase,我们可以选择**压缩(squash)**多个提交为一个、修改(edit)某些提交的内容或消息、甚至改变提交顺序等。

    例如,假设我们在当前分支有最近的5个提交需要整理,只需执行:

    git rebase -i HEAD~5
    

    Git 会打开一个文本界面,列出这5个提交以及可选的操作(pick, squash, edit 等)。根据需要编辑保存后,Git 就会按照新的指令重新依次应用这5个提交(期间如果有冲突需解决)。整理完成后,分支历史就被改写成了我们想要的样子。常用的操作包括将多次“小提交”合并成一次有意义的大提交(squash),或修改提交信息(reword/edit)以更清晰地描述改动。

rebase vs merge: Rebase 和 Merge 是实现分支集成的两种方式,各有优劣。Merge保留了完整的历史轨迹(包括分叉点和合并节点),能看出代码是如何在分支上演进再合流的;而Rebase则让历史看上去像一条直线,没有分叉(就像所有改动都串行发生在一个分支上)。Rebase后的历史往往更加简洁、线性,使用 git log 浏览时不会被大量的合并提交干扰。但另一方面,Merge不改变已有历史,而Rebase通过重写历史来达成线性,可能会让历史记录失去分支上下文。

**使用注意:由于 rebase 会重写提交历史,所以有一个“变基黄金法则”**必须牢记:绝不要在公共分支上对已经推送的提交执行 rebase 。换句话说,仅对尚未分享给别人的本地分支使用 rebase。一旦你在协作的分支(比如 origin/main 或团队共享的feature分支)上做了变基,然后强制推送,会导致他人的仓库出现冲突和混乱(因为他们的提交历史与你的不一致)。因此,在共享环境下,rebase 只能用于更新自己本地的开发分支,再正常推送,或用于自己fork后在PR合并前整理提交。遵循“变基黄金法则”:绝不在公有仓库的分支上执行 rebase。

5.2、举例:使用 rebase 更新分支

假设你正在开发分支 feature-A 上工作,此时需要同步主分支 main 最新的更新。在 merge 和 rebase 两种做法中,如果选择 rebase:

# 确保主分支是最新的
git checkout main
git pull origin main# 切回功能分支,执行变基
git checkout feature-A
git rebase main

执行上述操作后,Git 会将 feature-A 分支上的提交一个接一个地转移到 main最新提交之后。如果没有冲突,变基会自动完成。若有冲突,Git 会停止并让你解决冲突,解决完毕后用 git add 将更改暂存,然后 git rebase --continue 继续剩下的提交。全部结束后,切换到 main,可以使用 git merge feature-A 快速合并(fast-forward)或者直接 git push origin feature-A:main 推送 feature-A 分支作为 main 分支的新更新(比如在pull request被接受时)。这样 main 的历史就是线性的,没有额外的分叉点。相比直接 merge,这种方法避免了合并提交,使历史更干净。

5.3、举例:交互式 rebase 整理提交

假如在开发一个功能过程中,你做了3次提交:第一次提交遗漏了一些文件,第二次补上遗漏,第三次修正了前面提交信息的一个拼写错误。最终这些其实都是为实现同一个功能,理想情况下可以合并成一个提交。使用 git rebase -i 可以实现:

git rebase -i HEAD~3

在打开的编辑界面中,你会看到类似:

pick f1a2b3c Implement feature X
pick a3d4e5f Add missing files for feature X
pick b4c6d7e Fix typo in feature X description

将后两行的 pick 改为 squash(或缩写 s),表示将它们压缩到第一个提交中。保存退出后,Git 会应用第一个提交,然后将后两个提交的改动合并进第一个。在这个过程中会让你编辑合并后的提交消息,你可以整合原先三个提交的信息为一条完整描述。完成后,历史中就只剩下一条提交,包含所有改动。这就是一次提交压缩操作。类似地,你可以重新排序提交顺序(调整行的先后)、丢弃某些提交(改为 drop),或者标记 edit 手动修改某个提交的内容(在 rebase 过程中暂停让你修正然后继续),等等。

通过 rebase 的这些技巧,我们能够在不影响最终代码结果的情况下,让提交历史更符合阅读和维护的需要。例如,将“修复拼写错误”“调整格式”这类杂碎 commit 合并到主要 commit 中,保持主干历史的清晰。当然,这种修改历史的动作只应对自己本地分支进行,在与团队协作前完成整理。

6、推荐的 Git 使用流程与团队协作技巧

掌握了上述 Git 高级功能后,我们来讨论一下在团队协作中,如何高效地使用 Git 工作,以及一些值得遵循的实践规范。良好的协作流程可以避免很多问题,确保团队所有成员各司其职又步调一致。

下面是一套常见的 Git 团队协作开发流程(以功能开发为例):

  1. 同步主分支: 在开始新的工作之前,先切换到主分支(例如 maindevelop),执行 git pull 拉取远程最新代码。这样可以确保你的主分支是最新状态,为后续创建新分支打好基础。

  2. 新建功能分支: 从主分支创建一个新的功能分支用于开发你的任务:

    git checkout -b feature/some-feature
    

    分支命名应清晰反映工作内容,比如 feature/login-authbugfix/issue-101。在这个分支上进行你的开发工作,与此同时主分支的代码保持不变。

  3. 在分支上进行开发并频繁提交: 在功能分支上编写代码。建议遵循“早提交、勤提交”的原则,每实现一个独立的小功能或达到一个里程碑就使用 git addgit commit 提交一次。提交信息要清晰描述修改目的,例如 “Add login form UI” 而不是 “update code”。频繁提交有助于记录开发轨迹,也使得出现问题时更容易定位和回溯。

  4. 将分支推送到远程: 虽然还在开发中,但也最好经常将本地分支推送到远程仓库备份,并让团队可见你的进展:

    git push -u origin feature/some-feature
    

    使用 -u 将本地分支与远程同名分支建立跟踪关系。之后如果继续有新的提交,可定期 git push 更新远程。这样即使你的电脑发生故障,代码也不会丢失,同时同事也能看到并拉取你的最新代码(如果需要协作)。

  5. 与主分支保持同步: 开发过程中,主分支可能也在推进其他更新。为避免分支长期偏离主线,最好定期用主分支的变化更新你的功能分支。例如可以执行 git pull origin main(或 git fetch 然后在分支上 git merge origin/main)将主分支的修改合并过来。如果希望更整洁,可以用 git rebase origin/main 将你的提交变基到主分支后。这能减少最终合并时的冲突概率。

  6. 提交合并请求(Pull Request)并代码审查: 功能开发完成并经过自测后,将你的分支代码合并回主分支。通常做法是在托管平台上创建一个 Pull Request(合并请求),描述你的更改,通知团队其他成员审核代码。代码审查(Code Review)可以发现潜在问题并确保代码质量。在 PR 通过后,维护者会将你的功能分支合并到主分支。合并可以采用 Squash Merge(压缩成一次提交)或者普通 Merge,根据团队约定。

  7. 清理分支并部署: 功能成功合并进主分支后,可以删除远程和本地的功能分支(以避免过多分支混乱仓库):

    git branch -d feature/some-feature        # 删除本地分支
    git push origin --delete feature/some-feature  # 删除远程分支
    

    之后,在本地切回主分支并 git pull 获取最新的代码。如果需要部署或发布,可以根据主分支最新代码进行。

在上述流程中,有一些值得强调的团队协作技巧和最佳实践:

  • 确保主分支始终可用: 主分支(如 main/master)通常代表产品的稳定版本。不在主分支上直接开发新功能,所有对主分支的修改都通过分支合并来完成。这样主分支上的代码始终是可以随时发布或部署的。如果需要紧急修复,也使用热修复分支来完成并尽快合并。

  • 善用分支命名和标签: 清晰的分支命名有助于协作。例如见到 feature/login-auth 自然知道是开发登录认证功能。对于重要的里程碑或发布版本,使用 Git 标签(tag)来标记提交,方便以后查找对应版本的代码。

  • 编写有意义的提交消息: 一个好的提交消息能够简洁概括更改内容或原因,方便日后查阅历史时了解每次改动的目的。避免使用含糊不清的描述如“修改代码”“更新了几个文件”。常见格式是在简短的标题后可选地补充细节描述,团队可以采用一致的 commit message 风格(比如遵循 Angular 提交信息规范等)。

  • 及时拉取和合并: 团队协作中,每个人应经常同步远程仓库的最新变化(git pull),特别是在开始新的任务或准备将自己的修改推送前。这可以最大限度减少冲突。当发现与他人有冲突时,积极沟通,共同决定如何解决。

  • 不要在公共分支上强制推送: 避免使用 git push --force 强制推送主分支或他人也在用的分支。如果必须(例如更正错误的历史),也要提前与团队说明,确保别人没有基于旧历史继续工作。强制推送可能覆盖他人的提交,通常只有仓库管理员在特殊情况下才执行,而且通常通过 Pull Request 来协同完成。

  • 使用 .gitignore 保持仓库整洁: 在协作项目中,制定 .gitignore 文件很重要,确保编译产物、临时文件、不需要纳入版本控制的文件不被提交。例如日志文件、IDE配置、依赖生成的文件等应被忽略。这可以减少不必要的冲突和仓库臃肿。

  • 定期备份和维护仓库: 虽然远程仓库本身就是备份,但也可以克隆镜像仓库作为额外备份。对长期项目,定期清理无用的分支,避免仓库中累积过多过期分支。对于超大的历史(比如大量二进制文件意外提交),可以考虑使用 Git 工具(如 BFG Repo-Cleaner)清理,但这些都是进阶话题了。

相关文章:

Git 的进阶功能和技巧

1、分支的概念和使用 1.1、什么是分支&#xff1f; 分支&#xff08;Branch&#xff09;是在版本控制中非常重要的概念。几乎所有版本控制系统都支持某种形式的分支。在 Git 中&#xff0c;分支是 Git 强大功能之一&#xff0c;它允许我们从主开发线分离出来&#xff0c;在不…...

WSL1升级到WSL2注意事项

今天要在WSL上安装docker&#xff0c;因为机器上安装了wsl1&#xff0c;docker安装后启动不了&#xff0c;通过询问deepseek发现docker只能在wsl2上安装&#xff0c;因此就想着将本机的wsl1升级到wsl2。 确保你的 Windows 系统是 Windows 10&#xff08;版本 1903 及以上&…...

392. 判断子序列

https://leetcode.cn/problems/is-subsequence/?envTypestudy-plan-v2&envIdtop-interview-150因为是子序列我们只要关心后一个字符在前一个字符后面出现过就行&#xff0c;至于在哪出现出现几次我们不关心&#xff0c;所以我们可以用HashMap<Character, ArrayList<…...

在 VMware 中为 Ubuntu 24.04 虚拟机设置共享文件夹后,在虚拟机中未能看到共享的内容

在 VMware 中为 Ubuntu 24.04 虚拟机设置共享文件夹后&#xff0c;如果在虚拟机中未能看到共享的内容&#xff0c;可能是由于以下原因&#xff1a; VMware Tools 未正确安装&#xff1a;共享文件夹功能依赖于 VMware Tools 或 Open VM Tools。如果未安装或安装不完整&#xff0…...

台式电脑插入耳机没有声音或麦克风不管用

目录 一、如何确定插孔对应功能1.常见音频插孔颜色及功能2.如何确认电脑插孔?3.常见问题二、 解决方案1. 检查耳机连接和设备选择2. 检查音量设置和静音状态3. 更新或重新安装声卡驱动4. 检查默认音频格式5. 禁用音频增强功能6. 排查硬件问题7. 检查系统服务8. BIOS设置(可选…...

Windchill开发-WTContainer相关API整理

Windchill开发-WTContainer相关API整理 概述各容器对象相关方法站点容器组织容器产品容器/存储库容器上下文团队角色组 文件夹 方法汇总 概述 Windchill 的环境由一组容器组成&#xff0c;容器分为三级&#xff1a;第一级为站点容器&#xff0c;第二级为组织容器&#xff0c;第…...

理解JSON-RPC 2.0 协议

JSON-RPC 2.0是指一种基于 JSON 的远程过程调用协议&#xff0c;用于在网络上进行跨平台和跨语言的通信。它提供了一种简单、轻量级的方式来实现客户端和服务器之间的方法调用和数据交换。在原文中&#xff0c;JSON-RPC 2.0被用来描述 STDIO 传输机制中消息的格式&#xff0c;即…...

【 C# 使用 MiniExcel 库的典型场景】

以下是 C# 使用 MiniExcel 库的典型场景及代码示例&#xff1a; 一、基础读取操作 强类型读取‌&#xff08;需定义数据模型类&#xff09; 定义与 Excel 列名匹配的类后直接映射为对象集合&#xff1a; csharp Copy Code public class UserAccount { public int Id { get; …...

创建 Pod 失败,运行时报错 no space left on device?

遇到创建Pod失败并报错“no space left on device”时&#xff0c;请按照以下步骤排查和解决问题&#xff1a; 1. 定位问题来源 查看Pod事件&#xff1a; kubectl describe pod <pod-name> -n <namespace> 在输出中查找 Events 部分&#xff0c;确认错误是否与…...

[leetcode]查询区间内的所有素数

一.暴力求解 #include<iostream> #include<vector> using namespace std; vector<int> result; bool isPrime(int i) { if (i < 2) return false; for (int j 2;j * j < i;j) { if (i % j 0) { …...

【Web安全】如何在 CDN 干扰下精准检测 SSRF?Nuclei + Interactsh 实战

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 背景 在日常漏洞复核中&#xff0c;我们常用 DNSLog 平台判断目标是否存在 SSRF 漏洞&#xff1a;只要请…...

输入框只能输入非中文字符

在 Qt 中&#xff0c;可以通过设置输入法过滤器&#xff08;QInputContext&#xff09;或使用正则表达式来限制输入框&#xff08;QLineEdit 或 QTextEdit&#xff09;只能输入非中文字符。以下是两种实现方法&#xff1a; ### 方法 1&#xff1a;使用正则表达式 可以通过 QLi…...

LeeCode 136. 只出现一次的数字

给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 示例 1 &#xff1a; 输入&#xff1…...

Traefik应用:配置容器多个网络时无法访问问题

Traefik应用&#xff1a;配置容器多个网络时无法访问问题 介绍解决方法问题原因&#xff1a; **容器多网络归属导致 Traefik 无法正确发现路由规则**。解决方案方法 1&#xff1a;将应用容器 **仅连接** 到 traefik-public 网络方法 2&#xff1a;显式指定 Traefik 监听的网络 …...

超便捷超实用的文档处理工具,PDF排序,功能强大,应用广泛,无需下载,在线使用,简单易用快捷!

小白工具https://www.xiaobaitool.net/files/pdf-sort/ 中的 PDF 排序功能是一项便捷实用的文档处理服务&#xff0c;以下是其具体介绍&#xff1a; 操作便捷直观&#xff1a;用户上传 PDF 文件后&#xff0c;可通过直接拖动页面缩略图来调整顺序&#xff0c;就像在纸质文档中…...

zsh: command not found - 鸿蒙 HarmonyOS Next

终端中执行 hdc 命令抛出如下错误; zsh: command not found 解决办法 首先,查找到 DevEco-Studio 的 toolchains 目录路径; 其次,按照类似如下的文件夹层级结果推理到 toolchains 子级路径下,其中 sdk 后一级的路径可能会存在差异,以实际本地路径结构为主,直至找到 openharm…...

【动态规划】 深入动态规划—两个数组的dp问题

文章目录 前言例题一、最长公共子序列二、不相交的线三、不同的子序列四、通配符匹配五、交错字符串六、两个字符串的最小ASCII删除和七、最长重复子数组 结语 前言 问题本质 它主要围绕着给定的两个数组展开&#xff0c;旨在通过对这两个数组元素间关系的分析&#xff0c;找出…...

金融数据分析(Python)个人学习笔记(7):网络数据采集以及FNN分类

一、网络数据采集 证券宝是一个免费、开源的证券数据平台&#xff08;无需注册&#xff09;&#xff0c;提供大盘准确、完整的证券历史行情数据、上市公司财务数据等&#xff0c;通过python API获取证券数据信息。 1. 安装并导入第三方依赖库 baostock 在命令提示符中运行&…...

指定运行级别

linux系统下有7种运行级别,我们需要来了解一下常用的运行级别,方便我们熟悉以后的部署环境,话不多说,来看. 开机流程&#xff1a; 指定数级别 基本介绍 运行级别说明: 0:关机 相当于shutdown -h now ⭐️默认参数不能设置为0,否则系统无法正常启动 1:单用户(用于找回丢…...

7.第二阶段x64游戏实战-string类

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;7.第二阶段x64游戏实战-分析人物属性 string类是字符串类&#xff0c;在计算机中…...

【MySQL基础】左右连接实战:掌握数据关联的完整视图

1 左右连接基础概念 左连接(left join)和右连接(right join)是MySQL中两种重要的表连接方式&#xff0c;它们与内连接不同&#xff0c;能够保留不匹配的记录&#xff0c;为我们提供更完整的数据视图。 核心区别&#xff1a; left join&#xff1a;保留左表所有记录&#xff0c;…...

建筑工程行业如何选OA系统?4大主流产品分析

工程行业项目的复杂性与业务流程的繁琐性对办公效率提出了极高要求。而OA 系统&#xff08;办公自动化系统&#xff09;的出现&#xff0c;为工程企业提供了一种全新的、高效的管理模式。 工程行业OA系统选型关键指标 功能深度&#xff1a;项目管理模块完整度、文档版本控制能…...

动态科技感html导航网站源码

源码介绍 动态科技感html导航网站源码&#xff0c;这个设计完美呈现了科幻电影中的未来科技界面效果&#xff0c;适合展示技术类项目或作为个人作品集的入口页面&#xff0c;自适应手机。 修改卡片中的链接指向你实际的HTML文件可以根据需要调整卡片内容、图标和颜色要添加更…...

CLIPGaze: Zero-Shot Goal-Directed ScanpathPrediction Using CLIP

摘要 目标导向的扫描路径预测旨在预测人们在搜索视觉场景中的目标时的视线移动路径。大多数现有的目标导向扫描路径预测方法在面对训练过程中未出现的目标类别时,泛化能力较差。此外,它们通常采用不同的预训练模型分别提取目标提示和图像的特征,导致两者之间存在较大的特征…...

wsl-docker环境下启动ES报错vm.max_map_count [65530] is too low

问题描述 在windows环境下用Docker Desktop&#xff08;wsl docker&#xff09;启动 elasticsearch时报错 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]解决方案 方案一 默认的vm.max_map_count值是65530,而es需要至少262…...

js chrome 插件,下载微博视频

起因&#xff0c; 目的: 最初是想下载微博上的NBA视频&#xff0c;因为在看网页上看视频很不方便&#xff0c;快进一次是10秒&#xff0c;而本地 VLC 播放器&#xff0c;快进一次是5秒。另外我还想做点视频剪辑。 对比 原来手动下载的话&#xff0c;右键检查&#xff0c;复制…...

游戏引擎学习第212天

"我们将同步…"α 之前我们有一些内容是暂时搁置的&#xff0c;因为在调整代码的过程中&#xff0c;我们做了一些变动以使代码更加简洁&#xff0c;这样可以把数据放入调试缓冲区并显示出来&#xff0c;这一切现在看起来已经好多了。尽管现在看起来更好&#xff0c;…...

PXE远程安装服务器

目录 搭建PXE远程安装服务器 1、准备Linux安装源&#xff1a; 2、安装并启用TFTP服务&#xff1a; 3、准备Linux内核、初始化镜像文件 4、准备PXE引导程序 5、安装并启用DHCP服务 6、&#xff08;1&#xff09;配置启动菜单文件(有人应答) ‌6、&#xff08;2&#xff09…...

软件测试之功能测试详解

一、测试项目启动与研读需求文档 &#xff08;一&#xff09; 组建测试团队 1、测试团队中的角色 2、测试团队的基本责任 尽早地发现软件程序、系统或产品中所有的问题。 督促和协助开发人员尽快地解决程序中的缺陷。 帮助项目管理人员制定合理的开发和测试计划。 对缺陷进行…...

Python深度学习基础——卷积神经网络(CNN)(PyTorch)

CNN原理 从DNN到CNN 卷积层与汇聚 深度神经网络DNN中&#xff0c;相邻层的所有神经元之间都有连接&#xff0c;这叫全连接&#xff1b;卷积神经网络 CNN 中&#xff0c;新增了卷积层&#xff08;Convolution&#xff09;与汇聚&#xff08;Pooling&#xff09;。DNN 的全连接…...

pytorch 反向传播

文章目录 概念计算图自动求导的两种模式 自动求导-代码标量的反向传播非标量变量的反向传播将某些计算移动到计算图之外 概念 核心&#xff1a;链式法则 深度学习框架通过自动计算导数(自动微分)来加快求导。 实践中&#xff0c;根据涉及号的模型&#xff0c;系统会构建一个计…...

VSCode解决中文乱码方法

目录 一、底层原因 二、解决方法原理 三、解决方式&#xff1a; 1.预设更改cmd临时编码法 2.安装插件法&#xff1a; 一、底层原因 当在VSCode中遇到中文显示乱码的问题时&#xff0c;这通常是由于文件编码与VSCode的默认或设置编码不匹配&#xff0c;或…...

pandas.DataFrame.dtypes--查看和验证 DataFrame 列的数据类型!

查看每列的数据类型&#xff0c;方便分析是否需要数据类型转换 property DataFrame.dtypes[source] Return the dtypes in the DataFrame. This returns a Series with the data type of each column. The result’s index is the original DataFrame’s columns. Columns with…...

高性能服务开发利器:redis+lua

Redis 与 Lua 脚本的结合&#xff0c;其核心价值在于 ​原子性操作​ 和 ​减少网络开销。 一、Redis 执行 Lua 脚本的优势​ ​原子性​ Lua 脚本在 Redis 中原子执行&#xff0c;避免多命令竞态条件。 ​减少网络开销​ 将多个 Redis 命令合并为一个脚本&#xff0c;减少客…...

开源智能体MetaGPT记忆模块解读

MetaGPT 智能体框架 1. 框架概述 MetaGPT 是一个​​多智能体协作框架​​&#xff0c;通过模拟软件公司组织架构与工作流程&#xff0c;将大语言模型&#xff08;LLM&#xff09;转化为具备专业分工的智能体&#xff0c;协同完成复杂任务。其最大特点是能够将​​自然语言需…...

Docker部署MySQL大小写不敏感配置与数据迁移实战20250409

Docker部署MySQL大小写不敏感配置与数据迁移实战 &#x1f9ed; 引言 在企业实际应用中&#xff0c;尤其是使用Java、Hibernate等框架开发的系统&#xff0c;MySQL默认的大小写敏感特性容易引发各种兼容性问题。特别是在Linux系统中部署Docker版MySQL时&#xff0c;默认行为可…...

【RabbitMQ】延迟队列

1.概述 延迟队列其实就是队列里的消息是希望在指定时间到了以后或之前取出和处理&#xff0c;简单来说&#xff0c;延时队列就是用来存放需要在指定时间被处理的元素的队列。 延时队列的使用场景&#xff1a; 1.订单在十分钟之内未支付则自动取消 2.新创建的店铺&#xff0c;…...

深兰科技携多款AI医疗创新成果亮相第七届世界大健康博览会

4月8日&#xff0c;以“AI赋能 健康生活”为主题的2025年(第七届)世界大健康博览会(以下简称健博会)在武汉隆重开幕。应参展企业武汉市三甲医院——武汉中心医院的邀请&#xff0c;深兰科技最新研发的新一代智慧医疗解决方案和产品在其展位上公开亮相。 本届展会吸引了来自18个…...

20周年系列|美创科技再度入围「年度高成长企业」系列榜单

近日&#xff0c;资深产业信息服务平台【第一新声】发布「2024年度科技行业最佳CEO及高成长企业榜」&#xff0c;美创科技凭借在数据安全领域的持续创新和广泛行业实践&#xff0c; 再度入围“年度网络安全高成长企业”、“年度高科技高成长未来独角兽企业TOP30”。 美创科技作…...

saltstack分布式部署

一、saltstack分布式 在minion数量过多时&#xff0c;通过部署salt代理&#xff0c;减轻master负载 1、在master上删除说有minion证书 2、在minion上删除旧master信息 3、安装部署salt-syndic 4、修改minion 5、在master上签署代理的证书 6、在代理上签署minion证书 7、测试...

CCRC 与 EMVCo 双认证:中国智能卡企业的全球化突围

在全球经济一体化的浪潮中&#xff0c;智能卡行业正经历着前所未有的变革与发展。中国智能卡企业凭借技术优势与成本竞争力&#xff0c;在国内市场成绩斐然。然而&#xff0c;要想在国际市场站稳脚跟&#xff0c;获取权威认证成为关键一步。CCRC 与 EMVCo 双认证&#xff0c;宛…...

逆向工程的多层次解析:从实现到领域的全面视角

目录 前言1. 什么是逆向工程&#xff1f;2. 实现级逆向&#xff1a;揭示代码背后的结构2.1 抽象语法树的构建2.2 符号表的恢复2.3 过程设计表示的推导 3. 结构级逆向&#xff1a;重建模块之间的协作关系3.1 调用图与依赖分析3.2 程序与数据结构的映射 4. 功能级逆向&#xff1a…...

【Docker项目实战】使用Docker部署ToDoList任务管理工具

【Docker项目实战】使用Docker部署ToDoList任务管理工具 一、ToDoList介绍1.1 ToDoList简介1.2 ToDoList主要特点二、本次实践规划2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载ToDoList镜像…...

基于SpinrgBoot+Vue的医院管理系统-026

一、项目技术栈 Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;Vue开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 二、功能介绍 (1)…...

如何进行数据安全风险评估总结

一、基于场景进行安全风险评估 一、概述 数据安全风险评估总结(一)描述了数据安全风险评估的相关理论,数据安全应该关注业务流程,以基础安全为基础,以数据生命周期及数据应用场景两个维度为入口进行数据安全风险评估。最后以《信息安全技术 信息安全风险评估规范》为参考,…...

用 npm list -g --depth=0 探索全局包的秘密 ✨

用 npm list -g --depth0 探索全局包的秘密 &#x1f680;✨ 嗨&#xff0c;各位开发者朋友们&#xff01;&#x1f44b; 今天我们要聊一个超实用的小命令——npm list -g --depth0&#xff01;它就像一个“全局包侦探”&#x1f575;️‍♂️&#xff0c;能帮你快速查出系统中…...

依靠视频设备轨迹回放平台EasyCVR构建视频监控,为幼教连锁园区安全护航

一、项目背景 幼教行业连锁化发展态势越发明显。在此趋势下&#xff0c;幼儿园管理者对于深入了解园内日常教学与生活情况的需求愈发紧迫&#xff0c;将这些数据作为提升管理水平、优化教育服务的重要依据。同时&#xff0c;安装监控系统不仅有效缓解家长对孩子在校安全与生活…...

新闻发稿软文发布投稿选择媒体时几大注意

企业在选择新闻稿发布媒体时&#xff0c;需要综合考虑以下几个关键因素&#xff1a; 1. 匹配媒体定位 企业应根据自身品牌定位和传播目标&#xff0c;选择与之契合的媒体平台。确保新闻稿的内容和风格与媒体的定位高度一致&#xff0c;从而提高稿件被采纳的可能性。 2. 衡量…...

[Scade One] Swan与Scade 6的区别 - signal 特性的移除

signal 特性移除 在 Scade One 引入的Swan中&#xff0c;移除了Scade 6中存在的signal 特性。比如 Scade 6 中的signal声明 sig sig_o;或者signal使用&#xff0c;比如 o sig_o; 在Swan中已经被移除。 不过Swan仍旧保留了对布尔流的emit操作&#xff0c;比如 emit a if c …...

亚马逊推出“站外代购革命“:跨境购物进入全平台整合时代

一、创新功能解析&#xff1a;平台边界消融的购物新范式 亚马逊最新推出的External Product Fulfillment&#xff08;EPF&#xff09;服务&#xff0c;正以技术创新重构全球电商格局。这项被称作"代购终结者"的功能具备三大核心突破&#xff1a; 全链路智能化 • 智能…...