git 中的非破坏性操作

什么是非破坏性

如果你学习过 python 编程语言,你可能会知道,python 为列表内置了一个排序方法。

lst = [3, 1, 2]
lst.sort()

这个例子中,list.sort() 方法可以将列表内的元素按照从小到大的顺序排序。但是,跟其它大多数操作不同,list.sort() 没有返回值,或者返回的是 None。它将排序的结果直接保存在 lst 中。

出于性能上的考虑,list.sort() 直接修改要排序的列表。在计算机语言里,这种方法叫做破坏性方法。在使用破坏性方法前,我们需要提醒自己,以免人为引入 bug。

Git 中的破坏性操作与此类似。在 git 中,我们自己的文件组成了工作区(Workspace),git 为跟踪文件版本,维护索引(Index)和提交历史。破坏性操作指的是会影响工作区内容的操作。

意义在哪里

为什么需要区分破坏性操作和非破坏性操作呢?

这种操作主要应对对线上做重大修改的场景。在我们自己的机器上,怎么改都不是很重要。但是,当涉及到线上操作,就不能有一点差错,否则,后果将不堪设想。这时,我们就需要确切知道,哪些操作执行后会修改线上的文件,以免千万不必要的损失。

后面我们会找一个实例来更深入地理解一下。

哪些是非破坏性操作

首先,查看类的操作是非破坏性的,比如查看状态(git status)、查看某个对象(git cat-file -p object)、查看提交历史(git log)。

其次,只修改 git 文件的操作也是非破坏性的。包括修改索引和修改提交历史的操作。

这里我们重点说一下 reset 操作。git reset 是我们在操作线上时非常重要的一个命令,它可以帮我们重置 HEAD、索引或工作区到某个状态,即可以将 HEAD 指向某个提交、将索引设置为某次提交的索引或修改文件到某次提交。

需要注意一下,git reset –soft 和 git reset –mixed 是非破坏性操作,而 git reset –hard 会修改工作区,属于存坏性操作。

git fetch 也是一个典型的非破坏性操作,可以将远程对象下载并保存到 .git 下。

实战例子

这是根据一次实际的线上迁移抽象出来的一个例子。

假设我们有下面的目录结构:

.
└── new-root
    └── old-root
        ├── .git    <==== 仓库目录
        └── data
            ├── letter.txt
            └── number.txt

其中,old-root 是我们当前的仓库根目录,而我们的目标是将仓库根目录移动到上一层,并保持所有的实体文件不变,以确保不会影响到线上运行。

迁移完后目录结构是下面这个样子:

.
└── new-root
    ├── .git        <==== 新的仓库目录
    └── old-root
        └── data
            ├── letter.txt
            └── number.txt

分析

一个不用大脑思考的办法是,将远程处理完后,在线上重新克隆目录结构。但有两个问题,一是线上服务可能会有短暂的不可用,二是在整个仓库比较大时,克隆成本会很高。

可行的办法是,直接将原来的 .git 升级,并将其移动到上层目录。

使用这个方案,我们需要分三步。

第一步,使用一个临时性仓库将远程目录结构改为新的目录结构,并将该修改 fetch 到线上。

第二步,将 .git 目录移动到上一层。

第三步,将索引和提交历史重置为最新的状态。

实操

# 第一步
git clone old-root/remote.git tmp   # 克隆一个临时仓库
cd tmp
mkdir old-root
mv data old-root
git add .
git commit -m "fix dir structure"
git push origin master:master       # 将新目录结构推送到远程

cd $PROD/new-root/old-root
git fetch                           # 将新结构 fetch 到线上     <====

# 第二步
mv .git ../                         # 将 .git 仓库文件移动到新的仓库根目录  <====

# 第三步
cd ..
git reset origin/master             # 修改索引和提交历史到新结构    <====

到此,我们的目的就已经达到了。

注意上面用 <==== 标出的行,这些都是我们在线上做的操作。仔细查看这几个命令会发现,这几个操作都是非破坏性的,也就是说,它们不会修改线上的实际文件,对我们的服务没有丝毫影响。

你可能感兴趣的:(git)