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:
Junio C Hamano 2014-01-10 10:32:10 -08:00
commit b0504a9519
8 changed files with 141 additions and 26 deletions

View file

@ -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
----------------------------

View file

@ -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;

View file

@ -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
View file

@ -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);

View file

@ -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 */

View file

@ -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);

View file

@ -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;

View file

@ -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) &&