为什么要学习Git
协同工作 业界绝大多数公司都是基于Git进行代码管理,因此Git是一个程序员的必备技能。 开源社区 目前绝大多数的开源项目都是基于Git维护的,参与这些项目的开发都需要使用Git。
Git是什么
分布式版本控制系统
是一种记录一个或若干个文件内容变化,以便将来查阅特定版本修订情况的系统
更好的关注变更,了解每个版本的改动是什么,方便对改动的代码进行你检查,预防事故的发生,也能够随时切换到不同的版本,回滚误删误改的问题代码。
历史
本地版本控制(如:RCS)
最初通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本
解决方案:开发了一些本地的版本控制软件
基本原理:本地保存所有变更的补丁集,可以理解成就是所有的Diff,就是这些补丁,我们可以计算出每个版本的实际的文件内容
缺点:只能在本地使用,无法用于团队协作,使用场景非常有限
集中版本控制(如:SVN)
基本原理:提供一个远端服务来保存文件,所有用户的提交都提交到服务器中。增量保存每次提交的Diff,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突
优点:学习简单,更容易操作。支持二进制文件,对大文件的支持更好
缺点:本地不存储版本管理的概念,所有提交都只能连上服务器之后才可以提交。分支上的支持不够好,对于大型项目团队合作比较困难。用户本地不保存所有版本的代码,如果服务端故障容易导致历史版本的丢失。
分布式版本控制(Git)
基本原理:每一个库都存有完整的提交历史,可以直接在本地进行代码提交。每次提交记录的都是完整的文件快照,而不是记录增量。通过push等操作来完成和远端代码的同步。
优点:分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体。分支管理功能强大,方便团队合作,多人协同合作。校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失。
缺点:相对SVN更复杂,学习成本更高。对于大文件的支持不是特别好(git-lfs可以弥补这个功能)
Git的基本使用方式
项目初始化
mkdir demo
cd demo
git init
git init
- --initail-branch 初始化的分支
- --bare 创建一个裸仓库(纯git仓库,没有工作目录)
- --template 可以通过模板来创建预先构建好的自定义git目录
通过tree .git可以观察git的目录文件
tree .git
卷 Data 的文件夹 PATH 列表
卷序列号为 00000045 5863:2318
D:\MYPROGRAMME\MYWEBPROJECT\GITSTUDY\DEMO\.GIT
├─hooks
├─info
├─objects
│ ├─info
│ └─pack
└─refs
├─heads
└─tags
工作区&暂存区 在工作区完成之后,执行git add命令加到暂存区,再执行commit提交到git目录
配置
- --global : ~/.gitconfig
- --system : ${prefix}/etc/gitconfig
- --local : .git/config
每个级别的配置可能会重复,但是低级别的配置会覆盖高级别的配置
用户名配置
git config --global user.name "XXX"
git config --global user.email "XXXXX@xxx.com"
Instead of 配置(做一些url替换)
git config --global url.git@github.com:.insteadOf https://github.com
Git命令别名配置
git config --global alias.cin "commit--amend --no-edit"
Git Remote
查看Remote
git remote -v
添加Remote
git remote add origin-ssh git@github.com:git/git.git
git remote add origin-http https://github.com/git/git.git
config中:
HTTP Remote
免密配置
- 内存:git config --global credential.helper 'cache --timeout=3600'
- 硬盘:git config --global credential.helper "store --file /path/to/credential-file",不指定目录的情况默认是~/.git-credentials
将密钥信息存在指定文件中 具体格式:${scheme}://${user}:${password}@github.com
SSH Remote
URL:git@github.com:git/git.git
免密配置, ssh可以通过公私钥的机制,将生成的公钥存储在服务端,从而实现免密访问 key的四种类型:dsa,rsa(默认),ecdsa,ed25519(推荐)
配置过程
执行图片中的命令,会在.ssh中生成一个这样的文件
复制.pub文件中的内容,到github->setting->SSH and GPGkeys ->New SSH key
提交代码
Git Add
比如我们创建一个readme.md文件,在里面编辑“hello world”,之后执行git add . 命令添加到暂存区,会发现在.git的objects中新增了一个文件,我们使用
git cat-file -p <文件夹名+包含文件名的字符串>,来观察里面的信息,发现是我们的文件内容。
之后再执行git commit -m "add readme" 命令添加到仓库。发现.git的objects中新增了两个文件,同样的方式进行查看可以发现一个是blob的目录信息,另一个提交的信息(作者,提交者,提交备注等信息)
Objects
- blob:存储文件的内容
- Tree:存储文件的目录信息
- Commit:存储提交信息
三者如何联系起来的呢?
- 通过Commit寻找Tree信息,每个Commit都会存储对应的TreeID
- 通过Tree存储的信息,获取到对应目录的目录树信息
- 从Tree中获得blob的id,通过blobid获取对应的文件内容
Refs
refs/heads/master中存储了最新的commitid
我们新建分支通过:
git checkout -b test
//切换到一个新分支“test”
refs的内容就是对应的commitid 因此把refs当作指针,指向对应的commit来表示当前ref对应的版本
Breach
git checkout -b
可以创建一个新的分支 分支一般用于开发阶段,是可以不断添加Commit进行迭代的
Tag
标签一般表示的是一个稳定的版本,指向的Commit一般不会变更
通过git tag命令生成tag
Annotation Tag
一些特殊的Tag,可以给Tag提供一些格外的信息 通过
git tag -a v0.0.2 -m "add feature 1"
追溯历史版本
获取当前版本代码: 通过refs指向的commitid可以获取唯一的代码版本
获取历史版本的代码: Commit里面会存有,parent commit字段,通过commit的串联获取历史版本代码
commit --amend 可以修改最近的一次commit信息,修改之后commitid会变
修改后老的object没有ref指向它,为悬空commit 通过git fsck --lost-found来查找悬空commit ,通过Git GC删除不需要的object,以及对object进行打包压缩减少仓库的体积
指定时间 git gc prune=now指定的是修建多久之前的对象,默认是两周前
完整的git视图
远端同步
Git Clone & Pull & Fetch