Git简介

版本控制

什么是版本控制

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 除了项目源代码,你可以对任何类型的文件进行版本控制。

版本控制的作用

有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。

版本控制系统分类

本地版本控制系统

许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖文件。

为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。下图来源于Git官网。

本地版本控制系统.png

集中式版本控制系统

接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。

集中化的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。下图来源于Git官网。

集中式版本控制系统.png

这么做虽然解决了本地版本控制系统无法让在不同系统上的开发者协同工作的诟病,但也还是存在下面的问题:

单点故障:中央服务器宕机,则其他人无法使用;如果中心数据库磁盘损坏没有进行备份,你将丢失所有数据。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

必须联网才能工作:受网络状况、带宽影响。

分布式版本控制系统

于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 Git 就是一个典型的分布式版本控制系统。

这类系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。下图来源于Git官网。

分布式版本控制系统.png

分布式版本控制系统可以不用联网就可以工作,因为每个人的电脑上都是完整的版本库,当你修改了某个文件后,你只需要将自己的修改推送给别人就可以了。但是,在实际使用分布式版本控制系统的时候,很少会直接进行推送修改,而是使用一台充当“中央服务器”的东西。这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

分布式版本控制系统的优势不单是不必联网这么简单,后面我们还会看到 Git 极其强大的分支管理等功能。

Git概述

Git历史

同生活中的许多伟大事件一样,Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众广的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。到 2002 年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,于是整个项目组开始启用分布式版本控制系统 BitKeeper 来管理和维护代码。

到 2005 年的时候,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了免费使用 BitKeeper 的权力。这就迫使 Linux 开源社区(特别是 Linux的缔造者 Linus Torvalds )不得不吸取教训,只有开发一套属于自己的版本控制系统才不至于重蹈覆辙。他们对新的系统订了若干目标:

• 速度

• 简单的设计

• 对非线性开发模式的强力支持(允许上千个并行开发的分支)

• 完全分布式

• 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

Git是什么

Git是一款免费、开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。

Git是一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

官网地址为:https://git-scm.com/

Git特点

优点

适合分布式开发,强调个体;

公共服务器压力和数据量都不会太大;

速度快、灵活;

任意两个开发者之间可以很容易的解决冲突;

离线工作。

缺点

代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息;

权限控制不友好;如果需要对开发者限制各种权限的建议使用SVN。

Git与Svn对比

SVN

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

下图就是标准的集中式版本控制工具管理方式:

集中式版本控制工具管理方式

集中管理方式在一定程度上看到其他开发人员在干什么,而管理员也可以很轻松掌握每个人的开发权限。

但是相较于其优点而言,集中式版本控制工具缺点很明显:

  • 服务器单点故障
  • 容错性差

Git

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

下图就是分布式版本控制工具管理方式:

分布式版本控制工具管理方式

Git 与其他版本管理系统的主要区别

Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。

下面我们主要说一个关于 Git 其他版本管理系统的主要差别:对待数据的方式

Git采用的是直接记录快照的方式,而非差异比较。

大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。

具体原理如下图所示,理解起来其实很简单,每个我们对提交更新一个文件之后,系统记录都会记录这个文件做了哪些更新,以增量符号Δ(Delta)表示。下图来源于Git官网。

[图片上传失败...(image-aabf00-1561887950323)]

我们怎样才能得到一个文件的最终版本呢?

很简单,高中数学的基本知识,我们只需要将这些原文件和这些增加进行相加就行了。

这种方式有什么问题呢?

比如我们的增量特别特别多的话,如果我们要得到最终的文件是不是会耗费时间和性能。

Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。下图来源于Git官网。

[图片上传失败...(image-f13167-1561887950323)]

Git工作流程

一般工作流程如下:

  1. 从远程仓库中克隆 Git 资源作为本地仓库;
  2. 从本地仓库中checkout代码然后进行代码修改;
  3. 在提交本地仓库前先将代码提交到暂存区;
  4. 提交修改,提交到本地仓库;本地仓库中保存修改的各个历史版本;
  5. 在需要和团队成员共享代码时,可以将修改代码push到远程仓库。

Git 的工作流程图如下:

Git 的工作流程图

Git的几个核心概念

工作区、暂存区、版本库、远程仓库

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

Git工作流程

Workspace: 工作区,就是你平时存放项目代码的地方

Index / Stage: 暂存区,用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息

Repository: 仓库区(或版本库),就是安全存放数据的位置,这里面有你提交到所有版本的数据。其中HEAD指向最新放入仓库的版本

Remote: 远程仓库,托管代码的服务器,可以简单的认为是你项目组中的一台电脑用于远程数据交换

分支

每次的提交Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里这个分支叫主分支,即master分支。HEAD指针严格来说不是指向提交,而是指向master,master才是指向提交的。

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

分支1

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

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

分支2

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

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

分支3

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

分支4

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

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

分支5

远程仓库

远程仓库分为公有远程仓库和私有远程仓库。

公有远程仓库

本质和本地仓库无异,只是这个仓库①不在本地②大家可能都知道③需要将代码共享到远程仓库④可以被其他人克隆同步代码等。

一般情况下在企业中会有一个搭建在公司的远程仓库,可以让本公司内部的开发人员同步开发。而业界最富盛名的远程仓库则为github;它上面存放了非常多的开源组织、个人、企业等的开放源码库,任何都可以从上面获取源码。

私有远程仓库

远程仓库实际上和本地仓库一样,纯粹为了7x24小时开机并交换大家的修改。GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。

GitHub远程仓库

在本地创建了一个Git仓库,又想让其他人来协作开发,此时就可以把本地仓库同步到远程仓库,同时还增加了本地仓库的一个备份。

常用的远程仓库就是github:https://github.com/

Github支持两种同步方式“https”和“ssh”。如果使用https很简单基本不需要配置就可以使用,但是每次提交代码和下载代码时都需要输入用户名和密码。而且如果是公司配置的私有git服务器一般不提供https方式访问。

GitHub官网

忽略文件语法说明

在工程中,并不是所有文件都需要保存到版本库中的,例如“target”目录及目录下的文件就可以忽略。在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件或目录。

Git 忽略规则优先级

在 .gitingore 文件中,每一行指定一个忽略规则,Git 检查忽略规则的时候有多个来源,它的优先级如下(由高到低):

从命令行中读取可用的忽略规则
当前目录定义的规则
父级目录定义的规则,依次递推
$GIT_DIR/info/exclude 文件中定义的规则
core.excludesfile中定义的全局规则

Git 忽略规则匹配语法

在 .gitignore 文件中,每一行的忽略规则的语法如下:

空格不匹配任意文件,可作为分隔符,可用反斜杠转义
开头的文件标识注释,可以使用反斜杠进行转义
! 开头的模式标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义
/ 结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件
/ 开始的模式匹配项目跟目录
如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录
** 匹配多级目录,可在开始,中间,结束
? 通用匹配单个字符
* 通用匹配零个或多个字符
[] 通用匹配单个字符列表

常用匹配示例

bin/: 忽略当前路径下的bin文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件
/bin: 忽略根目录下的bin文件
/*.c: 忽略 cat.c,不忽略 build/cat.c
debug/*.obj: 忽略 debug/io.obj,不忽略 debug/common/io.obj 和 tools/debug/io.obj
**/foo: 忽略/foo, a/foo, a/b/foo等
a/**/b: 忽略a/b, a/x/b, a/x/y/b等
!/bin/run.sh: 不忽略 bin 目录下的 run.sh 文件
*.log: 忽略所有 .log 文件
config.php: 忽略当前路径的 config.php 文件

.gitignore规则不生效

.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。

解决方法就是先把本地缓存删除(改变成未track状态),然后再提交:

git rm -r --cached .
git add .
git commit -m 'update .gitignore'

你想添加一个文件到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行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。

Java项目中常用的.gitignore文件

# Compiled class file
*.class

# Eclipse
.project
.classpath
.settings/

# Intellij
*.ipr
*.iml
*.iws
.idea/

# Maven
target/

# Gradle
build
.gradle

# Log file
*.log
log/

# out
**/out/

# Mac
.DS_Store

# others
*.jar
*.war
*.zip
*.tar
*.tar.gz
*.pid
*.orig
temp/

你可能感兴趣的:(Git简介)