Git Tools¶
This part is mainly from ProGit Ch7
Tip
这部分随缘更新,用到的时候再学,学完顺便总结一下
最近在 skypilot
进行高强度开发,因此遇见很多git中曾经不重视的问题,借此机会学习并进行汇总
事实上,开发的过程中各种查缺补漏,到最后都是搞懂这张git指令集
只是,单纯花时间系统学没太大必要,用到再查,查完学会即可。
Stash and Clean¶
Examples¶
基础环境
Bash | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
使用场景
现在我在more
分支下新建两个文件:bxhu_add_not_commit.py
和 bxhu_not_add.py
.
Bash | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
为了更好的说明stash
的特性,我特地设置了两个文件,一个被追踪但未被提交,一个压根没被追踪。
现在的问题是,我突然要回到main
分支,新建一个6.py
;但是,上述两个文件都是我瞎写的,我甚至不想给它们commit,目前没法切换分支 ToT
Legal but not good
其实“目前没法切换分支”不完全准确,事实上也可以切换,但是这样很不安全,不符合规范
如果在 more
分支中添加了新文件而没有提交,当您切换回 main
分支时,这些文件会暂时出现在 main
分支的工作区。这种情况发生的原因是:
- 暂存文件
bxhu_add_not_commit.py
:- 该文件被
git add
暂存了但未提交。Git 会把暂存的更改带到切换后的分支上,因为暂存区的内容在 Git 看来是待定的,属于未完成的变更。
- 该文件被
- 未跟踪文件
bxhu_not_add.py
:- 未跟踪文件会一直停留在工作区中,跨分支切换时也会存在。Git 默认不会自动移除未跟踪的文件,除非通过
git clean
清理。
- 未跟踪文件会一直停留在工作区中,跨分支切换时也会存在。Git 默认不会自动移除未跟踪的文件,除非通过
因此我在这里,最规范的做法是:git stash
,暂存目前的变更进入缓冲区
From ProGit
Now you want to switch branches, but you don’t want to commit what you’ve been working on yet, so you’ll stash the changes. To push a new stash onto your stack, run git stash
or git stash push
:
现在我们进行stash操作:
stash状态1:
Bash | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
stash状态2:
Bash | |
---|---|
1 2 3 4 5 6 7 8 |
|
检查当前stash list:
Bash | |
---|---|
1 2 3 |
|
注意这里,stash@{0}
是最新的stash,stash@{1}
是之前的stash,以此类推...
现在假设我们==回到了 main
分支进行一系列操作,操作完,又回到 more
分支==,我们想要恢复之前的stash,那么我们可以使用git stash apply
命令:
先回到比较老的状态(stash@{1}
)试试:
Bash | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
再到最新的状态(stash@{0}
):
Bash | |
---|---|
1 2 3 4 5 6 |
|
注意这里,stash apply
之后,它会自动显示当前的status
,不需要再单独操作一次了
Summary¶
个人来看,stash
是一个“时间机器” for specific branch,它可以让我们在当前分支上,暂存当前的工作区,然后切换到其他分支,完成其他分支的工作,然后再切换回来,恢复之前的工作区。
这里总结一下常见的 stash
指令:
1) 保存修改
Bash | |
---|---|
1 2 |
|
Unstaged and Staged
- 默认情况下,
git stash
只会保存已跟踪的文件修改 (staged
) - 使用
git stash -u
可以同时保存未跟踪的文件 (staged + unstaged
)
2) 恢复修改
Bash | |
---|---|
1 2 |
|
3) 查看stash list
Bash | |
---|---|
1 |
|
4) 删除list条目
Bash | |
---|---|
1 2 |
|
My Choice:
git stash save "msg"
git stash list
git stash apply stash@{n}
git stash drop stash@{n}
Rebase and Merge¶
事实上这是一个历史遗留的疑惑点,正好今天有时间系统整理一下:
Bash | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
现在 commit timeline
长这样:
Difference¶
1)如果采取merge方案:
Bash | |
---|---|
1 2 |
|
2)如果采取rebase方案:
Bash | |
---|---|
1 2 3 4 |
|
中间态,在git rebase main
之后
最终态:after git merge feature
Exception Process¶
在feature分支merge到main分支的过程中,会出现一些常见的conflict错误,这里我们分类来说明解决方法:
Merge Conflict
首先确保你已经修改了有冲突的文件(file.txt),删除了冲突标记(<<<<<<
, =======
, >>>>>>>
)并保存了最终想要的内容。
这一步:
- 如果在vim中,就是逐个对比
<<<<<<
,=======
,>>>>>>>
区间内的异同,手动修改; - 如果在vscode中,可以利用“冲突合并编辑器”,直接点击“接受当前更改”或“接受传入更改”来解决冲突,非常方便 :)
1) 将修改后的文件添加到暂存区:
Bash | |
---|---|
1 |
|
2) 提交合并结果:
Bash | |
---|---|
1 |
|
PS, 如果在解决冲突过程中改变主意了,想要取消这次合并,可以使用:
Bash | |
---|---|
1 |
|
3) 如果之后需要推送到 remote repo,直接推送即可:
Bash | |
---|---|
1 |
|
检查状态的cmd
git status
, 查看当前冲突状态git diff
, 查看具体的冲突内容
Rebase Conflict
首先确保你已经修改了有冲突的文件(file.txt),删除了冲突标记(<<<<<<
, =======
, >>>>>>>
)并保存了最终想要的内容。
这里跟上面merge一模一样。
1) 将修改后的文件添加到暂存区:
Bash | |
---|---|
1 |
|
2) 继续 rebase 过程:
Bash | |
---|---|
1 |
|
abort in rebase
在 rebase 过程中可能需要多次解决冲突
放弃这次 rebase,回到之前的状态 (最初始状态):
Bash | |
---|---|
1 |
|
小心:这是回到最初始,而不是上一步状态!
3) rebase
完成后,如需推送到远程,要使用强制推送:
Bash | |
---|---|
1 |
|
Git-LFS¶
Git Large File Storage (LFS) 是一个 Git 的扩展,用于管理大型文件,如视频、音频、图像等。
它通过将大型文件存储在 Git 仓库之外的服务器上,并在 Git 仓库中存储指向这些文件的指针,从而解决了 Git 对大型文件的处理效率低下的问题。
Why Git-LFS¶
这张图实际上说清楚了git-lfs的设计理念,便是:
对于超级大的文件,我们实际上将其存储在git-lfs的远程库(第三方托管,有使用额度,超过则需要缴费),而在本地库中,我们只存储一个指向这个文件的指针,这样就可以大大减少本地库的大小,提高git的处理效率。
这是git-lfs官网的开篇图片,说的更加简明:
对于一般开发的code,常规来说文件肯定不会太大,我们使用git就足够了。
但是对于一些大型文件file.psd
,比如.pdf
/ .mp4
等媒体文件,我们就需要使用git-lfs来管理了。
How it works¶
Git LFS 通过将大文件替换为指针文件的方式来管理大文件:
- 在本地仓库中只保存指针文件,这些指针文件极小(通常小于 1KB)
- 实际的大文件内容存储在 Git LFS 服务器上
- 指针文件包含三个关键信息
Text Only 1 2 3
version https://git-lfs.github.com/spec/v1 oid sha256:[文件的唯一hash值] size [文件大小]
Process¶
-
添加文件时:
- 当执行
git add
命令时,Git LFS 会创建指针文件替换原文件内容 - 实际文件内容被存储在本地 Git LFS 缓存中(
.git/lfs/objects
目录)
- 当执行
-
推送到远程时:
- 当执行
git push
时,Git LFS 的 pre-push 钩子会被触发 - 大文件内容会从本地 LFS 缓存直接传输到远程 Git LFS 存储服务器
- 而==指针文件会被推送到常规的 Git 仓库中==
- 当执行
-
克隆和检出时:
- 克隆仓库时只会下载指针文件,不会下载大文件内容
- 只有在
checkout
到具体文件时,才会从 LFS 服务器下载对应版本的实际文件内容
Storage¶
-
本地仓库:
- 只存储指针文件(约 132 字节)
- 实际文件存在
.git/lfs/objects
目录下 - 只保存当前工作所需的文件版本
-
远程仓库:
- Git 仓库中存储指针文件
- LFS 服务器存储实际的大文件内容
- 保存文件的所有历史版本
这种设计的优点是:
- 显著减少 Git 仓库的体积
- 加快克隆和拉取操作
- 只下载实际需要的文件版本
缺点是:
- 需要依赖网络访问 LFS 服务器
- 本地仓库不再是完整的仓库副本,需要额外的 LFS 服务器支持
How to Use¶
在mac/linux上安装git-lfs非常简单,只需要在终端中输入:
Bash | |
---|---|
1 |
|
此时,你会发现仓库里多了一个.gitattributes
文件,这个文件用于配置哪些文件需要使用git-lfs来管理
Bash | |
---|---|
1 2 |
|
追踪需要的大文件,这里的文件可以是后缀名(群体),也可以是文件名(个体):
Bash | |
---|---|
1 2 3 4 5 |
|
此时检验一下.gitattributes
文件,会发现多了一些内容:
Text Only | |
---|---|
1 2 3 4 5 |
|
将.gitatributes
文件自身也追踪一下:
Bash | |
---|---|
1 |
|
No Forward Compatibility!
Note that defining the file types Git LFS should track will not, by itself, convert any pre-existing files to Git LFS, such as files on other branches or in your prior commit history. To do that, use the git lfs migrate
command, which has a range of options designed to suit various potential use cases.
如果我曾经有一些大文件了,但是那时候还没有启用git-lfs,则此时添加git-lfs后,不会自动前向追踪
我需要再额外使用 git lfs migrate
命令来将这些大文件转换为git-lfs的格式。
更加推荐的做法请参考:here
此时,就可以像往常一样,a-c-p了:
Bash | |
---|---|
1 2 3 |
|
TL;DR¶
Bash | |
---|---|
1 2 3 4 5 |
|