Skip to content

Commit bf2911d

Browse files
authored
Merge pull request libgit2#5275 from pks-t/pks/reflogs-with-newlines
reflogs: fix behaviour around reflogs with newlines
2 parents d5017a1 + 7968e90 commit bf2911d

File tree

8 files changed

+155
-132
lines changed

8 files changed

+155
-132
lines changed

src/parse.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base)
101101
return 0;
102102
}
103103

104+
int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx)
105+
{
106+
if (ctx->line_len < GIT_OID_HEXSZ)
107+
return -1;
108+
if ((git_oid_fromstrn(out, ctx->line, GIT_OID_HEXSZ)) < 0)
109+
return -1;
110+
git_parse_advance_chars(ctx, GIT_OID_HEXSZ);
111+
return 0;
112+
}
113+
104114
int git_parse_peek(char *out, git_parse_ctx *ctx, int flags)
105115
{
106116
size_t remain = ctx->line_len;

src/parse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ int git_parse_advance_expected(
5050
int git_parse_advance_ws(git_parse_ctx *ctx);
5151
int git_parse_advance_nl(git_parse_ctx *ctx);
5252
int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base);
53+
int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx);
5354

5455
enum GIT_PARSE_PEEK_FLAGS {
5556
GIT_PARSE_PEEK_SKIP_WHITESPACE = (1 << 0)

src/refdb_fs.c

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "futils.h"
1414
#include "filebuf.h"
1515
#include "pack.h"
16+
#include "parse.h"
1617
#include "reflog.h"
1718
#include "refdb.h"
1819
#include "iterator.h"
@@ -1651,70 +1652,57 @@ static int reflog_alloc(git_reflog **reflog, const char *name)
16511652

16521653
static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
16531654
{
1654-
const char *ptr;
1655-
git_reflog_entry *entry;
1656-
1657-
#define seek_forward(_increase) do { \
1658-
if (_increase >= buf_size) { \
1659-
git_error_set(GIT_ERROR_INVALID, "ran out of data while parsing reflog"); \
1660-
goto fail; \
1661-
} \
1662-
buf += _increase; \
1663-
buf_size -= _increase; \
1664-
} while (0)
1665-
1666-
while (buf_size > GIT_REFLOG_SIZE_MIN) {
1667-
entry = git__calloc(1, sizeof(git_reflog_entry));
1668-
GIT_ERROR_CHECK_ALLOC(entry);
1655+
git_parse_ctx parser = GIT_PARSE_CTX_INIT;
16691656

1670-
entry->committer = git__calloc(1, sizeof(git_signature));
1671-
GIT_ERROR_CHECK_ALLOC(entry->committer);
1657+
if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
1658+
return -1;
16721659

1673-
if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
1674-
goto fail;
1675-
seek_forward(GIT_OID_HEXSZ + 1);
1660+
for (; parser.remain_len; git_parse_advance_line(&parser)) {
1661+
git_reflog_entry *entry;
1662+
const char *sig;
1663+
char c;
16761664

1677-
if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
1678-
goto fail;
1679-
seek_forward(GIT_OID_HEXSZ + 1);
1665+
entry = git__calloc(1, sizeof(*entry));
1666+
GIT_ERROR_CHECK_ALLOC(entry);
1667+
entry->committer = git__calloc(1, sizeof(*entry->committer));
1668+
GIT_ERROR_CHECK_ALLOC(entry->committer);
16801669

1681-
ptr = buf;
1670+
if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
1671+
git_parse_advance_expected(&parser, " ", 1) < 0 ||
1672+
git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
1673+
goto next;
16821674

1683-
/* Seek forward to the end of the signature. */
1684-
while (*buf && *buf != '\t' && *buf != '\n')
1685-
seek_forward(1);
1675+
sig = parser.line;
1676+
while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
1677+
git_parse_advance_chars(&parser, 1);
16861678

1687-
if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
1688-
goto fail;
1679+
if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
1680+
goto next;
16891681

1690-
if (*buf == '\t') {
1691-
/* We got a message. Read everything till we reach LF. */
1692-
seek_forward(1);
1693-
ptr = buf;
1682+
if (c == '\t') {
1683+
size_t len;
1684+
git_parse_advance_chars(&parser, 1);
16941685

1695-
while (*buf && *buf != '\n')
1696-
seek_forward(1);
1686+
len = parser.line_len;
1687+
if (parser.line[len - 1] == '\n')
1688+
len--;
16971689

1698-
entry->msg = git__strndup(ptr, buf - ptr);
1690+
entry->msg = git__strndup(parser.line, len);
16991691
GIT_ERROR_CHECK_ALLOC(entry->msg);
1700-
} else
1701-
entry->msg = NULL;
1692+
}
17021693

1703-
while (*buf && *buf == '\n' && buf_size > 1)
1704-
seek_forward(1);
1694+
if ((git_vector_insert(&log->entries, entry)) < 0) {
1695+
git_reflog_entry__free(entry);
1696+
return -1;
1697+
}
17051698

1706-
if (git_vector_insert(&log->entries, entry) < 0)
1707-
goto fail;
1699+
continue;
1700+
1701+
next:
1702+
git_reflog_entry__free(entry);
17081703
}
17091704

17101705
return 0;
1711-
1712-
#undef seek_forward
1713-
1714-
fail:
1715-
git_reflog_entry__free(entry);
1716-
1717-
return -1;
17181706
}
17191707

17201708
static int create_new_reflog_file(const char *filepath)
@@ -1856,8 +1844,15 @@ static int serialize_reflog_entry(
18561844
git_buf_rtrim(buf);
18571845

18581846
if (msg) {
1847+
size_t i;
1848+
18591849
git_buf_putc(buf, '\t');
18601850
git_buf_puts(buf, msg);
1851+
1852+
for (i = 0; i < buf->size - 2; i++)
1853+
if (buf->ptr[i] == '\n')
1854+
buf->ptr[i] = ' ';
1855+
git_buf_rtrim(buf);
18611856
}
18621857

18631858
git_buf_putc(buf, '\n');

src/reflog.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,8 @@ int git_reflog_write(git_reflog *reflog)
7474

7575
int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
7676
{
77-
git_reflog_entry *entry;
7877
const git_reflog_entry *previous;
79-
const char *newline;
78+
git_reflog_entry *entry;
8079

8180
assert(reflog && new_oid && committer);
8281

@@ -87,19 +86,18 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_sign
8786
goto cleanup;
8887

8988
if (msg != NULL) {
90-
if ((entry->msg = git__strdup(msg)) == NULL)
91-
goto cleanup;
89+
size_t i, msglen = strlen(msg);
9290

93-
newline = strchr(msg, '\n');
94-
95-
if (newline) {
96-
if (newline[1] != '\0') {
97-
git_error_set(GIT_ERROR_INVALID, "reflog message cannot contain newline");
98-
goto cleanup;
99-
}
91+
if ((entry->msg = git__strndup(msg, msglen)) == NULL)
92+
goto cleanup;
10093

101-
entry->msg[newline - msg] = '\0';
102-
}
94+
/*
95+
* Replace all newlines with spaces, except for
96+
* the final trailing newline.
97+
*/
98+
for (i = 0; i < msglen; i++)
99+
if (entry->msg[i] == '\n')
100+
entry->msg[i] = ' ';
103101
}
104102

105103
previous = git_reflog_entry_byindex(reflog, 0);

src/stash.c

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -437,36 +437,33 @@ static int commit_worktree(
437437
return error;
438438
}
439439

440-
static int prepare_worktree_commit_message(
441-
git_buf* msg,
442-
const char *user_message)
440+
static int prepare_worktree_commit_message(git_buf *out, const char *user_message)
443441
{
444442
git_buf buf = GIT_BUF_INIT;
445-
int error;
446-
447-
if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
448-
return error;
449-
450-
git_buf_clear(msg);
443+
int error = 0;
451444

452-
if (!user_message)
453-
git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
454-
else {
445+
if (!user_message) {
446+
git_buf_printf(&buf, "WIP on %s", git_buf_cstr(out));
447+
} else {
455448
const char *colon;
456449

457-
if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
450+
if ((colon = strchr(git_buf_cstr(out), ':')) == NULL)
458451
goto cleanup;
459452

460-
git_buf_puts(msg, "On ");
461-
git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
462-
git_buf_printf(msg, ": %s\n", user_message);
453+
git_buf_puts(&buf, "On ");
454+
git_buf_put(&buf, git_buf_cstr(out), colon - out->ptr);
455+
git_buf_printf(&buf, ": %s\n", user_message);
456+
}
457+
458+
if (git_buf_oom(&buf)) {
459+
error = -1;
460+
goto cleanup;
463461
}
464462

465-
error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
463+
git_buf_swap(out, &buf);
466464

467465
cleanup:
468466
git_buf_dispose(&buf);
469-
470467
return error;
471468
}
472469

@@ -497,22 +494,19 @@ static int is_dirty_cb(const char *path, unsigned int status, void *payload)
497494
return GIT_PASSTHROUGH;
498495
}
499496

500-
static int ensure_there_are_changes_to_stash(
501-
git_repository *repo,
502-
bool include_untracked_files,
503-
bool include_ignored_files)
497+
static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flags)
504498
{
505499
int error;
506500
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
507501

508502
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
509503
opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
510504

511-
if (include_untracked_files)
505+
if (flags & GIT_STASH_INCLUDE_UNTRACKED)
512506
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
513507
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
514508

515-
if (include_ignored_files)
509+
if (flags & GIT_STASH_INCLUDE_IGNORED)
516510
opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
517511
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
518512

@@ -527,20 +521,14 @@ static int ensure_there_are_changes_to_stash(
527521
return error;
528522
}
529523

530-
static int reset_index_and_workdir(
531-
git_repository *repo,
532-
git_commit *commit,
533-
bool remove_untracked,
534-
bool remove_ignored)
524+
static int reset_index_and_workdir(git_repository *repo, git_commit *commit, uint32_t flags)
535525
{
536526
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
537527

538528
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
539-
540-
if (remove_untracked)
529+
if (flags & GIT_STASH_INCLUDE_UNTRACKED)
541530
opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
542-
543-
if (remove_ignored)
531+
if (flags & GIT_STASH_INCLUDE_IGNORED)
544532
opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED;
545533

546534
return git_checkout_tree(repo, (git_object *)commit, &opts);
@@ -566,43 +554,35 @@ int git_stash_save(
566554
if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
567555
goto cleanup;
568556

569-
if ((error = ensure_there_are_changes_to_stash(
570-
repo,
571-
(flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
572-
(flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
557+
if ((error = ensure_there_are_changes_to_stash(repo, flags)) < 0)
573558
goto cleanup;
574559

575560
if ((error = git_repository_index(&index, repo)) < 0)
576561
goto cleanup;
577562

578-
if ((error = commit_index(
579-
&i_commit, repo, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
563+
if ((error = commit_index(&i_commit, repo, index, stasher,
564+
git_buf_cstr(&msg), b_commit)) < 0)
580565
goto cleanup;
581566

582567
if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
583-
(error = commit_untracked(
584-
&u_commit, repo, stasher, git_buf_cstr(&msg),
585-
i_commit, flags)) < 0)
568+
(error = commit_untracked(&u_commit, repo, stasher,
569+
git_buf_cstr(&msg), i_commit, flags)) < 0)
586570
goto cleanup;
587571

588572
if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
589573
goto cleanup;
590574

591-
if ((error = commit_worktree(
592-
out, repo, stasher, git_buf_cstr(&msg),
593-
i_commit, b_commit, u_commit)) < 0)
575+
if ((error = commit_worktree(out, repo, stasher, git_buf_cstr(&msg),
576+
i_commit, b_commit, u_commit)) < 0)
594577
goto cleanup;
595578

596579
git_buf_rtrim(&msg);
597580

598581
if ((error = update_reflog(out, repo, git_buf_cstr(&msg))) < 0)
599582
goto cleanup;
600583

601-
if ((error = reset_index_and_workdir(
602-
repo,
603-
((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
604-
(flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
605-
(flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
584+
if ((error = reset_index_and_workdir(repo, (flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit,
585+
flags)) < 0)
606586
goto cleanup;
607587

608588
cleanup:

tests/refs/reflog/messages.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,27 @@ void test_refs_reflog_messages__creating_a_direct_reference(void)
281281
git_reference_free(reference);
282282
}
283283

284+
void test_refs_reflog_messages__newline_gets_replaced(void)
285+
{
286+
const git_reflog_entry *entry;
287+
git_signature *signature;
288+
git_reflog *reflog;
289+
git_oid oid;
290+
291+
cl_git_pass(git_signature_now(&signature, "me", "foo@example.com"));
292+
cl_git_pass(git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
293+
294+
cl_git_pass(git_reflog_read(&reflog, g_repo, "HEAD"));
295+
cl_assert_equal_sz(7, git_reflog_entrycount(reflog));
296+
cl_git_pass(git_reflog_append(reflog, &oid, signature, "inner\nnewline"));
297+
cl_assert_equal_sz(8, git_reflog_entrycount(reflog));
298+
299+
cl_assert(entry = git_reflog_entry_byindex(reflog, 0));
300+
cl_assert_equal_s(git_reflog_entry_message(entry), "inner newline");
301+
302+
git_signature_free(signature);
303+
git_reflog_free(reflog);
304+
}
284305

285306
void test_refs_reflog_messages__renaming_ref(void)
286307
{

0 commit comments

Comments
 (0)