git-subtree.shon commit Add a new --rejoin option. (b77172f)
   1#!/bin/bash
   2#
   3# git-subtree.sh: split/join git repositories in subdirectories of this one
   4#
   5# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
   6#
   7OPTS_SPEC="\
   8git subtree split [--rejoin] [--onto rev] <commit...> -- <path>
   9git subtree merge 
  10
  11git subtree does foo and bar!
  12--
  13h,help   show the help
  14q        quiet
  15v        verbose
  16onto=    existing subtree revision to connect, if any
  17rejoin   merge the new branch back into HEAD
  18"
  19eval $(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)
  20. git-sh-setup
  21require_work_tree
  22
  23quiet=
  24command=
  25onto=
  26rejoin=
  27
  28debug()
  29{
  30        if [ -z "$quiet" ]; then
  31                echo "$@" >&2
  32        fi
  33}
  34
  35assert()
  36{
  37        if "$@"; then
  38                :
  39        else
  40                die "assertion failed: " "$@"
  41        fi
  42}
  43
  44
  45#echo "Options: $*"
  46
  47while [ $# -gt 0 ]; do
  48        opt="$1"
  49        debug "option: $1"
  50        shift
  51        case "$opt" in
  52                -q) quiet=1 ;;
  53                --onto) onto="$1"; shift ;;
  54                --rejoin) rejoin=1 ;;
  55                --) break ;;
  56        esac
  57done
  58
  59command="$1"
  60shift
  61case "$command" in
  62        split|merge) ;;
  63        *) die "Unknown command '$command'" ;;
  64esac
  65
  66revs=$(git rev-parse --default HEAD --revs-only "$@") || exit $?
  67dirs="$(git rev-parse --sq --no-revs --no-flags "$@")" || exit $?
  68
  69#echo "dirs is {$dirs}"
  70eval $(echo set -- $dirs)
  71if [ "$#" -ne 1 ]; then
  72        die "Must provide exactly one subtree dir (got $#)"
  73fi
  74dir="$1"
  75
  76debug "command: {$command}"
  77debug "quiet: {$quiet}"
  78debug "revs: {$revs}"
  79debug "dir: {$dir}"
  80
  81cache_setup()
  82{
  83        cachedir="$GIT_DIR/subtree-cache/$$"
  84        rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir"
  85        mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir"
  86        debug "Using cachedir: $cachedir" >&2
  87}
  88
  89cache_get()
  90{
  91        for oldrev in $*; do
  92                if [ -r "$cachedir/$oldrev" ]; then
  93                        read newrev <"$cachedir/$oldrev"
  94                        echo $newrev
  95                fi
  96        done
  97}
  98
  99cache_set()
 100{
 101        oldrev="$1"
 102        newrev="$2"
 103        if [ "$oldrev" != "latest_old" \
 104             -a "$oldrev" != "latest_new" \
 105             -a -e "$cachedir/$oldrev" ]; then
 106                die "cache for $oldrev already exists!"
 107        fi
 108        echo "$newrev" >"$cachedir/$oldrev"
 109}
 110
 111copy_commit()
 112{
 113        # We're doing to set some environment vars here, so
 114        # do it in a subshell to get rid of them safely later
 115        git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" |
 116        (
 117                read GIT_AUTHOR_NAME
 118                read GIT_AUTHOR_EMAIL
 119                read GIT_AUTHOR_DATE
 120                read GIT_COMMITTER_NAME
 121                read GIT_COMMITTER_EMAIL
 122                read GIT_COMMITTER_DATE
 123                export  GIT_AUTHOR_NAME \
 124                        GIT_AUTHOR_EMAIL \
 125                        GIT_AUTHOR_DATE \
 126                        GIT_COMMITTER_NAME \
 127                        GIT_COMMITTER_EMAIL \
 128                        GIT_COMMITTER_DATE
 129                git commit-tree "$2" $3  # reads the rest of stdin
 130        ) || die "Can't copy commit $1"
 131}
 132
 133merge_msg()
 134{
 135        dir="$1"
 136        latest_old="$2"
 137        latest_new="$3"
 138        cat <<-EOF
 139                Split changes from '$dir/' into commit '$latest_new'
 140                
 141                git-subtree-dir: $dir
 142                git-subtree-includes: $latest_old
 143        EOF
 144}
 145
 146cmd_split()
 147{
 148        debug "Splitting $dir..."
 149        cache_setup || exit $?
 150        
 151        git rev-list --reverse --parents $revs -- "$dir" |
 152        while read rev parents; do
 153                newparents=$(cache_get $parents)
 154                debug
 155                debug "Processing commit: $rev / $newparents"
 156                
 157                git ls-tree $rev -- "$dir" |
 158                while read mode type tree name; do
 159                        assert [ "$name" = "$dir" ]
 160                        debug "  tree is: $tree"
 161                        p=""
 162                        for parent in $newparents; do
 163                                p="$p -p $parent"
 164                        done
 165                        
 166                        newrev=$(copy_commit $rev $tree "$p") || exit $?
 167                        debug "  newrev is: $newrev"
 168                        cache_set $rev $newrev
 169                        cache_set latest_new $newrev
 170                        cache_set latest_old $rev
 171                done || exit $?
 172        done || exit $?
 173        latest_new=$(cache_get latest_new)
 174        if [ -z "$latest_new" ]; then
 175                die "No new revisions were found"
 176        fi
 177        
 178        if [ -n "$rejoin" ]; then
 179                debug "Merging split branch into HEAD..."
 180                latest_old=$(cache_get latest_old)
 181                git merge -s ours \
 182                        -m "$(merge_msg $dir $latest_old $latest_new)" \
 183                        $latest_new
 184        fi
 185        echo $latest_new
 186        exit 0
 187}
 188
 189cmd_merge()
 190{
 191        die "merge command not implemented yet"
 192}
 193
 194"cmd_$command"