1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005-2006,2008 - TortoiseSVN
4 // Copyright (C) 2008-2011 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include ".\statuscacheentry.h"
22 #include "GitStatus.h"
23 #include "CacheInterface.h"
26 DWORD cachetimeout
= (DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\Cachetimeout"), CACHETIMEOUT
);
28 CStatusCacheEntry::CStatusCacheEntry()
30 , m_bSVNEntryFieldSet(false)
31 , m_kind(git_node_unknown
)
33 , m_highestPriorityLocalStatus(git_wc_status_none
)
38 CStatusCacheEntry::CStatusCacheEntry(const git_wc_status_kind status
)
40 , m_bSVNEntryFieldSet(false)
41 , m_kind(git_node_unknown
)
43 , m_highestPriorityLocalStatus(status
)
45 m_GitStatus
.prop_status
=m_GitStatus
.text_status
= status
;
46 m_discardAtTime
= GetTickCount()+cachetimeout
;
49 CStatusCacheEntry::CStatusCacheEntry(const git_wc_status2_t
* pGitStatus
, __int64 lastWriteTime
, bool bReadOnly
, DWORD validuntil
/* = 0*/)
51 , m_bSVNEntryFieldSet(false)
52 , m_kind(git_node_unknown
)
54 , m_highestPriorityLocalStatus(git_wc_status_none
)
56 SetStatus(pGitStatus
);
57 m_lastWriteTime
= lastWriteTime
;
59 m_discardAtTime
= validuntil
;
61 m_discardAtTime
= GetTickCount()+cachetimeout
;
62 m_bReadOnly
= bReadOnly
;
65 bool CStatusCacheEntry::SaveToDisk(FILE * pFile
)
67 #define WRITEVALUETOFILE(x) if (fwrite(&x, sizeof(x), 1, pFile)!=1) return false;
68 #define WRITESTRINGTOFILE(x) if (x.IsEmpty()) {value=0;WRITEVALUETOFILE(value);}else{value=x.GetLength();WRITEVALUETOFILE(value);if (fwrite((LPCSTR)x, sizeof(char), value, pFile)!=value) return false;}
70 unsigned int value
= 4;
71 WRITEVALUETOFILE(value
); // 'version' of this save-format
72 WRITEVALUETOFILE(m_highestPriorityLocalStatus
);
73 WRITEVALUETOFILE(m_lastWriteTime
);
74 WRITEVALUETOFILE(m_bSet
);
75 WRITEVALUETOFILE(m_bSVNEntryFieldSet
);
76 CStringA
srev(m_commitRevision
); WRITESTRINGTOFILE(srev
);
77 WRITESTRINGTOFILE(m_sUrl
);
78 WRITESTRINGTOFILE(m_sOwner
);
79 WRITESTRINGTOFILE(m_sAuthor
);
80 WRITEVALUETOFILE(m_kind
);
81 WRITEVALUETOFILE(m_bReadOnly
);
82 WRITESTRINGTOFILE(m_sPresentProps
);
84 // now save the status struct (without the entry field, because we don't use that) WRITEVALUETOFILE(m_GitStatus.copied);
85 // WRITEVALUETOFILE(m_GitStatus.locked);
86 WRITEVALUETOFILE(m_GitStatus
.prop_status
);
87 // WRITEVALUETOFILE(m_GitStatus.repos_prop_status);
88 // WRITEVALUETOFILE(m_GitStatus.repos_text_status);
89 // WRITEVALUETOFILE(m_GitStatus.switched);
90 WRITEVALUETOFILE(m_GitStatus
.text_status
);
94 bool CStatusCacheEntry::LoadFromDisk(FILE * pFile
)
96 #define LOADVALUEFROMFILE(x) if (fread(&x, sizeof(x), 1, pFile)!=1) return false;
99 unsigned int value
= 0;
100 LOADVALUEFROMFILE(value
);
102 return false; // not the correct version
103 LOADVALUEFROMFILE(m_highestPriorityLocalStatus
);
104 LOADVALUEFROMFILE(m_lastWriteTime
);
105 LOADVALUEFROMFILE(m_bSet
);
106 LOADVALUEFROMFILE(m_bSVNEntryFieldSet
);
107 LOADVALUEFROMFILE(value
);
111 if (fread(s
.GetBuffer(value
+1), sizeof(char), value
, pFile
)!=value
)
114 m_commitRevision
.Empty();
117 s
.ReleaseBuffer(value
);
118 m_commitRevision
= s
;
120 LOADVALUEFROMFILE(value
);
123 if (value
> INTERNET_MAX_URL_LENGTH
)
124 return false; // invalid length for an url
125 if (fread(m_sUrl
.GetBuffer(value
+1), sizeof(char), value
, pFile
)!=value
)
127 m_sUrl
.ReleaseBuffer(0);
130 m_sUrl
.ReleaseBuffer(value
);
132 LOADVALUEFROMFILE(value
);
135 if (fread(m_sOwner
.GetBuffer(value
+1), sizeof(char), value
, pFile
)!=value
)
137 m_sOwner
.ReleaseBuffer(0);
140 m_sOwner
.ReleaseBuffer(value
);
142 LOADVALUEFROMFILE(value
);
145 if (fread(m_sAuthor
.GetBuffer(value
+1), sizeof(char), value
, pFile
)!=value
)
147 m_sAuthor
.ReleaseBuffer(0);
150 m_sAuthor
.ReleaseBuffer(value
);
152 LOADVALUEFROMFILE(m_kind
);
153 LOADVALUEFROMFILE(m_bReadOnly
);
154 LOADVALUEFROMFILE(value
);
157 if (fread(m_sPresentProps
.GetBuffer(value
+1), sizeof(char), value
, pFile
)!=value
)
159 m_sPresentProps
.ReleaseBuffer(0);
162 m_sPresentProps
.ReleaseBuffer(value
);
164 SecureZeroMemory(&m_GitStatus
, sizeof(m_GitStatus
));
165 // LOADVALUEFROMFILE(m_GitStatus.copied);
166 // LOADVALUEFROMFILE(m_GitStatus.locked);
167 LOADVALUEFROMFILE(m_GitStatus
.prop_status
);
168 // LOADVALUEFROMFILE(m_GitStatus.repos_prop_status);
169 // LOADVALUEFROMFILE(m_GitStatus.repos_text_status);
170 // LOADVALUEFROMFILE(m_GitStatus.switched);
171 LOADVALUEFROMFILE(m_GitStatus
.text_status
);
172 // m_GitStatus.entry = NULL;
173 m_discardAtTime
= GetTickCount()+cachetimeout
;
175 catch ( CAtlException
)
182 void CStatusCacheEntry::SetStatus(const git_wc_status2_t
* pGitStatus
)
184 if(pGitStatus
== NULL
)
190 m_highestPriorityLocalStatus
= GitStatus::GetMoreImportant(pGitStatus
->prop_status
, pGitStatus
->text_status
);
191 m_GitStatus
= *pGitStatus
;
193 // Currently we don't deep-copy the whole entry value, but we do take a few members
194 /* if(pGitStatus->entry != NULL)
196 m_sUrl = pGitStatus->entry->url;
197 m_commitRevision = pGitStatus->entry->cmt_rev;
198 m_bSVNEntryFieldSet = true;
199 m_sOwner = pGitStatus->entry->lock_owner;
200 m_kind = pGitStatus->entry->kind;
201 m_sAuthor = pGitStatus->entry->cmt_author;
202 if (pGitStatus->entry->present_props)
203 m_sPresentProps = pGitStatus->entry->present_props;
208 m_commitRevision
= GIT_INVALID_REVNUM
;
209 m_bSVNEntryFieldSet
= false;
211 // m_GitStatus.entry = NULL;
213 m_discardAtTime
= GetTickCount()+cachetimeout
;
218 void CStatusCacheEntry::SetAsUnversioned()
220 SecureZeroMemory(&m_GitStatus
, sizeof(m_GitStatus
));
221 m_discardAtTime
= GetTickCount()+cachetimeout
;
222 git_wc_status_kind status
= git_wc_status_none
;
223 if (m_highestPriorityLocalStatus
== git_wc_status_missing
)
224 status
= git_wc_status_missing
;
225 if (m_highestPriorityLocalStatus
== git_wc_status_unversioned
)
226 status
= git_wc_status_unversioned
;
227 m_highestPriorityLocalStatus
= status
;
228 m_GitStatus
.prop_status
= git_wc_status_none
;
229 m_GitStatus
.text_status
= status
;
233 bool CStatusCacheEntry::HasExpired(long now
) const
235 return m_discardAtTime
!= 0 && (now
- m_discardAtTime
) >= 0;
238 void CStatusCacheEntry::BuildCacheResponse(TGITCacheResponse
& response
, DWORD
& responseLength
) const
240 SecureZeroMemory(&response
, sizeof(response
));
241 if(m_bSVNEntryFieldSet
)
243 response
.m_status
= m_GitStatus
;
244 wcscpy_s(response
.m_entry
.cmt_rev
, 41, m_commitRevision
.GetString());
246 // There is no point trying to set these pointers here, because this is not
247 // the process which will be using the data.
248 // The process which receives this response (generally the TSVN Shell Extension)
249 // must fix-up these pointers when it gets them
250 // response.m_status.entry = NULL;
251 // response.m_entry.url = NULL;
253 response
.m_kind
= m_kind
;
254 response
.m_readonly
= m_bReadOnly
;
256 if (m_sPresentProps
.Find("svn:needs-lock")>=0)
258 response
.m_needslock
= true;
261 response
.m_needslock
= false;
262 // The whole of response has been zeroed, so this will copy safely
263 strncat_s(response
.m_url
, INTERNET_MAX_URL_LENGTH
, m_sUrl
, _TRUNCATE
);
264 strncat_s(response
.m_owner
, 255, m_sOwner
, _TRUNCATE
);
265 strncat_s(response
.m_author
, 255, m_sAuthor
, _TRUNCATE
);
266 responseLength
= sizeof(response
);
270 response
.m_status
= m_GitStatus
;
271 responseLength
= sizeof(response
.m_status
);
274 // directories that are empty or only contain unversioned files will be git_wc_status_incomplete, report as unversioned
275 if (response
.m_status
.text_status
== git_wc_status_incomplete
)
277 response
.m_status
.text_status
= response
.m_status
.prop_status
= git_wc_status_unversioned
;
281 bool CStatusCacheEntry::IsVersioned() const
283 return m_highestPriorityLocalStatus
> git_wc_status_unversioned
;
286 bool CStatusCacheEntry::DoesFileTimeMatch(__int64 testTime
) const
288 return m_lastWriteTime
== testTime
;
292 bool CStatusCacheEntry::ForceStatus(git_wc_status_kind forcedStatus
)
294 git_wc_status_kind newStatus
= forcedStatus
;
296 if(newStatus
!= m_highestPriorityLocalStatus
)
298 // We've had a status change
299 m_highestPriorityLocalStatus
= newStatus
;
300 m_GitStatus
.text_status
= newStatus
;
301 m_GitStatus
.prop_status
= newStatus
;
302 m_discardAtTime
= GetTickCount()+cachetimeout
;
309 CStatusCacheEntry::HasBeenSet() const
314 void CStatusCacheEntry::Invalidate()