git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] Custom extended SHA-1 syntax
@ 2010-12-24 14:07 Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 1/5] alias: add functions to do param substitution and alias running Nguyễn Thái Ngọc Duy
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

Merry Christmas!

This series introduces two new syntax: @{~foo} and ^{~foo}.
Both syntax allow an external program to run and return the result. So
users can extend the ref/sha1 syntax as they want. External commands are
defined similar to aliases in config.

The former does ref->ref mapping. The latter does SHA-1->SHA-1
mapping. Both allow @{} and ^{} in the result respectively, so you can
make a chain of mapping (*). This should allow Kevin to add "search
the n-th commit that matches 'foo'" syntax.

I think this is something nice to have. For one thing, commonly used
mappings can be collected in config file and distributed. Shell requirements
can be loosened because you don't need shell backquotes
(OK I'm always on a shell so this is moot, but an IDE, maybe).

OK time for movies..

(*) They can also make infinite chain. I don't think I can stop them
from doing that.

Nguyễn Thái Ngọc Duy (5):
  alias: add functions to do param substitution and alias running
  get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax
  sha1_name: move interpret_nth_prior_checkout closer to
    interpret_branch_name
  interpret_branch_name: takes @{u} code out and reorder the function
  get_sha1: allow custom ref mapping with $ref@{~alias} syntax

 Documentation/config.txt    |   18 +++
 Documentation/revisions.txt |   12 ++
 alias.c                     |  167 ++++++++++++++++++++++-
 cache.h                     |   13 ++
 sha1_name.c                 |  321 ++++++++++++++++++++++++++++++++----------
 t/t1511-rev-parse-caret.sh  |   45 ++++++
 t/t1512-rev-parse-at.sh     |   63 +++++++++
 7 files changed, 557 insertions(+), 82 deletions(-)
 create mode 100755 t/t1512-rev-parse-at.sh

-- 
1.7.3.3.476.g10a82

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

* [PATCH 1/5] alias: add functions to do param substitution and alias running
  2010-12-24 14:07 [PATCH 0/5] Custom extended SHA-1 syntax Nguyễn Thái Ngọc Duy
@ 2010-12-24 14:07 ` Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 2/5] get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

These functions help substitute %foo% in an alias command to real
values, then run the alias and return the first line from stdout.

The normal use case is:

    /* extract all %xxx% from cmd to params */
    extract_alias_params(cmd, params);
    param = lookup_alias_param(params, "%foo%");
    if (param)
        param->value = "value for %foo%";
    param = lookup_alias_param(params, "%bar%");
    if (param)
        param->value = "value for %bar%";
    /* substitute %foo% and %bar% */
    expand_alias_params(cmd, params);
    free_alias_params(params);
    if (!get_alias_oneline(alias, cmd, output))
        /* do something with output->buf here */

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 alias.c |  139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 cache.h |   11 +++++
 2 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/alias.c b/alias.c
index eb9f08b..6626bb0 100644
--- a/alias.c
+++ b/alias.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "run-command.h"
 
 static const char *alias_key;
 static char *alias_val;
@@ -85,3 +86,141 @@ int split_cmdline(char *cmdline, const char ***argv)
 const char *split_cmdline_strerror(int split_cmdline_errno) {
 	return split_cmdline_errors[-split_cmdline_errno-1];
 }
+
+int extract_alias_params(const char *cmd, struct alias_param **params)
+{
+	const char *s = cmd;
+	int nr_param = 0;
+
+	*params = NULL;
+	while (s && (s = strchr(s, '%')) != NULL) {
+		int len = strcspn(s+1, "% ")+2;
+		if (len < 2 || s[len-1] != '%')
+			return error("malformed parameter at %s", s);
+		nr_param++;
+		*params = xrealloc(*params, sizeof(struct alias_param)*nr_param);
+		(*params)[nr_param-1].param = xstrndup(s, len);
+		(*params)[nr_param-1].pos = s - cmd;
+		(*params)[nr_param-1].value = NULL;
+		s += len;
+	}
+
+	nr_param++;
+	*params = xrealloc(*params, sizeof(struct alias_param)*nr_param);
+	(*params)[nr_param-1].param = NULL;
+	(*params)[nr_param-1].value = NULL;
+	return 0;
+}
+
+struct alias_param *lookup_alias_param(struct alias_param *params, const char *param)
+{
+	int i;
+	for (i = 0; params[i].param; i++)
+		if (!strcmp(params[i].param, param))
+			return params+i;
+	return NULL;
+}
+
+int expand_alias_params(struct strbuf *cmd, const struct alias_param *params)
+{
+	int i, offset = 0;
+
+	/* TODO: quote for '!' commands */
+	for (i = 0; params[i].param; i++) {
+		if (!params[i].value)
+			return error("param %s not substituted", params[i].param);
+		strbuf_splice(cmd,
+			      params[i].pos + offset, strlen(params[i].param),
+			      params[i].value, strlen(params[i].value));
+		offset += strlen(params[i].value) - strlen(params[i].param);
+	}
+
+	return 0;
+}
+
+void free_alias_params(struct alias_param *params)
+{
+	int i;
+	for (i = 0; params[i].param; i++) {
+		free(params[i].param);
+		free(params[i].value);
+	}
+	free(params);
+}
+
+static void *wait_and_finish(void *arg)
+{
+	struct child_process *cp = arg;
+	char buf[1024];
+	while (xread(cp->out, buf, 1024) > 0)
+		;
+	close(cp->out);
+	finish_command(cp);
+	free(cp->argv);
+	free(cp);
+	return (void *) (intptr_t) 0;
+}
+
+static int start_support_alias(const char *alias, char *cmd, struct child_process **cpp)
+{
+	struct child_process *cp;
+	const char **argv;
+	int count;
+
+	cp = xmalloc(sizeof(struct child_process));
+	memset(cp, 0, sizeof(struct child_process));
+	cp->in = 0;
+	cp->out = -1;
+
+	if (cmd[0] == '!') {
+		argv = xmalloc(sizeof(*argv)*4);
+		argv[0] = "/bin/sh";
+		argv[1] = "-c";
+		argv[2] = cmd+1;
+		argv[3] = NULL;
+	}
+	else {
+		count = split_cmdline(cmd, &argv);
+		if (count < 0) {
+			free(cp);
+			return error("Bad alias %s: %s", cmd, split_cmdline_strerror(count));
+		}
+		cp->git_cmd = 1;
+	}
+
+	cp->argv = argv;
+	if (start_command(cp)) {
+		error("Failed to run %s", cmd);
+		free(cp);
+		free(argv);
+		return -1;
+	}
+	*cpp = cp;
+	return 0;
+}
+
+int get_alias_oneline(const char *alias, char *cmd, struct strbuf *ref)
+{
+	struct child_process *cp;
+	FILE *fp;
+	int ret;
+
+	ret = start_support_alias(alias, cmd, &cp);
+	if (ret)
+		return ret;
+
+	fp = fdopen(cp->out, "r");
+	ret = strbuf_getline(ref, fp, '\n');
+	if (!ret) {
+		/* let it finish, if there's error, users should know */
+#ifdef NO_PTHREADS
+		wait_and_finish(cp);
+#else
+		pthread_t thread;
+		pthread_create(&thread, NULL, wait_and_finish, cp);
+#endif
+		return 0;
+	}
+	wait_and_finish(cp);
+	return -1;
+}
diff --git a/cache.h b/cache.h
index e83bc2d..20a37ff 100644
--- a/cache.h
+++ b/cache.h
@@ -1113,10 +1113,21 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
+struct alias_param {
+	char *param;
+	int pos;
+	char *value;
+};
+
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
+int extract_alias_params(const char *cmd, struct alias_param **params);
+struct alias_param *lookup_alias_param(struct alias_param *params, const char *param);
+int expand_alias_params(struct strbuf *cmd, const struct alias_param *params);
+void free_alias_params(struct alias_param *params);
+int get_alias_oneline(const char *alias, char *cmd, struct strbuf *result);
 
 /* git.c */
 struct startup_info {
-- 
1.7.3.3.476.g10a82

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

* [PATCH 2/5] get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax
  2010-12-24 14:07 [PATCH 0/5] Custom extended SHA-1 syntax Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 1/5] alias: add functions to do param substitution and alias running Nguyễn Thái Ngọc Duy
@ 2010-12-24 14:07 ` Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 3/5] sha1_name: move interpret_nth_prior_checkout closer to interpret_branch_name Nguyễn Thái Ngọc Duy
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

"alias" is defined in config as sha1.alias. %sha1% and %arg% in the
alias command will be substituted.

The alias command is supposed to return a piece of text that
get_sha1() can consume, preferably straight SHA-1.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt    |    9 ++++
 Documentation/revisions.txt |    7 +++
 alias.c                     |   23 +++++++++--
 cache.h                     |    1 +
 sha1_name.c                 |   86 +++++++++++++++++++++++++++++++++++++++++++
 t/t1511-rev-parse-caret.sh  |   45 ++++++++++++++++++++++
 6 files changed, 166 insertions(+), 5 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0f85793..0c4fb66 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -588,6 +588,15 @@ it will be treated as a shell command.  For example, defining
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
 
+sha1.*::
+	Extended SHA-1 syntax aliases. These aliases are similar to alias.*
+	but each will correspond to a '{caret}\{{tilde}alias\}' syntax (see
+	linkgit:gitrevisions[7]). '%sha1%' in the alias command will be
+	substituted with resolved SHA-1 before the caret. If
+	'{caret}\{{tilde}alias:extra\}' syntax is used, '%arg%' in the
+	alias command will be substituted with 'extra'. The command is
+	supposed to return an extended SHA-1 syntax.
+
 am.keepcr::
 	If true, git-am will call git-mailsplit for patches in mbox format
 	with parameter '--keep-cr'. In this case git-mailsplit will
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 174fa8e..413f91c 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -106,6 +106,13 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
   and dereference the tag recursively until a non-tag object is
   found.
 
+* A suffix '{caret}' followed by an SHA-1 alias with a leading
+  '{tilde}', all enclosed in brace pair (e.g.
+  `v0.998{caret}\{{tilde}alias\}`) will invoke the corresponding
+  alias command, specified in config (see linkgit:git-config[1],
+  key 'sha1.*'). The command is supposed to return an extended
+  SHA-1.
+
 * A suffix '{caret}' to a revision parameter followed by a brace
   pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`):
   this is the same as `:/fix nasty bug` syntax below except that
diff --git a/alias.c b/alias.c
index 6626bb0..29a9903 100644
--- a/alias.c
+++ b/alias.c
@@ -1,12 +1,12 @@
 #include "cache.h"
 #include "run-command.h"
 
-static const char *alias_key;
 static char *alias_val;
 
 static int alias_lookup_cb(const char *k, const char *v, void *cb)
 {
-	if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+	const char *key = cb;
+	if (!strcmp(k, key)) {
 		if (!v)
 			return config_error_nonbool(k);
 		alias_val = xstrdup(v);
@@ -15,14 +15,27 @@ static int alias_lookup_cb(const char *k, const char *v, void *cb)
 	return 0;
 }
 
-char *alias_lookup(const char *alias)
+static char *generic_alias_lookup(const char *alias, const char *namespase)
 {
-	alias_key = alias;
+	struct strbuf key = STRBUF_INIT;
+	strbuf_addstr(&key, namespase);
+	strbuf_addstr(&key, alias);
 	alias_val = NULL;
-	git_config(alias_lookup_cb, NULL);
+	git_config(alias_lookup_cb, key.buf);
+	strbuf_release(&key);
 	return alias_val;
 }
 
+char *alias_lookup(const char *alias)
+{
+	return generic_alias_lookup(alias, "alias.");
+}
+
+char *sha1_alias_lookup(const char *alias)
+{
+	return generic_alias_lookup(alias, "sha1.");
+}
+
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 static const char *split_cmdline_errors[] = {
diff --git a/cache.h b/cache.h
index 20a37ff..56a1b18 100644
--- a/cache.h
+++ b/cache.h
@@ -1120,6 +1120,7 @@ struct alias_param {
 };
 
 char *alias_lookup(const char *alias);
+char *sha1_alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/sha1_name.c b/sha1_name.c
index c5c59ce..3a98a50 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -529,6 +529,76 @@ struct object *peel_to_type(const char *name, int namelen,
 	}
 }
 
+/*
+ * Handle $SHA1^{~alias} syntax. Config key sha1.alias will be used.
+ * The following parameters are substituted:
+ *
+ *  - %sha1%  SHA-1 before ^{~alias}, required
+ *  - %arg%   extra arguments after colon in SHA1^{~alias:args}
+ *
+ * The alias returns a string, preferabbly SHA-1.
+ */
+static int peel_alias(const char *name, int len, const char *sp,
+		      const unsigned char *input_sha1,
+		      struct strbuf *result)
+{
+	struct alias_param *params, *param;
+	struct strbuf alias = STRBUF_INIT;
+	struct strbuf cmd = STRBUF_INIT;
+	char *s;
+	int ret, arg_len = 0;
+
+	if (name[len-1] != '}')
+		return -1;
+	len -= sp - name + 1; /* remove $SHA1^{~ and } */
+	if (!len)
+		return error("$SHA1^{~alias} syntax with no alias");
+
+	s = strchr(sp, ':');
+	if (s) {
+		arg_len = (sp + len) - (s + 1);
+		len = s - sp;
+	}
+	strbuf_add(&alias, sp, len);
+	s = sha1_alias_lookup(alias.buf);
+	if (!s) {
+		ret = error("unable to find SHA-1 alias '%s'", alias.buf);
+		goto done;
+	}
+	strbuf_attach(&cmd, s, strlen(s), strlen(s)+1);
+
+	extract_alias_params(cmd.buf, &params);
+	param = lookup_alias_param(params, "%sha1%");
+	if (!param) {		/* %sha1% is mandatory */
+		ret = error("%%sha1%% not found in alias '%s'", alias.buf);
+		goto done;
+	}
+	param->value = xstrdup(sha1_to_hex(input_sha1));
+
+	param = lookup_alias_param(params, "%arg%");
+	if (param && arg_len)
+		param->value = xstrndup(sp + len + 1,arg_len);
+	else if (param) {
+		ret = error("Alias '%s' needs arguments", alias.buf);
+		goto done;
+	}
+	else if (arg_len) {
+		ret = error("Arguments are given but alias '%s' does not need them", alias.buf);
+		goto done;
+	}
+
+	expand_alias_params(&cmd, params);
+	free_alias_params(params);
+
+	trace_printf("trace: sha1 alias expansion: %s => %s\n",
+		     alias.buf, cmd.buf);
+	ret = get_alias_oneline(alias.buf, cmd.buf, result);
+done:
+	strbuf_release(&alias);
+	strbuf_release(&cmd);
+	return ret;
+}
+
 static int peel_onion(const char *name, int len, unsigned char *sha1)
 {
 	unsigned char outer[20];
@@ -566,6 +636,22 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 		expected_type = OBJ_NONE;
 	else if (sp[0] == '/')
 		expected_type = OBJ_COMMIT;
+	else if (sp[0] == '~') {
+		struct strbuf new_name = STRBUF_INIT;
+
+		if (get_sha1_1(name, sp - name - 2, outer))
+			return -1;
+
+		if (peel_alias(name, len, sp + 1, outer, &new_name))
+			return -1;
+		if (get_sha1_1(new_name.buf, new_name.len, sha1)) {
+			strbuf_release(&new_name);
+			return -1;
+		}
+
+		strbuf_release(&new_name);
+		return 0;
+	}
 	else
 		return -1;
 
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
index e043cb7..b99bfa2 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -70,4 +70,49 @@ test_expect_success 'ref^{/Initial}' '
 	test_cmp expected actual
 '
 
+test_expect_success 'ref^{~}' '
+	test_must_fail git rev-parse HEAD^{~}
+'
+
+test_expect_success 'ref^{~non-existent}' '
+	test_must_fail git rev-parse HEAD^{~non-existent}
+'
+
+test_expect_success 'ref^{~simple}' '
+	git config sha1.simple "rev-parse %sha1%" &&
+	test_must_fail git rev-parse master^{~simple:something} &&
+	git rev-parse master^{~simple} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~external}' '
+	git config sha1.external "!echo %sha1%" &&
+	test_must_fail git rev-parse master^{~external:something} &&
+	git rev-parse master^{~external} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~witharg:args}' '
+	git config sha1.witharg "!echo %sha1%^{%arg%}" &&
+	test_must_fail git rev-parse master^{~witharg} &&
+	git rev-parse master^{~witharg:tree} >actual &&
+	git rev-parse master^{tree} >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~simple}^{~external}' '
+	git rev-parse master^{~simple}^{~external} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~mapping}' '
+	git config sha1.mapping "!echo %sha1%^{~simple}" &&
+	git rev-parse master^{~mapping} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
 test_done
-- 
1.7.3.3.476.g10a82

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

* [PATCH 3/5] sha1_name: move interpret_nth_prior_checkout closer to interpret_branch_name
  2010-12-24 14:07 [PATCH 0/5] Custom extended SHA-1 syntax Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 1/5] alias: add functions to do param substitution and alias running Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 2/5] get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax Nguyễn Thái Ngọc Duy
@ 2010-12-24 14:07 ` Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 4/5] interpret_branch_name: takes @{u} code out and reorder the function Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 5/5] get_sha1: allow custom ref mapping with $ref@{~alias} syntax Nguyễn Thái Ngọc Duy
  4 siblings, 0 replies; 6+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

interpret_nth_prior_checkout() is a subfunction of
interpret_branch_name(). So it's better to let them be close together
(i.e. not letting get_sha1_mb() stays in between).

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 sha1_name.c |   84 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index 3a98a50..dcdf1e6 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -883,6 +883,48 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 	return 0;
 }
 
+int get_sha1_mb(const char *name, unsigned char *sha1)
+{
+	struct commit *one, *two;
+	struct commit_list *mbs;
+	unsigned char sha1_tmp[20];
+	const char *dots;
+	int st;
+
+	dots = strstr(name, "...");
+	if (!dots)
+		return get_sha1(name, sha1);
+	if (dots == name)
+		st = get_sha1("HEAD", sha1_tmp);
+	else {
+		struct strbuf sb;
+		strbuf_init(&sb, dots - name);
+		strbuf_add(&sb, name, dots - name);
+		st = get_sha1(sb.buf, sha1_tmp);
+		strbuf_release(&sb);
+	}
+	if (st)
+		return st;
+	one = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!one)
+		return -1;
+
+	if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+		return -1;
+	two = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!two)
+		return -1;
+	mbs = get_merge_bases(one, two, 1);
+	if (!mbs || mbs->next)
+		st = -1;
+	else {
+		st = 0;
+		hashcpy(sha1, mbs->item->object.sha1);
+	}
+	free_commit_list(mbs);
+	return st;
+}
+
 /*
  * Parse @{-N} syntax, return the number of characters parsed
  * if successful; otherwise signal an error with negative value.
@@ -931,48 +973,6 @@ release_return:
 	return retval;
 }
 
-int get_sha1_mb(const char *name, unsigned char *sha1)
-{
-	struct commit *one, *two;
-	struct commit_list *mbs;
-	unsigned char sha1_tmp[20];
-	const char *dots;
-	int st;
-
-	dots = strstr(name, "...");
-	if (!dots)
-		return get_sha1(name, sha1);
-	if (dots == name)
-		st = get_sha1("HEAD", sha1_tmp);
-	else {
-		struct strbuf sb;
-		strbuf_init(&sb, dots - name);
-		strbuf_add(&sb, name, dots - name);
-		st = get_sha1(sb.buf, sha1_tmp);
-		strbuf_release(&sb);
-	}
-	if (st)
-		return st;
-	one = lookup_commit_reference_gently(sha1_tmp, 0);
-	if (!one)
-		return -1;
-
-	if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
-		return -1;
-	two = lookup_commit_reference_gently(sha1_tmp, 0);
-	if (!two)
-		return -1;
-	mbs = get_merge_bases(one, two, 1);
-	if (!mbs || mbs->next)
-		st = -1;
-	else {
-		st = 0;
-		hashcpy(sha1, mbs->item->object.sha1);
-	}
-	free_commit_list(mbs);
-	return st;
-}
-
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
-- 
1.7.3.3.476.g10a82

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

* [PATCH 4/5] interpret_branch_name: takes @{u} code out and reorder the function
  2010-12-24 14:07 [PATCH 0/5] Custom extended SHA-1 syntax Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2010-12-24 14:07 ` [PATCH 3/5] sha1_name: move interpret_nth_prior_checkout closer to interpret_branch_name Nguyễn Thái Ngọc Duy
@ 2010-12-24 14:07 ` Nguyễn Thái Ngọc Duy
  2010-12-24 14:07 ` [PATCH 5/5] get_sha1: allow custom ref mapping with $ref@{~alias} syntax Nguyễn Thái Ngọc Duy
  4 siblings, 0 replies; 6+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

Previously interpret_branch_name() is structured as:

 - check for @{-N}
 - if there's still some more chars left, recurse to try again
 - check for @{upstream}

Now it looks like

 - check for @{-N}
 - check for @{upstream}
 - still more? recurse

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 sha1_name.c |   99 ++++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 57 insertions(+), 42 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index dcdf1e6..cdf14c7 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -973,6 +973,27 @@ release_return:
 	return retval;
 }
 
+static int interpret_at_upstream(const char *at, struct strbuf *real_ref)
+{
+	struct branch *upstream;
+	int cplen = strlen(at);
+	int len;
+	char *upstream_ref;
+
+	len = upstream_mark(at, cplen);
+	if (!len)
+		return -1;
+	upstream = branch_get(real_ref->buf);
+	if (!upstream
+	    || !upstream->merge
+	    || !upstream->merge[0]->dst)
+		return error("No upstream branch found for '%s'", real_ref->buf);
+	upstream_ref = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
+	cplen = strlen(upstream_ref);
+	strbuf_attach(real_ref, upstream_ref, cplen, cplen+1);
+	return len;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -996,55 +1017,49 @@ release_return:
  */
 int interpret_branch_name(const char *name, struct strbuf *buf)
 {
-	char *cp;
-	struct branch *upstream;
 	int namelen = strlen(name);
-	int len = interpret_nth_prior_checkout(name, buf);
-	int tmp_len;
-
-	if (!len)
-		return len; /* syntax Ok, not enough switches */
-	if (0 < len && len == namelen)
-		return len; /* consumed all */
-	else if (0 < len) {
-		/* we have extra data, which might need further processing */
-		struct strbuf tmp = STRBUF_INIT;
-		int used = buf->len;
-		int ret;
-
-		strbuf_add(buf, name + len, namelen - len);
-		ret = interpret_branch_name(buf->buf, &tmp);
-		/* that data was not interpreted, remove our cruft */
-		if (ret < 0) {
-			strbuf_setlen(buf, used);
-			return len;
-		}
-		strbuf_reset(buf);
-		strbuf_addbuf(buf, &tmp);
-		strbuf_release(&tmp);
-		/* tweak for size of {-N} versus expanded ref name */
-		return ret - used + len;
-	}
+	struct strbuf tmp = STRBUF_INIT;
+	int ret, len = -1, used = 0;
+	const char *cp;
 
 	cp = strchr(name, '@');
 	if (!cp)
 		return -1;
-	tmp_len = upstream_mark(cp, namelen - (cp - name));
-	if (!tmp_len)
+	if (cp == name)
+		len = interpret_nth_prior_checkout(cp, buf);
+	else {
+		strbuf_reset(buf);
+		strbuf_add(buf, name, cp - name);
+		used += cp - name;
+	}
+
+	if (len == -1)
+		len = interpret_at_upstream(cp, buf);
+	if (len == -1) {
+		strbuf_reset(buf);
 		return -1;
-	len = cp + tmp_len - name;
-	cp = xstrndup(name, cp - name);
-	upstream = branch_get(*cp ? cp : NULL);
-	if (!upstream
-	    || !upstream->merge
-	    || !upstream->merge[0]->dst)
-		return error("No upstream branch found for '%s'", cp);
-	free(cp);
-	cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
+	}
+
+	if (!len)
+		return len; /* syntax Ok, not enough switches */
+
+	used += len;
+	if (used == namelen)
+		return used; /* consumed all */
+
+	/* we have extra data, which might need further processing */
+	len = buf->len;
+	strbuf_add(buf, name + used, namelen - used);
+	ret = interpret_branch_name(buf->buf, &tmp);
+	/* that data was not interpreted, remove our cruft */
+	if (ret < 0) {
+		strbuf_setlen(buf, len);
+		return used;
+	}
 	strbuf_reset(buf);
-	strbuf_addstr(buf, cp);
-	free(cp);
-	return len;
+	strbuf_addbuf(buf, &tmp);
+	strbuf_release(&tmp);
+	return used + (ret - len);
 }
 
 int strbuf_branchname(struct strbuf *sb, const char *name)
-- 
1.7.3.3.476.g10a82

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

* [PATCH 5/5] get_sha1: allow custom ref mapping with $ref@{~alias} syntax
  2010-12-24 14:07 [PATCH 0/5] Custom extended SHA-1 syntax Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2010-12-24 14:07 ` [PATCH 4/5] interpret_branch_name: takes @{u} code out and reorder the function Nguyễn Thái Ngọc Duy
@ 2010-12-24 14:07 ` Nguyễn Thái Ngọc Duy
  4 siblings, 0 replies; 6+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

Similar to $SHA1^{~alias}, the alias is defined as ref.alias. It is
supposed to return something that can resolve to a ref.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt    |    9 +++++
 Documentation/revisions.txt |    5 +++
 alias.c                     |    5 +++
 cache.h                     |    1 +
 sha1_name.c                 |   70 +++++++++++++++++++++++++++++++++++++++++-
 t/t1512-rev-parse-at.sh     |   63 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 151 insertions(+), 2 deletions(-)
 create mode 100755 t/t1512-rev-parse-at.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0c4fb66..a20b467 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -597,6 +597,15 @@ sha1.*::
 	alias command will be substituted with 'extra'. The command is
 	supposed to return an extended SHA-1 syntax.
 
+ref.*::
+	Ref translation aliases. These aliases are similar to alias.*
+	but each will correspond to a '@\{{tilde}alias\}' syntax (see
+	linkgit:gitrevisions[7]). '%ref%' in the alias command will be
+	substituted with the ref before the '@'. If
+	'@\{{tilde}alias:extra\}' syntax is used, '%arg%' in the alias
+	command will be substituted with 'extra'. The command is supposed
+	to return a ref.
+
 am.keepcr::
 	If true, git-am will call git-mailsplit for patches in mbox format
 	with parameter '--keep-cr'. In this case git-mailsplit will
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 413f91c..86b0b63 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -80,6 +80,11 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
   the branch the ref is set to build on top of.  Missing ref defaults
   to the current branch.
 
+* A ref followed by '@\{{tilde}alias\}' will invoke the corresponding
+  alias command, specified in config (see linkgit:git-config[1],
+  key 'ref.*'). The command is supposed to return a ref. The
+  result ref can contain '@\{..\}' syntax.
+
 * A suffix '{caret}' to a revision parameter (e.g. 'HEAD{caret}') means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
diff --git a/alias.c b/alias.c
index 29a9903..242401e 100644
--- a/alias.c
+++ b/alias.c
@@ -36,6 +36,11 @@ char *sha1_alias_lookup(const char *alias)
 	return generic_alias_lookup(alias, "sha1.");
 }
 
+char *ref_alias_lookup(const char *alias)
+{
+	return generic_alias_lookup(alias, "ref.");
+}
+
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 static const char *split_cmdline_errors[] = {
diff --git a/cache.h b/cache.h
index 56a1b18..1d4ce65 100644
--- a/cache.h
+++ b/cache.h
@@ -1121,6 +1121,7 @@ struct alias_param {
 
 char *alias_lookup(const char *alias);
 char *sha1_alias_lookup(const char *alias);
+char *ref_alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/sha1_name.c b/sha1_name.c
index cdf14c7..2734f24 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -359,7 +359,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 	if (len && str[len-1] == '}') {
 		for (at = len-2; at >= 0; at--) {
 			if (str[at] == '@' && str[at+1] == '{') {
-				if (!upstream_mark(str + at, len - at)) {
+				if (!upstream_mark(str + at, len - at) &&
+				    str[at+2] != '~') {
 					reflog_len = (len-1) - (at+2);
 					len = at;
 				}
@@ -994,6 +995,69 @@ static int interpret_at_upstream(const char *at, struct strbuf *real_ref)
 	return len;
 }
 
+static int interpret_at_alias(const char *at, struct strbuf *real_ref)
+{
+	struct alias_param *params, *param;
+	struct strbuf alias = STRBUF_INIT;
+	struct strbuf cmd = STRBUF_INIT;
+	char *s;
+	int ret, len, arg_len = 0;
+
+	if (at[1] != '{' || at[2] != '~')
+		return -1;
+	len = strcspn(at + 3, ":}");
+	if (!len)
+		return error("$ref@{~alias} syntax with no alias");
+	if (at[3 + len] == ':') {
+		s = strchr(at + 3 + len + 1, '}');
+		if (!s)
+			return error("$ref@{~alias} does not end with }");
+		arg_len = s - at - (3 + len + 1);
+	}
+	strbuf_add(&alias, at + 3, len);
+	s = ref_alias_lookup(alias.buf);
+	if (!s) {
+		ret = error("unable to find ref alias '%s'", alias.buf);
+		goto done;
+	}
+
+	strbuf_attach(&cmd, s, strlen(s), strlen(s)+1);
+	extract_alias_params(cmd.buf, &params);
+	param = lookup_alias_param(params, "%ref%");
+	if (param && real_ref->len)
+		param->value = xstrdup(real_ref->buf);
+	else if (param) {
+		ret = error("%%ref%% not found in alias '%s'", cmd.buf);
+		goto done;
+	}
+	else if (real_ref->len) {
+		ret = error("%%ref%% is required but alias '%s' is not followed by a ref", cmd.buf);
+		goto done;
+	}
+	param = lookup_alias_param(params, "%arg%");
+	if (param && arg_len) {
+		param->value = xstrndup(at + len + 4, arg_len);
+		len += arg_len + 1;
+	}
+	else if (param) {
+		ret = error("Alias '%s' needs arguments", alias.buf);
+		goto done;
+	}
+	else if (arg_len) {
+		ret = error("Arguments are given but alias '%s' does not need them", alias.buf);
+		goto done;
+	}
+	expand_alias_params(&cmd, params);
+	free_alias_params(params);
+
+	trace_printf("trace: ref alias expansion: %s => %s\n", alias.buf, cmd.buf);
+	ret = get_alias_oneline(alias.buf, cmd.buf, real_ref) ? -1 : len + 4;
+done:
+	strbuf_release(&alias);
+	strbuf_release(&cmd);
+	return ret;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -1035,6 +1099,8 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
 
 	if (len == -1)
 		len = interpret_at_upstream(cp, buf);
+	if (len == -1)
+		len = interpret_at_alias(cp, buf);
 	if (len == -1) {
 		strbuf_reset(buf);
 		return -1;
@@ -1044,7 +1110,7 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
 		return len; /* syntax Ok, not enough switches */
 
 	used += len;
-	if (used == namelen)
+	if (used == namelen && !strstr(buf->buf, "@{"))
 		return used; /* consumed all */
 
 	/* we have extra data, which might need further processing */
diff --git a/t/t1512-rev-parse-at.sh b/t/t1512-rev-parse-at.sh
new file mode 100755
index 0000000..6594146
--- /dev/null
+++ b/t/t1512-rev-parse-at.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='tests for $ref@{something}'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit foo &&
+	git branch foo &&
+	test_commit bar &&
+	git branch bar &&
+	test_commit zoo-bar &&
+	git branch zoo/bar &&
+	test_commit zoo-zoo-bar &&
+	git branch zoo/zoo/bar
+'
+
+test_expect_success 'ref@{~}' '
+	test_must_fail git rev-parse HEAD@{~}
+'
+
+test_expect_success 'ref@{~non-existent}' '
+	test_must_fail git rev-parse HEAD@{~non-existent}
+'
+
+test_expect_success 'ref@{~simple}' '
+	git config ref.simple "symbolic-ref %ref%" &&
+	test_must_fail git rev-parse HEAD@{~simple:something} &&
+	git rev-parse HEAD@{~simple} >actual &&
+	git rev-parse HEAD >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~external}' '
+	git config ref.external "!echo zoo/%ref%" &&
+	test_must_fail git rev-parse bar@{~external:something} &&
+	git rev-parse bar@{~external} >actual &&
+	git rev-parse zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~witharg:args}' '
+	git config ref.witharg "!echo %ref%/%arg%" &&
+	test_must_fail git rev-parse zoo@{~witharg} &&
+	git rev-parse zoo@{~witharg:bar} >actual &&
+	git rev-parse zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~external}@{~external}' '
+	git rev-parse bar@{~external}@{~external} >actual &&
+	git rev-parse zoo/zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~mapping}' '
+	git config ref.mapping "!echo %ref%@{~external}@{~external}" &&
+	git rev-parse bar@{~mapping} >actual &&
+	git rev-parse zoo/zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_done
-- 
1.7.3.3.476.g10a82

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

end of thread, other threads:[~2010-12-24 14:09 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-24 14:07 [PATCH 0/5] Custom extended SHA-1 syntax Nguyễn Thái Ngọc Duy
2010-12-24 14:07 ` [PATCH 1/5] alias: add functions to do param substitution and alias running Nguyễn Thái Ngọc Duy
2010-12-24 14:07 ` [PATCH 2/5] get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax Nguyễn Thái Ngọc Duy
2010-12-24 14:07 ` [PATCH 3/5] sha1_name: move interpret_nth_prior_checkout closer to interpret_branch_name Nguyễn Thái Ngọc Duy
2010-12-24 14:07 ` [PATCH 4/5] interpret_branch_name: takes @{u} code out and reorder the function Nguyễn Thái Ngọc Duy
2010-12-24 14:07 ` [PATCH 5/5] get_sha1: allow custom ref mapping with $ref@{~alias} syntax Nguyễn Thái Ngọc Duy

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 NNTP newsgroup(s).