git 的 use-commit-times 相当于什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1964470/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the equivalent of use-commit-times for git?
提问by Ben W
I need the timestamps of files on my local and on my server to be in sync. This is accomplished with Subversion by setting use-commit-times=true in the config so that the last modified of each file is when it was committed.
我需要本地和服务器上文件的时间戳同步。这是通过 Subversion 通过在配置中设置 use-commit-times=true 来实现的,以便每个文件的最后修改时间是提交时。
Each time I clone my repository, I want the timestamps of files to reflect when they were last changed in the remote repository, not when I cloned the repo.
每次克隆我的存储库时,我都希望文件的时间戳反映它们上次在远程存储库中的更改时间,而不是我克隆存储库的时间。
Is there any way to do this with git?
有没有办法用 git 做到这一点?
采纳答案by VonC
I am not sure this would be appropriate for a DVCS (as in "Distributed" VCS)
我不确定这是否适合 DVCS(如“分布式”VCS)
The huge discussion had already took place in 2007 (see this thread)
And some of Linus's answer were not too keen on the idea. Here is one sample:
并且 Linus 的一些回答对这个想法不太感兴趣。这是一个示例:
I'm sorry. If you don't see how it's WRONG to seta datestamp back to something that will make a simple "make" miscompileyour source tree, I don't know what defintiion of "wrong" you are talking about.
It's WRONG.
It's STUPID.
And it's totally INFEASIBLE to implement.
抱歉。如果你看不出它是错误的刚毛邮戳回东西,这将使一个简单的“化妆” miscompile源代码树,我不知道的“错”你在说什么defintiion。
这是错的。
这很傻。
并且它是完全不可行的。
(Note: small improvement: after a checkout, timestamps of up-to-date files are no longer modified(Git 2.2.2+, January 2015): "git checkout - how can I maintain timestamps when switching branches?".)
(注意:小改进:结帐后,不再修改最新文件的时间戳(Git 2.2.2+,2015 年 1 月):“git checkout - 如何在切换分支时维护时间戳?”。)
The long answer was:
长答案是:
I think you're much better off just using multiple repositories instead, if this is something common.
Messing with timestamps is not going to work in general. It's just going to guarantee you that "make" gets confused in a really bad way, and does not recompile enoughinstead of recompiling too much.
Git does make it possible to do your "check the other branch out" thing very easily, in many different ways.
You could create some trivial script that does any of the following (ranging from the trivial to the more exotic):
just create a new repo:
git clone old new cd new git checkout origin/<branch>
and there you are. The old timestamps are fine in your old repo, and you can work (and compile) in the new one, without affectign the old one at all.
Use the flags "-n -l -s" to "git clone" to basically make this instantaneous. For lots of files (eg big repos like the kernel), it's not going to be as fast as just switching branches, but havign a second copy of the working tree can be quite powerful.
do the same thing with just a tar-ball instead, if you want to
git archive --format=tar --prefix=new-tree/ <branchname> | (cd .. ; tar xvf -)
which is really quite fast, if you just want a snapshot.
get used to "
git show
", and just look at individual files.
This is actually reallyuseful at times. You just dogit show otherbranch:filename
in one xterm window, and look at the same file in your current branch in another window. In particular, this should be trivial to do with scriptable editors (ie GNU emacs), where it should be possible to basically have a whole "dired mode" for other branches within the editor, using this. For all I know, the emacs git mode already offers something like this (I'm not an emacs user)
and in the extreme example of that "virtual directory" thing, there was at least somebody working on a git plugin for FUSE, ie you could literally just have virtual directories showing allyour branches.
and I'm sure any of the above are better alternatives than playing games with file timestamps.
Linus
我认为你最好只使用多个存储库,如果这是常见的。
弄乱时间戳一般是行不通的。它只会向您保证“make”会以一种非常糟糕的方式混淆,并且不会重新编译足够多,而不是重新编译太多。
Git 确实让您可以通过许多不同的方式非常轻松地完成“检查另一个分支”的事情。
您可以创建一些简单的脚本来执行以下任何操作(从简单到更奇特):
只需创建一个新的回购:
git clone old new cd new git checkout origin/<branch>
你就在那里。旧的时间戳在您的旧仓库中很好,您可以在新仓库中工作(和编译),而完全不会影响旧仓库。
使用标志 "-n -l -s" 到 "git clone" 基本上可以做到这一点。对于很多文件(例如像内核这样的大存储库),它不会像仅仅切换分支那么快,但是拥有工作树的第二个副本可能非常强大。
如果你想,只用焦油球做同样的事情
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 插件,也就是说,您实际上可以只拥有显示所有分支的虚拟目录。
而且我确信以上任何一种都是比玩带有文件时间戳的游戏更好的选择。
莱纳斯
回答by Giel
If, however you reallywant to use commit times for timestamps when checking out then try using this script and place it (as executable) in the file $GIT_DIR/.git/hooks/post-checkout:
但是,如果您真的想在签出时使用提交时间作为时间戳,请尝试使用此脚本并将其(作为可执行文件)放在文件 $GIT_DIR/.git/hooks/post-checkout 中:
#!/bin/sh -e
OS=${OS:-`uname`}
old_rev=""
new_rev=""
get_file_rev() {
git rev-list -n 1 "$new_rev" ""
}
if [ "$OS" = 'Linux' ]
then
update_file_timestamp() {
file_time=`git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "")" | head -n 1`
touch -d "$file_time" ""
}
elif [ "$OS" = 'FreeBSD' ]
then
update_file_timestamp() {
file_time=`date -r "$(git show --pretty=format:%at --abbrev-commit "$(get_file_rev "")" | head -n 1)" '+%Y%m%d%H%M.%S'`
touch -h -t "$file_time" ""
}
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
Note however, that this script will cause quite a large delay for checking out large repositories (where large means large amount of files, not large file sizes).
但是请注意,此脚本会导致检出大型存储库的延迟相当大(其中大意味着大量文件,而不是大文件)。
回答by MestreLion
UPDATE: My solution is now packaged into Debian/Ubuntu/Mint, Fedora, Gentoo and possibly other distros:
更新:我的解决方案现在打包到 Debian/Ubuntu/Mint、Fedora、Gentoo 和其他可能的发行版中:
https://github.com/MestreLion/git-tools#install
https://github.com/MestreLion/git-tools#install
IMHO, not storing timestamps (and other metadata like permissions and ownership) is a biglimitation of git
.
IMHO,不存储时间戳(和其他元数据等的权限和所有权)是一个大的限制git
。
Linus' rationale of timestamps being harmful just because it "confuses make
" is lame:
Linus 认为时间戳是有害的仅仅因为它“混淆make
”的理由是蹩脚的:
make clean
is enough to fix any problems.Applies only to projects that use
make
, mostly C/C++. It is completely moot for scripts like Python, Perl, or documentation in general.There is only harm if you applythe timestamps. There would be no harm in storingthem in repo. Applying them could be a simple
--with-timestamps
option forgit checkout
and friends (clone
,pull
etc), at the user'sdiscretion.
make clean
足以解决任何问题。仅适用于使用
make
,主要是 C/C++ 的项目。它对于 Python、Perl 或一般文档等脚本完全没有实际意义。如果您应用时间戳,只会造成伤害。将它们存储在 repo 中没有任何害处。运用他们可以是一个简单
--with-timestamps
的选择git checkout
和朋友(clone
,pull
等等),在用户的自由裁量权。
Both Bazaar and Mercurial stores metadata. Users can apply them or not when checking out. But in git, since original timestamps are not even availablein the repo, there is no such option.
Bazaar 和 Mercurial 都存储元数据。用户可以在结帐时应用或不应用它们。但是在 git 中,由于原始时间戳在 repo 中甚至不可用,因此没有这样的选项。
So, for a very small gain (not having to re-compile everything) that is specific to a subset of projects, git
as a general DVCS was crippled, some information from about files is lost, and, as Linus said, it's INFEASIBLE to do it now. Sad.
因此,对于特定于项目子集的非常小的收益(不必重新编译所有内容),git
由于一般的 DVCS 已瘫痪,有关文件的一些信息丢失了,而且正如 Linus 所说,这是不可能的现在。伤心。
That said, may I offer 2 approaches?
也就是说,我可以提供两种方法吗?
1 - http://repo.or.cz/w/metastore.git, by David H?rdeman. Tries to do what git
should have done in the first place: stores metadata (not only timestamps)in the repo when commiting (via pre-commit hook), and re-applies them when pulling (also via hooks).
1 - http://repo.or.cz/w/metastore.git,David H?rdeman。尝试做一开始git
应该做的事情:在提交时(通过预提交钩子)将元数据(不仅是时间戳)存储在 repo 中,并在拉取时(也通过钩子)重新应用它们。
2 - My humble version of a script I used before for generating release tarballs. As mentioned in other answers, the approach is a little different: to apply for each file the timestampof the most recent commitwhere the file was modified.
2 - 我以前用于生成发布 tarball 的脚本的简陋版本。正如在其他的答案中提到,这种方法是有一点不同:申请每个文件的时间戳中的最近一次提交该文件已被修改。
- Core functionality, with --help, debug messages. Can be run anywhere within the work tree
- Full-fledged beast, with lots of options. Supports any repository layout.
Below is a reallybare-bones version of the script. For actual usage I strongly suggest one of the more robust versions above:
下面是脚本的一个非常简单的版本。对于实际使用,我强烈建议使用上面更强大的版本之一:
#!/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
Performance is pretty impressive, even for monster projects wine
, git
or even the linux kernel:
性能令人印象深刻,即使对于怪物项目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
回答by Alex Dean
I took Giel's answer and instead of using a post-commit hook script, worked it into my custom deployment script.
我接受了 Giel 的回答,而不是使用提交后挂钩脚本,而是将其处理到我的自定义部署脚本中。
Update: I've also removed one | head -n
following @eregon's suggestion, and added support for files with spaces in them:
更新:我还| head -n
根据@eregon 的建议删除了一个,并添加了对其中包含空格的文件的支持:
# Adapted to use HEAD rather than the new commit ref
get_file_rev() {
git rev-list -n 1 HEAD ""
}
# Same as Giel's answer above
update_file_timestamp() {
file_time=`git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "")" | head -n 1`
sudo touch -d "$file_time" ""
}
# 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
回答by Karel Tucek
we were forced to invent yet another solution, because we needed specifically modification times and not commit times, and the solution also had to be portable (i.e. getting python working in windows's git installations really is not a simple task) and fast. It resembles the David Hardeman's solution, which I decided not to use because of lack of documentation (from repository I was not able to get idea what exactly his code does).
我们被迫发明了另一种解决方案,因为我们需要专门的修改时间而不是提交时间,而且该解决方案还必须是可移植的(即让 python 在 Windows 的 git 安装中工作确实不是一项简单的任务)并且速度快。它类似于 David Hardeman 的解决方案,由于缺乏文档,我决定不使用该解决方案(从存储库中我无法了解他的代码究竟做了什么)。
This solution stores mtimes in a file .mtimes in git repository, updates them accordingly on commits (jsut selectively the mtimes of staged files) and applies them on checkout. It works even with cygwin/mingw versions of git (but you may need to copy some files from standard cygwin into git's folder)
该解决方案将 mtimes 存储在 git 存储库中的 .mtimes 文件中,在提交时相应地更新它们(只是有选择地对暂存文件的 mtimes 进行更新)并在结帐时应用它们。它甚至适用于 cygwin/mingw 版本的 git(但您可能需要将一些文件从标准 cygwin 复制到 git 的文件夹中)
The solution consists of 3 files:
该解决方案由 3 个文件组成:
- mtimestore - core script providing 3 option -a (save all - for initialization in already existing repo (works with git-versed files)), -s (to save staged changes), and -r to restore them. This actually comes in 2 versions - a bash one (portable, nice, easy to read/modify), and c version (messy one but fast, because mingw bash is horribly slow which makes impossible to use the bash solution on big projects).
- pre-commit hook
- post-checkout hook
- mtimestore - 核心脚本提供 3 个选项 -a(全部保存 - 用于在现有存储库中进行初始化(适用于 git-versed 文件))、-s(保存暂存更改)和 -r 以恢复它们。这实际上有 2 个版本 - 一个 bash 版本(便携、漂亮、易于阅读/修改)和 c 版本(一个凌乱但很快,因为 mingw bash 非常慢,这使得无法在大型项目中使用 bash 解决方案)。
- 预提交钩子
- 结帐后挂钩
pre-commit:
预提交:
#!/bin/bash
mtimestore -s
git add .mtimes
post-checkout
结帐后
#!/bin/bash
mtimestore -r
mtimestore - bash:
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 "")|" >> .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++
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 != '#!/bin/bash -e
OS=${OS:-`uname`}
get_file_rev() {
git rev-list -n 1 HEAD ""
}
if [ "$OS" = 'FreeBSD' ]
then
update_file_timestamp() {
file_time=`date -r "$(git show --pretty=format:%at --abbrev-commit "$(get_file_rev "")" | head -n 1)" '+%Y%m%d%H%M.%S'`
touch -h -t "$file_time" ""
}
else
update_file_timestamp() {
file_time=`git show --pretty=format:%ai --abbrev-commit "$(get_file_rev "")" | head -n 1`
touch -d "$file_time" ""
}
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
')
{
if(*ptr == '\n' || *ptr == '\r')
*ptr = 'git clone git://path/to/repository.git
cd repository
/path/to/templates/hooks/post-checkout
';
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()) != '#!/bin/sh
if [ "$(uname)" = 'Darwin' ] ||
[ "$(uname)" = 'FreeBSD' ]; then
gittouch() {
touch -ch -t "$(date -r "$(git log -1 --format=%ct "")" '+%Y%m%d%H%M.%S')" ""
}
else
gittouch() {
touch -ch -d "$(git log -1 --format=%ci "")" ""
}
fi
git ls-files |
while IFS= read -r file; do
gittouch "$file"
done
')
{
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;
}
- note that hooks can be placed into template-directory to automatize their placement
- 请注意,钩子可以放置到模板目录中以自动放置
more info may be found here https://github.com/kareltucek/git-mtime-extensionsome out of date information are at http://www.ktweb.cz/blog/index.php?page=page&id=116
更多信息可以在这里找到 https://github.com/kareltucek/git-mtime-extension一些过时的信息在 http://www.ktweb.cz/blog/index.php?page=page&id=116
//edit - c++ version updated:
//编辑 - C++ 版本更新:
- Now the c++ version maintains alphabetical ordering -> less merge conflicts.
- Got rid of the ugly system() calls.
- Deleted $git update-index --refresh$ from post-checkout hook. Causes some problems with revert under tortoise git, and does not seem to be much important anyway.
- Our windows package can be downloaded at http://ktweb.cz/blog/download/git-mtimestore-1.4.rar
- 现在 C++ 版本保持字母顺序 -> 减少合并冲突。
- 摆脱了丑陋的 system() 调用。
- 从结帐后挂钩中删除了 $git update-index --refresh$。在 tortoise git 下 revert 会导致一些问题,无论如何似乎都不是很重要。
- 我们的windows包可以在http://ktweb.cz/blog/download/git-mtimestore-1.4.rar下载
//edit see github for up-to-date version
//编辑见github以获取最新版本
回答by Ross Smith II
The following script incorporates the -n 1
and HEAD
suggestions, works in most non-Linux environments (like Cygwin), and can be run on a checkout after the fact:
以下脚本包含了-n 1
和HEAD
建议,适用于大多数非 Linux 环境(如 Cygwin),并且可以在事后结帐时运行:
#!/usr/bin/perl
# git-utimes: update file times to last commit on them
# Tom Christiansen <[email protected]>
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 "#!C:/Program\ Files/Git/usr/bin/sh.exe
exec powershell.exe -NoProfile -ExecutionPolicy Bypass -File "./[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;
}
}
.ps1"
: Cannot open pipe from `@gitlog`: $!\n";
our $Oops = 0;
our %Seen;
$/ = "";
while (<GITLOG>) {
next if /^"Merge branch/;
s/^"(.*)" // || die;
my $msg = ;
s/^(\d+) (\d+)\n//gm || die;
my @times = (, ); # 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 "#!/usr/bin/env php
<?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);
}
: Couldn't reset utimes on $file: $!\n";
$Oops++;
}
}
}
exit $Oops;
Assuming you named the above script /path/to/templates/hooks/post-checkout
and/or /path/to/templates/hooks/post-update
, you can run it on an existing repository via:
假设您命名了上述脚本/path/to/templates/hooks/post-checkout
和/或/path/to/templates/hooks/post-update
,您可以通过以下方式在现有存储库上运行它:
回答by vszakats
Here is an optimized version of the above shell solutions, with minor fixes:
这是上述 shell 解决方案的优化版本,有一些小的修复:
##代码##回答by tchrist
This solution should run pretty quickly. It sets atimes to committer times and mtimes to author times. It uses no modules so should be reasonably portable.
此解决方案应该运行得非常快。它将 atimes 设置为提交者时间,将 mtimes 设置为作者时间。它不使用任何模块,因此应该具有合理的便携性。
##代码##回答by Brain2000
I saw some requests for a Windows version, so here it is. Create the following two files:
我看到了一些对 Windows 版本的要求,所以就在这里。创建以下两个文件:
C:\Program Files\Git\mingw64\share\git-core\templates\hooks\post-checkout
C:\Program Files\Git\mingw64\share\git-core\templates\hooks\post-checkout
##代码##C:\Program Files\Git\mingw64\share\git-core\templates\hooks\post-checkout.ps1
C:\Program Files\Git\mingw64\share\git-core\templates\hooks\post-checkout.ps1
##代码##This utilizes git whatchanged, so it runs through all the files in one pass instead of calling git for each file.
这利用了git whatchanged,因此它一次遍历所有文件,而不是为每个文件调用 git。
回答by Andrew Mackrodt
I'm working on a project where a keep a clone of my repository for use with rsync
based deployments. I use branches to target different environments and git checkout
causes the file modifications to change.
我正在开发一个项目,其中保留我的存储库的克隆以用于rsync
基于部署的。我使用分支来定位不同的环境并git checkout
导致文件修改发生变化。
Having learnt that git does not provide a way to checkout files and preserve timestamps I came across the command git log --format=format:%ai --name-only .
in another SO question: List last commit dates for a large number of files, quickly.
了解到 git 不提供检出文件和保留时间戳的方法后,我git log --format=format:%ai --name-only .
在另一个 SO 问题中遇到了该命令:快速列出大量文件的最后提交日期。
I'm now using the following script to touch
my project files and directories so that my deployment with rsync
is easier to diff:
我现在对touch
我的项目文件和目录使用以下脚本,以便我的部署rsync
更容易区分: