* [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, ¶ms);
+ 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, ¶ms);
+ 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