carson 2.10.0 → 2.11.0
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 +4 -4
- data/RELEASE.md +12 -0
- data/VERSION +1 -1
- data/lib/carson/runtime/audit.rb +2 -2
- data/lib/carson/runtime/setup.rb +54 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef5d5bfa64c8a8b7f4d2b4893dd5a3cab05ab2f154bd12ab926837ae396c2fe2
|
|
4
|
+
data.tar.gz: 106a0dc1a54c05e59ba9fe4eb0d01f647c420ac305d92a5d70cbc3c606300006
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c1d53528fa822f8d7e4850234a039459f56053762ed8ed1982e7a27c2b1f7fd7820f6565afc2a8cda596971ce4dfab2e2288aeabf8b92e54e2cc5e9266ab213
|
|
7
|
+
data.tar.gz: 04755a262052a630f8200089d03ff1f5a7c48ab8e68f50097fcdd206a8672da988d51c5f041384752a7d97712854b054aa4fd85b2b7268989190ea248d33bf1f
|
data/RELEASE.md
CHANGED
|
@@ -5,6 +5,18 @@ Release-note scope rule:
|
|
|
5
5
|
- `RELEASE.md` records only version deltas, breaking changes, and migration actions.
|
|
6
6
|
- Operational usage guides live in `MANUAL.md` and `API.md`.
|
|
7
7
|
|
|
8
|
+
## 2.11.0 — Self-Diagnosing Audit and Duplicate-Remote Prevention
|
|
9
|
+
|
|
10
|
+
### What changed
|
|
11
|
+
|
|
12
|
+
- **Audit concise output now names the remote and suggests recovery actions.** "Main sync (origin): ahead by 1 — git fetch origin, or carson setup to switch remote." instead of the opaque "Main sync: ahead by 1 — reset local drift." The remote name is visible; the fix is embedded.
|
|
13
|
+
- **Setup warns when multiple remotes share the same URL.** Interactive mode annotates duplicates with `[duplicate]` and prints a warning. Silent mode logs a `duplicate_remotes:` verbose line. URL normalisation treats SSH and HTTPS variants as equal (`git@github.com:user/repo.git` matches `https://github.com/user/repo`).
|
|
14
|
+
|
|
15
|
+
### What users must do now
|
|
16
|
+
|
|
17
|
+
1. Upgrade Carson to `2.11.0`.
|
|
18
|
+
2. If you have duplicate remotes (e.g. both `origin` and `github` pointing to the same URL), remove the stale one with `git remote remove <name>`.
|
|
19
|
+
|
|
8
20
|
## 2.10.0 — Lower Ruby Requirement to 3.4
|
|
9
21
|
|
|
10
22
|
### What changed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.11.0
|
data/lib/carson/runtime/audit.rb
CHANGED
|
@@ -46,13 +46,13 @@ module Carson
|
|
|
46
46
|
puts_verbose "main_vs_remote_main_behind: #{behind_count}"
|
|
47
47
|
puts_verbose "ACTION: local #{config.main_branch} is ahead of #{config.git_remote}/#{config.main_branch} by #{ahead_count} commit#{plural_suffix( count: ahead_count )}; reset local drift before commit/push workflows."
|
|
48
48
|
audit_state = "block"
|
|
49
|
-
audit_concise_problems << "Main sync: ahead by #{ahead_count} —
|
|
49
|
+
audit_concise_problems << "Main sync (#{config.git_remote}): ahead by #{ahead_count} — git fetch #{config.git_remote}, or carson setup to switch remote."
|
|
50
50
|
elsif behind_count.positive?
|
|
51
51
|
puts_verbose "main_vs_remote_main_ahead: #{ahead_count}"
|
|
52
52
|
puts_verbose "main_vs_remote_main_behind: #{behind_count}"
|
|
53
53
|
puts_verbose "ACTION: local #{config.main_branch} is behind #{config.git_remote}/#{config.main_branch} by #{behind_count} commit#{plural_suffix( count: behind_count )}; run carson sync."
|
|
54
54
|
audit_state = "attention" if audit_state == "ok"
|
|
55
|
-
audit_concise_problems << "Main sync: behind by #{behind_count} — run carson sync."
|
|
55
|
+
audit_concise_problems << "Main sync (#{config.git_remote}): behind by #{behind_count} — run carson sync."
|
|
56
56
|
else
|
|
57
57
|
puts_verbose "main_vs_remote_main_ahead: 0"
|
|
58
58
|
puts_verbose "main_vs_remote_main_behind: 0"
|
data/lib/carson/runtime/setup.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "uri"
|
|
3
|
+
|
|
1
4
|
module Carson
|
|
2
5
|
class Runtime
|
|
3
6
|
module Setup
|
|
@@ -51,6 +54,15 @@ module Carson
|
|
|
51
54
|
puts_verbose "detected_remote: none"
|
|
52
55
|
end
|
|
53
56
|
|
|
57
|
+
remotes = list_git_remotes
|
|
58
|
+
duplicates = duplicate_remote_groups( remotes: remotes )
|
|
59
|
+
unless duplicates.empty?
|
|
60
|
+
duplicates.each_value do |group|
|
|
61
|
+
names = group.map { it.fetch( :name ) }.join( " and " )
|
|
62
|
+
puts_verbose "duplicate_remotes: #{names} share the same URL"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
54
66
|
branch = detect_main_branch
|
|
55
67
|
if branch && branch != config.main_branch
|
|
56
68
|
choices[ "git.main_branch" ] = branch
|
|
@@ -69,9 +81,18 @@ module Carson
|
|
|
69
81
|
return nil
|
|
70
82
|
end
|
|
71
83
|
|
|
84
|
+
duplicates = duplicate_remote_groups( remotes: remotes )
|
|
85
|
+
duplicate_names = duplicates.values.flatten.map { it.fetch( :name ) }.to_set
|
|
86
|
+
unless duplicates.empty?
|
|
87
|
+
duplicates.each_value do |group|
|
|
88
|
+
names = group.map { it.fetch( :name ) }.join( " and " )
|
|
89
|
+
puts_line "Remotes #{names} share the same URL. Consider removing the duplicate."
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
72
93
|
puts_line ""
|
|
73
94
|
puts_line "Git remote"
|
|
74
|
-
options = build_remote_options( remotes: remotes )
|
|
95
|
+
options = build_remote_options( remotes: remotes, duplicate_names: duplicate_names )
|
|
75
96
|
options << { label: "Other (enter name)", value: :other }
|
|
76
97
|
|
|
77
98
|
default_index = 0
|
|
@@ -151,12 +172,13 @@ module Carson
|
|
|
151
172
|
value.empty? ? nil : value
|
|
152
173
|
end
|
|
153
174
|
|
|
154
|
-
def build_remote_options( remotes: )
|
|
175
|
+
def build_remote_options( remotes:, duplicate_names: Set.new )
|
|
155
176
|
sorted = sort_remotes( remotes: remotes )
|
|
156
177
|
sorted.map do |entry|
|
|
157
178
|
name = entry.fetch( :name )
|
|
158
179
|
url = entry.fetch( :url )
|
|
159
|
-
|
|
180
|
+
tag = duplicate_names.include?( name ) ? " [duplicate]" : ""
|
|
181
|
+
{ label: "#{name} (#{url})#{tag}", value: name }
|
|
160
182
|
end
|
|
161
183
|
end
|
|
162
184
|
|
|
@@ -173,6 +195,35 @@ module Carson
|
|
|
173
195
|
well_known.sort_by { |e| WELL_KNOWN_REMOTES.index( e.fetch( :name ) ) || 999 } + others.sort_by { |e| e.fetch( :name ) }
|
|
174
196
|
end
|
|
175
197
|
|
|
198
|
+
# Normalises a remote URL so SSH and HTTPS variants of the same host/path compare equal.
|
|
199
|
+
# Strips trailing .git, lowercases, converts git@host:path to https://host/path.
|
|
200
|
+
def normalise_remote_url( url: )
|
|
201
|
+
text = url.to_s.strip
|
|
202
|
+
return "" if text.empty?
|
|
203
|
+
|
|
204
|
+
# Convert SSH shorthand (git@host:owner/repo) to HTTPS form.
|
|
205
|
+
if text.match?( /\A[\w.-]+@[\w.-]+:/ )
|
|
206
|
+
text = text.sub( /\A[\w.-]+@([\w.-]+):/, 'https://\1/' )
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
text = text.delete_suffix( ".git" )
|
|
210
|
+
text = text.chomp( "/" )
|
|
211
|
+
text.downcase
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Groups remotes that share the same normalised URL. Returns a hash of
|
|
215
|
+
# normalised_url => [remote entries] for groups with more than one member.
|
|
216
|
+
def duplicate_remote_groups( remotes: )
|
|
217
|
+
by_url = {}
|
|
218
|
+
remotes.each do |entry|
|
|
219
|
+
key = normalise_remote_url( url: entry.fetch( :url ) )
|
|
220
|
+
next if key.empty?
|
|
221
|
+
|
|
222
|
+
( by_url[ key ] ||= [] ) << entry
|
|
223
|
+
end
|
|
224
|
+
by_url.select { |_url, entries| entries.length > 1 }
|
|
225
|
+
end
|
|
226
|
+
|
|
176
227
|
def build_main_branch_options
|
|
177
228
|
options = []
|
|
178
229
|
main_exists = branch_exists_locally_or_remote?( branch: "main" )
|