]> git.ipfire.org Git - thirdparty/git.git/commitdiff
convert: provide additional metadata to filters
authorbrian m. carlson <bk2204@github.com>
Mon, 16 Mar 2020 18:05:03 +0000 (18:05 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Mar 2020 18:37:02 +0000 (11:37 -0700)
Now that we have the codebase wired up to pass any additional metadata
to filters, let's collect the additional metadata that we'd like to
pass.

The two main places we pass this metadata are checkouts and archives.
In these two situations, reading HEAD isn't a valid option, since HEAD
isn't updated for checkouts until after the working tree is written and
archives can accept an arbitrary tree.  In other situations, HEAD will
usually reflect the refname of the branch in current use.

We pass a smaller amount of data in other cases, such as git cat-file,
where we can really only logically know about the blob.

This commit updates only the parts of the checkout code where we don't
use unpack_trees.  That function and callers of it will be handled in a
future commit.

In the archive code, we leak a small amount of memory, since nothing we
pass in the archiver argument structure is freed.

Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
archive.c
archive.h
builtin/cat-file.c
builtin/checkout.c
cache.h
convert.c
convert.h
diff.c
entry.c
t/t0021/rot13-filter.pl

index d9e92cce582b6d5fce8869752618b2c9ecff7271..fb39706120cdd6b668fbc10cefad608692587d09 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -77,6 +77,11 @@ void *object_file_to_archive(const struct archiver_args *args,
 {
        void *buffer;
        const struct commit *commit = args->convert ? args->commit : NULL;
+       struct checkout_metadata meta;
+
+       init_checkout_metadata(&meta, args->refname,
+                              args->commit_oid ? args->commit_oid :
+                              (args->tree ? &args->tree->object.oid : NULL), oid);
 
        path += args->baselen;
        buffer = read_object_file(oid, type, sizep);
@@ -85,7 +90,7 @@ void *object_file_to_archive(const struct archiver_args *args,
                size_t size = 0;
 
                strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
-               convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf, NULL);
+               convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf, &meta);
                if (commit)
                        format_subst(commit, buf.buf, buf.len, &buf);
                buffer = strbuf_detach(&buf, &size);
@@ -385,16 +390,17 @@ static void parse_treeish_arg(const char **argv,
        struct tree *tree;
        const struct commit *commit;
        struct object_id oid;
+       char *ref = NULL;
 
        /* Remotes are only allowed to fetch actual refs */
        if (remote && !remote_allow_unreachable) {
-               char *ref = NULL;
                const char *colon = strchrnul(name, ':');
                int refnamelen = colon - name;
 
                if (!dwim_ref(name, refnamelen, &oid, &ref))
                        die(_("no such ref: %.*s"), refnamelen, name);
-               free(ref);
+       } else {
+               dwim_ref(name, strlen(name), &oid, &ref);
        }
 
        if (get_oid(name, &oid))
@@ -427,6 +433,7 @@ static void parse_treeish_arg(const char **argv,
 
                tree = parse_tree_indirect(&tree_oid);
        }
+       ar_args->refname = ref;
        ar_args->tree = tree;
        ar_args->commit_oid = commit_oid;
        ar_args->commit = commit;
index e60e3dd31c79f1c04b7afcae8eb5623238e1234f..3bd96bf6bba7ecaacb6b50e1f02080f95d11f17c 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -8,6 +8,7 @@ struct repository;
 
 struct archiver_args {
        struct repository *repo;
+       const char *refname;
        const char *base;
        size_t baselen;
        struct tree *tree;
index 545fed4924a6d47f0fed64f3d7967f4decc5797e..6ecc8ee6dc0ba0f904b3c48b65bdaae064e0ebcc 100644 (file)
@@ -42,7 +42,10 @@ static int filter_object(const char *path, unsigned mode,
                             oid_to_hex(oid), path);
        if ((type == OBJ_BLOB) && S_ISREG(mode)) {
                struct strbuf strbuf = STRBUF_INIT;
-               if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, NULL)) {
+               struct checkout_metadata meta;
+
+               init_checkout_metadata(&meta, NULL, NULL, oid);
+               if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
                        free(*buf);
                        *size = strbuf.len;
                        *buf = strbuf_detach(&strbuf, NULL);
index 8a28f48d6717bcb2b2a126d114e279a3ffb6c108..1bdb70d3dde486bad9dfeffda8d17f04521f0c0b 100644 (file)
@@ -92,6 +92,8 @@ struct branch_info {
        const char *name; /* The short name used */
        const char *path; /* The full name of a real branch */
        struct commit *commit; /* The named commit */
+       char *refname; /* The full name of the ref being checked out. */
+       struct object_id oid; /* The object ID of the commit being checked out. */
        /*
         * if not null the branch is detached because it's already
         * checked out in this checkout
@@ -360,6 +362,10 @@ static int checkout_worktree(const struct checkout_opts *opts,
        state.refresh_cache = 1;
        state.istate = &the_index;
 
+       init_checkout_metadata(&state.meta, info->refname,
+                              info->commit ? &info->commit->object.oid : &info->oid,
+                              NULL);
+
        enable_delayed_checkout(&state);
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
@@ -636,6 +642,13 @@ static void setup_branch_path(struct branch_info *branch)
 {
        struct strbuf buf = STRBUF_INIT;
 
+       /*
+        * If this is a ref, resolve it; otherwise, look up the OID for our
+        * expression.  Failure here is okay.
+        */
+       if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname))
+               repo_get_oid_committish(the_repository, branch->name, &branch->oid);
+
        strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
        if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
diff --git a/cache.h b/cache.h
index 37c899b53f7c3d36f1145d33f19d5c8302b911f5..9b24e5d61fec9a3eb3eb3908a9f1badef6393bca 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1679,6 +1679,7 @@ struct checkout {
        const char *base_dir;
        int base_dir_len;
        struct delayed_checkout *delayed_checkout;
+       struct checkout_metadata meta;
        unsigned force:1,
                 quiet:1,
                 not_new:1,
index 6261921cfb7728e23f78bb7d340583b52300d638..5aa87d45e3e5a03b7530fcc36d8a71581250f446 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -2006,3 +2006,25 @@ int stream_filter(struct stream_filter *filter,
 {
        return filter->vtbl->filter(filter, input, isize_p, output, osize_p);
 }
+
+void init_checkout_metadata(struct checkout_metadata *meta, const char *refname,
+                           const struct object_id *treeish,
+                           const struct object_id *blob)
+{
+       memset(meta, 0, sizeof(*meta));
+       if (refname)
+               meta->refname = refname;
+       if (treeish)
+               oidcpy(&meta->treeish, treeish);
+       if (blob)
+               oidcpy(&meta->blob, blob);
+}
+
+void clone_checkout_metadata(struct checkout_metadata *dst,
+                            const struct checkout_metadata *src,
+                            const struct object_id *blob)
+{
+       memcpy(dst, src, sizeof(*dst));
+       if (blob)
+               oidcpy(&dst->blob, blob);
+}
index 894e01c38b896f193c320d701ad0e5923c2226a8..e29d1026a6866b63b047b6838727bcb71ea6eccb 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -102,6 +102,23 @@ void convert_to_git_filter_fd(const struct index_state *istate,
 int would_convert_to_git_filter_fd(const struct index_state *istate,
                                   const char *path);
 
+/*
+ * Initialize the checkout metadata with the given values.  Any argument may be
+ * NULL if it is not applicable.  The treeish should be a commit if that is
+ * available, and a tree otherwise.
+ *
+ * The refname is not copied and must be valid for the lifetime of the struct.
+ * THe object IDs are copied.
+ */
+void init_checkout_metadata(struct checkout_metadata *meta, const char *refname,
+                           const struct object_id *treeish,
+                           const struct object_id *blob);
+
+/* Copy the metadata from src to dst, updating the blob. */
+void clone_checkout_metadata(struct checkout_metadata *dst,
+                            const struct checkout_metadata *src,
+                            const struct object_id *blob);
+
 /*
  * Reset the internal list of attributes used by convert_to_git and
  * convert_to_working_tree.
diff --git a/diff.c b/diff.c
index 12761c8017ca83ffc0b77420b61989199ceb02a6..1010d806f50dac47927f6f70125647e9bf8276ef 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4062,6 +4062,9 @@ static void prep_temp_blob(struct index_state *istate,
        struct strbuf tempfile = STRBUF_INIT;
        char *path_dup = xstrdup(path);
        const char *base = basename(path_dup);
+       struct checkout_metadata meta;
+
+       init_checkout_metadata(&meta, NULL, NULL, oid);
 
        /* Generate "XXXXXX_basename.ext" */
        strbuf_addstr(&tempfile, "XXXXXX_");
@@ -4071,7 +4074,7 @@ static void prep_temp_blob(struct index_state *istate,
        if (!temp->tempfile)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(istate, path,
-                       (const char *)blob, (size_t)size, &buf, NULL)) {
+                       (const char *)blob, (size_t)size, &buf, &meta)) {
                blob = buf.buf;
                size = buf.len;
        }
diff --git a/entry.c b/entry.c
index 4b2d9b2dadb99ef5084c34ccb9fd393fec0978e6..00b49033668160574c35555293c0c755cbafaeaa 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -264,6 +264,9 @@ static int write_entry(struct cache_entry *ce,
        size_t newsize = 0;
        struct stat st;
        const struct submodule *sub;
+       struct checkout_metadata meta;
+
+       clone_checkout_metadata(&meta, &state->meta, &ce->oid);
 
        if (ce_mode_s_ifmt == S_IFREG) {
                struct stream_filter *filter = get_stream_filter(state->istate, ce->name,
@@ -315,13 +318,13 @@ static int write_entry(struct cache_entry *ce,
                 */
                if (dco && dco->state != CE_NO_DELAY) {
                        ret = async_convert_to_working_tree(state->istate, ce->name, new_blob,
-                                                           size, &buf, NULL, dco);
+                                                           size, &buf, &meta, dco);
                        if (ret && string_list_has_string(&dco->paths, ce->name)) {
                                free(new_blob);
                                goto delayed;
                        }
                } else
-                       ret = convert_to_working_tree(state->istate, ce->name, new_blob, size, &buf, NULL);
+                       ret = convert_to_working_tree(state->istate, ce->name, new_blob, size, &buf, &meta);
 
                if (ret) {
                        free(new_blob);
index 470107248eb161b9314ceb0ab93f21f072cf86cd..c43cf433cff6622c030077641d5ae558e29582db 100644 (file)
@@ -135,7 +135,13 @@ while (1) {
                                if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
                                        $DELAY{$pathname}{"requested"} = 1;
                                }
+                       } elsif ($buffer =~ /^(ref|treeish|blob)=/) {
+                               # Do nothing.
                        } else {
+                               # In general, filters need to be graceful about
+                               # new metadata, since it's documented that we
+                               # can pass any key-value pairs, but for tests,
+                               # let's be a little stricter.
                                die "Unknown message '$buffer'";
                        }