Author: Lars Hjemli <hjemli@gmail.com>
Show list of modified files in ui-commit.c Compare current commit with 1.parent, and for each affected file display current filemode, old filemode if changed, current filename and source filename if it was a copy/rename. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
cgit.css | 51 +++++++++++++++++++++--- cgit.h | 1 git.h | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ html.c | 20 +++++++++ ui-commit.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/cgit.css b/cgit.css index 94372d60c895b5a3b24277055c2e3681500ab54a..ae304fae878c553e7dc558572d413188e9be817c 100644 --- a/cgit.css +++ b/cgit.css @@ -3,7 +3,7 @@ font-family: arial; font-size: normal; background: white; padding: 0em; - margin: 0.5em; + margin: 0.5em 1em; } @@ -50,16 +50,19 @@ } 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; @@ -97,8 +100,7 @@ } table.commit-info { border-collapse: collapse; - margin-top: 1em; - + margin-top: 1.5em; } table.commit-info th { text-align: left; @@ -111,13 +113,46 @@ 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; diff --git a/cgit.h b/cgit.h index 02cc26a4a4e698af4473572cb3ed0278e382e446..37584d6d5b4531c65375a357bfcf25ec5a091778 100644 --- a/cgit.h +++ b/cgit.h @@ -75,6 +75,7 @@ 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); diff --git a/git.h b/git.h index a3f977c5cb86b2ec066d50017f94fb6154741a2b..922a16754a7e70ff5e07a76bfdaca0335a18010f 100644 --- a/git.h +++ b/git.h @@ -128,6 +128,8 @@ /* 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); @@ -135,6 +137,24 @@ 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); +} + @@ -257,6 +277,61 @@ 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 @@ -352,6 +427,32 @@ 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' + /* @@ -456,6 +557,11 @@ 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 *); diff --git a/html.c b/html.c index fab67bd0b16b5c43bab558e20c7ecd7764d14711..8a6965970ae903dd434e490cfabbad27f39f900b 100644 --- a/html.c +++ b/html.c @@ -108,3 +108,23 @@ 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 8916212ef1bbacd4c39e312c22f200408302b678..c5ee8e70490e3e1c963c903b3e171ed131eb3775 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -8,6 +8,111 @@ */ #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; @@ -62,5 +167,11 @@ html("