Git具体的实用
多人协作工作模式
Git工作流
所谓工作流就是团队的怎么打配合,配合的方式可以有千万种,我不敢说哪种配合方式是最好的,我下面主要聊聊我所使用的,仅供参考
一般项目都会分成两个主要分支(至少我现在看过的项目都是),一个是master分支(主分支),一个是dev分支(开发分支),master分支应该是最稳定的,而dev则是日常开发更新变动最频繁的,其实对于开发者来说,只用管dev就好了,master分支一般都是项目管理者觉得是时候而且足够稳定,才把dev合并到master发布。
还有另外的一些分支,比如你在开发的时候需要开发一个新功能可以另外从dev中checkout一条新分支来开发新的功能,又比如上线到master代码中被发现了bug,也可以从master分支checkout一条bug fix分支来及时修复bug。这些都可以灵活搭配的 ,只要清楚现在主流都是master-dev这两条主要分支就好了
gitlab help中建议的工作流程是这样:
- 开发成员拷贝管理员建立好的项目到自己本地。
- 创建自己的分支。
- 在自己的分支上写代码,并提交。
- 推送到远程服务器,分支是自己的分支。
- 在Commit页面上浏览分支。
- 创建一个合并请求。
- 团队的管理员或者领导者审查并且决定是否合并员工提交的分支到主分支上。
Git 的开发者都喜欢以这种方式来开展工作,在master 分支中保留完全稳定的代码,即已经发布或即将发布的代码。 与此同时,他们还有一个名为develop 专门用于后续的开发,或仅用于稳定性测试。当然并不是说一定要绝对稳定,不过一旦进入某种稳定状态, 便可以把它合并到master 里。还有在工作中,把开发任务分解为各个功能或者模块, 用topic(topic branch主题分支,有又成为feature branch特性分支),实现之后并测试稳定之后,可以合并到其他分支。
具体情景举例(以下的模拟场景没有把develop分支考虑进去,直接在master分支上分解功能分支。):
管理员唐僧建好了一个项目,把孙悟空加入,并授予Developer角色权限,唐僧本身就是比孙悟空高一级的Master角色。唐僧在自己的电脑上设定好了master分支为受保护分支。
-
员工孙悟空在自己电脑上工作,并推送已经写好的代码到远程服务器的孙悟空分支上,即swkFeature1上 。
孙悟空做了如下操作git clone git@mygitlabold.sytes.net:root/testc01.git //从管理员唐僧那里克隆项目 cd testc01 //进入项目工作 git checkout -b swkFeature1 //创建自己的分支,并切换到此分支上,分支的命名规则为开发人员名字+所开发的功能名字。本例中命名为swkfeature1 vim aa.html //写了一个名为aa的html文件 git add aa.html //加入跟踪 git commit -am "swkFeature1 is ready" //提交 git push origin swkFeature1 //推送到远程服务器孙悟空分支上,git提示推送成功 git push origin swkFeature1:master //孙悟空想尝试直接推送到受保护的分支上,孙悟空是没有这个权限的,git会提示为推送失败
孙悟空在Commit页面上浏览分支swkFeature1,然后向项目领导唐僧创建一个合并请求。
-
管理员唐僧对要求合并的分支代码进行审核。管理员唐僧从服务器上获取孙悟空提交的分支。
git fetch
然后孙悟空推送的远程新分支被管理员唐僧拉拽到本地,但是不是以本地分支存在, 而是保存在.git/refs/heads
之外(.git/refs/remotes/origin/
之中)的远程分支。 管理员唐僧可以如下方法对孙悟空的分支进行审核。 执行
git log master..origin/swkFeature1 //可以看到孙悟空推送了几个提交。
管理员唐僧可以合并孙悟空的分支:
git merge origin/swkFeature1
管理员唐僧可以逐一对孙悟空的提交审核。如果发现有问题,管理员唐僧可以本地回滚。
git reset --hard HEAD^ # 或 master@{1}
几个反复后,当孙悟空的分支审核无误,管理员唐僧将合并后的本地 master 分支推送到远程服务器:
git push # 缺省 remote 为 origin
-
孙悟空那边。孙悟空看到请求已经接受。执行
git fetch
会获取到更新的 master 分支。然后
git checkout master
git merge --ff origin/master
swkFeature1分支完成使命,删掉吧:
git branch -d swkFeature1
参考自:
git命令大合集
git init repo #初始化一个叫repo的本地git仓库
实际上就是创建一个repo的目录,然后目录下放一个.git,.git包含了git的所有记录,判断一个目录是否为git仓库,就看有没有.git目录,有.git目录往下都属于同一个git仓库。如果你把.git这个目录删除了,你的代码虽然还在,但是你的历史变更记录就全部没有了,所以一般别动这个.git
git add file #往暂存区添加一个叫file的文件
git diff #对比修改内容和git中最新记录的commit的区别,最好在add之前用git diff看一看,避免一些像加了空白键那种无效提交
git add . #往暂存区添加在git status中提示需要add的文件(git status不会提示被.gitignore忽略的文件)
git commit -m "blablabla" #把暂存区的内容正式提交到本地的当前分支,其中那个blablabla就是对本次提交的说明
git commit 还有一个-a的选项,是表示连同未add的文件改动也一同加进来,因为你可能add以后又改动了文件,而一般你前面add了内容就只用-m就可以了,不用-am
git status #查看当前所在git仓库情况
git log #查看提交历史,你会看到你自己之前commit时候的说明,各种blablabla
git reflog #查看操作历史
git clone HTTPS/SSH #克隆一个项目
常见的要么就是长成https型的(e.g. https://github.com/tesseract-ocr/tesseract.git),要么就是长成ssh型的(e.g. git@github.com:tesseract-ocr/tesseract.git)
git remote -v #查看远程代码仓库的信息
git remote add origin HTTPS/SSH #添加一个叫origin远程仓库地址,一般克隆自带,如果是本地新创建的话就得执行这句,然后才能push
git remote remove origin #删除一个叫origin远程仓库地址
git push origin <本地分支>:<远程分支> #如果没有冒号后面的,此时默认推送到远程origin仓库的与<本地分支>同名的分支,如果不存在,则会被新建
git pull origin <远程分支>:<本地分支> #如果没有冒号后面的,此时默认拉取并合并远程仓库origin的 <远程分支>到当前分支,pull相当于fetch+merge
git pull --rebase origin dev <远程分支>:<本地分支> #拉取远程git代码仓库origin的dev分支更新,并以重建的方式合并到本地分支。
一般要是嫌麻烦,就像上面那样直接pull就好了,有些洁癖者喜欢用rebase保证本地的线性干净,本地可以rebase,远程就别这么做了
git merge --no-ff dev master #合并本地dev分支到master分支,洁癖者会为了干净历史加上--no-ff选项(--no-ff意思是no fast forward非快进式合并,某公司面试问了--no-ff含义)
git merge --no-ff dev #合并本地dev分支到本地当前分支,洁癖者会为了干净历史加上--no-ff选项
冲突解决:在pull 和merge的时候会因为合并双方文件内容不一样而导致冲突,它会显示给你发生冲突的文件,并自动在冲突文件中标注好不一样的段落。解决冲突其实已经有很多很好的工具了(比如vimdiff之类的),但是我觉得这些工具都稍显得复杂不简约,所以我推荐直接用vim打开发生冲突的脚本,手动修改好,保存。用vim修改(sublime也不错)清晰方便,再add==>commit==>push
就没问题了(第二次push是以你修改后提交为准,git这时候并不理会远程仓库的文件内容,直接覆盖过去~)
git branch #显示本地所有分支
git branch -D dev #-D是-d --force的shotcut,强制删除dev本地分支
git push origin :dev #删除远程仓库的dev分支,相当于把一个空分支push到远程仓库的dev上,等同于删除该分支。
git checkout -b dev #切换到dev分支,-b是没有dev分支时候才需要加上去的,这种情况下是创建并切换到dev分支的意思
git checkout -- file #撤销工作区中一个叫file的文件的修改
git reset HEAD file #撤销暂存区中一个叫file的文件到工作区中
git reset --hard HEAD^ #回滚当前分支到上一个版本,注意末尾的^,有n个^说明回滚多上个版本,也就是回到多少次commit前。
貌似把上面的file 换成*(wildcard)通配符来代指所有是可以的.我就经常多文件撤销工作区修改的时候git checkout -- * ,纯粹因为懒得一个个输入...
git stash
真是没有遇到问题就找不到来学习,用了那么久的git,现在才知道git stash这东西
使用场景:
- 当前工作区内容已被修改,但是并未完成(往往是写到一半有多).
- 这时Boss来了,说前面的分支上面有一个Bug(容易出现在线上hotfix),需要立即修复。
- 可是我又不想提交目前的修改,因为修改没有完成。
- 但是,不提交的话,又没有办法checkout到前面的分支(checkout会报错)。
- 此时可以用Git Stash就相当于备份工作区了。
- 然后在checkout过去修改,就能够达到保存当前工作区,并及时恢复的作用
简单应用:(最好不要过多缓存,因为pop的时候容易conflict)
git stash 缓存工作区修改
git stash list 当前的git stash栈打印出来
git stash pop 恢复所缓存内容
git stash clear 清除全部所缓存内容
git stash pop 相当于 git stash apply 加 git stash drop
Reference:
- Git Stash用法
- Difference between git stash pop and git stash apply
- Git Stash Pop Considered Harmful
- (Highly Recommended!) Git Stash Tutoria)
p.s.
在我用git的时候发生过一个小错误:
“fatal:refusing to merge unrelated histories”,我用的是git2.9.3的版本,然后想要合并远程仓库,可能本地和远程仓库提交历史区别太大,所以会产生这种错误,幸好网上都有答案,其实是git在2.9版本后添加的一个安全措施,避免无相关历史的两个仓库合并在一起,防止误操作的发生,以避免污染了两个代码仓库–“since git 2.9.* ‘git merge’ used to allow merging into branches that havo no common base by default ,which led to a branch new history of an existing project created and then pulled by an unsuspecting maintainer which allow an unnecessary parrallel history merged into the existing project.Here is an escape hatch(逃生舱)”
解决的办法 : 在merge的时候加上–allow-unrelated-histories就可以了。
扩展推荐:
- 廖雪峰老师的git基础教程
- Git 工作流程
- Linus Torvalds on git [Highly Recommended !!! ]