git——一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理,帮助我们更好的组织项目、控制项目,并且帮助我们进行更高效的团队开发。接下来,让我们走进git,掌握git的正确使用姿势。
邂逅Git:
git的初始化:
使用指令创建一个git仓库
➜ github mkdir git-study
目录: E:\github
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2022/5/25 14:48 git-study
➜ github cd .\git-study\
➜ git-study ls
➜ git-study git init
Initialized empty Git repository in E:/github/git-study/.git/
➜ git-study git:(master)
查看刚刚创建的仓库目录:
git的使用过程中主要有三个区域—工作区、暂存区和远程仓库,当我们在修改代码时,是在工作区进行代码的编写(Working Directory),当我们完成代码编写后会将代码添加到暂存区(Staging Area),最后将提交到远程仓库(Repository),后续我们会对这个过程进行详细解读。
git的配置:
三种级别配置:
git主要有三种不同级别的配置,每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置。
global:全局配置
system:系统配置
local:本地配置
常见git配置:
用户名配置:
➜ git-study git:(master) ✗ git config --global user.name "你的仓库用户名"
➜ git-study git:(master) ✗ git config --global user.email 仓库邮箱
注意:用户名的配置关系到你能否正确连接到你的远程仓库
instead of 配置:
➜ git-study git:(master) ✗ git config --global url.git@github.com:.insteadOf <https://github.com/>
将url “git@github.com” 替换为 “github.com/”
也可以进行ssh替换为http的协议替换。
Git命令别名配置:
➜ git-study git:(master) ✗ git config --global alias.cin "commit --amend --no-edit"
配置后:cin = commit —amend —no-edit
Git Remote配置:(配置源,用来拉取代码和push代码)
- 添加ssh和http两种协议的remote:
➜ git-study git:(master) git remote add origin_ssh git@github.com:git/git.git
➜ git-study git:(master) git remote add origin_http <https://github.com/git/git.git>
➜ git-study git:(master) git remote -v
origin_http <https://github.com/git/git.git> (fetch)
origin_http <https://github.com/git/git.git> (push)
origin_ssh git@github.com:git/git.git (fetch)
origin_ssh git@github.com:git/git.git (push)
查看remote内容的指令:git remote -v
查看现在的config配置,会发现多了两个关于remote的配置,分别记录了两个remote的url和fetch
➜ git-study git:(master) cat .git/config
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
ignorecase = true
[remote "origin_ssh"]
url = git@github.com:git/git.git
fetch = +refs/heads/*:refs/remotes/origin_ssh/*
[remote "origin_http"]
url = <https://github.com/git/git.git>
fetch = +refs/heads/*:refs/remotes/origin_http/*
- 对同一个Origin设置不同的Push和Fetch URL,可以实现拉取开源库然后push到自己的仓库里:
➜ git-study git:(master) git remote add origin git@github.com:git/git.git
➜ git-study git:(master) git remote set-url --add --push origin git@github.com:my_repo/git.git
// 查看一下remote发现新添加的remote的push和fetch不是一个源
➜ git-study git:(master) git remote -v
origin git@github.com:git/git.git (fetch)
origin git@github.com:my_repo/git.git (push)
origin_http <https://github.com/git/git.git> (fetch)
origin_http <https://github.com/git/git.git> (push)
origin_ssh git@github.com:git/git.git (fetch)
origin_ssh git@github.com:git/git.git (push)
- HTTP Remote与SSH Remote
一般我们都建议使用SSH连接远程仓库,也就是使用公私钥机制,SSH通过公私钥的机制,将生成的公钥存放在服务端,从而实现免密访问。HTTP相对来说不够安全,而且连接的机制也不方便。
SSH连接过程:
- 生成自己的公钥
然后通过打开公钥存储的文件,得到公钥。
- 然后保存到github上即可
Git指令基本操作:
git add—将数据保存到暂存区
首先在当前目录下新建一个readme文件并编辑
然后使用git add命令将修改的内容添加到暂存区:
➜ ~ ✗ cd E:
➜ E:\ cd .\github\git-study\
➜ git-study git:(master) ls
➜ git-study git:(master) code .
➜ git-study git:(master) git add .
➜ git-study git:(master) git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: readme.md
➜ git-study git:(master) tree .git
Folder PATH listing for volume 文档
Volume serial number is 3E82-D03A
E:\GITHUB\GIT-STUDY\.GIT
├───hooks
├───info
├───objects
│ ├───95
│ ├───info
│ └───pack
└───refs
├───heads
└───tags
➜ git-study git:(master)
git commit—将修改内容提交到远程仓库中
使用git commit命令将在存储区的内容提交到远程仓库:
➜ git-study git:(master) ✗ git commit -m"add readme"
[master (root-commit) 3df8d78] add readme
1 file changed, 1 insertion(+)
create mode 100644 readme.md
➜ git-study git:(master) tree .git
Folder PATH listing for volume 文档
Volume serial number is 3E82-D03A
E:\GITHUB\GIT-STUDY\.GIT
├───hooks
├───info
├───logs
│ └───refs
│ └───heads
├───objects
│ ├───3d
│ ├───95
│ ├───9a
│ ├───info
│ └───pack
└───refs
├───heads
└───tags
➜ git-study git:(master)
通过git日志查看此次操作,会发现整个提交分为了三部分,而第一部分有一串16进制的代码,这个就是此次commit最主要的内容,为commit ID。
➜ git-study git:(master) git log
commit 3df8d78b7e7a2dacbfd21a936cb918989c3f5b64 (HEAD -> master)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 00:01:56 2022 +0800
add readme
➜ git-study git:(master)
那现在就让我们来深究一下这个commit ID代表了什么, 首先先使用指令解构查看一下commit这串代码中存储了什么,
➜ git-study git:(test) git cat-file -p 3df8d78b7e7a2dacbfd21a936cb918989c3f5b64
tree 9aa4816ed4d7cce80d7fa619412de2aebededcee
author Flandre3569 <1332726596@qq.com> 1653494516 +0800
committer Flandre3569 <1332726596@qq.com> 1653494516 +0800
会发现打印出了三个不同的内容,让我们一步一步的解构,看看最后能打印出什么。
➜ git-study git:(test) git cat-file -p 9aa4816ed4d7cce80d7fa619412de2aebededcee
100644 blob 95d09f2b10159347eece71399a7e2e907ea3df4f readme.md
➜ git-study git:(test) git cat-file -p 95d09f2b10159347eece71399a7e2e907ea3df4f
hello world
我们发现,经过再二重映射之后,打印出了我们编写的内容,这就是commit ID与内容的映射过程。
commit ID提交内容的本质:
然后切换到新的分支:
➜ git-study git:(master) git checkout -b test
Switched to a new branch 'test'
当我们执行以上指令的时候,会新生成一个分支,生成这个分支后我们可以打印里面的内容看看,会发现test分支中存储的内容和master存储的内容是一样的,然后我们再去打印一个git日志,会发现master、test和log里面都有一串相同的内容,那就是commit ID,因此不难看出,refs的内容就是对应的Commit ID,因此把ref当做指针,指向对应的Commit来表示当前的Ref对应的版本。refs/heads前缀表示的是分支,还有代表其它内容的refs比如refs/tags等。
➜ git-study git:(test) git log
commit 3df8d78b7e7a2dacbfd21a936cb918989c3f5b64 (HEAD -> test, master)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 00:01:56 2022 +0800
add readme
➜ git-study git:(test) cat .git/refs/heads/master
3df8d78b7e7a2dacbfd21a936cb918989c3f5b64
➜ git-study git:(test) cat .git/refs/heads/test
3df8d78b7e7a2dacbfd21a936cb918989c3f5b64
- Branch — 新分支,一般用于开发阶段
- Tag — 稳定版本
- Annotation Tag — 附注标签,为tag添加附属内容
对于tag,我们可以新建一个tag看看分布情况,一般一个tag代表一个版本的内容,指向的commit一般不会变化。
➜ git-study git:(test) git tag v0.0.1
➜ git-study git:(test) tree .git
Folder PATH listing for volume 文档
Volume serial number is 3E82-D03A
E:\GITHUB\GIT-STUDY\.GIT
├───hooks
├───info
├───logs
│ └───refs
│ └───heads
├───objects
│ ├───3d
│ ├───95
│ ├───9a
│ ├───info
│ └───pack
└───refs
├───heads
└───tags
Git与版本控制:
获取历史版本代码:
我们获取当前版本代码是十分容易的,使用当前指向的commit就可以获取当前版本的代码,那么我们该怎么获取之前版本的代码呢?
首先先更改一次上面我们文件中的内容,生成一个历史文件。
➜ git-study git:(test) git add .
➜ git-study git:(test) git commit -m"update readme"
[test a0af44ff65] update readme
1 file changed, 1 insertion(+), 1 deletion(-)
然后按照老方法,先追踪一下映射,看看能不能发现什么。
➜ git-study git:(test) git log
commit a0af44ff657c576765657bbc15de4e4521b375a7 (HEAD -> test)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 13:35:18 2022 +0800
update readme
commit 3df8d78b7e7a2dacbfd21a936cb918989c3f5b64 (tag: v0.0.2, tag: v0.0.1, master)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 00:01:56 2022 +0800
add readme
➜ git-study git:(test) git cat-file -p a0af44ff657c576765657bbc15de4e4521b375a7
tree 96600c3ad0b50016b0b6857de382c891a9b2ad68
parent 3df8d78b7e7a2dacbfd21a936cb918989c3f5b64
author Flandre3569 <1332726596@qq.com> 1653543318 +0800
committer Flandre3569 <1332726596@qq.com> 1653543318 +0800
update readme
此时我们发现,在打印Commit ID指向的内容时,出现了一个之前不存在的属性,即**parent** ,即可通过该属性找到之前版本的代码。
修改历史版本:
- commit — amend
修改最近一次的commit提交信息,修改之后commit ID会产生变化。
- rebase
通过git rebase -i HEAD~n可以实现对最近n个commit的修改:
- 合并commit
- 修改具体的commit massage
- 删除某个commit
- filter — branch
指定删除提交中的某个文件或全局修改邮箱地址等。
尝试一下修改最近一次commit提交信息:
输入git commit —amend 会进入一个vim面板,在这个面板里可以修改之前提交的commit信息。
➜ git-study git:(test) git cat-file -p 77bcb95e0723bc355ecb523400e5acf1c2531187
tree 96600c3ad0b50016b0b6857de382c891a9b2ad68
parent 3df8d78b7e7a2dacbfd21a936cb918989c3f5b64
author Flandre3569 <1332726596@qq.com> 1653543318 +0800
committer Flandre3569 <1332726596@qq.com> 1653544720 +0800
update readme file
打印log发现,commit的提交内容确实发生了变化,并且本身的commit ID也发生了变化,但是tree和parent并没有发生变化,其实这里修改后是产生了一条新的commit ID指向,并且之前的commit ID并没有被删除,我们可以搜索检查一下文件系统查看当前的文件状态,
➜ git-study git:(test) git fsck --lost-found
Checking object directories: 100% (256/256), done.
Checking objects: 100% (327349/327349), done.
Checking connectivity: 327356, done.
dangling commit a0af44ff657c576765657bbc15de4e4521b375a7
会发现确实有一个内容处于悬空状态,并且确实是我们之前的commit ID,既然这个已经处于悬空状态了,这个object现在其实已经没用了,它的存在只会占据我们的空间,所以我们也可以使用一些方法进行垃圾回收。
Git GC
删除不需要的object,并进行打包压缩:
➜ git-study git:(test) ✗ git reflog expire --expire=now --all
➜ git-study git:(test) git gc --prune=now
Enumerating objects: 327356, done.
Counting objects: 100% (327356/327356), done.
Delta compression using up to 8 threads
Compressing objects: 100% (80236/80236), done.
Writing objects: 100% (327356/327356), done.
Total 327356 (delta 244725), reused 327349 (delta 244725), pack-reused 0
Checking connectivity: 327356, done.
这时候我们在追踪修改前的悬空的那条commit ID,会发现已经查找不到了。
➜ git-study git:(test) git cat-file -p a0af44ff657c576765657bbc15de4e4521b375a7
fatal: Not a valid object name a0af44ff657c576765657bbc15de4e4521b375a7
Git的多人合作和远端仓库同步:
当我们需要拉取远端仓库的代码时,有三种方式:
git clone— 拉取完整的仓库到本地git fetch— 拉取某些分支最新代码到本地,不会执行merge操作git pull— 拉取某些分支,并和本地代码进行合并,等同于 git fetch + git merge
当我们第一次想要完整的拉取远端仓库时,使用git clone直接将代码克隆到本地;
当我们想要拉取远端仓库的更新时,如果我们比较熟悉远端仓库代码修改了哪些内容时,建议使用git pull —rebase 实现 git fetch + git rebase的操作;如果我们不熟悉远端仓库修改了哪些内容时,建议使用git fetch先拉取代码,然后自己手动合并。
将本地代码同步至远端仓库:
git push
冲突问题:
使用push命令时会产生冲突问题,如果本地commit记录和远端的commit记录不一致,则会产生冲突,需要先解决冲突后再进行推送。
使用Git依托于远端仓库研发流程:
分支管理工作流:
github和gitlab使用的是分支管理工作流,可以定义不同性质的开发分支和上线分支,从开发分支上进行开发,开发完成后通过MR/PR合入主干分支。这不同于集中式工作流,集中式工作流只依托于master分支进行研发活动,直接在master分支进行开发,拉取代码和push代码都是到master分支,集中式工作流开发人员较多的情况下更容易产生冲突。
对于分支管理工作流,主要有三种分支:
- Git Flow
- Github Flow
- Gitlab Flow
这里我们主要根据github的分制管理工作流进行讲解,Github的工作流只有一个主干分支,基于Pull Request 往主干分支中提交代码,团队合作的方式主要有两种:
- owner 创建好仓库后,其他用户通过fork的方式来创建自己的仓库,并在fork的仓库上进行研发。
- owner 创建好仓库后,统一给团队内部成员分配权限,直接在同一个仓库内进行研发。
一般自己团队的成员,直接使用第二种方法进行研发即可。
基于Github的分支管理工作流使用流程:
那么现在让我们开始进行实际操作一下。
首先先在github中新建一个仓库,然后把仓库克隆到本地,
➜ git-study git:(test) git clone git@github.com:Flandre3569/git-study-demo.git
Cloning into 'git-study-demo'...
warning: You appear to have cloned an empty repository.
➜ git-study git:(test) ls
目录: E:\github\git-study
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2022/5/26 21:58 git-study-demo
-a---- 2022/5/26 13:32 12 readme.md
➜ git-study git:(test)
然后在项目目录下新建一个文件,使用readme文件即可,然后提交到远端库上。
➜ git-study git:(test) cd .\git-study-demo\
➜ git-study-demo git:(master) git add .
➜ git-study-demo git:(master) git commit -m"add readme"
[master (root-commit) 8cb345e] add readme
1 file changed, 1 insertion(+)
create mode 100644 readme.md
➜ git-study-demo git:(master) ✗ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 217 bytes | 72.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:Flandre3569/git-study-demo.git
* [new branch] master -> master
➜ git-study-demo git:(master)
然后回到我们的github中发现readme文件已经上传到远端仓库中了。
新建一个分支,切换到新分支然后对代码进行修改,然后提交到新的分支中,
➜ git-study-demo git:(master) git checkout -b feature
Switched to a new branch 'feature'
➜ git-study-demo git:(feature) git add .
➜ git-study-demo git:(feature) git commit -m"update readme"
[feature a5a836c] update readme
1 file changed, 1 insertion(+), 1 deletion(-)
➜ git-study-demo git:(feature) git push origin feature
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 255 bytes | 85.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a pull request for 'feature' on GitHub by visiting:
remote: <https://github.com/Flandre3569/git-study-demo/pull/new/feature>
remote:
To github.com:Flandre3569/git-study-demo.git
* [new branch] feature -> feature
➜ git-study-demo git:(feature)
我们可以看到在remote中给予了一个链接,进入链接所指的网页,我们会来到pull request页面,点击create pull request新建一个pull request,然后进入如图页面,
在该页面可以看到修改的内容,可以进行冲突检测和check设置等,我们也可以在这个页面进行此次变更的讨论,当我们确认此次修改没问题后,便可以进行合入主分支,也就是点击页面中的merge pull request ,那么我们此次操作就合入主分支代码了。
回到本地,在终端上拉取远端库的代码更新,
➜ git-study-demo git:(feature) git pull origin master
From github.com:Flandre3569/git-study-demo
* branch master -> FETCH_HEAD
Already up to date.
➜ git-study-demo git:(feature) git log
commit a5a836cd75f354d88ecbef860003c67c434f653b (HEAD -> feature, origin/feature)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 22:06:41 2022 +0800
update readme
commit 8cb345e5cbd7b0924d18c64d6f664515916dfee1 (origin/master, master)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 22:02:18 2022 +0800
add readme
➜ git-study-demo git:(feature)
至此,我们便把依托于github的使用流程进行了一遍。
当然我们可以通过github的设置进行一些提交的限制,让我们的研发流程更加规范。
基于Gitlab的分支管理工作流:
gitlab推荐的工作流是在git flow和github flow上做出优化,既保持单一主分支的简便,又可以适应不同的开发环境。
原则:upstream first 上游优先
只有在上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master。
代码合并:
- Fast-Forward:保持线性,不会产生新的merge节点。
- Three-Way Merge:三方合并,会产生一个新的merge节点。
尝试一下合并分支:
使用Fast-Forward方法进行分支合并:
新建一个test分支,然后修改一下readme的内容,再切回到主分支,将test分支合并到master分支,
➜ git-study-demo git:(feature) git checkout -b test
Switched to a new branch 'test'
➜ git-study-demo git:(test) ls
目录: E:\github\git-study\git-study-demo
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/5/26 22:16 21 readme.md
➜ git-study-demo git:(test) code .
➜ git-study-demo git:(test) git add .
➜ git-study-demo git:(test) git commit -m"test"
[test b15b643] test
1 file changed, 1 insertion(+), 1 deletion(-)
➜ git-study-demo git:(test) git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
➜ git-study-demo git:(master) ✗ git merge test --ff-only
Updating 8cb345e..b15b643
Fast-forward
readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
我们发现现在分支已经合并成功,然后我们打印一下日志,看看会不会产生merge节点,
➜ git-study-demo git:(master) git log
commit b15b643a234d23261949eb3962a7169db07694d1 (HEAD -> master, test)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 22:49:25 2022 +0800
test
commit a5a836cd75f354d88ecbef860003c67c434f653b (origin/feature, feature)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 22:06:41 2022 +0800
update readme
commit 8cb345e5cbd7b0924d18c64d6f664515916dfee1 (origin/master)
Author: Flandre3569 <1332726596@qq.com>
Date: Thu May 26 22:02:18 2022 +0800
add readme
➜ git-study-demo git:(master)
我们发现,使用该方法进行分支合并并不会产生新的merge节点。
使用Three-Way Merge进行分支合并:
➜ git-study-demo git:(test) git add .
➜ git-study-demo git:(test) git commit -m"update readme"
[test bafce6f] update readme
1 file changed, 2 insertions(+), 1 deletion(-)
➜ git-study-demo git:(test) git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
➜ git-study-demo git:(master) git merge test --no-ff
Merge made by the 'recursive' strategy.
readme.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
然后打印一下日志看这次合并的情况:
我们会发现这次产生了一个新的merge节点。
至此,我们便把Git依托于Github的基础知识与实践流程进行了完整的学习。