गिट के लिए उपयोग-प्रतिबद्ध-समय के बराबर क्या है?


97

मुझे अपने स्थानीय और मेरे सर्वर पर फाइलों के टाइमस्टैम्प की जरूरत है। यह उप-बार-बार = कॉन्फिगर में सही सेट करके सबवर्सन के साथ पूरा किया जाता है ताकि प्रत्येक फाइल के अंतिम रूप को संशोधित किया जा सके।

हर बार जब मैं अपनी रिपॉजिटरी को क्लोन करता हूं, तो मैं चाहता हूं कि फाइलों के टाइमस्टैम्प्स को प्रतिबिंबित करें जब वे आखिरी बार रिमोट रिपॉजिटरी में बदले गए थे, तब नहीं जब मैंने रेपो पर क्लोन किया था।

क्या गिट के साथ ऐसा करने का कोई तरीका है?


मेरी तैनाती की प्रक्रिया के हिस्से के रूप में, मैं एक सीडीएन को संपत्ति (चित्र, जावास्क्रिप्ट फाइलें और सीएसएस फाइलें) अपलोड करता हूं। प्रत्येक फ़ाइलनाम को अंतिम संशोधित टाइमस्टैम्प के साथ जोड़ा जाता है। यह महत्वपूर्ण है कि मैं अपनी हर संपत्ति को हर बार जब मैं तैनात करता हूं, तो समाप्त न करें। (उपयोग-कम-समय का एक और दुष्परिणाम यह है कि मैं इस प्रक्रिया को अपने स्थानीय पर कर सकता हूं और यह जानता हूं कि मेरा सर्वर उसी फाइलों को संदर्भित करेगा, लेकिन यह उतना महत्वपूर्ण नहीं है।) यदि एक git क्लोन करने के बजाय, मैंने किया। मेरे रिमोट रेपो से git रिसेट -हार्ड के बाद git fetch, जो सिंगल सर्वर के लिए काम करेगा, लेकिन मल्टीपल सर्वर के लिए नहीं क्योंकि प्रत्येक पर टाइमस्टैम्प अलग होगा।
बेन डब्ल्यू

@BenW: git annexचित्रों का ट्रैक रखने के लिए उपयोगी हो सकता है
jfs

आप आईडी की जाँच करके क्या बदल गया है की जाँच कर सकते हैं। आप फाइलसिस्टम टाइमस्टैम्प बनाने का प्रयास कर रहे हैं इसका मतलब वही है जो वीकेएस टाइमस्टैम्प है। वे एक ही बात मतलब नहीं है।
जथिल

जवाबों:


25

मुझे यकीन नहीं है कि यह डीवीसीएस के लिए उचित होगा (जैसा कि "वितरित" वीसीएस में)

बड़ी चर्चा 2007 में हुई थी (देखें यह धागा)

और लिनुस के कुछ उत्तर इस विचार के प्रति उत्सुक नहीं थे। यहाँ एक नमूना है:

मुझे माफ कर दो। आप कैसे कुछ करने के लिए एक डेटस्टैम्प वापस है कि एक साधारण "मेकअप" कर देगा स्थापित करने के लिए यह गलत नहीं देखते हैं miscompile अपने स्रोत पेड़, मैं "गलत" आप के बारे में बात कर रहे हैं की क्या defintiion पता नहीं है।
यह गलत है।
यह बेवकूफी है।
और इसे लागू करना पूरी तरह से प्रभावी है।


(नोट: छोटा सुधार: चेकआउट के बाद, अप-टू-डेट फ़ाइलों का टाइमस्टैम्प अब संशोधित नहीं किया गया है (Git 2.2.2+, जनवरी 2015): "गिट चेकआउट - मैं शाखाओं को स्विच करते समय टाइमस्टैम्प कैसे बना सकता हूं?"


लंबा जवाब था:

मुझे लगता है कि आप इसके बजाय कई रिपॉजिटरी का उपयोग करके बहुत बेहतर हैं, अगर यह कुछ सामान्य है।

टाइमस्टैम्प के साथ मेसिंग सामान्य रूप से काम नहीं कर रहा है। यह सिर्फ आपको गारंटी देता है कि "मेक" वास्तव में बहुत बुरे तरीके से भ्रमित हो जाता है, और बहुत अधिक रीकॉम्पेलिंग के बजाय पर्याप्त रूप से फिर से इकट्ठा नहीं करता है

Git कई अलग-अलग तरीकों से आपके "अन्य शाखा की जाँच करें" चीज़ को बहुत आसानी से करना संभव बनाता है।

आप कुछ तुच्छ स्क्रिप्ट बना सकते हैं जो निम्न में से किसी को भी (तुच्छ से अधिक विदेशी को लेकर) करती है:

  • बस एक नया रेपो बनाएँ:
    git clone old new
    cd new
    git checkout origin/<branch>

और तुम वहाँ हो आपके पुराने रेपो में पुराने टाइमस्टैम्प ठीक हैं, और आप पुराने एक को प्रभावित किए बिना, नए में काम कर सकते हैं (और संकलित कर सकते हैं)।

मूल रूप से तात्कालिक बनाने के लिए "git क्लोन" के लिए "-n -l -s" झंडे का उपयोग करें। बहुत सारी फाइलों के लिए (जैसे कि कर्नेल की तरह बड़ी रिपोज), यह सिर्फ स्विचिंग शाखाओं के रूप में तेज़ नहीं होने जा रहा है, लेकिन काम करने वाले पेड़ की एक दूसरी प्रतिलिपि हाइव करना काफी शक्तिशाली हो सकता है।

  • यदि आप चाहते हैं, तो केवल एक टार-बॉल के साथ ही काम करें
    git archive --format=tar --prefix=new-tree/ <branchname> |
            (cd .. ; tar xvf -)

जो वास्तव में काफी तेज है, अगर आप सिर्फ स्नैपशॉट चाहते हैं।

  • " git show" के लिए अभ्यस्त हो जाओ , और बस व्यक्तिगत फ़ाइलों को देखो।
    यह वास्तव में है वास्तव में समय पर उपयोगी। तुम बस करो
    git show otherbranch:filename

एक xterm विंडो में, और दूसरी विंडो में अपनी वर्तमान शाखा में उसी फ़ाइल को देखें। विशेष रूप से, यह स्क्रिप्ट करने योग्य संपादकों (यानी GNU emacs) के साथ करने के लिए तुच्छ होना चाहिए, जहां मूल रूप से संपादक के भीतर अन्य शाखाओं के लिए एक संपूर्ण "dired मोड" होना चाहिए, इसका उपयोग करना। सभी मुझे पता है के लिए, emacs git मोड पहले से ही कुछ इस तरह प्रदान करता है (मैं एक emacs उपयोगकर्ता नहीं हूँ)

  • और उस "आभासी निर्देशिका" के चरम उदाहरण में, कम से कम FUSE के लिए git प्लगइन पर काम करने वाला कोई व्यक्ति था, यानी आप शाब्दिक रूप से आभासी निर्देशिकाओं को अपनी सभी शाखाओं को दिखा सकते हैं।

और मुझे यकीन है कि उपरोक्त में से कोई भी फ़ाइल टाइमस्टैम्प के साथ गेम खेलने से बेहतर विकल्प हैं।

लीनुस


5
माना। आपको एक वितरण प्रणाली के साथ एक DVCS को भ्रमित नहीं करना चाहिए। gitएक DVCS है, जो स्रोत कोड में हेरफेर करने के लिए है जिसे आपके अंतिम उत्पाद में बनाया जाएगा । यदि आप एक वितरण प्रणाली चाहते हैं, तो आप जानते हैं कि कहां खोजना है rsync
रैंडल श्वार्ट्ज

14
हम्म, मुझे उसके तर्क पर भरोसा करना होगा कि यह असंभव है। फिर चाहे वह गलत हो या बेवकूफी। मैं टाइमस्टैम्प का उपयोग करके अपनी फ़ाइलों को संस्करणबद्ध करता हूं और उन्हें एक सीडीएन पर अपलोड करता हूं, यही कारण है कि यह महत्वपूर्ण है कि टाइमस्टैम्प प्रतिबिंबित करता है कि फाइल वास्तव में संशोधित हुई थी, न कि जब इसे पिछली बार रेपो से नीचे खींचा गया था।
बेन डब्ल्यू

3
@ बीन डब्ल्यू: "लिनुस का जवाब" यह कहने के लिए यहां नहीं है कि यह आपकी विशेष स्थिति में गलत है। यह केवल एक अनुस्मारक के रूप में है कि एक डीवीसीएस उस तरह की सुविधा (टाइमस्टैम्प संरक्षण) के लिए अच्छी तरह से अनुकूल नहीं है।
वॉन

15
@VonC: चूंकि बाज़ार और बाज़ार जैसे अन्य आधुनिक डीवीसीएस टाइमस्टैम्प को ठीक तरह से संभालते हैं, मैं यह कहूंगा कि " गिट उस तरह की सुविधा के लिए अच्छी तरह से अनुकूल नहीं है"। यदि "ए" डीवीसीएस में यह सुविधा होनी चाहिए कि यह बहस का विषय है (और मुझे लगता है कि वे ऐसा करते हैं)।
MestreLion

10
यह सवाल का जवाब नहीं है, लेकिन एक संस्करण नियंत्रण प्रणाली में ऐसा करने के गुणों के बारे में एक दार्शनिक चर्चा है। यदि वह व्यक्ति पसंद करता है, तो उन्होंने पूछा होगा, "क्या कारण है कि git फ़ाइलों के संशोधित समय के लिए प्रतिबद्ध समय का उपयोग नहीं करता है?"
थोमसफ़ुच्स

85

यदि, हालांकि आप वास्तव में टाइमस्टैम्प के लिए प्रतिबद्ध समय का उपयोग करना चाहते हैं , तो इस स्क्रिप्ट का उपयोग करके देखें और इसे $ GIT_DIR / .गित / हुक / पोस्ट-चेकआउट फ़ाइल में रखें (निष्पादन योग्य)।

#!/bin/sh -e

OS=${OS:-`uname`}
old_rev="$1"
new_rev="$2"

get_file_rev() {
    git rev-list -n 1 "$new_rev" "$1"
}

if   [ "$OS" = 'Linux' ]
then
    update_file_timestamp() {
        file_time=`git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "$1")" | head -n 1`
        touch -d "$file_time" "$1"
    }
elif [ "$OS" = 'FreeBSD' ]
then
    update_file_timestamp() {
        file_time=`date -r "$(git show --pretty=format:%at --abbrev-commit "$(get_file_rev "$1")" | head -n 1)" '+%Y%m%d%H%M.%S'`
        touch -h -t "$file_time" "$1"
    }
else
    echo "timestamp changing not implemented" >&2
    exit 1
fi

IFS=`printf '\t\n\t'`

git ls-files | while read -r file
do
    update_file_timestamp "$file"
done

हालाँकि, ध्यान दें कि यह स्क्रिप्ट बड़े रिपॉजिटरी (जहाँ बड़ी फ़ाइलों का अर्थ है, बड़े आकार के साइज़ नहीं) की जाँच के लिए काफी देरी होगी।


55
+1 वास्तविक उत्तर के लिए, केवल "ऐसा मत करो" कहने के बजाय
DanC

4
| head -n 1के रूप में यह एक नई प्रक्रिया spawns, बचा जाना चाहिए -n 1के लिए git rev-listऔर git logबजाय प्रयोग किया जा सकता है।
इरेगॉन

3
इसके साथ लाइनों को पढ़ना बेहतर नहीं है `...`और for; देखें कि आप "के लिए" के साथ लाइनें क्यों नहीं पढ़ते हैं । मैं git ls-files -zऔर के लिए जाना था while IFS= read -r -d ''
मुसीफिल

2
क्या विंडोज संस्करण संभव है?
एह्रीक

2
इसके बजाय git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "$1")" | head -n 1आप कर सकते हैं git show --pretty=format:%ai -s "$(get_file_rev "$1")", यह showकमांड द्वारा उत्पन्न होने वाले बहुत कम डेटा का कारण बनता है और ओवरहेड को कम करना चाहिए।
स्कॉट चैम्बरलेन

79

अद्यतन : मेरा समाधान अब डेबियन / उबंटू / टकसाल, फेडोरा, जेंटो और संभवतः अन्य डिस्ट्रो में पैक किया गया है:

https://github.com/MestreLion/git-tools#install

sudo apt install git-restore-mtime  # Debian/Ubuntu/Mint
yum install git-tools               # Fedora/ RHEL / CentOS
emerge dev-vcs/git-tools            # Gentoo

IMHO, टाइमस्टैम्प (और अन्य मेटाडेटा जैसे अनुमतियां और स्वामित्व) को संग्रहीत नहीं करना एक बड़ी सीमा है git

टाइमस्टैम्प्स के लिनेश का तर्क हानिकारक है क्योंकि यह "भ्रमित make" लंगड़ा है :

  • make clean किसी भी समस्या को ठीक करने के लिए पर्याप्त है।

  • केवल उन प्रोजेक्ट्स पर लागू होता है जो makeज्यादातर उपयोग करते हैं , ज्यादातर C / C ++। यह पूरी तरह से पायथन, पर्ल, या सामान्य रूप से प्रलेखन जैसी लिपियों के लिए मूट है।

  • टाइमस्टैम्प लगाने पर केवल नुकसान होता है। रेपो में उन्हें संग्रहीत करने में कोई नुकसान नहीं होगा । उन्हें लागू करने के लिए एक सरल हो सकता है --with-timestampsके लिए विकल्प git checkoutऔर दोस्तों ( clone, pullआदि), पर उपयोगकर्ता के विवेक।

बाज़ार और मर्क्यूरियल दोनों ही मेटाडेटा स्टोर करते हैं। जांच करते समय उपयोगकर्ता उन्हें लागू कर सकते हैं या नहीं। लेकिन गिट में, चूंकि मूल टाइमस्टैम्प रेपो में भी उपलब्ध नहीं हैं , इसलिए ऐसा कोई विकल्प नहीं है।

इसलिए, एक बहुत छोटे लाभ के लिए (सब कुछ फिर से संकलित करने के लिए नहीं) जो परियोजनाओं के सबसेट के लिए विशिष्ट है, gitजैसा कि एक सामान्य डीवीसीएस अपंग था , फाइलों के बारे में कुछ जानकारी खो जाती है , और, जैसा कि लिनुस ने कहा, यह करने के लिए महत्वपूर्ण है अब। दु: खी

उसने कहा, क्या मैं 2 दृष्टिकोण प्रस्तुत कर सकता हूं?

1 - http://repo.or.cz/w/metastore.git , डेविड हैदरमैन द्वारा। git पहली बार में जो करना चाहिए था वह करने की कोशिश करता है : आने पर (पूर्व-पूर्व हुक के माध्यम से ) रेपो में मेटाडेटा (न केवल टाइमस्टैम्प) को संग्रहीत करता है , और खींचने पर भी उन्हें फिर से लागू करता है (हुक के माध्यम से भी)।

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

  • गिट-रिस्टोर-माइम , बहुत सारे विकल्पों के साथ, किसी भी रिपॉजिटरी लेआउट का समर्थन करता है, और पायथन 3 पर चलता है।

नीचे एक है वास्तव में स्क्रिप्ट के नंगे हड्डियों संस्करण, एक-का-प्रमाण अवधारणा के रूप में, अजगर 2.7 पर। वास्तविक उपयोग के लिए मैं ऊपर दिए गए पूर्ण संस्करण की दृढ़ता से अनुशंसा करता हूं:

#!/usr/bin/env python
# Bare-bones version. Current dir must be top-level of work tree.
# Usage: git-restore-mtime-bare [pathspecs...]
# By default update all files
# Example: to only update only the README and files in ./doc:
# git-restore-mtime-bare README doc

import subprocess, shlex
import sys, os.path

filelist = set()
for path in (sys.argv[1:] or [os.path.curdir]):
    if os.path.isfile(path) or os.path.islink(path):
        filelist.add(os.path.relpath(path))
    elif os.path.isdir(path):
        for root, subdirs, files in os.walk(path):
            if '.git' in subdirs:
                subdirs.remove('.git')
            for file in files:
                filelist.add(os.path.relpath(os.path.join(root, file)))

mtime = 0
gitobj = subprocess.Popen(shlex.split('git whatchanged --pretty=%at'),
                          stdout=subprocess.PIPE)
for line in gitobj.stdout:
    line = line.strip()
    if not line: continue

    if line.startswith(':'):
        file = line.split('\t')[-1]
        if file in filelist:
            filelist.remove(file)
            #print mtime, file
            os.utime(file, (mtime, mtime))
    else:
        mtime = long(line)

    # All files done?
    if not filelist:
        break

प्रदर्शन बहुत प्रभावशाली है, यहां तक ​​कि राक्षस परियोजनाओं के लिए wine, gitया यहां तक ​​कि लिनक्स कर्नेल:

bash
# 0.27 seconds
# 5,750 log lines processed
# 62 commits evaluated
# 1,155 updated files

git
# 3.71 seconds
# 96,702 log lines processed
# 24,217 commits evaluated
# 2,495 updated files

wine
# 13.53 seconds
# 443,979 log lines processed
# 91,703 commits evaluated
# 6,005 updated files

linux kernel
# 59.11 seconds
# 1,484,567 log lines processed
# 313,164 commits evaluated
# 40,902 updated files

2
लेकिन टाइमस्टैम्प आदि git को स्टोर करता है , यह सिर्फ टाइमस्टैम्प को डिफ़ॉल्ट रूप से सेट नहीं करता है। बस के उत्पादन को देखोgit ls-files --debug
रॉस स्मिथ द्वितीय

9
@RossSmithII: git ls-filesवर्किंग डायरेक्टरी और इंडेक्स पर काम कर रहा है, इसलिए इसका मतलब यह नहीं है कि यह वास्तव में रेपो पर उस जानकारी को संग्रहीत करता है। यदि यह स्टोर करता है, तो माइम प्राप्त करना (और आवेदन करना) तुच्छ होगा।
MestreLion

13
"टाइमस्टैम्प्स का लिनेश का तर्क हानिकारक है, क्योंकि यह" भ्रमित करता है "लंगड़ा है" - सहमत 100%, एक डीसीवीएस को उस कोड के बारे में पता नहीं होना चाहिए या इसकी परवाह नहीं करनी चाहिए! फिर से यह सामान्य उपयोग के मामलों में विशिष्ट उपयोग के मामलों के लिए लिखे गए उपकरणों को पुन: पेश करने की कोशिश करने के नुकसान को दर्शाता है। मर्क्यूरियल है और हमेशा एक बेहतर विकल्प होगा क्योंकि इसे डिज़ाइन किया गया था, विकसित नहीं हुआ था।
इयान केम्प

6
@davec आपका स्वागत है, खुशी है कि यह उपयोगी था। Github.com/MestreLion/git-tools पर पूर्ण संस्करण पहले से ही विंडोज, पाइथन 3, नॉन-एएससीआईआई पाथनेम्स इत्यादि को संभालता है। उपरोक्त लिपि सिर्फ अवधारणा का एक कार्य प्रमाण है, इसे उत्पादन के उपयोग से बचें।
MestreLion

3
आपके तर्क मान्य हैं। मुझे उम्मीद है कि किसी न किसी तरह से कोई व्यक्ति आपके सुझाव के साथ-साथ समय-समय पर विकल्प चुनने के लिए एक वृद्धि अनुरोध करेगा।
वेबरजेन

12

मैंने गील का जवाब लिया और पोस्ट-कम हुक स्क्रिप्ट का उपयोग करने के बजाय, इसे अपने कस्टम परिनियोजन स्क्रिप्ट में काम किया।

अपडेट : मैंने | head -n@ ेरेगॉन के सुझाव के बाद एक को भी हटा दिया है , और उनमें रिक्त स्थान वाली फ़ाइलों के लिए समर्थन जोड़ा है:

# Adapted to use HEAD rather than the new commit ref
get_file_rev() {
    git rev-list -n 1 HEAD "$1"
}

# Same as Giel's answer above
update_file_timestamp() {
    file_time=`git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "$1")" | head -n 1`
    sudo touch -d "$file_time" "$1"
}

# Loop through and fix timestamps on all files in our CDN directory
old_ifs=$IFS
IFS=$'\n' # Support files with spaces in them
for file in $(git ls-files | grep "$cdn_dir")
do
    update_file_timestamp "${file}"
done
IFS=$old_ifs

धन्यवाद डैनियल, यह जानना उपयोगी है
एलेक्स डीन

प्रयोग में होने के कारण आदेश --abbrev-commitमें बहुत ही कम है (प्रतिबद्ध हैश आउटपुट का हिस्सा नहीं है) और ध्वज का उपयोग करने के साथ प्रतिस्थापित किया जा सकता हैgit show--pretty=format:%ai| head -n 1-sgit show
एलेन रुसमाई

1
@ DanielS.Sterling: %aiलेखक की तिथि, आईएसओ 8601 जैसे प्रारूप, सख्त iso8601 उपयोग के लिए %aI: git-scm.com/docs/git-show
Elan Ruusamäe

4

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

यह सॉल्यूशन एक फ़ाइल में mtimes को स्टोर करता है। git रिपॉजिटरी में mtimes, उन्हें कमिट्स के अनुसार अपडेट करता है (jsut चुनिंदा चरणबद्ध फाइलों के mtimes) और उन्हें चेकआउट पर लागू करता है। यह git के cygwin / mingw संस्करणों के साथ भी काम करता है (लेकिन आपको मानक cygwin से कुछ फ़ाइलों को git के फ़ोल्डर में कॉपी करने की आवश्यकता हो सकती है)

समाधान में 3 फाइलें हैं:

  1. mtimestore - कोर स्क्रिप्ट 3 विकल्प -a (सभी को बचाने - पहले से ही मौजूदा रेपो में काम के लिए - git-versed फ़ाइलों के साथ काम करता है) प्रदान करता है, -s (मंचन परिवर्तनों को बचाने के लिए), और -r उन्हें पुनर्स्थापित करने के लिए। यह वास्तव में 2 संस्करणों में आता है - एक बैश वन (पोर्टेबल, अच्छा, पढ़ने में आसान / संशोधित), और सी संस्करण (गन्दा एक लेकिन तेज, क्योंकि मिंग बैश बुरी तरह से धीमा है जो बड़ी परियोजनाओं पर बैश समाधान का उपयोग करना असंभव बनाता है)।
  2. पूर्व प्रतिबद्ध हुक
  3. पोस्ट-चेकआउट हुक

पूर्व के लिए प्रतिबद्ध:

#!/bin/bash
mtimestore -s
git add .mtimes

बाद चेकआउट

#!/bin/bash
mtimestore -r

mtimestore - बैश:

#!/bin/bash

function usage 
{
  echo "Usage: mtimestore (-a|-s|-r)"
  echo "Option  Meaning"
  echo " -a save-all - saves state of all files in a git repository"
  echo " -s save - saves mtime of all staged files of git repository"
  echo " -r restore - touches all files saved in .mtimes file"
  exit 1
}

function echodate 
{
  echo "$(stat -c %Y "$1")|$1" >> .mtimes
}

IFS=$'\n'

while getopts ":sar" optname
do
  case "$optname" in
    "s")
      echo "saving changes of staged files to file .mtimes"
      if [ -f .mtimes ]
      then
        mv .mtimes .mtimes_tmp
        pattern=".mtimes"
        for str in $(git diff --name-only --staged)
        do
          pattern="$pattern\|$str"
        done
        cat .mtimes_tmp | grep -vh "|\($pattern\)\b" >> .mtimes
      else
        echo "warning: file .mtimes does not exist - creating new"
      fi

      for str in $(git diff --name-only --staged)
      do
        echodate "$str" 
      done
      rm .mtimes_tmp 2> /dev/null
      ;;
    "a")
      echo "saving mtimes of all files to file .mtimes"
      rm .mtimes 2> /dev/null
      for str in $(git ls-files)
      do
        echodate "$str"
      done
      ;;
    "r")
      echo "restorim dates from .mtimes"
      if [ -f .mtimes ]
      then
        cat .mtimes | while read line
        do
          timestamp=$(date -d "1970-01-01 ${line%|*} sec GMT" +%Y%m%d%H%M.%S)
          touch -t $timestamp "${line##*|}"
        done
      else
        echo "warning: .mtimes not found"
      fi
      ;;
    ":")
      usage
      ;;
    *)
      usage
      ;;
esac

mtimestore - सी ++

#include <time.h>
#include <utime.h>
#include <sys/stat.h>
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <ctime>
#include <map>


void changedate(int time, const char* filename)
{
  try
  {
    struct utimbuf new_times;
    struct stat foo;
    stat(filename, &foo);

    new_times.actime = foo.st_atime;
    new_times.modtime = time;
    utime(filename, &new_times);
  }
  catch(...)
  {}
}

bool parsenum(int& num, char*& ptr)
{
  num = 0;
  if(!isdigit(*ptr))
    return false;
  while(isdigit(*ptr))
  {
    num = num*10 + (int)(*ptr) - 48;
    ptr++;
  }
  return true;
}

//splits line into numeral and text part - return numeral into time and set ptr to the position where filename starts
bool parseline(const char* line, int& time, char*& ptr)
{
  if(*line == '\n' || *line == '\r')
    return false;
  time = 0;
  ptr = (char*)line;
  if( parsenum(time, ptr))
  { 
    ptr++;
    return true;
  }
  else
    return false;
}

//replace \r and \n (otherwise is interpretted as part of filename)
void trim(char* string)
{
  char* ptr = string;
  while(*ptr != '\0')
  {
    if(*ptr == '\n' || *ptr == '\r')
      *ptr = '\0';
    ptr++;
  }
}


void help()
{
  std::cout << "version: 1.4" << std::endl;
  std::cout << "usage: mtimestore <switch>" << std::endl;
  std::cout << "options:" << std::endl;
  std::cout << "  -a  saves mtimes of all git-versed files into .mtimes file (meant to be done on intialization of mtime fixes)" << std::endl;
  std::cout << "  -s  saves mtimes of modified staged files into .mtimes file(meant to be put into pre-commit hook)" << std::endl;
  std::cout << "  -r  restores mtimes from .mtimes file (that is meant to be stored in repository server-side and to be called in post-checkout hook)" << std::endl;
  std::cout << "  -h  show this help" << std::endl;
}

void load_file(const char* file, std::map<std::string,int>& mapa)
{

  std::string line;
  std::ifstream myfile (file, std::ifstream::in);

  if(myfile.is_open())
  {
      while ( myfile.good() )
      {
        getline (myfile,line);
        int time;
        char* ptr;
        if( parseline(line.c_str(), time, ptr))
        {
          if(std::string(ptr) != std::string(".mtimes"))
            mapa[std::string(ptr)] = time;
        }
      }
    myfile.close();
  }

}

void update(std::map<std::string, int>& mapa, bool all)
{
  char path[2048];
  FILE *fp;
  if(all)
    fp = popen("git ls-files", "r");
  else
    fp = popen("git diff --name-only --staged", "r");

  while(fgets(path, 2048, fp) != NULL)
  {
    trim(path);
    struct stat foo;
    int err = stat(path, &foo);
    if(std::string(path) != std::string(".mtimes"))
      mapa[std::string(path)]=foo.st_mtime;
  }
}

void write(const char * file, std::map<std::string, int>& mapa)
{
  std::ofstream outputfile;
  outputfile.open(".mtimes", std::ios::out);
  for(std::map<std::string, int>::iterator itr = mapa.begin(); itr != mapa.end(); ++itr)
  {
    if(*(itr->first.c_str()) != '\0')
    {
      outputfile << itr->second << "|" << itr->first << std::endl;   
    }
  }
  outputfile.close();
}

int main(int argc, char *argv[])
{
  if(argc >= 2 && argv[1][0] == '-')
  {
    switch(argv[1][1])
    {
      case 'r':
        {
          std::cout << "restoring modification dates" << std::endl;
          std::string line;
          std::ifstream myfile (".mtimes");
          if (myfile.is_open())
          {
            while ( myfile.good() )
            {
              getline (myfile,line);
              int time, time2;
              char* ptr;
              parseline(line.c_str(), time, ptr);
              changedate(time, ptr);
            }
            myfile.close();
          }
        }
        break;
      case 'a':
      case 's':
        {
          std::cout << "saving modification times" << std::endl;

          std::map<std::string, int> mapa;
          load_file(".mtimes", mapa);
          update(mapa, argv[1][1] == 'a');
          write(".mtimes", mapa);
        }
        break;
      default:
        help();
        return 0;
    }
  } else
  {
    help();
    return 0;
  }

  return 0;
}
  • ध्यान दें कि हुक को अपने प्लेसमेंट को स्वचालित करने के लिए टेम्पलेट-निर्देशिका में रखा जा सकता है

अधिक जानकारी यहां मिल सकती है https://github.com/kareltucek/git-mtime-extension कुछ पुरानी जानकारी http://www.ktweb.cz/blog/index.php?page=page&id=116 पर है

// संपादित करें - c ++ संस्करण अपडेट किया गया:

  • अब c ++ संस्करण वर्णमाला क्रम को बनाए रखता है -> कम मर्ज संघर्ष।
  • बदसूरत प्रणाली से छुटकारा मिल गया () कॉल।
  • चेक-आउट हुक से $ git अपडेट-इंडेक्स --refresh $ हटाए गए। कछुआ गिट्ट के नीचे कुछ समस्याओं का कारण बनता है, और वैसे भी बहुत महत्वपूर्ण नहीं लगता है।
  • हमारे विंडोज़ पैकेज को http://ktweb.cz/blog/download/git-mtimestore-1.4.r पर डाउनलोड किया जा सकता है

// एडिट अप-टू-डेट संस्करण के लिए जीथब देखें


1
ध्यान दें कि एक चेकआउट के बाद, अप-टू-डेट फ़ाइलों का टाइमस्टैम्प अब संशोधित नहीं किया गया है (Git 2.2.2+, जनवरी 2015): stackoverflow.com/a/28256177/6309
VONC

3

निम्नलिखित स्क्रिप्ट में शामिल हैं -n 1और HEADसुझाव, अधिकांश गैर-लिनक्स वातावरण (जैसे सिगविन) में काम करता है, और इस तथ्य के बाद एक चेकआउट पर चलाया जा सकता है:

#!/bin/bash -e

OS=${OS:-`uname`}

get_file_rev() {
    git rev-list -n 1 HEAD "$1"
}    

if [ "$OS" = 'FreeBSD' ]
then
    update_file_timestamp() {
        file_time=`date -r "$(git show --pretty=format:%at --abbrev-commit "$(get_file_rev "$1")" | head -n 1)" '+%Y%m%d%H%M.%S'`
        touch -h -t "$file_time" "$1"
    }    
else    
    update_file_timestamp() {
        file_time=`git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "$1")" | head -n 1`
        touch -d "$file_time" "$1"
    }    
fi    

OLD_IFS=$IFS
IFS=$'\n'

for file in `git ls-files`
do
    update_file_timestamp "$file"
done

IFS=$OLD_IFS

git update-index --refresh

आपको उपरोक्त स्क्रिप्ट /path/to/templates/hooks/post-checkoutऔर / या नाम दिया गया मानकर /path/to/templates/hooks/post-update, आप इसे मौजूदा रिपॉजिटरी के माध्यम से चला सकते हैं:

git clone git://path/to/repository.git
cd repository
/path/to/templates/hooks/post-checkout

इसे एक और अंतिम पंक्ति की आवश्यकता है: git update-index --refresh // GUI टूल इंडेक्स पर भरोसा कर सकते हैं और इस तरह के ऑपरेशन के बाद सभी फाइल को "गंदा" स्थिति दिखा सकते हैं। अर्थात् विंडोज के लिए TortoiseGit में होता है कि code.google.com/p/tortoisegit/issues/detail?id=861
अर्योक '

1
और स्क्रिप्ट के लिए धन्यवाद। काश ऐसी स्क्रिप्ट गिट मानक इंस्टॉलर का हिस्सा होती। ऐसा नहीं है कि मुझे व्यक्तिगत रूप से इसकी आवश्यकता है, लेकिन टीम के सदस्यों को वीसीएस गोद लेने में लाल "स्टॉप" बैनर के रूप में टाइमस्टैम्प refreashing लगता है।
एरोक 'द

3

यह समाधान बहुत जल्दी चलना चाहिए। यह अपराधों के समय को कम करता है और लेखक के समय को कम करता है। यह कोई मॉड्यूल का उपयोग करता है तो यथोचित पोर्टेबल होना चाहिए।

#!/usr/bin/perl

# git-utimes: update file times to last commit on them
# Tom Christiansen <tchrist@perl.com>

use v5.10;      # for pipe open on a list
use strict;
use warnings;
use constant DEBUG => !!$ENV{DEBUG};

my @gitlog = ( 
    qw[git log --name-only], 
    qq[--format=format:"%s" %ct %at], 
    @ARGV,
);

open(GITLOG, "-|", @gitlog)             || die "$0: Cannot open pipe from `@gitlog`: $!\n";

our $Oops = 0;
our %Seen;
$/ = ""; 

while (<GITLOG>) {
    next if /^"Merge branch/;

    s/^"(.*)" //                        || die;
    my $msg = $1; 

    s/^(\d+) (\d+)\n//gm                || die;
    my @times = ($1, $2);               # last one, others are merges

    for my $file (split /\R/) {         # I'll kill you if you put vertical whitespace in our paths
        next if $Seen{$file}++;             
        next if !-f $file;              # no longer here

        printf "atime=%s mtime=%s %s -- %s\n", 
                (map { scalar localtime $_ } @times), 
                $file, $msg,
                                        if DEBUG;

        unless (utime @times, $file) {
            print STDERR "$0: Couldn't reset utimes on $file: $!\n";
            $Oops++;
        }   
    }   

}
exit $Oops;

2

यहाँ मामूली सुधारों के साथ उपरोक्त शैल समाधानों का एक अनुकूलित संस्करण है:

#!/bin/sh

if [ "$(uname)" = 'Darwin' ] ||
   [ "$(uname)" = 'FreeBSD' ]; then
   gittouch() {
      touch -ch -t "$(date -r "$(git log -1 --format=%ct "$1")" '+%Y%m%d%H%M.%S')" "$1"
   }
else
   gittouch() {
      touch -ch -d "$(git log -1 --format=%ci "$1")" "$1"
   }
fi

git ls-files |
   while IFS= read -r file; do
      gittouch "$file"
   done

1

यहाँ PHP के साथ एक विधि है:

<?php
$r = popen('git ls-files', 'r');
$n_file = 0;

while (true) {
   $s_gets = fgets($r);
   if (feof($r)) {
      break;
   }
   $s_trim = rtrim($s_gets);
   $m_file[$s_trim] = false;
   $n_file++;
}

$r = popen('git log -m -z --name-only --relative --format=%ct .', 'r');

while ($n_file > 0) {
   $s_get = fgets($r);
   $s_trim = rtrim($s_get);
   $a_name = explode("\x0", $s_trim);
   $s_unix = array_pop($a_name);
   foreach ($a_name as $s_name) {
      if (! array_key_exists($s_name, $m_file)) {
         continue;
      }
      if ($m_file[$s_name]) {
         continue;
      }
      touch($s_name, $n_unix);
      $m_file[$s_name] = true;
      $n_file--;
   }
   $n_unix = (int)($s_unix);
}

यह यहाँ उत्तर के समान है:

गिट के लिए उपयोग-प्रतिबद्ध-समय के बराबर क्या है?

यह उस उत्तर की तरह एक फ़ाइल सूची बनाता है, लेकिन यह git ls-files केवल कार्यशील निर्देशिका में देखने के बजाय से बनाता है । यह बाहर करने की समस्या को हल करता है .git, और यह अनटैक की गई फ़ाइलों की समस्या को भी हल करता है। इसके अलावा, यदि फ़ाइल की अंतिम प्रतिबद्धता एक मर्ज कमिट थी, जिसे मैंने हल किया था, तो वह उत्तर विफल हो जाता है git log -m। अन्य उत्तर की तरह, सभी फाइलें मिल जाने के बाद बंद हो जाएगी, इसलिए इसे सभी कमिट्स को पढ़ना नहीं है। उदाहरण के लिए:

https://github.com/git/git

इस पोस्टिंग के रूप में इसे केवल 292 कमिट्स पढ़ना था। इसके अलावा यह इतिहास की पुरानी फाइलों को आवश्यकतानुसार अनदेखा करता है, और यह एक ऐसी फाइल को नहीं छूता है जिसे पहले से ही छू लिया गया है। अंत में यह दूसरे समाधान की तुलना में थोड़ा तेज लगता है। git/gitरेपो के साथ परिणाम :

PS C:\git> Measure-Command { git-touch.php }
TotalSeconds      : 3.4215134

0

मैंने विंडोज संस्करण के लिए कुछ अनुरोधों को देखा, इसलिए यहां यह है। निम्नलिखित दो फ़ाइलें बनाएँ:

C: \ Program Files \ Git \ mingw64 \ share \ git-core \ टेम्पलेट्स \ हुक \ पोस्ट-चेकआउट

#!C:/Program\ Files/Git/usr/bin/sh.exe
exec powershell.exe -NoProfile -ExecutionPolicy Bypass -File "./$0.ps1"

C: \ Program Files \ Git \ mingw64 \ share \ git-core \ टेम्पलेट्स \ हुक \ पोस्ट-चेकबॉक्स .1.11

[string[]]$changes = &git whatchanged --pretty=%at
$mtime = [DateTime]::Now;
[string]$change = $null;
foreach($change in $changes)
{
    if($change.Length -eq 0) { continue; }
    if($change[0] -eq ":")
    {
        $parts = $change.Split("`t");
        $file = $parts[$parts.Length - 1];
        if([System.IO.File]::Exists($file))
        {
            [System.IO.File]::SetLastWriteTimeUtc($file, $mtime);
        }
    }
    else
    {
        #get timestamp
        $mtime = [DateTimeOffset]::FromUnixTimeSeconds([Int64]::Parse($change)).DateTime;
    }
}

यह git whatchanged का उपयोग करता है , इसलिए यह प्रत्येक फ़ाइल के लिए git को कॉल करने के बजाय एक पास में सभी फ़ाइलों के माध्यम से चलता है।


0

मैं एक ऐसी परियोजना पर काम कर रहा हूँ, जहाँ rsyncआधारित तैनाती के साथ उपयोग के लिए मेरे भंडार का एक क्लोन रखें। मैं विभिन्न वातावरणों को लक्षित करने के लिए शाखाओं का उपयोग करता हूं और git checkoutफ़ाइल संशोधनों को बदलने का कारण बनता हूं ।

यह जानने के बाद कि git फाइलों को चेकआउट करने और टाइमस्टैम्प को संरक्षित करने का एक तरीका प्रदान नहीं करता है जो कि मुझे git log --format=format:%ai --name-only .एक और SO प्रश्न में कमांड में आया है : बड़ी संख्या में फाइलों के लिए अंतिम प्रतिबद्ध तारीखों को सूचीबद्ध करें, जल्दी से

अब मैं touchअपनी परियोजना फ़ाइलों और निर्देशिकाओं के लिए निम्न स्क्रिप्ट का उपयोग कर रहा हूं ताकि मेरी तैनाती rsyncको आसान बनाने में आसानी हो:

<?php
$lines = explode("\n", shell_exec('git log --format=format:%ai --name-only .'));
$times = array();
$time  = null;
$cwd   = isset($argv[1]) ? $argv[1] : getcwd();
$dirs  = array();

foreach ($lines as $line) {
    if ($line === '') {
        $time = null;
    } else if ($time === null) {
        $time = strtotime($line);
    } else {
        $path = $cwd . DIRECTORY_SEPARATOR . $line;
        if (file_exists($path)) {
            $parent = dirname($path);
            $dirs[$parent] = max(isset($parent) ? $parent : 0, $time);
            touch($path, $time);
        }
    }
}

foreach ($dirs as $dir => $time) {
    touch($dir, $time);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.