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 
15 static int marked;
16 
17 struct negotiation_state {
18 	struct prio_queue rev_list;
19 	int non_common_revs;
20 };
21 
rev_list_push(struct negotiation_state * ns,struct commit * commit,int mark)22 static 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 
clear_marks(const char * refname,const struct object_id * oid,int flag,void * cb_data)38 static 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  */
mark_common(struct negotiation_state * ns,struct commit * commit,int ancestors_only,int dont_parse)54 static 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  */
get_rev(struct negotiation_state * ns)86 static 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 
known_common(struct fetch_negotiator * n,struct commit * c)128 static 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 
add_tip(struct fetch_negotiator * n,struct commit * c)136 static 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 
next(struct fetch_negotiator * n)142 static 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 
ack(struct fetch_negotiator * n,struct commit * c)149 static 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 
release(struct fetch_negotiator * n)156 static 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 
default_negotiator_init(struct fetch_negotiator * negotiator)162 void 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 = CALLOC_ARRAY(ns, 1);
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 }
177