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