From 5f9bf51438a16b73d6fcf30f3ca43fbb894ea5df Mon Sep 17 00:00:00 2001 From: Joey Hess <joeyh@joeyh.name> Date: Wed, 8 Feb 2023 15:37:28 -0400 Subject: [PATCH] sync in view branch updates the view branch * sync: When run in a view branch, refresh the view branch to reflect any changes that have been made to the parent branch or metadata. This is basically working, but probably needs some more work to deal with all the edge cases of things sync does. Sponsored-by: Lawrence Brogan on Patreon --- Annex/View.hs | 69 ++++++++++++++++--- CHANGELOG | 2 + Command/Sync.hs | 22 ++++-- Command/View.hs | 2 +- doc/git-annex-sync.mdwn | 5 +- ..._6ab0e38d8be8a70d501108f207d41d82._comment | 29 ++++++++ 6 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 doc/todo/Metadata_changes_are_not_reflected_in_a_view/comment_4_6ab0e38d8be8a70d501108f207d41d82._comment diff --git a/Annex/View.hs b/Annex/View.hs index 21f585d845..dd0025df13 100644 --- a/Annex/View.hs +++ b/Annex/View.hs @@ -19,6 +19,7 @@ import qualified Git import qualified Git.DiffTree as DiffTree import qualified Git.Branch import qualified Git.LsFiles +import qualified Git.LsTree import qualified Git.Ref import Git.UpdateIndex import Git.Sha @@ -428,21 +429,30 @@ narrowView = applyView' viewedFileReuse getViewedFileMetaData - or a file in a dotdir in the top. - Look up the metadata of annexed files, and generate any ViewedFiles, - and stage them. - - - - Must be run from top of repository. -} applyView' :: MkViewedFile -> (FilePath -> MetaData) -> View -> Annex Git.Branch applyView' mkviewedfile getfilemetadata view = do top <- fromRepo Git.repoPath (l, clean) <- inRepo $ Git.LsFiles.inRepoDetails [] [top] - liftIO . removeWhenExistsWith R.removeLink =<< fromRepo gitAnnexViewIndex - viewg <- withViewIndex gitRepo - withUpdateIndex viewg $ \uh -> do - forM_ l $ \(f, sha, mode) -> do + applyView'' mkviewedfile getfilemetadata view l clean $ + \go (f, sha, mode) -> do topf <- inRepo (toTopFilePath f) - go uh topf sha (toTreeItemType mode) =<< lookupKey f - liftIO $ void clean + go topf sha (toTreeItemType mode) =<< lookupKey f genViewBranch view + +applyView'' + :: MkViewedFile + -> (FilePath -> MetaData) + -> View + -> [t] + -> IO Bool + -> ((TopFilePath -> Sha -> Maybe TreeItemType -> Maybe Key -> Annex ()) -> t -> Annex ()) + -> Annex () +applyView'' mkviewedfile getfilemetadata view l clean a = do + viewg <- withNewViewIndex gitRepo + withUpdateIndex viewg $ \uh -> do + forM_ l $ a (go uh) + liftIO $ void clean where genviewedfiles = viewedFiles view mkviewedfile -- enables memoization @@ -464,6 +474,44 @@ applyView' mkviewedfile getfilemetadata view = do liftIO . Git.UpdateIndex.streamUpdateIndex' uh =<< inRepo (Git.UpdateIndex.stageSymlink f sha) +{- Updates the current view with any changes that have been made to its + - parent branch or the metadata since the view was created or last updated. + - + - When there were changes, returns a ref to a commit for the updated view. + - Does not update the view branch with it. + - + - This is not very optimised. An incremental update would be possible to + - implement and would be faster, but more complicated. + -} +updateView :: View -> Annex (Maybe Git.Ref) +updateView view = do + (l, clean) <- inRepo $ Git.LsTree.lsTree + Git.LsTree.LsTreeRecursive + (Git.LsTree.LsTreeLong True) + (viewParentBranch view) + applyView'' viewedFileFromReference getWorkTreeMetaData view l clean $ + \go ti -> do + let ref = Git.Ref.branchFileRef (viewParentBranch view) + (getTopFilePath (Git.LsTree.file ti)) + k <- case Git.LsTree.size ti of + Nothing -> catKey ref + Just sz -> catKey' ref sz + go + (Git.LsTree.file ti) + (Git.LsTree.sha ti) + (toTreeItemType (Git.LsTree.mode ti)) + k + oldcommit <- inRepo $ Git.Ref.sha (branchView view) + oldtree <- maybe (pure Nothing) (inRepo . Git.Ref.tree) oldcommit + newtree <- withViewIndex $ inRepo Git.Branch.writeTree + if oldtree /= Just newtree + then Just <$> do + cmode <- annexCommitMode <$> Annex.getGitConfig + let msg = "updated " ++ fromRef (branchView view) + let parent = catMaybes [oldcommit] + inRepo (Git.Branch.commitTree cmode msg parent newtree) + else return Nothing + {- Diff between currently checked out branch and staged changes, and - update metadata to reflect the changes that are being committed to the - view. @@ -501,6 +549,11 @@ withViewChanges addmeta removemeta = do withViewIndex :: Annex a -> Annex a withViewIndex = withIndexFile ViewIndexFile . const +withNewViewIndex :: Annex a -> Annex a +withNewViewIndex a = do + liftIO . removeWhenExistsWith R.removeLink =<< fromRepo gitAnnexViewIndex + withViewIndex a + {- Generates a branch for a view, using the view index file - to make a commit to the view branch. The view branch is not - checked out, but entering it will display the view. -} diff --git a/CHANGELOG b/CHANGELOG index 5917aeb4e8..f2bd1dc924 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,8 @@ git-annex (10.20230127) UNRELEASED; urgency=medium * Changed the name of view branches to include the parent branch. Existing view branches checked out using an old name will still work. * sync: Avoid pushing view branches to remotes. + * sync: When run in a view branch, refresh the view branch to reflect any + changes that have been made to the parent branch or metadata. -- Joey Hess <id@joeyh.name> Mon, 06 Feb 2023 13:39:18 -0400 diff --git a/Command/Sync.hs b/Command/Sync.hs index 2348f2ea14..fb4c79f135 100644 --- a/Command/Sync.hs +++ b/Command/Sync.hs @@ -1,7 +1,7 @@ {- git-annex command - - Copyright 2011 Joachim Breitner <mail@joachim-breitner.de> - - Copyright 2011-2021 Joey Hess <id@joeyh.name> + - Copyright 2011-2023 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -} @@ -59,10 +59,11 @@ import Annex.UUID import Logs.UUID import Logs.Export import Logs.PreferredContent -import Logs.View (fromViewBranch) +import Logs.View import Annex.AutoMerge import Annex.AdjustedBranch import Annex.AdjustedBranch.Merge +import Annex.View import Annex.Ssh import Annex.BloomFilter import Annex.UpdateInstead @@ -440,7 +441,6 @@ updateBranches (Just branch, madj) = do -- to be updated, if the adjustment is not stable, and the usual -- configuration does not update it. case madj of - Nothing -> noop Just adj -> do let origbranch = branch propigateAdjustedCommits origbranch adj @@ -448,13 +448,27 @@ updateBranches (Just branch, madj) = do annexAdjustedBranchRefresh <$> Annex.getGitConfig >>= \case 0 -> adjustedBranchRefreshFull adj origbranch _ -> return () + -- When in a view branch, update it to reflect any changes + -- of its parent branch or the metadata. + Nothing -> currentView >>= \case + Nothing -> noop + Just view -> updateView view >>= \case + Nothing -> noop + Just newcommit -> do + ok <- inRepo $ Git.Command.runBool + [ Param "merge" + , Param (Git.fromRef newcommit) + ] + unless ok $ + giveup $ "failed to update view" -- Update the sync branch to match the new state of the branch inRepo $ updateBranch (syncBranch branch) branch updateBranch :: Git.Branch -> Git.Branch -> Git.Repo -> IO () updateBranch syncbranch updateto g = - unlessM go $ giveup $ "failed to update " ++ Git.fromRef syncbranch + unlessM go $ + giveup $ "failed to update " ++ Git.fromRef syncbranch where go = Git.Command.runBool [ Param "branch" diff --git a/Command/View.hs b/Command/View.hs index a22d78bc4c..9954229e10 100644 --- a/Command/View.hs +++ b/Command/View.hs @@ -35,7 +35,7 @@ start [] = giveup "Specify metadata to include in view" start ps = ifM safeToEnterView ( do view <- mkView ps - go view =<< currentView + go view =<< currentView , giveup "Not safe to enter view." ) where diff --git a/doc/git-annex-sync.mdwn b/doc/git-annex-sync.mdwn index 4f5b66e247..634b110918 100644 --- a/doc/git-annex-sync.mdwn +++ b/doc/git-annex-sync.mdwn @@ -47,7 +47,10 @@ have the same value as the currently checked out branch. When [[git-annex-adjust]](1) has been used to check out an adjusted branch, running sync will propagate changes that have been made back to the -parent branch, without propagating the adjustments. +parent branch, without propagating the adjustments. When +[[git-annex-view]](1) has been used to check out a view branch, +running sync will update the view branch to reflect any changes +to the parent branch or metadata. # OPTIONS diff --git a/doc/todo/Metadata_changes_are_not_reflected_in_a_view/comment_4_6ab0e38d8be8a70d501108f207d41d82._comment b/doc/todo/Metadata_changes_are_not_reflected_in_a_view/comment_4_6ab0e38d8be8a70d501108f207d41d82._comment new file mode 100644 index 0000000000..75af01c6c8 --- /dev/null +++ b/doc/todo/Metadata_changes_are_not_reflected_in_a_view/comment_4_6ab0e38d8be8a70d501108f207d41d82._comment @@ -0,0 +1,29 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 4""" + date="2023-02-08T19:32:34Z" + content=""" +`git-annex sync` when in a view branch will now update it. + +Leaving this open for optimising it. + +Also because of this problem: + + joey@darkstar:~/tmp/m#master(author=_)>git-annex sync + commit + On branch views/master(author=_) + nothing to commit, working tree clean + ok + merge synced/master + fatal: refusing to merge unrelated histories + failed + +Looks like it should not be trying to merge the synced/master +branch into the view branch. But, this makes me wonder, does the master branch +get updated with new files pulled from remotes? If not, the view branch +won't be updated to have them either. + +Also, I think that it may try to import trees from importree special remotes, +into the view branch. Perhaps those should also get imported but merged into +the master branch... +"""]]