尚硅谷最新Git教程全套完整版(12h带你深入掌握Git)【笔记】
背景
虽然一直在用 Git,但是实际只会一些基础操作,并不理解其中的原理,随着工作的时间越来越长,感觉对 Git 的深入理解越来越有必要。
视频时间
版本信息
视频版本:Git version 2.19.1.windows.1
笔记版本:Git version 2.26.0.windows.1
1. Git 操作
版本控制
版本控制分为集中式(SVN)和分布式(Git)两种;
- 集中式(SVN):
SVN 每次存的都是差异,需要的硬盘空间会相对的小一些,可是回滚的速度会很慢
- 优点:
代码存成在单一的服务器上,便于项目的管理 - 缺点:
服务器宕机:员工写的代码得不到保障
服务器炸了:整个项目的历史记录都会丢失
- 分布式(Git)
Git 每次存的都是项目的完整块照,需要的硬盘空间会相对大一点
Git 团队对代码做了极致的压缩,最终需要的实际空间比 SVN 多不了多少,可是 Git 的回滚速度极快
Git 初始化配置
在 Windows 中,
system 级别的配置文件位于 C:\Program Files\Git\etc\gitconfig
global 级别配置文件位于 C:\Users\80953\.gitconfig
项目的配置文件位于为 .git\config
## 查看帮助信息
git --help
## 查看 Git 版本
git --version
## 查看 Git 配置信息
git config --list
## 配置全局用户信息
git config --global user.name "hwj"
git config --global user.email "hwj@github.com"
## 配置别名
git config --global alias.lol 'log --oneline --decorate --graph --all'
Git 底层概念(底层命令)
基础的 Linux 命令
* clear 清除屏幕
* echo 'hello word '>test.tet 命令台书写内容
* ll 将当前目录下的子目录展现出来
* find ./ 将当前目录下的子目录以及文件也展现出来
* find ./ -type f 只讲文件展现出来
* rm text.txt 删除文件
* mv a.txt b.txt 更名字
* cat a.txt 查看文件内容
* vim a.txt 编辑内容
i 插入
o 插入新行
ese 进入命令模式
:wq 保存退出
:set nu 显示行号
:q! 强制退出不保存
项目的 .git 目录
## 初始化仓库
git init
- hooks (钩子函数的一个库 类似于回调函数)
- info (包含一个全局性的排除文件)
- logs (日志信息)
- objects (目录存储所有数据内容)
- refs (目录存储指向数据(分支)的提交对象的指针)
- config (文件包含项目特有的配置选项)
- description (显示对仓库的描述信息)
- HEAD (文件目前被检出的分支)
- index (文件保存暂存区的信息)
git 对象
Git 的核心部分是一个简单的键值对数据库。你可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索该内容。
# hash-object
## 向数据库写入内容,并返回对应键值
### 返回的键值,前2位为文件夹名称,后38位为文件名称
### -w 指示存储数据对象,不指定则不存储,只返回键值
### --stdin 指示从标准输入读取内容,不指定则需要在命令尾部给出存储文件的路径
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
## Git 存储文件
$ git hash-object -w a.txt
190a18037c64c43e6b11489df4bf0b9eb6d2c9bf
## 查看 Git 存储的对象
$ find .git/objects/ -type f
.git/objects/19/0a18037c64c43e6b11489df4bf0b9eb6d2c9bf
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
# cat-file
## 查看任意对象
## 根据键值拉取数据,键值不需要完整,只要前面几位就可以
### -p 指示自动判断文件类型,并为我们显示格式友好的内容
### -t 查看文件类型
$ git cat-file -p d670460b4
test content
$ git cat-file -t d670460b4
blob
以上所有操作都是在对本地数据库进行操作,不涉及暂存区
上述操作存在的问题:
- 记住文件的每一个版本对应的 SHA-1 值并不现实
- 在 Git 中,文件名并没有被保存,我们仅保存了文件的内容
解决方案:树对象
tree 对象
树对象,它能解决文件名保存的问题,也允许我们将多个文件组织在一起。Git 以一种类似于 UNIX 文件系统的方式存储内容。所有内容均以树对象和数据对象(Git 对象)的形式存储,其中树对象对应了 UNIX 中的目录项,数据对象(Git 对象)则大致上对应文件内容。
一个树对象包含了一条或多条记录(每条记录含有一个指向 Git 对象或者子树对象的 SHA-1 指针,以及相应的模式、类型、文件名信息)。
一个树对象也可以包含另一个树对象。
# ls-files
## 查看暂存区当前的样子
$ git ls-files -s
# update-index
## 更新索引
## 只是为 Git 对象起一个名字,不生成任何对象,需要指定已生成对象的 hash,否则 write-tree 时会报错;但文件名称不需要与实际文件相同
## 相同文件如果已存在缓存区中,更新后再生成对象、更新索引,缓存区中会更新为更新后的文件
### 文件模式为100644 表明这是一个普通文件
### 文件模式为100755 表明这是一个可执行文件
### 文件模式为120000 表明这是一个符号连接
### --add 因为此前该文件并没有在暂存区中 首次要加add
### --cacheinfo 因为要添加的文件在git数据库中,没有位于当前目录下
git update-index --add --cacheinfo 100644 915c628f360b2d8c3edbe1ac65cf575b69029b61 test.txt
## 将文件添加为 Git 对象,并更新索引
## hash-object + update-index
git update-index --add new.txt
# write-tree
## 暂存区做一个快照生成一个对象放到git数据库中
## 对象类型是一个树对象
## 树对象里面的内容是暂存区的快照(项目的快照)
## 暂存区中文件名字不变 如果改变文件的内容,就会重新生成一个hash
git write-tree
# rm
## 删除操作
### -f 指示强制删除
### --cached 指示删除的是缓存区中的内容
$ git rm -f --cached a.txt
rm 'a.txt'
# read-tree
## 将一个树对象读入暂存区
$ git read-tree --prefix=bak 58e6d12d1bf385222144538d11ef7714c87da3b4
存在的问题:
- 不知道 hash 值对应的是哪一个版本
- 不知道这个树对象快照的一些基础信息
这些,正是 提交对象 能为你保存的基本信息
commit 对象
# git commit-tree
## 本质就是给树对象做一层包裹包含项目的基础信息
## commit-tree 创建一个提交对象,为此需要指定一个树对象的hash值,以及该提交的父提交对象
### -p 用来指定父提交对象
echo "first commit" | git commit-tree 019fb2c522b604cd94929085bbac93d60e2f2063
echo "second commit" | git commit-tree e8d77cd21e29ba9a511f129a8b908afc2151bb3b -p f0d25e23b9a52c992f3eab8e4fb8ed6973f670c1
commit-tree 不但生成提交对象,而且会将对应的快照(树对象)提交到本地库中
真正代表一个项目的是一个提交对象(数据和基本信息)这是一个链式的!!
Git 本地操作(高层命令)
Git 中的区域
- 工作区
- 暂存区
- 版本库
工作目录中的文件状态
- 未跟踪(untracked)
- 已跟踪
- 已提交(未修改)(unmodified)
- 已修改(modified)
- 已暂存(staged)
高层命令
# add
## 将修改添加暂存区
## 先到版本库,再到暂存区
## hash-object + update-index
git add ./
# commit
## 提交
## 生成一个树对象,一个提交对象
## write-tree + commit-tree
### -m 指示描述信息
git commit -m "first commit a.txt v1" a.txt
### -a 跳过 add 过程,将所有暂存一起直接提交,后面不能跟文件或目录;文件必须处于已跟踪状态
### -a 适用于修改了文件内容,不适用于新建文件的提交
git commit -am "m 1"
# status
## 检查当前文件状态
git status
# restore
## add 的反操作,将文件从暂存区移除
## 仅移除暂存区,对象仍然存在,工作目录中的文件仍然存在
## 作用等同于 rm --cached
git restore --staged c.txt
# diff
## 查看已暂存和未暂存的更新
### 不加参数,与暂存区中的比较
### --cached 指示与已提交的比较
### --staged 同 --cached
git diff a.txt
# rm
## 删除文件
## rm + git add
git rm a.txt
# mv
## 文件改名
## mv + git add
git mv b.txt bb.txt
# log
## 查看历史记录
### --oneline 指示简化日志显示
git log --oneline
### 查看完整的分支图(没删除前)
git log --oneline --decorate --graph --all
# reflog
## 查看 HEAD 的操作日志
## 比完整分支图更详细的日志信息
git reflog
Git 分支操作(杀手功能)
分支的本质是指向提交对象的可变指针。
HEAD 的本质是一个指针,默认指向 master 分支,切换分支就是让 HEAD 指向不同的分支。每次有新的提交时,HEAD 就会带着当前指向的分支,一起向前移动。
## 查看分支列表
git branch
## 查看所有分支最后一个提交
git branch -v
## 在当前的提交对象上创建一个分支 test
git branch test
## 在指定提交对象上新建一个分支
git branch test hashcode
## 切换分支
git checkout test
## 创建分支并且切换过去
git checkout -b test
## 检出远程分支到本地分支
git checkout -b dev(本地分支名) origin/dev(远程分支名)
## 删除分支
### 删除已合并分支
git branch -d test
### 强制删除分支,不管是否合并
git branch -d test
## 合并分支
git merge test
## 查看合并到当前分支的分支
git branch --merged
## 查看没有合并到当前分支的分支
git branch --no-merged
分支切换会改变你工作目录中的文件
切换分支的时候一定要提交完的时候再切否则会出现问题
每次切换分支前当前分支一定要是已提交状态,否则会污染主分支,如果第一次提交了再修改的时候没有提交他就不让切换分支了
最佳实践:每次切分支时,使用 git status
保证分支是干净的。
分支合并
合并分支分为快进合并和典型合并
快进合并不会产生冲突
Git 存储
解决的问题:
不希望因为切换分支过多的创建提交
Git 存储是栈结构,先进后出
# stash
## 创建存储
git stash
## 将栈顶存储移出,并恢复存储
git stash pop
## 查看存储列表
git stash list
## 恢复存储,不移除出栈
## 不指定则默认为栈顶存储
git stash apply stash@{1}
## 将存储在栈中删除
## 不指定则默认为栈顶存储
git stash drop stash@{2}
撤销&重置(后悔药)
## 工作区撤销修改
## 会修改文件内容
## 从已修改状态改变为未跟踪状态
git restore e.txt
git checkout -- e.txt
## 暂存区撤销修改
## 缓存区内容被修改,不改变文件内容
## 从已暂存状态改变为未跟踪状态
git restore --staged f.txt
git reset HEAD f.txt
## 版本库撤销修改
### 修改提交注释
git commit --amend
重置 reset
# reset
## 撤销一次提交
## 动HEAD(带着分支一起移动);不动暂存区和工作目录;文件内容不变
## 文件状态从已提交改为已暂存
git reset --soft HEAD~
## 动HEAD;动暂存区;文件内容不变
## 文件状态从已提交改为已修改
git reset [--mixed] HEAD~
## 动暂存区;动文件内容
git reset --hard HEAD~
git checkout
与 git reset --hard
区别:
- checkout 制动 HEAD;reset --hard 动 HEAD,而且带着分支一起走
- checkout 对工作目录是安全的;reset --hard 是强制覆盖工作目录
git checkout -- filename
只动工作目录,不动 HEAD 和暂存区
git checkout commitHash filename
动工作目录和暂存区,不动 HEAD
数据恢复
## 1. 通过命令查找要恢复的提交 hash
git reflog
git log -g
## 2. 使用两种方式恢复数据
### 2.1. checkout(更常用)
git checkout 分支名称 hashcode
### 2.2. reset
git reset --hard hashcode
Tag 功能
Git 可以给历史中的某一个提交打上标签,以示重要。
# tag
## 列出标签
git tag
## 正则匹配
git tag -l "v1*"
## 为提交对象打上 tag
git tag v1.0 [commitHash]
## 查看特定 tag
git show v1.0
## 删除标签
git tag -d v3
## 检出 tag
## 会导致头部分离,可以找到 tag 指向的提交对象后,检出为分支
git checkout v1.0
git checkout -b v1.0c dee383a
Git 特点
- 直接记录快照,而非差异比较
- 近乎所有操作都是本地执行
- 时刻保持数据完整性
- 多数操作仅添加数据
- 文件的三种状态
- 已提交(committed)
- 已修改(modified)
- 已暂存(staged)
- Git 管理项目时,文件流转的三个区域
- 工作目录
- 暂存区域
- 本地仓库
2. 远程仓库
# remote
## 添加远程分支
git remote add tg https://github.com/liuxing5yu/testGit.git
## 重命名远程分支
git remote rename old new
## 删除远程分支
git remote rm b1
## 查看远程分支
git remote -v
# push
## 将 master 分支推到远程仓库
git push tg master
# clone
## 克隆远程仓库
git clone https://github.com/liuxing5yu/testGit.git
# fetch
## 拉取远程仓库更新
git fetch [tg]
## 合并远程分支
git merge tg/master
## fetch 后出现新的远程分支时,在本地创建对应的本地分支
git checkout -b cb tg/cb
git checkout --track tg/b1
## 设置本地分支与远程跟踪分支同步
## 同步后可以直接执行 git push、git pull
git branch -u tg/master
## 查看设置的所有跟踪分支
git branch -vv
## 同步后,推送提交到远程仓库
git push
## 同步后,拉取远程更新到本地仓库
git pull
# 没有远程分支,本地已切换到 test2 分支
# 操作后,远程会创建 test2 分支,本地分支将会推送到远程
git push --set-upstream origin test2
# 本地分支关联远程分支
# git branch --set-upstream-to=origin/remote_branch local_branch
git branch --set-upstream-to=origin/remote_branch
# 解除关联
git branch --unset-upstream
- 可以在 Windows 的【凭据管理器|Windows 凭据】,删除已有的 GitHub 凭据
- 在 GitHub 仓库的【Settings|Manage access】中可以邀请合作者
- fetch 后需要 merge 远程分支;如果 fetch 后出现了新的远程分支,需要先建立本地分支,再合并远程分支
- 远程协作是存在 本地分支,远程跟踪分支,远程分支
- 远程跟踪分支 是远程分支状态的引用。它们是你不能移动的本地分支。当你做任何网络通信操作时,它们会自动移动。
- push 和 clone 时,生成远程跟踪分支
- 当克隆一个仓库时,它通常会自动创建一个跟踪
origin/master
的master
分支
解决远程协作冲突
- push 冲突
先 pull,解决冲突后,再 push - pull 冲突
- 先提交到本地,然后 push,会产生 push 冲突,之后解决方法如上
- 先 stash ,后 pull,stash pop,解决 pop 后的冲突
删除远程分支
## 删除远程分支
git push origin --delete b1
## 列出仍在远程跟踪但是远程已被删除的无用分支
git remote prune origin --dry-run
## 清除上面命令列出的远程无用分支
git remote prune origin
pull request 流程
GitHub 上操作 pull request 流程,提交后,等待被合并后关闭
.gitignore 文件
可以在 .gitignore
文件中列出要忽略的文件模式
.gitignore 参考写法
.gitignore 的格式规范:
- 所有空行或者以注释符号开头的行都会被Git忽略。
- 可以使用标准的glob模式匹配。
*
代表匹配任意个字符?
代表匹配任意一个字符**
代表匹配多级目录
- 匹配模式前跟反斜杠
/
代表项目根目录 - 匹配模式最后跟反斜杠
/
说明要忽略的是目录。 - 要忽路指定模式以外的文件或目录,可以在模式前加上惊収号
!
取反。
SSH
## 验证用户时走的不是验证密码的道路而是密匙
## 在 C:\Users\用户名\.ssh 下生成公私密匙
ssh -keygen -t rsa -C "邮箱"
## 在 GitHub 上添加 SSH keys
## 测试是否成功
ssh -T git@github.com
- 在 GitHub 的【Personal settings | SSH and GPG keys】里可以添加 SSH keys
3. Git 操作补充
git submodule
git 主库和子库之间的关系:
- git 主库拥有一个指向子库某一次提交的指针,更新指针需要 push 主库
- git 子库是独立的,git 子库的修改不影响 git 主库的 push
- 如果要操作子库,需要将目录路径从主库切换到子库,切换之后的操作只体现在子库,与主库无关
# 添加子库
git submodule add https://gitee.com/liuxing5yu/git-sub
# 加 -f 参数表示强制删除,即使子库中存在修改内容
# 字库删除后,本地文件
git submodule deinit -f git-sub/
git rm git-sub
# 检出存在子库的 git 库
git clone --recurse-submodules https://gitee.com/liuxing5yu/git-main