]>
git.ipfire.org Git - thirdparty/git.git/blob - add-patch.c
2 #include "add-interactive.h"
4 #include "run-command.h"
5 #include "argv-array.h"
10 enum { UNDECIDED_HUNK
= 0, SKIP_HUNK
, USE_HUNK
} use
;
15 struct strbuf answer
, buf
;
21 size_t hunk_nr
, hunk_alloc
;
24 static void setup_child_process(struct add_p_state
*s
,
25 struct child_process
*cp
, ...)
31 while ((arg
= va_arg(ap
, const char *)))
32 argv_array_push(&cp
->args
, arg
);
36 argv_array_pushf(&cp
->env_array
,
37 INDEX_ENVIRONMENT
"=%s", s
->r
->index_file
);
40 static int parse_diff(struct add_p_state
*s
, const struct pathspec
*ps
)
42 struct strbuf
*plain
= &s
->plain
;
43 struct child_process cp
= CHILD_PROCESS_INIT
;
46 struct hunk
*hunk
= NULL
;
49 /* Use `--no-color` explicitly, just in case `diff.color = always`. */
50 setup_child_process(s
, &cp
,
51 "diff-files", "-p", "--no-color", "--", NULL
);
52 for (i
= 0; i
< ps
->nr
; i
++)
53 argv_array_push(&cp
.args
, ps
->items
[i
].original
);
55 res
= capture_command(&cp
, plain
, 0);
57 return error(_("could not parse diff"));
60 strbuf_complete_line(plain
);
64 pend
= p
+ plain
->len
;
66 char *eol
= memchr(p
, '\n', pend
- p
);
70 if (starts_with(p
, "diff ")) {
72 BUG("multi-file diff not yet handled");
74 } else if (p
== plain
->buf
)
75 BUG("diff starts with unexpected line:\n"
76 "%.*s\n", (int)(eol
- p
), p
);
77 else if (starts_with(p
, "@@ ")) {
79 ALLOC_GROW(s
->hunk
, s
->hunk_nr
,
81 hunk
= s
->hunk
+ s
->hunk_nr
- 1;
82 memset(hunk
, 0, sizeof(*hunk
));
84 hunk
->start
= p
- plain
->buf
;
87 p
= eol
== pend
? pend
: eol
+ 1;
88 hunk
->end
= p
- plain
->buf
;
94 static void render_hunk(struct add_p_state
*s
, struct hunk
*hunk
,
97 strbuf_add(out
, s
->plain
.buf
+ hunk
->start
,
98 hunk
->end
- hunk
->start
);
101 static void reassemble_patch(struct add_p_state
*s
, struct strbuf
*out
)
106 render_hunk(s
, &s
->head
, out
);
108 for (i
= 0; i
< s
->hunk_nr
; i
++) {
110 if (hunk
->use
== USE_HUNK
)
111 render_hunk(s
, hunk
, out
);
115 static const char help_patch_text
[] =
116 N_("y - stage this hunk\n"
117 "n - do not stage this hunk\n"
118 "a - stage this and all the remaining hunks\n"
119 "d - do not stage this hunk nor any of the remaining hunks\n"
120 "j - leave this hunk undecided, see next undecided hunk\n"
121 "J - leave this hunk undecided, see next hunk\n"
122 "k - leave this hunk undecided, see previous undecided hunk\n"
123 "K - leave this hunk undecided, see previous hunk\n"
126 static int patch_update_file(struct add_p_state
*s
)
128 size_t hunk_index
= 0;
129 ssize_t i
, undecided_previous
, undecided_next
;
132 struct child_process cp
= CHILD_PROCESS_INIT
;
137 strbuf_reset(&s
->buf
);
138 render_hunk(s
, &s
->head
, &s
->buf
);
139 fputs(s
->buf
.buf
, stdout
);
141 if (hunk_index
>= s
->hunk_nr
)
143 hunk
= s
->hunk
+ hunk_index
;
145 undecided_previous
= -1;
146 for (i
= hunk_index
- 1; i
>= 0; i
--)
147 if (s
->hunk
[i
].use
== UNDECIDED_HUNK
) {
148 undecided_previous
= i
;
153 for (i
= hunk_index
+ 1; i
< s
->hunk_nr
; i
++)
154 if (s
->hunk
[i
].use
== UNDECIDED_HUNK
) {
159 /* Everything decided? */
160 if (undecided_previous
< 0 && undecided_next
< 0 &&
161 hunk
->use
!= UNDECIDED_HUNK
)
164 strbuf_reset(&s
->buf
);
165 render_hunk(s
, hunk
, &s
->buf
);
166 fputs(s
->buf
.buf
, stdout
);
168 strbuf_reset(&s
->buf
);
169 if (undecided_previous
>= 0)
170 strbuf_addstr(&s
->buf
, ",k");
172 strbuf_addstr(&s
->buf
, ",K");
173 if (undecided_next
>= 0)
174 strbuf_addstr(&s
->buf
, ",j");
175 if (hunk_index
+ 1 < s
->hunk_nr
)
176 strbuf_addstr(&s
->buf
, ",J");
177 printf("(%"PRIuMAX
"/%"PRIuMAX
") ",
178 (uintmax_t)hunk_index
+ 1, (uintmax_t)s
->hunk_nr
);
179 printf(_("Stage this hunk [y,n,a,d%s,?]? "), s
->buf
.buf
);
181 if (strbuf_getline(&s
->answer
, stdin
) == EOF
)
183 strbuf_trim_trailing_newline(&s
->answer
);
187 ch
= tolower(s
->answer
.buf
[0]);
189 hunk
->use
= USE_HUNK
;
191 hunk_index
= undecided_next
< 0 ?
192 s
->hunk_nr
: undecided_next
;
193 } else if (ch
== 'n') {
194 hunk
->use
= SKIP_HUNK
;
196 } else if (ch
== 'a') {
197 for (; hunk_index
< s
->hunk_nr
; hunk_index
++) {
198 hunk
= s
->hunk
+ hunk_index
;
199 if (hunk
->use
== UNDECIDED_HUNK
)
200 hunk
->use
= USE_HUNK
;
202 } else if (ch
== 'd') {
203 for (; hunk_index
< s
->hunk_nr
; hunk_index
++) {
204 hunk
= s
->hunk
+ hunk_index
;
205 if (hunk
->use
== UNDECIDED_HUNK
)
206 hunk
->use
= SKIP_HUNK
;
208 } else if (hunk_index
&& s
->answer
.buf
[0] == 'K')
210 else if (hunk_index
+ 1 < s
->hunk_nr
&&
211 s
->answer
.buf
[0] == 'J')
213 else if (undecided_previous
>= 0 &&
214 s
->answer
.buf
[0] == 'k')
215 hunk_index
= undecided_previous
;
216 else if (undecided_next
>= 0 && s
->answer
.buf
[0] == 'j')
217 hunk_index
= undecided_next
;
219 puts(_(help_patch_text
));
222 /* Any hunk to be used? */
223 for (i
= 0; i
< s
->hunk_nr
; i
++)
224 if (s
->hunk
[i
].use
== USE_HUNK
)
227 if (i
< s
->hunk_nr
) {
228 /* At least one hunk selected: apply */
229 strbuf_reset(&s
->buf
);
230 reassemble_patch(s
, &s
->buf
);
232 discard_index(s
->r
->index
);
233 setup_child_process(s
, &cp
, "apply", "--cached", NULL
);
234 if (pipe_command(&cp
, s
->buf
.buf
, s
->buf
.len
,
236 error(_("'git apply --cached' failed"));
237 if (!repo_read_index(s
->r
))
238 repo_refresh_and_write_index(s
->r
, REFRESH_QUIET
, 0,
239 1, NULL
, NULL
, NULL
);
246 int run_add_p(struct repository
*r
, const struct pathspec
*ps
)
248 struct add_p_state s
= { r
, STRBUF_INIT
, STRBUF_INIT
, STRBUF_INIT
};
250 if (discard_index(r
->index
) < 0 || repo_read_index(r
) < 0 ||
251 repo_refresh_and_write_index(r
, REFRESH_QUIET
, 0, 1,
252 NULL
, NULL
, NULL
) < 0 ||
253 parse_diff(&s
, ps
) < 0) {
254 strbuf_release(&s
.plain
);
259 patch_update_file(&s
);
261 strbuf_release(&s
.answer
);
262 strbuf_release(&s
.buf
);
263 strbuf_release(&s
.plain
);