前言

都说互联网的本质就是迭代!那我们如何保证代码可以快速迭代、回滚呢?所以版本控制工具就应运而生了。

 

一、git是什么?

git 是一款分布式的版本控制软件,在学习git之前我们先来看下版本控制工具发展的历程;

1.版本控制工具发展历程

1.1 手工保存文件

就像我们在大学里写论文从最开始----->版本1----->版本2----->版本3.........导师认可版,这个不断修正、反复修改的过程就是手工版本控制;

当然我们写的这些毕业论文是使用 N个文件得以实现版本控制的。

1.2.版本控制工具出现

使用软件实现本地版本控制后我们只能看到1文件,而其他的增量/减量软件自动帮我们管理起来,我们通过命令实现版本间的切换,

这就我们就实现了集中式本地版本控制(local computer versin control)。

1.3.集中式版本控制工具

 

本地版本控制的缺陷是只能1个人在自己的电脑上玩,无法满足我们多人协同去完成1个论文、书籍的撰写的需求;

如果我们多个人进行同1个项目的开发,如何做到版本控制呢?

于是出现就集中化版本控制(eg:SVN),把所有版本集中式的保存在1台服务器(数据仓库)里,这样大家就可以做协同开发了

1.4.分布式版本控制工具

集中式的版本控制的缺陷是所有的版本都保存在远程服务器上,我们本地电脑中没有历史版本,如果中心server down掉了,我们的本地电脑无法及时保存所有的历史版本,而分布式的版本控制工具是你可以1次从远程服务器  -----------》 pull到本地所有的历史版本。这样大家都有了历史版本就不怕单点故障了,即使server down了我们也可以把自己本地所以历史版本push-----》server,这样就不影响我们版本迭代了。

 

2.Git的架构

git的核心是两个仓库: 本地仓库和远程仓库。

  • 本地仓库:安装在开发者本机电脑,主要负责版本控制,日志记录

  • 远程仓库:安装在公共网络服务器,主要负责多人代码协作开发

 

 

 

二、本地仓库开发

现在我们就从在本地仓库中如何使用git开始;

首先进入要管理的文件夹,进入文件夹后点击 git bash here

1.git区域

git 划分为以下3个区域,从工作区到版本库的过程就是创建1个版本的过程

1.1.工作区

在Git本地仓库中,新增的文件/目录,都需要添加到暂存区由git进行管理;

新文件/目录只需要被add一次,文件/文件夹被add之后,如果内容发生了修改无需再次add.

1.2.暂存区

暂存区是1个缓冲区,通过 git add 把文件由工作区------》暂存区(文件为绿色)

1.3.本地版本库 

如果决定生成版本之后, 通过 git commit 把文件由暂存区提交到本地版本库生成1个版本

 

2.版本生成命令

初始化

$ git init

配置git信息

如果没有给git配置声明个人信息,将无法commit 生成新的版本。

git config --global user.name "zhanggen@le.com"
git config --global user.email "张根"

查看文件状态

$ git status

管理

$ git add index.html  管理单个文件
$ git add .           管理当前文件夹下所有文件 

生成本地版本

$ git commit -m 'v1'   v1为版本名称

查看生成的版本信息

$ git log

查看版本记录

$ git reflog 

 

3.版本库回退

注意再一下撤回这种使用的版本唯一标识符是 你想要回退到版本的唯一标识符,而不是本版本

 

一步一步地回退

 版本库------>暂存区

$ git reset --soft dc2c764b22b78d544746cd0419091982ca9b3dc8

暂存区------>工作区

$ git reset head index.html

工作区---->最原始未修改状态

如果我们修改了文件,文件会自动被检测到变红,那么我们如何撤销修改呢?

$ git checkout index.html

工作区----->堆栈区(暂时存放)

假设我们正在开发的过程中,原来的代码出现紧急bug需要修改,此时我们既无法提交代码到版本库,也需要保存已修改的代码  去修复源代码的bug怎么办?

那我就可以找个地方暂存一下我们修改的代码;

$ git stash
Saved working directory and index state WIP on master: fc0f6aa ‘yu
$ git stash pop #从堆栈区拿出来

 

以下是2种方式比较快捷的的回退方式

版本库------>直达工作区(文件已修改的状态)

git reset --mix dc205fe8ddc2f23c97e84a0e40f25c4b8c4bae39

版本----->版本

git log 可以查看每个版本的唯一标识符,通过唯一标识符就可以在版本之间切换;

$ git reflog  #查看所有版本记录
$ git reset --hard dc2c764b22b78d544746cd0419091982ca9b3dc8

 

三、多分支并行开发

本地仓库解决了代码版本迭代的问题,如果在团队中需要多个开发人员协作开发,就需要多个分支;

git的分支之间是隔离的; 

同时创建多个分支,每个人在不同的分支上开发不同的功能,功能开发完成后,所有子分支--->提交到远程代码仓库, 完成子分支合并到master主分支。

基于master分支反复分裂、合并, 实现了多人在多个分支上并行开发,大大提升了工作效率。

git的每个版本只保存自己修改的部分,原来的基础部分通过指针引指向   上一版本 直到  第1个版本,这种链接式设计节省了存储空间加快了我们在版本/分支之间切换的速度。

每个分支之间的代码都是相互隔离的,这样也可以帮助我们同时正在不同的分支上同时开发不同的功能,以及之前版本出现bug后及时修复。

git的主分支称为master ,其他分支由人为命名,一般都有master分支(线上代码)、 dev分支(开发新功能)。

 

创建分支

我们在哪个分支上创建了新分支,新分支的指针就会指向该分支最新的版本(表象是2个分支的代码一模一样)。

git branch dev

 

查看分支

查看本地分支

git branch

查看远程分支

git branch -r

查看所有分支

git branch -a

切换到远程分支

git checkout -b local_branch origin/reduce_input

 

删除分支

git branch -d dev

 

合并分支

如果你想让A分支合并到B分支,一定要切换到B分支上,执行git merge A。你想要谁就 merge谁过来。

既然merge就是2个分支合并嘛,但是二者合并的依据是什么呢?根据2个分支中分叉之后的commits。

 

从目标分支的commit 和当前分支commit (即 HEAD 所指向的 commit)分叉的位置起,把目标 分支commit 路径上的所有 commit 的内容一并应用到当前分支commit。
然后自动生成一个新的 commit
$ git checkout master
Switched to branch 'master'
$ git branch
  bug
  dev
* master

$ git merge bug
Updating 21fd06b..d559613
Fast-forward
 index.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

 

当前分支HEAD 领先于目标commit (nothing to do) 

如果 merge 时当前分支和目标分支的HEAD并不存在分叉,而是 当前分支HEAD 领先于目标分支:你就是最新版本,你还想merge旧版本。

那么 merge 就没必要再创建一个新的 commit 来进行合并操作,因为并没有什么需要合并的。在这种情况下, Git 什么也不会做,merge 是一个空操作。

$ git branch
* master
  zhanggen

$ git merge zhanggen
Already up-to-date.

 

当前分支HEAD落后于目标分支commit(fast-forward)

如果 merge 时当前分支和目标分支的HEAD并不存在分叉,切目标分支的HEAD领先于当前分支:既然别人是先进的,我们就fast-forward(快进)

$ git merge zhanggen
Updating 2b26aee..de6f040
Fast-forward
 src/main.js | 1 +
 1 file changed, 1 insertion(+)

 

合并分支出现冲突

出现原因

我们在master分支 v2阶段创建了dev分支,dev分支引用的就是master v2阶段的代码

然后我又在master分支 v2阶段的代码进行了修改,master开发到了v3阶段。

现在对dev分支和master进行合并,由于dev分支仍对master原来v2阶段的代码进行引用2个不同的分支,同时修改了同1行代码,就会引起冲突。

 

冲突出现时机

从远程仓库pull代码  

向远程仓库push代码 

凡是涉及到2个不同分支合并都有可能出现冲突;

 

git出现冲突现象

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

代码出现冲突现象

上面是master修改的代码,下面是dev分支修改的代码。

解决

手工选择1需要的代码部分,然后生成新的版本

git add .
git commit -m '修复冲突版本'

 

避免冲突的方法

pull操作的本质是将远程仓库中的某1个分支merge到本地的某1个分支中;

push操作的本质是将本地某1个分支merge到远程仓库的1个分支中。

所以以上2个操作,都有可能存在冲突,每1次push之前,先pull以下远程仓库的代码;

 

分支使用场景

 

分支就是帮助我们多个开发,在不同的分支上同时并行向前开发或者紧急修复bug。

 

假设目前有1个项目已经在master分支成熟上线,现需要开发1个新的功能,我创建了dev分支,在dev分支开发新功能时,master分支现有的功能又出现了bug。

如何在不耽误开发新功能的前提下修复一下线上的bug呢?

(1)创建bug 分支

(2)去bug分支修复master分支的bug,修复完成后。

(3)切换到master分支,把bug分支合并到master(master分支的bug修复完成)

(4)切换到dev分支继续开发.....开发完了新功能

(5)然后切回master分支,在master分支合并dev开发的新功能。

(6)最新master和dev分支 合并时肯定会出现冲突。

为什么会出现冲突呢?因为合并时  2条分支交叉点往后的版本中,2条分支均修改了同一行代码。

最新的master分支修改了原来的bug,但是dev分支指针指向的还是原来的就master(没有做任何改动)。这就是2条分支存在冲突的地方。

(7)出现冲突不着急,在master分支手动解决冲突

(8)在master分支  生成新的版本git add . 、git commit -m '解决冲突版本'  (新功能开发完成)

 

四、代码保存在远程代码仓库

多分支解决了团队明确分工的问题,在子分支上开发完成之后,如何把自己负责分支分析给团队其他成员呢?

这就需要1个共同的远程代码仓库;

如果需要跟团队其他成员共享文件,可以将代码push(推送)到远程仓库,也可以通过pull(拉取)得到远程仓库中的最新代码

我们在本地开发的代码(版本、分支信息)如何进行网络传播呢?gitlab、github就是做代码托管的远程仓库,它们和git工具进行了无缝衔接,我们可以使用git命令很方便的把代码(版本、分支信息)上传到远程代码仓库,也可使用git 命令下载到本地,使我们的代码可以通过网络传播。

 

推送本地代码到GitHub 

添加远程仓库地址

$ git remote add origin https://github.com/zhanggen3714/dbhot.git

推送本地master分支到github

$ git push -u origin master

 

本地分支、版本信息都已经提交到github 

 

克隆远程仓库代码

$ git clone  https://github.com/zhanggen3714/dbhot.git

 

所有的分支信息和版本已经拉取到本地

git branch只显示master,但是其他分支也可以通过 git checkout 切换。

$ git branch
  dev
* master

 

从远程仓库分支拉取版本信息

git pull origin dev

 等同于

git fetch origin dev  #从远程仓库 拿来dev到版本库
git merge origin/dev  #然后和本地dev进行合并到工作区 

 

git rebase应用

git 的rebase和merge命令都可以合并2个不同的分支

 

git rebase命令,会让你的提交记录放在目标分支记录的最前面;

 

如果你比较关注代码的历史提交记录,rebase可以让代码提交记录看起来更加简洁。

 

单条分支上的多次commit 记录整合为1次commit记录

有时候我们在本地开发时, git commit了很多次,那么如何把N个版本合并为1个呢?让我们的 commit 提交记录看起来更加简洁呢。

注意不要rebase已经提交到远程仓库的版本。

git rebase -i HEAD~3           #把3条commit 信息合并为1条

 

s标记要合并的版本

squash | meld into

 

填写合并后填写 本次合并commit信息

 

 

合并2条分支

git rebase给分支中文意思为变基-

在dev分支执行 git rebase master,把master分支所有的版本放到当前dev分支版本的下面(让master分支做dev分支的地基

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: 正在dev分支重新rebase 1

 

在master分支把dev分支merge过来把所有代码保存在master分支


$ git branch
  dev
* master
$ git merge dev
Updating 367ca8c..3dacbb6
Fast-forward
 s.py | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 s.py

 

rebase变基完成,使用图形查看git log 

提交记录更加简洁了

$ git log --graph --pretty=format:'%h %s'
*   bbfb548 merge dev to master
|\
| * 3df6d26 dev branch
* | f2a90ad master branch
|/
* 93d4c88 v2
* 58529b9 v1

 

应用3

使用git fetch + git rebase代替 git pull

假设我们 在公司和家2地之间开发,代码通过gitgub远程仓库的dev分支保存(现仓库中有1个a.py的空文件)。

我现在在公司pull下了代码, 打开a.py文件开发了0-50%只生成了版本,忘记push到github。

那么我到家后接着打开a.py开发了其余50-100%的功能,push到了github。

到了公司之后我该怎么办?才能保存a.py文件在公司开发的前0-50%和在家开发的的50-100%代码, 然后还能使代码提交记录看起来简洁呢?

(1)从github拉取远程仓库里的dev分支

git fetch origin dev

从远程代码库拉取分支,并在本地创建。

git fetch origin dev:dev

From githab.com:sre/godos
 * [new branch]      dev        -> dev

 

(2)切换到本地dev分支,让远程仓库的origin/dev分支做本地dev分支的地基。

git checkout dev
git rebase origin/dev

(3)出现冲突

$ git rebase origin/dev
First, rewinding head to replay your work on top of it...
Applying: 在家 dev分支开发v1
error: Failed to merge in the changes.
Using index info to reconstruct a base tree...
M       a.py
Falling back to patching base and 3-way merge...
Auto-merging a.py
CONFLICT (content): Merge conflict in a.py
Patch failed at 0001 在家 dev分支开发v1
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

(4)解决冲突

由于在家和在公司各开发的50%,在a.py的同一部分。所以就会出现冲突,出现冲突就手工解决。

(5) 把修复冲突的文件提交到暂存区

注意与git merge是出现冲突不一样的地方是,merge是出现冲突需要 commit而rebase时出现冲突,只需要 add .

$ git add .

(6)继续rebase生成版本

由于之前出现了冲突,rebase就会被中断,现在手工解决完了冲突,就可以继续 rebase了。

git rebase --continue
Applying: 在家 dev分支开发v1

 

解决冲突的工具

有时候我们进行1次merge/rebase会产生成千上万个冲突,如果逐个文件逐行的去寻找会非常麻烦,所以就需要借助工具。 -Beyond Compare

 配置 beyond compare

$ git config --local merge.tool bc4
$ git config --local mergetool.path 'D:\BeyondCompare4'
$ git config --local mergetool.keepBackup false

 

五、常见git开发模式

我们主要使用2种git开发模式: 多分支开发模式、fork开发模式。

 

1.多分支开发模式

分支开发模式就是你clone线上的代码库到本地PC之后创建自己的分支,在该分支之上进行开发,然后再把新功能所在的分支push到线上。

由代码库Owner在线上对你提交的分支和master分支进行1个合并。

 

2.fork模式

 

 

当我们不是某个代码库A 的项目成员角色(developer)角色时,我们只能查看别人的代码,无法对别人的代码进行修改,但是可以采用fork的开发模式。

其代码库架构如上图

其代码提交流程如下图

1.点击fork/派生等待代码库owner允许你fork他/她的代码。

2.当owner接收你的fork请求之后,就会产生1个和当前代码库一模一样的派生库。

3.我们clone这个派生库到本地进行开发,然后push新功能到派生库 。

4.到代码库A也就是主库发起merge/pull request(PR) ,然后主库的owner就会收到的pull request请求了。如果owner接收了你的pull request那么你的代码就可以提交到线上。

5.如果下次还想提交的话,派生库不会和owner的主库进行实时更新

6.我需要设置upstream为主库,从upstream pull/clone代码到本地,然后push本地代码到派生库(origin),然后再次发起merge/pull request请求。

git remote add upstream 主库的地址
git fetch upstream master 把主库的代码 git pull upstream master下来和本地master进行更新。
git merge upstream/master
------------------------------------------------
然后就可以在本地进行开发然后把本机代码push到派生库,然后在提交pullrequest/合并请求了。
$ git remote -v
origin  https://coding.jd.com/yw/nwdp-frount.git (fetch)
origin  https://coding.jd.com/yw/nwdp-frount.git (push)

$ git remote add upstream https://coding.jd.com/yw/nwdp-frount.git

$ git remote add origin https://coding.jd.com/zhanggen24/nwdp-frount/
fatal: remote origin already exists.

$ git remote rm origin
$ git remote add origin https://coding.jd.com/zhanggen24/nwdp-frount/

$ git push origin master
Username for 'https://coding.jd.com': zhanggen24
error: unable to read askpass response from 'D:/Git/mingw64/libexec/git-core/git-gui--askpass'
Password for 'https://zhanggen24@coding.jd.com':

$ git status
On branch master
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:   src/router/index.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        src/views/TrapAlarm/

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


$ git add src/router/index.js
$ git add src/views/TrapAlarm/
$ git commit -m 'It is first commit of trap alarms for online testing'
[master 4d4683d] It is first commit of trap alarms for online testing
 5 files changed, 1328 insertions(+), 9 deletions(-)
 create mode 100644 src/views/TrapAlarm/alarm_black.vue
 create mode 100644 src/views/TrapAlarm/alarm_config.vue
 create mode 100644 src/views/TrapAlarm/alarm_list.vue
 create mode 100644 src/views/TrapAlarm/alarm_sumary.vue

$ git remote -v
origin  https://coding.jd.com/zhanggen24/nwdp-frount/ (fetch)
origin  https://coding.jd.com/zhanggen24/nwdp-frount/ (push)
upstream        https://coding.jd.com/yw/nwdp-frount.git (fetch)
upstream        https://coding.jd.com/yw/nwdp-frount.git (push)

$ git push origin master
Username for 'https://coding.jd.com': zhanggen24
Counting objects: 11, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (11/11), 10.11 KiB | 0 bytes/s, done.
Total 11 (delta 6), reused 0 (delta 0)
remote: Resolving deltas: 100% (6/6)
remote: Coding: Checking the pushed content...
remote: Updating references: 100% (1/1)
To https://coding.jd.com/zhanggen24/nwdp-frount/
   f2a28bf..4d4683d  master -> master
代码

 

 

 

 

 

 

merge合并

参考

posted on 2017-09-18 16:50  Martin8866  阅读(549)  评论(0编辑  收藏  举报