#
# 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
LIB_OBJS =
PROGRAMS =
SCRIPT_PERL =
+SCRIPT_PYTHON =
SCRIPT_SH =
TEST_PROGRAMS =
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
+ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
git-instaweb
# Empty...
# 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
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
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
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
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
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
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
# 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
# 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
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 =
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
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
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
NO_PERL=NoThanks
endif
+ifeq ($(PYTHON_PATH),)
+NO_PYTHON=NoThanks
+endif
+
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
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)
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)
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) $@ && \
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' \
%.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)
$(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)
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
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
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:
$(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
.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
#
#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>",
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);
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];
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 */
* 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 &&
#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;
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;
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)
{
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);
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;
#include "rerere.h"
#include "help.h"
#include "merge-recursive.h"
+ #include "resolve-undo.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
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 },
"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",
discard_cache();
if (read_cache() < 0)
die("failed to read the cache");
+ resolve_undo_clear();
return ret;
}
}
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;
}
}
fclose(fp);
- rerere();
+ rerere(allow_rerere_auto);
printf("Automatic merge failed; "
"fix conflicts and then commit the result.\n");
return 1;
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.
#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];
}
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
};
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];
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;
#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
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;
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,
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;
}
}
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];
/* 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.
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);
#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:
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)
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;
#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 {
#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);
/* 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);
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 */
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,
{
memset(hash, 0, 20);
}
-extern int is_empty_blob_sha1(const unsigned char *sha1);
#define EMPTY_TREE_SHA1_HEX \
"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
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];
#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);
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[];
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);
#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;
#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
#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;
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,
{
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;
{
struct cache_entry *ce = istate->cache[pos];
+ record_resolve_undo(istate, ce);
remove_name_hash(ce);
istate->cache_changed = 1;
istate->cache_nr--;
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;
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;
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);
}
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",
* 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,
int discard_index(struct index_state *istate)
{
+ resolve_undo_clear_index(istate);
istate->cache_nr = 0;
istate->cache_changed = 0;
istate->timestamp.sec = 0;
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;
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",
#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;
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;
}
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);
+ }
#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