]> git.ipfire.org Git - thirdparty/git.git/commitdiff
built-in add -i: allow filtering the modified files list
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 29 Nov 2019 21:11:42 +0000 (21:11 +0000)
committerJunio C Hamano <gitster@pobox.com>
Sun, 1 Dec 2019 15:30:54 +0000 (07:30 -0800)
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>
add-interactive.c

index de2fccb0ef338273ed600eec6d2f755b278b87e3..c62d63e35b85ce5a5d4edc80245d26b9f7ea344e 100644 (file)
@@ -344,10 +344,11 @@ static int pathname_entry_cmp(const void *unused_cmp_data,
 }
 
 struct collection_status {
-       enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } phase;
+       enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } mode;
 
        const char *reference;
 
+       unsigned skip_unseen:1;
        struct string_list *files;
        struct hashmap file_map;
 };
@@ -375,6 +376,9 @@ static void collect_changes_cb(struct diff_queue_struct *q,
                entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
                                                    struct pathname_entry, ent);
                if (!entry) {
+                       if (s->skip_unseen)
+                               continue;
+
                        add_file_item(s->files, name);
 
                        entry = xcalloc(sizeof(*entry), 1);
@@ -385,7 +389,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
                }
 
                file_item = entry->item;
-               adddel = s->phase == FROM_INDEX ?
+               adddel = s->mode == FROM_INDEX ?
                        &file_item->index : &file_item->worktree;
                adddel->seen = 1;
                adddel->add = stat.files[i]->added;
@@ -396,13 +400,22 @@ static void collect_changes_cb(struct diff_queue_struct *q,
        free_diffstat_info(&stat);
 }
 
-static int get_modified_files(struct repository *r, struct string_list *files,
+enum modified_files_filter {
+       NO_FILTER = 0,
+       WORKTREE_ONLY = 1,
+       INDEX_ONLY = 2,
+};
+
+static int get_modified_files(struct repository *r,
+                             enum modified_files_filter filter,
+                             struct string_list *files,
                              const struct pathspec *ps)
 {
        struct object_id head_oid;
        int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
                                             &head_oid, NULL);
-       struct collection_status s = { FROM_WORKTREE };
+       struct collection_status s = { 0 };
+       int i;
 
        if (discard_index(r->index) < 0 ||
            repo_read_index_preload(r, ps, 0) < 0)
@@ -412,10 +425,16 @@ static int get_modified_files(struct repository *r, struct string_list *files,
        s.files = files;
        hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0);
 
-       for (s.phase = FROM_WORKTREE; s.phase <= FROM_INDEX; s.phase++) {
+       for (i = 0; i < 2; i++) {
                struct rev_info rev;
                struct setup_revision_opt opt = { 0 };
 
+               if (filter == INDEX_ONLY)
+                       s.mode = (i == 0) ? FROM_INDEX : FROM_WORKTREE;
+               else
+                       s.mode = (i == 0) ? FROM_WORKTREE : FROM_INDEX;
+               s.skip_unseen = filter && i;
+
                opt.def = is_initial ?
                        empty_tree_oid_hex() : oid_to_hex(&head_oid);
 
@@ -429,7 +448,7 @@ static int get_modified_files(struct repository *r, struct string_list *files,
                if (ps)
                        copy_pathspec(&rev.prune_data, ps);
 
-               if (s.phase == FROM_INDEX)
+               if (s.mode == FROM_INDEX)
                        run_diff_index(&rev, 1);
                else {
                        rev.diffopt.flags.ignore_dirty_submodules = 1;
@@ -502,7 +521,7 @@ static void print_file_item(int i, struct string_list_item *item,
 static int run_status(struct add_i_state *s, const struct pathspec *ps,
                      struct string_list *files, struct list_options *opts)
 {
-       if (get_modified_files(s->r, files, ps) < 0)
+       if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
                return -1;
 
        list(s, files, opts);