]>
Commit | Line | Data |
---|---|---|
072bf432 JS |
1 | #include "cache.h" |
2 | #include "refs.h" | |
3 | #include "cache-tree.h" | |
f5dd754c JS |
4 | #include "blame.h" |
5 | ||
6 | void blame_origin_decref(struct blame_origin *o) | |
7 | { | |
8 | if (o && --o->refcnt <= 0) { | |
9 | struct blame_origin *p, *l = NULL; | |
10 | if (o->previous) | |
11 | blame_origin_decref(o->previous); | |
12 | free(o->file.ptr); | |
13 | /* Should be present exactly once in commit chain */ | |
14 | for (p = o->commit->util; p; l = p, p = p->next) { | |
15 | if (p == o) { | |
16 | if (l) | |
17 | l->next = p->next; | |
18 | else | |
19 | o->commit->util = p->next; | |
20 | free(o); | |
21 | return; | |
22 | } | |
23 | } | |
24 | die("internal error in blame_origin_decref"); | |
25 | } | |
26 | } | |
27 | ||
28 | /* | |
29 | * Given a commit and a path in it, create a new origin structure. | |
30 | * The callers that add blame to the scoreboard should use | |
31 | * get_origin() to obtain shared, refcounted copy instead of calling | |
32 | * this function directly. | |
33 | */ | |
072bf432 | 34 | static struct blame_origin *make_origin(struct commit *commit, const char *path) |
f5dd754c JS |
35 | { |
36 | struct blame_origin *o; | |
37 | FLEX_ALLOC_STR(o, path, path); | |
38 | o->commit = commit; | |
39 | o->refcnt = 1; | |
40 | o->next = commit->util; | |
41 | commit->util = o; | |
42 | return o; | |
43 | } | |
44 | ||
45 | /* | |
46 | * Locate an existing origin or create a new one. | |
47 | * This moves the origin to front position in the commit util list. | |
48 | */ | |
49 | struct blame_origin *get_origin(struct commit *commit, const char *path) | |
50 | { | |
51 | struct blame_origin *o, *l; | |
52 | ||
53 | for (o = commit->util, l = NULL; o; l = o, o = o->next) { | |
54 | if (!strcmp(o->path, path)) { | |
55 | /* bump to front */ | |
56 | if (l) { | |
57 | l->next = o->next; | |
58 | o->next = commit->util; | |
59 | commit->util = o; | |
60 | } | |
61 | return blame_origin_incref(o); | |
62 | } | |
63 | } | |
64 | return make_origin(commit, path); | |
65 | } | |
072bf432 JS |
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 | } |