Git
前言
由于我的记性不大好,代码什么的经常忘。所以我的笔记是经常作为我的查阅手册存在的。但同时我也想在每一次通读我的笔记后,能完整的遍历一次Git的核心内容。
所以我尽量在查阅性和理解性内容中找到一个平衡。尽可能的做到两者兼得233
Git简介
Git是一个版本控制器。版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本。系统会保存你所有版本的历史记录
- 可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态。
- 可以多个人协同工作
版本控制器分为集中式与分布式两种
集中式的代表就是SVN,分布式的代表是Git。
集中式
优点:是把代码存放在单一的服务器上,便于项目的管理。
缺点:就是只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险
特点:存的只是每个版本之间的差异和索引所以所占空间小。但是相对应的回滚就会变慢
分布式
分布式版本控制的核心概念就是去中心化。它将所有的版本都存放在每一个电脑上。并且可以通过远端仓库Github来跨组进行开发。
特点:git每次存的都是项目的完整快照,需要的硬盘空间会相对大一点。(Git团队对代码做了极致的压缩,最终需要的实际空间比SVN多不了多少)可是Git的回滚速度极快。
优点:
1.断网的情况下也可以进行开发(因为版本控制是在本地进行的)
2.使用Github进行团队协作,哪怕Github挂了,每个客户端保存的也都是完整的项目(包含历史记录的)
极其适合管理大项目
Git的底层概念
Git的两条主线
区域
工作区
暂存区
版本库
对象
Git对象:
key:value 组成的键值对(key是val对应的hash值)键值对在git内部是一个blob类型
不论是文件还是控制台程序最终存储的类型都是blob类型的文件
Git对象的问题:
1.记住文件的每一个版本所对应的SHA-1值并不现实
2.在Git中,文件名并没有被保存–我们仅保存了文件的内容。并且每一个文件都会对应一个blob类型的文件所以Git对象并不适合直接作为版本控制
树对象:
树对象(tree object),他能解决文件名保存的问题,也允许我们将多个文件组织到一起。每一次提交一个新版本就多一个树对象。树对象就像是一次项目的快照。树对象的生成也是一个键值对类型的文件。键值为它的Hash值。类型为Tree
git对象代表文件的一次次版本。tree对象是项目的一次次版本
Tree对象的问题
Tree对象虽然可以代表一个版本的项目快照。但是若是想重用这些快照,你必须记住所有Tree对象的hash值。而且你也完全不知道是谁保存了这些快照,在什么时刻保存的,以及为什么保存这些快照。而以上这些。正是提交对象能为你保存的信息
提交对象:
提交对象就是对树对象 做一个封装。把该版本的信息,上一个版本的hash值,提交人和注释等信息封装进去。
find .git/objects/ -type f //查看所有的git对象
git cat-files -p //查看内容
git cat-files -t //查看类型
项目的每一个版本都是一个提交对象,而项目的每一次快照都是一个树对象
Git的本地操作(高层命令)
Git的初始化
查看版本信息
git --version
配置用户名和邮箱
安装成功后。需要配置用户名和邮箱。方便别人review你的代码或者你的代码有BUG时联系你
git config --global(或者--system 或者啥都不写) user.name "你的名字"
git config --global user.email "你的邮箱"
你任何时候都可以通过
git config --list
来查看你的配置信息
注意–global时指当前用户。–system是指该系统下所有的用户。而什么都不写仅代表当前文件。当前项目的优先级最高。如果当前项目没有就往上找
初始化Git仓库
git init
初始化后,当前目录下会出现一个名为.git的目录,所有Git需要的数据和资料都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录。但我们还没有开始跟踪管理项目中的任何一个文件。
工作区中每一个文件都是两种状态,以跟踪或未跟踪
一旦文件提交到暂存区一次。那他就是追踪状态了
Git的提交操作
将文件添加到暂存区(本质是先将文件添加到版本库再放入暂存区)你修改了几个文件就会生成几个Git文件.所以说Git是绝对安全的,哪怕没有提交到版本库只是记录到暂存区,你对文件的所有修改都会记录为Git对象。
当你觉得暂存区里的内容可以构成一个版本的时候。提交Git就会生成一个Tree对象和包裹着Tree对象的提交对象。
所以一次提交至少要包含一个Git对象,一个Tree对象和一个提交对象
Git查看暂存区命令
git ls-files -s
Git将修改添加到暂存区
git add ./
一旦添加底层的代码如下
git hash-object -w 文件名(修改了多少个工作目录中的文件 此命令就要被执行多少次)
Git将暂存区提交到版本库
git commit -m "注释内容"
-a //Git会直接将所有已追踪的文件添加到版本库。可跳过暂存区
一旦提交底层的代码如下
git write-tree
git commit-tree 哈希码
从版本库中删除文件或修改文件名
git rm (目标文件)
它会删除这个文件然后再将此次修改添加到暂存区,你只需要最后再提交一下就可以了
查看文件状态
git status
已追踪的文件的状态有三种:已提交,已修改,已暂存
查看文件与暂存区中文件的区别
git diff
查看文件跟暂存区的区别
查看缓存区与版本库中文件的区别
git diff --cached
git diff --stager //1.61版本以上支持
查看哪些更新已经暂存起来了准备好下次提交
查看历史记录
git log
--oneline //每个版本只显示一行简略信息
git reflog /
log与reflog的区别
log:只会留下你想看到的记录,也就是你撤回的,回退的记录它是不显示的。
reflog:只要是HEAD有变化,那么git reflog就会记录下来(包括撤销命令,修改提交注释命令)
起别名
git config --global (别名) (你想配别名的命令不需要加引号)
Git的分支操作
需求
当我们合作开发或者自己想加一个新功能的时候就可以创建分支。来避免万一写错了污染原来代码的情况
本质
Git分支的本质其实就是指向提交对象的动态指针
显示分支列表
git branch 后面不跟参数显示分支列表
-v 可以查看每一个分支的最后一次提交
创建分支
git branch (分支名)
时光机
git branch (分支名) (该版本的Hash) 新建一个分支并且使分支指向对应的提交对象
切换分支
git checkout (分支名)
//慎用
-b (分支名) 创建并切换到该分支
切换分支会动三个地方
1.HEAD
2.暂存区
3.工作目录
Checkout移动的是HEAD指针而分支是留在原地的。如果想带着分支一起跑需要用reset
最佳实践每次切换分支前,当前分支一定得是干净的(已提交状态),
否则在切换分支时,如果当前分支上有未暂存
的修改(第一次)或者有未提交的暂存(第一次)分支可以切换成功但是这种操作会把未提交的文件带过来,污染其他分支
删除分支
git branch -d (分支名) //如果该分支没有被合并需要使用 -D 强制删除分支
合并分支
git merge (你想合并掉的分支名)
查看项目分叉历史
git log --oneline --decorate --graph --all
建议起别名
Git临时存储
需求
该功能会将未完成的修改保存到一个栈上,而你可以在任何时候重新应用这些改动。比如你改bug改一半,老板又叫你去改另一个更重要的BUG这个时候你就可以使用这个功能
查看Git的存储
git stash list
Git的存储
git stash
其本质也是做一次提交但是普通日志上不会显示
取出存放的版本
git stash apply stash@{数字}
如果不指定则默认取栈顶元素
git stash pop
应用储藏然后立即从栈上扔掉它
git stash drop (名字)
删除所选元素
Git的撤销
需求
对提交不满意想要撤回(好正常的需求)
对提交不满意的情况基本分为以下三种情况
工作区
想撤回自己在工作目录中的修改
暂存区
想撤回自己的暂存
版本库
想撤回自己的提交
1.提交的时候注释写错了QAQ
2.提交后发现需要修改代码
但是Git并没有给予回退版本库的操作。对于第一种情况。可以使用 git commit --amend来重新修改注释。第二种情况就只能重新提交一次,生成一个版本
撤回工作区修改
git checkout --(文件名)
撤回暂存区修改
git reset (分支名) (文件名)
重新修改版本注释
git commit --amend
–amend的本质是HEAD带着分支回到了上一个版本。等你修改好后生成一个新的版本。而老的版本会变成树主干上的一个不起眼的小小的分叉,没有意外会因为主线的推进再也无人知晓。想想还有点沧桑感有木有被Git的垃圾回收器回收
reset的本质就是移动HEAD的指向
重置版本
git reset --soft (哈希值) //HEAD~代表回退到上一个版本。只回退Commit的部分
git reset --mixed (哈希值) //回退工作区和暂存区的部分
git reset --hard (哈希值) //三个区的东西都回退
–hard标记是rest命令唯一的危险用法,它也是Git会真正地销毁数据的仅有的几个操作之一。
假如你在工作区和暂存区还有文件checkout会把文件带过去。而如果用hard的话这些文件会被直接覆盖
重置单独文件
git reset [--mixed] 哈希值 文件名
checkout和reset的区别
1.checkout只动HEAD --hard动HEAD而且带着分支一起走
2.checkout对工作目录是安全的。 --hard是强制覆盖工作目录
数据恢复
如果硬重置回以前版本后发现重置前的文件还有用处想找回。但是log里面已经不显示重置前的分支了
那就通过reflog找到重置前的Hash码。再硬重置回去
Tag标签
Git可以给历史中的某一个提交打上标签,以示重要。比如我们可以用这个功能来标记发布版本
查看标签
git tag //查看所有标签
-l //带条件查询
查看特定标签
git show (标签名)
创建标签
git tag (标签名)
检出标签
git checkout -b(标签名)
如果不加-b创建分支的话会导致仓库处于“分离头指针“状态。在此状态下,如果你做了某些更改然后提交他们,标签不会发生变化,但是你的新提交将不属于任何分支。也就是除非访问确切的Hash值。再也无法访问。
所以如果你切回来需要进行更改。这通常需要创建一个新分支
Git的远程操作
首先和github勾搭的话需要一种加密措施,SSH或者HTTPS。这里我使用的是SSH公私钥。
创建一对公私钥
ssh-keygen -t rsa -C [用户名]
将公钥输入到Github中就可以了
测试本地仓库与github的连通性
ssh -T git@github.com
增加远程地址
首先我们需要本地仓库和Github上的一个仓库发生关系。
git remote add <远端代号> <远端地址>
远端代号: 是指远程链接的代号,一般直接用 origin 作代号,也可以自定义;
远端地址: 默认远程链接的 url;
push操作
git push -u <远端代号> <本地分支名称>
远端代号: 是指远程链接的代号;
分支名称: 是指要提交的分支名字,比如 master;
fetch操作
git fetch <远端代号> <远端分支名>;
fetch
是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
pull操作
git pull <远端代号> <远端分支名>;
pull的本质是git fetch+git merge
clone操作
git clone <远端地址> <新项目目录名>
项目目录名: 是指为克隆的项目在本地新建的目录名称,可以不填,默认是 GitHub 的项目名;