##// END OF EJS Templates
Remove trailing spaces
Thomas Arendsen Hein -
r5081:ea7b982b default
parent child Browse files
Show More
@@ -1,502 +1,502 b''
1 1 # bash completion for the Mercurial distributed SCM
2 2
3 3 # Docs:
4 4 #
5 5 # If you source this file from your .bashrc, bash should be able to
6 6 # complete a command line that uses hg with all the available commands
7 7 # and options and sometimes even arguments.
8 8 #
9 9 # Mercurial allows you to define additional commands through extensions.
10 10 # Bash should be able to automatically figure out the name of these new
11 11 # commands and their options. See below for how to define _hg_opt_foo
12 12 # and _hg_cmd_foo functions to fine-tune the completion for option and
13 13 # non-option arguments, respectively.
14 14 #
15 15 #
16 16 # Notes about completion for specific commands:
17 17 #
18 18 # - the completion function for the email command from the patchbomb
19 19 # extension will try to call _hg_emails to get a list of e-mail
20 20 # addresses. It's up to the user to define this function. For
21 21 # example, put the addresses of the lists that you usually patchbomb
22 22 # in ~/.patchbomb-to and the addresses that you usually use to send
23 23 # the patchbombs in ~/.patchbomb-from and use something like this:
24 24 #
25 25 # _hg_emails()
26 26 # {
27 27 # if [ -r ~/.patchbomb-$1 ]; then
28 28 # cat ~/.patchbomb-$1
29 29 # fi
30 30 # }
31 #
31 #
32 32 #
33 33 # Writing completion functions for additional commands:
34 34 #
35 35 # If it exists, the function _hg_cmd_foo will be called without
36 36 # arguments to generate the completion candidates for the hg command
37 37 # "foo". If the command receives some arguments that aren't options
38 38 # even though they start with a "-", you can define a function called
39 39 # _hg_opt_foo to generate the completion candidates. If _hg_opt_foo
40 40 # doesn't return 0, regular completion for options is attempted.
41 41 #
42 42 # In addition to the regular completion variables provided by bash,
43 43 # the following variables are also set:
44 44 # - $hg - the hg program being used (e.g. /usr/bin/hg)
45 45 # - $cmd - the name of the hg command being completed
46 46 # - $cmd_index - the index of $cmd in $COMP_WORDS
47 47 # - $cur - the current argument being completed
48 48 # - $prev - the argument before $cur
49 49 # - $global_args - "|"-separated list of global options that accept
50 50 # an argument (e.g. '--cwd|-R|--repository')
51 51 # - $canonical - 1 if we canonicalized $cmd before calling the function
52 52 # 0 otherwise
53 #
53 #
54 54
55 55 shopt -s extglob
56 56
57 57 _hg_commands()
58 58 {
59 59 local commands
60 60 commands="$("$hg" debugcomplete "$cur" 2>/dev/null)" || commands=""
61 61 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur"))
62 62 }
63 63
64 64 _hg_paths()
65 65 {
66 66 local paths="$("$hg" paths 2>/dev/null | sed -e 's/ = .*$//')"
67 67 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
68 68 }
69 69
70 70 _hg_repos()
71 71 {
72 72 local i
73 73 for i in $(compgen -d -- "$cur"); do
74 74 test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
75 75 done
76 76 }
77 77
78 78 _hg_status()
79 79 {
80 80 local files="$("$hg" status -n$1 . 2>/dev/null)"
81 81 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
82 82 }
83 83
84 84 _hg_tags()
85 85 {
86 86 local tags="$("$hg" tags -q 2>/dev/null)"
87 87 local IFS=$'\n'
88 88 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur"))
89 89 }
90 90
91 91 # this is "kind of" ugly...
92 92 _hg_count_non_option()
93 93 {
94 94 local i count=0
95 95 local filters="$1"
96 96
97 97 for ((i=1; $i<=$COMP_CWORD; i++)); do
98 98 if [[ "${COMP_WORDS[i]}" != -* ]]; then
99 99 if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
100 100 continue
101 101 fi
102 102 count=$(($count + 1))
103 103 fi
104 104 done
105 105
106 106 echo $(($count - 1))
107 107 }
108 108
109 109 _hg()
110 110 {
111 111 local cur prev cmd cmd_index opts i
112 112 # global options that receive an argument
113 113 local global_args='--cwd|-R|--repository'
114 114 local hg="$1"
115 115 local canonical=0
116 116
117 117 COMPREPLY=()
118 118 cur="$2"
119 119 prev="$3"
120 120
121 121 # searching for the command
122 122 # (first non-option argument that doesn't follow a global option that
123 123 # receives an argument)
124 124 for ((i=1; $i<=$COMP_CWORD; i++)); do
125 125 if [[ ${COMP_WORDS[i]} != -* ]]; then
126 126 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
127 127 cmd="${COMP_WORDS[i]}"
128 128 cmd_index=$i
129 129 break
130 130 fi
131 131 fi
132 132 done
133 133
134 134 if [[ "$cur" == -* ]]; then
135 135 if [ "$(type -t "_hg_opt_$cmd")" = function ] && "_hg_opt_$cmd"; then
136 136 return
137 137 fi
138 138
139 139 opts=$("$hg" debugcomplete --options "$cmd" 2>/dev/null)
140 140
141 141 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
142 142 return
143 143 fi
144 144
145 145 # global options
146 146 case "$prev" in
147 147 -R|--repository)
148 148 _hg_paths
149 149 _hg_repos
150 150 return
151 151 ;;
152 152 --cwd)
153 153 # Stick with default bash completion
154 154 return
155 155 ;;
156 156 esac
157 157
158 158 if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then
159 159 _hg_commands
160 160 return
161 161 fi
162 162
163 163 # try to generate completion candidates for whatever command the user typed
164 164 local help
165 165 if _hg_command_specific; then
166 166 return
167 167 fi
168 168
169 169 # canonicalize the command name and try again
170 170 help=$("$hg" help "$cmd" 2>/dev/null)
171 171 if [ $? -ne 0 ]; then
172 172 # Probably either the command doesn't exist or it's ambiguous
173 173 return
174 174 fi
175 175 cmd=${help#hg }
176 176 cmd=${cmd%%[$' \n']*}
177 177 canonical=1
178 178 _hg_command_specific
179 179 }
180 180
181 181 _hg_command_specific()
182 182 {
183 183 if [ "$(type -t "_hg_cmd_$cmd")" = function ]; then
184 184 "_hg_cmd_$cmd"
185 185 return 0
186 186 fi
187 187
188 188 if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" == --rev ]; then
189 189 if [ $canonical = 1 ]; then
190 190 _hg_tags
191 191 return 0
192 192 elif [[ status != "$cmd"* ]]; then
193 193 _hg_tags
194 194 return 0
195 195 else
196 196 return 1
197 197 fi
198 198 fi
199 199
200 200 case "$cmd" in
201 201 help)
202 202 _hg_commands
203 203 ;;
204 204 export)
205 205 if _hg_ext_mq_patchlist qapplied && [ "${COMPREPLY[*]}" ]; then
206 206 return 0
207 207 fi
208 208 _hg_tags
209 209 ;;
210 210 manifest|update)
211 211 _hg_tags
212 212 ;;
213 213 pull|push|outgoing|incoming)
214 214 _hg_paths
215 215 _hg_repos
216 216 ;;
217 217 paths)
218 218 _hg_paths
219 219 ;;
220 220 add)
221 221 _hg_status "u"
222 222 ;;
223 223 commit)
224 224 _hg_status "mar"
225 225 ;;
226 226 remove)
227 227 _hg_status "d"
228 228 ;;
229 229 forget)
230 230 _hg_status "a"
231 231 ;;
232 232 diff)
233 233 _hg_status "mar"
234 234 ;;
235 235 revert)
236 236 _hg_status "mard"
237 237 ;;
238 238 clone)
239 239 local count=$(_hg_count_non_option)
240 240 if [ $count = 1 ]; then
241 241 _hg_paths
242 242 fi
243 243 _hg_repos
244 244 ;;
245 245 debugindex|debugindexdot)
246 246 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
247 247 ;;
248 248 debugdata)
249 249 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
250 250 ;;
251 251 *)
252 252 return 1
253 253 ;;
254 254 esac
255 255
256 256 return 0
257 257 }
258 258
259 259 complete -o bashdefault -o default -F _hg hg 2>/dev/null \
260 260 || complete -o default -F _hg hg
261 261
262 262
263 263 # Completion for commands provided by extensions
264 264
265 265 # mq
266 266 _hg_ext_mq_patchlist()
267 267 {
268 268 local patches
269 269 patches=$("$hg" $1 2>/dev/null)
270 270 if [ $? -eq 0 ] && [ "$patches" ]; then
271 271 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$patches' -- "$cur"))
272 272 return 0
273 273 fi
274 274 return 1
275 275 }
276 276
277 277 _hg_ext_mq_queues()
278 278 {
279 279 local root=$("$hg" root 2>/dev/null)
280 280 local n
281 281 for n in $(cd "$root"/.hg && compgen -d -- "$cur"); do
282 282 # I think we're usually not interested in the regular "patches" queue
283 283 # so just filter it.
284 284 if [ "$n" != patches ] && [ -e "$root/.hg/$n/series" ]; then
285 285 COMPREPLY=(${COMPREPLY[@]:-} "$n")
286 286 fi
287 287 done
288 288 }
289 289
290 290 _hg_cmd_qpop()
291 291 {
292 292 if [[ "$prev" = @(-n|--name) ]]; then
293 293 _hg_ext_mq_queues
294 294 return
295 295 fi
296 296 _hg_ext_mq_patchlist qapplied
297 297 }
298 298
299 299 _hg_cmd_qpush()
300 300 {
301 301 if [[ "$prev" = @(-n|--name) ]]; then
302 302 _hg_ext_mq_queues
303 303 return
304 304 fi
305 305 _hg_ext_mq_patchlist qunapplied
306 306 }
307 307
308 308 _hg_cmd_qdelete()
309 309 {
310 310 local qcmd=qunapplied
311 311 if [[ "$prev" = @(-r|--rev) ]]; then
312 312 qcmd=qapplied
313 313 fi
314 314 _hg_ext_mq_patchlist $qcmd
315 315 }
316 316
317 317 _hg_cmd_qsave()
318 318 {
319 319 if [[ "$prev" = @(-n|--name) ]]; then
320 320 _hg_ext_mq_queues
321 321 return
322 322 fi
323 323 }
324 324
325 325 _hg_cmd_strip()
326 326 {
327 327 _hg_tags
328 328 }
329 329
330 330 _hg_cmd_qcommit()
331 331 {
332 332 local root=$("$hg" root 2>/dev/null)
333 333 # this is run in a sub-shell, so we can't use _hg_status
334 334 local files=$(cd "$root/.hg/patches" 2>/dev/null &&
335 335 "$hg" status -nmar 2>/dev/null)
336 336 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
337 337 }
338 338
339 339 _hg_cmd_qfold()
340 340 {
341 341 _hg_ext_mq_patchlist qunapplied
342 342 }
343 343
344 344 _hg_cmd_qrename()
345 345 {
346 346 _hg_ext_mq_patchlist qseries
347 347 }
348 348
349 349 _hg_cmd_qheader()
350 350 {
351 351 _hg_ext_mq_patchlist qseries
352 352 }
353 353
354 354 _hg_cmd_qclone()
355 355 {
356 356 local count=$(_hg_count_non_option)
357 357 if [ $count = 1 ]; then
358 358 _hg_paths
359 359 fi
360 360 _hg_repos
361 361 }
362 362
363 363 _hg_ext_mq_guards()
364 364 {
365 365 "$hg" qselect --series 2>/dev/null | sed -e 's/^.//'
366 366 }
367 367
368 368 _hg_cmd_qselect()
369 369 {
370 370 local guards=$(_hg_ext_mq_guards)
371 371 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$guards' -- "$cur"))
372 372 }
373 373
374 374 _hg_cmd_qguard()
375 375 {
376 376 local prefix=''
377 377
378 378 if [[ "$cur" == +* ]]; then
379 379 prefix=+
380 380 elif [[ "$cur" == -* ]]; then
381 381 prefix=-
382 382 fi
383 383 local ncur=${cur#[-+]}
384 384
385 385 if ! [ "$prefix" ]; then
386 386 _hg_ext_mq_patchlist qseries
387 387 return
388 388 fi
389 389
390 390 local guards=$(_hg_ext_mq_guards)
391 391 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -P $prefix -W '$guards' -- "$ncur"))
392 392 }
393 393
394 394 _hg_opt_qguard()
395 395 {
396 396 local i
397 397 for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
398 398 if [[ ${COMP_WORDS[i]} != -* ]]; then
399 399 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
400 400 _hg_cmd_qguard
401 401 return 0
402 402 fi
403 403 elif [ "${COMP_WORDS[i]}" = -- ]; then
404 404 _hg_cmd_qguard
405 405 return 0
406 406 fi
407 407 done
408 408 return 1
409 409 }
410 410
411 411
412 412 # hbisect
413 413 _hg_cmd_bisect()
414 414 {
415 415 local i subcmd
416 416
417 417 # find the sub-command
418 418 for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
419 419 if [[ ${COMP_WORDS[i]} != -* ]]; then
420 420 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
421 421 subcmd="${COMP_WORDS[i]}"
422 422 break
423 423 fi
424 424 fi
425 425 done
426 426
427 427 if [ -z "$subcmd" ] || [ $COMP_CWORD -eq $i ] || [ "$subcmd" = help ]; then
428 COMPREPLY=(${COMPREPLY[@]:-}
428 COMPREPLY=(${COMPREPLY[@]:-}
429 429 $(compgen -W 'bad good help init next reset' -- "$cur"))
430 430 return
431 431 fi
432 432
433 433 case "$subcmd" in
434 434 good|bad)
435 435 _hg_tags
436 436 ;;
437 437 esac
438 438
439 439 return
440 440 }
441 441
442 442
443 443 # patchbomb
444 444 _hg_cmd_email()
445 445 {
446 446 case "$prev" in
447 447 -c|--cc|-t|--to|-f|--from|--bcc)
448 # we need an e-mail address. let the user provide a function
448 # we need an e-mail address. let the user provide a function
449 449 # to get them
450 450 if [ "$(type -t _hg_emails)" = function ]; then
451 451 local arg=to
452 452 if [[ "$prev" == @(-f|--from) ]]; then
453 453 arg=from
454 454 fi
455 455 local addresses=$(_hg_emails $arg)
456 456 COMPREPLY=(${COMPREPLY[@]:-}
457 457 $(compgen -W '$addresses' -- "$cur"))
458 458 fi
459 459 return
460 460 ;;
461 461 -m|--mbox)
462 462 # fallback to standard filename completion
463 463 return
464 464 ;;
465 465 -s|--subject)
466 466 # free form string
467 467 return
468 468 ;;
469 469 esac
470 470
471 471 _hg_tags
472 472 return
473 473 }
474 474
475 475
476 476 # gpg
477 477 _hg_cmd_sign()
478 478 {
479 479 _hg_tags
480 480 }
481 481
482 482
483 483 # transplant
484 484 _hg_cmd_transplant()
485 485 {
486 486 case "$prev" in
487 487 -s|--source)
488 488 _hg_paths
489 489 _hg_repos
490 490 return
491 491 ;;
492 492 --filter)
493 493 # standard filename completion
494 494 return
495 495 ;;
496 496 esac
497 497
498 498 # all other transplant options values and command parameters are revisions
499 499 _hg_tags
500 500 return
501 501 }
502 502
@@ -1,105 +1,105 b''
1 1 #!/usr/bin/env python
2 2
3 3 import os, sys, struct, stat
4 4 import difflib
5 5 import re
6 6 from optparse import OptionParser
7 7 from mercurial.bdiff import bdiff, blocks
8 8 from mercurial.mdiff import bunidiff, diffopts
9 9
10 10 VERSION="0.3"
11 11 usage = "usage: %prog [options] file1 file2"
12 12 parser = OptionParser(usage=usage)
13 13
14 14 parser.add_option("-d", "--difflib", action="store_true", default=False)
15 15 parser.add_option('-x', '--count', default=1)
16 16 parser.add_option('-c', '--context', type="int", default=3)
17 17 parser.add_option('-p', '--show-c-function', action="store_true", default=False)
18 parser.add_option('-w', '--ignore-all-space', action="store_true",
18 parser.add_option('-w', '--ignore-all-space', action="store_true",
19 19 default=False)
20 20
21 21 (options, args) = parser.parse_args()
22 22
23 23 if not args:
24 24 parser.print_help()
25 25 sys.exit(1)
26 26
27 27 # simple utility function to put all the
28 28 # files from a directory tree into a dict
29 29 def buildlist(names, top):
30 30 tlen = len(top)
31 31 for root, dirs, files in os.walk(top):
32 32 l = root[tlen + 1:]
33 33 for x in files:
34 34 p = os.path.join(root, x)
35 35 st = os.lstat(p)
36 36 if stat.S_ISREG(st.st_mode):
37 37 names[os.path.join(l, x)] = (st.st_dev, st.st_ino)
38 38
39 39 def diff_files(file1, file2):
40 40 if file1 == None:
41 41 b = file(file2).read().splitlines(1)
42 42 l1 = "--- %s\n" % (file2)
43 43 l2 = "+++ %s\n" % (file2)
44 44 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
45 45 l = [l1, l2, l3] + ["+" + e for e in b]
46 46 elif file2 == None:
47 47 a = file(file1).read().splitlines(1)
48 48 l1 = "--- %s\n" % (file1)
49 49 l2 = "+++ %s\n" % (file1)
50 50 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
51 51 l = [l1, l2, l3] + ["-" + e for e in a]
52 52 else:
53 53 t1 = file(file1).read()
54 54 t2 = file(file2).read()
55 55 l1 = t1.splitlines(1)
56 56 l2 = t2.splitlines(1)
57 57 if options.difflib:
58 58 l = difflib.unified_diff(l1, l2, file1, file2)
59 59 else:
60 60 l = bunidiff(t1, t2, l1, l2, file1, file2,
61 61 diffopts(context=options.context,
62 62 showfunc=options.show_c_function,
63 63 ignorews=options.ignore_all_space))
64 64 for x in l:
65 65 if x[-1] != '\n':
66 66 x += "\n\ No newline at end of file\n"
67 67 print x,
68 68
69 69 file1 = args[0]
70 70 file2 = args[1]
71 71
72 72 if os.path.isfile(file1) and os.path.isfile(file2):
73 73 diff_files(file1, file2)
74 74 elif os.path.isdir(file1):
75 75 if not os.path.isdir(file2):
76 76 sys.stderr.write("file types don't match\n")
77 77 sys.exit(1)
78 78
79 79 d1 = {}
80 80 d2 = {}
81 81
82 82 buildlist(d1, file1)
83 83 buildlist(d2, file2)
84 84 keys = d1.keys()
85 85 keys.sort()
86 86 for x in keys:
87 87 if x not in d2:
88 88 f2 = None
89 89 else:
90 90 f2 = os.path.join(file2, x)
91 91 st1 = d1[x]
92 92 st2 = d2[x]
93 93 del d2[x]
94 94 if st1[0] == st2[0] and st1[1] == st2[1]:
95 95 sys.stderr.write("%s is a hard link\n" % x)
96 96 continue
97 97 x = os.path.join(file1, x)
98 98 diff_files(x, f2)
99 99 keys = d2.keys()
100 100 keys.sort()
101 101 for x in keys:
102 102 f1 = None
103 103 x = os.path.join(file2, x)
104 104 diff_files(f1, x)
105 105
@@ -1,439 +1,439 b''
1 1 /*
2 2 * hgsh.c - restricted login shell for mercurial
3 3 *
4 4 * Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 *
6 6 * This software may be used and distributed according to the terms of the
7 7 * GNU General Public License, incorporated herein by reference.
8 8 *
9 9 * this program is login shell for dedicated mercurial user account. it
10 10 * only allows few actions:
11 11 *
12 12 * 1. run hg in server mode on specific repository. no other hg commands
13 13 * are allowed. we try to verify that repo to be accessed exists under
14 14 * given top-level directory.
15 15 *
16 16 * 2. (optional) forward ssh connection from firewall/gateway machine to
17 17 * "real" mercurial host, to let users outside intranet pull and push
18 18 * changes through firewall.
19 19 *
20 20 * 3. (optional) run normal shell, to allow to "su" to mercurial user, use
21 21 * "sudo" to run programs as that user, or run cron jobs as that user.
22 22 *
23 23 * only tested on linux yet. patches for non-linux systems welcome.
24 24 */
25 25
26 26 #ifndef _GNU_SOURCE
27 27 #define _GNU_SOURCE /* for asprintf */
28 28 #endif
29 29
30 30 #include <stdio.h>
31 31 #include <stdlib.h>
32 32 #include <string.h>
33 33 #include <sys/stat.h>
34 34 #include <sys/types.h>
35 35 #include <sysexits.h>
36 36 #include <unistd.h>
37 37
38 38 /*
39 39 * user config.
40 40 *
41 41 * if you see a hostname below, just use first part of hostname. example,
42 42 * if you have host named foo.bar.com, use "foo".
43 43 */
44 44
45 45 /*
46 46 * HG_GATEWAY: hostname of gateway/firewall machine that people outside your
47 47 * intranet ssh into if they need to ssh to other machines. if you do not
48 48 * have such machine, set to NULL.
49 49 */
50 50 #ifndef HG_GATEWAY
51 51 #define HG_GATEWAY "gateway"
52 52 #endif
53 53
54 54 /*
55 55 * HG_HOST: hostname of mercurial server. if any machine is allowed, set to
56 56 * NULL.
57 57 */
58 58 #ifndef HG_HOST
59 59 #define HG_HOST "mercurial"
60 60 #endif
61 61
62 62 /*
63 63 * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and
64 64 * host username are same, set to NULL.
65 65 */
66 66 #ifndef HG_USER
67 67 #define HG_USER "hg"
68 68 #endif
69 69
70 70 /*
71 71 * HG_ROOT: root of tree full of mercurial repos. if you do not want to
72 72 * validate location of repo when someone is try to access, set to NULL.
73 73 */
74 74 #ifndef HG_ROOT
75 75 #define HG_ROOT "/home/hg/repos"
76 76 #endif
77 77
78 78 /*
79 79 * HG: path to the mercurial executable to run.
80 80 */
81 81 #ifndef HG
82 82 #define HG "/home/hg/bin/hg"
83 83 #endif
84 84
85 85 /*
86 86 * HG_SHELL: shell to use for actions like "sudo" and "su" access to
87 87 * mercurial user, and cron jobs. if you want to make these things
88 88 * impossible, set to NULL.
89 89 */
90 90 #ifndef HG_SHELL
91 91 #define HG_SHELL NULL
92 92 // #define HG_SHELL "/bin/bash"
93 93 #endif
94 94
95 95 /*
96 96 * HG_HELP: some way for users to get support if they have problem. if they
97 97 * should not get helpful message, set to NULL.
98 98 */
99 99 #ifndef HG_HELP
100 100 #define HG_HELP "please contact support@example.com for help."
101 101 #endif
102 102
103 103 /*
104 104 * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to
105 105 * HG_HOST. if you want to use rsh instead (why?), you need to modify
106 106 * arguments it is called with. see forward_through_gateway.
107 107 */
108 108 #ifndef SSH
109 109 #define SSH "/usr/bin/ssh"
110 110 #endif
111 111
112 112 /*
113 113 * tell whether to print command that is to be executed. useful for
114 114 * debugging. should not interfere with mercurial operation, since
115 115 * mercurial only cares about stdin and stdout, and this prints to stderr.
116 116 */
117 117 static const int debug = 0;
118 118
119 119 static void print_cmdline(int argc, char **argv)
120 120 {
121 121 FILE *fp = stderr;
122 122 int i;
123 123
124 124 fputs("command: ", fp);
125 125
126 126 for (i = 0; i < argc; i++) {
127 127 char *spc = strpbrk(argv[i], " \t\r\n");
128 128 if (spc) {
129 129 fputc('\'', fp);
130 130 }
131 131 fputs(argv[i], fp);
132 132 if (spc) {
133 133 fputc('\'', fp);
134 134 }
135 135 if (i < argc - 1) {
136 136 fputc(' ', fp);
137 137 }
138 138 }
139 139 fputc('\n', fp);
140 140 fflush(fp);
141 141 }
142 142
143 143 static void usage(const char *reason, int exitcode)
144 144 {
145 145 char *hg_help = HG_HELP;
146 146
147 147 if (reason) {
148 148 fprintf(stderr, "*** Error: %s.\n", reason);
149 149 }
150 150 fprintf(stderr, "*** This program has been invoked incorrectly.\n");
151 151 if (hg_help) {
152 152 fprintf(stderr, "*** %s\n", hg_help);
153 153 }
154 154 exit(exitcode ? exitcode : EX_USAGE);
155 155 }
156 156
157 157 /*
158 158 * run on gateway host to make another ssh connection, to "real" mercurial
159 159 * server. it sends its command line unmodified to far end.
160 160 *
161 161 * never called if HG_GATEWAY is NULL.
162 162 */
163 163 static void forward_through_gateway(int argc, char **argv)
164 164 {
165 165 char *ssh = SSH;
166 166 char *hg_host = HG_HOST;
167 167 char *hg_user = HG_USER;
168 168 char **nargv = alloca((10 + argc) * sizeof(char *));
169 169 int i = 0, j;
170 170
171 171 nargv[i++] = ssh;
172 172 nargv[i++] = "-q";
173 173 nargv[i++] = "-T";
174 174 nargv[i++] = "-x";
175 175 if (hg_user) {
176 176 nargv[i++] = "-l";
177 177 nargv[i++] = hg_user;
178 178 }
179 179 nargv[i++] = hg_host;
180 180
181 181 /*
182 182 * sshd called us with added "-c", because it thinks we are a shell.
183 183 * drop it if we find it.
184 184 */
185 185 j = 1;
186 186 if (j < argc && strcmp(argv[j], "-c") == 0) {
187 187 j++;
188 188 }
189 189
190 190 for (; j < argc; i++, j++) {
191 191 nargv[i] = argv[j];
192 192 }
193 193 nargv[i] = NULL;
194 194
195 195 if (debug) {
196 196 print_cmdline(i, nargv);
197 197 }
198 198
199 199 execv(ssh, nargv);
200 200 perror(ssh);
201 201 exit(EX_UNAVAILABLE);
202 202 }
203 203
204 204 /*
205 205 * run shell. let administrator "su" to mercurial user's account to do
206 206 * administrative works.
207 207 *
208 208 * never called if HG_SHELL is NULL.
209 209 */
210 210 static void run_shell(int argc, char **argv)
211 211 {
212 212 char *hg_shell = HG_SHELL;
213 213 char **nargv;
214 214 char *c;
215 215 int i;
216 216
217 217 nargv = alloca((argc + 3) * sizeof(char *));
218 218 c = strrchr(hg_shell, '/');
219 219
220 220 /* tell "real" shell it is login shell, if needed. */
221 221
222 222 if (argv[0][0] == '-' && c) {
223 223 nargv[0] = strdup(c);
224 224 if (nargv[0] == NULL) {
225 225 perror("malloc");
226 226 exit(EX_OSERR);
227 227 }
228 228 nargv[0][0] = '-';
229 229 } else {
230 230 nargv[0] = hg_shell;
231 231 }
232 232
233 233 for (i = 1; i < argc; i++) {
234 234 nargv[i] = argv[i];
235 235 }
236 236 nargv[i] = NULL;
237 237
238 238 if (debug) {
239 239 print_cmdline(i, nargv);
240 240 }
241 241
242 242 execv(hg_shell, nargv);
243 243 perror(hg_shell);
244 244 exit(EX_OSFILE);
245 245 }
246 246
247 247 enum cmdline {
248 248 hg_init,
249 249 hg_serve,
250 250 };
251 251
252
252
253 253 /*
254 254 * attempt to verify that a directory is really a hg repo, by testing
255 255 * for the existence of a subdirectory.
256 256 */
257 257 static int validate_repo(const char *repo_root, const char *subdir)
258 258 {
259 259 char *abs_path;
260 260 struct stat st;
261 261 int ret;
262 262
263 263 if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
264 264 ret = -1;
265 265 goto bail;
266 266 }
267 267
268 268 /* verify that we really are looking at valid repo. */
269 269
270 270 if (stat(abs_path, &st) == -1) {
271 271 ret = 0;
272 272 } else {
273 273 ret = 1;
274 274 }
275 275
276 276 bail:
277 277 return ret;
278 278 }
279 279
280 280 /*
281 281 * paranoid wrapper, runs hg executable in server mode.
282 282 */
283 283 static void serve_data(int argc, char **argv)
284 284 {
285 285 char *hg_root = HG_ROOT;
286 286 char *repo, *repo_root;
287 287 enum cmdline cmd;
288 288 char *nargv[6];
289 289 size_t repolen;
290 290 int i;
291 291
292 292 /*
293 293 * check argv for looking okay. we should be invoked with argv
294 294 * resembling like this:
295 295 *
296 296 * hgsh
297 297 * -c
298 298 * hg -R some/path serve --stdio
299 299 *
300 300 * the "-c" is added by sshd, because it thinks we are login shell.
301 301 */
302 302
303 303 if (argc != 3) {
304 304 goto badargs;
305 305 }
306 306
307 307 if (strcmp(argv[1], "-c") != 0) {
308 308 goto badargs;
309 309 }
310 310
311 311 if (sscanf(argv[2], "hg init %as", &repo) == 1) {
312 312 cmd = hg_init;
313 313 }
314 314 else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) {
315 315 cmd = hg_serve;
316 316 } else {
317 317 goto badargs;
318 318 }
319 319
320 320 repolen = repo ? strlen(repo) : 0;
321 321
322 322 if (repolen == 0) {
323 323 goto badargs;
324 324 }
325 325
326 326 if (hg_root) {
327 327 if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) {
328 328 goto badargs;
329 329 }
330 330
331 331 /*
332 332 * attempt to stop break out from inside the repository tree. could
333 333 * do something more clever here, because e.g. we could traverse a
334 334 * symlink that looks safe, but really breaks us out of tree.
335 335 */
336 336
337 337 if (strstr(repo_root, "/../") != NULL) {
338 338 goto badargs;
339 339 }
340 340
341 341 /* only hg init expects no repo. */
342 342
343 343 if (cmd != hg_init) {
344 344 int valid;
345
345
346 346 valid = validate_repo(repo_root, "data");
347 347
348 348 if (valid == -1) {
349 349 goto badargs;
350 350 }
351
351
352 352 if (valid == 0) {
353 353 valid = validate_repo(repo_root, "store");
354 354
355 355 if (valid == -1) {
356 356 goto badargs;
357 357 }
358 358 }
359
359
360 360 if (valid == 0) {
361 361 perror(repo);
362 362 exit(EX_DATAERR);
363 363 }
364 364 }
365 365
366 366 if (chdir(hg_root) == -1) {
367 367 perror(hg_root);
368 368 exit(EX_SOFTWARE);
369 369 }
370 370 }
371 371
372 372 i = 0;
373 373
374 374 switch (cmd) {
375 375 case hg_serve:
376 376 nargv[i++] = HG;
377 377 nargv[i++] = "-R";
378 378 nargv[i++] = repo;
379 379 nargv[i++] = "serve";
380 380 nargv[i++] = "--stdio";
381 381 break;
382 382 case hg_init:
383 383 nargv[i++] = HG;
384 384 nargv[i++] = "init";
385 385 nargv[i++] = repo;
386 386 break;
387 387 }
388
388
389 389 nargv[i] = NULL;
390 390
391 391 if (debug) {
392 392 print_cmdline(i, nargv);
393 393 }
394 394
395 395 execv(HG, nargv);
396 396 perror(HG);
397 397 exit(EX_UNAVAILABLE);
398 398
399 399 badargs:
400 400 /* print useless error message. */
401 401
402 402 usage("invalid arguments", EX_DATAERR);
403 403 }
404 404
405 405 int main(int argc, char **argv)
406 406 {
407 407 char host[1024];
408 408 char *c;
409 409
410 410 if (gethostname(host, sizeof(host)) == -1) {
411 411 perror("gethostname");
412 412 exit(EX_OSERR);
413 413 }
414 414
415 415 if ((c = strchr(host, '.')) != NULL) {
416 416 *c = '\0';
417 417 }
418 418
419 419 if (getenv("SSH_CLIENT")) {
420 420 char *hg_gateway = HG_GATEWAY;
421 421 char *hg_host = HG_HOST;
422 422
423 423 if (hg_gateway && strcmp(host, hg_gateway) == 0) {
424 424 forward_through_gateway(argc, argv);
425 425 }
426 426
427 427 if (hg_host && strcmp(host, hg_host) != 0) {
428 428 usage("invoked on unexpected host", EX_USAGE);
429 429 }
430 430
431 431 serve_data(argc, argv);
432 432 } else if (HG_SHELL) {
433 433 run_shell(argc, argv);
434 434 } else {
435 435 usage("invalid arguments", EX_DATAERR);
436 436 }
437 437
438 438 return 0;
439 439 }
@@ -1,1274 +1,1274 b''
1 1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
2 2
3 3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
4 4
5 5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6 6
7 7 ;; mercurial.el is free software; you can redistribute it and/or
8 8 ;; modify it under the terms of version 2 of the GNU General Public
9 9 ;; License as published by the Free Software Foundation.
10 10
11 11 ;; mercurial.el is distributed in the hope that it will be useful, but
12 12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 14 ;; General Public License for more details.
15 15
16 16 ;; You should have received a copy of the GNU General Public License
17 17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
18 18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
19 19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 20
21 21 ;;; Commentary:
22 22
23 23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
24 24 ;; integration with the Mercurial distributed SCM tool.
25 25
26 26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
27 27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
28 28 ;; usage overview.
29 29
30 30 ;; Much of the inspiration for mercurial.el comes from Rajesh
31 31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
32 32 ;; job for the commercial Perforce SCM product. In fact, substantial
33 33 ;; chunks of code are adapted from p4.el.
34 34
35 35 ;; This code has been developed under XEmacs 21.5, and may not work as
36 36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
37 37 ;; enhance the portability of this code, fix bugs, and add features
38 38 ;; are most welcome. You can clone a Mercurial repository for this
39 39 ;; package from http://www.serpentine.com/hg/hg-emacs
40 40
41 41 ;; Please send problem reports and suggestions to bos@serpentine.com.
42 42
43 43
44 44 ;;; Code:
45 45
46 46 (eval-when-compile (require 'cl))
47 47 (require 'diff-mode)
48 48 (require 'easymenu)
49 49 (require 'executable)
50 50 (require 'vc)
51 51
52 52 (defmacro hg-feature-cond (&rest clauses)
53 53 "Test CLAUSES for feature at compile time.
54 54 Each clause is (FEATURE BODY...)."
55 55 (dolist (x clauses)
56 56 (let ((feature (car x))
57 57 (body (cdr x)))
58 58 (when (or (eq feature t)
59 59 (featurep feature))
60 60 (return (cons 'progn body))))))
61 61
62 62
63 63 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
64 64
65 65 (hg-feature-cond
66 66 (xemacs (require 'view-less))
67 67 (t (require 'view)))
68 68
69 69
70 70 ;;; Variables accessible through the custom system.
71 71
72 72 (defgroup mercurial nil
73 73 "Mercurial distributed SCM."
74 74 :group 'tools)
75 75
76 76 (defcustom hg-binary
77 77 (or (executable-find "hg")
78 78 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
79 79 (when (file-executable-p path)
80 80 (return path))))
81 81 "The path to Mercurial's hg executable."
82 82 :type '(file :must-match t)
83 83 :group 'mercurial)
84 84
85 85 (defcustom hg-mode-hook nil
86 86 "Hook run when a buffer enters hg-mode."
87 87 :type 'sexp
88 88 :group 'mercurial)
89 89
90 90 (defcustom hg-commit-mode-hook nil
91 91 "Hook run when a buffer is created to prepare a commit."
92 92 :type 'sexp
93 93 :group 'mercurial)
94 94
95 95 (defcustom hg-pre-commit-hook nil
96 96 "Hook run before a commit is performed.
97 97 If you want to prevent the commit from proceeding, raise an error."
98 98 :type 'sexp
99 99 :group 'mercurial)
100 100
101 101 (defcustom hg-log-mode-hook nil
102 102 "Hook run after a buffer is filled with log information."
103 103 :type 'sexp
104 104 :group 'mercurial)
105 105
106 106 (defcustom hg-global-prefix "\C-ch"
107 107 "The global prefix for Mercurial keymap bindings."
108 108 :type 'sexp
109 109 :group 'mercurial)
110 110
111 111 (defcustom hg-commit-allow-empty-message nil
112 112 "Whether to allow changes to be committed with empty descriptions."
113 113 :type 'boolean
114 114 :group 'mercurial)
115 115
116 116 (defcustom hg-commit-allow-empty-file-list nil
117 117 "Whether to allow changes to be committed without any modified files."
118 118 :type 'boolean
119 119 :group 'mercurial)
120 120
121 121 (defcustom hg-rev-completion-limit 100
122 122 "The maximum number of revisions that hg-read-rev will offer to complete.
123 123 This affects memory usage and performance when prompting for revisions
124 124 in a repository with a lot of history."
125 125 :type 'integer
126 126 :group 'mercurial)
127 127
128 128 (defcustom hg-log-limit 50
129 129 "The maximum number of revisions that hg-log will display."
130 130 :type 'integer
131 131 :group 'mercurial)
132 132
133 133 (defcustom hg-update-modeline t
134 134 "Whether to update the modeline with the status of a file after every save.
135 135 Set this to nil on platforms with poor process management, such as Windows."
136 136 :type 'boolean
137 137 :group 'mercurial)
138 138
139 139 (defcustom hg-incoming-repository "default"
140 140 "The repository from which changes are pulled from by default.
141 141 This should be a symbolic repository name, since it is used for all
142 142 repository-related commands."
143 143 :type 'string
144 144 :group 'mercurial)
145 145
146 146 (defcustom hg-outgoing-repository "default-push"
147 147 "The repository to which changes are pushed to by default.
148 148 This should be a symbolic repository name, since it is used for all
149 149 repository-related commands."
150 150 :type 'string
151 151 :group 'mercurial)
152 152
153 153
154 154 ;;; Other variables.
155 155
156 156 (defvar hg-mode nil
157 157 "Is this file managed by Mercurial?")
158 158 (make-variable-buffer-local 'hg-mode)
159 159 (put 'hg-mode 'permanent-local t)
160 160
161 161 (defvar hg-status nil)
162 162 (make-variable-buffer-local 'hg-status)
163 163 (put 'hg-status 'permanent-local t)
164 164
165 165 (defvar hg-prev-buffer nil)
166 166 (make-variable-buffer-local 'hg-prev-buffer)
167 167 (put 'hg-prev-buffer 'permanent-local t)
168 168
169 169 (defvar hg-root nil)
170 170 (make-variable-buffer-local 'hg-root)
171 171 (put 'hg-root 'permanent-local t)
172 172
173 173 (defvar hg-view-mode nil)
174 174 (make-variable-buffer-local 'hg-view-mode)
175 175 (put 'hg-view-mode 'permanent-local t)
176 176
177 177 (defvar hg-view-file-name nil)
178 178 (make-variable-buffer-local 'hg-view-file-name)
179 179 (put 'hg-view-file-name 'permanent-local t)
180 180
181 181 (defvar hg-output-buffer-name "*Hg*"
182 182 "The name to use for Mercurial output buffers.")
183 183
184 184 (defvar hg-file-history nil)
185 185 (defvar hg-repo-history nil)
186 186 (defvar hg-rev-history nil)
187 187 (defvar hg-repo-completion-table nil) ; shut up warnings
188 188
189 189
190 190 ;;; Random constants.
191 191
192 192 (defconst hg-commit-message-start
193 193 "--- Enter your commit message. Type `C-c C-c' to commit. ---\n")
194 194
195 195 (defconst hg-commit-message-end
196 196 "--- Files in bold will be committed. Click to toggle selection. ---\n")
197 197
198 198 (defconst hg-state-alist
199 199 '((?M . modified)
200 200 (?A . added)
201 201 (?R . removed)
202 202 (?! . deleted)
203 203 (?C . normal)
204 204 (?I . ignored)
205 205 (?? . nil)))
206 206
207 207 ;;; hg-mode keymap.
208 208
209 209 (defvar hg-prefix-map
210 210 (let ((map (make-sparse-keymap)))
211 211 (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
212 212 (set-keymap-parent map vc-prefix-map)
213 213 (define-key map "=" 'hg-diff)
214 214 (define-key map "c" 'hg-undo)
215 215 (define-key map "g" 'hg-annotate)
216 216 (define-key map "i" 'hg-add)
217 217 (define-key map "l" 'hg-log)
218 218 (define-key map "n" 'hg-commit-start)
219 219 ;; (define-key map "r" 'hg-update)
220 220 (define-key map "u" 'hg-revert-buffer)
221 221 (define-key map "~" 'hg-version-other-window)
222 222 map)
223 223 "This keymap overrides some default vc-mode bindings.")
224 224
225 225 (defvar hg-mode-map
226 226 (let ((map (make-sparse-keymap)))
227 227 (define-key map "\C-xv" hg-prefix-map)
228 228 map))
229 229
230 230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
231 231
232 232
233 233 ;;; Global keymap.
234 234
235 235 (defvar hg-global-map
236 236 (let ((map (make-sparse-keymap)))
237 237 (define-key map "," 'hg-incoming)
238 238 (define-key map "." 'hg-outgoing)
239 239 (define-key map "<" 'hg-pull)
240 240 (define-key map "=" 'hg-diff-repo)
241 241 (define-key map ">" 'hg-push)
242 242 (define-key map "?" 'hg-help-overview)
243 243 (define-key map "A" 'hg-addremove)
244 244 (define-key map "U" 'hg-revert)
245 245 (define-key map "a" 'hg-add)
246 246 (define-key map "c" 'hg-commit-start)
247 247 (define-key map "f" 'hg-forget)
248 248 (define-key map "h" 'hg-help-overview)
249 249 (define-key map "i" 'hg-init)
250 250 (define-key map "l" 'hg-log-repo)
251 251 (define-key map "r" 'hg-root)
252 252 (define-key map "s" 'hg-status)
253 253 (define-key map "u" 'hg-update)
254 254 map))
255 255
256 256 (global-set-key hg-global-prefix hg-global-map)
257 257
258 258 ;;; View mode keymap.
259 259
260 260 (defvar hg-view-mode-map
261 261 (let ((map (make-sparse-keymap)))
262 262 (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
263 263 (define-key map (hg-feature-cond (xemacs [button2])
264 264 (t [mouse-2]))
265 265 'hg-buffer-mouse-clicked)
266 266 map))
267 267
268 268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
269 269
270 270
271 271 ;;; Commit mode keymaps.
272 272
273 273 (defvar hg-commit-mode-map
274 274 (let ((map (make-sparse-keymap)))
275 275 (define-key map "\C-c\C-c" 'hg-commit-finish)
276 276 (define-key map "\C-c\C-k" 'hg-commit-kill)
277 277 (define-key map "\C-xv=" 'hg-diff-repo)
278 278 map))
279 279
280 280 (defvar hg-commit-mode-file-map
281 281 (let ((map (make-sparse-keymap)))
282 282 (define-key map (hg-feature-cond (xemacs [button2])
283 283 (t [mouse-2]))
284 284 'hg-commit-mouse-clicked)
285 285 (define-key map " " 'hg-commit-toggle-file)
286 286 (define-key map "\r" 'hg-commit-toggle-file)
287 287 map))
288 288
289 289
290 290 ;;; Convenience functions.
291 291
292 292 (defsubst hg-binary ()
293 293 (if hg-binary
294 294 hg-binary
295 295 (error "No `hg' executable found!")))
296 296
297 297 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
298 298 "Replace all matches in STR for REGEXP with NEWTEXT string.
299 299 Return the new string. Optional LITERAL non-nil means do a literal
300 300 replacement.
301 301
302 302 This function bridges yet another pointless impedance gap between
303 303 XEmacs and GNU Emacs."
304 304 (hg-feature-cond
305 305 (xemacs (replace-in-string str regexp newtext literal))
306 306 (t (replace-regexp-in-string regexp newtext str nil literal))))
307 307
308 308 (defsubst hg-strip (str)
309 309 "Strip leading and trailing blank lines from a string."
310 310 (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
311 311 "\\`[ \t\r\n]*[\r\n]" ""))
312 312
313 313 (defsubst hg-chomp (str)
314 314 "Strip trailing newlines from a string."
315 315 (hg-replace-in-string str "[\r\n]+\\'" ""))
316 316
317 317 (defun hg-run-command (command &rest args)
318 318 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
319 319 The list ARGS contains a list of arguments to pass to the command."
320 320 (let* (exit-code
321 321 (output
322 322 (with-output-to-string
323 323 (with-current-buffer
324 324 standard-output
325 325 (setq exit-code
326 326 (apply 'call-process command nil t nil args))))))
327 327 (cons exit-code output)))
328 328
329 329 (defun hg-run (command &rest args)
330 330 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
331 331 (apply 'hg-run-command (hg-binary) command args))
332 332
333 333 (defun hg-run0 (command &rest args)
334 334 "Run the Mercurial command COMMAND, returning its output.
335 335 If the command does not exit with a zero status code, raise an error."
336 336 (let ((res (apply 'hg-run-command (hg-binary) command args)))
337 337 (if (not (eq (car res) 0))
338 338 (error "Mercurial command failed %s - exit code %s"
339 339 (cons command args)
340 340 (car res))
341 341 (cdr res))))
342 342
343 343 (defmacro hg-do-across-repo (path &rest body)
344 344 (let ((root-name (make-symbol "root-"))
345 345 (buf-name (make-symbol "buf-")))
346 346 `(let ((,root-name (hg-root ,path)))
347 347 (save-excursion
348 348 (dolist (,buf-name (buffer-list))
349 349 (set-buffer ,buf-name)
350 350 (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
351 351 ,@body))))))
352 352
353 353 (put 'hg-do-across-repo 'lisp-indent-function 1)
354 354
355 355 (defun hg-sync-buffers (path)
356 356 "Sync buffers visiting PATH with their on-disk copies.
357 357 If PATH is not being visited, but is under the repository root, sync
358 358 all buffers visiting files in the repository."
359 359 (let ((buf (find-buffer-visiting path)))
360 360 (if buf
361 361 (with-current-buffer buf
362 362 (vc-buffer-sync))
363 363 (hg-do-across-repo path
364 364 (vc-buffer-sync)))))
365 365
366 366 (defun hg-buffer-commands (pnt)
367 367 "Use the properties of a character to do something sensible."
368 368 (interactive "d")
369 369 (let ((rev (get-char-property pnt 'rev))
370 370 (file (get-char-property pnt 'file)))
371 371 (cond
372 372 (file
373 373 (find-file-other-window file))
374 374 (rev
375 375 (hg-diff hg-view-file-name rev rev))
376 376 ((message "I don't know how to do that yet")))))
377 377
378 378 (defsubst hg-event-point (event)
379 379 "Return the character position of the mouse event EVENT."
380 380 (hg-feature-cond (xemacs (event-point event))
381 381 (t (posn-point (event-start event)))))
382 382
383 383 (defsubst hg-event-window (event)
384 384 "Return the window over which mouse event EVENT occurred."
385 385 (hg-feature-cond (xemacs (event-window event))
386 386 (t (posn-window (event-start event)))))
387 387
388 388 (defun hg-buffer-mouse-clicked (event)
389 389 "Translate the mouse clicks in a HG log buffer to character events.
390 390 These are then handed off to `hg-buffer-commands'.
391 391
392 392 Handle frickin' frackin' gratuitous event-related incompatibilities."
393 393 (interactive "e")
394 394 (select-window (hg-event-window event))
395 395 (hg-buffer-commands (hg-event-point event)))
396 396
397 397 (defsubst hg-abbrev-file-name (file)
398 398 "Portable wrapper around abbreviate-file-name."
399 399 (hg-feature-cond (xemacs (abbreviate-file-name file t))
400 400 (t (abbreviate-file-name file))))
401 401
402 402 (defun hg-read-file-name (&optional prompt default)
403 403 "Read a file or directory name, or a pattern, to use with a command."
404 404 (save-excursion
405 405 (while hg-prev-buffer
406 406 (set-buffer hg-prev-buffer))
407 407 (let ((path (or default
408 408 (buffer-file-name)
409 409 (expand-file-name default-directory))))
410 410 (if (or (not path) current-prefix-arg)
411 411 (expand-file-name
412 412 (eval (list* 'read-file-name
413 413 (format "File, directory or pattern%s: "
414 414 (or prompt ""))
415 415 (and path (file-name-directory path))
416 416 nil nil
417 417 (and path (file-name-nondirectory path))
418 418 (hg-feature-cond
419 419 (xemacs (cons (quote 'hg-file-history) nil))
420 420 (t nil)))))
421 421 path))))
422 422
423 423 (defun hg-read-number (&optional prompt default)
424 424 "Read a integer value."
425 425 (save-excursion
426 426 (if (or (not default) current-prefix-arg)
427 427 (string-to-number
428 428 (eval (list* 'read-string
429 (or prompt "")
429 (or prompt "")
430 430 (if default (cons (format "%d" default) nil) nil))))
431 431 default)))
432 432
433 433 (defun hg-read-config ()
434 434 "Return an alist of (key . value) pairs of Mercurial config data.
435 435 Each key is of the form (section . name)."
436 436 (let (items)
437 437 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
438 438 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
439 439 (let* ((left (substring line (match-beginning 1) (match-end 1)))
440 440 (right (substring line (match-beginning 2) (match-end 2)))
441 441 (key (split-string left "\\."))
442 442 (value (hg-replace-in-string right "\\\\n" "\n" t)))
443 443 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
444 444
445 445 (defun hg-config-section (section config)
446 446 "Return an alist of (name . value) pairs for SECTION of CONFIG."
447 447 (let (items)
448 448 (dolist (item config items)
449 449 (when (equal (caar item) section)
450 450 (setq items (cons (cons (cdar item) (cdr item)) items))))))
451 451
452 452 (defun hg-string-starts-with (sub str)
453 453 "Indicate whether string STR starts with the substring or character SUB."
454 454 (if (not (stringp sub))
455 455 (and (> (length str) 0) (equal (elt str 0) sub))
456 456 (let ((sub-len (length sub)))
457 457 (and (<= sub-len (length str))
458 458 (string= sub (substring str 0 sub-len))))))
459 459
460 460 (defun hg-complete-repo (string predicate all)
461 461 "Attempt to complete a repository name.
462 462 We complete on either symbolic names from Mercurial's config or real
463 463 directory names from the file system. We do not penalise URLs."
464 464 (or (if all
465 465 (all-completions string hg-repo-completion-table predicate)
466 466 (try-completion string hg-repo-completion-table predicate))
467 467 (let* ((str (expand-file-name string))
468 468 (dir (file-name-directory str))
469 469 (file (file-name-nondirectory str)))
470 470 (if all
471 471 (let (completions)
472 472 (dolist (name (delete "./" (file-name-all-completions file dir))
473 473 completions)
474 474 (let ((path (concat dir name)))
475 475 (when (file-directory-p path)
476 476 (setq completions (cons name completions))))))
477 477 (let ((comp (file-name-completion file dir)))
478 478 (if comp
479 479 (hg-abbrev-file-name (concat dir comp))))))))
480 480
481 481 (defun hg-read-repo-name (&optional prompt initial-contents default)
482 482 "Read the location of a repository."
483 483 (save-excursion
484 484 (while hg-prev-buffer
485 485 (set-buffer hg-prev-buffer))
486 486 (let (hg-repo-completion-table)
487 487 (if current-prefix-arg
488 488 (progn
489 489 (dolist (path (hg-config-section "paths" (hg-read-config)))
490 490 (setq hg-repo-completion-table
491 491 (cons (cons (car path) t) hg-repo-completion-table))
492 492 (unless (hg-string-starts-with (hg-feature-cond
493 493 (xemacs directory-sep-char)
494 494 (t ?/))
495 495 (cdr path))
496 496 (setq hg-repo-completion-table
497 497 (cons (cons (cdr path) t) hg-repo-completion-table))))
498 498 (completing-read (format "Repository%s: " (or prompt ""))
499 499 'hg-complete-repo
500 500 nil
501 501 nil
502 502 initial-contents
503 503 'hg-repo-history
504 504 default))
505 505 default))))
506 506
507 507 (defun hg-read-rev (&optional prompt default)
508 508 "Read a revision or tag, offering completions."
509 509 (save-excursion
510 510 (while hg-prev-buffer
511 511 (set-buffer hg-prev-buffer))
512 512 (let ((rev (or default "tip")))
513 513 (if current-prefix-arg
514 514 (let ((revs (split-string
515 515 (hg-chomp
516 516 (hg-run0 "-q" "log" "-l"
517 517 (format "%d" hg-rev-completion-limit)))
518 518 "[\n:]")))
519 519 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
520 520 (setq revs (cons (car (split-string line "\\s-")) revs)))
521 521 (completing-read (format "Revision%s (%s): "
522 522 (or prompt "")
523 523 (or default "tip"))
524 524 (map 'list 'cons revs revs)
525 525 nil
526 526 nil
527 527 nil
528 528 'hg-rev-history
529 529 (or default "tip")))
530 530 rev))))
531 531
532 532 (defun hg-parents-for-mode-line (root)
533 533 "Format the parents of the working directory for the mode line."
534 534 (let ((parents (split-string (hg-chomp
535 535 (hg-run0 "--cwd" root "parents" "--template"
536 536 "{rev}\n")) "\n")))
537 537 (mapconcat 'identity parents "+")))
538 538
539 539 (defun hg-buffers-visiting-repo (&optional path)
540 540 "Return a list of buffers visiting the repository containing PATH."
541 541 (let ((root-name (hg-root (or path (buffer-file-name))))
542 542 bufs)
543 543 (save-excursion
544 544 (dolist (buf (buffer-list) bufs)
545 545 (set-buffer buf)
546 546 (let ((name (buffer-file-name)))
547 547 (when (and hg-status name (equal (hg-root name) root-name))
548 548 (setq bufs (cons buf bufs))))))))
549 549
550 550 (defun hg-update-mode-lines (path)
551 551 "Update the mode lines of all buffers visiting the same repository as PATH."
552 552 (let* ((root (hg-root path))
553 553 (parents (hg-parents-for-mode-line root)))
554 554 (save-excursion
555 555 (dolist (info (hg-path-status
556 556 root
557 557 (mapcar
558 558 (function
559 559 (lambda (buf)
560 560 (substring (buffer-file-name buf) (length root))))
561 561 (hg-buffers-visiting-repo root))))
562 562 (let* ((name (car info))
563 563 (status (cdr info))
564 564 (buf (find-buffer-visiting (concat root name))))
565 565 (when buf
566 566 (set-buffer buf)
567 567 (hg-mode-line-internal status parents)))))))
568
568
569 569
570 570 ;;; View mode bits.
571 571
572 572 (defun hg-exit-view-mode (buf)
573 573 "Exit from hg-view-mode.
574 574 We delete the current window if entering hg-view-mode split the
575 575 current frame."
576 576 (when (and (eq buf (current-buffer))
577 577 (> (length (window-list)) 1))
578 578 (delete-window))
579 579 (when (buffer-live-p buf)
580 580 (kill-buffer buf)))
581 581
582 582 (defun hg-view-mode (prev-buffer &optional file-name)
583 583 (goto-char (point-min))
584 584 (set-buffer-modified-p nil)
585 585 (toggle-read-only t)
586 586 (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
587 587 (t (view-mode-enter nil 'hg-exit-view-mode)))
588 588 (setq hg-view-mode t)
589 589 (setq truncate-lines t)
590 590 (when file-name
591 (setq hg-view-file-name
591 (setq hg-view-file-name
592 592 (hg-abbrev-file-name file-name))))
593 593
594 594 (defun hg-file-status (file)
595 595 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
596 596 (let* ((s (hg-run "status" file))
597 597 (exit (car s))
598 598 (output (cdr s)))
599 599 (if (= exit 0)
600 600 (let ((state (and (>= (length output) 2)
601 601 (= (aref output 1) ? )
602 602 (assq (aref output 0) hg-state-alist))))
603 603 (if state
604 604 (cdr state)
605 605 'normal)))))
606 606
607 607 (defun hg-path-status (root paths)
608 608 "Return status of PATHS in repo ROOT as an alist.
609 609 Each entry is a pair (FILE-NAME . STATUS)."
610 610 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
611 611 result)
612 612 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
613 613 (let (state name)
614 614 (cond ((= (aref entry 1) ? )
615 615 (setq state (assq (aref entry 0) hg-state-alist)
616 616 name (substring entry 2)))
617 617 ((string-match "\\(.*\\): " entry)
618 618 (setq name (match-string 1 entry))))
619 619 (setq result (cons (cons name state) result))))))
620 620
621 621 (defmacro hg-view-output (args &rest body)
622 622 "Execute BODY in a clean buffer, then quickly display that buffer.
623 623 If the buffer contains one line, its contents are displayed in the
624 624 minibuffer. Otherwise, the buffer is displayed in view-mode.
625 625 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
626 626 the name of the buffer to create, and FILE is the name of the file
627 627 being viewed."
628 628 (let ((prev-buf (make-symbol "prev-buf-"))
629 629 (v-b-name (car args))
630 630 (v-m-rest (cdr args)))
631 631 `(let ((view-buf-name ,v-b-name)
632 632 (,prev-buf (current-buffer)))
633 633 (get-buffer-create view-buf-name)
634 634 (kill-buffer view-buf-name)
635 635 (get-buffer-create view-buf-name)
636 636 (set-buffer view-buf-name)
637 637 (save-excursion
638 638 ,@body)
639 639 (case (count-lines (point-min) (point-max))
640 640 ((0)
641 641 (kill-buffer view-buf-name)
642 642 (message "(No output)"))
643 643 ((1)
644 644 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
645 645 (kill-buffer view-buf-name)
646 646 (message "%s" msg)))
647 647 (t
648 648 (pop-to-buffer view-buf-name)
649 649 (setq hg-prev-buffer ,prev-buf)
650 650 (hg-view-mode ,prev-buf ,@v-m-rest))))))
651 651
652 652 (put 'hg-view-output 'lisp-indent-function 1)
653 653
654 654 ;;; Context save and restore across revert and other operations.
655 655
656 656 (defun hg-position-context (pos)
657 657 "Return information to help find the given position again."
658 658 (let* ((end (min (point-max) (+ pos 98))))
659 659 (list pos
660 660 (buffer-substring (max (point-min) (- pos 2)) end)
661 661 (- end pos))))
662 662
663 663 (defun hg-buffer-context ()
664 664 "Return information to help restore a user's editing context.
665 665 This is useful across reverts and merges, where a context is likely
666 666 to have moved a little, but not really changed."
667 667 (let ((point-context (hg-position-context (point)))
668 668 (mark-context (let ((mark (mark-marker)))
669 669 (and mark (hg-position-context mark)))))
670 670 (list point-context mark-context)))
671 671
672 672 (defun hg-find-context (ctx)
673 673 "Attempt to find a context in the given buffer.
674 674 Always returns a valid, hopefully sane, position."
675 675 (let ((pos (nth 0 ctx))
676 676 (str (nth 1 ctx))
677 677 (fixup (nth 2 ctx)))
678 678 (save-excursion
679 679 (goto-char (max (point-min) (- pos 15000)))
680 680 (if (and (not (equal str ""))
681 681 (search-forward str nil t))
682 682 (- (point) fixup)
683 683 (max pos (point-min))))))
684 684
685 685 (defun hg-restore-context (ctx)
686 686 "Attempt to restore the user's editing context."
687 687 (let ((point-context (nth 0 ctx))
688 688 (mark-context (nth 1 ctx)))
689 689 (goto-char (hg-find-context point-context))
690 690 (when mark-context
691 691 (set-mark (hg-find-context mark-context)))))
692 692
693 693
694 694 ;;; Hooks.
695 695
696 696 (defun hg-mode-line-internal (status parents)
697 697 (setq hg-status status
698 698 hg-mode (and status (concat " Hg:"
699 699 parents
700 700 (cdr (assq status
701 701 '((normal . "")
702 702 (removed . "r")
703 703 (added . "a")
704 704 (deleted . "!")
705 705 (modified . "m"))))))))
706
706
707 707 (defun hg-mode-line (&optional force)
708 708 "Update the modeline with the current status of a file.
709 709 An update occurs if optional argument FORCE is non-nil,
710 710 hg-update-modeline is non-nil, or we have not yet checked the state of
711 711 the file."
712 712 (let ((root (hg-root)))
713 713 (when (and root (or force hg-update-modeline (not hg-mode)))
714 714 (let ((status (hg-file-status buffer-file-name))
715 715 (parents (hg-parents-for-mode-line root)))
716 716 (hg-mode-line-internal status parents)
717 717 status))))
718 718
719 719 (defun hg-mode (&optional toggle)
720 720 "Minor mode for Mercurial distributed SCM integration.
721 721
722 722 The Mercurial mode user interface is based on that of VC mode, so if
723 723 you're already familiar with VC, the same keybindings and functions
724 724 will generally work.
725 725
726 726 Below is a list of many common SCM tasks. In the list, `G/L\'
727 727 indicates whether a key binding is global (G) to a repository or
728 728 local (L) to a file. Many commands take a prefix argument.
729 729
730 730 SCM Task G/L Key Binding Command Name
731 731 -------- --- ----------- ------------
732 732 Help overview (what you are reading) G C-c h h hg-help-overview
733 733
734 734 Tell Mercurial to manage a file G C-c h a hg-add
735 735 Commit changes to current file only L C-x v n hg-commit-start
736 736 Undo changes to file since commit L C-x v u hg-revert-buffer
737 737
738 738 Diff file vs last checkin L C-x v = hg-diff
739 739
740 740 View file change history L C-x v l hg-log
741 741 View annotated file L C-x v a hg-annotate
742 742
743 743 Diff repo vs last checkin G C-c h = hg-diff-repo
744 744 View status of files in repo G C-c h s hg-status
745 745 Commit all changes G C-c h c hg-commit-start
746 746
747 747 Undo all changes since last commit G C-c h U hg-revert
748 748 View repo change history G C-c h l hg-log-repo
749 749
750 750 See changes that can be pulled G C-c h , hg-incoming
751 751 Pull changes G C-c h < hg-pull
752 752 Update working directory after pull G C-c h u hg-update
753 753 See changes that can be pushed G C-c h . hg-outgoing
754 754 Push changes G C-c h > hg-push"
755 755 (unless vc-make-backup-files
756 756 (set (make-local-variable 'backup-inhibited) t))
757 757 (run-hooks 'hg-mode-hook))
758 758
759 759 (defun hg-find-file-hook ()
760 760 (ignore-errors
761 761 (when (hg-mode-line)
762 762 (hg-mode))))
763 763
764 764 (add-hook 'find-file-hooks 'hg-find-file-hook)
765 765
766 766 (defun hg-after-save-hook ()
767 767 (ignore-errors
768 768 (let ((old-status hg-status))
769 769 (hg-mode-line)
770 770 (if (and (not old-status) hg-status)
771 771 (hg-mode)))))
772 772
773 773 (add-hook 'after-save-hook 'hg-after-save-hook)
774 774
775 775
776 776 ;;; User interface functions.
777 777
778 778 (defun hg-help-overview ()
779 779 "This is an overview of the Mercurial SCM mode for Emacs.
780 780
781 781 You can find the source code, license (GPL v2), and credits for this
782 782 code by typing `M-x find-library mercurial RET'."
783 783 (interactive)
784 784 (hg-view-output ("Mercurial Help Overview")
785 785 (insert (documentation 'hg-help-overview))
786 786 (let ((pos (point)))
787 787 (insert (documentation 'hg-mode))
788 788 (goto-char pos)
789 789 (end-of-line 1)
790 790 (delete-region pos (point)))
791 791 (let ((hg-root-dir (hg-root)))
792 792 (if (not hg-root-dir)
793 793 (error "error: %s: directory is not part of a Mercurial repository."
794 794 default-directory)
795 795 (cd hg-root-dir)))))
796 796
797 797 (defun hg-fix-paths ()
798 798 "Fix paths reported by some Mercurial commands."
799 799 (save-excursion
800 800 (goto-char (point-min))
801 801 (while (re-search-forward " \\.\\.." nil t)
802 802 (replace-match " " nil nil))))
803 803
804 804 (defun hg-add (path)
805 805 "Add PATH to the Mercurial repository on the next commit.
806 806 With a prefix argument, prompt for the path to add."
807 807 (interactive (list (hg-read-file-name " to add")))
808 808 (let ((buf (current-buffer))
809 809 (update (equal buffer-file-name path)))
810 810 (hg-view-output (hg-output-buffer-name)
811 811 (apply 'call-process (hg-binary) nil t nil (list "add" path))
812 812 (hg-fix-paths)
813 813 (goto-char (point-min))
814 814 (cd (hg-root path)))
815 815 (when update
816 816 (unless vc-make-backup-files
817 817 (set (make-local-variable 'backup-inhibited) t))
818 818 (with-current-buffer buf
819 819 (hg-mode-line)))))
820 820
821 821 (defun hg-addremove ()
822 822 (interactive)
823 823 (error "not implemented"))
824 824
825 825 (defun hg-annotate ()
826 826 (interactive)
827 827 (error "not implemented"))
828 828
829 829 (defun hg-commit-toggle-file (pos)
830 830 "Toggle whether or not the file at POS will be committed."
831 831 (interactive "d")
832 832 (save-excursion
833 833 (goto-char pos)
834 834 (let ((face (get-text-property pos 'face))
835 835 (inhibit-read-only t)
836 836 bol)
837 837 (beginning-of-line)
838 838 (setq bol (+ (point) 4))
839 839 (end-of-line)
840 840 (if (eq face 'bold)
841 841 (progn
842 842 (remove-text-properties bol (point) '(face nil))
843 843 (message "%s will not be committed"
844 844 (buffer-substring bol (point))))
845 845 (add-text-properties bol (point) '(face bold))
846 846 (message "%s will be committed"
847 847 (buffer-substring bol (point)))))))
848 848
849 849 (defun hg-commit-mouse-clicked (event)
850 850 "Toggle whether or not the file at POS will be committed."
851 851 (interactive "@e")
852 852 (hg-commit-toggle-file (hg-event-point event)))
853 853
854 854 (defun hg-commit-kill ()
855 855 "Kill the commit currently being prepared."
856 856 (interactive)
857 857 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
858 858 (let ((buf hg-prev-buffer))
859 859 (kill-buffer nil)
860 860 (switch-to-buffer buf))))
861 861
862 862 (defun hg-commit-finish ()
863 863 "Finish preparing a commit, and perform the actual commit.
864 864 The hook hg-pre-commit-hook is run before anything else is done. If
865 865 the commit message is empty and hg-commit-allow-empty-message is nil,
866 866 an error is raised. If the list of files to commit is empty and
867 867 hg-commit-allow-empty-file-list is nil, an error is raised."
868 868 (interactive)
869 869 (let ((root hg-root))
870 870 (save-excursion
871 871 (run-hooks 'hg-pre-commit-hook)
872 872 (goto-char (point-min))
873 873 (search-forward hg-commit-message-start)
874 874 (let (message files)
875 875 (let ((start (point)))
876 876 (goto-char (point-max))
877 877 (search-backward hg-commit-message-end)
878 878 (setq message (hg-strip (buffer-substring start (point)))))
879 879 (when (and (= (length message) 0)
880 880 (not hg-commit-allow-empty-message))
881 881 (error "Cannot proceed - commit message is empty"))
882 882 (forward-line 1)
883 883 (beginning-of-line)
884 884 (while (< (point) (point-max))
885 885 (let ((pos (+ (point) 4)))
886 886 (end-of-line)
887 887 (when (eq (get-text-property pos 'face) 'bold)
888 888 (end-of-line)
889 889 (setq files (cons (buffer-substring pos (point)) files))))
890 890 (forward-line 1))
891 891 (when (and (= (length files) 0)
892 892 (not hg-commit-allow-empty-file-list))
893 893 (error "Cannot proceed - no files to commit"))
894 894 (setq message (concat message "\n"))
895 895 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
896 896 (let ((buf hg-prev-buffer))
897 897 (kill-buffer nil)
898 898 (switch-to-buffer buf))
899 899 (hg-update-mode-lines root))))
900 900
901 901 (defun hg-commit-mode ()
902 902 "Mode for describing a commit of changes to a Mercurial repository.
903 903 This involves two actions: describing the changes with a commit
904 904 message, and choosing the files to commit.
905 905
906 906 To describe the commit, simply type some text in the designated area.
907 907
908 908 By default, all modified, added and removed files are selected for
909 909 committing. Files that will be committed are displayed in bold face\;
910 910 those that will not are displayed in normal face.
911 911
912 912 To toggle whether a file will be committed, move the cursor over a
913 913 particular file and hit space or return. Alternatively, middle click
914 914 on the file.
915 915
916 916 Key bindings
917 917 ------------
918 918 \\[hg-commit-finish] proceed with commit
919 919 \\[hg-commit-kill] kill commit
920 920
921 921 \\[hg-diff-repo] view diff of pending changes"
922 922 (interactive)
923 923 (use-local-map hg-commit-mode-map)
924 924 (set-syntax-table text-mode-syntax-table)
925 925 (setq local-abbrev-table text-mode-abbrev-table
926 926 major-mode 'hg-commit-mode
927 927 mode-name "Hg-Commit")
928 928 (set-buffer-modified-p nil)
929 929 (setq buffer-undo-list nil)
930 930 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
931 931
932 932 (defun hg-commit-start ()
933 933 "Prepare a commit of changes to the repository containing the current file."
934 934 (interactive)
935 935 (while hg-prev-buffer
936 936 (set-buffer hg-prev-buffer))
937 937 (let ((root (hg-root))
938 938 (prev-buffer (current-buffer))
939 939 modified-files)
940 940 (unless root
941 941 (error "Cannot commit outside a repository!"))
942 942 (hg-sync-buffers root)
943 943 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
944 944 (when (and (= (length modified-files) 0)
945 945 (not hg-commit-allow-empty-file-list))
946 946 (error "No pending changes to commit"))
947 947 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
948 948 (pop-to-buffer (get-buffer-create buf-name))
949 949 (when (= (point-min) (point-max))
950 950 (set (make-local-variable 'hg-root) root)
951 951 (setq hg-prev-buffer prev-buffer)
952 952 (insert "\n")
953 953 (let ((bol (point)))
954 954 (insert hg-commit-message-end)
955 955 (add-text-properties bol (point) '(face bold-italic)))
956 956 (let ((file-area (point)))
957 957 (insert modified-files)
958 958 (goto-char file-area)
959 959 (while (< (point) (point-max))
960 960 (let ((bol (point)))
961 961 (forward-char 1)
962 962 (insert " ")
963 963 (end-of-line)
964 964 (add-text-properties (+ bol 4) (point)
965 965 '(face bold mouse-face highlight)))
966 966 (forward-line 1))
967 967 (goto-char file-area)
968 968 (add-text-properties (point) (point-max)
969 969 `(keymap ,hg-commit-mode-file-map))
970 970 (goto-char (point-min))
971 971 (insert hg-commit-message-start)
972 972 (add-text-properties (point-min) (point) '(face bold-italic))
973 973 (insert "\n\n")
974 974 (forward-line -1)
975 975 (save-excursion
976 976 (goto-char (point-max))
977 977 (search-backward hg-commit-message-end)
978 978 (add-text-properties (match-beginning 0) (point-max)
979 979 '(read-only t))
980 980 (goto-char (point-min))
981 981 (search-forward hg-commit-message-start)
982 982 (add-text-properties (match-beginning 0) (match-end 0)
983 983 '(read-only t)))
984 984 (hg-commit-mode)
985 985 (cd root))))))
986 986
987 987 (defun hg-diff (path &optional rev1 rev2)
988 988 "Show the differences between REV1 and REV2 of PATH.
989 989 When called interactively, the default behaviour is to treat REV1 as
990 990 the \"parent\" revision, REV2 as the current edited version of the file, and
991 991 PATH as the file edited in the current buffer.
992 992 With a prefix argument, prompt for all of these."
993 993 (interactive (list (hg-read-file-name " to diff")
994 994 (let ((rev1 (hg-read-rev " to start with" 'parent)))
995 995 (and (not (eq rev1 'parent)) rev1))
996 996 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
997 997 (and (not (eq rev2 'working-dir)) rev2))))
998 998 (hg-sync-buffers path)
999 999 (let ((a-path (hg-abbrev-file-name path))
1000 1000 ;; none revision is specified explicitly
1001 1001 (none (and (not rev1) (not rev2)))
1002 1002 ;; only one revision is specified explicitly
1003 (one (or (and (or (equal rev1 rev2) (not rev2)) rev1)
1003 (one (or (and (or (equal rev1 rev2) (not rev2)) rev1)
1004 1004 (and (not rev1) rev2)))
1005 1005 diff)
1006 1006 (hg-view-output ((cond
1007 1007 (none
1008 1008 (format "Mercurial: Diff against parent of %s" a-path))
1009 1009 (one
1010 1010 (format "Mercurial: Diff of rev %s of %s" one a-path))
1011 1011 (t
1012 1012 (format "Mercurial: Diff from rev %s to %s of %s"
1013 1013 rev1 rev2 a-path))))
1014 1014 (cond
1015 (none
1015 (none
1016 1016 (call-process (hg-binary) nil t nil "diff" path))
1017 1017 (one
1018 1018 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1019 1019 (t
1020 1020 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1021 1021 (diff-mode)
1022 1022 (setq diff (not (= (point-min) (point-max))))
1023 1023 (font-lock-fontify-buffer)
1024 1024 (cd (hg-root path)))
1025 1025 diff))
1026 1026
1027 1027 (defun hg-diff-repo (path &optional rev1 rev2)
1028 1028 "Show the differences between REV1 and REV2 of repository containing PATH.
1029 1029 When called interactively, the default behaviour is to treat REV1 as
1030 1030 the \"parent\" revision, REV2 as the current edited version of the file, and
1031 1031 PATH as the `hg-root' of the current buffer.
1032 1032 With a prefix argument, prompt for all of these."
1033 1033 (interactive (list (hg-read-file-name " to diff")
1034 1034 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1035 1035 (and (not (eq rev1 'parent)) rev1))
1036 1036 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1037 1037 (and (not (eq rev2 'working-dir)) rev2))))
1038 1038 (hg-diff (hg-root path) rev1 rev2))
1039 1039
1040 1040 (defun hg-forget (path)
1041 1041 "Lose track of PATH, which has been added, but not yet committed.
1042 1042 This will prevent the file from being incorporated into the Mercurial
1043 1043 repository on the next commit.
1044 1044 With a prefix argument, prompt for the path to forget."
1045 1045 (interactive (list (hg-read-file-name " to forget")))
1046 1046 (let ((buf (current-buffer))
1047 1047 (update (equal buffer-file-name path)))
1048 1048 (hg-view-output (hg-output-buffer-name)
1049 1049 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1050 1050 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1051 1051 (hg-fix-paths)
1052 1052 (goto-char (point-min))
1053 1053 (cd (hg-root path)))
1054 1054 (when update
1055 1055 (with-current-buffer buf
1056 1056 (when (local-variable-p 'backup-inhibited)
1057 1057 (kill-local-variable 'backup-inhibited))
1058 1058 (hg-mode-line)))))
1059 1059
1060 1060 (defun hg-incoming (&optional repo)
1061 1061 "Display changesets present in REPO that are not present locally."
1062 1062 (interactive (list (hg-read-repo-name " where changes would come from")))
1063 1063 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1064 1064 (hg-abbrev-file-name (hg-root))
1065 1065 (hg-abbrev-file-name
1066 1066 (or repo hg-incoming-repository))))
1067 1067 (call-process (hg-binary) nil t nil "incoming"
1068 1068 (or repo hg-incoming-repository))
1069 1069 (hg-log-mode)
1070 1070 (cd (hg-root))))
1071 1071
1072 1072 (defun hg-init ()
1073 1073 (interactive)
1074 1074 (error "not implemented"))
1075 1075
1076 1076 (defun hg-log-mode ()
1077 1077 "Mode for viewing a Mercurial change log."
1078 1078 (goto-char (point-min))
1079 1079 (when (looking-at "^searching for changes.*$")
1080 1080 (delete-region (match-beginning 0) (match-end 0)))
1081 1081 (run-hooks 'hg-log-mode-hook))
1082 1082
1083 1083 (defun hg-log (path &optional rev1 rev2 log-limit)
1084 1084 "Display the revision history of PATH.
1085 1085 History is displayed between REV1 and REV2.
1086 1086 Number of displayed changesets is limited to LOG-LIMIT.
1087 1087 REV1 defaults to the tip, while REV2 defaults to 0.
1088 1088 LOG-LIMIT defaults to `hg-log-limit'.
1089 1089 With a prefix argument, prompt for each parameter."
1090 1090 (interactive (list (hg-read-file-name " to log")
1091 1091 (hg-read-rev " to start with"
1092 1092 "tip")
1093 1093 (hg-read-rev " to end with"
1094 1094 "0")
1095 1095 (hg-read-number "Output limited to: "
1096 1096 hg-log-limit)))
1097 1097 (let ((a-path (hg-abbrev-file-name path))
1098 1098 (r1 (or rev1 "tip"))
1099 1099 (r2 (or rev2 "0"))
1100 1100 (limit (format "%d" (or log-limit hg-log-limit))))
1101 1101 (hg-view-output ((if (equal r1 r2)
1102 1102 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1103 (format
1103 (format
1104 1104 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1105 1105 limit r1 r2 a-path)))
1106 1106 (eval (list* 'call-process (hg-binary) nil t nil
1107 1107 "log"
1108 1108 "-r" (format "%s:%s" r1 r2)
1109 1109 "-l" limit
1110 1110 (if (> (length path) (length (hg-root path)))
1111 1111 (cons path nil)
1112 1112 nil)))
1113 1113 (hg-log-mode)
1114 1114 (cd (hg-root path)))))
1115 1115
1116 1116 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1117 1117 "Display the revision history of the repository containing PATH.
1118 1118 History is displayed between REV1 and REV2.
1119 1119 Number of displayed changesets is limited to LOG-LIMIT,
1120 1120 REV1 defaults to the tip, while REV2 defaults to 0.
1121 1121 LOG-LIMIT defaults to `hg-log-limit'.
1122 1122 With a prefix argument, prompt for each parameter."
1123 1123 (interactive (list (hg-read-file-name " to log")
1124 1124 (hg-read-rev " to start with"
1125 1125 "tip")
1126 (hg-read-rev " to end with"
1126 (hg-read-rev " to end with"
1127 1127 "0")
1128 1128 (hg-read-number "Output limited to: "
1129 1129 hg-log-limit)))
1130 1130 (hg-log (hg-root path) rev1 rev2 log-limit))
1131 1131
1132 1132 (defun hg-outgoing (&optional repo)
1133 1133 "Display changesets present locally that are not present in REPO."
1134 1134 (interactive (list (hg-read-repo-name " where changes would go to" nil
1135 1135 hg-outgoing-repository)))
1136 1136 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1137 1137 (hg-abbrev-file-name (hg-root))
1138 1138 (hg-abbrev-file-name
1139 1139 (or repo hg-outgoing-repository))))
1140 1140 (call-process (hg-binary) nil t nil "outgoing"
1141 1141 (or repo hg-outgoing-repository))
1142 1142 (hg-log-mode)
1143 1143 (cd (hg-root))))
1144 1144
1145 1145 (defun hg-pull (&optional repo)
1146 1146 "Pull changes from repository REPO.
1147 1147 This does not update the working directory."
1148 1148 (interactive (list (hg-read-repo-name " to pull from")))
1149 1149 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1150 1150 (hg-abbrev-file-name (hg-root))
1151 1151 (hg-abbrev-file-name
1152 1152 (or repo hg-incoming-repository))))
1153 1153 (call-process (hg-binary) nil t nil "pull"
1154 1154 (or repo hg-incoming-repository))
1155 1155 (cd (hg-root))))
1156 1156
1157 1157 (defun hg-push (&optional repo)
1158 1158 "Push changes to repository REPO."
1159 1159 (interactive (list (hg-read-repo-name " to push to")))
1160 1160 (hg-view-output ((format "Mercurial: Push from %s to %s"
1161 1161 (hg-abbrev-file-name (hg-root))
1162 1162 (hg-abbrev-file-name
1163 1163 (or repo hg-outgoing-repository))))
1164 1164 (call-process (hg-binary) nil t nil "push"
1165 1165 (or repo hg-outgoing-repository))
1166 1166 (cd (hg-root))))
1167 1167
1168 1168 (defun hg-revert-buffer-internal ()
1169 1169 (let ((ctx (hg-buffer-context)))
1170 1170 (message "Reverting %s..." buffer-file-name)
1171 1171 (hg-run0 "revert" buffer-file-name)
1172 1172 (revert-buffer t t t)
1173 1173 (hg-restore-context ctx)
1174 1174 (hg-mode-line)
1175 1175 (message "Reverting %s...done" buffer-file-name)))
1176 1176
1177 1177 (defun hg-revert-buffer ()
1178 1178 "Revert current buffer's file back to the latest committed version.
1179 1179 If the file has not changed, nothing happens. Otherwise, this
1180 1180 displays a diff and asks for confirmation before reverting."
1181 1181 (interactive)
1182 1182 (let ((vc-suppress-confirm nil)
1183 1183 (obuf (current-buffer))
1184 1184 diff)
1185 1185 (vc-buffer-sync)
1186 1186 (unwind-protect
1187 1187 (setq diff (hg-diff buffer-file-name))
1188 1188 (when diff
1189 1189 (unless (yes-or-no-p "Discard changes? ")
1190 1190 (error "Revert cancelled")))
1191 1191 (when diff
1192 1192 (let ((buf (current-buffer)))
1193 1193 (delete-window (selected-window))
1194 1194 (kill-buffer buf))))
1195 1195 (set-buffer obuf)
1196 1196 (when diff
1197 1197 (hg-revert-buffer-internal))))
1198 1198
1199 1199 (defun hg-root (&optional path)
1200 1200 "Return the root of the repository that contains the given path.
1201 1201 If the path is outside a repository, return nil.
1202 1202 When called interactively, the root is printed. A prefix argument
1203 1203 prompts for a path to check."
1204 1204 (interactive (list (hg-read-file-name)))
1205 1205 (if (or path (not hg-root))
1206 1206 (let ((root (do ((prev nil dir)
1207 1207 (dir (file-name-directory
1208 1208 (or
1209 1209 path
1210 1210 buffer-file-name
1211 1211 (expand-file-name default-directory)))
1212 1212 (file-name-directory (directory-file-name dir))))
1213 1213 ((equal prev dir))
1214 1214 (when (file-directory-p (concat dir ".hg"))
1215 1215 (return dir)))))
1216 1216 (when (interactive-p)
1217 1217 (if root
1218 1218 (message "The root of this repository is `%s'." root)
1219 1219 (message "The path `%s' is not in a Mercurial repository."
1220 1220 (hg-abbrev-file-name path))))
1221 1221 root)
1222 1222 hg-root))
1223 1223
1224 1224 (defun hg-cwd (&optional path)
1225 1225 "Return the current directory of PATH within the repository."
1226 1226 (do ((stack nil (cons (file-name-nondirectory
1227 1227 (directory-file-name dir))
1228 1228 stack))
1229 1229 (prev nil dir)
1230 1230 (dir (file-name-directory (or path buffer-file-name
1231 1231 (expand-file-name default-directory)))
1232 1232 (file-name-directory (directory-file-name dir))))
1233 1233 ((equal prev dir))
1234 1234 (when (file-directory-p (concat dir ".hg"))
1235 1235 (let ((cwd (mapconcat 'identity stack "/")))
1236 1236 (unless (equal cwd "")
1237 1237 (return (file-name-as-directory cwd)))))))
1238 1238
1239 1239 (defun hg-status (path)
1240 1240 "Print revision control status of a file or directory.
1241 1241 With prefix argument, prompt for the path to give status for.
1242 1242 Names are displayed relative to the repository root."
1243 1243 (interactive (list (hg-read-file-name " for status" (hg-root))))
1244 1244 (let ((root (hg-root)))
1245 1245 (hg-view-output ((format "Mercurial: Status of %s in %s"
1246 1246 (let ((name (substring (expand-file-name path)
1247 1247 (length root))))
1248 1248 (if (> (length name) 0)
1249 1249 name
1250 1250 "*"))
1251 1251 (hg-abbrev-file-name root)))
1252 1252 (apply 'call-process (hg-binary) nil t nil
1253 1253 (list "--cwd" root "status" path))
1254 1254 (cd (hg-root path)))))
1255 1255
1256 1256 (defun hg-undo ()
1257 1257 (interactive)
1258 1258 (error "not implemented"))
1259 1259
1260 1260 (defun hg-update ()
1261 1261 (interactive)
1262 1262 (error "not implemented"))
1263 1263
1264 1264 (defun hg-version-other-window ()
1265 1265 (interactive)
1266 1266 (error "not implemented"))
1267 1267
1268 1268
1269 1269 (provide 'mercurial)
1270 1270
1271 1271
1272 1272 ;;; Local Variables:
1273 1273 ;;; prompt-to-byte-compile: nil
1274 1274 ;;; end:
@@ -1,410 +1,410 b''
1 1 ;;; mq.el --- Emacs support for Mercurial Queues
2 2
3 3 ;; Copyright (C) 2006 Bryan O'Sullivan
4 4
5 5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6 6
7 7 ;; mq.el is free software; you can redistribute it and/or modify it
8 8 ;; under the terms of version 2 of the GNU General Public License as
9 9 ;; published by the Free Software Foundation.
10 10
11 11 ;; mq.el is distributed in the hope that it will be useful, but
12 12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 14 ;; General Public License for more details.
15 15
16 16 ;; You should have received a copy of the GNU General Public License
17 17 ;; along with mq.el, GNU Emacs, or XEmacs; see the file COPYING (`C-h
18 18 ;; C-l'). If not, write to the Free Software Foundation, Inc., 59
19 19 ;; Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 20
21 21 (require 'mercurial)
22 22
23 23
24 24 (defcustom mq-mode-hook nil
25 25 "Hook run when a buffer enters mq-mode."
26 26 :type 'sexp
27 27 :group 'mercurial)
28 28
29 29 (defcustom mq-global-prefix "\C-cq"
30 30 "The global prefix for Mercurial Queues keymap bindings."
31 31 :type 'sexp
32 32 :group 'mercurial)
33 33
34 34 (defcustom mq-edit-mode-hook nil
35 35 "Hook run after a buffer is populated to edit a patch description."
36 36 :type 'sexp
37 37 :group 'mercurial)
38 38
39 39 (defcustom mq-edit-finish-hook nil
40 40 "Hook run before a patch description is finished up with."
41 41 :type 'sexp
42 42 :group 'mercurial)
43 43
44 44 (defcustom mq-signoff-address nil
45 45 "Address with which to sign off on a patch."
46 46 :type 'string
47 47 :group 'mercurial)
48 48
49 49
50 50 ;;; Internal variables.
51 51
52 52 (defvar mq-mode nil
53 53 "Is this file managed by MQ?")
54 54 (make-variable-buffer-local 'mq-mode)
55 55 (put 'mq-mode 'permanent-local t)
56 56
57 57 (defvar mq-patch-history nil)
58 58
59 59 (defvar mq-top-patch '(nil))
60 60
61 61 (defvar mq-prev-buffer nil)
62 62 (make-variable-buffer-local 'mq-prev-buffer)
63 63 (put 'mq-prev-buffer 'permanent-local t)
64 64
65 65
66 66 ;;; Global keymap.
67 67
68 68 (defvar mq-global-map (make-sparse-keymap))
69 69 (fset 'mq-global-map mq-global-map)
70 70 (global-set-key mq-global-prefix 'mq-global-map)
71 71 (define-key mq-global-map "." 'mq-push)
72 72 (define-key mq-global-map ">" 'mq-push-all)
73 73 (define-key mq-global-map "," 'mq-pop)
74 74 (define-key mq-global-map "<" 'mq-pop-all)
75 75 (define-key mq-global-map "=" 'mq-diff)
76 76 (define-key mq-global-map "r" 'mq-refresh)
77 77 (define-key mq-global-map "e" 'mq-refresh-edit)
78 78 (define-key mq-global-map "i" 'mq-new)
79 79 (define-key mq-global-map "n" 'mq-next)
80 80 (define-key mq-global-map "o" 'mq-signoff)
81 81 (define-key mq-global-map "p" 'mq-previous)
82 82 (define-key mq-global-map "s" 'mq-edit-series)
83 83 (define-key mq-global-map "t" 'mq-top)
84 84
85 85 (add-minor-mode 'mq-mode 'mq-mode)
86 86
87 87
88 88 ;;; Refresh edit mode keymap.
89 89
90 90 (defvar mq-edit-mode-map (make-sparse-keymap))
91 91 (define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
92 92 (define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
93 93 (define-key mq-edit-mode-map "\C-c\C-s" 'mq-signoff)
94 94
95 95
96 96 ;;; Helper functions.
97 97
98 98 (defun mq-read-patch-name (&optional source prompt force)
99 99 "Read a patch name to use with a command.
100 100 May return nil, meaning \"use the default\"."
101 101 (let ((patches (split-string
102 102 (hg-chomp (hg-run0 (or source "qseries"))) "\n")))
103 103 (when force
104 104 (completing-read (format "Patch%s: " (or prompt ""))
105 105 (map 'list 'cons patches patches)
106 106 nil
107 107 nil
108 108 nil
109 109 'mq-patch-history))))
110 110
111 111 (defun mq-refresh-buffers (root)
112 112 (save-excursion
113 113 (dolist (buf (hg-buffers-visiting-repo root))
114 114 (when (not (verify-visited-file-modtime buf))
115 115 (set-buffer buf)
116 116 (let ((ctx (hg-buffer-context)))
117 117 (message "Refreshing %s..." (buffer-name))
118 118 (revert-buffer t t t)
119 119 (hg-restore-context ctx)
120 120 (message "Refreshing %s...done" (buffer-name))))))
121 121 (hg-update-mode-lines root)
122 122 (mq-update-mode-lines root))
123 123
124 124 (defun mq-last-line ()
125 125 (goto-char (point-max))
126 126 (beginning-of-line)
127 127 (when (looking-at "^$")
128 128 (forward-line -1))
129 129 (let ((bol (point)))
130 130 (end-of-line)
131 131 (let ((line (buffer-substring bol (point))))
132 132 (when (> (length line) 0)
133 133 line))))
134
134
135 135 (defun mq-push (&optional patch)
136 136 "Push patches until PATCH is reached.
137 137 If PATCH is nil, push at most one patch."
138 138 (interactive (list (mq-read-patch-name "qunapplied" " to push"
139 139 current-prefix-arg)))
140 140 (let ((root (hg-root))
141 141 (prev-buf (current-buffer))
142 142 last-line ok)
143 143 (unless root
144 144 (error "Cannot push outside a repository!"))
145 145 (hg-sync-buffers root)
146 146 (let ((buf-name (format "MQ: Push %s" (or patch "next patch"))))
147 147 (kill-buffer (get-buffer-create buf-name))
148 148 (split-window-vertically)
149 149 (other-window 1)
150 150 (switch-to-buffer (get-buffer-create buf-name))
151 151 (cd root)
152 152 (message "Pushing...")
153 153 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpush"
154 154 (if patch (list patch))))
155 155 last-line (mq-last-line))
156 156 (let ((lines (count-lines (point-min) (point-max))))
157 157 (if (or (<= lines 1)
158 158 (and (equal lines 2) (string-match "Now at:" last-line)))
159 159 (progn
160 160 (kill-buffer (current-buffer))
161 161 (delete-window))
162 162 (hg-view-mode prev-buf))))
163 163 (mq-refresh-buffers root)
164 164 (sit-for 0)
165 165 (when last-line
166 166 (if ok
167 167 (message "Pushing... %s" last-line)
168 168 (error "Pushing... %s" last-line)))))
169
169
170 170 (defun mq-push-all ()
171 171 "Push patches until all are applied."
172 172 (interactive)
173 173 (mq-push "-a"))
174 174
175 175 (defun mq-pop (&optional patch)
176 176 "Pop patches until PATCH is reached.
177 177 If PATCH is nil, pop at most one patch."
178 178 (interactive (list (mq-read-patch-name "qapplied" " to pop to"
179 179 current-prefix-arg)))
180 180 (let ((root (hg-root))
181 181 last-line ok)
182 182 (unless root
183 183 (error "Cannot pop outside a repository!"))
184 184 (hg-sync-buffers root)
185 185 (set-buffer (generate-new-buffer "qpop"))
186 186 (cd root)
187 187 (message "Popping...")
188 188 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpop"
189 189 (if patch (list patch))))
190 190 last-line (mq-last-line))
191 191 (kill-buffer (current-buffer))
192 192 (mq-refresh-buffers root)
193 193 (sit-for 0)
194 194 (when last-line
195 195 (if ok
196 196 (message "Popping... %s" last-line)
197 197 (error "Popping... %s" last-line)))))
198
198
199 199 (defun mq-pop-all ()
200 200 "Push patches until none are applied."
201 201 (interactive)
202 202 (mq-pop "-a"))
203 203
204 204 (defun mq-refresh-internal (root &rest args)
205 205 (hg-sync-buffers root)
206 206 (let ((patch (mq-patch-info "qtop")))
207 207 (message "Refreshing %s..." patch)
208 208 (let ((ret (apply 'hg-run "qrefresh" args)))
209 209 (if (equal (car ret) 0)
210 210 (message "Refreshing %s... done." patch)
211 211 (error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
212 212
213 213 (defun mq-refresh (&optional git)
214 214 "Refresh the topmost applied patch.
215 215 With a prefix argument, generate a git-compatible patch."
216 216 (interactive "P")
217 217 (let ((root (hg-root)))
218 218 (unless root
219 219 (error "Cannot refresh outside of a repository!"))
220 220 (apply 'mq-refresh-internal root (if git '("--git")))))
221 221
222 222 (defun mq-patch-info (cmd &optional msg)
223 223 (let* ((ret (hg-run cmd))
224 224 (info (hg-chomp (cdr ret))))
225 225 (if (equal (car ret) 0)
226 226 (if msg
227 227 (message "%s patch: %s" msg info)
228 228 info)
229 229 (error "%s" info))))
230 230
231 231 (defun mq-top ()
232 232 "Print the name of the topmost applied patch."
233 233 (interactive)
234 234 (mq-patch-info "qtop" "Top"))
235 235
236 236 (defun mq-next ()
237 237 "Print the name of the next patch to be pushed."
238 238 (interactive)
239 239 (mq-patch-info "qnext" "Next"))
240 240
241 241 (defun mq-previous ()
242 242 "Print the name of the first patch below the topmost applied patch.
243 243 This would become the active patch if popped to."
244 244 (interactive)
245 245 (mq-patch-info "qprev" "Previous"))
246 246
247 247 (defun mq-edit-finish ()
248 248 "Finish editing the description of this patch, and refresh the patch."
249 249 (interactive)
250 250 (unless (equal (mq-patch-info "qtop") mq-top)
251 251 (error "Topmost patch has changed!"))
252 252 (hg-sync-buffers hg-root)
253 253 (run-hooks 'mq-edit-finish-hook)
254 254 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
255 255 (let ((buf mq-prev-buffer))
256 256 (kill-buffer nil)
257 257 (switch-to-buffer buf)))
258
258
259 259 (defun mq-edit-kill ()
260 260 "Kill the edit currently being prepared."
261 261 (interactive)
262 262 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
263 263 (let ((buf mq-prev-buffer))
264 264 (kill-buffer nil)
265 265 (switch-to-buffer buf))))
266 266
267 267 (defun mq-get-top (root)
268 268 (let ((entry (assoc root mq-top-patch)))
269 269 (if entry
270 270 (cdr entry))))
271 271
272 272 (defun mq-set-top (root patch)
273 273 (let ((entry (assoc root mq-top-patch)))
274 274 (if entry
275 275 (if patch
276 276 (setcdr entry patch)
277 277 (setq mq-top-patch (delq entry mq-top-patch)))
278 278 (setq mq-top-patch (cons (cons root patch) mq-top-patch)))))
279 279
280 280 (defun mq-update-mode-lines (root)
281 281 (let ((cwd default-directory))
282 282 (cd root)
283 283 (condition-case nil
284 284 (mq-set-top root (mq-patch-info "qtop"))
285 285 (error (mq-set-top root nil)))
286 286 (cd cwd))
287 287 (let ((patch (mq-get-top root)))
288 288 (save-excursion
289 289 (dolist (buf (hg-buffers-visiting-repo root))
290 290 (set-buffer buf)
291 291 (if mq-mode
292 292 (setq mq-mode (or (and patch (concat " MQ:" patch)) " MQ")))))))
293 293
294 294 (defun mq-mode (&optional arg)
295 295 "Minor mode for Mercurial repositories with an MQ patch queue"
296 296 (interactive "i")
297 297 (cond ((hg-root)
298 298 (setq mq-mode (if (null arg) (not mq-mode)
299 299 arg))
300 300 (mq-update-mode-lines (hg-root))))
301 301 (run-hooks 'mq-mode-hook))
302 302
303 303 (defun mq-edit-mode ()
304 304 "Mode for editing the description of a patch.
305 305
306 306 Key bindings
307 307 ------------
308 308 \\[mq-edit-finish] use this description
309 309 \\[mq-edit-kill] abandon this description"
310 310 (interactive)
311 311 (use-local-map mq-edit-mode-map)
312 312 (set-syntax-table text-mode-syntax-table)
313 313 (setq local-abbrev-table text-mode-abbrev-table
314 314 major-mode 'mq-edit-mode
315 315 mode-name "MQ-Edit")
316 316 (set-buffer-modified-p nil)
317 317 (setq buffer-undo-list nil)
318 318 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
319
319
320 320 (defun mq-refresh-edit ()
321 321 "Refresh the topmost applied patch, editing the patch description."
322 322 (interactive)
323 323 (while mq-prev-buffer
324 324 (set-buffer mq-prev-buffer))
325 325 (let ((root (hg-root))
326 326 (prev-buffer (current-buffer))
327 327 (patch (mq-patch-info "qtop")))
328 328 (hg-sync-buffers root)
329 329 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
330 330 (switch-to-buffer (get-buffer-create buf-name))
331 331 (when (= (point-min) (point-max))
332 332 (set (make-local-variable 'hg-root) root)
333 333 (set (make-local-variable 'mq-top) patch)
334 334 (setq mq-prev-buffer prev-buffer)
335 335 (insert (hg-run0 "qheader"))
336 336 (goto-char (point-min)))
337 337 (mq-edit-mode)
338 338 (cd root)))
339 339 (message "Type `C-c C-c' to finish editing and refresh the patch."))
340 340
341 341 (defun mq-new (name)
342 342 "Create a new empty patch named NAME.
343 343 The patch is applied on top of the current topmost patch.
344 344 With a prefix argument, forcibly create the patch even if the working
345 345 directory is modified."
346 346 (interactive (list (mq-read-patch-name "qseries" " to create" t)))
347 347 (message "Creating patch...")
348 348 (let ((ret (if current-prefix-arg
349 349 (hg-run "qnew" "-f" name)
350 350 (hg-run "qnew" name))))
351 351 (if (equal (car ret) 0)
352 352 (progn
353 353 (hg-update-mode-lines (buffer-file-name))
354 354 (message "Creating patch... done."))
355 355 (error "Creating patch... %s" (hg-chomp (cdr ret))))))
356 356
357 357 (defun mq-edit-series ()
358 358 "Edit the MQ series file directly."
359 359 (interactive)
360 360 (let ((root (hg-root)))
361 361 (unless root
362 362 (error "Not in an MQ repository!"))
363 363 (find-file (concat root ".hg/patches/series"))))
364 364
365 365 (defun mq-diff (&optional git)
366 366 "Display a diff of the topmost applied patch.
367 367 With a prefix argument, display a git-compatible diff."
368 368 (interactive "P")
369 369 (hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
370 370 (if git
371 371 (call-process (hg-binary) nil t nil "qdiff" "--git")
372 372 (call-process (hg-binary) nil t nil "qdiff"))
373 373 (diff-mode)
374 374 (font-lock-fontify-buffer)))
375 375
376 376 (defun mq-signoff ()
377 377 "Sign off on the current patch, in the style used by the Linux kernel.
378 378 If the variable mq-signoff-address is non-nil, it will be used, otherwise
379 379 the value of the ui.username item from your hgrc will be used."
380 380 (interactive)
381 381 (let ((was-editing (eq major-mode 'mq-edit-mode))
382 382 signed)
383 383 (unless was-editing
384 384 (mq-refresh-edit))
385 385 (save-excursion
386 386 (let* ((user (or mq-signoff-address
387 387 (hg-run0 "debugconfig" "ui.username")))
388 388 (signoff (concat "Signed-off-by: " user)))
389 389 (if (search-forward signoff nil t)
390 390 (message "You have already signed off on this patch.")
391 391 (goto-char (point-max))
392 392 (let ((case-fold-search t))
393 393 (if (re-search-backward "^Signed-off-by: " nil t)
394 394 (forward-line 1)
395 395 (insert "\n")))
396 396 (insert signoff)
397 397 (message "%s" signoff)
398 398 (setq signed t))))
399 399 (unless was-editing
400 400 (if signed
401 401 (mq-edit-finish)
402 402 (mq-edit-kill)))))
403 403
404 404
405 405 (provide 'mq)
406 406
407 407
408 408 ;;; Local Variables:
409 409 ;;; prompt-to-byte-compile: nil
410 410 ;;; end:
@@ -1,562 +1,562 b''
1 1 #!/usr/bin/env python
2 2 # Copyright (C) 2004, 2005 Canonical Ltd
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 2 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 17
18 18
19 19 # mbp: "you know that thing where cvs gives you conflict markers?"
20 20 # s: "i hate that."
21 21
22 22 from mercurial import demandimport
23 23 demandimport.enable()
24 24
25 25 from mercurial import util, mdiff, fancyopts
26 26 from mercurial.i18n import _
27 27
28 28
29 29 class CantReprocessAndShowBase(Exception):
30 30 pass
31
31
32 32
33 33 def warn(message):
34 34 sys.stdout.flush()
35 35 sys.stderr.write(message)
36 36 sys.stderr.flush()
37 37
38 38
39 39 def intersect(ra, rb):
40 40 """Given two ranges return the range where they intersect or None.
41 41
42 42 >>> intersect((0, 10), (0, 6))
43 43 (0, 6)
44 44 >>> intersect((0, 10), (5, 15))
45 45 (5, 10)
46 46 >>> intersect((0, 10), (10, 15))
47 47 >>> intersect((0, 9), (10, 15))
48 48 >>> intersect((0, 9), (7, 15))
49 49 (7, 9)
50 50 """
51 51 assert ra[0] <= ra[1]
52 52 assert rb[0] <= rb[1]
53
53
54 54 sa = max(ra[0], rb[0])
55 55 sb = min(ra[1], rb[1])
56 56 if sa < sb:
57 57 return sa, sb
58 58 else:
59 59 return None
60 60
61 61
62 62 def compare_range(a, astart, aend, b, bstart, bend):
63 63 """Compare a[astart:aend] == b[bstart:bend], without slicing.
64 64 """
65 65 if (aend-astart) != (bend-bstart):
66 66 return False
67 67 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
68 68 if a[ia] != b[ib]:
69 69 return False
70 70 else:
71 71 return True
72
72
73 73
74 74
75 75
76 76 class Merge3Text(object):
77 77 """3-way merge of texts.
78 78
79 79 Given strings BASE, OTHER, THIS, tries to produce a combined text
80 80 incorporating the changes from both BASE->OTHER and BASE->THIS."""
81 81 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
82 82 self.basetext = basetext
83 83 self.atext = atext
84 84 self.btext = btext
85 85 if base is None:
86 86 base = mdiff.splitnewlines(basetext)
87 87 if a is None:
88 88 a = mdiff.splitnewlines(atext)
89 89 if b is None:
90 90 b = mdiff.splitnewlines(btext)
91 91 self.base = base
92 92 self.a = a
93 93 self.b = b
94 94
95 95
96 96
97 97 def merge_lines(self,
98 98 name_a=None,
99 99 name_b=None,
100 100 name_base=None,
101 101 start_marker='<<<<<<<',
102 102 mid_marker='=======',
103 103 end_marker='>>>>>>>',
104 104 base_marker=None,
105 105 reprocess=False):
106 106 """Return merge in cvs-like form.
107 107 """
108 108 self.conflicts = False
109 109 newline = '\n'
110 110 if len(self.a) > 0:
111 111 if self.a[0].endswith('\r\n'):
112 112 newline = '\r\n'
113 113 elif self.a[0].endswith('\r'):
114 114 newline = '\r'
115 115 if base_marker and reprocess:
116 116 raise CantReprocessAndShowBase()
117 117 if name_a:
118 118 start_marker = start_marker + ' ' + name_a
119 119 if name_b:
120 120 end_marker = end_marker + ' ' + name_b
121 121 if name_base and base_marker:
122 122 base_marker = base_marker + ' ' + name_base
123 123 merge_regions = self.merge_regions()
124 124 if reprocess is True:
125 125 merge_regions = self.reprocess_merge_regions(merge_regions)
126 126 for t in merge_regions:
127 127 what = t[0]
128 128 if what == 'unchanged':
129 129 for i in range(t[1], t[2]):
130 130 yield self.base[i]
131 131 elif what == 'a' or what == 'same':
132 132 for i in range(t[1], t[2]):
133 133 yield self.a[i]
134 134 elif what == 'b':
135 135 for i in range(t[1], t[2]):
136 136 yield self.b[i]
137 137 elif what == 'conflict':
138 138 self.conflicts = True
139 139 yield start_marker + newline
140 140 for i in range(t[3], t[4]):
141 141 yield self.a[i]
142 142 if base_marker is not None:
143 143 yield base_marker + newline
144 144 for i in range(t[1], t[2]):
145 145 yield self.base[i]
146 146 yield mid_marker + newline
147 147 for i in range(t[5], t[6]):
148 148 yield self.b[i]
149 149 yield end_marker + newline
150 150 else:
151 151 raise ValueError(what)
152
153
152
153
154 154
155 155
156 156
157 157 def merge_annotated(self):
158 158 """Return merge with conflicts, showing origin of lines.
159 159
160 Most useful for debugging merge.
160 Most useful for debugging merge.
161 161 """
162 162 for t in self.merge_regions():
163 163 what = t[0]
164 164 if what == 'unchanged':
165 165 for i in range(t[1], t[2]):
166 166 yield 'u | ' + self.base[i]
167 167 elif what == 'a' or what == 'same':
168 168 for i in range(t[1], t[2]):
169 169 yield what[0] + ' | ' + self.a[i]
170 170 elif what == 'b':
171 171 for i in range(t[1], t[2]):
172 172 yield 'b | ' + self.b[i]
173 173 elif what == 'conflict':
174 174 yield '<<<<\n'
175 175 for i in range(t[3], t[4]):
176 176 yield 'A | ' + self.a[i]
177 177 yield '----\n'
178 178 for i in range(t[5], t[6]):
179 179 yield 'B | ' + self.b[i]
180 180 yield '>>>>\n'
181 181 else:
182 182 raise ValueError(what)
183
184
183
184
185 185
186 186
187 187
188 188 def merge_groups(self):
189 189 """Yield sequence of line groups. Each one is a tuple:
190 190
191 191 'unchanged', lines
192 192 Lines unchanged from base
193 193
194 194 'a', lines
195 195 Lines taken from a
196 196
197 197 'same', lines
198 198 Lines taken from a (and equal to b)
199 199
200 200 'b', lines
201 201 Lines taken from b
202 202
203 203 'conflict', base_lines, a_lines, b_lines
204 204 Lines from base were changed to either a or b and conflict.
205 205 """
206 206 for t in self.merge_regions():
207 207 what = t[0]
208 208 if what == 'unchanged':
209 209 yield what, self.base[t[1]:t[2]]
210 210 elif what == 'a' or what == 'same':
211 211 yield what, self.a[t[1]:t[2]]
212 212 elif what == 'b':
213 213 yield what, self.b[t[1]:t[2]]
214 214 elif what == 'conflict':
215 215 yield (what,
216 216 self.base[t[1]:t[2]],
217 217 self.a[t[3]:t[4]],
218 218 self.b[t[5]:t[6]])
219 219 else:
220 220 raise ValueError(what)
221 221
222 222
223 223 def merge_regions(self):
224 224 """Return sequences of matching and conflicting regions.
225 225
226 226 This returns tuples, where the first value says what kind we
227 227 have:
228 228
229 229 'unchanged', start, end
230 230 Take a region of base[start:end]
231 231
232 232 'same', astart, aend
233 233 b and a are different from base but give the same result
234 234
235 235 'a', start, end
236 236 Non-clashing insertion from a[start:end]
237 237
238 238 Method is as follows:
239 239
240 240 The two sequences align only on regions which match the base
241 241 and both descendents. These are found by doing a two-way diff
242 242 of each one against the base, and then finding the
243 243 intersections between those regions. These "sync regions"
244 244 are by definition unchanged in both and easily dealt with.
245 245
246 246 The regions in between can be in any of three cases:
247 247 conflicted, or changed on only one side.
248 248 """
249 249
250 250 # section a[0:ia] has been disposed of, etc
251 251 iz = ia = ib = 0
252
252
253 253 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
254 254 #print 'match base [%d:%d]' % (zmatch, zend)
255
255
256 256 matchlen = zend - zmatch
257 257 assert matchlen >= 0
258 258 assert matchlen == (aend - amatch)
259 259 assert matchlen == (bend - bmatch)
260
260
261 261 len_a = amatch - ia
262 262 len_b = bmatch - ib
263 263 len_base = zmatch - iz
264 264 assert len_a >= 0
265 265 assert len_b >= 0
266 266 assert len_base >= 0
267 267
268 268 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
269 269
270 270 if len_a or len_b:
271 271 # try to avoid actually slicing the lists
272 272 equal_a = compare_range(self.a, ia, amatch,
273 273 self.base, iz, zmatch)
274 274 equal_b = compare_range(self.b, ib, bmatch,
275 275 self.base, iz, zmatch)
276 276 same = compare_range(self.a, ia, amatch,
277 277 self.b, ib, bmatch)
278 278
279 279 if same:
280 280 yield 'same', ia, amatch
281 281 elif equal_a and not equal_b:
282 282 yield 'b', ib, bmatch
283 283 elif equal_b and not equal_a:
284 284 yield 'a', ia, amatch
285 285 elif not equal_a and not equal_b:
286 286 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
287 287 else:
288 288 raise AssertionError("can't handle a=b=base but unmatched")
289 289
290 290 ia = amatch
291 291 ib = bmatch
292 292 iz = zmatch
293 293
294 294 # if the same part of the base was deleted on both sides
295 295 # that's OK, we can just skip it.
296 296
297
297
298 298 if matchlen > 0:
299 299 assert ia == amatch
300 300 assert ib == bmatch
301 301 assert iz == zmatch
302
302
303 303 yield 'unchanged', zmatch, zend
304 304 iz = zend
305 305 ia = aend
306 306 ib = bend
307
307
308 308
309 309 def reprocess_merge_regions(self, merge_regions):
310 310 """Where there are conflict regions, remove the agreed lines.
311 311
312 Lines where both A and B have made the same changes are
312 Lines where both A and B have made the same changes are
313 313 eliminated.
314 314 """
315 315 for region in merge_regions:
316 316 if region[0] != "conflict":
317 317 yield region
318 318 continue
319 319 type, iz, zmatch, ia, amatch, ib, bmatch = region
320 320 a_region = self.a[ia:amatch]
321 321 b_region = self.b[ib:bmatch]
322 322 matches = mdiff.get_matching_blocks(''.join(a_region),
323 323 ''.join(b_region))
324 324 next_a = ia
325 325 next_b = ib
326 326 for region_ia, region_ib, region_len in matches[:-1]:
327 327 region_ia += ia
328 328 region_ib += ib
329 329 reg = self.mismatch_region(next_a, region_ia, next_b,
330 330 region_ib)
331 331 if reg is not None:
332 332 yield reg
333 333 yield 'same', region_ia, region_len+region_ia
334 334 next_a = region_ia + region_len
335 335 next_b = region_ib + region_len
336 336 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
337 337 if reg is not None:
338 338 yield reg
339 339
340 340
341 341 def mismatch_region(next_a, region_ia, next_b, region_ib):
342 342 if next_a < region_ia or next_b < region_ib:
343 343 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
344 344 mismatch_region = staticmethod(mismatch_region)
345
345
346 346
347 347 def find_sync_regions(self):
348 348 """Return a list of sync regions, where both descendents match the base.
349 349
350 350 Generates a list of (base1, base2, a1, a2, b1, b2). There is
351 351 always a zero-length sync region at the end of all the files.
352 352 """
353 353
354 354 ia = ib = 0
355 355 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
356 356 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
357 357 len_a = len(amatches)
358 358 len_b = len(bmatches)
359 359
360 360 sl = []
361 361
362 362 while ia < len_a and ib < len_b:
363 363 abase, amatch, alen = amatches[ia]
364 364 bbase, bmatch, blen = bmatches[ib]
365 365
366 366 # there is an unconflicted block at i; how long does it
367 367 # extend? until whichever one ends earlier.
368 368 i = intersect((abase, abase+alen), (bbase, bbase+blen))
369 369 if i:
370 370 intbase = i[0]
371 371 intend = i[1]
372 372 intlen = intend - intbase
373 373
374 374 # found a match of base[i[0], i[1]]; this may be less than
375 375 # the region that matches in either one
376 376 assert intlen <= alen
377 377 assert intlen <= blen
378 378 assert abase <= intbase
379 379 assert bbase <= intbase
380 380
381 381 asub = amatch + (intbase - abase)
382 382 bsub = bmatch + (intbase - bbase)
383 383 aend = asub + intlen
384 384 bend = bsub + intlen
385 385
386 386 assert self.base[intbase:intend] == self.a[asub:aend], \
387 387 (self.base[intbase:intend], self.a[asub:aend])
388 388
389 389 assert self.base[intbase:intend] == self.b[bsub:bend]
390 390
391 391 sl.append((intbase, intend,
392 392 asub, aend,
393 393 bsub, bend))
394 394
395 395 # advance whichever one ends first in the base text
396 396 if (abase + alen) < (bbase + blen):
397 397 ia += 1
398 398 else:
399 399 ib += 1
400
400
401 401 intbase = len(self.base)
402 402 abase = len(self.a)
403 403 bbase = len(self.b)
404 404 sl.append((intbase, intbase, abase, abase, bbase, bbase))
405 405
406 406 return sl
407 407
408 408
409 409
410 410 def find_unconflicted(self):
411 411 """Return a list of ranges in base that are not conflicted."""
412 412 am = mdiff.get_matching_blocks(self.basetext, self.atext)
413 413 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
414 414
415 415 unc = []
416 416
417 417 while am and bm:
418 418 # there is an unconflicted block at i; how long does it
419 419 # extend? until whichever one ends earlier.
420 420 a1 = am[0][0]
421 421 a2 = a1 + am[0][2]
422 422 b1 = bm[0][0]
423 423 b2 = b1 + bm[0][2]
424 424 i = intersect((a1, a2), (b1, b2))
425 425 if i:
426 426 unc.append(i)
427 427
428 428 if a2 < b2:
429 429 del am[0]
430 430 else:
431 431 del bm[0]
432
432
433 433 return unc
434 434
435 435
436 436 # bzr compatible interface, for the tests
437 437 class Merge3(Merge3Text):
438 438 """3-way merge of texts.
439 439
440 440 Given BASE, OTHER, THIS, tries to produce a combined text
441 441 incorporating the changes from both BASE->OTHER and BASE->THIS.
442 442 All three will typically be sequences of lines."""
443 443 def __init__(self, base, a, b):
444 444 basetext = '\n'.join([i.strip('\n') for i in base] + [''])
445 445 atext = '\n'.join([i.strip('\n') for i in a] + [''])
446 446 btext = '\n'.join([i.strip('\n') for i in b] + [''])
447 447 if util.binary(basetext) or util.binary(atext) or util.binary(btext):
448 448 raise util.Abort(_("don't know how to merge binary files"))
449 449 Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
450 450
451 451
452 452 def simplemerge(local, base, other, **opts):
453 453 def readfile(filename):
454 454 f = open(filename, "rb")
455 455 text = f.read()
456 456 f.close()
457 457 if util.binary(text):
458 458 msg = _("%s looks like a binary file.") % filename
459 459 if not opts.get('text'):
460 460 raise util.Abort(msg)
461 461 elif not opts.get('quiet'):
462 462 warn(_('warning: %s\n') % msg)
463 463 return text
464 464
465 465 name_a = local
466 466 name_b = other
467 467 labels = opts.get('label', [])
468 468 if labels:
469 469 name_a = labels.pop(0)
470 470 if labels:
471 471 name_b = labels.pop(0)
472 472 if labels:
473 473 raise util.Abort(_("can only specify two labels."))
474 474
475 475 localtext = readfile(local)
476 476 basetext = readfile(base)
477 477 othertext = readfile(other)
478 478
479 479 orig = local
480 480 local = os.path.realpath(local)
481 481 if not opts.get('print'):
482 482 opener = util.opener(os.path.dirname(local))
483 483 out = opener(os.path.basename(local), "w", atomictemp=True)
484 484 else:
485 485 out = sys.stdout
486 486
487 487 reprocess = not opts.get('no_minimal')
488 488
489 489 m3 = Merge3Text(basetext, localtext, othertext)
490 490 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
491 491 reprocess=reprocess):
492 492 out.write(line)
493 493
494 494 if not opts.get('print'):
495 495 out.rename()
496 496
497 497 if m3.conflicts:
498 498 if not opts.get('quiet'):
499 499 warn(_("warning: conflicts during merge.\n"))
500 500 return 1
501 501
502 502 options = [('L', 'label', [], _('labels to use on conflict markers')),
503 503 ('a', 'text', None, _('treat all files as text')),
504 504 ('p', 'print', None,
505 505 _('print results instead of overwriting LOCAL')),
506 506 ('', 'no-minimal', None,
507 507 _('do not try to minimize conflict regions')),
508 508 ('h', 'help', None, _('display help and exit')),
509 509 ('q', 'quiet', None, _('suppress output'))]
510 510
511 511 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER
512 512
513 513 Simple three-way file merge utility with a minimal feature set.
514
514
515 515 Apply to LOCAL the changes necessary to go from BASE to OTHER.
516
516
517 517 By default, LOCAL is overwritten with the results of this operation.
518 518 ''')
519 519
520 520 def showhelp():
521 521 sys.stdout.write(usage)
522 522 sys.stdout.write('\noptions:\n')
523 523
524 524 out_opts = []
525 525 for shortopt, longopt, default, desc in options:
526 526 out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
527 527 longopt and ' --%s' % longopt),
528 528 '%s' % desc))
529 529 opts_len = max([len(opt[0]) for opt in out_opts])
530 530 for first, second in out_opts:
531 531 sys.stdout.write(' %-*s %s\n' % (opts_len, first, second))
532 532
533 533 class ParseError(Exception):
534 534 """Exception raised on errors in parsing the command line."""
535 535
536 536 def main(argv):
537 537 try:
538 538 opts = {}
539 539 try:
540 540 args = fancyopts.fancyopts(argv[1:], options, opts)
541 541 except fancyopts.getopt.GetoptError, e:
542 542 raise ParseError(e)
543 543 if opts['help']:
544 544 showhelp()
545 545 return 0
546 546 if len(args) != 3:
547 547 raise ParseError(_('wrong number of arguments'))
548 548 return simplemerge(*args, **opts)
549 549 except ParseError, e:
550 550 sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
551 551 showhelp()
552 552 return 1
553 553 except util.Abort, e:
554 554 sys.stderr.write("abort: %s\n" % e)
555 555 return 255
556 556 except KeyboardInterrupt:
557 557 return 255
558 558
559 559 if __name__ == '__main__':
560 560 import sys
561 561 import os
562 562 sys.exit(main(sys.argv))
@@ -1,93 +1,93 b''
1 1 " vim600: set foldmethod=marker:
2 2 " =============================================================================
3 3 " Name Of File: hg-menu.vim
4 4 " Description: Interface to Mercurial Version Control.
5 5 " Author: Steve Borho (modified Jeff Lanzarotta's RCS script)
6 6 " Date: Wednesday, October 5, 2005
7 7 " Version: 0.1.0
8 8 " Copyright: None.
9 9 " Usage: These command and gui menu displays useful hg functions
10 10 " Configuration: Your hg executable must be in your path.
11 11 " =============================================================================
12
12
13 13 " Section: Init {{{1
14 14 if exists("loaded_hg_menu")
15 15 finish
16 16 endif
17 17 let loaded_hg_menu = 1
18 18
19 19 " Section: Menu Options {{{1
20 20 if has("gui")
21 21 " amenu H&G.Commit\ File<Tab>,ci :!hg commit %<CR>:e!<CR>
22 22 " amenu H&G.Commit\ All<Tab>,call :!hg commit<CR>:e!<CR>
23 23 " amenu H&G.-SEP1- <nul>
24 24 amenu H&G.Add<Tab>\\add :!hg add %<CR><CR>
25 25 amenu H&G.Forget\ Add<Tab>\\fgt :!hg forget %<CR><CR>
26 26 amenu H&G.Show\ Differences<Tab>\\diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
27 27 amenu H&G.Revert\ to\ Last\ Version<Tab>\\revert :!hg revert %<CR>:e!<CR>
28 28 amenu H&G.Show\ History<Tab>\\log :call ShowResults("FileLog", "hg\ log")<CR><CR>
29 29 amenu H&G.Annotate<Tab>\\an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
30 30 amenu H&G.-SEP1- <nul>
31 31 amenu H&G.Repo\ Status<Tab>\\stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
32 32 amenu H&G.Pull<Tab>\\pull :!hg pull<CR>:e!<CR>
33 33 amenu H&G.Update<Tab>\\upd :!hg update<CR>:e!<CR>
34 34 endif
35 35
36 36 " Section: Mappings {{{1
37 37 if(v:version >= 600)
38 38 " The default Leader is \ 'backslash'
39 39 map <Leader>add :!hg add %<CR><CR>
40 40 map <Leader>fgt :!hg forget %<CR><CR>
41 41 map <Leader>diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
42 42 map <Leader>revert :!hg revert %<CR>:e!<CR>
43 43 map <Leader>log :call ShowResults("FileLog", "hg\ log")<CR><CR>
44 44 map <Leader>an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
45 45 map <Leader>stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
46 46 map <Leader>upd :!hg update<CR>:e!<CR>
47 47 map <Leader>pull :!hg pull<CR>:e!<CR>
48 48 else
49 49 " pre 6.0, the default Leader was a comma
50 50 map ,add :!hg add %<CR><CR>
51 51 map ,fgt :!hg forget %<CR><CR>
52 52 map ,diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
53 53 map ,revert :!hg revert<CR>:e!<CR>
54 54 map ,log :call ShowResults("FileLog", "hg\ log")<CR><CR>
55 55 map ,an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
56 56 map ,stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
57 57 map ,upd :!hg update<CR>:e!<CR>
58 58 map ,pull :!hg pull<CR>:e!<CR>
59 59 endif
60 60
61 61 " Section: Functions {{{1
62 62 " Show the log results of the current file with a revision control system.
63 63 function! ShowResults(bufferName, cmdName)
64 64 " Modify the shortmess option:
65 65 " A don't give the "ATTENTION" message when an existing swap file is
66 66 " found.
67 67 set shortmess+=A
68 68
69 69 " Get the name of the current buffer.
70 70 let currentBuffer = bufname("%")
71 71
72 72 " If a buffer with the name rlog exists, delete it.
73 73 if bufexists(a:bufferName)
74 74 execute 'bd! ' a:bufferName
75 75 endif
76 76
77 77 " Create a new buffer.
78 78 execute 'new ' a:bufferName
79 79
80 80 " Execute the command.
81 81 execute 'r!' a:cmdName ' ' currentBuffer
82 82
83 83 " Make is so that the file can't be edited.
84 84 setlocal nomodified
85 85 setlocal nomodifiable
86 86 setlocal readonly
87 87
88 88 " Go to the beginning of the buffer.
89 89 execute "normal 1G"
90 90
91 91 " Restore the shortmess option.
92 92 set shortmess-=A
93 93 endfunction
@@ -1,112 +1,112 b''
1 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 2 <html>
3 3 <head>
4 4 <title>Mercurial for Windows</title>
5 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
5 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
6 6 <style type="text/css">
7 7 <!--
8 8 .indented
9 9 {
10 10 padding-left: 10pt;
11 11 }
12 12 -->
13 13 </style>
14 14 </head>
15 15
16 16 <body>
17 17 <h1>Mercurial for Windows</h1>
18 18
19 19 <p>Welcome to Mercurial for Windows!</p>
20 20
21 21 <p>Mercurial is a command-line application. You must run it from
22 22 the Windows command prompt (or if you're hard core, a <a
23 23 href="http://www.mingw.org/">MinGW</a> shell).</p>
24
24
25 25 <p><div class="indented"><i>Note: the standard <a
26 26 href="http://www.mingw.org/">MinGW</a> msys startup script uses
27 27 rxvt which has problems setting up standard input and output.
28 28 Running bash directly works correctly.</i></div>
29 29
30 30 <p>For documentation, please visit the <a
31 31 href="http://www.selenic.com/mercurial">Mercurial web site</a>.
32 32 You can also download a free book, <a
33 33 href="http://hgbook.red-bean.com/">Distributed revision control
34 34 with Mercurial</a>.</p>
35 35
36 36 <p>By default, Mercurial installs to <tt>C:\Mercurial</tt>. The
37 37 Mercurial command is called <tt>hg.exe</tt>.</p>
38 38
39 39 <h1>Testing Mercurial after you've installed it</h1>
40 40
41 41 <p>The easiest way to check that Mercurial is installed properly is to
42 42 just type the following at the command prompt:</p>
43 43
44 44 <pre>
45 45 hg
46 46 </pre>
47 47
48 48 <p>This command should print a useful help message. If it does,
49 49 other Mercurial commands should work fine for you.</p>
50 50
51 51 <h1>Configuration notes</h1>
52 52 <h4>Default editor</h4>
53 53 The default editor for commit messages is 'notepad'. You can set the EDITOR
54 54 (or HGEDITOR) environment variable to specify your preference or set it in
55 55 mercurial.ini:
56 56 <pre>
57 57 [ui]
58 58 editor = whatever
59 59 </pre>
60 60
61 61 <h4>Configuring a Merge program</h4>
62 It should be emphasized that Mercurial by itself doesn't attempt to do a
62 It should be emphasized that Mercurial by itself doesn't attempt to do a
63 63 Merge at the file level, neither does it make any attempt to Resolve the conflicts.
64 64
65 By default, Mercurial will use the merge program defined by the HGMERGE environment
65 By default, Mercurial will use the merge program defined by the HGMERGE environment
66 66 variable, or uses the one defined in the mercurial.ini file. (see <a href="http://www.selenic.com/mercurial/wiki/index.cgi/MergeProgram">MergeProgram</a> on the Mercurial Wiki for more information)
67 67
68 68 <h1>Reporting problems</h1>
69 69
70 70 <p>Before you report any problems, please consult the <a
71 71 href="http://www.selenic.com/mercurial">Mercurial web site</a> and
72 72 see if your question is already in our list of <a
73 73 href="http://www.selenic.com/mercurial/wiki/index.cgi/FAQ">Frequently
74 74 Answered Questions</a> (the "FAQ").
75 75
76 76 <p>If you cannot find an answer to your question, please feel
77 77 free to send mail to the Mercurial mailing list, at <a
78 78 href="mailto:mercurial@selenic.com">mercurial@selenic.com</a>.
79 79 <b>Remember</b>, the more useful information you include in your
80 80 report, the easier it will be for us to help you!</p>
81 81
82 82 <p>If you are IRC-savvy, that's usually the fastest way to get
83 83 help. Go to <tt>#mercurial</tt> on
84 84 <tt>irc.freenode.net</tt>.</p>
85 85
86 86 <h1>Author and copyright information</h1>
87 87
88 88 <p>Mercurial was written by <a href="http://www.selenic.com">Matt
89 89 Mackall</a>, and is maintained by Matt and a team of
90 90 volunteers.</p>
91 91
92 92 <p>The Windows installer was written by <a
93 93 href="http://www.serpentine.com/blog">Bryan
94 94 O'Sullivan</a>.</p>
95 95
96 96 <p>Mercurial is Copyright 2005-2007 Matt Mackall and others.
97 97 See the <tt>Contributors.txt</tt> file for a list of contributors.</p>
98 98
99 99 <p>Mercurial is free software; you can redistribute it and/or
100 100 modify it under the terms of the <a
101 101 href="http://www.gnu.org/copyleft/gpl.html">GNU General Public
102 102 License</a> as published by the Free Software Foundation; either
103 103 version 2 of the License, or (at your option) any later
104 104 version.</p>
105 105
106 106 <p>Mercurial is distributed in the hope that it will be useful,
107 107 but <b>without any warranty</b>; without even the implied
108 108 warranty of <b>merchantability</b> or <b>fitness for a
109 109 particular purpose</b>. See the GNU General Public License for
110 110 more details.</p>
111 111 </body>
112 112 </html>
@@ -1,41 +1,41 b''
1 1 ; System-wide Mercurial config file. To override these settings on a
2 2 ; per-user basis, please edit the following file instead, where
3 3 ; USERNAME is your Windows user name:
4 4 ; C:\Documents and Settings\USERNAME\Mercurial.ini
5 5
6 [ui]
6 [ui]
7 7 editor = notepad
8 8
9 9 ; By default, we try to encode and decode all files that do not
10 10 ; contain ASCII NUL characters. What this means is that we try to set
11 11 ; line endings to Windows style on update, and to Unix style on
12 12 ; commit. This lets us cooperate with Linux and Unix users, so
13 13 ; everybody sees files with their native line endings.
14 14
15 15 [extensions]
16 16 ; The win32text extension is available and installed by default. It
17 17 ; provides built-in Python hooks to perform line ending conversions.
18 18 ; This is normally much faster than running an external program.
19 19 hgext.win32text =
20 20
21 21
22 22 [encode]
23 23 ; Encode files that don't contain NUL characters.
24 24
25 25 ; ** = cleverencode:
26 26
27 27 ; Alternatively, you can explicitly specify each file extension that
28 28 ; you want encoded (any you omit will be left untouched), like this:
29 29
30 30 ; *.txt = dumbencode:
31 31
32 32
33 33 [decode]
34 34 ; Decode files that don't contain NUL characters.
35 35
36 36 ; ** = cleverdecode:
37 37
38 38 ; Alternatively, you can explicitly specify each file extension that
39 39 ; you want decoded (any you omit will be left untouched), like this:
40 40
41 41 ; **.txt = dumbdecode:
@@ -1,71 +1,71 b''
1 1 The standalone Windows installer for Mercurial is built in a somewhat
2 2 jury-rigged fashion.
3 3
4 4 It has the following prerequisites, at least as I build it:
5 5
6 6 Python for Windows
7 7 http://www.python.org/ftp/python/2.4.3/python-2.4.3.msi
8 8
9 9 MinGW
10 10 http://www.mingw.org/
11 11
12 12 Python for Windows Extensions
13 13 http://sourceforge.net/projects/pywin32/
14 14
15 15 mfc71.dll (just download, don't install)
16 16 http://starship.python.net/crew/mhammond/win32/
17 17
18 18 The py2exe distutils extension
19 19 http://sourceforge.net/projects/py2exe/
20 20
21 21 Inno Setup
22 22 http://www.jrsoftware.org/isinfo.php
23 23
24 24 ISTool - optional
25 25 http://www.istool.org/default.aspx/
26 26
27 27 add_path (you need only add_path.exe in the zip file)
28 28 http://www.barisione.org/apps.html#add_path
29 29
30 30 And, of course, Mercurial itself.
31 31
32 32 Once you have all this installed and built, clone a copy of the
33 33 Mercurial repository you want to package, and name the repo
34 34 C:\hg\hg-release.
35 35
36 36 In a shell, build a standalone copy of the hg.exe program:
37 37
38 python setup.py build -c mingw32
38 python setup.py build -c mingw32
39 39 python setup.py py2exe -b 1
40 40
41 41 Note: the previously suggested combined command of "python setup.py build -c
42 42 mingw32 py2exe -b 1" doesn't work correctly anymore as it doesn't include the
43 43 extensions in the mercurial subdirectory.
44 44
45 45 If you want to create a file named setup.cfg with the contents:
46 46
47 47 [build]
48 48 compiler=mingw32
49 49
50 50 you can skip the first build step.
51 51
52 52 Copy mfc71.dll and add_path.exe into the dist directory that just got created.
53 53
54 54 If you use ISTool, you open the C:\hg\hg-release\contrib\win32\mercurial.iss
55 55 file and type Ctrl-F9 to compile the installer file.
56 56
57 57 Otherwise you run the Inno Setup compiler. Assuming it's on the path you run:
58 58
59 59 iscc contrib\win32\mercurial.iss
60 60
61 61 The actual installer will be in the C:\hg\hg-release\Output directory.
62 62
63 63 To automate the steps above you may want to create a batchfile based on the
64 64 following:
65 65
66 66 echo [build] > setup.cfg
67 67 echo compiler=mingw32 >> setup.cfg
68 68 python setup.py py2exe -b 1
69 69 iscc contrib\win32\mercurial.iss
70 70
71 71 and run it from the root of the hg repository (c:\hg\hg-release).
@@ -1,579 +1,579 b''
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 NAME
6 6 ----
7 7 hgrc - configuration files for Mercurial
8 8
9 9 SYNOPSIS
10 10 --------
11 11
12 12 The Mercurial system uses a set of configuration files to control
13 13 aspects of its behaviour.
14 14
15 15 FILES
16 16 -----
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 The names of these files depend on the system on which Mercurial is
20 20 installed.
21 21
22 22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 23 (Unix) <install-root>/etc/mercurial/hgrc::
24 24 Per-installation configuration files, searched for in the
25 25 directory where Mercurial is installed. For example, if installed
26 26 in /shared/tools, Mercurial will look in
27 27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 28 all Mercurial commands executed by any user in any directory.
29 29
30 30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 31 (Unix) /etc/mercurial/hgrc::
32 32 (Windows) C:\Mercurial\Mercurial.ini::
33 33 Per-system configuration files, for the system on which Mercurial
34 34 is running. Options in these files apply to all Mercurial
35 35 commands executed by any user in any directory. Options in these
36 36 files override per-installation options.
37 37
38 38 (Unix) $HOME/.hgrc::
39 39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
40 40 (Windows) $HOME\Mercurial.ini::
41 41 Per-user configuration file, for the user running Mercurial.
42 42 Options in this file apply to all Mercurial commands executed by
43 43 any user in any directory. Options in this file override
44 44 per-installation and per-system options.
45 45 On Windows system, one of these is chosen exclusively according
46 46 to definition of HOME environment variable.
47 47
48 48 (Unix, Windows) <repo>/.hg/hgrc::
49 49 Per-repository configuration options that only apply in a
50 50 particular repository. This file is not version-controlled, and
51 51 will not get transferred during a "clone" operation. Options in
52 52 this file override options in all other configuration files.
53 53 On Unix, most of this file will be ignored if it doesn't belong
54 54 to a trusted user or to a trusted group. See the documentation
55 55 for the trusted section below for more details.
56 56
57 57 SYNTAX
58 58 ------
59 59
60 60 A configuration file consists of sections, led by a "[section]" header
61 61 and followed by "name: value" entries; "name=value" is also accepted.
62 62
63 63 [spam]
64 64 eggs=ham
65 65 green=
66 66 eggs
67 67
68 68 Each line contains one entry. If the lines that follow are indented,
69 69 they are treated as continuations of that entry.
70 70
71 71 Leading whitespace is removed from values. Empty lines are skipped.
72 72
73 73 The optional values can contain format strings which refer to other
74 74 values in the same section, or values in a special DEFAULT section.
75 75
76 76 Lines beginning with "#" or ";" are ignored and may be used to provide
77 77 comments.
78 78
79 79 SECTIONS
80 80 --------
81 81
82 82 This section describes the different sections that may appear in a
83 83 Mercurial "hgrc" file, the purpose of each section, its possible
84 84 keys, and their possible values.
85 85
86 86 decode/encode::
87 87 Filters for transforming files on checkout/checkin. This would
88 88 typically be used for newline processing or other
89 89 localization/canonicalization of files.
90 90
91 91 Filters consist of a filter pattern followed by a filter command.
92 92 Filter patterns are globs by default, rooted at the repository
93 93 root. For example, to match any file ending in ".txt" in the root
94 94 directory only, use the pattern "*.txt". To match any file ending
95 95 in ".c" anywhere in the repository, use the pattern "**.c".
96 96
97 97 The filter command can start with a specifier, either "pipe:" or
98 98 "tempfile:". If no specifier is given, "pipe:" is used by default.
99 99
100 100 A "pipe:" command must accept data on stdin and return the
101 101 transformed data on stdout.
102 102
103 103 Pipe example:
104 104
105 105 [encode]
106 106 # uncompress gzip files on checkin to improve delta compression
107 107 # note: not necessarily a good idea, just an example
108 108 *.gz = pipe: gunzip
109 109
110 110 [decode]
111 111 # recompress gzip files when writing them to the working dir (we
112 112 # can safely omit "pipe:", because it's the default)
113 113 *.gz = gzip
114 114
115 115 A "tempfile:" command is a template. The string INFILE is replaced
116 116 with the name of a temporary file that contains the data to be
117 117 filtered by the command. The string OUTFILE is replaced with the
118 118 name of an empty temporary file, where the filtered data must be
119 119 written by the command.
120 120
121 121 NOTE: the tempfile mechanism is recommended for Windows systems,
122 122 where the standard shell I/O redirection operators often have
123 123 strange effects. In particular, if you are doing line ending
124 124 conversion on Windows using the popular dos2unix and unix2dos
125 125 programs, you *must* use the tempfile mechanism, as using pipes will
126 126 corrupt the contents of your files.
127 127
128 128 Tempfile example:
129 129
130 130 [encode]
131 131 # convert files to unix line ending conventions on checkin
132 132 **.txt = tempfile: dos2unix -n INFILE OUTFILE
133 133
134 134 [decode]
135 135 # convert files to windows line ending conventions when writing
136 136 # them to the working dir
137 137 **.txt = tempfile: unix2dos -n INFILE OUTFILE
138 138
139 139 defaults::
140 Use the [defaults] section to define command defaults, i.e. the
140 Use the [defaults] section to define command defaults, i.e. the
141 141 default options/arguments to pass to the specified commands.
142
142
143 143 The following example makes 'hg log' run in verbose mode, and
144 144 'hg status' show only the modified files, by default.
145
145
146 146 [defaults]
147 147 log = -v
148 148 status = -m
149
150 The actual commands, instead of their aliases, must be used when
149
150 The actual commands, instead of their aliases, must be used when
151 151 defining command defaults. The command defaults will also be
152 152 applied to the aliases of the commands defined.
153 153
154 154 diff::
155 155 Settings used when displaying diffs. They are all boolean and
156 156 defaults to False.
157 157 git;;
158 158 Use git extended diff format.
159 159 nodates;;
160 160 Don't include dates in diff headers.
161 161 showfunc;;
162 162 Show which function each change is in.
163 163 ignorews;;
164 164 Ignore white space when comparing lines.
165 165 ignorewsamount;;
166 166 Ignore changes in the amount of white space.
167 167 ignoreblanklines;;
168 168 Ignore changes whose lines are all blank.
169 169
170 170 email::
171 171 Settings for extensions that send email messages.
172 172 from;;
173 173 Optional. Email address to use in "From" header and SMTP envelope
174 174 of outgoing messages.
175 175 to;;
176 176 Optional. Comma-separated list of recipients' email addresses.
177 177 cc;;
178 178 Optional. Comma-separated list of carbon copy recipients'
179 179 email addresses.
180 180 bcc;;
181 181 Optional. Comma-separated list of blind carbon copy
182 182 recipients' email addresses. Cannot be set interactively.
183 183 method;;
184 184 Optional. Method to use to send email messages. If value is
185 185 "smtp" (default), use SMTP (see section "[smtp]" for
186 186 configuration). Otherwise, use as name of program to run that
187 187 acts like sendmail (takes "-f" option for sender, list of
188 188 recipients on command line, message on stdin). Normally, setting
189 189 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
190 190 sendmail to send messages.
191 191
192 192 Email example:
193 193
194 194 [email]
195 195 from = Joseph User <joe.user@example.com>
196 196 method = /usr/sbin/sendmail
197 197
198 198 extensions::
199 199 Mercurial has an extension mechanism for adding new features. To
200 200 enable an extension, create an entry for it in this section.
201 201
202 202 If you know that the extension is already in Python's search path,
203 203 you can give the name of the module, followed by "=", with nothing
204 204 after the "=".
205 205
206 206 Otherwise, give a name that you choose, followed by "=", followed by
207 207 the path to the ".py" file (including the file name extension) that
208 208 defines the extension.
209 209
210 210 Example for ~/.hgrc:
211 211
212 212 [extensions]
213 213 # (the mq extension will get loaded from mercurial's path)
214 214 hgext.mq =
215 215 # (this extension will get loaded from the file specified)
216 216 myfeature = ~/.hgext/myfeature.py
217 217
218 218 format::
219 219
220 220 usestore;;
221 221 Enable or disable the "store" repository format which improves
222 222 compatibility with systems that fold case or otherwise mangle
223 223 filenames. Enabled by default. Disabling this option will allow
224 224 you to store longer filenames in some situations at the expense of
225 225 compatibility.
226 226
227 227 hooks::
228 228 Commands or Python functions that get automatically executed by
229 229 various actions such as starting or finishing a commit. Multiple
230 230 hooks can be run for the same action by appending a suffix to the
231 231 action. Overriding a site-wide hook can be done by changing its
232 232 value or setting it to an empty string.
233 233
234 234 Example .hg/hgrc:
235 235
236 236 [hooks]
237 237 # do not use the site-wide hook
238 238 incoming =
239 239 incoming.email = /my/email/hook
240 240 incoming.autobuild = /my/build/hook
241 241
242 242 Most hooks are run with environment variables set that give added
243 243 useful information. For each hook below, the environment variables
244 244 it is passed are listed with names of the form "$HG_foo".
245 245
246 246 changegroup;;
247 247 Run after a changegroup has been added via push, pull or
248 248 unbundle. ID of the first new changeset is in $HG_NODE. URL from
249 249 which changes came is in $HG_URL.
250 250 commit;;
251 251 Run after a changeset has been created in the local repository.
252 252 ID of the newly created changeset is in $HG_NODE. Parent
253 253 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
254 254 incoming;;
255 255 Run after a changeset has been pulled, pushed, or unbundled into
256 256 the local repository. The ID of the newly arrived changeset is in
257 257 $HG_NODE. URL that was source of changes came is in $HG_URL.
258 258 outgoing;;
259 259 Run after sending changes from local repository to another. ID of
260 260 first changeset sent is in $HG_NODE. Source of operation is in
261 261 $HG_SOURCE; see "preoutgoing" hook for description.
262 262 post-<command>;;
263 263 Run after successful invocations of the associated command. The
264 264 contents of the command line are passed as $HG_ARGS and the result
265 265 code in $HG_RESULT. Hook failure is ignored.
266 266 pre-<command>;;
267 267 Run before executing the associated command. The contents of the
268 268 command line are passed as $HG_ARGS. If the hook returns failure,
269 269 the command doesn't execute and Mercurial returns the failure code.
270 270 prechangegroup;;
271 271 Run before a changegroup is added via push, pull or unbundle.
272 272 Exit status 0 allows the changegroup to proceed. Non-zero status
273 273 will cause the push, pull or unbundle to fail. URL from which
274 274 changes will come is in $HG_URL.
275 275 precommit;;
276 276 Run before starting a local commit. Exit status 0 allows the
277 277 commit to proceed. Non-zero status will cause the commit to fail.
278 278 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
279 279 preoutgoing;;
280 280 Run before computing changes to send from the local repository to
281 281 another. Non-zero status will cause failure. This lets you
282 282 prevent pull over http or ssh. Also prevents against local pull,
283 283 push (outbound) or bundle commands, but not effective, since you
284 284 can just copy files instead then. Source of operation is in
285 285 $HG_SOURCE. If "serve", operation is happening on behalf of
286 286 remote ssh or http repository. If "push", "pull" or "bundle",
287 287 operation is happening on behalf of repository on same system.
288 288 pretag;;
289 289 Run before creating a tag. Exit status 0 allows the tag to be
290 290 created. Non-zero status will cause the tag to fail. ID of
291 291 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
292 292 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
293 293 pretxnchangegroup;;
294 294 Run after a changegroup has been added via push, pull or unbundle,
295 295 but before the transaction has been committed. Changegroup is
296 296 visible to hook program. This lets you validate incoming changes
297 297 before accepting them. Passed the ID of the first new changeset
298 298 in $HG_NODE. Exit status 0 allows the transaction to commit.
299 299 Non-zero status will cause the transaction to be rolled back and
300 300 the push, pull or unbundle will fail. URL that was source of
301 301 changes is in $HG_URL.
302 302 pretxncommit;;
303 303 Run after a changeset has been created but the transaction not yet
304 304 committed. Changeset is visible to hook program. This lets you
305 305 validate commit message and changes. Exit status 0 allows the
306 306 commit to proceed. Non-zero status will cause the transaction to
307 307 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
308 308 IDs are in $HG_PARENT1 and $HG_PARENT2.
309 309 preupdate;;
310 310 Run before updating the working directory. Exit status 0 allows
311 311 the update to proceed. Non-zero status will prevent the update.
312 312 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
313 313 of second new parent is in $HG_PARENT2.
314 314 tag;;
315 315 Run after a tag is created. ID of tagged changeset is in
316 316 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
317 317 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
318 318 update;;
319 319 Run after updating the working directory. Changeset ID of first
320 320 new parent is in $HG_PARENT1. If merge, ID of second new parent
321 321 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
322 322 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
323 323
324 324 Note: it is generally better to use standard hooks rather than the
325 325 generic pre- and post- command hooks as they are guaranteed to be
326 326 called in the appropriate contexts for influencing transactions.
327 327 Also, hooks like "commit" will be called in all contexts that
328 328 generate a commit (eg. tag) and not just the commit command.
329 329
330 330 Note2: Environment variables with empty values may not be passed to
331 331 hooks on platforms like Windows. For instance, $HG_PARENT2 will
332 332 not be available under Windows for non-merge changesets while being
333 333 set to an empty value under Unix-like systems.
334 334
335 335 The syntax for Python hooks is as follows:
336 336
337 337 hookname = python:modulename.submodule.callable
338 338
339 339 Python hooks are run within the Mercurial process. Each hook is
340 340 called with at least three keyword arguments: a ui object (keyword
341 341 "ui"), a repository object (keyword "repo"), and a "hooktype"
342 342 keyword that tells what kind of hook is used. Arguments listed as
343 343 environment variables above are passed as keyword arguments, with no
344 344 "HG_" prefix, and names in lower case.
345 345
346 346 If a Python hook returns a "true" value or raises an exception, this
347 347 is treated as failure of the hook.
348 348
349 349 http_proxy::
350 350 Used to access web-based Mercurial repositories through a HTTP
351 351 proxy.
352 352 host;;
353 353 Host name and (optional) port of the proxy server, for example
354 354 "myproxy:8000".
355 355 no;;
356 356 Optional. Comma-separated list of host names that should bypass
357 357 the proxy.
358 358 passwd;;
359 359 Optional. Password to authenticate with at the proxy server.
360 360 user;;
361 361 Optional. User name to authenticate with at the proxy server.
362 362
363 363 smtp::
364 364 Configuration for extensions that need to send email messages.
365 365 host;;
366 366 Host name of mail server, e.g. "mail.example.com".
367 367 port;;
368 368 Optional. Port to connect to on mail server. Default: 25.
369 369 tls;;
370 370 Optional. Whether to connect to mail server using TLS. True or
371 371 False. Default: False.
372 372 username;;
373 373 Optional. User name to authenticate to SMTP server with.
374 374 If username is specified, password must also be specified.
375 375 Default: none.
376 376 password;;
377 377 Optional. Password to authenticate to SMTP server with.
378 378 If username is specified, password must also be specified.
379 379 Default: none.
380 380 local_hostname;;
381 381 Optional. It's the hostname that the sender can use to identify itself
382 382 to the MTA.
383 383
384 384 paths::
385 385 Assigns symbolic names to repositories. The left side is the
386 386 symbolic name, and the right gives the directory or URL that is the
387 387 location of the repository. Default paths can be declared by
388 388 setting the following entries.
389 389 default;;
390 390 Directory or URL to use when pulling if no source is specified.
391 391 Default is set to repository from which the current repository
392 392 was cloned.
393 393 default-push;;
394 394 Optional. Directory or URL to use when pushing if no destination
395 395 is specified.
396 396
397 397 server::
398 398 Controls generic server settings.
399 399 uncompressed;;
400 400 Whether to allow clients to clone a repo using the uncompressed
401 401 streaming protocol. This transfers about 40% more data than a
402 402 regular clone, but uses less memory and CPU on both server and
403 403 client. Over a LAN (100Mbps or better) or a very fast WAN, an
404 404 uncompressed streaming clone is a lot faster (~10x) than a regular
405 405 clone. Over most WAN connections (anything slower than about
406 406 6Mbps), uncompressed streaming is slower, because of the extra
407 407 data transfer overhead. Default is False.
408 408
409 409 trusted::
410 410 For security reasons, Mercurial will not use the settings in
411 411 the .hg/hgrc file from a repository if it doesn't belong to a
412 412 trusted user or to a trusted group. The main exception is the
413 413 web interface, which automatically uses some safe settings, since
414 414 it's common to serve repositories from different users.
415 415
416 416 This section specifies what users and groups are trusted. The
417 417 current user is always trusted. To trust everybody, list a user
418 418 or a group with name "*".
419 419
420 420 users;;
421 421 Comma-separated list of trusted users.
422 422 groups;;
423 423 Comma-separated list of trusted groups.
424 424
425 425 ui::
426 426 User interface controls.
427 427 debug;;
428 428 Print debugging information. True or False. Default is False.
429 429 editor;;
430 430 The editor to use during a commit. Default is $EDITOR or "vi".
431 431 fallbackencoding;;
432 432 Encoding to try if it's not possible to decode the changelog using
433 433 UTF-8. Default is ISO-8859-1.
434 434 ignore;;
435 435 A file to read per-user ignore patterns from. This file should be in
436 436 the same format as a repository-wide .hgignore file. This option
437 437 supports hook syntax, so if you want to specify multiple ignore
438 438 files, you can do so by setting something like
439 439 "ignore.other = ~/.hgignore2". For details of the ignore file
440 440 format, see the hgignore(5) man page.
441 441 interactive;;
442 442 Allow to prompt the user. True or False. Default is True.
443 443 logtemplate;;
444 444 Template string for commands that print changesets.
445 445 merge;;
446 446 The conflict resolution program to use during a manual merge.
447 447 Default is "hgmerge".
448 448 patch;;
449 449 command to use to apply patches. Look for 'gpatch' or 'patch' in PATH if
450 450 unset.
451 451 quiet;;
452 452 Reduce the amount of output printed. True or False. Default is False.
453 453 remotecmd;;
454 454 remote command to use for clone/push/pull operations. Default is 'hg'.
455 455 report_untrusted;;
456 456 Warn if a .hg/hgrc file is ignored due to not being owned by a
457 457 trusted user or group. True or False. Default is True.
458 458 slash;;
459 459 Display paths using a slash ("/") as the path separator. This only
460 460 makes a difference on systems where the default path separator is not
461 461 the slash character (e.g. Windows uses the backslash character ("\")).
462 462 Default is False.
463 463 ssh;;
464 464 command to use for SSH connections. Default is 'ssh'.
465 465 strict;;
466 466 Require exact command names, instead of allowing unambiguous
467 467 abbreviations. True or False. Default is False.
468 468 style;;
469 469 Name of style to use for command output.
470 470 timeout;;
471 471 The timeout used when a lock is held (in seconds), a negative value
472 472 means no timeout. Default is 600.
473 473 username;;
474 474 The committer of a changeset created when running "commit".
475 475 Typically a person's name and email address, e.g. "Fred Widget
476 476 <fred@example.com>". Default is $EMAIL or username@hostname.
477 477 If the username in hgrc is empty, it has to be specified manually or
478 478 in a different hgrc file (e.g. $HOME/.hgrc, if the admin set "username ="
479 479 in the system hgrc).
480 480 verbose;;
481 481 Increase the amount of output printed. True or False. Default is False.
482 482
483 483
484 484 web::
485 485 Web interface configuration.
486 486 accesslog;;
487 487 Where to output the access log. Default is stdout.
488 488 address;;
489 489 Interface address to bind to. Default is all.
490 490 allow_archive;;
491 491 List of archive format (bz2, gz, zip) allowed for downloading.
492 492 Default is empty.
493 493 allowbz2;;
494 494 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
495 495 Default is false.
496 496 allowgz;;
497 497 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
498 498 Default is false.
499 499 allowpull;;
500 500 Whether to allow pulling from the repository. Default is true.
501 501 allow_push;;
502 502 Whether to allow pushing to the repository. If empty or not set,
503 503 push is not allowed. If the special value "*", any remote user
504 504 can push, including unauthenticated users. Otherwise, the remote
505 505 user must have been authenticated, and the authenticated user name
506 506 must be present in this list (separated by whitespace or ",").
507 507 The contents of the allow_push list are examined after the
508 508 deny_push list.
509 509 allowzip;;
510 510 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
511 511 Default is false. This feature creates temporary files.
512 512 baseurl;;
513 513 Base URL to use when publishing URLs in other locations, so
514 514 third-party tools like email notification hooks can construct URLs.
515 515 Example: "http://hgserver/repos/"
516 516 contact;;
517 517 Name or email address of the person in charge of the repository.
518 518 Default is "unknown".
519 519 deny_push;;
520 520 Whether to deny pushing to the repository. If empty or not set,
521 521 push is not denied. If the special value "*", all remote users
522 522 are denied push. Otherwise, unauthenticated users are all denied,
523 523 and any authenticated user name present in this list (separated by
524 524 whitespace or ",") is also denied. The contents of the deny_push
525 525 list are examined before the allow_push list.
526 526 description;;
527 527 Textual description of the repository's purpose or contents.
528 528 Default is "unknown".
529 529 encoding;;
530 530 Character encoding name.
531 531 Example: "UTF-8"
532 532 errorlog;;
533 533 Where to output the error log. Default is stderr.
534 534 hidden;;
535 535 Whether to hide the repository in the hgwebdir index. Default is false.
536 536 ipv6;;
537 537 Whether to use IPv6. Default is false.
538 538 name;;
539 539 Repository name to use in the web interface. Default is current
540 540 working directory.
541 541 maxchanges;;
542 542 Maximum number of changes to list on the changelog. Default is 10.
543 543 maxfiles;;
544 544 Maximum number of files to list per changeset. Default is 10.
545 545 port;;
546 546 Port to listen on. Default is 8000.
547 547 push_ssl;;
548 548 Whether to require that inbound pushes be transported over SSL to
549 549 prevent password sniffing. Default is true.
550 550 staticurl;;
551 551 Base URL to use for static files. If unset, static files (e.g.
552 552 the hgicon.png favicon) will be served by the CGI script itself.
553 553 Use this setting to serve them directly with the HTTP server.
554 554 Example: "http://hgserver/static/"
555 555 stripes;;
556 556 How many lines a "zebra stripe" should span in multiline output.
557 557 Default is 1; set to 0 to disable.
558 558 style;;
559 559 Which template map style to use.
560 560 templates;;
561 561 Where to find the HTML templates. Default is install path.
562 562
563 563
564 564 AUTHOR
565 565 ------
566 566 Bryan O'Sullivan <bos@serpentine.com>.
567 567
568 568 Mercurial was written by Matt Mackall <mpm@selenic.com>.
569 569
570 570 SEE ALSO
571 571 --------
572 572 hg(1), hgignore(5)
573 573
574 574 COPYING
575 575 -------
576 576 This manual page is copyright 2005 Bryan O'Sullivan.
577 577 Mercurial is copyright 2005-2007 Matt Mackall.
578 578 Free use of this software is granted under the terms of the GNU General
579 579 Public License (GPL).
@@ -1,583 +1,583 b''
1 1 #
2 2 # docbook.conf
3 3 #
4 4 # Asciidoc configuration file.
5 5 # Modified docbook backend for Japanese.
6 6 #
7 7
8 8 [miscellaneous]
9 9 outfilesuffix=.xml
10 10 # Printable page width in pts.
11 11 pagewidth=380
12 12 pageunits=pt
13 13
14 14 [attributes]
15 15 basebackend=docbook
16 16 basebackend-docbook=
17 17
18 18 [replacements]
19 19 # Line break markup is dropped (there is no DocBook line break tag).
20 20 (?m)^(.*)\s\+$=\1
21 21 # Superscripts.
22 22 \^(.+?)\^=<superscript>\1</superscript>
23 23 # Subscripts.
24 24 ~(.+?)~=<subscript>\1</subscript>
25 25
26 26 [ruler-blockmacro]
27 27 # Only applies to HTML so don't output anything.
28 28
29 29 [image-inlinemacro]
30 30 <inlinemediaobject>
31 31 <imageobject>
32 32 <imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
33 33 </imageobject>
34 34 <textobject><phrase>{1={target}}</phrase></textobject>
35 35 </inlinemediaobject>
36 36
37 37 [image-blockmacro]
38 38 <figure{id? id="{id}"}><title>{title}</title>
39 39 {title%}<informalfigure{id? id="{id}"}>
40 40 <mediaobject>
41 41 <imageobject>
42 42 <imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
43 43 </imageobject>
44 44 <textobject><phrase>{1={target}}</phrase></textobject>
45 45 </mediaobject>
46 46 {title#}</figure>
47 47 {title%}</informalfigure>
48 48
49 49 [indexterm-inlinemacro]
50 50 # Inline index term.
51 51 # Generate separate index entries for primary, secondary and tertiary
52 52 # descriptions.
53 53 # Primary only.
54 54 {2%}<indexterm>
55 55 {2%} <primary>{1}</primary>
56 56 {2%}</indexterm>
57 57 # Primary and secondary.
58 58 {2#}{3%}<indexterm>
59 59 {2#}{3%} <primary>{1}</primary><secondary>{2}</secondary>
60 60 {2#}{3%}</indexterm>
61 61 {2#}{3%}<indexterm>
62 62 {2#}{3%} <primary>{2}</primary>
63 63 {2#}{3%}</indexterm>
64 64 # Primary, secondary and tertiary.
65 65 {3#}<indexterm>
66 66 <primary>{1}</primary><secondary>{2}</secondary><tertiary>{3}</tertiary>
67 67 {3#}</indexterm>
68 68 {3#}<indexterm>
69 69 <primary>{2}</primary><secondary>{3}</secondary>
70 70 {3#}</indexterm>
71 71 {3#}<indexterm>
72 72 <primary>{3}</primary>
73 73 {3#}</indexterm>
74 74
75 75 [indexterm2-inlinemacro]
76 76 # Inline index term.
77 77 # Single entry index term that is visible in the primary text flow.
78 78 <indexterm>
79 79 <primary>{1}</primary>
80 80 </indexterm>
81 81 {1}
82 82
83 83 [footnote-inlinemacro]
84 84 # Inline footnote.
85 85 <footnote><simpara>{0}</simpara></footnote>
86 86
87 87 [callout-inlinemacro]
88 88 # Inline callout.
89 89 <co id="{coid}"/>
90 90
91 91 [tags]
92 92 # Bulleted, numbered and labeled list tags.
93 93 ilist=<itemizedlist{id? id="{id}"}>{title?<title>{title}</title>}|</itemizedlist>
94 94 ilistitem=<listitem>|</listitem>
95 95 ilisttext=<simpara>|</simpara>
96 96 olist=<orderedlist{id? id="{id}"}>{title?<title>{title}</title>}|</orderedlist>
97 97 olist2=<orderedlist{id? id="{id}"} numeration="loweralpha">|</orderedlist>
98 98 olistitem=<listitem>|</listitem>
99 99 olisttext=<simpara>|</simpara>
100 100 vlist=<variablelist{id? id="{id}"}>{title?<title>{title}</title>}|</variablelist>
101 101 vlistentry=<varlistentry>|</varlistentry>
102 102 vlistterm=<term>|</term>
103 103 vlisttext=<simpara>|</simpara>
104 104 vlistitem=<listitem>|</listitem>
105 105 # Horizontal labeled list (implemented with two column table).
106 106 # Hardwired column widths to 30%,70% because the current crop of PDF
107 107 # generators do not auto calculate column widths.
108 108 hlist=<{title?table}{title!informaltable}{id? id="{id}"} tabstyle="{style=hlabeledlist}" pgwide="0" frame="none" colsep="0" rowsep="0">{title?<title>{title}</title>}<tgroup cols="2"><colspec colwidth="{1=3}*"/><colspec colwidth="{2=7}*"/><tbody valign="top">|</tbody></tgroup><{title?/table}{title!/informaltable}>
109 109 hlistentry=<row>|</row>
110 110 hlisttext=<simpara>|</simpara>
111 111 hlistterm=<entry><simpara>|</simpara></entry>
112 112 hlistitem=<entry>|</entry>
113 113
114 114 # Question and Answer list.
115 115 qlist=<qandaset{id? id="{id}"}>{title?<title>{title}</title>}|</qandaset>
116 116 qlistentry=<qandaentry>|</qandaentry>
117 117 qlistterm=<question><simpara>|</simpara></question>
118 118 qlistitem=<answer>|</answer>
119 119 qlisttext=<simpara>|</simpara>
120 120 # Bibliography list.
121 121 blist=|
122 122 blistitem=<bibliomixed>|</bibliomixed>
123 123 blisttext=<bibliomisc>|</bibliomisc>
124 124 # Glossary list.
125 125 glist=|
126 126 glistentry=<glossentry>|</glossentry>
127 127 glistterm=<glossterm>|</glossterm>
128 128 glistitem=<glossdef>|</glossdef>
129 129 glisttext=<simpara>|</simpara>
130 130 # Callout list.
131 131 colist=<calloutlist{id? id="{id}"}>{title?<title>{title}</title>}|</calloutlist>
132 132 colistitem=<callout arearefs="{coids}">|</callout>
133 133 colisttext=<simpara>|</simpara>
134 134
135 135 # Quoted text
136 136 emphasis=<emphasis>|</emphasis>
137 137 strong=<emphasis role="strong">|</emphasis>
138 138 monospaced=<literal>|</literal>
139 139 quoted={amp}#8220;|{amp}#8221;
140 140
141 141 # Inline macros
142 142 [http-inlinemacro]
143 143 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
144 144 [https-inlinemacro]
145 145 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
146 146 [ftp-inlinemacro]
147 147 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
148 148 [file-inlinemacro]
149 149 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
150 150 [mailto-inlinemacro]
151 151 <ulink url="{name}:{target}">{0={target}}</ulink>
152 152 #<email>{target}</email>
153 153 [link-inlinemacro]
154 154 <ulink url="{target}">{0={target}}</ulink>
155 155 # anchor:id[text]
156 156 [anchor-inlinemacro]
157 157 <anchor id="{target}" xreflabel="{0=[{target}]}"/>
158 158 # [[id,text]]
159 159 [anchor2-inlinemacro]
160 160 <anchor id="{1}" xreflabel="{2=[{1}]}"/>
161 161 # [[[id]]]
162 162 [anchor3-inlinemacro]
163 163 <anchor id="{1}" xreflabel="[{1}]"/>[{1}]
164 164 # xref:id[text]
165 165 [xref-inlinemacro]
166 166 <link linkend="{target}">{0}</link>
167 167 {2%}<xref linkend="{target}"/>
168 168 # <<id,text>>
169 169 [xref2-inlinemacro]
170 170 <link linkend="{1}">{2}</link>
171 171 {2%}<xref linkend="{1}"/>
172 172
173 173
174 174 # Special word macros
175 175 [emphasizedwords]
176 176 <emphasis>{words}</emphasis>
177 177 [monospacedwords]
178 178 <literal>{words}</literal>
179 179 [strongwords]
180 180 <emphasis role="strong">{words}</emphasis>
181 181
182 182 # Paragraph substitution.
183 183 [paragraph]
184 184 <formalpara{id? id="{id}"}><title>{title}</title><para>
185 185 {title%}<simpara{id? id="{id}"}>
186 186 |
187 187 {title%}</simpara>
188 188 {title#}</para></formalpara>
189 189 {empty}
190 190
191 191 [admonitionparagraph]
192 192 <{name}{id? id="{id}"}><simpara>|</simpara></{name}>
193 193
194 194 [literalparagraph]
195 195 # The literal block employs the same markup.
196 196 template::[literalblock]
197 197
198 198 [verseparagraph]
199 199 template::[verseblock]
200 200
201 201 # Delimited blocks.
202 202 [literalblock]
203 203 <example><title>{title}</title>
204 204 <literallayout{id? id="{id}"} class="{font=monospaced}">
205 205 |
206 206 </literallayout>
207 207 {title#}</example>
208 208
209 209 [listingblock]
210 210 <example><title>{title}</title>
211 211 <screen>
212 212 |
213 213 </screen>
214 214 {title#}</example>
215 215
216 216 [verseblock]
217 217 <formalpara{id? id="{id}"}><title>{title}</title><para>
218 218 {title%}<literallayout{id? id="{id}"}>
219 219 {title#}<literallayout>
220 220 |
221 221 </literallayout>
222 222 {title#}</para></formalpara>
223 223
224 224 [sidebarblock]
225 225 <sidebar{id? id="{id}"}>
226 226 <title>{title}</title>
227 227 |
228 228 </sidebar>
229 229
230 230 [backendblock]
231 231 |
232 232
233 233 [quoteblock]
234 234 # The epigraph element may be more appropriate than blockquote.
235 235 <blockquote{id? id="{id}"}>
236 236 <title>{title}</title>
237 237 <attribution>
238 238 {attribution}
239 239 <citetitle>{citetitle}</citetitle>
240 240 </attribution>
241 241 |
242 242 </blockquote>
243 243
244 244 [exampleblock]
245 245 <{title?example}{title!informalexample}{id? id="{id}"}>
246 246 <title>{title}</title>
247 247 |
248 248 </{title?example}{title!informalexample}>
249 249
250 250 [admonitionblock]
251 251 <{name}{id? id="{id}"}>
252 252 <title>{title}</title>
253 253 |
254 254 </{name}>
255 255
256 256 # Tables.
257 257 [tabledef-default]
258 258 template=table
259 259 colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}"/>
260 260 bodyrow=<row>|</row>
261 261 bodydata=<entry>|</entry>
262 262
263 263 [table]
264 264 <{title?table}{title!informaltable}{id? id="{id}"} pgwide="0"
265 265 frame="{frame=topbot}"
266 266 {grid%rowsep="0" colsep="0"}
267 267 {eval:\{"none":"rowsep=\"0\" colsep=\"0\"", "cols":"rowsep=\"0\" colsep=\"1\"", "all":"rowsep=\"1\" colsep=\"1\"", "rows":"rowsep=\"1\" colsep=\"0\"" \}["{grid}"]}
268 268 >
269 269 <title>{title}</title>
270 270 <tgroup cols="{cols}">
271 271 {colspecs}
272 272 {headrows#}<thead>
273 273 {headrows}
274 274 {headrows#}</thead>
275 275 {footrows#}<tfoot>
276 276 {footrows}
277 277 {footrows#}</tfoot>
278 278 <tbody>
279 279 {bodyrows}
280 280 </tbody>
281 281 </tgroup>
282 282 </{title?table}{title!informaltable}>
283 283
284 284 [specialsections]
285 285 ifdef::doctype-article[]
286 286 ^Abstract$=sect-abstract
287 287 endif::doctype-article[]
288 288
289 289 ifdef::doctype-book[]
290 290 ^Colophon$=sect-colophon
291 291 ^Dedication$=sect-dedication
292 292 ^Preface$=sect-preface
293 293 endif::doctype-book[]
294 294
295 295 ^Index$=sect-index
296 296 ^(Bibliography|References)$=sect-bibliography
297 297 ^Glossary$=sect-glossary
298 298 ^Appendix [A-Z][:.](?P<title>.*)$=sect-appendix
299 299
300 300 # Special sections.
301 301 [sect-preface]
302 302 <preface{id? id="{id}"}>
303 303 <title>{title}</title>
304 304 |
305 305 </preface>
306 306
307 307 [sect-index]
308 308 <index{id? id="{id}"}>
309 309 <title>{title}</title>
310 310 |
311 311 </index>
312 312
313 313 [sect-bibliography]
314 314 <bibliography{id? id="{id}"}>
315 315 <title>{title}</title>
316 316 |
317 317 </bibliography>
318 318
319 319 [sect-glossary]
320 320 <glossary{id? id="{id}"}>
321 321 <title>{title}</title>
322 322 |
323 323 </glossary>
324 324
325 325 [sect-appendix]
326 326 <appendix{id? id="{id}"}>
327 327 <title>{title}</title>
328 328 |
329 329 </appendix>
330 330
331 331
332 332 [header-declarations]
333 333 <?xml version="1.0" encoding="{encoding}"?>
334 334 <!DOCTYPE {eval:\{"article":"article", "book":"book", "manpage":"refentry"\}["{doctype}"]} PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
335 335
336 336 #-------------------------
337 337 # article document type
338 338 #-------------------------
339 339 ifdef::doctype-article[]
340 340
341 341 [header]
342 342 template::[header-declarations]
343 343
344 344 <article lang="ja">
345 345 {doctitle#}<articleinfo>
346 346 <title>{doctitle}</title>
347 347 <date>{date}</date>
348 348 {authored#}<author>
349 349 <firstname>{firstname}</firstname>
350 350 <othername>{middlename}</othername>
351 351 <surname>{lastname}</surname>
352 352 <affiliation><address><email>{email}</email></address></affiliation>
353 353 {authored#}</author>
354 354
355 355 # If file named like source document with -revhistory.xml suffix exists
356 356 # include it as the document history, otherwise use current revision.
357 357 {revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
358 358 {revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
359 359
360 360 <corpname>{companyname}</corpname>
361 361 {doctitle#}</articleinfo>
362 362
363 363 [footer]
364 364 </article>
365 365
366 366 [preamble]
367 367 # Untitled elements between header and first section title.
368 368 |
369 369
370 370 [sect-abstract]
371 371 <abstract{id? id="{id}"}>
372 372 |
373 373 </abstract>
374 374
375 375 [sect1]
376 376 <section{id? id="{id}"}>
377 377 <title>{title}</title>
378 378 |
379 379 </section>
380 380
381 381 [sect2]
382 382 <section{id? id="{id}"}>
383 383 <title>{title}</title>
384 384 |
385 385 </section>
386 386
387 387 [sect3]
388 388 <section{id? id="{id}"}>
389 389 <title>{title}</title>
390 390 |
391 391 </section>
392 392
393 393 [sect4]
394 394 <section{id? id="{id}"}>
395 395 <title>{title}</title>
396 396 |
397 397 </section>
398 398
399 399 endif::doctype-article[]
400 400
401 401 #-------------------------
402 402 # manpage document type
403 403 #-------------------------
404 404 ifdef::doctype-manpage[]
405 405
406 406 [replacements]
407 407 # The roff format does not substitute special characters so just print them as
408 408 # text.
409 409 \(C\)=(C)
410 410 \(TM\)=(TM)
411 411
412 412 [header]
413 413 template::[header-declarations]
414 414 <refentry>
415 415 <refmeta>
416 416 <refentrytitle>{mantitle}</refentrytitle>
417 417 <manvolnum>{manvolnum}</manvolnum>
418 418 </refmeta>
419 419 <refnamediv>
420 420 <refname>{manname}</refname>
421 421 <refpurpose>{manpurpose}</refpurpose>
422 422 </refnamediv>
423 423
424 424 [footer]
425 425 </refentry>
426 426
427 427 # Section macros
428 428 [sect-synopsis]
429 429 <refsynopsisdiv{id? id="{id}"}>
430 430 |
431 431 </refsynopsisdiv>
432 432
433 433 [sect1]
434 434 <refsect1{id? id="{id}"}>
435 435 <title>{title}</title>
436 436 |
437 437 </refsect1>
438 438
439 439 [sect2]
440 440 <refsect2{id? id="{id}"}>
441 441 <title>{title}</title>
442 442 |
443 443 </refsect2>
444 444
445 445 [sect3]
446 446 <refsect3{id? id="{id}"}>
447 447 <title>{title}</title>
448 448 |
449 449 </refsect3>
450 450
451 451 endif::doctype-manpage[]
452 452
453 453 #-------------------------
454 454 # book document type
455 455 #-------------------------
456 456 ifdef::doctype-book[]
457 457
458 458 [header]
459 459 template::[header-declarations]
460 460
461 461 <book lang="ja">
462 462 {doctitle#}<bookinfo>
463 463 <title>{doctitle}</title>
464 464 <date>{date}</date>
465 465 {authored#}<author>
466 466 <firstname>{firstname}</firstname>
467 467 <othername>{middlename}</othername>
468 468 <surname>{lastname}</surname>
469 469 <affiliation><address><email>{email}</email></address></affiliation>
470 470 {authored#}</author>
471 471
472 472 # If file named like source document with -revhistory.xml suffix exists
473 473 # include it as the document history, otherwise use current revision.
474 474 {revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
475 475 {revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
476 476
477 477 <corpname>{companyname}</corpname>
478 478 {doctitle#}</bookinfo>
479 479
480 480 [footer]
481 481 </book>
482 482
483 483 [preamble]
484 # Preamble is not allowed in DocBook book so wrap it in a preface.
484 # Preamble is not allowed in DocBook book so wrap it in a preface.
485 485 <preface{id? id="{id}"}>
486 486 <title>Preface</title>
487 487 |
488 488 </preface>
489 489
490 490 [sect-dedication]
491 491 <dedication{id? id="{id}"}>
492 492 |
493 493 </dedication>
494 494
495 495 [sect-colophon]
496 496 <colophon{id? id="{id}"}>
497 497 |
498 498 </colophon>
499 499
500 500 [sect0]
501 501 <part{id? id="{id}"}>
502 502 <title>{title}</title>
503 503 |
504 504 </part>
505 505
506 506 [sect1]
507 507 <chapter{id? id="{id}"}>
508 508 <title>{title}</title>
509 509 |
510 510 </chapter>
511 511
512 512 [sect2]
513 513 <section{id? id="{id}"}>
514 514 <title>{title}</title>
515 515 |
516 516 </section>
517 517
518 518 [sect3]
519 519 <section{id? id="{id}"}>
520 520 <title>{title}</title>
521 521 |
522 522 </section>
523 523
524 524 [sect4]
525 525 <section{id? id="{id}"}>
526 526 <title>{title}</title>
527 527 |
528 528 </section>
529 529
530 530 endif::doctype-book[]
531 531
532 532 ifdef::sgml[]
533 533 #
534 534 # Optional DocBook SGML.
535 535 #
536 536 # Most of the differences between DocBook XML and DocBook SGML boils
537 537 # down to the empty element syntax: SGML does not like the XML empty
538 538 # element <.../> syntax, use <...> instead.
539 539 #
540 540 [miscellaneous]
541 541 outfilesuffix=.sgml
542 542
543 543 [header-declarations]
544 544 <!DOCTYPE {eval:\{"article":"article", "book":"book", "manpage":"refentry"\}["{doctype}"]} PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
545 545
546 546 [tabledef-default]
547 547 colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}">
548 548
549 549 [image-inlinemacro]
550 550 <inlinemediaobject>
551 551 <imageobject>
552 552 <imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
553 553 </imageobject>
554 554 <textobject><phrase>{1={target}}</phrase></textobject>
555 555 </inlinemediaobject>
556 556
557 557 [image-blockmacro]
558 558 <figure><title>{title}</title>
559 559 {title%}<informalfigure>
560 560 <mediaobject>
561 561 <imageobject>
562 562 <imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
563 563 </imageobject>
564 564 <textobject><phrase>{1={target}}</phrase></textobject>
565 565 </mediaobject>
566 566 {title#}</figure>
567 567 {title%}</informalfigure>
568 568
569 569 # Inline macros
570 570 [xref-inlinemacro]
571 571 <link linkend="{target}">{0}</link>
572 572 {2%}<xref linkend="{target}">
573 573 [xref2-inlinemacro]
574 574 # <<id,text>>
575 575 <link linkend="{1}">{2}</link>
576 576 {2%}<xref linkend="{1}">
577 577 [anchor-inlinemacro]
578 578 <anchor id="{target}" xreflabel="{0=[{target}]}">
579 579 [anchor2-inlinemacro]
580 580 # [[id,text]]
581 581 <anchor id="{1}" xreflabel="{2=[{1}]}">
582 582
583 583 endif::sgml[]
@@ -1,867 +1,867 b''
1 1 HG(1)
2 2 =====
3 3 Matt Mackall <mpm@selenic.com>
4 4
5 5 名前
6 6 --
7 7 hg - Mercurial ソースコード管理システム
8 8
9 9 書式
10 10 --
11 11 'hg' [-v -d -q -y] <command> [command options] [files]
12 12
13 13 説明
14 14 --
15 15 hg(1) コマンドは Mercurial システムへのコマンドラインインターフェ
16 16 イスを提供します。
17 17
18 18 オプション
19 19 ----
20 20
21 21 -R, --repository::
22 22 リポジトリのルートディレクトリを指定します。
23 23
24 24 --cwd::
25 25 作業ディレクトリを変更します。
26 26
27 27 -y, --noninteractive::
28 28 プロンプトを出さずに、要求された答えが全て 'yes' であると仮定
29 29 します。
30 30
31 31 -q, --quiet::
32 32 出力を抑制します。
33 33
34 34 -v, --verbose::
35 35 さらなる出力を可能にします。
36 36
37 37 7--debug::
38 38 デバッグ出力を可能にします。
39 39
40 40 --traceback::
41 41 例外時にトレースバックを表示します。
42 42
43 43 --time::
44 44 コマンドにどのくらい時間がかかるかを表示します。
45 45
46 46 --profile::
47 47 コマンドを実行したときのプロファイルを表示します。
48 48
49 49 --version::
50 50 バージョン情報を表示して終了します。
51 51
52 52 -h, --help::
53 53 ヘルプを表示して終了します。
54 54
55 55 コマンドの要素
56 56 -------
57 57
58 58 files ...::
59 59 1つ以上のファイル名か相対的なパスを表します; パターンマッチン
60 60 グの情報は「ファイル名のパターン」を参照してください。
61 61
62 62 path::
63 63 ローカルマシン上のパスを表します
64 64
65 65 revision::
66 66 チェンジセットのリビジョンナンバー, タグ, チェンジセットのハッ
67 67 シュ値のユニークな部分文字列により指定できるチェンジセットを表
68 68 します
69 69
70 70 repository path::
71 71 ローカルのリポジトリのパス名かリモートのリポジトリの URI を表
72 72 します。URI のプロトコルとしては現在 2 つが利用可能です。
73 73 http:// は高速で、static-http:// は遅いですがウェブのホストに特別
74 74 なサーバを必要としません。
75 75
76 76 コマンド
77 77 ----
78 78
79 79 add [options] [files ...]::
80 80 ファイルをバージョン管理下に置きリポジトリに追加することを予定
81 81 します。
82 82
83 83 ファイルは次にコミット時にリポジトリに追加されます。
84 84
85 85 ファイル名が与えられなければ、現在のディレクトリとサブディレク
86 86 トリの全てのファイルを追加します。
87 87
88 88 addremove [options] [files ...]::
89 89 新しいファイルを全て追加し無くなったファイルを全てリポジトリか
90 90 ら取り除きます。
91 91
92 92 新しいファイルは .hgignore 中のパターンにマッチした場合無視さ
93 93 れます。add のようにこの変更は次のコミット時に効果を持ちます。
94 94
95 95 annotate [-r <rev> -u -n -c] [files ...]::
96 96 ファイル中の変更を列挙し、各行の原因であるリビジョン id を表示
97 97 します。
98 98
99 99 このコマンドある変更が生じた際に誰がその変更をしたかを発見する
100 100 のに役に立ちます。
101 101
102 102 -a オプションが無いと、annotate はバイナリとして検出されたファ
103 103 イルを避けるようになります。-a があると、annotate はとくかく注
104 104 釈を生成し、おそらく望ましくない結果になるでしょう。
105 105
106 106 オプション:
107 107 -a, --text 全てのファイルをテキストとして扱います
108 108 -I, --include <pat> 与えられたパターンにマッチした名前を含め
109 109 ます
110 110 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
111 111 します
112 112 -r, --revision <rev> 指定されたリビジョンの注釈を生成します
113 113 -u, --user 著者を列挙します
114 114 -c, --changeset チェンジセットを列挙します
115 115 -n, --number リビジョンナンバーを列挙します
116 116 (デフォルト)
117 117
118 118 bundle <file> <other>::
119 119 (実験的)
120 120
121 121 他のリポジトリには見付からなかった全てのチェンジセットを集めた、
122 122 圧縮済みチェンジグループファイルを生成します。
123 123
124 124 このファイルは従来の方法で転送することができ、他のリポジトリに
125 125 unbundle コマンドで適用できます。これは push と pull が使えな
126 126 いか、リポジトリ全体をエクスポートしてしまうことが望ましくない
127 127 ときに便利です。標準のファイル拡張子は ".hg" です。
128 128
129 129 import/export と違って、これはパーミッション、名前変更のデータ、
130 130 リビジョンの履歴を含めたチェンジセットの内容全てを保存します。
131 131
132 132 cat [options] <file ...>::
133 133 指定されたファイルを与えられたリビジョンの内容で表示します。リ
134 134 ビジョンが指定されなかった場合は tip が使われます。
135 135
136 136 出力はファイルに対しても可能です。その場合、ファイル名はフォー
137 137 マット文字列により指定されます。フォーマット規則は export コマ
138 138 ンドと同じですが、さらに次のものが追加されます。
139 139
140 140 %s 出力されるファイルのベース名
141 141 %d 出力されるファイルのディレクトリ名か、リポジトリのルート
142 142 にいる場合は "."
143 143 %p 出力されるファイルのルートからの相対パス
144 144
145 145 オプション:
146 146 -I, --include <pat> 与えられたパターンにマッチした名前
147 147 を含めます
148 148 -X, --exclude <pat> 与えられたパターンにマッチした名前
149 149 を除外します
150 150 -o, --output <filespec> 整形された名前でファイルに出力しま
151 151
152 152 -r, --rev <rev> 与えられたリビジョンを表示します
153 153
154 154 clone [-U] <source> [dest]::
155 155 既存のリポジトリのコピーを新しいディレクトリに作成します
156 156
157 157 コピー先のディレクトリ名が指定されなければ、デフォルトでソース
158 158 のベース名を使用します。
159 159
160 160 今後の pull に使えるように、コピー元が新しいリポジトリの
161 161 .hg/hgrc に追加されます。
162 162
163 163 効率のために、コピー元とコピー先が同じファイルシステム上にある
164 164 場合はハードリンクが使われます。
165 165
166 166 オプションン:
167 167 -U, --noupdate 新しい作業ディレクトリで update を行いません
168 168 -e, --ssh 使用される ssh コマンドを指定します
169 169 --remotecmd リモート側で実行する hg コマンドを指定します
170 170
171 171 commit [options] [files...]::
172 172 指定されたファイルの変更をリポジトリにコミットします。
173 173
174 174 もしファイルのリストが省略されたら、リポジトリのルートから実行
175 175 した"hg status" で報告される全ての変更がコミットされます。
176 176
177 177 HGEDITOR や EDITOR 環境変数はコミット時のコメントを追加するエ
178 178 ディタを起動するために使われます。
179 179
180 180 オプション:
181 181
182 182 -A, --addremove コミット中に addremove を実行します
183 183 -I, --include <pat> 与えられたパターンにマッチした名前を含め
184 184 ます
185 185 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
186 186 します
187 187 -m, --message <text> <text> をコミットメッセージとして使いま
188 188
189 189 -l, --logfile <file> <file> からコミットメッセージを読み込み
190 190 ます
191 191 -d, --date <datecode> datecode をコミットした日付として記録し
192 192 ます
193 193 -u, --user <user> user をコミッタとして記録します。
194 194
195 195 別名: ci
196 196
197 197 copy <source ...> <dest>::
198 198 コピー先がコピー元のファイルのコピーを持っていると印を付けます。
199 199 もしコピー先がディレクトリなら、コピーはディレクトリ中に置かれ
200 200 ます。もしコピー先がファイルなら、コピー元は1つのみ指定可能で
201 201 す。
202 202
203 203 デフォルトでは、このコマンドはファイルがその作業ディレクトリに
204 204 あるものとしてその内容をコピーします。もし --after と一緒に呼
205 205 び出されれば、操作は記録されますが、コピーは実行されません。
206 206
207 207 このコマンドは次のコミット時に効果を持ちます。
208 208
209 209 注意: このコマンドは実験的です。リネームされたファイルを適切に
210 210 記録できますが、この情報はマージによってまだ完全には使われませ
211 211 んし、ログで完全に報告されることもありません。
212 212
213 213 オプション:
214 214 -A, --after 既に発生したコピーを記録します。
215 215 -I, --include <pat> 与えられたパターンにマッチした名前を含め
216 216 ます
217 217 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
218 218 します
219 219 -f, --force 既存の変更されたファイルに無理矢理コピー
220 220 します
221 221 -p, --parents コピー先にコピー元のパスを追加します
222
222
223 223 別名: cp
224 224
225 225 diff [-a] [-r revision] [-r revision] [files ...]::
226 226 指定されたファイルのリビジョン間の差分を表示します。
227 227
228 228 ファイル間の差分は unified diff 形式で表示されます。
229 229
230 230 2つのリビジョンが引数として指定された場合、それらのリビジョン
231 231 間の差分が表示されます。1つのリビジョンしか指定されなければ、
232 232 そのリビジョンは作業ディレクトリと比較されます。そして リビジョ
233 233 ンが指定されなければ、作業ディレクトリのファイルがその親と比較
234 234 されます。
235 235
236 236 -a オプション無しでは、diff はバイナリファイルを検出したときに
237 237 その差分を生成するのを避けます。-a オプションでは、diff はとに
238 238 かく差分を生成し、恐らく望ましくない結果をもたらすでしょう。
239 239
240 240 オプション:
241 241 -a, --text 全てのファイルをテキストとして扱います
242 242 -I, --include <pat> 与えられたパターンにマッチした名前を含め
243 243 ます
244 244 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
245 245 します
246 246
247 247 export [-o filespec] [revision] ...::
248 248 1つ以上のリビジョンのチェンジセットのヘッダと差分を出力します。
249 249
250 250 チェンジセットのヘッダに表示される情報は: 著者、チェンジセット
251 251 のハッシュ、親、コミット時のコメントです。
252 252
253 253 出力はファイルに対しても可能です。その場合、ファイル名はフォー
254 254 マット文字列により指定されます。フォーマット規則は下記の通りで
255 255 す:
256 256
257 257 %% そのままの "%" 文字
258 258 %H チェンジセットのハッシュ (40 バイトの 16 進数)
259 259 %N 生成されているパッチの番号
260 260 %R チェンジセットのリビジョンナンバー
261 261 %b エクスポートしているリポジトリのメース名
262 262 %h 短い形式のチェンジセットのハッシュ (12 バイトの 16 進数)
263 263 %n 0 で 詰められた 1 から始まる連番
264 264 %r 0 で 詰められたリビジョンナンバー
265 265
266 266 -a オプション無しでは、diff はバイナリファイルを検出したときに
267 267 その差分を生成するのを避けます。-a オプションでは、diff はとに
268 268 かく差分を生成し、恐らく望ましくない結果をもたらすでしょう。
269 269
270 270 オプション:
271 271 -a, --text 全てのファイルをテキストとして扱います
272 272 -o, --output <filespec> 整形された名前でファイルに出力します
273 273
274 274 forget [options] [files]::
275 275 次のコミット時に予定された 'hg add' を取り消します。
276 276
277 277 オプション:
278 278 -I, --include <pat> 与えられたパターンにマッチした名前を含めま
279 279 -すX, --exclude <pat> 与えられたパターンにマッチした名前を除外
280 280 -します
281 281
282 282 grep [options] pattern [files]::
283 283 正規表現によりファイルのリビジョンを検索します。
284 284
285 285 このコマンドは Unix の grep とは違う振舞いをします。これは
286 286 Python/Perl の正規表現だけを受けつけます。これは作業ディレクト
287 287 リではなくリポジトリの履歴を検索します。これは常にマッチしたも
288 288 のが現れたリビジョンナンバーを表示します。
289 289
290 290 デフォルトでは、grep はマッチしたものが見つかったファイルの最
291 291 初のリビジョンを出力します。マッチ状況の変化("-" はマッチが非
292 292 マッチに、"+" は非マッチがマッチに)を含んだ各リビジョンを表示
293 293 するには、--all フラグを使ってください。
294 294
295 295 オプション:
296 296 -0, --print0 ファイル名を NUL で終えます。
297 297 -I, --include <pat> 与えられたパターンにマッチした名前
298 298 を含めます
299 299 -X, --exclude <pat> 与えられたパターンにマッチした名前
300 300 を除外します
301 301 --all マッチした全てのリビジョンを表示し
302 302 ます
303 303 -i, --ignore-case マッチのときに英大文字と小文字を区
304 304 別しないようにします
305 305 -l, --files-with-matches マッチしたファイル名とリビジョンの
306 306 みを表示します
307 307 -n, --line-number マッチした行番号を表示します
308 308 -r, --rev <rev> 指定されたリビジョンの間で検索しま
309 309
310 310 -u, --user その変更をコミットしたユーザを表示
311 311 します
312 312
313 313 heads::
314 314 リポジトリの先頭のチェンジセットを全て表示します。
315 315
316 316 リポジトリの「先頭」とは子のチェンジセットを持っていないチェン
317 317 ジセットです。それらは大抵開発が行われる場所で、通常 update と
318 318 merge 操作の対象となるところです。
319 319
320 320 identify::
321 321 レポジトリの現在の状態の短いサマリを表示します。
322 322
323 323 このサマリはリポジトリの状態を1つまたは2つの親のハッシュ識別子
324 324 を使って識別します。親のハッシュ識別子はもし作業ディレクトリに
325 325 コミットされていない変更があれば後ろに + が付き、更にその後に
326 326 このリビジョンのタグのリストが付きます。
327 327
328 328 別名: id
329 329
330 330 import [-p <n> -b <base> -f] <patches>::
331 331 一連のパッチをインポートし、それぞれ個別にコミットします。
332 332
333 333 作業ディレクトリに未解決の変更があった場合、import は -f フラ
334 334 グが指定されてなければ中断します。
335 335
336 336 もしパッチがメールのよう(最初の行が "From " か RFC 822 ヘッダ
337 337 のよう) であれば、-f オプションが使われていない限りそれは適用
338 338 されません。インポート機構はメールのヘッダをパースもしなければ
339 339 破棄もしないので、本物のメールをインポートしないようにする「メー
340 340 ルのようなものの」健全性チェックを上書きするためだけに -f を使っ
341 341 てください。
342 342
343 343 オプション:
344 344 -p, --strip <n> patch の ディレクトリ除去オプションです。これ
345 345 は関連する patch のオプションと同じ意味を持ち
346 346 ます
347 347 -b <path> パッチを読み込むベースとなるディレクトリを指
348 348 定します
349 349 -f, --force 未解決でまだコミットされていない変更のチェッ
350 350 クを省略します
351 351
352 352 別名: patch
353 353
354 354 incoming [-p] [source]::
355 355 指定されたリポジトリか、デフォルトで pull するリポジトリ中に見
356 356 つかった新しいチェンジセットを表示します。これらは pull が要求
357 357 されたときにpull されるチェンジセットです。
358 358
359 359 現在はローカルのリポジトリのみがサポートされています。
360 360
361 361 オプション:
362 362 -p, --patch パッチを表示します
363 363
364 364 別名: in
365 365
366 366 init [dest]::
367 367 指定されたディレクトリ中に新しいリポジトリを初期化します。指定
368 368 されたディレクトリが存在しない場合は作成されます。
369 369
370 370 ディレクトリが指定されなければ、現在のディレクトリが使用されま
371 371 す。
372 372
373 373 locate [options] [files]::
374 374 Mercurial の管理下にあるファイルで名前が指定されたパターンにマッ
375 375 チしたものを全て表示します。
376 376
377 377 このコマンドは現在のディレクトリとサブディレクトリを検索します。
378 378 リポジトリ全体を検索するには、リポジトリのルートに移動してくだ
379 379 さい。
380 380
381 381 もしマッチするためのパターンが与えられなければ、このコマンドは
382 382 全てのファイルの名前を表示します。
383 383
384 384 もしこのコマンドの出力を "xargs" コマンドに送りたいなら、
385 385 "-0" オプションをこのコマンドと "xargs" コマンドの両方で使用し
386 386 てください。これは "xargs" がスペースの入ったファイル名を複数
387 387 のファイル名として扱わないようにします。
388 388
389 389 オプション:
390 390
391 -0, --print0 xargs と一緒に使うために、ファイル名を
391 -0, --print0 xargs と一緒に使うために、ファイル名を
392 392 NUL で終えます
393 393 -f, --fullpath ファイルシステムのルートからの完全なパ
394 394 スを表示します
395 395 -I, --include <pat> 与えられたパターンにマッチした名前を含
396 396 めます
397 397 -r, --rev <rev> rev のときのリポジトリを検索します
398 398 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
399 399 します
400 400
401 401 log [-r revision ...] [-p] [files]::
402 402 指定されたファイルかプロジェクト全体のリビジョンの履歴を表示し
403 403 ます。
404 404
405 405 デフォルトではこのコマンドは次のものを出力します: チェンジセッ
406 406 トのid とハッシュ、タグ、親、ユーザ、日付、各コミットのサマ
407 407 リ。-v スイッチは変更されたファイルやマニフェストのハッシュ、
408 408 メッセージのシグネチャといったより詳しい情報を追加します。
409 409
410 410 オプション:
411 411 -I, --include <pat> 与えられたパターンにマッチした名前を含め
412 412 ます
413 413 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
414 414 します
415 415 -r, --rev <A> 指定されたリビジョンまたは範囲を表示しま
416 416
417 417 -p, --patch パッチを表示します
418 418
419 419 別名: history
420 420
421 421 manifest [revision]::
422 422 指定されたリビジョンでバージョン管理されているファイルのリスト
423 423 を表示します。
424 424
425 425 manifest はバージョン管理されているファイルのリストです。もし
426 426 リビジョンが指定されなければ、tip が使われます。
427 427
428 428 outgoing [-p] [dest]::
429 429 指定された行き先のリポジトリかデフォルトで push するリポジトリ
430 430 中に見付からなかったチェンジセットを表示します。これらは push
431 431 が要求されたときに push されるであろうチェンジセットです。
432 432
433 433 オプション:
434 434 -p, --patch パッチを表示します
435 435
436 436 別名: out
437 437
438 438 parents::
439 439 作業ディレクトリの親リビジョンを表示します。
440 440
441 441 paths [NAME]::
442 442 シンボルのパス名である NAME の行き先を表示します。もしシンボル
443 443 名が指定されなければ、利用可能なシンボル名の行き先を表示します。
444 444
445 445 パス名は /etc/mercurial/hgrc と $HOME/.hgrc の [paths] セクショ
446 446 ンで定義されます。もしリポジトリの内部で実行された場
447 447 合、.hg/hgrc も使用されます。
448 448
449 449 pull <repository path>::
450 450 リモートのリポジトリの変更をローカルのリポジトリに pull します。
451 451
452 452 これは指定されたパスや URL にあるリポジトリの全ての変更を見つ
453 453 けて、それらをローカルのリポジトリに追加します。デフォルトでは、
454 454 これは作業ディレクトリのプロジェクトのコピーを更新しません。
455 455
456 456 有効な URL の次の形式です:
457 457
458 458 local/filesystem/path
459 459 http://[user@]host[:port][/path]
460 460 https://[user@]host[:port][/path]
461 461 ssh://[user@]host[:port][/path]
462 462
463 463 SSH は行き先のマシンのシェルアカウントと、リモートのパスにhg
464 464 のコピーが必要になります。SSH を使用すると、パスはデフォルトで
465 465 はリモートのユーザのホームディレクトリの相対パスになります; ファ
466 466 イルシステムのルートからの相対パスであることを指定するには、パ
467 467 スの最初にスラッシュを 2つ使用してください。
468 468
469 469 オプション:
470 470 -u, --update pull の後に作業ディレクトリを tip に更新します
471 471 -e, --ssh 使用する ssh コマンドを指定します
472 472 --remotecmd リモート側で使われる hg コマンドを指定します
473 473
474 474 push <destination>::
475 475 ローカルのリポジトリの変更を指定された行き先に push します。
476 476
477 477 これは pull と対称的な操作です。これは現在のリポジトリの変更を
478 478 他のリポジトリへ移すのに役立ちます。もし行き先がローカルであれ
479 479 ば、これはそのディレクトリから現在のディレクトリに対して pull
480 480 するのと同じです。
481 481
482 482 デフォルトでは、push は実行した結果リモートのヘッドの数が増え
483 483 るならば、実行を拒否します。これはたいていクライアントが push
484 484 する前に sync とmerge を忘れていることを意味します。
485 485
486 486 有効な URL は次の形式です:
487 487
488 488 local/filesystem/path
489 489 ssh://[user@]host[:port][/path]
490 490
491 491 SSH は行き先のマシンのシェルアカウントと、リモートのパスに hg
492 492 のコピーが必要になります。
493 493
494 494 オプション:
495 495
496 496 -f, --force update を強行します
497 497 -e, --ssh 使用される ssh コマンドを指定します
498 498 --remotecmd リモート側で実行される hg コマンドを指定します
499 499
500 500 rawcommit [-p -d -u -F -m -l]::
501 501 低レベルのコミットで、ヘルパースクリプト中で使用されます。
502 502
503 503 このコマンドは通常のユーザに使われることは想定していません。こ
504 504 れは主に他の SCM からインポートするときに便利です。
505 505
506 506 recover::
507 507 中断された commit や pull から復帰します。
508 508
509 509 このコマンドは中断された操作からリポジトリの状態を修整しようと
510 510 試みます。これは Mercurial がそうするよう提案したときのみ必要
511 511 でしょう。
512 512
513 513 remove [options] [files ...]::
514 514 指定されたファイルをリポジトリから削除することを予定します。
515 515
516 516 このコマンドはファイルを次のコミット時に削除することを予定しま
517 517 す。このコマンドはファイルを現在の枝から取り除くだけで、プロジェ
518 518 クトの履歴全体からは削除しません。もしファイルが作業ディレクト
519 519 リ中にまだ存在していれば、それらは作業ディレクトリから削除され
520 520 ます。
521 521
522 522 別名: rm
523 523
524 524 rename <source ...> <dest>::
525 525 コピー先をコピー元のコピーのコピーであると印をつけます; コピー
526 526 元に削除の印をつけます。もしコピー先がディレクトリであれば、コ
527 527 ピーはそのディレクトリ中に置かれます。もしコピー先がファイルな
528 528 ら、コピー元は 1 つのみ指定可能です。
529 529
530 530 デフォルトでは、このコマンドはファイルがその作業ディレクトリに
531 531 あるものとしてその内容をコピーします。もし --after と一緒に呼
532 532 び出されれば、操作は記録されますが、コピーは実行されません。
533 533
534 534 このコマンドは次のコミット時に効果を持ちます。
535 535
536 536 注意: このコマンドは実験的です。リネームされたファイルを適切に
537 537 記録できますが、この情報はマージによってまだ完全には使われませ
538 538 んし、ログで完全に報告されることもありません。
539 539
540 540 オプション:
541 541 -A, --after 既に発生したリネームを記録します
542 542 -f, --force 既存の変更されたファイルに無理矢理コピーし
543 543 ます
544 544 -p, --parents コピー先にコピー元のパスを追加します
545 545
546 546 別名: mv
547 547
548 548 revert [names ...]::
549 549 指定されたファイルやディレクトリの未コミットの変更を取り消しま
550 550 す。これは関連したファイルの内容をコミットされてない状態に戻し
551 551 ます。
552 552
553 553 もしファイルが削除されていれば、再作成されます。もしファイルの
554 554 実行モードが変更されていれば、リセットされます。
555 555
556 556 ディレクトリが指定された場合、そのディレクトリ中のすべてのファ
557 557 イルとサブディレクトリが元に戻されます。
558 558
559 559 もし引数が指定されなければ、現在のディレクトリ中の全てのファイ
560 560 ルとサブディレクトリが元の戻されます。
561 561
562 562 オプション:
563 563 -r, --rev <rev> 元に戻す先のリビジョンを指定します
564 564 -n, --nonrecursive サブディレクトリを再帰的に辿らないように
565 565 します
566 566
567 567 root::
568 568 現在のリポジトリのルートディレクトリを表示します。
569 569
570 570 serve [options]::
571 571 ローカルの HTTP リポジトリと pull サーバを起動します。
572 572
573 573 デフォルトでは、サーバはアクセスログを標準出力に、エラーログを
574 574 標準エラー出力に出力します。ファイルにログを取るには "-A" と
575 575 "-E" オプションを使ってください。
576 576
577 577 オプション:
578 578 -A, --accesslog <file> アクセスログが出力されるファイルの名前
579 579 を指定します
580 580 -E, --errorlog <file> エラーログが出力されるファイルの名前を
581 581 指定します
582 582 -a, --address <addr> 使用するアドレスを指定します
583 -p, --port <n> 使用するポートを指定します
583 -p, --port <n> 使用するポートを指定します
584 584 (デフォルト: 8000)
585 -n, --name <name> ウェブページで表示する名前を指定します
585 -n, --name <name> ウェブページで表示する名前を指定します
586 586 (デフォルト: working dir)
587 587 -t, --templatedir <path> 使用するウェブの雛型を指定します
588 588 -6, --ipv6 IPv4 に加えて IPv6 も使用します
589 589
590 590 status [options] [files]::
591 591 作業ディレクトリ中の変更されたファイルを表示します。名前が指定
592 592 されなければ、全てのファイルが表示されます。名前が指定されれば、
593 593 指定された名前にマッチしたファイルのみが表示されます。
594 594
595 595 ファイルの状態を表示するのに使われる記号:
596 596
597 597 M = 変更されました
598 598 A = 追加されました
599 599 R = 削除されました
600 600 ? = バージョン管理下にありません
601 601
602 602 オプション:
603 603
604 604 -m, --modified 変更されたファイルのみを表示します
605 605 -a, --added 追加されたファイルのみを表示します
606 606 -r, --removed 削除されたファイルのみを表示します
607 607 -u, --unknown 不明な(バージョン管理下にない)ファイルのみ
608 608 を表示します
609 609 -n, --no-status 状態を示す接頭辞を隠します
610 610 -0, --print0 xargs と一緒に使うために、ファイル名を NUL
611 611 で終えます
612 612 -I, --include <pat> 与えられたパターンにマッチした名前を含めま
613 613
614 614 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外し
615 615 ます
616 616
617 617 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
618 618 特定のリビジョンに <name> を使って名前を付けます。
619 619
620 620 タグはリポジトリの特定のリビジョンに名前を付けるために使われ、
621 621 そして異なるリビジョンを比較したり、重要な以前のバージョンに戻っ
622 622 たり、リリース等の分岐点に印をつけたりするのに便利です。
623 623
624 624 もしバージョンが指定されなければ、tip が使われます。
625 625
626 626 バージョン管理、配布、タグのマージを楽にするために、それらは
627 627 ".hgtags" という名前のファイルに格納され、他のプロジェクトのファ
628 628 イルと同様に扱ったり、必要であれば手で編集できます。
629 629
630 630 オプション:
631 631 -l, --local タグをローカルにします
632 632 -m, --message <text> タグをコミットしたときのログのエントリの
633 633 メッセージを指定します
634 634 -d, --date <datecode> コミットの日付を指定します
635 635 -u, --user <user> コミットするユーザを指定します
636 636
637 637 注意: ローカルのタグはバージョン管理や配布されることはなく、ま
638 638 た. hg/localtags ファイルに格納されます。もし同じ名前のローカ
639 639 ルのタグと公開されたタグがあれば、ローカルのタグが使われます。
640 640
641 641 tags::
642 642 リポジトリのタグを列挙します。
643 643
644 644 これは通常のタグとローカルのタグを両方列挙します。
645 645
646 646 tip::
647 647 tip のリビジョンを表示します。
648 648
649 649 unbundle <file>::
650 650 (実験的)
651 651
652 652 bundle コマンドで生成された、圧縮済みチェンジグループファイル
653 653 を適用します。
654 654
655 655 undo::
656 656 最後の commit や pull の処理を取り消します。
657 657
658 658 リポジトリの最後の pull や commit 処理を巻戻し、プロジェクトを
659 659 それより前の状態に戻します。
660 660
661 661 このコマンドは注意して使ってください。まだ 1回の undo だけで、
662 662 redo はありません。
663 663
664 664 このコマンドは公開したリポジトリで使われることは想定していませ
665 665 ん。いったん他のユーザから pull で変更が見えるようになれば、ロー
666 666 カルでそれを取り消しても意味がありません。
667 667
668 668 update [-m -C] [revision]::
669 669 作業ディレクトリを指定されたリビジョンに更新します。
670 670
671 671 デフォルトでは、更新によりローカルの変更をマージしたり破棄した
672 672 りすることが必要となるとき、update はそれを拒否します。
673 673
674 674 -m オプションで、マージが実行されます。
675 675
676 676 -C オプションで、ローカルの変更が失われます。
677 677
678 678 オプション:
679 679 -m, --merge 枝のマージを許可します
680 680 -C, --clean ローカルで変更されたファイルを上書きします
681 681
682 682 別名: up checkout co
683 683
684 684 verify::
685 685 現在のリポジトリの整合性を検証します。
686 686
687 687 これはリポジトリの整合性を全面的にチェックし、チェンジログの各
688 688 エントリ、manifest, 管理下のファイルのハッシュとチェックサムを
689 689 検証し、またクロスリンクとインデクスの整合性も検証します。
690 690
691 691 ファイル名とパターン
692 692 ---------
693 693
694 694 Mercurial では同時に複数のファイルを識別するのに複数の記法が使
695 695 えます。
696 696
697 697 デフォルトでは、Mercurial はファイル名をシェルのスタイルの拡張
698 698 glob パターンとして扱います。
699 699
700 700 別のパターン表記は明示的に指定する必要があります。
701 701
702 702 パターンマッチングなしの単純なパス名を使うには、パス名を
703 703 "path:" で始めてください。これらのパス名は、現在のリポジトリの
704 704 ルートから完全にマッチしている必要があります。
705 705
706 706 拡張 glob を使うには、名前を "glob:" で始めてください。glob は
707 707 現在のディレクトリのみに適用されます: "*.c" といった glob は現
708 708 在のディレクトリ中の ".c" で終わるファイルのみにマッチします。
709 709
710 710 サポートされている glob 文法の拡張はパスの分離記号を越えて全て
711 711 の文字列にマッチする "**" と、"a または b" を意味する "{a,b}"
712 712 です。
713 713
714 714 Perl/Python の正規表現を使うには、名前を "re:" で始めてくださ
715 715 い。正規表現によるマッチはリポジトリのルートの固定されています。
716 716
717 717 単純な例:
718 718
719 719 path:foo/bar リポジトリのルートにある foo というディレクトリ
720 720 の bar という名前
721 721 path:path:name "path:name" という名前のファイルまたはディレク
722 722 トリ
723 723
724 724 Glob の例:
725 725
726 726 glob:*.c 現在のディレクトリ中の ".c" で終わる全ての名前
727 727 *.c 現在のディレクトリ中の ".c" で終わる全ての名前
728 728 **.c 現在のディレクトリと全てのサブディレクトリ中の
729 729 ".c" で終わる全ての名前
730 730 foo/*.c ディレクトリ foo 中の ".c" で終わる全ての名前
731 731 foo/**.c ディレクトリ foo とその全てのサブディレクトリ中
732 732 の ".c" で終わる全ての名前
733 733
734 734 正規表現の例:
735 735
736 736 re:.*\.c$ リポジトリ全体の中の ".c" で終わる全ての名前
737 737
738 738
739 739 単一のリビジョンの指定法
740 740 -----------
741 741
742 742 Mercurial では個々のリビジョンを識別するのに複数の記法が使えま
743 743 す。
744 744
745 745 単純な整数はリビジョンナンバーとして扱われます。負の整数はtip
746 746 からのオフセットとして扱われ、-1 が tip を表します。
747 747
748 748 40 桁の 16 進数の文字列はユニークなリビジョン識別子として扱わ
749 749 れます。
750 750
751 751 40 桁より少ない 16 進数の文字列はユニークなリビジョン識別子と
752 752 して扱われ、短い形式の識別子と呼ばれます。短い形式の識別子はそ
753 753 れが完全な長さの識別子の接頭語であるときだけ有効です。
754 754
755 755 他の文字列は全てタグ名として扱われます。タグはあるリビジョン識
756 756 別子に関連付けられたシンボル名です。タグ名は ":" 文字を含んで
757 757 はいけません。
758 758
759 759 リビジョン名 "tip" は特別なタグで、常に一番最新のリビジョンを
760 760 指します。
761 761
762 762 複数のリビジョンの指定法
763 763 -----------
764 764
765 765 Mercurial が 1つより多くのリビジョンを受けいれるとき、それらは
766 766 別々に指定されるか、連続した範囲として ":" 文字で区切って与え
767 767 られるかもれません。
768 768
769 769 範囲表記の構文は [BEGIN]:[END] で BEGIN と END はリビジョンの
770 770 識別子です。BEGIN も END も両方とも任意です。もし BEGIN が指定
771 771 されなければ、デフォルトでリビジョンナンバー 0 になります。も
772 772 し END が指定されなければ、デフォルトで tip になります。従って
773 773 範囲 ":" は "全てのリビジョン" を意味します。
774 774
775 775 もし BEGIN が END より大きければ、リビジョンは逆の順序として扱
776 776 われます。
777 777
778 778 範囲は閉区間として動作します。これは範囲が 3:5 は 3,4,5 になる
779 779 ことを意味します。同様に、範囲 4:2 は 4,3,2 になります。
780 780
781 781 環境変数
782 782 ----
783 783
784 784 HGEDITOR::
785 785 これはコミッチ時に使われるエディタの名前です。デフォルトでは
786 786 EDITOR の値が使われます。
787 787
788 788 (廃止予定です, .hgrc を使ってください)
789 789
790 790 HGMERGE::
791 791 merge 時の衝突を解決するのに使われる実行ファイルです。プログラ
792 792 ムは3 つの引数で実行されます: ローカルのファイル、リモートのファ
793 793 イル、1 世代前のファイルです。
794 794
795 795 デフォルトのプログラムは "hgmerge" で、これは Mercurial によっ
796 796 て提供される常識的な設定のシェルスクリプトです。
797 797
798 798 (廃止予定です, .hgrc を使ってください)
799 799
800 800 HGUSER::
801 801 これはコミット時の著者として使われる文字列です。
802 802
803 803 (廃止予定です, .hgrc を使ってください)
804 804
805 805 EMAIL::
806 806 もし HGUSER が設定されていなければ、これがコミット時の著者とし
807 807 て使われます。
808 808
809 809 LOGNAME::
810 810 もし HGUSER も EMAIL も設定されていなければ、コミット時の著者
811 811 としてLOGNAME が('@hostname' を付けた形で)使われます。
812 812
813 813 EDITOR::
814 814 これは hgmerge スクリプト中で使われるエディタの名前です。もし
815 815 HGEDITOR が設定されていなければ、コミット時のメッセージに使わ
816 816 れます。デフォルトは 'vi' です。
817 817
818 818 PYTHONPATH::
819 819 これはインポートされるモジュールを見つけるために Python によっ
820 820 て使われ、Mercurial がシステム全体にインストールされていなけれ
821 821 ば、適切に設定される必要があるでしょう。
822 822
823 823 ファイル
824 824 ----
825 825 .hgignore::
826 826 このファイルは(1行ごとに) hg によって無視されるべきファイルを
827 827 記述した正規表現を含みます。
828 828
829 829 .hgtags::
830 830 このファイルはリポジトリの内容のタグ付けされたバージョンに一致
831 831 したハッシュ値とテキストのタグ名(それぞれは空白で区切られます)を
832 832 含みます。
833 833
834 834 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
835 835 このファイルはデフォルトの設定を含みます。.hg/hgrc の値は
836 836 $HOME/.hgrc の設定を上書きし、$HOME/.hgrc の設定はグローバルな
837 837 /etc/mercurial/hgrc の設定を上書きします。これらのファイルの内
838 838 容と書式の詳細については hgrc(5) を参照してください。
839 839
840 840 バグ
841 841 --
842 842 沢山あるでしょうから、もしバグを見つけたらそれをメーリングリスト
843 843 (下の情報源を参照)に送ってください。
844 844
845 845 関連項目
846 846 ----
847 847 hgrc(5)
848 848
849 849 著者
850 850 --
851 851 Matt Mackall <mpm@selenic.com> により書かれました。
852 852
853 853 情報源
854 854 ---
855 855 http://selenic.com/mercurial[主なウェブサイト]
856 856
857 857 http://www.serpentine.com/mercurial[Wiki サイト]
858 858
859 859 http://selenic.com/hg[ソースコードのリポジトリ]
860 860
861 861 http://selenic.com/mailman/listinfo/mercurial[メーリングリスト]
862 862
863 863 著作権情報
864 864 -----
865 865 Copyright (C) 2005-2007 Matt Mackall.
866 866 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
867 867 認められます。
@@ -1,204 +1,204 b''
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 名前
6 6 --
7 7 hgrc - Mercurial の設定ファイル
8 8
9 9 書式
10 10 --
11 11
12 12 Mercurial システムはその振舞いの正常を制御するのに、一連の設定ファ
13 13 イルを使用します。
14 14
15 15 ファイル
16 16 ----
17 17
18 18 Mercurial は 3つのファイルから設定を読みます:
19 19
20 20 /etc/mercurial/hgrc::
21 21 このグローバルの設定ファイルのオプションは実行したユーザ、ディ
22 22 レクトリを問わず全ての Mercurial コマンドに適用されます。
23 23
24 24 $HOME/.hgrc::
25 25 ユーザ毎の設定オプションで、ディレクトリを問わず全ての
26 26 Mercurial コマンドに適用されます。このファイルの値はグローバル
27 27 の設定を上書きします。
28 28
29 29 <repo>/.hg/hgrc::
30 30 リポジトリ毎の設定オプションで、そのリポジトリのみに適用されま
31 31 す。このファイルはバージョン管理されず、 "clone" 操作で転送さ
32 32 れることもありません。このファイルの値はグローバルの設定とユー
33 33 ザ毎の設定を上書きします。
34 34
35 35 構文
36 36 --
37 37
38 38 設定ファイルは "[セクション]" ヘッダから始まるセクションと、それに
39 39 続く"名前: 値"のエントリから成ります: "名前=値"も認められます。
40 40
41 41 [spam]
42 42 eggs=ham
43 43 green=
44 44 eggs
45 45
46 46 各行は1つのエントリを含みます。もし次の行がインデントされていた場
47 47 合、それは前の行の続きとして扱われます。
48 48
49 49 先行する空白は値から取り除かれます。空行は読み飛ばされます。
50 50
51 51 オプションの値は同じセクションや、特別な DEFAULT セクションの別の
52 52 値を参照するフォーマット文字列を含むことができます。
53 53
54 54 "#" や ";" で始まる行は無視されるので、コメントとして使うことがで
55 55 きます。
56 56
57 57 セクション
58 58 -----
59 59
60 60 このセクションは Merucurial の "hgrc" に使うことができる異なったセ
61 61 クションのそれぞれの目的や可能なキー、そして取り得る値について記述
62 62 します。
63 63
64 decode/encode::
64 decode/encode::
65 65 checkout/checkin でファイルを転送するときのフィルターです。これ
66 66 は典型的には改行を処理したり、他の地域化/標準化に使われるでしょ
67 67 う。
68 68
69 69 フィルターはフィルターパターンとそれに続くフィルターコマンドから
70 70 なります。コマンドは標準入力からのデータを受け付け、変換したデー
71 71 タを標準出力に返す必要があります。
72 72
73 73 例:
74 74
75 75 [encode]
76 76 # delta 圧縮を改善するためにチェックイン時に gzip ファイルを
77 77 # 伸長します
78 78 # 注意: 必ずしも良いアイディアではありません。ただの例です
79 79 *.gz = gunzip
80 80
81 81 [decode]
82 82 # 作業ディレクトリに書き出すときにファイルを gzip で再圧縮します
83 83 *.gz = gzip
84 84
85 85 hooks::
86 86 コミットの開始、終了時など様々なアクションで自動的に実行されるコ
87 87 マンドです。
88 88 changegroup;;
89 89 push や pull でチェンジグループが加えられたあとに起動します。
90 90 commit;;
91 91 チェンジセットが作成された後に起動します。新しく作成されたチェ
92 92 ンジセットの ID が渡されます。
93 93 precommit;;
94 94 コミット前に起動します。終了ステータス 0 によりコミットを続行
95 95 します。非ゼロのステータスでコミットは失敗します。
96 96
97 97 http_proxy::
98 98 HTTP プロキシを通してウェブを使った Mercurial のリポジトリにアク
99 99 セスするのに使われます。
100 100 host;;
101 プロキシサーバのホスト名と(オプションの)ポートで、例えば
101 プロキシサーバのホスト名と(オプションの)ポートで、例えば
102 102 "myproxy:8000"などです。
103 103 no;;
104 104 オプションです。コンマで区切られたプロキシを通過すべきホスト名
105 105 のリストです。
106 106 passwd;;
107 107 オプションです。プロキシサーバの認証用のパスワードです。
108 108 user;;
109 109 オプションです。プロキシサーバの認証用のユーザ名です。
110 110
111 111 paths::
112 112 リポジトリにシンボル名を割当てます。左側がシンボル名で、右側がリ
113 113 ポジトリの場所を示すディレクトリや URL です。
114 114
115 115 ui::
116 116 ユーザインターフェースの設定です。
117 117 debug;;
118 118 デバッグ情報を表示します。True か False を取ります。デフォルト
119 119 では False です。
120 120 editor;;
121 121 コミット中に使用するエディタです。デフォルトは $EDITOR か
122 122 "vi" です。
123 123 interactive;;
124 124 ユーザに対してプロンプトを出すようにします。True か False を取
125 125 ります。デフォルトでは True です。
126 126 merge;;
127 127 手動での merge 中に衝突を解決するために使われるプログラムです。
128 128 デフォルトは "hgmerge" です。
129 129 quiet;;
130 130 表示される出力の量を減らします。True か False を取ります。デフォ
131 131 ルトは False です。
132 132 remotecmd;;
133 133 clone/push/pull 操作で使われるリモートのコマンドです。デフォル
134 134 トは 'hg' です。
135 135 ssh;;
136 136 SSH 接続で使われるコマンドです。デフォルトは 'ssh' です。
137 137 username;;
138 138 コミットを実行したときに作成されるチェンジセットのコミッタです。
139 139 一般的には人名と電子メールアドレスで、例えば "Fred Widget
140 140 <fred@example.com>" などです。デフォルトは $EMAIL か
141 141 username@hostname です。
142 142 verbose;;
143 143 表示される出力の量を増やします。True か False を取ります。デフォ
144 144 ルトは False です。
145 145
146 146 web::
147 147 ウェブインターフェイスの設定です。
148 148 accesslog;;
149 149 アクセスログの出力先です。デフォルトでは標準出力です。
150 150 address;;
151 151 バインドするインターフェイスアドレスです。デフォルトでは全てで
152 152 す。
153 153 allowbz2;;
154 154 リポジトリのリビジョンから .tar.bz2 をダウンロードさせるかどう
155 155 かです。デフォルトでは false です。
156 156 allowgz;;
157 157 リポジトリのリビジョンから .tar.gz をダウンロードさせるかどう
158 158 かです。デフォルトでは false です。
159 159 allowpull;;
160 160 リポジトリから pull させるかどうかです。デフォルトでは true で
161 161 す。
162 162 allowzip;;
163 163 リポジトリのリビジョンから .zip をダウンロードさせるかどうかで
164 164 す。デフォルトでは false です。この機能は一時ファイルを作成し
165 165 ます。
166 166 description;;
167 167 リポジトリの目的や内容についてのテキストによる説明です。デフォ
168 168 ルトでは"unknown" です。
169 169 errorlog;;
170 170 エラーログの出力先です。デフォルトでは標準エラー出力です。
171 171 ipv6;;
172 172 IPv6 を使うかどうかです。デフォルトでは false です。
173 173 name;;
174 174 ウェブインターフェイスを使うときのリポジトリの名前です。デフォ
175 175 ルトは現在の作業ディレクトリです。
176 176 maxchanges;;
177 177 チェンジログに記載する変更の最大数です。デフォルトでは 10 です。
178 178 maxfiles;;
179 179 チェンジセットに記載するファイルの最大数です。デフォルトでは
180 180 10 です。
181 181 port;;
182 182 リスンするポートです。デフォルト は 8000 です。
183 183 style;;
184 184 使用するテンプレートマップのスタイルです。
185 185 templates;;
186 186 HTML テンプレートの在処です。デフォルトではインストールしたと
187 187 きのパスです。
188 188
189 189 著者
190 190 --
191 191 Bryan O'Sullivan <bos@serpentine.com>.
192 192
193 193 Mercurial は Matt Mackall <mpm@selenic.com> により書かれました。
194 194
195 195 関連項目
196 196 ----
197 197 hg(1)
198 198
199 199 COPYING
200 200 -------
201 201 このマニュアルの著作権は 2005 Bryan O'Sullivan です。
202 202 Mercurial の著作権は 2005 Matt Mackall です。
203 203 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
204 204 認められます。
@@ -1,168 +1,168 b''
1 1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 2 #
3 3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 4 # that removes files not known to mercurial
5 5 #
6 6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 7 # (http://www.red-bean.com/cvsutils/).
8 8 #
9 9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 10 # [extensions]
11 11 # hgext.purge =
12 12 #
13 13 # For help on the usage of "hg purge" use:
14 14 # hg help purge
15 15 #
16 16 # This program is free software; you can redistribute it and/or modify
17 17 # it under the terms of the GNU General Public License as published by
18 18 # the Free Software Foundation; either version 2 of the License, or
19 19 # (at your option) any later version.
20 20 #
21 21 # This program is distributed in the hope that it will be useful,
22 22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 24 # GNU General Public License for more details.
25 25 #
26 26 # You should have received a copy of the GNU General Public License
27 27 # along with this program; if not, write to the Free Software
28 28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 29
30 30 from mercurial import hg, util
31 31 from mercurial.i18n import _
32 32 import os
33 33
34 def dopurge(ui, repo, dirs=None, act=True, ignored=False,
34 def dopurge(ui, repo, dirs=None, act=True, ignored=False,
35 35 abort_on_err=False, eol='\n',
36 36 force=False, include=None, exclude=None):
37 37 def error(msg):
38 38 if abort_on_err:
39 39 raise util.Abort(msg)
40 40 else:
41 41 ui.warn(_('warning: %s\n') % msg)
42 42
43 43 def remove(remove_func, name):
44 44 if act:
45 45 try:
46 46 remove_func(os.path.join(repo.root, name))
47 47 except OSError, e:
48 48 error(_('%s cannot be removed') % name)
49 49 else:
50 50 ui.write('%s%s' % (name, eol))
51 51
52 52 directories = []
53 53 files = []
54 54 missing = []
55 55 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs,
56 56 include, exclude)
57 57 for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
58 58 ignored=ignored, directories=True):
59 59 if src == 'd':
60 60 directories.append(f)
61 61 elif src == 'm':
62 62 missing.append(f)
63 63 elif src == 'f' and f not in repo.dirstate:
64 64 files.append(f)
65 65
66 66 _check_missing(ui, repo, missing, force)
67 67
68 68 directories.sort()
69 69
70 70 for f in files:
71 71 if f not in repo.dirstate:
72 72 ui.note(_('Removing file %s\n') % f)
73 73 remove(os.remove, f)
74 74
75 75 for f in directories[::-1]:
76 76 if match(f) and not os.listdir(repo.wjoin(f)):
77 77 ui.note(_('Removing directory %s\n') % f)
78 78 remove(os.rmdir, f)
79 79
80 80 def _check_missing(ui, repo, missing, force=False):
81 81 """Abort if there is the chance of having problems with name-mangling fs
82 82
83 83 In a name mangling filesystem (e.g. a case insensitive one)
84 84 dirstate.walk() can yield filenames different from the ones
85 85 stored in the dirstate. This already confuses the status and
86 86 add commands, but with purge this may cause data loss.
87 87
88 88 To prevent this, _check_missing will abort if there are missing
89 89 files. The force option will let the user skip the check if he
90 90 knows it is safe.
91 91
92 92 Even with the force option this function will check if any of the
93 93 missing files is still available in the working dir: if so there
94 94 may be some problem with the underlying filesystem, so it
95 95 aborts unconditionally."""
96 96
97 97 found = [f for f in missing if util.lexists(repo.wjoin(f))]
98 98
99 99 if found:
100 100 if not ui.quiet:
101 101 ui.warn(_("The following tracked files weren't listed by the "
102 102 "filesystem, but could still be found:\n"))
103 103 for f in found:
104 104 ui.warn("%s\n" % f)
105 105 if util.checkfolding(repo.path):
106 106 ui.warn(_("This is probably due to a case-insensitive "
107 107 "filesystem\n"))
108 108 raise util.Abort(_("purging on name mangling filesystems is not "
109 109 "yet fully supported"))
110 110
111 111 if missing and not force:
112 112 raise util.Abort(_("there are missing files in the working dir and "
113 113 "purge still has problems with them due to name "
114 114 "mangling filesystems. "
115 115 "Use --force if you know what you are doing"))
116 116
117 117
118 118 def purge(ui, repo, *dirs, **opts):
119 119 '''removes files not tracked by mercurial
120 120
121 121 Delete files not known to mercurial, this is useful to test local and
122 122 uncommitted changes in the otherwise clean source tree.
123 123
124 124 This means that purge will delete:
125 125 - Unknown files: files marked with "?" by "hg status"
126 126 - Ignored files: files usually ignored by Mercurial because they match
127 127 a pattern in a ".hgignore" file
128 128 - Empty directories: in fact Mercurial ignores directories unless they
129 129 contain files under source control managment
130 130 But it will leave untouched:
131 131 - Unmodified tracked files
132 132 - Modified tracked files
133 133 - New files added to the repository (with "hg add")
134 134
135 135 If directories are given on the command line, only files in these
136 136 directories are considered.
137 137
138 138 Be careful with purge, you could irreversibly delete some files you
139 139 forgot to add to the repository. If you only want to print the list of
140 140 files that this program would delete use the --print option.
141 141 '''
142 142 act = not opts['print']
143 143 ignored = bool(opts['all'])
144 144 abort_on_err = bool(opts['abort_on_err'])
145 145 eol = opts['print0'] and '\0' or '\n'
146 146 if eol == '\0':
147 147 # --print0 implies --print
148 148 act = False
149 149 force = bool(opts['force'])
150 150 include = opts['include']
151 151 exclude = opts['exclude']
152 152 dopurge(ui, repo, dirs, act, ignored, abort_on_err,
153 153 eol, force, include, exclude)
154 154
155 155
156 156 cmdtable = {
157 157 'purge|clean':
158 158 (purge,
159 159 [('a', 'abort-on-err', None, _('abort if an error occurs')),
160 160 ('', 'all', None, _('purge ignored files too')),
161 161 ('f', 'force', None, _('purge even when missing files are detected')),
162 162 ('p', 'print', None, _('print the file names instead of deleting them')),
163 163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
164 164 ' (implies -p)')),
165 165 ('I', 'include', [], _('include names matching the given patterns')),
166 166 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
167 167 _('hg purge [OPTION]... [DIR]...'))
168 168 }
@@ -1,1945 +1,1945 b''
1 1 # localrepo.py - read/write repository class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import repo, changegroup
11 11 import changelog, dirstate, filelog, manifest, context
12 12 import re, lock, transaction, tempfile, stat, mdiff, errno, ui
13 13 import os, revlog, time, util, extensions, hook
14 14
15 15 class localrepository(repo.repository):
16 16 capabilities = ('lookup', 'changegroupsubset')
17 17 supported = ('revlogv1', 'store')
18 18
19 19 def __del__(self):
20 20 self.transhandle = None
21 21 def __init__(self, parentui, path=None, create=0):
22 22 repo.repository.__init__(self)
23 23 self.path = path
24 24 self.root = os.path.realpath(path)
25 25 self.path = os.path.join(self.root, ".hg")
26 26 self.origroot = path
27 27 self.opener = util.opener(self.path)
28 28 self.wopener = util.opener(self.root)
29 29
30 30 if not os.path.isdir(self.path):
31 31 if create:
32 32 if not os.path.exists(path):
33 33 os.mkdir(path)
34 34 os.mkdir(self.path)
35 35 requirements = ["revlogv1"]
36 36 if parentui.configbool('format', 'usestore', True):
37 37 os.mkdir(os.path.join(self.path, "store"))
38 38 requirements.append("store")
39 39 # create an invalid changelog
40 40 self.opener("00changelog.i", "a").write(
41 41 '\0\0\0\2' # represents revlogv2
42 42 ' dummy changelog to prevent using the old repo layout'
43 43 )
44 44 reqfile = self.opener("requires", "w")
45 45 for r in requirements:
46 46 reqfile.write("%s\n" % r)
47 47 reqfile.close()
48 48 else:
49 49 raise repo.RepoError(_("repository %s not found") % path)
50 50 elif create:
51 51 raise repo.RepoError(_("repository %s already exists") % path)
52 52 else:
53 53 # find requirements
54 54 try:
55 55 requirements = self.opener("requires").read().splitlines()
56 56 except IOError, inst:
57 57 if inst.errno != errno.ENOENT:
58 58 raise
59 59 requirements = []
60 60 # check them
61 61 for r in requirements:
62 62 if r not in self.supported:
63 63 raise repo.RepoError(_("requirement '%s' not supported") % r)
64 64
65 65 # setup store
66 66 if "store" in requirements:
67 67 self.encodefn = util.encodefilename
68 68 self.decodefn = util.decodefilename
69 69 self.spath = os.path.join(self.path, "store")
70 70 else:
71 71 self.encodefn = lambda x: x
72 72 self.decodefn = lambda x: x
73 73 self.spath = self.path
74 74 self.sopener = util.encodedopener(util.opener(self.spath), self.encodefn)
75 75
76 76 self.ui = ui.ui(parentui=parentui)
77 77 try:
78 78 self.ui.readconfig(self.join("hgrc"), self.root)
79 79 extensions.loadall(self.ui)
80 80 except IOError:
81 81 pass
82 82
83 83 self.tagscache = None
84 84 self.branchcache = None
85 85 self.nodetagscache = None
86 86 self.filterpats = {}
87 87 self.transhandle = None
88 88
89 89 def __getattr__(self, name):
90 90 if name == 'changelog':
91 91 self.changelog = changelog.changelog(self.sopener)
92 92 self.sopener.defversion = self.changelog.version
93 93 return self.changelog
94 94 if name == 'manifest':
95 95 self.changelog
96 96 self.manifest = manifest.manifest(self.sopener)
97 97 return self.manifest
98 98 if name == 'dirstate':
99 99 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
100 100 return self.dirstate
101 101 else:
102 102 raise AttributeError, name
103 103
104 104 def url(self):
105 105 return 'file:' + self.root
106 106
107 107 def hook(self, name, throw=False, **args):
108 108 return hook.hook(self.ui, self, name, throw, **args)
109 109
110 110 tag_disallowed = ':\r\n'
111 111
112 112 def _tag(self, name, node, message, local, user, date, parent=None):
113 113 use_dirstate = parent is None
114 114
115 115 for c in self.tag_disallowed:
116 116 if c in name:
117 117 raise util.Abort(_('%r cannot be used in a tag name') % c)
118 118
119 119 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
120 120
121 121 def writetag(fp, name, munge, prevtags):
122 122 if prevtags and prevtags[-1] != '\n':
123 123 fp.write('\n')
124 124 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
125 125 fp.close()
126 126 self.hook('tag', node=hex(node), tag=name, local=local)
127
127
128 128 prevtags = ''
129 129 if local:
130 130 try:
131 131 fp = self.opener('localtags', 'r+')
132 132 except IOError, err:
133 133 fp = self.opener('localtags', 'a')
134 134 else:
135 135 prevtags = fp.read()
136 136
137 137 # local tags are stored in the current charset
138 138 writetag(fp, name, None, prevtags)
139 139 return
140 140
141 141 if use_dirstate:
142 142 try:
143 143 fp = self.wfile('.hgtags', 'rb+')
144 144 except IOError, err:
145 145 fp = self.wfile('.hgtags', 'ab')
146 146 else:
147 147 prevtags = fp.read()
148 148 else:
149 149 try:
150 150 prevtags = self.filectx('.hgtags', parent).data()
151 151 except revlog.LookupError:
152 152 pass
153 153 fp = self.wfile('.hgtags', 'wb')
154 154
155 155 # committed tags are stored in UTF-8
156 156 writetag(fp, name, util.fromlocal, prevtags)
157 157
158 158 if use_dirstate and self.dirstate.state('.hgtags') == '?':
159 159 self.add(['.hgtags'])
160 160
161 161 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent)
162 162
163 163 self.hook('tag', node=hex(node), tag=name, local=local)
164 164
165 165 return tagnode
166 166
167 167 def tag(self, name, node, message, local, user, date):
168 168 '''tag a revision with a symbolic name.
169 169
170 170 if local is True, the tag is stored in a per-repository file.
171 171 otherwise, it is stored in the .hgtags file, and a new
172 172 changeset is committed with the change.
173 173
174 174 keyword arguments:
175 175
176 176 local: whether to store tag in non-version-controlled file
177 177 (default False)
178 178
179 179 message: commit message to use if committing
180 180
181 181 user: name of user to use if committing
182 182
183 183 date: date tuple to use if committing'''
184 184
185 185 for x in self.status()[:5]:
186 186 if '.hgtags' in x:
187 187 raise util.Abort(_('working copy of .hgtags is changed '
188 188 '(please commit .hgtags manually)'))
189 189
190 190
191 191 self._tag(name, node, message, local, user, date)
192 192
193 193 def tags(self):
194 194 '''return a mapping of tag to node'''
195 195 if self.tagscache:
196 196 return self.tagscache
197 197
198 198 globaltags = {}
199 199
200 200 def readtags(lines, fn):
201 201 filetags = {}
202 202 count = 0
203 203
204 204 def warn(msg):
205 205 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
206 206
207 207 for l in lines:
208 208 count += 1
209 209 if not l:
210 210 continue
211 211 s = l.split(" ", 1)
212 212 if len(s) != 2:
213 213 warn(_("cannot parse entry"))
214 214 continue
215 215 node, key = s
216 216 key = util.tolocal(key.strip()) # stored in UTF-8
217 217 try:
218 218 bin_n = bin(node)
219 219 except TypeError:
220 220 warn(_("node '%s' is not well formed") % node)
221 221 continue
222 222 if bin_n not in self.changelog.nodemap:
223 223 warn(_("tag '%s' refers to unknown node") % key)
224 224 continue
225 225
226 226 h = []
227 227 if key in filetags:
228 228 n, h = filetags[key]
229 229 h.append(n)
230 230 filetags[key] = (bin_n, h)
231 231
232 232 for k, nh in filetags.items():
233 233 if k not in globaltags:
234 234 globaltags[k] = nh
235 235 continue
236 236 # we prefer the global tag if:
237 237 # it supercedes us OR
238 238 # mutual supercedes and it has a higher rank
239 239 # otherwise we win because we're tip-most
240 240 an, ah = nh
241 241 bn, bh = globaltags[k]
242 242 if (bn != an and an in bh and
243 243 (bn not in ah or len(bh) > len(ah))):
244 244 an = bn
245 245 ah.extend([n for n in bh if n not in ah])
246 246 globaltags[k] = an, ah
247 247
248 248 # read the tags file from each head, ending with the tip
249 249 f = None
250 250 for rev, node, fnode in self._hgtagsnodes():
251 251 f = (f and f.filectx(fnode) or
252 252 self.filectx('.hgtags', fileid=fnode))
253 253 readtags(f.data().splitlines(), f)
254 254
255 255 try:
256 256 data = util.fromlocal(self.opener("localtags").read())
257 257 # localtags are stored in the local character set
258 258 # while the internal tag table is stored in UTF-8
259 259 readtags(data.splitlines(), "localtags")
260 260 except IOError:
261 261 pass
262 262
263 263 self.tagscache = {}
264 264 for k,nh in globaltags.items():
265 265 n = nh[0]
266 266 if n != nullid:
267 267 self.tagscache[k] = n
268 268 self.tagscache['tip'] = self.changelog.tip()
269 269
270 270 return self.tagscache
271 271
272 272 def _hgtagsnodes(self):
273 273 heads = self.heads()
274 274 heads.reverse()
275 275 last = {}
276 276 ret = []
277 277 for node in heads:
278 278 c = self.changectx(node)
279 279 rev = c.rev()
280 280 try:
281 281 fnode = c.filenode('.hgtags')
282 282 except revlog.LookupError:
283 283 continue
284 284 ret.append((rev, node, fnode))
285 285 if fnode in last:
286 286 ret[last[fnode]] = None
287 287 last[fnode] = len(ret) - 1
288 288 return [item for item in ret if item]
289 289
290 290 def tagslist(self):
291 291 '''return a list of tags ordered by revision'''
292 292 l = []
293 293 for t, n in self.tags().items():
294 294 try:
295 295 r = self.changelog.rev(n)
296 296 except:
297 297 r = -2 # sort to the beginning of the list if unknown
298 298 l.append((r, t, n))
299 299 l.sort()
300 300 return [(t, n) for r, t, n in l]
301 301
302 302 def nodetags(self, node):
303 303 '''return the tags associated with a node'''
304 304 if not self.nodetagscache:
305 305 self.nodetagscache = {}
306 306 for t, n in self.tags().items():
307 307 self.nodetagscache.setdefault(n, []).append(t)
308 308 return self.nodetagscache.get(node, [])
309 309
310 310 def _branchtags(self):
311 311 partial, last, lrev = self._readbranchcache()
312 312
313 313 tiprev = self.changelog.count() - 1
314 314 if lrev != tiprev:
315 315 self._updatebranchcache(partial, lrev+1, tiprev+1)
316 316 self._writebranchcache(partial, self.changelog.tip(), tiprev)
317 317
318 318 return partial
319 319
320 320 def branchtags(self):
321 321 if self.branchcache is not None:
322 322 return self.branchcache
323 323
324 324 self.branchcache = {} # avoid recursion in changectx
325 325 partial = self._branchtags()
326 326
327 327 # the branch cache is stored on disk as UTF-8, but in the local
328 328 # charset internally
329 329 for k, v in partial.items():
330 330 self.branchcache[util.tolocal(k)] = v
331 331 return self.branchcache
332 332
333 333 def _readbranchcache(self):
334 334 partial = {}
335 335 try:
336 336 f = self.opener("branch.cache")
337 337 lines = f.read().split('\n')
338 338 f.close()
339 339 except (IOError, OSError):
340 340 return {}, nullid, nullrev
341 341
342 342 try:
343 343 last, lrev = lines.pop(0).split(" ", 1)
344 344 last, lrev = bin(last), int(lrev)
345 345 if not (lrev < self.changelog.count() and
346 346 self.changelog.node(lrev) == last): # sanity check
347 347 # invalidate the cache
348 348 raise ValueError('Invalid branch cache: unknown tip')
349 349 for l in lines:
350 350 if not l: continue
351 351 node, label = l.split(" ", 1)
352 352 partial[label.strip()] = bin(node)
353 353 except (KeyboardInterrupt, util.SignalInterrupt):
354 354 raise
355 355 except Exception, inst:
356 356 if self.ui.debugflag:
357 357 self.ui.warn(str(inst), '\n')
358 358 partial, last, lrev = {}, nullid, nullrev
359 359 return partial, last, lrev
360 360
361 361 def _writebranchcache(self, branches, tip, tiprev):
362 362 try:
363 363 f = self.opener("branch.cache", "w", atomictemp=True)
364 364 f.write("%s %s\n" % (hex(tip), tiprev))
365 365 for label, node in branches.iteritems():
366 366 f.write("%s %s\n" % (hex(node), label))
367 367 f.rename()
368 368 except (IOError, OSError):
369 369 pass
370 370
371 371 def _updatebranchcache(self, partial, start, end):
372 372 for r in xrange(start, end):
373 373 c = self.changectx(r)
374 374 b = c.branch()
375 375 partial[b] = c.node()
376 376
377 377 def lookup(self, key):
378 378 if key == '.':
379 379 key, second = self.dirstate.parents()
380 380 if key == nullid:
381 381 raise repo.RepoError(_("no revision checked out"))
382 382 if second != nullid:
383 383 self.ui.warn(_("warning: working directory has two parents, "
384 384 "tag '.' uses the first\n"))
385 385 elif key == 'null':
386 386 return nullid
387 387 n = self.changelog._match(key)
388 388 if n:
389 389 return n
390 390 if key in self.tags():
391 391 return self.tags()[key]
392 392 if key in self.branchtags():
393 393 return self.branchtags()[key]
394 394 n = self.changelog._partialmatch(key)
395 395 if n:
396 396 return n
397 397 raise repo.RepoError(_("unknown revision '%s'") % key)
398 398
399 399 def dev(self):
400 400 return os.lstat(self.path).st_dev
401 401
402 402 def local(self):
403 403 return True
404 404
405 405 def join(self, f):
406 406 return os.path.join(self.path, f)
407 407
408 408 def sjoin(self, f):
409 409 f = self.encodefn(f)
410 410 return os.path.join(self.spath, f)
411 411
412 412 def wjoin(self, f):
413 413 return os.path.join(self.root, f)
414 414
415 415 def file(self, f):
416 416 if f[0] == '/':
417 417 f = f[1:]
418 418 return filelog.filelog(self.sopener, f)
419 419
420 420 def changectx(self, changeid=None):
421 421 return context.changectx(self, changeid)
422 422
423 423 def workingctx(self):
424 424 return context.workingctx(self)
425 425
426 426 def parents(self, changeid=None):
427 427 '''
428 428 get list of changectxs for parents of changeid or working directory
429 429 '''
430 430 if changeid is None:
431 431 pl = self.dirstate.parents()
432 432 else:
433 433 n = self.changelog.lookup(changeid)
434 434 pl = self.changelog.parents(n)
435 435 if pl[1] == nullid:
436 436 return [self.changectx(pl[0])]
437 437 return [self.changectx(pl[0]), self.changectx(pl[1])]
438 438
439 439 def filectx(self, path, changeid=None, fileid=None):
440 440 """changeid can be a changeset revision, node, or tag.
441 441 fileid can be a file revision or node."""
442 442 return context.filectx(self, path, changeid, fileid)
443 443
444 444 def getcwd(self):
445 445 return self.dirstate.getcwd()
446 446
447 447 def pathto(self, f, cwd=None):
448 448 return self.dirstate.pathto(f, cwd)
449 449
450 450 def wfile(self, f, mode='r'):
451 451 return self.wopener(f, mode)
452 452
453 453 def _link(self, f):
454 454 return os.path.islink(self.wjoin(f))
455 455
456 456 def _filter(self, filter, filename, data):
457 457 if filter not in self.filterpats:
458 458 l = []
459 459 for pat, cmd in self.ui.configitems(filter):
460 460 mf = util.matcher(self.root, "", [pat], [], [])[1]
461 461 l.append((mf, cmd))
462 462 self.filterpats[filter] = l
463 463
464 464 for mf, cmd in self.filterpats[filter]:
465 465 if mf(filename):
466 466 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
467 467 data = util.filter(data, cmd)
468 468 break
469 469
470 470 return data
471 471
472 472 def wread(self, filename):
473 473 if self._link(filename):
474 474 data = os.readlink(self.wjoin(filename))
475 475 else:
476 476 data = self.wopener(filename, 'r').read()
477 477 return self._filter("encode", filename, data)
478 478
479 479 def wwrite(self, filename, data, flags):
480 480 data = self._filter("decode", filename, data)
481 481 if "l" in flags:
482 482 self.wopener.symlink(data, filename)
483 483 else:
484 484 try:
485 485 if self._link(filename):
486 486 os.unlink(self.wjoin(filename))
487 487 except OSError:
488 488 pass
489 489 self.wopener(filename, 'w').write(data)
490 490 util.set_exec(self.wjoin(filename), "x" in flags)
491 491
492 492 def wwritedata(self, filename, data):
493 493 return self._filter("decode", filename, data)
494 494
495 495 def transaction(self):
496 496 tr = self.transhandle
497 497 if tr != None and tr.running():
498 498 return tr.nest()
499 499
500 500 # save dirstate for rollback
501 501 try:
502 502 ds = self.opener("dirstate").read()
503 503 except IOError:
504 504 ds = ""
505 505 self.opener("journal.dirstate", "w").write(ds)
506 506
507 507 renames = [(self.sjoin("journal"), self.sjoin("undo")),
508 508 (self.join("journal.dirstate"), self.join("undo.dirstate"))]
509 509 tr = transaction.transaction(self.ui.warn, self.sopener,
510 510 self.sjoin("journal"),
511 511 aftertrans(renames))
512 512 self.transhandle = tr
513 513 return tr
514 514
515 515 def recover(self):
516 516 l = self.lock()
517 517 if os.path.exists(self.sjoin("journal")):
518 518 self.ui.status(_("rolling back interrupted transaction\n"))
519 519 transaction.rollback(self.sopener, self.sjoin("journal"))
520 520 self.invalidate()
521 521 return True
522 522 else:
523 523 self.ui.warn(_("no interrupted transaction available\n"))
524 524 return False
525 525
526 526 def rollback(self, wlock=None, lock=None):
527 527 if not wlock:
528 528 wlock = self.wlock()
529 529 if not lock:
530 530 lock = self.lock()
531 531 if os.path.exists(self.sjoin("undo")):
532 532 self.ui.status(_("rolling back last transaction\n"))
533 533 transaction.rollback(self.sopener, self.sjoin("undo"))
534 534 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
535 535 self.invalidate()
536 536 self.dirstate.invalidate()
537 537 else:
538 538 self.ui.warn(_("no rollback information available\n"))
539 539
540 540 def invalidate(self):
541 541 for a in "changelog manifest".split():
542 542 if hasattr(self, a):
543 543 self.__delattr__(a)
544 544 self.tagscache = None
545 545 self.nodetagscache = None
546 546
547 547 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
548 548 desc=None):
549 549 try:
550 550 l = lock.lock(lockname, 0, releasefn, desc=desc)
551 551 except lock.LockHeld, inst:
552 552 if not wait:
553 553 raise
554 554 self.ui.warn(_("waiting for lock on %s held by %r\n") %
555 555 (desc, inst.locker))
556 556 # default to 600 seconds timeout
557 557 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
558 558 releasefn, desc=desc)
559 559 if acquirefn:
560 560 acquirefn()
561 561 return l
562 562
563 563 def lock(self, wait=1):
564 564 return self.do_lock(self.sjoin("lock"), wait,
565 565 acquirefn=self.invalidate,
566 566 desc=_('repository %s') % self.origroot)
567 567
568 568 def wlock(self, wait=1):
569 569 return self.do_lock(self.join("wlock"), wait, self.dirstate.write,
570 570 self.dirstate.invalidate,
571 571 desc=_('working directory of %s') % self.origroot)
572 572
573 573 def filecommit(self, fn, manifest1, manifest2, linkrev, transaction, changelist):
574 574 """
575 575 commit an individual file as part of a larger transaction
576 576 """
577 577
578 578 t = self.wread(fn)
579 579 fl = self.file(fn)
580 580 fp1 = manifest1.get(fn, nullid)
581 581 fp2 = manifest2.get(fn, nullid)
582 582
583 583 meta = {}
584 584 cp = self.dirstate.copied(fn)
585 585 if cp:
586 586 # Mark the new revision of this file as a copy of another
587 587 # file. This copy data will effectively act as a parent
588 588 # of this new revision. If this is a merge, the first
589 589 # parent will be the nullid (meaning "look up the copy data")
590 590 # and the second one will be the other parent. For example:
591 591 #
592 592 # 0 --- 1 --- 3 rev1 changes file foo
593 593 # \ / rev2 renames foo to bar and changes it
594 594 # \- 2 -/ rev3 should have bar with all changes and
595 595 # should record that bar descends from
596 596 # bar in rev2 and foo in rev1
597 597 #
598 598 # this allows this merge to succeed:
599 599 #
600 600 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
601 601 # \ / merging rev3 and rev4 should use bar@rev2
602 602 # \- 2 --- 4 as the merge base
603 603 #
604 604 meta["copy"] = cp
605 605 if not manifest2: # not a branch merge
606 606 meta["copyrev"] = hex(manifest1.get(cp, nullid))
607 607 fp2 = nullid
608 608 elif fp2 != nullid: # copied on remote side
609 609 meta["copyrev"] = hex(manifest1.get(cp, nullid))
610 610 elif fp1 != nullid: # copied on local side, reversed
611 611 meta["copyrev"] = hex(manifest2.get(cp))
612 612 fp2 = fp1
613 613 else: # directory rename
614 614 meta["copyrev"] = hex(manifest1.get(cp, nullid))
615 615 self.ui.debug(_(" %s: copy %s:%s\n") %
616 616 (fn, cp, meta["copyrev"]))
617 617 fp1 = nullid
618 618 elif fp2 != nullid:
619 619 # is one parent an ancestor of the other?
620 620 fpa = fl.ancestor(fp1, fp2)
621 621 if fpa == fp1:
622 622 fp1, fp2 = fp2, nullid
623 623 elif fpa == fp2:
624 624 fp2 = nullid
625 625
626 626 # is the file unmodified from the parent? report existing entry
627 627 if fp2 == nullid and not fl.cmp(fp1, t):
628 628 return fp1
629 629
630 630 changelist.append(fn)
631 631 return fl.add(t, meta, transaction, linkrev, fp1, fp2)
632 632
633 633 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None, extra={}):
634 634 if p1 is None:
635 635 p1, p2 = self.dirstate.parents()
636 636 return self.commit(files=files, text=text, user=user, date=date,
637 637 p1=p1, p2=p2, wlock=wlock, extra=extra)
638 638
639 639 def commit(self, files=None, text="", user=None, date=None,
640 640 match=util.always, force=False, lock=None, wlock=None,
641 641 force_editor=False, p1=None, p2=None, extra={}):
642 642
643 643 commit = []
644 644 remove = []
645 645 changed = []
646 646 use_dirstate = (p1 is None) # not rawcommit
647 647 extra = extra.copy()
648 648
649 649 if use_dirstate:
650 650 if files:
651 651 for f in files:
652 652 s = self.dirstate.state(f)
653 653 if s in 'nmai':
654 654 commit.append(f)
655 655 elif s == 'r':
656 656 remove.append(f)
657 657 else:
658 658 self.ui.warn(_("%s not tracked!\n") % f)
659 659 else:
660 660 changes = self.status(match=match)[:5]
661 661 modified, added, removed, deleted, unknown = changes
662 662 commit = modified + added
663 663 remove = removed
664 664 else:
665 665 commit = files
666 666
667 667 if use_dirstate:
668 668 p1, p2 = self.dirstate.parents()
669 669 update_dirstate = True
670 670 else:
671 671 p1, p2 = p1, p2 or nullid
672 672 update_dirstate = (self.dirstate.parents()[0] == p1)
673 673
674 674 c1 = self.changelog.read(p1)
675 675 c2 = self.changelog.read(p2)
676 676 m1 = self.manifest.read(c1[0]).copy()
677 677 m2 = self.manifest.read(c2[0])
678 678
679 679 if use_dirstate:
680 680 branchname = self.workingctx().branch()
681 681 try:
682 682 branchname = branchname.decode('UTF-8').encode('UTF-8')
683 683 except UnicodeDecodeError:
684 684 raise util.Abort(_('branch name not in UTF-8!'))
685 685 else:
686 686 branchname = ""
687 687
688 688 if use_dirstate:
689 689 oldname = c1[5].get("branch") # stored in UTF-8
690 690 if (not commit and not remove and not force and p2 == nullid
691 691 and branchname == oldname):
692 692 self.ui.status(_("nothing changed\n"))
693 693 return None
694 694
695 695 xp1 = hex(p1)
696 696 if p2 == nullid: xp2 = ''
697 697 else: xp2 = hex(p2)
698 698
699 699 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
700 700
701 701 if not wlock:
702 702 wlock = self.wlock()
703 703 if not lock:
704 704 lock = self.lock()
705 705 tr = self.transaction()
706 706
707 707 # check in files
708 708 new = {}
709 709 linkrev = self.changelog.count()
710 710 commit.sort()
711 711 is_exec = util.execfunc(self.root, m1.execf)
712 712 is_link = util.linkfunc(self.root, m1.linkf)
713 713 for f in commit:
714 714 self.ui.note(f + "\n")
715 715 try:
716 716 new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
717 717 new_exec = is_exec(f)
718 718 new_link = is_link(f)
719 719 if not changed or changed[-1] != f:
720 720 # mention the file in the changelog if some flag changed,
721 721 # even if there was no content change.
722 722 old_exec = m1.execf(f)
723 723 old_link = m1.linkf(f)
724 724 if old_exec != new_exec or old_link != new_link:
725 725 changed.append(f)
726 726 m1.set(f, new_exec, new_link)
727 727 except (OSError, IOError):
728 728 if use_dirstate:
729 729 self.ui.warn(_("trouble committing %s!\n") % f)
730 730 raise
731 731 else:
732 732 remove.append(f)
733 733
734 734 # update manifest
735 735 m1.update(new)
736 736 remove.sort()
737 737 removed = []
738 738
739 739 for f in remove:
740 740 if f in m1:
741 741 del m1[f]
742 742 removed.append(f)
743 743 elif f in m2:
744 744 removed.append(f)
745 745 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed))
746 746
747 747 # add changeset
748 748 new = new.keys()
749 749 new.sort()
750 750
751 751 user = user or self.ui.username()
752 752 if not text or force_editor:
753 753 edittext = []
754 754 if text:
755 755 edittext.append(text)
756 756 edittext.append("")
757 757 edittext.append("HG: user: %s" % user)
758 758 if p2 != nullid:
759 759 edittext.append("HG: branch merge")
760 760 if branchname:
761 761 edittext.append("HG: branch %s" % util.tolocal(branchname))
762 762 edittext.extend(["HG: changed %s" % f for f in changed])
763 763 edittext.extend(["HG: removed %s" % f for f in removed])
764 764 if not changed and not remove:
765 765 edittext.append("HG: no files changed")
766 766 edittext.append("")
767 767 # run editor in the repository root
768 768 olddir = os.getcwd()
769 769 os.chdir(self.root)
770 770 text = self.ui.edit("\n".join(edittext), user)
771 771 os.chdir(olddir)
772 772
773 773 lines = [line.rstrip() for line in text.rstrip().splitlines()]
774 774 while lines and not lines[0]:
775 775 del lines[0]
776 776 if not lines:
777 777 return None
778 778 text = '\n'.join(lines)
779 779 if branchname:
780 780 extra["branch"] = branchname
781 781 n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
782 782 user, date, extra)
783 783 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
784 784 parent2=xp2)
785 785 tr.close()
786 786
787 787 if self.branchcache and "branch" in extra:
788 788 self.branchcache[util.tolocal(extra["branch"])] = n
789 789
790 790 if use_dirstate or update_dirstate:
791 791 self.dirstate.setparents(n)
792 792 if use_dirstate:
793 793 self.dirstate.update(new, "n")
794 794 self.dirstate.forget(removed)
795 795
796 796 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
797 797 return n
798 798
799 799 def walk(self, node=None, files=[], match=util.always, badmatch=None):
800 800 '''
801 801 walk recursively through the directory tree or a given
802 802 changeset, finding all files matched by the match
803 803 function
804 804
805 805 results are yielded in a tuple (src, filename), where src
806 806 is one of:
807 807 'f' the file was found in the directory tree
808 808 'm' the file was only in the dirstate and not in the tree
809 809 'b' file was not found and matched badmatch
810 810 '''
811 811
812 812 if node:
813 813 fdict = dict.fromkeys(files)
814 814 # for dirstate.walk, files=['.'] means "walk the whole tree".
815 815 # follow that here, too
816 816 fdict.pop('.', None)
817 817 mdict = self.manifest.read(self.changelog.read(node)[0])
818 818 mfiles = mdict.keys()
819 819 mfiles.sort()
820 820 for fn in mfiles:
821 821 for ffn in fdict:
822 822 # match if the file is the exact name or a directory
823 823 if ffn == fn or fn.startswith("%s/" % ffn):
824 824 del fdict[ffn]
825 825 break
826 826 if match(fn):
827 827 yield 'm', fn
828 828 ffiles = fdict.keys()
829 829 ffiles.sort()
830 830 for fn in ffiles:
831 831 if badmatch and badmatch(fn):
832 832 if match(fn):
833 833 yield 'b', fn
834 834 else:
835 835 self.ui.warn(_('%s: No such file in rev %s\n')
836 836 % (self.pathto(fn), short(node)))
837 837 else:
838 838 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
839 839 yield src, fn
840 840
841 841 def status(self, node1=None, node2=None, files=[], match=util.always,
842 842 wlock=None, list_ignored=False, list_clean=False):
843 843 """return status of files between two nodes or node and working directory
844 844
845 845 If node1 is None, use the first dirstate parent instead.
846 846 If node2 is None, compare node1 with working directory.
847 847 """
848 848
849 849 def fcmp(fn, getnode):
850 850 t1 = self.wread(fn)
851 851 return self.file(fn).cmp(getnode(fn), t1)
852 852
853 853 def mfmatches(node):
854 854 change = self.changelog.read(node)
855 855 mf = self.manifest.read(change[0]).copy()
856 856 for fn in mf.keys():
857 857 if not match(fn):
858 858 del mf[fn]
859 859 return mf
860 860
861 861 modified, added, removed, deleted, unknown = [], [], [], [], []
862 862 ignored, clean = [], []
863 863
864 864 compareworking = False
865 865 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
866 866 compareworking = True
867 867
868 868 if not compareworking:
869 869 # read the manifest from node1 before the manifest from node2,
870 870 # so that we'll hit the manifest cache if we're going through
871 871 # all the revisions in parent->child order.
872 872 mf1 = mfmatches(node1)
873 873
874 874 mywlock = False
875 875
876 876 # are we comparing the working directory?
877 877 if not node2:
878 878 (lookup, modified, added, removed, deleted, unknown,
879 879 ignored, clean) = self.dirstate.status(files, match,
880 880 list_ignored, list_clean)
881 881
882 882 # are we comparing working dir against its parent?
883 883 if compareworking:
884 884 if lookup:
885 885 # do a full compare of any files that might have changed
886 886 mnode = self.changelog.read(self.dirstate.parents()[0])[0]
887 887 getnode = lambda fn: (self.manifest.find(mnode, fn)[0] or
888 888 nullid)
889 889 for f in lookup:
890 890 if fcmp(f, getnode):
891 891 modified.append(f)
892 892 else:
893 893 if list_clean:
894 894 clean.append(f)
895 895 if not wlock and not mywlock:
896 896 mywlock = True
897 897 try:
898 898 wlock = self.wlock(wait=0)
899 899 except lock.LockException:
900 900 pass
901 901 if wlock:
902 902 self.dirstate.update([f], "n")
903 903 else:
904 904 # we are comparing working dir against non-parent
905 905 # generate a pseudo-manifest for the working dir
906 906 # XXX: create it in dirstate.py ?
907 907 mf2 = mfmatches(self.dirstate.parents()[0])
908 908 is_exec = util.execfunc(self.root, mf2.execf)
909 909 is_link = util.linkfunc(self.root, mf2.linkf)
910 910 for f in lookup + modified + added:
911 911 mf2[f] = ""
912 912 mf2.set(f, is_exec(f), is_link(f))
913 913 for f in removed:
914 914 if f in mf2:
915 915 del mf2[f]
916 916
917 917 if mywlock and wlock:
918 918 wlock.release()
919 919 else:
920 920 # we are comparing two revisions
921 921 mf2 = mfmatches(node2)
922 922
923 923 if not compareworking:
924 924 # flush lists from dirstate before comparing manifests
925 925 modified, added, clean = [], [], []
926 926
927 927 # make sure to sort the files so we talk to the disk in a
928 928 # reasonable order
929 929 mf2keys = mf2.keys()
930 930 mf2keys.sort()
931 931 getnode = lambda fn: mf1.get(fn, nullid)
932 932 for fn in mf2keys:
933 933 if mf1.has_key(fn):
934 934 if (mf1.flags(fn) != mf2.flags(fn) or
935 935 (mf1[fn] != mf2[fn] and
936 936 (mf2[fn] != "" or fcmp(fn, getnode)))):
937 937 modified.append(fn)
938 938 elif list_clean:
939 939 clean.append(fn)
940 940 del mf1[fn]
941 941 else:
942 942 added.append(fn)
943 943
944 944 removed = mf1.keys()
945 945
946 946 # sort and return results:
947 947 for l in modified, added, removed, deleted, unknown, ignored, clean:
948 948 l.sort()
949 949 return (modified, added, removed, deleted, unknown, ignored, clean)
950 950
951 951 def add(self, list, wlock=None):
952 952 if not wlock:
953 953 wlock = self.wlock()
954 954 for f in list:
955 955 p = self.wjoin(f)
956 956 try:
957 957 st = os.lstat(p)
958 958 except:
959 959 self.ui.warn(_("%s does not exist!\n") % f)
960 960 continue
961 961 if st.st_size > 10000000:
962 962 self.ui.warn(_("%s: files over 10MB may cause memory and"
963 963 " performance problems\n"
964 964 "(use 'hg revert %s' to unadd the file)\n")
965 965 % (f, f))
966 966 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
967 967 self.ui.warn(_("%s not added: only files and symlinks "
968 968 "supported currently\n") % f)
969 969 elif self.dirstate.state(f) in 'an':
970 970 self.ui.warn(_("%s already tracked!\n") % f)
971 971 else:
972 972 self.dirstate.update([f], "a")
973 973
974 974 def forget(self, list, wlock=None):
975 975 if not wlock:
976 976 wlock = self.wlock()
977 977 for f in list:
978 978 if self.dirstate.state(f) not in 'ai':
979 979 self.ui.warn(_("%s not added!\n") % f)
980 980 else:
981 981 self.dirstate.forget([f])
982 982
983 983 def remove(self, list, unlink=False, wlock=None):
984 984 if unlink:
985 985 for f in list:
986 986 try:
987 987 util.unlink(self.wjoin(f))
988 988 except OSError, inst:
989 989 if inst.errno != errno.ENOENT:
990 990 raise
991 991 if not wlock:
992 992 wlock = self.wlock()
993 993 for f in list:
994 994 if unlink and os.path.exists(self.wjoin(f)):
995 995 self.ui.warn(_("%s still exists!\n") % f)
996 996 elif self.dirstate.state(f) == 'a':
997 997 self.dirstate.forget([f])
998 998 elif f not in self.dirstate:
999 999 self.ui.warn(_("%s not tracked!\n") % f)
1000 1000 else:
1001 1001 self.dirstate.update([f], "r")
1002 1002
1003 1003 def undelete(self, list, wlock=None):
1004 1004 p = self.dirstate.parents()[0]
1005 1005 mn = self.changelog.read(p)[0]
1006 1006 m = self.manifest.read(mn)
1007 1007 if not wlock:
1008 1008 wlock = self.wlock()
1009 1009 for f in list:
1010 1010 if self.dirstate.state(f) not in "r":
1011 1011 self.ui.warn("%s not removed!\n" % f)
1012 1012 else:
1013 1013 t = self.file(f).read(m[f])
1014 1014 self.wwrite(f, t, m.flags(f))
1015 1015 self.dirstate.update([f], "n")
1016 1016
1017 1017 def copy(self, source, dest, wlock=None):
1018 1018 p = self.wjoin(dest)
1019 1019 if not (os.path.exists(p) or os.path.islink(p)):
1020 1020 self.ui.warn(_("%s does not exist!\n") % dest)
1021 1021 elif not (os.path.isfile(p) or os.path.islink(p)):
1022 1022 self.ui.warn(_("copy failed: %s is not a file or a "
1023 1023 "symbolic link\n") % dest)
1024 1024 else:
1025 1025 if not wlock:
1026 1026 wlock = self.wlock()
1027 1027 if self.dirstate.state(dest) == '?':
1028 1028 self.dirstate.update([dest], "a")
1029 1029 self.dirstate.copy(source, dest)
1030 1030
1031 1031 def heads(self, start=None):
1032 1032 heads = self.changelog.heads(start)
1033 1033 # sort the output in rev descending order
1034 1034 heads = [(-self.changelog.rev(h), h) for h in heads]
1035 1035 heads.sort()
1036 1036 return [n for (r, n) in heads]
1037 1037
1038 1038 def branchheads(self, branch, start=None):
1039 1039 branches = self.branchtags()
1040 1040 if branch not in branches:
1041 1041 return []
1042 1042 # The basic algorithm is this:
1043 1043 #
1044 1044 # Start from the branch tip since there are no later revisions that can
1045 1045 # possibly be in this branch, and the tip is a guaranteed head.
1046 1046 #
1047 1047 # Remember the tip's parents as the first ancestors, since these by
1048 1048 # definition are not heads.
1049 1049 #
1050 1050 # Step backwards from the brach tip through all the revisions. We are
1051 1051 # guaranteed by the rules of Mercurial that we will now be visiting the
1052 1052 # nodes in reverse topological order (children before parents).
1053 1053 #
1054 1054 # If a revision is one of the ancestors of a head then we can toss it
1055 1055 # out of the ancestors set (we've already found it and won't be
1056 1056 # visiting it again) and put its parents in the ancestors set.
1057 1057 #
1058 1058 # Otherwise, if a revision is in the branch it's another head, since it
1059 1059 # wasn't in the ancestor list of an existing head. So add it to the
1060 1060 # head list, and add its parents to the ancestor list.
1061 1061 #
1062 1062 # If it is not in the branch ignore it.
1063 1063 #
1064 1064 # Once we have a list of heads, use nodesbetween to filter out all the
1065 1065 # heads that cannot be reached from startrev. There may be a more
1066 1066 # efficient way to do this as part of the previous algorithm.
1067 1067
1068 1068 set = util.set
1069 1069 heads = [self.changelog.rev(branches[branch])]
1070 1070 # Don't care if ancestors contains nullrev or not.
1071 1071 ancestors = set(self.changelog.parentrevs(heads[0]))
1072 1072 for rev in xrange(heads[0] - 1, nullrev, -1):
1073 1073 if rev in ancestors:
1074 1074 ancestors.update(self.changelog.parentrevs(rev))
1075 1075 ancestors.remove(rev)
1076 1076 elif self.changectx(rev).branch() == branch:
1077 1077 heads.append(rev)
1078 1078 ancestors.update(self.changelog.parentrevs(rev))
1079 1079 heads = [self.changelog.node(rev) for rev in heads]
1080 1080 if start is not None:
1081 1081 heads = self.changelog.nodesbetween([start], heads)[2]
1082 1082 return heads
1083 1083
1084 1084 def branches(self, nodes):
1085 1085 if not nodes:
1086 1086 nodes = [self.changelog.tip()]
1087 1087 b = []
1088 1088 for n in nodes:
1089 1089 t = n
1090 1090 while 1:
1091 1091 p = self.changelog.parents(n)
1092 1092 if p[1] != nullid or p[0] == nullid:
1093 1093 b.append((t, n, p[0], p[1]))
1094 1094 break
1095 1095 n = p[0]
1096 1096 return b
1097 1097
1098 1098 def between(self, pairs):
1099 1099 r = []
1100 1100
1101 1101 for top, bottom in pairs:
1102 1102 n, l, i = top, [], 0
1103 1103 f = 1
1104 1104
1105 1105 while n != bottom:
1106 1106 p = self.changelog.parents(n)[0]
1107 1107 if i == f:
1108 1108 l.append(n)
1109 1109 f = f * 2
1110 1110 n = p
1111 1111 i += 1
1112 1112
1113 1113 r.append(l)
1114 1114
1115 1115 return r
1116 1116
1117 1117 def findincoming(self, remote, base=None, heads=None, force=False):
1118 1118 """Return list of roots of the subsets of missing nodes from remote
1119 1119
1120 1120 If base dict is specified, assume that these nodes and their parents
1121 1121 exist on the remote side and that no child of a node of base exists
1122 1122 in both remote and self.
1123 1123 Furthermore base will be updated to include the nodes that exists
1124 1124 in self and remote but no children exists in self and remote.
1125 1125 If a list of heads is specified, return only nodes which are heads
1126 1126 or ancestors of these heads.
1127 1127
1128 1128 All the ancestors of base are in self and in remote.
1129 1129 All the descendants of the list returned are missing in self.
1130 1130 (and so we know that the rest of the nodes are missing in remote, see
1131 1131 outgoing)
1132 1132 """
1133 1133 m = self.changelog.nodemap
1134 1134 search = []
1135 1135 fetch = {}
1136 1136 seen = {}
1137 1137 seenbranch = {}
1138 1138 if base == None:
1139 1139 base = {}
1140 1140
1141 1141 if not heads:
1142 1142 heads = remote.heads()
1143 1143
1144 1144 if self.changelog.tip() == nullid:
1145 1145 base[nullid] = 1
1146 1146 if heads != [nullid]:
1147 1147 return [nullid]
1148 1148 return []
1149 1149
1150 1150 # assume we're closer to the tip than the root
1151 1151 # and start by examining the heads
1152 1152 self.ui.status(_("searching for changes\n"))
1153 1153
1154 1154 unknown = []
1155 1155 for h in heads:
1156 1156 if h not in m:
1157 1157 unknown.append(h)
1158 1158 else:
1159 1159 base[h] = 1
1160 1160
1161 1161 if not unknown:
1162 1162 return []
1163 1163
1164 1164 req = dict.fromkeys(unknown)
1165 1165 reqcnt = 0
1166 1166
1167 1167 # search through remote branches
1168 1168 # a 'branch' here is a linear segment of history, with four parts:
1169 1169 # head, root, first parent, second parent
1170 1170 # (a branch always has two parents (or none) by definition)
1171 1171 unknown = remote.branches(unknown)
1172 1172 while unknown:
1173 1173 r = []
1174 1174 while unknown:
1175 1175 n = unknown.pop(0)
1176 1176 if n[0] in seen:
1177 1177 continue
1178 1178
1179 1179 self.ui.debug(_("examining %s:%s\n")
1180 1180 % (short(n[0]), short(n[1])))
1181 1181 if n[0] == nullid: # found the end of the branch
1182 1182 pass
1183 1183 elif n in seenbranch:
1184 1184 self.ui.debug(_("branch already found\n"))
1185 1185 continue
1186 1186 elif n[1] and n[1] in m: # do we know the base?
1187 1187 self.ui.debug(_("found incomplete branch %s:%s\n")
1188 1188 % (short(n[0]), short(n[1])))
1189 1189 search.append(n) # schedule branch range for scanning
1190 1190 seenbranch[n] = 1
1191 1191 else:
1192 1192 if n[1] not in seen and n[1] not in fetch:
1193 1193 if n[2] in m and n[3] in m:
1194 1194 self.ui.debug(_("found new changeset %s\n") %
1195 1195 short(n[1]))
1196 1196 fetch[n[1]] = 1 # earliest unknown
1197 1197 for p in n[2:4]:
1198 1198 if p in m:
1199 1199 base[p] = 1 # latest known
1200 1200
1201 1201 for p in n[2:4]:
1202 1202 if p not in req and p not in m:
1203 1203 r.append(p)
1204 1204 req[p] = 1
1205 1205 seen[n[0]] = 1
1206 1206
1207 1207 if r:
1208 1208 reqcnt += 1
1209 1209 self.ui.debug(_("request %d: %s\n") %
1210 1210 (reqcnt, " ".join(map(short, r))))
1211 1211 for p in xrange(0, len(r), 10):
1212 1212 for b in remote.branches(r[p:p+10]):
1213 1213 self.ui.debug(_("received %s:%s\n") %
1214 1214 (short(b[0]), short(b[1])))
1215 1215 unknown.append(b)
1216 1216
1217 1217 # do binary search on the branches we found
1218 1218 while search:
1219 1219 n = search.pop(0)
1220 1220 reqcnt += 1
1221 1221 l = remote.between([(n[0], n[1])])[0]
1222 1222 l.append(n[1])
1223 1223 p = n[0]
1224 1224 f = 1
1225 1225 for i in l:
1226 1226 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1227 1227 if i in m:
1228 1228 if f <= 2:
1229 1229 self.ui.debug(_("found new branch changeset %s\n") %
1230 1230 short(p))
1231 1231 fetch[p] = 1
1232 1232 base[i] = 1
1233 1233 else:
1234 1234 self.ui.debug(_("narrowed branch search to %s:%s\n")
1235 1235 % (short(p), short(i)))
1236 1236 search.append((p, i))
1237 1237 break
1238 1238 p, f = i, f * 2
1239 1239
1240 1240 # sanity check our fetch list
1241 1241 for f in fetch.keys():
1242 1242 if f in m:
1243 1243 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1244 1244
1245 1245 if base.keys() == [nullid]:
1246 1246 if force:
1247 1247 self.ui.warn(_("warning: repository is unrelated\n"))
1248 1248 else:
1249 1249 raise util.Abort(_("repository is unrelated"))
1250 1250
1251 1251 self.ui.debug(_("found new changesets starting at ") +
1252 1252 " ".join([short(f) for f in fetch]) + "\n")
1253 1253
1254 1254 self.ui.debug(_("%d total queries\n") % reqcnt)
1255 1255
1256 1256 return fetch.keys()
1257 1257
1258 1258 def findoutgoing(self, remote, base=None, heads=None, force=False):
1259 1259 """Return list of nodes that are roots of subsets not in remote
1260 1260
1261 1261 If base dict is specified, assume that these nodes and their parents
1262 1262 exist on the remote side.
1263 1263 If a list of heads is specified, return only nodes which are heads
1264 1264 or ancestors of these heads, and return a second element which
1265 1265 contains all remote heads which get new children.
1266 1266 """
1267 1267 if base == None:
1268 1268 base = {}
1269 1269 self.findincoming(remote, base, heads, force=force)
1270 1270
1271 1271 self.ui.debug(_("common changesets up to ")
1272 1272 + " ".join(map(short, base.keys())) + "\n")
1273 1273
1274 1274 remain = dict.fromkeys(self.changelog.nodemap)
1275 1275
1276 1276 # prune everything remote has from the tree
1277 1277 del remain[nullid]
1278 1278 remove = base.keys()
1279 1279 while remove:
1280 1280 n = remove.pop(0)
1281 1281 if n in remain:
1282 1282 del remain[n]
1283 1283 for p in self.changelog.parents(n):
1284 1284 remove.append(p)
1285 1285
1286 1286 # find every node whose parents have been pruned
1287 1287 subset = []
1288 1288 # find every remote head that will get new children
1289 1289 updated_heads = {}
1290 1290 for n in remain:
1291 1291 p1, p2 = self.changelog.parents(n)
1292 1292 if p1 not in remain and p2 not in remain:
1293 1293 subset.append(n)
1294 1294 if heads:
1295 1295 if p1 in heads:
1296 1296 updated_heads[p1] = True
1297 1297 if p2 in heads:
1298 1298 updated_heads[p2] = True
1299 1299
1300 1300 # this is the set of all roots we have to push
1301 1301 if heads:
1302 1302 return subset, updated_heads.keys()
1303 1303 else:
1304 1304 return subset
1305 1305
1306 1306 def pull(self, remote, heads=None, force=False, lock=None):
1307 1307 mylock = False
1308 1308 if not lock:
1309 1309 lock = self.lock()
1310 1310 mylock = True
1311 1311
1312 1312 try:
1313 1313 fetch = self.findincoming(remote, force=force)
1314 1314 if fetch == [nullid]:
1315 1315 self.ui.status(_("requesting all changes\n"))
1316 1316
1317 1317 if not fetch:
1318 1318 self.ui.status(_("no changes found\n"))
1319 1319 return 0
1320 1320
1321 1321 if heads is None:
1322 1322 cg = remote.changegroup(fetch, 'pull')
1323 1323 else:
1324 1324 if 'changegroupsubset' not in remote.capabilities:
1325 1325 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1326 1326 cg = remote.changegroupsubset(fetch, heads, 'pull')
1327 1327 return self.addchangegroup(cg, 'pull', remote.url())
1328 1328 finally:
1329 1329 if mylock:
1330 1330 lock.release()
1331 1331
1332 1332 def push(self, remote, force=False, revs=None):
1333 1333 # there are two ways to push to remote repo:
1334 1334 #
1335 1335 # addchangegroup assumes local user can lock remote
1336 1336 # repo (local filesystem, old ssh servers).
1337 1337 #
1338 1338 # unbundle assumes local user cannot lock remote repo (new ssh
1339 1339 # servers, http servers).
1340 1340
1341 1341 if remote.capable('unbundle'):
1342 1342 return self.push_unbundle(remote, force, revs)
1343 1343 return self.push_addchangegroup(remote, force, revs)
1344 1344
1345 1345 def prepush(self, remote, force, revs):
1346 1346 base = {}
1347 1347 remote_heads = remote.heads()
1348 1348 inc = self.findincoming(remote, base, remote_heads, force=force)
1349 1349
1350 1350 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1351 1351 if revs is not None:
1352 1352 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1353 1353 else:
1354 1354 bases, heads = update, self.changelog.heads()
1355 1355
1356 1356 if not bases:
1357 1357 self.ui.status(_("no changes found\n"))
1358 1358 return None, 1
1359 1359 elif not force:
1360 1360 # check if we're creating new remote heads
1361 1361 # to be a remote head after push, node must be either
1362 1362 # - unknown locally
1363 1363 # - a local outgoing head descended from update
1364 1364 # - a remote head that's known locally and not
1365 1365 # ancestral to an outgoing head
1366 1366
1367 1367 warn = 0
1368 1368
1369 1369 if remote_heads == [nullid]:
1370 1370 warn = 0
1371 1371 elif not revs and len(heads) > len(remote_heads):
1372 1372 warn = 1
1373 1373 else:
1374 1374 newheads = list(heads)
1375 1375 for r in remote_heads:
1376 1376 if r in self.changelog.nodemap:
1377 1377 desc = self.changelog.heads(r, heads)
1378 1378 l = [h for h in heads if h in desc]
1379 1379 if not l:
1380 1380 newheads.append(r)
1381 1381 else:
1382 1382 newheads.append(r)
1383 1383 if len(newheads) > len(remote_heads):
1384 1384 warn = 1
1385 1385
1386 1386 if warn:
1387 1387 self.ui.warn(_("abort: push creates new remote branches!\n"))
1388 1388 self.ui.status(_("(did you forget to merge?"
1389 1389 " use push -f to force)\n"))
1390 1390 return None, 1
1391 1391 elif inc:
1392 1392 self.ui.warn(_("note: unsynced remote changes!\n"))
1393 1393
1394 1394
1395 1395 if revs is None:
1396 1396 cg = self.changegroup(update, 'push')
1397 1397 else:
1398 1398 cg = self.changegroupsubset(update, revs, 'push')
1399 1399 return cg, remote_heads
1400 1400
1401 1401 def push_addchangegroup(self, remote, force, revs):
1402 1402 lock = remote.lock()
1403 1403
1404 1404 ret = self.prepush(remote, force, revs)
1405 1405 if ret[0] is not None:
1406 1406 cg, remote_heads = ret
1407 1407 return remote.addchangegroup(cg, 'push', self.url())
1408 1408 return ret[1]
1409 1409
1410 1410 def push_unbundle(self, remote, force, revs):
1411 1411 # local repo finds heads on server, finds out what revs it
1412 1412 # must push. once revs transferred, if server finds it has
1413 1413 # different heads (someone else won commit/push race), server
1414 1414 # aborts.
1415 1415
1416 1416 ret = self.prepush(remote, force, revs)
1417 1417 if ret[0] is not None:
1418 1418 cg, remote_heads = ret
1419 1419 if force: remote_heads = ['force']
1420 1420 return remote.unbundle(cg, remote_heads, 'push')
1421 1421 return ret[1]
1422 1422
1423 1423 def changegroupinfo(self, nodes):
1424 1424 self.ui.note(_("%d changesets found\n") % len(nodes))
1425 1425 if self.ui.debugflag:
1426 1426 self.ui.debug(_("List of changesets:\n"))
1427 1427 for node in nodes:
1428 1428 self.ui.debug("%s\n" % hex(node))
1429 1429
1430 1430 def changegroupsubset(self, bases, heads, source):
1431 1431 """This function generates a changegroup consisting of all the nodes
1432 1432 that are descendents of any of the bases, and ancestors of any of
1433 1433 the heads.
1434 1434
1435 1435 It is fairly complex as determining which filenodes and which
1436 1436 manifest nodes need to be included for the changeset to be complete
1437 1437 is non-trivial.
1438 1438
1439 1439 Another wrinkle is doing the reverse, figuring out which changeset in
1440 1440 the changegroup a particular filenode or manifestnode belongs to."""
1441 1441
1442 1442 self.hook('preoutgoing', throw=True, source=source)
1443 1443
1444 1444 # Set up some initial variables
1445 1445 # Make it easy to refer to self.changelog
1446 1446 cl = self.changelog
1447 1447 # msng is short for missing - compute the list of changesets in this
1448 1448 # changegroup.
1449 1449 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1450 1450 self.changegroupinfo(msng_cl_lst)
1451 1451 # Some bases may turn out to be superfluous, and some heads may be
1452 1452 # too. nodesbetween will return the minimal set of bases and heads
1453 1453 # necessary to re-create the changegroup.
1454 1454
1455 1455 # Known heads are the list of heads that it is assumed the recipient
1456 1456 # of this changegroup will know about.
1457 1457 knownheads = {}
1458 1458 # We assume that all parents of bases are known heads.
1459 1459 for n in bases:
1460 1460 for p in cl.parents(n):
1461 1461 if p != nullid:
1462 1462 knownheads[p] = 1
1463 1463 knownheads = knownheads.keys()
1464 1464 if knownheads:
1465 1465 # Now that we know what heads are known, we can compute which
1466 1466 # changesets are known. The recipient must know about all
1467 1467 # changesets required to reach the known heads from the null
1468 1468 # changeset.
1469 1469 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1470 1470 junk = None
1471 1471 # Transform the list into an ersatz set.
1472 1472 has_cl_set = dict.fromkeys(has_cl_set)
1473 1473 else:
1474 1474 # If there were no known heads, the recipient cannot be assumed to
1475 1475 # know about any changesets.
1476 1476 has_cl_set = {}
1477 1477
1478 1478 # Make it easy to refer to self.manifest
1479 1479 mnfst = self.manifest
1480 1480 # We don't know which manifests are missing yet
1481 1481 msng_mnfst_set = {}
1482 1482 # Nor do we know which filenodes are missing.
1483 1483 msng_filenode_set = {}
1484 1484
1485 1485 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1486 1486 junk = None
1487 1487
1488 1488 # A changeset always belongs to itself, so the changenode lookup
1489 1489 # function for a changenode is identity.
1490 1490 def identity(x):
1491 1491 return x
1492 1492
1493 1493 # A function generating function. Sets up an environment for the
1494 1494 # inner function.
1495 1495 def cmp_by_rev_func(revlog):
1496 1496 # Compare two nodes by their revision number in the environment's
1497 1497 # revision history. Since the revision number both represents the
1498 1498 # most efficient order to read the nodes in, and represents a
1499 1499 # topological sorting of the nodes, this function is often useful.
1500 1500 def cmp_by_rev(a, b):
1501 1501 return cmp(revlog.rev(a), revlog.rev(b))
1502 1502 return cmp_by_rev
1503 1503
1504 1504 # If we determine that a particular file or manifest node must be a
1505 1505 # node that the recipient of the changegroup will already have, we can
1506 1506 # also assume the recipient will have all the parents. This function
1507 1507 # prunes them from the set of missing nodes.
1508 1508 def prune_parents(revlog, hasset, msngset):
1509 1509 haslst = hasset.keys()
1510 1510 haslst.sort(cmp_by_rev_func(revlog))
1511 1511 for node in haslst:
1512 1512 parentlst = [p for p in revlog.parents(node) if p != nullid]
1513 1513 while parentlst:
1514 1514 n = parentlst.pop()
1515 1515 if n not in hasset:
1516 1516 hasset[n] = 1
1517 1517 p = [p for p in revlog.parents(n) if p != nullid]
1518 1518 parentlst.extend(p)
1519 1519 for n in hasset:
1520 1520 msngset.pop(n, None)
1521 1521
1522 1522 # This is a function generating function used to set up an environment
1523 1523 # for the inner function to execute in.
1524 1524 def manifest_and_file_collector(changedfileset):
1525 1525 # This is an information gathering function that gathers
1526 1526 # information from each changeset node that goes out as part of
1527 1527 # the changegroup. The information gathered is a list of which
1528 1528 # manifest nodes are potentially required (the recipient may
1529 1529 # already have them) and total list of all files which were
1530 1530 # changed in any changeset in the changegroup.
1531 1531 #
1532 1532 # We also remember the first changenode we saw any manifest
1533 1533 # referenced by so we can later determine which changenode 'owns'
1534 1534 # the manifest.
1535 1535 def collect_manifests_and_files(clnode):
1536 1536 c = cl.read(clnode)
1537 1537 for f in c[3]:
1538 1538 # This is to make sure we only have one instance of each
1539 1539 # filename string for each filename.
1540 1540 changedfileset.setdefault(f, f)
1541 1541 msng_mnfst_set.setdefault(c[0], clnode)
1542 1542 return collect_manifests_and_files
1543 1543
1544 1544 # Figure out which manifest nodes (of the ones we think might be part
1545 1545 # of the changegroup) the recipient must know about and remove them
1546 1546 # from the changegroup.
1547 1547 def prune_manifests():
1548 1548 has_mnfst_set = {}
1549 1549 for n in msng_mnfst_set:
1550 1550 # If a 'missing' manifest thinks it belongs to a changenode
1551 1551 # the recipient is assumed to have, obviously the recipient
1552 1552 # must have that manifest.
1553 1553 linknode = cl.node(mnfst.linkrev(n))
1554 1554 if linknode in has_cl_set:
1555 1555 has_mnfst_set[n] = 1
1556 1556 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1557 1557
1558 1558 # Use the information collected in collect_manifests_and_files to say
1559 1559 # which changenode any manifestnode belongs to.
1560 1560 def lookup_manifest_link(mnfstnode):
1561 1561 return msng_mnfst_set[mnfstnode]
1562 1562
1563 1563 # A function generating function that sets up the initial environment
1564 1564 # the inner function.
1565 1565 def filenode_collector(changedfiles):
1566 1566 next_rev = [0]
1567 1567 # This gathers information from each manifestnode included in the
1568 1568 # changegroup about which filenodes the manifest node references
1569 1569 # so we can include those in the changegroup too.
1570 1570 #
1571 1571 # It also remembers which changenode each filenode belongs to. It
1572 1572 # does this by assuming the a filenode belongs to the changenode
1573 1573 # the first manifest that references it belongs to.
1574 1574 def collect_msng_filenodes(mnfstnode):
1575 1575 r = mnfst.rev(mnfstnode)
1576 1576 if r == next_rev[0]:
1577 1577 # If the last rev we looked at was the one just previous,
1578 1578 # we only need to see a diff.
1579 1579 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1580 1580 # For each line in the delta
1581 1581 for dline in delta.splitlines():
1582 1582 # get the filename and filenode for that line
1583 1583 f, fnode = dline.split('\0')
1584 1584 fnode = bin(fnode[:40])
1585 1585 f = changedfiles.get(f, None)
1586 1586 # And if the file is in the list of files we care
1587 1587 # about.
1588 1588 if f is not None:
1589 1589 # Get the changenode this manifest belongs to
1590 1590 clnode = msng_mnfst_set[mnfstnode]
1591 1591 # Create the set of filenodes for the file if
1592 1592 # there isn't one already.
1593 1593 ndset = msng_filenode_set.setdefault(f, {})
1594 1594 # And set the filenode's changelog node to the
1595 1595 # manifest's if it hasn't been set already.
1596 1596 ndset.setdefault(fnode, clnode)
1597 1597 else:
1598 1598 # Otherwise we need a full manifest.
1599 1599 m = mnfst.read(mnfstnode)
1600 1600 # For every file in we care about.
1601 1601 for f in changedfiles:
1602 1602 fnode = m.get(f, None)
1603 1603 # If it's in the manifest
1604 1604 if fnode is not None:
1605 1605 # See comments above.
1606 1606 clnode = msng_mnfst_set[mnfstnode]
1607 1607 ndset = msng_filenode_set.setdefault(f, {})
1608 1608 ndset.setdefault(fnode, clnode)
1609 1609 # Remember the revision we hope to see next.
1610 1610 next_rev[0] = r + 1
1611 1611 return collect_msng_filenodes
1612 1612
1613 1613 # We have a list of filenodes we think we need for a file, lets remove
1614 1614 # all those we now the recipient must have.
1615 1615 def prune_filenodes(f, filerevlog):
1616 1616 msngset = msng_filenode_set[f]
1617 1617 hasset = {}
1618 1618 # If a 'missing' filenode thinks it belongs to a changenode we
1619 1619 # assume the recipient must have, then the recipient must have
1620 1620 # that filenode.
1621 1621 for n in msngset:
1622 1622 clnode = cl.node(filerevlog.linkrev(n))
1623 1623 if clnode in has_cl_set:
1624 1624 hasset[n] = 1
1625 1625 prune_parents(filerevlog, hasset, msngset)
1626 1626
1627 1627 # A function generator function that sets up the a context for the
1628 1628 # inner function.
1629 1629 def lookup_filenode_link_func(fname):
1630 1630 msngset = msng_filenode_set[fname]
1631 1631 # Lookup the changenode the filenode belongs to.
1632 1632 def lookup_filenode_link(fnode):
1633 1633 return msngset[fnode]
1634 1634 return lookup_filenode_link
1635 1635
1636 1636 # Now that we have all theses utility functions to help out and
1637 1637 # logically divide up the task, generate the group.
1638 1638 def gengroup():
1639 1639 # The set of changed files starts empty.
1640 1640 changedfiles = {}
1641 1641 # Create a changenode group generator that will call our functions
1642 1642 # back to lookup the owning changenode and collect information.
1643 1643 group = cl.group(msng_cl_lst, identity,
1644 1644 manifest_and_file_collector(changedfiles))
1645 1645 for chnk in group:
1646 1646 yield chnk
1647 1647
1648 1648 # The list of manifests has been collected by the generator
1649 1649 # calling our functions back.
1650 1650 prune_manifests()
1651 1651 msng_mnfst_lst = msng_mnfst_set.keys()
1652 1652 # Sort the manifestnodes by revision number.
1653 1653 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1654 1654 # Create a generator for the manifestnodes that calls our lookup
1655 1655 # and data collection functions back.
1656 1656 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1657 1657 filenode_collector(changedfiles))
1658 1658 for chnk in group:
1659 1659 yield chnk
1660 1660
1661 1661 # These are no longer needed, dereference and toss the memory for
1662 1662 # them.
1663 1663 msng_mnfst_lst = None
1664 1664 msng_mnfst_set.clear()
1665 1665
1666 1666 changedfiles = changedfiles.keys()
1667 1667 changedfiles.sort()
1668 1668 # Go through all our files in order sorted by name.
1669 1669 for fname in changedfiles:
1670 1670 filerevlog = self.file(fname)
1671 1671 # Toss out the filenodes that the recipient isn't really
1672 1672 # missing.
1673 1673 if msng_filenode_set.has_key(fname):
1674 1674 prune_filenodes(fname, filerevlog)
1675 1675 msng_filenode_lst = msng_filenode_set[fname].keys()
1676 1676 else:
1677 1677 msng_filenode_lst = []
1678 1678 # If any filenodes are left, generate the group for them,
1679 1679 # otherwise don't bother.
1680 1680 if len(msng_filenode_lst) > 0:
1681 1681 yield changegroup.genchunk(fname)
1682 1682 # Sort the filenodes by their revision #
1683 1683 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1684 1684 # Create a group generator and only pass in a changenode
1685 1685 # lookup function as we need to collect no information
1686 1686 # from filenodes.
1687 1687 group = filerevlog.group(msng_filenode_lst,
1688 1688 lookup_filenode_link_func(fname))
1689 1689 for chnk in group:
1690 1690 yield chnk
1691 1691 if msng_filenode_set.has_key(fname):
1692 1692 # Don't need this anymore, toss it to free memory.
1693 1693 del msng_filenode_set[fname]
1694 1694 # Signal that no more groups are left.
1695 1695 yield changegroup.closechunk()
1696 1696
1697 1697 if msng_cl_lst:
1698 1698 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1699 1699
1700 1700 return util.chunkbuffer(gengroup())
1701 1701
1702 1702 def changegroup(self, basenodes, source):
1703 1703 """Generate a changegroup of all nodes that we have that a recipient
1704 1704 doesn't.
1705 1705
1706 1706 This is much easier than the previous function as we can assume that
1707 1707 the recipient has any changenode we aren't sending them."""
1708 1708
1709 1709 self.hook('preoutgoing', throw=True, source=source)
1710 1710
1711 1711 cl = self.changelog
1712 1712 nodes = cl.nodesbetween(basenodes, None)[0]
1713 1713 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1714 1714 self.changegroupinfo(nodes)
1715 1715
1716 1716 def identity(x):
1717 1717 return x
1718 1718
1719 1719 def gennodelst(revlog):
1720 1720 for r in xrange(0, revlog.count()):
1721 1721 n = revlog.node(r)
1722 1722 if revlog.linkrev(n) in revset:
1723 1723 yield n
1724 1724
1725 1725 def changed_file_collector(changedfileset):
1726 1726 def collect_changed_files(clnode):
1727 1727 c = cl.read(clnode)
1728 1728 for fname in c[3]:
1729 1729 changedfileset[fname] = 1
1730 1730 return collect_changed_files
1731 1731
1732 1732 def lookuprevlink_func(revlog):
1733 1733 def lookuprevlink(n):
1734 1734 return cl.node(revlog.linkrev(n))
1735 1735 return lookuprevlink
1736 1736
1737 1737 def gengroup():
1738 1738 # construct a list of all changed files
1739 1739 changedfiles = {}
1740 1740
1741 1741 for chnk in cl.group(nodes, identity,
1742 1742 changed_file_collector(changedfiles)):
1743 1743 yield chnk
1744 1744 changedfiles = changedfiles.keys()
1745 1745 changedfiles.sort()
1746 1746
1747 1747 mnfst = self.manifest
1748 1748 nodeiter = gennodelst(mnfst)
1749 1749 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1750 1750 yield chnk
1751 1751
1752 1752 for fname in changedfiles:
1753 1753 filerevlog = self.file(fname)
1754 1754 nodeiter = gennodelst(filerevlog)
1755 1755 nodeiter = list(nodeiter)
1756 1756 if nodeiter:
1757 1757 yield changegroup.genchunk(fname)
1758 1758 lookup = lookuprevlink_func(filerevlog)
1759 1759 for chnk in filerevlog.group(nodeiter, lookup):
1760 1760 yield chnk
1761 1761
1762 1762 yield changegroup.closechunk()
1763 1763
1764 1764 if nodes:
1765 1765 self.hook('outgoing', node=hex(nodes[0]), source=source)
1766 1766
1767 1767 return util.chunkbuffer(gengroup())
1768 1768
1769 1769 def addchangegroup(self, source, srctype, url):
1770 1770 """add changegroup to repo.
1771 1771
1772 1772 return values:
1773 1773 - nothing changed or no source: 0
1774 1774 - more heads than before: 1+added heads (2..n)
1775 1775 - less heads than before: -1-removed heads (-2..-n)
1776 1776 - number of heads stays the same: 1
1777 1777 """
1778 1778 def csmap(x):
1779 1779 self.ui.debug(_("add changeset %s\n") % short(x))
1780 1780 return cl.count()
1781 1781
1782 1782 def revmap(x):
1783 1783 return cl.rev(x)
1784 1784
1785 1785 if not source:
1786 1786 return 0
1787 1787
1788 1788 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1789 1789
1790 1790 changesets = files = revisions = 0
1791 1791
1792 1792 tr = self.transaction()
1793 1793
1794 1794 # write changelog data to temp files so concurrent readers will not see
1795 1795 # inconsistent view
1796 1796 cl = self.changelog
1797 1797 cl.delayupdate()
1798 1798 oldheads = len(cl.heads())
1799 1799
1800 1800 # pull off the changeset group
1801 1801 self.ui.status(_("adding changesets\n"))
1802 1802 cor = cl.count() - 1
1803 1803 chunkiter = changegroup.chunkiter(source)
1804 1804 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1805 1805 raise util.Abort(_("received changelog group is empty"))
1806 1806 cnr = cl.count() - 1
1807 1807 changesets = cnr - cor
1808 1808
1809 1809 # pull off the manifest group
1810 1810 self.ui.status(_("adding manifests\n"))
1811 1811 chunkiter = changegroup.chunkiter(source)
1812 1812 # no need to check for empty manifest group here:
1813 1813 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1814 1814 # no new manifest will be created and the manifest group will
1815 1815 # be empty during the pull
1816 1816 self.manifest.addgroup(chunkiter, revmap, tr)
1817 1817
1818 1818 # process the files
1819 1819 self.ui.status(_("adding file changes\n"))
1820 1820 while 1:
1821 1821 f = changegroup.getchunk(source)
1822 1822 if not f:
1823 1823 break
1824 1824 self.ui.debug(_("adding %s revisions\n") % f)
1825 1825 fl = self.file(f)
1826 1826 o = fl.count()
1827 1827 chunkiter = changegroup.chunkiter(source)
1828 1828 if fl.addgroup(chunkiter, revmap, tr) is None:
1829 1829 raise util.Abort(_("received file revlog group is empty"))
1830 1830 revisions += fl.count() - o
1831 1831 files += 1
1832 1832
1833 1833 # make changelog see real files again
1834 1834 cl.finalize(tr)
1835 1835
1836 1836 newheads = len(self.changelog.heads())
1837 1837 heads = ""
1838 1838 if oldheads and newheads != oldheads:
1839 1839 heads = _(" (%+d heads)") % (newheads - oldheads)
1840 1840
1841 1841 self.ui.status(_("added %d changesets"
1842 1842 " with %d changes to %d files%s\n")
1843 1843 % (changesets, revisions, files, heads))
1844 1844
1845 1845 if changesets > 0:
1846 1846 self.hook('pretxnchangegroup', throw=True,
1847 1847 node=hex(self.changelog.node(cor+1)), source=srctype,
1848 1848 url=url)
1849 1849
1850 1850 tr.close()
1851 1851
1852 1852 if changesets > 0:
1853 1853 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1854 1854 source=srctype, url=url)
1855 1855
1856 1856 for i in xrange(cor + 1, cnr + 1):
1857 1857 self.hook("incoming", node=hex(self.changelog.node(i)),
1858 1858 source=srctype, url=url)
1859 1859
1860 1860 # never return 0 here:
1861 1861 if newheads < oldheads:
1862 1862 return newheads - oldheads - 1
1863 1863 else:
1864 1864 return newheads - oldheads + 1
1865 1865
1866 1866
1867 1867 def stream_in(self, remote):
1868 1868 fp = remote.stream_out()
1869 1869 l = fp.readline()
1870 1870 try:
1871 1871 resp = int(l)
1872 1872 except ValueError:
1873 1873 raise util.UnexpectedOutput(
1874 1874 _('Unexpected response from remote server:'), l)
1875 1875 if resp == 1:
1876 1876 raise util.Abort(_('operation forbidden by server'))
1877 1877 elif resp == 2:
1878 1878 raise util.Abort(_('locking the remote repository failed'))
1879 1879 elif resp != 0:
1880 1880 raise util.Abort(_('the server sent an unknown error code'))
1881 1881 self.ui.status(_('streaming all changes\n'))
1882 1882 l = fp.readline()
1883 1883 try:
1884 1884 total_files, total_bytes = map(int, l.split(' ', 1))
1885 1885 except ValueError, TypeError:
1886 1886 raise util.UnexpectedOutput(
1887 1887 _('Unexpected response from remote server:'), l)
1888 1888 self.ui.status(_('%d files to transfer, %s of data\n') %
1889 1889 (total_files, util.bytecount(total_bytes)))
1890 1890 start = time.time()
1891 1891 for i in xrange(total_files):
1892 1892 # XXX doesn't support '\n' or '\r' in filenames
1893 1893 l = fp.readline()
1894 1894 try:
1895 1895 name, size = l.split('\0', 1)
1896 1896 size = int(size)
1897 1897 except ValueError, TypeError:
1898 1898 raise util.UnexpectedOutput(
1899 1899 _('Unexpected response from remote server:'), l)
1900 1900 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1901 1901 ofp = self.sopener(name, 'w')
1902 1902 for chunk in util.filechunkiter(fp, limit=size):
1903 1903 ofp.write(chunk)
1904 1904 ofp.close()
1905 1905 elapsed = time.time() - start
1906 1906 if elapsed <= 0:
1907 1907 elapsed = 0.001
1908 1908 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1909 1909 (util.bytecount(total_bytes), elapsed,
1910 1910 util.bytecount(total_bytes / elapsed)))
1911 1911 self.invalidate()
1912 1912 return len(self.heads()) + 1
1913 1913
1914 1914 def clone(self, remote, heads=[], stream=False):
1915 1915 '''clone remote repository.
1916 1916
1917 1917 keyword arguments:
1918 1918 heads: list of revs to clone (forces use of pull)
1919 1919 stream: use streaming clone if possible'''
1920 1920
1921 1921 # now, all clients that can request uncompressed clones can
1922 1922 # read repo formats supported by all servers that can serve
1923 1923 # them.
1924 1924
1925 1925 # if revlog format changes, client will have to check version
1926 1926 # and format flags on "stream" capability, and use
1927 1927 # uncompressed only if compatible.
1928 1928
1929 1929 if stream and not heads and remote.capable('stream'):
1930 1930 return self.stream_in(remote)
1931 1931 return self.pull(remote, heads)
1932 1932
1933 1933 # used to avoid circular references so destructors work
1934 1934 def aftertrans(files):
1935 1935 renamefiles = [tuple(t) for t in files]
1936 1936 def a():
1937 1937 for src, dest in renamefiles:
1938 1938 util.rename(src, dest)
1939 1939 return a
1940 1940
1941 1941 def instance(ui, path, create):
1942 1942 return localrepository(ui, util.drop_scheme('file', path), create)
1943 1943
1944 1944 def islocal(path):
1945 1945 return True
@@ -1,81 +1,81 b''
1 1 #!/usr/bin/env python
2 2 """Test the running system for features availability. Exit with zero
3 3 if all features are there, non-zero otherwise.
4 4 """
5 5 import optparse
6 6 import os
7 7 import sys
8 8 import tempfile
9 9
10 10 def has_symlink():
11 11 return hasattr(os, "symlink")
12 12
13 13 def has_fifo():
14 14 return hasattr(os, "mkfifo")
15 15
16 16 def has_executablebit():
17 17 fd, path = tempfile.mkstemp()
18 18 os.close(fd)
19 19 try:
20 20 s = os.lstat(path).st_mode
21 21 os.chmod(path, s | 0100)
22 22 return (os.lstat(path).st_mode & 0100 != 0)
23 23 finally:
24 24 os.remove(path)
25 25
26 26 def has_eol_in_paths():
27 27 try:
28 28 fd, path = tempfile.mkstemp(suffix='\n\r')
29 29 os.close(fd)
30 30 os.remove(path)
31 31 return True
32 32 except:
33 33 return False
34 34
35 35 checks = {
36 36 "symlink": (has_symlink, "symbolic links"),
37 37 "fifo": (has_fifo, "named pipes"),
38 38 "execbit": (has_executablebit, "executable bit"),
39 39 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
40 40 }
41 41
42 42 def list_features():
43 43 for name, feature in checks.iteritems():
44 44 desc = feature[1]
45 45 print name + ':', desc
46 46
47 47 parser = optparse.OptionParser("%prog [options] [features]")
48 48 parser.add_option("--list-features", action="store_true",
49 49 help="list available features")
50 50 parser.add_option("-q", "--quiet", action="store_true",
51 51 help="check features silently")
52 52
53 53 if __name__ == '__main__':
54 54 options, args = parser.parse_args()
55 55 if options.list_features:
56 56 list_features()
57 57 sys.exit(0)
58
58
59 59 quiet = options.quiet
60 60
61 61 failures = 0
62 62
63 63 def error(msg):
64 64 global failures
65 65 if not quiet:
66 66 sys.stderr.write(msg + '\n')
67 67 failures += 1
68
68
69 69 for feature in args:
70 70 if feature not in checks:
71 71 error('hghave: unknown feature: ' + feature)
72 72 continue
73
74 check, desc = checks[feature]
73
74 check, desc = checks[feature]
75 75 if not check():
76 76 error('hghave: missing feature: ' + desc)
77 77
78 78 if failures != 0:
79 79 sys.exit(1)
80 80
81
81
@@ -1,463 +1,463 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms
8 8 # of the GNU General Public License, incorporated herein by reference.
9 9
10 10 import difflib
11 11 import errno
12 12 import optparse
13 13 import os
14 14 import popen2
15 15 import re
16 16 import shutil
17 17 import signal
18 18 import sys
19 19 import tempfile
20 20 import time
21 21
22 22 # hghave reserved exit code to skip test
23 23 SKIPPED_STATUS = 80
24 24
25 25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
26 26
27 27 parser = optparse.OptionParser("%prog [options] [tests]")
28 28 parser.add_option("-v", "--verbose", action="store_true",
29 29 help="output verbose messages")
30 30 parser.add_option("-t", "--timeout", type="int",
31 31 help="kill errant tests after TIMEOUT seconds")
32 32 parser.add_option("-c", "--cover", action="store_true",
33 33 help="print a test coverage report")
34 34 parser.add_option("-s", "--cover_stdlib", action="store_true",
35 35 help="print a test coverage report inc. standard libraries")
36 36 parser.add_option("-C", "--annotate", action="store_true",
37 37 help="output files annotated with coverage")
38 38 parser.add_option("-r", "--retest", action="store_true",
39 39 help="retest failed tests")
40 40 parser.add_option("-f", "--first", action="store_true",
41 41 help="exit on the first test failure")
42 42 parser.add_option("-R", "--restart", action="store_true",
43 43 help="restart at last error")
44 44 parser.add_option("-i", "--interactive", action="store_true",
45 45 help="prompt to accept changed output")
46 46
47 47 parser.set_defaults(timeout=180)
48 48 (options, args) = parser.parse_args()
49 49 verbose = options.verbose
50 50 coverage = options.cover or options.cover_stdlib or options.annotate
51 51 python = sys.executable
52 52
53 53 def vlog(*msg):
54 54 if verbose:
55 55 for m in msg:
56 56 print m,
57 57 print
58 58
59 59 def splitnewlines(text):
60 60 '''like str.splitlines, but only split on newlines.
61 61 keep line endings.'''
62 62 i = 0
63 63 lines = []
64 64 while True:
65 65 n = text.find('\n', i)
66 66 if n == -1:
67 67 last = text[i:]
68 68 if last:
69 69 lines.append(last)
70 70 return lines
71 71 lines.append(text[i:n+1])
72 72 i = n + 1
73 73
74 74 def extract_missing_features(lines):
75 75 '''Extract missing/unknown features log lines as a list'''
76 76 missing = []
77 77 for line in lines:
78 78 if not line.startswith('hghave: '):
79 79 continue
80 80 line = line.splitlines()[0]
81 81 missing.append(line[8:])
82 82
83 83 return missing
84 84
85 85 def show_diff(expected, output):
86 86 for line in difflib.unified_diff(expected, output,
87 87 "Expected output", "Test output"):
88 88 sys.stdout.write(line)
89 89
90 90 def find_program(program):
91 91 """Search PATH for a executable program"""
92 92 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
93 93 name = os.path.join(p, program)
94 94 if os.access(name, os.X_OK):
95 95 return name
96 96 return None
97 97
98 98 def check_required_tools():
99 99 # Before we go any further, check for pre-requisite tools
100 100 # stuff from coreutils (cat, rm, etc) are not tested
101 101 for p in required_tools:
102 102 if os.name == 'nt':
103 103 p += '.exe'
104 104 found = find_program(p)
105 105 if found:
106 106 vlog("# Found prerequisite", p, "at", found)
107 107 else:
108 108 print "WARNING: Did not find prerequisite tool: "+p
109 109
110 110 def cleanup_exit():
111 111 if verbose:
112 112 print "# Cleaning up HGTMP", HGTMP
113 113 shutil.rmtree(HGTMP, True)
114 114
115 115 def use_correct_python():
116 116 # some tests run python interpreter. they must use same
117 117 # interpreter we use or bad things will happen.
118 118 exedir, exename = os.path.split(sys.executable)
119 119 if exename == 'python':
120 120 path = find_program('python')
121 121 if os.path.dirname(path) == exedir:
122 122 return
123 123 vlog('# Making python executable in test path use correct Python')
124 124 my_python = os.path.join(BINDIR, 'python')
125 125 try:
126 126 os.symlink(sys.executable, my_python)
127 127 except AttributeError:
128 128 # windows fallback
129 129 shutil.copyfile(sys.executable, my_python)
130 130 shutil.copymode(sys.executable, my_python)
131 131
132 132 def install_hg():
133 133 global python
134 134 vlog("# Performing temporary installation of HG")
135 135 installerrs = os.path.join("tests", "install.err")
136 136
137 137 os.chdir("..") # Get back to hg root
138 138 cmd = ('%s setup.py clean --all'
139 139 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
140 140 % (sys.executable, INST, PYTHONDIR, installerrs))
141 141 vlog("# Running", cmd)
142 142 if os.system(cmd) == 0:
143 143 if not verbose:
144 144 os.remove(installerrs)
145 145 else:
146 146 f = open(installerrs)
147 147 for line in f:
148 148 print line,
149 149 f.close()
150 150 sys.exit(1)
151 151 os.chdir(TESTDIR)
152 152
153 153 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
154 154 os.environ["PYTHONPATH"] = PYTHONDIR
155 155
156 156 use_correct_python()
157 157
158 158 if coverage:
159 159 vlog("# Installing coverage wrapper")
160 160 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
161 161 if os.path.exists(COVERAGE_FILE):
162 162 os.unlink(COVERAGE_FILE)
163 163 # Create a wrapper script to invoke hg via coverage.py
164 164 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
165 165 f = open(os.path.join(BINDIR, 'hg'), 'w')
166 166 f.write('#!' + sys.executable + '\n')
167 167 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
168 168 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
169 169 (os.path.join(TESTDIR, 'coverage.py'),
170 170 os.path.join(BINDIR, '_hg.py')))
171 171 f.close()
172 172 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
173 173 python = '"%s" "%s" -x' % (sys.executable,
174 174 os.path.join(TESTDIR,'coverage.py'))
175 175
176 176 def output_coverage():
177 177 vlog("# Producing coverage report")
178 178 omit = [BINDIR, TESTDIR, PYTHONDIR]
179 179 if not options.cover_stdlib:
180 180 # Exclude as system paths (ignoring empty strings seen on win)
181 181 omit += [x for x in sys.path if x != '']
182 182 omit = ','.join(omit)
183 183 os.chdir(PYTHONDIR)
184 184 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
185 185 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
186 186 vlog("# Running: "+cmd)
187 187 os.system(cmd)
188 188 if options.annotate:
189 189 adir = os.path.join(TESTDIR, 'annotated')
190 190 if not os.path.isdir(adir):
191 191 os.mkdir(adir)
192 192 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
193 193 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
194 194 adir, omit)
195 195 vlog("# Running: "+cmd)
196 196 os.system(cmd)
197 197
198 198 class Timeout(Exception):
199 199 pass
200 200
201 201 def alarmed(signum, frame):
202 202 raise Timeout
203 203
204 204 def run(cmd):
205 205 """Run command in a sub-process, capturing the output (stdout and stderr).
206 206 Return the exist code, and output."""
207 207 # TODO: Use subprocess.Popen if we're running on Python 2.4
208 208 if os.name == 'nt':
209 209 tochild, fromchild = os.popen4(cmd)
210 210 tochild.close()
211 211 output = fromchild.read()
212 212 ret = fromchild.close()
213 213 if ret == None:
214 214 ret = 0
215 215 else:
216 216 proc = popen2.Popen4(cmd)
217 217 try:
218 218 output = ''
219 219 proc.tochild.close()
220 220 output = proc.fromchild.read()
221 221 ret = proc.wait()
222 222 if os.WIFEXITED(ret):
223 223 ret = os.WEXITSTATUS(ret)
224 224 except Timeout:
225 225 vlog('# Process %d timed out - killing it' % proc.pid)
226 226 os.kill(proc.pid, signal.SIGTERM)
227 227 ret = proc.wait()
228 228 if ret == 0:
229 229 ret = signal.SIGTERM << 8
230 230 output += ("\n### Abort: timeout after %d seconds.\n"
231 231 % options.timeout)
232 232 return ret, splitnewlines(output)
233 233
234 234 def run_one(test):
235 235 '''tristate output:
236 236 None -> skipped
237 237 True -> passed
238 238 False -> failed'''
239 239
240 240 vlog("# Test", test)
241 241 if not verbose:
242 242 sys.stdout.write('.')
243 243 sys.stdout.flush()
244 244
245 245 # create a fresh hgrc
246 246 hgrc = file(HGRCPATH, 'w+')
247 247 hgrc.write('[ui]\n')
248 248 hgrc.write('slash = True\n')
249 249 hgrc.close()
250 250
251 251 err = os.path.join(TESTDIR, test+".err")
252 252 ref = os.path.join(TESTDIR, test+".out")
253 253 testpath = os.path.join(TESTDIR, test)
254 254
255 255 if os.path.exists(err):
256 256 os.remove(err) # Remove any previous output files
257 257
258 258 # Make a tmp subdirectory to work in
259 259 tmpd = os.path.join(HGTMP, test)
260 260 os.mkdir(tmpd)
261 261 os.chdir(tmpd)
262 262
263 263 try:
264 264 tf = open(testpath)
265 265 firstline = tf.readline().rstrip()
266 266 tf.close()
267 267 except:
268 268 firstline = ''
269 269 lctest = test.lower()
270 270
271 271 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
272 272 cmd = '%s "%s"' % (python, testpath)
273 273 elif lctest.endswith('.bat'):
274 274 # do not run batch scripts on non-windows
275 275 if os.name != 'nt':
276 276 print '\nSkipping %s: batch script' % test
277 277 return None
278 278 # To reliably get the error code from batch files on WinXP,
279 279 # the "cmd /c call" prefix is needed. Grrr
280 280 cmd = 'cmd /c call "%s"' % testpath
281 281 else:
282 282 # do not run shell scripts on windows
283 283 if os.name == 'nt':
284 284 print '\nSkipping %s: shell script' % test
285 285 return None
286 286 # do not try to run non-executable programs
287 287 if not os.access(testpath, os.X_OK):
288 288 print '\nSkipping %s: not executable' % test
289 289 return None
290 290 cmd = '"%s"' % testpath
291 291
292 292 if options.timeout > 0:
293 293 signal.alarm(options.timeout)
294 294
295 295 vlog("# Running", cmd)
296 296 ret, out = run(cmd)
297 297 vlog("# Ret was:", ret)
298 298
299 299 if options.timeout > 0:
300 300 signal.alarm(0)
301 301
302 302 skipped = (ret == SKIPPED_STATUS)
303 303 diffret = 0
304 304 # If reference output file exists, check test output against it
305 305 if os.path.exists(ref):
306 306 f = open(ref, "r")
307 307 ref_out = splitnewlines(f.read())
308 308 f.close()
309 309 else:
310 310 ref_out = []
311 311 if not skipped and out != ref_out:
312 312 diffret = 1
313 313 print "\nERROR: %s output changed" % (test)
314 314 show_diff(ref_out, out)
315 315 if skipped:
316 316 missing = extract_missing_features(out)
317 317 if not missing:
318 318 missing = ['irrelevant']
319 319 print '\nSkipping %s: %s' % (test, missing[-1])
320 320 elif ret:
321 321 print "\nERROR: %s failed with error code %d" % (test, ret)
322 322 elif diffret:
323 323 ret = diffret
324 324
325 if ret != 0 and not skipped:
325 if ret != 0 and not skipped:
326 326 # Save errors to a file for diagnosis
327 327 f = open(err, "wb")
328 328 for line in out:
329 329 f.write(line)
330 330 f.close()
331 331
332 332 # Kill off any leftover daemon processes
333 333 try:
334 334 fp = file(DAEMON_PIDS)
335 335 for line in fp:
336 336 try:
337 337 pid = int(line)
338 338 except ValueError:
339 339 continue
340 340 try:
341 341 os.kill(pid, 0)
342 342 vlog('# Killing daemon process %d' % pid)
343 343 os.kill(pid, signal.SIGTERM)
344 344 time.sleep(0.25)
345 345 os.kill(pid, 0)
346 346 vlog('# Daemon process %d is stuck - really killing it' % pid)
347 347 os.kill(pid, signal.SIGKILL)
348 348 except OSError, err:
349 349 if err.errno != errno.ESRCH:
350 350 raise
351 351 fp.close()
352 352 os.unlink(DAEMON_PIDS)
353 353 except IOError:
354 354 pass
355 355
356 356 os.chdir(TESTDIR)
357 357 shutil.rmtree(tmpd, True)
358 358 if skipped:
359 359 return None
360 360 return ret == 0
361 361
362 362
363 363 os.umask(022)
364 364
365 365 check_required_tools()
366 366
367 367 # Reset some environment variables to well-known values so that
368 368 # the tests produce repeatable output.
369 369 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
370 370 os.environ['TZ'] = 'GMT'
371 371
372 372 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
373 373 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
374 374 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
375 375 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
376 376
377 377 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
378 378 os.environ["HGMERGE"] = ('python "%s" -L my -L other'
379 379 % os.path.join(TESTDIR, os.path.pardir, 'contrib',
380 380 'simplemerge'))
381 381 os.environ["HGUSER"] = "test"
382 382 os.environ["HGENCODING"] = "ascii"
383 383 os.environ["HGENCODINGMODE"] = "strict"
384 384
385 385 vlog("# Using TESTDIR", TESTDIR)
386 386 vlog("# Using HGTMP", HGTMP)
387 387
388 388 INST = os.path.join(HGTMP, "install")
389 389 BINDIR = os.path.join(INST, "bin")
390 390 PYTHONDIR = os.path.join(INST, "lib", "python")
391 391 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
392 392
393 393 try:
394 394 try:
395 395 install_hg()
396 396
397 397 if options.timeout > 0:
398 398 try:
399 399 signal.signal(signal.SIGALRM, alarmed)
400 400 vlog('# Running tests with %d-second timeout' %
401 401 options.timeout)
402 402 except AttributeError:
403 403 print 'WARNING: cannot run tests with timeouts'
404 404 options.timeout = 0
405 405
406 406 tested = 0
407 407 failed = 0
408 408 skipped = 0
409 409
410 410 if len(args) == 0:
411 411 args = os.listdir(".")
412 412 args.sort()
413 413
414 414
415 415 tests = []
416 416 for test in args:
417 417 if (test.startswith("test-") and '~' not in test and
418 418 ('.' not in test or test.endswith('.py') or
419 419 test.endswith('.bat'))):
420 420 tests.append(test)
421 421
422 422 if options.restart:
423 423 orig = list(tests)
424 424 while tests:
425 425 if os.path.exists(tests[0] + ".err"):
426 426 break
427 427 tests.pop(0)
428 428 if not tests:
429 429 print "running all tests"
430 430 tests = orig
431 431
432 432 for test in tests:
433 433 if options.retest and not os.path.exists(test + ".err"):
434 434 skipped += 1
435 435 continue
436 436 ret = run_one(test)
437 437 if ret is None:
438 438 skipped += 1
439 439 elif not ret:
440 440 if options.interactive:
441 441 print "Accept this change? [n] ",
442 442 answer = sys.stdin.readline().strip()
443 443 if answer.lower() in "y yes".split():
444 444 os.rename(test + ".err", test + ".out")
445 445 tested += 1
446 446 continue
447 447 failed += 1
448 448 if options.first:
449 449 break
450 450 tested += 1
451 451
452 452 print "\n# Ran %d tests, %d skipped, %d failed." % (tested, skipped,
453 453 failed)
454 454 if coverage:
455 455 output_coverage()
456 456 except KeyboardInterrupt:
457 457 failed = True
458 458 print "\ninterrupted!"
459 459 finally:
460 460 cleanup_exit()
461 461
462 462 if failed:
463 463 sys.exit(1)
1 NO CONTENT: modified file, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now