git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 2/9] built-in add -i: allow filtering the modified files list

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Nov 29, 2019, 21:11 UTC
Message-ID
<fab098d86eb47d8ef108e62080db87c1381b2e56.1575061909.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.171.v2.git.1575061909.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>

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>
---
 add-interactive.c | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/add-interactive.c b/add-interactive.c
index de2fccb0ef..c62d63e35b 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -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);
-- 
gitgitgadget
Previous: Johannes Schindelin via GitGitGadgetNext: Johannes Schindelin via GitGitGadget
Message 22 of 29 in “build-in add -i: implement all commands in the main loop”
  1. 0/8 build-in add -i: implement all commands in the main loopJohannes Schindelin via GitGitGadget, Nov 15, 2019
  2. 1/8 built-in add -i: allow filtering the modified files listJohannes Schindelin via GitGitGadget, Nov 15, 2019
  3. Junio C HamanoNov 21, 2019
  4. Johannes SchindelinNov 25, 2019
  5. 3/8 built-in add -i: implement the `update` commandJohannes Schindelin via GitGitGadget, Nov 15, 2019
  6. 5/8 built-in add -i: re-implement `add-untracked` in CJohannes Schindelin via GitGitGadget, Nov 15, 2019
  7. 4/8 built-in add -i: re-implement `revert` in CJohannes Schindelin via GitGitGadget, Nov 15, 2019
  8. 6/8 built-in add -i: implement the `patch` commandJohannes Schindelin via GitGitGadget, Nov 15, 2019
  9. 8/8 built-in add -i: offer the `quit` commandJohannes Schindelin via GitGitGadget, Nov 15, 2019
  10. 7/8 built-in add -i: re-implement the `diff` commandJohannes Schindelin via GitGitGadget, Nov 15, 2019
  11. 2/8 built-in add -i: prepare for multi-selection commandsJohannes Schindelin via GitGitGadget, Nov 15, 2019
  12. Junio C HamanoNov 21, 2019
  13. Johannes SchindelinNov 25, 2019
  14. Junio C HamanoNov 18, 2019
  15. Junio C HamanoNov 18, 2019
  16. Johannes SchindelinNov 18, 2019
  17. Junio C HamanoNov 18, 2019
  18. Johannes SchindelinNov 18, 2019
  19. Junio C HamanoNov 19, 2019
  20. 0/9 built-in add -i: implement all commands in the main loopJohannes Schindelin via GitGitGadget, Nov 29, 2019
  21. 1/9 add-interactive: make sure to release `rev.prune_data`Johannes Schindelin via GitGitGadget, Nov 29, 2019
  22. 2/9 built-in add -i: allow filtering the modified files listJohannes Schindelin via GitGitGadget, Nov 29, 2019
  23. 3/9 built-in add -i: prepare for multi-selection commandsJohannes Schindelin via GitGitGadget, Nov 29, 2019
  24. 4/9 built-in add -i: implement the `update` commandJohannes Schindelin via GitGitGadget, Nov 29, 2019
  25. 5/9 built-in add -i: re-implement `revert` in CJohannes Schindelin via GitGitGadget, Nov 29, 2019
  26. 7/9 built-in add -i: implement the `patch` commandJohannes Schindelin via GitGitGadget, Nov 29, 2019
  27. 8/9 built-in add -i: re-implement the `diff` commandJohannes Schindelin via GitGitGadget, Nov 29, 2019
  28. 9/9 built-in add -i: offer the `quit` commandJohannes Schindelin via GitGitGadget, Nov 29, 2019
  29. 6/9 built-in add -i: re-implement `add-untracked` in CJohannes Schindelin via GitGitGadget, Nov 29, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.