51
51
52
52
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
53
53
54
+ /*
55
+ * To accommodate common filesystem limitations, where the loose refs' file
56
+ * names must not exceed `NAME_MAX`, the labels generated by `git rebase
57
+ * --rebase-merges` need to be truncated if the corresponding commit subjects
58
+ * are too long.
59
+ * Add some margin to stay clear from reaching `NAME_MAX`.
60
+ */
61
+ #define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16)
62
+
54
63
static const char sign_off_header [] = "Signed-off-by: " ;
55
64
static const char cherry_picked_prefix [] = "(cherry picked from commit " ;
56
65
@@ -5352,6 +5361,7 @@ struct label_state {
5352
5361
struct oidmap commit2label ;
5353
5362
struct hashmap labels ;
5354
5363
struct strbuf buf ;
5364
+ int max_label_length ;
5355
5365
};
5356
5366
5357
5367
static const char * label_oid (struct object_id * oid , const char * label ,
@@ -5408,6 +5418,8 @@ static const char *label_oid(struct object_id *oid, const char *label,
5408
5418
}
5409
5419
} else {
5410
5420
struct strbuf * buf = & state -> buf ;
5421
+ int label_is_utf8 = 1 ; /* start with this assumption */
5422
+ size_t max_len = buf -> len + state -> max_label_length ;
5411
5423
5412
5424
/*
5413
5425
* Sanitize labels by replacing non-alpha-numeric characters
@@ -5416,14 +5428,34 @@ static const char *label_oid(struct object_id *oid, const char *label,
5416
5428
*
5417
5429
* Note that we retain non-ASCII UTF-8 characters (identified
5418
5430
* via the most significant bit). They should be all acceptable
5419
- * in file names. We do not validate the UTF-8 here, that's not
5420
- * the job of this function.
5431
+ * in file names.
5432
+ *
5433
+ * As we will use the labels as names of (loose) refs, it is
5434
+ * vital that the name not be longer than the maximum component
5435
+ * size of the file system (`NAME_MAX`). We are careful to
5436
+ * truncate the label accordingly, allowing for the `.lock`
5437
+ * suffix and for the label to be UTF-8 encoded (i.e. we avoid
5438
+ * truncating in the middle of a character).
5421
5439
*/
5422
- for (; * label ; label ++ )
5423
- if ((* label & 0x80 ) || isalnum (* label ))
5440
+ for (; * label && buf -> len + 1 < max_len ; label ++ )
5441
+ if (isalnum (* label ) ||
5442
+ (!label_is_utf8 && (* label & 0x80 )))
5424
5443
strbuf_addch (buf , * label );
5444
+ else if (* label & 0x80 ) {
5445
+ const char * p = label ;
5446
+
5447
+ utf8_width (& p , NULL );
5448
+ if (p ) {
5449
+ if (buf -> len + (p - label ) > max_len )
5450
+ break ;
5451
+ strbuf_add (buf , label , p - label );
5452
+ label = p - 1 ;
5453
+ } else {
5454
+ label_is_utf8 = 0 ;
5455
+ strbuf_addch (buf , * label );
5456
+ }
5425
5457
/* avoid leading dash and double-dashes */
5426
- else if (buf -> len && buf -> buf [buf -> len - 1 ] != '-' )
5458
+ } else if (buf -> len && buf -> buf [buf -> len - 1 ] != '-' )
5427
5459
strbuf_addch (buf , '-' );
5428
5460
if (!buf -> len ) {
5429
5461
strbuf_addstr (buf , "rev-" );
@@ -5485,14 +5517,17 @@ static int make_script_with_merges(struct pretty_print_context *pp,
5485
5517
struct string_entry * entry ;
5486
5518
struct oidset interesting = OIDSET_INIT , child_seen = OIDSET_INIT ,
5487
5519
shown = OIDSET_INIT ;
5488
- struct label_state state = { OIDMAP_INIT , { NULL }, STRBUF_INIT };
5520
+ struct label_state state =
5521
+ { OIDMAP_INIT , { NULL }, STRBUF_INIT , GIT_MAX_LABEL_LENGTH };
5489
5522
5490
5523
int abbr = flags & TODO_LIST_ABBREVIATE_CMDS ;
5491
5524
const char * cmd_pick = abbr ? "p" : "pick" ,
5492
5525
* cmd_label = abbr ? "l" : "label" ,
5493
5526
* cmd_reset = abbr ? "t" : "reset" ,
5494
5527
* cmd_merge = abbr ? "m" : "merge" ;
5495
5528
5529
+ git_config_get_int ("rebase.maxlabellength" , & state .max_label_length );
5530
+
5496
5531
oidmap_init (& commit2todo , 0 );
5497
5532
oidmap_init (& state .commit2label , 0 );
5498
5533
hashmap_init (& state .labels , labels_cmp , NULL , 0 );
0 commit comments