Hi Thomas, On Tue, 8 Oct 2019, Thomas Gummerer wrote: > In ef283b3699 ("apply: make parse_git_diff_header public", 2019-07-11) > the 'parse_git_diff_header' function was made public and useable by > callers outside of apply.c. > > However it was missed that its (then) only caller, 'find_header' did > some error handling, and completing 'struct patch' appropriately. > > range-diff then started using this function, and tried to handle this > appropriately itself, but fell short in some cases. This in turn > would lead to range-diff segfaulting when there are mode-only changes > in a range. > > Move the error handling and completing of the struct into the > 'parse_git_diff_header' function, so other callers can take advantage > of it. This fixes the segfault in 'git range-diff'. > > Reported-by: Uwe Kleine-König <u.kleine-koenig@xxxxxxxxxxxxxx> Acked-by: Johannes Schindelin <johannes.schindelin@xxxxxx> Ciao, Dscho > Signed-off-by: Thomas Gummerer <t.gummerer@xxxxxxxxx> > --- > > Thanks Junio and Dscho for your reviews. I decided to lift the whole > error handling behaviour from find_header into parse_git_diff_header, > instead of just filling the two names with xstrdup(def_name) if > (!old_name && !new_name && !!def_name). I think the additional > information presented there can be useful. For example we would have > gotten some "error: git diff header lacks filename information" > instead of a segfault for the problem described in > https://public-inbox.org/git/20191002141615.GB17916@xxxxxxxxxxxxxxx/T/#me576615d7a151cf2ed46186c482fbd88f9959914. > > Dscho, I didn't re-use your test case here as I had already written > one, and think what I have is slightly nicer in that it follows what > most other range-diff tests do in using the fast-exported history. It > also expands the test coverage slightly, as we currently don't have > any coverage of the mode-change header, but will with this test. > > The downside is of course that the fast export script is harder to > understand than the test you had, at least for me, but I think the > tradeoff of having the additional test coverage, and having it similar > to the rest of the test script is worth it. If you strongly prefer > your test though I'm not going to be unhappy to use that :) > > apply.c | 43 +++++++++++++++++++++--------------------- > t/t3206-range-diff.sh | 40 +++++++++++++++++++++++++++++++++++++++ > t/t3206/history.export | 31 +++++++++++++++++++++++++++++- > 3 files changed, 92 insertions(+), 22 deletions(-) > > diff --git a/apply.c b/apply.c > index 57a61f2881..f8a046a6a5 100644 > --- a/apply.c > +++ b/apply.c > @@ -1361,11 +1361,32 @@ int parse_git_diff_header(struct strbuf *root, > if (check_header_line(*linenr, patch)) > return -1; > if (res > 0) > - return offset; > + goto done; > break; > } > } > > +done: > + if (!patch->old_name && !patch->new_name) { > + if (!patch->def_name) { > + error(Q_("git diff header lacks filename information when removing " > + "%d leading pathname component (line %d)", > + "git diff header lacks filename information when removing " > + "%d leading pathname components (line %d)", > + parse_hdr_state.p_value), > + parse_hdr_state.p_value, *linenr); > + return -128; > + } > + patch->old_name = xstrdup(patch->def_name); > + patch->new_name = xstrdup(patch->def_name); > + } > + if ((!patch->new_name && !patch->is_delete) || > + (!patch->old_name && !patch->is_new)) { > + error(_("git diff header lacks filename information " > + "(line %d)"), *linenr); > + return -128; > + } > + patch->is_toplevel_relative = 1; > return offset; > } > > @@ -1546,26 +1567,6 @@ static int find_header(struct apply_state *state, > return -128; > if (git_hdr_len <= len) > continue; > - if (!patch->old_name && !patch->new_name) { > - if (!patch->def_name) { > - error(Q_("git diff header lacks filename information when removing " > - "%d leading pathname component (line %d)", > - "git diff header lacks filename information when removing " > - "%d leading pathname components (line %d)", > - state->p_value), > - state->p_value, state->linenr); > - return -128; > - } > - patch->old_name = xstrdup(patch->def_name); > - patch->new_name = xstrdup(patch->def_name); > - } > - if ((!patch->new_name && !patch->is_delete) || > - (!patch->old_name && !patch->is_new)) { > - error(_("git diff header lacks filename information " > - "(line %d)"), state->linenr); > - return -128; > - } > - patch->is_toplevel_relative = 1; > *hdrsize = git_hdr_len; > return offset; > } > diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh > index ec548654ce..5b87fead2e 100755 > --- a/t/t3206-range-diff.sh > +++ b/t/t3206-range-diff.sh > @@ -226,6 +226,46 @@ test_expect_success 'renamed file' ' > test_cmp expected actual > ' > > +test_expect_success 'file with mode only change' ' > + git range-diff --no-color --submodule=log topic...mode-only-change >actual && > + sed s/Z/\ /g >expected <<-EOF && > + 1: fccce22 ! 1: 4d39cb3 s/4/A/ > + @@ Metadata > + ZAuthor: Thomas Rast <trast@xxxxxxxxxxx> > + Z > + Z ## Commit message ## > + - s/4/A/ > + + s/4/A/ + add other-file > + Z > + Z ## file ## > + Z@@ > + @@ file > + Z A > + Z 6 > + Z 7 > + + > + + ## other-file (new) ## > + 2: 147e64e ! 2: 26c107f s/11/B/ > + @@ Metadata > + ZAuthor: Thomas Rast <trast@xxxxxxxxxxx> > + Z > + Z ## Commit message ## > + - s/11/B/ > + + s/11/B/ + mode change other-file > + Z > + Z ## file ## > + Z@@ file: A > + @@ file: A > + Z 12 > + Z 13 > + Z 14 > + + > + + ## other-file (mode change 100644 => 100755) ## > + 3: a63e992 = 3: 4c1e0f5 s/12/B/ > + EOF > + test_cmp expected actual > +' > + > test_expect_success 'file added and later removed' ' > git range-diff --no-color --submodule=log topic...added-removed >actual && > sed s/Z/\ /g >expected <<-EOF && > diff --git a/t/t3206/history.export b/t/t3206/history.export > index 7bb3814962..4c808e5b3b 100644 > --- a/t/t3206/history.export > +++ b/t/t3206/history.export > @@ -55,7 +55,7 @@ A > 19 > 20 > > -commit refs/heads/topic > +commit refs/heads/mode-only-change > mark :4 > author Thomas Rast <trast@xxxxxxxxxxx> 1374485014 +0200 > committer Thomas Rast <trast@xxxxxxxxxxx> 1374485014 +0200 > @@ -678,3 +678,32 @@ s/12/B/ > from :55 > M 100644 :9 renamed-file > > +commit refs/heads/mode-only-change > +mark :57 > +author Thomas Rast <trast@xxxxxxxxxxx> 1374485024 +0200 > +committer Thomas Gummerer <t.gummerer@xxxxxxxxx> 1570473767 +0100 > +data 24 > +s/4/A/ + add other-file > +from :4 > +M 100644 :5 file > +M 100644 :49 other-file > + > +commit refs/heads/mode-only-change > +mark :58 > +author Thomas Rast <trast@xxxxxxxxxxx> 1374485036 +0200 > +committer Thomas Gummerer <t.gummerer@xxxxxxxxx> 1570473768 +0100 > +data 33 > +s/11/B/ + mode change other-file > +from :57 > +M 100644 :7 file > +M 100755 :49 other-file > + > +commit refs/heads/mode-only-change > +mark :59 > +author Thomas Rast <trast@xxxxxxxxxxx> 1374485044 +0200 > +committer Thomas Gummerer <t.gummerer@xxxxxxxxx> 1570473768 +0100 > +data 8 > +s/12/B/ > +from :58 > +M 100644 :9 file > + > -- > 2.23.0.501.gb744c3af07 > >