git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
@ 2017-01-20 10:22 Nguyễn Thái Ngọc Duy
  2017-01-20 10:46 ` Johannes Schindelin
  2017-01-20 16:09 ` Jeff King
  0 siblings, 2 replies; 12+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2017-01-20 10:22 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

OK This patch is horrible. Though the idea is cool and I've found it
very useful. So here it is. Perhaps the idea may be revised a bit
that's more suitable for more than one user.

The problem is old, SHA-1 name is not keyboard-friendly, even in
abbreviated form. And recent change has made abbrev form longer,
harder to type. Most of the time I just go with copy/paste with the
mouse, which I don't like. name-rev helps a bit, but it's still long
to type (especially with all the ^ and ~ that requires holding shift
down).

So, this patch adds an option --mark-commits to git-log. Whenever a
commit SHA-1 is printed on screen, it gets a new shortcut in form of
<at-sign><one-letter><number> e.g. @h2. The number keeps increasing
during one git-log, so you'll get @h1, @h2, @h3 and so on. Typing @h3
gets you back to the associated SHA-1.

Different git-log runs will change the <one-letter> part. So the
second git-log run may give you @j1, @j2, @j3... This is useful
because sometimes you want to run more than one command, then
reference back to commits from the first command.

There's a limited number of letters to be used here. I limit it to the
letters on the home row to avoid moving fingers too much. Which means
after like 9 runs, @h is recycled for something new. Not great if you
have "git blah blah @h2" in bash history and keep recalling it from
time to time.

So, whenever you use a shortcut, the shortcut is kept in a "database"
and will not be recycled for quite some time. When @h is recycled,
you'll get @h1, then @h3 and so on if @h2 has just been used recently.
This makes sure recently used shortcuts live on.

That's it. I have to say typing 3-4 characters is really nice. I kinda
want to reduce the number of keystokes further down, but it's been good
enough that I'm not so pissed to spend time tuning it further.

This shortcut format violates the extended sha1 syntax, I think. In
order have any chance of entering git, we'll have to deal with that
first.

---
 Makefile            |   1 +
 commit.h            |   1 +
 log-tree.c          |   7 +++
 pretty.c            |   5 ++
 rev-counter.c (new) | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 rev-counter.h (new) |  10 ++++
 revision.c          |   4 ++
 revision.h          |   1 +
 sha1_name.c         |   8 +++
 9 files changed, 203 insertions(+)
 create mode 100644 rev-counter.c
 create mode 100644 rev-counter.h

diff --git a/Makefile b/Makefile
index de5a030256..28948bd9eb 100644
--- a/Makefile
+++ b/Makefile
@@ -788,6 +788,7 @@ LIB_OBJS += replace_object.o
 LIB_OBJS += rerere.o
 LIB_OBJS += resolve-undo.o
 LIB_OBJS += revision.o
+LIB_OBJS += rev-counter.o
 LIB_OBJS += run-command.o
 LIB_OBJS += send-pack.o
 LIB_OBJS += sequencer.o
diff --git a/commit.h b/commit.h
index b06db4d5d9..8f67687208 100644
--- a/commit.h
+++ b/commit.h
@@ -155,6 +155,7 @@ struct pretty_print_context {
 	struct string_list *mailmap;
 	int color;
 	struct ident_split *from_ident;
+	int mark_commits;
 
 	/*
 	 * Fields below here are manipulated internally by pp_* functions and
diff --git a/log-tree.c b/log-tree.c
index 78a5381d0e..f49366b376 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -11,6 +11,7 @@
 #include "gpg-interface.h"
 #include "sequencer.h"
 #include "line-log.h"
+#include "rev-counter.h"
 
 static struct decoration name_decoration = { "object names" };
 static int decoration_loaded;
@@ -593,6 +594,9 @@ void show_log(struct rev_info *opt)
 	}
 	opt->shown_one = 1;
 
+	if (opt->mark_commits)
+		mark_commit(&commit->object.oid);
+
 	/*
 	 * If the history graph was requested,
 	 * print the graph, up to this commit's line
@@ -615,6 +619,8 @@ void show_log(struct rev_info *opt)
 			put_revision_mark(opt, commit);
 		fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),
 		      stdout);
+		if (opt->mark_commits)
+			printf(" @%s", oid_to_commit_mark(&commit->object.oid));
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
 		if (opt->children.name)
@@ -687,6 +693,7 @@ void show_log(struct rev_info *opt)
 	ctx.output_encoding = get_log_output_encoding();
 	if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
 		ctx.from_ident = &opt->from_ident;
+	ctx.mark_commits = opt->mark_commits;
 	pretty_print_commit(&ctx, commit, &msgbuf);
 
 	if (opt->add_signoff)
diff --git a/pretty.c b/pretty.c
index c3ec430220..14e0022085 100644
--- a/pretty.c
+++ b/pretty.c
@@ -10,6 +10,7 @@
 #include "color.h"
 #include "reflog-walk.h"
 #include "gpg-interface.h"
+#include "rev-counter.h"
 
 static char *user_format;
 static struct cmt_fmt_map {
@@ -1127,6 +1128,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 	case 'H':		/* commit hash */
 		strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
 		strbuf_addstr(sb, oid_to_hex(&commit->object.oid));
+		if (c->pretty_ctx->mark_commits)
+			strbuf_addf(sb, " @%s", oid_to_commit_mark(&commit->object.oid));
 		strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
 		return 1;
 	case 'h':		/* abbreviated commit hash */
@@ -1137,6 +1140,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 		}
 		strbuf_addstr(sb, find_unique_abbrev(commit->object.oid.hash,
 						     c->pretty_ctx->abbrev));
+		if (c->pretty_ctx->mark_commits)
+			strbuf_addf(sb, " @%s", oid_to_commit_mark(&commit->object.oid));
 		strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
 		c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
 		return 1;
diff --git a/rev-counter.c b/rev-counter.c
new file mode 100644
index 0000000000..60ab991fe1
--- /dev/null
+++ b/rev-counter.c
@@ -0,0 +1,166 @@
+#include "cache.h"
+#include "strbuf.h"
+#include "commit.h"
+#include "commit-slab.h"
+
+define_commit_slab(counter, char *);
+struct counter counter = COMMIT_SLAB_INIT(1, counter);
+
+static unsigned commit_counter;
+static int run_counter;
+int latest_fd = -1;
+struct strbuf recent = STRBUF_INIT;
+
+/* home row, except 'l' which looks too much like '1' */
+const char *starter = "asdfghjk";
+
+static void start_marking(void)
+{
+	char buf[64];
+	ssize_t len;
+
+	len = readlink(git_path("rev-counter/latest"), buf, sizeof(buf));
+	if (len == -1) {
+		if (errno != ENOENT)
+			die_errno("failed to open rev-counter/latest");
+	} else {
+		char ch, *p;
+		sscanf(buf, "%c", &ch);
+		p = strchrnul(starter, ch);
+		run_counter = ((p - starter) + 1) % strlen(starter);
+	}
+
+	if (!is_directory(git_path("rev-counter")))
+		mkdir(git_path("rev-counter"), 0755);
+	latest_fd = open(git_path("rev-counter/%c", starter[run_counter]),
+			 O_RDWR | O_CREAT | O_TRUNC, 0644);
+	if (latest_fd == -1)
+		die_errno("failed to create new rev-counter");
+
+	sprintf(buf, "%c", starter[run_counter]);
+	(void)unlink(git_path("rev-counter/latest"));
+	if (symlink(buf, git_path("rev-counter/latest")))
+		die_errno("failed to update rev-counter/latest");
+}
+
+static int strbuf_find_commit_mark(const struct strbuf *sb,
+				   const char *mark,
+				   struct object_id *oid)
+{
+	const char *p, *end;
+	int ret = -1;
+
+	end = sb->buf + sb->len;
+	p = sb->buf;
+	while (p < end && ret < 0) {
+		const char *next = strchrnul(p, '\n');
+
+		if (next - p > 41 &&
+		    p[40] == ' ' &&
+		    p + 41 + strlen(mark) == next &&
+		    !strncmp(p + 41, mark, strlen(mark)) &&
+		    !get_oid_hex(p, oid))
+			ret = 0;
+
+		p = next;
+		if (*p == '\n')
+			p++;
+	}
+	return ret;
+}
+
+void update_commit_counter(void)
+{
+	static int read_recent;
+	struct object_id oid;
+	char buf[16];
+
+	if (!read_recent) {
+		strbuf_read_file(&recent, git_path("rev-counter/recent"), 0);
+		read_recent = 1;
+
+		if (recent.len > 1000 * 45) { /* prune time? */
+			int fd;
+			const char *p = strchr(recent.buf + 1000 * 45, '\n');
+			strbuf_remove(&recent, 0, p + 1 - recent.buf);
+			if (!is_directory(git_path("rev-counter")))
+				mkdir(git_path("rev-counter"), 0755);
+			fd = open(git_path("rev-counter/recent"),
+				  O_CREAT | O_TRUNC | O_RDWR, 0644);
+			write_or_die(fd, recent.buf, recent.len);
+			close(fd);
+		}
+	}
+
+	do {
+		commit_counter++;
+		sprintf(buf, "%c%u", starter[run_counter], commit_counter);
+	} while (!strbuf_find_commit_mark(&recent, buf, &oid));
+}
+
+void mark_commit(const struct object_id *oid)
+{
+	struct commit *c;
+	char buf[64];
+	char **val;
+
+	if (latest_fd < 0)
+		start_marking();
+
+	if (!(c = lookup_commit(oid->hash)))
+		return;
+
+	update_commit_counter();
+	sprintf(buf, "%s %u\n", oid_to_hex(oid), commit_counter);
+	write_or_die(latest_fd, buf, strlen(buf));
+
+	sprintf(buf, "%c%u", starter[run_counter], commit_counter);
+	val = counter_at(&counter, c);
+	*val = xstrdup(buf);
+}
+
+const char *oid_to_commit_mark(const struct object_id *oid)
+{
+	struct commit *c;
+	char **val;
+
+	if (!(c = lookup_commit(oid->hash)) ||
+	    !(val = counter_peek(&counter, c)))
+		return NULL;
+
+	return *val;
+}
+
+int commit_mark_to_oid(const char *mark, struct object_id *oid)
+{
+	char buf[16];
+	int ret, c;
+	char r_ch;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (sscanf(mark, "%c%u", &r_ch, &c) != 2 ||
+	    !strchr(starter, r_ch) ||
+	    strbuf_read_file(&sb, git_path("rev-counter/%c", r_ch), 0) < 0)
+		return -1;
+
+	sprintf(buf, "%u", c);
+	ret = strbuf_find_commit_mark(&sb, buf, oid);
+	strbuf_release(&sb);
+
+	if (!ret) {
+		char line[80];
+		static int fd;
+
+		if (!fd) {
+			if (!is_directory(git_path("rev-counter")))
+				mkdir(git_path("rev-counter"), 0755);
+			fd = open(git_path("rev-counter/recent"),
+				  O_CREAT | O_RDWR | O_APPEND, 0644);
+			if (fd == -1)
+				die("failed to open rev-counter/recent");
+		}
+		sprintf(line, "%s %s\n", oid_to_hex(oid), mark);
+		write_or_die(fd, line, strlen(line));
+	}
+	return ret;
+}
diff --git a/rev-counter.h b/rev-counter.h
new file mode 100644
index 0000000000..ab2e8d8803
--- /dev/null
+++ b/rev-counter.h
@@ -0,0 +1,10 @@
+#ifndef __REV_COUNTER_H__
+#define __REV_COUNTER_H__
+
+struct object_id;
+
+void mark_commit(const struct object_id *oid);
+const char *oid_to_commit_mark(const struct object_id *oid);
+int commit_mark_to_oid(const char *mark, struct object_id *oid);
+
+#endif
diff --git a/revision.c b/revision.c
index 2f60062876..1e32e91b9f 100644
--- a/revision.c
+++ b/revision.c
@@ -1998,6 +1998,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->limited = 1;
 	} else if (!strcmp(arg, "--ignore-missing")) {
 		revs->ignore_missing = 1;
+	} else if (!strcmp(arg, "--mark-commits")) {
+		revs->mark_commits = 1;
+	} else if (!strcmp(arg, "--no-mark-commits")) {
+		revs->mark_commits = 0;
 	} else {
 		int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix);
 		if (!opts)
diff --git a/revision.h b/revision.h
index 9fac1a607d..94e5b70ff5 100644
--- a/revision.h
+++ b/revision.h
@@ -146,6 +146,7 @@ struct rev_info {
 	unsigned int	track_linear:1,
 			track_first_time:1,
 			linear:1;
+	unsigned int	mark_commits:1;
 
 	struct date_mode date_mode;
 	int		expand_tabs_in_log; /* unset if negative */
diff --git a/sha1_name.c b/sha1_name.c
index ca7ddd6f2c..d3e1ff3921 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -7,6 +7,7 @@
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
+#include "rev-counter.h"
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
@@ -485,6 +486,13 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 		}
 		return 0;
 	}
+	if (len > 2 && str[0] == '@' && isalpha(str[1]) && isdigit(str[2])) {
+		struct object_id oid;
+		if (!commit_mark_to_oid(str + 1, &oid)) {
+			hashcpy(sha1, oid.hash);
+			return 0;
+		}
+	}
 
 	/* basic@{time or number or -number} format to query ref-log */
 	reflog_len = at = 0;
-- 
2.11.0.157.gd943d85


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 10:22 [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard Nguyễn Thái Ngọc Duy
@ 2017-01-20 10:46 ` Johannes Schindelin
  2017-01-20 11:01   ` Duy Nguyen
  2017-01-20 16:09 ` Jeff King
  1 sibling, 1 reply; 12+ messages in thread
From: Johannes Schindelin @ 2017-01-20 10:46 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 1093 bytes --]

Hi Duy,

On Fri, 20 Jan 2017, Nguyễn Thái Ngọc Duy wrote:

> OK This patch is horrible.

Yeah ;-)

> Though the idea is cool and I've found it very useful. So here it is.
> Perhaps the idea may be revised a bit that's more suitable for more than
> one user.

Why not introduce a flag to "git log" that shows a keyboard-friendly name
similar to what `git name-rev` would have said, except that the name would
be generated using the name(s) specified on the command-line?

Example:

	git log 8923d2d0 upstream/pu

	commit 8923d2d00192ceb1107078484cccf537cb51c1b5 (8923d2d0)
	...
	commit 9f500d6cf5eaa49391d6deca85fc864e5bd23415 (8923d2d0^)
	...
	commit f79c24a291a58845b08cfec7573e22cc153693e1 (8923d2d0~2)
	...
	commit c921c5bb63baaa16dc760de9549da55c8c89dc9c (upstream/pu)
	...
	commit 16793ba6b6333ba0cdee1adb53d979c3fbdb17bc (upstream/pu^)
	...

Granted, this is still a little more cumbersome to type than @h1, but
then, you can skip those round-robin games as well as the possibly
backwards-incompatible extension of the rev syntax.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 10:46 ` Johannes Schindelin
@ 2017-01-20 11:01   ` Duy Nguyen
  2017-01-20 15:21     ` Johannes Schindelin
  0 siblings, 1 reply; 12+ messages in thread
From: Duy Nguyen @ 2017-01-20 11:01 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List

On Fri, Jan 20, 2017 at 5:46 PM, Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
> Why not introduce a flag to "git log" that shows a keyboard-friendly name
> similar to what `git name-rev` would have said, except that the name would
> be generated using the name(s) specified on the command-line?
>
> Example:
>
>         git log 8923d2d0 upstream/pu
>
>         commit 8923d2d00192ceb1107078484cccf537cb51c1b5 (8923d2d0)
>         ...
>         commit 9f500d6cf5eaa49391d6deca85fc864e5bd23415 (8923d2d0^)
>         ...
>         commit f79c24a291a58845b08cfec7573e22cc153693e1 (8923d2d0~2)
>         ...
>         commit c921c5bb63baaa16dc760de9549da55c8c89dc9c (upstream/pu)
>         ...
>         commit 16793ba6b6333ba0cdee1adb53d979c3fbdb17bc (upstream/pu^)
>         ...
>
> Granted, this is still a little more cumbersome to type than @h1, but
> then, you can skip those round-robin games as well as the possibly
> backwards-incompatible extension of the rev syntax.

I mentioned name-rev a few paragraphs down. No, I want the sweet and
short @h1 (or something like that). name-rev does not qualify. I don't
feel comfortable typing 8923d2d0 without looking at the keyboard, and
that's a lot of movement on the keyboard.  upstream/pu is a bit
better, but still very long (at least for me). Yes TAB-ing does help,
but not enough. Then you'll get the dreadful "^2~1^3" dance.
-- 
Duy

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 11:01   ` Duy Nguyen
@ 2017-01-20 15:21     ` Johannes Schindelin
  0 siblings, 0 replies; 12+ messages in thread
From: Johannes Schindelin @ 2017-01-20 15:21 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List

Hi Duy,

On Fri, 20 Jan 2017, Duy Nguyen wrote:

> On Fri, Jan 20, 2017 at 5:46 PM, Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
> > Why not introduce a flag to "git log" that shows a keyboard-friendly name
> > similar to what `git name-rev` would have said, except that the name would
> > be generated using the name(s) specified on the command-line?
> >
> > Example:
> >
> >         git log 8923d2d0 upstream/pu
> >
> >         commit 8923d2d00192ceb1107078484cccf537cb51c1b5 (8923d2d0)
> >         ...
> >         commit 9f500d6cf5eaa49391d6deca85fc864e5bd23415 (8923d2d0^)
> >         ...
> >         commit f79c24a291a58845b08cfec7573e22cc153693e1 (8923d2d0~2)
> >         ...
> >         commit c921c5bb63baaa16dc760de9549da55c8c89dc9c (upstream/pu)
> >         ...
> >         commit 16793ba6b6333ba0cdee1adb53d979c3fbdb17bc (upstream/pu^)
> >         ...
> >
> > Granted, this is still a little more cumbersome to type than @h1, but
> > then, you can skip those round-robin games as well as the possibly
> > backwards-incompatible extension of the rev syntax.
> 
> I mentioned name-rev a few paragraphs down.

Okay, then.

> No, I want the sweet and short @h1 (or something like that). name-rev
> does not qualify.

I am afraid that you have to go back to the drawing board, then, to come
up with a backwards-compatible syntax that is as equally short and sweet
as @h1.

And you will probably also have to come up with a strategy for
implementing "transient refs", because that is exactly what you are doing
here. After all, you need to ensure that @h1 still works correctly (i.e.
without scary warnings) when the corresponding ref has been removed and an
aggressive garbage collection was completed.

And there are other concerns to be considered, not only the
backwards-compatibility. Certainly the current design will need to be
overhauled.

Just from a cursory brain-storming on my side (I even left out the
hand-waving concerns):

- this feature requires write-access. From a user's point of view, this
  should be read-only, or at least also work without write access

- the term "commit mark" is already used for something very different in
  fast-import

- the idea that running "git log --mark-commits master" twice will
  generate *different* marks is surprising (and not in a good way)

- introducing a feature to save keyboard strokes, and then requiring the
  user to type ` --mark-commits`, i.e. 15 key presses, is, uhm, funny

- the fact that these marks are per-worktree makes it a lot less useful
  than if they were per-repository

- related: there is no way to re-use these marks in different
  repositories, e.g. when helping other developers, say, in a chat room

- the fact that the first mark in every repository will be @a1 opens the
  door very wide for using that mark accidentally in the wrong repository.
  At least if I write 8923d in the git-gui repository, it will tell me
  that there is no such revision. Using @a1, it would succeed, and it
  would take me a *long* time to even realize that I am in the wrong
  directory!

And then there are the implementation issues:

- it uses symlinks. I cannot let you do that, Dave.

- using sscanf() to get the value of the first byte of a buffer? What's
  wrong with `*buf*`?

- "asdfghjk"? That is *very* limiting. The opposite of diverse. Just ask
  the French, for starters

- latest_fd is never closed, nor flushed

It sure complicates the implementation side as well as the overall design
a heck of a lot to insist on not using existing rev syntax.

> I don't feel comfortable typing 8923d2d0 without looking at the
> keyboard, and that's a lot of movement on the keyboard.

I mentioned 8923d2d0 only as an example to illustrate the difference to
name-rev.

And when you talk about movement on the keyboard, you *must* realize that
you impose your favorite keyboard layout here. On the German keyboard, for
example, the `@` sign is typed via AltGr+q. That is very cumbersome right
there.

And when you talk about speed advantages of keyboard vs mouse, it seems
that there are studies that show that keyboard is faster than mouse. For
keyboard shortcuts. Not for typing. I actually found no study comparing
the speed of typing with the speed of copy/paste, although I am sure that
they exist, and that they show pretty clearly that copy/paste is faster,
and more accurate, than typing.

But back to minimizing movement on the keyboard.

You would usually type `git log upstream/pu`, tab-completing
`upstream/pu`, right? And then you would want to refer to one particular
entry in that output, say, `upstream/pu~50^2`, right?

Right:

> upstream/pu is a bit better, but still very long (at least for me). Yes
> TAB-ing does help, but not enough. Then you'll get the dreadful "^2~1^3"
> dance.

Yes, exactly. You would use tab-completion there, too, most of the way.
Although I do not find that "^2^^3" [*1*] dreadful, I can see that some
users find it cumbersome. And then it is a little bit over the top to try
to optimize away that "^2^^3".

However, I am starting to feel like you try to use the wrong approach,
bending Git's user interface out of shape just to be able force a workflow
that avoids copy-paste or GUIs, or for that matter, advanced ref notation
such as "upstream/pu^{/worktree.remove}".

Don't get me wrong, I would love to have faster method to go between log
and prompt. But to be honest, what costs me much more of my time is
figuring out which commit and branch any given mail talks about when
reviewing, or addressing reviews. Or for that matter, finding the mail
thread discussing the patch series that the "What's cooking" mail talks
about. I guess I'd spend more time on accelerating that than on avoiding
copy/paste from `git log`'s output.

Ciao,
Johannes

Footnote *1*: Do we really have octopus merges in Git? I do not think so.
*clicketyclick*. Urgh. You are correct. I found a whopping 45 in my
repository, and I certainly did not introduce any. On a sunny note, the
last Octopus seems to be v1.7.10-rc0~9^2~9(Merge branches
zj/decimal-width, zj/term-columns and jc/diff-stat-scaler, 2012-02-24),
i.e. a couple of years old.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 10:22 [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard Nguyễn Thái Ngọc Duy
  2017-01-20 10:46 ` Johannes Schindelin
@ 2017-01-20 16:09 ` Jeff King
  2017-01-20 18:27   ` Junio C Hamano
  2017-01-20 19:16   ` Jacob Keller
  1 sibling, 2 replies; 12+ messages in thread
From: Jeff King @ 2017-01-20 16:09 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

On Fri, Jan 20, 2017 at 05:22:49PM +0700, Nguyễn Thái Ngọc Duy wrote:

> OK This patch is horrible. Though the idea is cool and I've found it
> very useful. So here it is. Perhaps the idea may be revised a bit
> that's more suitable for more than one user.
> 
> The problem is old, SHA-1 name is not keyboard-friendly, even in
> abbreviated form. And recent change has made abbrev form longer,
> harder to type. Most of the time I just go with copy/paste with the
> mouse, which I don't like. name-rev helps a bit, but it's still long
> to type (especially with all the ^ and ~ that requires holding shift
> down).

Not really a comment on your patch itself, but I think a lot of people
solve this at a higher level, either in their terminal or via a tool
like tmux.

I recently taught urxvt to recognize sha1s and grab them via keyboard
hints, and I'm finding it quite useful. Here's what it looks like if
you're interested:

  http://peff.net/git-hints.gif

The hints technique is taken from pentadactyl (which I also use), but
the urxvt port is mine. I'm happy to share the code.

Which isn't to say solving it inside Git is wrong, but I've found it
really convenient for two reasons:

  1. It works whenever you see a sha1, not just in git commands (so
     emails, inside commit messages, etc).

  2. It doesn't take any screen space until you're ready to select.

The big downside is that it's scraping the screen, so you're guessing at
what is a sha1. False positives are a little annoying, but usually not
that big a deal because you're already looking at what you want to
select, and the hint pops up right there.

-Peff

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 16:09 ` Jeff King
@ 2017-01-20 18:27   ` Junio C Hamano
  2017-01-20 19:16   ` Jacob Keller
  1 sibling, 0 replies; 12+ messages in thread
From: Junio C Hamano @ 2017-01-20 18:27 UTC (permalink / raw)
  To: Jeff King; +Cc: Nguyễn Thái Ngọc Duy, git

Jeff King <peff@peff.net> writes:

> Not really a comment on your patch itself, but I think a lot of people
> solve this at a higher level, either in their terminal or via a tool
> like tmux.
>
> I recently taught urxvt to recognize sha1s and grab them via keyboard
> hints, and I'm finding it quite useful. Here's what it looks like if
> you're interested:
>
>   http://peff.net/git-hints.gif

Nice.  I would have called the "give me the string that is already
on the screen" solution solving at a lower level, not higher, but I
think I agree with the general direction.  I always work in "screen"
and grab a string I see displayed by going into its "copy" mode,
which lets me jump to where the string appears by searching, and I
think that is a solution in the same class.

>   2. It doesn't take any screen space until you're ready to select.

Yup, personally I find this quite important (as I am often on a box
with a smaller screen).

I dream an integration with the command line completion we have.  I
do not offhand see what the mecanism to tell what object names were
shown on the display recently to the completion mechanism should be,
but if we can solve that small detail, the result would be wonderful
;-)


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 16:09 ` Jeff King
  2017-01-20 18:27   ` Junio C Hamano
@ 2017-01-20 19:16   ` Jacob Keller
  2017-01-20 19:25     ` Jeff King
  2017-02-05 10:39     ` Duy Nguyen
  1 sibling, 2 replies; 12+ messages in thread
From: Jacob Keller @ 2017-01-20 19:16 UTC (permalink / raw)
  To: Jeff King; +Cc: Nguyễn Thái Ngọc Duy, Git mailing list

On Fri, Jan 20, 2017 at 8:09 AM, Jeff King <peff@peff.net> wrote:
> On Fri, Jan 20, 2017 at 05:22:49PM +0700, Nguyễn Thái Ngọc Duy wrote:
>
>> OK This patch is horrible. Though the idea is cool and I've found it
>> very useful. So here it is. Perhaps the idea may be revised a bit
>> that's more suitable for more than one user.
>>
>> The problem is old, SHA-1 name is not keyboard-friendly, even in
>> abbreviated form. And recent change has made abbrev form longer,
>> harder to type. Most of the time I just go with copy/paste with the
>> mouse, which I don't like. name-rev helps a bit, but it's still long
>> to type (especially with all the ^ and ~ that requires holding shift
>> down).
>
> Not really a comment on your patch itself, but I think a lot of people
> solve this at a higher level, either in their terminal or via a tool
> like tmux.
>
> I recently taught urxvt to recognize sha1s and grab them via keyboard
> hints, and I'm finding it quite useful. Here's what it looks like if
> you're interested:
>
>   http://peff.net/git-hints.gif
>
> The hints technique is taken from pentadactyl (which I also use), but
> the urxvt port is mine. I'm happy to share the code.
>
> Which isn't to say solving it inside Git is wrong, but I've found it
> really convenient for two reasons:
>
>   1. It works whenever you see a sha1, not just in git commands (so
>      emails, inside commit messages, etc).
>
>   2. It doesn't take any screen space until you're ready to select.
>
> The big downside is that it's scraping the screen, so you're guessing at
> what is a sha1. False positives are a little annoying, but usually not
> that big a deal because you're already looking at what you want to
> select, and the hint pops up right there.
>
> -Peff

I would be interested in the code for this.. I'm curious if I can
adapt it to my use of tmux.

Thanks,
Jake

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 19:16   ` Jacob Keller
@ 2017-01-20 19:25     ` Jeff King
  2017-01-21 12:44       ` Duy Nguyen
  2017-02-05 10:39     ` Duy Nguyen
  1 sibling, 1 reply; 12+ messages in thread
From: Jeff King @ 2017-01-20 19:25 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Nguyễn Thái Ngọc Duy, Git mailing list

On Fri, Jan 20, 2017 at 11:16:15AM -0800, Jacob Keller wrote:

> > I recently taught urxvt to recognize sha1s and grab them via keyboard
> > hints, and I'm finding it quite useful. Here's what it looks like if
> > you're interested:
> >
> >   http://peff.net/git-hints.gif
> >
> > The hints technique is taken from pentadactyl (which I also use), but
> > the urxvt port is mine. I'm happy to share the code.
> [...]
> 
> I would be interested in the code for this.. I'm curious if I can
> adapt it to my use of tmux.

See below. You can drop it into ~/.urxvt/ext/urlmatch and put something
like this into your Xresources:

  URxvt.keysym.C-b: urlmatch:list
  URxvt.keysym.C-semicolon: urlmatch:hint

I think the interesting bits if you're adapting it to another system
will be the regexes in find_commits(), and the key-at-a-time state
machine in handle_select_key().

Note that there's a sort-of bug in find_commits(). If you have:

  1234abcd..5678abcd

it finds three matches: one for each sha1, and one for the whole range.
But the range hint is covered up by the first sha1's hint.

And probably other bugs, too. I've only been using it for about a week,
and I did a bunch of cleanups this morning (after foolishly offering to
let other people see it ;) ).

-- >8 --
#!/usr/bin/perl

use warnings qw(all FATAL);
use strict;

sub on_action {
	my ($self, $action) = @_;
	($action, my @args) = split /:/, $action;
	if ($action eq 'list') {
		$self->matchlist(@args);
	} elsif ($action eq 'hint') {
		$self->open_hints(@args);
	}
	();
}

sub d { print STDERR "debug: " . join(' ', @_) . "\n"; }

sub handle_select_key {
	my ($self, $event, $keysym, $octets) = @_;

	my $input = $self->{input};

	my $escape;
	if (48 <= $keysym && $keysym <= 57) {
		$input->{number} .= ($keysym - 48);
	} elsif (97 <= $keysym && $keysym <= 122) {
		$input->{text} .= chr($keysym);
	} elsif ($self->XKeysymToString($keysym) eq 'Return') {
		$input->{number} .= '$';
	} elsif ($keysym == 59) {
		$input->{action} = 'yank';
		return 1;
	} else {
		$escape = 1;
	}

	if (!$escape) {
		my $min = 0;
		my $max = scalar(@{$input->{array}});

		my $nr_re = qr/^$input->{number}/;
		my $text_re = qr/$input->{text}/;
		my @matches;
		for (my $i = $min; $i < $max; $i++) {
			my $item = $input->{array}->[$i];
			if ($i =~ /$nr_re/ && $item->{text} =~ /$text_re/) {
				push @matches, $i;
			} elsif ($input->{unmatch}) {
				my $item = $input->{array}->[$i];
				$input->{unmatch}->($self, $item);
			}
		}
		if (@matches > 1) {
			# not enough data yet
			return 1;
		}
		if (@matches == 1) {
			my $item = $input->{array}->[$matches[0]];
			my $action = $input->{action} ||
			             $item->{action} ||
				     'activate';
			if ($action eq 'activate') {
				$self->exec_async(qw(webview), $item->{text});
			} elsif ($action eq 'yank') {
				$self->selection($item->{text});
				$self->selection_grab(urxvt::CurrentTime);
			}
		}
	}

	delete $self->{input};
	$self->disable('key_press');
	1;
}

sub enable_select {
	my $self = shift;
	$self->{input} = {text => '', number => '', @_};
	$self->enable(key_press => \&handle_select_key);
}

sub matchlist {
	my $self = shift;

	my @items = $self->find_matches(@_)
		or return;

	my $max_width = 0;
	for (my $i = 0; $i < @items; $i++) {
		my $item = $items[$i];

		$item->{number} = $i;
		$item->{menu} = "$i. $item->{text}";

		my $width = $self->strwidth($item->{menu});
		$max_width = $width if $width > $max_width;
	}

	my $rend = urxvt::SET_COLOR(urxvt::OVERLAY_RSTYLE, 0, 3);

	my $o = $self->overlay(0, 0, $max_width, scalar(@items), $rend, 0);
	for (my $i = 0; $i < @items; $i++) {
		$o->set(0, $i, $items[$i]->{menu});
	}
	$self->enable_select(
		array => \@items,
		overlay => $o,
		unmatch => sub {
			my ($self, $item) = @_;
			my $o = $self->{input}->{overlay};
			$o->set(0, $item->{number}, ' ' x length($item->{menu}));
		},
	);
}

sub open_hints {
	my $self = shift;

	my @items = $self->find_matches(@_)
		or return;

	for (my $i = 0; $i < @items; $i++) {
		my $item = $items[$i];

		$item->{overlay} = $self->overlay($item->{col}, $item->{row},
						  length($i), 1,
						  urxvt::OVERLAY_RSTYLE, 0);
		$item->{overlay}->set(0, 0, $i);
	}

	$self->enable_select(
		array => \@items,
		unmatch => sub {
			my ($self, $item) = @_;
			$item->{overlay} = undef;
		},
	);
}

my $URLCHAR = qr![-a-zA-Z:0-9./\#%+~_?=&()@;,]!;
my $URLEND = qr![a-zA-Z0-9/]!;
#my $URLLINE = qr!$URLCHAR{60,}$URLEND\n|$URLCHAR*$URLEND!;
my $URL = qr!$URLCHAR*$URLEND!;

sub find_urls {
	local $_ = shift;
	my @urls;

	while (m!(http|https|ftp|file)://$URL!g) {
		push @urls, [$-[0], $&];
	}
	while (m!^ *([a-zA-Z0-9]$URL\.(?:com|net|org)/$URL)!mg) {
		push @urls, [$-[0], "http://$&"];
	}
	while (m!(?:^|(?<=\s))(www|ftp)\.$URL!g) {
		push @urls, [$-[0], ($1 eq 'www' ? 'http://' : 'ftp://') . $&];
	}

	return map { {
		offset => $_->[0],
		text => $_->[1],
	} } @urls;
}

sub find_commits {
	local $_ = shift;

	my @items;
	while (m/(?:^|[^0-9a-f])
		 ([0-9a-f]{7,40})
		 (?![0-9a-f])/gx) {
		push @items, { offset => $-[1], text => $1, action => 'yank' };
	}
	while (m/(?:^|\s)
		 ([0-9a-f]{7,} \.{2,3} [0-9a-f]{7,})
		 (?:$|\s)/gx) {
		push @items, { offset => $-[1], text => $1, action => 'yank' };
	}
	return @items;
}

sub find_matches {
	my $self = shift;

	my @ret;
	my $row = 0;
	while ($row < $self->nrow) {
		my $line = $self->line($row);
		for my $item (find_line_matches($line->t)) {
			@$item{qw(row col)} = $line->coord_of($item->{offset});
			push @ret, $item;
		}
		$row = 1 + $line->end;
	}
	return @ret;
}

sub find_line_matches {
	my $buf = shift;
	return map {
		if ($_ eq 'url') { find_urls($buf) }
		elsif ($_ eq 'commit') { find_commits($buf) }
		else { d("unknown type: $_"); () }
	} @_ ? @_ : qw(url commit);
}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 19:25     ` Jeff King
@ 2017-01-21 12:44       ` Duy Nguyen
  2017-01-21 14:03         ` Jeff King
  0 siblings, 1 reply; 12+ messages in thread
From: Duy Nguyen @ 2017-01-21 12:44 UTC (permalink / raw)
  To: Jeff King, Jacob Keller; +Cc: Git mailing list

On Sat, Jan 21, 2017 at 2:25 AM, Jeff King <peff@peff.net> wrote:
> On Fri, Jan 20, 2017 at 11:16:15AM -0800, Jacob Keller wrote:
>
>> > I recently taught urxvt to recognize sha1s and grab them via keyboard
>> > hints, and I'm finding it quite useful. Here's what it looks like if
>> > you're interested:
>> >
>> >   http://peff.net/git-hints.gif
>> >
>> > The hints technique is taken from pentadactyl (which I also use), but
>> > the urxvt port is mine. I'm happy to share the code.

You just gave me a reason to rebuild urxvt with perl support. It
solves my problem with SHA-1 nicely (and fixes another problem with
very large counter in my approach, when you scroll git-log further
down, because I can't know what's on screen or already scrolled past).

Do you think it should be in contrib for better discoverability (than
burying it in the mail archive)? A separate git repo is also ok, but I
guess that's just more burden.

>> I would be interested in the code for this.. I'm curious if I can
>> adapt it to my use of tmux.

Yeah. Let me know if it works out. Would be nice to have this run on
basically any terminal, as long as I run tmux on top.
-- 
Duy

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-21 12:44       ` Duy Nguyen
@ 2017-01-21 14:03         ` Jeff King
  0 siblings, 0 replies; 12+ messages in thread
From: Jeff King @ 2017-01-21 14:03 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Jacob Keller, Git mailing list

On Sat, Jan 21, 2017 at 07:44:05PM +0700, Duy Nguyen wrote:

> You just gave me a reason to rebuild urxvt with perl support. It
> solves my problem with SHA-1 nicely (and fixes another problem with
> very large counter in my approach, when you scroll git-log further
> down, because I can't know what's on screen or already scrolled past).

Note that even without my module, you can probably use the "matcher"
extension which is included with urxvt to do something similar (it can
handle arbitrary numbers of matching regexps).

The things I like about my module (and why I bothered to write it) are:

  - it does nothing at all until you trigger it, so you don't have to
    worry about the cost of your regexes, or how ugly false positives
    will look when underlined

  - I think the hint mode is much better for keyboard navigation.
    The matcher extension does have a "list" mode where you get an
    overlay with the whole list and select by number. That's what I
    started with in mine, but I found it's really hard to use when there
    are a lot of matches. Especially with sha1s.

> Do you think it should be in contrib for better discoverability (than
> burying it in the mail archive)? A separate git repo is also ok, but I
> guess that's just more burden.

I'm not sure it makes sense in git's contrib. It's really more about
urxvt than it is about git.

-Peff

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-01-20 19:16   ` Jacob Keller
  2017-01-20 19:25     ` Jeff King
@ 2017-02-05 10:39     ` Duy Nguyen
  2017-02-05 22:45       ` Jacob Keller
  1 sibling, 1 reply; 12+ messages in thread
From: Duy Nguyen @ 2017-02-05 10:39 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Jeff King, Git mailing list

On Sat, Jan 21, 2017 at 2:16 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
> I would be interested in the code for this.. I'm curious if I can
> adapt it to my use of tmux.

I stumbled upon this which does mention about git SHA-1. Maybe you'll
find it useful. Haven't tried it out though.

https://github.com/morantron/tmux-fingers

-- 
Duy

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard
  2017-02-05 10:39     ` Duy Nguyen
@ 2017-02-05 22:45       ` Jacob Keller
  0 siblings, 0 replies; 12+ messages in thread
From: Jacob Keller @ 2017-02-05 22:45 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Jeff King, Git mailing list

On Sun, Feb 5, 2017 at 2:39 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Sat, Jan 21, 2017 at 2:16 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
>> I would be interested in the code for this.. I'm curious if I can
>> adapt it to my use of tmux.
>
> I stumbled upon this which does mention about git SHA-1. Maybe you'll
> find it useful. Haven't tried it out though.
>
> https://github.com/morantron/tmux-fingers
>
> --
> Duy

This sounds almost exactly like what I want! :)

Thanks,
Jake

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2017-02-05 22:45 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-20 10:22 [PATCH/TOY] Shortcuts to quickly refer to a commit name with keyboard Nguyễn Thái Ngọc Duy
2017-01-20 10:46 ` Johannes Schindelin
2017-01-20 11:01   ` Duy Nguyen
2017-01-20 15:21     ` Johannes Schindelin
2017-01-20 16:09 ` Jeff King
2017-01-20 18:27   ` Junio C Hamano
2017-01-20 19:16   ` Jacob Keller
2017-01-20 19:25     ` Jeff King
2017-01-21 12:44       ` Duy Nguyen
2017-01-21 14:03         ` Jeff King
2017-02-05 10:39     ` Duy Nguyen
2017-02-05 22:45       ` Jacob Keller

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).