TW-52308 add TeamCity version to ssh client version on server
[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   private static final String SSH_V2 = "SSH-2.0";
43
44   /**
45    * Convert remote URL to JGIT form
46    *
47    * @param file the file to convert
48    * @return the file URL recognized by JGit
49    */
50   public static String toURL(File file) {
51     return "file:///" + file.getAbsolutePath().replace(File.separatorChar, '/');
52   }
53
54   /**
55    * The version comparator
56    */
57   public static final Comparator<String> VERSION_COMPARATOR = new Comparator<String>() {
58     public int compare(String o1, String o2) {
59       return o1.compareTo(o2);
60     }
61   };
62
63   /**
64    * Make version string from revision hash and time
65    *
66    * @param revision the revision hash
67    * @param time     the time of revision
68    * @return the version string
69    */
70   @NotNull
71   public static String makeVersion(@NotNull String revision, long time) {
72     return revision + "@" + Long.toHexString(time);
73   }
74
75   /**
76    * Extract revision number from the version
77    *
78    * @param version string
79    * @return the revision number
80    */
81   @NotNull
82   public static String versionRevision(@NotNull String version) {
83     int i = version.indexOf('@');
84     if (i == -1)
85       return version;
86     return version.substring(0, i);
87   }
88
89   /**
90    * Expands ref name to full ref name
91    *
92    * @param ref the ref name
93    * @return full ref name
94    */
95   public static String expandRef(String ref) {
96     if (ref.startsWith("refs/")) {
97       return ref;
98     } else {
99       return "refs/heads/" + ref;
100     }
101   }
102
103   public static String getShortBranchName(@NotNull String fullRefName) {
104     if (isRegularBranch(fullRefName))
105       return fullRefName.substring(Constants.R_HEADS.length());
106     return fullRefName;
107   }
108
109   public static boolean isTag(@NotNull Ref ref) {
110     return isTag(ref.getName());
111   }
112
113   public static boolean isTag(@NotNull String fullRefName) {
114     return fullRefName.startsWith(Constants.R_TAGS);
115   }
116
117   public static boolean isRegularBranch(@NotNull String fullRefName) {
118     return fullRefName.startsWith(Constants.R_HEADS);
119   }
120
121   /**
122    * Creates remote ref from local ref name for remote called 'origin'
123    *
124    * @param ref local ref name
125    * @return full remote ref name
126    */
127   public static String createRemoteRef(String ref) {
128     if (ref.startsWith("refs/")) {
129       if (ref.startsWith("refs/heads/")) {
130         return "refs/remotes/origin/" + ref.substring("refs/heads/".length());
131       } else {
132         return ref;
133       }
134     } else {
135       return "refs/remotes/origin/" + ref;
136     }
137   }
138
139   /**
140    * Convert Git path to a relative File
141    *
142    * @param path the path to covert
143    * @return the {@link File} object
144    */
145   public static File toFile(String path) {
146     return new File(path.replace('/', File.separatorChar));
147   }
148
149   /**
150    * Normalize path removing ".." and "." elements assuming "/" as separator
151    *
152    * @param path the path to normalize
153    * @return the normalized path
154    */
155   public static String normalizePath(String path) {
156     if (path.length() == 0 || path.equals("/")) {
157       return path;
158     }
159     StringBuilder rc = new StringBuilder();
160     String[] pc = path.split("/");
161     int count = 0;
162     int startBacks = 0;
163     int[] pci = new int[pc.length];
164     boolean startsWithSlash = path.charAt(0) == '/';
165     for (int i = 0; i < pc.length; i++) {
166       String f = pc[i];
167       if (f.length() == 0 || ".".equals(f)) {
168         // do nothing
169       } else if ("..".equals(f)) {
170         if (count == 0) {
171           startBacks++;
172         } else {
173           count--;
174         }
175       } else {
176         pci[count++] = i;
177       }
178     }
179     for (int i = 0; i < startBacks; i++) {
180       if (rc.length() != 0 || startsWithSlash) {
181         rc.append('/');
182       }
183       rc.append("..");
184     }
185     for (int i = 0; i < count; i++) {
186       int fi = pci[i];
187       if (rc.length() != 0 || startsWithSlash) {
188         rc.append('/');
189       }
190       rc.append(pc[fi]);
191     }
192     return rc.toString();
193   }
194
195
196   public static boolean isAnonymousGitWithUsername(@NotNull URIish uri) {
197     return "git".equals(uri.getScheme()) && uri.getUser() != null;
198   }
199
200   /**
201    * Returns build parameter name with the vcs branch name for given
202    * git VCS root
203    */
204   public static String getGitRootBranchParamName(@NotNull VcsRoot root) {
205     return jetbrains.buildServer.buildTriggers.vcs.git.Constants.GIT_ROOT_BUILD_BRANCH_PREFIX + VcsUtil.getSimplifiedName(root);
206   }
207
208
209   public static File getGitDir(@NotNull File workingTreeDir) throws IOException, VcsException {
210     File dotGit = new File(workingTreeDir, ".git");
211     if (dotGit.isFile()) {
212       List<String> content = FileUtil.readFile(dotGit);
213       if (content.isEmpty())
214         throw new VcsException("Empty " + dotGit.getAbsolutePath());
215       String line = content.get(0);
216       if (!line.startsWith("gitdir:"))
217         throw new VcsException("Wrong format of " + dotGit.getAbsolutePath() + ": " + content);
218       String gitDirPath = line.substring("gitdir:".length()).trim();
219       File gitDir = new File(gitDirPath);
220       if (gitDir.isAbsolute())
221         return gitDir;
222       return new File(workingTreeDir, gitDirPath);
223     }
224     return dotGit;
225   }
226
227   @NotNull
228   public static String getRevision(@NotNull final Ref ref) {
229     return getObjectId(ref).name();
230   }
231
232   @NotNull
233   public static ObjectId getObjectId(@NotNull final Ref ref) {
234     if (isTag(ref) && ref.getPeeledObjectId() != null)
235       return ref.getPeeledObjectId();
236     return ref.getObjectId();
237   }
238
239
240   /**
241    * Returns short file name for the given file on windows. The file must exist.
242    * @param f file of interest
243    * @return see above
244    * @throws UnsupportedOperationException if called on non windows platform
245    * @throws IOException in case of IO error
246    */
247   public static String getShortFileName(@NotNull File f) throws UnsupportedOperationException, IOException {
248     if (!SystemInfo.isWindows)
249       throw new UnsupportedOperationException();
250
251     GeneralCommandLine cmd = new GeneralCommandLine();
252     cmd.setExePath("cmd.exe");
253     cmd.addParameters("/c", "for %F in (\"" + f.getCanonicalPath() + "\") do @echo %~sF");
254
255     ExecResult res = SimpleCommandLineProcessRunner.runCommand(cmd, null);
256     Throwable error = res.getException();
257     if (error != null)
258       throw new IOException(error);
259
260     return res.getStdout().trim();
261   }
262
263
264   @NotNull
265   public static String getSshClientVersion(@NotNull String originalSshClientVersion, @NotNull String teamcityVersion) {
266     if (!originalSshClientVersion.startsWith(SSH_V2))
267       return originalSshClientVersion;
268     //rfc4253-4.2
269     return SSH_V2 + "-" + teamcityVersion.replace(' ', '-') + originalSshClientVersion.substring(SSH_V2.length());
270   }
271 }