Git 版本控制
[ 本文档由黄超(Seeker),使用 MarkDown 构建而成,转载请注明出处 ]
1. 什么是 Git
Git 是当今世界上最先进/最好用的分布式版本控制系统,没有之一
2. 什么是版本控制系统?
版本控制 — 《维基百科》
版本控制是一种软件工程技巧,能在软件开发的过程中,确保由不同人所编辑的同一个代码文件都得到同步.
版本控制能使项目的设计者,将项目恢复到之前任意的状态,这种选择权在设计过程中特别重要.
理论上所有的信息记录都可以加上版本控制:利用版本控制来追踪、维护源码、文件以及配置文件等等的改动
3. 版本控制发展史
3.1 文件名方式
早年的软件开发过程,代码管理以手动和邮件等形式,文件命名及保存存在问题
如图 : 文件命名方式
[外链图片转存失败(img-CiACLTKs-1568082257946)(./imgs/文件命名.png)]
如图 : 毕业论文版本
[外链图片转存失败(img-Vbjs3jE9-1568082257947)(./imgs/01.jpg)]
3.2 集中式
包括库和工作区两部分:在工作区编码,然后上传至库的方式,完成多人协作。
问题:工作机与库机,需要联网才能控制版本,传输速度较慢。
[外链图片转存失败(img-2p8KYFnE-1568082257948)(./imgs/02.png)]
3.2 分布式
每台电脑都有,工作区和库,可以自己控制版本。数据更加安全,有逻辑上的中心。
[外链图片转存失败(img-avr8TZVd-1568082257949)(./imgs/03.png)]
4. 常见版本控制系统
如图 : 版本管理器的发展史
[外链图片转存失败(img-r4x0d6VV-1568082257949)(./imgs/04.png)]
这张图上,分成了四个时期 :
● 史前时期:1982年的RCS。现在你可能还能在Unix的发布包中找到它。
● 古典时期:1990年的CVS自身缺陷已经过时;1985年的PVCS、1992年的clearcase(费用昂贵、功能复杂沿用至今);微软VVS反人类;Perforace(广泛,谷歌内部最大代码管理器)
● 中世纪时期:SVN解决了CVS的问题,集中式领域王者。AccuRev(支持分支合并让很多公司拜托cvs和clearcase)。
● 文艺复兴:BitKeeper(SUN公司大量使用),2002年Linux内核使用BitKeeper,2005年闭源时有人试图破解BitKeeper,于是出现了Git。
Git 问世
Git的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的(两周内 用C写完),Linus不仅仅给出一个原始设计,并在向世人介绍Git时强烈批评了CVS和SVN等,Git消除了分支和合并的恐惧。很多大型开源项目由SVN迁移至Git。
2008年GitHub也成为世界最大的SCM系统(软件配置管理),它使用的就是Git版本库的技术.从此Git成为版本控制系统的主流。
GitHub上的著名项目 :
Linux内核、安卓、jQuery、Bootstrap、Ruby …
5. Git 的安装 和 基本配置
5.1 安装
Linux 安装
二进制包(在线)
yum -y install git // RedHat系列
apt-get git install // Debian系列
源码包(官网下载)
Windows 安装
Git 在 Windows 使用模拟环境 msysgit
下载地址:
https://git-for-windows.github.io/
注意:如果想让 windows 作为 git 服务器则需要搭建ssh服务。
本教程使用 windows 版本来做演示 :
安装步骤: (如果你不熟悉每个选项的意思,请保持默认的选项)
- 以
管理员方式
运行安装包,选择是
安装
[外链图片转存失败(img-tgoDvbgi-1568082257950)(./imgs/git00.png)]
- 同意协议条款
[外链图片转存失败(img-5UTMLSVY-1568082257951)(./imgs/git01.png)]
- 选择安装选项: 安装Git Bash和Git GUI,创建图标(可选)
[外链图片转存失败(img-NrX0HvLc-1568082257951)(./imgs/git02.png)]
- 选择 Git 的默认编辑器 (默认为vim,可选取自己熟悉的编辑器)
[外链图片转存失败(img-r7Rw4hfr-1568082257952)(./imgs/git03.png)]
- 选择:
git form the command line and also form 3rd-party software
(GIT命令行和第三方软件)
[外链图片转存失败(img-XY8CQ7tc-1568082257953)(./imgs/git04.png)]
- 选择: 选择 OpenSSL 传输 HTTPS 的信息
[外链图片转存失败(img-Eytz05Rv-1568082257954)(./imgs/git05.png)]
- 选择 : 换行符默认为 windows 环境为主
[外链图片转存失败(img-AOj6IXDk-1568082257955)(./imgs/git06.png)]
- 选择 : MinTTY 默认终端
[外链图片转存失败(img-uf3rozOd-1568082257955)(./imgs/git07.png)]
- 选择 : 系统文件缓存 和 git 的管理器(需要.net v4.5.1)
[外链图片转存失败(img-xLZuk4pG-1568082257956)(./imgs/git08.png)]
- 实验选项,暂不选择
[外链图片转存失败(img-Mtr3QP6Y-1568082257957)(./imgs/git09.png)]
- 点击 Install 安装,开始安装
[外链图片转存失败(img-EUKJRGZb-1568082257958)(./imgs/git10.png)]
- 安装完成,选择运行
git bash
工具
[外链图片转存失败(img-zbS0Vo3d-1568082257958)(./imgs/git11.png)]
- Git 运行界面 (命令行提示的是 你电脑的用户名)
[外链图片转存失败(img-XwPC8K4s-1568082257959)(./imgs/git12.png)]
5.2 配置
无论 Linux 还是 Windows,安装完成后都要初始化
# 查看 git 版本
git --version
# 查看配置
git config -l
# 设置用户名和邮箱
git config [--global] user.name "Your Name"
git config [--global] user.email "your@email.com"
# 中括号内的参数:
# --local 本地
# --system 系统
# --global 全局
# 无参,则为当前库配置身份
# 如:
git config --global user.name "徐凯"
git config --global user.email "765229854@qq.com"
6. Git常用操作
6.1 生成新的版本库
- 新建空目录
- 进入该目录—单击右键—选择
Git Bash Here
- 弹出 git 的命令行工具
- 初始化该目录为版本仓库,键入
git init
- 显示
Initialized empty Git repository in xxxxxx
ls -a
查看该目录下出现.git
的隐藏目录,即版本库- 初始化完成
6.2 添加文件到版本库
- 新建文件
1.txt
- 查看当前版本状态
# 查看当前版本状态
git status
-
红字提示有文件未跟踪(未加入版本控制)
-
在命令行内输入以下,添加文件至版本库:
# 添加文件至缓存区
git add 1.txt
# 再次查看版本库状态
git status
# 提示有要提交的修改,有一个新增的文件
# 提交文件至版本库
# 直接提交
# 回车后,会打开指定的编辑器编写描述信息
git commit
# 填写完描述信息,保存关闭即可
# 或者带参数 -m 直接写入提交的描述信息
git commit -m "新增1.txt"
# 新建 2.txt / 3.txt
# 多文件添加
git add 2.txt 3.txt
# 将缓存区的多次添加一次提交
git commit -m "新增2.txt+3.txt"
# ps:
# 添加所有改动过的和新增的文件到缓存区 (不包括被删除的文件)
git add .
# 添加所有文件到缓存区
git add --all
git add -A
-
执行完后,提示提交完成
-
查看提交后的版本库状态:
$ git status
On branch master
nothing to commit, working tree clean
- 至此,最简单的添加文件,到版本库的操作已完成
— PS部分: Git 实现原理 —
- 工作区 : 就是程序员日常编写代码的文件夹
- 版本库 : 负责代码版本控制,就是
.git
隐藏目录 - 版本库原理图 :
[外链图片转存失败(img-ZCYk6H6R-1568082257960)(./imgs/05.png)]
版本库包括:暂存区(index/stage),HEAD(指针),分支(默认为 master 主分支)等。
文件提交至版本库总共分两步:
git add filename
# 添加至 stage 缓存区git commit -m "描述"
# 将 stage 的内容提交至版本库的 master 分支
实验:
- 修改文件 >>> 查看状态
- 添加到缓存区 >>> 查看状态
- 再次修改 >>> 提交 >>> 查看状态
实验过程如下,观察理解 Git 实现机制:
- 修改文件 >>> 查看状态:
- 修改
1.txt
文件的内容,添加一行111
,查看状态:git status
- 提示:
Changes not staged for commit:
(工作区的修改,还没有提交到缓存区) - 显示红字,
modified: 1.txt
- 修改
- 添加到缓存区 >>> 查看状态:
- 添加到缓存区
git add .
- 查看状态:
git status
- 提示:
Changes to be committed
(要提交的修改。表示已提交到缓存区,待提交到版本库) - 显示绿字:
modified: 1.txt
- 添加到缓存区
- 再次修改 >>> 提交 >>> 查看状态
- 继续修改文件:
1.txt
文件的内容,添加一行222
- 提交版本库:
git commit -m "第1次修改 1.txt"
- 再次查看状态:
git status
- 提示:
Changes not staged for commit
(表示工作区中,还有修改记录,没有被提交到版本库之中) - 显示红字:
modified: 1.txt
- 继续修改文件:
说明:
-
第 3 步的提交,只是提交了缓存区内,已经缓存过的内容(即,第二步的
add .
操作) -
第 3 步中的第二次修改文件内容的记录,并没有添加到缓存区,所以版本库与工作区文件不一致
-
此时需要将第二次的修改记录,添加到缓存区,再次提交即可
git add.
git commit -m '第2次修改 1.txt'
git status
6.3 查看文件修改状态相关
# 查看当前版本状态(是否修改)
git status
修改文件,测试区别:
# 当 工作区/缓存区/版本库 都一致时,使用以下命令不会有任何提示
git diff # 工作区 与 缓存区的区别
git diff --cached # 缓存区 与 版本库的区别
git diff HEAD # 工作区 与 版本库的区别
# 更改工作区的 1.txt,并将更改添加到缓存区
# 然后再次更改工作区的 1.txt,并保存文件的更改
# 再次测试以上三条命令
6.4 Git 日志
# 查看提交历史
git log
# 以简短的方式查看提交日志
git log --oneline
# 行为日志,显示所有提交,回滚等..
git reflog
# 显示缓存区的所有文件
git ls-files
6.5 版本回退
# 将当前版本重置为 HEAD(通常用于清空缓存区,或 merge 失败回退)
git reset --hard HEAD
git reset --hard HEAD^ # 回退上一个版本
git reset --hard HEAD^^ # 回退上两个版本
git reset --hard HEAD~n # 回退上n个版本
# 回退到指定版本,commitid 可根据日志获取,'<>'内的id必填
git reset --hard <commitid>
6.6 撤销
目的:将尚未提交至版本库的修改撤回
- 情况一 : 文件修改后,尚未添加至缓存区
# 修改 1.txt 的内容:添加一行
# 在工作区撤销文件的修改
git checkout -- filename
# 注意'--'不可缺少。没有'--',就变成了“切换到另一个分支”的命令
- 情况二 : 文件修改后,已添加至缓存区
# 修改 1.txt 的内容:添加一行,并添加到缓存区
# 撤回添加至缓存区的修改
git checkout HEAD filename
6.7 Git 删除
- 删除文件和版本库记录
# `rm`命令只会删除工作区的文件,不会删除记录
rm 2.txt
git status # 提示2.txt 被删除,是否需要提交到版本库
# 如果删错文件,则可以撤回删除(因为此时的删除操作,还未写入到缓存区)
git checkout -- 2.txt
# 将 2.txt 的删除记录写入到缓存区,并删除文件
git rm 2.txt
# 如果还未提交,想要撤回,可使用 `git checkout HEAD 2.txt`
# 提交 2.txt 的删除记录到版本库中
git commit -m '删除2.txt'
- 删除缓存区的文件
# 新建 demo.html 文件,并加入到缓存区
git add demo.html
git status # 提示有新文件等待提交
# 将 demo.html 文件移出缓存区,但不删除(文件还会保留在工作区)
git rm --cached demo.html
git status # 提示工作区有文件等待 add
# 重新将 demo.html 加入到缓存区
# 将缓存区中的 demo.html 文件移出并删除(工作区不保留文件)
git rm -f demo.html
- 其他删除
# 新建 aaa/111.txt
# 新建 aaa/bbb/111.txt
# 将文件添加到版本库中
git add .
git commit -m '添加aaa目录及文件'
# 递归删除目录(删除的是,已被记录到版本库的目录)
git rm -r 目录名
git commit -m '删除aaa目录'
6.8 Git 流程图
[外链图片转存失败(img-zQTTRHL1-1568082257961)(./imgs/Git流程图.png)]
7. 远程仓库
7.1 得到远程的版本库
可以使用两种方式来得到远程版本库:
- 在某个指定的文件夹下使用,即可得到远端版本库及代码
git clone <远端版本库url> <本地存放该库的文件夹名>
- 手动添加版本库,并拉取文件
# 初始化本地仓库
git init
# 添加远程版本库 <remote>可自行取名,默认origin
git remote add <remote> <url>
# 查看远程版本库信息
git remote -v
# 查看指定远程版本库信息
git remote show <remote>
# 删除远程remote链接
git remote remove <remote>
# 下载代码及快速合并
git pull <remote> <branch>
7.2 推送分支代码
得到远端版本库后,可以在本地按正常的步骤编辑 :
新建或改动文件–>添加至缓存区–>提交到版本库
此时,要想将本地版本库发给远端,只有commit提交是不够的.
还需要下面的操作 :
git push <remote> <branch>
# 上传代码及快速合并
执行以上代码,会有报错 : 无法直接推送到远端的主分支
此时,可以曲线救国,推送自己的分支到远端即可 :
git push origin master:dev
此时,推送成功!
8. Git 分支管理
8.1 查看版本库分支
# 显示本地分支
git branch
# 显示所有分支
git branch -a
# 切换到指定分支或标签
git checkout 分支名/标签名
8.2 创建分支/删除分支
# 新建分支
git branch 分支名
# 创建并切换到dev分支
git checkout -b dev
# 删除本地分支 -D 强制删除
git branch -d 分支名
8.3 合并分支
要将 B分支 合并到 A分支里
请切换到A分支内,合并B分支的操作在A分支内进行
# 合并分支到当前分支
git merge 分支名
8.4 解决合并冲突
多分支修改同一文件,合并可能出现冲突。冲突部分用<<<===>>>表示
[外链图片转存失败(img-25qXMN7Y-1568082257962)(./imgs/06.png)]
解决方法:
先手动修改冲突部分,再次提交即可。
8.5 分支管理策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
[外链图片转存失败(img-UZkV41B4-1568082257963)(./imgs/07.png)]
8.6 多人协作
多人协作的工作模式通常是这样:
首先,可以试图用git push origin <branch-name>
推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull
试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
9. 使用代码托管系统
市面上有名的Git托管系统 :
9.1 码云 生成SSH密钥及使用
- 本地打开git bash ,
cd
切换到用户的家目录 - 使用
pwd
来查看目录是否正确 - 使用
ssh-keygen.exe -t rsa
来生成SHA256的SSH密钥(按回车确认即可) cd .ssh/
切换至ssh目录ls
查看目录下文件cat id_rsa.pub
查看生成的密钥,并复制- 打开码云,登录自己的账户.点选个人资料
- 选择SSH公钥
- 在添加公钥界面,将刚刚复制好的密钥粘贴进来,再自己取一个该密钥的名字,以便于区分管理
- 本地新建一个目录,用于拉取远端版本库
- 使用
git init
初始化该目录 - 使用
git remote add origin <SSH地址>
来添加远程版本库 - 使用
git remote -v
来查看远程版本库信息 - 使用
git pull origin master
来拉取版本库及代码
9.2 GitHub 生成SSH密钥及使用
- 运行 git Bash 客户端,输入如下代码:
$ cd ~/.ssh
$ ls
- 这两个命令就是检查是否已经存在 id_rsa.pub 或 id_dsa.pub 文件,如果文件已经存在,那么你可以跳过步骤2,直接进入步骤3
- 创建一个 SSH key
$ ssh-keygen -t rsa -C "your_email@example.com"
- 代码参数含义:
-t
指定密钥类型,默认是 rsa ,可以省略。-C
设置注释文字,比如邮箱。-f
指定密钥文件存储文件名。
- 添加你的 SSH key 到 github上面去
$ cat ~/.ssh/id_rsa.pub
- 测试一下该SSH key 在git Bash 中输入以下代码
$ ssh -T git@github.com
- 显示类似如下,表示成功:
Hi username! You've successfully authenticated
git clone 远程git仓库地址
10. 文件忽略
想要工作区的某些文件,不受版本的控制,可使用.gitignore
文件进行忽略.
!(注意, .gitignore文件 是没有名字的文件.)
规则:
- 以斜杠
/
开头表示目录 - 以星号
*
通配多个字符 - 以问号
?
通配单个字符 - 以方括号
[]
包含单个字符的匹配列表 - 以叹号
!
表示不忽略(跟踪)匹配到的文件或目录
配置文件是按行 从上到下 进行规则匹配的,
这就意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效.
11. 使用 TortoiseGit 操作 Git
TortoiseGit 俗称 [ GIT小乌龟 ]
Git 常用命令速查
- master 默认主分支
- dev 默认开发分支
创建版本库
# 初始化本地git版本库(创建新仓库)
git init
# 配置用户名
git config --global user.name "xxx"
# 配置邮件
git config --global user.email "xxx@xxx.com"
#查看当前配置列表
git config --list
# clone远程仓库
git clone <url>
修改、提交、删除
# 添加index.php文件到缓存区
git add index.php
# 添加所有改动过的文件到缓存区
git add .
# 添加所有文件到缓存区
git add --all
# 提交缓存区内的文件(回车后需要键入描述:wq保存退出)
git commit
# 提交缓存区内的文件,并提供描述
git commit -m "描述"
# 将add和commit合为一步
git commit -am '描述'
# 合并最后一次提交(用于反复修改)
git commit --amend -m 'xxx'
# 删除index.php文件
git rm index.php
# 将index.php文件移出缓存区,但不删除( -r * 递归目录)
git rm --cached index.php
# 将缓存区中的1.html文件移出并删除
git rm -f 1.html
查看
# 查看当前版本状态(是否修改)
git status
# 查看所有添加到缓存区的变更(工作区与版本库的区别)
git diff
# 查看工作区文件和库文件区别
git diff index.php
# 查看所有已添加到缓存区,但还未commit的变更(缓存区与版本库的区别)
git diff --cached
# 查看提交历史
git log
# 以简短的方式查看提交日志
git log --oneline
# 行为日志,显示所有提交,回滚等..
git reflog
# 显示缓存区的所有文件
git ls-files
回退 与 撤销
# 将当前版本重置为HEAD(通常用于merge失败回退)
git reset --hard HEAD
git reset --hard HEAD^ # 回退上一个版本
git reset --hard HEAD^^ # 回退上两个版本
git reset --hard HEAD~n # 回退上n个版本
# 回退指定版本,commitid根据log获取
git reset --hard <commitid>
# 在工作区撤销文件的修改
git checkout --filename
# 撤回添加至缓存区的修改,不指定filename则撤回所有
git checkout HEAD [filename]
分支操作
# 获取远程分支master并merge到当前分支
git pull origin master
# 显示本地分支
git branch
# 显示所有分支
git branch -a
# 切换到指定分支或标签
git checkout 分支名/标签名
# 新建分支
git branch 分支名
# 删除本地分支 -D 强制删除
git branch -d 分支名
远程协作
# 添加远程版本库
git remote add <remote> <url>
# 查看远程版本库信息
git remote -v
# 查看指定远程版本库信息
git remote show <remote>
# 删除远程remote链接
git remote remove <remote>
# 重命名远程链接名
git remote rename <old> <new>
# 下载代码及快速合并
git pull <remote> <branch>
# 上传代码及快速合并
git push <remote> <branch>
# 将本地的远端库合并
git merge origin master
# 将远端库获取本地但不合并
git fetch origin
上传3步骤
git add .
git commit -m ‘描述’
git push :
新建文件夹的话 需要 5步骤 dev 就写dev remote 别名 new branch= master
git add .
git remote add
git commit -m ‘描述’
git pull //会报错 问题不大
branch -d 分支名
> 远程协作
```sh
# 添加远程版本库
git remote add <remote> <url>
# 查看远程版本库信息
git remote -v
# 查看指定远程版本库信息
git remote show <remote>
# 删除远程remote链接
git remote remove <remote>
# 重命名远程链接名
git remote rename <old> <new>
# 下载代码及快速合并
git pull <remote> <branch>
# 上传代码及快速合并
git push <remote> <branch>
# 将本地的远端库合并
git merge origin master
# 将远端库获取本地但不合并
git fetch origin
Thanks Bye Bye
讲师.黄超.Seeker
更多信息,详见: Pro Git(中文版)
上传作业3步骤
git add .
git commit -m ‘描述’
git push :
新建文件夹的话 需要 5步骤 dev 就写dev remote 别名 new branch= master
git add .
git remote add
git commit -m ‘描述’
git pull //会报错 问题不大
git push :