diff options
Diffstat (limited to 'builtin/commit.c')
| -rw-r--r-- | builtin/commit.c | 380 | 
1 files changed, 261 insertions, 119 deletions
| diff --git a/builtin/commit.c b/builtin/commit.c index 7d711eafa6..c2300185ec 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -6,6 +6,7 @@   */  #include "cache.h" +#include "lockfile.h"  #include "cache-tree.h"  #include "color.h"  #include "dir.h" @@ -42,7 +43,20 @@ static const char * const builtin_status_usage[] = {  	NULL  }; -static const char implicit_ident_advice[] = +static const char implicit_ident_advice_noconfig[] = +N_("Your name and email address were configured automatically based\n" +"on your username and hostname. Please check that they are accurate.\n" +"You can suppress this message by setting them explicitly. Run the\n" +"following command and follow the instructions in your editor to edit\n" +"your configuration file:\n" +"\n" +"    git config --global --edit\n" +"\n" +"After doing this, you may fix the identity used for this commit with:\n" +"\n" +"    git commit --amend --reset-author\n"); + +static const char implicit_ident_advice_config[] =  N_("Your name and email address were configured automatically based\n"  "on your username and hostname. Please check that they are accurate.\n"  "You can suppress this message by setting them explicitly:\n" @@ -113,6 +127,7 @@ static char *sign_commit;  static enum {  	CLEANUP_SPACE,  	CLEANUP_NONE, +	CLEANUP_SCISSORS,  	CLEANUP_ALL  } cleanup_mode;  static const char *cleanup_arg; @@ -301,13 +316,11 @@ static void refresh_cache_or_die(int refresh_flags)  		die_resolve_conflict("commit");  } -static char *prepare_index(int argc, const char **argv, const char *prefix, -			   const struct commit *current_head, int is_status) +static const char *prepare_index(int argc, const char **argv, const char *prefix, +				 const struct commit *current_head, int is_status)  { -	int fd;  	struct string_list partial;  	struct pathspec pathspec; -	char *old_index_env = NULL;  	int refresh_flags = REFRESH_QUIET;  	if (is_status) @@ -320,16 +333,16 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,  		die(_("index file corrupt"));  	if (interactive) { -		fd = hold_locked_index(&index_lock, 1); +		char *old_index_env = NULL; +		hold_locked_index(&index_lock, 1);  		refresh_cache_or_die(refresh_flags); -		if (write_cache(fd, active_cache, active_nr) || -		    close_lock_file(&index_lock)) +		if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))  			die(_("unable to create temporary index"));  		old_index_env = getenv(INDEX_ENVIRONMENT); -		setenv(INDEX_ENVIRONMENT, index_lock.filename, 1); +		setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);  		if (interactive_add(argc, argv, prefix, patch_interactive) != 0)  			die(_("interactive add failed")); @@ -340,10 +353,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,  			unsetenv(INDEX_ENVIRONMENT);  		discard_cache(); -		read_cache_from(index_lock.filename); +		read_cache_from(index_lock.filename.buf); +		if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) { +			if (reopen_lock_file(&index_lock) < 0) +				die(_("unable to write index file")); +			if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) +				die(_("unable to update temporary index")); +		} else +			warning(_("Failed to update main cache tree"));  		commit_style = COMMIT_NORMAL; -		return index_lock.filename; +		return index_lock.filename.buf;  	}  	/* @@ -359,15 +379,14 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,  	 * (B) on failure, rollback the real index.  	 */  	if (all || (also && pathspec.nr)) { -		fd = hold_locked_index(&index_lock, 1); +		hold_locked_index(&index_lock, 1);  		add_files_to_cache(also ? prefix : NULL, &pathspec, 0);  		refresh_cache_or_die(refresh_flags);  		update_main_cache_tree(WRITE_TREE_SILENT); -		if (write_cache(fd, active_cache, active_nr) || -		    close_lock_file(&index_lock)) +		if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))  			die(_("unable to write new_index file"));  		commit_style = COMMIT_NORMAL; -		return index_lock.filename; +		return index_lock.filename.buf;  	}  	/* @@ -380,12 +399,16 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,  	 * We still need to refresh the index here.  	 */  	if (!only && !pathspec.nr) { -		fd = hold_locked_index(&index_lock, 1); +		hold_locked_index(&index_lock, 1);  		refresh_cache_or_die(refresh_flags); -		if (active_cache_changed) { +		if (active_cache_changed +		    || !cache_tree_fully_valid(active_cache_tree)) {  			update_main_cache_tree(WRITE_TREE_SILENT); -			if (write_cache(fd, active_cache, active_nr) || -			    commit_locked_index(&index_lock)) +			active_cache_changed = 1; +		} +		if (active_cache_changed) { +			if (write_locked_index(&the_index, &index_lock, +					       COMMIT_LOCK))  				die(_("unable to write new_index file"));  		} else {  			rollback_lock_file(&index_lock); @@ -422,8 +445,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,  			die(_("cannot do a partial commit during a cherry-pick."));  	} -	memset(&partial, 0, sizeof(partial)); -	partial.strdup_strings = 1; +	string_list_init(&partial, 1);  	if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))  		exit(1); @@ -431,30 +453,29 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,  	if (read_cache() < 0)  		die(_("cannot read the index")); -	fd = hold_locked_index(&index_lock, 1); +	hold_locked_index(&index_lock, 1);  	add_remove_files(&partial);  	refresh_cache(REFRESH_QUIET); -	if (write_cache(fd, active_cache, active_nr) || -	    close_lock_file(&index_lock)) +	update_main_cache_tree(WRITE_TREE_SILENT); +	if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))  		die(_("unable to write new_index file")); -	fd = hold_lock_file_for_update(&false_lock, -				       git_path("next-index-%"PRIuMAX, -						(uintmax_t) getpid()), -				       LOCK_DIE_ON_ERROR); +	hold_lock_file_for_update(&false_lock, +				  git_path("next-index-%"PRIuMAX, +					   (uintmax_t) getpid()), +				  LOCK_DIE_ON_ERROR);  	create_base_index(current_head);  	add_remove_files(&partial);  	refresh_cache(REFRESH_QUIET); -	if (write_cache(fd, active_cache, active_nr) || -	    close_lock_file(&false_lock)) +	if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))  		die(_("unable to write temporary index file"));  	discard_cache(); -	read_cache_from(false_lock.filename); +	read_cache_from(false_lock.filename.buf); -	return false_lock.filename; +	return false_lock.filename.buf;  }  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, @@ -525,56 +546,82 @@ static int sane_ident_split(struct ident_split *person)  	return 1;  } +static int parse_force_date(const char *in, struct strbuf *out) +{ +	strbuf_addch(out, '@'); + +	if (parse_date(in, out) < 0) { +		int errors = 0; +		unsigned long t = approxidate_careful(in, &errors); +		if (errors) +			return -1; +		strbuf_addf(out, "%lu", t); +	} + +	return 0; +} + +static void set_ident_var(char **buf, char *val) +{ +	free(*buf); +	*buf = val; +} + +static char *envdup(const char *var) +{ +	const char *val = getenv(var); +	return val ? xstrdup(val) : NULL; +} +  static void determine_author_info(struct strbuf *author_ident)  {  	char *name, *email, *date;  	struct ident_split author; -	name = getenv("GIT_AUTHOR_NAME"); -	email = getenv("GIT_AUTHOR_EMAIL"); -	date = getenv("GIT_AUTHOR_DATE"); +	name = envdup("GIT_AUTHOR_NAME"); +	email = envdup("GIT_AUTHOR_EMAIL"); +	date = envdup("GIT_AUTHOR_DATE");  	if (author_message) { -		const char *a, *lb, *rb, *eol; +		struct ident_split ident;  		size_t len; +		const char *a; -		a = strstr(author_message_buffer, "\nauthor "); +		a = find_commit_header(author_message_buffer, "author", &len);  		if (!a) -			die(_("invalid commit: %s"), author_message); - -		lb = strchrnul(a + strlen("\nauthor "), '<'); -		rb = strchrnul(lb, '>'); -		eol = strchrnul(rb, '\n'); -		if (!*lb || !*rb || !*eol) -			die(_("invalid commit: %s"), author_message); - -		if (lb == a + strlen("\nauthor ")) -			/* \nauthor <foo@example.com> */ -			name = xcalloc(1, 1); -		else -			name = xmemdupz(a + strlen("\nauthor "), -					(lb - strlen(" ") - -					 (a + strlen("\nauthor ")))); -		email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<"))); -		len = eol - (rb + strlen("> ")); -		date = xmalloc(len + 2); -		*date = '@'; -		memcpy(date + 1, rb + strlen("> "), len); -		date[len + 1] = '\0'; +			die(_("commit '%s' lacks author header"), author_message); +		if (split_ident_line(&ident, a, len) < 0) +			die(_("commit '%s' has malformed author line"), author_message); + +		set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin)); +		set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin)); + +		if (ident.date_begin) { +			struct strbuf date_buf = STRBUF_INIT; +			strbuf_addch(&date_buf, '@'); +			strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin); +			strbuf_addch(&date_buf, ' '); +			strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin); +			set_ident_var(&date, strbuf_detach(&date_buf, NULL)); +		}  	}  	if (force_author) { -		const char *lb = strstr(force_author, " <"); -		const char *rb = strchr(force_author, '>'); +		struct ident_split ident; -		if (!lb || !rb) +		if (split_ident_line(&ident, force_author, strlen(force_author)) < 0)  			die(_("malformed --author parameter")); -		name = xstrndup(force_author, lb - force_author); -		email = xstrndup(lb + 2, rb - (lb + 2)); +		set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin)); +		set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin)); +	} + +	if (force_date) { +		struct strbuf date_buf = STRBUF_INIT; +		if (parse_force_date(force_date, &date_buf)) +			die(_("invalid date format: %s"), force_date); +		set_ident_var(&date, strbuf_detach(&date_buf, NULL));  	} -	if (force_date) -		date = force_date;  	strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));  	if (!split_ident_line(&author, author_ident->buf, author_ident->len) &&  	    sane_ident_split(&author)) { @@ -582,15 +629,52 @@ static void determine_author_info(struct strbuf *author_ident)  		export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);  		export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');  	} + +	free(name); +	free(email); +	free(date); +} + +static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf) +{ +	if (split_ident_line(id, buf->buf, buf->len) || +	    !sane_ident_split(id)) +		die(_("Malformed ident string: '%s'"), buf->buf);  } -static char *cut_ident_timestamp_part(char *string) +static int author_date_is_interesting(void)  { -	char *ket = strrchr(string, '>'); -	if (!ket || ket[1] != ' ') -		die(_("Malformed ident string: '%s'"), string); -	*++ket = '\0'; -	return ket; +	return author_message || force_date; +} + +static void adjust_comment_line_char(const struct strbuf *sb) +{ +	char candidates[] = "#;@!$%^&|:"; +	char *candidate; +	const char *p; + +	comment_line_char = candidates[0]; +	if (!memchr(sb->buf, comment_line_char, sb->len)) +		return; + +	p = sb->buf; +	candidate = strchr(candidates, *p); +	if (candidate) +		*candidate = ' '; +	for (p = sb->buf; *p; p++) { +		if ((p[0] == '\n' || p[0] == '\r') && p[1]) { +			candidate = strchr(candidates, p[1]); +			if (candidate) +				*candidate = ' '; +		} +	} + +	for (p = candidates; *p == ' '; p++) +		; +	if (!*p) +		die(_("unable to select a comment character that is not used\n" +		      "in the current commit message")); +	comment_line_char = *p;  }  static int prepare_to_commit(const char *index_file, const char *prefix, @@ -600,12 +684,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  {  	struct stat statbuf;  	struct strbuf committer_ident = STRBUF_INIT; -	int commitable, saved_color_setting; +	int commitable;  	struct strbuf sb = STRBUF_INIT; -	char *buffer;  	const char *hook_arg1 = NULL;  	const char *hook_arg2 = NULL; -	int ident_shown = 0;  	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);  	int old_display_comment_prefix; @@ -649,10 +731,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  				  logfile);  		hook_arg1 = "message";  	} else if (use_message) { +		char *buffer;  		buffer = strstr(use_message_buffer, "\n\n"); -		if (!use_editor && (!buffer || buffer[2] == '\0')) -			die(_("commit has empty message")); -		strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); +		if (buffer) +			strbuf_addstr(&sb, buffer + 2);  		hook_arg1 = "commit";  		hook_arg2 = use_message;  	} else if (fixup_message) { @@ -748,13 +830,20 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)  		die_errno(_("could not write commit template")); +	if (auto_comment_line_char) +		adjust_comment_line_char(&sb);  	strbuf_release(&sb);  	/* This checks if committer ident is explicitly given */  	strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT));  	if (use_editor && include_status) { -		char *ai_tmp, *ci_tmp; -		if (whence != FROM_COMMIT) +		int ident_shown = 0; +		int saved_color_setting; +		struct ident_split ci, ai; + +		if (whence != FROM_COMMIT) { +			if (cleanup_mode == CLEANUP_SCISSORS) +				wt_status_add_cut_line(s->fp);  			status_printf_ln(s, GIT_COLOR_NORMAL,  			    whence == FROM_MERGE  				? _("\n" @@ -770,6 +859,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  				git_path(whence == FROM_MERGE  					 ? "MERGE_HEAD"  					 : "CHERRY_PICK_HEAD")); +		}  		fprintf(s->fp, "\n");  		if (cleanup_mode == CLEANUP_ALL) @@ -777,6 +867,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  				_("Please enter the commit message for your changes."  				  " Lines starting\nwith '%c' will be ignored, and an empty"  				  " message aborts the commit.\n"), comment_line_char); +		else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT) +			wt_status_add_cut_line(s->fp);  		else /* CLEANUP_SPACE, that is. */  			status_printf(s, GIT_COLOR_NORMAL,  				_("Please enter the commit message for your changes." @@ -788,32 +880,39 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  			status_printf_ln(s, GIT_COLOR_NORMAL,  					"%s", only_include_assumed); -		ai_tmp = cut_ident_timestamp_part(author_ident->buf); -		ci_tmp = cut_ident_timestamp_part(committer_ident.buf); -		if (strcmp(author_ident->buf, committer_ident.buf)) +		split_ident_or_die(&ai, author_ident); +		split_ident_or_die(&ci, &committer_ident); + +		if (ident_cmp(&ai, &ci)) +			status_printf_ln(s, GIT_COLOR_NORMAL, +				_("%s" +				"Author:    %.*s <%.*s>"), +				ident_shown++ ? "" : "\n", +				(int)(ai.name_end - ai.name_begin), ai.name_begin, +				(int)(ai.mail_end - ai.mail_begin), ai.mail_begin); + +		if (author_date_is_interesting())  			status_printf_ln(s, GIT_COLOR_NORMAL,  				_("%s" -				"Author:    %s"), +				"Date:      %s"),  				ident_shown++ ? "" : "\n", -				author_ident->buf); +				show_ident_date(&ai, DATE_NORMAL));  		if (!committer_ident_sufficiently_given())  			status_printf_ln(s, GIT_COLOR_NORMAL,  				_("%s" -				"Committer: %s"), +				"Committer: %.*s <%.*s>"),  				ident_shown++ ? "" : "\n", -				committer_ident.buf); +				(int)(ci.name_end - ci.name_begin), ci.name_begin, +				(int)(ci.mail_end - ci.mail_begin), ci.mail_begin);  		if (ident_shown) -			status_printf_ln(s, GIT_COLOR_NORMAL, ""); +			status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");  		saved_color_setting = s->use_color;  		s->use_color = 0;  		commitable = run_status(s->fp, index_file, prefix, 1, s);  		s->use_color = saved_color_setting; - -		*ai_tmp = ' '; -		*ci_tmp = ' ';  	} else {  		unsigned char sha1[20];  		const char *parent = "HEAD"; @@ -826,8 +925,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,  		if (get_sha1(parent, sha1))  			commitable = !!active_nr; -		else -			commitable = index_differs_from(parent, 0); +		else { +			/* +			 * Unless the user did explicitly request a submodule +			 * ignore mode by passing a command line option we do +			 * not ignore any changed submodule SHA-1s when +			 * comparing index and parent, no matter what is +			 * configured. Otherwise we won't commit any +			 * submodules which were manually staged, which would +			 * be really confusing. +			 */ +			int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG; +			if (ignore_submodule_arg && +			    !strcmp(ignore_submodule_arg, "all")) +				diff_flags |= DIFF_OPT_IGNORE_SUBMODULES; +			commitable = index_differs_from(parent, diff_flags); +		}  	}  	strbuf_release(&committer_ident); @@ -934,7 +1047,7 @@ static int message_is_empty(struct strbuf *sb)  static int template_untouched(struct strbuf *sb)  {  	struct strbuf tmpl = STRBUF_INIT; -	char *start; +	const char *start;  	if (cleanup_mode == CLEANUP_NONE && sb->len)  		return 0; @@ -943,8 +1056,7 @@ static int template_untouched(struct strbuf *sb)  		return 0;  	stripspace(&tmpl, cleanup_mode == CLEANUP_ALL); -	start = (char *)skip_prefix(sb->buf, tmpl.buf); -	if (!start) +	if (!skip_prefix(sb->buf, tmpl.buf, &start))  		start = sb->buf;  	strbuf_release(&tmpl);  	return rest_is_empty(sb, start - sb->buf); @@ -969,7 +1081,8 @@ static const char *find_author_by_nickname(const char *name)  	revs.mailmap = &mailmap;  	read_mailmap(revs.mailmap, NULL); -	prepare_revision_walk(&revs); +	if (prepare_revision_walk(&revs)) +		die(_("revision walk setup failed"));  	commit = get_revision(&revs);  	if (commit) {  		struct pretty_print_context ctx = {0}; @@ -1130,6 +1243,8 @@ static int parse_and_validate_options(int argc, const char *argv[],  		cleanup_mode = CLEANUP_SPACE;  	else if (!strcmp(cleanup_arg, "strip"))  		cleanup_mode = CLEANUP_ALL; +	else if (!strcmp(cleanup_arg, "scissors")) +		cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;  	else  		die(_("Invalid cleanup mode %s"), cleanup_arg); @@ -1321,6 +1436,24 @@ int cmd_status(int argc, const char **argv, const char *prefix)  	return 0;  } +static const char *implicit_ident_advice(void) +{ +	char *user_config = NULL; +	char *xdg_config = NULL; +	int config_exists; + +	home_config_paths(&user_config, &xdg_config, "config"); +	config_exists = file_exists(user_config) || file_exists(xdg_config); +	free(user_config); +	free(xdg_config); + +	if (config_exists) +		return _(implicit_ident_advice_config); +	else +		return _(implicit_ident_advice_noconfig); + +} +  static void print_summary(const char *prefix, const unsigned char *sha1,  			  int initial_commit)  { @@ -1347,12 +1480,19 @@ static void print_summary(const char *prefix, const unsigned char *sha1,  		strbuf_addstr(&format, "\n Author: ");  		strbuf_addbuf_percentquote(&format, &author_ident);  	} +	if (author_date_is_interesting()) { +		struct strbuf date = STRBUF_INIT; +		format_commit_message(commit, "%ad", &date, &pctx); +		strbuf_addstr(&format, "\n Date: "); +		strbuf_addbuf_percentquote(&format, &date); +		strbuf_release(&date); +	}  	if (!committer_ident_sufficiently_given()) {  		strbuf_addstr(&format, "\n Committer: ");  		strbuf_addbuf_percentquote(&format, &committer_ident);  		if (advice_implicit_identity) {  			strbuf_addch(&format, '\n'); -			strbuf_addstr(&format, _(implicit_ident_advice)); +			strbuf_addstr(&format, implicit_ident_advice());  		}  	}  	strbuf_release(&author_ident); @@ -1404,6 +1544,10 @@ static int git_commit_config(const char *k, const char *v, void *cb)  	}  	if (!strcmp(k, "commit.cleanup"))  		return git_config_string(&cleanup_arg, k, v); +	if (!strcmp(k, "commit.gpgsign")) { +		sign_commit = git_config_bool(k, v) ? "" : NULL; +		return 0; +	}  	status = git_gpg_config(k, v, NULL);  	if (status) @@ -1416,7 +1560,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1,  {  	/* oldsha1 SP newsha1 LF NUL */  	static char buf[2*40 + 3]; -	struct child_process proc; +	struct child_process proc = CHILD_PROCESS_INIT;  	const char *argv[3];  	int code;  	size_t n; @@ -1428,7 +1572,6 @@ static int run_rewrite_hook(const unsigned char *oldsha1,  	argv[1] = "amend";  	argv[2] = NULL; -	memset(&proc, 0, sizeof(proc));  	proc.argv = argv;  	proc.in = -1;  	proc.stdout_to_stderr = 1; @@ -1488,7 +1631,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),  		OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),  		OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), -		{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), +		{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),  		  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },  		/* end commit message options */ @@ -1528,12 +1671,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  	const char *index_file, *reflog_msg;  	char *nl;  	unsigned char sha1[20]; -	struct ref_lock *ref_lock;  	struct commit_list *parents = NULL, **pptr = &parents;  	struct stat statbuf; -	int allow_fast_forward = 1;  	struct commit *current_head = NULL;  	struct commit_extra_header *extra = NULL; +	struct ref_transaction *transaction; +	struct strbuf err = STRBUF_INIT;  	if (argc == 2 && !strcmp(argv[1], "-h"))  		usage_with_options(builtin_commit_usage, builtin_commit_options); @@ -1579,6 +1722,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  	} else if (whence == FROM_MERGE) {  		struct strbuf m = STRBUF_INIT;  		FILE *fp; +		int allow_fast_forward = 1;  		if (!reflog_msg)  			reflog_msg = "commit (merge)"; @@ -1621,8 +1765,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  		die(_("could not read commit message: %s"), strerror(saved_errno));  	} -	/* Truncate the message just before the diff, if any. */ -	if (verbose) +	if (verbose || /* Truncate the message just before the diff, if any. */ +	    cleanup_mode == CLEANUP_SCISSORS)  		wt_status_truncate_message_at_cut_line(&sb);  	if (cleanup_mode != CLEANUP_NONE) @@ -1646,20 +1790,14 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  		append_merge_tag_headers(parents, &tail);  	} -	if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1, -				 author_ident.buf, sign_commit, extra)) { +	if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1, +			 parents, sha1, author_ident.buf, sign_commit, extra)) {  		rollback_index_files();  		die(_("failed to write commit object"));  	}  	strbuf_release(&author_ident);  	free_commit_extra_headers(extra); -	ref_lock = lock_any_ref_for_update("HEAD", -					   !current_head -					   ? NULL -					   : current_head->object.sha1, -					   0, NULL); -  	nl = strchr(sb.buf, '\n');  	if (nl)  		strbuf_setlen(&sb, nl + 1 - sb.buf); @@ -1668,14 +1806,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));  	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); -	if (!ref_lock) { -		rollback_index_files(); -		die(_("cannot lock HEAD ref")); -	} -	if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) { +	transaction = ref_transaction_begin(&err); +	if (!transaction || +	    ref_transaction_update(transaction, "HEAD", sha1, +				   current_head +				   ? current_head->object.sha1 : NULL, +				   0, !!current_head, &err) || +	    ref_transaction_commit(transaction, sb.buf, &err)) {  		rollback_index_files(); -		die(_("cannot update HEAD ref")); +		die("%s", err.buf);  	} +	ref_transaction_free(transaction);  	unlink(git_path("CHERRY_PICK_HEAD"));  	unlink(git_path("REVERT_HEAD")); @@ -1686,7 +1827,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  	if (commit_index_files())  		die (_("Repository has been updated, but unable to write\n" -		     "new_index file. Check that disk is not full or quota is\n" +		     "new_index file. Check that disk is not full and quota is\n"  		     "not exceeded, and then \"git reset HEAD\" to recover."));  	rerere(0); @@ -1704,5 +1845,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)  	if (!quiet)  		print_summary(prefix, sha1, !current_head); +	strbuf_release(&err);  	return 0;  } | 
