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 abc123

git-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=、某个分支指针在什么时候从哪个提交移动到了哪个提交。它是你在“误操作后找回提交/分支”的最常用工具之一。

它解决什么问题(有什么用)

  1. 找回“丢了”的提交

    • 例如你 git reset --hard=、=rebase=、=commit --amend 后,分支指针被改写,旧提交看似“不见了”。
    • reflog 仍能列出之前 =HEAD=/分支指向过的提交哈希,让你回退或恢复。
  2. 撤销/回滚危险操作

    • reset --hard 回不去?用 reflog 找到 reset 之前的那条记录,再 =git reset —hard <hash>=。
    • rebase 搞乱了?用 reflog 找到 rebase 前的提交点,reset 回去或新建分支。
  3. 定位 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 之后找回

    1. 先看 reflog:
      git reflog
      你会看到类似:
         a1b2c3d HEAD@{0}: reset: moving to HEAD~1
         9f8e7d6 HEAD@{1}: commit: add feature
    2. 回到 reset 前的位置:
      git reset --hard 9f8e7d6
  • 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 HEAD
    Auto-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 origin

git-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。

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

  • 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 | Hacker News

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)的详尽介绍,帮助你了解它的原理、使用场景、安装配置及常用命令等。

  1. 什么是 Git LFS

    • Git LFS 是 Git 的一个扩展,用于在版本库中管理大体积文件(如音视频、二进制资源、数据集等)。
    • 它将大文件本身存储在专门的存储服务器(LFS Server)上,而在 Git 仓库里只保留一个指向这些大文件的“指针”文件,从而避免 Git 仓库体积暴涨。
  2. 为什么需要 Git LFS

    • 传统 Git 会把所有文件都完整存入 .git 目录,导致仓库体积随大文件增加而迅速膨胀。
    • 克隆、拉取、推送都会变得很慢,也不容易回滚到历史版本。
    • Git LFS 可显著减小仓库大小,加快 Git 操作。
  3. Git LFS 的工作原理

    1)在本地提交大文件时:

    • Git LFS 钩子(pre-push 或 clean filter)会拦截符合规则的大文件,替换为一个小型的指针文件(包含文件 ID、大小、下载地址等元信息)。

    2)推送到远程时:

    • 指针文件随 Git 普通流程推到 Git 服务器。
    • 大文件内容则通过 LFS 协议上传到 LFS 服务器(可以是 GitHub/GitLab 内置的 LFS 服务,也可以自行部署)。

    3)克隆或检出时:

    • Git 拉取指针文件后,Git LFS 会自动(或按需)下载实际的文件内容到工作区。
  4. 安装与初始化

    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 钩子。

  5. 常用命令

    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
  6. 与远程仓库的配合

    • GitHub、GitLab、Bitbucket 等平台都对 Git LFS 提供官方支持,你只需在仓库中开启 LFS 功能即可。
    • 免费额度和计费:

      这些平台通常对 LFS 存储和带宽有免费额度,超过后可能需要付费。
    • 私有部署:

      可结合开源项目(如 Git LFS Server、Minio 等)搭建自建 LFS 服务。
  7. 注意事项

    • .gitattributes 文件要纳入版本控制,确保团队成员都使用相同的跟踪规则。
    • LFS 对历史大文件的清理需要配合工具(如 git lfs prune、git gc 或 BFG Repo-Cleaner 等)。
    • 在切换分支、执行 git checkout 时,LFS 会自动下载对应版本的大文件,可能造成网络请求或延迟。
    • 避免频繁修改同一个大文件,否则 LFS 存储也会不断累积多份历史版本。
  8. 常见问题

    • “LFS objects missing from server”

      请检查远程 LFS 服务是否启用、访问凭据是否正确。
    • 带宽/存储超限

      评估团队使用场景,必要时升级配额或自建存储。
    • 大文件未被正确追踪

      确认 .gitattributes 规则匹配到文件,并重新 git add 后提交。
  9. 参考资源

通过以上介绍,你应该对 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

Products providing Git hosting

github

sourcehut

gitea

gitlab

Codeberg

Gitee