summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2009-07-31 14:55:27 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2009-07-31 15:41:21 (UTC)
commit46b7abed99e957008c01c02cf612aa526ba92f04 (patch) (unidiff)
tree2cdbfe5281c2ffa645def488d53db0a8b82842f4
parent18dfbdc099c1398016427b6fa7f1a1facb363998 (diff)
downloadcgit-46b7abed99e957008c01c02cf612aa526ba92f04.zip
cgit-46b7abed99e957008c01c02cf612aa526ba92f04.tar.gz
cgit-46b7abed99e957008c01c02cf612aa526ba92f04.tar.bz2
ui-tree: add support for source-filter option
This new option is used to specify an external command which will be executed when displaying blob content in the tree view. Blob content will be written to STDIN of the filter and STDOUT from the filter will be included verbatim in the html output from cgit. The file name of the blob will be passed as the only argument to the filter command. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt8
-rw-r--r--ui-tree.c18
4 files changed, 25 insertions, 4 deletions
diff --git a/cgit.c b/cgit.c
index 779a464..eb7b45d 100644
--- a/cgit.c
+++ b/cgit.c
@@ -79,48 +79,50 @@ void config_cb(const char *name, const char *value)
79 else if (!strcmp(name, "max-stats")) 79 else if (!strcmp(name, "max-stats"))
80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
81 else if (!strcmp(name, "cache-size")) 81 else if (!strcmp(name, "cache-size"))
82 ctx.cfg.cache_size = atoi(value); 82 ctx.cfg.cache_size = atoi(value);
83 else if (!strcmp(name, "cache-root")) 83 else if (!strcmp(name, "cache-root"))
84 ctx.cfg.cache_root = xstrdup(value); 84 ctx.cfg.cache_root = xstrdup(value);
85 else if (!strcmp(name, "cache-root-ttl")) 85 else if (!strcmp(name, "cache-root-ttl"))
86 ctx.cfg.cache_root_ttl = atoi(value); 86 ctx.cfg.cache_root_ttl = atoi(value);
87 else if (!strcmp(name, "cache-repo-ttl")) 87 else if (!strcmp(name, "cache-repo-ttl"))
88 ctx.cfg.cache_repo_ttl = atoi(value); 88 ctx.cfg.cache_repo_ttl = atoi(value);
89 else if (!strcmp(name, "cache-static-ttl")) 89 else if (!strcmp(name, "cache-static-ttl"))
90 ctx.cfg.cache_static_ttl = atoi(value); 90 ctx.cfg.cache_static_ttl = atoi(value);
91 else if (!strcmp(name, "cache-dynamic-ttl")) 91 else if (!strcmp(name, "cache-dynamic-ttl"))
92 ctx.cfg.cache_dynamic_ttl = atoi(value); 92 ctx.cfg.cache_dynamic_ttl = atoi(value);
93 else if (!strcmp(name, "embedded")) 93 else if (!strcmp(name, "embedded"))
94 ctx.cfg.embedded = atoi(value); 94 ctx.cfg.embedded = atoi(value);
95 else if (!strcmp(name, "max-message-length")) 95 else if (!strcmp(name, "max-message-length"))
96 ctx.cfg.max_msg_len = atoi(value); 96 ctx.cfg.max_msg_len = atoi(value);
97 else if (!strcmp(name, "max-repodesc-length")) 97 else if (!strcmp(name, "max-repodesc-length"))
98 ctx.cfg.max_repodesc_len = atoi(value); 98 ctx.cfg.max_repodesc_len = atoi(value);
99 else if (!strcmp(name, "max-repo-count")) 99 else if (!strcmp(name, "max-repo-count"))
100 ctx.cfg.max_repo_count = atoi(value); 100 ctx.cfg.max_repo_count = atoi(value);
101 else if (!strcmp(name, "max-commit-count")) 101 else if (!strcmp(name, "max-commit-count"))
102 ctx.cfg.max_commit_count = atoi(value); 102 ctx.cfg.max_commit_count = atoi(value);
103 else if (!strcmp(name, "source-filter"))
104 ctx.cfg.source_filter = new_filter(value, 1);
103 else if (!strcmp(name, "summary-log")) 105 else if (!strcmp(name, "summary-log"))
104 ctx.cfg.summary_log = atoi(value); 106 ctx.cfg.summary_log = atoi(value);
105 else if (!strcmp(name, "summary-branches")) 107 else if (!strcmp(name, "summary-branches"))
106 ctx.cfg.summary_branches = atoi(value); 108 ctx.cfg.summary_branches = atoi(value);
107 else if (!strcmp(name, "summary-tags")) 109 else if (!strcmp(name, "summary-tags"))
108 ctx.cfg.summary_tags = atoi(value); 110 ctx.cfg.summary_tags = atoi(value);
109 else if (!strcmp(name, "agefile")) 111 else if (!strcmp(name, "agefile"))
110 ctx.cfg.agefile = xstrdup(value); 112 ctx.cfg.agefile = xstrdup(value);
111 else if (!strcmp(name, "renamelimit")) 113 else if (!strcmp(name, "renamelimit"))
112 ctx.cfg.renamelimit = atoi(value); 114 ctx.cfg.renamelimit = atoi(value);
113 else if (!strcmp(name, "robots")) 115 else if (!strcmp(name, "robots"))
114 ctx.cfg.robots = xstrdup(value); 116 ctx.cfg.robots = xstrdup(value);
115 else if (!strcmp(name, "clone-prefix")) 117 else if (!strcmp(name, "clone-prefix"))
116 ctx.cfg.clone_prefix = xstrdup(value); 118 ctx.cfg.clone_prefix = xstrdup(value);
117 else if (!strcmp(name, "local-time")) 119 else if (!strcmp(name, "local-time"))
118 ctx.cfg.local_time = atoi(value); 120 ctx.cfg.local_time = atoi(value);
119 else if (!strcmp(name, "repo.group")) 121 else if (!strcmp(name, "repo.group"))
120 ctx.cfg.repo_group = xstrdup(value); 122 ctx.cfg.repo_group = xstrdup(value);
121 else if (!strcmp(name, "repo.url")) 123 else if (!strcmp(name, "repo.url"))
122 ctx.repo = cgit_add_repo(value); 124 ctx.repo = cgit_add_repo(value);
123 else if (!strcmp(name, "repo.name")) 125 else if (!strcmp(name, "repo.name"))
124 ctx.repo->name = xstrdup(value); 126 ctx.repo->name = xstrdup(value);
125 else if (ctx.repo && !strcmp(name, "repo.path")) 127 else if (ctx.repo && !strcmp(name, "repo.path"))
126 ctx.repo->path = trim_end(value, '/'); 128 ctx.repo->path = trim_end(value, '/');
diff --git a/cgit.h b/cgit.h
index d0fff1f..f9cf0df 100644
--- a/cgit.h
+++ b/cgit.h
@@ -162,48 +162,49 @@ struct cgit_config {
162 int cache_size; 162 int cache_size;
163 int cache_dynamic_ttl; 163 int cache_dynamic_ttl;
164 int cache_max_create_time; 164 int cache_max_create_time;
165 int cache_repo_ttl; 165 int cache_repo_ttl;
166 int cache_root_ttl; 166 int cache_root_ttl;
167 int cache_static_ttl; 167 int cache_static_ttl;
168 int embedded; 168 int embedded;
169 int enable_index_links; 169 int enable_index_links;
170 int enable_log_filecount; 170 int enable_log_filecount;
171 int enable_log_linecount; 171 int enable_log_linecount;
172 int local_time; 172 int local_time;
173 int max_repo_count; 173 int max_repo_count;
174 int max_commit_count; 174 int max_commit_count;
175 int max_lock_attempts; 175 int max_lock_attempts;
176 int max_msg_len; 176 int max_msg_len;
177 int max_repodesc_len; 177 int max_repodesc_len;
178 int max_stats; 178 int max_stats;
179 int nocache; 179 int nocache;
180 int noheader; 180 int noheader;
181 int renamelimit; 181 int renamelimit;
182 int snapshots; 182 int snapshots;
183 int summary_branches; 183 int summary_branches;
184 int summary_log; 184 int summary_log;
185 int summary_tags; 185 int summary_tags;
186 struct cgit_filter *source_filter;
186}; 187};
187 188
188struct cgit_page { 189struct cgit_page {
189 time_t modified; 190 time_t modified;
190 time_t expires; 191 time_t expires;
191 size_t size; 192 size_t size;
192 char *mimetype; 193 char *mimetype;
193 char *charset; 194 char *charset;
194 char *filename; 195 char *filename;
195 char *etag; 196 char *etag;
196 char *title; 197 char *title;
197 int status; 198 int status;
198 char *statusmsg; 199 char *statusmsg;
199}; 200};
200 201
201struct cgit_context { 202struct cgit_context {
202 struct cgit_query qry; 203 struct cgit_query qry;
203 struct cgit_config cfg; 204 struct cgit_config cfg;
204 struct cgit_repo *repo; 205 struct cgit_repo *repo;
205 struct cgit_page page; 206 struct cgit_page page;
206}; 207};
207 208
208struct cgit_snapshot_format { 209struct cgit_snapshot_format {
209 const char *suffix; 210 const char *suffix;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index a207fe0..d420ad4 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -177,48 +177,56 @@ robots::
177 177
178root-desc:: 178root-desc::
179 Text printed below the heading on the repository index page. Default 179 Text printed below the heading on the repository index page. Default
180 value: "a fast webinterface for the git dscm". 180 value: "a fast webinterface for the git dscm".
181 181
182root-readme:: 182root-readme::
183 The content of the file specified with this option will be included 183 The content of the file specified with this option will be included
184 verbatim below the "about" link on the repository index page. Default 184 verbatim below the "about" link on the repository index page. Default
185 value: none. 185 value: none.
186 186
187root-title:: 187root-title::
188 Text printed as heading on the repository index page. Default value: 188 Text printed as heading on the repository index page. Default value:
189 "Git Repository Browser". 189 "Git Repository Browser".
190 190
191snapshots:: 191snapshots::
192 Text which specifies the default (and allowed) set of snapshot formats 192 Text which specifies the default (and allowed) set of snapshot formats
193 supported by cgit. The value is a space-separated list of zero or more 193 supported by cgit. The value is a space-separated list of zero or more
194 of the following values: 194 of the following values:
195 "tar" uncompressed tar-file 195 "tar" uncompressed tar-file
196 "tar.gz"gzip-compressed tar-file 196 "tar.gz"gzip-compressed tar-file
197 "tar.bz2"bzip-compressed tar-file 197 "tar.bz2"bzip-compressed tar-file
198 "zip" zip-file 198 "zip" zip-file
199 Default value: none. 199 Default value: none.
200 200
201source-filter::
202 Specifies a command which will be invoked to format plaintext blobs
203 in the tree view. The command will get the blob content on its STDIN
204 and the name of the blob as its only command line argument. The STDOUT
205 from the command will be included verbatim as the blob contents, i.e.
206 this can be used to implement e.g. syntax highlighting. Default value:
207 none.
208
201summary-branches:: 209summary-branches::
202 Specifies the number of branches to display in the repository "summary" 210 Specifies the number of branches to display in the repository "summary"
203 view. Default value: "10". 211 view. Default value: "10".
204 212
205summary-log:: 213summary-log::
206 Specifies the number of log entries to display in the repository 214 Specifies the number of log entries to display in the repository
207 "summary" view. Default value: "10". 215 "summary" view. Default value: "10".
208 216
209summary-tags:: 217summary-tags::
210 Specifies the number of tags to display in the repository "summary" 218 Specifies the number of tags to display in the repository "summary"
211 view. Default value: "10". 219 view. Default value: "10".
212 220
213virtual-root:: 221virtual-root::
214 Url which, if specified, will be used as root for all cgit links. It 222 Url which, if specified, will be used as root for all cgit links. It
215 will also cause cgit to generate 'virtual urls', i.e. urls like 223 will also cause cgit to generate 'virtual urls', i.e. urls like
216 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 224 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
217 value: none. 225 value: none.
218 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 226 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
219 same kind of virtual urls, so this option will probably be deprecated. 227 same kind of virtual urls, so this option will probably be deprecated.
220 228
221REPOSITORY SETTINGS 229REPOSITORY SETTINGS
222------------------- 230-------------------
223repo.clone-url:: 231repo.clone-url::
224 A list of space-separated urls which can be used to clone this repo. 232 A list of space-separated urls which can be used to clone this repo.
diff --git a/ui-tree.c b/ui-tree.c
index 553dbaa..816e121 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,120 +1,130 @@
1/* ui-tree.c: functions for tree output 1/* ui-tree.c: functions for tree output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include <ctype.h> 9#include <ctype.h>
10#include "cgit.h" 10#include "cgit.h"
11#include "html.h" 11#include "html.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13 13
14char *curr_rev; 14char *curr_rev;
15char *match_path; 15char *match_path;
16int header = 0; 16int header = 0;
17 17
18static void print_text_buffer(char *buf, unsigned long size) 18static void print_text_buffer(const char *name, char *buf, unsigned long size)
19{ 19{
20 unsigned long lineno, idx; 20 unsigned long lineno, idx;
21 const char *numberfmt = 21 const char *numberfmt =
22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; 22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n";
23 23
24 html("<table summary='blob content' class='blob'>\n"); 24 html("<table summary='blob content' class='blob'>\n");
25 if (ctx.cfg.source_filter) {
26 html("<tr><td class='lines'><pre><code>");
27 ctx.cfg.source_filter->argv[1] = xstrdup(name);
28 cgit_open_filter(ctx.cfg.source_filter);
29 write(STDOUT_FILENO, buf, size);
30 cgit_close_filter(ctx.cfg.source_filter);
31 html("</code></pre></td></tr></table>\n");
32 return;
33 }
34
25 html("<tr><td class='linenumbers'><pre>"); 35 html("<tr><td class='linenumbers'><pre>");
26 idx = 0; 36 idx = 0;
27 lineno = 0; 37 lineno = 0;
28 38
29 if (size) { 39 if (size) {
30 htmlf(numberfmt, ++lineno); 40 htmlf(numberfmt, ++lineno);
31 while(idx < size - 1) { // skip absolute last newline 41 while(idx < size - 1) { // skip absolute last newline
32 if (buf[idx] == '\n') 42 if (buf[idx] == '\n')
33 htmlf(numberfmt, ++lineno); 43 htmlf(numberfmt, ++lineno);
34 idx++; 44 idx++;
35 } 45 }
36 } 46 }
37 html("</pre></td>\n"); 47 html("</pre></td>\n");
38 html("<td class='lines'><pre><code>"); 48 html("<td class='lines'><pre><code>");
39 html_txt(buf); 49 html_txt(buf);
40 html("</code></pre></td></tr></table>\n"); 50 html("</code></pre></td></tr></table>\n");
41} 51}
42 52
43#define ROWLEN 32 53#define ROWLEN 32
44 54
45static void print_binary_buffer(char *buf, unsigned long size) 55static void print_binary_buffer(char *buf, unsigned long size)
46{ 56{
47 unsigned long ofs, idx; 57 unsigned long ofs, idx;
48 static char ascii[ROWLEN + 1]; 58 static char ascii[ROWLEN + 1];
49 59
50 html("<table summary='blob content' class='bin-blob'>\n"); 60 html("<table summary='blob content' class='bin-blob'>\n");
51 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); 61 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
52 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { 62 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
53 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); 63 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs);
54 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 64 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
55 htmlf("%*s%02x", 65 htmlf("%*s%02x",
56 idx == 16 ? 4 : 1, "", 66 idx == 16 ? 4 : 1, "",
57 buf[idx] & 0xff); 67 buf[idx] & 0xff);
58 html(" </td><td class='hex'>"); 68 html(" </td><td class='hex'>");
59 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 69 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
60 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; 70 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
61 ascii[idx] = '\0'; 71 ascii[idx] = '\0';
62 html_txt(ascii); 72 html_txt(ascii);
63 html("</td></tr>\n"); 73 html("</td></tr>\n");
64 } 74 }
65 html("</table>\n"); 75 html("</table>\n");
66} 76}
67 77
68static void print_object(const unsigned char *sha1, char *path) 78static void print_object(const unsigned char *sha1, char *path, const char *basename)
69{ 79{
70 enum object_type type; 80 enum object_type type;
71 char *buf; 81 char *buf;
72 unsigned long size; 82 unsigned long size;
73 83
74 type = sha1_object_info(sha1, &size); 84 type = sha1_object_info(sha1, &size);
75 if (type == OBJ_BAD) { 85 if (type == OBJ_BAD) {
76 cgit_print_error(fmt("Bad object name: %s", 86 cgit_print_error(fmt("Bad object name: %s",
77 sha1_to_hex(sha1))); 87 sha1_to_hex(sha1)));
78 return; 88 return;
79 } 89 }
80 90
81 buf = read_sha1_file(sha1, &type, &size); 91 buf = read_sha1_file(sha1, &type, &size);
82 if (!buf) { 92 if (!buf) {
83 cgit_print_error(fmt("Error reading object %s", 93 cgit_print_error(fmt("Error reading object %s",
84 sha1_to_hex(sha1))); 94 sha1_to_hex(sha1)));
85 return; 95 return;
86 } 96 }
87 97
88 html(" ("); 98 html(" (");
89 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 99 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
90 curr_rev, path); 100 curr_rev, path);
91 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); 101 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1));
92 102
93 if (buffer_is_binary(buf, size)) 103 if (buffer_is_binary(buf, size))
94 print_binary_buffer(buf, size); 104 print_binary_buffer(buf, size);
95 else 105 else
96 print_text_buffer(buf, size); 106 print_text_buffer(basename, buf, size);
97} 107}
98 108
99 109
100static int ls_item(const unsigned char *sha1, const char *base, int baselen, 110static int ls_item(const unsigned char *sha1, const char *base, int baselen,
101 const char *pathname, unsigned int mode, int stage, 111 const char *pathname, unsigned int mode, int stage,
102 void *cbdata) 112 void *cbdata)
103{ 113{
104 char *name; 114 char *name;
105 char *fullpath; 115 char *fullpath;
106 enum object_type type; 116 enum object_type type;
107 unsigned long size = 0; 117 unsigned long size = 0;
108 118
109 name = xstrdup(pathname); 119 name = xstrdup(pathname);
110 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 120 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
111 ctx.qry.path ? "/" : "", name); 121 ctx.qry.path ? "/" : "", name);
112 122
113 if (!S_ISGITLINK(mode)) { 123 if (!S_ISGITLINK(mode)) {
114 type = sha1_object_info(sha1, &size); 124 type = sha1_object_info(sha1, &size);
115 if (type == OBJ_BAD) { 125 if (type == OBJ_BAD) {
116 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 126 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
117 name, 127 name,
118 sha1_to_hex(sha1)); 128 sha1_to_hex(sha1));
119 return 0; 129 return 0;
120 } 130 }
@@ -192,49 +202,49 @@ static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
192 const char *pathname, unsigned mode, int stage, 202 const char *pathname, unsigned mode, int stage,
193 void *cbdata) 203 void *cbdata)
194{ 204{
195 static int state; 205 static int state;
196 static char buffer[PATH_MAX]; 206 static char buffer[PATH_MAX];
197 char *url; 207 char *url;
198 208
199 if (state == 0) { 209 if (state == 0) {
200 memcpy(buffer, base, baselen); 210 memcpy(buffer, base, baselen);
201 strcpy(buffer+baselen, pathname); 211 strcpy(buffer+baselen, pathname);
202 url = cgit_pageurl(ctx.qry.repo, "tree", 212 url = cgit_pageurl(ctx.qry.repo, "tree",
203 fmt("h=%s&amp;path=%s", curr_rev, buffer)); 213 fmt("h=%s&amp;path=%s", curr_rev, buffer));
204 html("/"); 214 html("/");
205 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, 215 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head,
206 curr_rev, buffer); 216 curr_rev, buffer);
207 217
208 if (strcmp(match_path, buffer)) 218 if (strcmp(match_path, buffer))
209 return READ_TREE_RECURSIVE; 219 return READ_TREE_RECURSIVE;
210 220
211 if (S_ISDIR(mode)) { 221 if (S_ISDIR(mode)) {
212 state = 1; 222 state = 1;
213 ls_head(); 223 ls_head();
214 return READ_TREE_RECURSIVE; 224 return READ_TREE_RECURSIVE;
215 } else { 225 } else {
216 print_object(sha1, buffer); 226 print_object(sha1, buffer, pathname);
217 return 0; 227 return 0;
218 } 228 }
219 } 229 }
220 ls_item(sha1, base, baselen, pathname, mode, stage, NULL); 230 ls_item(sha1, base, baselen, pathname, mode, stage, NULL);
221 return 0; 231 return 0;
222} 232}
223 233
224 234
225/* 235/*
226 * Show a tree or a blob 236 * Show a tree or a blob
227 * rev: the commit pointing at the root tree object 237 * rev: the commit pointing at the root tree object
228 * path: path to tree or blob 238 * path: path to tree or blob
229 */ 239 */
230void cgit_print_tree(const char *rev, char *path) 240void cgit_print_tree(const char *rev, char *path)
231{ 241{
232 unsigned char sha1[20]; 242 unsigned char sha1[20];
233 struct commit *commit; 243 struct commit *commit;
234 const char *paths[] = {path, NULL}; 244 const char *paths[] = {path, NULL};
235 245
236 if (!rev) 246 if (!rev)
237 rev = ctx.qry.head; 247 rev = ctx.qry.head;
238 248
239 curr_rev = xstrdup(rev); 249 curr_rev = xstrdup(rev);
240 if (get_sha1(rev, sha1)) { 250 if (get_sha1(rev, sha1)) {