W Git, jak mogę zapisać bieżący skrót zatwierdzenia do pliku w tym samym zatwierdzeniu

138

Próbuję tutaj zrobić wymyślne rzeczy z hookami Gita, ale tak naprawdę nie wiem, jak to zrobić (lub jeśli to możliwe).

To, co muszę zrobić, to: w każdym zatwierdzeniu chcę wziąć jego hash, a następnie zaktualizować plik w zatwierdzeniu tym hashem.

Jakieś pomysły?

Felipe Kamakura
źródło
12
Zasadniczo mam aplikację internetową i chcę powiązać zainstalowaną wersję tej aplikacji z dokładnym zatwierdzeniem, z którym ta wersja jest powiązana. Moim początkowym pomysłem było zaktualizowanie pewnego rodzaju pliku about.html za pomocą skrótu zatwierdzenia. Ale po przestudiowaniu modelu obiektów gita, zdałem sobie sprawę, że jest to trochę niemożliwe = /
Felipe Kamakura
31
To bardzo praktyczny problem. Ja też na to wpadłem!
Li Dong
7
Jeśli chodzi o mnie, chciałbym, aby mój program zapisywał w logach taki komunikat: "myprog start up, v.56c6bb2". W ten sposób, jeśli ktoś zgłosi błąd i prześle mi pliki dziennika, mogę dowiedzieć się , jaka wersja mojego programu była uruchomiona.
Edward Falk
5
@Jefromi, rzeczywisty przypadek użycia jest w rzeczywistości bardzo powszechny i ​​bardzo łatwo trafia do początkujących. Posiadanie prawdziwej wersji w jakiś sposób "odciśniętej" w plikach bazowych jest podstawową potrzebą i nie jest oczywiste, dlaczego byłby to zły pomysł, np. Ponieważ jest to właściwie jedyna opcja z ręcznymi metodami kontroli wersji. (Pamiętaj o początkujących.) Dodaj do tego, że wiele projektów po prostu nie ma żadnego etapu kompilacji / instalacji / wdrażania, który mógłby pobrać i stemplować wersję w aktywnych plikach. Niezależnie od tego, zamiast pre-commit, hook post-checkout mógłby pomóc nawet w takich przypadkach.
Sz.
1
To jest niemożliwe! Jeśli możesz to zrobić, złamałeś algorytm skrótu SHA-1 ... ericsink.com/vcbe/html/cryptographic_hashes.html
betontalpfa

Odpowiedzi:

86

Poleciłbym zrobić coś podobnego do tego, co masz na myśli: umieszczenie SHA1 w nieśledzonym pliku, generowanym w ramach procesu budowania / instalacji / wdrażania. Jest to oczywiście łatwe do zrobienia ( git rev-parse HEAD > filenamea może git describe [--tags] > filename) i pozwala uniknąć zrobienia czegoś szalonego, na przykład skończenia z plikiem, który różni się od tego, co śledzi git.

Twój kod może następnie odwoływać się do tego pliku, gdy potrzebuje numeru wersji, lub proces kompilacji może uwzględnić te informacje w produkcie końcowym. To ostatnie jest w rzeczywistości sposobem, w jaki sam git otrzymuje numery wersji - proces budowania pobiera numer wersji z repozytorium, a następnie wbudowuje go w plik wykonywalny.

Cascabel
źródło
3
Czy mógłby ktoś dalej wyjaśnić krok po kroku, jak to zrobić? A przynajmniej szturchnięcie we właściwym kierunku?
Joel Worsham
1
@Joel Jak co zrobić? Wspomniałem, jak umieścić hash w pliku; reszta jest prawdopodobnie związana z procesem tworzenia? Może nowe pytanie, jeśli próbujesz zapytać o tę część.
Cascabel
1
W moim przypadku dodałem regułę do mojego Makefile, która generuje plik "gitversion.h" przy każdej kompilacji. Zobacz stackoverflow.com/a/38087913/338479
Edward Falk
1
Możesz to zautomatyzować za pomocą haka „git-checkout”. Problem w tym, że haki musiałyby być instalowane ręcznie.
Edward Falk
15

Nie można zapisać aktualnego skrótu zatwierdzenia: jeśli uda Ci się wstępnie obliczyć wartość skrótu przyszłego zatwierdzenia - zmieni się on, gdy tylko zmodyfikujesz dowolny plik.

Istnieją jednak trzy opcje:

  1. Użyj skryptu, aby zwiększyć „identyfikator zatwierdzenia” i umieścić go gdzieś. Brzydki
  2. .gitignore plik, w którym zamierzasz przechowywać hash. Niezbyt poręczne
  3. W pre-commit, zapisz poprzedni hash zatwierdzenia :) Nie modyfikujesz / nie wstawiasz zatwierdzeń w 99,99% przypadków, więc to BĘDZIE działało. W najgorszym przypadku nadal możesz zidentyfikować wersję źródłową.

Pracuję nad skryptem przechwytującym, wrzucę go tutaj 'po zakończeniu', ale nadal - przed wydaniem Duke Nukem Forever :))

Aktualizacja : kod dla .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

Teraz jedyne, czego potrzebujemy, to narzędzie, które konwertuje prev_commit,branchparę na prawdziwy hash zatwierdzenia :)

Nie wiem, czy to podejście może odróżnić scalanie zatwierdzeń od siebie. Niedługo to sprawdzę

kolypto
źródło
13

Ktoś wskazał mi sekcję „man gitattributes” na ident, która zawiera:

ident

Gdy identyfikator atrybutu jest ustawiony dla ścieżki, git zamienia $ Id $ w obiekcie blob na $ Id :, po którym następuje 40-znakowa szesnastkowa nazwa obiektu blob, po której następuje znak dolara $. Każda sekwencja bajtów, która zaczyna się od $ Id: i kończy na $ w pliku drzewa roboczego, jest zastępowana $ Id $ podczas wpisywania.

Jeśli się nad tym zastanowić, to właśnie robią CVS, Subversion itp. Jeśli spojrzysz na repozytorium, zobaczysz, że plik w repozytorium zawsze zawiera na przykład $ Id $. Nigdy nie zawiera rozszerzenia tego. Dopiero przy kasie tekst jest rozszerzany.

Baron Schwartz
źródło
8
identjest skrótem dla samego pliku, a nie przyspieszeniem zatwierdzenia. Z git-scm.com/book/en/… : "Jednak ten wynik ma ograniczone zastosowanie. Jeśli używałeś podstawiania słów kluczowych w CVS lub Subversion, możesz dołączyć datownik - SHA nie jest aż tak pomocny, ponieważ jest to dość przypadkowe i nie można stwierdzić, czy jeden SHA jest starszy czy nowszy od innego ”. filterwymaga pracy, ale może pobrać informacje o zmianach do (iz) pliku.
Zach Young,
11

Można to osiągnąć za pomocą filteratrybutu w gitattributes . Musiałbyś podać smudgepolecenie, które wstawia identyfikator zatwierdzenia, i cleanpolecenie, które je usuwa, tak aby plik, w którym jest wstawiony, nie zmienił się tylko z powodu identyfikatora zatwierdzenia.

W związku z tym identyfikator zatwierdzenia nigdy nie jest przechowywany w obiekcie blob pliku; jest po prostu rozszerzony w kopii roboczej. (Właściwie wstawienie identyfikatora zatwierdzenia do obiektu blob stałoby się zadaniem nieskończenie rekurencyjnym. ☺) Każdy, kto klonuje to drzewo, musiałby ustawić atrybuty dla siebie.

legoscia
źródło
8
Niemożliwe zadanie, nie rekurencyjne. Wartość skrótu zatwierdzenia zależy od skrótu drzewa, który zależy od skrótu pliku, który zależy od zawartości pliku. Musisz uzyskać spójność siebie. Chyba że znajdziesz rodzaj [uogólnionego] stałego punktu dla skrótu SHA-1.
Jakub Narębski
1
@Jakub, czy jest jakaś sztuczka w git, która pozwoli stworzyć śledzone pliki, które nie modyfikują wynikowego hasha? Może jakiś sposób na zastąpienie haszyszu. To będzie rozwiązanie :)
kolypto
@o_O Tync: Niemożliwe. Zmieniony plik oznacza zmieniony hash (pliku) - jest to zgodne z projektem iz definicją funkcji skrótu.
Jakub Narębski
2
To całkiem dobre rozwiązanie, ale pamiętaj, że wiąże się to z punktami zaczepienia, które muszą być instalowane ręcznie za każdym razem, gdy klonujesz repozytorium.
Edward Falk
8

Pomyśl poza polem zmian!

wrzuć to do pliku hooks / post-checkout

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

Wersja będzie dostępna wszędzie tam, gdzie jej użyjesz.

Keith Patrick
źródło
3

Nie sądzę, abyś chciał to zrobić, ponieważ kiedy plik w zatwierdzeniu jest zmieniany, zmienia się również skrót zatwierdzenia.

midtiby
źródło
1

Pozwólcie, że zbadam, dlaczego jest to trudny problem z wykorzystaniem wewnętrznych elementów git. Możesz pobrać sha1 bieżącego zatwierdzenia przez

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

Zasadniczo uruchamiasz sumę kontrolną sha1 dla wiadomości zwróconej przez git cat-file commit HEAD. Po przeanalizowaniu tej wiadomości od razu pojawiają się dwie rzeczy. Jeden to drzewo sha1, a drugi to czas zatwierdzenia.

Teraz można łatwo zająć się czasem zatwierdzenia, zmieniając wiadomość i odgadując, ile czasu zajmie wykonanie zatwierdzenia lub zaplanowanie zatwierdzenia w określonym czasie. Prawdziwym problemem jest drzewo sha1, z którego można uzyskać git ls-tree $(git write-tree) | git mktree. Zasadniczo robisz sumę kontrolną sha1 dla wiadomości z ls-tree, która jest listą wszystkich plików i ich sumą kontrolną sha1.

Dlatego suma kontrolna sha1 twojego zatwierdzenia zależy od sumy kontrolnej sha1 twojego drzewa, która bezpośrednio zależy od sumy kontrolnej pliku sha1, która zamyka okrąg i zależy od zatwierdzenia sha1. Tak więc masz problem z metodami dostępnymi dla mnie.

Przy mniej bezpiecznych sumach kontrolnych okazało się, że możliwe jest zapisanie sumy kontrolnej pliku do samego pliku przy użyciu brutalnej siły; jednak nie znam żadnej pracy, która wykonałaby to zadanie z sha1. Nie jest to niemożliwe, ale prawie niemożliwe przy naszym obecnym rozumieniu (ale kto wie, może za kilka lat będzie to trywialne). Jednak nadal jest to jeszcze trudniejsze do brutalnej siły, ponieważ musisz zapisać (zatwierdzoną) sumę kontrolną (drzewa) sumy kontrolnej (blob) do pliku.

Nowicjusz C
źródło
Czy istnieje sposób, w jaki można zatwierdzić pliki, a następnie wykonać wypisanie i umieścić najnowszą wartość skrótu zatwierdzenia jako komentarz na początku każdego pliku kodu źródłowego? Więc zbuduj i uciekaj od tego?
John Wooten