add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
{- git-union-merge program
|
|
|
|
-
|
|
|
|
- Copyright 2011 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
import System.Environment
|
|
|
|
import System.FilePath
|
|
|
|
import System.Directory
|
|
|
|
import System.Cmd.Utils
|
|
|
|
import System.Posix.Env (setEnv)
|
2011-06-21 01:35:39 +00:00
|
|
|
import Control.Monad (when)
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
import Data.List
|
2011-06-21 01:35:39 +00:00
|
|
|
import Data.Maybe
|
|
|
|
import Data.String.Utils
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
|
|
|
|
import qualified GitRepo as Git
|
|
|
|
import Utility
|
|
|
|
|
|
|
|
header :: String
|
2011-06-21 02:21:02 +00:00
|
|
|
header = "Usage: git-union-merge ref ref newref"
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
|
|
|
|
usage :: IO a
|
|
|
|
usage = error $ "bad parameters\n\n" ++ header
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
2011-06-21 01:42:17 +00:00
|
|
|
[aref, bref, newref] <- parseArgs
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
g <- setup
|
|
|
|
stage g aref bref
|
2011-06-21 01:42:17 +00:00
|
|
|
commit g aref bref newref
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
cleanup g
|
|
|
|
|
|
|
|
parseArgs :: IO [String]
|
|
|
|
parseArgs = do
|
|
|
|
args <- getArgs
|
|
|
|
if (length args /= 3)
|
|
|
|
then usage
|
|
|
|
else return args
|
|
|
|
|
|
|
|
tmpIndex :: Git.Repo -> FilePath
|
2011-06-21 01:35:39 +00:00
|
|
|
tmpIndex g = Git.workTree g </> Git.gitDir g </> "index.git-union-merge"
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
|
2011-06-21 01:35:39 +00:00
|
|
|
{- Configures git to use a temporary index file. -}
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
setup :: IO Git.Repo
|
|
|
|
setup = do
|
|
|
|
g <- Git.configRead =<< Git.repoFromCwd
|
|
|
|
cleanup g -- idempotency
|
|
|
|
setEnv "GIT_INDEX_FILE" (tmpIndex g) True
|
|
|
|
return g
|
|
|
|
|
|
|
|
cleanup :: Git.Repo -> IO ()
|
|
|
|
cleanup g = do
|
|
|
|
e' <- doesFileExist (tmpIndex g)
|
|
|
|
when e' $ removeFile (tmpIndex g)
|
|
|
|
|
|
|
|
{- Stages the content of both refs into the index. -}
|
|
|
|
stage :: Git.Repo -> String -> String -> IO ()
|
|
|
|
stage g aref bref = do
|
2011-06-21 01:35:39 +00:00
|
|
|
-- Get the contents of aref, as a starting point.
|
|
|
|
ls <- fromgit
|
|
|
|
["ls-tree", "-z", "-r", "--full-tree", aref]
|
|
|
|
-- Identify files that are different between aref and bref, and
|
|
|
|
-- inject merged versions into git.
|
|
|
|
diff <- fromgit
|
|
|
|
["diff-tree", "--raw", "-z", "-r", "--no-renames", "-l0", aref, bref]
|
|
|
|
ls' <- mapM mergefile (pairs diff)
|
|
|
|
-- Populate the index file. Later lines override earlier ones.
|
|
|
|
togit ["update-index", "-z", "--index-info"]
|
|
|
|
(join "\0" $ ls++catMaybes ls')
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
where
|
2011-06-21 01:35:39 +00:00
|
|
|
fromgit l = Git.pipeNullSplit g (map Param l)
|
|
|
|
togit l content = Git.pipeWrite g (map Param l) content
|
|
|
|
>>= forceSuccess
|
|
|
|
tofromgit l content = do
|
|
|
|
(h, s) <- Git.pipeWriteRead g (map Param l) content
|
|
|
|
length s `seq` do
|
|
|
|
forceSuccess h
|
|
|
|
Git.reap
|
|
|
|
return ((), s)
|
|
|
|
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
pairs [] = []
|
|
|
|
pairs (_:[]) = error "parse error"
|
|
|
|
pairs (a:b:rest) = (a,b):pairs rest
|
2011-06-21 01:35:39 +00:00
|
|
|
|
|
|
|
nullsha = take shaSize $ repeat '0'
|
|
|
|
ls_tree_line sha file = "100644 blob " ++ sha ++ "\t" ++ file
|
|
|
|
unionmerge = unlines . nub . lines
|
|
|
|
|
|
|
|
mergefile (info, file) = do
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
let [_colonamode, _bmode, asha, bsha, _status] = words info
|
2011-06-21 01:35:39 +00:00
|
|
|
if bsha == nullsha
|
|
|
|
then return Nothing -- already staged from aref
|
|
|
|
else mergefile' file asha bsha
|
|
|
|
mergefile' file asha bsha = do
|
|
|
|
let shas = filter (/= nullsha) [asha, bsha]
|
|
|
|
content <- Git.pipeRead g $ map Param ("show":shas)
|
|
|
|
sha <- getSha "hash-object" $
|
|
|
|
tofromgit ["hash-object", "-w", "--stdin"] $
|
|
|
|
unionmerge content
|
|
|
|
return $ Just $ ls_tree_line sha file
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
|
|
|
|
{- Commits the index into the specified branch. -}
|
|
|
|
commit :: Git.Repo -> String -> String -> String -> IO ()
|
2011-06-21 01:42:17 +00:00
|
|
|
commit g aref bref newref = do
|
2011-06-21 01:35:39 +00:00
|
|
|
tree <- getSha "write-tree" $
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
pipeFrom "git" ["write-tree"]
|
2011-06-21 01:35:39 +00:00
|
|
|
sha <- getSha "commit-tree" $
|
add git-union-merge
This is a new git subcommand, that does a generic union merge operation
between two refs, storing the result in a branch. It operates efficiently
without touching the working tree. It does need to write out a temporary
index file, and may need to write out some other temp files as well.
This could be useful for anything that stores data in a branch,
and needs to merge changes into that branch without actually checking the
branch out. Since conflict handling can't be done without a working copy,
the merge type is always a union merge, which is fine for data stored in
log format (as git-annex does), or in non-conflicting files
(as pristine-tar does).
This probably belongs in git proper, but it will live in git-annex for now.
---
Plan is to move .git-annex/ to a git-annex branch, and use git-union-merge
to handle merging changes when pulling from remotes.
Some preliminary benchmarking using real .git-annex/ data indicates
that it's quite fast, except for the "git add" call, which is as slow
as "git add" tends to be with a big index.
2011-06-20 23:44:45 +00:00
|
|
|
pipeBoth "git" ["commit-tree", tree, "-p", aref, "-p", bref]
|
|
|
|
"union merge"
|
2011-06-21 01:42:17 +00:00
|
|
|
Git.run g "update-ref" [Param newref, Param sha]
|
2011-06-21 01:35:39 +00:00
|
|
|
|
|
|
|
{- Runs an action that causes a git subcommand to emit a sha, and strips
|
|
|
|
any trailing newline, returning the sha. -}
|
|
|
|
getSha :: String -> IO (a, String) -> IO String
|
|
|
|
getSha subcommand a = do
|
|
|
|
(_, t) <- a
|
|
|
|
let t' = if last t == '\n'
|
|
|
|
then take (length t - 1) t
|
|
|
|
else t
|
|
|
|
when (length t' /= shaSize) $
|
|
|
|
error $ "failed to read sha from git " ++ subcommand ++ " (" ++ t' ++ ")"
|
|
|
|
return t'
|
|
|
|
|
|
|
|
shaSize :: Int
|
|
|
|
shaSize = 40
|