Git撤销与合并

Git撤销与合并,第1张

1. git init

创建一个空的git repo,也就是创建一个.git的子目录,这个目录包含了几乎所有git存储和 *** 作的东西。新初始化的.git目录的典型结构如下:

description文件仅供git web程序使用,平常无需关心。

config文件包含项目特有的配置选项。

info目录包含一个全局性排除文件,用以放置那些不希望被记录在.gitignore文件中的忽略模式。

hooks目录包含客户端或服务端的钩子脚本。

HEAD文件指向目前被检出的分支

index文件(尚待创建)保存暂存区信息。

objects目录存储所有数据内容

refs目录存储指向数据的提交对象的指针。

git的默认分支名字是master,git init时默认创建它。

2. git的三种状态,以及工作区(Working directory),暂存区(Index),HEAD

Git 有三种状态,你的文件可能处于其中之一:已修改(modified)、已暂存(staged)和已提交(committed)

基于刚才init的git project,做一些改动。

会看到在git add之后,.git下面多了一个index文件。

这时候,所做山运蠢的改动就处于已暂存状态,体现在index文件中。

可以利用以下命令查看逗陪git缓存了的内容。

同时,.git/objects下面多了一个子文件夹,并生成了一个新文件。这个新文件就对应了刚才所做的改动。这就是git存储内容的方式--一个文件对应一条内容,以该内容加上特定头部信息一起的SHA-1校验和作为文件名。校验和的前两个字符用于命名子目录,余下的38个字符则作为文件名。后面会详叙。

可以通过cat-file命令从git那里查看存储的内容。悄租

git cat-file -p e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

由于file1.txt的内容为空,所以这里显示为空。

这时候可以往file1.txt里添加一些内容,并git add。可以看到.git/objects又多了一个object。

查看这个新的对象的内容以及类型。会发现它是一个blob对象。

接下来commit这个change。

myProject $ git commit -m "first commit"

查看这个commit 对象的类型以及内容,commit的tree对象所指向的内容, 我们会发现,这个tree指向的是一个blob,而这个blob的内容,就是我们刚刚做过改动的文件。

同时,我们查看一下暂存区的内容:

会发现,暂存区指向的也是同样的blob对象。

至此,一个commit就提交了,工作区,暂存区,以及head又指向了同样的内容。

它们更新内容的顺序为,工作区->暂存区->head

3. git reset

将做过的change撤销掉,就像没有发生过一样。

git reset 应用的顺序为 head->暂存区->工作区。

(1) git reset --soft

当前,git的状态如下。

head指向的内容为:

(head是当前分支引用的指针,总是指向该分支上的最后一次提交。)

index指向的内容为:

(索引是你的预期的下一个提交)

我们来进行一次reset。(移动HEAD, --soft)

--soft将仅仅移动HEAD的指向,而并不会移动index以及工作区。

HEAD 指的是HEAD的父节点。HEAD 是父节点的父节点,也可以写成HEAD 2.

所以这个命令本质上是撤销了上一次git commit命令。

(2) git reset --mixed

接下来,再通过reset来更新索引。(--mixed,默认行为)

(3) git reset --hard

reset更新工作目录(--hard)

git reset --hard HEAD~

--hard标记是reset命令的危险用法,它也是git会真正销毁数据的几个 *** 作之一。

如果这个commit已经被推送到远端,可以用这个命令使远端也回退到相应的版本。

git push origin <branch>--force

4. git revert

将做过的change撤销掉,通过“反做”某一个版本,用一个新的commit来消除做过的change。

当前git的状态:

revert其中一个commit:

再来看,多了一个commit,也就是用来revert的commit:

而若是想要revert某个版本,但是在这个版本后又做过change,则在revert的过程中可能出现冲突,则需要解决冲突之后再提交。

5. git merge 与git rebase

先来讲讲git merge。

当前master 和 dev branch:

接下来打算将dev的工作并入master分支。

另外,还想将master的工作也并入dev。

git merge之后,会发现dev branch指向了与master相同的commit:

所以,git merge是把两个分支的最新快照,以及两者最近的共同祖先进行三方合并,合并的结果是生成一个新的快照。

接下来,用git rebase来合并分支。

当前的git状态

此时,采用git rebase,将dev的工作并入到master。

当在master branch上执行git rebase dev的时候,实际发生的事情是,找到master和dev两个分支的最近共同祖先,对比当前分支(master分支)相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将master分支指向目标基底(dev的head指向的commit),最后以此将之前另存为临时文件的修改依序应用。

可以看到,rebase使得提交历史更加整洁。尽管实际的开发工作是并行在不同branch上进行的,但是它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

因此,变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。这两种方式,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同。

我们知道git 合并文件是以行为单位进行一行一行进行合并的,但是有些时候并不是两行内容不一样git就会报冲突,因为smart git 会帮我们自动帮我们进行取舍,分析出那个结果才是我们所期望的,如果smart git 都无法进行取舍时候才会报冲突,这个时候才需要我们进行人工干预。那git 是如何帮我们进行Smart *** 作的呢?

Mine 代表你本地修改...

Theirs 代表其他人修改...

假设对于同一个文件,出现你和其他人一起修改,此时如果git来进行合并,git就懵逼了,因为Git既不敢得罪你(Mine),也不能得罪他们(Theirs) 无理无据,git只能让你自己搞了,但是这种情况太多了而且也没有必要…

三路合并就是先找出一个基准,然后以基准为Base 进行合并,如果2个文件相对基准(base)都发生了改变 那git 就报冲突,然后让你人工决断。否则,git将取相对于基准(base)变化的那个为最终结果。

下面面的合并就是我们常见的分支graph,结合具体分析:

git merge-base –all commit_id1(Yours/Theirs) commit_id2(Yours/Theirs) 就能找出公共祖先的commitId(Base)

图虽然复杂 但是核心原理是不变的,下面我们看 另外一个稍微高级一点的核心原理”递归三路合并” 也是我们很常见看到 git merge 输出的 recursive strategy

简短描述下 如何会出现上面的图:

从上面我们DAG图可以知道公共祖先有③和④,那到迹中底选择哪个呢,我们分别来看:

最终期待的结果是什么?

所以 我们的最佳公共祖先应该是4,最终结果应该是 /foo.c = C

git 如何选择公共祖先呢?

你可能会说用 git merge-base ⑥ ⑦ 输出的是 ④ 但是git 就真的是用 ④ 做祖先吗 ?答案是No

从git的解释中,我们就知道 如果有2个都是最佳公共祖先时候,这个时候git 会随便输出一个不确定公共祖先。

git 是这样进行合并的:

当项目中包含多条功能分支时,有时就需要使用 git merge 命令,指定将某肆拍个分支的提交合并到当前分支。Git 中有两个合并策略:fast-forward 和 no-fast-forward。

当merge ② 和 ⑥时候 由于②是公共祖先,所以进行Fast-Forward 合并,直接指向⑥ 不用生成一个新的⑧进行merge了。

no-fast-forward(--no-ff)意为非快进式合并,fast-forward的场景很少遇到,基本是:在当前分支分离出子分支后(比如分支dev),后续会有其他分支合并进来的修改,而分离出的dev分支也做了修改。这个时候再使用git merge,就会触发 no-fast-forward 策略了。

看懂下面这个例子你就明白了:

现在 f 提交是我们正在合并姿雹山的提交

如果现在找 e 和 d 的共同祖先,你会发现并不唯一,b 和 c 都是。那么此时怎么合并呢?

git 会首先将 b 和 c 合并成一个虚拟的提交 x,这个 x 当作 e 和 d 的共同祖先。

而要合并 b 和 c,也需要进行同样的 *** 作,即找到一个共同的祖先 a。

我们这里的 a、b、c 只是个比较简单的例子,实际上提交树往往更加复杂,这就需要不断重复以上 *** 作以便找到一个真实存在的共同祖先,而这个 *** 作是递归的。这便是“递归三路合并”的含义。

这是 git 合并时默认采用的策略。

git官方文档

git merge原理

git merge的合并原理


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/12285799.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存