log: Add --gource mode, which generates output usable by gource.
As part of this, I fixed up how log was getting the descriptions of remotes.
This commit is contained in:
parent
2f0c3befbd
commit
a35278430a
6 changed files with 97 additions and 31 deletions
|
@ -8,6 +8,7 @@
|
||||||
module Command.Log where
|
module Command.Log where
|
||||||
|
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.Map as M
|
||||||
import qualified Data.ByteString.Lazy.Char8 as L
|
import qualified Data.ByteString.Lazy.Char8 as L
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
import Data.Time
|
import Data.Time
|
||||||
|
@ -32,12 +33,17 @@ data RefChange = RefChange
|
||||||
, newref :: Git.Ref
|
, newref :: Git.Ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Outputter = Bool -> POSIXTime -> [UUID] -> Annex ()
|
||||||
|
|
||||||
def :: [Command]
|
def :: [Command]
|
||||||
def = [withOptions options $
|
def = [withOptions options $
|
||||||
command "log" paramPaths seek "shows location log"]
|
command "log" paramPaths seek "shows location log"]
|
||||||
|
|
||||||
options :: [Option]
|
options :: [Option]
|
||||||
options = map odate ["since", "after", "until", "before"] ++
|
options = passthruOptions ++ [gourceOption]
|
||||||
|
|
||||||
|
passthruOptions :: [Option]
|
||||||
|
passthruOptions = map odate ["since", "after", "until", "before"] ++
|
||||||
[ Option.field ['n'] "max-count" paramNumber
|
[ Option.field ['n'] "max-count" paramNumber
|
||||||
"limit number of logs displayed"
|
"limit number of logs displayed"
|
||||||
]
|
]
|
||||||
|
@ -45,26 +51,37 @@ options = map odate ["since", "after", "until", "before"] ++
|
||||||
odate n = Option.field [] n paramDate $
|
odate n = Option.field [] n paramDate $
|
||||||
"show log " ++ n ++ " date"
|
"show log " ++ n ++ " date"
|
||||||
|
|
||||||
|
gourceOption :: Option
|
||||||
|
gourceOption = Option.flag [] "gource" "format output for gource"
|
||||||
|
|
||||||
seek :: [CommandSeek]
|
seek :: [CommandSeek]
|
||||||
seek = [withValue (concat <$> mapM getoption options) $ \os ->
|
seek = [withValue (Remote.uuidDescriptions) $ \m ->
|
||||||
withFilesInGit $ whenAnnexed $ start os]
|
withValue (liftIO getCurrentTimeZone) $ \zone ->
|
||||||
|
withValue (concat <$> mapM getoption passthruOptions) $ \os ->
|
||||||
|
withFlag gourceOption $ \gource ->
|
||||||
|
withFilesInGit $ whenAnnexed $ start m zone os gource]
|
||||||
where
|
where
|
||||||
getoption o = maybe [] (use o) <$>
|
getoption o = maybe [] (use o) <$>
|
||||||
Annex.getField (Option.name o)
|
Annex.getField (Option.name o)
|
||||||
use o v = [Param ("--" ++ Option.name o), Param v]
|
use o v = [Param ("--" ++ Option.name o), Param v]
|
||||||
|
|
||||||
start :: [CommandParam] -> FilePath -> (Key, Backend) -> CommandStart
|
start :: (M.Map UUID String) -> TimeZone -> [CommandParam] -> Bool ->
|
||||||
start os file (key, _) = do
|
FilePath -> (Key, Backend) -> CommandStart
|
||||||
showLog file =<< readLog <$> getLog key os
|
start m zone os gource file (key, _) = do
|
||||||
|
showLog output =<< readLog <$> getLog key os
|
||||||
liftIO Git.Command.reap
|
liftIO Git.Command.reap
|
||||||
stop
|
stop
|
||||||
|
where
|
||||||
|
output
|
||||||
|
| gource = gourceOutput lookupdescription file
|
||||||
|
| otherwise = normalOutput lookupdescription file zone
|
||||||
|
lookupdescription u = fromMaybe (fromUUID u) $ M.lookup u m
|
||||||
|
|
||||||
showLog :: FilePath -> [RefChange] -> Annex ()
|
showLog :: Outputter -> [RefChange] -> Annex ()
|
||||||
showLog file ps = do
|
showLog outputter ps = do
|
||||||
zone <- liftIO getCurrentTimeZone
|
|
||||||
sets <- mapM (getset newref) ps
|
sets <- mapM (getset newref) ps
|
||||||
previous <- maybe (return genesis) (getset oldref) (lastMaybe ps)
|
previous <- maybe (return genesis) (getset oldref) (lastMaybe ps)
|
||||||
sequence_ $ compareChanges (output zone) $ sets ++ [previous]
|
sequence_ $ compareChanges outputter $ sets ++ [previous]
|
||||||
where
|
where
|
||||||
genesis = (0, S.empty)
|
genesis = (0, S.empty)
|
||||||
getset select change = do
|
getset select change = do
|
||||||
|
@ -72,28 +89,36 @@ showLog file ps = do
|
||||||
return (changetime change, s)
|
return (changetime change, s)
|
||||||
get ref = map toUUID . Logs.Presence.getLog . L.unpack <$>
|
get ref = map toUUID . Logs.Presence.getLog . L.unpack <$>
|
||||||
catObject ref
|
catObject ref
|
||||||
output zone present ts s = do
|
|
||||||
rs <- map (dropWhile isSpace) . lines <$>
|
normalOutput :: (UUID -> String) -> FilePath -> TimeZone -> Outputter
|
||||||
Remote.prettyPrintUUIDs "log" (S.toList s)
|
normalOutput lookupdescription file zone present ts us = do
|
||||||
liftIO $ mapM_ (putStrLn . format) rs
|
liftIO $ mapM_ (putStrLn . format) us
|
||||||
where
|
where
|
||||||
time = showTimeStamp zone ts
|
time = showTimeStamp zone ts
|
||||||
addel = if present then "+" else "-"
|
addel = if present then "+" else "-"
|
||||||
format r = unwords
|
format u = unwords [ addel, time, file, "|",
|
||||||
[ addel, time, file, "|", r ]
|
fromUUID u ++ " -- " ++ lookupdescription u ]
|
||||||
|
|
||||||
|
gourceOutput :: (UUID -> String) -> FilePath -> Outputter
|
||||||
|
gourceOutput lookupdescription file present ts us = do
|
||||||
|
liftIO $ mapM_ (putStrLn . intercalate "|" . format) us
|
||||||
|
where
|
||||||
|
time = takeWhile isDigit $ show ts
|
||||||
|
addel = if present then "A" else "M"
|
||||||
|
format u = [ time, lookupdescription u, addel, file ]
|
||||||
|
|
||||||
{- Generates a display of the changes (which are ordered with newest first),
|
{- Generates a display of the changes (which are ordered with newest first),
|
||||||
- by comparing each change with the previous change.
|
- by comparing each change with the previous change.
|
||||||
- Uses a formatter to generate a display of items that are added and
|
- Uses a formatter to generate a display of items that are added and
|
||||||
- removed. -}
|
- removed. -}
|
||||||
compareChanges :: Ord a => (Bool -> POSIXTime -> S.Set a -> b) -> [(POSIXTime, S.Set a)] -> [b]
|
compareChanges :: Ord a => (Bool -> POSIXTime -> [a] -> b) -> [(POSIXTime, S.Set a)] -> [b]
|
||||||
compareChanges format changes = concatMap diff $ zip changes (drop 1 changes)
|
compareChanges format changes = concatMap diff $ zip changes (drop 1 changes)
|
||||||
where
|
where
|
||||||
diff ((ts, new), (_, old)) =
|
diff ((ts, new), (_, old)) =
|
||||||
[format True ts added, format False ts removed]
|
[format True ts added, format False ts removed]
|
||||||
where
|
where
|
||||||
added = S.difference new old
|
added = S.toList $ S.difference new old
|
||||||
removed = S.difference old new
|
removed = S.toList $ S.difference old new
|
||||||
|
|
||||||
{- Gets the git log for a given location log file.
|
{- Gets the git log for a given location log file.
|
||||||
-
|
-
|
||||||
|
|
27
Remote.hs
27
Remote.hs
|
@ -19,6 +19,7 @@ module Remote (
|
||||||
remoteList,
|
remoteList,
|
||||||
enabledRemoteList,
|
enabledRemoteList,
|
||||||
remoteMap,
|
remoteMap,
|
||||||
|
uuidDescriptions,
|
||||||
byName,
|
byName,
|
||||||
prettyPrintUUIDs,
|
prettyPrintUUIDs,
|
||||||
remotesWithUUID,
|
remotesWithUUID,
|
||||||
|
@ -94,6 +95,18 @@ enabledRemoteList = filterM (repoNotIgnored . repo) =<< remoteList
|
||||||
remoteMap :: Annex (M.Map UUID String)
|
remoteMap :: Annex (M.Map UUID String)
|
||||||
remoteMap = M.fromList . map (\r -> (uuid r, name r)) <$> remoteList
|
remoteMap = M.fromList . map (\r -> (uuid r, name r)) <$> remoteList
|
||||||
|
|
||||||
|
{- Map of UUIDs and their descriptions.
|
||||||
|
- The names of Remotes are added to suppliment any description that has
|
||||||
|
- been set for a repository. -}
|
||||||
|
uuidDescriptions :: Annex (M.Map UUID String)
|
||||||
|
uuidDescriptions = M.unionWith addName <$> uuidMap <*> remoteMap
|
||||||
|
|
||||||
|
addName :: String -> String -> String
|
||||||
|
addName desc n
|
||||||
|
| desc == n = desc
|
||||||
|
| null desc = n
|
||||||
|
| otherwise = n ++ " (" ++ desc ++ ")"
|
||||||
|
|
||||||
{- When a name is specified, looks up the remote matching that name.
|
{- When a name is specified, looks up the remote matching that name.
|
||||||
- (Or it can be a UUID.) Only finds currently configured git remotes. -}
|
- (Or it can be a UUID.) Only finds currently configured git remotes. -}
|
||||||
byName :: Maybe String -> Annex (Maybe Remote)
|
byName :: Maybe String -> Annex (Maybe Remote)
|
||||||
|
@ -143,28 +156,24 @@ nameToUUID n = byName' n >>= go
|
||||||
prettyPrintUUIDs :: String -> [UUID] -> Annex String
|
prettyPrintUUIDs :: String -> [UUID] -> Annex String
|
||||||
prettyPrintUUIDs desc uuids = do
|
prettyPrintUUIDs desc uuids = do
|
||||||
hereu <- getUUID
|
hereu <- getUUID
|
||||||
m <- M.unionWith addname <$> uuidMap <*> remoteMap
|
m <- uuidDescriptions
|
||||||
maybeShowJSON [(desc, map (jsonify m hereu) uuids)]
|
maybeShowJSON [(desc, map (jsonify m hereu) uuids)]
|
||||||
return $ unwords $ map (\u -> "\t" ++ prettify m hereu u ++ "\n") uuids
|
return $ unwords $ map (\u -> "\t" ++ prettify m hereu u ++ "\n") uuids
|
||||||
where
|
where
|
||||||
addname d n
|
finddescription m u = M.findWithDefault "" u m
|
||||||
| d == n = d
|
|
||||||
| null d = n
|
|
||||||
| otherwise = n ++ " (" ++ d ++ ")"
|
|
||||||
findlog m u = M.findWithDefault "" u m
|
|
||||||
prettify m hereu u
|
prettify m hereu u
|
||||||
| not (null d) = fromUUID u ++ " -- " ++ d
|
| not (null d) = fromUUID u ++ " -- " ++ d
|
||||||
| otherwise = fromUUID u
|
| otherwise = fromUUID u
|
||||||
where
|
where
|
||||||
ishere = hereu == u
|
ishere = hereu == u
|
||||||
n = findlog m u
|
n = finddescription m u
|
||||||
d
|
d
|
||||||
| null n && ishere = "here"
|
| null n && ishere = "here"
|
||||||
| ishere = addname n "here"
|
| ishere = addName n "here"
|
||||||
| otherwise = n
|
| otherwise = n
|
||||||
jsonify m hereu u = toJSObject
|
jsonify m hereu u = toJSObject
|
||||||
[ ("uuid", toJSON $ fromUUID u)
|
[ ("uuid", toJSON $ fromUUID u)
|
||||||
, ("description", toJSON $ findlog m u)
|
, ("description", toJSON $ finddescription m u)
|
||||||
, ("here", toJSON $ hereu == u)
|
, ("here", toJSON $ hereu == u)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
3
Seek.hs
3
Seek.hs
|
@ -101,6 +101,9 @@ withField :: Option -> (Maybe String -> Annex a) -> (a -> CommandSeek) -> Comman
|
||||||
withField option converter = withValue $
|
withField option converter = withValue $
|
||||||
converter =<< Annex.getField (Option.name option)
|
converter =<< Annex.getField (Option.name option)
|
||||||
|
|
||||||
|
withFlag :: Option -> (Bool -> CommandSeek) -> CommandSeek
|
||||||
|
withFlag option = withValue $ Annex.getFlag (Option.name option)
|
||||||
|
|
||||||
withNothing :: CommandStart -> CommandSeek
|
withNothing :: CommandStart -> CommandSeek
|
||||||
withNothing a [] = return [a]
|
withNothing a [] = return [a]
|
||||||
withNothing _ _ = error "This command takes no parameters."
|
withNothing _ _ = error "This command takes no parameters."
|
||||||
|
|
6
debian/changelog
vendored
6
debian/changelog
vendored
|
@ -1,3 +1,9 @@
|
||||||
|
git-annex (3.20120107) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* log: Add --gource mode, which generates output usable by gource.
|
||||||
|
|
||||||
|
-- Joey Hess <joeyh@debian.org> Sat, 07 Jan 2012 18:12:09 -0400
|
||||||
|
|
||||||
git-annex (3.20120106) unstable; urgency=low
|
git-annex (3.20120106) unstable; urgency=low
|
||||||
|
|
||||||
* Support unescaped repository urls, like git does.
|
* Support unescaped repository urls, like git does.
|
||||||
|
|
|
@ -282,6 +282,9 @@ subdirectories).
|
||||||
--since, --after, --until, --before, and --max-count can be specified.
|
--since, --after, --until, --before, and --max-count can be specified.
|
||||||
They are passed through to git log. For example, --since "1 month ago"
|
They are passed through to git log. For example, --since "1 month ago"
|
||||||
|
|
||||||
|
To generate output suitable for the gource visualisation program,
|
||||||
|
specify --gource.
|
||||||
|
|
||||||
* status
|
* status
|
||||||
|
|
||||||
Displays some statistics and other information, including how much data
|
Displays some statistics and other information, including how much data
|
||||||
|
|
20
doc/tips/visualizing_repositories_with_gource.mdwn
Normal file
20
doc/tips/visualizing_repositories_with_gource.mdwn
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[Gource](http://code.google.com/p/gource/) is an amazing animated
|
||||||
|
visualisation of a git repository.
|
||||||
|
|
||||||
|
Normally, gource shows files being added, removed, and changed in
|
||||||
|
the repository, and the user(s) making the changes. Of course it can be
|
||||||
|
used in this way in a repository using git-annex too; just run `gource`.
|
||||||
|
|
||||||
|
The other way to use gource with git-annex is to visualise the movement of
|
||||||
|
annexed file contents between repositories. In this view, the "users" are
|
||||||
|
repositories, and they move around the file contents that are being added
|
||||||
|
or removed from them with git-annex.
|
||||||
|
|
||||||
|
To use gource this way, first go into the directory you want to visualize,
|
||||||
|
and use `git annex log` to make an input file for `gource`:
|
||||||
|
|
||||||
|
git annex log --gource | tee gorce.log
|
||||||
|
sort gource.log | gource --log-format custom -
|
||||||
|
|
||||||
|
The `git annex log` can take a while, to speed it up you can use something
|
||||||
|
like `--after "4 monts ago" to limit how far back it goes.
|
Loading…
Reference in a new issue