Wie kann ich in Git den aktuellen Commit-Hash in eine Datei im selben Commit schreiben?

130

Ich versuche hier ein ausgefallenes Zeug mit Git-Hooks zu machen, aber ich weiß nicht wirklich, wie ich es machen soll (oder ob es möglich ist).

Was ich tun muss, ist: In jedem Commit möchte ich seinen Hash nehmen und dann eine Datei im Commit mit diesem Hash aktualisieren.

Irgendwelche Ideen?

Felipe Kamakura
quelle
12
Grundsätzlich habe ich eine Webanwendung und möchte eine installierte Version dieser Anwendung mit dem genauen Commit verknüpfen, dem diese Version zugeordnet ist. Meine ursprüngliche Idee war es, eine Art about.html-Datei mit dem Commit-Hash zu aktualisieren. Aber nachdem ich das Objektmodell von git studiert hatte, wurde mir klar, dass dies irgendwie unmöglich ist = /
Felipe Kamakura
29
Dies ist ein sehr praktisches Problem. Ich bin auch darauf gestoßen!
Li Dong
7
Ich möchte, dass mein Programm eine Nachricht wie diese in die Protokolle schreibt: "myprog wird gestartet, v.56c6bb2". Auf diese Weise, wenn jemand einen Fehler Dateien und schickt mir die Log - Dateien, kann ich herausfinden, genau , was Version meines Programms ausgeführt wurde.
Edward Falk
5
@Jefromi, der eigentliche Anwendungsfall ist in der Tat sehr häufig und trifft Anfänger sehr leicht. Die reale Version irgendwie in Basisdateien "eingeprägt" zu haben, ist ein Grundbedürfnis, und es ist alles andere als offensichtlich, warum dies eine falsche Idee wäre, z. B. weil dies so ziemlich Ihre einzige Option bei manuellen Revisionskontroll-Hacks ist. (Denken Sie an Anfänger.) Hinzu kommt, dass viele Projekte einfach keinen Build- / Installations- / Bereitstellungsschritt haben, der die Version in Live-Dateien erfassen und stempeln könnte. Unabhängig davon kann der Post-Checkout-Hook anstelle des Pre-Commits auch in diesen Fällen hilfreich sein.
Gr.
Das ist unmöglich! Wenn Sie dies tun können, haben Sie den SHA-1-Hash-Algorithmus gebrochen
führenalpfa

Antworten:

81

Ich würde empfehlen, etwas Ähnliches zu tun, wie Sie es sich vorgestellt haben: Platzieren des SHA1 in einer nicht verfolgten Datei, die im Rahmen des Erstellungs- / Installations- / Bereitstellungsprozesses generiert wird. Es ist offensichtlich einfach zu tun (git rev-parse HEAD > filename oder vielleicht git describe [--tags] > filename) und es vermeidet es, etwas Verrücktes zu tun, wie eine Datei zu erhalten, die sich von der Verfolgung durch Git unterscheidet.

Ihr Code kann dann auf diese Datei verweisen, wenn er die Versionsnummer benötigt, oder ein Erstellungsprozess kann die Informationen in das Endprodukt integrieren. Letzteres ist tatsächlich, wie Git selbst seine Versionsnummern erhält - der Erstellungsprozess holt die Versionsnummer aus dem Repo und baut sie dann in die ausführbare Datei ein.

Cascabel
quelle
3
Könnte jemand Schritt für Schritt näher erläutern, wie das geht? Oder zumindest einen Schubs in die richtige Richtung?
Joel Worsham
1
@ Joel Wie mache ich was? Ich erwähnte, wie man den Hash in eine Datei einfügt. Der Rest ist vermutlich etwas über Ihren Build-Prozess? Vielleicht eine neue Frage, wenn Sie versuchen, nach diesem Teil zu fragen.
Cascabel
1
In meinem Fall habe ich meinem Makefile eine Regel hinzugefügt, die bei jedem Build eine "gitversion.h" -Datei generiert. Siehe stackoverflow.com/a/38087913/338479
Edward Falk
1
Möglicherweise können Sie dies mit einem "Git-Checkout" -Hook automatisieren. Das Problem ist, dass die Haken manuell installiert werden müssten.
Edward Falk
14

Es ist unmöglich, den aktuellen Commit-Hash zu schreiben: Wenn Sie den zukünftigen Commit-Hash vorberechnen können, ändert sich dieser, sobald Sie eine Datei ändern.

Es gibt jedoch drei Möglichkeiten:

  1. Verwenden Sie ein Skript, um die Festschreibungs-ID zu erhöhen und irgendwo einzufügen. Hässlich
  2. .gitignore die Datei, in der Sie den Hash speichern möchten. Nicht sehr praktisch
  3. In pre-commitspeichern die vorherigen Hash begehen :) Sie nicht ändern / Insert - Commits in 99,99% der Fälle, so, dies funktionieren wird. Im schlimmsten Fall können Sie die Quellrevision immer noch identifizieren.

Ich arbeite an einem Hook-Skript, werde es hier veröffentlichen, wenn es fertig ist, aber immer noch - früher als Duke Nukem Forever veröffentlicht wird :))

Update : Code für .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

Jetzt brauchen wir nur noch ein Tool, das prev_commit,branchPaare in einen echten Commit-Hash konvertiert :)

Ich weiß nicht, ob dieser Ansatz das Zusammenführen von Commits voneinander unterscheiden kann. Werde es bald überprüfen

kolypto
quelle
13

Jemand hat mich auf den Abschnitt "man gitattributes" auf ident hingewiesen, der Folgendes enthält:

ident

Wenn das Attribut ident für einen Pfad festgelegt ist, ersetzt git $ Id $ im Blob-Objekt durch $ Id:, gefolgt vom 40-stelligen hexadezimalen Blob-Objektnamen, gefolgt von einem Dollarzeichen $ beim Auschecken. Jede Byte-Sequenz, die mit $ Id: beginnt und in der Arbeitsbaumdatei mit $ endet, wird beim Einchecken durch $ Id $ ersetzt.

Wenn Sie darüber nachdenken, ist dies auch das, was CVS, Subversion usw. tun. Wenn Sie sich das Repository ansehen, werden Sie feststellen, dass die Datei im Repository immer beispielsweise $ Id $ enthält. Es enthält nie die Erweiterung davon. Nur beim Auschecken wird der Text erweitert.

Baron Schwartz
quelle
8
identist der Hash für die Datei selbst, nicht der Hast des Commits. Von git-scm.com/book/en/… : "Dieses Ergebnis ist jedoch von begrenztem Nutzen. Wenn Sie die Schlüsselwortersetzung in CVS oder Subversion verwendet haben, können Sie einen Datenstempel einfügen - der SHA ist nicht allzu hilfreich. weil es ziemlich zufällig ist und man nicht sagen kann, ob ein SHA älter oder neuer als ein anderer ist. " filternimmt Arbeit in Anspruch, kann aber die Festschreibungsinformationen in eine Datei (und aus dieser heraus) übertragen.
Zach Young
11

Dies kann erreicht werden, indem das filterAttribut in gitattributes verwendet wird . Sie müssten einen smudgeBefehl angeben, der die Festschreibungs-ID einfügt, und aclean Befehl, der sie entfernt, sodass sich die Datei, in die sie eingefügt wird, nicht nur aufgrund der Festschreibungs-ID ändert.

Daher wird die Festschreibungs-ID niemals im Blob der Datei gespeichert. Es ist nur in Ihrer Arbeitskopie erweitert. (Das Einfügen der Commit-ID in den Blob wird zu einer unendlich rekursiven Aufgabe. ☺) Jeder, der diesen Baum klont, muss die Attribute für sich selbst einrichten.

Legoscia
quelle
7
Unmögliche Aufgabe, keine rekursive Aufgabe. Der Commit-Hash hängt vom Baum-Hash ab, der vom Datei-Hash und vom Dateiinhalt abhängt. Sie müssen Selbstkonsistenz bekommen. Es sei denn, Sie finden eine Art [verallgemeinerten] Fixpunkt für SHA-1-Hash.
Jakub Narębski
1
@ Jakub, gibt es eine Art Trick in Git, mit dem verfolgte Dateien erstellt werden können, die den resultierenden Hash nicht ändern? Vielleicht eine Möglichkeit, seinen Hash zu überschreiben. Das wird eine Lösung sein :)
kolypto
@o_O Tync: Nicht möglich. Geänderte Datei bedeutet geänderter Hash (einer Datei) - dies ist beabsichtigt und per Definition einer Hash-Funktion.
Jakub Narębski
2
Dies ist eine ziemlich gute Lösung, aber denken Sie daran, dass dies Hooks beinhaltet, die manuell installiert werden müssen, wenn Sie ein Repository klonen.
Edward Falk
7

Denken Sie außerhalb des Commit-Bereichs!

Pop dies in die Datei Hooks / Post-Checkout

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

Die Version ist überall verfügbar, wo Sie sie verwenden.

Keith Patrick
quelle
3

Ich glaube nicht, dass Sie das wirklich tun möchten, denn wenn eine Datei im Commit geändert wird, wird auch der Hash des Commits geändert.

Midtiby
quelle
1

Lassen Sie mich anhand der Git-Interna untersuchen, warum dies ein herausforderndes Problem ist. Sie können den sha1 des aktuellen Commits von abrufen

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

Im Wesentlichen führen Sie eine sha1-Prüfsumme für die von zurückgegebene Nachricht aus git cat-file commit HEAD. Zwei Dinge treten sofort als Problem auf, wenn Sie diese Nachricht untersuchen. Einer ist der Baum sha1 und der zweite ist die Festschreibungszeit.

Jetzt kann die Festschreibungszeit leicht behoben werden, indem die Nachricht geändert und erraten wird, wie lange es dauert, eine Festschreibung durchzuführen oder eine Festschreibung zu einem bestimmten Zeitpunkt zu planen. Das wahre Problem ist der Baum sha1, von dem Sie erhalten können git ls-tree $(git write-tree) | git mktree. Im Wesentlichen führen Sie eine sha1-Prüfsumme für die Nachricht von ls-tree durch, bei der es sich um eine Liste aller Dateien und ihrer sha1-Prüfsumme handelt.

Daher hängt Ihre Commit-sha1-Prüfsumme von Ihrer Baum-sha1-Prüfsumme ab, die direkt von der Datei sha1-Prüfsumme abhängt, die den Kreis schließt und von der Commit-sha1 abhängt. Sie haben also ein zirkuläres Problem mit Techniken, die mir zur Verfügung stehen.

Mit weniger sicheren Prüfsummen wurde gezeigt, dass es möglich ist, die Prüfsumme der Datei mit brutaler Gewalt in die Datei selbst zu schreiben. Ich kenne jedoch keine Arbeit, die diese Aufgabe mit sha1 erfüllt hat. Dies ist nicht unmöglich, aber nach unserem derzeitigen Verständnis nahezu unmöglich (aber wer weiß, dass es in ein paar Jahren vielleicht trivial sein wird). Dies ist jedoch noch schwieriger zu erzwingen, da Sie die (Commit-) Prüfsumme einer (Baum-) Prüfsumme einer (Blob-) Prüfsumme in die Datei schreiben müssen.

Anfänger C.
quelle
Gibt es eine Möglichkeit, die Dateien festzuschreiben, dann eine Kasse durchzuführen und den neuesten Festschreibungs-Hash als Kommentar am Anfang jeder Quellcodedatei zu platzieren? Dann bauen und davon rennen?
John Wooten