aboutsummaryrefslogtreecommitdiff
path: root/Annex
diff options
context:
space:
mode:
Diffstat (limited to 'Annex')
-rw-r--r--Annex/AdjustedBranch.hs186
-rw-r--r--Annex/Link.hs3
2 files changed, 189 insertions, 0 deletions
diff --git a/Annex/AdjustedBranch.hs b/Annex/AdjustedBranch.hs
new file mode 100644
index 000000000..5762c6b30
--- /dev/null
+++ b/Annex/AdjustedBranch.hs
@@ -0,0 +1,186 @@
+{- adjusted version of main branch
+ -
+ - Copyright 2016 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Annex.AdjustedBranch (
+ Adjustment(..),
+ OrigBranch,
+ AdjBranch,
+ adjustedToOriginal,
+ fromAdjustedBranch,
+ enterAdjustedBranch,
+ updateAdjustedBranch,
+ propigateAdjustedCommits,
+) where
+
+import Annex.Common
+import qualified Annex
+import Git.Types
+import qualified Git.Branch
+import qualified Git.Ref
+import qualified Git.Command
+import Git.Tree
+import Git.Env
+import Git.Index
+import qualified Git.LockFile
+import Annex.CatFile
+import Annex.Link
+import Git.HashObject
+import Annex.AutoMerge
+import qualified Database.Keys
+
+data Adjustment = UnlockAdjustment
+ deriving (Show)
+
+adjustTreeItem :: Adjustment -> HashObjectHandle -> TreeItem -> Annex (Maybe TreeItem)
+adjustTreeItem UnlockAdjustment h ti@(TreeItem f m s)
+ | toBlobType m == Just SymlinkBlob = do
+ mk <- catKey s
+ case mk of
+ Just k -> do
+ Database.Keys.addAssociatedFile k f
+ Just . TreeItem f (fromBlobType FileBlob)
+ <$> hashPointerFile' h k
+ Nothing -> return (Just ti)
+ | otherwise = return (Just ti)
+
+type OrigBranch = Branch
+type AdjBranch = Branch
+
+adjustedBranchPrefix :: String
+adjustedBranchPrefix = "refs/heads/adjusted/"
+
+serialize :: Adjustment -> String
+serialize UnlockAdjustment = "unlock"
+
+deserialize :: String -> Maybe Adjustment
+deserialize "unlock" = Just UnlockAdjustment
+deserialize _ = Nothing
+
+originalToAdjusted :: OrigBranch -> Adjustment -> AdjBranch
+originalToAdjusted orig adj = Git.Ref.under base orig
+ where
+ base = adjustedBranchPrefix ++ serialize adj
+
+adjustedToOriginal :: AdjBranch -> Maybe (Adjustment, OrigBranch)
+adjustedToOriginal b
+ | adjustedBranchPrefix `isPrefixOf` bs = do
+ adj <- deserialize (takeWhile (/= '/') (drop prefixlen bs))
+ Just (adj, Git.Ref.basename b)
+ | otherwise = Nothing
+ where
+ bs = fromRef b
+ prefixlen = length adjustedBranchPrefix
+
+fromAdjustedBranch :: Branch -> OrigBranch
+fromAdjustedBranch b = maybe b snd (adjustedToOriginal b)
+
+originalBranch :: Annex (Maybe OrigBranch)
+originalBranch = fmap fromAdjustedBranch <$> inRepo Git.Branch.current
+
+{- Enter an adjusted version of current branch (or, if already in an
+ - adjusted version of a branch, changes the adjustment of the original
+ - branch).
+ -
+ - Can fail, if no branch is checked out, or perhaps if staged changes
+ - conflict with the adjusted branch.
+ -}
+enterAdjustedBranch :: Adjustment -> Annex ()
+enterAdjustedBranch adj = go =<< originalBranch
+ where
+ go (Just origbranch) = do
+ adjbranch <- preventCommits $ adjustBranch adj origbranch
+ inRepo $ Git.Command.run
+ [ Param "checkout"
+ , Param $ fromRef $ Git.Ref.base $ adjbranch
+ ]
+ go Nothing = error "not on any branch!"
+
+adjustBranch :: Adjustment -> OrigBranch -> Annex AdjBranch
+adjustBranch adj origbranch = do
+ sha <- adjust adj origbranch
+ inRepo $ Git.Branch.update adjbranch sha
+ return adjbranch
+ where
+ adjbranch = originalToAdjusted origbranch adj
+
+adjust :: Adjustment -> Ref -> Annex Sha
+adjust adj orig = do
+ h <- inRepo hashObjectStart
+ treesha <- adjustTree (adjustTreeItem adj h) orig =<< Annex.gitRepo
+ liftIO $ hashObjectStop h
+ commitAdjustedTree treesha orig
+
+{- Locks git's index file, preventing git from making a commit, merge,
+ - or otherwise changing the HEAD ref while the action is run.
+ -
+ - Throws an IO exception if the index file is already locked.
+ -}
+preventCommits :: Annex a -> Annex a
+preventCommits = bracket setup cleanup . const
+ where
+ setup = do
+ lck <- fromRepo indexFileLock
+ liftIO $ Git.LockFile.openLock lck
+ cleanup lckhandle = liftIO $ Git.LockFile.closeLock lckhandle
+
+{- Commits a given adjusted tree, with the provided parent ref.
+ -
+ - This should always yield the same value, even if performed in different
+ - clones of a repo, at different times. The commit message and other
+ - metadata is based on the parent.
+ -}
+commitAdjustedTree :: Sha -> Ref -> Annex Sha
+commitAdjustedTree treesha parent = go =<< catCommit parent
+ where
+ go Nothing = inRepo mkcommit
+ go (Just parentcommit) = inRepo $ commitWithMetaData
+ (commitAuthorMetaData parentcommit)
+ (commitCommitterMetaData parentcommit)
+ mkcommit
+ mkcommit = Git.Branch.commitTree
+ Git.Branch.AutomaticCommit "adjusted branch" [parent] treesha
+
+{- Update the currently checked out adjusted branch, merging the provided
+ - branch into it. -}
+updateAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> Git.Branch.CommitMode -> Annex Bool
+updateAdjustedBranch tomerge (origbranch, adj) commitmode =
+ catchBoolIO $ preventCommits $ go =<< (,)
+ <$> inRepo (Git.Ref.sha tomerge)
+ <*> inRepo Git.Branch.current
+ where
+ go (Just mergesha, Just currbranch) = ifM (inRepo $ Git.Branch.changed currbranch mergesha)
+ ( do
+ propigateAdjustedCommits origbranch adj
+ adjustedtomerge <- adjust adj mergesha
+ ifM (inRepo $ Git.Branch.changed currbranch adjustedtomerge)
+ ( ifM (autoMergeFrom adjustedtomerge (Just currbranch) commitmode)
+ ( recommit currbranch mergesha =<< catCommit currbranch
+ , return False
+ )
+ , return True -- no changes to merge
+ )
+ , return True -- no changes to merge
+ )
+ go _ = return False
+ {- Once a merge commit has been made, re-do it, removing
+ - the old version of the adjusted branch as a parent, and
+ - making the only parent be the branch that was merged in.
+ -
+ - Doing this ensures that the same commit Sha is
+ - always arrived at for a given commit from the merged in branch.
+ -}
+ recommit currbranch parent (Just commit) = do
+ commitsha <- commitAdjustedTree (commitTree commit) parent
+ inRepo $ Git.Branch.update currbranch commitsha
+ propigateAdjustedCommits origbranch adj
+ return True
+ recommit _ _ Nothing = return False
+
+{- Check for any commits present on the adjusted branch that have not yet
+ - been propigated to the orig branch, and propigate them. -}
+propigateAdjustedCommits :: OrigBranch -> Adjustment -> Annex ()
+propigateAdjustedCommits originbranch adj = return () -- TODO
diff --git a/Annex/Link.hs b/Annex/Link.hs
index 40e56f23e..1f2830c40 100644
--- a/Annex/Link.hs
+++ b/Annex/Link.hs
@@ -123,6 +123,9 @@ hashPointerFile :: Key -> Annex Sha
hashPointerFile key = inRepo $ Git.HashObject.hashObject BlobObject $
formatPointer key
+hashPointerFile' :: Git.HashObject.HashObjectHandle -> Key -> Annex Sha
+hashPointerFile' h = liftIO . Git.HashObject.hashBlob h . formatPointer
+
{- Stages a pointer file, using a Sha of its content -}
stagePointerFile :: FilePath -> Sha -> Annex ()
stagePointerFile file sha =