Git 基础知识

Learn Git

基础

确认修改 commit

  • 确认你的修改,将其持久化
1
git commit

分支 branch

  • 创建分支 branch1
1
git branch branch1
  • 切换到分支 branch1
1
git checkout branch1
  • 新建分支 branch1,并且切换到这个分支
1
git checkout -b branch1
  • 查看所有分支
1
git branch -a

合并分支 merge

  • branch1 分支合并到当前分支
    • branch1 中的头指针没有变换,当前分支的头指针指向合并后的节点
1
git merge branch1

例子

  • * 表示 head 节点
1
2
3
  A---B---C topic
/
D---E---F---G master*
1
git merge topic
1
2
3
  A---B---C topic
/ \
D---E---F---G---H master*

合并分支 rebase

  • 将当前分支的修改在 branch1 中都执行一遍,然后当前分支的头指针指向修改后的节点
    • nowBranch 默认为 HEAD
1
git rebase branch1 nowBranch
  • Q:如果说没有删除当前分支头指针指向的原来的那个节点,如何回到这个节点呢?
    • 实际上的 git 会删除原来的节点(删除没有引用的节点)
  • rebase 的含义:将当前分支和 branch1 的分支节点往后移动到指定的节点
    • 往前无效

例子

  • * 表示 head 节点
1
2
3
      A---B---C topic*
/
D---E---F---G master
1
git rebase master
1
2
3
               B'---C' topic*
/
D---E---F---G master

rebase/merge

  • rebase 让提交树更干净(一条线),但是会修改提交历史
  • merge 不会修改提交历史,但是提交树更复杂

删除分支

1
git branch --delete branch_name
  • 注意不能删除当前分支(需要先 checkout 到其他分支)

高级

  • head 指针:当前状态

  • Description 栏下,蓝色的小圆圈就是 head 的位置

分离头指针

  • 查看 head 指针
1
2
3
4
5
# linux
cat ./.git/HEAD

# windows
type .\.git\HEAD
  • 使用 git 查看 head 指针
1
2
# 需要 head 所在位置恰好是某个分支的位置(不能只是只是简单的哈希值的结点)
git symbolic-ref HEAD
  • 分离 head
1
2
git checkout main     # 指向 main
git checkout hashcode # 指向某一个提交节点

相对引用

  • 利用哈希值修改 head 指针
1
2
3
git checkout 092d58b38f41f9594c0c284ef6defc6aba788e8e
# 可以修改为唯一前缀
git checkout 092d5

^

  • ^ 表示父节点
  • 例如移动 headflower 的父节点
1
2
3
4
git checkout flower^
# 注意如果是在 windows 命令行中, 由于 ^ 表示转义,
# 如下命令才是表示移动到 flower 的父节点
# git checkout flower^^
  • 例如移动 headflower 的祖父节点
1
git checkout flower^^
  • 加数字表示移动到第几个父提交(存在多个父节点的时候)
加数字

  • 每一个命令都是基于上面的图(状态)进行
1
2
git checkout HEAD^^  # windows
git checkout HEAD^^1 # windows

1
git checkout HEAD^^2 # windows

~

  • ~N:表示向前移动 N 个节点

1
2
3
git checkout flower~1
# 等价于 git checkout flower~
# 等价于 git checkout flower^

链式连接

1
git checkout b1^^2~3

强制修改分支位置

  • main 分支强制指向 HEAD 的第 3 级父提交
1
git branch -f main HEAD^3

撤销变更

  • 原始状态

reset

  • 用于本地分支的修改
    • 相当于把哈希值为 e816** 结点之后的 commit 都取消了
1
git reset e816
image-20230102154651492

revert

  • 远程分支修改,用于合作的同步
    • 新增加一个节点,这个节点便是取消 f9f7** 之后的修改
1
git revert f9f7

另外的细微区别

  • git reset hashcode:表示恢复到 hashcode 节点的状态
    • 例如在上面的原始状态,运行 git reset f9f7,则无事发生(因为当前状态就是 f9f7** 的状态)
  • git reset hashcode:表示撤销 hashcode 节点之后(包括这个节点)的修改
    • 例如在上面的原始状态,运行 git reset f9f7,则回到前一个结点 e816** 的状态

移动提交记录

cherry-pick

  • 将某个节点上的 commit 在当前节点上进行 commit
  • 可以接多个修改

1
git cherry-pick abd8 00ae 8ea1

  • 可以接区间
    • 注意是左开右闭 \((a,b]\)
1
(abd8^, 8ea1] = [abd8, 8ea1]
1
2
3
4
5
# 上面的命令等价于
git cherry-pick abd8^..8ea1

# windows 版本如下
git cherry-pick abd8^^..8ea1 # 需要转义 ^

交互式 rebase

1
2
git rebase -i branch1
git rebase --interactive branch1
  • 可以选择需要当前分支上的哪些修改
    • 可以选择 pick/omit(还有更多选项)
    • 可以修改 commit 的顺序

1
2
git rebase -i e816
# 提示信息: Rebase e816ef1..2c5279a onto e816ef1 (3 commands)
1
2
3
4
# git-rebase-todo
pick 2c5279a c
drop c4a3404 p
pick 49f25db m

应用

debug

  • debug 的时候,某些 commit 适用于输出调试信息的,debug 结束之后可以不需要这些调试的commit
1
2
3
4
  main
/
A--------B--------C--------D bugFix*
(debug) (printf) (fixed)
  • 命令如下
1
2
git checkout main
git cherry-pick D
1
2
3
4
   D'---main*
/
A--------B--------C--------D bugFix
(debug) (printf) (fixed)

修改之前的记录

rebase
  • 需要对如下的 module3 进行一些修改
image-20230102223848364
1
2
3
4
5
git rebase -i bcc4
# 调换顺序
# pick a0cc8eb modify1
# pick e56f8ec modify2
# pick 082f9c4 add module3

  • 此时修改 module3,修改之后 add
1
2
git add module3.txt
git commit --amend
  • 修改 commit 信息
1
add module3(modify)

1
2
3
4
5
git rebase -i bcc4
# 调换顺序
# pick 603c602 add module3(modify)
# pick 8a1d97d modify1
# pick b3fb43a modify2

  • 实际操作会存在很多冲突,只有在需要修改的模块很独立,提交也比较独立的时候比较方便
cherry-pick

1
git checkout bcc4

1
git cherry-pick 76e2

  • 修改 module3,修改之后 add
1
2
git add module3.txt
git commit --amend
1
add module3(modify2)

1
git cherry-pick d7fa^^..6453 # windows

1
git branch -f master 3e49

  • 好处是比 rebase 更加容易处理冲突

杂项

tag

1
git tag tagName Node # unannotated tag
  • 给节点 Node 打上 tagName
  • 可以被作为节点的引用
  • 长久留存
1
git tag -a tagName -m tagMessage # annotated tag

describe

  • 找到距离节点 hashcode 最近的一个 annotated tag
    • hashcode 默认为 HEAD
1
git describe hashcode
  • 找到距离节点 hashcode 最近的一个 tag(包括 unannotated tag
1
git describe hashcode --tags
  • 输出格式
1
A-B-gC
1
2
3
A: tagName
B: commit numbers from hashnode
C: hashcode's hash number

远程

clone

  • 下载
1
2
git clone git@github.com:github/pycon2011.git
git clone https://github.com/github/pycon2011.git
  • 此时会至少有 3 个分支
    • master
    • remotes/origin/HEAD
      • 会自动形成远程的分离 HEAD,无法让你操作
    • remotes/origin/master
  • 远程分支命名规范:name/branch
  • 切换远程分支
1
2
git checkout remotes/origin/master
git checkout origin/master
  • 切换远程分支之后 commit,会形成本地的分离 HEAD 状态
    • 不会更新远程状态

  • 拉取特定分支
1
git clone -b branchName remote-link

fetch

  • 拉取所有远程分支
  • 不会更新本地其他分支的状态
  • 不改变 HEAD 指针位置
  • 合并分支:使用 merge/cherry-pick/merge 等方式

pull

  • fetch+merge
  • 拉取远程分支并合并
    • 只拉取当前分支
  • 修改 HEAD 指针位置为最新

push

  • 推送本地修改到远程
    • 只推送当前分支
  • 如果是然后会更新本地对应的远程分支状态
  • push 命令执行之前,会先将本地的记录和远程的同步
    • 不然可能有冲突,而远程分支自身不知道如何处理
  • 也就是说如果 push 命令执行失败,需要先 fetch 下来处理冲突之后再 push
1
2
3
4
git pull --rebase
# 等价于如下的命令
git fetch
git rebase

锁定的 master 分支

  • 不允许直接推送到远程的主分支,需要推送到其他分支
  • 用于权限管理,大型项目开发
  • 最好将 master 分支和 remotes/origin/master 分支保持一致
    • 这样在 pull 的时候不要会出问题

管理

  • 开发特性不在主分支上进行
  • 专门有人合并分支

远程追踪

  • 当前分支为 master,本地对应的远程分支为 origin/master
  • push
    • 推送本地分支到远程
    • 更新 origin/master
  • pull
    • 拉取远程分支,更新 origin/master
    • 更新 master
  • 默认关联:master 对应 origin/master
  • 自己设定(如下两种方式都 OK)
1
2
3
4
5
# notMasterBranch 分支可以不存在
git checkout -b notMasterBranch origin/master

# notMasterBranch 分支必须已经存在
git branch -u origin/master notMasterBranch

push 参数

1
git push remote branch
  • 在远程仓库 remote 中找到分支 branch,并将其更新,更新之后更新本地的 origin/branch
    • 默认没有参数的时候,branch 为当前 checkout 的点,此时只有检出点跟踪了某个远程分支,命令才生效
1
git push remote branch1:branch2
  • 把本地的 branch1 分支推送到 remotebranch2 分支
  • 支持相对引用的节点
    • branch1 可以是节点
  • 如果 branch2 不存在,会自动新建

fetch 参数

  • 和 push 类似
1
2
3
4
5
6
7
8
9
# 拉取远程的 branch 分支
git fetch remote branch

# 拉取远程的 branch1 节点到本地分支 branch2
# branch 2 不存在则会新建
git fetch remote branch1:branch2

# 拉取所有远程分支
git fetch
  • 注意运行结束前后,当前 HEAD 位置不变

source 留空

  • 神奇操作
  • push:远程删除分支
  • fetch:本地新建空分支
1
2
3
4
5
# 我们通过给 push 传空值 source,成功删除了远程仓库中的 side 分支
git push origin :side

# 如果 fetch 空值到本地,会在本地(当前节点)创建一个新分支
git fetch origin :bugFix

pull

1
2
3
4
git pull origin foo
# 等价于如下两条命令之和
git fetch origin foo
git merge o/foo
1
2
3
4
git pull origin bar~1:bugFix
# 等价于如下两条命令之和
git fetch origin bar~1:bugFix
git merge bugFix

其他

升级 git 版本

  • windows
1
git update-git-for-windows

git 老是卡死

1
git tag --column # 直接卡死