git学习日记

HangYF Lv2
前言:找到一个学习git很方便的网站:https://learngitbranching.js.org/?locale=zh_CN,记录一下学习关键点和总结。

版本节点树

使用git时,要在脑内形成一个版本树每一个节点都是一个提交记录,有独特的哈希码代表该节点。

分支

分支名可以看作一个指向版本节点的指针。

image-20250220123419979

当前所在分支是main,使用命令:

1
git branch newImage  # 创建一个新分支指向c1提交记录。
1
git checkout newImage # 切换到分支newImage 

简写:

1
git checkout -b newImage  # 创建同时切换分支

合并

merge方式合并

将bugFix分支的内容合并到main中,并产生新提交结点,同时main指向新节点。(提交树变复杂,但是不会改变提交历史)

1
git merge bugFix # 当前在分支main

image-20250220124318996

此时如何将bugFix移动到与main相同的位置?

1
2
git checkout bugFix
git merge main # 因为main是bugFix的子节点,已经包含了bugFix的内容,所以bugFix此时只会单纯的移动到main的位置。
rebase方式合并

变基,就是将本节点及一系列父提交记录复制并粘贴到另一个地方,创造更加线性的提交记录,但是改变了提交树的历史(从提交树上无法得到正确的提交流程)。

image-20250220151044744

1
git rebase main # 当前在bugFix分支上 以main为基础进行合并

image-20250220151205252

提交记录c3仍然存在,合并的同时,提交记录变为线性的。

同理,执行:

1
2
git checkout main
git rebase bugFix # 可以使main分支移动到bugFix的位置

在提交树上移动

可以理解为一个指针,指向当前正在处理的提交记录或者分支名。默认下,HEAD指向的都是分支名,但是可以通过checkout指令让它指向提交记录,这就是分离HEAD。

image-20250220152042141

此时 HEAD->main->c1 ,(注意:当HEAD指向分支名时,HEAD等价于该分支名)执行:

1
git checkout c1

image-20250220152149214

这里的c1代表提交记录的哈希值。分离后,git commit不会使main自动指向最新的提交c2,但是HEAD会指向c2。

通过哈希值指定HEAD指向比较困难,一般会使用git log来查看哈希值。哈希值很长,但是只需要写唯一标识提交记录的前几位即可。

相对引用

可以通过^和~两种符号来进行相对引用。

[哈希值|分支名]^ 表示该节点的父节点 (使用~也是相同的)

[哈希值|分支名]~num 表示该节点往上num个节点

[哈希值|分支名]^^^ 表示该节点往上3个节点

特殊:[哈希值|分支名]^num 在当前节点有多个父节点时,默认回到第一个父节点,上述指令则可以通过num选择第几个父节点来回溯

上面的符号支持链式操作。

强制修改分支位置

1
git branch -f main HEAD~3 # 让main强制指向HEAD的第三个父节点

撤销变更

reset:在本地让分支指向回退版本
1
git reset HEAD^ 

但是这样只能在本地使用,不能分享到远端。此时使用revert

revert:在本地创建新提交,该提交与某个历史版本相同。
1
git revert HEAD^

整理提交记录

cherry-pick:选定某些提交复制到当前位置(HEAD)下面

image-20250220154914625

1
git cherry-pick c2 c4

image-20250220154950266

rebase -i :当不清楚提交的哈希值时,使用交互式rebase整理提交
1
git rebase -i [结束整理的节点\产生分支的节点]

执行命令会导致出现交互式窗口,然后从当前节点到指定节点的一系列提交都可以选择和排序,然后git复制处理后的一系列节点到指定节点下(整理并复制之前的提交,产生新的一系列提交)。

历史提交记录不能直接修改,需要移动为最新提交(没有子孙节点)然后再修改

远程操作

1
git clone [远程仓库地址]  # 复制一个相同的仓库到本地,同时在本地创建同名分支和一个同名远程分支

远程分支命名:[remote name/branch name],remote name往往是origin

若执行:

1
git checkout origin/main # 自动分离HEAD,因为本地的远程分支是远程仓库中该分支的镜像,时只读的

git fetch

image-20250220161929784

1
git fetch (无参数,下载远程仓库所有分支更新到本地的每一个对应远程分支)

image-20250220161950933

可以看到:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 o/main)
  • 本地的main分支没有更新
1
2
3
git fetch [remote] [place]
例如: git fetch origin foo # 将远端foo分支内容下载到本地origin/foo
git fetch [remote] [source:destination] # 注意:source是远端,这条命令不常用,若destination不存在,则会在本地创建

git fetch + git merge = git pull

在fetch基础上:

1
git merge origin/main #更新本地main分支
1
git pull # 一步完成以上步骤

git pull的参数可以看作提供给了git fetch和git merge。

1
2
3
4
5
6
7
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

# git pull 实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)

git push

  • 将本地与远端对应的分支推送到远端,更新远端的同时,将本地在远端分支的镜像也更新

即:将main分支推送并更新远端,本地的origin/main也更新。

1
2
3
4
5
6
git push [remote] [place]
如:git push origin main
含义是:切换到本地的main分支,push到origin中的main分支(本地main追踪的分支)。

如果来源去向不同呢?(本地分支和追踪的分支名称不同) 使用以下命令
git push [remote] [source:destination]

仅使用git push也可以,但是要保证当前工作分支是要push的分支。

image-20250220171814055

1
git push origin foo^:main

image-20250220171840086

如果目标分支不存在,则在远端创建一个对应名称的分支。

没有指定source时
1
2
git push origin :foo # 会导致远程仓库中的foo分支被删除
git fetch origin :foo # 会在本地创建foo分支(前提是foo不存在)

冲突的工作

设想以下场景:假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

这时你怎么将自己的改动push上去?

  1. 先将更新拉取下来并合并到本分支
  2. 然后将结果push到远端
1
2
3
4
git fetch
git rebase o/main
(可以合并为 git pull --rebase)
git push

远程服务器拒绝 要求pull request

远程仓库拒绝直接将改动push到main,多见于提交别人的仓库时,别人不想你随便改动他的仓库,他希望控制权在自己手中,于是设置了pull requist。此时你不能将改动直接push到对方已存在的分支上。

解决:新建一个分支fix并提交,将合并的权力交给对方。然后将本地的main分支reset到origin/main的位置,防止产生冲突。

跟踪远程分支

远程分支需要与本地分支进行绑定,然后才能使用fetch pull push等指令进行交流。clone时会默认生成本地分支并绑定,名称往往是相同的。

如何自己指定某个本地分支与远程绑定?

  1. 创建一个newMain 跟踪远程分支o/main
1
git checkout -b newMain o/main
  1. 使现有分支追踪远程分支
1
git branch -u o/main foo # 如果当前就在foo上,还可以省略foo
  • Title: git学习日记
  • Author: HangYF
  • Created at : 2025-02-20 17:32:59
  • Updated at : 2025-02-20 17:35:06
  • Link: https://redefine.ohevan.com/2025/02/20/git学习日记/
  • License: This work is licensed under CC BY-NC-SA 4.0.