@dotinc/ogre 0.15.3 → 0.15.4

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.
@@ -2,7 +2,7 @@ TAP version 14
2
2
  # Subtest: merge with no commit fails
3
3
  ok 1 - expected to reject
4
4
  1..1
5
- ok 1 - merge with no commit fails # time=142.615ms
5
+ ok 1 - merge with no commit fails # time=171.332ms
6
6
 
7
7
  # Subtest: merge fast-forward
8
8
  ok 1 - HEAD not pointing to new_branch
@@ -11,6 +11,6 @@ ok 1 - merge with no commit fails # time=142.615ms
11
11
  ok 4 - master is not at expected commit
12
12
  ok 5 - fast-forward failed, superfluous commit detected
13
13
  1..5
14
- ok 2 - merge fast-forward # time=16.056ms
14
+ ok 2 - merge fast-forward # time=25.959ms
15
15
 
16
16
  1..2
@@ -1,9 +1,9 @@
1
1
  TAP version 14
2
2
  # Subtest: diff is ok
3
3
  ok 1 - main is pointing at wrong commit
4
- ok 2 - invalid \# of change entries: [{"op":"add","path":"/nested/0","value":{"uuid":"cf4e4336-0066-4a17-9b72-96c408c40429","name":"new name"}},{"op":"add","path":"/nested/1","value":{"uuid":"e8c30e3f-ff76-4052-85ff-7ba6343f035b","name":"first name"}}]
4
+ ok 2 - invalid \# of change entries: [{"op":"add","path":"/nested/0","value":{"uuid":"293cebbb-9f78-45ee-a07a-735bdfc8a9f9","name":"new name"}},{"op":"add","path":"/nested/1","value":{"uuid":"8817b9e4-0baf-499e-8ce6-49ae3862d18f","name":"first name"}}]
5
5
  1..2
6
- ok 1 - diff is ok # time=199.319ms
6
+ ok 1 - diff is ok # time=181.102ms
7
7
 
8
8
  # Subtest: restore
9
9
  # Subtest: history check
@@ -16,7 +16,7 @@ ok 1 - diff is ok # time=199.319ms
16
16
  ok 7 - incorrect \# of commits
17
17
  ok 8 - incorrect \# of changelog entries
18
18
  1..8
19
- ok 1 - history check # time=86.356ms
19
+ ok 1 - history check # time=77.498ms
20
20
 
21
21
  # Subtest: date stays date
22
22
  ok 1 - incorrect \# of commits
@@ -24,7 +24,7 @@ ok 1 - diff is ok # time=199.319ms
24
24
  ok 3 - restored object does not equal last version.
25
25
  ok 4 - incorrect \# of changelog entries
26
26
  1..4
27
- ok 2 - date stays date # time=10.883ms
27
+ ok 2 - date stays date # time=9.386ms
28
28
 
29
29
  # Subtest: reconstruct with 2 commits
30
30
  ok 1 - main is pointing at wrong commit
@@ -33,17 +33,17 @@ ok 1 - diff is ok # time=199.319ms
33
33
  ok 4 - incorrect \# of commits
34
34
  ok 5 - incorrect \# of changelog entries
35
35
  1..5
36
- ok 3 - reconstruct with 2 commits # time=11.138ms
36
+ ok 3 - reconstruct with 2 commits # time=8.567ms
37
37
 
38
38
  1..3
39
- ok 2 - restore # time=159.863ms
39
+ ok 2 - restore # time=134.232ms
40
40
 
41
41
  # Subtest: history
42
42
  # Subtest: successful restore
43
43
  ok 1 - restored object does not equal last version.
44
44
  ok 2 - should not be the js ref
45
45
  1..2
46
- ok 1 - successful restore # time=270.127ms
46
+ ok 1 - successful restore # time=253.739ms
47
47
 
48
48
  # Subtest: remoteRefs doesn't change on commit
49
49
  ok 1 - input history refs and remote before change should not be the same object
@@ -52,7 +52,7 @@ ok 2 - restore # time=159.863ms
52
52
  ok 4 - history refs must not be the same as static remotes
53
53
  ok 5 - histories must not match anymore
54
54
  1..5
55
- ok 2 - remoteRefs doesn't change on commit # time=8.727ms
55
+ ok 2 - remoteRefs doesn't change on commit # time=14.737ms
56
56
 
57
57
  # Subtest: history contains HEAD ref
58
58
  ok 1 - should be equal
@@ -60,15 +60,15 @@ ok 2 - restore # time=159.863ms
60
60
  ok 3 - should be equal
61
61
  ok 4 - should be equal
62
62
  1..4
63
- ok 3 - history contains HEAD ref # time=1.159ms
63
+ ok 3 - history contains HEAD ref # time=1.192ms
64
64
 
65
65
  # Subtest: empty history unreachable HEAD
66
66
  ok 1 - expected to throw
67
67
  1..1
68
- ok 4 - empty history unreachable HEAD # time=1.218ms
68
+ ok 4 - empty history unreachable HEAD # time=1.567ms
69
69
 
70
70
  1..4
71
- ok 3 - history # time=358.304ms
71
+ ok 3 - history # time=339.06ms
72
72
 
73
73
  # Subtest: reset
74
74
  # Subtest: discard uncommitted changes
@@ -76,7 +76,7 @@ ok 3 - history # time=358.304ms
76
76
  ok 2 - wrong \# of changes in diff
77
77
  ok 3 - failed to reset
78
78
  1..3
79
- ok 1 - discard uncommitted changes # time=217.515ms
79
+ ok 1 - discard uncommitted changes # time=158.972ms
80
80
 
81
81
  # Subtest: reset to earlier commit
82
82
  ok 1 - should be equal
@@ -86,7 +86,7 @@ ok 3 - history # time=358.304ms
86
86
  ok 5 - failed to reset
87
87
  ok 6 - main should point to first hash
88
88
  1..6
89
- ok 2 - reset to earlier commit # time=22.263ms
89
+ ok 2 - reset to earlier commit # time=21.585ms
90
90
 
91
91
  # Subtest: reset to earlier version tag
92
92
  ok 1 - should be equal
@@ -96,54 +96,54 @@ ok 3 - history # time=358.304ms
96
96
  ok 5 - failed to reset
97
97
  ok 6 - main should point to first hash
98
98
  1..6
99
- ok 3 - reset to earlier version tag # time=9.324ms
99
+ ok 3 - reset to earlier version tag # time=7.045ms
100
100
 
101
101
  1..3
102
- ok 4 - reset # time=299.753ms
102
+ ok 4 - reset # time=259.271ms
103
103
 
104
104
  # Subtest: status
105
105
  # Subtest: clean repo no change
106
106
  ok 1 - Shouldn't have pending changes
107
107
  1..1
108
- ok 1 - clean repo no change # time=476.566ms
108
+ ok 1 - clean repo no change # time=437.377ms
109
109
 
110
110
  # Subtest: clean repo pending change
111
111
  ok 1 - Status doesn't contain the expected \# of changes: [{"op":"replace","path":"/name","value":"changed name"}]
112
112
  1..1
113
- ok 2 - clean repo pending change # time=1.092ms
113
+ ok 2 - clean repo pending change # time=0.764ms
114
114
 
115
115
  # Subtest: reading status shouldn't clean observer
116
116
  ok 1 - Status doesn't contain the expected \# of changes: [{"op":"replace","path":"/name","value":"changed name"}]
117
117
  ok 2 - Status doesn't contain the expected \# of changes: [{"op":"replace","path":"/name","value":"changed name"}]
118
118
  ok 3 - why different pending changes??
119
119
  1..3
120
- ok 3 - reading status shouldn't clean observer # time=1.654ms
120
+ ok 3 - reading status shouldn't clean observer # time=1.248ms
121
121
 
122
122
  # Subtest: after commit no change
123
123
  ok 1 - Shouldn't have pending changes
124
124
  1..1
125
- ok 4 - after commit no change # time=15.047ms
125
+ ok 4 - after commit no change # time=7.633ms
126
126
 
127
127
  # Subtest: after commit pending change
128
128
  ok 1 - Shouldn't have pending changes
129
129
  ok 2 - Status doesn't contain changes
130
130
  1..2
131
- ok 5 - after commit pending change # time=8.184ms
131
+ ok 5 - after commit pending change # time=4.985ms
132
132
 
133
133
  # Subtest: after commit pending change for rewrite array
134
134
  ok 1 - Shouldn't have pending changes
135
135
  ok 2 - Status doesn't contain changes
136
136
  1..2
137
- ok 6 - after commit pending change for rewrite array # time=7.358ms
137
+ ok 6 - after commit pending change for rewrite array # time=8.616ms
138
138
 
139
139
  # Subtest: change of nested array element prop
140
140
  ok 1 - Shouldn't have pending changes
141
141
  ok 2 - Status doesn't contain changes
142
142
  1..2
143
- ok 7 - change of nested array element prop # time=4.329ms
143
+ ok 7 - change of nested array element prop # time=3.596ms
144
144
 
145
145
  1..7
146
- ok 5 - status # time=585.67ms
146
+ ok 5 - status # time=533.401ms
147
147
 
148
148
  # Subtest: apply
149
149
  # Subtest: single patch
@@ -153,7 +153,7 @@ ok 5 - status # time=585.67ms
153
153
  ok 4 - Status doesn't contain changes
154
154
  ok 5 - It should have the right changes
155
155
  1..5
156
- ok 1 - single patch # time=207.337ms
156
+ ok 1 - single patch # time=163.461ms
157
157
 
158
158
  # Subtest: patch for undefined props with workaround
159
159
  ok 1 - Shouldn't have pending changes
@@ -161,7 +161,7 @@ ok 5 - status # time=585.67ms
161
161
  ok 3 - The final state should match up
162
162
  ok 4 - Status should contain 1 change
163
163
  1..4
164
- ok 2 - patch for undefined props with workaround # time=2.919ms
164
+ ok 2 - patch for undefined props with workaround # time=2.697ms
165
165
 
166
166
  # Subtest: multiple patches
167
167
  ok 1 - Shouldn't have pending changes
@@ -170,10 +170,10 @@ ok 5 - status # time=585.67ms
170
170
  ok 4 - It should have the right changes
171
171
  ok 5 - The final state does not match up
172
172
  1..5
173
- ok 3 - multiple patches # time=2.172ms
173
+ ok 3 - multiple patches # time=1.831ms
174
174
 
175
175
  1..3
176
- ok 6 - apply # time=311.126ms
176
+ ok 6 - apply # time=234.711ms
177
177
 
178
178
  # Subtest: pending changes - push helpers
179
179
  # Subtest: 1 commit & 1 ref update
@@ -182,7 +182,7 @@ ok 6 - apply # time=311.126ms
182
182
  ok 3 - wrong pending commit
183
183
  ok 4 - wrong pending ref update
184
184
  1..4
185
- ok 1 - 1 commit & 1 ref update # time=675.386ms
185
+ ok 1 - 1 commit & 1 ref update # time=632.334ms
186
186
 
187
187
  # Subtest: 2 commit & 1 ref update
188
188
  ok 1 - incorrect number of pending commits
@@ -190,7 +190,7 @@ ok 6 - apply # time=311.126ms
190
190
  ok 3 - wrong pending commits
191
191
  ok 4 - wrong pending ref update
192
192
  1..4
193
- ok 2 - 2 commit & 1 ref update # time=9.24ms
193
+ ok 2 - 2 commit & 1 ref update # time=8.541ms
194
194
 
195
195
  # Subtest: 1 commit & 2 ref updates
196
196
  ok 1 - incorrect number of pending commits
@@ -198,7 +198,7 @@ ok 6 - apply # time=311.126ms
198
198
  ok 3 - wrong pending commits
199
199
  ok 4 - wrong pending ref update
200
200
  1..4
201
- ok 3 - 1 commit & 2 ref updates # time=16.107ms
201
+ ok 3 - 1 commit & 2 ref updates # time=13.569ms
202
202
 
203
203
  # Subtest: 2 commit & 2 ref updates
204
204
  ok 1 - incorrect number of pending commits
@@ -206,7 +206,7 @@ ok 6 - apply # time=311.126ms
206
206
  ok 3 - wrong pending commits
207
207
  ok 4 - wrong pending ref update
208
208
  1..4
209
- ok 4 - 2 commit & 2 ref updates # time=32.931ms
209
+ ok 4 - 2 commit & 2 ref updates # time=33.024ms
210
210
 
211
211
  # Subtest: 3 commit & 2 ref updates
212
212
  ok 1 - incorrect number of pending commits
@@ -214,7 +214,7 @@ ok 6 - apply # time=311.126ms
214
214
  ok 3 - wrong pending commits
215
215
  ok 4 - wrong pending ref update
216
216
  1..4
217
- ok 5 - 3 commit & 2 ref updates # time=16.636ms
217
+ ok 5 - 3 commit & 2 ref updates # time=11.833ms
218
218
 
219
219
  # Subtest: 3 commit & 3 ref updates
220
220
  ok 1 - incorrect number of pending commits
@@ -222,7 +222,7 @@ ok 6 - apply # time=311.126ms
222
222
  ok 3 - wrong pending commits
223
223
  ok 4 - wrong pending ref update
224
224
  1..4
225
- ok 6 - 3 commit & 3 ref updates # time=10.661ms
225
+ ok 6 - 3 commit & 3 ref updates # time=11.692ms
226
226
 
227
227
  # Subtest: after merge 1 commit & 3 ref updates
228
228
  ok 1 - incorrect number of pending commits
@@ -230,7 +230,7 @@ ok 6 - apply # time=311.126ms
230
230
  ok 3 - wrong pending commits
231
231
  ok 4 - wrong pending ref update
232
232
  1..4
233
- ok 7 - after merge 1 commit & 3 ref updates # time=8.026ms
233
+ ok 7 - after merge 1 commit & 3 ref updates # time=10.797ms
234
234
 
235
235
  # Subtest: after merge 1 commit & 2 ref updates
236
236
  ok 1 - incorrect number of pending commits
@@ -238,7 +238,7 @@ ok 6 - apply # time=311.126ms
238
238
  ok 3 - wrong pending commits
239
239
  ok 4 - wrong pending ref update
240
240
  1..4
241
- ok 8 - after merge 1 commit & 2 ref updates # time=7.048ms
241
+ ok 8 - after merge 1 commit & 2 ref updates # time=9.483ms
242
242
 
243
243
  # Subtest: no merge 1 commit & 1 ref updates
244
244
  ok 1 - incorrect number of pending commits
@@ -246,7 +246,7 @@ ok 6 - apply # time=311.126ms
246
246
  ok 3 - wrong pending commits
247
247
  ok 4 - wrong pending ref update
248
248
  1..4
249
- ok 9 - no merge 1 commit & 1 ref updates # time=6.097ms
249
+ ok 9 - no merge 1 commit & 1 ref updates # time=5.398ms
250
250
 
251
251
  # Subtest: no merge no commit & 1 ref updates
252
252
  ok 1 - incorrect number of pending commits
@@ -254,7 +254,7 @@ ok 6 - apply # time=311.126ms
254
254
  ok 3 - wrong pending commits
255
255
  ok 4 - wrong pending ref update
256
256
  1..4
257
- ok 10 - no merge no commit & 1 ref updates # time=3.489ms
257
+ ok 10 - no merge no commit & 1 ref updates # time=3.725ms
258
258
 
259
259
  # Subtest: local remote state is updated after push
260
260
  ok 1 - incorrect number of pending commits
@@ -264,9 +264,9 @@ ok 6 - apply # time=311.126ms
264
264
  local remote state is updated after push
265
265
  ok 5 - remote should be different after push
266
266
  1..5
267
- ok 11 - local remote state is updated after push # time=5.247ms
267
+ ok 11 - local remote state is updated after push # time=5.175ms
268
268
 
269
269
  1..11
270
- ok 7 - pending changes - push helpers # time=884.975ms
270
+ ok 7 - pending changes - push helpers # time=840.222ms
271
271
 
272
272
  1..7
@@ -2,18 +2,18 @@ TAP version 14
2
2
  # Subtest: cannot tag on an empty repo
3
3
  ok 1 - expected to throw
4
4
  1..1
5
- ok 1 - cannot tag on an empty repo # time=31.484ms
5
+ ok 1 - cannot tag on an empty repo # time=32.148ms
6
6
 
7
7
  # Subtest: can create simple tag pointing to HEAD
8
8
  ok 1 - should be equal
9
9
  ok 2 - tag is not pointing to expected commit
10
10
  ok 3 - reference was not present in history
11
11
  1..3
12
- ok 2 - can create simple tag pointing to HEAD # time=130.111ms
12
+ ok 2 - can create simple tag pointing to HEAD # time=100.589ms
13
13
 
14
14
  # Subtest: cannot create tag with whitespace
15
15
  ok 1 - expected to throw
16
16
  1..1
17
- ok 3 - cannot create tag with whitespace # time=7.08ms
17
+ ok 3 - cannot create tag with whitespace # time=7.565ms
18
18
 
19
19
  1..3
@@ -3,42 +3,42 @@ TAP version 14
3
3
  ok 1 - should be equal
4
4
  ok 2 - should be equal
5
5
  1..2
6
- ok 1 - author <email@domain.info> # time=5.511ms
6
+ ok 1 - author <email@domain.info> # time=3.785ms
7
7
 
8
8
  # Subtest: author with space <email@domain.info>
9
9
  ok 1 - should be equal
10
10
  ok 2 - should be equal
11
11
  1..2
12
- ok 2 - author with space <email@domain.info> # time=1.578ms
12
+ ok 2 - author with space <email@domain.info> # time=0.666ms
13
13
 
14
14
  # Subtest: author @handle
15
15
  ok 1 - should be equal
16
16
  ok 2 - should be equal
17
17
  1..2
18
- ok 3 - author @handle # time=1.317ms
18
+ ok 3 - author @handle # time=0.45ms
19
19
 
20
20
  # Subtest: author with space @handle
21
21
  ok 1 - should be equal
22
22
  ok 2 - should be equal
23
23
  1..2
24
- ok 4 - author with space @handle # time=1.095ms
24
+ ok 4 - author with space @handle # time=0.405ms
25
25
 
26
26
  # Subtest: email@domain.info
27
27
  ok 1 - should be equal
28
28
  ok 2 - should be equal
29
29
  1..2
30
- ok 5 - email@domain.info # time=1.872ms
30
+ ok 5 - email@domain.info # time=0.569ms
31
31
 
32
32
  # Subtest: @handle
33
33
  ok 1 - should be equal
34
34
  ok 2 - should be equal
35
35
  1..2
36
- ok 6 - @handle # time=0.722ms
36
+ ok 6 - @handle # time=0.612ms
37
37
 
38
38
  # Subtest: empty author
39
39
  ok 1 - expected to throw
40
40
  1..1
41
- ok 7 - empty author # time=3.485ms
41
+ ok 7 - empty author # time=2.33ms
42
42
 
43
43
  # Subtest: mapPath
44
44
  # Subtest: find root
@@ -46,7 +46,7 @@ ok 7 - empty author # time=3.485ms
46
46
  ok 2 - root is not ancestor of commit
47
47
  ok 3 - path does not contain the right commit
48
48
  1..3
49
- ok 1 - find root # time=118.47ms
49
+ ok 1 - find root # time=115.819ms
50
50
 
51
51
  # Subtest: finds full path to root
52
52
  ok 1 - path does not contain 1 commit
@@ -54,13 +54,13 @@ ok 7 - empty author # time=3.485ms
54
54
  ok 3 - path does not contain the right commit at 0
55
55
  ok 4 - path does not contain the right commit at 1
56
56
  1..4
57
- ok 2 - finds full path to root # time=4.551ms
57
+ ok 2 - finds full path to root # time=4.71ms
58
58
 
59
59
  # Subtest: parent-to-child no ancestor
60
60
  ok 1 - path contains a commit
61
61
  ok 2 - child must not be an ancestor of parent
62
62
  1..2
63
- ok 3 - parent-to-child no ancestor # time=3.971ms
63
+ ok 3 - parent-to-child no ancestor # time=4.017ms
64
64
 
65
65
  # Subtest: finds path across 2 branches
66
66
  ok 1 - path does not contain 1 commit
@@ -68,9 +68,9 @@ ok 7 - empty author # time=3.485ms
68
68
  ok 3 - path does not contain the right commit at 0
69
69
  ok 4 - path does not contain the right commit at 1
70
70
  1..4
71
- ok 4 - finds path across 2 branches # time=4.799ms
71
+ ok 4 - finds path across 2 branches # time=4.932ms
72
72
 
73
73
  1..4
74
- ok 8 - mapPath # time=156.145ms
74
+ ok 8 - mapPath # time=148.136ms
75
75
 
76
76
  1..8
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v0.15.4 (Tue Mar 24 2026)
2
+
3
+ #### 🐛 Bug Fix
4
+
5
+ - fix: tags shouldn't be moved with detached HEAD ([@nadilas](https://github.com/nadilas))
6
+
7
+ #### Authors: 1
8
+
9
+ - [@nadilas](https://github.com/nadilas)
10
+
11
+ ---
12
+
1
13
  # v0.15.3 (Sun Mar 15 2026)
2
14
 
3
15
  #### 🐛 Bug Fix
package/lib/repository.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { applyPatch, compare, deepClone, JsonPatchError, validate, } from "fast-json-patch";
2
- import { brancheNameToRef, cleanRefValue, commitAtRefIn, createHeadRefValue, getLastRefPathElement, headValueRefPrefix, immutableArrayCopy, immutableMapCopy, refKeysAtCommit, localHeadPathPrefix, mapPath, mutableMapCopy, REFS_HEAD_KEY, REFS_MAIN_KEY, shaishToCommit, tagToRef, validateBranchName, validateRef, } from "./utils.js";
2
+ import { brancheNameToRef, cleanRefValue, commitAtRefIn, createHeadRefValue, getLastRefPathElement, headValueRefPrefix, immutableArrayCopy, immutableMapCopy, refKeysAtCommit, localHeadPathPrefix, mapPath, mutableMapCopy, REFS_HEAD_KEY, REFS_MAIN_KEY, shaishToCommit, isTagRef, tagToRef, validateBranchName, validateRef, } from "./utils.js";
3
3
  import * as jsondiffpatch from 'jsondiffpatch';
4
4
  /**
5
5
  * A repository recording and managing the state transitions of an object
@@ -249,7 +249,9 @@ export class Repository {
249
249
  else {
250
250
  const [commit, isRef, refKey] = shaishToCommit(shaish, this.refs, this.commits);
251
251
  await this.moveTo(commit);
252
- this.moveRef(REFS_HEAD_KEY, isRef && refKey !== undefined ? refKey : commit);
252
+ // Only follow branch refs. Tags produce detached HEAD (like git).
253
+ const isBranchRef = isRef && refKey !== undefined && !isTagRef(refKey);
254
+ this.moveRef(REFS_HEAD_KEY, isBranchRef ? refKey : commit);
253
255
  }
254
256
  }
255
257
  async commit(message, author, amend) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/dotindustries/ogre.git"
6
6
  },
7
- "version": "0.15.3",
7
+ "version": "0.15.4",
8
8
  "description": "Git-like repository for in-memory object versioning",
9
9
  "private": false,
10
10
  "main": "lib/index.js",
@@ -49,5 +49,5 @@
49
49
  "registry": "https://registry.npmjs.org/",
50
50
  "access": "public"
51
51
  },
52
- "gitHead": "c746013dd91c5154a25d0d617189c04ed81d8106"
52
+ "gitHead": "603470971151a33246e87fff83410e61a46364d4"
53
53
  }
@@ -109,6 +109,50 @@ test("checkout and create new branch with at least 1 commit", async (t) => {
109
109
  );
110
110
  });
111
111
 
112
+ test("checkout tag leaves HEAD detached", async (t) => {
113
+ const [repo] = await getBaseline();
114
+ repo.data.name = "new name";
115
+ const commitHash = await repo.commit("tagged change", testAuthor);
116
+ repo.tag("1.0.0");
117
+
118
+ // Move main forward so tag and main diverge
119
+ repo.data.description = "newer change";
120
+ await repo.commit("after tag", testAuthor);
121
+
122
+ // Checkout the tag
123
+ await repo.checkout("1.0.0");
124
+ t.equal(repo.branch(), "HEAD", "HEAD should be detached after tag checkout");
125
+ t.equal(repo.head(), commitHash, "HEAD should point to tagged commit hash");
126
+ });
127
+
128
+ test("commit after tag checkout does not move tag", async (t) => {
129
+ const [repo] = await getBaseline();
130
+ repo.data.name = "tagged";
131
+ const taggedCommit = await repo.commit("tagged change", testAuthor);
132
+ repo.tag("1.0.0");
133
+
134
+ await repo.checkout("1.0.0");
135
+
136
+ // Commit on detached HEAD
137
+ repo.data.description = "detached work";
138
+ const detachedCommit = await repo.commit("detached commit", testAuthor);
139
+
140
+ // Tag should NOT have moved
141
+ t.equal(
142
+ repo.ref("refs/tags/1.0.0"),
143
+ taggedCommit,
144
+ "tag should still point to original commit",
145
+ );
146
+ // HEAD should point to the new detached commit
147
+ t.equal(repo.head(), detachedCommit, "HEAD should point to detached commit");
148
+ // refs/heads/main should not have changed
149
+ t.equal(
150
+ repo.ref("refs/heads/main"),
151
+ taggedCommit,
152
+ "main should not move",
153
+ );
154
+ });
155
+
112
156
  test("replacing default branch on empty master removes main", async (t) => {
113
157
  const cx: ComplexObject = {
114
158
  nested: [],
package/src/repository.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  REFS_HEAD_KEY,
25
25
  REFS_MAIN_KEY,
26
26
  shaishToCommit,
27
+ isTagRef,
27
28
  tagToRef,
28
29
  validateBranchName,
29
30
  validateRef,
@@ -435,10 +436,9 @@ export class Repository<T extends { [k: PropertyKey]: any }>
435
436
  this.commits,
436
437
  );
437
438
  await this.moveTo(commit);
438
- this.moveRef(
439
- REFS_HEAD_KEY,
440
- isRef && refKey !== undefined ? refKey : commit,
441
- );
439
+ // Only follow branch refs. Tags produce detached HEAD (like git).
440
+ const isBranchRef = isRef && refKey !== undefined && !isTagRef(refKey);
441
+ this.moveRef(REFS_HEAD_KEY, isBranchRef ? refKey : commit);
442
442
  }
443
443
  }
444
444