VOLGIT



LOG | FILES | OVERVIEW


#ifndef VOLGIT_GIT_PART_C
#define VOLGIT_GIT_PART_C VOLGIT_GIT_PART_C
#include <git_part.h>

static const char *special_chars[256]
	=
	{
		['\"']="&quot;",
		['\'']="&apos;",
		['&']="&amp;",
		['<']="&lt;",
		['>']="&gt;",

	};
struct Index_File *get_index_file(int dir_fd,const char *name)
{
	struct Index_File *ret;
	ret=calloc(sizeof(struct Index_File),1);
	ret->out=create_file(dir_fd,name);
	
	fprintf(ret->out,"<pre>\n");
	return ret;
}
void release_index_file(struct Index_File *index)
{
	fprintf(index->out,"</pre>\n");
	close_file(index->out);
	free(index);
}

int print_diff_line(const git_diff_delta *delta,const git_diff_hunk *hunk,const git_diff_line *line,FILE *out)
{
	size_t i;
	if(line->origin=='+')
		fprintf(out,"<div class=\"origin_added\">");
	else if(line->origin=='-')
		fprintf(out,"<div class=\"origin_removed\">");
	else 
		fprintf(out,"<div class=\"origin_other\">");

	fprintf(out,"%c ",line->origin);
	for(i=0;i<line->content_len;++i)
		if(special_chars[(unsigned char)line->content[i]])
		{
			fwrite(special_chars[line->content[i]],1,strlen(special_chars[(unsigned char)line->content[i]]),out);
		}else
		{
			fwrite(line->content+i,1,1,out);
		}
	fprintf(out,"</div>");

	return 0;
}

void print_diff(FILE *out,git_tree *parent_tree,git_tree *current_tree,git_repository *repo)
{
	git_diff *diff_from_parent;
	size_t number_of_deltas=0;
	size_t i;

	fprintf(out,"<pre>");
	git_diff_tree_to_tree(&diff_from_parent,repo,parent_tree,current_tree,NULL);

	git_diff_print(diff_from_parent,GIT_DIFF_FORMAT_PATCH_ID,(git_diff_line_cb)print_diff_line,out);

	if(diff_from_parent)
		git_diff_free(diff_from_parent);
	fprintf(out,"</pre>");
}
void print_headers_and_commit_message(FILE *where,git_commit *current_commit,const git_oid *current,_Bool has_diff)
{
	const git_signature *who_commited;
	fprintf(where,"<div class=\"commit_header commit_commit_header\">COMMIT: %s",git_oid_tostr_s(current));
	if(has_diff)
		fprintf(where," <a href=\"diffs/%s.html\">[DIFF]</a></div>\n",git_oid_tostr_s(current));
	else
		fprintf(where,"</div>\n");

	who_commited=git_commit_committer(current_commit);

	fprintf(where,"<div class=\"commit_header commit_author_header\">AUTHOR: %s &lt;%s&gt;</div>\n",who_commited->name,who_commited->email);

	fprintf(where,"<div class=\"commit_header commit_date_header\">DATE: %s</div>\n",ctime(&who_commited->when.time));


	fprintf(where,"<pre><div class=\"commit_header commit_message\">\t%s</div></pre>\n",git_commit_message(current_commit));
}
void print_commits(int dir_fd,const git_reference *branch, git_repository *repo)
{
	const git_oid *id;
	FILE *log_file;
	int diff_directory_fd;
	int files_directory_fd;

	git_revwalk *walker;
	git_oid current;
	git_commit *current_commit;
	git_commit *parent_commit=NULL;

	git_revwalk_new(&walker,repo);
	id=git_reference_target(branch);
	git_revwalk_push(walker,id);


	log_file=create_file(dir_fd,"log");
	diff_directory_fd=create_dir(dir_fd,"diffs");
	files_directory_fd=create_dir(dir_fd,"files");
	
	if(git_revwalk_next(&current,walker))
		return;

	git_commit_lookup(&current_commit,repo,&current);

	print_files_top(files_directory_fd,current_commit,repo);

	while(!git_revwalk_next(&current,walker))
	{
		git_commit_lookup(&parent_commit,repo,&current);

		print_commit(current_commit,parent_commit,log_file,diff_directory_fd,repo);

		git_commit_free(current_commit);
		current_commit=parent_commit;
	}
	if(parent_commit!=NULL)
		print_commit(current_commit,NULL,log_file,diff_directory_fd,repo);

	git_revwalk_free(walker);
	close_file(log_file);
}
void print_commit(git_commit *current_commit,git_commit *parent_commit,FILE *log_file,int diff_directory_fd,git_repository *repo)
{
	git_tree *current_tree;
	git_tree *parent_tree;
	FILE *diff_file;

	git_commit_tree(&current_tree,current_commit);
	if(parent_commit==NULL)
		parent_tree=NULL;
	else
		git_commit_tree(&parent_tree,parent_commit);

	diff_file=create_file(diff_directory_fd,git_oid_tostr_s(git_commit_id(current_commit)));

	print_diff(diff_file,parent_tree,current_tree,repo);

	git_tree_free(parent_tree);
	print_headers_and_commit_message(log_file,current_commit,git_commit_id(current_commit),1);

	close_file(diff_file);
}

void print_branches(git_repository *repo)
{
	const char *branch_name;
	int branch_dir_fd;
	git_branch_iterator *it;
	git_reference *ref;
	git_branch_t branch_type=GIT_BRANCH_LOCAL;

	git_branch_iterator_new(&it,repo,branch_type);
	while(git_branch_next(&ref,&branch_type,it)==0)
	{
		git_branch_name(&branch_name,ref);	
		if(branch_name)
		{
			branch_dir_fd=create_branch_dir(branch_name);

			print_commits(branch_dir_fd,ref,repo);	

			close(branch_dir_fd);
		}else
		{
			printf("NULL\n");
		}
	}

	git_branch_iterator_free(it);
}
void print_files(struct Index_File *index_file,int dir_fd,git_tree *tree,git_repository *repo)
{
	size_t number_of_entries;
	size_t i;
	const git_tree_entry *current_entry;

	number_of_entries=git_tree_entrycount(tree);
	
	for(i=0;i<number_of_entries;++i)
	{
		current_entry=git_tree_entry_byindex(tree,i);

		if(i<number_of_entries-1)
			indentation_set_is_not_final(index_file,1);
		else
			indentation_set_is_not_final(index_file,0);

		
		if(state.output_is_tree_like)
		{
			print_indentation_for_treelike_output(index_file);
			if(i<number_of_entries-1)
				fprintf(index_file->out,"├── ");
			else
				fprintf(index_file->out,"└── ");
		}
		print_entry(index_file,current_entry,dir_fd,repo);
		if(i<number_of_entries-1)
			fprintf(index_file->out,"\n");
	}
}
void print_files_top(int dir_fd,git_commit *top_commit,git_repository *repo)
{
	struct Index_File *index_file;
	git_tree *tree;

	git_commit_tree(&tree,top_commit);

	index_file=get_index_file(dir_fd,"index");

	if(state.output_is_tree_like)
		fprintf(index_file->out,"│\n");

	print_files(index_file,dir_fd,tree,repo);


	release_index_file(index_file);
}
int print_entry(struct Index_File *index_file,const git_tree_entry *entry,int base_dir_fd,git_repository *repo)
{
	git_object *obj;
	const char *entry_name;

	git_tree_entry_to_object(&obj,repo,entry);
	entry_name=git_tree_entry_name(entry);

	switch(git_object_type(obj))
	{
		case GIT_OBJECT_TREE:
			print_tree_entry(index_file,entry_name,obj,base_dir_fd,repo);
			break;
		case GIT_OBJECT_BLOB:
			print_blob_entry(index_file,entry_name,obj,base_dir_fd,repo);
			break;
	}

	git_object_free(obj);
	return 0;
}
static inline void print_tree_entry(struct Index_File *index_file,const char *name,git_object *obj,int base_dir_fd,git_repository *repo)
{
	const git_oid *obj_oid;
	const char *entry_oid;
	git_tree *tree;
	
	obj_oid=git_object_id(obj);
	entry_oid=git_oid_tostr_s(obj_oid);
	tree=(git_tree*)obj;



	if(state.output_is_tree_like)
	{
		if(index_file->indentation==state.tree_cutoff)
		{
			push_html_link_for_tree(index_file->out,name,entry_oid);
			print_files_in_another_index(base_dir_fd,entry_oid,name,tree,repo);

		}else
		{
			fprintf(index_file->out,"%s",name);	
			increment_indentation(index_file,name);

			fprintf(index_file->out,"\n");
			print_files(index_file,base_dir_fd,tree,repo);
			
			decrement_indentation(index_file);	
		}
	}else
	{
		push_html_link_for_tree(index_file->out,name,entry_oid);
		print_files_in_another_index(base_dir_fd,entry_oid,name,tree,repo);
	}
}
static inline void print_blob_entry(struct Index_File *index_file,const char *name,git_object *obj,int base_dir_fd,git_repository *repo)
{
	const git_oid *obj_oid;
	const char *entry_oid;

	FILE *blob_file;
	const unsigned char *blob_data;
	size_t blob_size;
	size_t i;
	git_blob *blob;

	obj_oid=git_object_id(obj);
	entry_oid=git_oid_tostr_s(obj_oid);

	push_html_link_for_blob(index_file->out,name,entry_oid);

	blob=(git_blob*)obj;
	blob_file=create_file(base_dir_fd,entry_oid);

	blob_size=git_blob_rawsize(blob);
	blob_data=git_blob_rawcontent(blob);

	fprintf(blob_file,"<code><pre>\n");
	for(i=0;i<blob_size;++i)
	{
		if(special_chars[blob_data[i]])
		{
			fwrite(special_chars[blob_data[i]],1,strlen(special_chars[blob_data[i]]),blob_file);
		}else
		{
			fwrite(blob_data+i,1,1,blob_file);
		}
	}
	fprintf(blob_file,"</pre></code>\n");

	close_file(blob_file);
}
void print_indentation_for_treelike_output(struct Index_File *out)
{
	int i;
	int j;
	for(i=0;i<out->indentation;++i)
	{
		if(out->branches[i])
			fprintf(out->out,"│");
		else
			fprintf(out->out," ");

		for(j=0;j<out->offset[i]+2;++j)
			fprintf(out->out," ");
	}
}
void increment_indentation(struct Index_File *index,const char *name)
{
	index->offset[index->indentation]=strnlen(name,1000);
	++index->indentation;
}
void decrement_indentation(struct Index_File *index)
{
	--index->indentation;
}
void indentation_set_is_not_final(struct Index_File *index,_Bool is_final)
{
	index->branches[index->indentation]=is_final;
}

void print_files_in_another_index(int base_dir_fd,const char *entry_oid,const char *name,git_tree *tree,git_repository *repo)
{
	int new_dir_fd;
	struct Index_File *new_index;

	new_dir_fd=create_dir(base_dir_fd,entry_oid);
	new_index=get_index_file(new_dir_fd,"index");
	if(state.output_is_tree_like)
		fprintf(new_index->out,"%s\n│\n",name);
	print_files(new_index,new_dir_fd,tree,repo);
	
	release_index_file(new_index);
	close(new_dir_fd);
}
#endif