Subject: Re: How to selectively recreate merge state?

From: Michael J Gruber <[email-masked]>
Date: Fri, 11 Dec 2009 13:33:34 +0100

Jakub Narebski venit, vidit, dixit 11.12.2009 12:20:
> Dnia piątek 11. grudnia 2009 11:44, Michael J Gruber napisał:
>> Jakub Narebski venit, vidit, dixit 11.12.2009 02:33:
>>> Dnia piątek 11. grudnia 2009 02:11, Junio C Hamano napisał:
>>>> Jakub Narebski <[email-masked]> writes:
>>>>
>>>>>   --unresolve::
>>>>>         Restores the 'unmerged' or 'needs updating' state of a
>>>>>         file during a merge if it was cleared by accident.
>>>>>
>>>>> Unless "git add foo" not only adds current contents of foo at stage 0,
>>>>> but also removes higher stages from index...
>>>>
>>>> By definition, adding anything at stage #0 is to remove higher stages.
>>>
>>> Hmmm... let's test it:
>>>
>>>  $ git merge side-branch 
>>>  Auto-merging foo
>>>  CONFLICT (content): Merge conflict in foo
>>>  Automatic merge failed; fix conflicts and then commit the result.
>>>  $ git ls-files --stage
>>>  100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 1       foo
>>>  100644 3bd1f0e29744a1f32b08d5650e62e2e62afb177c 2       foo
>>>  100644 469a41eda5c8b45503a3bfc32ad6b5decc658132 3       foo
>>>  $ <edit foo>
>>>  $ git add foo
>>>  $ git ls-files --stage
>>>  100644 a1b58d38ffa61e8e99b7cb95cdf540aedf2a96b3 0       foo
> 
> I thought that "git add foo" only adds current contents of foo in stage 0,
> and does not delete other stages.
>  
> Unless "git add foo" does more than "git update-index foo" does here.

Quoting Junio:

By definition, adding anything at stage #0 is to remove higher stages.

Could one leave 1 alone but still mark the conflict resolved?

>>> Now let's test '--unresolve' option of git-update-index:
>>>
>>>  $ git update-index --unresolve foo
>>>  $ git ls-files --stage foo
>>>  100644 3bd1f0e29744a1f32b08d5650e62e2e62afb177c 2       foo
>>>  100644 469a41eda5c8b45503a3bfc32ad6b5decc658132 3       foo
>>>
>>> WTF? What happened to stage 1 (ancestor)?
>>
>> 2 and 3 are easy (cheap) to recreate from HEAD and MERGE_HEAD, 1 is not.
>> I guess that's why --unresolve doesn't even attempt to do anything with 1.
> 
> But then "git update-index --unresolve <file>" is next to useless.

Well, I'm not defending current behaviour, just describing its
implementation.

> 
>>>
>>>  $ git checkout --conflict=merge foo
>>>  error: path 'foo' does not have all three versions
>>>
>>> Let's recover it by hand:
>>>
>>>  $ echo -e "100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 1\tfoo" | 
>>>    git update-index --index-info
>>>  $ git ls-files --stage foo
>>>  100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 1       foo
>>>  100644 3bd1f0e29744a1f32b08d5650e62e2e62afb177c 2       foo
>>>  100644 469a41eda5c8b45503a3bfc32ad6b5decc658132 3       foo
>>>  $ git checkout --conflict=merge foo
>>
>> Yeah, if we knew that sha1...
> 
> Isn't it:
> 
>   $ git ls-tree $(git merge-base HEAD MERGE_HEAD) -- foo
> 
> or
> 
>   $ git rev-parse "$(git merge-base HEAD MERGE_HEAD):foo"

Yes, sure. That's why I wrote "cheap": --unresolve simply reads HEAD and
MERGE_HEAD. Resetting 1 requires (re)calculation of the merge base.

Michael