Что такое эквивалент Git для номера ревизии?

мы используем SVN на работе, но для моих личных проектов я решил использовать Git. Поэтому я установил Git вчера, и мне интересно, что такое номер версии эквивалентно в Git.

предположим, мы работаем над версией 3.0.8, и каждое исправление ошибки имеет свой номер редакции, который мы можем использовать, когда говорим об этом исправлении ошибки. Итак, если я помечу код в Git до 3.0.8, что тогда я могу использовать в качестве номера ревизии или другого более подробного вида идентификации? Я считаю хэш не так дружественный для людей.

18 ответов


хорошая или плохая новость для вас, что хэш-это номер ревизии. У меня также были проблемы с этим, когда я сделал переход от 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

таким образом, у вас есть отдельная ветвь исправлений для конкретного выпуска, но вы все еще тянете изменения исправлений в свою основную магистраль dev.


С современным Git (1.8.3.4 в моем случае) и без использования ветвей вы можете сделать:

$ git rev-list --count HEAD
68

на git describe команда создает немного более удобочитаемое имя, которое ссылается на определенную фиксацию. Например, из документации:

С чем-то вроде git.git текущее дерево, я получаю:

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

т. е. текущий руководитель моей" родительской " ветви основан на v1.0.4, но поскольку у него есть несколько коммитов поверх этого, describe добавил количество дополнительных коммитов ("14") и сокращенное имя объекта для самого коммита ("2414721") в конце.

пока вы используете разумно названные теги для тегов определенных выпусков, это можно считать примерно эквивалентным SVN "номеру редакции".


другие плакаты правы, нет"ревизионного номера".

Я думаю, что лучший способ-использовать теги для "релизов"!

но я использовал следующее поддельные номера редакции (только для клиентов, чтобы увидеть изменения и прогресс, поскольку они хотели иметь те же самые увеличивающиеся изменения от git, что и при использовании subversion).

показать, что" текущая редакция "" HEAD " моделируется с помощью это:

git rev-list HEAD | wc -l

но что, если клиент скажет мне, что есть ошибка в "ревизии" 1302 ?

для этого я добавил следующее в раздел [alias] моего ~/.gitconfig хранит настройки:

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

используя печать хэш для "ревизии" :)

Я сообщение в блоге (на немецком языке) об этой "технике" некоторое время назад.


Git не имеет той же концепции номеров ревизий, что и subversion. Вместо этого каждый моментальный снимок, сделанный с фиксацией, помечается контрольной суммой SHA1. Почему? Существует несколько проблем с запуском revno в распределенной системе управления версиями:

во-первых, поскольку развитие вообще не линейно, вложение числа довольно сложно решить таким образом, чтобы удовлетворить ваши потребности как программиста. Попытка исправить это, добавив число, может быстро стать проблематично, когда число не ведет себя так, как вы ожидаете.

во-вторых, номера ревизий могут быть созданы на разных машинах. Это значительно затрудняет синхронизацию чисел, особенно с учетом одностороннего подключения; возможно, у вас даже нет доступа ко всем машинам с хранилищем.

в-третьих, в git, несколько пионером теперь несуществующей системы OpenCM,личность фиксации (что такое фиксация) эквивалентно его имя (the SHA id). Это именования тож= концепция очень сильная. Когда вы сидите с именем фиксации в руке, он также идентифицирует фиксацию непростительным способом. Это в свою очередь позволяет проверить все ваши коммиты возвращаются к первому начальному для коррупции с .

теперь, поскольку у нас есть DAG (направленный ациклический граф) ревизий, и они составляют текущее дерево, нам нужны некоторые инструменты для решения код проблема: как мы различают разные версии. Во-первых, вы можете опустить часть хэша, если задан префикс,1516bd скажем, однозначно идентифицирует вашу фиксацию. Но это тоже довольно надуманно. Вместо этого нужно использовать теги и ветви. Тег или ветвь сродни "желтой палочке", которую вы прикрепляете к данному фиксатору SHA1-id. Теги, по сути, должны быть неподвижными, тогда как ветвь будет двигаться, когда новые коммиты будут сделаны в ее голове. Есть способы ссылаться на фиксацию вокруг тега или ветви, см. справочную страницу git-rev-parse.

обычно, если вам нужно работать над определенным фрагментом кода, этот фрагмент перемены и должна быть ветка с красноречивым названием темы. Создание множества ветвей (20-30 на одного программиста не является неслыханным, с некоторыми 4-5 опубликованными для других) - это трюк для эффективного git. Каждая часть работы должна начинаться как собственная ветвь, а затем объединяться при тестировании. Неопубликованные ветви можно переписать полностью и эта часть разрушительной истории-сила git.

когда изменение принято в master он несколько замерзает и становится археологией. В этот момент Вы можете пометить его, но чаще ссылка на конкретную фиксацию делается в трекере ошибок или трекере проблем через сумму sha1. Теги, как правило, зарезервированы для ударов версий и точек ветвей для ветвей обслуживания (для старых версий).


Если вам интересно, я управлял номерами версий автоматически из 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]*\).*//")
MINOR      = $(shell echo $(VERSION) | sed "s/[0-9]*\.\([0-9]*\).*//")
PATCH      = $(shell echo $(VERSION) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*//")
# 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.


Это то, что я сделал в моем 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

Я написал некоторые утилиты 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 нет номеров ревизий. Вам нужно будет пометить коммиты, если вы хотите больше удобства для пользователя.


проблема с использованием хэша git в качестве номера сборки заключается в том, что он не монотонно увеличивается. OSGi предлагает использовать отметку времени для номера сборки. Похоже, что количество коммитов в ветке может использоваться вместо номера subversion или принудительного изменения.


я хотел бы отметить еще один возможный подход - и это с помощью git git-notes (1), существует с v 1.6.6 (Примечание Для Self-Git) (я использую git версии 1.7.9.5).

в основном, я использовал git svn клонировать репозиторий SVN с линейной историей (без стандартной компоновки, без ветвей, без тегов), и я хотел сравнить номера ревизий в клонированном git репозитория. Этот клон git не имеет тегов по умолчанию, поэтому я не могу использовать git describe. Стратегия здесь, вероятно, будет работать только для линейной истории - не уверен,как это получится со слияниями и т. д.; но вот основная стратегия:

  • задать git rev-list для списка всей истории фиксации
    • С rev-list по умолчанию в "обратном хронологическом порядке", мы бы использовали его --reverse переключатель, чтобы получить список коммитов, отсортированный по убыванию
  • использовать bash консоли
    • увеличение переменной счетчика для каждой фиксации в качестве счетчика ревизий,
    • создайте и добавьте "временную" заметку git для каждой фиксации
  • затем просмотрите журнал с помощью git log С --notes, который также будет сбрасывать Примечание фиксации, которое в этом случае будет "номером ревизии"
  • когда закончите, удалите временные заметки (NB: я не уверен, что эти заметки совершены или нет; они действительно не отображаются в git status)

первый, отметим, что git имеет расположение заметок по умолчанию - но вы также можете указать ref(разница) для заметок - которые будут хранить их в другом каталоге под .git; например, в то время как в git папка РЕПО, вы можете позвонить git notes get-ref чтобы увидеть, какой каталог это будет:

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

следует отметить, что если вы notes add С --ref, вы также должны впоследствии использовать эту ссылку снова-в противном случае вы можете получить ошибки, такие как "примечание не найдено для объект XXX...".

для этого примера я решил назвать ref Примечаний "linrev" (для линейной ревизии) - это также означает, что вряд ли процедура будет мешать уже существующим Примечаниям. Я также использую --git-dir переключатель, так как git новичок, у меня были некоторые проблемы с пониманием - поэтому я хотел бы "запомнить на потом":); и я также использовать --no-pager для подавления нереста less при использовании git log.

так, предполагая, что вы находитесь в каталоге с подпапкой 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 с другим контекстом...

надеюсь, это поможет кто-то,
Ура!


EDIT: хорошо, здесь немного проще, с git псевдонимы для вышеуказанных циклов, называемые setlinrev и unsetlinrev; когда в вашей папке репозитория 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 <<

время, которое потребуется оболочке для завершения этих псевдонимов, будет зависеть от размера истории репозитория.


для людей, у которых есть АНТ процесс сборки, вы можете создать номер версии для проекта на 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

грязный флаг здесь, когда у вас есть файл(ы) не помогут, когда вы генерируете версию. Потому что обычно, когда вы создаете / упаковываете свое приложение, каждая модификация кода должна быть в репозитории.


наряду с идентификатором SHA-1 фиксации, дата и время серверного времени помогли бы?

что-то вроде этого:

commit произошло в 11: 30: 25 19 aug 2013 покажет, как 6886bbb7be18e63fc4be68ba41917b48f02e09d7_19aug2013_113025


из руководства Git теги являются блестящим ответом на этот вопрос:

создание аннотированного тега в Git просто. Самый простой способ - укажите -a при выполнении команды tag:

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

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

проверить 2.6 Git Основы-Пометки


после долгого исследования в онлайн-репозиториях я обнаружил, что логически нет разницы между номером ревизии и номером фиксации в Git.


событие Post build для Visual Studio

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