threads / discuss / 3071

Question on empty commit

Subject: Question on empty commit

## tl;dr

6 messages between Jan 14, 2006 and Jan 16, 2006.

replies: 5people: 2as markdown or json

Luben Tuikov· Jan 14, 2006, 02:18 UTC · lore
Hi,
Scenario:
trunk remote
  |
  \/
trunk local
     \
      `--> tree A
               \
                `--> tree B
         Figure 1.

This is the dependecny since project B depends on project A introduced in tree A. And merging is done as follows (branch per directory; everything but HEAD and index is shared):

cd trunk && git pull trunk cd ../treeA && git merge "merge trunk" HEAD trunk cd ../treeB && git merge "merge tree A" HEAD treeA

But this isn't the only way to do it since tree B depends only on the introduced into tree A project A, and both project A and project B depend on the trunk, as follows:

trunk remote
  |
  \/
trunk local
  \  \
   \  `--> tree A
    \          \
     `--------> `--> tree B
         Figure 2.

So at point in time t where tree B had been synced with tree A, and tree A had been synced with trunk, but trunk had _not_ been synced with remote trunk, a merge was done following Figure 2:

cd trunk && git pull trunk cd ../treeA && git merge "merge trunk" HEAD trunk cd ../treeB && git merge "merge trunk" HEAD trunk git merge "merge tree A" HEAD treeA

Now the last merge introduced an empty commit, since tree A and tree B had been in sync (only local and remote trunk had been out of sync). While it was expected that no commit would be introduced since they were in sync.

Was the empty commit correct behavior?
Thanks,
   Luben
Junio C Hamano· Jan 15, 2006, 09:48 UTC · re: Luben Tuikov · lore

Re: Question on empty commit

Luben Tuikov <ltuikov@yahoo.com> writes:
Show 6 quoted lines
> Now the last merge introduced an empty commit, since
> tree A and tree B had been in sync (only local and
> remote trunk had been out of sync).  While it was expected
> that no commit would be introduced since they were in sync.
>
> Was the empty commit correct behavior?

I do not quite follow you, but immediately before the "empty commit" (I presume you mean the last "git merge" that merges treeA head in treeB), you say "the treeA and treeB had been in sync". What do you exactly mean? The tree object in the head commits in treeA branch and treeB branch were identical?

If that is the case, the commit being empty is the correct behaviour, because there is no difference in the set of files introduced by that commit. And the commit being made is also the correct behaviour, because those two branches have different development history, and the commit is what binds them together.

Luben Tuikov· Jan 16, 2006, 21:58 UTC · re: Junio C Hamano · lore

Re: Question on empty commit

--- Junio C Hamano <junkio@cox.net> wrote:
Show 13 quoted lines
> Luben Tuikov <ltuikov@yahoo.com> writes:
> 
> > Now the last merge introduced an empty commit, since
> > tree A and tree B had been in sync (only local and
> > remote trunk had been out of sync).  While it was expected
> > that no commit would be introduced since they were in sync.
> >
> > Was the empty commit correct behavior?
> 
> I do not quite follow you, but immediately before the "empty
> commit" (I presume you mean the last "git merge" that merges
> treeA head in treeB), you say "the treeA and treeB had been in
> sync".

Meaning that merging Tree A into Tree B would introduce 0 changes to Tree B.

> What do you exactly mean?  The tree object in the head
> commits in treeA branch and treeB branch were identical?

They cannot be, since Tree B contains Project B, which is not present in Tree A, but it depends on Project A in Tree A.

Show 5 quoted lines
> If that is the case, the commit being empty is the correct
> behaviour, because there is no difference in the set of files
> introduced by that commit.  And the commit being made is also
> the correct behaviour, because those two branches have different
> development history, and the commit is what binds them together.

So a new commit object, with 0 files changed was the correct behavior after all?

I'd expect that there would be no commit object whatsoever since those trees had been synced before. I.e. the only thing which changed was the trunk and that trunk was synced to Tree A and Tree B separately.

Thanks,
    Luben
Luben Tuikov· Jan 16, 2006, 22:57 UTC · re: Luben Tuikov · lore

Re: Question on empty commit

--- Luben Tuikov <ltuikov@yahoo.com> wrote:
Show 40 quoted lines
> --- Junio C Hamano <junkio@cox.net> wrote:
> 
> > Luben Tuikov <ltuikov@yahoo.com> writes:
> > 
> > > Now the last merge introduced an empty commit, since
> > > tree A and tree B had been in sync (only local and
> > > remote trunk had been out of sync).  While it was expected
> > > that no commit would be introduced since they were in sync.
> > >
> > > Was the empty commit correct behavior?
> > 
> > I do not quite follow you, but immediately before the "empty
> > commit" (I presume you mean the last "git merge" that merges
> > treeA head in treeB), you say "the treeA and treeB had been in
> > sync".
> 
> Meaning that merging Tree A into Tree B would introduce 0 changes
> to Tree B.
> 
> > What do you exactly mean?  The tree object in the head
> > commits in treeA branch and treeB branch were identical?
> 
> They cannot be, since Tree B contains Project B, which is not
> present in Tree A, but it depends on Project A in Tree A.
> 
> > If that is the case, the commit being empty is the correct
> > behaviour, because there is no difference in the set of files
> > introduced by that commit.  And the commit being made is also
> > the correct behaviour, because those two branches have different
> > development history, and the commit is what binds them together.
> 
> So a new commit object, with 0 files changed was the correct behavior
> after all?
> 
> I'd expect that there would be no commit object whatsoever since
> those trees had been synced before.  I.e. the only thing which changed
> was the trunk and that trunk was synced to Tree A and Tree B separately.
> 
> Thanks,
>     Luben

Ok, so what I'm seeing is that git history records what the _user did_ as opposed to _what happened to the code_.

Is it possible to have an env var to control this "feature". I.e. if the result is 0 lines 0 files changed, then do not update the index and/or the git database.

Thanks Junio,
    Luben
Junio C Hamano· Jan 16, 2006, 23:32 UTC · re: Luben Tuikov · lore

Re: Question on empty commit

Luben Tuikov <ltuikov@yahoo.com> writes:
Show 6 quoted lines
> Ok, so what I'm seeing is that git history records what the _user did_
> as opposed to _what happened to the code_.
>
> Is it possible to have an env var to control this "feature".  I.e. if
> the result is 0 lines 0 files changed, then do not update the index and/or
> the git database.

Good question. We could go stronger than that, and say it would make more sense not to make that commit by default, except when the merge was done with "ours" strategy (it explicitly asks for an empty commit to make it stand out that trees were in sync at that point), or the merge was manually resolved and the result happens to match what treeB already had.

Why?
          treeB ---------o--o--o--*--o--o---*--o..?
                                 /         /     .
                                /         /     .
          common upstream ---- / --o--o--o--o  .
                              /           \   . 
                             /             \ .
          treeA ------o--o--o---------------*
                                    ^
                                    |
                     No commit on this segment.
                o are commits, * are merge commits.

At point '?' we can choose not to make a merge. You could, after seeing such an empty merge, emulate this behaviour by doing "git reset --hard HEAD^" yourself. Then some time later, when treeA truly has something new to offer treeB, the next merge attempt will create a true merge commit at '!':

          treeB ---------o--o--o--*--o--o---*--o--?--o--o--!
                                 /         /     .        /
                                /         /     .        /
          common upstream ---- / --o--o--o--o  .        /
                              /           \   .        /
                             /             \ .        / 
          treeA ------o--o--o---------------*--o--o--o

When we have the last two 'o' commits on treeB development track, creating '?' merge _might_ have some value, because we can clearly see that those two new 'o' commits on treeB are compatible with the '*' merge commit on treeA track. Without the merge at '?' we would not be able to tell that, so in that sense it is a small plus to have '?' merge commit, and it may even help bisection bug hunting. But in practice it may not matter and just clutter your history.

Especially if we did not have the last two 'o' commits on treeB line, making an empty merge commit at '?' would mostly be pointless.

          treeB ---------o--o--o--*--o--o---*--o--?--------!
                                 /         /     .        /
                                /         /     .        /
          common upstream ---- / --o--o--o--o  .        /
                              /           \   .        /
                             /             \ .        / 
          treeA ------o--o--o---------------*--o--o--o

But unfortunately we cannot tell if we are going to build on top of '?' before we merge with treeA again next time at '!', when we are at point '?', so we could have an option or environment variable to control this "feature", but unless you can predict the future at point '?', deciding whether to use that option would be rather difficult.

Junio C Hamano· Jan 16, 2006, 22:58 UTC · re: Luben Tuikov · lore

Re: Question on empty commit

Luben Tuikov <ltuikov@yahoo.com> writes:
Show 7 quoted lines
>> I do not quite follow you, but immediately before the "empty
>> commit" (I presume you mean the last "git merge" that merges
>> treeA head in treeB), you say "the treeA and treeB had been in
>> sync".
>
> Meaning that merging Tree A into Tree B would introduce 0 changes
> to Tree B.

You had two separate development tracks treeA and treeB, and the entire merge result is taken from treeB because it had all the changes from the common upstream already and there was no development in treeA on its own:

          treeB ---------o--o--o--*--o--o---*--o..?
                                 /         /     .
                                /         /     .
          common upstream ---- / --o--o--o--o  .
                              /           \   . 
                             /             \ .
          treeA ------o--o--o---------------*
                                    ^
                                    |
                     No commit on this segment.
                o are commits, * are merge commits.

and we are trying to merge the last commit on treeA line into the tip of treeB. The last merge commit on the treeB line of development would introduce zero change from treeB's point of view, because there is nothing new treeB would get by merging with treeA.

Before the '?' mergepoint, what is known about treeA and treeB was that treeB contained everything up to the third from the left 'o' commit on treeA line. We now noticed that treeA has some changes since then (i.e. all the good stuff from the common upstream), and merge even has noticed that these changes happen to be already what treeB already had and you did not have to hand resolve (or you might have had to; I dunno). Making a commit for '?' merge records the fact that these two tips are in sync. IOW, earlier treeB did not know about the merge commit at the tip of treeA; now it does.

After this sync, if you try to merge again with treeA, no merge commit would be made, of course. Now you are truly up-to-date.

When git talks about a branch being up-to-date wrt another branch, it is not about "all of the patches that the other line has have been applied to our tree" (we are not a patchset based system like darcs). It is about commit ancestry, IOW, "in the past we have merged with them, and all the commits that line has right now are what we have already examined when we made that merge" is what we mean by us being up-to-date wrt them.

Fast-forward is the other way around but the principle is the same. We are fast-forward to them not because we have applied all the patches they have and we have more. We are fast-forward because we have seen all of their commits in the past and recorded in our commit ancestry chain that fact as a merge, and in addition we have one or more commits on top of that merge commit. That is when we are fast-forward wrt to them.

← back to recent threads