From: Junio C Hamano Date: Wed, 20 Jan 2010 22:44:31 +0000 (-0800) Subject: Merge branch 'jc/cache-unmerge' X-Git-Tag: v1.7.0-rc0~61 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/6751e0471df1bdc4a1d5e5a3929a531c74e95aeb?ds=inline;hp=-c Merge branch 'jc/cache-unmerge' * 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 --- 6751e0471df1bdc4a1d5e5a3929a531c74e95aeb diff --combined Makefile index cf0723ea12,762898ae7f..ad890ec925 --- a/Makefile +++ 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 @@@ -187,6 -185,10 +187,6 @@@ # 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. @@@ -218,7 -220,7 +218,7 @@@ # 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)) @@@ -1046,8 -1032,10 +1048,8 @@@ 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. @@@ -1106,10 -1091,7 +1108,10 @@@ 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"; \ @@@ -1747,54 -1693,42 +1749,54 @@@ # 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/" || \ @@@ -1890,12 -1818,6 +1892,12 @@@ 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 @@@ -1993,9 -1914,6 +1995,9 @@@ 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 e44e238c39,bdef1aa386..d0b1a728cb --- a/builtin-checkout.c +++ b/builtin-checkout.c @@@ -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] ", @@@ -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) @@@ -397,7 -403,7 +403,7 @@@ 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)); @@@ -422,13 -428,7 +428,13 @@@ 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 [] * * With no paths, if 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 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 is _not_ a commit, no -t nor -b * was given, and there is a tracking branch whose name is @@@ -725,7 -722,7 +731,7 @@@ 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 738215768e,ef3a06889a..b065061392 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@@ -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; @@@ -179,8 -214,7 +215,8 @@@ 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) { @@@ -194,8 -228,6 +230,8 @@@ 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 }, @@@ -485,9 -519,6 +523,9 @@@ 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) { @@@ -497,7 -528,7 +535,8 @@@ 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; @@@ -516,6 -547,7 +555,6 @@@ pathspec = get_pathspec(prefix, argv); /* be nice with submodule paths ending in a slash */ - read_cache(); if (pathspec) strip_trailing_slash_from_submodules(); @@@ -536,7 -568,7 +575,7 @@@ /* 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) @@@ -551,6 -583,8 +590,8 @@@ 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 6f1311414b,6bc2f7af08..9f60ffa2cd --- a/builtin-merge.c +++ b/builtin-merge.c @@@ -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 ', 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 50413ca17d,7d378b7548..5fda9905fc --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@@ -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=] [-u [--exclude-per-directory=] | -i]] [--index-output=] [ []]", + "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=] [-u [--exclude-per-directory=] | -i]] [--no-sparse-checkout] [--index-output=] [ []]", 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() }; @@@ -124,6 -123,7 +125,7 @@@ 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 5028138898,0253abf9b6..25f507a2f1 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@@ -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 a64a752990,750db163b9..3ab214d24e --- a/builtin-update-index.c +++ b/builtin-update-index.c @@@ -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, ...) @@@ -42,15 -42,19 +43,15 @@@ 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! @@@ -206,13 -210,7 +207,13 @@@ 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] [--] ..."; +"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] [--] ..."; 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")) { @@@ -719,6 -715,10 +731,10 @@@ 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 bebe1a891d,97b4a74d93..5abcb09fcf --- a/cache.h +++ 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 f4512967b8,9e0fb04075..edd995943d --- a/read-cache.c +++ b/read-cache.c @@@ -14,9 -14,8 +14,10 @@@ #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 e0ac5bcaed,d92990a6bb..70d0f7afff --- a/rerere.c +++ 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); @@@ -147,13 -163,13 +163,13 @@@ 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 */ @@@ -161,23 -177,133 +177,133 @@@ } 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; @@@ -375,8 -501,6 +501,8 @@@ 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); @@@ -384,13 -508,62 +510,62 @@@ 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); } + + 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); ++ 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 10a94a4ea1,36560ff2f5..eaa9004dcd --- a/rerere.h +++ 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