ローカルとサーバーのファイルのタイムスタンプを同期させる必要があります。これはSubversionのconfigでuse-commit-times = trueを設定することで実現され、各ファイルの最終変更はコミットされた日時になります。
リポジトリのクローンを作成するたびに、リポジトリのクローンを作成したときではなく、リモートリポジトリでファイルが最後に変更されたときのファイルのタイムスタンプを反映させます。
gitでこれを行う方法はありますか?
ローカルとサーバーのファイルのタイムスタンプを同期させる必要があります。これはSubversionのconfigでuse-commit-times = trueを設定することで実現され、各ファイルの最終変更はコミットされた日時になります。
リポジトリのクローンを作成するたびに、リポジトリのクローンを作成したときではなく、リモートリポジトリでファイルが最後に変更されたときのファイルのタイムスタンプを反映させます。
gitでこれを行う方法はありますか?
回答:
これがDVCSに適しているかどうかはわかりません(「分散」VCSの場合など)。
2007年にはすでに大規模な議論が行われていました(このスレッドを参照)
そして、Linusの回答の一部は、そのアイデアにあまり熱心ではありませんでした。これが1つのサンプルです。
申し訳ありません。日付スタンプをソースツリーの単純な「make」で誤ってコンパイルするようなものに戻すのがどのように間違っているのかわからない場合、私はあなたが話している「間違った」の定義を知りません。
それは間違っています。
それは愚かです。
そして、それを実装することは完全に不可能です。
(注:小さな改善:チェックアウトした後、もはや変更された最新のファイルのタイムスタンプのGit 2.2.2+、2015年1月)(:「gitのチェックアウトを-枝を切り替えたとき、私はタイムスタンプを維持できますか?」。)
長い答えは:
これが一般的である場合は、代わりに複数のリポジトリを使用する方がはるかに良いと思います。
タイムスタンプをいじることは、一般的に機能しません。「make」が本当に悪い方法で混乱し、再コンパイルしすぎずに再コンパイルしないことを保証するだけです。
Gitを使用すると、「他のブランチをチェックアウトする」ことが非常に簡単に、さまざまな方法で実行できます。
次のいずれかを実行する簡単なスクリプトを作成できます(簡単なものからよりエキゾチックなものまで)。
- 新しいリポジトリを作成するだけです:
git clone old new cd new git checkout origin/<branch>
そしてあなたはそこにいます。古いタイムスタンプは古いリポジトリでは問題なく、古いタイムスタンプにまったく影響を与えることなく、新しいタイムスタンプで作業(およびコンパイル)できます。
フラグ「-n -l -s」を「git clone」に使用して、これを基本的に瞬時にします。多くのファイル(たとえば、カーネルのような大きなリポジトリ)の場合、ブランチを切り替えるほど高速ではありませんが、作業ツリーの2番目のコピーを作成することは非常に強力です。
- 代わりに、タールボールだけで同じことを行います
git archive --format=tar --prefix=new-tree/ <branchname> | (cd .. ; tar xvf -)
スナップショットが必要な場合は、これは非常に高速です。
- "
git show
"に慣れ、個々のファイルを見てください。
これは時々本当に便利です。あなたがするだけ
git show otherbranch:filename
1つのxtermウィンドウで、別のウィンドウで現在のブランチの同じファイルを確認します。特に、これは、スクリプト可能なエディター(つまり、GNU emacs)を使用する場合は簡単です。これを使用すると、エディター内の他のブランチに対して基本的に「diredモード」全体を使用できるはずです。私が知っていることすべてのために、emacs gitモードはすでにこのようなものを提供しています(私はemacsユーザーではありません)
- そして、その「仮想ディレクトリ」の極端な例では、少なくとも誰かがFUSEのgitプラグインで作業していました。つまり、文字通りすべてのブランチを表示する仮想ディレクトリを持つことができます。
上記のいずれも、ファイルのタイムスタンプを使用してゲームをプレイするよりも優れた選択肢です。
ライナス
git
ソースコードを操作するためのDVCS です。配布システムが必要な場合は、どこにあるかがわかります。rsync
ただし、チェックアウト時に本当にタイムスタンプのコミット時間を使用したい場合は、このスクリプトを使用して(実行可能ファイルとして)ファイル$ 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
ただし、このスクリプトは、大きなリポジトリのチェックアウトにかなりの遅延を引き起こすことに注意してください(ここで、大きなとは、大きなファイルサイズではなく、大量のファイルを意味します)。
| head -n 1
それは、新しいプロセスを生成して避けるべきである-n 1
ためgit rev-list
とgit log
代わりに使用することができます。
`...`
andで行を読み取らない方がよいでしょうfor
。「for」で行を読み取らない理由をご覧ください。私はのために行くだろうgit ls-files -z
とwhile IFS= read -r -d ''
。
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
コマンドによって生成されるはるかに少ないデータを引き起こし、オーバーヘッドを減らすべきです。
更新:私のソリューションは現在、Debian / Ubuntu / Mint、Fedora、Gentoo、そしておそらく他のディストリビューションにパッケージ化されています:
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
私見、タイムスタンプ(および権限や所有権などの他のメタデータ)を保存しないことは、の大きな制限ですgit
。
Linusがタイムスタンプを「混乱させるmake
」という理由だけで有害であるという根拠は不十分です。
make clean
問題を修正するには十分です。
make
主にC / C ++ を使用するプロジェクトにのみ適用されます。これは、Python、Perl、または一般的なドキュメントなどのスクリプトには完全に無意味です。
タイムスタンプを適用する場合にのみ害があります。それらをリポジトリに保存しても害はありません。それらを適用することは、ユーザーの裁量で、および友人(、など)の--with-timestamps
ための簡単なオプションである可能性があります。git checkout
clone
pull
BazaarとMercurialはどちらもメタデータを保存します。ユーザーはチェックアウト時にそれらを適用するかどうかを指定できます。しかしgitでは、元のタイムスタンプはリポジトリでも利用できないため、そのようなオプションはありません。
したがって、プロジェクトのサブセットに固有の非常に小さな利益(すべてを再コンパイルするgit
必要はない)の場合、一般的なDVCSが機能しなくなったため、ファイルに関する情報の一部が失われ、Linusが言ったように、実行するのは不可能ですそれ。悲しい。
とはいえ、2つのアプローチを提供できますか?
1- DavidHärdemanによるhttp://repo.or.cz/w/metastore.git。git
最初に行うべきことを実行しようとします:コミットするときに(事前コミットフックを介して)メタデータ(タイムスタンプだけでなく)をリポジトリに格納し、プルするときに(フックを介して)メタデータを再適用します。
2-リリースtarballを生成するために以前使用したスクリプトの控えめなバージョン。他の回答で述べたように、アプローチがある少し異なるファイルごとに適用する:タイムスタンプの最新のコミットファイルが変更されました。
以下は、Python 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
あるいは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
git
ない店のタイムスタンプなどは、それはちょうど、デフォルトでタイムスタンプが設定されていません。の出力を見るだけgit ls-files --debug
git ls-files
作業ディレクトリとインデックスを操作するので、実際にその情報をリポジトリに保存するわけではありません。保存した場合、mtimeの取得(および適用)は簡単です。
Gielの回答を受け取り、コミット後のフックスクリプトを使用する代わりに、それをカスタムデプロイメントスクリプトに組み込みました。
更新:| head -n
@eregonの提案に従って1つ削除し、スペースを含むファイルのサポートを追加しました:
# 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
使用と置き換えることができる-s
にフラグをgit show
コミット時間ではなく具体的に変更時間を必要とするため、さらに別のソリューションを発明せざるを得ませんでした。また、ソリューションは移植可能(つまり、Windowsのgitインストールでpythonを動作させることは、実際には簡単な作業ではありません)かつ高速でなければなりませんでした。これは、David Hardemanのソリューションに似ています。これは、ドキュメントがないために使用しないことにしました(リポジトリから、彼のコードが正確に何をしているのかを理解できませんでした)。
このソリューションは、gitリポジトリ内のファイル.mtimesにmtimesを保存し、コミット時にそれらを更新し(ステージングされたファイルのmtimesを選択的にjsut)、チェックアウトに適用します。gitのcygwin / mingwバージョンでも機能します(ただし、一部のファイルを標準のcygwinからgitのフォルダーにコピーする必要がある場合があります)
ソリューションは3つのファイルで構成されています。
事前コミット:
#!/bin/bash
mtimestore -s
git add .mtimes
チェックアウト後
#!/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;
}
詳細については、 https://github.com/kareltucek/git-mtime-extension を参照してください。古い情報は http://www.ktweb.cz/blog/index.php?page=page&id=116にあります。
//編集-更新されたC ++バージョン:
//最新バージョンについてはgithubを参照してください
次のスクリプトは-n 1
およびHEAD
を組み込んでおり、ほとんどの非Linux環境(Cygwinなど)で機能し、事後のチェックアウトで実行できます。
#!/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
このソリューションはかなり迅速に実行されます。atimesをコミッター時間に、mtimesを作成者時間に設定します。それはモジュールを使用しないので、合理的に移植可能でなければなりません。
#!/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;
上記のシェルソリューションの最適化バージョンを以下に示します。
#!/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
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のuse-commit-timesに相当するものは何ですか?
それはその答えのようなファイルリストを構築しますが、それgit ls-files
は単に作業ディレクトリを調べるのではなく、そこから構築されます。これにより、を除外する問題が.git
解決され、追跡されないファイルの問題も解決されます。また、ファイルの最後のコミットがマージコミットである場合、その答えは失敗しgit log -m
ます。他の回答と同様に、すべてのファイルが見つかると停止します。そのため、すべてのコミットを読み取る必要はありません。たとえば:
この投稿の時点では、292回のコミットを読み取るだけで済みました。また、必要に応じて履歴から古いファイルを無視し、すでに変更されているファイルには変更しません。最後に、それは他のソリューションより少し速いようです。git/git
リポジトリの結果:
PS C:\git> Measure-Command { git-touch.php }
TotalSeconds : 3.4215134
Windows版のリクエストがいくつかありましたので、こちらをご覧ください。次の2つのファイルを作成します。
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;
}
}
これはgit whatchangedを利用するため、ファイルごとにgitを呼び出すのではなく、1つのパスですべてのファイルを実行します。
rsync
ベースのデプロイメントで使用するためにリポジトリのクローンを保持するプロジェクトに取り組んでいます。ブランチを使用してさまざまな環境をターゲットにしgit checkout
、ファイルの変更を変更します。
gitがファイルをチェックアウトしてタイムスタンプを保持する方法を提供していないことを知ったのでgit log --format=format:%ai --name-only .
、別のSOの質問でコマンドに出くわしました:多数のファイルの最終コミット日付をすばやくリストします。
touch
プロジェクトファイルとディレクトリに次のスクリプトを使用しているので、を使用した展開でrsync
diffが簡単になりました。
<?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);
}