Resources
Simple Git tutorial for beginners | Nulab
Learn Git Branching
Pro Git - Git
Mergetools: Stop doing three-way merges!
So You Think You Know Git - FOSDEM 2024 - YouTube
Reference
git-config
git-branch
重命名当前分支
git branch -m 新分支名称重命名其他分支
git branch -m 旧分支名称 新分支名称git-checkout
git checkout <branch_name>
git checkout abc123git-add
-p 有这么个东西
可以对每个 hunk 交互式选择是否 stage
git-clone
--depth 1 很好用
也可以写成 --depth=1
限制了深度的 clone 叫做浅克隆
浅克隆 (git clone —depth=N) 得到的历史只到某个深度,这导致本地的 master(或其他分支)缺少 older commits。
Git 允许你在此基础上提交并 push,前提是你只基于已有的浅历史产生新提交且远端 tip 仍是你浅克隆时的父提交;否则 push 会失败并提示你需要更多历史。
一旦远端出现你本地没有的旧提交(例如你想基于一个更早的 commit rebase),你需要先运行 git fetch —unshallow 或 git fetch —depth=M 来补齐缺失历史,再进行操作。
如果仓库开启了“接收浅更新”限制(大多数默认允许),无需额外设置;但若服务器禁用了 receive.shallowUpdate,浅仓库 push 会被拒绝,需要管理员解除限制或改用完整克隆。
git-commit
Angular提交信息规范 - Git Guide
如何规范你的Git commit?
How to Write a Git Commit Message
因为我在搞 majutsu 的 jjdescription 部分,所以我大概需要对 message 信息有深刻的理解
fixup
How to mask meaningless “fix typo” commit messages | by Daniel Megyesi | Infr…
trailer
git-log
—grep
内置的对 message 的搜索功能
—graph
原来 git 也是有图的输出的
git-reflog
和我讲讲 git-reflog,这个命令有什么用?
git reflog 用来查看 *Git 在本地记录的“引用(ref)移动历史”*:比如 =HEAD=、某个分支指针在什么时候从哪个提交移动到了哪个提交。它是你在“误操作后找回提交/分支”的最常用工具之一。
它解决什么问题(有什么用)
-
找回“丢了”的提交
- 例如你
git reset --hard=、=rebase=、=commit --amend后,分支指针被改写,旧提交看似“不见了”。 - reflog 仍能列出之前 =HEAD=/分支指向过的提交哈希,让你回退或恢复。
- 例如你
-
撤销/回滚危险操作
reset --hard回不去?用 reflog 找到 reset 之前的那条记录,再 =git reset —hard <hash>=。rebase搞乱了?用 reflog 找到 rebase 前的提交点,reset 回去或新建分支。
-
定位 HEAD 的历史位置
HEAD@{n}语法可以引用“n 次移动之前的 HEAD”。- 也支持按时间:=HEAD@{yesterday}=、=HEAD@{2.hours.ago}=(具体格式因版本略有差异)。
常用命令
- 看 HEAD 的移动历史(最常用):
git reflog - 看某个分支的 reflog:
git reflog show main - 更清晰一点(带一行提交信息):
git reflog --date=iso
典型救援例子
-
1)reset —hard 之后找回
- 先看 reflog:
你会看到类似:git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~1 9f8e7d6 HEAD@{1}: commit: add feature - 回到 reset 前的位置:
git reset --hard 9f8e7d6
- 先看 reflog:
-
2)把“丢了的提交”捞出来建个新分支
git branch rescue 9f8e7d6
重要特点/限制
- *reflog 是本地的*:默认不会随
push共享到远端。 - *不是永久保存*:旧记录会过期并被 GC 清理(通常提交 reflog 90 天、不可达对象更短;可配置)。
- 它记录的是“引用怎么移动”,不是所有对象的完整历史。
git-grep
在工作树中的已跟踪文件、索引文件中注册的 blobs 或给定树对象中的 blobs 中查找指定的 pattern。
pattern 是由换行符分隔的一个或多个搜索表达式组成的列表。
空字符串作为搜索表达式将匹配所有行。
git-diff
diff --git 头里的 index <old>..<new> <mode> 表示该文件在“变更前/变更后”的 blob 对象 ID(缩写),以及文件模式(权限)。
- =<old>=:变更前文件内容对应的 blob 哈希(不是 commit)
- =<new>=:变更后文件内容对应的 blob 哈希
<mode>=:文件类型/权限,例如 =100644普通文件,=100755= 可执行文件,=120000= 符号链接,=160000= 子模块(gitlink)
新建/删除文件时常见:=index 0000000..<new> 100644= 或 =index <old>..0000000 100644=。
git diff默认等价 =-p/—patch=,输出的是“git diff”格式(unified diff + Git 额外头部)。- 结构大致是:
diff --git a/file b/file+
扩展头(=old/new mode=、=rename from/to=、=copy from/to=、=index <hash>..<hash> <mode>=)+
---/++++
@@hunk。 - unified diff 用
--- /dev/null或+++ /dev/null表示新增/删除,但diff --git头仍使用a/和b/前缀路径。
combined diff
-
standard combined diff
combined diff 只显示“被所有父提交都改过的文件”(man 里写的是 modified from all parents)。
- 用
-c或--cc生成(=git show= 显示 merge 时默认就是 combined diff)。 - 头部会是
diff --combined <path>或 =diff —cc <path>=。 - 扩展头示例:=index <p1>,<p2>..<dst>=、=mode <m1>,<m2>..<dst>=;创建/删除用
new file mode/ =deleted file mode=。 - from/to 头通常是两行:=--- a/file= 与 =+++ b/file=;若加 =—combined-all-paths=,会变成 N+1 行(N 为父提交数)。
- hunk 头使用 =@@@=,数量为 =父提交数 + 1=,用于提示这是 combined diff,不建议直接喂给 =patch -p1=。
- 行前会有“每个父提交一列”的标记:列中
-表示该父提交有此行但结果没有,=+= 表示结果有此行但该父提交没有。 - 只有 merge commit 才有 combined diff;fast-forward merge 不会产生 merge commit,因此看不到 combined diff。
--cc是“压缩版”,只保留“所有父提交都改到的行”,干净合并时常见空输出;想看完整 combined diff 用-c或 =—diff-merges=combined=。- 若两个分支改动互不重叠(文件层面),combined diff 可能为空,但
git show -m <merge>仍能分别看到相对每个父提交的 diff。
- 用
-
dense combined diff
--cc等价于--diff-merges=dense-combined -p=(见 =git show手册)。- 基于 combined diff:仍然只列出“所有父提交都改过的文件”,并用
diff --cc/@@@/ 多列前缀的格式呈现。 - 进一步“压缩”输出:会省略那些“父提交只有两种版本,而合并结果不做修改只是选了其中一个版本”的 hunk。
- 直观理解:如果合并结果只是“挑一边”,就被认为不够“有信息量”,会被折叠掉。
- 因此干净合并时常见空输出;真正发生冲突并手动解决时更容易看到内容。
- 直观理解:如果合并结果只是“挑一边”,就被认为不够“有信息量”,会被折叠掉。
- 想看完整细节改用 =-c=;想看每个父提交各自的 diff 用 =-m=。
-
直观理解
#!/usr/bin/env bash set -euo pipefail workdir="$(mktemp -d)" git -C "$workdir" init -q git -C "$workdir" config user.name "demo" git -C "$workdir" config user.email "demo@example.com" cat > "$workdir/f.txt" <<'EOF' L01 L02 L03 L04 L05 L06 L07 L08 L09 L10 L11 L12 L13 L14 L15 L16 L17 L18 L19 L20 EOF git -C "$workdir" add f.txt git -C "$workdir" commit -q -m "base" git -C "$workdir" checkout -q -b left sed -i 's/^L03$/L03-left/' "$workdir/f.txt" sed -i 's/^L15$/L15-left/' "$workdir/f.txt" git -C "$workdir" commit -q -am "left" git -C "$workdir" checkout -q -b right master sed -i 's/^L03$/L03-right/' "$workdir/f.txt" sed -i 's/^L15$/L15-right/' "$workdir/f.txt" git -C "$workdir" commit -q -am "right" git -C "$workdir" checkout -q left git -C "$workdir" merge -q right || true # 冲突解决:一处“选左边”,另一处写新内容 cat > "$workdir/f.txt" <<'EOF' L01 L02 L03-left L04 L05 L06 L07 L08 L09 L10 L11 L12 L13 L14 L15-merged L16 L17 L18 L19 L20 EOF git -C "$workdir" add f.txt git -C "$workdir" commit -q -m "merge resolve" echo "=== -c (combined) ===" git -C "$workdir" show -c HEAD echo echo "=== --cc (dense combined) ===" git -C "$workdir" show --cc HEADAuto-merging f.txt CONFLICT (content): Merge conflict in f.txt Automatic merge failed; fix conflicts and then commit the result. === -c (combined) === commit c632e6e359ca7d9f65ba44791ad484bca762bdc4 Merge: 9892f30 6ba6d59 Author: demo <demo@example.com> Date: Sun Dec 21 19:16:36 2025 +0800 merge resolve diff --combined f.txt index 4b70f4c,5db724c..4c4eb9f --- a/f.txt +++ b/f.txt @@@ -1,6 -1,6 +1,6 @@@ L01 L02 -L03-right +L03-left L04 L05 L06 @@@ -12,7 -12,7 +12,7 @@@ L1 L12 L13 L14 - L15-left -L15-right ++L15-merged L16 L17 L18 === --cc (dense combined) === commit c632e6e359ca7d9f65ba44791ad484bca762bdc4 Merge: 9892f30 6ba6d59 Author: demo <demo@example.com> Date: Sun Dec 21 19:16:36 2025 +0800 merge resolve diff --cc f.txt index 4b70f4c,5db724c..4c4eb9f --- a/f.txt +++ b/f.txt @@@ -12,7 -12,7 +12,7 @@@ L1 L12 L13 L14 - L15-left -L15-right ++L15-merged L16 L17 L18
git-reset
git reset --hard HEAD~1 用来回退一个 commit
git-notes
Git Notes: git’s coolest, most unloved feature - Tyler Cipriani
magit 支持这个功能 Notes (Magit User Manual)
git-remote
添加远程仓库
使用 git remote add 命令:
# 基本语法
git remote add <远程名称> <仓库URL>
# 示例
git remote add origin https://github.com/user/repo.git
git remote add origin git@github.com:user/repo.git或者编辑 .git/config 文件:
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*常用命令
# 查看现有远程仓库
git remote -v
# 修改远程仓库 URL
git remote set-url origin new-url
# 删除远程仓库
git remote remove origin
# 重命名远程仓库
git remote rename old-name new-name注意事项:
- 确保 URL 格式正确
- 检查访问权限
- HTTPS 或 SSH 协议选择
- 远程名称通常用 “origin”
验证配置
# 测试连接
git fetch origin
# 查看远程分支
git branch -r
# 查看详细配置
git remote show origingit-fetch
遇到了 jujutsu 在 git fetch 的时候给我的警告:
Warning: Ignored refspec `+refs/pull/*/head:refs/pullreqs/*` from `origin`: only refs/heads/ is supported for refspec sources
在这里解释一下
这条 +refs/pull/*/head:refs/pullreqs/* 针对的是 GitHub 暴露的 PR 头引用 refs/pull/<ID>/head,属于 GitHub 的命名约定,并非通用 Git 特性。
(docs.github.com (https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally))
Git 本身不定义 PR 引用,各托管商各用一套:如果远端不是 GitHub,这条 refspec 会抓不到任何东西。
GitLab 的等价路径是 refs/merge-requests/*/head,常见写法:fetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*。
https://docs.gitlab.com/user/project/merge_requests/merge_request_troubleshooting/
Azure DevOps(Azure Repos)通常提供 refs/pull/*/merge(试合并结果)甚至 refs/pull/*/head,对应 refspec 可写成 +refs/pull/*/merge:refs/remotes/origin/pull/*。
https://stackoverflow.com/questions/67715404/git-fetch-pull-requests-from-azure-devops
- 在 refspec 里写成 +<src>:<dst>,前面的 + 表示“允许强制更新”这条映射,不做快进检查。
- 没有 + 时,如果 <dst> 需要回退(非 fast-forward),git fetch 会拒绝更新该引用并报错。
- 默认的远端抓取 refspec 就带 +,这样远端即便被强推重写历史,本地的远端追踪分支也会被同步回退。
- CLI 等价:git fetch origin —force 会对所有 refspec 生效;而前缀 + 是按条目单独生效。
- 推送时的 refspec 也用同样语法:+master:master 表示只对这一条执行 force-push。
- 没有 + 时,如果 <dst> 需要回退(非 fast-forward),git fetch 会拒绝更新该引用并报错。
git-push
#更新远程分支:
git push origin 新分支名称
# 删除远程的旧分支:
git push origin --delete 旧分支名称
#设置上游分支:
git push --set-upstream origin 新分支名称git-pull
git-cherry-pick
git-diff
Git has four main diff algorithms: Myers, Minimal, Patience, and Histogram, with Myers being the default.
Patch
补丁就是用 diff 生成出来的
所以 jj-diff 无法提供这样可以 apply 的 patch 吗?
Algorithms
-
Myers
默认算法,它找出最长公共子序列,并将更改分类为添加或删除。
The Myers diff algorithm: part 1 – The If Works
-
Minimal
Myers 算法的改进版本,它尝试生成尽可能小的 diff,即使计算时间更长。
-
Patience
一种更高级的算法,可以产生比 Myers 更易读的差异比较结果。
-
Histogram
耐心算法的扩展,尝试识别频繁出现的常见行并降低其优先级,对于某些类型的更改,可以产生更易读的差异比较结果。
组成
-
about
这个东西之前没注意到过,然后 jj 的 diff —git 输出没有这个功能
about 不是 Magit 新加的东西,它对应的是 unified diff 里 hunk 头这行的可选尾巴:
@@ -k,l +n,m @@ TEXT
这里的 TEXT 在 git help diff 里就写明了:hunk header 会“提到该 hunk 所在的函数名”。
它的来源更早,跟 GNU diff -p/“show C function” 那套传统一致(所以不是近期 Git 才加入的)。你对它没印象通常是因为它不一定会出现:只有当 Git 能用 diff.<driver>.xfuncname(见 git help gitattributes “Defining a custom hunk-header”)
从改动位置向上找到匹配的“函数/章节行”时才会填;匹配不到就空着,所以 about 常常是 nil。
git-apply
Apply a patch to files and/or to the index
可以用 --check 来查看一个 patch 的合法性
可以用 --stat 来从一个 patch 中提取 stat
git-rebase
git-revert
git-fsck
git-format-patch
要是哪天我能用上这东西我也就出息了
git-hook
Git hooks 是在特定 Git 事件发生时自动执行的脚本,位于 .git/hooks/ 目录。
客户端 hooks
-
pre-commit
提交前执行,用于代码检查、格式化等。退出非零值会中止提交。
-
prepare-commit-msg
在编辑器打开前修改提交信息模板。
-
commit-msg
检查提交信息格式,常用于强制提交信息规范。
-
post-commit
提交完成后执行,用于通知或日志记录。
-
pre-push
推送前执行,可用于运行测试或检查。
服务端 hooks
-
pre-receive
接收推送前执行,可拒绝整个推送。
-
update
对每个分支执行,可单独拒绝某个分支的更新。
-
post-receive
推送完成后执行,常用于部署、通知等。
使用方法
# hooks 位于
.git/hooks/
# 创建 hook(去掉 .sample 后缀)
mv .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit注意事项
- hooks 不会被 git 跟踪,需要手动分发或使用工具管理
- 可以用 core.hooksPath 配置自定义 hooks 目录
- 常用工具:husky、pre-commit 等
git-submodule
Resources
Working with submodules - The GitHub Blog
Git - Submodules
gitattributes
Defining attributes per path
git extensions
git-annex
git-annex: 使用 git 管理文件,而无需将其内容提交到 git 中。
Git Annex Is The Coolest Program You’ve Never Heard Of - YouTube
git-lfs
Git Large File Storage | Git Large File Storage (LFS) replaces large files su…
Learning About Git Large File System (LFS) | by Amy Li | The Startup | Medium
Managing large files - GitHub Docs
lfs-s3
把大文件托管到代码托管平台貌似不是一件便利的事情
但是我们可以用自己配置的存储后端!
这个项目是最新的项目,而且实现方式貌似更加简单
Introduction chat
下面是关于 Git LFS(Git Large File Storage)的详尽介绍,帮助你了解它的原理、使用场景、安装配置及常用命令等。
-
什么是 Git LFS
- Git LFS 是 Git 的一个扩展,用于在版本库中管理大体积文件(如音视频、二进制资源、数据集等)。
- 它将大文件本身存储在专门的存储服务器(LFS Server)上,而在 Git 仓库里只保留一个指向这些大文件的“指针”文件,从而避免 Git 仓库体积暴涨。
- Git LFS 是 Git 的一个扩展,用于在版本库中管理大体积文件(如音视频、二进制资源、数据集等)。
-
为什么需要 Git LFS
- 传统 Git 会把所有文件都完整存入 .git 目录,导致仓库体积随大文件增加而迅速膨胀。
- 克隆、拉取、推送都会变得很慢,也不容易回滚到历史版本。
- Git LFS 可显著减小仓库大小,加快 Git 操作。
- 传统 Git 会把所有文件都完整存入 .git 目录,导致仓库体积随大文件增加而迅速膨胀。
-
Git LFS 的工作原理
1)在本地提交大文件时:- Git LFS 钩子(pre-push 或 clean filter)会拦截符合规则的大文件,替换为一个小型的指针文件(包含文件 ID、大小、下载地址等元信息)。
2)推送到远程时:
- 指针文件随 Git 普通流程推到 Git 服务器。
- 大文件内容则通过 LFS 协议上传到 LFS 服务器(可以是 GitHub/GitLab 内置的 LFS 服务,也可以自行部署)。
3)克隆或检出时:
- Git 拉取指针文件后,Git LFS 会自动(或按需)下载实际的文件内容到工作区。
- Git LFS 钩子(pre-push 或 clean filter)会拦截符合规则的大文件,替换为一个小型的指针文件(包含文件 ID、大小、下载地址等元信息)。
-
安装与初始化
1)安装- macOS(Homebrew):
brew install git-lfs - Windows(Chocolatey 或官方安装包):
choco install git-lfs - Linux(包管理器或二进制包):
apt-get install git-lfs
或下载并解压官方 Release。
2)配置全局钩子
执行一次:
git lfs install
该命令会在 ~/.gitconfig 或仓库 .git/config 中添加相应的 filter 钩子。 - macOS(Homebrew):
-
常用命令
1)指定跟踪某类文件
git lfs track “.psd”
git lfs track “data/.zip”
该命令会在仓库根目录生成或更新 .gitattributes 文件。
2)查看跟踪规则
git lfs track
3)移除跟踪
git lfs untrack “*.psd”
4)添加、提交、推送同普通 Git
git add .gitattributes
git add path/to/large.file
git commit -m “add large file”
git push
5)查看 LFS 状态
git lfs status
6)列出已上传的大文件引用
git lfs ls-files
7)下载/拉取 LFS 文件- 在 clone 后自动下载(默认行为)。
- 或者手动执行:
git lfs pull
git lfs fetch —all
- 在 clone 后自动下载(默认行为)。
-
与远程仓库的配合
- GitHub、GitLab、Bitbucket 等平台都对 Git LFS 提供官方支持,你只需在仓库中开启 LFS 功能即可。
- 免费额度和计费:
这些平台通常对 LFS 存储和带宽有免费额度,超过后可能需要付费。 - 私有部署:
可结合开源项目(如 Git LFS Server、Minio 等)搭建自建 LFS 服务。
- GitHub、GitLab、Bitbucket 等平台都对 Git LFS 提供官方支持,你只需在仓库中开启 LFS 功能即可。
-
注意事项
- .gitattributes 文件要纳入版本控制,确保团队成员都使用相同的跟踪规则。
- LFS 对历史大文件的清理需要配合工具(如 git lfs prune、git gc 或 BFG Repo-Cleaner 等)。
- 在切换分支、执行 git checkout 时,LFS 会自动下载对应版本的大文件,可能造成网络请求或延迟。
- 避免频繁修改同一个大文件,否则 LFS 存储也会不断累积多份历史版本。
- .gitattributes 文件要纳入版本控制,确保团队成员都使用相同的跟踪规则。
-
常见问题
- “LFS objects missing from server”
请检查远程 LFS 服务是否启用、访问凭据是否正确。 - 带宽/存储超限
评估团队使用场景,必要时升级配额或自建存储。 - 大文件未被正确追踪
确认 .gitattributes 规则匹配到文件,并重新 git add 后提交。
- “LFS objects missing from server”
-
参考资源
通过以上介绍,你应该对 Git LFS 的动机、原理、使用方法与注意事项有了全面的了解。开始使用时,可先在测试仓库里试验一遍,熟悉工作流后再在正式项目中推广。
git-flow
Using git-flow to automate your git branching workflow
gitflow-avh
GitHub - nvie/gitflow: Git extensions to provide high-level repository operat…
gitflow-cjs
AUR (en) - gitflow-cjs
GitHub - CJ-Systems/gitflow-cjs: CJS Edition of the git extensions to provide…
avh 版本已经不再维护了,现在这个是主流版本
为什么 GitFlow”失宠”了?
CI/CD 兴起:持续部署让复杂分支变得不必要
敏捷开发:快速迭代不需要复杂流程
小团队增多:独立开发者和小团队更喜欢简单方案
云服务普及:部署成本降低,可以更频繁发布