sugarjar 2.0.0.beta.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ # This is a sample `repoconfig` for SugarJar
2
+ #
3
+ # Configs should be named `.sugarjar.yaml` and placed in the root
4
+ # of your repository.
5
+ #
6
+
7
+ # `include_from` is a meta config wich will read from an additional
8
+ # configuration file and merge anything from the file onto whatever is in the
9
+ # primary file. This is helpful to have a repo configuration that applies to
10
+ # all/most developers, but allow individual developers to add to over overwrite
11
+ # specific configurations for themselves. If the file does not exist, this
12
+ # configuration is ignored.
13
+
14
+ include_from: .sugarjar_local.yaml
15
+
16
+ # `overwrite_from` is a meta config which works much like `include_from`,
17
+ # except that if the file is found, everything else in this configuration file
18
+ # will be ignored and the configuration will be entirely read from the
19
+ # referenced file. If the file does not exist, this configuration is ignored.
20
+
21
+ overwrite_from: .sugarjar_local_overwrite.yaml
22
+
23
+ # `lint` is a list of scripts to run when `sj lint` is executed (or, if
24
+ # configured, to run on `sj spush`/`sj fpush` - see `on_push` below).
25
+ # Regardless of where `sj` is run from, these scripts will be run from the root
26
+ # of the repo. If a slash is detected in the first 'word' of the command, it
27
+ # is assumed it is a relative path and `sj` will check that the file exists.
28
+
29
+ lint:
30
+ - scripts/run_rubocop.sh
31
+ - scripts/run_mdl.sh
32
+
33
+ # `unit` is a list of scripts to run when `sj unit` is executed (or, if
34
+ # configured to run on `sj spush`/`sj fpush`- see `on_push` below). Regardless
35
+ # of where `sj` is run from, these scripts will be run from the root of the
36
+ # repo. If a slash is detected in the first 'word' of the command, it is
37
+ # assumed it is a relative path and `sj` will check that the file exists.
38
+
39
+ unit:
40
+ - bundle exec rspec
41
+ - scripts/run_tests.sh
42
+
43
+ # `lint_list_cmd` is like `lint`, except it's a command to run which will
44
+ # determine the proper lints to run and return them, one per line. This is
45
+ # useful, for example, when you want to only run lints relevant to the changed
46
+ # files.
47
+
48
+ lint_list_cmd: scripts/determine_linters.sh
49
+
50
+ # `unit_list_cmd` is like `unit`, except it's a command to run which will
51
+ # determine the proper units to run and return them, one per line. This is
52
+ # useful, for example, when you want to only run tests relevant to the changed
53
+ # files.
54
+
55
+ unit_list_cmd: scripts/determine_tests.sh
56
+
57
+ # `on_push` determines what checks should be run when pushing a repo. Valid
58
+ # options are `lint` and/or `unit` (or nothing, of course).
59
+
60
+ on_push: [lint] # or [lint, unit]
61
+
62
+ # `commit_template` points to a file to set the git `commit.template` config
63
+ # to. This is really useful for ensuring that everyone has the same
64
+ # template configured.
65
+
66
+ commit_template: .git_commit_template.txt
67
+
68
+ # `github_user` is the user to use when talking to GitHub. Overrides any such
69
+ # setting in the regular SugarJar config. Most useful when in the
70
+ # `include_from` file.
71
+
72
+ github_user: myuser
73
+
74
+ # `github_host` is the GitHub host to use when talking to GitHub (for hosted
75
+ # GHE). See `github_user`.
76
+
77
+ github_host: github.sample.com
@@ -0,0 +1,17 @@
1
+ require_relative '../util'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def amend(*)
6
+ assert_in_repo!
7
+ # This cannot use shellout since we need a full terminal for the editor
8
+ exit(system(SugarJar::Util.which('git'), 'commit', '--amend', *))
9
+ end
10
+
11
+ def qamend(*)
12
+ assert_in_repo!
13
+ SugarJar::Log.info(git('commit', '--amend', '--no-edit', *).stdout)
14
+ end
15
+ alias amendq qamend
16
+ end
17
+ end
@@ -0,0 +1,233 @@
1
+ class SugarJar
2
+ class Commands
3
+ def lbclean(name = nil)
4
+ assert_in_repo!
5
+ name ||= current_branch
6
+ name = fprefix(name)
7
+
8
+ wt_branches = worktree_branches
9
+
10
+ if wt_branches.include?(name)
11
+ SugarJar::Log.warn("#{name}: #{color('skipped', :yellow)} (worktree)")
12
+ return
13
+ end
14
+
15
+ if clean_branch(name)
16
+ SugarJar::Log.info("#{name}: #{color('reaped', :green)}")
17
+ else
18
+ die(
19
+ "#{color("Cannot clean #{name}", :red)}! there are unmerged " +
20
+ "commits; use 'git branch -D #{name}' to forcefully delete it.",
21
+ )
22
+ end
23
+ end
24
+ alias localbranchclean lbclean
25
+ # backcompat
26
+ alias bclean lbclean
27
+
28
+ def rbclean(name = nil, remote = nil)
29
+ assert_in_repo!
30
+ name ||= current_branch
31
+ name = fprefix(name)
32
+ remote ||= 'origin'
33
+
34
+ ref = "refs/remotes/#{remote}/#{name}"
35
+ if git_nofail('show-ref', '--quiet', ref).error?
36
+ SugarJar::Log.warn("Remote branch #{name} on #{remote} does not exist.")
37
+ return
38
+ end
39
+
40
+ if clean_branch(ref, :remote)
41
+ SugarJar::Log.info("#{ref}: #{color('reaped', :green)}")
42
+ else
43
+ die(
44
+ "#{color("Cannot clean #{ref}", :red)}! there are unmerged " +
45
+ "commits; use 'git push #{remote} -d #{name}' to forcefully delete " +
46
+ ' it.',
47
+ )
48
+ end
49
+ end
50
+ alias remotebranchclean rbclean
51
+
52
+ def gbclean(name = nil, remote = nil)
53
+ assert_in_repo!
54
+ name ||= current_branch
55
+ remote ||= 'origin'
56
+ lbclean(name)
57
+ rbclean(name, remote)
58
+ end
59
+ alias globalbranchclean gbclean
60
+
61
+ def lbcleanall
62
+ assert_in_repo!
63
+ curr = current_branch
64
+ wt_branches = worktree_branches
65
+ all_local_branches.each do |branch|
66
+ if MAIN_BRANCHES.include?(branch)
67
+ SugarJar::Log.debug("Skipping #{branch}")
68
+ next
69
+ end
70
+ if wt_branches.include?(branch)
71
+ SugarJar::Log.info(
72
+ "#{branch}: #{color('skipped', :yellow)} (worktree)",
73
+ )
74
+ next
75
+ end
76
+
77
+ if clean_branch(branch)
78
+ SugarJar::Log.info("#{branch}: #{color('reaped', :green)}")
79
+ else
80
+ SugarJar::Log.info("#{branch}: skipped")
81
+ SugarJar::Log.debug(
82
+ "There are unmerged commits; use 'git branch -D #{branch}' to " +
83
+ 'forcefully delete it)',
84
+ )
85
+ end
86
+ end
87
+
88
+ # Return to the branch we were on, or main
89
+ if all_local_branches.include?(curr)
90
+ git('checkout', curr)
91
+ else
92
+ checkout_main_branch
93
+ end
94
+ end
95
+ alias localbranchcleanall lbcleanall
96
+ # backcomat
97
+ alias bcleanall lbcleanall
98
+
99
+ def rbcleanall(remote = nil)
100
+ assert_in_repo!
101
+ curr = current_branch
102
+ remote ||= 'origin'
103
+ all_remote_branches(remote).each do |branch|
104
+ if (MAIN_BRANCHES + ['HEAD']).include?(branch)
105
+ SugarJar::Log.debug("Skipping #{branch}")
106
+ next
107
+ end
108
+
109
+ ref = "refs/remotes/#{remote}/#{branch}"
110
+ if clean_branch(ref, :remote)
111
+ SugarJar::Log.info("#{ref}: #{color('reaped', :green)}")
112
+ else
113
+ SugarJar::Log.info("#{ref}: skipped")
114
+ SugarJar::Log.debug(
115
+ "There are unmerged commits; use 'git branch -D #{branch}' to " +
116
+ 'forcefully delete it)',
117
+ )
118
+ end
119
+ end
120
+
121
+ # Return to the branch we were on, or main
122
+ if all_local_branches.include?(curr)
123
+ git('checkout', curr)
124
+ else
125
+ checkout_main_branch
126
+ end
127
+ end
128
+ alias remotebranchcleanall rbcleanall
129
+
130
+ def gbcleanall(remote = nil)
131
+ assert_in_repo!
132
+ bcleanall
133
+ rcleanall(remote)
134
+ end
135
+ alias globalbranchcleanall gbcleanall
136
+
137
+ private
138
+
139
+ # rubocop:disable Naming/PredicateMethod
140
+ def clean_branch(name, type = :local)
141
+ undeleteable = MAIN_BRANCHES.dup
142
+ undeleteable << 'HEAD' if type == :remote
143
+ die("Cannot remove #{name} branch") if undeleteable.include?(name)
144
+ SugarJar::Log.debug('Fetch relevant remote...')
145
+ fetch_upstream
146
+ fetch(remote_from_ref(name)) if type == :remote
147
+ return false unless safe_to_clean?(name)
148
+
149
+ SugarJar::Log.debug('branch deemed safe to delete...')
150
+ if type == :remote
151
+ remote = remote_from_ref(name)
152
+ branch = branch_from_ref(name, :remote)
153
+ git('push', remote, '--delete', branch)
154
+ else
155
+ checkout_main_branch
156
+ git('branch', '-D', name)
157
+ rebase
158
+ end
159
+ true
160
+ end
161
+ # rubocop:enable Naming/PredicateMethod
162
+
163
+ def safe_to_clean?(branch)
164
+ # cherry -v will output 1 line per commit on the target branch
165
+ # prefixed by a - or + - anything with a - can be dropped, anything
166
+ # else cannot.
167
+ SugarJar::Log.debug("Checking if branch #{branch} is safe to delete...")
168
+ if branch.start_with?('refs/remotes/')
169
+ remote = remote_from_ref(branch)
170
+ tracked = main_remote_branch(remote)
171
+ else
172
+ tracked = tracked_branch(branch)
173
+ end
174
+ out = git(
175
+ 'cherry', '-v', tracked, branch
176
+ ).stdout.lines.reject do |line|
177
+ line.start_with?('-')
178
+ end
179
+ if out.empty?
180
+ SugarJar::Log.debug(
181
+ "cherry-pick shows branch #{branch} obviously safe to delete",
182
+ )
183
+ return true
184
+ end
185
+
186
+ # if the "easy" check didn't work, it's probably because there
187
+ # was a squash-merge. To check for that we make our own squash
188
+ # merge to upstream/main and see if that has any delta
189
+
190
+ # First we need a temp branch to work on
191
+ tmpbranch = "_sugar_jar.#{Process.pid}"
192
+
193
+ git('checkout', '-b', tmpbranch, tracked)
194
+ s = git_nofail('merge', '--squash', branch)
195
+ if s.error?
196
+ cleanup_tmp_branch(tmpbranch, branch, tracked)
197
+ SugarJar::Log.debug(
198
+ 'Failed to merge changes into current main. This means we could ' +
199
+ 'not figure out if this is merged or not. Check manually and use ' +
200
+ "'git branch -D #{branch}' if it is safe to do so.",
201
+ )
202
+ return false
203
+ end
204
+
205
+ s = git('diff', '--staged')
206
+ out = s.stdout
207
+ SugarJar::Log.debug("Squash-merged diff: #{out}")
208
+ cleanup_tmp_branch(tmpbranch, branch, tracked)
209
+ if out.empty?
210
+ SugarJar::Log.debug(
211
+ 'After squash-merging, this branch appears safe to delete',
212
+ )
213
+ true
214
+ else
215
+ SugarJar::Log.debug(
216
+ 'After squash-merging, this branch is NOT fully merged to main',
217
+ )
218
+ false
219
+ end
220
+ end
221
+
222
+ def cleanup_tmp_branch(tmp, backto, tracked = nil)
223
+ tracked ||= tracked_branch
224
+ # Reset any changes on our temp branch from various merge attempts
225
+ # so we're in a state we know we can 'checkout' away from.
226
+ git('reset', '--hard', tracked)
227
+ # checkout whatever branch we were on before
228
+ git('checkout', backto)
229
+ # delete our temp branch
230
+ git('branch', '-D', tmp)
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,42 @@
1
+ class SugarJar
2
+ class Commands
3
+ def checkout(*args)
4
+ assert_in_repo!
5
+ # Pop the last arguement, which is _probably_ a branch name
6
+ # and then add any featureprefix, and if _that_ is a branch
7
+ # name, replace the last arguement with that
8
+ name = args.last
9
+ bname = fprefix(name)
10
+ if all_local_branches.include?(bname)
11
+ SugarJar::Log.debug("Featurepefixing #{name} -> #{bname}")
12
+ args[-1] = bname
13
+ end
14
+ s = git('checkout', *args)
15
+ SugarJar::Log.info(s.stderr + s.stdout.chomp)
16
+ end
17
+ alias co checkout
18
+
19
+ def br
20
+ assert_in_repo!
21
+ SugarJar::Log.info(git('branch', '-v').stdout.chomp)
22
+ end
23
+
24
+ def binfo
25
+ assert_in_repo!
26
+ SugarJar::Log.info(git(
27
+ 'log', '--graph', '--oneline', '--decorate', '--boundary',
28
+ "#{tracked_branch}.."
29
+ ).stdout.chomp)
30
+ end
31
+
32
+ # binfo for all branches
33
+ def smartlog
34
+ assert_in_repo!
35
+ SugarJar::Log.info(git(
36
+ 'log', '--graph', '--oneline', '--decorate', '--boundary',
37
+ '--branches', "#{most_main}.."
38
+ ).stdout.chomp)
39
+ end
40
+ alias sl smartlog
41
+ end
42
+ end
@@ -0,0 +1,141 @@
1
+ require_relative '../util'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def lint
6
+ assert_in_repo!
7
+
8
+ # does not use dirty_check! as we want a custom message
9
+ if dirty?
10
+ if @ignore_dirty
11
+ SugarJar::Log.warn(
12
+ 'Your repo is dirty, but --ignore-dirty was specified, so ' +
13
+ 'carrying on anyway. If the linter autocorrects, the displayed ' +
14
+ 'diff will be misleading',
15
+ )
16
+ else
17
+ SugarJar::Log.error(
18
+ 'Your repo is dirty, but --ignore-dirty was not specified. ' +
19
+ 'Refusing to run lint. This is to ensure that if the linter ' +
20
+ 'autocorrects, we can show the correct diff.',
21
+ )
22
+ exit(1)
23
+ end
24
+ end
25
+ exit(1) unless run_check('lint')
26
+ end
27
+
28
+ def unit
29
+ assert_in_repo!
30
+ exit(1) unless run_check('unit')
31
+ end
32
+
33
+ def get_checks_from_command(type)
34
+ return nil unless @repo_config["#{type}_list_cmd"]
35
+
36
+ cmd = @repo_config["#{type}_list_cmd"]
37
+ short = cmd.split.first
38
+ unless File.exist?(short)
39
+ SugarJar::Log.error(
40
+ "Configured #{type}_list_cmd #{short} does not exist!",
41
+ )
42
+ return false
43
+ end
44
+ s = Mixlib::ShellOut.new(cmd).run_command
45
+ if s.error?
46
+ SugarJar::Log.error(
47
+ "#{type}_list_cmd (#{cmd}) failed: #{s.format_for_exception}",
48
+ )
49
+ return false
50
+ end
51
+ s.stdout.split("\n")
52
+ end
53
+
54
+ # determine if we're using the _list_cmd and if so run it to get the
55
+ # checks, or just use the directly-defined check, and cache it
56
+ def get_checks(type)
57
+ return @checks[type] if @checks[type]
58
+
59
+ ret = get_checks_from_command(type)
60
+ if ret
61
+ SugarJar::Log.debug("Found #{type}s: #{ret}")
62
+ @checks[type] = ret
63
+ # if it's explicitly false, we failed to run the command
64
+ elsif ret == false
65
+ @checks[type] = false
66
+ # otherwise, we move on (basically: it's nil, there was no _list_cmd)
67
+ else
68
+ SugarJar::Log.debug("[#{type}]: using listed linters: #{ret}")
69
+ @checks[type] = @repo_config[type] || []
70
+ end
71
+ @checks[type]
72
+ end
73
+
74
+ def run_check(type)
75
+ repo_root = SugarJar::Util.repo_root
76
+ Dir.chdir repo_root do
77
+ checks = get_checks(type)
78
+ # if we failed to determine the checks, the the checks have effectively
79
+ # failed
80
+ return false unless checks
81
+
82
+ checks.each do |check|
83
+ SugarJar::Log.debug("Running #{type} #{check}")
84
+
85
+ short = check.split.first
86
+ if short.include?('/')
87
+ short = File.join(repo_root, short) unless short.start_with?('/')
88
+ unless File.exist?(short)
89
+ SugarJar::Log.error("Configured #{type} #{short} does not exist!")
90
+ end
91
+ elsif !SugarJar::Util.which_nofail(short)
92
+ SugarJar::Log.error("Configured #{type} #{short} does not exist!")
93
+ return false
94
+ end
95
+ s = Mixlib::ShellOut.new(check).run_command
96
+
97
+ # Linters auto-correct, lets handle that gracefully
98
+ if type == 'lint' && dirty?
99
+ SugarJar::Log.info(
100
+ "[#{type}] #{short}: #{color('Corrected', :yellow)}",
101
+ )
102
+ SugarJar::Log.warn(
103
+ "The linter modified the repo. Here's the diff:\n",
104
+ )
105
+ puts git('diff').stdout
106
+ loop do
107
+ $stdout.print(
108
+ "\nWould you like to\n\t[q]uit and inspect\n\t[a]mend the " +
109
+ "changes to the current commit and re-run\n > ",
110
+ )
111
+ ans = $stdin.gets.strip
112
+ case ans
113
+ when /^q/
114
+ SugarJar::Log.info('Exiting at user request.')
115
+ exit(1)
116
+ when /^a/
117
+ qamend('-a')
118
+ # break here, if we get out of this loop we 'redo', assuming
119
+ # the user chose this option
120
+ break
121
+ end
122
+ end
123
+ redo
124
+ end
125
+
126
+ if s.error?
127
+ SugarJar::Log.info(
128
+ "[#{type}] #{short} #{color('failed', :red)}, output follows " +
129
+ "(see debug for more)\n#{s.stdout}",
130
+ )
131
+ SugarJar::Log.debug(s.format_for_exception)
132
+ return false
133
+ end
134
+ SugarJar::Log.info(
135
+ "[#{type}] #{short}: #{color('OK', :green)}",
136
+ )
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def debuginfo(*args)
6
+ puts "sugarjar version #{SugarJar::VERSION}"
7
+ puts ghcli('version').stdout
8
+ puts git('version').stdout
9
+
10
+ puts "Config: #{JSON.pretty_generate(args[0])}"
11
+ return unless @repo_config
12
+
13
+ puts "Repo config: #{JSON.pretty_generate(@repo_config)}"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ class SugarJar
2
+ class Commands
3
+ def feature(name, base = nil)
4
+ assert_in_repo!
5
+ SugarJar::Log.debug("Feature: #{name}, #{base}")
6
+ name = fprefix(name)
7
+ die("#{name} already exists!") if all_local_branches.include?(name)
8
+ if base
9
+ fbase = fprefix(base)
10
+ base = fbase if all_local_branches.include?(fbase)
11
+ else
12
+ base ||= most_main
13
+ end
14
+ # If our base is a local branch, don't try to parse it for a remote name
15
+ unless all_local_branches.include?(base)
16
+ base_pieces = base.split('/')
17
+ git('fetch', base_pieces[0]) if base_pieces.length > 1
18
+ end
19
+ git('checkout', '-b', name, base)
20
+ git('branch', '-u', base)
21
+ SugarJar::Log.info(
22
+ "Created feature branch #{color(name, :green)} based on " +
23
+ color(base, :green),
24
+ )
25
+ end
26
+ alias f feature
27
+
28
+ def subfeature(name)
29
+ assert_in_repo!
30
+ SugarJar::Log.debug("Subfature: #{name}")
31
+ feature(name, current_branch)
32
+ end
33
+ alias sf subfeature
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../util'
2
+
3
+ class SugarJar
4
+ class Commands
5
+ def pullsuggestions
6
+ assert_in_repo!
7
+ dirty_check!
8
+
9
+ src = "origin/#{current_branch}"
10
+ fetch('origin')
11
+
12
+ diff = git('diff', "..#{src}").stdout
13
+ return unless diff && !diff.empty?
14
+
15
+ puts "Will merge the following suggestions:\n\n#{diff}"
16
+
17
+ loop do
18
+ $stdout.print("\nAre you sure? [y/n] ")
19
+ ans = $stdin.gets.strip
20
+ case ans
21
+ when /^[Yy]$/
22
+ git = SugarJar::Util.which('git')
23
+ system(git, 'merge', '--ff', "origin/#{current_branch}")
24
+ break
25
+ when /^[Nn]$/, /^[Qq](uit)?/
26
+ puts 'Not merging at user request...'
27
+ break
28
+ else
29
+ puts "Didn't understand '#{ans}'."
30
+ end
31
+ end
32
+ end
33
+ alias ps pullsuggestions
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ class SugarJar
2
+ class Commands
3
+ def smartpush(remote = nil, branch = nil)
4
+ assert_in_repo!
5
+ _smartpush(remote, branch, false)
6
+ end
7
+ alias spush smartpush
8
+
9
+ def forcepush(remote = nil, branch = nil)
10
+ assert_in_repo!
11
+ _smartpush(remote, branch, true)
12
+ end
13
+ alias fpush forcepush
14
+
15
+ private
16
+
17
+ def _smartpush(remote, branch, force)
18
+ unless remote && branch
19
+ remote ||= 'origin'
20
+ branch ||= current_branch
21
+ end
22
+
23
+ dirty_check!
24
+
25
+ unless run_prepush
26
+ if @ignore_prerun_failure
27
+ SugarJar::Log.warn(
28
+ 'Pre-push checks failed, but --ignore-prerun-failure was ' +
29
+ 'specified, so carrying on anyway',
30
+ )
31
+ else
32
+ SugarJar::Log.error('Pre-push checks failed. Not pushing.')
33
+ exit(1)
34
+ end
35
+ end
36
+
37
+ args = ['push', remote, branch]
38
+ args << '--force-with-lease' if force
39
+ puts git(*args).stderr
40
+ end
41
+
42
+ # rubocop:disable Naming/PredicateMethod
43
+ def run_prepush
44
+ @repo_config['on_push']&.each do |item|
45
+ SugarJar::Log.debug("Running on_push check type #{item}")
46
+ unless run_check(item)
47
+ SugarJar::Log.info("[prepush]: #{item} #{color('failed', :red)}.")
48
+ return false
49
+ end
50
+ end
51
+ true
52
+ end
53
+ # rubocop:enable Naming/PredicateMethod
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ class SugarJar
2
+ class Commands
3
+ def smartclone(repo, dir = nil, *)
4
+ reponame = File.basename(repo, '.git')
5
+ dir ||= reponame
6
+ org = extract_org(repo)
7
+
8
+ SugarJar::Log.info("Cloning #{reponame}...")
9
+
10
+ # GH's 'fork' command (with the --clone arg) will fork, if necessary,
11
+ # then clone, and then setup the remotes with the appropriate names. So
12
+ # we just let it do all the work for us and return.
13
+ #
14
+ # Unless the repo is in our own org and cannot be forked, then it
15
+ # will fail.
16
+ if org == @ghuser
17
+ git('clone', canonicalize_repo(repo), dir, *)
18
+ else
19
+ ghcli('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *)
20
+ # make the main branch track upstream
21
+ Dir.chdir dir do
22
+ git('branch', '-u', "upstream/#{main_branch}")
23
+ end
24
+ end
25
+
26
+ SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
27
+ end
28
+ alias sclone smartclone
29
+ end
30
+ end