Merge branch 'zk/difftool-counts'
[gitweb.git] / builtin / cat-file.c
index 425346048bbba66874eb857985bed9937c44e1d6..f8288c830cad215af63dd6d575c9bd4bf7aeaf6c 100644 (file)
@@ -45,6 +45,14 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
        case 'e':
                return !has_sha1_file(sha1);
 
+       case 'c':
+               if (!obj_context.path[0])
+                       die("git cat-file --textconv %s: <object> must be <sha1:path>",
+                           obj_name);
+
+               if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
+                       break;
+
        case 'p':
                type = sha1_object_info(sha1, NULL);
                if (type < 0)
@@ -67,16 +75,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                /* otherwise just spit out the data */
                break;
 
-       case 'c':
-               if (!obj_context.path[0])
-                       die("git cat-file --textconv %s: <object> must be <sha1:path>",
-                           obj_name);
-
-               if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
-                       die("git cat-file --textconv: unable to run textconv on %s",
-                           obj_name);
-               break;
-
        case 0:
                if (type_from_string(exp_type) == OBJ_BLOB) {
                        unsigned char blob_sha1[20];
@@ -119,6 +117,7 @@ struct expand_data {
        enum object_type type;
        unsigned long size;
        unsigned long disk_size;
+       const char *rest;
 
        /*
         * If mark_query is true, we do not expand anything, but rather
@@ -126,6 +125,13 @@ struct expand_data {
         */
        int mark_query;
 
+       /*
+        * Whether to split the input on whitespace before feeding it to
+        * get_sha1; this is decided during the mark_query phase based on
+        * whether we have a %(rest) token in our format.
+        */
+       int split_on_whitespace;
+
        /*
         * After a mark_query run, this object_info is set up to be
         * passed to sha1_object_info_extended. It will point to the data
@@ -163,6 +169,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                        data->info.disk_sizep = &data->disk_size;
                else
                        strbuf_addf(sb, "%lu", data->disk_size);
+       } else if (is_atom("rest", atom, len)) {
+               if (data->mark_query)
+                       data->split_on_whitespace = 1;
+               else if (data->rest)
+                       strbuf_addstr(sb, data->rest);
        } else
                die("unknown format element: %.*s", len, atom);
 }
@@ -182,25 +193,28 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data)
        return end - start + 1;
 }
 
-static void print_object_or_die(int fd, const unsigned char *sha1,
-                               enum object_type type, unsigned long size)
+static void print_object_or_die(int fd, struct expand_data *data)
 {
-       if (type == OBJ_BLOB) {
+       const unsigned char *sha1 = data->sha1;
+
+       assert(data->info.typep);
+
+       if (data->type == OBJ_BLOB) {
                if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
                        die("unable to stream %s to stdout", sha1_to_hex(sha1));
        }
        else {
-               enum object_type rtype;
-               unsigned long rsize;
+               enum object_type type;
+               unsigned long size;
                void *contents;
 
-               contents = read_sha1_file(sha1, &rtype, &rsize);
+               contents = read_sha1_file(sha1, &type, &size);
                if (!contents)
                        die("object %s disappeared", sha1_to_hex(sha1));
-               if (rtype != type)
+               if (type != data->type)
                        die("object %s changed type!?", sha1_to_hex(sha1));
-               if (rsize != size)
-                       die("object %s change size!?", sha1_to_hex(sha1));
+               if (data->info.sizep && size != data->size)
+                       die("object %s changed size!?", sha1_to_hex(sha1));
 
                write_or_die(fd, contents, size);
                free(contents);
@@ -239,7 +253,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
        strbuf_release(&buf);
 
        if (opt->print_contents) {
-               print_object_or_die(1, data->sha1, data->type, data->size);
+               print_object_or_die(1, data);
                write_or_die(1, "\n", 1);
        }
        return 0;
@@ -263,6 +277,13 @@ static int batch_objects(struct batch_options *opt)
        strbuf_expand(&buf, opt->format, expand_format, &data);
        data.mark_query = 0;
 
+       /*
+        * If we are printing out the object, then always fill in the type,
+        * since we will want to decide whether or not to stream.
+        */
+       if (opt->print_contents)
+               data.info.typep = &data.type;
+
        /*
         * We are going to call get_sha1 on a potentially very large number of
         * objects. In most large cases, these will be actual object sha1s. The
@@ -273,7 +294,23 @@ static int batch_objects(struct batch_options *opt)
        warn_on_object_refname_ambiguity = 0;
 
        while (strbuf_getline(&buf, stdin, '\n') != EOF) {
-               int error = batch_one_object(buf.buf, opt, &data);
+               int error;
+
+               if (data.split_on_whitespace) {
+                       /*
+                        * Split at first whitespace, tying off the beginning
+                        * of the string and saving the remainder (or NULL) in
+                        * data.rest.
+                        */
+                       char *p = strpbrk(buf.buf, " \t");
+                       if (p) {
+                               while (*p && strchr(" \t", *p))
+                                       *p++ = '\0';
+                       }
+                       data.rest = p;
+               }
+
+               error = batch_one_object(buf.buf, opt, &data);
                if (error)
                        return error;
        }