Add a parameter to each_ref_fn so that callers to the ref APIs that use this function as a callback can have acess to the unresolved value of a symbolic ref. Signed-off-by: John Cai <johncai86@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			201 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#define USE_THE_REPOSITORY_VARIABLE
 | 
						|
 | 
						|
#include "git-compat-util.h"
 | 
						|
#include "default.h"
 | 
						|
#include "../commit.h"
 | 
						|
#include "../fetch-negotiator.h"
 | 
						|
#include "../prio-queue.h"
 | 
						|
#include "../refs.h"
 | 
						|
#include "../repository.h"
 | 
						|
#include "../tag.h"
 | 
						|
 | 
						|
/* Remember to update object flag allocation in object.h */
 | 
						|
#define COMMON		(1U << 2)
 | 
						|
#define COMMON_REF	(1U << 3)
 | 
						|
#define SEEN		(1U << 4)
 | 
						|
#define POPPED		(1U << 5)
 | 
						|
 | 
						|
static int marked;
 | 
						|
 | 
						|
struct negotiation_state {
 | 
						|
	struct prio_queue rev_list;
 | 
						|
	int non_common_revs;
 | 
						|
};
 | 
						|
 | 
						|
static void rev_list_push(struct negotiation_state *ns,
 | 
						|
			  struct commit *commit, int mark)
 | 
						|
{
 | 
						|
	if (!(commit->object.flags & mark)) {
 | 
						|
		commit->object.flags |= mark;
 | 
						|
 | 
						|
		if (repo_parse_commit(the_repository, commit))
 | 
						|
			return;
 | 
						|
 | 
						|
		prio_queue_put(&ns->rev_list, commit);
 | 
						|
 | 
						|
		if (!(commit->object.flags & COMMON))
 | 
						|
			ns->non_common_revs++;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
 | 
						|
		       int flag UNUSED,
 | 
						|
		       void *cb_data UNUSED)
 | 
						|
{
 | 
						|
	struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
 | 
						|
 | 
						|
	if (o && o->type == OBJ_COMMIT)
 | 
						|
		clear_commit_marks((struct commit *)o,
 | 
						|
				   COMMON | COMMON_REF | SEEN | POPPED);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * This function marks a rev and its ancestors as common.
 | 
						|
 * In some cases, it is desirable to mark only the ancestors (for example
 | 
						|
 * when only the server does not yet know that they are common).
 | 
						|
 */
 | 
						|
static void mark_common(struct negotiation_state *ns, struct commit *commit,
 | 
						|
		int ancestors_only, int dont_parse)
 | 
						|
{
 | 
						|
	struct prio_queue queue = { NULL };
 | 
						|
 | 
						|
	if (!commit || (commit->object.flags & COMMON))
 | 
						|
		return;
 | 
						|
 | 
						|
	prio_queue_put(&queue, commit);
 | 
						|
	if (!ancestors_only) {
 | 
						|
		commit->object.flags |= COMMON;
 | 
						|
 | 
						|
		if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED))
 | 
						|
			ns->non_common_revs--;
 | 
						|
	}
 | 
						|
	while ((commit = prio_queue_get(&queue))) {
 | 
						|
		struct object *o = (struct object *)commit;
 | 
						|
 | 
						|
		if (!(o->flags & SEEN))
 | 
						|
			rev_list_push(ns, commit, SEEN);
 | 
						|
		else {
 | 
						|
			struct commit_list *parents;
 | 
						|
 | 
						|
			if (!o->parsed && !dont_parse)
 | 
						|
				if (repo_parse_commit(the_repository, commit))
 | 
						|
					continue;
 | 
						|
 | 
						|
			for (parents = commit->parents;
 | 
						|
					parents;
 | 
						|
					parents = parents->next) {
 | 
						|
				struct commit *p = parents->item;
 | 
						|
 | 
						|
				if (p->object.flags & COMMON)
 | 
						|
					continue;
 | 
						|
 | 
						|
				p->object.flags |= COMMON;
 | 
						|
 | 
						|
				if ((p->object.flags & SEEN) && !(p->object.flags & POPPED))
 | 
						|
					ns->non_common_revs--;
 | 
						|
 | 
						|
				prio_queue_put(&queue, parents->item);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	clear_prio_queue(&queue);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Get the next rev to send, ignoring the common.
 | 
						|
 */
 | 
						|
static const struct object_id *get_rev(struct negotiation_state *ns)
 | 
						|
{
 | 
						|
	struct commit *commit = NULL;
 | 
						|
 | 
						|
	while (commit == NULL) {
 | 
						|
		unsigned int mark;
 | 
						|
		struct commit_list *parents;
 | 
						|
 | 
						|
		if (ns->rev_list.nr == 0 || ns->non_common_revs == 0)
 | 
						|
			return NULL;
 | 
						|
 | 
						|
		commit = prio_queue_get(&ns->rev_list);
 | 
						|
		repo_parse_commit(the_repository, commit);
 | 
						|
		parents = commit->parents;
 | 
						|
 | 
						|
		commit->object.flags |= POPPED;
 | 
						|
		if (!(commit->object.flags & COMMON))
 | 
						|
			ns->non_common_revs--;
 | 
						|
 | 
						|
		if (commit->object.flags & COMMON) {
 | 
						|
			/* do not send "have", and ignore ancestors */
 | 
						|
			commit = NULL;
 | 
						|
			mark = COMMON | SEEN;
 | 
						|
		} else if (commit->object.flags & COMMON_REF)
 | 
						|
			/* send "have", and ignore ancestors */
 | 
						|
			mark = COMMON | SEEN;
 | 
						|
		else
 | 
						|
			/* send "have", also for its ancestors */
 | 
						|
			mark = SEEN;
 | 
						|
 | 
						|
		while (parents) {
 | 
						|
			if (!(parents->item->object.flags & SEEN))
 | 
						|
				rev_list_push(ns, parents->item, mark);
 | 
						|
			if (mark & COMMON)
 | 
						|
				mark_common(ns, parents->item, 1, 0);
 | 
						|
			parents = parents->next;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return &commit->object.oid;
 | 
						|
}
 | 
						|
 | 
						|
static void known_common(struct fetch_negotiator *n, struct commit *c)
 | 
						|
{
 | 
						|
	if (!(c->object.flags & SEEN)) {
 | 
						|
		rev_list_push(n->data, c, COMMON_REF | SEEN);
 | 
						|
		mark_common(n->data, c, 1, 1);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void add_tip(struct fetch_negotiator *n, struct commit *c)
 | 
						|
{
 | 
						|
	n->known_common = NULL;
 | 
						|
	rev_list_push(n->data, c, SEEN);
 | 
						|
}
 | 
						|
 | 
						|
static const struct object_id *next(struct fetch_negotiator *n)
 | 
						|
{
 | 
						|
	n->known_common = NULL;
 | 
						|
	n->add_tip = NULL;
 | 
						|
	return get_rev(n->data);
 | 
						|
}
 | 
						|
 | 
						|
static int ack(struct fetch_negotiator *n, struct commit *c)
 | 
						|
{
 | 
						|
	int known_to_be_common = !!(c->object.flags & COMMON);
 | 
						|
	mark_common(n->data, c, 0, 1);
 | 
						|
	return known_to_be_common;
 | 
						|
}
 | 
						|
 | 
						|
static void release(struct fetch_negotiator *n)
 | 
						|
{
 | 
						|
	clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
 | 
						|
	FREE_AND_NULL(n->data);
 | 
						|
}
 | 
						|
 | 
						|
void default_negotiator_init(struct fetch_negotiator *negotiator)
 | 
						|
{
 | 
						|
	struct negotiation_state *ns;
 | 
						|
	negotiator->known_common = known_common;
 | 
						|
	negotiator->add_tip = add_tip;
 | 
						|
	negotiator->next = next;
 | 
						|
	negotiator->ack = ack;
 | 
						|
	negotiator->release = release;
 | 
						|
	negotiator->data = CALLOC_ARRAY(ns, 1);
 | 
						|
	ns->rev_list.compare = compare_commits_by_commit_date;
 | 
						|
 | 
						|
	if (marked)
 | 
						|
		refs_for_each_ref(get_main_ref_store(the_repository),
 | 
						|
				  clear_marks, NULL);
 | 
						|
	marked = 1;
 | 
						|
}
 |