- 学习基本的Git命令,并了解原理,在遇到Git相关问题时,能自行排查并解决
- 了解研发流程中的基本概念和规范,学会正确使用Git
Git基本使用方式
项目初始化
mkdir demo
cd demo
git init
git init
参数
--initial-brance
初始化的分支,默认是master
分支--bare
创建一个裸仓库(纯git目录,没有工作目录)--template
可以通过模板来创建预先构建好的自定义git目录
$ tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ └── update.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
config
: git配置HEAD
: 指向当前的分支 ref: refs/heads/masterobjects
: 文件信息refs
: 分支信息
- 工作区:本地的文件
- 暂存区:通过git add将文件加入到暂存区
- Git仓库:通过git commit提交到远程仓库
2.1.1 Git Config
--system
: 保存在 /etc/gitconfig 中--global
: 保存在 ~/.gitconfig 中--local
: 保存在 .git/config 中
每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置
用户名配置
git config --global user.name "kavin"
git config --global user.email 1817136760@qq.com
instead of配置
将https协议替换成ssh协议
git config --global url.git@github.com:.insteadOf https://github.com
Git命令别名配置
git config --global alias.cin "commit --amend --no-edit"
2.2 Git Remote
本地和远程仓库的关联信息
查看Remote
git remote -v
刚开始没有,需要添加
git remote add origin_ssh git@github.com:git/git.git
git remote add origin_http https://github.com/git/git.git
再次查看
$ 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)
config
文件也会有相应变化,多了origin_ssh
和origin_http
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = 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/*
2.2.2 SSH Remote
URL: git@github.com:git/git.git
免密配置
SSH可以通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问
目前Key的类型有4种,分别是dsa、rsa、ecdsa、ed25519
默认使用的是rsa,由于一些安全问题,现在推荐使用ed25519
$ ssh-keygen -t ed25519 -C "1817136760@qq.com"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/kavin/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kavin/.ssh/id_ed25519.
Your public key has been saved in /home/kavin/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:i0ZztEWiJGLtF60Pl4+Tw6fXq4Jmyn4HGGWxz40aer0 1817136760@qq.com
The key's randomart image is:
+--[ED25519 256]--+
| o.. oo. . |
| . ..o+o.o |
| . ooo... |
| o ++o= |
| ++=S+. |
| .o.BB.o |
| . =oo= . |
| . o= +.. . |
| .+= .Eo.... |
+----[SHA256]-----+
$ cat /home/kavin/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBv8hu55tnA1l7b0XdJ7/WDAdBE9ZiCXN7S3JdWYfoIL 1817136760@qq.com
然后添加到github
2.3 Git Add
添加一个文件
$ vim README.md
查看当前状态,是红色的
$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
nothing added to commit but untracked files present (use "git add" to track)
执行git add .
之后,就加入了暂存区
再次查看状态,变成绿色了
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
在objects
目录下生成一个文件
├── objects
│ ├── 3b
│ │ └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
可以查看该文件,保存的就是新建的文件的内容
$ git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world
提交
$ git commit -m "add readme"
[master (root-commit) c18499f] add readme
1 file changed, 1 insertion(+)
create mode 100644 README.md
objects
目录再次发生变化
├── objects
│ ├── 3b
│ │ └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
│ ├── 43
│ │ └── b71c903ff52b9885bd36f3866324ef60e27b9b
│ ├── c1
│ │ └── 8499fff0da1411179060f661152f5b92e41cfe
同样可以查看,第一个文件保存的是目录树
$ git cat-file -p 43b71c903ff52b9885bd36f3866324ef60e27b9b
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad README.md
第二个文件保存的是提交信息
$ git cat-file -p c18499fff0da1411179060f661152f5b92e41cfe
tree 43b71c903ff52b9885bd36f3866324ef60e27b9b
author kavinwkp <1817136760@qq.com> 1653704489 +0800
committer kavinwkp <1817136760@qq.com> 1653704489 +0800
add readme
可以从git log
里面看到,两个commit
序号是一样的
commit c18499fff0da1411179060f661152f5b92e41cfe (HEAD -> master)
Author: kavinwkp <1817136760@qq.com>
Date: Sat May 28 10:21:29 2022 +0800
add readme
2.5 Objects
commit/tree/blob
在git
里面都统一称为Object
blob
: 存储文件内容tree
: 存储文件目录信息commit
: 存储提交信息,一个commit可以对应唯一版本的代码
1、通过commit
寻找到tree
信息,每个commit
都会存储相应的tree id
$ git cat-file -p c18499fff0da1411179060f661152f5b92e41cfe
tree 43b71c903ff52b9885bd36f3866324ef60e27b9b
author kavinwkp <1817136760@qq.com> 1653704489 +0800
committer kavinwkp <1817136760@qq.com> 1653704489 +0800
add readme
2、通过tree
存储的信息,获取到对应的目录树信息
$ git cat-file -p 43b71c903ff52b9885bd36f3866324ef60e27b9b
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad README.md
3、从tree
中获得blob id
,通过blob id
获取对应的文件内容
$ git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world
2.6 Refs
refs
文件夹下也会发生变化
$ cat .git/refs/heads/master
c18499fff0da1411179060f661152f5b92e41cfe
保存的是刚才提交的
commit id
新建分支
$ git checkout -b test
Switched to a new branch 'test'
可以看到在refs/heads
目录下生成了test
文件
└── refs
├── heads
│ ├── master
│ └── test
其内容和master
是一样的
$ cat .git/refs/heads/test
c18499fff0da1411179060f661152f5b92e41cfe
refs
的内容就是对应的commit id
,因此把ref
当做指针,指向对应的commit
来表示当前ref
对应的版本
不同种类的ref
refs/heads
前缀表示的是分支,refs/tags
前缀表示的是标签
分支一般用于开发阶段,是可以不断添加commit
进行迭代
标签一般表示的是一个稳定版本,指向的commit
一般不会变更
通过git tag
命令生成tag
$ git tag v0.0.1
可以看到tags
目录下生成了v0.0.1
└── refs
├── heads
│ ├── master
│ └── test
└── tags
└── v0.0.1
内容跟master
是一样的
$ cat .git/refs/tags/v0.0.1
c18499fff0da1411179060f661152f5b92e41cfe
2.7 Annotation Tag
附注标签:一种特殊的tag,可以给tag提供一些额外的信息
$ git tag -a v0.0.2 -m "add feature 1"
可以看到生成了v0.0.2
标签
└── refs
├── heads
│ ├── master
│ └── test
└── tags
├── v0.0.1
└── v0.0.2
可以查看其内容
$ cat .git/refs/tags/v0.0.2
20883e70ce48a660349d0ce58974a6d0113983a0
其内容指向了objects
下新增的文件
├── objects
│ ├── 20
│ │ └── 883e70ce48a660349d0ce58974a6d0113983a0
查看其内容
$ git cat-file -p 20883e70ce48a660349d0ce58974a6d0113983a0
object c18499fff0da1411179060f661152f5b92e41cfe
type commit
tag v0.0.2
tagger kavinwkp <1817136760@qq.com> 1653707227 +0800
add feature 1
其中object
表示它指向的真正的commit id
,同时还有一些附加信息
2.8 追溯历史版本
- 获取当前版本代码
通过ref
指向的commit
可以获取唯一的代码版本
- 获取历史版本代码
commit
里面会存有parent commit
字段,通过commit
的串联获取历史版本代码
再次修改README.md并提交
git add .
git commit -m "update readme"
通过git log
查询到commit id
commit c0931534a6d62c4b924a9e52e3bf16c3a8055ef2 (HEAD -> test)
Author: kavinwkp <1817136760@qq.com>
Date: Sat May 28 11:15:41 2022 +0800
update readme
然后查看commit id
的内容
$ git cat-file -p c0931534a6d62c4b924a9e52e3bf16c3a8055ef2
tree 3981df8931e1d31cc30e98366fddae7716919cf8
parent c18499fff0da1411179060f661152f5b92e41cfe
author kavinwkp <1817136760@qq.com> 1653707741 +0800
committer kavinwkp <1817136760@qq.com> 1653707741 +0800
update readme
通过parent
字段可以知道当前commit
的前一个commit
通过tree id
查询到blob id
,获取到文件内容
$ git cat-file -p 3981df8931e1d31cc30e98366fddae7716919cf8
100644 blob 759f3c31e352fe4917e59f548ec26b40e5771d4f README.md
$ git cat-file -p 759f3c31e352fe4917e59f548ec26b40e5771d4f
#hello world
test
也指向了最新的commit
$ cat .git/refs/heads/test
c0931534a6d62c4b924a9e52e3bf16c3a8055ef2
2.9 修改历史版本
commit --amend
通过这个命令可以修改最近的一次commit
信息,修改之后commit id
会变
rebase
通过git rebase -i HEAD~3
可以实现对最近三个commit
的修改
- 合并commit
- 修改具体的commit message
- 删除某个commit
filter --branch
该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作
使用git commit --amend
修改提交信息
然后git log
,发现跟之前不一样了
commit aeb4d5e973f400acd452097bd7504141fcc40345 (HEAD -> test)
Author: kavinwkp <1817136760@qq.com>
Date: Sat May 28 11:15:41 2022 +0800
update readme!!!
可以看到commit
信息发生了变化,正是刚才修改后的样子
$ git cat-file -p aeb4d5e973f400acd452097bd7504141fcc40345
tree 3981df8931e1d31cc30e98366fddae7716919cf8
parent c18499fff0da1411179060f661152f5b92e41cfe
author kavinwkp <1817136760@qq.com> 1653707741 +0800
committer kavinwkp <1817136760@qq.com> 1653709256 +0800
update readme!!!
但是tree
和parent
还是一样的
同时之前的那个commit
还存在,只是没有refs
指向它
$ git fsck --lost-found
Checking object directories: 100% (256/256), done.
dangling commit c0931534a6d62c4b924a9e52e3bf16c3a8055ef2
2.11 Git GC
通过git gc
命令,可以删除一些不需要的object
,以及会对object
进行一些打包压缩来减少仓库的体积
reflog
是用于记录操作日志,防止误操作后数据丢失,通过reflog
来找到丢失的数据,手动将日志设置为过期
git gc prune=now
指定的是修剪多久之前的对象,默认是两周前
$ git reflog expire --expire=now --all
$ git gc --prune=now
Counting objects: 7, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (7/7), done.
Total 7 (delta 0), reused 0 (delta 0)
这样没有的commit
就会被删掉了
$ git cat-file -p c0931534a6d62c4b924a9e52e3bf16c3a8055ef2
fatal: Not a valid object name c0931534a6d62c4b924a9e52e3bf16c3a8055ef2
2.13 Git Clone & Pull & Fetch
Clone
拉取完整的仓库到本地目录,可以指定分支,深度
Fetch
将远端某些分支最新代码拉取到本地,不会指定merge操作
会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动操作
Pull
拉取远端某分支,并和本地代码进行合并,操作等同于git fetch + git merge
也可以通过git pull --rebase
完成
可能存在冲突,需要解决冲突
2.14 Git Push
将本地代码同步至远端
一般使用 git push origin main
命令
冲突问题
如果本地的commit
记录和远端的commit
历史不一致,则会发生冲突
如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过git push origin main -f
来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送
推送规则限制
可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码对视
Git研发流程
$ git push origin feature
Counting objects: 3, done.
Writing objects: 100% (3/3), 254 bytes | 254.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'feature' on GitHub by visiting:
remote: https://github.com/kavinwkp/demo/pull/new/feature
remote:
To github.com:kavinwkp/demo.git
* [new branch] feature -> feature
新建分支并提交之后,会生成一个链接,打开连接之后,可以pull request
提交一个请求
确认没问题之后就可以Merge pull request
这样远程仓库就进行了合并分支
再拉取到本地
$ git pull origin main
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From github.com:kavinwkp/demo
* branch main -> FETCH_HEAD
76b33cd..f0d5eb3 main -> origin/main
Updating 76b33cd..f0d5eb3
Fast-forward
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
通过git log
可以看到进行了一次合并
commit f0d5eb3189ffcb24215fd8a3feee198e863ea197 (HEAD -> main, origin/main)
Merge: 76b33cd 7c4849c
Author: kavinwkp <71803246+kavinwkp@users.noreply.github.com>
Date: Sat May 28 14:34:08 2022 +0800
Merge pull request #1 from kavinwkp/feature
update readme
代码合并
- Fast-Forward
不会产生一个merge
节点,合并后保持一个线性历史,如果target
分支有了更新,则需要通过rebase
操作更新source branch
后才可以合入
比如新建分支test
,然后做一个修改并提交,然后切换会main
分支进行合并
$ git merge test --ff-only
Updating f0d5eb3..a18575f
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
可以从git log
看出,没有产生merge
节点
commit a18575febae09bc14fec2b54aa243079ac3f6c79 (HEAD -> main, test)
Author: kavinwkp <1817136760@qq.com>
Date: Sat May 28 14:44:40 2022 +0800
test
- Three-Way Merge
三方合并,会产生一个新的merge
节点
同样新建分支test
,然后做一个修改并提交,然后切换会main
分支进行合并
$ git merge test --no-ff
Merge made by the 'recursive' strategy.
README.md | 1 +
1 file changed, 1 insertion(+)
可以从git log
看出增加了merge
节点,跟pull request
是一样的
commit 2d9ba4e378b8e4a5c49298fb908566ad953a6eaa (HEAD -> main)
Merge: a18575f 23ab11b
Author: kavinwkp <1817136760@qq.com>
Date: Sat May 28 14:49:49 2022 +0800
Merge branch 'test' into main