Top Level Namespace

Defined Under Namespace

Modules: Socialcast Classes: Numeric, String

Constant Summary collapse

HELP =
<<EOS
git-wtf displays the state of your repository in a readable, easy-to-scan
format. It's useful for getting a summary of how a branch relates to a remote
server, and for wrangling many topic branches.

git-wtf can show you:
- How a branch relates to the remote repo, if it's a tracking branch.
- How a branch relates to integration branches, if it's a feature branch.
- How a branch relates to the feature branches, if it's an integration
  branch.

git-wtf is best used before a git push, or between a git fetch and a git
merge. Be sure to set color.ui to auto or yes for maximum viewing pleasure.
EOS
KEY =
<<EOS
KEY:
() branch only exists locally
{} branch only exists on a remote repo
[] branch exists locally and remotely

x merge occurs both locally and remotely
~ merge occurs only locally
  (space) branch isn't merged in

(It's possible for merges to occur remotely and not locally, of course, but
that's a less common case and git-wtf currently doesn't display anything
special for it.)
EOS
USAGE =
<<EOS
Usage: git wtf [branch+] [options]

If [branch] is not specified, git-wtf will use the current branch. The possible
[options] are:

  -l, --long          include author info and date for each commit
  -a, --all           show all branches across all remote repos, not just
                      those from origin
  -A, --all-commits   show all commits, not just the first 5
  -s, --short         don't show commits
  -k, --key           show key
  -r, --relations     show relation to features / integration branches
      --dump-config   print out current configuration and exit

git-wtf uses some heuristics to determine which branches are integration
branches, and which are feature branches. (Specifically, it assumes the
integration branches are named "master", "next" and "edge".) If it guesses
incorrectly, you will have to create a .git-wtfrc file.

To start building a configuration file, run "git-wtf --dump-config >
.git-wtfrc" and edit it. The config file is a YAML file that specifies the
integration branches, any branches to ignore, and the max number of commits to
display when --all-commits isn't used.  git-wtf will look for a .git-wtfrc file
starting in the current directory, and recursively up to the root.

IMPORTANT NOTE: all local branches referenced in .git-wtfrc must be prefixed
with heads/, e.g. "heads/master". Remote branches must be of the form
remotes/<remote>/<branch>.
EOS
<<EOS
git-wtf Copyright 2008--2009 William Morgan <wmorgan at the masanjin dot nets>.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details.

You can find the GNU General Public License at: http://www.gnu.org/licenses/
EOS
CONFIG_FN =
".git-wtfrc"

Instance Method Summary collapse

Instance Method Details

#ahead_behind_string(ahead, behind) ⇒ Object



144
145
146
147
148
# File 'bin/git-wtf', line 144

def ahead_behind_string ahead, behind
  [ahead.empty? ? nil : "#{ahead.size.pluralize 'commit'} ahead",
   behind.empty? ? nil : "#{behind.size.pluralize 'commit'} behind"].
   compact.join("; ")
end

#commits_between(from, to) ⇒ Object

the set of commits in ‘to’ that aren’t in ‘from’. if empty, ‘to’ has been merged into ‘from’.



125
126
127
128
129
130
131
# File 'bin/git-wtf', line 125

def commits_between from, to
  if $long
    `git log --pretty=format:"- %s [#{yellow "%h"}] (#{purple "%ae"}; %ar)" #{from}..#{to}`
  else
    `git log --pretty=format:"- %s [#{yellow "%h"}]" #{from}..#{to}`
  end.split(/[\r\n]+/)
end

#cyan(s) ⇒ Object



119
# File 'bin/git-wtf', line 119

def cyan s; $color ? "\033[36m#{s}\033[0m" : s end

#find_file(fn) ⇒ Object

search up the path for a file



100
101
102
103
104
105
106
107
# File 'bin/git-wtf', line 100

def find_file fn
  while true
    return fn if File.exist? fn
    fn2 = File.join("..", fn)
    return nil if File.expand_path(fn2) == File.expand_path(fn)
    fn = fn2
  end
end

#green(s) ⇒ Object



117
# File 'bin/git-wtf', line 117

def green s; $color ? "\033[32m#{s}\033[0m" : s end

#grey(s) ⇒ Object



120
# File 'bin/git-wtf', line 120

def grey s; $color ? "\033[1;30m#{s}\033[0m" : s end

#kObject

assemble remotes



330
331
332
333
334
335
336
337
338
# File 'bin/git-wtf', line 330

branches.each do |k, b|
  next unless b[:remote] && b[:remote_mergepoint]
  b[:remote_branch] = if b[:remote] == "."
    b[:remote_mergepoint]
  else
    t = "#{b[:remote]}/#{b[:remote_mergepoint]}"
    remote_branches[t] && t # only if it's still alive
  end
end

#purple(s) ⇒ Object



121
# File 'bin/git-wtf', line 121

def purple s; $color ? "\033[35m#{s}\033[0m" : s end

#red(s) ⇒ Object



116
# File 'bin/git-wtf', line 116

def red s; $color ? "\033[31m#{s}\033[0m" : s end

#show(b) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'bin/git-wtf', line 164

def show b
  have_both = b[:local_branch] && b[:remote_branch]

  pushc, pullc, oosync = if have_both
    [x = commits_between(b[:remote_branch], b[:local_branch]),
     y = commits_between(b[:local_branch], b[:remote_branch]),
     !x.empty? && !y.empty?]
  end

  if b[:local_branch]
    puts "Local branch: " + green(b[:local_branch].sub(/^heads\//, ""))

    if have_both
      if pushc.empty?
        puts "#{widget true} in sync with remote"
      else
        action = oosync ? "push after rebase / merge" : "push"
        puts "#{widget false} NOT in sync with remote (you should #{action})"
        show_commits pushc unless $short
      end
    end
  end

  if b[:remote_branch]
    puts "Remote branch: #{cyan b[:remote_branch]} (#{b[:remote_url]})"

    if have_both
      if pullc.empty?
        puts "#{widget true} in sync with local"
      else
        action = pushc.empty? ? "merge" : "rebase / merge"
        puts "#{widget false} NOT in sync with local (you should #{action})"
        show_commits pullc unless $short
      end
    end
  end

  puts "\n#{red "WARNING"}: local and remote branches have diverged. A merge will occur unless you rebase." if oosync
end

#show_commits(commits, prefix = " ") ⇒ Object



133
134
135
136
137
138
139
140
141
142
# File 'bin/git-wtf', line 133

def show_commits commits, prefix="    "
  if commits.empty?
    puts "#{prefix} none"
  else
    max = $all_commits ? commits.size : $config["max_commits"]
    max -= 1 if max == commits.size - 1 # never show "and 1 more"
    commits[0 ... max].each { |c| puts "#{prefix}#{c}" }
    puts grey("#{prefix}... and #{commits.size - max} more (use -A to see all).") if commits.size > max
  end
end

#show_relations(b, all_branches) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'bin/git-wtf', line 204

def show_relations b, all_branches
  ibs, fbs = all_branches.partition { |name, br| $config["integration-branches"].include?(br[:local_branch]) || $config["integration-branches"].include?(br[:remote_branch]) }
  if $config["integration-branches"].include? b[:local_branch]
    puts "\nFeature branches:" unless fbs.empty?
    fbs.each do |name, br|
      next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch])
      next if br[:ignore]
      local_only = br[:remote_branch].nil?
      remote_only = br[:local_branch].nil?
      name = if local_only
        purple br[:name]
      elsif remote_only
        cyan br[:name]
      else
        green br[:name]
      end

      ## for remote_only branches, we'll compute wrt the remote branch head. otherwise, we'll
      ## use the local branch head.
      head = remote_only ? br[:remote_branch] : br[:local_branch]

      remote_ahead = b[:remote_branch] ? commits_between(b[:remote_branch], head) : []
      local_ahead = b[:local_branch] ? commits_between(b[:local_branch], head) : []

      if local_ahead.empty? && remote_ahead.empty?
        puts "#{widget true, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is merged in"
      elsif local_ahead.empty?
        puts "#{widget true, remote_only, local_only, true} #{name} merged in (only locally)"
      else
        behind = commits_between head, (br[:local_branch] || br[:remote_branch])
        ahead = remote_only ? remote_ahead : local_ahead
        puts "#{widget false, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is NOT merged in (#{ahead_behind_string ahead, behind})"
        show_commits ahead unless $short
      end
    end
  else
    puts "\nIntegration branches:" unless ibs.empty? # unlikely
    ibs.sort_by { |v, br| v }.each do |v, br|
      next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch])
      next if br[:ignore]
      local_only = br[:remote_branch].nil?
      remote_only = br[:local_branch].nil?
      name = remote_only ? cyan(br[:name]) : green(br[:name])

      ahead = commits_between v, (b[:local_branch] || b[:remote_branch])
      if ahead.empty?
        puts "#{widget true, local_only} merged into #{name}"
      else
        #behind = commits_between b[:local_branch], v
        puts "#{widget false, local_only} NOT merged into #{name} (#{ahead.size.pluralize 'commit'} ahead)"
        show_commits ahead unless $short
      end
    end
  end
end

#widget(merged_in, remote_only = false, local_only = false, local_only_merge = false) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'bin/git-wtf', line 150

def widget merged_in, remote_only=false, local_only=false, local_only_merge=false
  left, right = case
    when remote_only; %w({ })
    when local_only; %w{( )}
    else %w([ ])
  end
  middle = case
    when merged_in && local_only_merge; green("~")
    when merged_in; green("x")
    else " "
  end
  print left, middle, right
end

#yellow(s) ⇒ Object



118
# File 'bin/git-wtf', line 118

def yellow s; $color ? "\033[33m#{s}\033[0m" : s end