summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2014-03-18 18:55:43 -0400
committerGravatar Joey Hess <joey@kitenet.net>2014-03-18 18:55:43 -0400
commit2402266d0d9731445af1876327a43795795d1a18 (patch)
tree2f52191002e8ee79275c2f4e6070b057a61f9389
parent9adb236103d0b8f1db8110b40cc33b9fe5fda4ae (diff)
Each for each metadata field, there's now an automatically maintained "$field-lastchanged" that gives the timestamp of the last change to that field.
Note that this is a nearly entirely free feature. The data was already stored in the metadata log in an easily accessible way, and already was parsed to a time when parsing the log. The generation of the metadata fields may even be done lazily, although probably not entirely (the map has to be evaulated to when queried).
-rw-r--r--Annex/MetaData.hs17
-rw-r--r--Annex/MetaData/StandardFields.hs38
-rw-r--r--Command/MetaData.hs3
-rw-r--r--Logs/MetaData.hs42
-rw-r--r--Types/MetaData.hs4
-rw-r--r--debian/changelog3
-rw-r--r--doc/metadata.mdwn7
-rw-r--r--doc/todo/wishlist:_metadata_metadata_view.mdwn3
8 files changed, 94 insertions, 23 deletions
diff --git a/Annex/MetaData.hs b/Annex/MetaData.hs
index 68aef33f1..f382f0ab1 100644
--- a/Annex/MetaData.hs
+++ b/Annex/MetaData.hs
@@ -5,11 +5,15 @@
- Licensed under the GNU GPL version 3 or higher.
-}
-module Annex.MetaData where
+module Annex.MetaData (
+ genMetaData,
+ module X
+) where
import Common.Annex
import qualified Annex
-import Types.MetaData
+import Types.MetaData as X
+import Annex.MetaData.StandardFields as X
import Logs.MetaData
import Annex.CatFile
@@ -19,15 +23,6 @@ import Data.Time.Calendar
import Data.Time.Clock
import Data.Time.Clock.POSIX
-tagMetaField :: MetaField
-tagMetaField = mkMetaFieldUnchecked "tag"
-
-yearMetaField :: MetaField
-yearMetaField = mkMetaFieldUnchecked "year"
-
-monthMetaField :: MetaField
-monthMetaField = mkMetaFieldUnchecked "month"
-
{- Adds metadata for a file that has just been ingested into the
- annex, but has not yet been committed to git.
-
diff --git a/Annex/MetaData/StandardFields.hs b/Annex/MetaData/StandardFields.hs
new file mode 100644
index 000000000..00b810593
--- /dev/null
+++ b/Annex/MetaData/StandardFields.hs
@@ -0,0 +1,38 @@
+{- git-annex metadata, standard fields
+ -
+ - Copyright 2014 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Annex.MetaData.StandardFields (
+ tagMetaField,
+ yearMetaField,
+ monthMetaField,
+ lastChangedField,
+ isLastChangedField
+) where
+
+import Types.MetaData
+
+import Data.List
+
+tagMetaField :: MetaField
+tagMetaField = mkMetaFieldUnchecked "tag"
+
+yearMetaField :: MetaField
+yearMetaField = mkMetaFieldUnchecked "year"
+
+monthMetaField :: MetaField
+monthMetaField = mkMetaFieldUnchecked "month"
+
+lastChangedField :: MetaField -> MetaField
+lastChangedField f = mkMetaFieldUnchecked (fromMetaField f ++ lastchanged)
+
+isLastChangedField :: MetaField -> Bool
+isLastChangedField f = lastchanged `isSuffixOf` s && s /= lastchanged
+ where
+ s = fromMetaField f
+
+lastchanged :: String
+lastchanged = "-lastchanged"
diff --git a/Command/MetaData.hs b/Command/MetaData.hs
index 46d112162..d932315ab 100644
--- a/Command/MetaData.hs
+++ b/Command/MetaData.hs
@@ -12,7 +12,6 @@ import qualified Annex
import Command
import Annex.MetaData
import Logs.MetaData
-import Types.MetaData
import qualified Data.Set as S
import Data.Time.Clock.POSIX
@@ -84,7 +83,7 @@ perform :: POSIXTime -> [ModMeta] -> Key -> CommandPerform
perform _ [] k = next $ cleanup k
perform now ms k = do
oldm <- getCurrentMetaData k
- let m = foldl' unionMetaData emptyMetaData $ map (modMeta oldm) ms
+ let m = combineMetaData $ map (modMeta oldm) ms
addMetaData' k m now
next $ cleanup k
diff --git a/Logs/MetaData.hs b/Logs/MetaData.hs
index 6702c3733..724f309ff 100644
--- a/Logs/MetaData.hs
+++ b/Logs/MetaData.hs
@@ -36,26 +36,50 @@ module Logs.MetaData (
import Common.Annex
import Types.MetaData
+import Annex.MetaData.StandardFields
import qualified Annex.Branch
import Logs
import Logs.SingleValue
import qualified Data.Set as S
+import qualified Data.Map as M
import Data.Time.Clock.POSIX
+import Data.Time.Format
+import System.Locale
instance SingleValueSerializable MetaData where
serialize = Types.MetaData.serialize
deserialize = Types.MetaData.deserialize
-getMetaData :: Key -> Annex (Log MetaData)
-getMetaData = readLog . metaDataLogFile
+getMetaDataLog :: Key -> Annex (Log MetaData)
+getMetaDataLog = readLog . metaDataLogFile
{- Go through the log from oldest to newest, and combine it all
- - into a single MetaData representing the current state. -}
+ - into a single MetaData representing the current state.
+ -
+ - Automatically generates a lastchanged metadata for each field that's
+ - currently set, based on timestamps in the log.
+ -}
getCurrentMetaData :: Key -> Annex MetaData
-getCurrentMetaData = currentMetaData . collect <$$> getMetaData
+getCurrentMetaData k = do
+ ls <- S.toAscList <$> getMetaDataLog k
+ let loggedmeta = currentMetaData $ combineMetaData $ map value ls
+ return $ currentMetaData $ unionMetaData loggedmeta
+ (lastchanged ls loggedmeta)
where
- collect = foldl' unionMetaData emptyMetaData . map value . S.toAscList
+ lastchanged ls (MetaData wanted) =
+ let m = foldl' (flip M.union) M.empty (map genlastchanged ls)
+ in MetaData $ M.mapKeys lastChangedField $
+ -- Only include fields that are currently set.
+ m `M.intersection` wanted
+ -- Makes each field have the timestamp as its value.
+ genlastchanged l =
+ let MetaData m = value l
+ ts = S.singleton $ toMetaValue $
+ formatTime defaultTimeLocale "%s" $
+ posixSecondsToUTCTime $
+ changed l
+ in M.map (const ts) m
{- Adds in some metadata, which can override existing values, or unset
- them, but otherwise leaves any existing metadata as-is. -}
@@ -67,10 +91,12 @@ addMetaData k metadata = addMetaData' k metadata =<< liftIO getPOSIXTime
- will tend to be generated across the different log files, and so
- git will be able to pack the data more efficiently. -}
addMetaData' :: Key -> MetaData -> POSIXTime -> Annex ()
-addMetaData' k metadata now = Annex.Branch.change (metaDataLogFile k) $
+addMetaData' k (MetaData m) now = Annex.Branch.change (metaDataLogFile k) $
showLog . simplifyLog
- . S.insert (LogEntry now metadata)
+ . S.insert (LogEntry now metadata)
. parseLog
+ where
+ metadata = MetaData $ M.filterWithKey (\f _ -> not (isLastChangedField f)) m
{- Simplify a log, removing historical values that are no longer
- needed.
@@ -148,7 +174,7 @@ copyMetaData :: Key -> Key -> Annex ()
copyMetaData oldkey newkey
| oldkey == newkey = noop
| otherwise = do
- l <- getMetaData oldkey
+ l <- getMetaDataLog oldkey
unless (S.null l) $
Annex.Branch.change (metaDataLogFile newkey) $
const $ showLog l
diff --git a/Types/MetaData.hs b/Types/MetaData.hs
index c37b31c51..70b5bd840 100644
--- a/Types/MetaData.hs
+++ b/Types/MetaData.hs
@@ -28,6 +28,7 @@ module Types.MetaData (
emptyMetaData,
updateMetaData,
unionMetaData,
+ combineMetaData,
differenceMetaData,
isSet,
currentMetaData,
@@ -188,6 +189,9 @@ unionMetaData :: MetaData -> MetaData -> MetaData
unionMetaData (MetaData old) (MetaData new) = MetaData $
M.unionWith S.union new old
+combineMetaData :: [MetaData] -> MetaData
+combineMetaData = foldl' unionMetaData emptyMetaData
+
differenceMetaData :: MetaData -> MetaData -> MetaData
differenceMetaData (MetaData m) (MetaData excludem) = MetaData $
M.differenceWith diff m excludem
diff --git a/debian/changelog b/debian/changelog
index 8fde3c704..6ef4e5e84 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -27,6 +27,9 @@ git-annex (5.20140307) UNRELEASED; urgency=medium
* map: Fix crash when one of the remotes of a repo is a local directory
that does not exist, or is not a git repo.
* rsync special remote: Fix slashes when used on Windows.
+ * Each for each metadata field, there's now an automatically maintained
+ "$field-lastchanged" that gives the timestamp of the last change to that
+ field.
-- Joey Hess <joeyh@debian.org> Thu, 06 Mar 2014 16:17:01 -0400
diff --git a/doc/metadata.mdwn b/doc/metadata.mdwn
index df873c4c1..d51758038 100644
--- a/doc/metadata.mdwn
+++ b/doc/metadata.mdwn
@@ -23,12 +23,15 @@ The field names are limited to alphanumerics (and `[_-.]`), and are case
insensitive. The metadata values can contain absolutely anything you
like -- but you're recommended to keep it simple and reasonably short.
-Here are some recommended metadata fields to use:
+Here are some metadata fields that git-annex has special support for:
* `tag` - With each tag being a different value.
* `year`, `month` - When this particular version of the file came into
being.
-
+* `$field-lastchanged` - This is automatically maintained for each
+ field that's set, and gives the time stamp (since the Unix epoch)
+ of the most recent change to the field. It cannot be modified directly.
+
To make git-annex automatically set the year and month when adding files,
run `git config annex.genmetadata true`. Also, see
[[tips/automatically_adding_metadata]].
diff --git a/doc/todo/wishlist:_metadata_metadata_view.mdwn b/doc/todo/wishlist:_metadata_metadata_view.mdwn
index 841a84ddc..a4b243cdd 100644
--- a/doc/todo/wishlist:_metadata_metadata_view.mdwn
+++ b/doc/todo/wishlist:_metadata_metadata_view.mdwn
@@ -18,3 +18,6 @@ Something along the lines of
This would allow me to review files that haven't had any tag changes applied for a while and thus, may need the tags updating.
I've done this in every tagging system I've used by (ab)using mtime, but that requires an additional step (of touching the file).
+
+> [[done]]; "$field-lastchanged" is automatically made available for each
+> field! --[[Joey]]