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