En Git, ¿cómo puedo escribir el hash de confirmación actual en un archivo en la misma confirmación?

131

Estoy tratando de hacer cosas elegantes aquí con ganchos Git, pero realmente no sé cómo hacerlo (o si es posible).

Lo que necesito hacer es: en cada commit quiero tomar su hash y luego actualizar un archivo en el commit con este hash.

¿Algunas ideas?

Felipe Kamakura
fuente
12
Básicamente tengo una aplicación web y quiero asociar una versión instalada de esa aplicación con la confirmación exacta a la que está asociada esa versión. Mi idea inicial era actualizar una especie de archivo about.html con el hash commit. Pero después de estudiar el modelo de objetos de git, me di cuenta de que esto es algo imposible = /
Felipe Kamakura
29
Este es un problema muy práctico. Me encontré con él también!
Li Dong
77
En cuanto a mí, me gustaría que mi programa escriba un mensaje como este en los registros: "myprog start up, v.56c6bb2". De esa manera, si alguien presenta un error y me envía los archivos de registro, puedo averiguar exactamente qué versión de mi programa se estaba ejecutando.
Edward Falk
55
@Jefromi, el caso de uso real es muy común y afecta a los principiantes con mucha facilidad. Tener la versión real "impresa" de alguna manera en archivos de línea base es una necesidad básica, y está lejos de ser obvio por qué sería una idea incorrecta, por ejemplo, porque esa es prácticamente su única opción con los hacks de control de revisión manual. (Recuerde a los principiantes). Agregue a eso que muchos proyectos simplemente no tienen ningún tipo de paso de compilación / instalación / implementación que pueda capturar y sellar la versión en archivos en vivo. Independientemente, en lugar de pre-commit, el gancho posterior al pago podría ayudar incluso en esos casos.
Sz.
¡Esto es imposible! Si puede hacer esto, rompió el algoritmo hash SHA-1 ... ericsink.com/vcbe/html/cryptographic_hashes.html
betontalpfa

Respuestas:

82

Recomendaría hacer algo similar a lo que tiene en mente: colocar el SHA1 en un archivo sin seguimiento , generado como parte del proceso de construcción / instalación / implementación. Obviamente es fácil de hacer ( git rev-parse HEAD > filenameo tal vez git describe [--tags] > filename), y evita hacer algo loco como terminar con un archivo que es diferente del seguimiento de git.

Su código puede hacer referencia a este archivo cuando necesita el número de versión, o un proceso de compilación podría incorporar la información en el producto final. Esto último es en realidad cómo git mismo obtiene sus números de versión: el proceso de compilación toma el número de versión del repositorio, luego lo construye en el ejecutable.

Cascabel
fuente
3
¿Podría alguien exponer más con un paso a paso sobre cómo hacer esto? ¿O al menos un empujón en la dirección correcta?
Joel Worsham
1
@ Joo ¿Cómo hacer qué? Mencioné cómo poner el hash en un archivo; el resto es presumiblemente algo sobre su proceso de construcción? Tal vez una nueva pregunta si está tratando de preguntar sobre esa parte.
Cascabel
1
En mi caso, agregué una regla a mi Makefile que genera un archivo "gitversion.h" en cada compilación. Ver stackoverflow.com/a/38087913/338479
Edward Falk
1
Es posible que pueda automatizar esto con un gancho "git-checkout". El problema es que los ganchos tendrían que instalarse manualmente.
Edward Falk
14

Es imposible escribir el hash de confirmación actual: si logra calcular previamente el hash de confirmación futuro, cambiará tan pronto como modifique cualquier archivo.

Sin embargo, hay tres opciones:

  1. Use un script para incrementar 'commit id' e inclúyalo en alguna parte. Feo
  2. .gitignore el archivo en el que va a almacenar el hash. No muy práctico
  3. En pre-commit, almacene el hash de confirmación anterior :) No modifica / inserta confirmaciones en el 99,99% de los casos, por lo tanto, esto FUNCIONARÁ. En el peor de los casos, aún puede identificar la revisión de origen.

Estoy trabajando en un script de gancho, lo publicaré aquí 'cuando esté terminado', pero aún así, antes de que se publique Duke Nukem Forever :))

Actualización : código para .git/hooks/pre-commit:

#!/usr/bin/env bash
set -e

#=== 'prev-commit' solution by o_O Tync
#commit_hash=$(git rev-parse --verify HEAD)
commit=$(git log -1 --pretty="%H%n%ci") # hash \n date
commit_hash=$(echo "$commit" | head -1)
commit_date=$(echo "$commit" | head -2 | tail -1) # 2010-12-28 05:16:23 +0300

branch_name=$(git symbolic-ref -q HEAD) # http://stackoverflow.com/questions/1593051/#1593487
branch_name=${branch_name##refs/heads/}
branch_name=${branch_name:-HEAD} # 'HEAD' indicates detached HEAD situation

# Write it
echo -e "prev_commit='$commit_hash'\ndate='$commit_date'\nbranch='$branch'\n" > gitcommit.py

Ahora lo único que necesitamos es una herramienta que convierta el prev_commit,branchpar en un hash de confirmación real :)

No sé si este enfoque puede distinguir los compromisos de fusión. Lo comprobaré pronto

Kolypto
fuente
13

Alguien me señaló la sección "man gitattributes" en ident, que tiene esto:

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 comienza con $ Id: y termina con $ en el archivo de árbol de trabajo se reemplaza con $ Id $ al registrarse.

Si lo piensa, esto es lo que también hacen CVS, Subversion, etc. Si observa el repositorio, verá que el archivo en el repositorio siempre contiene, por ejemplo, $ Id $. Nunca contiene la expansión de eso. Solo al finalizar la compra se expande el texto.

Baron Schwartz
fuente
8
identes el hash para el archivo en sí, no el más rápido del commit. De git-scm.com/book/en/… : "Sin embargo, ese resultado es de uso limitado. Si ha utilizado la sustitución de palabras clave en CVS o Subversion, puede incluir un sello de fecha; el SHA no es tan útil, porque es bastante aleatorio y no se puede saber si un SHA es más antiguo o más nuevo que otro ". filtertoma trabajo, pero puede obtener la información de confirmación dentro (y fuera) de un archivo.
Zach Young
11

Esto se puede lograr usando el filteratributo en gitattributes . Tendría que proporcionar un smudgecomando que inserte el ID de confirmación y un cleancomando que lo elimine, de modo que el archivo en el que se inserta no cambie solo por el ID de confirmación.

Por lo tanto, la identificación de confirmación nunca se almacena en el blob del archivo; simplemente se expande en tu copia de trabajo. (En realidad, insertar el id de confirmación en el blob se convertiría en una tarea infinitamente recursiva. ☺) Cualquiera que clone este árbol necesitaría configurar los atributos por sí misma.

legoscia
fuente
77
Tarea imposible , no tarea recursiva. El hash de confirmación depende del hash del árbol que depende del hash del archivo, que depende del contenido del archivo. Tienes que tener autoconsistencia. A menos que encuentre una especie de punto fijo [generalizado] para el hash SHA-1.
Jakub Narębski
1
@Jakub, ¿hay algún tipo de truco en git que permita crear archivos rastreados que no modifiquen el hash resultante? Alguna forma de anular su hash, tal vez. Esa será una solución :)
kolypto
@o_O Tync: no es posible. Archivo modificado significa hash modificado (de un archivo): esto es por diseño y por definición de una función hash.
Jakub Narębski
2
Esta es una solución bastante buena, pero tenga en cuenta que esto implica ganchos que deben instalarse manualmente cada vez que clone un repositorio.
Edward Falk
7

¡Piensa fuera del cuadro de confirmación!

ingrese esto en los ganchos de archivo / post-pago

#!/bin/sh
git describe --all --long > config/git-commit-version.txt

La versión estará disponible donde sea que la use.

Keith Patrick
fuente
3

No creo que realmente quieras hacer eso, porque cuando se cambia un archivo en el commit, el hash del commit también cambia.

midtiby
fuente
1

Permítanme explorar por qué este es un problema desafiante usando los componentes internos de git. Puede obtener el sha1 de la confirmación actual mediante

#!/bin/bash
commit=$(git cat-file commit HEAD) #
sha1=($((printf "commit %s\0" $(echo "$commit" | wc -c); echo "$commit") | sha1sum))
echo ${sha1[0]}

Esencialmente ejecuta una suma de comprobación sha1 en el mensaje devuelto por git cat-file commit HEAD. Dos cosas saltan inmediatamente como un problema cuando examinas este mensaje. Uno es el árbol sha1 y el segundo es el tiempo de confirmación.

Ahora, el tiempo de confirmación se soluciona fácilmente alterando el mensaje y adivinando cuánto tiempo lleva realizar una confirmación o una programación para confirmar en un momento específico. El verdadero problema es el árbol sha1, del que puede obtener git ls-tree $(git write-tree) | git mktree. Esencialmente, está haciendo una suma de verificación sha1 en el mensaje de ls-tree, que es una lista de todos los archivos y su suma de verificación sha1.

Por lo tanto, su suma de comprobación commit sha1 depende de la suma de comprobación de su árbol sha1, que depende directamente de la suma de comprobación de archivos sha1, que completa el círculo y depende de commit sha1. Por lo tanto, tiene un problema circular con las técnicas disponibles para mí.

Con sumas de verificación menos seguras , se ha demostrado que es posible escribir la suma de verificación del archivo en el mismo archivo mediante fuerza bruta; sin embargo, no conozco ningún trabajo que haya logrado esa tarea con sha1. Esto no es imposible, pero casi imposible con nuestra comprensión actual (pero quién sabe, tal vez en un par de años será trivial). Sin embargo, esto es aún más difícil de aplicar debido a la fuerza bruta, ya que tiene que escribir la suma de verificación (commit) de una suma de verificación (árbol) de una suma de verificación (blob) en el archivo.

Novato C
fuente
¿Hay alguna manera de que uno pueda confirmar los archivos, luego hacer un pago y tener el último hash de confirmación colocado como un comentario al comienzo de cada archivo de código fuente? Entonces construir y correr de eso?
John Wooten