tag number git version-control revision-history version-numbering

number - ¿Cuál es el equivalente de Git para el número de revisión?



git revision number (18)

Buenas o malas noticias para ti, ese hash ES el número de revisión. También tuve problemas con esto cuando hice el cambio de SVN a git.

Puede usar "etiquetar" en git para etiquetar una determinada revisión como el "lanzamiento" de una versión específica, lo que facilita la consulta de esa revisión. Echa un vistazo a esta entrada de blog .

La clave para comprender es que git no puede tener números de revisión; piense en la naturaleza descentralizada. Si los usuarios A y B se comprometen con sus repositorios locales, ¿cómo puede git asignar un número de revisión secuencial de manera razonable? A no tiene conocimiento de B antes de que empujen / tiren los cambios de cada uno.

Otra cosa a tener en cuenta es la ramificación simplificada para las ramas de corrección de errores:

Comience con una versión: 3.0.8. Luego, después de ese lanzamiento, haz esto:

git branch bugfixes308

Esto creará una rama para las correcciones de errores. Revisa la sucursal:

git checkout bugfixes308

Ahora haga los cambios de corrección de errores que desee.

git commit -a

Comprométalos y vuelve a la rama maestra:

git checkout master

Luego tira esos cambios de la otra rama:

git merge bugfixes308

De esa manera, tiene una rama de corrección de errores específica de la versión separada, pero todavía está llevando a cabo los cambios de corrección de errores en su tronco de desarrollo principal.

Usamos SVN en el trabajo, pero para mis proyectos personales decidí usar Git. Así que instalé Git ayer, y me pregunto cuál es el número de revisión equivalente en Git .

Digamos que trabajamos en la versión 3.0.8 y cada corrección de errores tiene su propio número de revisión que podemos usar cuando hablamos de esta corrección de errores. Entonces, si etiqueto el código en Git a 3.0.8, ¿qué puedo usar como número de revisión o algún otro tipo de identificación más detallada? Encuentro el hash no tan amigable para los humanos.


Cada commit tiene un hash único. Aparte de eso no hay números de revisión en git. Tendrá que etiquetar los compromisos si desea una mayor facilidad de uso.


Con el Git moderno (1.8.3.4 en mi caso) y sin usar sucursales puedes hacer:

$ git rev-list --count HEAD 68


Del manual de Git, las etiquetas son una respuesta brillante a este problema:

Crear una etiqueta anotada en Git es simple. La forma más fácil es especificar -a cuando ejecuta el comando de etiqueta:

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

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

Echa un vistazo a 2.6 Git Basics - Etiquetado


Después de investigar durante mucho tiempo en los repositorios en línea, descubrí que, lógicamente, no hay diferencia entre un número de revisión y un número de confirmación en Git.



El comando git describe crea un nombre legible un poco más humano que se refiere a un compromiso específico. Por ejemplo, a partir de la documentación:

Con algo como git.git tree actual, obtengo:

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

es decir, el jefe actual de mi rama "primaria" se basa en v1.0.4, pero como tiene algunas confirmaciones además de eso, describe ha agregado la cantidad de confirmaciones adicionales ("14") y un nombre de objeto abreviado para la confirmación sí ("2414721") al final.

Siempre que use etiquetas con nombres sensatos para etiquetar lanzamientos particulares, esto se puede considerar que es aproximadamente equivalente a un "número de revisión" de SVN.


El problema con el uso del hash de git como número de compilación es que no está aumentando de forma monotónica. OSGi sugiere usar una marca de tiempo para el número de compilación. Parece que el número de confirmaciones en la rama podría usarse en lugar del número de subversión o cambio forzado.


Escribí algunas utilidades de PowerShell para recuperar información de versión de Git y simplificar el etiquetado

funciones: 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 }


Esto es lo que hice en mi makefile basado en otras soluciones. Tenga en cuenta que no solo le da a su código un número de revisión, sino que también agrega el hash que le permite recrear la versión.

# 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


Evento posterior a la compilación para Visual Studio

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


Git no tiene el mismo concepto de números de revisión como subversión. En su lugar, cada instantánea dada realizada con una confirmación está etiquetada por una suma de comprobación SHA1. ¿Por qué? Hay varios problemas con un revno en ejecución en un sistema de control de versiones distribuido:

Primero, dado que el desarrollo no es lineal en absoluto, la conexión de un número es un problema difícil de resolver de una manera que satisfaga sus necesidades como programador. Intentar solucionar este problema agregando un número puede convertirse rápidamente en un problema cuando el número no se comporta como espera.

En segundo lugar, se pueden generar números de revisión en diferentes máquinas. Esto hace que la sincronización de los números sea mucho más difícil, especialmente porque la conectividad es unidireccional; Es posible que ni siquiera tenga acceso a todas las máquinas que tienen el repositorio.

En tercer lugar, en git, un tanto pionero en el sistema OpenCM ahora desaparecido, la identidad de un compromiso (lo que es el compromiso) es equivalente a su nombre (el ID de SHA). Esta denominación = concepto de identidad es muy fuerte. Cuando te sientas con un nombre de compromiso en la mano, también identifica el compromiso de una manera imperdonable. Esto, a su vez, le permite verificar que todas sus confirmaciones vuelvan a la primera inicial para detectar daños con el comando git fsck .

Ahora, como tenemos un DAG (Directed Acyclic Graph) de revisiones y estas constituyen el árbol actual, necesitamos algunas herramientas para resolver su problema: ¿Cómo discriminamos las diferentes versiones? Primero, puede omitir parte del hash si un prefijo dado, por ejemplo, identifica de forma única su confirmación. Pero esto también es algo artificial. En cambio, el truco es usar etiquetas y / o ramas. Una etiqueta o rama es similar a una "nota amarilla" que adjuntas a una confirmación SHA1-id dada. Las etiquetas son, en esencia, destinadas a no moverse, mientras que una rama se moverá cuando se realicen nuevas confirmaciones en su CABEZA. Hay formas de referirse a un compromiso alrededor de una etiqueta o rama, consulte la página de manual de git-rev-parse.

Por lo general, si necesita trabajar en una pieza específica de código, esa pieza está experimentando cambios y, como tal, debe ser una rama con un nombre de tema que diga. Crear un montón de sucursales (20-30 por programador no es inaudito, con unos 4-5 publicados para que otros trabajen) es el truco para un git efectivo. Cada trabajo debe comenzar como su propia rama y luego fusionarse cuando se prueba. Las ramas no publicadas se pueden reescribir por completo y esta parte de la historia de la destrucción es una fuerza de git.

Cuando el cambio se acepta como maestro, se congela y se convierte en arqueología. En ese momento, puede etiquetarlo, pero más a menudo se hace una referencia a la confirmación en particular en un rastreador de errores o un rastreador de problemas a través de la suma sha1. Las etiquetas tienden a ser reservadas para los bump de la versión y los puntos de ramificación para las sucursales de mantenimiento (para versiones anteriores).


Junto con el ID SHA-1 de la confirmación, ¿la fecha y la hora de la hora del servidor habrían ayudado?

Algo como esto:

el compromiso ocurrido a las 11:30:25 del 19 de agosto de 2013 se mostraría como 6886bbb7be18e63fc4be68ba41917b48f02e09d7_19aug2013_113025


Los otros carteles tienen razón, no hay un "número de revisión".

Creo que la mejor manera es usar etiquetas para "lanzamientos".

Pero utilicé lo siguiente para falsificar números de revisión (solo para que los clientes vean las revisiones y el progreso, ya que querían tener las mismas revisiones cada vez mayores de git que las que usaban por subversión).

Mostrar la "revisión actual" de "HEAD" se simula usando esto:

git rev-list HEAD | wc -l

¿Pero qué pasa si el cliente me dice que hay un error en la "revisión" 1302?

Para esto, agregué lo siguiente a la sección [alias] de mi ~ / .gitconfig:

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

el uso de git show-rev-number 1302 imprimirá el hash para la "revisión" :)

Hice una publicación de blog (en alemán) sobre esa "técnica" hace algún tiempo.


Para las personas que tienen un proceso de compilación Ant , puede generar un número de versión para un proyecto en git con este objetivo:

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

El resultado se ve así:

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

La bandera sucia está aquí cuando tiene archivos no confirmados cuando genera el número de versión. Porque generalmente, cuando creas / empaquetas tu aplicación, cada modificación de código tiene que estar en el repositorio.


Si está interesado, administré los números de versión automáticamente desde las informaciones de git here en el formato

<major>.<minor>.<patch>-b<build>

donde construir es el número total de confirmaciones. Verás el código interesante en el Makefile . Aquí está la parte relevante para acceder a la parte diferente del número de versión:

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)


Solo me gustaría señalar otro enfoque posible, y es mediante el uso de git git-notes(1) , que existe desde la versión 1.6.6 ( Note to Self - Git ) (Estoy usando git versión 1.7.9.5).

Básicamente, utilicé git svn para clonar un repositorio SVN con un historial lineal (sin diseño estándar, sin ramas, sin etiquetas), y quería comparar los números de revisión en el repositorio de git clonado. Este clon de git no tiene etiquetas por defecto, así que no puedo usar git describe . La estrategia aquí probablemente funcionaría solo para el historial lineal, no estoy seguro de cómo se desarrollaría con las combinaciones, etc .; Pero aquí está la estrategia básica:

  • Solicite a git rev-list una lista de todos los registros de historial
    • Como rev-list está por defecto en "orden cronológico inverso", --reverse su interruptor --reverse para obtener la lista de confirmaciones ordenadas por las más antiguas primero
  • Usa el shell bash para
    • aumentar una variable de contador en cada confirmación como un contador de revisión,
    • generar y agregar una nota git "temporal" para cada confirmación
  • Luego, navegue por el registro utilizando git log con --notes , que también descargará una nota de confirmación, que en este caso sería el "número de revisión"
  • Cuando haya terminado, borre las notas temporales ( NB: No estoy seguro de si estas notas se han confirmado o no; en realidad no se muestran en el git status )

Primero, git que git tiene una ubicación predeterminada de notas, pero también puede especificar una ref (referencia) para las notas, que las almacenaría en un directorio diferente debajo de .git ; por ejemplo, mientras está en una carpeta de git repo, puede llamar a git notes get-ref para ver qué directorio será:

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

Lo que se debe tener en cuenta es que si notes add con una --ref , también debe usar esa referencia nuevamente, de lo contrario, podría obtener errores como " No se encontró ninguna nota para el objeto XXX ... ".

Para este ejemplo, he elegido llamar a la ref de las notas "linrev" (para revisión lineal); esto también significa que no es probable que el procedimiento interfiera con las notas ya existentes. También estoy usando el interruptor --git-dir , ya que siendo un git newbie, tuve algunos problemas para entenderlo, así que me gustaría "recordar para más adelante" :) ; y también uso --no-pager para suprimir el desove de less cuando se usa git log .

Entonces, asumiendo que estás en un directorio, con una subcarpeta myrepo_git que es un repositorio git ; uno podria hacer

### 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.

Por lo tanto, al menos en mi caso específico de historial lineal sin ramas, los números de revisión parecen coincidir con este enfoque, y además, parece que este enfoque permitirá el uso de git log con los rangos de revisión, mientras sigue obteniendo los números de revisión correctos. - YMMV con un contexto diferente, aunque ...

Espero que esto ayude a alguien,
¡Aclamaciones!

EDIT: Ok, aquí es un poco más fácil, con alias git para los bucles anteriores, llamados setlinrev y unsetlinrev ; cuando esté en su carpeta de repositorio de git, haga (tenga en cuenta que se escapó el bash desagradable, consulte también # 16136745 - Agregar un alias de Git que contenga un punto y coma ):

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

... así que simplemente puede invocar git setlinrev antes de intentar hacer un registro que incluya notas de revisión lineales; y git unsetlinrev para eliminar esas notas cuando hayas terminado; Un ejemplo desde dentro del directorio 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 <<

El tiempo que tomaría el shell para completar estos alias, dependería del tamaño del historial del repositorio.


Una función 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} }

salidas algo como

$ git_rev 2:0f8e14e:20130220

Es decir

commit_count:last_abbrev_commit:date_YYmmdd