On this page
- 基本操作
- checkout
- commit
- Branch
- HEAD
- 撤销变更
- 整理提交记录
- git tag
- 远程仓库
- no-fast-forward
- 丢弃更改
- git config
- 关联远程仓库
- Stash
- 设置代理
- 查看提交次数
- 代码量统计
- 同步更新 fork 的仓库
- 多账号问题
- crlf
- no matching host key type found. Their offer: ssh-rsa
- fatal: early EOF fatal: fetch-pack: invalid index-pack output
- git flow
- git worktree
- submodule
- submodule vs subtree
- mono-repo vs multi-repo
- gist
- 问题

基本操作
| command | remark |
|---|---|
git clone -b <branch name> <url> | 克隆指定分支 |
git add <file> | 可反复多次使用,添加多个文件 |
| git commit | 提交 |
| git status | 工作区的状态 |
| git diff | 查看修改内容 |
| git reset —hard commit_id | 版本恢复,HEAD 指向的版本就是当前版本 |
| git checkout — file | 未添加到暂缓区,丢弃工作区的修改 |
| git log | 查看提交历史 |
| git reflog | 查看命令历史,以便确定要回到未来的哪个版本 |
| git rm | 删除 |
| git remote -v | 查看远程库信息 |
| 分支😊 | |
| git branch -a | 查看远程分支 |
| git branch | 查看分支 |
git branch <name> | 创建分支 |
git checkout <name> | 切换分支 |
git checkout -b <name> | 创建 + 切换分支 |
git merge <name> | 合并某分支到当前分支 |
git branch -d <name> | 删除分支 |
| git log —graph —pretty=oneline —abbrev-commit | 查看分支的合并情况 |
| git merge —abort | 终止 merge |
| git checkout -b branch-name origin/branch-name | 在本地创建和远程分支对应的分支 |
| git fetch | 从远程获取最新版本到本地,不会自动 merge |
| git pull | 更新分支 从远程获取最新版本并 merge 到本地 |
| git push origin test | 把分支推到远程分支 |
| tag😊 | |
git tag <name> | 用于新建一个标签,默认为 HEAD,也可以指定一个 commit id |
git tag -a <tagname> -m “qaq…” | 指定标签信息 |
git tag -s <tagname> -m “qaq…” | 用 PGP 签名标签 |
git show <tagname> | 查看标签详细信息 |
| git tag | 可以查看所有标签 |
git push origin <tagname> | 推送一个本地标签 |
| git push origin —tags | 推送全部未推送过的本地标签 |
git tag -d <tagname> | 删除一个本地标签 |
git push origin :refs/tags/<tagname> | 删除一个远程标签 |
| other😊 | |
| ssh-keygen -R github.com | 删除 known_hosts 配置 |
| ssh -T [email protected] | 测试连接 |
checkout
git checkout 提交记录
git checkout main^
git checkout main^^
git checkout HEAD~4
git checkout -b foo o/main;
git branch -u o/main foo
commit
- Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!
- 它会将当前版本与仓库中的上一个版本进行对比,并把所有的差异打包到一起作为一个提交记录。
- Git 还保存了提交的历史记录。这也是为什么大多数提交记录的上面都有 parent 节点的原因
Branch
git branch -f 用于强制将一个分支指向另一个特定的提交,无论是否会导致非快进更新(non-fast-forward)
git checkout -b 命令用于创建一个新的分支并立即切换到该分支
git switch 命令用于切换到另一个分支或创建并切换到一个新分支
对比:
git switch专注于分支管理,简化了切换和创建分支的流程。git checkout还可以用于检出文件或特定的提交,因此有更多的功能,但更复杂
Git 的分支也非常轻量。它们只是简单地指向某个提交纪录 —— 仅此而已
“我想基于这个提交以及它所有的 parent 提交进行新的工作。”
git push origin <source>:<destination> source 推送到 destination
git fetch origin <source>:<destination> 拉取 source 到 destination
git push origin :foo 删除远程 foo 分支
git fetch origin :foo 本地创建 foo 分支
git pull origin foo 相当于:git fetch origin foo; git merge o/foo
git pull origin bar:bugFix 相当于:git fetch origin bar:bugFix; git merge bugFix
merge
在 Git 中合并两个分支时会产生一个特殊的提交记录,它有两个 parent 节点。翻译成自然语言相当于:“我要把这两个 parent 节点本身及它们所有的祖先都包含进来。”

rebase
优点:
- Rebase 使你的提交树变得很干净, 所有的提交都在一条线上
缺点:
- Rebase 修改了提交树的历史
比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。
一些开发人员喜欢保留提交历史,因此更偏爱 merge。而其他人(比如我自己)可能更喜欢干净的提交树,于是偏爱 rebase。仁者见仁,智者见智。
将一个分支的更改重新应用到另一个分支的基础上
实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。
- git rebase
<upstream>: 这将把当前分支的更改应用到指定的上游分支 - git rebase —continue
- git rebase —abort
- git rebase -i HEAD~3
- git rebase <upstream_branch> <topic_branch>
- git checkout <topic_branch>
- git rebase <upstream_branch>
Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰

rebase vs merge
- rebase:会重写历史,没有合并记录,历史更线性。
- merge:保留了历史中的分支点和合并点,历史记录可能更复杂。
HEAD
分离 HEAD 状态(Detached HEAD)是指在 Git 中,你的 HEAD 指向了一个具体的提交,而不是某个分支。这意味着你当前所做的更改是基于一个特定的提交,而不是在一个分支上进行的
~ 向上返回几代
^ 合并提交记录正上方的那个提交记录
HEAD 是一个对当前所在分支的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。
HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。
HEAD 通常情况下是指向分支名的(如 bugFix)。在你提交时,改变了 bugFix 的状态,这一变化通过 HEAD 变得可见。
通过哈希值指定提交记录很不方便,所以 Git 引入了相对引用
强制修改分支位置
git branch -f main HEAD~3
撤销变更
用于回退或移动当前分支的提交历史和暂存区的状态。它可以改变 HEAD 指向的位置,并根据使用的选项来调整暂存区和工作目录的内容
在 Git 里撤销变更的方法很多。和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这个应用主要关注的是后者。
主要有两种方法用来撤销变更 —— 一是 git reset,还有就是 git revert。接下来咱们逐个进行讲解。
采用传入:git checkout —theirs .
采用当前:git checkout —ours .
整理提交记录
“我想要把这个提交放到这里, 那个提交放到刚才那个提交的后面”
git cherry-pick 将某个分支的一个或多个提交应用到当前所在的位置(HEAD),只要不是 HEAD 上游的提交就没问题
git rebase -i HEAD~i : pick/omit/sort 手动修改、重排、合并、删除或修改提交记录。它是一个非常强大的工具,用于清理和优化提交历史
git reset —hard C3'''
git checkout -B main C3'''
git tag
- 分支会被人为移动
- 永远指向某个提交记录的标识
git describe 的语法是:
git describe <ref>
<ref> 可以是任何能被 Git 识别成提交记录的引用,如果你没有指定的话,Git 会使用你目前所在的位置(HEAD)。
它输出的结果是这样的:
<tag>_<numCommits>_g<hash>
tag 表示的是离 ref 最近的标签, numCommits 是表示这个 ref 与 tag 相差有多少个提交记录, hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。
当 ref 提交记录上有某个标签时,则只输出标签名称
远程仓库
远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据
git fetch 完成了仅有的但是很重要的两步:
- 从远程仓库下载本地仓库中缺失的提交记录
- 更新远程分支指针 (如
o/main) - 并不会改变你本地仓库的状态。它不会更新你的
main分支,也不会修改你磁盘上的文件
更新到我们的工作当中
git cherry-pick o/maingit rebase o/maingit merge o/main
git fetch; git rebase o/main; git push
git pull --rebase; git push
git fetch; git merge o/main; git push
git pull; git push
git push
git push origin main
no-fast-forward
merge 时生成一个新的 commit,这样,从分支历史上就可以看出分支信息
git merge --no-ff -m "merge with no-ff" dev

丢弃更改
如果修改未添加到暂缓区,想丢弃工作区的修改:
git checkout -- file
如果修改已经添加到了暂缓区,想丢弃修改:
git reset HEAD filegit checkout -- file
如果修改已经提交到了版本库:
git reset --hard commit_id版本回退
git config
# 项目配置
git config --local -l
# 用户配置
git config --global -l
# 系统配置
git config --system -l
关联远程仓库
1.git remote add origin git@server-name:path/repo-name.git
2.git push -u origin master:第一次推送
3.git push origin master:以后的推送
Stash
暂存工作区和暂存区的修改
-
git stash:存储工作现场 -
git stash pop:恢复并删除 stash 内容 -
git stash apply:恢复工作现场 -
git stash list:查看 stash 内容
设置代理
ssh
~/.ssh/config 文件
Socks 代理
mac
Host github.com
HostName github.com
ProxyCommand nc -v -x 127.0.0.1:1086 %h %p
windows
Host github.com
ProxyCommand connect -H 127.0.0.1:7890 %h %p
http
指定 github.com
git config --global http.https://github.com.proxy http://127.0.0.1:8080
git config --global https.https://github.com.proxy http://127.0.0.1:8080
git config --global http.https://github.com.proxy socks5://127.0.0.1:7890
git config --global http.https://github.com.proxy socks5h://127.0.0.1:7890
取消
git config --global --unset http.proxy
git config --global --unset http.https://github.com.proxy
git config --global --unset https.proxy
git config --global --unset https.https://github.com.proxy
查看提交次数
总提交次数:git log --oneline | wc -l
某个用户提交次数:git log --author="用户名" --oneline | wc -l
每个用户提交次数:git shortlog -s -n
某个用户时间范围内提交次数:git log --author="用户名" --since="2014-07-01" --oneline | wc -l
代码量统计
行数
git ls-files | xargs cat | wc -l
个人代码量:
git log --author="username" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
每个人代码量
git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done
同步更新 fork 的仓库
- 添加一个将被同步给 fork 远程的上游仓库
git remote add upstream https://github.com/apache/flink.git
- 从上游仓库 fetch 分支和提交点,传送到本地,并会被存储在一个本地分支 upstream/master
git fetch upstream- 指定分支
git fetch upstream 18-3-1
- 在本地分支上,执行合并
git merge upstream/master
- 推送到远程
git push origin master
多账号问题
Git 最著名报错“ERROR: Permission to XXX.git denied to user”终极解决方案
Git’s famous “ERROR: Permission to .git denied to user”
crlf
// 拒绝提交包含混合换行符的文件 (一般设置为true)
git config --global core.safecrlf true
// 提交检出均不转换
git config --global core.autocrlf false
core.autocrlf
true:提交时改成 LF,检出时改成 CRLF
input:提交时改成 LF,检出时不改
false:提交时是什么就是什么,不改换行符,检出时也不改 (默认值)
core.safecrlf
true: 拒绝提交包含混合换行符的文件(会提示 Fatal:xxx)
false: 允许提交包含混合换行符的文件
warn: 提交包含混合换行符的文件时给出警告 (默认值)
.gitattributes 文件中
注释
* text=auto
或改为:
* text=eol=lf
no matching host key type found. Their offer: ssh-rsa
配置好公私钥之后,仍然无法直接用 git ssh 的方式,下载代码,解决:
.ssh/config 增加以下二项
Host x.x.com
HostKeyAlgorithms ssh-rsa
PubkeyAcceptedKeyTypes ssh-rsa
fatal: early EOF fatal: fetch-pack: invalid index-pack output
<https://stackoverflow.com/questions/21277806/fatal-early-eof-fatal-index-pack-failed>
# Git 服务器的内存不够了,导致压缩传输数据失败,服务器直接挂了
# 整数 -1..9,表示默认压缩级别。 -1 是 zlib 默认值。0 表示不压缩,9 是最慢的。
# 关闭压缩
git config --global core.compression=0
# 下载最近一次提交
git clone --depth 1 `<repo_URI>`
# 拉取剩余部分
git fetch --unshallow
# 常规拉取
git pull --all
git flow
https://nvie.com/posts/a-successful-git-branching-model/
release 分支理解为 提测分支。来自 develop
如果测试有 bug,在 release1.2 分支修复,合并回 develop,有冲突解决
测试完成要发布了,分别合并回 develop 和 master
问题:
1、从 master 拉分支,分支名:bugfix/xxxxxx feature/xxxxx
2、开发自测完将新拉分支合到 dev,开发环境自测一波
3、开发环境自测没问题,将新拉分支合到 test,用 test 发预发环境
4、测试发现 bug/需求调整等,重复 2,3 步骤
5、测试通过,将此次拉的分支合到 master,如需发正式,从 master 发

git worktree
git worktree add <新路径> -b <新分支名>
git worktree add <新路径> -b <新分支名> <指定分支名>
git worktree add ../worktree-fixa -b feature/fixa release
从 release 分支拉一个新分支 feature/fixa,放到../worktree-fixa 目录
submodule
https://git-scm.com/docs/git-submodule
https://git-scm.com/docs/gitsubmodules
https://juejin.cn/post/7154398231449829383
添加子模块
# 会自动拉取代码 .gitmodules添加lodash
git submodule add [email protected]:lodash/lodash.git lodash
更新
新 clone 的项目不会自动 clone submodule
git submodule init 用来初始化本地配置文件,将.gitmodules中关于[submodule]的部分拷贝到.git/config文件中。
git submodule update 根据项目的.gitmodules文件,抓取远程仓库的代码。
git submodule update --init --recursive 初始化,拉取所有子模块
git submodule update —remote:这个命令会更新子模块并将其切换到最新的远程提交。
git submodule update:使 submodule 的分支处于主项目里指定的 commit id。可能并不是拉 submodule 的 master 最新代码
删除
# 清空lodash目录(lodash文件夹本身未删除),移除$GIT_DIR/config中的lodash
git submodule deinit lodash
# .gitmodules移除lodash,删除lodash目录,config未修改
git rm lodash
# 手动删除 $GIT_DIR/modules/<name>/
submodule vs subtree
https://stackoverflow.com/questions/31769820/differences-between-git-submodule-and-subtree
submodule is link
subtree is copy
mono-repo vs multi-repo
gist
github secret gist
只有知道 url 的才能看到
问题
https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/