tags:

views:

125

answers:

2

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.

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?

+3  A: 

I am not sure this would be appropriate for a DVCS (as in "Distributed" 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:

I'm sorry. If you don't see how it's WRONG to seta datestamp back to something that will make a simple "make" miscompile your 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.


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 enough instead 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 really useful at times. You just do
git 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 all your branches.

and I'm sure any of the above are better alternatives than playing games with file timestamps.

Linus

VonC
Agreed. You shouldn't be confusing a DVCS with a distribution system. `git` is a DVCS, for manipulating source code that will be *built* into your final product. If you want a distribution system, you know where to find `rsync`.
Randal Schwartz
Hm, I'll have to trust his argument that it's infeasible. Whether it's wrong or stupid though is another matter. I version my files using a timestamp and upload them to a CDN, which is why it's important that the timestamps reflect when the file was actually modified, not when it was last pulled down from the repo.
Ben W
Does anyone know how else I might preserve last modified times of my files if they're in git repo?
Ben W
@Ben W: the "Linus's answer" is not here to say it is wrong in *your* particular situation. It is there only as a reminder that a DVCS is not well-suited for that kind of feature (timestamp preserving).
VonC
+1  A: 

If, however you really want 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:

#!/bin/sh -e

OS=${OS:-`uname`}
old_rev="$1"
new_rev="$2"

get_file_rev() {
    git rev-list "$new_rev" "$1" | head -n 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

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).

Giel