summaryrefslogtreecommitdiff
path: root/builtin/rev-parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/rev-parse.c')
-rw-r--r--builtin/rev-parse.c303
1 files changed, 216 insertions, 87 deletions
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 98d1cbecca..c961b74c5a 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -9,6 +9,9 @@
#include "quote.h"
#include "builtin.h"
#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
+#include "split-index.h"
#define DO_REVS 1
#define DO_NOREV 2
@@ -30,6 +33,9 @@ static int abbrev_ref;
static int abbrev_ref_strict;
static int output_sq;
+static int stuck_long;
+static struct string_list *ref_excludes;
+
/*
* Some arguments are relevant "revision" arguments,
* others are about output format or other details.
@@ -145,6 +151,7 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
error("refname '%s' is ambiguous", name);
break;
}
+ free(full);
} else {
show_with_type(type, name);
}
@@ -183,15 +190,23 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
- show_rev(NORMAL, sha1, refname);
+ if (ref_excluded(ref_excludes, refname))
+ return 0;
+ show_rev(NORMAL, oid->hash, refname);
return 0;
}
-static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
- show_rev(REVERSED, sha1, refname);
+ show_rev(REVERSED, oid->hash, refname);
+ return 0;
+}
+
+static int show_abbrev(const unsigned char *sha1, void *cb_data)
+{
+ show_rev(NORMAL, sha1, NULL);
return 0;
}
@@ -206,11 +221,17 @@ static void show_datestring(const char *flag, const char *datestr)
show(buffer);
}
-static int show_file(const char *arg)
+static int show_file(const char *arg, int output_prefix)
{
show_default();
if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
- show(arg);
+ if (output_prefix) {
+ const char *prefix = startup_info->prefix;
+ show(prefix_filename(prefix,
+ prefix ? strlen(prefix) : 0,
+ arg));
+ } else
+ show(arg);
return 1;
}
return 0;
@@ -224,6 +245,7 @@ static int try_difference(const char *arg)
const char *next;
const char *this;
int symmetric;
+ static const char head_by_default[] = "HEAD";
if (!(dotdot = strstr(arg, "..")))
return 0;
@@ -235,10 +257,21 @@ static int try_difference(const char *arg)
next += symmetric;
if (!*next)
- next = "HEAD";
+ next = head_by_default;
if (dotdot == arg)
- this = "HEAD";
- if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
+ this = head_by_default;
+
+ if (this == head_by_default && next == head_by_default &&
+ !symmetric) {
+ /*
+ * Just ".."? That is not a range but the
+ * pathspec for the parent directory.
+ */
+ *dotdot = '.';
+ return 0;
+ }
+
+ if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
show_rev(NORMAL, end, next);
show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
if (symmetric) {
@@ -246,15 +279,13 @@ static int try_difference(const char *arg)
struct commit *a, *b;
a = lookup_commit_reference(sha1);
b = lookup_commit_reference(end);
- exclude = get_merge_bases(a, b, 1);
+ exclude = get_merge_bases(a, b);
while (exclude) {
- struct commit_list *n = exclude->next;
- show_rev(REVERSED,
- exclude->item->object.sha1,NULL);
- free(exclude);
- exclude = n;
+ struct commit *commit = pop_commit(&exclude);
+ show_rev(REVERSED, commit->object.oid.hash, NULL);
}
}
+ *dotdot = '.';
return 1;
}
*dotdot = '.';
@@ -278,16 +309,19 @@ static int try_parent_shorthands(const char *arg)
return 0;
*dotdot = 0;
- if (get_sha1(arg, sha1))
+ if (get_sha1_committish(arg, sha1)) {
+ *dotdot = '^';
return 0;
+ }
if (!parents_only)
show_rev(NORMAL, sha1, arg);
commit = lookup_commit_reference(sha1);
for (parents = commit->parents; parents; parents = parents->next)
show_rev(parents_only ? NORMAL : REVERSED,
- parents->item->object.sha1, arg);
+ parents->item->object.oid.hash, arg);
+ *dotdot = '^';
return 1;
}
@@ -296,12 +330,15 @@ static int parseopt_dump(const struct option *o, const char *arg, int unset)
struct strbuf *parsed = o->value;
if (unset)
strbuf_addf(parsed, " --no-%s", o->long_name);
- else if (o->short_name)
+ else if (o->short_name && (o->long_name == NULL || !stuck_long))
strbuf_addf(parsed, " -%c", o->short_name);
else
strbuf_addf(parsed, " --%s", o->long_name);
if (arg) {
- strbuf_addch(parsed, ' ');
+ if (!stuck_long)
+ strbuf_addch(parsed, ' ');
+ else if (o->long_name)
+ strbuf_addch(parsed, '=');
sq_quote_buf(parsed, arg);
}
return 0;
@@ -318,17 +355,20 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
{
static int keep_dashdash = 0, stop_at_non_option = 0;
static char const * const parseopt_usage[] = {
- "git rev-parse --parseopt [options] -- [<args>...]",
+ N_("git rev-parse --parseopt [<options>] -- [<args>...]"),
NULL
};
static struct option parseopt_opts[] = {
- OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
- "keep the `--` passed as an arg"),
- OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
- "stop parsing after the "
- "first non-option argument"),
+ OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
+ N_("keep the `--` passed as an arg")),
+ OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
+ N_("stop parsing after the "
+ "first non-option argument")),
+ OPT_BOOL(0, "stuck-long", &stuck_long,
+ N_("output in stuck long form")),
OPT_END(),
};
+ static const char * const flag_chars = "*=?!";
struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
const char **usage = NULL;
@@ -343,7 +383,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
/* get the usage up to the first line with a -- on it */
for (;;) {
- if (strbuf_getline(&sb, stdin, '\n') == EOF)
+ if (strbuf_getline(&sb, stdin) == EOF)
die("premature end of input");
ALLOC_GROW(usage, unb + 1, usz);
if (!strcmp("--", sb.buf)) {
@@ -355,9 +395,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
usage[unb++] = strbuf_detach(&sb, NULL);
}
- /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
- while (strbuf_getline(&sb, stdin, '\n') != EOF) {
+ /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
+ while (strbuf_getline(&sb, stdin) != EOF) {
const char *s;
+ const char *help;
struct option *o;
if (!sb.len)
@@ -367,44 +408,56 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
memset(opts + onb, 0, sizeof(opts[onb]));
o = &opts[onb++];
- s = strchr(sb.buf, ' ');
- if (!s || *sb.buf == ' ') {
+ help = strchr(sb.buf, ' ');
+ if (!help || *sb.buf == ' ') {
o->type = OPTION_GROUP;
o->help = xstrdup(skipspaces(sb.buf));
continue;
}
o->type = OPTION_CALLBACK;
- o->help = xstrdup(skipspaces(s));
+ o->help = xstrdup(skipspaces(help));
o->value = &parsed;
o->flags = PARSE_OPT_NOARG;
o->callback = &parseopt_dump;
- while (s > sb.buf && strchr("*=?!", s[-1])) {
- switch (*--s) {
+
+ /* name(s) */
+ s = strpbrk(sb.buf, flag_chars);
+ if (s == NULL)
+ s = help;
+
+ if (s - sb.buf == 1) /* short option only */
+ o->short_name = *sb.buf;
+ else if (sb.buf[1] != ',') /* long option only */
+ o->long_name = xmemdupz(sb.buf, s - sb.buf);
+ else {
+ o->short_name = *sb.buf;
+ o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
+ }
+
+ /* flags */
+ while (s < help) {
+ switch (*s++) {
case '=':
o->flags &= ~PARSE_OPT_NOARG;
- break;
+ continue;
case '?':
o->flags &= ~PARSE_OPT_NOARG;
o->flags |= PARSE_OPT_OPTARG;
- break;
+ continue;
case '!':
o->flags |= PARSE_OPT_NONEG;
- break;
+ continue;
case '*':
o->flags |= PARSE_OPT_HIDDEN;
- break;
+ continue;
}
+ s--;
+ break;
}
- if (s - sb.buf == 1) /* short option only */
- o->short_name = *sb.buf;
- else if (sb.buf[1] != ',') /* long option only */
- o->long_name = xmemdupz(sb.buf, s - sb.buf);
- else {
- o->short_name = *sb.buf;
- o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
- }
+ if (s < help)
+ o->argh = xmemdupz(s, help - s);
}
strbuf_release(&sb);
@@ -443,17 +496,22 @@ static void die_no_single_rev(int quiet)
}
static const char builtin_rev_parse_usage[] =
-"git rev-parse --parseopt [options] -- [<args>...]\n"
-" or: git rev-parse --sq-quote [<arg>...]\n"
-" or: git rev-parse [options] [<arg>...]\n"
-"\n"
-"Run \"git rev-parse --parseopt -h\" for more information on the first usage.";
+N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
+ " or: git rev-parse --sq-quote [<arg>...]\n"
+ " or: git rev-parse [<options>] [<arg>...]\n"
+ "\n"
+ "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
{
int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+ int did_repo_setup = 0;
+ int has_dashdash = 0;
+ int output_prefix = 0;
unsigned char sha1[20];
+ unsigned int flags = 0;
const char *name = NULL;
+ struct object_context unused;
if (argc > 1 && !strcmp("--parseopt", argv[1]))
return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -461,32 +519,60 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
if (argc > 1 && !strcmp("--sq-quote", argv[1]))
return cmd_sq_quote(argc - 2, argv + 2);
- if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
- int i;
- for (i = 0; local_repo_env[i]; i++)
- printf("%s\n", local_repo_env[i]);
- return 0;
+ if (argc > 1 && !strcmp("-h", argv[1]))
+ usage(builtin_rev_parse_usage);
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ has_dashdash = 1;
+ break;
+ }
}
- if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
- const char *gitdir = resolve_gitdir(argv[2]);
- if (!gitdir)
- die("not a gitdir '%s'", argv[2]);
- puts(gitdir);
+ /* No options; just report on whether we're in a git repo or not. */
+ if (argc == 1) {
+ setup_git_directory();
+ git_config(git_default_config, NULL);
return 0;
}
- if (argc > 1 && !strcmp("-h", argv[1]))
- usage(builtin_rev_parse_usage);
-
- prefix = setup_git_directory();
- git_config(git_default_config, NULL);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
+ if (!strcmp(arg, "--local-env-vars")) {
+ int i;
+ for (i = 0; local_repo_env[i]; i++)
+ printf("%s\n", local_repo_env[i]);
+ continue;
+ }
+ if (!strcmp(arg, "--resolve-git-dir")) {
+ const char *gitdir = argv[++i];
+ if (!gitdir)
+ die("--resolve-git-dir requires an argument");
+ gitdir = resolve_gitdir(gitdir);
+ if (!gitdir)
+ die("not a gitdir '%s'", argv[i]);
+ puts(gitdir);
+ continue;
+ }
+
+ /* The rest of the options require a git repository. */
+ if (!did_repo_setup) {
+ prefix = setup_git_directory();
+ git_config(git_default_config, NULL);
+ did_repo_setup = 1;
+ }
+
+ if (!strcmp(arg, "--git-path")) {
+ if (!argv[i + 1])
+ die("--git-path requires an argument");
+ puts(git_path("%s", argv[i + 1]));
+ i++;
+ continue;
+ }
if (as_is) {
- if (show_file(arg) && as_is < 2)
- verify_filename(prefix, arg);
+ if (show_file(arg, output_prefix) && as_is < 2)
+ verify_filename(prefix, arg, 0);
continue;
}
if (!strcmp(arg,"-n")) {
@@ -498,7 +584,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
continue;
}
- if (!prefixcmp(arg, "-n")) {
+ if (starts_with(arg, "-n")) {
if ((filter & DO_FLAGS) && (filter & DO_REVS))
show(arg);
continue;
@@ -509,12 +595,21 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
as_is = 2;
/* Pass on the "--" if we show anything but files.. */
if (filter & (DO_FLAGS | DO_REVS))
- show_file(arg);
+ show_file(arg, 0);
continue;
}
if (!strcmp(arg, "--default")) {
- def = argv[i+1];
- i++;
+ def = argv[++i];
+ if (!def)
+ die("--default requires an argument");
+ continue;
+ }
+ if (!strcmp(arg, "--prefix")) {
+ prefix = argv[++i];
+ if (!prefix)
+ die("--prefix requires an argument");
+ startup_info->prefix = prefix;
+ output_prefix = 1;
continue;
}
if (!strcmp(arg, "--revs-only")) {
@@ -540,10 +635,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
+ flags |= GET_SHA1_QUIETLY;
continue;
}
if (!strcmp(arg, "--short") ||
- !prefixcmp(arg, "--short=")) {
+ starts_with(arg, "--short=")) {
filter &= ~(DO_FLAGS|DO_NOREV);
verify = 1;
abbrev = DEFAULT_ABBREV;
@@ -571,7 +667,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
symbolic = SHOW_SYMBOLIC_FULL;
continue;
}
- if (!prefixcmp(arg, "--abbrev-ref") &&
+ if (starts_with(arg, "--abbrev-ref") &&
(!arg[12] || arg[12] == '=')) {
abbrev_ref = 1;
abbrev_ref_strict = warn_ambiguous_refs;
@@ -589,40 +685,55 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
for_each_ref(show_reference, NULL);
continue;
}
+ if (starts_with(arg, "--disambiguate=")) {
+ for_each_abbrev(arg + 15, show_abbrev, NULL);
+ continue;
+ }
if (!strcmp(arg, "--bisect")) {
for_each_ref_in("refs/bisect/bad", show_reference, NULL);
for_each_ref_in("refs/bisect/good", anti_reference, NULL);
continue;
}
- if (!prefixcmp(arg, "--branches=")) {
+ if (starts_with(arg, "--branches=")) {
for_each_glob_ref_in(show_reference, arg + 11,
"refs/heads/", NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
if (!strcmp(arg, "--branches")) {
for_each_branch_ref(show_reference, NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
- if (!prefixcmp(arg, "--tags=")) {
+ if (starts_with(arg, "--tags=")) {
for_each_glob_ref_in(show_reference, arg + 7,
"refs/tags/", NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
if (!strcmp(arg, "--tags")) {
for_each_tag_ref(show_reference, NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
- if (!prefixcmp(arg, "--glob=")) {
+ if (starts_with(arg, "--glob=")) {
for_each_glob_ref(show_reference, arg + 7, NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
- if (!prefixcmp(arg, "--remotes=")) {
+ if (starts_with(arg, "--remotes=")) {
for_each_glob_ref_in(show_reference, arg + 10,
"refs/remotes/", NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
if (!strcmp(arg, "--remotes")) {
for_each_remote_ref(show_reference, NULL);
+ clear_ref_exclusion(&ref_excludes);
+ continue;
+ }
+ if (starts_with(arg, "--exclude=")) {
+ add_ref_exclusion(&ref_excludes, arg + 10);
continue;
}
if (!strcmp(arg, "--show-toplevel")) {
@@ -634,6 +745,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
if (!strcmp(arg, "--show-prefix")) {
if (prefix)
puts(prefix);
+ else
+ putchar('\n');
continue;
}
if (!strcmp(arg, "--show-cdup")) {
@@ -657,7 +770,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--git-dir")) {
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
- static char cwd[PATH_MAX];
+ char *cwd;
int len;
if (gitdir) {
puts(gitdir);
@@ -667,10 +780,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
puts(".git");
continue;
}
- if (!getcwd(cwd, PATH_MAX))
- die_errno("unable to get current working directory");
+ cwd = xgetcwd();
len = strlen(cwd);
printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
+ free(cwd);
+ continue;
+ }
+ if (!strcmp(arg, "--git-common-dir")) {
+ const char *pfx = prefix ? prefix : "";
+ puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir()));
continue;
}
if (!strcmp(arg, "--is-inside-git-dir")) {
@@ -688,19 +806,28 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
: "false");
continue;
}
- if (!prefixcmp(arg, "--since=")) {
+ if (!strcmp(arg, "--shared-index-path")) {
+ if (read_cache() < 0)
+ die(_("Could not read the index"));
+ if (the_index.split_index) {
+ const unsigned char *sha1 = the_index.split_index->base_sha1;
+ puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
+ }
+ continue;
+ }
+ if (starts_with(arg, "--since=")) {
show_datestring("--max-age=", arg+8);
continue;
}
- if (!prefixcmp(arg, "--after=")) {
+ if (starts_with(arg, "--after=")) {
show_datestring("--max-age=", arg+8);
continue;
}
- if (!prefixcmp(arg, "--before=")) {
+ if (starts_with(arg, "--before=")) {
show_datestring("--min-age=", arg+9);
continue;
}
- if (!prefixcmp(arg, "--until=")) {
+ if (starts_with(arg, "--until=")) {
show_datestring("--min-age=", arg+8);
continue;
}
@@ -720,7 +847,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
name++;
type = REVERSED;
}
- if (!get_sha1(name, sha1)) {
+ if (!get_sha1_with_context(name, flags, sha1, &unused)) {
if (verify)
revs_count++;
else
@@ -729,10 +856,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (verify)
die_no_single_rev(quiet);
+ if (has_dashdash)
+ die("bad revision '%s'", arg);
as_is = 1;
- if (!show_file(arg))
+ if (!show_file(arg, output_prefix))
continue;
- verify_filename(prefix, arg);
+ verify_filename(prefix, arg, 1);
}
if (verify) {
if (revs_count == 1) {