git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 3/3] correct ce_compare_data() in a middle of a merge

From
Torsten Bögershausen <tboegi@web.de>
Date
Jul 7, 2016, 17:16 UTC
Message-ID
<c36fe487-b8dc-9767-7fae-bee513dac0b2@web.de>
In-Reply-To
<xmqqa8huvmpv.fsf@gitster.mtv.corp.google.com>
On 2016-07-06 16.57, Junio C Hamano wrote:
Show 13 quoted lines
> Torsten Bögershausen <tboegi@web.de> writes:
> 
>> At some point inside the merge, Git calls read-cache.c/ce_compare_data(),
>>   to check if the path named "file" is clean.
>>   According to the tree information, the path "file" has the sha1 99b633.
>>   #Note:
>>   #sha1 99b633 is "first line\nsame line\n"
> 
> I thought your scenario was that our side had CRLF both in the blob
> and in the working tree.  So this is a different example?  Our side
> has LF in the blob that is checked out with CRLF in the working tree,
> and their side has CRLF in the blob?
> 

That was probably to confuse myself, and the rest of the world, sorry for confusion.

This is the callstack:
merge-recursive.c/add_cacheinfo(unsigned int mode, const unsigned char *sha1,
		const char *path, int stage, int refresh, int options)
{
	struct cache_entry *ce;
	ce = make_cache_entry
	if (!ce)
		return error(_("addinfo_cache failed for path '%s'"), path);
	return add_cache_entry(ce, options);
}
#Calls
read-cache.c/make_cache_entry(path=file sha1=0x99b633 stage=0)
struct cache_entry *make_cache_entry(unsigned int mode,
		const unsigned char *sha1, const char *path, int stage,
		unsigned int refresh_options)
{
        [snip]
	ret = refresh_cache_entry(ce, refresh_options);
	if (ret != ce)
		free(ce);
	return ret;
}

#Calls refresh_cache_ent(&the_index, ce, options, NULL, NULL);

#Calls ie_modified()

#Calls read-cache.c/ie_match_stat()

#Calls changed |= ce_modified_check_fs(ce, st);

#Calls ce_compare_data(path=file sha1=0x99b633)

#Calls index_fd(..., ..., ce->name, ...) #Note the sha is lost here, the parameter sha is the output.

Deep down, has_cr_in_index(path) will look at ad55e2 instead, which is wrong.

Show 55 quoted lines
>> ce_compare_data() starts the work:
>> OK, lets run index_fd(...,ce->name,...)
>> # index_fd will simulate a "git add"  and return the sha1 (via the sha1 pointer)
>> # after the hashing.
>>
>> # ce_compare_data() will later compare ce->sha1 with the result stored in
>> # the local sha1. That's why a sha1 is in the parameter list.
>> # To return the resulting hash:
>>
>> ce_compare_data() calls index_fd(sha1, ..., ce->name, ...)
>>
>> #Down in index_fd():
>>
>> sha1_file.c/index_fd() calls index_core() (after consulting the attributes)
>> index_core() reads the file from the working tree into memory using
>> read_in_full(), and after that index_mem() calls hash_sha1_file(buf)
>> to calculate the sha1.
>>
>> Before the hashing is done, index_mem() runs
>> convert_to_git(path, buf, size, &nbuf, SAFE_CRLF_FALSE)
>> to convert  "blobs to git internal format".
>>
>>
>> Here, convert_to_git() consults the .gitattributes (again) to find out that
>> crlf_action is CRLF_AUTO in our case.
>> The "new safer autocrlf handling" says that if a blob as any CR, don't convert
>> CRLFs at "git add".
>>
>> convert.c/crlf_to_git() starts to do it's job:
>> - look at buf (It has CRLF, conversion may be needed)
>> - consult blob_has_cr()
>>   # Note: Before this patch, has_cr_in_index(path)) was used
>>
>> #Again, before this patch,
>> has_cr_in_index(path) calls read_blob_data_from_cache(path, &sz) to read the
>> blob into memory.
>>
>> Now read_blob_data_from_cache() is a macro, and we end up in
>> read_blob_data_from_index(&the_index, (path), (sz))
>>
>> read-cache.c/read_blob_data_from_index() starts its work:
>> 	pos = index_name_pos(istate, path, len);
>> 	if (pos < 0) {
>> 		/*
>> 		 * We might be in the middle of a merge, in which
>> 		 * case we would read stage #2 (ours).
>> 		 */
>>
>> # And here, and this is important to notice, "ours" is sha1 ad55e2,
>> # which corresponds to "first line\r\nsame line\r\n"
> 
> Where did 99b633 come from then?  There still is something missing
> in this description.
> 
> Puzzled...

This is an unfinished attempt for a commit message: -------------------------------------------------- correct ce_compare_data() at the end of a merge

The following didn't work as expected:
 - At the end of a merge
 - merge.renormalize is true,
 - .gitattributes = "* text=auto"
 - core.eol = crlf

Merge a blob with CRLF "first line\r\nsame line\r\n" and a blob with LF "first line\nsame line\n".

The expected result of the merge is "first line\nsame line\n".

The content in the working tree is "first line\r\nsame line\r\n", and ce_compare_data() should find that the content is clean and return 0.

The following callstack does not work: merge-recursive.c/add_cacheinfo(path=file sha1=0x99b633) #Calls refresh_cache_ent(&the_index, ce, options, NULL, NULL);

#Calls ie_modified()

#Calls read-cache.c/ie_match_stat()

#Calls changed |= ce_modified_check_fs(ce, st);

#Calls ce_compare_data(path=file sha1=0x99b633)

#Calls index_fd(..., ..., ce->name, ...) #Note the sha is lost here.

#Calls index_core()

index_core() reads the file from the working tree into memory using read_in_full(), and after that index_mem() calls hash_sha1_file(buf) to calculate the sha1. Before the hashing is done, index_mem() runs convert_to_git(path, buf, size, &nbuf, SAFE_CRLF_FALSE) to convert "blobs to git internal format".

Here, convert_to_git() consults the .gitattributes (again) to find out that crlf_action is CRLF_AUTO in our case. The "new safer autocrlf handling" says that if a blob as any CR, don't convert CRLFs at "git add".

convert.c/crlf_to_git() starts to do it's job:
- look at buf (It has CRLF, conversion may be needed)
- consult blob_has_cr()
  # Note: Before this patch, has_cr_in_index(path)) was used

#Before this patch, has_cr_in_index(path) #Calls read_blob_data_from_cache(path, &sz) to read the blob into memory.

Now read_blob_data_from_cache() is a macro, and we end up in read_blob_data_from_index(&the_index, (path), (sz))

read-cache.c/read_blob_data_from_index() starts its work:
	pos = index_name_pos(istate, path, len);
	if (pos < 0) {
		/*
		 * We might be in the middle of a merge, in which
		 * case we would read stage #2 (ours).
		 */

# And here, and this is important to notice, "ours" is sha1 ad55e2, # which corresponds to "first line\r\nsame line\r\n"

The result of the check is that the blob 99b633 doesn't seem
to match the working tree, and the whold merge is aborted:
  error: addinfo_cache failed for path 'file'

Solution: Make sure that ce_compare_data() forwards the source sha1 into crlf_to_git().

Replace has_cr_in_index(path) with blob_has_cr(sha1), and forward the source sha1 from ce_compare_data() into blob_has_cr(sha1).

While at it, rename has_cr_in_index() into blob_has_cr() and replace 0 with SAFE_CRLF_FALSE.

Add a TC in t6038 to have a test coverage under Linux.
Previous: Junio C HamanoNext: Junio C Hamano
Message 11 of 24 in “What's cooking in git.git (Jun 2016, #09; Mon, 27)”
  1. Junio C HamanoJun 27, 2016
  2. 0/3 unified auto CRLF handling, V3tboegi@web.de, Jun 28, 2016
  3. 1/3 convert: unify the "auto" handling of CRLFtboegi@web.de, Jun 28, 2016
  4. 2/3 read-cache: factor out get_sha1_from_index() helpertboegi@web.de, Jun 28, 2016
  5. 3/3 correct ce_compare_data() in a middle of a mergetboegi@web.de, Jun 28, 2016
  6. Junio C HamanoJun 29, 2016
  7. Torsten BögershausenJun 30, 2016
  8. Junio C HamanoJul 1, 2016
  9. Torsten BögershausenJul 2, 2016
  10. Junio C HamanoJul 6, 2016
  11. Torsten BögershausenJul 7, 2016
  12. Junio C HamanoJul 7, 2016
  13. Junio C HamanoJul 7, 2016
  14. Torsten BögershausenJul 8, 2016
  15. Junio C HamanoJul 8, 2016
  16. Torsten BögershausenJul 8, 2016
  17. Junio C HamanoJul 8, 2016
  18. Junio C HamanoJul 8, 2016
  19. Junio C HamanoJul 8, 2016
  20. Junio C HamanoJul 8, 2016
  21. Junio C HamanoJul 11, 2016
  22. Torsten BögershausenJul 12, 2016
  23. Junio C HamanoJul 12, 2016
  24. Torsten BögershausenJul 8, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.