跳到主要内容

Git

Local

  • init
    • -b 初始分支名称
    • --bare
    • --shared
  • clean: 高危操作!
    • -d: 包括目录
    • -x: 包括 ignore 的文件!
  • stash
    • list
    • show
    • create
    • store
    • save
    • push
    • pop
    • apply
    • drop
    • clear
    • branch
    • export
    • import
  • add
  • rm
    • --cached
    • -r
  • mv
  • restore: 默认从索引恢复, 若有 --staged 则从 HEAD 恢复; 默认恢复工作区, 若指定 --staged 则只恢复索引, 指定两者则都恢复
    • --staged
    • --worktree (默认)
  • reset 改变历史
    • --soft: 只重置 HEAD (撤销 commit). 可用于重置历史(简易版 rebase)
    • --mixed (默认): 重置 HEAD 和索引 (撤销 commit 和 stage)
    • --hard: 重置 HEAD、索引和工作区
    • --merge
    • --keep: 重置 HEAD 但保存本地变更
  • commit
    • --amend
  • revert
  • status
  • diff
  • blame
  • show
  • log
    • -p --follow
    • --stat
    • --graph
  • reflog

Branch

  • branch
    • -a
    • -d
    • -m
    • -c
    • -l
    • -f
    • -u
    • -v
  • switch
  • checkout
  • merge
    • -X
      • ort(默认)
      • ours
      • theirs
    • --stat
    • --squash
    • --commit(默认)
    • --ff(默认)
    • --no-ff
    • --ff-only
    • -s
    • --autostash: 这种方式之后原分支就不可用了, 需要 reset --hard
    • --allow-unrelated-histories
  • rebase
    • --continue
    • --skip
    • --abort
    • --quit
    • --edit-todo
    • --show-current-patch
    • --onto
    • --keep-base
    • --apply
    • --empty=
    • --merge(默认)
    • --strategy
    • -i <after-this-commit> %% 也可以直接填分支, 作用和非交互式相同. rebase 之前需先 pull 相应分支, 之后通常需要强制推送 %%
      自上而下列出 (start, end] 之间的 commit (即最早的在最上面, 最新的在最下面)
      如果要合并提交将第二个及后续提交的 "pick" 命令替换为 "squash" 或 "fixup" (:2,$s/pick/squash/)
      之后会再打开一个编辑界面用于编辑新的 commit 信息.
      • pick
      • reword
      • edit
      • squash
      • fixup
      • exec
      • break
      • drop
      • label
      • reset
      • merge
      • update-ref
    • --rebase-merges
    • --[no-]fork-point
    • --root
    • --autosquash: 要求历史 commit 符合特定格式
  • cherry-pick
  • tag
    • -l
    • -d
    • -a -m
  • worktree
  • submodule

Remote

  • remote
    • add
    • rename
    • remove
    • set-head
    • show
    • prune
    • update
    • set-branches
    • get-url
    • set-url
  • clone
  • fetch
  • pull
  • push 远程仓库名 本地分支名:远程分支名
    • --all
    • --tags
    • -d
    • --[no-]force-with-lease
    • --prune
    • -u

Config

https://git-scm.com/docs/git-config

[user]
name =
email =

[core]
eol = lf
autocrlf = input
# symlinks = false
# filemode = false
# ignorecase = true
quotepath = false

[init]
defaultBranch = main

[gui]
encoding = utf-8

[i18n]
commitencoding = utf-8

[pull]
# rebase =

[push]
autoSetRemote = true

[http]
proxy =
[https]
proxy =

Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:

  • /etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果在执行 git config 时带上 --system 选项,那么它就会读写该文件中的配置变量。 (由于它是系统配置文件,因此你需要管理员或超级用户权限来修改它。)
  • ~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。 你可以传递 --global 选项让 Git 读写此文件,这会对你系统上 所有 的仓库生效。
  • 当前使用仓库的 Git 目录中的 config 文件(即 .git/config):针对该仓库。 你可以传递 --local 选项让 Git 强制读写此文件,虽然默认情况下用的就是它。 (当然,你需要进入某个 Git 仓库中才能让该选项生效。)

每一个级别会覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。

查看配置及来源: git config -l --show-origin

alias

https://github.com/GitAlias/gitalias

.gitignore

Which file to place a pattern in depends on how the pattern is meant to be used.

  • Patterns which should be version-controlled and distributed to other repositories via clone (i.e., files that all developers will want to ignore) should go into a .gitignore file.
  • Patterns which are specific to a particular repository but which do not need to be shared with other related repositories (e.g., auxiliary files that live inside the repository but are specific to one user’s workflow) should go into the $GIT_DIR/info/exclude file.
  • Patterns which a user wants Git to ignore in all situations (e.g., backup or temporary files generated by the user’s editor of choice) generally go into a file specified by core.excludesFile in the user’s ~/.gitconfig. Its default value is XDGCONFIGHOME/git/ignore.IfXDG_CONFIG_HOME/git/ignore. If XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore is used instead.
# 注释
! 否定
/ 目录分隔符
**
* 匹配除斜杠之外的任何内容
? 匹配除'/'之外的任何单个字符
[范围]

If there is a separator at the beginning or middle (or both) of the pattern, then the pattern is relative to the directory level of the particular .gitignore file itself. Otherwise the pattern may also match at any level below the .gitignore level.

测试: git-check-ignore -v $filepath

查看: git status --ignoredgit ls-files --cached

移除已追踪文件: find . -name '.DS_Store' -exec git rm -r --cached {} \;

注意: merge 等操作之前必须先处理好.gitignore.

git ls-files --cached | while IFS= read -r file; do
if git check-ignore -v --no-index "$file" >/dev/null 2>&1; then
git rm -r --cached "$file"
fi
done

假设在 main 分支 merge dev, 何种情况下会导致被 ignore 的文件丢失?

  • 该文件不曾在 main 中跟踪 & 不曾在 dev 中跟踪 -> 保持不变(独立于 git)
  • 该文件曾在 main 中跟踪 & 在 dev 中跟踪 -> 正常合并
  • 该文件不曾在 main 中跟踪, 但在 dev 中跟踪了 (从.gitignore 移除了) -> merge 之前会在 main 中消失, merge 后继承 dev 的状态
  • 该文件曾在 main 中跟踪, 但在 dev 中 git rm --cached 了 (添加到了.gitignore 中) -> merge 后被删除(回到 dev 下也没了)

结论:
未跟踪的文件, 若被其他分支跟踪或者在本分支 git rm --cached, 在切换分支再切回将从工作区消失.

解决办法:
在切分支之前执行 git stash --all

.gitattributes

*.docx filter=lfs diff=lfs merge=lfs -text
*.xlsx filter=lfs diff=lfs merge=lfs -text
*.pptx filter=lfs diff=lfs merge=lfs -text

*.doc filter=lfs diff=lfs merge=lfs -text
*.xls filter=lfs diff=lfs merge=lfs -text
*.ppt filter=lfs diff=lfs merge=lfs -text

*.pdf filter=lfs diff=lfs merge=lfs -text
*.wps filter=lfs diff=lfs merge=lfs -text

*.zip filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.7z filter=lfs diff=lfs merge=lfs -text
*.tar.gz filter=lfs diff=lfs merge=lfs -text

*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
*.svg filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text
*.sketch filter=lfs diff=lfs merge=lfs -text
*.fig filter=lfs diff=lfs merge=lfs -text

*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text

Internals

.git 目录:

  • hooks/
  • info/
    • exclude
  • logs/
  • objects/: blob、tree、commit、tag
    • info/
    • pack/
  • refs/
    • heads/
    • tags/
    • remotes/
  • COMMIT_EDITMSG
  • HEAD
  • ORIG_HEAD
  • config
  • description
  • index: 暂存区(下一次提交的状态)

Tips

  • HEAD~n 当前提交路径上回溯 n 步
  • HEAD^n 第 n 个父级提交

origin

恢复工作区

git restore .

重命名仓库

重命名分支

Renaming a branch - GitHub Docs

# 重命名本地分支
git branch -m master main
# 推送新分支
git push -u origin main
# 到 GitHub Settings 中更改主分支, 然后在本地拉取更新
git fetch -p origin
# 设置本地分支跟踪的远程分支
git branch -u origin/main main
# 设置 HEAD 指向新分支
git remote set-head origin -a
# 删除远程分支
git push origin -d master
#git remote prune origin

清除历史记录

github - Make the current commit the only (initial) commit in a Git repository? - Stack Overflow

git checkout --orphan new
git add -A
git commit -am "init"
git branch -D main
git branch -m main
git push -f origin main
git gc --aggressive --prune=all

查询某文件的提交记录及当时内容

git log --follow --name-status -- <path>
git show <commit>:<path>

恢复已删除文件

git log --diff-filter=D --summary | grep delete | grep $name
git log --all -- FILEPATH $path
git show --summary $commit
git checkout $commit^ -- $path

恢复历史版本 git restore --source <SHA> path/to/file # SHA 为更改的上一个 commit-id

忽略大文件

find . -size +100M -printf '%P\n' >> .gitignore

git-lfs

严格覆盖合并

仅在本地忽略更改

  • assume-unchanged
    • git update-index --assume-unchanged <file-path>
    • git update-index --no-assume-unchanged <file-path>
    • git ls-files -v | grep '^h'
  • skip-worktree
    • git update-index --skip-worktree <file-path>
    • git update-index --no-skip-worktree <file-path>
    • git ls-files -v | grep '^S'
  • .git/info/exclude

大小写问题

注意删掉旧的那个