]> git.ipfire.org Git - thirdparty/git.git/blobdiff - sequencer.c
Merge branch 'mp/rebase-label-length-limit' into maint-2.42
[thirdparty/git.git] / sequencer.c
index 5e0c15a16b73b3bf04fc76afc2bc2bd1c15f677d..828efd589deff91a67804951176b689d63a4cf18 100644 (file)
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
+/*
+ * To accommodate common filesystem limitations, where the loose refs' file
+ * names must not exceed `NAME_MAX`, the labels generated by `git rebase
+ * --rebase-merges` need to be truncated if the corresponding commit subjects
+ * are too long.
+ * Add some margin to stay clear from reaching `NAME_MAX`.
+ */
+#define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16)
+
 static const char sign_off_header[] = "Signed-off-by: ";
 static const char cherry_picked_prefix[] = "(cherry picked from commit ";
 
@@ -5352,6 +5361,7 @@ struct label_state {
        struct oidmap commit2label;
        struct hashmap labels;
        struct strbuf buf;
+       int max_label_length;
 };
 
 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,
                }
        } else {
                struct strbuf *buf = &state->buf;
+               int label_is_utf8 = 1; /* start with this assumption */
+               size_t max_len = buf->len + state->max_label_length;
 
                /*
                 * Sanitize labels by replacing non-alpha-numeric characters
@@ -5416,14 +5428,34 @@ static const char *label_oid(struct object_id *oid, const char *label,
                 *
                 * Note that we retain non-ASCII UTF-8 characters (identified
                 * via the most significant bit). They should be all acceptable
-                * in file names. We do not validate the UTF-8 here, that's not
-                * the job of this function.
+                * in file names.
+                *
+                * As we will use the labels as names of (loose) refs, it is
+                * vital that the name not be longer than the maximum component
+                * size of the file system (`NAME_MAX`). We are careful to
+                * truncate the label accordingly, allowing for the `.lock`
+                * suffix and for the label to be UTF-8 encoded (i.e. we avoid
+                * truncating in the middle of a character).
                 */
-               for (; *label; label++)
-                       if ((*label & 0x80) || isalnum(*label))
+               for (; *label && buf->len + 1 < max_len; label++)
+                       if (isalnum(*label) ||
+                           (!label_is_utf8 && (*label & 0x80)))
                                strbuf_addch(buf, *label);
+                       else if (*label & 0x80) {
+                               const char *p = label;
+
+                               utf8_width(&p, NULL);
+                               if (p) {
+                                       if (buf->len + (p - label) > max_len)
+                                               break;
+                                       strbuf_add(buf, label, p - label);
+                                       label = p - 1;
+                               } else {
+                                       label_is_utf8 = 0;
+                                       strbuf_addch(buf, *label);
+                               }
                        /* avoid leading dash and double-dashes */
-                       else if (buf->len && buf->buf[buf->len - 1] != '-')
+                       else if (buf->len && buf->buf[buf->len - 1] != '-')
                                strbuf_addch(buf, '-');
                if (!buf->len) {
                        strbuf_addstr(buf, "rev-");
@@ -5485,7 +5517,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        struct string_entry *entry;
        struct oidset interesting = OIDSET_INIT, child_seen = OIDSET_INIT,
                shown = OIDSET_INIT;
-       struct label_state state = { OIDMAP_INIT, { NULL }, STRBUF_INIT };
+       struct label_state state =
+               { OIDMAP_INIT, { NULL }, STRBUF_INIT, GIT_MAX_LABEL_LENGTH };
 
        int abbr = flags & TODO_LIST_ABBREVIATE_CMDS;
        const char *cmd_pick = abbr ? "p" : "pick",
@@ -5493,6 +5526,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                *cmd_reset = abbr ? "t" : "reset",
                *cmd_merge = abbr ? "m" : "merge";
 
+       git_config_get_int("rebase.maxlabellength", &state.max_label_length);
+
        oidmap_init(&commit2todo, 0);
        oidmap_init(&state.commit2label, 0);
        hashmap_init(&state.labels, labels_cmp, NULL, 0);