
为啥要写这篇文章呢?因为 2017-03-28日清 晨 的一次情趣使然的提交干掉了我2个月的项目。。。
为啥能干掉2个月的?因为是个人没事做着玩的所以2个月一直没提交过。
工作环境 是 win10+sourcetree+git+unreal工程
记得那是一个朦朦胧胧的清晨(雾霾),我兴奋的从床上跳了起来(我的项目终于告一段落)。我打开电脑,打开sourcetree提交一下以免夜长梦多。
我整理了文件并且对比了变化。把整理过的文件add了一下,然后成功的commit(都TMD的是错觉 sourcetree bug了根本没add成功)之后我点击了push。。然后sourcetree 提示了一堆异常,其中有一条叫我用终端执行 git reset --hard 理由是我的工作区有垃圾文件,叫我清空下!
我不由自主的考虑了一下,
听sourcetree的,你说执行就执行~
然后我打开了终端执行了那条命令之后突然感觉哪里不对
我打开工作目录发现git清空了我2个月的项目,什么都没有了,真TMD的干净。
然后我去网上搜索如何找回。发现了好多方法,比如git reflog 这样就能看见自己的所有commit
然后在 git reset --hard 后面写上你想要的commit id 就能找到文件
然后我执行完 git reflog后 发现根本没有我最后的那次提交。突然间恍然大悟,刚刚sourcetree 可能bug了导致commit失败了
然后我发现了另一条命令 git fsck --lost-found 执行后会出现一堆文件 在 git/lost-found 文件夹里 不过是这个样子的
网上老哥说了 用 git show 2e43cd56ee4fb08664cd843cd32836b54fbf594a 就能看见内容
原来 2进制的文件看不见
然后我再次发现了新命令 find git/objects -type f | xargs ls -lt | sed 110q q前面是你要打印的行
这个命令已经脱离git了,他是终端的一个 查找命令 就是查找 git/objects 文件夹下的普通文件 按照时间排序后 打印在终端里 sed 110q 是你要打印多少行。
要看懂这个命令就需要了解git底层的工作原理
讲一下git 原理
GIT对象模型
SHA
所有用来表示项目历史信息的文件,是通过一个40个字符的(40-digit)“对象名”来索引的,对象名看起来像这样:
6ff87c4664981e4397625791c8ea3bbb5f2279a3
你会在Git里到处看到这种“40个字符”字符串。每一个“对象名”都是对“对象”内容做SHA1哈希计算得来的,(SHA1是一种密码学的哈希算法)。这样就意味着两个不同内容的对象不可能有相同的“对象名”。
这样做会有几个好处:
Git只要比较对象名,就可以很快的判断两个对象是否相同。
因为在每个仓库(repository)的“对象名”的计算方法都完全一样,如果同样的内容存在两个不同的仓库中,就会存在相同的“对象名”下。
Git还可以通过检查对象内容的SHA1的哈希值和“对象名”是否相同,来判断对象内容是否正确。
对象
每个对象(object) 包括三个部分: 类型 , 大小 和 内容 。大小就是指内容的大小,内容取决于对象的类型,有四种类型的对象:"blob"、"tree"、 "commit" 和"tag"。
“blob” 用来存储文件数据,通常是一个文件。
“tree” 有点像一个目录,它管理一些 “tree” 或是 “blob” (就像文件和子目录)
一个 “commit” 只指向一个"tree",它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交(commits)的指针等等。
一个 “tag” 是来标记某一个提交(commit) 的方法。
几乎所有的Git功能都是使用这四个简单的对象类型来完成的。它就像是在你本机的文件系统之上构建一个小的文件系统。
与SVN的区别
Git与你熟悉的大部分版本控制系统的差别是很大的。也许你熟悉Subversion、CVS、Perforce、Mercurial 等等,他们使用 “增量文件系统” (Delta Storage systems), 就是说它们存储每次提交(commit)之间的差异。Git正好与之相反,它会把你的每次提交的文件的全部内容(snapshot)都会记录下来。这会是在使用Git时的一个很重要的理念。
Blob对象
一个blob通常用来存储文件的内容
你可以使用 git show 命令来查看一个blob对象里的内容。假设我们现在有一个Blob对象的SHA1哈希值,我们可以通过下面的的命令来查看内容:
$ git show 6ff87c4664
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
v22 or v3x or whatever), unless explicitly otherwise stated
一个"blob对象"就是一块二进制数据,它没有指向任何东西或有任何其它属性,甚至连文件名都没有
因为blob对象内容全部都是数据,如两个文件在一个目录树(或是一个版本仓库)中有同样的数据内容,那么它们将会共享同一个blob对象。Blob对象和其所对应的文件所在路径、文件名是否改被更改都完全没有关系。
Tree 对象
一个tree对象有一串(bunch)指向blob对象或是其它tree对象的指针,它一般用来表示内容之间的目录层次关系。
git show 命令还可以用来查看tree对象,但是 git ls-tree 能让你看到更多的细节。如果我们有一个tree对象的SHA1哈希值,我们可以像下面一样来查看它:
$ git ls-tree fb3a8bdd0ce
100644 blob 63c918c667fa005ff12ad89437f2fdc80926e21c gitignore
100644 blob 5529b198e8d14decbe4ad99db3f7fb632de0439d mailmap
100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 COPYING
040000 tree 2fb783e477100ce076f6bf57e4a6f026013dc745 Documentation
100755 blob 3c0032cec592a765692234f1cba47dfdcc3a9200 GIT-VERSION-GEN
100644 blob 289b046a443c0647624607d471289b2c7dcd470b INSTALL
100644 blob 4eb463797adc693dc168b926b6932ff53f17d0b1 Makefile
100644 blob 548142c327a6790ff8821d67c2ee1eff7a656b52 README
就如同你所见,一个tree对象包括一串(list)条目,每一个条目包括:mode、对象类型、SHA1值 和名字(这串条目是按名字排序的)。它用来表示一个目录树的内容。
一个tree对象可以指向(reference): 一个包含文件内容的blob对象, 也可以是其它包含某个子目录内容的其它tree对象 Tree对象、blob对象和其它所有的对象一样,都用其内容的SHA1哈希值来命名的;只有当两个tree对象的内容完全相同(包括其所指向所有子对象)时,它的名字才会一样,反之亦然。这样就能让Git仅仅通过比较两个相关的tree对象的名字是否相同,来快速的判断其内容是否不同。
(注意:在submodules里,trees对象也可以指向commits对象 请参见 Submodules 章节)
注意:所有的文件的mode位都是644 或 755,这意味着Git只关心文件的可执行位
Commit对象
"commit对象"指向一个"tree对象", 并且带有相关的描述信息
你可以用 --pretty=raw 参数来配合 git show 或 git log 去查看某个提交(commit):
$ git show -s --pretty=raw 2be7fcb476
commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4
tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf
parent 257a84d9d02e90447b149af58b271c19405edb6a
author Dave Watson 1187576872 -0400
committer Junio C Hamano 1187591163 -0700
Fix misspelling of 'suppress' in docs
Signed-off-by: Junio C Hamano
你可以看到, 一个提交(commit)由以下的部分组成:
一个 tree 对象: tree对象的SHA1签名, 代表着目录在某一时间点的内容
父对象 (parent(s)): 提交(commit)的SHA1签名代表着当前提交前一步的项目历史 上面的那个例子就只有一个父对象; 合并的提交(merge commits)可能会有不只一个父对象 如果一个提交没有父对象, 那么我们就叫它“根提交"(root commit), 它就代表着项目最初的一个版本(revision) 每个项目必须有至少有一个“根提交"(root commit) 一个项目可能有多个"根提交“,虽然这并不常见(这不是好的作法)
作者 : 做了此次修改的人的名字, 还有修改日期
提交者 (committer): 实际创建提交(commit)的人的名字, 同时也带有提交日期 TA可能会和作者不是同一个人; 例如作者写一个补丁(patch)并把它用邮件发给提交者, 由他来创建提交(commit)
- 注释 用来描述此次提交
注意: 一个提交(commit)本身并没有包括任何信息来说明其做了哪些修改; 所有的修改(changes)都是通过与父提交(parents)的内容比较而得出的 值得一提的是, 尽管git可以检测到文件内容不变而路径改变的情况, 但是它不会去显式(explicitly)的记录文件的更名 *** 作 (你可以看一下 git diff 的 -M 参数的用法)
一般用 git commit 来创建一个提交(commit), 这个提交(commit)的父对象一般是当前分支(current HEAD), 同时把存储在当前索引(index)的内容全部提交
对象模型
现在我们已经了解了3种主要对象类型(blob, tree 和 commit), 好现在就让我们大概了解一下它们怎么组合到一起的
如果我们一个小项目, 有如下的目录结构:
$>tree
|-- README
`-- lib
|-- inc
| `-- tricksrb
`-- mylibrb
2 directories, 3 files
如果我们把它提交(commit)到一个Git仓库中, 在Git中它们也许看起来就如下图:
你可以看到: 每个目录都创建了 tree对象 (包括根目录), 每个文件都创建了一个对应的 blob对象 最后有一个 commit对象 来指向根tree对象(root of trees), 这样我们就可以追踪项目每一项提交内容
标签对象
一个标签对象包括一个对象名(译者注:就是SHA1签名), 对象类型, 标签名, 标签创建人的名字("tagger"), 还有一条可能包含有签名(signature)的消息 你可以用 git cat-file 命令来查看这些信息:
$ git cat-file tag v150
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v150
tagger Junio C Hamano 1171411200 +0000
GIT 150
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v146 (GNU/Linux)
iD8DBQBF0lGqwMbZpPMRm5oRAuRiAJ9ohBLd7s2kqjkKlq1qqC57SbnmzQCdG4ui
nLE/L9aUXdWeTFPron96DLA=
=2E+0
-----END PGP SIGNATURE-----
点击 git tag , 可以了解如何创建和验证标签对象 (注意: git tag 同样也可以用来创建 "轻量级的标签"(lightweight tags), 但它们并不是标签对象, 而只一些以 "refs/tags/" 开头的引用罢了)
然后我从这一堆文件里整理出了 tree文件 并且总结出了规律
运行 find git/objects -tpe f | xargs ls -lt | sed 100q 后会得到最近改动的100个文件
-r--r--r-- 1 qipaworld 197608 57103 3月 25 07:23 git/objects/31/68e9047b751d98f7c280dab676cff4af3cb8f6 文件以这种形式显示在终端里
git cat-file -t 3168e9047b751d98f7c280dab676cff4af3cb8f6 就能看见文件类型 把最后一个/去掉 复制从objects/ 后面的所有东西放在-t后面
然后找到tree类型的文件
每个红框框选的区域里的类型应该是同一种,选一个cat 一下看看就行了
找到tree后 git cat-file -p 4452fd53aa36018b20a6556dc3649f50a4be4561 ^{tree}
就能看见tree里面的结构
然后就能知道 哪个blob对应哪个文件了。是不是很屌?
我搞了一晚上终于找到了某个tree并且跟到了文件目录。然后发现并没有我的项目文件,当时已经是半夜12点多了,我静静的看着屏幕
我连add都没成功就提示我commit成功了
以后谁叫你 reset --hard你就干他,别问我为什么
最终结果就是项目消失了,但是还好unreal 有自动保存功能,而自动保存的文件目录被我加到了git忽略列表里。找回了一部分
最后加一些git常用命令
查看、添加、提交、删除、找回,重置修改文件
git help # 显示command的help
git show # 显示某次提交的内容 git show $id
git co -- # 抛弃工作区修改
git co # 抛弃工作区修改
git add # 将工作文件修改提交到本地暂存区
git add # 将所有修改过的工作文件提交暂存区
git rm # 从版本库中删除文件
git rm --cached # 从版本库中删除文件,但不删除文件
git reset # 从暂存区恢复到工作文件
git reset -- # 从暂存区恢复到工作文件
git reset --hard # 恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改
git ci git ci git ci -a # 将git add, git rm和git ci等 *** 作都合并在一起做git ci -am "some comments"
git ci --amend # 修改最后一次提交记录
git revert <$id> # 恢复某次提交的状态,恢复动作本身也创建次提交对象
git revert HEAD # 恢复最后一次提交的状态
查看文件diff
git diff # 比较当前文件和暂存区文件差异 git diff
git diff # 比较两次提交之间的差异
git diff # 在两个分支之间比较
git diff --staged # 比较暂存区和版本库差异
git diff --cached # 比较暂存区和版本库差异
git diff --stat # 仅仅比较统计信息
查看提交记录
git log git log # 查看该文件每次提交记录
git log -p # 查看每次详细修改内容的diff
git log -p -2 # 查看最近两次详细修改内容的diff
git log --stat #查看提交统计信息
tig
Mac上可以使用tig代替diff和log,brew install tig
Git 本地分支管理
查看、切换、创建和删除分支
git br -r # 查看远程分支
git br # 创建新的分支
git br -v # 查看各个分支最后提交信息
git br --merged # 查看已经被合并到当前分支的分支
git br --no-merged # 查看尚未被合并到当前分支的分支
git co # 切换到某个分支
git co -b # 创建新的分支,并且切换过去
git co -b # 基于branch创建新的new_branch
git co $id # 把某次历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除
git co $id -b # 把某次历史提交记录checkout出来,创建成一个分支
git br -d # 删除某个分支
git br -D # 强制删除某个分支 (未被合并的分支被删除的时候需要强制)
分支合并和rebase
git merge # 将branch分支合并到当前分支
git merge origin/master --no-ff # 不要Fast-Foward合并,这样可以生成merge提交
git rebase master # 将master rebase到branch,相当于: git co && git rebase master && git co master && git merge
Git补丁管理(方便在多台机器上开发同步时用)
git diff > /syncpatch # 生成补丁
git apply /syncpatch # 打补丁
git apply --check /syncpatch #测试补丁能否成功
Git暂存管理
git stash # 暂存
git stash list # 列所有stash
git stash apply # 恢复暂存的内容
git stash drop # 删除暂存区
Git远程分支管理
git pull # 抓取远程仓库所有分支更新并合并到本地
git pull --no-ff # 抓取远程仓库所有分支更新并合并到本地,不要快进合并
git fetch origin # 抓取远程仓库更新
git merge origin/master # 将远程主分支合并到本地当前分支
git co --track origin/branch # 跟踪某个远程分支创建相应的本地分支
git co -b origin/ # 基于远程分支创建本地分支,功能同上
git push # push所有分支
git push origin master # 将本地主分支推到远程主分支
git push -u origin master # 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库)
git push origin # 创建远程分支, origin是远程仓库名
git push origin : # 创建远程分支
git push origin : #先删除本地分支(git br -d ),然后再push删除远程分支
Git远程仓库管理
GitHub
git remote -v # 查看远程服务器地址和仓库名称
git remote show origin # 查看远程服务器仓库状态
git remote add origin git@ github:robbin/robbin_sitegit # 添加远程仓库地址
git remote set-url origin git@ githubcom:robbin/robbin_sitegit # 设置远程仓库地址(用于修改远程仓库地址) git remote rm # 删除远程仓库
创建远程仓库
git clone --bare robbin_site robbin_sitegit # 用带版本的项目创建纯版本仓库
scp -r my_projectgit git@ gitcsdnnet:~ # 将纯仓库上传到服务器上
mkdir robbin_sitegit && cd robbin_sitegit && git --bare init # 在服务器创建纯仓库
git remote add origin git@ githubcom:robbin/robbin_sitegit # 设置远程仓库地址
git push -u origin master # 客户端首次提交
git push -u origin develop # 首次将本地develop分支提交到远程develop分支,并且track
git remote set-head origin master # 设置远程仓库的HEAD指向master分支
也可以命令设置跟踪远程库和本地库
git branch --set-upstream master origin/master
git branch --set-upstream develop origin/develop
点击这里可以看到作者的其他文章
git stash;
就能恢复到修改前的状态,得到修改文件之后,再执行:
git stash pop;
这又恢复了修改之后的状态了。如果你有多个仓库需要管理,可以使用repo forall命令帮助执行。
再说说另外一种方法。有时候修改完代码之后,不想马上就提交,可以另外其一个分支,在这个分支上提交,结束之后再切回原来的分支。一般常用命令如下:
repo start name (注意name之后有一个"",表示当前工程。或者使用git checkout -b name 远程分支名称)
git commit -a
git checkout name (或者在新建一个分支)
这两种方法一般本地团队使用比较好。如果涉及到跨公司跨地域的交流,建议还是用下面的patch文件方式。
个人理解android开发接触到的patch,一般可以分为两种,一种是UNIX下的patch,可以使用git diff或者其他UNIX的diff命令生成(本人只用过git diff命令,后面的看书有提到过但是自己木有用过),一种是git 工具生成的patch,也可以说是git专用的patch,使用git format-patch命令生成。先来看看git diff生成patch的过程。一般常用命令如下:
git diff > patch
git diff --cached > patch
git diff branchname --cached > patch
这个时候当前目录下就会有一个patch文件,这是一个非git环境也可以使用的patch。对于这种patch,在git上使用要用git apply命令,如下:
git apply patch
由于这是一个类似UNIX下更新文件的 *** 作,所以执行完上述 *** 作之后,实际上是等于手动修改了文件,还要做一些git commit之类的 *** 作。另外请注意,git apply 是一个事务性 *** 作的命令,也就是说,要么所有补丁都打上去,要么全部放弃。ProGit上说明在实际打补丁之前,可以先用git apply --check 查看补丁是否能够干净顺利地应用到当前分支中:git apply --check patch,如果执行完该命令之后没有任何输出,表示我们可以顺利采纳该补丁,接下来就是git上的提交了。
在接着说说git format-patch生成的补丁,这是git专用的,也是日常工作中最常接触到的补丁类型。常用命令如下:
1)两个节点之间的提交: git format-patch 节点A 节点B
2)单个节点: git format-patch -1 节点A (-n就表示要生成几个节点的提交)
3)最近一次提交节点的patch :git format-patch HEAD^ 依次类推……
使用git format-patch命令生成的patch文件,包含了提交的附加信息:比如作者,时间等。再次基础上使用git am命令即可将此补丁应用到当前分支。注意应用完之后,你会发现当前分支多了一次提交记录,并且有完整的信息,而不是简单的修改文件。在对比一下,git diff 和git format-patch生成的patch一个重要不同之处,实际使用中会发现git diff一次只会生成一个patch文件,不管差别了多少个提交,都是一个;而git format-patch是根据提交的节点来的,一个节点一个patch。
补充小技巧: 如果有额外信息需要补充,但又不想放在提交消息中说明以免搞乱了提交说明。可以编辑这些补丁文件,在第一个“---” 行之前添加说明即可。这样的信息能阅读,但在执行完git am命令之后该节点的提交信息不回包含上述信息。
如果你想在一起使用补丁,最简单的方法是使用一个文本编辑器来编辑补丁文件,然后将它复制到你的应用程序的源码目录中。接下来,按照补丁文件中的说明,运行相应的命令来应用补丁。
如果你的应用程序使用版本控制系统,比如Git,你可以使用Git命令来应用补丁,具体步骤如下:
1 将补丁文件复制到你的应用程序的源码目录中。
2 打开终端,进入你的应用程序的源码目录,然后运行“git apply patch-namepatch”,其中patch-namepatch是你的补丁文件的名字,来应用补丁。
3 如果你的补丁文件不能被正确应用,可能是因为你的文件已经被修改过了。如果这是情况,运行“git apply --reject patch-namepatch”,来获取详细的错误报告,以便你可以手动修改文件。
从170版本开始Git提供稀疏检出的功能。所谓稀疏检出就是本地版本库检出时不检出全部,只将指定的文件从本地版本库检出到工作区,而其他未指定的文件则不予检出(即使这些文件存在于工作区,其修改也会被忽略)。
要想实现稀疏检出的功能,必须同时设置 coresparseCheckout 配置变量,并存在文件 git/info/sparse-checkout 。即首先要设置Git配置变量 coresparseCheckout 为 true ,然后编辑 git/info/sparse-checkout 文件,将要检出的目录或文件的路径写入其中。其中文件 git/info/sparse-checkout 的格式就和 gitignore 文件格式一样,路径可以使用通配符。
稀疏检出是如何实现的呢?
实际上Git在index(即暂存区)中为每个文件提供一个名为 skip-worktree 标志位,缺省这个标识位处于关闭状态。如果该标识位开启,则无论工作区对应的文件存在与否,或者是否被修改,Git都认为工作区该文件的版本是最新的、无变化。Git通过配置文件 git/info/sparse-checkout 定义一个要检出的目录和/或文件列表,当前Git的 git read-tree 命令及其他基于合并的命令( git merge , git checkout 等等)能够根据该配置文件更新index中文件的 skip-worktree 标志位,实现版本库文件的稀疏检出。
先来在工作区 /path/to/my/workspace 中创建一个示例版本库sparse1,创建后的sparse1版本库中包含如下内容:
即版本库sparse1中包含三个目录 doc1 、 doc2 和 doc3 。命令 git ls-files 的 -s 参数用于显示对象的SHA1哈希值以及所处的暂存区编号。而 -v 参数则还会显示工作区文件的状态,每一行命令输出的第一个字符即是文件状态:字母 H 表示文件已被暂存,如果是字母 S 则表示该文件 skip-worktree 标志位已开启。
下面我们就来体验一下稀疏检出的功能。
文件 git/info/sparse-checkout 的文件格式类似于 gitignore 的格式,也支持用感叹号实现反向 *** 作。例如不检出目录 doc2 下的文件,而检出其他文件,可以使用下面的语法(注意顺序不能写反):
注意如果使用命令 git checkout – <file> ,即不是切换分支而是用分支中的文件替换暂存区和工作区的话,则忽略 skip-worktree 标志。例如下面的 *** 作中,虽然 doc2 被设置为不检出,但是执行 git checkout 命令后,还是所有的目录都被检出了。
如果修改 doc2 目录下的文件,或者在 doc2 目录下添加新文件,Git会视而不见。
若此时通过取消 coresparseCheckout 配置变量的设置而关闭稀疏检出,也不会改变目录 doc2 下的文件的 skip-worktree 标志。这种情况或者通过 git update-index –no-skip-worktree – <file> 来更改index中对应文件的 skip-worktree 标志,或者重新启用稀疏检出更改相应文件的检出状态。
在克隆一个版本库时只希望检出部分文件或目录,可以在执行克隆 *** 作的时候使用 --no-checkout 或 -n 参数,不进行工作区文件的检出。例如下面的 *** 作从前面示例的sparse1版本库克隆到sparse2中,不进行工作区文件的检出。
检出完成后可以发现sparse2的工作区是空的,而且版本库中也不存在 index 文件。如果执行 git status 命令会看到所有文件都被标识为删除。
如果希望通过稀疏检出的功能,只检出其中一个目录如 doc2 ,可以用如下方法实现:
之后看到工作区中检出了 doc2 目录,而其他文件被设置了 skip-worktree 标志。
上一节介绍的稀疏检出,可以部分检出版本库中的文件,但是版本库本身仍然包含所有的文件和历史。如果只对一个大的版本库的最近的部分历史提交感兴趣,而不想克隆整个版本库,稀疏检出是解决不了的,而是要采用本节介绍的浅克隆。
实现版本库的浅克隆的非常简单,只需要在执行 git clone 或者 git fetch *** 作时用 --depth <depth> 参数设定要获取的历史提交的深度( <depth> 大于0),就会把源版本库分支上最近的 <depth> + 1 个历史提交作为新版本库的全部历史提交。
通过浅克隆方式克隆出来的版本库,每一个提交的SHA1哈希值和源版本库的相同,包括提交的根节点也是如次,但是Git通过特殊的实现,使得浅克隆的根节点提交看起来没有父提交。正因为浅克隆的提交对象的SHA1哈希值和源版本库一致,所以浅克隆版本库可以执行 git fetch 或者 git pull 从源版本库获取新的提交。但是浅克隆版本库也存在着很多限制,如:
由于浅克隆包含上述限制,因此浅克隆一般用于对远程版本库的查看和研究,如果在浅克隆版本库中进行了提交,最好通过 git format-patch 命令导出为补丁文件再应用到远程版本库中。
下面的 *** 作使用 git clone 命令创建一个浅克隆。注意:源版本库如果是本地版本库要使用 file:// 协议,若直接接使用本地路径则不会实现浅克隆。
然后进入到本地克隆目录中,会看到当前分支上只有3个提交。
查看提交的根节点 d81896e ,则会看到该提交实际上也包含父提交。
而查看该提交的父提交,Git会报错。
对于正常的Git版本库来说,如果对象库中一个提交丢失绝对是大问题,版本库不可能被正常使用。而浅克隆之所以看起来一切正常,是因为Git使用了类似嫁接(下一节即将介绍)的技术。
在浅克隆版本库中存在一个文件 git/shallow ,这个文件中罗列了应该被视为提交根节点的提交SHA1哈希值。查看这个文件会看到提交 d81896e 正在其中:
列在 git/shallow 文件中的提交会构建出对应的嫁接提交,使用类似嫁接文件 git/info/grafts (下节讨论)的机制,当Git访问这些对象时就好像这些对象是没有父提交的根节点一样。
以上就是关于git底层原理以及丢失文件找回和坑爹案例(一招干没我2个月的项目)全部的内容,包括:git底层原理以及丢失文件找回和坑爹案例(一招干没我2个月的项目)、git 怎么把command id打成patch、与你在一起mod补丁怎么用等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)