tools/git/git-banner

Git 简介

Git教程

这篇博文是自己在学习git过程中的思考总结。本文仅仅代表个人的看法,如有不妥地方还请本文文末留言。
😊

相关知识

集中式版本控制系统:CVS、SVN

分布式版本控制系统:GIT

一、Git简介

1.1. Git的诞生
1.2.集中式的vs分布式

原文链接git的学习汇总–原创

安装与配置

安装完成后需要做如下设置:

$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

二、安装Git

GIT是什么

GIT是一个免费并且开源的分布式版本控制系统,能够高速有效的处理或小或大的项目。(以上的话是自己翻译github官网)

至今,自己用过了window系统的TortoiseSVN,
mac系统的CornerStone,最近的大半年也在用GIT(主要管理自己的github项目)。比较下来,还是GIT优势比较明显。

GIT跨平台

GIT可以在不同的操作系统中使用。也许你注意到了,我在window上和mac系统上工作的时候是使用两个不同的svn。如果我在linux上工作会不会又是一个呢。

GIT是分布式版本控制系统,而svn是集中式版本控制系统

集中式版本控制系统是集中放在中央服务器上面的,而团队的人需要从中央服务器上面拉取最新的代码,然后进行开发,最后推送到中央服务器上面,就像串联的电路。而分布式版本控制系统没有中央服务器,团队的每个人的电脑就是一个完整的版本库,就好像并联的电路(自我理解)。

集中式版本控制系统必须联网才能工作,如果是在局域网内还好,带宽足够大,速度足够快,但是遇到网速慢的话,那心里就一万个羊驼🐑在蹦腾了。

集中式版本控制系统安全性比较低,如果中央系统崩溃了,那就有点悲催了。当然你不嫌麻烦,可以定期备份的啦。而分布式中央系统就比较安全,团队的每个成员的电脑就是一个完整的版本库。如果其中一个坏掉了,你可以从团队另外一个的人员电脑那里拷贝一份就行了。对了,GIT也会有一台中央的机子,主要是为了方便团队的交流,它是可以不存在的。

创建版本库

在特定目录中初始化Git仓库:git init

提交修改的步骤:

第一步,添加文件到仓库: git add <file>

第二步,提交文件到仓库: git commit -m "introduce content"

三、创建版本库

GIT安装

GIT支持不同的系统,看者可以在链接https://git-scm.com/downloads中,找到和自己电脑系统匹配的GIT版本,下载安装包后根据提示进行安装。当然,GIT还提供图形界面管理工具,看者也可以在链接中下载GUI Clients,如下图所示–

tools/git/git_install

根据提示安装完成后,要验证是否安装成功。看者可打开命令行工具,输入git --version命令,如果安装成功,控制台输出安装的版本号(当然,安装前就应该输入git
–version查看是否安装了git),我这里安装的GIT版本是2.10.0

版本控制

四、时光穿梭机

4.1版本回退
4.2工作区和暂存区
4.3管理修改
4.4撤销修改
4.5删除文件

GIT配置

GIT在使用前,需要进行相关的配置。每台计算机上面只需要配置一次,程序升级的时候会保留配置信息。当然,看者可以在任何时候再次通过运行命令行来修改它们。

版本回退

git status : 查看仓库当前状态

git diff : 查看仓库改动情况

git log : 查看提交历史,据此可以确定要回退到哪个版本

git reflog : 查看历史命令,据此确定要返回之后的哪个版本

git reset --hard HEAD^
回到上一个版本,上上一个版本可以写为HEAD^^,上100个版本可以写为HEAD~100

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从当前版本指向要回退的版本。

五、远程仓库

5.1添加远程仓库
5.2从远程库中克隆

用户信息

设置GIT的用户名称和邮件地址,这个很重要,因为每个GIT的提交都会使用这些信息,并且它会写入到每一次的提交中。你可以在自己的仓库中使用git log,控制台上面显示的每次的提交都有Author字段,它的值就是用户名称 <邮件地址>。方便查看某次的提交的负责人是谁。

$ git config --global user.name "你的用户名"
$ git config --global user.email 你的邮箱地址 

⚠️
GIT一般和github配合使用,看者应该设置用户名称为你的github用户名。当然,还有和gitlab等配合使用…

⚠️
如果配置中使用了--global选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情,GIT都会使用这些信息。但是,当你想针对特定项目使用不同的用户名称与邮件地址的时候,可以在那个仓库目录下运行不使用global选项的命令来配置。

工作区与版本库

工作区:在电脑里能看到的目录,比如和当前git仓库对应的learngit文件夹就是一个工作区

版本库:工作区里的隐藏目录.git是git的版本库

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

我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。提交之后,并且没有对工作区做任何修改,那么工作区就是“干净”的。

六、分支管理

6.1创建与合并分支
6.2解决冲突
6.3分支管理策略
6.4Bug分支
6.5Feature分支
6.6多人协作

检查配置信息

通过git config --list命令可以列出所有GIT能找到的配置。如下:(我的git版本为2.10.0)

...
user.name=reng99
user.email=1837895991@qq.com
color.ui=true
core.repositoryformatversion=0
core.filemode=true
core.bare=false
...

当然,你可以通过git config <key>来检查GIT的某一项配置。比如$ git config user.name

管理修改

Git跟踪并管理的是修改,而非文件。

每次修改,如果不add到暂存区,那就不会加入到commit中。

七、标签管理

7.1创建标签
7.2操作标签

帮助中心

在使用GIT的时候,遇到问题寻求帮助的时候,可以运行git helpgit --helpgit命令来查看。在控制台上会展示相关的帮助啦。

usage:
    ...
start a working area (see also: git help tutorial)
    ...
work on the current change (see also: git help everyday)
    ...
examine the history and state (see alse: git help revisions)
    ...
grow,mark and tweak your common history
    ...
collaborate (see also: git help workflows)
    ...

更加详细的内容,请点击传送门

撤销修改

git checkout -- file : 撤销file在工作区的修改,分两种情况:

一种是file自修改后还没有被放到暂存区,撤销修改就回到和版本库一样的状态,即回到最近一次git
commmit的状态;

另一种是file添加到暂存区后,又作了修改,撤销修改就回到和添加到暂存区时一样的状态,即回到最近一次git
add时的状态。

撤销修改的三个场景:

场景1:当你改乱了工作区某个文件的内容(未添加到暂存区),想直接丢弃工作区的修改时,用命令git checkout -- file

场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,使用版本回退方法,不过前提是没有推送到远程库。

八、使用GitHub

创建版本库

版本库又名仓库(repository),可以理解成一个目录,这个目录里面所有文件都可以被GIT管理起来,每个文件的修改、删除,GIT都能跟踪,以便任何时刻都能可以追踪历史,或者在将来某个时刻可以还原。

创建一个版本库,首先得选择一个存放目录的地方,我这里选择了桌面,并且创建一个空的目录。

$ cd desktop
$ mkdir -p learngit
$ cd learngit
$ pwd
/Users/reng/desktop/learngit

mkdir -p dirnanme是创建一个子目录,这里的-p确保目录的名称存在,如果目录不存在的就新建一个,如果你确定目录不存在,直接使用mkdir dirname就可以了。pwd(Print Working Directory)是显示当前目录的整个路径名。

然后,通过命令行git init,将创建的目录变成GIT可以管理的仓库:

$ git init 
Initialized empty Git repository in /Users/reng/Desktop/learngit/.git/

初始化好仓库后就可以愉快的玩耍了,但是,得先来了解下GIT整个工作流程先。

删除文件

从版本库中删除该文件,用命令git rm删掉,并且git commit
如果删错了,可以使用 git checkout -- file
将误删的file文件恢复到最新版本。

git checkout
实质上是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以直接恢复。

注意:若出现误删时,只能恢复文件到最新版本,这样会丢失最近一次提交后修改的内容。

九、自定义Git

9.1忽略特殊定义符
9.2配置别名
9.3搭建Git服务器

GIT工作流程

为了更好的学习,自己用Axure RP 8粗略的画了下流程图,如下–

tools/git/git_brief_process

本地仓库(repo)包含工作区和版本库,那么什么是工作区和版本库呢?基本的流程又是什么呢?

远程仓库

添加远程仓库的方法:

第一步,创建SSH Key(若已有SSH Key,可跳过)
$ ssh-keygen -t rsa -C "youremail@example.com"

第二步,在GitHub账户中添加SSH Key
在 Account settings 的 Add SSH Key
选项中填上Title,在Key文本框里粘贴id_rsa.pub文件的内容。

十、总结

1、Git简介

Git是什么?

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

Git有什么特点?简单来说就是:高端大气上档次!

1.1 Git的诞生

你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。

1.2 分布式 VS 集中式

  • 集中式
    SVN – CVS – ClearCase(IBM 收费) – VSS(微软)

  • 分布式
    git – BitKeeper(促使git诞生) – Mercurial – Bazaar

目前集中式常用的SVN, 分布式常用git

集中式

集中式版本控制系统, 版本库需要集中的放到一台服务器上, 在工作时,
用自己的电脑从中央服务器上将目前最新内容下载到本地,
实现效果后再讲内容推送到中央服务器.

  • 缺点

1.中央服务器崩溃, 所有人都干不了活
2.网络带宽会影响文件下载和上传速度

分布式

分布式版本控制系统压根没有”中央服务器”这个东西,
每个人的电脑都是一个完整的版本库, 这样工作的时候并不需要网络. 多人协作,
在合并作品的时候, 只需将自己的内容传给对方, 对方既可以看到你修改的内容.
同时, git提供强大的分支, 可以将项目拆分, 使项目更加安全.

注意

git虽然没有”中央服务器”, 但常常找出一台电脑来充当”中央服务器”,
不过其作用是为了方便交换, 因为, 协作的两个人不在同一个局域网,
或者其中一人电脑未开启等特殊情况.

  • 优点

安全性极高, 即便自己的电脑坏了,
只需要从别人的电脑上重新clone一个版本库即可
强大的分支, 可以将一个大项目拆分成若干小功能来实现, 实现后再合并

工作区和版本库

我们新建一个仓库,就像我们新建的learngit仓库,现在在里面添加一个文件README.md,用sublime打开learngit目录。此时会出现如下图的情况(当然你设置了其他东西例外)–

tools/git/working_version_area

如上图,出现的内容就是工作区(
电脑上能看到的此目录下的内容),这里工作区只有README.md一个文件。工作区有一个隐藏的目录.git,这个不算工作区,而是GIT的版本库。版本库又包括暂存区和GIT仓库。暂存区是一个文件,保存了下次将提交的文件列表信息,而GIT仓库目录是GIT用来保存项目的元数据和对象数据库的地方。这是GIT中最重要的部分,从其他计算机克隆仓库的时候,拷贝的就是这里的数据。当执行git add .或者git add path/to/filename的时候,文件从工作区转到暂存区;执行git commit -m"here is the message described the file you add"的时候,文件从缓存区添加到GIT仓库。

添加远程库

在GitHub上创建仓库后,将本地仓库和远程库关联起来,使用命令:

git remote add origin git@server-name:path/repo-name.git

关联后,使用命令 git push -u origin master
第一次推送master分支的所有内容;此后,每次本地提交后,只要有必要,就可以使用命令
git push origin master 推送最新修改;

安装git(Linux)

1.在终端输入 git 查看git信息(如果没有安装会有下方的友好提示)

$ git
The program 'git' is currently not installed. You can install it by typing:sudo apt-get install git

2.如果没有安装, 在终端输入sudo apt-get install git 安装

注意:

老一点的Debian或Ubuntu
Linux,要把命令改为sudo apt-get install git-core,因为以前有个软件也叫GIT(GNU
Interactive
Tools),结果Git就只能叫git-core了。由于Git名气实在太大,后来就把GNU Interactive Tools改成gnuit,git-core正式改为git。

3、创建版本库

版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

在一个有足够权限的地方创建空文件夹

在终端使用命令:

$git init
git initInitialized empty Git repository in /Users/michael/learngit/.git/

这时git参考就建立好了, 而且是一个空仓库,
此时文件夹下会多一个.git隐藏文件, 它是来跟踪仓库变化的,
不要随意改动这个文件.
将内容放到版本库

1.git add fileName
2.git commit -m “info”

$ git add readme.md
$ git add git_study.md
$ git commit -m "add 2 files."

说明:

git add 告诉 git 我要加入某个文件, git commit告诉 git
内容可以放入版本库了, git commit后的 -m ‘xxx’
参数的意思是这次提交做了哪些改动, 强烈建议写上,
因为用不了多久你就会忘了曾经做了什么. 因为git commit
一次可以提交多个文件, 所以可以先通过add多个文件, 然后commit一次提交.

4、时光机穿梭

  • 在终端使用命令git status查看那些文件被改动,
    以及那些文件将要被commit提交

  • 在终端使用命令git diff查看文件被改动了什么

4.1版本回退

如果在修改文件过程中出现了什么错误, 需要回退到就版本, 怎么办呢?

在终端使用命令git log查看提交历史

commit f384b35e6c614ac5444efd0167e29fee66c9c37d
Merge: c65e7a0 2a0f55c
Author: caoyuan <2675142924@qq.com>
Date:   Wed Sep 21 15:46:03 2016 +0800

    merge with no-ff

commit 2a0f55c64f68abb7cb69547063330821a3a0df39
Author: caoyuan <2675142924@qq.com>
Date:   Wed Sep 21 15:35:31 2016 +0800

    add merge

commit c65e7a0f684587adce36c2f972de7fb4ce53cd69
Merge: 28d8785 71dbe9d
Author: caoyuan <2675142924@qq.com>
Date:   Wed Sep 21 14:58:04 2016 +0800

如果觉得内容太多, 可以使用命令git log –graph –pretty=oneline
–abbrev-commit 查看

yuan@yuan:~/learngit$ git log --pretty=oneline --abbrev-commit
f384b35 merge with no-ff
2a0f55c add merge
c65e7a0 conflict fixed
28d8785 & simple
71dbe9d AND simple
fc62eb2 brach test
4c51d94 remove test.txt
1ee3747 add test.text
5895409 add cao
9a97047 git track changes
440c6b3 understand how stage works
5396aed append GPL
21c867f add 3 file
9c6ce9f add 1 file

说明:
前面的一大串数字是commit id, 和SVN不一样,Git的commit
id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示.

上一个版本就是HEAD,上上一个版本就是HEAD,当然往上100个版本写100个比较容易数不过来,所以写成HEAD~100。

yuan@yuan:~/learngit$ git reset --hard HEAD^
HEAD is now at c65e7a0 conflict fixed

注意:
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。

4.2工作区和暂存区

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

  • 工作区

工作区可以简单的理解为是git仓库目录下内容.

  • 暂存区

回顾一下, 在将内容提交到仓库时候, 需要先使用git add一下,
此时这个add其实是吧内容提交到stage暂存区,
在执行commit的使用才会将stage暂存区中的内容提交到工作区.

git add命令将文件提交到缓存区

图片 1

20160921133228815.jpg

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

先来看名词解释。

基本的工作流

基本的GIT工作流可以简单总结如下–

  1. 在工作区目录中修改文件
  2. 暂存区中暂存文件,将文件的快照放入暂存区域
  3. 提交更新,找到暂存区域的文件,将快照永久性存储到GIT仓库目录

从远程库克隆

要克隆一个仓库,首先必须知道仓库的地址,然后使用 git clone 命令克隆。

Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。

工作区(Working Directory)

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

working-dir

时光机穿梭

到目前为止,在自己创建的本地仓库–learngit中已经初具形态了。进入learngit,执行ls,可看到目前仓库中已有的文件README.md。

$ cd desktop/learngit
$ ls
README.md
$ cat README.md
## content

上面展示了本地learngit内的相关的内容。运行下git status查看现在的状态。

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

这时候会提示没有内容可以提交,工作区是干净的。因为我之前已经提交(git
commit)过了。上面还提示了目前是位于主分支上面,GIT在初始化(git
init)的时候会自动创建一个HEAD指针指向默认master分支,也只有一个分支,看者可以通过git branch查看。

现在,在README.md上添加一些内容。

## content

### first change

此刻再通过git status查看当前状态。

$ 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:   README.md

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

这时候显示出一堆的东西,告诉我们现在是位于主分支上面,然后告诉我们修改的文件啊,可以使用的命令进行下一步的操纵。那么我们来进行下一步的操作了,git add . 或者 git add README.md将修改的文件添加到暂存区域。

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

    modified:   README.md

对了,有时候需要在添加的之前(执行git add . 或者 git add
path/to/filename)的时候,需要看下修改了哪些内容可以执行下git diff。那么,现在先回退到修改的前一个版本。

$ git reset HEAD README.md
Unstaged changes after reset:
M   README.md
$ git checkout -- README.md
$ ls
README.md
$ cat README.md
## content

回退正确,现在像上次那样添加内容### first change,然后执行命令git diff来查看更改的内容。

$ git diff
diff --git a/README.md b/README.md
index 75759ec..0bc52b9 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-## content
\ No newline at end of file
+## content
+
+### first change
\ No newline at end of file

现在就显示了修改前的内容–-前为修改前的内容,和修改后的内容–+前修改后的内容。查看完之后,觉得没有问题了,就可以进行添加(git
add),提交(git commit)。当然,一般不常用git
diff的,因为自己修改的东西自己心里总有点数吧,可能合作中团队的其他人需要查看文件前后的不同点就需要用到git diff啦。

分支管理

在Git里,Head严格来说不是指向提交,而是指向分支,分支才是指向提交,Head指向的是当前分支。

在Git里主分支叫做master,Git鼓励创建分支,在新创建的分支上工作,再将该分支合并到主分支master上,最后可将创建的分支删掉。

版本库(Repository)

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

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

git-repo

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

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

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

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

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

俗话说,实践出真知。现在,我们再练习一遍,先对readme.txt做个修改,比如加上一行内容:

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.

然后,在工作区新增一个LICENSE文本文件(内容随便写)。

先用git status查看一下状态:

$ 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:   readme.txt

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

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

Git非常清楚地告诉我们,readme.txt被修改了,而LICENSE还从来没有被添加过,所以它的状态是Untracked。

现在,使用两次命令git add,把readme.txt和LICENSE都添加后,用git
status再查看一下:

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

 new file:   LICENSE
 modified:   readme.txt

现在,暂存区的状态就变成这样了:

git-stage

所以,git
add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git
commit就可以一次性把暂存区的所有修改提交到分支。

$ git commit -m "understand how stage works"
[master 27c9860] understand how stage works
 2 files changed, 675 insertions(+)
 create mode 100644 LICENSE

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

$ git status
# On branch master
nothing to commit (working directory clean)

现在版本库变成了这样,暂存区就没有任何内容了:

git-stage-after-commit

版本回退

为了方便讲解下版本回退,我先将上面添加的### first change提交以下–git add . && git commit -m "add first change"。下面通过git log就可以查看自己提交的记录了。

$ git log
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date:   Sun Dec 17 17:11:53 2017 +0800

    init README.md

$ git add . && git commit -m "add first change"
[master 0ac49ba] add first change
 1 file changed, 3 insertions(+), 1 deletion(-)

$ git log
commit 0ac49bae6ab55df9c05d0770de347665a2568f31
Author: reng99 <1837895991@qq.com>
Date:   Mon Dec 18 15:26:06 2017 +0800

    add first change

commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date:   Sun Dec 17 17:11:53 2017 +0800

    init README.md

在上面中,自己先执行了git log来显示提交的日志,显示只有一条,然后执行了add和commit的命令,打印的内容是现实主分支、commit的id、commit的信息、多少个文件的更改、多少个插入以及多少个删除。之后再次执行git log打印日志,显示了两次提交。⚠️
注意:当提交(commit)的次数较多之后,控制台会显示不下(最多现实4条)那么多的条数,可以通过按键盘的向上或向下键查看日志的内容,需要退出查看日志命令的话,在英文输入法的状态按下q,意思就是quit(退出)。

版本的回退就是改变HEAD指针的指向。通过git reset --hard HEAD^返回上一个版本,通过git reset --hard HEAD^^返回上上个版本…由此推论,往上100个版本的话就是100个^,当然,这样你数到明天也未必数得正确,所以写成git reset --hard HEAD~100。另外一种是,你知道提交的id,例如commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67的前7位就是commit的id(5c2639e),执行git reset --hard 5c2639e就回到此版本啦。

$ reng$ git reset --hard HEAD^
HEAD is now at 5c2639e init README.md
$ git log
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date:   Sun Dec 17 17:11:53 2017 +0800

    init README.md
$ ls
README.md
$ cat README.md
## content

现在你已经回到了最初的版本,这里演示的是通过HEAD,你也可以通过commit id来实现的。执行上面的代码后,README.md文件里面只有一### content文字内容,但是过了段时间后,你想恢复到原先的版本,通过git log命令行,控制台显示的以前的信息,通过它找不到回退前的commit id,怎么办?GIT提供一个git reflog显示提交的历史记录,在那里可以查看提交的id、HEAD的指针历史和操作的信息记录。下面演示回退到最新的版本(也就是commit
-m “add first change”)–

$ git log
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date:   Sun Dec 17 17:11:53 2017 +0800

    init README.md
$ git reflog
5c2639e HEAD@{0}: reset: moving to HEAD^
0ac49ba HEAD@{1}: commit: add first change
5c2639e HEAD@{2}: commit (initial): init README.md
$ git reset --hard 0ac49ba
HEAD is now at 0ac49ba add first change
$ ls
README.md
$ cat README.md
## content

### first 

现在又回到了最新的版本,又能够愉快的玩耍了。😊

创建和合并分支

查看分支:git branch

创建分支:git branch <name>

切换分支:git checkout <name>

创建+切换分支:git checkout -b <name>

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

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

小结

暂存区是Git非常重要的概念,弄明白了暂存区,就弄明白了Git的很多操作到底干了什么。

没弄明白暂存区是怎么回事的童鞋,请向上滚动页面,再看一次。

4.3 git撤销修改

如果在reqdme.txt中多加了一行,在提交前,我们要撤销修改

删除添加的行,返回到上一个历史版本。

例1:

  yuan@yuan:~/learngit$ 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:   readme.txt
    no changes added to commit (use "git add" and/or "git commit -a")

执行git status后git会提醒git checkout -- file

git checkout -- file 可以丢弃工作区的修改

文件在工作区的修改全部撤销

修改后还没被放到暂存区

撤销修改就回到和版本库一模一样状态

修改后已经添加到暂存区

又作了修改,现在,撤销修改就回到添加到暂存区后的状态

git commit  
git add
git checkout -- file 

命令中的 –的作用

没用– 就变成切换到另一分支命令

git checkout命令

在commit之前发现这个问题,git status
修改只是添加到了暂存区,还没有提交:

```
yuan@yuan:~/learngit$ vim 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.
dddd cao yuan
i love you
```

通过git diff可以看到里面添加了一个 I love you的内容

    yuan@yuan:~/learngit$ git diff
    diff --git a/readme.txt b/readme.txt
    index 5d1ed32..fcb9573 100644
    --- a/readme.txt
    +++ b/readme.txt
    @@ -3,3 +3,4 @@ Git is free software distributed under the GPL.
     Git has a mutable index called stage.
     Git tracks changes.
     dddd cao yuan
    +i love you^M   //此行是添加的内容,可以看到前面有一个+号

2、我们现在把readme.txt添加到暂存区

    yuan@yuan:~/learngit$ git add readme.txt  //把readme.txt添加到暂存区
    yuan@yuan:~/learngit$ git status              //查看状态
    On branch master
    Changes to be committed: 
    (use "git reset HEAD <file>..." to unstage) //提示撤销暂存区修改
    modified:   readme.txt                     //提示修改reqdme.txt

    yuan@yuan:~/learngit$ git reset HEAD readme.txt //撤销暂存区修改,重新放回工作区
    Unstaged changes after reset:
    M   readme.txt

管理修改

GIT比其他版本控制系统设计优秀,其中一点是–GIT跟踪并管理的是修改,而非文件。

下面在README.md内添加信息### second change。之后看下变化后的文件的状态和差异等。

$ ls
README.md
$ cat README.md
## content

### first change

#### second change
$ 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:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
$ git add README.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   README.md

此时,对README.md进行第三次的修改,添加内容### third change

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

    modified:   README.md

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.md

$ cat README.md
## content

### first change

#### second change

### third change
$ git commit -m "test file modify"
[master 18f86ba] test file modify
 1 file changed, 3 insertions(+), 1 deletion(-)
$ 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:   README.md

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

上面的演示流程是这样的第一次修改(#### second change) -> git add -> 第二次修改(### third change) -> git commit。但是最后查看状态的时候(git
status),第二次的修改并没有被提交上去。因为GIT管理的是修改,当使用git add命令的时候,在工作区的第一次修改被放入暂存区,准备提交,但是在工作区的第二次修改并没有放入到暂存区,而git commit是将暂存区的修改提交到GIT仓库,所以第二次修改的内容是不会被提交的。这也是说明为什么可以多次添加(git
add),一次提交(git commit)的原因了。

解决冲突

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,才能完成合并。

git log --graph命令可以看到分支合并图。

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

git log --graph --pretty=oneline --abbrev-commit

查看状态,当前已前暂存区回到的工作区

 yuan@yuan:~/learngit$ 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:   readme.txt
    no changes added to commit (use "git add" and/or "git commit -a") //

撤销修改

文件的撤销修改分成三种情况,一种是修改在工作区的内容,一种是修改在暂存区的内容,另一种是修改在GIT仓库的内容。也许会有看者说,不能修改在远程库中的内容吗?有啊,就是git add->git commit->git push将远程仓库的内容覆盖被,不过团队人在克隆远程库下来的时候,还是可以查看到你提交的错误内容的。我们现在只针对本地仓库的三种情况谈下自己的看法–

情况一:撤销工作区的内容

在管理修改中,自己的工作区还是没有提交,此时想放弃当前工作区的编辑内容执行git checkout -- file。接着上面的内容,我这里的工作区内有的内容是### third change,现在我要放弃第三次修改,只要执行git checkout -- README.md就可以了。

$ 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:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
$ ls
README.md
$ cat README.md
## content

### first change

#### second change

### third change
$ git checkout -- README.md
$ cat README.md
## content

### first change

#### second change
$ git status
On branch master
nothing to commit, working tree clean

情况二:撤销暂存区的内容

当你不但改乱了工作区的某个文件的内容,还添加(git
add)到了暂存区时,想丢弃修改,那么得分两步来撤销文件。先是通过git reset HEAD file,将暂存区的文件退回到工作区,然后通过git checkout -- file放弃修改改文件的内容。为了方便演示,我这里的暂存区没什么内容,所以添加内容### tentative content并将它添加到缓存区。之后,演示将缓存区的内容撤回–

$ cat README.md
## content

### first change

#### second change

### tentative content
$ git add .
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   README.md

$ git reset HEAD README.md
Unstaged changes after reset:
M   README.md
$ git checkout -- README.md
$ cat README.md
## content

### first change

#### second change
$ git status
On branch master
nothing to commit, working tree clean

情况三:撤销GIT仓库的内容

如果你不仅添加(git add)了内容到暂存区并且提交(git
commit)了内容到GIT仓库中了。你需要撤销上一次的内容,也就是要回退到上一个版本,执行git reset --hard HEAD^就可以啦,详细的内容查看版本回退。如下–

$ git status
On branch master
nothing to commit, working tree clean
$ cat README.md
## content

### first change

#### second change
$ git reset --hard HEAD^
HEAD is now at 0ac49ba add first change
$ cat READMEmd
## content

### first change

分支管理策略

合并分支时,加上 --no-ff
参数就可以用普通模式合并,合并后的历史有分支,

能看出来曾经做过合并,而 fast forward 合并就看不出来曾经做过合并。

再从工作区撤销命令,看到结果中没有i love you字母了

    yuan@yuan:~/learngit$ git checkout -- readme.txt   //撤销工作区修改
    yuan@yuan:~/learngit$ cat readme.txt               //查看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.
    cao                                           

3、如果从暂存区提交到了版本库,只能用前面讲到的版本回退操作

前提是没有把本地版本库推送到远程,否则无法撤销。

总结:

1、修改内容有误时了,但没有提交到暂存区 直接使用 git checkout — file

2、修改某文件内容有误后,提交到暂存区想撤销修改分如下2步

  • 第一步: 先让文件回到工作区 git rest HEAD file

  • 第二步:从工作区撤销修改 git checkout — file

3、已经提交了修改内容到版本库时,想要撤销本次提交只能用版本回退,前提没有推送到远程库

4.4 删除文件

1、添加一个test.txt新文件

```
yuan@yuan:~/learngit$ vim test.txt
yuan@yuan:~/learngit$ ls      //ls查看到当前目录下多了一个test.txt文件
LICENSE.txt  readme.txt  test.txt
```

2、把test文件提交到暂存区

```
yuan@yuan:~/learngit$ git add test.txt               //把test添加到暂存区
yuan@yuan:~/learngit$ git commit -m "add test.text"  //把test添加到本地库
[master 1ee3747] add test.text
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test.txt
```

3、删除test.txt文件

yuan@yuan:~/learngit$ rm test.txt

删除后Git知道你删除了文件,因此,工作区和版本库就不一致了

4、git status查看状态

    yuan@yuan:~/learngit$ git status
    On branch master
    Changes not staged for commit:
    (use "git add/rm <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    deleted:    test.txt                        //提示文件被删除
    no changes added to commit (use "git add" and/or "git commit -a")

5、是否确认删除文件

如果删除文件执行git rm test.txt 删掉,并且git commit

    yuan@yuan:~/learngit$ git rm test.txt
   rm 'test.txt'
   yuan@yuan:~/learngit$ git commit -m "remove test.txt"
   [master 4c51d94] remove test.txt
    1 file changed, 0 insertions(+), 0 deletions(-)
    delete mode 100644 test.txt

如果不小心删错了用git checkout — test.txt 就可以找回来

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

总结:

1、执行git rm file 删除文件

2、只要提交到版本库不用提心误删除,但只能恢复最版本。

5、远程仓库

远程仓库

远程仓库的使用能够提高你和团队的工作效率,无论何时何地,团队的人员都可以在联网的情况下将代码进行拉取,修改和更新。因为我是使用github来管理项目的,所以我的远程仓库是放在github里面。这里默认看者已经安装了github,当然也可以用码云、gitlab等。

Bug分支

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场 git stash
一下,修复bug后,可以再回到工作现场。

工作现场可以用 git stash list 查看

恢复工作现场有两种方法:

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

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

1、时光穿梭可恢复历史版本

本地库添加到远程库

这点很容易,登录自己注册的github,如果打不开,请开下VPN。进入自己的首页(https://github.com/username),点击+号创建(new
repository)一个名为learngit的仓库(注意哦⚠️
名称是本地仓库已经初始化过的,我这里本地有个同名初始化的learngit仓库),其他的字段自选来填写。点击Create repository创建此远程仓库。紧接着就是进行本地仓库和远程仓库的关联啦,github很友好的提示了你怎么进行一个远程仓库的关联。

tools/git/related_github_step1

tools/git/related_github_step2

tools/git/related_github_step3

现在按照上图来关联下远程仓库。

$ git remote add origin https://github.com/reng99/learngit.git
$ git push -u origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 456 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

注意⚠️
第一次向远程仓库(关联)push的时候是$ git push -u origin master,不能忽略-u,以后的push不用带-u。至此,打开你的github的相关的仓库就可以看到添加了README.md文件,我这里地址是https://github.com/reng99/learngit,因为我是使用markdown语法写的,控制台显示的内容和仓库的显示内容有所区别啦。<del>(⚠️
后期我将learngit仓库删除啦,所以你访问链接是找不到这个仓库的,毕竟不想放一个没什么内容的仓库在我的github上)</del>。

Feature分支

开发一个新feature,最好新建一个分支;

如果要丢弃一个没有被合并过的分支,可以通过 git branch -D \<name\>
强行删除。

2、git分布式版本控制系统

同一个Git仓库,可以分布到不同的机器上。

从版本库机器上克隆原始版本库,每台机器都一样

远程库克隆到本地

从远程仓库克隆东西到本地同样很简单,只需要进入你想克隆的仓库,将仓库的url复制下来(当然你也可以复制window.location.href的内容),运行git clone address。现在我将本地桌面的learngit的仓库删除,然后从远程将learngit克隆到本地。

tools/git/git_clone_repository

$ cd desktop
$ rm -rf learngit
$ find learngit
find: learngit: No such file or directory
$ git clone https://github.com/reng99/learngit
Cloning into 'learngit'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), done.

成功将gitlearn从远程克隆下来,接下来又可以愉快的玩耍啦。

多人协作

多人协作的工作模式通常是这样:

1.首先,可以试图用 git push origin branch-name 推送自己的修改;

2.如果推送失败,则因为远程分支比你的本地更新,需要先用 git pull
试图合并;

3.如果合并有冲突,则解决冲突,并在本地提交;

4.没有冲突或者解决掉冲突后,再用 git push origin branch-name
推送就能成功

如果 git pull 提示 “no tracking information”
,则说明本地分支和远程分支的链接关系没有创建,用命令
git branch --set-upstream branch-name origin/branch-name

查看远程库信息,使用git remote -v

本地新建的分支如果不推送到远程,对其他人就是不可见的;

从本地推送分支,使用 git push origin branch-name ,如果推送失败,先用
git pull 抓取远程的新提交;

在本地创建和远程分支对应的分支,使用
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致;

建立本地分支和远程分支的关联,使用
git branch --set-upstream branch-name origin/branch-name

从远程抓取分支,使用 git pull ,如果有冲突,要先处理冲突。

3、通过一台电脑玩远程库

一台电脑上可以克隆多个版本库,只要不在同一个目录下

分支管理

分支管理允许创建另一条线/方向上开发,能够让你在不影响他人工作的情况下,正常的工作。当在自己创建的分支中完成自己的功过后,合并到主分支就行了(git
init初始化的时候已经默认创建了master主分支)。一般团队的合作是不在主分支上进行的,个人项目除外(个人理解)。

标签管理

Git的标签是版本库的快照,实质上它是指向某个commit的指针(跟分支很像,但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。

tag是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。

4、多台电脑玩远程库

一台电脑当服务器用,其它的电脑从服务器中克隆一份到电脑上,并把各自的提交到服务器仓库,也

可从服务器拉取最新版本

创建分支

当前learngit仓库上只有一个分支,那就是master分支,看者可以通过git branch命令来查看当前的分支,git branch branchName命令来创建一个新的分支,我这里创建的是dev分支。

$ cd desktop/learngit
$ git branch
* master
$ git branch dev
$ git branch
  dev
* master

现在已经创建了dev分支,有两个分支了,分支前面带有一个星号的分支说明是当前的正在工作的分区。执行上面的分支后,可以简单的画下现在的情况了,有个HEAD指针指向主分支的最新点,刚才新创建的dev分支我这里默认是一个dev的指针指向了dev分支的最新点。

.
.       HEAD指针
.       │
├────────*master
└────────dev
        │   
        dev指针   

创建标签

git tag <name> 用于新建一个标签,默认为HEAD,也可以指定一个commit id;

git tag -a <tagname> -m "blablabla..." 可以指定标签信息;

git tag -s <tagname> -m "blablabla..." 可以用PGP签名标签;

git tag 可以查看所有标签。

5、GitHub网站提供版本托管服务

只要注册一个GitHub帐号,就可免费获得Git远程仓库,GitHub通过SSH加密传输

第一步:创建SSH Key

查看用户主目录下是否有.ssh目录,有再看看有没有id_rsaid_rsa.pub这两个文件如果有跳过

没有就创建SSH Key,执行完.ssh目录下生成 id_rsa和id_rsa.pub两个密钥文件,

id_rsa私钥,不可泄露,id_rsa.pub是公钥可以告诉别人

ssh-keygen -t rsa -C "2675142924@qq.com"  //更换自己的邮件地址

第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:

然后,点“Add SSH
Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:
点“Add Key”,你就应该看到已经添加的Key:

总结:

1、Github 有公钥就可以确认只有你自己推送

2、允许添加多个key,多台电脑,只要把每台要访问的电脑都加到GitHub中,就可以每台推送到Github中

3、GitHub免费托管的Git仓库,任何人都可以看到,但只有自己可以修改,所以重要信息不能放进去

4、如果不想让别人看到放在GitHub中第一交点钱,把公开的变成私有的,别人就看不见了,再一个就是自己

搭建一个Git服务器,自己的服务器别人就看不到

5.1添加远程库

1、创建本地Git仓库,GitHub创建一个Git仓库

2、让本地仓库与远程仓库进行远程同步,可以作为备份

3、登录GitHub右上角Create a new repo创建一个新库

Respository name 填learngit 点击Create repository

4、本地库与远程库关联

yuan@yuan:~/learngit$ git remote add origin 2675142924@qq.com:michaelliao/learngit.git

5、本地所有内容添加到远程库上

yuan@yuan:~/learngit$ git push -u origin master

用git push命令,实际上是把当前分支master推送到远程

第一次推送master加上-u参数

本地master分支内容添加到远程新的master分支内容
还会把本地的master分支与远程master分支关联起来,方便以后推送

以后推送就可以通过以下命令

    git push origin master

第一次使用Git的clone或者push命令连接GitHub时会有警告

The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?

提示确认GitHub的Key的指纹信服务息是否真的来自GitHub服务器,输入Yes即可

Warning: Permanently added 'github.com' (RSA) to the list of known hosts.

警告出现一次,后面的操作就不会有任何警告了

总结:

1、关联远程库使用命令

git remote add origin 2675142924@qq.com:path/repo-name.git

2、第一次推送分支所有内容:git push -u origin master

3、以后每次提次只需输入命令:git push origin master 推送最新修改

6.3分支管理策略

合并分支时,Git会用Fast forwards模式,这种模式,删除分支后会丢掉信息

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

–no–ff方式的git merge

1、创建并切换dev分支:

yuan@yuan:~/learngit$ git checkout -b dev
Switched to a new branch 'dev'

2、修改readme.txt,提交一个新的add commit

yuan@yuan:~/learngit$ git add readme.txt
yuan@yuan:~/learngit$ git commit -m "add merge"
[dev 2a0f55c] add merge
 1 file changed, 1 insertion(+), 5 deletions(-)

3、切换回master分支

yuan@yuan:~/learngit$ git branch
* dev
  master
yuan@yuan:~/learngit$ git checkout master
Switched to branch 'master'
yuan@yuan:~/learngit$ git branch
  dev
* master

4、合并dev分支,请注意–no-ff参数,表示禁用Fast forward:

yuan@yuan:~/learngit$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。

5、查看分支历史git log:

yuan@yuan:~/learngit$ git log --graph --pretty=oneline --abbrev-commit  //查看分支

*   f384b35 merge with no-ff
|\  
| * 2a0f55c add merge
|/  
*   c65e7a0 conflict fixed
|\  
| * 71dbe9d AND simple
* | 28d8785 & simple
|/  
* fc62eb2 brach test
* 4c51d94 remove test.txt
……

可以看到,不使用Fast forward模式,merge后就像这样:

6、分支策略

管理分支原则

master分支非常稳定,仅用来发布新版本,平时不在上面干活

干活一般都在dev分支上,dev分支是不稳定的,到某个时候,比如1.0版本发布,再把dev分支合并到master上,在master分支发布1.0版本

每个人都在dev分支上干活,每个人都可以有自己的分支,时不时地往dev分支上合并就可以了

团队合作的支支看起来就像这样

总结

1、合并分支时,加上–no-ff参数就可以用普通模式合并

2、合并后的历史有分支,能看出来曾经做过合并

3、fast forward合并就看不出来曾经做过合并。

6.3Bug分支

开发中Bug修复办法

yuan@yuan:~/learngit$ git status
On branch master
nothing to commit, working directory clean

在Git中分支功能比较强大,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

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

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

yuan@yuan:~/learngit$ git stash
Saved working directory and index state WIP on dev: f384b35 merge with no-ff
HEAD is now at f384b35 merge with no-ff

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

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

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
$ git checkout -b issue-101
Switched to a new branch 'issue-101'

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

yuan@yuan:~/learngit$ git commit -m "fix bug 101"
On branch master
nothing to commit, working directory clean

yuan@yuan:~/learngit$ git commit -m "fix bug 101"
[master 32614a1] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

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

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 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(-)
$ git branch -d issue-101
Deleted branch issue-101 (was cc17032).

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

$ git checkout dev
Switched to branch 'dev'
$ git status
# On branch dev
nothing to commit (working directory clean)

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

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

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

yuan@yuan:~/learngit$ git stash pop
On branch issue-101
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

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (a3c15cae9ce0df80247abf06a3462f3d7a44a1d0)

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

yuan@yuan:~/learngit$ git stash list

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

git stash apply stash@{0}

总结

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场git
stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。

6.4Feature分支

软件开发中,总要不断添加进来,添加新功能时。

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

yuan@yuan:~/learngit$ git checkout -b feature-vulcan
M   readme.txt
Switched to a new branch 'feature-vulcan'

5分钟后,开发完毕:

yuan@yuan:~/learngit$ git add vulcan.py
yuan@yuan:~/learngit$ git commit -m "add feature vulcan"
[feature-vulcan afb809a] add feature vulcan
 1 file changed, 1 insertion(+)
 create mode 100644 vulcan.py

切回dev

切换分支

我们一般是很少在主分支进行工作的,所以在创建出新的分支之后,我们就切换到新的分支进行相关的工作。可以通过git checkout branchName切换到已经存在的分支工作,通过分支前面的*可查看目前位于哪个分支内。现在我切换到创建的dev分支。

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

操作标签

git push origin <tagname> : 可以推送一个本地标签;

git push origin --tags : 可以推送全部未推送过的本地标签;

git tag -d <tagname> : 可以删除一个本地标签;

git push origin :refs/tags/<tagname> : 可以删除一个远程标签。

准备合并:

yuan@yuan:~/learngit$ git checkout dev

合并分支

在创建好分支后,我们在新的分支上工作完成后,就需要往主分支上进行合并啦。我修改了分支dev上的README.md的内容,就是添加文字### new branch content。合并分支可以分成两个合并的方式,一种是本地合并到materz主分支之后,推送(push)到远程库,一种是直接将分支推送到远程库,在远程库进行合并。

本地合并推送

在合并分支前,需要切换到要合并到哪个分支(一般是master主分支),通过git merge branchName将需要的合并的分支合并到当前分支,我是将dev分支合并到master分支。

$ git branch
* dev
  master
$ git checkout master
M   README.md
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge dev
$ Already up-to-date.
$ git add .
$ git commit -m "merge dev branch"
[master d705e73] merge dev branch
 1 file changed, 3 insertions(+), 1 deletion(-)
$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 282 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit
   0ac49ba..d705e73  master -> master

合并之后,此时,HEAD指针就指向了dev指针,也就是两者同时指向了master主分支的最新处。具体的内容参考传送门

.
.           
.           
├────────*master
└────────dev
        │   
        dev指针    ── HEAD指针

远程库推送合并

远程库内合并的话,要先将dev的分支推送到远程库,然后在远程库进行合并。我这里在dev分支上添加了### add new branch content into again然后demo演示推送(git
push origin dev)以及合并。

$ git branch
  dev
* master
$ git checkout dev
Switched to branch 'dev'
$ git add .
$ git commit -m "add dev branch commit again"
[dev dc817c4] add dev branch commit again
 1 file changed, 3 insertions(+), 1 deletion(-)
$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 300 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit
 * [new branch]      dev -> dev

接下来就是进入我的远程learngit仓库进行合并,你会看到下面图示的提示。点击Compare && pull request,然后写点相关的comment(选填),点击Create pull request。之后在绿色勾的提示下Merge pull request,紧接着点击Confirm merge按钮确定合并此分支,这时候返回主分支就可以看到dev内合并的内容了(后期我改动了dev的内容)。看者如果看得不明白,自己上手尝试一下呗!

tools/git/merge_branch

完成后,你会看到learngit仓库的Pull requests量为1,branches量为2。你可以点击进入分支,在ALL branches里面查看分支的具体内容。

自定义Git

让Git显示颜色 : git config --global color.ui true

feature分支和bug分支合并,然后删除。

但是,
就在此时,接到上级命令,因经费不足,新功能必须取消!
虽然白干了,但是这个分支还是必须就地销毁:

yuan@yuan:~/learngit$ 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
分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用命令git
branch -D feature-vulcan

现在我们强行删除:

yuan@yuan:~/learngit$ 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'.

终于删除成功!

6.5多人协作

多人协作, 往往要用到远程仓库,
那么要如何解决与远程仓库之间的同步以及和伙伴代码合并的问题呢?
和远程仓库同步

当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。

可是使用git remote查看远程仓库的信息

$ git remote
origin

或者,使用git remote -v查看详细的信息

$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上,
比如下面这句就是吧本地的master分支推送到远程的master分支上:

$ git push origin master

但是并不是所有分支都需要推送, 像一些大家都需要用到的诸如:
master(主分支), dev(开发分支)等这些需要推送,
像一些只是自己完成任务的独立分支, 类似:bug(修改bug的分支)就不用推送,
除非你的老板想知道你都修改了哪些bug.

抓取分支

多人协作时,大家都会往master和dev分支上推送各自的修改。

当你的协作伙伴从远程clone时, 他只能看到master分支,
如果想看到dev等其他分支,
,就必须创建远程origin的dev分支到本地,可以使用这个命令创建本地dev分支:

$ git checkout -b dev origin/dev

此后, 你的伙伴就可以在分支上做修改了, 并且会提交修改,
如果他提交后(此时有个问题: 你手里的版本已经不是最新版本了),
你同样做好了修改需要提交, 这时当你pull时, 你会发现报错,
因为你和你的伙伴可能修改了同样的文件, 这就和前面分支合并一样,
出现了冲突. 解决办法也很简单,Git已经提示我们,就是先用git
pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送(这时可能会git
pull失败,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接).

创建分支链接

$ git branch --set-upstream dev origin/dev

然后pull到本地

$ git pull

这时, 在本地对伙伴修改的内容和自己修改的内容进行合并, 会有合并冲突,
需要手动解决, 解决后在推送.

注意:

多人协作的工作模式通常是这样:

1、可以试图用git push origin branch-name推送自己的修改;
2、如果推送失败,则因为远程分支比你的本地更新,需要先用git
pull试图合并;
3、如果合并有冲突,则解决冲突,并在本地提交;
4、没有冲突或者解决掉冲突后,再用git push origin
branch-name推送就能成功!
5、如果git pull提示“no tracking
information”,则说明本地分支和远程分支的链接关系没有创建,用命令git
branch –set-upstream branch-name origin/branch-name。

说明:

  • 查看远程库信息,使用git remote -v;
  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;
  • 从本地推送分支,使用git push origin
    branch-name,如果推送失败,先用git pull抓取远程的新提交;
  • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name
    origin/branch-name,本地和远程分支的名称最好一致;
  • 建立本地分支和远程分支的关联,使用git branch –set-upstream
    branch-name origin/branch-name;
  • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

7、标签管理

标签(tag), 一个简单的理解就是它记录了一次commit id,
tag一般建立在主分支上, 方便用于版本发布. 标签和分支类似,
一个指针指向某次提交, 只不过分支可以继续先前移动, 而标签不可移动.

7.1创建标签

在创建标签前需要先切换到需要打标签的分支上, 然后使用命令 git tag v1.0
来创建标签. 默认标签是打在HEAD指向的地方(即最新提交),
如果想把标签创建在先前的提交上, 需要先知道commit id(用命令git log
–pretty oneline –abbrev-commit), 然后用命令 git tag v0.9 6224937
在对应的提交上打上标签, 可以使用 git tag 查看所有标签, 使用命令 git show
v0.9 来查看标签详细信息.

还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:

$ git tag -a v0.1 -m "version 0.1 released" 3628164

还可以通过-s用私钥签名一个标签:

$ git tag -s v0.2 -m "signed version 0.2 released" fec145a

注意:

签名采用PGP签名,因此,必须首先安装gpg(GnuPG),如果没有找到gpg,或者没有gpg密钥对,就会报错

7.2操作标签

如果标签打错了,也可以删除:

$ git tag -d v0.1

如果要推送某个标签到远程,使用命令git push origin

$ git push origin v1.0

或者,一次性推送全部尚未推送到远程的本地标签:

$ git push origin --tags

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:

$ git tag -d v0.9

然后,从远程删除。删除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9

8、使用GitHub

  • 在GitHub上,可以任意Fork开源仓库;
  • 自己拥有Fork后的仓库的读写权限;
  • 可以推送pull request给官方仓库来贡献代码。

9、自定义Git

  • 让Git显示颜色,会让命令输出看起来更醒目:

$ git config --global color.ui true

8.1忽略特殊文件

在工作目录下创建.gitignore文件, 在这个文件中填写需要忽略的文件内容,
然后git add进去. 此后这个.gitignore就开始起作用了.

忽略文件的原则是:

  1. 忽略操作系统自动生成的文件,比如缩略图等;

  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;

  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

如果有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了.

如果你确实想添加该文件,可以用-f强制添加到Git:

$ git add -f App.class

8.2配置别名

通过以下命令将status 缩写成st

$ git config --global alias.st status

8.3搭建Git服务器

搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt命令就可以完成安装。

假设你已经有sudo权限的用户账号,下面,正式开始安装。

1、安装git

yuan@yuan:~$ sudo apt-get install git

2、创建一个git用户,用来运行git服务

$ sudo adduser git

3、创建证书登录

收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。

4、初始化Git仓库:

先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:

$ sudo git init --bare sample.git

Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:

$ sudo chown -R git:git sample.git

5、禁用shell登录:

出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:

git:x:1001:1001:,,,:/home/git:/bin/bash

改为:

git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。

6、克隆远程仓库:

现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:

$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.

剩下的推送就简单了。

管理公钥

如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。

这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。

管理权限

有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。

删除分支

在创建了分支,然后将分支的内容合并到主分支后,分支的使命就完成了,你就可以将分支删除了,这里的删除个人认为可以是两种,一种是本地仓库的分支删除,一种是远程仓库的分支的删除。当然啦,留着分支也没啥,可以留着呗<del>,自己认为有点碍眼</del>。

本地分支的删除

在本地的learngit的目录下,执行命令行git branch -D branchName就可以删除了。我这里删除的是dev分支。注意⚠️
,删除的分支不应该是当前工作的分支,需要切换到其他分支,我这里切换的是master分支,毕竟我只有两个分支呢。

$ git branch
* dev
  master
$ git branch -D dev
error: Cannot delete branch 'dev' checked out at '/Users/reng/desktop/learngit'
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git branch
  dev
* master
$ git branch -D dev
Deleted branch dev (was dc817c4).
$ git branch
* master

远程库分支的删除

删除远程库的分支,只要执行git push origin :branchName命令就行了。现在我要删除我远程库中的dev分支,执行git push origin :dev

$ git push origin :dev
To https://github.com/reng99/learngit
 - [deleted]         dev

此时,打开我的远程库learngit,发现之前的Pull requests量为0,branch量为1。

忽略特殊文件

忽略某些文件时,需要编写.gitignore;

.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理。

重命名分支

通过git branch -m oldBranchName newBranchName来重命名分支。我这里没有分支了,现在创建一个reng分支,然后将它重命名为dev分支。

$ git branch
* master
$ git branch reng
$ git branch
* master
  reng
$ git branch -m reng dev
$ git branch
  dev
* master

配置别名

$ git config --global alias.xxx 'xxx xxx'

例如 $ git config --global alias.unstage 'reset HEAD'

配置Git的时候,加上 --global
是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。

每个仓库的Git配置文件都放在 .git/config 文件中;

当前用户的Git配置文件放在用户主目录下的一个隐藏文件 .gitconfig 中。

解决冲突

在我们开发的时候,不知道分支和分支之间的进度情况是什么,难免会产生冲突。当产生冲突的时候,就得将冲突的内容更正,然后提交。为了方便演示,我将本地的learngit删除,重新拉取远程的gitlearn仓库(因为我不知道我之前在本地仓库做的修改是啥,对了,我将远程的分支删除了,只剩下master主分支)。克隆下来后,如果还存在本地分支,也将它删除,之后我将在masterdev分支中重新填充里面的README.md的内容。

$ cd desktop
$ git clone https://github.com/reng99/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 43, done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 43 (delta 4), reused 38 (delta 1), pack-reused 0
Unpacking objects: 100% (43/43), done.
$ cd learngit
$ git branch
* master
$ ls
README.md
$ cat README.md
## master branch content
$ git add .
$ git commit -m "add master branch content"
[master 1cfa0aa] add master branch content
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit.git
   d2f936f..1cfa0aa  master -> master
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
$ cat README.md
## master branch content

### dev branch content
$ git add .
$ git commit -m "add dev branch content"
[dev 80faf6d] add dev branch content
 1 file changed, 2 insertions(+)
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ cat README.md
## master content

### new master branch content
$ git add .
$ git commit -m "change master content"
[master ec18715] change master content
 1 file changed, 3 insertions(+), 1 deletion(-)
$ git merge dev
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

README.md文件中冲突内容–

<<<<<<< HEAD (当前更改)
## master content

### new master branch content
=======
## master branch content

### dev branch content
>>>>>>> dev (传入更改)

手动修改了README.md文件中冲突的内容–

## master branch content
### new master branch content
### dev branch content

然后命令行执行–

$ git add .
$ git commit -m "fix confict content"
[master dd848b4] fix confict content
$ git log --graph
*   commit 980788b7690d8bcf14610072fc072460bee7e9f1
|\  Merge: c49d09e 2929dca
| | Author: reng99 <1837895991@qq.com>
| | Date:   Thu Dec 21 11:14:10 2017 +0800
| | 
| |     fix confict content
| |   
| * commit 2929dca91ef8f493adba7744cdad19656538334f
| | Author: reng99 <1837895991@qq.com>
| | Date:   Thu Dec 21 11:11:49 2017 +0800
| | 
| |     add dev branch content
| |   
* | commit c49d09e33e7098d67b59c845d18e9c6f8a8f4fea
|/  Author: reng99 <1837895991@qq.com>
|   Date:   Thu Dec 21 11:12:50 2017 +0800
|   
|       change master content
|  
* commit b07f0be8280e4e437cccf2a3f8fac6beef03ff41
| Author: reng99 <1837895991@qq.com>
| Date:   Thu Dec 21 11:10:51 2017 +0800
| 
:

上面操作过程是,我先从远程库中克隆learngit仓库到本地,目前的本地learngit的分支只有master分支,然后我在master分支的README.md中添加相关的文字(见代码),接着把它推送到远程库。然后创建并切换dev分支,在README.md文件中添加新内容(见代码),接着将它提交到GIT仓库。又切换到master分支,修改README.md到内容(见代码),提交到GIT仓库后开始执行merge命令合并dev分支的内容。此时,产生了冲突,这就需要手动将冲突的内容解决,重新commitGIT仓库,最后你就可以提交到远程库了(这步我没有演示,也就是git
push origin
master一行命令行的事情)。最后我还使用git log ----graph打印出整个分支合并图(从下往上看),方便查看。⚠️
此时退出git log --graph是书写英文状态按键盘的q键。

说这么多,目的只有一个 –> 产生冲突后,需要手动调整😊

搭建Git服务器

搭建Git服务器 –
廖雪峰的Git教程

分支管理策略

先放上一张分支管理策略图,然后再慢慢讲解相关的内容…

tools/git/manager_branch_tactics

在分支管理中,我们不断的新建分支,开发,合并分支,删除分支的操作。这里需要注意合并分子的操作,之前我们进行分支的时候是直接将dev开发的分支使用git merge dev进行合并,这样有个缺点:我们看不出分支信息。因为在默认情况下,合并分支的时候,GIT是使用了Fast Foward的模式,在这种模式下,删除分支后,会丢掉分支的信息。下面我重新克隆下我远程learngit仓库,然后创建并更改dev分支的信息,使用默认的模式进行合并。

$ git branch
* master
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
$ git add .
$ git commit -m "add new dev contentt"
[dev 750e1f1] add new dev content
 1 file changed, 1 insertion(+)
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge dev
Updating 980788b..750e1f1
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)
$ git log --graph
* commit 750e1f17854872eed4d6cff8315e404079ecb18f
| Author: reng99 <1837895991@qq.com>
| Date:   Fri Dec 22 10:05:36 2017 +0800
| 
|     add new dev content
|    
*   commit 980788b7690d8bcf14610072fc072460bee7e9f1
...

上面的合并就是将master分支上面的HEAD指向dev指针,如下:

# 记录是从上往下
- before merge
    master
    * (begin)
    |
    |
    *
    \
     \
      *
      |
      |
      *  (end)
     dev

- after merge
    master
    * (begin)
    |
    |
    *
    |
    |
    * 
    |
    |
    * (end)

为了保留分支的情况,保证版本演进的清晰,我们就得使用普通模式合并,也就是在Fast Foward的模式基础上加上--no-ff参数,即git merge --no-ff branchName,不过我们一般加上你合并的相关信息,即git merge --no-ff -m "your msg here" banchName。现在更改dev分支的内容,再进行合并。

$ git checkout dev
Switched to branch 'dev'
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
$ git add .
$ git commit -m "add no-ff mode content"
[dev 80b628c] add no-ff mode content
 1 file changed, 2 insertions(+), 1 deletion(-)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
$ git merge dev --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 README.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
$ git log --graph
*   commit 98746d93a9b64ea02b8ff1c7f0fa5e915405c0e6
|\  Merge: 750e1f1 80b628c
| | Author: reng99 <1837895991@qq.com>
| | Date:   Fri Dec 22 14:39:32 2017 +0800
| | 
| |     merge with no-ff
| |   
| * commit 80b628c334618711b77da81fa805ffc246a2cf7d
|/  Author: reng99 <1837895991@qq.com>
|   Date:   Fri Dec 22 14:38:17 2017 +0800
|   
|       add no-ff mode content
|  
* commit 750e1f17854872eed4d6cff8315e404079ecb18f
...

使用--no-ff参数的普通模式合并,会执行正常合并,在master主分支上面会生成一个新的节点,如下(我上面的分支管理策略图里面的合并就是使用了普通的模式):

# 记录是从上往下
- --no-ff合并
    master
    * (before)
    |
    |
    *
    |\ 
    | \
    |  *dev
    |  |
    |  |
    |  *
    | /
    |/
    * (after) 

我们在开发中,分支管理可以分成master主分支、dev开发分支、feature功能分支、release预发布分支、hotfixes修补bug分支。其中功能分支、预发布分支和修补bug分支可以归为临时分支临时分支在进行分支的合并之后就可以被删除了。下面就一一讲解自己眼中的各种分支。

主分支master

主分支是在你初始化仓库的时候(git
init),自动生成的一个master分支,删除不了的哦(演示待会给)。主分支是有且仅有一个,也是发布上线的分支,团队合作的最终代码都会在master主分支上面体现出来。也许你也注意到了分支管理策略图里面的主分支会被打上TAG的标签,这是为了方便到某个时间段对版本的查找,标签tag的学习总结后面给出。

# 记录是从上往下
    master
    |
    |
    *(tag 1.0)
    |
    |
    *(tag 1.1)
    |
    |
    *(tag 1.2)

下面代码演示下不能放删除master的情况:

$ cd learngit
$ git branch
  dev
* master
$ git branch -D master
error: Cannot delete branch 'master' checked out at '/Users/reng/desktop/learngit'

开发分支develop

在开发的过程中,项目合作者应该保持自己本地有一个开发环境的分支,在进行分支开发之前,需要进行git pull拉取master主分支的最新内容,或者通过其他的方法。在获取到最新的内容之后才可以进行本地的新功能的开发。在开发完成后将内容merge到主分支之后,不用将dev分支删除,因为你开发的就是在这里进行,何必删除后再新建一个开发环境的分支呢。

接着上面的情况,我目前已经拥有了dev开发分支:

$ cd learngit
$ git branch
  dev
* master

功能(特性)分支feature

一个软件就是一个个功能叠加起来的,在软件的开发中,我们总不能在主分支开发,将主分支搞乱吧。当然,你可以在dev分支中开发,一般新建功能分支来开发,然后功能开发完再合并到dev分支,之后删除功能分支。需要的时候就可以将dev开发分支合并到master主分支,这样就随时保证dev分支功能的完整性。

下面演示功能分支user开发(随便写点内容)的合并(这里也演示了合并到master主分支,跳过了release分支的测试),删除。

$ git checkout dev
Switched to branch 'dev'
$ git branch user
$ git branch
* dev
  master
  user
$ git checkout user
Switched to branch 'user'
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
### function user
$ git add .
$ git commit -m "function user was acheive"
[user 26beda3] function user was acheive
 1 file changed, 2 insertions(+), 1 deletion(-)
$ git checkout dev
Switched to branch 'dev'
$ git merge --no-ff -m "merge user feature" user
Merge made by the 'recursive' strategy.
 README.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)
$ git merge --no-ff -m "merge dev branch" dev
Merge made by the 'recursive' strategy.
 README.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
$ git log --graph
*   commit f15a1e9012635fc21e944ab76c4cd4bbd539f82f
|\  Merge: 98746d9 0ca83c6
| | Author: reng99 <1837895991@qq.com>
| | Date:   Fri Dec 22 16:35:43 2017 +0800
| | 
| |     merge dev branch
| |     
| *   commit 0ca83c654df64724743a966f5f0989477e504cbc
| |\  Merge: 80b628c 26beda3
| | | Author: reng99 <1837895991@qq.com>
| | | Date:   Fri Dec 22 16:33:27 2017 +0800
| | | 
| | |     merge user feature
| | |    
| | * commit 26beda3b8246e047f10ac0461ca11d1a6f132819
| |/  Author: reng99 <1837895991@qq.com>
| |   Date:   Fri Dec 22 16:31:41 2017 +0800
| |   
| |       function user was acheive
| |     
* |   commit 98746d93a9b64ea02b8ff1c7f0fa5e915405c0e6
|\ \  Merge: 750e1f1 80b628c
| |/  Author: reng99 <1837895991@qq.com>
:
$ git branch -D user
Deleted branch user (was 26beda3).
$ git branch
  dev
* master

预发布分支release

在进行一系列的功能的开发和合并后,在满足迭代目标的时候,就可以打包送测了。这里就需要一个预发布分支release。预发布分支是指在发布正式版本之前(
即合并到master分支之前,可查看上面分支管理策略图),需要一个有预发布的版本(可以理解为灰度环境)进行测试。

预发布环境是从dev分支上面分出来的,预发布结束之后,必须合并到devmaster分支上面。这里我就不演示了,跟功能分支差不多,就是合并的时候要合并到devmaster上,这时候dev分支和master的同步的代码,就不需要将dev分支合并到master了。最后将预发布分支删除掉。

修复bug分支 bug/hotfixes

在写代码的过程中,由于种种原因 ->
比如功能考虑不周全,版本上线时间有限,产品突然改需求等,我们写的代码就出现一些或大或小的bug或者需要紧急修复。那么我们就可以使用bug分支(其实就是新建一个分支处理bug而已啦,命名随意起的),然后在这个分支上处理编码出现的问题。我在分支管理策略图上面已经展示了一种出现bug的情况
->
就是在测试发布版本看似没问题的情况下,将release版本整合到masterdev中,这时候火眼精金发现了遗留的一个bug,然后新建一个bug分支处理,再合并到masterdev中,之后将bug分支移除啦。

在开发的过程中,无论咋样都是这样 : 新建bug分支 -> 把分支合并 ->
删除分支,这里的demo就不演示了,可以参考上面的功能(特性)分支feature

这里需要注意⚠️的一点,当在开发的过程中,开发到一定的程度,需要停下来需改紧急的bug,那么需要停下手头的工作需改bug啦。这时候需要将工作现场储藏(stash功能)起来,等以后回复现场了后接着工作。现在我在原先的gitlearn仓库中README.md文件文末添加### modify content内容来进行演示。

$ cd desktop
$ cd learngit
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
$ git status
On branch dev
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.md

no changes added to commit (use "git add" and/or "git commit -a")
$ git stash
Saved working directory and index state WIP on dev: 80b628c add no-ff mode content
HEAD is now at 80b628c add no-ff mode content
$ git status
On branch dev
nothing to commit, working tree clean

然后过段时间(这里省略修改的演示),代码已经修改好合并后,需要回到最新的内容区域进行工作,这就需要还原最新的内容了,demo如下:

$ cd learngit
$ git stash list
stash@{0}: WIP on dev: 80b628c add no-ff mode content
$ git stash pop
On branch dev
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.md

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (9e85bcc8435ae38c17db59ddc3cd8401af404827)
$ git stash list

⚠️
git stash不仅可以隐藏工作区的内容,也可以隐藏暂存区的内容。git stash list是查看隐藏的列表。git stash pop是将隐藏的内容恢复并删除,git stash pop相当于git stash apply && git stash drop,这里的git stash apply是恢复隐藏内容,git stash drop是删除隐藏内容。

多人协作

简单谈下自己git协作的过程吧。在负责人将搭建好的仓库上传到远程的仓库后(一般是包含了master默认的分支和dev分支),自己将远程仓库克隆到本地,然后在本地的仓库上新建一个dev分支,将远程的dev分支重新拉取下git pull origin dev,开发完成后就可以提交自己的代码到远程的dev分支了,如果提交之前或者之后需要修改bug或者添加新的需求的话,需要新建一个相关的分支并完成开发,将他们合并到本地dev分支后上传到远程dev分支。如果新建的远程仓库中只有master分支,我是这样处理的:依然要在本地新建一个dev分支,然后在完成特定版本的开发后,将分支合并到本地master分支然后再推送到远程master分支,本地的dev分支保留哦。我自己比较偏向于第一种情况。

标签管理

发布一个版本前,为了唯一确定时刻的版本,我们通常在版本库中打一个标签(tag),方便在发布版本以后,可以在某个时刻将某个历史的版本提取出来(因为标签tag也是版本库的一个快照)。

创建标签

创建标签是默认在你切换的分支最新提交处创建的。我这里在本地桌面的learngit仓库master分支上打一个v1.0标签

$ cd desktop/learngit
$ git branch
* dev
  master
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git tag
$ git tag v1.0
$ git tag
v1.0

当然,你可以在非新commit的地方进行标签。这就需要你查找到需要打标签处的commit的id,然后执行git tag tagName commitId。这里我随意找master分支中的commit
id进行标签v0.9的标签创建。

$ git log --pretty=oneline --abbrev-commit
f15a1e9 merge dev branch
0ca83c6 merge user feature
26beda3 function user was acheive
98746d9 merge with no-ff
...

现在在commit id为 98746d9处打标签。

$ git tag v0.9 98746d9
$ git tag
v0.9
v1.0

操作标签

在上面创建标签,我们已经有了标签v0.9 v1.0。有时候我们标签打错了,需要进行删除,那么就得更改啦,运用git tag -d tagName

$ git tag -d v0.9
Deleted tag 'v0.9' (was 98746d9)
$ git tag
v1.0
$ git tag v0.8 80b628c -m "version 0.8"
$ git tag
v0.8
v1.0
$ git show v0.8
$ git show v0.8
tag v0.8
Tagger: reng99 <1837895991@qq.com>
tag v0.8
Tagger: reng99 <1837895991@qq.com>
Date:   Wed Dec 27 16:07:46 2017 +0800

version 0.8

在上面的演示中,我删除了v0.9,然后在创建v0.8的时候追加了打标签的信息,之后使用git show tagName查看签名信息。

我们还可以进行分支切换标签,类似于分支的切换,我这里打的两个标签的内容是不同的,我可以通过观察内容的改表来得知时候成功切换标签了。

$ git tag
v0.8
v1.0
$ git checkout v1.0
HEAD is now at f15a1e9... merge dev branch
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
### function user
$ git checkout v0.8
Previous HEAD position was f15a1e9... merge dev branch
HEAD is now at 80b628c... add no-ff mode content
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff

在确认好标签后,就可以像远程推送标签了,我这里推送v1.0

$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit.git
 * [new tag]         v1.0 -> v1.0

上面是使用git push origin tagName推送特定的tag到远程库,但是我们能不能推送全部的tag呢?答案是肯定的,看者可以通过git push origin --tags进行推送。有时候,我们推送了tag标签到远程库中了,现在想删除掉怎么办?这个就略微麻烦点,我们不能像上面提到的删除本地库的标签那样,通过git tag -d tagName那样,而是通过git push origin :refs/tags/tagName,这里不演示,如果看者感兴趣可以自己来把弄一下哦。

参考内容

廖雪峰官方网站–Git教程

易百教程–Git教程

git官网

分支管理模型图

Git分支管理策略 –
阮一峰的网络日志

原文链接

git的学习汇总–原创

相关文章