Merge branch 'jc/sha1-name-more'
Teaches the object name parser things like a "git describe" output is always a commit object, "A" in "git log A" must be a committish, and "A" and "B" in "git log A...B" both must be committish, etc., to prolong the lifetime of abbreviated object names. * jc/sha1-name-more: (27 commits) t1512: match the "other" object names t1512: ignore whitespaces in wc -l output rev-parse --disambiguate=<prefix> rev-parse: A and B in "rev-parse A..B" refer to committish reset: the command takes committish commit-tree: the command wants a tree and commits apply: --build-fake-ancestor expects blobs sha1_name.c: add support for disambiguating other types revision.c: the "log" family, except for "show", takes committish revision.c: allow handle_revision_arg() to take other flags sha1_name.c: introduce get_sha1_committish() sha1_name.c: teach lookup context to get_sha1_with_context() sha1_name.c: many short names can only be committish sha1_name.c: get_sha1_1() takes lookup flags sha1_name.c: get_describe_name() by definition groks only commits sha1_name.c: teach get_short_sha1() a commit-only option sha1_name.c: allow get_short_sha1() to take other flags get_sha1(): fix error status regression sha1_name.c: restructure disambiguation of short names sha1_name.c: correct misnamed "canonical" and "res" ...
This commit is contained in:
commit
0958a24d73
@ -101,6 +101,12 @@ OPTIONS
|
|||||||
The option core.warnAmbiguousRefs is used to select the strict
|
The option core.warnAmbiguousRefs is used to select the strict
|
||||||
abbreviation mode.
|
abbreviation mode.
|
||||||
|
|
||||||
|
--disambiguate=<prefix>::
|
||||||
|
Show every object whose name begins with the given prefix.
|
||||||
|
The <prefix> must be at least 4 hexadecimal digits long to
|
||||||
|
avoid listing each and every object in the repository by
|
||||||
|
mistake.
|
||||||
|
|
||||||
--all::
|
--all::
|
||||||
Show all refs found in `refs/`.
|
Show all refs found in `refs/`.
|
||||||
|
|
||||||
|
@ -3589,7 +3589,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
|
|||||||
name = patch->old_name ? patch->old_name : patch->new_name;
|
name = patch->old_name ? patch->old_name : patch->new_name;
|
||||||
if (0 < patch->is_new)
|
if (0 < patch->is_new)
|
||||||
continue;
|
continue;
|
||||||
else if (get_sha1(patch->old_sha1_prefix, sha1))
|
else if (get_sha1_blob(patch->old_sha1_prefix, sha1))
|
||||||
/* git diff has no index line for mode/type changes */
|
/* git diff has no index line for mode/type changes */
|
||||||
if (!patch->lines_added && !patch->lines_deleted) {
|
if (!patch->lines_added && !patch->lines_deleted) {
|
||||||
if (get_current_sha1(patch->old_name, sha1))
|
if (get_current_sha1(patch->old_name, sha1))
|
||||||
|
@ -91,7 +91,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
|
|||||||
unsigned long size;
|
unsigned long size;
|
||||||
struct object_context obj_context;
|
struct object_context obj_context;
|
||||||
|
|
||||||
if (get_sha1_with_context(obj_name, sha1, &obj_context))
|
if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
|
||||||
die("Not a valid object name %s", obj_name);
|
die("Not a valid object name %s", obj_name);
|
||||||
|
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
|
@ -48,8 +48,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
|||||||
if (argc < 2 || !strcmp(argv[1], "-h"))
|
if (argc < 2 || !strcmp(argv[1], "-h"))
|
||||||
usage(commit_tree_usage);
|
usage(commit_tree_usage);
|
||||||
|
|
||||||
if (get_sha1(argv[1], tree_sha1))
|
if (get_sha1_tree(argv[1], tree_sha1))
|
||||||
die("Not a valid object name %s", argv[1]);
|
die("Not a valid tree object name %s", argv[1]);
|
||||||
|
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
const char *arg = argv[i];
|
const char *arg = argv[i];
|
||||||
@ -57,7 +57,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
|||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
if (argc <= ++i)
|
if (argc <= ++i)
|
||||||
usage(commit_tree_usage);
|
usage(commit_tree_usage);
|
||||||
if (get_sha1(argv[i], sha1))
|
if (get_sha1_commit(argv[i], sha1))
|
||||||
die("Not a valid object name %s", argv[i]);
|
die("Not a valid object name %s", argv[i]);
|
||||||
assert_sha1_type(sha1, OBJ_COMMIT);
|
assert_sha1_type(sha1, OBJ_COMMIT);
|
||||||
new_parent(lookup_commit(sha1), &parents);
|
new_parent(lookup_commit(sha1), &parents);
|
||||||
@ -104,7 +104,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_sha1(arg, tree_sha1))
|
if (get_sha1_tree(arg, tree_sha1))
|
||||||
die("Not a valid object name %s", arg);
|
die("Not a valid object name %s", arg);
|
||||||
if (got_tree)
|
if (got_tree)
|
||||||
die("Cannot give more than one trees");
|
die("Cannot give more than one trees");
|
||||||
|
@ -367,6 +367,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
|
|||||||
rev.simplify_history = 0;
|
rev.simplify_history = 0;
|
||||||
memset(&opt, 0, sizeof(opt));
|
memset(&opt, 0, sizeof(opt));
|
||||||
opt.def = "HEAD";
|
opt.def = "HEAD";
|
||||||
|
opt.revarg_opt = REVARG_COMMITTISH;
|
||||||
cmd_log_init(argc, argv, prefix, &rev, &opt);
|
cmd_log_init(argc, argv, prefix, &rev, &opt);
|
||||||
if (!rev.diffopt.output_format)
|
if (!rev.diffopt.output_format)
|
||||||
rev.diffopt.output_format = DIFF_FORMAT_RAW;
|
rev.diffopt.output_format = DIFF_FORMAT_RAW;
|
||||||
@ -557,6 +558,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
|
|||||||
rev.always_show_header = 1;
|
rev.always_show_header = 1;
|
||||||
memset(&opt, 0, sizeof(opt));
|
memset(&opt, 0, sizeof(opt));
|
||||||
opt.def = "HEAD";
|
opt.def = "HEAD";
|
||||||
|
opt.revarg_opt = REVARG_COMMITTISH;
|
||||||
cmd_log_init(argc, argv, prefix, &rev, &opt);
|
cmd_log_init(argc, argv, prefix, &rev, &opt);
|
||||||
return cmd_log_walk(&rev);
|
return cmd_log_walk(&rev);
|
||||||
}
|
}
|
||||||
@ -1132,6 +1134,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
|||||||
rev.subject_prefix = fmt_patch_subject_prefix;
|
rev.subject_prefix = fmt_patch_subject_prefix;
|
||||||
memset(&s_r_opt, 0, sizeof(s_r_opt));
|
memset(&s_r_opt, 0, sizeof(s_r_opt));
|
||||||
s_r_opt.def = "HEAD";
|
s_r_opt.def = "HEAD";
|
||||||
|
s_r_opt.revarg_opt = REVARG_COMMITTISH;
|
||||||
|
|
||||||
if (default_attach) {
|
if (default_attach) {
|
||||||
rev.mime_boundary = default_attach;
|
rev.mime_boundary = default_attach;
|
||||||
|
@ -2373,7 +2373,7 @@ static void get_object_list(int ac, const char **av)
|
|||||||
}
|
}
|
||||||
die("not a rev '%s'", line);
|
die("not a rev '%s'", line);
|
||||||
}
|
}
|
||||||
if (handle_revision_arg(line, &revs, flags, 1))
|
if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME))
|
||||||
die("bad revision '%s'", line);
|
die("bad revision '%s'", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
|||||||
* Otherwise, argv[i] could be either <rev> or <paths> and
|
* Otherwise, argv[i] could be either <rev> or <paths> and
|
||||||
* has to be unambiguous.
|
* has to be unambiguous.
|
||||||
*/
|
*/
|
||||||
else if (!get_sha1(argv[i], sha1)) {
|
else if (!get_sha1_committish(argv[i], sha1)) {
|
||||||
/*
|
/*
|
||||||
* Ok, argv[i] looks like a rev; it should not
|
* Ok, argv[i] looks like a rev; it should not
|
||||||
* be a filename.
|
* be a filename.
|
||||||
@ -289,9 +289,15 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_sha1(rev, sha1))
|
if (get_sha1_committish(rev, sha1))
|
||||||
die(_("Failed to resolve '%s' as a valid ref."), rev);
|
die(_("Failed to resolve '%s' as a valid ref."), rev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: As "git reset $treeish -- $path" should be usable on
|
||||||
|
* any tree-ish, this is not strictly correct. We are not
|
||||||
|
* moving the HEAD to any commit; we are merely resetting the
|
||||||
|
* entries in the index to that of a treeish.
|
||||||
|
*/
|
||||||
commit = lookup_commit_reference(sha1);
|
commit = lookup_commit_reference(sha1);
|
||||||
if (!commit)
|
if (!commit)
|
||||||
die(_("Could not parse object '%s'."), rev);
|
die(_("Could not parse object '%s'."), rev);
|
||||||
|
@ -195,6 +195,12 @@ static int anti_reference(const char *refname, const unsigned char *sha1, int fl
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int show_abbrev(const unsigned char *sha1, void *cb_data)
|
||||||
|
{
|
||||||
|
show_rev(NORMAL, sha1, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void show_datestring(const char *flag, const char *datestr)
|
static void show_datestring(const char *flag, const char *datestr)
|
||||||
{
|
{
|
||||||
static char buffer[100];
|
static char buffer[100];
|
||||||
@ -238,7 +244,7 @@ static int try_difference(const char *arg)
|
|||||||
next = "HEAD";
|
next = "HEAD";
|
||||||
if (dotdot == arg)
|
if (dotdot == arg)
|
||||||
this = "HEAD";
|
this = "HEAD";
|
||||||
if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
|
if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
|
||||||
show_rev(NORMAL, end, next);
|
show_rev(NORMAL, end, next);
|
||||||
show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
|
show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
|
||||||
if (symmetric) {
|
if (symmetric) {
|
||||||
@ -278,7 +284,7 @@ static int try_parent_shorthands(const char *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
*dotdot = 0;
|
*dotdot = 0;
|
||||||
if (get_sha1(arg, sha1))
|
if (get_sha1_committish(arg, sha1))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!parents_only)
|
if (!parents_only)
|
||||||
@ -589,6 +595,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
|||||||
for_each_ref(show_reference, NULL);
|
for_each_ref(show_reference, NULL);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!prefixcmp(arg, "--disambiguate=")) {
|
||||||
|
for_each_abbrev(arg + 15, show_abbrev, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp(arg, "--bisect")) {
|
if (!strcmp(arg, "--bisect")) {
|
||||||
for_each_ref_in("refs/bisect/bad", show_reference, NULL);
|
for_each_ref_in("refs/bisect/bad", show_reference, NULL);
|
||||||
for_each_ref_in("refs/bisect/good", anti_reference, NULL);
|
for_each_ref_in("refs/bisect/good", anti_reference, NULL);
|
||||||
|
28
cache.h
28
cache.h
@ -790,17 +790,25 @@ struct object_context {
|
|||||||
unsigned mode;
|
unsigned mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GET_SHA1_QUIETLY 01
|
||||||
|
#define GET_SHA1_COMMIT 02
|
||||||
|
#define GET_SHA1_COMMITTISH 04
|
||||||
|
#define GET_SHA1_TREE 010
|
||||||
|
#define GET_SHA1_TREEISH 020
|
||||||
|
#define GET_SHA1_BLOB 040
|
||||||
|
#define GET_SHA1_ONLY_TO_DIE 04000
|
||||||
|
|
||||||
extern int get_sha1(const char *str, unsigned char *sha1);
|
extern int get_sha1(const char *str, unsigned char *sha1);
|
||||||
extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
|
extern int get_sha1_commit(const char *str, unsigned char *sha1);
|
||||||
static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
|
extern int get_sha1_committish(const char *str, unsigned char *sha1);
|
||||||
{
|
extern int get_sha1_tree(const char *str, unsigned char *sha1);
|
||||||
return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
|
extern int get_sha1_treeish(const char *str, unsigned char *sha1);
|
||||||
}
|
extern int get_sha1_blob(const char *str, unsigned char *sha1);
|
||||||
extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
|
extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
|
||||||
static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
|
extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
|
||||||
{
|
|
||||||
return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
|
typedef int each_abbrev_fn(const unsigned char *sha1, void *);
|
||||||
}
|
extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to read a SHA1 in hexadecimal format from the 40 characters
|
* Try to read a SHA1 in hexadecimal format from the 40 characters
|
||||||
|
2
commit.c
2
commit.c
@ -68,7 +68,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
|
|||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
|
|
||||||
if (get_sha1(name, sha1))
|
if (get_sha1_committish(name, sha1))
|
||||||
return NULL;
|
return NULL;
|
||||||
commit = lookup_commit_reference(sha1);
|
commit = lookup_commit_reference(sha1);
|
||||||
if (!commit || parse_commit(commit))
|
if (!commit || parse_commit(commit))
|
||||||
|
38
revision.c
38
revision.c
@ -1000,7 +1000,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
|
|||||||
flags ^= UNINTERESTING;
|
flags ^= UNINTERESTING;
|
||||||
arg++;
|
arg++;
|
||||||
}
|
}
|
||||||
if (get_sha1(arg, sha1))
|
if (get_sha1_committish(arg, sha1))
|
||||||
return 0;
|
return 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
it = get_reference(revs, arg, sha1, 0);
|
it = get_reference(revs, arg, sha1, 0);
|
||||||
@ -1114,16 +1114,16 @@ static void prepare_show_merge(struct rev_info *revs)
|
|||||||
revs->limited = 1;
|
revs->limited = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int handle_revision_arg(const char *arg_, struct rev_info *revs,
|
int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
|
||||||
int flags,
|
|
||||||
int cant_be_filename)
|
|
||||||
{
|
{
|
||||||
unsigned mode;
|
struct object_context oc;
|
||||||
char *dotdot;
|
char *dotdot;
|
||||||
struct object *object;
|
struct object *object;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
int local_flags;
|
int local_flags;
|
||||||
const char *arg = arg_;
|
const char *arg = arg_;
|
||||||
|
int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
|
||||||
|
unsigned get_sha1_flags = 0;
|
||||||
|
|
||||||
dotdot = strstr(arg, "..");
|
dotdot = strstr(arg, "..");
|
||||||
if (dotdot) {
|
if (dotdot) {
|
||||||
@ -1141,8 +1141,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs,
|
|||||||
next = "HEAD";
|
next = "HEAD";
|
||||||
if (dotdot == arg)
|
if (dotdot == arg)
|
||||||
this = "HEAD";
|
this = "HEAD";
|
||||||
if (!get_sha1(this, from_sha1) &&
|
if (!get_sha1_committish(this, from_sha1) &&
|
||||||
!get_sha1(next, sha1)) {
|
!get_sha1_committish(next, sha1)) {
|
||||||
struct commit *a, *b;
|
struct commit *a, *b;
|
||||||
struct commit_list *exclude;
|
struct commit_list *exclude;
|
||||||
|
|
||||||
@ -1201,13 +1201,17 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs,
|
|||||||
local_flags = UNINTERESTING;
|
local_flags = UNINTERESTING;
|
||||||
arg++;
|
arg++;
|
||||||
}
|
}
|
||||||
if (get_sha1_with_mode(arg, sha1, &mode))
|
|
||||||
|
if (revarg_opt & REVARG_COMMITTISH)
|
||||||
|
get_sha1_flags = GET_SHA1_COMMITTISH;
|
||||||
|
|
||||||
|
if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
|
||||||
return revs->ignore_missing ? 0 : -1;
|
return revs->ignore_missing ? 0 : -1;
|
||||||
if (!cant_be_filename)
|
if (!cant_be_filename)
|
||||||
verify_non_filename(revs->prefix, arg);
|
verify_non_filename(revs->prefix, arg);
|
||||||
object = get_reference(revs, arg, sha1, flags ^ local_flags);
|
object = get_reference(revs, arg, sha1, flags ^ local_flags);
|
||||||
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
|
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
|
||||||
add_pending_object_with_mode(revs, object, arg, mode);
|
add_pending_object_with_mode(revs, object, arg, oc.mode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1257,7 +1261,7 @@ static void read_revisions_from_stdin(struct rev_info *revs,
|
|||||||
}
|
}
|
||||||
die("options not supported in --stdin mode");
|
die("options not supported in --stdin mode");
|
||||||
}
|
}
|
||||||
if (handle_revision_arg(sb.buf, revs, 0, 1))
|
if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
|
||||||
die("bad revision '%s'", sb.buf);
|
die("bad revision '%s'", sb.buf);
|
||||||
}
|
}
|
||||||
if (seen_dashdash)
|
if (seen_dashdash)
|
||||||
@ -1708,7 +1712,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
|
|||||||
*/
|
*/
|
||||||
int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
|
int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
|
||||||
{
|
{
|
||||||
int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
|
int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
|
||||||
struct cmdline_pathspec prune_data;
|
struct cmdline_pathspec prune_data;
|
||||||
const char *submodule = NULL;
|
const char *submodule = NULL;
|
||||||
|
|
||||||
@ -1736,6 +1740,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
|||||||
|
|
||||||
/* Second, deal with arguments and options */
|
/* Second, deal with arguments and options */
|
||||||
flags = 0;
|
flags = 0;
|
||||||
|
revarg_opt = opt ? opt->revarg_opt : 0;
|
||||||
|
if (seen_dashdash)
|
||||||
|
revarg_opt |= REVARG_CANNOT_BE_FILENAME;
|
||||||
read_from_stdin = 0;
|
read_from_stdin = 0;
|
||||||
for (left = i = 1; i < argc; i++) {
|
for (left = i = 1; i < argc; i++) {
|
||||||
const char *arg = argv[i];
|
const char *arg = argv[i];
|
||||||
@ -1771,7 +1778,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
|
|
||||||
|
if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
|
||||||
int j;
|
int j;
|
||||||
if (seen_dashdash || *arg == '^')
|
if (seen_dashdash || *arg == '^')
|
||||||
die("bad revision '%s'", arg);
|
die("bad revision '%s'", arg);
|
||||||
@ -1822,11 +1830,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
|||||||
if (revs->def && !revs->pending.nr && !got_rev_arg) {
|
if (revs->def && !revs->pending.nr && !got_rev_arg) {
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
struct object *object;
|
struct object *object;
|
||||||
unsigned mode;
|
struct object_context oc;
|
||||||
if (get_sha1_with_mode(revs->def, sha1, &mode))
|
if (get_sha1_with_context(revs->def, 0, sha1, &oc))
|
||||||
die("bad default revision '%s'", revs->def);
|
die("bad default revision '%s'", revs->def);
|
||||||
object = get_reference(revs, revs->def, sha1, 0);
|
object = get_reference(revs, revs->def, sha1, 0);
|
||||||
add_pending_object_with_mode(revs, object, revs->def, mode);
|
add_pending_object_with_mode(revs, object, revs->def, oc.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Did the user ask for any diff output? Run the diff! */
|
/* Did the user ask for any diff output? Run the diff! */
|
||||||
|
@ -184,6 +184,7 @@ struct setup_revision_opt {
|
|||||||
void (*tweak)(struct rev_info *, struct setup_revision_opt *);
|
void (*tweak)(struct rev_info *, struct setup_revision_opt *);
|
||||||
const char *submodule;
|
const char *submodule;
|
||||||
int assume_dashdash;
|
int assume_dashdash;
|
||||||
|
unsigned revarg_opt;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void init_revisions(struct rev_info *revs, const char *prefix);
|
extern void init_revisions(struct rev_info *revs, const char *prefix);
|
||||||
@ -191,7 +192,9 @@ extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, s
|
|||||||
extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
||||||
const struct option *options,
|
const struct option *options,
|
||||||
const char * const usagestr[]);
|
const char * const usagestr[]);
|
||||||
extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
|
#define REVARG_CANNOT_BE_FILENAME 01
|
||||||
|
#define REVARG_COMMITTISH 02
|
||||||
|
extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
|
||||||
|
|
||||||
extern void reset_revision_walk(void);
|
extern void reset_revision_walk(void);
|
||||||
extern int prepare_revision_walk(struct rev_info *revs);
|
extern int prepare_revision_walk(struct rev_info *revs);
|
||||||
|
8
setup.c
8
setup.c
@ -77,9 +77,6 @@ static void NORETURN die_verify_filename(const char *prefix,
|
|||||||
const char *arg,
|
const char *arg,
|
||||||
int diagnose_misspelt_rev)
|
int diagnose_misspelt_rev)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
|
||||||
unsigned mode;
|
|
||||||
|
|
||||||
if (!diagnose_misspelt_rev)
|
if (!diagnose_misspelt_rev)
|
||||||
die("%s: no such path in the working tree.\n"
|
die("%s: no such path in the working tree.\n"
|
||||||
"Use '-- <path>...' to specify paths that do not exist locally.",
|
"Use '-- <path>...' to specify paths that do not exist locally.",
|
||||||
@ -88,11 +85,10 @@ static void NORETURN die_verify_filename(const char *prefix,
|
|||||||
* Saying "'(icase)foo' does not exist in the index" when the
|
* Saying "'(icase)foo' does not exist in the index" when the
|
||||||
* user gave us ":(icase)foo" is just stupid. A magic pathspec
|
* user gave us ":(icase)foo" is just stupid. A magic pathspec
|
||||||
* begins with a colon and is followed by a non-alnum; do not
|
* begins with a colon and is followed by a non-alnum; do not
|
||||||
* let get_sha1_with_mode_1(only_to_die=1) to even trigger.
|
* let maybe_die_on_misspelt_object_name() even trigger.
|
||||||
*/
|
*/
|
||||||
if (!(arg[0] == ':' && !isalnum(arg[1])))
|
if (!(arg[0] == ':' && !isalnum(arg[1])))
|
||||||
/* try a detailed diagnostic ... */
|
maybe_die_on_misspelt_object_name(arg, prefix);
|
||||||
get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
|
|
||||||
|
|
||||||
/* ... or fall back the most general message. */
|
/* ... or fall back the most general message. */
|
||||||
die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
||||||
|
496
sha1_name.c
496
sha1_name.c
@ -9,14 +9,82 @@
|
|||||||
|
|
||||||
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
|
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
|
||||||
|
|
||||||
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
|
typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
|
||||||
|
|
||||||
|
struct disambiguate_state {
|
||||||
|
disambiguate_hint_fn fn;
|
||||||
|
void *cb_data;
|
||||||
|
unsigned char candidate[20];
|
||||||
|
unsigned candidate_exists:1;
|
||||||
|
unsigned candidate_checked:1;
|
||||||
|
unsigned candidate_ok:1;
|
||||||
|
unsigned disambiguate_fn_used:1;
|
||||||
|
unsigned ambiguous:1;
|
||||||
|
unsigned always_call_fn:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
|
||||||
|
{
|
||||||
|
if (ds->always_call_fn) {
|
||||||
|
ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ds->candidate_exists) {
|
||||||
|
/* this is the first candidate */
|
||||||
|
hashcpy(ds->candidate, current);
|
||||||
|
ds->candidate_exists = 1;
|
||||||
|
return;
|
||||||
|
} else if (!hashcmp(ds->candidate, current)) {
|
||||||
|
/* the same as what we already have seen */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ds->fn) {
|
||||||
|
/* cannot disambiguate between ds->candidate and current */
|
||||||
|
ds->ambiguous = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ds->candidate_checked) {
|
||||||
|
ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
|
||||||
|
ds->disambiguate_fn_used = 1;
|
||||||
|
ds->candidate_checked = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ds->candidate_ok) {
|
||||||
|
/* discard the candidate; we know it does not satisify fn */
|
||||||
|
hashcpy(ds->candidate, current);
|
||||||
|
ds->candidate_checked = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we reach this point, we know ds->candidate satisfies fn */
|
||||||
|
if (ds->fn(current, ds->cb_data)) {
|
||||||
|
/*
|
||||||
|
* if both current and candidate satisfy fn, we cannot
|
||||||
|
* disambiguate.
|
||||||
|
*/
|
||||||
|
ds->candidate_ok = 0;
|
||||||
|
ds->ambiguous = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, current can be discarded and candidate is still good */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
|
||||||
{
|
{
|
||||||
struct alternate_object_database *alt;
|
struct alternate_object_database *alt;
|
||||||
char hex[40];
|
char hex[40];
|
||||||
int found = 0;
|
|
||||||
static struct alternate_object_database *fakeent;
|
static struct alternate_object_database *fakeent;
|
||||||
|
|
||||||
if (!fakeent) {
|
if (!fakeent) {
|
||||||
|
/*
|
||||||
|
* Create a "fake" alternate object database that
|
||||||
|
* points to our own object database, to make it
|
||||||
|
* easier to get a temporary working space in
|
||||||
|
* alt->name/alt->base while iterating over the
|
||||||
|
* object databases including our own.
|
||||||
|
*/
|
||||||
const char *objdir = get_object_directory();
|
const char *objdir = get_object_directory();
|
||||||
int objdir_len = strlen(objdir);
|
int objdir_len = strlen(objdir);
|
||||||
int entlen = objdir_len + 43;
|
int entlen = objdir_len + 43;
|
||||||
@ -27,33 +95,28 @@ static int find_short_object_filename(int len, const char *name, unsigned char *
|
|||||||
}
|
}
|
||||||
fakeent->next = alt_odb_list;
|
fakeent->next = alt_odb_list;
|
||||||
|
|
||||||
sprintf(hex, "%.2s", name);
|
sprintf(hex, "%.2s", hex_pfx);
|
||||||
for (alt = fakeent; alt && found < 2; alt = alt->next) {
|
for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
sprintf(alt->name, "%.2s/", name);
|
sprintf(alt->name, "%.2s/", hex_pfx);
|
||||||
dir = opendir(alt->base);
|
dir = opendir(alt->base);
|
||||||
if (!dir)
|
if (!dir)
|
||||||
continue;
|
continue;
|
||||||
while ((de = readdir(dir)) != NULL) {
|
|
||||||
|
while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
|
||||||
|
unsigned char sha1[20];
|
||||||
|
|
||||||
if (strlen(de->d_name) != 38)
|
if (strlen(de->d_name) != 38)
|
||||||
continue;
|
continue;
|
||||||
if (memcmp(de->d_name, name + 2, len - 2))
|
if (memcmp(de->d_name, hex_pfx + 2, len - 2))
|
||||||
continue;
|
continue;
|
||||||
if (!found) {
|
memcpy(hex + 2, de->d_name, 38);
|
||||||
memcpy(hex + 2, de->d_name, 38);
|
if (!get_sha1_hex(hex, sha1))
|
||||||
found++;
|
update_candidates(ds, sha1);
|
||||||
}
|
|
||||||
else if (memcmp(hex + 2, de->d_name, 38)) {
|
|
||||||
found = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
}
|
}
|
||||||
if (found == 1)
|
|
||||||
return get_sha1_hex(hex, sha1) == 0;
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
|
static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
|
||||||
@ -71,103 +134,157 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char *
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
|
static void unique_in_pack(int len,
|
||||||
|
const unsigned char *bin_pfx,
|
||||||
|
struct packed_git *p,
|
||||||
|
struct disambiguate_state *ds)
|
||||||
|
{
|
||||||
|
uint32_t num, last, i, first = 0;
|
||||||
|
const unsigned char *current = NULL;
|
||||||
|
|
||||||
|
open_pack_index(p);
|
||||||
|
num = p->num_objects;
|
||||||
|
last = num;
|
||||||
|
while (first < last) {
|
||||||
|
uint32_t mid = (first + last) / 2;
|
||||||
|
const unsigned char *current;
|
||||||
|
int cmp;
|
||||||
|
|
||||||
|
current = nth_packed_object_sha1(p, mid);
|
||||||
|
cmp = hashcmp(bin_pfx, current);
|
||||||
|
if (!cmp) {
|
||||||
|
first = mid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cmp > 0) {
|
||||||
|
first = mid+1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, "first" is the location of the lowest object
|
||||||
|
* with an object name that could match "bin_pfx". See if we have
|
||||||
|
* 0, 1 or more objects that actually match(es).
|
||||||
|
*/
|
||||||
|
for (i = first; i < num && !ds->ambiguous; i++) {
|
||||||
|
current = nth_packed_object_sha1(p, i);
|
||||||
|
if (!match_sha(len, bin_pfx, current))
|
||||||
|
break;
|
||||||
|
update_candidates(ds, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void find_short_packed_object(int len, const unsigned char *bin_pfx,
|
||||||
|
struct disambiguate_state *ds)
|
||||||
{
|
{
|
||||||
struct packed_git *p;
|
struct packed_git *p;
|
||||||
const unsigned char *found_sha1 = NULL;
|
|
||||||
int found = 0;
|
|
||||||
|
|
||||||
prepare_packed_git();
|
prepare_packed_git();
|
||||||
for (p = packed_git; p && found < 2; p = p->next) {
|
for (p = packed_git; p && !ds->ambiguous; p = p->next)
|
||||||
uint32_t num, last;
|
unique_in_pack(len, bin_pfx, p, ds);
|
||||||
uint32_t first = 0;
|
|
||||||
open_pack_index(p);
|
|
||||||
num = p->num_objects;
|
|
||||||
last = num;
|
|
||||||
while (first < last) {
|
|
||||||
uint32_t mid = (first + last) / 2;
|
|
||||||
const unsigned char *now;
|
|
||||||
int cmp;
|
|
||||||
|
|
||||||
now = nth_packed_object_sha1(p, mid);
|
|
||||||
cmp = hashcmp(match, now);
|
|
||||||
if (!cmp) {
|
|
||||||
first = mid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cmp > 0) {
|
|
||||||
first = mid+1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
last = mid;
|
|
||||||
}
|
|
||||||
if (first < num) {
|
|
||||||
const unsigned char *now, *next;
|
|
||||||
now = nth_packed_object_sha1(p, first);
|
|
||||||
if (match_sha(len, match, now)) {
|
|
||||||
next = nth_packed_object_sha1(p, first+1);
|
|
||||||
if (!next|| !match_sha(len, match, next)) {
|
|
||||||
/* unique within this pack */
|
|
||||||
if (!found) {
|
|
||||||
found_sha1 = now;
|
|
||||||
found++;
|
|
||||||
}
|
|
||||||
else if (hashcmp(found_sha1, now)) {
|
|
||||||
found = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* not even unique within this pack */
|
|
||||||
found = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found == 1)
|
|
||||||
hashcpy(sha1, found_sha1);
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SHORT_NAME_NOT_FOUND (-1)
|
#define SHORT_NAME_NOT_FOUND (-1)
|
||||||
#define SHORT_NAME_AMBIGUOUS (-2)
|
#define SHORT_NAME_AMBIGUOUS (-2)
|
||||||
|
|
||||||
static int find_unique_short_object(int len, char *canonical,
|
static int finish_object_disambiguation(struct disambiguate_state *ds,
|
||||||
unsigned char *res, unsigned char *sha1)
|
unsigned char *sha1)
|
||||||
{
|
{
|
||||||
int has_unpacked, has_packed;
|
if (ds->ambiguous)
|
||||||
unsigned char unpacked_sha1[20], packed_sha1[20];
|
return SHORT_NAME_AMBIGUOUS;
|
||||||
|
|
||||||
prepare_alt_odb();
|
if (!ds->candidate_exists)
|
||||||
has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
|
|
||||||
has_packed = find_short_packed_object(len, res, packed_sha1);
|
|
||||||
if (!has_unpacked && !has_packed)
|
|
||||||
return SHORT_NAME_NOT_FOUND;
|
return SHORT_NAME_NOT_FOUND;
|
||||||
if (1 < has_unpacked || 1 < has_packed)
|
|
||||||
|
if (!ds->candidate_checked)
|
||||||
|
/*
|
||||||
|
* If this is the only candidate, there is no point
|
||||||
|
* calling the disambiguation hint callback.
|
||||||
|
*
|
||||||
|
* On the other hand, if the current candidate
|
||||||
|
* replaced an earlier candidate that did _not_ pass
|
||||||
|
* the disambiguation hint callback, then we do have
|
||||||
|
* more than one objects that match the short name
|
||||||
|
* given, so we should make sure this one matches;
|
||||||
|
* otherwise, if we discovered this one and the one
|
||||||
|
* that we previously discarded in the reverse order,
|
||||||
|
* we would end up showing different results in the
|
||||||
|
* same repository!
|
||||||
|
*/
|
||||||
|
ds->candidate_ok = (!ds->disambiguate_fn_used ||
|
||||||
|
ds->fn(ds->candidate, ds->cb_data));
|
||||||
|
|
||||||
|
if (!ds->candidate_ok)
|
||||||
return SHORT_NAME_AMBIGUOUS;
|
return SHORT_NAME_AMBIGUOUS;
|
||||||
if (has_unpacked != has_packed) {
|
|
||||||
hashcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1));
|
hashcpy(sha1, ds->candidate);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* Both have unique ones -- do they match? */
|
|
||||||
if (hashcmp(packed_sha1, unpacked_sha1))
|
|
||||||
return SHORT_NAME_AMBIGUOUS;
|
|
||||||
hashcpy(sha1, packed_sha1);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_short_sha1(const char *name, int len, unsigned char *sha1,
|
static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
|
||||||
int quietly)
|
|
||||||
{
|
{
|
||||||
int i, status;
|
int kind = sha1_object_info(sha1, NULL);
|
||||||
char canonical[40];
|
return kind == OBJ_COMMIT;
|
||||||
unsigned char res[20];
|
}
|
||||||
|
|
||||||
if (len < MINIMUM_ABBREV || len > 40)
|
static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
|
||||||
return -1;
|
{
|
||||||
hashclr(res);
|
struct object *obj;
|
||||||
memset(canonical, 'x', 40);
|
int kind;
|
||||||
|
|
||||||
|
kind = sha1_object_info(sha1, NULL);
|
||||||
|
if (kind == OBJ_COMMIT)
|
||||||
|
return 1;
|
||||||
|
if (kind != OBJ_TAG)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* We need to do this the hard way... */
|
||||||
|
obj = deref_tag(lookup_object(sha1), NULL, 0);
|
||||||
|
if (obj && obj->type == OBJ_COMMIT)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
|
||||||
|
{
|
||||||
|
int kind = sha1_object_info(sha1, NULL);
|
||||||
|
return kind == OBJ_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
|
||||||
|
{
|
||||||
|
struct object *obj;
|
||||||
|
int kind;
|
||||||
|
|
||||||
|
kind = sha1_object_info(sha1, NULL);
|
||||||
|
if (kind == OBJ_TREE || kind == OBJ_COMMIT)
|
||||||
|
return 1;
|
||||||
|
if (kind != OBJ_TAG)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* We need to do this the hard way... */
|
||||||
|
obj = deref_tag(lookup_object(sha1), NULL, 0);
|
||||||
|
if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
|
||||||
|
{
|
||||||
|
int kind = sha1_object_info(sha1, NULL);
|
||||||
|
return kind == OBJ_BLOB;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prepare_prefixes(const char *name, int len,
|
||||||
|
unsigned char *bin_pfx,
|
||||||
|
char *hex_pfx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
hashclr(bin_pfx);
|
||||||
|
memset(hex_pfx, 'x', 40);
|
||||||
for (i = 0; i < len ;i++) {
|
for (i = 0; i < len ;i++) {
|
||||||
unsigned char c = name[i];
|
unsigned char c = name[i];
|
||||||
unsigned char val;
|
unsigned char val;
|
||||||
@ -181,18 +298,76 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
canonical[i] = c;
|
hex_pfx[i] = c;
|
||||||
if (!(i & 1))
|
if (!(i & 1))
|
||||||
val <<= 4;
|
val <<= 4;
|
||||||
res[i >> 1] |= val;
|
bin_pfx[i >> 1] |= val;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_short_sha1(const char *name, int len, unsigned char *sha1,
|
||||||
|
unsigned flags)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
char hex_pfx[40];
|
||||||
|
unsigned char bin_pfx[20];
|
||||||
|
struct disambiguate_state ds;
|
||||||
|
int quietly = !!(flags & GET_SHA1_QUIETLY);
|
||||||
|
|
||||||
|
if (len < MINIMUM_ABBREV || len > 40)
|
||||||
|
return -1;
|
||||||
|
if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
prepare_alt_odb();
|
||||||
|
|
||||||
|
memset(&ds, 0, sizeof(ds));
|
||||||
|
if (flags & GET_SHA1_COMMIT)
|
||||||
|
ds.fn = disambiguate_commit_only;
|
||||||
|
else if (flags & GET_SHA1_COMMITTISH)
|
||||||
|
ds.fn = disambiguate_committish_only;
|
||||||
|
else if (flags & GET_SHA1_TREE)
|
||||||
|
ds.fn = disambiguate_tree_only;
|
||||||
|
else if (flags & GET_SHA1_TREEISH)
|
||||||
|
ds.fn = disambiguate_treeish_only;
|
||||||
|
else if (flags & GET_SHA1_BLOB)
|
||||||
|
ds.fn = disambiguate_blob_only;
|
||||||
|
|
||||||
|
find_short_object_filename(len, hex_pfx, &ds);
|
||||||
|
find_short_packed_object(len, bin_pfx, &ds);
|
||||||
|
status = finish_object_disambiguation(&ds, sha1);
|
||||||
|
|
||||||
status = find_unique_short_object(i, canonical, res, sha1);
|
|
||||||
if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
|
if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
|
||||||
return error("short SHA1 %.*s is ambiguous.", len, canonical);
|
return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
|
||||||
|
{
|
||||||
|
char hex_pfx[40];
|
||||||
|
unsigned char bin_pfx[20];
|
||||||
|
struct disambiguate_state ds;
|
||||||
|
int len = strlen(prefix);
|
||||||
|
|
||||||
|
if (len < MINIMUM_ABBREV || len > 40)
|
||||||
|
return -1;
|
||||||
|
if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
prepare_alt_odb();
|
||||||
|
|
||||||
|
memset(&ds, 0, sizeof(ds));
|
||||||
|
ds.always_call_fn = 1;
|
||||||
|
ds.cb_data = cb_data;
|
||||||
|
ds.fn = fn;
|
||||||
|
|
||||||
|
find_short_object_filename(len, hex_pfx, &ds);
|
||||||
|
find_short_packed_object(len, bin_pfx, &ds);
|
||||||
|
return ds.ambiguous;
|
||||||
|
}
|
||||||
|
|
||||||
const char *find_unique_abbrev(const unsigned char *sha1, int len)
|
const char *find_unique_abbrev(const unsigned char *sha1, int len)
|
||||||
{
|
{
|
||||||
int status, exists;
|
int status, exists;
|
||||||
@ -204,7 +379,7 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
|
|||||||
return hex;
|
return hex;
|
||||||
while (len < 40) {
|
while (len < 40) {
|
||||||
unsigned char sha1_ret[20];
|
unsigned char sha1_ret[20];
|
||||||
status = get_short_sha1(hex, len, sha1_ret, 1);
|
status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
|
||||||
if (exists
|
if (exists
|
||||||
? !status
|
? !status
|
||||||
: status == SHORT_NAME_NOT_FOUND) {
|
: status == SHORT_NAME_NOT_FOUND) {
|
||||||
@ -255,7 +430,7 @@ static inline int upstream_mark(const char *string, int len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_sha1_1(const char *name, int len, unsigned char *sha1);
|
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
|
||||||
|
|
||||||
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
@ -292,7 +467,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
|||||||
ret = interpret_branch_name(str+at, &buf);
|
ret = interpret_branch_name(str+at, &buf);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
/* substitute this branch name and restart */
|
/* substitute this branch name and restart */
|
||||||
return get_sha1_1(buf.buf, buf.len, sha1);
|
return get_sha1_1(buf.buf, buf.len, sha1, 0);
|
||||||
} else if (ret == 0) {
|
} else if (ret == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -362,7 +537,7 @@ static int get_parent(const char *name, int len,
|
|||||||
unsigned char *result, int idx)
|
unsigned char *result, int idx)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
int ret = get_sha1_1(name, len, sha1);
|
int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
struct commit_list *p;
|
struct commit_list *p;
|
||||||
|
|
||||||
@ -395,7 +570,7 @@ static int get_nth_ancestor(const char *name, int len,
|
|||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = get_sha1_1(name, len, sha1);
|
ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
commit = lookup_commit_reference(sha1);
|
commit = lookup_commit_reference(sha1);
|
||||||
@ -441,6 +616,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
|
|||||||
unsigned char outer[20];
|
unsigned char outer[20];
|
||||||
const char *sp;
|
const char *sp;
|
||||||
unsigned int expected_type = 0;
|
unsigned int expected_type = 0;
|
||||||
|
unsigned lookup_flags = 0;
|
||||||
struct object *o;
|
struct object *o;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -476,7 +652,10 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
|
|||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (get_sha1_1(name, sp - name - 2, outer))
|
if (expected_type == OBJ_COMMIT)
|
||||||
|
lookup_flags = GET_SHA1_COMMITTISH;
|
||||||
|
|
||||||
|
if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
o = parse_object(outer);
|
o = parse_object(outer);
|
||||||
@ -525,6 +704,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
|
|||||||
static int get_describe_name(const char *name, int len, unsigned char *sha1)
|
static int get_describe_name(const char *name, int len, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
const char *cp;
|
const char *cp;
|
||||||
|
unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
|
||||||
|
|
||||||
for (cp = name + len - 1; name + 2 <= cp; cp--) {
|
for (cp = name + len - 1; name + 2 <= cp; cp--) {
|
||||||
char ch = *cp;
|
char ch = *cp;
|
||||||
@ -535,14 +715,14 @@ static int get_describe_name(const char *name, int len, unsigned char *sha1)
|
|||||||
if (ch == 'g' && cp[-1] == '-') {
|
if (ch == 'g' && cp[-1] == '-') {
|
||||||
cp++;
|
cp++;
|
||||||
len -= cp - name;
|
len -= cp - name;
|
||||||
return get_short_sha1(cp, len, sha1, 1);
|
return get_short_sha1(cp, len, sha1, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_sha1_1(const char *name, int len, unsigned char *sha1)
|
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
|
||||||
{
|
{
|
||||||
int ret, has_suffix;
|
int ret, has_suffix;
|
||||||
const char *cp;
|
const char *cp;
|
||||||
@ -587,7 +767,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return get_short_sha1(name, len, sha1, 0);
|
return get_short_sha1(name, len, sha1, lookup_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -769,7 +949,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
|
|||||||
struct strbuf sb;
|
struct strbuf sb;
|
||||||
strbuf_init(&sb, dots - name);
|
strbuf_init(&sb, dots - name);
|
||||||
strbuf_add(&sb, name, dots - name);
|
strbuf_add(&sb, name, dots - name);
|
||||||
st = get_sha1(sb.buf, sha1_tmp);
|
st = get_sha1_committish(sb.buf, sha1_tmp);
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
}
|
}
|
||||||
if (st)
|
if (st)
|
||||||
@ -778,7 +958,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
|
|||||||
if (!one)
|
if (!one)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
|
if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
|
||||||
return -1;
|
return -1;
|
||||||
two = lookup_commit_reference_gently(sha1_tmp, 0);
|
two = lookup_commit_reference_gently(sha1_tmp, 0);
|
||||||
if (!two)
|
if (!two)
|
||||||
@ -905,7 +1085,52 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
|
|||||||
int get_sha1(const char *name, unsigned char *sha1)
|
int get_sha1(const char *name, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
struct object_context unused;
|
struct object_context unused;
|
||||||
return get_sha1_with_context(name, sha1, &unused);
|
return get_sha1_with_context(name, 0, sha1, &unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Many callers know that the user meant to name a committish by
|
||||||
|
* syntactical positions where the object name appears. Calling this
|
||||||
|
* function allows the machinery to disambiguate shorter-than-unique
|
||||||
|
* abbreviated object names between committish and others.
|
||||||
|
*
|
||||||
|
* Note that this does NOT error out when the named object is not a
|
||||||
|
* committish. It is merely to give a hint to the disambiguation
|
||||||
|
* machinery.
|
||||||
|
*/
|
||||||
|
int get_sha1_committish(const char *name, unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct object_context unused;
|
||||||
|
return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
|
||||||
|
sha1, &unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_sha1_treeish(const char *name, unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct object_context unused;
|
||||||
|
return get_sha1_with_context(name, GET_SHA1_TREEISH,
|
||||||
|
sha1, &unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_sha1_commit(const char *name, unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct object_context unused;
|
||||||
|
return get_sha1_with_context(name, GET_SHA1_COMMIT,
|
||||||
|
sha1, &unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_sha1_tree(const char *name, unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct object_context unused;
|
||||||
|
return get_sha1_with_context(name, GET_SHA1_TREE,
|
||||||
|
sha1, &unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_sha1_blob(const char *name, unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct object_context unused;
|
||||||
|
return get_sha1_with_context(name, GET_SHA1_BLOB,
|
||||||
|
sha1, &unused);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called only when object_name:filename doesn't exist. */
|
/* Must be called only when object_name:filename doesn't exist. */
|
||||||
@ -1004,16 +1229,6 @@ static void diagnose_invalid_index_path(int stage,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
|
|
||||||
int only_to_die, const char *prefix)
|
|
||||||
{
|
|
||||||
struct object_context oc;
|
|
||||||
int ret;
|
|
||||||
ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix);
|
|
||||||
*mode = oc.mode;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *resolve_relative_path(const char *rel)
|
static char *resolve_relative_path(const char *rel)
|
||||||
{
|
{
|
||||||
if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
|
if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
|
||||||
@ -1031,20 +1246,24 @@ static char *resolve_relative_path(const char *rel)
|
|||||||
rel);
|
rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
static int get_sha1_with_context_1(const char *name,
|
||||||
struct object_context *oc,
|
unsigned flags,
|
||||||
int only_to_die, const char *prefix)
|
const char *prefix,
|
||||||
|
unsigned char *sha1,
|
||||||
|
struct object_context *oc)
|
||||||
{
|
{
|
||||||
int ret, bracket_depth;
|
int ret, bracket_depth;
|
||||||
int namelen = strlen(name);
|
int namelen = strlen(name);
|
||||||
const char *cp;
|
const char *cp;
|
||||||
|
int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
|
||||||
|
|
||||||
memset(oc, 0, sizeof(*oc));
|
memset(oc, 0, sizeof(*oc));
|
||||||
oc->mode = S_IFINVALID;
|
oc->mode = S_IFINVALID;
|
||||||
ret = get_sha1_1(name, namelen, sha1);
|
ret = get_sha1_1(name, namelen, sha1, flags);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return ret;
|
return ret;
|
||||||
/* sha1:path --> object name of path in ent sha1
|
/*
|
||||||
|
* sha1:path --> object name of path in ent sha1
|
||||||
* :path -> object name of absolute path in index
|
* :path -> object name of absolute path in index
|
||||||
* :./path -> object name of path relative to cwd in index
|
* :./path -> object name of path relative to cwd in index
|
||||||
* :[0-3]:path -> object name of path in index at stage
|
* :[0-3]:path -> object name of path in index at stage
|
||||||
@ -1119,7 +1338,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
strncpy(object_name, name, cp-name);
|
strncpy(object_name, name, cp-name);
|
||||||
object_name[cp-name] = '\0';
|
object_name[cp-name] = '\0';
|
||||||
}
|
}
|
||||||
if (!get_sha1_1(name, cp-name, tree_sha1)) {
|
if (!get_sha1_1(name, cp-name, tree_sha1, GET_SHA1_TREEISH)) {
|
||||||
const char *filename = cp+1;
|
const char *filename = cp+1;
|
||||||
char *new_filename = NULL;
|
char *new_filename = NULL;
|
||||||
|
|
||||||
@ -1146,3 +1365,22 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call this function when you know "name" given by the end user must
|
||||||
|
* name an object but it doesn't; the function _may_ die with a better
|
||||||
|
* diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not
|
||||||
|
* exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
|
||||||
|
* you have a chance to diagnose the error further.
|
||||||
|
*/
|
||||||
|
void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
|
||||||
|
{
|
||||||
|
struct object_context oc;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
|
||||||
|
{
|
||||||
|
return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
|
||||||
|
}
|
||||||
|
264
t/t1512-rev-parse-disambiguation.sh
Executable file
264
t/t1512-rev-parse-disambiguation.sh
Executable file
@ -0,0 +1,264 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='object name disambiguation
|
||||||
|
|
||||||
|
Create blobs, trees, commits and a tag that all share the same
|
||||||
|
prefix, and make sure "git rev-parse" can take advantage of
|
||||||
|
type information to disambiguate short object names that are
|
||||||
|
not necessarily unique.
|
||||||
|
|
||||||
|
The final history used in the test has five commits, with the bottom
|
||||||
|
one tagged as v1.0.0. They all have one regular file each.
|
||||||
|
|
||||||
|
+-------------------------------------------+
|
||||||
|
| |
|
||||||
|
| .-------b3wettvi---- ad2uee |
|
||||||
|
| / / |
|
||||||
|
| a2onsxbvj---czy8f73t--ioiley5o |
|
||||||
|
| |
|
||||||
|
+-------------------------------------------+
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'blob and tree' '
|
||||||
|
test_tick &&
|
||||||
|
(
|
||||||
|
for i in 0 1 2 3 4 5 6 7 8 9
|
||||||
|
do
|
||||||
|
echo $i
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
echo b1rwzyc3
|
||||||
|
) >a0blgqsjc &&
|
||||||
|
|
||||||
|
# create one blob 0000000000b36
|
||||||
|
git add a0blgqsjc &&
|
||||||
|
|
||||||
|
# create one tree 0000000000cdc
|
||||||
|
git write-tree
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'warn ambiguity when no candidate matches type hint' '
|
||||||
|
test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
|
||||||
|
grep "short SHA1 000000000 is ambiguous" actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'disambiguate tree-ish' '
|
||||||
|
# feed tree-ish in an unambiguous way
|
||||||
|
git rev-parse --verify 0000000000cdc:a0blgqsjc &&
|
||||||
|
|
||||||
|
# ambiguous at the object name level, but there is only one
|
||||||
|
# such tree-ish (the other is a blob)
|
||||||
|
git rev-parse --verify 000000000:a0blgqsjc
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'disambiguate blob' '
|
||||||
|
sed -e "s/|$//" >patch <<-EOF &&
|
||||||
|
diff --git a/frotz b/frotz
|
||||||
|
index 000000000..ffffff 100644
|
||||||
|
--- a/frotz
|
||||||
|
+++ b/frotz
|
||||||
|
@@ -10,3 +10,4 @@
|
||||||
|
9
|
||||||
|
|
|
||||||
|
b1rwzyc3
|
||||||
|
+irwry
|
||||||
|
EOF
|
||||||
|
(
|
||||||
|
GIT_INDEX_FILE=frotz &&
|
||||||
|
export GIT_INDEX_FILE &&
|
||||||
|
git apply --build-fake-ancestor frotz patch &&
|
||||||
|
git cat-file blob :frotz >actual
|
||||||
|
) &&
|
||||||
|
test_cmp a0blgqsjc actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'disambiguate tree' '
|
||||||
|
commit=$(echo "d7xm" | git commit-tree 000000000) &&
|
||||||
|
test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'first commit' '
|
||||||
|
# create one commit 0000000000e4f
|
||||||
|
git commit -m a2onsxbvj
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'disambiguate commit-ish' '
|
||||||
|
# feed commit-ish in an unambiguous way
|
||||||
|
git rev-parse --verify 0000000000e4f^{commit} &&
|
||||||
|
|
||||||
|
# ambiguous at the object name level, but there is only one
|
||||||
|
# such commit (the others are tree and blob)
|
||||||
|
git rev-parse --verify 000000000^{commit} &&
|
||||||
|
|
||||||
|
# likewise
|
||||||
|
git rev-parse --verify 000000000^0
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'disambiguate commit' '
|
||||||
|
commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
|
||||||
|
test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
|
||||||
|
git log 000000000..000000000 &&
|
||||||
|
git log ..000000000 &&
|
||||||
|
git log 000000000.. &&
|
||||||
|
git log 000000000...000000000 &&
|
||||||
|
git log ...000000000 &&
|
||||||
|
git log 000000000...
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
|
||||||
|
git rev-parse 000000000..000000000 &&
|
||||||
|
git rev-parse ..000000000 &&
|
||||||
|
git rev-parse 000000000..
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git log takes only commit-ish' '
|
||||||
|
git log 000000000
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git reset takes only commit-ish' '
|
||||||
|
git reset 000000000
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'first tag' '
|
||||||
|
# create one tag 0000000000f8f
|
||||||
|
git tag -a -m j7cp83um v1.0.0
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure 'two semi-ambiguous commit-ish' '
|
||||||
|
# Once the parser becomes ultra-smart, it could notice that
|
||||||
|
# 110282 before ^{commit} name many different objects, but
|
||||||
|
# that only two (HEAD and v1.0.0 tag) can be peeled to commit,
|
||||||
|
# and that peeling them down to commit yield the same commit
|
||||||
|
# without ambiguity.
|
||||||
|
git rev-parse --verify 110282^{commit} &&
|
||||||
|
|
||||||
|
# likewise
|
||||||
|
git log 000000000..000000000 &&
|
||||||
|
git log ..000000000 &&
|
||||||
|
git log 000000000.. &&
|
||||||
|
git log 000000000...000000000 &&
|
||||||
|
git log ...000000000 &&
|
||||||
|
git log 000000000...
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure 'three semi-ambiguous tree-ish' '
|
||||||
|
# Likewise for tree-ish. HEAD, v1.0.0 and HEAD^{tree} share
|
||||||
|
# the prefix but peeling them to tree yields the same thing
|
||||||
|
git rev-parse --verify 000000000^{tree}
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'parse describe name' '
|
||||||
|
# feed an unambiguous describe name
|
||||||
|
git rev-parse --verify v1.0.0-0-g0000000000e4f &&
|
||||||
|
|
||||||
|
# ambiguous at the object name level, but there is only one
|
||||||
|
# such commit (others are blob, tree and tag)
|
||||||
|
git rev-parse --verify v1.0.0-0-g000000000
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'more history' '
|
||||||
|
# commit 0000000000043
|
||||||
|
git mv a0blgqsjc d12cr3h8t &&
|
||||||
|
echo h62xsjeu >>d12cr3h8t &&
|
||||||
|
git add d12cr3h8t &&
|
||||||
|
|
||||||
|
test_tick &&
|
||||||
|
git commit -m czy8f73t &&
|
||||||
|
|
||||||
|
# commit 00000000008ec
|
||||||
|
git mv d12cr3h8t j000jmpzn &&
|
||||||
|
echo j08bekfvt >>j000jmpzn &&
|
||||||
|
git add j000jmpzn &&
|
||||||
|
|
||||||
|
test_tick &&
|
||||||
|
git commit -m ioiley5o &&
|
||||||
|
|
||||||
|
# commit 0000000005b0
|
||||||
|
git checkout v1.0.0^0 &&
|
||||||
|
git mv a0blgqsjc f5518nwu &&
|
||||||
|
|
||||||
|
for i in h62xsjeu j08bekfvt kg7xflhm
|
||||||
|
do
|
||||||
|
echo $i
|
||||||
|
done >>f5518nwu &&
|
||||||
|
git add f5518nwu &&
|
||||||
|
|
||||||
|
test_tick &&
|
||||||
|
git commit -m b3wettvi &&
|
||||||
|
side=$(git rev-parse HEAD) &&
|
||||||
|
|
||||||
|
# commit 000000000066
|
||||||
|
git checkout master &&
|
||||||
|
|
||||||
|
# If you use recursive, merge will fail and you will need to
|
||||||
|
# clean up a0blgqsjc as well. If you use resolve, merge will
|
||||||
|
# succeed.
|
||||||
|
test_might_fail git merge --no-commit -s recursive $side &&
|
||||||
|
git rm -f f5518nwu j000jmpzn &&
|
||||||
|
|
||||||
|
test_might_fail git rm -f a0blgqsjc &&
|
||||||
|
(
|
||||||
|
git cat-file blob $side:f5518nwu
|
||||||
|
echo j3l0i9s6
|
||||||
|
) >ab2gs879 &&
|
||||||
|
git add ab2gs879 &&
|
||||||
|
|
||||||
|
test_tick &&
|
||||||
|
git commit -m ad2uee
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure 'parse describe name taking advantage of generation' '
|
||||||
|
# ambiguous at the object name level, but there is only one
|
||||||
|
# such commit at generation 0
|
||||||
|
git rev-parse --verify v1.0.0-0-g000000000 &&
|
||||||
|
|
||||||
|
# likewise for generation 2 and 4
|
||||||
|
git rev-parse --verify v1.0.0-2-g000000000 &&
|
||||||
|
git rev-parse --verify v1.0.0-4-g000000000
|
||||||
|
'
|
||||||
|
|
||||||
|
# Note: because rev-parse does not even try to disambiguate based on
|
||||||
|
# the generation number, this test currently succeeds for a wrong
|
||||||
|
# reason. When it learns to use the generation number, the previous
|
||||||
|
# test should succeed, and also this test should fail because the
|
||||||
|
# describe name used in the test with generation number can name two
|
||||||
|
# commits. Make sure that such a future enhancement does not randomly
|
||||||
|
# pick one.
|
||||||
|
test_expect_success 'parse describe name not ignoring ambiguity' '
|
||||||
|
# ambiguous at the object name level, and there are two such
|
||||||
|
# commits at generation 1
|
||||||
|
test_must_fail git rev-parse --verify v1.0.0-1-g000000000
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'ambiguous commit-ish' '
|
||||||
|
# Now there are many commits that begin with the
|
||||||
|
# common prefix, none of these should pick one at
|
||||||
|
# random. They all should result in ambiguity errors.
|
||||||
|
test_must_fail git rev-parse --verify 110282^{commit} &&
|
||||||
|
|
||||||
|
# likewise
|
||||||
|
test_must_fail git log 000000000..000000000 &&
|
||||||
|
test_must_fail git log ..000000000 &&
|
||||||
|
test_must_fail git log 000000000.. &&
|
||||||
|
test_must_fail git log 000000000...000000000 &&
|
||||||
|
test_must_fail git log ...000000000 &&
|
||||||
|
test_must_fail git log 000000000...
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'rev-parse --disambiguate' '
|
||||||
|
# The test creates 16 objects that share the prefix and two
|
||||||
|
# commits created by commit-tree in earlier tests share a
|
||||||
|
# different prefix.
|
||||||
|
git rev-parse --disambiguate=000000000 >actual &&
|
||||||
|
test $(wc -l <actual) = 16 &&
|
||||||
|
test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Loading…
Reference in New Issue
Block a user