From de8f6959d3cd4af348e7a72c45e6e1d6d3cd4cfa Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 1 Jun 2017 12:46:36 -0400 Subject: 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. --- Annex/AdjustedBranch.hs | 6 ++-- Annex/AutoMerge.hs | 13 ++++--- Assistant/Sync.hs | 3 +- Assistant/Threads/Merger.hs | 1 + CHANGELOG | 4 +++ Command/Merge.hs | 2 +- Command/PostReceive.hs | 2 +- Command/Sync.hs | 41 ++++++++++++++-------- Types/GitConfig.hs | 3 ++ ...ent_2_c091e5a668b15aca7b2faa955beab403._comment | 8 +++++ doc/git-annex-config.mdwn | 6 ++++ doc/git-annex-merge.mdwn | 3 ++ doc/git-annex-sync.mdwn | 17 ++++++--- doc/git-annex.mdwn | 9 +++++ 14 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 doc/forum/controlling_how___34__git_annex_sync__34___resolves_conflicts/comment_2_c091e5a668b15aca7b2faa955beab403._comment diff --git a/Annex/AdjustedBranch.hs b/Annex/AdjustedBranch.hs index c0225540a..52f73e638 100644 --- a/Annex/AdjustedBranch.hs +++ b/Annex/AdjustedBranch.hs @@ -318,8 +318,8 @@ findAdjustingCommit (AdjBranch b) = go =<< catCommit b {- Update the currently checked out adjusted branch, merging the provided - branch into it. Note that the provided branch should be a non-adjusted - branch. -} -updateAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Annex Bool -updateAdjustedBranch tomerge (origbranch, adj) mergeconfig commitmode = catchBoolIO $ +updateAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> [Git.Merge.MergeConfig] -> Annex Bool -> Git.Branch.CommitMode -> Annex Bool +updateAdjustedBranch tomerge (origbranch, adj) mergeconfig canresolvemerge commitmode = catchBoolIO $ join $ preventCommits go where adjbranch@(AdjBranch currbranch) = originalToAdjusted origbranch adj @@ -417,7 +417,7 @@ updateAdjustedBranch tomerge (origbranch, adj) mergeconfig commitmode = catchBoo -- this commit will be a fast-forward. adjmergecommitff <- commitAdjustedTree' adjtree (BasisBranch mergecommit) [currbranch] showAction "Merging into adjusted branch" - ifM (autoMergeFrom adjmergecommitff (Just currbranch) mergeconfig commitmode) + ifM (autoMergeFrom adjmergecommitff (Just currbranch) mergeconfig canresolvemerge commitmode) ( reparent adjtree adjmergecommit =<< getcurrentcommit , return False ) diff --git a/Annex/AutoMerge.hs b/Annex/AutoMerge.hs index 7f982e515..91f8fc99e 100644 --- a/Annex/AutoMerge.hs +++ b/Annex/AutoMerge.hs @@ -43,18 +43,18 @@ import qualified Data.ByteString.Lazy as L - Callers should use Git.Branch.changed first, to make sure that - 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 branch currbranch mergeconfig commitmode = do +autoMergeFrom :: Git.Ref -> Maybe Git.Ref -> [Git.Merge.MergeConfig] -> Annex Bool -> Git.Branch.CommitMode -> Annex Bool +autoMergeFrom branch currbranch mergeconfig canresolvemerge commitmode = do showOutput case currbranch of Nothing -> go Nothing Just b -> go =<< inRepo (Git.Ref.sha b) where go old = ifM isDirect - ( mergeDirect currbranch old branch (resolveMerge old branch False) mergeconfig commitmode + ( mergeDirect currbranch old branch resolvemerge mergeconfig commitmode , do 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 -- and the smudge filter will add them to the database. -- To ensure that this process sees those changes, @@ -62,6 +62,11 @@ autoMergeFrom branch currbranch mergeconfig commitmode = do Database.Keys.closeDb return r ) + where + resolvemerge = ifM canresolvemerge + ( resolveMerge old branch False + , return False + ) {- Resolves a conflicted merge. It's important that any conflicts be - resolved in a way that itself avoids later merge conflicts, since diff --git a/Assistant/Sync.hs b/Assistant/Sync.hs index 8f30aa4f7..e6a5bc5d5 100644 --- a/Assistant/Sync.hs +++ b/Assistant/Sync.hs @@ -211,7 +211,8 @@ manualPull currentbranch remotes = do else return Nothing haddiverged <- liftAnnex Annex.Branch.forceUpdate 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) where wantpull gc = remoteAnnexPull gc diff --git a/Assistant/Threads/Merger.hs b/Assistant/Threads/Merger.hs index 0bb37e664..05341db1e 100644 --- a/Assistant/Threads/Merger.hs +++ b/Assistant/Threads/Merger.hs @@ -78,6 +78,7 @@ onChange file ] void $ liftAnnex $ Command.Sync.merge currbranch Command.Sync.mergeConfig + def Git.Branch.AutomaticCommit changedbranch mergecurrent _ = noop diff --git a/CHANGELOG b/CHANGELOG index 3540b937a..d85926d70 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,10 @@ git-annex (6.20170520) UNRELEASED; urgency=medium * metadata: When setting metadata of a file that did not exist, no error message was displayed, unlike getting metadata and most other 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 Wed, 24 May 2017 14:03:40 -0400 diff --git a/Command/Merge.hs b/Command/Merge.hs index 80a8227d1..4f99093ab 100644 --- a/Command/Merge.hs +++ b/Command/Merge.hs @@ -33,4 +33,4 @@ mergeBranch = do mergeSynced :: CommandStart mergeSynced = do prepMerge - mergeLocal mergeConfig =<< join getCurrBranch + mergeLocal mergeConfig def =<< join getCurrBranch diff --git a/Command/PostReceive.hs b/Command/PostReceive.hs index ffb5516a2..4db775214 100644 --- a/Command/PostReceive.hs +++ b/Command/PostReceive.hs @@ -48,4 +48,4 @@ fixPostReceiveHookEnv = do updateInsteadEmulation :: CommandStart updateInsteadEmulation = do prepMerge - mergeLocal mergeConfig =<< join getCurrBranch + mergeLocal mergeConfig def =<< join getCurrBranch diff --git a/Command/Sync.hs b/Command/Sync.hs index 332daca73..9ecb98620 100644 --- a/Command/Sync.hs +++ b/Command/Sync.hs @@ -76,8 +76,14 @@ data SyncOptions = SyncOptions , noContentOption :: Bool , contentOfOption :: [FilePath] , keyOptions :: Maybe KeyOptions + , resolveMergeOverride :: ResolveMergeOverride } +newtype ResolveMergeOverride = ResolveMergeOverride Bool + +instance Default ResolveMergeOverride where + def = ResolveMergeOverride False + optParser :: CmdParamsDesc -> Parser SyncOptions optParser desc = SyncOptions <$> (many $ argument str @@ -117,6 +123,9 @@ optParser desc = SyncOptions <> metavar paramPath )) <*> optional parseAllOption + <*> (ResolveMergeOverride <$> invertableSwitch "resolvemerge" True + ( help "do not automatically resolve merge conflicts" + )) -- Since prepMerge changes the working directory, FilePath options -- have to be adjusted. @@ -132,6 +141,7 @@ instance DeferredParseClass SyncOptions where <*> pure (noContentOption v) <*> liftIO (mapM absPath (contentOfOption v)) <*> pure (keyOptions v) + <*> pure (resolveMergeOverride v) seek :: SyncOptions -> CommandSeek seek o = allowConcurrentOutput $ do @@ -150,7 +160,7 @@ seek o = allowConcurrentOutput $ do -- These actions cannot be run concurrently. mapM_ includeCommandAction $ concat [ [ commit o ] - , [ withbranch (mergeLocal mergeConfig) ] + , [ withbranch (mergeLocal mergeConfig (resolveMergeOverride o)) ] , map (withbranch . pullRemote o mergeConfig) gitremotes , [ mergeAnnex ] ] @@ -219,11 +229,14 @@ mergeConfig = , Git.Merge.MergeUnrelatedHistories ] -merge :: CurrBranch -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool -merge (Just b, Just adj) mergeconfig commitmode tomerge = - updateAdjustedBranch tomerge (b, adj) mergeconfig commitmode -merge (b, _) mergeconfig commitmode tomerge = - autoMergeFrom tomerge b mergeconfig commitmode +merge :: CurrBranch -> [Git.Merge.MergeConfig] -> ResolveMergeOverride -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool +merge currbranch mergeconfig resolvemergeoverride commitmode tomerge = case currbranch of + (Just b, Just adj) -> updateAdjustedBranch tomerge (b, adj) mergeconfig canresolvemerge commitmode + (b, _) -> autoMergeFrom tomerge b mergeconfig canresolvemerge commitmode + where + canresolvemerge = case resolvemergeoverride of + ResolveMergeOverride True -> getGitConfigVal annexResolveMerge + ResolveMergeOverride False -> return False syncBranch :: Git.Branch -> Git.Branch 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 return True -mergeLocal :: [Git.Merge.MergeConfig] -> CurrBranch -> CommandStart -mergeLocal mergeconfig currbranch@(Just _, _) = +mergeLocal :: [Git.Merge.MergeConfig] -> ResolveMergeOverride -> CurrBranch -> CommandStart +mergeLocal mergeconfig resolvemergeoverride currbranch@(Just _, _) = go =<< needMerge currbranch where go Nothing = stop go (Just syncbranch) = do showStart "merge" $ Git.Ref.describe syncbranch - next $ next $ merge currbranch mergeconfig Git.Branch.ManualCommit syncbranch -mergeLocal _ (Nothing, madj) = do + next $ next $ merge currbranch mergeconfig resolvemergeoverride Git.Branch.ManualCommit syncbranch +mergeLocal _ _ (Nothing, madj) = do b <- inRepo Git.Branch.currentUnsafe ifM (isJust <$> needMerge (b, madj)) ( do @@ -365,7 +378,7 @@ pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && want next $ do showOutput stopUnless fetch $ - next $ mergeRemote remote branch mergeconfig + next $ mergeRemote remote branch mergeconfig (resolveMergeOverride o) where fetch = inRepoWithSshOptionsTo (Remote.repo remote) (Remote.gitconfig remote) $ 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), - while the synced/master may have changes that some - other remote synced to this remote. So, merge them both. -} -mergeRemote :: Remote -> CurrBranch -> [Git.Merge.MergeConfig] -> CommandCleanup -mergeRemote remote currbranch mergeconfig = ifM isBareRepo +mergeRemote :: Remote -> CurrBranch -> [Git.Merge.MergeConfig] -> ResolveMergeOverride -> CommandCleanup +mergeRemote remote currbranch mergeconfig resolvemergeoverride = ifM isBareRepo ( return True , case currbranch of (Nothing, _) -> do @@ -390,7 +403,7 @@ mergeRemote remote currbranch mergeconfig = ifM isBareRepo ) where 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) branchlist Nothing = [] branchlist (Just branch) = [branch, syncBranch branch] diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs index f66136cb1..cec64b57a 100644 --- a/Types/GitConfig.hs +++ b/Types/GitConfig.hs @@ -58,6 +58,7 @@ data GitConfig = GitConfig , annexHttpHeaders :: [String] , annexHttpHeadersCommand :: Maybe String , annexAutoCommit :: Configurable Bool + , annexResolveMerge :: Configurable Bool , annexSyncContent :: Configurable Bool , annexDebug :: Bool , annexWebOptions :: [String] @@ -115,6 +116,8 @@ extractGitConfig r = GitConfig , annexHttpHeadersCommand = getmaybe (annex "http-headers-command") , annexAutoCommit = configurable True $ getmaybebool (annex "autocommit") + , annexResolveMerge = configurable True $ + getmaybebool (annex "resolvemerge") , annexSyncContent = configurable False $ getmaybebool (annex "synccontent") , annexDebug = getbool (annex "debug") False diff --git a/doc/forum/controlling_how___34__git_annex_sync__34___resolves_conflicts/comment_2_c091e5a668b15aca7b2faa955beab403._comment b/doc/forum/controlling_how___34__git_annex_sync__34___resolves_conflicts/comment_2_c091e5a668b15aca7b2faa955beab403._comment new file mode 100644 index 000000000..f254f6e7d --- /dev/null +++ b/doc/forum/controlling_how___34__git_annex_sync__34___resolves_conflicts/comment_2_c091e5a668b15aca7b2faa955beab403._comment @@ -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` +"""]] diff --git a/doc/git-annex-config.mdwn b/doc/git-annex-config.mdwn index 8b505cde3..bf24251d8 100644 --- a/doc/git-annex-config.mdwn +++ b/doc/git-annex-config.mdwn @@ -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 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` Set to true to make git-annex sync default to syncing content. diff --git a/doc/git-annex-merge.mdwn b/doc/git-annex-merge.mdwn index 3001e9bed..bb204d725 100644 --- a/doc/git-annex-merge.mdwn +++ b/doc/git-annex-merge.mdwn @@ -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 data. +When annex.resolvemerge is set to false, merge conflict resolution +will not be done. + # SEE ALSO [[git-annex]](1) diff --git a/doc/git-annex-sync.mdwn b/doc/git-annex-sync.mdwn index a27d31565..2aa009cf8 100644 --- a/doc/git-annex-sync.mdwn +++ b/doc/git-annex-sync.mdwn @@ -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 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 tree with changes made to the local repository. (Unless it's configured 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 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 [[git-annex]](1) diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn index 8a841a941..99f6c9076 100644 --- a/doc/git-annex.mdwn +++ b/doc/git-annex.mdwn @@ -1040,6 +1040,15 @@ Here are all the supported configuration settings. To configure the behavior in all clones of the repository, 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` Set to true to make git-annex sync default to syncing content. -- cgit v1.2.3