Module: GitRemoteBranch

Includes:
CaptureFu
Defined in:
lib/version.rb,
lib/param_reader.rb,
lib/git_remote_branch.rb

Defined Under Namespace

Modules: VERSION Classes: InvalidBranchError, NotOnGitRepositoryError

Constant Summary collapse

NAME =
'git_remote_branch'.freeze
COMPLETE_NAME =
"#{NAME} #{VERSION::STRING}".freeze
COMMAND_NAME =
'grb'.freeze
SHORT_NAME =
COMMAND_NAME
COMMANDS =
{
  :create     => {
    :description => 'create a new remote branch and track it locally',
    :aliases  => %w{create new},
    :commands => [
      '"git push #{origin} #{current_branch}:refs/heads/#{branch_name}"',
      '"git fetch #{origin}"',
      '"git branch --track #{branch_name} #{origin}/#{branch_name}"',
      '"git checkout #{branch_name}"'
    ]
  },

  :publish     => {
    :description => 'publish an exiting local branch',
    :aliases  => %w{publish remotize},
    :commands => [
      '"git push #{origin} #{branch_name}:refs/heads/#{branch_name}"',
      '"git fetch #{origin}"',
      '"git config branch.#{branch_name}.remote #{origin}"',
      '"git config branch.#{branch_name}.merge refs/heads/#{branch_name}"',
      '"git checkout #{branch_name}"'
    ]
  },

  :rename     => {
    :description => 'rename a remote branch and its local tracking branch',
    :aliases  => %w{rename rn mv move},
    :commands => [
      '"git push #{origin} #{current_branch}:refs/heads/#{branch_name}"',
      '"git fetch #{origin}"',
      '"git branch --track #{branch_name} #{origin}/#{branch_name}"',
      '"git checkout #{branch_name}"',
      '"git push #{origin} :refs/heads/#{current_branch}"',
      '"git branch -d #{current_branch}"',
    ]
  },

  :delete     => {
    :description => 'delete a local and a remote branch',
    :aliases  => %w{delete destroy kill remove rm},
    :commands => [
      '"git push #{origin} :refs/heads/#{branch_name}"',
      '"git checkout master" if current_branch == branch_name',
      '"git branch -d #{branch_name}"'
    ]
  },

  :track      => {
    :description => 'track an existing remote branch',
    :aliases  => %w{track follow grab fetch},
    :commands => [
      '"git fetch #{origin}"',
      '"git checkout master" if current_branch == branch_name',
      '"git branch --track #{branch_name} #{origin}/#{branch_name}"'
    ]
  }
}
ALIAS_REVERSE_MAP =
get_reverse_map(COMMANDS)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.get_reverse_map(commands) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/git_remote_branch.rb', line 75

def self.get_reverse_map(commands)
  h={}
  commands.each_pair do |cmd, params|
    params[:aliases].each do |alias_|
      unless h[alias_]
        h[alias_] = cmd
      else
        raise "Duplicate aliases: #{alias_.inspect} already defined for command #{h[alias_].inspect}"
      end
    end
  end
  h
end

Instance Method Details

#execute_action(action, branch_name, origin, current_branch) ⇒ Object



118
119
120
121
# File 'lib/git_remote_branch.rb', line 118

def execute_action(action, branch_name, origin, current_branch)
  cmds = COMMANDS[action][:commands].map{ |c| eval(c) }.compact
  execute_cmds(cmds)
end

#execute_cmds(*cmds) ⇒ Object



131
132
133
134
135
136
137
138
# File 'lib/git_remote_branch.rb', line 131

def execute_cmds(*cmds)
  silencer = $SILENT ? ' 2>&1' : ''
  cmds.flatten.each do |c|
    puts_cmd c
    `#{c}#{silencer}`
    whisper ''
  end
end

#explain_action(action, branch_name, origin, current_branch) ⇒ Object



123
124
125
126
127
128
129
# File 'lib/git_remote_branch.rb', line 123

def explain_action(action, branch_name, origin, current_branch)
  cmds = COMMANDS[action][:commands].map{ |c| eval(c) }.compact

  whisper "List of operations to do to #{COMMANDS[action][:description]}:", ''
  puts_cmd cmds
  whisper ''
end

#explain_mode!(argv) ⇒ Object



40
41
42
43
44
45
46
47
# File 'lib/param_reader.rb', line 40

def explain_mode!(argv)
  if argv[0].to_s.downcase == 'explain'
    argv.shift
    true
  else
    false
  end
end

#get_action(action) ⇒ Object



53
54
55
# File 'lib/param_reader.rb', line 53

def get_action(action)
  ALIAS_REVERSE_MAP[action.to_s.downcase]
end

#get_branch(branch) ⇒ Object



57
58
59
# File 'lib/param_reader.rb', line 57

def get_branch(branch)
  branch
end

#get_current_branchObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/param_reader.rb', line 69

def get_current_branch
  #This is sensitive to checkouts of branches specified with wrong case
  
  listing = capture_process_output("#{BRANCH_LISTING_COMMAND}")[1]
  raise(NotOnGitRepositoryError, listing.chomp) if listing =~ /Not a git repository/i
  
  current_branch = listing.scan(/^\*\s+(.+)/).flatten.first
  
  if current_branch =~ /\(no branch\)/
    raise InvalidBranchError, ["Couldn't identify the current local branch. The branch listing was:",
      BRANCH_LISTING_COMMAND.red, 
      listing].join("\n")
  end
  current_branch.strip
end

#get_origin(origin) ⇒ Object



61
62
63
# File 'lib/param_reader.rb', line 61

def get_origin(origin)
  return origin || 'origin'
end

#get_usageObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/git_remote_branch.rb', line 94

def get_usage
  return <<-HELP
Usage:

#{[:create, :publish, :rename, :delete, :track].map{|action|
    "  grb #{action} branch_name [origin_server] \n\n"
  }  
}

Notes:
- If origin_server is not specified, the name 'origin' is assumed (git's default)
- The rename functionality renames the current branch

The explain meta-command: you can also prepend any command with the keyword 'explain'. Instead of executing the command, git_remote_branch will simply output the list of commands you need to run to accomplish that goal.
Example: 
  grb explain create
  grb explain create my_branch github

All commands also have aliases:
#{ COMMANDS.keys.map{|k| k.to_s}.sort.map {|cmd| 
  "#{cmd}: #{COMMANDS[cmd.to_sym][:aliases].join(', ')}" }.join("\n  ") }
HELP
end

#get_welcomeObject



90
91
92
# File 'lib/git_remote_branch.rb', line 90

def get_welcome
  "git_remote_branch version #{VERSION::STRING}\n\n"
end

#puts_cmd(*cmds) ⇒ Object



140
141
142
143
144
# File 'lib/git_remote_branch.rb', line 140

def puts_cmd(*cmds)
  cmds.flatten.each do |c|
    whisper "#{c}".red
  end
end

#read_params(argv) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/param_reader.rb', line 8

def read_params(argv)
  #TODO Some validation on the params
  
  p={}
  p[:silent]  = silent!(argv)
  p[:explain] = explain_mode!(argv)
  
  p[:action]  = get_action(argv[0]) or return HELP_PARAMS

  return HELP_PARAMS if p[:action] == :help

  p[:branch]  = get_branch(argv[1])
  p[:origin]  = get_origin(argv[2])
  
  # If in explain mode, the user doesn't have to specify a branch or be on in
  # actual repo to get the explanation. 
  # Of course if he is, the explanation will be made better by using contextual info.
  if p[:explain]
    p[:branch] ||= "branch_to_#{p[:action]}"
    p[:current_branch] = begin
      get_current_branch
    rescue NotOnGitRepositoryError, InvalidBranchError
      'current_branch'
    end

  else
    return HELP_PARAMS unless p[:branch]
    p[:current_branch] = get_current_branch
  end
  return p
end

#silent!(argv) ⇒ Object



49
50
51
# File 'lib/param_reader.rb', line 49

def silent!(argv)
  !!argv.delete('--silent')
end

#whisper(*msgs) ⇒ Object



146
147
148
149
150
# File 'lib/git_remote_branch.rb', line 146

def whisper(*msgs)
  unless $SILENT
    msgs.flatten ?  msgs.flatten.each{|m| puts m} : puts
  end
end