文档章节

Git代码防丢指南

joymufeng
 joymufeng
发布于 01/23 21:10
字数 2145
阅读 2560
收藏 91

我们在日常使用Git的过程中经常会发生一些意外情况,如果处理不当,则可能会出现代码丢失的假象。本文将针对IDEA&Git日常开发中的一些场景,为你层层拨开迷雾,解析常见的错误及其发生原因,让你从此不再惧怕代码冲突或丢失问题。

为简化问题,本文假设所有团队成员均在同一分支上开发。
文中更新操作是指在IDEA中单击菜单VCS-Update Project...

1. 常见工作流程

通常当你早上到公司打开电脑,首先执行更新操作(单击IDEA菜单VCS-Update Project...),然后开始愉快地编码。编码完成后通常要执行以下几个操作:

  • 更新操作
  • 创建本次提交
  • 推送远程分支

1.1 更新操作

为了保证Git拥有一个简洁的提交历史,在提交之前需要先执行更新操作,即在IDEA中依次单击菜单VCS-Update Project...,或者按下Ctrl+T,弹出如下窗口:

窗口左侧选择更新类型(Update Type):

  • Merge:更新时执行合并操作。等价于执行git fetch && git merge或者git pull --no-rebase
  • Rebase:更新时执行rebase操作。等价于执行git fetch && git rebase或者git pull --rebase
  • Branch Default:在.git/config文件中指定不同分支的更新类型。

窗口右侧选择在更新前工作目录(Working Directory)的清理方式:

  • Using Stash:使用git stash储藏本地修改。
  • Using Shelve:使用IDEA内置的Shelve功能储藏本地修改。

通常选择MergeUsing Stash即可,单击OK后,IDEA执行步骤如下:

  • 第1步:使用git stash储藏本地修改
  • 第2步:执行git fetch && git merge拉取远程分支并合并
  • 第3步:执行git stash pop恢复储藏

有些同学可能更习惯先创建本地提交,然后在执行更新操作,这样会导致Git自动生成一个合并提交,导致提交历史不够简洁。

1.2 创建本次提交

更新完成后,在IDEA中单击菜单VCS-Commit...创建本次提交。

1.3 推送远程分支

然后单击VCS-Git-Push...推送至远程分支。

2. 常见问题分析

在上面的3步执行步骤中,第2步和第3步发生意外的风险最高,最常见的两种意外情况是冲突和文件占用,下面我们分别讨论。

2.1 合并远程分支冲突

如果在执行更新操作之前,你的本地分支已经创建过提交,并且尚未推送至远程分支,则在第2步执行git merge时很可能会发生冲突。

此时关闭上面的冲突窗口,Version Control工具窗口显示内容如下:

窗口右下角原本显示分支名称的位置变成了Merging master,表示本地分支master目前处于正在合并状态。单击左侧红框内Resolve按钮可以再次调出处理冲突窗口。基于IDEA的图形界面手动解决冲突后,IDEA会自动将该文件加入暂存区(加入暂存区即表示冲突解决完成),最后执行一次提交便可以完成冲突处理。

2.2 恢复储藏冲突

在更新操作的第3步执行git stash pop恢复储藏时,储藏内容可能与刚更新的内容发生冲突。

恢复储藏时发生的冲突跟上面的合并冲突稍微有些区别,首先是右下角的分支名称没有Merging字样,另外会在右下角额外弹出一个小窗提示恢复储藏失败,并且告诉你不用担心,所有的修改都在stash列表中,并没有丢失。查看stash列表的方式为单击菜单VCS-Git-UnStash Changes...:

选中列表最上面的条目,然后单击Apply Stash,之前的修改就会重新回到工作目录。
我们继续回到冲突问题,手动解决冲突后执行一次提交就可以了。如果在解决冲突过程中发生了误操作,可以右击Default Changelist-Revert...清空当前工作目录内容,重新执行一次Apply Stash,然后重复解决冲突过程。

2.3 文件占用错误

在执行第2步git merge时,可能会因为文件被占用导致执行失败。例如项目可能引入了一些jar文件,这些jar文件在本地已经被JVM动态加载了,如果有其它人更新了该jar文件并且推送到了远程分支,当你更新时便会遇到上述问题。

对于这种错误的解决方法很简单,首先解除文件的占用状态,例如终止本地JVM进程,然后再次点击VCS-Update

在执行第3步git stash pop时,也会因为文件被占用导致执行失败。例如你更新了某个jar文件,当恢复储藏时可能因为该jar文件被占用导致恢复失败。

对于这种错误,你需要首先解除文件占用状态,然后手动执行unstash操作。

3. 先提交还是先更新?是个问题!

3.1 先提交后更新导致的问题

3.1.1 发生冲突时难以处理

如果先提交,但是在更新时却发生了冲突,这就意味着你刚刚创建的提交其实是有问题的,通常是团队沟通或是分工出了问题,但是不管这么说,别人已经抢先一步push了,你的提交便会被拒之门外。即便是手动解决了冲突,这个提交保留在历史中也会成为隐患,如果有其他人reset回这个提交继续工作,则在合并其它分支内容时发生冲突的概率会大大增加,所以最好处理方式是先撤销这个提交(reset --soft HEAD~),然后更新并解决冲突,最后创建一个新的提交。

3.1.2 错误的处理冲突方式

在发生冲突后,有些同学可能会想到下面的处理方式:

  • 清空当前工作空间
  • 调整冲突部分的代码
  • 然后再次执行更新操作

上面的处理方式很明显是不可行的,因为你调整的代码首选会被IDEA储藏(stash)起来,然后在更新的第2步中仍然会发生冲突,并且发生冲突时,你的修改尚未恢复储藏(unstash),导致看起来你调整的代码不见了,让人摸不着头脑。

3.1.3 Rebase会改写提交历史

如果在IDEA的更新窗口选择更新类型为Rebase,则等价于手动执行git fetch && git rebase或者git pull --rebase命令。这样的好处是不会生成一个自动合并提交,保持简洁的提交历史。但是需要注意的是,Rebase之后,你的本地提交会被改写,虽然提交信息一样,但是commit hash已经改变了,如下图所示:

在执行完如下的Rebase命令后,

$ git checkout dev
$ git rebase master

执行结果为:

请注意,结果中的v4v5提交已经被改写了。

3.2 推荐先更新后提交

如果你事先知道会发生冲突,相信你一定不会选择先提交代码,但是冲突是不可避免的,这就要求我们平时养成良好的开发习惯。与其解决提交后的冲突,不如尽早地解决冲突然后提交,这样不仅可以减少一个无意义的自动合并提交,而且可以在冲突发生时简化处理过程。

3.3 养成良好习惯

为了尽量避免冲突发生,建议养成如下开发习惯:

  • 编码前先更新
  • 提交前先更新
  • 提交前检查是否有编译错误
  • 提交粒度尽可能小,描述尽可能准确
  • 修改了公共文件,尽早通知其他成员更新
  • 最后一条,也是最重要的,团队分工要明确

© 著作权归作者所有

共有 人打赏支持
joymufeng
粉丝 98
博文 73
码字总数 67225
作品 2
杨浦
高级程序员
私信 提问
加载中

评论(7)

No1_Man
No1_Man
老哥 写得不错 转了
joymufeng
joymufeng

引用来自“波波侠”的评论

感觉作者对整个GIT的原理理解得还太浅,SVN思维残存啊。什么叫“先更新,后提交”,你想下,如果本地已经有几个COMMIT了,你怎么去先更新,再提交。如果你说的“提交”是PUSH,那更没必要有这个推荐了,你不更新,根本PUSH不上去,还有REBASE的HASH值,未必会变。再者也没分清MERGE 和 REBASE的区别,REBASE不会生成多余的合并结点,冲突时,解决结果所在的COMMIT节点,也不同。

引用来自“joymufeng”的评论

请先读完再评论,莫断章取义!(至少请先读下文章开头的注释部分,搞清楚文中的更新操作具体指什么?)
为避免误导读者,先指出你评论中的几个问题:
问题1:什么叫“先更新,后提交”
回复:本文阅读对象是IDEA+GIT用户,更新操作含义看清文章开头注释
2. 关于“本地已经有几个COMMIT了,你怎么先去更新,再提交。”
回复:自己在IDEA中尝试下
3. 如果你说的“提交”是PUSH,那更没必要有这个推荐了
回复:提交一定是指Commit,怎么会跟Push扯到一起?
4. 再者也没分清MERGE 和 REBASE的区别
回复:文中多次提到,如果本地有未Push的Commit且远程有未Pull的Commit,更新时一定会自动生成一个合并Commit,请仔细阅读原文。另外文中讲述Rebase是在先提交后更新的背景下,如果远程有未Pull的Commit,Rebase后,本地的Commit一定会被改写(Commit Hash改变)。
joymufeng
joymufeng

引用来自“波波侠”的评论

感觉作者对整个GIT的原理理解得还太浅,SVN思维残存啊。什么叫“先更新,后提交”,你想下,如果本地已经有几个COMMIT了,你怎么去先更新,再提交。如果你说的“提交”是PUSH,那更没必要有这个推荐了,你不更新,根本PUSH不上去,还有REBASE的HASH值,未必会变。再者也没分清MERGE 和 REBASE的区别,REBASE不会生成多余的合并结点,冲突时,解决结果所在的COMMIT节点,也不同。
请先读完再评论,莫断章取义!(至少请先读下文章开头的注释部分,搞清楚文中的更新操作具体指什么?)
波波侠
波波侠
感觉作者对整个GIT的原理理解得还太浅,SVN思维残存啊。什么叫“先更新,后提交”,你想下,如果本地已经有几个COMMIT了,你怎么去先更新,再提交。如果你说的“提交”是PUSH,那更没必要有这个推荐了,你不更新,根本PUSH不上去,还有REBASE的HASH值,未必会变。再者也没分清MERGE 和 REBASE的区别,REBASE不会生成多余的合并结点,冲突时,解决结果所在的COMMIT节点,也不同。
py庄稼汉
py庄稼汉
说的很细,很好
joymufeng
joymufeng

引用来自“红薯”的评论

企业用户可以使用码云企业版的强推限制特性
码云非常棒,企业用户首选👍
红薯
红薯
企业用户可以使用码云企业版的强推限制特性
服务端工程师入门与进阶 Java 版

前言 欢迎加入我们。这是一份针对实习生/毕业生的服务端开发入门与进阶指南。遇到问题及时问你的 mentor 或者直接问我。 建议: 尽量用google查找技术资料。 有问题在stackoverflow找找,大部...

天天顺利
2015/10/14
82
0
java程序员从入门到精通

前言 java路漫漫,这是一份针对java服务端开发入门与进阶指南。 建议: 尽量用 google 查找技术资料。 有问题在 stackoverflow 找找,大部分都已经有人回答。 多看官方的技术文档。 ibm deve...

rock912
2016/07/04
92
4
git merge使用不当引发的代码丢失血案

背景 几年前大批量的团队都在转用,的本地库和分支特性让代码管理的便利性大大增加,也因为本地库和分支的大批量使用导致了代码之间的频繁merge,我们团队以前就有遇到过以后丢代码的情况,表...

临江仙卜算子
2018/07/02
0
0
java程序员必备技能

前端:jquery,jsp,javascript,ajax,el,tablelist,nextpage 后台:线程池,数据库连接池,webservice连接池,链表,算法,httpclient,钩子线程(防止丢数据),jackson(解析json串) http协...

兜里有佩
2016/10/24
0
0
IT 技术数据&翻译 目录整理--Awesome-IT-Books

Awesome-IT-Books 本目录旨在集合各个优秀IT书籍&API翻译&官方Docs翻译。 这个仓库不产出书籍,只是将网上流传着的书籍&书籍翻译集合在一起,供大家检索学习。 在此先感谢那些为我们提供书籍...

charchen
2017/04/25
382
1

没有更多内容

加载失败,请刷新页面

加载更多

精品书籍推荐

JavaScript书籍推荐 1、[JavaScript高级程序设计(第3版)] 2、你不知道的JavaScript(中卷) 3、ES6标准入门(第二版)阮一峰

轻轻的往前走
12分钟前
2
0
JVM(六)为什么新生代有两个Survivor分区?

本文会使用排除法的手段,来讲解新生代的区域划分,从而让读者能够更清晰的理解分代回收器的原理,在开始之前我们先来整体认识一下分代收集器。 分代收集器会把内存空间分为:老生代和新生代...

王磊的博客
17分钟前
5
0
程序员最喜欢的15款文本编辑器推荐

程序员最喜欢的15款文本编辑器推荐 2017年09月18日 17:30:50 kangle_zhu 阅读数:59390 转载地址:http://www.cr173.com/html/50553_1.html 很多时候比如编程查看代码或者打开各种文档下我们...

linjin200
19分钟前
6
0
如何在php后端及时推送消息给客户端

walkor大神,目前需求是这样的: 有一群商家在后台网页处理批量导入产品 -》 服务器接受请求 -》 开始foreach一个一个处理导入请求; 我现在想每成功导入一个就推送到前台显示已经导入成功,...

dragon_tech
38分钟前
13
0
Java利用hanlp完成语句相似度分析的案例详解

分享一篇hanlp分词工具使用的小案例,即利用hanlp分词工具分析两个中文语句的相似度的案例。供大家一起学习参考! 在做考试系统需求时,后台题库系统提供录入题目的功能。在录入题目的时候,...

左手的倒影
44分钟前
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部