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]
|
||||
'git replace' [-f] <object> <replacement>
|
||||
'git replace' -d <object>...
|
||||
'git replace' -l [<pattern>]
|
||||
'git replace' [--format=<format>] [-l [<pattern>]]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -70,6 +70,23 @@ OPTIONS
|
||||
Typing "git replace" without arguments, also lists all replace
|
||||
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
|
||||
----------------------------
|
||||
|
||||
|
@ -241,7 +241,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
|
||||
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);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
|
@ -16,27 +16,69 @@
|
||||
static const char * const git_replace_usage[] = {
|
||||
N_("git replace [-f] <object> <replacement>"),
|
||||
N_("git replace -d <object>..."),
|
||||
N_("git replace -l [<pattern>]"),
|
||||
N_("git replace [--format=<format>] [-l [<pattern>]]"),
|
||||
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,
|
||||
int flag, void *cb_data)
|
||||
{
|
||||
const char *pattern = cb_data;
|
||||
struct show_data *data = cb_data;
|
||||
|
||||
if (!fnmatch(pattern, refname, 0))
|
||||
printf("%s\n", refname);
|
||||
if (!fnmatch(data->pattern, refname, 0)) {
|
||||
if (data->format == REPLACE_FORMAT_SHORT)
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
@ -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 list = 0, delete = 0, force = 0;
|
||||
const char *format = NULL;
|
||||
struct option options[] = {
|
||||
OPT_BOOL('l', "list", &list, N_("list replace refs")),
|
||||
OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
|
||||
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()
|
||||
};
|
||||
|
||||
read_replace_refs = 0;
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
|
||||
|
||||
if (list && delete)
|
||||
usage_msg_opt("-l and -d cannot be used together",
|
||||
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))
|
||||
usage_msg_opt("-f cannot be used with -d or -l",
|
||||
git_replace_usage, options);
|
||||
@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
|
||||
if (argc != 2)
|
||||
usage_msg_opt("bad number of arguments",
|
||||
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);
|
||||
}
|
||||
|
||||
@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
|
||||
usage_msg_opt("-f needs some arguments",
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
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 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 */
|
||||
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
||||
@ -1098,7 +1104,7 @@ struct object_info {
|
||||
} packed;
|
||||
} 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 */
|
||||
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;
|
||||
const unsigned char *cur = sha1;
|
||||
|
||||
if (!read_replace_refs)
|
||||
return sha1;
|
||||
|
||||
prepare_replace_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;
|
||||
}
|
||||
|
||||
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 pack_entry e;
|
||||
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 (oi->typep)
|
||||
*(oi->typep) = co->type;
|
||||
@ -2461,23 +2462,23 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!find_pack_entry(sha1, &e)) {
|
||||
if (!find_pack_entry(real, &e)) {
|
||||
/* 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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not a loose object; someone else may have just packed it. */
|
||||
reprepare_packed_git();
|
||||
if (!find_pack_entry(sha1, &e))
|
||||
if (!find_pack_entry(real, &e))
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtype = packed_object_info(e.p, e.offset, oi);
|
||||
if (rtype < 0) {
|
||||
mark_bad_packed_object(e.p, sha1);
|
||||
return sha1_object_info_extended(sha1, oi);
|
||||
mark_bad_packed_object(e.p, real);
|
||||
return sha1_object_info_extended(real, oi, 0);
|
||||
} else if (in_delta_base_cache(e.p, e.offset)) {
|
||||
oi->whence = OI_DBCACHED;
|
||||
} else {
|
||||
@ -2499,7 +2500,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
|
||||
|
||||
oi.typep = &type;
|
||||
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 type;
|
||||
}
|
||||
@ -2591,8 +2592,7 @@ void *read_sha1_file_extended(const unsigned char *sha1,
|
||||
void *data;
|
||||
char *path;
|
||||
const struct packed_git *p;
|
||||
const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
|
||||
? lookup_replace_object(sha1) : sha1;
|
||||
const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
|
||||
|
||||
errno = 0;
|
||||
data = read_object(repl, type, size);
|
||||
|
@ -113,7 +113,7 @@ static enum input_source istream_source(const unsigned char *sha1,
|
||||
|
||||
oi->typep = type;
|
||||
oi->sizep = &size;
|
||||
status = sha1_object_info_extended(sha1, oi);
|
||||
status = sha1_object_info_extended(sha1, oi, 0);
|
||||
if (status < 0)
|
||||
return stream_error;
|
||||
|
||||
|
@ -276,6 +276,48 @@ test_expect_success '-f option bypasses the type check' '
|
||||
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 -n "$(git replace)" &&
|
||||
git replace -d $(git replace) &&
|
||||
|
Loading…
Reference in New Issue
Block a user