Fix RepositoryMapping.getRepoRelativePath to honor linked resources
[egit/zawir.git] / org.spearce.egit.core / src / org / spearce / egit / core / project / RepositoryMapping.java
blob730aea067fb79ca4dd81106e4080483ac16587f8
1 /*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * Copyright (C) 2008, Shunichi Fuji <palglowr@gmail.com>
6 * Copyright (C) 2008, Google Inc.
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * See LICENSE for the full license text, also available.
11 *******************************************************************************/
12 package org.spearce.egit.core.project;
14 import java.io.File;
15 import java.io.IOException;
16 import java.io.UnsupportedEncodingException;
17 import java.util.Properties;
19 import org.eclipse.core.resources.IContainer;
20 import org.eclipse.core.resources.IFile;
21 import org.eclipse.core.resources.IProject;
22 import org.eclipse.core.resources.IResource;
23 import org.eclipse.core.runtime.IPath;
24 import org.eclipse.core.runtime.Path;
25 import org.eclipse.team.core.RepositoryProvider;
26 import org.spearce.egit.core.GitProvider;
27 import org.spearce.jgit.lib.GitIndex;
28 import org.spearce.jgit.lib.Repository;
29 import org.spearce.jgit.lib.Tree;
30 import org.spearce.jgit.lib.TreeEntry;
31 import org.spearce.jgit.lib.GitIndex.Entry;
33 /**
34 * This class keeps track
36 public class RepositoryMapping {
37 static boolean isInitialKey(final String key) {
38 return key.endsWith(".gitdir");
41 private final String containerPath;
43 private final String gitdirPath;
45 private Repository db;
47 private String workdirPrefix;
49 private IContainer container;
51 /**
52 * Construct a {@link RepositoryMapping} for a previously connected project.
54 * @param p TODO
55 * @param initialKey TODO
57 public RepositoryMapping(final Properties p, final String initialKey) {
58 final int dot = initialKey.lastIndexOf('.');
60 containerPath = initialKey.substring(0, dot);
61 gitdirPath = p.getProperty(initialKey);
64 /**
65 * Construct a {@link RepositoryMapping} for previously
66 * unknown project.
68 * @param mappedContainer
69 * @param gitDir
71 public RepositoryMapping(final IContainer mappedContainer, final File gitDir) {
72 final IPath cLoc = mappedContainer.getLocation()
73 .removeTrailingSeparator();
74 final IPath gLoc = Path.fromOSString(gitDir.getAbsolutePath())
75 .removeTrailingSeparator();
76 final IPath gLocParent = gLoc.removeLastSegments(1);
77 String p;
78 int cnt;
80 container = mappedContainer;
81 containerPath = container.getProjectRelativePath().toPortableString();
83 if (cLoc.isPrefixOf(gLoc)) {
84 gitdirPath = gLoc.removeFirstSegments(
85 gLoc.matchingFirstSegments(cLoc)).toPortableString();
86 } else if (gLocParent.isPrefixOf(cLoc)) {
87 cnt = cLoc.segmentCount() - cLoc.matchingFirstSegments(gLocParent);
88 p = "";
89 while (cnt-- > 0) {
90 p += "../";
92 p += gLoc.segment(gLoc.segmentCount() - 1);
93 gitdirPath = p;
94 } else {
95 gitdirPath = gLoc.toPortableString();
99 IPath getContainerPath() {
100 return Path.fromPortableString(containerPath);
103 IPath getGitDirPath() {
104 return Path.fromPortableString(gitdirPath);
108 * @return the workdir file, i.e. where the files are checked out
110 public File getWorkDir() {
111 return getRepository().getWorkDir();
114 synchronized void clear() {
115 db = null;
116 workdirPrefix = null;
117 container = null;
121 * @return a reference to the repository object handled by this mapping
123 public synchronized Repository getRepository() {
124 return db;
127 synchronized void setRepository(final Repository r) {
128 db = r;
130 try {
131 workdirPrefix = getWorkDir().getCanonicalPath();
132 } catch (IOException err) {
133 workdirPrefix = getWorkDir().getAbsolutePath();
135 workdirPrefix = workdirPrefix.replace('\\', '/');
136 if (!workdirPrefix.endsWith("/"))
137 workdirPrefix += "/";
141 * @return the mapped container (currently project)
143 public synchronized IContainer getContainer() {
144 return container;
147 synchronized void setContainer(final IContainer c) {
148 container = c;
152 * Notify registered {@link RepositoryChangeListener}s of a change.
154 * @see GitProjectData#addRepositoryChangeListener(RepositoryChangeListener)
156 public void fireRepositoryChanged() {
157 GitProjectData.fireRepositoryChanged(this);
160 synchronized void store(final Properties p) {
161 p.setProperty(containerPath + ".gitdir", gitdirPath);
164 public String toString() {
165 return "RepositoryMapping[" + containerPath + " -> " + gitdirPath + "]";
169 * Check whether a resource has been changed relative to the checked out
170 * version. Content is assumed changed by this routine if the resource's
171 * modification time differs from what is recorded in the index, but the
172 * real content hasn't changed. The reason is performance.
174 * @param rsrc
175 * @return true if a resource differs in the workdir or index relative to
176 * HEAD
178 * @throws IOException
179 * @throws UnsupportedEncodingException
181 public boolean isResourceChanged(IResource rsrc) throws IOException, UnsupportedEncodingException {
182 Repository repository = getRepository();
183 GitIndex index = repository.getIndex();
184 String repoRelativePath = getRepoRelativePath(rsrc);
185 Tree headTree = repository.mapTree("HEAD");
186 TreeEntry blob = headTree!=null ? headTree.findBlobMember(repoRelativePath) : null;
187 Entry entry = index.getEntry(repoRelativePath);
188 if (rsrc instanceof IFile && entry == null && blob == null)
189 return false;
190 if (entry == null)
191 return true; // flags new resources as changes
192 if (blob == null)
193 return true; // added in index
194 boolean hashesDiffer = !entry.getObjectId().equals(blob.getId());
195 return hashesDiffer || entry.isModified(getWorkDir());
199 * @param rsrc
200 * @return the path relative to the Git repository, including base name.
202 public String getRepoRelativePath(final IResource rsrc) {
203 // We should only be called for resources that are actually
204 // in this repository, so we can safely assume that their
205 // path prefix matches workdirPrefix. Testing that here is
206 // rather expensive so we don't bother.
208 final int pfxLen = workdirPrefix.length();
209 final String p = rsrc.getLocation().toString();
210 final int pLen = p.length();
211 if (pLen > pfxLen)
212 return p.substring(pfxLen);
213 else if (p.length() == pfxLen - 1)
214 return "";
215 return null;
219 * Get the repository mapping for a resource
221 * @param resource
222 * @return the RepositoryMapping for this resource,
223 * or null for non GitProvider.
225 public static RepositoryMapping getMapping(IResource resource) {
226 IProject project = resource.getProject();
227 if (project == null)
228 return null;
229 RepositoryProvider provider = RepositoryProvider.getProvider(project);
230 if (!(provider instanceof GitProvider))
231 return null;
232 GitProvider gp = (GitProvider)provider;
233 RepositoryMapping repositoryMapping = gp.getData().getRepositoryMapping(project);
234 return repositoryMapping;