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
和 jj 不一样,git使用的是 ini 风格的配置
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-blame
--reverse <start>..<end>
Walk history forward instead of backward. Instead of showing the
revision in which a line appeared, this shows the last revision in
which a line has existed. This requires a range of revision like
<start>..<end> where the path to blame exists in <start>. git blame
—reverse <start> is taken as git blame —reverse <start>..HEAD for
convenience.
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
因为 jj 没有区分 pull 和 fetch,所以我其实不太了解这个东西
Integrate changes from a remote repository into the current branch.
First, git pull runs git fetch with the same arguments (excluding merge
options) to fetch remote branch(es). Then it decides which remote branch
to integrate: if you run git pull with no arguments this defaults to the
upstream for the current branch. Then it integrates that branch into the
current branch.
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-bundle
概述
git bundle 用于把提交历史打包成单个 .bundle 文件,适合离线传输和网络受限场景。
常用命令
-
创建 bundle
# 打包整个分支 git bundle create repo.bundle main # 打包增量(接收方已包含 old-tip) git bundle create update.bundle old-tip..main
-
导入 bundle
# 直接克隆 git clone /path/to/repo.bundle repo-b # 或在已有仓库拉取 git fetch /path/to/repo.bundle main:bundle-main git checkout bundle-main
-
检查 bundle
git bundle list-heads /path/to/repo.bundle git bundle verify /path/to/repo.bundle
注意事项
- *增量包有前置依赖*:
A..B形式要求接收方已具备A。 - *bundle 可作为只读 remote*:可
fetch/pull,不可push。 - *建议单独 remote 名称*:例如
git remote add bundle /path/to/repo.bundle,避免覆盖常规origin。 - *bundle 不是动态远端*:有新提交时需要重新生成并传输新的 bundle 文件。
Git Internals
Packfiles
https://git-scm.com/book/en/v2/Git-Internals-Packfiles
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 兴起:持续部署让复杂分支变得不必要
敏捷开发:快速迭代不需要复杂流程
小团队增多:独立开发者和小团队更喜欢简单方案
云服务普及:部署成本降低,可以更频繁发布
git-filter-repo
Contribute to Git
https://git-scm.com/docs/SubmittingPatches
或者有这个变通办法?
gitgitgadget
Platforms providing Git hosting
github
sourcehut
gitea
gitlab
Codeberg
Gitee
sourceware
Forks and reimplementations
Events
Git Merge
有很多视频可以在 gitbutler 的频道看到