Skip to content

Commit bcd45e2

Browse files
committed
Merge branch 'bc/mailsplit-cr-at-eol'
* bc/mailsplit-cr-at-eol: Allow mailsplit (and hence git-am) to handle mails with CRLF line-endings builtin-mailsplit.c: remove read_line_with_nul() since it is no longer used builtin-mailinfo,builtin-mailsplit: use strbufs strbuf: add new function strbuf_getwholeline()
2 parents f5d5ea5 + c2ca1d7 commit bcd45e2

File tree

7 files changed

+61
-43
lines changed

7 files changed

+61
-43
lines changed

builtin-mailinfo.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,6 @@ static void handle_filter(struct strbuf *line)
765765

766766
static void handle_body(void)
767767
{
768-
int len = 0;
769768
struct strbuf prev = STRBUF_INIT;
770769

771770
/* Skip up to the first boundary */
@@ -775,8 +774,6 @@ static void handle_body(void)
775774
}
776775

777776
do {
778-
strbuf_setlen(&line, line.len + len);
779-
780777
/* process any boundary lines */
781778
if (*content_top && is_multipart_boundary(&line)) {
782779
/* flush any leftover */
@@ -832,10 +829,7 @@ static void handle_body(void)
832829
handle_filter(&line);
833830
}
834831

835-
strbuf_reset(&line);
836-
if (strbuf_avail(&line) < 100)
837-
strbuf_grow(&line, 100);
838-
} while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
832+
} while (!strbuf_getwholeline(&line, fin, '\n'));
839833

840834
handle_body_out:
841835
strbuf_release(&prev);

builtin-mailsplit.c

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "cache.h"
88
#include "builtin.h"
99
#include "string-list.h"
10+
#include "strbuf.h"
1011

1112
static const char git_mailsplit_usage[] =
1213
"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
@@ -42,26 +43,8 @@ static int is_from_line(const char *line, int len)
4243
return 1;
4344
}
4445

45-
/* Could be as small as 64, enough to hold a Unix "From " line. */
46-
static char buf[4096];
47-
48-
/* We cannot use fgets() because our lines can contain NULs */
49-
int read_line_with_nul(char *buf, int size, FILE *in)
50-
{
51-
int len = 0, c;
52-
53-
for (;;) {
54-
c = getc(in);
55-
if (c == EOF)
56-
break;
57-
buf[len++] = c;
58-
if (c == '\n' || len + 1 >= size)
59-
break;
60-
}
61-
buf[len] = '\0';
62-
63-
return len;
64-
}
46+
static struct strbuf buf = STRBUF_INIT;
47+
static int keep_cr;
6548

6649
/* Called with the first line (potentially partial)
6750
* already in buf[] -- normally that should begin with
@@ -71,10 +54,9 @@ int read_line_with_nul(char *buf, int size, FILE *in)
7154
static int split_one(FILE *mbox, const char *name, int allow_bare)
7255
{
7356
FILE *output = NULL;
74-
int len = strlen(buf);
7557
int fd;
7658
int status = 0;
77-
int is_bare = !is_from_line(buf, len);
59+
int is_bare = !is_from_line(buf.buf, buf.len);
7860

7961
if (is_bare && !allow_bare)
8062
goto corrupt;
@@ -88,20 +70,23 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
8870
* "From " and having something that looks like a date format.
8971
*/
9072
for (;;) {
91-
int is_partial = len && buf[len-1] != '\n';
73+
if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
74+
buf.buf[buf.len-2] == '\r') {
75+
strbuf_setlen(&buf, buf.len-2);
76+
strbuf_addch(&buf, '\n');
77+
}
9278

93-
if (fwrite(buf, 1, len, output) != len)
79+
if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
9480
die_errno("cannot write output");
9581

96-
len = read_line_with_nul(buf, sizeof(buf), mbox);
97-
if (len == 0) {
82+
if (strbuf_getwholeline(&buf, mbox, '\n')) {
9883
if (feof(mbox)) {
9984
status = 1;
10085
break;
10186
}
10287
die_errno("cannot read mbox");
10388
}
104-
if (!is_partial && !is_bare && is_from_line(buf, len))
89+
if (!is_bare && is_from_line(buf.buf, buf.len))
10590
break; /* done with one message */
10691
}
10792
fclose(output);
@@ -166,7 +151,7 @@ static int split_maildir(const char *maildir, const char *dir,
166151
goto out;
167152
}
168153

169-
if (fgets(buf, sizeof(buf), f) == NULL) {
154+
if (strbuf_getwholeline(&buf, f, '\n')) {
170155
error("cannot read mail %s (%s)", file, strerror(errno));
171156
goto out;
172157
}
@@ -203,7 +188,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
203188
} while (isspace(peek));
204189
ungetc(peek, f);
205190

206-
if (fgets(buf, sizeof(buf), f) == NULL) {
191+
if (strbuf_getwholeline(&buf, f, '\n')) {
207192
/* empty stdin is OK */
208193
if (f != stdin) {
209194
error("cannot read mbox %s", file);
@@ -248,6 +233,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
248233
nr = strtol(arg+2, NULL, 10);
249234
} else if ( arg[1] == 'b' && !arg[2] ) {
250235
allow_bare = 1;
236+
} else if (!strcmp(arg, "--keep-cr")) {
237+
keep_cr = 1;
251238
} else if ( arg[1] == 'o' && arg[2] ) {
252239
dir = arg+2;
253240
} else if ( arg[1] == '-' && !arg[2] ) {

builtin.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ extern const char git_more_info_string[];
1313
extern void list_common_cmds_help(void);
1414
extern const char *help_unknown_cmd(const char *cmd);
1515
extern void prune_packed_objects(int);
16-
extern int read_line_with_nul(char *buf, int size, FILE *file);
1716
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
1817
struct strbuf *out);
1918
extern int commit_tree(const char *msg, unsigned char *tree,

git-am.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,13 @@ check_patch_format () {
211211
split_patches () {
212212
case "$patch_format" in
213213
mbox)
214-
git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
214+
case "$rebasing" in
215+
'')
216+
keep_cr= ;;
217+
?*)
218+
keep_cr=--keep-cr ;;
219+
esac
220+
git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
215221
clean_abort
216222
;;
217223
stgit-series)

strbuf.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
322322
return -1;
323323
}
324324

325-
int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
325+
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
326326
{
327327
int ch;
328328

@@ -332,10 +332,10 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
332332

333333
strbuf_reset(sb);
334334
while ((ch = fgetc(fp)) != EOF) {
335-
if (ch == term)
336-
break;
337335
strbuf_grow(sb, 1);
338336
sb->buf[sb->len++] = ch;
337+
if (ch == term)
338+
break;
339339
}
340340
if (ch == EOF && sb->len == 0)
341341
return EOF;
@@ -344,6 +344,15 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
344344
return 0;
345345
}
346346

347+
int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
348+
{
349+
if (strbuf_getwholeline(sb, fp, term))
350+
return EOF;
351+
if (sb->buf[sb->len-1] == term)
352+
strbuf_setlen(sb, sb->len-1);
353+
return 0;
354+
}
355+
347356
int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
348357
{
349358
int fd, len;

strbuf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
126126
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
127127
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
128128

129+
extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
129130
extern int strbuf_getline(struct strbuf *, FILE *, int);
130131

131132
extern void stripspace(struct strbuf *buf, int skip_comments);

t/t3400-rebase.sh

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
# Copyright (c) 2005 Amos Waterland
44
#
55

6-
test_description='git rebase should not destroy author information
6+
test_description='git rebase assorted tests
77
8-
This test runs git rebase and checks that the author information is not lost.
8+
This test runs git rebase and checks that the author information is not lost
9+
among other things.
910
'
1011
. ./test-lib.sh
1112

@@ -133,4 +134,25 @@ test_expect_success 'rebase -q is quiet' '
133134
test ! -s output.out
134135
'
135136

137+
q_to_cr () {
138+
tr Q '\015'
139+
}
140+
141+
test_expect_success 'Rebase a commit that sprinkles CRs in' '
142+
(
143+
echo "One"
144+
echo "TwoQ"
145+
echo "Three"
146+
echo "FQur"
147+
echo "Five"
148+
) | q_to_cr >CR &&
149+
git add CR &&
150+
test_tick &&
151+
git commit -a -m "A file with a line with CR" &&
152+
git tag file-with-cr &&
153+
git checkout HEAD^0 &&
154+
git rebase --onto HEAD^^ HEAD^ &&
155+
git diff --exit-code file-with-cr:CR HEAD:CR
156+
'
157+
136158
test_done

0 commit comments

Comments
 (0)