static int patch_update_file()

in add-patch.c [1365:1689]


static int patch_update_file(struct add_p_state *s,
			     struct file_diff *file_diff)
{
	size_t hunk_index = 0;
	ssize_t i, undecided_previous, undecided_next;
	struct hunk *hunk;
	char ch;
	struct child_process cp = CHILD_PROCESS_INIT;
	int colored = !!s->colored.len, quit = 0;
	enum prompt_mode_type prompt_mode_type;
	enum {
		ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
		ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
		ALLOW_GOTO_NEXT_HUNK = 1 << 2,
		ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
		ALLOW_SEARCH_AND_GOTO = 1 << 4,
		ALLOW_SPLIT = 1 << 5,
		ALLOW_EDIT = 1 << 6
	} permitted = 0;

	/* Empty added files have no hunks */
	if (!file_diff->hunk_nr && !file_diff->added)
		return 0;

	strbuf_reset(&s->buf);
	render_diff_header(s, file_diff, colored, &s->buf);
	fputs(s->buf.buf, stdout);
	for (;;) {
		if (hunk_index >= file_diff->hunk_nr)
			hunk_index = 0;
		hunk = file_diff->hunk_nr
				? file_diff->hunk + hunk_index
				: &file_diff->head;
		undecided_previous = -1;
		undecided_next = -1;

		if (file_diff->hunk_nr) {
			for (i = hunk_index - 1; i >= 0; i--)
				if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
					undecided_previous = i;
					break;
				}

			for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
				if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
					undecided_next = i;
					break;
				}
		}

		/* Everything decided? */
		if (undecided_previous < 0 && undecided_next < 0 &&
		    hunk->use != UNDECIDED_HUNK)
			break;

		strbuf_reset(&s->buf);
		if (file_diff->hunk_nr) {
			render_hunk(s, hunk, 0, colored, &s->buf);
			fputs(s->buf.buf, stdout);

			strbuf_reset(&s->buf);
			if (undecided_previous >= 0) {
				permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
				strbuf_addstr(&s->buf, ",k");
			}
			if (hunk_index) {
				permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
				strbuf_addstr(&s->buf, ",K");
			}
			if (undecided_next >= 0) {
				permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
				strbuf_addstr(&s->buf, ",j");
			}
			if (hunk_index + 1 < file_diff->hunk_nr) {
				permitted |= ALLOW_GOTO_NEXT_HUNK;
				strbuf_addstr(&s->buf, ",J");
			}
			if (file_diff->hunk_nr > 1) {
				permitted |= ALLOW_SEARCH_AND_GOTO;
				strbuf_addstr(&s->buf, ",g,/");
			}
			if (hunk->splittable_into > 1) {
				permitted |= ALLOW_SPLIT;
				strbuf_addstr(&s->buf, ",s");
			}
			if (hunk_index + 1 > file_diff->mode_change &&
			    !file_diff->deleted) {
				permitted |= ALLOW_EDIT;
				strbuf_addstr(&s->buf, ",e");
			}
		}
		if (file_diff->deleted)
			prompt_mode_type = PROMPT_DELETION;
		else if (file_diff->added)
			prompt_mode_type = PROMPT_ADDITION;
		else if (file_diff->mode_change && !hunk_index)
			prompt_mode_type = PROMPT_MODE_CHANGE;
		else
			prompt_mode_type = PROMPT_HUNK;

		printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->s.prompt_color,
			      (uintmax_t)hunk_index + 1,
			      (uintmax_t)(file_diff->hunk_nr
						? file_diff->hunk_nr
						: 1));
		printf(_(s->mode->prompt_mode[prompt_mode_type]),
		       s->buf.buf);
		if (*s->s.reset_color)
			fputs(s->s.reset_color, stdout);
		fflush(stdout);
		if (read_single_character(s) == EOF)
			break;

		if (!s->answer.len)
			continue;
		ch = tolower(s->answer.buf[0]);
		if (ch == 'y') {
			hunk->use = USE_HUNK;
soft_increment:
			hunk_index = undecided_next < 0 ?
				file_diff->hunk_nr : undecided_next;
		} else if (ch == 'n') {
			hunk->use = SKIP_HUNK;
			goto soft_increment;
		} else if (ch == 'a') {
			if (file_diff->hunk_nr) {
				for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
					hunk = file_diff->hunk + hunk_index;
					if (hunk->use == UNDECIDED_HUNK)
						hunk->use = USE_HUNK;
				}
			} else if (hunk->use == UNDECIDED_HUNK) {
				hunk->use = USE_HUNK;
			}
		} else if (ch == 'd' || ch == 'q') {
			if (file_diff->hunk_nr) {
				for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
					hunk = file_diff->hunk + hunk_index;
					if (hunk->use == UNDECIDED_HUNK)
						hunk->use = SKIP_HUNK;
				}
			} else if (hunk->use == UNDECIDED_HUNK) {
				hunk->use = SKIP_HUNK;
			}
			if (ch == 'q') {
				quit = 1;
				break;
			}
		} else if (s->answer.buf[0] == 'K') {
			if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
				hunk_index--;
			else
				err(s, _("No previous hunk"));
		} else if (s->answer.buf[0] == 'J') {
			if (permitted & ALLOW_GOTO_NEXT_HUNK)
				hunk_index++;
			else
				err(s, _("No next hunk"));
		} else if (s->answer.buf[0] == 'k') {
			if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
				hunk_index = undecided_previous;
			else
				err(s, _("No previous hunk"));
		} else if (s->answer.buf[0] == 'j') {
			if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
				hunk_index = undecided_next;
			else
				err(s, _("No next hunk"));
		} else if (s->answer.buf[0] == 'g') {
			char *pend;
			unsigned long response;

			if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
				err(s, _("No other hunks to goto"));
				continue;
			}
			strbuf_remove(&s->answer, 0, 1);
			strbuf_trim(&s->answer);
			i = hunk_index - DISPLAY_HUNKS_LINES / 2;
			if (i < file_diff->mode_change)
				i = file_diff->mode_change;
			while (s->answer.len == 0) {
				i = display_hunks(s, file_diff, i);
				printf("%s", i < file_diff->hunk_nr ?
				       _("go to which hunk (<ret> to see "
					 "more)? ") : _("go to which hunk? "));
				fflush(stdout);
				if (strbuf_getline(&s->answer,
						   stdin) == EOF)
					break;
				strbuf_trim_trailing_newline(&s->answer);
			}

			strbuf_trim(&s->answer);
			response = strtoul(s->answer.buf, &pend, 10);
			if (*pend || pend == s->answer.buf)
				err(s, _("Invalid number: '%s'"),
				    s->answer.buf);
			else if (0 < response && response <= file_diff->hunk_nr)
				hunk_index = response - 1;
			else
				err(s, Q_("Sorry, only %d hunk available.",
					  "Sorry, only %d hunks available.",
					  file_diff->hunk_nr),
				    (int)file_diff->hunk_nr);
		} else if (s->answer.buf[0] == '/') {
			regex_t regex;
			int ret;

			if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
				err(s, _("No other hunks to search"));
				continue;
			}
			strbuf_remove(&s->answer, 0, 1);
			strbuf_trim_trailing_newline(&s->answer);
			if (s->answer.len == 0) {
				printf("%s", _("search for regex? "));
				fflush(stdout);
				if (strbuf_getline(&s->answer,
						   stdin) == EOF)
					break;
				strbuf_trim_trailing_newline(&s->answer);
				if (s->answer.len == 0)
					continue;
			}
			ret = regcomp(&regex, s->answer.buf,
				      REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
			if (ret) {
				char errbuf[1024];

				regerror(ret, &regex, errbuf, sizeof(errbuf));
				err(s, _("Malformed search regexp %s: %s"),
				    s->answer.buf, errbuf);
				continue;
			}
			i = hunk_index;
			for (;;) {
				/* render the hunk into a scratch buffer */
				render_hunk(s, file_diff->hunk + i, 0, 0,
					    &s->buf);
				if (regexec(&regex, s->buf.buf, 0, NULL, 0)
				    != REG_NOMATCH)
					break;
				i++;
				if (i == file_diff->hunk_nr)
					i = 0;
				if (i != hunk_index)
					continue;
				err(s, _("No hunk matches the given pattern"));
				break;
			}
			hunk_index = i;
		} else if (s->answer.buf[0] == 's') {
			size_t splittable_into = hunk->splittable_into;
			if (!(permitted & ALLOW_SPLIT))
				err(s, _("Sorry, cannot split this hunk"));
			else if (!split_hunk(s, file_diff,
					     hunk - file_diff->hunk))
				color_fprintf_ln(stdout, s->s.header_color,
						 _("Split into %d hunks."),
						 (int)splittable_into);
		} else if (s->answer.buf[0] == 'e') {
			if (!(permitted & ALLOW_EDIT))
				err(s, _("Sorry, cannot edit this hunk"));
			else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
				hunk->use = USE_HUNK;
				goto soft_increment;
			}
		} else {
			const char *p = _(help_patch_remainder), *eol = p;

			color_fprintf(stdout, s->s.help_color, "%s",
				      _(s->mode->help_patch_text));

			/*
			 * Show only those lines of the remainder that are
			 * actually applicable with the current hunk.
			 */
			for (; *p; p = eol + (*eol == '\n')) {
				eol = strchrnul(p, '\n');

				/*
				 * `s->buf` still contains the part of the
				 * commands shown in the prompt that are not
				 * always available.
				 */
				if (*p != '?' && !strchr(s->buf.buf, *p))
					continue;

				color_fprintf_ln(stdout, s->s.help_color,
						 "%.*s", (int)(eol - p), p);
			}
		}
	}

	/* Any hunk to be used? */
	for (i = 0; i < file_diff->hunk_nr; i++)
		if (file_diff->hunk[i].use == USE_HUNK)
			break;

	if (i < file_diff->hunk_nr ||
	    (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
		/* At least one hunk selected: apply */
		strbuf_reset(&s->buf);
		reassemble_patch(s, file_diff, 0, &s->buf);

		discard_index(s->s.r->index);
		if (s->mode->apply_for_checkout)
			apply_for_checkout(s, &s->buf,
					   s->mode->is_reverse);
		else {
			setup_child_process(s, &cp, "apply", NULL);
			strvec_pushv(&cp.args, s->mode->apply_args);
			if (pipe_command(&cp, s->buf.buf, s->buf.len,
					 NULL, 0, NULL, 0))
				error(_("'git apply' failed"));
		}
		if (repo_read_index(s->s.r) >= 0)
			repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
						     1, NULL, NULL, NULL);
	}

	putchar('\n');
	return quit;
}