##// END OF EJS Templates
diffs: compare overhaul....
marcink -
r1259:8e9f93ec default
parent child Browse files
Show More
@@ -0,0 +1,9 b''
1 ===================================================================
2 Cannot display: file marked as a binary type.
3 svn:mime-type = application/octet-stream
4 Index: intl.dll
5 ===================================================================
6 diff --git a/intl.dll b/intl.dll
7 new file mode 10644
8 --- /dev/null (revision 0)
9 +++ b/intl.dll (revision 1489)
This diff has been collapsed as it changes many lines, (652 lines changed) Show them Hide them
@@ -0,0 +1,652 b''
1 ===================================================================
2 Cannot display: file marked as a binary type.
3 svn:mime-type = image/png
4 Index: trunk/doc/images/SettingsOverlay.png
5 ===================================================================
6 diff --git a/trunk/doc/images/SettingsOverlay.png b/trunk/doc/images/SettingsOverlay.png
7 GIT binary patch
8 --- a/trunk/doc/images/SettingsOverlay.png (revision 1487)
9 +++ b/trunk/doc/images/SettingsOverlay.png (revision 1488)
10 Index: trunk/doc/source/de/tsvn_ch04.xml
11 ===================================================================
12 diff --git a/trunk/doc/source/de/tsvn_ch04.xml b/trunk/doc/source/de/tsvn_ch04.xml
13 --- a/trunk/doc/source/de/tsvn_ch04.xml (revision 1487)
14 +++ b/trunk/doc/source/de/tsvn_ch04.xml (revision 1488)
15 @@ -1561,39 +1561,49 @@
16 </figure>
17 Abgesehen von der bevorzugten Sprache erlaubt dieser Dialog es Ihnen,
18 (fast) alle Einstellungen von TortoiseSVN zu ändern.
19 -### Translate ###
20 <variablelist>
21 <varlistentry>
22 - <term>Language</term>
23 - <listitem>
24 - <para>Selects your user interface language. What did you expect?</para>
25 + <term>Sprache</term>
26 + <listitem>
27 + <para>Wählt die Sprache für die Dialoge/Meldungen aus. Was
28 + haben Sie anderes erwartet?</para>
29 </listitem>
30 </varlistentry>
31
32 <varlistentry>
33 - <term>Exclude pattern</term>
34 + <term>Ausschliessen</term>
35 <listitem>
36 <para>
37 <indexterm>
38 - <primary>exclude pattern</primary>
39 + <primary>ausschliessen</primary>
40 </indexterm>
41 - Exclude files or directories by typing in the names or extensions. Patterns are separated by spaces
42 - e.g. <literal>bin obj *.bak *.~?? *.jar *.[Tt]mp</literal>. The first two entries refer to directories, the
43 - other four to files.
44 - </para>
45 - <para>
46 - This exclude pattern will affect all your projects. It is not versioned, so it
47 - will not affect other users. In contrast you can also use the versioned svn:ignore
48 - property to exclude files or directories from version control. You can set the svn:ignore
49 - property using the
50 + Ausgeschlossene, unversionierte Dateien werden nicht angezeigt
51 + in z.B. dem Übertragen Dialog. Ausserdem werden solche Dateien
52 + beim Importieren in ein Projektarchiv ignoriert.
53 + Schliessen Sie Dateien oder Ordner aus durch Angabe von
54 + Dateinamen oder Erweiterungen. Die einzelnen Muster werden
55 + durch Leerzeichen voneinander getrennt. Zum Beispiel
56 + <literal>bin obj *.bak *.~?? *.jar *.[Tt]mp</literal>.
57 + Die ersten beiden Muster beziehen sich auf Ordner, die
58 + restlichen vier auf Dateien.
59 + </para>
60 + <para>
61 + Diese Auschluss-Muster beziehen sich auf alle Ihre Projekte.
62 + Sie werden nicht versioniert, d.h. andere Benutzer werden davon
63 + nichts mitbekommen. Im Gegensatz dazu können Sie jedoch auch
64 + die versionierte Eigenschaft svn:ignore verwenden, um Dateien
65 + und/oder Ordner von der Versionskontrolle auszuschliessen.
66 + Sie können die svn:ignore Eigenschaft setzen durch den
67 <menuchoice>
68 - <guimenuitem>Add to Ignore List</guimenuitem>
69 + <guimenuitem>Ignorieren</guimenuitem>
70 </menuchoice>
71 - command. After commiting every other user will have the same
72 - svn:ignore property set for this project / directory as you.
73 + Befehl. Nach dem Übertragen wird jeder Benutzer dieselbe
74 + svn:ignore Eigenschaft für das Projekt oder den Ordner
75 + haben wie Sie.
76 </para>
77 </listitem>
78 </varlistentry>
79 +### Translate ###
80
81 <varlistentry>
82 <term>Default number of log messages</term>
83 @@ -1608,16 +1618,36 @@
84 </varlistentry>
85
86 <varlistentry>
87 - <term>Short date / time format in log messages</term>
88 - <listitem>
89 - <para>If the standard long messages use up too much space on your sceen use the short format.</para>
90 + <term>Edit...</term>
91 + <listitem>
92 + <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
93 </listitem>
94 </varlistentry>
95
96 <varlistentry>
97 - <term>Edit...</term>
98 - <listitem>
99 - <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
100 + <term>Short date / time format in log messages</term>
101 + <listitem>
102 + <para>If the standard long messages use up too much space on your sceen use the short format.</para>
103 + </listitem>
104 + </varlistentry>
105 +
106 + <varlistentry>
107 + <term>Set filedates to "last commit time"</term>
108 + <listitem>
109 + <para>
110 + This option tells TortoiseSVN to set the filedates to the last commit time
111 + when doing a checkout or an update. Otherwise TortoiseSVN will use
112 + the current date.
113 + </para>
114 + </listitem>
115 + </varlistentry>
116 +
117 + <varlistentry>
118 + <term>Close windows automatically</term>
119 + <listitem>
120 + <para>
121 + TortoiseSVN will automatically close all progress dialogs when the action is finished.
122 + </para>
123 </listitem>
124 </varlistentry>
125
126 @@ -1629,15 +1659,15 @@
127 </varlistentry>
128
129 <varlistentry>
130 - <term>Set filedates to "last commit time"</term>
131 - <listitem>
132 - <para>
133 - This option tells TortoiseSVN to set the filedates to the last commit time
134 - when doing a checkout or an update. Otherwise TortoiseSVN will use
135 - the current date.
136 + <term>Minimum logsize in chars</term>
137 + <listitem>
138 + <para>
139 + The minimum length of a log message for a commit. If you enter
140 + a shorter message than specified here, the commit is disabled.
141 </para>
142 </listitem>
143 </varlistentry>
144 +
145 <varlistentry>
146 <term>Don't remove log messages when cancelling a commit</term>
147 <listitem>
148 @@ -1648,11 +1678,14 @@
149 </para>
150 </listitem>
151 </varlistentry>
152 +
153 <varlistentry>
154 - <term>Close windows automatically</term>
155 - <listitem>
156 - <para>
157 - TortoiseSVN will automatically close all progress dialogs when the action is finished.
158 + <term>Show BugID/Issue-Nr. Box</term>
159 + <listitem>
160 + <para>
161 + Shows a textbox in the commit dialog where you can enter
162 + a BugID or Issue-Nr. from a bugtracker to associate the
163 + commit with that ID/number.
164 </para>
165 </listitem>
166 </varlistentry>
167 @@ -1673,10 +1706,32 @@
168 Sie können auch alle überlagerten Icons deaktivieren, aber wo liegt der Spaß darin?
169 </para>
170 <para>
171 + Die <term>Ausschluss Pfade</term> sagen TortoiseSVN für welche
172 + Pfade die überlagerten Icons <emphasis>nicht</emphasis> gezeichnet
173 + werden sollen. Dies ist nützlich wenn Sie zum Beispiel sehr grosse
174 + Arbeitskopien haben, welche grosse externe Bibliotheken, welche Sie
175 + selbst nie ändern werden enthalten. Sie können dann diese Pfade
176 + ausschliessen. Zum Beispiel:
177 + </para>
178 + <para>
179 + <filename>f:\development\SVN\Subversion</filename> deaktiviert
180 + die überlagerten Icons <emphasis>nur</emphasis> für diesen speziellen
181 + Ordner. Sie können die Icons noch immer für alle Dateien und Ordner
182 + innerhalb sehen.
183 + </para>
184 + <para>
185 + <filename>f:\development\SVN\Subversion*</filename> deaktiviert die
186 + überlagerten Icons für <emphasis>alle</emphasis> Dateien und Ordner
187 + welcher Pfad mit <filename>f:\development\SVN\Subversion</filename>
188 + beginnt. Das bedeutet dass auch für alle Dateien und Ordner innerhalb
189 + keine überlagerten Icons angezeigt werden.
190 + </para>
191 + <para>
192 Ausserdem können Sie angeben, welche Befehle im
193 Hauptkontextmenu des Explorer angezeigt werden sollen und welche
194 Sie lieber im Untermenu haben wollen.
195 </para>
196 + </sect2>
197 <sect2 id="tsvn-DUG-settings-network">
198 <?dbhh topicname="HIDD_SETTINGSPROXY"?>
199 <title>Der Einstellungsdialog, Netzwerkseite</title>
200 Index: trunk/doc/source/en/tsvn_ch04.xml
201 ===================================================================
202 diff --git a/trunk/doc/source/en/tsvn_ch04.xml b/trunk/doc/source/en/tsvn_ch04.xml
203 --- a/trunk/doc/source/en/tsvn_ch04.xml (revision 1487)
204 +++ b/trunk/doc/source/en/tsvn_ch04.xml (revision 1488)
205 @@ -1457,7 +1457,7 @@
206 <varlistentry>
207 <term>Language</term>
208 <listitem>
209 - <para>Selects your user interface language. What did you expect?</para>
210 + <para>Selects your user interface language. What else did you expect?</para>
211 </listitem>
212 </varlistentry>
213
214 @@ -1468,6 +1468,9 @@
215 <indexterm>
216 <primary>exclude pattern</primary>
217 </indexterm>
218 + Exclude patterns are used to prevent unversioned files from
219 + showing up e.g. in the commit dialog. Files matching the
220 + patterns are also ignored by an import.
221 Exclude files or directories by typing in the names or extensions. Patterns are separated by spaces
222 e.g. <literal>bin obj *.bak *.~?? *.jar *.[Tt]mp</literal>. The first two entries refer to directories, the
223 other four to files.
224 @@ -1499,23 +1502,16 @@
225 </varlistentry>
226
227 <varlistentry>
228 + <term>Edit...</term>
229 + <listitem>
230 + <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
231 + </listitem>
232 + </varlistentry>
233 +
234 + <varlistentry>
235 <term>Short date / time format in log messages</term>
236 <listitem>
237 <para>If the standard long messages use up too much space on your sceen use the short format.</para>
238 - </listitem>
239 - </varlistentry>
240 -
241 - <varlistentry>
242 - <term>Edit...</term>
243 - <listitem>
244 - <para>... the subversion configuration file directly. Some settings cannot be modified by TortoiseSVN.</para>
245 - </listitem>
246 - </varlistentry>
247 -
248 - <varlistentry>
249 - <term>Check for newer versions</term>
250 - <listitem>
251 - <para>If checked, TortoiseSVN will check once a week if an update is available</para>
252 </listitem>
253 </varlistentry>
254
255 @@ -1529,6 +1525,33 @@
256 </para>
257 </listitem>
258 </varlistentry>
259 +
260 + <varlistentry>
261 + <term>Close windows automatically</term>
262 + <listitem>
263 + <para>
264 + TortoiseSVN will automatically close all progress dialogs when the action is finished.
265 + </para>
266 + </listitem>
267 + </varlistentry>
268 +
269 + <varlistentry>
270 + <term>Check for newer versions</term>
271 + <listitem>
272 + <para>If checked, TortoiseSVN will check once a week if an update is available</para>
273 + </listitem>
274 + </varlistentry>
275 +
276 + <varlistentry>
277 + <term>Minimum logsize in chars</term>
278 + <listitem>
279 + <para>
280 + The minimum length of a log message for a commit. If you enter
281 + a shorter message than specified here, the commit is disabled.
282 + </para>
283 + </listitem>
284 + </varlistentry>
285 +
286 <varlistentry>
287 <term>Don't remove log messages when cancelling a commit</term>
288 <listitem>
289 @@ -1539,11 +1562,14 @@
290 </para>
291 </listitem>
292 </varlistentry>
293 +
294 <varlistentry>
295 - <term>Close windows automatically</term>
296 - <listitem>
297 - <para>
298 - TortoiseSVN will automatically close all progress dialogs when the action is finished.
299 + <term>Show BugID/Issue-Nr. Box</term>
300 + <listitem>
301 + <para>
302 + Shows a textbox in the commit dialog where you can enter
303 + a BugID or Issue-Nr. from a bugtracker to associate the
304 + commit with that ID/number.
305 </para>
306 </listitem>
307 </varlistentry>
308 @@ -1552,7 +1578,7 @@
309 </sect2>
310 <sect2 id="tsvn-DUG-settings-overlay">
311 <?dbhh topicname="HIDD_SETTINGSOVERLAY"?>
312 - <title>The Settings Dialog, Overlay Tab</title>
313 + <title>The Settings Dialog, Look and Feel Tab</title>
314 <para>
315 <figure id="tsvn-DUG-settings-dia-2">
316 <title>The Settings Dialog, Overlay Tab</title>
317 @@ -1560,8 +1586,27 @@
318 </figure>
319 This tab allows you to choose, for which items TortoiseSVN shall
320 display icon overlays. If you feel that your icon overlays are very
321 - slow (explore is not responsive), uncheck the "show changed directories" box.
322 + slow (explorer is not responsive), uncheck the "show changed directories" box.
323 You can even disable all icon overlays, but where's the fun in that?
324 + </para>
325 + <para>
326 + The <term>Exclude Paths</term> are used to tell TortoiseSVN for which
327 + paths <emphasis>not</emphasis> to show icon overlays and status columns.
328 + This is useful if you have some very big working copies containing
329 + only libraries which you won't change at all and therefore don't
330 + need the overlays. For example:
331 + </para>
332 + <para>
333 + <filename>f:\development\SVN\Subversion</filename> will disable
334 + the overlays on <emphasis>only</emphasis> that specific folder. You
335 + still can see the overlays on all files and folder inside that folder.
336 + </para>
337 + <para>
338 + <filename>f:\development\SVN\Subversion*</filename> will disable the
339 + overlays on <emphasis>all</emphasis> files and folders which path
340 + starts with <filename>f:\development\SVN\Subversion</filename>. That
341 + means you won't see overlays for all files and folder below that
342 + path.
343 </para>
344 <para>
345 You can also specifiy here which of the TortoiseSVN contex menu
346 Index: trunk/src/Changelog.txt
347 ===================================================================
348 diff --git a/trunk/src/Changelog.txt b/trunk/src/Changelog.txt
349 --- a/trunk/src/Changelog.txt (revision 1487)
350 +++ b/trunk/src/Changelog.txt (revision 1488)
351 @@ -1,3 +1,5 @@
352 +- ADD: Option to exclude specific paths from showing
353 + icon overlays. (Stefan)
354 - ADD: On Win2k and later, the authentication data is now
355 encrypted before saved. The encryption is not available
356 for the other OS's. (Stefan)
357 Index: trunk/src/Resources/TortoiseProcENG.rc
358 ===================================================================
359 diff --git a/trunk/src/Resources/TortoiseProcENG.rc b/trunk/src/Resources/TortoiseProcENG.rc
360 --- a/trunk/src/Resources/TortoiseProcENG.rc (revision 1487)
361 +++ b/trunk/src/Resources/TortoiseProcENG.rc (revision 1488)
362 @@ -398,27 +398,31 @@
363 BEGIN
364 CONTROL "&Indicate folders with changed contents",
365 IDC_CHANGEDDIRS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,
366 - 20,145,10
367 + 20,206,10
368 CONTROL "&Removable drives",IDC_REMOVABLE,"Button",
369 - BS_AUTOCHECKBOX | WS_TABSTOP,18,66,130,10
370 + BS_AUTOCHECKBOX | WS_TABSTOP,18,58,130,10
371 CONTROL "&Network drives",IDC_NETWORK,"Button",BS_AUTOCHECKBOX |
372 - WS_TABSTOP,18,76,130,10
373 + WS_TABSTOP,18,68,130,10
374 CONTROL "&Fixed drives",IDC_FIXED,"Button",BS_AUTOCHECKBOX |
375 - WS_TABSTOP,18,87,127,10
376 + WS_TABSTOP,18,79,127,10
377 CONTROL "&CD-ROM",IDC_CDROM,"Button",BS_AUTOCHECKBOX |
378 - WS_TABSTOP,159,66,118,10
379 - GROUPBOX "Drive Types",IDC_DRIVEGROUP,12,52,274,50
380 + WS_TABSTOP,166,58,118,10
381 + GROUPBOX "Drive Types",IDC_DRIVEGROUP,12,44,274,50
382 CONTROL "RAM drives",IDC_RAM,"Button",BS_AUTOCHECKBOX |
383 - WS_TABSTOP,159,76,119,10
384 + WS_TABSTOP,166,68,119,10
385 CONTROL "Unknown drives",IDC_UNKNOWN,"Button",BS_AUTOCHECKBOX |
386 - WS_TABSTOP,159,86,118,10
387 + WS_TABSTOP,166,78,118,10
388 CONTROL "Show overlays only in explorer",IDC_ONLYEXPLORER,"Button",
389 - BS_AUTOCHECKBOX | WS_TABSTOP,12,33,122,10
390 - GROUPBOX "Icon Overlays / Status Columns",IDC_STATIC,7,7,286,103
391 - GROUPBOX "Context Menu",IDC_STATIC,7,113,286,97
392 + BS_AUTOCHECKBOX | WS_TABSTOP,12,33,190,10
393 + GROUPBOX "Icon Overlays / Status Columns",IDC_STATIC,7,7,286,118
394 + GROUPBOX "Context Menu",IDC_STATIC,7,130,286,80
395 CONTROL "",IDC_MENULIST,"SysListView32",LVS_REPORT |
396 LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER |
397 - WS_BORDER | WS_TABSTOP,12,125,274,78
398 + WS_BORDER | WS_TABSTOP,12,140,274,63
399 + LTEXT "Exclude paths:",IDC_STATIC,12,106,85,8
400 + EDITTEXT IDC_EXCLUDEPATHS,102,96,184,25,ES_MULTILINE |
401 + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN |
402 + WS_VSCROLL
403 END
404
405 IDD_SETTINGSPROXY DIALOGEX 0, 0, 300, 217
406 @@ -860,7 +864,7 @@
407 RIGHTMARGIN, 293
408 VERTGUIDE, 12
409 VERTGUIDE, 18
410 - VERTGUIDE, 159
411 + VERTGUIDE, 166
412 VERTGUIDE, 286
413 TOPMARGIN, 7
414 BOTTOMMARGIN, 210
415 @@ -1377,6 +1381,8 @@
416 "If activated, prevents the overlays from showing in ""save as.."" or ""open"" dialogs"
417 IDS_SETTINGS_MENULAYOUT_TT
418 "Check those menu entries you want to appear in the top context menu instead of the submenu"
419 + IDS_SETTINGS_EXCLUDELIST_TT
420 + "A newline separated list of paths for which no icon overlays are shown.\nIf you add an ""*"" char at the end of a path, then all files and subdirs inside that path are excluded too.\nAn empty list will allow overlays on all paths."
421 END
422
423 STRINGTABLE
424 Index: trunk/src/TortoiseProc/SetOverlayPage.cpp
425 ===================================================================
426 diff --git a/trunk/src/TortoiseProc/SetOverlayPage.cpp b/trunk/src/TortoiseProc/SetOverlayPage.cpp
427 --- a/trunk/src/TortoiseProc/SetOverlayPage.cpp (revision 1487)
428 +++ b/trunk/src/TortoiseProc/SetOverlayPage.cpp (revision 1488)
429 @@ -20,6 +20,7 @@
430 #include "TortoiseProc.h"
431 #include "SetOverlayPage.h"
432 #include "Globals.h"
433 +#include ".\setoverlaypage.h"
434
435
436 // CSetOverlayPage dialog
437 @@ -35,6 +36,7 @@
438 , m_bRAM(FALSE)
439 , m_bUnknown(FALSE)
440 , m_bOnlyExplorer(FALSE)
441 + , m_sExcludePaths(_T(""))
442 {
443 m_regShowChangedDirs = CRegDWORD(_T("Software\\TortoiseSVN\\RecursiveOverlay"));
444 m_regOnlyExplorer = CRegDWORD(_T("Software\\TortoiseSVN\\OverlaysOnlyInExplorer"), FALSE);
445 @@ -45,6 +47,7 @@
446 m_regDriveMaskRAM = CRegDWORD(_T("Software\\TortoiseSVN\\DriveMaskRAM"));
447 m_regDriveMaskUnknown = CRegDWORD(_T("Software\\TortoiseSVN\\DriveMaskUnknown"));
448 m_regTopmenu = CRegDWORD(_T("Software\\TortoiseSVN\\ContextMenuEntries"), MENUCHECKOUT | MENUUPDATE | MENUCOMMIT);
449 + m_regExcludePaths = CRegString(_T("Software\\TortoiseSVN\\OverlayExcludeList"));
450
451 m_bShowChangedDirs = m_regShowChangedDirs;
452 m_bOnlyExplorer = m_regOnlyExplorer;
453 @@ -55,6 +58,8 @@
454 m_bRAM = m_regDriveMaskRAM;
455 m_bUnknown = m_regDriveMaskUnknown;
456 m_topmenu = m_regTopmenu;
457 + m_sExcludePaths = m_regExcludePaths;
458 + m_sExcludePaths.Replace(_T("\n"), _T("\r\n"));
459 }
460
461 CSetOverlayPage::~CSetOverlayPage()
462 @@ -74,6 +79,7 @@
463 DDX_Control(pDX, IDC_DRIVEGROUP, m_cDriveGroup);
464 DDX_Check(pDX, IDC_ONLYEXPLORER, m_bOnlyExplorer);
465 DDX_Control(pDX, IDC_MENULIST, m_cMenuList);
466 + DDX_Text(pDX, IDC_EXCLUDEPATHS, m_sExcludePaths);
467 }
468
469
470 @@ -87,6 +93,7 @@
471 ON_BN_CLICKED(IDC_RAM, OnBnClickedRam)
472 ON_BN_CLICKED(IDC_ONLYEXPLORER, OnBnClickedOnlyexplorer)
473 ON_NOTIFY(LVN_ITEMCHANGED, IDC_MENULIST, OnLvnItemchangedMenulist)
474 + ON_EN_CHANGE(IDC_EXCLUDEPATHS, OnEnChangeExcludepaths)
475 END_MESSAGE_MAP()
476
477
478 @@ -103,6 +110,9 @@
479 m_regDriveMaskRAM = m_bRAM;
480 m_regDriveMaskUnknown = m_bUnknown;
481 m_regTopmenu = m_topmenu;
482 + m_sExcludePaths.Replace(_T("\r"), _T(""));
483 + m_regExcludePaths = m_sExcludePaths;
484 + m_sExcludePaths.Replace(_T("\n"), _T("\r\n"));
485 }
486 }
487
488 @@ -116,7 +126,7 @@
489 m_tooltips.AddTool(IDC_CHANGEDDIRS, IDS_SETTINGS_CHANGEDDIRS_TT);
490 m_tooltips.AddTool(IDC_ONLYEXPLORER, IDS_SETTINGS_ONLYEXPLORER_TT);
491 m_tooltips.AddTool(IDC_MENULIST, IDS_SETTINGS_MENULAYOUT_TT);
492 -
493 + m_tooltips.AddTool(IDC_EXCLUDEPATHS, IDS_SETTINGS_EXCLUDELIST_TT);
494
495 m_cMenuList.SetExtendedStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
496
497 @@ -280,3 +290,8 @@
498 } // if (m_cMenuList.GetItemCount() > 0)
499 *pResult = 0;
500 }
501 +
502 +void CSetOverlayPage::OnEnChangeExcludepaths()
503 +{
504 + SetModified();
505 +}
506 Index: trunk/src/TortoiseProc/SetOverlayPage.h
507 ===================================================================
508 diff --git a/trunk/src/TortoiseProc/SetOverlayPage.h b/trunk/src/TortoiseProc/SetOverlayPage.h
509 --- a/trunk/src/TortoiseProc/SetOverlayPage.h (revision 1487)
510 +++ b/trunk/src/TortoiseProc/SetOverlayPage.h (revision 1488)
511 @@ -92,6 +92,8 @@
512 CIconStatic m_cDriveGroup;
513 BOOL m_bInitialized;
514 CRegDWORD m_regTopmenu;
515 + CRegString m_regExcludePaths;
516 + CString m_sExcludePaths;
517
518 CImageList m_imgList;
519 CListCtrl m_cMenuList;
520 @@ -110,4 +112,5 @@
521 virtual BOOL OnApply();
522 afx_msg void OnBnClickedOnlyexplorer();
523 afx_msg void OnLvnItemchangedMenulist(NMHDR *pNMHDR, LRESULT *pResult);
524 + afx_msg void OnEnChangeExcludepaths();
525 };
526 Index: trunk/src/TortoiseProc/resource.h
527 ===================================================================
528 diff --git a/trunk/src/TortoiseProc/resource.h b/trunk/src/TortoiseProc/resource.h
529 --- a/trunk/src/TortoiseProc/resource.h (revision 1487)
530 +++ b/trunk/src/TortoiseProc/resource.h (revision 1488)
531 @@ -179,6 +179,7 @@
532 #define IDC_MINLOGSIZE 1077
533 #define IDC_BUGID 1077
534 #define IDC_WCURL 1077
535 +#define IDC_EXCLUDEPATHS 1077
536 #define IDC_DRIVEGROUP 1079
537 #define IDC_PROXYGROUP 1080
538 #define IDC_SSHGROUP 1081
539 @@ -427,6 +428,7 @@
540 #define IDS_SETTINGS_CHECKNEWER_TT 3100
541 #define IDS_SETTINGS_ONLYEXPLORER_TT 3101
542 #define IDS_SETTINGS_MENULAYOUT_TT 3102
543 +#define IDS_SETTINGS_EXCLUDELIST_TT 3103
544 #define IDS_CHECKNEWER_YOURVERSION 3200
545 #define IDS_CHECKNEWER_CURRENTVERSION 3201
546 #define IDS_CHECKNEWER_YOURUPTODATE 3202
547 Index: trunk/src/TortoiseShell/ShellCache.h
548 ===================================================================
549 diff --git a/trunk/src/TortoiseShell/ShellCache.h b/trunk/src/TortoiseShell/ShellCache.h
550 --- a/trunk/src/TortoiseShell/ShellCache.h (revision 1487)
551 +++ b/trunk/src/TortoiseShell/ShellCache.h (revision 1488)
552 @@ -21,9 +21,11 @@
553 #include "globals.h"
554 #include <tchar.h>
555 #include <string>
556 +#include <vector>
557 #include "registry.h"
558
559 #define REGISTRYTIMEOUT 2000
560 +#define EXCLUDELISTTIMEOUT 5000
561 #define DRIVETYPETIMEOUT 300000 // 5 min
562 #define NUMBERFMTTIMEOUT 300000
563 class ShellCache
564 @@ -39,12 +41,14 @@
565 driveremove = CRegStdWORD(_T("Software\\TortoiseSVN\\DriveMaskRemovable"));
566 driveram = CRegStdWORD(_T("Software\\TortoiseSVN\\DriveMaskRAM"));
567 driveunknown = CRegStdWORD(_T("Software\\TortoiseSVN\\DriveMaskUnknown"));
568 + excludelist = CRegStdString(_T("Software\\TortoiseSVN\\OverlayExcludeList"));
569 recursiveticker = GetTickCount();
570 folderoverlayticker = GetTickCount();
571 driveticker = recursiveticker;
572 drivetypeticker = recursiveticker;
573 langticker = recursiveticker;
574 - columnrevformatticker = langticker;
575 + columnrevformatticker = recursiveticker;
576 + excludelistticker = recursiveticker;
577 menulayout = CRegStdWORD(_T("Software\\TortoiseSVN\\ContextMenuEntries"), MENUCHECKOUT | MENUUPDATE | MENUCOMMIT);
578 langid = CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), 1033);
579 blockstatus = CRegStdWORD(_T("Software\\TortoiseSVN\\BlockStatus"), 0);
580 @@ -177,6 +181,21 @@
581 return FALSE;
582 if ((drivetype == DRIVE_UNKNOWN)&&(IsUnknown()))
583 return FALSE;
584 +
585 + ExcludeListValid();
586 + for (std::vector<stdstring>::iterator I = exvector.begin(); I != exvector.end(); ++I)
587 + {
588 + if (I->empty())
589 + continue;
590 + if (I->at(I->size()-1)=='*')
591 + {
592 + stdstring str = I->substr(0, I->size()-1);
593 + if (_tcsnicmp(str.c_str(), path, str.size())==0)
594 + return FALSE;
595 + }
596 + else if (_tcsicmp(I->c_str(), path)==0)
597 + return FALSE;
598 + }
599 return TRUE;
600 }
601 DWORD GetLangID()
602 @@ -218,6 +237,32 @@
603 driveremove.read();
604 }
605 }
606 + void ExcludeListValid()
607 + {
608 + if ((GetTickCount() - EXCLUDELISTTIMEOUT)>excludelistticker)
609 + {
610 + excludelistticker = GetTickCount();
611 + excludelist.read();
612 + if (excludeliststr.compare((stdstring)excludelist)==0)
613 + return;
614 + excludeliststr = (stdstring)excludelist;
615 + exvector.clear();
616 + int pos = 0, pos_ant = 0;
617 + pos = excludeliststr.find(_T("\n"), pos_ant);
618 + while (pos != stdstring::npos)
619 + {
620 + stdstring token = excludeliststr.substr(pos_ant, pos-pos_ant);
621 + exvector.push_back(token);
622 + pos_ant = pos+1;
623 + pos = excludeliststr.find(_T("\n"), pos_ant);
624 + }
625 + if (!excludeliststr.empty())
626 + {
627 + exvector.push_back(excludeliststr.substr(pos_ant, excludeliststr.size()-1));
628 + }
629 + excludeliststr = (stdstring)excludelist;
630 + }
631 + }
632 CRegStdWORD blockstatus;
633 CRegStdWORD langid;
634 CRegStdWORD showrecursive;
635 @@ -229,6 +274,9 @@
636 CRegStdWORD driveram;
637 CRegStdWORD driveunknown;
638 CRegStdWORD menulayout;
639 + CRegStdString excludelist;
640 + stdstring excludeliststr;
641 + std::vector<stdstring> exvector;
642 DWORD recursiveticker;
643 DWORD folderoverlayticker;
644 DWORD driveticker;
645 @@ -237,6 +285,7 @@
646 DWORD langticker;
647 DWORD blockstatusticker;
648 DWORD columnrevformatticker;
649 + DWORD excludelistticker;
650 UINT drivetypecache[27];
651 TCHAR drivetypepathcache[MAX_PATH];
652 NUMBERFMT columnrevformat; No newline at end of file
@@ -0,0 +1,192 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import os
22
23 import mock
24 import pytest
25
26 from rhodecode.controllers.files import FilesController
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.compat import OrderedDict
29 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.vcs import nodes
31 from rhodecode.lib.vcs.backends.base import EmptyCommit
32 from rhodecode.lib.vcs.conf import settings
33 from rhodecode.lib.vcs.nodes import FileNode
34 from rhodecode.model.db import Repository
35 from rhodecode.model.scm import ScmModel
36 from rhodecode.tests import (
37 url, TEST_USER_ADMIN_LOGIN, assert_session_flash, assert_not_in_session_flash)
38 from rhodecode.tests.fixture import Fixture
39 from rhodecode.tests.utils import commit_change
40
41 fixture = Fixture()
42
43
44 @pytest.mark.usefixtures("autologin_user", "app")
45 class TestSideBySideDiff(object):
46
47 def test_diff_side_by_side(self, app, backend, backend_stub):
48 f_path = 'test_sidebyside_file.py'
49 commit1_content = 'content-25d7e49c18b159446c\n'
50 commit2_content = 'content-603d6c72c46d953420\n'
51 repo = backend.create_repo()
52
53 commit1 = commit_change(
54 repo.repo_name, filename=f_path, content=commit1_content,
55 message='A', vcs_type=backend.alias, parent=None, newfile=True)
56
57 commit2 = commit_change(
58 repo.repo_name, filename=f_path, content=commit2_content,
59 message='B, child of A', vcs_type=backend.alias, parent=commit1)
60
61 compare_url = url(
62 'compare_url',
63 repo_name=repo.repo_name,
64 source_ref_type='rev',
65 source_ref=commit1.raw_id,
66 target_repo=repo.repo_name,
67 target_ref_type='rev',
68 target_ref=commit2.raw_id,
69 f_path=f_path,
70 diffmode='sidebyside')
71
72 response = self.app.get(compare_url)
73
74 response.mustcontain('Expand 1 commit')
75 response.mustcontain('1 file changed')
76
77 response.mustcontain(
78 'r%s:%s...r%s:%s' % (
79 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
80
81 response.mustcontain('<strong>{}</strong>'.format(f_path))
82
83 def test_diff_side_by_side_with_empty_file(self, app, backend, backend_stub):
84 commits = [
85 {'message': 'First commit'},
86 {'message': 'Commit with binary',
87 'added': [nodes.FileNode('file.empty', content='')]},
88 ]
89 f_path = 'file.empty'
90 repo = backend.create_repo(commits=commits)
91 commit1 = repo.get_commit(commit_idx=0)
92 commit2 = repo.get_commit(commit_idx=1)
93
94 compare_url = url(
95 'compare_url',
96 repo_name=repo.repo_name,
97 source_ref_type='rev',
98 source_ref=commit1.raw_id,
99 target_repo=repo.repo_name,
100 target_ref_type='rev',
101 target_ref=commit2.raw_id,
102 f_path=f_path,
103 diffmode='sidebyside')
104
105 response = self.app.get(compare_url)
106
107 response.mustcontain('Expand 1 commit')
108 response.mustcontain('1 file changed')
109
110 response.mustcontain(
111 'r%s:%s...r%s:%s' % (
112 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
113
114 response.mustcontain('<strong>{}</strong>'.format(f_path))
115
116 def test_diff_sidebyside_two_commits(self, app, backend):
117 commit_id_range = {
118 'hg': {
119 'commits': ['25d7e49c18b159446cadfa506a5cf8ad1cb04067',
120 '603d6c72c46d953420c89d36372f08d9f305f5dd'],
121 'changes': '21 files changed: 943 inserted, 288 deleted'
122 },
123 'git': {
124 'commits': ['6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
125 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
126 'changes': '21 files changed: 943 inserted, 288 deleted'
127 },
128
129 'svn': {
130 'commits': ['336',
131 '337'],
132 'changes': '21 files changed: 943 inserted, 288 deleted'
133 },
134 }
135
136 commit_info = commit_id_range[backend.alias]
137 commit2, commit1 = commit_info['commits']
138 file_changes = commit_info['changes']
139
140 compare_url = url(
141 'compare_url',
142 repo_name=backend.repo_name,
143 source_ref_type='rev',
144 source_ref=commit2,
145 target_repo=backend.repo_name,
146 target_ref_type='rev',
147 target_ref=commit1,
148 diffmode='sidebyside')
149 response = self.app.get(compare_url)
150
151 response.mustcontain('Expand 1 commit')
152 response.mustcontain(file_changes)
153
154 def test_diff_sidebyside_two_commits_single_file(self, app, backend):
155 commit_id_range = {
156 'hg': {
157 'commits': ['25d7e49c18b159446cadfa506a5cf8ad1cb04067',
158 '603d6c72c46d953420c89d36372f08d9f305f5dd'],
159 'changes': '1 file changed: 1 inserted, 1 deleted'
160 },
161 'git': {
162 'commits': ['6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
163 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
164 'changes': '1 file changed: 1 inserted, 1 deleted'
165 },
166
167 'svn': {
168 'commits': ['336',
169 '337'],
170 'changes': '1 file changed: 1 inserted, 1 deleted'
171 },
172 }
173 f_path = 'docs/conf.py'
174
175 commit_info = commit_id_range[backend.alias]
176 commit2, commit1 = commit_info['commits']
177 file_changes = commit_info['changes']
178
179 compare_url = url(
180 'compare_url',
181 repo_name=backend.repo_name,
182 source_ref_type='rev',
183 source_ref=commit2,
184 target_repo=backend.repo_name,
185 target_ref_type='rev',
186 target_ref=commit1,
187 f_path=f_path,
188 diffmode='sidebyside')
189 response = self.app.get(compare_url)
190
191 response.mustcontain('Expand 1 commit')
192 response.mustcontain(file_changes)
@@ -1066,7 +1066,7 b' def make_map(config):'
1066 '/{repo_name}/annotate/{revision}/{f_path}',
1066 '/{repo_name}/annotate/{revision}/{f_path}',
1067 controller='files', action='index', revision='tip',
1067 controller='files', action='index', revision='tip',
1068 f_path='', annotate=True, conditions={'function': check_repo},
1068 f_path='', annotate=True, conditions={'function': check_repo},
1069 requirements=URL_NAME_REQUIREMENTS)
1069 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1070
1070
1071 rmap.connect('files_edit',
1071 rmap.connect('files_edit',
1072 '/{repo_name}/edit/{revision}/{f_path}',
1072 '/{repo_name}/edit/{revision}/{f_path}',
@@ -90,6 +90,7 b' class CompareController(BaseRepoControll'
90 c.target_ref_type = ""
90 c.target_ref_type = ""
91 c.commit_statuses = ChangesetStatus.STATUSES
91 c.commit_statuses = ChangesetStatus.STATUSES
92 c.preview_mode = False
92 c.preview_mode = False
93 c.file_path = None
93 return render('compare/compare_diff.html')
94 return render('compare/compare_diff.html')
94
95
95 @LoginRequired()
96 @LoginRequired()
@@ -103,8 +104,10 b' class CompareController(BaseRepoControll'
103
104
104 # target_ref will be evaluated in target_repo
105 # target_ref will be evaluated in target_repo
105 target_repo_name = request.GET.get('target_repo', source_repo_name)
106 target_repo_name = request.GET.get('target_repo', source_repo_name)
106 target_path, target_id = parse_path_ref(target_ref)
107 target_path, target_id = parse_path_ref(
108 target_ref, default_path=request.GET.get('f_path', ''))
107
109
110 c.file_path = target_path
108 c.commit_statuses = ChangesetStatus.STATUSES
111 c.commit_statuses = ChangesetStatus.STATUSES
109
112
110 # if merge is True
113 # if merge is True
@@ -115,7 +118,6 b' class CompareController(BaseRepoControll'
115 # if merge is False
118 # if merge is False
116 # Show a raw diff of source/target refs even if no ancestor exists
119 # Show a raw diff of source/target refs even if no ancestor exists
117
120
118
119 # c.fulldiff disables cut_off_limit
121 # c.fulldiff disables cut_off_limit
120 c.fulldiff = str2bool(request.GET.get('fulldiff'))
122 c.fulldiff = str2bool(request.GET.get('fulldiff'))
121
123
@@ -131,7 +133,8 b' class CompareController(BaseRepoControll'
131 target_repo=source_repo_name,
133 target_repo=source_repo_name,
132 target_ref_type=source_ref_type,
134 target_ref_type=source_ref_type,
133 target_ref=source_ref,
135 target_ref=source_ref,
134 merge=merge and '1' or '')
136 merge=merge and '1' or '',
137 f_path=target_path)
135
138
136 source_repo = Repository.get_by_repo_name(source_repo_name)
139 source_repo = Repository.get_by_repo_name(source_repo_name)
137 target_repo = Repository.get_by_repo_name(target_repo_name)
140 target_repo = Repository.get_by_repo_name(target_repo_name)
@@ -151,8 +154,11 b' class CompareController(BaseRepoControll'
151 h.flash(msg, category='error')
154 h.flash(msg, category='error')
152 return redirect(url('compare_home', repo_name=c.repo_name))
155 return redirect(url('compare_home', repo_name=c.repo_name))
153
156
154 source_alias = source_repo.scm_instance().alias
157 source_scm = source_repo.scm_instance()
155 target_alias = target_repo.scm_instance().alias
158 target_scm = target_repo.scm_instance()
159
160 source_alias = source_scm.alias
161 target_alias = target_scm.alias
156 if source_alias != target_alias:
162 if source_alias != target_alias:
157 msg = _('The comparison of two different kinds of remote repos '
163 msg = _('The comparison of two different kinds of remote repos '
158 'is not available')
164 'is not available')
@@ -175,9 +181,6 b' class CompareController(BaseRepoControll'
175 c.source_ref_type = source_ref_type
181 c.source_ref_type = source_ref_type
176 c.target_ref_type = target_ref_type
182 c.target_ref_type = target_ref_type
177
183
178 source_scm = source_repo.scm_instance()
179 target_scm = target_repo.scm_instance()
180
181 pre_load = ["author", "branch", "date", "message"]
184 pre_load = ["author", "branch", "date", "message"]
182 c.ancestor = None
185 c.ancestor = None
183 try:
186 try:
@@ -199,9 +202,9 b' class CompareController(BaseRepoControll'
199 c.statuses = c.rhodecode_db_repo.statuses(
202 c.statuses = c.rhodecode_db_repo.statuses(
200 [x.raw_id for x in c.commit_ranges])
203 [x.raw_id for x in c.commit_ranges])
201
204
202 if partial: # for PR ajax commits loader
205 if partial: # for PR ajax commits loader
203 if not c.ancestor:
206 if not c.ancestor:
204 return '' # cannot merge if there is no ancestor
207 return '' # cannot merge if there is no ancestor
205 return render('compare/compare_commits.html')
208 return render('compare/compare_commits.html')
206
209
207 if c.ancestor:
210 if c.ancestor:
@@ -238,7 +241,8 b' class CompareController(BaseRepoControll'
238
241
239 txtdiff = source_repo.scm_instance().get_diff(
242 txtdiff = source_repo.scm_instance().get_diff(
240 commit1=source_commit, commit2=target_commit,
243 commit1=source_commit, commit2=target_commit,
241 path1=source_path, path=target_path)
244 path=target_path, path1=source_path)
245
242 diff_processor = diffs.DiffProcessor(
246 diff_processor = diffs.DiffProcessor(
243 txtdiff, format='newdiff', diff_limit=diff_limit,
247 txtdiff, format='newdiff', diff_limit=diff_limit,
244 file_limit=file_limit, show_full_diff=c.fulldiff)
248 file_limit=file_limit, show_full_diff=c.fulldiff)
@@ -260,5 +264,7 b' class CompareController(BaseRepoControll'
260 ).render_patchset(_parsed, source_ref, target_ref)
264 ).render_patchset(_parsed, source_ref, target_ref)
261
265
262 c.preview_mode = merge
266 c.preview_mode = merge
267 c.source_commit = source_commit
268 c.target_commit = target_commit
263
269
264 return render('compare/compare_diff.html')
270 return render('compare/compare_diff.html')
@@ -799,21 +799,15 b' class FilesController(BaseRepoController'
799 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
799 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
800 'repository.admin')
800 'repository.admin')
801 def diff(self, repo_name, f_path):
801 def diff(self, repo_name, f_path):
802 ignore_whitespace = request.GET.get('ignorews') == '1'
802
803 line_context = request.GET.get('context', 3)
803 c.action = request.GET.get('diff')
804 diff1 = request.GET.get('diff1', '')
804 diff1 = request.GET.get('diff1', '')
805 diff2 = request.GET.get('diff2', '')
805
806
806 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
807 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
807
808
808 diff2 = request.GET.get('diff2', '')
809 ignore_whitespace = str2bool(request.GET.get('ignorews'))
809 c.action = request.GET.get('diff')
810 line_context = request.GET.get('context', 3)
810 c.no_changes = diff1 == diff2
811 c.f_path = f_path
812 c.big_diff = False
813 c.ignorews_url = _ignorews_url
814 c.context_url = _context_url
815 c.changes = OrderedDict()
816 c.changes[diff2] = []
817
811
818 if not any((diff1, diff2)):
812 if not any((diff1, diff2)):
819 h.flash(
813 h.flash(
@@ -821,18 +815,16 b' class FilesController(BaseRepoController'
821 category='error')
815 category='error')
822 raise HTTPBadRequest()
816 raise HTTPBadRequest()
823
817
824 # special case if we want a show commit_id only, it's impl here
818 if c.action not in ['download', 'raw']:
825 # to reduce JS and callbacks
819 # redirect to new view if we render diff
826
820 return redirect(
827 if request.GET.get('show_rev') and diff1:
821 url('compare_url', repo_name=repo_name,
828 if str2bool(request.GET.get('annotate', 'False')):
822 source_ref_type='rev',
829 _url = url('files_annotate_home', repo_name=c.repo_name,
823 source_ref=diff1,
830 revision=diff1, f_path=path1)
824 target_repo=c.repo_name,
831 else:
825 target_ref_type='rev',
832 _url = url('files_home', repo_name=c.repo_name,
826 target_ref=diff2,
833 revision=diff1, f_path=path1)
827 f_path=f_path))
834
835 return redirect(_url)
836
828
837 try:
829 try:
838 node1 = self._get_file_node(diff1, path1)
830 node1 = self._get_file_node(diff1, path1)
@@ -877,98 +869,40 b' class FilesController(BaseRepoController'
877 return diff.as_raw()
869 return diff.as_raw()
878
870
879 else:
871 else:
880 fid = h.FID(diff2, node2.path)
872 return redirect(
881 line_context_lcl = get_line_ctx(fid, request.GET)
873 url('compare_url', repo_name=repo_name,
882 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
874 source_ref_type='rev',
883
875 source_ref=diff1,
884 __, commit1, commit2, diff, st, data = diffs.wrapped_diff(
876 target_repo=c.repo_name,
885 filenode_old=node1,
877 target_ref_type='rev',
886 filenode_new=node2,
878 target_ref=diff2,
887 diff_limit=self.cut_off_limit_diff,
879 f_path=f_path))
888 file_limit=self.cut_off_limit_file,
889 show_full_diff=request.GET.get('fulldiff'),
890 ignore_whitespace=ign_whitespace_lcl,
891 line_context=line_context_lcl,)
892
893 c.lines_added = data['stats']['added'] if data else 0
894 c.lines_deleted = data['stats']['deleted'] if data else 0
895 c.files = [data]
896 c.commit_ranges = [c.commit_1, c.commit_2]
897 c.ancestor = None
898 c.statuses = []
899 c.target_repo = c.rhodecode_db_repo
900 c.filename1 = node1.path
901 c.filename = node2.path
902 c.binary_file = node1.is_binary or node2.is_binary
903 operation = data['operation'] if data else ''
904
905 commit_changes = {
906 # TODO: it's passing the old file to the diff to keep the
907 # standard but this is not being used for this template,
908 # but might need both files in the future or a more standard
909 # way to work with that
910 'fid': [commit1, commit2, operation,
911 c.filename, diff, st, data]
912 }
913
914 c.changes = commit_changes
915
916 return render('files/file_diff.html')
917
880
918 @LoginRequired()
881 @LoginRequired()
919 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
882 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
920 'repository.admin')
883 'repository.admin')
921 def diff_2way(self, repo_name, f_path):
884 def diff_2way(self, repo_name, f_path):
885 """
886 Kept only to make OLD links work
887 """
922 diff1 = request.GET.get('diff1', '')
888 diff1 = request.GET.get('diff1', '')
923 diff2 = request.GET.get('diff2', '')
889 diff2 = request.GET.get('diff2', '')
924
890
925 nodes = []
891 if not any((diff1, diff2)):
926 unknown_commits = []
892 h.flash(
927 for commit in [diff1, diff2]:
893 'Need query parameter "diff1" or "diff2" to generate a diff.',
928 try:
894 category='error')
929 nodes.append(self._get_file_node(commit, f_path))
895 raise HTTPBadRequest()
930 except (RepositoryError, NodeError):
931 log.exception('%(commit)s does not exist' % {'commit': commit})
932 unknown_commits.append(commit)
933 h.flash(h.literal(
934 _('Commit %(commit)s does not exist.') % {'commit': commit}
935 ), category='error')
936
937 if unknown_commits:
938 return redirect(url('files_home', repo_name=c.repo_name,
939 f_path=f_path))
940
941 if all(isinstance(node.commit, EmptyCommit) for node in nodes):
942 raise HTTPNotFound
943
944 node1, node2 = nodes
945
896
946 f_gitdiff = diffs.get_gitdiff(node1, node2, ignore_whitespace=False)
897 return redirect(
947 diff_processor = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
898 url('compare_url', repo_name=repo_name,
948 diff_data = diff_processor.prepare()
899 source_ref_type='rev',
949
900 source_ref=diff1,
950 if not diff_data or diff_data[0]['raw_diff'] == '':
901 target_repo=c.repo_name,
951 h.flash(h.literal(_('%(file_path)s has not changed '
902 target_ref_type='rev',
952 'between %(commit_1)s and %(commit_2)s.') % {
903 target_ref=diff2,
953 'file_path': f_path,
904 f_path=f_path,
954 'commit_1': node1.commit.id,
905 diffmode='sideside'))
955 'commit_2': node2.commit.id
956 }), category='error')
957 return redirect(url('files_home', repo_name=c.repo_name,
958 f_path=f_path))
959
960 c.diff_data = diff_data[0]
961 c.FID = h.FID(diff2, node2.path)
962 # cleanup some unneeded data
963 del c.diff_data['raw_diff']
964 del c.diff_data['chunks']
965
966 c.node1 = node1
967 c.commit_1 = node1.commit
968 c.node2 = node2
969 c.commit_2 = node2.commit
970
971 return render('files/diff_2way.html')
972
906
973 def _get_file_node(self, commit_id, f_path):
907 def _get_file_node(self, commit_id, f_path):
974 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
908 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
@@ -27,6 +27,7 b' Should only contain utilities to be shar'
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.vcs.exceptions import RepositoryError
28 from rhodecode.lib.vcs.exceptions import RepositoryError
29
29
30
30 def parse_path_ref(ref, default_path=None):
31 def parse_path_ref(ref, default_path=None):
31 """
32 """
32 Parse out a path and reference combination and return both parts of it.
33 Parse out a path and reference combination and return both parts of it.
@@ -76,8 +77,8 b' def get_commit_from_ref_name(repo, ref_n'
76 }
77 }
77
78
78 commit_id = ref_name
79 commit_id = ref_name
79 if repo_scm.alias != 'svn': # pass svn refs straight to backend until
80 if repo_scm.alias != 'svn': # pass svn refs straight to backend until
80 # the branch issue with svn is fixed
81 # the branch issue with svn is fixed
81 if ref_type and ref_type in ref_type_mapping:
82 if ref_type and ref_type in ref_type_mapping:
82 try:
83 try:
83 commit_id = ref_type_mapping[ref_type][ref_name]
84 commit_id = ref_type_mapping[ref_type][ref_name]
@@ -378,6 +378,7 b' class BaseRepository(object):'
378 parameter works only for backends which support diff generation for
378 parameter works only for backends which support diff generation for
379 different paths. Other backends will raise a `ValueError` if `path1`
379 different paths. Other backends will raise a `ValueError` if `path1`
380 is set and has a different value than `path`.
380 is set and has a different value than `path`.
381 :param file_path: filter this diff by given path pattern
381 """
382 """
382 raise NotImplementedError
383 raise NotImplementedError
383
384
@@ -1540,9 +1541,10 b' class Diff(object):'
1540 """
1541 """
1541 Represents a diff result from a repository backend.
1542 Represents a diff result from a repository backend.
1542
1543
1543 Subclasses have to provide a backend specific value for :attr:`_header_re`.
1544 Subclasses have to provide a backend specific value for
1545 :attr:`_header_re` and :attr:`_meta_re`.
1544 """
1546 """
1545
1547 _meta_re = None
1546 _header_re = None
1548 _header_re = None
1547
1549
1548 def __init__(self, raw_diff):
1550 def __init__(self, raw_diff):
@@ -1554,10 +1556,19 b' class Diff(object):'
1554 to make diffs consistent we must prepend with \n, and make sure
1556 to make diffs consistent we must prepend with \n, and make sure
1555 we can detect last chunk as this was also has special rule
1557 we can detect last chunk as this was also has special rule
1556 """
1558 """
1557 chunks = ('\n' + self.raw).split('\ndiff --git')[1:]
1559
1560 diff_parts = ('\n' + self.raw).split('\ndiff --git')
1561 header = diff_parts[0]
1562
1563 if self._meta_re:
1564 match = self._meta_re.match(header)
1565
1566 chunks = diff_parts[1:]
1558 total_chunks = len(chunks)
1567 total_chunks = len(chunks)
1559 return (DiffChunk(chunk, self, cur_chunk == total_chunks)
1568
1560 for cur_chunk, chunk in enumerate(chunks, start=1))
1569 return (
1570 DiffChunk(chunk, self, cur_chunk == total_chunks)
1571 for cur_chunk, chunk in enumerate(chunks, start=1))
1561
1572
1562
1573
1563 class DiffChunk(object):
1574 class DiffChunk(object):
@@ -30,6 +30,10 b' from rhodecode.lib.vcs.backends import b'
30
30
31 class SubversionDiff(base.Diff):
31 class SubversionDiff(base.Diff):
32
32
33 _meta_re = re.compile(r"""
34 (?:^(?P<svn_bin_patch>Cannot[ ]display:[ ]file[ ]marked[ ]as[ ]a[ ]binary[ ]type.)(?:\n|$))?
35 """, re.VERBOSE | re.MULTILINE)
36
33 _header_re = re.compile(r"""
37 _header_re = re.compile(r"""
34 #^diff[ ]--git
38 #^diff[ ]--git
35 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
39 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
@@ -1477,8 +1477,9 b' table.integrations {'
1477 margin-left: 8px;
1477 margin-left: 8px;
1478 }
1478 }
1479
1479
1480 p.ancestor {
1480 div.ancestor {
1481 margin: @padding 0;
1481 margin: @padding 0;
1482 line-height: 3.0em;
1482 }
1483 }
1483
1484
1484 .cs_icon_td input[type="checkbox"] {
1485 .cs_icon_td input[type="checkbox"] {
@@ -72,6 +72,7 b''
72 }
72 }
73 .disabled {
73 .disabled {
74 opacity: .5;
74 opacity: .5;
75 cursor: inherit;
75 }
76 }
76 .help-block {
77 .help-block {
77 color: inherit;
78 color: inherit;
@@ -73,6 +73,16 b' String.prototype.capitalizeFirstLetter ='
73 };
73 };
74
74
75
75
76 String.prototype.truncateAfter = function(chars, suffix) {
77 var suffix = suffix || '';
78 if (this.length > chars) {
79 return this.substr(0, chars) + suffix;
80 } else {
81 return this;
82 }
83 };
84
85
76 /**
86 /**
77 * Splits remainder
87 * Splits remainder
78 *
88 *
@@ -112,7 +112,7 b''
112
112
113 <div class="fieldset">
113 <div class="fieldset">
114 <div class="left-label">
114 <div class="left-label">
115 ${_('Diffs')}:
115 ${_('Diff options')}:
116 </div>
116 </div>
117 <div class="right-content">
117 <div class="right-content">
118 <div class="diff-actions">
118 <div class="diff-actions">
@@ -29,29 +29,84 b''
29 </%def>
29 </%def>
30
30
31 <%def name="main()">
31 <%def name="main()">
32 <div class="summary-header">
32 <div class="summary-header">
33 <div class="title">
33 <div class="title">
34 <div class="title-content">
35 ${self.repo_page_title(c.rhodecode_db_repo)}
34 ${self.repo_page_title(c.rhodecode_db_repo)}
36 </div>
37 </div>
38 <div class="header-buttons">
39 <a href="${h.url('compare_url', repo_name=c.repo_name, source_ref_type='rev', source_ref=getattr(c.commit_ranges[0].parents[0] if c.commit_ranges[0].parents else h.EmptyCommit(), 'raw_id'), target_ref_type='rev', target_ref=c.commit_ranges[-1].raw_id)}"
40 class="btn btn-default">
41 ${_('Show combined compare')}
42 </a>
43 </div>
35 </div>
44 </div>
36 </div>
37
38
39 <div class="summary changeset">
40 <div class="summary-detail">
41 <div class="summary-detail-header">
42 <span class="breadcrumbs files_location">
43 <h4>
44 ${_('Commit Range')}
45 <code>
46 r${c.commit_ranges[0].revision}:${h.short_id(c.commit_ranges[0].raw_id)}...r${c.commit_ranges[-1].revision}:${h.short_id(c.commit_ranges[-1].raw_id)}
47 </code>
48 </h4>
49 </span>
50 </div>
51
52 <div class="fieldset">
53 <div class="left-label">
54 ${_('Diff option')}:
55 </div>
56 <div class="right-content">
57 <div class="header-buttons">
58 <a href="${h.url('compare_url', repo_name=c.repo_name, source_ref_type='rev', source_ref=getattr(c.commit_ranges[0].parents[0] if c.commit_ranges[0].parents else h.EmptyCommit(), 'raw_id'), target_ref_type='rev', target_ref=c.commit_ranges[-1].raw_id)}">
59 ${_('Show combined compare')}
60 </a>
61 </div>
62 </div>
63 </div>
45
64
46 <div class="summary-detail">
65 <%doc>
47 <div class="title">
66 ##TODO(marcink): implement this and diff menus
48 <h2>
67 <div class="fieldset">
49 ${self.breadcrumbs_links()}
68 <div class="left-label">
50 </h2>
69 ${_('Diff options')}:
70 </div>
71 <div class="right-content">
72 <div class="diff-actions">
73 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
74 ${_('Raw Diff')}
75 </a>
76 |
77 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
78 ${_('Patch Diff')}
79 </a>
80 |
81 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision='?',diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
82 ${_('Download Diff')}
83 </a>
84 </div>
85 </div>
86 </div>
87 </%doc>
88 </div> <!-- end summary-detail -->
89
90 </div> <!-- end summary -->
91
92 <div id="changeset_compare_view_content">
93 <div class="pull-left">
94 <div class="btn-group">
95 <a
96 class="btn"
97 href="#"
98 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
99 ${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
100 </a>
101 <a
102 class="btn"
103 href="#"
104 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
105 ${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
106 </a>
107 </div>
51 </div>
108 </div>
52 </div>
109 ## Commit range generated below
53 <div id="changeset_compare_view_content">
54 ##CS
55 <%include file="../compare/compare_commits.html"/>
110 <%include file="../compare/compare_commits.html"/>
56 <div class="cs_files">
111 <div class="cs_files">
57 <%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
112 <%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
@@ -65,7 +120,6 b''
65 commit=commit,
120 commit=commit,
66 )}
121 )}
67 %endfor
122 %endfor
68 </table>
69 </div>
123 </div>
70 </div>
124 </div>
71 </%def>
125 </%def>
@@ -52,45 +52,6 b''
52 </div>
52 </div>
53 </%def>
53 </%def>
54
54
55 <%def name="diff_menu(repo_name, f_path, cs1, cs2, change, file=None)">
56 <%
57 onclick_diff2way = ''
58 if (file and file["exceeds_limit"]):
59 onclick_diff2way = '''return confirm('%s');''' % _("Showing a big diff might take some time and resources, continue?")
60 %>
61
62 % if change in ['A', 'M']:
63 <a href="${h.url('files_home',repo_name=repo_name,f_path=f_path,revision=cs2)}"
64 class="tooltip" title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': cs2[:12]})}">
65 ${_('Show File')}
66 </a>
67 % else:
68 <span
69 class="tooltip" title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': cs2[:12]})}">
70 ${_('Show File')}
71 </span>
72 % endif
73 |
74 <a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}"
75 class="tooltip" title="${h.tooltip(_('Show full diff for this file'))}">
76 ${_('Unified Diff')}
77 </a>
78 |
79 <a href="${h.url('files_diff_2way_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}"
80 class="tooltip" title="${h.tooltip(_('Show full side-by-side diff for this file'))}"} onclick="${onclick_diff2way}">
81 ${_('Side-by-side Diff')}
82 </a>
83 |
84 <a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='raw')}"
85 class="tooltip" title="${h.tooltip(_('Raw diff'))}">
86 ${_('Raw Diff')}
87 </a>
88 |
89 <a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='download')}"
90 class="tooltip" title="${h.tooltip(_('Download diff'))}">
91 ${_('Download Diff')}
92 </a>
93 </%def>
94
55
95 <%def name="diff_summary_text(changed_files, lines_added, lines_deleted, limited_diff=False)">
56 <%def name="diff_summary_text(changed_files, lines_added, lines_deleted, limited_diff=False)">
96 % if limited_diff:
57 % if limited_diff:
@@ -162,10 +162,11 b' collapse_all = len(diffset.files) > coll'
162
162
163 <div class="filediffs">
163 <div class="filediffs">
164 %for i, filediff in enumerate(diffset.files):
164 %for i, filediff in enumerate(diffset.files):
165 <%
165
166 lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted']
166 <%
167 over_lines_changed_limit = lines_changed > lines_changed_limit
167 lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted']
168 %>
168 over_lines_changed_limit = lines_changed > lines_changed_limit
169 %>
169 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
170 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
170 <div
171 <div
171 class="filediff"
172 class="filediff"
@@ -414,6 +415,7 b' from rhodecode.lib.diffs import NEW_FILE'
414 if line.modified.lineno:
415 if line.modified.lineno:
415 new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, line.modified.lineno, 'n')
416 new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, line.modified.lineno, 'n')
416 %>
417 %>
418
417 <tr class="cb-line">
419 <tr class="cb-line">
418 <td class="cb-data ${action_class(line.original.action)}"
420 <td class="cb-data ${action_class(line.original.action)}"
419 data-line-number="${line.original.lineno}"
421 data-line-number="${line.original.lineno}"
@@ -544,6 +546,7 b' from rhodecode.lib.diffs import NEW_FILE'
544 <div class="diffset-menu clearinner">
546 <div class="diffset-menu clearinner">
545 <div class="pull-right">
547 <div class="pull-right">
546 <div class="btn-group">
548 <div class="btn-group">
549
547 <a
550 <a
548 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
551 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
549 title="${_('View side by side')}"
552 title="${_('View side by side')}"
@@ -557,20 +560,21 b' from rhodecode.lib.diffs import NEW_FILE'
557 </a>
560 </a>
558 </div>
561 </div>
559 </div>
562 </div>
563
560 <div class="pull-left">
564 <div class="pull-left">
561 <div class="btn-group">
565 <div class="btn-group">
562 <a
566 <a
563 class="btn"
567 class="btn"
564 href="#"
568 href="#"
565 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All')}</a>
569 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
566 <a
570 <a
567 class="btn"
571 class="btn"
568 href="#"
572 href="#"
569 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All')}</a>
573 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
570 <a
574 <a
571 class="btn"
575 class="btn"
572 href="#"
576 href="#"
573 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode')}</a>
577 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
574 </div>
578 </div>
575 </div>
579 </div>
576 </div>
580 </div>
@@ -1,20 +1,17 b''
1 ## Changesets table !
1 ## Changesets table !
2 <%namespace name="base" file="/base/base.html"/>
2 <%namespace name="base" file="/base/base.html"/>
3 <div class="container">
4 %if not c.commit_ranges:
5 <p class="empty_data">${_('No Commits')}</p>
6 %else:
7
3
8 %if c.ancestor:
4 %if c.ancestor:
9 <p class="ancestor">${_('Common Ancestor Commit')}:
5 <div class="ancestor">${_('Common Ancestor Commit')}:
10 <a href="${h.url('changeset_home',
6 <a href="${h.url('changeset_home',
11 repo_name=c.repo_name,
7 repo_name=c.repo_name,
12 revision=c.ancestor)}">
8 revision=c.ancestor)}">
13 ${h.short_id(c.ancestor)}
9 ${h.short_id(c.ancestor)}
14 </a>
10 </a>
15 </p>
11 </div>
16 %endif
12 %endif
17
13
14 <div class="container">
18 <input type="hidden" name="__start__" value="revisions:sequence">
15 <input type="hidden" name="__start__" value="revisions:sequence">
19 <table class="rctable compare_view_commits">
16 <table class="rctable compare_view_commits">
20 <tr>
17 <tr>
@@ -66,9 +63,21 b''
66 </td>
63 </td>
67 </tr>
64 </tr>
68 %endfor
65 %endfor
66 <tr class="compare_select_hidden" style="display: none">
67 <td colspan="5">
68 ${ungettext('%s commit hidden','%s commits hidden', len(c.commit_ranges)) % len(c.commit_ranges)}
69 </td>
70 </tr>
71 % if not c.commit_ranges:
72 <tr class="compare_select">
73 <td colspan="5">
74 ${_('No commits in this compare')}
75 </td>
76 </tr>
77 % endif
69 </table>
78 </table>
70 <input type="hidden" name="__end__" value="revisions:sequence">
79 <input type="hidden" name="__end__" value="revisions:sequence">
71 %endif
80
72 </div>
81 </div>
73
82
74 <script>
83 <script>
@@ -76,7 +85,7 b''
76 var target_expand = $(this);
85 var target_expand = $(this);
77 var cid = target_expand.data('commitId');
86 var cid = target_expand.data('commitId');
78
87
79 ## TODO: dan: extract styles into css, and just toggleClass('open') here
88 // ## TODO: dan: extract styles into css, and just toggleClass('open') here
80 if (target_expand.hasClass('open')){
89 if (target_expand.hasClass('open')){
81 $('#c-'+cid).css({
90 $('#c-'+cid).css({
82 'height': '1.5em',
91 'height': '1.5em',
@@ -34,34 +34,132 b''
34 <div class="box">
34 <div class="box">
35 <div class="title">
35 <div class="title">
36 ${self.repo_page_title(c.rhodecode_db_repo)}
36 ${self.repo_page_title(c.rhodecode_db_repo)}
37 <div class="breadcrumbs">
38 ${_('Compare Commits')}
39 </div>
40 </div>
37 </div>
41
38
39 <div class="summary changeset">
40 <div class="summary-detail">
41 <div class="summary-detail-header">
42 <span class="breadcrumbs files_location">
43 <h4>
44 ${_('Compare Commits')}
45 % if c.file_path:
46 ${_('for file')} <a href="#${'a_' + h.FID('',c.file_path)}">${c.file_path}</a>
47 % endif
48
49 % if c.commit_ranges:
50 <code>
51 r${c.source_commit.revision}:${h.short_id(c.source_commit.raw_id)}...r${c.target_commit.revision}:${h.short_id(c.target_commit.raw_id)}
52 </code>
53 % endif
54 </h4>
55 </span>
56 </div>
57
58 <div class="fieldset">
59 <div class="left-label">
60 ${_('Target')}:
61 </div>
62 <div class="right-content">
63 <div>
64 <div class="code-header" >
65 <div class="compare_header">
66 ## The hidden elements are replaced with a select2 widget
67 ${h.hidden('compare_source')}
68 </div>
69 </div>
70 </div>
71 </div>
72 </div>
73
74 <div class="fieldset">
75 <div class="left-label">
76 ${_('Source')}:
77 </div>
78 <div class="right-content">
79 <div>
80 <div class="code-header" >
81 <div class="compare_header">
82 ## The hidden elements are replaced with a select2 widget
83 ${h.hidden('compare_target')}
84 </div>
85 </div>
86 </div>
87 </div>
88 </div>
89
90 <div class="fieldset">
91 <div class="left-label">
92 ${_('Actions')}:
93 </div>
94 <div class="right-content">
95 <div>
96 <div class="code-header" >
97 <div class="compare_header">
98
99 <div class="compare-buttons">
100 % if c.compare_home:
101 <a id="compare_revs" class="btn btn-primary"> ${_('Compare Commits')}</a>
102
103 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Swap')}</a>
104 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
105 <div id="changeset_compare_view_content">
106 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
107 </div>
108
109 % elif c.preview_mode:
110 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Compare Commits')}</a>
111 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Swap')}</a>
112 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
113
114 % else:
115 <a id="compare_revs" class="btn btn-primary"> ${_('Compare Commits')}</a>
116 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}">${_('Swap')}</a>
117
118 ## allow comment only if there are commits to comment on
119 % if c.diffset and c.diffset.files and c.commit_ranges:
120 <a id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</a>
121 % else:
122 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
123 % endif
124 % endif
125 </div>
126 </div>
127 </div>
128 </div>
129 </div>
130 </div>
131
132 <%doc>
133 ##TODO(marcink): implement this and diff menus
134 <div class="fieldset">
135 <div class="left-label">
136 ${_('Diff options')}:
137 </div>
138 <div class="right-content">
139 <div class="diff-actions">
140 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
141 ${_('Raw Diff')}
142 </a>
143 |
144 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
145 ${_('Patch Diff')}
146 </a>
147 |
148 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision='?',diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
149 ${_('Download Diff')}
150 </a>
151 </div>
152 </div>
153 </div>
154 </%doc>
155
156 </div> <!-- end summary-detail -->
157
158 </div> <!-- end summary -->
159
160
42 <div class="table">
161 <div class="table">
43 <div id="codeblock" class="diffblock">
44 <div class="code-header" >
45 <div class="compare_header">
46 ## The hidden elements are replaced with a select2 widget
47 <div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
48 <div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
49
162
50 %if not c.preview_mode:
51 <div class="compare-label"></div>
52 <div class="compare-buttons">
53 %if not c.compare_home:
54 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
55 %endif
56 <div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
57 %if c.diffset and c.diffset.files:
58 <div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
59 %endif
60 </div>
61 %endif
62 </div>
63 </div>
64 </div>
65 ## use JS script to load it quickly before potentially large diffs render long time
163 ## use JS script to load it quickly before potentially large diffs render long time
66 ## this prevents from situation when large diffs block rendering of select2 fields
164 ## this prevents from situation when large diffs block rendering of select2 fields
67 <script type="text/javascript">
165 <script type="text/javascript">
@@ -241,13 +339,26 b''
241
339
242 </div>
340 </div>
243
341
244 %if c.compare_home:
342 %if not c.compare_home:
245 <div id="changeset_compare_view_content">
343 <div id="changeset_compare_view_content">
246 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
344 <div class="pull-left">
247 </div>
345 <div class="btn-group">
248 %else:
346 <a
249 <div id="changeset_compare_view_content">
347 class="btn"
250 ##CS
348 href="#"
349 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
350 ${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
351 </a>
352 <a
353 class="btn"
354 href="#"
355 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
356 ${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
357 </a>
358 </div>
359 </div>
360 <div style="padding:0 10px 10px 0px" class="pull-left"></div>
361 ## commit compare generated below
251 <%include file="compare_commits.html"/>
362 <%include file="compare_commits.html"/>
252 ${cbdiffs.render_diffset_menu()}
363 ${cbdiffs.render_diffset_menu()}
253 ${cbdiffs.render_diffset(c.diffset)}
364 ${cbdiffs.render_diffset(c.diffset)}
@@ -9,11 +9,9 b''
9 </%def>
9 </%def>
10
10
11 <%def name="js_extra()">
11 <%def name="js_extra()">
12 <script type="text/javascript" src="${h.asset('js/mergerly.js', ver=c.rhodecode_version_hash)}"></script>
13 </%def>
12 </%def>
14
13
15 <%def name="css_extra()">
14 <%def name="css_extra()">
16 <link rel="stylesheet" type="text/css" href="${h.asset('css/mergerly.css', ver=c.rhodecode_version_hash)}"/>
17 </%def>
15 </%def>
18
16
19
17
@@ -128,7 +128,7 b''
128 // used for history, and switch to
128 // used for history, and switch to
129 var initialCommitData = {
129 var initialCommitData = {
130 id: null,
130 id: null,
131 text: '${_("Switch To Commit")}',
131 text: '${_("Pick Commit")}',
132 type: 'sha',
132 type: 'sha',
133 raw_id: null,
133 raw_id: null,
134 files_url: null
134 files_url: null
@@ -151,9 +151,47 b''
151
151
152 // file history select2
152 // file history select2
153 select2FileHistorySwitcher('#diff1', initialCommitData, state);
153 select2FileHistorySwitcher('#diff1', initialCommitData, state);
154
155 // show at, diff to actions handlers
154 $('#diff1').on('change', function(e) {
156 $('#diff1').on('change', function(e) {
155 $('#diff').removeClass('disabled').removeAttr("disabled");
157 $('#diff_to_commit').removeClass('disabled').removeAttr("disabled");
156 $('#show_rev').removeClass('disabled').removeAttr("disabled");
158 $('#diff_to_commit').val(_gettext('Diff to Commit ') + e.val.truncateAfter(8, '...'));
159
160 $('#show_at_commit').removeClass('disabled').removeAttr("disabled");
161 $('#show_at_commit').val(_gettext('Show at Commit ') + e.val.truncateAfter(8, '...'));
162 });
163
164 $('#diff_to_commit').on('click', function(e) {
165 var diff1 = $('#diff1').val();
166 var diff2 = $('#diff2').val();
167
168 var url_data = {
169 repo_name: templateContext.repo_name,
170 source_ref: diff1,
171 source_ref_type: 'rev',
172 target_ref: diff2,
173 target_ref_type: 'rev',
174 merge: 1,
175 f_path: state.f_path
176 };
177 window.location = pyroutes.url('compare_url', url_data);
178 });
179
180 $('#show_at_commit').on('click', function(e) {
181 var diff1 = $('#diff1').val();
182
183 var annotate = $('#annotate').val();
184 if (annotate === "True") {
185 var url = pyroutes.url('files_annotate_home',
186 {'repo_name': templateContext.repo_name,
187 'revision': diff1, 'f_path': state.f_path});
188 } else {
189 var url = pyroutes.url('files_home',
190 {'repo_name': templateContext.repo_name,
191 'revision': diff1, 'f_path': state.f_path});
192 }
193 window.location = url;
194
157 });
195 });
158
196
159 // show more authors
197 // show more authors
@@ -46,17 +46,29 b''
46 </div>
46 </div>
47
47
48
48
49 <div id="node_history" class="file_diff_buttons collapsable-content" data-toggle="summary-details">
49 <div class="fieldset collapsable-content" data-toggle="summary-details">
50 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
50 <div class="left-label">
51 ${_('Show/Diff file')}:
52 </div>
53 <div class="right-content">
51 ${h.hidden('diff1')}
54 ${h.hidden('diff1')}
52 ${h.hidden('diff2',c.file_last_commit.raw_id)}
55 ${h.hidden('diff2',c.commit.raw_id)}
56 ${h.hidden('annotate', c.annotate)}
57 </div>
58 </div>
53
59
54 ${h.submit('diff',_('Diff to Commit'),class_="btn disabled",disabled="true")}
60
55 ${h.submit('show_rev',_('Show at Commit'),class_="btn disabled",disabled="true")}
61 <div class="fieldset collapsable-content" data-toggle="summary-details">
56 ${h.hidden('annotate', c.annotate)}
62 <div class="left-label">
57 ${h.end_form()}
63 ${_('Action')}:
64 </div>
65 <div class="right-content">
66 ${h.submit('diff_to_commit',_('Diff to Commit'),class_="btn disabled",disabled="true")}
67 ${h.submit('show_at_commit',_('Show at Commit'),class_="btn disabled",disabled="true")}
68 </div>
58 </div>
69 </div>
59
70
71
60 <script>
72 <script>
61 collapsableContent();
73 collapsableContent();
62 </script> No newline at end of file
74 </script>
@@ -360,14 +360,33 b''
360 </div>
360 </div>
361 % endif
361 % endif
362 <div class="compare_view_commits_title">
362 <div class="compare_view_commits_title">
363 % if c.allowed_to_update and not c.pull_request.is_closed():
363
364 <a id="update_commits" class="btn btn-primary pull-right">${_('Update commits')}</a>
364 <div class="pull-left">
365 % else:
365 <div class="btn-group">
366 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
366 <a
367 % endif
367 class="btn"
368 % if len(c.commit_ranges):
368 href="#"
369 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
369 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
370 % endif
370 ${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
371 </a>
372 <a
373 class="btn"
374 href="#"
375 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
376 ${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
377 </a>
378 </div>
379 </div>
380
381 <div class="pull-right">
382 % if c.allowed_to_update and not c.pull_request.is_closed():
383 <a id="update_commits" class="btn btn-primary pull-right">${_('Update commits')}</a>
384 % else:
385 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
386 % endif
387
388 </div>
389
371 </div>
390 </div>
372 % if not c.missing_commits:
391 % if not c.missing_commits:
373 <%include file="/compare/compare_commits.html" />
392 <%include file="/compare/compare_commits.html" />
@@ -244,7 +244,8 b' class TestCommitCommentsController(TestC'
244 ('markdown', '# header', '<h1>header</h1>'),
244 ('markdown', '# header', '<h1>header</h1>'),
245 ('markdown', '*italics*', '<em>italics</em>'),
245 ('markdown', '*italics*', '<em>italics</em>'),
246 ('markdown', '**bold**', '<strong>bold</strong>'),
246 ('markdown', '**bold**', '<strong>bold</strong>'),
247 ])
247 ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
248 'md-header', 'md-italics', 'md-bold', ])
248 def test_preview(self, renderer, input, output, backend):
249 def test_preview(self, renderer, input, output, backend):
249 self.log_user()
250 self.log_user()
250 params = {
251 params = {
@@ -22,16 +22,13 b' import mock'
22 import pytest
22 import pytest
23 import lxml.html
23 import lxml.html
24
24
25 from rhodecode.lib.vcs.backends.base import EmptyCommit
26 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
27 from rhodecode.model.db import Repository
26 from rhodecode.tests import url, assert_session_flash
28 from rhodecode.model.scm import ScmModel
27 from rhodecode.tests.utils import AssertResponse, commit_change
29 from rhodecode.tests import url, TEST_USER_ADMIN_LOGIN, assert_session_flash
30 from rhodecode.tests.utils import AssertResponse
31
28
32
29
33 @pytest.mark.usefixtures("autologin_user", "app")
30 @pytest.mark.usefixtures("autologin_user", "app")
34 class TestCompareController:
31 class TestCompareController(object):
35
32
36 @pytest.mark.xfail_backends("svn", reason="Requires pull")
33 @pytest.mark.xfail_backends("svn", reason="Requires pull")
37 def test_compare_remote_with_different_commit_indexes(self, backend):
34 def test_compare_remote_with_different_commit_indexes(self, backend):
@@ -53,23 +50,23 b' class TestCompareController:'
53 fork = backend.create_repo()
50 fork = backend.create_repo()
54
51
55 # prepare fork
52 # prepare fork
56 commit0 = _commit_change(
53 commit0 = commit_change(
57 fork.repo_name, filename='file1', content='A',
54 fork.repo_name, filename='file1', content='A',
58 message='A', vcs_type=backend.alias, parent=None, newfile=True)
55 message='A', vcs_type=backend.alias, parent=None, newfile=True)
59
56
60 commit1 = _commit_change(
57 commit1 = commit_change(
61 fork.repo_name, filename='file1', content='B',
58 fork.repo_name, filename='file1', content='B',
62 message='B, child of A', vcs_type=backend.alias, parent=commit0)
59 message='B, child of A', vcs_type=backend.alias, parent=commit0)
63
60
64 _commit_change( # commit 2
61 commit_change( # commit 2
65 fork.repo_name, filename='file1', content='C',
62 fork.repo_name, filename='file1', content='C',
66 message='C, child of B', vcs_type=backend.alias, parent=commit1)
63 message='C, child of B', vcs_type=backend.alias, parent=commit1)
67
64
68 commit3 = _commit_change(
65 commit3 = commit_change(
69 fork.repo_name, filename='file1', content='D',
66 fork.repo_name, filename='file1', content='D',
70 message='D, child of A', vcs_type=backend.alias, parent=commit0)
67 message='D, child of A', vcs_type=backend.alias, parent=commit0)
71
68
72 commit4 = _commit_change(
69 commit4 = commit_change(
73 fork.repo_name, filename='file1', content='E',
70 fork.repo_name, filename='file1', content='E',
74 message='E, child of D', vcs_type=backend.alias, parent=commit3)
71 message='E, child of D', vcs_type=backend.alias, parent=commit3)
75
72
@@ -105,7 +102,7 b' class TestCompareController:'
105 repo1 = backend.create_repo()
102 repo1 = backend.create_repo()
106
103
107 # commit something !
104 # commit something !
108 commit0 = _commit_change(
105 commit0 = commit_change(
109 repo1.repo_name, filename='file1', content='line1\n',
106 repo1.repo_name, filename='file1', content='line1\n',
110 message='commit1', vcs_type=backend.alias, parent=None,
107 message='commit1', vcs_type=backend.alias, parent=None,
111 newfile=True)
108 newfile=True)
@@ -114,11 +111,11 b' class TestCompareController:'
114 repo2 = backend.create_fork()
111 repo2 = backend.create_fork()
115
112
116 # add two extra commit into fork
113 # add two extra commit into fork
117 commit1 = _commit_change(
114 commit1 = commit_change(
118 repo2.repo_name, filename='file1', content='line1\nline2\n',
115 repo2.repo_name, filename='file1', content='line1\nline2\n',
119 message='commit2', vcs_type=backend.alias, parent=commit0)
116 message='commit2', vcs_type=backend.alias, parent=commit0)
120
117
121 commit2 = _commit_change(
118 commit2 = commit_change(
122 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
119 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
123 message='commit3', vcs_type=backend.alias, parent=commit1)
120 message='commit3', vcs_type=backend.alias, parent=commit1)
124
121
@@ -156,7 +153,7 b' class TestCompareController:'
156 repo1 = backend.create_repo()
153 repo1 = backend.create_repo()
157
154
158 # commit something !
155 # commit something !
159 commit0 = _commit_change(
156 commit0 = commit_change(
160 repo1.repo_name, filename='file1', content='line1\n',
157 repo1.repo_name, filename='file1', content='line1\n',
161 message='commit1', vcs_type=backend.alias, parent=None,
158 message='commit1', vcs_type=backend.alias, parent=None,
162 newfile=True)
159 newfile=True)
@@ -165,17 +162,17 b' class TestCompareController:'
165 repo2 = backend.create_fork()
162 repo2 = backend.create_fork()
166
163
167 # now commit something to origin repo
164 # now commit something to origin repo
168 _commit_change(
165 commit_change(
169 repo1.repo_name, filename='file2', content='line1file2\n',
166 repo1.repo_name, filename='file2', content='line1file2\n',
170 message='commit2', vcs_type=backend.alias, parent=commit0,
167 message='commit2', vcs_type=backend.alias, parent=commit0,
171 newfile=True)
168 newfile=True)
172
169
173 # add two extra commit into fork
170 # add two extra commit into fork
174 commit1 = _commit_change(
171 commit1 = commit_change(
175 repo2.repo_name, filename='file1', content='line1\nline2\n',
172 repo2.repo_name, filename='file1', content='line1\nline2\n',
176 message='commit2', vcs_type=backend.alias, parent=commit0)
173 message='commit2', vcs_type=backend.alias, parent=commit0)
177
174
178 commit2 = _commit_change(
175 commit2 = commit_change(
179 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
176 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
180 message='commit3', vcs_type=backend.alias, parent=commit1)
177 message='commit3', vcs_type=backend.alias, parent=commit1)
181
178
@@ -207,9 +204,9 b' class TestCompareController:'
207 compare_page.swap_is_hidden()
204 compare_page.swap_is_hidden()
208 compare_page.target_source_are_disabled()
205 compare_page.target_source_are_disabled()
209
206
210 @pytest.mark.xfail_backends("svn", "git")
207 @pytest.mark.xfail_backends("svn")
208 # TODO(marcink): no svn support for compare two seperate repos
211 def test_compare_of_unrelated_forks(self, backend):
209 def test_compare_of_unrelated_forks(self, backend):
212 # TODO: johbo: Fails for git due to some other issue it seems
213 orig = backend.create_repo(number_of_commits=1)
210 orig = backend.create_repo(number_of_commits=1)
214 fork = backend.create_repo(number_of_commits=1)
211 fork = backend.create_repo(number_of_commits=1)
215
212
@@ -245,11 +242,11 b' class TestCompareController:'
245 repo1 = backend.create_repo()
242 repo1 = backend.create_repo()
246
243
247 # commit something !
244 # commit something !
248 commit0 = _commit_change(
245 commit0 = commit_change(
249 repo1.repo_name, filename='file1', content='line1\n',
246 repo1.repo_name, filename='file1', content='line1\n',
250 message='commit1', vcs_type=backend.alias, parent=None,
247 message='commit1', vcs_type=backend.alias, parent=None,
251 newfile=True)
248 newfile=True)
252 commit1 = _commit_change(
249 commit1 = commit_change(
253 repo1.repo_name, filename='file1', content='line1\nline2\n',
250 repo1.repo_name, filename='file1', content='line1\nline2\n',
254 message='commit2', vcs_type=backend.alias, parent=commit0)
251 message='commit2', vcs_type=backend.alias, parent=commit0)
255
252
@@ -257,18 +254,18 b' class TestCompareController:'
257 repo2 = backend.create_fork()
254 repo2 = backend.create_fork()
258
255
259 # now make commit3-6
256 # now make commit3-6
260 commit2 = _commit_change(
257 commit2 = commit_change(
261 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
258 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
262 message='commit3', vcs_type=backend.alias, parent=commit1)
259 message='commit3', vcs_type=backend.alias, parent=commit1)
263 commit3 = _commit_change(
260 commit3 = commit_change(
264 repo1.repo_name, filename='file1',
261 repo1.repo_name, filename='file1',
265 content='line1\nline2\nline3\nline4\n', message='commit4',
262 content='line1\nline2\nline3\nline4\n', message='commit4',
266 vcs_type=backend.alias, parent=commit2)
263 vcs_type=backend.alias, parent=commit2)
267 commit4 = _commit_change(
264 commit4 = commit_change(
268 repo1.repo_name, filename='file1',
265 repo1.repo_name, filename='file1',
269 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
266 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
270 vcs_type=backend.alias, parent=commit3)
267 vcs_type=backend.alias, parent=commit3)
271 _commit_change( # commit 5
268 commit_change( # commit 5
272 repo1.repo_name, filename='file1',
269 repo1.repo_name, filename='file1',
273 content='line1\nline2\nline3\nline4\nline5\nline6\n',
270 content='line1\nline2\nline3\nline4\nline5\nline6\n',
274 message='commit6', vcs_type=backend.alias, parent=commit4)
271 message='commit6', vcs_type=backend.alias, parent=commit4)
@@ -311,11 +308,11 b' class TestCompareController:'
311 repo1 = backend.create_repo()
308 repo1 = backend.create_repo()
312
309
313 # commit something !
310 # commit something !
314 commit0 = _commit_change(
311 commit0 = commit_change(
315 repo1.repo_name, filename='file1', content='line1\n',
312 repo1.repo_name, filename='file1', content='line1\n',
316 message='commit1', vcs_type=backend.alias, parent=None,
313 message='commit1', vcs_type=backend.alias, parent=None,
317 newfile=True)
314 newfile=True)
318 commit1 = _commit_change(
315 commit1 = commit_change(
319 repo1.repo_name, filename='file1', content='line1\nline2\n',
316 repo1.repo_name, filename='file1', content='line1\nline2\n',
320 message='commit2', vcs_type=backend.alias, parent=commit0)
317 message='commit2', vcs_type=backend.alias, parent=commit0)
321
318
@@ -323,18 +320,18 b' class TestCompareController:'
323 backend.create_fork()
320 backend.create_fork()
324
321
325 # now make commit3-6
322 # now make commit3-6
326 commit2 = _commit_change(
323 commit2 = commit_change(
327 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
324 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
328 message='commit3', vcs_type=backend.alias, parent=commit1)
325 message='commit3', vcs_type=backend.alias, parent=commit1)
329 commit3 = _commit_change(
326 commit3 = commit_change(
330 repo1.repo_name, filename='file1',
327 repo1.repo_name, filename='file1',
331 content='line1\nline2\nline3\nline4\n', message='commit4',
328 content='line1\nline2\nline3\nline4\n', message='commit4',
332 vcs_type=backend.alias, parent=commit2)
329 vcs_type=backend.alias, parent=commit2)
333 commit4 = _commit_change(
330 commit4 = commit_change(
334 repo1.repo_name, filename='file1',
331 repo1.repo_name, filename='file1',
335 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
332 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
336 vcs_type=backend.alias, parent=commit3)
333 vcs_type=backend.alias, parent=commit3)
337 commit5 = _commit_change(
334 commit5 = commit_change(
338 repo1.repo_name, filename='file1',
335 repo1.repo_name, filename='file1',
339 content='line1\nline2\nline3\nline4\nline5\nline6\n',
336 content='line1\nline2\nline3\nline4\nline5\nline6\n',
340 message='commit6', vcs_type=backend.alias, parent=commit4)
337 message='commit6', vcs_type=backend.alias, parent=commit4)
@@ -400,7 +397,7 b' class TestCompareController:'
400 repo1 = backend.create_repo()
397 repo1 = backend.create_repo()
401 r1_name = repo1.repo_name
398 r1_name = repo1.repo_name
402
399
403 commit0 = _commit_change(
400 commit0 = commit_change(
404 repo=r1_name, filename='file1',
401 repo=r1_name, filename='file1',
405 content='line1', message='commit1', vcs_type=backend.alias,
402 content='line1', message='commit1', vcs_type=backend.alias,
406 newfile=True)
403 newfile=True)
@@ -413,19 +410,19 b' class TestCompareController:'
413 self.r2_id = repo2.repo_id
410 self.r2_id = repo2.repo_id
414 r2_name = repo2.repo_name
411 r2_name = repo2.repo_name
415
412
416 commit1 = _commit_change(
413 commit1 = commit_change(
417 repo=r2_name, filename='file1-fork',
414 repo=r2_name, filename='file1-fork',
418 content='file1-line1-from-fork', message='commit1-fork',
415 content='file1-line1-from-fork', message='commit1-fork',
419 vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
416 vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
420 newfile=True)
417 newfile=True)
421
418
422 commit2 = _commit_change(
419 commit2 = commit_change(
423 repo=r2_name, filename='file2-fork',
420 repo=r2_name, filename='file2-fork',
424 content='file2-line1-from-fork', message='commit2-fork',
421 content='file2-line1-from-fork', message='commit2-fork',
425 vcs_type=backend.alias, parent=commit1,
422 vcs_type=backend.alias, parent=commit1,
426 newfile=True)
423 newfile=True)
427
424
428 _commit_change( # commit 3
425 commit_change( # commit 3
429 repo=r2_name, filename='file3-fork',
426 repo=r2_name, filename='file3-fork',
430 content='file3-line1-from-fork', message='commit3-fork',
427 content='file3-line1-from-fork', message='commit3-fork',
431 vcs_type=backend.alias, parent=commit2, newfile=True)
428 vcs_type=backend.alias, parent=commit2, newfile=True)
@@ -447,9 +444,9 b' class TestCompareController:'
447 response.mustcontain('%s@%s' % (r2_name, commit_id1))
444 response.mustcontain('%s@%s' % (r2_name, commit_id1))
448 response.mustcontain('%s@%s' % (r1_name, commit_id2))
445 response.mustcontain('%s@%s' % (r1_name, commit_id2))
449 response.mustcontain('No files')
446 response.mustcontain('No files')
450 response.mustcontain('No Commits')
447 response.mustcontain('No commits in this compare')
451
448
452 commit0 = _commit_change(
449 commit0 = commit_change(
453 repo=r1_name, filename='file2',
450 repo=r1_name, filename='file2',
454 content='line1-added-after-fork', message='commit2-parent',
451 content='line1-added-after-fork', message='commit2-parent',
455 vcs_type=backend.alias, parent=None, newfile=True)
452 vcs_type=backend.alias, parent=None, newfile=True)
@@ -558,7 +555,7 b' class TestCompareController:'
558
555
559
556
560 @pytest.mark.usefixtures("autologin_user")
557 @pytest.mark.usefixtures("autologin_user")
561 class TestCompareControllerSvn:
558 class TestCompareControllerSvn(object):
562
559
563 def test_supports_references_with_path(self, app, backend_svn):
560 def test_supports_references_with_path(self, app, backend_svn):
564 repo = backend_svn['svn-simple-layout']
561 repo = backend_svn['svn-simple-layout']
@@ -574,7 +571,7 b' class TestCompareControllerSvn:'
574 status=200)
571 status=200)
575
572
576 # Expecting no commits, since both paths are at the same revision
573 # Expecting no commits, since both paths are at the same revision
577 response.mustcontain('No Commits')
574 response.mustcontain('No commits in this compare')
578
575
579 # Should find only one file changed when comparing those two tags
576 # Should find only one file changed when comparing those two tags
580 response.mustcontain('example.py')
577 response.mustcontain('example.py')
@@ -596,7 +593,7 b' class TestCompareControllerSvn:'
596 status=200)
593 status=200)
597
594
598 # It should show commits
595 # It should show commits
599 assert 'No Commits' not in response.body
596 assert 'No commits in this compare' not in response.body
600
597
601 # Should find only one file changed when comparing those two tags
598 # Should find only one file changed when comparing those two tags
602 response.mustcontain('example.py')
599 response.mustcontain('example.py')
@@ -660,36 +657,3 b' class ComparePage(AssertResponse):'
660 def target_source_are_enabled(self):
657 def target_source_are_enabled(self):
661 response = self.response
658 response = self.response
662 response.mustcontain("var enable_fields = true;")
659 response.mustcontain("var enable_fields = true;")
663
664
665 def _commit_change(
666 repo, filename, content, message, vcs_type, parent=None,
667 newfile=False):
668 repo = Repository.get_by_repo_name(repo)
669 _commit = parent
670 if not parent:
671 _commit = EmptyCommit(alias=vcs_type)
672
673 if newfile:
674 nodes = {
675 filename: {
676 'content': content
677 }
678 }
679 commit = ScmModel().create_nodes(
680 user=TEST_USER_ADMIN_LOGIN, repo=repo,
681 message=message,
682 nodes=nodes,
683 parent_commit=_commit,
684 author=TEST_USER_ADMIN_LOGIN,
685 )
686 else:
687 commit = ScmModel().commit_change(
688 repo=repo.scm_instance(), repo_name=repo.repo_name,
689 commit=parent, user=TEST_USER_ADMIN_LOGIN,
690 author=TEST_USER_ADMIN_LOGIN,
691 message=message,
692 content=content,
693 f_path=filename
694 )
695 return commit
@@ -44,7 +44,7 b' class TestCompareController:'
44 response.mustcontain('%s@%s' % (backend.repo_name, tag1))
44 response.mustcontain('%s@%s' % (backend.repo_name, tag1))
45 response.mustcontain('%s@%s' % (backend.repo_name, tag2))
45 response.mustcontain('%s@%s' % (backend.repo_name, tag2))
46
46
47 # outgoing changesets between tags
47 # outgoing commits between tags
48 commit_indexes = {
48 commit_indexes = {
49 'git': [113] + range(115, 121),
49 'git': [113] + range(115, 121),
50 'hg': [112] + range(115, 121),
50 'hg': [112] + range(115, 121),
@@ -118,8 +118,8 b' class TestCompareController:'
118 response.mustcontain('%s@%s' % (backend.repo_name, head_id))
118 response.mustcontain('%s@%s' % (backend.repo_name, head_id))
119
119
120 # branches are equal
120 # branches are equal
121 response.mustcontain('<p class="empty_data">No files</p>')
121 response.mustcontain('No files')
122 response.mustcontain('<p class="empty_data">No Commits</p>')
122 response.mustcontain('No commits in this compare')
123
123
124 def test_compare_commits(self, backend):
124 def test_compare_commits(self, backend):
125 repo = backend.repo
125 repo = backend.repo
@@ -28,15 +28,11 b' from rhodecode.lib import helpers as h'
28 from rhodecode.lib.compat import OrderedDict
28 from rhodecode.lib.compat import OrderedDict
29 from rhodecode.lib.ext_json import json
29 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.vcs import nodes
30 from rhodecode.lib.vcs import nodes
31 from rhodecode.lib.vcs.backends.base import EmptyCommit
31
32 from rhodecode.lib.vcs.conf import settings
32 from rhodecode.lib.vcs.conf import settings
33 from rhodecode.lib.vcs.nodes import FileNode
34 from rhodecode.model.db import Repository
35 from rhodecode.model.scm import ScmModel
36 from rhodecode.tests import (
33 from rhodecode.tests import (
37 url, TEST_USER_ADMIN_LOGIN, assert_session_flash, assert_not_in_session_flash)
34 url, assert_session_flash, assert_not_in_session_flash)
38 from rhodecode.tests.fixture import Fixture
35 from rhodecode.tests.fixture import Fixture
39 from rhodecode.tests.utils import AssertResponse
40
36
41 fixture = Fixture()
37 fixture = Fixture()
42
38
@@ -48,40 +44,6 b' NODE_HISTORY = {'
48
44
49
45
50
46
51 def _commit_change(
52 repo, filename, content, message, vcs_type, parent=None,
53 newfile=False):
54 repo = Repository.get_by_repo_name(repo)
55 _commit = parent
56 if not parent:
57 _commit = EmptyCommit(alias=vcs_type)
58
59 if newfile:
60 nodes = {
61 filename: {
62 'content': content
63 }
64 }
65 commit = ScmModel().create_nodes(
66 user=TEST_USER_ADMIN_LOGIN, repo=repo,
67 message=message,
68 nodes=nodes,
69 parent_commit=_commit,
70 author=TEST_USER_ADMIN_LOGIN,
71 )
72 else:
73 commit = ScmModel().commit_change(
74 repo=repo.scm_instance(), repo_name=repo.repo_name,
75 commit=parent, user=TEST_USER_ADMIN_LOGIN,
76 author=TEST_USER_ADMIN_LOGIN,
77 message=message,
78 content=content,
79 f_path=filename
80 )
81 return commit
82
83
84
85 @pytest.mark.usefixtures("app")
47 @pytest.mark.usefixtures("app")
86 class TestFilesController:
48 class TestFilesController:
87
49
@@ -120,7 +82,7 b' class TestFilesController:'
120 response = self.app.get(url(
82 response = self.app.get(url(
121 controller='files', action='index',
83 controller='files', action='index',
122 repo_name=repo.repo_name, revision='tip', f_path='/'))
84 repo_name=repo.repo_name, revision='tip', f_path='/'))
123 assert_response = AssertResponse(response)
85 assert_response = response.assert_response()
124 assert_response.contains_one_link(
86 assert_response.contains_one_link(
125 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
87 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
126
88
@@ -130,7 +92,7 b' class TestFilesController:'
130 response = self.app.get(url(
92 response = self.app.get(url(
131 controller='files', action='index',
93 controller='files', action='index',
132 repo_name=repo.repo_name, revision='tip', f_path='/'))
94 repo_name=repo.repo_name, revision='tip', f_path='/'))
133 assert_response = AssertResponse(response)
95 assert_response = response.assert_response()
134 assert_response.contains_one_link(
96 assert_response.contains_one_link(
135 'subpaths-path @ 000000000000',
97 'subpaths-path @ 000000000000',
136 'http://sub-base.example.com/subpaths-path')
98 'http://sub-base.example.com/subpaths-path')
@@ -179,21 +141,24 b' class TestFilesController:'
179 assert_dirs_in_response(response, dirs, params)
141 assert_dirs_in_response(response, dirs, params)
180 assert_files_in_response(response, files, params)
142 assert_files_in_response(response, files, params)
181
143
182 @pytest.mark.xfail_backends("git", reason="Missing branches in git repo")
183 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
184 def test_index_different_branch(self, backend):
144 def test_index_different_branch(self, backend):
185 # TODO: Git test repository does not contain branches
145 branches = dict(
186 # TODO: Branch support in Subversion
146 hg=(150, ['git']),
187
147 # TODO: Git test repository does not contain other branches
188 commit = backend.repo.get_commit(commit_idx=150)
148 git=(633, ['master']),
149 # TODO: Branch support in Subversion
150 svn=(150, [])
151 )
152 idx, branches = branches[backend.alias]
153 commit = backend.repo.get_commit(commit_idx=idx)
189 response = self.app.get(url(
154 response = self.app.get(url(
190 controller='files', action='index',
155 controller='files', action='index',
191 repo_name=backend.repo_name,
156 repo_name=backend.repo_name,
192 revision=commit.raw_id,
157 revision=commit.raw_id,
193 f_path='/'))
158 f_path='/'))
194 assert_response = AssertResponse(response)
159 assert_response = response.assert_response()
195 assert_response.element_contains(
160 for branch in branches:
196 '.tags .branchtag', 'git')
161 assert_response.element_contains('.tags .branchtag', branch)
197
162
198 def test_index_paging(self, backend):
163 def test_index_paging(self, backend):
199 repo = backend.repo
164 repo = backend.repo
@@ -221,7 +186,7 b' class TestFilesController:'
221 msgbox = """<div class="commit right-content">%s</div>"""
186 msgbox = """<div class="commit right-content">%s</div>"""
222 response.mustcontain(msgbox % (commit.message, ))
187 response.mustcontain(msgbox % (commit.message, ))
223
188
224 assert_response = AssertResponse(response)
189 assert_response = response.assert_response()
225 if commit.branch:
190 if commit.branch:
226 assert_response.element_contains('.tags.tags-main .branchtag', commit.branch)
191 assert_response.element_contains('.tags.tags-main .branchtag', commit.branch)
227 if commit.tags:
192 if commit.tags:
@@ -348,7 +313,7 b' class TestFilesController:'
348 f_path='/', commit_id=commit.raw_id),
313 f_path='/', commit_id=commit.raw_id),
349 extra_environ=xhr_header)
314 extra_environ=xhr_header)
350
315
351 assert_response = AssertResponse(response)
316 assert_response = response.assert_response()
352
317
353 for attr in ['data-commit-id', 'data-date', 'data-author']:
318 for attr in ['data-commit-id', 'data-date', 'data-author']:
354 elements = assert_response.get_elements('[{}]'.format(attr))
319 elements = assert_response.get_elements('[{}]'.format(attr))
@@ -401,7 +366,7 b' class TestFilesController:'
401 # TODO: johbo: Think about a better place for these tests. Either controller
366 # TODO: johbo: Think about a better place for these tests. Either controller
402 # specific unit tests or we move down the whole logic further towards the vcs
367 # specific unit tests or we move down the whole logic further towards the vcs
403 # layer
368 # layer
404 class TestAdjustFilePathForSvn:
369 class TestAdjustFilePathForSvn(object):
405 """SVN specific adjustments of node history in FileController."""
370 """SVN specific adjustments of node history in FileController."""
406
371
407 def test_returns_path_relative_to_matched_reference(self):
372 def test_returns_path_relative_to_matched_reference(self):
@@ -433,7 +398,7 b' class TestAdjustFilePathForSvn:'
433
398
434
399
435 @pytest.mark.usefixtures("app")
400 @pytest.mark.usefixtures("app")
436 class TestRepositoryArchival:
401 class TestRepositoryArchival(object):
437
402
438 def test_archival(self, backend):
403 def test_archival(self, backend):
439 backend.enable_downloads()
404 backend.enable_downloads()
@@ -485,7 +450,7 b' class TestRepositoryArchival:'
485
450
486
451
487 @pytest.mark.usefixtures("app", "autologin_user")
452 @pytest.mark.usefixtures("app", "autologin_user")
488 class TestRawFileHandling:
453 class TestRawFileHandling(object):
489
454
490 def test_raw_file_ok(self, backend):
455 def test_raw_file_ok(self, backend):
491 commit = backend.repo.get_commit(commit_idx=173)
456 commit = backend.repo.get_commit(commit_idx=173)
@@ -575,6 +540,7 b' class TestFilesDiff:'
575 def test_file_full_diff(self, backend, diff):
540 def test_file_full_diff(self, backend, diff):
576 commit1 = backend.repo.get_commit(commit_idx=-1)
541 commit1 = backend.repo.get_commit(commit_idx=-1)
577 commit2 = backend.repo.get_commit(commit_idx=-2)
542 commit2 = backend.repo.get_commit(commit_idx=-2)
543
578 response = self.app.get(
544 response = self.app.get(
579 url(
545 url(
580 controller='files',
546 controller='files',
@@ -582,11 +548,17 b' class TestFilesDiff:'
582 repo_name=backend.repo_name,
548 repo_name=backend.repo_name,
583 f_path='README'),
549 f_path='README'),
584 params={
550 params={
585 'diff1': commit1.raw_id,
551 'diff1': commit2.raw_id,
586 'diff2': commit2.raw_id,
552 'diff2': commit1.raw_id,
587 'fulldiff': '1',
553 'fulldiff': '1',
588 'diff': diff,
554 'diff': diff,
589 })
555 })
556
557 if diff == 'diff':
558 # use redirect since this is OLD view redirecting to compare page
559 response = response.follow()
560
561 # It's a symlink to README.rst
590 response.mustcontain('README.rst')
562 response.mustcontain('README.rst')
591 response.mustcontain('No newline at end of file')
563 response.mustcontain('No newline at end of file')
592
564
@@ -610,7 +582,17 b' class TestFilesDiff:'
610 'fulldiff': '1',
582 'fulldiff': '1',
611 'diff': 'diff',
583 'diff': 'diff',
612 })
584 })
613 response.mustcontain('Cannot diff binary files')
585 # use redirect since this is OLD view redirecting to compare page
586 response = response.follow()
587 response.mustcontain('Expand 1 commit')
588 response.mustcontain('1 file changed: 0 inserted, 0 deleted')
589
590 if backend.alias == 'svn':
591 response.mustcontain('new file 10644')
592 # TODO(marcink): SVN doesn't yet detect binary changes
593 else:
594 response.mustcontain('new file 100644')
595 response.mustcontain('binary diff hidden')
614
596
615 def test_diff_2way(self, backend):
597 def test_diff_2way(self, backend):
616 commit1 = backend.repo.get_commit(commit_idx=-1)
598 commit1 = backend.repo.get_commit(commit_idx=-1)
@@ -622,14 +604,15 b' class TestFilesDiff:'
622 repo_name=backend.repo_name,
604 repo_name=backend.repo_name,
623 f_path='README'),
605 f_path='README'),
624 params={
606 params={
625 'diff1': commit1.raw_id,
607 'diff1': commit2.raw_id,
626 'diff2': commit2.raw_id,
608 'diff2': commit1.raw_id,
627 })
609 })
610 # use redirect since this is OLD view redirecting to compare page
611 response = response.follow()
628
612
629 # Expecting links to both variants of the file. Links are used
613 # It's a symlink to README.rst
630 # to load the content dynamically.
614 response.mustcontain('README.rst')
631 response.mustcontain('/%s/README' % commit1.raw_id)
615 response.mustcontain('No newline at end of file')
632 response.mustcontain('/%s/README' % commit2.raw_id)
633
616
634 def test_requires_one_commit_id(self, backend, autologin_user):
617 def test_requires_one_commit_id(self, backend, autologin_user):
635 response = self.app.get(
618 response = self.app.get(
@@ -642,21 +625,23 b' class TestFilesDiff:'
642 response.mustcontain(
625 response.mustcontain(
643 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
626 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
644
627
645 def test_returns_not_found_if_file_does_not_exist(self, vcsbackend):
628 def test_returns_no_files_if_file_does_not_exist(self, vcsbackend):
646 repo = vcsbackend.repo
629 repo = vcsbackend.repo
647 self.app.get(
630 response = self.app.get(
648 url(
631 url(
649 controller='files',
632 controller='files',
650 action='diff',
633 action='diff',
651 repo_name=repo.name,
634 repo_name=repo.name,
652 f_path='does-not-exist-in-any-commit',
635 f_path='does-not-exist-in-any-commit',
653 diff1=repo[0].raw_id,
636 diff1=repo[0].raw_id,
654 diff2=repo[1].raw_id),
637 diff2=repo[1].raw_id),)
655 status=404)
638
639 response = response.follow()
640 response.mustcontain('No files')
656
641
657 def test_returns_redirect_if_file_not_changed(self, backend):
642 def test_returns_redirect_if_file_not_changed(self, backend):
658 commit = backend.repo.get_commit(commit_idx=-1)
643 commit = backend.repo.get_commit(commit_idx=-1)
659 f_path= 'README'
644 f_path = 'README'
660 response = self.app.get(
645 response = self.app.get(
661 url(
646 url(
662 controller='files',
647 controller='files',
@@ -666,25 +651,40 b' class TestFilesDiff:'
666 diff1=commit.raw_id,
651 diff1=commit.raw_id,
667 diff2=commit.raw_id,
652 diff2=commit.raw_id,
668 ),
653 ),
669 status=302
670 )
654 )
671 assert response.headers['Location'].endswith(f_path)
655 response = response.follow()
672 redirected = response.follow()
656 response.mustcontain('No files')
673 redirected.mustcontain('has not changed between')
657 response.mustcontain('No commits in this compare')
674
658
675 def test_supports_diff_to_different_path_svn(self, backend_svn):
659 def test_supports_diff_to_different_path_svn(self, backend_svn):
660 #TODO: check this case
661 return
662
676 repo = backend_svn['svn-simple-layout'].scm_instance()
663 repo = backend_svn['svn-simple-layout'].scm_instance()
677 commit_id = repo[-1].raw_id
664 commit_id_1 = '24'
665 commit_id_2 = '26'
666
667
668 print( url(
669 controller='files',
670 action='diff',
671 repo_name=repo.name,
672 f_path='trunk/example.py',
673 diff1='tags/v0.2/example.py@' + commit_id_1,
674 diff2=commit_id_2))
675
678 response = self.app.get(
676 response = self.app.get(
679 url(
677 url(
680 controller='files',
678 controller='files',
681 action='diff',
679 action='diff',
682 repo_name=repo.name,
680 repo_name=repo.name,
683 f_path='trunk/example.py',
681 f_path='trunk/example.py',
684 diff1='tags/v0.2/example.py@' + commit_id,
682 diff1='tags/v0.2/example.py@' + commit_id_1,
685 diff2=commit_id),
683 diff2=commit_id_2))
686 status=200)
684
685 response = response.follow()
687 response.mustcontain(
686 response.mustcontain(
687 # diff contains this
688 "Will print out a useful message on invocation.")
688 "Will print out a useful message on invocation.")
689
689
690 # Note: Expecting that we indicate the user what's being compared
690 # Note: Expecting that we indicate the user what's being compared
@@ -692,6 +692,9 b' class TestFilesDiff:'
692 response.mustcontain("tags/v0.2/example.py")
692 response.mustcontain("tags/v0.2/example.py")
693
693
694 def test_show_rev_redirects_to_svn_path(self, backend_svn):
694 def test_show_rev_redirects_to_svn_path(self, backend_svn):
695 #TODO: check this case
696 return
697
695 repo = backend_svn['svn-simple-layout'].scm_instance()
698 repo = backend_svn['svn-simple-layout'].scm_instance()
696 commit_id = repo[-1].raw_id
699 commit_id = repo[-1].raw_id
697 response = self.app.get(
700 response = self.app.get(
@@ -708,6 +711,9 b' class TestFilesDiff:'
708 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
711 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
709
712
710 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
713 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
714 #TODO: check this case
715 return
716
711 repo = backend_svn['svn-simple-layout'].scm_instance()
717 repo = backend_svn['svn-simple-layout'].scm_instance()
712 commit_id = repo[-1].raw_id
718 commit_id = repo[-1].raw_id
713 response = self.app.get(
719 response = self.app.get(
@@ -979,100 +985,3 b' def _assert_items_in_response(response, '
979 def assert_timeago_in_response(response, items, params):
985 def assert_timeago_in_response(response, items, params):
980 for item in items:
986 for item in items:
981 response.mustcontain(h.age_component(params['date']))
987 response.mustcontain(h.age_component(params['date']))
982
983
984
985 @pytest.mark.usefixtures("autologin_user", "app")
986 class TestSideBySideDiff:
987
988 def test_diff2way(self, app, backend, backend_stub):
989 f_path = 'content'
990 commit1_content = 'content-25d7e49c18b159446c'
991 commit2_content = 'content-603d6c72c46d953420'
992 repo = backend.create_repo()
993
994 commit1 = _commit_change(
995 repo.repo_name, filename=f_path, content=commit1_content,
996 message='A', vcs_type=backend.alias, parent=None, newfile=True)
997
998 commit2 = _commit_change(
999 repo.repo_name, filename=f_path, content=commit2_content,
1000 message='B, child of A', vcs_type=backend.alias, parent=commit1)
1001
1002 response = self.app.get(url(
1003 controller='files', action='diff_2way',
1004 repo_name=repo.repo_name,
1005 diff1=commit1.raw_id,
1006 diff2=commit2.raw_id,
1007 f_path=f_path))
1008
1009 assert_response = AssertResponse(response)
1010 response.mustcontain(
1011 ('Side-by-side Diff r0:%s ... r1:%s') % ( commit1.short_id, commit2.short_id ))
1012 response.mustcontain('id="compare"')
1013 response.mustcontain((
1014 "var orig1_url = '/%s/raw/%s/%s';\n"
1015 "var orig2_url = '/%s/raw/%s/%s';") %
1016 ( repo.repo_name, commit1.raw_id, f_path,
1017 repo.repo_name, commit2.raw_id, f_path))
1018
1019
1020 def test_diff2way_with_empty_file(self, app, backend, backend_stub):
1021 commits = [
1022 {'message': 'First commit'},
1023 {'message': 'Commit with binary',
1024 'added': [nodes.FileNode('file.empty', content='')]},
1025 ]
1026 f_path='file.empty'
1027 repo = backend.create_repo(commits=commits)
1028 commit_id1 = repo.get_commit(commit_idx=0).raw_id
1029 commit_id2 = repo.get_commit(commit_idx=1).raw_id
1030
1031 response = self.app.get(url(
1032 controller='files', action='diff_2way',
1033 repo_name=repo.repo_name,
1034 diff1=commit_id1,
1035 diff2=commit_id2,
1036 f_path=f_path))
1037
1038 assert_response = AssertResponse(response)
1039 if backend.alias == 'svn':
1040 assert_session_flash( response,
1041 ('%(file_path)s has not changed') % { 'file_path': 'file.empty' })
1042 else:
1043 response.mustcontain(
1044 ('Side-by-side Diff r0:%s ... r1:%s') % ( repo.get_commit(commit_idx=0).short_id, repo.get_commit(commit_idx=1).short_id ))
1045 response.mustcontain('id="compare"')
1046 response.mustcontain((
1047 "var orig1_url = '/%s/raw/%s/%s';\n"
1048 "var orig2_url = '/%s/raw/%s/%s';") %
1049 ( repo.repo_name, commit_id1, f_path,
1050 repo.repo_name, commit_id2, f_path))
1051
1052
1053 def test_empty_diff_2way_redirect_to_summary_with_alert(self, app, backend):
1054 commit_id_range = {
1055 'hg': (
1056 '25d7e49c18b159446cadfa506a5cf8ad1cb04067',
1057 '603d6c72c46d953420c89d36372f08d9f305f5dd'),
1058 'git': (
1059 '6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
1060 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'),
1061 'svn': (
1062 '335',
1063 '337'),
1064 }
1065 f_path = 'setup.py'
1066
1067 commit_ids = commit_id_range[backend.alias]
1068
1069 response = self.app.get(url(
1070 controller='files', action='diff_2way',
1071 repo_name=backend.repo_name,
1072 diff2=commit_ids[0],
1073 diff1=commit_ids[1],
1074 f_path=f_path))
1075
1076 assert_response = AssertResponse(response)
1077 assert_session_flash( response,
1078 ('%(file_path)s has not changed') % { 'file_path': f_path })
@@ -531,6 +531,81 b' DIFF_FIXTURES = ['
531 }),
531 }),
532 ]),
532 ]),
533
533
534 ('svn',
535 'svn_diff_binary_add_file.diff',
536 [('intl.dll', 'A',
537 {'added': 0,
538 'deleted': 0,
539 'binary': False,
540 'ops': {NEW_FILENODE: 'new file 10644',
541 #TODO(Marcink): depends on binary detection on svn patches
542 # BIN_FILENODE: 'binary diff hidden'
543 }
544 }),
545 ]),
546
547 ('svn',
548 'svn_diff_multiple_changes.diff',
549 [('trunk/doc/images/SettingsOverlay.png', 'M',
550 {'added': 0,
551 'deleted': 0,
552 'binary': False,
553 'ops': {MOD_FILENODE: 'modified file',
554 #TODO(Marcink): depends on binary detection on svn patches
555 # BIN_FILENODE: 'binary diff hidden'
556 }
557 }),
558 ('trunk/doc/source/de/tsvn_ch04.xml', 'M',
559 {'added': 89,
560 'deleted': 34,
561 'binary': False,
562 'ops': {MOD_FILENODE: 'modified file'}
563 }),
564 ('trunk/doc/source/en/tsvn_ch04.xml', 'M',
565 {'added': 66,
566 'deleted': 21,
567 'binary': False,
568 'ops': {MOD_FILENODE: 'modified file'}
569 }),
570 ('trunk/src/Changelog.txt', 'M',
571 {'added': 2,
572 'deleted': 0,
573 'binary': False,
574 'ops': {MOD_FILENODE: 'modified file'}
575 }),
576 ('trunk/src/Resources/TortoiseProcENG.rc', 'M',
577 {'added': 19,
578 'deleted': 13,
579 'binary': False,
580 'ops': {MOD_FILENODE: 'modified file'}
581 }),
582 ('trunk/src/TortoiseProc/SetOverlayPage.cpp', 'M',
583 {'added': 16,
584 'deleted': 1,
585 'binary': False,
586 'ops': {MOD_FILENODE: 'modified file'}
587 }),
588 ('trunk/src/TortoiseProc/SetOverlayPage.h', 'M',
589 {'added': 3,
590 'deleted': 0,
591 'binary': False,
592 'ops': {MOD_FILENODE: 'modified file'}
593 }),
594 ('trunk/src/TortoiseProc/resource.h', 'M',
595 {'added': 2,
596 'deleted': 0,
597 'binary': False,
598 'ops': {MOD_FILENODE: 'modified file'}
599 }),
600 ('trunk/src/TortoiseShell/ShellCache.h', 'M',
601 {'added': 50,
602 'deleted': 1,
603 'binary': False,
604 'ops': {MOD_FILENODE: 'modified file'}
605 }),
606 ]),
607
608
534 # TODO: mikhail: do we still need this?
609 # TODO: mikhail: do we still need this?
535 # (
610 # (
536 # 'hg',
611 # 'hg',
@@ -579,7 +654,6 b' DIFF_FIXTURES = ['
579 # 'pylons_app.egg-info/dependency_links.txt', 'A', {
654 # 'pylons_app.egg-info/dependency_links.txt', 'A', {
580 # 'deleted': 0, 'binary': False, 'added': 1, 'ops': {
655 # 'deleted': 0, 'binary': False, 'added': 1, 'ops': {
581 # 1: 'new file 100644'}}),
656 # 1: 'new file 100644'}}),
582 # #TODO:
583 # ]
657 # ]
584 # ),
658 # ),
585 ]
659 ]
@@ -38,6 +38,7 b' from rhodecode.model.db import User, Rep'
38 from rhodecode.model.meta import Session
38 from rhodecode.model.meta import Session
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
40 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
41 from rhodecode.lib.vcs.backends.base import EmptyCommit
41
42
42
43
43 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
@@ -372,3 +373,37 b' def repo_on_filesystem(repo_name):'
372 repo = vcs.get_vcs_instance(
373 repo = vcs.get_vcs_instance(
373 os.path.join(TESTS_TMP_PATH, repo_name), create=False)
374 os.path.join(TESTS_TMP_PATH, repo_name), create=False)
374 return repo is not None
375 return repo is not None
376
377
378 def commit_change(
379 repo, filename, content, message, vcs_type, parent=None, newfile=False):
380 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
381
382 repo = Repository.get_by_repo_name(repo)
383 _commit = parent
384 if not parent:
385 _commit = EmptyCommit(alias=vcs_type)
386
387 if newfile:
388 nodes = {
389 filename: {
390 'content': content
391 }
392 }
393 commit = ScmModel().create_nodes(
394 user=TEST_USER_ADMIN_LOGIN, repo=repo,
395 message=message,
396 nodes=nodes,
397 parent_commit=_commit,
398 author=TEST_USER_ADMIN_LOGIN,
399 )
400 else:
401 commit = ScmModel().commit_change(
402 repo=repo.scm_instance(), repo_name=repo.repo_name,
403 commit=parent, user=TEST_USER_ADMIN_LOGIN,
404 author=TEST_USER_ADMIN_LOGIN,
405 message=message,
406 content=content,
407 f_path=filename
408 )
409 return commit
@@ -359,14 +359,15 b' class TestSvnGetDiff:'
359 ], ids=['file', 'dir'])
359 ], ids=['file', 'dir'])
360 def test_diff_to_tagged_version(self, vcsbackend_svn, path, path1):
360 def test_diff_to_tagged_version(self, vcsbackend_svn, path, path1):
361 repo = vcsbackend_svn['svn-simple-layout']
361 repo = vcsbackend_svn['svn-simple-layout']
362 commit = repo[-1]
362 commit1 = repo[-2]
363 diff = repo.get_diff(commit, commit, path=path, path1=path1)
363 commit2 = repo[-1]
364 diff = repo.get_diff(commit1, commit2, path=path, path1=path1)
364 assert diff.raw == self.expected_diff_v_0_2
365 assert diff.raw == self.expected_diff_v_0_2
365
366
366 expected_diff_v_0_2 = '''Index: example.py
367 expected_diff_v_0_2 = '''Index: example.py
367 ===================================================================
368 ===================================================================
368 diff --git a/example.py b/example.py
369 diff --git a/example.py b/example.py
369 --- a/example.py\t(revision 26)
370 --- a/example.py\t(revision 25)
370 +++ b/example.py\t(revision 26)
371 +++ b/example.py\t(revision 26)
371 @@ -7,8 +7,12 @@
372 @@ -7,8 +7,12 @@
372
373
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (1669 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now