_(°:з」∠)_/Git Cheatsheet

Created Tue, 28 Sep 2021 00:00:00 +0000
2188 Words

本地仓库

基础

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 指这个 reftag 相差多少个提交记录,hashref 所表示的提交记录哈希值的前几位。当 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 pullgit fetch; git merge origin/main 的缩写。
  • git pull --rebasegit 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

这种情况可以先 mergerebasepush

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 分支

远程追踪

mainorigin/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