当前位置: 首页 > 后端技术 > Java

如何在不切换Git分支的情况下同时处理多个分支?

时间:2023-04-01 15:17:00 Java

在上一篇文章中,保持一个干净的Git提交记录就足够了。大家看完后纷纷私信说这是一个很好用的功能。突然想到工作中用到的另一个Git功能,也很好用,我们一定要说全,作为程序员,我们都应该有一种感觉,一旦进入某个项目,从开发,到发布生产,到hotfix,到后期维护,基本都有你的份,我们在开发某个特征。老大突然跳出来说你在production做hotfix比较常见。面对这种情况,使用Git的我们通常有两种解决方案:草草提交未完成的特性,然后切换分支到hotfixgitstash|gitstashpop暂存工作内容,然后切换到hotfix第二种方式比第一种方式好很多,但是面对下面的场景,stash仍然不是一个很好的解决方案我们面对的场景是运行在主分支上的长时间测试,切换到hotfix或feature,测试会中断。项目很大,频繁切换索引,成本很高。还有几年前发布的老版本,设置和现在的不一样,IDE重构适配切换也会带来很多切换分支,需要重新设置相应的环境变量。比如dev/qa/prod需要切换到同事的代码,帮助调试代码,重现问题。有同学觉得gitclonemultiplerepos就够了吗?这是解决上述问题的一种方法,但背后也隐藏着很多问题:多个repos的状态不容易同步,比如没有办法快速cherry-pick,一个repocheckout的一个分支,另一个repo需要再次checkoutgithistory/log是重复的。当项目历史很长时,.git文件夹下的内容是同一个项目占用磁盘空间大,多个repos不好管理。如果没有上述这些问题,我们如何满足这些特殊场景??git-worktree其实这是Git从2015年开始支持的一个功能,但是很少有人知道。git-worktree的使用非常方便。在终端输入:gitworktree--help可以快速查看帮助文档。简单来说,git-worktree的作用就是:只需要维护一个repo,可以同时在多个分支上工作,互不影响。上面红框中的命令有很多,但是我们通常只用到下面这四个:gitworktree添加[-f][--detach][--checkout][--lock][-b][]gitworktreelist[--porcelain]gitworktreeremove[-f]gitworktreeprune[-n][-v][--expire]在展开描述之前,需要科普一下两个你可能忽略的Git知识点:默认情况下,repo由gitinit或gitclone初始化的只有一个工作树,称为主工作树。在某个目录下使用Git命令。当前目录下有一个.git文件夹;或.git文件。如果只有一个.git文件,第二句必须指向.git文件夹里面的内容感觉比较绕。我举个例子,很容易理解gitworktreeadd当前的项目目录结构是这样的(amend-crash-demo是repo的根):.└──amend-crash-demo1directorycdamend-crash-demoruncommandgitworktreeadd../feature/feature2?amend-crash-demogit:(main)gitworktreeadd../feature/feature2Preparingworktree(newbranch'feature2')HEAD现在位于82b8711添加主文件查看目录结构。├──amend-crash-demo└──feature└──feature23directories此命令默认为HEAD所在的commit-ish(当然你也可以在log中指定gitanycommit-ish)创建一个名为feature2,分支盘的位置如上结构所示cd../feature/feature2/你会发现这个分支下没有.git文件夹,但是有一个.git文件,打开文件,内容如下:gitdir:/Users/rgyb/Documents/projects/amend-crash-demo/.git/worktrees/feature2到这里,可以再次理解上面的知识点2,就是清楚多少?接下来,您可以在feature2分支上做任何您想做的事情(添加/提交/拉/推),而不会干扰主工作树。一般来说,项目组都有一定的分支命名规范,比如feature/JIRAID-Title,hotfix/JIRAID-Title,如果你只是按照上面的命令新建一个worktree,那么分支名中的/会被当作一个文件目录.gitworktreeadd../hotfix/hotfix/JIRA234-fix-naming命令运行,文件目录结构是这样的。├──amend-crash-demo├──feature│└──feature2└──hotfix└──hotfix└──JIRA234-fix-naming6directories显然这不是我们想要的,那么我们需要-的支持b参数,执行命令就像gitcheckout-b:gitworktreeadd-b"hotfix/JIRA234-fix-naming"../hotfix/JIRA234-fix-naming看看目录结构├──amend-crash-demo├──feature│└──feature2└──hotfix├──JIRA234-fix-naming└──hotfix└──JIRA234-fix-naming7目录到JIRA234-fix-命名目录,默认情况下是开启的hotfix/JIRA234-fix-naming分支。构建工作树很容易。没有管理,项目目录结构一定很乱。这是我们不想看到的,所以我们需要清楚的知道某个repo建立在哪个worktreegitworktreelist所有的worktree都在共享一个repo,所以在任何在一个worktree目录下,可以执行如下命令查看worktree列表gitworktreelist执行命令后,可以查看到我们上面创建的所有worktree信息,这里也会显示主要的worktree/Users/rgyb/Documents/projects/amend-crash-demo82b8711[main]/Users/rgyb/Documents/projects/chore/chore8782898(分离的HEAD)/Users/rgyb/Documents/projects/feature/feature282b8711[feature2]/Users/rgyb/Documents/projects/hotfix/hotfix/JIRA234-fix-naming82b8711[JIRA234-fix-naming]/Users/rgyb/Documents/projects/hotfix/JIRA234-fix-naming82b8711[hotfix/JIRA234-fix-naming]工作树工作完成,也一定要及时删除,否则会浪费很多磁盘空间。命令gitworktreeremove非常简单。只需删除工作树的名称即可。gitworktreeremovehotfix/hotfix/JIRA234-fix-naming此时,删除分支名错误的hotfix/Users/rgyb/Documents/projects/amend-crash-demo82b8711[main]/Users/rgyb/Documents/projects/chore/chore8782898(分离的HEAD)/Users/rgyb/Documents/projects/feature/feature282b8711[feature2]/Users/rgyb/Documents/projects/hotfix/JIRA234-fix-naming82b8711[hotfix/JIRA234-fix-naming]假设您创建一个工作树并在其中进行更改,突然不再需要这个工作树。现在不能按照上面的命令删除了。这时候就需要-f参数来帮忙了。gitworktreeremove-fhotfix/JIRA234-fix-naming删除worktree,其实在Git文件中,还有很多没有用的管理文件。为了保持干净,我们需要进一步清理。命令gitworktreeprune是一个干净的自下而上的操作,可以让我们的工作保持干净整洁。整个git-worktree的使用过程就是以下四个命令:gitworktreeaddgitworktreelistgitworktreeremovegitworktreeprune你也应该明白gitworktree和gitclonemultiplerepo的区别。只维护一个repo,创建多个worktree,运行流畅。我的做法:我一般使用gitworktree,我会统一目录结构。例如,所有功能工作树都存储在功能目录中,所有修补程序工作树都存储在修补程序目录中。这样,整个磁盘的目录结构就不会因为创建多个工作树而变得混乱。我对磁盘管理有些强迫症。理想情况下,某个repo的worktree最好放在这个repo的文件目录下,但是这样会导致Git跟踪新建的worktree下的所有文件,为了避免Git跟踪worktree的内容,肯定是来回修改gitignore文件不合适。在下一节中,我将介绍一种更好的方法。灵魂探索可以删除主工作树吗?为什么要反复创建和删除worktree,你能看懂repo/.git/wortree目录的变化吗?孙公一兵|原创