从头创建分支并从上一次提交提交

时间:2020-01-28 08:51:15

标签: git

对于我的工作,我需要使用GIT管理我的项目。对我来说这是新的,我不知道所有的技巧。

我必须处理以下情况(从最旧到最新):

A
|
B
|
C (head)

但是,我需要在不丢失版本C的情况下更正版本B的错误(当前是非官方的)。我曾想这样做:

A
|
B
| \
D  C

D:(head)

但是,如果我可以创建分支C,则无法“返回”至B并提交文件以创建D。 也许这不是使用GIT的正确方法,但是我该怎么做呢?

谢谢

3 个答案:

答案 0 :(得分:3)

我无法“返回”至B并提交文件以创建D

是的,您可以:

# create a brackup branch to store commit C for later
git branch <name-for-your-store-branch>

# then return to B
git reset --hard B

# make your needed changes here

# then add and commit as usual, for example
git add .
git commit -m "Done this and that"

您最终会得到(这里我假设您的主分支为“ master”)

A---B---D <<< master <<< HEAD
     \
      C <<< your "store" branch

答案 1 :(得分:2)

要在RomainValeri's answer上进行扩展,请记住有关Git的这些事情:

  • Git存储 commits 。任何给定提交的“真实名称”是其哈希ID,即git log在单词“ commit”之后打印的难看的字母和数字大字符串。 (您可以随意缩写它们。)这些 look 是随机的,但实际上根本不是随机的。

  • 每个提交都包含每个文件的完整快照。 1 它还包含指向其 parent 的向后链接。 > commit,这就是我们制作这些A--B--C类型的图时要绘制的内容。我喜欢从左到右为StackOverflow绘制它们,并在其他一些情况下,以较新的提交垂直向顶部(git log --graph绘制它们的方式)。您可以并且应该练习绘制这些东西。

每个提交中的文件都以一种特殊的,只读的,仅Git的,压缩的(有时是高度压缩的)格式存储。 Git本身是唯一可以与它们一起使用的程序。另外,它们是只读的:更改是什么,不是您,甚至不是Git本身。提交后,该提交将一直冻结。如果该提交是错误的-如果需要替换它-您最终将不得不做出一个新的和改进的提交,将旧的错误提交推到一边。也就是说,您可以从以下开始:

A--B--C

,然后确定C不好,然后使用git commit --amend对其进行修复。实际上,这根本不是更改 C,因为它不能。相反,它会将C放在一边,并进行了新的改进的提交:C'C'的父级与C的父级相同:

     C
    /
A--B--C'

旧的提交仍然存在,只是移开了为C'腾出空间的机会。

一旦您掌握了这些内容(以只读方式提交存储文件并链接回较早的提交),您将意识到两件事:

  1. 我们怎么可能完成任何工作?提交内的文件是只读的,一直冻结,并且对所有 Git来说都是完全不可读的。我们需要可以更改的普通文件。

  2. 我们如何找到 正确提交?

第一个问题的答案是,Git为我们提供了进行工作的场所,Git称其为工作树工作树。 (旧版本的Git曾经将其称为工作目录或类似的名称,但是将其与pwd命令打印的工作目录字符串混淆起来太容易了,所以现在它是工作目录-树或工作树。)Git会将提交的文件 out 复制到此工作区。 2

问题2的答案是分支名称的来源。每个分支名称都存储仅一个提交的哈希ID。当我们以这种方式绘制提交时:

A--B--C   <-- branch

或:

C  <-- branch
|
B
|
A

我们正在绘制Git内部真正拥有的东西:一个名称,例如branch指向(包含其哈希ID),例如一个C的提交。同时,提交C本身的哈希ID为-或指向-其父提交B,依此类推。

您可以随时选择一个现有的提交,然后说一个 new 分支名称:使这个新的分支名称指向该现有的提交。使用git branch命令执行此操作。因此,要使用hotfix之类的新名称来指向一些现有的提交,可以运行:

git branch hotfix <hash>

其中<hash>是哈希ID,可能是通过剪切和粘贴git log打印的哈希ID来获得的。现在您有了:

A--B   <-- hotfix
    \
     C   <-- branch

运行git checkout name时,Git使用 name 查找提交。这是工作树(和索引;请参见脚注2)中出现的 commit ,以便您可以进行处理。当您运行git commit时,Git会进行* new提交(从当时索引中的内容开始),该提交将选择具有随机外观的哈希ID。 3

一般来说,这也是建立新分支的方式。只是您从以下开始:

A--B--C   <-- master

,然后命名为develop本身的新名称,例如C

A--B--C   <-- develop, master

我们现在需要知道我们使用的是哪个分支名称,因此在这类图纸中,我们可以将所有大写字母添加为特殊名称HEAD,并将其附加到分支名称,例如:

A--B--C   <-- develop (HEAD), master

如果我们现在进行一次新提交,Git所做的就是将新提交的哈希ID写入附加了HEAD名称中,这样:

A--B--C   <-- master
       \
        D   <-- develop (HEAD)

如果我们使名称hotfix指向提交B,然后执行git checkout hotfix,则得到:

A--B   <-- hotfix (HEAD)
    \
     C   <-- master
      \
       D   <-- develop

提交B在我们的工作区中,以便我们可以更改文件(并在索引中可以进行提交)。我们进行工作,git add个文件(将它们复制回索引中(请参见脚注2),然后git commit,Git进行一次 new 提交,该提交将唯一的大丑陋哈希ID,但我们将其称为E,然后Git将E的哈希ID写入名称hotfix,因为这是附加HEAD的地方:

A--B--E   <-- hotfix (HEAD)
    \
     C   <-- master
      \
       D   <-- develop

请注意,无论我们做什么,我们总是在向存储库中添加新的提交现有的提交不会更改。他们不能!我们只添加 new 个。

分支名称走动!他们总是指向 last 提交(在分支上)。实际上,这就是Git中“分支”的一个定义:这是 last 提交。或者,有时当我们说“一个分支”时,我们指的是名称本身 ,或者有时我们指的是导致并包括最后一次提交的一组提交。 Git中的 branch 一词有点含糊。首先考虑提交是很有帮助的。以后添加分支名称。使用名称​​查找提交;这就是Git所做的。


1 从技术上讲,每个文件仅包含其所包含文件的快照。例如,考虑一下,如果在将一个全新文件放入文件C之后进行提交B会发生什么,而在进行提交B时却不存在。现有的提交C 不能更改,并且没有该文件。因此B和以后的提交将拥有该文件; git add没有文件。每次提交都将具有其所有文件的完整快照。

另一种思考方式是:提交是某些文件集的永久冻结的副本。什么文件副本进入 new 提交?答案存储在Git的索引中;参见脚注2。

2 然后,您会认为Git将在工作区中进行新的提交,但事实并非如此。相反,Git从Git的 index 中进行新的提交。索引,Git也称为暂存区,或者(最近很少是) cache ,是一个很大的话题,我们在这里不会对其进行适当介绍。为了有效地使用Git,理解它很重要,但是请参阅有关它的其他文章或文章。但是,您可以认为它包含每个文件的 third 副本。运行git add时,Git将您正在git checkout的文件的工作树副本复制到索引副本中。运行git commit时,Git从您正在检出的现有提交中将每个文件的冻结副本复制到索引副本中,还复制到工作树中。这不是100%正确的,但足以开始使用。

3 此哈希ID取决于:

  • 整个快照;
  • 父级提交哈希ID;
  • 您的姓名和电子邮件地址等,如提交中所记录;
  • 您的日志消息,记录在提交中;和
  • 甚至是您提交提交的日期和时间。

如果您事先了解所有这些内容,则可以预测提交的哈希ID是什么。但是没有人知道何时进行新提交,直到他们真正提交为止。预先计算哈希值,然后等到准确的秒并运行{{1}}时,这是不值得的。只需进行提交并找出它得到了什么哈希即可。而且,如果再次进行,即使其他条件相同,这次也会得到不同的哈希值,因为它不再是相同的 time

(但是,如果您使用 program 在两个不同的分支名称上非常快速地进行相同的提交,则会导致一种特殊的怪异。由于它们是同时进行的,从相同的东西,使用相同的父对象,它们将获得相同的哈希ID。这不是也不是 bug ,但是这些相同的提交会获得相同的哈希ID,因此两个分支名称指向 same 提交!但这就是我们首先要做的。)

答案 2 :(得分:0)

所以您在分支上有A-B-C,假设dev(所有内容都已提交)。您想在分支A-B-D上拥有dev,但想在分支A-B-C上拥有non_official

非官方分支

# we are on dev (A-B-C with head on C)
git checkout -b non_official

分支开发

# we are on dev (A-B-C with head on C, same as non_official) 
git reset --hard <hash-commit-B>

,或者在这种情况下,因为Bhead之前的提交:

git reset --hard HEAD~1

现在A-B上有dev,您可以添加其他一些提交(D)。