mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 08:48:25 +00:00 
			
		
		
		
	This describes their purpose better, as we now initialize ssl and some other global stuff in there. Calling the init function is not something which has been optional for a while now.
		
			
				
	
	
		
			320 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * libgit2 "tag" example - shows how to list, create and delete tags
 | 
						|
 *
 | 
						|
 * Written by the libgit2 contributors
 | 
						|
 *
 | 
						|
 * To the extent possible under law, the author(s) have dedicated all copyright
 | 
						|
 * and related and neighboring rights to this software to the public domain
 | 
						|
 * worldwide. This software is distributed without any warranty.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the CC0 Public Domain Dedication along
 | 
						|
 * with this software. If not, see
 | 
						|
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "common.h"
 | 
						|
 | 
						|
/**
 | 
						|
 * The following example partially reimplements the `git tag` command
 | 
						|
 * and some of its options.
 | 
						|
 *
 | 
						|
 * These commands should work:
 | 
						|
 | 
						|
 * - Tag name listing (`tag`)
 | 
						|
 * - Filtered tag listing with messages (`tag -n3 -l "v0.1*"`)
 | 
						|
 * - Lightweight tag creation (`tag test v0.18.0`)
 | 
						|
 * - Tag creation (`tag -a -m "Test message" test v0.18.0`)
 | 
						|
 * - Tag deletion (`tag -d test`)
 | 
						|
 *
 | 
						|
 * The command line parsing logic is simplified and doesn't handle
 | 
						|
 * all of the use cases.
 | 
						|
 */
 | 
						|
 | 
						|
/** tag_options represents the parsed command line options */
 | 
						|
typedef struct {
 | 
						|
	const char *message;
 | 
						|
	const char *pattern;
 | 
						|
	const char *tag_name;
 | 
						|
	const char *target;
 | 
						|
	int num_lines;
 | 
						|
	int force;
 | 
						|
} tag_options;
 | 
						|
 | 
						|
/** tag_state represents the current program state for dragging around */
 | 
						|
typedef struct {
 | 
						|
	git_repository *repo;
 | 
						|
	tag_options *opts;
 | 
						|
} tag_state;
 | 
						|
 | 
						|
/** An action to execute based on the command line arguments */
 | 
						|
typedef void (*tag_action)(tag_state *state);
 | 
						|
typedef struct args_info args_info;
 | 
						|
 | 
						|
static void check(int result, const char *message)
 | 
						|
{
 | 
						|
	if (result) fatal(message, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/** Tag listing: Print individual message lines */
 | 
						|
static void print_list_lines(const char *message, const tag_state *state)
 | 
						|
{
 | 
						|
	const char *msg = message;
 | 
						|
	int num = state->opts->num_lines - 1;
 | 
						|
 | 
						|
	if (!msg) return;
 | 
						|
 | 
						|
	/** first line - headline */
 | 
						|
	while(*msg && *msg != '\n') printf("%c", *msg++);
 | 
						|
 | 
						|
	/** skip over new lines */
 | 
						|
	while(*msg && *msg == '\n') msg++;
 | 
						|
 | 
						|
	printf("\n");
 | 
						|
 | 
						|
	/** print just headline? */
 | 
						|
	if (num == 0) return;
 | 
						|
	if (*msg && msg[1]) printf("\n");
 | 
						|
 | 
						|
	/** print individual commit/tag lines */
 | 
						|
	while (*msg && num-- >= 2) {
 | 
						|
		printf("    ");
 | 
						|
 | 
						|
		while (*msg && *msg != '\n') printf("%c", *msg++);
 | 
						|
 | 
						|
		/** handle consecutive new lines */
 | 
						|
		if (*msg && *msg == '\n' && msg[1] == '\n') {
 | 
						|
			num--;
 | 
						|
			printf("\n");
 | 
						|
		}
 | 
						|
		while(*msg && *msg == '\n') msg++;
 | 
						|
 | 
						|
		printf("\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/** Tag listing: Print an actual tag object */
 | 
						|
static void print_tag(git_tag *tag, const tag_state *state)
 | 
						|
{
 | 
						|
	printf("%-16s", git_tag_name(tag));
 | 
						|
 | 
						|
	if (state->opts->num_lines) {
 | 
						|
		const char *msg = git_tag_message(tag);
 | 
						|
		print_list_lines(msg, state);
 | 
						|
	} else {
 | 
						|
		printf("\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/** Tag listing: Print a commit (target of a lightweight tag) */
 | 
						|
static void print_commit(git_commit *commit, const char *name,
 | 
						|
		const tag_state *state)
 | 
						|
{
 | 
						|
	printf("%-16s", name);
 | 
						|
 | 
						|
	if (state->opts->num_lines) {
 | 
						|
		const char *msg = git_commit_message(commit);
 | 
						|
		print_list_lines(msg, state);
 | 
						|
	} else {
 | 
						|
		printf("\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/** Tag listing: Fallback, should not happen */
 | 
						|
static void print_name(const char *name)
 | 
						|
{
 | 
						|
	printf("%s\n", name);
 | 
						|
}
 | 
						|
 | 
						|
/** Tag listing: Lookup tags based on ref name and dispatch to print */
 | 
						|
static int each_tag(const char *name, tag_state *state)
 | 
						|
{
 | 
						|
	git_repository *repo = state->repo;
 | 
						|
	git_object *obj;
 | 
						|
 | 
						|
	check_lg2(git_revparse_single(&obj, repo, name),
 | 
						|
			"Failed to lookup rev", name);
 | 
						|
 | 
						|
	switch (git_object_type(obj)) {
 | 
						|
		case GIT_OBJ_TAG:
 | 
						|
			print_tag((git_tag *) obj, state);
 | 
						|
			break;
 | 
						|
		case GIT_OBJ_COMMIT:
 | 
						|
			print_commit((git_commit *) obj, name, state);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			print_name(name);
 | 
						|
	}
 | 
						|
 | 
						|
	git_object_free(obj);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void action_list_tags(tag_state *state)
 | 
						|
{
 | 
						|
	const char *pattern = state->opts->pattern;
 | 
						|
	git_strarray tag_names = {0};
 | 
						|
	size_t i;
 | 
						|
 | 
						|
	check_lg2(git_tag_list_match(&tag_names, pattern ? pattern : "*", state->repo),
 | 
						|
			"Unable to get list of tags", NULL);
 | 
						|
 | 
						|
	for(i = 0; i < tag_names.count; i++) {
 | 
						|
		each_tag(tag_names.strings[i], state);
 | 
						|
	}
 | 
						|
 | 
						|
	git_strarray_free(&tag_names);
 | 
						|
}
 | 
						|
 | 
						|
static void action_delete_tag(tag_state *state)
 | 
						|
{
 | 
						|
	tag_options *opts = state->opts;
 | 
						|
	git_object *obj;
 | 
						|
	git_buf abbrev_oid = {0};
 | 
						|
 | 
						|
	check(!opts->tag_name, "Name required");
 | 
						|
 | 
						|
	check_lg2(git_revparse_single(&obj, state->repo, opts->tag_name),
 | 
						|
			"Failed to lookup rev", opts->tag_name);
 | 
						|
 | 
						|
	check_lg2(git_object_short_id(&abbrev_oid, obj),
 | 
						|
			"Unable to get abbreviated OID", opts->tag_name);
 | 
						|
 | 
						|
	check_lg2(git_tag_delete(state->repo, opts->tag_name),
 | 
						|
			"Unable to delete tag", opts->tag_name);
 | 
						|
 | 
						|
	printf("Deleted tag '%s' (was %s)\n", opts->tag_name, abbrev_oid.ptr);
 | 
						|
 | 
						|
	git_buf_free(&abbrev_oid);
 | 
						|
	git_object_free(obj);
 | 
						|
}
 | 
						|
 | 
						|
static void action_create_lighweight_tag(tag_state *state)
 | 
						|
{
 | 
						|
	git_repository *repo = state->repo;
 | 
						|
	tag_options *opts = state->opts;
 | 
						|
	git_oid oid;
 | 
						|
	git_object *target;
 | 
						|
 | 
						|
	check(!opts->tag_name, "Name required");
 | 
						|
 | 
						|
	if (!opts->target) opts->target = "HEAD";
 | 
						|
 | 
						|
	check(!opts->target, "Target required");
 | 
						|
 | 
						|
	check_lg2(git_revparse_single(&target, repo, opts->target),
 | 
						|
			"Unable to resolve spec", opts->target);
 | 
						|
 | 
						|
	check_lg2(git_tag_create_lightweight(&oid, repo, opts->tag_name,
 | 
						|
				target, opts->force), "Unable to create tag", NULL);
 | 
						|
 | 
						|
	git_object_free(target);
 | 
						|
}
 | 
						|
 | 
						|
static void action_create_tag(tag_state *state)
 | 
						|
{
 | 
						|
	git_repository *repo = state->repo;
 | 
						|
	tag_options *opts = state->opts;
 | 
						|
	git_signature *tagger;
 | 
						|
	git_oid oid;
 | 
						|
	git_object *target;
 | 
						|
 | 
						|
	check(!opts->tag_name, "Name required");
 | 
						|
	check(!opts->message, "Message required");
 | 
						|
 | 
						|
	if (!opts->target) opts->target = "HEAD";
 | 
						|
 | 
						|
	check_lg2(git_revparse_single(&target, repo, opts->target),
 | 
						|
			"Unable to resolve spec", opts->target);
 | 
						|
 | 
						|
	check_lg2(git_signature_default(&tagger, repo),
 | 
						|
			"Unable to create signature", NULL);
 | 
						|
 | 
						|
	check_lg2(git_tag_create(&oid, repo, opts->tag_name,
 | 
						|
				target, tagger, opts->message, opts->force), "Unable to create tag", NULL);
 | 
						|
 | 
						|
	git_object_free(target);
 | 
						|
	git_signature_free(tagger);
 | 
						|
}
 | 
						|
 | 
						|
static void print_usage(void)
 | 
						|
{
 | 
						|
	fprintf(stderr, "usage: see `git help tag`\n");
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
/** Parse command line arguments and choose action to run when done */
 | 
						|
static void parse_options(tag_action *action, tag_options *opts, int argc, char **argv)
 | 
						|
{
 | 
						|
	args_info args = ARGS_INFO_INIT;
 | 
						|
	*action = &action_list_tags;
 | 
						|
 | 
						|
	for (args.pos = 1; args.pos < argc; ++args.pos) {
 | 
						|
		const char *curr = argv[args.pos];
 | 
						|
 | 
						|
		if (curr[0] != '-') {
 | 
						|
			if (!opts->tag_name)
 | 
						|
				opts->tag_name = curr;
 | 
						|
			else if (!opts->target)
 | 
						|
				opts->target = curr;
 | 
						|
			else
 | 
						|
				print_usage();
 | 
						|
 | 
						|
			if (*action != &action_create_tag)
 | 
						|
				*action = &action_create_lighweight_tag;
 | 
						|
		} else if (!strcmp(curr, "-n")) {
 | 
						|
			opts->num_lines = 1;
 | 
						|
			*action = &action_list_tags;
 | 
						|
		} else if (!strcmp(curr, "-a")) {
 | 
						|
			*action = &action_create_tag;
 | 
						|
		} else if (!strcmp(curr, "-f")) {
 | 
						|
			opts->force = 1;
 | 
						|
		} else if (match_int_arg(&opts->num_lines, &args, "-n", 0)) {
 | 
						|
			*action = &action_list_tags;
 | 
						|
		} else if (match_str_arg(&opts->pattern, &args, "-l")) {
 | 
						|
			*action = &action_list_tags;
 | 
						|
		} else if (match_str_arg(&opts->tag_name, &args, "-d")) {
 | 
						|
			*action = &action_delete_tag;
 | 
						|
		} else if (match_str_arg(&opts->message, &args, "-m")) {
 | 
						|
			*action = &action_create_tag;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/** Initialize tag_options struct */
 | 
						|
static void tag_options_init(tag_options *opts)
 | 
						|
{
 | 
						|
	memset(opts, 0, sizeof(*opts));
 | 
						|
 | 
						|
	opts->message   = NULL;
 | 
						|
	opts->pattern   = NULL;
 | 
						|
	opts->tag_name  = NULL;
 | 
						|
	opts->target    = NULL;
 | 
						|
	opts->num_lines = 0;
 | 
						|
	opts->force     = 0;
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
	git_repository *repo;
 | 
						|
	tag_options opts;
 | 
						|
	tag_action action;
 | 
						|
	tag_state state;
 | 
						|
 | 
						|
	git_libgit2_init();
 | 
						|
 | 
						|
	check_lg2(git_repository_open_ext(&repo, ".", 0, NULL),
 | 
						|
			"Could not open repository", NULL);
 | 
						|
 | 
						|
	tag_options_init(&opts);
 | 
						|
	parse_options(&action, &opts, argc, argv);
 | 
						|
 | 
						|
	state.repo = repo;
 | 
						|
	state.opts = &opts;
 | 
						|
	action(&state);
 | 
						|
 | 
						|
	git_repository_free(repo);
 | 
						|
	git_libgit2_shutdown();
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |