git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Daniel Ferreira <bnmvco@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, Johannes.Schindelin@gmx.de,
	Daniel Ferreira <bnmvco@gmail.com>
Subject: [PATCH 2/3] add--interactive: add builtin helper for interactive add
Date: Fri,  5 May 2017 15:43:39 -0300	[thread overview]
Message-ID: <1494009820-2090-3-git-send-email-bnmvco@gmail.com> (raw)
In-Reply-To: <1494009820-2090-1-git-send-email-bnmvco@gmail.com>

Create a builtin helper for git-add--interactive, which right now is
only able to reproduce git-add--interactive.perl's status_cmd()
function, providing a summarized diff numstat to the user.

This is the first step in an effort to convert git-add--interactive.perl
to a C builtin, in search for better portability, expressibility and
performance (specially on non-POSIX systems like Windows).

Additionally, an eventual complete port of git-add--interactive would
remove the last "big" Git script to have Perl as a dependency, allowing
most Git users to have a NOPERL build running without big losses.

Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>
---
 .gitignore                        |   1 +
 Makefile                          |   1 +
 builtin.h                         |   1 +
 builtin/add-interactive--helper.c | 258 ++++++++++++++++++++++++++++++++++++++
 git.c                             |   1 +
 5 files changed, 262 insertions(+)
 create mode 100644 builtin/add-interactive--helper.c

diff --git a/.gitignore b/.gitignore
index 833ef3b..0d6cfe4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
 /git
 /git-add
 /git-add--interactive
+/git-add-interactive--helper
 /git-am
 /git-annotate
 /git-apply
diff --git a/Makefile b/Makefile
index e35542e..842fce2 100644
--- a/Makefile
+++ b/Makefile
@@ -873,6 +873,7 @@ LIB_OBJS += xdiff-interface.o
 LIB_OBJS += zlib.o
 
 BUILTIN_OBJS += builtin/add.o
+BUILTIN_OBJS += builtin/add-interactive--helper.o
 BUILTIN_OBJS += builtin/am.o
 BUILTIN_OBJS += builtin/annotate.o
 BUILTIN_OBJS += builtin/apply.o
diff --git a/builtin.h b/builtin.h
index 9e4a898..3d6a0ab 100644
--- a/builtin.h
+++ b/builtin.h
@@ -30,6 +30,7 @@ extern int textconv_object(const char *path, unsigned mode, const struct object_
 extern int is_builtin(const char *s);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_add_interactive__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_am(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add-interactive--helper.c b/builtin/add-interactive--helper.c
new file mode 100644
index 0000000..97ca1b3
--- /dev/null
+++ b/builtin/add-interactive--helper.c
@@ -0,0 +1,258 @@
+#include "builtin.h"
+#include "color.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+
+#define ADD_INTERACTIVE_HEADER_INDENT "      "
+
+enum add_interactive_collection_mode {
+	COLLECTION_MODE_NONE,
+	COLLECTION_MODE_WORKTREE,
+	COLLECTION_MODE_INDEX
+};
+
+struct add_interactive_file_status {
+	int selected;
+
+	char path[PATH_MAX];
+
+	int lines_added_index;
+	int lines_deleted_index;
+	int lines_added_worktree;
+	int lines_deleted_worktree;
+};
+
+struct add_interactive_status {
+	enum add_interactive_collection_mode current_mode;
+
+	const char *reference;
+	struct pathspec pathspec;
+
+	int file_count;
+	struct add_interactive_file_status *files;
+};
+
+static int add_interactive_use_color = -1;
+enum color_add_interactive {
+	ADD_INTERACTIVE_PROMPT,
+	ADD_INTERACTIVE_HEADER,
+	ADD_INTERACTIVE_HELP,
+	ADD_INTERACTIVE_ERROR
+};
+
+static char add_interactive_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_BOLD_BLUE, /* Prompt */
+	GIT_COLOR_BOLD,      /* Header */
+	GIT_COLOR_BOLD_RED,  /* Help */
+	GIT_COLOR_BOLD_RED   /* Error */
+};
+
+static const char *add_interactive_get_color(enum color_add_interactive ix)
+{
+	if (want_color(add_interactive_use_color))
+		return add_interactive_colors[ix];
+	return "";
+}
+
+static int parse_add_interactive_color_slot(const char *slot)
+{
+	if (!strcasecmp(slot, "prompt"))
+		return ADD_INTERACTIVE_PROMPT;
+	if (!strcasecmp(slot, "header"))
+		return ADD_INTERACTIVE_HEADER;
+	if (!strcasecmp(slot, "help"))
+		return ADD_INTERACTIVE_HELP;
+	if (!strcasecmp(slot, "error"))
+		return ADD_INTERACTIVE_ERROR;
+
+	return -1;
+}
+
+static int git_add_interactive_config(const char *var,
+		const char *value, void *cbdata)
+{
+	const char *name;
+
+	if (!strcmp(var, "color.interactive")) {
+		add_interactive_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+
+	if (skip_prefix(var, "color.interactive", &name)) {
+		int slot = parse_add_interactive_color_slot(name);
+		if (slot < 0)
+			return 0;
+		if (!value)
+			return config_error_nonbool(var);
+		return color_parse(value, add_interactive_colors[slot]);
+	}
+
+	return git_default_config(var, value, cbdata);
+}
+
+static void add_interactive_status_collect_changed_cb(struct diff_queue_struct *q,
+					 struct diff_options *options,
+					 void *data)
+{
+	struct add_interactive_status *s = data;
+	struct diffstat_t stat;
+	int i, j;
+
+	if (!q->nr)
+		return;
+
+	memset(&stat, 0, sizeof(struct diffstat_t));
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p;
+		p = q->queue[i];
+		diff_flush_stat(p, options, &stat);
+	}
+
+	for (i = 0; i < stat.nr; i++) {
+		int file_index = s->file_count;
+		for (j = 0; j < s->file_count; j++) {
+			if (!strcmp(s->files[j].path, stat.files[i]->name)) {
+				file_index = j;
+				break;
+			}
+		}
+
+		if (file_index == s->file_count) {
+			s->file_count++;
+			s->files = realloc(s->files,
+					(q->nr + s->file_count) * sizeof(*s->files));
+			memset(&s->files[file_index], 0,
+					sizeof(struct add_interactive_file_status));
+		}
+
+		memcpy(s->files[file_index].path, stat.files[i]->name,
+				strlen(stat.files[i]->name) + 1);
+		if (s->current_mode == COLLECTION_MODE_WORKTREE) {
+			s->files[file_index].lines_added_worktree = stat.files[i]->added;
+			s->files[file_index].lines_deleted_worktree = stat.files[i]->deleted;
+		} else if (s->current_mode == COLLECTION_MODE_INDEX) {
+			s->files[file_index].lines_added_index = stat.files[i]->added;
+			s->files[file_index].lines_deleted_index = stat.files[i]->deleted;
+		}
+	}
+}
+
+static void add_interactive_status_collect_changes_worktree(struct add_interactive_status *s)
+{
+	struct rev_info rev;
+
+	s->current_mode = COLLECTION_MODE_WORKTREE;
+
+	init_revisions(&rev, NULL);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.max_count = 0;
+
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = add_interactive_status_collect_changed_cb;
+	rev.diffopt.format_callback_data = s;
+
+	run_diff_files(&rev, 0);
+}
+
+static void add_interactive_status_collect_changes_index(struct add_interactive_status *s)
+{
+	struct rev_info rev;
+	struct setup_revision_opt opt;
+
+	s->current_mode = COLLECTION_MODE_INDEX;
+
+	init_revisions(&rev, NULL);
+	memset(&opt, 0, sizeof(opt));
+	opt.def = s->reference;
+	setup_revisions(0, NULL, &rev, &opt);
+
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = add_interactive_status_collect_changed_cb;
+	rev.diffopt.format_callback_data = s;
+
+	run_diff_index(&rev, 1);
+}
+
+static void list_modified_into_status(struct add_interactive_status *s)
+{
+	add_interactive_status_collect_changes_worktree(s);
+	add_interactive_status_collect_changes_index(s);
+}
+
+static void print_modified(void)
+{
+	int i;
+	struct add_interactive_status s;
+	const char *modified_fmt = _("%12s %12s %s");
+	const char *header_color = add_interactive_get_color(ADD_INTERACTIVE_HEADER);
+	unsigned char sha1[20];
+
+	if (read_cache() < 0)
+		return;
+
+	s.current_mode = COLLECTION_MODE_NONE;
+	s.reference = !get_sha1("HEAD", sha1) ? "HEAD": EMPTY_TREE_SHA1_HEX;
+	s.file_count = 0;
+	s.files = NULL;
+	list_modified_into_status(&s);
+
+	printf(ADD_INTERACTIVE_HEADER_INDENT);
+	color_fprintf(stdout, header_color, modified_fmt, _("staged"),
+			_("unstaged"), _("path"));
+	printf("\n");
+
+	for (i = 0; i < s.file_count; i++) {
+		struct add_interactive_file_status f = s.files[i];
+		char selection = f.selected ? '*' : ' ';
+
+		char worktree_changes[50];
+		char index_changes[50];
+
+		if (f.lines_added_worktree != 0 || f.lines_deleted_worktree != 0)
+			snprintf(worktree_changes, 50, "+%d/-%d", f.lines_added_worktree,
+					f.lines_deleted_worktree);
+		else
+			snprintf(worktree_changes, 50, "%s", _("nothing"));
+
+		if (f.lines_added_index != 0 || f.lines_deleted_index != 0)
+			snprintf(index_changes, 50, "+%d/-%d", f.lines_added_index,
+					f.lines_deleted_index);
+		else
+			snprintf(index_changes, 50, "%s", _("unchanged"));
+
+		printf("%c%2d: ", selection, i + 1);
+		printf(modified_fmt, index_changes, worktree_changes, f.path);
+		printf("\n");
+	}
+}
+
+static void status_cmd(void)
+{
+	print_modified();
+}
+
+static const char add_interactive_helper_usage[] =
+"git add-interactive--helper <command>";
+
+int cmd_add_interactive__helper(int argc, const char **argv, const char *prefix)
+{
+	int i, found_opt = 0;
+
+	git_config(git_add_interactive_config, NULL);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (!strcmp(arg, "--status")) {
+			status_cmd();
+			found_opt = 1;
+		}
+	}
+
+	if (!found_opt)
+		usage(add_interactive_helper_usage);
+
+	return 0;
+}
diff --git a/git.c b/git.c
index 8ff44f0..796971e 100644
--- a/git.c
+++ b/git.c
@@ -391,6 +391,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
 static struct cmd_struct commands[] = {
 	{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+	{ "add-interactive--helper", cmd_add_interactive__helper, RUN_SETUP | NEED_WORK_TREE },
 	{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
 	{ "annotate", cmd_annotate, RUN_SETUP },
 	{ "apply", cmd_apply, RUN_SETUP_GENTLY },
-- 
2.7.4 (Apple Git-66)


  parent reply	other threads:[~2017-05-05 18:44 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-05 18:43 [PATCH 0/3] Port git-add--interactive.perl:status_cmd to C Daniel Ferreira
2017-05-05 18:43 ` [PATCH 1/3] diff: export diffstat interface Daniel Ferreira
2017-05-05 21:28   ` Johannes Schindelin
2017-05-05 18:43 ` Daniel Ferreira [this message]
2017-05-05 20:16   ` [PATCH 2/3] add--interactive: add builtin helper for interactive add Ævar Arnfjörð Bjarmason
2017-05-05 21:21     ` Johannes Schindelin
2017-05-05 22:09       ` Ævar Arnfjörð Bjarmason
2017-05-05 22:30   ` Johannes Schindelin
2017-05-05 22:49     ` Ævar Arnfjörð Bjarmason
2017-05-08 11:35       ` Johannes Schindelin
2017-05-05 23:13     ` Daniel Ferreira (theiostream)
2017-05-05 23:28       ` Ævar Arnfjörð Bjarmason
2017-05-08 12:15       ` Johannes Schindelin
2017-05-05 18:43 ` [PATCH 3/3] add--interactive: use add-interactive--helper for status_cmd Daniel Ferreira
2017-05-05 22:32   ` Johannes Schindelin
2017-05-05 19:31 ` [PATCH 0/3] Port git-add--interactive.perl:status_cmd to C Ævar Arnfjörð Bjarmason
2017-05-05 22:33   ` Johannes Schindelin
2017-05-05 22:35   ` Jonathan Nieder
2017-05-05 22:38 ` Johannes Schindelin
2017-05-05 23:37   ` Daniel Ferreira (theiostream)
2017-05-08 12:23     ` Johannes Schindelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1494009820-2090-3-git-send-email-bnmvco@gmail.com \
    --to=bnmvco@gmail.com \
    --cc=Johannes.Schindelin@gmx.de \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).