Git中与版本号相对应的是什么?

262

我们在工作中使用SVN,但是对于我的个人项目,我决定使用Git。所以昨天我安装了Git,并想知道在Git中等同于修订号的内容是什么。

假设我们正在处理版本3.0.8,每个漏洞修复都有自己的修订号,我们在谈论此漏洞修复时可以使用这个修订号。那么,如果我在Git中将代码标记为3.0.8,那么我可以使用哪个修订号或其他更详细的标识?我认为哈希值对于人类来说不太友好。


可能是如何获取git提交计数?的重复问题。 - Laurence Gonsalves
有一篇文章介绍了其中一种可能的解决方案。尽管看起来有点繁琐,但可以融入到“git log”输出中,而且除了“git push / pull / fetch”之外不需要任何其他命令。 - Dmitry Pavlenko
不幸的是,来自@DmitryPavlenko的链接文章在一个失效的域名上:gitsvn.x10.mx。由于没有主题的指示,任何人如果要在其他地方找到它将会很困难。 - Michael Donohue
注意:使用git describe时不再需要更多的缩写,请参见https://dev59.com/rGw15IYBdhLWcg3wqNuM#41308073。 - VonC
3
不,Laurence Gonsalves,这不是“如何获取git提交次数”的重复 - 这是关于版本号的,即使提交次数可以被欺骗看起来有点相似 - 散列版本与提交次数非常不同 :-) - David Svarrer
Git和SVN之间的差异是如此深刻,以至于试图用SVN术语理解Git基本上是行不通的。更容易的方法是假装你对版本控制一无所知,从头开始阅读Git书籍,并且要花时间。Git几乎令人惊叹。 - Lutz Prechelt
19个回答

201

使用现代Git(例如我的版本为1.8.3.4),并且不使用分支,您可以执行以下操作:

$ git rev-list --count HEAD
68

但这种方法存在各种问题,可能不容易复制或在需要时回到提交哈希值。因此,请尽量避免使用它或仅将其用作提示。


25
考虑使用 git rev-list --count --first-parent HEAD - Flimm
9
根据这个信息(数字68),是否有办法确定重新获取代码所需的修订版本?假设“修订版本68”已发布到测试环境,开发继续进行,稍后开发人员需要从源代码控制重新获取“修订版本68”。他如何定位特定版本进行克隆?还是我对Git的理解有误,使这一步不必要? - David
10
请记住,对于不同的开发人员,此解决方案将产生不同的结果。而且,从“count”向后计算到提交会为不同的开发人员产生不同的提交记录!这是因为Git具有分布式特性,在同一时间段内在不同的存储库中发生提交,但根据最后一次推送和拉取的时间,可能不会在 git rev-list --count HEAD 中计入。 - Jason
3
@Jason,David关于添加 --first-parent 的评论是否解决了你的问题?如果有一种同样简单的解决方法或者能够使其更加健壮的方法,我不想因为一种罕见的边缘情况而避开这个看似最简单的解决方案。 - MarkHu
4
@MarkHu,“--first-parent”有帮助。只要不进行变基操作并且始终使用同一分支进行发布(当然,还需要计算此版本号),我认为可以使用这个方法。虽然我仍然不确定它是否总是能唯一地标识出发行版所在的提交。这里有很多事情可能会出错...但目前我想不到任何肯定会破坏这种方式的情况(根据我上面提到的“只要”的语句)。在另一个回答中提到的“git describe”方法是正确的方法。如果你想要一些人类可读的东西,就创建一个标签。 - Jason
显示剩余7条评论

159

对你来说是好消息还是坏消息,那个哈希码就是版本号。当我从SVN转到Git时,我也遇到了这个问题。

你可以在Git中使用“标签”将某个修订版本标记为特定版本的“发布”,使其易于引用该修订版本。请查看此博客文章

关键是要理解Git不能有修订版本号——考虑去中心化的性质。如果用户A和B都提交到他们各自的本地仓库,Git如何合理地分配顺序修订版本号呢?A在推送/拉取彼此的更改之前不知道B的任何信息。

另一个要看的东西是简化缺陷修复分支的分支:

从发布版本3.0.8开始。然后,在该发布版之后,执行以下操作:

git branch bugfixes308

这将创建一个用于修复错误的分支。检出该分支:

git checkout bugfixes308

现在进行您想要的任何错误修复更改。

git commit -a

提交它们,然后切换回主分支:

git checkout master

然后从另一个分支拉取这些更改:

git merge bugfixes308

这样做,你就有了一个单独的发布特定的 bug 修复分支,但仍然将 bug 修复更改合并到主要的开发主干中。


11
我知道哈希是修订号,但我希望它不是 :-))) 非常感谢您清楚的解释以及如何处理它的建议。 - Radek
3
不用谢。当我第一次从SVN转向使用git时,我感到非常沮丧,但现在我已经转而喜欢它了... - makdad
4
另外,我已经有一段时间没有发布这篇文章了,但请记住,您也可以使用“git checkout -b new_branch_name”来执行“git branch foo”和“git checkout foo”作为一行命令。 - makdad
1
@Olie 是的,你说得对。你可以使用 git log 来做到这一点。 - Cole Tobin
3
在SVN中,如果你使用分支,一个bug在r42版本已被修复并不能告诉你它是否也在r43版本中被修复。 - Matthieu Moy
显示剩余5条评论

109

git describe命令可以创建一个稍微更易读的名称,用于指向特定的提交。例如,根据文档:

使用类似 git.git 当前树的东西,我会得到:

[torvalds@g5 git]$ git describe parent
v1.0.4-14-g2414721

例如,我的“父”分支当前的头部基于v1.0.4,但因为它在其之上有几个提交,所以 describe 在末尾添加了附加提交数(“14”)和缩写的提交对象名称(“2414721”)。

只要您使用合理命名的标签来标记特定版本,这可以被视为大致等同于 SVN 的“修订号”。


9
请注意,这种方法仅在您的 git 代码库中已经有标签的情况下才适用;如果没有,则可能会出现 git describe fails with "fatal: No names found, cannot describe anything." - Stack Overflow 的错误提示,这意味着您需要自己设置标签。 - sdaau
14
如果您在脚本或其他程序中使用此命令并希望git describe永远不会失败,请使用git describe --always选项。 - Greg Hewgill
git describe 的输出能否用于找到源提交?我的意思是,除了手动计算日志中的提交次数之外。 - Lii
@Lii:您所说的“源代码提交”是什么意思?最近的标签(v1.0.4)和最新的提交ID(2414721)都已经包含在git describe输出中。 - Greg Hewgill
@GregHewgill:是的,谢谢。当我提出这个问题时,我没有意识到“缩写对象名称”是一个可以用来标识提交的值。太棒了! - Lii

68
其他海报都是正确的,没有“修订号(revision-number)”。
我认为最好的方法是使用"发布标签(tags)"来代替!
但是,我利用以下内容来伪造修订号(只是为了让客户看到修订版本和进度,因为他们希望能够像在Subversion中一样使用递增的git修订版本)。
显示"HEAD"的当前修订版本是通过使用以下内容模拟的: git rev-list HEAD | wc -l 但是,如果客户告诉我在"修订版(revision)" 1302中存在一个错误呢?
对此,我将以下内容添加到我的~/.gitconfig文件的[alias]部分中: show-rev-number = !sh -c 'git rev-list --reverse HEAD | nl | awk \"{ if(\\$1 == "$0") { print \\$2 }}\"' 然后使用git show-rev-number 1302将打印出该"修订版(revision)"的哈希值 :)
我之前写了一篇关于这个"技巧"的博客文章(德语)

1
我喜欢你的解决方案。请注意,你可以简化它:show-rev-number = !sh -c 'git rev-list --reverse HEAD | awk NR==$0' - avner
3
我需要使用 git rev-list --reverse HEAD | awk "{ print NR }" | tail -n 1 - Gerry
“--topo-order” 选项适用于使用分支的环境:使用 “show-rev-number = !sh -c 'git rev-list --topo-order --reverse HEAD | awk "NR==$1 {print}"' -” 命令从提交号转到 git 哈希。话虽如此,我仍然不确定在使用分支的环境中是否可靠地从提交号转到哈希。您可以通过反复查找 <hash> ,使 git rev-list <hash> | wc -l 显示确切的提交号来确保。 - John McGehee
好的,我有点晚加入这个讨论。我对这个解决方案印象深刻,特别是它允许从“构建号”获取哈希值。这可靠吗?如果我采用数字(Rev-list计数),是否始终会与相同的哈希值相关联,或者在git中可能发生更改以更改相关性?谢谢。 - Basya
Linux的使用方式... Sed可以大大缩短这个命令:snr = !sh -c'git rev-list --reverse HEAD | sed -n“$0p”' - MoonCactus
显示剩余5条评论

28

Git没有像Subversion那样的版本号概念。相反,每个通过提交所做的给定快照都被标记为SHA1校验和。为什么?在分布式版本控制系统中,使用运行中的版本号存在几个问题:

首先,由于开发不是线性的,因此给一个数字命名很难解决,以一种满足程序员需求的方式。尝试通过添加数字来解决这个问题时,当数字的行为与您预期的不同时,可能会很快出现问题。

其次,版本号可能在不同机器上生成。这使得数字同步变得更加困难-特别是因为连通性是单向的;您甚至可能无法访问具有存储库的所有机器。

第三,在Git中,某种程度上是由现已不再支持的OpenCM系统首创的,提交的标识(即提交的内容)等同于其名称(SHA ID)。这种命名=标识概念非常强大。当你手持一个提交名称时,它也以无法伪造的方式标识了提交。这反过来让您使用git fsck 命令检查从第一个初始提交开始的所有提交是否存在损坏

现在,由于我们有一个修订版本的DAG(有向无环图),它们构成了当前的树,所以我们需要一些工具来解决您的问题:如何区分不同版本。首先,如果给定前缀,比如1516bd唯一地标识您的提交,则可以省略哈希的一部分。但这也相当牵强。相反,诀窍是使用标记和/或分支。标记或分支类似于您附加到给定提交SHA1 ID的“黄色便签”。标记本质上是不动的,而分支将在其HEAD添加新提交时移动。有多种方法可以引用标记或分支周围的提交,请参阅git-rev-parse的手册页面。

通常,如果你需要处理特定的代码片段,那么这部分代码正在经历变化,并且应该作为一个具有说明性主题名称的分支。创建大量的分支(每个程序员通常会有20-30个分支,其中一些有4-5个可以供其他人使用)是使用Git进行有效工作的技巧。每个工作部分都应该从自己的分支开始,然后在测试后合并。未发布的分支可以被完全重写,这种销毁历史记录的方式是Git的一种功能。
当更改被接受并合并到主分支后,它就会被冻结下来,并成为历史遗迹。此时,你可以对其进行标记,但更常见的做法是通过SHA1校验和在漏洞跟踪器或问题跟踪器中引用特定的提交。标签往往是用于版本更新和维护旧版的分支点。

20
如果你感兴趣的话,我可以从git信息中自动管理版本号,具体见这里,格式如下:
<major>.<minor>.<patch>-b<build>

其中build是提交的总数。您可以在Makefile中看到有趣的代码。以下是访问版本号不同部分的相关部分:

LAST_TAG_COMMIT = $(shell git rev-list --tags --max-count=1)
LAST_TAG = $(shell git describe --tags $(LAST_TAG_COMMIT) )
TAG_PREFIX = "latex-tutorial-v"

VERSION  = $(shell head VERSION)
# OR try to guess directly from the last git tag
#VERSION    = $(shell  git describe --tags $(LAST_TAG_COMMIT) | sed "s/^$(TAG_PREFIX)//")
MAJOR      = $(shell echo $(VERSION) | sed "s/^\([0-9]*\).*/\1/")
MINOR      = $(shell echo $(VERSION) | sed "s/[0-9]*\.\([0-9]*\).*/\1/")
PATCH      = $(shell echo $(VERSION) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/")
# total number of commits       
BUILD      = $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g")

#REVISION   = $(shell git rev-list $(LAST_TAG).. --count)
#ROOTDIR    = $(shell git rev-parse --show-toplevel)
NEXT_MAJOR_VERSION = $(shell expr $(MAJOR) + 1).0.0-b$(BUILD)
NEXT_MINOR_VERSION = $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD)
NEXT_PATCH_VERSION = $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD)

10
一个 Bash 函数:
git_rev ()
{
    d=`date +%Y%m%d`
    c=`git rev-list --full-history --all --abbrev-commit | wc -l | sed -e 's/^ *//'`
    h=`git rev-list --full-history --all --abbrev-commit | head -1`
    echo ${c}:${h}:${d}
}

输出类似于

$ git_rev
2:0f8e14e:20130220

那就是

commit_count:last_abbrev_commit:date_YYmmdd

那种东西可能很有用,但如果有人对增量版本号感兴趣,他们会交换字段位置,使得(非增量)哈希值位于最后。 - MarkHu

9

9
很不幸,它与版本号有很大的区别。它相当长,而且不是单调递增的。猜想这就是分发的代价... - CodesInChaos
1
@CodeInChaos:我知道你的意思,但通常只需要使用哈希码的前6或8个字符就可以引用git提交。 - Chris Pitman
5
@Radek:它们不保证独特性(尽管如果你找到碰撞,你可以获得一些小的名望)。 - Slartibartfast
8
根据维基百科上的碰撞攻击页面,使用哈希函数的前4个字符可以生成一个16位的ID。这意味着在具有256(=2^(16/2))个提交的仓库中,存在50%的可能性出现两个提交具有相同的四个字符前缀(并且当有65536个提交时,由于4字符ID的范围已经用尽,这是确定的)。当你增加一个字符时,你将得到一个20位的ID,这意味着50%的阈值在1024个提交处。但由于这是一个统计参数,没有任何保证这样的冲突不会更早发生。 - Rudi
2
由于哈希是基于内容的,因此两个提交是否具有相同的哈希前缀仍然是一种概率,而不是确定性。在65536个提交中,很可能会有两个具有相同的四个字符前缀,但仍然不确定。顺便说一句,完整的哈希尚未发生冲突,但git正在处理它 :) https://dev59.com/AXA75IYBdhLWcg3wGVCI#wp-dEYcBWogLw_1bFy6- - goodeye
显示剩余2条评论

7

这是我根据他人的解决方案在我的makefile中所做的。请注意,这不仅为您的代码提供了修订号,还附加了哈希值,使您能够重新创建发布版本。

# Set the source control revision similar to subversion to use in 'c'
# files as a define.
# You must build in the master branch otherwise the build branch will
# be prepended to the revision and/or "dirty" appended. This is to
# clearly ID developer builds.
REPO_REVISION_:=$(shell git rev-list HEAD --count)
BUILD_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD)
BUILD_REV_ID:=$(shell git rev-parse HEAD)
BUILD_REV_ID_SHORT:=$(shell git describe --long --tags --dirty --always)
ifeq ($(BUILD_BRANCH), master)
REPO_REVISION:=$(REPO_REVISION_)_g$(BUILD_REV_ID_SHORT)
else
REPO_REVISION:=$(BUILD_BRANCH)_$(REPO_REVISION_)_r$(BUILD_REV_ID_SHORT)
endif
export REPO_REVISION
export BUILD_BRANCH
export BUILD_REV_ID

3
使用git describe --always --dirty --long --tags似乎是避免错误的最安全的方法,这适用于我能想到的所有情况。 - MarkHu

6
我编写了一些用于从Git中检索版本信息和简化标签的PowerShell实用程序。
函数:Get-LastVersion,Get-Revision,Get-NextMajorVersion,Get-NextMinorVersion,TagNextMajorVersion,TagNextMinorVersion:
# Returns the last version by analysing existing tags,
# assumes an initial tag is present, and
# assumes tags are named v{major}.{minor}.[{revision}]
#
function Get-LastVersion(){
  $lastTagCommit = git rev-list --tags --max-count=1
  $lastTag = git describe --tags $lastTagCommit
  $tagPrefix = "v"
  $versionString = $lastTag -replace "$tagPrefix", ""
  Write-Host -NoNewline "last tagged commit "
  Write-Host -NoNewline -ForegroundColor "yellow" $lastTag
  Write-Host -NoNewline " revision "
  Write-Host -ForegroundColor "yellow" "$lastTagCommit"
  [reflection.assembly]::LoadWithPartialName("System.Version")

  $version = New-Object System.Version($versionString)
  return $version;
}

# Returns current revision by counting the number of commits to HEAD
function Get-Revision(){
   $lastTagCommit = git rev-list HEAD
   $revs  = git rev-list $lastTagCommit |  Measure-Object -Line
   return $revs.Lines
}

# Returns the next major version {major}.{minor}.{revision}
function Get-NextMajorVersion(){
    $version = Get-LastVersion;
    [reflection.assembly]::LoadWithPartialName("System.Version")
    [int] $major = $version.Major+1;
    $rev = Get-Revision
    $nextMajor = New-Object System.Version($major, 0, $rev);
    return $nextMajor;
}

# Returns the next minor version {major}.{minor}.{revision}
function Get-NextMinorVersion(){
    $version = Get-LastVersion;
    [reflection.assembly]::LoadWithPartialName("System.Version")
    [int] $minor = $version.Minor+1;
    $rev = Get-Revision
    $next = New-Object System.Version($version.Major, $minor, $rev);
    return $next;
}

# Creates a tag with the next minor version
function TagNextMinorVersion($tagMessage){
    $version = Get-NextMinorVersion;
    $tagName = "v{0}" -f "$version".Trim();
    Write-Host -NoNewline "Tagging next minor version to ";
    Write-Host -ForegroundColor DarkYellow "$tagName";
    git tag -a $tagName -m $tagMessage
}

# Creates a tag with the next major version (minor version starts again at 0)
function TagNextMajorVersion($tagMessage){
    $version = Get-NextMajorVersion;
    $tagName = "v{0}" -f "$version".Trim();
    Write-Host -NoNewline "Tagging next majo version to ";
    Write-Host -ForegroundColor DarkYellow "$tagName";
    git tag -a $tagName -m $tagMessage
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接