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
;
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
;
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
;
52 * Construct a {@link RepositoryMapping} for a previously connected project.
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
);
65 * Construct a {@link RepositoryMapping} for previously
68 * @param mappedContainer
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);
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
);
92 p
+= gLoc
.segment(gLoc
.segmentCount() - 1);
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() {
116 workdirPrefix
= null;
121 * @return a reference to the repository object handled by this mapping
123 public synchronized Repository
getRepository() {
127 synchronized void setRepository(final Repository r
) {
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() {
147 synchronized void setContainer(final IContainer 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.
175 * @return true if a resource differs in the workdir or index relative to
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)
191 return true; // flags new resources as changes
193 return true; // added in index
194 boolean hashesDiffer
= !entry
.getObjectId().equals(blob
.getId());
195 return hashesDiffer
|| entry
.isModified(getWorkDir());
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();
212 return p
.substring(pfxLen
);
213 else if (p
.length() == pfxLen
- 1)
219 * Get the repository mapping for a 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();
229 RepositoryProvider provider
= RepositoryProvider
.getProvider(project
);
230 if (!(provider
instanceof GitProvider
))
232 GitProvider gp
= (GitProvider
)provider
;
233 RepositoryMapping repositoryMapping
= gp
.getData().getRepositoryMapping(project
);
234 return repositoryMapping
;