From bc5c54c987f548505a3877e8a0e460abe0b2a081 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 14 Mar 2011 23:00:23 -0400 Subject: [PATCH] symlink touching fun When adding files to the annex, the symlinks pointing at the annexed content are made to have the same mtime as the original file. While git does not preserve that information, this allows a tool like metastore to be used with annexed files. --- .gitignore | 1 + Command/Add.hs | 7 ++++ Command/Find.hs | 1 - Command/PreCommit.hs | 1 - Makefile | 7 ++-- Touch.hsc | 70 ++++++++++++++++++++++++++++++++++++ debian/changelog | 4 +++ doc/bugs/softlink_atime.mdwn | 14 ++++++++ 8 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 Touch.hsc diff --git a/.gitignore b/.gitignore index 764a1af9d5..69d2c80709 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ doc/.ikiwiki html *.tix .hpc +Touch.hs diff --git a/Command/Add.hs b/Command/Add.hs index 26e7fa258d..09fff7cff7 100644 --- a/Command/Add.hs +++ b/Command/Add.hs @@ -18,6 +18,7 @@ import Types import Content import Messages import Utility +import Touch command :: [Command] command = [Command "add" paramPath seek "add files to annex"] @@ -53,5 +54,11 @@ cleanup file key = do link <- calcGitLink file key liftIO $ createSymbolicLink link file + + -- touch the symlink to have the same mtime as the file it points to + s <- liftIO $ getFileStatus file + let mtime = modificationTime s + _ <- liftIO $ touch file (TimeSpec mtime 0) False + Annex.queue "add" [Param "--"] file return True diff --git a/Command/Find.hs b/Command/Find.hs index 1ca6ff1e7c..3ed15c1537 100644 --- a/Command/Find.hs +++ b/Command/Find.hs @@ -12,7 +12,6 @@ import Control.Monad.State (liftIO) import Command import Content -import Messages command :: [Command] command = [Command "find" (paramOptional $ paramRepeating paramPath) seek diff --git a/Command/PreCommit.hs b/Command/PreCommit.hs index 6f9adb79a5..1465ebc615 100644 --- a/Command/PreCommit.hs +++ b/Command/PreCommit.hs @@ -14,7 +14,6 @@ import qualified Annex import qualified GitRepo as Git import qualified Command.Add import qualified Command.Fix -import Messages import Utility command :: [Command] diff --git a/Makefile b/Makefile index c888fc2151..c381ae986d 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,10 @@ SysConfig.hs: configure.hs TestConfig.hs $(GHCMAKE) configure ./configure -$(bins): SysConfig.hs +Touch.hs: Touch.hsc + hsc2hs $< + +$(bins): SysConfig.hs Touch.hs $(GHCMAKE) $@ git-annex.1: doc/git-annex.mdwn @@ -57,7 +60,7 @@ docs: $(mans) --exclude='news/.*' clean: - rm -rf build $(bins) $(mans) test configure SysConfig.hs *.tix .hpc + rm -rf build $(bins) $(mans) test configure Touch.hs SysConfig.hs *.tix .hpc rm -rf doc/.ikiwiki html find . \( -name \*.o -or -name \*.hi \) -exec rm {} \; diff --git a/Touch.hsc b/Touch.hsc new file mode 100644 index 0000000000..ad36761c4b --- /dev/null +++ b/Touch.hsc @@ -0,0 +1,70 @@ +{- More control over touching a file. + - + - Copyright 2011 Joey Hess + - + - Licensed under the GNU GPL version 3 or higher. + -} + +{-# LANGUAGE ForeignFunctionInterface #-} + +module Touch ( + TimeSpec(..), + now, + omit, + touchBoth, + touch +) where + +import Foreign +import Foreign.C + +#include +#include +#include + +#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__) + +data TimeSpec = TimeSpec CTime CLong + +instance Storable TimeSpec where + alignment _ = #{alignment struct timespec} + sizeOf _ = #{size struct timespec} + peek ptr = do + sec <- #{peek struct timespec, tv_sec} ptr + nsec <- #{peek struct timespec, tv_nsec} ptr + return $ TimeSpec sec nsec + poke ptr (TimeSpec sec nsec) = do + #{poke struct timespec, tv_sec} ptr sec + #{poke struct timespec, tv_nsec} ptr nsec + +{- special timespecs -} +omit :: TimeSpec +omit = TimeSpec 0 #const UTIME_OMIT +now :: TimeSpec +now = TimeSpec 0 #const UTIME_NOW + +{- While its interface is beastly, utimensat is in recent + POSIX standards, unlike futimes. -} +foreign import ccall "utimensat" + c_utimensat :: CInt -> CString -> Ptr TimeSpec -> CInt -> IO CInt + +{- Changes the access and/or modification times of a file. + Can follow symlinks, or not. -} +touchBoth :: FilePath -> TimeSpec -> TimeSpec -> Bool -> IO Bool +touchBoth file atime mtime follow = + allocaArray 2 $ \ptr -> + withCString file $ \f -> do + pokeArray ptr [atime, mtime] + r <- c_utimensat at_fdcwd f ptr flags + putStrLn $ "ret " ++ (show r) + return (r == 0) + where + at_fdcwd = #const AT_FDCWD + at_symlink_nofollow = #const AT_SYMLINK_NOFOLLOW + + flags = if follow + then 0 + else at_symlink_nofollow + +touch :: FilePath -> TimeSpec -> Bool -> IO Bool +touch file mtime follow = touchBoth file omit mtime follow diff --git a/debian/changelog b/debian/changelog index 6e70ac67e3..e7017a26d0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,10 @@ git-annex (0.24) UNRELEASED; urgency=low * Add Suggests on graphviz. Closes: #618039 + * When adding files to the annex, the symlinks pointing at the annexed + content are made to have the same mtime as the original file. + While git does not preserve that information, this allows a tool + like metastore to be used with annexed files. -- Joey Hess Sun, 13 Mar 2011 14:25:17 -0400 diff --git a/doc/bugs/softlink_atime.mdwn b/doc/bugs/softlink_atime.mdwn index 49e26013ff..ebb040dd1e 100644 --- a/doc/bugs/softlink_atime.mdwn +++ b/doc/bugs/softlink_atime.mdwn @@ -21,3 +21,17 @@ Optionally, editing the meta-data should change the times in all annexes. >>> unlikely to do it better. >>>> OK, thanks for the clarification. Would it be acceptable for you to put the timestamps into the metastore with vanilla git? If such an option existed, everyone would be able to benefit and not just me. -- RichiH + +>>>>> I've now committed to git changes to make git-annex add make +>>>>> symlinks that reflect the original file's mtime. (It's not possible +>>>>> to set the ctime of a symlink; nor would you want to as messing with +>>>>> ctimes can break backup software ... and atime doesn't much matter.) +>>>>> +>>>>> So all you have to do is make the pre-commit hook call +>>>>> [metastore](http://david.hardeman.nu/software.php). The hook +>>>>> would look like this: ---[[Joey]] [[!tag done]] + + #!/bin/sh + git annex pre-commit . + metastore --save + git add .metadata