/** * 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; }
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"); } }
/** * 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(); } }
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; }
/** * 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); } }
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); }
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); } } }
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()); }
/** * 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); } }
/** * 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; }