list-objects.con commit t/lib-diff-alternative: abstract away SHA-1-specific constants (d29d500)
   1#include "cache.h"
   2#include "tag.h"
   3#include "commit.h"
   4#include "tree.h"
   5#include "blob.h"
   6#include "diff.h"
   7#include "tree-walk.h"
   8#include "revision.h"
   9#include "list-objects.h"
  10#include "list-objects-filter.h"
  11#include "list-objects-filter-options.h"
  12#include "packfile.h"
  13
  14static void process_blob(struct rev_info *revs,
  15                         struct blob *blob,
  16                         show_object_fn show,
  17                         struct strbuf *path,
  18                         const char *name,
  19                         void *cb_data,
  20                         filter_object_fn filter_fn,
  21                         void *filter_data)
  22{
  23        struct object *obj = &blob->object;
  24        size_t pathlen;
  25        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
  26
  27        if (!revs->blob_objects)
  28                return;
  29        if (!obj)
  30                die("bad blob object");
  31        if (obj->flags & (UNINTERESTING | SEEN))
  32                return;
  33
  34        /*
  35         * Pre-filter known-missing objects when explicitly requested.
  36         * Otherwise, a missing object error message may be reported
  37         * later (depending on other filtering criteria).
  38         *
  39         * Note that this "--exclude-promisor-objects" pre-filtering
  40         * may cause the actual filter to report an incomplete list
  41         * of missing objects.
  42         */
  43        if (revs->exclude_promisor_objects &&
  44            !has_object_file(&obj->oid) &&
  45            is_promisor_object(&obj->oid))
  46                return;
  47
  48        pathlen = path->len;
  49        strbuf_addstr(path, name);
  50        if (filter_fn)
  51                r = filter_fn(LOFS_BLOB, obj,
  52                              path->buf, &path->buf[pathlen],
  53                              filter_data);
  54        if (r & LOFR_MARK_SEEN)
  55                obj->flags |= SEEN;
  56        if (r & LOFR_DO_SHOW)
  57                show(obj, path->buf, cb_data);
  58        strbuf_setlen(path, pathlen);
  59}
  60
  61/*
  62 * Processing a gitlink entry currently does nothing, since
  63 * we do not recurse into the subproject.
  64 *
  65 * We *could* eventually add a flag that actually does that,
  66 * which would involve:
  67 *  - is the subproject actually checked out?
  68 *  - if so, see if the subproject has already been added
  69 *    to the alternates list, and add it if not.
  70 *  - process the commit (or tag) the gitlink points to
  71 *    recursively.
  72 *
  73 * However, it's unclear whether there is really ever any
  74 * reason to see superprojects and subprojects as such a
  75 * "unified" object pool (potentially resulting in a totally
  76 * humongous pack - avoiding which was the whole point of
  77 * having gitlinks in the first place!).
  78 *
  79 * So for now, there is just a note that we *could* follow
  80 * the link, and how to do it. Whether it necessarily makes
  81 * any sense what-so-ever to ever do that is another issue.
  82 */
  83static void process_gitlink(struct rev_info *revs,
  84                            const unsigned char *sha1,
  85                            show_object_fn show,
  86                            struct strbuf *path,
  87                            const char *name,
  88                            void *cb_data)
  89{
  90        /* Nothing to do */
  91}
  92
  93static void process_tree(struct rev_info *revs,
  94                         struct tree *tree,
  95                         show_object_fn show,
  96                         struct strbuf *base,
  97                         const char *name,
  98                         void *cb_data,
  99                         filter_object_fn filter_fn,
 100                         void *filter_data)
 101{
 102        struct object *obj = &tree->object;
 103        struct tree_desc desc;
 104        struct name_entry entry;
 105        enum interesting match = revs->diffopt.pathspec.nr == 0 ?
 106                all_entries_interesting: entry_not_interesting;
 107        int baselen = base->len;
 108        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
 109        int gently = revs->ignore_missing_links ||
 110                     revs->exclude_promisor_objects;
 111
 112        if (!revs->tree_objects)
 113                return;
 114        if (!obj)
 115                die("bad tree object");
 116        if (obj->flags & (UNINTERESTING | SEEN))
 117                return;
 118        if (parse_tree_gently(tree, gently) < 0) {
 119                if (revs->ignore_missing_links)
 120                        return;
 121
 122                /*
 123                 * Pre-filter known-missing tree objects when explicitly
 124                 * requested.  This may cause the actual filter to report
 125                 * an incomplete list of missing objects.
 126                 */
 127                if (revs->exclude_promisor_objects &&
 128                    is_promisor_object(&obj->oid))
 129                        return;
 130
 131                die("bad tree object %s", oid_to_hex(&obj->oid));
 132        }
 133
 134        strbuf_addstr(base, name);
 135        if (filter_fn)
 136                r = filter_fn(LOFS_BEGIN_TREE, obj,
 137                              base->buf, &base->buf[baselen],
 138                              filter_data);
 139        if (r & LOFR_MARK_SEEN)
 140                obj->flags |= SEEN;
 141        if (r & LOFR_DO_SHOW)
 142                show(obj, base->buf, cb_data);
 143        if (base->len)
 144                strbuf_addch(base, '/');
 145
 146        init_tree_desc(&desc, tree->buffer, tree->size);
 147
 148        while (tree_entry(&desc, &entry)) {
 149                if (match != all_entries_interesting) {
 150                        match = tree_entry_interesting(&entry, base, 0,
 151                                                       &revs->diffopt.pathspec);
 152                        if (match == all_entries_not_interesting)
 153                                break;
 154                        if (match == entry_not_interesting)
 155                                continue;
 156                }
 157
 158                if (S_ISDIR(entry.mode))
 159                        process_tree(revs,
 160                                     lookup_tree(entry.oid),
 161                                     show, base, entry.path,
 162                                     cb_data, filter_fn, filter_data);
 163                else if (S_ISGITLINK(entry.mode))
 164                        process_gitlink(revs, entry.oid->hash,
 165                                        show, base, entry.path,
 166                                        cb_data);
 167                else
 168                        process_blob(revs,
 169                                     lookup_blob(entry.oid),
 170                                     show, base, entry.path,
 171                                     cb_data, filter_fn, filter_data);
 172        }
 173
 174        if (filter_fn) {
 175                r = filter_fn(LOFS_END_TREE, obj,
 176                              base->buf, &base->buf[baselen],
 177                              filter_data);
 178                if (r & LOFR_MARK_SEEN)
 179                        obj->flags |= SEEN;
 180                if (r & LOFR_DO_SHOW)
 181                        show(obj, base->buf, cb_data);
 182        }
 183
 184        strbuf_setlen(base, baselen);
 185        free_tree_buffer(tree);
 186}
 187
 188static void mark_edge_parents_uninteresting(struct commit *commit,
 189                                            struct rev_info *revs,
 190                                            show_edge_fn show_edge)
 191{
 192        struct commit_list *parents;
 193
 194        for (parents = commit->parents; parents; parents = parents->next) {
 195                struct commit *parent = parents->item;
 196                if (!(parent->object.flags & UNINTERESTING))
 197                        continue;
 198                mark_tree_uninteresting(parent->tree);
 199                if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
 200                        parent->object.flags |= SHOWN;
 201                        show_edge(parent);
 202                }
 203        }
 204}
 205
 206void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
 207{
 208        struct commit_list *list;
 209        int i;
 210
 211        for (list = revs->commits; list; list = list->next) {
 212                struct commit *commit = list->item;
 213
 214                if (commit->object.flags & UNINTERESTING) {
 215                        mark_tree_uninteresting(commit->tree);
 216                        if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
 217                                commit->object.flags |= SHOWN;
 218                                show_edge(commit);
 219                        }
 220                        continue;
 221                }
 222                mark_edge_parents_uninteresting(commit, revs, show_edge);
 223        }
 224        if (revs->edge_hint_aggressive) {
 225                for (i = 0; i < revs->cmdline.nr; i++) {
 226                        struct object *obj = revs->cmdline.rev[i].item;
 227                        struct commit *commit = (struct commit *)obj;
 228                        if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
 229                                continue;
 230                        mark_tree_uninteresting(commit->tree);
 231                        if (!(obj->flags & SHOWN)) {
 232                                obj->flags |= SHOWN;
 233                                show_edge(commit);
 234                        }
 235                }
 236        }
 237}
 238
 239static void add_pending_tree(struct rev_info *revs, struct tree *tree)
 240{
 241        add_pending_object(revs, &tree->object, "");
 242}
 243
 244static void traverse_trees_and_blobs(struct rev_info *revs,
 245                                     struct strbuf *base,
 246                                     show_object_fn show_object,
 247                                     void *show_data,
 248                                     filter_object_fn filter_fn,
 249                                     void *filter_data)
 250{
 251        int i;
 252
 253        assert(base->len == 0);
 254
 255        for (i = 0; i < revs->pending.nr; i++) {
 256                struct object_array_entry *pending = revs->pending.objects + i;
 257                struct object *obj = pending->item;
 258                const char *name = pending->name;
 259                const char *path = pending->path;
 260                if (obj->flags & (UNINTERESTING | SEEN))
 261                        continue;
 262                if (obj->type == OBJ_TAG) {
 263                        obj->flags |= SEEN;
 264                        show_object(obj, name, show_data);
 265                        continue;
 266                }
 267                if (!path)
 268                        path = "";
 269                if (obj->type == OBJ_TREE) {
 270                        process_tree(revs, (struct tree *)obj, show_object,
 271                                     base, path, show_data,
 272                                     filter_fn, filter_data);
 273                        continue;
 274                }
 275                if (obj->type == OBJ_BLOB) {
 276                        process_blob(revs, (struct blob *)obj, show_object,
 277                                     base, path, show_data,
 278                                     filter_fn, filter_data);
 279                        continue;
 280                }
 281                die("unknown pending object %s (%s)",
 282                    oid_to_hex(&obj->oid), name);
 283        }
 284        object_array_clear(&revs->pending);
 285}
 286
 287static void do_traverse(struct rev_info *revs,
 288                        show_commit_fn show_commit,
 289                        show_object_fn show_object,
 290                        void *show_data,
 291                        filter_object_fn filter_fn,
 292                        void *filter_data)
 293{
 294        struct commit *commit;
 295        struct strbuf csp; /* callee's scratch pad */
 296        strbuf_init(&csp, PATH_MAX);
 297
 298        while ((commit = get_revision(revs)) != NULL) {
 299                /*
 300                 * an uninteresting boundary commit may not have its tree
 301                 * parsed yet, but we are not going to show them anyway
 302                 */
 303                if (commit->tree)
 304                        add_pending_tree(revs, commit->tree);
 305                show_commit(commit, show_data);
 306
 307                if (revs->tree_blobs_in_commit_order)
 308                        /*
 309                         * NEEDSWORK: Adding the tree and then flushing it here
 310                         * needs a reallocation for each commit. Can we pass the
 311                         * tree directory without allocation churn?
 312                         */
 313                        traverse_trees_and_blobs(revs, &csp,
 314                                                 show_object, show_data,
 315                                                 filter_fn, filter_data);
 316        }
 317        traverse_trees_and_blobs(revs, &csp,
 318                                 show_object, show_data,
 319                                 filter_fn, filter_data);
 320        strbuf_release(&csp);
 321}
 322
 323void traverse_commit_list(struct rev_info *revs,
 324                          show_commit_fn show_commit,
 325                          show_object_fn show_object,
 326                          void *show_data)
 327{
 328        do_traverse(revs, show_commit, show_object, show_data, NULL, NULL);
 329}
 330
 331void traverse_commit_list_filtered(
 332        struct list_objects_filter_options *filter_options,
 333        struct rev_info *revs,
 334        show_commit_fn show_commit,
 335        show_object_fn show_object,
 336        void *show_data,
 337        struct oidset *omitted)
 338{
 339        filter_object_fn filter_fn = NULL;
 340        filter_free_fn filter_free_fn = NULL;
 341        void *filter_data = NULL;
 342
 343        filter_data = list_objects_filter__init(omitted, filter_options,
 344                                                &filter_fn, &filter_free_fn);
 345        do_traverse(revs, show_commit, show_object, show_data,
 346                    filter_fn, filter_data);
 347        if (filter_data && filter_free_fn)
 348                filter_free_fn(filter_data);
 349}