Index: git-shared.c
===================================================================
--- git-shared.c (nonexistent)
+++ git-shared.c (revision 5)
@@ -0,0 +1,1410 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2) */
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h> /* strdup(3) */
+#include <libgen.h> /* basename(3) */
+#include <ctype.h> /* tolower(3) */
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <magic.h>
+
+#include <git2.h>
+
+#include <nls.h>
+
+#include <defs.h>
+#include <fatal.h>
+#include <http.h>
+#include <html.h>
+#include <strbuf.h>
+#include <wrapper.h>
+#include <system.h>
+
+#include <ctx.h>
+#include <git-shared.h>
+
+
+#define CGIT_ERRMSG_SIZE 4096
+
+void cgit_error( const char *fmt, ... )
+{
+ va_list arg_ptr;
+ char buf[CGIT_ERRMSG_SIZE];
+ char msg[CGIT_ERRMSG_SIZE];
+ char *format = "%s: %s\n";
+
+ va_start( arg_ptr, fmt );
+
+ vsnprintf( msg, CGIT_ERRMSG_SIZE, (const void *)fmt, arg_ptr );
+
+ va_end( arg_ptr ); /* Reset variable arguments. */
+
+ snprintf( buf, CGIT_ERRMSG_SIZE, format, "cgit", msg );
+
+ (void)write( STDERR_FILENO, buf, strlen( buf ) );
+
+ exit( 1 );
+}
+
+cgit_errfunc cgit_fatal = cgit_error;
+
+
+
+int is_bare( const char *path )
+{
+ char *p = NULL;
+ int ret = -1;
+
+ if( !path || !*path ) return ret;
+
+ p = rindex( path, '.' );
+ if( !p )
+ {
+ ret = 0;
+ }
+ else
+ {
+ if( !strncmp( p, ".git", 4 ) )
+ ret = 1;
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+git_repository *open_repository( const char *path )
+{
+ git_repository *repo = NULL;
+ int bare = 0;
+
+ if( (bare = is_bare( path )) < 0 ) return repo;
+
+ if( bare )
+ {
+ if( git_repository_open_bare( &repo, path ) < 0 ) return repo;
+ }
+ else
+ {
+ if( git_repository_open( &repo, path ) < 0 ) return repo;
+ }
+
+ return repo;
+}
+
+void close_repository( git_repository *repo )
+{
+ if( !repo ) return;
+ git_repository_free( repo );
+}
+
+/*
+ Get Last Commit:
+ (Only commit-ish refs are permitted)
+ */
+git_commit *get_commit_by_ref( git_repository *repo, const char *ref )
+{
+ int error = 0;
+ git_object *obj = NULL;
+ git_commit *commit = NULL;
+
+ char reference[PATH_MAX] = { 0 };
+
+ if( !repo ) return commit;
+ if( !ref ) ref = "HEAD^{commit}";
+
+ sprintf( (char *)&reference[0], "%s^{commit}", ref );
+
+ error = git_revparse_single( &obj, repo, (const char *)&reference[0] );
+ if( error < 0 )
+ {
+ git_error_clear();
+ return commit;
+ }
+
+ if( git_object_type(obj) != GIT_OBJECT_COMMIT )
+ {
+ git_object_free( obj );
+ return commit;
+ }
+
+ error = git_commit_lookup( &commit, repo, git_object_id( obj ) );
+ if( error < 0 )
+ {
+ git_error_clear();
+ git_object_free( obj );
+ return NULL;
+ }
+
+ git_object_free( obj );
+
+ return commit;
+}
+
+git_commit *get_commit_by_hex( git_repository *repo, const char *hex )
+{
+ git_oid cid;
+ git_commit *commit = NULL;
+
+ if( !repo || !hex ) return commit;
+ if( git_oid_fromstr( &cid, hex ) < 0 ) return commit;
+
+ if( git_commit_lookup( &commit, repo, &cid ) < 0 ) return NULL;
+ return commit;
+}
+
+
+void fill_short_commit_info( struct short_commit_info *info, const char *path )
+{
+ git_repository *repo = NULL;
+ git_commit *commit = NULL;
+
+ if( !info || !path ) return;
+
+ if( !(repo = open_repository( path )) ) return;
+
+ if( !(commit = get_commit_by_ref( repo, NULL )) )
+ {
+ close_repository( repo );
+ return;
+ }
+
+ git_oid_tostr( info->rev, GIT_OID_HEXSZ+1, git_commit_id( commit ) );
+ info->date = git_commit_time( commit );
+ info->offset = git_commit_time_offset( commit );
+
+ info->offset = (info->offset % 60) + ((info->offset / 60) * 100);
+
+ git_commit_free( commit );
+
+ close_repository( repo );
+}
+
+struct found {
+ git_object_t kind;
+ git_filemode_t mode;
+ const git_oid *oid;
+ git_object *obj;
+};
+
+static void find_entry( struct found *ret, git_repository *repo, git_tree *tree, char *path )
+{
+ git_tree *ntree = NULL;
+ int i, cnt;
+ char *s, *p = NULL, *n;
+
+ if( !ret || !repo || !tree ) return;
+
+ if( path && *path )
+ {
+ s = path;
+
+ while( *s && *s == '/' )
+ ++s;
+ n = p = s;
+
+ while( *p && *p != '/' )
+ ++p;
+ if( *p )
+ {
+ *p = '\0'; s = ++p;
+ }
+ else
+ s = p;
+
+ git_tree_dup( &ntree, tree );
+ cnt = git_tree_entrycount( ntree );
+
+ for( i = 0; i < cnt; ++i )
+ {
+ const git_tree_entry *entry;
+ entry = git_tree_entry_byindex( ntree, i );
+
+ if( !strcmp( git_tree_entry_name( entry ), n ) )
+ {
+ ret->kind = git_tree_entry_type( entry );
+ ret->mode = git_tree_entry_filemode( entry );
+ git_tree_entry_to_object( &ret->obj, repo, entry );
+ ret->oid = git_object_id( (const git_object *)ret->obj );
+
+ if( ret->kind == GIT_OBJECT_TREE && s && *s )
+ {
+ git_object *obj;
+ git_tree_entry_to_object( &obj, repo, entry );
+ find_entry( ret, repo, (git_tree *)obj, s );
+ git_object_free( obj );
+ break;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ }
+ git_tree_free( ntree );
+ }
+
+ return;
+}
+
+
+void fill_commit_info( struct cgit_info *info, const char *path, const char *rpath )
+{
+ git_repository *repo = NULL;
+ git_oid cid;
+ git_commit *commit = NULL;
+ git_tree *tree = NULL;
+ const git_oid *oid;
+ const git_signature *sign = NULL;
+
+ char *author;
+ int len;
+
+ if( !info || !path || !info->revision[0] ) return;
+ if( git_oid_fromstr( &cid, (const char *)&info->revision[0] ) < 0 ) return;
+ if( !(repo = open_repository( path )) ) return;
+
+ git_commit_lookup( &commit, repo, &cid );
+ git_commit_tree( &tree, commit );
+
+ info->kind = git_object_type( (const git_object *)tree );
+ oid = git_tree_id( (const git_tree *)tree );
+ git_oid_fmt( (char *)&info->oid[0], oid );
+
+ if( rpath && *rpath )
+ {
+ struct found ret = { .kind = GIT_OBJECT_TREE, .oid = NULL, .obj = NULL };
+ char relative_path[PATH_MAX] = { 0 };
+ sprintf( (char *)&relative_path[0], rpath );
+ find_entry( &ret, repo, tree, (char *)&relative_path[0] );
+ info->kind = ret.kind;
+ info->mode = ret.mode;
+ git_oid_fmt( (char *)&info->oid[0], ret.oid );
+ git_object_free( ret.obj );
+ }
+
+ git_tree_free( tree );
+
+ info->date = git_commit_time( commit );
+ info->offset = git_commit_time_offset( commit );
+
+ info->offset = (info->offset % 60) + ((info->offset / 60) * 100);
+
+ sign = git_commit_author( commit );
+ len = (int)strlen( sign->name ) + 1;
+ author = (char *)__sbrk( len );
+ strcpy( author, sign->name );
+ info->author = author;
+
+ git_commit_free( commit );
+ close_repository( repo );
+}
+
+
+static const char *mime_info( struct cgit_info *info, const char *buffer, size_t length )
+{
+ const char *mime = NULL;
+ magic_t magic;
+
+ if( !info || !buffer || !length ) return mime;
+
+ magic = magic_open( MAGIC_MIME );
+ if( !magic )
+ {
+ html_fatal( "unable to initialize magic library" );
+ return mime;
+ }
+ if( magic_load( magic, NULL ) != 0 )
+ {
+ html_fatal( "cannot load magic database - %s\n", magic_error( magic ) );
+ magic_close( magic );
+ return mime;
+ }
+
+ mime = magic_buffer( magic, buffer, length );
+ if( mime )
+ {
+ int len = (int)strlen( mime ) + 1;
+ char *mime_type = (char *)__sbrk( len );
+ memcpy( (void *)mime_type, (const void *)mime, len );
+ info->mime = (const char *)mime_type;
+ }
+
+ magic_close( magic );
+ return mime;
+}
+
+void fill_mime_info( struct cgit_info *info, const char *path, const char *rpath )
+{
+ git_repository *repo = NULL;
+ git_oid cid;
+ git_commit *commit = NULL;
+ git_tree *tree = NULL;
+ git_object *obj = NULL;
+ git_object_t kind;
+ git_blob *blob = NULL;
+ int blob_allocated = 0;
+ git_off_t rawsize = 0;
+ const char *raw = NULL;
+ char mime_buf[1024] = { 0 };
+ size_t len = 1024;
+
+ if( !info || !path || !info->revision[0] || info->kind != GIT_OBJECT_BLOB ) return;
+ if( git_oid_fromstr( &cid, (const char *)&info->revision[0] ) < 0 ) return;
+ if( !(repo = open_repository( path )) ) return;
+
+ git_commit_lookup( &commit, repo, &cid );
+ git_commit_tree( &tree, commit );
+
+ blob = (git_blob *)tree;
+ kind = git_object_type( (const git_object *)tree );
+ if( rpath && *rpath )
+ {
+ struct found ret = { .kind = GIT_OBJECT_TREE, .oid = NULL, .obj = NULL };
+ char relative_path[PATH_MAX] = { 0 };
+ sprintf( (char *)&relative_path[0], rpath );
+ find_entry( &ret, repo, tree, (char *)&relative_path[0] );
+ kind = ret.kind;
+ if( kind == GIT_OBJECT_BLOB )
+ {
+ git_object_dup( &obj, ret.obj );
+ blob_allocated = 1;
+ blob = (git_blob *)obj;
+ }
+ git_object_free( ret.obj ); /* We have to free allocated object returned by find_entry(). */
+ }
+
+ if( kind != GIT_OBJECT_BLOB )
+ {
+ if( blob_allocated )
+ git_object_free( (git_object *)blob );
+ git_tree_free( tree );
+ git_commit_free( commit );
+ close_repository( repo );
+ return;
+ }
+
+ rawsize = git_blob_rawsize( blob );
+ raw = (const char *)git_blob_rawcontent( blob );
+ if( rawsize > 1024 )
+ {
+ memcpy( (void *)&mime_buf[0], (const void *)raw, len );
+ }
+ else
+ {
+ memcpy( (void *)&mime_buf[0], (const void *)raw, (size_t)rawsize );
+ len = (size_t)rawsize;
+ }
+
+ if( blob_allocated )
+ git_object_free( (git_object *)blob );
+ git_tree_free( tree );
+
+ mime_info( info, (const char *)&mime_buf[0], len );
+
+ git_commit_free( commit );
+ close_repository( repo );
+}
+
+
+size_t branches_number( const char *path, const char *skip )
+{
+ git_repository *repo = NULL;
+ git_reference_iterator *iter = NULL;
+ const char *name = NULL;
+ size_t ret = 0;
+
+ if( !path ) return ret;
+
+ if( !(repo = open_repository( path )) ) return ret;
+
+ if( git_reference_iterator_glob_new( &iter, repo, "refs/heads/*" ) < 0 )
+ {
+ close_repository( repo );
+ return ret;
+ }
+
+ while( !git_reference_next_name( &name, iter ) )
+ {
+ if( skip && *skip )
+ {
+ if( strcmp( (const char *)&name[11], skip ) )
+ {
+ ++ret;
+ }
+ }
+ else
+ ++ret;
+ }
+
+ git_reference_iterator_free( iter );
+ close_repository( repo );
+
+ return ret;
+}
+
+size_t tags_number( const char *path )
+{
+ git_repository *repo = NULL;
+ git_strarray tags;
+ size_t ret = 0;
+
+ if( !path ) return ret;
+
+ if( !(repo = open_repository( path )) ) return ret;
+
+ if( git_tag_list( &tags, repo ) < 0 )
+ {
+ close_repository( repo );
+ return ret;
+ }
+
+ ret = tags.count;
+ git_strarray_free( &tags );
+
+ close_repository( repo );
+
+ return ret;
+}
+
+/**********************************
+ List of references functions:
+ */
+struct cgit_ref_names *cgit_ref_names_new( void )
+{
+ struct cgit_ref_names *names = NULL;
+
+ names = (struct cgit_ref_names *)xmalloc( sizeof(struct cgit_ref_names) );
+ return names;
+}
+
+void cgit_ref_names_allocate( struct cgit_ref_names **ref_names )
+{
+ struct cgit_ref_names *names = NULL;
+
+ if( !ref_names ) return;
+ names = (struct cgit_ref_names *)xmalloc( sizeof(struct cgit_ref_names) );
+ *ref_names = names;
+}
+
+void cgit_ref_names_add( struct cgit_ref_names *names, const char *name )
+{
+ if( !names ) return;
+
+ if( !names->name )
+ {
+ names->name = (char **)xmalloc( sizeof(char *) );
+ names->name[0] = strdup( name );
+ names->len = (size_t)1;
+ }
+ else
+ {
+ names->name = (char **)xrealloc( names->name, (names->len + 1) * sizeof(char *) );
+ names->name[names->len] = strdup( name );
+ ++names->len;
+ }
+}
+
+void cgit_ref_names_free( struct cgit_ref_names *names )
+{
+ if( !names ) return;
+
+ if( names->len && names->name )
+ {
+ size_t i;
+ for( i = 0; i < names->len; ++i )
+ {
+ if( names->name[i] )
+ free( names->name[i] );
+ }
+ free( names->name );
+ }
+ free( names );
+}
+/*
+ End of List of references functions.
+ **************************************/
+
+void lookup_branches_by_prefix( struct cgit_ref_names **ref_names, const char *prefix )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+ struct cgit_ref_names *names = NULL;
+
+ if( !ref_names || !prefix || !*prefix ) return;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ git_repository *repo = NULL;
+ char path[PATH_MAX] = { 0 };
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ if( !(repo = open_repository( (const char *)&path[0] )) ) return;
+
+ {
+ git_reference_iterator *iter = NULL;
+ const char *name = NULL;
+ const char *refs = "refs/heads/*";
+
+ if( git_reference_iterator_glob_new( &iter, repo, refs ) < 0 )
+ {
+ close_repository( repo );
+ return;
+ }
+
+ cgit_ref_names_allocate( &names );
+ *ref_names = names;
+
+ while( !git_reference_next_name( &name, iter ) )
+ {
+ size_t len = strlen( prefix );
+
+ if( !strncmp( prefix, (char *)&name[11], len ) )
+ cgit_ref_names_add( names, (const char *)&name[11] );
+ }
+ git_reference_iterator_free( iter );
+ }
+
+ close_repository( repo );
+ }
+
+ return;
+}
+
+void lookup_tags_by_prefix( struct cgit_ref_names **ref_names, const char *prefix )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+ struct cgit_ref_names *names = NULL;
+
+ if( !ref_names || !prefix || !*prefix ) return;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ git_repository *repo = NULL;
+ char path[PATH_MAX] = { 0 };
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ if( !(repo = open_repository( (const char *)&path[0] )) ) return;
+
+ {
+ git_reference_iterator *iter = NULL;
+ const char *name = NULL;
+ const char *refs = "refs/tags/*";
+
+ if( git_reference_iterator_glob_new( &iter, repo, refs ) < 0 )
+ {
+ close_repository( repo );
+ return;
+ }
+
+ cgit_ref_names_allocate( &names );
+ *ref_names = names;
+
+ while( !git_reference_next_name( &name, iter ) )
+ {
+ size_t len = strlen( prefix );
+
+ if( !strncmp( prefix, (char *)&name[10], len ) )
+ cgit_ref_names_add( names, (const char *)&name[10] );
+ }
+ git_reference_iterator_free( iter );
+ }
+
+ close_repository( repo );
+ }
+
+ return;
+}
+
+
+/***********************************
+ List of commits functions:
+ */
+struct cgit_hex_commits *cgit_hex_commits_new( void )
+{
+ struct cgit_hex_commits *commits = NULL;
+
+ commits = (struct cgit_hex_commits *)xmalloc( sizeof(struct cgit_hex_commits) );
+ return commits;
+}
+
+void cgit_hex_commits_allocate( struct cgit_hex_commits **hex_commits )
+{
+ struct cgit_hex_commits *commits = NULL;
+
+ if( !hex_commits ) return;
+ commits = (struct cgit_hex_commits *)xmalloc( sizeof(struct cgit_hex_commits) );
+ *hex_commits = commits;
+}
+
+void cgit_hex_commits_add( struct cgit_hex_commits *commits, const char *hex )
+{
+ if( !commits ) return;
+
+ if( !commits->hex )
+ {
+ commits->hex = (char **)xmalloc( sizeof(char *) );
+ commits->hex[0] = strdup( hex );
+ commits->len = (size_t)1;
+ }
+ else
+ {
+ commits->hex = (char **)xrealloc( commits->hex, (commits->len + 1) * sizeof(char *) );
+ commits->hex[commits->len] = strdup( hex );
+ ++commits->len;
+ }
+}
+
+void cgit_hex_commits_free( struct cgit_hex_commits *commits )
+{
+ if( !commits ) return;
+
+ if( commits->len && commits->hex )
+ {
+ size_t i;
+ for( i = 0; i < commits->len; ++i )
+ {
+ if( commits->hex[i] )
+ free( commits->hex[i] );
+ }
+ free( commits->hex );
+ }
+ free( commits );
+}
+/*
+ End of List of commits functions.
+ ***********************************/
+
+void parse_relative_path( char *ref, char *rpath, const char *relative_path )
+{
+ char *s, *p = NULL, *n, *path, *path_info;
+ int len = 0;
+
+ *ref = '\0', *rpath = '\0';
+
+ if( relative_path && *relative_path )
+ {
+ path_info = xstrdup( relative_path );
+ s = path_info;
+
+ while( *s )
+ {
+ while( *s && *s == '/' )
+ ++s;
+ n = p = s;
+
+ while( *p && *p != '/' )
+ ++p;
+ if( *p )
+ {
+ *p = '\0'; s = ++p;
+ }
+ else
+ s = p;
+
+ if( !strcmp( n, "tags" ) )
+ {
+ sprintf( ref, "refs/%s/", n );
+ n = s;
+ while( *p && *p != '/' )
+ ++p;
+ if( *p )
+ {
+ *p = '\0'; s = ++p;
+ }
+ else
+ s = p;
+
+ if( n && *n )
+ {
+ int found = 0;
+ char tag[PATH_MAX] = { 0 };
+ char reminder[PATH_MAX] = { 0 };
+ char *b, *r;
+
+ struct cgit_ref_names *names = NULL;
+
+ sprintf( reminder, "%s", s );
+ sprintf( tag, "%s", n );
+
+ r = (char *)&reminder[0];
+
+ /*********************************
+ Searching the real name of tag:
+ */
+ lookup_tags_by_prefix( &names, n );
+ while( *r )
+ {
+ b = r;
+ while( *r && *r != '/' )
+ ++r;
+ if( *r )
+ {
+ *r = '\0'; ++r;
+ }
+
+ if( b && *b )
+ {
+ char probe[PATH_MAX] = { 0 };
+ sprintf( probe, "%s/%s", tag, b );
+
+ found = 0;
+ {
+ size_t i, len = strlen( probe );
+ for( i = 0; i < names->len; ++i )
+ {
+ if( !strncmp( probe, names->name[i], len ) )
+ {
+ ++found;
+ }
+ }
+ }
+ if( found == 1 )
+ {
+ strcat( tag, "/" );
+ strcat( tag, b );
+ break;
+ }
+ if( found )
+ {
+ strcat( tag, "/" );
+ strcat( tag, b );
+ }
+ }
+ }
+ cgit_ref_names_free( names );
+
+ if( found )
+ {
+ strcat( ref, tag );
+ p += r - &reminder[0];
+ s = p;
+ }
+ else
+ strcat( ref, n );
+ }
+ else
+ sprintf( ref, "refs/heads/%s", ctx.repo.trunk );
+
+ break;
+ }
+ else if( !strcmp( n, "branches" ) )
+ {
+ sprintf( ref, "refs/heads/" );
+
+ n = s;
+ while( *p && *p != '/' )
+ ++p;
+ if( *p )
+ {
+ *p = '\0'; s = ++p;
+ }
+ else
+ s = p;
+
+ if( n && *n )
+ {
+ int found = 0;
+ char branch[PATH_MAX] = { 0 };
+ char reminder[PATH_MAX] = { 0 };
+ char *b, *r;
+
+ struct cgit_ref_names *names = NULL;
+
+ sprintf( reminder, "%s", s );
+ sprintf( branch, "%s", n );
+
+ r = (char *)&reminder[0];
+
+ /************************************
+ Searching the real name of branch:
+ */
+ lookup_branches_by_prefix( &names, n );
+ while( *r )
+ {
+ b = r;
+ while( *r && *r != '/' )
+ ++r;
+ if( *r )
+ {
+ *r = '\0'; ++r;
+ }
+
+ if( b && *b )
+ {
+ char probe[PATH_MAX] = { 0 };
+ sprintf( probe, "%s/%s", branch, b );
+
+ found = 0;
+ {
+ size_t i, len = strlen( probe );
+ for( i = 0; i < names->len; ++i )
+ {
+ if( !strncmp( probe, names->name[i], len ) )
+ {
+ ++found;
+ }
+ }
+ }
+ if( found == 1 )
+ {
+ strcat( branch, "/" );
+ strcat( branch, b );
+ break;
+ }
+ if( found )
+ {
+ strcat( branch, "/" );
+ strcat( branch, b );
+ }
+ }
+ }
+ cgit_ref_names_free( names );
+
+ if( found )
+ {
+ strcat( ref, branch );
+ p += r - &reminder[0];
+ s = p;
+ }
+ else
+ strcat( ref, n );
+ }
+ else
+ sprintf( ref, "refs/heads/%s", ctx.repo.trunk );
+
+ break;
+ }
+ else if( !strcmp( n, "trunk" ) )
+ {
+ sprintf( ref, "refs/heads/%s", ctx.repo.trunk );
+ break;
+ }
+ else
+ {
+ /* return '/' */
+ *--p = '/'; p = n;
+ sprintf( ref, "refs/heads/%s", ctx.repo.trunk );
+ break;
+ }
+ }
+
+ path = p;
+
+ if( *path )
+ {
+ len = (int)strlen( path );
+
+ if( path[len-1] =='/' ) { path[len-1] = '\0'; --len; }
+ len += 1;
+
+ sprintf( rpath, "%s", path );
+ }
+
+ free( path_info );
+ }
+}
+
+static void fill_hex_list( struct cgit_hex_commits **hex_commits, char *buf )
+{
+ char *s, *p = NULL, *h;
+ struct cgit_hex_commits *commits = NULL;
+
+ if( !hex_commits || !buf ) return;
+
+ cgit_hex_commits_allocate( &commits );
+ *hex_commits = commits;
+
+ s = buf;
+ while( *s )
+ {
+ while( *s && *s == '\n' )
+ ++s;
+ h = p = s;
+
+ while( *p && *p != '\n' )
+ ++p;
+ if( *p )
+ {
+ *p = '\0'; s = ++p;
+ }
+ else
+ s = p;
+
+ cgit_hex_commits_add( commits, h );
+ }
+}
+
+
+void cgit_fill_commits_list( struct cgit_hex_commits **hex_commits, int ofs, const char *relative_path, const char *revision )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+
+ if( !hex_commits || !relative_path ) return;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ char ref[PATH_MAX] = { 0 };
+ char rpath[PATH_MAX] = { 0 };
+ int psize = atoi( ctx.vars.page_size );
+
+ char path[PATH_MAX] = { 0 };
+ char cmd[PATH_MAX];
+ struct strbuf buf = STRBUF_INIT;
+ pid_t p = (pid_t) -1;
+ int rc;
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ parse_relative_path( (char *)&ref[0], (char *)&rpath[0], relative_path );
+
+ /*
+ NOTE:
+ ====
+ The --follow option for blobs is not applicable here because the option --skip=n
+ doesn't works when option --follow is defined.
+
+ In other words we cannot use --follow here:
+
+ struct cgit_info info = CGIT_INFO_INIT;
+ const char *follow = "";
+
+ if( revision && *revision )
+ strncpy( info.revision, revision, GIT_OID_HEXSZ+1 );
+ else
+ strncpy( info.revision, ctx.repo.info.revision, GIT_OID_HEXSZ+1 );
+
+ fill_commit_info( &info, (const char *)&path[0], (const char *)&rpath[0] );
+ if( info.kind == GIT_OBJECT_BLOB )
+ {
+ follow = "--follow";
+ }
+ */
+
+ ++psize;
+
+ if( revision && *revision )
+ snprintf( (char *)&cmd[0], 1024, "git --git-dir=%s log --pretty=format:'%%H' --skip=%d -%d %s -- %s 2>/dev/null", (char *)&path[0], ofs, psize, revision, (char *)&rpath[0] );
+ else
+ snprintf( (char *)&cmd[0], 1024, "git --git-dir=%s log --pretty=format:'%%H' --skip=%d -%d %s -- %s 2>/dev/null", (char *)&path[0], ofs, psize, (char *)&ref[0], (char *)&rpath[0] );
+ p = sys_exec_command( &buf, cmd );
+ rc = sys_wait_command( p, NULL );
+ if( rc != 0 )
+ {
+ strbuf_release( &buf );
+ return;
+ }
+
+ if( buf.buf[0] )
+ {
+ strbuf_trim( &buf );
+
+ fill_hex_list( hex_commits, (char *)&buf.buf[0] );
+ }
+
+ strbuf_release( &buf );
+ }
+
+ return;
+}
+
+git_repository *cgit_open_repository( void )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+ git_repository *repo = NULL;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ char path[PATH_MAX] = { 0 };
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ return open_repository( (const char *)&path[0] );
+ }
+
+ return repo;
+}
+
+git_commit *lookup_commit_by_ref( const char *ref )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+ git_commit *commit = NULL;
+
+ if( !ref || !*ref ) return commit;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ git_repository *repo = NULL;
+ char path[PATH_MAX] = { 0 };
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ if( !(repo = open_repository( (const char *)&path[0] )) ) return commit;
+ commit = get_commit_by_ref( repo, ref );
+ close_repository( repo );
+ }
+
+ return commit;
+}
+
+git_commit *lookup_commit_by_hex( const char *hex )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+ git_commit *commit = NULL;
+
+ if( !hex || !*hex ) return commit;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ git_repository *repo = NULL;
+ char path[PATH_MAX] = { 0 };
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ if( !(repo = open_repository( (const char *)&path[0] )) ) return commit;
+ commit = get_commit_by_hex( repo, hex );
+ close_repository( repo );
+ }
+
+ return commit;
+}
+
+
+struct print_data {
+ struct strbuf *sb;
+ int *files;
+ int *insertions;
+ int *deletions;
+};
+
+static int printer( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *data )
+{
+ struct print_data *pdata = (struct print_data *)data;
+
+ (void)delta; (void)hunk;
+
+ switch( line->origin )
+ {
+ case GIT_DIFF_LINE_ADDITION: *pdata->insertions += 1; break;
+ case GIT_DIFF_LINE_DELETION: *pdata->deletions += 1; break;
+ case GIT_DIFF_LINE_FILE_HDR: *pdata->files += 1; break;
+ default: break;
+ }
+
+ if( line->origin == GIT_DIFF_LINE_CONTEXT ||
+ line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION )
+ strbuf_addch( pdata->sb, line->origin );
+
+ strbuf_add( pdata->sb, (const void *)line->content, (size_t)line->content_len );
+
+ return 0;
+}
+
+void cgit_fill_diff_with_parent( struct strbuf *sb, char *parent_hex, size_t parent_len, int *files, int *insertions, int *deletions, const char *hex )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+
+ git_commit *commit = NULL, *parent = NULL;
+ git_tree *commit_tree = NULL, *parent_tree = NULL;
+ git_diff *diff = NULL;
+
+ struct print_data data = { .sb = NULL, .files = NULL, .insertions = NULL, .deletions = NULL };
+
+ if( !parent_hex || !files || !insertions || !deletions || !hex || !*hex ) return;
+ if( parent_len != GIT_OID_HEXSZ+1 ) return;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ if( name && git_root )
+ {
+ git_repository *repo = NULL;
+ char path[PATH_MAX] = { 0 };
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ if( !(repo = open_repository( (const char *)&path[0] )) ) return;
+
+ commit = get_commit_by_hex( repo, hex );
+ if( git_commit_parent( &parent, commit, 0 ) < 0 )
+ {
+ git_commit_free( commit );
+ close_repository( repo );
+ return;
+ }
+ if( git_oid_tostr( parent_hex, GIT_OID_HEXSZ+1, git_commit_id( parent ) ) < 0 )
+ {
+ git_commit_free( commit );
+ git_commit_free( parent );
+ close_repository( repo );
+ return;
+ }
+
+ if( git_commit_tree( &commit_tree, commit ) < 0 )
+ {
+ git_commit_free( commit );
+ git_commit_free( parent );
+ close_repository( repo );
+ return;
+ }
+ if( git_commit_tree( &parent_tree, parent ) < 0 )
+ {
+ git_tree_free( commit_tree );
+ git_commit_free( commit );
+ git_commit_free( parent );
+ close_repository( repo );
+ return;
+ }
+ if( git_diff_tree_to_tree( &diff, repo, parent_tree, commit_tree, NULL ) < 0 )
+ {
+ git_tree_free( commit_tree );
+ git_tree_free( parent_tree );
+ git_commit_free( commit );
+ git_commit_free( parent );
+ close_repository( repo );
+ return;
+ }
+
+
+ *files = 0, *insertions = 0, *deletions = 0;
+
+ data.sb = sb;
+ data.files = files;
+ data.insertions = insertions;
+ data.deletions = deletions;
+
+ git_diff_print( diff, GIT_DIFF_FORMAT_PATCH, printer, &data );
+
+ *files = *data.files, *insertions = *data.insertions, *deletions = *data.deletions;
+
+ git_diff_free( diff );
+ git_tree_free( commit_tree );
+ git_tree_free( parent_tree );
+ git_commit_free( commit );
+ git_commit_free( parent );
+
+ close_repository( repo );
+ }
+}
+
+
+void cgit_diff_with_parent( struct strbuf *sb, char *parent_hex, size_t parent_len, int *files, int *insertions, int *deletions, const char *hex, const char *relative_path )
+{
+ const char *name = NULL, *git_root = NULL, *repo_root = NULL;
+
+ git_commit *commit = NULL, *parent = NULL;
+
+ if( !parent_hex || !files || !insertions || !deletions || !hex || !*hex || !relative_path || !*relative_path ) return;
+ if( parent_len != GIT_OID_HEXSZ+1 ) return;
+
+ name = ctx.repo.name;
+ git_root = ctx.repo.git_root;
+ repo_root = ctx.repo.repo_root;
+
+ *files = 0, *insertions = 0, *deletions = 0;
+
+ if( name && git_root )
+ {
+ char ref[PATH_MAX] = { 0 };
+ char rpath[PATH_MAX] = { 0 };
+
+ git_repository *repo = NULL;
+ char path[PATH_MAX] = { 0 };
+ char cmd[PATH_MAX];
+ struct strbuf buf = STRBUF_INIT;
+ pid_t p = (pid_t) -1;
+ int rc;
+
+ sprintf( (char *)&path[0], "%s/", git_root );
+ if( repo_root && *repo_root )
+ {
+ strcat( (char *)&path[0], repo_root );
+ strcat( (char *)&path[0], "/" );
+ }
+ strcat( (char *)&path[0], name );
+
+ if( !is_bare( (char *)&path[0] ) )
+ {
+ strcat( (char *)&path[0], "/.git" );
+ }
+
+ if( !(repo = open_repository( (const char *)&path[0] )) ) return;
+
+ commit = get_commit_by_hex( repo, hex );
+ if( git_commit_parent( &parent, commit, 0 ) < 0 )
+ {
+ git_commit_free( commit );
+ close_repository( repo );
+ return;
+ }
+ if( git_oid_tostr( parent_hex, GIT_OID_HEXSZ+1, git_commit_id( parent ) ) < 0 )
+ {
+ git_commit_free( commit );
+ git_commit_free( parent );
+ close_repository( repo );
+ return;
+ }
+
+ git_commit_free( commit );
+ git_commit_free( parent );
+ close_repository( repo );
+
+ /* now we have two revisions */
+ parse_relative_path( (char *)&ref[0], (char *)&rpath[0], relative_path );
+
+ snprintf( (char *)&cmd[0], 1024, "git --git-dir=%s diff %s %s -- %s 2>/dev/null", (char *)&path[0], parent_hex, hex, (char *)&rpath[0] );
+ p = sys_exec_command( &buf, cmd );
+ rc = sys_wait_command( p, NULL );
+ if( rc != 0 )
+ {
+ strbuf_release( &buf );
+ return;
+ }
+
+ if( buf.buf[0] )
+ {
+ char *p = (char *)&buf.buf[0];
+
+ while( *p )
+ {
+ if( *p == '\n' && p[1] && p[2] && p[3] )
+ {
+ if( (p[1] == '+' && p[2] == '+' && p[3] == '+') ||
+ (p[1] == '-' && p[2] == '-' && p[3] == '-') )
+ *files += 1;
+ if( p[1] == '+' && p[2] != '+' && p[3] != '+' ) { ++p; *insertions += 1; }
+ if( p[1] == '-' && p[2] != '-' && p[3] != '-' ) { ++p; *deletions += 1; }
+ }
+ ++p;
+ }
+ *files /= 2;
+
+ strbuf_addbuf( sb, (const struct strbuf *)&buf );
+ }
+
+ strbuf_release( &buf );
+ }
+}