@matheusbsbs / matheus-bagliano / commits / c08171d156
Fork 0

built-in add -i: allow filtering the modified files list

In the `update` command of `git add -i`, we are primarily interested in the list of modified files that have worktree (i.e. unstaged) changes. At the same time, we need to determine _also_ the staged changes, to be able to produce the full added/deleted information. The Perl script version of `git add -i` has a parameter of the `list_modified()` function for that matter. In C, we can be a lot more precise, using an `enum`. The C implementation of the filter also has an easier time to avoid unnecessary work, simply by using an adaptive order of the `diff-index` and `diff-files` phases, and then skipping files in the second phase when they have not been seen in the first phase. Seeing as we change the meaning of the `phase` field, we rename it to `mode` to reflect that the order depends on the exact invocation of the `git add -i` command. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Nov 29, 2019 at 21:11 UTC c08171d156f6f77bdab6500f0b433b650a31ccae
1 file changed +26 -7
add-interactive.c
+26 -7
@@ -344,10 +344,11 @@ static int pathname_entry_cmp(const void *unused_cmp_data,
344 }
345
346 struct collection_status {
347 - enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } phase;
347 + enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } mode;
348
349 const char *reference;
350
351 + unsigned skip_unseen:1;
352 struct string_list *files;
353 struct hashmap file_map;
354 };
@@ -375,6 +376,9 @@ static void collect_changes_cb(struct diff_queue_struct *q,
376 entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
377 struct pathname_entry, ent);
378 if (!entry) {
379 + if (s->skip_unseen)
380 + continue;
381 +
382 add_file_item(s->files, name);
383
384 entry = xcalloc(sizeof(*entry), 1);
@@ -385,7 +389,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
389 }
390
391 file_item = entry->item;
388 - adddel = s->phase == FROM_INDEX ?
392 + adddel = s->mode == FROM_INDEX ?
393 &file_item->index : &file_item->worktree;
394 adddel->seen = 1;
395 adddel->add = stat.files[i]->added;
@@ -396,13 +400,22 @@ static void collect_changes_cb(struct diff_queue_struct *q,
400 free_diffstat_info(&stat);
401 }
402
399 -static int get_modified_files(struct repository *r, struct string_list *files,
403 +enum modified_files_filter {
404 + NO_FILTER = 0,
405 + WORKTREE_ONLY = 1,
406 + INDEX_ONLY = 2,
407 +};
408 +
409 +static int get_modified_files(struct repository *r,
410 + enum modified_files_filter filter,
411 + struct string_list *files,
412 const struct pathspec *ps)
413 {
414 struct object_id head_oid;
415 int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
416 &head_oid, NULL);
405 - struct collection_status s = { FROM_WORKTREE };
417 + struct collection_status s = { 0 };
418 + int i;
419
420 if (discard_index(r->index) < 0 ||
421 repo_read_index_preload(r, ps, 0) < 0)
@@ -412,10 +425,16 @@ static int get_modified_files(struct repository *r, struct string_list *files,
425 s.files = files;
426 hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
427
415 - for (s.phase = FROM_WORKTREE; s.phase <= FROM_INDEX; s.phase++) {
428 + for (i = 0; i < 2; i++) {
429 struct rev_info rev;
430 struct setup_revision_opt opt = { 0 };
431
432 + if (filter == INDEX_ONLY)
433 + s.mode = (i == 0) ? FROM_INDEX : FROM_WORKTREE;
434 + else
435 + s.mode = (i == 0) ? FROM_WORKTREE : FROM_INDEX;
436 + s.skip_unseen = filter && i;
437 +
438 opt.def = is_initial ?
439 empty_tree_oid_hex() : oid_to_hex(&head_oid);
440
@@ -429,7 +448,7 @@ static int get_modified_files(struct repository *r, struct string_list *files,
448 if (ps)
449 copy_pathspec(&rev.prune_data, ps);
450
432 - if (s.phase == FROM_INDEX)
451 + if (s.mode == FROM_INDEX)
452 run_diff_index(&rev, 1);
453 else {
454 rev.diffopt.flags.ignore_dirty_submodules = 1;
@@ -502,7 +521,7 @@ static void print_file_item(int i, struct string_list_item *item,
521 static int run_status(struct add_i_state *s, const struct pathspec *ps,
522 struct string_list *files, struct list_options *opts)
523 {
505 - if (get_modified_files(s->r, files, ps) < 0)
524 + if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
525 return -1;
526
527 list(s, files, opts);