git分支使用的坏习惯
最近使用git提交代码发现大家的方式都不一样,自己在使用中也遇到了一些问题,导致代码危险。具体描述一下就是:
1.刚开始创建本地b_28分支与远程b_28对应。在此基础上,我自己改一个bug就基于这个又拉取了本地分支b_bug,一直在这个本地分支上修改,这个b_bug也一直没有关联任何远程代码。
2.后来远程b_featureA分支(原来是和b_28一样的)做了些版本小修改。
3.我不明就里的,本地又checkout了一个b_featureA,还把这些修改merge到了自己的b_bug。
4.管理员让我提交自己bug的修复。我将b_bug merge到本地b_28,然后先拉了origin b_28的更新,还好本地没冲突,然后push到了远端origin b_28。
5.远端分支的话,管理员b_featureA开发完了,要merge到b_28,并以后要在b_28基础上开发
其实这就有问题了,本来是用大家最新提交后的origin b_featureA去merge到origin b_28,但是由于我之前push到上面的代码包含了不必要的对origin b_featureA拉取,导致这个提交不安全。(事实上由于我在第3步之后又将b_bug中一个文件K恢复到了b_28版本,导致这个更新丢失了。这次远端merge的时候,K竟然还是未改的,b_featureA对它的改动并没有merge成功,但也没有提示,还好最后检查了。其实要是我没有恢复过这个K也就没事了)
因此,我犯了一个不必要的错误,要保证本地分支的parent只有一个,不要随便merge别的分支。
然后对于分支的使用原则,问了些人,大家习惯不一样,这几找了几个列子,想分清楚集中使用方法。
大家怎么用
主要关注功能分支:
我觉得可能不同的项目或者不同习惯的人需要不同的分支策略,这些没有必要非要按照某种规则去遵守。
一、阮一峰:Git分支管理策略 重点推荐,简单全面
1.创建一个功能分支:
git checkout -b feature-x develop
2.开发完成后,将功能分支合并到develop分支:
git checkout develop
git merge --no-ff feature-x
3.删除feature分支:
git branch -d feature-x
二、这个跟上面的是一样的方法,还有全面的命令,总结的很好:git使用规范
三、 这个稍微有点不一样:Git 问题, 一个 master, 多个新功能分支, 怎样有序地合并和提交?
git支持很多种工作流程,我们采用的一般是这样,远程创建一个主分支,本地每人创建功能分支,日常工作流程如下:
1.去自己的工作分支
$ git checkout work
2.工作
3.提交工作分支的修改
$ git commit -a
4.回到主分支
$ git checkout master
5.获取远程最新的修改,此时不会产生冲突
$ git pull
6.回到工作分支
$ git checkout work
7.用rebase合并主干的修改,如果有冲突在此时解决
$ git rebase master // 将改变当前工作分支的提交历史,rebase使得master上的更改在work上重演一遍
8.回到主分支
$ git checkout master
9.合并工作分支的修改,此时不会产生冲突。
$ git merge work
10.提交到远程主干
$ git push
这样做的好处是,远程主干上的历史永远是线性的。每个人在本地分支解决冲突,不会在主干上产生冲突。
注意衍合rebase的使用:分支的衍合
简单介绍rebase的:git rebase简介(基本篇)
Attention:一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作。
如果你遵循这条金科玉律,就不会出差错。否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你
四、一个小团队的使用:请推荐一个适合4-6人小团队的git代码管理模式?
整体项目分为master和develop两个分支,master主要用来发布网站使用.develop主要是用来分开使用.
1.平时每个人开发的时候,从develop中clone一下并创建一个开发者自己的分支,如zhang.(再有新加入者的时候,类似同样的方法分支另起一个名字li.).** Q:相当于每个开发者有一个自己的远程分支?按照人来确立分支而不是按照功能分支 **
2.当开发工作完成后,提交本地仓库并git push到自己的分支.
3.最后先将develop合并到自己的分支(开发期间可能被开发者进行过修改),以确保合并成功.
4.合并无误后,再将当前合并后的zhang分支合并到develop分支中.(注:这里的合并操作先是在本地分支合并.然后再合并到远程分支.有点多操作一步).
5.到最后一天工作结束后再将develop合并到master分支,通过master上线运行.
另外对于线上环境有紧急bug要修改的时候.再从master里创建一个分支.独立维护.结束后,再分别同步master和develop两个分支.
** 五、我打算用这个方式了,感觉跟第三个很类似:3.2 Git 分支 - 分支的新建与合并**
让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。 你将经历如下步骤:
1.开发某个网站。
2.为实现某个新的需求,创建一个分支。
3.在这个分支上开展工作。
正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:
1.切换到你的线上分支(production branch)。
2.为这个紧急任务新建一个分支,并在其中修复它。
3.在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
4.切换回你最初工作的分支上,继续工作。
我认为的使用方式,还没有验证过不知道是不是好用:
比如有两个相互独立的任务featureA和featureB,简单点,他们都是从origin/dev拉过来开发。
第一种:
- 分别建立本地分支
$ git branch b_featureA origin/dev //新建本地分支featureA --关联远程分支dev
$ git branch b_featureB origin/dev //新建本地分支featureB --关联远程分支dev
featureA和featureB在各自的分支上工作
最后修改完,先pull远端的代码,可能会有冲突,此时
$ git stash // 先暂存本地修改
$ git pull // 再拉取
$ git stash pop stash@{0} // 还原暂存区的内容,并解决冲突
- 解决冲突后
$ git push origin dev
- 如果本地分支featureA和本地分支featureB没用了,可以删除
这种方法没有一个比较稳定的本地分支。
第二种:
1.新建本地local_dev --关联远程dev
$ git branch local_dev origin/dev
2.基于本地的开发分支local_dev 创建两个功能分支,它们不关联任何远程分支
$ git branch b_featureA
$ git branch b_featureB
3.分别在featureA和featureB在各自的分支上工作
4.featureA开发完毕,切回到local_dev,它pull最新代码,此时不会有冲突。然后两种情况:
方法一:将最新的更改merge到local_dev,有可能冲突,也有rebase的,不熟悉先不用了。
没有冲突工作区也干净后,push到远端。删除本地featureA。
如果有暂时不想提的,可以stash起来,先不删除,然后进行后续开发。
方法二:再复杂一点,切回到featureA,将local_dev的更新merge过来,冲突在此处解决。
都提交后,切回到local_dev,将featureA的更新merge过来,此时不会再有冲突。
local_dev推送到远端。
方法三:我没想到,一个同事说的,切回到featureA,将origin/dev的更新merge过来,就是直接将远端分支的更改merge过来,有冲突就在此处解决。然后都开发完毕后,切回到local_dev,将featureA的更新merge过来,然后将local_dev推送到远端。比方法二少一步merge,然后好像也没什么大的缺点。
不过还没用过直接将远程分支merge到本地这种未对应远程分支的分支上。而且,经常看到这样的忠告:
不要用git pull,用git fetch和git merge代替它。
5.featureB同上
我之前是用的最复杂的第二种的方法二。好处是冲突都是在本地最末端的分支上,和其他功能都不会相互影响,就是操作太麻烦了。
打算采用上面的三,期待有更好的方法。
一些常用命令
基本命令
$ git remote add origin git@github.com:michaelliao/learngit.git // 将本地的git目录和远程建好的git目录关联
$ git add . // 添加所有文件到git版本控制,注意不是git stash
$ git commit -m "commit log" // 提交修改,必须要加注释
$ git checkout -b story remotes/origin/story // 在本地创建story分支并track,可以pull和push
//恢复版本
$ git checkout -- readme.txt // 把readme.txt文件在工作区的修改全部撤销,还未添加到暂存区
$ git reset HEAD readme.txt // 已经add,还没有commit,先用这句unstage,在用上一句
// 如果已经commit
$ git log // 查看提交记录,确定恢复到哪个版本
$ git log -p // 查看详细提交记录
$ git reset --hard HEAD^ // 恢复到上一个版本,因为HEAD指的是当期版本,HEAD是上一个,HEAD^是上上个,其余的可以用版本编号的前几位来恢复
$ git reset --hard 3628164 // 前几位来恢复,只要这个号码能唯一确定一个版本就可以
$ git push -u origin b_28 // 将本地修改的b_28推送到远端b_28,加上了-u参数,Git不但会把本地的分支内容推送的远程新的分支,还会把本地的分支和远程的分支关联起来,在以后的推送或者拉取时就可以简化命令。第一次推送分支的所有内容,以后就可以用git push origin b_28了
$ git branch -d feature // 删除feature分支
$ git branch -a // 加上-a参数可以查看远程分支,远程分支会用红色表示出来
$ git branch -vv // git branch -vv(两个v),就能够看到本地分支跟踪的远程分支。
$ git merge --no-ff develop // 默认情况下,Git执行"快进式合并"(fast-farward merge)加上这个参数就是非快进式合并
$ git push origin :branch_you_want_to_delete // 删除远程的某个分支,注意冒号前面有个空格,慎用
跟踪远程分支
从远程分支 checkout 出来的本地分支,称为 跟踪分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入 git push,Git 会自行推断应该向哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来。
在克隆仓库时,Git 通常会自动创建一个名为 master 的分支来跟踪 origin/master。这正是 git push
和 git pull 一开始就能正常工作的原因。当然,你可以随心所欲地设定为其它跟踪分支,比如 origin
上除了 master 之外的其它分支。刚才我们已经看到了这样的一个例子:git checkout -b [分支名] [远程名]/[分支名]。如果你有 1.6.2 以上版本的 Git,还可以用 --track
选项简化:
$ git checkout --track origin/serverfixBranch //serverfix set up to track remote branch serverfix from origin.Switched to a new branch 'serverfix'
要为本地分支设定不同于远程分支的名字,只需在第一个版本的命令里换个名字:
$ git checkout -b sf origin/serverfixBranch //sf set up to track remote branch serverfix from origin.Switched to a new branch 'sf'
git本地新建一个分支后,如果没有做远程分支关联, git 会在git pull, git push操作中提示你显示的添加关联。你只要没有显示指定,git pull的时候就提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建。如果新建分支的时候没有指定,随后再指定本地dev分支与远程origin/dev分支的链接,可以用下面的命令
$ git branch --set-upstream dev origin/dev
工作区域暂存
$git stash
$ git stash list
$ git stash apply
$ git stash drop stash@{0}
$ git stash pop //来重新应用储藏,同时立刻将其从堆栈中移走。
学习参考资料:看了这些就够了
1.Git常用操作和技巧
2.git - 简明指南
3.Pro Git简体中文版
4.廖雪峰 简明git教程
5.Git Commands and Best Practices Cheat Sheet