Merge branch 'jk/diff-blob'
The result from "git diff" that compares two blobs, e.g. "git diff $commit1:$path $commit2:$path", used to be shown with the full object name as given on the command line, but it is more natural to use the $path in the output and use it to look up .gitattributes. * jk/diff-blob: diff: use blob path for blob/file diffs diff: use pending "path" if it is available diff: use the word "path" instead of "name" for blobs diff: pass whole pending entry in blobinfo handle_revision_arg: record paths for pending objects handle_revision_arg: record modes for "a..b" endpoints t4063: add tests of direct blob diffs get_sha1_with_context: dynamically allocate oc->path get_sha1_with_context: always initialize oc->symlink_path sha1_name: consistently refer to object_context as "oc" handle_revision_arg: add handle_dotdot() helper handle_revision_arg: hoist ".." check out of range parsing handle_revision_arg: stop using "dotdot" as a generic pointer handle_revision_arg: simplify commit reference lookups handle_revision_arg: reset "dotdot" consistently
This commit is contained in:
243
revision.c
243
revision.c
@ -1429,134 +1429,168 @@ static void prepare_show_merge(struct rev_info *revs)
|
||||
revs->limited = 1;
|
||||
}
|
||||
|
||||
static int dotdot_missing(const char *arg, char *dotdot,
|
||||
struct rev_info *revs, int symmetric)
|
||||
{
|
||||
if (revs->ignore_missing)
|
||||
return 0;
|
||||
/* de-munge so we report the full argument */
|
||||
*dotdot = '.';
|
||||
die(symmetric
|
||||
? "Invalid symmetric difference expression %s"
|
||||
: "Invalid revision range %s", arg);
|
||||
}
|
||||
|
||||
static int handle_dotdot_1(const char *arg, char *dotdot,
|
||||
struct rev_info *revs, int flags,
|
||||
int cant_be_filename,
|
||||
struct object_context *a_oc,
|
||||
struct object_context *b_oc)
|
||||
{
|
||||
const char *a_name, *b_name;
|
||||
struct object_id a_oid, b_oid;
|
||||
struct object *a_obj, *b_obj;
|
||||
unsigned int a_flags, b_flags;
|
||||
int symmetric = 0;
|
||||
unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
|
||||
unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
|
||||
|
||||
a_name = arg;
|
||||
if (!*a_name)
|
||||
a_name = "HEAD";
|
||||
|
||||
b_name = dotdot + 2;
|
||||
if (*b_name == '.') {
|
||||
symmetric = 1;
|
||||
b_name++;
|
||||
}
|
||||
if (!*b_name)
|
||||
b_name = "HEAD";
|
||||
|
||||
if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
|
||||
get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
|
||||
return -1;
|
||||
|
||||
if (!cant_be_filename) {
|
||||
*dotdot = '.';
|
||||
verify_non_filename(revs->prefix, arg);
|
||||
*dotdot = '\0';
|
||||
}
|
||||
|
||||
a_obj = parse_object(&a_oid);
|
||||
b_obj = parse_object(&b_oid);
|
||||
if (!a_obj || !b_obj)
|
||||
return dotdot_missing(arg, dotdot, revs, symmetric);
|
||||
|
||||
if (!symmetric) {
|
||||
/* just A..B */
|
||||
b_flags = flags;
|
||||
a_flags = flags_exclude;
|
||||
} else {
|
||||
/* A...B -- find merge bases between the two */
|
||||
struct commit *a, *b;
|
||||
struct commit_list *exclude;
|
||||
|
||||
a = lookup_commit_reference(&a_obj->oid);
|
||||
b = lookup_commit_reference(&b_obj->oid);
|
||||
if (!a || !b)
|
||||
return dotdot_missing(arg, dotdot, revs, symmetric);
|
||||
|
||||
exclude = get_merge_bases(a, b);
|
||||
add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
|
||||
flags_exclude);
|
||||
add_pending_commit_list(revs, exclude, flags_exclude);
|
||||
free_commit_list(exclude);
|
||||
|
||||
b_flags = flags;
|
||||
a_flags = flags | SYMMETRIC_LEFT;
|
||||
}
|
||||
|
||||
a_obj->flags |= a_flags;
|
||||
b_obj->flags |= b_flags;
|
||||
add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags);
|
||||
add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags);
|
||||
add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path);
|
||||
add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_dotdot(const char *arg,
|
||||
struct rev_info *revs, int flags,
|
||||
int cant_be_filename)
|
||||
{
|
||||
struct object_context a_oc, b_oc;
|
||||
char *dotdot = strstr(arg, "..");
|
||||
int ret;
|
||||
|
||||
if (!dotdot)
|
||||
return -1;
|
||||
|
||||
memset(&a_oc, 0, sizeof(a_oc));
|
||||
memset(&b_oc, 0, sizeof(b_oc));
|
||||
|
||||
*dotdot = '\0';
|
||||
ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
|
||||
&a_oc, &b_oc);
|
||||
*dotdot = '.';
|
||||
|
||||
free(a_oc.path);
|
||||
free(b_oc.path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
|
||||
{
|
||||
struct object_context oc;
|
||||
char *dotdot;
|
||||
char *mark;
|
||||
struct object *object;
|
||||
struct object_id oid;
|
||||
int local_flags;
|
||||
const char *arg = arg_;
|
||||
int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
|
||||
unsigned get_sha1_flags = 0;
|
||||
unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
|
||||
|
||||
flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
|
||||
|
||||
dotdot = strstr(arg, "..");
|
||||
if (dotdot) {
|
||||
struct object_id from_oid;
|
||||
const char *next = dotdot + 2;
|
||||
const char *this = arg;
|
||||
int symmetric = *next == '.';
|
||||
unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
|
||||
static const char head_by_default[] = "HEAD";
|
||||
unsigned int a_flags;
|
||||
|
||||
*dotdot = 0;
|
||||
next += symmetric;
|
||||
|
||||
if (!*next)
|
||||
next = head_by_default;
|
||||
if (dotdot == arg)
|
||||
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.
|
||||
*/
|
||||
if (!cant_be_filename) {
|
||||
*dotdot = '.';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!get_sha1_committish(this, from_oid.hash) &&
|
||||
!get_sha1_committish(next, oid.hash)) {
|
||||
struct object *a_obj, *b_obj;
|
||||
|
||||
if (!cant_be_filename) {
|
||||
*dotdot = '.';
|
||||
verify_non_filename(revs->prefix, arg);
|
||||
}
|
||||
|
||||
a_obj = parse_object(&from_oid);
|
||||
b_obj = parse_object(&oid);
|
||||
if (!a_obj || !b_obj) {
|
||||
missing:
|
||||
if (revs->ignore_missing)
|
||||
return 0;
|
||||
die(symmetric
|
||||
? "Invalid symmetric difference expression %s"
|
||||
: "Invalid revision range %s", arg);
|
||||
}
|
||||
|
||||
if (!symmetric) {
|
||||
/* just A..B */
|
||||
a_flags = flags_exclude;
|
||||
} else {
|
||||
/* A...B -- find merge bases between the two */
|
||||
struct commit *a, *b;
|
||||
struct commit_list *exclude;
|
||||
|
||||
a = (a_obj->type == OBJ_COMMIT
|
||||
? (struct commit *)a_obj
|
||||
: lookup_commit_reference(&a_obj->oid));
|
||||
b = (b_obj->type == OBJ_COMMIT
|
||||
? (struct commit *)b_obj
|
||||
: lookup_commit_reference(&b_obj->oid));
|
||||
if (!a || !b)
|
||||
goto missing;
|
||||
exclude = get_merge_bases(a, b);
|
||||
add_rev_cmdline_list(revs, exclude,
|
||||
REV_CMD_MERGE_BASE,
|
||||
flags_exclude);
|
||||
add_pending_commit_list(revs, exclude,
|
||||
flags_exclude);
|
||||
free_commit_list(exclude);
|
||||
|
||||
a_flags = flags | SYMMETRIC_LEFT;
|
||||
}
|
||||
|
||||
a_obj->flags |= a_flags;
|
||||
b_obj->flags |= flags;
|
||||
add_rev_cmdline(revs, a_obj, this,
|
||||
REV_CMD_LEFT, a_flags);
|
||||
add_rev_cmdline(revs, b_obj, next,
|
||||
REV_CMD_RIGHT, flags);
|
||||
add_pending_object(revs, a_obj, this);
|
||||
add_pending_object(revs, b_obj, next);
|
||||
return 0;
|
||||
}
|
||||
*dotdot = '.';
|
||||
if (!cant_be_filename && !strcmp(arg, "..")) {
|
||||
/*
|
||||
* Just ".."? That is not a range but the
|
||||
* pathspec for the parent directory.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
dotdot = strstr(arg, "^@");
|
||||
if (dotdot && !dotdot[2]) {
|
||||
*dotdot = 0;
|
||||
if (!handle_dotdot(arg, revs, flags, revarg_opt))
|
||||
return 0;
|
||||
|
||||
mark = strstr(arg, "^@");
|
||||
if (mark && !mark[2]) {
|
||||
*mark = 0;
|
||||
if (add_parents_only(revs, arg, flags, 0))
|
||||
return 0;
|
||||
*dotdot = '^';
|
||||
*mark = '^';
|
||||
}
|
||||
dotdot = strstr(arg, "^!");
|
||||
if (dotdot && !dotdot[2]) {
|
||||
*dotdot = 0;
|
||||
mark = strstr(arg, "^!");
|
||||
if (mark && !mark[2]) {
|
||||
*mark = 0;
|
||||
if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
|
||||
*dotdot = '^';
|
||||
*mark = '^';
|
||||
}
|
||||
dotdot = strstr(arg, "^-");
|
||||
if (dotdot) {
|
||||
mark = strstr(arg, "^-");
|
||||
if (mark) {
|
||||
int exclude_parent = 1;
|
||||
|
||||
if (dotdot[2]) {
|
||||
if (mark[2]) {
|
||||
char *end;
|
||||
exclude_parent = strtoul(dotdot + 2, &end, 10);
|
||||
exclude_parent = strtoul(mark + 2, &end, 10);
|
||||
if (*end != '\0' || !exclude_parent)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*dotdot = 0;
|
||||
*mark = 0;
|
||||
if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
|
||||
*dotdot = '^';
|
||||
*mark = '^';
|
||||
}
|
||||
|
||||
local_flags = 0;
|
||||
@ -1566,7 +1600,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
|
||||
}
|
||||
|
||||
if (revarg_opt & REVARG_COMMITTISH)
|
||||
get_sha1_flags = GET_SHA1_COMMITTISH;
|
||||
get_sha1_flags |= GET_SHA1_COMMITTISH;
|
||||
|
||||
if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc))
|
||||
return revs->ignore_missing ? 0 : -1;
|
||||
@ -1574,7 +1608,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
|
||||
verify_non_filename(revs->prefix, arg);
|
||||
object = get_reference(revs, arg, &oid, flags ^ local_flags);
|
||||
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
|
||||
add_pending_object_with_mode(revs, object, arg, oc.mode);
|
||||
add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
|
||||
free(oc.path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user