我需要本地和服务器上的文件时间戳同步.这是通过在配置中设置use-commit-times = true来完成Subversion,以便每个文件的最后修改时间是在提交时.
每次我克隆我的存储库时,我都希望文件的时间戳能够反映最后一次更改它们在远程存储库中的时间,而不是在克隆存储库时.
有没有办法用git做到这一点?
Gie*_*iel 85
但是,如果您真的想在签出时使用时间戳的提交时间,请尝试使用此脚本并将其(作为可执行文件)放在$ GIT_DIR/.git/hooks/post-checkout文件中:
#!/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
Run Code Online (Sandbox Code Playgroud)
但是请注意,此脚本会导致检出大型存储库时出现相当大的延迟(大型存储库意味着大量文件,而不是大文件大小).
Mes*_*ion 73
更新:我的解决方案现在打包成Debian/Ubuntu/Mint,Fedora,Gentoo和其他可能的发行版:
https://github.com/MestreLion/git-tools#install
恕我直言,不存储时间戳(和其他元数据,如权限和所有权)是一个很大的限制git.
Linus的时间戳因为它"混淆make" 而有害的理由是蹩脚的:
make clean 足以解决任何问题.
仅适用于使用makeC++ C++的项目.对于Python,Perl或一般文档这样的脚本来说,这完全没有实际意义.
如果您应用时间戳,则只会造成伤害.将它们存放在回购中是没有害处的.运用他们可以是一个简单--with-timestamps的选择git checkout和朋友(clone,pull等等),在用户的自由裁量权.
Bazaar和Mercurial都存储元数据.用户可以在结账时申请或不申请.但在git的,因为原来的时间戳,甚至没有可用的回购,有没有这样的选项.
因此,对于一个项目子集特定的非常小的收益(不必重新编译所有内容),git因为一般的DVCS被削弱,来自文件的一些信息丢失了,并且,正如Linus所说,它是不可能的现在.伤心.
那就是说,我可以提供两种方法吗?
1 - http://repo.or.cz/w/metastore.git,DavidHärdeman.尝试做git 首先应该做的事情:在提交时(通过预提交挂钩)在repo中存储元数据(不仅是时间戳),并在拉(也通过挂钩)时重新应用它们.
2 - 我之前用于生成发布tarball的脚本的简陋版本.正如在其他的答案中提到,这种方法是有一点不同:申请每个文件的时间戳中的最近一次提交该文件已被修改.
下面是一个非常简单的脚本版本.对于实际使用,我强烈建议使用上面一个更强大的版本:
#!/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
Run Code Online (Sandbox Code Playgroud)
性能相当令人印象深刻,即使对于怪物项目wine,git甚至是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
Run Code Online (Sandbox Code Playgroud)
Von*_*onC 24
我不确定这适用于DVCS(如"分布式"VCS)
Linus的一些答案对这个想法并不太热衷.这是一个样本:
对不起.如果你没有看到它是如何错误的刚毛邮戳回东西,这将使一个简单的"做" miscompile源代码树,我不知道"错了"你说的是什么defintiion.
这是不对的.
这很傻.
实施它是完全不可能的.
(注意:小改进:结账后,不再修改最新文件的时间戳(Git 2.2.2 +,2015年1月):"git checkout - 如何在切换分支时保持时间戳?".)
答案很长的答案是:
如果这是常见的话,我认为你只需要使用多个存储库就可以了.
弄乱时间戳一般不会起作用.它只是要你保证,"制造"获取一个非常糟糕的方式混淆,并且不重新编译足够的重新编译,而不是太多.
Git确实能够以很多不同的方式轻松地"检查其他分支".
你可以创建一些琐碎的脚本来执行以下任何操作(从琐碎到更具异国情调):
只需创建一个新的回购:
Run Code Online (Sandbox Code Playgroud)git clone old new cd new git checkout origin/<branch>那就是你.旧的仓库中的旧时间戳很好,您可以在新的仓库中工作(并编译),而不会对旧的仓库产生影响.
使用标志"-n -l -s"来"git clone"基本上可以立即实现.对于许多文件(例如像内核这样的大型存储库),它不会像仅仅切换分支一样快,但是对工作树的第二个副本进行分配可能非常强大.
如果你愿意,只用一个tar-ball做同样的事情
Run Code Online (Sandbox Code Playgroud)git archive --format=tar --prefix=new-tree/ <branchname> | (cd .. ; tar xvf -)如果你只想要一个快照,这真的很快.
习惯于"
git show",只看个别文件.
实际上这有时非常有用.你这样做Run Code Online (Sandbox Code Playgroud)git show otherbranch:filename在一个xterm窗口中,在另一个窗口中查看当前分支中的同一文件.特别是,这对于可编写脚本的编辑器(即GNU emacs)来说应该是微不足道的,在编辑器中,应该可以基本上为编辑器中的其他分支设置一个完整的"直接模式".据我所知,emacs git模式已经提供了这样的东西(我不是emacs用户)
在"虚拟目录"的极端例子中,至少有人为FUSE工作git插件,也就是说,你可以真正拥有显示所有分支的虚拟目录.
而且我确信上述任何一种都比使用文件时间戳玩游戏更好.
莱纳斯
Ale*_*ean 13
我接受了Giel的回答,而不是使用post-commit钩子脚本,将其用于我的自定义部署脚本.
更新:我还删除了一个关于| head -n@ eregon的建议,并添加了对包含空格的文件的支持:
# 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
Run Code Online (Sandbox Code Playgroud)
我们被迫发明了另一个解决方案,因为我们需要特定的修改时间而不是提交时间,并且解决方案也必须是可移植的(即在Windows的git安装中使python工作真的不是一项简单的任务)而且速度快.它类似于David Hardeman的解决方案,我决定不使用它,因为缺少文档(从存储库我无法知道他的代码到底是什么).
此解决方案将mtimes存储在git存储库中的文件中,并在提交时相应地更新它们(有选择地jsut是阶段文件的mtimes)并在结帐时应用它们.它甚至适用于git的cygwin/mingw版本(但你可能需要将一些文件从标准cygwin复制到git的文件夹中)
该解决方案包含3个文件:
预提交:
#!/bin/bash
mtimestore -s
git add .mtimes
Run Code Online (Sandbox Code Playgroud)
后结账
#!/bin/bash
mtimestore -r
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
更多信息可以在这里找到 https://github.com/kareltucek/git-mtime-extension 一些过时的信息在 http://www.ktweb.cz/blog/index.php?page=page&id=116
//编辑 - 更新的c ++版本:
//编辑请参阅github获取最新版本
| 归档时间: |
|
| 查看次数: |
22359 次 |
| 最近记录: |