28
NULL
29
};
30
31
-#define STRCMP_SORT 0 /* must be zero */
32
-#define VERCMP_SORT 1
33
-#define SORT_MASK 0x7fff
34
-#define REVERSE_SORT 0x8000
35
-
36
-static int tag_sort;
37
-
31
static unsigned int colopts;
32
40
-static int match_pattern(const char **patterns, const char *ref)
41
-{
42
- /* no pattern means match everything */
43
- if (!*patterns)
44
- return 1;
45
- for (; *patterns; patterns++)
46
- if (!wildmatch(*patterns, ref, 0, NULL))
47
- return 1;
48
- return 0;
49
-}
50
-
51
-/*
52
- * This is currently duplicated in ref-filter.c, and will eventually be
53
- * removed as we port tag.c to use the ref-filter APIs.
54
- */
55
-static const unsigned char *match_points_at(const char *refname,
56
- const unsigned char *sha1,
57
- struct sha1_array *points_at)
58
-{
59
- const unsigned char *tagged_sha1 = NULL;
60
- struct object *obj;
61
-
62
- if (sha1_array_lookup(points_at, sha1) >= 0)
63
- return sha1;
64
- obj = parse_object(sha1);
65
- if (!obj)
66
- die(_("malformed object at '%s'"), refname);
67
- if (obj->type == OBJ_TAG)
68
- tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
69
- if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
70
- return tagged_sha1;
71
- return NULL;
72
-}
73
-
74
-static int in_commit_list(const struct commit_list *want, struct commit *c)
75
-{
76
- for (; want; want = want->next)
77
- if (!hashcmp(want->item->object.sha1, c->object.sha1))
78
- return 1;
79
- return 0;
80
-}
81
-
82
-/*
83
- * The entire code segment for supporting the --contains option has been
84
- * copied over to ref-filter.{c,h}. This will be deleted evetually when
85
- * we port tag.c to use ref-filter APIs.
86
- */
87
-enum contains_result {
88
- CONTAINS_UNKNOWN = -1,
89
- CONTAINS_NO = 0,
90
- CONTAINS_YES = 1
91
-};
92
-
93
-/*
94
- * Test whether the candidate or one of its parents is contained in the list.
95
- * Do not recurse to find out, though, but return -1 if inconclusive.
96
- */
97
-static enum contains_result contains_test(struct commit *candidate,
98
- const struct commit_list *want)
99
-{
100
- /* was it previously marked as containing a want commit? */
101
- if (candidate->object.flags & TMP_MARK)
102
- return 1;
103
- /* or marked as not possibly containing a want commit? */
104
- if (candidate->object.flags & UNINTERESTING)
105
- return 0;
106
- /* or are we it? */
107
- if (in_commit_list(want, candidate)) {
108
- candidate->object.flags |= TMP_MARK;
109
- return 1;
110
- }
111
-
112
- if (parse_commit(candidate) < 0)
113
- return 0;
114
-
115
- return -1;
116
-}
117
-
118
-/*
119
- * Mimicking the real stack, this stack lives on the heap, avoiding stack
120
- * overflows.
121
- *
122
- * At each recursion step, the stack items points to the commits whose
123
- * ancestors are to be inspected.
124
- */
125
-struct stack {
126
- int nr, alloc;
127
- struct stack_entry {
128
- struct commit *commit;
129
- struct commit_list *parents;
130
- } *stack;
131
-};
132
-
133
-static void push_to_stack(struct commit *candidate, struct stack *stack)
134
-{
135
- int index = stack->nr++;
136
- ALLOC_GROW(stack->stack, stack->nr, stack->alloc);
137
- stack->stack[index].commit = candidate;
138
- stack->stack[index].parents = candidate->parents;
139
-}
140
-
141
-static enum contains_result contains(struct commit *candidate,
142
- const struct commit_list *want)
143
-{
144
- struct stack stack = { 0, 0, NULL };
145
- int result = contains_test(candidate, want);
146
-
147
- if (result != CONTAINS_UNKNOWN)
148
- return result;
149
-
150
- push_to_stack(candidate, &stack);
151
- while (stack.nr) {
152
- struct stack_entry *entry = &stack.stack[stack.nr - 1];
153
- struct commit *commit = entry->commit;
154
- struct commit_list *parents = entry->parents;
155
-
156
- if (!parents) {
157
- commit->object.flags |= UNINTERESTING;
158
- stack.nr--;
159
- }
160
- /*
161
- * If we just popped the stack, parents->item has been marked,
162
- * therefore contains_test will return a meaningful 0 or 1.
163
- */
164
- else switch (contains_test(parents->item, want)) {
165
- case CONTAINS_YES:
166
- commit->object.flags |= TMP_MARK;
167
- stack.nr--;
168
- break;
169
- case CONTAINS_NO:
170
- entry->parents = parents->next;
171
- break;
172
- case CONTAINS_UNKNOWN:
173
- push_to_stack(parents->item, &stack);
174
- break;
175
- }
176
- }
177
- free(stack.stack);
178
- return contains_test(candidate, want);
179
-}
180
-
181
-/*
182
- * Currently modified and used in ref-filter as append_lines(), will
183
- * eventually be removed as we port tag.c to use ref-filter APIs.
184
- */
185
-static void show_tag_lines(const struct object_id *oid, int lines)
186
-{
187
- int i;
188
- unsigned long size;
189
- enum object_type type;
190
- char *buf, *sp, *eol;
191
- size_t len;
192
-
193
- buf = read_sha1_file(oid->hash, &type, &size);
194
- if (!buf)
195
- die_errno("unable to read object %s", oid_to_hex(oid));
196
- if (type != OBJ_COMMIT && type != OBJ_TAG)
197
- goto free_return;
198
- if (!size)
199
- die("an empty %s object %s?",
200
- typename(type), oid_to_hex(oid));
201
-
202
- /* skip header */
203
- sp = strstr(buf, "\n\n");
204
- if (!sp)
205
- goto free_return;
206
-
207
- /* only take up to "lines" lines, and strip the signature from a tag */
208
- if (type == OBJ_TAG)
209
- size = parse_signature(buf, size);
210
- for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
211
- if (i)
212
- printf("\n ");
213
- eol = memchr(sp, '\n', size - (sp - buf));
214
- len = eol ? eol - sp : size - (sp - buf);
215
- fwrite(sp, len, 1, stdout);
216
- if (!eol)
217
- break;
218
- sp = eol + 1;
219
- }
220
-free_return:
221
- free(buf);
222
-}
223
-
224
-static void ref_array_append(struct ref_array *array, const char *refname)
225
-{
226
- size_t len = strlen(refname);
227
- struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);
228
- memcpy(ref->refname, refname, len);
229
- ref->refname[len] = '\0';
230
- REALLOC_ARRAY(array->items, array->nr + 1);
231
- array->items[array->nr++] = ref;
232
-}
233
-
234
-static int show_reference(const char *refname, const struct object_id *oid,
235
- int flag, void *cb_data)
236
-{
237
- struct ref_filter_cbdata *data = cb_data;
238
- struct ref_array *array = data->array;
239
- struct ref_filter *filter = data->filter;
240
-
241
- if (match_pattern(filter->name_patterns, refname)) {
242
- if (filter->with_commit) {
243
- struct commit *commit;
244
-
245
- commit = lookup_commit_reference_gently(oid->hash, 1);
246
- if (!commit)
247
- return 0;
248
- if (!contains(commit, filter->with_commit))
249
- return 0;
250
- }
251
-
252
- if (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))
253
- return 0;
254
-
255
- if (!filter->lines) {
256
- if (tag_sort)
257
- ref_array_append(array, refname);
258
- else
259
- printf("%s\n", refname);
260
- return 0;
261
- }
262
- printf("%-15s ", refname);
263
- show_tag_lines(oid, filter->lines);
264
- putchar('\n');
265
- }
266
-
267
- return 0;
268
-}
269
-
270
-static int sort_by_version(const void *a_, const void *b_)
271
-{
272
- const struct ref_array_item *a = *((struct ref_array_item **)a_);
273
- const struct ref_array_item *b = *((struct ref_array_item **)b_);
274
- return versioncmp(a->refname, b->refname);
275
-}
276
-
277
-static int list_tags(struct ref_filter *filter, int sort)
33
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)
34
{
35
struct ref_array array;
280
- struct ref_filter_cbdata data;
36
+ char *format, *to_free = NULL;
37
+ int i;
38
39
memset(&array, 0, sizeof(array));
283
- data.array = &array;
284
- data.filter = filter;
40
41
if (filter->lines == -1)
42
filter->lines = 0;
43
289
- for_each_tag_ref(show_reference, &data);
290
- if (sort) {
291
- int i;
292
- if ((sort & SORT_MASK) == VERCMP_SORT)
293
- qsort(array.items, array.nr,
294
- sizeof(struct ref_array_item *), sort_by_version);
295
- if (sort & REVERSE_SORT)
296
- for (i = array.nr - 1; i >= 0; i--)
297
- printf("%s\n", array.items[i]->refname);
298
- else
299
- for (i = 0; i < array.nr; i++)
300
- printf("%s\n", array.items[i]->refname);
301
- ref_array_clear(&array);
302
- }
44
+ if (filter->lines) {
45
+ to_free = xstrfmt("%s %%(contents:lines=%d)",
46
+ "%(align:15)%(refname:short)%(end)", filter->lines);
47
+ format = to_free;
48
+ } else
49
+ format = "%(refname:short)";
50
+
51
+ verify_ref_format(format);
52
+ filter_refs(&array, filter, FILTER_REFS_TAGS);
53
+ ref_array_sort(sorting, &array);
54
+
55
+ for (i = 0; i < array.nr; i++)
56
+ show_ref_array_item(array.items[i], format, 0);
57
+ ref_array_clear(&array);
58
+ free(to_free);
59
+
60
return 0;
61
}
62
123
"Lines starting with '%c' will be kept; you may remove them"
124
" yourself if you want to.\n");
125
369
-/*
370
- * Parse a sort string, and return 0 if parsed successfully. Will return
371
- * non-zero when the sort string does not parse into a known type. If var is
372
- * given, the error message becomes a warning and includes information about
373
- * the configuration value.
374
- */
375
-static int parse_sort_string(const char *var, const char *arg, int *sort)
126
+/* Parse arg given and add it the ref_sorting array */
127
+static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
128
{
377
- int type = 0, flags = 0;
378
-
379
- if (skip_prefix(arg, "-", &arg))
380
- flags |= REVERSE_SORT;
129
+ struct ref_sorting *s;
130
+ int len;
131
382
- if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg))
383
- type = VERCMP_SORT;
384
- else
385
- type = STRCMP_SORT;
132
+ s = xcalloc(1, sizeof(*s));
133
+ s->next = *sorting_tail;
134
+ *sorting_tail = s;
135
387
- if (strcmp(arg, "refname")) {
388
- if (!var)
389
- return error(_("unsupported sort specification '%s'"), arg);
390
- else {
391
- warning(_("unsupported sort specification '%s' in variable '%s'"),
392
- var, arg);
393
- return -1;
394
- }
136
+ if (*arg == '-') {
137
+ s->reverse = 1;
138
+ arg++;
139
}
140
+ if (skip_prefix(arg, "version:", &arg) ||
141
+ skip_prefix(arg, "v:", &arg))
142
+ s->version = 1;
143
397
- *sort = (type | flags);
144
+ len = strlen(arg);
145
+ s->atom = parse_ref_filter_atom(arg, arg+len);
146
147
return 0;
148
}
150
static int git_tag_config(const char *var, const char *value, void *cb)
151
{
152
int status;
153
+ struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
154
155
if (!strcmp(var, "tag.sort")) {
156
if (!value)
157
return config_error_nonbool(var);
409
- parse_sort_string(var, value, &tag_sort);
158
+ parse_sorting_string(value, sorting_tail);
159
return 0;
160
}
161
313
return check_refname_format(sb->buf, 0);
314
}
315
567
-static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
568
-{
569
- int *sort = opt->value;
570
-
571
- return parse_sort_string(NULL, arg, sort);
572
-}
573
-
316
int cmd_tag(int argc, const char **argv, const char *prefix)
317
{
318
struct strbuf buf = STRBUF_INIT;
329
struct ref_transaction *transaction;
330
struct strbuf err = STRBUF_INIT;
331
struct ref_filter filter;
332
+ static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
333
struct option options[] = {
334
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
335
{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
356
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
357
OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
358
OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
616
- {
617
- OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"),
618
- PARSE_OPT_NONEG, parse_opt_sort
619
- },
359
+ OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
360
+ N_("field name to sort on"), &parse_opt_ref_sorting),
361
{
362
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
363
N_("print only tags of the object"), 0, parse_opt_object_name
365
OPT_END()
366
};
367
627
- git_config(git_tag_config, NULL);
368
+ git_config(git_tag_config, sorting_tail);
369
370
memset(&opt, 0, sizeof(opt));
371
memset(&filter, 0, sizeof(filter));
391
die(_("--column and -n are incompatible"));
392
colopts = 0;
393
}
394
+ if (!sorting)
395
+ sorting = ref_default_sorting();
396
if (cmdmode == 'l') {
397
int ret;
398
if (column_active(colopts)) {
401
copts.padding = 2;
402
run_column_filter(colopts, &copts);
403
}
661
- if (filter.lines != -1 && tag_sort)
662
- die(_("--sort and -n are incompatible"));
404
filter.name_patterns = argv;
664
- ret = list_tags(&filter, tag_sort);
405
+ ret = list_tags(&filter, sorting);
406
if (column_active(colopts))
407
stop_column_filter();
408
return ret;