-rw-r--r-- | cgit.css | 51 | ||||
-rw-r--r-- | cgit.h | 1 | ||||
-rw-r--r-- | git.h | 106 | ||||
-rw-r--r-- | html.c | 20 | ||||
-rw-r--r-- | ui-commit.c | 111 |
5 files changed, 281 insertions, 8 deletions
@@ -1,18 +1,18 @@ body { font-family: arial; font-size: normal; background: white; padding: 0em; - margin: 0.5em; + margin: 0.5em 1em; } h2 { font-size: normal; font-weight: bold; margin-bottom: 0.1em; } a { color: blue; text-decoration: none; @@ -41,34 +41,37 @@ table.list th { vertical-align: baseline; } table.list td { border: none; padding: 0.1em 0.5em 0.1em 0.5em; } img { border: none; } div#header { - background-color: #ddd; + background-color: #eee; padding: 0.25em 0.25em 0.25em 0.5em; font-size: 150%; font-weight: bold; - border: solid 1px #aaa; + border: solid 1px #ccc; vertical-align: middle; } div#header img#logo { float: right; } +div#header a { + color: black; +} div#content { margin: 0.5em 0.5em; } div#blob { border: solid 1px black; } div.error { color: red; font-weight: bold; @@ -88,44 +91,76 @@ td.blob { white-space: pre; font-family: courier; font-size: 100%; background-color: white; } table.log td { white-space: nowrap; } table.commit-info { border-collapse: collapse; - margin-top: 1em; - + margin-top: 1.5em; } table.commit-info th { text-align: left; font-weight: normal; padding: 0.1em 1em 0.1em 0.1em; } table.commit-info td { font-weight: normal; padding: 0.1em 1em 0.1em 0.1em; } div.commit-subject { font-weight: bold; - font-size: 110%; - margin: 1em 0em 1em; + font-size: 125%; + margin: 1.5em 0em 0.5em 0em; + padding: 0em; } div.commit-msg { white-space: pre; - font-family: courier; + font-family: monospace; +} +table.diffstat { + border-collapse: collapse; + margin-top: 1.5em; +} +table.diffstat th { + font-weight: normal; + text-align: left; + text-decoration: underline; + padding: 0.1em 1em 0.1em 0.1em; font-size: 100%; } +table.diffstat td { + padding: 0.1em 1em 0.1em 0.1em; + font-size: 100%; +} +table.diffstat td span.modechange { + padding-left: 1em; + color: red; +} +table.diffstat td.add a { + color: green; +} +table.diffstat td.del a { + color: red; +} +table.diffstat td.upd a { + color: blue; +} +table.diffstat td.summary { + /* border-top: solid 1px black; */ + color: #888; + padding-top: 0.5em; +} .sha1 { font-family: courier; font-size: 90%; } .left { text-align: left; } .right { text-align: right; } @@ -66,24 +66,25 @@ extern void cgit_repo_config_cb(const char *name, const char *value); extern void cgit_querystring_cb(const char *name, const char *value); extern void *cgit_free_commitinfo(struct commitinfo *info); extern char *fmt(const char *format,...); extern void html(const char *txt); extern void htmlf(const char *format,...); extern void html_txt(char *txt); extern void html_attr(char *txt); extern void html_link_open(char *url, char *title, char *class); extern void html_link_close(void); +extern void html_filemode(unsigned short mode); extern int cgit_read_config(const char *filename, configfn fn); extern int cgit_parse_query(char *txt, configfn fn); extern struct commitinfo *cgit_parse_commit(struct commit *commit); extern void cache_prepare(struct cacheitem *item); extern int cache_lock(struct cacheitem *item); extern int cache_unlock(struct cacheitem *item); extern int cache_cancel_lock(struct cacheitem *item); extern int cache_exist(struct cacheitem *item); extern int cache_expired(struct cacheitem *item); @@ -119,32 +119,52 @@ static inline ssize_t xwrite(int fd, const void *buf, size_t len) /* * from git:cache.h */ /* Convert to/from hex/sha1 representation */ #define MINIMUM_ABBREV 4 #define DEFAULT_ABBREV 7 +extern const unsigned char null_sha1[20]; + extern int sha1_object_info(const unsigned char *, char *, unsigned long *); extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size); extern int get_sha1(const char *str, unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ +static inline int is_null_sha1(const unsigned char *sha1) +{ + return !memcmp(sha1, null_sha1, 20); +} +static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2) +{ + return memcmp(sha1, sha2, 20); +} +static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src) +{ + memcpy(sha_dst, sha_src, 20); +} +static inline void hashclr(unsigned char *hash) +{ + memset(hash, 0, 20); +} + + /* * from git:object.h */ struct object_list { struct object *item; struct object_list *next; }; struct object_refs { @@ -248,24 +268,79 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED, }; extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, int relative_date); typedef void (*topo_sort_set_fn_t)(struct commit*, void *data); typedef void* (*topo_sort_get_fn_t)(struct commit*); +/* + * from git:diffcore.h + */ + +struct diff_filespec { + unsigned char sha1[20]; + char *path; + void *data; + void *cnt_data; + unsigned long size; + int xfrm_flags; /* for use by the xfrm */ + unsigned short mode; /* file mode */ + unsigned sha1_valid : 1; /* if true, use sha1 and trust mode; + * if false, use the name and read from + * the filesystem. + */ +#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0) + unsigned should_free : 1; /* data should be free()'ed */ + unsigned should_munmap : 1; /* data should be munmap()'ed */ +}; + +struct diff_filepair { + struct diff_filespec *one; + struct diff_filespec *two; + unsigned short int score; + char status; /* M C R N D U (see Documentation/diff-format.txt) */ + unsigned source_stays : 1; /* all of R/C are copies */ + unsigned broken_pair : 1; + unsigned renamed_pair : 1; +}; + +#define DIFF_PAIR_UNMERGED(p) \ + (!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two)) + +#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair) + +#define DIFF_PAIR_BROKEN(p) \ + ( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \ + ((p)->broken_pair != 0) ) + +#define DIFF_PAIR_TYPE_CHANGED(p) \ + ((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode)) + +#define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode) + +extern void diff_free_filepair(struct diff_filepair *); + +extern int diff_unmodified_pair(struct diff_filepair *); + +struct diff_queue_struct { + struct diff_filepair **queue; + int alloc; + int nr; +}; + /* * from git:diff.h */ struct rev_info; struct diff_options; struct diff_queue_struct; typedef void (*change_fn_t)(struct diff_options *options, unsigned old_mode, unsigned new_mode, @@ -343,24 +418,50 @@ struct diff_options { enum color_diff { DIFF_RESET = 0, DIFF_PLAIN = 1, DIFF_METAINFO = 2, DIFF_FRAGINFO = 3, DIFF_FILE_OLD = 4, DIFF_FILE_NEW = 5, DIFF_COMMIT = 6, DIFF_WHITESPACE = 7, }; +extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, + const char *base, struct diff_options *opt); + +extern int diff_root_tree_sha1(const unsigned char *new, const char *base, + struct diff_options *opt); + +extern int git_diff_ui_config(const char *var, const char *value); +extern void diff_setup(struct diff_options *); +extern int diff_opt_parse(struct diff_options *, const char **, int); +extern int diff_setup_done(struct diff_options *); + + +extern void diffcore_std(struct diff_options *); +extern void diff_flush(struct diff_options*); + + +/* diff-raw status letters */ +#define DIFF_STATUS_ADDED 'A' +#define DIFF_STATUS_COPIED 'C' +#define DIFF_STATUS_DELETED 'D' +#define DIFF_STATUS_MODIFIED 'M' +#define DIFF_STATUS_RENAMED 'R' +#define DIFF_STATUS_TYPE_CHANGED 'T' +#define DIFF_STATUS_UNKNOWN 'X' +#define DIFF_STATUS_UNMERGED 'U' + /* * from git:refs.g */ typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data); extern int head_ref(each_ref_fn, void *); extern int for_each_ref(each_ref_fn, void *); extern int for_each_tag_ref(each_ref_fn, void *); extern int for_each_branch_ref(each_ref_fn, void *); extern int for_each_remote_ref(each_ref_fn, void *); @@ -449,14 +550,19 @@ struct rev_info { }; extern void init_revisions(struct rev_info *revs, const char *prefix); extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def); extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename); extern void prepare_revision_walk(struct rev_info *revs); extern struct commit *get_revision(struct rev_info *revs); +/* from git:log-tree.h */ + +int log_tree_commit(struct rev_info *, struct commit *); + + #endif /* GIT_H */ @@ -99,12 +99,32 @@ void html_link_open(char *url, char *title, char *class) } if (class) { html("' class='"); html_attr(class); } html("'>"); } void html_link_close(void) { html("</a>"); } + +void html_fileperm(unsigned short mode) +{ + htmlf("%c%c%c", (mode & 4 ? 'r' : '-'), + (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-')); +} + +void html_filemode(unsigned short mode) +{ + if (S_ISDIR(mode)) + html("d"); + else if (S_ISLNK(mode)) + html("l"); + else + html("-"); + html_fileperm(mode >> 6); + html_fileperm(mode >> 3); + html_fileperm(mode); +} + diff --git a/ui-commit.c b/ui-commit.c index 8916212..c5ee8e7 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -1,22 +1,127 @@ /* ui-commit.c: generate commit view * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" +int files = 0; + +void print_filepair(struct diff_filepair *pair) +{ + char *query; + char *class; + + switch (pair->status) { + case DIFF_STATUS_ADDED: + class = "add"; + break; + case DIFF_STATUS_COPIED: + class = "cpy"; + break; + case DIFF_STATUS_DELETED: + class = "del"; + break; + case DIFF_STATUS_MODIFIED: + class = "upd"; + break; + case DIFF_STATUS_RENAMED: + class = "mov"; + break; + case DIFF_STATUS_TYPE_CHANGED: + class = "typ"; + break; + case DIFF_STATUS_UNKNOWN: + class = "unk"; + break; + case DIFF_STATUS_UNMERGED: + class = "stg"; + break; + default: + die("bug: unhandled diff status %c", pair->status); + } + + html("<tr>"); + htmlf("<td class='mode'>"); + html_filemode(pair->two->mode); + if (pair->one->mode != pair->two->mode) { + html("<span class='modechange'>["); + html_filemode(pair->one->mode); + html("]</span>"); + } + htmlf("</td><td class='%s'>", class); + query = fmt("id=%s", sha1_to_hex(pair->two->sha1)); + html_link_open(cgit_pageurl(cgit_query_repo, "view", query), + NULL, NULL); + if (pair->status == DIFF_STATUS_COPIED || + pair->status == DIFF_STATUS_RENAMED) { + html_txt(pair->two->path); + htmlf("</a> (%s from ", pair->status == DIFF_STATUS_COPIED ? + "copied" : "renamed"); + query = fmt("id=%s", sha1_to_hex(pair->one->sha1)); + html_link_open(cgit_pageurl(cgit_query_repo, "view", query), + NULL, NULL); + html_txt(pair->one->path); + html("</a>)"); + } else { + html_txt(pair->two->path); + html("</a>"); + } + html("<td>"); + + //TODO: diffstat graph + + html("</td></tr>\n"); + files++; +} + +void diff_format_cb(struct diff_queue_struct *q, + struct diff_options *options, void *data) +{ + int i; + + for (i = 0; i < q->nr; i++) { + if (q->queue[i]->status == 'U') + continue; + print_filepair(q->queue[i]); + } +} + +void cgit_diffstat(struct commit *commit) +{ + struct diff_options opt; + int ret; + + diff_setup(&opt); + opt.output_format = DIFF_FORMAT_CALLBACK; + opt.detect_rename = 1; + opt.recursive = 1; + opt.format_callback = diff_format_cb; + diff_setup_done(&opt); + + if (commit->parents) + ret = diff_tree_sha1(commit->parents->item->object.sha1, + commit->object.sha1, + "", &opt); + else + ret = diff_root_tree_sha1(commit->object.sha1, "", &opt); + + diffcore_std(&opt); + diff_flush(&opt); +} + void cgit_print_commit(const char *hex) { struct commit *commit; struct commitinfo *info; struct commit_list *p; unsigned char sha1[20]; char *query; if (get_sha1(hex, sha1)) { cgit_print_error(fmt("Bad object id: %s", hex)); return; } @@ -53,14 +158,20 @@ void cgit_print_commit(const char *hex) query = fmt("id=%s", sha1_to_hex(p->item->object.sha1)); html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); htmlf("'>%s</a></td></tr>\n", sha1_to_hex(p->item->object.sha1)); } html("</table>\n"); html("<div class='commit-subject'>"); html_txt(info->subject); html("</div>"); html("<div class='commit-msg'>"); html_txt(info->msg); html("</div>"); + html("<table class='diffstat'>"); + html("<tr><th colspan='3'>Affected files</tr>\n"); + cgit_diffstat(commit); + htmlf("<tr><td colspan='3' class='summary'>" + "%d file%s changed</td></tr>\n", files, files > 1 ? "s" : ""); + html("</table>"); cgit_free_commitinfo(info); } |