一篇文章搞定Git——Git代码管理及使用规范

基本概念:

版本库(Repository)是版本控制系统用来存放所有历史数据的地方,主要存放各个文件的当前状态,历史修改时间,谁做的修改,以及修改的原因。举个简单的例子,就好比银行的保险箱,每次往里存钱,都会记录谁,什么时间,存放多少钱,存入的原因等。对应的版本库(Repository)主要存放代码(文档,数据,图标等),并且每一次更新都要记录谁,什么时间,提交了什么更新,以及更新的原因是什么。

GIT常用配置文件

  1. .gitgnore忽略文件列表
  2. .git/config配置文件

GIT常用命令

 

GIT分支管理

GIT远程分支主要包含:master develop feature release fixbug

master整个项目主分支,有且仅有一个,除项目负责人以外的开发人员不能像master分支合并内容。

develop主分支只用来分布重大版本,日常开发应该在另一条分支上完成。开发用的分支为Develop。

featurefeature是为了开发后续版本的功能,从Develop分支上面分出来的。开发完成稳定后,要再并入Develop

release:release是发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。

fixbugfixbug分支是从master分支上面分出来的。fix结束以后,再合并进MasterDevelop分支。最后,删除"fixbug分支"

一、主分支Master

首先,代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。

Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。

二、开发分支Develop

主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做Develop。

 这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行”合并”(merge)。

Git创建Develop分支的命令:

$ git checkout -b develop master

将Develop分支发布到Master分支的命令:

# 切换到Master分支

$ git checkout master

# 对Develop分支进行合并

$ git merge noff develop

这里稍微解释一下,上一条命令的–no–ff参数是什么意思。默认情况下,Git执行”快进式合并”(fast-farward merge),会直接将Master分支指向Develop分支。

使用–no–ff参数后,会执行正常合并,在Master分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。关于合并的更多解释,请参考Benjamin Sandofsky的《Understanding the Git Workflow》

三、临时性分支

前面讲到版本库的两条主要分支:Master和Develop。前者用于正式发布,后者用于日常开发。其实,常设分支只需要这两条就够了,不需要其他了。

但是,除了常设分支以外,还有一些临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种:

  1. 功能(feature)分支
  2. 预发布(release)分支
  3. 修补bug(fixbug)分支

这三种分支都属于临时性需要,使用完以后,应该删除,使得代码库的常设分支始终只有Master和Develop。

四、 功能分支

接下来,一个个来看这三种”临时性分支”。

第一种是功能分支,它是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。

功能分支的名字,可以采用feature-*的形式命名。

创建一个功能分支:

$ git checkout -b feature-x develop

开发完成后,将功能分支合并到develop分支:

$ git checkout develop

$ git merge no-ff feature-x

删除feature分支:

$ git branch -d feature-x

五、预发布分支

第二种是预发布分支,它是指发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。

预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。它的命名,可以采用release-*的形式。

创建一个预发布分支:

$ git checkout -b release-1.2 develop

确认没有问题后,合并到master分支:

$ git checkout master

$ git merge no-ff release-1.2

# 对合并生成的新节点,做一个标签

$ git tag -a 1.2

再合并到develop分支:

$ git checkout develop

$ git merge no-ff release-1.2

最后,删除预发布分支:

$ git branch -d release-1.2

六、修补bug分支

最后一种是修补bug分支。软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。

修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。它的命名,可以采用fixbug-*的形式

      创建一个修补bug分支:

$ git checkout -b fixbug-0.1 master

修补结束后,合并到master分支:

$ git checkout master

$ git merge no-ff fixbug-0.1

$ git tag -a 0.1.1

再合并到develop分支:

$ git checkout develop

$ git merge no-ff fixbug-0.1

最后,删除”修补bug分支”:

$ git branch -d fixbug-0.1

GIT分支新建与合并

我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。 你将经历如下步骤:

  1. 开发某个网站。
  2. 为实现某个新的需求,创建一个分支。
  3. 在这个分支上开展工作。

正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:

  1. 切换到你的线上分支(production branch)。
  2. 为这个紧急任务新建一个分支,并在其中修复它。
  3. 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
  4. 切换回你最初工作的分支上,继续工作。

新建分支

首先,我们假设你正在你的项目上工作,并且已经有一些提交。

现在,你已经决定要解决你的公司使用的问题追踪系统中的 #53 问题。 想要新建一个分支并同时切换到那个分支上,你可以运行一个带有-b参数的git checkout命令:

$ git checkout -b iss53

它是下面两条命令的简写:

$ git branch iss53

$ git checkout iss53

你继续在 #53 问题上工作,并且做了一些提交。 在此过程中,iss53分支在不断的向前推进,因为你已经检出到该分支(也就是说,你的HEAD指针指向了iss53分支)

$ vim index.html

$ git commit -a -m 'added a new footer [issue 53]'

现在有个紧急问题等待你来解决。 有了 Git 的帮助,你不必把这个紧急问题和 iss53的修改混在一起,你也不需要花大力气来还原关于 53# 问题的修改,然后再添加关于这个紧急问题的修改,最后将这个修改提交到线上分支。 你所要做的仅仅是切换回 master 分支。

但是,在你这么做之前,要留意你的工作目录和暂存区里那些还没有被提交的修改,它可能会和你即将检出的分支产生冲突从而阻止 Git 切换到该分支。 最好的方法是,在你切换分支之前,保持好一个干净的状态。 有一些方法可以绕过这个问题(即,保存进度(stashing) 和 修补提交(commit amending))。现在,我们假设你已经把你的修改全部提交了,这时你可以切换回 master 分支了:

$ git checkout master

$ Switched to branch 'master'

这个时候,你的工作目录和你在开始 #53 问题之前一模一样,现在你可以专心修复紧急问题了。 请牢记:当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样。

接下来,你要修复这个紧急问题。 让我们建立一个针对该紧急问题的分支(hotfix branch),在该分支上工作直到问题解决:

$ git checkout -b hotfix

$ vim index.html

$ git commit -a -m 'fixed the broken email address'

你可以运行你的测试,确保你的修改是正确的,然后将其合并回你的 master 分支来部署到线上。 你可以使用 git merge 命令来达到上述目的:

$ git checkout master

$ git merge hotfix

现在,最新的修改已经在 master 分支所指向的提交快照中,可以着手发布该修复了。

关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。 然而,你应该先删除hotfix分支,因为你已经不再需要它了 ——master分支已经指向了同一个位置。 你可以使用带-d选项的git branch命令来删除分支:

$ git branch -d hotfix

现在你可以切换回你正在工作的分支继续你的工作,也就是针对 #53 问题的那个分支(iss53 分支)。

$ git checkout iss53

$ vim index.html

$ git commit -a -m 'finished the new footer [issue 53]'

你在hotfix分支上所做的工作并没有包含到iss53分支中。 如果你需要拉取hotfix所做的修改,你可以使用git merge master命令将master分支合并入iss53分支,或者你也可以等到iss53分支完成其使命,再将其合并回master分支。

分支的合并

如果已经修正了 #53 问题,并且打算将你的工作合并入master分支。 为此,你需要合并iss53分支到master分支,这和之前你合并hotfix分支所做的工作差不多。 你只需要检出到你想合并入的分支,然后运行git merge命令:

$ git checkout master

$ git merge iss53

和之前合并hotfix分支的时候看起来有一点不一样。 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为master分支所在提交并不是iss53分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。

和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。

既然修改已经合并进来了,你已经不再需要iss53分支了。 现在你可以在任务追踪系统中关闭此项任务,并删除这个分支。

$ git branch -d iss53

遇到冲突时的分支合并

有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关hotfix的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:

$ git merge iss53

Auto-merging index.html

CONFLICT (content): Merge conflict in index.html

Automatic merge failed; fix conflicts and then commit the result.

此时 Git 做了合并,但是没有自动地创建一个新的合并提交。 Git 会暂停下来,等待你去解决合并产生的冲突。 你可以在合并冲突后的任意时刻使用git status命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:

$ git status

On branch master

You have unmerged paths.

  (fix conflicts and run "git commit")

Unmerged paths:

  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

在你解决了所有文件里的冲突之后,对每个文件使用git add命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。

如果你想使用图形化工具来解决冲突,你可以运行 git mergetool,该命令会为你启动一个合适的可视化合并工具,并带领你一步一步解决这些冲突。

GIT文件状态

GIT文件状态主要包括untracked 未追踪、unmodified未修改、modified已修改、staged

GIT开发流程

Gitflow工作流

Gitflow工作流定义了一个围绕项目发布的严格分支模型。虽然比功能分支工作流复杂几分,但提供了用于一个健壮的用于管理大型项目的框架。

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflows-gitflow.png

工作方式

Gitflow工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并push分支到要中央仓库中。

历史分支

相对使用仅有的一个master分支,Gitflow工作流使用2个分支来记录项目的历史。master分支存储了正式发布的历史,而develop分支作为功能的集成分支。这样也方便master分支上的所有提交分配一个版本号。

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-1historical.png

剩下要说明的问题围绕着这2个分支的区别展开。

功能分支

每个新功能位于一个自己的分支,这样可以push到中央仓库以备份和协作。但功能分支不是从master分支上拉出新分支,而是使用develop分支作为父分支。当新功能完成时,合并回develop分支。新功能提交应该从不直接与master分支交互。

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-2feature.png

注意,从各种含义和目的上来看,功能分支加上develop分支就是功能分支工作流的用法。但Gitflow工作流没有在这里止步。

发布分支

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-3release.png

一旦develop分支上有了做一次发布(或者说快到了既定的发布日)的足够功能,就从develop分支上fork一个发布分支。新建的分支用于开始发布循环,所以从这个时间点开始之后新的功能不能再加到这个分支上 —— 这个分支只应该做Bug修复、文档生成和其它面向发布任务。一旦对外发布的工作都完成了,发布分支合并到master分支并分配一个版本号打好Tag。另外,这些从新建发布分支以来的做的修改要合并回develop分支。

使用一个用于发布准备的专门分支,使得一个团队可以在完善当前的发布版本的同时,另一个团队可以继续开发下个版本的功能。
这也打造定义良好的开发阶段(比如,可以很轻松地说,『这周我们要做准备发布版本4.0』,并且在仓库的目录结构中可以实际看到)。

维护分支

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-4maintenance.png

维护分支或说是热修复(hotfix)分支用于生成快速给产品发布版本(production releases)打补丁,这是唯一可以直接从master分支fork出来的分支。修复完成,修改应该马上合并回master分支和develop分支(当前的发布分支),master分支应该用新的版本号打好Tag。

为Bug修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。你可以把维护分支想成是一个直接在master分支上处理的临时发布。

示例

创建开发分支

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-5createdev.png

第一步为master分支配套一个develop分支。简单来做可以本地创建一个空的develop分支,push到服务器上:

$ git branch develop
$ git push -u origin develop

以后这个分支将会包含了项目的全部历史,而master分支将只包含了部分历史。其它开发者这时应该克隆中央仓库,建好develop分支的跟踪分支:

$ git clone ssh://user@host/path/to/repo.git
$ git checkout -b develop origin/develop

现在每个开发都有了这些历史分支的本地拷贝。

小红和小明开始开发新功能

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-6maryjohnbeginnew.png

这个示例中,小红和小明开始各自的功能开发。他们需要为各自的功能创建相应的分支。新分支不是基于master分支,而是应该基于develop分支

$ git checkout -b some-feature develop

他们用老套路添加提交到各自功能分支上:编辑、暂存、提交:

$ git status
$ git add
$ git commit

小红完成功能开发

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-7maryfinishes.png

添加了提交后,小红觉得她的功能OK了。如果团队使用Pull Requests,这时候可以发起一个用于合并到develop分支。否则她可以直接合并到她本地的develop分支后push到中央仓库:

$ git pull origin develop
$ git checkout develop
$ git merge some-feature
$ git push
$ git branch -d some-feature

第一条命令在合并功能前确保develop分支是最新的。注意,功能决不应该直接合并到master分支。冲突解决方法和集中式工作流一样。

小红开始准备发布

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-8maryprepsrelease.png

这个时候小明正在实现他的功能,小红开始准备她的第一个项目正式发布。像功能开发一样,她用一个新的分支来做发布准备。这一步也确定了发布的版本号:

$ git checkout -b release-0.1 develop

这个分支是清理发布、执行所有测试、更新文档和其它为下个发布做准备操作的地方,像是一个专门用于改善发布的功能分支。

只要小红创建这个分支并push到中央仓库,这个发布就是功能冻结的。任何不在develop分支中的新功能都推到下个发布循环中。

小红完成发布

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-release-cycle-9maryfinishes.png

一旦准备好了对外发布,小红合并修改到master分支和develop分支上,删除发布分支。合并回develop分支很重要,因为在发布分支中已经提交的更新需要在后面的新功能中也要是可用的。另外,如果小红的团队要求Code Review,这是一个发起Pull Request的理想时机。

$ git checkout master
$ git merge release-0.1
$ git push
$ git checkout develop
$ git merge release-0.1
$ git push
$ git branch -d release-0.1

发布分支是作为功能开发(develop分支)和对外发布(master分支)间的缓冲。只要有合并到master分支,就应该打好Tag以方便跟踪。

$ git tag -a 0.1 -m "Initial public release" master
$ git push –tags

Git有提供各种勾子(hook),即仓库有事件发生时触发执行的脚本。可以配置一个勾子,在你push中央仓库的master分支时,自动构建好对外发布。

最终用户发现Bug

https://raw.githubusercontent.com/quickhack/translations/master/git-workflows-and-tutorials/images/git-workflow-gitflow-enduserbug.png

对外发布后,小红回去和小明一起做下个发布的新功能开发,直到有最终用户开了一个Ticket抱怨当前版本的一个Bug。为了处理Bug,小红(或小明)从master分支上拉出了一个维护分支,提交修改以解决问题,然后直接合并回master分支:

$ git checkout -b issue-#001 master
# Fix the bug
$ git checkout master
$ git merge issue-#001
$ git push

就像发布分支,维护分支中新加这些重要修改需要包含到develop分支中,所以小红要执行一个合并操作。然后就可以安全地删除这个分支了:

$ git checkout develop
$ git merge issue-#001
$ git push
$ git branch -d issue-#001

GIT目录结构

GIT针对于每个项目保证目录统一,以方便代码管理,其中目录主要包括:Management、Document、Code、UI

Management:该项目团队出开发以外的资料

Document:整个项目实施过程中的文档,包括(需求,开发,测试)

Code:项目源代码

UI:项目UI部分资料

GIT代码提交规则

  1. 设置用户名为姓名全拼,邮箱为公司邮箱地址。
  2. 代码提交规则
    1. 使用英文说明提交内容。
    2. 提交的说明包括两部分,动作类型: 简要说明,以英文的“: ”进行区分。
    3. 动作类型使用英文大写
    4. 理论上一次提交仅包含一个功能点修改,如果功能过大,则需要注明功能点进度。如果一次提交包含多个功能修改,则每个功能点提交描述作为单独一行,每行以英文分号";"作为行尾结束符。
    5. ADD: [PluginFramework], 提交的内容信息。

动作

说明

示例

ADD

新加功能、文件等

 

DEL

删除功能、文件等

 

MOD

修改功能

 

FIX

修复问题等,如果修复的为BUG,请标注BUG编码

FIX: BUG#123, bug's description;

FIX: BUG#124, bug's description;

MERGE

合并代码,自动合并时一般不需要修改说明文字

 

REVIEW

代码审核注释,需注明审核结果(需使用中括号括起)、@被审核者、被审核模块名称、文件名

REVIEW: [PASS], @zhanxy, for excel operation model

REFACTOR

代码重构,需注明重构模块,及说明。一般重构需要使用新的独立分支处理。

 

DOC

文档

 

 

  1. 代码提交流程
    1. 代码修改完成后,首先进行编码规范检查,注释检查,自我测试,单元测试、编译测试等。
    2. 测试通过后提交至本地,提交过程中检查修改的文件是否正确,有无遗漏文件等,注明修改说明后提交代码。
    3. 拉取服务器上的代码,如果本地代码是最新的提交,则直接推送到服务器。如果拉取到别人提交的代码,则需合并服务器的代码到本地,并仔细检查代码合并结果,如有冲突则需要找到相应的编码人员一起解决冲突。问题解决后,重新编译本地代码,并测试,通过测试后提交本地代码。
    4. 推送本地提交至服务器。服务器检测到有代码推送后会自行构建所有代码。
  2. 工作流程
    1. 每天早上开始工作前,请先拉取服务器代码,并编译,检查服务器代码是否有错误。检查无误后即可开始自己的工作。如果有误,请通知最后的代码提交者及项目负责人。
    2. 理论上没修改一个完整的功能点都可以提交代码并推送至服务器(参考4. 代码提交流程)。

每天工作结束后或者下班前,请提交本地代码,并推送至服务器(参考4. 代码提交流程)。

发布了15 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42092278/article/details/90448721