Merge branch 'cc/replace-object-info'
read_sha1_file() that is the workhorse to read the contents given an object name honoured object replacements, but there is no corresponding mechanism to sha1_object_info() that is used to obtain the metainfo (e.g. type & size) about the object, leading callers to weird inconsistencies. * cc/replace-object-info: replace info: rename 'full' to 'long' and clarify in-code symbols Documentation/git-replace: describe --format option builtin/replace: unset read_replace_refs t6050: add tests for listing with --format builtin/replace: teach listing using short, medium or full formats sha1_file: perform object replacement in sha1_object_info_extended() t6050: show that git cat-file --batch fails with replace objects sha1_object_info_extended(): add an "unsigned flags" parameter sha1_file.c: add lookup_replace_object_extended() to pass flags replace_object: don't check read_replace_refs twice rename READ_SHA1_FILE_REPLACE flag to LOOKUP_REPLACE_OBJECT
This commit is contained in:
commit
b0504a9519
@ -10,7 +10,7 @@ SYNOPSIS
|
|||||||
[verse]
|
[verse]
|
||||||
'git replace' [-f] <object> <replacement>
|
'git replace' [-f] <object> <replacement>
|
||||||
'git replace' -d <object>...
|
'git replace' -d <object>...
|
||||||
'git replace' -l [<pattern>]
|
'git replace' [--format=<format>] [-l [<pattern>]]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
@ -70,6 +70,23 @@ OPTIONS
|
|||||||
Typing "git replace" without arguments, also lists all replace
|
Typing "git replace" without arguments, also lists all replace
|
||||||
refs.
|
refs.
|
||||||
|
|
||||||
|
--format=<format>::
|
||||||
|
When listing, use the specified <format>, which can be one of
|
||||||
|
'short', 'medium' and 'long'. When omitted, the format
|
||||||
|
defaults to 'short'.
|
||||||
|
|
||||||
|
FORMATS
|
||||||
|
-------
|
||||||
|
|
||||||
|
The following format are available:
|
||||||
|
|
||||||
|
* 'short':
|
||||||
|
<replaced sha1>
|
||||||
|
* 'medium':
|
||||||
|
<replaced sha1> -> <replacement sha1>
|
||||||
|
* 'long':
|
||||||
|
<replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>)
|
||||||
|
|
||||||
CREATING REPLACEMENT OBJECTS
|
CREATING REPLACEMENT OBJECTS
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
|
if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
|
||||||
printf("%s missing\n", obj_name);
|
printf("%s missing\n", obj_name);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -16,27 +16,69 @@
|
|||||||
static const char * const git_replace_usage[] = {
|
static const char * const git_replace_usage[] = {
|
||||||
N_("git replace [-f] <object> <replacement>"),
|
N_("git replace [-f] <object> <replacement>"),
|
||||||
N_("git replace -d <object>..."),
|
N_("git replace -d <object>..."),
|
||||||
N_("git replace -l [<pattern>]"),
|
N_("git replace [--format=<format>] [-l [<pattern>]]"),
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum replace_format {
|
||||||
|
REPLACE_FORMAT_SHORT,
|
||||||
|
REPLACE_FORMAT_MEDIUM,
|
||||||
|
REPLACE_FORMAT_LONG
|
||||||
|
};
|
||||||
|
|
||||||
|
struct show_data {
|
||||||
|
const char *pattern;
|
||||||
|
enum replace_format format;
|
||||||
|
};
|
||||||
|
|
||||||
static int show_reference(const char *refname, const unsigned char *sha1,
|
static int show_reference(const char *refname, const unsigned char *sha1,
|
||||||
int flag, void *cb_data)
|
int flag, void *cb_data)
|
||||||
{
|
{
|
||||||
const char *pattern = cb_data;
|
struct show_data *data = cb_data;
|
||||||
|
|
||||||
if (!fnmatch(pattern, refname, 0))
|
if (!fnmatch(data->pattern, refname, 0)) {
|
||||||
|
if (data->format == REPLACE_FORMAT_SHORT)
|
||||||
printf("%s\n", refname);
|
printf("%s\n", refname);
|
||||||
|
else if (data->format == REPLACE_FORMAT_MEDIUM)
|
||||||
|
printf("%s -> %s\n", refname, sha1_to_hex(sha1));
|
||||||
|
else { /* data->format == REPLACE_FORMAT_LONG */
|
||||||
|
unsigned char object[20];
|
||||||
|
enum object_type obj_type, repl_type;
|
||||||
|
|
||||||
|
if (get_sha1(refname, object))
|
||||||
|
return error("Failed to resolve '%s' as a valid ref.", refname);
|
||||||
|
|
||||||
|
obj_type = sha1_object_info(object, NULL);
|
||||||
|
repl_type = sha1_object_info(sha1, NULL);
|
||||||
|
|
||||||
|
printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
|
||||||
|
sha1_to_hex(sha1), typename(repl_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int list_replace_refs(const char *pattern)
|
static int list_replace_refs(const char *pattern, const char *format)
|
||||||
{
|
{
|
||||||
|
struct show_data data;
|
||||||
|
|
||||||
if (pattern == NULL)
|
if (pattern == NULL)
|
||||||
pattern = "*";
|
pattern = "*";
|
||||||
|
data.pattern = pattern;
|
||||||
|
|
||||||
for_each_replace_ref(show_reference, (void *) pattern);
|
if (format == NULL || *format == '\0' || !strcmp(format, "short"))
|
||||||
|
data.format = REPLACE_FORMAT_SHORT;
|
||||||
|
else if (!strcmp(format, "medium"))
|
||||||
|
data.format = REPLACE_FORMAT_MEDIUM;
|
||||||
|
else if (!strcmp(format, "long"))
|
||||||
|
data.format = REPLACE_FORMAT_LONG;
|
||||||
|
else
|
||||||
|
die("invalid replace format '%s'\n"
|
||||||
|
"valid formats are 'short', 'medium' and 'long'\n",
|
||||||
|
format);
|
||||||
|
|
||||||
|
for_each_replace_ref(show_reference, (void *) &data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref,
|
|||||||
int cmd_replace(int argc, const char **argv, const char *prefix)
|
int cmd_replace(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
int list = 0, delete = 0, force = 0;
|
int list = 0, delete = 0, force = 0;
|
||||||
|
const char *format = NULL;
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_BOOL('l', "list", &list, N_("list replace refs")),
|
OPT_BOOL('l', "list", &list, N_("list replace refs")),
|
||||||
OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
|
OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
|
||||||
OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
|
OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
|
||||||
|
OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
read_replace_refs = 0;
|
||||||
|
|
||||||
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
|
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
|
||||||
|
|
||||||
if (list && delete)
|
if (list && delete)
|
||||||
usage_msg_opt("-l and -d cannot be used together",
|
usage_msg_opt("-l and -d cannot be used together",
|
||||||
git_replace_usage, options);
|
git_replace_usage, options);
|
||||||
|
|
||||||
|
if (format && delete)
|
||||||
|
usage_msg_opt("--format and -d cannot be used together",
|
||||||
|
git_replace_usage, options);
|
||||||
|
|
||||||
if (force && (list || delete))
|
if (force && (list || delete))
|
||||||
usage_msg_opt("-f cannot be used with -d or -l",
|
usage_msg_opt("-f cannot be used with -d or -l",
|
||||||
git_replace_usage, options);
|
git_replace_usage, options);
|
||||||
@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
|
|||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
usage_msg_opt("bad number of arguments",
|
usage_msg_opt("bad number of arguments",
|
||||||
git_replace_usage, options);
|
git_replace_usage, options);
|
||||||
|
if (format)
|
||||||
|
usage_msg_opt("--format cannot be used when not listing",
|
||||||
|
git_replace_usage, options);
|
||||||
return replace_object(argv[0], argv[1], force);
|
return replace_object(argv[0], argv[1], force);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
|
|||||||
usage_msg_opt("-f needs some arguments",
|
usage_msg_opt("-f needs some arguments",
|
||||||
git_replace_usage, options);
|
git_replace_usage, options);
|
||||||
|
|
||||||
return list_replace_refs(argv[0]);
|
return list_replace_refs(argv[0], format);
|
||||||
}
|
}
|
||||||
|
12
cache.h
12
cache.h
@ -760,11 +760,11 @@ int daemon_avoid_alias(const char *path);
|
|||||||
int offset_1st_component(const char *path);
|
int offset_1st_component(const char *path);
|
||||||
|
|
||||||
/* object replacement */
|
/* object replacement */
|
||||||
#define READ_SHA1_FILE_REPLACE 1
|
#define LOOKUP_REPLACE_OBJECT 1
|
||||||
extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
|
extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
|
||||||
static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
|
static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
|
||||||
{
|
{
|
||||||
return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE);
|
return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
|
||||||
}
|
}
|
||||||
extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
|
extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
|
||||||
static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
|
static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
|
||||||
@ -773,6 +773,12 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh
|
|||||||
return sha1;
|
return sha1;
|
||||||
return do_lookup_replace_object(sha1);
|
return do_lookup_replace_object(sha1);
|
||||||
}
|
}
|
||||||
|
static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
|
||||||
|
{
|
||||||
|
if (!(flag & LOOKUP_REPLACE_OBJECT))
|
||||||
|
return sha1;
|
||||||
|
return lookup_replace_object(sha1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
|
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
|
||||||
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
||||||
@ -1098,7 +1104,7 @@ struct object_info {
|
|||||||
} packed;
|
} packed;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
|
extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
|
||||||
|
|
||||||
/* Dumb servers support */
|
/* Dumb servers support */
|
||||||
extern int update_server_info(int);
|
extern int update_server_info(int);
|
||||||
|
@ -97,9 +97,6 @@ const unsigned char *do_lookup_replace_object(const unsigned char *sha1)
|
|||||||
int pos, depth = MAXREPLACEDEPTH;
|
int pos, depth = MAXREPLACEDEPTH;
|
||||||
const unsigned char *cur = sha1;
|
const unsigned char *cur = sha1;
|
||||||
|
|
||||||
if (!read_replace_refs)
|
|
||||||
return sha1;
|
|
||||||
|
|
||||||
prepare_replace_object();
|
prepare_replace_object();
|
||||||
|
|
||||||
/* Try to recursively replace the object */
|
/* Try to recursively replace the object */
|
||||||
|
20
sha1_file.c
20
sha1_file.c
@ -2443,13 +2443,14 @@ static int sha1_loose_object_info(const unsigned char *sha1,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
|
int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
|
||||||
{
|
{
|
||||||
struct cached_object *co;
|
struct cached_object *co;
|
||||||
struct pack_entry e;
|
struct pack_entry e;
|
||||||
int rtype;
|
int rtype;
|
||||||
|
const unsigned char *real = lookup_replace_object_extended(sha1, flags);
|
||||||
|
|
||||||
co = find_cached_object(sha1);
|
co = find_cached_object(real);
|
||||||
if (co) {
|
if (co) {
|
||||||
if (oi->typep)
|
if (oi->typep)
|
||||||
*(oi->typep) = co->type;
|
*(oi->typep) = co->type;
|
||||||
@ -2461,23 +2462,23 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!find_pack_entry(sha1, &e)) {
|
if (!find_pack_entry(real, &e)) {
|
||||||
/* Most likely it's a loose object. */
|
/* Most likely it's a loose object. */
|
||||||
if (!sha1_loose_object_info(sha1, oi)) {
|
if (!sha1_loose_object_info(real, oi)) {
|
||||||
oi->whence = OI_LOOSE;
|
oi->whence = OI_LOOSE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Not a loose object; someone else may have just packed it. */
|
/* Not a loose object; someone else may have just packed it. */
|
||||||
reprepare_packed_git();
|
reprepare_packed_git();
|
||||||
if (!find_pack_entry(sha1, &e))
|
if (!find_pack_entry(real, &e))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtype = packed_object_info(e.p, e.offset, oi);
|
rtype = packed_object_info(e.p, e.offset, oi);
|
||||||
if (rtype < 0) {
|
if (rtype < 0) {
|
||||||
mark_bad_packed_object(e.p, sha1);
|
mark_bad_packed_object(e.p, real);
|
||||||
return sha1_object_info_extended(sha1, oi);
|
return sha1_object_info_extended(real, oi, 0);
|
||||||
} else if (in_delta_base_cache(e.p, e.offset)) {
|
} else if (in_delta_base_cache(e.p, e.offset)) {
|
||||||
oi->whence = OI_DBCACHED;
|
oi->whence = OI_DBCACHED;
|
||||||
} else {
|
} else {
|
||||||
@ -2499,7 +2500,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
|
|||||||
|
|
||||||
oi.typep = &type;
|
oi.typep = &type;
|
||||||
oi.sizep = sizep;
|
oi.sizep = sizep;
|
||||||
if (sha1_object_info_extended(sha1, &oi) < 0)
|
if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@ -2591,8 +2592,7 @@ void *read_sha1_file_extended(const unsigned char *sha1,
|
|||||||
void *data;
|
void *data;
|
||||||
char *path;
|
char *path;
|
||||||
const struct packed_git *p;
|
const struct packed_git *p;
|
||||||
const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
|
const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
|
||||||
? lookup_replace_object(sha1) : sha1;
|
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
data = read_object(repl, type, size);
|
data = read_object(repl, type, size);
|
||||||
|
@ -113,7 +113,7 @@ static enum input_source istream_source(const unsigned char *sha1,
|
|||||||
|
|
||||||
oi->typep = type;
|
oi->typep = type;
|
||||||
oi->sizep = &size;
|
oi->sizep = &size;
|
||||||
status = sha1_object_info_extended(sha1, oi);
|
status = sha1_object_info_extended(sha1, oi, 0);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return stream_error;
|
return stream_error;
|
||||||
|
|
||||||
|
@ -276,6 +276,48 @@ test_expect_success '-f option bypasses the type check' '
|
|||||||
git replace -f HEAD^ $BLOB
|
git replace -f HEAD^ $BLOB
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git cat-file --batch works on replace objects' '
|
||||||
|
git replace | grep $PARA3 &&
|
||||||
|
echo $PARA3 | git cat-file --batch
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test --format bogus' '
|
||||||
|
test_must_fail git replace --format bogus >/dev/null 2>&1
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test --format short' '
|
||||||
|
git replace --format=short >actual &&
|
||||||
|
git replace >expected &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test --format medium' '
|
||||||
|
H1=$(git --no-replace-objects rev-parse HEAD~1) &&
|
||||||
|
HT=$(git --no-replace-objects rev-parse HEAD^{tree}) &&
|
||||||
|
MYTAG=$(git --no-replace-objects rev-parse mytag) &&
|
||||||
|
{
|
||||||
|
echo "$H1 -> $BLOB" &&
|
||||||
|
echo "$BLOB -> $REPLACED" &&
|
||||||
|
echo "$HT -> $H1" &&
|
||||||
|
echo "$PARA3 -> $S" &&
|
||||||
|
echo "$MYTAG -> $HASH1"
|
||||||
|
} | sort >expected &&
|
||||||
|
git replace -l --format medium | sort > actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test --format long' '
|
||||||
|
{
|
||||||
|
echo "$H1 (commit) -> $BLOB (blob)" &&
|
||||||
|
echo "$BLOB (blob) -> $REPLACED (blob)" &&
|
||||||
|
echo "$HT (tree) -> $H1 (commit)" &&
|
||||||
|
echo "$PARA3 (commit) -> $S (commit)" &&
|
||||||
|
echo "$MYTAG (tag) -> $HASH1 (commit)"
|
||||||
|
} | sort >expected &&
|
||||||
|
git replace --format=long | sort > actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'replace ref cleanup' '
|
test_expect_success 'replace ref cleanup' '
|
||||||
test -n "$(git replace)" &&
|
test -n "$(git replace)" &&
|
||||||
git replace -d $(git replace) &&
|
git replace -d $(git replace) &&
|
||||||
|
Loading…
Reference in New Issue
Block a user