Java tutorial
/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.uberfire.java.nio.fs.jgit.util.commands; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.ketch.Proposal; import org.eclipse.jgit.internal.storage.reftree.Command; import org.eclipse.jgit.internal.storage.reftree.RefTree; import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase; import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.SymbolicRef; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; import org.uberfire.java.nio.fs.jgit.util.Git; import org.uberfire.java.nio.fs.jgit.util.exceptions.GitException; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.eclipse.jgit.internal.ketch.Proposal.State.QUEUED; import static org.eclipse.jgit.lib.Constants.HEAD; import static org.eclipse.jgit.lib.Constants.MASTER; import static org.eclipse.jgit.lib.Ref.Storage.NETWORK; import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED; import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON; public class RefTreeUpdateCommand { private final Git git; private final String name; private final RevCommit commit; public RefTreeUpdateCommand(final Git git, final String branchName, final RevCommit commit) { this.git = git; this.name = branchName; this.commit = commit; } public void execute() throws java.io.IOException, ConcurrentRefUpdateException { update(git.getRepository(), Constants.R_HEADS + name, commit); //this `initialization` aims to be temporary // -> without this cgit can't find master when cloning repos if (name.equals(MASTER) && !git.isHEADInitialized()) { synchronized (git.getRepository()) { symRef(git, HEAD, Constants.R_HEADS + name); git.setHeadAsInitialized(); } } } private void symRef(final Git git, final String name, final String dst) throws java.io.IOException { commit(git.getRepository(), null, (reader, tree) -> { Ref old = tree.exactRef(reader, name); Ref newx = tree.exactRef(reader, dst); final Command n; if (newx != null) { n = new Command(old, new SymbolicRef(name, newx)); } else { n = new Command(old, new SymbolicRef(name, new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null))); } return tree.apply(Collections.singleton(n)); }); } private void update(final Repository _repo, final String _name, final RevCommit _commit) throws IOException { commit(_repo, _commit, (reader, refTree) -> { final Ref old = refTree.exactRef(reader, _name); final List<Command> n = new ArrayList<>(1); try (RevWalk rw = new RevWalk(_repo)) { n.add(new Command(old, toRef(rw, _commit, _name, true))); if (git.isKetchEnabled()) { proposeKetch(n, _commit); } } catch (final IOException | InterruptedException e) { String msg = JGitText.get().transactionAborted; for (Command cmd : n) { if (cmd.getResult() == NOT_ATTEMPTED) { cmd.setResult(REJECTED_OTHER_REASON, msg); } } throw new GitException("Error"); //log.error(msg, e); } return refTree.apply(n); }); } private void proposeKetch(final List<Command> n, final RevCommit _commit) throws IOException, InterruptedException { final Proposal proposal = new Proposal(n).setAuthor(_commit.getAuthorIdent()).setMessage("push"); git.getKetchLeader().queueProposal(proposal); if (proposal.isDone()) { // This failed fast, e.g. conflict or bad precondition. throw new GitException("Error"); } if (proposal.getState() == QUEUED) { waitForQueue(proposal); } if (!proposal.isDone()) { waitForPropose(proposal); } } private void waitForQueue(final Proposal proposal) throws InterruptedException { while (!proposal.awaitStateChange(QUEUED, 250, MILLISECONDS)) { System.out.println("waiting queue..."); } switch (proposal.getState()) { case RUNNING: default: break; case EXECUTED: break; case ABORTED: break; } } private void waitForPropose(final Proposal proposal) throws InterruptedException { while (!proposal.await(250, MILLISECONDS)) { System.out.println("waiting propose..."); } } private static Ref toRef(final RevWalk rw, final ObjectId id, final String name, final boolean mustExist) throws IOException { if (ObjectId.zeroId().equals(id)) { return null; } try { RevObject o = rw.parseAny(id); if (o instanceof RevTag) { RevObject p = rw.peel(o); return new ObjectIdRef.PeeledTag(NETWORK, name, id, p.copy()); } return new ObjectIdRef.PeeledNonTag(NETWORK, name, id); } catch (MissingObjectException e) { if (mustExist) { throw e; } return new ObjectIdRef.Unpeeled(NETWORK, name, id); } } interface BiFunction { boolean apply(final ObjectReader reader, final RefTree refTree) throws IOException; } private void commit(final Repository repo, final RevCommit original, final BiFunction fun) throws IOException { try (final ObjectReader reader = repo.newObjectReader(); final ObjectInserter inserter = repo.newObjectInserter(); final RevWalk rw = new RevWalk(reader)) { final RefTreeDatabase refdb = (RefTreeDatabase) repo.getRefDatabase(); final RefDatabase bootstrap = refdb.getBootstrap(); final RefUpdate refUpdate = bootstrap.newUpdate(refdb.getTxnCommitted(), false); final CommitBuilder cb = new CommitBuilder(); final Ref ref = bootstrap.exactRef(refdb.getTxnCommitted()); final RefTree tree; if (ref != null && ref.getObjectId() != null) { tree = RefTree.read(reader, rw.parseTree(ref.getObjectId())); cb.setParentId(ref.getObjectId()); refUpdate.setExpectedOldObjectId(ref.getObjectId()); } else { tree = RefTree.newEmptyTree(); refUpdate.setExpectedOldObjectId(ObjectId.zeroId()); } if (fun.apply(reader, tree)) { final Ref ref2 = bootstrap.exactRef(refdb.getTxnCommitted()); if (ref2 == null || ref2.getObjectId().equals(ref != null ? ref.getObjectId() : null)) { cb.setTreeId(tree.writeTree(inserter)); if (original != null) { cb.setAuthor(original.getAuthorIdent()); cb.setCommitter(original.getAuthorIdent()); } else { final PersonIdent personIdent = new PersonIdent("user", "user@example.com"); cb.setAuthor(personIdent); cb.setCommitter(personIdent); } refUpdate.setNewObjectId(inserter.insert(cb)); inserter.flush(); final RefUpdate.Result result = refUpdate.update(rw); switch (result) { case NEW: case FAST_FORWARD: break; default: throw new RuntimeException( repo.getDirectory() + " -> " + result.toString() + " : " + refUpdate.getName()); } final File commited = new File(repo.getDirectory(), refdb.getTxnCommitted()); final File accepted = new File(repo.getDirectory(), refdb.getTxnNamespace() + "accepted"); Files.copy(commited.toPath(), accepted.toPath(), StandardCopyOption.REPLACE_EXISTING); } } } } }