मैं अपने C कोड को स्वचालित रूप से इसके Git संस्करण हैश का प्रिंट आउट कैसे प्राप्त कर सकता हूं?


84

क्या सी कोड लिखने का एक आसान तरीका है जो अपने जीआईटी संस्करण हैश तक पहुंच सकता है?

मैंने एक प्रयोगशाला सेटिंग में वैज्ञानिक डेटा एकत्र करने के लिए सी में सॉफ्टवेयर लिखा था। मेरा कोड बाद में विश्लेषण के लिए एक .yaml फ़ाइल में एकत्रित डेटा को रिकॉर्ड करता है। मेरे प्रयोग दिन-प्रतिदिन बदलते रहते हैं और मुझे अक्सर कोड को संशोधित करना पड़ता है। संशोधनों का ट्रैक रखने के लिए, मैं एक गिट रिपॉजिटरी का उपयोग करता हूं।

मैं अपने .yaml डेटा फ़ाइलों में एक टिप्पणी के रूप में Git संशोधन हैश को शामिल करने में सक्षम होना चाहूंगा। इस तरह, मैं .yaml फ़ाइल को देख सकता हूं और यह जान सकता हूं कि उस फ़ाइल में दिखाए गए डेटा को उत्पन्न करने के लिए किस कोड का उपयोग किया गया था। क्या स्वचालित रूप से ऐसा करने का एक आसान तरीका है?


1
पूर्व-प्रतिबद्ध हुक का उपयोग करना (देखें book.git-scm.com/5_git_hooks.html ) ऐसा करने के लिए जाने का एक और तरीका होगा।
यकतुला

जवाबों:


39

मेरे कार्यक्रम में, मैं एक अलग फ़ाइल में git संस्करण संख्या और निर्माण की तिथि रखता हूं, जिसे कहा जाता है version.c, जो इस तरह दिखता है:

#include "version.h"
const char * build_date = "2009-11-10 11:09";
const char * build_git_sha = "6b54ea36e92d4907aba8b3fade7f2d58a921b6cd";

एक हेडर फ़ाइल भी है, जो इस प्रकार है:

#ifndef VERSION_H
#define VERSION_H
extern const char * build_date; /* 2009-11-10 11:09 */
extern const char * build_git_sha; /* 6b54ea36e92d4907aba8b3fade7f2d58a921b6cd */
#endif /* VERSION_H */

हेडर फ़ाइल और C फ़ाइल दोनों एक पर्ल स्क्रिप्ट द्वारा बनाई गई हैं जो इस तरह दिखता है:

my $git_sha = `git rev-parse HEAD`;
$git_sha =~ s/\s+//g;
# This contains all the build variables.
my %build;
$build{date} = make_date_time ();
$build{git_sha} = $git_sha;

hash_to_c_file ("version.c", \%build, "build_");

यहाँ hash_to_c_fileबनाने version.cऔर बनाने का सारा काम किया गया है version.hऔर make_date_timeजैसा दिखाया गया है वैसा ही एक स्ट्रिंग बनाता है।

मुख्य कार्यक्रम में, मेरी एक दिनचर्या है

#include "version.h"

// The name of this program.
const char * program_name = "magikruiser";
// The version of this program.
const char * version = "0.010";

/* Print an ID stamp for the program. */

static void _program_id_stamp (FILE * output)
{
    fprintf (output, "%s / %s / %s / %s\n",
             program_name, version,
             build_date, build_git_sha);
}

मैं git के बारे में जानकार नहीं हूं, इसलिए अगर ऐसा करने का एक बेहतर तरीका है तो मैं टिप्पणियों का स्वागत करूंगा।


1
पर्ल स्क्रिप्ट बिल्ड स्क्रिप्ट का हिस्सा है, जो हर चीज के लिए एक "एक कदम निर्माण" है।

12
यह जहां तक ​​जाता है यह अच्छा है, लेकिन यह ध्यान रखें कि यह शाखा पर नवीनतम प्रतिबद्ध के हैश की रिपोर्ट करेगा, न कि कोड के हैश को संकलित किए जाने के बारे में। यदि कोई परिवर्तन नहीं हुए हैं, तो वे स्पष्ट नहीं होंगे।
फिल मिलर 3

1
git आपके कार्यक्षेत्र और इंडेक्स के बीच अंतर के लिए डिफ़ॉल्ट चेक द्वारा भिन्न होता है। आप यह भी कोशिश कर सकते हैं कि git को अलग करें
Karl

6
उन सभी 'कास्ट चार * नाम = "मूल्य";) निर्माणों को समझदारी से 'कास्ट चार नाम [] = "मान" में बदला जा सकता है, जो 32-बिट मशीन पर 4 बाइट्स प्रति आइटम और 64-बिट मशीन पर 8 बाइट्स प्रति आइटम बचाता है। दी गई, मुख्य मेमोरी के जीबी के इन दिनों में, यह एक बड़ी समस्या नहीं है, लेकिन यह सब मदद करता है। ध्यान दें कि नामों का उपयोग करने वाले किसी भी कोड को बदलने की आवश्यकता नहीं है।
जोनाथन लेफ़लर

1
जैसा आपने सुझाव दिया मैंने उन्हें बदल दिया। const char []319356 बाइट्स (छीन) के साथ मेरे कार्यक्रम का आकार । मेरे कार्यक्रम का आकार const char *: 319324 बाइट्स (छीन लिया गया)। तो आपका विचार किसी भी बाइट को बचाने के लिए नहीं लगता है, लेकिन कुल संख्या को 32 से बढ़ाएं। मुझे नहीं पता कि क्यों। मूल "version.c" में तीन तार हैं, लेकिन एक उपरोक्त उत्तर से छोड़ा गया था। यदि आप पहले संपादन को देखते हैं तो यह अभी भी है।

163

यदि आप एक मेक-आधारित निर्माण का उपयोग कर रहे हैं, तो आप इसे मेकफाइल में डाल सकते हैं:

GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"

(देखें आदमी गिट वर्णन स्विच क्या करते हैं, इसके लिए )

फिर इसे अपने CFLAGS में जोड़ें:

-DVERSION=\"$(GIT_VERSION)\"

तब आप प्रोग्राम में सीधे संस्करण का संदर्भ दे सकते हैं, जैसे कि यह एक # शेड्यूल था:

printf("Version: %s\n", VERSION);

डिफ़ॉल्ट रूप से यह सिर्फ एक संक्षिप्त git प्रतिबद्ध आईडी प्रिंट करता है, लेकिन वैकल्पिक रूप से आप कुछ के साथ विशेष रिलीज टैग कर सकते हैं:

git tag -a v1.1 -m "Release v1.1"

तो यह प्रिंट होगा:

Version: v1.1-2-g766d

जिसका अर्थ है, 2 पिछले v1.1 से शुरू होता है, जिसमें "766d" के साथ जीआईटी कमिट आईडी है।

यदि आपके पेड़ में अनकही बदलाव हैं, तो यह "-डर्टी" को जोड़ देगा।

कोई निर्भरता स्कैनिंग नहीं है, इसलिए आपको make cleanसंस्करण को अद्यतन करने के लिए मजबूर करने के लिए एक स्पष्ट करना होगा। यह हालांकि हल किया जा सकता है

लाभ यह है कि यह सरल है और पर्ल या ओके जैसी किसी अतिरिक्त निर्माण निर्भरता की आवश्यकता नहीं है। मैंने जीएनयू ऑटोमैटिक के साथ और एंड्रॉइड एनडीके बिल्ड के साथ इस दृष्टिकोण का उपयोग किया है।


6
+1 निजी तौर पर, मैं पसंद करता हूं कि मेकफाइल एक हेडर फ़ाइल उत्पन्न करता है जिसमें #define GIT_VERSION ...इसे -Dविकल्प के साथ कमांड लाइन पर डालने के बजाय होता है; यह निर्भरता की समस्या को समाप्त करता है। इसके अलावा, डबल अंडरस्कोर क्यों? तकनीकी रूप से यह एक आरक्षित पहचानकर्ता है।
डेन मोल्डिंग

8
प्रत्येक अपने स्वयं के लिए - जैसा कि मैं कहता हूं कि लाभ यह है कि इसके कुछ चलती हिस्से हैं और वे समझ में आते हैं। मैंने इसे अंडरस्कोर हटाने के लिए संपादित किया है।
ndyer

यह जोड़ा जाना चाहिए, कि अगर आप gengetopt का उपयोग करते हैं, तो इसे सीधे Makefile में gengetopt में जोड़ सकते हैं: gengetopt --set-version = $ (GIT_VERSION)
Trygve

1
पहला कथन उद्धरणों के साथ होना चाहिए GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)", बिना उद्धरणों के काम नहीं करता।
हाबिल टॉम

11

मैंने @ Kinopiko के उत्तर के समान कुछ का उपयोग करके समाप्त किया, लेकिन मैंने पर्ल के बजाय awk का उपयोग किया। यह उपयोगी है अगर आपका विंडोज़ मशीनों पर अटक गया है जो कि मिंगव की प्रकृति द्वारा स्थापित है, लेकिन पर्ल नहीं। यहां देखिए यह कैसे काम करता है।

मेरे मेकफाइल में एक लाइन है जो एसी फ़ाइल बनाने के लिए git, date और awk को आमंत्रित करती है:

$(MyLibs)/version.c: FORCE 
    $(GIT) rev-parse HEAD | awk ' BEGIN {print "#include \"version.h\""} {print "const char * build_git_sha = \"" $$0"\";"} END {}' > $(MyLibs)/version.c
    date | awk 'BEGIN {} {print "const char * build_git_time = \""$$0"\";"} END {} ' >> $(MyLibs)/version.c 

हर बार जब मैं अपना कोड संकलित करता हूं, तो awk कमांड एक वर्जन.c फाइल बनाता है जो इस तरह दिखता है:

/* version.c */
#include "version.h"
const char * build_git_sha = "ac5bffc90f0034df9e091a7b3aa12d150df26a0e";
const char * build_git_time = "Thu Dec  3 18:03:58 EST 2009";

मेरे पास एक स्थैतिक version.h फ़ाइल है जो इस तरह दिखता है:

/*version.h*/
#ifndef VERSION_H_
#define VERSION_H_

extern const char * build_git_time;
extern const char * build_git_sha;


#endif /* VERSION_H_ */

मेरे बाकी कोड अब वर्जन -h हैडर सहित बिल्ड-टाइम और git हैश तक पहुँच सकते हैं। यह सब लपेटने के लिए, मैं अपने .itignore फ़ाइल में एक पंक्ति जोड़कर version.c को अनदेखा करने की बात कहता हूँ। इस तरह git मुझे लगातार मर्ज टकराव नहीं दे रहा है। उम्मीद है की यह मदद करेगा!


एक परिशिष्ट ... यह Matlab में काम करेगा: mathworks.com/matlabcentral/fileexchange/32864-get-git-info
AndyL

1
मुझे नहीं लगता कि FORCEयह एक अच्छा विचार है क्योंकि मेकफाइल कभी संतुष्ट नहीं होगा (हर बार जब आप एक नया हेडर बनाते हैं)। इसके बजाय, आप केवल सूत्र में प्रासंगिक गिट फ़ाइलों में निर्भरता जोड़ सकते हैं $(MyLibs)/version.c : .git/COMMIT_EDITMSG .git/HEAD COMMIT_EDITMSGहर बार जब आप कोई कमिटमेंट करते हैं तो फाइल बदल जाती है और HEADहर बार जब आप इतिहास ब्राउज़ करते हैं, तो आपकी फाइल प्रासंगिक होने पर हर बार अपडेट हो जाती है।
कामिल एस जेरोन

9

आपका कार्यक्रम git describeरनटाइम पर या निर्माण प्रक्रिया के हिस्से के रूप में खोल सकता है।


4
से git help describe: "सबसे हाल का टैग दिखाएं जो एक प्रतिबद्ध से पहुंच योग्य है" - यह वह नहीं है जो सवाल पूछता है। मैं हालांकि आपके उत्तर के बाकी हिस्सों से सहमत हूं। सही होने के लिए, कमांड होना चाहिए git rev-parse HEAD
माइक मजुर

5
@ मिमिकम, git describeअधिकांश अन्य परियोजनाएं क्या उपयोग करती हैं, क्योंकि इसमें मानव-पढ़ने योग्य टैग जानकारी भी शामिल है। यदि आप एक टैग पर बिल्कुल नहीं हैं, तो यह निकटतम टैग के बाद से कमिट की संख्या पर निर्भर करता है, और संक्षिप्त संशोधन हैश।
17

7

दो चीजें हैं जो आप कर सकते हैं:

  • आप गिट बना सकते हैं फ़ाइल में कुछ संस्करण जानकारी एम्बेड करने के हैं।

    सरल तरीका ident विशेषता का उपयोग करना है , जिसका अर्थ है (उदाहरण के लिए)

    *.yaml    ident
    

    में .gitattributesफ़ाइल, और $Id$उचित जगह में। यह स्वचालित रूप से SHA-1 फ़ाइल की सामग्री (बूँद आईडी) की पहचान करने के लिए विस्तारित किया जाएगा : यह फ़ाइल संस्करण या अंतिम वचन नहीं है।

    Git इस तरह से $ Id का समर्थन करता है कि उन फ़ाइलों को छूने से बचें जो शाखा स्विचिंग, रीवाइंडिंग शाखा आदि के दौरान नहीं बदली गई थीं। यदि आप वास्तव में Git को फ़ाइल में प्रतिबद्ध (संस्करण) पहचानकर्ता या विवरण डालना चाहते हैं, तो आप (ab) उपयोग कर सकते हैं filterविशेषता, चेकआउट पर कुछ कीवर्ड (जैसे $ संशोधन $) का विस्तार करने के लिए स्वच्छ / स्मूद फिल्टर का उपयोग करके, और इसे कमिट करने के लिए साफ़ करें।

  • आप ऐसा करने के लिए बिल्ड प्रक्रिया कर सकते हैं , जैसे कि लिनक्स कर्नेल या गिट खुद करता है।

    GIT Makefile में GIT-VERSION-GEN स्क्रिप्ट और इसके उपयोग पर एक नज़र डालें , या उदाहरण के लिए कि यह Makefile पीढ़ी / कॉन्फ़िगरेशन के दौरान संस्करण की जानकारी कैसे एम्बेड करता हैgitweb/gitweb.cgi फ़ाइल ।

    GIT-VERSION-GEN संस्करण विवरण उत्पन्न करने के लिए git वर्णन का उपयोग करता है । यह बेहतर काम करने की जरूरत है कि आप टैग (हस्ताक्षरित / एनोटेट टैग का उपयोग करके) अपनी परियोजना के रिलीज / मील के पत्थर को जारी करते हैं।


4

जब मुझे ऐसा करने की आवश्यकता होती है, तो मैं एक टैग का उपयोग करता हूं , जैसे RELEASE_1_23। मैं यह तय कर सकता हूं कि SHA-1 को जाने बिना टैग क्या हो सकता है। मैं तब टैग करता हूं। आप उस टैग को अपने प्रोग्राम में वैसे भी स्टोर कर सकते हैं जो आपको पसंद है।


4

Ndd27 के उत्तर के आधार पर, मैं कोड को एक भिन्न तरीके से बनाने के लिए डिफ़ॉल्ट मान के साथ version.h फ़ाइल के साथ संयोजन में निर्भरता स्कैनिंग के साथ एक संस्करण का उपयोग कर रहा हूं। Version.h शामिल सभी फ़ाइलों को फिर से बनाया जाएगा।

इसमें एक अलग परिभाषित के रूप में संशोधन की तारीख भी शामिल है।

# Get git commit version and date
GIT_VERSION := $(shell git --no-pager describe --tags --always --dirty)
GIT_DATE := $(firstword $(shell git --no-pager show --date=short --format="%ad" --name-only))

# recompile version.h dependants when GIT_VERSION changes, uses temporary file version~
.PHONY: force
version~: force
    @echo '$(GIT_VERSION) $(GIT_DATE)' | cmp -s - $@ || echo '$(GIT_VERSION) $(GIT_DATE)' > $@
version.h: version~
    @touch $@
    @echo Git version $(GIT_VERSION) $(GIT_DATE)

1
मुझे लगता है कि आपके पास GIT_VERSION है और GIT_DATE CFLAGS से होकर गुजरा है इसलिए version.h उनका उपयोग कर सकते हैं। ठंडा!
जेसी चिशोल्म

2

मैं अपने वैज्ञानिक कोड में परिवर्तनों को ट्रैक करने के लिए git का उपयोग करता हूं। मैं एक बाहरी कार्यक्रम का उपयोग नहीं करना चाहता था क्योंकि यह कोड की पोर्टेबिलिटी (यदि कोई उदाहरण के लिए MSVS पर परिवर्तन करना चाहेगा) को सीमित करता है।

मेरा समाधान गणना के लिए केवल मुख्य शाखा का उपयोग करना था और इसे प्रीप्रोसेसर मैक्रोज़ __DATE__और का उपयोग करके निर्माण समय बनाना था __TIME__। इस तरह मैं इसे git लॉग के साथ देख सकता हूं और देख सकता हूं कि मैं किस संस्करण का उपयोग कर रहा हूं। रेफरी: http://gcc.gnu.org/onbuildocs/cpp/Standard-Predefined-Macros.html

समस्या को हल करने के लिए एक और सुंदर तरीका निष्पादन योग्य में गिट लॉग को शामिल करना है। गिट लॉग से ऑब्जेक्ट फ़ाइल बनाएं और इसे कोड में शामिल करें। इस समय आपके द्वारा उपयोग किया जाने वाला एकमात्र बाहरी कार्यक्रम ओब्जेक्टोपी है लेकिन इसमें कम कोडिंग है। रेफरी: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 और एम्बेड डेटा C ++ प्रोग्राम में


1
प्रीप्रोसेसर मैक्रोज़ का उपयोग बहुत चालाक है! धन्यवाद।
एंडीएल

4
लेकिन अगर मैं एक पुराने संस्करण की जांच करता हूं, तो इसे संकलित करें, यह मुझे गलत प्रतिबद्ध करने के लिए मार्गदर्शन करेगा।
सेबेस्टियन मच

2

आपको एक हेडर फ़ाइल (जैसे कि cmd लाइन से इको का उपयोग करके) उत्पन्न करना है, कुछ इस तरह है:

#define GIT_HASH \
"098709a0b098c098d0e"

इसे उत्पन्न करने के लिए कुछ इस तरह से उपयोग करें:

echo #define GIT_HASH \ > file.h
echo " > file.h
echo git status <whatever cmd to get the hash> > file.h
echo " > file.h

इसे संकलित करने के लिए उद्धरण और बैकस्लैश के साथ खेलने की आवश्यकता हो सकती है, लेकिन आपको यह विचार मिलता है।


बस सोच रहा था, क्या वह हर बार ऐसा नहीं करता है और इसलिए फ़ाइल बदल जाता है। वह, और फिर स्रोत में बदलाव करता है, तो गीता क्या बदल जाएगी?
जॉर्ज इजराइल Peña

@ ब्लेक .. यही सोच रहा था। लेकिन प्रोग्राम के समय रन के बारे में पूछने के बारे में बोंडलन का विचार इस समस्या के आसपास लगता है।
एंडीएल

6
ठीक है, इस फाइल को परियोजना के निर्माण के दौरान हर समय .itignore के तहत और उत्पन्न करना होगा।
इगोर ज़ेवाका

वैकल्पिक रूप से आप इस फ़ाइल का एक मूल संस्करण शामिल कर सकते हैं और --assume-unchangedउस पर ध्वज सेट कर सकते हैं ( git update-index --assume-unchanged)
इगोर ज़ेवाका

1

फिर भी मेकफाइल और शेल पर आधारित एक और भिन्नता

GIT_COMMIT_FILE=git_commit_filename.h

$(GIT_COMMIT_FILE): phony
    $(eval GIT_COMMIT_SHA=$(shell git describe --abbrev=6 --always 2>/dev/null || echo 'Error'))
    @echo SHA=$(GIT_COMMIT_SHA)
    echo -n "static const char *GIT_COMMIT_SHA = \"$(GIT_COMMIT_SHA)\";" > $(GIT_COMMIT_FILE)

फ़ाइल git_commit_filename.h स्थिर कॉन्स्ट चर * GIT_COMMIT_SHA = "" वाली एकल पंक्ति के साथ समाप्त होगी;

से https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406


1

यह CMake प्रोजेक्ट के लिए एक समाधान है जो विंडोज और लिनक्स के लिए काम करता है, बिना किसी अन्य प्रोग्राम (जैसे स्क्रिप्ट भाषाओं) को स्थापित करने की आवश्यकता के बिना।

Git हैश को एक स्क्रिप्ट द्वारा .h फ़ाइल में लिखा जाता है, जो कि लिनक्स पर संकलन करते समय एक bash स्क्रिप्ट या विंडोज़ पर संकलन करते समय एक विंडोज़ बैच स्क्रिप्ट, और CMakeLists.txt में एक इफ़-क्लॉज स्क्रिप्ट को प्लेटफ़ॉर्म के अनुरूप चुनता है कोड पर संकलन है।

निम्नलिखित 2 लिपियाँ CMakeLists.txt के समान निर्देशिका में सहेजी गई हैं:

get_git_hash.sh:

#!/bin/bash
hash=$(git describe --dirty --always --tags)
echo "#ifndef GITHASH_H" > include/my_project/githash.h
echo "#define GITHASH_H" >> include/my_project/githash.h
echo "const std::string kGitHash = \"$hash\";" >> include/my_project/githash.h
echo "#endif // GITHASH_H" >> include/my_project/githash.h

get_git_hash.cmd:

@echo off
FOR /F "tokens=* USEBACKQ" %%F IN (`git describe --dirty --always --tags`) DO (
SET var=%%F
)
ECHO #ifndef GITHASH_H > include/my_project/githash.h
ECHO #define GITHASH_H >> include/my_project/githash.h
ECHO const std::string kGitHash = "%var%"; >> include/my_project/githash.h
ECHO #endif // GITHASH_H >> include/my_project/githash.h

CMakeLists.txt में follwoing लाइनें जोड़ी जाती हैं

if(WIN32)
  add_custom_target(
    run ALL
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMAND get_git_hash.cmd
  )
else()
  add_custom_target(
    run ALL
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMAND ./get_git_hash.sh
  )
endif()

include_directories(include)

कोड में उत्पन्न फ़ाइल के साथ शामिल है #include <my_project/githash.h>और git हैश के साथ टर्मिनल पर मुद्रित किया जा सकता है std::cout << "Software version: " << kGitHash << std::endl;, या इसी तरह से एक याम्ल (या किसी भी) फ़ाइल में लिखा जा सकता है ।


0

आप देख सकते हैं कि मैंने इसे मूल वचन में कैसे लागू किया

मूल रूप से, कभी-कभी टैग करें, और सुनिश्चित करें कि आपके द्वारा वितरित की गई वस्तु make distसमान या समान है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.