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
.Constants
;
28 import org
.spearce
.jgit
.lib
.GitIndex
;
29 import org
.spearce
.jgit
.lib
.Repository
;
30 import org
.spearce
.jgit
.lib
.Tree
;
31 import org
.spearce
.jgit
.lib
.TreeEntry
;
32 import org
.spearce
.jgit
.lib
.GitIndex
.Entry
;
35 * This class keeps track
37 public class RepositoryMapping
{
38 static boolean isInitialKey(final String key
) {
39 return key
.endsWith(".gitdir");
42 private final String containerPath
;
44 private final String gitdirPath
;
46 private Repository db
;
48 private String workdirPrefix
;
50 private IContainer container
;
53 * Construct a {@link RepositoryMapping} for a previously connected project.
56 * @param initialKey TODO
58 public RepositoryMapping(final Properties p
, final String initialKey
) {
59 final int dot
= initialKey
.lastIndexOf('.');
61 containerPath
= initialKey
.substring(0, dot
);
62 gitdirPath
= p
.getProperty(initialKey
);
66 * Construct a {@link RepositoryMapping} for previously
69 * @param mappedContainer
72 public RepositoryMapping(final IContainer mappedContainer
, final File gitDir
) {
73 final IPath cLoc
= mappedContainer
.getLocation()
74 .removeTrailingSeparator();
75 final IPath gLoc
= Path
.fromOSString(gitDir
.getAbsolutePath())
76 .removeTrailingSeparator();
77 final IPath gLocParent
= gLoc
.removeLastSegments(1);
81 container
= mappedContainer
;
82 containerPath
= container
.getProjectRelativePath().toPortableString();
84 if (cLoc
.isPrefixOf(gLoc
)) {
85 gitdirPath
= gLoc
.removeFirstSegments(
86 gLoc
.matchingFirstSegments(cLoc
)).toPortableString();
87 } else if (gLocParent
.isPrefixOf(cLoc
)) {
88 cnt
= cLoc
.segmentCount() - cLoc
.matchingFirstSegments(gLocParent
);
93 p
+= gLoc
.segment(gLoc
.segmentCount() - 1);
96 gitdirPath
= gLoc
.toPortableString();
100 IPath
getContainerPath() {
101 return Path
.fromPortableString(containerPath
);
104 IPath
getGitDirPath() {
105 return Path
.fromPortableString(gitdirPath
);
109 * @return the workdir file, i.e. where the files are checked out
111 public File
getWorkDir() {
112 return getRepository().getWorkDir();
115 synchronized void clear() {
117 workdirPrefix
= null;
122 * @return a reference to the repository object handled by this mapping
124 public synchronized Repository
getRepository() {
128 synchronized void setRepository(final Repository r
) {
132 workdirPrefix
= getWorkDir().getCanonicalPath();
133 } catch (IOException err
) {
134 workdirPrefix
= getWorkDir().getAbsolutePath();
136 workdirPrefix
= workdirPrefix
.replace('\\', '/');
137 if (!workdirPrefix
.endsWith("/"))
138 workdirPrefix
+= "/";
142 * @return the mapped container (currently project)
144 public synchronized IContainer
getContainer() {
148 synchronized void setContainer(final IContainer c
) {
153 * Notify registered {@link RepositoryChangeListener}s of a change.
155 * @see GitProjectData#addRepositoryChangeListener(RepositoryChangeListener)
157 public void fireRepositoryChanged() {
158 GitProjectData
.fireRepositoryChanged(this);
161 synchronized void store(final Properties p
) {
162 p
.setProperty(containerPath
+ ".gitdir", gitdirPath
);
165 public String
toString() {
166 return "RepositoryMapping[" + containerPath
+ " -> " + gitdirPath
+ "]";
170 * Check whether a resource has been changed relative to the checked out
171 * version. Content is assumed changed by this routine if the resource's
172 * modification time differs from what is recorded in the index, but the
173 * real content hasn't changed. The reason is performance.
176 * @return true if a resource differs in the workdir or index relative to
179 * @throws IOException
180 * @throws UnsupportedEncodingException
182 public boolean isResourceChanged(IResource rsrc
) throws IOException
, UnsupportedEncodingException
{
183 Repository repository
= getRepository();
184 GitIndex index
= repository
.getIndex();
185 String repoRelativePath
= getRepoRelativePath(rsrc
);
186 Tree headTree
= repository
.mapTree(Constants
.HEAD
);
187 TreeEntry blob
= headTree
!=null ? headTree
.findBlobMember(repoRelativePath
) : null;
188 Entry entry
= index
.getEntry(repoRelativePath
);
189 if (rsrc
instanceof IFile
&& entry
== null && blob
== null)
192 return true; // flags new resources as changes
194 return true; // added in index
195 boolean hashesDiffer
= !entry
.getObjectId().equals(blob
.getId());
196 return hashesDiffer
|| entry
.isModified(getWorkDir());
201 * @return the path relative to the Git repository, including base name.
203 public String
getRepoRelativePath(final IResource rsrc
) {
204 // We should only be called for resources that are actually
205 // in this repository, so we can safely assume that their
206 // path prefix matches workdirPrefix. Testing that here is
207 // rather expensive so we don't bother.
209 final int pfxLen
= workdirPrefix
.length();
210 final String p
= rsrc
.getLocation().toString();
211 final int pLen
= p
.length();
213 return p
.substring(pfxLen
);
214 else if (p
.length() == pfxLen
- 1)
220 * Get the repository mapping for a resource
223 * @return the RepositoryMapping for this resource,
224 * or null for non GitProvider.
226 public static RepositoryMapping
getMapping(final IResource resource
) {
227 final IProject project
= resource
.getProject();
231 final RepositoryProvider rp
= RepositoryProvider
.getProvider(project
);
232 if (!(rp
instanceof GitProvider
))
235 if (((GitProvider
)rp
).getData() == null)
238 return ((GitProvider
)rp
).getData().getRepositoryMapping(resource
);
242 * @return the name of the .git directory
244 public String
getGitDir() {