修订号码的Git等价物是什么?

我们在工作中使用SVN,但对于我个人的项目,我决定使用Git。 所以我昨天安装了Git,我想知道Git中的修订版本号多less

假设我们在3.0.8版本上工作,并且在讨论这个错误修复时,每个错误修复都有自己的版本号。 所以如果我把Git中的代码标记到3.0.8然后我可以使用它作为修订号或其他更详细的标识types? 我发现哈希不是那么用户友好的人类。

好消息或坏消息,哈希是修订号。 当我从SVN切换到git时,我也遇到了麻烦。

您可以在git中使用“标记”来标记特定版本的“版本”,从而可以轻松地引用该版本。 看看这个博客文章 。

要理解的关键是git不能有修订号 – 考虑分散性。 如果用户A和B都同时向本地存储库提交,git如何合理地分配一个连续的修订版本号? A在推动/拉扯彼此的变化之前,对B没有了解。

另一个要看的是简化分支为bugfix分支:

从一个版本开始:3.0.8。 然后,释放后,做到这一点:

git branch bugfixes308 

这将创build一个错误修正分支。 结帐分行:

 git checkout bugfixes308 

现在进行任何你想要的bug修改。

 git commit -a 

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

 git checkout master 

然后从另一个分支中引入这些更改:

 git merge bugfixes308 

这样,你有一个单独的版本特定的错误修复分支,但你仍然把错误修改更改到你的主开发中心。

用现代Git(1.8.3.4在我的情况下)而不使用分支你可以做:

 $ git rev-list --count HEAD 68 

git describe命令会创build一个更具人性化的名称来引用特定的提交。 例如,从文档:

有了像git.git当前树,我得到:

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

即我的“父”分支的当前头是基于v1.0.4,但是由于它有几个提交,describe已经增加了提交的数量(“14”)和提交的缩写对象名称本身(“2414721”)最后。

只要您使用明智的命名标签来标记特定发行版,就可以认为这大致等同于SVN“版本号”。

其他海报是正确的,没有“修订号”。

我认为最好的方法是使用“发布”标签!

但我使用了以下伪造版本号 (只是为了让客户看到版本和进度,因为他们希望从git那里获得和使用版本相同的增加版本)。

显示“HEAD”的“当前版本”是用这个模拟的:

git rev-list HEAD | wc -l

但是如果客户告诉我在“修订”1302中有一个错误呢?

为此,我将以下内容添加到〜/ .gitconfig的[别名]部分:

show-rev-number = !sh -c 'git rev-list --reverse HEAD | nl | awk \"{ if(\\$1 == "$0") { print \\$2 }}\"'

然后使用git show-rev-number 1302将会打印“修订”:)的散列

我前段时间就这个“技术”做了一篇博客文章(德文) 。

Git没有与Subversion相同的版本号的概念。 取而代之的是,每个给定的快照都由一个SHA1校验和标记。 为什么? 在分布式版本控制系统中运行revno有几个问题:

首先,由于发展并不是线性的,所以附加一个数字作为一个难以解决的问题是很难满足你程序员的需要的。 试图通过添加一个数字来解决这个问题可能会很快成为问题,当数字不符合您的预期。

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

第三,在git中,由现在已经不存在的OpenCM系统开创了一席之地,一个提交(提交是什么)的身份等同于它的名字 (SHA id)。 这个命名=身份概念非常强大。 当你坐在一个提交的名字,它也以不可伪造的方式标识提交。 这反过来可以让你检查所有的提交回到第一个初始的腐败与git fsck命令。

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

通常,如果您需要处理特定的代码段,则该段正在发生变化 ,因此应该是具有主题名称的分支。 创build大量的分支(每个程序员20-30个并不是闻所未闻的,有些4-5是为其他人发布的)是有效的git技巧。 每一项工作都应该从自己的分支开始,然后在被testing时被合并。 未发表的分支可以完全重写,这部分的破坏历史是一个混帐的力量。

当变化被接受到主人有些冻结,成为考古学。 在这一点上,你可以标记它,但更多的时候,通过sha1总和来引用特定的提交是在bug跟踪器或问题跟踪器中进行的。 标签倾向于保留用于维护分支的版本颠簸和分支点(对于旧版本)。

如果你有兴趣,我在这里根据格式从git infos自动pipe理版本号

 <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) 

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 

提交的SHA1哈希等价于一个Subversion版本号。

每个提交都有一个唯一的散列。 除此之外,git中没有修订版本号。 如果您需要更多的用户友好性,则必须自行标记提交。

使用git散列作为内部版本号的问题是它不是单调递增的。 OSGibuild议使用时间戳作为内部版本号。 它看起来像分支提交的数量可以用来代替subversion或perforce更改编号。

我编写了一些PowerShell实用程序,用于从Git中检索版本信息并简化标记

函数: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 } 

这是我在基于其他解决scheme的makefile中所做的。 请注意,这不仅会为您的代码提供修订版本号,还会附加允许您重新创build版本的散列。

 # 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 

我只是想说明另一种可能的方法 – 那就是使用git git-notes(1) ,自从1.6.6( 注意Self-Git )(我使用git 1.7.9.5版本)以来就已经存在了。

基本上,我用git svn来克隆具有线性历史(没有标准布局,没有分支,没有标签)的SVN仓库,我想比较克隆的git仓库中的版本号。 这个git克隆默认没有标签,所以我不能使用git describe 。 这里的策略可能只适用于线性历史 – 不知道如何合并等等。 但这里是基本策略:

  • 询问所有提交历史列表的git rev-list
    • 由于rev-list在默认情况下是“反向时间顺序”,所以我们使用它的--reverse开关来获取按照最早的第一个sorting的提交列表
  • 使用bash shell来
    • 在每次提交时增加一个计数器variables作为修订计数器,
    • 为每个提交生成并添加一个“临时”git注释
  • 然后,通过使用git log--notes来浏览日志,这也将转储一个提交的注释,在这种情况下,它将是“修订版本号”
  • 完成后,清除临时票据( 注意:我不确定这些票据是否被提交;它们并不真正以git status显示

首先,我们注意到git有一个默认的注释位置 – 但是你也可以为注释指定一个ref (erence) – 这会将它们存储在.git下的不同目录中; 例如,在一个git repo文件夹中,你可以调用git notes get-ref来查看哪个目录是:

 $ git notes get-ref refs/notes/commits $ git notes --ref=whatever get-ref refs/notes/whatever 

要注意的是,如果你notes add一个 – --ref ,你也必须再次使用该参考 – 否则,您可能会收到错误,如“ 找不到对象XXX … ”的错误。

在这个例子中,我select了调用“linrev”的注释(用于线性修改) – 这也意味着程序不太可能干扰已经存在的注释。 我也使用了--git-dir git --git-dir开关,因为作为一个git新手,我有一些问题的理解 – 所以我想“记得以后” :) ; 而且我也使用--no-pager来压制使用git logless产卵。

所以,假设你在一个目录中,有一个子文件夹myrepo_git ,它是一个git仓库; 人们可以这样做:

 ### check for already existing notes: $ git --git-dir=./myrepo_git/.git notes show # error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33. $ git --git-dir=./myrepo_git/.git notes --ref=linrev show # error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33. ### iterate through rev-list three, oldest first, ### create a cmdline adding a revision count as note to each revision $ ix=0; for ih in $(git --git-dir=./myrepo_git/.git rev-list --reverse HEAD); do \ TCMD="git --git-dir=./myrepo_git/.git notes --ref linrev"; \ TCMD="$TCMD add $ih -m \"(r$((++ix)))\""; \ echo "$TCMD"; \ eval "$TCMD"; \ done # git --git-dir=./myrepo_git/.git notes --ref linrev add 6886bbb7be18e63fc4be68ba41917b48f02e09d7 -m "(r1)" # git --git-dir=./myrepo_git/.git notes --ref linrev add f34910dbeeee33a40806d29dd956062d6ab3ad97 -m "(r2)" # ... # git --git-dir=./myrepo_git/.git notes --ref linrev add 04051f98ece25cff67e62d13c548dacbee6c1e33 -m "(r15)" ### check status - adding notes seem to not affect it: $ cd myrepo_git/ $ git status # # On branch master # nothing to commit (working directory clean) $ cd ../ ### check notes again: $ git --git-dir=./myrepo_git/.git notes show # error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33. $ git --git-dir=./myrepo_git/.git notes --ref=linrev show # (r15) ### note is saved - now let's issue a `git log` command, using a format string and notes: $ git --git-dir=./myrepo_git/.git --no-pager log --notes=linrev --format=format:"%h: %an: %ad: >>%s<< %N" HEAD # 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000: >>test message 15 << (r15) # 77f3902: _user_: Sun Apr 21 18:29:00 2013 +0000: >>test message 14<< (r14) # ... # 6886bbb: _user_: Sun Apr 21 17:11:52 2013 +0000: >>initial test message 1<< (r1) ### test git log with range: $ git --git-dir=./myrepo_git/.git --no-pager log --notes=linrev --format=format:"%h: %an: %ad: >>%s<< %N" HEAD^..HEAD # 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000: >>test message 15 << (r15) ### erase notes - again must iterate through rev-list $ ix=0; for ih in $(git --git-dir=./myrepo_git/.git rev-list --reverse HEAD); do \ TCMD="git --git-dir=./myrepo_git/.git notes --ref linrev"; \ TCMD="$TCMD remove $ih"; \ echo "$TCMD"; \ eval "$TCMD"; \ done # git --git-dir=./myrepo_git/.git notes --ref linrev remove 6886bbb7be18e63fc4be68ba41917b48f02e09d7 # Removing note for object 6886bbb7be18e63fc4be68ba41917b48f02e09d7 # git --git-dir=./myrepo_git/.git notes --ref linrev remove f34910dbeeee33a40806d29dd956062d6ab3ad97 # Removing note for object f34910dbeeee33a40806d29dd956062d6ab3ad97 # ... # git --git-dir=./myrepo_git/.git notes --ref linrev remove 04051f98ece25cff67e62d13c548dacbee6c1e33 # Removing note for object 04051f98ece25cff67e62d13c548dacbee6c1e33 ### check notes again: $ git --git-dir=./myrepo_git/.git notes show # error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33. $ git --git-dir=./myrepo_git/.git notes --ref=linrev show # error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33. 

所以,至less在我的完全线性的历史没有分支的具体情况下,修订号似乎与这种方法相匹配 – 此外,似乎这种方法将允许使用git log版本范围,同时仍然得到正确的版本号 – YMMV有不同的上下文,虽然…

希望这有助于某人,
干杯!


编辑:好的,这里有点容易,上面的循环git别名,称为setlinrevunsetlinrev ; 当你在你的git存储库文件夹中做( 注意令人讨厌的bash转义,另请参阅#16136745 – 添加包含分号的Git别名 ):

 cat >> .git/config <<"EOF" [alias] setlinrev = "!bash -c 'ix=0; for ih in $(git rev-list --reverse HEAD); do \n\ TCMD=\"git notes --ref linrev\"; \n\ TCMD=\"$TCMD add $ih -m \\\"(r\\$((++ix)))\\\"\"; \n\ #echo \"$TCMD\"; \n\ eval \"$TCMD\"; \n\ done; \n\ echo \"Linear revision notes are set.\" '" unsetlinrev = "!bash -c 'ix=0; for ih in $(git rev-list --reverse HEAD); do \n\ TCMD=\"git notes --ref linrev\"; \n\ TCMD=\"$TCMD remove $ih\"; \n\ #echo \"$TCMD\"; \n\ eval \"$TCMD 2>/dev/null\"; \n\ done; \n\ echo \"Linear revision notes are unset.\" '" EOF 

…所以你可以简单地调用git setlinrev然后尝试执行涉及线性修订注释的日志; 和git unsetlinrev删除这些笔记,当你完成; 从git仓库目录中的一个例子:

 $ git log --notes=linrev --format=format:"%h: %an: %ad: >>%s<< %N" HEAD^..HEAD 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000: >>test message 15 << $ git setlinrev Linear revision notes are set. $ git log --notes=linrev --format=format:"%h: %an: %ad: >>%s<< %N" HEAD^..HEAD 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000: >>test message 15 << (r15) $ git unsetlinrev Linear revision notes are unset. $ git log --notes=linrev --format=format:"%h: %an: %ad: >>%s<< %N" HEAD^..HEAD 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000: >>test message 15 << 

完成这些别名需要花费的时间取决于版本库历史logging的大小。

除了提交的SHA-1 ID之外,服务器时间的date和时间会有帮助吗?

像这样的东西:

发生于2013年8月19日11:30:25将显示为6886bbb7be18e63fc4be68ba41917b48f02e09d7_19aug2013_113025

从Git手册中,标签是这个问题的一个绝妙的答案:

在Git中创build一个带注释的标签很简单。 最简单的方法是在运行标签命令时指定-a:

$ git tag -a v1.4 -m 'my version 1.4'

 $ git tag v0.1 v1.3 v1.4 

查看2.6 Git基础知识 – 标记

对于具有Ant构build过程的人员,可以使用以下目标为git上的项目生成版本号:

 <target name="generate-version"> <exec executable="git" outputproperty="version.revisions"> <arg value="log"/> <arg value="--oneline"/> </exec> <resourcecount property="version.revision" count="0" when="eq"> <tokens> <concat> <filterchain> <tokenfilter> <stringtokenizer delims="\r" /> </tokenfilter> </filterchain> <propertyresource name="version.revisions" /> </concat> </tokens> </resourcecount> <echo>Revision : ${version.revision}</echo> <exec executable="git" outputproperty="version.hash"> <arg value="rev-parse"/> <arg value="--short"/> <arg value="HEAD"/> </exec> <echo>Hash : ${version.hash}</echo> <exec executable="git" outputproperty="version.branch"> <arg value="rev-parse"/> <arg value="--abbrev-ref"/> <arg value="HEAD"/> </exec> <echo>Branch : ${version.branch}</echo> <exec executable="git" outputproperty="version.diff"> <arg value="diff"/> </exec> <condition property="version.dirty" value="" else="-dirty"> <equals arg1="${version.diff}" arg2=""/> </condition> <tstamp> <format property="version.date" pattern="yyyy-mm-dd.HH:mm:ss" locale="en,US"/> </tstamp> <echo>Date : ${version.date}</echo> <property name="version" value="${version.revision}.${version.hash}.${version.branch}${version.dirty}.${version.date}" /> <echo>Version : ${version}</echo> <echo file="version.properties" append="false">version = ${version}</echo> </target> 

结果如下所示:

 generate-version: [echo] Generate version [echo] Revision : 47 [echo] Hash : 2af0b99 [echo] Branch : master [echo] Date : 2015-04-20.15:04:03 [echo] Version : 47.2af0b99.master-dirty.2015-04-20.15:04:03 

如果在生成版本号时没有提交文件,脏标志就在这里。 因为通常,当您构build/打包应用程序时,每个代码修改必须位于存储库中。

TortoiseGit似乎认为SHA-1的前7位是修订版本。 在文件上查看日志时,对话框显示在底部:

显示从版本ABCDEF1到版本FEFCBA1的N版本

如果TortoiseGit认为这是正确的答案吗?

在长时间研究在线仓库之后,我发现在逻辑上Git中的版本号和提交号之间没有区别。