版本管理常用相关命令1、使用git在不同版本之间转换要知道版本号git有两个命令可以帮助你查询gitloglog命令会把截至当前版本之前的提交记录展示出来说白了就是它的展示结果只有截至到你当前所在的版本为止commit过的版本记录如你已经回退到了某个版本那之后的版本你是看不到的gitreflogreflog会把当前项目操作过的git记录都展示出来不止都是版本信息还存在很多过程类日志因此在每一次提交时要合理设置版本信息不然找的时候会很头疼2、大多时候一个项目不止有一个master主分支还有其他分支在不同分支之间转换需要借助下面的两个命令gitbranch#查看所有分支gitbranch 分支#新增分支不切换gitcheckout 分支#切换到某个分支gitcheckout-b分支#新增的同时切换到某个分支如果你希望分支来自远程仓库则在命令后跟远程分支的引用gitcheckout-b本地分支名远程仓库名/远程分支名#如果你有多个远程仓库最好是先更新远程信息不然从不同的远程仓库拉取代码可能会报错不是一个有效的版本信息gitfetch--all--prune也有人会用如下方式先把分支的引用拉下来再创建分支但是有点多此一举没必要gitfetch远程仓库名gitcheckout-b本地分支名远程仓库名/远程分支名后续如果一个分支不需要了删除使用-d 或者 -D(强制删除)即可至于远程分支push更改即可#当前代码推送新分支gitpush-uorigin new-branch-name#删除本地分支gitbranch-d分支名称#删除远程分支gitpush origin--delete分支名称 注意上面这种删除本地分支操作本质上并没有真正的物理意义上删除它只是在git的数据目录中悬空不可见了通过git reflog找到删除记录的上一条并使用git branch 新的分支名称 版本号 的方式恢复过来当你需要真的删除时确保当前用标签或者分支的形式保存了版本代码后执行如下命令gitgc--prunenow--aggressive而如果你是远程仓库的分支被删了那就有点麻烦如果你或者同事之间都没有本地的分支快照那只能去联系远程仓库服务提供商问问了这也是注重代码安全的公司普遍不直接用github的原因因为它没这个功能3、最常见的一个问题就是需要回退某个版本的代码改bug或者是归档一些问题首先你要用log或者reflog找到你需要的版本记录它的版本号。推荐用loglog只有各提交版本信息好找这里是需要给git不熟练的朋友说一下reflog展示的信息版本列出后你就要开始找每一条记录开头的7位码值是这个版本的版本号缩写它展开后就是log命令看到的40位左右版本号版本号后的括号中表示该记录生成时版本指针的所处位置HEAD是git中的一个指针用来代表对于版本生成时你所处的版本快照一般情况下多是HEAD-分支通俗理解为当下版本信息并入某个分支当你Push生成远程版本的时候HEAD就会变成具体提交人和所在分支了HEAD{数字}是版本顺序从当前版本开始自0计数顺序的后面跟的是该版本记录生成的原因和系统生成的描述系统描述的后面跟的是操作git提交时人为赋予的描述4、当你找到了你需要回退的版本号运行如下命令gitreset--hard版本号回退版本后就可以正常的修改代码了当你想要再次提交的时候且开发工具是idea偶尔项目中Git选项没有了需要在settings中找到如下配置选中灰色内容点击加号如果在后续推远程的时候提示远程库中的版本比当前版本新让你先pull这个时候你在push命令中添加-f强推就行5、过多的提交操作会生成很多记录不利于项目的版本管理因此要时常考虑对不必要存在的版本记录做删除在工作中一般只有发版的时候才会在主分支上生成一个版本其他时候都是团队成员在各自的分支做代码开发以及相互之间产生合并等而且通常主分支的维护权限在架构师或者一个专属负责人员的手中每次发版会有很多注意事项所以自然而然的从重要程度上讲主分支不太会有需要裁剪提交记录的可能每个版本都很重要注意git删除提交记录是一种软删除并不是物理上直接删了可以理解为为了log结果方便查看和维护而做的一种隐藏设置但就算常操作的分支是自己的也有很多影响因素最终导致能不删就不删下面会提到例如当前某项目的git log提交记录如下我们的目的是删除测试提交时生成的记录此时需要拿着该记录的上一条记录id运行如下命令gitrebase-idd491e6f5f3879ce24374b3d97cf89e15936d5f8此时会进入vim编辑器展示的内容是命令中对应的记录之后的所有提交记录不含命令中的记录使用i进入编辑模式将要删除的记录前pick改为drop随后ShiftZZ或者:wq保存并退出再次log时可以发现对应的记录已删除。注意其他内容不要变否则会导致很麻烦的情况发生而且只有当前要删除的版本开始后面所有东西都没用了或者后续版本数量不多或者后续版本提交内容不多再操作因为当一个中间版本改为drop后对git来说后续版本就变基了会让你一个个的解决冲突。而且删除的版本如果以及被别人的分支合并过则不能删除因为会导致其他分支树出现异常删除操作生效后可以通过reflog看到被删除的记录并在需要的时候回退6、git的核心目的就是版本控制在这个过程中难免遇到分支合并问题所以git提供了两个命令merge和rebase这两个命令有些区别。注意在工作中涉及不同的分支操作时一定不要直接动master上的代码一是因为master是生产环境用的无论从哪方面说都应该保持稳定且保持在正式版本的状态不能也不应该被随意修改二是不同分支操作时如果你当前分支被修改在没有提交版本时是不允许切换分支的会报如下错误让你先提交------------- MINGW64 ~/Desktop/test(master)$gitcheckout fea error: Yourlocalchanges to the following files would be overwritten by checkout: t.txt Please commit your changes or stash them before you switch branches. Aborting所以不要直接改master代码你要想改的话应该是创建一个master代码的新分支。相对应得情况常见于在开发中master上的代码在服务器上运行发现了bug需要更改时就需要对你当前开发中的分支先提交保障你的工作进度不丢失之后回到master生成一个基于master的新分支修改完bug后和master分支合并最后回到你先前开发的分支同步master。当然如果你不担心工作进度和主分支代码冲突可以直接合并到自己的分支言归正传首先rebase命令常用格式如下gitrebase[-i|--interactive][options][--execcmd][--ontonewbase][upstream[branch]]gitrebase[-i|--interactive][options][--execcmd][--ontonewbase]--root[branch]gitrebase--continue|--skip|--abort|--quit|--edit-todo --------------------常用------------------------#将master分支的版本合并到fea分支gitrebase master fea#上面的命令也可以如下操作gitcheckout feagitrebase mastermerge命令格式如下gitmerge[-n][--stat][--no-commit][--squash][--[no-]edit][-sstrategy][-Xstrategy-option][-S[keyid]][--[no-]allow-unrelated-histories][--[no-]rerere-autoupdate][-mmsg][commit…​]gitmerge--abortgitmerge--continue---------------------常用-------------------------#合并分支fixes到enhancementsgitmerge fixes enhancements#合并obsolete分支到当前分支使用ours合并策略gitmerge-sours obsolete#将分支maint合并到当前分支中但不要自动进行新的提交gitmerge --no-commit maint#将分支dev合并到当前分支中自动进行新的提交gitmerge dev#merge 最最最最最最常用的参数其实是压缩提交用在当你要合并来的分支是一个专门修某个bug后不想要中间的那些提交记录gitmerge--squash被合并分支#手动解决可能发生的冲突后提交gitcommit-m修复bug问题描述两条命令在使用上的区别在于合并分支Git会找两个分支共有的一个提交点做为父提交点当你以A分支为基点被合并的B分支仍然处于父提交点时此时无论你使用merge还是rebase合并操作都会中断并提示Current branch XX is up to date.这是因为B分支的所有提交没有超出A分支的范围不用触发合并反之如果B分支超出了父提交点那么情况就很有趣了meger会将B分支父提交点开始截至到B分支版本指针指向的当前版本中间的这些版本以分叉树的形式从A分支上的父提交点开始拼接并识别B分支最新版本和A分支最新版本是否有代码冲突如有则提示你Automatic merge failed; fix conflicts and then commit the result.并在你解决冲突后需要你手动提交从而最后生成一个新版本如果巧合A、B分支两个分支都超出了父节点但是代码没冲突也就是说修改的文件不同则直接自动生成新版本这也就是meger只合并最新版本这一说法的由来# 初始状态A --- B --- C(main)\D --- E(feature)# 在 main 分支执行: git merge featureA --- B --- C --- F(main)\/ D ----------- E(feature)# 如果main分支上没有新提交不会有新版本生成但是代码版本会在 E 上A(main)\D ----------- E(feature)# 在feature执行 git merge无冲突时A(父提交点)|B -------------- C(main)|\D -- E(feature最新版本)-- F(feature生成一个新版本)rebase合并同样的也是把被合并分支父提交点到被合并分支版本指针指向的当前版本中间的这些版本拿过来但不同的是它不是merge那样的分叉树只关联了树关系rebase本身是变基合并所有合并过来的版本都会从物理上直接插入到当前版本树的父提交后面而A分支上原本超出父提交点的提交记录会拼接到合并过来的分支版本中最新提交的后面同时git会从A分支的父提交点开始重新进行一次版本代码比较这会导致父提交点后所有提交都会生成新的版本系列号这就是变基如果这个过程中当A分支原本的那些提交记录在变基过程中发生冲突处理起来就比较麻烦不同于merge只处理最后一个版本就算后续有冲突也只是一次冲突解决后手动提交。rebase时git会在冲突的版本上暂停下来并告诉你那个文件版本冲突了当你解决完需要执行git add file来标记这个文件版本冲突解决完成当这个版本出现的冲突都解决完成后执行git rebase --continuegit会将你的处理应用生效在冲突版本上并继续往后执行分支合并如果后面的版本又出现冲突你就要在来一边手动处理如果当前版本的冲突不需要处理则执行git rebase --skip就这样直到所有分支版本合并完成# 初始状态A --- B --- C(main)\D --- E(feature)# 在 feature 分支执行: git rebase mainA --- B --- C(main正常存在)\B --- C(main拼接来的版本)---- D --- E(feature)冲突时目标文件会有如下内容HEAD 上面这部分是发送冲突的原因版本中的代码下面是当前版本中的东西4ca566f(当前版本的版本号)所以工作中用官方话术来说合并分支优先使用rebase在合并的时候处理版本冲突有助于历史代码的维护。而不用官方话术用工作老油条话术来说就是慎用rebase多数情况下merge也是一种可观的选择除非你明确知道版本冲突到那个程度并且和写代码的人沟通好了留那个部分出了问题能把不属于自己的锅甩出去不然你随便变动了合并来的代码那出现了什么Bug谁提交的谁写的新的版本记录在处理冲突的时候又被改了版本号也变了这个时候问题谁说的清楚至于维护历史代码上没有太关键的影响毕竟你见过那个架构某个版本出现bug了是在问题版本上改动的不都是下一个版本修复说白了不会又人去头铁的动历史版本本身的甚至是重塑这个版本的代码。尤其要是自己写的代码你写的什么东西即使忘了细节但大致的目的肯定能想起来而别人写的代码都成屎山了怎么维护代码就像程序员的内裤冷暖自己知道就可以了。而且要想一个问题怎么这么巧代码到你手上了就需要维护历史版本这点懂的都懂。至于历史版本对照rebase合并之后都变基了对照信息也就没有太大帮助。再说那个版本节点关系图这个东西在工作中能盯着它仔细看的就两种人一种是不懂技术或者稍微懂一点技术的小领导第二种就是摸鱼的毕竟手上有急活谁家好人有那么多时间照着那么复杂的合并关系网去找版本节点早就通过log去找需要的版本了或者直接下个版本修复bug了即使有问题meger也保留了完整的历史版本消息谁的问题谁去解决话再说回来为什么官方话术不建议使用merge是因为merge并没有物理合并因此当你想要在合并过来的那些提交上再次做出更改时你提交的代码版本本质上并不是一个完整的以某个合并来的版本为上一个版本的正常版本树分支而是形成一个孤岛版本这个版本将没有物理意义上的上游即使你提交了在你切换其他分支时他也会丢失。因此在meger后任然需要在当前分支上沿着合并来的分支做修改正常的操作是切换到这些版本自身的真实分支上后用checkout生成一个新的分支随后如有需要再进行合并但这样操作起来就很麻烦所以才有了不建议直接使用merge的说法至于rebase版本冲突的解决细节上这里用删除一个提交时发生的版本变基为例合并分支时在冲突解决的操作上是一样的客户端会给一个类似这样的提示你要做的就是打开你的代码按照上图开头的文件信息去查那些冲突了将冲突解决后执行git rebase --continue每一个中间版本都要这样做直到没有版本冲突为止如果中途你发现不需要删除了可以使用git rebase --abort取消这次rebase操作如果你发现之前的一个continu改错了除了取消充头再来一遍外你只能接着走下去等本次版本删除完成生成最终版本后在做一次常规的改代码提交如果当前版本的冲突对你来说不需要维护则执行git rebase --skip跳过这个版本的冲突如果你用的是idea它会给你一个可观界面左面是冲突文件列表右面三个按钮分别是直接使用你的分支代码、直接使用合并来的分支代码、merge是打开代码看详情并手动更改如果有明确的分支就用上面的两个反之点开meger默认是左边是发生冲突时的当前状态右面是出现冲突的基准版本中间是合并后的结果最上面的魔棒工具是个快捷自动处理如果冲突的代码很少你可以用一用如果多了就手动改吧不然都乱了 或者 意思是保留对应版本的内容按照需要保留一方即可x意思是舍弃对应版本的内容处理完apply就可以了如果你是因为删除版本进行的代码合并apply之后idea左上角会有个continue rebase别忘了点因为删除版本的命令主体是rebase如果你手动操作命令行或者其他原因idea没展示差别文件列表你可以在idea中找到对应的文件通常会标红文件详情的右上角会有一个展开对比的按钮选择在删除提交的这个操作最后要注意虽然命令主体是rebase但最后的生效版本任然会在最后按照meger逻辑走一个合并提交有个常见的问题就是合并完成代码后idea对项目目录展示不全了这是因为idea对项目的结构配置丢失了在 File-Project Structure-Modules 界面里点击 号 选择import module重新加载一下项目就可以了还要留意的是如果你删除的这个提交以及推送到远程仓库上了在删除后首次提交新版本时需要携带--force/-f参数强制推送从而覆写远程分支的版本树数据删除除了使用rebase -i之外你还可以使用git revert 版本号 | git revert 起始版本号..结束版本号 | git revert 版本号1 版本号2实现撤销提交和rebase的区别是revert它将保留要删除的提交树节点好处是如果你要删除的这个版本以及被其他人的分支使用了不会影响那些分支而rebase对于这种已经被使用的情况直接删除会造成其他分支丢失树关系而发生异常在使用流程上和rebase用的参数一样结果上除了不会删掉对于版本树节点的可见性外也是生成一个新的版本提交但是revert 有个限制它在删除非最新提交上很可能无效什么都不发生因为从结果上它不改变版本关系树只是变相的给所有人提供了一个反向提交的回退提示因此不如你直接在最新版本上改完代码正常提交来的方便所以用的不多对于删除历史版本提交最后要说的一点是就像前面提到的那样一旦操作删除就意味着变基、冲突、是否影响其他分支等等一系列麻烦事情所以一般情况下是不做这种删除操作的除非操作的分支你完全可控删除不会有任何影响因此一般都是历史有bug就有bug吧反正也是团队内部的事情所有人不用对于版本就好了有影响直接下一个版本迭代调就好了7、推送代码用push拉取远程的更新用pull#常用推送将当前分支代码推送到远程主分支gitpush-u远程仓库名#推送当前分支到远程的某个分支不存在会创建gitpush-u远程仓库名远程分支#同理如上gitpull远程仓库名远程分支拉取分支上要知道一点你只能拉取看得见的分支也就是远程仓库页面上展示的反过来将只有你推送了你的代码其他人才能看到并拉取使用否则只有你自己本地能够看到并操作无论是推送还是拉取最常见的就是冲突会提示你当前远程仓库有更新的提交记录这个时候就要先拉随后关注代码是否因为远程仓库内容和本地的对比基础版本不一致而需要的手动整理合并冲突的地方git会将内容整理成如下格式HEAD 发生冲突的代码本地代码代码的版本号第一种合并思路命令合并将当前分支合并到master注意rebase是变基合并也就是当前分支和目标分支相比较出一个共同基点随后目标分支基点后的所有版本都会合并到当前分支当前版本后面且以当前分支当前版本为对比基点就好比代码从当前版本开始提交的一样。而merge是直接合并也就是舍弃目标分支之前的版本只把目标分支的最新版本合并到当前分支---------- MINGW64 ~/Desktop/test(tmp)$gitrebase master Current branch tmp is up to date. ---------- MINGW64 ~/Desktop/test(tmp)$gitmerge master Already up to date.注意我上面案例输出内容是因为目标分支当前版本就是当前分支的一部分所以不会继续执行第二种合并思路Pull请求这种合并方法用在某个开源项目不是你自己的你只是这个项目的使用者或者你是这个项目的代码贡献者之一但是你发现了项目中的一个没有修复的bug你修复了这种情况下你需要发出Pull请求这里说的Pull不是往下拉用我自己的项目给大家做个例子。首先需要先把项目的主分支代码git clone下来随后立马生成一个分支因为这种场景下git不允许直接提交master主分支你只能先提交到分支上比如我生成了一个tmp_1109注意如果你已经是这个项目的贡献者就是说你曾经提交过那么就已经有了属于你的分支你就不需要新建了而是在你的分支中pull或者合并一下主分支--------- /d/developtool/IntelliJ IDEA2021.2.2/iedaobj/worldmatter(master)$gitcheckout-btmp_1109 Switched to a new branchtmp_1109此时你就可以在这个分支上修改代码了如果出现意外最多删掉再和主分支上生成一遍。当你修改完bug你需要commit一下例如下面我提交之后新的版本提示我删除了Test.java------------/d/developtool/IntelliJ IDEA2021.2.2/iedaobj/worldmatter(tmp_1109)$gitcommit-m远程分支合并测试[tmp_1109 3a5b629]远程分支合并测试1filechanged,18deletions(-)delete mode100644src/main/java/com/wy/worldmatter/Test.java此时你要把这个分支推送到这个项目的git远程库的分支上------------- /d/developtool/IntelliJ IDEA2021.2.2/iedaobj/worldmatter(tmp_1109)$gitpush origin tmp_1109:tmp_1109 Enumerating objects:109, done. Counting objects:100%(109/109), done. Delta compression using up to20threads Compressing objects:100%(95/95), done. Writing objects:100%(109/109),5.52MiB|1.24MiB/s, done. Total109(delta24), reused0(delta0)remote: Resolving deltas:100%(24/24), done. remote: Powered by GITEE.COM[GNK-6.4]remote: Create a pull requestfortmp_1109on Gitee by visiting: remote: https://gitee.com/wangyang159/worldmatter/pull/new/wangyang159:tmp_1109...wangyang159:master To gitee.com:wangyang159/worldmatter.git *[new branch]tmp_1109 -tmp_1109推送上去之后你就可以在看到你的分支并且进入你的分支在你的分支下找到Pull Request的面板在gitee或者github上可以直接看到点击新建在新建页面确定源分支就是你上传的分支目标分支是主分支并且填写你提交代码的目的和详细说明填好后点击下面的创建---------------分割线到此就没你什么事了。项目的主作者会收到邮件并审核你的代码如果没问题就会同意合并第三种合并思路改bug这种合并适用于你在工作中正常开发的时候你的boss突然告诉你目前生产环境用的主分支有个bug你要改而这个时候你用的代码通常你本地的代码已经是高于生产用的了。说句题外话我在开头说合并命令的时候也提到过不要上来直接就改master主分支的代码要养成一个好习惯建一个分支改没问题了再和主分支合并。所以第三种思路主要就是解决日常开发与维护的。命令上面都有所以这里直接说思路针对这种情况你应该提交你当前分支的代码保证代码不丢失的同时也是git强制的要不然不让你切换。提交后你回到主分支再生一个分支用来改bug改完后和master主分支合并。最后回到你之前开发的分支用rebase同步master主分支的改动然后继续你的日常开发就可以了。不仅是主分支如果其他的任意分支需要改动保险起见均应副本一个新的分支再改代码因为工作中你要改动的分支不一定只有你用