Subject: Re: Fwd: git status options feature suggestion

From: Junio C Hamano <[email-masked]>
Date: Sun, 12 Oct 2008 01:10:01 -0700

Jeff King <[email-masked]> writes:

> I remember a long time ago you started on a parallel diff walker that
> could diff the working tree, the index, and a tree at once. Do you
> remember the issues with it?

Sorry, I don't.

> I think that would be the right tool here to show each file only once,
> but with multiple status flags. Something like:
>
>   A M foo
>
> to show that "foo" has been added since the last commit, but there are
> modifications in the working tree that have not yet been staged.

One thing to keep in mind is what to do when you would want to detect
renames.  The parallel walk would be Ok but between HEAD and index there
could be renames involved, and at that point it would get quite tricky.
Once the index is in-core, I think it hurts us much to walk HEAD vs index
and index vs working tree in separate passes.

I think it is perfectly fine to run the diff-index first, and keep the
result from it in a string_list, and then run "diff-files" and annotate
the string_list with the output from it.

Something like...

	struct git_st_data {
        	const char *head_path;
                char head_to_index_status;
                char index_to_worktree_status;
	};

	static int cmp_head_path(const void *a_, const void *b_)
        {
		struct git_st_data *a = (struct git_st_data *)a_;
		struct git_st_data *b = (struct git_st_data *)b_;
		return strcmp(a->head_path, b->head_path);
        }

	static void git_st_inspect_index_cb(struct diff_queue_struct *q,
        			struct diff_options *opts, void *data)
	{
		struct string_list *git_st_list = data;
		int i;
		for (i = 0; i < q->nr; i++) {
                	struct git_st_data *d;
                        struct string_list_item *e;
			struct diff_filepair *fp = &q->queue[i];
			d = xcalloc(1, sizeof(*d));
                        d->head_path = xstrdup(fp->one->path);
                        e = string_list_insert(fp->two->path, git_st_list);
			e->util = d;
                        d->head_to_index_status = fp->status;
                }
	}

	static void git_st_inspect_file_cb(struct diff_queue_struct *q,
        			struct diff_options *opts, void *data)
	{
		struct string_list *git_st_list = data;
		int i;
		for (i = 0; i < q->nr; i++) {
                	struct git_st_data *d;
                        struct string_list_item *e;
			struct diff_filepair *fp = &q->queue[i];
			e = string_list_lookup(fp->one->path, git_st_list);
			if (!e)
                        	die("Oops -- shouldn't happen");
			d = e->util;
                        d->index_to_worktree_status = fp->status;
                }
	}

	void git_st_inspect(struct string_list *git_st_list)
        {
        	struct rev_info rev;

		git_st_list->items = NULL;
                git_st_list->nr = git_st_list->alloc = 0;
                git_st_list->strdup_strings = 1;

		/*
                 * run "diff-index -B -M HEAD" and keep the result in a
                 * string list, keyed by the path in the index.
                 */
                init_revisions(&rev, NULL);
                setup_revisions(0, NULL, &rev, "HEAD");
                rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                rev.diffopt.format_callback = git_st_inspect_index_cb;
                rev.diffopt.format_callback_data = git_st_list;
                rev.diffopt.detect_rename = 1;
                rev.diffopt.rename_limit = 200;
                rev.diffopt.break_opt = 0;
                run_diff_index(&rev, 1);

		/*
                 * run "diff-files" and update the previous with the result.
		 */
		init_revisions(&rev, NULL);
                setup_revisions(0, NULL, &rev, NULL);
                rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                rev.diffopt.format_callback = git_st_inspect_file_cb;                
                rev.diffopt.format_callback_data = git_st_list;
                run_diff_files(&rev, 0);

		/*
                 * sort the string-list entries in HEAD path order
                 */
		qsort(git_st_list->items, git_st_list->nr,
                      sizeof(struct string_list_item),
                      cmp_head_path);
	}

Then git_st_inspect() can also be called by wt_status_print(), making it
unnecessary to do the equivalent of the above in wt_status_print_updated()
and wt_status_print_changed().