Version 0 of git timestamps

Updated 2010-10-04 23:43:36 by RJM

Git timestamps

Sometimes, people want to have timestamps of files (mtime) preserved, especially when documents or binary files are subject to versioning. People may also like it when a source file collection that is not subject to make processes have original time stamps preserved through checkout of branches or versions. This is an attempt to add this feature.

Two hooks do the job, a pre-commit hook and a post-checkout hook. The mechanism has been tested to work when "standard" things are going on (it is tested with checkouts in a linear history). Since git is also a real hacker tool, use cases that could infer the timestamp processing should be considered when such special uses are going on. Example: git reset --hard.

Concept

Upon commit, a file .git_mtimes will be created. It contains the names of all files that are subject to commit (new, modified) together with their timestamps. This special file must be versioned.

Upon checkout, .git_mtimes is scanned and each file that has a timestamp close to the current time is treated as updated, so this is safe with branch checkouts as well as with file checkouts. Additionally, a filter file .gitignore_mtime may be used to prevent files, or file classes from time stamp restoration. Typical content could be:

 *.c
 *.h

For any cases where original timestamps of all changes in a commit must be retrieved temporarily, this file can be renamed for this case, or other options could be considered for this. Theoretically, all timestamps of a particular revision could be restored, when the post-checkout hook would dig in the history for last commits of particular file.

Code

Both files are stored in .git/hooks/

pre-commit

#!/bin/tclsh
# pre-commit has no parameters

# Save all mtimes and filenames that are subject to commit.
proc save_mtimes {} {
    set fid [open .git_mtimes w+]
    foreach {stat fname} [exec git diff --cached --name-status] {
        puts $fid "$fname [file mtime $fname]"
    }
    close $fid
    exec git add .git_mtimes
}

save_mtimes
exit 0

post-checkout

#!/bin/tclsh
# post-checkout arguments:
#     ref prev. HEAD; ref new HEAD; branch(1)/file(0) checkout

# Restore all mtimes of files that are touched no longer than 5 s ago
# and restore their file mtime when filename is not in .gitignore_mtime.
proc update_mtimes {type} {
    # type not yet used
    set filterlist ""
    if {[file exists .gitignore_mtime]} {
        set fid [open .gitignore_mtime r]
        set filterlist [read $fid]
        close $fid
    }
    set fid [open .git_mtimes r]
    set time [clock seconds]
    set count 0
    while {![eof $fid]} {
        lassign [gets $fid] fname timestamp
        if {$fname == ""} break
        if {[file mtime $fname] > $time - 5} {
            # if file updated, then check ignorelist
            set match 0
            foreach filter $filterlist {
                if {[string match $filter $fname]} {
                    set match 1
                }
            }
            if {!$match} {
                file mtime $fname $timestamp
                incr count
            }
        }
    }
    close $fid
    if {$count} {
        puts "$count file(s) have restored mtime"
    }
}

if {![file exists .git_mtimes]} {
    exit 0
}
update_mtimes [lindex argv 2]
exit 0

Upon first configuration an empty file .git_mtimes must be created and git add must be applied. Otherwise, it is not in the commit. Note: these hooks are provided "as is" - no liability for any problems arising from its usage. Improvements welcome... (RJM)