configuration to disable automatic merge conflict resolution

* Added annex.resolvemerge configuration, which can be set to false to
  disable the usual automatic merge conflict resolution done by git-annex
  sync and the assistant.
* sync: Added --no-resolvemerge option.

Note that disabling merge conflict resolution is probably not a good idea
in a direct mode repo or adjusted branch. Since updates to both are done
outside the usual work tree, if it fails the tree is not left in a
conflicted state, and it would be hard to manually resolve the conflict.
Still, made annex.resolvemerge be supported in those cases for consistency.

This commit was sponsored by Riku Voipio.
This commit is contained in:
Joey Hess 2017-06-01 12:46:36 -04:00
parent d5cb853dd0
commit 94351daba6
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
14 changed files with 89 additions and 29 deletions

View file

@ -318,8 +318,8 @@ findAdjustingCommit (AdjBranch b) = go =<< catCommit b
{- Update the currently checked out adjusted branch, merging the provided {- Update the currently checked out adjusted branch, merging the provided
- branch into it. Note that the provided branch should be a non-adjusted - branch into it. Note that the provided branch should be a non-adjusted
- branch. -} - branch. -}
updateAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Annex Bool updateAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> [Git.Merge.MergeConfig] -> Annex Bool -> Git.Branch.CommitMode -> Annex Bool
updateAdjustedBranch tomerge (origbranch, adj) mergeconfig commitmode = catchBoolIO $ updateAdjustedBranch tomerge (origbranch, adj) mergeconfig canresolvemerge commitmode = catchBoolIO $
join $ preventCommits go join $ preventCommits go
where where
adjbranch@(AdjBranch currbranch) = originalToAdjusted origbranch adj adjbranch@(AdjBranch currbranch) = originalToAdjusted origbranch adj
@ -417,7 +417,7 @@ updateAdjustedBranch tomerge (origbranch, adj) mergeconfig commitmode = catchBoo
-- this commit will be a fast-forward. -- this commit will be a fast-forward.
adjmergecommitff <- commitAdjustedTree' adjtree (BasisBranch mergecommit) [currbranch] adjmergecommitff <- commitAdjustedTree' adjtree (BasisBranch mergecommit) [currbranch]
showAction "Merging into adjusted branch" showAction "Merging into adjusted branch"
ifM (autoMergeFrom adjmergecommitff (Just currbranch) mergeconfig commitmode) ifM (autoMergeFrom adjmergecommitff (Just currbranch) mergeconfig canresolvemerge commitmode)
( reparent adjtree adjmergecommit =<< getcurrentcommit ( reparent adjtree adjmergecommit =<< getcurrentcommit
, return False , return False
) )

View file

@ -43,18 +43,18 @@ import qualified Data.ByteString.Lazy as L
- Callers should use Git.Branch.changed first, to make sure that - Callers should use Git.Branch.changed first, to make sure that
- there are changes from the current branch to the branch being merged in. - there are changes from the current branch to the branch being merged in.
-} -}
autoMergeFrom :: Git.Ref -> Maybe Git.Ref -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Annex Bool autoMergeFrom :: Git.Ref -> Maybe Git.Ref -> [Git.Merge.MergeConfig] -> Annex Bool -> Git.Branch.CommitMode -> Annex Bool
autoMergeFrom branch currbranch mergeconfig commitmode = do autoMergeFrom branch currbranch mergeconfig canresolvemerge commitmode = do
showOutput showOutput
case currbranch of case currbranch of
Nothing -> go Nothing Nothing -> go Nothing
Just b -> go =<< inRepo (Git.Ref.sha b) Just b -> go =<< inRepo (Git.Ref.sha b)
where where
go old = ifM isDirect go old = ifM isDirect
( mergeDirect currbranch old branch (resolveMerge old branch False) mergeconfig commitmode ( mergeDirect currbranch old branch resolvemerge mergeconfig commitmode
, do , do
r <- inRepo (Git.Merge.merge branch mergeconfig commitmode) r <- inRepo (Git.Merge.merge branch mergeconfig commitmode)
<||> (resolveMerge old branch False <&&> commitResolvedMerge commitmode) <||> (resolvemerge <&&> commitResolvedMerge commitmode)
-- Merging can cause new associated files to appear -- Merging can cause new associated files to appear
-- and the smudge filter will add them to the database. -- and the smudge filter will add them to the database.
-- To ensure that this process sees those changes, -- To ensure that this process sees those changes,
@ -62,6 +62,11 @@ autoMergeFrom branch currbranch mergeconfig commitmode = do
Database.Keys.closeDb Database.Keys.closeDb
return r return r
) )
where
resolvemerge = ifM canresolvemerge
( resolveMerge old branch False
, return False
)
{- Resolves a conflicted merge. It's important that any conflicts be {- Resolves a conflicted merge. It's important that any conflicts be
- resolved in a way that itself avoids later merge conflicts, since - resolved in a way that itself avoids later merge conflicts, since

View file

@ -211,7 +211,8 @@ manualPull currentbranch remotes = do
else return Nothing else return Nothing
haddiverged <- liftAnnex Annex.Branch.forceUpdate haddiverged <- liftAnnex Annex.Branch.forceUpdate
forM_ normalremotes $ \r -> forM_ normalremotes $ \r ->
liftAnnex $ Command.Sync.mergeRemote r currentbranch Command.Sync.mergeConfig liftAnnex $ Command.Sync.mergeRemote r
currentbranch Command.Sync.mergeConfig def
return (catMaybes failed, haddiverged) return (catMaybes failed, haddiverged)
where where
wantpull gc = remoteAnnexPull gc wantpull gc = remoteAnnexPull gc

View file

@ -78,6 +78,7 @@ onChange file
] ]
void $ liftAnnex $ Command.Sync.merge void $ liftAnnex $ Command.Sync.merge
currbranch Command.Sync.mergeConfig currbranch Command.Sync.mergeConfig
def
Git.Branch.AutomaticCommit Git.Branch.AutomaticCommit
changedbranch changedbranch
mergecurrent _ = noop mergecurrent _ = noop

View file

@ -12,6 +12,10 @@ git-annex (6.20170520) UNRELEASED; urgency=medium
* metadata: When setting metadata of a file that did not exist, * metadata: When setting metadata of a file that did not exist,
no error message was displayed, unlike getting metadata and most other no error message was displayed, unlike getting metadata and most other
git-annex commands. Fixed this oversight. git-annex commands. Fixed this oversight.
* Added annex.resolvemerge configuration, which can be set to false to
disable the usual automatic merge conflict resolution done by git-annex
sync and the assistant.
* sync: Added --no-resolvemerge option.
-- Joey Hess <id@joeyh.name> Wed, 24 May 2017 14:03:40 -0400 -- Joey Hess <id@joeyh.name> Wed, 24 May 2017 14:03:40 -0400

View file

@ -33,4 +33,4 @@ mergeBranch = do
mergeSynced :: CommandStart mergeSynced :: CommandStart
mergeSynced = do mergeSynced = do
prepMerge prepMerge
mergeLocal mergeConfig =<< join getCurrBranch mergeLocal mergeConfig def =<< join getCurrBranch

View file

@ -48,4 +48,4 @@ fixPostReceiveHookEnv = do
updateInsteadEmulation :: CommandStart updateInsteadEmulation :: CommandStart
updateInsteadEmulation = do updateInsteadEmulation = do
prepMerge prepMerge
mergeLocal mergeConfig =<< join getCurrBranch mergeLocal mergeConfig def =<< join getCurrBranch

View file

@ -76,8 +76,14 @@ data SyncOptions = SyncOptions
, noContentOption :: Bool , noContentOption :: Bool
, contentOfOption :: [FilePath] , contentOfOption :: [FilePath]
, keyOptions :: Maybe KeyOptions , keyOptions :: Maybe KeyOptions
, resolveMergeOverride :: ResolveMergeOverride
} }
newtype ResolveMergeOverride = ResolveMergeOverride Bool
instance Default ResolveMergeOverride where
def = ResolveMergeOverride False
optParser :: CmdParamsDesc -> Parser SyncOptions optParser :: CmdParamsDesc -> Parser SyncOptions
optParser desc = SyncOptions optParser desc = SyncOptions
<$> (many $ argument str <$> (many $ argument str
@ -117,6 +123,9 @@ optParser desc = SyncOptions
<> metavar paramPath <> metavar paramPath
)) ))
<*> optional parseAllOption <*> optional parseAllOption
<*> (ResolveMergeOverride <$> invertableSwitch "resolvemerge" True
( help "do not automatically resolve merge conflicts"
))
-- Since prepMerge changes the working directory, FilePath options -- Since prepMerge changes the working directory, FilePath options
-- have to be adjusted. -- have to be adjusted.
@ -132,6 +141,7 @@ instance DeferredParseClass SyncOptions where
<*> pure (noContentOption v) <*> pure (noContentOption v)
<*> liftIO (mapM absPath (contentOfOption v)) <*> liftIO (mapM absPath (contentOfOption v))
<*> pure (keyOptions v) <*> pure (keyOptions v)
<*> pure (resolveMergeOverride v)
seek :: SyncOptions -> CommandSeek seek :: SyncOptions -> CommandSeek
seek o = allowConcurrentOutput $ do seek o = allowConcurrentOutput $ do
@ -150,7 +160,7 @@ seek o = allowConcurrentOutput $ do
-- These actions cannot be run concurrently. -- These actions cannot be run concurrently.
mapM_ includeCommandAction $ concat mapM_ includeCommandAction $ concat
[ [ commit o ] [ [ commit o ]
, [ withbranch (mergeLocal mergeConfig) ] , [ withbranch (mergeLocal mergeConfig (resolveMergeOverride o)) ]
, map (withbranch . pullRemote o mergeConfig) gitremotes , map (withbranch . pullRemote o mergeConfig) gitremotes
, [ mergeAnnex ] , [ mergeAnnex ]
] ]
@ -219,11 +229,14 @@ mergeConfig =
, Git.Merge.MergeUnrelatedHistories , Git.Merge.MergeUnrelatedHistories
] ]
merge :: CurrBranch -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool merge :: CurrBranch -> [Git.Merge.MergeConfig] -> ResolveMergeOverride -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool
merge (Just b, Just adj) mergeconfig commitmode tomerge = merge currbranch mergeconfig resolvemergeoverride commitmode tomerge = case currbranch of
updateAdjustedBranch tomerge (b, adj) mergeconfig commitmode (Just b, Just adj) -> updateAdjustedBranch tomerge (b, adj) mergeconfig canresolvemerge commitmode
merge (b, _) mergeconfig commitmode tomerge = (b, _) -> autoMergeFrom tomerge b mergeconfig canresolvemerge commitmode
autoMergeFrom tomerge b mergeconfig commitmode where
canresolvemerge = case resolvemergeoverride of
ResolveMergeOverride True -> getGitConfigVal annexResolveMerge
ResolveMergeOverride False -> return False
syncBranch :: Git.Branch -> Git.Branch syncBranch :: Git.Branch -> Git.Branch
syncBranch = Git.Ref.underBase "refs/heads/synced" . fromDirectBranch . fromAdjustedBranch syncBranch = Git.Ref.underBase "refs/heads/synced" . fromDirectBranch . fromAdjustedBranch
@ -296,15 +309,15 @@ commitStaged commitmode commitmessage = do
void $ inRepo $ Git.Branch.commit commitmode False commitmessage branch parents void $ inRepo $ Git.Branch.commit commitmode False commitmessage branch parents
return True return True
mergeLocal :: [Git.Merge.MergeConfig] -> CurrBranch -> CommandStart mergeLocal :: [Git.Merge.MergeConfig] -> ResolveMergeOverride -> CurrBranch -> CommandStart
mergeLocal mergeconfig currbranch@(Just _, _) = mergeLocal mergeconfig resolvemergeoverride currbranch@(Just _, _) =
go =<< needMerge currbranch go =<< needMerge currbranch
where where
go Nothing = stop go Nothing = stop
go (Just syncbranch) = do go (Just syncbranch) = do
showStart "merge" $ Git.Ref.describe syncbranch showStart "merge" $ Git.Ref.describe syncbranch
next $ next $ merge currbranch mergeconfig Git.Branch.ManualCommit syncbranch next $ next $ merge currbranch mergeconfig resolvemergeoverride Git.Branch.ManualCommit syncbranch
mergeLocal _ (Nothing, madj) = do mergeLocal _ _ (Nothing, madj) = do
b <- inRepo Git.Branch.currentUnsafe b <- inRepo Git.Branch.currentUnsafe
ifM (isJust <$> needMerge (b, madj)) ifM (isJust <$> needMerge (b, madj))
( do ( do
@ -365,7 +378,7 @@ pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && want
next $ do next $ do
showOutput showOutput
stopUnless fetch $ stopUnless fetch $
next $ mergeRemote remote branch mergeconfig next $ mergeRemote remote branch mergeconfig (resolveMergeOverride o)
where where
fetch = inRepoWithSshOptionsTo (Remote.repo remote) (Remote.gitconfig remote) $ fetch = inRepoWithSshOptionsTo (Remote.repo remote) (Remote.gitconfig remote) $
Git.Command.runBool Git.Command.runBool
@ -377,8 +390,8 @@ pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && want
- were committed (or pushed changes, if this is a bare remote), - were committed (or pushed changes, if this is a bare remote),
- while the synced/master may have changes that some - while the synced/master may have changes that some
- other remote synced to this remote. So, merge them both. -} - other remote synced to this remote. So, merge them both. -}
mergeRemote :: Remote -> CurrBranch -> [Git.Merge.MergeConfig] -> CommandCleanup mergeRemote :: Remote -> CurrBranch -> [Git.Merge.MergeConfig] -> ResolveMergeOverride -> CommandCleanup
mergeRemote remote currbranch mergeconfig = ifM isBareRepo mergeRemote remote currbranch mergeconfig resolvemergeoverride = ifM isBareRepo
( return True ( return True
, case currbranch of , case currbranch of
(Nothing, _) -> do (Nothing, _) -> do
@ -390,7 +403,7 @@ mergeRemote remote currbranch mergeconfig = ifM isBareRepo
) )
where where
mergelisted getlist = and <$> mergelisted getlist = and <$>
(mapM (merge currbranch mergeconfig Git.Branch.ManualCommit . remoteBranch remote) =<< getlist) (mapM (merge currbranch mergeconfig resolvemergeoverride Git.Branch.ManualCommit . remoteBranch remote) =<< getlist)
tomerge = filterM (changed remote) tomerge = filterM (changed remote)
branchlist Nothing = [] branchlist Nothing = []
branchlist (Just branch) = [branch, syncBranch branch] branchlist (Just branch) = [branch, syncBranch branch]

View file

@ -58,6 +58,7 @@ data GitConfig = GitConfig
, annexHttpHeaders :: [String] , annexHttpHeaders :: [String]
, annexHttpHeadersCommand :: Maybe String , annexHttpHeadersCommand :: Maybe String
, annexAutoCommit :: Configurable Bool , annexAutoCommit :: Configurable Bool
, annexResolveMerge :: Configurable Bool
, annexSyncContent :: Configurable Bool , annexSyncContent :: Configurable Bool
, annexDebug :: Bool , annexDebug :: Bool
, annexWebOptions :: [String] , annexWebOptions :: [String]
@ -115,6 +116,8 @@ extractGitConfig r = GitConfig
, annexHttpHeadersCommand = getmaybe (annex "http-headers-command") , annexHttpHeadersCommand = getmaybe (annex "http-headers-command")
, annexAutoCommit = configurable True $ , annexAutoCommit = configurable True $
getmaybebool (annex "autocommit") getmaybebool (annex "autocommit")
, annexResolveMerge = configurable True $
getmaybebool (annex "resolvemerge")
, annexSyncContent = configurable False $ , annexSyncContent = configurable False $
getmaybebool (annex "synccontent") getmaybebool (annex "synccontent")
, annexDebug = getbool (annex "debug") False , annexDebug = getbool (annex "debug") False

View file

@ -0,0 +1,8 @@
[[!comment format=mdwn
username="joey"
subject="""comment 2"""
date="2017-06-01T16:16:44Z"
content="""
I've implemented the annex.automerge configuration setting, for the next
release. There's also a `git annex sync --no-resolvemerge`
"""]]

View file

@ -32,6 +32,12 @@ These settings can be overridden on a per-repository basis using
Set to false to prevent the git-annex assistant and git-annex sync Set to false to prevent the git-annex assistant and git-annex sync
from automatically committing changes to files in the repository. from automatically committing changes to files in the repository.
* `annex.resolvemerge`
Set to false to prevent merge conflicts being automatically resolved
by the git-annex assitant, git-annex sync, git-annex merge,
and the git-annex post-receive hook.
* `annex.synccontent` * `annex.synccontent`
Set to true to make git-annex sync default to syncing content. Set to true to make git-annex sync default to syncing content.

View file

@ -12,6 +12,9 @@ This performs the same merging (and merge conflict resolution)
that is done by the sync command, but without pushing or pulling any that is done by the sync command, but without pushing or pulling any
data. data.
When annex.resolvemerge is set to false, merge conflict resolution
will not be done.
# SEE ALSO # SEE ALSO
[[git-annex]](1) [[git-annex]](1)

View file

@ -21,11 +21,6 @@ worry about the details, you can use sync.
The content of annexed objects is not synced by default, but the --content The content of annexed objects is not synced by default, but the --content
option (see below) can make that also be synchronized. option (see below) can make that also be synchronized.
Merge conflicts are automatically handled by sync. When two conflicting
versions of a file have been committed, both will be added to the tree,
under different filenames. For example, file "foo" would be replaced
with "foo.somekey" and "foo.otherkey".
Note that syncing with a remote will not normally update the remote's working Note that syncing with a remote will not normally update the remote's working
tree with changes made to the local repository. (Unless it's configured tree with changes made to the local repository. (Unless it's configured
with receive.denyCurrentBranch=updateInstead.) However, those changes with receive.denyCurrentBranch=updateInstead.) However, those changes
@ -114,6 +109,18 @@ by running "git annex sync" on the remote.
less efficient. When --content is synced, the files are processed less efficient. When --content is synced, the files are processed
in parallel as well. in parallel as well.
* `--resolvemerge`, `--no-resolvemerge`
By default, merge conflicts are automatically handled by sync. When two
conflicting versions of a file have been committed, both will be added
to the tree, under different filenames. For example, file "foo"
would be replaced with "foo.variant-A" and "foo.variant-B". (See
[[git-annex-resolvemerge]](1) for details.)
Use `--no-resolvemerge` to disable this automatic merge conflict
resolution. It can also be disabled by setting annex.resolvemerge
to false.
# SEE ALSO # SEE ALSO
[[git-annex]](1) [[git-annex]](1)

View file

@ -1040,6 +1040,15 @@ Here are all the supported configuration settings.
To configure the behavior in all clones of the repository, To configure the behavior in all clones of the repository,
this can be set in [[git-annex-config]]. this can be set in [[git-annex-config]].
* `annex.resolvemerge`
Set to false to prevent merge conflicts being automatically resolved
by the git-annex assitant, git-annex sync, git-annex merge,
and the git-annex post-receive hook.
To configure the behavior in all clones of the repository,
this can be set in [[git-annex-config]].
* `annex.synccontent` * `annex.synccontent`
Set to true to make git-annex sync default to syncing content. Set to true to make git-annex sync default to syncing content.