]>
Commit | Line | Data |
---|---|---|
4f6728d5 | 1 | #include "git-compat-util.h" |
ec062838 JT |
2 | #include "default.h" |
3 | #include "../commit.h" | |
4 | #include "../fetch-negotiator.h" | |
5 | #include "../prio-queue.h" | |
6 | #include "../refs.h" | |
d1cbe1e6 | 7 | #include "../repository.h" |
ec062838 JT |
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 | ||
ecb5091f | 29 | if (repo_parse_commit(the_repository, commit)) |
ec062838 JT |
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, | |
5cf88fd8 ÆAB |
40 | int flag UNUSED, |
41 | void *cb_data UNUSED) | |
ec062838 | 42 | { |
3a2a1dc1 | 43 | struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0); |
ec062838 JT |
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 | { | |
8e21ff5e HX |
59 | struct prio_queue queue = { NULL }; |
60 | ||
61 | if (!commit || (commit->object.flags & COMMON)) | |
62 | return; | |
63 | ||
64 | prio_queue_put(&queue, commit); | |
65 | if (!ancestors_only) { | |
66 | commit->object.flags |= COMMON; | |
ec062838 | 67 | |
8e21ff5e HX |
68 | if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED)) |
69 | ns->non_common_revs--; | |
70 | } | |
71 | while ((commit = prio_queue_get(&queue))) { | |
72 | struct object *o = (struct object *)commit; | |
ec062838 JT |
73 | |
74 | if (!(o->flags & SEEN)) | |
75 | rev_list_push(ns, commit, SEEN); | |
76 | else { | |
77 | struct commit_list *parents; | |
78 | ||
ec062838 | 79 | if (!o->parsed && !dont_parse) |
ecb5091f | 80 | if (repo_parse_commit(the_repository, commit)) |
8e21ff5e | 81 | continue; |
ec062838 JT |
82 | |
83 | for (parents = commit->parents; | |
84 | parents; | |
8e21ff5e HX |
85 | parents = parents->next) { |
86 | struct commit *p = parents->item; | |
87 | ||
88 | if (p->object.flags & COMMON) | |
89 | continue; | |
90 | ||
91 | p->object.flags |= COMMON; | |
92 | ||
93 | if ((p->object.flags & SEEN) && !(p->object.flags & POPPED)) | |
94 | ns->non_common_revs--; | |
95 | ||
96 | prio_queue_put(&queue, parents->item); | |
97 | } | |
ec062838 JT |
98 | } |
99 | } | |
8e21ff5e HX |
100 | |
101 | clear_prio_queue(&queue); | |
ec062838 JT |
102 | } |
103 | ||
104 | /* | |
105 | * Get the next rev to send, ignoring the common. | |
106 | */ | |
107 | static const struct object_id *get_rev(struct negotiation_state *ns) | |
108 | { | |
109 | struct commit *commit = NULL; | |
110 | ||
111 | while (commit == NULL) { | |
112 | unsigned int mark; | |
113 | struct commit_list *parents; | |
114 | ||
115 | if (ns->rev_list.nr == 0 || ns->non_common_revs == 0) | |
116 | return NULL; | |
117 | ||
118 | commit = prio_queue_get(&ns->rev_list); | |
ecb5091f | 119 | repo_parse_commit(the_repository, commit); |
ec062838 JT |
120 | parents = commit->parents; |
121 | ||
122 | commit->object.flags |= POPPED; | |
123 | if (!(commit->object.flags & COMMON)) | |
124 | ns->non_common_revs--; | |
125 | ||
126 | if (commit->object.flags & COMMON) { | |
127 | /* do not send "have", and ignore ancestors */ | |
128 | commit = NULL; | |
129 | mark = COMMON | SEEN; | |
130 | } else if (commit->object.flags & COMMON_REF) | |
131 | /* send "have", and ignore ancestors */ | |
132 | mark = COMMON | SEEN; | |
133 | else | |
134 | /* send "have", also for its ancestors */ | |
135 | mark = SEEN; | |
136 | ||
137 | while (parents) { | |
138 | if (!(parents->item->object.flags & SEEN)) | |
139 | rev_list_push(ns, parents->item, mark); | |
140 | if (mark & COMMON) | |
141 | mark_common(ns, parents->item, 1, 0); | |
142 | parents = parents->next; | |
143 | } | |
144 | } | |
145 | ||
146 | return &commit->object.oid; | |
147 | } | |
148 | ||
149 | static void known_common(struct fetch_negotiator *n, struct commit *c) | |
150 | { | |
151 | if (!(c->object.flags & SEEN)) { | |
152 | rev_list_push(n->data, c, COMMON_REF | SEEN); | |
153 | mark_common(n->data, c, 1, 1); | |
154 | } | |
155 | } | |
156 | ||
157 | static void add_tip(struct fetch_negotiator *n, struct commit *c) | |
158 | { | |
159 | n->known_common = NULL; | |
160 | rev_list_push(n->data, c, SEEN); | |
161 | } | |
162 | ||
163 | static const struct object_id *next(struct fetch_negotiator *n) | |
164 | { | |
165 | n->known_common = NULL; | |
166 | n->add_tip = NULL; | |
167 | return get_rev(n->data); | |
168 | } | |
169 | ||
170 | static int ack(struct fetch_negotiator *n, struct commit *c) | |
171 | { | |
172 | int known_to_be_common = !!(c->object.flags & COMMON); | |
173 | mark_common(n->data, c, 0, 1); | |
174 | return known_to_be_common; | |
175 | } | |
176 | ||
177 | static void release(struct fetch_negotiator *n) | |
178 | { | |
179 | clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list); | |
180 | FREE_AND_NULL(n->data); | |
181 | } | |
182 | ||
183 | void default_negotiator_init(struct fetch_negotiator *negotiator) | |
184 | { | |
185 | struct negotiation_state *ns; | |
186 | negotiator->known_common = known_common; | |
187 | negotiator->add_tip = add_tip; | |
188 | negotiator->next = next; | |
189 | negotiator->ack = ack; | |
190 | negotiator->release = release; | |
ca56dadb | 191 | negotiator->data = CALLOC_ARRAY(ns, 1); |
ec062838 JT |
192 | ns->rev_list.compare = compare_commits_by_commit_date; |
193 | ||
194 | if (marked) | |
195 | for_each_ref(clear_marks, NULL); | |
196 | marked = 1; | |
197 | } |