tagged this wishlist; was I supposed to do that?
[ikiwiki] / doc / rcs / details.mdwn
1 A few bits about the RCS backends
2
3 [[!toc ]]
4
5 ## Terminology
6
7 ``web-edit'' means that a page is edited by using the web (CGI) interface
8 as opposed to using a editor and the RCS interface.
9
10
11 ## [[svn]]
12
13 Subversion was the first RCS to be supported by ikiwiki.
14
15 ### How does it work internally?
16
17 Master repository M.
18
19 RCS commits from the outside are installed into M.
20
21 There is a working copy of M (a checkout of M): W.
22
23 HTML is generated from W.  rcs_update() will update from M to W.
24
25 CGI operates on W.  rcs_commit() will commit from W to M.
26
27 For all the gory details of how ikiwiki handles this behind the scenes,
28 see [[commit-internals]].
29
30 You browse and web-edit the wiki on W.
31
32 W "belongs" to ikiwiki and should not be edited directly.
33
34
35 ## [[darcs]]
36
37 Regarding the repository layout: There are two darcs repositories. One is the `srcdir`, the other we'll call `master`.
38
39 *  HTML is generated from `srcdir`.
40 *  CGI edits happen in `srcdir`.
41 *  The backend pulls updates from `master` into `srcdir`, i.e. darcs commits should happen to `master`.
42 *  `master` calls ikiwiki (through a wrapper) in its apply posthook, i.e. `master/_darcs/prefs/defaults` should look like this:
43
44         apply posthook ikiwrap
45         apply run-posthook
46
47 *  The backend pushes CGI edits from `srcdir` back into `master` (triggering the apply hook).
48 *  The working copies in `srcdir` and `master` should *not* be touched by the user, only by the CGI or darcs, respectively.
49
50 ## [[Git]]
51
52 Regarding the Git support, Recai says:
53
54 I have been testing it for the past few days and it seems satisfactory.  I
55 haven't observed any race condition regarding the concurrent blog commits
56 and it handles merge conflicts gracefully as far as I can see.
57
58 (After about a year, git support is nearly as solid as subversion support --[[Joey]])
59
60 As you may notice from the patch size, GIT support is not so trivial to
61 implement (for me, at least). It has some drawbacks (especially wrt merge
62 which was the hard part).  GIT doesn't have a similar functionality like
63 'svn merge -rOLD:NEW FILE' (please see the relevant comment in `_merge_past`
64 for more details), so I had to invent an ugly hack just for the purpose.
65
66 > I was looking at this, and WRT the problem of uncommitted local changes,
67 > it seems to me you could just git-stash them now that git-stash exists.
68 > I think it didn't when you first added the git support.. --[[Joey]]
69
70
71 >> Yes,  git-stash had not existed before.  What about sth like below?  It
72 >> seems to work (I haven't given much thought on the specific implementation
73 details).  --[[roktas]]
74
75 >>          # create test files
76 >>          cd /tmp
77 >>          seq 6 >page
78 >>          cat page
79 >>          1
80 >>          2
81 >>          3
82 >>          4
83 >>          5
84 >>          6
85 >>          sed -e 's/2/2ME/' page >page.me # my changes
86 >>          cat page
87 >>          1
88 >>          2ME
89 >>          3
90 >>          4
91 >>          5
92 >>          6
93 >>          sed -e 's/5/5SOMEONE/' page >page.someone # someone's changes
94 >>          cat page
95 >>          1
96 >>          2
97 >>          3
98 >>          4
99 >>          5SOMEONE
100 >>          6
101 >>
102 >>          # create a test repository
103 >>          mkdir t
104 >>          cd t
105 >>          cp ../page .
106 >>          git init
107 >>          git add .
108 >>          git commit -m init
109 >>
110 >>          # save the current HEAD
111 >>          ME=$(git rev-list HEAD -- page)
112 >>          $EDITOR page # assume that I'm starting to edit page via web
113 >>
114 >>          # simulates someone's concurrent commit
115 >>          cp ../page.someone page
116 >>          git commit -m someone -- page
117 >>
118 >>          # My editing session ended, the resulting content is in page.me
119 >>          cp ../page.me page
120 >>          cat page
121 >>          1
122 >>          2ME
123 >>          3
124 >>          4
125 >>          5
126 >>          6
127 >>
128 >>          # let's start to save my uncommitted changes
129 >>          git stash clear
130 >>          git stash save "changes by me"
131 >>          # we've reached a clean state
132 >>          cat page
133 >>          1
134 >>          2
135 >>          3
136 >>          4
137 >>          5SOMEONE
138 >>          6
139 >>
140 >>          # roll-back to the $ME state
141 >>          git reset --soft $ME
142 >>          # now, the file is marked as modified
143 >>          git stash save "changes by someone"
144 >>
145 >>          # now, we're at the $ME state
146 >>          cat page
147 >>          1
148 >>          2
149 >>          3
150 >>          4
151 >>          5
152 >>          6
153 >>          git stash list
154 >>          stash@{0}: On master: changes by someone
155 >>          stash@{1}: On master: changes by me
156 >>
157 >>          # first apply my changes
158 >>          git stash apply stash@{1}
159 >>          cat page
160 >>          1
161 >>          2ME
162 >>          3
163 >>          4
164 >>          5
165 >>          6
166 >>          # ... and commit
167 >>          git commit -m me -- page
168 >>
169 >>          # apply someone's changes
170 >>          git stash apply stash@{0}
171 >>          cat page
172 >>          1
173 >>          2ME
174 >>          3
175 >>          4
176 >>          5SOMEONE
177 >>          6
178 >>          # ... and commit
179 >>          git commit -m me+someone -- page
180
181 By design, Git backend uses a "master-clone" repository pair approach in contrast
182 to the single repository approach (here, _clone_ may be considered as the working
183 copy of a fictious web user).  Even though a single repository implementation is
184 possible, it somewhat increases the code complexity of backend (I couldn't figure
185 out a uniform method which doesn't depend on the prefered repository model, yet).
186 By exploiting the fact that the master repo and _web user_'s repo (`srcdir`) are all
187 on the same local machine, I suggest to create the latter with the "`git clone -l -s`"
188 command to save disk space.
189
190 Note that, as a rule of thumb, you should always put the rcs wrapper (`post-update`)
191 into the master repository (`.git/hooks/`).
192
193 Here is how a web edit works with ikiwiki and git:
194
195 * ikiwiki cgi modifies the page source in the clone
196 * git-commit in the clone
197 * git push origin master, pushes the commit from the clone to the master repo
198 * the master repo's post-update hook notices this update, and runs ikiwiki
199 * ikiwiki notices the modifies page source, and compiles it
200
201 Here is a how a commit from a remote repository works:
202
203 * git-commit in the remote repository
204 * git-push, pushes the commit to the master repo on the server
205 * (Optionally, the master repo's pre-receive hook runs, and checks that the
206   update only modifies files that the pushing user is allowed to update. 
207   If not, it aborts the receive.)
208 * the master repo's post-update hook notices this update, and runs ikiwiki
209 * ikiwiki notices the modifies page source, and compiles it
210
211 ## [[Mercurial]]
212
213 The Mercurial backend is still in a early phase, so it may not be mature 
214 enough, but it should be simple to understand and use.
215
216 As Mercurial is a distributed RCS, it lacks the distinction between 
217 repository and working copy (every wc is a repo).
218
219 This means that the Mercurial backend uses directly the repository as 
220 working copy (the master M and the working copy W described in the svn 
221 example are the same thing).
222
223 You only need to specify 'srcdir' (the repository M) and 'destdir' (where
224 the HTML will be generated).
225
226 Master repository M.
227
228 RCS commit from the outside are installed into M.
229
230 M is directly used as working copy (M is also W).
231
232 HTML is generated from the working copy in M. rcs_update() will update 
233 to the last committed revision in M (the same as 'hg update').
234 If you use an 'update' hook you can generate automatically the HTML
235 in the destination directory each time 'hg update' is called.
236
237 CGI operates on M. rcs_commit() will commit directly in M.
238
239 If you have any question or suggestion about the Mercurial backend 
240 please refer to [Emanuele](http://nerd.ocracy.org/em/)
241
242 ## [[tla]]
243
244 Nobody really understands how tla works. ;-)
245
246 ## rcs
247
248 There is a patch that needs a bit of work linked to from [[todo/rcs]].
249
250 ## [[Monotone]]
251
252 In normal use, monotone has a local database as well as a workspace/working copy.
253 In ikiwiki terms, the local database takes the role of the master repository, and
254 the srcdir is the workspace.  As all monotone workspaces point to a default
255 database, there is no need to tell ikiwiki explicitly about the "master" database.  It
256 will know.
257
258 The backend currently supports normal committing and getting the history of the page.
259 To understand the parallel commit approach, you need to understand monotone's
260 approach to conflicts:
261
262 Monotone allows multiple micro-branches in the database.  There is a command,
263 `mtn merge`, that takes the heads of all these branches and merges them back together
264 (turning the tree of branches into a dag).  Conflicts in monotone (at time of writing)
265 need to be resolved interactively during this merge process.
266 It is important to note that having multiple heads is not an error condition in a
267 monotone database.  This condition will occur in normal use.  In this case
268 'update' will choose a head if it can, or complain and tell the user to merge.
269
270 For the ikiwiki plugin, the monotone ikiwiki plugin borrows some ideas from the svn ikiwiki plugin.
271 On prepedit() we record the revision that this change is based on (I'll refer to this as the prepedit revision).  When the web user
272 saves the page, we check if that is still the current revision.  If it is, then we commit.
273 If it isn't then we check to see if there were any changes by anyone else to the file
274 we're editing while we've been editing (a diff bewteen the prepedit revision and the current rev).
275 If there were no changes to the file we're editing then we commit as normal.
276
277 It is only if there have been parallel changes to the file we're trying to commit that
278 things get hairy.  In this case the current approach is to
279 commit the web changes as a branch from the prepedit revision.  This
280 will leave the repository with multiple heads.  At this point, all data is saved.
281 The system then tries to merge the heads with a merger that will fail if it cannot
282 resolve the conflict.  If the merge succeeds then everything is ok.
283
284 If that merge failed then there are conflicts.  In this case, the current code calls
285 merge again with a merger that inserts conflict markers.  It commits this new
286 revision with conflict markers to the repository.  It then returns the text to the
287 user for cleanup.  This is less neat than it could be, in that a conflict marked
288 revision gets committed to the repository.
289
290 ## [[bzr]]
291
292 ## [[cvs]]