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 * which accompanies this distribution, and is available at
11 * http://www.eclipse.org/legal/epl-v10.html
12 *******************************************************************************/
13 package org
.eclipse
.egit
.core
.project
;
16 import java
.io
.IOException
;
17 import java
.io
.UnsupportedEncodingException
;
18 import java
.util
.Properties
;
20 import org
.eclipse
.core
.resources
.IContainer
;
21 import org
.eclipse
.core
.resources
.IFile
;
22 import org
.eclipse
.core
.resources
.IProject
;
23 import org
.eclipse
.core
.resources
.IResource
;
24 import org
.eclipse
.core
.runtime
.IPath
;
25 import org
.eclipse
.core
.runtime
.Path
;
26 import org
.eclipse
.egit
.core
.GitProvider
;
27 import org
.eclipse
.team
.core
.RepositoryProvider
;
28 import org
.eclipse
.jgit
.lib
.Constants
;
29 import org
.eclipse
.jgit
.lib
.GitIndex
;
30 import org
.eclipse
.jgit
.lib
.Repository
;
31 import org
.eclipse
.jgit
.lib
.Tree
;
32 import org
.eclipse
.jgit
.lib
.TreeEntry
;
33 import org
.eclipse
.jgit
.lib
.GitIndex
.Entry
;
36 * This class keeps track
38 public class RepositoryMapping
{
39 static boolean isInitialKey(final String key
) {
40 return key
.endsWith(".gitdir");
43 private final String containerPath
;
45 private final String gitdirPath
;
47 private Repository db
;
49 private String workdirPrefix
;
51 private IContainer container
;
54 * Construct a {@link RepositoryMapping} for a previously connected project.
57 * @param initialKey TODO
59 public RepositoryMapping(final Properties p
, final String initialKey
) {
60 final int dot
= initialKey
.lastIndexOf('.');
62 containerPath
= initialKey
.substring(0, dot
);
63 gitdirPath
= p
.getProperty(initialKey
);
67 * Construct a {@link RepositoryMapping} for previously
70 * @param mappedContainer
73 public RepositoryMapping(final IContainer mappedContainer
, final File gitDir
) {
74 final IPath cLoc
= mappedContainer
.getLocation()
75 .removeTrailingSeparator();
76 final IPath gLoc
= Path
.fromOSString(gitDir
.getAbsolutePath())
77 .removeTrailingSeparator();
78 final IPath gLocParent
= gLoc
.removeLastSegments(1);
82 container
= mappedContainer
;
83 containerPath
= container
.getProjectRelativePath().toPortableString();
85 if (cLoc
.isPrefixOf(gLoc
)) {
86 gitdirPath
= gLoc
.removeFirstSegments(
87 gLoc
.matchingFirstSegments(cLoc
)).toPortableString();
88 } else if (gLocParent
.isPrefixOf(cLoc
)) {
89 cnt
= cLoc
.segmentCount() - cLoc
.matchingFirstSegments(gLocParent
);
94 p
+= gLoc
.segment(gLoc
.segmentCount() - 1);
97 gitdirPath
= gLoc
.toPortableString();
101 IPath
getContainerPath() {
102 return Path
.fromPortableString(containerPath
);
105 IPath
getGitDirPath() {
106 return Path
.fromPortableString(gitdirPath
);
110 * @return the workdir file, i.e. where the files are checked out
112 public File
getWorkDir() {
113 return getRepository().getWorkDir();
116 synchronized void clear() {
118 workdirPrefix
= null;
123 * @return a reference to the repository object handled by this mapping
125 public synchronized Repository
getRepository() {
129 synchronized void setRepository(final Repository r
) {
133 workdirPrefix
= getWorkDir().getCanonicalPath();
134 } catch (IOException err
) {
135 workdirPrefix
= getWorkDir().getAbsolutePath();
137 workdirPrefix
= workdirPrefix
.replace('\\', '/');
138 if (!workdirPrefix
.endsWith("/"))
139 workdirPrefix
+= "/";
143 * @return the mapped container (currently project)
145 public synchronized IContainer
getContainer() {
149 synchronized void setContainer(final IContainer c
) {
154 * Notify registered {@link RepositoryChangeListener}s of a change.
156 * @see GitProjectData#addRepositoryChangeListener(RepositoryChangeListener)
158 public void fireRepositoryChanged() {
159 GitProjectData
.fireRepositoryChanged(this);
162 synchronized void store(final Properties p
) {
163 p
.setProperty(containerPath
+ ".gitdir", gitdirPath
);
166 public String
toString() {
167 return "RepositoryMapping[" + containerPath
+ " -> " + gitdirPath
+ "]";
171 * Check whether a resource has been changed relative to the checked out
172 * version. Content is assumed changed by this routine if the resource's
173 * modification time differs from what is recorded in the index, but the
174 * real content hasn't changed. The reason is performance.
177 * @return true if a resource differs in the workdir or index relative to
180 * @throws IOException
181 * @throws UnsupportedEncodingException
183 public boolean isResourceChanged(IResource rsrc
) throws IOException
, UnsupportedEncodingException
{
184 Repository repository
= getRepository();
185 GitIndex index
= repository
.getIndex();
186 String repoRelativePath
= getRepoRelativePath(rsrc
);
187 Tree headTree
= repository
.mapTree(Constants
.HEAD
);
188 TreeEntry blob
= headTree
!=null ? headTree
.findBlobMember(repoRelativePath
) : null;
189 Entry entry
= index
.getEntry(repoRelativePath
);
190 if (rsrc
instanceof IFile
&& entry
== null && blob
== null)
193 return true; // flags new resources as changes
195 return true; // added in index
196 boolean hashesDiffer
= !entry
.getObjectId().equals(blob
.getId());
197 return hashesDiffer
|| entry
.isModified(getWorkDir());
202 * @return the path relative to the Git repository, including base name.
204 public String
getRepoRelativePath(final IResource rsrc
) {
205 // We should only be called for resources that are actually
206 // in this repository, so we can safely assume that their
207 // path prefix matches workdirPrefix. Testing that here is
208 // rather expensive so we don't bother.
210 final int pfxLen
= workdirPrefix
.length();
211 final String p
= rsrc
.getLocation().toString();
212 final int pLen
= p
.length();
214 return p
.substring(pfxLen
);
215 else if (p
.length() == pfxLen
- 1)
221 * Get the repository mapping for a resource
224 * @return the RepositoryMapping for this resource,
225 * or null for non GitProvider.
227 public static RepositoryMapping
getMapping(final IResource resource
) {
228 final IProject project
= resource
.getProject();
232 final RepositoryProvider rp
= RepositoryProvider
.getProvider(project
);
233 if (!(rp
instanceof GitProvider
))
236 if (((GitProvider
)rp
).getData() == null)
239 return ((GitProvider
)rp
).getData().getRepositoryMapping(resource
);
243 * @return the name of the .git directory
245 public String
getGitDir() {