threads / patch / 20399

patch, 6 partsTeach --id/-d to "git status"

Subject: [PATCH 1/6] Teach --id/-d to "git status"

## tl;dr

17 messages between Aug 5, 2009 and Aug 5, 2009. Diffs are folded; open one to read it.

replies: 16people: 8as markdown or json

Nazri Ramliy· Aug 5, 2009, 09:51 UTC · lore

[PATCH] RFC - Say goodbye to the rodent

Hello list,
Git status provides a list of modified/staged files etc.

Normally I use the mouse to cut and paste the filenames in subsequent git operations.

Oftentimes I get tired of moving my hand away from the comfort of the home rows of my keybard in order to grab the mouse to highlight the files that I'd like to operate on.

This patch is my attempt at scratching this itch. It is, at best, works fine; albeit a little bit experimental. I may have overlooked a dozen things - variable names, places where I define the variables etc. If people find this useful I'll look into polishing the code. All seem to work well. Running make test does not break anything. I have not written test case for them.

The idea is that "git status --id" shows a unique id for each file in its output (modified/staged/unknown/etc). The ids and the corresponding filenames are stored in .git/FILE_IDS. This file gets overwritten everytime you run "git status --id"

Subsequent git operations can be taught to accept the --id command line option to tell them what files to operate on. They will read the FILE_IDS to find out which file to operate based on the given id. In the patch series I have taught the add, checkout, commit, rm and reset commands the --id option.

Example:

$ git status --id # On branch local # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: builtin-write-tree.c (m1) # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # file1.c (x1) # file2.c (x2)

Note the id at the end of the files.
Now we can do the following:
$ git add --id m1

Ids are specified via the --id command line option, and can be separated with commas if you want to specify more than one of them:

$ git add --id m1,x2
You get the idea ...

What do you guys think about this new approach of "cut-and-paste" from the command line?

Where would be the best place to put the file id? end? beginning? before ':'? The argument to --id could be improved so that it understand regexes for file ids.

Comments are welcomed.
Nazri.
-- 
 builtin-add.c      |   19 +++++--
 builtin-checkout.c |   26 ++++++---
 builtin-commit.c   |    4 +-
 builtin-reset.c    |   32 +++++++----
 builtin-rm.c       |   11 ++++-
 cache.h            |    1 +
 path.c             |   44 ++++++++++++++++
 wt-status.c        |  146 ++++++++++++++++++++++++++++++++++++++++++++++++----
 wt-status.h        |    1 +
 9 files changed, 245 insertions(+), 39 deletions(-)
Nazri Ramliy· Aug 5, 2009, 09:51 UTC · re: Nazri Ramliy · lore
This patch adds a unique identifier for each file shown by "git status".

Subsequent operation (add/rm/checkout/commit/reset) may use these ids to simplify specifying the files, provided that they are taught about the --id option as well.

Example:
  $ git status --id
  # On branch local
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
  #   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #	modified:   builtin-add.c (m1)
  #	modified:   builtin-checkout.c (m2)
  #	modified:   builtin-commit.c (m3)
  #	modified:   builtin-reset.c (m4)
  #	modified:   builtin-rm.c (m5)
  #
  # Untracked files:
  #   (use "git add <file>..." to include in what will be committed)
  #
  #	makefile.debug (x1)
  #	nazri.sh (x2)
  no changes added to commit (use "git add" and/or "git commit -a")
  $ git add --id m1
  # Short for "git add builtin-add.c
  $ git add --id m3,m5
  # Short for "git add builtin-commit.c builtin-rm.c"
  # Even shorter: -d is equivalent to --id
  $ git add -d m3,m5
Signed-off-by: Nazri Ramliy <ayiehere@gmail.com>
---
 cache.h     |    1 +
 path.c      |   44 ++++++++++++++++++
 wt-status.c |  146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 wt-status.h |    1 +
 4 files changed, 181 insertions(+), 11 deletions(-)
Show changes to 4 files +181 −11

cache.h, path.c, wt-status.c, wt-status.h

diff --git a/cache.h b/cache.h
index e6c7f33..05817e3 100644
--- a/cache.h
+++ b/cache.h
@@ -649,6 +649,7 @@ const char *make_relative_path(const char *abs, const char *base);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, const char *prefix_list);
 char *strip_path_suffix(const char *path, const char *suffix);
+char ** expand_file_ids(const char *file_id, int *argc, char **argv);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
diff --git a/path.c b/path.c
index 047fdb0..7824c4a 100644
--- a/path.c
+++ b/path.c
@@ -564,3 +564,47 @@ char *strip_path_suffix(const char *path, const char *suffix)
 		return NULL;
 	return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
 }
+
+char ** expand_file_ids(const char *file_id, int *argc, char **argv)
+{
+	FILE *fp;
+	const char *filename = git_path("FILE_IDS");
+	struct strbuf sb = STRBUF_INIT;
+	int i;
+
+	char **new_argv;
+
+	new_argv = (char **) xmalloc(*argc * sizeof(char *));
+	for (i = 0; i < *argc; i++) {
+		new_argv[i] = (char *) argv[i];
+	}
+
+	fp = fopen(filename, "r");
+	if (!fp)
+		die("cannot open %s: %s\n", filename, strerror(errno));
+
+	while (strbuf_getline(&sb, fp, '\n') == 0) {
+		char *b, *e;
+		if (sb.len == 0)
+			continue;
+		b = e = sb.buf;
+		while(*e && *e != ' ')
+			e++;
+		*e = '\0';
+		if(strstr(file_id, b)) {
+			 (*argc)++;
+			 new_argv = xrealloc(new_argv, (*argc) * sizeof(char *));
+			 new_argv[*argc - 1] = xstrdup(e+1);
+		}
+	}
+	strbuf_release(&sb);
+
+	/* 
+	 * parse_options adds a NULL terminator at end of argv. Do the same for
+	 * new_argv.
+	 */
+	new_argv = xrealloc(new_argv, *argc + 1 * sizeof(char *));
+	new_argv[*argc] = NULL;
+
+	return new_argv;
+}
diff --git a/wt-status.c b/wt-status.c
index 47735d8..6013fbd 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -96,6 +96,106 @@ static void wt_status_print_trailer(struct wt_status *s)
 	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 }
 
+struct id_list {
+	struct id_list *next;
+	char status;
+	int nr;
+	char *name;
+};
+
+static struct id_list *wt_files = NULL;
+static struct id_list *wt_files_last = NULL;
+static int n_id_list = 0;
+
+static int get_id_list_id(char status)
+{
+	static int added = 0;
+	static int copied = 0;
+	static int deleted = 0;
+	static int modified = 0;
+	static int renamed = 0;
+	static int type_changed = 0;
+	static int unknown = 0;
+	static int unmerged = 0;
+
+	switch(status) {
+	case DIFF_STATUS_ADDED:
+		return ++added;
+	case DIFF_STATUS_COPIED:
+		return ++copied;
+	case DIFF_STATUS_DELETED:
+		return ++deleted;
+	case DIFF_STATUS_MODIFIED:
+		return ++modified;
+	case DIFF_STATUS_RENAMED:
+		return ++renamed;
+	case DIFF_STATUS_TYPE_CHANGED:
+		return ++type_changed;
+	case DIFF_STATUS_UNKNOWN:
+		return ++unknown;
+	case DIFF_STATUS_UNMERGED:
+		return ++unmerged;
+	}
+	die("bug: unhandled diff status %c", status);
+}
+
+static int add_to_id_list(char status, const char *name)
+{
+	struct id_list *new_entry;
+	if(wt_files_last == NULL) {
+		wt_files = xcalloc(1, sizeof(*wt_files));
+		wt_files_last = wt_files;
+		new_entry = wt_files_last;
+	} else {
+		wt_files_last->next = xcalloc(1, sizeof(*wt_files));
+		new_entry = wt_files_last->next;
+		wt_files_last = wt_files_last->next;
+	}
+	new_entry->name = xstrdup(name);
+	new_entry->nr = get_id_list_id(status);
+	new_entry->status = status;
+	new_entry->next = NULL;
+
+	n_id_list++;
+
+	return new_entry->nr;
+}
+
+static void store_id_list()
+{
+	FILE *fp;
+	struct id_list *p = wt_files;
+	const char *filename = git_path("FILE_IDS");
+
+	fp = fopen(filename, "w");
+	if (!fp)
+		die("cannot open %s: %s\n", filename, strerror(errno));
+
+	while (p) {
+		fprintf(fp, "%c%d %s\n",tolower(p->status), p->nr, p->name);
+		p = p->next;
+	}
+	fclose(fp);
+}
+
+static void free_id_list()
+{
+  struct id_list *p = wt_files;
+  struct id_list *next = p;
+  while (p) {
+    next = p->next;
+    free(p->name);
+    free(p);
+    p = next;
+  }
+  wt_files = NULL;
+}
+
+static void format_id(char *buf, char status, int nr)
+{
+	sprintf(buf, " (%c%d)", tolower(status), nr);
+}
+
 #define quote_path quote_path_relative
 
 static void wt_status_print_filepair(struct wt_status *s,
@@ -104,35 +204,48 @@ static void wt_status_print_filepair(struct wt_status *s,
 	const char *c = color(t);
 	const char *one, *two;
 	struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
+	char id[64] = ""; /* arbitrary limit, 64 should be enough for everybody ... */
+	int nr;
 
 	one = quote_path(p->one->path, -1, &onebuf, s->prefix);
 	two = quote_path(p->two->path, -1, &twobuf, s->prefix);
 
+	if (s->show_file_id) {
+		if ( p->status == DIFF_STATUS_RENAMED 
+		     || p->status == DIFF_STATUS_COPIED)
+			nr = add_to_id_list(p->status, two);
+		else
+			nr = add_to_id_list(p->status, one);
+		format_id(id, p->status, nr);
+	}
+
+
 	color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
 	switch (p->status) {
 	case DIFF_STATUS_ADDED:
-		color_fprintf(s->fp, c, "new file:   %s", one);
+		color_fprintf(s->fp, c, "new file:   %s%s", one, id);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, "copied:     %s -> %s%s", one, two, id);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_fprintf(s->fp, c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, "deleted:    %s%s", one, id);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_fprintf(s->fp, c, "modified:   %s", one);
+		color_fprintf(s->fp, c, "modified:   %s%s", one, id);
+
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, "renamed:    %s -> %s%s", one, two, id);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_fprintf(s->fp, c, "typechange: %s", one);
+		color_fprintf(s->fp, c, "typechange: %s%s", one, id);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_fprintf(s->fp, c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, "unknown:    %s%s", one, id);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_fprintf(s->fp, c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, "unmerged:   %s%s", one, id);
 		break;
 	default:
 		die("bug: unhandled diff status %c", p->status);
@@ -247,6 +360,9 @@ static void wt_status_print_untracked(struct wt_status *s)
 	int i;
 	int shown_header = 0;
 	struct strbuf buf = STRBUF_INIT;
+	char id[64] = ""; /* arbitrary limit, 64 should be enough for everybody ... */
+	char *quoted;
+	int nr;
 
 	memset(&dir, 0, sizeof(dir));
 
@@ -266,9 +382,13 @@ static void wt_status_print_untracked(struct wt_status *s)
 			shown_header = 1;
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-		color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
-				quote_path(ent->name, ent->len,
-					&buf, s->prefix));
+		quoted = quote_path(ent->name, ent->len, &buf, s->prefix);
+		if (s->show_file_id) {
+			nr = add_to_id_list(DIFF_STATUS_UNKNOWN, quoted);
+			format_id(id, DIFF_STATUS_UNKNOWN, nr);
+		}
+		color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s%s",
+				quoted, id);
 	}
 	strbuf_release(&buf);
 }
@@ -370,6 +490,10 @@ void wt_status_print(struct wt_status *s)
 		else
 			printf("nothing to commit (working directory clean)\n");
 	}
+	if (s->show_file_id) {
+		store_id_list();
+		free_id_list();
+	}
 }
 
 int git_status_config(const char *k, const char *v, void *cb)
diff --git a/wt-status.h b/wt-status.h
index 78add09..efcb566 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -23,6 +23,7 @@ struct wt_status {
 	char *branch;
 	const char *reference;
 	int verbose;
+	int show_file_id;
 	int amend;
 	int untracked;
 	int nowarn;
-- 
1.6.4.13.ge6580
Nazri Ramliy· Aug 5, 2009, 09:51 UTC · re: Nazri Ramliy · lore

[PATCH 2/6] Teach --id to "git add"

---
 builtin-add.c |   19 ++++++++++++++-----
 1 files changed, 14 insertions(+), 5 deletions(-)
Show changes to builtin-add.c +14 −5
diff --git a/builtin-add.c b/builtin-add.c
index 581a2a1..1ed95d5 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -212,6 +212,7 @@ static const char ignore_error[] =
 
 static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 static int ignore_add_errors, addremove, intent_to_add;
+static const char *file_id;
 
 static struct option builtin_add_options[] = {
 	OPT__DRY_RUN(&show_only),
@@ -226,6 +227,7 @@ static struct option builtin_add_options[] = {
 	OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
 	OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
 	OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
+	OPT_STRING('d', "id", &file_id, "FILE_ID", "file id"),
 	OPT_END(),
 };
 
@@ -268,27 +270,34 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	int flags;
 	int add_new_files;
 	int require_pathspec;
+	char **new_argv;
 
 	git_config(add_config, NULL);
 
 	argc = parse_options(argc, argv, prefix, builtin_add_options,
 			  builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
+
+	if (file_id)
+		new_argv = (char **) expand_file_ids(file_id, &argc, (char **) argv);
+	else
+		new_argv = (char **) argv;
+
 	if (patch_interactive)
 		add_interactive = 1;
 	if (add_interactive)
-		exit(interactive_add(argc - 1, argv + 1, prefix));
+		exit(interactive_add(argc - 1, (const char **) (new_argv + 1), prefix));
 
 	if (edit_interactive)
-		return(edit_patch(argc, argv, prefix));
+		return(edit_patch(argc, (const char **) new_argv, prefix));
 	argc--;
-	argv++;
+	new_argv++;
 
 	if (addremove && take_worktree_changes)
 		die("-A and -u are mutually incompatible");
 	if ((addremove || take_worktree_changes) && !argc) {
 		static const char *here[2] = { ".", NULL };
 		argc = 1;
-		argv = here;
+		new_argv = (char **) here;
 	}
 
 	add_new_files = !take_worktree_changes && !refresh_only;
@@ -308,7 +317,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
 		return 0;
 	}
-	pathspec = validate_pathspec(argc, argv, prefix);
+	pathspec = validate_pathspec(argc, (const char **) new_argv, prefix);
 
 	if (read_cache() < 0)
 		die("index file corrupt");
-- 
1.6.4.13.ge6580
Nazri Ramliy· Aug 5, 2009, 09:51 UTC · re: Nazri Ramliy · lore

[PATCH 3/6] Teach --id to "git checkout"

---
 builtin-checkout.c |   26 +++++++++++++++++---------
 1 files changed, 17 insertions(+), 9 deletions(-)
Show changes to builtin-checkout.c +17 −9
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 8a9a474..94ef419 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -32,6 +32,7 @@ struct checkout_opts {
 	int writeout_error;
 
 	const char *new_branch;
+	const char *file_id;
 	int new_branch_log;
 	enum branch_track track;
 };
@@ -583,6 +584,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	struct option options[] = {
 		OPT__QUIET(&opts.quiet),
 		OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+		OPT_STRING('d', "id", &opts.file_id, "FILE_ID", "file id"),
 		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
 		OPT_SET_INT('t', "track",  &opts.track, "track",
 			BRANCH_TRACK_EXPLICIT),
@@ -597,6 +599,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_END(),
 	};
 	int has_dash_dash;
+	char **new_argv;
 
 	memset(&opts, 0, sizeof(opts));
 	memset(&new, 0, sizeof(new));
@@ -608,9 +611,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
+	if (opts.file_id)
+		new_argv = (char **) expand_file_ids(opts.file_id, &argc, (char **) argv);
+	else
+		new_argv = (char **) argv;
+
 	/* --track without -b should DWIM */
 	if (0 < opts.track && !opts.new_branch) {
-		const char *argv0 = argv[0];
+		const char *argv0 = new_argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die ("--track needs a branch name");
 		if (!prefixcmp(argv0, "refs/"))
@@ -655,14 +663,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 *
 	 */
 	if (argc) {
-		if (!strcmp(argv[0], "--")) {       /* case (2) */
-			argv++;
+		if (!strcmp(new_argv[0], "--")) {       /* case (2) */
+			new_argv++;
 			argc--;
 			goto no_reference;
 		}
 
-		arg = argv[0];
-		has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+		arg = new_argv[0];
+		has_dash_dash = (argc > 1) && !strcmp(new_argv[1], "--");
 
 		if (!strcmp(arg, "-"))
 			arg = "@{-1}";
@@ -674,7 +682,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		}
 
 		/* we can't end up being in (2) anymore, eat the argument */
-		argv++;
+		new_argv++;
 		argc--;
 
 		new.name = arg;
@@ -702,14 +710,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 				verify_non_filename(NULL, arg);
 		}
 		else {
-			argv++;
+			new_argv++;
 			argc--;
 		}
 	}
 
 no_reference:
 	if (argc) {
-		const char **pathspec = get_pathspec(prefix, argv);
+		const char **pathspec = get_pathspec(prefix, (const char **) new_argv);
 
 		if (!pathspec)
 			die("invalid path specification");
@@ -717,7 +725,7 @@ no_reference:
 		/* Checkout paths */
 		if (opts.new_branch) {
 			if (argc == 1) {
-				die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
+				die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", new_argv[0]);
 			} else {
 				die("git checkout: updating paths is incompatible with switching branches.");
 			}
-- 
1.6.4.13.ge6580
Nazri Ramliy· Aug 5, 2009, 09:51 UTC · re: Nazri Ramliy · lore

[PATCH 4/6] Teach --d to "git commit"

---
 builtin-commit.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
Show changes to builtin-commit.c +3 −1
diff --git a/builtin-commit.c b/builtin-commit.c
index 4bcce06..a0bda66 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -50,7 +50,7 @@ static const char *logfile, *force_author;
 static const char *template_file;
 static char *edit_message, *use_message;
 static char *author_name, *author_email, *author_date;
-static int all, edit_flag, also, interactive, only, amend, signoff;
+static int all, edit_flag, also, interactive, only, amend, signoff, id_flag;
 static int quiet, verbose, no_verify, allow_empty;
 static char *untracked_files_arg;
 /*
@@ -96,6 +96,7 @@ static struct option builtin_commit_options[] = {
 	OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
 	OPT_FILENAME('t', "template", &template_file, "use specified template file"),
 	OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+	OPT_BOOLEAN('d', "id", &id_flag, "show file id"),
 
 	OPT_GROUP("Commit contents options"),
 	OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
@@ -352,6 +353,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 		s.reference = "HEAD^1";
 	}
 	s.verbose = verbose;
+	s.show_file_id = id_flag;
 	s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
 	s.index_file = index_file;
 	s.fp = fp;
-- 
1.6.4.13.ge6580
Nazri Ramliy· Aug 5, 2009, 09:51 UTC · re: Nazri Ramliy · lore

[PATCH 5/6] Teach --id to "git rm"

---
 builtin-rm.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletions(-)
Show changes to builtin-rm.c +10 −1
diff --git a/builtin-rm.c b/builtin-rm.c
index 57975db..d2982a0 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -137,6 +137,7 @@ static struct lock_file lock_file;
 
 static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
 static int ignore_unmatch = 0;
+static const char *file_id;
 
 static struct option builtin_rm_options[] = {
 	OPT__DRY_RUN(&show_only),
@@ -146,6 +147,7 @@ static struct option builtin_rm_options[] = {
 	OPT_BOOLEAN('r', NULL,             &recursive,  "allow recursive removal"),
 	OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
 				"exit with a zero status even if nothing matched"),
+	OPT_STRING('d', "id", &file_id, "FILE_ID", "file id"),
 	OPT_END(),
 };
 
@@ -154,11 +156,18 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	int i, newfd;
 	const char **pathspec;
 	char *seen;
+	char **new_argv;
 
 	git_config(git_default_config, NULL);
 
 	argc = parse_options(argc, argv, prefix, builtin_rm_options,
 			     builtin_rm_usage, 0);
+
+	if (file_id)
+		new_argv = (char **) expand_file_ids(file_id, &argc, (char **) argv);
+	else
+		new_argv = (char **) argv;
+
 	if (!argc)
 		usage_with_options(builtin_rm_usage, builtin_rm_options);
 
@@ -171,7 +180,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		die("index file corrupt");
 	refresh_cache(REFRESH_QUIET);
 
-	pathspec = get_pathspec(prefix, argv);
+	pathspec = get_pathspec(prefix, (const char **) new_argv);
 	seen = NULL;
 	for (i = 0; pathspec[i] ; i++)
 		/* nothing */;
-- 
1.6.4.13.ge6580
Nazri Ramliy· Aug 5, 2009, 09:51 UTC · re: Nazri Ramliy · lore

[PATCH 6/6] Teach --id to "git reset"

---
 builtin-reset.c |   32 ++++++++++++++++++++------------
 1 files changed, 20 insertions(+), 12 deletions(-)
Show changes to builtin-reset.c +20 −12
diff --git a/builtin-reset.c b/builtin-reset.c
index 5fa1789..cad5cf5 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -183,6 +183,8 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
+	char *file_id;
+	char **new_argv;
 	const char *rev = "HEAD";
 	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
 				*old_orig = NULL, sha1_old_orig[20];
@@ -198,6 +200,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 				"reset HEAD, index and working tree", MERGE),
 		OPT_BOOLEAN('q', NULL, &quiet,
 				"disable showing new HEAD in hard reset and progress message"),
+		OPT_STRING('d', "id", &file_id, "FILE_ID", "file id"),
 		OPT_END()
 	};
 
@@ -205,7 +208,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
 						PARSE_OPT_KEEP_DASHDASH);
-	reflog_action = args_to_str(argv);
+	if (file_id)
+		new_argv = (char **) expand_file_ids(file_id, &argc, (char **) argv);
+	else
+		new_argv = (char **) argv;
+
+	reflog_action = args_to_str((const char **) new_argv);
 	setenv("GIT_REFLOG_ACTION", reflog_action, 0);
 
 	/*
@@ -216,30 +224,30 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	 * git reset [-opts] -- <paths>...
 	 * git reset [-opts] <paths>...
 	 *
-	 * At this point, argv[i] points immediately after [-opts].
+	 * At this point, new_argv[i] points immediately after [-opts].
 	 */
 
 	if (i < argc) {
-		if (!strcmp(argv[i], "--")) {
+		if (!strcmp(new_argv[i], "--")) {
 			i++; /* reset to HEAD, possibly with paths */
-		} else if (i + 1 < argc && !strcmp(argv[i+1], "--")) {
-			rev = argv[i];
+		} else if (i + 1 < argc && !strcmp(new_argv[i+1], "--")) {
+			rev = new_argv[i];
 			i += 2;
 		}
 		/*
-		 * Otherwise, argv[i] could be either <rev> or <paths> and
+		 * Otherwise, new_argv[i] could be either <rev> or <paths> and
 		 * has to be unambiguous.
 		 */
-		else if (!get_sha1(argv[i], sha1)) {
+		else if (!get_sha1(new_argv[i], sha1)) {
 			/*
-			 * Ok, argv[i] looks like a rev; it should not
+			 * Ok, new_argv[i] looks like a rev; it should not
 			 * be a filename.
 			 */
-			verify_non_filename(prefix, argv[i]);
-			rev = argv[i++];
+			verify_non_filename(prefix, new_argv[i]);
+			rev = new_argv[i++];
 		} else {
 			/* Otherwise we treat this as a filename */
-			verify_filename(prefix, argv[i]);
+			verify_filename(prefix, new_argv[i]);
 		}
 	}
 
@@ -260,7 +268,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		else if (reset_type != NONE)
 			die("Cannot do %s reset with paths.",
 					reset_type_names[reset_type]);
-		return read_from_tree(prefix, argv + i, sha1,
+		return read_from_tree(prefix, (const char **) argv + i, sha1,
 				quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
 	}
 	if (reset_type == NONE)
-- 
1.6.4.13.ge6580
Alex Riesen· Aug 5, 2009, 18:11 UTC · re: Nazri Ramliy · lore

Re: [PATCH 1/6] Teach --id/-d to "git status"

On Wed, Aug 5, 2009 at 11:51, Nazri Ramliy<ayiehere@gmail.com> wrote:
Show 5 quoted lines
> This patch adds a unique identifier for each file shown by "git status".
>
> Subsequent operation (add/rm/checkout/commit/reset) may use these ids
> to simplify specifying the files, provided that they are taught about
> the --id option as well.
What is it for?
Junio C Hamano· Aug 5, 2009, 18:27 UTC · re: Sverre Rabbelier · lore

Re: [PATCH 1/6] Teach --id/-d to "git status"

Sverre Rabbelier <srabbelier@gmail.com> writes:
> On Wed, Aug 5, 2009 at 11:11, Alex Riesen<raa.lkml@gmail.com> wrote:
>> What is it for?
>
> See [PATCH 0/6].
Sounds useless.
Sverre Rabbelier· Aug 5, 2009, 18:30 UTC · re: Junio C Hamano · lore

Re: [PATCH 1/6] Teach --id/-d to "git status"

Heya,
On Wed, Aug 5, 2009 at 11:27, Junio C Hamano<gitster@pobox.com> wrote:
> Sounds useless.

It's actually called "[PATCH] RFC - Say goodbye to the rodent"; can't find it on GMane though.

-- 
Cheers,

Sverre Rabbelier
Alex Riesen· Aug 5, 2009, 18:33 UTC · re: Sverre Rabbelier · lore

Re: [PATCH 1/6] Teach --id/-d to "git status"

Sverre Rabbelier, Wed, Aug 05, 2009 20:25:43 +0200:
Show 7 quoted lines
> Heya,
> 
> On Wed, Aug 5, 2009 at 11:11, Alex Riesen<raa.lkml@gmail.com> wrote:
> > What is it for?
> 
> See [PATCH 0/6].
> 
Complicated solution to a non-existing problem.
Nazri, have you ever tried "git gui"? (or tig, for console?)
Sverre Rabbelier· Aug 5, 2009, 18:35 UTC · re: Alex Riesen · lore

Re: [PATCH 1/6] Teach --id/-d to "git status"

Heya,
On Wed, Aug 5, 2009 at 11:33, Alex Riesen<raa.lkml@gmail.com> wrote:
> Complicated solution to a non-existing problem.

That's harsh; obviously it is an existing problem for Nazri, or they wouldn't have spent the time writing these patches. So at worst it's a complicated solution for an _already solved_ problem (tig, git add -i, etc), but definitely not non-existing ;).

-- 
Cheers,

Sverre Rabbelier
Andreas Ericsson· Aug 5, 2009, 10:04 UTC · re: Nazri Ramliy · lore

Re: [PATCH] RFC - Say goodbye to the rodent

Nazri Ramliy wrote:
> 
> What do you guys think about this new approach of "cut-and-paste" from the
> command line?
> 

Dunno, really. Tab-completion's working just fine for me, so I doubt I'll use it. It's definitely novel, but how much aggravation will it cause if not all file-managing programs support it?

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

Considering the successes of the wars on alcohol, poverty, drugs and
terror, I think we should give some serious thought to declaring war
on peace.
Matthieu Moy· Aug 5, 2009, 11:54 UTC · re: Nazri Ramliy · lore

Re: [PATCH] RFC - Say goodbye to the rodent

Nazri Ramliy <ayiehere@gmail.com> writes:
> Oftentimes I get tired of moving my hand away from the comfort of the home rows
> of my keybard in order to grab the mouse to highlight the files that I'd like
> to operate on.

Usually, when you cut-and-paste from $(git status), it's to run a staging/unstaging command, or to view a diff.

Actually, "git add -i" is already a pretty good way of doing this, with few keystrokes.

Show 15 quoted lines
> Example:
>
> $ git status --id
> # On branch local
> # Changed but not updated:
> #   (use "git add <file>..." to update what will be committed)
> #   (use "git checkout -- <file>..." to discard changes in working directory)
> #
> #       modified:   builtin-write-tree.c (m1)
> #
> # Untracked files:
> #   (use "git add <file>..." to include in what will be committed)
> #
> #       file1.c (x1)
> #       file2.c (x2)
$ git add -i
           staged     unstaged path
  1:    unchanged        +1/-0 builtin-write-tree.c
*** Commands ***
  1: [s]tatus     2: [u]pdate     3: [r]evert     4: [a]dd untracked
  5: [p]atch      6: [d]iff       7: [q]uit       8: [h]elp
> Now we can do the following:
>
> $ git add --id m1
What now> u
           staged     unstaged path
  1:    unchanged        +1/-0 [b]uiltin-write-tree.c
Update>> 1
           staged     unstaged path
* 1:    unchanged        +1/-0 [b]uiltin-write-tree.c
Update>> 
updated one path
*** Commands ***
  1: [s]tatus     2: [u]pdate     3: [r]evert     4: [a]dd untracked
  5: [p]atch      6: [d]iff       7: [q]uit       8: [h]elp
> Ids are specified via the --id command line option, and can be separated with
> commas if you want to specify more than one of them:
>
> $ git add --id m1,x2
What now> a
  1: file1.c
  2: file2.c
Add untracked>> 1
* 1: file1.c
  2: file2.c
Add untracked>> 2
* 1: file1.c
* 2: file2.c
Add untracked>> 
added 2 paths

for these examples, I have far fewer keystrokes with "git add -i" than you have. I don't think it's worth adding yet-another-option to most Git commands since it doesn't really bring much IMHO.

-- 
Matthieu
Jeff King· Aug 5, 2009, 19:02 UTC · re: Nazri Ramliy · lore

Re: [PATCH] RFC - Say goodbye to the rodent

On Wed, Aug 05, 2009 at 05:51:40PM +0800, Nazri Ramliy wrote:
> The idea is that "git status --id" shows a unique id for each file in
> its output (modified/staged/unknown/etc). The ids and the
> corresponding filenames are stored in .git/FILE_IDS. This file gets
> overwritten everytime you run "git status --id"

But files _already_ have a unique id: the filename. You never say why those unique ids must be cut-and-pasted using the mouse when your unique ids would be suitable for typing, but I'll assume it's because your unique ids are much shorter than your filenames.

There are already two classes of solutions to this problem:
  1. Make typing the filenames easier. Generally, this is accomplished
     by tab completion. Even stock bash (and other shells) should
     complete filenames easily, but you can also complete much more
     using the programmable bash completion included with git.
     The main advantage of this approach is that it is totally
     generalizable. Anytime you have to input a filename, you can use
     it.
  2. Structure your workflow to iterate over the list of items to be
     acted on, and then select actions for each item. This is what "git
     add -i" does, as well as "git mergetool" and "git difftool".
     This can save a lot of typing over (1), but requires a new script
     for every such workflow (e.g., the scripts above cover only adding,
     resolving merges, and diffing; you would need a new script to
     iterate over files, doing "git checkout" on each one, for example).

I think your solution is a third class, which is to assign a mapping of shorter ids to items that persists over multiple commands. But it's not clear to me where it has an advantage over the existing two solutions. It is actually less general than (1), because you have to have assigned the mappings beforehand. But it is not nearly as convenient as (2).

In addition, it's more complicated to implement and use, because you have to care about the persistence of the mapping (i.e., how long does it last, which commands reset it, etc).

So while it is somewhat clever, I don't think it is worth merging to mainline git; it introduces complexity to solve a problem that already has overlapping solutions.

-Peff
Nicolas Pitre· Aug 5, 2009, 20:01 UTC · re: Jeff King · lore

Re: [PATCH] RFC - Say goodbye to the rodent

On Wed, 5 Aug 2009, Jeff King wrote:
Show 31 quoted lines
> On Wed, Aug 05, 2009 at 05:51:40PM +0800, Nazri Ramliy wrote:
> 
> > The idea is that "git status --id" shows a unique id for each file in
> > its output (modified/staged/unknown/etc). The ids and the
> > corresponding filenames are stored in .git/FILE_IDS. This file gets
> > overwritten everytime you run "git status --id"
> 
> But files _already_ have a unique id: the filename. You never say why
> those unique ids must be cut-and-pasted using the mouse when your unique
> ids would be suitable for typing, but I'll assume it's because your
> unique ids are much shorter than your filenames.
> 
> There are already two classes of solutions to this problem:
> 
>   1. Make typing the filenames easier. Generally, this is accomplished
>      by tab completion. Even stock bash (and other shells) should
>      complete filenames easily, but you can also complete much more
>      using the programmable bash completion included with git.
> 
>      The main advantage of this approach is that it is totally
>      generalizable. Anytime you have to input a filename, you can use
>      it.
> 
>   2. Structure your workflow to iterate over the list of items to be
>      acted on, and then select actions for each item. This is what "git
>      add -i" does, as well as "git mergetool" and "git difftool".
> 
>      This can save a lot of typing over (1), but requires a new script
>      for every such workflow (e.g., the scripts above cover only adding,
>      resolving merges, and diffing; you would need a new script to
>      iterate over files, doing "git checkout" on each one, for example).
BTW I often use this incantation:
	xargs git add
	[paste list of files obtained from git status output]
	^D
Nicolas

← back to recent threads