diff options
| -rwxr-xr-x | bin/git-deli | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/bin/git-deli b/bin/git-deli new file mode 100755 index 0000000..6a00b40 --- /dev/null +++ b/bin/git-deli @@ -0,0 +1,140 @@ +#!/bin/sh +# +# git-deli.sh: delinearized git workflows helper +# +# Copytight (C) 2025 Mirek Kratochvil <exa.exa@gmail.com> +# + +OPTS_SPEC="\ +git deli head [-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> +-- +h,help! show the help +m,message!= specify the commit message + 'head' options: +b,branch!= start a new branch instead of using the target one + 'boundary' options: +d,delete delete the boundary commit instead of adding it + 'push' options: +f,force internally do a true force-push instead of the safe force-with-lease +" + +main() { + if test $# -eq 0 + then set -- -h + fi + + set_args="$(echo "$OPTS_SPEC" | git-rev-parse --parseopt --stuck-long -- "$@" || echo exit $?)" + eval "$set_args" + . git-sh-setup + require_work_tree + + # find the command right away (TODO: actually validate if the options + # belong to the given command later) + while test $# -gt 0 + do + opt="$1" + shift + [ "$opt" = "--" ] && break + done + arg_command=$1 + eval "$set_args" + + arg_msg= + arg_branch= + arg_all= + arg_patch= + arg_delete= + arg_force= + while test $# -gt 0 + do + opt="$1" + shift + case "$opt" in + --message=*) + [ -z "$arg_msg" ] || die "conflicting options for --message"] + arg_msg="${opt#*=}" ;; + --branch=*) + [ -z "$arg_branch" ] || die "conflicting options for --branch"] + arg_branch="${opt#*=}" ;; + --delete) + [ -z "$arg_delete" ] || die "conflicting options for --delete"] + arg_delete=yes ;; + --no-delete) + [ -z "$arg_delete" ] || die "conflicting options for --delete"] + arg_delete=no ;; + --force) + [ -z "$arg_force" ] || die "conflicting options for --force"] + arg_force=yes ;; + --no-force) + [ -z "$arg_force" ] || die "conflicting options for --force"] + arg_force=no ;; + --) + break ;; + *) + die "fatal: unexpected option: $opt" + esac + done + + shift # drop the command, we already got it + case "$arg_command" in + head) cmd_head "$arg_msg" "$arg_branch" "$@" ;; + commit) cmd_commit "$arg_msg" "$@" ;; + boundary) cmd_boundary "$arg_delete" "$@" ;; + merge) cmd_merge "$@" ;; + push) cmd_push "$arg_force" "$@" ;; + pull) cmd_pull "$@" ;; + *) + die "fatal: unknonwn command: $arg_command" ;; + esac +} + +cmd_head () { + msg="$1" + branch="$2" + # TODO one extra optional commit arg + # if the arg is there, this is "rebase" mode that picks up a deli view + # from the history and separates the commits into branches below it + # + # if it's not there, we make a new head on the current branch, possibly + # making a new one in the process +} + +cmd_commit () { + msg="$1" + shift + # TODO run a git commit with all extra args, THEN run head with the original commit +} + +cmd_boundary () { + delete="$1" + shift + [ $# -eq 1 ] || die "specify one boundary commit" + commit="$1" + + # TODO this adds or removes the "extra parents" to the head commit +} + +cmd_merge () { + # TODO this should merge multiple "head" commits + # notably, there must not be any actual merging involved -- there must + # be no conflicts etc. What happens is that, quite simply, the parents + # of the new head become all (latest) parents of all original heads. If + # there is any actual merging required, it must be resolved elsewhere + # and recorded in the history so that branches merge cleanly. +} + +cmd_push () { + # TODO like git-push but actually force-pushes the current multihead. + # Uses a force-with-lease by default. +} + +cmd_pull () { + # TODO like a normal git fetch followed by git deli merge +} + +main "$@" |
