"e - manually edit the current hunk\n"
"p - print the current hunk\n"
"P - print the current hunk using the pager\n"
- "? - print help\n");
+ "> - go to the next file, roll over at the bottom\n"
+ "< - go to the previous file, roll over at the top\n"
+ "? - print help\n"
+ "HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n");
static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)
{
char ch;
int colored = !!s->colored.len, use_pager = 0;
enum prompt_mode_type prompt_mode_type;
+ int all_decided = 0;
struct file_diff *file_diff = s->file_diff + idx;
size_t patch_update_resp = idx;
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
ALLOW_SEARCH_AND_GOTO = 1 << 4,
ALLOW_SPLIT = 1 << 5,
- ALLOW_EDIT = 1 << 6
+ ALLOW_EDIT = 1 << 6,
+ ALLOW_GOTO_PREVIOUS_FILE = 1 << 7,
+ ALLOW_GOTO_NEXT_FILE = 1 << 8
} permitted = 0;
if (hunk_index >= file_diff->hunk_nr)
/* Everything decided? */
if (undecided_previous < 0 && undecided_next < 0 &&
hunk->use != UNDECIDED_HUNK) {
- patch_update_resp++;
- break;
+ if (!s->s.auto_advance)
+ all_decided = 1;
+ else {
+ patch_update_resp++;
+ break;
+ }
}
strbuf_reset(&s->buf);
if (file_diff->hunk_nr) {
permitted |= ALLOW_EDIT;
strbuf_addstr(&s->buf, ",e");
}
+ if (!s->s.auto_advance && s->file_diff_nr > 1) {
+ permitted |= ALLOW_GOTO_NEXT_FILE;
+ strbuf_addstr(&s->buf, ",>");
+ }
+ if (!s->s.auto_advance && s->file_diff_nr > 1) {
+ permitted |= ALLOW_GOTO_PREVIOUS_FILE;
+ strbuf_addstr(&s->buf, ",<");
+ }
strbuf_addstr(&s->buf, ",p,P");
}
if (file_diff->deleted)
} else if (ch == 'q') {
patch_update_resp = s->file_diff_nr;
break;
+ } else if (!s->s.auto_advance && s->answer.buf[0] == '>') {
+ if (permitted & ALLOW_GOTO_NEXT_FILE) {
+ if (patch_update_resp == s->file_diff_nr - 1)
+ patch_update_resp = 0;
+ else
+ patch_update_resp++;
+ break;
+ } else {
+ err(s, _("No next file"));
+ continue;
+ }
+ } else if (!s->s.auto_advance && s->answer.buf[0] == '<') {
+ if (permitted & ALLOW_GOTO_PREVIOUS_FILE) {
+ if (patch_update_resp == 0)
+ patch_update_resp = s->file_diff_nr - 1;
+ else
+ patch_update_resp--;
+ break;
+ } else {
+ err(s, _("No previous file"));
+ continue;
+ }
} else if (s->answer.buf[0] == 'K') {
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
hunk_index = dec_mod(hunk_index,
* commands shown in the prompt that are not
* always available.
*/
+ if (all_decided && !strncmp(p, "HUNKS SUMMARY", 13)) {
+ int total = file_diff->hunk_nr, used = 0, skipped = 0;
+
+ for (i = 0; i < file_diff->hunk_nr; i++) {
+ if (file_diff->hunk[i].use == USE_HUNK)
+ used += 1;
+ if (file_diff->hunk[i].use == SKIP_HUNK)
+ skipped += 1;
+ }
+ color_fprintf_ln(stdout, s->s.help_color, _(p),
+ total, used, skipped);
+ }
if (*p != '?' && !strchr(s->buf.buf, *p))
continue;
}
}
- apply_patch(s, file_diff);
+ if (s->s.auto_advance)
+ apply_patch(s, file_diff);
putchar('\n');
return patch_update_resp;
if ((i = patch_update_file(&s, i)) == s.file_diff_nr)
break;
}
+ if (!s.s.auto_advance)
+ for (i = 0; i < s.file_diff_nr; i++)
+ apply_patch(&s, s.file_diff + i);
if (s.file_diff_nr == 0)
err(&s, _("No changes."));
test_grep file out &&
test_grep ! file2 out
'
+for cmd in add checkout reset "stash save" "stash push"
+do
+ test_expect_success "$cmd rejects invalid --no-auto-advance options" '
+ test_must_fail git $cmd --no-auto-advance 2>actual &&
+ test_grep -E "requires .*--(interactive|patch)" actual
+ '
+done
+
+test_expect_success 'manual advance (">") moves to next file with --no-auto-advance' '
+ git reset --hard &&
+ echo line1 >first-file &&
+ echo line2 >second-file &&
+ git add -A &&
+ git commit -m initial >/dev/null 2>&1 &&
+ echo change_first >>first-file &&
+ echo change_second >>second-file &&
+
+ printf ">\nq\n" | git add -p --no-auto-advance >output.test 2>&1 &&
+ test_grep -E "(a|b)/second-file" output.test
+'
+
+test_expect_success 'select n on a hunk, go to another file, come back and change to y stages' '
+ git reset --hard &&
+ echo one >f1 &&
+ echo one >f2 &&
+ git add -A &&
+ git commit -m initial >/dev/null 2>&1 &&
+ echo change1 >>f1 &&
+ echo change2 >>f2 &&
+
+ printf "n\n>\n<\ny\nq\n" | git add -p --no-auto-advance >output.staged 2>&1 &&
+ git diff --cached --name-only >staged &&
+ test_grep -E "(a/f1)" output.staged
+'
+
+test_expect_success 'select y on a hunk, go to another file, come back and change to n does not stage' '
+ git reset --hard &&
+ echo one >f1 &&
+ echo one >f2 &&
+ git add -A &&
+ git commit -m initial >/dev/null 2>&1 &&
+ echo change1 >>f1 &&
+ echo change2 >>f2 &&
+
+ printf "y\n>\n<\nn\nq\n" | git add -p --no-auto-advance >output.unstaged 2>&1 &&
+ git diff --cached --name-only >staged &&
+ test_must_be_empty staged
+'
+
+test_expect_success 'deciding all hunks in a file does not auto advance' '
+ git reset --hard &&
+ echo line >stay &&
+ echo line >other &&
+ git add -A &&
+ git commit -m initial >/dev/null 2>&1 &&
+ echo change >>stay &&
+ echo change >>other &&
+ test_write_lines y | git add -p --no-auto-advance >raw-output 2>&1 &&
+ test_grep "(1/1) Stage this hunk (was: y)" raw-output &&
+ test_grep ! "diff --git a/stay b/stay" raw-output
+'
+test_expect_success 'HUNKS SUMMARY does not show in help text when there are undecided hunks' '
+ git reset --hard &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >f &&
+ git add f &&
+ git commit -m initial >/dev/null 2>&1 &&
+ test_write_lines 1 X 3 4 Y 6 7 Z 9 >f &&
+ test_write_lines s y n | git add -p --no-auto-advance >raw-nostat 2>&1 &&
+ test_grep ! "HUNKS SUMMARY - Hunks: " raw-nostat
+'
+
+test_expect_success 'help text shows HUNK SUMMARY when all hunks have been decided' '
+ git reset --hard &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >f2 &&
+ git add f2 &&
+ git commit -m initial >/dev/null 2>&1 &&
+ test_write_lines 1 X 3 4 Y 6 7 Z 9 >f2 &&
+ printf "s\ny\nn\ny\n?\n" | git add -p --no-auto-advance >raw-stat 2>&1 &&
+ test_grep "HUNKS SUMMARY - Hunks: 3, USE: 2, SKIP: 1" raw-stat
+'
+
+test_expect_success 'selective staging across multiple files with --no-advance' '
+ git reset --hard &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >a.file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >b.file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >c.file &&
+ git add -A &&
+ git commit -m initial >/dev/null 2>&1 &&
+ test_write_lines 1 A2 3 4 A5 6 7 8 9 >a.file &&
+ test_write_lines 1 2 B3 4 5 6 7 B8 9 >b.file &&
+ test_write_lines C1 2 3 4 5 C6 7 8 9 >c.file &&
+ printf "s\ny\nn\n>\ns\nn\ny\n>\ns\ny\ny\nq\n" | git add -p --no-auto-advance >output.index 2>&1 &&
+ git diff --cached >staged.diff &&
+ test_grep "+A2" staged.diff &&
+ test_grep ! "+A5" staged.diff &&
+ test_grep "+B8" staged.diff &&
+ test_grep ! "+B3" staged.diff &&
+ test_grep "+C1" staged.diff &&
+ test_grep "+C6" staged.diff
+'
test_done