In Git, come posso scrivere l'hash di commit corrente in un file nello stesso commit


131

Sto provando a fare cose fantasiose qui con i ganci Git, ma non so davvero come farlo (o se è possibile).

Quello che devo fare è: in ogni commit voglio prendere il suo hash e quindi aggiornare un file nel commit con questo hash.

Qualche idea?


12
Fondamentalmente ho un'applicazione web e voglio associare una versione installata di quell'applicazione al commit esatto a cui è associata la versione. La mia idea iniziale era quella di aggiornare una sorta di file about.html con l'hash di commit. Ma dopo aver studiato il modello di oggetti di Git, mi sono reso conto che questo è un po 'impossibile = /
Felipe Kamakura,

29
Questo è un problema molto pratico. Ci sono stato anche io!
Li Dong

7
Per quanto mi riguarda, vorrei che il mio programma scrivesse un messaggio come questo nei registri: "myprog start up, v.56c6bb2". In questo modo, se qualcuno presenta un bug e mi invia i file di registro, posso scoprire esattamente quale versione del mio programma era in esecuzione.
Edward Falk,

5
@Jefromi, il caso d'uso reale è in effetti molto comune e colpisce i principianti molto facilmente. Avere la versione reale in qualche modo "impressa" in file di base è un'esigenza fondamentale, ed è tutt'altro che ovvio perché sarebbe un'idea sbagliata, ad esempio perché è praticamente l'unica opzione con gli hack del controllo di revisione manuale. (Ricordate i principianti.) Aggiungete a ciò che molti progetti semplicemente non hanno alcun tipo di fase di compilazione / installazione / distribuzione che potrebbe catturare e timbrare la versione in file live. Indipendentemente da ciò, invece del pre-commit, l'hook post-checkout potrebbe aiutare anche in quei casi.
Sz.

Questo è impossibile! Se riesci a farlo, hai rotto l'algoritmo hash SHA-1 ... ericsink.com/vcbe/html/cryptographic_hashes.html
betontalpfa

Risposte:


82

Consiglierei di fare qualcosa di simile a quello che hai in mente: posizionare SHA1 in un file non tracciato , generato come parte del processo di compilazione / installazione / distribuzione. È ovviamente facile da fare ( git rev-parse HEAD > filenameo forse git describe [--tags] > filename), ed evita di fare qualcosa di folle come finire con un file diverso dal tracciamento di Git.

Il codice può quindi fare riferimento a questo file quando è necessario il numero di versione o un processo di generazione potrebbe incorporare le informazioni nel prodotto finale. Quest'ultimo è in realtà il modo in cui git stesso ottiene i suoi numeri di versione: il processo di compilazione estrae il numero di versione dal repository, quindi lo crea nell'eseguibile.


3
Qualcuno potrebbe esporre ulteriormente con un passo dopo passo su come farlo? O almeno una spinta nella giusta direzione?
Joel Worsham,

1
@Joel Come fare cosa? Ho menzionato come mettere l'hash in un file; il resto è presumibilmente qualcosa nel tuo processo di compilazione? Forse una nuova domanda se stai cercando di porre domande su quella parte.
Cascabel,

1
Nel mio caso, ho aggiunto una regola al mio Makefile che genera un file "gitversion.h" su ogni build. Vedi stackoverflow.com/a/38087913/338479
Edward Falk il

1
Potresti essere in grado di automatizzare questo con un hook "git-checkout". Il problema è che i ganci dovrebbero essere installati manualmente.
Edward Falk,

14

È impossibile scrivere l'hash di commit corrente: se riesci a pre-calcolare l'hash di commit futuro - cambierà non appena modifichi qualsiasi file.

Tuttavia, ci sono tre opzioni:

  1. Utilizzare uno script per incrementare "commit id" e includerlo da qualche parte. Brutta
  2. .gitignore il file in cui archiverai l'hash. Non molto utile
  3. In pre-commit, archivia l' hash di commit precedente :) Non modificare / inserire i commit nel 99,99% dei casi, quindi, funzionerà. Nel peggiore dei casi è ancora possibile identificare la revisione della fonte.

Sto lavorando a uno script hook, lo pubblicherò qui 'quando avrà finito', ma comunque - prima che Duke Nukem Forever venga rilasciato :))

Aggiornamento : codice per .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

Ora l'unica cosa di cui abbiamo bisogno è uno strumento che converta la prev_commit,branchcoppia in un vero hash di commit :)

Non so se questo approccio sia in grado di distinguere gli impegni di fusione. Lo verificherò presto


13

Qualcuno mi ha indicato la sezione "man gitattributes" su ident, che ha questo:

ident

Quando l'attributo ident è impostato per un percorso, git sostituisce $ Id $ nell'oggetto BLOB con $ Id :, seguito dal nome dell'oggetto BLOB esadecimale di 40 caratteri, seguito da un simbolo di dollaro $ al momento del pagamento. Qualsiasi sequenza di byte che inizia con $ Id: e termina con $ nel file worktree viene sostituita con $ Id $ al momento del check-in.

Se ci pensate, questo è ciò che fanno anche CVS, Subversion, ecc. Se guardi il repository, vedrai che il file nel repository contiene sempre, ad esempio, $ Id $. Non contiene mai l'espansione di quello. È solo alla cassa che il testo viene espanso.


8
identè l'hash per il file stesso, non l'hast del commit. Da git-scm.com/book/en/… : "Tuttavia, quel risultato è di uso limitato. Se hai utilizzato la sostituzione di parole chiave in CVS o Subversion, puoi includere un datestamp: lo SHA non è poi così utile, perché è abbastanza casuale e non si può dire se uno SHA è più vecchio o più recente di un altro. " filterfunziona, ma può ottenere le informazioni di commit in (e fuori) da un file.
Zach Young,

11

Ciò può essere ottenuto utilizzando l' filterattributo in gitattributes . Dovresti fornire un smudgecomando che inserisce l'id di commit e un cleancomando che lo rimuova, in modo tale che il file in cui è inserito non cambierà solo a causa dell'ID di commit.

Pertanto, l'ID commit non viene mai archiviato nel BLOB del file; è appena stato ampliato nella tua copia di lavoro. (L'inserimento effettivo dell'ID commit nel BLOB diventerebbe un compito infinitamente ricorsivo. ☺) Chiunque cloni questo albero dovrebbe impostare gli attributi per se stesso.


7
Compito impossibile , non ricorsivo. L'hash di commit dipende dall'hash dell'albero che dipende dall'hash del file, che dipende dal contenuto del file. Devi ottenere coerenza. A meno che non trovi una sorta di punto fisso [generalizzato] per l'hash SHA-1.
Jakub Narębski,

1
@Jakub, c'è qualche tipo di trucco in git che permetterà di creare file tracciati che non modificano l'hash risultante? Forse un modo per sovrascrivere il suo hash. Sarà una soluzione :)
kolypto il

@o_O Tync: impossibile. File modificato significa hash modificato (di un file) - questo è in base alla progettazione e per definizione di una funzione hash.
Jakub Narębski,

2
Questa è una soluzione abbastanza buona, ma tieni presente che si tratta di hook che devono essere installati manualmente ogni volta che cloni un repository.
Edward Falk,

7

Pensa fuori dagli schemi!

inseriscilo nei file hook / post-checkout

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

La versione sarà disponibile ovunque tu la usi.


3

Non penso che tu voglia farlo, perché quando un file nel commit viene modificato, anche l'hash del commit viene modificato.


1

Permettetemi di esplorare perché questo è un problema difficile usando gli interni git. È possibile ottenere lo sha1 del commit corrente da

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

Essenzialmente si esegue un checksum sha1 sul messaggio restituito da git cat-file commit HEAD. Due cose saltano immediatamente fuori come un problema quando si esamina questo messaggio. Uno è l'albero sha1 e il secondo è il tempo di commit.

Ora è possibile gestire facilmente il tempo di commit modificando il messaggio e indovinando quanto tempo ci vuole per effettuare un commit o una pianificazione per eseguire il commit in un momento specifico. Il vero problema è l'albero sha1, da cui è possibile ottenere git ls-tree $(git write-tree) | git mktree. Essenzialmente stai facendo un checksum sha1 sul messaggio da ls-tree, che è un elenco di tutti i file e il loro checksum sha1.

Pertanto il checksum commit sha1 dipende dal checksum sha1 dell'albero, che dipende direttamente dai file checksum sha1 dei file, che completa il cerchio e dipende dal commit sha1. Quindi hai un problema circolare con le tecniche a mia disposizione.

Con checksum meno sicuri , è stato dimostrato che è possibile scrivere il checksum del file nel file stesso tramite forza bruta; tuttavia, non conosco alcun lavoro che abbia portato a termine questo compito con sha1. Questo non è impossibile, ma quasi impossibile con la nostra attuale comprensione (ma chissà forse tra un paio d'anni sarà banale). Tuttavia, è ancora più difficile forzare la forza poiché è necessario scrivere il checksum (commit) di un checksum (albero) di un checksum (BLOB) nel file.


C'è un modo in cui è possibile eseguire il commit dei file, quindi fare un checkout e disporre dell'ultimo hash di commit come commento all'inizio di ogni file di codice sorgente? Quindi costruire ed eseguire da quello?
John Wooten,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.