a0472b6a97a9b2a389fa22d38dd23baf82f35f66
[teamcity/git-plugin.git] / git-common / src / jetbrains / buildServer / buildTriggers / vcs / git / GitUtils.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package jetbrains.buildServer.buildTriggers.vcs.git;
18
19 import com.intellij.execution.configurations.GeneralCommandLine;
20 import com.intellij.openapi.util.SystemInfo;
21 import jetbrains.buildServer.ExecResult;
22 import jetbrains.buildServer.SimpleCommandLineProcessRunner;
23 import jetbrains.buildServer.util.FileUtil;
24 import jetbrains.buildServer.vcs.VcsException;
25 import jetbrains.buildServer.vcs.VcsRoot;
26 import jetbrains.buildServer.vcs.VcsUtil;
27 import org.eclipse.jgit.lib.Constants;
28 import org.eclipse.jgit.lib.ObjectId;
29 import org.eclipse.jgit.lib.Ref;
30 import org.eclipse.jgit.transport.URIish;
31 import org.jetbrains.annotations.NotNull;
32
33 import java.io.File;
34 import java.io.IOException;
35 import java.util.Comparator;
36 import java.util.List;
37
38 /**
39  * Commands that allows working with git repositories
40  */
41 public class GitUtils {
42   /**
43    * Convert remote URL to JGIT form
44    *
45    * @param file the file to convert
46    * @return the file URL recognized by JGit
47    */
48   public static String toURL(File file) {
49     return "file:///" + file.getAbsolutePath().replace(File.separatorChar, '/');
50   }
51
52   /**
53    * The version comparator
54    */
55   public static final Comparator<String> VERSION_COMPARATOR = new Comparator<String>() {
56     public int compare(String o1, String o2) {
57       return o1.compareTo(o2);
58     }
59   };
60
61   /**
62    * Make version string from revision hash and time
63    *
64    * @param revision the revision hash
65    * @param time     the time of revision
66    * @return the version string
67    */
68   @NotNull
69   public static String makeVersion(@NotNull String revision, long time) {
70     return revision + "@" + Long.toHexString(time);
71   }
72
73   /**
74    * Extract revision number from the version
75    *
76    * @param version string
77    * @return the revision number
78    */
79   @NotNull
80   public static String versionRevision(@NotNull String version) {
81     int i = version.indexOf('@');
82     if (i == -1)
83       return version;
84     return version.substring(0, i);
85   }
86
87   /**
88    * Expands ref name to full ref name
89    *
90    * @param ref the ref name
91    * @return full ref name
92    */
93   public static String expandRef(String ref) {
94     if (ref.startsWith("refs/")) {
95       return ref;
96     } else {
97       return "refs/heads/" + ref;
98     }
99   }
100
101   public static String getShortBranchName(@NotNull String fullRefName) {
102     if (isRegularBranch(fullRefName))
103       return fullRefName.substring(Constants.R_HEADS.length());
104     return fullRefName;
105   }
106
107   public static boolean isTag(@NotNull Ref ref) {
108     return isTag(ref.getName());
109   }
110
111   public static boolean isTag(@NotNull String fullRefName) {
112     return fullRefName.startsWith(Constants.R_TAGS);
113   }
114
115   public static boolean isRegularBranch(@NotNull String fullRefName) {
116     return fullRefName.startsWith(Constants.R_HEADS);
117   }
118
119   /**
120    * Creates remote ref from local ref name for remote called 'origin'
121    *
122    * @param ref local ref name
123    * @return full remote ref name
124    */
125   public static String createRemoteRef(String ref) {
126     if (ref.startsWith("refs/")) {
127       if (ref.startsWith("refs/heads/")) {
128         return "refs/remotes/origin/" + ref.substring("refs/heads/".length());
129       } else {
130         return ref;
131       }
132     } else {
133       return "refs/remotes/origin/" + ref;
134     }
135   }
136
137   /**
138    * Convert Git path to a relative File
139    *
140    * @param path the path to covert
141    * @return the {@link File} object
142    */
143   public static File toFile(String path) {
144     return new File(path.replace('/', File.separatorChar));
145   }
146
147   /**
148    * Normalize path removing ".." and "." elements assuming "/" as separator
149    *
150    * @param path the path to normalize
151    * @return the normalized path
152    */
153   public static String normalizePath(String path) {
154     if (path.length() == 0 || path.equals("/")) {
155       return path;
156     }
157     StringBuilder rc = new StringBuilder();
158     String[] pc = path.split("/");
159     int count = 0;
160     int startBacks = 0;
161     int[] pci = new int[pc.length];
162     boolean startsWithSlash = path.charAt(0) == '/';
163     for (int i = 0; i < pc.length; i++) {
164       String f = pc[i];
165       if (f.length() == 0 || ".".equals(f)) {
166         // do nothing
167       } else if ("..".equals(f)) {
168         if (count == 0) {
169           startBacks++;
170         } else {
171           count--;
172         }
173       } else {
174         pci[count++] = i;
175       }
176     }
177     for (int i = 0; i < startBacks; i++) {
178       if (rc.length() != 0 || startsWithSlash) {
179         rc.append('/');
180       }
181       rc.append("..");
182     }
183     for (int i = 0; i < count; i++) {
184       int fi = pci[i];
185       if (rc.length() != 0 || startsWithSlash) {
186         rc.append('/');
187       }
188       rc.append(pc[fi]);
189     }
190     return rc.toString();
191   }
192
193
194   public static boolean isAnonymousGitWithUsername(@NotNull URIish uri) {
195     return "git".equals(uri.getScheme()) && uri.getUser() != null;
196   }
197
198   /**
199    * Returns build parameter name with the vcs branch name for given
200    * git VCS root
201    */
202   public static String getGitRootBranchParamName(@NotNull VcsRoot root) {
203     return jetbrains.buildServer.buildTriggers.vcs.git.Constants.GIT_ROOT_BUILD_BRANCH_PREFIX + VcsUtil.getSimplifiedName(root);
204   }
205
206
207   public static File getGitDir(@NotNull File workingTreeDir) throws IOException, VcsException {
208     File dotGit = new File(workingTreeDir, ".git");
209     if (dotGit.isFile()) {
210       List<String> content = FileUtil.readFile(dotGit);
211       if (content.isEmpty())
212         throw new VcsException("Empty " + dotGit.getAbsolutePath());
213       String line = content.get(0);
214       if (!line.startsWith("gitdir:"))
215         throw new VcsException("Wrong format of " + dotGit.getAbsolutePath() + ": " + content);
216       String gitDirPath = line.substring("gitdir:".length()).trim();
217       File gitDir = new File(gitDirPath);
218       if (gitDir.isAbsolute())
219         return gitDir;
220       return new File(workingTreeDir, gitDirPath);
221     }
222     return dotGit;
223   }
224
225   @NotNull
226   public static String getRevision(@NotNull final Ref ref) {
227     return getObjectId(ref).name();
228   }
229
230   @NotNull
231   public static ObjectId getObjectId(@NotNull final Ref ref) {
232     if (isTag(ref) && ref.getPeeledObjectId() != null)
233       return ref.getPeeledObjectId();
234     return ref.getObjectId();
235   }
236
237
238   /**
239    * Returns short file name for the given file on windows. The file must exist.
240    * @param f file of interest
241    * @return see above
242    * @throws UnsupportedOperationException if called on non windows platform
243    * @throws IOException in case of IO error
244    */
245   public static String getShortFileName(@NotNull File f) throws UnsupportedOperationException, IOException {
246     if (!SystemInfo.isWindows)
247       throw new UnsupportedOperationException();
248
249     GeneralCommandLine cmd = new GeneralCommandLine();
250     cmd.setExePath("cmd.exe");
251     cmd.addParameters("/c", "for %F in (\"" + f.getCanonicalPath() + "\") do @echo %~sF");
252
253     ExecResult res = SimpleCommandLineProcessRunner.runCommand(cmd, null);
254     Throwable error = res.getException();
255     if (error != null)
256       throw new IOException(error);
257
258     return res.getStdout().trim();
259   }
260 }