blame: move fake-commit-related methods to libgit

Signed-off-by: Jeff Smith <whydoubt@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Smith committed May 24, 2017 at 00:15 UTC 072bf4321fb484a8eabb315c031f47883336fba2
3 files changed +205 -199
blame.c
+202 -1
@@ -1,3 +1,6 @@
1 +#include "cache.h"
2 +#include "refs.h"
3 +#include "cache-tree.h"
4 #include "blame.h"
5
6 void blame_origin_decref(struct blame_origin *o)
@@ -28,7 +31,7 @@ void blame_origin_decref(struct blame_origin *o)
31 * get_origin() to obtain shared, refcounted copy instead of calling
32 * this function directly.
33 */
31 -struct blame_origin *make_origin(struct commit *commit, const char *path)
34 +static struct blame_origin *make_origin(struct commit *commit, const char *path)
35 {
36 struct blame_origin *o;
37 FLEX_ALLOC_STR(o, path, path);
@@ -60,3 +63,201 @@ struct blame_origin *get_origin(struct commit *commit, const char *path)
63 }
64 return make_origin(commit, path);
65 }
66 +
67 +
68 +
69 +static void verify_working_tree_path(struct commit *work_tree, const char *path)
70 +{
71 + struct commit_list *parents;
72 + int pos;
73 +
74 + for (parents = work_tree->parents; parents; parents = parents->next) {
75 + const struct object_id *commit_oid = &parents->item->object.oid;
76 + struct object_id blob_oid;
77 + unsigned mode;
78 +
79 + if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
80 + sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
81 + return;
82 + }
83 +
84 + pos = cache_name_pos(path, strlen(path));
85 + if (pos >= 0)
86 + ; /* path is in the index */
87 + else if (-1 - pos < active_nr &&
88 + !strcmp(active_cache[-1 - pos]->name, path))
89 + ; /* path is in the index, unmerged */
90 + else
91 + die("no such path '%s' in HEAD", path);
92 +}
93 +
94 +static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
95 +{
96 + struct commit *parent;
97 +
98 + parent = lookup_commit_reference(oid->hash);
99 + if (!parent)
100 + die("no such commit %s", oid_to_hex(oid));
101 + return &commit_list_insert(parent, tail)->next;
102 +}
103 +
104 +static void append_merge_parents(struct commit_list **tail)
105 +{
106 + int merge_head;
107 + struct strbuf line = STRBUF_INIT;
108 +
109 + merge_head = open(git_path_merge_head(), O_RDONLY);
110 + if (merge_head < 0) {
111 + if (errno == ENOENT)
112 + return;
113 + die("cannot open '%s' for reading", git_path_merge_head());
114 + }
115 +
116 + while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
117 + struct object_id oid;
118 + if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
119 + die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
120 + tail = append_parent(tail, &oid);
121 + }
122 + close(merge_head);
123 + strbuf_release(&line);
124 +}
125 +
126 +/*
127 + * This isn't as simple as passing sb->buf and sb->len, because we
128 + * want to transfer ownership of the buffer to the commit (so we
129 + * must use detach).
130 + */
131 +static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
132 +{
133 + size_t len;
134 + void *buf = strbuf_detach(sb, &len);
135 + set_commit_buffer(c, buf, len);
136 +}
137 +
138 +/*
139 + * Prepare a dummy commit that represents the work tree (or staged) item.
140 + * Note that annotating work tree item never works in the reverse.
141 + */
142 +struct commit *fake_working_tree_commit(struct diff_options *opt,
143 + const char *path,
144 + const char *contents_from)
145 +{
146 + struct commit *commit;
147 + struct blame_origin *origin;
148 + struct commit_list **parent_tail, *parent;
149 + struct object_id head_oid;
150 + struct strbuf buf = STRBUF_INIT;
151 + const char *ident;
152 + time_t now;
153 + int size, len;
154 + struct cache_entry *ce;
155 + unsigned mode;
156 + struct strbuf msg = STRBUF_INIT;
157 +
158 + read_cache();
159 + time(&now);
160 + commit = alloc_commit_node();
161 + commit->object.parsed = 1;
162 + commit->date = now;
163 + parent_tail = &commit->parents;
164 +
165 + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
166 + die("no such ref: HEAD");
167 +
168 + parent_tail = append_parent(parent_tail, &head_oid);
169 + append_merge_parents(parent_tail);
170 + verify_working_tree_path(commit, path);
171 +
172 + origin = make_origin(commit, path);
173 +
174 + ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
175 + strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
176 + for (parent = commit->parents; parent; parent = parent->next)
177 + strbuf_addf(&msg, "parent %s\n",
178 + oid_to_hex(&parent->item->object.oid));
179 + strbuf_addf(&msg,
180 + "author %s\n"
181 + "committer %s\n\n"
182 + "Version of %s from %s\n",
183 + ident, ident, path,
184 + (!contents_from ? path :
185 + (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
186 + set_commit_buffer_from_strbuf(commit, &msg);
187 +
188 + if (!contents_from || strcmp("-", contents_from)) {
189 + struct stat st;
190 + const char *read_from;
191 + char *buf_ptr;
192 + unsigned long buf_len;
193 +
194 + if (contents_from) {
195 + if (stat(contents_from, &st) < 0)
196 + die_errno("Cannot stat '%s'", contents_from);
197 + read_from = contents_from;
198 + }
199 + else {
200 + if (lstat(path, &st) < 0)
201 + die_errno("Cannot lstat '%s'", path);
202 + read_from = path;
203 + }
204 + mode = canon_mode(st.st_mode);
205 +
206 + switch (st.st_mode & S_IFMT) {
207 + case S_IFREG:
208 + if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
209 + textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
210 + strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
211 + else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
212 + die_errno("cannot open or read '%s'", read_from);
213 + break;
214 + case S_IFLNK:
215 + if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
216 + die_errno("cannot readlink '%s'", read_from);
217 + break;
218 + default:
219 + die("unsupported file type %s", read_from);
220 + }
221 + }
222 + else {
223 + /* Reading from stdin */
224 + mode = 0;
225 + if (strbuf_read(&buf, 0, 0) < 0)
226 + die_errno("failed to read from stdin");
227 + }
228 + convert_to_git(path, buf.buf, buf.len, &buf, 0);
229 + origin->file.ptr = buf.buf;
230 + origin->file.size = buf.len;
231 + pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
232 +
233 + /*
234 + * Read the current index, replace the path entry with
235 + * origin->blob_sha1 without mucking with its mode or type
236 + * bits; we are not going to write this index out -- we just
237 + * want to run "diff-index --cached".
238 + */
239 + discard_cache();
240 + read_cache();
241 +
242 + len = strlen(path);
243 + if (!mode) {
244 + int pos = cache_name_pos(path, len);
245 + if (0 <= pos)
246 + mode = active_cache[pos]->ce_mode;
247 + else
248 + /* Let's not bother reading from HEAD tree */
249 + mode = S_IFREG | 0644;
250 + }
251 + size = cache_entry_size(len);
252 + ce = xcalloc(1, size);
253 + oidcpy(&ce->oid, &origin->blob_oid);
254 + memcpy(ce->name, path, len);
255 + ce->ce_flags = create_ce_flags(0);
256 + ce->ce_namelen = len;
257 + ce->ce_mode = create_ce_mode(mode);
258 + add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
259 +
260 + cache_tree_invalidate_path(&the_index, path);
261 +
262 + return commit;
263 +}
blame.h
+3 -1
@@ -6,6 +6,7 @@
6 #include "xdiff-interface.h"
7 #include "revision.h"
8 #include "prio-queue.h"
9 +#include "diff.h"
10
11 /*
12 * One blob in a commit that is being suspected
@@ -152,7 +153,8 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)
153 }
154 extern void blame_origin_decref(struct blame_origin *o);
155
155 -extern struct blame_origin *make_origin(struct commit *commit, const char *path);
156 extern struct blame_origin *get_origin(struct commit *commit, const char *path);
157
158 +extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from);
159 +
160 #endif /* BLAME_H */
builtin/blame.c
-197
@@ -16,7 +16,6 @@
16 #include "revision.h"
17 #include "quote.h"
18 #include "xdiff-interface.h"
19 -#include "cache-tree.h"
19 #include "string-list.h"
20 #include "mailmap.h"
21 #include "mergesort.h"
@@ -2006,202 +2005,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)
2005 return git_default_config(var, value, cb);
2006 }
2007
2009 -static void verify_working_tree_path(struct commit *work_tree, const char *path)
2010 -{
2011 - struct commit_list *parents;
2012 - int pos;
2013 -
2014 - for (parents = work_tree->parents; parents; parents = parents->next) {
2015 - const struct object_id *commit_oid = &parents->item->object.oid;
2016 - struct object_id blob_oid;
2017 - unsigned mode;
2018 -
2019 - if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
2020 - sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
2021 - return;
2022 - }
2023 -
2024 - pos = cache_name_pos(path, strlen(path));
2025 - if (pos >= 0)
2026 - ; /* path is in the index */
2027 - else if (-1 - pos < active_nr &&
2028 - !strcmp(active_cache[-1 - pos]->name, path))
2029 - ; /* path is in the index, unmerged */
2030 - else
2031 - die("no such path '%s' in HEAD", path);
2032 -}
2033 -
2034 -static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
2035 -{
2036 - struct commit *parent;
2037 -
2038 - parent = lookup_commit_reference(oid->hash);
2039 - if (!parent)
2040 - die("no such commit %s", oid_to_hex(oid));
2041 - return &commit_list_insert(parent, tail)->next;
2042 -}
2043 -
2044 -static void append_merge_parents(struct commit_list **tail)
2045 -{
2046 - int merge_head;
2047 - struct strbuf line = STRBUF_INIT;
2048 -
2049 - merge_head = open(git_path_merge_head(), O_RDONLY);
2050 - if (merge_head < 0) {
2051 - if (errno == ENOENT)
2052 - return;
2053 - die("cannot open '%s' for reading", git_path_merge_head());
2054 - }
2055 -
2056 - while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
2057 - struct object_id oid;
2058 - if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
2059 - die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
2060 - tail = append_parent(tail, &oid);
2061 - }
2062 - close(merge_head);
2063 - strbuf_release(&line);
2064 -}
2065 -
2066 -/*
2067 - * This isn't as simple as passing sb->buf and sb->len, because we
2068 - * want to transfer ownership of the buffer to the commit (so we
2069 - * must use detach).
2070 - */
2071 -static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
2072 -{
2073 - size_t len;
2074 - void *buf = strbuf_detach(sb, &len);
2075 - set_commit_buffer(c, buf, len);
2076 -}
2077 -
2078 -/*
2079 - * Prepare a dummy commit that represents the work tree (or staged) item.
2080 - * Note that annotating work tree item never works in the reverse.
2081 - */
2082 -static struct commit *fake_working_tree_commit(struct diff_options *opt,
2083 - const char *path,
2084 - const char *contents_from)
2085 -{
2086 - struct commit *commit;
2087 - struct blame_origin *origin;
2088 - struct commit_list **parent_tail, *parent;
2089 - struct object_id head_oid;
2090 - struct strbuf buf = STRBUF_INIT;
2091 - const char *ident;
2092 - time_t now;
2093 - int size, len;
2094 - struct cache_entry *ce;
2095 - unsigned mode;
2096 - struct strbuf msg = STRBUF_INIT;
2097 -
2098 - read_cache();
2099 - time(&now);
2100 - commit = alloc_commit_node();
2101 - commit->object.parsed = 1;
2102 - commit->date = now;
2103 - parent_tail = &commit->parents;
2104 -
2105 - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
2106 - die("no such ref: HEAD");
2107 -
2108 - parent_tail = append_parent(parent_tail, &head_oid);
2109 - append_merge_parents(parent_tail);
2110 - verify_working_tree_path(commit, path);
2111 -
2112 - origin = make_origin(commit, path);
2113 -
2114 - ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
2115 - strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
2116 - for (parent = commit->parents; parent; parent = parent->next)
2117 - strbuf_addf(&msg, "parent %s\n",
2118 - oid_to_hex(&parent->item->object.oid));
2119 - strbuf_addf(&msg,
2120 - "author %s\n"
2121 - "committer %s\n\n"
2122 - "Version of %s from %s\n",
2123 - ident, ident, path,
2124 - (!contents_from ? path :
2125 - (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
2126 - set_commit_buffer_from_strbuf(commit, &msg);
2127 -
2128 - if (!contents_from || strcmp("-", contents_from)) {
2129 - struct stat st;
2130 - const char *read_from;
2131 - char *buf_ptr;
2132 - unsigned long buf_len;
2133 -
2134 - if (contents_from) {
2135 - if (stat(contents_from, &st) < 0)
2136 - die_errno("Cannot stat '%s'", contents_from);
2137 - read_from = contents_from;
2138 - }
2139 - else {
2140 - if (lstat(path, &st) < 0)
2141 - die_errno("Cannot lstat '%s'", path);
2142 - read_from = path;
2143 - }
2144 - mode = canon_mode(st.st_mode);
2145 -
2146 - switch (st.st_mode & S_IFMT) {
2147 - case S_IFREG:
2148 - if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
2149 - textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
2150 - strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
2151 - else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
2152 - die_errno("cannot open or read '%s'", read_from);
2153 - break;
2154 - case S_IFLNK:
2155 - if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
2156 - die_errno("cannot readlink '%s'", read_from);
2157 - break;
2158 - default:
2159 - die("unsupported file type %s", read_from);
2160 - }
2161 - }
2162 - else {
2163 - /* Reading from stdin */
2164 - mode = 0;
2165 - if (strbuf_read(&buf, 0, 0) < 0)
2166 - die_errno("failed to read from stdin");
2167 - }
2168 - convert_to_git(path, buf.buf, buf.len, &buf, 0);
2169 - origin->file.ptr = buf.buf;
2170 - origin->file.size = buf.len;
2171 - pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
2172 -
2173 - /*
2174 - * Read the current index, replace the path entry with
2175 - * origin->blob_sha1 without mucking with its mode or type
2176 - * bits; we are not going to write this index out -- we just
2177 - * want to run "diff-index --cached".
2178 - */
2179 - discard_cache();
2180 - read_cache();
2181 -
2182 - len = strlen(path);
2183 - if (!mode) {
2184 - int pos = cache_name_pos(path, len);
2185 - if (0 <= pos)
2186 - mode = active_cache[pos]->ce_mode;
2187 - else
2188 - /* Let's not bother reading from HEAD tree */
2189 - mode = S_IFREG | 0644;
2190 - }
2191 - size = cache_entry_size(len);
2192 - ce = xcalloc(1, size);
2193 - oidcpy(&ce->oid, &origin->blob_oid);
2194 - memcpy(ce->name, path, len);
2195 - ce->ce_flags = create_ce_flags(0);
2196 - ce->ce_namelen = len;
2197 - ce->ce_mode = create_ce_mode(mode);
2198 - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
2199 -
2200 - cache_tree_invalidate_path(&the_index, path);
2201 -
2202 - return commit;
2203 -}
2204 -
2008 static struct commit *find_single_final(struct rev_info *revs,
2009 const char **name_p)
2010 {