本地仓库
基础
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 main
git push <remote> <source>:<destination>
,例如git push origin foo~1:main
,当<destination>
不存在时,git 会在远程仓库中创建这个分支,例如git push origin main:newBranch
git 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/main
git branch -u origin/main foo
(如果当前就在foo
分支上,可以省略foo
)