diff --git a/.gitignore b/.gitignore index 88cf2e9..e605e1e 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ pkg # # For vim: *.swp +# +# For IDEA: +.idea/ +*.iml diff --git a/lib/homesick.rb b/lib/homesick.rb index 17d67f7..cf7f5bf 100644 --- a/lib/homesick.rb +++ b/lib/homesick.rb @@ -148,22 +148,49 @@ class Homesick < Thor desc "track FILE CASTLE", "add a file to a castle" def track(file, castle) castle = Pathname.new(castle) - file = Pathname.new(file) + file = Pathname.new(file.chomp('/')) check_castle_existance(castle, 'track') absolute_path = file.expand_path - castle_path = castle_dir(castle) - mv absolute_path, castle_path + relative_dir = absolute_path.relative_path_from(home_dir).dirname + castle_path = Pathname.new(castle_dir(castle)).join(relative_dir) + + unless castle_path.exist? + FileUtils.mkdir_p castle_path + end + + # Are we already tracking this or anything inside it? + target = Pathname.new(castle_path.join(file.basename)) + if target.exist? + if absolute_path.directory? + move_dir_contents(target, absolute_path) + absolute_path.rmtree + manifest_remove(castle, relative_dir + file.basename) + + elsif more_recent? absolute_path, target + target.delete + mv absolute_path, castle_path + else + shell.say_status(:track, "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.", :blue) unless options[:quiet] + end + else + mv absolute_path, castle_path + end inside home_dir do - absolute_path = castle_dir(castle) + file.basename - home_path = home_dir + file + absolute_path = castle_path + file.basename + home_path = home_dir + relative_dir + file.basename ln_s absolute_path, home_path end inside castle_path do git_add absolute_path end + + # are we tracking something nested? Add the parent dir to the manifest + unless relative_dir.eql?(Pathname.new('.')) + manifest_add(castle, relative_dir) + end end desc "list", "List cloned castles" @@ -255,4 +282,54 @@ class Homesick < Thor git_push end end + + def manifest(castle) + Pathname.new(repos_dir.join(castle, '.manifest')) + end + + def manifest_add(castle, path) + manifest_path = manifest(castle) + File.open(manifest_path, 'a+') do |manifest| + manifest.puts path unless manifest.readlines.inject(false) { |memo, line| line.eql?("#{path.to_s}\n") || memo } + end + + inside castle_dir(castle) do + git_add manifest_path + end + end + + def manifest_remove(castle, path) + manifest_file = manifest(castle) + if manifest_file.exist? + lines = IO.readlines(manifest_file).delete_if { |line| line == "#{path}\n" } + File.open(manifest_file, 'w') { |manfile| manfile.puts lines } + end + + inside castle_dir(castle) do + git_add manifest_file + end + end + + def move_dir_contents(target, dir_path) + child_files = dir_path.children + child_files.each do |child| + + target_path = target.join(child.basename) + if target_path.exist? + if more_recent?(child, target_path) && target.file? + target_path.delete + mv child, target + end + next + end + + mv child, target + end + end + + def more_recent?(first, second) + first_p = Pathname.new(first) + second_p = Pathname.new(second) + first_p.mtime > second_p.mtime && !first_p.symlink? + end end diff --git a/spec/homesick_spec.rb b/spec/homesick_spec.rb index 90132ed..acecdcb 100644 --- a/spec/homesick_spec.rb +++ b/spec/homesick_spec.rb @@ -197,5 +197,70 @@ describe "homesick" do some_rc_file.readlink.should == tracked_file end + + it 'should track a file in nested folder structure' do + castle = given_castle('castle_repo') + + some_nested_file = home.file('some/nested/file.txt') + homesick.track(some_nested_file.to_s, 'castle_repo') + + tracked_file = castle.join('some/nested/file.txt') + tracked_file.should exist + some_nested_file.readlink.should == tracked_file + end + + it 'should track a nested directory' do + castle = given_castle('castle_repo') + + some_nested_dir = home.directory('some/nested/directory/') + homesick.track(some_nested_dir.to_s, 'castle_repo') + + tracked_file = castle.join('some/nested/directory/') + tracked_file.should exist + File.realdirpath(some_nested_dir).should == File.realdirpath(tracked_file) + end + + describe "manifest" do + + it 'should add the nested files parent to the manifest' do + castle = given_castle('castle_repo') + + some_nested_file = home.file('some/nested/file.txt') + homesick.track(some_nested_file.to_s, 'castle_repo') + + manifest = Pathname.new(castle.parent.join('.manifest')) + File.open(manifest, 'r') do |f| + f.readline.should == "some/nested\n" + end + end + + it 'should NOT add anything if the files parent is already listed' do + castle = given_castle('castle_repo') + + some_nested_file = home.file('some/nested/file.txt') + other_nested_file = home.file('some/nested/other.txt') + homesick.track(some_nested_file.to_s, 'castle_repo') + homesick.track(other_nested_file.to_s, 'castle_repo') + + manifest = Pathname.new(castle.parent.join('.manifest')) + File.open(manifest, 'r') do |f| + f.readlines.size.should == 1 + end + end + + it 'should remove the parent of a tracked file from the manifest if the parent itself is tracked' do + castle = given_castle('castle_repo') + + some_nested_file = home.file('some/nested/file.txt') + nested_parent = home.directory('some/nested/') + homesick.track(some_nested_file.to_s, 'castle_repo') + homesick.track(nested_parent.to_s, 'castle_repo') + + manifest = Pathname.new(castle.parent.join('.manifest')) + File.open(manifest, 'r') do |f| + f.each_line { |line| line.should_not == "some/nested\n" } + end + end + end end end