lib/gitdocs/repository.rb in gitdocs-0.5.0.pre3 vs lib/gitdocs/repository.rb in gitdocs-0.5.0.pre5
- old
+ new
@@ -1,16 +1,16 @@
# -*- encoding : utf-8 -*-
+require 'find'
# Wrapper for accessing the shared git repositories.
-# Rugged, grit, or shell will be used in that order of preference depending
+# Rugged or Grit will be used, in that order of preference, depending
# upon the features which are available with each option.
#
# @note If a repository is invalid then query methods will return nil, and
# command methods will raise exceptions.
#
class Gitdocs::Repository
- include ShellTools
attr_reader :invalid_reason
# Initialize the repository on the specified path. If the path is not valid
# for some reason, the object will be initialized but it will be put into an
# invalid state.
@@ -24,13 +24,14 @@
path = path_or_share.path
@remote_name = path_or_share.remote_name
@branch_name = path_or_share.branch_name
end
- @rugged = Rugged::Repository.new(path)
- @grit = Grit::Repo.new(path)
- @invalid_reason = nil
+ @rugged = Rugged::Repository.new(path)
+ @grit = Grit::Repo.new(path)
+ Grit::Git.git_timeout = 120
+ @invalid_reason = nil
rescue Rugged::OSError
@invalid_reason = :directory_missing
rescue Rugged::RepositoryError
@invalid_reason = :no_repository
end
@@ -127,10 +128,11 @@
def available_branches
return nil unless valid?
Rugged::Branch.each_name(@rugged, :local).sort
end
+ # @return [nil] if there are no commits present
# @return [String] oid of the HEAD of the working directory
def current_oid
@rugged.head.target
rescue Rugged::ReferenceError
nil
@@ -148,37 +150,49 @@
# @return [:ok] if pulled and merged with no errors or conflicts
def pull
return nil unless valid?
return :no_remote unless has_remote?
- out, status = sh_with_code("cd #{root} ; git fetch --all 2>/dev/null && git merge #{@remote_name}/#{@branch_name} 2>/dev/null")
+ begin
+ @rugged.remotes.each { |x| @grit.remote_fetch(x.name) }
+ rescue Grit::Git::GitTimeout
+ return "Fetch timed out for #{root}"
+ rescue Grit::Git::CommandFailed => e
+ return e.err
+ end
- if status.success?
- :ok
- elsif out[/CONFLICT/]
- # Find the conflicted files
- conflicted_files = sh('git ls-files -u --full-name -z').split("\0")
- .reduce(Hash.new { |h, k| h[k] = [] }) do|h, line|
- parts = line.split(/\t/)
- h[parts.last] << parts.first.split(/ /)
- h
- end
+ return :ok if remote_branch.nil? || remote_branch.tip.oid == current_oid
- # Mark the conflicted files
- conflicted_files.each do |conflict, ids|
- conflict_start, conflict_end = conflict.scan(/(.*?)(|\.[^\.]+)$/).first
- 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 --quiet #{conflict} >/dev/null 2>/dev/null") || fail
+ @grit.git.merge({ raise: true, chdir: root }, "#{@remote_name}/#{@branch_name}")
+ :ok
+ rescue Grit::Git::GitTimeout
+ "Merged command timed out for #{root}"
+ rescue Grit::Git::CommandFailed => e
+ # HACK: The rugged in-memory index will not have been updated after the
+ # Grit merge command. Reload it before checking for conflicts.
+ @rugged.index.reload
+ return e.err unless @rugged.index.conflicts?
+
+ conflicted_paths = @rugged.index.map do |index_entry|
+ filename, extension = index_entry[:path].scan(/(.*?)(|\.[^\.]+)$/).first
+
+ author = ' original' if index_entry[:stage] == 1
+ short_oid = index_entry[:oid][0..6]
+ new_filename = "#{filename} (#{short_oid}#{author})#{extension}"
+ File.open(File.join(root, new_filename), 'wb') do |f|
+ f.write(Rugged::Blob.lookup(@rugged, index_entry[:oid]).content)
end
- conflicted_files.keys
- else
- out # return the output on error
+ index_entry[:path]
end
+
+ conflicted_paths.uniq!
+ conflicted_paths.each { |path| FileUtils.remove(File.join(root, path), force: true) }
+
+ # NOTE: leave the commit until the next push
+
+ conflicted_paths
end
# Commit and push the repository
#
# @return [nil] if the repository is invalid
@@ -189,23 +203,38 @@
def push(last_synced_oid, message='Auto-commit from gitdocs')
return nil unless valid?
return :no_remote unless has_remote?
#add and commit
- Dir.glob(File.join(root, '**', '*'))
- .select { |x| File.directory?(x) && Dir.glob("#{x}/*").empty? }
- .each { |x| FileUtils.touch(File.join(x, '.gitignore')) }
- Dir.chdir(root) do
- @rugged.index.add_all
- @rugged.index.update_all
+ Find.find(root).each do |path|
+ Find.prune if File.basename(path) == '.git'
+ if File.directory?(path) && Dir.entries(path).count == 2
+ FileUtils.touch(File.join(path, '.gitignore'))
+ end
end
- @rugged.index.write
- @grit.commit_index(message) if @rugged.index.count
- remote_branch = Rugged::Branch.lookup(@rugged, "#{@remote_name}/#{@branch_name}", :remote)
+ # Check if there are uncommitted changes
+ dirty =
+ if current_oid.nil?
+ Dir.glob(File.join(root, '*')).any?
+ else
+ @rugged.diff_workdir(current_oid, include_untracked: true).deltas.any?
+ end
- if last_synced_oid.nil? || remote_branch.nil? || remote_branch.tip.oid != @rugged.head.target
+ # Commit any changes in the working directory.
+ if dirty
+ Dir.chdir(root) do
+ @rugged.index.add_all
+ @rugged.index.update_all
+ end
+ @rugged.index.write
+ @grit.commit_index(message)
+ end
+
+ return :nothing if current_oid.nil?
+
+ if last_synced_oid.nil? || remote_branch.nil? || remote_branch.tip.oid != current_oid
begin
@grit.git.push({ raise: true }, @remote_name, @branch_name)
:ok
rescue Grit::Git::CommandFailed => e
return :nothing if last_synced_oid.nil?
@@ -328,27 +357,26 @@
##############################################################################
private
def has_remote?
- sh_string('git remote')
+ @rugged.remotes.any?
end
+ # HACK: This will return nil if there are no commits in the remote branch.
+ # It is not the response that I would expect but it mostly gets the job
+ # done. This should probably be reviewed when upgrading to the next version
+ # of Rugged.
+ #
+ # @return [nil] if the remote branch does not exist
+ # @return [Rugged::Remote]
+ def remote_branch
+ Rugged::Branch.lookup(@rugged, "#{@remote_name}/#{@branch_name}", :remote)
+ end
+
def head_walker
walker = Rugged::Walker.new(@rugged)
walker.sorting(Rugged::SORT_DATE)
walker.push(@rugged.head.target)
walker
- end
-
- # sh_string("git config branch.`git branch | grep '^\*' | sed -e 's/\* //'`.remote", "origin")
- def sh_string(cmd, default = nil)
- val = sh("cd #{root} ; #{cmd}").strip rescue nil
- val.nil? || val.empty? ? default : val
- end
-
- # Run in shell, return both status and output
- # @see #sh
- def sh_with_code(cmd)
- ShellTools.sh_with_code(cmd, root)
end
end