Merge branch 'rr/revert-cherry-pick-continue'
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:19 +0000 (12:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:19 +0000 (12:36 -0700)
* rr/revert-cherry-pick-continue:
builtin/revert.c: make commit_list_append() static
revert: Propagate errors upwards from do_pick_commit
revert: Introduce --continue to continue the operation
revert: Don't implicitly stomp pending sequencer operation
revert: Remove sequencer state when no commits are pending
reset: Make reset remove the sequencer state
revert: Introduce --reset to remove sequencer state
revert: Make pick_commits functionally act on a commit list
revert: Save command-line options for continuing operation
revert: Save data for continuing after conflict resolution
revert: Don't create invalid replay_opts in parse_args
revert: Separate cmdline parsing from functional code
revert: Introduce struct to keep command-line options
revert: Eliminate global "commit" variable
revert: Rename no_replay to record_origin
revert: Don't check lone argument in get_encoding
revert: Simplify and inline add_message_to_msg
config: Introduce functions to write non-standard file
advice: Introduce error_resolve_conflict

1  2 
Documentation/git-cherry-pick.txt
Documentation/git-revert.txt
Makefile
branch.c
builtin/revert.c
cache.h
config.c
index 7cfa3d92ac8dc7a90068311c0047c667f98515a8,663186bda747ce02af4b5ca1732254a0e2425369..2660a842fc2ac76660963bc65c95ca47cb0e97cb
@@@ -7,8 -7,9 +7,10 @@@ git-cherry-pick - Apply the changes int
  
  SYNOPSIS
  --------
 +[verse]
  'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
+ 'git cherry-pick' --reset
+ 'git cherry-pick' --continue
  
  DESCRIPTION
  -----------
@@@ -110,33 -111,37 +112,37 @@@ effect to your index in a row
        Pass the merge strategy-specific option through to the
        merge strategy.  See linkgit:git-merge[1] for details.
  
+ SEQUENCER SUBCOMMANDS
+ ---------------------
+ include::sequencer.txt[]
  EXAMPLES
  --------
 -git cherry-pick master::
 +`git cherry-pick master`::
  
        Apply the change introduced by the commit at the tip of the
        master branch and create a new commit with this change.
  
 -git cherry-pick ..master::
 -git cherry-pick ^HEAD master::
 +`git cherry-pick ..master`::
 +`git cherry-pick ^HEAD master`::
  
        Apply the changes introduced by all commits that are ancestors
        of master but not of HEAD to produce new commits.
  
 -git cherry-pick master{tilde}4 master{tilde}2::
 +`git cherry-pick master{tilde}4 master{tilde}2`::
  
        Apply the changes introduced by the fifth and third last
        commits pointed to by master and create 2 new commits with
        these changes.
  
 -git cherry-pick -n master~1 next::
 +`git cherry-pick -n master~1 next`::
  
        Apply to the working tree and the index the changes introduced
        by the second last commit pointed to by master and by the last
        commit pointed to by next, but do not create any commit with
        these changes.
  
 -git cherry-pick --ff ..next::
 +`git cherry-pick --ff ..next`::
  
        If history is linear and HEAD is an ancestor of next, update
        the working tree and advance the HEAD pointer to match next.
        are in next but not HEAD to the current branch, creating a new
        commit for each new change.
  
 -git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
 +`git rev-list --reverse master \-- README | git cherry-pick -n --stdin`::
  
        Apply the changes introduced by all commits on the master
        branch that touched README to the working tree and index,
index b311d59c7c06dd696e3c667c97a5982137bdd87a,9be2fe2b2fe8a00941490e476f672280ae6eccd5..f3519413e7e8704deee0197df6876eaed97e28b0
@@@ -7,8 -7,9 +7,10 @@@ git-revert - Revert some existing commi
  
  SYNOPSIS
  --------
 +[verse]
  'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
+ 'git revert' --reset
+ 'git revert' --continue
  
  DESCRIPTION
  -----------
@@@ -24,7 -25,7 +26,7 @@@ throw away all uncommitted changes in y
  should see linkgit:git-reset[1], particularly the '--hard' option.  If
  you want to extract specific files as they were in another commit, you
  should see linkgit:git-checkout[1], specifically the `git checkout
 -<commit> -- <filename>` syntax.  Take care with these alternatives as
 +<commit> \-- <filename>` syntax.  Take care with these alternatives as
  both will discard uncommitted changes in your working directory.
  
  OPTIONS
@@@ -91,14 -92,18 +93,18 @@@ effect to your index in a row
        Pass the merge strategy-specific option through to the
        merge strategy.  See linkgit:git-merge[1] for details.
  
+ SEQUENCER SUBCOMMANDS
+ ---------------------
+ include::sequencer.txt[]
  EXAMPLES
  --------
 -git revert HEAD~3::
 +`git revert HEAD~3`::
  
        Revert the changes specified by the fourth last commit in HEAD
        and create a new commit with the reverted changes.
  
 -git revert -n master{tilde}5..master{tilde}2::
 +`git revert -n master{tilde}5..master{tilde}2`::
  
        Revert the changes done by commits from the fifth last commit
        in master (included) to the third last commit in master
diff --combined Makefile
index d736b5e196e7739cabd5b79aafa5cabf92d203ae,f69eef254cbd28c7450d1617a2b3b5286fe39565..68201dea7838bd12b8be7432f9fe4366033bda1a
+++ b/Makefile
@@@ -30,15 -30,15 +30,15 @@@ all:
  # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
 -# Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 +# Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
  # git-http-push are not built, and you cannot use http:// and https://
 -# transports.
 +# transports (neither smart nor dumb).
  #
  # Define CURLDIR=/foo/bar if your curl header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
  # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 -# not built, and you cannot push using http:// and https:// transports.
 +# not built, and you cannot push using http:// and https:// transports (dumb).
  #
  # Define EXPATDIR=/foo/bar if your expat header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
  # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
  #
 +# Define NEEDS_SSL_WITH_CURL if you need -lssl with -lcurl (Minix).
 +#
 +# Define NEEDS_IDN_WITH_CURL if you need -lidn when using -lcurl (Minix).
 +#
  # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
  #
  # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
  # that tells runtime paths to dynamic libraries;
  # "-Wl,-rpath=/path/lib" is used instead.
  #
 +# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
 +# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 +#
  # Define USE_NSEC below if you want git to care about sub-second file mtimes
  # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
  # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
  #   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
  #   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
  #
 -# Define COMPUTE_HEADER_DEPENDENCIES if your compiler supports the -MMD option
 -# and you want to avoid rebuilding objects when an unrelated header file
 -# changes.
 -#
  # Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded
  # dependency rules.
  #
@@@ -298,7 -295,6 +298,7 @@@ bindir = $(prefix)/$(bindir_relative
  mandir = share/man
  infodir = share/info
  gitexecdir = libexec/git-core
 +mergetoolsdir = $(gitexecdir)/mergetools
  sharedir = $(prefix)/share
  gitwebdir = $(sharedir)/gitweb
  template_dir = share/git-core/templates
@@@ -511,7 -507,6 +511,7 @@@ LIB_H += commit.
  LIB_H += compat/bswap.h
  LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
 +LIB_H += compat/obstack.h
  LIB_H += compat/win32/pthread.h
  LIB_H += compat/win32/syslog.h
  LIB_H += compat/win32/sys/poll.h
@@@ -530,7 -525,6 +530,7 @@@ LIB_H += graph.
  LIB_H += grep.h
  LIB_H += hash.h
  LIB_H += help.h
 +LIB_H += kwset.h
  LIB_H += levenshtein.h
  LIB_H += list-objects.h
  LIB_H += ll-merge.h
@@@ -557,12 -551,12 +557,13 @@@ LIB_H += rerere.
  LIB_H += resolve-undo.h
  LIB_H += revision.h
  LIB_H += run-command.h
+ LIB_H += sequencer.h
  LIB_H += sha1-array.h
  LIB_H += sha1-lookup.h
  LIB_H += sideband.h
  LIB_H += sigchain.h
  LIB_H += strbuf.h
 +LIB_H += streaming.h
  LIB_H += string-list.h
  LIB_H += submodule.h
  LIB_H += tag.h
@@@ -592,7 -586,6 +593,7 @@@ LIB_OBJS += cache-tree.
  LIB_OBJS += color.o
  LIB_OBJS += combine-diff.o
  LIB_OBJS += commit.o
 +LIB_OBJS += compat/obstack.o
  LIB_OBJS += config.o
  LIB_OBJS += connect.o
  LIB_OBJS += convert.o
@@@ -622,7 -615,6 +623,7 @@@ LIB_OBJS += hash.
  LIB_OBJS += help.o
  LIB_OBJS += hex.o
  LIB_OBJS += ident.o
 +LIB_OBJS += kwset.o
  LIB_OBJS += levenshtein.o
  LIB_OBJS += list-objects.o
  LIB_OBJS += ll-merge.o
@@@ -643,7 -635,6 +644,7 @@@ LIB_OBJS += pack-revindex.
  LIB_OBJS += pack-write.o
  LIB_OBJS += pager.o
  LIB_OBJS += parse-options.o
 +LIB_OBJS += parse-options-cb.o
  LIB_OBJS += patch-delta.o
  LIB_OBJS += patch-ids.o
  LIB_OBJS += path.o
@@@ -664,6 -655,7 +665,7 @@@ LIB_OBJS += revision.
  LIB_OBJS += run-command.o
  LIB_OBJS += server-info.o
  LIB_OBJS += setup.o
+ LIB_OBJS += sequencer.o
  LIB_OBJS += sha1-array.o
  LIB_OBJS += sha1-lookup.o
  LIB_OBJS += sha1_file.o
@@@ -672,7 -664,6 +674,7 @@@ LIB_OBJS += shallow.
  LIB_OBJS += sideband.o
  LIB_OBJS += sigchain.o
  LIB_OBJS += strbuf.o
 +LIB_OBJS += streaming.o
  LIB_OBJS += string-list.o
  LIB_OBJS += submodule.o
  LIB_OBJS += symlinks.o
@@@ -1135,6 -1126,8 +1137,6 @@@ endi
        X = .exe
  endif
  ifeq ($(uname_S),Interix)
 -      NO_SYS_POLL_H = YesPlease
 -      NO_INTTYPES_H = YesPlease
        NO_INITGROUPS = YesPlease
        NO_IPV6 = YesPlease
        NO_MEMMEM = YesPlease
        ifeq ($(uname_R),3.5)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
 +              NO_SOCKADDR_STORAGE = YesPlease
 +              NO_FNMATCH_CASEFOLD = YesPlease
        endif
        ifeq ($(uname_R),5.2)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
 +              NO_SOCKADDR_STORAGE = YesPlease
 +              NO_FNMATCH_CASEFOLD = YesPlease
        endif
  endif
 +ifeq ($(uname_S),Minix)
 +      NO_IPV6 = YesPlease
 +      NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 +      NO_NSEC = YesPlease
 +      NEEDS_LIBGEN =
 +      NEEDS_CRYPTO_WITH_SSL = YesPlease
 +      NEEDS_IDN_WITH_CURL = YesPlease
 +      NEEDS_SSL_WITH_CURL = YesPlease
 +      NEEDS_RESOLV =
 +      NO_HSTRERROR = YesPlease
 +      NO_MMAP = YesPlease
 +      NO_CURL =
 +      NO_EXPAT =
 +endif
  ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
@@@ -1238,15 -1213,6 +1240,15 @@@ endi
  ifdef CHECK_HEADER_DEPENDENCIES
  COMPUTE_HEADER_DEPENDENCIES =
  USE_COMPUTED_HEADER_DEPENDENCIES =
 +else
 +ifndef COMPUTE_HEADER_DEPENDENCIES
 +dep_check = $(shell $(CC) $(ALL_CFLAGS) \
 +      -c -MF /dev/null -MMD -MP -x c /dev/null -o /dev/null 2>&1; \
 +      echo $$?)
 +ifeq ($(dep_check),0)
 +COMPUTE_HEADER_DEPENDENCIES=YesPlease
 +endif
 +endif
  endif
  
  ifdef COMPUTE_HEADER_DEPENDENCIES
        else
                CURL_LIBCURL = -lcurl
        endif
 +      ifdef NEEDS_SSL_WITH_CURL
 +              CURL_LIBCURL += -lssl
 +              ifdef NEEDS_CRYPTO_WITH_SSL
 +                      CURL_LIBCURL += -lcrypto
 +              endif
 +      endif
 +      ifdef NEEDS_IDN_WITH_CURL
 +              CURL_LIBCURL += -lidn
 +      endif
 +
        REMOTE_CURL_PRIMARY = git-remote-http$X
        REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
        REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
@@@ -1368,7 -1324,7 +1370,7 @@@ ifndef NO_OPENSS
                OPENSSL_LINK =
        endif
        ifdef NEEDS_CRYPTO_WITH_SSL
 -              OPENSSL_LINK += -lcrypto
 +              OPENSSL_LIBSSL += -lcrypto
        endif
  else
        BASIC_CFLAGS += -DNO_OPENSSL
@@@ -1420,9 -1376,6 +1422,9 @@@ endi
  ifdef USE_ST_TIMESPEC
        BASIC_CFLAGS += -DUSE_ST_TIMESPEC
  endif
 +ifdef NO_NORETURN
 +      BASIC_CFLAGS += -DNO_NORETURN
 +endif
  ifdef NO_NSEC
        BASIC_CFLAGS += -DNO_NSEC
  endif
@@@ -1755,7 -1708,7 +1757,7 @@@ git.sp git.s git.o: EXTRA_CPPFLAGS = -D
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
  
 -git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
 +git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
@@@ -1887,7 -1840,7 +1889,7 @@@ ifndef NO_CUR
        GIT_OBJS += http.o http-walker.o remote-curl.o
  endif
  XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
 -      xdiff/xmerge.o xdiff/xpatience.o
 +      xdiff/xmerge.o xdiff/xpatience.o xdiff/xhistogram.o
  VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
        vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
  VCSSVN_TEST_OBJS = test-obj-pool.o test-string-pool.o \
@@@ -2053,17 -2006,17 +2055,17 @@@ compat/nedmalloc/nedmalloc.sp compat/ne
        -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
  endif
  
 -git-%$X: %.o $(GITLIBS)
 +git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -git-imap-send$X: imap-send.o $(GITLIBS)
 +git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
  
 -git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
 +git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL)
 -git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
 +git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
@@@ -2073,7 -2026,7 +2075,7 @@@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_P
        ln -s $< $@ 2>/dev/null || \
        cp $< $@
  
 -$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)
 +$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
@@@ -2143,15 -2096,6 +2145,15 @@@ GIT-CFLAGS: FORC
                echo "$$FLAGS" >GIT-CFLAGS; \
              fi
  
 +TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS))
 +
 +GIT-LDFLAGS: FORCE
 +      @FLAGS='$(TRACK_LDFLAGS)'; \
 +          if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \
 +              echo 1>&2 "    * new link flags"; \
 +              echo "$$FLAGS" >GIT-LDFLAGS; \
 +            fi
 +
  # We need to apply sq twice, once to protect from the shell
  # that runs GIT-BUILD-OPTIONS, and then again to protect it
  # and the first level quoting from the shell that runs "echo".
@@@ -2215,7 -2159,7 +2217,7 @@@ test-delta$X: diff-delta.o patch-delta.
  
  test-line-buffer$X: vcs-svn/lib.a
  
 -test-parse-options$X: parse-options.o
 +test-parse-options$X: parse-options.o parse-options-cb.o
  
  test-string-pool$X: vcs-svn/lib.a
  
@@@ -2223,7 -2167,7 +2225,7 @@@ test-svn-fe$X: vcs-svn/lib.
  
  .PRECIOUS: $(TEST_OBJS)
  
 -test-%$X: test-%.o $(GITLIBS)
 +test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
  
  check-sha1:: test-sha1$X
@@@ -2268,13 -2212,6 +2270,13 @@@ endi
  gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
  export gitexec_instdir
  
 +ifneq ($(filter /%,$(firstword $(mergetoolsdir))),)
 +mergetools_instdir = $(mergetoolsdir)
 +else
 +mergetools_instdir = $(prefix)/$(mergetoolsdir)
 +endif
 +mergetools_instdir_SQ = $(subst ','\'',$(mergetools_instdir))
 +
  install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
  
  install: all
        $(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
 +      (cd mergetools && $(TAR) cf - .) | \
 +      (cd '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' && umask 022 && $(TAR) xof -)
  ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C gitweb install
@@@ -2443,7 -2377,7 +2445,7 @@@ ifndef NO_TCLT
        $(MAKE) -C gitk-git clean
        $(MAKE) -C git-gui clean
  endif
 -      $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
 +      $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
  
  .PHONY: all install clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
@@@ -2554,19 -2488,3 +2556,19 @@@ cover_db: coverage-repor
  
  cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
 +
 +### profile feedback build
 +#
 +.PHONY: profile-all profile-clean
 +
 +PROFILE_GEN_CFLAGS := $(CFLAGS) -fprofile-generate -DNO_NORETURN=1
 +PROFILE_USE_CFLAGS := $(CFLAGS) -fprofile-use -fprofile-correction -DNO_NORETURN=1
 +
 +profile-clean:
 +      $(RM) $(addsuffix *.gcda,$(object_dirs))
 +      $(RM) $(addsuffix *.gcno,$(object_dirs))
 +
 +profile-all: profile-clean
 +      $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" all
 +      $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" -j1 test
 +      $(MAKE) CFLAGS="$(PROFILE_USE_CFLAGS)" all
diff --combined branch.c
index fecedd3b4635c79adcbe0e26613af079beff64d0,d06aec4637ca26a473d7cb45ecc97114db656db1..d8098762f62a9dfb991f64702d34047182cfa951
+++ b/branch.c
@@@ -3,6 -3,7 +3,7 @@@
  #include "refs.h"
  #include "remote.h"
  #include "commit.h"
+ #include "sequencer.h"
  
  struct tracking {
        struct refspec spec;
@@@ -135,28 -136,6 +136,28 @@@ static int setup_tracking(const char *n
        return 0;
  }
  
 +int validate_new_branchname(const char *name, struct strbuf *ref,
 +                          int force, int attr_only)
 +{
 +      if (strbuf_check_branch_ref(ref, name))
 +              die("'%s' is not a valid branch name.", name);
 +
 +      if (!ref_exists(ref->buf))
 +              return 0;
 +      else if (!force && !attr_only)
 +              die("A branch named '%s' already exists.", ref->buf + strlen("refs/heads/"));
 +
 +      if (!attr_only) {
 +              const char *head;
 +              unsigned char sha1[20];
 +
 +              head = resolve_ref("HEAD", sha1, 0, NULL);
 +              if (!is_bare_repository() && head && !strcmp(head, ref->buf))
 +                      die("Cannot force update the current branch.");
 +      }
 +      return 1;
 +}
 +
  void create_branch(const char *head,
                   const char *name, const char *start_name,
                   int force, int reflog, enum branch_track track)
        if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE)
                explicit_tracking = 1;
  
 -      if (strbuf_check_branch_ref(&ref, name))
 -              die("'%s' is not a valid branch name.", name);
 -
 -      if (resolve_ref(ref.buf, sha1, 1, NULL)) {
 -              if (!force && track == BRANCH_TRACK_OVERRIDE)
 +      if (validate_new_branchname(name, &ref, force,
 +                                  track == BRANCH_TRACK_OVERRIDE)) {
 +              if (!force)
                        dont_change_ref = 1;
 -              else if (!force)
 -                      die("A branch named '%s' already exists.", name);
 -              else if (!is_bare_repository() && head && !strcmp(head, name))
 -                      die("Cannot force update the current branch.");
 -              forcing = 1;
 +              else
 +                      forcing = 1;
        }
  
        real_ref = NULL;
                         start_name);
  
        if (real_ref && track)
 -              setup_tracking(name, real_ref, track);
 +              setup_tracking(ref.buf+11, real_ref, track);
  
        if (!dont_change_ref)
                if (write_ref_sha1(lock, sha1, msg) < 0)
@@@ -245,4 -229,5 +246,5 @@@ void remove_branch_state(void
        unlink(git_path("MERGE_MSG"));
        unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
+       remove_sequencer_state(0);
  }
diff --combined builtin/revert.c
index 3117776c2c030bec03563f043c4dc8bb34eb17cd,8409f4c886aeb23b4d0d5d000661eb3e2c278558..ba27cf15ee5478981a0a9a53493f41b67fcf9440
@@@ -13,6 -13,8 +13,8 @@@
  #include "rerere.h"
  #include "merge-recursive.h"
  #include "refs.h"
+ #include "dir.h"
+ #include "sequencer.h"
  
  /*
   * This implements the builtins revert and cherry-pick.
  
  static const char * const revert_usage[] = {
        "git revert [options] <commit-ish>",
+       "git revert <subcommand>",
        NULL
  };
  
  static const char * const cherry_pick_usage[] = {
        "git cherry-pick [options] <commit-ish>",
+       "git cherry-pick <subcommand>",
        NULL
  };
  
- static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
- static enum { REVERT, CHERRY_PICK } action;
- static struct commit *commit;
- static int commit_argc;
- static const char **commit_argv;
- static int allow_rerere_auto;
- static const char *me;
- /* Merge strategy. */
- static const char *strategy;
- static const char **xopts;
- static size_t xopts_nr, xopts_alloc;
+ enum replay_action { REVERT, CHERRY_PICK };
+ enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
+ struct replay_opts {
+       enum replay_action action;
+       enum replay_subcommand subcommand;
+       /* Boolean options */
+       int edit;
+       int record_origin;
+       int no_commit;
+       int signoff;
+       int allow_ff;
+       int allow_rerere_auto;
+       int mainline;
+       int commit_argc;
+       const char **commit_argv;
+       /* Merge strategy */
+       const char *strategy;
+       const char **xopts;
+       size_t xopts_nr, xopts_alloc;
+ };
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
+ static const char *action_name(const struct replay_opts *opts)
+ {
+       return opts->action == REVERT ? "revert" : "cherry-pick";
+ }
  static char *get_encoding(const char *message);
  
- static const char * const *revert_or_cherry_pick_usage(void)
+ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
  {
-       return action == REVERT ? revert_usage : cherry_pick_usage;
+       return opts->action == REVERT ? revert_usage : cherry_pick_usage;
  }
  
  static int option_parse_x(const struct option *opt,
                          const char *arg, int unset)
  {
+       struct replay_opts **opts_ptr = opt->value;
+       struct replay_opts *opts = *opts_ptr;
        if (unset)
                return 0;
  
-       ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
-       xopts[xopts_nr++] = xstrdup(arg);
+       ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+       opts->xopts[opts->xopts_nr++] = xstrdup(arg);
        return 0;
  }
  
- static void parse_args(int argc, const char **argv)
+ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
+ {
+       const char *this_opt;
+       va_list ap;
+       va_start(ap, base_opt);
+       while ((this_opt = va_arg(ap, const char *))) {
+               if (va_arg(ap, int))
+                       break;
+       }
+       va_end(ap);
+       if (this_opt)
+               die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
+ }
+ static void verify_opt_mutually_compatible(const char *me, ...)
+ {
+       const char *opt1, *opt2;
+       va_list ap;
+       va_start(ap, me);
+       while ((opt1 = va_arg(ap, const char *))) {
+               if (va_arg(ap, int))
+                       break;
+       }
+       if (opt1) {
+               while ((opt2 = va_arg(ap, const char *))) {
+                       if (va_arg(ap, int))
+                               break;
+               }
+       }
+       if (opt1 && opt2)
+               die(_("%s: %s cannot be used with %s"), me, opt1, opt2);
+ }
+ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
  {
-       const char * const * usage_str = revert_or_cherry_pick_usage();
+       const char * const * usage_str = revert_or_cherry_pick_usage(opts);
+       const char *me = action_name(opts);
        int noop;
+       int reset = 0;
+       int contin = 0;
        struct option options[] = {
-               OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
-               OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
+               OPT_BOOLEAN(0, "reset", &reset, "forget the current operation"),
+               OPT_BOOLEAN(0, "continue", &contin, "continue the current operation"),
+               OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
+               OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
                { OPTION_BOOLEAN, 'r', NULL, &noop, NULL, "no-op (backward compatibility)",
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 0 },
-               OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-               OPT_INTEGER('m', "mainline", &mainline, "parent number"),
-               OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
-               OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
-               OPT_CALLBACK('X', "strategy-option", &xopts, "option",
+               OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
+               OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
+               OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
+               OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
+               OPT_CALLBACK('X', "strategy-option", &opts, "option",
                        "option for merge strategy", option_parse_x),
                OPT_END(),
                OPT_END(),
                OPT_END(),
        };
  
-       if (action == CHERRY_PICK) {
+       if (opts->action == CHERRY_PICK) {
                struct option cp_extra[] = {
-                       OPT_BOOLEAN('x', NULL, &no_replay, "append commit name"),
-                       OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
+                       OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
+                       OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
                        OPT_END(),
                };
                if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
                        die(_("program error"));
        }
  
-       commit_argc = parse_options(argc, argv, NULL, options, usage_str,
-                                   PARSE_OPT_KEEP_ARGV0 |
-                                   PARSE_OPT_KEEP_UNKNOWN);
-       if (commit_argc < 2)
+       opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
+                                       PARSE_OPT_KEEP_ARGV0 |
+                                       PARSE_OPT_KEEP_UNKNOWN);
+       /* Check for incompatible subcommands */
+       verify_opt_mutually_compatible(me,
+                               "--reset", reset,
+                               "--continue", contin,
+                               NULL);
+       /* Set the subcommand */
+       if (reset)
+               opts->subcommand = REPLAY_RESET;
+       else if (contin)
+               opts->subcommand = REPLAY_CONTINUE;
+       else
+               opts->subcommand = REPLAY_NONE;
+       /* Check for incompatible command line arguments */
+       if (opts->subcommand != REPLAY_NONE) {
+               char *this_operation;
+               if (opts->subcommand == REPLAY_RESET)
+                       this_operation = "--reset";
+               else
+                       this_operation = "--continue";
+               verify_opt_compatible(me, this_operation,
+                               "--no-commit", opts->no_commit,
+                               "--signoff", opts->signoff,
+                               "--mainline", opts->mainline,
+                               "--strategy", opts->strategy ? 1 : 0,
+                               "--strategy-option", opts->xopts ? 1 : 0,
+                               "-x", opts->record_origin,
+                               "--ff", opts->allow_ff,
+                               NULL);
+       }
+       else if (opts->commit_argc < 2)
                usage_with_options(usage_str, options);
  
-       commit_argv = argv;
+       if (opts->allow_ff)
+               verify_opt_compatible(me, "--ff",
+                               "--signoff", opts->signoff,
+                               "--no-commit", opts->no_commit,
+                               "-x", opts->record_origin,
+                               "--edit", opts->edit,
+                               NULL);
+       opts->commit_argv = argv;
  }
  
  struct commit_message {
        const char *message;
  };
  
- static int get_message(const char *raw_message, struct commit_message *out)
+ static int get_message(struct commit *commit, struct commit_message *out)
  {
        const char *encoding;
        const char *abbrev, *subject;
        int abbrev_len, subject_len;
        char *q;
  
-       if (!raw_message)
+       if (!commit->buffer)
                return -1;
-       encoding = get_encoding(raw_message);
+       encoding = get_encoding(commit->buffer);
        if (!encoding)
                encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
  
        out->reencoded_message = NULL;
-       out->message = raw_message;
+       out->message = commit->buffer;
        if (strcmp(encoding, git_commit_encoding))
-               out->reencoded_message = reencode_string(raw_message,
+               out->reencoded_message = reencode_string(commit->buffer,
                                        git_commit_encoding, encoding);
        if (out->reencoded_message)
                out->message = out->reencoded_message;
@@@ -167,9 -273,6 +273,6 @@@ static char *get_encoding(const char *m
  {
        const char *p = message, *eol;
  
-       if (!p)
-               die (_("Could not read commit message of %s"),
-                               sha1_to_hex(commit->object.sha1));
        while (*p && *p != '\n') {
                for (eol = p + 1; *eol && *eol != '\n'; eol++)
                        ; /* do nothing */
        return NULL;
  }
  
- static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
- {
-       const char *p = message;
-       while (*p && (*p != '\n' || p[1] != '\n'))
-               p++;
-       if (!*p)
-               strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
-       p += 2;
-       strbuf_addstr(msgbuf, p);
- }
- static void write_cherry_pick_head(void)
+ static void write_cherry_pick_head(struct commit *commit)
  {
        int fd;
        struct strbuf buf = STRBUF_INIT;
        strbuf_release(&buf);
  }
  
- static void advise(const char *advice, ...)
- {
-       va_list params;
-       va_start(params, advice);
-       vreportf("hint: ", advice, params);
-       va_end(params);
- }
  static void print_advice(void)
  {
        char *msg = getenv("GIT_CHERRY_PICK_HELP");
@@@ -258,28 -339,28 +339,23 @@@ static void write_message(struct strbu
  
  static struct tree *empty_tree(void)
  {
 -      struct tree *tree = xcalloc(1, sizeof(struct tree));
 -
 -      tree->object.parsed = 1;
 -      tree->object.type = OBJ_TREE;
 -      pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
 -      return tree;
 +      return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
  }
  
- static NORETURN void die_dirty_index(const char *me)
+ static int error_dirty_index(struct replay_opts *opts)
  {
-       if (read_cache_unmerged()) {
-               die_resolve_conflict(me);
-       } else {
-               if (advice_commit_before_merge) {
-                       if (action == REVERT)
-                               die(_("Your local changes would be overwritten by revert.\n"
-                                         "Please, commit your changes or stash them to proceed."));
-                       else
-                               die(_("Your local changes would be overwritten by cherry-pick.\n"
-                                         "Please, commit your changes or stash them to proceed."));
-               } else {
-                       if (action == REVERT)
-                               die(_("Your local changes would be overwritten by revert.\n"));
-                       else
-                               die(_("Your local changes would be overwritten by cherry-pick.\n"));
-               }
-       }
+       if (read_cache_unmerged())
+               return error_resolve_conflict(action_name(opts));
+       /* Different translation strings for cherry-pick and revert */
+       if (opts->action == CHERRY_PICK)
+               error(_("Your local changes would be overwritten by cherry-pick."));
+       else
+               error(_("Your local changes would be overwritten by revert."));
+       if (advice_commit_before_merge)
+               advise(_("Commit your changes or stash them to proceed."));
+       return -1;
  }
  
  static int fast_forward_to(const unsigned char *to, const unsigned char *from)
  
  static int do_recursive_merge(struct commit *base, struct commit *next,
                              const char *base_label, const char *next_label,
-                             unsigned char *head, struct strbuf *msgbuf)
+                             unsigned char *head, struct strbuf *msgbuf,
+                             struct replay_opts *opts)
  {
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        next_tree = next ? next->tree : empty_tree();
        base_tree = base ? base->tree : empty_tree();
  
-       for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
+       for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
                parse_merge_opt(&o, *xopt);
  
        clean = merge_trees(&o,
            (write_cache(index_fd, active_cache, active_nr) ||
             commit_locked_index(&index_lock)))
                /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
-               die(_("%s: Unable to write new index file"), me);
+               die(_("%s: Unable to write new index file"), action_name(opts));
        rollback_lock_file(&index_lock);
  
        if (!clean) {
   * If we are revert, or if our cherry-pick results in a hand merge,
   * we had better say that the current user is responsible for that.
   */
- static int run_git_commit(const char *defmsg)
+ static int run_git_commit(const char *defmsg, struct replay_opts *opts)
  {
        /* 6 is max possible length of our args array including NULL */
        const char *args[6];
  
        args[i++] = "commit";
        args[i++] = "-n";
-       if (signoff)
+       if (opts->signoff)
                args[i++] = "-s";
-       if (!edit) {
+       if (!opts->edit) {
                args[i++] = "-F";
                args[i++] = defmsg;
        }
        return run_command_v_opt(args, RUN_GIT_CMD);
  }
  
- static int do_pick_commit(void)
+ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
  {
        unsigned char head[20];
        struct commit *base, *next, *parent;
        struct strbuf msgbuf = STRBUF_INIT;
        int res;
  
-       if (no_commit) {
+       if (opts->no_commit) {
                /*
                 * We do not intend to commit immediately.  We just want to
                 * merge the differences in, so let's compute the tree
                        die (_("Your index file is unmerged."));
        } else {
                if (get_sha1("HEAD", head))
-                       die (_("You do not have a valid HEAD"));
+                       return error(_("You do not have a valid HEAD"));
                if (index_differs_from("HEAD", 0))
-                       die_dirty_index(me);
+                       return error_dirty_index(opts);
        }
        discard_cache();
  
                int cnt;
                struct commit_list *p;
  
-               if (!mainline)
-                       die(_("Commit %s is a merge but no -m option was given."),
-                           sha1_to_hex(commit->object.sha1));
+               if (!opts->mainline)
+                       return error(_("Commit %s is a merge but no -m option was given."),
+                               sha1_to_hex(commit->object.sha1));
  
                for (cnt = 1, p = commit->parents;
-                    cnt != mainline && p;
+                    cnt != opts->mainline && p;
                     cnt++)
                        p = p->next;
-               if (cnt != mainline || !p)
-                       die(_("Commit %s does not have parent %d"),
-                           sha1_to_hex(commit->object.sha1), mainline);
+               if (cnt != opts->mainline || !p)
+                       return error(_("Commit %s does not have parent %d"),
+                               sha1_to_hex(commit->object.sha1), opts->mainline);
                parent = p->item;
-       } else if (0 < mainline)
-               die(_("Mainline was specified but commit %s is not a merge."),
-                   sha1_to_hex(commit->object.sha1));
+       } else if (0 < opts->mainline)
+               return error(_("Mainline was specified but commit %s is not a merge."),
+                       sha1_to_hex(commit->object.sha1));
        else
                parent = commit->parents->item;
  
-       if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
+       if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
                return fast_forward_to(commit->object.sha1, head);
  
        if (parent && parse_commit(parent) < 0)
                /* TRANSLATORS: The first %s will be "revert" or
                   "cherry-pick", the second %s a SHA1 */
-               die(_("%s: cannot parse parent commit %s"),
-                   me, sha1_to_hex(parent->object.sha1));
+               return error(_("%s: cannot parse parent commit %s"),
+                       action_name(opts), sha1_to_hex(parent->object.sha1));
  
-       if (get_message(commit->buffer, &msg) != 0)
-               die(_("Cannot get commit message for %s"),
-                               sha1_to_hex(commit->object.sha1));
+       if (get_message(commit, &msg) != 0)
+               return error(_("Cannot get commit message for %s"),
+                       sha1_to_hex(commit->object.sha1));
  
        /*
         * "commit" is an existing commit.  We would want to apply
  
        defmsg = git_pathdup("MERGE_MSG");
  
-       if (action == REVERT) {
+       if (opts->action == REVERT) {
                base = commit;
                base_label = msg.label;
                next = parent;
                }
                strbuf_addstr(&msgbuf, ".\n");
        } else {
+               const char *p;
                base = parent;
                base_label = msg.parent_label;
                next = commit;
                next_label = msg.label;
-               add_message_to_msg(&msgbuf, msg.message);
-               if (no_replay) {
+               /*
+                * Append the commit log message to msgbuf; it starts
+                * after the tree, parent, author, committer
+                * information followed by "\n\n".
+                */
+               p = strstr(msg.message, "\n\n");
+               if (p) {
+                       p += 2;
+                       strbuf_addstr(&msgbuf, p);
+               }
+               if (opts->record_origin) {
                        strbuf_addstr(&msgbuf, "(cherry picked from commit ");
                        strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
                        strbuf_addstr(&msgbuf, ")\n");
                }
-               if (!no_commit)
-                       write_cherry_pick_head();
+               if (!opts->no_commit)
+                       write_cherry_pick_head(commit);
        }
  
-       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+       if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REVERT) {
                res = do_recursive_merge(base, next, base_label, next_label,
-                                        head, &msgbuf);
+                                        head, &msgbuf, opts);
                write_message(&msgbuf, defmsg);
        } else {
                struct commit_list *common = NULL;
  
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res = try_merge_command(strategy, xopts_nr, xopts, common,
-                                       sha1_to_hex(head), remotes);
+               res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+                                       common, sha1_to_hex(head), remotes);
                free_commit_list(common);
                free_commit_list(remotes);
        }
  
        if (res) {
-               error(action == REVERT
+               error(opts->action == REVERT
                      ? _("could not revert %s... %s")
                      : _("could not apply %s... %s"),
                      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
                      msg.subject);
                print_advice();
-               rerere(allow_rerere_auto);
+               rerere(opts->allow_rerere_auto);
        } else {
-               if (!no_commit)
-                       res = run_git_commit(defmsg);
+               if (!opts->no_commit)
+                       res = run_git_commit(defmsg, opts);
        }
  
        free_message(&msg);
        return res;
  }
  
- static void prepare_revs(struct rev_info *revs)
+ static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
  {
        int argc;
  
        init_revisions(revs, NULL);
        revs->no_walk = 1;
-       if (action != REVERT)
+       if (opts->action != REVERT)
                revs->reverse = 1;
  
-       argc = setup_revisions(commit_argc, commit_argv, revs, NULL);
+       argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
        if (argc > 1)
-               usage(*revert_or_cherry_pick_usage());
+               usage(*revert_or_cherry_pick_usage(opts));
  
        if (prepare_revision_walk(revs))
                die(_("revision walk setup failed"));
                die(_("empty commit set passed"));
  }
  
- static void read_and_refresh_cache(const char *me)
+ static void read_and_refresh_cache(struct replay_opts *opts)
  {
        static struct lock_file index_lock;
        int index_fd = hold_locked_index(&index_lock, 0);
        if (read_index_preload(&the_index, NULL) < 0)
-               die(_("git %s: failed to read the index"), me);
+               die(_("git %s: failed to read the index"), action_name(opts));
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
        if (the_index.cache_changed) {
                if (write_index(&the_index, index_fd) ||
                    commit_locked_index(&index_lock))
-                       die(_("git %s: failed to refresh the index"), me);
+                       die(_("git %s: failed to refresh the index"), action_name(opts));
        }
        rollback_lock_file(&index_lock);
  }
  
- static int revert_or_cherry_pick(int argc, const char **argv)
+ /*
+  * Append a commit to the end of the commit_list.
+  *
+  * next starts by pointing to the variable that holds the head of an
+  * empty commit_list, and is updated to point to the "next" field of
+  * the last item on the list as new commits are appended.
+  *
+  * Usage example:
+  *
+  *     struct commit_list *list;
+  *     struct commit_list **next = &list;
+  *
+  *     next = commit_list_append(c1, next);
+  *     next = commit_list_append(c2, next);
+  *     assert(commit_list_count(list) == 2);
+  *     return list;
+  */
+ static struct commit_list **commit_list_append(struct commit *commit,
+                                              struct commit_list **next)
+ {
+       struct commit_list *new = xmalloc(sizeof(struct commit_list));
+       new->item = commit;
+       *next = new;
+       new->next = NULL;
+       return &new->next;
+ }
+ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
+               struct replay_opts *opts)
+ {
+       struct commit_list *cur = NULL;
+       struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+       const char *sha1_abbrev = NULL;
+       const char *action_str = opts->action == REVERT ? "revert" : "pick";
+       for (cur = todo_list; cur; cur = cur->next) {
+               sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
+               if (get_message(cur->item, &msg))
+                       return error(_("Cannot get commit message for %s"), sha1_abbrev);
+               strbuf_addf(buf, "%s %s %s\n", action_str, sha1_abbrev, msg.subject);
+       }
+       return 0;
+ }
+ static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
+ {
+       unsigned char commit_sha1[20];
+       char sha1_abbrev[40];
+       enum replay_action action;
+       int insn_len = 0;
+       char *p, *q;
+       if (!prefixcmp(start, "pick ")) {
+               action = CHERRY_PICK;
+               insn_len = strlen("pick");
+               p = start + insn_len + 1;
+       } else if (!prefixcmp(start, "revert ")) {
+               action = REVERT;
+               insn_len = strlen("revert");
+               p = start + insn_len + 1;
+       } else
+               return NULL;
+       q = strchr(p, ' ');
+       if (!q)
+               return NULL;
+       q++;
+       strlcpy(sha1_abbrev, p, q - p);
+       /*
+        * Verify that the action matches up with the one in
+        * opts; we don't support arbitrary instructions
+        */
+       if (action != opts->action) {
+               const char *action_str;
+               action_str = action == REVERT ? "revert" : "cherry-pick";
+               error(_("Cannot %s during a %s"), action_str, action_name(opts));
+               return NULL;
+       }
+       if (get_sha1(sha1_abbrev, commit_sha1) < 0)
+               return NULL;
+       return lookup_commit_reference(commit_sha1);
+ }
+ static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
+                       struct replay_opts *opts)
+ {
+       struct commit_list **next = todo_list;
+       struct commit *commit;
+       char *p = buf;
+       int i;
+       for (i = 1; *p; i++) {
+               commit = parse_insn_line(p, opts);
+               if (!commit)
+                       return error(_("Could not parse line %d."), i);
+               next = commit_list_append(commit, next);
+               p = strchrnul(p, '\n');
+               if (*p)
+                       p++;
+       }
+       if (!*todo_list)
+               return error(_("No commits parsed."));
+       return 0;
+ }
+ static void read_populate_todo(struct commit_list **todo_list,
+                       struct replay_opts *opts)
+ {
+       const char *todo_file = git_path(SEQ_TODO_FILE);
+       struct strbuf buf = STRBUF_INIT;
+       int fd, res;
+       fd = open(todo_file, O_RDONLY);
+       if (fd < 0)
+               die_errno(_("Could not open %s."), todo_file);
+       if (strbuf_read(&buf, fd, 0) < 0) {
+               close(fd);
+               strbuf_release(&buf);
+               die(_("Could not read %s."), todo_file);
+       }
+       close(fd);
+       res = parse_insn_buffer(buf.buf, todo_list, opts);
+       strbuf_release(&buf);
+       if (res)
+               die(_("Unusable instruction sheet: %s"), todo_file);
+ }
+ static int populate_opts_cb(const char *key, const char *value, void *data)
+ {
+       struct replay_opts *opts = data;
+       int error_flag = 1;
+       if (!value)
+               error_flag = 0;
+       else if (!strcmp(key, "options.no-commit"))
+               opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.edit"))
+               opts->edit = git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.signoff"))
+               opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.record-origin"))
+               opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.allow-ff"))
+               opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+       else if (!strcmp(key, "options.mainline"))
+               opts->mainline = git_config_int(key, value);
+       else if (!strcmp(key, "options.strategy"))
+               git_config_string(&opts->strategy, key, value);
+       else if (!strcmp(key, "options.strategy-option")) {
+               ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+               opts->xopts[opts->xopts_nr++] = xstrdup(value);
+       } else
+               return error(_("Invalid key: %s"), key);
+       if (!error_flag)
+               return error(_("Invalid value for %s: %s"), key, value);
+       return 0;
+ }
+ static void read_populate_opts(struct replay_opts **opts_ptr)
+ {
+       const char *opts_file = git_path(SEQ_OPTS_FILE);
+       if (!file_exists(opts_file))
+               return;
+       if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
+               die(_("Malformed options sheet: %s"), opts_file);
+ }
+ static void walk_revs_populate_todo(struct commit_list **todo_list,
+                               struct replay_opts *opts)
  {
        struct rev_info revs;
+       struct commit *commit;
+       struct commit_list **next;
  
-       git_config(git_default_config, NULL);
-       me = action == REVERT ? "revert" : "cherry-pick";
-       setenv(GIT_REFLOG_ACTION, me, 0);
-       parse_args(argc, argv);
-       if (allow_ff) {
-               if (signoff)
-                       die(_("cherry-pick --ff cannot be used with --signoff"));
-               if (no_commit)
-                       die(_("cherry-pick --ff cannot be used with --no-commit"));
-               if (no_replay)
-                       die(_("cherry-pick --ff cannot be used with -x"));
-               if (edit)
-                       die(_("cherry-pick --ff cannot be used with --edit"));
+       prepare_revs(&revs, opts);
+       next = todo_list;
+       while ((commit = get_revision(&revs)))
+               next = commit_list_append(commit, next);
+ }
+ static int create_seq_dir(void)
+ {
+       const char *seq_dir = git_path(SEQ_DIR);
+       if (file_exists(seq_dir))
+               return error(_("%s already exists."), seq_dir);
+       else if (mkdir(seq_dir, 0777) < 0)
+               die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
+       return 0;
+ }
+ static void save_head(const char *head)
+ {
+       const char *head_file = git_path(SEQ_HEAD_FILE);
+       static struct lock_file head_lock;
+       struct strbuf buf = STRBUF_INIT;
+       int fd;
+       fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+       strbuf_addf(&buf, "%s\n", head);
+       if (write_in_full(fd, buf.buf, buf.len) < 0)
+               die_errno(_("Could not write to %s."), head_file);
+       if (commit_lock_file(&head_lock) < 0)
+               die(_("Error wrapping up %s."), head_file);
+ }
+ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
+ {
+       const char *todo_file = git_path(SEQ_TODO_FILE);
+       static struct lock_file todo_lock;
+       struct strbuf buf = STRBUF_INIT;
+       int fd;
+       fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+       if (format_todo(&buf, todo_list, opts) < 0)
+               die(_("Could not format %s."), todo_file);
+       if (write_in_full(fd, buf.buf, buf.len) < 0) {
+               strbuf_release(&buf);
+               die_errno(_("Could not write to %s."), todo_file);
+       }
+       if (commit_lock_file(&todo_lock) < 0) {
+               strbuf_release(&buf);
+               die(_("Error wrapping up %s."), todo_file);
        }
+       strbuf_release(&buf);
+ }
  
-       read_and_refresh_cache(me);
+ static void save_opts(struct replay_opts *opts)
+ {
+       const char *opts_file = git_path(SEQ_OPTS_FILE);
+       if (opts->no_commit)
+               git_config_set_in_file(opts_file, "options.no-commit", "true");
+       if (opts->edit)
+               git_config_set_in_file(opts_file, "options.edit", "true");
+       if (opts->signoff)
+               git_config_set_in_file(opts_file, "options.signoff", "true");
+       if (opts->record_origin)
+               git_config_set_in_file(opts_file, "options.record-origin", "true");
+       if (opts->allow_ff)
+               git_config_set_in_file(opts_file, "options.allow-ff", "true");
+       if (opts->mainline) {
+               struct strbuf buf = STRBUF_INIT;
+               strbuf_addf(&buf, "%d", opts->mainline);
+               git_config_set_in_file(opts_file, "options.mainline", buf.buf);
+               strbuf_release(&buf);
+       }
+       if (opts->strategy)
+               git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+       if (opts->xopts) {
+               int i;
+               for (i = 0; i < opts->xopts_nr; i++)
+                       git_config_set_multivar_in_file(opts_file,
+                                                       "options.strategy-option",
+                                                       opts->xopts[i], "^$", 0);
+       }
+ }
  
-       prepare_revs(&revs);
+ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
+ {
+       struct commit_list *cur;
+       int res;
  
-       while ((commit = get_revision(&revs))) {
-               int res = do_pick_commit();
-               if (res)
+       setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+       if (opts->allow_ff)
+               assert(!(opts->signoff || opts->no_commit ||
+                               opts->record_origin || opts->edit));
+       read_and_refresh_cache(opts);
+       for (cur = todo_list; cur; cur = cur->next) {
+               save_todo(cur, opts);
+               res = do_pick_commit(cur->item, opts);
+               if (res) {
+                       if (!cur->next)
+                               /*
+                                * An error was encountered while
+                                * picking the last commit; the
+                                * sequencer state is useless now --
+                                * the user simply needs to resolve
+                                * the conflict and commit
+                                */
+                               remove_sequencer_state(0);
                        return res;
+               }
        }
  
+       /*
+        * Sequence of picks finished successfully; cleanup by
+        * removing the .git/sequencer directory
+        */
+       remove_sequencer_state(1);
        return 0;
  }
  
+ static int pick_revisions(struct replay_opts *opts)
+ {
+       struct commit_list *todo_list = NULL;
+       unsigned char sha1[20];
+       read_and_refresh_cache(opts);
+       /*
+        * Decide what to do depending on the arguments; a fresh
+        * cherry-pick should be handled differently from an existing
+        * one that is being continued
+        */
+       if (opts->subcommand == REPLAY_RESET) {
+               remove_sequencer_state(1);
+               return 0;
+       } else if (opts->subcommand == REPLAY_CONTINUE) {
+               if (!file_exists(git_path(SEQ_TODO_FILE)))
+                       goto error;
+               read_populate_opts(&opts);
+               read_populate_todo(&todo_list, opts);
+               /* Verify that the conflict has been resolved */
+               if (!index_differs_from("HEAD", 0))
+                       todo_list = todo_list->next;
+       } else {
+               /*
+                * Start a new cherry-pick/ revert sequence; but
+                * first, make sure that an existing one isn't in
+                * progress
+                */
+               walk_revs_populate_todo(&todo_list, opts);
+               if (create_seq_dir() < 0) {
+                       error(_("A cherry-pick or revert is in progress."));
+                       advise(_("Use --continue to continue the operation"));
+                       advise(_("or --reset to forget about it"));
+                       return -1;
+               }
+               if (get_sha1("HEAD", sha1)) {
+                       if (opts->action == REVERT)
+                               return error(_("Can't revert as initial commit"));
+                       return error(_("Can't cherry-pick into empty head"));
+               }
+               save_head(sha1_to_hex(sha1));
+               save_opts(opts);
+       }
+       return pick_commits(todo_list, opts);
+ error:
+       return error(_("No %s in progress"), action_name(opts));
+ }
  int cmd_revert(int argc, const char **argv, const char *prefix)
  {
+       struct replay_opts opts;
+       int res;
+       memset(&opts, 0, sizeof(opts));
        if (isatty(0))
-               edit = 1;
-       action = REVERT;
-       return revert_or_cherry_pick(argc, argv);
+               opts.edit = 1;
+       opts.action = REVERT;
+       git_config(git_default_config, NULL);
+       parse_args(argc, argv, &opts);
+       res = pick_revisions(&opts);
+       if (res < 0)
+               die(_("revert failed"));
+       return res;
  }
  
  int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
  {
-       action = CHERRY_PICK;
-       return revert_or_cherry_pick(argc, argv);
+       struct replay_opts opts;
+       int res;
+       memset(&opts, 0, sizeof(opts));
+       opts.action = CHERRY_PICK;
+       git_config(git_default_config, NULL);
+       parse_args(argc, argv, &opts);
+       res = pick_revisions(&opts);
+       if (res < 0)
+               die(_("cherry-pick failed"));
+       return res;
  }
diff --combined cache.h
index 607c2ea612889c46e81ae375b6985db18e529a8e,58a277d7a58af93b0d17c57721fbfc058e287279..82e12c862c0ed4fb0da7fb86fc3cef26558810dc
+++ b/cache.h
@@@ -6,7 -6,6 +6,7 @@@
  #include "hash.h"
  #include "advice.h"
  #include "gettext.h"
 +#include "convert.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
  #endif
  
  #include <zlib.h>
 -#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
 -#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
 -#endif
 -
 -void git_inflate_init(z_streamp strm);
 -void git_inflate_end(z_streamp strm);
 -int git_inflate(z_streamp strm, int flush);
 +typedef struct git_zstream {
 +      z_stream z;
 +      unsigned long avail_in;
 +      unsigned long avail_out;
 +      unsigned long total_in;
 +      unsigned long total_out;
 +      unsigned char *next_in;
 +      unsigned char *next_out;
 +} git_zstream;
 +
 +void git_inflate_init(git_zstream *);
 +void git_inflate_init_gzip_only(git_zstream *);
 +void git_inflate_end(git_zstream *);
 +int git_inflate(git_zstream *, int flush);
 +
 +void git_deflate_init(git_zstream *, int level);
 +void git_deflate_init_gzip(git_zstream *, int level);
 +void git_deflate_end(git_zstream *);
 +int git_deflate_end_gently(git_zstream *);
 +int git_deflate(git_zstream *, int flush);
 +unsigned long git_deflate_bound(git_zstream *, unsigned long);
  
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
@@@ -394,7 -379,6 +394,7 @@@ static inline enum object_type object_t
  }
  
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
 +#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@@ -435,16 -419,13 +435,16 @@@ extern char *get_object_directory(void)
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
 +extern const char *get_git_namespace(void);
 +extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
 -extern const char *read_gitfile_gently(const char *path);
 +extern const char *read_gitfile(const char *path);
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 +extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -601,6 -582,35 +601,6 @@@ extern int fsync_object_files
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
  
 -enum safe_crlf {
 -      SAFE_CRLF_FALSE = 0,
 -      SAFE_CRLF_FAIL = 1,
 -      SAFE_CRLF_WARN = 2
 -};
 -
 -extern enum safe_crlf safe_crlf;
 -
 -enum auto_crlf {
 -      AUTO_CRLF_FALSE = 0,
 -      AUTO_CRLF_TRUE = 1,
 -      AUTO_CRLF_INPUT = -1
 -};
 -
 -extern enum auto_crlf auto_crlf;
 -
 -enum eol {
 -      EOL_UNSET,
 -      EOL_CRLF,
 -      EOL_LF,
 -#ifdef NATIVE_CRLF
 -      EOL_NATIVE = EOL_CRLF
 -#else
 -      EOL_NATIVE = EOL_LF
 -#endif
 -};
 -
 -extern enum eol core_eol;
 -
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -737,7 -747,7 +737,7 @@@ extern char *expand_user_path(const cha
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
 -      return path[0] == '/' || has_dos_drive_prefix(path);
 +      return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
  const char *real_path(const char *path);
@@@ -770,16 -780,10 +770,16 @@@ extern int hash_sha1_file(const void *b
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 +extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 +extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
  /* global flag to enable extra checks when accessing packed objects */
  extern int do_check_packed_object_crc;
  
 +/* for development: log offset of pack access */
 +extern const char *log_pack_access;
 +
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
@@@ -1005,7 -1009,7 +1005,7 @@@ extern struct packed_git *find_sha1_pac
  extern void pack_report(void);
  extern int open_pack_index(struct packed_git *);
  extern void close_pack_index(struct packed_git *);
 -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 +extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
  extern void free_pack_by_name(const char *);
@@@ -1017,36 -1021,7 +1017,36 @@@ extern off_t find_pack_entry_one(const 
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 -extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 +extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 +
 +struct object_info {
 +      /* Request */
 +      unsigned long *sizep;
 +
 +      /* Response */
 +      enum {
 +              OI_CACHED,
 +              OI_LOOSE,
 +              OI_PACKED,
 +              OI_DBCACHED
 +      } whence;
 +      union {
 +              /*
 +               * struct {
 +               *      ... Nothing to expose in this case
 +               * } cached;
 +               * struct {
 +               *      ... Nothing to expose in this case
 +               * } loose;
 +               */
 +              struct {
 +                      struct packed_git *pack;
 +                      off_t offset;
 +                      unsigned int is_delta;
 +              } packed;
 +      } u;
 +};
 +extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1076,9 -1051,11 +1076,11 @@@ extern int git_config_bool(const char *
  extern int git_config_maybe_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
+ extern int git_config_set_in_file(const char *, const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
+ extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
@@@ -1088,8 -1065,6 +1090,8 @@@ extern int config_error_nonbool(const c
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
  
 +extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1156,6 -1131,13 +1158,6 @@@ extern void trace_strbuf(const char *ke
  
  void packet_trace_identity(const char *prog);
  
 -/* convert.c */
 -/* returns 1 if *dst was used */
 -extern int convert_to_git(const char *path, const char *src, size_t len,
 -                          struct strbuf *dst, enum safe_crlf checksafe);
 -extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 -extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
 -
  /* add */
  /*
   * return 0 if success, 1 - if addition of a file failed and
@@@ -1195,7 -1177,7 +1197,7 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 +int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
diff --combined config.c
index 4183f80262ea9f24e286295a2295f93459548b78,275427b53aaa87a5e06ed070b1634ce50a2040d6..a9e23594bd18b1cde49b55a40b2af8e2f3b74439
+++ b/config.c
  
  #define MAXNAME (256)
  
 -static FILE *config_file;
 -static const char *config_file_name;
 -static int config_linenr;
 -static int config_file_eof;
 +typedef struct config_file {
 +      struct config_file *prev;
 +      FILE *f;
 +      const char *name;
 +      int linenr;
 +      int eof;
 +      struct strbuf value;
 +      char var[MAXNAME];
 +} config_file;
 +
 +static config_file *cf;
 +
  static int zlib_compression_seen;
  
  const char *config_exclusive_filename = NULL;
@@@ -47,13 -39,13 +47,13 @@@ void git_config_push_parameter(const ch
        strbuf_release(&env);
  }
  
 -static int git_config_parse_parameter(const char *text,
 -                                    config_fn_t fn, void *data)
 +int git_config_parse_parameter(const char *text,
 +                             config_fn_t fn, void *data)
  {
 -      struct strbuf tmp = STRBUF_INIT;
        struct strbuf **pair;
 -      strbuf_addstr(&tmp, text);
 -      pair = strbuf_split(&tmp, '=');
 +      pair = strbuf_split_str(text, '=', 2);
 +      if (!pair[0])
 +              return error("bogus config parameter: %s", text);
        if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
                strbuf_setlen(pair[0], pair[0]->len - 1);
        strbuf_trim(pair[0]);
@@@ -107,7 -99,7 +107,7 @@@ static int get_next_char(void
        FILE *f;
  
        c = '\n';
 -      if ((f = config_file) != NULL) {
 +      if (cf && ((f = cf->f) != NULL)) {
                c = fgetc(f);
                if (c == '\r') {
                        /* DOS like systems */
                        }
                }
                if (c == '\n')
 -                      config_linenr++;
 +                      cf->linenr++;
                if (c == EOF) {
 -                      config_file_eof = 1;
 +                      cf->eof = 1;
                        c = '\n';
                }
        }
  
  static char *parse_value(void)
  {
 -      static struct strbuf value = STRBUF_INIT;
        int quote = 0, comment = 0, space = 0;
  
 -      strbuf_reset(&value);
 +      strbuf_reset(&cf->value);
        for (;;) {
                int c = get_next_char();
                if (c == '\n') {
                        if (quote)
                                return NULL;
 -                      return value.buf;
 +                      return cf->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
 -                      if (value.len)
 +                      if (cf->value.len)
                                space++;
                        continue;
                }
                        }
                }
                for (; space; space--)
 -                      strbuf_addch(&value, ' ');
 +                      strbuf_addch(&cf->value, ' ');
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
                        default:
                                return NULL;
                        }
 -                      strbuf_addch(&value, c);
 +                      strbuf_addch(&cf->value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
 -              strbuf_addch(&value, c);
 +              strbuf_addch(&cf->value, c);
        }
  }
  
@@@ -199,7 -192,7 +199,7 @@@ static int get_value(config_fn_t fn, vo
        /* Get the full name */
        for (;;) {
                c = get_next_char();
 -              if (config_file_eof)
 +              if (cf->eof)
                        break;
                if (!iskeychar(c))
                        break;
@@@ -263,7 -256,7 +263,7 @@@ static int get_base_var(char *name
  
        for (;;) {
                int c = get_next_char();
 -              if (config_file_eof)
 +              if (cf->eof)
                        return -1;
                if (c == ']')
                        return baselen;
@@@ -281,7 -274,7 +281,7 @@@ static int git_parse_file(config_fn_t f
  {
        int comment = 0;
        int baselen = 0;
 -      static char var[MAXNAME];
 +      char *var = cf->var;
  
        /* U+FEFF Byte Order Mark in UTF8 */
        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
                        }
                }
                if (c == '\n') {
 -                      if (config_file_eof)
 +                      if (cf->eof)
                                return 0;
                        comment = 0;
                        continue;
                if (get_value(fn, data, var, baselen+1) < 0)
                        break;
        }
 -      die("bad config file line %d in %s", config_linenr, config_file_name);
 +      die("bad config file line %d in %s", cf->linenr, cf->name);
  }
  
  static int parse_unit_factor(const char *end, unsigned long *val)
@@@ -381,8 -374,8 +381,8 @@@ int git_parse_ulong(const char *value, 
  
  static void die_bad_config(const char *name)
  {
 -      if (config_file_name)
 -              die("bad config value for '%s' in %s", name, config_file_name);
 +      if (cf && cf->name)
 +              die("bad config value for '%s' in %s", name, cf->name);
        die("bad config value for '%s'", name);
  }
  
@@@ -576,9 -569,6 +576,9 @@@ static int git_default_core_config(cons
                return 0;
        }
  
 +      if (!strcmp(var, "core.logpackaccess"))
 +              return git_config_string(&log_pack_access, var, value);
 +
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
                        if (core_eol == EOL_CRLF)
@@@ -805,24 -795,13 +805,24 @@@ int git_config_from_file(config_fn_t fn
  
        ret = -1;
        if (f) {
 -              config_file = f;
 -              config_file_name = filename;
 -              config_linenr = 1;
 -              config_file_eof = 0;
 +              config_file top;
 +
 +              /* push config-file parsing state stack */
 +              top.prev = cf;
 +              top.f = f;
 +              top.name = filename;
 +              top.linenr = 1;
 +              top.eof = 0;
 +              strbuf_init(&top.value, 1024);
 +              cf = &top;
 +
                ret = git_parse_file(fn, data);
 +
 +              /* pop config-file parsing state stack */
 +              strbuf_release(&top.value);
 +              cf = top.prev;
 +
                fclose(f);
 -              config_file_name = NULL;
        }
        return ret;
  }
@@@ -877,7 -856,7 +877,7 @@@ int git_config_early(config_fn_t fn, vo
  
        switch (git_config_from_parameters(fn, data)) {
        case -1: /* error */
 -              ret--;
 +              die("unable to parse command-line config");
                break;
        case 0: /* found nothing */
                break;
@@@ -930,7 -909,6 +930,7 @@@ static int store_aux(const char *key, c
  {
        const char *ep;
        size_t section_len;
 +      FILE *f = cf->f;
  
        switch (store.state) {
        case KEY_SEEN:
                                return 1;
                        }
  
 -                      store.offset[store.seen] = ftell(config_file);
 +                      store.offset[store.seen] = ftell(f);
                        store.seen++;
                }
                break;
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
 -              store.offset[store.seen] = ftell(config_file);
 +              store.offset[store.seen] = ftell(f);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
 -                      store.offset[store.seen] = ftell(config_file);
 +                      store.offset[store.seen] = ftell(f);
                        store.state = KEY_SEEN;
                        store.seen++;
                } else {
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
 -                                      store.offset[store.seen] = ftell(config_file);
 +                                      store.offset[store.seen] = ftell(f);
                        }
                }
        }
@@@ -1095,6 -1073,12 +1095,12 @@@ contline
        return offset;
  }
  
+ int git_config_set_in_file(const char *config_filename,
+                       const char *key, const char *value)
+ {
+       return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
+ }
  int git_config_set(const char *key, const char *value)
  {
        return git_config_set_multivar(key, value, NULL, 0);
@@@ -1192,19 -1176,14 +1198,14 @@@ out_free_ret_1
   * - the config file is removed and the lock file rename()d to it.
   *
   */
- int git_config_set_multivar(const char *key, const char *value,
-       const char *value_regex, int multi_replace)
+ int git_config_set_multivar_in_file(const char *config_filename,
+                               const char *key, const char *value,
+                               const char *value_regex, int multi_replace)
  {
        int fd = -1, in_fd;
        int ret;
-       char *config_filename;
        struct lock_file *lock = NULL;
  
-       if (config_exclusive_filename)
-               config_filename = xstrdup(config_exclusive_filename);
-       else
-               config_filename = git_pathdup("config");
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
        if (ret)
  out_free:
        if (lock)
                rollback_lock_file(lock);
-       free(config_filename);
        return ret;
  
  write_err_out:
  
  }
  
+ int git_config_set_multivar(const char *key, const char *value,
+                       const char *value_regex, int multi_replace)
+ {
+       const char *config_filename;
+       char *buf = NULL;
+       int ret;
+       if (config_exclusive_filename)
+               config_filename = config_exclusive_filename;
+       else
+               config_filename = buf = git_pathdup("config");
+       ret = git_config_set_multivar_in_file(config_filename, key, value,
+                                       value_regex, multi_replace);
+       free(buf);
+       return ret;
+ }
  static int section_name_match (const char *buf, const char *name)
  {
        int i = 0, j = 0, dot = 0;
@@@ -1437,7 -1433,6 +1455,7 @@@ int git_config_rename_section(const cha
        struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
        int out_fd;
        char buf[1024];
 +      FILE *config_file;
  
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
                }
        }
        fclose(config_file);
 - unlock_and_out:
 +unlock_and_out:
        if (commit_lock_file(lock) < 0)
                ret = error("could not commit config file %s", config_filename);
 - out:
 +out:
        free(config_filename);
        return ret;
  }