added git-annex satisfy

This ended up having an interface like sync, rather than like get/copy/drop.
That let it be implemented in terms of sync, which took a lot less code.
Also, it lets it handle many of the edge cases that sync does, such as
getting files that are not visible in a --hide-missing branch, and sending
files to exporttree remotes.

As well as being easier to implement, `git-annex satisfy myremote` makes
sense as it satisfies the preferred content settings of the remote.
`git-annex satisfy somefile` does not form a sentence that makes sense. So
while -C can be a little bit annoying, it still makes sense to have this
syntax.

Note that, while I initially thought this would also satisfy numcopies, it
does not. Arguably it ought to. But, sync does not send files in order to
satisfy numcopies, it only sends files to satisfy preferred content. And
it's important that this transfer the same files as sync does, because
it will probably be used in a workflow where the user sometimes syncs and
sometimes satisfies, and does not expect satisfy to do things that sync
would not do.

(Also opened a new bug that also affects sync et all, not only this command.)

Sponsored-by: Nicholas Golder-Manning on Patreon
This commit is contained in:
Joey Hess 2023-06-29 15:34:53 -04:00
parent 1b9958f4fd
commit e1fc9e204e
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
10 changed files with 129 additions and 97 deletions

View file

@ -1,9 +1,8 @@
git-annex (10.20230627) UNRELEASED; urgency=medium git-annex (10.20230627) UNRELEASED; urgency=medium
* satify: New command that gets/sends/drops content to satisfy * satify: New command that gets/sends/drops content to satisfy
numcopies and preferred content settings. This is similar to preferred content settings. This is like to the --content
the --content part of git-annex sync, but with an interface part of git-annex sync.
more like get/move/drop.
* reinject: Added --guesskeys option. * reinject: Added --guesskeys option.
* diffdriver: Added --text option for easy diffing of the contents of * diffdriver: Added --text option for easy diffing of the contents of
annexed text files. annexed text files.

View file

@ -103,6 +103,7 @@ import qualified Command.Sync
import qualified Command.Assist import qualified Command.Assist
import qualified Command.Pull import qualified Command.Pull
import qualified Command.Push import qualified Command.Push
import qualified Command.Satisfy
import qualified Command.Mirror import qualified Command.Mirror
import qualified Command.AddUrl import qualified Command.AddUrl
import qualified Command.ImportFeed import qualified Command.ImportFeed
@ -151,6 +152,7 @@ cmds testoptparser testrunner mkbenchmarkgenerator = map addGitAnnexCommonOption
, Command.Assist.cmd , Command.Assist.cmd
, Command.Pull.cmd , Command.Pull.cmd
, Command.Push.cmd , Command.Push.cmd
, Command.Satisfy.cmd
, Command.Mirror.cmd , Command.Mirror.cmd
, Command.AddUrl.cmd , Command.AddUrl.cmd
, Command.ImportFeed.cmd , Command.ImportFeed.cmd

17
Command/Satisfy.hs Normal file
View file

@ -0,0 +1,17 @@
{- git-annex command
-
- Copyright 2023 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
module Command.Satisfy (cmd) where
import Command
import Command.Sync hiding (cmd)
cmd :: Command
cmd = withAnnexOptions [jobsOption, backendOption] $
command "satisfy" SectionCommon
"transfer and drop content as configured"
(paramRepeating paramRemote) (seek <--< optParser SatisfyMode)

View file

@ -95,7 +95,7 @@ cmd = withAnnexOptions [jobsOption, backendOption] $
"synchronize local repository with remotes" "synchronize local repository with remotes"
(paramRepeating paramRemote) (seek <--< optParser SyncMode) (paramRepeating paramRemote) (seek <--< optParser SyncMode)
data OperationMode = SyncMode | PullMode | PushMode | AssistMode data OperationMode = SyncMode | PullMode | PushMode | SatisfyMode | AssistMode
deriving (Eq, Show) deriving (Eq, Show)
data SyncOptions = SyncOptions data SyncOptions = SyncOptions
@ -143,15 +143,17 @@ optParser mode desc = SyncOptions
( metavar desc ( metavar desc
<> completeRemotes <> completeRemotes
)) ))
<*> switch <*> whenmode [SatisfyMode] True
( long "only-annex" (switch
<> short 'a' ( long "only-annex"
<> help "do not operate on git branches" <> short 'a'
) <> help "do not operate on git branches"
<*> switch ))
( long "not-only-annex" <*> whenmode [SatisfyMode] False
<> help "operate on git branches as well as annex" ( switch
) ( long "not-only-annex"
<> help "operate on git branches as well as annex"
))
<*> case mode of <*> case mode of
SyncMode -> switch SyncMode -> switch
( long "commit" ( long "commit"
@ -159,21 +161,25 @@ optParser mode desc = SyncOptions
) )
PushMode -> pure False PushMode -> pure False
PullMode -> pure False PullMode -> pure False
SatisfyMode -> pure False
AssistMode -> pure True AssistMode -> pure True
<*> unlessmode [SyncMode] True (switch <*> unlessmode [SyncMode] True
( long "no-commit" (switch
<> help "avoid git commit" ( long "no-commit"
)) <> help "avoid git commit"
<*> unlessmode [SyncMode, AssistMode] Nothing (optional (strOption ))
( long "message" <> short 'm' <> metavar "MSG" <*> unlessmode [SyncMode, AssistMode] Nothing
<> help "commit message" (optional (strOption
))) ( long "message" <> short 'm' <> metavar "MSG"
<> help "commit message"
)))
<*> case mode of <*> case mode of
SyncMode -> invertableSwitch "pull" True SyncMode -> invertableSwitch "pull" True
( help "avoid git pulls from remotes" ( help "avoid git pulls from remotes"
) )
PullMode -> pure True PullMode -> pure True
PushMode -> pure False PushMode -> pure False
SatisfyMode -> pure False
AssistMode -> pure True AssistMode -> pure True
<*> case mode of <*> case mode of
SyncMode -> invertableSwitch "push" True SyncMode -> invertableSwitch "push" True
@ -181,31 +187,36 @@ optParser mode desc = SyncOptions
) )
PullMode -> pure False PullMode -> pure False
PushMode -> pure True PushMode -> pure True
SatisfyMode -> pure False
AssistMode -> pure True AssistMode -> pure True
<*> optional (flag' True <*> whenmode [SatisfyMode] (Just True)
( long "content" (optional (flag' True
<> help "transfer annexed file contents" ( long "content"
)) <> help "transfer annexed file contents"
<*> optional (flag' True )))
( long "no-content" <*> whenmode [SatisfyMode] Nothing
<> short 'g' (optional (flag' True
<> help "do not transfer annexed file contents" ( long "no-content"
)) <> short 'g'
<> help "do not transfer annexed file contents"
)))
<*> many (strOption <*> many (strOption
( long "content-of" ( long "content-of"
<> short 'C' <> short 'C'
<> help "transfer contents of annexed files in a given location" <> help "transfer contents of annexed files in a given location"
<> metavar paramPath <> metavar paramPath
)) ))
<*> whenmode [PullMode] False (switch <*> whenmode [PullMode, SatisfyMode] False
( long "cleanup" (switch
<> help "remove synced/ branches" ( long "cleanup"
)) <> help "remove synced/ branches"
))
<*> optional parseAllOption <*> optional parseAllOption
<*> whenmode [PushMode] False (invertableSwitch "resolvemerge" True <*> whenmode [PushMode, SatisfyMode] False
( help "do not automatically resolve merge conflicts" (invertableSwitch "resolvemerge" True
)) ( help "do not automatically resolve merge conflicts"
<*> whenmode [PushMode] False ))
<*> whenmode [PushMode, SatisfyMode] False
parseUnrelatedHistoriesOption parseUnrelatedHistoriesOption
<*> pure mode <*> pure mode
where where
@ -838,6 +849,7 @@ seekSyncContent o rs currbranch = do
SyncMode -> "sync" SyncMode -> "sync"
PullMode -> "pull" PullMode -> "pull"
PushMode -> "push" PushMode -> "push"
SatisfyMode -> "satisfy"
AssistMode -> "assist" AssistMode -> "assist"
gofile bloom mvar _ f k = gofile bloom mvar _ f k =
@ -910,7 +922,7 @@ syncFile o ebloom rs af k = do
return (got || not (null putrs)) return (got || not (null putrs))
where where
wantget have inhere = allM id wantget have inhere = allM id
[ pure (pullOption o) [ pure (pullOption o || operationMode o == SatisfyMode)
, pure (not $ null have) , pure (not $ null have)
, pure (not inhere) , pure (not inhere)
, wantGet True (Just k) af , wantGet True (Just k) af
@ -924,7 +936,7 @@ syncFile o ebloom rs af k = do
next $ return True next $ return True
wantput r wantput r
| pushOption o == False = return False | pushOption o == False && operationMode o /= SatisfyMode = return False
| Remote.readonly r || remoteAnnexReadOnly (Remote.gitconfig r) = return False | Remote.readonly r || remoteAnnexReadOnly (Remote.gitconfig r) = return False
| isExport r = return False | isExport r = return False
| isThirdPartyPopulated r = return False | isThirdPartyPopulated r = return False
@ -949,6 +961,7 @@ syncFile o ebloom rs af k = do
- of the branch. (If the branch is not currently checked out, anything - of the branch. (If the branch is not currently checked out, anything
- imported from the remote will not yet have been merged into it yet and - imported from the remote will not yet have been merged into it yet and
- so exporting would delete files from the remote unexpectedly.) - so exporting would delete files from the remote unexpectedly.)
- (This is not done in SatifyMode.)
- -
- Otherwise, transfer any files that were part of a previous export - Otherwise, transfer any files that were part of a previous export
- but are not in the remote yet. - but are not in the remote yet.
@ -959,12 +972,14 @@ seekExportContent :: Maybe SyncOptions -> [Remote] -> CurrBranch -> Annex Bool
seekExportContent o rs (currbranch, _) = or <$> forM rs go seekExportContent o rs (currbranch, _) = or <$> forM rs go
where where
go r go r
| maybe False (\o' -> operationMode o' == SatisfyMode) o =
case remoteAnnexTrackingBranch (Remote.gitconfig r) of
Nothing -> return False
Just b -> withdb r $ \db ->
cannotupdateexport r db (Just b) False
| not (maybe True pushOption o) = return False | not (maybe True pushOption o) = return False
| not (remoteAnnexPush (Remote.gitconfig r)) = return False | not (remoteAnnexPush (Remote.gitconfig r)) = return False
| otherwise = bracket | otherwise = withdb r (go' r)
(Export.openDb (Remote.uuid r))
Export.closeDb
(\db -> Export.writeLockDbWhile db (go' r db))
go' r db = case remoteAnnexTrackingBranch (Remote.gitconfig r) of go' r db = case remoteAnnexTrackingBranch (Remote.gitconfig r) of
Nothing -> cannotupdateexport r db Nothing True Nothing -> cannotupdateexport r db Nothing True
Just b -> do Just b -> do
@ -986,6 +1001,11 @@ seekExportContent o rs (currbranch, _) = or <$> forM rs go
| otherwise -> cannotupdateexport r db (Just b) False | otherwise -> cannotupdateexport r db (Just b) False
_ -> cannotupdateexport r db (Just b) True _ -> cannotupdateexport r db (Just b) True
withdb r a = bracket
(Export.openDb (Remote.uuid r))
Export.closeDb
(\db -> Export.writeLockDbWhile db (a db))
cannotupdateexport r db mtb showwarning = do cannotupdateexport r db mtb showwarning = do
exported <- getExport (Remote.uuid r) exported <- getExport (Remote.uuid r)
when showwarning $ when showwarning $
@ -1056,6 +1076,7 @@ cleanupRemote remote (Just b, _) =
shouldSyncContent :: SyncOptions -> Annex Bool shouldSyncContent :: SyncOptions -> Annex Bool
shouldSyncContent o shouldSyncContent o
| fromMaybe False (noContentOption o) = pure False | fromMaybe False (noContentOption o) = pure False
| operationMode o == SatisfyMode = pure True
-- For git-annex pull and git-annex push and git-annex assist, -- For git-annex pull and git-annex push and git-annex assist,
-- annex.syncontent defaults to True unless set -- annex.syncontent defaults to True unless set
| operationMode o /= SyncMode = annexsynccontent True | operationMode o /= SyncMode = annexsynccontent True

View file

@ -0,0 +1,14 @@
When an exporttree=yes remote has had a tree exported to it, but eg with
`git-annex export master --to foo --fast`, so not all the files have been
sent to it yet, `git-annex sync -Cfoo` will also export files in other
directories to it.
This seems like a bug, it ought to only export any files in the specified
directory.
Another way this can happen is `git-annex sync -Cfoo`, when file bar
has changed. Currently this exports bar to the export remote, but does
not send it to any other remotes, since it's not in directory foo.
Also this affects `git-annex satisfy`, and other related commands too.
--[[Joey]]

View file

@ -33,7 +33,7 @@ to the parent branch or metadata.
Normally this tries to download the content of each annexed file, Normally this tries to download the content of each annexed file,
from any remote that it's pulling from that has a copy. from any remote that it's pulling from that has a copy.
To control which files it downloads, configure the preferred To control which files it downloads, configure the preferred
content of the local repository.It will also drop files from a content of the local repository. It will also drop files from a
remote that are not preferred content of the remote. remote that are not preferred content of the remote.
See [[git-annex-preferred-content]](1). See [[git-annex-preferred-content]](1).

View file

@ -4,21 +4,34 @@ git-annex satisfy - transfer and drop content as configured
# SYNOPSIS # SYNOPSIS
git annex satisffy `[path ...]` git annex satisfy `[remote ...]`
# DESCRIPTION # DESCRIPTION
This transfers and drops content to satisfy the numcopies and preferred This transfers and drops content of annexed files to work toward satisfying
content settings of the local repository and remotes. the preferred content settings of the local repository and remotes.
It does the same thing as `git-annex sync --content` without the pulling It does the same thing as `git-annex sync --content` without the pulling
and pushing of git repositories. and pushing of git repositories, and without changing the trees that are
imported to or exported from special remotes.
By default it operates on all annexed files in the current directory and
its subdirectories. Paths of files or directories can also be specified.
# OPTIONS # OPTIONS
* `[remote]`
By default this command operates on all remotes, except for remotes
that have `remote.<name>.annex-sync` set to false.
By specifying the names of remotes (or remote groups), you can control
which ones to operate on.
* `--content-of=path` `-C path`
Operate on only files in the specified path. The default is to operate on
all files in the working tree.
This option can be repeated multiple times with different paths.
* `--jobs=N` `-JN` * `--jobs=N` `-JN`
Enables parallel processing with up to the specified number of jobs Enables parallel processing with up to the specified number of jobs
@ -28,47 +41,13 @@ its subdirectories. Paths of files or directories can also be specified.
* `--all` `-A` * `--all` `-A`
Operate on all objects stored in the git annex, not only objects used by Usually this command operates on annexed files in the current branch.
currently existing files. This option makes it operate on all available versions of all annexed files
(when preferred content settings allow).
However, this bypasses checking the .gitattributes annex.numcopies
setting when dropping files.
This is the default behavior when running git-annex in a bare repository. Note that preferred content settings that use `include=` or `exclude=`
will only match the version of files currently in the work tree, but not
* `--branch=ref` past versions of files.
Operate on files in the specified branch or treeish.
Like --all, this bypasses checking the .gitattributes annex.numcopies
setting when dropping files.
* `--unused`
Operate on files found by last run of git-annex unused.
* `--failed`
Operate on files that have recently failed to be transferred.
* matching options
The [[git-annex-matching-options]](1)
can be used to control what files to operate on.
* `--json`
Enable JSON output. This is intended to be parsed by programs that use
git-annex. Each line of output is a JSON object.
* `--json-progress`
Include progress objects in JSON output.
* `--json-error-messages`
Messages that would normally be output to standard error are included in
the JSON instead.
* Also the [[git-annex-common-options]](1) can be used. * Also the [[git-annex-common-options]](1) can be used.
@ -78,8 +57,6 @@ its subdirectories. Paths of files or directories can also be specified.
[[git-annex-sync]](1) [[git-annex-sync]](1)
[[git-annex-preferred-numcopies]](1)
[[git-annex-preferred-content]](1) [[git-annex-preferred-content]](1)
# AUTHOR # AUTHOR

View file

@ -138,10 +138,9 @@ content from the key-value store.
See [[git-annex-assist]](1) for details. See [[git-annex-assist]](1) for details.
* `satisfy [path ...]` * `satisfy [remote ...]`
Satisfy numcopies and preferred content settings by transferring Satisfy preferred content settings by transferring and dropping content.
and dropping content.
See [[git-annex-satisfy]](1) for details. See [[git-annex-satisfy]](1) for details.

View file

@ -19,3 +19,5 @@ What would be a good name for such a command? --[[Joey]]
> How about `git-annex port` as it carries content over like a luggage carrier aka. porter? > How about `git-annex port` as it carries content over like a luggage carrier aka. porter?
> Another one that comes to mind is `git-annex shuttle` but that could be semantically too > Another one that comes to mind is `git-annex shuttle` but that could be semantically too
> similar to `git-annex assist`, perhaps. --[[jkniiv]] > similar to `git-annex assist`, perhaps. --[[jkniiv]]
>> `git-annex satisfy` [[done]] --[[Joey]]

View file

@ -790,6 +790,7 @@ Executable git-annex
Command.ResolveMerge Command.ResolveMerge
Command.Restage Command.Restage
Command.RmUrl Command.RmUrl
Command.Satisfy
Command.Schedule Command.Schedule
Command.Semitrust Command.Semitrust
Command.SendKey Command.SendKey