2016-02-25 20:11:13 +00:00
|
|
|
{- adjusted version of main branch
|
|
|
|
-
|
|
|
|
- Copyright 2016 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2016-02-29 19:04:03 +00:00
|
|
|
module Annex.AdjustedBranch (
|
|
|
|
Adjustment(..),
|
|
|
|
OrigBranch,
|
|
|
|
AdjBranch,
|
2016-03-03 20:19:09 +00:00
|
|
|
originalToAdjusted,
|
2016-02-29 19:04:03 +00:00
|
|
|
adjustedToOriginal,
|
2016-03-03 18:13:54 +00:00
|
|
|
fromAdjustedBranch,
|
2016-02-29 19:04:03 +00:00
|
|
|
enterAdjustedBranch,
|
|
|
|
updateAdjustedBranch,
|
2016-03-03 18:13:54 +00:00
|
|
|
propigateAdjustedCommits,
|
2016-02-29 19:04:03 +00:00
|
|
|
) where
|
2016-02-25 20:11:13 +00:00
|
|
|
|
|
|
|
import Annex.Common
|
|
|
|
import qualified Annex
|
2016-03-03 20:19:09 +00:00
|
|
|
import Git
|
2016-02-25 20:11:13 +00:00
|
|
|
import Git.Types
|
|
|
|
import qualified Git.Branch
|
|
|
|
import qualified Git.Ref
|
|
|
|
import qualified Git.Command
|
2016-03-03 20:19:09 +00:00
|
|
|
import qualified Git.Tree
|
|
|
|
import Git.Tree (TreeItem(..))
|
2016-02-25 20:11:13 +00:00
|
|
|
import Git.Env
|
2016-03-03 16:55:00 +00:00
|
|
|
import Git.Index
|
2016-03-03 20:19:09 +00:00
|
|
|
import Git.FilePath
|
2016-03-03 16:55:00 +00:00
|
|
|
import qualified Git.LockFile
|
2016-02-25 20:11:13 +00:00
|
|
|
import Annex.CatFile
|
|
|
|
import Annex.Link
|
|
|
|
import Git.HashObject
|
2016-02-29 21:16:56 +00:00
|
|
|
import Annex.AutoMerge
|
2016-02-29 21:27:19 +00:00
|
|
|
import qualified Database.Keys
|
2016-02-25 20:11:13 +00:00
|
|
|
|
|
|
|
data Adjustment = UnlockAdjustment
|
|
|
|
deriving (Show)
|
|
|
|
|
2016-03-03 20:19:09 +00:00
|
|
|
data Direction = Forward | Reverse
|
|
|
|
|
|
|
|
adjustTreeItem :: Adjustment -> Direction -> HashObjectHandle -> TreeItem -> Annex (Maybe TreeItem)
|
|
|
|
adjustTreeItem UnlockAdjustment Forward h ti@(TreeItem f m s)
|
2016-02-25 20:11:13 +00:00
|
|
|
| toBlobType m == Just SymlinkBlob = do
|
|
|
|
mk <- catKey s
|
|
|
|
case mk of
|
2016-02-29 21:27:19 +00:00
|
|
|
Just k -> do
|
|
|
|
Database.Keys.addAssociatedFile k f
|
|
|
|
Just . TreeItem f (fromBlobType FileBlob)
|
|
|
|
<$> hashPointerFile' h k
|
2016-02-25 20:11:13 +00:00
|
|
|
Nothing -> return (Just ti)
|
|
|
|
| otherwise = return (Just ti)
|
2016-03-03 20:19:09 +00:00
|
|
|
adjustTreeItem UnlockAdjustment Reverse h ti@(TreeItem f m s)
|
|
|
|
-- XXX does not remember when files were originally unlocked; locks
|
|
|
|
-- everything
|
|
|
|
| toBlobType m /= Just SymlinkBlob = do
|
|
|
|
mk <- catKey s
|
|
|
|
case mk of
|
|
|
|
Just k -> do
|
|
|
|
absf <- inRepo $ \r -> absPath $
|
2016-03-03 20:47:51 +00:00
|
|
|
fromTopFilePath f r
|
2016-03-03 20:19:09 +00:00
|
|
|
linktarget <- calcRepo $ gitAnnexLink absf k
|
|
|
|
Just . TreeItem f (fromBlobType SymlinkBlob)
|
|
|
|
<$> hashSymlink' h linktarget
|
|
|
|
Nothing -> return (Just ti)
|
|
|
|
| otherwise = return (Just ti)
|
2016-02-25 20:11:13 +00:00
|
|
|
|
|
|
|
type OrigBranch = Branch
|
|
|
|
type AdjBranch = Branch
|
|
|
|
|
|
|
|
adjustedBranchPrefix :: String
|
|
|
|
adjustedBranchPrefix = "refs/heads/adjusted/"
|
|
|
|
|
2016-02-29 19:04:03 +00:00
|
|
|
serialize :: Adjustment -> String
|
2016-03-03 20:38:56 +00:00
|
|
|
serialize UnlockAdjustment = "unlocked"
|
2016-02-25 20:11:13 +00:00
|
|
|
|
2016-02-29 19:04:03 +00:00
|
|
|
deserialize :: String -> Maybe Adjustment
|
2016-03-03 20:38:56 +00:00
|
|
|
deserialize "unlocked" = Just UnlockAdjustment
|
2016-02-29 19:04:03 +00:00
|
|
|
deserialize _ = Nothing
|
|
|
|
|
|
|
|
originalToAdjusted :: OrigBranch -> Adjustment -> AdjBranch
|
2016-03-03 20:38:56 +00:00
|
|
|
originalToAdjusted orig adj = Ref $
|
|
|
|
adjustedBranchPrefix ++ base ++ '(' : serialize adj ++ ")"
|
2016-02-29 19:04:03 +00:00
|
|
|
where
|
2016-03-03 20:38:56 +00:00
|
|
|
base = fromRef (Git.Ref.basename orig)
|
2016-02-29 19:04:03 +00:00
|
|
|
|
|
|
|
adjustedToOriginal :: AdjBranch -> Maybe (Adjustment, OrigBranch)
|
2016-02-25 20:11:13 +00:00
|
|
|
adjustedToOriginal b
|
2016-02-29 19:04:03 +00:00
|
|
|
| adjustedBranchPrefix `isPrefixOf` bs = do
|
2016-03-03 20:38:56 +00:00
|
|
|
let (base, as) = separate (== '(') (drop prefixlen bs)
|
|
|
|
adj <- deserialize (takeWhile (/= ')') as)
|
|
|
|
Just (adj, Git.Ref.under "refs/heads" (Ref base))
|
2016-02-25 20:11:13 +00:00
|
|
|
| otherwise = Nothing
|
|
|
|
where
|
|
|
|
bs = fromRef b
|
|
|
|
prefixlen = length adjustedBranchPrefix
|
|
|
|
|
2016-03-03 18:13:54 +00:00
|
|
|
fromAdjustedBranch :: Branch -> OrigBranch
|
|
|
|
fromAdjustedBranch b = maybe b snd (adjustedToOriginal b)
|
|
|
|
|
2016-02-25 20:11:13 +00:00
|
|
|
originalBranch :: Annex (Maybe OrigBranch)
|
2016-03-03 18:13:54 +00:00
|
|
|
originalBranch = fmap fromAdjustedBranch <$> inRepo Git.Branch.current
|
2016-02-25 20:11:13 +00:00
|
|
|
|
|
|
|
{- 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
|
2016-03-03 18:17:37 +00:00
|
|
|
go (Just origbranch) = do
|
2016-03-03 20:19:09 +00:00
|
|
|
adjbranch <- preventCommits $ adjustBranch adj Forward origbranch
|
2016-02-25 20:11:13 +00:00
|
|
|
inRepo $ Git.Command.run
|
|
|
|
[ Param "checkout"
|
|
|
|
, Param $ fromRef $ Git.Ref.base $ adjbranch
|
|
|
|
]
|
|
|
|
go Nothing = error "not on any branch!"
|
|
|
|
|
2016-03-03 20:19:09 +00:00
|
|
|
adjustBranch :: Adjustment -> Direction -> OrigBranch -> Annex AdjBranch
|
|
|
|
adjustBranch adj direction origbranch = do
|
|
|
|
sha <- adjust adj direction origbranch
|
2016-02-29 21:16:56 +00:00
|
|
|
inRepo $ Git.Branch.update adjbranch sha
|
2016-02-25 20:11:13 +00:00
|
|
|
return adjbranch
|
|
|
|
where
|
2016-02-29 19:04:03 +00:00
|
|
|
adjbranch = originalToAdjusted origbranch adj
|
2016-02-25 20:11:13 +00:00
|
|
|
|
2016-03-03 20:19:09 +00:00
|
|
|
adjust :: Adjustment -> Direction -> Ref -> Annex Sha
|
|
|
|
adjust adj direction orig = do
|
|
|
|
treesha <- adjustTree adj direction orig
|
|
|
|
commitAdjustedTree treesha orig
|
|
|
|
|
|
|
|
adjustTree :: Adjustment -> Direction -> Ref -> Annex Sha
|
|
|
|
adjustTree adj direction orig = do
|
2016-02-29 21:16:56 +00:00
|
|
|
h <- inRepo hashObjectStart
|
2016-03-03 20:19:09 +00:00
|
|
|
treesha <- Git.Tree.adjustTree (adjustTreeItem adj direction h) orig
|
|
|
|
=<< Annex.gitRepo
|
2016-02-29 21:16:56 +00:00
|
|
|
liftIO $ hashObjectStop h
|
2016-03-03 20:19:09 +00:00
|
|
|
return treesha
|
2016-02-29 21:16:56 +00:00
|
|
|
|
2016-03-03 16:55:00 +00:00
|
|
|
{- 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
|
|
|
|
|
2016-02-25 20:11:13 +00:00
|
|
|
{- 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
|
2016-03-03 20:19:09 +00:00
|
|
|
mkcommit = Git.Branch.commitTree Git.Branch.AutomaticCommit
|
|
|
|
adjustedBranchCommitMessage [parent] treesha
|
|
|
|
|
|
|
|
adjustedBranchCommitMessage :: String
|
|
|
|
adjustedBranchCommitMessage = "git-annex adjusted branch"
|
2016-02-29 19:04:03 +00:00
|
|
|
|
|
|
|
{- Update the currently checked out adjusted branch, merging the provided
|
|
|
|
- branch into it. -}
|
2016-02-29 19:57:47 +00:00
|
|
|
updateAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> Git.Branch.CommitMode -> Annex Bool
|
2016-03-03 16:55:00 +00:00
|
|
|
updateAdjustedBranch tomerge (origbranch, adj) commitmode =
|
|
|
|
catchBoolIO $ preventCommits $ go =<< (,)
|
2016-02-29 21:16:56 +00:00
|
|
|
<$> inRepo (Git.Ref.sha tomerge)
|
|
|
|
<*> inRepo Git.Branch.current
|
|
|
|
where
|
|
|
|
go (Just mergesha, Just currbranch) = ifM (inRepo $ Git.Branch.changed currbranch mergesha)
|
|
|
|
( do
|
2016-03-03 20:19:09 +00:00
|
|
|
propigateAdjustedCommits origbranch (adj, currbranch)
|
|
|
|
adjustedtomerge <- adjust adj Forward mergesha
|
2016-02-29 21:16:56 +00:00
|
|
|
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
|
2016-03-03 20:19:09 +00:00
|
|
|
propigateAdjustedCommits origbranch (adj, currbranch)
|
2016-02-29 21:16:56 +00:00
|
|
|
return True
|
|
|
|
recommit _ _ Nothing = return False
|
|
|
|
|
|
|
|
{- Check for any commits present on the adjusted branch that have not yet
|
2016-03-03 20:19:09 +00:00
|
|
|
- been propigated to the orig branch, and propigate them.
|
|
|
|
-
|
|
|
|
- After propigating the commits back to the orig banch,
|
|
|
|
- rebase the adjusted branch on top of the updated orig branch.
|
|
|
|
-}
|
|
|
|
propigateAdjustedCommits :: OrigBranch -> (Adjustment, AdjBranch) -> Annex ()
|
|
|
|
propigateAdjustedCommits origbranch (adj, currbranch) = do
|
2016-03-03 21:00:48 +00:00
|
|
|
ov <- inRepo $ Git.Ref.sha (Git.Ref.under "refs/heads" origbranch)
|
|
|
|
case ov of
|
|
|
|
Just origsha -> preventCommits $ do
|
|
|
|
cv <- catCommit currbranch
|
|
|
|
case cv of
|
|
|
|
Just currcommit ->
|
|
|
|
newcommits
|
|
|
|
>>= go origsha False
|
|
|
|
>>= rebase currcommit
|
|
|
|
Nothing -> return ()
|
2016-03-03 20:19:09 +00:00
|
|
|
Nothing -> return ()
|
|
|
|
where
|
|
|
|
newcommits = inRepo $ Git.Branch.changedCommits origbranch currbranch
|
|
|
|
-- Get commits oldest first, so they can be processed
|
|
|
|
-- in order made.
|
|
|
|
[Param "--reverse"]
|
2016-03-03 21:00:48 +00:00
|
|
|
go parent _ [] = do
|
|
|
|
inRepo $ Git.Branch.update origbranch parent
|
|
|
|
return parent
|
2016-03-03 20:19:09 +00:00
|
|
|
go parent pastadjcommit (sha:l) = do
|
|
|
|
mc <- catCommit sha
|
|
|
|
case mc of
|
|
|
|
Just c
|
|
|
|
| commitMessage c == adjustedBranchCommitMessage ->
|
|
|
|
go parent True l
|
|
|
|
| pastadjcommit -> do
|
|
|
|
commit <- reverseAdjustedCommit parent adj c
|
|
|
|
go commit pastadjcommit l
|
|
|
|
_ -> go parent pastadjcommit l
|
2016-03-03 21:00:48 +00:00
|
|
|
rebase currcommit newparent = do
|
|
|
|
-- Reuse the current adjusted tree,
|
|
|
|
-- and reparent it on top of the new
|
|
|
|
-- version of the origbranch.
|
|
|
|
commitAdjustedTree (commitTree currcommit) newparent
|
|
|
|
>>= inRepo . Git.Branch.update currbranch
|
2016-03-03 20:19:09 +00:00
|
|
|
|
|
|
|
{- Reverses an adjusted commit, yielding a commit sha.
|
|
|
|
-
|
|
|
|
- Note that the commit message, and the author and committer metadata are
|
|
|
|
- copied over. However, any gpg signature will be lost, and any other
|
|
|
|
- headers are not copied either. -}
|
|
|
|
reverseAdjustedCommit :: Sha -> Adjustment -> Commit -> Annex Sha
|
|
|
|
reverseAdjustedCommit parent adj c = do
|
|
|
|
treesha <- adjustTree adj Reverse (commitTree c)
|
|
|
|
inRepo $ commitWithMetaData
|
|
|
|
(commitAuthorMetaData c)
|
|
|
|
(commitCommitterMetaData c) $
|
|
|
|
Git.Branch.commitTree Git.Branch.AutomaticCommit
|
|
|
|
(commitMessage c) [parent] treesha
|