negotiator / default.con commit Merge branch 'js/diff-notice-has-drive-prefix' into maint (45dda3a)
   1#include "cache.h"
   2#include "default.h"
   3#include "../commit.h"
   4#include "../fetch-negotiator.h"
   5#include "../prio-queue.h"
   6#include "../refs.h"
   7#include "../tag.h"
   8
   9/* Remember to update object flag allocation in object.h */
  10#define COMMON          (1U << 2)
  11#define COMMON_REF      (1U << 3)
  12#define SEEN            (1U << 4)
  13#define POPPED          (1U << 5)
  14
  15static int marked;
  16
  17struct negotiation_state {
  18        struct prio_queue rev_list;
  19        int non_common_revs;
  20};
  21
  22static void rev_list_push(struct negotiation_state *ns,
  23                          struct commit *commit, int mark)
  24{
  25        if (!(commit->object.flags & mark)) {
  26                commit->object.flags |= mark;
  27
  28                if (parse_commit(commit))
  29                        return;
  30
  31                prio_queue_put(&ns->rev_list, commit);
  32
  33                if (!(commit->object.flags & COMMON))
  34                        ns->non_common_revs++;
  35        }
  36}
  37
  38static int clear_marks(const char *refname, const struct object_id *oid,
  39                       int flag, void *cb_data)
  40{
  41        struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
  42
  43        if (o && o->type == OBJ_COMMIT)
  44                clear_commit_marks((struct commit *)o,
  45                                   COMMON | COMMON_REF | SEEN | POPPED);
  46        return 0;
  47}
  48
  49/*
  50 * This function marks a rev and its ancestors as common.
  51 * In some cases, it is desirable to mark only the ancestors (for example
  52 * when only the server does not yet know that they are common).
  53 */
  54static void mark_common(struct negotiation_state *ns, struct commit *commit,
  55                int ancestors_only, int dont_parse)
  56{
  57        if (commit != NULL && !(commit->object.flags & COMMON)) {
  58                struct object *o = (struct object *)commit;
  59
  60                if (!ancestors_only)
  61                        o->flags |= COMMON;
  62
  63                if (!(o->flags & SEEN))
  64                        rev_list_push(ns, commit, SEEN);
  65                else {
  66                        struct commit_list *parents;
  67
  68                        if (!ancestors_only && !(o->flags & POPPED))
  69                                ns->non_common_revs--;
  70                        if (!o->parsed && !dont_parse)
  71                                if (parse_commit(commit))
  72                                        return;
  73
  74                        for (parents = commit->parents;
  75                                        parents;
  76                                        parents = parents->next)
  77                                mark_common(ns, parents->item, 0,
  78                                            dont_parse);
  79                }
  80        }
  81}
  82
  83/*
  84 * Get the next rev to send, ignoring the common.
  85 */
  86static const struct object_id *get_rev(struct negotiation_state *ns)
  87{
  88        struct commit *commit = NULL;
  89
  90        while (commit == NULL) {
  91                unsigned int mark;
  92                struct commit_list *parents;
  93
  94                if (ns->rev_list.nr == 0 || ns->non_common_revs == 0)
  95                        return NULL;
  96
  97                commit = prio_queue_get(&ns->rev_list);
  98                parse_commit(commit);
  99                parents = commit->parents;
 100
 101                commit->object.flags |= POPPED;
 102                if (!(commit->object.flags & COMMON))
 103                        ns->non_common_revs--;
 104
 105                if (commit->object.flags & COMMON) {
 106                        /* do not send "have", and ignore ancestors */
 107                        commit = NULL;
 108                        mark = COMMON | SEEN;
 109                } else if (commit->object.flags & COMMON_REF)
 110                        /* send "have", and ignore ancestors */
 111                        mark = COMMON | SEEN;
 112                else
 113                        /* send "have", also for its ancestors */
 114                        mark = SEEN;
 115
 116                while (parents) {
 117                        if (!(parents->item->object.flags & SEEN))
 118                                rev_list_push(ns, parents->item, mark);
 119                        if (mark & COMMON)
 120                                mark_common(ns, parents->item, 1, 0);
 121                        parents = parents->next;
 122                }
 123        }
 124
 125        return &commit->object.oid;
 126}
 127
 128static void known_common(struct fetch_negotiator *n, struct commit *c)
 129{
 130        if (!(c->object.flags & SEEN)) {
 131                rev_list_push(n->data, c, COMMON_REF | SEEN);
 132                mark_common(n->data, c, 1, 1);
 133        }
 134}
 135
 136static void add_tip(struct fetch_negotiator *n, struct commit *c)
 137{
 138        n->known_common = NULL;
 139        rev_list_push(n->data, c, SEEN);
 140}
 141
 142static const struct object_id *next(struct fetch_negotiator *n)
 143{
 144        n->known_common = NULL;
 145        n->add_tip = NULL;
 146        return get_rev(n->data);
 147}
 148
 149static int ack(struct fetch_negotiator *n, struct commit *c)
 150{
 151        int known_to_be_common = !!(c->object.flags & COMMON);
 152        mark_common(n->data, c, 0, 1);
 153        return known_to_be_common;
 154}
 155
 156static void release(struct fetch_negotiator *n)
 157{
 158        clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
 159        FREE_AND_NULL(n->data);
 160}
 161
 162void default_negotiator_init(struct fetch_negotiator *negotiator)
 163{
 164        struct negotiation_state *ns;
 165        negotiator->known_common = known_common;
 166        negotiator->add_tip = add_tip;
 167        negotiator->next = next;
 168        negotiator->ack = ack;
 169        negotiator->release = release;
 170        negotiator->data = ns = xcalloc(1, sizeof(*ns));
 171        ns->rev_list.compare = compare_commits_by_commit_date;
 172
 173        if (marked)
 174                for_each_ref(clear_marks, NULL);
 175        marked = 1;
 176}