包含结目的iluff所有页面包含一些错误,现在以wwWiluffcom无法在显示了

7被浏览10,475分享邀请回答7添加评论分享收藏感谢收起1添加评论分享收藏感谢收起写回答飞沐视觉飞沐视觉www.fimvisual.com关注专栏更多最新文章{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&title&:&WooCommerce 微信支付( JSAPI 公众号支付)&,&author&:&fimvisual&,&content&:&\u003Cp\u003E\u003Cstrong\u003E插件名称\u003C\u002Fstrong\u003E:fim-wechat-payment-for-woo\u003Cbr\u003E\u003Cstrong\u003E插件版本\u003C\u002Fstrong\u003E:1.0\u003Cbr\u003E\u003Cstrong\u003E最近更新\u003C\u002Fstrong\u003E:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E支持游客支付(需要在 WooCommerce-&设置-&结算页面允许游客支付)\u003C\u002Fli\u003E\u003Cli\u003E支持微信支付的 JSAPI 接口\u003C\u002Fli\u003E\u003Cli\u003E据微信支付的支付成功异步通知同步查询订单支付状态,并显示在 WooCommerce 后台的订单详情页面\u003C\u002Fli\u003E\u003Cli\u003E支持管理员在WooCommerce-&订单-&某个订单详情页面的退款操作(退款的订单将会即时通知微信支付退款给订单相应的顾客)\u003C\u002Fli\u003E\u003Cli\u003E支持库存管理(支付完成后,订单为处理中的状态,减少库存)\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Ch4\u003E更新日志:\u003C\u002Fh4\u003E\u003Cp\u003Ev1.0 3Cbr\u003E\n插件完成。\u003C\u002Fp\u003E\u003Ch4\u003E下载:\u003C\u002Fh4\u003E\u003Cp\u003E商业合作插件,暂不提供下载。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ch4\u003E截图示例:\u003C\u002Fh4\u003E\u003Cimg src=\&a8c07dcb9fd.png\& data-rawwidth=\&750\& data-rawheight=\&1334\&\u003E\u003Cimg src=\&1c32d9a355fefd0c619ef7.png\& data-rawwidth=\&750\& data-rawheight=\&1334\&\u003E\u003Cimg src=\&e8ef48bb344a815ae6d82f8f9c9f9a16.png\& data-rawwidth=\&750\& data-rawheight=\&1334\&\u003E\u003Cimg src=\&155dbedfa98a599b229e.png\& data-rawwidth=\&750\& data-rawheight=\&1334\&\u003E\u003Cimg src=\&9b5edaa11bd03a07ce267.png\& data-rawwidth=\&750\& data-rawheight=\&1334\&\u003E\u003Cimg src=\&afe3f7d9d5f48a812e55.png\& data-rawwidth=\&750\& data-rawheight=\&1334\&\u003E&,&updated&:new Date(&T06:34:22.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:0,&likeCount&:3,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T14:34:22+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Ffab43caffa4f_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:0,&likesCount&:3},&&:{&title&:&浅析 Docker 容器的应用场景&,&author&:&fimvisual&,&content&:&过去几年开源界以OpenStack为代表的云计算持续火了好久,这两年突然又冒出一个叫Docker的容器技术,其发展之迅猛远超预料。\u003Cbr\u003E\u003Cbr\u003E网上介绍Docker容器的文章已经很多了,用一句话概括:Docker是一种轻量虚拟化的容器技术,提供类似虚拟机的隔离功能,并使用了一种分层的联合文件系统技术管理镜像,能极大简化环境运维过程。\u003Cbr\u003E\u003Cbr\u003EDocker容器云则是使用Docker技术打造的一站式容器云服务平台,即CaaS(Containers as a Service)——容器即服务;可以将它简单看作为PaaS的升级版,使用Docker容器技术的CaaS平台功能更强大,使用灵活,部署更方便。\u003Cbr\u003E\u003Cbr\u003EDocker容器作为一个全新的概念虽然广受关注,但是对于如何使用Docker,将容器技术真正落地用起来,目前估计很多人还没有非常清晰的思路。写这篇文章主要目的就是根据Docker容器的特点,描述Docker容器到底能用来干什么,达到抛砖引玉的目的。\u003Cbr\u003E\u003Cbr\u003E可以认为容器云是处于IaaS和PaaS之间的位置,容器编排集成度越高越像PaaS,反之越像IaaS,据此这里简单地将Docker容器的使用分两大类:主机和服务。\u003Cbr\u003E\u003Ch3\u003E1. 作为云主机使用\u003C\u002Fh3\u003E相比虚拟机来说,容器使用的是一系列非常轻量级的虚拟化技术,使得其启动、部署、升级跟管理进程一样迅速,用起来灵活又感觉跟虚拟机一样没什么区别,所以有些人直接使用Docker的Ubuntu等镜像创建容器,当作轻量的虚拟机来使用。\u003Cbr\u003E\u003Cbr\u003E特别是现在随着系统、软件越来越多,开发测试环境越来越复杂,仅仅靠多用户共享的这种方式节省资源带来的后果就是环境完全不可控。Docker容器的出现让每个人仅仅通过一个几KB的Dockerfile文件就能构建一个自定义的系统镜像,进而启动一个完整系统容器,让人人都能成为 DevOps。\u003Cbr\u003E\u003Cbr\u003E容器云主机也完全能像普通主机一样随意启动、稳定运行、关机、重启,所以在上面随意搭建博客、小网站等完全不在话下。除了常用的托管服务业务,你完全可以自定义任何用法,包括在上面使用任何云服务提供商的云硬盘、云数据库,部署各种你需要的服务。\u003Cbr\u003E\u003Cbr\u003E目前Docker容器管理服务器在Windows下运行需要借助Toolbox工具,虽然微软在2014年底就计划提供Windows Server容器镜像但目前还没有发布,所以想在Docker里跑Windows系统的容器的人还需要等待,希望到时候微软能裁剪出一种轻巧的Windows基础镜像,毕竟容器本身就是一种更轻量级的系统。\u003Cbr\u003E\u003Ch3\u003E2. 作为服务使用\u003C\u002Fh3\u003E如果你仅仅把Docker容器当作一个轻量的固定虚拟机用,那其实只能算是另类用法,Docker容器最重要价值在于提供一整套平台无关的标准化技术,简化服务的部署、升级、维护,只要把需要运维的各种服务打包成标准的集装箱,就可以在任何能运行Docker的环境下跑起来,达到开箱即用的效果,这个特点才是Docker容器风靡全球的根本原因。\u003Cbr\u003E\u003Cbr\u003E下面重点列举几个使用Docker容器部署服务的场景。\u003Cbr\u003E\u003Ch4\u003E1. Web应用服务\u003C\u002Fh4\u003EWeb应用服务是使用最广泛的一类服务,典型的架构是前端一个Tomcat + Java服务,后端MySQl数据库。\u003Cbr\u003E\u003Cbr\u003E前端的Java Web服务器是最适合使用Docker容器的,先将Java运行环境、web服务器直接打包成一个通用的基础Docker镜像,之后再将自定义应用代码或编译程序包加入到该基础镜像中就能产生一个新的应用镜像,最后通过Docker服务立马就能以容器的形式启动Web应用服务。\u003Cbr\u003E\u003Cbr\u003E因为web应用程序一般是无状态的,随着业务访问量增减,用同样的镜像新建、销毁容器即可轻松实现伸缩(前面还需配上DNS域名或者负载均衡的服务),例如下图所示。\u003Cbr\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cem\u003EDocker容器的Web服务\u003C\u002Fem\u003E\u003Cbr\u003E\u003Ch4\u003E2. 持续集成和持续部署\u003C\u002Fh4\u003E互联网行业倡导敏捷开发,持续集成部署CI\u002FCD便是最典型的开发模式。\u003Cbr\u003E使用Docker容器云平台,就能实现从代码编写完成推送到git\u002Fsvn后,自动触发后端CaaS平台将代码下载、编译并构建成测试Docker镜像,再替换测试环境容器服务,自动在Jenkins或者Hudson中运行单元\u002F集成测试,最后测试通过后,马上就能自动将新版本镜像更新到线上,完成服务升级。整个过程全自动化,一气呵成,最大程度地简化了运维成本,而且保证线上、线下环境完全一致,线上服务版本与git\u002Fsvn发布分支也实现统一。\u003Cbr\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cem\u003EDocker的持续集成持续部署流程\u003C\u002Fem\u003E\u003Cbr\u003E\u003Ch4\u003E3. 微服务架构使用\u003C\u002Fh4\u003E如果说上面两种应用场景还不足以体现出与传统的PaaS平台相比的巨大优势的话,那么对微服务的架构这种复杂又灵活的使用场景的无缝支持绝对具有革命意义。\u003Cbr\u003E\u003Cbr\u003E微服务架构将传统分布式服务继续拆分解耦,形成一些更小服务模块,服务模块之间独立部署升级,这些特性与容器的轻量、高效部署不谋而合。\u003Cbr\u003E\u003Cbr\u003E如下图所示,每个容器里可以使用完全不同环境的镜像服务,容器启动即产生了一个独立的微服务主机节点(独立的网络IP),上层服务与下层服务之间服务发现通过环境变量注入、配置文件挂载等多种方式灵活解决,而且还可以直接将云平台提供的各种云服务与自定义的微服务整合组成一个强大的服务集群。\u003Cbr\u003E\u003Cbr\u003E更重要的是,拥有如此多服务的集群环境迁移、复制也非常轻松,只需选择好各服务对应的Docker服务镜像、配置好相互之间访问地址就能很快搭建出一份完全一样的新集群。\u003Cbr\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cem\u003EDocker容器搭建微服务架构例子\u003C\u002Fem\u003E\u003Cbr\u003E\u003Ch4\u003E4. 其他应用场景\u003C\u002Fh4\u003E跑一次性\u002F定时任务有些情况下用户只是需要执行一次性的任务,例如计算出某个结果即可,如果采用传统的服务模式,服务器需要一直运行,造成的极大的资源浪费。而容器的快速创建、销毁能很灵活满足这种完全按需付费的场景。只要制作好一次性程序运行的Docker镜像,当需要运行的时候实时通过镜像创建出容器来执行任务,程序执行完成容器自动退出释放资源。\u003Cbr\u003E\u003Cbr\u003E当然,容器本身只包含静态的镜像和上层的临时可读写层,运行的数据是不会持久化的,所以任务相关数据可以从云存储、云硬盘或者云端数据库里读写。\u003Cbr\u003E科学计算服务在信息化的今天,除了计算机领域,自然科学等领域也越来越需要借助一些复杂的程序软件来做计算分析,如果还要求他们学习繁杂的服务运维部署就太麻烦了,国外就有科研人员用Docker自行部署IPython Notebook服务器开展数据科学研究的例子。\u003Cbr\u003E游戏和网联网等腾讯曾宣称他们已经将Docker容器用在游戏服务端使用Docker,因为对游戏不了解,具体怎么用的没太关注,有需要的人可以参考下他们的使用思路。\u003Cbr\u003E\u003Cbr\u003E网易蜂巢是网易基于自研IaaS为企业及开发者打造的基于Docker的容器云,提供安全灵活的容器即服务,兼具应用引擎类PaaS服务的便利性和IaaS服务的灵活性,是虚拟主机的进化版本。为满足不同用户需求,如上文所述,目前同时提供容器(主机)和服务两种使用方式,容器可看成是一个独立的主机节点,提供ssh客户端登录实现自由操作;服务则是一组无状态的可随时伸缩的服务组,对外通过网易的负载均衡暴露统一服务入口提供服务。\u003Cbr\u003E\u003Cbr\u003E本文转自\u003Ca href=\&http:\u002F\u002Fmp.weixin.qq.com\u002Fs?src=3&timestamp=&ver=1&signature=plxFcNT4dK2Ge*jf97Yw6o4ec7dOgIQoVraZu4o8J1PpsmTV5ZVag5G6Z7EBZcTw8iqihRk-84kYCcniAdjoWBSjwvo8l28g*X1sqVrrML382UhR3sWKKczlCuUtK1Q02HpoJ1QbBwZRnn-NITW*EDFRWgA73Y6BZsVWn9j*YE4=\& data-editable=\&true\& data-title=\&网易云公众号\&\u003E网易云公众号\u003C\u002Fa\u003E。&,&updated&:new Date(&T08:31:22.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:0,&likeCount&:1,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T16:31:22+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002Faeb758abbd4c6_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:0,&likesCount&:1},&&:{&title&:&Oculus + Node.js + Three.js 打造VR世界&,&author&:&fimvisual&,&content&:&\u003Cblockquote\u003E\u003Cp\u003EOculus Rift 是一款为电子游戏设计的头戴式显示器。这是一款虚拟现实设备。这款设备很可能改变未来人们游戏的方式。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E周五Hackday Showcase的时候,突然有了点小灵感,便将闲置在公司的Oculus DK2借回家了——已经都是灰尘了~~。\u003C\u002Fp\u003E\u003Cp\u003E在尝试一个晚上的开发环境搭建后,我放弃了开发原生应用的想法。一是没有属于自己的电脑(如果Raspberry Pi II不算的话)——没有Windows、没有GNU\u002FLinux,二是公司配的电脑是Mac OS。对于嵌入式开发和游戏开发来说,Mac OS简直是手机中的Windows Phone——坑爹的LLVM、GCC(Mac OS )、OpenGL、OGLPlus、C++11。并且官方对Mac OS和Linux的SDK的支持已经落后了好几个世纪。\u003C\u002Fp\u003E\u003Cp\u003E说到底,还是Web的开发环境到底还是比较容易搭建的。这个repo的最后效果图如下所示:\u003C\u002Fp\u003E\u003Cp\u003E效果:\u003C\u002Fp\u003E\u003Col\u003E\u003Cli\u003EWASD控制前进、后退等等。\u003C\u002Fli\u003E\u003Cli\u003E旋转头部 = 真实的世界。\u003C\u002Fli\u003E\u003Cli\u003E附加效果: 看久了头晕。\u003C\u002Fli\u003E\u003C\u002Fol\u003E\u003Cp\u003E现在,让我们开始构建吧。\u003C\u002Fp\u003E\u003Ch2\u003ENode Oculus Services\u003C\u002Fh2\u003E\u003Cp\u003E这里,我们所要做的事情便是将传感器返回来的四元数(Quaternions)与欧拉角(Euler angles)以API的形式返回到前端。\u003C\u002Fp\u003E\u003Ch3\u003E安装Node NMD\u003C\u002Fh3\u003E\u003Cp\u003ENode.js上有一个Oculus的插件名为node-hmd,hmd即面向头戴式显示器。它就是Oculus SDK的Node接口,虽说年代已经有些久远了,但是似乎是可以用的——官方针对 Mac OS和Linux的SDK也已经很久没有更新了。\u003C\u002Fp\u003E\u003Cp\u003E在GNU\u002FLinux系统下,你需要安装下面的这些东西的\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Efreeglut3-dev\nmesa-common-dev\nlibudev-dev\nlibxext-dev\nlibxinerama-dev\nlibxrandr-dev\nlibxxf86vm-dev\n\u003C\u002Fcode\u003E\u003Cp\u003EMac OS如果安装失败,请使用Clang来,以及GCC的C标准库(PS: 就是 Clang + GCC的混合体,它们之间就是各种复杂的关系。。):\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Eexport CXXFLAGS=-stdlib=libstdc++\n\nexport CC=\u002Fusr\u002Fbin\u002Fclang\nexport CXX=\u002Fusr\u002Fbin\u002Fclang++\n\u003C\u002Fcode\u003E\u003Cp\u003E(PS: 我使用的是Mac OS El Captian + Xcode 7.0. 2)clang版本如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003EApple LLVM version 7.0.2 (clang-700.1.81)\nTarget: x86_64-apple-darwin15.0.0\nThread model: posix\n\u003C\u002Fcode\u003E\u003Cp\u003E反正都是会报错的:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Eld: warning: object file (Release\u002Fobj.target\u002Fhmd\u002Fsrc\u002Fplatform\u002Fmac\u002FLibOVR\u002FSrc\u002FService\u002FService_NetClient.o) was built for newer OSX version (10.7) than being linked (10.5)\nld: warning: object file (Release\u002Fobj.target\u002Fhmd\u002Fsrc\u002Fplatform\u002Fmac\u002FLibOVR\u002FSrc\u002FTracking\u002FTracking_SensorStateReader.o) was built for newer OSX version (10.7) than being linked (10.5)\nld: warning: object file (Release\u002Fobj.target\u002Fhmd\u002Fsrc\u002Fplatform\u002Fmac\u002FLibOVR\u002FSrc\u002FUtil\u002FUtil_ImageWindow.o) was built for newer OSX version (10.7) than being linked (10.5)\nld: warning: object file (Release\u002Fobj.target\u002Fhmd\u002Fsrc\u002Fplatform\u002Fmac\u002FLibOVR\u002FSrc\u002FUtil\u002FUtil_Interface.o) was built for newer OSX version (10.7) than being linked (10.5)\nld: warning: object file (Release\u002Fobj.target\u002Fhmd\u002Fsrc\u002Fplatform\u002Fmac\u002FLibOVR\u002FSrc\u002FUtil\u002FUtil_LatencyTest2Reader.o) was built for newer OSX version (10.7) than being linked (10.5)\nld: warning: object file (Release\u002Fobj.target\u002Fhmd\u002Fsrc\u002Fplatform\u002Fmac\u002FLibOVR\u002FSrc\u002FUtil\u002FUtil_Render_Stereo.o) was built for newer OSX version (10.7) than being linked (10.5)\nnode-hmd@0.2.1 node_modules\u002Fnode-hmd\n\u003C\u002Fcode\u003E\u003Cp\u003E不过,有最后一行就够了。\u003C\u002Fp\u003E\u003Ch3\u003ENode.js Oculus Hello,World\u003C\u002Fh3\u003E\u003Cp\u003E现在,我们就可以写一个Hello,World了,直接来官方的示例~~。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Evar hmd = require('node-hmd');\n\nvar manager = hmd.createManager(\&oculusrift\&);\n\nmanager.getDeviceInfo(function(err, deviceInfo) {\n
if(!err) {\n
console.log(deviceInfo);\n
console.error(\&Unable to retrieve device information.\&);\n
}\n});\n\nmanager.getDeviceOrientation(function(err, deviceOrientation) {\n
if(!err) {\n
console.log(deviceOrientation);\n
console.error(\&Unable to retrieve device orientation.\&);\n
}\n});\u003C\u002Fcode\u003E\u003Cp\u003E运行之前,记得先连上你的Oculus。会有类似于下面的结果:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003E{ CameraFrustumFarZInMeters: 2.5,\n
CameraFrustumHFovInRadians: 1.71,\n
CameraFrustumNearZInMeters: 0.4645,\n
CameraFrustumVFovInRadians: 0.772,\n
DefaultEyeFov:\n
[ { RightTan: 1.2378,\n
LeftTan: 1.1992,\n
DownTan: 1.8037,\n
UpTan: 1.8037 },\n
{ RightTan: 1.1992,\n
LeftTan: 1.2378,\n
DownTan: 1.8037,\n
UpTan: 1.8037 } ],\n
DisplayDeviceName: '',\n
DisplayId: ,\n
DistortionCaps: 66027,\n
EyeRenderOrder: [ 1, 0 ],\n
...\u003C\u002Fcode\u003E\u003Cp\u003E接着,我们就可以实时返回这些数据了。\u003C\u002Fp\u003E\u003Ch3\u003ENode Oculus WebSocket\u003C\u002Fh3\u003E\u003Cp\u003E在网上看到\u003Ca href=\&http:\u002F\u002Flaht.info\u002FWebGL\u002FDK2Demo.html\&\u003Ehttp:\u002F\u002Flaht.info\u002FWebGL\u002FDK2Demo.html\u003C\u002Fa\u003E这个虚拟现实的电影,并且发现了它有一个WebSocket,然而是Java写的,只能拿来当参考代码。\u003C\u002Fp\u003E\u003Cp\u003E现在我们就可以写一个这样的Web Services,用的仍然是Express + Node.js + WS。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Evar hmd = require(\&node-hmd\&),\n
express = require(\&express\&),\n
http = require(\&http\&).createServer(),\n
WebSocketServer = require('ws').Server,\n
path = require('path');\n\n\u002F\u002F Create HMD manager object\nconsole.info(\&Attempting to load node-hmd driver: oculusrift\&);\nvar manager = hmd.createManager(\&oculusrift\&);\nif (typeof(manager) === \&undefined\&) {\n
console.error(\&Unable to load driver: oculusrift\&);\n
process.exit(1);\n}\n\u002F\u002F Instantiate express server\nvar app = express();\napp.set('port', process.env.PORT || 3000);\n\napp.use(express.static(path.join(__dirname + '\u002F', 'public')));\napp.set('views', path.join(__dirname + '\u002Fpublic\u002F', 'views'));\napp.set('view engine', 'jade');\n\napp.get('\u002Fdemo', function (req, res) {\n
'use strict';\n
res.render('demo', {\n
title: 'Home'\n
});\n});\n\n\u002F\u002F Attach socket.io listener to the server\nvar wss = new WebSocketServer({server: http});\nvar id = 1;\n\nwss.on('open', function open() {\n
console.log('connected');\n});\n\n\u002F\u002F On socket connection set up event emitters to automatically push the HMD orientation data\nwss.on(\&connection\&, function (ws) {\n
function emitOrientation() {\n
id = id + 1;\n
var deviceQuat = manager.getDeviceQuatSync();\n
var devicePosition = manager.getDevicePositionSync();\n\n
var data = JSON.stringify({\n
quat: deviceQuat,\n
position: devicePosition\n
ws.send(data, function (error) {\n
\u002F\u002Fit's a bug of websocket, see in https:\u002F\u002Fgithub.com\u002Fwebsockets\u002Fws\u002Fissues\u002F337\n
var orientation = setInterval(emitOrientation, 1000);\n\n
ws.on(\&message\&, function (data) {\n
clearInterval(orientation);\n
orientation = setInterval(emitOrientation, data);\n
ws.on(\&close\&, function () {\n
setTimeout(null, 500);\n
clearInterval(orientation);\n
console.log(\&disconnect\&);\n
});\n});\n\n\u002F\u002F Launch express server\nhttp.on('request', app);\nhttp.listen(3000, function () {\n
console.log(\&Express server listening on port 3000\&);\n});\u003C\u002Fcode\u003E\u003Cp\u003E总之,就是连上的时候不断地发现设备的数据:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Evar data = JSON.stringify({\n
quat: deviceQuat,\n
position: devicePosition\n});\n\nws.send(data, function (error) {\n
\u002F\u002Fit's a bug of websocket, see in https:\u002F\u002Fgithub.com\u002Fwebsockets\u002Fws\u002Fissues\u002F337\n});\u003C\u002Fcode\u003E\u003Cp\u003E上面有一行注释是我之前一直遇到的一个坑,总之需要callback就是了。\u003C\u002Fp\u003E\u003Ch2\u003EThree.js + Oculus Effect + DK2 Control\u003C\u002Fh2\u003E\u003Cp\u003E在最后我们需要如下的画面:\u003C\u002Fp\u003E\u003Cp\u003E当然,如果你已经安装了Web VR这一类的东西,你就不需要这样的效果了。如标题所说,你已经知道要用Oculus Effect,它是一个Three.js的插件。\u003C\u002Fp\u003E\u003Cp\u003E在之前的版本中,Three.js都提供了Oculus的Demo,当然只能用来看。并且交互的接口是HTTP,感觉很难玩~~。\u003C\u002Fp\u003E\u003Ch2\u003EThree.js DK2Controls\u003C\u002Fh2\u003E\u003Cp\u003E这时,我们就需要根据上面传过来的四元数(Quaternions)与欧拉角(Euler angles)来作相应的处理。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003E{\n
\&position\&: {\n
\&x\&: 0.272804,\n
\&y\&: -0.2976265,\n
\&z\&: 0.35455\n
\&quat\&: {\n
\&w\&: 0.80069,\n
\&x\&: -0.41938,\n
\&y\&: -0.77148,\n
\&z\&: -0.506439\n
}\n}\u003C\u002Fcode\u003E\u003Ch3\u003E欧拉角与四元数\u003C\u002Fh3\u003E\u003Cp\u003E(ps: 如果没copy好,麻烦提出正确的说法,原谅我这个挂过高数的人。我只在高中的时候,看到这些资料。)\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E欧拉角是一组用于描述刚体姿态的角度,欧拉提出,刚体在三维欧氏空间中的任意朝向可以由绕三个轴的转动复合生成。通常情况下,三个轴是相互正交的。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E对应的三个角度又分别成为roll(横滚角),pitch(俯仰角)和yaw(偏航角),就是上面的postion里面的三个值。。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Eroll = (rotation about Z);\n\npitch = (rotation about (Roll o Y));\n\nyaw = (rotation about (Pitch o Raw o Z));”\n\u003C\u002Fcode\u003E\u003Cp\u003E-- 引自《Oculus Rift In Action》\u003C\u002Fp\u003E\u003Cp\u003E转换成代码。。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Ethis.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);\n\u003C\u002Fcode\u003E\u003Cblockquote\u003E\u003Cp\u003E四元数是由爱尔兰数学家威廉·卢云·哈密顿在1843年发现的数学概念。\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E从明确地角度而言,四元数是复数的不可交换延伸。如把四元数的集合考虑成多维实数空间的话,四元数就代表着一个四维空间,相对于复数为二维空间。\u003C\u002Fp\u003E\u003Cp\u003E反正就是用于描述三维空间的旋转变换。\u003C\u002Fp\u003E\u003Cp\u003E结合下代码:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Ethis.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);\nthis.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w);\n\nthis.camera.setRotationFromQuaternion(this.headQuat);\nthis.controller.setRotationFromMatrix(this.camera.matrix);\u003C\u002Fcode\u003E\u003Cp\u003E就是,我们需要设置camera和controller的旋转。\u003C\u002Fp\u003E\u003Cp\u003E这使我有足够的理由相信Oculus就是一个手机 + 一个6轴运动处理组件的升级板——因为,我玩过MPU6050这样的传感器,如图。。。\u003C\u002Fp\u003E\u003Ch3\u003EThree.js DK2Controls\u003C\u002Fh3\u003E\u003Cp\u003E虽然下面的代码不是我写的,但是还是简单地说一下。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003E\u002F*\n Copyright 2014 Lars Ivar Hatledal\n Licensed under the Apache License, Version 2.0 (the \&License\&);\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \&AS IS\& BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n *\u002F\n\nTHREE.DK2Controls = function (camera) {\n\n
this.camera =\n
this.sensorD\n
this.lastId = -1;\n\n
this.controller = new THREE.Object3D();\n\n
this.headPos = new THREE.Vector3();\n
this.headQuat = new THREE.Quaternion();\n\n
var that =\n
var ws = new WebSocket(\&ws:\u002F\u002Flocalhost:F\&);\n
ws.onopen = function () {\n
console.log(\&### Connected ####\&);\n
ws.onmessage = function (evt) {\n
var message = evt.\n
that.sensorData = JSON.parse(message);\n
} catch (err) {\n
console.log(message);\n
ws.onclose = function () {\n
console.log(\&### Closed ####\&);\n
this.update = function () {\n\n
var sensorData = this.sensorD\n
if (sensorData) {\n
var id = sensorData.\n
if (id & this.lastId) {\n
this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);\n
this.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w);\n\n
this.camera.setRotationFromQuaternion(this.headQuat);\n
this.controller.setRotationFromMatrix(this.camera.matrix);\n
this.lastId =\n
this.camera.position.addVectors(this.controller.position, this.headPos);\n
if (this.camera.position.y & -10) {\n
this.camera.position.y = -10;\n
if (ws) {\n
if (ws.readyState === 1) {\n
ws.send(\&get\\n\&);\n
};\n};\u003C\u002Fcode\u003E\u003Cp\u003E打开WebSocket的时候,不断地获取最新的传感器状态,然后update。谁在调用update方法?Three.js\u003C\u002Fp\u003E\u003Cp\u003E我们需要在我们的初始化代码里初始化我们的control:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Evar oculusC\n\nfunction init() {\n
oculusControl = new THREE.DK2Controls( camera );\n
...\n}\u003C\u002Fcode\u003E\u003Cp\u003E并且不断地调用update方法。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Efunction animate() {\n
requestAnimationFrame( animate );\n
render();\n
stats.update();\n}\nfunction render() {\n
oculusControl.update( clock.getDelta() );\n
THREE.AnimationHandler.update( clock.getDelta() * 100 );\n\n
camera.useQuaternion =\n
camera.matrixWorldNeedsUpdate =\n\n
effect.render(scene, camera);\n}\u003C\u002Fcode\u003E\u003Cp\u003E最后,添加相应的KeyHandler就好了~~。\u003C\u002Fp\u003E\u003Ch3\u003EThree.js KeyHandler\u003C\u002Fh3\u003E\u003Cp\u003EKeyHandler对于习惯了Web开发的人来说就比较简单了:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Ethis.onKeyDown = function (event) {\n
switch (event.keyCode) {\n
case 87: \u002F\u002FW\n
this.wasd.up =\\n
case 83: \u002F\u002FS\n
this.wasd.down =\\n
case 68: \u002F\u002FD\n
this.wasd.right =\\n
case 65: \u002F\u002FA\n
this.wasd.left =\\n
}\n};\n\nthis.onKeyUp = function (event) {\n
switch (event.keyCode) {\n
case 87: \u002F\u002FW\n
this.wasd.up =\\n
case 83: \u002F\u002FS\n
this.wasd.down =\\n
case 68: \u002F\u002FD\n
this.wasd.right =\\n
case 65: \u002F\u002FA\n
this.wasd.left =\\n
}\n};\u003C\u002Fcode\u003E\u003Cp\u003E然后就是万恶的if语句了:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Eif (this.wasd.up) {\n
this.controller.translateZ(-this.translationSpeed * delta);\n}\n\nif (this.wasd.down) {\n
this.controller.translateZ(this.translationSpeed * delta);\n}\n\nif (this.wasd.right) {\n
this.controller.translateX(this.translationSpeed * delta);\n}\n\nif (this.wasd.left) {\n
this.controller.translateX(-this.translationSpeed * delta);\n}\n\nthis.camera.position.addVectors(this.controller.position, this.headPos);\n\nif (this.camera.position.y & -10) {\n
this.camera.position.y = -10;\n}\u003C\u002Fcode\u003E\u003Cp\u003E快接上你的HMD试试吧~~\u003C\u002Fp\u003E\u003Cp\u003EFrom: \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fphodal\u002Foculus-nodejs-threejs-example\&\u003Ehttps:\u002F\u002Fgithub.com\u002Fphodal\u002Foculus-nodejs-threejs-example\u003C\u002Fa\u003E\u003C\u002Fp\u003E&,&updated&:new Date(&T02:18:36.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:0,&likeCount&:3,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T10:18:36+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:0,&likesCount&:3},&&:{&title&:&Git 工作流指南&,&author&:&fimvisual&,&content&:&\u003Ch1\u003E说明:\u003C\u002Fh1\u003E\u003Cp\u003E个人在学习Git工作流的过程中,从原有的 SVN 模式很难完全理解Git的协作模式,直到有一天我看到了下面的文章,好多遗留在心中的困惑迎刃而解:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E我们以使用SVN的工作流来使用Git有什么不妥?\u003C\u002Fli\u003E\u003Cli\u003EGit方便的branch在哪里,团队多人如何协作?冲突了怎么办?如何进行发布控制?\u003C\u002Fli\u003E\u003Cli\u003E经典的master-发布、develop-主开发、hotfix-不过修复如何避免代码不经过验证上线?\u003C\u002Fli\u003E\u003Cli\u003E如何在GitHub上面与他人一起协作,star-fork-pull request是怎样的流程?\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E我个人很感激这篇文章,所以进行了整理,希望能帮到更多的人。整篇文章由 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\& data-editable=\&true\& data-title=\&xirong\&\u003Exirong\u003C\u002Fa\u003E 整理自 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\& data-editable=\&true\& data-title=\&oldratlee\&\u003Eoldratlee\u003C\u002Fa\u003E 的GitHub,方便统一的学习回顾,在此感谢下面两位的贡献。\u003C\u002Fp\u003E\u003Cp\u003E原文链接:\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Fworkflows\& data-editable=\&true\& data-title=\&Git Workflows and Tutorials\&\u003EGit Workflows and Tutorials\u003C\u002Fa\u003E\u003Cbr\u003E简体中文:由 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\& data-editable=\&true\& data-title=\&oldratlee\&\u003Eoldratlee\u003C\u002Fa\u003E 翻译在 GitHub 上 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Ftranslations\u002Fblob\u002Fmaster\u002Fgit-workflows-and-tutorials\u002FREADME.md\& data-editable=\&true\& data-title=\&Git工作流指南\&\u003EGit工作流指南\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E在第三部分 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E4%B8%89%E4%BC%81%E4%B8%9A%E6%97%A5%E5%B8%B8%E5%BC%80%E5%8F%91%E6%A8%A1%E5%BC%8F%E6%8E%A2%E7%B4%A2\& data-editable=\&true\& data-title=\&企业日常开发模式探索\&\u003E企业日常开发模式探索\u003C\u002Fa\u003E,xirong 结合自己所在公司使用git的版本分支开发过程,进行了总结,欢迎大家提出更好的建议。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E4%B8%80%E8%AF%91%E5%BA%8F\& data-editable=\&true\& data-title=\&一、译序\&\u003E一、译序\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E4%BA%8Cgit%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%8C%87%E5%8D%97\& data-editable=\&true\& data-title=\&二、Git工作流指南\&\u003E二、Git工作流指南\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#21-%E9%9B%86%E4%B8%AD%E5%BC%8F%E5%B7%A5%E4%BD%9C%E6%B5%81\& data-editable=\&true\& data-title=\&2.1 集中式工作流\&\u003E2.1 集中式工作流\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#211-%E5%B7%A5%E4%BD%9C%E6%96%B9%E5%BC%8F\& data-editable=\&true\& data-title=\&2.1.1 工作方式\&\u003E2.1.1 工作方式\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#212-%E5%86%B2%E7%AA%81%E8%A7%A3%E5%86%B3\& data-editable=\&true\& data-title=\&2.1.2 冲突解决\&\u003E2.1.2 冲突解决\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#213-%E7%A4%BA%E4%BE%8B\& data-editable=\&true\& data-title=\&2.1.3 示例\&\u003E2.1.3 示例\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E6%9C%89%E4%BA%BA%E5%85%88%E5%88%9D%E5%A7%8B%E5%8C%96%E5%A5%BD%E4%B8%AD%E5%A4%AE%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&有人先初始化好中央仓库\&\u003E有人先初始化好中央仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E6%89%80%E6%9C%89%E4%BA%BA%E5%85%8B%E9%9A%86%E4%B8%AD%E5%A4%AE%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&所有人克隆中央仓库\&\u003E所有人克隆中央仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E6%98%8E%E5%BC%80%E5%8F%91%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小明开发功能\&\u003E小明开发功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%BC%80%E5%8F%91%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红开发功能\&\u003E小红开发功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E6%98%8E%E5%8F%91%E5%B8%83%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小明发布功能\&\u003E小明发布功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E8%AF%95%E7%9D%80%E5%8F%91%E5%B8%83%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红试着发布功能\&\u003E小红试着发布功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%9C%A8%E5%B0%8F%E6%98%8E%E7%9A%84%E6%8F%90%E4%BA%A4%E4%B9%8B%E4%B8%8Arebase\& data-editable=\&true\& data-title=\&小红在小明的提交之上rebase\&\u003E小红在小明的提交之上rebase\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E8%A7%A3%E5%86%B3%E5%90%88%E5%B9%B6%E5%86%B2%E7%AA%81\& data-editable=\&true\& data-title=\&小红解决合并冲突\&\u003E小红解决合并冲突\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E6%88%90%E5%8A%9F%E5%8F%91%E5%B8%83%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红成功发布功能\&\u003E小红成功发布功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#22-%E5%8A%9F%E8%83%BD%E5%88%86%E6%94%AF%E5%B7%A5%E4%BD%9C%E6%B5%81\& data-editable=\&true\& data-title=\&2.2 功能分支工作流\&\u003E2.2 功能分支工作流\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#221-%E5%B7%A5%E4%BD%9C%E6%96%B9%E5%BC%8F\& data-editable=\&true\& data-title=\&2.2.1 工作方式\&\u003E2.2.1 工作方式\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#222-pull-requests\& data-editable=\&true\& data-title=\&2.2.2 Pull Requests\&\u003E2.2.2 Pull Requests\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#223-%E7%A4%BA%E4%BE%8B\& data-editable=\&true\& data-title=\&2.2.3 示例\&\u003E2.2.3 示例\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%BC%80%E5%A7%8B%E5%BC%80%E5%8F%91%E4%B8%80%E4%B8%AA%E6%96%B0%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红开始开发一个新功能\&\u003E小红开始开发一个新功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E8%A6%81%E5%8E%BB%E5%90%83%E4%B8%AA%E5%8D%88%E9%A5%AD\& data-editable=\&true\& data-title=\&小红要去吃个午饭\&\u003E小红要去吃个午饭\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%AE%8C%E6%88%90%E5%8A%9F%E8%83%BD%E5%BC%80%E5%8F%91\& data-editable=\&true\& data-title=\&小红完成功能开发\&\u003E小红完成功能开发\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E9%BB%91%E6%94%B6%E5%88%B0pull-request\& data-editable=\&true\& data-title=\&小黑收到Pull Request\&\u003E小黑收到Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%86%8D%E5%81%9A%E4%BF%AE%E6%94%B9\& data-editable=\&true\& data-title=\&小红再做修改\&\u003E小红再做修改\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%8F%91%E5%B8%83%E5%A5%B9%E7%9A%84%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红发布她的功能\&\u003E小红发布她的功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E4%B8%8E%E6%AD%A4%E5%90%8C%E6%97%B6%E5%B0%8F%E6%98%8E%E5%9C%A8%E5%81%9A%E5%92%8C%E5%B0%8F%E7%BA%A2%E4%B8%80%E6%A0%B7%E7%9A%84%E4%BA%8B\& data-editable=\&true\& data-title=\&与此同时,小明在做和小红一样的事\&\u003E与此同时,小明在做和小红一样的事\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#23-gitflow%E5%B7%A5%E4%BD%9C%E6%B5%81\& data-editable=\&true\& data-title=\&2.3 Gitflow工作流\&\u003E2.3 Gitflow工作流\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#231-%E5%B7%A5%E4%BD%9C%E6%96%B9%E5%BC%8F\& data-editable=\&true\& data-title=\&2.3.1 工作方式\&\u003E2.3.1 工作方式\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#232-%E5%8E%86%E5%8F%B2%E5%88%86%E6%94%AF\& data-editable=\&true\& data-title=\&2.3.2 历史分支\&\u003E2.3.2 历史分支\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#233-%E5%8A%9F%E8%83%BD%E5%88%86%E6%94%AF\& data-editable=\&true\& data-title=\&2.3.3 功能分支\&\u003E2.3.3 功能分支\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#234-%E5%8F%91%E5%B8%83%E5%88%86%E6%94%AF\& data-editable=\&true\& data-title=\&2.3.4 发布分支\&\u003E2.3.4 发布分支\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#235-%E7%BB%B4%E6%8A%A4%E5%88%86%E6%94%AF\& data-editable=\&true\& data-title=\&2.3.5 维护分支\&\u003E2.3.5 维护分支\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#236-%E7%A4%BA%E4%BE%8B\& data-editable=\&true\& data-title=\&2.3.6 示例\&\u003E2.3.6 示例\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%88%9B%E5%BB%BA%E5%BC%80%E5%8F%91%E5%88%86%E6%94%AF\& data-editable=\&true\& data-title=\&创建开发分支\&\u003E创建开发分支\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%92%8C%E5%B0%8F%E6%98%8E%E5%BC%80%E5%A7%8B%E5%BC%80%E5%8F%91%E6%96%B0%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红和小明开始开发新功能\&\u003E小红和小明开始开发新功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%AE%8C%E6%88%90%E5%8A%9F%E8%83%BD%E5%BC%80%E5%8F%91-1\& data-editable=\&true\& data-title=\&小红完成功能开发\&\u003E小红完成功能开发\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%BC%80%E5%A7%8B%E5%87%86%E5%A4%87%E5%8F%91%E5%B8%83\& data-editable=\&true\& data-title=\&小红开始准备发布\&\u003E小红开始准备发布\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%AE%8C%E6%88%90%E5%8F%91%E5%B8%83\& data-editable=\&true\& data-title=\&小红完成发布\&\u003E小红完成发布\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E6%9C%80%E7%BB%88%E7%94%A8%E6%88%B7%E5%8F%91%E7%8E%B0bug\& data-editable=\&true\& data-title=\&最终用户发现Bug\&\u003E最终用户发现Bug\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#24-forking%E5%B7%A5%E4%BD%9C%E6%B5%81\& data-editable=\&true\& data-title=\&2.4 Forking工作流\&\u003E2.4 Forking工作流\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#241-%E5%B7%A5%E4%BD%9C%E6%96%B9%E5%BC%8F\& data-editable=\&true\& data-title=\&2.4.1 工作方式\&\u003E2.4.1 工作方式\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#242-%E6%AD%A3%E5%BC%8F%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&2.4.2 正式仓库\&\u003E2.4.2 正式仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#243-forking%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%9A%84%E5%88%86%E6%94%AF%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F\& data-editable=\&true\& data-title=\&2.4.3 Forking工作流的分支使用方式\&\u003E2.4.3 Forking工作流的分支使用方式\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#244-%E7%A4%BA%E4%BE%8B\& data-editable=\&true\& data-title=\&2.4.4 示例\&\u003E2.4.4 示例\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E9%A1%B9%E7%9B%AE%E7%BB%B4%E6%8A%A4%E8%80%85%E5%88%9D%E5%A7%8B%E5%8C%96%E6%AD%A3%E5%BC%8F%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&项目维护者初始化正式仓库\&\u003E项目维护者初始化正式仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%BC%80%E5%8F%91%E8%80%85fork%E6%AD%A3%E5%BC%8F%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&开发者fork正式仓库\&\u003E开发者fork正式仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%BC%80%E5%8F%91%E8%80%85%E5%85%8B%E9%9A%86%E8%87%AA%E5%B7%B1fork%E5%87%BA%E6%9D%A5%E7%9A%84%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&开发者克隆自己fork出来的仓库\&\u003E开发者克隆自己fork出来的仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%BC%80%E5%8F%91%E8%80%85%E5%BC%80%E5%8F%91%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&开发者开发自己的功能\&\u003E开发者开发自己的功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%BC%80%E5%8F%91%E8%80%85%E5%8F%91%E5%B8%83%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&开发者发布自己的功能\&\u003E开发者发布自己的功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E9%A1%B9%E7%9B%AE%E7%BB%B4%E6%8A%A4%E8%80%85%E9%9B%86%E6%88%90%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&项目维护者集成开发者的功能\&\u003E项目维护者集成开发者的功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%BC%80%E5%8F%91%E8%80%85%E5%92%8C%E6%AD%A3%E5%BC%8F%E4%BB%93%E5%BA%93%E5%81%9A%E5%90%8C%E6%AD%A5\& data-editable=\&true\& data-title=\&开发者和正式仓库做同步\&\u003E开发者和正式仓库做同步\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#25-pull-requests\& data-editable=\&true\& data-title=\&2.5 Pull Requests\&\u003E2.5 Pull Requests\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#251-%E8%A7%A3%E6%9E%90pull-request\& data-editable=\&true\& data-title=\&2.5.1 解析Pull Request\&\u003E2.5.1 解析Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#252-%E5%B7%A5%E4%BD%9C%E6%96%B9%E5%BC%8F\& data-editable=\&true\& data-title=\&2.5.2 工作方式\&\u003E2.5.2 工作方式\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#253-%E5%9C%A8%E5%8A%9F%E8%83%BD%E5%88%86%E6%94%AF%E5%B7%A5%E4%BD%9C%E6%B5%81%E4%B8%AD%E4%BD%BF%E7%94%A8pull-request\& data-editable=\&true\& data-title=\&2.5.3 在功能分支工作流中使用Pull Request\&\u003E2.5.3 在功能分支工作流中使用Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#254-%E5%9C%A8gitflow%E5%B7%A5%E4%BD%9C%E6%B5%81%E4%B8%AD%E4%BD%BF%E7%94%A8pull-request\& data-editable=\&true\& data-title=\&2.5.4 在Gitflow工作流中使用Pull Request\&\u003E2.5.4 在Gitflow工作流中使用Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#255-%E5%9C%A8forking%E5%B7%A5%E4%BD%9C%E6%B5%81%E4%B8%AD%E4%BD%BF%E7%94%A8pull-request\& data-editable=\&true\& data-title=\&2.5.5 在Forking工作流中使用Pull Request\&\u003E2.5.5 在Forking工作流中使用Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#256-%E7%A4%BA%E4%BE%8B\& data-editable=\&true\& data-title=\&2.5.6 示例\&\u003E2.5.6 示例\u003C\u002Fa\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2fork%E6%AD%A3%E5%BC%8F%E9%A1%B9%E7%9B%AE\& data-editable=\&true\& data-title=\&小红fork正式项目\&\u003E小红fork正式项目\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%85%8B%E9%9A%86%E5%A5%B9%E7%9A%84bitbucket%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&小红克隆她的Bitbucket仓库\&\u003E小红克隆她的Bitbucket仓库\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%BC%80%E5%8F%91%E6%96%B0%E5%8A%9F%E8%83%BD\& data-editable=\&true\& data-title=\&小红开发新功能\&\u003E小红开发新功能\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2push%E5%8A%9F%E8%83%BD%E5%88%B0%E5%A5%B9%E7%9A%84bitbucket%E4%BB%93%E5%BA%93%E4%B8%AD\& data-editable=\&true\& data-title=\&小红push功能到她的Bitbucket仓库中\&\u003E小红push功能到她的Bitbucket仓库中\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E5%8F%91%E8%B5%B7pull-request\& data-editable=\&true\& data-title=\&小红发起Pull Request\&\u003E小红发起Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E6%98%8Ereview-pull-request\& data-editable=\&true\& data-title=\&小明review Pull Request\&\u003E小明review Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E7%BA%A2%E8%A1%A5%E5%8A%A0%E6%8F%90%E4%BA%A4\& data-editable=\&true\& data-title=\&小红补加提交\&\u003E小红补加提交\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E5%B0%8F%E6%98%8E%E6%8E%A5%E5%8F%97pull-request\& data-editable=\&true\& data-title=\&小明接受Pull Request\&\u003E小明接受Pull Request\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\u002Fblob\u002Fmaster\u002Fgit-workflow-tutorial.md#%E4%B8%89%E4%BC%81%E4%B8%9A%E6%97%A5%E5%B8%B8%E5%BC%80%E5%8F%91%E6%A8%A1%E5%BC%8F%E6%8E%A2%E7%B4%A2\& data-editable=\&true\& data-title=\&三、企业日常开发模式探索\&\u003E三、企业日常开发模式探索\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Ch1\u003E一、译序\u003C\u002Fh1\u003E\u003Cp\u003E这篇指南以大家在SVN中已经广为熟悉使用的集中式工作流作为起点,循序渐进地演进到其它高效的分布式工作流,还介绍了如何配合使用便利的Pull Request功能,体系地讲解了各种工作流的应用。 如果你Git用的还不多,可以从前面的讲的工作流开始操练。操作过程去感受指南的讲解:解决什么问题、如何解决问题,这样理解就深了,也方便活用。\u003C\u002Fp\u003E\u003Cp\u003E行文中实践原则和操作示例并重,对于Git的资深玩家可以梳理思考提升,而新接触的同学,也可以跟着step-by-step操练学习并在实际工作中上手使用。\u003C\u002Fp\u003E\u003Cp\u003E工作流其实不是一个初级主题,背后的本质问题其实是 有效的项目流程管理 和 高效的开发协同约定,而不仅是Git或SVN等\u003Ca href=\&http:\u002F\u002Fzh.wikipedia.org\u002Fwiki\u002F%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6\& data-editable=\&true\& data-title=\&VCS\&\u003EVCS\u003C\u002Fa\u003E或\u003Ca href=\&http:\u002F\u002Fzh.wikipedia.org\u002Fwiki\u002F%E8%BD%AF%E4%BB%B6%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86\& data-editable=\&true\& data-title=\&SCM\&\u003ESCM\u003C\u002Fa\u003E工具的使用。\u003C\u002Fp\u003E\u003Cp\u003E关于Git工作流主题,网上体系的中文资料不多,主要是零散的操作说明,希望这篇文章能让你更深入理解并在工作中灵活有效地使用起来。\u003C\u002Fp\u003E\u003Cp\u003EGitflow工作流是经典模型,处于核心位置,体现了工作流的经验和精髓。随着项目过程复杂化,你会感受到这个工作流中的深思熟虑和威力!\u003C\u002Fp\u003E\u003Cp\u003EForking工作流是分布式协作的(GitHub风格)可以先看看GitHub的Help:\u003Ca href=\&https:\u002F\u002Fhelp.github.com\u002Farticles\u002Ffork-a-repo\u002F\& data-editable=\&true\& data-title=\&Fork A Repo\&\u003EFork A Repo\u003C\u002Fa\u003E和\u003Ca href=\&https:\u002F\u002Fhelp.github.com\u002Farticles\u002Fusing-pull-requests\u002F\& data-editable=\&true\& data-title=\&Using pull requests\&\u003EUsing pull requests\u003C\u002Fa\u003E 。照着操作,给一个GitHub项目贡献你的提交,有操作经验再看指南容易意会。指南中给了\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Ftranslations\u002Fblob\u002Fmaster\u002Fgit-workflows-and-tutorials\u002Fworkflow-forking.md#%E5%BC%80%E5%8F%91%E8%80%85fork%E6%AD%A3%E5%BC%8F%E4%BB%93%E5%BA%93\& data-editable=\&true\& data-title=\&自己实现Fork的方法\&\u003E自己实现Fork的方法\u003C\u002Fa\u003E:Fork就是服务端的克隆。在指南的操练中使用代码托管服务(如GitHub、Bitbucket),可以点一下按钮就让开发者完成仓库的fork操作。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003EPS\u003C\u002Fem\u003E:\u003C\u002Fp\u003E\u003Cp\u003E文中Pull Request的介绍用的是Bitbucket代码托管服务,由于和GitHub基本一样,如果你用的是GitHub(我自己也主要使用GitHub托管代码),不影响理解和操作。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cem\u003EPPS\u003C\u002Fem\u003E:\u003C\u002Fp\u003E\u003Cp\u003E更多Git学习资料参见\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\u002Fmy-git\& data-editable=\&true\& data-title=\&Git的资料整理\&\u003EGit的资料整理\u003C\u002Fa\u003E by \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Fxirong\& data-editable=\&true\& data-title=\&@xirong\&\u003E@xirong\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E自己整理的分享PPT \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Fsoftware-practice-miscellany\u002Fblob\u002Fmaster\u002Fgit\u002Fgit-gitlab-usage.pptx\& data-editable=\&true\& data-title=\&Git使用与实践\&\u003EGit使用与实践\u003C\u002Fa\u003E @ \u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Fsoftware-practice-miscellany\u002Ftree\u002Fmaster\u002Fgit\& data-editable=\&true\& data-title=\&个人整理一些Git\&\u003E个人整理一些Git\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cbr\u003E\u003Cli\u003E? \u003Ca href=\&http:\u002F\u002Fweibo.com\u002Foldratlee\& data-editable=\&true\& data-title=\&自己\&\u003E自己\u003C\u002Fa\u003E理解粗浅,翻译中不足和不对之处,欢迎 ?\u003Cul\u003E\u003Cli\u003E建议,\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Ftranslations\u002Fissues\u002Fnew\& data-editable=\&true\& data-title=\&提交Issue\&\u003E提交Issue\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003Cli\u003E指正,\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Ftranslations\u002Ffork\& data-editable=\&true\& data-title=\&Fork后提通过Pull Requst贡献修改\&\u003EFork后提通过Pull Requst贡献修改\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003C\u002Fli\u003E\u003Cli\u003E如有文章理解上有疑问 或是 使用过程中碰到些疑惑,请随意?\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Foldratlee\u002Ftranslations\u002Fissues\u002Fnew\& data-editable=\&true\& data-title=\&提交Issue\&\u003E提交Issue\u003C\u002Fa\u003E ,一起交流学习讨论!\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Ch1\u003E二、Git工作流指南\u003C\u002Fh1\u003E\u003Cp\u003E? 工作流有各式各样的用法,但也正因此使得在实际工作中如何上手使用变得很头大。这篇指南通过总览公司团队中最常用的几种Git工作流让大家可以上手使用。\u003C\u002Fp\u003E\u003Cp\u003E在阅读的过程中请记住,本文中的几种工作流是作为方案指导而不是条例规定。在展示了各种工作流可能的用法后,你可以从不同的工作流中挑选或揉合出一个满足你自己需求的工作流。\u003C\u002Fp\u003E\u003Ch2\u003E2.1 集中式工作流\u003C\u002Fh2\u003E\u003Cp\u003E如果你的开发团队成员已经很熟悉Subversion,集中式工作流让你无需去适应一个全新流程就可以体验Git带来的收益。这个工作流也可以作为向更Git风格工作流迁移的友好过渡。 \u003C\u002Fp\u003E\u003Cp\u003E转到分布式版本控制系统看起来像个令人生畏的任务,但不改变已用的工作流你也可以用上Git带来的收益。团队可以用和Subversion完全不变的方式来开发项目。\u003C\u002Fp\u003E\u003Cp\u003E但使用Git加强开发的工作流,Git有相比SVN的几个优势。 首先,每个开发可以有属于自己的整个工程的本地拷贝。隔离的环境让各个开发者的工作和项目的其他部分修改独立开来 —— 即自由地提交到自己的本地仓库,先完全忽略上游的开发,直到方便的时候再把修改反馈上去。\u003C\u002Fp\u003E\u003Cp\u003E其次,Git提供了强壮的分支和合并模型。不像SVN,Git的分支设计成可以做为一种用来在仓库之间集成代码和分享修改的『失败安全』的机制。\u003C\u002Fp\u003E\u003Ch3\u003E2.1.1 工作方式\u003C\u002Fh3\u003E\u003Cp\u003E像Subversion一样,集中式工作流以中央仓库作为项目所有修改的单点实体。相比SVN缺省的开发分支trunk,Git叫做master,所有修改提交到这个分支上。本工作流只用到master这一个分支。\u003C\u002Fp\u003E\u003Cp\u003E开发者开始先克隆中央仓库。在自己的项目拷贝中像SVN一样的编辑文件和提交修改;但修改是存在本地的,和中央仓库是完全隔离的。开发者可以把和上游的同步延后到一个方便时间点。\u003C\u002Fp\u003E\u003Cp\u003E要发布修改到正式项目中,开发者要把本地master分支的修改『推』到中央仓库中。这相当于svn commit操作,但push操作会把所有还不在中央仓库的本地提交都推上去。\u003C\u002Fp\u003E\u003Ch3\u003E2.1.2 冲突解决\u003C\u002Fh3\u003E\u003Cp\u003E中央仓库代表了正式项目,所以提交历史应该被尊重且是稳定不变的。如果开发者本地的提交历史和中央仓库有分歧,Git会拒绝push提交否则会覆盖已经在中央库的正式提交。\u003C\u002Fp\u003E\u003Cp\u003E在开发者提交自己功能修改到中央库前,需要先fetch在中央库的新增提交,rebase自己提交到中央库提交历史之上。 这样做的意思是在说,『我要把自己的修改加到别人已经完成的修改上。』最终的结果是一个完美的线性历史,就像以前的SVN的工作流中一样。\u003C\u002Fp\u003E\u003Cp\u003E如果本地修改和上游提交有冲突,Git会暂停rebase过程,给你手动解决冲突的机会。Git解决合并冲突,用和生成提交一样的\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fgit-basics#!status\& data-editable=\&true\& data-title=\&git status\&\u003Egit status\u003C\u002Fa\u003E和\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fgit-basics#!add\& data-editable=\&true\& data-title=\&git add\&\u003Egit add\u003C\u002Fa\u003E命令,很一致方便。还有一点,如果解决冲突时遇到麻烦,Git可以很简单中止整个rebase操作,重来一次(或者让别人来帮助解决)。\u003C\u002Fp\u003E\u003Ch3\u003E2.1.3 示例\u003C\u002Fh3\u003E\u003Cp\u003E让我们一起逐步分解来看看一个常见的小团队如何用这个工作流来协作的。有两个开发者小明和小红,看他们是如何开发自己的功能并提交到中央仓库上的。\u003C\u002Fp\u003E\u003Ch4\u003E有人先初始化好中央仓库\u003C\u002Fh4\u003E\u003Cp\u003E第一步,有人在服务器上创建好中央仓库。如果是新项目,你可以初始化一个空仓库;否则你要导入已有的Git或SVN仓库。\u003C\u002Fp\u003E\u003Cp\u003E中央仓库应该是个裸仓库(bare repository),即没有工作目录(working directory)的仓库。可以用下面的命令创建:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Essh user@host\ngit init --bare \u002Fpath\u002Fto\u002Frepo.git\u003C\u002Fcode\u003E\u003Cp\u003E确保写上有效的user(SSH的用户名),host(服务器的域名或IP地址),\u002Fpath\u002Fto\u002Frepo.git(你想存放仓库的位置)。 注意,为了表示是一个裸仓库,按照约定加上.git扩展名到仓库名上。\u003C\u002Fp\u003E\u003Ch4\u003E所有人克隆中央仓库\u003C\u002Fh4\u003E\u003Cp\u003E下一步,各个开发者创建整个项目的本地拷贝。通过\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fgit-basics#!clone\& data-editable=\&true\& data-title=\&git clone\&\u003Egit clone\u003C\u002Fa\u003E命令完成:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit clone ssh:\u002F\u002Fuser@host\u002Fpath\u002Fto\u002Frepo.git\u003C\u002Fcode\u003E\u003Cp\u003E基于你后续会持续和克隆的仓库做交互的假设,克隆仓库时Git会自动添加远程别名origin指回『父』仓库。\u003C\u002Fp\u003E\u003Ch4\u003E小明开发功能\u003C\u002Fh4\u003E\u003Cp\u003E在小明的本地仓库中,他使用标准的Git过程开发功能:编辑、暂存(Stage)和提交。 如果你不熟悉暂存区(Staging Area),这里说明一下:暂存区用来准备一个提交,但可以不用把工作目录中所有的修改内容都包含进来。 这样你可以创建一个高度聚焦的提交,尽管你本地修改很多内容。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit status # 查看本地仓库的修改状态\ngit add # 暂存文件\ngit commit # 提交文件\u003C\u002Fcode\u003E\u003Cp\u003E请记住,因为这些命令生成的是本地提交,小明可以按自己需求反复操作多次,而不用担心中央仓库上有了什么操作。 对需要多个更简单更原子分块的大功能,这个做法是很有用的。\u003C\u002Fp\u003E\u003Ch4\u003E小红开发功能\u003C\u002Fh4\u003E\u003Cp\u003E与此同时,小红在自己的本地仓库中用相同的编辑、暂存和提交过程开发功能。和小明一样,她也不关心中央仓库有没有新提交; 当然更不关心小明在他的本地仓库中的操作,因为所有本地仓库都是私有的。\u003C\u002Fp\u003E\u003Ch4\u003E小明发布功能\u003C\u002Fh4\u003E\u003Cp\u003E一旦小明完成了他的功能开发,会发布他的本地提交到中央仓库中,这样其它团队成员可以看到他的修改。他可以用下面的\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fremote-repositories#!push\& data-editable=\&true\& data-title=\&git push命令\&\u003Egit push命令\u003C\u002Fa\u003E:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit push origin master\u003C\u002Fcode\u003E\u003Cp\u003E注意,origin是在小明克隆仓库时Git创建的远程中央仓库别名。master参数告诉Git推送的分支。 由于中央仓库自从小明克隆以来还没有被更新过,所以push操作不会有冲突,成功完成。\u003C\u002Fp\u003E\u003Ch4\u003E小红试着发布功能\u003C\u002Fh4\u003E\u003Cp\u003E一起来看看在小明发布修改后,小红push修改会怎么样?她使用完全一样的push命令:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit push origin master\u003C\u002Fcode\u003E\u003Cp\u003E但她的本地历史已经和中央仓库有分岐了,Git拒绝操作并给出下面很长的出错消息:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Eerror: failed to push some refs to '\u002Fpath\u002Fto\u002Frepo.git'\nhint: Updates were rejected because the tip of your current branch is behind\nhint: its remote counterpart. Merge the remote changes (e.g. 'git pull')\nhint: before pushing again.\nhint: See the 'Note about fast-forwards' in 'git push --help' for details.\n\u003C\u002Fcode\u003E\u003Cp\u003E这避免了小红覆写正式的提交。她要先pull小明的更新到她的本地仓库合并上她的本地修改后,再重试。\u003C\u002Fp\u003E\u003Ch4\u003E小红在小明的提交之上rebase\u003C\u002Fh4\u003E\u003Cp\u003E小红用\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fremote-repositories#!pull\& data-editable=\&true\& data-title=\&git pull\&\u003Egit pull\u003C\u002Fa\u003E合并上游的修改到自己的仓库中。 这条命令类似svn update——拉取所有上游提交命令到小红的本地仓库,并尝试和她的本地修改合并:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit pull --rebase origin master\u003C\u002Fcode\u003E\u003Cp\u003E--rebase选项告诉Git把小红的提交移到同步了中央仓库修改后的master分支的顶部,如下图所示:\u003C\u002Fp\u003E\u003Cp\u003E如果你忘加了这个选项,pull操作仍然可以完成,但每次pull操作要同步中央仓库中别人修改时,提交历史会以一个多余的『合并提交』结尾。 对于集中式工作流,最好是使用rebase而不是生成一个合并提交。\u003C\u002Fp\u003E\u003Ch4\u003E小红解决合并冲突\u003C\u002Fh4\u003E\u003Cp\u003Erebase操作过程是把本地提交一次一个地迁移到更新了的中央仓库master分支之上。 这意味着可能要解决在迁移某个提交时出现的合并冲突,而不是解决包含了所有提交的大型合并时所出现的冲突。 这样的方式让你尽可能保持每个提交的聚焦和项目历史的整洁。反过来,简化了哪里引入Bug的分析,如果有必要,回滚修改也可以做到对项目影响最小。\u003C\u002Fp\u003E\u003Cp\u003E如果小红和小明的功能是不相关的,不大可能在rebase过程中有冲突。如果有,Git在合并有冲突的提交处暂停rebase过程,输出下面的信息并带上相关的指令:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003ECONFLICT (content): Merge conflict in &some-file&\n\u003C\u002Fcode\u003E\u003Cp\u003EGit很赞的一点是,任何人可以解决他自己的冲突。在这个例子中,小红可以简单的运行\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fgit-basics#!status\& data-editable=\&true\& data-title=\&git status\&\u003Egit status\u003C\u002Fa\u003E命令来查看哪里有问题。 冲突文件列在Unmerged paths(未合并路径)一节中:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E# Unmerged paths:\n# (use \&git reset HEAD &some-file&...\& to unstage)\n# (use \&git add\u002Frm &some-file&...\& as appropriate to mark resolution)\n#\n# both modified: &some-file&\n\u003C\u002Fcode\u003E\u003Cp\u003E接着小红编辑这些文件。修改完成后,用老套路暂存这些文件,并让\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Frewriting-git-history#!rebase\& data-editable=\&true\& data-title=\&git rebase\&\u003Egit rebase\u003C\u002Fa\u003E完成剩下的事:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit add &some-file& \ngit rebase --continue\u003C\u002Fcode\u003E\u003Cp\u003E要做的就这些了。Git会继续一个一个地合并后面的提交,如其它的提交有冲突就重复这个过程。\u003C\u002Fp\u003E\u003Cp\u003E如果你碰到了冲突,但发现搞不定,不要惊慌。只要执行下面这条命令,就可以回到你执行\u003Ca href=\&https:\u002F\u002Fwww.atlassian.com\u002Fgit\u002Ftutorial\u002Fremote-repositories#!pull\& data-editable=\&true\& data-title=\&git pull --rebase\&\u003Egit pull --rebase\u003C\u002Fa\u003E命令前的样子:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit rebase --abort\u003C\u002Fcode\u003E\u003Ch4\u003E小红成功发布功能\u003C\u002Fh4\u003E\u003Cp\u003E小红完成和中央仓库的同步后,就能成功发布她的修改了:\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ccode lang=\&text\&\u003Egit push origin master\u003C\u002Fcode\u003E\u003Cp\u003E如你所见,仅使用几个Git命令我们就可以模拟出传统Subversion开发环境。对于要从SVN迁移过来的团队来说这太好了,但没有发挥出Git分布式本质的优势。\u003C\u002Fp\u003E\u003Cp\u003E如果你的团队适应了集中式工作流,但想要更流畅的协作效果,绝对值得探索一

我要回帖

更多关于 包含目的字 的文章

 

随机推荐