Class: SugarJar::Commands

Inherits:
Object
  • Object
show all
Defined in:
lib/sugarjar/commands.rb,
lib/sugarjar/commands/up.rb,
lib/sugarjar/commands/push.rb,
lib/sugarjar/commands/amend.rb,
lib/sugarjar/commands/bclean.rb,
lib/sugarjar/commands/branch.rb,
lib/sugarjar/commands/checks.rb,
lib/sugarjar/commands/feature.rb,
lib/sugarjar/commands/debuginfo.rb,
lib/sugarjar/commands/smartclone.rb,
lib/sugarjar/commands/pullsuggestions.rb,
lib/sugarjar/commands/smartpullrequest.rb

Overview

This is the workhorse of SugarJar. Short of #initialize, all other public methods are “commands”. Anything in private is internal implementation details.

Constant Summary collapse

MAIN_BRANCHES =
%w{master main}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Commands

Returns a new instance of Commands.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/sugarjar/commands.rb', line 26

def initialize(options)
  SugarJar::Log.debug("Commands.initialize options: #{options}")
  @ignore_dirty = options['ignore_dirty']
  @ignore_prerun_failure = options['ignore_prerun_failure']
  @repo_config = SugarJar::RepoConfig.config
  SugarJar::Log.debug("Repoconfig: #{@repo_config}")
  @color = options['color']
  @pr_autofill = options['pr_autofill']
  @pr_autostack = options['pr_autostack']
  @feature_prefix = options['feature_prefix']
  @checks = {}
  @main_branch = nil
  @main_remote_branches = {}
  # This is CONFIGURED host, which may be null, as opposed
  # to the method forge_host which will always return something
  @_forge_host = @repo_config['forge_host'] || options['forge_host']
  @repo_forge = @repo_config['forge_type'] || options['forge_type'] ||
                _determine_forge_type

  unless @repo_forge.nil?
    cmd = _forge_cmd
    unless SugarJar::Util.which_nofail(cmd)
      die("No '#{cmd}' found, please install it'")
    end
  end

  user_option = "#{@repo_forge}_user"
  @forge_user = @repo_config[user_option] || options[user_option]

  # Tell the cli where to talk to, if not default
  if @_forge_host
    ENV['GH_HOST'] = @_forge_host
    ENV['GL_HOST'] = @_forge_host
  end

  return if options['no_change']

  set_commit_template if @repo_config['commit_template']
end

Instance Method Details

#_gitlab_clone(_org, repo, dir) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sugarjar/commands/smartclone.rb', line 35

def _gitlab_clone(_org, repo, dir, *)
  # The gitlab CLI is much less forgiving about already-forked
  # repos, and it has no option to clone to a differently-named
  # directory. So we have to special case it.

  # glab requires a short-name for the fork command...
  shortname = repo_shortname(repo)

  # We call fork without --clone since --clone can't clone
  # to another directory. Also, we must specify =false, or it
  # will prompt
  s = forge_nofail('repo', 'fork', shortname, '--clone=false')

  # It fails with:
  #    409 {message: [Project namespace name has already been taken,
  #         Name has already been taken, Path has already been taken]}
  #
  # when there's already a fork... or if you happen to have a name
  # collision. There's no way to tell, so we assume it means we've
  # already forked.
  if s.error?
    if s.stderr.include?(' 409 ')
      SugarJar::Log.debug('Forking failed, probably already forked')
    else
      s.error!
    end
  end

  # Now we clone ourselves...
  git('clone', canonicalize_repo(repo), dir, *)
  Dir.chdir dir do
    # and then configure remotes properly
    git('remote', 'rename', 'origin', 'upstream')

    fork_url = forked_repo(repo, @forge_user)
    git('remote', 'add', 'origin', fork_url)
  end
end

#amendObject



5
6
7
8
9
# File 'lib/sugarjar/commands/amend.rb', line 5

def amend(*)
  assert_in_repo!
  # This cannot use shellout since we need a full terminal for the editor
  exit(system(SugarJar::Util.which('git'), 'commit', '--amend', *))
end

#binfoObject



24
25
26
27
28
29
30
# File 'lib/sugarjar/commands/branch.rb', line 24

def binfo
  assert_in_repo!
  SugarJar::Log.info(git(
    'log', '--graph', '--oneline', '--decorate', '--boundary',
    "#{tracked_branch}.."
  ).stdout.chomp)
end

#brObject



19
20
21
22
# File 'lib/sugarjar/commands/branch.rb', line 19

def br
  assert_in_repo!
  SugarJar::Log.info(git('branch', '-v').stdout.chomp)
end

#checkout(*args) ⇒ Object Also known as: co



3
4
5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/sugarjar/commands/branch.rb', line 3

def checkout(*args)
  assert_in_repo!
  # Pop the last arguement, which is _probably_ a branch name
  # and then add any featureprefix, and if _that_ is a branch
  # name, replace the last arguement with that
  name = args.last
  bname = fprefix(name)
  if all_local_branches.include?(bname)
    SugarJar::Log.debug("Featurepefixing #{name} -> #{bname}")
    args[-1] = bname
  end
  s = git('checkout', *args)
  SugarJar::Log.info(s.stderr + s.stdout.chomp)
end

#debuginfo(*args) ⇒ Object



5
6
7
8
9
10
11
12
13
14
# File 'lib/sugarjar/commands/debuginfo.rb', line 5

def debuginfo(*args)
  puts "sugarjar version #{SugarJar::VERSION}"
  puts forge('version').stdout
  puts git('version').stdout

  puts "Config: #{JSON.pretty_generate(args[0])}"
  return unless @repo_config

  puts "Repo config: #{JSON.pretty_generate(@repo_config)}"
end

#feature(name, base = nil) ⇒ Object Also known as: f



3
4
5
6
7
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
39
40
41
42
43
44
45
46
47
# File 'lib/sugarjar/commands/feature.rb', line 3

def feature(name, base = nil)
  assert_in_repo!
  SugarJar::Log.debug("Feature: #{name}, #{base}")
  name = fprefix(name)
  die("#{name} already exists!") if all_local_branches.include?(name)
  rel_branches = release_branches
  if base
    # If the user specified a base branch (sf mything base)
    # we check if <base> is a release branch and if so, we make
    # this track <upstream>/<base>
    if rel_branches.include?(base)
      newbase = "#{upstream}/#{base}"
      SugarJar::Log.info(
        "Base branch #{base} is a release branch, setting it to track " +
        newbase,
      )
      base = newbase
    else
      fbase = fprefix(base)
      base = fbase if all_local_branches.include?(fbase)
    end
  elsif rel_branches.include?(name)
    # If the user did NOT specify a base *and* this new feature is
    # a release branch, check it out tracking the upstream release
    # branch instead of main
    base = "#{upstream}/#{name}"
    SugarJar::Log.info(
      "Feature #{name} is a release branch, setting it to track #{base}",
    )
  else
    # otherwise, fallback to most-main
    base ||= most_main
  end
  # If our base is a local branch, don't try to parse it for a remote name
  unless all_local_branches.include?(base)
    base_pieces = base.split('/')
    git('fetch', base_pieces[0]) if base_pieces.length > 1
  end
  git('checkout', '-b', name, base)
  git('branch', '-u', base)
  SugarJar::Log.info(
    "Created feature branch #{color(name, :green)} based on " +
    color(base, :green),
  )
end

#forcepush(remote = nil, branch = nil) ⇒ Object Also known as: fpush



9
10
11
12
# File 'lib/sugarjar/commands/push.rb', line 9

def forcepush(remote = nil, branch = nil)
  assert_in_repo!
  _smartpush(remote, branch, true)
end

#fsyncObject Also known as: forcesync



56
57
58
# File 'lib/sugarjar/commands/up.rb', line 56

def fsync
  sync(:force => true)
end

#gbclean(name = nil, remote = nil) ⇒ Object Also known as: globalbranchclean



53
54
55
56
57
58
59
# File 'lib/sugarjar/commands/bclean.rb', line 53

def gbclean(name = nil, remote = nil)
  assert_in_repo!
  name ||= current_branch
  remote ||= 'origin'
  lbclean(name)
  rbclean(name, remote)
end

#gbcleanall(remote = nil) ⇒ Object Also known as: globalbranchcleanall



132
133
134
135
136
# File 'lib/sugarjar/commands/bclean.rb', line 132

def gbcleanall(remote = nil)
  assert_in_repo!
  bcleanall
  rcleanall(remote)
end

#get_checks(type) ⇒ Object

determine if we’re using the _list_cmd and if so run it to get the checks, or just use the directly-defined check, and cache it



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sugarjar/commands/checks.rb', line 56

def get_checks(type)
  return @checks[type] if @checks[type]

  ret = get_checks_from_command(type)
  if ret
    SugarJar::Log.debug("Found #{type}s: #{ret}")
    @checks[type] = ret
  # if it's explicitly false, we failed to run the command
  elsif ret == false
    @checks[type] = false
  # otherwise, we move on (basically: it's nil, there was no _list_cmd)
  else
    SugarJar::Log.debug("[#{type}]: using listed linters: #{ret}")
    @checks[type] = @repo_config[type] || []
  end
  @checks[type]
end

#get_checks_from_command(type) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/sugarjar/commands/checks.rb', line 33

def get_checks_from_command(type)
  return nil unless @repo_config["#{type}_list_cmd"]

  cmd = @repo_config["#{type}_list_cmd"]
  short = cmd.split.first
  unless File.exist?(short)
    SugarJar::Log.error(
      "Configured #{type}_list_cmd #{short} does not exist!",
    )
    return false
  end
  s = Mixlib::ShellOut.new(cmd).run_command
  if s.error?
    SugarJar::Log.error(
      "#{type}_list_cmd (#{cmd}) failed: #{s.format_for_exception}",
    )
    return false
  end
  s.stdout.split("\n")
end

#lbclean(name = nil) ⇒ Object Also known as: localbranchclean, bclean



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/sugarjar/commands/bclean.rb', line 3

def lbclean(name = nil)
  assert_in_repo!
  name ||= current_branch
  name = fprefix(name)

  should_skip, why = skip_branch_info(name)
  if should_skip
    msg = "#{name}: #{color('skipped', :yellow)}"
    msg << " (#{why})" if why
    SugarJar::Log.warn(msg)
    return
  end

  if clean_branch(name)
    SugarJar::Log.info("#{name}: #{color('reaped', :green)}")
  else
    die(
      "#{color("Cannot clean #{name}", :red)}! there are unmerged " +
      "commits; use 'git branch -D #{name}' to forcefully delete it.",
    )
  end
end

#lbcleanallObject Also known as: localbranchcleanall, bcleanall



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/sugarjar/commands/bclean.rb', line 62

def lbcleanall
  assert_in_repo!
  curr = current_branch
  worktree_branches
  all_local_branches.each do |branch|
    # skip_branch info will check for MAIN_BRANCHES, but we
    # quietly skip them.
    next if MAIN_BRANCHES.include?(branch)

    should_skip, why = skip_branch_info(branch)
    if should_skip
      msg = "#{branch}: #{color('skipped', :yellow)}"
      msg << " (#{why})" if why
      SugarJar::Log.info(msg)
      next
    end

    if clean_branch(branch)
      SugarJar::Log.info("#{branch}: #{color('reaped', :green)}")
    else
      SugarJar::Log.info("#{branch}: skipped")
      SugarJar::Log.debug(
        "There are unmerged commits; use 'git branch -D #{branch}' to " +
        'forcefully delete it)',
      )
    end
  end

  # Return to the branch we were on, or main
  if all_local_branches.include?(curr)
    git('checkout', curr)
  else
    checkout_main_branch
  end
end

#lintObject



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/sugarjar/commands/checks.rb', line 5

def lint
  assert_in_repo!

  # does not use dirty_check! as we want a custom message
  if dirty?
    if @ignore_dirty
      SugarJar::Log.warn(
        'Your repo is dirty, but --ignore-dirty was specified, so ' +
        'carrying on anyway. If the linter autocorrects, the displayed ' +
        'diff will be misleading',
      )
    else
      SugarJar::Log.error(
        'Your repo is dirty, but --ignore-dirty was not specified. ' +
        'Refusing to run lint. This is to ensure that if the linter ' +
        'autocorrects, we can show the correct diff.',
      )
      exit(1)
    end
  end
  exit(1) unless run_check('lint', false)
end

#pullsuggestionsObject Also known as: ps



5
6
7
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
# File 'lib/sugarjar/commands/pullsuggestions.rb', line 5

def pullsuggestions
  assert_in_repo!
  dirty_check!

  src = "origin/#{current_branch}"
  fetch('origin')

  diff = git('diff', "..#{src}").stdout
  return unless diff && !diff.empty?

  puts "Will merge the following suggestions:\n\n#{diff}"

  loop do
    $stdout.print("\nAre you sure? [y/n] ")
    ans = $stdin.gets.strip
    case ans
    when /^[Yy]$/
      git = SugarJar::Util.which('git')
      system(git, 'merge', '--ff', "origin/#{current_branch}")
      break
    when /^[Nn]$/, /^[Qq](uit)?/
      puts 'Not merging at user request...'
      break
    else
      puts "Didn't understand '#{ans}'."
    end
  end
end

#qamendObject Also known as: amendq



11
12
13
14
# File 'lib/sugarjar/commands/amend.rb', line 11

def qamend(*)
  assert_in_repo!
  SugarJar::Log.info(git('commit', '--amend', '--no-edit', *).stdout)
end

#rbclean(name = nil, remote = nil) ⇒ Object Also known as: remotebranchclean



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/sugarjar/commands/bclean.rb', line 29

def rbclean(name = nil, remote = nil)
  assert_in_repo!
  name ||= current_branch
  name = fprefix(name)
  remote ||= 'origin'

  ref = "refs/remotes/#{remote}/#{name}"
  if git_nofail('show-ref', '--quiet', ref).error?
    SugarJar::Log.warn("Remote branch #{name} on #{remote} does not exist.")
    return
  end

  if clean_branch(ref, :remote)
    SugarJar::Log.info("#{ref}: #{color('reaped', :green)}")
  else
    die(
      "#{color("Cannot clean #{ref}", :red)}! there are unmerged " +
      "commits; use 'git push #{remote} -d #{name}' to forcefully delete " +
      ' it.',
    )
  end
end

#rbcleanall(remote = nil) ⇒ Object Also known as: remotebranchcleanall



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/sugarjar/commands/bclean.rb', line 101

def rbcleanall(remote = nil)
  assert_in_repo!
  curr = current_branch
  remote ||= 'origin'
  all_remote_branches(remote).each do |branch|
    if (MAIN_BRANCHES + ['HEAD']).include?(branch)
      SugarJar::Log.debug("Skipping #{branch}")
      next
    end

    ref = "refs/remotes/#{remote}/#{branch}"
    if clean_branch(ref, :remote)
      SugarJar::Log.info("#{ref}: #{color('reaped', :green)}")
    else
      SugarJar::Log.info("#{ref}: skipped")
      SugarJar::Log.debug(
        "There are unmerged commits; use 'git branch -D #{branch}' to " +
        'forcefully delete it)',
      )
    end
  end

  # Return to the branch we were on, or main
  if all_local_branches.include?(curr)
    git('checkout', curr)
  else
    checkout_main_branch
  end
end

#run_check(type, autorun) ⇒ Object

autorun is true when we’re running from push, and false when someone ran ‘lint’ or ‘unit’ directly

In the case of a autorun, if a linter changes the code, we require either the user amend, or bail out. If it’s a manual run, then we allow them to just go on.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/sugarjar/commands/checks.rb', line 80

def run_check(type, autorun)
  repo_root = SugarJar::Util.repo_root
  Dir.chdir repo_root do
    checks = get_checks(type)
    # if we failed to determine the checks, the the checks have effectively
    # failed
    return false unless checks

    checks.each do |check|
      SugarJar::Log.debug("Running #{type} #{check}")
      skip_redo = false

      short = check.split.first
      if short.include?('/')
        short = File.join(repo_root, short) unless short.start_with?('/')
        unless File.exist?(short)
          SugarJar::Log.error("Configured #{type} #{short} does not exist!")
        end
      elsif !SugarJar::Util.which_nofail(short)
        SugarJar::Log.error("Configured #{type} #{short} does not exist!")
        return false
      end
      s = Mixlib::ShellOut.new(check).run_command

      # Linters auto-correct, lets handle that gracefully
      if type == 'lint' && dirty?
        SugarJar::Log.info(
          "[#{type}] #{short}: #{color('Corrected', :yellow)}",
        )
        SugarJar::Log.warn(
          "The linter modified the repo. Here's the diff:\n",
        )
        puts git('diff').stdout
        loop do
          options = [
            '[q]uit and inspect',
            '[a]mend the changes to the current commit and re-run',
          ]
          options << '[i]gnore the changes and keep going' unless autorun

          msg = "\nWould you like to\n\t" + options.join("\n\t") + "\n  > "

          $stdout.print(msg)
          ans = $stdin.gets.strip
          case ans
          when /^q/
            SugarJar::Log.info('Exiting at user request.')
            exit(1)
          when /^a/
            qamend('-a')
            # break here, if we get out of this loop we 'redo', assuming
            # the user chose this option
            break
          when /^i/
            unless autorun
              skip_redo = true
              break
            end
          end
        end
        redo unless skip_redo
      end

      if s.error?
        SugarJar::Log.info(
          "[#{type}] #{short} #{color('failed', :red)}, output follows " +
          "(see debug for more)\n#{s.stdout}",
        )
        SugarJar::Log.debug(s.format_for_exception)
        return false
      end
      SugarJar::Log.info(
        "[#{type}] #{short}: #{color('OK', :green)}",
      )
    end
  end
end

#smartclone(repo, dir = nil) ⇒ Object Also known as: sclone



3
4
5
6
7
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
# File 'lib/sugarjar/commands/smartclone.rb', line 3

def smartclone(repo, dir = nil, *)
  reponame = extract_repo(repo)
  dir ||= reponame
  org = extract_org(repo)

  SugarJar::Log.info("Cloning #{reponame}...")

  # GH's 'fork' command (with the --clone arg) will fork, if necessary,
  # then clone, and then setup the remotes with the appropriate names. So
  # we just let it do all the work for us and return.
  #
  # Unless the repo is in our own org and cannot be forked, then it
  # will fail.
  if org == @forge_user
    git('clone', canonicalize_repo(repo), dir, *)
  else
    if @repo_forge == 'gitlab'
      _gitlab_clone(org, repo, dir, *)
    else
      forge('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *)
    end

    # make the main branch track upstream
    Dir.chdir dir do
      git('branch', '-u', "upstream/#{main_branch}")
    end
  end

  SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
end

#smartlogObject Also known as: sl

binfo for all branches



33
34
35
36
37
38
39
# File 'lib/sugarjar/commands/branch.rb', line 33

def smartlog
  assert_in_repo!
  SugarJar::Log.info(git(
    'log', '--graph', '--oneline', '--decorate', '--boundary',
    '--branches', "#{most_main}.."
  ).stdout.chomp)
end

#smartpullrequest(*args) ⇒ Object Also known as: spr, smartpr



5
6
7
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/sugarjar/commands/smartpullrequest.rb', line 5

def smartpullrequest(*args)
  assert_in_repo!
  assert_common_main_branch!

  # does not use `dirty_check!` because we don't allow overriding here
  if dirty?
    SugarJar::Log.warn(
      'Your repo is dirty, so I am not going to create a pull request. ' +
      'You should commit or amend and push it to your remote first.',
    )
    exit(1)
  end

  user_specified_base = args.include?('-B') || args.include?('--base')

  curr = current_branch
  base = tracked_branch
  if @pr_autofill
    SugarJar::Log.info('Autofilling in PR from commit message')
    num_commits = git(
      'rev-list', '--count', curr, "^#{base}"
    ).stdout.strip.to_i
    if num_commits > 1
      args.unshift('--fill-first')
    else
      args.unshift('--fill')
    end
  end
  unless user_specified_base
    if subfeature?(base)
      if upstream_org != push_org
        SugarJar::Log.warn(
          'Unfortunately you cannot based one PR on another PR when' +
          " using fork-based PRs. We will base this on #{most_main}." +
          ' This just means the PR "Changes" tab will show changes for' +
          ' the full stack until those other PRs are merged and this PR' +
          ' PR is rebased.',
        )
      # nil is prompt, true is always, false is never
      elsif @pr_autostack.nil?
        $stdout.print(
          'It looks like this is a subfeature, would you like to base ' +
          "this PR on #{base}? [y/n] ",
        )
        ans = $stdin.gets.strip
        args.unshift('--base', base) if %w{Y y}.include?(ans)
      elsif @pr_autostack
        args.unshift('--base', base)
      end
    elsif base.include?('/') && base != most_main
      # If our base is a remote branch, then use that as the
      # base branch of the PR
      args.unshift('--base', base.split('/').last)
    end
  end

  # <org>:<branch> is the GH API syntax for:
  #   look for a branch of name <branch>, from a fork in owner <org>
  if @repo_forge == 'github'
    # On GitHub, the head is the org and the *BRANCH* name to use as
    # the head branch...
    args.unshift('--head', "#{push_org}:#{curr}")
  else
    # On GitLab, the head is the repo (org/repo) to use as the head
    # _repo_, and then branch is configured seperately (with -s), but
    # we don't need that since it defaults to the local branch name.
    #
    # Then we need --yes for it to not prompt us
    args.unshift('--head', "#{push_org}/#{reponame}", '--yes')
  end

  bin = SugarJar::Util.which(_forge_cmd)
  subcmd = _pr_cmd
  SugarJar::Log.trace(
    "Running: #{bin} #{subcmd} create #{args.join(' ')}",
  )
  system(bin, subcmd, 'create', *args)
end

#smartpush(remote = nil, branch = nil) ⇒ Object Also known as: spush



3
4
5
6
# File 'lib/sugarjar/commands/push.rb', line 3

def smartpush(remote = nil, branch = nil)
  assert_in_repo!
  _smartpush(remote, branch, false)
end

#subfeature(name) ⇒ Object Also known as: sf

alias for “feature <current_branch>‘



51
52
53
54
55
# File 'lib/sugarjar/commands/feature.rb', line 51

def subfeature(name)
  assert_in_repo!
  SugarJar::Log.debug("Subfature: #{name}")
  feature(name, current_branch)
end

#sync(force: false) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/sugarjar/commands/up.rb', line 61

def sync(force: false)
  assert_in_repo!
  dirty_check!

  src = "origin/#{current_branch}"
  fetch('origin')
  want_reset = false
  if force
    SugarJar::Log.debug('Forcing reset instead of rebase at user request')
    want_reset = true
  end

  unless force
    s = git_nofail('merge-base', '--is-ancestor', 'HEAD', src)
    # if this IS an ancestor, we can just force reset.
    #
    # otherwise, we attempt a rebase to not lose anything (unless
    # force is set)
    if s.error?
      SugarJar::Log.debug(
        "Choosing rebase sync since this isn't a direct ancestor",
      )
    else
      SugarJar::Log.debug('Choosing reset sync since this is an ancestor')
      want_reset = true
    end
  end

  if want_reset
    git('reset', '--hard', src)
  else
    rebase(src)
    s = git_nofail('rev-parse', '--verify', 'REBASE_HEAD')
    unless s.error?
      SugarJar::Log.info(
        'Rebase required input. You may continue the rebase from' +
        ' here normally, or you may abort (`git rebase --abort`)' +
        ' and instead to `sj fsync` to skip a rebase and force' +
        ' reset to the remote branch.',
      )
      return
    end
  end
  SugarJar::Log.info("Synced to #{src}.")
end

#unitObject



28
29
30
31
# File 'lib/sugarjar/commands/checks.rb', line 28

def unit
  assert_in_repo!
  exit(1) unless run_check('unit', false)
end

#up(branch = nil) ⇒ Object



3
4
5
6
7
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
# File 'lib/sugarjar/commands/up.rb', line 3

def up(branch = nil)
  assert_in_repo!
  branch ||= current_branch
  branch = fprefix(branch)
  # get a copy of our current branch, if rebase fails, we won't
  # be able to determine it without backing out
  curr = current_branch
  git('checkout', branch)
  result = rebase
  if result['so'].error?
    backout = ''
    if rebase_in_progress?
      backout = ' You can get out of this with a `git rebase --abort`.'
    end

    die(
      "#{color(curr, :red)}: Failed to rebase on " +
      "#{result['base']}. Leaving the repo as-is.#{backout} " +
      'Output from failed rebase is: ' +
      "\nSTDOUT:\n#{result['so'].stdout.lines.map { |x| "\t#{x}" }.join}" +
      "\nSTDERR:\n#{result['so'].stderr.lines.map { |x| "\t#{x}" }.join}",
    )
  else
    SugarJar::Log.info(
      "#{color(current_branch, :green)} rebased on #{result['base']}",
    )
    # go back to where we were if we rebased a different branch
    git('checkout', curr) if branch != curr
  end
end

#upallObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/sugarjar/commands/up.rb', line 34

def upall
  assert_in_repo!
  all_local_branches.each do |branch|
    next if MAIN_BRANCHES.include?(branch)

    git('checkout', branch)
    result = rebase
    if result['so'].error?
      SugarJar::Log.error(
        "#{color(branch, :red)} failed rebase. Reverting attempt and " +
        'moving to next branch. Try `sj up` manually on that branch.',
      )
      git('rebase', '--abort') if rebase_in_progress?
    else
      SugarJar::Log.info(
        "#{color(branch, :green)} rebased on " +
        color(result['base'], :green).to_s,
      )
    end
  end
end