From: Junio C Hamano Date: Fri, 2 Nov 2018 02:04:52 +0000 (+0900) Subject: Merge branch 'pk/rebase-in-c' X-Git-Tag: v2.20.0-rc0~95 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/5ae50845d8a30d7db32e139ce04b712f9deb99cd?ds=inline;hp=-c Merge branch 'pk/rebase-in-c' Rewrite of the "rebase" machinery in C. * pk/rebase-in-c: builtin/rebase: support running "git rebase " rebase: refactor common shell functions into their own file rebase: start implementing it as a builtin --- 5ae50845d8a30d7db32e139ce04b712f9deb99cd diff --combined .gitignore index 64b3377d40,824141cba1..4d5de166e8 --- a/.gitignore +++ b/.gitignore @@@ -1,6 -1,3 +1,6 @@@ +/fuzz_corpora +/fuzz-pack-headers +/fuzz-pack-idx /GIT-BUILD-OPTIONS /GIT-CFLAGS /GIT-LDFLAGS @@@ -81,6 -78,7 +81,7 @@@ /git-init-db /git-interpret-trailers /git-instaweb + /git-legacy-rebase /git-log /git-ls-files /git-ls-remote @@@ -102,9 -100,8 +103,9 @@@ /git-mergetool--lib /git-mktag /git-mktree -/git-name-rev +/git-multi-pack-index /git-mv +/git-name-rev /git-notes /git-p4 /git-pack-redundant @@@ -117,10 -114,10 +118,11 @@@ /git-pull /git-push /git-quiltimport +/git-range-diff /git-read-tree /git-rebase /git-rebase--am + /git-rebase--common /git-rebase--helper /git-rebase--interactive /git-rebase--merge @@@ -212,7 -209,6 +214,7 @@@ /config.mak.autogen /config.mak.append /configure +/.vscode/ /tags /TAGS /cscope* diff --combined Makefile index b08d5ea258,c210681a1d..95b93c709d --- a/Makefile +++ b/Makefile @@@ -400,7 -400,7 +400,7 @@@ all: # (defaults to "man") if you want to have a different default when # "git help" is called without a parameter specifying the format. # -# Define TEST_GIT_INDEX_VERSION to 2, 3 or 4 to run the test suite +# Define GIT_TEST_INDEX_VERSION to 2, 3 or 4 to run the test suite # with a different indexfile format version. If it isn't set the index # file format used is index-v[23]. # @@@ -484,11 -484,6 +484,11 @@@ # The DEVELOPER mode enables -Wextra with a few exceptions. By # setting this flag the exceptions are removed, and all of # -Wextra is used. +# +# pedantic: +# +# Enable -pedantic compilation. This also disables +# USE_PARENS_AROUND_GETTEXT_N to produce only relevant warnings. GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN @@@ -569,7 -564,7 +569,7 @@@ SPATCH = spatc export TCL_PATH TCLTK_PATH SPARSE_FLAGS = -SPATCH_FLAGS = --all-includes +SPATCH_FLAGS = --all-includes --patch . @@@ -590,8 -585,6 +590,8 @@@ XDIFF_OBJS VCSSVN_OBJS = GENERATED_H = EXTRA_CPPFLAGS = +FUZZ_OBJS = +FUZZ_PROGRAMS = LIB_OBJS = PROGRAM_OBJS = PROGRAMS = @@@ -616,7 -609,7 +616,7 @@@ SCRIPT_SH += git-merge-one-file.s SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-quiltimport.sh - SCRIPT_SH += git-rebase.sh + SCRIPT_SH += git-legacy-rebase.sh SCRIPT_SH += git-remote-testgit.sh SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-stash.sh @@@ -626,6 -619,7 +626,7 @@@ SCRIPT_SH += git-web--browse.s SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--am + SCRIPT_LIB += git-rebase--common SCRIPT_LIB += git-rebase--interactive SCRIPT_LIB += git-rebase--preserve-merges SCRIPT_LIB += git-rebase--merge @@@ -684,14 -678,6 +685,14 @@@ SCRIPTS = $(SCRIPT_SH_INS) ETAGS_TARGET = TAGS +FUZZ_OBJS += fuzz-pack-headers.o +FUZZ_OBJS += fuzz-pack-idx.o + +# Always build fuzz objects even if not testing, to prevent bit-rot. +all:: $(FUZZ_OBJS) + +FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS)) + # Empty... EXTRA_PROGRAMS = @@@ -719,34 -705,27 +720,34 @@@ TEST_BUILTINS_OBJS += test-date. TEST_BUILTINS_OBJS += test-delta.o TEST_BUILTINS_OBJS += test-drop-caches.o TEST_BUILTINS_OBJS += test-dump-cache-tree.o +TEST_BUILTINS_OBJS += test-dump-fsmonitor.o TEST_BUILTINS_OBJS += test-dump-split-index.o +TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-hashmap.o TEST_BUILTINS_OBJS += test-index-version.o +TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o TEST_BUILTINS_OBJS += test-match-trees.o TEST_BUILTINS_OBJS += test-mergesort.o TEST_BUILTINS_OBJS += test-mktemp.o TEST_BUILTINS_OBJS += test-online-cpus.o +TEST_BUILTINS_OBJS += test-parse-options.o TEST_BUILTINS_OBJS += test-path-utils.o +TEST_BUILTINS_OBJS += test-pkt-line.o TEST_BUILTINS_OBJS += test-prio-queue.o +TEST_BUILTINS_OBJS += test-reach.o TEST_BUILTINS_OBJS += test-read-cache.o +TEST_BUILTINS_OBJS += test-read-midx.o TEST_BUILTINS_OBJS += test-ref-store.o TEST_BUILTINS_OBJS += test-regex.o TEST_BUILTINS_OBJS += test-repository.o TEST_BUILTINS_OBJS += test-revision-walking.o TEST_BUILTINS_OBJS += test-run-command.o TEST_BUILTINS_OBJS += test-scrap-cache-tree.o -TEST_BUILTINS_OBJS += test-sha1-array.o TEST_BUILTINS_OBJS += test-sha1.o +TEST_BUILTINS_OBJS += test-sha1-array.o TEST_BUILTINS_OBJS += test-sigchain.o TEST_BUILTINS_OBJS += test-strcmp-offset.o TEST_BUILTINS_OBJS += test-string-list.o @@@ -754,13 -733,14 +755,13 @@@ TEST_BUILTINS_OBJS += test-submodule-co TEST_BUILTINS_OBJS += test-subprocess.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o TEST_BUILTINS_OBJS += test-wildmatch.o +TEST_BUILTINS_OBJS += test-windows-named-pipe.o TEST_BUILTINS_OBJS += test-write-cache.o -TEST_PROGRAMS_NEED_X += test-dump-fsmonitor -TEST_PROGRAMS_NEED_X += test-dump-untracked-cache +# Do not add more tests here unless they have extra dependencies. Add +# them in TEST_BUILTINS_OBJS above. TEST_PROGRAMS_NEED_X += test-fake-ssh TEST_PROGRAMS_NEED_X += test-line-buffer -TEST_PROGRAMS_NEED_X += test-parse-options -TEST_PROGRAMS_NEED_X += test-pkt-line TEST_PROGRAMS_NEED_X += test-svn-fe TEST_PROGRAMS_NEED_X += test-tool @@@ -850,7 -830,6 +851,7 @@@ LIB_OBJS += column. LIB_OBJS += combine-diff.o LIB_OBJS += commit.o LIB_OBJS += commit-graph.o +LIB_OBJS += commit-reach.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o LIB_OBJS += config.o @@@ -863,7 -842,6 +864,7 @@@ LIB_OBJS += csum-file. LIB_OBJS += ctype.o LIB_OBJS += date.o LIB_OBJS += decorate.o +LIB_OBJS += delta-islands.o LIB_OBJS += diffcore-break.o LIB_OBJS += diffcore-delta.o LIB_OBJS += diffcore-order.o @@@ -893,12 -871,9 +894,12 @@@ LIB_OBJS += gpg-interface. LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hashmap.o +LIB_OBJS += linear-assignment.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o +LIB_OBJS += interdiff.o +LIB_OBJS += json-writer.o LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o LIB_OBJS += line-log.o @@@ -918,7 -893,6 +919,7 @@@ LIB_OBJS += merge. LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o LIB_OBJS += mergesort.o +LIB_OBJS += midx.o LIB_OBJS += name-hash.o LIB_OBJS += negotiator/default.o LIB_OBJS += negotiator/skipping.o @@@ -951,7 -925,6 +952,7 @@@ LIB_OBJS += progress. LIB_OBJS += prompt.o LIB_OBJS += protocol.o LIB_OBJS += quote.o +LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o LIB_OBJS += reflog-walk.o @@@ -1079,7 -1052,6 +1080,7 @@@ BUILTIN_OBJS += builtin/merge-recursive BUILTIN_OBJS += builtin/merge-tree.o BUILTIN_OBJS += builtin/mktag.o BUILTIN_OBJS += builtin/mktree.o +BUILTIN_OBJS += builtin/multi-pack-index.o BUILTIN_OBJS += builtin/mv.o BUILTIN_OBJS += builtin/name-rev.o BUILTIN_OBJS += builtin/notes.o @@@ -1091,8 -1063,8 +1092,9 @@@ BUILTIN_OBJS += builtin/prune-packed. BUILTIN_OBJS += builtin/prune.o BUILTIN_OBJS += builtin/pull.o BUILTIN_OBJS += builtin/push.o +BUILTIN_OBJS += builtin/range-diff.o BUILTIN_OBJS += builtin/read-tree.o + BUILTIN_OBJS += builtin/rebase.o BUILTIN_OBJS += builtin/rebase--helper.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o @@@ -1806,7 -1778,6 +1808,7 @@@ ifndef QUIET_MSGFMT = @echo ' ' MSGFMT $@; QUIET_GCOV = @echo ' ' GCOV $@; QUIET_SP = @echo ' ' SP $<; + QUIET_HDR = @echo ' ' HDR $<; QUIET_RC = @echo ' ' RC $@; QUIET_SUBDIR0 = +@subdir= QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ @@@ -2068,7 -2039,7 +2070,7 @@@ $(BUILT_INS): git$ command-list.h: generate-cmdlist.sh command-list.txt -command-list.h: $(wildcard Documentation/git*.txt) +command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@ SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\ @@@ -2263,7 -2234,6 +2265,7 @@@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_ OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(VCSSVN_OBJS) \ + $(FUZZ_OBJS) \ common-main.o \ git.o ifndef NO_CURL @@@ -2621,8 -2591,8 +2623,8 @@@ endi ifdef GIT_INTEROP_MAKE_OPTS @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+ endif -ifdef TEST_GIT_INDEX_VERSION - @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+ +ifdef GIT_TEST_INDEX_VERSION + @echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+ endif @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi @@@ -2690,17 -2660,6 +2692,17 @@@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORC .PHONY: sparse $(SP_OBJ) sparse: $(SP_OBJ) +GEN_HDRS := command-list.h unicode-width.h +EXCEPT_HDRS := $(GEN_HDRS) compat% xdiff% +CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(patsubst ./%,%,$(LIB_H))) +HCO = $(patsubst %.h,%.hco,$(CHK_HDRS)) + +$(HCO): %.hco: %.h FORCE + $(QUIET_HDR)$(CC) -include git-compat-util.h -I. -o /dev/null -c -xc $< + +.PHONY: hdr-check $(HCO) +hdr-check: $(HCO) + .PHONY: style style: git clang-format --style file --diff --extensions c,h @@@ -2716,16 -2675,10 +2718,16 @@@ check: command-list. fi C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ)) -%.cocci.patch: %.cocci $(C_SOURCES) +ifdef DC_SHA1_SUBMODULE +COCCI_SOURCES = $(filter-out sha1collisiondetection/%,$(C_SOURCES)) +else +COCCI_SOURCES = $(filter-out sha1dc/%,$(C_SOURCES)) +endif + +%.cocci.patch: %.cocci $(COCCI_SOURCES) @echo ' ' SPATCH $<; \ ret=0; \ - for f in $(C_SOURCES); do \ + for f in $(COCCI_SOURCES); do \ $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \ { ret=$$?; break; }; \ done >$@+ 2>$@.log; \ @@@ -2739,9 -2692,7 +2741,9 @@@ then \ echo ' ' SPATCH result: $@; \ fi -coccicheck: $(patsubst %.cocci,%.cocci.patch,$(wildcard contrib/coccinelle/*.cocci)) +coccicheck: $(addsuffix .patch,$(wildcard contrib/coccinelle/*.cocci)) + +.PHONY: coccicheck ### Installation rules @@@ -2953,22 -2904,19 +2955,22 @@@ profile-clean $(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs))) $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs))) -clean: profile-clean coverage-clean +cocciclean: + $(RM) contrib/coccinelle/*.cocci.patch* + +clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(NO_INSTALL) + $(RM) $(FUZZ_PROGRAMS) $(RM) -r bin-wrappers $(dep_dirs) $(RM) -r po/build/ $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope* $(RM) -r $(GIT_TARNAME) .doc-tmp-dir $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz $(RM) $(htmldocs).tar.gz $(manpages).tar.gz - $(RM) contrib/coccinelle/*.cocci.patch* $(MAKE) -C Documentation/ clean ifndef NO_PERL $(MAKE) -C gitweb clean @@@ -2984,7 -2932,7 +2986,7 @@@ endi $(RM) GIT-USER-AGENT GIT-PREFIX $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS -.PHONY: all install profile-clean clean strip +.PHONY: all install profile-clean cocciclean clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: FORCE cscope @@@ -3087,24 -3035,3 +3089,24 @@@ cover_db: coverage-repor cover_db_html: cover_db cover -report html -outputdir cover_db_html cover_db + +### Fuzz testing +# +# Building fuzz targets generally requires a special set of compiler flags that +# are not necessarily appropriate for general builds, and that vary greatly +# depending on the compiler version used. +# +# An example command to build against libFuzzer from LLVM 4.0.0: +# +# make CC=clang CXX=clang++ \ +# CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \ +# LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \ +# fuzz-all +# +.PHONY: fuzz-all + +$(FUZZ_PROGRAMS): all + $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \ + $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@ + +fuzz-all: $(FUZZ_PROGRAMS) diff --combined builtin.h index 962f0489ab,44651a447f..6fb66f5ba4 --- a/builtin.h +++ b/builtin.h @@@ -191,7 -191,6 +191,7 @@@ extern int cmd_merge_recursive(int argc extern int cmd_merge_tree(int argc, const char **argv, const char *prefix); extern int cmd_mktag(int argc, const char **argv, const char *prefix); extern int cmd_mktree(int argc, const char **argv, const char *prefix); +extern int cmd_multi_pack_index(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_notes(int argc, const char **argv, const char *prefix); @@@ -202,8 -201,8 +202,9 @@@ extern int cmd_prune(int argc, const ch extern int cmd_prune_packed(int argc, const char **argv, const char *prefix); extern int cmd_pull(int argc, const char **argv, const char *prefix); extern int cmd_push(int argc, const char **argv, const char *prefix); +extern int cmd_range_diff(int argc, const char **argv, const char *prefix); extern int cmd_read_tree(int argc, const char **argv, const char *prefix); + extern int cmd_rebase(int argc, const char **argv, const char *prefix); extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix); extern int cmd_receive_pack(int argc, const char **argv, const char *prefix); extern int cmd_reflog(int argc, const char **argv, const char *prefix); diff --combined git-legacy-rebase.sh index 0000000000,af2cdfef03..5d92648014 mode 000000,100755..100755 --- a/git-legacy-rebase.sh +++ b/git-legacy-rebase.sh @@@ -1,0 -1,708 +1,708 @@@ + #!/bin/sh + # + # Copyright (c) 2005 Junio C Hamano. + # + + SUBDIRECTORY_OK=Yes + OPTIONS_KEEPDASHDASH= + OPTIONS_STUCKLONG=t + OPTIONS_SPEC="\ + git rebase [-i] [options] [--exec ] [--onto ] [] [] + git rebase [-i] [options] [--exec ] [--onto ] --root [] + git rebase --continue | --abort | --skip | --edit-todo + -- + Available options are + v,verbose! display a diffstat of what changed upstream + q,quiet! be quiet. implies --no-stat + autostash automatically stash/stash pop before and after + fork-point use 'merge-base --fork-point' to refine upstream + onto=! rebase onto given branch instead of upstream + r,rebase-merges? try to rebase merges instead of skipping them + p,preserve-merges! try to recreate merges instead of ignoring them + s,strategy=! use the given merge strategy + X,strategy-option=! pass the argument through to the merge strategy + no-ff! cherry-pick all commits, even if unchanged + f,force-rebase! cherry-pick all commits, even if unchanged + m,merge! use merging strategies to rebase + i,interactive! let the user edit the list of commits to rebase + x,exec=! add exec lines after each commit of the editable list + k,keep-empty preserve empty commits during rebase + allow-empty-message allow rebasing commits with empty messages + stat! display a diffstat of what changed upstream + n,no-stat! do not show diffstat of what changed upstream + verify allow pre-rebase hook to run + rerere-autoupdate allow rerere to update index with resolved conflicts + root! rebase all reachable commits up to the root(s) + autosquash move commits that begin with squash!/fixup! under -i + signoff add a Signed-off-by: line to each commit + committer-date-is-author-date! passed to 'git am' + ignore-date! passed to 'git am' + whitespace=! passed to 'git apply' + ignore-whitespace! passed to 'git apply' + C=! passed to 'git apply' + S,gpg-sign? GPG-sign commits + Actions: + continue! continue + abort! abort and check out the original branch + skip! skip current patch and continue + edit-todo! edit the todo list during an interactive rebase + quit! abort but keep HEAD where it is + show-current-patch! show the patch file being applied or merged + " + . git-sh-setup + set_reflog_action rebase + require_work_tree_exists + cd_to_toplevel + + LF=' + ' + ok_to_skip_pre_rebase= + + squash_onto= + unset onto + unset restrict_revision + cmd= + strategy= + strategy_opts= + do_merge= + merge_dir="$GIT_DIR"/rebase-merge + apply_dir="$GIT_DIR"/rebase-apply + verbose= + diffstat= + test "$(git config --bool rebase.stat)" = true && diffstat=t + autostash="$(git config --bool rebase.autostash || echo false)" + fork_point=auto + git_am_opt= + git_format_patch_opt= + rebase_root= + force_rebase= + allow_rerere_autoupdate= + # Non-empty if a rebase was in progress when 'git rebase' was invoked + in_progress= + # One of {am, merge, interactive} + type= + # One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge} + state_dir= + # One of {'', continue, skip, abort}, as parsed from command line + action= + rebase_merges= + rebase_cousins= + preserve_merges= + autosquash= + keep_empty= + allow_empty_message=--allow-empty-message + signoff= + test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t + case "$(git config --bool commit.gpgsign)" in + true) gpg_sign_opt=-S ;; + *) gpg_sign_opt= ;; + esac + . git-rebase--common + + read_basic_state () { + test -f "$state_dir/head-name" && + test -f "$state_dir/onto" && + head_name=$(cat "$state_dir"/head-name) && + onto=$(cat "$state_dir"/onto) && + # We always write to orig-head, but interactive rebase used to write to + # head. Fall back to reading from head to cover for the case that the + # user upgraded git with an ongoing interactive rebase. + if test -f "$state_dir"/orig-head + then + orig_head=$(cat "$state_dir"/orig-head) + else + orig_head=$(cat "$state_dir"/head) + fi && + GIT_QUIET=$(cat "$state_dir"/quiet) && + test -f "$state_dir"/verbose && verbose=t + test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)" + test -f "$state_dir"/strategy_opts && + strategy_opts="$(cat "$state_dir"/strategy_opts)" + test -f "$state_dir"/allow_rerere_autoupdate && + allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)" + test -f "$state_dir"/gpg_sign_opt && + gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)" + test -f "$state_dir"/signoff && { + signoff="$(cat "$state_dir"/signoff)" + force_rebase=t + } + } + + finish_rebase () { + rm -f "$(git rev-parse --git-path REBASE_HEAD)" + apply_autostash && + { git gc --auto || true; } && + rm -rf "$state_dir" + } + + run_specific_rebase () { + if [ "$interactive_rebase" = implied ]; then + GIT_EDITOR=: + export GIT_EDITOR + autosquash= + fi + . git-rebase--$type + + if test -z "$preserve_merges" + then + git_rebase__$type + else + git_rebase__preserve_merges + fi + + ret=$? + if test $ret -eq 0 + then + finish_rebase + elif test $ret -eq 2 # special exit status for rebase -i + then + apply_autostash && + rm -rf "$state_dir" && + die "Nothing to do" + fi + exit $ret + } + + run_pre_rebase_hook () { + if test -z "$ok_to_skip_pre_rebase" && + test -x "$(git rev-parse --git-path hooks/pre-rebase)" + then + "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} || + die "$(gettext "The pre-rebase hook refused to rebase.")" + fi + } + + test -f "$apply_dir"/applying && + die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")" + + if test -d "$apply_dir" + then + type=am + state_dir="$apply_dir" + elif test -d "$merge_dir" + then + if test -d "$merge_dir"/rewritten + then + type=preserve-merges + interactive_rebase=explicit + preserve_merges=t + elif test -f "$merge_dir"/interactive + then + type=interactive + interactive_rebase=explicit + else + type=merge + fi + state_dir="$merge_dir" + fi + test -n "$type" && in_progress=t + + total_argc=$# + while test $# != 0 + do + case "$1" in + --no-verify) + ok_to_skip_pre_rebase=yes + ;; + --verify) + ok_to_skip_pre_rebase= + ;; + --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch) + test $total_argc -eq 2 || usage + action=${1##--} + ;; + --onto=*) + onto="${1#--onto=}" + ;; + --exec=*) + cmd="${cmd}exec ${1#--exec=}${LF}" + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --interactive) + interactive_rebase=explicit + ;; + --keep-empty) + keep_empty=yes + ;; + --allow-empty-message) + allow_empty_message=--allow-empty-message + ;; + --no-keep-empty) + keep_empty= + ;; + --rebase-merges) + rebase_merges=t + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --rebase-merges=*) + rebase_merges=t + case "${1#*=}" in + rebase-cousins) rebase_cousins=t;; + no-rebase-cousins) rebase_cousins=;; + *) die "Unknown mode: $1";; + esac + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --preserve-merges) + preserve_merges=t + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --autosquash) + autosquash=t + ;; + --no-autosquash) + autosquash= + ;; + --fork-point) + fork_point=t + ;; + --no-fork-point) + fork_point= + ;; + --merge) + do_merge=t + ;; + --strategy-option=*) + strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)" + do_merge=t + test -z "$strategy" && strategy=recursive + ;; + --strategy=*) + strategy="${1#--strategy=}" + do_merge=t + ;; + --no-stat) + diffstat= + ;; + --stat) + diffstat=t + ;; + --autostash) + autostash=true + ;; + --no-autostash) + autostash=false + ;; + --verbose) + verbose=t + diffstat=t + GIT_QUIET= + ;; + --quiet) + GIT_QUIET=t + git_am_opt="$git_am_opt -q" + verbose= + diffstat= + ;; + --whitespace=*) + git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}" + case "${1#--whitespace=}" in + fix|strip) + force_rebase=t + ;; + esac + ;; + --ignore-whitespace) + git_am_opt="$git_am_opt $1" + ;; + --signoff) + signoff=--signoff + ;; + --no-signoff) + signoff= + ;; + --committer-date-is-author-date|--ignore-date) + git_am_opt="$git_am_opt $1" + force_rebase=t + ;; + -C*) + git_am_opt="$git_am_opt $1" + ;; + --root) + rebase_root=t + ;; + --force-rebase|--no-ff) + force_rebase=t + ;; + --rerere-autoupdate|--no-rerere-autoupdate) + allow_rerere_autoupdate="$1" + ;; + --gpg-sign) + gpg_sign_opt=-S + ;; + --gpg-sign=*) + gpg_sign_opt="-S${1#--gpg-sign=}" + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift + done + test $# -gt 2 && usage + + if test -n "$action" + then + test -z "$in_progress" && die "$(gettext "No rebase in progress?")" + # Only interactive rebase uses detailed reflog messages + if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase + then + GIT_REFLOG_ACTION="rebase -i ($action)" + export GIT_REFLOG_ACTION + fi + fi + + if test "$action" = "edit-todo" && test -z "$interactive_rebase" + then + die "$(gettext "The --edit-todo action can only be used during interactive rebase.")" + fi + + case "$action" in + continue) + # Sanity check + git rev-parse --verify HEAD >/dev/null || + die "$(gettext "Cannot read HEAD")" + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules || { + echo "$(gettext "You must edit all merge conflicts and then + mark them as resolved using git add")" + exit 1 + } + read_basic_state + run_specific_rebase + ;; + skip) + output git reset --hard HEAD || exit $? + read_basic_state + run_specific_rebase + ;; + abort) + git rerere clear + read_basic_state + case "$head_name" in + refs/*) + git symbolic-ref -m "rebase: aborting" HEAD $head_name || + die "$(eval_gettext "Could not move back to \$head_name")" + ;; + esac + output git reset --hard $orig_head + finish_rebase + exit + ;; + quit) + exec rm -rf "$state_dir" + ;; + edit-todo) + run_specific_rebase + ;; + show-current-patch) + run_specific_rebase + die "BUG: run_specific_rebase is not supposed to return here" + ;; + esac + + # Make sure no rebase is in progress + if test -n "$in_progress" + then + state_dir_base=${state_dir##*/} + cmd_live_rebase="git rebase (--continue | --abort | --skip)" + cmd_clear_stale_rebase="rm -fr \"$state_dir\"" + die " + $(eval_gettext 'It seems that there is already a $state_dir_base directory, and + I wonder if you are in the middle of another rebase. If that is the + case, please try + $cmd_live_rebase + If that is not the case, please + $cmd_clear_stale_rebase + and run me again. I am stopping in case you still have something + valuable there.')" + fi + + if test -n "$rebase_root" && test -z "$onto" + then + test -z "$interactive_rebase" && interactive_rebase=implied + fi + + if test -n "$keep_empty" + then + test -z "$interactive_rebase" && interactive_rebase=implied + fi + + if test -n "$interactive_rebase" + then + if test -z "$preserve_merges" + then + type=interactive + else + type=preserve-merges + fi + + state_dir="$merge_dir" + elif test -n "$do_merge" + then + type=merge + state_dir="$merge_dir" + else + type=am + state_dir="$apply_dir" + fi + + if test -t 2 && test -z "$GIT_QUIET" + then + git_format_patch_opt="$git_format_patch_opt --progress" + fi + + if test -n "$git_am_opt"; then + incompatible_opts=$(echo " $git_am_opt " | \ + sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/') + if test -n "$interactive_rebase" + then + if test -n "$incompatible_opts" + then + die "$(gettext "error: cannot combine interactive options (--interactive, --exec, --rebase-merges, --preserve-merges, --keep-empty, --root + --onto) with am options ($incompatible_opts)")" + fi + fi + if test -n "$do_merge"; then + if test -n "$incompatible_opts" + then + die "$(gettext "error: cannot combine merge options (--merge, --strategy, --strategy-option) with am options ($incompatible_opts)")" + fi + fi + fi + + if test -n "$signoff" + then + test -n "$preserve_merges" && + die "$(gettext "error: cannot combine '--signoff' with '--preserve-merges'")" + git_am_opt="$git_am_opt $signoff" + force_rebase=t + fi + + if test -n "$preserve_merges" + then + # Note: incompatibility with --signoff handled in signoff block above + # Note: incompatibility with --interactive is just a strong warning; + # git-rebase.txt caveats with "unless you know what you are doing" + test -n "$rebase_merges" && - die "$(gettext "error: cannot combine '--preserve_merges' with '--rebase-merges'")" ++ die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")" + fi + + if test -n "$rebase_merges" + then + test -n "$strategy_opts" && - die "$(gettext "error: cannot combine '--rebase_merges' with '--strategy-option'")" ++ die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy-option'")" + test -n "$strategy" && - die "$(gettext "error: cannot combine '--rebase_merges' with '--strategy'")" ++ die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy'")" + fi + + if test -z "$rebase_root" + then + case "$#" in + 0) + if ! upstream_name=$(git rev-parse --symbolic-full-name \ + --verify -q @{upstream} 2>/dev/null) + then + . git-parse-remote + error_on_missing_default_upstream "rebase" "rebase" \ + "against" "git rebase $(gettext '')" + fi + + test "$fork_point" = auto && fork_point=t + ;; + *) upstream_name="$1" + if test "$upstream_name" = "-" + then + upstream_name="@{-1}" + fi + shift + ;; + esac + upstream=$(peel_committish "${upstream_name}") || + die "$(eval_gettext "invalid upstream '\$upstream_name'")" + upstream_arg="$upstream_name" + else + if test -z "$onto" + then + empty_tree=$(git hash-object -t tree /dev/null) + onto=$(git commit-tree $empty_tree or "detached HEAD" + switch_to= + case "$#" in + 1) + # Is it "rebase other $branchname" or "rebase other $commit"? + branch_name="$1" + switch_to="$1" + + # Is it a local branch? + if git show-ref --verify --quiet -- "refs/heads/$branch_name" && + orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name") + then + head_name="refs/heads/$branch_name" + # If not is it a valid ref (branch or commit)? + elif orig_head=$(git rev-parse -q --verify "$branch_name") + then + head_name="detached HEAD" + + else + die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")" + fi + ;; + 0) + # Do not need to switch branches, we are already on it. + if branch_name=$(git symbolic-ref -q HEAD) + then + head_name=$branch_name + branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)') + else + head_name="detached HEAD" + branch_name=HEAD + fi + orig_head=$(git rev-parse --verify HEAD) || exit + ;; + *) + die "BUG: unexpected number of arguments left to parse" + ;; + esac + + if test "$fork_point" = t + then + new_upstream=$(git merge-base --fork-point "$upstream_name" \ + "${switch_to:-HEAD}") + if test -n "$new_upstream" + then + restrict_revision=$new_upstream + fi + fi + + if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null + then + stash_sha1=$(git stash create "autostash") || + die "$(gettext 'Cannot autostash')" + + mkdir -p "$state_dir" && + echo $stash_sha1 >"$state_dir/autostash" && + stash_abbrev=$(git rev-parse --short $stash_sha1) && + echo "$(eval_gettext 'Created autostash: $stash_abbrev')" && + git reset --hard + fi + + require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")" + + # Now we are rebasing commits $upstream..$orig_head (or with --root, + # everything leading up to $orig_head) on top of $onto + + # Check if we are already based on $onto with linear history, + # but this should be done only when upstream and onto are the same + # and if this is not an interactive rebase. + mb=$(git merge-base "$onto" "$orig_head") + if test -z "$interactive_rebase" && test "$upstream" = "$onto" && + test "$mb" = "$onto" && test -z "$restrict_revision" && + # linear history? + ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null + then + if test -z "$force_rebase" + then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \ + git checkout -q "$switch_to" -- + if test "$branch_name" = "HEAD" && + ! git symbolic-ref -q HEAD + then + say "$(eval_gettext "HEAD is up to date.")" + else + say "$(eval_gettext "Current branch \$branch_name is up to date.")" + fi + finish_rebase + exit 0 + else + if test "$branch_name" = "HEAD" && + ! git symbolic-ref -q HEAD + then + say "$(eval_gettext "HEAD is up to date, rebase forced.")" + else + say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")" + fi + fi + fi + + # If a hook exists, give it a chance to interrupt + run_pre_rebase_hook "$upstream_arg" "$@" + + if test -n "$diffstat" + then + if test -n "$verbose" + then + echo "$(eval_gettext "Changes from \$mb to \$onto:")" + fi + # We want color (if set), but no pager + GIT_PAGER='' git diff --stat --summary "$mb" "$onto" + fi + + test -n "$interactive_rebase" && run_specific_rebase + + # Detach HEAD and reset the tree + say "$(gettext "First, rewinding head to replay your work on top of it...")" + + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \ + git checkout -q "$onto^0" || die "could not detach HEAD" + git update-ref ORIG_HEAD $orig_head + + # If the $onto is a proper descendant of the tip of the branch, then + # we just fast-forwarded. + if test "$mb" = "$orig_head" + then + say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")" + move_to_original_branch + finish_rebase + exit 0 + fi + + if test -n "$rebase_root" + then + revisions="$onto..$orig_head" + else + revisions="${restrict_revision-$upstream}..$orig_head" + fi + + run_specific_rebase diff --combined git.c index adac132956,2c6b188c77..8e52276831 --- a/git.c +++ b/git.c @@@ -164,7 -164,7 +164,7 @@@ static int handle_options(const char ** if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-replace-objects")) { - check_replace_refs = 0; + read_replace_refs = 0; setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; @@@ -318,9 -318,6 +318,9 @@@ static int handle_alias(int *argcp, con alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { + if (*argcp > 1 && !strcmp((*argv)[1], "-h")) + fprintf_ln(stderr, _("'%s' is aliased to '%s'"), + alias_command, alias_string); if (alias_string[0] == '!') { struct child_process child = CHILD_PROCESS_INIT; int nongit_ok; @@@ -511,7 -508,6 +511,7 @@@ static struct cmd_struct commands[] = { "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT }, { "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT }, { "mktree", cmd_mktree, RUN_SETUP }, + { "multi-pack-index", cmd_multi_pack_index, RUN_SETUP_GENTLY }, { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "notes", cmd_notes, RUN_SETUP }, @@@ -524,8 -520,13 +524,14 @@@ { "prune-packed", cmd_prune_packed, RUN_SETUP }, { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE }, { "push", cmd_push, RUN_SETUP }, + { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER }, { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX}, + /* + * NEEDSWORK: Until the rebase is independent and needs no redirection + * to rebase shell script this is kept as is, then should be changed to + * RUN_SETUP | NEED_WORK_TREE + */ + { "rebase", cmd_rebase }, { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE }, { "receive-pack", cmd_receive_pack }, { "reflog", cmd_reflog, RUN_SETUP }, @@@ -678,8 -679,6 +684,8 @@@ static void execv_dashed_external(cons static int run_argv(int *argcp, const char ***argv) { int done_alias = 0; + struct string_list cmd_list = STRING_LIST_INIT_NODUP; + struct string_list_item *seen; while (1) { /* @@@ -697,37 -696,17 +703,37 @@@ /* .. then try the external ones */ execv_dashed_external(*argv); - /* It could be an alias -- this works around the insanity + seen = unsorted_string_list_lookup(&cmd_list, *argv[0]); + if (seen) { + int i; + struct strbuf sb = STRBUF_INIT; + for (i = 0; i < cmd_list.nr; i++) { + struct string_list_item *item = &cmd_list.items[i]; + + strbuf_addf(&sb, "\n %s", item->string); + if (item == seen) + strbuf_addstr(&sb, " <=="); + else if (i == cmd_list.nr - 1) + strbuf_addstr(&sb, " ==>"); + } + die(_("alias loop detected: expansion of '%s' does" + " not terminate:%s"), cmd_list.items[0].string, sb.buf); + } + + string_list_append(&cmd_list, *argv[0]); + + /* + * It could be an alias -- this works around the insanity * of overriding "git log" with "git show" by having * alias.log = show */ - if (done_alias) - break; if (!handle_alias(argcp, argv)) break; done_alias = 1; } + string_list_clear(&cmd_list, 0); + return done_alias; }