1

When using rugged to remove files to stage a commit, the diff for the commit comes out with false information about which files were deleted. The files that are supposed to be there are still actually there and the resulting state of the project is correct. It's just that the diff is reporting incorrectly.

I may be misunderstanding / misusing Rugged::Tree::Builder, but other than the diffs, it everything seems to come out correctly. Note that I am leaving an empty tree object here, but I don't see why that should affect this.

Project structure:

demo-project/ └── MasterDir ├── Directory1 │   ├── File1 │   ├── File2 │   └── File3 ├── Directory2 │   ├── File4 │   └── File5 ├── Directory3 │   └── File6 └── Directory4 └── File7

Call sequence

require 'rugged'

repo = Rugged::Repository.new("/Users/davetakahashi/demo-project")

# get the tree for "MasterDir/Directory1"
tree = repo.lookup(repo.head.target.tree.path("MasterDir/Directory1")[:oid])

# initialize a builder with the tree
builder = Rugged::Tree::Builder.new(tree)

# remove the files
builder.remove("File1")

builder.remove("File2")

builder.remove("File3")

# write the tree, saving the oid to write to parent tree
empty_directory_1_oid = builder.write(repo)

# get the tree for "MasterDir"
parent_tree = repo.lookup(repo.head.target.tree.path("MasterDir")[:oid])

# initialize a builder with it
builder = Rugged::Tree::Builder.new(parent_tree)

# add the updated entry for "Directory1"
builder << {:oid => empty_directory_1_oid, :filemode => 040000, :name => "Directory1", :type => :tree}

# write to repo, save oid
new_master_dir_oid = builder.write(repo)

# oid for "MasterDir" @ HEAD
old_master_dir_oid = repo.head.target.tree.path("MasterDir")[:oid]

# new MasterDir tree with empty Directory1
new_thing = repo.lookup(new_master_dir_oid)

# original MasterDir tree 
old_thing = repo.lookup(old_master_dir_oid)

# diff shows that every file in MasterDir was deleted
old_thing.diff(new_thing).deltas

irb session:

  [demo-project (master)]$ irb

  2.1.3 :001 > require 'rugged'
   => true 

  2.1.3 :002 > repo = Rugged::Repository.new("/Users/davetakahashi/demo-project")
   => #<Rugged::Repository:70270137753220 {path: "/Users/davetakahashi/demo-project/.git/"}> 

  2.1.3 :003 > tree = repo.lookup(repo.head.target.tree.path("MasterDir/Directory1")[:oid])
   => #<Rugged::Tree:70270137716900 {oid: abdee7bc13e432263b8da96776faa870a0b705b5}>
    <"File1" edd02898ce2deab5b06f9af79c97225427aa0202>
    <"File2" 2289c92f857605706b5bb1405e8b62b188777f36>
    <"File3" 248bce651b8c13085beb99c77b63d83a9866fbe5>

  2.1.3 :004 > builder = Rugged::Tree::Builder.new(tree)
   => #<Rugged::Tree::Builder:0x007fd215310a08> 

  2.1.3 :005 > builder.remove("File1")
   => true 

  2.1.3 :006 > builder.remove("File2")
   => true 

  2.1.3 :007 > builder.remove("File3")
   => true 

  2.1.3 :008 > empty_directory_1_oid = builder.write(repo)
   => "4b825dc642cb6eb9a060e54bf8d69288fbee4904" 

  2.1.3 :009 > parent_tree = repo.lookup(repo.head.target.tree.path("MasterDir")[:oid])
   => #<Rugged::Tree:70270137598260 {oid: 67e1a15042779fd7a0bb5f55517d15acb407a36a}>
    <"Directory1" abdee7bc13e432263b8da96776faa870a0b705b5>
    <"Directory2" ba23af1963e685c6385440ea1d76d92eb0fc4728>
    <"Directory3" eaea4f258ad3488468c52e068994c6289c9ee4ea>
    <"Directory4" 3344b4aa8fec8f28d189d5d2f05285e60f26cbd4>

  2.1.3 :010 > builder = Rugged::Tree::Builder.new(parent_tree)
   => #<Rugged::Tree::Builder:0x007fd2152d2730> 

  2.1.3 :011 > builder << {:oid => empty_directory_1_oid, :filemode => 040000, :name => "Directory1", :type => :tree}
   => nil 

  2.1.3 :012 > new_master_dir_oid = builder.write(repo)
   => "a1a2ebae492b845684bb53a01981b5710a9d1814" 

  2.1.3 :013 > old_master_dir_oid = repo.head.target.tree.path("MasterDir")[:oid]
   => "67e1a15042779fd7a0bb5f55517d15acb407a36a" 

  2.1.3 :014 > new_thing = repo.lookup(new_master_dir_oid)
   => #<Rugged::Tree:70270137454060 {oid: a1a2ebae492b845684bb53a01981b5710a9d1814}>
    <"Directory1" 4b825dc642cb6eb9a060e54bf8d69288fbee4904>
    <"Directory2" ba23af1963e685c6385440ea1d76d92eb0fc4728>
    <"Directory3" eaea4f258ad3488468c52e068994c6289c9ee4ea>
    <"Directory4" 3344b4aa8fec8f28d189d5d2f05285e60f26cbd4>

  2.1.3 :015 > old_thing = repo.lookup(old_master_dir_oid)
   => #<Rugged::Tree:70270137436000 {oid: 67e1a15042779fd7a0bb5f55517d15acb407a36a}>
    <"Directory1" abdee7bc13e432263b8da96776faa870a0b705b5>
    <"Directory2" ba23af1963e685c6385440ea1d76d92eb0fc4728>
    <"Directory3" eaea4f258ad3488468c52e068994c6289c9ee4ea>
    <"Directory4" 3344b4aa8fec8f28d189d5d2f05285e60f26cbd4>

  2.1.3 :016 > old_thing.diff(new_thing).deltas
   => [#<Rugged::Diff::Delta:70270137419160 {old_file: {:oid=>"edd02898ce2deab5b06f9af79c97225427aa0202", :path=>"Directory1/File1", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory1/File1", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>, 
       #<Rugged::Diff::Delta:70270137418980 {old_file: {:oid=>"2289c92f857605706b5bb1405e8b62b188777f36", :path=>"Directory1/File2", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory1/File2", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>, 
       #<Rugged::Diff::Delta:70270137418840 {old_file: {:oid=>"248bce651b8c13085beb99c77b63d83a9866fbe5", :path=>"Directory1/File3", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory1/File3", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>, 
       #<Rugged::Diff::Delta:70270137418700 {old_file: {:oid=>"252bae4bbacebe7a44e02b688960ccaff0515ee1", :path=>"Directory2/File4", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory2/File4", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>, 
       #<Rugged::Diff::Delta:70270137418560 {old_file: {:oid=>"a4b3a268c46f5a5340ab8a59fc74ebdc38d9a74a", :path=>"Directory2/File5", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory2/File5", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>, 
       #<Rugged::Diff::Delta:70270137418420 {old_file: {:oid=>"1928ee6db9fcd32139f8b7bd315766d645aec086", :path=>"Directory3/File6", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory3/File6", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>, 
       #<Rugged::Diff::Delta:70270137418280 {old_file: {:oid=>"d4fc3289375ca9a25b52badad06540fe31f9ed73", :path=>"Directory4/File7", :size=>0, :flags=>4, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"Directory4/File7", :size=>0, :flags=>4, :mode=>0}, similarity: 0, status: :deleted>] 

So even though I've only removed 3 files, the deltas show I've deleted everything (7 files). I've tried all the options that are available on diff(), so I'm guessing I'm missing a step in using Tree::Builder. Am I not allowed to leave the empty tree object?

davetakahashi
  • 494
  • 1
  • 4
  • 12
  • 1
    From the console's output, the only thing that seems to be different between `old_thing` and `new_thing` is the entry "Directory1". Do you need the multiple uses of the TreeBuilder in the beginning to reproduce your issue? – Carlos Martín Nieto Dec 05 '14 at 20:53
  • @carlos-martín-nieto Unfortunately it still reproduces even making all 3 `remove()` calls (for File1, File2, File3) on the same `builder`. – davetakahashi Dec 05 '14 at 22:30
  • Then show that. It is hard to follow a long irb session. Reduce the example to as few operations as possible, or it's going to be a lot harder to try to help. – Carlos Martín Nieto Dec 06 '14 at 00:51
  • Of course, sorry about that. I have distilled and tried to clarify it. Thank you very much for looking at it. – davetakahashi Dec 06 '14 at 06:50

1 Answers1

2

I suspect this is because you're inserting the empty tree into another tree, which is not something that git would do. If you want to remove 'MasterDir/Directory1', then do so and it should work well.

The usual way of handling multi-level trees is via the Index which takes care of removing empty trees.

Carlos Martín Nieto
  • 5,207
  • 1
  • 15
  • 16