lib/gitdocs/runner.rb in gitdocs-0.1.5 vs lib/gitdocs/runner.rb in gitdocs-0.2.0

- old
+ new

@@ -5,75 +5,93 @@ def initialize(root, opts = nil) @root = root out, status = sh_with_code "which growlnotify" @use_growl = opts && opts.key?(:growl) ? opts[:growl] : status.success? @polling_interval = opts && opts[:polling_interval] || 15 - @icon = File.expand_path("../img/icon.png", __FILE__) + @icon = File.expand_path("../../img/icon.png", __FILE__) end def run info("Running gitdocs!", "Running gitdocs in `#{@root}'") - @current_remote = sh_string("git config branch.`git branch | grep '^\*' | sed -e 's/\* //'`.remote", "origin") - @current_branch = sh_string("git branch | grep '^\*' | sed -e 's/\* //'", "master") - @current_revision = sh("git rev-parse HEAD").strip rescue nil + @current_remote = sh_string("git config branch.`git branch | grep '^\*' | sed -e 's/\* //'`.remote", 'origin') + @current_branch = sh_string("git branch | grep '^\*' | sed -e 's/\* //'", 'master') + @current_revision = sh_string("git rev-parse HEAD") + mutex = Mutex.new + # Pull changes from remote repository Thread.new do loop do - mutex.synchronize do - begin - out, status = sh_with_code("git fetch --all && git merge #{@current_remote}/#{@current_branch}") - if status.success? - changes = get_latest_changes - unless changes.empty? - info("Updated with #{changes.size} change#{changes.size == 1 ? '' : 's'}", "`#{@root}' has been updated") - end - else - warn("Error attempting to pull", out) - end - push_changes - rescue Exception - error("There was an error", $!.message) rescue nil - end - end + mutex.synchronize { sync_changes } sleep @polling_interval end - end + end.abort_on_exception = true + + # Listen for changes in local repository listener = FSEvent.new listener.watch(@root) do |directories| directories.uniq! directories.delete_if {|d| d =~ /\/\.git/} unless directories.empty? - mutex.synchronize do - push_changes - end + mutex.synchronize { push_changes } end end at_exit { listener.stop } listener.run end + def sync_changes + out, status = sh_with_code("git fetch --all && git merge #{@current_remote}/#{@current_branch}") + if status.success? + changes = get_latest_changes + unless changes.empty? + author_list = changes.inject(Hash.new{|h, k| h[k] = 0}) {|h, c| h[c['author']] += 1; h}.to_a.sort{|a,b| b[1] <=> a[1]}.map{|(name, count)| "* #{name} (#{count} change#{count == 1 ? '' : 's'})"}.join("\n") + info("Updated with #{changes.size} change#{changes.size == 1 ? '' : 's'}", "In `#{@root}':\n#{author_list}") + end + push_changes + elsif out[/CONFLICT/] + conflicted_files = sh("git ls-files -u --full-name -z").split("\0"). + inject(Hash.new{|h, k| h[k] = []}) {|h, line| + parts = line.split(/\t/) + h[parts.last] << parts.first.split(/ /) + h + } + warn("There were some conflicts", "#{conflicted_files.keys.map{|f| "* #{f}"}.join("\n")}") + conflicted_files.each do |conflict, ids| + conflict_start, conflict_end = conflict.scan(/(.*?)(|\.[^\.]+)$/).first + puts "solving #{conflict} with #{ids.inspect}" + ids.each do |(mode, sha, id)| + author = " original" if id == "1" + system("cd #{@root} && git show :#{id}:#{conflict} > '#{conflict_start} (#{sha[0..6]}#{author})#{conflict_end}'") + end + system("cd #{@root} && git rm #{conflict}") or raise + end + push_changes + elsif sh_string("git remote").nil? # no remote to pull from + # Do nothing, no remote repo yet + else + error("There was a problem synchronizing this gitdoc", "A problem occurred in #{@root}:\n#{out}") + end + end + def push_changes sh 'find . -type d -regex ``./[^.].*'' -empty -exec touch \'{}/.gitignore\' \;' sh 'git add .' # TODO make this message nicer sh "git commit -a -m'Auto-commit from gitdocs'" unless sh("git status -s").strip.empty? - if @current_revision.nil? + if @current_revision.nil? || sh('git status')[/branch is ahead/] out, code = sh_with_code("git push #{@current_remote} #{@current_branch}") if code.success? changes = get_latest_changes info("Pushed #{changes.size} change#{changes.size == 1 ? '' : 's'}", "`#{@root}' has been pushed") + elsif @current_revision.nil? + # ignorable + elsif out[/\[rejected\]/] + warn("There was a conflict in #{@root}, retrying", "") else - error("Could not push changes", out) + error("BAD Could not push changes in #{@root}", out) + exit end - elsif sh('git status')[/branch is ahead/] - out, code = sh_with_code("git push") - if code.success? - changes = get_latest_changes - info("Pushed #{changes.size} change#{changes.size == 1 ? '' : 's'}", "`#{@root}' has been pushed") - else - error("Could not push changes", out) - end end end def get_latest_changes if @current_revision @@ -113,14 +131,13 @@ if @use_growl Growl.notify_error(msg, :title => title) else Kernel.warn("#{title}: #{msg}") end - raise end # sh_string("git config branch.`git branch | grep '^\*' | sed -e 's/\* //'`.remote", "origin") - def sh_string(cmd, default) + def sh_string(cmd, default=nil) val = sh(cmd).strip rescue nil (val.nil? || val.empty?) ? default : val end def sh(cmd)