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