Merge branch 'jc/cache-unmerge'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:44:31 +0000 (14:44 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:46:35 +0000 (14:46 -0800)
* jc/cache-unmerge:
rerere forget path: forget recorded resolution
rerere: refactor rerere logic to make it independent from I/O
rerere: remove silly 1024-byte line limit
resolve-undo: teach "update-index --unresolve" to use resolve-undo info
resolve-undo: "checkout -m path" uses resolve-undo information
resolve-undo: allow plumbing to clear the information
resolve-undo: basic tests
resolve-undo: record resolved conflicts in a new index extension section
builtin-merge.c: use standard active_cache macros

Conflicts:
builtin-ls-files.c
builtin-merge.c
builtin-rerere.c

1  2 
Makefile
builtin-checkout.c
builtin-ls-files.c
builtin-merge.c
builtin-read-tree.c
builtin-rerere.c
builtin-update-index.c
cache.h
read-cache.c
rerere.c
rerere.h
diff --combined Makefile
index cf0723ea122b09c66a6da367a653381966729847,762898ae7f4bd985ba85a1b5af878de6eec9c3ef..ad890ec9254617e2b4a6cfb35ec91b44ebd8f37a
+++ b/Makefile
@@@ -168,8 -168,6 +168,8 @@@ all:
  #
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
 +# Define NO_PYTHON if you do not want Python scripts or libraries at all.
 +#
  # Define NO_TCLTK if you do not want Tcl/Tk GUI.
  #
  # The TCL_PATH variable governs the location of the Tcl interpreter
  # is a simplified version of the merge sort used in glibc. This is
  # recommended if Git triggers O(n^2) behavior in your platform's qsort().
  #
 -# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
 -# your external grep (e.g., if your system lacks grep, if its grep is
 -# broken, or spawning external process is slower than built-in grep git has).
 -#
  # Define UNRELIABLE_FSTAT if your system's fstat does not return the same
  # information on a not yet closed file that lstat would return for the same
  # file after it was closed.
  #   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
  #   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
  
 -GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 +GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
  -include GIT-VERSION-FILE
  
@@@ -339,7 -341,6 +339,7 @@@ LIB_H 
  LIB_OBJS =
  PROGRAMS =
  SCRIPT_PERL =
 +SCRIPT_PYTHON =
  SCRIPT_SH =
  TEST_PROGRAMS =
  
@@@ -378,7 -379,6 +378,7 @@@ SCRIPT_PERL += git-svn.per
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
 +        $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
          git-instaweb
  
  # Empty...
@@@ -427,15 -427,6 +427,15 @@@ ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS
  # what 'all' will build but not install in gitexecdir
  OTHER_PROGRAMS = git$X
  
 +# what test wrappers are needed and 'install' will install, in bindir
 +BINDIR_PROGRAMS_NEED_X += git
 +BINDIR_PROGRAMS_NEED_X += git-upload-pack
 +BINDIR_PROGRAMS_NEED_X += git-receive-pack
 +BINDIR_PROGRAMS_NEED_X += git-upload-archive
 +BINDIR_PROGRAMS_NEED_X += git-shell
 +
 +BINDIR_PROGRAMS_NO_X += git-cvsserver
 +
  # Set paths to tools early so that they can be used for version tests.
  ifndef SHELL_PATH
        SHELL_PATH = /bin/sh
@@@ -443,12 -434,8 +443,12 @@@ endi
  ifndef PERL_PATH
        PERL_PATH = /usr/bin/perl
  endif
 +ifndef PYTHON_PATH
 +      PYTHON_PATH = /usr/bin/python
 +endif
  
  export PERL_PATH
 +export PYTHON_PATH
  
  LIB_FILE=libgit.a
  XDIFF_LIB=xdiff/lib.a
@@@ -464,7 -451,6 +464,7 @@@ LIB_H += commit.
  LIB_H += compat/bswap.h
  LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
 +LIB_H += compat/win32/pthread.h
  LIB_H += csum-file.h
  LIB_H += decorate.h
  LIB_H += delta.h
@@@ -497,6 -483,7 +497,7 @@@ LIB_H += reflog-walk.
  LIB_H += refs.h
  LIB_H += remote.h
  LIB_H += rerere.h
+ LIB_H += resolve-undo.h
  LIB_H += revision.h
  LIB_H += run-command.h
  LIB_H += sha1-lookup.h
@@@ -592,6 -579,7 +593,7 @@@ LIB_OBJS += refs.
  LIB_OBJS += remote.o
  LIB_OBJS += replace_object.o
  LIB_OBJS += rerere.o
+ LIB_OBJS += resolve-undo.o
  LIB_OBJS += revision.o
  LIB_OBJS += run-command.o
  LIB_OBJS += server-info.o
@@@ -791,6 -779,7 +793,6 @@@ ifeq ($(uname_S),SunOS
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
 -      NO_EXTERNAL_GREP = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
@@@ -841,7 -830,6 +843,7 @@@ ifeq ($(uname_O),Cygwin
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
 +      OLD_ICONV = YesPlease
        NO_MEMMEM = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@@ -909,6 -897,7 +911,6 @@@ ifeq ($(uname_S),IRIX
        # NO_MMAP.  If you suspect that your compiler is not affected by this
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
 -      NO_EXTERNAL_GREP = UnfortunatelyYes
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -928,6 -917,7 +930,6 @@@ ifeq ($(uname_S),IRIX64
        # NO_MMAP.  If you suspect that your compiler is not affected by this
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
 -      NO_EXTERNAL_GREP = UnfortunatelyYes
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -979,16 -969,15 +981,16 @@@ ifeq ($(uname_S),Windows
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
 -      NO_PTHREADS = YesPlease
 +      NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
  
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
        CFLAGS =
        BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
 -      COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
 -      COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
 +      COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o
 +      COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
        EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
        lib =
@@@ -1030,13 -1019,10 +1032,13 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
 +      NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 -      COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
 +      COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
 +              compat/win32/pthread.o
        EXTLIBS += -lws2_32
        X = .exe
  ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        EXTLIBS += /mingw/lib/libz.a
        NO_R_TO_GCC_LINKER = YesPlease
        INTERNAL_QSORT = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  else
        NO_CURL = YesPlease
 -      NO_PTHREADS = YesPlease
  endif
  endif
  
@@@ -1095,9 -1083,6 +1097,9 @@@ endi
  
  ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
 +      REMOTE_CURL_PRIMARY =
 +      REMOTE_CURL_ALIASES =
 +      REMOTE_CURL_NAMES =
  else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
        else
                CURL_LIBCURL = -lcurl
        endif
 -      PROGRAMS += git-remote-curl$X git-http-fetch$X
 +      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)
 +      PROGRAMS += $(REMOTE_CURL_NAMES) git-http-fetch$X
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
@@@ -1342,6 -1324,9 +1344,6 @@@ endi
  ifdef DIR_HAS_BSD_GROUP_SEMANTICS
        COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
  endif
 -ifdef NO_EXTERNAL_GREP
 -      BASIC_CFLAGS += -DNO_EXTERNAL_GREP
 -endif
  ifdef UNRELIABLE_FSTAT
        BASIC_CFLAGS += -DUNRELIABLE_FSTAT
  endif
@@@ -1363,10 -1348,6 +1365,10 @@@ ifeq ($(PERL_PATH),
  NO_PERL=NoThanks
  endif
  
 +ifeq ($(PYTHON_PATH),)
 +NO_PYTHON=NoThanks
 +endif
 +
  QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
  QUIET_SUBDIR1  =
  
@@@ -1414,7 -1395,6 +1416,7 @@@ prefix_SQ = $(subst ','\'',$(prefix)
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
  PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 +PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
  TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
  
  LIBS = $(GITLIBS) $(EXTLIBS)
@@@ -1461,9 -1441,6 +1463,9 @@@ ifndef NO_TCLT
  endif
  ifndef NO_PERL
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
 +endif
 +ifndef NO_PYTHON
 +      $(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
  endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
  
@@@ -1475,19 -1452,20 +1477,19 @@@ shell_compatibility_test: please_set_SH
  strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
  
 -git.o: git.c common-cmds.h GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
 -              '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 -              $(ALL_CFLAGS) -o $@ -c $(filter %.c,$^)
 +git.o: common-cmds.h
 +git.s git.o: ALL_CFLAGS += -DGIT_VERSION='"$(GIT_VERSION)"' \
 +      '-DGIT_HTML_PATH="$(htmldir_SQ)"'
  
  git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
 -builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 -              '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 -              '-DGIT_MAN_PATH="$(mandir_SQ)"' \
 -              '-DGIT_INFO_PATH="$(infodir_SQ)"' $<
 +builtin-help.o: common-cmds.h
 +builtin-help.s builtin-help.o: ALL_CFLAGS += \
 +      '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 +      '-DGIT_MAN_PATH="$(mandir_SQ)"' \
 +      '-DGIT_INFO_PATH="$(infodir_SQ)"'
  
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@@ -1590,41 -1568,11 +1592,41 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git
        mv $@+ $@
  endif # NO_PERL
  
 +
  ifdef JSMIN
  gitweb/gitweb.min.js: gitweb/gitweb.js
        $(QUIET_GEN)$(JSMIN) <$< >$@
  endif # JSMIN
  
 +ifndef NO_PYTHON
 +$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS
 +$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
 +      $(QUIET_GEN)$(RM) $@ $@+ && \
 +      INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
 +              --no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
 +              instlibdir` && \
 +      sed -e '1{' \
 +          -e '        s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
 +          -e '}' \
 +          -e 's|^import sys.*|&; \\\
 +                 import os; \\\
 +                 sys.path[0] = os.environ.has_key("GITPYTHONLIB") and \\\
 +                               os.environ["GITPYTHONLIB"] or \\\
 +                               "@@INSTLIBDIR@@"|' \
 +          -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
 +          $@.py >$@+ && \
 +      chmod +x $@+ && \
 +      mv $@+ $@
 +else # NO_PYTHON
 +$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
 +      $(QUIET_GEN)$(RM) $@ $@+ && \
 +      sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 +          -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
 +          unimplemented.sh >$@+ && \
 +      chmod +x $@+ && \
 +      mv $@+ $@
 +endif # NO_PYTHON
 +
  configure: configure.ac
        $(QUIET_GEN)$(RM) $@ $<+ && \
        sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@@@ -1640,26 -1588,30 +1642,26 @@@ git.o git.spec 
  
  %.o: %.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
 -%.s: %.c GIT-CFLAGS
 +%.s: %.c GIT-CFLAGS FORCE
        $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
 -%.o: %.S
 +%.o: %.S GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
  
 -exec_cmd.o: exec_cmd.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 -              '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
 -              '-DBINDIR="$(bindir_relative_SQ)"' \
 -              '-DPREFIX="$(prefix_SQ)"' \
 -              $<
 +exec_cmd.s exec_cmd.o: ALL_CFLAGS += \
 +      '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
 +      '-DBINDIR="$(bindir_relative_SQ)"' \
 +      '-DPREFIX="$(prefix_SQ)"'
  
 -builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
 +builtin-init-db.s builtin-init-db.o: ALL_CFLAGS += \
 +      -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
  
 -config.o: config.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
 +config.s config.o: ALL_CFLAGS += -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
  
 -http.o: http.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 +http.s http.o: ALL_CFLAGS += -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
  
  ifdef NO_EXPAT
 -http-walker.o: http-walker.c http.h GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
 +http-walker.o: http.h
 +http-walker.s http-walker.o: ALL_CFLAGS += -DNO_EXPAT
  endif
  
  git-%$X: %.o $(GITLIBS)
@@@ -1680,13 -1632,7 +1682,13 @@@ git-http-push$X: revision.o http.o http
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
 -git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
 +$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
 +      $(QUIET_LNCP)$(RM) $@ && \
 +      ln $< $@ 2>/dev/null || \
 +      ln -s $< $@ 2>/dev/null || \
 +      cp $< $@
 +
 +$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
@@@ -1737,7 -1683,7 +1739,7 @@@ cscope
  TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
               $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
  
 -GIT-CFLAGS: .FORCE-GIT-CFLAGS
 +GIT-CFLAGS: FORCE
        @FLAGS='$(TRACK_CFLAGS)'; \
            if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \
                echo 1>&2 "    * new build flags or prefix"; \
  # 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".
 -GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
 +GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 +      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
  
  ### Detect Tck/Tk interpreter path changes
  ifndef NO_TCLTK
  TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
  
 -GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
 +GIT-GUI-VARS: FORCE
        @VARS='$(TRACK_VARS)'; \
            if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
                echo 1>&2 "    * new Tcl/Tk interpreter location"; \
                echo "$$VARS" >$@; \
              fi
 -
 -.PHONY: .FORCE-GIT-GUI-VARS
  endif
  
  ### Testing rules
  
 -TEST_PROGRAMS += test-chmtime$X
 -TEST_PROGRAMS += test-ctype$X
 -TEST_PROGRAMS += test-date$X
 -TEST_PROGRAMS += test-delta$X
 -TEST_PROGRAMS += test-dump-cache-tree$X
 -TEST_PROGRAMS += test-genrandom$X
 -TEST_PROGRAMS += test-match-trees$X
 -TEST_PROGRAMS += test-parse-options$X
 -TEST_PROGRAMS += test-path-utils$X
 -TEST_PROGRAMS += test-sha1$X
 -TEST_PROGRAMS += test-sigchain$X
 -
 -all:: $(TEST_PROGRAMS)
 +TEST_PROGRAMS_NEED_X += test-chmtime
 +TEST_PROGRAMS_NEED_X += test-ctype
 +TEST_PROGRAMS_NEED_X += test-date
 +TEST_PROGRAMS_NEED_X += test-delta
 +TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 +TEST_PROGRAMS_NEED_X += test-genrandom
 +TEST_PROGRAMS_NEED_X += test-match-trees
 +TEST_PROGRAMS_NEED_X += test-parse-options
 +TEST_PROGRAMS_NEED_X += test-path-utils
 +TEST_PROGRAMS_NEED_X += test-run-command
 +TEST_PROGRAMS_NEED_X += test-sha1
 +TEST_PROGRAMS_NEED_X += test-sigchain
 +TEST_PROGRAMS_NEED_X += test-index-version
 +
 +TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 +
 +test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 +
 +all:: $(TEST_PROGRAMS) $(test_bindir_programs)
 +
 +bin-wrappers/%: wrap-for-bin.sh
 +      @mkdir -p bin-wrappers
 +      $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 +           -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
 +           -e 's|@@PROG@@|$(@F)|' < $< > $@ && \
 +      chmod +x $@
  
  # GNU make supports exporting all variables by "export" without parameters.
  # However, the environment gets quite big, and some programs have problems
@@@ -1855,20 -1789,15 +1857,20 @@@ endi
  gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
  export gitexec_instdir
  
 +install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
 +
  install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -      $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
  ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
  endif
 +ifndef NO_PYTHON
 +      $(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 +endif
  ifndef NO_TCLTK
        $(MAKE) -C gitk-git install
        $(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install
@@@ -1876,7 -1805,6 +1878,7 @@@ endi
  ifneq (,$X)
        $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
  endif
 +
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
        { test "$$bindir/" = "$$execdir/" || \
                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
          done; } && \
 +      { for p in $(REMOTE_CURL_ALIASES); do \
 +              $(RM) "$$execdir/$$p" && \
 +              ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 +              ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 +              cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
 +        done; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
  
  install-doc:
@@@ -1982,7 -1904,6 +1984,7 @@@ clean
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
 +      $(RM) -r bin-wrappers
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
  ifndef NO_PERL
        $(RM) gitweb/gitweb.cgi
        $(MAKE) -C perl clean
 +endif
 +ifndef NO_PYTHON
 +      $(MAKE) -C git_remote_helpers clean
  endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
@@@ -2007,7 -1925,8 +2009,7 @@@ endi
  
  .PHONY: all install clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
 -.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
 -.PHONY: .FORCE-GIT-BUILD-OPTIONS
 +.PHONY: FORCE TAGS tags cscope
  
  ### Check documentation
  #
diff --combined builtin-checkout.c
index e44e238c39a7d4c276edf3bfe054b5929c009eda,bdef1aa38699e6c74ca30b313406ef093890e902..d0b1a728cb81e041c685b37014688f0684779132
@@@ -17,6 -17,7 +17,7 @@@
  #include "blob.h"
  #include "xdiff-interface.h"
  #include "ll-merge.h"
+ #include "resolve-undo.h"
  
  static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@@ -167,7 -168,7 +168,7 @@@ static int checkout_merged(int pos, str
        fill_mm(active_cache[pos+2]->sha1, &theirs);
  
        status = ll_merge(&result_buf, path, &ancestor,
 -                        &ours, "ours", &theirs, "theirs", 1);
 +                        &ours, "ours", &theirs, "theirs", 0);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
@@@ -234,6 -235,10 +235,10 @@@ static int checkout_paths(struct tree *
        if (report_path_error(ps_matched, pathspec, 0))
                return 1;
  
+       /* "checkout -m path" to recreate conflicted state */
+       if (opts->merge)
+               unmerge_cache(pathspec);
        /* Any unmerged paths? */
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
@@@ -370,6 -375,7 +375,7 @@@ static int merge_working_tree(struct ch
        if (read_cache_preload(NULL) < 0)
                return error("corrupt index file");
  
+       resolve_undo_clear();
        if (opts->force) {
                ret = reset_tree(new->commit->tree, opts, 1);
                if (ret)
                topts.initial_checkout = is_cache_unborn();
                topts.update = 1;
                topts.merge = 1;
 -              topts.gently = opts->merge;
 +              topts.gently = opts->merge && old->commit;
                topts.verbose_update = !opts->quiet;
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                        struct merge_options o;
                        if (!opts->merge)
                                return 1;
 -                      parse_commit(old->commit);
 +
 +                      /*
 +                       * Without old->commit, the below is the same as
 +                       * the two-tree unpack we already tried and failed.
 +                       */
 +                      if (!old->commit)
 +                              return 1;
  
                        /* Do more real merge */
  
@@@ -696,10 -696,7 +702,10 @@@ int cmd_checkout(int argc, const char *
         * case 3: git checkout <something> [<paths>]
         *
         *   With no paths, if <something> is a commit, that is to
 -       *   switch to the branch or detach HEAD at it.
 +       *   switch to the branch or detach HEAD at it.  As a special case,
 +       *   if <something> is A...B (missing A or B means HEAD but you can
 +       *   omit at most one side), and if there is a unique merge base
 +       *   between A and B, A...B names that merge base.
         *
         *   With no paths, if <something> is _not_ a commit, no -t nor -b
         *   was given, and there is a tracking branch whose name is
                if (!strcmp(arg, "-"))
                        arg = "@{-1}";
  
 -              if (get_sha1(arg, rev)) {
 +              if (get_sha1_mb(arg, rev)) {
                        if (has_dash_dash)          /* case (1) */
                                die("invalid reference: %s", arg);
                        if (!patch_mode &&
diff --combined builtin-ls-files.c
index 738215768ea701f0a35ed1e7bdc543779e6f4185,ef3a06889ab193b9e3aae679560f3eeaf300a8d5..b065061392718e4250d2c52dc3d53a0c1c7938e9
@@@ -11,6 -11,8 +11,8 @@@
  #include "builtin.h"
  #include "tree.h"
  #include "parse-options.h"
+ #include "resolve-undo.h"
+ #include "string-list.h"
  
  static int abbrev;
  static int show_deleted;
@@@ -18,6 -20,7 +20,7 @@@ static int show_cached
  static int show_others;
  static int show_stage;
  static int show_unmerged;
+ static int show_resolve_undo;
  static int show_modified;
  static int show_killed;
  static int show_valid_bit;
@@@ -37,7 -40,7 +40,8 @@@ static const char *tag_removed = ""
  static const char *tag_other = "";
  static const char *tag_killed = "";
  static const char *tag_modified = "";
 +static const char *tag_skip_worktree = "";
+ static const char *tag_resolve_undo = "";
  
  static void show_dir_entry(const char *tag, struct dir_entry *ent)
  {
@@@ -156,6 -159,38 +160,38 @@@ static void show_ce_entry(const char *t
        write_name_quoted(ce->name + offset, stdout, line_terminator);
  }
  
+ static int show_one_ru(struct string_list_item *item, void *cbdata)
+ {
+       int offset = prefix_offset;
+       const char *path = item->string;
+       struct resolve_undo_info *ui = item->util;
+       int i, len;
+       len = strlen(path);
+       if (len < prefix_len)
+               return 0; /* outside of the prefix */
+       if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
+               return 0; /* uninterested */
+       for (i = 0; i < 3; i++) {
+               if (!ui->mode[i])
+                       continue;
+               printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+                      abbrev
+                      ? find_unique_abbrev(ui->sha1[i], abbrev)
+                      : sha1_to_hex(ui->sha1[i]),
+                      i + 1);
+               write_name_quoted(path + offset, stdout, line_terminator);
+       }
+       return 0;
+ }
+ static void show_ru_info(const char *prefix)
+ {
+       if (!the_index.resolve_undo)
+               return;
+       for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
+ }
  static void show_files(struct dir_struct *dir, const char *prefix)
  {
        int i;
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
 -                      show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
 +                      show_ce_entry(ce_stage(ce) ? tag_unmerged :
 +                              (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
                }
        }
        if (show_deleted | show_modified) {
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
 +                      if (ce_skip_worktree(ce))
 +                              continue;
                        err = lstat(ce->name, &st);
                        if (show_deleted && err)
                                show_ce_entry(tag_removed, ce);
@@@ -458,6 -490,8 +494,8 @@@ int cmd_ls_files(int argc, const char *
                        DIR_HIDE_EMPTY_DIRECTORIES),
                OPT_BOOLEAN('u', "unmerged", &show_unmerged,
                        "show unmerged files in the output"),
+               OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
+                           "show resolve-undo information"),
                { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
                        "skip files matching pattern",
                        0, option_parse_exclude },
                prefix_offset = strlen(prefix);
        git_config(git_default_config, NULL);
  
 +      if (read_cache() < 0)
 +              die("index file corrupt");
 +
        argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
                        ls_files_usage, 0);
        if (show_tag || show_valid_bit) {
                tag_modified = "C ";
                tag_other = "? ";
                tag_killed = "K ";
 +              tag_skip_worktree = "S ";
+               tag_resolve_undo = "U ";
        }
        if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
                require_work_tree = 1;
        pathspec = get_pathspec(prefix, argv);
  
        /* be nice with submodule paths ending in a slash */
 -      read_cache();
        if (pathspec)
                strip_trailing_slash_from_submodules();
  
  
        /* With no flags, we default to showing the cached files */
        if (!(show_stage | show_deleted | show_others | show_unmerged |
-             show_killed | show_modified))
+             show_killed | show_modified | show_resolve_undo))
                show_cached = 1;
  
        if (prefix)
                overlay_tree_on_cache(with_tree, prefix);
        }
        show_files(&dir, prefix);
+       if (show_resolve_undo)
+               show_ru_info(prefix);
  
        if (ps_matched) {
                int bad;
diff --combined builtin-merge.c
index 6f1311414b6ba6c956cdc75a4afc0ed361a0f92d,6bc2f7af08ab94a5af8cd619d18dbe2244143c8b..9f60ffa2cd32fd78c04d81135cee42b584627e7c
@@@ -24,6 -24,7 +24,7 @@@
  #include "rerere.h"
  #include "help.h"
  #include "merge-recursive.h"
+ #include "resolve-undo.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -52,7 -53,6 +53,7 @@@ static struct strategy **use_strategies
  static size_t use_strategies_nr, use_strategies_alloc;
  static const char *branch;
  static int verbosity;
 +static int allow_rerere_auto;
  
  static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@@ -171,7 -171,6 +172,7 @@@ static struct option builtin_merge_opti
                "allow fast-forward (default)"),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
                "abort if fast-forward is not possible"),
 +      OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
                "merge strategy to use", option_parse_strategy),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
@@@ -606,6 -605,7 +607,7 @@@ static int try_merge_strategy(const cha
                discard_cache();
                if (read_cache() < 0)
                        die("failed to read the cache");
+               resolve_undo_clear();
                return ret;
        }
  }
@@@ -620,11 -620,10 +622,10 @@@ static void count_diff_files(struct dif
  
  static int count_unmerged_entries(void)
  {
-       const struct index_state *state = &the_index;
        int i, ret = 0;
  
-       for (i = 0; i < state->cache_nr; i++)
-               if (ce_stage(state->cache[i]))
+       for (i = 0; i < active_nr; i++)
+               if (ce_stage(active_cache[i]))
                        ret++;
  
        return ret;
@@@ -792,7 -791,7 +793,7 @@@ static int suggest_conflicts(void
                }
        }
        fclose(fp);
 -      rerere();
 +      rerere(allow_rerere_auto);
        printf("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n");
        return 1;
@@@ -849,21 -848,12 +850,22 @@@ int cmd_merge(int argc, const char **ar
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list **remotes = &remoteheads;
  
 -      if (file_exists(git_path("MERGE_HEAD")))
 -              die("You have not concluded your merge. (MERGE_HEAD exists)");
 -      if (read_cache_unmerged())
 -              die("You are in the middle of a conflicted merge."
 -                              " (index unmerged)");
 +      if (read_cache_unmerged()) {
 +              die_resolve_conflict("merge");
 +      }
 +      if (file_exists(git_path("MERGE_HEAD"))) {
 +              /*
 +               * There is no unmerged entry, don't advise 'git
 +               * add/rm <file>', just 'git commit'.
 +               */
 +              if (advice_resolve_conflict)
 +                      die("You have not concluded your merge (MERGE_HEAD exists).\n"
 +                          "Please, commit your changes before you can merge.");
 +              else
 +                      die("You have not concluded your merge (MERGE_HEAD exists).");
 +      }
 +
+       resolve_undo_clear();
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
diff --combined builtin-read-tree.c
index 50413ca17df294a8b10dd406db55a24cf601bb55,7d378b75480331b242410b9680399728efbae49d..5fda9905fcfa5ccbc0f28578cd371314a8a7e5a7
@@@ -13,6 -13,7 +13,7 @@@
  #include "dir.h"
  #include "builtin.h"
  #include "parse-options.h"
+ #include "resolve-undo.h"
  
  static int nr_trees;
  static struct tree *trees[MAX_UNPACK_TREES];
@@@ -31,7 -32,7 +32,7 @@@ static int list_tree(unsigned char *sha
  }
  
  static const char * const read_tree_usage[] = {
 -      "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]]  [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
 +      "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
        NULL
  };
  
@@@ -98,8 -99,6 +99,8 @@@ int cmd_read_tree(int argc, const char 
                  PARSE_OPT_NONEG, exclude_per_directory_cb },
                OPT_SET_INT('i', NULL, &opts.index_only,
                            "don't check the working tree after merging", 1),
 +              OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
 +                          "skip applying sparse checkout filter", 1),
                OPT_END()
        };
  
                        die("You need to resolve your current index first");
                stage = opts.merge = 1;
        }
+       resolve_undo_clear();
  
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
diff --combined builtin-rerere.c
index 5028138898bf0d95d969ea19b85d23af129912d0,0253abf9b698ed10a8df5c21ef07c93b8e6460dc..25f507a2f1df89919d966b7f586fd57d9c57a227
@@@ -103,24 -103,17 +103,27 @@@ static int diff_two(const char *file1, 
  int cmd_rerere(int argc, const char **argv, const char *prefix)
  {
        struct string_list merge_rr = { NULL, 0, 0, 1 };
 -      int i, fd;
 -
 +      int i, fd, flags = 0;
 +
 +      if (2 < argc) {
 +              if (!strcmp(argv[1], "-h"))
 +                      usage(git_rerere_usage);
 +              if (!strcmp(argv[1], "--rerere-autoupdate"))
 +                      flags = RERERE_AUTOUPDATE;
 +              else if (!strcmp(argv[1], "--no-rerere-autoupdate"))
 +                      flags = RERERE_NOAUTOUPDATE;
 +              if (flags) {
 +                      argc--;
 +                      argv++;
 +              }
 +      }
        if (argc < 2)
 -              return rerere();
 +              return rerere(flags);
  
 -      if (!strcmp(argv[1], "-h"))
 -              usage(git_rerere_usage);
 -      else if (!strcmp(argv[1], "forget"))
++      if (!strcmp(argv[1], "forget"))
+               return rerere_forget(argv + 2);
 -      fd = setup_rerere(&merge_rr);
 +      fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
  
diff --combined builtin-update-index.c
index a64a752990b85b69590cdbeed3901154642007b8,750db163b93be19e1fe5648af49f3302cacecb2f..3ab214d24e537865875d6d1e3acd8cf6b019ff22
@@@ -9,6 -9,7 +9,7 @@@
  #include "tree-walk.h"
  #include "builtin.h"
  #include "refs.h"
+ #include "resolve-undo.h"
  
  /*
   * Default to not allowing changes to the list of files. The
@@@ -24,9 -25,8 +25,9 @@@ static int info_only
  static int force_remove;
  static int verbose;
  static int mark_valid_only;
 -#define MARK_VALID 1
 -#define UNMARK_VALID 2
 +static int mark_skip_worktree_only;
 +#define MARK_FLAG 1
 +#define UNMARK_FLAG 2
  
  __attribute__((format (printf, 1, 2)))
  static void report(const char *fmt, ...)
        va_end(vp);
  }
  
 -static int mark_valid(const char *path)
 +static int mark_ce_flags(const char *path, int flag, int mark)
  {
        int namelen = strlen(path);
        int pos = cache_name_pos(path, namelen);
        if (0 <= pos) {
 -              switch (mark_valid_only) {
 -              case MARK_VALID:
 -                      active_cache[pos]->ce_flags |= CE_VALID;
 -                      break;
 -              case UNMARK_VALID:
 -                      active_cache[pos]->ce_flags &= ~CE_VALID;
 -                      break;
 -              }
 +              if (mark)
 +                      active_cache[pos]->ce_flags |= flag;
 +              else
 +                      active_cache[pos]->ce_flags &= ~flag;
                cache_tree_invalidate_path(active_cache_tree, path);
                active_cache_changed = 1;
                return 0;
@@@ -173,29 -177,29 +174,29 @@@ static int process_directory(const cha
        return error("%s: is a directory - add files inside instead", path);
  }
  
 -/*
 - * Process a regular file
 - */
 -static int process_file(const char *path, int len, struct stat *st)
 -{
 -      int pos = cache_name_pos(path, len);
 -      struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
 -
 -      if (ce && S_ISGITLINK(ce->ce_mode))
 -              return error("%s is already a gitlink, not replacing", path);
 -
 -      return add_one_path(ce, path, len, st);
 -}
 -
  static int process_path(const char *path)
  {
 -      int len;
 +      int pos, len;
        struct stat st;
 +      struct cache_entry *ce;
  
        len = strlen(path);
        if (has_symlink_leading_path(path, len))
                return error("'%s' is beyond a symbolic link", path);
  
 +      pos = cache_name_pos(path, len);
 +      ce = pos < 0 ? NULL : active_cache[pos];
 +      if (ce && ce_skip_worktree(ce)) {
 +              /*
 +               * working directory version is assumed "good"
 +               * so updating it does not make sense.
 +               * On the other hand, removing it from index should work
 +               */
 +              if (allow_remove && remove_file_from_cache(path))
 +                      return error("%s: cannot remove from the index", path);
 +              return 0;
 +      }
 +
        /*
         * First things first: get the stat information, to decide
         * what to do about the pathname!
        if (S_ISDIR(st.st_mode))
                return process_directory(path, len, &st);
  
 -      return process_file(path, len, &st);
 +      /*
 +       * Process a regular file
 +       */
 +      if (ce && S_ISGITLINK(ce->ce_mode))
 +              return error("%s is already a gitlink, not replacing", path);
 +
 +      return add_one_path(ce, path, len, &st);
  }
  
  static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
@@@ -280,12 -278,7 +281,12 @@@ static void update_one(const char *path
                goto free_return;
        }
        if (mark_valid_only) {
 -              if (mark_valid(p))
 +              if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))
 +                      die("Unable to mark file %s", path);
 +              goto free_return;
 +      }
 +      if (mark_skip_worktree_only) {
 +              if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
                        die("Unable to mark file %s", path);
                goto free_return;
        }
@@@ -397,7 -390,7 +398,7 @@@ static void read_index_info(int line_te
  }
  
  static const char update_index_usage[] =
 -"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
 +"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
  
  static unsigned char head_sha1[20];
  static unsigned char merge_head_sha1[20];
@@@ -440,7 -433,18 +441,18 @@@ static int unresolve_one(const char *pa
  
        /* See if there is such entry in the index. */
        pos = cache_name_pos(path, namelen);
-       if (pos < 0) {
+       if (0 <= pos) {
+               /* already merged */
+               pos = unmerge_cache_entry_at(pos);
+               if (pos < active_nr) {
+                       struct cache_entry *ce = active_cache[pos];
+                       if (ce_stage(ce) &&
+                           ce_namelen(ce) == namelen &&
+                           !memcmp(ce->name, path, namelen))
+                               return 0;
+               }
+               /* no resolve-undo information; fall back */
+       } else {
                /* If there isn't, either it is unmerged, or
                 * resolved as "removed" by mistake.  We do not
                 * want to do anything in the former case.
@@@ -656,19 -660,11 +668,19 @@@ int cmd_update_index(int argc, const ch
                                continue;
                        }
                        if (!strcmp(path, "--assume-unchanged")) {
 -                              mark_valid_only = MARK_VALID;
 +                              mark_valid_only = MARK_FLAG;
                                continue;
                        }
                        if (!strcmp(path, "--no-assume-unchanged")) {
 -                              mark_valid_only = UNMARK_VALID;
 +                              mark_valid_only = UNMARK_FLAG;
 +                              continue;
 +                      }
 +                      if (!strcmp(path, "--no-skip-worktree")) {
 +                              mark_skip_worktree_only = UNMARK_FLAG;
 +                              continue;
 +                      }
 +                      if (!strcmp(path, "--skip-worktree")) {
 +                              mark_skip_worktree_only = MARK_FLAG;
                                continue;
                        }
                        if (!strcmp(path, "--info-only")) {
                                verbose = 1;
                                continue;
                        }
+                       if (!strcmp(path, "--clear-resolve-undo")) {
+                               resolve_undo_clear();
+                               continue;
+                       }
                        if (!strcmp(path, "-h") || !strcmp(path, "--help"))
                                usage(update_index_usage);
                        die("unknown option %s", path);
diff --combined cache.h
index bebe1a891da7afdf22431a7b8816fa729fe7eb75,97b4a74d93cb5a5ea594c478f76602065a9d5188..5abcb09fcfe8d30f87e90f390b923fc07ec8e6e3
+++ b/cache.h
@@@ -177,20 -177,15 +177,20 @@@ struct cache_entry 
  
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
 +#define CE_CONFLICTED (0x800000)
 +
 +/* Only remove in work directory, not index */
 +#define CE_WT_REMOVE (0x400000)
  
  /*
   * Extended on-disk flags
   */
  #define CE_INTENT_TO_ADD 0x20000000
 +#define CE_SKIP_WORKTREE 0x40000000
  /* CE_EXTENDED2 is for future extension */
  #define CE_EXTENDED2 0x80000000
  
 -#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
 +#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  /*
   * Safeguard to avoid saving wrong flags:
@@@ -239,7 -234,6 +239,7 @@@ static inline size_t ce_namelen(const s
                            ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 +#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@@ -288,6 -282,7 +288,7 @@@ static inline int ce_to_dtype(const str
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
+       struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
        struct cache_time timestamp;
        void *alloc;
@@@ -342,6 -337,9 +343,9 @@@ static inline void remove_name_hash(str
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
+ #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
+ #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
+ #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #endif
  
  enum object_type {
@@@ -451,6 -449,7 +455,6 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 -extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  extern int remove_index_entry_at(struct index_state *, int pos);
  extern void remove_marked_cache_entries(struct index_state *istate);
@@@ -469,9 -468,7 +473,9 @@@ extern int index_name_is_other(const st
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
  /* do not check the contents but report dirty on racily-clean entries */
 -#define CE_MATCH_RACY_IS_DIRTY        02
 +#define CE_MATCH_RACY_IS_DIRTY                02
 +/* do stat comparison even if CE_SKIP_WORKTREE is true */
 +#define CE_MATCH_IGNORE_SKIP_WORKTREE 04
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
@@@ -480,6 -477,9 +484,6 @@@ extern int index_fd(unsigned char *sha1
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
 -/* "careful lstat()" */
 -extern int check_path(const char *path, int len, struct stat *st, int skiplen);
 -
  #define REFRESH_REALLY                0x0001  /* ignore_valid */
  #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
  #define REFRESH_QUIET         0x0004  /* be quiet about it */
@@@ -533,7 -533,6 +537,7 @@@ extern int auto_crlf
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
 +extern int core_apply_sparse_checkout;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -623,6 -622,7 +627,6 @@@ static inline void hashclr(unsigned cha
  {
        memset(hash, 0, 20);
  }
 -extern int is_empty_blob_sha1(const unsigned char *sha1);
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@@@ -692,6 -692,7 +696,6 @@@ extern int has_sha1_pack(const unsigne
  extern int has_sha1_file(const unsigned char *sha1);
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern int has_pack_file(const unsigned char *sha1);
  extern int has_pack_index(const unsigned char *sha1);
  
  extern const signed char hexval_table[256];
@@@ -705,11 -706,7 +709,11 @@@ static inline unsigned int hexval(unsig
  #define DEFAULT_ABBREV 7
  
  extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
 +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
 +static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 +{
 +      return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
 +}
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
@@@ -717,7 -714,6 +721,7 @@@ extern const char *resolve_ref(const ch
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  extern int interpret_branch_name(const char *str, struct strbuf *);
 +extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
  extern const char *ref_rev_parse_rules[];
@@@ -792,6 -788,8 +796,6 @@@ extern int has_symlink_leading_path(con
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
  extern int has_symlink_or_noent_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 -extern void invalidate_lstat_cache(const char *name, int len);
 -extern void clear_lstat_cache(void);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -931,11 -929,7 +935,11 @@@ extern const char *config_exclusive_fil
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
  extern char git_default_name[MAX_GITNAME];
 +#define IDENT_NAME_GIVEN 01
 +#define IDENT_MAIL_GIVEN 02
 +#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
  extern int user_ident_explicitly_given;
 +extern int user_ident_sufficiently_given(void);
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
diff --combined read-cache.c
index f4512967b8393e7934812e667d42a97f4d67ba8c,9e0fb04075e19bab4dc70b494b7c5e45712dacef..edd995943d6741a67322e2967dcacd2121511494
  #include "diffcore.h"
  #include "revision.h"
  #include "blob.h"
+ #include "resolve-undo.h"
  
 +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 +
  /* Index extensions.
   *
   * The first letter should be 'A'..'Z' for extensions that are not
@@@ -28,6 -27,7 +29,7 @@@
  
  #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
  #define CACHE_EXT_TREE 0x54524545     /* "TREE" */
+ #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUN" */
  
  struct index_state the_index;
  
@@@ -158,7 -158,7 +160,7 @@@ static int ce_modified_check_fs(struct 
        return 0;
  }
  
 -int is_empty_blob_sha1(const unsigned char *sha1)
 +static int is_empty_blob_sha1(const unsigned char *sha1)
  {
        static const unsigned char empty_blob_sha1[20] = {
                0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
@@@ -261,17 -261,12 +263,17 @@@ int ie_match_stat(const struct index_st
  {
        unsigned int changed;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
 +      int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
  
        /*
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
 +       *
 +       * skip-worktree has the same effect with higher precedence
         */
 +      if (!ignore_skip_worktree && ce_skip_worktree(ce))
 +              return 0;
        if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
  
@@@ -456,6 -451,7 +458,7 @@@ int remove_index_entry_at(struct index_
  {
        struct cache_entry *ce = istate->cache[pos];
  
+       record_resolve_undo(istate, ce);
        remove_name_hash(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
@@@ -571,7 -567,7 +574,7 @@@ int add_to_index(struct index_state *is
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
        struct cache_entry *ce, *alias;
 -      unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
 +      unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
        int intent_only = flags & ADD_CACHE_INTENT;
@@@ -1007,20 -1003,14 +1010,20 @@@ static struct cache_entry *refresh_cach
        struct cache_entry *updated;
        int changed, size;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
 +      int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
  
        if (ce_uptodate(ce))
                return ce;
  
        /*
 -       * CE_VALID means the user promised us that the change to
 -       * the work tree does not matter and told us not to worry.
 +       * CE_VALID or CE_SKIP_WORKTREE means the user promised us
 +       * that the change to the work tree does not matter and told
 +       * us not to worry.
         */
 +      if (!ignore_skip_worktree && ce_skip_worktree(ce)) {
 +              ce_mark_uptodate(ce);
 +              return ce;
 +      }
        if (!ignore_valid && (ce->ce_flags & CE_VALID)) {
                ce_mark_uptodate(ce);
                return ce;
@@@ -1154,7 -1144,7 +1157,7 @@@ int refresh_index(struct index_state *i
        return has_errors;
  }
  
 -struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
 +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
  {
        return refresh_cache_ent(&the_index, ce, really, NULL);
  }
@@@ -1183,6 -1173,9 +1186,9 @@@ static int read_index_extension(struct 
        case CACHE_EXT_TREE:
                istate->cache_tree = cache_tree_read(data, sz);
                break;
+       case CACHE_EXT_RESOLVE_UNDO:
+               istate->resolve_undo = resolve_undo_read(data, sz);
+               break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@@ -1335,7 -1328,7 +1341,7 @@@ int read_index_from(struct index_state 
                 * extension name (4-byte) and section length
                 * in 4-byte network byte order.
                 */
 -              unsigned long extsize;
 +              uint32_t extsize;
                memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
                extsize = ntohl(extsize);
                if (read_index_extension(istate,
@@@ -1362,6 -1355,7 +1368,7 @@@ int is_index_unborn(struct index_state 
  
  int discard_index(struct index_state *istate)
  {
+       resolve_undo_clear_index(istate);
        istate->cache_nr = 0;
        istate->cache_changed = 0;
        istate->timestamp.sec = 0;
@@@ -1587,6 -1581,17 +1594,17 @@@ int write_index(struct index_state *ist
                if (err)
                        return -1;
        }
+       if (istate->resolve_undo) {
+               struct strbuf sb = STRBUF_INIT;
+               resolve_undo_write(&sb, istate->resolve_undo);
+               err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,
+                                            sb.len) < 0
+                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               if (err)
+                       return -1;
+       }
  
        if (ce_flush(&c, newfd) || fstat(newfd, &st))
                return -1;
@@@ -1619,8 -1624,9 +1637,8 @@@ int read_index_unmerged(struct index_st
                len = strlen(ce->name);
                size = cache_entry_size(len);
                new_ce = xcalloc(1, size);
 -              hashcpy(new_ce->sha1, ce->sha1);
                memcpy(new_ce->name, ce->name, len);
 -              new_ce->ce_flags = create_ce_flags(len, 0);
 +              new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
diff --combined rerere.c
index e0ac5bcaed7da9cfdff10afc9d1ebb8c27f7ba9f,d92990a6bba01c321bb67bd07287772075f6994a..70d0f7afffe983e6c70ad8494b095c1292393745
+++ b/rerere.c
@@@ -3,6 -3,9 +3,9 @@@
  #include "rerere.h"
  #include "xdiff/xdiff.h"
  #include "xdiff-interface.h"
+ #include "dir.h"
+ #include "resolve-undo.h"
+ #include "ll-merge.h"
  
  /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
  static int rerere_enabled = -1;
@@@ -83,61 -86,74 +86,74 @@@ static inline void ferr_puts(const cha
        ferr_write(s, strlen(s), fp, err);
  }
  
- static int handle_file(const char *path,
-        unsigned char *sha1, const char *output)
+ struct rerere_io {
+       int (*getline)(struct strbuf *, struct rerere_io *);
+       FILE *output;
+       int wrerror;
+       /* some more stuff */
+ };
+ static void rerere_io_putstr(const char *str, struct rerere_io *io)
+ {
+       if (io->output)
+               ferr_puts(str, io->output, &io->wrerror);
+ }
+ static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io)
+ {
+       if (io->output)
+               ferr_write(mem, sz, io->output, &io->wrerror);
+ }
+ struct rerere_io_file {
+       struct rerere_io io;
+       FILE *input;
+ };
+ static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
+ {
+       struct rerere_io_file *io = (struct rerere_io_file *)io_;
+       return strbuf_getwholeline(sb, io->input, '\n');
+ }
+ static int handle_path(unsigned char *sha1, struct rerere_io *io)
  {
        git_SHA_CTX ctx;
-       char buf[1024];
        int hunk_no = 0;
        enum {
                RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
        } hunk = RR_CONTEXT;
        struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
-       FILE *f = fopen(path, "r");
-       FILE *out = NULL;
-       int wrerror = 0;
-       if (!f)
-               return error("Could not open %s", path);
-       if (output) {
-               out = fopen(output, "w");
-               if (!out) {
-                       fclose(f);
-                       return error("Could not write %s", output);
-               }
-       }
+       struct strbuf buf = STRBUF_INIT;
  
        if (sha1)
                git_SHA1_Init(&ctx);
  
-       while (fgets(buf, sizeof(buf), f)) {
-               if (!prefixcmp(buf, "<<<<<<< ")) {
+       while (!io->getline(&buf, io)) {
+               if (!prefixcmp(buf.buf, "<<<<<<< ")) {
                        if (hunk != RR_CONTEXT)
                                goto bad;
                        hunk = RR_SIDE_1;
-               } else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) {
+               } else if (!prefixcmp(buf.buf, "|||||||") && isspace(buf.buf[7])) {
                        if (hunk != RR_SIDE_1)
                                goto bad;
                        hunk = RR_ORIGINAL;
-               } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
+               } else if (!prefixcmp(buf.buf, "=======") && isspace(buf.buf[7])) {
                        if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
                                goto bad;
                        hunk = RR_SIDE_2;
-               } else if (!prefixcmp(buf, ">>>>>>> ")) {
+               } else if (!prefixcmp(buf.buf, ">>>>>>> ")) {
                        if (hunk != RR_SIDE_2)
                                goto bad;
                        if (strbuf_cmp(&one, &two) > 0)
                                strbuf_swap(&one, &two);
                        hunk_no++;
                        hunk = RR_CONTEXT;
-                       if (out) {
-                               ferr_puts("<<<<<<<\n", out, &wrerror);
-                               ferr_write(one.buf, one.len, out, &wrerror);
-                               ferr_puts("=======\n", out, &wrerror);
-                               ferr_write(two.buf, two.len, out, &wrerror);
-                               ferr_puts(">>>>>>>\n", out, &wrerror);
-                       }
+                       rerere_io_putstr("<<<<<<<\n", io);
+                       rerere_io_putmem(one.buf, one.len, io);
+                       rerere_io_putstr("=======\n", io);
+                       rerere_io_putmem(two.buf, two.len, io);
+                       rerere_io_putstr(">>>>>>>\n", io);
                        if (sha1) {
                                git_SHA1_Update(&ctx, one.buf ? one.buf : "",
                                            one.len + 1);
                        strbuf_reset(&one);
                        strbuf_reset(&two);
                } else if (hunk == RR_SIDE_1)
-                       strbuf_addstr(&one, buf);
+                       strbuf_addstr(&one, buf.buf);
                else if (hunk == RR_ORIGINAL)
                        ; /* discard */
                else if (hunk == RR_SIDE_2)
-                       strbuf_addstr(&two, buf);
-               else if (out)
-                       ferr_puts(buf, out, &wrerror);
+                       strbuf_addstr(&two, buf.buf);
+               else
+                       rerere_io_putstr(buf.buf, io);
                continue;
        bad:
                hunk = 99; /* force error exit */
        }
        strbuf_release(&one);
        strbuf_release(&two);
+       strbuf_release(&buf);
  
-       fclose(f);
-       if (wrerror)
-               error("There were errors while writing %s (%s)",
-                     path, strerror(wrerror));
-       if (out && fclose(out))
-               wrerror = error("Failed to flush %s: %s",
-                               path, strerror(errno));
        if (sha1)
                git_SHA1_Final(sha1, &ctx);
-       if (hunk != RR_CONTEXT) {
+       if (hunk != RR_CONTEXT)
+               return -1;
+       return hunk_no;
+ }
+ static int handle_file(const char *path, unsigned char *sha1, const char *output)
+ {
+       int hunk_no = 0;
+       struct rerere_io_file io;
+       memset(&io, 0, sizeof(io));
+       io.io.getline = rerere_file_getline;
+       io.input = fopen(path, "r");
+       io.io.wrerror = 0;
+       if (!io.input)
+               return error("Could not open %s", path);
+       if (output) {
+               io.io.output = fopen(output, "w");
+               if (!io.io.output) {
+                       fclose(io.input);
+                       return error("Could not write %s", output);
+               }
+       }
+       hunk_no = handle_path(sha1, (struct rerere_io *)&io);
+       fclose(io.input);
+       if (io.io.wrerror)
+               error("There were errors while writing %s (%s)",
+                     path, strerror(io.io.wrerror));
+       if (io.io.output && fclose(io.io.output))
+               io.io.wrerror = error("Failed to flush %s: %s",
+                                     path, strerror(errno));
+       if (hunk_no < 0) {
                if (output)
                        unlink_or_warn(output);
                return error("Could not parse conflict hunks in %s", path);
        }
-       if (wrerror)
+       if (io.io.wrerror)
+               return -1;
+       return hunk_no;
+ }
+ struct rerere_io_mem {
+       struct rerere_io io;
+       struct strbuf input;
+ };
+ static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
+ {
+       struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
+       char *ep;
+       size_t len;
+       strbuf_release(sb);
+       if (!io->input.len)
+               return -1;
+       ep = strchrnul(io->input.buf, '\n');
+       if (*ep == '\n')
+               ep++;
+       len = ep - io->input.buf;
+       strbuf_add(sb, io->input.buf, len);
+       strbuf_remove(&io->input, 0, len);
+       return 0;
+ }
+ static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+ {
+       mmfile_t mmfile[3];
+       mmbuffer_t result = {NULL, 0};
+       struct cache_entry *ce;
+       int pos, len, i, hunk_no;
+       struct rerere_io_mem io;
+       /*
+        * Reproduce the conflicted merge in-core
+        */
+       len = strlen(path);
+       pos = cache_name_pos(path, len);
+       if (0 <= pos)
                return -1;
+       pos = -pos - 1;
+       for (i = 0; i < 3; i++) {
+               enum object_type type;
+               unsigned long size;
+               mmfile[i].size = 0;
+               mmfile[i].ptr = NULL;
+               if (active_nr <= pos)
+                       break;
+               ce = active_cache[pos++];
+               if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
+                   || ce_stage(ce) != i + 1)
+                       break;
+               mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
+               mmfile[i].size = size;
+       }
+       for (i = 0; i < 3; i++) {
+               if (!mmfile[i].ptr && !mmfile[i].size)
+                       mmfile[i].ptr = xstrdup("");
+       }
+       ll_merge(&result, path, &mmfile[0],
+                &mmfile[1], "ours",
+                &mmfile[2], "theirs", 0);
+       for (i = 0; i < 3; i++)
+               free(mmfile[i].ptr);
+       memset(&io, 0, sizeof(&io));
+       io.io.getline = rerere_mem_getline;
+       if (output)
+               io.io.output = fopen(output, "w");
+       else
+               io.io.output = NULL;
+       strbuf_init(&io.input, 0);
+       strbuf_attach(&io.input, result.ptr, result.size, result.size);
+       hunk_no = handle_path(sha1, (struct rerere_io *)&io);
+       strbuf_release(&io.input);
+       if (io.io.output)
+               fclose(io.io.output);
        return hunk_no;
  }
  
@@@ -367,7 -493,7 +493,7 @@@ static int is_rerere_enabled(void
        return 1;
  }
  
 -int setup_rerere(struct string_list *merge_rr)
 +int setup_rerere(struct string_list *merge_rr, int flags)
  {
        int fd;
  
        if (!is_rerere_enabled())
                return -1;
  
 +      if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
 +              rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
        merge_rr_path = git_pathdup("MERGE_RR");
        fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
                                       LOCK_DIE_ON_ERROR);
        return fd;
  }
  
 -int rerere(void)
 +int rerere(int flags)
  {
        struct string_list merge_rr = { NULL, 0, 0, 1 };
        int fd;
  
 -      fd = setup_rerere(&merge_rr);
 +      fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
        return do_plain_rerere(&merge_rr, fd);
  }
 -      fd = setup_rerere(&merge_rr);
+ static int rerere_forget_one_path(const char *path, struct string_list *rr)
+ {
+       const char *filename;
+       char *hex;
+       unsigned char sha1[20];
+       int ret;
+       ret = handle_cache(path, sha1, NULL);
+       if (ret < 1)
+               return error("Could not parse conflict hunks in '%s'", path);
+       hex = xstrdup(sha1_to_hex(sha1));
+       filename = rerere_path(hex, "postimage");
+       if (unlink(filename))
+               return (errno == ENOENT
+                       ? error("no remembered resolution for %s", path)
+                       : error("cannot unlink %s: %s", filename, strerror(errno)));
+       handle_cache(path, sha1, rerere_path(hex, "preimage"));
+       fprintf(stderr, "Updated preimage for '%s'\n", path);
+       string_list_insert(path, rr)->util = hex;
+       fprintf(stderr, "Forgot resolution for %s\n", path);
+       return 0;
+ }
+ int rerere_forget(const char **pathspec)
+ {
+       int i, fd;
+       struct string_list conflict = { NULL, 0, 0, 1 };
+       struct string_list merge_rr = { NULL, 0, 0, 1 };
+       if (read_cache() < 0)
+               return error("Could not read index");
++      fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
+       unmerge_cache(pathspec);
+       find_conflict(&conflict);
+       for (i = 0; i < conflict.nr; i++) {
+               struct string_list_item *it = &conflict.items[i];
+               if (!match_pathspec(pathspec, it->string, strlen(it->string),
+                                   0, NULL))
+                       continue;
+               rerere_forget_one_path(it->string, &merge_rr);
+       }
+       return write_rr(&merge_rr, fd);
+ }
diff --combined rerere.h
index 10a94a4ea10aa4c7a1ba4ea8397b82f1278afc9d,36560ff2f5501105049e2220f959721104fc4922..eaa9004dcdbb2f0dfd446e1ac68237c6dff18530
+++ b/rerere.h
@@@ -3,15 -3,10 +3,16 @@@
  
  #include "string-list.h"
  
 -extern int setup_rerere(struct string_list *);
 -extern int rerere(void);
 +#define RERERE_AUTOUPDATE   01
 +#define RERERE_NOAUTOUPDATE 02
 +
 +extern int setup_rerere(struct string_list *, int);
 +extern int rerere(int);
  extern const char *rerere_path(const char *hex, const char *file);
  extern int has_rerere_resolution(const char *hex);
+ extern int rerere_forget(const char **);
  
 +#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
 +      "update the index with reused conflict resolution if possible")
 +
  #endif