Git的修订号是什么?

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

假设我们在3.0.8版本上工作,当我们讨论这个错误修复时,每个错误修复都有自己的修订号。那么,如果我在Git中将代码标记为3.0.8,那么我可以使用什么作为修订号或其他更详细的标识?我发现哈希对人类不太友好。

239374 次浏览

每个提交都有一个唯一的散列。除此之外,git中没有修订号。如果您想要更加用户友好,就必须自己标记提交。

提交的SHA1哈希值相当于一个Subversion版本号。

不管对你来说是好消息还是坏消息,哈希值就是修订号。当我从SVN切换到git时,我也遇到了这个问题。

你可以在git中使用“tagging”将某个修订标记为特定版本的“release”,这样就可以方便地引用该修订。看看这个博客

要理解的关键是git不能有修订号——想想去中心化的本质。如果用户A和B都提交到他们的本地存储库,git如何合理地分配一个连续的修订号?在他们相互推动/拉动对方的变化之前,A对B一无所知。

另一件需要考虑的事情是用于修复错误的分支的简化分支:

从一个版本开始:3.0.8。然后,在释放之后,这样做:

git branch bugfixes308

这将为错误修复创建一个分支。检查分支:

git checkout bugfixes308

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

git commit -a

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

git checkout master

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

git merge bugfixes308

这样,您就有了一个单独的特定于发行版的错误修复分支,但是您仍然将错误修复更改拉到您的主开发主干中。

Git没有与subversion相同的版本号概念。相反,使用提交生成的每个给定快照都被标记为SHA1校验和。为什么?在分布式版本控制系统中运行revno有几个问题:

首先,由于开发根本不是线性的,数字的附加作为一个问题很难以一种满足程序员需求的方式解决。试图通过添加一个数字来解决这个问题,当这个数字没有像您期望的那样表现时,可能很快就会出现问题。

第二,修订号可以在不同的机器上生成。这使得数字的同步变得更加困难——特别是因为连接是单向的;您甚至可能无法访问拥有存储库的所有计算机。

第三,在git中,在某种程度上是由现在已经不存在的OpenCM系统开创的,一个提交的身份(提交是什么)等价于它的的名字 (SHA id)。这个命名=身份概念非常强大。当你手上有一个提交名称时,它也会以一种不可伪造的方式标识提交。这反过来又让你用git fsck命令检查所有的提交都返回到第一个初始值是否损坏。

现在,因为我们有一个修订的DAG(有向无环图),它们构成了当前的树,我们需要一些工具来解决你的问题:我们如何区分不同的版本。首先,如果一个给定的前缀(比如1516双相障碍)唯一地标识了你的提交,你可以省略一部分散列。但这也是相当做作的。相反,诀窍是使用标记和或分支。标记或分支类似于您附加到给定提交SHA1-id的“黄色便签”。从本质上讲,标记是不移动的,而当新的提交到分支的HEAD时,分支将移动。有很多方法可以围绕标记或分支引用提交,请参阅git-rev-parse的手册页。

通常,如果你需要处理一段特定的代码,这段代码是接受变化,因此应该是一个具有特定主题名称的分支。创建大量的分支(每个程序员20-30个分支也不是没听说过,还有4-5个发布给其他人工作)是有效git的诀窍。每一项工作都应该作为自己的分支开始,然后在测试时被合并。未发布的分支可以完全重写,这部分破坏历史的部分是git的力量。

当变化是被录取为硕士时,它有点冻结并成为考古学。在这一点上,您可以标记它,但更多情况下,对特定提交的引用是通过sha1 sum在错误跟踪器或问题跟踪器中进行的。标签倾向于为版本颠簸保留,为维护分支保留分支点(用于旧版本)。

git describe命令创建了一个更容易读懂的名字,它引用了一个特定的提交。例如,从文档中:

和像少年犯一样的东西。git当前树,我得到:

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

也就是说,我“父母”的现任负责人;Branch基于v1.0.4,但由于它在此基础上有一些提交,所以describe在末尾添加了额外提交的数量(&;14&;)和提交本身的缩写对象名称(&;2414721&;)。

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

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

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

但是我使用了下面的假修订号(只是为了让客户端看到修订和进展,因为他们想要从git中获得与使用subversion相同的不断增加的修订)。

显示“HEAD”的“当前版本”是通过使用下面的代码来模拟的:

git rev-list HEAD | wc -l

但是如果客户告诉我“修订版”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”的哈希:)

前段时间我做了一个博客文章(德文)关于那个“技巧”。

如果你感兴趣,我从git infos 在这里自动管理版本号的格式

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

使用git散列作为构建号的问题是,它不是单调递增的。OSGi建议对构建号使用时间戳。似乎可以使用向分支提交的数量来代替subversion或强制更改数量。

一个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

我只想指出另一种可能的方法-那就是使用git git-notes (1),自v 1.6.6 (注意到自我- Git)以来就存在(我使用的是git版本1.7.9.5)。

基本上,我使用git svn克隆了一个具有线性历史的SVN存储库(没有标准布局,没有分支,没有标签),并且我想比较克隆的git存储库中的修订号。这个git克隆默认没有标签,所以我不能使用git describe。这里的策略可能只适用于线性历史-不确定它将如何与合并等;但基本策略如下:

  • git rev-list请求所有提交历史的列表
    • 由于rev-list默认是“逆时间顺序”,我们使用它的--reverse开关来获得按最老时间排序的提交列表
    • 李< / ul > < / >
    • 使用bash shell来
      • 在每次提交时增加一个计数器变量作为修订计数器,
      • 为每次提交生成并添加一个“临时”git记录
      • 李< / ul > < / >
      • 然后,通过使用git log--notes浏览日志,这也将转储提交的注释,在这种情况下将是“修订号”
      • 完成后,删除临时注释(NB:我不确定这些笔记是否已提交;它们并没有真正显示在git status)

      首先,让我们注意到git有一个默认的笔记位置-但你也可以为笔记指定一个ref(erence) -这将把它们存储在.git下的另一个目录中;例如,在git回购文件夹中,你可以调用git notes get-ref来查看它将是什么目录:

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

      需要注意的是,如果你用--refnotes add,之后你还必须再次使用该引用——否则你可能会得到类似"没有找到对象XXX的说明…"的错误。

      对于这个例子,我选择将注释的ref称为“linrev”(用于线性修订)——这也意味着这个过程不太可能干扰已经存在的注释。我也在使用--git-dir开关,因为是一个git新手,我在理解它时遇到了一些问题-所以我想“以后记得”:);在使用git log时,我也使用--no-pager来抑制less的生成。

      所以,假设你在一个目录中,有一个子文件夹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.
      

      因此,至少在我的完全线性历史没有分支的特定情况下,修订编号似乎与这种方法相匹配-此外,似乎这种方法将允许使用修订范围git log,同时仍然获得正确的修订编号- YMMV具有不同的上下文,尽管…

      希望这能帮助到某人 干杯!< / p >


      编辑:好的,这里稍微简单一点,上面的循环有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 repo目录的例子:

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

      shell完成这些别名所需的时间取决于存储库历史记录的大小。

除了提交的SHA-1 id,服务器时间的日期和时间也有帮助吗?

就像这样:

commit happens at 11:30:25 on aug 2013将显示为 6886年bbb7be18e63fc4be68ba41917b48f02e09d7_19aug2013_113025 < / p >

使用现代Git(在我的例子中是1.8.3.4)并且不使用分支,你可以做到:

$ git rev-list --count HEAD
68

但这有各种各样的问题,可能不容易重现,或者在需要时不容易回到提交散列。所以尽量避免,或者只把它作为一个暗示。

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

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

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

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

查看2.6 Git基础-标记

我编写了一些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
}

对于那些有蚂蚁构建过程的人,你可以用这个目标在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

当生成版本号时有文件未提交时,此处会出现脏标志。因为通常,当构建/打包应用程序时,每个代码修改都必须在存储库中。

这是我在基于其他解决方案的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

Visual Studio的后构建事件

echo  >RevisionNumber.cs static class Git { public static int RevisionNumber =
git  >>RevisionNumber.cs rev-list --count HEAD
echo >>RevisionNumber.cs ; }

我们使用从git中获取版本和修订:

git describe --always --tags --dirty

它返回

  • 当没有使用标记时提交哈希作为修订(例如gcc7b71f)
  • 在标签上时,标签名作为版本(例如v2.1.0,用于发布)
  • 标签名称,自上一个标签以来的修订号,以及在标签之后提交哈希(例如v5.3.0-88-gcc7b71f)
  • 如上所述,如果工作树有局部修改,则加一个“dirty”标签(例如v5.3.0-88-gcc7b71f-dirty)

参见:https://www.git-scm.com/docs/git-describe#Documentation/git-describe.txt

考虑使用

git-rev-label

以类似master-c73-gabc6bec的格式给出Git存储库修订的信息。 可以填充模板字符串或文件与环境变量和信息从Git。 用于提供关于程序版本的信息:分支,标记,提交散列, 提交计数,脏状态,日期和时间。最有用的事情之一是计算 提交,不考虑合并的分支-只考虑第一个父分支

 ></a></p></div>
                                                                            </div>
                                </div>
                            </div>
                        </div>
                                            </div>
                </div>

                <br/>
                <div class=