From 81e651bb7502c685ad33d4790db61ea4b5e3aaa2 Mon Sep 17 00:00:00 2001 From: mikiyashiki Date: Sat, 9 Apr 2022 23:47:28 +0800 Subject: [PATCH] daily commit --- Git/git分支.md | 210 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 Git/git分支.md diff --git a/Git/git分支.md b/Git/git分支.md new file mode 100644 index 0000000..acab502 --- /dev/null +++ b/Git/git分支.md @@ -0,0 +1,210 @@ +# Git Branch +* ## Git原理 + * 在git中,并不通过修改集或者差异来存储数据,而是按照快照的方式来存储数据。 + * 当在执行一次commit时,git会存储commit object。commit object中包含了一个指向快照的指针。 + * commit object包含如下信息: + * 作者名称和email地址 + * 为commit指定的提交信息(comment) + * 指向快照(snapshot)的指针 + * 指向之前提交的指针(parent或parents): + * 对于正常的提交,有0个(init commit)或者一个parent + * 对于合并多个分支的commit,有多个parent + * git 提交的具体细节: + * 对于将文件stage的操作,git会为每个staged的文件计算一个checksum,并且将文件的版本存储到git仓库中(文件版本将会被当做blob访问),并且将checksum添加到staging area中 + * 当调用git commit时,git为项目的每个子目录计算checksum,,并且将它们作为树形对象存储到git仓库中。 + * git稍后会创建一个commit object,commit object中包含元数据和指向项目树对象的指针,树结构中指明了目录下文件指向哪个blob。由于快照只是指向树对象的指针,故而快照可以被重复创建。 + * snapshot会包含一个指向它前面快照的指针 + * git branch的本质: + * 在git中,git branch实际上只是一个指向commmit的可移动指针。 + * 对于每次提交,master branch的指针都会自动移动向最新的提交。 + * HEAD指针: + * HEAD指针指向当前所在的分支,可以通过git log指令查看HEAD指针当前和哪些分支的指针相同 +* ## Git Branch Operation + * 创建新的branch + * 创建新的分支实际上是创建了一个可供移动的新指针,指向了当前所在的提交(HEAD指针所指向的提交) + * 可以通过git branch来创建一个新的分支 + ```shell + # 创建一个指向当前快照的新branch指针 + $ git branch + or + # 创建一个新分支并且移动到该分支 + $ git checkout -b + # 创建一个新分支并移动到该分支 + $ git switch -c + ``` + * 在现有的分支之间进行切换 + * 在分支之间进行切换实际上是把HEAD指针指向别的branch指针 + * 可以通过git checkout来实现分支之间的切换 + * 在调用git checkout在分支之间进行切换时,工作目录中的内容也会被还原到目标分支的版本,故而在切换之前需要对尚未提交的任务进行提交,否则未保存的任务会丢失 + ```shell + # 在分支之间进行切换 + # * 在切换分支时,会做如下事情 + # * 将HEAD指针指向想要切换到的分支 + # * 将working directory中的文件还原到目标分支的快照状态 + $ git checkout + or + $ git switch + # 移动到上一个分支 + $ git switch - + ``` + * 在分支下调用git log + * git log只会显示当前分支下的提交历史,不会显示其他分支下的提交历史。 + * 如果要显示指定分支的提交历史,为git log指定branch-name + ```shell + $ git log + ``` + * 如果想要显示所有分支下的提交历史,可以为git log添加-all选项 + ```shell + $ git log --all + ``` + * 显示git分支状态图 + * 通过为git log指定--graph选项,可以输出git分支的状态图 + ```shell + $ git log --all --pretty=oneline --graph + ``` + * 合并分支 + * 可以通过git merge操作来合并两个分支 + * 如果想要让当前分支合并一个分支,并且当前分支对于目标分支来说在commit history中是可达的,那么git会直接让当前分支的指针移动到目标分支的最后一次提交位置。这被称之为“fast-forward”机制。 + ```shell + # 可以通过git merge命令来对分支进行合并 + # 如果合并的目标分支的git log记录中当前分支是可达的,那么 + # 会直接将当前分支的指针移动到目标分支指向的位置 + $ git merge + ``` + * 如果当前分支的last commit并不是目标分支的祖先,那么合并分支需要通过创建一个新的snapshot,新的snapshot存储合并分支的结果,并且创建一个commit指向snapshot。新的commit具有两个parent。 + * 在合并后,如果被合并的分支也不再被需要,那么同样可以通过git branch -d操作来删除旧的分支 + * 删除不再被需要的分支 + * 通常,当创建一个hotfix分支来修复bug时,如果bug已经被修复,切换到master分支进行merge操作后,hotfix分支不再被需要,可以调用git branch -d来删除分支 + ```shell + # 删除不再被需要的分支 + # * 当删除已经被合并的分支时,可使用-d选项 + # * 如果待删除分支没有被合并,那么需要指定-D选项来强制删除 + $ git branch -d + ``` + * 分支合并冲突 + * 在合并时,相较于两个分支的共同祖先,如果两个分支修改了同一个文件的同一部分,那么文件的合并会发生冲突。 + * 在发生合并冲突后,可以通过git status来检测冲突的细节 + ```shell + # 通过git status来检测合并冲突的细节 + $ git status + ``` + * git分支管理 + * 查看当前项目的分支,可以用git branch命令 + ```shell + # 查看当前项目的分支 + $ git branch + or + # 查看当前项目的分支,并且列出每个分支的最后一次提交 + $ git branch -v + # 可以通过--merged或者--no-merged选项来对已经或者还未合并到当前 + # 分支的分支进行过滤 + # 列出已经合并到当前分支的分支 + $ git branch --merged + # 列出尚未被合并到当前分支的分支 + $ git branch --no-merged + ``` + * 对于git branch --merged列出的分支,除了当前分支外都可以删除,因为已经对其进行了合并操作,可以无丢失的删除该类分支 + * 对分支进行重命名 + * 可以通过git branch --move来对本地分支进行重命名 + ```shell + # 重命名本地分支 + $ git branch --move + ``` + * 对远程仓库的分支进行重命名操作 + * 可以通过git push --set-upstream origin new-name来对远程仓库的分支进行重命名操作 + * 执行该操作后,远程仓库中旧分支名仍然会存在,需要手动的删除 + ```shell + # 重命名远程分支 + $ git push --set-upstream + # 删除旧分支 + $ git push --delete +* ## 远程分支 + * 远程tracking分支 + * 远程tracking分支是对远程分支状态的引用。远程tracking分支虽然是本地分支,但是你并不能对其进行移动。只有每次当你与服务器进行网络连接时,远程tracking分支才会移动。 + * 远程tracking分支名字按照remote/branch-name的形式组成 + * 在调用git clone方法时,会自动创建一个origin/master的远程tracking分支和一个本地的master分支。origin/master分支在之后没有和服务器通信的时间段内都不会被修改。 + * 当调用git fetch origin操作后,位于服务端的信息被拉取到本地,并且origin/master也会被移动到和服务端相同的位置,此时origin/master和本地master分支进行合并可能会出现冲突的问题 + * 如果有多个远程项目,那么属于不同项目的远程tracking分支通过remote shortname区分 + ``` + # remote tracking branch in different remote server + # * such as remote-1/ProjectName + # * remote-2/ProjectName + ``` + * 将本地分支推送到远程仓库 + * 可以用git push操作将本地分支推送到远程分支 + ```shell + # 推送本地分支 + $ git push + # 如果远程分支名称和本地分支相同,那么可以简化为如下形式 + $ git push + * 将远程分支拉取到本地 + * 当调用git fetch方法将远程仓库拉取到本地时,对于远程的分支hotfix,拉取到本地后只会有一个remote/hotfix的远程tracking分支,并不会创建一个名为hotfix的本地分支。 + * 如果想要基于远程tracking分支的基础上创建一个本地分支,可以调用如下命令 + ```shell + # 创建一个本地分支,并且将开始位置设置为remote/branch + $ git checkout -b 'branch-name' remote/branch + or + $ git switch -c 'branch-name' remote/branch + ``` + * 跟踪分支 + * 根据远程tracking分支创建分支时,会自动的创建一个跟踪分支。 + * 跟踪分支是一个和远程分支相关联的本地分支,当位于跟踪分支时,调用git pull时,会自动知道应该从哪个远程服务器进行拉取,并且知道整合哪个分支 + * 当调用git clone时,会自动创建一个本地的跟踪分支master,master分支和远程分支origin/master分支相关联 + * 创建一个跟踪分支,除了使用git checkout -b 'branch-name' 'remote/branch'外,还可以使用--track来指定 + ```shell + # 创建跟踪分支并且切换到该分支 + $ git checkout --track remote/branch + + # 甚至,当checkout指定的名称 + # * 本地仓库中并不存在该名字的分支 + # * 该名字的分支在远程仓库中存在 + # 那么,调用git checkout 就会直接创建跟踪分支 + $ git checkout + ``` + * 如果已经存在一个本地分支,并且想将其跟踪远程分支;或者想要对一个本地分支跟踪的远程分支进行修改,可以使用git branch -u来完成 + ```shell + # 添加或者修改本地分支绑定到的远程分支 + # * 通过指定-u选项或者--set-upstream-to选项 + $ git branch -u remote/branch [local-branch] + or + $ git branch --set-upstream-to remote/branch [local-branch] + ``` + * 查看各个本地分支绑定的远程分支 + * 在git remote show中查看 + ```shell + # 通过git remote show查看 + $ git remote show + ``` + * 通过git branch -vv查看 + ```shell + # 通过git branch -vv查看 + # * git branch -vv并不会连接服务器,而是会从上次连接服务器缓存的 + # 数据中读取 + # * 如果想要获得实时的值,在执行该命令前最好加上git fetch --all + $ git branch -vv + ``` + * git pull和git fetch区别 + * git fetch只是从远程服务端拉取数据,但是并不会自动合并,需要自己手动合并 + * git pull,从服务端拉取数据后会自动合并 + * git删除远程仓库的分支 + * 可以通过git push remote --delete branch来删除远程的分支 + ```shell + # 删除位于远程服务端的分支 + $ git push --delete + ``` +* ## Git Rebasing + * 和merge操作不同,对于rebase操作,如果想把A快照基于B快照rebase,此时会找到A和B的最近公共祖先快照,并且把快照A基于祖先快照的修改记录下来,并且将该修改基于快照B再进行执行产生一个新的快照C,并且将C的parent设置为B。之后会将指向快照B的指针通过fast-forward机制修改指向快照C。 + * 相对于merge操作,rebase操作产生的结果和merge操作合并后快照的结果完全相同。但是,相对于merge操作,rebase操作所产生的git log是一条直线,其 历史会更加的简洁。 + * git rebase操作: + ```shell + # git rebase + # 其会找到当前分支和目标分支的公共祖先快照,并且将当前分支的快照 + # 相对于公共祖先的快照变化基于target分支的最后提交replay + # 然后将当前分支的节点指向到产生的新快照 + $ git rebase + ``` + * 在调用git rebase时,和git merge一样,可能会出现冲突的问题。故而,在出现冲突后,需要手动解决冲突,并且调用git rebase --continue命令。 + * git rebase复杂操作 + ```shell + # 调用git rebase,可以将一个分支基于另一个分支的改变replay到第三分支上 + $ git rebase --onto target-branch base-branch topic-branch \ No newline at end of file