Home Tags Posts tagged with "Git"

Git

0 74

IDEA中如何使用Git

一、 在Idea中配置Git

安装好IntelliJ IDEA后,如果Git 安装在默认路径下,那么idea会自动找到git的位置,如果更改了Git 的安装位置则需要手动配置下Git 的路径。

选择File→Settings打开设置窗口,找到Version Control下的git选项

选择git的安装目录后可以点击“Test”按钮测试是否正确配置。

至此,Git 已经集成到IDEA当中,下一步我们需要配置Git 账号信息

在IDEA中设置Git Hub,File–>Setting->Version Control–>GibHub

可以使用账号密码,或者Token(Token需要在你的Git Hub网站生成)

img

img

二、 从远程Git Lab下拉取项目代码

1.cmd操作

在你存放代码的目录下,通过cmd命令克隆代码

git clone + 项目URL

2.使用IDEA的Git 操作

选择File→New→Project from Version Control

填写要拉取的远程项目的URL,点击Clone

注意,能够拉取该项目的前提是你具有对应的权限

三、导入拉取的工程

此时项目工程已经拉取到了你的电脑上,但此时观察IDEA里面的工程列表,并没有新增工程

这样的场景常会出现,比如你负责的开发任务,刚开始只涉及一个工程,而后涉及到其他工程,你满怀期待的将其他工程拉取下来,本地有了代码,却没有同时出现在IDEA的Project列表

对应的解决办法就是,将新拉取的工程加入到当前的项目模块组中

选择File→New→Project Structure→Modules→"+"→Import Module

选择你代码所在的文件夹,以Maven类型导入即可

如果你需要移除工程,将上述操作的“+”换成“-”即可

四、将修改保存到暂存区

Git 和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

先来看名词解释。

工作区(Working Directory)

就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区:

版本库(Repository)

工作区有一个隐藏目录.git,这个不算工作区,而是Git 的版本库。

Git 的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git 为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

分支和HEAD的概念我们以后再讲。

前面讲了我们把文件往Git 版本库里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

在IDEA中将文件修改添加到暂存区

修改文件,这里理解为进行开发即可,比如我新增一个字段,一个接口

右键工程→Git →Add

当然,在工作中涉及到改动多个文件,我们可以同时全部Add,也可以先Add一部分,再Add一部分

你可以把它想象成往盒子里面放物品,你可以一下子全部放进去,也可以一部分一部分的放入,同时,你不用担心先放进去的部分会遗漏(盒子就是暂存区,Add即将改动放进暂存区,commit则是将暂存区的内容放进版本库)

如果改动的文件很多,我们就很难记住哪些文件被修改,同样的,即使改动的地方很少,我们也希望改动被记录下来,我们需要查看时,就会一目了然

1.使用Git Status命令对比分支

点击IDEA下方的Terminal→输入Git status(Git status用于查看当前所有的Git 状态,包括未Add,已Add未Commit等)

对于热衷于敲命令的同学来说,这是一件好事,但它无法显示文件具体的改动,我们来看看方式二

如果你需要使用Git 命令,那么就在此处畅所欲敲吧

2.使用IDAE的Git 操作对比分支

右键改动所在的工程→Git →Compare with Branch→选择你需要进行对比的分支(一般和master分支对比)

可以看到修改的文件,点击查看具体的修改内容,这实在是太酷了

在与master(或你选择的分支)对比的情况下,我们更容易发现自己改动是否有误,同时我们可以在此处直接修改,文件会实时生效,不用再Add

五、将暂存区中的改动提交到版本库的分支中

复习一下工作区,版本库,暂存区,分支的概念

工作区 git add →暂存区 git commit →本地分支,需要注意的是本地分支和暂存区stage都存在于版本库中(可回看【四、将修改保存到暂存区】)

右键改动所在的工程→Git →Commit Changes

现在,你的改动已经被提交到本地分支

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

Git  status查看工作区状态

你可以以此判断你是否漏掉了需要Commit的内容

工作区 git add →暂存区 git commit →本地分支 此时我们处于这条链路的最后一点【本地分支】

六、查看分支级别的改动信息

当你需要查看分支改动的时候,你可以使用 show history 查看当前分支的改动

右键工程→Git →show history,此时你会发现左下角的Git 面板被弹出

分支级别的信息,在这里都可以找到 你可以根据分支名/用户/日期/路径等对Git log 进行筛选

这些信息对于你后期避免分支冲突,查看分支改动有莫大的帮助

七、查看工作区,暂存区和版本库里面最新版本的区别

右键工程→Git →Compare with Revision

同样的,你也可以选择与其他版本进行比较

诶,你可能会说我们在前文提到了一种比较方法 使用IDEA的Git 操作对比改动 ,当时用的是 Compare with Branch,那现在为什么还要 Compare with Rivison 呢?

注意,这二者可是不一样的,Compare with Branch 是将你当前的工作区,暂存区,本地分支与远程分支进行对比,你可以理解为工程维度,尽管你可能会回滚;

而Compare with Rivision则是将你当前的工作区,暂存区与本地分支里的版本进行对比(每一次Commit都会创建一个版本)

八、回滚改动

1.你的改动还没Add到暂存区,只在工作区

这种情况直接改就可以了,工作区无所畏惧

2.你的改动已经Add到暂存区,但未Commit到本地分支

此时,你可以先使用 Git status 查看目前的Git 状态,它提示我们ApollConfigUtil文件的改动已经Add(绿色表示已经Add)

此时进行RollBack会回滚到未Add的状态

右键工程→Git →RollBack→选择你需要回滚的文件

使用 Git status 查看状态,红色表示已经Add,等待Commit,如果你有其他的未Add的改动,也会以绿色的形式显示在下面

右键工程→Git →RollBack→选择你需要回滚的文件

此时可以看到,你Add的改动,Commit的改动均消失不见了

3.你的改动已Commit到本地分支

此时你会发现RollBack无法点击,当你在工作区进行改动之后,它又可以点击了

总之,RollBack会让【未Add / 已Add但未Commit】的所有改动消失

九、从版本库删除文件

如果你需要删除一个文件,很简单,右键该文件→Delete 即可

在删除之后,如果你想查看它所带来的影响,同样可以使用 Git Status 命令

现在,我删除了 ApollConfigUtil.java 文件,使用 Git Status 来查看当前状态

此时,你具有两种选择

1.确实要从版本库中删除该文件,则直接提交改动即可提交改动

2.误删除该文件,那么因为版本库中还有,所以可以很轻松地把巫山的文件恢复到最新版本

使用IDEA 的 RollBack 操作回滚改动 或者 git checkout -- XX.java

git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!

命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容

十、分支管理

分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git 的时候,另一个你正在另一个平行宇宙里努力学习SVN。

如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git 又学会了SVN!

learn-branches

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作

其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。

但Git 的分支是与众不同的,无论创建、切换和删除分支,Git 在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。

1.Git 的分支原理

版本回退里,你已经知道,每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git 里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

一开始的时候,master分支是一条线,Git 用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

git-br-initial

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

当我们创建新的分支,例如dev时,Git 新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

git-br-create

你看,Git 创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!

不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

git-br-dev-fd

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git 怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

git-br-ff-merge

所以Git 合并分支也很快!就改改指针,工作区内容也不变!

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

git-br-rm

真是太神奇了,你看得出来有些提交是通过分支完成的吗?

下面开始实战。

首先,我们创建dev分支,然后切换到dev分支:

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后,用git branch命令查看当前分支:

$ git branch
* dev
  master

git branch命令会列出所有分支,当前分支前面会标一个*号。

然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行:

Creating a new branch is quick.

然后提交:

$ git add readme.txt 
$ git commit -m "branch test"
[dev b17d20e] branch test
 1 file changed, 1 insertion(+)

现在,dev分支的工作完成,我们就可以切换回master分支:

$ git checkout master
Switched to branch 'master'

切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:

git-br-on-master

现在,我们把dev分支的工作成果合并到master分支上:

$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。

注意到上面的Fast-forward信息,Git 告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

合并完成后,就可以放心地删除dev分支了:

$ git branch -d dev
Deleted branch dev (was b17d20e).

删除后,查看branch,就只剩下master分支了:

$ git branch
* master

因为创建、合并和删除分支非常快,所以Git 鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

2.本地分支 / 远程分支的概念

分支分为本地分支,远程分支两大类;注意,这是学习分支内容的重要前提!

a.本地分支的理解

现在你的导师给你分配了一个任务 —— 给某一个服务新增字段;可能他会给你具体的工程路径,也可能让你自己推测(我导师即是如此)

好的,经过努力,你找到了新增服务所在的工程路径,现在回到第二步 从远程Git Lab下拉取项目代码

此时你的代码就从 远程的工程路径分支(可能是master 也可能是 你导师的分支)克隆到 → 你的本地分支(如果你没有创建新的分支,则为master)

接下来,导入拉取的工程,进行你的修改并保存到暂存区,再提交到版本库中,对应于前文的 第三点,第四点,第五点

b.远程分支的理解(master release rel_xx_xx)

我们知道,公司的代码存放于 GitLab,我们也是从 GitLab 上拉取工程代码,回顾第二步 从远程Git Lab下拉取项目代码 ,其实,我们忽略了一步——选择分支

Master分支:主分支,公司服务正在使用的代码分支;开发过程中的最后一步即合并到Master分支,Master为生产环境,对Master的操作不当将直接影响生产环境,因此对Master分支的操作需要极度小心(如果你有权限的话)

Release分支:可以理解为Master分支的防线,我们所作的改动会现在Release分支上运行检验,通过后才会合并到Master分支,Release分支的内容与Master分支完全一致

Rel_xx_xx需求分支:由于 Git 创建分支的便捷性,我们提倡每一个需求都创建一个对应的分支,这些分支可以命名为 Rel_xx_xx(根据公司规定),这些分支里面包含我们的具体改动,比如

注意,需求分支一般需要先在本地创建,然后推送到远程仓库时,远程仓库会创建对应同名分支

3.创建和合并分支

对于Master分支和Release分支,我们一般没有权限操作,在此不表;

那么对于需求分支,我们先在本地创建即可

a. 使用Git branch命令创建本地分支

b. 使用 IDEA 分支面板 创建本地分支

右下角master→New Branch →Create 即可

注意:IDEA 关于 Git 的操作面板即为此处,可完成分支的新增,删除,切换,查看等

4.切换分支

当前你工作在rel_test_002分支上,你需要查看rel_test_001分支状态的代码,那么你可以 切换分支

a. 使用git checkout <name>或者git switch <name>命令切换
b.使用 IDEA 的分支面板切换分支

右下角→选择分支→checkout 即可

Git 鼓励大量使用分支:

查看分支:git branch

创建分支:git branch <name>

切换分支:git checkout <name>或者git switch <name>

创建+切换分支:git checkout -b <name>或者git switch -c <name>

合并某分支到当前分支:git merge <name>

删除分支:git branch -d <name>

5.对比分支

使用IDAE对比分支

6.分支冲突

人生不如意之事十之八九,合并分支往往也不是一帆风顺的。

准备新的feature1分支,继续我们的新分支开发:

$ git switch -c feature1
Switched to a new branch 'feature1'

修改readme.txt最后一行,改为:

Creating a new branch is quick AND simple.

feature1分支上提交:

$ git add readme.txt

$ git commit -m "AND simple"
[feature1 14096d0] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)

切换到master分支:

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Git 还会自动提示我们当前master分支比远程的master分支要超前1个提交。

master分支上把readme.txt文件的最后一行改为:

Creating a new branch is quick & simple.

提交:

$ git add readme.txt 
$ git commit -m "& simple"
[master 5dc6824] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

现在,master分支和feature1分支各自都分别有新的提交,变成了这样:

git-br-feature1

这种情况下,Git 无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:

$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

果然冲突了!Git 告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

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

    both modified:   readme.txt

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

我们可以直接查看readme.txt的内容:

Git  is a distributed version control system.
Git  is free software distributed under the GPL.
Git  has a mutable index called stage.
Git  tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

Git 用<<<<<<<=======>>>>>>>标记出不同分支的内容,我们修改如下后保存:

Creating a new branch is quick and simple.

再提交:

$ git add readme.txt 
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed

现在,master分支和feature1分支变成了下图所示:

git-br-conflict-merged

用带参数的git log也可以看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
*   cf810e4 (HEAD -> master) conflict fixed
|\  
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/  
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file

最后,删除feature1分支:

$ git branch -d feature1
Deleted branch feature1 (was 14096d0).

工作完成。

7.分支合并

a.Fast forward模式

通常,合并分支时,如果可能,Git 会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

如果要强制禁用Fast forward模式,Git 就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

b.不使用Fast forward模式

merge后就像这样:

git-no-ff-mode

8.Git 的Head指针

Git 中的 HEAD 可以理解为指针,指向当前仓库所处的分支。 一般在有 Git 管理的目录下打开 Git 终端都能在当前路径的尾巴上,看到所处的分支名。 还可以在命令行中输入 查看当前 HEAD 指向哪里。

9.Bug分支

软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git 中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:

$ git status
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   readme.txt

并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?

幸好,Git 还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge

现在,用git status查看工作区,就是干净的(除非有没有被 Git 管理的文件),因此可以放心地创建分支来修复bug。

首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git checkout -b issue-101
Switched to a new branch 'issue-101'

现在修复bug,需要把“Git is free software …”改为“Git is a free software …”,然后提交:

$ git add readme.txt 
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

太棒了,原计划两个小时的bug修复只花了5分钟!现在,是时候接着回到dev分支干活了!

$ git switch dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean

工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:

$ git stash list
stash@{0}: WIP on dev: f52c633 add merge

工作现场还在,Git 把stash内容存在某个地方了,但是需要恢复一下,有两个办法:

一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;

另一种方式是用git stash pop,恢复的同时把stash内容也删了:

$ git stash pop
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   readme.txt

Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)

再用git stash list查看,就看不到任何stash内容了:

$ git stash list

你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

$ git stash apply stash@{0}

10.Feature分支

软件开发中,总有无穷无尽的新的功能要不断添加进来。

添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

现在,你终于接到了一个新任务:开发代号为Vulcan的新功能,该功能计划用于下一代星际飞船。

于是准备开发:

$ git switch -c feature-vulcan
Switched to a new branch 'feature-vulcan'

5分钟后,开发完毕:

$ git add vulcan.py

$ git status
On branch feature-vulcan
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   vulcan.py

$ git commit -m "add feature vulcan"
[feature-vulcan 287773e] add feature vulcan
 1 file changed, 2 insertions(+)
 create mode 100644 vulcan.py

切回dev,准备合并:

$ git switch dev

一切顺利的话,feature分支和bug分支是类似的,合并,然后删除。

但是!

就在此时,接到上级命令,因经费不足,新功能必须取消!

虽然白干了,但是这个包含机密资料的分支还是必须就地销毁:

$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.

销毁失败。Git 友情提醒,feature-vulcan分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数。。

现在我们强行删除:

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 287773e).

终于删除成功!

十一、Pull最新代码

Pull操作会把远程仓库最新的代码拉取下来,这样能确保你本地分支的代码是最新的,因此,在开发过程中,如果你发现其他人修改了你工作对应的远程分支,你应该立即Pull最新的代码

十二、Push到远程仓库

现在,你终于完成了代码修改,你确定一切准备就绪,那么可以将你的分支推送到远程了;之前我们所有的操作均在本地分支上,如果想让我们的修改生效或者被其他人所看到,则需要推送到远程仓库

右键工程→Git →Push

当然,建议在Push你的修改之前,先Pull一下,Pull操作会把远程仓库最新的代码拉取下来,这样能确保你本地分支的代码是最新的,因此,在开发过程中,如果你发现其他人修改了你工作对应的远程分支,你应该立即Pull最新的代码(再次强调)

十三、修改 commit 描述信息

idea中,如何修改已经commit记录的message?如何修改已经push过记录message?全冉 的博客-CSDN博客idea修改commit信息

完结

Git 是目前世界上最先进的分布式版本控制系统(没有之一)。如果你想要学习更多的 Git 操作,可以前往廖雪峰前辈的博客网站进行学习!

0 70

我今天分享的主题是:大厂都在用哪些敏捷方法?我将分为上下两篇,来与你一起讨论这个话题。在我还是一个野路子程序员,到处接私活做网站时,就开始好奇:大厂都是怎么开发软件项目的?直到毕业后,我前前后后加入了若干大中小型企业,包括这些年在美国高校、公司的一些经历,对大厂的项目开发有了比较多的了解。

其实大厂做项目也没有什么特别的,无非就是工程中常见的“分而治之”的策略:大项目拆成小项目,大服务拆成小服务,大团队拆成小团队。

服务之间通过商定好的标准协议进行通信,架构上将大的服务拆分隔离成微服务,大团队按照业务或者服务拆分成小组,按照一定的流程规范保障协作。最终,各个小组要负责的内容其实就不多了。

就像淘宝这种网站,不需要一个庞大的项目组,通过逐级分拆,一个小组可能就只需要负责一个页面中的一个小模块。

所以,也要归功于现在微服务、容器等新技术,可以将复杂的业务逐级拆分,让很多公司能真正敏捷起来。

 

一、和敏捷开发相关的主要流程规范

一切工作任务围绕 Ticket 开展

早些年的项目开发,都是围绕着项目计划开展的,把甘特图打印贴在墙上,方便团队成员看项目进展到什么地步了。自从敏捷化后,开始变成了看板。

所谓的看板,就是把白板分成几个栏,每一栏为一类,分别写着“To Do(待选取)”、“In Progress(进行中)”、“Done(完成)”等,再把工作任务变成一个个五颜六色的即时贴,根据状态贴在不同的栏下面。

慢慢的物理的看板变成了电子看板,通过各种项目管理软件来管理跟踪这些任务,即时贴也变成了 Ticket(也有叫 Issue 的)。逐渐的,所有与开发相关的任务也都和 Ticket 挂钩了:

  • 报一个 Bug,提交一个 Ticket ;
  • 提一条需求,提交一个 Ticket ;
  • 要重构一下代码,提交一个 Ticket 。

看板这种基于 Ticket 来管理跟踪任务的方式,看起来繁琐,但确实是很高效的一种方式。

  • 每一个任务的状态都可以被跟踪起来:什么时候开始做的,谁在做,做完没有。
  • 整个团队在做什么一目了然。
  • Ticket 和敏捷开发中的 Backlog(任务清单)正好结合起来,通过 Ticket 可以收集管理整个项目的 Backlog 和当前 Sprint(迭代)的 Backlog。

有了看板后,大家每天上班第一件事就是打开看板,看看当前 Sprint 还有哪些 Ticket 没有完成,哪些已经完成,哪些正在进行中,非常直观。

作为项目成员来说,做完手头的事情也不用去问项目经理该干什么事情了,直接从 To Do 栏选一条 Ticket 做就是了;对于项目经理,看看 To Do 栏还有多少没有被选取,就知道还剩多少 Ticket 没完成,看看 In Progress 栏就知道哪些 Ticket 正在进行中。

如果有 Ticket 在这一栏待太久或者这一栏 Ticket 太多,那可能就有风险了,就可以及时介入。

 

基于 Git 和 CI 的开发流程

如果你的团队应用瀑布模型来开发,大概会有两大烦恼:代码不稳定和部署太麻烦。

好在基于 Git 的开发流程结合 CI 的自动测试部署,很完美的解决了这两大问题。

我们假设现在 master 的代码是稳定的,那么怎么保证新加入的代码也稳定呢?

答案就是代码审查(Code Review)和自动化测试。如果代码有严格的审查,并且所有自动化测试代码都能测试通过,那么可以认为代码质量是可靠的。当然前提是自动化测试代码要有一定的覆盖比率。

Git 本来只是源代码管理工具,但是其强大的分支管理和灵活的权限控制,结合一定的开发流程,却可以帮助你很好的控制代码质量。

接下来还剩下自动化测试的问题。这时候该 CI (持续集成)出场了。如果你不了解 CI 是什么,可以把它想象成一个机器人,每次你提交一个 PR(严格来说是 Commit,这里略作简化)到源代码服务器,这个机器人马上就知道了。然后它创建一个干净的运行环境,把你提交的代码下载下来,再下载安装所有依赖项,然后运行你的所有测试代码,运行完后,把测试结果报告给你。测试结果直观的反馈在 PR 上,绿色表示通过,红色表示不通过。

关于 Git 和 CI,我在之后的文章中会展开讲解,这里只是为了展现敏捷开发方法的流程。另外,阮一峰老师写过两篇文章,《Git 工作流程》《持续集成是什么?》,你也可以先行阅读了解。

http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html

http://www.ruanyifeng.com/blog/2015/12/git-workflow.html

至此,代码审查和自动测试的问题都解决了。当一个 PR 代码审查通过,以及 CI 通过了所有自动化测试,就可以合并到 master 了,而且我们也可以认为合并到 master 后的代码也是稳定的。(PR—合并分支,CI—持续集成)

至于自动部署测试环境,反倒是简单,就是 CI 这个机器人,在你代码合并到 master 的时候,再次运行自动化测试代码,测试通过后直接运行自动部署的脚本,把 master 代码部署到开发环境或测试环境上。

在这里以一个开发任务为例,大致讲解一下应用敏捷开发方法的基本开发流程:

  • 把要开发的 Ticket 从“To Do”栏移动到“In Progress”栏;
  • 从主干(master)创建一个分支(branch),基于分支去开发功能或修复 Bug;
  • 编写实现代码和测试代码(单元测试和集成测试),是不是测试驱动不重要,看个人偏好或团队要求;
  • 持续提交代码更新到分支,直到完成;创建 PR(Pull Request,合并请求),邀请其他人帮忙 Review 代码,根据 Review 的结果,可能还需要更新几次;
  • CI 在每一次提交代码到代码库后都会自动运行,运行后主要做这些工作:– 检查代码格式是不是符合规范;– 运行单元测试代码;
  • – 运行集成测试。最终这些检查都完成后,CI 会把执行结果显示在 PR 上。通常绿色表示通过,红色表示失败;
  • PR 能合并需要满足两个条件:CI 变绿 + 代码 Review 通过;
  • PR 合并后,CI 会自动构建 Docker Image,将 Image 部署到开发环境;
  • 将相应的 Ticket 从看板上的“In Progress”栏移动到“Done”栏。

正常来讲,你是需要严格遵守开发流程的,但偶尔肯定也有紧急任务,来不及写测试代码,这种情况下,一定要再创建一条 Ticket 跟踪,以确保后续完成测试代码

 

部署上线流程

最早的时候,程序员都是自己管服务器,但是由于这样过于随意,就会导致很多问题出现。

于是后来有专门的运维团队,将开发好的程序,编译好,数据生成脚本写好,然后写成部署文档,交给运维去手动部署。这个过程无比繁琐、无比慎重,通常几周才部署一次,遇上打补丁才隔几天部署。

这些年随着容器化、微服务、DevOps 这些技术或概念的兴起,部署已经变得越来越高效,大厂已经开始在部署流程上融合这些理念。

以前是运维人员按照文档部署,现在已经变成了 DevOps 写自动化部署工具,然后开发人员自己去部署生产环境。

现在大厂的部署也都实现了自动化,但是流程上还是有一些控制。

  • 首先,部署的不再是程序代码,而是 Docker 的 Image,每次代码合并后 CI 都会自动生成新的 Image,测试也是基于 Image 测试。
  • 部署生产环境之前,先在内部的测试环境充分测试。
  • 部署生产环境前,需要审批确认,有 Ticket 跟踪。
  • 部署时,先部署一部分,监测正常后再全量部署。
  • 整个过程都有监控报警,出现问题及时回滚。

如果一切顺利的话,整个生产环境的服务部署过程通常几分钟就完成了,这在以前简直是不敢想象的事。

 

每日站立会议

是不是站着开会其实不重要,重点是要高效沟通反馈。开会时间控制在半小时以内,半小时内不能完成的应该另外组织会议。

1. 成员轮流发言

2.检查最新的 Ticket

3.停车场问题(在这个环节,大家可以针对之前来不及讨论的问题进行讨论,能在会议时间内解决的问题,就马上解决,不能解决的会后再私下讨论或者再组织会议。)

 

总结

分治策略是应对庞大复杂系统的惯用思路,但它的难点或精髓在于如何确保形散神聚。

详细计划(甘特图)VS任务状态(Ticket)

代码不稳定&环境部署麻烦
VS
代码审查&自动测试&自动部署(GIT、CI、DevOps)

上传下达VS频繁沟通、提醒、分享

大厂的敏捷开发实践,把枯燥的编码变得跟玩游戏一样。借助有效的流程与工具,能够有效节约团队成员的精力,聚焦于任务或角色,不会因频繁“统一思想”导致“技术动作变形”。而另一面,在大厂里每个人通常都是螺丝钉,长此以往也许会养成不谋全局的习惯。如果能从自己的角色中跳出来,俯瞰整个组织协作的全过程,并站在这个视角上思考问题,一定会有更喜人的收获。