简体   繁体   English

Git:签出到标签后2次提交

[英]Git: checkout to 2 commit after a tag

Knowing the following scheme : 知道以下方案: 在此处输入图片说明

Yellow line is Master branch. 黄线是Master分支。 Red line is Develop branch. 红线是Develop分支。

I am on Master on tag 6.2.1. 我在标签6.2.1的Master上。 My need would be to get automatically the sha1 of the commit circled in blue, checking out to this sha1 and create my branch from this sha1. 我需要自动获取以蓝色圈出的提交的sha1,签出到此sha1,然后从此sha1创建我的分支。 This commit is not the last commit of develop. 该提交不是开发的最后一个提交。

The thing to do could consists in : 可能要做的事情包括:

  • getting the sha1 of tag 6.2.1 : git log --oneline | head -1 | cut -d ' ' -f1 得到标签6.2.1的sha1: git log --oneline | head -1 | cut -d ' ' -f1 git log --oneline | head -1 | cut -d ' ' -f1
  • getting the parent of tag 6.2.1 : called commit 'P' 获取标签6.2.1的父级:称为提交'P'
  • getting the following commit of 'P' called commit 'C1' which should be on develop 得到下面的“ P”提交,称为提交“ C1”,应该在开发中
  • getting the following commit of 'C1' called 'C2' also on develop. 在开发中也获得了以下名为“ C2”的“ C1”提交。

Currently I have to manually go on Gitlab, get the related sha1 before checking out and then create my branch. 目前,我必须手动执行Gitlab,在签出之前获取相关的sha1,然后创建我的分支。

There is a way, but there is a potential problem. 有办法,但是有潜在的问题。

A tag name identifies a commit. 标签名称标识提交。

More specifically and precisely, a tag name like v2.1 is the short version of the fully-qualified reference name refs/tags/v2.1 , and any Git reference identifies some Git object. 更确切地说,像v2.1这样的标记名称是完全限定的引用名称refs/tags/v2.1的简短版本,任何Git引用都标识一些Git对象。 All branch names, such as master which is short for refs/heads/master , are constrained to identify only a commit object. 所有分支名称(例如master ,是refs/heads/master缩写)都被约束为标识提交对象。 Tag names usually identify either a commit object—Git call this a lightweight tag —or an annotated tag object, which then goes on to identify the commit. 标记名称通常标识提交对象(Git将此称为轻量级标记)或带注释的标记对象,然后继续标识提交。 In either case, the special gitrevisions syntax name ^{commit} refers to the specific commit, if you need that (usually you don't—Git figures out that, eg, git checkout needs a commit and adds the ^{commit} automatically). 在这两种情况下, 特殊的gitrevisions语法 name ^{commit}指特定的提交(如果您需要的话)(通常不需要)— Git指出,例如git checkout需要一个提交并自动添加^{commit} )。

The problem is, you don't want the commit the tag identifies. 问题是,您不希望标记标识的提交。 You want a nearby commit. 您想要附近的提交。 You said "two commits after " and this is a problem: there's no such thing as a commit after another commit. 您说过“两次提交之后 ”,这是一个问题:没有另一次提交之后的提交。 There are only commits before another commit. 其他提交之前只有提交。

Objection, your honor! 异议,您的荣幸!

How can there not be a commit after a commit if there is a commit before a commit? 如果在提交之前有提交,怎么可能在提交之后没有提交呢? For instance, let's write down four commits in a row: 例如,让我们连续写下四个提交:

A <- B <- C <- D

In Git, commit D stores the ID of its parent commit, which is commit C . 在Git中,提交D存储其父提交(即提交C的ID。 So given D it's very easy to step back to C . 因此,给定D ,很容易退回到C Likewise, C store's B 's ID, and B stores A 's. 同样, C存储B的ID, B存储A的ID。 (If A is the very first commit ever, there is no parent: you cannot step back, and A is a root commit . A root commit is one that has no parent.) (如果A是有史以来的第一个提交,则没有父提交:您不能后退,而A根提交 。根提交是没有父提交的。)

The problem here is that there can be more than one commit before a commit, or after a commit, for that matter. 这里的问题是,就此而言,在提交之前或之后可能有多个提交。 Instead of just the above, consider this graph fragment: 不仅仅是上述内容,请考虑以下图形片段:

A--B--C--D---H   <-- master
    \       /
     E--F--G     <-- feature

Commit H , which is the tip of branch master , is a merge commit. 提交H是分支master尖端 ,它是合并提交。 It has two parents: D and G . 它有两个父母: DG When we examine commit H we see something like this (with different numbers): 当我们检查提交H我们看到这样的东西(具有不同的数字):

$ git show master
commit 3e5c63943d35be1804d302c0393affc4916c3dc3
Merge: c13c783 20690b2

This means it has two parents, c13c783... and 20690b2... . 这意味着它有两个父母, c13c783...20690b2... Which one is the commit before commit H ? 哪一个提交前提交H

So the answer to the objection is that there isn't a commit before a commit, there are possibly- many (often 1, sometimes 2, rarer but occasionally zero, or three-or-more) commits before another commit. 因此,对此异议的答案是,在提交之前没有提交,在另一个提交之前可能有很多 (通常是1,有时是2,稀有,但有时是零,或三或更多)提交。 Likewise, there may be none, or two or more, commits after another commit. 同样, 另一个提交之后可能没有任何提交,或者有两个或多个提交。

To find the commit(s) you want , you need more information. 要查找的commit()你 ,你需要更多的信息。

More information 更多信息

In general, moving backwards is easy. 通常,向后移动很容易。 The first (and often the only ) parent of a commit is usually the interesting one, as it's the commit that was on the branch before that commit got added to that branch. 提交的第一个 (通常是唯一的 )父级通常是有趣的一个,因为是该提交被添加该分支之前在该分支上的提交。 That is, while commit H has both D and G as parents, if master used to point to D and now points to H , it's H 's first parent we want. 也就是说,尽管提交H具有D G作为父母,但如果master过去一直指向D并现在指向H ,则它是我们想要的H的第一个父母。

Gitrevisions has a quick and easy syntax for following first-parents: you simply add a tilde ~ character and a count of how many first-parents to step back. Gitrevisions具有用于跟随初生父母的快速简便的语法:您只需添加波浪号~字符和后退多少初生父母的计数即可。 So to go two steps back from v2.1 , following only first-parents, just write v2.1~2 : 所以要从v2.1 退后两步,只跟随第一个父母,只需编写v2.1~2

git checkout -b newbranch v2.1~2

and you're done. 到此为止。

Going forward is harder. 前进比较困难。 Git only stores backwards links, so what you need to do is provide a name-or-identity of a later commit, from which Git can work backwards to reach your known (tagged) commit. Git仅存储向后链接,因此您需要做的是提供以后提交的名称或身份,Git可以从中向后工作以达到您的已知(带标签)提交。 For instance, if we have a tag pointing to commit C and we want to move forward two steps (to D and then to H ), we would give Git two bits of information: 例如,如果我们有一个指向提交C的标签,并且想向前移动两个步骤(到D然后到H ),我们将为Git提供两点信息:

  1. please end at v2.1 请以v2.1结尾
  2. and start from master master开始

and have it enumerate all the commits between those two points, and then pick the one two steps "closer to master " than the final one at v2.1 . 并枚举这两点之间的所有提交,然后选择比v2.1的最后一步“更容易master ”的前两个步骤。 Let's say at this point that there are even more commits on master now, so the drawing looks like this: 假设此时, master上有更多提交,因此图形如下所示:

   tag:v2.1
      |
      v
A--B--C--D---H--I   <-- master
    \       /
     E--F--G     <-- feature

To find H this way, we use git rev-list and yet more gitrevisions syntax . 为了以这种方式找到H ,我们使用git rev-list更多的gitrevisions语法 (By now it should be obvious that learning gitrevisions syntax is critical to using Git .) (现在,很明显, 学习gitrevisions语法对于使用Git至关重要 。)

git rev-list v2.1..master

The two-dot syntax here means " don't include v2.1 itself", so this will list commits starting from master and working back to the commit after v2.1 : 这里的两点语法表示“ 包括v2.1本身”,因此这将列出从master提交到v2.1之后的提交的提交:

0fbc341...   # id of I
a9315c3...   # id of H
9911231...   # id of D

We take the last two of these IDs, throw away the last one of these IDs, and get a9315c3 , which is the ID of H . 我们获取这些ID的最后两个,丢弃这些ID的最后一个 ,得到a9315c3 ,它是H的ID。

But there's still a potential problem 但是仍然存在潜在的问题

What if the graph looks more like this? 如果图形看起来更像这样呢?

   tag:v2.1
      |
      v
A--B--C--D---------H--I   <-- master
          \       /
           E--F--G     <-- feature

We noted earlier that using the ~ notation uses the first parent. 前面我们注意到,使用~表示法使用第一个父代。 We can do that here, using git rev-list --first-parent v2.1..master . 我们可以在这里使用git rev-list --first-parent v2.1..master做到这git rev-list --first-parent v2.1..master That will keep Git from listing GFE , which will make sure we find H instead of E . 这将使Git无法上市GFE ,这将确保我们找到H而不是E

It's a good idea to add --topo-order to the rev-list command as well, because otherwise these commits are listed in commit-date order, and if the date/times were ever screwed up on one of the computers involved in making the repository, the commits may be listed in the wrong order. 最好在rev-list命令中添加--topo-order ,因为否则这些提交将按commit-date顺序列出,并且日期/时间是否曾经被弄错了,在存储库中,提交可能以错误的顺序列出。

There is also the --ancestry-path option, which is useful in some cases (maybe not applicable to your own case): it makes sure that git rev-list lists only commits that are descendants of the left side of the .. operation. 还有--ancestry-path选项,该选项在某些情况下很有用(可能不适用于您自己的情况):它确保git rev-list仅列出..操作左侧后代的提交.. You would use this where the --first-parent is not wanted, eg: 您可以在--first-parent地方使用它,例如:

...--o--*--A--B--C
      \        \  \
       D--E--F--G--H   <-- branch

If we want the commit "three steps" after tagged commit * , that could be C or G . 如果我们希望在标记的commit *之后执行“三步”提交,则可以为CG Using git rev-list tag..branch will print all of A through H (in some order). 使用git rev-list tag..branch将打印所有的AH (以某种顺序)。 Adding --first-parent will cut off either G or C —only one of those two can be the first parent of H —and if you want to look at both you cannot use --first-parent . 添加--first-parent将切断G C这两个中只有一个可以成为H第一父级-并且如果要查看两者,则不能使用--first-parent Adding --ancestry-path will make git rev-list omit DEF ; 添加--ancestry-path将使git rev-list忽略DEF ; the rest will still come out, in some order. 其余的仍然会以某种顺序出现。 Adding --topo-order will guarantee that B comes out second-to-last and A comes out last (remember, Git is working backwards), with C and G in any order before them, and H first. 添加--topo-order可以确保B排在倒数第二位,而A在最后(请记住,Git在向后工作), CG按任何顺序排列,而H位于第一。

That's not a complete solution, but there is no complete solution, if you have a branch-and-merge-y graph like this. 这不是一个完整的解决方案,但是如果您有一个这样的分支并合并图,则没有完整的解决方案。

Anyway, pick some subset of these options. 无论如何,选择这些选项的一些子集。 If you know your graphs are first-parent-able, then, eg: 如果您知道您的图是第一父级的,那么,例如:

git rev-list --topo-order --first-parent tag .. branchname | tail -2 | head -1

will get you the hash you want. 将为您提供所需的哈希值。

(For another slightly subtle trick, add --reverse to reverse the printout order from git rev-list and then use sed -n -e 2p to print the second line. Note, however, that you cannot use git rev-list 's -n flag here, as it counts and stops from the unreversed list, and we don't know how many commits there will be, we want the second-from-bottom, however many that is.) (对于另一个微妙的技巧,请添加--reverse以反转git rev-list的打印输出顺序,然后使用sed -n -e 2p打印第二行。但是请注意,您不能使用git rev-list-n标志在这里,因为它从不可逆的列表开始计数并停止,并且我们不知道将有多少个提交,我们想要从下至上的第二个提交,但实际上是第二个。

Open git bash, go to selected directory then run the following commands. 打开git bash,转到所选目录,然后运行以下命令。 To get SHA1 run command 获取SHA1运行命令

git log branchname --oneline

It will have the sha1 next to the commit message 提交消息旁边将有sha1

在此处输入图片说明

git branch branchname <sha1-of-commit>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM