aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/git-deli55
-rwxr-xr-xgit-deli64
2 files changed, 91 insertions, 28 deletions
diff --git a/bin/git-deli b/bin/git-deli
index 6cca2cc..ca45002 100755
--- a/bin/git-deli
+++ b/bin/git-deli
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# git-deli.sh: delinearized git workflows helper
#
@@ -11,17 +11,17 @@
OPTS_SPEC="\
git deli head [-b=<branch>] [-m=<msg>] [<commit>] [-s]
-git deli go [-c] [-s] [-b=<branch>] [-m=<msg>] <commit>
+git deli eat [-c] [-s] [-b=<branch>] [-m=<msg>] <commit>
git deli commit ...
git deli boundary [-d] <commit>
git deli merge <commit> ...
-git deli push [-f] <repository> <refspec>
-git deli pull <repository> <ref>
+git deli push [-f] [<repository>] [<refspec>]
+git deli pull [<repository>] [<ref>]
--
common options:
h,help! show the help
m,message!= specify the commit message
- 'head' and 'go' options:
+ 'head' and 'eat' actions:
b,branch!= start a new branch instead of using the target one
s,set-boundary use the <commit> as a boundary for the new head
c,create-head start a new head at <commit> and delinearize into it
@@ -110,7 +110,7 @@ main() {
shift # drop the command, we already got it
case "$arg_command" in
head) cmd_head "$arg_msg" "$arg_branch" "$arg_set_boundary" "$@" ;;
- go) cmd_go "$arg_msg" "$arg_branch" "$@" ;;
+ eat) cmd_eat "$arg_msg" "$arg_branch" "$@" ;;
commit) cmd_commit "$arg_msg" "$@" ;;
boundary) cmd_boundary "$arg_delete" "$@" ;;
merge) cmd_merge "$@" ;;
@@ -156,14 +156,16 @@ cmd_head () {
git reset --soft "$new_head" || die "git-reset failed to set the new HEAD"
}
-cmd_go () {
+cmd_eat () {
# we're squashing whatever has been done atop of a given head view
+ false
}
cmd_commit () {
msg="$1"
shift
# TODO run a git commit with all extra args, THEN run head with the original commit
+ false
}
cmd_boundary () {
@@ -173,6 +175,7 @@ cmd_boundary () {
commit="$1"
# TODO this adds or removes the "extra parents" to the head commit
+ false
}
cmd_merge () {
@@ -279,6 +282,7 @@ stream_independent_commit_set () {
}
# usage: revision <diff >shas
+# TODO: Binary files (match ^Binary in output, ask for all lines)
deli_diff_to_source_lines () {
awk -f '
BEGIN {
@@ -300,10 +304,40 @@ match($0, /^@@ -([0-9]+),([0-9]+) /, matched) && file!="" {
END {
if(ranges!="") print(ranges, "--", file);
}
- ' | xargs -l git annotate -l "$1" | cut -d'\t' -f1
+ ' \
+ | xargs -l git annotate -l "$1" \
+ | cut -d'\t' -f1
}
-deli_commit () {
+deli_eat_commit () {
+ delihead="$1"
+ target="$2"
+ # TODO insert bounds
+ parents=$( git diff ${DELI_DIFF_ARGS:-} -p "$delihead" "$target" | deli_diff_to_source_lines | sort | uniq | stream_independent_commit_set )
+ # check if the parents are ok
+ for i in parents
+ do git merge-base --is-ancestor "$delihead" "$i" || die "changes in $target are based on $delihead"
+ done
+ set -- $parents
+ echo "sourcing commit $1..." >&2
+ merged_tree="$1^{tree}"
+ shift
+ while test $# -gt 0
+ do
+ echo "merging commit $1..." >&2
+ merged_tree=$( git merge-tree --write-tree "$merged_tree" "$1^{tree}" || die "merging of the parents failed" )
+ shift
+ done
+
+ new_commit=$(
+ ( echo "tree $merged_tree"
+ for i in $parents
+ do echo "parent $i"
+ done
+ git cat-file commit "$target" | sed '/^$/bx; /^tree /d ; /^parent /d ; n ; :x'
+ ) | git hash-object -t commit --stdin -w || die "could not save new commit for tree $merged_tree"
+ )
+
false
}
@@ -317,6 +351,7 @@ deli_commit () {
# Given the heads merge cleanly, the algorithm actually reduces to a very
# simple task:
#
+# - we do a normal merge (this must solve cleanly)
# - all parents from the original 2 commits are combined and reduced to
# independent set
# - all bounds from the original 2 commits are also reduced to an independent
@@ -326,6 +361,8 @@ deli_commit () {
# (TODO does it make sense to check this?)
# - we write out the new commit with all parents and all bounds
#
+# TODO tbh this should be the very same as doing the `eat` operation after a
+# merge, but the eat would need to handle multiple parents correctly.
deli_merge () {
false
diff --git a/git-deli b/git-deli
index 8f0165d..beca6a8 100755
--- a/git-deli
+++ b/git-deli
@@ -102,8 +102,8 @@ indepCommits = go []
$ "git-merge-base failed on " ++ show (acc, take batch xs)
go (lines out) (drop batch xs)
-diffToSources :: String -> String -> IO [String]
-diffToSources base commit = do
+diffToSources :: String -> String -> String -> IO [String]
+diffToSources base commit bottom = do
git <- gitProg
(out, st) <-
withCreateProcess
@@ -115,7 +115,7 @@ diffToSources base commit = do
bis <-
fmap (uniq . sort . uniq . concat) . traverse (blameItem base) . sourceItems
$ lines out
- indepCommits bis
+ indepCommits (bottom:bis)
makeEmptyTree :: IO String
makeEmptyTree = do
@@ -172,7 +172,7 @@ mergedCommitsTree (c:cs) = go c c cs
_ -> fail "merge base output?"
(out, st) <-
withCreateProcess
- (proc git ["merge-tree", "--write-tree", "--merge-base=" ++ b', t, c1])
+ (proc git ["merge-tree", "--write-tree", "-Xno-renames", "--merge-base=" ++ b', t, c1])
{std_in = NoStream, std_out = CreatePipe} $ \_ (Just oh) _ p ->
(,) <$> hGetContents' oh <*> waitForProcess p
unless (st == ExitSuccess) . fail
@@ -183,9 +183,9 @@ mergedCommitsTree (c:cs) = go c c cs
_ -> fail "merge-tree output??"
go b' t' cs
-delinearize :: String -> String -> IO String
-delinearize head commit = do
- parents <- diffToSources head commit
+delinearize :: String -> String -> String -> IO String
+delinearize head commit bottom = do
+ parents <- diffToSources head commit bottom
t <- mergedCommitsTree parents
git <- gitProg
-- create a new tree which is essentially commit-head+mergedtree
@@ -193,10 +193,10 @@ delinearize head commit = do
withCreateProcess
(proc
git
- ["merge-tree", "--write-tree", "--merge-base=" ++ head, t, commit])
+ ["merge-tree", "--write-tree", "-Xno-renames", "--merge-base=" ++ head, t, commit])
{std_in = NoStream, std_out = CreatePipe} $ \_ (Just oh) _ p ->
(,) <$> hGetContents' oh <*> waitForProcess p
- unless (st == ExitSuccess) . fail $ "final git-merge-tree failed"
+ unless (st == ExitSuccess) . fail $ "final git-merge-tree failed" ++ show (head,t,commit)
t' <-
case lines out of
[x] -> pure x
@@ -210,24 +210,50 @@ delinearize head commit = do
unless (st == ExitSuccess) . fail $ "git-cat-file failed"
let (msgHead, msgBody) = break null $ lines commitMsg'
commitMsg =
- unlines
- $ ["tree " ++ t']
- ++ map ("parent " ++) parents
- ++ filter ((/= ["parent"]) . take 1 . words) msgHead
- ++ msgBody
+ unlines $ concat
+ [["tree " ++ t'],
+ map ("parent " ++) parents
+ , filter (not . flip elem [["parent"], ["tree"]] . take 1 . words) msgHead
+ , msgBody ]
-- label the stuff with a proper commit
(out, st) <-
withCreateProcess
(proc git ["hash-object", "-w", "-t", "commit", "--stdin"])
{std_in = CreatePipe, std_out = CreatePipe} $ \(Just ih) (Just oh) _ p ->
- (,) <$> (hPutStr ih commitMsg >> hGetContents' oh) <*> waitForProcess p
+ (,) <$> (hPutStr ih commitMsg >> hClose ih >> hGetContents' oh) <*> waitForProcess p
unless (st == ExitSuccess) . fail $ "git-hash-object final commit failed"
commit' <-
case lines out of
[x] -> pure x
_ -> fail "final has-object returned what?"
- -- move the ref!
- pure commit'
+ -- take the old head
+ (headMsg',st) <-
+ withCreateProcess
+ (proc git ["cat-file", "commit", head])
+ {std_in = CreatePipe, std_out = CreatePipe} $ \(Just ih) (Just oh) _ p ->
+ (,) <$> (hPutStr ih commitMsg >> hClose ih >> hGetContents' oh) <*> waitForProcess p
+ unless (st == ExitSuccess) . fail $ "git-cat-file old head failed"
+ -- make a new one
+ let (hHead, hBody) = break null $ lines headMsg'
+ getP ["parent",x] = [x]
+ getP _ = []
+ hParents <- indepCommits . (commit':) $ concatMap (getP.words) hHead
+ let headMsg = unlines $ concat
+ [ filter ((==["tree"]) . take 1 . words) msgHead
+ , map ("parent "++) hParents
+ , filter (not . flip elem [["parent"], ["tree"]] . take 1 . words) hHead
+ , hBody ]
+ -- write the new head
+ (out, st) <-
+ withCreateProcess
+ (proc git ["hash-object", "-w", "-t", "commit", "--stdin"])
+ {std_in = CreatePipe, std_out = CreatePipe} $ \(Just ih) (Just oh) _ p ->
+ (,) <$> (hPutStr ih headMsg >> hClose ih >> hGetContents' oh) <*> waitForProcess p
+ unless (st == ExitSuccess) . fail $ "git-hash-object new head commit failed"
+ hcommit' <-
+ case lines out of
+ [x] -> pure x
+ _ -> fail "hash-object new head returned what?"
+ pure hcommit'
-main = do
- diffToSources "HEAD^" "HEAD"
+main = undefined