Merge branch 'js/maint-rebase-i-submodule'
authorJunio C Hamano <gitster@pobox.com>
Sun, 1 Feb 2009 02:07:55 +0000 (18:07 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 1 Feb 2009 02:07:55 +0000 (18:07 -0800)
* js/maint-rebase-i-submodule:
Fix submodule squashing into unrelated commit
rebase -i squashes submodule changes into unrelated commit

1  2 
git-rebase--interactive.sh
t/t3404-rebase-interactive.sh
index 1438650ae8a2ec71615821168970936b9d170f35,1ceb57ae8302dc3f0673779cf60f70df5c26a64d..3dc659dd5896ce6c887eb786718919aef82f4ac6
@@@ -27,7 -27,6 +27,7 @@@ continue           continue rebasing pr
  abort              abort rebasing process and restore original branch
  skip               skip current patch and continue rebasing process
  no-verify          override pre-rebase hook from stopping the operation
 +root               rebase all reachable commmits up to the root(s)
  "
  
  . git-sh-setup
@@@ -45,7 -44,6 +45,7 @@@ STRATEGY
  ONTO=
  VERBOSE=
  OK_TO_SKIP_PRE_REBASE=
 +REBASE_ROOT=
  
  GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
  mark the corrected paths with 'git add <paths>', and
@@@ -156,11 -154,6 +156,11 @@@ pick_one () 
        output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
        test -d "$REWRITTEN" &&
                pick_one_preserving_merges "$@" && return
 +      if test ! -z "$REBASE_ROOT"
 +      then
 +              output git cherry-pick "$@"
 +              return
 +      fi
        parent_sha1=$(git rev-parse --verify $sha1^) ||
                die "Could not get the parent of $sha1"
        current_sha1=$(git rev-parse --verify HEAD)
@@@ -204,11 -197,7 +204,11 @@@ pick_one_preserving_merges () 
  
        # rewrite parents; if none were rewritten, we can fast-forward.
        new_parents=
 -      pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)"
 +      pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
 +      if test "$pend" = " "
 +      then
 +              pend=" root"
 +      fi
        while [ "$pend" != "" ]
        do
                p=$(expr "$pend" : ' \([^ ]*\)')
                        if test -f "$DROPPED"/$p
                        then
                                fast_forward=f
 -                              pend=" $(cat "$DROPPED"/$p)$pend"
 +                              replacement="$(cat "$DROPPED"/$p)"
 +                              test -z "$replacement" && replacement=root
 +                              pend=" $replacement$pend"
                        else
                                new_parents="$new_parents $p"
                        fi
@@@ -373,17 -360,15 +373,15 @@@ do_next () 
                pick_one -n $sha1 || failed=t
                case "$(peek_next_command)" in
                squash|s)
-                       EDIT_COMMIT=
                        USE_OUTPUT=output
                        MSG_OPT=-F
-                       MSG_FILE="$MSG"
+                       EDIT_OR_FILE="$MSG"
                        cp "$MSG" "$SQUASH_MSG"
                        ;;
                *)
-                       EDIT_COMMIT=-e
                        USE_OUTPUT=
                        MSG_OPT=
-                       MSG_FILE=
+                       EDIT_OR_FILE=-e
                        rm -f "$SQUASH_MSG" || exit
                        cp "$MSG" "$GIT_DIR"/SQUASH_MSG
                        rm -f "$GIT_DIR"/MERGE_MSG || exit
                        GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
                        GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
                        GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-                       $USE_OUTPUT git commit --no-verify $MSG_OPT "$MSG_FILE" $EDIT_COMMIT || failed=t
+                       $USE_OUTPUT git commit --no-verify \
+                               $MSG_OPT "$EDIT_OR_FILE" || failed=t
                fi
                if test $failed = t
                then
@@@ -456,7 -442,6 +455,7 @@@ get_saved_options () 
        test -d "$REWRITTEN" && PRESERVE_MERGES=t
        test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
        test -f "$DOTEST"/verbose && VERBOSE=t
 +      test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
  }
  
  while test $# != 0
@@@ -561,9 -546,6 +560,9 @@@ first and then run 'git rebase --contin
        -i)
                # yeah, we know
                ;;
 +      --root)
 +              REBASE_ROOT=t
 +              ;;
        --onto)
                shift
                ONTO=$(git rev-parse --verify "$1") ||
                ;;
        --)
                shift
 -              run_pre_rebase_hook ${1+"$@"}
 -              test $# -eq 1 -o $# -eq 2 || usage
 +              test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
 +              test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
                test -d "$DOTEST" &&
                        die "Interactive rebase already started"
  
                git var GIT_COMMITTER_IDENT >/dev/null ||
                        die "You need to set your committer info first"
  
 +              if test -z "$REBASE_ROOT"
 +              then
 +                      UPSTREAM_ARG="$1"
 +                      UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
 +                      test -z "$ONTO" && ONTO=$UPSTREAM
 +                      shift
 +              else
 +                      UPSTREAM=
 +                      UPSTREAM_ARG=--root
 +                      test -z "$ONTO" &&
 +                              die "You must specify --onto when using --root"
 +              fi
 +              run_pre_rebase_hook "$UPSTREAM_ARG" "$@"
 +
                comment_for_reflog start
  
                require_clean_work_tree
  
 -              UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
 -              test -z "$ONTO" && ONTO=$UPSTREAM
 -
 -              if test ! -z "$2"
 +              if test ! -z "$1"
                then
 -                      output git show-ref --verify --quiet "refs/heads/$2" ||
 -                              die "Invalid branchname: $2"
 -                      output git checkout "$2" ||
 -                              die "Could not checkout $2"
 +                      output git show-ref --verify --quiet "refs/heads/$1" ||
 +                              die "Invalid branchname: $1"
 +                      output git checkout "$1" ||
 +                              die "Could not checkout $1"
                fi
  
                HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
                        echo "detached HEAD" > "$DOTEST"/head-name
  
                echo $HEAD > "$DOTEST"/head
 -              echo $UPSTREAM > "$DOTEST"/upstream
 +              case "$REBASE_ROOT" in
 +              '')
 +                      rm -f "$DOTEST"/rebase-root ;;
 +              *)
 +                      : >"$DOTEST"/rebase-root ;;
 +              esac
                echo $ONTO > "$DOTEST"/onto
                test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
                test t = "$VERBOSE" && : > "$DOTEST"/verbose
                        # This ensures that commits on merged, but otherwise
                        # unrelated side branches are left alone. (Think "X"
                        # in the man page's example.)
 -                      mkdir "$REWRITTEN" &&
 -                      for c in $(git merge-base --all $HEAD $UPSTREAM)
 -                      do
 -                              echo $ONTO > "$REWRITTEN"/$c ||
 +                      if test -z "$REBASE_ROOT"
 +                      then
 +                              mkdir "$REWRITTEN" &&
 +                              for c in $(git merge-base --all $HEAD $UPSTREAM)
 +                              do
 +                                      echo $ONTO > "$REWRITTEN"/$c ||
 +                                              die "Could not init rewritten commits"
 +                              done
 +                      else
 +                              mkdir "$REWRITTEN" &&
 +                              echo $ONTO > "$REWRITTEN"/root ||
                                        die "Could not init rewritten commits"
 -                      done
 +                      fi
                        # No cherry-pick because our first pass is to determine
                        # parents to rewrite and skipping dropped commits would
                        # prematurely end our probe
                        MERGES_OPTION="--no-merges --cherry-pick"
                fi
  
 -              SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
                SHORTHEAD=$(git rev-parse --short $HEAD)
                SHORTONTO=$(git rev-parse --short $ONTO)
 +              if test -z "$REBASE_ROOT"
 +                      # this is now equivalent to ! -z "$UPSTREAM"
 +              then
 +                      SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
 +                      REVISIONS=$UPSTREAM...$HEAD
 +                      SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD
 +              else
 +                      REVISIONS=$ONTO...$HEAD
 +                      SHORTREVISIONS=$SHORTHEAD
 +              fi
                git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
                        --abbrev=7 --reverse --left-right --topo-order \
 -                      $UPSTREAM...$HEAD | \
 +                      $REVISIONS | \
                        sed -n "s/^>//p" | while read shortsha1 rest
                do
                        if test t != "$PRESERVE_MERGES"
                                echo "pick $shortsha1 $rest" >> "$TODO"
                        else
                                sha1=$(git rev-parse $shortsha1)
 -                              preserve=t
 -                              for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
 -                              do
 -                                      if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
 -                                      then
 -                                              preserve=f
 -                                      fi
 -                              done
 +                              if test -z "$REBASE_ROOT"
 +                              then
 +                                      preserve=t
 +                                      for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
 +                                      do
 +                                              if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
 +                                              then
 +                                                      preserve=f
 +                                              fi
 +                                      done
 +                              else
 +                                      preserve=f
 +                              fi
                                if test f = "$preserve"
                                then
                                        touch "$REWRITTEN"/$sha1
                then
                        mkdir "$DROPPED"
                        # Save all non-cherry-picked changes
 -                      git rev-list $UPSTREAM...$HEAD --left-right --cherry-pick | \
 +                      git rev-list $REVISIONS --left-right --cherry-pick | \
                                sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks
                        # Now all commits and note which ones are missing in
                        # not-cherry-picks and hence being dropped
 -                      git rev-list $UPSTREAM..$HEAD |
 +                      git rev-list $REVISIONS |
                        while read rev
                        do
                                if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
                                        # not worthwhile, we don't want to track its multiple heads,
                                        # just the history of its first-parent for others that will
                                        # be rebasing on top of it
 -                                      git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$rev
 +                                      git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev
                                        short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
                                        grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
                                        rm "$REWRITTEN"/$rev
                                fi
                        done
                fi
 +
                test -s "$TODO" || echo noop >> "$TODO"
                cat >> "$TODO" << EOF
  
 -# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
 +# Rebase $SHORTREVISIONS onto $SHORTONTO
  #
  # Commands:
  #  p, pick = use commit
index 3592403af7fd9eef81cfab44944ab2e1464b08ef,4becc5513dc3f46b73fbe8d7ea09a4c2912aa0a2..603b003edff6d32fe8725f119778658c76c806fb
@@@ -10,10 -10,6 +10,10 @@@ that the result still makes sense
  '
  . ./test-lib.sh
  
 +. ../lib-rebase.sh
 +
 +set_fake_editor
 +
  # set up two branches like this:
  #
  # A - B - C - D - E
@@@ -65,6 -61,39 +65,6 @@@ test_expect_success 'setup' 
        git tag I
  '
  
 -echo "#!$SHELL_PATH" >fake-editor.sh
 -cat >> fake-editor.sh <<\EOF
 -case "$1" in
 -*/COMMIT_EDITMSG)
 -      test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
 -      test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
 -      exit
 -      ;;
 -esac
 -test -z "$EXPECT_COUNT" ||
 -      test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
 -      exit
 -test -z "$FAKE_LINES" && exit
 -grep -v '^#' < "$1" > "$1".tmp
 -rm -f "$1"
 -cat "$1".tmp
 -action=pick
 -for line in $FAKE_LINES; do
 -      case $line in
 -      squash|edit)
 -              action="$line";;
 -      *)
 -              echo sed -n "${line}s/^pick/$action/p"
 -              sed -n "${line}p" < "$1".tmp
 -              sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
 -              action=pick;;
 -      esac
 -done
 -EOF
 -
 -test_set_editor "$(pwd)/fake-editor.sh"
 -chmod a+x fake-editor.sh
 -
  test_expect_success 'no changes are a nop' '
        git rebase -i F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
@@@ -433,4 -462,30 +433,30 @@@ test_expect_success 'do "noop" when the
  
  '
  
+ test_expect_success 'submodule rebase setup' '
+       git checkout A &&
+       mkdir sub &&
+       (
+               cd sub && git init && >elif &&
+               git add elif && git commit -m "submodule initial"
+       ) &&
+       echo 1 >file1 &&
+       git add file1 sub
+       test_tick &&
+       git commit -m "One" &&
+       echo 2 >file1 &&
+       test_tick &&
+       git commit -a -m "Two" &&
+       (
+               cd sub && echo 3 >elif &&
+               git commit -a -m "submodule second"
+       ) &&
+       test_tick &&
+       git commit -a -m "Three changes submodule"
+ '
+ test_expect_success 'submodule rebase -i' '
+       FAKE_LINES="1 squash 2 3" git rebase -i A
+ '
  test_done