Merge branch 'ag/rebase-i-in-c'
authorJunio C Hamano <gitster@pobox.com>
Fri, 2 Nov 2018 02:04:53 +0000 (11:04 +0900)
committerJunio C Hamano <gitster@pobox.com>
Fri, 2 Nov 2018 02:04:53 +0000 (11:04 +0900)
Rewrite of the remaining "rebase -i" machinery in C.

* ag/rebase-i-in-c:
rebase -i: move rebase--helper modes to rebase--interactive
rebase -i: remove git-rebase--interactive.sh
rebase--interactive2: rewrite the submodes of interactive rebase in C
rebase -i: implement the main part of interactive rebase as a builtin
rebase -i: rewrite init_basic_state() in C
rebase -i: rewrite write_basic_state() in C
rebase -i: rewrite the rest of init_revisions_and_shortrevisions() in C
rebase -i: implement the logic to initialize $revisions in C
rebase -i: remove unused modes and functions
rebase -i: rewrite complete_action() in C
t3404: todo list with commented-out commands only aborts
sequencer: change the way skip_unnecessary_picks() returns its result
sequencer: refactor append_todo_help() to write its message to a buffer
rebase -i: rewrite checkout_onto() in C
rebase -i: rewrite setup_reflog_action() in C
sequencer: add a new function to silence a command, except if it fails
rebase -i: rewrite the edit-todo functionality in C
editor: add a function to launch the sequence editor
rebase -i: rewrite append_todo_help() in C
sequencer: make three functions and an enum from sequencer.c public

1  2 
.gitignore
Makefile
builtin.h
cache.h
git-legacy-rebase.sh
git-rebase--preserve-merges.sh
git.c
sequencer.c
sequencer.h
strbuf.h
t/t3404-rebase-interactive.sh
diff --combined .gitignore
index 4d5de166e85fb8cf249bff8a15d4d1c7209697bc,406f26d0507961c9e39da636bb87ff62fae6a90b..0d77ea5894274c43c4b348c8b52b8e665a1a339e
@@@ -1,6 -1,3 +1,6 @@@
 +/fuzz_corpora
 +/fuzz-pack-headers
 +/fuzz-pack-idx
  /GIT-BUILD-OPTIONS
  /GIT-CFLAGS
  /GIT-LDFLAGS
@@@ -81,7 -78,6 +81,7 @@@
  /git-init-db
  /git-interpret-trailers
  /git-instaweb
 +/git-legacy-rebase
  /git-log
  /git-ls-files
  /git-ls-remote
  /git-mergetool--lib
  /git-mktag
  /git-mktree
 -/git-name-rev
 +/git-multi-pack-index
  /git-mv
 +/git-name-rev
  /git-notes
  /git-p4
  /git-pack-redundant
  /git-pull
  /git-push
  /git-quiltimport
 +/git-range-diff
  /git-read-tree
  /git-rebase
  /git-rebase--am
- /git-rebase--helper
 +/git-rebase--common
  /git-rebase--interactive
  /git-rebase--merge
  /git-rebase--preserve-merges
  /config.mak.autogen
  /config.mak.append
  /configure
 +/.vscode/
  /tags
  /TAGS
  /cscope*
diff --combined Makefile
index 95b93c709d65f1a3b8a96e5911c33c1322220e8c,ca3a0888ddfdb48e85ed5d785d34d9194ff3c8a1..bbfbb4292d49e99704d75a093a97d432c537cd8d
+++ b/Makefile
@@@ -400,7 -400,7 +400,7 @@@ all:
  # (defaults to "man") if you want to have a different default when
  # "git help" is called without a parameter specifying the format.
  #
 -# Define TEST_GIT_INDEX_VERSION to 2, 3 or 4 to run the test suite
 +# Define GIT_TEST_INDEX_VERSION to 2, 3 or 4 to run the test suite
  # with a different indexfile format version.  If it isn't set the index
  # file format used is index-v[23].
  #
  #        The DEVELOPER mode enables -Wextra with a few exceptions. By
  #        setting this flag the exceptions are removed, and all of
  #        -Wextra is used.
 +#
 +#    pedantic:
 +#
 +#        Enable -pedantic compilation. This also disables
 +#        USE_PARENS_AROUND_GETTEXT_N to produce only relevant warnings.
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -569,7 -564,7 +569,7 @@@ SPATCH = spatc
  export TCL_PATH TCLTK_PATH
  
  SPARSE_FLAGS =
 -SPATCH_FLAGS = --all-includes
 +SPATCH_FLAGS = --all-includes --patch .
  
  
  
@@@ -590,8 -585,6 +590,8 @@@ XDIFF_OBJS 
  VCSSVN_OBJS =
  GENERATED_H =
  EXTRA_CPPFLAGS =
 +FUZZ_OBJS =
 +FUZZ_PROGRAMS =
  LIB_OBJS =
  PROGRAM_OBJS =
  PROGRAMS =
@@@ -616,7 -609,7 +616,7 @@@ SCRIPT_SH += git-merge-one-file.s
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-quiltimport.sh
 -SCRIPT_SH += git-rebase.sh
 +SCRIPT_SH += git-legacy-rebase.sh
  SCRIPT_SH += git-remote-testgit.sh
  SCRIPT_SH += git-request-pull.sh
  SCRIPT_SH += git-stash.sh
@@@ -626,8 -619,6 +626,7 @@@ SCRIPT_SH += git-web--browse.s
  SCRIPT_LIB += git-mergetool--lib
  SCRIPT_LIB += git-parse-remote
  SCRIPT_LIB += git-rebase--am
- SCRIPT_LIB += git-rebase--interactive
 +SCRIPT_LIB += git-rebase--common
  SCRIPT_LIB += git-rebase--preserve-merges
  SCRIPT_LIB += git-rebase--merge
  SCRIPT_LIB += git-sh-setup
@@@ -685,14 -676,6 +684,14 @@@ SCRIPTS = $(SCRIPT_SH_INS) 
  
  ETAGS_TARGET = TAGS
  
 +FUZZ_OBJS += fuzz-pack-headers.o
 +FUZZ_OBJS += fuzz-pack-idx.o
 +
 +# Always build fuzz objects even if not testing, to prevent bit-rot.
 +all:: $(FUZZ_OBJS)
 +
 +FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
 +
  # Empty...
  EXTRA_PROGRAMS =
  
@@@ -720,34 -703,26 +719,34 @@@ TEST_BUILTINS_OBJS += test-date.
  TEST_BUILTINS_OBJS += test-delta.o
  TEST_BUILTINS_OBJS += test-drop-caches.o
  TEST_BUILTINS_OBJS += test-dump-cache-tree.o
 +TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
  TEST_BUILTINS_OBJS += test-dump-split-index.o
 +TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
  TEST_BUILTINS_OBJS += test-example-decorate.o
  TEST_BUILTINS_OBJS += test-genrandom.o
  TEST_BUILTINS_OBJS += test-hashmap.o
  TEST_BUILTINS_OBJS += test-index-version.o
 +TEST_BUILTINS_OBJS += test-json-writer.o
  TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
  TEST_BUILTINS_OBJS += test-match-trees.o
  TEST_BUILTINS_OBJS += test-mergesort.o
  TEST_BUILTINS_OBJS += test-mktemp.o
  TEST_BUILTINS_OBJS += test-online-cpus.o
 +TEST_BUILTINS_OBJS += test-parse-options.o
  TEST_BUILTINS_OBJS += test-path-utils.o
 +TEST_BUILTINS_OBJS += test-pkt-line.o
  TEST_BUILTINS_OBJS += test-prio-queue.o
 +TEST_BUILTINS_OBJS += test-reach.o
  TEST_BUILTINS_OBJS += test-read-cache.o
 +TEST_BUILTINS_OBJS += test-read-midx.o
  TEST_BUILTINS_OBJS += test-ref-store.o
  TEST_BUILTINS_OBJS += test-regex.o
 +TEST_BUILTINS_OBJS += test-repository.o
  TEST_BUILTINS_OBJS += test-revision-walking.o
  TEST_BUILTINS_OBJS += test-run-command.o
  TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 -TEST_BUILTINS_OBJS += test-sha1-array.o
  TEST_BUILTINS_OBJS += test-sha1.o
 +TEST_BUILTINS_OBJS += test-sha1-array.o
  TEST_BUILTINS_OBJS += test-sigchain.o
  TEST_BUILTINS_OBJS += test-strcmp-offset.o
  TEST_BUILTINS_OBJS += test-string-list.o
@@@ -755,13 -730,14 +754,13 @@@ TEST_BUILTINS_OBJS += test-submodule-co
  TEST_BUILTINS_OBJS += test-subprocess.o
  TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
  TEST_BUILTINS_OBJS += test-wildmatch.o
 +TEST_BUILTINS_OBJS += test-windows-named-pipe.o
  TEST_BUILTINS_OBJS += test-write-cache.o
  
 -TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
 -TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 +# Do not add more tests here unless they have extra dependencies. Add
 +# them in TEST_BUILTINS_OBJS above.
  TEST_PROGRAMS_NEED_X += test-fake-ssh
  TEST_PROGRAMS_NEED_X += test-line-buffer
 -TEST_PROGRAMS_NEED_X += test-parse-options
 -TEST_PROGRAMS_NEED_X += test-pkt-line
  TEST_PROGRAMS_NEED_X += test-svn-fe
  TEST_PROGRAMS_NEED_X += test-tool
  
@@@ -851,7 -827,6 +850,7 @@@ LIB_OBJS += column.
  LIB_OBJS += combine-diff.o
  LIB_OBJS += commit.o
  LIB_OBJS += commit-graph.o
 +LIB_OBJS += commit-reach.o
  LIB_OBJS += compat/obstack.o
  LIB_OBJS += compat/terminal.o
  LIB_OBJS += config.o
@@@ -864,7 -839,6 +863,7 @@@ LIB_OBJS += csum-file.
  LIB_OBJS += ctype.o
  LIB_OBJS += date.o
  LIB_OBJS += decorate.o
 +LIB_OBJS += delta-islands.o
  LIB_OBJS += diffcore-break.o
  LIB_OBJS += diffcore-delta.o
  LIB_OBJS += diffcore-order.o
@@@ -884,7 -858,6 +883,7 @@@ LIB_OBJS += ewah/ewah_bitmap.
  LIB_OBJS += ewah/ewah_io.o
  LIB_OBJS += ewah/ewah_rlw.o
  LIB_OBJS += exec-cmd.o
 +LIB_OBJS += fetch-negotiator.o
  LIB_OBJS += fetch-object.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
@@@ -894,12 -867,9 +893,12 @@@ LIB_OBJS += gpg-interface.
  LIB_OBJS += graph.o
  LIB_OBJS += grep.o
  LIB_OBJS += hashmap.o
 +LIB_OBJS += linear-assignment.o
  LIB_OBJS += help.o
  LIB_OBJS += hex.o
  LIB_OBJS += ident.o
 +LIB_OBJS += interdiff.o
 +LIB_OBJS += json-writer.o
  LIB_OBJS += kwset.o
  LIB_OBJS += levenshtein.o
  LIB_OBJS += line-log.o
@@@ -919,10 -889,7 +918,10 @@@ LIB_OBJS += merge.
  LIB_OBJS += merge-blobs.o
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += mergesort.o
 +LIB_OBJS += midx.o
  LIB_OBJS += name-hash.o
 +LIB_OBJS += negotiator/default.o
 +LIB_OBJS += negotiator/skipping.o
  LIB_OBJS += notes.o
  LIB_OBJS += notes-cache.o
  LIB_OBJS += notes-merge.o
@@@ -952,9 -919,9 +951,10 @@@ LIB_OBJS += progress.
  LIB_OBJS += prompt.o
  LIB_OBJS += protocol.o
  LIB_OBJS += quote.o
 +LIB_OBJS += range-diff.o
  LIB_OBJS += reachable.o
  LIB_OBJS += read-cache.o
+ LIB_OBJS += rebase-interactive.o
  LIB_OBJS += reflog-walk.o
  LIB_OBJS += refs.o
  LIB_OBJS += refs/files-backend.o
@@@ -1080,7 -1047,6 +1080,7 @@@ BUILTIN_OBJS += builtin/merge-recursive
  BUILTIN_OBJS += builtin/merge-tree.o
  BUILTIN_OBJS += builtin/mktag.o
  BUILTIN_OBJS += builtin/mktree.o
 +BUILTIN_OBJS += builtin/multi-pack-index.o
  BUILTIN_OBJS += builtin/mv.o
  BUILTIN_OBJS += builtin/name-rev.o
  BUILTIN_OBJS += builtin/notes.o
@@@ -1092,10 -1058,8 +1092,10 @@@ BUILTIN_OBJS += builtin/prune-packed.
  BUILTIN_OBJS += builtin/prune.o
  BUILTIN_OBJS += builtin/pull.o
  BUILTIN_OBJS += builtin/push.o
 +BUILTIN_OBJS += builtin/range-diff.o
  BUILTIN_OBJS += builtin/read-tree.o
- BUILTIN_OBJS += builtin/rebase--helper.o
 +BUILTIN_OBJS += builtin/rebase.o
+ BUILTIN_OBJS += builtin/rebase--interactive.o
  BUILTIN_OBJS += builtin/receive-pack.o
  BUILTIN_OBJS += builtin/reflog.o
  BUILTIN_OBJS += builtin/remote.o
@@@ -1808,7 -1772,6 +1808,7 @@@ ifndef 
        QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
        QUIET_GCOV     = @echo '   ' GCOV $@;
        QUIET_SP       = @echo '   ' SP $<;
 +      QUIET_HDR      = @echo '   ' HDR $<;
        QUIET_RC       = @echo '   ' RC $@;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
@@@ -2070,7 -2033,7 +2070,7 @@@ $(BUILT_INS): git$
  
  command-list.h: generate-cmdlist.sh command-list.txt
  
 -command-list.h: $(wildcard Documentation/git*.txt)
 +command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt
        $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
@@@ -2265,7 -2228,6 +2265,7 @@@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_
  OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
 +      $(FUZZ_OBJS) \
        common-main.o \
        git.o
  ifndef NO_CURL
@@@ -2436,7 -2398,6 +2436,6 @@@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS
  LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
  LOCALIZED_SH = $(SCRIPT_SH)
  LOCALIZED_SH += git-parse-remote.sh
- LOCALIZED_SH += git-rebase--interactive.sh
  LOCALIZED_SH += git-rebase--preserve-merges.sh
  LOCALIZED_SH += git-sh-setup.sh
  LOCALIZED_PERL = $(SCRIPT_PERL)
@@@ -2623,8 -2584,8 +2622,8 @@@ endi
  ifdef GIT_INTEROP_MAKE_OPTS
        @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
  endif
 -ifdef TEST_GIT_INDEX_VERSION
 -      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
 +ifdef GIT_TEST_INDEX_VERSION
 +      @echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+
  endif
        @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
  
@@@ -2692,17 -2653,6 +2691,17 @@@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORC
  .PHONY: sparse $(SP_OBJ)
  sparse: $(SP_OBJ)
  
 +GEN_HDRS := command-list.h unicode-width.h
 +EXCEPT_HDRS := $(GEN_HDRS) compat% xdiff%
 +CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(patsubst ./%,%,$(LIB_H)))
 +HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
 +
 +$(HCO): %.hco: %.h FORCE
 +      $(QUIET_HDR)$(CC) -include git-compat-util.h -I. -o /dev/null -c -xc $<
 +
 +.PHONY: hdr-check $(HCO)
 +hdr-check: $(HCO)
 +
  .PHONY: style
  style:
        git clang-format --style file --diff --extensions c,h
@@@ -2718,16 -2668,10 +2717,16 @@@ check: command-list.
        fi
  
  C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ))
 -%.cocci.patch: %.cocci $(C_SOURCES)
 +ifdef DC_SHA1_SUBMODULE
 +COCCI_SOURCES = $(filter-out sha1collisiondetection/%,$(C_SOURCES))
 +else
 +COCCI_SOURCES = $(filter-out sha1dc/%,$(C_SOURCES))
 +endif
 +
 +%.cocci.patch: %.cocci $(COCCI_SOURCES)
        @echo '    ' SPATCH $<; \
        ret=0; \
 -      for f in $(C_SOURCES); do \
 +      for f in $(COCCI_SOURCES); do \
                $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \
                        { ret=$$?; break; }; \
        done >$@+ 2>$@.log; \
        then \
                echo '    ' SPATCH result: $@; \
        fi
 -coccicheck: $(patsubst %.cocci,%.cocci.patch,$(wildcard contrib/coccinelle/*.cocci))
 +coccicheck: $(addsuffix .patch,$(wildcard contrib/coccinelle/*.cocci))
 +
 +.PHONY: coccicheck
  
  ### Installation rules
  
@@@ -2955,22 -2897,19 +2954,22 @@@ profile-clean
        $(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
  
 -clean: profile-clean coverage-clean
 +cocciclean:
 +      $(RM) contrib/coccinelle/*.cocci.patch*
 +
 +clean: profile-clean coverage-clean cocciclean
        $(RM) *.res
        $(RM) $(OBJECTS)
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
 +      $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
 -      $(RM) contrib/coccinelle/*.cocci.patch*
        $(MAKE) -C Documentation/ clean
  ifndef NO_PERL
        $(MAKE) -C gitweb clean
@@@ -2986,7 -2925,7 +2985,7 @@@ endi
        $(RM) GIT-USER-AGENT GIT-PREFIX
        $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
  
 -.PHONY: all install profile-clean clean strip
 +.PHONY: all install profile-clean cocciclean clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
  .PHONY: FORCE cscope
  
@@@ -3089,24 -3028,3 +3088,24 @@@ cover_db: coverage-repor
  cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
  
 +
 +### Fuzz testing
 +#
 +# Building fuzz targets generally requires a special set of compiler flags that
 +# are not necessarily appropriate for general builds, and that vary greatly
 +# depending on the compiler version used.
 +#
 +# An example command to build against libFuzzer from LLVM 4.0.0:
 +#
 +# make CC=clang CXX=clang++ \
 +#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
 +#      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
 +#      fuzz-all
 +#
 +.PHONY: fuzz-all
 +
 +$(FUZZ_PROGRAMS): all
 +      $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
 +              $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
 +
 +fuzz-all: $(FUZZ_PROGRAMS)
diff --combined builtin.h
index 6fb66f5ba40bca73ba62c6d340ab8f4f58ce1e32,7feb689d87c765fdb59f1b8c0e368bcd5819712f..6538932e99a72f1be97c41806ca6e394f18f9a0c
+++ b/builtin.h
@@@ -191,7 -191,6 +191,7 @@@ extern int cmd_merge_recursive(int argc
  extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_mktag(int argc, const char **argv, const char *prefix);
  extern int cmd_mktree(int argc, const char **argv, const char *prefix);
 +extern int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
  extern int cmd_mv(int argc, const char **argv, const char *prefix);
  extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
  extern int cmd_notes(int argc, const char **argv, const char *prefix);
@@@ -202,10 -201,8 +202,10 @@@ extern int cmd_prune(int argc, const ch
  extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
  extern int cmd_pull(int argc, const char **argv, const char *prefix);
  extern int cmd_push(int argc, const char **argv, const char *prefix);
 +extern int cmd_range_diff(int argc, const char **argv, const char *prefix);
  extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
- extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
 +extern int cmd_rebase(int argc, const char **argv, const char *prefix);
+ extern int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
  extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
  extern int cmd_reflog(int argc, const char **argv, const char *prefix);
  extern int cmd_remote(int argc, const char **argv, const char *prefix);
diff --combined cache.h
index f7fabdde8f37d857f6160c0e4798788f1963d3ae,d70ae49ca23ab00c79b05bb75940f9fddc4eed5d..8b1ee42ae93b4bfd025f0f761e6adeac6c642907
+++ b/cache.h
@@@ -15,7 -15,6 +15,7 @@@
  #include "path.h"
  #include "sha1-array.h"
  #include "repository.h"
 +#include "mem-pool.h"
  
  #include <zlib.h>
  typedef struct git_zstream {
@@@ -157,7 -156,6 +157,7 @@@ struct cache_entry 
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
 +      unsigned int mem_pool_allocated;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
        struct object_id oid;
  /* Forward structure decls */
  struct pathspec;
  struct child_process;
 +struct tree;
  
  /*
   * Copy the sha1 and stat state of a cache entry from one to
@@@ -230,7 -227,6 +230,7 @@@ static inline void copy_cache_entry(str
                                    const struct cache_entry *src)
  {
        unsigned int state = dst->ce_flags & CE_HASHED;
 +      int mem_pool_allocated = dst->mem_pool_allocated;
  
        /* Don't copy hash chain and name */
        memcpy(&dst->ce_stat_data, &src->ce_stat_data,
  
        /* Restore the hash state */
        dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
 +
 +      /* Restore the mem_pool_allocated flag */
 +      dst->mem_pool_allocated = mem_pool_allocated;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -335,7 -328,6 +335,7 @@@ struct index_state 
        struct untracked_cache *untracked;
        uint64_t fsmonitor_last_update;
        struct ewah_bitmap *fsmonitor_dirty;
 +      struct mem_pool *ce_mem_pool;
  };
  
  extern struct index_state the_index;
@@@ -347,60 -339,6 +347,60 @@@ extern void remove_name_hash(struct ind
  extern void free_name_hash(struct index_state *istate);
  
  
 +/* Cache entry creation and cleanup */
 +
 +/*
 + * Create cache_entry intended for use in the specified index. Caller
 + * is responsible for discarding the cache_entry with
 + * `discard_cache_entry`.
 + */
 +struct cache_entry *make_cache_entry(struct index_state *istate,
 +                                   unsigned int mode,
 +                                   const struct object_id *oid,
 +                                   const char *path,
 +                                   int stage,
 +                                   unsigned int refresh_options);
 +
 +struct cache_entry *make_empty_cache_entry(struct index_state *istate,
 +                                         size_t name_len);
 +
 +/*
 + * Create a cache_entry that is not intended to be added to an index.
 + * Caller is responsible for discarding the cache_entry
 + * with `discard_cache_entry`.
 + */
 +struct cache_entry *make_transient_cache_entry(unsigned int mode,
 +                                             const struct object_id *oid,
 +                                             const char *path,
 +                                             int stage);
 +
 +struct cache_entry *make_empty_transient_cache_entry(size_t name_len);
 +
 +/*
 + * Discard cache entry.
 + */
 +void discard_cache_entry(struct cache_entry *ce);
 +
 +/*
 + * Check configuration if we should perform extra validation on cache
 + * entries.
 + */
 +int should_validate_cache_entries(void);
 +
 +/*
 + * Duplicate a cache_entry. Allocate memory for the new entry from a
 + * memory_pool. Takes into account cache_entry fields that are meant
 + * for managing the underlying memory allocation of the cache_entry.
 + */
 +struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
 +
 +/*
 + * Validate the cache entries in the index.  This is an internal
 + * consistency check that the cache_entry structs are allocated from
 + * the expected memory pool.
 + */
 +void validate_cache_entries(const struct index_state *istate);
 +
  #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
  #define active_cache (the_index.cache)
  #define active_nr (the_index.cache_nr)
  
  #define read_cache() read_index(&the_index)
  #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
 -#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 +#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
  #define is_cache_unborn() is_index_unborn(&the_index)
  #define read_cache_unmerged() read_index_unmerged(&the_index)
  #define discard_cache() discard_index(&the_index)
@@@ -659,9 -597,7 +659,9 @@@ extern int daemonize(void)
  /* Initialize and use the cache information */
  struct lock_file;
  extern int read_index(struct index_state *);
 -extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 +extern int read_index_preload(struct index_state *,
 +                            const struct pathspec *pathspec,
 +                            unsigned int refresh_flags);
  extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path,
@@@ -699,15 -635,12 +699,15 @@@ extern void move_index_extensions(struc
  extern int unmerged_index(const struct index_state *);
  
  /**
 - * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
 - * branch, returns 1 if there are entries in the index, 0 otherwise. If an
 - * strbuf is provided, the space-separated list of files that differ will be
 - * appended to it.
 + * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
 + * compares istate to HEAD.  If tree is NULL and on an unborn branch,
 + * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
 + * provided, the space-separated list of files that differ will be appended
 + * to it.
   */
 -extern int index_has_changes(struct strbuf *sb);
 +extern int index_has_changes(struct index_state *istate,
 +                           struct tree *tree,
 +                           struct strbuf *sb);
  
  extern int verify_path(const char *path, unsigned mode);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@@ -765,6 -698,7 +765,6 @@@ extern int remove_file_from_index(struc
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  
 -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@@ -783,16 -717,14 +783,16 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_REFRESH              0x10
  /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
  #define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int is_racy_timestamp(const struct index_state *istate,
 +                           const struct cache_entry *ce);
  extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  #define HASH_RENORMALIZE  4
 -extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 -extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 +extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 +extern int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
  /*
   * Record to sd the data from st that we use to check whether a file
@@@ -818,9 -750,8 +818,9 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 +#define REFRESH_PROGRESS      0x0040  /* show progress bar if stderr is tty */
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 -extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
 +extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
  
  /*
   * Opportunistically update the index but do not complain if we can't.
@@@ -873,13 -804,16 +873,13 @@@ void reset_shared_repository(void)
   * Do replace refs need to be checked this run?  This variable is
   * initialized to true unless --no-replace-object is used or
   * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
 - * commands that do not want replace references to be active.  As an
 - * optimization it is also set to false if replace references have
 - * been sought but there were none.
 + * commands that do not want replace references to be active.
   */
 -extern int check_replace_refs;
 +extern int read_replace_refs;
  extern char *git_replace_ref_base;
  
  extern int fsync_object_files;
  extern int core_preload_index;
 -extern int core_commit_graph;
  extern int core_apply_sparse_checkout;
  extern int precomposed_unicode;
  extern int protect_hfs;
@@@ -922,6 -856,15 +922,6 @@@ enum log_refs_config 
  };
  extern enum log_refs_config log_all_ref_updates;
  
 -enum branch_track {
 -      BRANCH_TRACK_UNSPECIFIED = -1,
 -      BRANCH_TRACK_NEVER = 0,
 -      BRANCH_TRACK_REMOTE,
 -      BRANCH_TRACK_ALWAYS,
 -      BRANCH_TRACK_EXPLICIT,
 -      BRANCH_TRACK_OVERRIDE
 -};
 -
  enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
@@@ -938,6 -881,7 +938,6 @@@ enum push_default_type 
        PUSH_DEFAULT_UNSPECIFIED
  };
  
 -extern enum branch_track git_branch_track;
  extern enum rebase_setup_type autorebase;
  extern enum push_default_type push_default;
  
@@@ -1028,17 -972,7 +1028,17 @@@ extern const struct object_id null_oid
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 +      /*
 +       * This is a temporary optimization hack. By asserting the size here,
 +       * we let the compiler know that it's always going to be 20, which lets
 +       * it turn this fixed-size memcmp into a few inline instructions.
 +       *
 +       * This will need to be extended or ripped out when we learn about
 +       * hashes of different sizes.
 +       */
 +      if (the_hash_algo->rawsz != 20)
 +              BUG("hash size not yet supported by hashcmp");
 +      return memcmp(sha1, sha2, the_hash_algo->rawsz);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
        return hashcmp(oid1->hash, oid2->hash);
  }
  
 +static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 +{
 +      return !hashcmp(sha1, sha2);
 +}
 +
 +static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
 +{
 +      return hasheq(oid1->hash, oid2->hash);
 +}
 +
  static inline int is_null_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, null_sha1);
 +      return hasheq(sha1, null_sha1);
  }
  
  static inline int is_null_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, null_sha1);
 +      return hasheq(oid->hash, null_sha1);
  }
  
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
  {
 -      memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
 +      memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
  }
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
@@@ -1085,7 -1009,7 +1085,7 @@@ static inline struct object_id *oiddup(
  
  static inline void hashclr(unsigned char *hash)
  {
 -      memset(hash, 0, GIT_SHA1_RAWSZ);
 +      memset(hash, 0, the_hash_algo->rawsz);
  }
  
  static inline void oidclr(struct object_id *oid)
@@@ -1100,22 -1024,22 +1100,22 @@@ static inline void oidread(struct objec
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
 +      return hasheq(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !oidcmp(oid, the_hash_algo->empty_blob);
 +      return oideq(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
 +      return hasheq(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !oidcmp(oid, the_hash_algo->empty_tree);
 +      return oideq(oid, the_hash_algo->empty_tree);
  }
  
  const char *empty_tree_oid_hex(void);
@@@ -1438,20 -1362,18 +1438,20 @@@ extern void *read_object_with_reference
  extern struct object *peel_to_type(const char *name, int namelen,
                                   struct object *o, enum object_type);
  
 +enum date_mode_type {
 +      DATE_NORMAL = 0,
 +      DATE_RELATIVE,
 +      DATE_SHORT,
 +      DATE_ISO8601,
 +      DATE_ISO8601_STRICT,
 +      DATE_RFC2822,
 +      DATE_STRFTIME,
 +      DATE_RAW,
 +      DATE_UNIX
 +};
 +
  struct date_mode {
 -      enum date_mode_type {
 -              DATE_NORMAL = 0,
 -              DATE_RELATIVE,
 -              DATE_SHORT,
 -              DATE_ISO8601,
 -              DATE_ISO8601_STRICT,
 -              DATE_RFC2822,
 -              DATE_STRFTIME,
 -              DATE_RAW,
 -              DATE_UNIX
 -      } type;
 +      enum date_mode_type type;
        const char *strftime_fmt;
        int local;
  };
@@@ -1487,6 -1409,7 +1487,7 @@@ extern const char *fmt_name(const char 
  extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
+ extern const char *git_sequence_editor(void);
  extern const char *git_pager(int stdout_is_tty);
  extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
@@@ -1533,7 -1456,6 +1534,7 @@@ struct checkout 
        unsigned force:1,
                 quiet:1,
                 not_new:1,
 +               clone:1,
                 refresh_cache:1;
  };
  #define CHECKOUT_INIT { NULL, "" }
@@@ -1591,6 -1513,62 +1592,6 @@@ extern int odb_mkstemp(struct strbuf *t
   */
  extern int odb_pack_keep(const char *name);
  
 -/*
 - * Iterate over the files in the loose-object parts of the object
 - * directory "path", triggering the following callbacks:
 - *
 - *  - loose_object is called for each loose object we find.
 - *
 - *  - loose_cruft is called for any files that do not appear to be
 - *    loose objects. Note that we only look in the loose object
 - *    directories "objects/[0-9a-f]{2}/", so we will not report
 - *    "objects/foobar" as cruft.
 - *
 - *  - loose_subdir is called for each top-level hashed subdirectory
 - *    of the object directory (e.g., "$OBJDIR/f0"). It is called
 - *    after the objects in the directory are processed.
 - *
 - * Any callback that is NULL will be ignored. Callbacks returning non-zero
 - * will end the iteration.
 - *
 - * In the "buf" variant, "path" is a strbuf which will also be used as a
 - * scratch buffer, but restored to its original contents before
 - * the function returns.
 - */
 -typedef int each_loose_object_fn(const struct object_id *oid,
 -                               const char *path,
 -                               void *data);
 -typedef int each_loose_cruft_fn(const char *basename,
 -                              const char *path,
 -                              void *data);
 -typedef int each_loose_subdir_fn(unsigned int nr,
 -                               const char *path,
 -                               void *data);
 -int for_each_file_in_obj_subdir(unsigned int subdir_nr,
 -                              struct strbuf *path,
 -                              each_loose_object_fn obj_cb,
 -                              each_loose_cruft_fn cruft_cb,
 -                              each_loose_subdir_fn subdir_cb,
 -                              void *data);
 -int for_each_loose_file_in_objdir(const char *path,
 -                                each_loose_object_fn obj_cb,
 -                                each_loose_cruft_fn cruft_cb,
 -                                each_loose_subdir_fn subdir_cb,
 -                                void *data);
 -int for_each_loose_file_in_objdir_buf(struct strbuf *path,
 -                                    each_loose_object_fn obj_cb,
 -                                    each_loose_cruft_fn cruft_cb,
 -                                    each_loose_subdir_fn subdir_cb,
 -                                    void *data);
 -
 -/*
 - * Iterate over loose objects in both the local
 - * repository and any alternates repositories (unless the
 - * LOCAL_ONLY flag is set).
 - */
 -#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 -
  /*
   * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
   * blobs. This has a difference only if extensions.partialClone is set.
@@@ -1710,7 -1688,7 +1711,7 @@@ void shift_tree_by(const struct object_
  /* All WS_* -- when extended, adapt diff.c emit_symbol */
  #define WS_RULE_MASK           07777
  extern unsigned whitespace_rule_cfg;
 -extern unsigned whitespace_rule(const char *);
 +extern unsigned whitespace_rule(struct index_state *, const char *);
  extern unsigned parse_whitespace_rule(const char *);
  extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
  extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
@@@ -1732,12 -1710,10 +1733,12 @@@ extern struct startup_info *startup_inf
  
  /* merge.c */
  struct commit_list;
 -int try_merge_command(const char *strategy, size_t xopts_nr,
 +int try_merge_command(struct repository *r,
 +              const char *strategy, size_t xopts_nr,
                const char **xopts, struct commit_list *common,
                const char *head_arg, struct commit_list *remotes);
 -int checkout_fast_forward(const struct object_id *from,
 +int checkout_fast_forward(struct repository *r,
 +                        const struct object_id *from,
                          const struct object_id *to,
                          int overwrite_ignore);
  
diff --combined git-legacy-rebase.sh
index 5d92648014a41208617692a0d18d65db851d4717,0000000000000000000000000000000000000000..75a08b2683e76e6832475bc91e4d4f38b3d3bc3c
mode 100755,000000..100755
--- /dev/null
@@@ -1,708 -1,0 +1,745 @@@
-       . git-rebase--$type
 +#!/bin/sh
 +#
 +# Copyright (c) 2005 Junio C Hamano.
 +#
 +
 +SUBDIRECTORY_OK=Yes
 +OPTIONS_KEEPDASHDASH=
 +OPTIONS_STUCKLONG=t
 +OPTIONS_SPEC="\
 +git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
 +git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
 +git rebase --continue | --abort | --skip | --edit-todo
 +--
 + Available options are
 +v,verbose!         display a diffstat of what changed upstream
 +q,quiet!           be quiet. implies --no-stat
 +autostash          automatically stash/stash pop before and after
 +fork-point         use 'merge-base --fork-point' to refine upstream
 +onto=!             rebase onto given branch instead of upstream
 +r,rebase-merges?   try to rebase merges instead of skipping them
 +p,preserve-merges! try to recreate merges instead of ignoring them
 +s,strategy=!       use the given merge strategy
 +X,strategy-option=! pass the argument through to the merge strategy
 +no-ff!             cherry-pick all commits, even if unchanged
 +f,force-rebase!    cherry-pick all commits, even if unchanged
 +m,merge!           use merging strategies to rebase
 +i,interactive!     let the user edit the list of commits to rebase
 +x,exec=!           add exec lines after each commit of the editable list
 +k,keep-empty     preserve empty commits during rebase
 +allow-empty-message allow rebasing commits with empty messages
 +stat!              display a diffstat of what changed upstream
 +n,no-stat!         do not show diffstat of what changed upstream
 +verify             allow pre-rebase hook to run
 +rerere-autoupdate  allow rerere to update index with resolved conflicts
 +root!              rebase all reachable commits up to the root(s)
 +autosquash         move commits that begin with squash!/fixup! under -i
 +signoff            add a Signed-off-by: line to each commit
 +committer-date-is-author-date! passed to 'git am'
 +ignore-date!       passed to 'git am'
 +whitespace=!       passed to 'git apply'
 +ignore-whitespace! passed to 'git apply'
 +C=!                passed to 'git apply'
 +S,gpg-sign?        GPG-sign commits
 + Actions:
 +continue!          continue
 +abort!             abort and check out the original branch
 +skip!              skip current patch and continue
 +edit-todo!         edit the todo list during an interactive rebase
 +quit!              abort but keep HEAD where it is
 +show-current-patch! show the patch file being applied or merged
 +"
 +. git-sh-setup
 +set_reflog_action rebase
 +require_work_tree_exists
 +cd_to_toplevel
 +
 +LF='
 +'
 +ok_to_skip_pre_rebase=
 +
 +squash_onto=
 +unset onto
 +unset restrict_revision
 +cmd=
 +strategy=
 +strategy_opts=
 +do_merge=
 +merge_dir="$GIT_DIR"/rebase-merge
 +apply_dir="$GIT_DIR"/rebase-apply
 +verbose=
 +diffstat=
 +test "$(git config --bool rebase.stat)" = true && diffstat=t
 +autostash="$(git config --bool rebase.autostash || echo false)"
 +fork_point=auto
 +git_am_opt=
 +git_format_patch_opt=
 +rebase_root=
 +force_rebase=
 +allow_rerere_autoupdate=
 +# Non-empty if a rebase was in progress when 'git rebase' was invoked
 +in_progress=
 +# One of {am, merge, interactive}
 +type=
 +# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
 +state_dir=
 +# One of {'', continue, skip, abort}, as parsed from command line
 +action=
 +rebase_merges=
 +rebase_cousins=
 +preserve_merges=
 +autosquash=
 +keep_empty=
 +allow_empty_message=--allow-empty-message
 +signoff=
 +test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 +case "$(git config --bool commit.gpgsign)" in
 +true) gpg_sign_opt=-S ;;
 +*)    gpg_sign_opt= ;;
 +esac
 +. git-rebase--common
 +
 +read_basic_state () {
 +      test -f "$state_dir/head-name" &&
 +      test -f "$state_dir/onto" &&
 +      head_name=$(cat "$state_dir"/head-name) &&
 +      onto=$(cat "$state_dir"/onto) &&
 +      # We always write to orig-head, but interactive rebase used to write to
 +      # head. Fall back to reading from head to cover for the case that the
 +      # user upgraded git with an ongoing interactive rebase.
 +      if test -f "$state_dir"/orig-head
 +      then
 +              orig_head=$(cat "$state_dir"/orig-head)
 +      else
 +              orig_head=$(cat "$state_dir"/head)
 +      fi &&
 +      GIT_QUIET=$(cat "$state_dir"/quiet) &&
 +      test -f "$state_dir"/verbose && verbose=t
 +      test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
 +      test -f "$state_dir"/strategy_opts &&
 +              strategy_opts="$(cat "$state_dir"/strategy_opts)"
 +      test -f "$state_dir"/allow_rerere_autoupdate &&
 +              allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
 +      test -f "$state_dir"/gpg_sign_opt &&
 +              gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
 +      test -f "$state_dir"/signoff && {
 +              signoff="$(cat "$state_dir"/signoff)"
 +              force_rebase=t
 +      }
 +}
 +
 +finish_rebase () {
 +      rm -f "$(git rev-parse --git-path REBASE_HEAD)"
 +      apply_autostash &&
 +      { git gc --auto || true; } &&
 +      rm -rf "$state_dir"
 +}
 +
++run_interactive () {
++      GIT_CHERRY_PICK_HELP="$resolvemsg"
++      export GIT_CHERRY_PICK_HELP
++
++      test -n "$keep_empty" && keep_empty="--keep-empty"
++      test -n "$rebase_merges" && rebase_merges="--rebase-merges"
++      test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins"
++      test -n "$autosquash" && autosquash="--autosquash"
++      test -n "$verbose" && verbose="--verbose"
++      test -n "$force_rebase" && force_rebase="--no-ff"
++      test -n "$restrict_revision" && \
++              restrict_revision="--restrict-revision=^$restrict_revision"
++      test -n "$upstream" && upstream="--upstream=$upstream"
++      test -n "$onto" && onto="--onto=$onto"
++      test -n "$squash_onto" && squash_onto="--squash-onto=$squash_onto"
++      test -n "$onto_name" && onto_name="--onto-name=$onto_name"
++      test -n "$head_name" && head_name="--head-name=$head_name"
++      test -n "$strategy" && strategy="--strategy=$strategy"
++      test -n "$strategy_opts" && strategy_opts="--strategy-opts=$strategy_opts"
++      test -n "$switch_to" && switch_to="--switch-to=$switch_to"
++      test -n "$cmd" && cmd="--cmd=$cmd"
++      test -n "$action" && action="--$action"
++
++      exec git rebase--interactive "$action" "$keep_empty" "$rebase_merges" "$rebase_cousins" \
++              "$upstream" "$onto" "$squash_onto" "$restrict_revision" \
++              "$allow_empty_message" "$autosquash" "$verbose" \
++              "$force_rebase" "$onto_name" "$head_name" "$strategy" \
++              "$strategy_opts" "$cmd" "$switch_to" \
++              "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff"
++}
++
 +run_specific_rebase () {
 +      if [ "$interactive_rebase" = implied ]; then
 +              GIT_EDITOR=:
 +              export GIT_EDITOR
 +              autosquash=
 +      fi
-       if test -z "$preserve_merges"
 +
-               git_rebase__$type
++      if test -n "$interactive_rebase" -a -z "$preserve_merges"
 +      then
-               git_rebase__preserve_merges
++              run_interactive
 +      else
-       elif test $ret -eq 2 # special exit status for rebase -i
++              . git-rebase--$type
++
++              if test -z "$preserve_merges"
++              then
++                      git_rebase__$type
++              else
++                      git_rebase__preserve_merges
++              fi
 +      fi
 +
 +      ret=$?
 +      if test $ret -eq 0
 +      then
 +              finish_rebase
++      elif test $ret -eq 2 # special exit status for rebase -p
 +      then
 +              apply_autostash &&
 +              rm -rf "$state_dir" &&
 +              die "Nothing to do"
 +      fi
 +      exit $ret
 +}
 +
 +run_pre_rebase_hook () {
 +      if test -z "$ok_to_skip_pre_rebase" &&
 +         test -x "$(git rev-parse --git-path hooks/pre-rebase)"
 +      then
 +              "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
 +              die "$(gettext "The pre-rebase hook refused to rebase.")"
 +      fi
 +}
 +
 +test -f "$apply_dir"/applying &&
 +      die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")"
 +
 +if test -d "$apply_dir"
 +then
 +      type=am
 +      state_dir="$apply_dir"
 +elif test -d "$merge_dir"
 +then
 +      if test -d "$merge_dir"/rewritten
 +      then
 +              type=preserve-merges
 +              interactive_rebase=explicit
 +              preserve_merges=t
 +      elif test -f "$merge_dir"/interactive
 +      then
 +              type=interactive
 +              interactive_rebase=explicit
 +      else
 +              type=merge
 +      fi
 +      state_dir="$merge_dir"
 +fi
 +test -n "$type" && in_progress=t
 +
 +total_argc=$#
 +while test $# != 0
 +do
 +      case "$1" in
 +      --no-verify)
 +              ok_to_skip_pre_rebase=yes
 +              ;;
 +      --verify)
 +              ok_to_skip_pre_rebase=
 +              ;;
 +      --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
 +              test $total_argc -eq 2 || usage
 +              action=${1##--}
 +              ;;
 +      --onto=*)
 +              onto="${1#--onto=}"
 +              ;;
 +      --exec=*)
 +              cmd="${cmd}exec ${1#--exec=}${LF}"
 +              test -z "$interactive_rebase" && interactive_rebase=implied
 +              ;;
 +      --interactive)
 +              interactive_rebase=explicit
 +              ;;
 +      --keep-empty)
 +              keep_empty=yes
 +              ;;
 +      --allow-empty-message)
 +              allow_empty_message=--allow-empty-message
 +              ;;
 +      --no-keep-empty)
 +              keep_empty=
 +              ;;
 +      --rebase-merges)
 +              rebase_merges=t
 +              test -z "$interactive_rebase" && interactive_rebase=implied
 +              ;;
 +      --rebase-merges=*)
 +              rebase_merges=t
 +              case "${1#*=}" in
 +              rebase-cousins) rebase_cousins=t;;
 +              no-rebase-cousins) rebase_cousins=;;
 +              *) die "Unknown mode: $1";;
 +              esac
 +              test -z "$interactive_rebase" && interactive_rebase=implied
 +              ;;
 +      --preserve-merges)
 +              preserve_merges=t
 +              test -z "$interactive_rebase" && interactive_rebase=implied
 +              ;;
 +      --autosquash)
 +              autosquash=t
 +              ;;
 +      --no-autosquash)
 +              autosquash=
 +              ;;
 +      --fork-point)
 +              fork_point=t
 +              ;;
 +      --no-fork-point)
 +              fork_point=
 +              ;;
 +      --merge)
 +              do_merge=t
 +              ;;
 +      --strategy-option=*)
 +              strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)"
 +              do_merge=t
 +              test -z "$strategy" && strategy=recursive
 +              ;;
 +      --strategy=*)
 +              strategy="${1#--strategy=}"
 +              do_merge=t
 +              ;;
 +      --no-stat)
 +              diffstat=
 +              ;;
 +      --stat)
 +              diffstat=t
 +              ;;
 +      --autostash)
 +              autostash=true
 +              ;;
 +      --no-autostash)
 +              autostash=false
 +              ;;
 +      --verbose)
 +              verbose=t
 +              diffstat=t
 +              GIT_QUIET=
 +              ;;
 +      --quiet)
 +              GIT_QUIET=t
 +              git_am_opt="$git_am_opt -q"
 +              verbose=
 +              diffstat=
 +              ;;
 +      --whitespace=*)
 +              git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}"
 +              case "${1#--whitespace=}" in
 +              fix|strip)
 +                      force_rebase=t
 +                      ;;
 +              esac
 +              ;;
 +      --ignore-whitespace)
 +              git_am_opt="$git_am_opt $1"
 +              ;;
 +      --signoff)
 +              signoff=--signoff
 +              ;;
 +      --no-signoff)
 +              signoff=
 +              ;;
 +      --committer-date-is-author-date|--ignore-date)
 +              git_am_opt="$git_am_opt $1"
 +              force_rebase=t
 +              ;;
 +      -C*)
 +              git_am_opt="$git_am_opt $1"
 +              ;;
 +      --root)
 +              rebase_root=t
 +              ;;
 +      --force-rebase|--no-ff)
 +              force_rebase=t
 +              ;;
 +      --rerere-autoupdate|--no-rerere-autoupdate)
 +              allow_rerere_autoupdate="$1"
 +              ;;
 +      --gpg-sign)
 +              gpg_sign_opt=-S
 +              ;;
 +      --gpg-sign=*)
 +              gpg_sign_opt="-S${1#--gpg-sign=}"
 +              ;;
 +      --)
 +              shift
 +              break
 +              ;;
 +      *)
 +              usage
 +              ;;
 +      esac
 +      shift
 +done
 +test $# -gt 2 && usage
 +
 +if test -n "$action"
 +then
 +      test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
 +      # Only interactive rebase uses detailed reflog messages
 +      if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase
 +      then
 +              GIT_REFLOG_ACTION="rebase -i ($action)"
 +              export GIT_REFLOG_ACTION
 +      fi
 +fi
 +
 +if test "$action" = "edit-todo" && test -z "$interactive_rebase"
 +then
 +      die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
 +fi
 +
 +case "$action" in
 +continue)
 +      # Sanity check
 +      git rev-parse --verify HEAD >/dev/null ||
 +              die "$(gettext "Cannot read HEAD")"
 +      git update-index --ignore-submodules --refresh &&
 +      git diff-files --quiet --ignore-submodules || {
 +              echo "$(gettext "You must edit all merge conflicts and then
 +mark them as resolved using git add")"
 +              exit 1
 +      }
 +      read_basic_state
 +      run_specific_rebase
 +      ;;
 +skip)
 +      output git reset --hard HEAD || exit $?
 +      read_basic_state
 +      run_specific_rebase
 +      ;;
 +abort)
 +      git rerere clear
 +      read_basic_state
 +      case "$head_name" in
 +      refs/*)
 +              git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
 +              die "$(eval_gettext "Could not move back to \$head_name")"
 +              ;;
 +      esac
 +      output git reset --hard $orig_head
 +      finish_rebase
 +      exit
 +      ;;
 +quit)
 +      exec rm -rf "$state_dir"
 +      ;;
 +edit-todo)
 +      run_specific_rebase
 +      ;;
 +show-current-patch)
 +      run_specific_rebase
 +      die "BUG: run_specific_rebase is not supposed to return here"
 +      ;;
 +esac
 +
 +# Make sure no rebase is in progress
 +if test -n "$in_progress"
 +then
 +      state_dir_base=${state_dir##*/}
 +      cmd_live_rebase="git rebase (--continue | --abort | --skip)"
 +      cmd_clear_stale_rebase="rm -fr \"$state_dir\""
 +      die "
 +$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
 +I wonder if you are in the middle of another rebase.  If that is the
 +case, please try
 +      $cmd_live_rebase
 +If that is not the case, please
 +      $cmd_clear_stale_rebase
 +and run me again.  I am stopping in case you still have something
 +valuable there.')"
 +fi
 +
 +if test -n "$rebase_root" && test -z "$onto"
 +then
 +      test -z "$interactive_rebase" && interactive_rebase=implied
 +fi
 +
 +if test -n "$keep_empty"
 +then
 +      test -z "$interactive_rebase" && interactive_rebase=implied
 +fi
 +
 +if test -n "$interactive_rebase"
 +then
 +      if test -z "$preserve_merges"
 +      then
 +              type=interactive
 +      else
 +              type=preserve-merges
 +      fi
 +
 +      state_dir="$merge_dir"
 +elif test -n "$do_merge"
 +then
 +      type=merge
 +      state_dir="$merge_dir"
 +else
 +      type=am
 +      state_dir="$apply_dir"
 +fi
 +
 +if test -t 2 && test -z "$GIT_QUIET"
 +then
 +      git_format_patch_opt="$git_format_patch_opt --progress"
 +fi
 +
 +if test -n "$git_am_opt"; then
 +      incompatible_opts=$(echo " $git_am_opt " | \
 +                          sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
 +      if test -n "$interactive_rebase"
 +      then
 +              if test -n "$incompatible_opts"
 +              then
 +                      die "$(gettext "error: cannot combine interactive options (--interactive, --exec, --rebase-merges, --preserve-merges, --keep-empty, --root + --onto) with am options ($incompatible_opts)")"
 +              fi
 +      fi
 +      if test -n "$do_merge"; then
 +              if test -n "$incompatible_opts"
 +              then
 +                      die "$(gettext "error: cannot combine merge options (--merge, --strategy, --strategy-option) with am options ($incompatible_opts)")"
 +              fi
 +      fi
 +fi
 +
 +if test -n "$signoff"
 +then
 +      test -n "$preserve_merges" &&
 +              die "$(gettext "error: cannot combine '--signoff' with '--preserve-merges'")"
 +      git_am_opt="$git_am_opt $signoff"
 +      force_rebase=t
 +fi
 +
 +if test -n "$preserve_merges"
 +then
 +      # Note: incompatibility with --signoff handled in signoff block above
 +      # Note: incompatibility with --interactive is just a strong warning;
 +      #       git-rebase.txt caveats with "unless you know what you are doing"
 +      test -n "$rebase_merges" &&
 +              die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")"
 +fi
 +
 +if test -n "$rebase_merges"
 +then
 +      test -n "$strategy_opts" &&
 +              die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy-option'")"
 +      test -n "$strategy" &&
 +              die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy'")"
 +fi
 +
 +if test -z "$rebase_root"
 +then
 +      case "$#" in
 +      0)
 +              if ! upstream_name=$(git rev-parse --symbolic-full-name \
 +                      --verify -q @{upstream} 2>/dev/null)
 +              then
 +                      . git-parse-remote
 +                      error_on_missing_default_upstream "rebase" "rebase" \
 +                              "against" "git rebase $(gettext '<branch>')"
 +              fi
 +
 +              test "$fork_point" = auto && fork_point=t
 +              ;;
 +      *)      upstream_name="$1"
 +              if test "$upstream_name" = "-"
 +              then
 +                      upstream_name="@{-1}"
 +              fi
 +              shift
 +              ;;
 +      esac
 +      upstream=$(peel_committish "${upstream_name}") ||
 +      die "$(eval_gettext "invalid upstream '\$upstream_name'")"
 +      upstream_arg="$upstream_name"
 +else
 +      if test -z "$onto"
 +      then
 +              empty_tree=$(git hash-object -t tree /dev/null)
 +              onto=$(git commit-tree $empty_tree </dev/null)
 +              squash_onto="$onto"
 +      fi
 +      unset upstream_name
 +      unset upstream
 +      test $# -gt 1 && usage
 +      upstream_arg=--root
 +fi
 +
 +# Make sure the branch to rebase onto is valid.
 +onto_name=${onto-"$upstream_name"}
 +case "$onto_name" in
 +*...*)
 +      if      left=${onto_name%...*} right=${onto_name#*...} &&
 +              onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
 +      then
 +              case "$onto" in
 +              ?*"$LF"?*)
 +                      die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
 +                      ;;
 +              '')
 +                      die "$(eval_gettext "\$onto_name: there is no merge base")"
 +                      ;;
 +              esac
 +      else
 +              die "$(eval_gettext "\$onto_name: there is no merge base")"
 +      fi
 +      ;;
 +*)
 +      onto=$(peel_committish "$onto_name") ||
 +      die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
 +      ;;
 +esac
 +
 +# If the branch to rebase is given, that is the branch we will rebase
 +# $branch_name -- branch/commit being rebased, or HEAD (already detached)
 +# $orig_head -- commit object name of tip of the branch before rebasing
 +# $head_name -- refs/heads/<that-branch> or "detached HEAD"
 +switch_to=
 +case "$#" in
 +1)
 +      # Is it "rebase other $branchname" or "rebase other $commit"?
 +      branch_name="$1"
 +      switch_to="$1"
 +
 +      # Is it a local branch?
 +      if git show-ref --verify --quiet -- "refs/heads/$branch_name" &&
 +         orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name")
 +      then
 +              head_name="refs/heads/$branch_name"
 +      # If not is it a valid ref (branch or commit)?
 +      elif orig_head=$(git rev-parse -q --verify "$branch_name")
 +      then
 +              head_name="detached HEAD"
 +
 +      else
 +              die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")"
 +      fi
 +      ;;
 +0)
 +      # Do not need to switch branches, we are already on it.
 +      if branch_name=$(git symbolic-ref -q HEAD)
 +      then
 +              head_name=$branch_name
 +              branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)')
 +      else
 +              head_name="detached HEAD"
 +              branch_name=HEAD
 +      fi
 +      orig_head=$(git rev-parse --verify HEAD) || exit
 +      ;;
 +*)
 +      die "BUG: unexpected number of arguments left to parse"
 +      ;;
 +esac
 +
 +if test "$fork_point" = t
 +then
 +      new_upstream=$(git merge-base --fork-point "$upstream_name" \
 +                      "${switch_to:-HEAD}")
 +      if test -n "$new_upstream"
 +      then
 +              restrict_revision=$new_upstream
 +      fi
 +fi
 +
 +if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
 +then
 +      stash_sha1=$(git stash create "autostash") ||
 +      die "$(gettext 'Cannot autostash')"
 +
 +      mkdir -p "$state_dir" &&
 +      echo $stash_sha1 >"$state_dir/autostash" &&
 +      stash_abbrev=$(git rev-parse --short $stash_sha1) &&
 +      echo "$(eval_gettext 'Created autostash: $stash_abbrev')" &&
 +      git reset --hard
 +fi
 +
 +require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
 +
 +# Now we are rebasing commits $upstream..$orig_head (or with --root,
 +# everything leading up to $orig_head) on top of $onto
 +
 +# Check if we are already based on $onto with linear history,
 +# but this should be done only when upstream and onto are the same
 +# and if this is not an interactive rebase.
 +mb=$(git merge-base "$onto" "$orig_head")
 +if test -z "$interactive_rebase" && test "$upstream" = "$onto" &&
 +      test "$mb" = "$onto" && test -z "$restrict_revision" &&
 +      # linear history?
 +      ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
 +then
 +      if test -z "$force_rebase"
 +      then
 +              # Lazily switch to the target branch if needed...
 +              test -z "$switch_to" ||
 +              GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
 +                      git checkout -q "$switch_to" --
 +              if test "$branch_name" = "HEAD" &&
 +                       ! git symbolic-ref -q HEAD
 +              then
 +                      say "$(eval_gettext "HEAD is up to date.")"
 +              else
 +                      say "$(eval_gettext "Current branch \$branch_name is up to date.")"
 +              fi
 +              finish_rebase
 +              exit 0
 +      else
 +              if test "$branch_name" = "HEAD" &&
 +                       ! git symbolic-ref -q HEAD
 +              then
 +                      say "$(eval_gettext "HEAD is up to date, rebase forced.")"
 +              else
 +                      say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
 +              fi
 +      fi
 +fi
 +
 +# If a hook exists, give it a chance to interrupt
 +run_pre_rebase_hook "$upstream_arg" "$@"
 +
 +if test -n "$diffstat"
 +then
 +      if test -n "$verbose"
 +      then
 +              echo "$(eval_gettext "Changes from \$mb to \$onto:")"
 +      fi
 +      # We want color (if set), but no pager
 +      GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
 +fi
 +
 +test -n "$interactive_rebase" && run_specific_rebase
 +
 +# Detach HEAD and reset the tree
 +say "$(gettext "First, rewinding head to replay your work on top of it...")"
 +
 +GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
 +      git checkout -q "$onto^0" || die "could not detach HEAD"
 +git update-ref ORIG_HEAD $orig_head
 +
 +# If the $onto is a proper descendant of the tip of the branch, then
 +# we just fast-forwarded.
 +if test "$mb" = "$orig_head"
 +then
 +      say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
 +      move_to_original_branch
 +      finish_rebase
 +      exit 0
 +fi
 +
 +if test -n "$rebase_root"
 +then
 +      revisions="$onto..$orig_head"
 +else
 +      revisions="${restrict_revision-$upstream}..$orig_head"
 +fi
 +
 +run_specific_rebase
index c214c5e4d6ce21996f16031ec14dd624b75a9939,d43b4b582e907a6b2f78e8de19ff9176d0601869..afbb65765d46102339068e7e9aa397fcf88ee6a5
@@@ -711,11 -711,11 +711,11 @@@ do_rest () 
  }
  
  expand_todo_ids() {
-       git rebase--helper --expand-ids
+       git rebase--interactive --expand-ids
  }
  
  collapse_todo_ids() {
-       git rebase--helper --shorten-ids
+       git rebase--interactive --shorten-ids
  }
  
  # Switch to the branch in $into and notify it in the reflog
@@@ -876,8 -876,8 +876,8 @@@ init_revisions_and_shortrevisions () 
  
  complete_action() {
        test -s "$todo" || echo noop >> "$todo"
-       test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
-       test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
+       test -z "$autosquash" || git rebase--interactive --rearrange-squash || exit
+       test -n "$cmd" && git rebase--interactive --add-exec-commands --cmd "$cmd"
  
        todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
        todocount=${todocount##* }
@@@ -891,9 -891,9 +891,9 @@@ $comment_char $(eval_ngettext 
  EOF
        append_todo_help
        gettext "
 -      However, if you remove everything, the rebase will be aborted.
 +However, if you remove everything, the rebase will be aborted.
  
 -      " | git stripspace --comment-lines >>"$todo"
 +" | git stripspace --comment-lines >>"$todo"
  
        if test -z "$keep_empty"
        then
        has_action "$todo" ||
                return 2
  
-       git rebase--helper --check-todo-list || {
+       git rebase--interactive --check-todo-list || {
                ret=$?
                checkout_onto
                exit $ret
diff --combined git.c
index 8e522768315581a7a401e8cc6a09900e9ab9ad68,81aabd1423a99dad6ad17ffaedb6447de04fd51c..2f604a41eaf620f4d185c612cd7c6aee975faa67
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -164,7 -164,7 +164,7 @@@ static int handle_options(const char **
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--no-replace-objects")) {
 -                      check_replace_refs = 0;
 +                      read_replace_refs = 0;
                        setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
                        if (envchanged)
                                *envchanged = 1;
@@@ -318,9 -318,6 +318,9 @@@ static int handle_alias(int *argcp, con
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
 +              if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
 +                      fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
 +                                 alias_command, alias_string);
                if (alias_string[0] == '!') {
                        struct child_process child = CHILD_PROCESS_INIT;
                        int nongit_ok;
@@@ -417,10 -414,7 +417,10 @@@ static int run_builtin(struct cmd_struc
  
        trace_argv_printf(argv, "trace: built-in: git");
  
 +      validate_cache_entries(&the_index);
        status = p->fn(argc, argv, prefix);
 +      validate_cache_entries(&the_index);
 +
        if (status)
                return status;
  
@@@ -511,7 -505,6 +511,7 @@@ static struct cmd_struct commands[] = 
        { "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
        { "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
        { "mktree", cmd_mktree, RUN_SETUP },
 +      { "multi-pack-index", cmd_multi_pack_index, RUN_SETUP_GENTLY },
        { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
        { "name-rev", cmd_name_rev, RUN_SETUP },
        { "notes", cmd_notes, RUN_SETUP },
        { "prune-packed", cmd_prune_packed, RUN_SETUP },
        { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
        { "push", cmd_push, RUN_SETUP },
 +      { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
        { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
-       { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE },
 +      /*
 +       * NEEDSWORK: Until the rebase is independent and needs no redirection
 +       * to rebase shell script this is kept as is, then should be changed to
 +       * RUN_SETUP | NEED_WORK_TREE
 +       */
 +      { "rebase", cmd_rebase },
+       { "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
        { "remote", cmd_remote, RUN_SETUP },
@@@ -684,8 -670,6 +684,8 @@@ static void execv_dashed_external(cons
  static int run_argv(int *argcp, const char ***argv)
  {
        int done_alias = 0;
 +      struct string_list cmd_list = STRING_LIST_INIT_NODUP;
 +      struct string_list_item *seen;
  
        while (1) {
                /*
                /* .. then try the external ones */
                execv_dashed_external(*argv);
  
 -              /* It could be an alias -- this works around the insanity
 +              seen = unsorted_string_list_lookup(&cmd_list, *argv[0]);
 +              if (seen) {
 +                      int i;
 +                      struct strbuf sb = STRBUF_INIT;
 +                      for (i = 0; i < cmd_list.nr; i++) {
 +                              struct string_list_item *item = &cmd_list.items[i];
 +
 +                              strbuf_addf(&sb, "\n  %s", item->string);
 +                              if (item == seen)
 +                                      strbuf_addstr(&sb, " <==");
 +                              else if (i == cmd_list.nr - 1)
 +                                      strbuf_addstr(&sb, " ==>");
 +                      }
 +                      die(_("alias loop detected: expansion of '%s' does"
 +                            " not terminate:%s"), cmd_list.items[0].string, sb.buf);
 +              }
 +
 +              string_list_append(&cmd_list, *argv[0]);
 +
 +              /*
 +               * It could be an alias -- this works around the insanity
                 * of overriding "git log" with "git show" by having
                 * alias.log = show
                 */
 -              if (done_alias)
 -                      break;
                if (!handle_alias(argcp, argv))
                        break;
                done_alias = 1;
        }
  
 +      string_list_clear(&cmd_list, 0);
 +
        return done_alias;
  }
  
diff --combined sequencer.c
index 0c164d5f98f152156ef21a79159f6348fecb0273,8dd6db5a017030da8468a48fe868d2982394699d..3c86c7694be5c364eaa7b4ea9724c66854b7477a
@@@ -30,7 -30,7 +30,8 @@@
  #include "oidset.h"
  #include "commit-slab.h"
  #include "alias.h"
 +#include "commit-reach.h"
+ #include "rebase-interactive.h"
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
@@@ -53,7 -53,10 +54,10 @@@ static GIT_PATH_FUNC(rebase_path, "reba
   * the lines are processed, they are removed from the front of this
   * file and written to the tail of 'done'.
   */
- static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
+ GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
+ static GIT_PATH_FUNC(rebase_path_todo_backup,
+                    "rebase-merge/git-rebase-todo.backup")
  /*
   * The rebase command lines that have already been processed. A line
   * is moved here when it is first handled, before any associated user
@@@ -64,12 -67,12 +68,12 @@@ static GIT_PATH_FUNC(rebase_path_done, 
   * The file to keep track of how many commands were already processed (e.g.
   * for the prompt).
   */
 -static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum");
 +static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum")
  /*
   * The file to keep track of how many commands are to be processed in total
   * (e.g. for the prompt).
   */
 -static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end");
 +static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end")
  /*
   * The commit message that is planned to be used for any changes that
   * need to be committed following a user interaction.
@@@ -141,7 -144,7 +145,7 @@@ static GIT_PATH_FUNC(rebase_path_refs_t
  
  /*
   * The following files are written by git-rebase just after parsing the
-  * command-line (and are only consumed, not modified, by the sequencer).
+  * command-line.
   */
  static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
  static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
@@@ -153,6 -156,7 +157,7 @@@ static GIT_PATH_FUNC(rebase_path_autost
  static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
  static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
  static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
+ static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
  
  static int git_sequencer_config(const char *k, const char *v, void *cb)
  {
@@@ -226,16 -230,13 +231,16 @@@ static const char *get_todo_path(const 
   * Returns 3 when sob exists within conforming footer as last entry
   */
  static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 -      int ignore_footer)
 +      size_t ignore_footer)
  {
 +      struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
        struct trailer_info info;
 -      int i;
 +      size_t i;
        int found_sob = 0, found_sob_last = 0;
  
 -      trailer_info_get(&info, sb->buf);
 +      opts.no_divider = 1;
 +
 +      trailer_info_get(&info, sb->buf, &opts);
  
        if (info.trailer_start == info.trailer_end)
                return 0;
@@@ -311,7 -312,7 +316,7 @@@ static const char *action_name(const st
        case REPLAY_INTERACTIVE_REBASE:
                return N_("rebase -i");
        }
 -      die(_("Unknown action: %d"), opts->action);
 +      die(_("unknown action: %d"), opts->action);
  }
  
  struct commit_message {
@@@ -377,8 -378,8 +382,8 @@@ static void print_advice(int show_hint
        }
  }
  
static int write_message(const void *buf, size_t len, const char *filename,
-                        int append_eol)
+ int write_message(const void *buf, size_t len, const char *filename,
+                 int append_eol)
  {
        struct lock_file msg_file = LOCK_INIT;
  
@@@ -437,7 -438,7 +442,7 @@@ static int read_oneliner(struct strbuf 
  
  static struct tree *empty_tree(void)
  {
 -      return lookup_tree(the_hash_algo->empty_tree);
 +      return lookup_tree(the_repository, the_repository->hash_algo->empty_tree);
  }
  
  static int error_dirty_index(struct replay_opts *opts)
@@@ -474,8 -475,8 +479,8 @@@ static int fast_forward_to(const struc
        struct strbuf sb = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
  
 -      read_cache();
 -      if (checkout_fast_forward(from, to, 1))
 +      read_index(&the_index);
 +      if (checkout_fast_forward(the_repository, from, to, 1))
                return -1; /* the callee should have complained already */
  
        strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
@@@ -598,7 -599,7 +603,7 @@@ static int is_index_unchanged(void
        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return error(_("could not resolve HEAD commit"));
  
 -      head_commit = lookup_commit(&head_oid);
 +      head_commit = lookup_commit(the_repository, &head_oid);
  
        /*
         * If head_commit is NULL, check_commit, called from
        if (!(cache_tree_oid = get_cache_tree_oid()))
                return -1;
  
 -      return !oidcmp(cache_tree_oid, get_commit_tree_oid(head_commit));
 +      return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
  }
  
  static int write_author_script(const char *message)
@@@ -643,7 -644,7 +648,7 @@@ missing_author
                else if (*message != '\'')
                        strbuf_addch(&buf, *(message++));
                else
 -                      strbuf_addf(&buf, "'\\\\%c'", *(message++));
 +                      strbuf_addf(&buf, "'\\%c'", *(message++));
        strbuf_addstr(&buf, "'\nGIT_AUTHOR_EMAIL='");
        while (*message && *message != '\n' && *message != '\r')
                if (skip_prefix(message, "> ", &message))
                else if (*message != '\'')
                        strbuf_addch(&buf, *(message++));
                else
 -                      strbuf_addf(&buf, "'\\\\%c'", *(message++));
 +                      strbuf_addf(&buf, "'\\%c'", *(message++));
        strbuf_addstr(&buf, "'\nGIT_AUTHOR_DATE='@");
        while (*message && *message != '\n' && *message != '\r')
                if (*message != '\'')
                        strbuf_addch(&buf, *(message++));
                else
 -                      strbuf_addf(&buf, "'\\\\%c'", *(message++));
 +                      strbuf_addf(&buf, "'\\%c'", *(message++));
 +      strbuf_addch(&buf, '\'');
        res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);
        strbuf_release(&buf);
        return res;
  }
  
 +
 +/*
 + * write_author_script() used to fail to terminate the last line with a "'" and
 + * also escaped "'" incorrectly as "'\\\\''" rather than "'\\''". We check for
 + * the terminating "'" on the last line to see how "'" has been escaped in case
 + * git was upgraded while rebase was stopped.
 + */
 +static int quoting_is_broken(const char *s, size_t n)
 +{
 +      /* Skip any empty lines in case the file was hand edited */
 +      while (n > 0 && s[--n] == '\n')
 +              ; /* empty */
 +      if (n > 0 && s[n] != '\'')
 +              return 1;
 +
 +      return 0;
 +}
 +
  /*
   * Read a list of environment variable assignments (such as the author-script
   * file) into an environment block. Returns -1 on error, 0 otherwise.
  static int read_env_script(struct argv_array *env)
  {
        struct strbuf script = STRBUF_INIT;
 -      int i, count = 0;
 -      char *p, *p2;
 +      int i, count = 0, sq_bug;
 +      const char *p2;
 +      char *p;
  
        if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
                return -1;
 -
 +      /* write_author_script() used to quote incorrectly */
 +      sq_bug = quoting_is_broken(script.buf, script.len);
        for (p = script.buf; *p; p++)
 -              if (skip_prefix(p, "'\\\\''", (const char **)&p2))
 +              if (sq_bug && skip_prefix(p, "'\\\\''", &p2))
 +                      strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
 +              else if (skip_prefix(p, "'\\''", &p2))
                        strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
                else if (*p == '\'')
                        strbuf_splice(&script, p-- - script.buf, 1, "", 0);
@@@ -735,51 -713,43 +740,51 @@@ static const char *read_author_ident(st
        const char *keys[] = {
                "GIT_AUTHOR_NAME=", "GIT_AUTHOR_EMAIL=", "GIT_AUTHOR_DATE="
        };
 -      char *in, *out, *eol;
 -      int i = 0, len;
 +      struct strbuf out = STRBUF_INIT;
 +      char *in, *eol;
 +      const char *val[3];
 +      int i = 0;
  
        if (strbuf_read_file(buf, rebase_path_author_script(), 256) <= 0)
                return NULL;
  
        /* dequote values and construct ident line in-place */
 -      for (in = out = buf->buf; i < 3 && in - buf->buf < buf->len; i++) {
 +      for (in = buf->buf; i < 3 && in - buf->buf < buf->len; i++) {
                if (!skip_prefix(in, keys[i], (const char **)&in)) {
 -                      warning("could not parse '%s' (looking for '%s'",
 +                      warning(_("could not parse '%s' (looking for '%s')"),
                                rebase_path_author_script(), keys[i]);
                        return NULL;
                }
  
                eol = strchrnul(in, '\n');
                *eol = '\0';
 -              sq_dequote(in);
 -              len = strlen(in);
 -
 -              if (i > 0) /* separate values by spaces */
 -                      *(out++) = ' ';
 -              if (i == 1) /* email needs to be surrounded by <...> */
 -                      *(out++) = '<';
 -              memmove(out, in, len);
 -              out += len;
 -              if (i == 1) /* email needs to be surrounded by <...> */
 -                      *(out++) = '>';
 +              if (!sq_dequote(in)) {
 +                      warning(_("bad quoting on %s value in '%s'"),
 +                              keys[i], rebase_path_author_script());
 +                      return NULL;
 +              }
 +              val[i] = in;
                in = eol + 1;
        }
  
        if (i < 3) {
 -              warning("could not parse '%s' (looking for '%s')",
 +              warning(_("could not parse '%s' (looking for '%s')"),
                        rebase_path_author_script(), keys[i]);
                return NULL;
        }
  
 -      buf->len = out - buf->buf;
 +      /* validate date since fmt_ident() will die() on bad value */
 +      if (parse_date(val[2], &out)){
 +              warning(_("invalid date format '%s' in '%s'"),
 +                      val[2], rebase_path_author_script());
 +              strbuf_release(&out);
 +              return NULL;
 +      }
 +
 +      strbuf_reset(&out);
 +      strbuf_addstr(&out, fmt_ident(val[0], val[1], val[2], 0));
 +      strbuf_swap(buf, &out);
 +      strbuf_release(&out);
        return buf->buf;
  }
  
@@@ -804,6 -774,23 +809,23 @@@ N_("you have staged changes in your wor
  #define VERIFY_MSG  (1<<4)
  #define CREATE_ROOT_COMMIT (1<<5)
  
+ static int run_command_silent_on_success(struct child_process *cmd)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       int rc;
+       cmd->stdout_to_stderr = 1;
+       rc = pipe_command(cmd,
+                         NULL, 0,
+                         NULL, 0,
+                         &buf, 0);
+       if (rc)
+               fputs(buf.buf, stderr);
+       strbuf_release(&buf);
+       return rc;
+ }
  /*
   * If we are cherry-pick, and if the merge did not result in
   * hand-editing, we will hit this commit and inherit the original
@@@ -824,18 -811,11 +846,18 @@@ static int run_git_commit(const char *d
  
        if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {
                struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
 -              const char *author = is_rebase_i(opts) ?
 -                      read_author_ident(&script) : NULL;
 +              const char *author = NULL;
                struct object_id root_commit, *cache_tree_oid;
                int res = 0;
  
 +              if (is_rebase_i(opts)) {
 +                      author = read_author_ident(&script);
 +                      if (!author) {
 +                              strbuf_release(&script);
 +                              return -1;
 +                      }
 +              }
 +
                if (!defmsg)
                        BUG("root commit without message");
  
  
        cmd.git_cmd = 1;
  
-       if (is_rebase_i(opts)) {
-               if (!(flags & EDIT_MSG)) {
-                       cmd.stdout_to_stderr = 1;
-                       cmd.err = -1;
-               }
-               if (read_env_script(&cmd.env_array)) {
-                       const char *gpg_opt = gpg_sign_opt_quoted(opts);
+       if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
+               const char *gpg_opt = gpg_sign_opt_quoted(opts);
  
-                       return error(_(staged_changes_advice),
-                                    gpg_opt, gpg_opt);
-               }
+               return error(_(staged_changes_advice),
+                            gpg_opt, gpg_opt);
        }
  
        argv_array_push(&cmd.args, "commit");
        if ((flags & ALLOW_EMPTY))
                argv_array_push(&cmd.args, "--allow-empty");
  
 -      if (opts->allow_empty_message)
 +      if (!(flags & EDIT_MSG))
                argv_array_push(&cmd.args, "--allow-empty-message");
  
-       if (cmd.err == -1) {
-               /* hide stderr on success */
-               struct strbuf buf = STRBUF_INIT;
-               int rc = pipe_command(&cmd,
-                                     NULL, 0,
-                                     /* stdout is already redirected */
-                                     NULL, 0,
-                                     &buf, 0);
-               if (rc)
-                       fputs(buf.buf, stderr);
-               strbuf_release(&buf);
-               return rc;
-       }
-       return run_command(&cmd);
+       if (is_rebase_i(opts) && !(flags & EDIT_MSG))
+               return run_command_silent_on_success(&cmd);
+       else
+               return run_command(&cmd);
  }
  
  static int rest_is_empty(const struct strbuf *sb, int start)
@@@ -1143,7 -1105,7 +1147,7 @@@ void print_commit_summary(const char *p
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
  
 -      commit = lookup_commit(oid);
 +      commit = lookup_commit(the_repository, oid);
        if (!commit)
                die(_("couldn't look up newly created commit"));
        if (parse_commit(commit))
        strbuf_release(&author_ident);
        strbuf_release(&committer_ident);
  
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
  
        rev.diff = 1;
@@@ -1218,10 -1180,10 +1222,10 @@@ static int parse_head(struct commit **h
        if (get_oid("HEAD", &oid)) {
                current_head = NULL;
        } else {
 -              current_head = lookup_commit_reference(&oid);
 +              current_head = lookup_commit_reference(the_repository, &oid);
                if (!current_head)
                        return error(_("could not parse HEAD"));
 -              if (oidcmp(&oid, &current_head->object.oid)) {
 +              if (!oideq(&oid, &current_head->object.oid)) {
                        warning(_("HEAD %s is not a commit!"),
                                oid_to_hex(&oid));
                }
@@@ -1286,14 -1248,14 +1290,14 @@@ static int try_to_commit(struct strbuf 
                commit_list_insert(current_head, &parents);
        }
  
 -      if (write_cache_as_tree(&tree, 0, NULL)) {
 +      if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL)) {
                res = error(_("git write-tree failed to write a tree"));
                goto out;
        }
  
 -      if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
 -                                            get_commit_tree_oid(current_head) :
 -                                            the_hash_algo->empty_tree, &tree)) {
 +      if (!(flags & ALLOW_EMPTY) && oideq(current_head ?
 +                                          get_commit_tree_oid(current_head) :
 +                                          the_hash_algo->empty_tree, &tree)) {
                res = 1; /* run 'git commit' to display error message */
                goto out;
        }
  
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
 -      if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
 +      if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
                res = 1; /* run 'git commit' to display error message */
                goto out;
        }
@@@ -1398,7 -1360,7 +1402,7 @@@ static int is_original_commit_empty(str
                ptree_oid = the_hash_algo->empty_tree; /* commit is root */
        }
  
 -      return !oidcmp(ptree_oid, get_commit_tree_oid(commit));
 +      return oideq(ptree_oid, get_commit_tree_oid(commit));
  }
  
  /*
@@@ -1487,7 -1449,7 +1491,7 @@@ static const char *command_to_string(co
  {
        if (command < TODO_COMMENT)
                return todo_command_info[command].str;
 -      die("Unknown command: %d", command);
 +      die(_("unknown command: %d"), command);
  }
  
  static char command_to_char(const enum todo_command command)
@@@ -1553,7 -1515,7 +1557,7 @@@ static int update_squash_messages(enum 
  
                if (get_oid("HEAD", &head))
                        return error(_("need a HEAD to fixup"));
 -              if (!(head_commit = lookup_commit_reference(&head)))
 +              if (!(head_commit = lookup_commit_reference(the_repository, &head)))
                        return error(_("could not read HEAD"));
                if (!(head_message = get_commit_buffer(head_commit, NULL)))
                        return error(_("could not read HEAD's commit message"));
                unlink(rebase_path_fixup_msg());
                strbuf_addf(&buf, "\n%c ", comment_line_char);
                strbuf_addf(&buf, _("This is the commit message #%d:"),
 -                          ++opts->current_fixup_count);
 +                          ++opts->current_fixup_count + 1);
                strbuf_addstr(&buf, "\n\n");
                strbuf_addstr(&buf, body);
        } else if (command == TODO_FIXUP) {
                strbuf_addf(&buf, "\n%c ", comment_line_char);
                strbuf_addf(&buf, _("The commit message #%d will be skipped:"),
 -                          ++opts->current_fixup_count);
 +                          ++opts->current_fixup_count + 1);
                strbuf_addstr(&buf, "\n\n");
                strbuf_add_commented_lines(&buf, body, strlen(body));
        } else
@@@ -1672,13 -1634,13 +1676,13 @@@ static int do_pick_commit(enum todo_com
                 * that represents the "current" state for merge-recursive
                 * to work on.
                 */
 -              if (write_cache_as_tree(&head, 0, NULL))
 +              if (write_index_as_tree(&head, &the_index, get_index_file(), 0, NULL))
                        return error(_("your index file is unmerged."));
        } else {
                unborn = get_oid("HEAD", &head);
                /* Do we want to generate a root commit? */
                if (is_pick_or_similar(command) && opts->have_squash_onto &&
 -                  !oidcmp(&head, &opts->squash_onto)) {
 +                  oideq(&head, &opts->squash_onto)) {
                        if (is_fixup(command))
                                return error(_("cannot fixup root commit"));
                        flags |= CREATE_ROOT_COMMIT;
                        oid_to_hex(&commit->object.oid));
  
        if (opts->allow_ff && !is_fixup(command) &&
 -          ((parent && !oidcmp(&parent->object.oid, &head)) ||
 +          ((parent && oideq(&parent->object.oid, &head)) ||
             (!parent && unborn))) {
                if (is_rebase_i(opts))
                        write_author_script(msg.message);
  
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
 -              res |= try_merge_command(opts->strategy,
 +              res |= try_merge_command(the_repository, opts->strategy,
                                         opts->xopts_nr, (const char **)opts->xopts,
                                        common, oid_to_hex(&head), remotes);
                free_commit_list(common);
                      : _("could not apply %s... %s"),
                      short_commit_name(commit), msg.subject);
                print_advice(res == 1, opts);
 -              rerere(opts->allow_rerere_auto);
 +              repo_rerere(the_repository, opts->allow_rerere_auto);
                goto leave;
        }
  
@@@ -1906,6 -1868,8 +1910,6 @@@ static int prepare_revs(struct replay_o
        if (prepare_revision_walk(opts->revs))
                return error(_("revision walk setup failed"));
  
 -      if (!opts->revs->commits)
 -              return error(_("empty commit set passed"));
        return 0;
  }
  
@@@ -1913,7 -1877,7 +1917,7 @@@ static int read_and_refresh_cache(struc
  {
        struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
 -      if (read_index_preload(&the_index, NULL) < 0) {
 +      if (read_index_preload(&the_index, NULL, 0) < 0) {
                rollback_lock_file(&index_lock);
                return error(_("git %s: failed to read the index"),
                        _(action_name(opts)));
@@@ -2049,7 -2013,7 +2053,7 @@@ static int parse_insn_line(struct todo_
        if (status < 0)
                return -1;
  
 -      item->commit = lookup_commit_reference(&commit_oid);
 +      item->commit = lookup_commit_reference(the_repository, &commit_oid);
        return !item->commit;
  }
  
@@@ -2244,21 -2208,14 +2248,14 @@@ static int populate_opts_cb(const char 
        return 0;
  }
  
static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
  {
        int i;
-       char *strategy_opts_string;
-       strbuf_reset(buf);
-       if (!read_oneliner(buf, rebase_path_strategy(), 0))
-               return;
-       opts->strategy = strbuf_detach(buf, NULL);
-       if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
-               return;
+       char *strategy_opts_string = raw_opts;
  
-       strategy_opts_string = buf->buf;
        if (*strategy_opts_string == ' ')
                strategy_opts_string++;
        opts->xopts_nr = split_cmdline(strategy_opts_string,
                                       (const char ***)&opts->xopts);
        for (i = 0; i < opts->xopts_nr; i++) {
        }
  }
  
+ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
+ {
+       strbuf_reset(buf);
+       if (!read_oneliner(buf, rebase_path_strategy(), 0))
+               return;
+       opts->strategy = strbuf_detach(buf, NULL);
+       if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
+               return;
+       parse_strategy_opts(opts, buf->buf);
+ }
  static int read_populate_opts(struct replay_opts *opts)
  {
        if (is_rebase_i(opts)) {
        return 0;
  }
  
+ static void write_strategy_opts(struct replay_opts *opts)
+ {
+       int i;
+       struct strbuf buf = STRBUF_INIT;
+       for (i = 0; i < opts->xopts_nr; ++i)
+               strbuf_addf(&buf, " --%s", opts->xopts[i]);
+       write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
+       strbuf_release(&buf);
+ }
+ int write_basic_state(struct replay_opts *opts, const char *head_name,
+                     const char *onto, const char *orig_head)
+ {
+       const char *quiet = getenv("GIT_QUIET");
+       if (head_name)
+               write_file(rebase_path_head_name(), "%s\n", head_name);
+       if (onto)
+               write_file(rebase_path_onto(), "%s\n", onto);
+       if (orig_head)
+               write_file(rebase_path_orig_head(), "%s\n", orig_head);
+       if (quiet)
+               write_file(rebase_path_quiet(), "%s\n", quiet);
+       else
+               write_file(rebase_path_quiet(), "\n");
+       if (opts->verbose)
+               write_file(rebase_path_verbose(), "");
+       if (opts->strategy)
+               write_file(rebase_path_strategy(), "%s\n", opts->strategy);
+       if (opts->xopts_nr > 0)
+               write_strategy_opts(opts);
+       if (opts->allow_rerere_auto == RERERE_AUTOUPDATE)
+               write_file(rebase_path_allow_rerere_autoupdate(), "--rerere-autoupdate\n");
+       else if (opts->allow_rerere_auto == RERERE_NOAUTOUPDATE)
+               write_file(rebase_path_allow_rerere_autoupdate(), "--no-rerere-autoupdate\n");
+       if (opts->gpg_sign)
+               write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
+       if (opts->signoff)
+               write_file(rebase_path_signoff(), "--signoff\n");
+       return 0;
+ }
  static int walk_revs_populate_todo(struct todo_list *todo_list,
                                struct replay_opts *opts)
  {
                        short_commit_name(commit), subject_len, subject);
                unuse_commit_buffer(commit, commit_buffer);
        }
 +
 +      if (!todo_list->nr)
 +              return error(_("empty commit set passed"));
 +
        return 0;
  }
  
@@@ -2426,7 -2440,7 +2484,7 @@@ static int rollback_is_safe(void
        if (get_oid("HEAD", &actual_head))
                oidclr(&actual_head);
  
 -      return !oidcmp(&actual_head, &expected_head);
 +      return oideq(&actual_head, &expected_head);
  }
  
  static int reset_for_rollback(const struct object_id *oid)
@@@ -2599,7 -2613,7 +2657,7 @@@ static int make_patch(struct commit *co
  
        strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
 -      init_revisions(&log_tree_opt, NULL);
 +      repo_init_revisions(the_repository, &log_tree_opt, NULL);
        log_tree_opt.abbrev = 0;
        log_tree_opt.diff = 1;
        log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
@@@ -2643,39 -2657,23 +2701,39 @@@ static int error_with_patch(struct comm
        const char *subject, int subject_len,
        struct replay_opts *opts, int exit_code, int to_amend)
  {
 -      if (make_patch(commit, opts))
 -              return -1;
 +      if (commit) {
 +              if (make_patch(commit, opts))
 +                      return -1;
 +      } else if (copy_file(rebase_path_message(),
 +                           git_path_merge_msg(the_repository), 0666))
 +              return error(_("unable to copy '%s' to '%s'"),
 +                           git_path_merge_msg(the_repository), rebase_path_message());
  
        if (to_amend) {
                if (intend_to_amend())
                        return -1;
  
 -              fprintf(stderr, "You can amend the commit now, with\n"
 -                      "\n"
 -                      "  git commit --amend %s\n"
 -                      "\n"
 -                      "Once you are satisfied with your changes, run\n"
 -                      "\n"
 -                      "  git rebase --continue\n", gpg_sign_opt_quoted(opts));
 -      } else if (exit_code)
 -              fprintf(stderr, "Could not apply %s... %.*s\n",
 -                      short_commit_name(commit), subject_len, subject);
 +              fprintf(stderr,
 +                      _("You can amend the commit now, with\n"
 +                        "\n"
 +                        "  git commit --amend %s\n"
 +                        "\n"
 +                        "Once you are satisfied with your changes, run\n"
 +                        "\n"
 +                        "  git rebase --continue\n"),
 +                      gpg_sign_opt_quoted(opts));
 +      } else if (exit_code) {
 +              if (commit)
 +                      fprintf_ln(stderr, _("Could not apply %s... %.*s"),
 +                                 short_commit_name(commit), subject_len, subject);
 +              else
 +                      /*
 +                       * We don't have the hash of the parent so
 +                       * just print the line from the todo file.
 +                       */
 +                      fprintf_ln(stderr, _("Could not merge %.*s"),
 +                                 subject_len, subject);
 +      }
  
        return exit_code;
  }
@@@ -2703,8 -2701,6 +2761,8 @@@ static int do_exec(const char *command_
        fprintf(stderr, "Executing: %s\n", command_line);
        child_argv[0] = command_line;
        argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
 +      argv_array_pushf(&child_env, "GIT_WORK_TREE=%s",
 +                       absolute_path(get_git_work_tree()));
        status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
                                          child_env.argv);
  
@@@ -2788,7 -2784,7 +2846,7 @@@ static int do_label(const char *name, i
        struct object_id head_oid;
  
        if (len == 1 && *name == '#')
 -              return error("Illegal label name: '%.*s'", len, name);
 +              return error(_("illegal label name: '%.*s'"), len, name);
  
        strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
        strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name);
@@@ -2910,26 -2906,6 +2968,26 @@@ static int do_reset(const char *name, i
        return ret;
  }
  
 +static struct commit *lookup_label(const char *label, int len,
 +                                 struct strbuf *buf)
 +{
 +      struct commit *commit;
 +
 +      strbuf_reset(buf);
 +      strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
 +      commit = lookup_commit_reference_by_name(buf->buf);
 +      if (!commit) {
 +              /* fall back to non-rewritten ref or commit */
 +              strbuf_splice(buf, 0, strlen("refs/rewritten/"), "", 0);
 +              commit = lookup_commit_reference_by_name(buf->buf);
 +      }
 +
 +      if (!commit)
 +              error(_("could not resolve '%s'"), buf->buf);
 +
 +      return commit;
 +}
 +
  static int do_merge(struct commit *commit, const char *arg, int arg_len,
                    int flags, struct replay_opts *opts)
  {
        struct strbuf ref_name = STRBUF_INIT;
        struct commit *head_commit, *merge_commit, *i;
        struct commit_list *bases, *j, *reversed = NULL;
 +      struct commit_list *to_merge = NULL, **tail = &to_merge;
        struct merge_options o;
 -      int merge_arg_len, oneline_offset, can_fast_forward, ret;
 +      int merge_arg_len, oneline_offset, can_fast_forward, ret, k;
        static struct lock_file lock;
        const char *p;
  
                goto leave_merge;
        }
  
 -      oneline_offset = arg_len;
 -      merge_arg_len = strcspn(arg, " \t\n");
 -      p = arg + merge_arg_len;
 -      p += strspn(p, " \t\n");
 -      if (*p == '#' && (!p[1] || isspace(p[1]))) {
 -              p += 1 + strspn(p + 1, " \t\n");
 -              oneline_offset = p - arg;
 -      } else if (p - arg < arg_len)
 -              BUG("octopus merges are not supported yet: '%s'", p);
 -
 -      strbuf_addf(&ref_name, "refs/rewritten/%.*s", merge_arg_len, arg);
 -      merge_commit = lookup_commit_reference_by_name(ref_name.buf);
 -      if (!merge_commit) {
 -              /* fall back to non-rewritten ref or commit */
 -              strbuf_splice(&ref_name, 0, strlen("refs/rewritten/"), "", 0);
 -              merge_commit = lookup_commit_reference_by_name(ref_name.buf);
 +      /*
 +       * For octopus merges, the arg starts with the list of revisions to be
 +       * merged. The list is optionally followed by '#' and the oneline.
 +       */
 +      merge_arg_len = oneline_offset = arg_len;
 +      for (p = arg; p - arg < arg_len; p += strspn(p, " \t\n")) {
 +              if (!*p)
 +                      break;
 +              if (*p == '#' && (!p[1] || isspace(p[1]))) {
 +                      p += 1 + strspn(p + 1, " \t\n");
 +                      oneline_offset = p - arg;
 +                      break;
 +              }
 +              k = strcspn(p, " \t\n");
 +              if (!k)
 +                      continue;
 +              merge_commit = lookup_label(p, k, &ref_name);
 +              if (!merge_commit) {
 +                      ret = error(_("unable to parse '%.*s'"), k, p);
 +                      goto leave_merge;
 +              }
 +              tail = &commit_list_insert(merge_commit, tail)->next;
 +              p += k;
 +              merge_arg_len = p - arg;
        }
  
 -      if (!merge_commit) {
 -              ret = error(_("could not resolve '%s'"), ref_name.buf);
 +      if (!to_merge) {
 +              ret = error(_("nothing to merge: '%.*s'"), arg_len, arg);
                goto leave_merge;
        }
  
        if (opts->have_squash_onto &&
 -          !oidcmp(&head_commit->object.oid, &opts->squash_onto)) {
 +          oideq(&head_commit->object.oid, &opts->squash_onto)) {
                /*
                 * When the user tells us to "merge" something into a
                 * "[new root]", let's simply fast-forward to the merge head.
                 */
                rollback_lock_file(&lock);
 -              ret = fast_forward_to(&merge_commit->object.oid,
 -                                     &head_commit->object.oid, 0, opts);
 +              if (to_merge->next)
 +                      ret = error(_("octopus merge cannot be executed on "
 +                                    "top of a [new root]"));
 +              else
 +                      ret = fast_forward_to(&to_merge->item->object.oid,
 +                                            &head_commit->object.oid, 0,
 +                                            opts);
                goto leave_merge;
        }
  
                        p = arg + oneline_offset;
                        len = arg_len - oneline_offset;
                } else {
 -                      strbuf_addf(&buf, "Merge branch '%.*s'",
 +                      strbuf_addf(&buf, "Merge %s '%.*s'",
 +                                  to_merge->next ? "branches" : "branch",
                                    merge_arg_len, arg);
                        p = buf.buf;
                        len = buf.len;
         * commit, we cannot fast-forward.
         */
        can_fast_forward = opts->allow_ff && commit && commit->parents &&
 -              !oidcmp(&commit->parents->item->object.oid,
 -                      &head_commit->object.oid);
 +              oideq(&commit->parents->item->object.oid,
 +                    &head_commit->object.oid);
  
        /*
 -       * If the merge head is different from the original one, we cannot
 +       * If any merge head is different from the original one, we cannot
         * fast-forward.
         */
        if (can_fast_forward) {
 -              struct commit_list *second_parent = commit->parents->next;
 +              struct commit_list *p = commit->parents->next;
  
 -              if (second_parent && !second_parent->next &&
 -                  oidcmp(&merge_commit->object.oid,
 -                         &second_parent->item->object.oid))
 +              for (j = to_merge; j && p; j = j->next, p = p->next)
 +                      if (!oideq(&j->item->object.oid,
 +                                 &p->item->object.oid)) {
 +                              can_fast_forward = 0;
 +                              break;
 +                      }
 +              /*
 +               * If the number of merge heads differs from the original merge
 +               * commit, we cannot fast-forward.
 +               */
 +              if (j || p)
                        can_fast_forward = 0;
        }
  
 -      if (can_fast_forward && commit->parents->next &&
 -          !commit->parents->next->next &&
 -          !oidcmp(&commit->parents->next->item->object.oid,
 -                  &merge_commit->object.oid)) {
 +      if (can_fast_forward) {
                rollback_lock_file(&lock);
                ret = fast_forward_to(&commit->object.oid,
                                      &head_commit->object.oid, 0, opts);
                goto leave_merge;
        }
  
 +      if (to_merge->next) {
 +              /* Octopus merge */
 +              struct child_process cmd = CHILD_PROCESS_INIT;
 +
 +              if (read_env_script(&cmd.env_array)) {
 +                      const char *gpg_opt = gpg_sign_opt_quoted(opts);
 +
 +                      ret = error(_(staged_changes_advice), gpg_opt, gpg_opt);
 +                      goto leave_merge;
 +              }
 +
 +              cmd.git_cmd = 1;
 +              argv_array_push(&cmd.args, "merge");
 +              argv_array_push(&cmd.args, "-s");
 +              argv_array_push(&cmd.args, "octopus");
 +              argv_array_push(&cmd.args, "--no-edit");
 +              argv_array_push(&cmd.args, "--no-ff");
 +              argv_array_push(&cmd.args, "--no-log");
 +              argv_array_push(&cmd.args, "--no-stat");
 +              argv_array_push(&cmd.args, "-F");
 +              argv_array_push(&cmd.args, git_path_merge_msg(the_repository));
 +              if (opts->gpg_sign)
 +                      argv_array_push(&cmd.args, opts->gpg_sign);
 +
 +              /* Add the tips to be merged */
 +              for (j = to_merge; j; j = j->next)
 +                      argv_array_push(&cmd.args,
 +                                      oid_to_hex(&j->item->object.oid));
 +
 +              strbuf_release(&ref_name);
 +              unlink(git_path_cherry_pick_head(the_repository));
 +              rollback_lock_file(&lock);
 +
 +              rollback_lock_file(&lock);
 +              ret = run_command(&cmd);
 +
 +              /* force re-reading of the cache */
 +              if (!ret && (discard_cache() < 0 || read_cache() < 0))
 +                      ret = error(_("could not read index"));
 +              goto leave_merge;
 +      }
 +
 +      merge_commit = to_merge->item;
        write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
                      git_path_merge_head(the_repository), 0);
        write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
  
        bases = get_merge_bases(head_commit, merge_commit);
 -      if (bases && !oidcmp(&merge_commit->object.oid,
 -                           &bases->item->object.oid)) {
 +      if (bases && oideq(&merge_commit->object.oid,
 +                         &bases->item->object.oid)) {
                ret = 0;
                /* skip merging an ancestor of HEAD */
                goto leave_merge;
  
        rollback_lock_file(&lock);
        if (ret)
 -              rerere(opts->allow_rerere_auto);
 +              repo_rerere(the_repository, opts->allow_rerere_auto);
        else
                /*
                 * In case of problems, we now want to return a positive
  leave_merge:
        strbuf_release(&ref_name);
        rollback_lock_file(&lock);
 +      free_commit_list(to_merge);
        return ret;
  }
  
@@@ -3286,6 -3198,55 +3344,55 @@@ static const char *reflog_message(struc
        return buf.buf;
  }
  
+ static int run_git_checkout(struct replay_opts *opts, const char *commit,
+                           const char *action)
+ {
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       cmd.git_cmd = 1;
+       argv_array_push(&cmd.args, "checkout");
+       argv_array_push(&cmd.args, commit);
+       argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
+       if (opts->verbose)
+               return run_command(&cmd);
+       else
+               return run_command_silent_on_success(&cmd);
+ }
+ int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit)
+ {
+       const char *action;
+       if (commit && *commit) {
+               action = reflog_message(opts, "start", "checkout %s", commit);
+               if (run_git_checkout(opts, commit, action))
+                       return error(_("could not checkout %s"), commit);
+       }
+       return 0;
+ }
+ static int checkout_onto(struct replay_opts *opts,
+                        const char *onto_name, const char *onto,
+                        const char *orig_head)
+ {
+       struct object_id oid;
+       const char *action = reflog_message(opts, "start", "checkout %s", onto_name);
+       if (get_oid(orig_head, &oid))
+               return error(_("%s: not a valid OID"), orig_head);
+       if (run_git_checkout(opts, onto, action)) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               return error(_("could not detach HEAD"));
+       }
+       return update_ref(NULL, "ORIG_HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+ }
  static const char rescheduled_advice[] =
  N_("Could not execute the todo command\n"
  "\n"
@@@ -3382,9 -3343,9 +3489,9 @@@ static int pick_commits(struct todo_lis
                                 */
                                if (item->command == TODO_REWORD &&
                                    !get_oid("HEAD", &oid) &&
 -                                  (!oidcmp(&item->commit->object.oid, &oid) ||
 +                                  (oideq(&item->commit->object.oid, &oid) ||
                                     (opts->have_squash_onto &&
 -                                    !oidcmp(&opts->squash_onto, &oid))))
 +                                    oideq(&opts->squash_onto, &oid))))
                                        to_amend = 1;
  
                                return res | error_with_patch(item->commit,
@@@ -3510,7 -3471,7 +3617,7 @@@ cleanup_head_ref
                        struct object_id orig, head;
  
                        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
 -                      init_revisions(&log_tree_opt, NULL);
 +                      repo_init_revisions(the_repository, &log_tree_opt, NULL);
                        log_tree_opt.diff = 1;
                        log_tree_opt.diffopt.output_format =
                                DIFF_FORMAT_DIFFSTAT;
@@@ -3599,7 -3560,7 +3706,7 @@@ static int commit_staged_changes(struc
                if (get_oid_hex(rev.buf, &to_amend))
                        return error(_("invalid contents: '%s'"),
                                rebase_path_amend());
 -              if (!is_clean && oidcmp(&head, &to_amend))
 +              if (!is_clean && !oideq(&head, &to_amend))
                        return error(_("\nYou have uncommitted changes in your "
                                       "working tree. Please, commit them\n"
                                       "first and then run 'git rebase "
                 * the commit message and if there was a squash, let the user
                 * edit it.
                 */
 -              if (is_clean && !oidcmp(&head, &to_amend) &&
 -                  opts->current_fixup_count > 0 &&
 -                  file_exists(rebase_path_stopped_sha())) {
 +              if (!is_clean || !opts->current_fixup_count)
 +                      ; /* this is not the final fixup */
 +              else if (!oideq(&head, &to_amend) ||
 +                       !file_exists(rebase_path_stopped_sha())) {
 +                      /* was a final fixup or squash done manually? */
 +                      if (!is_fixup(peek_command(todo_list, 0))) {
 +                              unlink(rebase_path_fixup_msg());
 +                              unlink(rebase_path_squash_msg());
 +                              unlink(rebase_path_current_fixups());
 +                              strbuf_reset(&opts->current_fixups);
 +                              opts->current_fixup_count = 0;
 +                      }
 +              } else {
 +                      /* we are in a fixup/squash chain */
                        const char *p = opts->current_fixups.buf;
                        int len = opts->current_fixups.len;
  
@@@ -3789,7 -3739,7 +3896,7 @@@ int sequencer_pick_revisions(struct rep
                        continue;
  
                if (!get_oid(name, &oid)) {
 -                      if (!lookup_commit_reference_gently(&oid, 1)) {
 +                      if (!lookup_commit_reference_gently(the_repository, &oid, 1)) {
                                enum object_type type = oid_object_info(the_repository,
                                                                        &oid,
                                                                        NULL);
                if (prepare_revision_walk(opts->revs))
                        return error(_("revision walk setup failed"));
                cmit = get_revision(opts->revs);
 -              if (!cmit || get_revision(opts->revs))
 -                      return error("BUG: expected exactly one commit from walk");
 +              if (!cmit)
 +                      return error(_("empty commit set passed"));
 +              if (get_revision(opts->revs))
 +                      BUG("unexpected extra commit from walk");
                return single_pick(cmit, opts);
        }
  
        return res;
  }
  
 -void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
  {
        unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
        struct strbuf sob = STRBUF_INIT;
@@@ -4060,6 -4008,7 +4167,6 @@@ static int make_script_with_merges(stru
         */
        while ((commit = get_revision(revs))) {
                struct commit_list *to_merge;
 -              int is_octopus;
                const char *p1, *p2;
                struct object_id *oid;
                int is_empty;
                        continue;
                }
  
 -              is_octopus = to_merge && to_merge->next;
 -
 -              if (is_octopus)
 -                      BUG("Octopus merges not yet supported");
 -
                /* Create a label */
                strbuf_reset(&label);
                if (skip_prefix(oneline.buf, "Merge ", &p1) &&
                strbuf_addf(&buf, "%s -C %s",
                            cmd_merge, oid_to_hex(&commit->object.oid));
  
 -              /* label the tip of merged branch */
 -              oid = &to_merge->item->object.oid;
 -              strbuf_addch(&buf, ' ');
 +              /* label the tips of merged branches */
 +              for (; to_merge; to_merge = to_merge->next) {
 +                      oid = &to_merge->item->object.oid;
 +                      strbuf_addch(&buf, ' ');
 +
 +                      if (!oidset_contains(&interesting, oid)) {
 +                              strbuf_addstr(&buf, label_oid(oid, NULL,
 +                                                            &state));
 +                              continue;
 +                      }
  
 -              if (!oidset_contains(&interesting, oid))
 -                      strbuf_addstr(&buf, label_oid(oid, NULL, &state));
 -              else {
                        tips_tail = &commit_list_insert(to_merge->item,
                                                        tips_tail)->next;
  
                        struct object_id *oid = &parent->item->object.oid;
                        if (!oidset_contains(&interesting, oid))
                                continue;
 -                      if (!oidset_contains(&child_seen, oid))
 -                              oidset_insert(&child_seen, oid);
 -                      else
 +                      if (oidset_insert(&child_seen, oid))
                                label_oid(oid, "branch-point", &state);
                }
  
                entry = oidmap_get(&state.commit2label, &commit->object.oid);
  
                if (entry)
 -                      fprintf(out, "\n# Branch %s\n", entry->string);
 +                      fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
                else
                        fprintf(out, "\n");
  
@@@ -4254,7 -4206,7 +4361,7 @@@ int sequencer_make_script(FILE *out, in
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
        int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
  
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        revs.verbose_header = 1;
        if (!rebase_merges)
                revs.max_parents = 1;
@@@ -4311,9 -4263,10 +4418,9 @@@ int sequencer_add_exec_commands(const c
  {
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
 -      struct todo_item *item;
        struct strbuf *buf = &todo_list.buf;
        size_t offset = 0, commands_len = strlen(commands);
 -      int i, first;
 +      int i, insert;
  
        if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error(_("could not read '%s'."), todo_file);
                return error(_("unusable todo list: '%s'"), todo_file);
        }
  
 -      first = 1;
 -      /* insert <commands> before every pick except the first one */
 -      for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
 -              if (item->command == TODO_PICK && !first) {
 -                      strbuf_insert(buf, item->offset_in_buf + offset,
 -                                    commands, commands_len);
 +      /*
 +       * Insert <commands> after every pick. Here, fixup/squash chains
 +       * are considered part of the pick, so we insert the commands *after*
 +       * those chains if there are any.
 +       */
 +      insert = -1;
 +      for (i = 0; i < todo_list.nr; i++) {
 +              enum todo_command command = todo_list.items[i].command;
 +
 +              if (insert >= 0) {
 +                      /* skip fixup/squash chains */
 +                      if (command == TODO_COMMENT)
 +                              continue;
 +                      else if (is_fixup(command)) {
 +                              insert = i + 1;
 +                              continue;
 +                      }
 +                      strbuf_insert(buf,
 +                                    todo_list.items[insert].offset_in_buf +
 +                                    offset, commands, commands_len);
                        offset += commands_len;
 +                      insert = -1;
                }
 -              first = 0;
 +
 +              if (command == TODO_PICK || command == TODO_MERGE)
 +                      insert = i + 1;
        }
  
 -      /* append final <commands> */
 -      strbuf_add(buf, commands, commands_len);
 +      /* insert or append final <commands> */
 +      if (insert >= 0 && insert < todo_list.nr)
 +              strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
 +                            offset, commands, commands_len);
 +      else if (insert >= 0 || !offset)
 +              strbuf_add(buf, commands, commands_len);
  
        i = write_message(buf->buf, buf->len, todo_file, 0);
        todo_list_release(&todo_list);
@@@ -4420,24 -4352,20 +4527,20 @@@ int transform_todos(unsigned flags
        return i;
  }
  
- enum check_level {
-       CHECK_IGNORE = 0, CHECK_WARN, CHECK_ERROR
- };
- static enum check_level get_missing_commit_check_level(void)
+ enum missing_commit_check_level get_missing_commit_check_level(void)
  {
        const char *value;
  
        if (git_config_get_value("rebase.missingcommitscheck", &value) ||
                        !strcasecmp("ignore", value))
-               return CHECK_IGNORE;
+               return MISSING_COMMIT_CHECK_IGNORE;
        if (!strcasecmp("warn", value))
-               return CHECK_WARN;
+               return MISSING_COMMIT_CHECK_WARN;
        if (!strcasecmp("error", value))
-               return CHECK_ERROR;
+               return MISSING_COMMIT_CHECK_ERROR;
        warning(_("unrecognized setting %s for option "
                  "rebase.missingCommitsCheck. Ignoring."), value);
-       return CHECK_IGNORE;
+       return MISSING_COMMIT_CHECK_IGNORE;
  }
  
  define_commit_slab(commit_seen, unsigned char);
   */
  int check_todo_list(void)
  {
-       enum check_level check_level = get_missing_commit_check_level();
+       enum missing_commit_check_level check_level = get_missing_commit_check_level();
        struct strbuf todo_file = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
        advise_to_edit_todo = res =
                parse_insn_buffer(todo_list.buf.buf, &todo_list);
  
-       if (res || check_level == CHECK_IGNORE)
+       if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
                goto leave_check;
  
        /* Mark the commits in git-rebase-todo as seen */
        if (!missing.len)
                goto leave_check;
  
-       if (check_level == CHECK_ERROR)
+       if (check_level == MISSING_COMMIT_CHECK_ERROR)
                advise_to_edit_todo = res = 1;
  
        fprintf(stderr,
@@@ -4547,17 -4475,17 +4650,17 @@@ static int rewrite_file(const char *pat
  }
  
  /* skip picking commits whose parents are unchanged */
int skip_unnecessary_picks(void)
static int skip_unnecessary_picks(struct object_id *output_oid)
  {
        const char *todo_file = rebase_path_todo();
        struct strbuf buf = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
-       struct object_id onto_oid, *oid = &onto_oid, *parent_oid;
+       struct object_id *parent_oid;
        int fd, i;
  
        if (!read_oneliner(&buf, rebase_path_onto(), 0))
                return error(_("could not read 'onto'"));
-       if (get_oid(buf.buf, &onto_oid)) {
+       if (get_oid(buf.buf, output_oid)) {
                strbuf_release(&buf);
                return error(_("need a HEAD to fixup"));
        }
                if (item->commit->parents->next)
                        break; /* merge commit */
                parent_oid = &item->commit->parents->item->object.oid;
-               if (!oideq(parent_oid, oid))
 -              if (hashcmp(parent_oid->hash, output_oid->hash))
++              if (!oideq(parent_oid, output_oid))
                        break;
-               oid = &item->commit->object.oid;
+               oidcpy(output_oid, &item->commit->object.oid);
        }
        if (i > 0) {
                int offset = get_item_line_offset(&todo_list, i);
  
                todo_list.current = i;
                if (is_fixup(peek_command(&todo_list, 0)))
-                       record_in_rewritten(oid, peek_command(&todo_list, 0));
+                       record_in_rewritten(output_oid, peek_command(&todo_list, 0));
        }
  
        todo_list_release(&todo_list);
-       printf("%s\n", oid_to_hex(oid));
  
        return 0;
  }
  
+ int complete_action(struct replay_opts *opts, unsigned flags,
+                   const char *shortrevisions, const char *onto_name,
+                   const char *onto, const char *orig_head, const char *cmd,
+                   unsigned autosquash)
+ {
+       const char *shortonto, *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct strbuf *buf = &(todo_list.buf);
+       struct object_id oid;
+       struct stat st;
+       get_oid(onto, &oid);
+       shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
+       if (!lstat(todo_file, &st) && st.st_size == 0 &&
+           write_message("noop\n", 5, todo_file, 0))
+               return -1;
+       if (autosquash && rearrange_squash())
+               return -1;
+       if (cmd && *cmd)
+               sequencer_add_exec_commands(cmd);
+       if (strbuf_read_file(buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+       if (parse_insn_buffer(buf->buf, &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+       if (count_commands(&todo_list) == 0) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               todo_list_release(&todo_list);
+               return error(_("nothing to do"));
+       }
+       strbuf_addch(buf, '\n');
+       strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+                                     "Rebase %s onto %s (%d commands)",
+                                     count_commands(&todo_list)),
+                             shortrevisions, shortonto, count_commands(&todo_list));
+       append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
+       if (write_message(buf->buf, buf->len, todo_file, 0)) {
+               todo_list_release(&todo_list);
+               return -1;
+       }
+       if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
+               return error(_("could not copy '%s' to '%s'."), todo_file,
+                            rebase_path_todo_backup());
+       if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
+               return error(_("could not transform the todo list"));
+       strbuf_reset(buf);
+       if (launch_sequence_editor(todo_file, buf, NULL)) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               todo_list_release(&todo_list);
+               return -1;
+       }
+       strbuf_stripspace(buf, 1);
+       if (buf->len == 0) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               todo_list_release(&todo_list);
+               return error(_("nothing to do"));
+       }
+       todo_list_release(&todo_list);
+       if (check_todo_list()) {
+               checkout_onto(opts, onto_name, onto, orig_head);
+               return -1;
+       }
+       if (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))
+               return error(_("could not transform the todo list"));
+       if (opts->allow_ff && skip_unnecessary_picks(&oid))
+               return error(_("could not skip unnecessary pick commands"));
+       if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
+               return -1;
+ ;
+       if (require_clean_work_tree("rebase", "", 1, 1))
+               return -1;
+       return sequencer_continue(opts);
+ }
  struct subject2item_entry {
        struct hashmap_entry entry;
        int i;
diff --combined sequencer.h
index c986bc825161f1f4702a0cd435c6d9705e3be2df,aab280f276a55bdc0af81027dc497aa34354ee52..660cff5050b39e38e721182861ada83e95e8378b
@@@ -1,13 -1,9 +1,14 @@@
  #ifndef SEQUENCER_H
  #define SEQUENCER_H
  
 +#include "cache.h"
 +#include "strbuf.h"
 +
 +struct commit;
 +
  const char *git_path_commit_editmsg(void);
  const char *git_path_seq_dir(void);
+ const char *rebase_path_todo(void);
  
  #define APPEND_SIGNOFF_DEDUP (1u << 0)
  
@@@ -62,6 -58,15 +63,15 @@@ struct replay_opts 
  };
  #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
  
+ enum missing_commit_check_level {
+       MISSING_COMMIT_CHECK_IGNORE = 0,
+       MISSING_COMMIT_CHECK_WARN,
+       MISSING_COMMIT_CHECK_ERROR
+ };
+ int write_message(const void *buf, size_t len, const char *filename,
+                 int append_eol);
  /* Call this to setup defaults before parsing command line options */
  void sequencer_init_config(struct replay_opts *opts);
  int sequencer_pick_revisions(struct replay_opts *opts);
@@@ -84,20 -89,17 +94,24 @@@ int sequencer_make_script(FILE *out, in
  
  int sequencer_add_exec_commands(const char *command);
  int transform_todos(unsigned flags);
+ enum missing_commit_check_level get_missing_commit_check_level(void);
  int check_todo_list(void);
- int skip_unnecessary_picks(void);
+ int complete_action(struct replay_opts *opts, unsigned flags,
+                   const char *shortrevisions, const char *onto_name,
+                   const char *onto, const char *orig_head, const char *cmd,
+                   unsigned autosquash);
  int rearrange_squash(void);
  
  extern const char sign_off_header[];
  
 -void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 +/*
 + * Append a signoff to the commit message in "msgbuf". The ignore_footer
 + * parameter specifies the number of bytes at the end of msgbuf that should
 + * not be considered at all. I.e., they are not checked for existing trailers,
 + * and the new signoff will be spliced into the buffer before those bytes.
 + */
 +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
 +
  void append_conflicts_hint(struct strbuf *msgbuf);
  int message_is_empty(const struct strbuf *sb,
                     enum commit_msg_cleanup_mode cleanup_mode);
@@@ -110,8 -112,14 +124,14 @@@ int update_head_with_reflog(const struc
  void commit_post_rewrite(const struct commit *current_head,
                         const struct object_id *new_head);
  
+ int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit);
  #define SUMMARY_INITIAL_COMMIT   (1 << 0)
  #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
  void print_commit_summary(const char *prefix, const struct object_id *oid,
                          unsigned int flags);
  #endif
+ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
+ int write_basic_state(struct replay_opts *opts, const char *head_name,
+                     const char *onto, const char *orig_head);
diff --combined strbuf.h
index bf18fddb5bb7883c089b30cfabc54cc5a9d055b9,66da9822fd860cab6321505c368afa78496a4520..9981f782b2573c1a4360879e03884ec753999aec
+++ b/strbuf.h
@@@ -87,7 -87,7 +87,7 @@@ struct object_id
   * Initialize the structure. The second parameter can be zero or a bigger
   * number to allocate memory, in case you want to prevent further reallocs.
   */
 -extern void strbuf_init(struct strbuf *, size_t);
 +void strbuf_init(struct strbuf *sb, size_t alloc);
  
  /**
   * Release a string buffer and the memory it used. After this call, the
@@@ -97,7 -97,7 +97,7 @@@
   * To clear a strbuf in preparation for further use without the overhead
   * of free()ing and malloc()ing again, use strbuf_reset() instead.
   */
 -extern void strbuf_release(struct strbuf *);
 +void strbuf_release(struct strbuf *sb);
  
  /**
   * Detach the string from the strbuf and returns it; you now own the
   * The strbuf that previously held the string is reset to `STRBUF_INIT` so
   * it can be reused after calling this function.
   */
 -extern char *strbuf_detach(struct strbuf *, size_t *);
 +char *strbuf_detach(struct strbuf *sb, size_t *sz);
  
  /**
   * Attach a string to a buffer. You should specify the string to attach,
   * malloc()ed, and after attaching, the pointer cannot be relied upon
   * anymore, and neither be free()d directly.
   */
 -extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
 +void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t mem);
  
  /**
   * Swap the contents of two string buffers.
@@@ -148,7 -148,7 +148,7 @@@ static inline size_t strbuf_avail(cons
   * This is never a needed operation, but can be critical for performance in
   * some cases.
   */
 -extern void strbuf_grow(struct strbuf *, size_t);
 +void strbuf_grow(struct strbuf *sb, size_t amount);
  
  /**
   * Set the length of the buffer to a given value. This function does *not*
@@@ -183,30 -183,30 +183,30 @@@ static inline void strbuf_setlen(struc
   * Strip whitespace from the beginning (`ltrim`), end (`rtrim`), or both side
   * (`trim`) of a string.
   */
 -extern void strbuf_trim(struct strbuf *);
 -extern void strbuf_rtrim(struct strbuf *);
 -extern void strbuf_ltrim(struct strbuf *);
 +void strbuf_trim(struct strbuf *sb);
 +void strbuf_rtrim(struct strbuf *sb);
 +void strbuf_ltrim(struct strbuf *sb);
  
  /* Strip trailing directory separators */
 -extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
 +void strbuf_trim_trailing_dir_sep(struct strbuf *sb);
  
  /**
   * Replace the contents of the strbuf with a reencoded form.  Returns -1
   * on error, 0 on success.
   */
 -extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 +int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
  
  /**
   * Lowercase each character in the buffer using `tolower`.
   */
 -extern void strbuf_tolower(struct strbuf *sb);
 +void strbuf_tolower(struct strbuf *sb);
  
  /**
   * Compare two buffers. Returns an integer less than, equal to, or greater
   * than zero if the first buffer is found, respectively, to be less than,
   * to match, or be greater than the second buffer.
   */
 -extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 +int strbuf_cmp(const struct strbuf *first, const struct strbuf *second);
  
  
  /**
@@@ -233,38 -233,37 +233,38 @@@ static inline void strbuf_addch(struct 
  /**
   * Add a character the specified number of times to the buffer.
   */
 -extern void strbuf_addchars(struct strbuf *sb, int c, size_t n);
 +void strbuf_addchars(struct strbuf *sb, int c, size_t n);
  
  /**
   * Insert data to the given position of the buffer. The remaining contents
   * will be shifted, not overwritten.
   */
 -extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
 +void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
  
  /**
   * Remove given amount of data from a given position of the buffer.
   */
 -extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
 +void strbuf_remove(struct strbuf *sb, size_t pos, size_t len);
  
  /**
   * Remove the bytes between `pos..pos+len` and replace it with the given
   * data.
   */
 -extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
 -                        const void *, size_t);
 +void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
 +                 const void *data, size_t data_len);
  
  /**
   * Add a NUL-terminated string to the buffer. Each line will be prepended
   * by a comment character and a blank.
   */
 -extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size);
 +void strbuf_add_commented_lines(struct strbuf *out,
 +                              const char *buf, size_t size);
  
  
  /**
   * Add data of given length to the buffer.
   */
 -extern void strbuf_add(struct strbuf *, const void *, size_t);
 +void strbuf_add(struct strbuf *sb, const void *data, size_t len);
  
  /**
   * Add a NUL-terminated string to the buffer.
@@@ -283,7 -282,7 +283,7 @@@ static inline void strbuf_addstr(struc
  /**
   * Copy the contents of another buffer at the end of the current one.
   */
 -extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
 +void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
  
  /**
   * This function can be used to expand a format string containing
   * parameters to the callback, `strbuf_expand()` passes a context pointer,
   * which can be used by the programmer of the callback as she sees fit.
   */
 -typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
 -extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
 +typedef size_t (*expand_fn_t) (struct strbuf *sb,
 +                             const char *placeholder,
 +                             void *context);
 +void strbuf_expand(struct strbuf *sb,
 +                 const char *format,
 +                 expand_fn_t fn,
 +                 void *context);
  
  /**
   * Used as callback for `strbuf_expand()`, expects an array of
@@@ -327,9 -321,7 +327,9 @@@ struct strbuf_expand_dict_entry 
        const char *placeholder;
        const char *value;
  };
 -extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
 +size_t strbuf_expand_dict_cb(struct strbuf *sb,
 +                           const char *placeholder,
 +                           void *context);
  
  /**
   * Append the contents of one strbuf to another, quoting any
   * destination. This is useful for literal data to be fed to either
   * strbuf_expand or to the *printf family of functions.
   */
 -extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
 +void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
  
  /**
   * Append the given byte size as a human-readable string (i.e. 12.23 KiB,
   * 3.50 MiB).
   */
 -extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
 +void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
  
  /**
   * Add a formatted string to the buffer.
   */
  __attribute__((format (printf,2,3)))
 -extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 +void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
  
  /**
   * Add a formatted string prepended by a comment character and a
   * blank to the buffer.
   */
  __attribute__((format (printf, 2, 3)))
 -extern void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
 +void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
  
  __attribute__((format (printf,2,0)))
 -extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
 +void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
  
  /**
   * Add the time specified by `tm`, as formatted by `strftime`.
   * `suppress_tz_name`, when set, expands %Z internally to the empty
   * string rather than passing it to `strftime`.
   */
 -extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
 -                          const struct tm *tm, int tz_offset,
 -                          int suppress_tz_name);
 +void strbuf_addftime(struct strbuf *sb, const char *fmt,
 +                  const struct tm *tm, int tz_offset,
 +                  int suppress_tz_name);
  
  /**
   * Read a given size of data from a FILE* pointer to the buffer.
   * `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline_*()`
   * family of functions have the same behaviour as well.
   */
 -extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
 +size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *file);
  
  /**
   * Read the contents of a given file descriptor. The third argument can be
   * used to give a hint about the file size, to avoid reallocs.  If read fails,
   * any partial read is undone.
   */
 -extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
 +ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint);
  
  /**
   * Read the contents of a given file descriptor partially by using only one
   * file size, to avoid reallocs. Returns the number of new bytes appended to
   * the sb.
   */
 -extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint);
 +ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint);
  
  /**
   * Read the contents of a file, specified by its path. The third argument
   * Return the number of bytes read or a negative value if some error
   * occurred while opening or reading the file.
   */
 -extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 +ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
  
  /**
   * Read the target of a symbolic link, specified by its path.  The third
   * argument can be used to give a hint about the size, to avoid reallocs.
   */
 -extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 +int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
  
  /**
   * Write the whole content of the strbuf to the stream not stopping at
   * NUL bytes.
   */
 -extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 +ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
  
  /**
   * Read a line from a FILE *, overwriting the existing contents of
  typedef int (*strbuf_getline_fn)(struct strbuf *, FILE *);
  
  /* Uses LF as the line terminator */
 -extern int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
 +int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
  
  /* Uses NUL as the line terminator */
 -extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
 +int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
  
  /*
   * Similar to strbuf_getline_lf(), but additionally treats a CR that
   * that can come from platforms whose native text format is CRLF
   * terminated.
   */
 -extern int strbuf_getline(struct strbuf *, FILE *);
 +int strbuf_getline(struct strbuf *sb, FILE *file);
  
  
  /**
   * Like `strbuf_getline`, but keeps the trailing terminator (if
   * any) in the buffer.
   */
 -extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
 +int strbuf_getwholeline(struct strbuf *sb, FILE *file, int term);
  
  /**
   * Like `strbuf_getwholeline`, but operates on a file descriptor.
   * use it unless you need the correct position in the file
   * descriptor.
   */
 -extern int strbuf_getwholeline_fd(struct strbuf *, int, int);
 +int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term);
  
  /**
   * Set the buffer to the path of the current working directory.
   */
 -extern int strbuf_getcwd(struct strbuf *sb);
 +int strbuf_getcwd(struct strbuf *sb);
  
  /**
   * Add a path to a buffer, converting a relative path to an
   * absolute one in the process.  Symbolic links are not
   * resolved.
   */
 -extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
 +void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  
  /**
   * Canonize `path` (make it absolute, resolve symlinks, remove extra
   * Callers that don't mind links should use the more lightweight
   * strbuf_add_absolute_path() instead.
   */
 -extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
 +void strbuf_add_real_path(struct strbuf *sb, const char *path);
  
  
  /**
   * normalize_path_copy() for details. If an error occurs, the contents of "sb"
   * are left untouched, and -1 is returned.
   */
 -extern int strbuf_normalize_path(struct strbuf *sb);
 +int strbuf_normalize_path(struct strbuf *sb);
  
  /**
   * Strip whitespace from a buffer. The second parameter controls if
   * comments are considered contents to be removed or not.
   */
 -extern void strbuf_stripspace(struct strbuf *buf, int skip_comments);
 +void strbuf_stripspace(struct strbuf *buf, int skip_comments);
  
  static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
  {
   * For lighter-weight alternatives, see string_list_split() and
   * string_list_split_in_place().
   */
 -extern struct strbuf **strbuf_split_buf(const char *, size_t,
 -                                      int terminator, int max);
 +struct strbuf **strbuf_split_buf(const char *str, size_t len,
 +                               int terminator, int max);
  
  static inline struct strbuf **strbuf_split_str(const char *str,
                                               int terminator, int max)
  }
  
  static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
 -                                              int terminator, int max)
 +                                             int terminator, int max)
  {
        return strbuf_split_buf(sb->buf, sb->len, terminator, max);
  }
@@@ -557,23 -549,23 +557,23 @@@ static inline struct strbuf **strbuf_sp
   *   'element1, element2, ..., elementN'
   * to str.  If only one element, just write "element1" to str.
   */
 -extern void strbuf_add_separated_string_list(struct strbuf *str,
 -                                           const char *sep,
 -                                           struct string_list *slist);
 +void strbuf_add_separated_string_list(struct strbuf *str,
 +                                    const char *sep,
 +                                    struct string_list *slist);
  
  /**
   * Free a NULL-terminated list of strbufs (for example, the return
   * values of the strbuf_split*() functions).
   */
 -extern void strbuf_list_free(struct strbuf **);
 +void strbuf_list_free(struct strbuf **list);
  
  /**
   * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
   * the strbuf `sb`.
   */
 -extern void strbuf_add_unique_abbrev(struct strbuf *sb,
 -                                   const struct object_id *oid,
 -                                   int abbrev_len);
 +void strbuf_add_unique_abbrev(struct strbuf *sb,
 +                            const struct object_id *oid,
 +                            int abbrev_len);
  
  /**
   * Launch the user preferred editor to edit a file and fill the buffer
   * run in. If the buffer is NULL the editor is launched as usual but the
   * file's contents are not read into the buffer upon completion.
   */
- int launch_editor(const char *path,
-                 struct strbuf *buffer,
 -extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
 -extern int launch_sequence_editor(const char *path, struct strbuf *buffer,
 -                                const char *const *env);
++int launch_editor(const char *path, struct strbuf *buffer,
 +                const char *const *env);
 +
++int launch_sequence_editor(const char *path, struct strbuf *buffer,
++                         const char *const *env);
 -extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
 +void strbuf_add_lines(struct strbuf *sb,
 +                    const char *prefix,
 +                    const char *buf,
 +                    size_t size);
  
  /**
   * Append s to sb, with the characters '<', '>', '&' and '"' converted
   * into XML entities.
   */
 -extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);
 +void strbuf_addstr_xml_quoted(struct strbuf *sb,
 +                            const char *s);
  
  /**
   * "Complete" the contents of `sb` by ensuring that either it ends with the
@@@ -626,8 -614,8 +628,8 @@@ static inline void strbuf_complete_line
   * If "allowed" is non-zero, restrict the set of allowed expansions. See
   * interpret_branch_name() for details.
   */
 -extern void strbuf_branchname(struct strbuf *sb, const char *name,
 -                            unsigned allowed);
 +void strbuf_branchname(struct strbuf *sb, const char *name,
 +                     unsigned allowed);
  
  /*
   * Like strbuf_branchname() above, but confirm that the result is
   *
   * The return value is "0" if the result is valid, and "-1" otherwise.
   */
 -extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 +int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
  
 -extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
 -                                  int reserved);
 +void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
 +                           int reserved);
  
  __attribute__((format (printf,1,2)))
 -extern int printf_ln(const char *fmt, ...);
 +int printf_ln(const char *fmt, ...);
  __attribute__((format (printf,2,3)))
 -extern int fprintf_ln(FILE *fp, const char *fmt, ...);
 +int fprintf_ln(FILE *fp, const char *fmt, ...);
  
  char *xstrdup_tolower(const char *);
  char *xstrdup_toupper(const char *);
index ff89b6341a6fc19801959058e35039778bc6577b,90c3ca70e6b9f6b4d8bfaa28db90c8ec19c4cf85..d60e59ecdb87d1a9722340acd09af82e29cb9dca
@@@ -75,6 -75,16 +75,16 @@@ test_expect_success 'rebase --keep-empt
        test_line_count = 6 actual
  '
  
+ cat > expect <<EOF
+ error: nothing to do
+ EOF
+ test_expect_success 'rebase -i with empty HEAD' '
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="1 exec_true" git rebase -i HEAD^ >actual 2>&1 &&
+       test_i18ncmp expect actual
+ '
  test_expect_success 'rebase -i with the exec command' '
        git checkout master &&
        (
@@@ -119,15 -129,6 +129,15 @@@ test_expect_success 'rebase -i with exe
        )
  '
  
 +test_expect_success 'rebase -i sets work tree properly' '
 +      test_when_finished "rm -rf subdir" &&
 +      test_when_finished "test_might_fail git rebase --abort" &&
 +      mkdir subdir &&
 +      git rebase -x "(cd subdir && git rev-parse --show-toplevel)" HEAD^ \
 +              >actual &&
 +      ! grep "/subdir$" actual
 +'
 +
  test_expect_success 'rebase -i with the exec command checks tree cleanness' '
        git checkout master &&
        set_fake_editor &&
@@@ -273,18 -274,11 +283,18 @@@ test_expect_success 'retain authorship
  '
  
  test_expect_success 'retain authorship w/ conflicts' '
 +      oGIT_AUTHOR_NAME=$GIT_AUTHOR_NAME &&
 +      test_when_finished "GIT_AUTHOR_NAME=\$oGIT_AUTHOR_NAME" &&
 +
        git reset --hard twerp &&
        test_commit a conflict a conflict-a &&
        git reset --hard twerp &&
 -      GIT_AUTHOR_NAME=AttributeMe \
 +
 +      GIT_AUTHOR_NAME=AttributeMe &&
 +      export GIT_AUTHOR_NAME &&
        test_commit b conflict b conflict-b &&
 +      GIT_AUTHOR_NAME=$oGIT_AUTHOR_NAME &&
 +
        set_fake_editor &&
        test_must_fail git rebase -i conflict-a &&
        echo resolved >conflict &&
@@@ -525,7 -519,7 +535,7 @@@ test_expect_success 'interrupted squas
        one=$(git rev-parse HEAD~3) &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
 -      (echo one; echo two; echo four) > conflict &&
 +      test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
        echo resolved > conflict &&
@@@ -539,10 -533,10 +549,10 @@@ test_expect_success 'interrupted squas
        one=$(git rev-parse HEAD~3) &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
 -      (echo one; echo four) > conflict &&
 +      test_write_lines one four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
 -      (echo one; echo two; echo four) > conflict &&
 +      test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
        echo resolved > conflict &&
@@@ -795,15 -789,16 +805,15 @@@ test_expect_success 'always cherry-pic
        git tag original-no-ff-branch &&
        set_fake_editor &&
        git rebase -i --no-ff A &&
 -      touch empty &&
        for p in 0 1 2
        do
                test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
                git diff HEAD~$p original-no-ff-branch~$p > out &&
 -              test_cmp empty out
 +              test_must_be_empty out
        done &&
        test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&
        git diff HEAD~3 original-no-ff-branch~3 > out &&
 -      test_cmp empty out
 +      test_must_be_empty out
  '
  
  test_expect_success 'set up commits with funny messages' '
@@@ -1245,7 -1240,7 +1255,7 @@@ rebase_setup_and_clean () 
                test_might_fail git branch -D $1 &&
                test_might_fail git rebase --abort
        " &&
 -      git checkout -b $1 master
 +      git checkout -b $1 ${2:-master}
  }
  
  test_expect_success 'drop' '
@@@ -1422,24 -1417,4 +1432,24 @@@ test_expect_success 'rebase -i --gpg-si
        test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
  '
  
 +test_expect_success 'valid author header after --root swap' '
 +      rebase_setup_and_clean author-header no-conflict-branch &&
 +      set_fake_editor &&
 +      git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit &&
 +      git cat-file commit HEAD | grep ^author >expected &&
 +      FAKE_LINES="5 1" git rebase -i --root &&
 +      git cat-file commit HEAD^ | grep ^author >actual &&
 +      test_cmp expected actual
 +'
 +
 +test_expect_success 'valid author header when author contains single quote' '
 +      rebase_setup_and_clean author-header no-conflict-branch &&
 +      set_fake_editor &&
 +      git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit &&
 +      git cat-file commit HEAD | grep ^author >expected &&
 +      FAKE_LINES="2" git rebase -i HEAD~2 &&
 +      git cat-file commit HEAD | grep ^author >actual &&
 +      test_cmp expected actual
 +'
 +
  test_done