Skip to content
  • Eric Sunshine's avatar
    checkout-index: fix --temp relative path mangling · 74c4de58
    Eric Sunshine authored and Junio C Hamano's avatar Junio C Hamano committed
    
    
    checkout-index --temp only properly prints relative paths which are
    descendants of the current directory. Paths in ancestor or sibling
    directories (or their children) are often printed in mangled form. For
    example:
    
        mkdir a bbb &&
        >file &&
        >bbb/file &&
        git update-index --add file bbb/file &&
        cd a &&
        git checkout-index --temp ../file ../bbb/file
    
    prints:
    
        .merge_file_ooblek  le
        .merge_file_igloo0  b/file
    
    rather than the correct:
    
        .merge_file_ooblek  ../file
        .merge_file_igloo0  ../bbb/file
    
    Internally, given the above example, checkout-index prefixes each input
    argument with the name of the current directory ("a/", in this case),
    and then assumes that it can simply skip forward by strlen("a/") bytes
    to recover the original name. This works for files in the current
    directory or its descendants, but fails for files in ancestors or
    siblings (or their children) due to path normalization.
    
    For instance, given "../file", "a/" is prepended, giving "a/../file".
    Path normalization folds out "a/../", resulting in "file". Attempting
    to recover the original name by skipping strlen("a/") bytes gives the
    incorrect "le" rather than the desired "../file".
    
    Fix this by taking advantage of write_name_quoted_relative() to recover
    the original name properly, rather than assuming that it can be
    recovered by skipping strlen(prefix) bytes.
    
    As a bonus, this also fixes a bug in which checkout-index --temp
    accessed and printed memory beyond the end-of-string. For instance,
    within a subdirectory named "subdirectory", and given argument
    "../file", prefixing would give "subdirectory/../file", which would
    become "file" after normalization. checkout-index would then attempt to
    recover the original name by skipping strlen("subdirectory/") bytes of
    "file", which placed it well beyond end-of-string. Despite this error,
    it often appeared to give the correct result, but only due to an
    accident of implementation which left an apparently correct copy of the
    path in memory following the normalized value. In particular, handed
    "subdirectory/../file", in-place processing by normalize_path_copy_len()
    resulted in "file\0rectory/../file". When checkout-index skipped
    strlen("subdirectory/") bytes, it ended up back at "../file" and thus
    appeared to give the correct answer, despite being past end-of-string.
    
    Reported-by: default avatarRuss Cox <rsc@golang.org>
    Signed-off-by: default avatarEric Sunshine <sunshine@sunshineco.com>
    Signed-off-by: default avatarJunio C Hamano <gitster@pobox.com>
    74c4de58