lib/between_meals/repo/hg.rb (161 lines of code) (raw):

# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2 # Copyright 2013-present Facebook # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'pathname' require 'mixlib/shellout' require 'between_meals/changeset' require 'between_meals/repo/hg/cmd' module BetweenMeals class Repo class Hg < BetweenMeals::Repo def setup @bin = 'hg' @cmd = BetweenMeals::Repo::Hg::Cmd.new( :bin => @bin, :cwd => @repo_path, :logger => @logger, ) end def exists? Dir.exist?(Pathname.new(@repo_path).join('.hg')) end def head_rev @cmd.log('node').stdout end def checkout(url) @cmd.clone(url, @repo_path) end # Return files changed between two revisions def changes(start_ref, end_ref) valid_ref?(start_ref) valid_ref?(end_ref) if end_ref stdout = @cmd.status(start_ref, end_ref).stdout begin parse_status(stdout).compact rescue StandardError => e # We've seen some weird non-reproducible failures here @logger.error( 'Something went wrong. Please report this output.', ) @logger.error(e) stdout.lines.each do |line| @logger.error(line.strip) end exit(1) end end def update @cmd.pull.stdout rescue StandardError => e @logger.error('Something went wrong with hg!') @logger.error(e) raise end # Return all files def files @cmd.manifest.stdout.split("\n").map do |x| { :path => x, :status => :created } end end def head_parents [{ :time => Time.parse(@cmd.log('date|isodate', 'master').stdout), :rev => @cmd.log('node', 'master').stdout, }] rescue StandardError [{ :time => nil, :rev => nil, }] end def last_author [ /^.*<(.*)>$/, /^(.*@.*)$/, ].each do |re| m = @cmd.log('author').stdout.match(re) return { :email => m[1] } if m end return { :email => nil } end def last_msg @cmd.log('desc').stdout rescue StandardError nil end def last_msg=(msg) if last_msg.strip != msg.strip @cmd.amend(msg.strip) end end def email username[2] rescue StandardError nil end def name username[1] rescue StandardError nil end def status @cmd.status.stdout end def upstream?(rev) # Check if commit is an ancestor of master # Returns the diff if common ancestor is found, # returns nothing if not if @cmd.rev("'ancestor(master,#{rev}) & #{rev}'").stdout.empty? return false else return true end end def valid_ref?(ref) @cmd.rev(ref) return true rescue StandardError raise Changeset::ReferenceError end private def username @cmd.username.stdout.lines.first.strip.match(/^(.*?)(?:\s<(.*)>)?$/) end def parse_status(changes) # The codes used to show the status of files are: # # M = modified # A = added # R = removed # C = clean # ! = missing (deleted by non-hg command, but still tracked) # ? = not tracked # I = ignored # = origin of the previous file (with --copies) changes.lines.map do |line| parts = line.chomp.split(nil, 2) case parts[0] when 'A' { :status => :added, :path => parts[1], } when 'C' { :status => :clean, :path => parts[1], } when 'R' { :status => :deleted, :path => parts[1], } when 'M' { :status => :modified, :path => parts[1], } when '!' { :status => :missing, :path => parts[1], } when '?' { :status => :untracked, :path => parts[1], } when 'I' { :status => :ignored, :path => parts[1], } else fail 'Failed to parse repo diff line.' end end end end end end