Comment 0 for bug 1072513

Revision history for this message
Matthew Fuller (fullermd) wrote :

Depending on the shape of the history graph, doing "log" on a directory can yield very unexpected results.

This is a very long explanation. I don't have time to make it shorter :(

The graph is convoluted enough you probably want to use qlog to look at it. However, here's a quick overview:

    7 Matthew Fuller 2012-10-28
      Nada

    6 Matthew Fuller 2012-10-28 [merge]
      Merge C:1

          1.2.4 Matthew Fuller 2012-10-28 [merge]
                Merge A:2

          1.2.3 Matthew Fuller 2012-10-28
                Nada

          1.2.2 Matthew Fuller 2012-10-28 [merge]
                Merge A:1
                A foo/
                A foo/bar

          1.2.1 Matthew Fuller 2012-10-28
                Nada

    5 Matthew Fuller 2012-10-28
      Nada

    4 Matthew Fuller 2012-10-28
      Empty to force merge

    3 Matthew Fuller 2012-10-28
      Edit bar
      M foo/bar

    2 Matthew Fuller 2012-10-28 [merge]
      Merge B:1
      A foo/
      A foo/bar

          1.1.1 Matthew Fuller 2012-10-28
                Create foo
                A foo/
                A foo/bar

    1 Matthew Fuller 2012-10-28
      Init

C started at (1). The A:1 merge landed (2,3,4), and A:2 landed (5).

Now, depending on minor alterations I make, mostly to revs that don't touch foo, I get differing results from log.

The various permutations of running the script below look like this:

Without -n0, I only ever see

  "Edit bar" (3)
  "Merge B:1" (2)

which is probably what's expected.

If I uncomment the mk_dir= at [3] to make foo a file rather than a
directory, I get

  "Edit bar" (3)
  "Merge B:1" (2)
    "Create foo" (1.1.1)

which may possibly be the "right" answer (though see below), and none of the below changes have any effect.

If, however, I run a "log -v" instead, I see only

    "Merge A:1" (1.2.2)

just like the directory version below, and the described alterations have the same effects.

However, with foo/ as a directory (the presence/absence of -v makes no difference in this case):

Running the whole thing, I get only

    "Merge A:1" (1.2.2)

which is the *least* useful possible answer, since it tells me nothing at all about most of the revs that touched files in the dir, only the merge into C of the "nothing" -> "final state" transition.

If I take out any one of the Nada's labelled [1] I get

    "Merge A:1" (1.2.2)
  "Edit bar" (3)

which means I see the above "everything rolled up" merge, plus the edit rev. But I never see the rev that added foo/bar in its original form.

If I take out any 2 of [1], or uncomment the "exit" to skip the [2] block, I get
    "Merge A:1" (1.2.2)
  "Edit bar" (3)
  "Merge B:1" (2)

which means in addition to the above, I now see the merge of the original creation, though not the creation itself.

If I take out any 3 or more of [1], or 1+ of the [1] AND the whole [2] block, I
get
    "Merge A:1" (1.2.2)
  "Edit bar" (3)
  "Merge B:1" (2)
    "Create foo" (1.1.1)

which now gives me every rev that changes, relative to its left-hand parent, something in foo/.

I believe either the 3-rev version shown without -v for the file version (3, 2, 1.1) OR the 4-rev version for dirs with several deletions (1.2.2, 3, 2, 1.1) are "correct"; I'm not sure which is "better".

However, I'm _quite_ sure all the 1- and 2-rev versions, and that the 3-rev in the deletions are wrong wrong wrong. And very unpleasant to run across, since you may not realize bzr is lying to you and take log at face value.

I checked this with bzr.dev, 2.5.1, and the latest 2.0; all acted identically.

Creation script (inline):

#!/bin/sh -e
#bzr="/home/fullermd/src/bzr/bzr.dev/bzr --no-plugins --no-aliases"
bzr="/usr/local/bin/bzr --no-plugins --no-aliases"

if [ -d "test" ]; then
 echo "test/ already exists"
 exit
fi

mk_dir=true
# [3]
#mk_dir=false

${bzr} init-repo test
(
 cd test

 # First, create a branch with a rev
 ${bzr} init A
 (
  cd A
  ${bzr} ci --unchanged -m 'Init'
 )

 # Now work on a file
 ${bzr} branch A B
 (
  cd B

  if $mk_dir; then
   mkdir foo
   echo bar > foo/bar
  else
   touch foo
  fi

  ${bzr} add
  ${bzr} ci -m 'Create foo'
 )

 # Merge that
 (
  cd A
  ${bzr} merge ../B
  ${bzr} ci -m "Merge B:1"
  if $mk_dir; then
   echo baz > foo/bar
  else
   echo baz > foo
  fi
  ${bzr} ci --unchanged -m 'Edit bar'
  # [1]
  ${bzr} ci --unchanged -m 'Nada'
 )

 # Now make another branch from the start of A, and merge later stuff
 # from A
 ${bzr} branch -r1 A C
 (
  cd C
  # [1]
  ${bzr} ci --unchanged -m 'Nada'
  ${bzr} merge ../A
  ${bzr} ci -m 'Merge A:1'
  # [1]
  ${bzr} ci --unchanged -m 'Nada'
 )

 # Make another change, and merge it into C
 # [2]
 (
  #exit
  cd A
  ${bzr} ci --unchanged -m 'Empty to force merge'

  cd ../C
  ${bzr} merge ../A
  ${bzr} ci -m 'Merge A:2'
 )

 # Now land those changes back on A
 (
  cd A
  # [1]
  ${bzr} ci --unchanged -m 'Nada'
  ${bzr} merge ../C
  ${bzr} ci -m "Merge C:1"
  # [1]
  ${bzr} ci --unchanged -m 'Nada'
 )

 # Now see when foo was touched.
 (
  cd A
  ${bzr} log -n0 foo
  #${bzr} log -n0 -v foo
 )
)