一、版本控制工具简介

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统,方便查看更改历史,备份以及恢复以前的版本,保证多人的协作不出问题

1. 原始的版本控制

版本控制工具的黑暗时代:

  • 最原始的版本控制是纯手工的版本控制:修改文件,保存文件副本
  • 保存副本命名随意→版本难辨新旧,不能辨别每一版的修改内容

2. 版本控制的起源:diff与patch

  • 在最初的版本控制软件出现之前,其实已经有了比较好用的源码比较与打补丁的工具:diff与patch
  • Linus Torvalds(Linux之父)也对这两个工具偏爱有佳
  • 在1991-2002年之间,即使CVS出现之后,Linus一直使用diff和patch管理着Linux的代码
  • diff与patch是用于源码版本控制中的两个最基本的概念

2.1 diff简介:diff用来比较两个文件或者目录之间的差异

image-20220418195948906

2.2 Patch简介:Patch是diff的反向操作

我们把上述差异结果保存到文件中,例如diff.txt中,那么这个diff.txt就可以用来从left.c推算出right.c的内容,或者从right.c推算出left.c的内容

image-20220418200233010

3. RCS:最早期的本地版本控制工具

RCS(Revision Control System)

  • RCS作为非常古老的版本工具,远远在SVN和已经退役的CVS之前,它的古老程度应该比Web开发的ASP前代的CGI还要久远
  • 如果想对版本管理实现方式进行深入研究的话,研究RCS是一种最为简单的入手方式
  • RCS采用把diff的集合,采用RCS自己的格式保存到磁盘中(可以通过diff -n left.c right.c 产生RCS格式的diff内容),能通过这些diff集合,重新回到文件修改的任何历史中的点

4.CVS & SVN:集中式版本控制工具

image-20220418201017293

5.Git:Linus的第二个伟大作品

5.1Git的起源

Linux之父Linus是坚定的CVS反对者,他也同样的反对SVN。2002年Linus顶着开源社区精英的口诛笔伐,选择了一个商业版本控制系统BitKeeper作为Linux内核的代码管理工具。和CVS/SVN不同,BitKeeper是属于分布式版本控制系统。

Git诞生大事记:

  • 2005年4月3日,开始开发Git
  • 2005年4月6日,项目发布
  • 2005年4月7日,Git就可以作为自身的版本控制工具
  • 2005年4月18日,发生第一个多分支合并
  • 2005年4月29日,Git的性能就已经达到了Linus的预期
  • 2005年6月16日,Linux核心2.6.12发布,那时Git已经在维护Linux核心的源代码

5.2集中式 VS 分布式

(1)记录差异还是记录快照

  • Git和其他版本控制系统(包括Subversion和近似工具)的主要差别在于Git对待数据的方法
  • 感念上来区分,其他大部分系统以文件变更列表的方式存储信息
  • 这类系统(CVS,Subversion,Perforce,Bazaar等等)将保存的信息看作是一组基本文件和每个文件随时间逐步积累的差异

image-20220418202024388

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

image-20220418202424479

(2)脆弱的中央库 VS 强壮的分布库

image-20220418202545487

5.3选择合适的版本控制工具

SVN不适合的领域:

跨地域的协同开发

对代码的高质量追求和代码门禁

Git不适合的领域:

不适合Word等二进制文档的版本控制

因为:Git无锁定/解锁模式,故不能排他式修改,整体的读授权,不能将读授权精细到目录级别

解决方案:版本库按目录拆分

6.结语:Git是什么

Git是一个版本控制工具,而且是一个开源的分布式版本控制工具

按照Linus本人的描述,Git的很多命令设计是源于BitKeeper,但是Git有更多属性

  • 极快的速度
  • 简单的设计
  • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似Linux内核一样的超大规模项目(速度和数据量)

本章小结

  • 版本控制工具的发展历史经过:原始人工维护状态,本地RCS,集中式如CVS,SVN和分布式如Git
  • 版本控制工具提供了协作开发的能力,借助它们我们可以回到任何时间的代码状态
  • 集中式版本控制工具,几乎所有的动作都需要服务器的参与,并且数据安全性与服务器关系很大
  • Git是分布式版本控制工具,除了服务器之前进行按需同步之外,所有的提交操作都不需要服务器

二、Git安装与配置

1.Linux下安装Git

Git诞生于Linux平台并作为版本控制系统率先服务于Linux核心,因此在Linux安装Git是非常方便的。可以通过不同的方法在Linux上安装Git

安装Git的两种方式:

  • 一种方法是通过Linux发行版的包管理器安装已经编译好的二进制格式的Git软件包
  • 另外一种方式就是从Git源码开始安装

1.1包管理器安装

Linux系统 Ubuntu 10.10(maverick)或更新版本,Debian(squeeze)或更新版本

1
2
$ sudo aptitude install git
$ sudo aptitude install git-doc git-svn git-email gitk

其中Git软件包包含了大部分Git命令,是必装的软件包

软件包git-svn,git-email,gitk本来也是Git软件包的一部分,但是因为有着不一样的软件包依赖(如更多perl模组,tk等),所以单独作为软件包发布

Linux系统:RHRL,Fedora,CentOS等版本:

1
2
$ yum install git
$ yum install git-svn git-email gitk

1.2源码安装

访问Git的官方网站:http://git-scm.com/ 下载Git源码包,例如:git-2.19.0.tar.gz

展开源码包,并进入到相应的目录中

1
2
$ tar -jxvf git-2.19.0.tar.bz2
$ cd git-2.19.0

安装方法写在INSTALL文件当中,参照其中的指示完成安装,下面的命令将Git安装在/usr/local/bin中

1
2
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install

安装Git文档(可选)

1
2
$ make prefix=/usr/local doc info
$ sudo make prefix=/usr/local install-doc install-html install-info

1.3命令补齐

Linux的shell环境(bash)通过bash-completion软件包提供命令补齐功能,能够实现在录入命令参数时按一下或两下TAB键,实现参数的自动补齐或提示。例如输入git com后按下TAB键,会自动补齐为git commit

将Git源码包中的命令补齐脚本复制到bash-completion对应的目录中:

1
$ cp contrib/completion/git-completion.bash /etc/bash_completion.d/

重新加载自动补齐脚本,使之在当前shell中生效:

1
$ . /etc/bash_completion

为了能够在终端开启时自动加载bash.completion脚本,需要在本地配置文件~/.bash_profile或全局文件/etc/bashrc文件中添加下面的内容:

1
2
3
if [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi

2.Windows下安装Git

目前Git提供的Windows安装包自带MinGW(Minimalist GNU for Windows,最简GNU工具集),在安装后MinGW提供了一个bash提供的shell环境(Git Bash)以及其他相关工具软件,组成了一个最简系统(Minimal SYStem),这样在Git Bash中,Git的使用和在Linux下使用完全一致

2.1安装Git

Step1:到https://git-scm.com/download/win下载Windows安装包,例如:Git-2.19.0-64-bit.exe

执行开始安装,如图:

image-20220419101536015

默认安装到C:\Program Files\Git目录中

image-20220419101553318

Step2:这里选择一些必要的组件,开源的git-lfs存在一些问题,建议把勾选去掉

image-20220419101729289

Step3:Git默认的编辑器,建议保持默认,当然你也可以选择其他的,例如 Notepad++

image-20220419101854039

Step4:在安装过程中会询问是否修改环境变量,建议选择“Use Git Bash Only”,即只在MinGW提供的shell环境中使用Git,不修改PATH环境变量,避免Git自带的工具与Windows下已有的产生冲突

image-20220419102353200

如果不清楚PATH,可以参考https://en.wikpedia.org/wiki/PATH_%28variable%29,简单来讲就是你输出一条命令的时候,系统会从PATH这个配置中寻找实现这条命令的程序在哪里,找到后就启动程序

Step5:其他后续提示可以都采取缺省配置,进行安装过程。安装完成后,我们可以在Windows任意目录下,右键单击选中“Git Bash”启动Git Bash

image-20220419102558899

可以执行git version 查看安装的git版本信息

image-20220419102609762

2.2安装TortoiseGit

在Windows下安装和使用Git有两个不同的方案,除了刚刚的Git安装包,再有一个就是基于msysGit的图形界面工具—TortoiseGit

TortoiseGit简介

TortoiseGit提供了Git和Windows资源管理器的整合,提供了Git的图形化操作界面

像其他TortoiseGit系列产品(TortoiseCVS,TortoiseSVN)一样,Git工作区的目录和文件的图标附加了标识版本控制状态的图像,可以非常直观的看到哪些文件被更改了需要提交。通过对右键菜单的扩展,可以非常方便的在资源管理器中操作Git版本库

TortoiseGit安装

安装TortoiseGit非常简单,访问网站http://code.google.com/p/tortoisegit/,下载安装包,然后根据提示完成安装

安装过程中会询问要使用的SSH客户端,缺省使用内置的TortoisePLink(来自PuTTY项目)做为SSH客户端。TortoisePLink和TortoiseGit的整合性更好,可以直接通过对话框设置SSH私钥(PuTTY格式),而无需再到字符界面去配置SSH私钥和其他配置文件

image-20220419103707176

如果你的本地同时安装了命令行的Git版本,可以通过TortoiseGit的设置对话框选中Git提供的SSH客户端,这样在下载SSH协议的代码仓库的时候,通过命令行与TortoiseGit图形界面都可以使用同一套公钥和密钥

image-20220419103949277

3.Git配置

Git有三种配置,分别以文件的形式存放在三个不同的地方。可以在命令行中使用git config工具查看这些变量

系统配置(对所有用户都适用)

存放在git的安装目录下:%Git%/etc/gitconfig:若使用git config时用–system选项,读写的就是这个文件:git config –system core。autocrlf

用户配置(只适用于该用户)

存放在用户目录下,例如Linux存放在:~/gitconfig:若使用git config时用–global选项,读写的就是这个文件:git config –global user.name

仓库配置(只对当前项目有效)

当前仓库的配置文件(也就是工作目录中的.git/config文件);若使用git config时用–local选项,读写的就是这个文件:git config –local remote.origin.url

注:每一个级别的配置都会覆盖上层的相同配置,例如.git/config里的配置会覆盖%Git%/etc/gitconfig中的同名变量

3.1配置个人身份

首次的Git设定(设定身份)

git config –global user.name “Zhang san”

git config –global user.email zhangsan123@huawei.com

这个配置信息会在Git仓库中提交的修改信息中体现,但和Git服务器认证使用的密码或者公钥密码无关

3.2文本换行符配置

假如你正在Windows上写程序,又或者你正在和其他人合作,他们在Windows上编程,而你却在其他系统上,在这些情况下,你可能会遇到行尾结束符问题,这是因为Windows使用回车和换行两个字符来结束一行,而Mac和Linux只使用换行一个字符,虽然这是小问题,但它会极大的扰乱跨平台协作


Git可以在你提交时自动的把行结束符CRLF转换成LF,而在迁出代码时把LF转换成CRLF。用core.autocrlf来打开此项功能,如果是在Windows系统上,把它设置成true,这样当迁出代码时,LF会被转换成CRLF:

1
$ git config --global core. autocrlf true

Linux或Mac系统使用LF作为行结束符,因此你不想Git在迁出文件时进行自动的转换;当一个以CRLF为行结束符的文件不小心被引入时你肯定想进行修正,把core.autocrlf设置成input来告诉Git在提交时把CRLF转换成LF ,签出时不转换:

1
$ git config --global core. autocrlf input

这样会在Windows系统上的签出文件中保留CRLF ,会在Mac和Linux系统上,包括仓库中保留LF。


如果你是Windows程序员,且正在开发仅运行在Windows.上的项目,可以设置false取消此功能,把回车符记录在库中:

1
$ git config --global core. autocrlf false

3.3文本编码配置

  • **i18n.commitEncoding 选项:**用来让git commit log存储时,采用的编码,默认UTF-8.
  • **i18n.logOutputEncoding选项:**查看git log时,显示采用的编码,建议设置为UTF-8.

image-20220419110618742

3.4与服务器的认证配置

3.4.1常见的两种协议认证方式

http / https协议认证

设置口令缓存:

1
git : config --global credential . helper store

添加HTTPS证书信任:

1
git config http. sslverify false

SSH协议认证
SSH协议是种非常常用的Git仓库访问协议 ,使用公钥认证、无需输入密码,加密传输,操作便利又保证安全性

3.4.2 ssh认证的配置过程

生成公钥:
Git工具安装成功后运行Git Bash ,在弹出的客户端命令行界面中输入下面提示的命令。(比如你的邮箱是zhangsan1123@Huawei.com)

1
$ ssh-keygen -t rsa -C zhangsan1123@huawei . com

添加公钥到代码平台:

  1. 登录代码平台
  2. 进入”Profile Settings’”
  3. 点击左侧栏的”SSH Keys”
  4. 点击”Add SSH Key” ,将刚生成的公钥文件的内容,复制到”Public Key”栏,保存即可。

生成公钥举例:

image-20220419111126099

三、Git基本命令

1 Git版本控制下的三种工程区域&文件状态

Git版本控制下的工程区域只有3种:

  1. 版本库( Repository )

    在工作区中有一个隐藏目录.git,这个文件夹就是Git的版本库,里面存放了Git用来管理该工程的所有版本数据,也可以叫本地仓库。

  2. 工作区( Working Directory )

    日常工作的代码文件或者文档所在的文件夹。

  3. 暂存区( stage )

    一般存放在工程根目录.git/index文件中,所以我们也可以把暂存区叫作索引(index) 。

Git版本控制下的文件状态只有三种:

  1. 已提交( committed )

    该文件已经被安全地保存在本地数据库中了;

  2. 已修改(modified)

    修改了某个文件,但还没有提交保存;

  3. 已暂存(staged)

    把已修改的文件放在下次提交时要保存的清单中。

2 Git常用命令

工程准备

工程克隆一git clone

查看工作区

查看工作区的修改内容一git diff

查看工作区文件状态一git status

文件修改后提交推送

新增/删除/移动文件到暂存区一git add/ git rm/ git mv

提交更改的文件一git commit

推送远端仓库一git push

查看日志

查看当前分支上的提交日志一git log

分支管理

列出本地分支一git branch

新建分支一git branch 1 git checkout -b

删除分支一git branch -d

切换分支一git checkout

更新分支一git pull

合并分支一git merge

撤销操作

强制回退到历史节点一git reset

回退本地所有修改而未提交的一git checkout

分支合并

合并目标分支内容到当前分支一git merge/git rebase

3 Git常用命令实操

3.1工程准备

git init用于在本地目录下新建git项目仓库。

执行git init后,当前目录下自动生成一-个名为.git的目录, 这代表当前项目所在目录已纳入Git管理。.git目录下存放着本项目的Git版本库,在此强烈不建议初学者改动.git目录下的文件内容。下图可知,Git仓库下的.git目录默认是不可见的,有一-定程度 上是出于防止用户误操作考虑。

image-20220419141804788

git clone用于克隆远端工程到本地磁盘

如果想从远端服务器获取某个工程,那么:

  1. 确定自己Git账号拥有访问、 下载该工程的权限
  2. 获取该工程的Git仓库URL
  3. 本地命令行执行git clone [URL]或 git 1fs clone [URL]

image-20220419141937805

**注:**如果你所在的项目git服务器支持git-lfs,对二进制文件进行了区别管理,那么克隆工程的时候务必使用git lfs clone。否则克隆操作无法下载到工程中的二进制文件,工程内容不完整。

3.2新增/删除/移动文件到暂存区

在提交你修改的文件之前,需要git add把文件添加到暂存区。

如果该文件是新创建,尚未被git跟踪的,需要先执行git add. 将该文件添加到暂存区,再执行提交。如果文件已经被git追踪,即曾经提交过的。在早期版本的git中,需要git add再提交;在较新版本的git中,不需要git add即可提交。

image-20220419142507412

git rm将指定文件彻底从当前分支的缓存区删除,因此它从当前分支的下一个提交快照中被删除。

如果一个文件被git rm后进行了提交,那么它将脱离git跟踪,这个文件在之后的节点中不再受git工程的管理。执行git rm后, 该文件会在缓存区消失。你也可以直接从硬盘上删除文件,然后对该文件执行git commit, git会自动将删除的文件从索引中移除,效果一样。

image-20220419142649937

git mv命令用于移动文件,也可以用于重命名文件。

例1:需要将文件codehunter_ nginx.conf从当前目录移动到config目录下,可执行:

1
git mv codehunter_ nginx . conf config

例2:需要将文件codehunter_ nginx.conf重命名为new_ nginx.conf, 可执行:

1
git mv config/ codehunter_ _nginx . conf config/ new_ nginx. conf

3.3查看工作区

git diff用于比较项目中任意两个版本(分支)的差异,也可以用来比较当前的索引和上次提交间的差异。

比较两个节点之间的差异

image-20220419143104996

比较两个分支之间的差异

image-20220419143117128

当前的索引和上次提交间的差异

image-20220419143124820

在diff后面加–name-status参数,只看文件列表

image-20220419143133769

git status 命令用于显示工作目录和暂存区的状态

使用此命令能看到修改的git文件是否已被暂存,新增的文件是否纳入了git版本库的管理。

下例中的信息表明: modeules/_ init_ .py已被修改并暂存, LICENSE已被修改但未暂存,README.md已被删除但未暂存,extend.txt已被新建但未跟踪。

注意:请保证能理解git status回显的每一 -行文字含义。

image-20220419143316688

3.4提交更改的文件

git commit主要是将暂存区里的文件改动提交到本地的版本库。

在此强调,提交这个动作是本地动作,是往本地的版本库中记录改动,不影响远端服务器。git commit一般需要附带提交描述信息,所以常见用法是:

1
git commit file_ name -m “commit message”

image-20220419143841548

提交成功后,git日志可查到此次提交的id和提交描述信息

image-20220419143800170

如果要一次性提交所有在暂存区改动的文件到版本库,可以执行:

1
git commit -am"commit message”

3.5查看日志

git log用于查看提交历史

默认加其他参数的话,git log会按提交时间由近到远列出所有的历史提交日志。每个日志基本包含提交节点、作者信息、提交时间、提交说明等。常用的日志命令格式: git log

git log配合不同参数具有相当灵活强大的展示功能,常见的如一name-status/-p/–pretty/–graph等等。

image-20220419144038646

3.6推送远端仓库

在使用git commit命令将自己的修改从暂存区提交到本地版本库后,可以使用git push将本地版本库的分支推送到远程服务器.上对应的分支。

成功推动远端仓库后,其他开发人员可以获取到你新提交的内容。

常用的推送命令格式:

1
git push origin branch_name

branch_ name决定了你的本地分支推送成功后,在远端服务器.上的分支名,其他人据此可以获取该分支上的改动内容。

你的本地分支名可以与推送到远端的分支名不同:

1
git push origin branch_ _name:new_ _branch_ _name

image-20220419144259562

3.7分支管理

git branch命令即可查看本地工程的所有git分支名称。

下图可见,git返回了当前本地工程所有的分支名称,其中master分支前面的“*” 表示一当前工作区所在
的分支是master。

image-20220419144417591

如果想查看远端服务器.上拥有哪些分支,那么执行git branch -r即可,返回的分支名带origin前缀,表示在远端;

如果想查看远端服务器和本地工程所有的分支,那么执行git branch -a即可。

image-20220419144454869

git. branch和git checkout -的异同:

相同点:

  • git branch和git checkout -b都可以用于新建分支(默认基于当前分支节点创建)。

区别点:

  • git branch新建分支后并不会切换到新分支;
  • git checkout -b新建分支后会自动切换到新分支。

常用的新建分支命令格式:

1
git branch new_branch_name / git checkout -b branch_name

image-20220419144742329

git branch -d和git branch -D都可以用来删除本地分支,后者大写表示强制删除。

有时候当事分支上包含了未合并的改动,或者当事分支是当前所在分支,则-d无法删除,需要使用强制删除来达到目的。

常用的删除分支命令格式:

1
git branch -d branch_ _name/git branch -D branch_ _name

删除服务器上的远程分支可以使用git branch -d -r branch__name, 其中 branch_ name为本地分支名。
删除后,还要推送到服务器.上才行,即git push origin : branch_ name

image-20220419145020378

git checkout 命令除了创建分支,还用来切换分支,当然比较官方的叫法是“检出”

有时候,当前分支工作区存在修改而未提交的文件,与目的分支上的内容冲突,会导致checkout切换失败,这时候,可以使用git checkout -f进行强制切换。

常用的切换分支命令格式:

1
git checkout branch_ name

git checkout对象可以是分支,也可以是某个提交节点或者节点下的某个文件。

image-20220419145138271

git pull的作用是,从远端服务器中获取某个分支的更新,再与本地指定的分支进行自动合并。

常用的切换分支命令格式:

1
git pull origin remote_ branch:local_ branch

如果远程指定的分支与本地指定的分支相同,则可直接执行git pull origin remote_branch

image-20220419145246969

git fetch的作用是,从远端服务器中获取某个分支的更新到本地仓库。

注意:与git. pull不同,git fetch在获取到更新后,并不会进行合并(即git merge) 操作,这样能留给用户一个操作空间,确认git fetch内容符合预期后,再决定是否手动合并节点。

常用的获取远端分支更新命令格式:

1
git fetch origin remote_branch: local_branch

如果远程指定的分支与本地指定的分支相同,则可直接执行

1
git fetch origin remote_branch

image-20220419145429654

3.8分支合并

git merge命令是用于从指定的分支(节点)合并到当前分支的操作

git会将指定的分支与当前分支进行比较,找出二者最近的一个共同节点base,之后将指定分支在base之后分离的节点合并到当前分支上。分支合并,实际上是分支间差异提交节点的合并。

常用的切换分支命令格式:

1
git merge branch_name

image-20220419145712538

git rebase用于合并目标分支内容到当前分支。

git rebase这条命令用于分支合并,git merge也是用于分支合并。如果你要将其他分支的提交节点合并到当前分支,那么git rebase和git merge都可以达到目的。

常用的合并命令格式:

1
git rebase branch_ name

image-20220419145847505

3.9强制回退到历史节点

git reset通常用于撤销当前工作区中的某些git add/commit操作, 可将工作区内容回退到历史提交节点。

常用的工作区回退命令格式:

1
git reset commit_id

image-20220419145954589

git checkout.用于回退本地所有修改而未提交的文件内容。

git checkout.这是条有风险的命令,因为它会取消本地工作区的修改(相对于暂存区),用暂存区的所有文件直接覆盖本地文件,达到回退内容的目的。但它不给用户任何确认机会,所以谨慎使用

常用的回退命令格式:

1
git checkout.

如果仅仅想回退某个文件的未提交改动,可以使用git checkout -filename来达到目的;如果想将工具区回退(检出)到某个提交版本,可以使用git checkout commit_ _id。

image-20220419150159795