分支管理

Git branch

  • 目的:“我想基于这个提交以及它所有的父提交进行新的工作。”

  • 创建一个新分支的方法:

    git branch <name>

  • 切换分支的方法:

    git checkout <name>

  • 创建一个新分支并切换到该分支上:

    git checkout -b <branch-name>

    切换完以后,会在原地出现一个新分支,也就是基于当前所在提交的一个新分支出现了。

Git merge

  • 目的:“我要把这两个父节点本身及它们所有的祖先都包含进来。”

  • 合并分支:

    在当前的分支上,执行:
    git merge <name>
    会将 name 分支合并入 main,生成一次新的提交,而 name 分支将保持在原地不动

Git rebase

  • 目的:我们某个分支上的工作全部转移到另一个分支上, 执行 rebase 指令后,所在分支的一个复制的将直接以对应分支为父节点,且会切换到这个复制的分支上(原分支仍会存在)

  • rebase 分支:
    git rebase <name>
    即将当前分支转移到 name 分支上

    如果要使接受转移的分支上的原指针和复制来的被转移分支同步,只需再次执行:
    git switch originbranch; git rebase newbranch

    通常如果我们将附属分支转移到主分支上,我们就需要再次 rebase 将主分支与 rebase 的附属分支同步,保持主分支在最顶端。

提交树移动

Head 分离

  • 定义:HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。** 可以理解 head 为一个鼠标 **

  • 分离 head 指针

    未分离情况下,head 指针跟随用户所在分支,指向 该分支上的最近一次提交记录

    使用以下指令分离 head 和分支:
    git checkout <某提交记录的哈希值>

    checkout 方法的实质更像是移动 head 指针,所以 checkout + 位置更广义地理解就是移动 head 指针到对应的位置,比如移动 head 指针到 main 分支上

    注意,一个哈希值可能是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8, 所以这不是一个友好的方法

    注意,使用 checkout+hash 的方法,可以将 head 移动到任何哈希值所指定的提交记录上!

相对移动

  • 相对移动操作符:

    ^ :向上移动一个提交记录
    ~<num> 向上移动多个提交记录, 默认为一次

  • 相对移动 HEAD 指针用途:

    如上文所提,我们已经知道移动 head 指针的方法就是 checkout, 但是移动的位置表示不是很方便,所以使用相对位置可以更方便地移动。注意,相对的参考点可以是提交记录的哈希值,分支名,也可以 head 指针自己

    git checkout bugFix^
    git checkout HEAD^^

移动分支指向:

我们可以使用以下命令使分支强制指向某一个提交记录

git branch -f <branch_name> <record>

注意,提交记录可以是相对引用,而且相对引用可能会方便一点

撤销更改

  • git reset

    git reset <record> 通过更改将分支记录倒退实现撤销改动,撤销到 record 记录。比如:

    git reset HEAD~1

    这样,原来指向的提交就就像从来没有提交过的一样,我们可以当作这次提交从未发生过

  • git revert

    git reset 只能在本地撤销本地修改,虽然这对于本地写代码已经足够了,但是不能分享给别人。或者说,如果操作对象是一个远程分支,那么远程记录中,被 reset 的提交不会消失,这不太妙。因此,我们可以新建一个分支,让这个分支的信息就是我们想要撤回到的信息。这个代码就是 git revert

    git revert <record> 运行以后,会在当前工作分支下,新建一个新的提交,这个提交就是撤回了 record 提交记录后的新提交,可以理解为与 record 记录之前一个提交记录相同

远程管理

git clone

  • 原理:从技术上来讲,git clone 命令在真实的环境下的作用是在本地创建一个远程仓库的拷贝(比如从 github.com)

远程分支

  • 原理:远程分支与本地创建的分支是不同的,它将反应最近一次你与远程仓库通信后的状态。git 不允许直接在远程分支上进行工作。因此远程分支会自动在检出的时候进入 head 的分离状态,意思是必须在别的分支上完成工作,然后更新远程分支,再分享自己的工作成果

  • 远程分支的命名格式:<remote_name>/<branch_name>

    通常 git 默认的远程仓库的名字就是 origin,如果一个分支名字是 br,那么这个分支的远程分支名字就是 origin/br

    注意,远程分支就是一个新的分支了,和原来的分支基本没有联系了

    远程分支无法在本地 commit 提交而改变(因为当你 checkout 到它上面的时候,他会自动分离出 head,你无法尝试直接在它上面 commit)

    每个分支都可以有对应的远程分支,都记录了最近一次远程仓库的状态

    如上所言,远程分支是用来 ** 指示最近一次你与远程仓库通信后的状态的!**

Git Fetch

  • 原理:git fetch 是与远程仓库通信的指令,完成了仅有的但是很重要的两步:

    • 从远程仓库下载本地仓库中缺失的提交记录
    • 更新远程分支指针 (如 o/main),使远程分支指向最新的同步远程记录

    注意,这说明 git 没有改动本地仓库的任何文件。需要另外的命令做到这一点

Git Pull

Git Pull 是 fetch 和 merge 的集合简写。意思就是拉去更新,然后做出一个新提交,这个提交是本地分支与远程分支的两个父节点的子节点

Git Push

Git push 和 pull 命令相反,它将本地仓库的提交上传到远程仓库,并且更改远程分支指针与本地分支相同

解决冲突

当本地仓库基于 提交一 做了提交三, 远程仓库中的版本是基于 提交一 做的提交二,这是会出现冲突,git 会拒绝直接的 push 请求。因此我们需要先解决冲突。

让我们先假设我们的提交三不会因为并入提交二而出错

我们需要让本地仓库包含远程仓库的提交,才能使用 push。数学地说,需要本地的提交有以远程仓库现有提交为父节点。

要打到这一点,其实只需要使用合并分支的各种方法就可以

  • 方法一: 先 pull 后 push

    pull 就是 fetch 和 merge 的集合简写。git 会先同步远程分支与远程仓库,然后将远程分支与本地分支合并,这样生成的新提交(如果合并没有出现问题的话)就可以 git push 了!

  • 方法二: 先 pull –rebase 后 push

    pull –rebase 就是 fetch 和 rebase 的集合简写。git 会先同步远程分支和远程仓库,然后将本地分支复制,接在远程分支下面,这样以后就可以用 git push 提交了

使用 pull request 的方法

pull request 的部分操作在 github 中进行,但是这之前,我们需要在本地提交代码。这部分需要执行 git 操作

我们最好 ** 新建一个带有我们代码的分支,将他推送到远程服务器 **。之后 ** 检查我们的主分支,让他保持和远程仓库的提交记录一致 **,防止之后我们的开发和远程仓库的开发不在同一起始点上,这虽然可能能用 pull + push 解决,但是不同起点的开发有可能会遇到 pull 后代码冲突的问题。

假设我们没有事先在分支上开发,而是不小心直接用 main 开发了(且远程仓库主分支就是 main),我们只需要以下三个命令:(假定我们的远程仓库就是对应的 PR 目标仓库)

1
2
3
git checkout -b myFeature
git push
git branch -f main origin/main