本地仓库
基础
git commit
# 新建分支
git branch <your-branch-name>
# 切换分支
git checkout <your-branch-name>
# 简写
git checkout -b <your-branch-name>
Merge
用于合并两个分支,把这两个分支的父节点本身及它们所有的祖先都包含进来
flowchart RL
C2((C2)) --> C1((C1))
C3((C3)) --> C1
C1 --> C0((C0))
main* --o C2
bugFix --o C3
git merge bugFix
flowchart RL
C4((C4)) --> C2((C2))
C4 --> C3((C3))
C2 --> C1((C1))
C3 --> C1
C1 --> C0((C0))
bugFix --o C3
main* --o C4
Rebase
把一个分支的工作直接移到另一个分支
flowchart RL
C2((C2)) --> C1((C1))
C3((C3)) --> C1
C1 --> C0((C0))
main --o C2
bugFix* --o C3
git rebase main
把当前分支嫁接到另一条分支下
flowchart RL
C2((C2)) --> C1((C1))
C3((C3')) --> C2((C2))
C1 --> C0((C0))
main --o C2
bugFix* --o C3
git checkout bugFix
git rebase main
# 简写:git rebase <branch-as-base> <branch-to-rebase>
git rebase main bugFix
在提交树上移动
HEAD: 对当前检出记录的符号引用,总是指向当前分支上最近一次提交记录。
分离 HEAD
flowchart RL
C1((C1)) --> C0((C0))
main* --o C1
HEAD --o main*
通过哈希值指定提交记录:git checkout C1 (实际情况下 C1 应为哈希值)
flowchart RL
C1((C1)) --> C0((C0))
main --o C1
HEAD --o C1
通过相对引用指定提交记录
- 使用
^向上移动 1 个提交记录(^^向上移动 2 个,依此类推) - 使用
~<num>向上移动多个提交记录,如~3
~,~~具有^,^^同样的功能,因为^还有指定父节点的功能,为免混淆尽量用~来表达相对位置
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main* --o C2
HEAD --o main*
git checkout main~ 或 git checkout HEAD~
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main* --o C2
HEAD --o C1
当提交记录有多个父节点时,操作符
^后跟数字可以指定第几个父节点
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C0
C3((C3)) --> C1
C3 --> C2
main* --o C3
git checkout main^2
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C0
C3((C3)) --> C1
C3 --> C2
main --o C3
HEAD --o C2
强制修改分支位置
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
C4((C4)) --> C3
main --o C4
bugFix* --o C4
git branch -f main HEAD~3
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
C4((C4)) --> C3
main --o C1
bugFix* --o C4
撤销变更
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main* --o C2
a) git reset HEAD~1
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) -.-> C1
main* --o C1
C2所作的变更还在,但是处于未加入暂存区状态。git reset可以在本地分支使用,但是对于远程分支无效。撤销更改并分享给别人需要使用git revert。
b) git revert HEAD
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C2')) --> C2
main* --o C3
C2'中包含的更改用于撤销C2这个提交,C2'的状态与C1相同。
移动提交记录
已知所需提交记录的哈希值的情况下,将一些提交复制到当前所在位置(HEAD)下。
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
C4((C4)) --> C3
C5((C5)) --> C1
side --o C4
main* --o C5
git cherry-pick C2 C4
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
C4((C4)) --> C3
C5((C5)) --> C1
C6((C2')) --> C5
C7((C4')) --> C6
side --o C4
main* --o C7
在不知道提交记录哈希值的情况下,可以使用交互式 rebase。
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
C4((C4)) --> C3
C5((C5)) --> C4
main* --o C5
git rebase -i HEAD~4
这时可以任意调整
C2, C3, C4, C5的顺序,也可以删除或合并提交。
Git Tags
永远指向某个提交记录的标识,如软件版本、新的特性等。不会随着新的提交而移动。
可以签出到标签(进入分离
HEAD的状态),但是不能做提交
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main* --o C2
git tag v1 C1
如果不明确指定提交记录(
C1),会默认使用HEAD所指向的位置。
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main* --o C2
v1(v1) --o C1
Git Describe
git describe <ref>,其中 <ref> 可以是任何能被 Git 识别成提交记录的引用,缺省值为 HEAD。
输出结果为 <tag>_<numCommits>_g<hash>。<tag> 表示离 ref 最近的标签,numCommits 指这个 ref 与 tag 相差多少个提交记录,hash 是 ref 所表示的提交记录哈希值的前几位。当 ref 提交记录上有标签时,则只输出标签。
远程仓库
远程仓库统一用虚线节点来表示。
拷贝远程仓库
flowchart RL
C1((C1)) --> C0((C0))
main* --o C1
classDef remote stroke-dasharray: 5 5
class C0,C1,main* remote
git clone
flowchart RL
C1((C1)) --> C0((C0))
main* --o C1
origin/main --o C1
origin/main叫做远程分支,反映最后一次和远程仓库通信时的状态。远程分支会在检出时自动进入分离HEAD的状态。不能直接在远程分支工作,必须在其他地方完成工作再把它分享到远程仓库,在这之后远程分支才会更新。远程分支的命名方式为<remote name>/<branch name>。
从远程仓库获取数据
git fetch:单纯从远程仓库下载数据
- 从远程仓库下载本地仓库缺失的记录
- 更新远程分支指针
- 不会改变本地仓库的状态,也不会修改本地文件
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
main --o C3
classDef remote stroke-dasharray: 5 5
class C0,C1,C2,C3,main remote
flowchart RL
C1((C1)) --> C0((C0))
main* --o C1
origin/main --o C1
git fetch
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
C3((C3)) --> C2
main* --o C1
origin/main --o C3
git fetch 进阶用法:
git fetch <remote> <place>,例如git fetch origin foo会更新origin/foo但不会修改本地的foo分支git fetch <remote> <source>:<destination>,例如git fetch origin foo~1:bar,这时会更新远程仓库的foo~1到本地的bar分支git fetch <remote> :<destination>,例如git fetch origin :bar,会在本地创建一个新的bar分支
获取数据再合并
git pull是git fetch; git merge origin/main的缩写。git pull --rebase是git fetch; git rebase origin/main的缩写。
git pull <remote> <source>:<destination>相当于git fetch <remote> <source>:<destination>; git merge <remote>/<destination>
上传新的提交记录
flowchart RL
C1((C1)) --> C0((C0))
main --o C1
classDef remote stroke-dasharray: 5 5
class C0,C1,main remote
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
origin/main --o C1
main* --o C2
git push
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main --o C2
classDef remote stroke-dasharray: 5 5
class C0,C1,C2,main remote
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
origin/main --o C2
main* --o C2
当本地仓库与远程仓库的提交记录不匹配时,无法 push:
flowchart RL
C1((C1)) --> C0((C0))
C2((C2)) --> C1
main --o C2
classDef remote stroke-dasharray: 5 5
class C0,C1,C2,main remote
flowchart RL
C1((C1)) --> C0((C0))
C2((C3)) --> C1
origin/main --o C1
main* --o C2
这种情况可以先 merge 或 rebase 再 push
git pull --rebase
# 或 git pull,推荐使用 rebase
git push
git push 的进阶用法(类似 git fetch):
git push <remote> <place>,例如git push origin maingit push <remote> <source>:<destination>,例如git push origin foo~1:main,当<destination>不存在时,git 会在远程仓库中创建这个分支,例如git push origin main:newBranchgit push <remote> :<destination>,例如git push origin :foo会删除远程仓库的foo分支
远程追踪
main 和 origin/main 的关系是由分支的 remote tracking 属性决定的。main 被设定为跟踪 origin/main,也就是说为 main 分支指定了推送(push)的目的地以及拉取后合并(merge)的目标。
我们可以让任意分支跟踪 origin/main,然后该分支会像 main 一样得到隐含的 push 目的地和 merge 的目标。
有两种方法设置这个属性
git checkout -b foo origin/maingit branch -u origin/main foo(如果当前就在foo分支上,可以省略foo)