summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joeyh@joeyh.name>2015-01-27 17:38:06 -0400
committerGravatar Joey Hess <joeyh@joeyh.name>2015-01-27 17:38:06 -0400
commitdf81023501e2b0d930ec90cc6f5a6c6735f84818 (patch)
tree5bd3b9d8f91464bd27c9d86f700b975a76067147
parent1e07d61b9669f85a02551d7858177bd33ffaea6f (diff)
Repository tuning parameters can now be passed when initializing a repository for the first time.
* init: Repository tuning parameters can now be passed when initializing a repository for the first time. For details, see http://git-annex.branchable.com/tuning/ * merge: Refuse to merge changes from a git-annex branch of a repo that has been tuned in incompatable ways.
-rw-r--r--Annex/AutoMerge.hs2
-rw-r--r--Annex/Branch.hs11
-rw-r--r--Annex/Content.hs2
-rw-r--r--Annex/Difference.hs60
-rw-r--r--Annex/Direct.hs6
-rw-r--r--Annex/Init.hs2
-rw-r--r--Annex/View.hs2
-rw-r--r--Assistant/Threads/Committer.hs2
-rw-r--r--Assistant/Threads/Watcher.hs4
-rw-r--r--Command/Add.hs4
-rw-r--r--Command/Fix.hs2
-rw-r--r--Command/FromKey.hs2
-rw-r--r--Command/Fsck.hs2
-rw-r--r--Command/Indirect.hs2
-rw-r--r--Locations.hs24
-rw-r--r--Logs.hs4
-rw-r--r--Logs/Difference.hs40
-rw-r--r--Logs/Difference/Pure.hs26
-rw-r--r--Types/Difference.hs135
-rw-r--r--Types/GitConfig.hs3
-rw-r--r--Upgrade/V1.hs2
-rw-r--r--debian/changelog5
-rw-r--r--doc/git-annex.mdwn6
-rw-r--r--doc/internals.mdwn9
-rw-r--r--doc/tuning.mdwn44
25 files changed, 376 insertions, 25 deletions
diff --git a/Annex/AutoMerge.hs b/Annex/AutoMerge.hs
index 92cccc004..f0f183dfb 100644
--- a/Annex/AutoMerge.hs
+++ b/Annex/AutoMerge.hs
@@ -134,7 +134,7 @@ resolveMerge' (Just us) them u = do
makelink key = do
let dest = variantFile file key
- l <- inRepo $ gitAnnexLink dest key
+ l <- calcRepo $ gitAnnexLink dest key
replacewithlink dest l
stageSymlink dest =<< hashSymlink l
diff --git a/Annex/Branch.hs b/Annex/Branch.hs
index 2191ec263..c67270312 100644
--- a/Annex/Branch.hs
+++ b/Annex/Branch.hs
@@ -49,9 +49,11 @@ import Annex.Perms
import Logs
import Logs.Transitions
import Logs.Trust.Pure
+import Logs.Difference.Pure
import Annex.ReplaceFile
import qualified Annex.Queue
import Annex.Branch.Transitions
+import qualified Annex
{- Name of the branch that is used to store git-annex's information. -}
name :: Git.Ref
@@ -160,6 +162,7 @@ updateTo pairs = do
<$> getLocal transitionsLog
unless (null branches) $ do
showSideAction merge_desc
+ mapM_ checkBranchDifferences refs
mergeIndex jl refs
let commitrefs = nub $ fullname:refs
unlessM (handleTransitions jl localtransitions commitrefs) $ do
@@ -537,3 +540,11 @@ performTransitionsLocked jl ts neednewlocalbranch transitionedrefs = do
apply rest hasher file content' trustmap
PreserveFile ->
apply rest hasher file content trustmap
+
+checkBranchDifferences :: Git.Ref -> Annex ()
+checkBranchDifferences ref = do
+ theirdiffs <- allDifferences . parseDifferencesLog . decodeBS
+ <$> catFile ref differenceLog
+ mydiffs <- annexDifferences <$> Annex.getGitConfig
+ when (theirdiffs /= mydiffs) $
+ error "Remote repository is tuned in incompatable way; cannot be merged with local repository."
diff --git a/Annex/Content.hs b/Annex/Content.hs
index 73f27fd28..a78cf674c 100644
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -446,7 +446,7 @@ removeAnnex (ContentLock key) = withObjectLoc key remove removedirect
removeInodeCache key
mapM_ (resetfile cache) fs
resetfile cache f = whenM (sameInodeCache f cache) $ do
- l <- inRepo $ gitAnnexLink f key
+ l <- calcRepo $ gitAnnexLink f key
secureErase f
replaceFile f $ makeAnnexLink l
diff --git a/Annex/Difference.hs b/Annex/Difference.hs
new file mode 100644
index 000000000..cb363e80c
--- /dev/null
+++ b/Annex/Difference.hs
@@ -0,0 +1,60 @@
+{- git-annex repository differences
+ -
+ - Copyright 2015 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Annex.Difference (
+ module Types.Difference,
+ setDifferences,
+) where
+
+import Common.Annex
+import Types.Difference
+import Logs.Difference
+import Config
+import Annex.UUID
+import Logs.UUID
+import Annex.Version
+import qualified Annex
+
+import qualified Data.Map as M
+
+-- Differences are only allowed to be tweaked when initializing a
+-- repository for the first time, and then only if there is not another
+-- known uuid. If the repository was cloned from elsewhere, it inherits
+-- the existing settings.
+--
+-- Must be called before setVersion, so it can check if this is the first
+-- time the repository is being initialized.
+setDifferences :: Annex ()
+setDifferences = do
+ u <- getUUID
+ otherds <- either error return
+ =<< sanityCheckDifferences . allDifferences
+ <$> recordedDifferences
+ ds <- mappend otherds . annexDifferences <$> Annex.getGitConfig
+ when (ds /= mempty) $ do
+ ds'@(Differences l) <- ifM (isJust <$> getVersion)
+ ( do
+ oldds <- recordedDifferencesFor u
+ when (ds /= oldds) $
+ warning $ "Cannot change tunable parameters in already initialized repository."
+ return oldds
+ , if otherds == mempty
+ then ifM (not . null . filter (/= u) . M.keys <$> uuidMap)
+ ( do
+ warning "Cannot change tunable parameters in a clone of an existing repository."
+ return mempty
+ , return ds
+ )
+ else if otherds /= ds
+ then do
+ warning "The specified tunable parameters differ from values being used in other clones of this repository."
+ return otherds
+ else return ds
+ )
+ forM_ l $ \d ->
+ setConfig (ConfigKey $ differenceConfigKey d) (differenceConfigVal d)
+ recordDifferences ds' u
diff --git a/Annex/Direct.hs b/Annex/Direct.hs
index 06d0342c1..1c733cb55 100644
--- a/Annex/Direct.hs
+++ b/Annex/Direct.hs
@@ -86,7 +86,7 @@ stageDirect = do
deletegit file
stageannexlink file key = do
- l <- inRepo $ gitAnnexLink file key
+ l <- calcRepo $ gitAnnexLink file key
stageSymlink file =<< hashSymlink l
void $ addAssociatedFile key file
@@ -131,7 +131,7 @@ addDirect file cache = do
return False
got (Just (key, _)) = ifM (sameInodeCache file [cache])
( do
- l <- inRepo $ gitAnnexLink file key
+ l <- calcRepo $ gitAnnexLink file key
stageSymlink file =<< hashSymlink l
addInodeCache key cache
void $ addAssociatedFile key file
@@ -282,7 +282,7 @@ updateWorkTree d oldref = do
- with the content. -}
movein item makeabs k f = unlessM (goodContent k f) $ do
preserveUnannexed item makeabs f oldref
- l <- inRepo $ gitAnnexLink f k
+ l <- calcRepo $ gitAnnexLink f k
replaceFile f $ makeAnnexLink l
toDirect k f
diff --git a/Annex/Init.hs b/Annex/Init.hs
index 24c5e18b1..3f27a1172 100644
--- a/Annex/Init.hs
+++ b/Annex/Init.hs
@@ -27,6 +27,7 @@ import Logs.UUID
import Logs.Trust.Basic
import Types.TrustLevel
import Annex.Version
+import Annex.Difference
import Annex.UUID
import Config
import Annex.Direct
@@ -73,6 +74,7 @@ initialize' = do
checkCrippledFileSystem
unlessM isBare $
hookWrite preCommitHook
+ setDifferences
setVersion supportedVersion
ifM (crippledFileSystem <&&> not <$> isBare)
( do
diff --git a/Annex/View.hs b/Annex/View.hs
index 3a9168f6c..315cc7df2 100644
--- a/Annex/View.hs
+++ b/Annex/View.hs
@@ -354,7 +354,7 @@ applyView' mkviewedfile getfilemetadata view = do
let metadata' = getfilemetadata f `unionMetaData` metadata
forM_ (genviewedfiles f metadata') $ \fv -> do
f' <- fromRepo $ fromTopFilePath $ asTopFilePath fv
- stagesymlink uh hasher f' =<< inRepo (gitAnnexLink f' k)
+ stagesymlink uh hasher f' =<< calcRepo (gitAnnexLink f' k)
go uh hasher f Nothing
| "." `isPrefixOf` f = do
s <- liftIO $ getSymbolicLinkStatus f
diff --git a/Assistant/Threads/Committer.hs b/Assistant/Threads/Committer.hs
index 2c1addb24..3f8debdc2 100644
--- a/Assistant/Threads/Committer.hs
+++ b/Assistant/Threads/Committer.hs
@@ -352,7 +352,7 @@ handleAdds havelsof delayadd cs = returnWhen (null incomplete) $ do
done change mcache file key = liftAnnex $ do
logStatus key InfoPresent
link <- ifM isDirect
- ( inRepo $ gitAnnexLink file key
+ ( calcRepo $ gitAnnexLink file key
, Command.Add.link file key mcache
)
whenM (pure DirWatcher.eventsCoalesce <||> isDirect) $
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
index ca9de6836..6f3afa8ca 100644
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -232,7 +232,7 @@ onAddDirect symlinkssupported matcher file fs = do
- so it symlink is restaged to make sure. -}
( ifM (shouldRestage <$> getDaemonStatus)
( do
- link <- liftAnnex $ inRepo $ gitAnnexLink file key
+ link <- liftAnnex $ calcRepo $ gitAnnexLink file key
addLink file link (Just key)
, noChange
)
@@ -279,7 +279,7 @@ onAddSymlink' linktarget mk isdirect file filestatus = go mk
go (Just key) = do
when isdirect $
liftAnnex $ void $ addAssociatedFile key file
- link <- liftAnnex $ inRepo $ gitAnnexLink file key
+ link <- liftAnnex $ calcRepo $ gitAnnexLink file key
if linktarget == Just link
then ensurestaged (Just link) =<< getDaemonStatus
else do
diff --git a/Command/Add.hs b/Command/Add.hs
index 2671126ca..51921fbc3 100644
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -230,7 +230,7 @@ undo file key e = do
{- Creates the symlink to the annexed content, returns the link target. -}
link :: FilePath -> Key -> Maybe InodeCache -> Annex String
link file key mcache = flip catchNonAsync (undo file key) $ do
- l <- inRepo $ gitAnnexLink file key
+ l <- calcRepo $ gitAnnexLink file key
replaceFile file $ makeAnnexLink l
-- touch symlink to have same time as the original file,
@@ -272,7 +272,7 @@ cleanup :: FilePath -> Key -> Maybe InodeCache -> Bool -> CommandCleanup
cleanup file key mcache hascontent = do
ifM (isDirect <&&> pure hascontent)
( do
- l <- inRepo $ gitAnnexLink file key
+ l <- calcRepo $ gitAnnexLink file key
stageSymlink file =<< hashSymlink l
, addLink file key mcache
)
diff --git a/Command/Fix.hs b/Command/Fix.hs
index 1ef15d0eb..c981d23ae 100644
--- a/Command/Fix.hs
+++ b/Command/Fix.hs
@@ -28,7 +28,7 @@ seek = withFilesInGit $ whenAnnexed start
{- Fixes the symlink to an annexed file. -}
start :: FilePath -> Key -> CommandStart
start file key = do
- link <- inRepo $ gitAnnexLink file key
+ link <- calcRepo $ gitAnnexLink file key
stopUnless ((/=) (Just link) <$> liftIO (catchMaybeIO $ readSymbolicLink file)) $ do
showStart "fix" file
next $ perform file link
diff --git a/Command/FromKey.hs b/Command/FromKey.hs
index 14a3d8176..d9ebe6b4c 100644
--- a/Command/FromKey.hs
+++ b/Command/FromKey.hs
@@ -33,7 +33,7 @@ start _ = error "specify a key and a dest file"
perform :: Key -> FilePath -> CommandPerform
perform key file = do
- link <- inRepo $ gitAnnexLink file key
+ link <- calcRepo $ gitAnnexLink file key
liftIO $ createDirectoryIfMissing True (parentDir file)
liftIO $ createSymbolicLink link file
next $ cleanup file
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
index 46fe983d8..0242a1f75 100644
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -192,7 +192,7 @@ check cs = and <$> sequence cs
-}
fixLink :: Key -> FilePath -> Annex Bool
fixLink key file = do
- want <- inRepo $ gitAnnexLink file key
+ want <- calcRepo $ gitAnnexLink file key
have <- getAnnexLinkTarget file
maybe noop (go want) have
return True
diff --git a/Command/Indirect.hs b/Command/Indirect.hs
index 34a2ef8c9..1d703d2f3 100644
--- a/Command/Indirect.hs
+++ b/Command/Indirect.hs
@@ -88,7 +88,7 @@ perform = do
v <- tryNonAsync (moveAnnex k f)
case v of
Right _ -> do
- l <- inRepo $ gitAnnexLink f k
+ l <- calcRepo $ gitAnnexLink f k
liftIO $ createSymbolicLink l f
Left e -> catchNonAsync (Command.Add.undo f k e)
warnlocked
diff --git a/Locations.hs b/Locations.hs
index 596bf4f85..dcbde4bd9 100644
--- a/Locations.hs
+++ b/Locations.hs
@@ -76,6 +76,7 @@ import Common
import Types
import Types.Key
import Types.UUID
+import Types.Difference
import qualified Git
{- Conventions:
@@ -120,17 +121,22 @@ annexLocation key hasher = objectDir </> keyPath key hasher
- the actual location of the file's content.
-}
gitAnnexLocation :: Key -> Git.Repo -> GitConfig -> IO FilePath
-gitAnnexLocation key r config = gitAnnexLocation' key r (annexCrippledFileSystem config)
-gitAnnexLocation' :: Key -> Git.Repo -> Bool -> IO FilePath
-gitAnnexLocation' key r crippled
+gitAnnexLocation key r config = gitAnnexLocation' key r config (annexCrippledFileSystem config)
+gitAnnexLocation' :: Key -> Git.Repo -> GitConfig -> Bool -> IO FilePath
+gitAnnexLocation' key r config crippled
{- Bare repositories default to hashDirLower for new
- content, as it's more portable.
-
- Repositories on filesystems that are crippled also use
- hashDirLower, since they do not use symlinks and it's
- - more portable. -}
- | Git.repoIsLocalBare r || crippled =
- check $ map inrepo $ annexLocations key
+ - more portable.
+ -
+ - ObjectHashLower can also be set to force it.
+ -}
+ | Git.repoIsLocalBare r
+ || crippled
+ || hasDifference (== ObjectHashLower True) (annexDifferences config) =
+ check $ map inrepo $ annexLocations key
{- Non-bare repositories only use hashDirMixed, so
- don't need to do any work to check if the file is
- present. -}
@@ -141,11 +147,11 @@ gitAnnexLocation' key r crippled
check [] = error "internal"
{- Calculates a symlink to link a file to an annexed object. -}
-gitAnnexLink :: FilePath -> Key -> Git.Repo -> IO FilePath
-gitAnnexLink file key r = do
+gitAnnexLink :: FilePath -> Key -> Git.Repo -> GitConfig -> IO FilePath
+gitAnnexLink file key r config = do
currdir <- getCurrentDirectory
let absfile = fromMaybe whoops $ absNormPathUnix currdir file
- loc <- gitAnnexLocation' key r False
+ loc <- gitAnnexLocation' key r config False
relPathDirToFile (parentDir absfile) loc
where
whoops = error $ "unable to normalize " ++ file
diff --git a/Logs.hs b/Logs.hs
index 7452339cd..8d8ae993b 100644
--- a/Logs.hs
+++ b/Logs.hs
@@ -39,6 +39,7 @@ topLevelUUIDBasedLogs =
, preferredContentLog
, requiredContentLog
, scheduleLog
+ , differenceLog
]
{- All the ways to get a key from a presence log file -}
@@ -82,6 +83,9 @@ groupPreferredContentLog = "group-preferred-content.log"
scheduleLog :: FilePath
scheduleLog = "schedule.log"
+differenceLog :: FilePath
+differenceLog = "difference.log"
+
{- The pathname of the location log file for a given key. -}
locationLogFile :: Key -> String
locationLogFile key = hashDirLower key ++ keyFile key ++ ".log"
diff --git a/Logs/Difference.hs b/Logs/Difference.hs
new file mode 100644
index 000000000..68d624f99
--- /dev/null
+++ b/Logs/Difference.hs
@@ -0,0 +1,40 @@
+{- git-annex difference log
+ -
+ - Copyright 2015 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Logs.Difference (
+ recordDifferences,
+ recordedDifferences,
+ recordedDifferencesFor,
+ module Logs.Difference.Pure
+) where
+
+import Data.Monoid
+import Data.Time.Clock.POSIX
+import qualified Data.Map as M
+
+import Common.Annex
+import Types.Difference
+import qualified Annex.Branch
+import Logs
+import Logs.UUIDBased
+import Logs.Difference.Pure
+
+recordDifferences :: Differences -> UUID -> Annex ()
+recordDifferences differences uuid = do
+ ts <- liftIO getPOSIXTime
+ Annex.Branch.change differenceLog $
+ showLog id . changeLog ts uuid (show differences) . parseLog Just
+
+-- Map of UUIDs that have Differences recorded.
+-- If a new version of git-annex has added a Difference this version
+-- doesn't know about, it will contain UnknownDifferences.
+recordedDifferences :: Annex (M.Map UUID Differences)
+recordedDifferences = parseDifferencesLog <$> Annex.Branch.get differenceLog
+
+recordedDifferencesFor :: UUID -> Annex Differences
+recordedDifferencesFor u = fromMaybe mempty . M.lookup u
+ <$> recordedDifferences
diff --git a/Logs/Difference/Pure.hs b/Logs/Difference/Pure.hs
new file mode 100644
index 000000000..76d995a01
--- /dev/null
+++ b/Logs/Difference/Pure.hs
@@ -0,0 +1,26 @@
+{- git-annex difference log, pure functions
+ -
+ - Copyright 2015 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Logs.Difference.Pure (
+ allDifferences,
+ parseDifferencesLog,
+) where
+
+import Data.Monoid
+import qualified Data.Map as M
+
+import Common.Annex
+import Types.Difference
+import Logs.UUIDBased
+
+parseDifferencesLog :: String -> (M.Map UUID Differences)
+parseDifferencesLog = simpleMap
+ . parseLog (Just . fromMaybe UnknownDifferences . readish)
+
+-- The sum of all recorded differences, across all UUIDs.
+allDifferences :: M.Map UUID Differences -> Differences
+allDifferences = mconcat . M.elems
diff --git a/Types/Difference.hs b/Types/Difference.hs
new file mode 100644
index 000000000..cbfad0fce
--- /dev/null
+++ b/Types/Difference.hs
@@ -0,0 +1,135 @@
+{- git-annex repository differences
+ -
+ - Copyright 2015 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Types.Difference (
+ Difference(..),
+ Differences(..),
+ getDifferences,
+ sanityCheckDifferences,
+ differenceConfigKey,
+ differenceConfigVal,
+ hasDifference,
+) where
+
+import Utility.PartialPrelude
+import qualified Git
+import qualified Git.Config
+
+import Data.List
+import Data.Maybe
+import Data.Monoid
+import Control.Applicative
+
+-- Describes differences from the v5 repository format.
+--
+-- The serilization is stored in difference.log, so avoid changes that
+-- would break compatability.
+--
+-- Not breaking comparability is why a list of Differences is used, rather
+-- than a sum type. With a sum type, adding a new field for some future
+-- difference would serialize to a value that an older version could not
+-- parse, even if that new field was not used. With the Differences list,
+-- old versions can still parse it, unless the new Difference constructor
+-- is used.
+data Difference
+ = Version Int
+ | ObjectHashLower Bool
+ | ObjectHashDirectories Int
+ | BranchHashDirectories Int
+ deriving (Show, Read, Ord)
+
+instance Eq Difference where
+ Version a == Version b = a == b
+ ObjectHashLower a == ObjectHashLower b = a == b
+ ObjectHashDirectories a == ObjectHashDirectories b = a == b
+ BranchHashDirectories a == BranchHashDirectories b = a == b
+ _ == _ = False
+
+data Differences
+ = Differences [Difference]
+ | UnknownDifferences
+ deriving (Show, Read, Ord)
+
+instance Eq Differences where
+ Differences a == Differences b = simplify (defver:a) == simplify (defver:b)
+ _ == _ = False
+
+instance Monoid Differences where
+ mempty = Differences []
+ mappend (Differences l1) (Differences l2) = Differences (simplify (l1 ++ l2))
+ mappend _ _ = UnknownDifferences
+
+-- This is the default repository version that is assumed when no other one
+-- is given. Note that [] == [Version 5]
+defver :: Difference
+defver = Version 5
+
+-- Larger values of the same Difference constructor dominate
+-- over smaller values, so given [Version 6, Version 5], returns [Version 6]
+simplify :: [Difference] -> [Difference]
+simplify = go . sort
+ where
+ go [] = []
+ go (d:[]) = [d]
+ go (d1:d2:ds)
+ | like d1 d2 = go (d2:ds)
+ | otherwise = d1 : go (d2:ds)
+
+ like (Version _) (Version _) = True
+ like (ObjectHashLower _) (ObjectHashLower _) = True
+ like (ObjectHashDirectories _) (ObjectHashDirectories _) = True
+ like (BranchHashDirectories _) (BranchHashDirectories _) = True
+ like _ _ = False
+
+getDifferences :: Git.Repo -> Differences
+getDifferences r = checksane $ Differences $ catMaybes
+ [ ObjectHashLower
+ <$> getmaybebool (differenceConfigKey (ObjectHashLower undefined))
+ , ObjectHashDirectories
+ <$> getmayberead (differenceConfigKey (ObjectHashDirectories undefined))
+ , BranchHashDirectories
+ <$> getmayberead (differenceConfigKey (BranchHashDirectories undefined))
+ ]
+ where
+ getmaybe k = Git.Config.getMaybe k r
+ getmayberead k = readish =<< getmaybe k
+ getmaybebool k = Git.Config.isTrue =<< getmaybe k
+ checksane = either error id . sanityCheckDifferences
+
+differenceConfigKey :: Difference -> String
+differenceConfigKey (Version _) = "annex.version"
+differenceConfigKey (ObjectHashLower _) = tunable "objecthashlower"
+differenceConfigKey (ObjectHashDirectories _) = tunable "objecthashdirectories"
+differenceConfigKey (BranchHashDirectories _) = tunable "branchhashdirectories"
+
+differenceConfigVal :: Difference -> String
+differenceConfigVal (Version v) = show v
+differenceConfigVal (ObjectHashLower b) = Git.Config.boolConfig b
+differenceConfigVal (ObjectHashDirectories n) = show n
+differenceConfigVal (BranchHashDirectories n) = show n
+
+tunable :: String -> String
+tunable k = "annex.tune." ++ k
+
+sanityCheckDifferences :: Differences -> Either String Differences
+sanityCheckDifferences d@(Differences l)
+ | null problems = Right d
+ | otherwise = Left (intercalate "; " problems)
+ where
+ problems = catMaybes (map check l)
+ check (ObjectHashDirectories n)
+ | n == 1 || n == 2 = Nothing
+ | otherwise = Just $ "Bad value for objecthashdirectories -- should be 1 or 2, not " ++ show n
+ check (BranchHashDirectories n)
+ | n == 1 || n == 2 = Nothing
+ | otherwise = Just $ "Bad value for branhhashdirectories -- should be 1 or 2, not " ++ show n
+ check _ = Nothing
+sanityCheckDifferences UnknownDifferences = Left "unknown differences detected; update git-annex"
+
+hasDifference :: (Difference -> Bool) -> Differences -> Bool
+hasDifference f (Differences l) = any f l
+hasDifference _ UnknownDifferences = False
diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs
index ef8068cc4..5ac524f45 100644
--- a/Types/GitConfig.hs
+++ b/Types/GitConfig.hs
@@ -20,6 +20,7 @@ import Config.Cost
import Types.Distribution
import Types.Availability
import Types.NumCopies
+import Types.Difference
import Utility.HumanTime
{- Main git-annex settings. Each setting corresponds to a git-config key
@@ -56,6 +57,7 @@ data GitConfig = GitConfig
, annexHardLink :: Bool
, coreSymlinks :: Bool
, gcryptId :: Maybe String
+ , annexDifferences :: Differences
}
extractGitConfig :: Git.Repo -> GitConfig
@@ -93,6 +95,7 @@ extractGitConfig r = GitConfig
, annexHardLink = getbool (annex "hardlink") False
, coreSymlinks = getbool "core.symlinks" True
, gcryptId = getmaybe "core.gcrypt-id"
+ , annexDifferences = getDifferences r
}
where
getbool k def = fromMaybe def $ getmaybebool k
diff --git a/Upgrade/V1.hs b/Upgrade/V1.hs
index 1f0dae43e..e8a7efe13 100644
--- a/Upgrade/V1.hs
+++ b/Upgrade/V1.hs
@@ -92,7 +92,7 @@ updateSymlinks = do
case r of
Nothing -> noop
Just (k, _) -> do
- link <- inRepo $ gitAnnexLink f k
+ link <- calcRepo $ gitAnnexLink f k
liftIO $ removeFile f
liftIO $ createSymbolicLink link f
Annex.Queue.addCommand "add" [Param "--"] [f]
diff --git a/debian/changelog b/debian/changelog
index 0e028afc2..3caa7ddad 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -21,6 +21,11 @@ git-annex (5.20150114) UNRELEASED; urgency=medium
with an existing file. (--file overrides this)
* Fix default repository description created by git annex init,
which got broken by the relative path changes in the last release.
+ * init: Repository tuning parameters can now be passed when initializing a
+ repository for the first time. For details, see
+ http://git-annex.branchable.com/tuning/
+ * merge: Refuse to merge changes from a git-annex branch of a repo
+ that has been tuned in incompatable ways.
-- Joey Hess <id@joeyh.name> Tue, 13 Jan 2015 17:03:39 -0400
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
index c4586ba1d..06eb85e11 100644
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -1839,6 +1839,12 @@ Here are all the supported configuration settings.
Used by hook special remotes and external special remotes to record
the type of the remote.
+* `annex.tune.objecthashdirectories`, `annex.tune.objecthashlower`, `annex.tune.branchhashdirectories`
+
+ These can be passed to `git annex init` to tune the repository.
+ They cannot be safely changed in a running repository.
+ For details, see <http://git-annex.branchable.com/tuning/>.
+
# CONFIGURATION VIA .gitattributes
The key-value backend used when adding a new file to the annex can be
diff --git a/doc/internals.mdwn b/doc/internals.mdwn
index b8cb559b6..4eb72ceac 100644
--- a/doc/internals.mdwn
+++ b/doc/internals.mdwn
@@ -257,3 +257,12 @@ Example:
ForgetGitHistory 1387325539.685136s
ForgetDeadRemotes 1387325539.685136s
+
+## `difference.log`
+
+Used when a repository has fundamental differences from other repositories,
+that should prevent merging.
+
+Example:
+
+ e605dca6-446a-11e0-8b2a-002170d25c55 [Version 5] timestamp=1422387398.30395s
diff --git a/doc/tuning.mdwn b/doc/tuning.mdwn
new file mode 100644
index 000000000..761071b6b
--- /dev/null
+++ b/doc/tuning.mdwn
@@ -0,0 +1,44 @@
+git-annex now has experimental support for tuning a repository for
+different work loads.
+
+For example, a repository with a very large number of files in it may work
+better if git-annex uses some nonstandard hash format, for either the
+`.git/annex/objects/` directory, or for the log files in the git-annex
+branch.
+
+A repository can currently only be tuned when it is first created; this is
+done by passing `-c name=value` parameters to `git annex init`.
+
+For example, this will make git-annex use only 1 level for hash directories
+in `.git/annex/objects`:
+
+ git -c annex.tune.objecthashdirectories=1 annex init
+
+It's very important to keep in mind that this makes a nonstandard format
+git-annex repository. In general, this cannot safely be used with
+git-annex older than version 5.20150128. Older version of git-annex will
+not understand and will get confused and perhaps do bad things.
+
+Also, it's not safe to merge two separate git repositories that have been
+tuned differently (or one tuned and the other one not). git-annex will
+prevent merging their git-annex branches together, but it cannot prevent
+`git merge remote/master` merging two branches, and the result will be ugly
+at best (`git annex fix` can fix up the mess somewhat).
+
+Again, tuned repositories are an experimental feature; use with caution!
+
+The following tuning parameters are available:
+
+* `annex.tune.objecthashdirectories` (default: 2)
+ Sets the number of hash directories to use in `.git/annex/objects/`
+
+* `annex.tune.objecthashlower` (default: false)
+ Set to true to make the hash directories in `.git/annex/objects/` use
+ all lower-case.
+
+* `annex.tune.branchhashdirectories` (default: 2)
+ Sets the number of hash directories to use in the git-annex branch.
+
+Note that git-annex will automatically propigate these setting to
+`.git/config` for tuned repsitories. You should never directly change
+these settings in `.git/config`