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.View
|
||||||
import Types.MetaData
|
import Types.MetaData
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import qualified Git.DiffTree
|
import qualified Git.DiffTree as DiffTree
|
||||||
import qualified Git.Branch
|
import qualified Git.Branch
|
||||||
import qualified Git.LsFiles
|
import qualified Git.LsFiles
|
||||||
|
import qualified Git.Ref
|
||||||
import Git.UpdateIndex
|
import Git.UpdateIndex
|
||||||
import Git.Sha
|
import Git.Sha
|
||||||
import Git.HashObject
|
import Git.HashObject
|
||||||
import Git.Types
|
import Git.Types
|
||||||
|
import Git.FilePath
|
||||||
import qualified Backend
|
import qualified Backend
|
||||||
import Annex.Index
|
import Annex.Index
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
|
import Annex.CatFile
|
||||||
import Logs.MetaData
|
import Logs.MetaData
|
||||||
import Logs.View
|
import Logs.View
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
import Types.Command
|
||||||
|
import Config
|
||||||
|
import CmdLine.Action
|
||||||
|
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import System.Path.WildMatch
|
import System.Path.WildMatch
|
||||||
|
@ -337,14 +343,50 @@ applyView' mkfileview view = do
|
||||||
-}
|
-}
|
||||||
updateView :: View -> Git.Ref -> Git.Ref -> Annex Git.Branch
|
updateView :: View -> Git.Ref -> Git.Ref -> Annex Git.Branch
|
||||||
updateView view ref oldref = genViewBranch view $ do
|
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
|
forM_ diffs go
|
||||||
void $ liftIO cleanup
|
void $ liftIO cleanup
|
||||||
where
|
where
|
||||||
go diff
|
go diff
|
||||||
| Git.DiffTree.dstsha diff == nullSha = error "TODO delete file"
|
| DiffTree.dstsha diff == nullSha = error "TODO delete file"
|
||||||
| otherwise = error "TODO add 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
|
{- 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.
|
- 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
|
- Then a commit is made, to the view branch. The view branch is not
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex command
|
{- 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.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -13,6 +13,13 @@ import Config
|
||||||
import qualified Command.Add
|
import qualified Command.Add
|
||||||
import qualified Command.Fix
|
import qualified Command.Fix
|
||||||
import Annex.Direct
|
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]
|
||||||
def = [command "pre-commit" paramPaths seek SectionPlumbing
|
def = [command "pre-commit" paramPaths seek SectionPlumbing
|
||||||
|
@ -27,13 +34,45 @@ seek ps = ifM isDirect
|
||||||
withFilesToBeCommitted (whenAnnexed Command.Fix.start) ps
|
withFilesToBeCommitted (whenAnnexed Command.Fix.start) ps
|
||||||
-- inject unlocked files into the annex
|
-- inject unlocked files into the annex
|
||||||
withFilesUnlockedToBeCommitted startIndirect ps
|
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 :: FilePath -> CommandStart
|
||||||
startIndirect file = next $ do
|
startIndirect f = next $ do
|
||||||
unlessM (callCommandAction $ Command.Add.start file) $
|
unlessM (callCommandAction $ Command.Add.start f) $
|
||||||
error $ "failed to add " ++ file ++ "; canceling commit"
|
error $ "failed to add " ++ f ++ "; canceling commit"
|
||||||
next $ return True
|
next $ return True
|
||||||
|
|
||||||
startDirect :: [String] -> CommandStart
|
startDirect :: [String] -> CommandStart
|
||||||
startDirect _ = next $ next $ preCommitDirect
|
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 (
|
module Logs.MetaData (
|
||||||
getCurrentMetaData,
|
getCurrentMetaData,
|
||||||
getMetaData,
|
getMetaData,
|
||||||
setMetaData,
|
|
||||||
unsetMetaData,
|
|
||||||
addMetaData,
|
addMetaData,
|
||||||
addMetaData',
|
addMetaData',
|
||||||
currentMetaData,
|
currentMetaData,
|
||||||
|
@ -58,16 +56,6 @@ getCurrentMetaData = currentMetaData . collect <$$> getMetaData
|
||||||
where
|
where
|
||||||
collect = foldl' unionMetaData newMetaData . map value . S.toAscList
|
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
|
{- Adds in some metadata, which can override existing values, or unset
|
||||||
- them, but otherwise leaves any existing metadata as-is. -}
|
- them, but otherwise leaves any existing metadata as-is. -}
|
||||||
addMetaData :: Key -> MetaData -> Annex ()
|
addMetaData :: Key -> MetaData -> Annex ()
|
||||||
|
|
|
@ -22,12 +22,14 @@ module Types.MetaData (
|
||||||
toMetaValue,
|
toMetaValue,
|
||||||
mkMetaValue,
|
mkMetaValue,
|
||||||
unsetMetaValue,
|
unsetMetaValue,
|
||||||
|
unsetMetaData,
|
||||||
fromMetaValue,
|
fromMetaValue,
|
||||||
fromMetaData,
|
fromMetaData,
|
||||||
newMetaData,
|
newMetaData,
|
||||||
updateMetaData,
|
updateMetaData,
|
||||||
unionMetaData,
|
unionMetaData,
|
||||||
differenceMetaData,
|
differenceMetaData,
|
||||||
|
isSet,
|
||||||
currentMetaData,
|
currentMetaData,
|
||||||
currentMetaDataValues,
|
currentMetaDataValues,
|
||||||
metaDataValues,
|
metaDataValues,
|
||||||
|
@ -137,6 +139,10 @@ mkMetaValue = MetaValue
|
||||||
unsetMetaValue :: MetaValue -> MetaValue
|
unsetMetaValue :: MetaValue -> MetaValue
|
||||||
unsetMetaValue (MetaValue _ s) = MetaValue (CurrentlySet False) s
|
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 -> String
|
||||||
fromMetaField (MetaField f) = f
|
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
|
* view: New command that creates and checks out a branch that provides
|
||||||
a structured view of selected metadata.
|
a structured view of selected metadata.
|
||||||
* vadd, vpop, vcycle: New commands for operating within views.
|
* 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.
|
* 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 set up box.com, Amazon S3, and rsync.net remotes
|
||||||
* Windows webapp: Can create repos on removable drives.
|
* Windows webapp: Can create repos on removable drives.
|
||||||
|
|
|
@ -321,10 +321,12 @@ subdirectories).
|
||||||
shown in the view.
|
shown in the view.
|
||||||
|
|
||||||
Multiple values for a metadata field can be specified, either by using
|
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
|
Once within a view, you can make additional subdirectories, and
|
||||||
subdirectory for each value.
|
copy or move files into them. When you commit, the metadata will
|
||||||
|
be updated to correspond to your changes.
|
||||||
|
|
||||||
* `vpop [N]`
|
* `vpop [N]`
|
||||||
|
|
||||||
|
@ -801,7 +803,8 @@ subdirectories).
|
||||||
|
|
||||||
Fixes up symlinks that are staged as part of a commit, to ensure they
|
Fixes up symlinks that are staged as part of a commit, to ensure they
|
||||||
point to annexed content. Also handles injecting changes to unlocked
|
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 ...]`
|
* `lookupkey [file ...]`
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue