本篇博客介绍 Git 的基本使用,争取按规范的使用 Git 使开发流程更加规范。
.git 文件结构
以下是一个 .git 文件的结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
| .git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ ├── heads
│ │ └── main
│ └── remotes
│ └── origin
│ ├── HEAD
│ └── main
├── modules
│ └── themes
│ └── FixIt
│ ├── branches
│ ├── config
│ ├── description
│ ├── HEAD
│ ├── hooks
│ │ ├── applypatch-msg.sample
│ │ ├── commit-msg.sample
│ │ ├── fsmonitor-watchman.sample
│ │ ├── post-update.sample
│ ├── index
│ ├── info
│ │ └── exclude
│ ├── logs
│ │ ├── HEAD
│ │ └── refs
│ │ ├── heads
│ │ │ └── master
│ │ └── remotes
│ │ └── origin
│ │ └── HEAD
│ ├── objects
│ │ ├── info
│ │ └── pack
│ │ ├── pack-3f2759e5d239cc34913467329d51643b48f3e651.idx
│ │ └── pack-3f2759e5d239cc34913467329d51643b48f3e651.pack
│ ├── packed-refs
│ └── refs
│ ├── heads
│ │ └── master
│ ├── remotes
│ │ └── origin
│ │ └── HEAD
│ └── tags
├── objects
│ ├── 0e
│ │ └── f1525d81bad33f58a45e7a6c8018056e98bd02
│ ├── 12
│ │ └── 4aa45213eb54a6b7c7d5b715b88837086ee27f
│ ├── 1e
│ │ └── 14dfb2d0fa8c159232825937cb68013f268154
│ ├── 33
│ │ └── 5c6256c5ce8569a84743471d92376488b8595e
│ ├── 3c
│ │ └── 8f38a136aa07edbd81c8aa92562b1b16139b36
│ ├── info
│ └── pack
│ ├── pack-4b3a92177530dc3b688096fe7081564903e6e4bb.idx
│ └── pack-4b3a92177530dc3b688096fe7081564903e6e4bb.pack
├── packed-refs
└── refs
├── heads
│ └── main
├── remotes
│ └── origin
│ ├── HEAD
│ └── main
└── tags
|
- HEAD 存储当前指向的分支
- config git 配置信息
- hooks 配置 hook
- object 存储一些文件信息 (如git add 后会生成暂存区文件)
- refs 存储分支信息
object 文件
1
2
3
4
5
| ├── objects
│ ├── 0e
│ │ └── f1525d81bad33f58a45e7a6c8018056e98bd02
│ ├── 12
│ │ └── 4aa45213eb54a6b7c7d5b715b88837086ee27f
|
object 有三种类型: commit, tree, blob 和一个 tag 的 object 类型。
- blob 存储文件内容(git add 后产生)
- tree 存储文件目录信息
- commit 存储文件提交信息,对应唯一的版本代码
获取当前版本代码:会根据 commit 中的 tree ID 寻找 tree 信息,通过 tree 存储信息获取对应目录树信息,再从 tree 中找到 blob ID 获取文件内容。
如文件头 0e
加上后面的 f1525d81bad33f58a45e7a6c8018056e98bd02
为文件的 object id。
该文件进行了加密,但可以使用 git 命令查看文件信息
1
| git cat-file -p 0ef1525d81bad33f58a45e7a6c8018056e98bd02
|
如果当修改了一个历史的 commit 可能会造成悬空 object,即历史的 commit 没有 refs 指向。
1
2
3
4
5
| # 查看悬空的 obj
git fsck --lost-found
# 删除不需要的 obj
git gc
|
Refs
存储文件内容,实际是一个指针指向对应 commit ID,一般指向最新的 commit。他有不同种类 refs/heads
表示分支,refs/tags
表示标签。
- git reflog 查看本地更新过 HEAD 的 commit 记录。
git tag 表示一个稳定的不常更新的分支。
1
2
3
4
| git tag v0.0.1
# 添加附注标签 Annotation Tag
git tag -a v0.0.2 -m "bla bla bla"
|
refs/tags
文件中存储了 object 信息和标签信息,包括那个用户打了标签。
git 配置
git 配置分为三个级别从高到低为 --system
, --global
, --local
。每个级别的配置可能重复,低级别会覆盖高级别配置。
--system
系统配置存储在 /etc/gitconfig
--global
全局配置存储在 ~/.gitconfig
--local
本地配置存储在 .git/config
http 和 ssh 远端区别
- http 远端走 http 的认证方式(token)
- ssh 走 ssh 的认证方式(公钥私钥)出于安全推荐使用 ed25519 加密
go 开发依赖库很多时经常会 pull 一些远端仓库,因此有时验证是十分麻烦的。
http 免密配置方式:
1
2
3
4
5
| # 内存配置免密
git config --global credential.helper 'cache --timeout=3600'
# 硬盘配置免密 默认路径 ~/.git-credential
git config --global credential.helper "store --file /path/to/credential-file"
|
常用的配置指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 修改信息
git config --global user.name "xxx"
git config --global user.email "xxx"
# Instead of 配置(替换 git 传输方式,比如下面把 ssh 替换为 https 传输)
git config --global url.git@github.com:.insteadOf https://github.com/
# git 自定义命令别名(下面把 commit --amend --no-edit 命令替换为 cin)
git config --global alias.cin "commit --amend --no-edit"
##---远端配置---##
# 查看远端
git remote -v
# 设置 fetch 和 push 不同的远端(下面我单独设置了 push 远端)
git remote set--url --add --push origin git@github.com:my_repo/git.git
|
git gc
删除一些不需要的 object 与打包 object 减少仓库体积。
使用该指令需要进行一些前置操作才可执行。
- reflog 用于记录操作日志,防止误操作。
- git gc prune=now 指定修改多长时间的对象,默认两周前
1
2
| git reflog expire --expire=now --all
git gc --prune=now
|
gitignore
Git 会自动忽略空文件夹,因此在想要忽略某个文件夹下的全部文件,但还想保留上传该父文件夹时需要在父文件夹目录下创建 .gitignore
。
忽略规则相关常用命令
1
2
3
4
5
| 清除已经持久化文件的缓存
git rm --cached filename
检测文件忽略规则
git check-ignore -v filename
|
我的 git 常用指令辞典
;
: 使用分号分隔多个指令可在同一行顺序执行多个 git 指令&&
: 使用 && 分隔多个指令可在同一行顺序执行多个 git 指令,但是如果前一个指令执行失败,后面的指令不会执行- git log –oneline –graph –decorate –all 查看所有分支的提交记录
初始化仓库相关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # 添加远程仓库
git remote add origin xxxx
# 查看本地配置文件
git config --global -l
#修改信息
git config --global user.name "xxx"
git config --global user.email "xxx"
# 提交
git push origin +分支名称
# 设置代理,端口自定义,我用的 clash 默认端口 7890
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy https://127.0.0.1:7890
# 取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy
|
修改历史版本
1
2
3
4
5
| # 修改最近一次 commit信息
commit --amend
# rebase 修改最近三个 commit
git rebase -i HEAD~3
|
分支相关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # 查看当前分支
git branch
# -a 查看所有分支
git branch -a
# 删除分支,注意需要切换到其他分支再删除
git branch -d 分支名
# 切换分支
git checkout 分支名
# 从当前所在分支下创建并进入 dev 分支
git checkout -b dev
# push 该分支
git push origin dev
# 更新远端分支到本地(不合并)
git fetch origin dev
# 合并远端分支
git merge origin/dev
git pull = git fetch + git merge
# 远端新建分支
git push origin nikuliu(本地):nikuliu(远端)
|
删除
1
2
3
4
5
6
7
| # 删除暂存区或分支上的文件, 但本地又需要使用, 只是不希望这个文件被版本控制
git rm --cached file_path
git commit -m 'delete remote somefile'
git push
# 放弃修改复写缓存区
git reset --hard branch
|
回档
HEAD
关键字指的是当前分支最末梢最新的一个提交。也就是版本库中该分支上的最新版本。
submodule 使用
克隆一个带有 submodule 的仓库。
1
2
3
4
5
6
7
8
9
10
| git clone url
cd 进入仓库路径
# --recursive 对嵌套 submodule 也可执行
git submodules update --init --recursive
# 查看当前子模块信息
git submodule status
# 更新 submodule 到最新提交版本
git submodule update --remote
|
--recursive
参数用于下载子模块
创建克隆后,使用默认设置初始化其中的所有子模块。这是 相当于在克隆完成后立即运行 git submodule update –init –recursive 完成的。
更新 submodule 到指定 tag/commit
1
2
3
4
5
6
| cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m "moved submodule to v1.0"
git push
|
Git rebase
可以整合多个提交记录,注意不要合并已经 push 的记录。
merge 操作会生成一个新的节点,之前提交分开显示。 而 rebase 操作不会生成新的节点,是将两个分支融合成一个线性的操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 从当前提交位置开始,合并最近三条 commit 记录
git rebase -i HEAD~3
GNU nano 4.8 /home/niku/code/git-text/test-clone/.git/rebase-merge/git-rebase-todo
pick b21522a rebase2
# 变基 54b6e35..b21522a 到 54b6e35(1 个提交)
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但修改提交说明
# e, edit <提交> = 使用提交,进入 shell 以便进行提交修补
# s, squash <提交> = 使用提交,但融合到前一个提交
# f, fixup <提交> = 类似于 "squash",但丢弃提交说明日志
修改 s 参数表示将下面的 commit 合并到上面的版本
|
Git 使用中常见问题
换行符问题 LF/CRLF
在 windows 下使用 Git 一定会碰到的一个问题,在我们用 git add .
命令时会提示 warning: LF will be replaced by CRLF
。
CR(CarriageReturn),LF(LineFeed),CR/LF
是不同操作系统上使用的换行符,具体如下:
Windows 平台: 使用回车 CR
和换行 LF
两个字符来结束一行,即回车+换行 \r\n
;
Mac 和 Linux平台:只使用换行 LF
一个字符来结束一行,即 \n
;
解决方法:
1
| git config --global core.autocrlf true
|
这样在你提交时自动地把回车 CRLF
转换成换行 LF
,而在检出代码时把换行 LF
转换成回车 CRLF
。
多主机一个分支
多主机公用一个仓库是可能会遇到这个问题,直接按照提示使用如下指令做关联即可。或者每次都这样提交 git push origin branch
。
1
| git push --set-upstream origin main
|
常见问题
- 分离 HEAD 情况下更改代码时,最好预先创建新分支。
git commit message 规范
type
说明 commit 的类型。
- feat:新功能(feature)
- fix:修补 bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改 bug 的代码变动)
- test:增加测试
- chore:构建过程或辅助工具的变动
- build:改变构建流程,新增依赖库、工具、构造工具的或者外部依赖的改动
- perf:提高性能的改动
- ci:自动化流程配置修改、与CI(持续集成服务)有关的改动
- revert:回滚到上一个版本,执行git revert打印的message
subject
commit 内容简要描述,字数不宜过长。第一人称,动词现在时开头。
cherry pick
指从一个分支中选择一个提交,并将其应用到另一个分支。例如 merge 和 rebase 通常会将许多提交应用到另一个分支。