Git从入门到入门的入门学习文档

版本控制

版本控制(Revision control)是维护工程蓝图的标准作法,能追踪工程蓝图从诞生一直到定案的过程。此外,版本控制也是一种软件工程技巧,借此能在软件开发的过程中,确保由不同人所编辑的同一代码文件案都得到同步。

软件设计师常会利用版本控制来追踪、维护源码、文件以及配置文件等等的改动,并且提供控制这些改动控制权的程序。

在最简单的情况下,软件设计师可以自己保留一个程序的许多不同版本,并且为它们做适当的编号。这种简单的方法已被用在很多大型的软件项目中。该方法虽然可行,但不够有效率。除了必须同时维护很多几乎一样的源码备分外;而且极度依赖软件设计师的自我修养与开发纪律,但这却常是导致错误发生的原因。

有时候,一个程序同时存有两个以上的版本也有其必要性,例如:在一个为了部署的版本中程序错误已经被修正、但没有加入新功能;在另一个开发版本则有新的功能正在开发、也有新的错误待解决,这使得同时间需要不同的版本并修改。

集中式版本控制系统和分布式版本控制系统区别

SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了。

Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

版本控制系统也不是无所不能

版本控制系统并不是无所不能,它只能跟踪文本文件的改动,比如txt文件,网页,所有程序的代码等,Git也不列外,版本控制系统可以告诉你每次的改动,但是图片,视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是知道图片从1kb变成2kb,但是到底改了啥,版本控制也不知道。

Git的简历

git是一个分布式版本控制软件,最初由林纳斯·托瓦兹(Linus Torvalds)创作,于2005年以GPL发布。最初目的是为更好地管理Linux内核开发而设计。

都说Git是目前世界上最先进的分布式版本控制系统,没有之一。没有必要去在乎谁是第一,谁是第二,对于使用者来说只有适合不适合。

实际上内核开发团队决定开始开发和使用 Git 来作为内核开发的版本控制系统的时候,世界开源社群的反对声音不少,最大的理由是 Git 太艰涩难懂,从 Git 的内部工作机制来说,的确是这样。但是随着开发的深入,Git 的正常使用都由一些友好的脚本命令来执行,使 Git 变得非常好用,即使是用来管理我们自己的开发项目,Git 都是一个友好,有力的工具。现在,越来越多的著名项目采用 Git 来管理项目开发.

Git的优缺点

优点:

  • 适合分布式开发,强调个体。
  • 公共服务器压力和数据量都不会太大。
  • 速度快、灵活。
  • 任意两个开发者之间可以很容易的解决冲突。
  • 离线工作。

缺点:

  • 资料少(起码中文资料很少)。
  • 学习周期相对而言比较长。
  • 不符合常规思维。
  • 代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。

Git的安装

在windows操作

Git控制台介绍

当你在Windows中安装完Git之后,在开始菜单中就会多出一个Git菜单,该菜单中会有三个快捷方式:

  • Git Bash:与Linux命令行完全相同的操作控制台。
  • Git CMD: 其实就是我们操作系统的CMD窗口,被集成过来而已,没有什么特别之处。
  • Git GUI:这是Git提供的一个可视化操作面板。

对于三种面板的选择完全根据个人的喜好,我更习惯于Git Bash,因此,本文中,我的所有操作都将在Git Bash中进行演示操作。但是在实际开发中很少有人这样使用,几乎都使用IDE中集成的Git工具。在文章的最后会做相应的介绍。

Git账号的配置

因为Git是分布式版本控制系统,所以需要填写用户名和邮箱作为一个标识。

配置用户名:git config –global user.name ‘你自己的Github的用户名’
配置邮箱:git config –global user.email ‘你的注册邮箱账号’

admin@KevinBruce MINGW64 ~
$ git config --global user.name "Your Name"

admin@KevinBruce MINGW64 ~
$ git config --global user.email "[email protected]"

注意:git config  --global 参数,有了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然你也可以对某个仓库指定不同的用户名和邮箱。

版本管理

版本库

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

创建版本库

版本库都是存放在自己本地的,可以管理自己不同时间对文件的修改的跟踪,便于自己的管理和维护以及记录。创建一个版本库很简单,只需要在你想要作为版本库的目录下执行git init命令即可。

示例:我首先通过控制台进入到D盘,在D盘的根目录下创建一个目录(这个目录在研发过程中就是我们项目的目录),进入到这个目录下执行初始化命令。

admin@KevinBruce MINGW64 ~
$ cd /d/

admin@KevinBruce MINGW64 /d
$ mkdir KevinProject

admin@KevinBruce MINGW64 /d
$ cd KevinProject/

admin@KevinBruce MINGW64 /d/KevinProject
$ git init
Initialized empty Git repository in D:/KevinProject/.git/

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ ls -a
./  ../  .git/

admin@KevinBruce MINGW64 /d/KevinProject (master)

当你执行完以上的命令后你会发现KevinProject目录下会多了一个.git的目录(可能会隐藏,可以使用ls -a命令查看),这个目录是Git来跟踪管理版本的,千万不要手动修改这个目录里面的文件,否则,会把git仓库给破坏掉。

查看版本库状态

git status :该命令可以让我们时刻掌握仓库当前状态,在之后的介绍中,我会多次的使用这个命令

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

git diff:该命令可以查看修改内容
git log命令显示从最近到最远的显示日志.如果嫌上面显示的信息太多的话,我们可以使用命令:git log –pretty=oneline 

git reflog :该命令记录每一次命令。类似Linux的history命令。

添加文件到版本库

首先我们在仓库目录中创建一个README.md的文件并在改文件中添加一些内容。

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ touch README.md

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ vi README.md

admin@KevinBruce MINGW64 /d/KevinProject (master)

我在README.md文件中添加了两行歌词。

有时候我觉得自己像一只小小鸟
想要飞却怎么样也飞不高

而此时,我新创建的这个文件并没有被Git管理起来,我们需要执行一些命令来将其提交到版本库中。如果想要将文件提交到版本库中,我们需要做两步操作:

把文件添加到暂存区:git add 文件名

把文件提交到仓库: git commit –m ‘注释信息’

示例:将我们的README.md文件提交到版本库中。

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git add README.md

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   README.md


admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git commit -m "初始提交我写的歌曲"
[master (root-commit) 6856dbc] 初始提交我写的歌曲
 1 file changed, 2 insertions(+)
 create mode 100644 README.md

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git status
On branch master
nothing to commit, working tree clean

admin@KevinBruce MINGW64 /d/KevinProject (master)

理解工作区和暂存区

工作区:就是你在电脑上看到的目录,比如目录下KevinProject里的文件(.git隐藏目录版本库除外)。或者以后需要再新建的目录文件等等都属于工作区范畴。
版本库(Repository):工作区有一个隐藏目录.git,这个不属于工作区,这是版本库。其中版本库里面存了很多东西,其中最重要的就是stage(暂存区),还有Git为我们自动创建了第一个分支master,以及指向master的一个指针HEAD。
我们前面说过使用Git提交文件到版本库有两步:
  第一步:是使用 git add 把文件添加进去,实际上就是把文件添加到暂存区。
  第二步:使用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支上。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

版本回退

回退到上一版本:git reset --hard HEAD^
回退到上上个版本:git reset –hard HEAD^^
到上100个版本:git reset –hard HEAD~100
回退到具体版本:git reset –hard 具体版本号

管理修改

为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。

你会问,什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。

Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。

提交后,用git diff HEAD -- readme.txt命令可以查看工作区和版本库里面最新版本的区别

撤销修改

在你没有执行git commit语句的时候,想要撤销某些修改内容。

命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

总之,就是让这个文件回到最近一次git commit或git add时的状态。

用命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区。git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

删除文件

一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了,这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了。
现在你有两个选择:
一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit。现在,文件就从版本库中被删除了。

另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

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

远程仓库

对于我所熟知的远程仓库就是以下的两个:对于功能上能有什么不同之处,只不过是github是世界级的,码云是国产级的。

  • Github:https://github.com
  • 码云:https://gitee.com

使用GitHub时,国内的用户经常遇到的问题是访问速度太慢,有时候还会出现无法连接的情况(原因你懂的)。

如果我们希望体验Git飞一般的速度,可以使用国内的Git托管服务——码云(gitee.com)。

和GitHub相比,码云也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,码云还提供了项目管理、代码托管、文档管理的服务,5人以下小团队免费。

我还是更倾向于码云,本示例将会以码云作为远程仓库演示。

注册并登录码云

此处不需要赘述,注册登录应该都会,如果还不会这个,估计你也不会有看到这里的机会了。

账号授权

如果想要在远程仓库中进行对代码进行操作,那么就要对账号就行授权。有两种授权方式:

  • 完全使用账号密码
  • 使用SSH进行对主机的认证

对于使用账号密码的操作,我们做完全局的配置之后就可以进行提交了,这样我们就可以通过第一次输入密码的到认证。我们不做过多介绍。

使用SSH进行对主机的认证,首先就是要在我们本地的Windows计算机中,创建SSH的公钥。

使用 ssh-keygen –t rsa –C “注册邮箱”命令创建ssh-key,根据提示做相应的输入:

Enter file in which to save the key (/c/Users/admin/.ssh/id_rsa):

以上的这行提示是问我们将私钥和公钥存放在什么位置,我们直接回车按照默认就好。 

Enter passphrase (empty for no passphrase):

Enter same passphrase again:

以上的这行提示是让我们输入密码,我们什么都不输入,一路回车即可,如果提示以下信息。表示创建成功。


admin@KevinBruce MINGW64 ~
$ ssh-keygen -t rsa -C "[email protected]"
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/admin/.ssh/id_rsa):
Created directory '/c/Users/admin/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /c/Users/admin/.ssh/id_rsa.
Your public key has been saved in /c/Users/admin/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Pr2wg+wHvyJYUJDsArbfyrE2AOCDsqRYibv90rzitHM [email protected]
The key's randomart image is:
+---[RSA 2048]----+
| ..o             |
|o.o .            |
|*o.o             |
|*==              |
|*=o..   S        |
|=. o.... .       |
| o+=+. ++ .      |
|..*BE + ++ .     |
| .+*++.ooo.      |
+----[SHA256]-----+

admin@KevinBruce MINGW64 ~

查看我们刚刚生成的公钥:公钥存放在我们第一次填写的目录下(因为我们没有填写,默认在/c/Users/admin/.ssh/目录下)。

admin@KevinBruce MINGW64 ~
$ cat /c/Users/admin/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbfF9G3QqNd7E/XZ8AlDbMo8NdlwaLmwjYIJKQbikJX6BZSNi9zByCmEl57IASEl77BlezIiRCOft3XQUOAOP2CXTLF7otR0bTE9WDp5bqvB0eh3hsD2EJULaY/NQVBScBpDeLhHjIBsOt4eG5g+obeRuykf5tA7GyU2gAal+E3/i6HqcKfV5vt416njWtprKK8z9HX1R5y6/+wR5elUXhiG6xO06M9BF0WHr5+XlPekRBRyXUh6JyCv4Ipw3VmxPCEHYo/D7sol3frW/LQ2mTQewu2KrbwLF1gF+Pcjbiy3AIaqUL5fjvZhwjWWmvki1PjsCmMvSOS6ZvcO+HPDFL [email protected]

admin@KevinBruce MINGW64 ~

将公钥中的内容复制到码云上。

登录到码云后-->设置-->SSH公钥

测试是否通过:

   执行ssh -T [email protected]>yes-->输入邮箱账号的密码,出现以下效果证明测试通过。

admin@KevinBruce MINGW64 ~
$ ssh -T [email protected]
The authenticity of host 'gitee.com (116.211.167.14)' can't be established.
ECDSA key fingerprint is SHA256:FQGC9Kn/eye1W8icdBgrQp+KkGYoFgbVr17bmjey0Wc.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitee.com,116.211.167.14' (ECDSA) to the list of known hosts.
Welcome to Gitee.com, KevinBruce!

admin@KevinBruce MINGW64 ~

创建远程仓库

登录到码云后-->右上角加号-->新建项目

根据提示填写相关信息点击创建即可。

关联远程仓库

在工程目录下执行:‘git remote add origin 远程仓库地址’ 命令:

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git remote add origin [email protected]:JackBruceYuHome/KevinProject.git

admin@KevinBruce MINGW64 /d/KevinProject (master)

如果你在执行以上的命令时遇到了以下问题:

fatal: remote origin already exists.

这说明本地库已经关联了一个名叫origin的远程库,此时,可以先用git remote -v查看远程库信息:

我们可以删除已有的远程库:

$ git remote rm origin

然后再重新执行关联命令即可。

拉取远程库内容到本地库

git pull origin master

推送本地仓库到远程仓库

$ git push origin master

注意:需要先把远程库的内容拉到本地,否则会报错。

同步多个远程库

因为git本身是分布式版本控制系统,可以同步到另外一个远程库,当然也可以同步到另外两个远程库。

使用多个远程库时,我们要注意,git给远程库起的默认名称是origin,如果有多个远程库,我们需要用不同的名称来标识不同的远程库。

仍然以KevinProject本地库为例,我们先删除已关联的名为origin的远程库:

$ git remote rm origin

然后,先关联GitHub的远程库:

$ git remote add github-origin [email protected]:JackBruceYuHome/KevinProject.git

注意,远程库的名称叫github-origin,不叫origin了。

接着,再关联码云的远程库:

$ git remote add gitee-origin [email protected]:JackBruceYuHome/KevinProject.git

同样注意,远程库的名称叫gitee-origin,不叫origin了。

现在,我们用git remote -v查看远程库信息,可以看到两个远程库:

admin@KevinBruce MINGW64 /d/KevinProject(master)
$ git remote -v
gitee-origin  github-origin [email protected]:JackBruceYuHome/KevinProject.git (fetch)
gitee-origin  github-origin [email protected]:JackBruceYuHome/KevinProject.git (push)
github-origin  github-origin [email protected]:JackBruceYuHome/KevinProject.git (fetch)
github-origin  github-origin [email protected]:JackBruceYuHome/KevinProject.git (push)

如果要推送到GitHub,使用命令:

admin@KevinBruce MINGW64 /d/KevinProject(master)
$ git push github-origin master

如果要推送到码云,使用命令:

admin@KevinBruce MINGW64 /d/KevinProject(master)
$ git push gitee-origin master

这样一来,我们的本地库就可以同时与多个远程库互相同步

从远程库克隆

假设我们从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。

$ git clone [email protected]:JackBruceYuHome/KevinProject.git

分支管理

在网上看到过一个分支的比喻非常不错:

分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!

作用:假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

特点:git的分支是与众不同的,无论创建、切换和删除分支,git在非常短的时间内就能完成。无论版本库是一个文件还是一万了文件。

master主分支

在版本回退中,每次提交,git都把他们串成一条时间线,在git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。每次提交,master分支都会向前移动一步,这样,随着不断提交,master分支的线也越来越长。

细说分支

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上。不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变。假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并。

为什么Git在对分支的增加、删除和切换回如此的快呢,是因为他不需要做过多的工作,只需要修改一下指针即可。

查看分支

查看当前分支:git branch :会列出所有的分支,当前分支前面会有一个*

创建与切换分支

创建分支: git branch 分支名

切换分支: git checkout 分支名

创建与切换同时进行:git checkout -b 分支名;使用这个命令就相当于执行以上两个命令。

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git checkout -b Kevin
Switched to a new branch 'Kevin'

admin@KevinBruce MINGW64 /d/KevinProject (Kevin)

将分支提交到远程仓库

将分支提交到远程仓库首先要将分支切换到master,然后执行git push origin 分支名。

admin@KevinBruce MINGW64 /d/KevinProject (Kevin)
$ git checkout master
Switched to branch 'master'

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git push origin Kevin
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 343 bytes | 343.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To gitee.com:JackBruceYuHome/KevinProject.git
 * [new branch]      Kevin -> Kevin

admin@KevinBruce MINGW64 /d/KevinProject (master)

合并分支

把Kevin分支的工作成果合并到master分支上。说先要切换到master分支上,然后执行: git merge 分支名

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git merge Kevin
Updating 5588ac7..6ae33e9
Fast-forward
 doc.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 doc.txt

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

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

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

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

下面我们实战一下--no-ff方式的git merge:

首先,仍然创建并切换dev分支:

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

修改readme.txt文件,并提交一个新的commit:

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

现在,我们切换回master:

$ git checkout master
Switched to branch 'master'


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

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt |    1 +
 1 file changed, 1 insertion(+)


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

删除分支

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

删除本地分支: git branch -d 分支名
删除远程分支: git push origin --delete 分支名(在删除远程分支的时候一定要先删除本地分支)

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git branch -d Kevin
Deleted branch Kevin (was 6ae33e9).

admin@KevinBruce MINGW64 /d/KevinProject (master)
$ git push origin --delete Kevin
To gitee.com:JackBruceYuHome/KevinProject.git
 - [deleted]         Kevin

admin@KevinBruce MINGW64 /d/KevinProject (master)

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

查看分支合并图

git log --graph :查看分支合并图

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

BUG分支

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

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

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

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

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

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

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

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

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

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

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

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

$ git stash apply stash@{0}

Feature分支

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

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

现在,你终于接到了一个新任务:开发代号为Vulcan的新功能。

于是准备开发:

$ git checkout -b feature-vulcan
Switched to a new branch 'feature-vulcan'
$ 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 756d4af] add feature vulcan
 1 file changed, 2 insertions(+)
 create mode 100644 vulcan.py

切回dev,准备合并:

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

现在我们强行删除:

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 756d4af).

终于删除成功!

多人协作

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

要查看远程库的信息,用git remote:

$ git remote
origin

或者,用git remote -v显示更详细的信息:

$ git remote -v
origin  [email protected]:JackBruceYuHome/KevinProject.git (fetch)
origin  [email protected]:JackBruceYuHome/KevinProject.git (push)

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

推送分支

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

$ git push origin master

如果要推送其他分支,比如dev,就改成:

$ git push origin dev

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

master分支是主分支,因此要时刻与远程同步;

dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;

bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;

feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!

抓取分支

从远程库clone时,默认只能看到master

想在dev分支上开发,就必须创建origin的dev分支到本地

git checkout -b dev origin/dev

工作模式

1、可以试图使用git push origin branch-name推送自己的修改

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

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

4、解决冲突后,再用git push origin branch-name推送就能成功。

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

标签管理

意义:发布一个版本时,我们通常现在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也就是版本的一个快照。

在Git中打标签非常简单,首先,切换到需要打标签的分支上,然后,敲命令git tag <name>就可以打一个新标签:

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

$ git tag v1.0

可以用命令git tag查看所有标签:

$ git tag
v1.0

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?方法是找到历史提交的commit id,然后打上就可以了

$ git log --pretty=oneline --abbrev-commit
6a5819e merged bug fix 101
cc17032 fix bug 101
7825a50 merge with no-ff
6224937 add merge
59bc1cb conflict fixed
400b400  simple
75a857c AND simple
fec145a branch test
d17efd8 remove test.txt
...

$ git tag v0.9 6224937

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

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

用命令git show <tagname>可以看到说明文字:

$ git show v0.1
tag v0.1
Tagger: Kevin Bruce<[email protected]>
Date:   Mon Aug 26 07:28:11 2013 +0800

version 0.1 released

commit 3628164fb26d48395383f8f31179f24e0882e1e0
Author: Kevin Bruce<[email protected]>
Date:   Tue Aug 20 15:11:49 2013 +0800

    append GPL

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

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

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

gpg: signing failed: secret key not available
error: gpg failed to sign the data
error: unable to sign the tag

如果报错,请参考GnuPG帮助文档配置Key。

用命令git show <tagname>可以看到PGP签名信息:

$ git show v0.2
tag v0.2
Tagger: Kevin Bruce <[email protected]>
Date:   Mon Aug 26 07:28:33 2013 +0800

signed version 0.2 released
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (Darwin)

iQEcBAABAgAGBQJSGpMhAAoJEPUxHyDAhBpT4QQIAKeHfR3bo...
-----END PGP SIGNATURE-----

commit fec145accd63cdc9ed95a2f557ea0658a2a6537f
Author: Kevin Bruce<[email protected]>
Date:   Thu Aug 22 10:37:30 2013 +0800

    branch test

用PGP签名的标签是不可伪造的,因为可以验证PGP签名。验证签名的方法比较复杂,这里就不介绍了。

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

$ git tag -d v0.1
Deleted tag 'v0.1' (was e078af9)

因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。

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

$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To [email protected]:JackBruceYuHome/KevinProject.git
 * [new tag]         v1.0 -> v1.0

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

$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 554 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To [email protected]:JackBruceYuHome/KevinProject.git
 * [new tag]         v0.2 -> v0.2
 * [new tag]         v0.9 -> v0.9

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

$ git tag -d v0.9
Deleted tag 'v0.9' (was 6224937)

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

$ git push origin :refs/tags/v0.9
To [email protected]:JackBruceYuHome/KevinProject.git
 - [deleted]         v0.9

解决冲突

如果你是用类似于Intellij IDEA这类集成开发工具,当遇到冲突时,他会提示你去解决冲突后合并。如果使用git bash 就需要你手动修改文件中冲突的部分了。

自定义Git

忽略特殊文件

有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status都会显示Untracked files ...,有强迫症的童鞋心里肯定不爽。

这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。

不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore

忽略文件的原则是:

  • 忽略操作系统自动生成的文件,比如缩略图等;
  • 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  • 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

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

$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.

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

$ git add -f App.class

或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore命令检查:

$ git check-ignore -v App.class
.gitignore:3:*.class    App.class

Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。

搭建Git服务器

远程仓库一节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改。

GitHub和码云就是免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。

搭建Git服务器需要准备一台运行Linux的机器(本示例使用centos7 x64)。假设你已经有sudo权限的用户账号,下面,正式开始安装。

第一步,安装git:

$ sudo yum install git

第二步,创建一个git用户,用来运行git服务:

$ sudo adduser git

第三步,创建证书登录:

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

第四步,初始化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

第五步,禁用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每次一登录就自动退出。

第六步,克隆远程仓库:

现在,可以通过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就是这个工具。

这里我们也不介绍Gitolite了,不要把有限的生命浪费到权限斗争中。

附录:Git常用命令汇总

mkdir:         XX (创建一个空目录 XX指目录名)
pwd:          显示当前目录的路径。
git init          把当前的目录变成可以管理的git仓库,生成隐藏.git文件。
git add XX       把xx文件添加到暂存区去。
git commit –m “XX”  提交文件 –m 后面的是注释。
git status        查看仓库状态
git diff  XX      查看XX文件修改了那些内容
git log          查看历史记录
git reset  –hard HEAD^ 或者 git reset  –hard HEAD~ 回退到上一个版本
                    (如果想回退到100个版本,使用git reset –hard HEAD~100 )
cat XX         查看XX文件内容
git reflog       查看历史记录的版本号id
git checkout — XX  把XX文件在工作区的修改全部撤销。
git rm XX          删除XX文件
git remote add origin https://github.com/tugenhua0707/testgit 关联一个远程库
git push –u(第一次要用-u 以后不需要) origin master 把当前master分支推送到远程库
git clone https://github.com/tugenhua0707/testgit  从远程库中克隆
git checkout –b dev  创建dev分支 并切换到dev分支上
git branch  查看当前所有的分支
git checkout master 切换回master分支
git merge dev    在当前的分支上合并dev分支
git branch –d dev 删除dev分支
git branch name  创建分支
git stash 把当前的工作隐藏起来 等以后恢复现场后继续工作
git stash list 查看所有被隐藏的文件列表
git stash apply 恢复被隐藏的文件,但是内容不删除
git stash drop 删除文件
git stash pop 恢复文件的同时 也删除文件
git remote 查看远程库的信息
git remote –v 查看远程库的详细信息
git push origin master  Git会把master分支推送到远程库对应的远程分支上

git tag 标签名 打标签

git tag 查看所有标签

git tag 标签名 commitid  指定commitid打标签

git tag -a 标签名 -m "标签信息"  指定标签信息

git checkout 标签名 切换到指定标签

git push origin 标签名  推送标签到远程

git push origin --tag  一次性推送全部尚未推送到远程的本地标签

删除已经推送到远程的标签

    先从本地删除:git tag -d 标签名

    再从远程删除:git push origin :refs/tag/标签名

本期的内容就到这里。我会不断的去更新和修改我的文档。如果有幸您看到我的文章,希望您给出一些评价和指点,我会进行修改学习,更希望看到这篇文章的你在学习与工作的路上一天比一天优秀。

猜你喜欢

转载自my.oschina.net/epoch/blog/1785629