# Git 学习
# 版本控制
版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。
版本控制最主要的功能就是追踪文件的变更。
版本控制包括:检入检出控制、分支和合并、历史记录。
流程:创建配置项 -> 修改状态为 “草稿” 的配置项目 -> 技术评审领导审批 -> 正式发布 -> 变更
# 集中式 VS 分布式
# 集中式:
版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。
集中式版本控制系统必须联网才能工作。
# 分布式:
分布式版本控制系统根本没有 “中央服务器”,每个人的电脑上都是一个完整的版本库。
和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库。
分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一个完整的 Git 仓库。
# Git 介绍
# 简介
Git 是目前世界上最先进的分布式版本控制系统。
中文文档
# 功能特性
- 从服务器上克隆完整的 Git 仓库(包括代码和版本信息)到单机上。
- 在自己的机器上根据不同的开发目的,创建分支,修改代码。
- 在单机上自己创建的分支上提交代码。
- 在单机上合并分支。
- 把服务器上最新版的代码 fetch 下来,然后跟自己的主分支合并。
- 生成补丁(patch),把补丁发送给主开发者。
- 看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。
- 一般开发者之间解决冲突的方法,开发者之间可以使用 pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。
# 优点:
- 适合分布式开发,强调个体。
- 公共服务器压力和数据不会太大。
- 速度快、灵活。
- 任意两个开发者之间可以很容易解决冲突。
- 离线工作
# 缺点
资料少
学习周期相对长
不符合常规思维
代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。
# Git 使用
# 安装
最早 Git 是在 Linux 上开发的,很长一段时间内,Git 也只能在 Linux 和 Unix 系统上跑。不过,慢慢地有人把它移植到了 Windows 上。现在,Git 可以在 Linux、Unix、Mac 和 Windows 这几大平台上正常运行了
# Linux 安装
首先,你可以试着输入 git
,看看系统有没有安装 Git:
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git
像上面的命令,有很多 Linux 会友好地告诉你 Git 没有安装,还会告诉你如何安装 Git。*
Debian 或 Ubuntu Linux 安装: sudo apt-get install git
老一点的 Debian 或 Ubuntu Linux,要把命令改为 sudo apt-get install git-core
# windows 安装
官网下载 https://git-scm.com/downloads 按默认安装即可
详细安装教程
安装完成后,还需要进一步设置,在命令行输入
1 | git config --global user.name "Your Name" |
由于 git 是分布式版本控制系统,每个机器需要有一个识别标志:名字与邮箱地址
git config
命令的 --global
参数,用了这个参数,表示你这台机器上所有的 Git 仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和 Email 地址。
# 创建版本库
什么是版本库呢?版本库又名仓库,英文名 repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被 Git 管理起来,每个文件的修改、删除,Git 都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以 “还原”。
创建空目录:
1 | mkdir repositoryname |
pwd
命令用于显示当前目录。
mkdir
同 dos 命令 指定目录不存在则创建目录
cd
同 dos 命令 打开相应目录
<span style="color:red"> 如果你使用 Windows 系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。</span>
通过 ``git init` 命令把这个目录变成 Git 可以管理的仓库
1 | git init |
新建仓库之后会看到当前目录下出现 .git
目录,这个目录是 Git 来跟踪管理版本库的。
# 添加文件到仓库
版本控制系统只能跟踪文本文件的改动,对于文件夹不感知。
编写一个 readme.txt
文件:
Git is a version control system
Git is free software
需要将文件放到仓库的目录下(子目录也可)
<span style="color:red"> 在使用命令之前,需要注意的是,一般我们使用 git bash 窗口进行输入命令,因为其比 CMD 支持的功能更多一点,在仓库当前目录右键即可看到 Git Bash Here
,点击即可。</span>
第一步使用命令 git add
+ 文件名(带扩展名),将文件添加到仓库:
1 | git add readme.txt |
对 unix 而言,只要没有提示出错就是好消息
第二步,使用命令 git commit
将文件提交至仓库
1 | git commit -m "create a readme.txt" |
简单解释一下 git commit
命令, -m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
提交成功会有提示:
1 file changed
即一个文件被改动
2 insertions
插入了两行内容
之所以分两步上传文件至仓库是因为:多次添加一次提交,方便快捷
# Git 常用指令
Git status
显示仓库当前状态,如果有文件修改且未提交,会有提示
1 | Changes not staged for commit: |
- uncommited:已有的,刚被修改尚未提交的
- untracked:未跟踪的,不参与版本控制
git diff
查看改动内容(掌握工作区状态
1 | git diff |
git log
查看 git 日志来查看之前改动信息
1 | git log |
git log --pretty=oneline
简化信息:
1 | git log --pretty=oneline |
在 git 中,(HEAD)表示当前版本,HEAD^ 表示上一版本,HEAD~n 上 n 个版本
如果想要回溯版本,输入命令
git reset --hard HEAD^
1 | git reset --hard HEAD^ |
不过回溯之后貌似回溯之前的版本就没了。
最新的那个版本 modify readme.txt
已经看不到了!好比你从 21 世纪坐时光穿梭机来到了 19 世纪,想再回去已经回不去了,肿么办?
办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个 modify readme.txt
的 commit id
是 1094adb...
,于是就可以指定回到未来的某个版本:
1 | git reset --hard df66027f00c |
版本号没必要写全,前几位就可以了,Git 会自动去找。当然也不能只写前一两位,因为 Git 可能会找到多个版本号,就无法确定是哪一个了。
Git 的版本回退速度非常快,因为 Git 在内部有个指向当前版本的 HEAD
指针,当你回退版本的时候,Git 仅仅是把 HEAD 从指向 modify readme.txt
(git 是用 c 写的,参考 c 指针)
git reflog
:前面说到,如果版本回退了,想到再找回最新版本需要窗口还没关掉,但是如果关掉了其实也是可以通过 git reflog
找到
1 | git reflog |
查找到版本号就可以回退了(参考上条)
小结
HEAD
指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令
git reset --hard commit_id`。穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用
git reflog
查看命令历史,以便确定要回到未来的哪个版本。
git checkout -- filename
撤销工作区所有未添加的修改(以暂存区内容为主)// 可用 git resotre --worktree readme.txt 代替
<span style="color:blue">Git 社区发布了 Git 的新版本 2.23。在该版本中,有一个特性非常引人瞩目,就是新版本的 Git 引入了两个新命令 git switch 和 git restore,用以替代现在的 git checkout。</span>
git restore
当我们将修改添加到暂存区时,突然意识到修改是有问题的,那么我们可以使用git restore
从暂存区恢复工作区,
git restore --worktree readme.txt
从 master 恢复暂存区
git restore --staged readme.txt
从 master 同时恢复工作区和暂存区
git restore --source=HEAD --staged --worktree readme.txt
场景 1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file
。
场景 2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令 ``git restore --staged readme.txt`,就回到了场景 1,第二步按场景 1 操作。
场景 3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退,不过前提是没有推送到远程库。
rm
一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm
命令删除文件
这个时候,Git 知道你删除了文件,因此,工作区和版本库就不一致了, git status
命令会立刻告诉你哪些文件被删除了:
1 | git status |
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令 git rm
删掉,并且 git commit
:
1 | git rm test.txt |
之后再进行提交:
1 | git commit -m "del test.txt" |
<span style="color:green"> 先手动删除文件,然后使用 git rm <file > 和 git add<file > 效果是一样的。</span>
<span style="color:red"> 注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!</span>
$ git push
推送分支到远程库
1 | git push origin dev |
但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;- bug 分支只用于在本地修复 bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个 bug;
- feature 分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
git pull
同步远程仓库
# Git 工作区与暂存区
git add /git commit
# 工作区(working Directory)
就是你在电脑里能看到的目录
# 版本库(Repository)
工作区目录 .git
,这个并不属于工作区,而是 Git 版本库
Git 的版本库里存了很多东西,其中最重要的就是称为 stage(或者叫 index)的暂存区,还有 Git 为我们自动创建的第一个分支 master
,以及指向 master
的一个指针叫 HEAD
。
第一步是用 git add
把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用 git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建 Git 版本库时,Git 自动为我们创建了唯一一个 master
分支,所以,现在, git commit
就是往 master
分支上提交更改。
如果我们再新建一个文件(随便写),然后再进行 ``git status 查询状态会提示你新建的文件状态是
untracked`
1 | git status |
使用两次命令 git add
,把 readme.txt
和 LICENSE
都添加后,用 git status
再查看一下:
1 | git status |
这时候暂存区是这样的:
所以, git add
命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行 git commit
就可以一次性把暂存区的所有修改提交到分支。
1 | git commit -m "create LICENSE.txt" |
再提交后你对工作区没有任何修改,那么工作区就是 clean
<span style="color:red">Git 跟踪并管理修改,而非文件 </span>
# SSH
Secure Shell (安全外壳协议,简称 SSH )是一种加密的 网络传输协议 ,可在不安全的网络中为网络服务提供安全的传输环境 。.
SSH 通过在网络中建立 安全隧道 (英语:secure channel) 来实现 SSH 客户端与服务器之间的连接 。.
SSH 最常见的用途是远程登录系统,人们通常利用 SSH 来传输 命令行界面 和远程执行命令。. SSH 使用频率最高的场合是 类 Unix 系统,但是 Windows 操作系统也能有限度地使用 SSH。
由 IETF 的网络小组(Network Working Group)所制定;SSH 为建立在应用层基础上的安全协议。SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。
# 远程仓库 (github)
如果只是在一个仓库里管理文件历史,Git 和 SVN 真没啥区别。
第 1 步:创建 SSH Key。在电脑 C:\Users\ 用户目录下,看看有没有.ssh 目录,如果有,再看看这个目录下有没有 id_rsa
和 id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开 Shell(Windows 下打开 Git Bash),创建 SSH Key:
1 | ssh-keygen -t rsa -C "youremail@example.com" |
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个 Key 也不是用于军事目的,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到 .ssh
目录,里面有 id_rsa
和 id_rsa.pub
两个文件,这两个就是 SSH Key 的秘钥对, id_rsa
是私钥,不能泄露出去, id_rsa.pub
是公钥,可以放心地告诉任何人。
第 2 步:登陆 GitHub,打开 “Account settings”,“SSH Keys” 页面
然后,点 “Add SSH Key”,填上任意 Title,在 Key 文本框里粘贴 id_rsa.pub
文件的内容:
点 “Add Key”,你就应该看到已经添加的 Key
<span style="color:blue"> 为什么 GitHub 需要 SSH Key 呢?因为 GitHub 需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而 Git 支持 SSH 协议,所以,GitHub 只要知道了你的公钥,就可以确认只有你自己才能推送。 </span>
<span style="color:blue"> 当然,GitHub 允许你添加多个 Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的 Key 都添加到 GitHub,就可以在每台电脑上往 GitHub 推送了。</span>
# 添加远程仓库
在网页中找到 Repositories 点击 new 即可
之后参考 gitee 填写库名描述其他不用动即可。
新建了一个新的库之后,我们就要将本地的库跟这个库进行关联
在本地的 git 里输入命令
1 | git remote add origin git@github.com:你的github用户名/你的库名.git |
添加后,远程库的名字就是 origin
,这是 Git 默认的叫法,也可以改成别的,但是 origin
这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:
1 | git push -u origin master |
由于远程库是空的,我们第一次推送 master
分支时,加上了 -u
参数,Git 不但会把本地的 master
分支内容推送的远程新的 master
分支,还会把本地的 master
分支和远程的 master
分支关联起来,在以后的推送或者拉取时就可以简化命令
<span style="color:rgb (194, 4, 4)"> 当你第一次使用 Git 的 clone
或者 push
命令连接 GitHub 时,会得到一个警告:</span>
1 | The authenticity of host 'github.com (13.250.177.223)' can't be established. |
这是因为 Git 使用 SSH 连接,而 SSH 连接在第一次验证 GitHub 服务器的 Key 时,需要你确认 GitHub 的 Key 的指纹信息是否真的来自 GitHub 的服务器,输入 yes
回车即可。
Git 会输出一个警告,告诉你已经把 GitHub 的 Key 添加到本机的一个信任列表里了:
1 | Warning: Permanently added 'github.com' (RSA) to the list of known hosts. |
这个警告只会出现一次,后面的操作就不会有任何警告了。
之后可以通过 git remote -v
查看连接的远程库
删除远程库:
如果添加的时候地址写错了,或者就是想删除远程库,可以用 git remote rm <name>
命令。使用前,建议先用 git remote -v
查看远程库信息,然后,根据名字删除,比如删除 origin
:
1 | git remote rm origin |
此处的 “删除” 其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到 GitHub,在后台页面找到删除按钮再删除。
小结
要关联一个远程库,使用命令 git remote add origin git@server-name:path/repo-name.git
;
关联一个远程库时必须给远程库指定一个名字, origin
是默认习惯命名;
关联后,使用命令 git push -u origin master
第一次推送 master 分支的所有内容;
此后,每次本地提交后,只要有必要,就可以使用命令 git push origin master
推送最新修改;
# 远程库克隆
指令 git clone git@github.com:username/repo .git
1 | git clone git@github.com:windlinxy/WinLife.git |
# 分支管理
几乎每一种版本控制系统都以某种形式支持分支。. 使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。
有人把 Git 的分支模型称为 必杀技特性 ,而正是因为它,将 Git 从版本控制系统家族里区分出来。 当你切换分支的时候,Git 会用该分支的最后提交的快照替换你的工作目录的内容, 所以多个分支不需要多个目录。你可以多次合并到统一分支, 也可以选择在合并之后直接删除被并入的分支。
# 创建分支
1 | git branch (branchname) |
# 切换分支
1 | git checkout (branchname) |
1 | git checkout -b dev |
-b
表示创建并切换
也可以:
1 | git switch dev |
创建并切换
# 查看分支
1 | git branch |
# 合并分支
在当前分支合并其他分支
1 | git merge test |
# 删除分支
1 | git branch -d test |
# 分支管理策略
通常,合并分支时,如果可能,Git 会用
Fast forward
模式,这种模式下,删除分支后,会丢掉分支信息。
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,
master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在
dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如 1.0 版本发布时,再把dev
分支合并到master
上,在master
分支发布 1.0 版本;你和你的小伙伴们每个人都在
dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
# 合并冲突
在合作开发的时候我们会遇到这种情况:
在不同分支同时对一个文件进行修改并在本地提交,准备合并,但是合并时会报错。
1 | git merge test |
这是因为在合并分支时,git 会自动将文件进行合并,但是两个分支修改了文件同一处,在合并时就产生了冲突。
使用 git status
会看到:
1 | git status |
这时候我们查看冲突文件的内容:
1 | Git is a distributed version control system. |
Git 用 <<<<<<<
, =======
, >>>>>>>
标记出不同分支的内容
用带参数的 git log
也可以看到分支的合并情况
1 | git log --graph --pretty=oneline --abbrev-commit |
用 git log --graph
命令可以看到分支合并图
1 | git log --graph |
<span style="color:brown"> 合并分支时,加上 --no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而 fast forward
合并就看不出来曾经做过合并。</span>
一般分支:
- master:git 默认主分支(这里不作操作)。
- stable:稳定分支,替代 master,主要用来版本发布。
- develop:日常开发分支,该分支正常保存了开发的最新代码。
- feature:具体的功能开发分支,只与 develop 分支交互。
- release:release 分支可以认为是 stable 分支的未测试版。比如说某一期的功能全部开发完成,那么就将 develop 分支合并到 release 分支,测试没有问题并且到了发布日期就合并到 stable 分支,进行发布。
- bugfix:线上 bug 修复分支。
<span style="color:brown"> 开发一个新 feature,最好新建一个分支;</span>
<span style="color:brown"> 如果要丢弃一个没有被合并过的分支,可以通过 git branch <name>
强行删除。</span>
# 多人协作
- 查看远程库信息,使用
git remote -v
; - 本地新建的分支如果不推送到远程,对其他人就是不可见的;
- 从本地推送分支,使用
git push origin branch-name
,如果推送失败,先用git pull
抓取远程的新提交; - 在本地创建和远程分支对应的分支,使用
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致; - 建立本地分支和远程分支的关联,使用
git branch --set-upstream branch-name origin/branch-name
; - 从远程抓取分支,使用
git pull
,如果有冲突,要先处理冲突。