Пример #1
0
  /**
   * Return a list of 'tip' branches (I.E. branches that aren't included entirely within another
   * branch).
   *
   * @param git
   * @return
   */
  public Collection<Revision> filterTipBranches(Collection<Revision> revisions) {
    // If we have 3 branches that we might want to build
    // ----A--.---.--- B
    //        \-----C

    // we only want (B) and (C), as (A) is an ancestor (old).

    List<Revision> l = new ArrayList<Revision>(revisions);

    OUTER:
    for (int i = 0; i < l.size(); i++) {
      for (int j = i + 1; j < l.size(); j++) {
        Revision ri = l.get(i);
        Revision rj = l.get(j);
        ObjectId commonAncestor = git.mergeBase(ri.getSha1(), rj.getSha1());
        if (commonAncestor == null) {
          continue;
        }

        if (commonAncestor.equals(ri.getSha1())) {
          LOGGER.fine("filterTipBranches: " + rj + " subsumes " + ri);
          l.remove(i);
          i--;
          continue OUTER;
        }
        if (commonAncestor.equals(rj.getSha1())) {
          LOGGER.fine("filterTipBranches: " + ri + " subsumes " + rj);
          l.remove(j);
          j--;
        }
      }
    }

    return l;
  }
Пример #2
0
  public CodeReviewCommit createCherryPickFromCommit(
      Repository repo,
      ObjectInserter inserter,
      RevCommit mergeTip,
      RevCommit originalCommit,
      PersonIdent cherryPickCommitterIdent,
      String commitMsg,
      CodeReviewRevWalk rw)
      throws MissingObjectException, IncorrectObjectTypeException, IOException,
          MergeIdenticalTreeException, MergeConflictException {

    final ThreeWayMerger m = newThreeWayMerger(repo, inserter);

    m.setBase(originalCommit.getParent(0));
    if (m.merge(mergeTip, originalCommit)) {
      ObjectId tree = m.getResultTreeId();
      if (tree.equals(mergeTip.getTree())) {
        throw new MergeIdenticalTreeException("identical tree");
      }

      CommitBuilder mergeCommit = new CommitBuilder();
      mergeCommit.setTreeId(tree);
      mergeCommit.setParentId(mergeTip);
      mergeCommit.setAuthor(originalCommit.getAuthorIdent());
      mergeCommit.setCommitter(cherryPickCommitterIdent);
      mergeCommit.setMessage(commitMsg);
      return rw.parseCommit(commit(inserter, mergeCommit));
    } else {
      throw new MergeConflictException("merge conflict");
    }
  }
Пример #3
0
  /**
   * Adds a set of refs to the set of packed-refs. Only non-symbolic refs are added. If a ref with
   * the given name already existed in packed-refs it is updated with the new value. Each loose ref
   * which was added to the packed-ref file is deleted. If a given ref can't be locked it will not
   * be added to the pack file.
   *
   * @param refs the refs to be added. Must be fully qualified.
   * @throws IOException
   */
  public void pack(List<String> refs) throws IOException {
    if (refs.size() == 0) return;
    FS fs = parent.getFS();

    // Lock the packed refs file and read the content
    LockFile lck = new LockFile(packedRefsFile);
    if (!lck.lock())
      throw new IOException(MessageFormat.format(JGitText.get().cannotLock, packedRefsFile));

    try {
      final PackedRefList packed = getPackedRefs();
      RefList<Ref> cur = readPackedRefs();

      // Iterate over all refs to be packed
      for (String refName : refs) {
        Ref ref = readRef(refName, cur);
        if (ref.isSymbolic()) continue; // can't pack symbolic refs
        // Add/Update it to packed-refs
        int idx = cur.find(refName);
        if (idx >= 0) cur = cur.set(idx, peeledPackedRef(ref));
        else cur = cur.add(idx, peeledPackedRef(ref));
      }

      // The new content for packed-refs is collected. Persist it.
      commitPackedRefs(lck, cur, packed);

      // Now delete the loose refs which are now packed
      for (String refName : refs) {
        // Lock the loose ref
        File refFile = fileFor(refName);
        if (!fs.exists(refFile)) continue;
        LockFile rLck = new LockFile(refFile);
        if (!rLck.lock()) continue;
        try {
          LooseRef currentLooseRef = scanRef(null, refName);
          if (currentLooseRef == null || currentLooseRef.isSymbolic()) continue;
          Ref packedRef = cur.get(refName);
          ObjectId clr_oid = currentLooseRef.getObjectId();
          if (clr_oid != null && clr_oid.equals(packedRef.getObjectId())) {
            RefList<LooseRef> curLoose, newLoose;
            do {
              curLoose = looseRefs.get();
              int idx = curLoose.find(refName);
              if (idx < 0) break;
              newLoose = curLoose.remove(idx);
            } while (!looseRefs.compareAndSet(curLoose, newLoose));
            int levels = levelsIn(refName) - 2;
            delete(refFile, levels, rLck);
          }
        } finally {
          rLck.unlock();
        }
      }
      // Don't fire refsChanged. The refs have not change, only their
      // storage.
    } finally {
      lck.unlock();
    }
  }
Пример #4
0
  public List<Tag> getTagsOnCommit(final String revName) throws GitException, IOException {
    final Repository db = getRepository();
    final ObjectId commit = db.resolve(revName);
    final List<Tag> ret = new ArrayList<Tag>();

    for (final Map.Entry<String, Ref> tag : db.getTags().entrySet()) {
      final ObjectId tagId = tag.getValue().getObjectId();
      if (commit.equals(tagId)) ret.add(new Tag(tag.getKey(), tagId));
    }
    return ret;
  }
Пример #5
0
 /**
  * Load the configuration as a Git text style configuration file.
  *
  * <p>If the file does not exist, this configuration is cleared, and thus behaves the same as
  * though the file exists, but is empty.
  *
  * @throws IOException the file could not be read (but does exist).
  * @throws ConfigInvalidException the file is not a properly formatted configuration file.
  */
 @Override
 public void load() throws IOException, ConfigInvalidException {
   final FileSnapshot oldSnapshot = snapshot;
   final FileSnapshot newSnapshot = FileSnapshot.save(getFile());
   try {
     final byte[] in = IO.readFully(getFile());
     final ObjectId newHash = hash(in);
     if (hash.equals(newHash)) {
       if (oldSnapshot.equals(newSnapshot)) oldSnapshot.setClean(newSnapshot);
       else snapshot = newSnapshot;
     } else {
       final String decoded;
       if (in.length >= 3
           && in[0] == (byte) 0xEF
           && in[1] == (byte) 0xBB
           && in[2] == (byte) 0xBF) {
         decoded = RawParseUtils.decode(RawParseUtils.UTF8_CHARSET, in, 3, in.length);
         utf8Bom = true;
       } else {
         decoded = RawParseUtils.decode(in);
       }
       fromText(decoded);
       snapshot = newSnapshot;
       hash = newHash;
     }
   } catch (FileNotFoundException noFile) {
     clear();
     snapshot = newSnapshot;
   } catch (IOException e) {
     final IOException e2 =
         new IOException(MessageFormat.format(JGitText.get().cannotReadFile, getFile()));
     e2.initCause(e);
     throw e2;
   } catch (ConfigInvalidException e) {
     throw new ConfigInvalidException(
         MessageFormat.format(JGitText.get().cannotReadFile, getFile()), e);
   }
 }
Пример #6
0
  LooseRef scanRef(LooseRef ref, String name) throws IOException {
    final File path = fileFor(name);
    FileSnapshot currentSnapshot = null;

    if (ref != null) {
      currentSnapshot = ref.getSnapShot();
      if (!currentSnapshot.isModified(path)) return ref;
      name = ref.getName();
    }

    final int limit = 4096;
    final byte[] buf;
    FileSnapshot otherSnapshot = FileSnapshot.save(path);
    try {
      buf = IO.readSome(path, limit);
    } catch (FileNotFoundException noFile) {
      if (path.exists() && path.isFile()) {
        throw noFile;
      }
      return null; // doesn't exist or no file; not a reference.
    }

    int n = buf.length;
    if (n == 0) return null; // empty file; not a reference.

    if (isSymRef(buf, n)) {
      if (n == limit) return null; // possibly truncated ref

      // trim trailing whitespace
      while (0 < n && Character.isWhitespace(buf[n - 1])) n--;
      if (n < 6) {
        String content = RawParseUtils.decode(buf, 0, n);
        throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content));
      }
      final String target = RawParseUtils.decode(buf, 5, n);
      if (ref != null && ref.isSymbolic() && ref.getTarget().getName().equals(target)) {
        assert (currentSnapshot != null);
        currentSnapshot.setClean(otherSnapshot);
        return ref;
      }
      return newSymbolicRef(otherSnapshot, name, target);
    }

    if (n < OBJECT_ID_STRING_LENGTH)
      return null; // impossibly short object identifier; not a reference.

    final ObjectId id;
    try {
      id = ObjectId.fromString(buf, 0);
      if (ref != null && !ref.isSymbolic() && id.equals(ref.getTarget().getObjectId())) {
        assert (currentSnapshot != null);
        currentSnapshot.setClean(otherSnapshot);
        return ref;
      }

    } catch (IllegalArgumentException notRef) {
      while (0 < n && Character.isWhitespace(buf[n - 1])) n--;
      String content = RawParseUtils.decode(buf, 0, n);

      IOException ioException =
          new IOException(MessageFormat.format(JGitText.get().notARef, name, content));
      ioException.initCause(notRef);
      throw ioException;
    }
    return new LooseUnpeeled(otherSnapshot, name, id);
  }
Пример #7
0
  void processEntry(TreeEntry h, TreeEntry m, Entry i) throws IOException {
    ObjectId iId = (i == null ? null : i.getObjectId());
    ObjectId mId = (m == null ? null : m.getId());
    ObjectId hId = (h == null ? null : h.getId());

    String name = (i != null ? i.getName() : (h != null ? h.getFullName() : m.getFullName()));

    if (i == null) {
      /*
      I (index)                H        M        Result
         -------------------------------------------------------
         0 nothing             nothing  nothing  (does not happen)
         1 nothing             nothing  exists   use M
         2 nothing             exists   nothing  remove path from index
         3 nothing             exists   exists   use M */

      if (h == null) {
        updated.put(name, mId);
      } else if (m == null) {
        removed.add(name);
      } else {
        updated.put(name, mId);
      }
    } else if (h == null) {
      /*
      clean I==H  I==M       H        M        Result
           -----------------------------------------------------
          4 yes   N/A   N/A     nothing  nothing  keep index
          5 no    N/A   N/A     nothing  nothing  keep index

          6 yes   N/A   yes     nothing  exists   keep index
          7 no    N/A   yes     nothing  exists   keep index
          8 yes   N/A   no      nothing  exists   fail
          9 no    N/A   no      nothing  exists   fail       */

      if (m == null || mId.equals(iId)) {
        if (hasParentBlob(merge, name)) {
          if (i.isModified(root, true)) {
            conflicts.add(name);
          } else {
            removed.add(name);
          }
        }
      } else {
        conflicts.add(name);
      }
    } else if (m == null) {
      /*
      10 yes   yes   N/A     exists   nothing  remove path from index
            11 no    yes   N/A     exists   nothing  fail
            12 yes   no    N/A     exists   nothing  fail
            13 no    no    N/A     exists   nothing  fail
       */

      if (hId.equals(iId)) {
        if (i.isModified(root, true)) {
          conflicts.add(name);
        } else {
          removed.add(name);
        }
      } else {
        conflicts.add(name);
      }
    } else {
      if (!hId.equals(mId) && !hId.equals(iId) && !mId.equals(iId)) {
        conflicts.add(name);
      } else if (hId.equals(iId) && !mId.equals(iId)) {
        if (i.isModified(root, true)) conflicts.add(name);
        else updated.put(name, mId);
      }
    }
  }
Пример #8
0
 private static RawText getRawText(ObjectId id, ObjectReader reader) throws IOException {
   if (id.equals(ObjectId.zeroId())) return new RawText(new byte[] {});
   return new RawText(reader.open(id, OBJ_BLOB).getCachedBytes());
 }
Пример #9
0
  /**
   * Executes the {@code commit} command with all the options and parameters collected by the setter
   * methods of this class. Each instance of this class should only be used for one invocation of
   * the command (means: one call to {@link #call()})
   *
   * @return a {@link RevCommit} object representing the successful commit.
   * @throws NoHeadException when called on a git repo without a HEAD reference
   * @throws NoMessageException when called without specifying a commit message
   * @throws UnmergedPathsException when the current index contained unmerged paths (conflicts)
   * @throws ConcurrentRefUpdateException when HEAD or branch ref is updated concurrently by someone
   *     else
   * @throws WrongRepositoryStateException when repository is not in the right state for committing
   * @throws AbortedByHookException if there are either pre-commit or commit-msg hooks present in
   *     the repository and one of them rejects the commit.
   */
  public RevCommit call()
      throws GitAPIException, NoHeadException, NoMessageException, UnmergedPathsException,
          ConcurrentRefUpdateException, WrongRepositoryStateException, AbortedByHookException {
    checkCallable();
    Collections.sort(only);

    try (RevWalk rw = new RevWalk(repo)) {
      RepositoryState state = repo.getRepositoryState();
      if (!state.canCommit())
        throw new WrongRepositoryStateException(
            MessageFormat.format(JGitText.get().cannotCommitOnARepoWithState, state.name()));

      if (!noVerify) {
        Hooks.preCommit(repo, hookOutRedirect.get(PreCommitHook.NAME)).call();
      }

      processOptions(state, rw);

      if (all && !repo.isBare()) {
        try (Git git = new Git(repo)) {
          git.add()
              .addFilepattern(".") // $NON-NLS-1$
              .setUpdate(true)
              .call();
        } catch (NoFilepatternException e) {
          // should really not happen
          throw new JGitInternalException(e.getMessage(), e);
        }
      }

      Ref head = repo.exactRef(Constants.HEAD);
      if (head == null)
        throw new NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);

      // determine the current HEAD and the commit it is referring to
      ObjectId headId = repo.resolve(Constants.HEAD + "^{commit}"); // $NON-NLS-1$
      if (headId == null && amend)
        throw new WrongRepositoryStateException(JGitText.get().commitAmendOnInitialNotPossible);

      if (headId != null)
        if (amend) {
          RevCommit previousCommit = rw.parseCommit(headId);
          for (RevCommit p : previousCommit.getParents()) parents.add(p.getId());
          if (author == null) author = previousCommit.getAuthorIdent();
        } else {
          parents.add(0, headId);
        }

      if (!noVerify) {
        message =
            Hooks.commitMsg(repo, hookOutRedirect.get(CommitMsgHook.NAME))
                .setCommitMessage(message)
                .call();
      }

      // lock the index
      DirCache index = repo.lockDirCache();
      try (ObjectInserter odi = repo.newObjectInserter()) {
        if (!only.isEmpty()) index = createTemporaryIndex(headId, index, rw);

        // Write the index as tree to the object database. This may
        // fail for example when the index contains unmerged paths
        // (unresolved conflicts)
        ObjectId indexTreeId = index.writeTree(odi);

        if (insertChangeId) insertChangeId(indexTreeId);

        // Check for empty commits
        if (headId != null && !allowEmpty.booleanValue()) {
          RevCommit headCommit = rw.parseCommit(headId);
          headCommit.getTree();
          if (indexTreeId.equals(headCommit.getTree())) {
            throw new EmtpyCommitException(JGitText.get().emptyCommit);
          }
        }

        // Create a Commit object, populate it and write it
        CommitBuilder commit = new CommitBuilder();
        commit.setCommitter(committer);
        commit.setAuthor(author);
        commit.setMessage(message);

        commit.setParentIds(parents);
        commit.setTreeId(indexTreeId);
        ObjectId commitId = odi.insert(commit);
        odi.flush();

        RevCommit revCommit = rw.parseCommit(commitId);
        RefUpdate ru = repo.updateRef(Constants.HEAD);
        ru.setNewObjectId(commitId);
        if (!useDefaultReflogMessage) {
          ru.setRefLogMessage(reflogComment, false);
        } else {
          String prefix =
              amend
                  ? "commit (amend): " //$NON-NLS-1$
                  : parents.size() == 0
                      ? "commit (initial): " //$NON-NLS-1$
                      : "commit: "; //$NON-NLS-1$
          ru.setRefLogMessage(prefix + revCommit.getShortMessage(), false);
        }
        if (headId != null) ru.setExpectedOldObjectId(headId);
        else ru.setExpectedOldObjectId(ObjectId.zeroId());
        Result rc = ru.forceUpdate();
        switch (rc) {
          case NEW:
          case FORCED:
          case FAST_FORWARD:
            {
              setCallable(false);
              if (state == RepositoryState.MERGING_RESOLVED || isMergeDuringRebase(state)) {
                // Commit was successful. Now delete the files
                // used for merge commits
                repo.writeMergeCommitMsg(null);
                repo.writeMergeHeads(null);
              } else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
                repo.writeMergeCommitMsg(null);
                repo.writeCherryPickHead(null);
              } else if (state == RepositoryState.REVERTING_RESOLVED) {
                repo.writeMergeCommitMsg(null);
                repo.writeRevertHead(null);
              }
              Hooks.postCommit(repo, hookOutRedirect.get(PostCommitHook.NAME)).call();

              return revCommit;
            }
          case REJECTED:
          case LOCK_FAILURE:
            throw new ConcurrentRefUpdateException(
                JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
          default:
            throw new JGitInternalException(
                MessageFormat.format(
                    JGitText.get().updatingRefFailed, Constants.HEAD, commitId.toString(), rc));
        }
      } finally {
        index.unlock();
      }
    } catch (UnmergedPathException e) {
      throw new UnmergedPathsException(e);
    } catch (IOException e) {
      throw new JGitInternalException(
          JGitText.get().exceptionCaughtDuringExecutionOfCommitCommand, e);
    }
  }
Пример #10
0
 /**
  * Compares whether two pairs of ObjectId and FileMode are equal.
  *
  * @param id1
  * @param mode1
  * @param id2
  * @param mode2
  * @return <code>true</code> if FileModes and ObjectIds are equal. <code>false</code> otherwise
  */
 private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2, FileMode mode2) {
   if (!mode1.equals(mode2)) return false;
   return id1 != null ? id1.equals(id2) : id2 == null;
 }