Skip to content

Commit

Permalink
reflog: cleanse messages in the refs.c layer
Browse files Browse the repository at this point in the history
Regarding reflog messages:

 - We expect that a reflog message consists of a single line.  The
   file format used by the files backend may add a LF after the
   message as a delimiter, and output by commands like "git log -g"
   may complete such an incomplete line by adding a LF at the end,
   but philosophically, the terminating LF is not a part of the
   message.

 - We however allow callers of refs API to supply a random sequence
   of NUL terminated bytes.  We cleanse caller-supplied message by
   squashing a run of whitespaces into a SP, and by trimming trailing
   whitespace, before storing the message.  This is how we tolerate,
   instead of erring out, a message with LF in it (be it at the end,
   in the middle, or both).

Currently, the cleansing of the reflog message is done by the files
backend, before the log is written out.  This is sufficient with the
current code, as that is the only backend that writes reflogs.  But
new backends can be added that write reflogs, and we'd want the
resulting log message we would read out of "log -g" the same no
matter what backend is used, and moving the code to do so to the
generic layer is a way to do so.

An added benefit is that the "cleansing" function could be updated
later, independent from individual backends, to e.g. allow
multi-line log messages if we wanted to, and when that happens, it
would help a lot to ensure we covered all bases if the cleansing
function (which would be updated) is called from the generic layer.

Side note: I am not interested in supporting multi-line reflog
messages right at the moment (nobody is asking for it), but I
envision that instead of the "squash a run of whitespaces into a SP
and rtrim" cleansing, we can %urlencode problematic bytes in the
message *AND* append a SP at the end, when a new version of Git that
supports multi-line and/or verbatim reflog messages writes a reflog
record.  The reading side can detect the presense of SP at the end
(which should have been rtrimmed out if it were written by existing
versions of Git) as a signal that decoding %urlencode recovers the
original reflog message.

Signed-off-by: Han-Wen Nienhuys <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
gitster committed Jul 10, 2020
1 parent de966e3 commit 523fa69
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 16 deletions.
50 changes: 41 additions & 9 deletions refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ int delete_ref(const char *msg, const char *refname,
old_oid, flags);
}

void copy_reflog_msg(struct strbuf *sb, const char *msg)
static void copy_reflog_msg(struct strbuf *sb, const char *msg)
{
char c;
int wasspace = 1;
Expand All @@ -887,6 +887,15 @@ void copy_reflog_msg(struct strbuf *sb, const char *msg)
strbuf_rtrim(sb);
}

static char *normalize_reflog_message(const char *msg)
{
struct strbuf sb = STRBUF_INIT;

if (msg && *msg)
copy_reflog_msg(&sb, msg);
return strbuf_detach(&sb, NULL);
}

int should_autocreate_reflog(const char *refname)
{
switch (log_all_ref_updates) {
Expand Down Expand Up @@ -1092,7 +1101,7 @@ struct ref_update *ref_transaction_add_update(
oidcpy(&update->new_oid, new_oid);
if (flags & REF_HAVE_OLD)
oidcpy(&update->old_oid, old_oid);
update->msg = xstrdup_or_null(msg);
update->msg = normalize_reflog_message(msg);
return update;
}

Expand Down Expand Up @@ -1951,9 +1960,14 @@ int refs_create_symref(struct ref_store *refs,
const char *refs_heads_master,
const char *logmsg)
{
return refs->be->create_symref(refs, ref_target,
refs_heads_master,
logmsg);
char *msg;
int retval;

msg = normalize_reflog_message(logmsg);
retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
msg);
free(msg);
return retval;
}

int create_symref(const char *ref_target, const char *refs_heads_master,
Expand Down Expand Up @@ -2268,10 +2282,16 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
return refs->be->initial_transaction_commit(refs, transaction, err);
}

int refs_delete_refs(struct ref_store *refs, const char *msg,
int refs_delete_refs(struct ref_store *refs, const char *logmsg,
struct string_list *refnames, unsigned int flags)
{
return refs->be->delete_refs(refs, msg, refnames, flags);
char *msg;
int retval;

msg = normalize_reflog_message(logmsg);
retval = refs->be->delete_refs(refs, msg, refnames, flags);
free(msg);
return retval;
}

int delete_refs(const char *msg, struct string_list *refnames,
Expand All @@ -2283,7 +2303,13 @@ int delete_refs(const char *msg, struct string_list *refnames,
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
return refs->be->rename_ref(refs, oldref, newref, logmsg);
char *msg;
int retval;

msg = normalize_reflog_message(logmsg);
retval = refs->be->rename_ref(refs, oldref, newref, msg);
free(msg);
return retval;
}

int rename_ref(const char *oldref, const char *newref, const char *logmsg)
Expand All @@ -2294,7 +2320,13 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
return refs->be->copy_ref(refs, oldref, newref, logmsg);
char *msg;
int retval;

msg = normalize_reflog_message(logmsg);
retval = refs->be->copy_ref(refs, oldref, newref, msg);
free(msg);
return retval;
}

int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
Expand Down
2 changes: 1 addition & 1 deletion refs/files-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1629,7 +1629,7 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,

strbuf_addf(&sb, "%s %s %s", oid_to_hex(old_oid), oid_to_hex(new_oid), committer);
if (msg && *msg)
copy_reflog_msg(&sb, msg);
strbuf_addstr(&sb, msg);
strbuf_addch(&sb, '\n');
if (write_in_full(fd, sb.buf, sb.len) < 0)
ret = -1;
Expand Down
6 changes: 0 additions & 6 deletions refs/refs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ enum peel_status {
*/
enum peel_status peel_object(const struct object_id *name, struct object_id *oid);

/*
* Copy the reflog message msg to sb while cleaning up the whitespaces.
* Especially, convert LF to space, because reflog file is one line per entry.
*/
void copy_reflog_msg(struct strbuf *sb, const char *msg);

/**
* Information needed for a single ref update. Set new_oid to the new
* value or to null_oid to delete the ref. To check the old value
Expand Down

0 comments on commit 523fa69

Please sign in to comment.