예제 #1
0
  private boolean isIndexDirty() {
    if (inCore) return false;

    final int modeI = tw.getRawMode(T_INDEX);
    final int modeO = tw.getRawMode(T_OURS);

    // Index entry has to match ours to be considered clean
    final boolean isDirty = nonTree(modeI) && !(modeO == modeI && tw.idEqual(T_INDEX, T_OURS));
    if (isDirty) failingPaths.put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
    return isDirty;
  }
예제 #2
0
  private boolean isWorktreeDirty(WorkingTreeIterator work, DirCacheEntry ourDce)
      throws IOException {
    if (work == null) return false;

    final int modeF = tw.getRawMode(T_FILE);
    final int modeO = tw.getRawMode(T_OURS);

    // Worktree entry has to match ours to be considered clean
    boolean isDirty;
    if (ourDce != null) isDirty = work.isModified(ourDce, true, reader);
    else {
      isDirty = work.isModeDifferent(modeO);
      if (!isDirty && nonTree(modeF)) isDirty = !tw.idEqual(T_FILE, T_OURS);
    }

    // Ignore existing empty directories
    if (isDirty && modeF == FileMode.TYPE_TREE && modeO == FileMode.TYPE_MISSING) isDirty = false;
    if (isDirty) failingPaths.put(tw.getPathString(), MergeFailureReason.DIRTY_WORKTREE);
    return isDirty;
  }
예제 #3
0
  /**
   * Processes one path and tries to merge. This method will do all do all trivial (not content)
   * merges and will also detect if a merge will fail. The merge will fail when one of the following
   * is true
   *
   * <ul>
   *   <li>the index entry does not match the entry in ours. When merging one branch into the
   *       current HEAD, ours will point to HEAD and theirs will point to the other branch. It is
   *       assumed that the index matches the HEAD because it will only not match HEAD if it was
   *       populated before the merge operation. But the merge commit should not accidentally
   *       contain modifications done before the merge. Check the <a href=
   *       "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" >git
   *       read-tree</a> documentation for further explanations.
   *   <li>A conflict was detected and the working-tree file is dirty. When a conflict is detected
   *       the content-merge algorithm will try to write a merged version into the working-tree. If
   *       the file is dirty we would override unsaved data.
   * </ul>
   *
   * @param base the common base for ours and theirs
   * @param ours the ours side of the merge. When merging a branch into the HEAD ours will point to
   *     HEAD
   * @param theirs the theirs side of the merge. When merging a branch into the current HEAD theirs
   *     will point to the branch which is merged into HEAD.
   * @param index the index entry
   * @param work the file in the working tree
   * @param ignoreConflicts see {@link ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree,
   *     RevTree, boolean)}
   * @return <code>false</code> if the merge will fail because the index entry didn't match ours or
   *     the working-dir file was dirty and a conflict occurred
   * @throws MissingObjectException
   * @throws IncorrectObjectTypeException
   * @throws CorruptObjectException
   * @throws IOException
   * @since 3.5
   */
  protected boolean processEntry(
      CanonicalTreeParser base,
      CanonicalTreeParser ours,
      CanonicalTreeParser theirs,
      DirCacheBuildIterator index,
      WorkingTreeIterator work,
      boolean ignoreConflicts)
      throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException,
          IOException {
    enterSubtree = true;
    final int modeO = tw.getRawMode(T_OURS);
    final int modeT = tw.getRawMode(T_THEIRS);
    final int modeB = tw.getRawMode(T_BASE);

    if (modeO == 0 && modeT == 0 && modeB == 0)
      // File is either untracked or new, staged but uncommitted
      return true;

    if (isIndexDirty()) return false;

    DirCacheEntry ourDce = null;

    if (index == null || index.getDirCacheEntry() == null) {
      // create a fake DCE, but only if ours is valid. ours is kept only
      // in case it is valid, so a null ourDce is ok in all other cases.
      if (nonTree(modeO)) {
        ourDce = new DirCacheEntry(tw.getRawPath());
        ourDce.setObjectId(tw.getObjectId(T_OURS));
        ourDce.setFileMode(tw.getFileMode(T_OURS));
      }
    } else {
      ourDce = index.getDirCacheEntry();
    }

    if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) {
      // OURS and THEIRS have equal content. Check the file mode
      if (modeO == modeT) {
        // content and mode of OURS and THEIRS are equal: it doesn't
        // matter which one we choose. OURS is chosen. Since the index
        // is clean (the index matches already OURS) we can keep the existing one
        keep(ourDce);
        // no checkout needed!
        return true;
      } else {
        // same content but different mode on OURS and THEIRS.
        // Try to merge the mode and report an error if this is
        // not possible.
        int newMode = mergeFileModes(modeB, modeO, modeT);
        if (newMode != FileMode.MISSING.getBits()) {
          if (newMode == modeO)
            // ours version is preferred
            keep(ourDce);
          else {
            // the preferred version THEIRS has a different mode
            // than ours. Check it out!
            if (isWorktreeDirty(work, ourDce)) return false;
            // we know about length and lastMod only after we have written the new content.
            // This will happen later. Set these values to 0 for know.
            DirCacheEntry e = add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_0, 0, 0);
            toBeCheckedOut.put(tw.getPathString(), e);
          }
          return true;
        } else {
          // FileModes are not mergeable. We found a conflict on modes.
          // For conflicting entries we don't know lastModified and length.
          add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
          add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
          add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
          unmergedPaths.add(tw.getPathString());
          mergeResults.put(
              tw.getPathString(), new MergeResult<RawText>(Collections.<RawText>emptyList()));
        }
        return true;
      }
    }

    if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
      // THEIRS was not changed compared to BASE. All changes must be in
      // OURS. OURS is chosen. We can keep the existing entry.
      if (ourDce != null) keep(ourDce);
      // no checkout needed!
      return true;
    }

    if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
      // OURS was not changed compared to BASE. All changes must be in
      // THEIRS. THEIRS is chosen.

      // Check worktree before checking out THEIRS
      if (isWorktreeDirty(work, ourDce)) return false;
      if (nonTree(modeT)) {
        // we know about length and lastMod only after we have written
        // the new content.
        // This will happen later. Set these values to 0 for know.
        DirCacheEntry e = add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_0, 0, 0);
        if (e != null) toBeCheckedOut.put(tw.getPathString(), e);
        return true;
      } else {
        // we want THEIRS ... but THEIRS contains a folder or the
        // deletion of the path. Delete what's in the workingtree (the
        // workingtree is clean) but do not complain if the file is
        // already deleted locally. This complements the test in
        // isWorktreeDirty() for the same case.
        if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) return true;
        toBeDeleted.add(tw.getPathString());
        return true;
      }
    }

    if (tw.isSubtree()) {
      // file/folder conflicts: here I want to detect only file/folder
      // conflict between ours and theirs. file/folder conflicts between
      // base/index/workingTree and something else are not relevant or
      // detected later
      if (nonTree(modeO) && !nonTree(modeT)) {
        if (nonTree(modeB)) add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
        unmergedPaths.add(tw.getPathString());
        enterSubtree = false;
        return true;
      }
      if (nonTree(modeT) && !nonTree(modeO)) {
        if (nonTree(modeB)) add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
        unmergedPaths.add(tw.getPathString());
        enterSubtree = false;
        return true;
      }

      // ours and theirs are both folders or both files (and treewalk
      // tells us we are in a subtree because of index or working-dir).
      // If they are both folders no content-merge is required - we can
      // return here.
      if (!nonTree(modeO)) return true;

      // ours and theirs are both files, just fall out of the if block
      // and do the content merge
    }

    if (nonTree(modeO) && nonTree(modeT)) {
      // Check worktree before modifying files
      if (isWorktreeDirty(work, ourDce)) return false;

      // Don't attempt to resolve submodule link conflicts
      if (isGitLink(modeO) || isGitLink(modeT)) {
        add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
        add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
        unmergedPaths.add(tw.getPathString());
        return true;
      }

      MergeResult<RawText> result = contentMerge(base, ours, theirs);
      if (ignoreConflicts) result.setContainsConflicts(false);
      updateIndex(base, ours, theirs, result);
      if (result.containsConflicts() && !ignoreConflicts) unmergedPaths.add(tw.getPathString());
      modifiedFiles.add(tw.getPathString());
    } else if (modeO != modeT) {
      // OURS or THEIRS has been deleted
      if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS))
          || (modeT != 0 && !tw.idEqual(T_BASE, T_THEIRS)))) {

        add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
        DirCacheEntry e = add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);

        // OURS was deleted checkout THEIRS
        if (modeO == 0) {
          // Check worktree before checking out THEIRS
          if (isWorktreeDirty(work, ourDce)) return false;
          if (nonTree(modeT)) {
            if (e != null) toBeCheckedOut.put(tw.getPathString(), e);
          }
        }

        unmergedPaths.add(tw.getPathString());

        // generate a MergeResult for the deleted file
        mergeResults.put(tw.getPathString(), contentMerge(base, ours, theirs));
      }
    }
    return true;
  }