@@ -147,12 +147,6 @@ func getLastCommitForPaths(c *object.Commit, treePath string, paths []string) (m
147
147
break
148
148
}
149
149
current := cIn.(*commitAndPaths)
150
- currentID := current.commit.ID()
151
-
152
- if seen[currentID] {
153
- continue
154
- }
155
- seen[currentID] = true
156
150
157
151
// Load the parent commits for the one we are currently examining
158
152
numParents := current.commit.NumParents()
@@ -166,8 +160,7 @@ func getLastCommitForPaths(c *object.Commit, treePath string, paths []string) (m
166
160
}
167
161
168
162
// Examine the current commit and set of interesting paths
169
- numOfParentsWithPath := make([]int, len(current.paths))
170
- pathChanged := make([]bool, len(current.paths))
163
+ pathUnchanged := make([]bool, len(current.paths))
171
164
parentHashes := make([]map[string]plumbing.Hash, len(parents))
172
165
for j, parent := range parents {
173
166
parentHashes[j], err = getFileHashes(parent, treePath, current.paths)
@@ -176,42 +169,32 @@ func getLastCommitForPaths(c *object.Commit, treePath string, paths []string) (m
176
169
}
177
170
178
171
for i, path := range current.paths {
179
- if parentHashes[j][path] != plumbing.ZeroHash {
180
- numOfParentsWithPath[i]++
181
- if parentHashes[j][path] != current.hashes[path] {
182
- pathChanged[i] = true
183
- }
172
+ if parentHashes[j][path] == current.hashes[path] {
173
+ pathUnchanged[i] = true
184
174
}
185
175
}
186
176
}
187
177
188
178
var remainingPaths []string
189
179
for i, path := range current.paths {
190
- switch numOfParentsWithPath[i] {
191
- case 0:
192
- // The path didn't exist in any parent, so it must have been created by
193
- // this commit. The results could already contain some newer change from
194
- // different path, so don't override that.
195
- if result[path] == nil {
196
- result[path] = current.commit
197
- }
198
- case 1:
199
- // The file is present on exactly one parent, so check if it was changed
200
- // and save the revision if it did.
201
- if pathChanged[i] {
202
- if result[path] == nil {
203
- result[path] = current.commit
204
- }
205
- } else {
180
+ // The results could already contain some newer change for the same path,
181
+ // so don't override that and bail out on the file early.
182
+ if result[path] == nil {
183
+ if pathUnchanged[i] {
184
+ // The path existed with the same hash in at least one parent so it could
185
+ // not have been changed in this commit directly.
206
186
remainingPaths = append(remainingPaths, path)
187
+ } else {
188
+ // There are few possible cases how can we get here:
189
+ // - The path didn't exist in any parent, so it must have been created by
190
+ // this commit.
191
+ // - The path did exist in the parent commit, but the hash of the file has
192
+ // changed.
193
+ // - We are looking at a merge commit and the hash of the file doesn't
194
+ // match any of the hashes being merged. This is more common for directories,
195
+ // but it can also happen if a file is changed through conflict resolution.
196
+ result[path] = current.commit
207
197
}
208
- default:
209
- // The file is present on more than one of the parent paths, so this is
210
- // a merge. We have to examine all the parent trees to find out where
211
- // the change occurred. pathChanged[i] would tell us that the file was
212
- // changed during the merge, but it wouldn't tell us the relevant commit
213
- // that introduced it.
214
- remainingPaths = append(remainingPaths, path)
215
198
}
216
199
}
217
200
@@ -222,17 +205,29 @@ func getLastCommitForPaths(c *object.Commit, treePath string, paths []string) (m
222
205
if seen[parent.ID()] {
223
206
continue
224
207
}
208
+ seen[parent.ID()] = true
225
209
226
210
// Combine remainingPath with paths available on the parent branch
227
211
// and make union of them
228
212
var remainingPathsForParent []string
213
+ var newRemainingPaths []string
229
214
for _, path := range remainingPaths {
230
- if parentHashes[j][path] != plumbing.ZeroHash {
215
+ if parentHashes[j][path] == current.hashes[path] {
231
216
remainingPathsForParent = append(remainingPathsForParent, path)
217
+ } else {
218
+ newRemainingPaths = append(newRemainingPaths, path)
232
219
}
233
220
}
234
221
235
- heap.Push(&commitAndPaths{parent, remainingPathsForParent, parentHashes[j]})
222
+ if remainingPathsForParent != nil {
223
+ heap.Push(&commitAndPaths{parent, remainingPathsForParent, parentHashes[j]})
224
+ }
225
+
226
+ if len(newRemainingPaths) == 0 {
227
+ break
228
+ } else {
229
+ remainingPaths = newRemainingPaths
230
+ }
236
231
}
237
232
}
238
233
}
0 commit comments