16
16
*/
17
17
#define CUTOFF_DATE_SLOP 86400
18
18
19
- typedef struct rev_name {
20
- const char * tip_name ;
19
+ struct rev_name {
20
+ char * tip_name ;
21
21
timestamp_t taggerdate ;
22
22
int generation ;
23
23
int distance ;
24
24
int from_tag ;
25
- } rev_name ;
25
+ };
26
26
27
- define_commit_slab (commit_rev_name , struct rev_name * );
27
+ define_commit_slab (commit_rev_name , struct rev_name );
28
28
29
29
static timestamp_t cutoff = TIME_MAX ;
30
30
static struct commit_rev_name rev_names ;
31
31
32
32
/* How many generations are maximally preferred over _one_ merge traversal? */
33
33
#define MERGE_TRAVERSAL_WEIGHT 65535
34
34
35
- static struct rev_name * get_commit_rev_name ( struct commit * commit )
35
+ static int is_valid_rev_name ( const struct rev_name * name )
36
36
{
37
- struct rev_name * * slot = commit_rev_name_peek (& rev_names , commit );
38
-
39
- return slot ? * slot : NULL ;
37
+ return name && (name -> generation || name -> tip_name );
40
38
}
41
39
42
- static void set_commit_rev_name ( struct commit * commit , struct rev_name * name )
40
+ static struct rev_name * get_commit_rev_name ( const struct commit * commit )
43
41
{
44
- * commit_rev_name_at (& rev_names , commit ) = name ;
42
+ struct rev_name * name = commit_rev_name_peek (& rev_names , commit );
43
+
44
+ return is_valid_rev_name (name ) ? name : NULL ;
45
45
}
46
46
47
47
static int is_better_name (struct rev_name * name ,
@@ -81,28 +81,54 @@ static int is_better_name(struct rev_name *name,
81
81
}
82
82
83
83
static struct rev_name * create_or_update_name (struct commit * commit ,
84
- const char * tip_name ,
85
84
timestamp_t taggerdate ,
86
85
int generation , int distance ,
87
86
int from_tag )
88
87
{
89
- struct rev_name * name = get_commit_rev_name (commit );
90
-
91
- if (name == NULL ) {
92
- name = xmalloc (sizeof (* name ));
93
- set_commit_rev_name (commit , name );
94
- goto copy_data ;
95
- } else if (is_better_name (name , taggerdate , distance , from_tag )) {
96
- copy_data :
97
- name -> tip_name = tip_name ;
98
- name -> taggerdate = taggerdate ;
99
- name -> generation = generation ;
100
- name -> distance = distance ;
101
- name -> from_tag = from_tag ;
102
-
103
- return name ;
104
- } else
105
- return NULL ;
88
+ struct rev_name * name = commit_rev_name_at (& rev_names , commit );
89
+
90
+ if (is_valid_rev_name (name )) {
91
+ if (!is_better_name (name , taggerdate , distance , from_tag ))
92
+ return NULL ;
93
+
94
+ /*
95
+ * This string might still be shared with ancestors
96
+ * (generation > 0). We can release it here regardless,
97
+ * because the new name that has just won will be better
98
+ * for them as well, so name_rev() will replace these
99
+ * stale pointers when it processes the parents.
100
+ */
101
+ if (!name -> generation )
102
+ free (name -> tip_name );
103
+ }
104
+
105
+ name -> taggerdate = taggerdate ;
106
+ name -> generation = generation ;
107
+ name -> distance = distance ;
108
+ name -> from_tag = from_tag ;
109
+
110
+ return name ;
111
+ }
112
+
113
+ static char * get_parent_name (const struct rev_name * name , int parent_number )
114
+ {
115
+ struct strbuf sb = STRBUF_INIT ;
116
+ size_t len ;
117
+
118
+ strip_suffix (name -> tip_name , "^0" , & len );
119
+ if (name -> generation > 0 ) {
120
+ strbuf_grow (& sb , len +
121
+ 1 + decimal_width (name -> generation ) +
122
+ 1 + decimal_width (parent_number ));
123
+ strbuf_addf (& sb , "%.*s~%d^%d" , (int )len , name -> tip_name ,
124
+ name -> generation , parent_number );
125
+ } else {
126
+ strbuf_grow (& sb , len +
127
+ 1 + decimal_width (parent_number ));
128
+ strbuf_addf (& sb , "%.*s^%d" , (int )len , name -> tip_name ,
129
+ parent_number );
130
+ }
131
+ return strbuf_detach (& sb , NULL );
106
132
}
107
133
108
134
static void name_rev (struct commit * start_commit ,
@@ -113,20 +139,20 @@ static void name_rev(struct commit *start_commit,
113
139
struct commit * commit ;
114
140
struct commit * * parents_to_queue = NULL ;
115
141
size_t parents_to_queue_nr , parents_to_queue_alloc = 0 ;
116
- char * to_free = NULL ;
142
+ struct rev_name * start_name ;
117
143
118
144
parse_commit (start_commit );
119
145
if (start_commit -> date < cutoff )
120
146
return ;
121
147
122
- if (deref )
123
- tip_name = to_free = xstrfmt ("%s^0" , tip_name );
124
-
125
- if (!create_or_update_name (start_commit , tip_name , taggerdate , 0 , 0 ,
126
- from_tag )) {
127
- free (to_free );
148
+ start_name = create_or_update_name (start_commit , taggerdate , 0 , 0 ,
149
+ from_tag );
150
+ if (!start_name )
128
151
return ;
129
- }
152
+ if (deref )
153
+ start_name -> tip_name = xstrfmt ("%s^0" , tip_name );
154
+ else
155
+ start_name -> tip_name = xstrdup (tip_name );
130
156
131
157
memset (& queue , 0 , sizeof (queue )); /* Use the prio_queue as LIFO */
132
158
prio_queue_put (& queue , start_commit );
@@ -142,38 +168,31 @@ static void name_rev(struct commit *start_commit,
142
168
parents ;
143
169
parents = parents -> next , parent_number ++ ) {
144
170
struct commit * parent = parents -> item ;
145
- const char * new_name ;
171
+ struct rev_name * parent_name ;
146
172
int generation , distance ;
147
173
148
174
parse_commit (parent );
149
175
if (parent -> date < cutoff )
150
176
continue ;
151
177
152
178
if (parent_number > 1 ) {
153
- size_t len ;
154
-
155
- strip_suffix (name -> tip_name , "^0" , & len );
156
- if (name -> generation > 0 )
157
- new_name = xstrfmt ("%.*s~%d^%d" ,
158
- (int )len ,
159
- name -> tip_name ,
160
- name -> generation ,
161
- parent_number );
162
- else
163
- new_name = xstrfmt ("%.*s^%d" , (int )len ,
164
- name -> tip_name ,
165
- parent_number );
166
179
generation = 0 ;
167
180
distance = name -> distance + MERGE_TRAVERSAL_WEIGHT ;
168
181
} else {
169
- new_name = name -> tip_name ;
170
182
generation = name -> generation + 1 ;
171
183
distance = name -> distance + 1 ;
172
184
}
173
185
174
- if (create_or_update_name (parent , new_name , taggerdate ,
175
- generation , distance ,
176
- from_tag )) {
186
+ parent_name = create_or_update_name (parent , taggerdate ,
187
+ generation ,
188
+ distance , from_tag );
189
+ if (parent_name ) {
190
+ if (parent_number > 1 )
191
+ parent_name -> tip_name =
192
+ get_parent_name (name ,
193
+ parent_number );
194
+ else
195
+ parent_name -> tip_name = name -> tip_name ;
177
196
ALLOC_GROW (parents_to_queue ,
178
197
parents_to_queue_nr + 1 ,
179
198
parents_to_queue_alloc );
@@ -228,20 +247,29 @@ static struct tip_table {
228
247
struct tip_table_entry {
229
248
struct object_id oid ;
230
249
const char * refname ;
250
+ struct commit * commit ;
251
+ timestamp_t taggerdate ;
252
+ unsigned int from_tag :1 ;
253
+ unsigned int deref :1 ;
231
254
} * table ;
232
255
int nr ;
233
256
int alloc ;
234
257
int sorted ;
235
258
} tip_table ;
236
259
237
260
static void add_to_tip_table (const struct object_id * oid , const char * refname ,
238
- int shorten_unambiguous )
261
+ int shorten_unambiguous , struct commit * commit ,
262
+ timestamp_t taggerdate , int from_tag , int deref )
239
263
{
240
264
refname = name_ref_abbrev (refname , shorten_unambiguous );
241
265
242
266
ALLOC_GROW (tip_table .table , tip_table .nr + 1 , tip_table .alloc );
243
267
oidcpy (& tip_table .table [tip_table .nr ].oid , oid );
244
268
tip_table .table [tip_table .nr ].refname = xstrdup (refname );
269
+ tip_table .table [tip_table .nr ].commit = commit ;
270
+ tip_table .table [tip_table .nr ].taggerdate = taggerdate ;
271
+ tip_table .table [tip_table .nr ].from_tag = from_tag ;
272
+ tip_table .table [tip_table .nr ].deref = deref ;
245
273
tip_table .nr ++ ;
246
274
tip_table .sorted = 0 ;
247
275
}
@@ -252,12 +280,30 @@ static int tipcmp(const void *a_, const void *b_)
252
280
return oidcmp (& a -> oid , & b -> oid );
253
281
}
254
282
283
+ static int cmp_by_tag_and_age (const void * a_ , const void * b_ )
284
+ {
285
+ const struct tip_table_entry * a = a_ , * b = b_ ;
286
+ int cmp ;
287
+
288
+ /* Prefer tags. */
289
+ cmp = b -> from_tag - a -> from_tag ;
290
+ if (cmp )
291
+ return cmp ;
292
+
293
+ /* Older is better. */
294
+ if (a -> taggerdate < b -> taggerdate )
295
+ return -1 ;
296
+ return a -> taggerdate != b -> taggerdate ;
297
+ }
298
+
255
299
static int name_ref (const char * path , const struct object_id * oid , int flags , void * cb_data )
256
300
{
257
301
struct object * o = parse_object (the_repository , oid );
258
302
struct name_ref_data * data = cb_data ;
259
303
int can_abbreviate_output = data -> tags_only && data -> name_only ;
260
304
int deref = 0 ;
305
+ int from_tag = 0 ;
306
+ struct commit * commit = NULL ;
261
307
timestamp_t taggerdate = TIME_MAX ;
262
308
263
309
if (data -> tags_only && !starts_with (path , "refs/tags/" ))
@@ -306,8 +352,6 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
306
352
return 0 ;
307
353
}
308
354
309
- add_to_tip_table (oid , path , can_abbreviate_output );
310
-
311
355
while (o && o -> type == OBJ_TAG ) {
312
356
struct tag * t = (struct tag * ) o ;
313
357
if (!t -> tagged )
@@ -317,17 +361,35 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
317
361
taggerdate = t -> date ;
318
362
}
319
363
if (o && o -> type == OBJ_COMMIT ) {
320
- struct commit * commit = (struct commit * )o ;
321
- int from_tag = starts_with (path , "refs/tags/" );
322
-
364
+ commit = (struct commit * )o ;
365
+ from_tag = starts_with (path , "refs/tags/" );
323
366
if (taggerdate == TIME_MAX )
324
367
taggerdate = commit -> date ;
325
- path = name_ref_abbrev (path , can_abbreviate_output );
326
- name_rev (commit , xstrdup (path ), taggerdate , from_tag , deref );
327
368
}
369
+
370
+ add_to_tip_table (oid , path , can_abbreviate_output , commit , taggerdate ,
371
+ from_tag , deref );
328
372
return 0 ;
329
373
}
330
374
375
+ static void name_tips (void )
376
+ {
377
+ int i ;
378
+
379
+ /*
380
+ * Try to set better names first, so that worse ones spread
381
+ * less.
382
+ */
383
+ QSORT (tip_table .table , tip_table .nr , cmp_by_tag_and_age );
384
+ for (i = 0 ; i < tip_table .nr ; i ++ ) {
385
+ struct tip_table_entry * e = & tip_table .table [i ];
386
+ if (e -> commit ) {
387
+ name_rev (e -> commit , e -> refname , e -> taggerdate ,
388
+ e -> from_tag , e -> deref );
389
+ }
390
+ }
391
+ }
392
+
331
393
static const unsigned char * nth_tip_table_ent (size_t ix , void * table_ )
332
394
{
333
395
struct tip_table_entry * table = table_ ;
@@ -357,11 +419,11 @@ static const char *get_exact_ref_match(const struct object *o)
357
419
static const char * get_rev_name (const struct object * o , struct strbuf * buf )
358
420
{
359
421
struct rev_name * n ;
360
- struct commit * c ;
422
+ const struct commit * c ;
361
423
362
424
if (o -> type != OBJ_COMMIT )
363
425
return get_exact_ref_match (o );
364
- c = (struct commit * ) o ;
426
+ c = (const struct commit * ) o ;
365
427
n = get_commit_rev_name (c );
366
428
if (!n )
367
429
return NULL ;
@@ -540,6 +602,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
540
602
cutoff = TIME_MIN ;
541
603
}
542
604
for_each_ref (name_ref , & data );
605
+ name_tips ();
543
606
544
607
if (transform_stdin ) {
545
608
char buffer [2048 ];
0 commit comments