104 votes

Ce qui est l'équivalent de l'utilisation de validation-temps pour git?

J'ai besoin de l'horodatage des fichiers sur mon local et sur mon serveur, pour être en phase. Ceci est accompli avec la Subversion par le réglage de l'utilisation de validation-temps=true dans le fichier de configuration de sorte que la dernière modification de chaque fichier est où elle a été commise.

Chaque fois que je clone mon dépôt, je veux de l'horodatage des fichiers à réfléchir quand ils ont été modifié dans le dépôt distant, pas quand j'ai cloné le repo.

Est-il possible de le faire avec git?

95voto

MestreLion Points 1789

À mon humble avis, ne pas stocker les dates (et les autres métadonnées comme les autorisations et la propriété) est une grande limitation de l' git.

Linus " justification de l'horodatage être nuisibles, juste parce qu'il "confond make" est boiteux:

  • make clean est suffisante pour résoudre tous les problèmes.

  • S'applique uniquement aux projets qui utilisent des make, principalement en C/C++. Il est totalement discutable pour les scripts comme Python, Perl, ou de la documentation en général.

  • Il est seulement dommage si vous appliquez les horodateurs. Il n'y aurait pas de mal à ranger dans des pensions. Leur application pourrait être un simple --with-timestamps option pour git checkout et des amis (clone, pull etc), à l' utilisateur de discrétion.

Les deux Bazar et Mercurial stocke les métadonnées. Les utilisateurs peuvent appliquer ou non lors de la vérification. Mais dans git, puisque les horodateurs ne sont pas encore disponibles dans le dépôt, il n'y a pas une telle option.

Ainsi, pour un très faible gain (ne pas avoir à re-compiler tout) qui est spécifique à un sous-ensemble de projets, git générale DVCS a été paralysé, quelques renseignements sur les fichiers sont perdus, et, comme Linus dit, il est IMPOSSIBLE de le faire maintenant. Triste.

Cela dit, je vous propose 2 approches?

1 - http://repo.or.cz/w/metastore.git par David Härdeman. Essaie de faire ce git devrait faire en premier lieu: stocke les métadonnées (pas seulement les horodatages) dans le repo lors de la validation (via pre-commit hook), et ré-applique lors de l'extraction (également via les hooks).

2 - Mon humble version d'un script que j'ai utilisé avant pour générer des versions archivées. Comme mentionné dans d'autres réponses, l'approche est un peu différente: pour chaque fichier de l' horodatage de la plus récente s'engager où le fichier a été modifié.

Ci-dessous est un vraiment bare-bones version du script. Pour l'utilisation réelle, je vous suggère fortement un des plus robuste versions ci-dessus:

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

La Performance est assez impressionnant, même pour monster projets wine, git ou même le noyau linux:

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

93voto

Giel Points 1290

Si, toutefois, vous vraiment voulez utiliser commettre fois pour l'horodatage lors de la vérification, puis essayez d'utiliser ce script et de le placer (comme exécutable) dans le fichier $GIT_DIR/.git/hooks/post-paiement:

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

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

Cependant, notez que ce script va créer un grand retard pour le contrôle de grands dépôts (où grand grande quantité de fichiers, pas de grandes tailles de fichier).

28voto

VonC Points 414372

Je ne suis pas sûr que ce serait approprié pour un DVCS (comme dans "Distribué" VCS)

La grande discussion avait déjà eu lieu en 2007 (voir ce fil)

Et certains de Linus de réponse n'étaient pas trop vif sur l'idée. Voici un exemple:

Je suis désolé. Si vous ne voyez pas comment il est FAUX de seta datestamp retour à quelque chose qui va faire une simple "faire" miscompile votre arborescence des sources, je ne sais pas ce defintiion de "mal" dont vous parlez.
C'est FAUX.
Il est STUPIDE.
Et c'est totalement IMPOSSIBLE à mettre en œuvre.


La réponse a été:

Je pense que vous êtes beaucoup mieux l'utilisation de plusieurs référentiels au lieu de cela, si c'est quelque chose de commun.

Jouer avec les horodateurs ne va pas au travail en général. Il va juste pour vous garantir que "faire" se confond dans une très mauvaise manière, et de ne pas recompiler assez à la place de la recompilation de trop.

Git, il est possible de faire votre "case à l'autre branche de" chose très facilement, dans de nombreuses façons différentes.

Vous pouvez créer certains trivial script qui effectue l'un des suivants (allant de la simple à la plus exotique):

  • il suffit de créer un nouveau repo:

    git clone old new
    cd new
    git checkout origin/<branch>
    

    et vous y êtes. L'ancien horodatages sont bien dans tes vieux repo, et vous pouvez travailler (et compiler) dans le nouveau, sans affectign l'ancien.

    Utiliser les drapeaux "-n -l-s" à "git clone" pour faire de cet instantané. Pour les lots de fichiers (par exemple grand repos comme le noyau), il ne va pas être aussi rapide que juste de commutation branches, mais havign une deuxième copie de l'arbre de travail peut être assez puissant.

  • faire la même chose avec juste un tar-ball au lieu de cela, si vous voulez

    git archive --format=tar --prefix=new-tree/ <branchname> |
            (cd .. ; tar xvf -)
    

    ce qui est vraiment très rapide, si vous voulez juste un instantané.

  • s'habituer à "git show", et il suffit de regarder les fichiers individuels.
    C'est en fait vraiment utile à la fois. Vous venez de le faire

    git show otherbranch:filename
    

    dans une fenêtre xterm, et de regarder le même fichier dans votre branche dans une autre fenêtre. En particulier, cela devrait être facile à faire avec des scripts éditeurs (c'est à dire GNU emacs), où il devrait être possible de, essentiellement, ont "dired mode" pour les autres branches au sein de l'éditeur, à l'aide de ce. Pour tout ce que je sais, emacs git mode propose déjà quelque chose comme ça (je ne suis pas un utilisateur d'emacs)

  • et dans l'exemple extrême de ce "répertoire virtuel", il n'y avait au moins quelqu'un travaille sur un plugin git pour le FUSIBLE, c'est à dire que vous pouvez littéralement juste répertoires virtuels montrant toutes vos branches.

et je suis sûr que tout les ci-dessus sont de meilleures alternatives que de jouer à des jeux avec des timestamps.

Linus

13voto

Alex Dean Points 3997

J'ai pris Gil la réponse et au lieu d'utiliser un post-commit hook script, travaillé dans mon script de déploiement personnalisé.

Mise à jour: j'ai également supprimé un | head -n suivant @eregon de la suggestion, et ajouté le support pour les fichiers avec des espaces dedans:

# 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

6voto

Karel Tucek Points 26

nous avons été obligés d'inventer encore une autre solution, parce que nous avions besoin spécifiquement des moments de modification et de ne pas commettre de temps, et la solution devait également être portable (c'est à dire obtenir python de travail dans windows git du des installations n'est vraiment pas une tâche simple) et rapide. Il ressemble à David Hardeman la solution, j'ai décidé de ne pas l'utiliser en raison du manque de documentation (à partir de la logithèque, je n'étais pas en mesure de se faire une idée de ce que exactement son code).

Cette solution de magasins mtimes dans un fichier .mtimes dans le dépôt git, mises à jour en conséquence sur les commits (juste sélectivement la mtimes de la mise en scène de fichiers) et les applique à la caisse. Il fonctionne même avec cygwin/mingw versions de git (mais vous pouvez avoir besoin de copier des fichiers à partir de la norme cygwin dans le dépôt git du dossier)

La solution se compose de 3 fichiers:

  1. mtimestore - core script offrant 3 option -a (enregistrer tous - pour l'initialisation déjà existants repo (fonctionne avec git-versé fichiers)), -s (pour enregistrer la scène, en évolution), et -r pour les restaurer. Cette réalité existe en 2 versions - un bash un (portable, agréable, facile à lire/modifier), et la version c (sale mais rapide, parce que mingw bash est horriblement lent ce qui rend impossible l'utilisation du bash solution sur les gros projets).
  2. pre-commit hook
  3. post-caisse crochet

pre-commit:

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

post-caisse

#!/bin/bash
mtimestore -r
git update-index --refresh

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 "touch -c -d \"$(stat -c %y "$1")\" \"$1\"" >> .mtimes
}

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=""
        for str in $( git diff --name-only --staged )
        do
          if [ "$pattern" != "" ]
          then
            pattern="$pattern|"
          else
            pattern="$pattern|$str" 
          fi
        done
        cat .mtimes_tmp | grep -vhF "($pattern)" >> .mtimes
      else
        echo "warning: file .mtimes does not exist - creating new"
        echo "#!/bin/bash" >> .mtimes
      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
      echo "#!/bin/bash" >> .mtimes
      for str in $(git ls-files)
      do
        echodate "$str"
      done
      ;;
    "r")
      echo "restorim dates from .mtimes"
      if [ -f .mtimes ]
      then
        bash .mtimes
      else
        echo "warning: .mtimes not found"
      fi
      ;;
    ":")
      usage
      ;;
    *)
      usage
      ;;
  esac
done
exit 0

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>

std::ofstream outputfile;

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(...)
    {}
}

void parsenum(int& num, char*& ptr)
{
    num = 0;
    while(isdigit(*ptr))
    {
        num = num*10 + (int)(*ptr) - 48;
        ptr++;
    }
}

//rozdělí line na číselnou část, a textovou část - číselnou vrátí do time, a ptr nastaví na pozici v line, kde začíná filename
void parseline(const char* line, int& time, char*& ptr)
{
    time = 0;
    ptr = (char*)line;
    parsenum(time, ptr);
    ptr++;
}


//získej datum a zapiš ho do souboru
void echodate(const char* filename)
{
    try
    {
        struct stat foo;
        int err = stat(filename, &foo);
        outputfile << foo.st_mtime << "|" << filename << std::endl;
    }
    catch(...)
    {}
}

//nahraď \n na konci řádků (jinak je interpretován součástí názvu)
void trim(char* string)
{
    char* ptr = string;
    while(*ptr != '\0')
    {
        if(*ptr == '\n')
            *ptr = '\0';
        ptr++;
    }
}


void help()
{
    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 << "  -g  <file list> generates records of files in list (i.e. \"mtimestore -g */*.cpp\") - this does NOT filter already existing records" << std::endl;
    std::cout << "  -h  show this help" << std::endl;
}

int main(int argc, char *argv[])
{
    //parsuj options podle unixového standardu
    if(argc >= 2 && argv[1][0] == '-')
    {
        switch(argv[1][1])
        {
        case 'r':
        {
            std::cout << "restoring real 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 'g':
            std::cout << "saving modification times" << std::endl;
            outputfile.open(".mtimes", std::ios::out | std::ios::app);
            for(int i = 2; i < argc; i++)
                echodate(argv[i]);
            outputfile.close();
            break;
        case 'a':
            system("rm .mtimes");
        case 's':
        {
            std::cout << "saving modification times" << std::endl;
            char path[2048];
            if(argv[1][1] == 's') //projed aktualni .mtimes grepem na vyhazeni zaznamu, ktere chceme updatovat
            {
                std::string pattern = "";
                FILE *fp = popen("git diff --name-only --staged -z", "r"); //otevira output prikazu pusteneho c prostredi bashe
                bool first = true;
                while(fgets(path, 1024, fp) != NULL)
                {
                    trim(path);
                    if(!first)
                        pattern.append("\\|");
                    pattern.append("|");
                    pattern.append(path);
                    first = false;
                }
                if(!first)
                {
                    system("mv .mtimes .mtimes_tmp");
                    std::string command = "cat .mtimes_tmp | grep -vh \"\\(";
                    command.append(pattern);
                    command.append("\\)\" >> .mtimes");
                    system(command.c_str());
                    system("rm .mtimes_tmp");
                }
            }

            outputfile.open(".mtimes", std::ios::out | std::ios::app);

            FILE *fp;
            if(argv[1][1] == 'a')
                fp = popen("git ls-files -z", "r");
            else
                fp = popen("git diff --name-only --staged -z", "r");
            while(fgets(path, 2048, fp) != NULL)
            {
                trim(path);
                echodate(path);
            }
            outputfile.close();

        }
            break;
        default:
            help();
            return 0;
        }
    }
    else
    {
        help();
        return 0;
    }

    return 0;
}
  • notez que les crochets peuvent être placés dans le modèle de répertoire, afin de permettre d'automatiser leur placement

pour les commentaires/questions/contribution/ou autre, veuillez visiter

http://84.42.186.246/blog/index.php?page=page&id=116

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X