author | Lars Hjemli <hjemli@gmail.com> | 2007-12-10 20:47:29 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2007-12-11 01:43:24 (UTC) |
commit | 620bb3e5e4ff87da740fe7232ba74330b5f862d4 (patch) (side-by-side diff) | |
tree | 152cc090cfc56c387393f6aa6666b9d87ad4a23a | |
parent | afcdd083dab81afef744e261d81a452698188c30 (diff) | |
download | cgit-620bb3e5e4ff87da740fe7232ba74330b5f862d4.zip cgit-620bb3e5e4ff87da740fe7232ba74330b5f862d4.tar.gz cgit-620bb3e5e4ff87da740fe7232ba74330b5f862d4.tar.bz2 |
Add plain patch view
The new view mimics the output from `git format-patch`, making it possible
to cherry-pick directly from cgit with something like `curl $url | git am`.
Inspired by a patch to `git-apply` by Mike Hommey:
http://thread.gmane.org/gmane.comp.version-control.git/67611/focus=67610
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | cgit.c | 5 | ||||
-rw-r--r-- | cgit.h | 2 | ||||
-rw-r--r-- | shared.c | 2 | ||||
-rw-r--r-- | ui-patch.c | 105 | ||||
-rw-r--r-- | ui-shared.c | 8 |
6 files changed, 122 insertions, 2 deletions
@@ -1,51 +1,51 @@ CGIT_VERSION = v0.7.1 CGIT_SCRIPT_NAME = cgit.cgi CGIT_SCRIPT_PATH = /var/www/htdocs/cgit CGIT_CONFIG = /etc/cgitrc CACHE_ROOT = /var/cache/cgit SHA1_HEADER = <openssl/sha.h> GIT_VER = 1.5.3.5 GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 # # Let the user override the above settings. # -include cgit.conf EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ - ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o + ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o ui-patch.o ifdef NEEDS_LIBICONV EXTLIBS += -liconv endif .PHONY: all git test install clean distclean emptycache force-version get-git all: cgit git VERSION: force-version @./gen-version.sh "$(CGIT_VERSION)" -include VERSION CFLAGS += -g -Wall -Igit CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' cgit: cgit.c $(OBJECTS) $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) $(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION git/xdiff/lib.a: | git git/libgit.a: | git @@ -46,64 +46,69 @@ static int cgit_prepare_cache(struct cacheitem *item) } static void cgit_print_repo_page(struct cacheitem *item) { char *title; int show_search; if (!cgit_query_head) cgit_query_head = cgit_repo->defbranch; if (chdir(cgit_repo->path)) { title = fmt("%s - %s", cgit_root_title, "Bad request"); cgit_print_docstart(title, item); cgit_print_pageheader(title, 0); cgit_print_error(fmt("Unable to scan repository: %s", strerror(errno))); cgit_print_docend(); return; } title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); show_search = 0; setenv("GIT_DIR", cgit_repo->path, 1); if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1, cgit_repobasename(cgit_repo->url), cgit_query_path, cgit_repo->snapshots ); return; } + if (cgit_cmd == CMD_PATCH) { + cgit_print_patch(cgit_query_sha1, item); + return; + } + if (cgit_cmd == CMD_BLOB) { cgit_print_blob(item, cgit_query_sha1, cgit_query_path); return; } show_search = (cgit_cmd == CMD_LOG); cgit_print_docstart(title, item); if (!cgit_cmd) { cgit_print_pageheader("summary", show_search); cgit_print_summary(); cgit_print_docend(); return; } cgit_print_pageheader(cgit_query_page, show_search); switch(cgit_cmd) { case CMD_LOG: cgit_print_log(cgit_query_sha1, cgit_query_ofs, cgit_max_commit_count, cgit_query_grep, cgit_query_search, cgit_query_path, 1); break; case CMD_TREE: cgit_print_tree(cgit_query_sha1, cgit_query_path); break; case CMD_COMMIT: cgit_print_commit(cgit_query_sha1); break; case CMD_REFS: cgit_print_refs(); break; case CMD_TAG: @@ -1,64 +1,65 @@ #ifndef CGIT_H #define CGIT_H #include <git-compat-util.h> #include <cache.h> #include <grep.h> #include <object.h> #include <tree.h> #include <commit.h> #include <tag.h> #include <diff.h> #include <diffcore.h> #include <refs.h> #include <revision.h> #include <log-tree.h> #include <archive.h> #include <xdiff/xdiff.h> #include <utf8.h> /* * The valid cgit repo-commands */ #define CMD_LOG 1 #define CMD_COMMIT 2 #define CMD_DIFF 3 #define CMD_TREE 4 #define CMD_BLOB 5 #define CMD_SNAPSHOT 6 #define CMD_TAG 7 #define CMD_REFS 8 +#define CMD_PATCH 9 /* * Dateformats used on misc. pages */ #define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" #define FMT_SHORTDATE "%Y-%m-%d" /* * Limits used for relative dates */ #define TM_MIN 60 #define TM_HOUR (TM_MIN * 60) #define TM_DAY (TM_HOUR * 24) #define TM_WEEK (TM_DAY * 7) #define TM_YEAR (TM_DAY * 365) #define TM_MONTH (TM_YEAR / 12.0) /* * Default encoding */ #define PAGE_ENCODING "UTF-8" typedef void (*configfn)(const char *name, const char *value); typedef void (*filepair_fn)(struct diff_filepair *pair); typedef void (*linediff_fn)(char *line, int len); struct cacheitem { char *name; struct stat st; int ttl; @@ -254,40 +255,41 @@ extern void cgit_commit_link(char *name, char *title, char *class, char *head, char *rev); extern void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path); extern void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename); extern void cgit_diff_link(char *name, char *title, char *class, char *head, char *new_rev, char *old_rev, char *path); extern void cgit_object_link(struct object *obj); extern void cgit_print_error(char *msg); extern void cgit_print_date(time_t secs, char *format); extern void cgit_print_age(time_t t, time_t max_relative, char *format); extern void cgit_print_docstart(char *title, struct cacheitem *item); extern void cgit_print_docend(); extern void cgit_print_pageheader(char *title, int show_search); extern void cgit_print_snapshot_start(const char *mimetype, const char *filename, struct cacheitem *item); extern void cgit_print_branches(int maxcount); extern void cgit_print_tags(int maxcount); extern void cgit_print_repolist(struct cacheitem *item); extern void cgit_print_summary(); extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager); extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); extern void cgit_print_tree(const char *rev, char *path); extern void cgit_print_commit(char *hex); extern void cgit_print_refs(); extern void cgit_print_tag(char *revname); extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); +extern void cgit_print_patch(char *hex, struct cacheitem *item); extern void cgit_print_snapshot(struct cacheitem *item, const char *head, const char *hex, const char *prefix, const char *filename, int snapshot); extern void cgit_print_snapshot_links(const char *repo, const char *head, const char *hex, int snapshots); extern int cgit_parse_snapshots_mask(const char *str); #endif /* CGIT_H */ @@ -41,65 +41,65 @@ int cgit_cache_dynamic_ttl = 5; int cgit_cache_static_ttl = -1; int cgit_cache_max_create_time = 5; int cgit_summary_log = 0; int cgit_summary_tags = 0; int cgit_summary_branches = 0; int cgit_renamelimit = -1; int cgit_max_msg_len = 60; int cgit_max_repodesc_len = 60; int cgit_max_commit_count = 50; int cgit_query_has_symref = 0; int cgit_query_has_sha1 = 0; char *cgit_querystring = NULL; char *cgit_query_repo = NULL; char *cgit_query_page = NULL; char *cgit_query_head = NULL; char *cgit_query_search = NULL; char *cgit_query_grep = NULL; char *cgit_query_sha1 = NULL; char *cgit_query_sha2 = NULL; char *cgit_query_path = NULL; char *cgit_query_name = NULL; int cgit_query_ofs = 0; int htmlfd = 0; int cgit_get_cmd_index(const char *cmd) { static char *cmds[] = {"log", "commit", "diff", "tree", "blob", - "snapshot", "tag", "refs", NULL}; + "snapshot", "tag", "refs", "patch", NULL}; int i; for(i = 0; cmds[i]; i++) if (!strcmp(cmd, cmds[i])) return i + 1; return 0; } int chk_zero(int result, char *msg) { if (result != 0) die("%s: %s", msg, strerror(errno)); return result; } int chk_positive(int result, char *msg) { if (result <= 0) die("%s: %s", msg, strerror(errno)); return result; } int chk_non_negative(int result, char *msg) { if (result < 0) die("%s: %s",msg, strerror(errno)); return result; } struct repoinfo *add_repo(const char *url) { struct repoinfo *ret; diff --git a/ui-patch.c b/ui-patch.c new file mode 100644 index 0000000..ef79c7c --- a/dev/null +++ b/ui-patch.c @@ -0,0 +1,105 @@ +/* ui-patch.c: generate patch view + * + * Copyright (C) 2007 Lars Hjemli + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" + +static void print_line(char *line, int len) +{ + char c = line[len-1]; + + line[len-1] = '\0'; + htmlf("%s\n", line); + line[len-1] = c; +} + +static void header(unsigned char *sha1, char *path1, int mode1, + unsigned char *sha2, char *path2, int mode2) +{ + char *abbrev1, *abbrev2; + int subproject; + + subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); + htmlf("diff --git a/%s b/%s\n", path1, path2); + + if (is_null_sha1(sha1)) + path1 = "dev/null"; + if (is_null_sha1(sha2)) + path2 = "dev/null"; + + if (mode1 == 0) + htmlf("new file mode %.6o\n", mode2); + + if (mode2 == 0) + htmlf("deleted file mode %.6o\n", mode1); + + if (!subproject) { + abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); + abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); + htmlf("index %s..%s", abbrev1, abbrev2); + free(abbrev1); + free(abbrev2); + if (mode1 != 0 && mode2 != 0) { + htmlf(" %.6o", mode1); + if (mode2 != mode1) + htmlf("..%.6o", mode2); + } + htmlf("\n--- a/%s\n", path1); + htmlf("+++ b/%s\n", path2); + } +} + +static void filepair_cb(struct diff_filepair *pair) +{ + header(pair->one->sha1, pair->one->path, pair->one->mode, + pair->two->sha1, pair->two->path, pair->two->mode); + if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { + if (S_ISGITLINK(pair->one->mode)) + print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); + if (S_ISGITLINK(pair->two->mode)) + print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); + return; + } + if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line)) + html("Error running diff"); +} + +void cgit_print_patch(char *hex, struct cacheitem *item) +{ + struct commit *commit; + struct commitinfo *info; + unsigned char sha1[20], old_sha1[20]; + char *patchname; + + if (!hex) + hex = cgit_query_head; + + if (get_sha1(hex, sha1)) { + cgit_print_error(fmt("Bad object id: %s", hex)); + return; + } + commit = lookup_commit_reference(sha1); + if (!commit) { + cgit_print_error(fmt("Bad commit reference: %s", hex)); + return; + } + info = cgit_parse_commit(commit); + hashcpy(old_sha1, commit->parents->item->object.sha1); + + patchname = fmt("%s.patch", sha1_to_hex(sha1)); + cgit_print_snapshot_start("text/plain", patchname, item); + htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); + htmlf("From: %s%s\n", info->author, info->author_email); + html("Date: "); + cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n"); + htmlf("Subject: %s\n\n%s", info->subject, info->msg); + html("---\n"); + cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); + html("--\n"); + htmlf("cgit %s\n", CGIT_VERSION); + cgit_free_commitinfo(info); +} diff --git a/ui-shared.c b/ui-shared.c index ece041c..60aa2e3 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -243,64 +243,70 @@ void cgit_refs_link(char *name, char *title, char *class, char *head, { reporevlink("refs", name, title, class, head, rev, path); } void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename) { reporevlink("snapshot", name, title, class, head, rev, archivename); } void cgit_diff_link(char *name, char *title, char *class, char *head, char *new_rev, char *old_rev, char *path) { char *delim; delim = repolink(title, class, "diff", head, path); if (new_rev && strcmp(new_rev, cgit_query_head)) { html(delim); html("id="); html_attr(new_rev); delim = "&"; } if (old_rev) { html(delim); html("id2="); html_attr(old_rev); } html("'>"); html_txt(name); html("</a>"); } +void cgit_patch_link(char *name, char *title, char *class, char *head, + char *rev) +{ + reporevlink("patch", name, title, class, head, rev, NULL); +} + void cgit_object_link(struct object *obj) { char *page, *arg, *url; if (obj->type == OBJ_COMMIT) { cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, cgit_query_head, sha1_to_hex(obj->sha1)); return; } else if (obj->type == OBJ_TREE) { page = "tree"; arg = "id"; } else if (obj->type == OBJ_TAG) { page = "tag"; arg = "id"; } else { page = "blob"; arg = "id"; } url = cgit_pageurl(cgit_query_repo, page, fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); html_link_open(url, NULL, NULL); htmlf("%s %s", typename(obj->type), sha1_to_hex(obj->sha1)); html_link_close(); } void cgit_print_date(time_t secs, char *format) { char buf[64]; struct tm *time; @@ -461,64 +467,66 @@ void cgit_print_pageheader(char *title, int show_search) static const char *default_info = "This is cgit, a fast webinterface for git repositories"; int header = 0; char *url; html("<table id='layout' summary=''>\n"); html("<tr><td id='sidebar'>\n"); html("<table class='sidebar' cellspacing='0' summary=''>\n"); html("<tr><td class='sidebar'>\n<a href='"); html_attr(cgit_rooturl()); htmlf("'><img src='%s' alt='cgit'/></a>\n", cgit_logo); html("</td></tr>\n<tr><td class='sidebar'>\n"); if (cgit_query_repo) { html("<h1 class='first'>"); html_txt(strrpart(cgit_repo->name, 20)); html("</h1>\n"); html_txt(cgit_repo->desc); if (cgit_repo->owner) { html("<h1>owner</h1>\n"); html_txt(cgit_repo->owner); } html("<h1>navigate</h1>\n"); reporevlink(NULL, "summary", NULL, "menu", cgit_query_head, NULL, NULL); cgit_log_link("log", NULL, "menu", cgit_query_head, NULL, NULL, 0, NULL, NULL); cgit_tree_link("tree", NULL, "menu", cgit_query_head, cgit_query_sha1, NULL); cgit_commit_link("commit", NULL, "menu", cgit_query_head, cgit_query_sha1); cgit_diff_link("diff", NULL, "menu", cgit_query_head, cgit_query_sha1, cgit_query_sha2, NULL); + cgit_patch_link("patch", NULL, "menu", cgit_query_head, + cgit_query_sha1); for_each_ref(print_archive_ref, &header); if (cgit_repo->clone_url || cgit_clone_prefix) { html("<h1>clone</h1>\n"); if (cgit_repo->clone_url) url = cgit_repo->clone_url; else url = fmt("%s%s", cgit_clone_prefix, cgit_repo->url); html("<a class='menu' href='"); html_attr(url); html("' title='"); html_attr(url); html("'>\n"); html_txt(strrpart(url, 20)); html("</a>\n"); } html("<h1>branch</h1>\n"); html("<form method='get' action=''>\n"); add_hidden_formfields(0, 1, cgit_query_page); // html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>"); html("<select name='h' onchange='this.form.submit();'>\n"); for_each_branch_ref(print_branch_option, cgit_query_head); html("</select>\n"); // html("</td><td>"); html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n"); // html("</td></tr></table>"); html("</form>\n"); html("<h1>search</h1>\n"); |