Skip to main content

Git FAQ

git config --global core.ignorecase false

修正用户名

git commit --amend --author="Author Name <email@address.com>"

设置当前用户信息

提交时的 commitor

# 确认当前用户信息
git config --global user.name
git config --global user.email

# 如果不正确可以修改
git config --global user.name "Author Name"
git config --global user.email "email@address.com"
  • --local 针对当前仓库修改

git moved file

  • 追溯移动的文件
# --find-copies-harder
git log --follow notes/os/linux/fs/zfs/README.md
git log --follow --find-copies-harder notes/ops/storage/fs/zfs.md
# -p 历史
git log --follow -p -- notes/ops/storage/fs/zfs.md

.../storage/fs

git+ssh proxy

  • 默认支持 http_proxy 和 https_proxy
  • 使用 git+ssh 代理需要额外配置
$HOME/.ssh/config
Host github.com
ProxyCommand socat - PROXY:your.proxy.ip:%h:%p,proxyport=1080,proxyauth=user:pwd
  • 替换 your.proxy.ip
  • 替换 1080
  • 无认证可删除 proxyauth

获取仓库目录

git rev-parse --show-toplevel

子模块初始化

git pull --recurse-submodules
# 或者递归拉取
git clone --recursive <URL>

统计

# 统计所有分支的用户提交数
git shortlog -s -n --all --no-merges

# 统计 LOC
git ls-files | xargs -n1 git blame --line-porcelain | sed -n 's/^author //p' | sort -f | uniq -ic | sort -nr

移除历史文件和敏感数据

# BFG
# 比 git-filter-branch 快 10 - 720x
brew install bfg

# 在删除前先做一次把文件删除的提交
git rm id_{dsa,rsa}
git commit -m 'remove files'
# 清理历史
bfg --delete-files id_{dsa,rsa} my-repo.git
bfg --strip-blobs-bigger-than 50M my-repo.git
bfg --delete-folders dirname my-repo.git

# 清理
git reflog expire --expire=now --all && git gc --prune=now --aggressive

文件太大

如果服务器限制了文件大小, 则当推送一个较大的仓库时会失败, 如果无法修改服务器参数, 则可以考虑分批推送

# 获取总得有多少个提交
gut log --pretty=format:"%h" | wc -l
# 获取第一千个提交
git log --reverse --pretty=format:"%h" | sed '1000!d'
# 推送前一千个提交
git push origin $(git log --reverse --pretty=format:"%h" | sed '1000!d'):master
# 如果出现该错误
# The destination refspec neither matches an existing ref on the remote nor
# begins with refs/, and we are unable to guess a prefix based on the source ref.
# 则使用
git push origin $(git log --reverse --pretty=format:"%h" | sed '1000!d'):refs/heads/master

自定义配置

Git 不允许直接引用仓库中的文件作为配置, 但可以通过修改本地配置来 include 仓库中的配置文件.

# 创建仓库中共享的配置
touch .gitconfig
# 修改 .git/config 来引入仓库中的 .gitconfig, 这个命令会添加一下内容到配置文件中
# [include]
# path = ../.gitconfig
git config --local include.path ../.gitconfig

将另外一个仓库合并为当前仓库的一个子目录

git subtree add -P <prefix> <repo> <rev>

Undo a commit and redo

git commit ...
git reset --soft 'HEAD^'
edit
git add ....
git commit -c ORIG_HEAD

from here

显示 不同

git diff \
--cached \
--staged \
HEAD # 尚未暂存的,当前正在编辑的

from here

移除 git 历史

# $sha 为需要移除的历史位置
git checkout --orphan temp $sha
git commit -m "Truncated history"
git rebase --onto temp $sha master
git branch -D temp

git push tags

git push --tags
# 只 push 一个
git push origin <tag_name>

git clone tag

git clone --branch <tag_name> <repo_url>

生成 patch

# git diff --cached
git diff > my.patch

检测是否发生变化

认证缓存

# 默认 15m
git config --global credential.helper "cache --timeout=3600"

# macOS 可以使用 keychain
# git config --global credential.helper osxkeychain

# 或者 ~/.netrc
# machine <hostname> login <username> password <password>
chmod 600 ~/.netrc

git credential store

git config --global credential.helper store
git credential fill << CONF | git credential approve
url=https://github.com/
username=$GITHUB_USERNAME
password=$GITHUB_TOKEN
CONF
.git-credentials
https://user:pass@example.com

强制 pull

git fetch --all
git reset --hard origin/master

部分克隆转完整克隆

git fetch --unshallow

signoff

git commit --amend --no-edit --signoff
git push --force-with-lease origin $(git rev-parse --abbrev-ref HEAD)

为什么需要 signoff

signoff 是在 commit 消息最后添加一行表示提交作者。

Signed-off-by: wener <wener@wener.me>

用于跟踪谁的提交,如果 review 时做了修改,也可以继续暴力 signoff 以跟踪原始提交人。

迁移子目录为仓库

# git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME

git subtree split -P <name-of-folder> -b <name-of-new-branch>

迁移分支为仓库

mkdir /path/to/new/repo && cd "$@"
git config receive.denyCurrentBranch warn
cd -
git push /path/to/new/repo:optional-new-branch-name branch-name

from Here

取第一个 commit

git rev-list --max-parents=0 HEAD

搜索文件

git log --all -- '*.wmv'
# 只显示 hash
git rev-list --all -- '*.wmv'

ci env var

envform
CI_COMMIT_BRANCHgit branch --show-current
CI_COMMIT_MESSAGE
CI_COMMIT_DESCRIPTION
CI_COMMIT_AUTHOR
CI_COMMIT_TAG
CI_COMMIT_TAG_MESSAGE
CI_COMMIT_SHA
CI_COMMIT_SHORT_SHA
CI_COMMIT_REF_SLUG
CI_COMMIT_REF_PROTECTED
CI_COMMIT_REF_NAME
CI_DEFAULT_BRANCH
CI_COMMIT_TITLE
CI_COMMIT_TIMESTAMP
# 获取 tag 的方式
git describe --tags
git describe --exact-match --tags $(git log -n1 --pretty='%h')
git tag --points-at HEAD
git describe --tags --abbrev=0

skip ci

  • 一般都支持 message 包含 [ci skip], [skip ci]
  • git 2.10 支持选项 git push -o ci.skip
  • 参考
git push -o ci.skip
git push -o ci.variable="MAX_RETRIES=10" -o ci.variable="MAX_TIME=600"

git ci info

# 最近 tag + rev
# v1.0-COMMIT_COUNT-COMMIT_ID
git describe --tags
# 最近 tag
# v1.0
git describe --tags --abbrev=0
# most recent annotated tag
git describe --abbrev=0
# 当前 tag
git tag --points-at HEAD
# 7 位 commitid
git rev-parse --short HEAD

annotated tag vs. unannotated tag

  • git tag <tagname>
    • 无 -a -s -m
    • 无消息
  • git tag -a <tagname> -m '<message>'
    • 包含 tagger/auther, date 信息
    • 包含 message

svn export

git archive master | tar -x -C /somewhere/else
git archive --format zip --output /full/path/to/zipfile.zip master

svn export https://github.com/username/repo-name/trunk/
svn export https://github.com/username/repo-name/trunk/src/lib/folder

git checkout-index --prefix=git-export-dir/ -af

大型 monorepo

  • FSMonitor
git config core.fsmonitor true
git config core.untrackedcache true

git status # 第一次慢
git status # 第二次命中大量缓存,非常快

master to main

  • rename master branch -> main
git branch -m master main

# owner
git push -u origin main
git push origin --delete master

# user
git fetch origin main
git branch --unset-upstream
git branch -u origin/main

# 或修改 .git/config fetch

Error: src refspec master does not match any

git branch -m master main
git push -u origin main
git push origin --delete master

fetch remote branch

git fetch origin 3.16-stable:3.16-stable
git switch 3.16-stable

user.signingkey

# GPG
gpg --list-secret-keys --keyid-format=long
git config --global user.signingkey KEY
git config --global user.signingkey SUBKEY!

# SSH - Git 2.34+
git config --global gpg.format ssh
git config --global user.signingkey "$(cat ~/.ssh/id_ed25519.pub)"
# ssh-agent 如果没有则会出现 Load key git_signing_key invalid format
# ssh-add ~/.ssh/id_ed25519

# X.509
git config --global gpg.x509.program smimesign
git config --global gpg.format x509
smimesign --list-keys
git config --global user.signingkey KEY

# 提交自动签名
git config --global commit.gpgsign true

git Load key git_signing_key invalid format

先执行 ssh-add

unsupported value for gpg.format: ssh

fatal: detected dubious ownership in repository

目录权限不为当前 user

# 报错
git status

# 1. 修改 owner 可解决
chown -R $(id -u):$(id -g) $PWD
# 2. 允许单个目录
git config --global --add safe.directory $PWD
# 3. 忽略 - 允许所有
git config --global --add safe.directory '*'

如何在仓库里存储明文密钥

  1. 非 git 相关方案
  • sops
    • 字段维度
    • argocd vault plugin 支持
    • 支持很多后端,可以用 age
  • age - 比 gpg 简单,功能单一
    • 文件维度
    • 可以用 ssh key
    • 不支持 ssh agent - 可以为 gitops 的应用创建无密码 key
  • sealed-secrets - 没有解决存储明文的问题,解决了密钥放到 kube 的问题
  1. git 相关方案
  • git-secret
  • git-crypt
  • blackbox
  1. 外部服务方案 - KMS, Vaulr
  • 维护额外进程
  • 不方便自动化 - 涉及人工配置
  • 还是需要解决 SSOT - Single Source Of Truth 问题

merge branch without checkout

不切换分支的前提下,合并分支到其他分支

develop -> main

git push . develop:main

push other branch without checkout

git push -u origin main:main