Pasar de CVS a Git: $ Id $ equivalente?

124

Leí un montón de preguntas sobre herramientas simples de control de código fuente y Git parecía una opción razonable. Lo tengo en funcionamiento, y funciona bien hasta ahora. Un aspecto que me gusta de CVS es el incremento automático de un número de versión.

Entiendo que esto tiene menos sentido en un repositorio distribuido, pero como desarrollador, quiero / necesito algo como esto. Déjame explicarte por qué:

Yo uso Emacs. Periódicamente reviso y busco nuevas versiones de los archivos fuente de Lisp para paquetes de terceros. Digamos que tengo un archivo, foo.el, que, según el encabezado, es la versión 1.3; Si busco la última versión y veo que es 1.143 o 2.6 o lo que sea, sé que estoy bastante atrasado.

Si, en cambio, veo un par de hashes de 40 caracteres, no sabré cuál es más tarde ni tendré idea de cuánto más tarde será. Absolutamente lo odiaría si tuviera que verificar manualmente ChangeLogs solo para tener una idea de cuán desactualizado estoy.

Como desarrollador, quiero extender esta cortesía, tal como la veo, a las personas que usan mi salida (y tal vez me estoy engañando a mí mismo de que alguien lo es, pero dejemos eso de lado por un momento). No quiero tener que recordar incrementar el maldito número cada vez, o una marca de tiempo o algo así. Esa es una verdadera PITA, y lo sé por experiencia.

Entonces, ¿qué alternativas tengo? Si no puedo obtener un $ Id: $ equivalente, ¿de qué otra forma puedo proporcionar lo que estoy buscando?

Debo mencionar que mi expectativa es que el usuario final NO tendrá instalado Git e incluso si lo tiene, no tendrá un repositorio local (de hecho, espero que no esté disponible de esa manera).

Joe Casadonte
fuente

Respuestas:

67

El SHA es solo una representación de una versión (aunque canónica). El git describecomando ofrece a otros y lo hace bastante bien.

Por ejemplo, cuando ejecuto git describeen mi rama maestra de mi fuente de cliente memcached Java , obtengo esto:

2.2-16-gc0cd61a

Eso dice dos cosas importantes:

  1. Ha habido exactamente 16 confirmaciones en este árbol desde 2.2
  2. El árbol fuente exacto se puede mostrar en el clon de cualquier otra persona.

Digamos, por ejemplo, que empaquetó un versionarchivo con la fuente (o incluso reescribió todo el contenido para su distribución) para mostrar ese número. Digamos que la versión empaquetada era 2.2-12-g6c4ae7a(no una versión, sino una versión válida).

Ahora puede ver exactamente qué tan atrás está (4 commits), y puede ver exactamente qué 4 commits:

# The RHS of the .. can be origin/master or empty, or whatever you want.
% git log --pretty=format:"%h %an %s" 2.2-12-g6c4ae7a..2.2-16-gc0cd61a
c0cd61a Dustin Sallings More tries to get a timeout.
8c489ff Dustin Sallings Made the timeout test run on every protocol on every bui
fb326d5 Dustin Sallings Added a test for bug 35.
fba04e9 Valeri Felberg Support passing an expiration date into CAS operations.
Dustin
fuente
1
El uso de este fallará al fusionar la rama de desarrollo, mientras que el maestro tenía algunas revisiones. El número de confirmaciones desde la última versión cambiará. El hash no es confiable ya que alguien puede reconstruir todo con filter-branchalgo o algo así.
LeMike
La escritura solo describe aprender la información, no incrustarla en su ejecutable. Para eso, debe ejecutar el git describecomando justo antes de compilar, guardar el resultado en un archivo de encabezado o incrustar el valor en su código.
Jesse Chisholm
55

Por ahora hay soporte para $ Id: $ en Git. Para habilitarlo para el archivo README , debe colocar "README ident" en .gitattributes . Se admiten comodines en los nombres de archivo. Ver man gitattributes para más detalles.

Sebastian Pipping
fuente
14
Esto le da el sha1 del blob, pero no el sha1 del commit. Útil, simplemente no como un identificador para la confirmación.
Stephen Jennings
44
git no tiene ningún mecanismo de expansión de palabras clave como el $Id$mencionado. Lo que se almacena es exactamente lo que obtienes. En cualquier caso, la versión pertenece a la colección completa de archivos que componen una confirmación, no a un archivo en particular (esa idea es un remanente de los días de RCS, o quizás SCCS tiene la culpa aquí ... Como CVS es solo un frontend glorificado a RCS, y SVN intenta ser un CVS-workalike, se atascó).
vonbrand
32

Esta no es una solicitud irrazonable del OP.

Mi caso de uso es:

  1. Utilizo Git para mi propio código personal, por lo tanto, no colaboro con otros.
  2. Mantengo los scripts de Bash del sistema allí que pueden entrar /usr/local/bincuando estén listos.

Yo uso tres máquinas separadas con el mismo repositorio Git. Sería bueno saber en qué "versión" del archivo tengo actualmente/usr/local/bin sin tener que hacer un manual "diff -u <versión de repositorio> <versión en / usr / local / bin>".

Para aquellos de ustedes que son negativos, recuerden que existen otros casos de uso. No todos usan Git para el trabajo colaborativo con los archivos en el repositorio de Git como su ubicación "final".

De todos modos, la forma en que lo hice fue crear un archivo de atributos en el repositorio como este:

cat .git/info/attributes
# see man gitattributes
*.sh ident
*.pl ident
*.cgi ident

Luego ponga $ Id $ en algún lugar del archivo (me gusta ponerlo después del shebang).

El compromiso Tenga en cuenta que esto no hace la expansión automáticamente como esperaba. Debe volver a copiar el archivo, por ejemplo,

git commit foo.sh
rm foo.sh
git co foo.sh

Y luego verá la expansión, por ejemplo:

$ head foo.sh
#!/bin/sh

# $Id: e184834e6757aac77fd0f71344934b1cd774e6d4 $

Hay buena información en ¿Cómo habilito la cadena de identificación para un repositorio de Git? .

Imran-Reino Unido
fuente
3
Cabe señalar que esto identifica el archivo actual (blob), no la confirmación actual
CharlesB
3
¿Qué se git cosupone que debe hacer? Recibí el mensaje de error " git: 'co' is not a git command. See 'git --help'." ¿Debería serlogit checkout ?
Peter Mortensen
23

No estoy seguro de que esto vaya a estar en Git. Para citar a Linus :

"Toda la noción de sustitución de palabras clave es totalmente idiota. Es trivial hacer" fuera "del seguimiento de contenido real, si quieres tenerlo cuando liberas árboles como bolas de alquitrán, etc."

Sin embargo, es bastante fácil verificar el registro: si está rastreando la rama estable de foo.el, puede ver qué nuevas confirmaciones hay en el registro de la rama estable que no están en su copia local. Si desea simular el número de versión interno de CVS, puede comparar la marca de tiempo de la última confirmación.

Editar: debe escribir o usar los scripts de otra persona para esto, por supuesto, no hacerlo manualmente.

orip
fuente
26
Sí, leí parte de esa larga serie de correos electrónicos sobre expansiones de palabras clave. La actitud de Linus fue casi suficiente para desanimarme por completo.
Joe Casadonte
15
Sí, a veces carece de cortesía, pero generalmente es correcto, y definitivamente está en el tema de la expansión de palabras clave.
Bombe
El seguimiento de versiones es necesario. git se usa en muchas otras infraestructuras además del desarrollo puro donde uno puede leer el código para determinar si tiene sentido. Pero cuando se utiliza un sistema de control de revisiones para rastrear archivos con contenido arbitrario, entonces debe tener algún medio para conocer el lanzamiento oficial. git, y mucho menos el registro de git no está en la máquina donde se insertaron .ini, .conf, .html y otros archivos.
rjt
77
Linus comentó solo un aspecto de la expansión de palabras clave: el seguimiento de versiones. Pero no es el único propósito de esto. Esa cita demuestra claramente la actitud de un hombre, pero no dice nada útil sobre el tema. Un truco político típico de "declara lo obvio de una manera exaltada", y la multitud es tuya. El problema es que la multitud, por definición, es estúpida, porque solo tiene un cerebro en todo. Que explican claramente la situación con git y la expansión de palabras clave. ¡Un idiota dijo "no" y todos aplaudieron!
AnrDaemon
1
@AnrDaemon no solo eso, git ahora ya que agregó soporte para a $Id$través del identatributo, como se menciona en otra respuesta aquí, mostrando que incluso git en sí mismo no es rehén de la opinión de Linus.
orip
21

Como he escrito antes :

Tener etiquetas de identificación generadas automáticamente que muestren un número de versión razonable es imposible de hacer con herramientas DSCM como Bazaar porque la línea de desarrollo de cada persona puede ser diferente de todas las demás. Entonces, alguien podría referirse a la versión "1.41" de un archivo, pero su versión "1.41" de ese archivo es diferente.

Básicamente, $ Id $ no tiene ningún sentido con Bazaar, Git y otras herramientas de administración de código fuente distribuido.

Bombe
fuente
66
Bien, lo leí antes de publicar, y es por eso que pedí una solución más general al problema subyacente. Creo que el deseo de que un archivo individual tenga una versión # es legítimo, al igual que la incapacidad de git para proporcionar una solución.
Joe Casadonte
No es una incapacidad de Git, es una incapacidad de todos los SCM distribuidos. Si realmente desea números de versión significativos, use Subversion, o CVS, o algún otro sistema centralizado.
Bombe
77
¿Qué tiene de malo escupir el hash en lugar de un "número de versión"? Quiero declaraciones de registro, páginas web de depuración y opciones de "versión" en scripts internos que me dirán fácilmente qué revisión se está ejecutando y dónde, para que pueda verificar ese hash específico y ver por qué se está comportando de la manera en que está. Simplifica la administración de aplicaciones implementadas ... y no quiero un enlace de confirmación que considere que cada confirmación sea un cambio en cada archivo que tenga una etiqueta $ Id $.
nairbv
1
el hash del archivo en el que está trabajando funcionaría igual que una "versión", siempre que pueda buscarlo en git por esa identificación
Erik Aronesty
2
@Brian: según la edición del OP, el usuario final quiere saber el número de versión pero no tiene acceso a git ni a los registros de git. En este caso, el hash es un número sin sentido, no un número de versión. Un DSCM no brinda asistencia para resolver esta necesidad.
Jesse Chisholm
10

Yo tuve el mismo problema. Necesitaba tener una versión más simple que una cadena hash y disponible para las personas que usan la herramienta sin necesidad de conectarse al repositorio.

Lo hice con un gancho de confirmación previa de Git y cambié mi script para poder actualizarse automáticamente.

Baso la versión en la cantidad de confirmaciones realizadas. Esta es una condición de carrera leve porque dos personas podrían comprometerse al mismo tiempo y ambas piensan que están cometiendo el mismo número de versión, pero no tenemos muchos desarrolladores en este proyecto.

Como ejemplo, tengo un script que reviso que está en Ruby, y le agrego este código: es un código bastante simple, por lo que es fácil de portar a diferentes idiomas si está ingresando algo en un idioma diferente (aunque obviamente esto no funcionará fácilmente con registros no ejecutables, como archivos de texto). He añadido:

MYVERSION = '1.090'
## Call script to do updateVersion from .git/hooks/pre-commit
def updateVersion
  # We add 1 because the next commit is probably one more - though this is a race
  commits = %x[git log #{$0} | grep '^commit ' | wc -l].to_i + 1
  vers = "1.%0.3d" % commits

  t = File.read($0)
  t.gsub!(/^MYVERSION = '(.*)'$/, "MYVERSION = '#{vers}'")
  bak = $0+'.bak'
  File.open(bak,'w') { |f| f.puts t }
  perm = File.stat($0).mode & 0xfff
  File.rename(bak,$0)
  File.chmod(perm,$0)
  exit
end

Y luego agrego una opción de línea de comandos (-updateVersion) al script, así que si lo llamo "tool -updateVersion", simplemente llama a updateVersion para la herramienta que modifica el valor "MYVERSION" en sí mismo y luego sale (podría que también actualice otros archivos si se abren y si lo desea).

Una vez que está configurado, voy al encabezado de Git y creo un script bash ejecutable de una línea .git/hooks/pre-commit.

El script simplemente cambia al encabezado del directorio Git y llama a mi script con -updateVersion .

Cada vez que reviso el script previo a la confirmación se ejecuta, que ejecuta mi script con -updateVersion, y luego la variable MYVERSION se actualiza en función de cuál será el número de confirmaciones. ¡Magia!

David Ljung Madison Stellar
fuente
Entonces, ¿su script Ruby debe llamarse updateVersion git updateVersion? Ponga algunas muestras de cómo se llama.
rjt
Hago una opción (-updateVersion) al script que estoy revisando que llama a la función 'updateVersion' (en este caso estoy tratando de cambiar el número de versión en el script en sí). Luego solo hago un comando de shell oneliner que llama a mi script con -updateVersion, y luego se actualiza antes de cada registro.
David Ljung Madison Stellar
8

Si tener $ Palabras clave $ es esencial para usted, ¿tal vez podría intentar mirar Mercurial en su lugar? Tiene una extensión hgkeyword que implementa lo que quieres. Mercurial es interesante como DVCS de todos modos.

Keltia
fuente
8

Algo que se hace con los repositorios de Git es usar el tagobjeto. Esto se puede usar para etiquetar una confirmación con cualquier tipo de cadena y se puede usar para marcar versiones. Puede ver esas etiquetas en un repositorio con el git tagcomando, que devuelve todas las etiquetas.

Es fácil ver una etiqueta. Por ejemplo, si hay una etiqueta v1.1, puede verificar esa etiqueta en una rama como esta:

git checkout -b v1.1

Como se trata de un objeto de nivel superior, verá todo el historial de esa confirmación, así como podrá ejecutar diffs, realizar cambios y fusiones.

No solo eso, sino que una etiqueta persiste, incluso si la rama en la que estaba se ha eliminado sin volver a fusionarse con la línea principal.

Abizern
fuente
66
¿Hay, entonces, una manera de que git inserte esta etiqueta en el archivo automáticamente? ¡Gracias!
Joe Casadonte
1
Si te refieres a la expansión de palabras clave? No tan lejos como sé. Si está compilando productos, puede obtener la información como parte de su script de compilación e insertarlo en algún lugar de su producto compilado. Pruebe man git-describe que proporciona la última etiqueta, el número de confirmaciones desde esta etiqueta y el hash actual.
Abizern
Sí, las etiquetas y otra información relacionada ahora se pueden editar en archivos automáticamente mediante git a través de la export-substfunción de gitattributes(5). Por supuesto, esto requiere el uso de git archivepara crear versiones, y solo en el archivo tar resultante se verán las ediciones de sustitución.
Greg A. Woods
4

Si entiendo correctamente, esencialmente, desea saber cuántas confirmaciones han ocurrido en un archivo dado desde la última actualización.

Primero obtenga los cambios en el origen remoto, pero no los combine en su masterrama:

% git fetch

Luego obtenga un registro de los cambios que ocurrieron en un archivo determinado entre su mastersucursal y el control remoto origin/master.

% git log master..origin/master foo.el

Esto le proporciona los mensajes de registro de todas las confirmaciones que han sucedido en el repositorio remoto desde la última vez que se fusionó origin/mastercon su master.

Si solo desea un recuento de los cambios, canalícelo a wc. Diga así:

% git rev-list master..origin/master foo.el | wc -l
Otón
fuente
1
Por lo tanto, no use log: git rev-list master..origin / master | wc -l
Dustin
4

Si solo desea que las personas puedan tener una idea de cuán desactualizadas están, Git puede informarles de eso de varias maneras bastante fáciles. Comparan las fechas de la última confirmación en su tronco y su tronco, por ejemplo. Pueden usargit cherry para ver cuántas confirmaciones se han producido en su troncal que no están presentes en la suya.

Si eso es todo lo que desea, buscaría una forma de proporcionarlo sin un número de versión.

Además, no me molestaría en extender la cortesía a nadie a menos que esté seguro de que lo quieren. :)

skiphoppy
fuente
Si las fechas están bien para comparar, coloque el DateTImeStamp en el archivo. git tiene muchos otros casos de uso además de los desarrolladores. TI en el campo necesita saber si el archivo .INI o .conf en la estación de trabajo que se está solucionando actualmente está cerca de lo actual.
rjt
¿Será suficiente una simple marca de tiempo? La rama incorrecta puede tener una marca de tiempo atractiva y aún así ser menos correcta.
user2066657
4

Para aplicar la expansión a todos los archivos en todos los subdirectorios en el repositorio, agregue un .gitattributesarchivo al directorio de nivel superior en el repositorio (es decir, donde normalmente colocaría el .gitignorearchivo) que contiene:

* ident

Para ver esto en efecto, primero deberá realizar un pago efectivo de los archivos, como eliminarlos o editarlos de cualquier manera. Luego restaurarlos con:

git checkout .

Y debería ver $Id$reemplazado por algo como:

$Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $

De man gitattributes:

ident

Cuando el identificador de atributo se establece para una ruta, Git reemplaza $ Id $ en el objeto blob con $ Id :, seguido del nombre del objeto blob hexadecimal de 40 caracteres, seguido de un signo de dólar $ al finalizar la compra. Cualquier secuencia de bytes que comience con $ Id: y termine con $ en el archivo del árbol de trabajo se reemplaza con $ Id $ al registrarse.

Este ID cambiará cada vez que se confirme una nueva versión del archivo.

Nagev
fuente
3

Los ID de RCS son buenos para proyectos de un solo archivo, pero para cualquier otro, $ Id $ no dice nada sobre el proyecto (a menos que haga forzados registros ficticios a un archivo de versión ficticia).

Todavía uno podría estar interesado en cómo obtener los equivalentes de $ Author $, $ Date $, $ Revision $, $ RCSfile $, etc. en un nivel por archivo o en el nivel de confirmación (cómo colocarlos donde están algunas palabras clave es otra pregunta). No tengo una respuesta sobre estos, pero veo el requisito de actualizarlos, especialmente cuando los archivos (ahora en Git) se originaron en sistemas compatibles con RCS (CVS).

Tales palabras clave pueden ser interesantes si las fuentes se distribuyen por separado de cualquier repositorio de Git (eso es lo que yo también hago). Mi solución es así:

Cada proyecto tiene un directorio propio, y en la raíz del proyecto tengo un archivo de texto llamado .version cuyo contenido describe la versión actual (el nombre que se utilizará al exportar las fuentes).

Mientras trabajaba para la próxima versión, un script extrae ese .versionnúmero, un descriptor de versión de Git (como git describe) y un número de compilación monótono .build(más host y fecha) en un archivo fuente generado automáticamente que está vinculado al programa final, para que pueda encontrar fuera de qué fuente y cuándo fue construido.

Desarrollo nuevas características en ramas separadas, y lo primero que hago es agregar n(para "siguiente") a la .versioncadena (varias ramas que se originan en la misma raíz usarían el mismo .versionnúmero temporal ). Antes del lanzamiento, decido qué ramas fusionar (espero que todas tengan lo mismo .version). Antes de comprometer la fusión, actualizo .versional siguiente número (actualización mayor o menor, dependiendo de las características fusionadas).

U. Windl
fuente
3

Si desea que el git commit información accesible en su código, entonces usted tiene que hacer una etapa de pre-construcción para llegar allí. En bash para C / C ++ podría verse así:

prebuild.sh

#!/bin/bash
commit=$(git rev-parse HEAD)
tag=$(git describe --tags --always ${commit})
cat <<EOF >version.c
#include "version.h"
const char* git_tag="${tag}";
const char* git_commit="${commit}";
EOF

con version.haspecto de:

#pragma once
const char* git_tag;
const char* git_commit;

Luego, donde lo necesite en su código #include "version.h"y referencia git_tago git_commitsegún sea necesario.

Y tu Makefilepodría tener algo como esto:

all: package
version:
  ./prebuild.sh
package: version
  # the normal build stuff for your project

Esto tiene el beneficio de:

  • obtener los valores correctos actualmente para esta compilación, independientemente de la ramificación, la fusión de la selección de cerezas y demás.

Esta implementación de prepublish.shtiene los inconvenientes de:

  • forzando una recompilación incluso si git_tag/ git_commitno cambió.
  • no tiene en cuenta los archivos modificados locales que no se han confirmado pero afectan a la compilación.
    • utilizar git describe --tags --always --dirtypara atrapar ese caso de uso.
  • contamina el espacio de nombres global.

Un aficionado prebuild.shque podría evitar estos problemas se deja como ejercicio para el lector.

Jesse Chisholm
fuente
1

Estoy de acuerdo con aquellos que piensan que el reemplazo de tokens pertenece a herramientas de compilación en lugar de herramientas de control de versiones.

Debería tener alguna herramienta de lanzamiento automatizada para configurar los ID de versión en sus fuentes al momento de etiquetar el lanzamiento.

Agustí Sánchez
fuente
2
.INI .conf y .txt no suelen tener una herramienta de compilación.
rjt
Pero puede hackear un script de lanzamiento que toma la etiqueta Git actual y la escribe en un archivo, o algo por el estilo.
Marnen Laibow-Koser el
1

Los nombres de etiquetas y otra información relacionada ahora se pueden editar directamente en archivos automáticamente por Git a través de la export-substfunción de gitattributes(5). Por supuesto, esto requiere el uso de git archivepara crear versiones, y solo en el archivo tar resultante se verán las ediciones de sustitución.

Por ejemplo en el .gitattributesarchivo pon la siguiente línea:

* export-subst

Luego, en los archivos de origen, puede agregar una línea como esta:

#ident  "@(#)PROJECTNAME:FILENAME:$Format:%D:%ci:%cN:%h$"

Y se expandirá para verse así en una versión creada, por ejemplo, por git archive v1.2.0.90:

#ident  "@(#)PROJECTNAME:FILENAME:HEAD -> master, tag: v1.2.0.90:2020-04-03 18:40:44 -0700:Greg A. Woods:e48f949"
Greg A. Woods
fuente
0

Como usa Emacs, puede que tenga suerte :)

Me encontré con esta pregunta por coincidencia, y también por coincidencia, encontré Lively hace unos días, un paquete de Emacs que permite tener piezas vivas de Emacs Lisp en su documento. No lo he intentado para ser honesto, pero me vino a la mente al leer esto.

Amr Mostafa
fuente
0

También vine de SCCS, RCS y CVS ( %W% %G% %U%).

Tuve un desafío similar. Quería saber qué versión tenía un código en cualquier sistema que lo ejecutara. El sistema puede o no estar conectado a ninguna red. El sistema puede o no tener instalado Git. El sistema puede o no tener el repositorio de GitHub instalado en él.

Quería la misma solución para varios tipos de código (.sh, .go, .yml, .xml, etc.). Quería que cualquier persona sin conocimiento de Git o GitHub pudiera responder la pregunta "¿Qué versión está ejecutando?"

Entonces, escribí lo que llamo un contenedor alrededor de algunos comandos Git. Lo uso para marcar un archivo con un número de versión y alguna información. Resuelve mi desafío. Te puede ayudar.

https://github.com/BradleyA/markit

git clone https://github.com/BradleyA/markit
cd markit
Bradley Allen
fuente
0

Para resolver este problema por mí mismo, creé un pequeño "hack" como enlace posterior a la confirmación:

echo | tee --append *
git checkout *

En más detalle documentado en esta publicación en mi blog .

Richard Ostrochovský
fuente