本文为笔者参加字节青训营时听字节青训课所做的笔记。
本文目录
一、Git的相关方向
Git可以应用的方向有很多,比如代码托管、代码智能、代码分析、持续集成、Cloud IDE等。
这里重点讲一下代码分析+持续集成:
代码分析顾名思义,就是研发流程中自动发现并且反馈代码中存在的问题,然后持续集成是一种软件开发实践,团队成员将工作的code集成一起,然后每次提交之后,都会自动触发一次包含自动化验证集的构建任务,以便能够尽早的发现集成问题。
Git主要是:协同工作+开源社区,所以必须掌握。
现有的几个大的Git平台:
Github、Gitlab(国内基本上都是基于这个在自己的服务器上搭建、主要是做定制化的东西)、Gerrit(谷歌开发的一个代码托管平台,比如Android就是)
二、工作中Git相关的点
- 对Git了解掌握不够好,配置git之后拉取代码依然有问题,缺少自己排查问题的能力。
- 研发过程中有异常操作,不清楚保护分支,MR/PR的概念。
所以,为什么我们的日常工作中需要Git?
首先我们需要知道Git是记录一个或者一些项目文件的内容变化,方便将来查阅特定版本的修订变动情况。所以为了更好的关注变动,了解到每次变动的版本改变的地方有哪些,对代码改动进行检查,能够预防事故发生。同时也能够随时切换到不同的版本,回滚误删误改的问题代码。
三、版本控制的发展历程
最早是本地版本控制,也就是通过复制文件夹+命名文件名的方式进行控制。
之前是“集中式版本控制”,比如SVN,提供一个服务器来维护版本代码,本地是不保存代码的。
后面逐渐发展成了“分布式版本控制”,Git,每个仓库都能记录版本历史,解决只有一个服务器保存版本的问题。
3.1 本地控制版本
最早是本地版本控制,也就是通过复制文件夹+命名文件名的方式进行控制。
当时最流行的就是RCS,能够保存本地所有变更的补丁集,也就是所谓的文件Diff(差异),通过这些补丁,能够计算出每个版本的实际文件内容差异。
缺点就是没有团队协作,使用场景比较有限,所以慢慢发展了后面的集中式版本控制。
3.2 集中式版本控制
“集中式版本控制”,比如SVN,提供一个服务器来维护版本代码,本地是不保存代码的。
原理就是:通过远程服务器,所有的团队成员都会提交到这个服务器中,增量保存每次提交的Diff,如果提交的增量和服务器的文件存在冲突(也就是A和B都对文件1做了修改,那么到底是以哪一个的修改为准,需要进行判断和解决冲突。),那么需要本地提前解决相关的冲突。
现在依然有很多团队用SVN,比如说游戏开发、设计团队等。因为操作起来很简单,容易上手,并且支持大文件、二进制文件。
缺点如下:
1、本地不存储版本管理的概念,只能上服务器提交。
2、分支上的支持不够友好,大型项目团队合作比较困难。
3、用户本地不保存所有版本的代码,如果服务端故障了可能会导致历史版本丢失。
3.3 分布式版本控制
分布式版本控制最大的特点就是每个库都有完整的提交历史,可以直接在本地进行代码提交。
每次提交都是完整的文件快照,而不是记录增量。
因此极大的方便了多人协同开发,并且校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失。
缺点是相对来说学习成本会比较高,比较复杂,并且对于大文件来说不是那么友好(通过git-lfs工具可以弥补这个功能)。
四、Git简单实战
4.1 git init
首先创建一个空的文件夹,然后在这个路径下VScode中输入命令git init
进行初始化,这个分支默认是master分支。
也可以通过命令 git init --initial-branch=main
来指定初始化分支(比如main分支)。
git init bare
:创建一个裸仓库,纯Git目录,没有工作目录,一般在服务器的仓库是这个命令创建出来的, 本地的话通过git init
命令。
git init --template
可以通过模版来创建预先构建好的自定义git目录。
我们来看看初始化的目录文件长什么样子:
直接在文件夹中看到的是这样的。
通过下面的命令可以看到HEAD存的是 当前指向的这个分支是什么,下面的图中表示当前的分支是master。
config指的是当前文件的配置。
objects存储的是一些文件信息。
refs会存一些分支信息。
Git可以分为工作区和暂存区,写代码的时候是在工作区,然后通过 git add
命令添加到暂存区,然后通过git commit
提交到git目录。
4.2 Git配置
Git配置可以分为全局global级(存储在当前用户的~/.gitconfig
里面)、系统system级(存储在/etc/gitconfig
下)、本地local级的配置(本地级别的配置就存在刚刚的.git/config
路径下)。
每个级别的配置是有可能重复的, 但是低级别的配置会覆盖高级别的配置,最高级别是系统级别,然后是全局级别,最低是本地级别。
http协议、ssh协议、https协议的转换配置:
git config --global url.git@github.com:.insteadOf https://github.com/
Git别名配置:可以通过命令git config --global alias.cin "commit --amend --no-edit"
通过把一些比较复杂的命令配置为相对简单的命令。
Git Remote配置指的是我们本地和远端github的连接的一些信息的配置。
通过命令 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
命令来设置remote。
设置好之后就可以看到如下的相关配置了。
然后通过 cat .git/config
可以查看到我们配置的信息。
也可以通过命令来设置进行fetch和push的两个源不是同一个:
git remote add origin git@github.com:/git.git
和 git remote set-url -add --push origin git@github.com:my_repo/git.git
命令进行设置。
操作完两个命令之后依旧可以通过 cat .git/config
查看到设置好的配置。
HTTP配置是走HTTP协议做认证,SSH是走SSH协议走的身份认证。
首先HTTP的URL是:https://github.com/git/git.git
进行免密配置的方式有两种:
内存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
进行操作。
但是相对来说呢HTTP没那么安全,并且没那么方便。所以一般会通过SSH远端方式进行连接。
ssh的URL为git@github.com:/git.git
免密配置:SSH通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问。
目前key的类型四种:dsa、rsa、ecdssa、ed25519四种,默认使用rsa,但是由于一些安全问题(windows新版容易把dsa、rsa给拒绝,所以配置后也没有用。),已经不推荐使用dsa和rsa了,优先推荐使用ed25519。
通过命令ssh-keygen -t ed25519 -C "邮箱地址"
,然后我们使用默认的文件地址,并输入两次设置的密码。
然后通过命令cat xxx(git给你的key路径).pub
查看刚刚生成的密钥内容(注意是路径+.pub结尾,比如 C://user/.ssh/id_25519.pub
),然后存到github上即可。
把文件输出的密钥,复制到下面的key中。
4.3 Git Add与Objects
接下来在刚刚的目录中创建readme文件,然后查看一下git的状态。
可以看到刚刚添加的read.md文件还在工作区,没有被跟踪。所以需要使用git add 命令来添加。
通过命令 git add readme.md
添加这个文件,然后后再次查看一次。
通过tree .git
可以看到这个路径下面的文件变更,可以看到obj文件下多了一个文件内容。
我们一路cd进来,可以看到对应的id。
通过命令如下,查看对应的文件内容,因为我的文件是空文件,所以这里是显示为空的(这其实是blob,也就是存储文件的内容)。
然后我们提交这个readme,并且继续查看对应的git目录结构文件。
可以看到objects文件目录下多了三个文件,分别是e6 d9 23,那么为什么一次git会多三个文件呢?
通过命令可以看到,这是一个目录树(tree)类型的文件。我们来详细分析一下:
git cat-file是查看git对象的内容,-p表示可以以人类可读的格式显示对象内容。然后239…这一长串是对象的SHA-1的哈希值,用于唯一标识Git仓库中的一个对象。
输出的100664表示文件的权限模式,表示是一个普通文件,权限为644,所有者可读写,组和其他用户可读。
blob表示是blob类型的对象,用于存储文件的内容。
而e69d…则是blob对象的sha-1哈希值,用于唯一标识这个blob对象。
blob文件的文件名是:readme.md.
代码除了本身的信息还会有一个目录树,所以这个239ec…的object是目录树类型的object,存储了刚才的目录信息。
然后继续查看另一个object对象文件,这个是一个commit类型的object文件。
可以看到这个存了tree目录树的ID信息(可以看到这个tree信息的sha-1值是239…,与前面的tree信息对应上了。)是什么,以及作者、commiter信息。
通过git log文件也可以看到,最新的commit对象是对应上的。
在git里面,·commit/tree/blob
都是称之为object,除此之外还有个tag的object。
Blob是存储文件的内容。Tree是存储文件的目录信息,Commit是存储提交信息,一个Commit可以对应唯一版本的代码。
所以我们可以梳理下,在commit
的objects文件中,第一行是tree信息,会存储对应的tree的ID,然后通过Tree的id,可以找到对应的目录树信息,在目录树tree中的信息中,可以找到对应的blob
文件信息及其对应的id,然后通过blob id可以找到对应的文件内容。
4.4 refs
在refs的路径下,我们查看对应的文件信息是什么。
可以看到存储的是一个commit的id,也就是我们刚刚commit的id。
通过git checkout -b test
命令创建一个名称为test的分支,可以看到在ref下面的head中多了一个test文件。
查看这个test的分支的内容,可以看到也是跟master的内容是一样的。(指向的内容是一样的)
也就是refs的内容是对应的commit ID,ref当做指针,指向对应的commit来表示当前Ref对应的版本。
refs/heads 前缀表示的是分支,除此之外还有其他种类的ref,比如说refs/tag前缀表示的是标签。
4.5 git checkout
通过git checkout -b
可以新建一个分支,分支一般用于开发阶段,可以不断添加commit进行迭代。
4.6 git tag
Tag表示的是稳定的版本,指向的commit一般不会变更。
通过git tag <版本名>
命令来生成tag,如下图所示。并且创建的这个tag的id跟刚刚我们看到的master和test是一样的。
4.7 Annotation Tag
附注标签,一种特殊的Tag,可以给Tag提供额外的信息。
通过命令 git tag -a v0.0.2 -m "add feature1
命令进行,并且所指向的id也跟以前的不同了。同时objects也新增了。
通过命令继续查看:git cat-file -p d7f3c8115d41f19dcd7b5b76dc9ac8a85e939e8b
这是我们见到第四个object类型,object提供的信息如图中所示,有tag信息、tag作者、tag名称等。
所以综上所述,一共有四个object:commit\tree\blob\tag
。
4.8 追溯历史版本
通过ref指向的commit可以获取唯一的代码版本。
然后commit里面会存有 parent commit字段,所以可以获取历史版本代码。
首先我们修改read.me里面的文件,然后通过git add .
代码进行登记和git commit -m "update readme"
进行更新的提交。
可以看到objects从4个变成了7个,因为新提交了一个commit,所以多了三个objects(也就是commit\blob\tree)。
可以看到在新的commit中,有parent字段,能够追踪到parent的objects的信息id。并且blob、tree中的id都变了。
4.9 修改历史版本
通过命令 commit --amend
来修改最近一次的commit信息,修改之后commit id会改变。
rebase
命令:
通过git rebase -i HEAD~3
可以实现对最近三个的commit的修改:
1、合并commit
2、修改某次具体的commit message
3、删除某个commit
filter --branch
可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作。
输入git commit --amend
会后会提示让我们修改commit信息:
我们在第一行修改之后保存,然后查看git log,并且查看对应的commit信息。
可以发现tree的id是没变的,也就是只会新增一个commit的object,其他的不会变化。经过验证,commit也从7个变成了8个。
新增一个commit object之后,之前旧的commit没有被删除,成为了悬空的object,顾名思义没有ref指向的object。
通过命令 git fsck --lost-found
来查看悬空的objects。
4.10 git GC
通过git GC命令,可以删除一些不需要的object,以及对object进行打包压缩来减少仓库的体积。
这里就涉及到了reflog,用于记录操作日志,防止误操作后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期。
通过git gc prune=now
指定修剪多久之前的对象,默认是两周前。
会发现.git已经大不一样了。(把objects打包成pack,把refs打包成packed-refs)。
4.11 完整的Git视图
4.12 git clone、pull、fetch
clone:拉取完整的仓库到本地目录,可以指定分支、深度。
fetch:将远端某些分支的最新代码拉取到本地,不会执行merge操作,会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动操作。(注意Fetch会拉取远端代码到本地的远端分支,但是并不会合并到当前的分支,所以当前的分支历史没变化。)
pull:拉取远端某个分支,并且和本地代码进行合并,操作等同于git fetch+git merge
,也可以通过git pull --rebase
完成fetch+merge操作。但是这个过程可能会存在冲突,需要解决对应的冲突。(如果对项目很了解,可以用git pull --rebase)
4.13 git push
将本地代码同步至远端的方式。
命令git push origin master
完成。
但是如果本地的commit和远端的commit历史不一样,会产生冲突,比如
git commit --amend
或者git rebase
都有可能导致这个问题。
强制推送的命令:git push origin master -f
,但是不推荐这么做。
也可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失了。
标签:文件,git,--,代码,Git,青训营,字长,commit From: https://blog.csdn.net/theaipower/article/details/145225500