Git için kullanım-kaydetme sürelerinin eşdeğeri nedir?


97

Yerel ve sunucumdaki dosyaların zaman damgalarının senkronize olması gerekiyor. Bu, Subversion ile yapılandırmada use-commit-times = true ayarlanarak gerçekleştirilir, böylece her dosyanın en son değiştirildiği zaman işlenir.

Depomu her klonladığımda, dosyaların zaman damgalarının depoyu klonladığım zaman değil, uzak depoda en son değiştirildikleri zamanı yansıtmasını istiyorum.

Bunu git ile yapmanın bir yolu var mı?


Dağıtım sürecimin bir parçası olarak varlıkları (görüntüler, javascript dosyaları ve css dosyaları) bir CDN'ye yüklüyorum. Her dosya adına son değiştirilen zaman damgası eklenir. Her konuşlandırdığımda tüm varlıklarımın süresinin dolmaması önemlidir. (Use-commit-times'ın bir başka yan etkisi de, bu işlemi kendi yerelimde yapabilmem ve sunucumun aynı dosyalara başvuracağını bilmemdir, ancak bu o kadar önemli değildir.) Git klonu yapmak yerine, bir git getirme ve ardından git sıfırlama işlemi, tek bir sunucu için işe yarayacak, ancak her birindeki zaman damgaları farklı olacağından birden fazla sunucu için geçerli olmayacak şekilde uzak depomdan zor bir şekilde.
Ben W

@BenW: git annexgörüntüleri takip etmek için yararlı olabilir
jfs

Kimlikleri kontrol ederek nelerin değiştiğini kontrol edebilirsiniz. Dosya sistemi zaman damgalarının vcs zaman damgalarıyla aynı anlama gelmesini sağlamaya çalışıyorsunuz. Aynı şeyi ifade etmiyorlar.
jthill

Yanıtlar:


25

Bunun bir DVCS için uygun olacağından emin değilim ("Dağıtılmış" VCS'deki gibi)

Büyük tartışma 2007'de çoktan gerçekleşmişti (bu konu başlığına bakın)

Ve Linus'un yanıtlarından bazıları bu fikre pek de hevesli değildi. İşte bir örnek:

Üzgünüm. Eğer 's YANLIŞ bir basit "marka" yapacak bir şey için bir tarih damgası geri nasıl ayarlanacağını görmüyorsanız miscompile kaynak ağacı, ben bahsediyorsun "yanlış" ne defintiion bilmiyorum.
Yanlış.
Bu aptalca.
Ve uygulanması tamamen INFEASIBLE.


(Not: küçük iyileştirme: bir teslim alma işleminden sonra, güncel dosyaların zaman damgaları artık değiştirilmez (Git 2.2.2+, Ocak 2015): "git checkout - dalları değiştirirken zaman damgalarını nasıl koruyabilirim?" .)


Uzun cevap şuydu:

Bence bu ortak bir şeyse, bunun yerine birden fazla depo kullanmaktan çok daha iyi olursunuz.

Zaman damgalarıyla uğraşmak genel olarak işe yaramayacak. Bu sadece "make" in gerçekten kötü bir şekilde karıştırılacağını ve çok fazla yeniden derlemek yerine yeterince yeniden derlenmediğini garanti edecek .

Git, "diğer şubeyi kontrol et" işinizi birçok farklı şekilde çok kolay bir şekilde yapmanızı mümkün kılar.

Aşağıdakilerden herhangi birini yapan (önemsizden egzotik olana kadar) bazı önemsiz komut dosyası oluşturabilirsiniz:

  • sadece yeni bir depo oluşturun:
    git clone old new
    cd new
    git checkout origin/<branch>

ve işte buradasın. Eski deponuzda eski zaman damgaları iyidir ve yenisinde eskisini etkilemeden çalışabilir (ve derleyebilirsiniz).

Temelde bunu anlık hale getirmek için "-n -l -s" ile "git clone" seçeneklerini kullanın. Bir çok dosya için (örneğin çekirdek gibi büyük depolar), dallar arasında geçiş yapmak kadar hızlı olmayacaktır, ancak çalışma ağacının ikinci bir kopyasına sahip olmak oldukça güçlü olabilir.

  • aynı şeyi sadece bir katran topuyla yapın, isterseniz
    git archive --format=tar --prefix=new-tree/ <branchname> |
            (cd .. ; tar xvf -)

sadece bir anlık görüntü istiyorsanız, bu gerçekten oldukça hızlıdır.

  • alışmak git showve sadece tek tek dosyalara bakmak.
    Bu aslında bazen gerçekten yararlıdır. Sen sadece yap
    git show otherbranch:filename

bir xterm penceresinde ve başka bir pencerede mevcut şubenizdeki aynı dosyaya bakın. Özellikle, bu, komut dosyası yazılabilir düzenleyicilerle (yani GNU emacs) yapmak önemsiz olmalıdır, burada bunu kullanarak düzenleyicideki diğer dallar için temelde tam bir "yönlendirilmiş kip" sahibi olmak mümkün olmalıdır. Tek bildiğim, emacs git modu zaten böyle bir şey sunuyor (ben bir emacs kullanıcısı değilim)

  • ve bu "sanal dizin" olayının en uç örneğinde, en azından biri FUSE için git eklentisi üzerinde çalışıyordu, yani tam anlamıyla tüm şubelerinizi gösteren sanal dizinlere sahip olabilirsiniz .

ve eminim yukarıdakilerden herhangi biri dosya zaman damgalı oyun oynamaktan daha iyi alternatiflerdir.

Linus


5
Kabul. Bir DVCS'yi bir dağıtım sistemiyle karıştırmamalısınız. gitedilecek kaynak kodunu işlemek için bir DVCS olduğunu inşa son ürün haline. Bir dağıtım sistemi istiyorsanız, nerede bulacağınızı biliyorsunuz rsync.
Randal Schwartz

14
Hm, bunun mümkün olmadığına dair argümanına güvenmem gerekecek. Yanlış da olsa aptalca da olsa başka bir konu. Dosyalarımı bir zaman damgası kullanarak biçimlendiriyorum ve bir CDN'ye yüklüyorum, bu nedenle zaman damgalarının, dosyanın depodan en son çekildiği zaman değil, gerçekten değiştirildiği zamanı yansıtması önemlidir.
Ben W

3
@Ben W: "Linus'un cevabı" sizin özel durumunuzda yanlış olduğunu söylemek için burada değil . Sadece bir DVCS'nin bu tür bir özellik için uygun olmadığını (zaman damgası koruma) hatırlatmak için var.
VonC

15
@VonC: Bazaar ve Mercurial gibi diğer modern DVCS'ler zaman damgalarını gayet iyi işlediğinden, " git bu tür bir özellik için pek uygun değil " demeyi tercih ederim . "Bir" DVCS'nin bu özelliğe sahip olması gerekiyorsa tartışmalı (ve kesinlikle yaptıklarını düşünüyorum).
MestreLion

10
Bu, sorunun cevabı değil, bunu bir sürüm kontrol sisteminde yapmanın yararları hakkında felsefi bir tartışmadır. Kişi bunu beğenmiş olsaydı, "Git'in dosyaların değiştirilme zamanı için kesinleştirme süresini kullanmamasının nedeni nedir?"
thomasfuchs

85

Bununla birlikte, teslim alırken zaman damgaları için teslim sürelerini gerçekten kullanmak istiyorsanız, bu komut dosyasını kullanmayı deneyin ve (yürütülebilir olarak) $ GIT_DIR / .git / hooks / post-checkout dosyasına yerleştirin:

#!/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

Bununla birlikte, bu komut dosyasının büyük depoları kontrol etmek için oldukça büyük bir gecikmeye neden olacağını unutmayın (burada büyük, büyük dosya boyutları değil, büyük miktarda dosya anlamına gelir).


55
Yalnızca "Bunu yapma"
demek

4
| head -n 1o, yeni bir süreç olarak çoğaltılır olarak kaçınılmalıdır -n 1için git rev-listve git logbunun yerine kullanılabilir.
eregon

3
`...`Ve ile satırları OKUMAK daha iyidir for; Neden "for" ile satırları okumuyorsunuz konusuna bakın . Ben giderdim git ls-files -zve while IFS= read -r -d ''.
musiphil

2
Windows sürümü mümkün mü?
Ehryk

2
git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "$1")" | head -n 1sizin yapabilmeniz yerine git show --pretty=format:%ai -s "$(get_file_rev "$1")", showkomut tarafından çok daha az veri üretilmesine neden olur ve ek yükü azaltmalıdır.
Scott Chamberlain

81

GÜNCELLEME : Çözümüm artık Debian / Ubuntu / Mint, Fedora, Gentoo ve muhtemelen diğer dağıtımlarda paketlenmiştir:

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, zaman damgalarını (ve izinler ve sahiplik gibi diğer meta verileri) depolamamak, büyük bir sınırlamadır git.

Linus'un zaman damgalarının, sırf "kafaları karıştırdığı make" için zararlı olduğuna dair mantığı yetersiz :

  • make clean herhangi bir sorunu düzeltmek için yeterlidir.

  • Yalnızca make, çoğunlukla C / C ++ kullanan projeler için geçerlidir . Python, Perl veya genel olarak dokümantasyon gibi komut dosyaları için tamamen tartışmalı.

  • Yalnızca zaman damgalarını uygularsanız zarar vardır . Depoda saklanmasının hiçbir sakıncası olmaz . Onları uygulamak basit olabilir --with-timestampsseçeneği git checkoutve arkadaşları ( clone, pullat vb), kullanıcının takdirine.

Hem Bazaar hem de Mercurial meta verileri depolar. Kullanıcılar ödünç verirken bunları uygulayabilir veya uygulamayabilir. Orijinal damgaları bile çünkü Ama GIT'de, mevcut repoda, böyle bir seçeneği yoktur.

Dolayısıyla, projelerin bir alt kümesine özgü çok küçük bir kazanç için (her şeyi yeniden derlemek zorunda kalmadan), gitgenel bir DVCS sakatlandıkça , dosyalar hakkında bazı bilgiler kaybolur ve Linus'un dediği gibi, yapılması INFEASIBLE şimdi. Üzücü .

Bununla birlikte, 2 yaklaşım önerebilir miyim?

1 - http://repo.or.cz/w/metastore.git , David Härdeman. git İlk etapta yapılması gerekeni yapmaya çalışır : Metadata'yı (sadece zaman damgalarını değil) işlenirken depoda depolar (ön taahhüt kancası aracılığıyla) ve çekerken (ayrıca kancalar aracılığıyla) yeniden uygular.

2 - Daha önce sürüm tarball'ları oluşturmak için kullandığım bir betiğin mütevazı versiyonu. Diğer yanıtlar belirtildiği gibi, yaklaşımdır biraz farklı her dosya için başvuruda: zaman damgası arasında En son taahhüt dosyası değiştirilmiş olabilir.

  • birçok seçeneğe sahip git-restore-mtime , herhangi bir depo düzenini destekler ve Python 3 üzerinde çalışır.

Aşağıda, Python 2.7'de bir kavram kanıtı olarak betiğin gerçekten basit bir versiyonu bulunmaktadır. Gerçek kullanım için yukarıdaki tam sürümü şiddetle tavsiye ederim:

#!/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

Performans bile canavar projeleri için, oldukça etkileyici wine, githatta Linux çekirdeği:

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
Ama git yaptığı mağaza vb damgalarını, sadece varsayılan olarak damgaları belirlemez. Sadece çıkışına bakıngit ls-files --debug
Ross Smith II

9
@RossSmithII: git ls-filesçalışma dizini ve indeks üzerinde çalışır, bu yüzden bu bilgiyi depoda depoladığı anlamına gelmez . Eğer saklamış olsaydı, mtime'ı almak (ve uygulamak) önemsiz olurdu.
MestreLion

14
"Linus'un zaman damgalarının zararlı olmasının mantığı sırf" kafaları karıştırdığı için "yetersizdir" -% 100 kabul etti, bir DCVS içerdiği kodu bilmemeli veya önemsememelidir! Yine bu, belirli kullanım durumları için yazılmış araçları genel kullanım senaryolarına dönüştürmeye çalışmanın tehlikelerini göstermektedir. Mercurial, evrim geçirmediği için tasarlandığı için her zaman üstün bir seçim olacaktır.
Ian Kemp

6
@davec Rica ederim, yararlı olduğuna sevindim. Github.com/MestreLion/git-tools adresindeki tam sürüm, Windows, Python 3, ASCII olmayan yol adlarını , vb. Halihazırda işliyor . Yukarıdaki komut dosyası yalnızca çalışan bir kavram kanıtıdır, üretimde kullanım için bundan kaçının.
MestreLion

3
Argümanlarınız geçerli. Umarım biraz nüfuzu olan biri git için sizin önerdiğiniz --with-timestamps seçeneğine sahip olmak için bir geliştirme talebinde bulunur.
weberjn

12

Giel'in cevabını aldım ve bir teslim sonrası kanca komut dosyası kullanmak yerine, özel dağıtım betiğimde çalıştırdım.

Güncelleme : Ayrıca | head -n@ eregon'un önerisini takiben birini kaldırdım ve içinde boşluklar olan dosyalar için destek ekledim:

# 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

Teşekkürler Daniel, bunu bilmek faydalı oluyor
Alex Dean

--abbrev-commitgereksiz olduğu git shownedeniyle komutu --pretty=format:%ai(karma çıkış parçası olmayan tamamlama) kullanılan ve | head -n 1kullanma ile değiştirilebilir -sbayrağıgit show
Elan Ruusamäe

1
@ DanielS.Sterling: sıkı iso8601 kullanımı için %aiyazar tarihi, ISO 8601 benzeri biçimdir : git-scm.com/docs/git-show%aI
Elan Ruusamäe

4

Başka bir çözüm bulmaya zorlandık, çünkü özel olarak değişiklik zamanlarına ihtiyacımız vardı ve zamanları işlememeliydik ve çözüm de taşınabilir olmalıydı (yani python'un windows'un git kurulumlarında çalışmasını sağlamak gerçekten basit bir iş değil) ve hızlıydı. Dokümantasyon eksikliği nedeniyle kullanmamaya karar verdiğim David Hardeman'ın çözümüne benziyor (depodan kodunun tam olarak ne işe yaradığına dair bir fikrimi alamadım).

Bu çözüm mtimes'ları git deposundaki bir .mtimes dosyasında saklar, bunları işlemlerde uygun şekilde günceller (seçimli olarak hazırlanmış dosyaların mtimes'larını jsut) ve bunları kullanıma alma sırasında uygular. Git'in cygwin / mingw sürümleriyle bile çalışır (ancak bazı dosyaları standart cygwin'den git klasörüne kopyalamanız gerekebilir)

Çözüm 3 dosyadan oluşur:

  1. mtimestore - 3 seçenek sağlayan çekirdek komut dosyası -a (tümünü kaydet - mevcut depoda başlatma için (git-versed dosyalarla çalışır)), -s (aşamalı değişiklikleri kaydetmek için) ve bunları geri yüklemek için -r. Bu aslında 2 versiyonda geliyor - bir bash (taşınabilir, güzel, okuması / değiştirmesi kolay) ve c versiyonu (dağınık ama hızlı, çünkü mingw bash korkunç derecede yavaş ve bu da büyük projelerde bash çözümünü kullanmayı imkansız kılıyor).
  2. ön işleme kancası
  3. ödeme sonrası kanca

ön taahhüt:

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

ödeme sonrası

#!/bin/bash
mtimestore -r

mtimestore - bash:

#!/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 - c ++

#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;
}
  • kancaların yerleşimlerini otomatikleştirmek için şablon dizinine yerleştirilebileceğini unutmayın

daha fazla bilgi burada bulunabilir https://github.com/kareltucek/git-mtime-extension Bazı güncel olmayan bilgiler http://www.ktweb.cz/blog/index.php?page=page&id=116 adresindedir.

// düzenle - c ++ sürümü güncellendi:

  • Artık c ++ sürümü alfabetik sıralamayı koruyor -> daha az birleştirme çakışması.
  • Çirkin sistem () çağrılarından kurtuldum.
  • Ödeme sonrası kancasından $ git update-index --refresh $ silindi. Kaplumbağa altında geri dönüş ile bazı sorunlara neden olur ve yine de çok önemli görünmüyor.
  • Windows paketimiz http://ktweb.cz/blog/download/git-mtimestore-1.4.rar adresinden indirilebilir.

// güncel sürüm için github'a bakın


1
Bir teslim alma işleminden sonra, güncel dosyaların zaman damgalarının artık değiştirilmediğini unutmayın (Git 2.2.2+, Ocak 2015): stackoverflow.com/a/28256177/6309
VonC

3

Aşağıdaki komut dosyası -n 1ve HEADönerileri içerir, Linux olmayan ortamların çoğunda (Cygwin gibi) çalışır ve gerçeğin ardından bir kontrol işleminde çalıştırılabilir:

#!/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

Yukarıdaki betiği adlandırdığınızı varsayarsak /path/to/templates/hooks/post-checkoutve / veya /path/to/templates/hooks/post-updatebunu mevcut bir havuzda şu yolla çalıştırabilirsiniz:

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

Son bir satıra daha ihtiyacı var: git update-index --refresh // GUI araçları dizine güvenebilir ve böyle bir işlemden sonra tüm dosyaya "kirli" durumunu gösterebilir. Yani bu, Windows için TortoiseGit'te olur code.google.com/p/tortoisegit/issues/detail?id=861
Arioch '

1
Senaryo için de teşekkürler. Keşke böyle bir komut dosyası Git standart yükleyicisinin bir parçası olsaydı. Kişisel olarak ihtiyacım olduğundan değil, ancak ekip üyeleri zaman damgasının VCS'nin benimsenmesinde kırmızı bir "durdurma" bayrağı olarak yeniden düzenlendiğini hissediyorlar.
Arioch 'The

3

Bu çözüm oldukça hızlı çalışmalıdır. Zamanları kaydetme zamanlarına ve zamanları yazar zamanlarına ayarlar. Modül kullanmaz, bu nedenle makul şekilde taşınabilir olmalıdır.

#!/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

İşte yukarıdaki kabuk çözümlerinin küçük düzeltmelerle optimize edilmiş bir sürümü:

#!/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

İşte PHP ile bir yöntem:

<?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);
}

Buradaki cevaba benzer:

Git için kullanım-kaydetme sürelerinin eşdeğeri nedir?

bu yanıt gibi bir dosya listesi oluşturur, ancak git ls-files yalnızca çalışma dizinine bakmak yerine bundan oluşur . Bu, dışlama sorununu çözer .gitve aynı zamanda izlenmeyen dosyalar sorununu da çözer. Ayrıca, bir dosyanın son işlemesi çözdüğüm bir birleştirme işlemiyse bu yanıt başarısız olur git log -m. Diğer cevap gibi, tüm dosyalar bulunduğunda duracaktır, bu nedenle tüm kayıtları okuması gerekmez. Örneğin:

https://github.com/git/git

bu gönderi itibariyle sadece 292 kayıt okumak zorunda kaldı. Ayrıca geçmişteki eski dosyaları gerektiği gibi yok sayar ve zaten dokunulmuş bir dosyaya dokunmaz. Nihayet diğer çözümden biraz daha hızlı görünüyor. git/gitRepo ile sonuçlar :

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

0

Bir Windows sürümü için bazı istekler gördüm, işte burada. Aşağıdaki iki dosyayı oluşturun:

C: \ Program Files \ Git \ mingw64 \ share \ git-core \ templates \ hooks \ post-checkout

#!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 \ templates \ hooks \ post-checkout.ps1

[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;
    }
}

Bu , git whatchanged kullanır , böylece her dosya için git'i çağırmak yerine tüm dosyaları tek geçişte çalıştırır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.