95
95
private IDictionary<string, DirCacheEntry> toBeCheckedOut = new Dictionary<string
96
96
, DirCacheEntry>();
98
private IList<string> toBeDeleted = new AList<string>();
98
100
private IDictionary<string, MergeResult<Sequence>> mergeResults = new Dictionary<
99
101
string, MergeResult<Sequence>>();
101
103
private IDictionary<string, ResolveMerger.MergeFailureReason> failingPaths = new
102
104
Dictionary<string, ResolveMerger.MergeFailureReason>();
104
private ObjectInserter oi;
106
106
private bool enterSubtree;
108
108
private bool inCore;
176
// No problem found. The only thing left to be done is to
177
// checkout all files from "theirs" which have been selected to
178
// go into the new index.
177
180
// All content-merges are successfully done. If we can now write the
178
181
// new index we are on quite safe ground. Even if the checkout of
179
182
// files coming from "theirs" fails the user can work around such
184
187
throw new IndexWriteException();
187
// No problem found. The only thing left to be done is to checkout
188
// all files from "theirs" which have been selected to go into the
194
193
builder.Finish();
197
if (GetUnmergedPaths().IsEmpty())
196
if (GetUnmergedPaths().IsEmpty() && !Failed())
199
resultTree = dircache.WriteTree(oi);
198
resultTree = dircache.WriteTree(GetObjectInserter());
224
223
foreach (KeyValuePair<string, DirCacheEntry> entry in toBeCheckedOut.EntrySet())
226
225
FilePath f = new FilePath(db.WorkTree, entry.Key);
227
if (entry.Value != null)
229
CreateDir(f.GetParentFile());
230
DirCacheCheckout.CheckoutEntry(db, f, entry.Value, r);
236
failingPaths.Put(entry.Key, ResolveMerger.MergeFailureReason.COULD_NOT_DELETE);
226
CreateDir(f.GetParentFile());
227
DirCacheCheckout.CheckoutEntry(db, f, entry.Value, r);
239
228
modifiedFiles.AddItem(entry.Key);
230
// Iterate in reverse so that "folder/file" is deleted before
231
// "folder". Otherwise this could result in a failing path because
232
// of a non-empty directory, for which delete() would fail.
233
for (int i = toBeDeleted.Count - 1; i >= 0; i--)
235
string fileName = toBeDeleted[i];
236
FilePath f = new FilePath(db.WorkTree, fileName);
239
failingPaths.Put(fileName, ResolveMerger.MergeFailureReason.COULD_NOT_DELETE);
241
modifiedFiles.AddItem(fileName);
310
312
/// <param name="path"></param>
311
313
/// <param name="p"></param>
312
314
/// <param name="stage"></param>
315
/// <param name="lastMod"></param>
316
/// <param name="len"></param>
313
317
/// <returns>the entry which was added to the index</returns>
314
private DirCacheEntry Add(byte[] path, CanonicalTreeParser p, int stage)
318
private DirCacheEntry Add(byte[] path, CanonicalTreeParser p, int stage, long lastMod
316
321
if (p != null && !p.EntryFileMode.Equals(FileMode.TREE))
318
323
DirCacheEntry e = new DirCacheEntry(path, stage);
319
324
e.FileMode = p.EntryFileMode;
320
325
e.SetObjectId(p.EntryObjectId);
326
e.LastModified = lastMod;
335
/// adds a entry to the index builder which is a copy of the specified
338
/// <param name="e">the entry which should be copied</param>
339
/// <returns>the entry which was added to the index</returns>
340
private DirCacheEntry Keep(DirCacheEntry e)
342
DirCacheEntry newEntry = new DirCacheEntry(e.PathString, e.Stage);
343
newEntry.FileMode = e.FileMode;
344
newEntry.SetObjectId(e.GetObjectId());
345
newEntry.LastModified = e.LastModified;
346
newEntry.SetLength(e.Length);
347
builder.Add(newEntry);
327
351
/// <summary>Processes one path and tries to merge.</summary>
329
353
/// Processes one path and tries to merge. This method will do all do all
410
DirCacheEntry ourDce = null;
411
if (index == null || index.GetDirCacheEntry() == null)
413
// create a fake DCE, but only if ours is valid. ours is kept only
414
// in case it is valid, so a null ourDce is ok in all other cases.
417
ourDce = new DirCacheEntry(tw.RawPath);
418
ourDce.SetObjectId(tw.GetObjectId(T_OURS));
419
ourDce.FileMode = tw.GetFileMode(T_OURS);
424
ourDce = index.GetDirCacheEntry();
386
426
if (NonTree(modeO) && NonTree(modeT) && tw.IdEqual(T_OURS, T_THEIRS))
388
428
// OURS and THEIRS have equal content. Check the file mode
389
429
if (modeO == modeT)
391
431
// content and mode of OURS and THEIRS are equal: it doesn't
392
// matter which one we choose. OURS is chosen.
393
Add(tw.RawPath, ours, DirCacheEntry.STAGE_0);
432
// matter which one we choose. OURS is chosen. Since the index
433
// is clean (the index matches already OURS) we can keep the existing one
394
435
// no checkout needed!
405
446
if (newMode == modeO)
407
448
// ours version is preferred
408
Add(tw.RawPath, ours, DirCacheEntry.STAGE_0);
412
453
// the preferred version THEIRS has a different mode
413
454
// than ours. Check it out!
414
if (IsWorktreeDirty())
455
if (IsWorktreeDirty(work))
418
DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0);
459
// we know about length and lastMod only after we have written the new content.
460
// This will happen later. Set these values to 0 for know.
461
DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0, 0, 0);
419
462
toBeCheckedOut.Put(tw.PathString, e);
425
// FileModes are not mergeable. We found a conflict on modes
426
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1);
427
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2);
428
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3);
468
// FileModes are not mergeable. We found a conflict on modes.
469
// For conflicting entries we don't know lastModified and length.
470
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
471
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0);
472
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0);
429
473
unmergedPaths.AddItem(tw.PathString);
430
474
mergeResults.Put(tw.PathString, new MergeResult<RawText>(Sharpen.Collections.EmptyList
431
475
<RawText>()).Upcast ());
436
480
if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS))
438
482
// THEIRS was not changed compared to BASE. All changes must be in
439
// OURS. OURS is chosen.
440
Add(tw.RawPath, ours, DirCacheEntry.STAGE_0);
483
// OURS. OURS is chosen. We can keep the existing entry.
441
485
// no checkout needed!
446
490
// OURS was not changed compared to BASE. All changes must be in
447
491
// THEIRS. THEIRS is chosen.
448
492
// Check worktree before checking out THEIRS
449
if (IsWorktreeDirty())
493
if (IsWorktreeDirty(work))
453
497
if (NonTree(modeT))
455
DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0);
499
// we know about length and lastMod only after we have written
501
// This will happen later. Set these values to 0 for know.
502
DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0, 0, 0);
458
505
toBeCheckedOut.Put(tw.PathString, e);
481
528
if (NonTree(modeB))
483
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1);
530
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
485
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2);
532
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0);
486
533
unmergedPaths.AddItem(tw.PathString);
487
534
enterSubtree = false;
492
539
if (NonTree(modeB))
494
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1);
541
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
496
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3);
543
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0);
497
544
unmergedPaths.AddItem(tw.PathString);
498
545
enterSubtree = false;
512
559
if (NonTree(modeO) && NonTree(modeT))
514
561
// Check worktree before modifying files
515
if (IsWorktreeDirty())
562
if (IsWorktreeDirty(work))
566
// Don't attempt to resolve submodule link conflicts
567
if (IsGitLink(modeO) || IsGitLink(modeT))
569
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
570
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0);
571
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0);
572
unmergedPaths.AddItem(tw.PathString);
519
575
MergeResult<RawText> result = ContentMerge(@base, ours, theirs);
520
576
FilePath of = WriteMergedFile(result);
521
577
UpdateIndex(@base, ours, theirs, result, of);
533
589
if (((modeO != 0 && !tw.IdEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw.IdEqual(T_BASE
536
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1);
537
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2);
538
DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3);
592
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
593
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0);
594
DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0);
539
595
// OURS was deleted checkout THEIRS
542
598
// Check worktree before checking out THEIRS
543
if (IsWorktreeDirty())
599
if (IsWorktreeDirty(work))
592
648
int modeI = tw.GetRawMode(T_INDEX);
593
649
int modeO = tw.GetRawMode(T_OURS);
594
650
// Index entry has to match ours to be considered clean
595
bool isDirty = NonTree(modeI) && !(tw.IdEqual(T_INDEX, T_OURS) && modeO == modeI);
651
bool isDirty = NonTree(modeI) && !(modeO == modeI && tw.IdEqual(T_INDEX, T_OURS));
598
654
failingPaths.Put(tw.PathString, ResolveMerger.MergeFailureReason.DIRTY_INDEX);
603
private bool IsWorktreeDirty()
659
private bool IsWorktreeDirty(WorkingTreeIterator work)
661
if (inCore || work == null)
609
665
int modeF = tw.GetRawMode(T_FILE);
610
666
int modeO = tw.GetRawMode(T_OURS);
611
667
// Worktree entry has to match ours to be considered clean
612
bool isDirty = NonTree(modeF) && !(tw.IdEqual(T_FILE, T_OURS) && modeO == modeF);
668
bool isDirty = work.IsModeDifferent(modeO);
669
if (!isDirty && NonTree(modeF))
671
isDirty = !tw.IdEqual(T_FILE, T_OURS);
615
675
failingPaths.Put(tw.PathString, ResolveMerger.MergeFailureReason.DIRTY_WORKTREE);
640
700
// a conflict occurred, the file will contain conflict markers
641
701
// the index will be populated with the three stages and only the
642
702
// workdir (if used) contains the halfways merged content
643
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1);
644
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2);
645
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3);
703
Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
704
Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0);
705
Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0);
646
706
mergeResults.Put(tw.PathString, result.Upcast ());
661
721
InputStream @is = new FileInputStream(of);
664
dce.SetObjectId(oi.Insert(Constants.OBJ_BLOB, of.Length(), @is));
724
dce.SetObjectId(GetObjectInserter().Insert(Constants.OBJ_BLOB, of.Length(), @is));
702
762
throw new NGit.Errors.NotSupportedException();
704
764
of = new FilePath(workTree, tw.PathString);
765
FilePath parentFolder = of.GetParentFile();
766
if (!parentFolder.Exists())
768
parentFolder.Mkdirs();
705
770
fos = new FileOutputStream(of);
787
852
return mode != 0 && !FileMode.TREE.Equals(mode);
855
private static bool IsGitLink(int mode)
857
return FileMode.GITLINK.Equals(mode);
790
860
public override ObjectId GetResultTreeId()
792
862
return (resultTree == null) ? null : resultTree.ToObjectId();