From: Junio C Hamano Date: Fri, 9 Dec 2011 21:37:05 +0000 (-0800) Subject: Merge branch 'jc/request-pull-show-head-4' X-Git-Tag: v1.7.9-rc0~84 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/a4043aeafed7962716a513e38ca1d5e192e7e831?hp=-c Merge branch 'jc/request-pull-show-head-4' * jc/request-pull-show-head-4: request-pull: use the annotated tag contents fmt-merge-msg.c: Fix an "dubious one-bit signed bitfield" sparse error environment.c: Fix an sparse "symbol not declared" warning builtin/log.c: Fix an "Using plain integer as NULL pointer" warning fmt-merge-msg: use branch.$name.description request-pull: use the branch description request-pull: state what commit to expect request-pull: modernize style branch: teach --edit-description option format-patch: use branch description in cover letter branch: add read_branch_desc() helper function Conflicts: builtin/branch.c --- a4043aeafed7962716a513e38ca1d5e192e7e831 diff --combined Documentation/git-branch.txt index f46013c91f,8871a4ec21..0427e80a35 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@@ -9,22 -9,19 +9,23 @@@ SYNOPSI -------- [verse] 'git branch' [--color[=] | --no-color] [-r | -a] - [-v [--abbrev= | --no-abbrev]] - [(--merged | --no-merged | --contains) []] + [--list] [-v [--abbrev= | --no-abbrev]] + [(--merged | --no-merged | --contains) []] [...] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] [] 'git branch' (-m | -M) [] 'git branch' (-d | -D) [-r] ... + 'git branch' --edit-description [] DESCRIPTION ----------- With no arguments, existing branches are listed and the current branch will be highlighted with an asterisk. Option `-r` causes the remote-tracking -branches to be listed, and option `-a` shows both. +branches to be listed, and option `-a` shows both. This list mode is also +activated by the `--list` option (see below). + restricts the output to matching branches, the pattern is a shell +wildcard (i.e., matched using fnmatch(3)) +Multiple patterns may be given; if any of them matches, the tag is shown. With `--contains`, shows only the branches that contain the named commit (in other words, the branches whose tip commits are descendants of the @@@ -68,7 -65,6 +69,7 @@@ way to clean up all obsolete remote-tra OPTIONS ------- -d:: +--delete:: Delete a branch. The branch must be fully merged in its upstream branch, or in `HEAD` if no upstream was set with `--track` or `--set-upstream`. @@@ -77,7 -73,6 +78,7 @@@ Delete a branch irrespective of its merged status. -l:: +--create-reflog:: Create the branch's reflog. This activates recording of all changes made to the branch ref, enabling use of date based sha1 expressions such as "@\{yesterday}". @@@ -90,7 -85,6 +91,7 @@@ already. Without `-f` 'git branch' refuses to change an existing branch. -m:: +--move:: Move/rename a branch and the corresponding reflog. -M:: @@@ -107,21 -101,14 +108,21 @@@ Same as `--color=never`. -r:: +--remotes:: List or delete (if used with -d) the remote-tracking branches. -a:: +--all:: List both remote-tracking branches and local branches. +--list:: + Activate the list mode. `git branch ` would try to create a branch, + use `git branch --list ` to list matching branches. + -v:: --verbose:: - Show sha1 and commit subject line for each head, along with + When in list mode, + show sha1 and commit subject line for each head, along with relationship to upstream branch (if any). If given twice, print the name of the upstream branch, as well. @@@ -158,6 -145,10 +159,10 @@@ start-point is either a local or remote like '--track' would when creating the branch, except that where branch points to is not changed. + --edit-description:: + Open an editor and edit the text to explain what the branch is + for, to be used by various other commands (e.g. `request-pull`). + --contains :: Only list branches which contain the specified commit. diff --combined Makefile index b21d2f1417,b499049ae7..d78bd76935 --- a/Makefile +++ b/Makefile @@@ -57,8 -57,8 +57,8 @@@ all: # # Define NO_STRLCPY if you don't have strlcpy. # -# Define NO_STRTOUMAX if you don't have strtoumax in the C library. -# If your compiler also does not support long long or does not have +# Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the +# C library. If your compiler also does not support long long or does not have # strtoull, define NO_STRTOULL. # # Define NO_SETENV if you don't have setenv in the C library. @@@ -250,11 -250,9 +250,11 @@@ # DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR', # DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork' # -# Define COMPUTE_HEADER_DEPENDENCIES if your compiler supports the -MMD option -# and you want to avoid rebuilding objects when an unrelated header file -# changes. +# Define COMPUTE_HEADER_DEPENDENCIES to "yes" if you want dependencies on +# header files to be automatically computed, to avoid rebuilding objects when +# an unrelated header file changes. Define it to "no" to use the hard-coded +# dependency rules. The default is "auto", which means to use computed header +# dependencies if your compiler is detected to support it. # # Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded # dependency rules. @@@ -507,7 -505,6 +507,7 @@@ VCSSVN_LIB=vcs-svn/lib. LIB_H += advice.h LIB_H += archive.h +LIB_H += argv-array.h LIB_H += attr.h LIB_H += blob.h LIB_H += builtin.h @@@ -521,10 -518,8 +521,10 @@@ LIB_H += compat/mingw. LIB_H += compat/obstack.h LIB_H += compat/win32/pthread.h LIB_H += compat/win32/syslog.h -LIB_H += compat/win32/sys/poll.h +LIB_H += compat/win32/poll.h LIB_H += compat/win32/dirent.h +LIB_H += connected.h +LIB_H += convert.h LIB_H += csum-file.h LIB_H += decorate.h LIB_H += delta.h @@@ -532,6 -527,7 +532,7 @@@ LIB_H += diffcore. LIB_H += diff.h LIB_H += dir.h LIB_H += exec_cmd.h + LIB_H += fmt-merge-msg.h LIB_H += fsck.h LIB_H += gettext.h LIB_H += git-compat-util.h @@@ -566,7 -562,6 +567,7 @@@ LIB_H += rerere. LIB_H += resolve-undo.h LIB_H += revision.h LIB_H += run-command.h +LIB_H += sequencer.h LIB_H += sha1-array.h LIB_H += sha1-lookup.h LIB_H += sideband.h @@@ -592,7 -587,6 +593,7 @@@ LIB_OBJS += alloc. LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o +LIB_OBJS += argv-array.o LIB_OBJS += attr.o LIB_OBJS += base85.o LIB_OBJS += bisect.o @@@ -606,7 -600,6 +607,7 @@@ LIB_OBJS += commit. LIB_OBJS += compat/obstack.o LIB_OBJS += config.o LIB_OBJS += connect.o +LIB_OBJS += connected.o LIB_OBJS += convert.o LIB_OBJS += copy.o LIB_OBJS += csum-file.o @@@ -676,7 -669,6 +677,7 @@@ LIB_OBJS += revision. LIB_OBJS += run-command.o LIB_OBJS += server-info.o LIB_OBJS += setup.o +LIB_OBJS += sequencer.o LIB_OBJS += sha1-array.o LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1_file.o @@@ -829,7 -821,6 +830,7 @@@ ifeq ($(uname_S),GNU/kFreeBSD NO_STRLCPY = YesPlease NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease + DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease endif ifeq ($(uname_S),UnixWare) CC = cc @@@ -1095,7 -1086,6 +1096,7 @@@ ifeq ($(uname_S),Windows NO_PREAD = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease NO_LIBGEN_H = YesPlease + NO_SYS_POLL_H = YesPlease NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease NO_SETENV = YesPlease @@@ -1134,7 -1124,7 +1135,7 @@@ 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/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/sys/poll.o compat/win32/dirent.o + compat/win32/poll.o compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib @@@ -1189,7 -1179,6 +1190,7 @@@ ifneq (,$(findstring MINGW,$(uname_S)) NO_PREAD = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease NO_LIBGEN_H = YesPlease + NO_SYS_POLL_H = YesPlease NO_SYMLINK_HEAD = YesPlease NO_SETENV = YesPlease NO_UNSETENV = YesPlease @@@ -1223,7 -1212,7 +1224,7 @@@ COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/sys/poll.o compat/win32/dirent.o + compat/win32/poll.o compat/win32/dirent.o EXTLIBS += -lws2_32 PTHREAD_LIBS = X = .exe @@@ -1252,32 -1241,12 +1253,32 @@@ endi endif ifdef CHECK_HEADER_DEPENDENCIES -COMPUTE_HEADER_DEPENDENCIES = +COMPUTE_HEADER_DEPENDENCIES = no USE_COMPUTED_HEADER_DEPENDENCIES = endif -ifdef COMPUTE_HEADER_DEPENDENCIES +ifndef COMPUTE_HEADER_DEPENDENCIES +COMPUTE_HEADER_DEPENDENCIES = auto +endif + +ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto) +dep_check = $(shell $(CC) $(ALL_CFLAGS) \ + -c -MF /dev/null -MMD -MP -x c /dev/null -o /dev/null 2>&1; \ + echo $$?) +ifeq ($(dep_check),0) +override COMPUTE_HEADER_DEPENDENCIES = yes +else +override COMPUTE_HEADER_DEPENDENCIES = no +endif +endif + +ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes) USE_COMPUTED_HEADER_DEPENDENCIES = YesPlease +else +ifneq ($(COMPUTE_HEADER_DEPENDENCIES),no) +$(error please set COMPUTE_HEADER_DEPENDENCIES to yes, no, or auto \ +(not "$(COMPUTE_HEADER_DEPENDENCIES)")) +endif endif ifdef SANE_TOOL_PATH @@@ -1478,7 -1447,7 +1479,7 @@@ ifdef NO_STRLCP endif ifdef NO_STRTOUMAX COMPAT_CFLAGS += -DNO_STRTOUMAX - COMPAT_OBJS += compat/strtoumax.o + COMPAT_OBJS += compat/strtoumax.o compat/strtoimax.o endif ifdef NO_STRTOULL COMPAT_CFLAGS += -DNO_STRTOULL @@@ -1924,9 -1893,9 +1925,9 @@@ OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $( dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d) dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS)))) -ifdef COMPUTE_HEADER_DEPENDENCIES +ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes) $(dep_dirs): - mkdir -p $@ + @mkdir -p $@ missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs)) dep_file = $(dir $@).depend/$(notdir $@).d @@@ -1937,7 -1906,7 +1938,7 @@@ Please unset CHECK_HEADER_DEPENDENCIES endif endif -ifndef COMPUTE_HEADER_DEPENDENCIES +ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes) ifndef CHECK_HEADER_DEPENDENCIES dep_dirs = missing_dep_dirs = @@@ -2027,13 -1996,13 +2028,13 @@@ builtin/branch.o builtin/checkout.o bui builtin/bundle.o bundle.o transport.o: bundle.h builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h -builtin/grep.o builtin/pack-objects.o transport-helper.o: thread-utils.h +builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h builtin/send-pack.o transport.o: send-pack.h builtin/log.o builtin/shortlog.o: shortlog.h builtin/prune.o builtin/reflog.o reachable.o: reachable.h builtin/commit.o builtin/revert.o wt-status.o: wt-status.h builtin/tar-tree.o archive-tar.o: tar.h -connect.o transport.o http-backend.o: url.h +connect.o transport.o url.o http-backend.o: url.h http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h @@@ -2147,21 -2116,17 +2148,21 @@@ po/git.pot: $(LOCALIZED_C pot: po/git.pot +FIND_SOURCE_FILES = ( git ls-files '*.[hcS]' 2>/dev/null || \ + $(FIND) . \( -name .git -type d -prune \) \ + -o \( -name '*.[hcS]' -type f -print \) ) + $(ETAGS_TARGET): FORCE $(RM) $(ETAGS_TARGET) - $(FIND) . -name '*.[hcS]' -print | xargs etags -a -o $(ETAGS_TARGET) + $(FIND_SOURCE_FILES) | xargs etags -a -o $(ETAGS_TARGET) tags: FORCE $(RM) tags - $(FIND) . -name '*.[hcS]' -print | xargs ctags -a + $(FIND_SOURCE_FILES) | xargs ctags -a cscope: $(RM) cscope* - $(FIND) . -name '*.[hcS]' -print | xargs cscope -b + $(FIND_SOURCE_FILES) | xargs cscope -b ### Detect prefix changes TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):\ @@@ -2316,7 -2281,8 +2317,7 @@@ install: al $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' - (cd mergetools && $(TAR) cf - .) | \ - (cd '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' && umask 022 && $(TAR) xof -) + $(INSTALL) -m 644 mergetools/* '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' ifndef NO_PERL $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C gitweb install diff --combined branch.c index 025a97be02,50088a4190..d91a099fdd --- a/branch.c +++ b/branch.c @@@ -3,7 -3,6 +3,7 @@@ #include "refs.h" #include "remote.h" #include "commit.h" +#include "sequencer.h" struct tracking { struct refspec spec; @@@ -136,6 -135,37 +136,37 @@@ static int setup_tracking(const char *n return 0; } + struct branch_desc_cb { + const char *config_name; + const char *value; + }; + + static int read_branch_desc_cb(const char *var, const char *value, void *cb) + { + struct branch_desc_cb *desc = cb; + if (strcmp(desc->config_name, var)) + return 0; + free((char *)desc->value); + return git_config_string(&desc->value, var, value); + } + + int read_branch_desc(struct strbuf *buf, const char *branch_name) + { + struct branch_desc_cb cb; + struct strbuf name = STRBUF_INIT; + strbuf_addf(&name, "branch.%s.description", branch_name); + cb.config_name = name.buf; + cb.value = NULL; + if (git_config(read_branch_desc_cb, &cb) < 0) { + strbuf_release(&name); + return -1; + } + if (cb.value) + strbuf_addstr(buf, cb.value); + strbuf_release(&name); + return 0; + } + int validate_new_branchname(const char *name, struct strbuf *ref, int force, int attr_only) { @@@ -241,11 -271,9 +272,11 @@@ void create_branch(const char *head void remove_branch_state(void) { unlink(git_path("CHERRY_PICK_HEAD")); + unlink(git_path("REVERT_HEAD")); unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_RR")); unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); + remove_sequencer_state(0); } diff --combined builtin/branch.c index 55cad766c7,fffa319e0e..ad02f0fd7e --- a/builtin/branch.c +++ b/builtin/branch.c @@@ -260,22 -260,9 +260,22 @@@ static char *resolve_symref(const char struct append_ref_cb { struct ref_list *ref_list; + const char **pattern; int ret; }; +static int match_patterns(const char **pattern, const char *refname) +{ + if (!*pattern) + return 1; /* no pattern always matches */ + while (*pattern) { + if (!fnmatch(*pattern, refname, 0)) + return 1; + pattern++; + } + return 0; +} + static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data); @@@ -310,9 -297,6 +310,9 @@@ if ((kind & ref_list->kinds) == 0) return 0; + if (!match_patterns(cb->pattern, refname)) + return 0; + commit = NULL; if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) { commit = lookup_commit_reference_gently(sha1, 1); @@@ -508,7 -492,7 +508,7 @@@ static void show_detached(struct ref_li } } -static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit) +static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) { int i; struct append_ref_cb cb; @@@ -522,7 -506,6 +522,7 @@@ if (merge_filter != NO_FILTER) init_revisions(&ref_list.revs, NULL); cb.ref_list = &ref_list; + cb.pattern = pattern; cb.ret = 0; for_each_rawref(append_ref, &cb); if (merge_filter != NO_FILTER) { @@@ -540,7 -523,7 +540,7 @@@ qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached) + if (detached && match_patterns(pattern, "HEAD")) show_detached(&ref_list); for (i = 0; i < ref_list.index; i++) { @@@ -623,11 -606,49 +623,49 @@@ static int opt_parse_merge_filter(cons return 0; } + static const char edit_description[] = "BRANCH_DESCRIPTION"; + + static int edit_branch_description(const char *branch_name) + { + FILE *fp; + int status; + struct strbuf buf = STRBUF_INIT; + struct strbuf name = STRBUF_INIT; + + read_branch_desc(&buf, branch_name); + if (!buf.len || buf.buf[buf.len-1] != '\n') + strbuf_addch(&buf, '\n'); + strbuf_addf(&buf, + "# Please edit the description for the branch\n" + "# %s\n" + "# Lines starting with '#' will be stripped.\n", + branch_name); + fp = fopen(git_path(edit_description), "w"); + if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) { + strbuf_release(&buf); + return error(_("could not write branch description template: %s\n"), + strerror(errno)); + } + strbuf_reset(&buf); + if (launch_editor(git_path(edit_description), &buf, NULL)) { + strbuf_release(&buf); + return -1; + } + stripspace(&buf, 1); + + strbuf_addf(&name, "branch.%s.description", branch_name); + status = git_config_set(name.buf, buf.buf); + strbuf_release(&name); + strbuf_release(&buf); + + return status; + } + int cmd_branch(int argc, const char **argv, const char *prefix) { - int delete = 0, rename = 0, force_create = 0; + int delete = 0, rename = 0, force_create = 0, list = 0; int verbose = 0, abbrev = -1, detached = 0; - int reflog = 0; + int reflog = 0, edit_description = 0; enum branch_track track; int kinds = REF_LOCAL_BRANCH; struct commit_list *with_commit = NULL; @@@ -641,7 -662,7 +679,7 @@@ OPT_SET_INT( 0, "set-upstream", &track, "change upstream info", BRANCH_TRACK_OVERRIDE), OPT__COLOR(&branch_use_color, "use colored output"), - OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", + OPT_SET_INT('r', "remotes", &kinds, "act on remote-tracking branches", REF_REMOTE_BRANCH), { OPTION_CALLBACK, 0, "contains", &with_commit, "commit", @@@ -658,14 -679,15 +696,16 @@@ OPT__ABBREV(&abbrev), OPT_GROUP("Specific git-branch actions:"), - OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches", + OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches", REF_REMOTE_BRANCH | REF_LOCAL_BRANCH), - OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1), + OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1), OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2), - OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1), + OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1), OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2), - OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN(0, "list", &list, "list branch names"), + OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"), + OPT_BOOLEAN(0, "edit-description", &edit_description, + "edit the description for the branch"), OPT__FORCE(&force_create, "force creation (when already exists)"), { OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, @@@ -704,11 -726,7 +744,11 @@@ argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, 0); - if (!!delete + !!rename + !!force_create > 1) + - if (!delete && !rename && argc == 0) ++ if (!delete && !rename && !edit_description && argc == 0) + list = 1; + + if (!!delete + !!rename + !!force_create + !!list > 1) usage_with_options(builtin_branch_usage, options); if (abbrev == -1) @@@ -716,17 -734,25 +756,29 @@@ if (delete) return delete_branches(argc, argv, delete > 1, kinds); + else if (list) + return print_ref_list(kinds, detached, verbose, abbrev, + with_commit, argv); - else if (rename) { + else if (edit_description) { + const char *branch_name; + if (detached) + die("Cannot give description to detached HEAD"); + if (!argc) + branch_name = head; + else if (argc == 1) + branch_name = argv[0]; + else + usage_with_options(builtin_branch_usage, options); + if (edit_branch_description(branch_name)) + return 1; - } else if (argc == 0) - return print_ref_list(kinds, detached, verbose, abbrev, with_commit); - else if (rename && (argc == 1)) - rename_branch(head, argv[0], rename > 1); - else if (rename && (argc == 2)) - rename_branch(argv[0], argv[1], rename > 1); - else if (argc <= 2) { ++ } else if (rename) { + if (argc == 1) + rename_branch(head, argv[0], rename > 1); + else if (argc == 2) + rename_branch(argv[0], argv[1], rename > 1); + else + usage_with_options(builtin_branch_usage, options); + } else if (argc > 0 && argc <= 2) { if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); create_branch(head, argv[0], (argc == 2) ? argv[1] : head, diff --combined builtin/fmt-merge-msg.c index 7e2f22589d,f6d92e263c..26e7412639 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@@ -5,23 -5,27 +5,27 @@@ #include "revision.h" #include "tag.h" #include "string-list.h" + #include "branch.h" + #include "fmt-merge-msg.h" static const char * const fmt_merge_msg_usage[] = { "git fmt-merge-msg [-m ] [--log[=]|--no-log] [--file ]", NULL }; - static int shortlog_len; + static int use_branch_desc; - static int fmt_merge_msg_config(const char *key, const char *value, void *cb) + int fmt_merge_msg_config(const char *key, const char *value, void *cb) { if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) { int is_bool; - shortlog_len = git_config_bool_or_int(key, value, &is_bool); - if (!is_bool && shortlog_len < 0) + merge_log_config = git_config_bool_or_int(key, value, &is_bool); + if (!is_bool && merge_log_config < 0) return error("%s: negative length %s", key, value); - if (is_bool && shortlog_len) - shortlog_len = DEFAULT_MERGE_LOG_LEN; + if (is_bool && merge_log_config) + merge_log_config = DEFAULT_MERGE_LOG_LEN; + } else if (!strcmp(key, "merge.branchdesc")) { + use_branch_desc = git_config_bool(key, value); } return 0; } @@@ -31,6 -35,11 +35,11 @@@ struct src_data int head_status; }; + struct origin_data { + unsigned char sha1[20]; + unsigned is_local_branch:1; + }; + static void init_src_data(struct src_data *data) { data->branch.strdup_strings = 1; @@@ -45,7 -54,7 +54,7 @@@ static struct string_list origins = STR static int handle_line(char *line) { int i, len = strlen(line); - unsigned char *sha1; + struct origin_data *origin_data; char *src, *origin; struct src_data *src_data; struct string_list_item *item; @@@ -61,11 -70,13 +70,13 @@@ return 2; line[40] = 0; - sha1 = xmalloc(20); - i = get_sha1(line, sha1); + origin_data = xcalloc(1, sizeof(struct origin_data)); + i = get_sha1(line, origin_data->sha1); line[40] = '\t'; - if (i) + if (i) { + free(origin_data); return 3; + } if (line[len - 1] == '\n') line[len - 1] = 0; @@@ -93,6 -104,7 +104,7 @@@ origin = src; src_data->head_status |= 1; } else if (!prefixcmp(line, "branch ")) { + origin_data->is_local_branch = 1; origin = line + 7; string_list_append(&src_data->branch, origin); src_data->head_status |= 2; @@@ -119,7 -131,9 +131,9 @@@ sprintf(new_origin, "%s of %s", origin, src); origin = new_origin; } - string_list_append(&origins, origin)->util = sha1; + if (strcmp(".", src)) + origin_data->is_local_branch = 0; + string_list_append(&origins, origin)->util = origin_data; return 0; } @@@ -140,9 -154,30 +154,30 @@@ static void print_joined(const char *si } } - static void shortlog(const char *name, unsigned char *sha1, - struct commit *head, struct rev_info *rev, int limit, - struct strbuf *out) + static void add_branch_desc(struct strbuf *out, const char *name) + { + struct strbuf desc = STRBUF_INIT; + + if (!read_branch_desc(&desc, name)) { + const char *bp = desc.buf; + while (*bp) { + const char *ep = strchrnul(bp, '\n'); + if (*ep) + ep++; + strbuf_addf(out, " : %.*s", (int)(ep - bp), bp); + bp = ep; + } + if (out->buf[out->len - 1] != '\n') + strbuf_addch(out, '\n'); + } + strbuf_release(&desc); + } + + static void shortlog(const char *name, + struct origin_data *origin_data, + struct commit *head, + struct rev_info *rev, int limit, + struct strbuf *out) { int i, count = 0; struct commit *commit; @@@ -150,6 -185,7 +185,7 @@@ struct string_list subjects = STRING_LIST_INIT_DUP; int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; struct strbuf sb = STRBUF_INIT; + const unsigned char *sha1 = origin_data->sha1; branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40); if (!branch || branch->type != OBJ_COMMIT) @@@ -188,6 -224,9 +224,9 @@@ else strbuf_addf(out, "\n* %s:\n", name); + if (origin_data->is_local_branch && use_branch_desc) + add_branch_desc(out, name); + for (i = 0; i < subjects.nr; i++) if (i >= limit) strbuf_addf(out, " ...\n"); @@@ -293,7 -332,7 +332,7 @@@ static int do_fmt_merge_msg(int merge_t struct commit *head; struct rev_info rev; - head = lookup_commit(head_sha1); + head = lookup_commit_or_die(head_sha1, "HEAD"); init_revisions(&rev, NULL); rev.commit_format = CMIT_FMT_ONELINE; rev.ignore_merges = 1; @@@ -303,8 -342,9 +342,9 @@@ strbuf_addch(out, '\n'); for (i = 0; i < origins.nr; i++) - shortlog(origins.items[i].string, origins.items[i].util, - head, &rev, shortlog_len, out); + shortlog(origins.items[i].string, + origins.items[i].util, + head, &rev, shortlog_len, out); } return 0; } @@@ -318,6 -358,7 +358,7 @@@ int cmd_fmt_merge_msg(int argc, const c { const char *inpath = NULL; const char *message = NULL; + int shortlog_len = -1; struct option options[] = { { OPTION_INTEGER, 0, "log", &shortlog_len, "n", "populate log with at most entries from shortlog", @@@ -341,6 -382,8 +382,8 @@@ 0); if (argc > 0) usage_with_options(fmt_merge_msg_usage, options); + if (shortlog_len < 0) + shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; if (message && !shortlog_len) { char nl = '\n'; write_in_full(STDOUT_FILENO, message, strlen(message)); diff --combined builtin/merge.c index 8a7bebd96a,b8f25dd44e..3fc54923b5 --- a/builtin/merge.c +++ b/builtin/merge.c @@@ -26,6 -26,7 +26,7 @@@ #include "merge-recursive.h" #include "resolve-undo.h" #include "remote.h" + #include "fmt-merge-msg.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@@ -44,12 -45,13 +45,12 @@@ static const char * const builtin_merge NULL }; - static int show_diffstat = 1, shortlog_len, squash; + static int show_diffstat = 1, shortlog_len = -1, squash; static int option_commit = 1, allow_fast_forward = 1; -static int fast_forward_only; +static int fast_forward_only, option_edit; static int allow_trivial = 1, have_message; static struct strbuf merge_msg; static struct commit_list *remoteheads; -static unsigned char head[20], stash[20]; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; static const char **xopts; @@@ -189,8 -191,6 +190,8 @@@ static struct option builtin_merge_opti "create a single commit instead of doing a merge"), OPT_BOOLEAN(0, "commit", &option_commit, "perform a commit if the merge succeeds (default)"), + OPT_BOOLEAN('e', "edit", &option_edit, + "edit message before committing"), OPT_BOOLEAN(0, "ff", &allow_fast_forward, "allow fast-forward (default)"), OPT_BOOLEAN(0, "ff-only", &fast_forward_only, @@@ -218,7 -218,7 +219,7 @@@ static void drop_save(void unlink(git_path("MERGE_MODE")); } -static void save_state(void) +static int save_state(unsigned char *stash) { int len; struct child_process cp; @@@ -237,12 -237,11 +238,12 @@@ if (finish_command(&cp) || len < 0) die(_("stash failed")); - else if (!len) - return; + else if (!len) /* no changes */ + return -1; strbuf_setlen(&buffer, buffer.len-1); if (get_sha1(buffer.buf, stash)) die(_("not a valid object: %s"), buffer.buf); + return 0; } static void read_empty(unsigned const char *sha1, int verbose) @@@ -280,8 -279,7 +281,8 @@@ static void reset_hard(unsigned const c die(_("read-tree failed")); } -static void restore_state(void) +static void restore_state(const unsigned char *head, + const unsigned char *stash) { struct strbuf sb = STRBUF_INIT; const char *args[] = { "stash", "apply", NULL, NULL }; @@@ -311,25 -309,25 +312,25 @@@ static void finish_up_to_date(const cha drop_save(); } -static void squash_message(void) +static void squash_message(struct commit *commit) { struct rev_info rev; - struct commit *commit; struct strbuf out = STRBUF_INIT; struct commit_list *j; + const char *filename; int fd; struct pretty_print_context ctx = {0}; printf(_("Squash commit -- not updating HEAD\n")); - fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666); + filename = git_path("SQUASH_MSG"); + fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) - die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG")); + die_errno(_("Could not write to '%s'"), filename); init_revisions(&rev, NULL); rev.ignore_merges = 1; rev.commit_format = CMIT_FMT_MEDIUM; - commit = lookup_commit(head); commit->object.flags |= UNINTERESTING; add_pending_object(&rev, &commit->object, NULL); @@@ -358,11 -356,9 +359,11 @@@ strbuf_release(&out); } -static void finish(const unsigned char *new_head, const char *msg) +static void finish(struct commit *head_commit, + const unsigned char *new_head, const char *msg) { struct strbuf reflog_message = STRBUF_INIT; + const unsigned char *head = head_commit->object.sha1; if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); @@@ -373,7 -369,7 +374,7 @@@ getenv("GIT_REFLOG_ACTION"), msg); } if (squash) { - squash_message(); + squash_message(head_commit); } else { if (verbosity >= 0 && !merge_msg.len) printf(_("No merge message -- not updating HEAD\n")); @@@ -408,16 -404,6 +409,16 @@@ strbuf_release(&reflog_message); } +static struct object *want_commit(const char *name) +{ + struct object *obj; + unsigned char sha1[20]; + if (get_sha1(name, sha1)) + return NULL; + obj = parse_object(sha1); + return peel_to_type(name, 0, obj, OBJ_COMMIT); +} + /* Get the name for the merge commit's message. */ static void merge_name(const char *remote, struct strbuf *msg) { @@@ -433,7 -419,7 +434,7 @@@ remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); - remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); + remote_head = want_commit(remote); if (!remote_head) die(_("'%s' does not point to a commit"), remote); @@@ -494,16 -480,14 +495,16 @@@ if (!strcmp(remote, "FETCH_HEAD") && !access(git_path("FETCH_HEAD"), R_OK)) { + const char *filename; FILE *fp; struct strbuf line = STRBUF_INIT; char *ptr; - fp = fopen(git_path("FETCH_HEAD"), "r"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "r"); if (!fp) die_errno(_("could not open '%s' for reading"), - git_path("FETCH_HEAD")); + filename); strbuf_getline(&line, fp, '\n'); fclose(fp); ptr = strstr(line.buf, "\tnot-for-merge\t"); @@@ -542,6 -526,8 +543,8 @@@ static void parse_branch_merge_options( static int git_merge_config(const char *k, const char *v, void *cb) { + int status; + if (branch && !prefixcmp(k, "branch.") && !prefixcmp(k + 7, branch) && !strcmp(k + 7 + strlen(branch), ".mergeoptions")) { @@@ -558,15 -544,7 +561,7 @@@ return git_config_string(&pull_octopus, k, v); else if (!strcmp(k, "merge.renormalize")) option_renormalize = git_config_bool(k, v); - else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) { - int is_bool; - shortlog_len = git_config_bool_or_int(k, v, &is_bool); - if (!is_bool && shortlog_len < 0) - return error(_("%s: negative length %s"), k, v); - if (is_bool && shortlog_len) - shortlog_len = DEFAULT_MERGE_LOG_LEN; - return 0; - } else if (!strcmp(k, "merge.ff")) { + else if (!strcmp(k, "merge.ff")) { int boolval = git_config_maybe_bool(k, v); if (0 <= boolval) { allow_fast_forward = boolval; @@@ -579,6 -557,9 +574,9 @@@ default_to_upstream = git_config_bool(k, v); return 0; } + status = fmt_merge_msg_config(k, v, cb); + if (status) + return status; return git_diff_ui_config(k, v, cb); } @@@ -678,7 -659,7 +676,7 @@@ int try_merge_command(const char *strat } static int try_merge_strategy(const char *strategy, struct commit_list *common, - const char *head_arg) + struct commit *head, const char *head_arg) { int index_fd; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); @@@ -724,7 -705,7 +722,7 @@@ commit_list_insert(j->item, &reversed); index_fd = hold_locked_index(lock, 1); - clean = merge_recursive(&o, lookup_commit(head), + clean = merge_recursive(&o, head, remoteheads->item, reversed, &result); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || @@@ -775,7 -756,7 +773,7 @@@ int checkout_fast_forward(const unsigne memset(&t, 0, sizeof(t)); memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; - dir.exclude_per_dir = ".gitignore"; + setup_standard_excludes(&dir); opts.dir = &dir; opts.head_idx = 1; @@@ -849,78 -830,51 +847,78 @@@ static void add_strategies(const char * } -static void write_merge_msg(void) +static void write_merge_msg(struct strbuf *msg) { - int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); + const char *filename = git_path("MERGE_MSG"); + int fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MSG")); - if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG")); + filename); + if (write_in_full(fd, msg->buf, msg->len) != msg->len) + die_errno(_("Could not write to '%s'"), filename); close(fd); } -static void read_merge_msg(void) +static void read_merge_msg(struct strbuf *msg) { - strbuf_reset(&merge_msg); - if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0) - die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG")); + const char *filename = git_path("MERGE_MSG"); + strbuf_reset(msg); + if (strbuf_read_file(msg, filename, 0) < 0) + die_errno(_("Could not read from '%s'"), filename); } -static void run_prepare_commit_msg(void) +static void write_merge_state(void); +static void abort_commit(const char *err_msg) { - write_merge_msg(); + if (err_msg) + error("%s", err_msg); + fprintf(stderr, + _("Not committing merge; use 'git commit' to complete the merge.\n")); + write_merge_state(); + exit(1); +} + +static void prepare_to_commit(void) +{ + struct strbuf msg = STRBUF_INIT; + strbuf_addbuf(&msg, &merge_msg); + strbuf_addch(&msg, '\n'); + write_merge_msg(&msg); run_hook(get_index_file(), "prepare-commit-msg", git_path("MERGE_MSG"), "merge", NULL, NULL); - read_merge_msg(); + if (option_edit) { + if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) + abort_commit(NULL); + } + read_merge_msg(&msg); + stripspace(&msg, option_edit); + if (!msg.len) + abort_commit(_("Empty commit message.")); + strbuf_release(&merge_msg); + strbuf_addbuf(&merge_msg, &msg); + strbuf_release(&msg); } -static int merge_trivial(void) +static int merge_trivial(struct commit *head) { unsigned char result_tree[20], result_commit[20]; struct commit_list *parent = xmalloc(sizeof(*parent)); write_tree_trivial(result_tree); printf(_("Wonderful.\n")); - parent->item = lookup_commit(head); + parent->item = head; parent->next = xmalloc(sizeof(*parent->next)); parent->next->item = remoteheads->item; parent->next->next = NULL; - run_prepare_commit_msg(); + prepare_to_commit(); commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL); - finish(result_commit, "In-index merge"); + finish(head, result_commit, "In-index merge"); drop_save(); return 0; } -static int finish_automerge(struct commit_list *common, +static int finish_automerge(struct commit *head, + struct commit_list *common, unsigned char *result_tree, const char *wt_strategy) { @@@ -931,22 -885,22 +929,22 @@@ free_commit_list(common); if (allow_fast_forward) { parents = remoteheads; - commit_list_insert(lookup_commit(head), &parents); + commit_list_insert(head, &parents); parents = reduce_heads(parents); } else { struct commit_list **pptr = &parents; - pptr = &commit_list_insert(lookup_commit(head), + pptr = &commit_list_insert(head, pptr)->next; for (j = remoteheads; j; j = j->next) pptr = &commit_list_insert(j->item, pptr)->next; } - free_commit_list(remoteheads); strbuf_addch(&merge_msg, '\n'); - run_prepare_commit_msg(); + prepare_to_commit(); + free_commit_list(remoteheads); commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); - finish(result_commit, buf.buf); + finish(head, result_commit, buf.buf); strbuf_release(&buf); drop_save(); return 0; @@@ -954,14 -908,13 +952,14 @@@ static int suggest_conflicts(int renormalizing) { + const char *filename; FILE *fp; int pos; - fp = fopen(git_path("MERGE_MSG"), "a"); + filename = git_path("MERGE_MSG"); + fp = fopen(filename, "a"); if (!fp) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MSG")); + die_errno(_("Could not open '%s' for writing"), filename); fprintf(fp, "\nConflicts:\n"); for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; @@@ -981,8 -934,7 +979,8 @@@ return 1; } -static struct commit *is_old_style_invocation(int argc, const char **argv) +static struct commit *is_old_style_invocation(int argc, const char **argv, + const unsigned char *head) { struct commit *second_token = NULL; if (argc > 2) { @@@ -1051,47 -1003,12 +1049,47 @@@ static int setup_with_upstream(const ch return i; } +static void write_merge_state(void) +{ + const char *filename; + int fd; + struct commit_list *j; + struct strbuf buf = STRBUF_INIT; + + for (j = remoteheads; j; j = j->next) + strbuf_addf(&buf, "%s\n", + sha1_to_hex(j->item->object.sha1)); + filename = git_path("MERGE_HEAD"); + fd = open(filename, O_WRONLY | O_CREAT, 0666); + if (fd < 0) + die_errno(_("Could not open '%s' for writing"), filename); + if (write_in_full(fd, buf.buf, buf.len) != buf.len) + die_errno(_("Could not write to '%s'"), filename); + close(fd); + strbuf_addch(&merge_msg, '\n'); + write_merge_msg(&merge_msg); + + filename = git_path("MERGE_MODE"); + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) + die_errno(_("Could not open '%s' for writing"), filename); + strbuf_reset(&buf); + if (!allow_fast_forward) + strbuf_addf(&buf, "no-ff"); + if (write_in_full(fd, buf.buf, buf.len) != buf.len) + die_errno(_("Could not write to '%s'"), filename); + close(fd); +} + int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; + unsigned char stash[20]; + unsigned char head_sha1[20]; + struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; - int flag, head_invalid = 0, i; + int flag, i; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; const char *best_strategy = NULL, *wt_strategy = NULL; @@@ -1104,13 -1021,11 +1102,13 @@@ * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = resolve_ref("HEAD", head, 0, &flag); + branch = resolve_ref("HEAD", head_sha1, 0, &flag); if (branch && !prefixcmp(branch, "refs/heads/")) branch += 11; - if (is_null_sha1(head)) - head_invalid = 1; + if (!branch || is_null_sha1(head_sha1)) + head_commit = NULL; + else + head_commit = lookup_commit_or_die(head_sha1, "HEAD"); git_config(git_merge_config, NULL); @@@ -1118,6 -1033,8 +1116,8 @@@ parse_branch_merge_options(branch_mergeoptions); argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); + if (shortlog_len < 0) + shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; if (verbosity < 0 && show_progress == -1) show_progress = 0; @@@ -1169,12 -1086,9 +1169,12 @@@ die(_("You cannot combine --no-ff with --ff-only.")); if (!abort_current_merge) { - if (!argc && default_to_upstream) - argc = setup_with_upstream(&argv); - else if (argc == 1 && !strcmp(argv[0], "-")) + if (!argc) { + if (default_to_upstream) + argc = setup_with_upstream(&argv); + else + die(_("No commit specified and merge.defaultToUpstream not set.")); + } else if (argc == 1 && !strcmp(argv[0], "-")) argv[0] = "@{-1}"; } if (!argc) @@@ -1190,13 -1104,12 +1190,13 @@@ * additional safety measure to check for it. */ - if (!have_message && is_old_style_invocation(argc, argv)) { + if (!have_message && head_commit && + is_old_style_invocation(argc, argv, head_commit->object.sha1)) { strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; - } else if (head_invalid) { + } else if (!head_commit) { struct object *remote_head; /* * If the merged head is a valid one there is no reason @@@ -1211,7 -1124,7 +1211,7 @@@ if (!allow_fast_forward) die(_("Non-fast-forward commit does not make sense into " "an empty head")); - remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT); + remote_head = want_commit(argv[0]); if (!remote_head) die(_("%s - not something we can merge"), argv[0]); read_empty(remote_head->sha1, 0); @@@ -1243,7 -1156,7 +1243,7 @@@ } } - if (head_invalid || !argc) + if (!head_commit || !argc) usage_with_options(builtin_merge_usage, builtin_merge_options); @@@ -1257,7 -1170,7 +1257,7 @@@ struct object *o; struct commit *commit; - o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT); + o = want_commit(argv[i]); if (!o) die(_("%s - not something we can merge"), argv[i]); commit = lookup_commit(o->sha1); @@@ -1284,16 -1197,17 +1284,16 @@@ } if (!remoteheads->next) - common = get_merge_bases(lookup_commit(head), - remoteheads->item, 1); + common = get_merge_bases(head_commit, remoteheads->item, 1); else { struct commit_list *list = remoteheads; - commit_list_insert(lookup_commit(head), &list); + commit_list_insert(head_commit, &list); common = get_octopus_merge_bases(list); free(list); } - update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0, - DIE_ON_ERR); + update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1, + NULL, 0, DIE_ON_ERR); if (!common) ; /* No common ancestors found. We need a real merge. */ @@@ -1307,13 -1221,13 +1307,13 @@@ return 0; } else if (allow_fast_forward && !remoteheads->next && !common->next && - !hashcmp(common->item->object.sha1, head)) { + !hashcmp(common->item->object.sha1, head_commit->object.sha1)) { /* Again the most common case of merging one remote. */ struct strbuf msg = STRBUF_INIT; struct object *o; char hex[41]; - strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV)); + strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV)); if (verbosity >= 0) printf(_("Updating %s..%s\n"), @@@ -1324,14 -1238,15 +1324,14 @@@ if (have_message) strbuf_addstr(&msg, " (no commit created; -m option ignored)"); - o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1), - 0, NULL, OBJ_COMMIT); + o = want_commit(sha1_to_hex(remoteheads->item->object.sha1)); if (!o) return 1; - if (checkout_fast_forward(head, remoteheads->item->object.sha1)) + if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1)) return 1; - finish(o->sha1, msg.buf); + finish(head_commit, o->sha1, msg.buf); drop_save(); return 0; } else if (!remoteheads->next && common->next) @@@ -1351,8 -1266,8 +1351,8 @@@ git_committer_info(IDENT_ERROR_ON_NO_NAME); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, - head, remoteheads->item->object.sha1)) - return merge_trivial(); + head_commit->object.sha1, remoteheads->item->object.sha1)) + return merge_trivial(head_commit); printf(_("Nope.\n")); } } else { @@@ -1371,7 -1286,8 +1371,7 @@@ * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = get_merge_bases(lookup_commit(head), - j->item, 1); + common_one = get_merge_bases(head_commit, j->item, 1); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; @@@ -1398,18 -1314,21 +1398,18 @@@ * sync with the head commit. The strategies are responsible * to ensure this. */ - if (use_strategies_nr != 1) { - /* - * Stash away the local changes so that we can try more - * than one. - */ - save_state(); - } else { - memcpy(stash, null_sha1, 20); - } + if (use_strategies_nr == 1 || + /* + * Stash away the local changes so that we can try more than one. + */ + save_state(stash)) + hashcpy(stash, null_sha1); for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf(_("Rewinding the tree to pristine...\n")); - restore_state(); + restore_state(head_commit->object.sha1, stash); } if (use_strategies_nr != 1) printf(_("Trying merge strategy %s...\n"), @@@ -1421,7 -1340,7 +1421,7 @@@ wt_strategy = use_strategies[i]->name; ret = try_merge_strategy(use_strategies[i]->name, - common, head_arg); + common, head_commit, head_arg); if (!option_commit && !ret) { merge_was_ok = 1; /* @@@ -1463,15 -1382,14 +1463,15 @@@ * auto resolved the merge cleanly. */ if (automerge_was_ok) - return finish_automerge(common, result_tree, wt_strategy); + return finish_automerge(head_commit, common, result_tree, + wt_strategy); /* * Pick the result from the best strategy and have the user fix * it up. */ if (!best_strategy) { - restore_state(); + restore_state(head_commit->object.sha1, stash); if (use_strategies_nr > 1) fprintf(stderr, _("No merge strategy handled the merge.\n")); @@@ -1483,16 -1401,41 +1483,16 @@@ ; /* We already have its result in the working tree. */ else { printf(_("Rewinding the tree to pristine...\n")); - restore_state(); + restore_state(head_commit->object.sha1, stash); printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); - try_merge_strategy(best_strategy, common, head_arg); + try_merge_strategy(best_strategy, common, head_commit, head_arg); } if (squash) - finish(NULL, NULL); - else { - int fd; - struct commit_list *j; - - for (j = remoteheads; j; j = j->next) - strbuf_addf(&buf, "%s\n", - sha1_to_hex(j->item->object.sha1)); - fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666); - if (fd < 0) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_HEAD")); - if (write_in_full(fd, buf.buf, buf.len) != buf.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD")); - close(fd); - strbuf_addch(&merge_msg, '\n'); - write_merge_msg(); - fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MODE")); - strbuf_reset(&buf); - if (!allow_fast_forward) - strbuf_addf(&buf, "no-ff"); - if (write_in_full(fd, buf.buf, buf.len) != buf.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE")); - close(fd); - } + finish(head_commit, NULL, NULL); + else + write_merge_state(); if (merge_was_ok) { fprintf(stderr, _("Automatic merge went well; " diff --combined environment.c index 0bee6a7a88,4b7ecab8b5..2c41d7d6cb --- a/environment.c +++ b/environment.c @@@ -9,6 -9,7 +9,7 @@@ */ #include "cache.h" #include "refs.h" + #include "fmt-merge-msg.h" char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; @@@ -29,7 -30,6 +30,7 @@@ const char *git_log_output_encoding int shared_repository = PERM_UMASK; const char *apply_default_whitespace; const char *apply_default_ignorewhitespace; +const char *git_attributes_file; int zlib_compression_level = Z_BEST_SPEED; int core_compression_level; int core_compression_seen; @@@ -59,6 -59,7 +60,7 @@@ enum object_creation_mode object_creati char *notes_ref_name; int grafts_replace_parents = 1; int core_apply_sparse_checkout; + int merge_log_config = -1; struct startup_info *startup_info; /* Parallel index stat data preload? */ @@@ -107,7 -108,7 +109,7 @@@ static char *expand_namespace(const cha if (strcmp((*c)->buf, "/") != 0) strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf); strbuf_list_free(components); - if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK) + if (check_refname_format(buf.buf, 0)) die("bad git namespace path \"%s\"", raw_namespace); strbuf_addch(&buf, '/'); return strbuf_detach(&buf, NULL);