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)