gip-remote 1.2.5 → 1.2.7

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.
Files changed (2) hide show
  1. package/index.js +35 -4
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -210,9 +210,26 @@ class Remote extends ReadyResource {
210
210
  }
211
211
  }
212
212
 
213
- // 4b. Insert file records (idempotent w.r.t. (branch, path) acts as
214
- // upsert for paths whose blob/mode/metadata changed).
213
+ // 4b. Insert/update file records but ONLY for paths whose blob or mode
214
+ // actually changed. Earlier versions blindly upserted every file in
215
+ // the new tree on every push, which overwrote the commit metadata of
216
+ // untouched files with HEAD's author/message/timestamp. The visible
217
+ // symptom: a tree view shows every file as "last modified by the
218
+ // latest commit", so per-file timestamps become useless.
219
+ //
220
+ // Skipping unchanged rows preserves the metadata of the commit that
221
+ // last *actually* modified the file. New paths and modified paths
222
+ // still take the current commit's metadata as before.
215
223
  for (const file of files) {
224
+ const existing = await this._db.get('@gip/files', {
225
+ branch: refName,
226
+ path: file.path
227
+ })
228
+ if (existing && existing.oid === file.oid && existing.mode === file.mode) {
229
+ // Same blob, same mode → file is unchanged in this commit. Leave
230
+ // the existing row untouched so its commit metadata stays accurate.
231
+ continue
232
+ }
216
233
  await this._db.insert('@gip/files', {
217
234
  branch: refName,
218
235
  path: file.path,
@@ -241,7 +258,21 @@ class Remote extends ReadyResource {
241
258
  objects: [...objects.keys()]
242
259
  })
243
260
  } else {
244
- // 5b. Insert branch record
261
+ // 5b. Insert/update branch record.
262
+ //
263
+ // `objects` is the denormalized "everything reachable from this branch"
264
+ // set used by getRefObjects() at fetch time. CRITICAL: we must MERGE
265
+ // with the prior record's objects, not overwrite. A real git client
266
+ // sends a thin pack on follow-up pushes (only the new objects), so
267
+ // `objects.keys()` here would be e.g. just {commit B, new tree} —
268
+ // commit A and its tree from the previous push would be dropped from
269
+ // the list, and a fresh clone calling getRefObjects(headOfB) would be
270
+ // missing every parent commit. That's the "I cloned and lost a
271
+ // commit" symptom.
272
+ const prev = await this._db.get('@gip/branches', { name: refName })
273
+ const merged = new Set(prev ? prev.objects : [])
274
+ for (const k of objects.keys()) merged.add(k)
275
+
245
276
  await this._db.insert('@gip/branches', {
246
277
  name: refName,
247
278
  commitOid: oid,
@@ -249,7 +280,7 @@ class Remote extends ReadyResource {
249
280
  author: commit.author,
250
281
  message: commit.message,
251
282
  timestamp: commit.timestamp,
252
- objects: [...objects.keys()]
283
+ objects: [...merged]
253
284
  })
254
285
 
255
286
  // 6. Set HEAD to first branch pushed (like git init)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gip-remote",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "description": "Git+Pear remote DB for handling git data",
5
5
  "main": "index.js",
6
6
  "scripts": {