require 'build-tool/vcs/base' require 'build-tool/errors' require 'grit' module BuildTool; module VCS class GitError < BuildTool::Error; end class GitRemote # The name for the remote attr_accessor :name # The hostname for the remote attr_accessor :server # The path for the remote attr_accessor :path # The hostname to push to for the remote attr_accessor :push_server # The path to push to for the remote attr_accessor :push_path attr_reader :user def initialize( name ) self.name = name @user = nil end def url if !server raise ConfigurationError, "No server specified for remote #{name}" end if !server.host raise ConfigurationError, "No host specified for server #{server.name}" end url = server.host if server.path url = "#{url}/#{server.path}" end if user url = "#{user}@#{url}" end if server.protocol url = "#{server.protocol}://#{url}" end if path url = "#{url}/#{path}" end url end # Returns the pushUrl configured or nil if not configured. def push_url if !push_server return nil end if !push_server.host raise ConfigurationError, "No host specified for server #{push_server.name}" end url = push_server.host if push_server.path url = "#{url}/#{push_server.path}" end if user url = "#{user}@#{url}" end if push_server.protocol url = "#{push_server.protocol}://#{url}" end if path url = "#{url}/#{path}" end url end end class GitConfiguration < BaseConfiguration def name "git" end attr_accessor :remote def initialize super @remote = {} @track = "origin/master" end def vcs( mod ) raise StandardError if @module and ! mod.equal?( @module ) @module = mod Git.new( self ) end # The branch to track attr_accessor :track def track_remote rc = track.split('/') # If there is only one string we assume it is the branch name from # origin return "origin" if rc.length == 1 return rc[0] end def track_branch rc = track.split('/') # If there is only one string we assume it is the branch name from # origin return rc[0] if rc.length == 1 return rc[1..-1].join( '/' ) end def copy_configuration( other ) super @remote = {} # Do not copy the remotes end end # # Implementation for the git version control system. # class Git < Base def initialize( config ) super( config ) @remote = {} @vcs = nil end # ### ATTRIBUTES # def name "git" end def fetching_supported? true end def repo return @repo if @repo @repo = Grit::Repo.new( local_path ) @repo end # ### METHODS # def checkedout? return false if !local_path_exist? if !Pathname.new( local_path ).join( ".git" ).exist? raise Base::VcsError, "Checkout path #{local_path} is not a git repo!" end return true end # Initialize the local repository def clone # Check if path exists if local_path_exist? raise GitError, "Failed to create repository at '#{local_path}': Path exists" end # Create the directory FileUtils.mkdir_p( local_path ) if !$noop # Initialize the repository if git( "init", local_path ) != 0 raise GitError, "Error while initializing the repo `git init #{local_path}'`: #{$?}" end config.remote.each do |name, val| if git( "remote add #{name} #{val.url}" ) != 0 raise GitError, "Error while initializing the repo `git remote add #{name} #{val.url}`: #{$?}" end if val.push_url and git( "remote set-url --push #{name} #{val.push_url}" ) != 0 raise GitError, "Error while initializing the repo `git remote add #{name} #{val.url}`: #{$?}" end end logger.info <<-EOS The following command sometimes fails when issued from this script. Reason unknown. The best chance you have is issuing the command manually! #{local_path}: git fetch #{config.track_remote} #{local_path}: git checkout -b #{config.track_branch} #{config.track_remote}/#{config.track_branch} EOS fetch() cmd = "checkout -b #{config.track_branch} #{config.track_remote}/#{config.track_branch}" if git( cmd, local_path ) != 0 raise GitError, "Error while initializing the repo `#{cmd}`: #{$?}" end end def gc git( "gc" ) end # Fetch from +repository+ # # Initializes the local clone if it does not exist. def fetch() if !checkedout? and !$noop clone end cmd = "fetch -q --prune #{config.track_remote}" if ( rc = git( cmd ) ) != 0 raise GitError, "Error while fetching: #{rc}" end end def git( command, wd = local_path, &block ) self.class.execute "git #{command}", wd, &block end def prepare_for_access # If our server has an associated ssh-key, add it to the ssh-agent. return check_for_sshkey( config.remote[ config.track_remote ].server.sshkey ) end # Check if the module is ready for vcs action def ready_for_access if checkedout? # Check that the remotes are the same as configured. gitconfig = repo.config config.remote.each do |name, val| if val.url and val.url != gitconfig["remote.#{name}.url"] logger.info "#{config.module.name}: Setting remote.origin.url to #{val.url}" gitconfig["remote.#{name}.url"] = val.url end if val.push_server and val.push_url != gitconfig["remote.#{name}.pushurl"] logger.info "#{config.module.name}: Setting remote.origin.pushurl to #{val.push_url}" gitconfig["remote.#{name}.pushurl"] = val.push_url end end # Check if the index is dirty. if git( "diff --exit-code" ) != 0 logger.info( "#{config.module.name}: A dirty index will prevent the rebase." ) git( "status -s -uno" ) do |line| logger.info line.chomp end return false end if git( "diff --cached --exit-code" ) != 0 logger.info( "#{config.module.name}: A dirty index will prevent the rebase." ) git( "status -s -uno" ) do |line| logger.info line.chomp end return false end end true end def rebase if 0 != ( git "rebase #{config.track_remote}/#{config.track_branch}" ) raise GitSvnError, "Error while rebasing the repo with `#{config.track_remote}/#{config.track_branch}': #{$?}" end end end # class Git end; end # module BuildTool::VCS