Skip to content

Commit

Permalink
amend! sequencer: truncate lockfile and ref to NAME_MAX
Browse files Browse the repository at this point in the history
sequencer: truncate labels to accommodate loose refs

Some commits may have unusually long subject lines. When those subject
lines are used as labels in the `--rebase-merges` mode of `git rebase`,
they can cause errors when writing the corresponding loose refs because
most file systems have a maximal file name length of 255 (`NAME_MAX`).
The symptom looks like this:

	$ git rebase --continue
	error: cannot lock ref 'refs/rewritten/SANITIZED-SUBJECT': Unable to create '.git/refs/rewritten/SANITIZED-SUBJECT.lock': File name too long - where SANITIZED-SUBJECT is very long

Let's accommodate this situation by truncating the labels.

Care must be taken in case the subject line contains multi-byte
characters so as not to truncate in the middle of a character.

Signed-off-by: Mark Ruvald Pedersen <[email protected]>
Signed-off-by: Johannes Schindelin <[email protected]>
  • Loading branch information
dscho committed Aug 10, 2023
1 parent b0758b8 commit cc6927e
Showing 1 changed file with 34 additions and 12 deletions.
46 changes: 34 additions & 12 deletions sequencer.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@

#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"

#define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN))

#define GIT_MIN(a, b) ((a) < (b) ? (a) : (b))
/*
* 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.
*/
#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 ";
Expand Down Expand Up @@ -3705,12 +3709,11 @@ static int do_label(struct repository *r, const char *name, int len)
struct strbuf msg = STRBUF_INIT;
int ret = 0;
struct object_id head_oid;
int len_trunc = GIT_MIN(len, GIT_MAX_LABEL_LENGTH);

if (len == 1 && *name == '#')
return error(_("illegal label name: '%.*s'"), len, name);

strbuf_addf(&ref_name, "refs/rewritten/%.*s", len_trunc, name);
strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
strbuf_addf(&msg, "rebase (label) '%.*s'", len, name);

transaction = ref_store_transaction_begin(refs, &err);
Expand Down Expand Up @@ -3774,12 +3777,11 @@ static const char *reflog_message(struct replay_opts *opts,
static struct commit *lookup_label(struct repository *r, const char *label,
int len, struct strbuf *buf)
{
int len_trunc = GIT_MIN(len, GIT_MAX_LABEL_LENGTH);
struct commit *commit;
struct object_id oid;

strbuf_reset(buf);
strbuf_addf(buf, "refs/rewritten/%.*s", len_trunc, label);
strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
if (!read_ref(buf->buf, &oid)) {
commit = lookup_commit_object(r, &oid);
} else {
Expand Down Expand Up @@ -5402,6 +5404,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 + GIT_MAX_LABEL_LENGTH;

/*
* Sanitize labels by replacing non-alpha-numeric characters
Expand All @@ -5410,14 +5414,32 @@ 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) {
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-");
Expand Down

0 comments on commit cc6927e

Please sign in to comment.