代码之家  ›  专栏  ›  技术社区  ›  Mark Bolusmjak

我可以追溯修正“删除-添加”本应是“移动和更新”?

git
  •  1
  • Mark Bolusmjak  · 技术社区  · 6 年前

    有没有办法追溯修正这个问题?

    我想知道如何创建一个新分支,从现有分支中挑选更改(但尚未提交)。此时,我可以强制git将(delete+adds)视为move+update。这样的事情可能吗?

    1 回复  |  直到 6 年前
        1
  •  1
  •   torek    6 年前

    摆弄策略选项参数进行重命名检测。这取决于你的Git年份 -X find-renames= threshold -X rename-threshold= threshold . 使用 git diff 确定适当的阈值;在 差异比较 这就是 -M --find-renames 参数。

    请记住,cherry picking是作为merge实现的,merge base是cherry picking的commit的父级 --ours 承诺成为 HEAD --theirs 承诺就是你要做的承诺。

    吉特从不 记录

    例如,考虑典型的 Spot the Difference puzzle . 给你两张照片,让你找出不同的地方。如果左边的图片是“before”,右边的图片是“after”,一把椅子不见了,你会说“椅子被拿走了”。如果不同的椅子出现在不同的位置,您可能会说“一把椅子被移除,另一把被添加”。但是如果这两张椅子看起来是一样的呢 一样吗?

    你可以说:椅子A被移除,椅子B被添加,就像你在两张椅子看起来非常不同时所做的那样。或者,你可以说椅子 移动 定位 B!

    不管怎样,Git的快照就像照片一样。它们不包含任何 运动 比较 快照,即使有人是Git本身。你告诉Git: 对我来说,比较快照A和快照B。 Git将文件报告为 移动 如果A中的一个名称丢失了,而B中的另一个名称下出现了完全相同的内容, 你已经告诉Git:“检查东西,看看它们是否也移动了。”

    git diff <commit-L> <commit-R> ,其中使用 --查找重命名 选项。(这里L代表左侧,R代表右侧)如果文件100%相同,Git会找到这样的重命名。但是如果他们不是,如果椅子移动了,但是一路上有一些划痕呢?

    Git会认为“移动的文件”是 相同的 如果文件符合 重命名候选队列 .

    最好的

    找到最佳配对后,这两个文件将从重命名候选队列中删除。这两个文件现在 ,或者在我们的椅子类比中,在左边和右边的图片中是“同一把椅子”,只是移动了一下,可能在这个过程中刮了一点。

    我称之为确定文件身份的过程。 从哲学上讲,这是Git对 the Ship of Theseus ,或者更非正式地说 Grandfather's Axe paradox . "这是我祖父的斧头。我父亲换了把手,我换了头,但还是那把斧头!”两个文件是 相同的 一旦他们被确认为这样的人,就立即归档。

    为了提高速度,Git默认将提交L和R中的任意两个文件配对为“相同”,如果它们具有完全相同的名称。与 差异比较 ,您可以选择 打破 这种配对,以防出现错误;这会将更多的文件名放入重命名检测队列,使其花费更长的时间。

    那是关于 git merge ? (为什么 合并分支 当我摘樱桃的时候!)

    我们一会儿就知道原因了,但是我们来谈谈 现在。当我们使用Git时,我们使用 工作偏离的点。 这一点是关键 合并基 ,而且由于Git完全是关于提交的,这相当于找到 共同承诺

    当我们把它画成一幅犯罪的图画时,这一切都很有意义。每次提交都会记住它的 CommitCommits在这个特定commits之前的commits,我们可以从左到右绘制commits,较旧的commits在左边,较新的commits在右边,如下所示:

    ...  <-o  <-o  <-o  ...
    

    假设Alice和Bob都从一个公共源库开始 git clone 在同一个Git存储库中,例如,它们有一系列以 最近 提交 master :

    ...--F--G--H   <-- master
    

    包含某个提交的实际哈希ID H .

    现在,爱丽丝做了一些工作,做出了一两个新的承诺。她的提交将获得新的、唯一的哈希ID,其他任何人都不会在其他任何地方使用它:

                 I--J   <-- master (Alice's)
                /
    ...--F--G--H   <-- origin/master
    

    同时,Bob做了一些工作并进行了一两次新的提交,他的提交得到了新的、唯一的哈希ID,其他任何人都不会在其他任何地方使用:

                 I--J   <-- [Alice's master]
                /
    ...--F--G--H   <-- origin/master
                \
                 K--L   <-- master (Bob's)
    

    一旦我们得到 全部的 将提交到一个公共存储库中,我们有两个 公共启动提交 ,原版 主人 :

                 I--J   <-- alice/master
                /
    ...--F--G--H
                \
                 K--L   <-- bob/master
    

    我们可以这样做,无论我们是爱丽丝,鲍勃,或一些第三人称卡罗尔,只要我们有 姓名 这是我用的 alice/master bob/master J L

    很明显,爱丽丝和鲍勃都是从“承诺”开始的 小时 ,所以现在很容易看到 Git将Alice的工作与Bob的工作合并:Git只需要 犯罪 小时 反对 看看爱丽丝做了什么,比较一下 小时 看看鲍勃做了什么。所以Git做到了:

    git diff --find-renames <hash-of-H> <hash-of-J>   # what Alice changed
    git diff --find-renames <hash-of-H> <hash-of-L>   # what Bob changed
    

    注意 --查找重命名 选项,它使用默认的“50%相似”度量来定位在Alice或Bob工作时重命名的任何文件。(值得思考的是:为什么Git不需要查看 中间的 提交?这一点尤其重要,因为在某些情况下,它可能有助于重命名检测。不过,Git不会这么做。)

    不管怎样,吉特 联合收割机 那是在我们现在的委员会之后,这两个分支中的谁 附属于它。 1

    当你跑的时候 ,你可以给Git一个 -X rename-threshold 争论,正如你所能给予的 这样的论点。Merge只是将相同的数字传递给diff,以控制重命名检测器在确定文件标识时的严格程度或宽松程度。


    1 在这方面,我们也在增加 鲍勃/主人 重命名冲突: 如果两个都是爱丽丝 Bob重命名了某个特定的文件,Git应该使用哪个名称?它将使用文件中的任何名称 默认情况下,提交。在更典型的合并冲突情况下,它还影响工作树文件的标记方式。


    樱桃采摘(终于!)

    当你使用 git cherry-pick ,Git认为这是一种有趣的合并。让我们再次绘制一些提交链,看看它是如何工作的:

    ...--o--*--o--P--C--o--o   <-- branch-X
             \
              o--o--L   <-- branch-Y (HEAD)
    

    名字 这是附在 branch-Y --我们的 C 上面是我们想要挑选的樱桃(C代表樱桃),和 P 是它的父对象。(我知道 可以代表Pick,但我需要一个字母代表Parent,所以P代表Parent,C代表Cherry。)大多数其他提交都是无趣的,我们从不需要它们的散列ID,所以我们只将它们显示为 o . 我标记了一个 * 因为这是明显的合并基础,但实际上Git也不会使用它!

    Git现在要做的是运行一个合并,就像我们运行 合并分支 ,除了 合并基,它将是commit * 我只是 使用

    git diff --find-renames <hash-of-P> <hash-of-L>
    

    看什么 我们 changedGit将尝试保留这些更改!然后:

    git diff --find-renames <hash-of-P> <hash-of-C>
    

    他们

    Git现在将合并这些更改,就像任何合并时一样,可能会发生合并冲突。正如你现在所看到的 --查找重命名 视情况而定 在提交中存储的文件的值 , C级 . 吉特 检测之间的重命名 为了将特定文件识别为 相同的 文件,否则它将不知道如何组合对该文件的更改。