1#include "builtin.h"
2#include "cache.h"
3#include "commit.h"
4#include "tag.h"
5#include "refs.h"
6
7static const char name_rev_usage[] =
8 "git-name-rev [--tags] ( --all | --stdin | committish [committish...] )\n";
9
10typedef struct rev_name {
11 const char *tip_name;
12 int merge_traversals;
13 int generation;
14} rev_name;
15
16static long cutoff = LONG_MAX;
17
18static void name_rev(struct commit *commit,
19 const char *tip_name, int merge_traversals, int generation,
20 int deref)
21{
22 struct rev_name *name = (struct rev_name *)commit->util;
23 struct commit_list *parents;
24 int parent_number = 1;
25
26 if (!commit->object.parsed)
27 parse_commit(commit);
28
29 if (commit->date < cutoff)
30 return;
31
32 if (deref) {
33 char *new_name = xmalloc(strlen(tip_name)+3);
34 strcpy(new_name, tip_name);
35 strcat(new_name, "^0");
36 tip_name = new_name;
37
38 if (generation)
39 die("generation: %d, but deref?", generation);
40 }
41
42 if (name == NULL) {
43 name = xmalloc(sizeof(rev_name));
44 commit->util = name;
45 goto copy_data;
46 } else if (name->merge_traversals > merge_traversals ||
47 (name->merge_traversals == merge_traversals &&
48 name->generation > generation)) {
49copy_data:
50 name->tip_name = tip_name;
51 name->merge_traversals = merge_traversals;
52 name->generation = generation;
53 } else
54 return;
55
56 for (parents = commit->parents;
57 parents;
58 parents = parents->next, parent_number++) {
59 if (parent_number > 1) {
60 int len = strlen(tip_name);
61 char *new_name = xmalloc(len + 8);
62
63 if (len > 2 && !strcmp(tip_name + len - 2, "^0"))
64 len -= 2;
65 if (generation > 0)
66 sprintf(new_name, "%.*s~%d^%d", len, tip_name,
67 generation, parent_number);
68 else
69 sprintf(new_name, "%.*s^%d", len, tip_name,
70 parent_number);
71
72 name_rev(parents->item, new_name,
73 merge_traversals + 1 , 0, 0);
74 } else {
75 name_rev(parents->item, tip_name, merge_traversals,
76 generation + 1, 0);
77 }
78 }
79}
80
81static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
82{
83 struct object *o = parse_object(sha1);
84 int tags_only = *(int*)cb_data;
85 int deref = 0;
86
87 if (tags_only && strncmp(path, "refs/tags/", 10))
88 return 0;
89
90 while (o && o->type == OBJ_TAG) {
91 struct tag *t = (struct tag *) o;
92 if (!t->tagged)
93 break; /* broken repository */
94 o = parse_object(t->tagged->sha1);
95 deref = 1;
96 }
97 if (o && o->type == OBJ_COMMIT) {
98 struct commit *commit = (struct commit *)o;
99
100 if (!strncmp(path, "refs/heads/", 11))
101 path = path + 11;
102 else if (!strncmp(path, "refs/", 5))
103 path = path + 5;
104
105 name_rev(commit, xstrdup(path), 0, 0, deref);
106 }
107 return 0;
108}
109
110/* returns a static buffer */
111static const char* get_rev_name(struct object *o)
112{
113 static char buffer[1024];
114 struct rev_name *n;
115 struct commit *c;
116
117 if (o->type != OBJ_COMMIT)
118 return "undefined";
119 c = (struct commit *) o;
120 n = c->util;
121 if (!n)
122 return "undefined";
123
124 if (!n->generation)
125 return n->tip_name;
126 else {
127 int len = strlen(n->tip_name);
128 if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
129 len -= 2;
130 snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
131 n->generation);
132
133 return buffer;
134 }
135}
136
137int cmd_name_rev(int argc, const char **argv, const char *prefix)
138{
139 struct object_array revs = { 0, 0, NULL };
140 int as_is = 0, all = 0, transform_stdin = 0;
141 int tags_only = 0;
142
143 git_config(git_default_config);
144
145 if (argc < 2)
146 usage(name_rev_usage);
147
148 for (--argc, ++argv; argc; --argc, ++argv) {
149 unsigned char sha1[20];
150 struct object *o;
151 struct commit *commit;
152
153 if (!as_is && (*argv)[0] == '-') {
154 if (!strcmp(*argv, "--")) {
155 as_is = 1;
156 continue;
157 } else if (!strcmp(*argv, "--tags")) {
158 tags_only = 1;
159 continue;
160 } else if (!strcmp(*argv, "--all")) {
161 if (argc > 1)
162 die("Specify either a list, or --all, not both!");
163 all = 1;
164 cutoff = 0;
165 continue;
166 } else if (!strcmp(*argv, "--stdin")) {
167 if (argc > 1)
168 die("Specify either a list, or --stdin, not both!");
169 transform_stdin = 1;
170 cutoff = 0;
171 continue;
172 }
173 usage(name_rev_usage);
174 }
175
176 if (get_sha1(*argv, sha1)) {
177 fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
178 *argv);
179 continue;
180 }
181
182 o = deref_tag(parse_object(sha1), *argv, 0);
183 if (!o || o->type != OBJ_COMMIT) {
184 fprintf(stderr, "Could not get commit for %s. Skipping.\n",
185 *argv);
186 continue;
187 }
188
189 commit = (struct commit *)o;
190
191 if (cutoff > commit->date)
192 cutoff = commit->date;
193
194 add_object_array((struct object *)commit, *argv, &revs);
195 }
196
197 for_each_ref(name_ref, &tags_only);
198
199 if (transform_stdin) {
200 char buffer[2048];
201 char *p, *p_start;
202
203 while (!feof(stdin)) {
204 int forty = 0;
205 p = fgets(buffer, sizeof(buffer), stdin);
206 if (!p)
207 break;
208
209 for (p_start = p; *p; p++) {
210#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
211 if (!ishex(*p))
212 forty = 0;
213 else if (++forty == 40 &&
214 !ishex(*(p+1))) {
215 unsigned char sha1[40];
216 const char *name = "undefined";
217 char c = *(p+1);
218
219 forty = 0;
220
221 *(p+1) = 0;
222 if (!get_sha1(p - 39, sha1)) {
223 struct object *o =
224 lookup_object(sha1);
225 if (o)
226 name = get_rev_name(o);
227 }
228 *(p+1) = c;
229
230 if (!strcmp(name, "undefined"))
231 continue;
232
233 fwrite(p_start, p - p_start + 1, 1,
234 stdout);
235 printf(" (%s)", name);
236 p_start = p + 1;
237 }
238 }
239
240 /* flush */
241 if (p_start != p)
242 fwrite(p_start, p - p_start, 1, stdout);
243 }
244 } else if (all) {
245 int i, max;
246
247 max = get_max_object_index();
248 for (i = 0; i < max; i++) {
249 struct object * obj = get_indexed_object(i);
250 if (!obj)
251 continue;
252 printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj));
253 }
254 } else {
255 int i;
256 for (i = 0; i < revs.nr; i++)
257 printf("%s %s\n",
258 revs.objects[i].name,
259 get_rev_name(revs.objects[i].item));
260 }
261
262 return 0;
263}
264