@@ -7,11 +7,13 @@ package vcs
77import (
88 "fmt"
99 "runtime/debug"
10+ "strconv"
1011)
1112
12- // Version returns the build time and revision information .
13- // The returned string format is "time-revision" or "time-revision-dirty"
13+ // Version returns the build time, revision hash, and revision integer .
14+ // The returned string format is "time-revision-int " or "time-revision-int -dirty"
1415// if the working directory had uncommitted changes at build time.
16+ // The integer is derived from the first 4 bytes of the git revision hash.
1517func Version () string {
1618 var (
1719 time string
@@ -35,9 +37,79 @@ func Version() string {
3537 }
3638 }
3739
40+ revInt , err := revisionToInt (revision )
41+ if err != nil {
42+ revInt = 0
43+ }
44+
3845 if modified {
39- return fmt .Sprintf ("%s-%s-dirty" , time , revision )
46+ return fmt .Sprintf ("%s-%s-%d-dirty" , time , revision , revInt )
47+ }
48+
49+ return fmt .Sprintf ("%s-%s-%d" , time , revision , revInt )
50+ }
51+
52+ // revisionToInt converts the first 4 bytes (8 hex characters) of a git revision hash
53+ // to a signed 32-bit integer suitable for use as SQLite user_version.
54+ // The conversion uses the first 8 hex characters of the hash, which are
55+ // cryptographically random and uniformly distributed.
56+ //
57+ // Example: "abc123def456..." -> 0xabc123de -> -1412567330
58+ //
59+ // Returns an error if the revision is too short (< 8 characters) or contains
60+ // invalid hexadecimal characters.
61+ func revisionToInt (revision string ) (int32 , error ) {
62+ if len (revision ) < 8 {
63+ return 0 , fmt .Errorf ("revision too short: need at least 8 characters, got %d" , len (revision ))
64+ }
65+
66+ // Take first 8 hex characters (4 bytes)
67+ hexStr := revision [:8 ]
68+
69+ // Parse as uint32 first to handle the full range
70+ val , err := strconv .ParseUint (hexStr , 16 , 32 )
71+ if err != nil {
72+ return 0 , fmt .Errorf ("failed to parse revision hex: %w" , err )
73+ }
74+
75+ // Convert to int32 (may be negative due to sign bit)
76+ return int32 (val ), nil
77+ }
78+
79+ // VersionInt32 returns the git revision hash and its integer representation.
80+ // The integer is derived from the first 4 bytes (8 hex characters) of the
81+ // revision hash and is suitable for use as SQLite user_version (signed 32-bit integer).
82+ //
83+ // This function extracts the vcs.revision value from the build information
84+ // embedded in the binary during compilation with -buildvcs flag.
85+ //
86+ // Returns:
87+ // - revision: The full git commit hash (e.g., "abc123def456...")
88+ // - revisionInt: A signed 32-bit integer representation (e.g., -1412567330)
89+ // - error: Non-nil if build info is unavailable, revision not found, or parsing fails
90+ func VersionInt32 () (string , int32 , error ) {
91+ var revision string
92+
93+ bi , ok := debug .ReadBuildInfo ()
94+ if ! ok {
95+ return "" , 0 , fmt .Errorf ("build info not available" )
96+ }
97+
98+ for _ , s := range bi .Settings {
99+ if s .Key == "vcs.revision" {
100+ revision = s .Value
101+ break
102+ }
103+ }
104+
105+ if revision == "" {
106+ return "" , 0 , fmt .Errorf ("vcs.revision not found in build info" )
107+ }
108+
109+ revInt , err := revisionToInt (revision )
110+ if err != nil {
111+ return revision , 0 , err
40112 }
41113
42- return fmt . Sprintf ( "%s-%s" , time , revision )
114+ return revision , revInt , nil
43115}
0 commit comments