public String prettyRevisionInfo(GitClient newgit, Revision r) {
    List<String> test3 = null;
    try {
      test3 = newgit.showRevision(r.getSha1());
    } catch (GitException e1) {
      return "";
    } catch (InterruptedException e1) {
      return "";
    }
    String[] authorDate = test3.get(3).split(">");
    String author = authorDate[0].replaceFirst("author ", "").replaceFirst("committer ", "") + ">";
    String goodDate = null;
    try {
      String totmp = authorDate[1].trim().split("\\+")[0].trim();
      long timestamp = Long.parseLong(totmp, 10) * 1000;
      Date date = new Date();
      date.setTime(timestamp);

      goodDate = new SimpleDateFormat("yyyy:MM:dd HH:mm").format(date);

    } catch (Exception e) {
      e.toString();
    }
    return r.getSha1String().substring(0, 8) + " " + goodDate + " " + author;
  }
Esempio n. 2
0
 public Revision getRevisionForSHA1(ObjectId sha1) throws GitException, IOException {
   for (Revision revision : getAllBranchRevisions()) {
     if (revision.getSha1().equals(sha1)) {
       return revision;
     }
   }
   return null;
 }
Esempio n. 3
0
 /**
  * Return the revision containing the branch name.
  *
  * @param branchName
  * @return
  * @throws IOException
  * @throws GitException
  */
 public Revision getRevisionContainingBranch(String branchName) throws GitException, IOException {
   for (Revision revision : getAllBranchRevisions()) {
     for (Branch b : revision.getBranches()) {
       if (b.getName().equals(branchName)) {
         return revision;
       }
     }
   }
   return null;
 }
Esempio n. 4
0
  @Override
  public Revision decorateRevisionToBuild(
      GitSCM scm, AbstractBuild<?, ?> build, GitClient git, BuildListener listener, Revision rev)
      throws IOException, InterruptedException {
    String remoteBranchRef =
        GitSCM.getParameterString(options.getRef(), build.getEnvironment(listener));

    // if the branch we are merging is already at the commit being built, the entire merge becomes
    // no-op
    // so there's nothing to do
    if (rev.containsBranchName(remoteBranchRef)) return rev;

    // Only merge if there's a branch to merge that isn't us..
    listener
        .getLogger()
        .println(
            "Merging "
                + rev
                + " onto "
                + remoteBranchRef
                + " using "
                + scm.getUserMergeOptions().getMergeStrategy().toString()
                + " strategy");

    // checkout origin/blah
    ObjectId target = git.revParse(remoteBranchRef);

    String paramLocalBranch = scm.getParamLocalBranch(build);
    git.checkoutBranch(paramLocalBranch, remoteBranchRef);

    try {
      MergeCommand cmd = git.merge().setRevisionToMerge(rev.getSha1());
      for (GitSCMExtension ext : scm.getExtensions())
        ext.decorateMergeCommand(scm, build, git, listener, cmd);
      cmd.execute();
    } catch (GitException ex) {
      // merge conflict. First, avoid leaving any conflict markers in the working tree
      // by checking out some known clean state. We don't really mind what commit this is,
      // since the next build is going to pick its own commit to build, but 'rev' is as good any.
      git.checkoutBranch(paramLocalBranch, rev.getSha1String());

      // record the fact that we've tried building 'rev' and it failed, or else
      // BuildChooser in future builds will pick up this same 'rev' again and we'll see the exact
      // same merge failure
      // all over again.
      scm.getBuildData(build).saveBuild(new Build(rev, build.getNumber(), FAILURE));
      throw new AbortException("Branch not suitable for integration as it does not merge cleanly");
    }

    build.addAction(new MergeRecord(remoteBranchRef, target.getName()));

    return new GitUtils(listener, git).getRevisionForSHA1(git.revParse(HEAD));
  }
Esempio n. 5
0
 /**
  * Return a list of "Revisions" - where a revision knows about all the branch names that refer to
  * a SHA1.
  *
  * @return
  * @throws IOException
  * @throws GitException
  */
 public Collection<Revision> getAllBranchRevisions() throws GitException, IOException {
   Map<ObjectId, Revision> revisions = new HashMap<ObjectId, Revision>();
   List<Branch> branches = git.getRemoteBranches();
   for (Branch b : branches) {
     Revision r = revisions.get(b.getSHA1());
     if (r == null) {
       r = new Revision(b.getSHA1());
       revisions.put(b.getSHA1(), r);
     }
     r.getBranches().add(b);
   }
   return revisions.values();
 }
  private Collection<Revision> getHeadRevision(
      boolean isPollCall, String singleBranch, GitClient git, TaskListener listener, BuildData data)
      throws InterruptedException {
    try {
      ObjectId sha1 = git.revParse(singleBranch);
      verbose(listener, "rev-parse {0} -> {1}", singleBranch, sha1);

      // if polling for changes don't select something that has
      // already been built as a build candidate
      if (isPollCall && data.hasBeenBuilt(sha1)) {
        verbose(listener, "{0} has already been built", sha1);
        return emptyList();
      }

      verbose(listener, "Found a new commit {0} to be built on {1}", sha1, singleBranch);
      Revision revision = new Revision(sha1);
      revision.getBranches().add(new Branch(singleBranch, sha1));
      return Collections.singletonList(revision);
      /*
      // calculate the revisions that are new compared to the last build
      List<Revision> candidateRevs = new ArrayList<Revision>();
      List<ObjectId> allRevs = git.revListAll(); // index 0 contains the newest revision
      if (data != null && allRevs != null) {
          Revision lastBuiltRev = data.getLastBuiltRevision();
          if (lastBuiltRev == null) {
              return Collections.singletonList(objectId2Revision(singleBranch, sha1));
          }
          int indexOfLastBuildRev = allRevs.indexOf(lastBuiltRev.getSha1());
          if (indexOfLastBuildRev == -1) {
              // mhmmm ... can happen when branches are switched.
              return Collections.singletonList(objectId2Revision(singleBranch, sha1));
          }
          List<ObjectId> newRevisionsSinceLastBuild = allRevs.subList(0, indexOfLastBuildRev);
          // translate list of ObjectIds into list of Revisions
          for (ObjectId objectId : newRevisionsSinceLastBuild) {
              candidateRevs.add(objectId2Revision(singleBranch, objectId));
          }
      }
      if (candidateRevs.isEmpty()) {
          return Collections.singletonList(objectId2Revision(singleBranch, sha1));
      }
      return candidateRevs;
      */
    } catch (GitException e) {
      // branch does not exist, there is nothing to build
      verbose(listener, "Failed to rev-parse: {0}", singleBranch);
      return emptyList();
    }
  }
Esempio n. 7
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;
  }
  /**
   * Determines which Revisions to build.
   *
   * <p>If only one branch is chosen and only one repository is listed, then just attempt to find
   * the latest revision number for the chosen branch.
   *
   * <p>If multiple branches are selected or the branches include wildcards, then use the advanced
   * usecase as defined in the getAdvancedCandidateRevisons method.
   *
   * @throws IOException
   * @throws GitException
   */
  @Override
  public Collection<Revision> getCandidateRevisions(
      boolean isPollCall,
      String singleBranch,
      GitClient git,
      TaskListener listener,
      BuildData data,
      BuildChooserContext context)
      throws GitException, IOException, InterruptedException {

    verbose(
        listener,
        "getCandidateRevisions({0},{1},,,{2}) considering branches to build",
        isPollCall,
        singleBranch,
        data);

    // if the branch name contains more wildcards then the simple usecase
    // does not apply and we need to skip to the advanced usecase
    if (singleBranch == null || singleBranch.contains("*"))
      return getAdvancedCandidateRevisions(
          isPollCall, listener, new GitUtils(listener, git), data, context);

    // check if we're trying to build a specific commit
    // this only makes sense for a build, there is no
    // reason to poll for a commit
    if (!isPollCall && singleBranch.matches("[0-9a-f]{6,40}")) {
      try {
        ObjectId sha1 = git.revParse(singleBranch);
        Revision revision = new Revision(sha1);
        revision.getBranches().add(new Branch("detached", sha1));
        verbose(listener, "Will build the detached SHA1 {0}", sha1);
        return Collections.singletonList(revision);
      } catch (GitException e) {
        // revision does not exist, may still be a branch
        // for example a branch called "badface" would show up here
        verbose(listener, "Not a valid SHA1 {0}", singleBranch);
      }
    }

    Collection<Revision> revisions = new ArrayList<Revision>();

    // if it doesn't contain '/' then it could be either a tag or an unqualified branch
    if (!singleBranch.contains("/")) {
      // the 'branch' could actually be a tag:
      Set<String> tags = git.getTagNames(singleBranch);
      if (tags.size() != 0) {
        verbose(listener, "{0} is a tag");
        return getHeadRevision(isPollCall, singleBranch, git, listener, data);
      }

      // <tt>BRANCH</tt> is recognized as a shorthand of <tt>*/BRANCH</tt>
      // so check all remotes to fully qualify this branch spec
      for (RemoteConfig config : gitSCM.getRepositories()) {
        String repository = config.getName();
        String fqbn = repository + "/" + singleBranch;
        verbose(
            listener,
            "Qualifying {0} as a branch in repository {1} -> {2}",
            singleBranch,
            repository,
            fqbn);
        revisions.addAll(getHeadRevision(isPollCall, fqbn, git, listener, data));
      }
    } else {
      // either the branch is qualified (first part should match a valid remote)
      // or it is still unqualified, but the branch name contains a '/'
      List<String> possibleQualifiedBranches = new ArrayList<String>();
      boolean singleBranchIsQualified = false;
      for (RemoteConfig config : gitSCM.getRepositories()) {
        String repository = config.getName();
        if (singleBranch.startsWith(repository + "/")) {
          singleBranchIsQualified = true;
          break;
        }
        String fqbn = repository + "/" + singleBranch;
        verbose(
            listener,
            "Qualifying {0} as a branch in repository {1} -> {2}",
            singleBranch,
            repository,
            fqbn);
        possibleQualifiedBranches.add(fqbn);
      }
      if (singleBranchIsQualified) {
        revisions.addAll(getHeadRevision(isPollCall, singleBranch, git, listener, data));
      } else {
        for (String fqbn : possibleQualifiedBranches) {
          revisions.addAll(getHeadRevision(isPollCall, fqbn, git, listener, data));
        }
      }
    }

    return revisions;
  }
  /**
   * In order to determine which Revisions to build.
   *
   * <p>Does the following : 1. Find all the branch revisions 2. Filter out branches that we don't
   * care about from the revisions. Any Revisions with no interesting branches are dropped. 3. Get
   * rid of any revisions that are wholly subsumed by another revision we're considering. 4. Get rid
   * of any revisions that we've already built. 5. Sort revisions from old to new.
   *
   * <p>NB: Alternate BuildChooser implementations are possible - this may be beneficial if "only 1"
   * branch is to be built, as much of this work is irrelevant in that usecase.
   *
   * @throws IOException
   * @throws GitException
   */
  private List<Revision> getAdvancedCandidateRevisions(
      boolean isPollCall,
      TaskListener listener,
      GitUtils utils,
      BuildData data,
      BuildChooserContext context)
      throws GitException, IOException, InterruptedException {
    EnvVars env = context.getBuild().getEnvironment();

    // 1. Get all the (branch) revisions that exist
    List<Revision> revs = new ArrayList<Revision>(utils.getAllBranchRevisions());
    verbose(listener, "Starting with all the branches: {0}", revs);

    // 2. Filter out any revisions that don't contain any branches that we
    // actually care about (spec)
    for (Iterator<Revision> i = revs.iterator(); i.hasNext(); ) {
      Revision r = i.next();

      // filter out uninteresting branches
      for (Iterator<Branch> j = r.getBranches().iterator(); j.hasNext(); ) {
        Branch b = j.next();
        boolean keep = false;
        for (BranchSpec bspec : gitSCM.getBranches()) {
          if (bspec.matches(b.getName(), env)) {
            keep = true;
            break;
          }
        }

        if (!keep) {
          verbose(listener, "Ignoring {0} because it doesn''t match branch specifier", b);
          j.remove();
        }
      }

      // filter out HEAD ref if it's not the only ref
      if (r.getBranches().size() > 1) {
        for (Iterator<Branch> j = r.getBranches().iterator(); j.hasNext(); ) {
          Branch b = j.next();
          if (HEAD.matches(b.getName(), env)) {
            verbose(
                listener,
                "Ignoring {0} because there''s named branch for this revision",
                b.getName());
            j.remove();
          }
        }
      }

      if (r.getBranches().size() == 0) {
        verbose(
            listener,
            "Ignoring {0} because we don''t care about any of the branches that point to it",
            r);
        i.remove();
      }
    }

    verbose(listener, "After branch filtering: {0}", revs);

    // 3. We only want 'tip' revisions
    revs = utils.filterTipBranches(revs);
    verbose(listener, "After non-tip filtering: {0}", revs);

    // 4. Finally, remove any revisions that have already been built.
    verbose(listener, "Removing what''s already been built: {0}", data.getBuildsByBranchName());
    Revision lastBuiltRevision = data.getLastBuiltRevision();
    for (Iterator<Revision> i = revs.iterator(); i.hasNext(); ) {
      Revision r = i.next();

      if (data.hasBeenBuilt(r.getSha1())) {
        i.remove();

        // keep track of new branches pointing to the last built revision
        if (lastBuiltRevision != null && lastBuiltRevision.getSha1().equals(r.getSha1())) {
          lastBuiltRevision = r;
        }
      }
    }
    verbose(listener, "After filtering out what''s already been built: {0}", revs);

    // if we're trying to run a build (not an SCM poll) and nothing new
    // was found then just run the last build again but ensure that the branch list
    // is ordered according to the configuration. Sorting the branch list ensures
    // a deterministic value for GIT_BRANCH and allows a git-flow style workflow
    // with fast-forward merges between branches
    if (!isPollCall && revs.isEmpty() && lastBuiltRevision != null) {
      verbose(
          listener,
          "Nothing seems worth building, so falling back to the previously built revision: {0}",
          data.getLastBuiltRevision());
      return Collections.singletonList(
          utils.sortBranchesForRevision(lastBuiltRevision, gitSCM.getBranches(), env));
    }

    // 5. sort them by the date of commit, old to new
    // this ensures the fairness in scheduling.
    final List<Revision> in = revs;
    return utils.git.withRepository(
        new RepositoryCallback<List<Revision>>() {
          public List<Revision> invoke(Repository repo, VirtualChannel channel)
              throws IOException, InterruptedException {
            Collections.sort(in, new CommitTimeComparator(repo));
            return in;
          }
        });
  }
 private Revision objectId2Revision(String singleBranch, ObjectId sha1) {
   Revision revision = new Revision(sha1);
   revision.getBranches().add(new Branch(singleBranch, sha1));
   return revision;
 }
  public Map<String, String> generateContents(AbstractProject<?, ?> project, GitSCM git)
      throws IOException, InterruptedException {

    Map<String, String> paramList = new LinkedHashMap<String, String>();
    // for (AbstractProject<?,?> project :
    // Hudson.getInstance().getItems(AbstractProject.class)) {
    if (project.getSomeWorkspace() == null) {
      this.errorMessage = "noWorkspace";
    }

    EnvVars environment = null;

    try {
      environment = project.getSomeBuildWithWorkspace().getEnvironment(TaskListener.NULL);
    } catch (Exception e) {
    }

    for (RemoteConfig repository : git.getRepositories()) {
      LOGGER.log(
          Level.INFO,
          "generateContents contenttype " + type + " RemoteConfig " + repository.getURIs());
      for (URIish remoteURL : repository.getURIs()) {
        GitClient newgit =
            git.createClient(
                TaskListener.NULL, environment, new Run(project) {}, project.getSomeWorkspace());
        FilePath wsDir = null;
        if (project.getSomeBuildWithWorkspace() != null) {
          wsDir = project.getSomeBuildWithWorkspace().getWorkspace();
          if (wsDir == null || !wsDir.exists()) {
            LOGGER.log(
                Level.WARNING, "generateContents create wsDir " + wsDir + " for " + remoteURL);
            wsDir.mkdirs();
            if (!wsDir.exists()) {
              LOGGER.log(Level.SEVERE, "generateContents wsDir.mkdirs() failed.");
              String errMsg = "!Failed To Create Workspace";
              return Collections.singletonMap(errMsg, errMsg);
            }
            newgit.init();
            newgit.clone(remoteURL.toASCIIString(), "origin", false, null);
            LOGGER.log(Level.INFO, "generateContents clone done");
          }
        } else {
          // probably our first build. We cannot yet fill in any
          // values.
          LOGGER.log(Level.INFO, "getSomeBuildWithWorkspace is null");
          String errMsg = "!No workspace. Please build the project at least once";
          return Collections.singletonMap(errMsg, errMsg);
        }

        long time = -System.currentTimeMillis();
        FetchCommand fetch = newgit.fetch_().from(remoteURL, repository.getFetchRefSpecs());
        fetch.execute();
        LOGGER.finest("Took " + (time + System.currentTimeMillis()) + "ms to fetch");
        if (type.equalsIgnoreCase(PARAMETER_TYPE_REVISION)) {
          List<ObjectId> oid;

          if (this.branch != null && !this.branch.isEmpty()) {
            oid = newgit.revList(this.branch);
          } else {
            oid = newgit.revListAll();
          }

          for (ObjectId noid : oid) {
            Revision r = new Revision(noid);
            paramList.put(r.getSha1String(), prettyRevisionInfo(newgit, r));
          }
        }
        if (type.equalsIgnoreCase(PARAMETER_TYPE_TAG)
            || type.equalsIgnoreCase(PARAMETER_TYPE_TAG_BRANCH)) {

          Set<String> tagSet = newgit.getTagNames(tagFilter);
          ArrayList<String> orderedTagNames;

          if (this.getSortMode().getIsSorting()) {
            orderedTagNames = sortByName(tagSet);
            if (this.getSortMode().getIsDescending()) Collections.reverse(orderedTagNames);
          } else {
            orderedTagNames = new ArrayList<String>(tagSet);
          }

          for (String tagName : orderedTagNames) {
            paramList.put(tagName, tagName);
          }
        }
        if (type.equalsIgnoreCase(PARAMETER_TYPE_BRANCH)
            || type.equalsIgnoreCase(PARAMETER_TYPE_TAG_BRANCH)) {
          time = -System.currentTimeMillis();
          Set<String> branchSet = new HashSet<String>();
          final boolean wildcard = "*".equals(branchfilter);
          for (Branch branch : newgit.getRemoteBranches()) {
            // It'd be nice if we could filter on remote branches via the GitClient,
            // but that's not an option.
            final String branchName = branch.getName();
            if (wildcard || branchName.matches(branchfilter)) {
              branchSet.add(branchName);
            }
          }
          LOGGER.finest("Took " + (time + System.currentTimeMillis()) + "ms to fetch branches");

          time = -System.currentTimeMillis();
          List<String> orderedBranchNames;
          if (this.getSortMode().getIsSorting()) {
            orderedBranchNames = sortByName(branchSet);
            if (this.getSortMode().getIsDescending()) Collections.reverse(orderedBranchNames);
          } else {
            orderedBranchNames = new ArrayList<String>(branchSet);
          }

          for (String branchName : orderedBranchNames) {
            paramList.put(branchName, branchName);
          }
          LOGGER.finest(
              "Took " + (time + System.currentTimeMillis()) + "ms to sort and add to param list.");
        }
      }
      break;
    }
    return paramList;
  }
 @Override
 protected void before() throws Throwable {
   when(data.getLastBuiltRevision()).thenReturn(rev);
   data.lastBuild = new hudson.plugins.git.util.Build(rev, rev, 0, Result.SUCCESS);
   when(rev.getSha1()).thenReturn(ObjectId.fromString(SOME_SHA));
 }