| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * 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 git4idea; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vcs.VcsException; |
| import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber; |
| import com.intellij.openapi.vcs.history.VcsRevisionNumber; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import git4idea.commands.GitCommand; |
| import git4idea.commands.GitSimpleHandler; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.Date; |
| import java.util.StringTokenizer; |
| |
| /** |
| * Git revision number |
| */ |
| public class GitRevisionNumber implements ShortVcsRevisionNumber { |
| /** |
| * the hash from 40 zeros representing not yet created commit |
| */ |
| public static final String NOT_COMMITTED_HASH = StringUtil.repeat("0", 40); |
| |
| /** |
| * the revision number (40 character hashcode, tag, or reference). In some cases incomplete hashcode could be used. |
| */ |
| @NotNull private final String myRevisionHash; |
| /** |
| * the date when revision created |
| */ |
| @NotNull private final Date myTimestamp; |
| |
| private static final Logger LOG = Logger.getInstance(GitRevisionNumber.class); |
| |
| /** |
| * A constructor from version. The current date is used. |
| * |
| * @param version the version number. |
| */ |
| public GitRevisionNumber(@NonNls @NotNull String version) { |
| // TODO review usages |
| myRevisionHash = version; |
| myTimestamp = new Date(); |
| } |
| |
| /** |
| * A constructor from version and time |
| * |
| * @param version the version number |
| * @param timeStamp the time when the version has been created |
| */ |
| public GitRevisionNumber(@NotNull String version, @NotNull Date timeStamp) { |
| myTimestamp = timeStamp; |
| myRevisionHash = version; |
| } |
| |
| @NotNull |
| public String asString() { |
| return myRevisionHash; |
| } |
| |
| @Override |
| public String toShortString() { |
| return asString().substring(0, 7); |
| } |
| |
| /** |
| * @return revision time |
| */ |
| @NotNull |
| public Date getTimestamp() { |
| return myTimestamp; |
| } |
| |
| /** |
| * @return revision number |
| */ |
| @NotNull |
| public String getRev() { |
| return myRevisionHash; |
| } |
| |
| /** |
| * @return the short revision number. The revision number likely unambiguously identify local revision, however in rare cases there could be conflicts. |
| */ |
| @NotNull |
| public String getShortRev() { |
| return GitUtil.getShortHash(myRevisionHash); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public int compareTo(VcsRevisionNumber crev) { |
| if (this == crev) return 0; |
| |
| if (crev instanceof GitRevisionNumber) { |
| GitRevisionNumber other = (GitRevisionNumber)crev; |
| if ((other.myRevisionHash != null) && myRevisionHash.equals(other.myRevisionHash)) { |
| return 0; |
| } |
| |
| if ((other.myRevisionHash.indexOf("[") > 0) && (other.myTimestamp != null)) { |
| return myTimestamp.compareTo(other.myTimestamp); |
| } |
| |
| // check for parent revs |
| String otherName = null; |
| String thisName = null; |
| int otherParents = -1; |
| int thisParent = -1; |
| |
| if (other.myRevisionHash.contains("~")) { |
| int tildeIndex = other.myRevisionHash.indexOf('~'); |
| otherName = other.myRevisionHash.substring(0, tildeIndex); |
| otherParents = Integer.parseInt(other.myRevisionHash.substring(tildeIndex)); |
| } |
| |
| if (myRevisionHash.contains("~")) { |
| int tildeIndex = myRevisionHash.indexOf('~'); |
| thisName = myRevisionHash.substring(0, tildeIndex); |
| thisParent = Integer.parseInt(myRevisionHash.substring(tildeIndex)); |
| } |
| |
| if (otherName == null && thisName == null) { |
| final int result = myTimestamp.compareTo(other.myTimestamp); |
| if (result == 0) { |
| // it can NOT be 0 - it would mean that revisions are equal but they have different hash codes |
| // but this is NOT correct. but we don't know here how to sort |
| return myRevisionHash.compareTo(other.myRevisionHash); |
| } |
| return result; |
| } |
| else if (otherName == null) { |
| return 1; // I am an ancestor of the compared revision |
| } |
| else if (thisName == null) { |
| return -1; // the compared revision is my ancestor |
| } |
| else { |
| return thisParent - otherParents; // higher relative rev numbers are older ancestors |
| } |
| } |
| |
| return -1; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) return true; |
| if ((obj == null) || (obj.getClass() != getClass())) return false; |
| |
| GitRevisionNumber test = (GitRevisionNumber)obj; |
| // TODO normalize revision string? |
| return myRevisionHash.equals(test.myRevisionHash); |
| } |
| |
| @Override |
| public int hashCode() { |
| return myRevisionHash.hashCode(); |
| } |
| |
| /** |
| * @return a revision string that refers to the parent revision relatively |
| * to the current one. The git operator "~" is used. Note that in case of merges, |
| * the first revision of several will referred. |
| */ |
| public String getParentRevisionStr() { |
| String rev = myRevisionHash; |
| int bracketIdx = rev.indexOf("["); |
| if (bracketIdx > 0) { |
| rev = myRevisionHash.substring(bracketIdx + 1, myRevisionHash.indexOf("]")); |
| } |
| |
| int tildeIndex = rev.indexOf("~"); |
| if (tildeIndex > 0) { |
| int n = Integer.parseInt(rev.substring(tildeIndex)) + 1; |
| return rev.substring(0, tildeIndex) + "~" + n; |
| } |
| return rev + "~1"; |
| } |
| |
| /** |
| * Resolve revision number for the specified revision |
| * |
| * @param project a project |
| * @param vcsRoot a vcs root |
| * @param rev a revision expression |
| * @return a resolved revision number with correct time |
| * @throws VcsException if there is a problem with running git |
| */ |
| public static GitRevisionNumber resolve(Project project, VirtualFile vcsRoot, @NonNls String rev) throws VcsException { |
| GitSimpleHandler h = new GitSimpleHandler(project, vcsRoot, GitCommand.REV_LIST); |
| h.setSilent(true); |
| h.addParameters("--timestamp", "--max-count=1", rev); |
| h.endOptions(); |
| final String output = h.run(); |
| return parseRevlistOutputAsRevisionNumber(h, output); |
| } |
| |
| @NotNull |
| public static GitRevisionNumber parseRevlistOutputAsRevisionNumber(@NotNull GitSimpleHandler h, @NotNull String output) { |
| StringTokenizer tokenizer = new StringTokenizer(output, "\n\r \t", false); |
| LOG.assertTrue(tokenizer.hasMoreTokens(), "No required tokens in the output: \n" + output); |
| Date timestamp = GitUtil.parseTimestampWithNFEReport(tokenizer.nextToken(), h, output); |
| return new GitRevisionNumber(tokenizer.nextToken(), timestamp); |
| } |
| |
| @Override |
| public String toString() { |
| return myRevisionHash; |
| } |
| } |