]> granicus.if.org Git - nethack/commitdiff
Incorporate some git information into NetHack
authornhmall <nhmall@nethack.org>
Mon, 29 Jan 2018 03:54:08 +0000 (22:54 -0500)
committerkeni <keni@his.com>
Sat, 24 Feb 2018 00:34:44 +0000 (19:34 -0500)
Incorporate some git information into NetHack so that it
is potentially visible to a player. That's useful when
collecting details about the version that they are
running and, if the gitinfo is present, it can tie the
code to a specific git commit in the repository.

This modifies 'makedefs -v' to check for the presence of a data file
called dat/gitinfo.txt and if it is there, parse out its
contents, then write additional lines to include/date.h beyond
what 'makedefs -v' was previously putting in there, similar to
this sample:

      #define NETHACK_GIT_SHA "0c84e564c78e2024e562d39539376ce2e21eec8e"
      #define NETHACK_GIT_BRANCH "NetHack-3.6.0"

The contents of an appropriate dat/gitinfo.txt are as follows,
and trailing/leading whitespace is not significant:

      githash = 0c84e564c78e2024e562d39539376ce2e21eec8e
      gitbranch = NetHack-3.6.0

It also adjusts the contents of the 'v' version information to
include the additional git info when available.

Also adds some hooks DEVEL/hooksdir and a perl file to DEVEL
for simplifying and automating the deposit of dat/gitinfo.txt
so that it generally reflects the most current git commit.

DEVEL/gitinfo.pl can be used to build dat/gitinfo.txt at any
time without doing a commit, merge, or checkout.
     perl DEVEL/gitinfo.pl

command line --version and -version support

To complement the extra information being provided in the
version by the 'v' command, this also adds support for the
following new command line arguments:
    --version
     -version            Output the NetHack version string then exit.

    --version:paste      Output the NetHack version string and also copy it to
     -version:paste      the platform's paste buffer for insertion somewhere,
                         then exit.

If the paste variation of -version is requested on a platform that
hasn't incorporated any support for the capability, it will deliver
the version info then an error message, prior to exiting.

To support the extended -version:paste variation, a port needs to:
    - provide a port-specific routine to perform
      the paste buffer copy in a port code file.
    - #define RUNTIME_PASTEBUF_SUPPORT in the include/portconf.h header file.

    --skeleton--
    void port_insert_pastebuf(buf)
    char *buf;
    {
     /* insert code to copy the version info from buf into
        platform's paste buffer in a supported way */
    }

macosx and Windows have both added support for RUNTIME_PASTEBUF_SUPPORT

17 files changed:
DEVEL/gitinfo.pl [new file with mode: 0644]
DEVEL/hooksdir/NHgithook.pm
DEVEL/hooksdir/post-checkout
DEVEL/hooksdir/post-commit
DEVEL/hooksdir/post-merge
doc/makedefs.6
doc/nethack.6
include/decl.h
include/extern.h
include/ntconf.h
include/unixconf.h
src/allmain.c
src/version.c
sys/share/pcmain.c
sys/unix/unixmain.c
sys/winnt/winnt.c
util/makedefs.c

diff --git a/DEVEL/gitinfo.pl b/DEVEL/gitinfo.pl
new file mode 100644 (file)
index 0000000..b624289
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+
+#STARTUP-START
+BEGIN {
+    # OS hackery has to be duplicated in each of the hooks :/
+    # first the directory separator
+    my $DS = quotemeta('/');
+    my $PDS = '/';
+    # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n).
+    # temporarily removed because inconsistent behavior
+    # if ($^O eq "msys")
+    # {
+    #   $/ = "\r\n";
+    #   $\ = "\r\n";
+    # }
+    if($^O eq "MSWin32"){
+        $DS = quotemeta('\\');
+       $PDS = '\\';
+    }
+    $gitdir = `git rev-parse --git-dir`;
+    chomp $gitdir;
+    push(@INC, $gitdir.$PDS."hooks");
+}
+use NHgithook;
+
+&NHgithook::nhversioning;
index 6024c1c03eb4e8373b5b2fc931e1b78a3910aafd..1bb92cf7ef3d89158bea6d4e300bf4ed7a771988 100644 (file)
@@ -60,6 +60,36 @@ sub POST {
        &do_hook("POST");
 }
 
+###
+### store githash and gitbranch in dat/gitinfo.txt
+###
+
+sub nhversioning {
+    use strict;
+    use warnings;
+
+    my $git_sha = `git rev-parse HEAD`;
+    $git_sha =~ s/\s+//g;
+    my $git_branch = `git rev-parse --abbrev-ref HEAD`;
+    $git_branch =~ s/\s+//g;
+
+    if (open my $fh, '<', 'dat/gitinfo.txt') {
+        while(my $line = <$fh>) {
+            if ((index $line, $git_sha) >= 0) {
+                close $fh;
+                print "No update made to dat/gitinfo.txt, existing githash=".$git_sha."\n";
+                return;
+            }
+        }
+        close $fh;
+    }
+    if (open my $fh, '>', 'dat/gitinfo.txt') {
+        print $fh 'githash='.$git_sha."\n";
+        print $fh 'gitbranch='.$git_branch."\n";
+        print "An updated dat/gitinfo.txt was written, githash=".$git_sha."\n";
+    }
+}
+
 # PRIVATE
 sub do_hook {
        my($p) = @_;
index b5bf990fee1295555a464ed0d4ebc97b27d19ef1..2df107a2feac7a84ddfc7842078b79ceda3e06e8 100755 (executable)
@@ -26,5 +26,6 @@ use NHgithook;
 #STARTUP-END
 
 &NHgithook::PRE;
+&NHgithook::nhversioning;
 &NHgithook::POST;
 exit 0;
index b5bf990fee1295555a464ed0d4ebc97b27d19ef1..2df107a2feac7a84ddfc7842078b79ceda3e06e8 100755 (executable)
@@ -26,5 +26,6 @@ use NHgithook;
 #STARTUP-END
 
 &NHgithook::PRE;
+&NHgithook::nhversioning;
 &NHgithook::POST;
 exit 0;
index b5bf990fee1295555a464ed0d4ebc97b27d19ef1..2dc7f36288a0246b994d3816f37c0eeb5873c8cc 100755 (executable)
@@ -22,9 +22,11 @@ BEGIN {
     chomp $gitdir;
     push(@INC, $gitdir.$PDS."hooks");
 }
+
 use NHgithook;
 #STARTUP-END
 
 &NHgithook::PRE;
+&NHgithook::nhversioning;
 &NHgithook::POST;
 exit 0;
index 7b55de3e4abbec7c7584857bc69e9490e2388ef9..29d4899be2ccc5be7270140b0cd843ffa0b06f72 100644 (file)
@@ -85,6 +85,14 @@ Generate
 .I date.h
 and
 .I options
+file. It will read 
+.I dat/gitinfo.txt
+,only if it is present, to obtain 
+.B githash=
+and 
+.B gitbranch=
+ info and include related preprocessor #defines in
+.I date.h
 file.
 .br
 .TP
index 3803ae583b18f4226ab0fdc91eb84a64676fcbdd..6b38a68ba8f7662b90621fe92622c7cd276b24f0 100644 (file)
@@ -55,6 +55,9 @@ nethack \- Exploring The Mazes of Menace
 [
 .I playernames
 ]
+[
+.B \-\-version
+]
 .ad
 .hy 14
 .\" Make sure path is not hyphenated below
@@ -207,6 +210,17 @@ The playground must contain several auxiliary files such as help files,
 the list of top scorers, and a subdirectory
 .I save
 where games are saved.
+.PP
+.B \-\-version
+can be used to cause NetHack to show the version information it
+was compiled with, then exit. That will include the
+.I git
+commit hash if the information was available when the game was compiled.
+On some platforms, such as windows and macosx, a variation 
+.B \-\-version:paste
+can be used to cause NetHack to show the version information, then exit,
+while also leaving a copy of the version information in the paste buffer 
+or clipboard for potential insertion into things like bug reports.
 .SH AUTHORS
 .PP
 Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the
index f6d5764cb1b4f15ca9e3a5aae60ca18e6d5d51ac..bf99227f951de3c4b7bb855e73997e7367c5d29f 100644 (file)
@@ -422,6 +422,15 @@ E struct plinemsg_type *plinemsg_types;
 E const char *ARGV0;
 #endif
 
+enum earlyarg {ARG_DEBUG, ARG_VERSION};
+
+struct early_opt {
+    enum earlyarg e;
+    const char *name;
+    int minlength;
+    boolean valallowed;
+};
+
 #undef E
 
 #endif /* DECL_H */
index 24a0c06cdc26ecc7bf66bdcf7ba4197b88c92167..4ea4915c49147db25a8989a0dbc92aa010c8ef5e 100644 (file)
@@ -26,6 +26,7 @@ E void NDECL(display_gamewindows);
 E void NDECL(newgame);
 E void FDECL(welcome, (BOOLEAN_P));
 E time_t NDECL(get_realtime);
+E boolean FDECL(argcheck, (int, char **, enum earlyarg));
 
 /* ### apply.c ### */
 
@@ -2567,10 +2568,14 @@ E void FDECL(store_version, (int));
 E unsigned long FDECL(get_feature_notice_ver, (char *));
 E unsigned long NDECL(get_current_feature_ver);
 E const char *FDECL(copyright_banner_line, (int));
+E void FDECL(early_version_info, (BOOLEAN_P));
 
 #ifdef RUNTIME_PORT_ID
 E char *FDECL(get_port_id, (char *));
 #endif
+#ifdef RUNTIME_PASTEBUF_SUPPORT
+E void FDECL(port_insert_pastebuf, (char *));
+#endif
 
 /* ### video.c ### */
 
index a096a2ff1f3d70b66dd49eba308440e987c917b8..534fa6e5d5aa6da8c0fe410aab83d1fb01c91528 100644 (file)
 #define PORT_DEBUG /* include ability to debug international keyboard issues \
                       */
 
+#define RUNTIME_PORT_ID /* trigger run-time port identification for \
+                         * identification of exe CPU architecture   \
+                         */
+#define RUNTIME_PASTEBUF_SUPPORT
+
+
 #define SAFERHANGUP /* Define SAFERHANGUP to delay hangup processing   \
                      * until the main command loop. 'safer' because it \
                      * avoids certain cheats and also avoids losing    \
@@ -117,11 +123,6 @@ extern void FDECL(interject, (int));
 #endif
 #endif /* _MSC_VER */
 
-
-#define RUNTIME_PORT_ID /* trigger run-time port identification for \
-                         * identification of exe CPU architecture   \
-                         */
-
 /* The following is needed for prototypes of certain functions */
 #if defined(_MSC_VER)
 #include <process.h> /* Provides prototypes of exit(), spawn()      */
index 0d4fbce17723b2102e7e3bd669a123eaadcbbfe4..88e309d55df1affccb1ad412cff506d8f8af5130 100644 (file)
 #endif /* LINUX */
 #endif /* GNOME_GRAPHICS */
 
+#ifdef __APPLE__
+# define RUNTIME_PASTEBUF_SUPPORT
+#endif
+
 #endif /* UNIXCONF_H */
 #endif /* UNIX */
index 4a0cfaf8fad7825dfc334465e3e3e16ae8ee9bc1..789c5d48f4488ea151b69a6962840a4ae787e4bc 100644 (file)
@@ -737,5 +737,87 @@ const char *msg;
     }
 }
 
+/*
+ * Argument processing helpers - for xxmain() to share
+ * and call.
+ *
+ * These should return TRUE if the argument matched,
+ * whether the processing of the argument was
+ * successful or not.
+ *
+ * Most of these do their thing, then after returning
+ * to xxmain(), the code exits without starting a game.
+ *
+ */
+
+static struct early_opt earlyopts[] = {
+    {ARG_DEBUG, "debug", 5, FALSE},
+    {ARG_VERSION, "version", 4, TRUE},
+};
+
+boolean
+argcheck(argc, argv, e_arg)
+int argc;
+char *argv[];
+enum earlyarg e_arg;
+{
+    int i, idx;
+    boolean match = FALSE;
+    char *userea = (char *)0, *dashdash = "";
+
+    for (idx = 0; idx < SIZE(earlyopts); idx++) {
+        if (earlyopts[idx].e == e_arg)
+            break;
+    }
+    if ((idx >= SIZE(earlyopts)) || (argc <= 1))
+            return FALSE;
+
+    for (i = 1; i < argc; ++i) {
+        if (argv[i][0] != '-')
+            continue;
+        if (argv[i][1] == '-') {
+            userea = &argv[i][2];
+            dashdash = "-";
+        } else {
+            userea = &argv[i][1];
+        }
+        match = match_optname(userea, earlyopts[idx].name,
+                    earlyopts[idx].minlength, earlyopts[idx].valallowed);
+        if (match) break;
+    }
+
+    if (match) {
+        switch(e_arg) {
+            case ARG_DEBUG:
+                        break;
+            case ARG_VERSION: {
+                        boolean insert_into_pastebuf = FALSE;
+                        const char *extended_opt = index(userea,':');
+
+                        if (!extended_opt)
+                            extended_opt = index(userea, '=');
+
+                        if (extended_opt) {
+                            extended_opt++;
+                            if (match_optname(extended_opt, "paste",
+                                                   5, FALSE)) {
+                                insert_into_pastebuf = TRUE;
+                            } else {
+                                raw_printf(
+                     "-%sversion can only be extended with -%sversion:paste.\n",
+                                            dashdash, dashdash);
+                                return TRUE;
+                           }
+                       }
+                        early_version_info(insert_into_pastebuf);
+                        return TRUE;
+                        break;
+            }
+            default:
+                        break;
+        }
+    };
+    return FALSE;
+}
 
 /*allmain.c*/
index 19f74c26559cf30d35aaecaa586a56ad8c2b876c..5146656635ae1c3348d7f7d8528bcedd0595ea61 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 version.c       $NHDT-Date: 1506993902 2017/10/03 01:25:02 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.44 $ */
+/* NetHack 3.6 version.c       $NHDT-Date: 1517140532 2018/01/28 11:55:32 $  $NHDT-Branch: nhmall-githash3 $:$NHDT-Revision: 1.50 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "patchlevel.h"
 #endif
 
+#if defined(NETHACK_GIT_SHA)
+const char * NetHack_git_sha = NETHACK_GIT_SHA;
+#endif
+#if defined(NETHACK_GIT_BRANCH)
+const char * NetHack_git_branch = NETHACK_GIT_BRANCH;
+#endif
+
 STATIC_DCL void FDECL(insert_rtoption, (char *));
 
 /* fill buffer with short version (so caller can avoid including date.h) */
@@ -30,23 +37,34 @@ char *
 getversionstring(buf)
 char *buf;
 {
-    int details = 0;
+    boolean details = FALSE;
 
     Strcpy(buf, VERSION_ID);
-#if defined(RUNTIME_PORT_ID)
-    details++;
+#if defined(RUNTIME_PORT_ID) || \
+    defined(NETHACK_GIT_SHA) || defined(NETHACK_GIT_BRANCH)
+    details = TRUE;
 #endif
 
     if (details) {
         int c = 0;
+#if defined(RUNTIME_PORT_ID)
         char tmpbuf[BUFSZ];
         char *tmp = (char *)0;
+#endif
 
         Sprintf(eos(buf), " (");
 #if defined(RUNTIME_PORT_ID)
         tmp = get_port_id(tmpbuf);        
         if (tmp)
             Sprintf(eos(buf), "%s%s", c++ ? "," : "", tmp);
+#endif
+#if defined(NETHACK_GIT_SHA)
+        if (NetHack_git_sha)
+            Sprintf(eos(buf), "%s%s", c++ ? "," : "", NetHack_git_sha);
+#endif
+#if defined(NETHACK_GIT_BRANCH)
+        if (NetHack_git_branch)
+            Sprintf(eos(buf), "%s%s", c++ ? "," : "", NetHack_git_branch);
 #endif
         Sprintf(eos(buf), ")");
     }
@@ -130,6 +148,37 @@ doextversion()
     return 0;
 }
 
+void early_version_info(pastebuf)
+boolean pastebuf;
+{
+    char buf[BUFSZ], buf2[BUFSZ];
+    char *tmp = getversionstring(buf);
+
+    /* this is early enough that we have to do
+       our own line-splitting */
+    if (tmp) {
+        tmp = strstri(buf," (");
+        if (tmp) *tmp++ = '\0';
+    }
+
+    Sprintf(buf2, "%s\n", buf);
+    if (tmp) Sprintf(eos(buf2), "%s\n", tmp);
+    raw_printf("%s", buf2);
+
+    if (pastebuf) {
+#ifdef RUNTIME_PASTEBUF_SUPPORT
+        /*
+         * Call a platform/port-specific routine to insert the
+         * version information into a paste buffer. Useful for
+         * easy inclusion in bug reports.
+         */
+        port_insert_pastebuf(buf2);
+#else
+        raw_printf("%s", "Paste buffer copy is not available.\n");
+#endif
+    }
+}
+
 extern const char regex_id[];
 
 /*
index a18157639bdedd65a42096d26e2ea6b70ac7c3b3..64a175c1435abfb345d1b4a7f4ba8857a9635bed 100644 (file)
@@ -329,6 +329,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
         Strcpy(hackdir, HACKDIR);
 #endif
     if (argc > 1) {
+        if (argcheck(argc, argv, ARG_VERSION))
+            nethack_exit(EXIT_SUCCESS);
+
         if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
             /* avoid matching "-dec" for DECgraphics; since the man page
              * says -d directory, hope nobody's using -desomething_else
index b9ab44526be3daccb621e7c39a243158b08e980d..95e48cc14c48a583ed7d2c9ece98890f1f65b09e 100644 (file)
@@ -110,6 +110,9 @@ char *argv[];
         dir = nh_getenv("HACKDIR");
 
     if (argc > 1) {
+        if (argcheck(argc, argv, ARG_VERSION))
+            exit(EXIT_SUCCESS);
+
         if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
             /* avoid matching "-dec" for DECgraphics; since the man page
              * says -d directory, hope nobody's using -desomething_else
@@ -720,4 +723,40 @@ get_login_name()
     return buf;
 }
 
+#ifdef __APPLE__
+extern int errno;
+
+void
+port_insert_pastebuf(buf)
+char *buf;
+{
+    /* This should be replaced when there is a Cocoa port. */
+    const char *errfmt;
+    size_t len;
+    FILE *PB = popen("/usr/bin/pbcopy","w");
+    if(!PB){
+       errfmt = "Unable to start pbcopy (%d)\n";
+       goto error;
+    }
+
+    len = strlen(buf);
+    /* Remove the trailing \n, carefully. */
+    if(buf[len-1] == '\n') len--;
+
+    /* XXX Sorry, I'm too lazy to write a loop for output this short. */
+    if(len!=fwrite(buf,1,len,PB)){
+       errfmt = "Error sending data to pbcopy (%d)\n";
+       goto error;
+    }
+
+    if(pclose(PB)!=-1){
+       return;
+    }
+    errfmt = "Error finishing pbcopy (%d)\n";
+
+error:
+    raw_printf(errfmt,strerror(errno));
+}
+#endif
+
 /*unixmain.c*/
index 842bfd036e89fd3ff26fe6a282595ee984b00129..cfd27acac4394867f96943ae76db11479d883b7c 100644 (file)
@@ -35,6 +35,9 @@
 /* globals required within here */
 HANDLE ffhandle = (HANDLE) 0;
 WIN32_FIND_DATA ffd;
+typedef HWND(WINAPI *GETCONSOLEWINDOW)();
+static HWND GetConsoleHandle(void);
+static HWND GetConsoleHwnd(void);
 
 /* The function pointer nt_kbhit contains a kbhit() equivalent
  * which varies depending on which window port is active.
@@ -316,6 +319,119 @@ int interjection_type;
         msmsg(interjection_buf[interjection_type]);
 }
 
+#ifdef RUNTIME_PASTEBUF_SUPPORT
+
+void port_insert_pastebuf(buf)
+char *buf;
+{
+    /* This implementation will utilize the windows clipboard
+     * to accomplish this.
+     */
+
+    char *tmp = buf;
+    HWND hwndConsole = GetConsoleHandle();
+    HGLOBAL hglbCopy; 
+    WCHAR *w, w2[2];
+    int cc, rc, abytes;
+    LPWSTR lpwstrCopy;
+    HANDLE hresult;
+
+    if (!buf || (hwndConsole == NULL))
+        return; 
+    cc = strlen(buf);
+    /* last arg=0 means "tell me the size of the buffer that I need" */
+    rc = MultiByteToWideChar(GetConsoleOutputCP(), 0, buf, -1, w2, 0);
+    if (!rc) return;
+
+    abytes = rc * sizeof(WCHAR);
+    w = (WCHAR *)alloc(abytes);     
+    /* Housekeeping need: +free(w) */
+
+    rc = MultiByteToWideChar(GetConsoleOutputCP(), 0, buf, -1, w, rc);
+    if (!rc) {
+        free(w);
+        return;
+    }
+    if (!OpenClipboard(hwndConsole)) {
+        free(w);
+        return;
+    }
+    /* Housekeeping need: +CloseClipboard(), free(w) */
+
+    EmptyClipboard(); 
+
+    /* allocate global mem obj to hold the text */
+    hglbCopy = GlobalAlloc(GMEM_MOVEABLE, abytes);
+    if (hglbCopy == NULL) { 
+        CloseClipboard(); 
+        free(w);
+        return;
+    } 
+    /* Housekeeping need: +GlobalFree(hglbCopy), CloseClipboard(), free(w) */
+    lpwstrCopy = (LPWSTR)GlobalLock(hglbCopy);
+    /* Housekeeping need: +GlobalUnlock(hglbCopy), GlobalFree(hglbCopy),
+                            CloseClipboard(), free(w) */
+
+    memcpy(lpwstrCopy, w, abytes);
+    GlobalUnlock(hglbCopy);
+    /* Housekeeping need: GlobalFree(hglbCopy), CloseClipboard(), free(w) */
+
+    /* put it on the clipboard */
+    hresult = SetClipboardData(CF_UNICODETEXT, hglbCopy);
+    if (!hresult) {
+        raw_printf("Error copying to clipboard.\n");
+        GlobalFree(hglbCopy); /* only needed if clipboard didn't accept data */
+    }
+    /* Housekeeping need: CloseClipboard(), free(w) */
+    CloseClipboard(); 
+    free(w);
+    return;
+}
+
+static HWND
+GetConsoleHandle(void)
+{
+    HMODULE hMod = GetModuleHandle("kernel32.dll");
+    GETCONSOLEWINDOW pfnGetConsoleWindow =
+        (GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow");
+    if (pfnGetConsoleWindow)
+        return pfnGetConsoleWindow();
+    else
+        return GetConsoleHwnd();
+}
+
+static HWND
+GetConsoleHwnd(void)
+{
+    int iterations = 0;
+    HWND hwndFound = 0;
+    char OldTitle[1024], NewTitle[1024], TestTitle[1024];
+
+    /* Get current window title */
+    GetConsoleTitle(OldTitle, sizeof OldTitle);
+
+    (void) sprintf(NewTitle, "NETHACK%d/%d", GetTickCount(),
+                   GetCurrentProcessId());
+    SetConsoleTitle(NewTitle);
+
+    GetConsoleTitle(TestTitle, sizeof TestTitle);
+    while (strcmp(TestTitle, NewTitle) != 0) {
+        iterations++;
+        /* sleep(0); */
+        GetConsoleTitle(TestTitle, sizeof TestTitle);
+    }
+    hwndFound = FindWindow(NULL, NewTitle);
+    SetConsoleTitle(OldTitle);
+    /*       printf("%d iterations\n", iterations); */
+    return hwndFound;
+}
+
+#endif
+
 #ifdef RUNTIME_PORT_ID
 /*
  * _M_IX86 is Defined for x86 processors. This is not defined for x64
index f3f7135bed6b36cb549ee72ae1fc0928465c96bd..2726816e8a65fa9c60a0ce3b755d5f453cf301b1 100644 (file)
@@ -72,6 +72,7 @@ static const char SCCS_Id[] = "@(#)makedefs.c\t3.6\t2016/02/12";
 #define QTXT_O_FILE "quest.dat"
 #define VIS_TAB_H "vis_tab.h"
 #define VIS_TAB_C "vis_tab.c"
+#define GITINFO_FILE "gitinfo.txt"
 /* locations for those files */
 #ifdef AMIGA
 #define FILE_PREFIX
@@ -177,6 +178,7 @@ static char *FDECL(bannerc_string, (char *, const char *));
 static char *FDECL(xcrypt, (const char *));
 static unsigned long FDECL(read_rumors_file,
                            (const char *, int *, long *, unsigned long));
+static boolean FDECL(get_gitinfo, (char *, char *));
 static void FDECL(do_rnd_access_file, (const char *));
 static boolean FDECL(d_filter, (char *));
 static boolean FDECL(h_filter, (char *));
@@ -209,6 +211,7 @@ static char *FDECL(fgetline, (FILE*));
 static char *FDECL(tmpdup, (const char *));
 static char *FDECL(limit, (char *, int));
 static char *FDECL(eos, (char *));
+static int FDECL(case_insensitive_comp, (const char *, const char *));
 
 /* input, output, tmp */
 static FILE *ifp, *ofp, *tfp;
@@ -1239,6 +1242,7 @@ do_date()
 #else
     time_t clocktim = 0;
 #endif
+    char githash[BUFSZ], gitbranch[BUFSZ];
     char *c, cbuf[60], buf[BUFSZ];
     const char *ul_sfx;
 
@@ -1372,6 +1376,10 @@ do_date()
     Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
             bannerc_string(buf, cbuf));
     Fprintf(ofp, "\n");
+    if (get_gitinfo(githash, gitbranch)) {
+        Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash);
+        Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch);
+    }
 #ifdef AMIGA
     {
         struct tm *tm = localtime((time_t *) &clocktim);
@@ -1386,6 +1394,84 @@ do_date()
     return;
 }
 
+boolean
+get_gitinfo(githash, gitbranch)
+char *githash, *gitbranch;
+{
+    FILE *gifp;
+    size_t len;
+    char infile[600];
+    char *line, *strval, *opt, *c, *end;
+    boolean havebranch = FALSE, havehash = FALSE;
+
+    if (!githash || !gitbranch) return FALSE;
+
+    Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE);
+    if (!(gifp = fopen(infile, RDTMODE))) {
+        /* perror(infile); */
+        return FALSE;
+    }
+
+    /* read the gitinfo file */
+    while ((line = fgetline(gifp)) != 0) {
+        strval = index(line, '=');
+        if (strval && strlen(strval) < (BUFSZ-1)) {
+            opt = line;
+            *strval++ = '\0';
+            /* strip off the '\n' */
+            if ((c = index(strval, '\n')) != 0)
+                *c = '\0'; 
+            if ((c = index(opt, '\n')) != 0)
+                *c = '\0';
+            /* strip leading and trailing white space */
+            while (*strval == ' ' || *strval == '\t')
+                strval++;
+            end = eos(strval);
+            while (--end >= strval && (*end == ' ' || *end == '\t'))
+            *end = '\0';
+            while (*opt == ' ' || *opt == '\t')
+                opt++;
+            end = eos(opt);
+            while (--end >= opt && (*end == ' ' || *end == '\t'))
+            *end = '\0';
+
+            len = strlen(opt);
+            if ((len >= strlen("gitbranch")) && !case_insensitive_comp(opt, "gitbranch")) {
+                Strcpy(gitbranch, strval);
+                havebranch = TRUE;
+            }
+            if ((len >= strlen("githash")) && !case_insensitive_comp(opt, "githash")) {
+                Strcpy(githash, strval);
+                havehash = TRUE;
+            }
+       }
+    }
+    Fclose(gifp);
+    if (havebranch && havehash)
+        return TRUE;
+    return FALSE;
+}
+
+static int
+case_insensitive_comp(s1, s2)
+const char *s1;
+const char *s2;
+{
+    uchar u1, u2;
+
+    for (;; s1++, s2++) {
+        u1 = (uchar) *s1;
+        if (isupper(u1))
+            u1 = tolower(u1);
+        u2 = (uchar) *s2;
+        if (isupper(u2))
+            u2 = tolower(u2);
+        if (u1 == '\0' || u1 != u2)
+            break;
+    }
+    return u1 - u2;
+}
+
 static char save_bones_compat_buf[BUFSZ];
 
 static void