2 The Problem We're Solving
3 -------------------------
5 Subversion users typically edit sets of files in their working copy.
6 If a working copy contains a set of edited files which represents a
7 single logical change, then commands like 'svn diff', 'svn status',
8 'svn revert' and 'svn commit' automatically discover the edited files
11 A common problem, however, is that users often work on more than one
12 set of logical changes at a time. The user is required to remember
13 which edited file belongs to which set, and carefully run 'diff',
14 'revert', or 'commit' commands only on lists of files which belong
17 One workaround for this problem is to checkout multiple working
18 copies, and have one task per working copy. Of course, this uses a
19 lot of disk space, and it's sometimes inconvenient to move around
20 between working copies.
22 The simple solution we're proposing here is to teach the svn client
23 (and working copy) do some simple management of local, human-named
24 sets of files, known as 'changesets'. The goal is to allow users to
25 create, view, and manipulate sets of files in a working copy by
26 referring to them by name.
29 Doesn't Perforce Do This?
30 -------------------------
32 Perforce performs changelist management, and it's a large motivation
33 for this new feature. But there's no way to emulate Perforce's
34 feature exactly; it has a different network model than Subversion. So
35 instead, we'll examine the use-cases that Perforce enables, and
36 discuss how to solve those same use-cases in Subversion.
42 Here are problems/features that are NOT in our list of goals:
44 * Server management of changesets
46 Subversion prides itself on being disconnected; that's why it
47 scales so well. A changeset is an ephemeral thing created by a
48 single user in a single working copy, whose only purpose is to
49 make it easier to manipulate a change-in-progress. It's not a
50 "named revision", or a long-lived object in the repository.
51 That's what global revision numbers are for.
53 Some people aren't happy with the way tags work in Subversion, and
54 have asked for the ability to identify repository revisions by
55 human name. While everybody wants to see the ability to search
56 over revprops (for many reasons!), that whole issue is out of
57 scope for this changelist feature. It's been suggested that when
58 a changelist gets committed, it become a searchable revprop;
59 sounds fine, but lets get changelists and searchable revprops
60 implemented independently first!
62 * Enforcement of groupings
64 Changesets don't exist as a prescriptive SCM process. Some have
65 suggested that the client not allow people to commit individual
66 files in a changelist, or to do some side of server-side process
67 enforcement revolving around changelists. This is definitely not
68 in the Subversion spirit, which allows teams to create whatever
69 policies they wish. The only purpose of changelists is to do
70 provide some convenient bookkeeping to the user.
72 * Overlapping changelists
74 A number of people ask "but what if two different changes within a
75 single file belong to different logical changes?" My reply is:
76 either "tough luck" or "don't do that" or "checkout a separate
77 working copy". My feeling is that trying to create a UI to
78 manipulate individual diff-hunks within a file is a HUGE can of
79 worms, probably best suited for a GUI. While I wouldn't rule it
80 out as a future *enhancement* to a changelist feature, it's
81 certainly not worth the initial effort in the first draft of
82 changelist management. Overlapping changelists do occasionally
83 happen, but they're rare enough that's it's not worth spending 90%
84 of our time on a 10% case -- at least not in the beginning.
86 * "Shelving" of changes
88 Distributed version control systems don't have this sort of
89 problem; one could just do a 'local commit' of each
90 changeset-in-progress, create local branches, and magically swap
91 patches in and out as needed. To that end, many have talked about
92 making subversion working copies into "deep" objects containing
93 some degree of history, or to write a nice 'svn patch' command to
94 read custom 'svn diff' output. My response is: nice ideas, and
95 those sort of really advanced designs are certainly things that
96 simple changelist management can grow to take advantage of, but
97 aren't prerequisites for tackling this problem.
104 A. Define a changelist by explicitly adding/removing paths to it.
106 B. See all existing changelist names (and their member paths)
108 C. Destroy a changelist definition all at once.
110 D. Examine all edits within a changelist (svn diff)
112 E. Revert all edits within a changelist (svn revert)
114 F. Commit all edits within a changelist (svn commit)
116 G. Receive server changes only paths within a changelist (svn update)
118 H. See the history of all paths within a changelst (svn log)
120 I. Fetch or set props on every path within a changelist (svn pl/ps/pe/pg/pd)
124 How Perforce Tackles the Use-Cases
125 ----------------------------------
127 A. Defining changelists
129 The Perforce server tracks each and every working copy, as well as
130 every changelist within every working copy. All working copy files
131 are read-only until the user declares the intent to edit ('p4 edit')
132 one. The server then makes the file read-write and places it into a
133 changelist with the name 'default'.
135 Users aren't allowed to invent their own names for changelists, as
136 this might lead to namespace overlaps. (This is a side effect of
137 having the server track all changelists.) 'p4 change' creates a new
138 changelist by prompting the user for a log message, at which point the
139 server yanks the 'next' global global revision number and assigns it
140 as a name for the changelist. The server not only tracks the
141 changelist via some number, but also tracks the
142 log-message-in-progress for the list. ('p4 describe' can show the log
143 message attached to a changelist.)
145 B. Viewing changelists
147 At any time, the 'p4 open' command shows all files that are being
148 edited, and which changelists they belong to. It's quite similar to
149 the 'svn status' command, except that the output is somewhat harder to
150 read, due to non-aligned columns.
152 The response time is also quite fast, since p4 doesn't need to crawl
153 the working copy to discover edited files. On the other hand, p4
154 doesn't scale so well when the server tries to track thousands of
157 C. Destroying changelists
159 'p4 change -d' will delete a changelist, but only if the edited files
160 within the changelist have been reverted.
162 D. Viewing edits in a changelist
164 'p4 diff' shows contextual diffs for all edited files. This is
165 actually a bit weak, as it shows diffs for *all* changelists in a
166 working copy. Subversion should improve on this by allowing one to
167 'diff' just a single changelist.
169 E. Reverting a changelist
171 'p4 revert -c NNN' reverts all edited files within changelist #NNN.
172 Note that it's also possible to revert single files ('p4 revert
173 foo.c'). If a single file within a changelist is reverted, its path
174 is removed from the changelist.
176 F. Committing a changelist
178 'p4 submit -c NNN' atomically commits changelist #NNN to the
179 repository. If the commit succeeds, a *new* global revision number is
180 assigned to the final commit, and the old 'NNN' number is discarded.
181 (This means that p4 actually burns through global revnums at twice the
182 speed as subversion!) After the commit, the working copy no longer
183 has any record of the changelist.
185 G. Updating a changelist
187 'p4 sync' is equivalent to 'svn up'. Like subversion, 'p4 sync' can
188 be restricted to specific path targets, but amazingly not restricted
189 to a set of paths that make up a changelist. This may be something
190 subversion can improve upon.
192 H. Examining the history of changelist members
194 'p4 changes' is the closest thing to 'svn log'. With no arguments, it
195 shows all changelists ever submitted. With specific path arguments,
196 it limits the response to showing only changelists that affected those
197 paths. Again, a changelist number cannot be supplied, which is
200 I. Propgets/sets on a changelist
202 Perforce has no versioned metadata.
207 Proposal for Subversion's Tackling of Use-Cases
208 -----------------------------------------------
210 A. Defining changelists
212 Subversion's changelist feature will be entirely client-side
213 bookkeeping. The purpose is to allow users to 'talk about' a set of
214 local paths via a convenient name, often restricting subcommands to
215 operate only on those paths.
217 The 'svn changelist' command allows a user to define a changelist with
218 an arbitrary UTF-8 name, as well as add member paths. (At the moment,
219 a --remove flag is used to remove member paths.) Unversioned items may
220 not be added to changelists.
222 $ svn changelist MYCHANGE foo.c bar.c
223 Path 'foo.c' is now part of changelist 'mychange'.
224 Path 'bar.c' is now part of changelist 'mychange'.
226 $ svn changelist bar.c --remove
227 Path 'bar.c' is no longer associated with a changelist.
230 ### Open question: should we add a UI which allows the working copy to
231 manage a log-message-in-progress for each changelist, the way p4
232 does? This could be something stored in ~/.subversion/ area.
235 B. Viewing changelists
237 'svn status' currently shows changelist definitions by crawling the
238 working copy. Output is much more readable than perforce, because
239 we're still preserving column alignment.
243 M notes/wc-improvements
245 --- Changelist 'status-cleanup':
246 M subversion/svn/main.c
247 subversion/svn/revert-cmd.c
248 M subversion/svn/info-cmd.c
250 --- Changelist 'status-printing':
251 M subversion/svn/status-cmd.c
255 Note that unlike perforce, changelist membership is orthogonal to
256 whether or not the file has local modifications. So it's possible for
257 'svn status' to show a changelist containing unmodified files.
258 Conversely, it's possible for a file to be modified, but unassociated
261 'svn status' considers changelist membership to be inherently
262 "interesting enough" to justify displaying a path, regardless of
263 whether it's modified.
265 Note that merely upgrading subversion won't break scripts that parse
266 'svn status' output. Such scripts might break *only* if users begin
267 to use the new changelist feature. This is a good balance between
268 allowing subversion's development to progress, while not automatically
269 punishing users for upgrading. (Either way, the "---" characters
270 should prevent scripts from accidentally detecting conflicts with "^C"
271 regular expressions.)
273 ### Open question: at the moment, changelists are implemented by
274 simply storing a new attribute in the .svn/entries file. Rather
275 than having the svn client crawl and 'discover' changelists,
276 should we take a hint from p4 and have them centrally managed in
277 the ~/.subversion/ area?
280 - much faster than crawling
281 - whole changelist definition available, regardless of CWD
284 - breaks the 'portable WC' ideal. (If WC moves to another box,
285 changelist definition is lost.)
288 ### Open question: should 'svn status' be able to restrict its output
289 to a single changelist, a la 'svn status --changelist mychange'?
292 C. Destroying changelists
294 Commands can be restricted to operate only on changelist members by
295 specifying the "--changelist NAME" flag. (Perhaps it can be shortened
298 To destroy a changelist, one would need to remove all member-paths
299 from it. There's no good UI for this yet, other than to use 'svn
300 changelist --remove path1 path2 path3 ...'. ### Improve this?
303 D. Viewing edits in a changelist
305 Improve on perforce by allowing 'svn diff' to restrict its output to
306 only members of a certain changelist:
308 $ svn diff --changelist mychange
312 E. Reverting a changelist
314 Allow 'svn revert' to restrict its effect just to members of a
317 $ svn revert --changelist mychange
320 Again, note that this won't destroy the changelist. The changelist
321 would now contain just a set of unmodified paths, and 'svn status'
322 would continue to display them. (This differs from perforce, whereby
323 local-edits are intimately tied to changelist membership.)
326 F. Committing a changelist
328 'svn commit' should be able to commit only changelist members, just as
329 if the paths had been typed on the commandline individually:
331 $ svn commit --changelist mychange
335 Committed revision YYY.
338 After the commit succeeds, the committed files are NO LONGER
339 associated with the changelist, and so the changelist definition
340 ceases to exist. (Note: we probably want to have a switch to
341 'preserve the changelist' after a commit, similar to the way in which
342 the '--no-unlock' switch preserves locks after a commit.)
344 If the user chooses to commit just a single member of a changelist,
345 that member is removed from the changelist after the commit.
348 G. Updating a changelist
350 ### Open question: is this a useful use-case? Perforce doesn't have
351 it, and I've never missed it. I always want to update the entire
352 working copy, not just some small set of files.
355 H. Examining the history of changelist members
357 'svn log' should be able to restrict its history retrieval to only
358 revisions which affected members of the changelist. So running
360 $ svn log --changelist mychange
362 ...should produce output equivalent to
364 $ svn log member1 member2 member3 ...
367 'svn log' already knows not to print log messages more than once
368 (i.e. it prints the union of all revisions).
370 Note that this feature would be an improvement over perforce, which
371 allows multiple targets on the commandline, but no changelist
375 I. Propgets/sets on a changelist
377 'svn proplist', 'svn propget', 'svn propset', 'svn propdel' should all
378 work with the --changelist switch as well, so that a user can quickly
379 perform metadata operations on a whole set of files.
381 ### Open question: should we also allow 'svn lock/unlock' to operate
382 on changelists? It might be just as convenient in certain
389 ### Open UI question:
391 If one's CWD is deep within a working copy, how should
393 $ svn subcommand --changlist mychange
395 ...behave? Should it operate on *all* members of the changelist,
396 or only those members within the CWD (and recursively "below")?
398 --> malcolmr and dlr believe that it's perfectly fine to use only
399 parts of changelists 'below' the target path.
406 * svn changelist [--remove]
408 * svn status shows grouped changelists
409 - 'svn status --changelist' works too
410 * 'svn info' shows changelists
412 * svn commit --changelist
413 * svn revert --changelist
414 * svn log --changelist
415 * svn diff --changelist (wc-wc and wc-repos cases)
416 * svn update --changelist
417 * svn lock/unlock --changelist
418 * svn propget/propset --changelist
419 ### * svn proplist/propdel --changelist
425 * make --cl the same as --changelist, for convenience?
427 * questions about commits:
429 - how does 'svn ci --changelist' interact with nonrecursive commits?
430 - how does it interact with a list of specific targets?
431 - how does it deal with a schedule-delete folder?
434 ----------------------------
436 Commandline UI use-cases:
439 1. add path(s) to a CL:
441 svn cl CLNAME foo.c bar.c baz.c
443 2. remove path(s) from whatever CLs they each belong to.
445 svn cl --remove foo.c bar.c baz.c
447 3. move path(s) from CL1 to CL2.
451 4. undefine a CL all at once (by removing all members)
453 svn cl --remove --changelist CLNAME
457 svn cl NEWNAME --changelist OLDNAME
460 ==================================================================
462 Feature Revamp: sussman and cmpilato.
465 Goal: changelists should be treated as 'filters' everywhere, not as a
466 way to just add targets to a commandline.
469 The basic syntax of commands will be:
471 svn subcommand target1 target2 ... targetN \
472 --changelist foo1 --changelist foo2 ... --changelist fooM
474 The CLI parses the targets as usual: possibly inserting an implicit
475 '.' target, canonicalizing the list, etc.
477 The CLI now passes a list of changelist-names down into each
478 svn_client_subcommand() routine as a "bunch of filters" to apply while
479 working. If svn_client_subcommand() decides to process a target --
480 either one it got explicitly, or one it discovered through recursion
481 -- it first checks that the target is a member of one of the
482 changelists. If not, it skips the target and keeps going.
484 (This is the way 'svn commit' currently works: harvest_committables()
485 only harvests things that are committable *and* a member of the
486 passed-in changelist.)
488 This means that the UI use-cases listed above change slightly:
491 4. undefine a CL all at once (by removing all members)
493 svn cl TARGET --remove --changelist CLNAME
495 (TARGET might be implicit '.' or not, and depth is empty by
496 default; use --depth to override.)
500 svn cl NEWNAME TARGET --changelist OLDNAME
502 (TARGET might be implicit '.' or not, and depth is empty by
503 default; use --depth to override.)
508 [X] allow multiple --changelist args
509 [X] svn status should display grouped changelists
510 [X] 'svn info' should display a target's changelist field
511 [X] rename --keep-changelist option to --keep-changelists
512 [X] fix --changelist and allow multiple changelists in subcommands:
514 No-problem subcommands:
515 [X] svn changelist --changelist
516 [X] svn commit --changelist
517 [X] svn diff --changelist (only wc-wc and wc-repos cases)
518 [X] svn info --changelist
519 [X] svn propget --changelist
520 [X] svn proplist --changelist
521 [X] svn propset --changelist
522 [X] svn propdel --changelist
523 [X] svn revert --changelist
524 [X] svn status --changelist
526 Problem subcommands (see below):
527 [X] svn update --changelist
528 [X] svn lock --changelist ### removed changelist support
529 [X] svn log --changelist ### removed changelist support
530 [X] svn unlock --changelist ### removed changelist support
532 [ ] ensure that the bindings implementations of these APIs are up to snuff
537 Using a definition of --changelist as a filter means that
538 subcommands which are, by default, non-recursive in nature, have a
539 somewhat odd interface. For example, 'svn info --changelist FOO'
540 (which ultimately translates to 'svn info . --depth empty
541 --changelist FOO') will either return exactly one info result, or
542 exactly none, depending on whether or not the current working
543 directory is in changelist FOO. This is trivially worked around by
544 deepening the invocation: 'svn info -R --changelist FOO'. But what
545 about subcommands for which there is no --depth support, such as
546 'lock', 'log', 'unlock'? Do we lose the changelist support, or grow
547 some sort of depth-crawling ability for these things? [RESOLUTION:
548 We've removed changelist support from 'lock', 'unlock', and 'log'.]
550 'svn update' presents an interesting challenge, too. The public
551 svn_client_update3() API takes a list of paths, and returns a list
552 of revision numbers to which those paths were updated. Each path is
553 treated as, effectively, a separate update -- complete with output
554 line that notes the updated-to revision. So, if we do changelist
555 expansion outside the API, we might turn a single-target operation
556 into a multi-target one, and the user sees N full updates processes
557 happen. If we push 'changelists' down into the API, we can fake a
558 single update with notification tricks. But that starts to get
559 nasty when we look at non-file changelist support later and the
560 interactions with externals and such. And if we push 'changelists'
561 all the way down into the update editor, then we've got a mess of a
562 whole 'nuther type, downloading tons of server data we won't use,
563 and so on. [RESOLUTION: Let the command-line client do the
564 changelist path expansion.]