sugarjar 3.0.0 → 3.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0359bbf05d9c9930fb303ddcba57fd651d00de7ce046a5640c77f596fa148531'
4
- data.tar.gz: 6a505ebcdc042fb4e3b6bd6c4e55e85401787f748a1f8874b4fd18e7489600e7
3
+ metadata.gz: a412f3091d9ce81db841098d46c0b74f5ccf210e7f62916db5f36f68d4f9218f
4
+ data.tar.gz: db6f60757390c2e8decf11cada9a423f4d61284304e9db43ef4785636f621fd3
5
5
  SHA512:
6
- metadata.gz: 88050a2cc342cac84d22ed6395f5fa36df8ff98361283408ae227611c4814b23881ae4a59eef276feb160bea04c9b84e81d6cd76d7eae0c8d8caac0e199dcdad
7
- data.tar.gz: 932d4c9918a3eb50c078e6e3a7dd4728ea648cca222daa460954ec51ea7109fc374ea8a8517e9cf1b71d900ac5bb44c4a6120f0a7aaa581fee87d154a6be08f7
6
+ metadata.gz: 550d6c2792dd3b800f8fcbb3ab0d34019ba59ae09596988fc31c92452270d220c6e0a8feead9f767750f2fb529f2e0c60421a8e5756bb5c80974a7329142404b
7
+ data.tar.gz: b723f16e4a2640777ebc8496afa635ff146f0a5de8e0359213aa0824e43b236a312fd199c5ddea047ff5acba604848e48ea94d9716a80ff4d280210b6dc32c83
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # SugarJar Changelog
2
2
 
3
+ ## 3.0.1 (2026-06-19)
4
+
5
+ * Update tests for more resiliency in various package-building environments
6
+ * Better fallbacks for forge type
7
+ * Add config option to disable fork-based flows
8
+ * Fix false-positive for "primary branch doesn't match" error
9
+ * gitlab: Handle 3-level repos better
10
+ * gitlab: Fix `smartpullrequest` issues
11
+ * gitlab: significant improvement to `smartclone`
12
+ * Add `--fork-name` option to enable forking to a different repo name
13
+
3
14
  ## 3.0.0 (2026-06-08)
4
15
 
5
16
  * Add support for GitLab
data/README.md CHANGED
@@ -115,7 +115,8 @@ https://github.com/jaymzh/sugarjar/blob/main/images/sclone.png
115
115
 
116
116
  This will:
117
117
 
118
- * Fork the repo to your personal org (if you don't already have a fork)
118
+ * Fork the repo to your personal org (if you don't already have a fork,
119
+ and if it's not already in your personal org)
119
120
  * Clone your fork
120
121
  * Add the original as an 'upstream' remote
121
122
 
@@ -126,6 +127,9 @@ Like `git clone`, `sj smartclone` will accept an additional argument as the
126
127
  destination directory to clone to. It will also pass any other unknown options
127
128
  to `git clone` under the hood.
128
129
 
130
+ If you don't work with fork-based workflows, you can set `use_forks: false`
131
+ in your config, or pass in `--no-use-forks` to `sj sclone`.
132
+
129
133
  ### Work with stacked branches more easily
130
134
 
131
135
  It's important to break changes into reviewable chunks, but working with
data/bin/sj CHANGED
@@ -103,6 +103,22 @@ parser = OptionParser.new do |opts|
103
103
  options['pr_autofill'] = autofill
104
104
  end
105
105
 
106
+ opts.on(
107
+ '--[no-]use-forks',
108
+ 'Create (add a remote for) forks, when the organization does not match ' +
109
+ 'the user. [default: true]',
110
+ ) do |val|
111
+ options['use_forks'] = val
112
+ end
113
+
114
+ opts.on(
115
+ '--fork-name NAME',
116
+ 'When forking a repo (in `smartclone`), fork the repo to a different ' +
117
+ 'name. See the help for `smartclone` below.',
118
+ ) do |val|
119
+ options['fork_name'] = val
120
+ end
121
+
106
122
  opts.on(
107
123
  '--[no-]pr-autostack',
108
124
  'When creating a PR, if this is a subfeature, should we make it a ' +
@@ -224,15 +240,40 @@ COMMANDS:
224
240
  Walk all remote branches, and try to delete them if it's safe. See
225
241
  "rbclean" for details.
226
242
 
227
- smartclone, sclone
243
+ smartclone, sclone <repo> [<dir>]
228
244
  A smart wrapper to "git clone" that handles forking and managing
229
245
  remotes for you.
230
- It will clone a git repository using hub-style short name
231
- ("$org/$repo"). If the org of the repository is not the same
232
- as your github-user then it will fork the repo for you to
233
- your account (if not already done) and then setup your remotes
246
+
247
+ If the org of the repository is not the same as your forge user
248
+ then it will fork the repo for you to your account (if not
249
+ already done), clone the repo, and then setup your remotes
234
250
  so that "origin" is your fork and "upstream" is the upstream.
235
251
 
252
+ It is assumed that there will be at least one positional
253
+ argument, and it will be the repo in any format (git, ssh,
254
+ forge-style shortname [e.g. e.g. "$org/$repo"]). A second
255
+ positional argument will be interpreted as the directory to
256
+ clone into.
257
+
258
+ If you want to change the name of the repo in your fork of it,
259
+ you may pass in --fork-name to specify another.
260
+
261
+ Note that if you pass in additional options after ' -- ', they
262
+ will be passed to 'gh' in the case of GitHub, or 'git' in the
263
+ case of GitLab.
264
+
265
+ For example to clone foo/bar/docs on gitlab, but have the
266
+ repo named 'bar-docs' when it's cloned to your org, and to
267
+ have the directory be called 'bar-docs':
268
+
269
+ sj sclone foo/bar/docs bar-docs \
270
+ --forge-type glab --fork-name bar-docs
271
+
272
+ Or for GitHub:
273
+
274
+ sj sclone bar/docs bar-docs \
275
+ --forge-type github --fork-name bar-docs
276
+
236
277
  smartlog, sl
237
278
  Inspired by Facebook's "sl" extension to Mercurial, this command
238
279
  will show you a tree of all your local branches relative to your
@@ -1,9 +1,15 @@
1
1
  class SugarJar
2
2
  class Commands
3
- def smartclone(repo, dir = nil, *)
4
- reponame = extract_repo(repo)
5
- dir ||= reponame
3
+ def smartclone(repo, *args)
6
4
  org = extract_org(repo)
5
+ reponame = extract_repo(repo)
6
+ dir = if args.length.positive? && !args.first.start_with?('-')
7
+ args.shift
8
+ else
9
+ reponame
10
+ end
11
+
12
+ forkname = @fork_name || reponame
7
13
 
8
14
  SugarJar::Log.info("Cloning #{reponame}...")
9
15
 
@@ -13,37 +19,44 @@ class SugarJar
13
19
  #
14
20
  # Unless the repo is in our own org and cannot be forked, then it
15
21
  # will fail.
16
- if org == @forge_user
17
- git('clone', canonicalize_repo(repo), dir, *)
18
- else
22
+ if @use_forks && @forge_user != org
19
23
  if @repo_forge == 'gitlab'
20
- _gitlab_clone(org, repo, dir, *)
24
+ _gitlab_clone(org, repo, dir, forkname, *args)
21
25
  else
22
- forge('repo', 'fork', '--clone', canonicalize_repo(repo), dir, *)
26
+ forge(
27
+ 'repo', 'fork', '--clone', canonicalize_repo(repo), dir,
28
+ '--fork-name', forkname, *args
29
+ )
23
30
  end
24
31
 
25
32
  # make the main branch track upstream
26
33
  Dir.chdir dir do
27
34
  git('branch', '-u', "upstream/#{main_branch}")
28
35
  end
36
+ else
37
+ git('clone', canonicalize_repo(repo), dir, *args)
29
38
  end
30
39
 
31
40
  SugarJar::Log.info('Remotes "origin" and "upstream" configured.')
32
41
  end
33
42
  alias sclone smartclone
34
43
 
35
- def _gitlab_clone(_org, repo, dir, *)
44
+ def _gitlab_clone(_org, repo, dir, forkname, *)
36
45
  # The gitlab CLI is much less forgiving about already-forked
37
46
  # repos, and it has no option to clone to a differently-named
38
47
  # directory. So we have to special case it.
39
48
 
49
+ extract_repo(repo)
50
+
40
51
  # glab requires a short-name for the fork command...
41
52
  shortname = repo_shortname(repo)
42
53
 
43
54
  # We call fork without --clone since --clone can't clone
44
55
  # to another directory. Also, we must specify =false, or it
45
56
  # will prompt
46
- s = forge_nofail('repo', 'fork', shortname, '--clone=false')
57
+ s = forge_nofail(
58
+ 'repo', 'fork', shortname, '--clone=false', '--name', forkname
59
+ )
47
60
 
48
61
  # It fails with:
49
62
  # 409 {message: [Project namespace name has already been taken,
@@ -70,7 +70,7 @@ class SugarJar
70
70
  # we don't need that since it defaults to the local branch name.
71
71
  #
72
72
  # Then we need --yes for it to not prompt us
73
- args.unshift('--head', "#{push_org}/#{reponame}", '--yes')
73
+ args.unshift('--head', "#{push_org}/#{repo_name}", '--yes')
74
74
  end
75
75
 
76
76
  bin = SugarJar::Util.which(_forge_cmd)
@@ -103,7 +103,9 @@ class SugarJar
103
103
  "\tgit remote set-head #{upstream} -a",
104
104
  )
105
105
  end
106
- return if upstream_branch == 'origin'
106
+
107
+ # If we don't have an upstream and an origin, we're done
108
+ return if upstream == 'origin'
107
109
 
108
110
  origin_branch = main_remote_branch('origin')
109
111
  # NOTE: that on GL, forks don't fork any branches by default, even
@@ -33,6 +33,8 @@ class SugarJar
33
33
  @pr_autofill = options['pr_autofill']
34
34
  @pr_autostack = options['pr_autostack']
35
35
  @feature_prefix = options['feature_prefix']
36
+ @use_forks = options['use_forks']
37
+ @fork_name = options['fork_name']
36
38
  @checks = {}
37
39
  @main_branch = nil
38
40
  @main_remote_branches = {}
@@ -88,27 +90,20 @@ class SugarJar
88
90
 
89
91
  # otherwise, if we're in a repo, use the hostname of the remote
90
92
  if SugarJar::Util.in_repo?
91
- extract_host(remote_url_map['origin'])
92
- else
93
- @repo_forge == 'gitlab' ? 'gitlab.com' : 'github.com'
93
+ url = remote_url_map.values.first
94
+ return extract_host(url)
94
95
  end
96
+
97
+ @repo_forge == 'gitlab' ? 'gitlab.com' : 'github.com'
95
98
  end
96
99
 
97
100
  def repo_shortname(repo)
98
101
  # if it's already a shortname, return
99
102
  return repo unless repo.start_with?('http', 'git@')
100
103
 
101
- # otherwise, parse it
102
- if repo.start_with?('http')
103
- bits = repo.split('/')
104
- elsif repo.start_with?('git@')
105
- relevant = repo.split(':').last
106
- bits = relevant.split('/')
107
- end
108
- repo = bits[-1].gsub('.git', '')
109
- org = bits[-2]
110
-
111
- "#{org}/#{repo}"
104
+ repo_name = extract_repo(repo)
105
+ org_name = extract_org(repo)
106
+ [org_name, repo_name].join('/')
112
107
  end
113
108
 
114
109
  def set_commit_template
@@ -176,11 +171,7 @@ class SugarJar
176
171
  end
177
172
 
178
173
  def determine_main_branch(branches)
179
- if branches.include?('main')
180
- 'main'
181
- elsif branches.include?('master')
182
- 'master'
183
- end
174
+ branches.include?('master') ? 'master' : 'main'
184
175
  end
185
176
 
186
177
  def main_branch
@@ -353,12 +344,12 @@ class SugarJar
353
344
 
354
345
  def extract_org(repo)
355
346
  if repo.start_with?('http')
356
- File.basename(File.dirname(repo))
347
+ repo.split('://').last.split('/')[1..-2].join('/')
357
348
  elsif repo.start_with?('git@')
358
- repo.split(':')[1].split('/')[0]
349
+ repo.split(':').last.split('/')[0..-2].join('/')
359
350
  else
360
- # assume they passed in a ghcli-friendly name
361
- repo.split('/').first
351
+ # assume they passed in a cli-friendly shortname
352
+ repo.split('/')[0..-2].join('/')
362
353
  end
363
354
  end
364
355
 
@@ -429,15 +420,13 @@ class SugarJar
429
420
  end
430
421
 
431
422
  def _determine_forge_type
432
- return nil unless SugarJar::Util.in_repo?
433
-
434
- if remote_url_map.values.any? do |x|
435
- x.include?('gitlab')
436
- end
437
- 'gitlab'
438
- else
439
- 'github'
423
+ if SugarJar::Util.in_repo?
424
+ gl = remote_url_map.values.any? { |x| x.include?('gitlab') }
425
+ return gl ? 'gitlab' : 'github'
440
426
  end
427
+
428
+ # if all else fails, guess GH
429
+ 'github'
441
430
  end
442
431
 
443
432
  def _forge_cmd
@@ -11,6 +11,7 @@ class SugarJar
11
11
  'pr_autofill' => true,
12
12
  'pr_autostack' => nil,
13
13
  'color' => true,
14
+ 'use_forks' => true,
14
15
  'ignore_deprecated_options' => [],
15
16
  }.freeze
16
17
 
@@ -1,3 +1,3 @@
1
1
  class SugarJar
2
- VERSION = '3.0.0'.freeze
2
+ VERSION = '3.0.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sugarjar
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phil Dibowitz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-08 00:00:00.000000000 Z
11
+ date: 2026-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge