pre-commit: Update metadata when committing changes to annexed files within a view.
So the user can now switch to a view and then move files around within it to manage metadata. For example, moving a file into a new directory when in the tags=* view adds a tag to it. Implementation is fairly efficient. One diff-index, which is no more expensive than the first stage of a git commit, followed by possibly some cat-file --batch traffic to find the key (when deleting a file). Very similar to what's done in direct mode when committing. And like direct mode when updating the WC after a merge, it has to buffer the diff-tree values in order to make 2 passes over them. When not in a view, pre-commit now does one extra git symbolic-ref, which is tiny overhead. This commit was sponsored by Andrew Eskridge.
This commit is contained in:
parent
02259d2a55
commit
39ebfa1a2e
6 changed files with 103 additions and 23 deletions
|
@ -13,19 +13,25 @@ import Common.Annex
|
|||
import Types.View
|
||||
import Types.MetaData
|
||||
import qualified Git
|
||||
import qualified Git.DiffTree
|
||||
import qualified Git.DiffTree as DiffTree
|
||||
import qualified Git.Branch
|
||||
import qualified Git.LsFiles
|
||||
import qualified Git.Ref
|
||||
import Git.UpdateIndex
|
||||
import Git.Sha
|
||||
import Git.HashObject
|
||||
import Git.Types
|
||||
import Git.FilePath
|
||||
import qualified Backend
|
||||
import Annex.Index
|
||||
import Annex.Link
|
||||
import Annex.CatFile
|
||||
import Logs.MetaData
|
||||
import Logs.View
|
||||
import Utility.FileMode
|
||||
import Types.Command
|
||||
import Config
|
||||
import CmdLine.Action
|
||||
|
||||
import qualified Data.Set as S
|
||||
import System.Path.WildMatch
|
||||
|
@ -337,14 +343,50 @@ applyView' mkfileview view = do
|
|||
-}
|
||||
updateView :: View -> Git.Ref -> Git.Ref -> Annex Git.Branch
|
||||
updateView view ref oldref = genViewBranch view $ do
|
||||
(diffs, cleanup) <- inRepo $ Git.DiffTree.diffTree oldref ref
|
||||
(diffs, cleanup) <- inRepo $ DiffTree.diffTree oldref ref
|
||||
forM_ diffs go
|
||||
void $ liftIO cleanup
|
||||
where
|
||||
go diff
|
||||
| Git.DiffTree.dstsha diff == nullSha = error "TODO delete file"
|
||||
| DiffTree.dstsha diff == nullSha = error "TODO delete file"
|
||||
| otherwise = error "TODO add file"
|
||||
|
||||
{- Diff between currently checked out branch and staged changes, and
|
||||
- update metadata to reflect the changes that are being committed to the
|
||||
- view.
|
||||
-
|
||||
- Adding a file to a directory adds the metadata represented by
|
||||
- that directory to the file, and removing a file from a directory
|
||||
- removes the metadata.
|
||||
-
|
||||
- Note that removes must be handled before adds. This is so
|
||||
- that moving a file from x/foo/ to x/bar/ adds back the metadata for x.
|
||||
-}
|
||||
withViewChanges :: (FileView -> Key -> CommandStart) -> (FileView -> Key -> CommandStart) -> Annex ()
|
||||
withViewChanges addmeta removemeta = do
|
||||
makeabs <- flip fromTopFilePath <$> gitRepo
|
||||
(diffs, cleanup) <- inRepo $ DiffTree.diffIndex Git.Ref.headRef
|
||||
forM_ diffs handleremovals
|
||||
forM_ diffs (handleadds makeabs)
|
||||
void $ liftIO cleanup
|
||||
where
|
||||
handleremovals item
|
||||
| DiffTree.srcsha item /= nullSha =
|
||||
handle item removemeta
|
||||
=<< catKey (DiffTree.srcsha item) (DiffTree.srcmode item)
|
||||
| otherwise = noop
|
||||
handleadds makeabs item
|
||||
| DiffTree.dstsha item /= nullSha =
|
||||
handle item addmeta
|
||||
=<< ifM isDirect
|
||||
( catKey (DiffTree.dstsha item) (DiffTree.dstmode item)
|
||||
-- optimisation
|
||||
, isAnnexLink $ makeabs $ DiffTree.file item
|
||||
)
|
||||
| otherwise = noop
|
||||
handle item a = maybe noop
|
||||
(void . commandAction . a (getTopFilePath $ DiffTree.file item))
|
||||
|
||||
{- Generates a branch for a view. This is done using a different index
|
||||
- file. An action is run to stage the files that will be in the branch.
|
||||
- Then a commit is made, to the view branch. The view branch is not
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{- git-annex command
|
||||
-
|
||||
- Copyright 2010, 2013 Joey Hess <joey@kitenet.net>
|
||||
- Copyright 2010-2014 Joey Hess <joey@kitenet.net>
|
||||
-
|
||||
- Licensed under the GNU GPL version 3 or higher.
|
||||
-}
|
||||
|
@ -13,6 +13,13 @@ import Config
|
|||
import qualified Command.Add
|
||||
import qualified Command.Fix
|
||||
import Annex.Direct
|
||||
import Annex.View
|
||||
import Logs.View
|
||||
import Logs.MetaData
|
||||
import Types.View
|
||||
import Types.MetaData
|
||||
|
||||
import qualified Data.Set as S
|
||||
|
||||
def :: [Command]
|
||||
def = [command "pre-commit" paramPaths seek SectionPlumbing
|
||||
|
@ -27,13 +34,45 @@ seek ps = ifM isDirect
|
|||
withFilesToBeCommitted (whenAnnexed Command.Fix.start) ps
|
||||
-- inject unlocked files into the annex
|
||||
withFilesUnlockedToBeCommitted startIndirect ps
|
||||
-- committing changes to a view updates metadata
|
||||
mv <- currentView
|
||||
case mv of
|
||||
Nothing -> noop
|
||||
Just v -> withViewChanges
|
||||
(addViewMetaData v)
|
||||
(removeViewMetaData v)
|
||||
)
|
||||
|
||||
startIndirect :: FilePath -> CommandStart
|
||||
startIndirect file = next $ do
|
||||
unlessM (callCommandAction $ Command.Add.start file) $
|
||||
error $ "failed to add " ++ file ++ "; canceling commit"
|
||||
startIndirect f = next $ do
|
||||
unlessM (callCommandAction $ Command.Add.start f) $
|
||||
error $ "failed to add " ++ f ++ "; canceling commit"
|
||||
next $ return True
|
||||
|
||||
startDirect :: [String] -> CommandStart
|
||||
startDirect _ = next $ next $ preCommitDirect
|
||||
|
||||
addViewMetaData :: View -> FileView -> Key -> CommandStart
|
||||
addViewMetaData v f k = do
|
||||
showStart "metadata" f
|
||||
next $ next $ changeMetaData k $ fromView v f
|
||||
|
||||
removeViewMetaData :: View -> FileView -> Key -> CommandStart
|
||||
removeViewMetaData v f k = do
|
||||
showStart "metadata" f
|
||||
next $ next $ changeMetaData k $ unsetMetaData $ fromView v f
|
||||
|
||||
changeMetaData :: Key -> MetaData -> CommandCleanup
|
||||
changeMetaData k metadata = do
|
||||
showMetaDataChange metadata
|
||||
addMetaData k metadata
|
||||
return True
|
||||
|
||||
showMetaDataChange :: MetaData -> Annex ()
|
||||
showMetaDataChange = showLongNote . unlines . concatMap showmeta . fromMetaData
|
||||
where
|
||||
showmeta (f, vs) = map (showmetavalue f) $ S.toList vs
|
||||
showmetavalue f v = fromMetaField f ++ showset v ++ "=" ++ fromMetaValue v
|
||||
showset v
|
||||
| isSet v = "+"
|
||||
| otherwise = "-"
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
module Logs.MetaData (
|
||||
getCurrentMetaData,
|
||||
getMetaData,
|
||||
setMetaData,
|
||||
unsetMetaData,
|
||||
addMetaData,
|
||||
addMetaData',
|
||||
currentMetaData,
|
||||
|
@ -58,16 +56,6 @@ getCurrentMetaData = currentMetaData . collect <$$> getMetaData
|
|||
where
|
||||
collect = foldl' unionMetaData newMetaData . map value . S.toAscList
|
||||
|
||||
setMetaData :: Key -> MetaField -> String -> Annex ()
|
||||
setMetaData = setMetaData' True
|
||||
|
||||
unsetMetaData :: Key -> MetaField -> String -> Annex ()
|
||||
unsetMetaData = setMetaData' False
|
||||
|
||||
setMetaData' :: Bool -> Key -> MetaField -> String -> Annex ()
|
||||
setMetaData' isset k field s = addMetaData k $
|
||||
updateMetaData field (mkMetaValue (CurrentlySet isset) s) newMetaData
|
||||
|
||||
{- Adds in some metadata, which can override existing values, or unset
|
||||
- them, but otherwise leaves any existing metadata as-is. -}
|
||||
addMetaData :: Key -> MetaData -> Annex ()
|
||||
|
|
|
@ -22,12 +22,14 @@ module Types.MetaData (
|
|||
toMetaValue,
|
||||
mkMetaValue,
|
||||
unsetMetaValue,
|
||||
unsetMetaData,
|
||||
fromMetaValue,
|
||||
fromMetaData,
|
||||
newMetaData,
|
||||
updateMetaData,
|
||||
unionMetaData,
|
||||
differenceMetaData,
|
||||
isSet,
|
||||
currentMetaData,
|
||||
currentMetaDataValues,
|
||||
metaDataValues,
|
||||
|
@ -137,6 +139,10 @@ mkMetaValue = MetaValue
|
|||
unsetMetaValue :: MetaValue -> MetaValue
|
||||
unsetMetaValue (MetaValue _ s) = MetaValue (CurrentlySet False) s
|
||||
|
||||
{- Marks all MetaValues as no longer currently set. -}
|
||||
unsetMetaData :: MetaData -> MetaData
|
||||
unsetMetaData (MetaData m) = MetaData $ M.map (S.map unsetMetaValue) m
|
||||
|
||||
fromMetaField :: MetaField -> String
|
||||
fromMetaField (MetaField f) = f
|
||||
|
||||
|
|
2
debian/changelog
vendored
2
debian/changelog
vendored
|
@ -8,6 +8,8 @@ git-annex (5.20140211) UNRELEASED; urgency=medium
|
|||
* view: New command that creates and checks out a branch that provides
|
||||
a structured view of selected metadata.
|
||||
* vadd, vpop, vcycle: New commands for operating within views.
|
||||
* pre-commit: Update metadata when committing changes to annexed files
|
||||
within a view.
|
||||
* Add progress display for transfers to/from external special remotes.
|
||||
* Windows webapp: Can set up box.com, Amazon S3, and rsync.net remotes
|
||||
* Windows webapp: Can create repos on removable drives.
|
||||
|
|
|
@ -321,10 +321,12 @@ subdirectories).
|
|||
shown in the view.
|
||||
|
||||
Multiple values for a metadata field can be specified, either by using
|
||||
a glob (field="\*") or by listing each wanted value.
|
||||
a glob (field="\*") or by listing each wanted value. The resulting view
|
||||
will put files in subdirectories according to the value of their fields.
|
||||
|
||||
When multiple field values match, the view branch will have a
|
||||
subdirectory for each value.
|
||||
Once within a view, you can make additional subdirectories, and
|
||||
copy or move files into them. When you commit, the metadata will
|
||||
be updated to correspond to your changes.
|
||||
|
||||
* `vpop [N]`
|
||||
|
||||
|
@ -801,7 +803,8 @@ subdirectories).
|
|||
|
||||
Fixes up symlinks that are staged as part of a commit, to ensure they
|
||||
point to annexed content. Also handles injecting changes to unlocked
|
||||
files into the annex.
|
||||
files into the annex. When in a view, updates metadata to reflect changes
|
||||
made to files in the view.
|
||||
|
||||
* `lookupkey [file ...]`
|
||||
|
||||
|
|
Loading…
Reference in a new issue