# Git前言

本文是Git Book,以及其他关于Git博客的综合笔记,为什么会有这篇笔记呢?笔者虽然使用Git有很长时间了,但是深感只是略了解Git的基础功能,时间长了便想系统性的研究下Git,于是便有了这几篇笔记。

# 版本管理是什么

想象这样一个情景,项目要用excel表来统计项目的出勤,进度和成本,你可能先做好一个excel表格式,在你统计项目的进度的同时分发给财务和人力资源,到最后就会出现三个版本,你需要把财务和人力资源的表合并到你自己的excel表中,此时你就要对比财务和人力资源在你的基础上改了啥,然后你要把改动的部分复制过来,如果表很大的话这个工作量就大了,此时你就会想,有没有一个软件,能帮助记住每次文件的改动,在合并的时候也可自动合并,不用再把excel整个传来传去,而这个软件基本就是版本管理软件。

# 版本管理的演变

# VCS出现前

  • 用目录区别不同的版本
  • 公共文件容器被覆盖
  • 成员沟通成本高,代码集成效率低下

# 集中式VCS

  • 有集中的版本管理服务器
  • 具备文件版本管理和分支管理能力
  • 继承效率明显提高
  • 客户端必须时刻和服务器相连

# 分布式VCS

  • 服务端和客户端都有完整的版本库
  • 脱离服务端,客户端照样可以管理版本
  • 查看历史和版本比较等多数操作,都不需要访问服务器,比集中式VCS更能提高版本管理效率

# 集中式vs分布式

上面提到集中式VCS和分布式VCS他们有啥区别呢?集中式VCS,版本库是集中存放在中央服务器,干活前将最新版本从中央服务器上下载下来,干完活再推送给中央服务器。它下载和推送的过程需要有网络连接中央服务器。

而分布式VCS,每个人电脑上都有一个完整的版本库,当多人协作时,会向一台中央服务器(github,gitlab)拉取或推送,同时会合并每个人不一样的地方。

# Git的优势

Git在保存和处理各种信息的时候与其他版本控制系统有很大差异,下面就来看看Git有什么不同。

# 直接记录快照

Git和其他版本控制系统最大的差别是Git关心文件数据整体是否发生变化,而其他系统值关心文件内容的差异,实现上来说,Git是直接记录文件的快照,而其他版本控制系统(CVS,Subversion,Perforce,Bazaar等)则是记录基本文件和每个文件随时间逐步累积的差异。请看其他版本控制系统的原理图(图片出自Git官网):

OtherVersionControl

而Git的原理如下:

GitTheory

从图看出Git并不保存文件前后变化的差异数据,Git把变化的文件制成快照后,记录在一个微型文件系统中,每次提交更新,会检查一遍所有文件的指纹信息并作一个快照,然后保存一个指向这次快照的索引,为了提高性能,如果文件没有变化,Git不会再次保存,而只是对上次保存的快照作一次链接。

# 几乎所有操作都是在本地执行

由于Git是分布式的,每个客户端都保存一份完整的仓库,这让你每次commit无需联网,当使用其他版本管理系统连接网络延时的中央服务器时,你可能用零点几秒提交到本地仓库中,同时可以让你离线操作,只有想获取别人的版本或将自己版本推送给别人时才同中央服务器连接操作。

# 保证完整性

Git中所有的数据在存储前都计算校验和,然后以校验和来引用,而且这个功能构建在Git底层,是构成Git哲学不可或缺的一部分,这意味着当你修改一个文件,或者在传送过程中丢失信息时,Git能立刻发现。

Git用以计算校验和的机制叫SHA-1散列(Hash,哈希)。它是一个由40个十六进制字符组成,基于Git中文件内容或目录结构计算出来。Git中使用该哈希值很多,比如Git数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。

# Git一般只添加数据

常用的Git操作大多仅仅是把数据添加到数据库,因为任何一种不可逆的操作,比如删除都会使回退或重现历史版本变得困难重重,同别的VCS一样,未提交更新可能会丢失信息,但是一旦你提交快照到Git中,就再难以丢失数据。

# 基于Git的服务

# Github

Github是一个面向开源及私有软件项目的托管平台,它的独特之处是为一个项目贡献代码的简易,点击项目站点的fork按钮,然后将代码检出并将修改加入到刚才分出的代码库中,最后通过内建的"pull request"机制向项目负责人申请代码合并。

# Gitlab

Gitlab是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务。它使用Ruby语言写成,一些部分用Go语言重写,web框架使用RubyOnRails。

# Git的安装和配置

# 安装

Git官网下载安装包并安装。

# 配置

Git提供了一个叫做git config的工具来(实际是git-config命令)专门配置和读取相应的工作环境变量。

# config的作用域

git中的config有三个级别分别是local,global和system。每个级别都有自己的配置文件,local级别的配置文件在每个仓库的.git/config位置;global级别的配置文件在系统用户目录下的.gitconfig文件中,windows下是C:\Users\用户名\.gitconfig,linux下是~/.gitconfig;system级别的配置文件在/etc/gitcofig,在windows下是git的安装目录中的mingw64下的/etc/gitconfig。

git config --local ...
//只对该仓库有效
git config --global ...
//对登陆计算机当前用户的所有仓库有效
git config --system ...
//对登陆计算机的所有用户的所有仓库有效
1
2
3
4
5
6

如果不指定作用域默认为local。这三个级别优先级从高往低依次是:local,global,system,依次去查找每个级别的配置文件。

# 配置user信息

git config --global user.name 'yourname'
git config --global user.email 'youremail'
1
2

# 配置代理信息

git config --global http.proxy http://127.0.0.1:11000
git config --global https.proxy http://127.0.0.1:11000
1
2

# 配置文本编辑器

git config --global core.editor emacs
1

指定的是默认使用的文本编辑器。Git在commit或有些时候需要输入一些文本信息,会自动调用一个外部文本编辑器,默认会使用操作系统指定的默认编辑器,也可用上面命令自己制定。

# 配置差异分析工具

git config --global merge.tool vimdiff
1

在解决合并冲突时使用哪种差异分析工具,Git还有kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge和opendiff等合并工具。

# 显示config的配置

git config --list AcitionDomain
比如
git config --list --local
或者使用缩写
git config -l --global

查看所有配置信息,依次是系统级别,用户级别,仓库级别
git config -l

1
2
3
4
5
6
7
8
9

# config中的增删改查

//增加
git config --global --add configName configValue
//删除
git config --global --unset configName
//修改
git config --global configName configValue
//查看
git config --global configName
1
2
3
4
5
6
7
8

# 第一个Git仓库

本节的操作先不要管每条命令的具体细节,只需知道每条命令的作用,来跟随创建过程即可。

# 创建本地仓库

//方法一:在已有目录创建
cd CodeFolder
git init
//方法二:创建新的目录并建立仓库
git init CodeFolder
1
2
3
4
5

# Git服务器操作

前面我们只是在本地创建一个仓库,如果我们想把我们的仓库共享给其他人,需要将仓库托管到第三方服务器上,国外比较好的是github,国内是码云平台。不管哪个平台,都有详细的文档,具体操作可以参照相应的文档,这里以github为例。

# 连接服务器

使用git连接这些服务器一般有两种方式,一种是使用在该平台注册的账号密码,另一种是将自己的本地ssh公钥提前放到该类平台上面,这样做的好处是免去了每次提交都要输入账号密码,安全省时省力。使用账号密码相信大家都会,这里提下使用ssh公钥的关键操作:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
1

接下来会提示将key保存到目录和输入保护密钥密码,直接输入回车就是默认位置和无密码。下面我们将目录下的id_rsa.pub保存到github上面。

saveSSHKey

# 和服务器同步

本地仓库和服务器仓库同步也分两种情况,第一种就是本地没有服务器的仓库,这种情况下将服务器的仓库克隆到本地,修改完代码后提交即可:

git clone https://github.com/RepoUrl
git add .
git commit -m "commit message"
git push origin master
1
2
3
4

第二种情况就是像之前先在本地创建仓库,然后再同步到服务器上:

cd RepoFolder
git remote add origin https://github.com/RepoUrl
git push -u origin master
//-u是--set-upstream的省略形式

在这种情况里,如果服务器的仓库有Readme或者其他文件,提交时会存在冲突:

hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

此时用户可以选择是保留线上的文件,还是舍弃线上文件:

舍弃线上文件,不推荐该方式,做之前确保你知道自己在做啥
git push origin master -f

保留线上文件
git pull origin master

它会提示merge错误:
fatal: refusing to merge unrelated histories

这说明我们已经将服务器端的master拉取到本地,但是尚未和本地合并,它显示本地历史与远端没有关联,这时我们需要合并下:
git merge origin/master --allow-unrelated-histories

接下来便可以向服务器推送了:
git push -u origin master
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29