##// 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 # bash completion for the Mercurial distributed SCM
1 # bash completion for the Mercurial distributed SCM
2
2
3 # Docs:
3 # Docs:
4 #
4 #
5 # If you source this file from your .bashrc, bash should be able to
5 # If you source this file from your .bashrc, bash should be able to
6 # complete a command line that uses hg with all the available commands
6 # complete a command line that uses hg with all the available commands
7 # and options and sometimes even arguments.
7 # and options and sometimes even arguments.
8 #
8 #
9 # Mercurial allows you to define additional commands through extensions.
9 # Mercurial allows you to define additional commands through extensions.
10 # Bash should be able to automatically figure out the name of these new
10 # Bash should be able to automatically figure out the name of these new
11 # commands and their options. See below for how to define _hg_opt_foo
11 # commands and their options. See below for how to define _hg_opt_foo
12 # and _hg_cmd_foo functions to fine-tune the completion for option and
12 # and _hg_cmd_foo functions to fine-tune the completion for option and
13 # non-option arguments, respectively.
13 # non-option arguments, respectively.
14 #
14 #
15 #
15 #
16 # Notes about completion for specific commands:
16 # Notes about completion for specific commands:
17 #
17 #
18 # - the completion function for the email command from the patchbomb
18 # - the completion function for the email command from the patchbomb
19 # extension will try to call _hg_emails to get a list of e-mail
19 # extension will try to call _hg_emails to get a list of e-mail
20 # addresses. It's up to the user to define this function. For
20 # addresses. It's up to the user to define this function. For
21 # example, put the addresses of the lists that you usually patchbomb
21 # example, put the addresses of the lists that you usually patchbomb
22 # in ~/.patchbomb-to and the addresses that you usually use to send
22 # in ~/.patchbomb-to and the addresses that you usually use to send
23 # the patchbombs in ~/.patchbomb-from and use something like this:
23 # the patchbombs in ~/.patchbomb-from and use something like this:
24 #
24 #
25 # _hg_emails()
25 # _hg_emails()
26 # {
26 # {
27 # if [ -r ~/.patchbomb-$1 ]; then
27 # if [ -r ~/.patchbomb-$1 ]; then
28 # cat ~/.patchbomb-$1
28 # cat ~/.patchbomb-$1
29 # fi
29 # fi
30 # }
30 # }
31 #
31 #
32 #
32 #
33 # Writing completion functions for additional commands:
33 # Writing completion functions for additional commands:
34 #
34 #
35 # If it exists, the function _hg_cmd_foo will be called without
35 # If it exists, the function _hg_cmd_foo will be called without
36 # arguments to generate the completion candidates for the hg command
36 # arguments to generate the completion candidates for the hg command
37 # "foo". If the command receives some arguments that aren't options
37 # "foo". If the command receives some arguments that aren't options
38 # even though they start with a "-", you can define a function called
38 # even though they start with a "-", you can define a function called
39 # _hg_opt_foo to generate the completion candidates. If _hg_opt_foo
39 # _hg_opt_foo to generate the completion candidates. If _hg_opt_foo
40 # doesn't return 0, regular completion for options is attempted.
40 # doesn't return 0, regular completion for options is attempted.
41 #
41 #
42 # In addition to the regular completion variables provided by bash,
42 # In addition to the regular completion variables provided by bash,
43 # the following variables are also set:
43 # the following variables are also set:
44 # - $hg - the hg program being used (e.g. /usr/bin/hg)
44 # - $hg - the hg program being used (e.g. /usr/bin/hg)
45 # - $cmd - the name of the hg command being completed
45 # - $cmd - the name of the hg command being completed
46 # - $cmd_index - the index of $cmd in $COMP_WORDS
46 # - $cmd_index - the index of $cmd in $COMP_WORDS
47 # - $cur - the current argument being completed
47 # - $cur - the current argument being completed
48 # - $prev - the argument before $cur
48 # - $prev - the argument before $cur
49 # - $global_args - "|"-separated list of global options that accept
49 # - $global_args - "|"-separated list of global options that accept
50 # an argument (e.g. '--cwd|-R|--repository')
50 # an argument (e.g. '--cwd|-R|--repository')
51 # - $canonical - 1 if we canonicalized $cmd before calling the function
51 # - $canonical - 1 if we canonicalized $cmd before calling the function
52 # 0 otherwise
52 # 0 otherwise
53 #
53 #
54
54
55 shopt -s extglob
55 shopt -s extglob
56
56
57 _hg_commands()
57 _hg_commands()
58 {
58 {
59 local commands
59 local commands
60 commands="$("$hg" debugcomplete "$cur" 2>/dev/null)" || commands=""
60 commands="$("$hg" debugcomplete "$cur" 2>/dev/null)" || commands=""
61 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur"))
61 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur"))
62 }
62 }
63
63
64 _hg_paths()
64 _hg_paths()
65 {
65 {
66 local paths="$("$hg" paths 2>/dev/null | sed -e 's/ = .*$//')"
66 local paths="$("$hg" paths 2>/dev/null | sed -e 's/ = .*$//')"
67 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
67 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
68 }
68 }
69
69
70 _hg_repos()
70 _hg_repos()
71 {
71 {
72 local i
72 local i
73 for i in $(compgen -d -- "$cur"); do
73 for i in $(compgen -d -- "$cur"); do
74 test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
74 test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
75 done
75 done
76 }
76 }
77
77
78 _hg_status()
78 _hg_status()
79 {
79 {
80 local files="$("$hg" status -n$1 . 2>/dev/null)"
80 local files="$("$hg" status -n$1 . 2>/dev/null)"
81 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
81 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
82 }
82 }
83
83
84 _hg_tags()
84 _hg_tags()
85 {
85 {
86 local tags="$("$hg" tags -q 2>/dev/null)"
86 local tags="$("$hg" tags -q 2>/dev/null)"
87 local IFS=$'\n'
87 local IFS=$'\n'
88 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur"))
88 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur"))
89 }
89 }
90
90
91 # this is "kind of" ugly...
91 # this is "kind of" ugly...
92 _hg_count_non_option()
92 _hg_count_non_option()
93 {
93 {
94 local i count=0
94 local i count=0
95 local filters="$1"
95 local filters="$1"
96
96
97 for ((i=1; $i<=$COMP_CWORD; i++)); do
97 for ((i=1; $i<=$COMP_CWORD; i++)); do
98 if [[ "${COMP_WORDS[i]}" != -* ]]; then
98 if [[ "${COMP_WORDS[i]}" != -* ]]; then
99 if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
99 if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
100 continue
100 continue
101 fi
101 fi
102 count=$(($count + 1))
102 count=$(($count + 1))
103 fi
103 fi
104 done
104 done
105
105
106 echo $(($count - 1))
106 echo $(($count - 1))
107 }
107 }
108
108
109 _hg()
109 _hg()
110 {
110 {
111 local cur prev cmd cmd_index opts i
111 local cur prev cmd cmd_index opts i
112 # global options that receive an argument
112 # global options that receive an argument
113 local global_args='--cwd|-R|--repository'
113 local global_args='--cwd|-R|--repository'
114 local hg="$1"
114 local hg="$1"
115 local canonical=0
115 local canonical=0
116
116
117 COMPREPLY=()
117 COMPREPLY=()
118 cur="$2"
118 cur="$2"
119 prev="$3"
119 prev="$3"
120
120
121 # searching for the command
121 # searching for the command
122 # (first non-option argument that doesn't follow a global option that
122 # (first non-option argument that doesn't follow a global option that
123 # receives an argument)
123 # receives an argument)
124 for ((i=1; $i<=$COMP_CWORD; i++)); do
124 for ((i=1; $i<=$COMP_CWORD; i++)); do
125 if [[ ${COMP_WORDS[i]} != -* ]]; then
125 if [[ ${COMP_WORDS[i]} != -* ]]; then
126 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
126 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
127 cmd="${COMP_WORDS[i]}"
127 cmd="${COMP_WORDS[i]}"
128 cmd_index=$i
128 cmd_index=$i
129 break
129 break
130 fi
130 fi
131 fi
131 fi
132 done
132 done
133
133
134 if [[ "$cur" == -* ]]; then
134 if [[ "$cur" == -* ]]; then
135 if [ "$(type -t "_hg_opt_$cmd")" = function ] && "_hg_opt_$cmd"; then
135 if [ "$(type -t "_hg_opt_$cmd")" = function ] && "_hg_opt_$cmd"; then
136 return
136 return
137 fi
137 fi
138
138
139 opts=$("$hg" debugcomplete --options "$cmd" 2>/dev/null)
139 opts=$("$hg" debugcomplete --options "$cmd" 2>/dev/null)
140
140
141 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
141 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
142 return
142 return
143 fi
143 fi
144
144
145 # global options
145 # global options
146 case "$prev" in
146 case "$prev" in
147 -R|--repository)
147 -R|--repository)
148 _hg_paths
148 _hg_paths
149 _hg_repos
149 _hg_repos
150 return
150 return
151 ;;
151 ;;
152 --cwd)
152 --cwd)
153 # Stick with default bash completion
153 # Stick with default bash completion
154 return
154 return
155 ;;
155 ;;
156 esac
156 esac
157
157
158 if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then
158 if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then
159 _hg_commands
159 _hg_commands
160 return
160 return
161 fi
161 fi
162
162
163 # try to generate completion candidates for whatever command the user typed
163 # try to generate completion candidates for whatever command the user typed
164 local help
164 local help
165 if _hg_command_specific; then
165 if _hg_command_specific; then
166 return
166 return
167 fi
167 fi
168
168
169 # canonicalize the command name and try again
169 # canonicalize the command name and try again
170 help=$("$hg" help "$cmd" 2>/dev/null)
170 help=$("$hg" help "$cmd" 2>/dev/null)
171 if [ $? -ne 0 ]; then
171 if [ $? -ne 0 ]; then
172 # Probably either the command doesn't exist or it's ambiguous
172 # Probably either the command doesn't exist or it's ambiguous
173 return
173 return
174 fi
174 fi
175 cmd=${help#hg }
175 cmd=${help#hg }
176 cmd=${cmd%%[$' \n']*}
176 cmd=${cmd%%[$' \n']*}
177 canonical=1
177 canonical=1
178 _hg_command_specific
178 _hg_command_specific
179 }
179 }
180
180
181 _hg_command_specific()
181 _hg_command_specific()
182 {
182 {
183 if [ "$(type -t "_hg_cmd_$cmd")" = function ]; then
183 if [ "$(type -t "_hg_cmd_$cmd")" = function ]; then
184 "_hg_cmd_$cmd"
184 "_hg_cmd_$cmd"
185 return 0
185 return 0
186 fi
186 fi
187
187
188 if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" == --rev ]; then
188 if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" == --rev ]; then
189 if [ $canonical = 1 ]; then
189 if [ $canonical = 1 ]; then
190 _hg_tags
190 _hg_tags
191 return 0
191 return 0
192 elif [[ status != "$cmd"* ]]; then
192 elif [[ status != "$cmd"* ]]; then
193 _hg_tags
193 _hg_tags
194 return 0
194 return 0
195 else
195 else
196 return 1
196 return 1
197 fi
197 fi
198 fi
198 fi
199
199
200 case "$cmd" in
200 case "$cmd" in
201 help)
201 help)
202 _hg_commands
202 _hg_commands
203 ;;
203 ;;
204 export)
204 export)
205 if _hg_ext_mq_patchlist qapplied && [ "${COMPREPLY[*]}" ]; then
205 if _hg_ext_mq_patchlist qapplied && [ "${COMPREPLY[*]}" ]; then
206 return 0
206 return 0
207 fi
207 fi
208 _hg_tags
208 _hg_tags
209 ;;
209 ;;
210 manifest|update)
210 manifest|update)
211 _hg_tags
211 _hg_tags
212 ;;
212 ;;
213 pull|push|outgoing|incoming)
213 pull|push|outgoing|incoming)
214 _hg_paths
214 _hg_paths
215 _hg_repos
215 _hg_repos
216 ;;
216 ;;
217 paths)
217 paths)
218 _hg_paths
218 _hg_paths
219 ;;
219 ;;
220 add)
220 add)
221 _hg_status "u"
221 _hg_status "u"
222 ;;
222 ;;
223 commit)
223 commit)
224 _hg_status "mar"
224 _hg_status "mar"
225 ;;
225 ;;
226 remove)
226 remove)
227 _hg_status "d"
227 _hg_status "d"
228 ;;
228 ;;
229 forget)
229 forget)
230 _hg_status "a"
230 _hg_status "a"
231 ;;
231 ;;
232 diff)
232 diff)
233 _hg_status "mar"
233 _hg_status "mar"
234 ;;
234 ;;
235 revert)
235 revert)
236 _hg_status "mard"
236 _hg_status "mard"
237 ;;
237 ;;
238 clone)
238 clone)
239 local count=$(_hg_count_non_option)
239 local count=$(_hg_count_non_option)
240 if [ $count = 1 ]; then
240 if [ $count = 1 ]; then
241 _hg_paths
241 _hg_paths
242 fi
242 fi
243 _hg_repos
243 _hg_repos
244 ;;
244 ;;
245 debugindex|debugindexdot)
245 debugindex|debugindexdot)
246 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
246 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
247 ;;
247 ;;
248 debugdata)
248 debugdata)
249 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
249 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
250 ;;
250 ;;
251 *)
251 *)
252 return 1
252 return 1
253 ;;
253 ;;
254 esac
254 esac
255
255
256 return 0
256 return 0
257 }
257 }
258
258
259 complete -o bashdefault -o default -F _hg hg 2>/dev/null \
259 complete -o bashdefault -o default -F _hg hg 2>/dev/null \
260 || complete -o default -F _hg hg
260 || complete -o default -F _hg hg
261
261
262
262
263 # Completion for commands provided by extensions
263 # Completion for commands provided by extensions
264
264
265 # mq
265 # mq
266 _hg_ext_mq_patchlist()
266 _hg_ext_mq_patchlist()
267 {
267 {
268 local patches
268 local patches
269 patches=$("$hg" $1 2>/dev/null)
269 patches=$("$hg" $1 2>/dev/null)
270 if [ $? -eq 0 ] && [ "$patches" ]; then
270 if [ $? -eq 0 ] && [ "$patches" ]; then
271 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$patches' -- "$cur"))
271 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$patches' -- "$cur"))
272 return 0
272 return 0
273 fi
273 fi
274 return 1
274 return 1
275 }
275 }
276
276
277 _hg_ext_mq_queues()
277 _hg_ext_mq_queues()
278 {
278 {
279 local root=$("$hg" root 2>/dev/null)
279 local root=$("$hg" root 2>/dev/null)
280 local n
280 local n
281 for n in $(cd "$root"/.hg && compgen -d -- "$cur"); do
281 for n in $(cd "$root"/.hg && compgen -d -- "$cur"); do
282 # I think we're usually not interested in the regular "patches" queue
282 # I think we're usually not interested in the regular "patches" queue
283 # so just filter it.
283 # so just filter it.
284 if [ "$n" != patches ] && [ -e "$root/.hg/$n/series" ]; then
284 if [ "$n" != patches ] && [ -e "$root/.hg/$n/series" ]; then
285 COMPREPLY=(${COMPREPLY[@]:-} "$n")
285 COMPREPLY=(${COMPREPLY[@]:-} "$n")
286 fi
286 fi
287 done
287 done
288 }
288 }
289
289
290 _hg_cmd_qpop()
290 _hg_cmd_qpop()
291 {
291 {
292 if [[ "$prev" = @(-n|--name) ]]; then
292 if [[ "$prev" = @(-n|--name) ]]; then
293 _hg_ext_mq_queues
293 _hg_ext_mq_queues
294 return
294 return
295 fi
295 fi
296 _hg_ext_mq_patchlist qapplied
296 _hg_ext_mq_patchlist qapplied
297 }
297 }
298
298
299 _hg_cmd_qpush()
299 _hg_cmd_qpush()
300 {
300 {
301 if [[ "$prev" = @(-n|--name) ]]; then
301 if [[ "$prev" = @(-n|--name) ]]; then
302 _hg_ext_mq_queues
302 _hg_ext_mq_queues
303 return
303 return
304 fi
304 fi
305 _hg_ext_mq_patchlist qunapplied
305 _hg_ext_mq_patchlist qunapplied
306 }
306 }
307
307
308 _hg_cmd_qdelete()
308 _hg_cmd_qdelete()
309 {
309 {
310 local qcmd=qunapplied
310 local qcmd=qunapplied
311 if [[ "$prev" = @(-r|--rev) ]]; then
311 if [[ "$prev" = @(-r|--rev) ]]; then
312 qcmd=qapplied
312 qcmd=qapplied
313 fi
313 fi
314 _hg_ext_mq_patchlist $qcmd
314 _hg_ext_mq_patchlist $qcmd
315 }
315 }
316
316
317 _hg_cmd_qsave()
317 _hg_cmd_qsave()
318 {
318 {
319 if [[ "$prev" = @(-n|--name) ]]; then
319 if [[ "$prev" = @(-n|--name) ]]; then
320 _hg_ext_mq_queues
320 _hg_ext_mq_queues
321 return
321 return
322 fi
322 fi
323 }
323 }
324
324
325 _hg_cmd_strip()
325 _hg_cmd_strip()
326 {
326 {
327 _hg_tags
327 _hg_tags
328 }
328 }
329
329
330 _hg_cmd_qcommit()
330 _hg_cmd_qcommit()
331 {
331 {
332 local root=$("$hg" root 2>/dev/null)
332 local root=$("$hg" root 2>/dev/null)
333 # this is run in a sub-shell, so we can't use _hg_status
333 # this is run in a sub-shell, so we can't use _hg_status
334 local files=$(cd "$root/.hg/patches" 2>/dev/null &&
334 local files=$(cd "$root/.hg/patches" 2>/dev/null &&
335 "$hg" status -nmar 2>/dev/null)
335 "$hg" status -nmar 2>/dev/null)
336 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
336 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
337 }
337 }
338
338
339 _hg_cmd_qfold()
339 _hg_cmd_qfold()
340 {
340 {
341 _hg_ext_mq_patchlist qunapplied
341 _hg_ext_mq_patchlist qunapplied
342 }
342 }
343
343
344 _hg_cmd_qrename()
344 _hg_cmd_qrename()
345 {
345 {
346 _hg_ext_mq_patchlist qseries
346 _hg_ext_mq_patchlist qseries
347 }
347 }
348
348
349 _hg_cmd_qheader()
349 _hg_cmd_qheader()
350 {
350 {
351 _hg_ext_mq_patchlist qseries
351 _hg_ext_mq_patchlist qseries
352 }
352 }
353
353
354 _hg_cmd_qclone()
354 _hg_cmd_qclone()
355 {
355 {
356 local count=$(_hg_count_non_option)
356 local count=$(_hg_count_non_option)
357 if [ $count = 1 ]; then
357 if [ $count = 1 ]; then
358 _hg_paths
358 _hg_paths
359 fi
359 fi
360 _hg_repos
360 _hg_repos
361 }
361 }
362
362
363 _hg_ext_mq_guards()
363 _hg_ext_mq_guards()
364 {
364 {
365 "$hg" qselect --series 2>/dev/null | sed -e 's/^.//'
365 "$hg" qselect --series 2>/dev/null | sed -e 's/^.//'
366 }
366 }
367
367
368 _hg_cmd_qselect()
368 _hg_cmd_qselect()
369 {
369 {
370 local guards=$(_hg_ext_mq_guards)
370 local guards=$(_hg_ext_mq_guards)
371 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$guards' -- "$cur"))
371 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$guards' -- "$cur"))
372 }
372 }
373
373
374 _hg_cmd_qguard()
374 _hg_cmd_qguard()
375 {
375 {
376 local prefix=''
376 local prefix=''
377
377
378 if [[ "$cur" == +* ]]; then
378 if [[ "$cur" == +* ]]; then
379 prefix=+
379 prefix=+
380 elif [[ "$cur" == -* ]]; then
380 elif [[ "$cur" == -* ]]; then
381 prefix=-
381 prefix=-
382 fi
382 fi
383 local ncur=${cur#[-+]}
383 local ncur=${cur#[-+]}
384
384
385 if ! [ "$prefix" ]; then
385 if ! [ "$prefix" ]; then
386 _hg_ext_mq_patchlist qseries
386 _hg_ext_mq_patchlist qseries
387 return
387 return
388 fi
388 fi
389
389
390 local guards=$(_hg_ext_mq_guards)
390 local guards=$(_hg_ext_mq_guards)
391 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -P $prefix -W '$guards' -- "$ncur"))
391 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -P $prefix -W '$guards' -- "$ncur"))
392 }
392 }
393
393
394 _hg_opt_qguard()
394 _hg_opt_qguard()
395 {
395 {
396 local i
396 local i
397 for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
397 for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
398 if [[ ${COMP_WORDS[i]} != -* ]]; then
398 if [[ ${COMP_WORDS[i]} != -* ]]; then
399 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
399 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
400 _hg_cmd_qguard
400 _hg_cmd_qguard
401 return 0
401 return 0
402 fi
402 fi
403 elif [ "${COMP_WORDS[i]}" = -- ]; then
403 elif [ "${COMP_WORDS[i]}" = -- ]; then
404 _hg_cmd_qguard
404 _hg_cmd_qguard
405 return 0
405 return 0
406 fi
406 fi
407 done
407 done
408 return 1
408 return 1
409 }
409 }
410
410
411
411
412 # hbisect
412 # hbisect
413 _hg_cmd_bisect()
413 _hg_cmd_bisect()
414 {
414 {
415 local i subcmd
415 local i subcmd
416
416
417 # find the sub-command
417 # find the sub-command
418 for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
418 for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
419 if [[ ${COMP_WORDS[i]} != -* ]]; then
419 if [[ ${COMP_WORDS[i]} != -* ]]; then
420 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
420 if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
421 subcmd="${COMP_WORDS[i]}"
421 subcmd="${COMP_WORDS[i]}"
422 break
422 break
423 fi
423 fi
424 fi
424 fi
425 done
425 done
426
426
427 if [ -z "$subcmd" ] || [ $COMP_CWORD -eq $i ] || [ "$subcmd" = help ]; then
427 if [ -z "$subcmd" ] || [ $COMP_CWORD -eq $i ] || [ "$subcmd" = help ]; then
428 COMPREPLY=(${COMPREPLY[@]:-}
428 COMPREPLY=(${COMPREPLY[@]:-}
429 $(compgen -W 'bad good help init next reset' -- "$cur"))
429 $(compgen -W 'bad good help init next reset' -- "$cur"))
430 return
430 return
431 fi
431 fi
432
432
433 case "$subcmd" in
433 case "$subcmd" in
434 good|bad)
434 good|bad)
435 _hg_tags
435 _hg_tags
436 ;;
436 ;;
437 esac
437 esac
438
438
439 return
439 return
440 }
440 }
441
441
442
442
443 # patchbomb
443 # patchbomb
444 _hg_cmd_email()
444 _hg_cmd_email()
445 {
445 {
446 case "$prev" in
446 case "$prev" in
447 -c|--cc|-t|--to|-f|--from|--bcc)
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 # to get them
449 # to get them
450 if [ "$(type -t _hg_emails)" = function ]; then
450 if [ "$(type -t _hg_emails)" = function ]; then
451 local arg=to
451 local arg=to
452 if [[ "$prev" == @(-f|--from) ]]; then
452 if [[ "$prev" == @(-f|--from) ]]; then
453 arg=from
453 arg=from
454 fi
454 fi
455 local addresses=$(_hg_emails $arg)
455 local addresses=$(_hg_emails $arg)
456 COMPREPLY=(${COMPREPLY[@]:-}
456 COMPREPLY=(${COMPREPLY[@]:-}
457 $(compgen -W '$addresses' -- "$cur"))
457 $(compgen -W '$addresses' -- "$cur"))
458 fi
458 fi
459 return
459 return
460 ;;
460 ;;
461 -m|--mbox)
461 -m|--mbox)
462 # fallback to standard filename completion
462 # fallback to standard filename completion
463 return
463 return
464 ;;
464 ;;
465 -s|--subject)
465 -s|--subject)
466 # free form string
466 # free form string
467 return
467 return
468 ;;
468 ;;
469 esac
469 esac
470
470
471 _hg_tags
471 _hg_tags
472 return
472 return
473 }
473 }
474
474
475
475
476 # gpg
476 # gpg
477 _hg_cmd_sign()
477 _hg_cmd_sign()
478 {
478 {
479 _hg_tags
479 _hg_tags
480 }
480 }
481
481
482
482
483 # transplant
483 # transplant
484 _hg_cmd_transplant()
484 _hg_cmd_transplant()
485 {
485 {
486 case "$prev" in
486 case "$prev" in
487 -s|--source)
487 -s|--source)
488 _hg_paths
488 _hg_paths
489 _hg_repos
489 _hg_repos
490 return
490 return
491 ;;
491 ;;
492 --filter)
492 --filter)
493 # standard filename completion
493 # standard filename completion
494 return
494 return
495 ;;
495 ;;
496 esac
496 esac
497
497
498 # all other transplant options values and command parameters are revisions
498 # all other transplant options values and command parameters are revisions
499 _hg_tags
499 _hg_tags
500 return
500 return
501 }
501 }
502
502
@@ -1,105 +1,105 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 import os, sys, struct, stat
3 import os, sys, struct, stat
4 import difflib
4 import difflib
5 import re
5 import re
6 from optparse import OptionParser
6 from optparse import OptionParser
7 from mercurial.bdiff import bdiff, blocks
7 from mercurial.bdiff import bdiff, blocks
8 from mercurial.mdiff import bunidiff, diffopts
8 from mercurial.mdiff import bunidiff, diffopts
9
9
10 VERSION="0.3"
10 VERSION="0.3"
11 usage = "usage: %prog [options] file1 file2"
11 usage = "usage: %prog [options] file1 file2"
12 parser = OptionParser(usage=usage)
12 parser = OptionParser(usage=usage)
13
13
14 parser.add_option("-d", "--difflib", action="store_true", default=False)
14 parser.add_option("-d", "--difflib", action="store_true", default=False)
15 parser.add_option('-x', '--count', default=1)
15 parser.add_option('-x', '--count', default=1)
16 parser.add_option('-c', '--context', type="int", default=3)
16 parser.add_option('-c', '--context', type="int", default=3)
17 parser.add_option('-p', '--show-c-function', action="store_true", default=False)
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 default=False)
19 default=False)
20
20
21 (options, args) = parser.parse_args()
21 (options, args) = parser.parse_args()
22
22
23 if not args:
23 if not args:
24 parser.print_help()
24 parser.print_help()
25 sys.exit(1)
25 sys.exit(1)
26
26
27 # simple utility function to put all the
27 # simple utility function to put all the
28 # files from a directory tree into a dict
28 # files from a directory tree into a dict
29 def buildlist(names, top):
29 def buildlist(names, top):
30 tlen = len(top)
30 tlen = len(top)
31 for root, dirs, files in os.walk(top):
31 for root, dirs, files in os.walk(top):
32 l = root[tlen + 1:]
32 l = root[tlen + 1:]
33 for x in files:
33 for x in files:
34 p = os.path.join(root, x)
34 p = os.path.join(root, x)
35 st = os.lstat(p)
35 st = os.lstat(p)
36 if stat.S_ISREG(st.st_mode):
36 if stat.S_ISREG(st.st_mode):
37 names[os.path.join(l, x)] = (st.st_dev, st.st_ino)
37 names[os.path.join(l, x)] = (st.st_dev, st.st_ino)
38
38
39 def diff_files(file1, file2):
39 def diff_files(file1, file2):
40 if file1 == None:
40 if file1 == None:
41 b = file(file2).read().splitlines(1)
41 b = file(file2).read().splitlines(1)
42 l1 = "--- %s\n" % (file2)
42 l1 = "--- %s\n" % (file2)
43 l2 = "+++ %s\n" % (file2)
43 l2 = "+++ %s\n" % (file2)
44 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
44 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
45 l = [l1, l2, l3] + ["+" + e for e in b]
45 l = [l1, l2, l3] + ["+" + e for e in b]
46 elif file2 == None:
46 elif file2 == None:
47 a = file(file1).read().splitlines(1)
47 a = file(file1).read().splitlines(1)
48 l1 = "--- %s\n" % (file1)
48 l1 = "--- %s\n" % (file1)
49 l2 = "+++ %s\n" % (file1)
49 l2 = "+++ %s\n" % (file1)
50 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
50 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
51 l = [l1, l2, l3] + ["-" + e for e in a]
51 l = [l1, l2, l3] + ["-" + e for e in a]
52 else:
52 else:
53 t1 = file(file1).read()
53 t1 = file(file1).read()
54 t2 = file(file2).read()
54 t2 = file(file2).read()
55 l1 = t1.splitlines(1)
55 l1 = t1.splitlines(1)
56 l2 = t2.splitlines(1)
56 l2 = t2.splitlines(1)
57 if options.difflib:
57 if options.difflib:
58 l = difflib.unified_diff(l1, l2, file1, file2)
58 l = difflib.unified_diff(l1, l2, file1, file2)
59 else:
59 else:
60 l = bunidiff(t1, t2, l1, l2, file1, file2,
60 l = bunidiff(t1, t2, l1, l2, file1, file2,
61 diffopts(context=options.context,
61 diffopts(context=options.context,
62 showfunc=options.show_c_function,
62 showfunc=options.show_c_function,
63 ignorews=options.ignore_all_space))
63 ignorews=options.ignore_all_space))
64 for x in l:
64 for x in l:
65 if x[-1] != '\n':
65 if x[-1] != '\n':
66 x += "\n\ No newline at end of file\n"
66 x += "\n\ No newline at end of file\n"
67 print x,
67 print x,
68
68
69 file1 = args[0]
69 file1 = args[0]
70 file2 = args[1]
70 file2 = args[1]
71
71
72 if os.path.isfile(file1) and os.path.isfile(file2):
72 if os.path.isfile(file1) and os.path.isfile(file2):
73 diff_files(file1, file2)
73 diff_files(file1, file2)
74 elif os.path.isdir(file1):
74 elif os.path.isdir(file1):
75 if not os.path.isdir(file2):
75 if not os.path.isdir(file2):
76 sys.stderr.write("file types don't match\n")
76 sys.stderr.write("file types don't match\n")
77 sys.exit(1)
77 sys.exit(1)
78
78
79 d1 = {}
79 d1 = {}
80 d2 = {}
80 d2 = {}
81
81
82 buildlist(d1, file1)
82 buildlist(d1, file1)
83 buildlist(d2, file2)
83 buildlist(d2, file2)
84 keys = d1.keys()
84 keys = d1.keys()
85 keys.sort()
85 keys.sort()
86 for x in keys:
86 for x in keys:
87 if x not in d2:
87 if x not in d2:
88 f2 = None
88 f2 = None
89 else:
89 else:
90 f2 = os.path.join(file2, x)
90 f2 = os.path.join(file2, x)
91 st1 = d1[x]
91 st1 = d1[x]
92 st2 = d2[x]
92 st2 = d2[x]
93 del d2[x]
93 del d2[x]
94 if st1[0] == st2[0] and st1[1] == st2[1]:
94 if st1[0] == st2[0] and st1[1] == st2[1]:
95 sys.stderr.write("%s is a hard link\n" % x)
95 sys.stderr.write("%s is a hard link\n" % x)
96 continue
96 continue
97 x = os.path.join(file1, x)
97 x = os.path.join(file1, x)
98 diff_files(x, f2)
98 diff_files(x, f2)
99 keys = d2.keys()
99 keys = d2.keys()
100 keys.sort()
100 keys.sort()
101 for x in keys:
101 for x in keys:
102 f1 = None
102 f1 = None
103 x = os.path.join(file2, x)
103 x = os.path.join(file2, x)
104 diff_files(f1, x)
104 diff_files(f1, x)
105
105
@@ -1,439 +1,439 b''
1 /*
1 /*
2 * hgsh.c - restricted login shell for mercurial
2 * hgsh.c - restricted login shell for mercurial
3 *
3 *
4 * Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 * Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 *
5 *
6 * This software may be used and distributed according to the terms of the
6 * This software may be used and distributed according to the terms of the
7 * GNU General Public License, incorporated herein by reference.
7 * GNU General Public License, incorporated herein by reference.
8 *
8 *
9 * this program is login shell for dedicated mercurial user account. it
9 * this program is login shell for dedicated mercurial user account. it
10 * only allows few actions:
10 * only allows few actions:
11 *
11 *
12 * 1. run hg in server mode on specific repository. no other hg commands
12 * 1. run hg in server mode on specific repository. no other hg commands
13 * are allowed. we try to verify that repo to be accessed exists under
13 * are allowed. we try to verify that repo to be accessed exists under
14 * given top-level directory.
14 * given top-level directory.
15 *
15 *
16 * 2. (optional) forward ssh connection from firewall/gateway machine to
16 * 2. (optional) forward ssh connection from firewall/gateway machine to
17 * "real" mercurial host, to let users outside intranet pull and push
17 * "real" mercurial host, to let users outside intranet pull and push
18 * changes through firewall.
18 * changes through firewall.
19 *
19 *
20 * 3. (optional) run normal shell, to allow to "su" to mercurial user, use
20 * 3. (optional) run normal shell, to allow to "su" to mercurial user, use
21 * "sudo" to run programs as that user, or run cron jobs as that user.
21 * "sudo" to run programs as that user, or run cron jobs as that user.
22 *
22 *
23 * only tested on linux yet. patches for non-linux systems welcome.
23 * only tested on linux yet. patches for non-linux systems welcome.
24 */
24 */
25
25
26 #ifndef _GNU_SOURCE
26 #ifndef _GNU_SOURCE
27 #define _GNU_SOURCE /* for asprintf */
27 #define _GNU_SOURCE /* for asprintf */
28 #endif
28 #endif
29
29
30 #include <stdio.h>
30 #include <stdio.h>
31 #include <stdlib.h>
31 #include <stdlib.h>
32 #include <string.h>
32 #include <string.h>
33 #include <sys/stat.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
34 #include <sys/types.h>
35 #include <sysexits.h>
35 #include <sysexits.h>
36 #include <unistd.h>
36 #include <unistd.h>
37
37
38 /*
38 /*
39 * user config.
39 * user config.
40 *
40 *
41 * if you see a hostname below, just use first part of hostname. example,
41 * if you see a hostname below, just use first part of hostname. example,
42 * if you have host named foo.bar.com, use "foo".
42 * if you have host named foo.bar.com, use "foo".
43 */
43 */
44
44
45 /*
45 /*
46 * HG_GATEWAY: hostname of gateway/firewall machine that people outside your
46 * HG_GATEWAY: hostname of gateway/firewall machine that people outside your
47 * intranet ssh into if they need to ssh to other machines. if you do not
47 * intranet ssh into if they need to ssh to other machines. if you do not
48 * have such machine, set to NULL.
48 * have such machine, set to NULL.
49 */
49 */
50 #ifndef HG_GATEWAY
50 #ifndef HG_GATEWAY
51 #define HG_GATEWAY "gateway"
51 #define HG_GATEWAY "gateway"
52 #endif
52 #endif
53
53
54 /*
54 /*
55 * HG_HOST: hostname of mercurial server. if any machine is allowed, set to
55 * HG_HOST: hostname of mercurial server. if any machine is allowed, set to
56 * NULL.
56 * NULL.
57 */
57 */
58 #ifndef HG_HOST
58 #ifndef HG_HOST
59 #define HG_HOST "mercurial"
59 #define HG_HOST "mercurial"
60 #endif
60 #endif
61
61
62 /*
62 /*
63 * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and
63 * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and
64 * host username are same, set to NULL.
64 * host username are same, set to NULL.
65 */
65 */
66 #ifndef HG_USER
66 #ifndef HG_USER
67 #define HG_USER "hg"
67 #define HG_USER "hg"
68 #endif
68 #endif
69
69
70 /*
70 /*
71 * HG_ROOT: root of tree full of mercurial repos. if you do not want to
71 * HG_ROOT: root of tree full of mercurial repos. if you do not want to
72 * validate location of repo when someone is try to access, set to NULL.
72 * validate location of repo when someone is try to access, set to NULL.
73 */
73 */
74 #ifndef HG_ROOT
74 #ifndef HG_ROOT
75 #define HG_ROOT "/home/hg/repos"
75 #define HG_ROOT "/home/hg/repos"
76 #endif
76 #endif
77
77
78 /*
78 /*
79 * HG: path to the mercurial executable to run.
79 * HG: path to the mercurial executable to run.
80 */
80 */
81 #ifndef HG
81 #ifndef HG
82 #define HG "/home/hg/bin/hg"
82 #define HG "/home/hg/bin/hg"
83 #endif
83 #endif
84
84
85 /*
85 /*
86 * HG_SHELL: shell to use for actions like "sudo" and "su" access to
86 * HG_SHELL: shell to use for actions like "sudo" and "su" access to
87 * mercurial user, and cron jobs. if you want to make these things
87 * mercurial user, and cron jobs. if you want to make these things
88 * impossible, set to NULL.
88 * impossible, set to NULL.
89 */
89 */
90 #ifndef HG_SHELL
90 #ifndef HG_SHELL
91 #define HG_SHELL NULL
91 #define HG_SHELL NULL
92 // #define HG_SHELL "/bin/bash"
92 // #define HG_SHELL "/bin/bash"
93 #endif
93 #endif
94
94
95 /*
95 /*
96 * HG_HELP: some way for users to get support if they have problem. if they
96 * HG_HELP: some way for users to get support if they have problem. if they
97 * should not get helpful message, set to NULL.
97 * should not get helpful message, set to NULL.
98 */
98 */
99 #ifndef HG_HELP
99 #ifndef HG_HELP
100 #define HG_HELP "please contact support@example.com for help."
100 #define HG_HELP "please contact support@example.com for help."
101 #endif
101 #endif
102
102
103 /*
103 /*
104 * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to
104 * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to
105 * HG_HOST. if you want to use rsh instead (why?), you need to modify
105 * HG_HOST. if you want to use rsh instead (why?), you need to modify
106 * arguments it is called with. see forward_through_gateway.
106 * arguments it is called with. see forward_through_gateway.
107 */
107 */
108 #ifndef SSH
108 #ifndef SSH
109 #define SSH "/usr/bin/ssh"
109 #define SSH "/usr/bin/ssh"
110 #endif
110 #endif
111
111
112 /*
112 /*
113 * tell whether to print command that is to be executed. useful for
113 * tell whether to print command that is to be executed. useful for
114 * debugging. should not interfere with mercurial operation, since
114 * debugging. should not interfere with mercurial operation, since
115 * mercurial only cares about stdin and stdout, and this prints to stderr.
115 * mercurial only cares about stdin and stdout, and this prints to stderr.
116 */
116 */
117 static const int debug = 0;
117 static const int debug = 0;
118
118
119 static void print_cmdline(int argc, char **argv)
119 static void print_cmdline(int argc, char **argv)
120 {
120 {
121 FILE *fp = stderr;
121 FILE *fp = stderr;
122 int i;
122 int i;
123
123
124 fputs("command: ", fp);
124 fputs("command: ", fp);
125
125
126 for (i = 0; i < argc; i++) {
126 for (i = 0; i < argc; i++) {
127 char *spc = strpbrk(argv[i], " \t\r\n");
127 char *spc = strpbrk(argv[i], " \t\r\n");
128 if (spc) {
128 if (spc) {
129 fputc('\'', fp);
129 fputc('\'', fp);
130 }
130 }
131 fputs(argv[i], fp);
131 fputs(argv[i], fp);
132 if (spc) {
132 if (spc) {
133 fputc('\'', fp);
133 fputc('\'', fp);
134 }
134 }
135 if (i < argc - 1) {
135 if (i < argc - 1) {
136 fputc(' ', fp);
136 fputc(' ', fp);
137 }
137 }
138 }
138 }
139 fputc('\n', fp);
139 fputc('\n', fp);
140 fflush(fp);
140 fflush(fp);
141 }
141 }
142
142
143 static void usage(const char *reason, int exitcode)
143 static void usage(const char *reason, int exitcode)
144 {
144 {
145 char *hg_help = HG_HELP;
145 char *hg_help = HG_HELP;
146
146
147 if (reason) {
147 if (reason) {
148 fprintf(stderr, "*** Error: %s.\n", reason);
148 fprintf(stderr, "*** Error: %s.\n", reason);
149 }
149 }
150 fprintf(stderr, "*** This program has been invoked incorrectly.\n");
150 fprintf(stderr, "*** This program has been invoked incorrectly.\n");
151 if (hg_help) {
151 if (hg_help) {
152 fprintf(stderr, "*** %s\n", hg_help);
152 fprintf(stderr, "*** %s\n", hg_help);
153 }
153 }
154 exit(exitcode ? exitcode : EX_USAGE);
154 exit(exitcode ? exitcode : EX_USAGE);
155 }
155 }
156
156
157 /*
157 /*
158 * run on gateway host to make another ssh connection, to "real" mercurial
158 * run on gateway host to make another ssh connection, to "real" mercurial
159 * server. it sends its command line unmodified to far end.
159 * server. it sends its command line unmodified to far end.
160 *
160 *
161 * never called if HG_GATEWAY is NULL.
161 * never called if HG_GATEWAY is NULL.
162 */
162 */
163 static void forward_through_gateway(int argc, char **argv)
163 static void forward_through_gateway(int argc, char **argv)
164 {
164 {
165 char *ssh = SSH;
165 char *ssh = SSH;
166 char *hg_host = HG_HOST;
166 char *hg_host = HG_HOST;
167 char *hg_user = HG_USER;
167 char *hg_user = HG_USER;
168 char **nargv = alloca((10 + argc) * sizeof(char *));
168 char **nargv = alloca((10 + argc) * sizeof(char *));
169 int i = 0, j;
169 int i = 0, j;
170
170
171 nargv[i++] = ssh;
171 nargv[i++] = ssh;
172 nargv[i++] = "-q";
172 nargv[i++] = "-q";
173 nargv[i++] = "-T";
173 nargv[i++] = "-T";
174 nargv[i++] = "-x";
174 nargv[i++] = "-x";
175 if (hg_user) {
175 if (hg_user) {
176 nargv[i++] = "-l";
176 nargv[i++] = "-l";
177 nargv[i++] = hg_user;
177 nargv[i++] = hg_user;
178 }
178 }
179 nargv[i++] = hg_host;
179 nargv[i++] = hg_host;
180
180
181 /*
181 /*
182 * sshd called us with added "-c", because it thinks we are a shell.
182 * sshd called us with added "-c", because it thinks we are a shell.
183 * drop it if we find it.
183 * drop it if we find it.
184 */
184 */
185 j = 1;
185 j = 1;
186 if (j < argc && strcmp(argv[j], "-c") == 0) {
186 if (j < argc && strcmp(argv[j], "-c") == 0) {
187 j++;
187 j++;
188 }
188 }
189
189
190 for (; j < argc; i++, j++) {
190 for (; j < argc; i++, j++) {
191 nargv[i] = argv[j];
191 nargv[i] = argv[j];
192 }
192 }
193 nargv[i] = NULL;
193 nargv[i] = NULL;
194
194
195 if (debug) {
195 if (debug) {
196 print_cmdline(i, nargv);
196 print_cmdline(i, nargv);
197 }
197 }
198
198
199 execv(ssh, nargv);
199 execv(ssh, nargv);
200 perror(ssh);
200 perror(ssh);
201 exit(EX_UNAVAILABLE);
201 exit(EX_UNAVAILABLE);
202 }
202 }
203
203
204 /*
204 /*
205 * run shell. let administrator "su" to mercurial user's account to do
205 * run shell. let administrator "su" to mercurial user's account to do
206 * administrative works.
206 * administrative works.
207 *
207 *
208 * never called if HG_SHELL is NULL.
208 * never called if HG_SHELL is NULL.
209 */
209 */
210 static void run_shell(int argc, char **argv)
210 static void run_shell(int argc, char **argv)
211 {
211 {
212 char *hg_shell = HG_SHELL;
212 char *hg_shell = HG_SHELL;
213 char **nargv;
213 char **nargv;
214 char *c;
214 char *c;
215 int i;
215 int i;
216
216
217 nargv = alloca((argc + 3) * sizeof(char *));
217 nargv = alloca((argc + 3) * sizeof(char *));
218 c = strrchr(hg_shell, '/');
218 c = strrchr(hg_shell, '/');
219
219
220 /* tell "real" shell it is login shell, if needed. */
220 /* tell "real" shell it is login shell, if needed. */
221
221
222 if (argv[0][0] == '-' && c) {
222 if (argv[0][0] == '-' && c) {
223 nargv[0] = strdup(c);
223 nargv[0] = strdup(c);
224 if (nargv[0] == NULL) {
224 if (nargv[0] == NULL) {
225 perror("malloc");
225 perror("malloc");
226 exit(EX_OSERR);
226 exit(EX_OSERR);
227 }
227 }
228 nargv[0][0] = '-';
228 nargv[0][0] = '-';
229 } else {
229 } else {
230 nargv[0] = hg_shell;
230 nargv[0] = hg_shell;
231 }
231 }
232
232
233 for (i = 1; i < argc; i++) {
233 for (i = 1; i < argc; i++) {
234 nargv[i] = argv[i];
234 nargv[i] = argv[i];
235 }
235 }
236 nargv[i] = NULL;
236 nargv[i] = NULL;
237
237
238 if (debug) {
238 if (debug) {
239 print_cmdline(i, nargv);
239 print_cmdline(i, nargv);
240 }
240 }
241
241
242 execv(hg_shell, nargv);
242 execv(hg_shell, nargv);
243 perror(hg_shell);
243 perror(hg_shell);
244 exit(EX_OSFILE);
244 exit(EX_OSFILE);
245 }
245 }
246
246
247 enum cmdline {
247 enum cmdline {
248 hg_init,
248 hg_init,
249 hg_serve,
249 hg_serve,
250 };
250 };
251
251
252
252
253 /*
253 /*
254 * attempt to verify that a directory is really a hg repo, by testing
254 * attempt to verify that a directory is really a hg repo, by testing
255 * for the existence of a subdirectory.
255 * for the existence of a subdirectory.
256 */
256 */
257 static int validate_repo(const char *repo_root, const char *subdir)
257 static int validate_repo(const char *repo_root, const char *subdir)
258 {
258 {
259 char *abs_path;
259 char *abs_path;
260 struct stat st;
260 struct stat st;
261 int ret;
261 int ret;
262
262
263 if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
263 if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
264 ret = -1;
264 ret = -1;
265 goto bail;
265 goto bail;
266 }
266 }
267
267
268 /* verify that we really are looking at valid repo. */
268 /* verify that we really are looking at valid repo. */
269
269
270 if (stat(abs_path, &st) == -1) {
270 if (stat(abs_path, &st) == -1) {
271 ret = 0;
271 ret = 0;
272 } else {
272 } else {
273 ret = 1;
273 ret = 1;
274 }
274 }
275
275
276 bail:
276 bail:
277 return ret;
277 return ret;
278 }
278 }
279
279
280 /*
280 /*
281 * paranoid wrapper, runs hg executable in server mode.
281 * paranoid wrapper, runs hg executable in server mode.
282 */
282 */
283 static void serve_data(int argc, char **argv)
283 static void serve_data(int argc, char **argv)
284 {
284 {
285 char *hg_root = HG_ROOT;
285 char *hg_root = HG_ROOT;
286 char *repo, *repo_root;
286 char *repo, *repo_root;
287 enum cmdline cmd;
287 enum cmdline cmd;
288 char *nargv[6];
288 char *nargv[6];
289 size_t repolen;
289 size_t repolen;
290 int i;
290 int i;
291
291
292 /*
292 /*
293 * check argv for looking okay. we should be invoked with argv
293 * check argv for looking okay. we should be invoked with argv
294 * resembling like this:
294 * resembling like this:
295 *
295 *
296 * hgsh
296 * hgsh
297 * -c
297 * -c
298 * hg -R some/path serve --stdio
298 * hg -R some/path serve --stdio
299 *
299 *
300 * the "-c" is added by sshd, because it thinks we are login shell.
300 * the "-c" is added by sshd, because it thinks we are login shell.
301 */
301 */
302
302
303 if (argc != 3) {
303 if (argc != 3) {
304 goto badargs;
304 goto badargs;
305 }
305 }
306
306
307 if (strcmp(argv[1], "-c") != 0) {
307 if (strcmp(argv[1], "-c") != 0) {
308 goto badargs;
308 goto badargs;
309 }
309 }
310
310
311 if (sscanf(argv[2], "hg init %as", &repo) == 1) {
311 if (sscanf(argv[2], "hg init %as", &repo) == 1) {
312 cmd = hg_init;
312 cmd = hg_init;
313 }
313 }
314 else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) {
314 else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) {
315 cmd = hg_serve;
315 cmd = hg_serve;
316 } else {
316 } else {
317 goto badargs;
317 goto badargs;
318 }
318 }
319
319
320 repolen = repo ? strlen(repo) : 0;
320 repolen = repo ? strlen(repo) : 0;
321
321
322 if (repolen == 0) {
322 if (repolen == 0) {
323 goto badargs;
323 goto badargs;
324 }
324 }
325
325
326 if (hg_root) {
326 if (hg_root) {
327 if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) {
327 if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) {
328 goto badargs;
328 goto badargs;
329 }
329 }
330
330
331 /*
331 /*
332 * attempt to stop break out from inside the repository tree. could
332 * attempt to stop break out from inside the repository tree. could
333 * do something more clever here, because e.g. we could traverse a
333 * do something more clever here, because e.g. we could traverse a
334 * symlink that looks safe, but really breaks us out of tree.
334 * symlink that looks safe, but really breaks us out of tree.
335 */
335 */
336
336
337 if (strstr(repo_root, "/../") != NULL) {
337 if (strstr(repo_root, "/../") != NULL) {
338 goto badargs;
338 goto badargs;
339 }
339 }
340
340
341 /* only hg init expects no repo. */
341 /* only hg init expects no repo. */
342
342
343 if (cmd != hg_init) {
343 if (cmd != hg_init) {
344 int valid;
344 int valid;
345
345
346 valid = validate_repo(repo_root, "data");
346 valid = validate_repo(repo_root, "data");
347
347
348 if (valid == -1) {
348 if (valid == -1) {
349 goto badargs;
349 goto badargs;
350 }
350 }
351
351
352 if (valid == 0) {
352 if (valid == 0) {
353 valid = validate_repo(repo_root, "store");
353 valid = validate_repo(repo_root, "store");
354
354
355 if (valid == -1) {
355 if (valid == -1) {
356 goto badargs;
356 goto badargs;
357 }
357 }
358 }
358 }
359
359
360 if (valid == 0) {
360 if (valid == 0) {
361 perror(repo);
361 perror(repo);
362 exit(EX_DATAERR);
362 exit(EX_DATAERR);
363 }
363 }
364 }
364 }
365
365
366 if (chdir(hg_root) == -1) {
366 if (chdir(hg_root) == -1) {
367 perror(hg_root);
367 perror(hg_root);
368 exit(EX_SOFTWARE);
368 exit(EX_SOFTWARE);
369 }
369 }
370 }
370 }
371
371
372 i = 0;
372 i = 0;
373
373
374 switch (cmd) {
374 switch (cmd) {
375 case hg_serve:
375 case hg_serve:
376 nargv[i++] = HG;
376 nargv[i++] = HG;
377 nargv[i++] = "-R";
377 nargv[i++] = "-R";
378 nargv[i++] = repo;
378 nargv[i++] = repo;
379 nargv[i++] = "serve";
379 nargv[i++] = "serve";
380 nargv[i++] = "--stdio";
380 nargv[i++] = "--stdio";
381 break;
381 break;
382 case hg_init:
382 case hg_init:
383 nargv[i++] = HG;
383 nargv[i++] = HG;
384 nargv[i++] = "init";
384 nargv[i++] = "init";
385 nargv[i++] = repo;
385 nargv[i++] = repo;
386 break;
386 break;
387 }
387 }
388
388
389 nargv[i] = NULL;
389 nargv[i] = NULL;
390
390
391 if (debug) {
391 if (debug) {
392 print_cmdline(i, nargv);
392 print_cmdline(i, nargv);
393 }
393 }
394
394
395 execv(HG, nargv);
395 execv(HG, nargv);
396 perror(HG);
396 perror(HG);
397 exit(EX_UNAVAILABLE);
397 exit(EX_UNAVAILABLE);
398
398
399 badargs:
399 badargs:
400 /* print useless error message. */
400 /* print useless error message. */
401
401
402 usage("invalid arguments", EX_DATAERR);
402 usage("invalid arguments", EX_DATAERR);
403 }
403 }
404
404
405 int main(int argc, char **argv)
405 int main(int argc, char **argv)
406 {
406 {
407 char host[1024];
407 char host[1024];
408 char *c;
408 char *c;
409
409
410 if (gethostname(host, sizeof(host)) == -1) {
410 if (gethostname(host, sizeof(host)) == -1) {
411 perror("gethostname");
411 perror("gethostname");
412 exit(EX_OSERR);
412 exit(EX_OSERR);
413 }
413 }
414
414
415 if ((c = strchr(host, '.')) != NULL) {
415 if ((c = strchr(host, '.')) != NULL) {
416 *c = '\0';
416 *c = '\0';
417 }
417 }
418
418
419 if (getenv("SSH_CLIENT")) {
419 if (getenv("SSH_CLIENT")) {
420 char *hg_gateway = HG_GATEWAY;
420 char *hg_gateway = HG_GATEWAY;
421 char *hg_host = HG_HOST;
421 char *hg_host = HG_HOST;
422
422
423 if (hg_gateway && strcmp(host, hg_gateway) == 0) {
423 if (hg_gateway && strcmp(host, hg_gateway) == 0) {
424 forward_through_gateway(argc, argv);
424 forward_through_gateway(argc, argv);
425 }
425 }
426
426
427 if (hg_host && strcmp(host, hg_host) != 0) {
427 if (hg_host && strcmp(host, hg_host) != 0) {
428 usage("invoked on unexpected host", EX_USAGE);
428 usage("invoked on unexpected host", EX_USAGE);
429 }
429 }
430
430
431 serve_data(argc, argv);
431 serve_data(argc, argv);
432 } else if (HG_SHELL) {
432 } else if (HG_SHELL) {
433 run_shell(argc, argv);
433 run_shell(argc, argv);
434 } else {
434 } else {
435 usage("invalid arguments", EX_DATAERR);
435 usage("invalid arguments", EX_DATAERR);
436 }
436 }
437
437
438 return 0;
438 return 0;
439 }
439 }
@@ -1,1274 +1,1274 b''
1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
2
2
3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
4
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
6
7 ;; mercurial.el is free software; you can redistribute it and/or
7 ;; mercurial.el is free software; you can redistribute it and/or
8 ;; modify it under the terms of version 2 of the GNU General Public
8 ;; modify it under the terms of version 2 of the GNU General Public
9 ;; License as published by the Free Software Foundation.
9 ;; License as published by the Free Software Foundation.
10
10
11 ;; mercurial.el is distributed in the hope that it will be useful, but
11 ;; mercurial.el is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; General Public License for more details.
14 ;; General Public License for more details.
15
15
16 ;; You should have received a copy of the GNU General Public License
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
20
21 ;;; Commentary:
21 ;;; Commentary:
22
22
23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
24 ;; integration with the Mercurial distributed SCM tool.
24 ;; integration with the Mercurial distributed SCM tool.
25
25
26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
28 ;; usage overview.
28 ;; usage overview.
29
29
30 ;; Much of the inspiration for mercurial.el comes from Rajesh
30 ;; Much of the inspiration for mercurial.el comes from Rajesh
31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
32 ;; job for the commercial Perforce SCM product. In fact, substantial
32 ;; job for the commercial Perforce SCM product. In fact, substantial
33 ;; chunks of code are adapted from p4.el.
33 ;; chunks of code are adapted from p4.el.
34
34
35 ;; This code has been developed under XEmacs 21.5, and may not work as
35 ;; This code has been developed under XEmacs 21.5, and may not work as
36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
37 ;; enhance the portability of this code, fix bugs, and add features
37 ;; enhance the portability of this code, fix bugs, and add features
38 ;; are most welcome. You can clone a Mercurial repository for this
38 ;; are most welcome. You can clone a Mercurial repository for this
39 ;; package from http://www.serpentine.com/hg/hg-emacs
39 ;; package from http://www.serpentine.com/hg/hg-emacs
40
40
41 ;; Please send problem reports and suggestions to bos@serpentine.com.
41 ;; Please send problem reports and suggestions to bos@serpentine.com.
42
42
43
43
44 ;;; Code:
44 ;;; Code:
45
45
46 (eval-when-compile (require 'cl))
46 (eval-when-compile (require 'cl))
47 (require 'diff-mode)
47 (require 'diff-mode)
48 (require 'easymenu)
48 (require 'easymenu)
49 (require 'executable)
49 (require 'executable)
50 (require 'vc)
50 (require 'vc)
51
51
52 (defmacro hg-feature-cond (&rest clauses)
52 (defmacro hg-feature-cond (&rest clauses)
53 "Test CLAUSES for feature at compile time.
53 "Test CLAUSES for feature at compile time.
54 Each clause is (FEATURE BODY...)."
54 Each clause is (FEATURE BODY...)."
55 (dolist (x clauses)
55 (dolist (x clauses)
56 (let ((feature (car x))
56 (let ((feature (car x))
57 (body (cdr x)))
57 (body (cdr x)))
58 (when (or (eq feature t)
58 (when (or (eq feature t)
59 (featurep feature))
59 (featurep feature))
60 (return (cons 'progn body))))))
60 (return (cons 'progn body))))))
61
61
62
62
63 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
63 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
64
64
65 (hg-feature-cond
65 (hg-feature-cond
66 (xemacs (require 'view-less))
66 (xemacs (require 'view-less))
67 (t (require 'view)))
67 (t (require 'view)))
68
68
69
69
70 ;;; Variables accessible through the custom system.
70 ;;; Variables accessible through the custom system.
71
71
72 (defgroup mercurial nil
72 (defgroup mercurial nil
73 "Mercurial distributed SCM."
73 "Mercurial distributed SCM."
74 :group 'tools)
74 :group 'tools)
75
75
76 (defcustom hg-binary
76 (defcustom hg-binary
77 (or (executable-find "hg")
77 (or (executable-find "hg")
78 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
78 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
79 (when (file-executable-p path)
79 (when (file-executable-p path)
80 (return path))))
80 (return path))))
81 "The path to Mercurial's hg executable."
81 "The path to Mercurial's hg executable."
82 :type '(file :must-match t)
82 :type '(file :must-match t)
83 :group 'mercurial)
83 :group 'mercurial)
84
84
85 (defcustom hg-mode-hook nil
85 (defcustom hg-mode-hook nil
86 "Hook run when a buffer enters hg-mode."
86 "Hook run when a buffer enters hg-mode."
87 :type 'sexp
87 :type 'sexp
88 :group 'mercurial)
88 :group 'mercurial)
89
89
90 (defcustom hg-commit-mode-hook nil
90 (defcustom hg-commit-mode-hook nil
91 "Hook run when a buffer is created to prepare a commit."
91 "Hook run when a buffer is created to prepare a commit."
92 :type 'sexp
92 :type 'sexp
93 :group 'mercurial)
93 :group 'mercurial)
94
94
95 (defcustom hg-pre-commit-hook nil
95 (defcustom hg-pre-commit-hook nil
96 "Hook run before a commit is performed.
96 "Hook run before a commit is performed.
97 If you want to prevent the commit from proceeding, raise an error."
97 If you want to prevent the commit from proceeding, raise an error."
98 :type 'sexp
98 :type 'sexp
99 :group 'mercurial)
99 :group 'mercurial)
100
100
101 (defcustom hg-log-mode-hook nil
101 (defcustom hg-log-mode-hook nil
102 "Hook run after a buffer is filled with log information."
102 "Hook run after a buffer is filled with log information."
103 :type 'sexp
103 :type 'sexp
104 :group 'mercurial)
104 :group 'mercurial)
105
105
106 (defcustom hg-global-prefix "\C-ch"
106 (defcustom hg-global-prefix "\C-ch"
107 "The global prefix for Mercurial keymap bindings."
107 "The global prefix for Mercurial keymap bindings."
108 :type 'sexp
108 :type 'sexp
109 :group 'mercurial)
109 :group 'mercurial)
110
110
111 (defcustom hg-commit-allow-empty-message nil
111 (defcustom hg-commit-allow-empty-message nil
112 "Whether to allow changes to be committed with empty descriptions."
112 "Whether to allow changes to be committed with empty descriptions."
113 :type 'boolean
113 :type 'boolean
114 :group 'mercurial)
114 :group 'mercurial)
115
115
116 (defcustom hg-commit-allow-empty-file-list nil
116 (defcustom hg-commit-allow-empty-file-list nil
117 "Whether to allow changes to be committed without any modified files."
117 "Whether to allow changes to be committed without any modified files."
118 :type 'boolean
118 :type 'boolean
119 :group 'mercurial)
119 :group 'mercurial)
120
120
121 (defcustom hg-rev-completion-limit 100
121 (defcustom hg-rev-completion-limit 100
122 "The maximum number of revisions that hg-read-rev will offer to complete.
122 "The maximum number of revisions that hg-read-rev will offer to complete.
123 This affects memory usage and performance when prompting for revisions
123 This affects memory usage and performance when prompting for revisions
124 in a repository with a lot of history."
124 in a repository with a lot of history."
125 :type 'integer
125 :type 'integer
126 :group 'mercurial)
126 :group 'mercurial)
127
127
128 (defcustom hg-log-limit 50
128 (defcustom hg-log-limit 50
129 "The maximum number of revisions that hg-log will display."
129 "The maximum number of revisions that hg-log will display."
130 :type 'integer
130 :type 'integer
131 :group 'mercurial)
131 :group 'mercurial)
132
132
133 (defcustom hg-update-modeline t
133 (defcustom hg-update-modeline t
134 "Whether to update the modeline with the status of a file after every save.
134 "Whether to update the modeline with the status of a file after every save.
135 Set this to nil on platforms with poor process management, such as Windows."
135 Set this to nil on platforms with poor process management, such as Windows."
136 :type 'boolean
136 :type 'boolean
137 :group 'mercurial)
137 :group 'mercurial)
138
138
139 (defcustom hg-incoming-repository "default"
139 (defcustom hg-incoming-repository "default"
140 "The repository from which changes are pulled from by default.
140 "The repository from which changes are pulled from by default.
141 This should be a symbolic repository name, since it is used for all
141 This should be a symbolic repository name, since it is used for all
142 repository-related commands."
142 repository-related commands."
143 :type 'string
143 :type 'string
144 :group 'mercurial)
144 :group 'mercurial)
145
145
146 (defcustom hg-outgoing-repository "default-push"
146 (defcustom hg-outgoing-repository "default-push"
147 "The repository to which changes are pushed to by default.
147 "The repository to which changes are pushed to by default.
148 This should be a symbolic repository name, since it is used for all
148 This should be a symbolic repository name, since it is used for all
149 repository-related commands."
149 repository-related commands."
150 :type 'string
150 :type 'string
151 :group 'mercurial)
151 :group 'mercurial)
152
152
153
153
154 ;;; Other variables.
154 ;;; Other variables.
155
155
156 (defvar hg-mode nil
156 (defvar hg-mode nil
157 "Is this file managed by Mercurial?")
157 "Is this file managed by Mercurial?")
158 (make-variable-buffer-local 'hg-mode)
158 (make-variable-buffer-local 'hg-mode)
159 (put 'hg-mode 'permanent-local t)
159 (put 'hg-mode 'permanent-local t)
160
160
161 (defvar hg-status nil)
161 (defvar hg-status nil)
162 (make-variable-buffer-local 'hg-status)
162 (make-variable-buffer-local 'hg-status)
163 (put 'hg-status 'permanent-local t)
163 (put 'hg-status 'permanent-local t)
164
164
165 (defvar hg-prev-buffer nil)
165 (defvar hg-prev-buffer nil)
166 (make-variable-buffer-local 'hg-prev-buffer)
166 (make-variable-buffer-local 'hg-prev-buffer)
167 (put 'hg-prev-buffer 'permanent-local t)
167 (put 'hg-prev-buffer 'permanent-local t)
168
168
169 (defvar hg-root nil)
169 (defvar hg-root nil)
170 (make-variable-buffer-local 'hg-root)
170 (make-variable-buffer-local 'hg-root)
171 (put 'hg-root 'permanent-local t)
171 (put 'hg-root 'permanent-local t)
172
172
173 (defvar hg-view-mode nil)
173 (defvar hg-view-mode nil)
174 (make-variable-buffer-local 'hg-view-mode)
174 (make-variable-buffer-local 'hg-view-mode)
175 (put 'hg-view-mode 'permanent-local t)
175 (put 'hg-view-mode 'permanent-local t)
176
176
177 (defvar hg-view-file-name nil)
177 (defvar hg-view-file-name nil)
178 (make-variable-buffer-local 'hg-view-file-name)
178 (make-variable-buffer-local 'hg-view-file-name)
179 (put 'hg-view-file-name 'permanent-local t)
179 (put 'hg-view-file-name 'permanent-local t)
180
180
181 (defvar hg-output-buffer-name "*Hg*"
181 (defvar hg-output-buffer-name "*Hg*"
182 "The name to use for Mercurial output buffers.")
182 "The name to use for Mercurial output buffers.")
183
183
184 (defvar hg-file-history nil)
184 (defvar hg-file-history nil)
185 (defvar hg-repo-history nil)
185 (defvar hg-repo-history nil)
186 (defvar hg-rev-history nil)
186 (defvar hg-rev-history nil)
187 (defvar hg-repo-completion-table nil) ; shut up warnings
187 (defvar hg-repo-completion-table nil) ; shut up warnings
188
188
189
189
190 ;;; Random constants.
190 ;;; Random constants.
191
191
192 (defconst hg-commit-message-start
192 (defconst hg-commit-message-start
193 "--- Enter your commit message. Type `C-c C-c' to commit. ---\n")
193 "--- Enter your commit message. Type `C-c C-c' to commit. ---\n")
194
194
195 (defconst hg-commit-message-end
195 (defconst hg-commit-message-end
196 "--- Files in bold will be committed. Click to toggle selection. ---\n")
196 "--- Files in bold will be committed. Click to toggle selection. ---\n")
197
197
198 (defconst hg-state-alist
198 (defconst hg-state-alist
199 '((?M . modified)
199 '((?M . modified)
200 (?A . added)
200 (?A . added)
201 (?R . removed)
201 (?R . removed)
202 (?! . deleted)
202 (?! . deleted)
203 (?C . normal)
203 (?C . normal)
204 (?I . ignored)
204 (?I . ignored)
205 (?? . nil)))
205 (?? . nil)))
206
206
207 ;;; hg-mode keymap.
207 ;;; hg-mode keymap.
208
208
209 (defvar hg-prefix-map
209 (defvar hg-prefix-map
210 (let ((map (make-sparse-keymap)))
210 (let ((map (make-sparse-keymap)))
211 (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
211 (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
212 (set-keymap-parent map vc-prefix-map)
212 (set-keymap-parent map vc-prefix-map)
213 (define-key map "=" 'hg-diff)
213 (define-key map "=" 'hg-diff)
214 (define-key map "c" 'hg-undo)
214 (define-key map "c" 'hg-undo)
215 (define-key map "g" 'hg-annotate)
215 (define-key map "g" 'hg-annotate)
216 (define-key map "i" 'hg-add)
216 (define-key map "i" 'hg-add)
217 (define-key map "l" 'hg-log)
217 (define-key map "l" 'hg-log)
218 (define-key map "n" 'hg-commit-start)
218 (define-key map "n" 'hg-commit-start)
219 ;; (define-key map "r" 'hg-update)
219 ;; (define-key map "r" 'hg-update)
220 (define-key map "u" 'hg-revert-buffer)
220 (define-key map "u" 'hg-revert-buffer)
221 (define-key map "~" 'hg-version-other-window)
221 (define-key map "~" 'hg-version-other-window)
222 map)
222 map)
223 "This keymap overrides some default vc-mode bindings.")
223 "This keymap overrides some default vc-mode bindings.")
224
224
225 (defvar hg-mode-map
225 (defvar hg-mode-map
226 (let ((map (make-sparse-keymap)))
226 (let ((map (make-sparse-keymap)))
227 (define-key map "\C-xv" hg-prefix-map)
227 (define-key map "\C-xv" hg-prefix-map)
228 map))
228 map))
229
229
230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
231
231
232
232
233 ;;; Global keymap.
233 ;;; Global keymap.
234
234
235 (defvar hg-global-map
235 (defvar hg-global-map
236 (let ((map (make-sparse-keymap)))
236 (let ((map (make-sparse-keymap)))
237 (define-key map "," 'hg-incoming)
237 (define-key map "," 'hg-incoming)
238 (define-key map "." 'hg-outgoing)
238 (define-key map "." 'hg-outgoing)
239 (define-key map "<" 'hg-pull)
239 (define-key map "<" 'hg-pull)
240 (define-key map "=" 'hg-diff-repo)
240 (define-key map "=" 'hg-diff-repo)
241 (define-key map ">" 'hg-push)
241 (define-key map ">" 'hg-push)
242 (define-key map "?" 'hg-help-overview)
242 (define-key map "?" 'hg-help-overview)
243 (define-key map "A" 'hg-addremove)
243 (define-key map "A" 'hg-addremove)
244 (define-key map "U" 'hg-revert)
244 (define-key map "U" 'hg-revert)
245 (define-key map "a" 'hg-add)
245 (define-key map "a" 'hg-add)
246 (define-key map "c" 'hg-commit-start)
246 (define-key map "c" 'hg-commit-start)
247 (define-key map "f" 'hg-forget)
247 (define-key map "f" 'hg-forget)
248 (define-key map "h" 'hg-help-overview)
248 (define-key map "h" 'hg-help-overview)
249 (define-key map "i" 'hg-init)
249 (define-key map "i" 'hg-init)
250 (define-key map "l" 'hg-log-repo)
250 (define-key map "l" 'hg-log-repo)
251 (define-key map "r" 'hg-root)
251 (define-key map "r" 'hg-root)
252 (define-key map "s" 'hg-status)
252 (define-key map "s" 'hg-status)
253 (define-key map "u" 'hg-update)
253 (define-key map "u" 'hg-update)
254 map))
254 map))
255
255
256 (global-set-key hg-global-prefix hg-global-map)
256 (global-set-key hg-global-prefix hg-global-map)
257
257
258 ;;; View mode keymap.
258 ;;; View mode keymap.
259
259
260 (defvar hg-view-mode-map
260 (defvar hg-view-mode-map
261 (let ((map (make-sparse-keymap)))
261 (let ((map (make-sparse-keymap)))
262 (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
262 (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
263 (define-key map (hg-feature-cond (xemacs [button2])
263 (define-key map (hg-feature-cond (xemacs [button2])
264 (t [mouse-2]))
264 (t [mouse-2]))
265 'hg-buffer-mouse-clicked)
265 'hg-buffer-mouse-clicked)
266 map))
266 map))
267
267
268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
269
269
270
270
271 ;;; Commit mode keymaps.
271 ;;; Commit mode keymaps.
272
272
273 (defvar hg-commit-mode-map
273 (defvar hg-commit-mode-map
274 (let ((map (make-sparse-keymap)))
274 (let ((map (make-sparse-keymap)))
275 (define-key map "\C-c\C-c" 'hg-commit-finish)
275 (define-key map "\C-c\C-c" 'hg-commit-finish)
276 (define-key map "\C-c\C-k" 'hg-commit-kill)
276 (define-key map "\C-c\C-k" 'hg-commit-kill)
277 (define-key map "\C-xv=" 'hg-diff-repo)
277 (define-key map "\C-xv=" 'hg-diff-repo)
278 map))
278 map))
279
279
280 (defvar hg-commit-mode-file-map
280 (defvar hg-commit-mode-file-map
281 (let ((map (make-sparse-keymap)))
281 (let ((map (make-sparse-keymap)))
282 (define-key map (hg-feature-cond (xemacs [button2])
282 (define-key map (hg-feature-cond (xemacs [button2])
283 (t [mouse-2]))
283 (t [mouse-2]))
284 'hg-commit-mouse-clicked)
284 'hg-commit-mouse-clicked)
285 (define-key map " " 'hg-commit-toggle-file)
285 (define-key map " " 'hg-commit-toggle-file)
286 (define-key map "\r" 'hg-commit-toggle-file)
286 (define-key map "\r" 'hg-commit-toggle-file)
287 map))
287 map))
288
288
289
289
290 ;;; Convenience functions.
290 ;;; Convenience functions.
291
291
292 (defsubst hg-binary ()
292 (defsubst hg-binary ()
293 (if hg-binary
293 (if hg-binary
294 hg-binary
294 hg-binary
295 (error "No `hg' executable found!")))
295 (error "No `hg' executable found!")))
296
296
297 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
297 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
298 "Replace all matches in STR for REGEXP with NEWTEXT string.
298 "Replace all matches in STR for REGEXP with NEWTEXT string.
299 Return the new string. Optional LITERAL non-nil means do a literal
299 Return the new string. Optional LITERAL non-nil means do a literal
300 replacement.
300 replacement.
301
301
302 This function bridges yet another pointless impedance gap between
302 This function bridges yet another pointless impedance gap between
303 XEmacs and GNU Emacs."
303 XEmacs and GNU Emacs."
304 (hg-feature-cond
304 (hg-feature-cond
305 (xemacs (replace-in-string str regexp newtext literal))
305 (xemacs (replace-in-string str regexp newtext literal))
306 (t (replace-regexp-in-string regexp newtext str nil literal))))
306 (t (replace-regexp-in-string regexp newtext str nil literal))))
307
307
308 (defsubst hg-strip (str)
308 (defsubst hg-strip (str)
309 "Strip leading and trailing blank lines from a string."
309 "Strip leading and trailing blank lines from a string."
310 (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
310 (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
311 "\\`[ \t\r\n]*[\r\n]" ""))
311 "\\`[ \t\r\n]*[\r\n]" ""))
312
312
313 (defsubst hg-chomp (str)
313 (defsubst hg-chomp (str)
314 "Strip trailing newlines from a string."
314 "Strip trailing newlines from a string."
315 (hg-replace-in-string str "[\r\n]+\\'" ""))
315 (hg-replace-in-string str "[\r\n]+\\'" ""))
316
316
317 (defun hg-run-command (command &rest args)
317 (defun hg-run-command (command &rest args)
318 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
318 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
319 The list ARGS contains a list of arguments to pass to the command."
319 The list ARGS contains a list of arguments to pass to the command."
320 (let* (exit-code
320 (let* (exit-code
321 (output
321 (output
322 (with-output-to-string
322 (with-output-to-string
323 (with-current-buffer
323 (with-current-buffer
324 standard-output
324 standard-output
325 (setq exit-code
325 (setq exit-code
326 (apply 'call-process command nil t nil args))))))
326 (apply 'call-process command nil t nil args))))))
327 (cons exit-code output)))
327 (cons exit-code output)))
328
328
329 (defun hg-run (command &rest args)
329 (defun hg-run (command &rest args)
330 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
330 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
331 (apply 'hg-run-command (hg-binary) command args))
331 (apply 'hg-run-command (hg-binary) command args))
332
332
333 (defun hg-run0 (command &rest args)
333 (defun hg-run0 (command &rest args)
334 "Run the Mercurial command COMMAND, returning its output.
334 "Run the Mercurial command COMMAND, returning its output.
335 If the command does not exit with a zero status code, raise an error."
335 If the command does not exit with a zero status code, raise an error."
336 (let ((res (apply 'hg-run-command (hg-binary) command args)))
336 (let ((res (apply 'hg-run-command (hg-binary) command args)))
337 (if (not (eq (car res) 0))
337 (if (not (eq (car res) 0))
338 (error "Mercurial command failed %s - exit code %s"
338 (error "Mercurial command failed %s - exit code %s"
339 (cons command args)
339 (cons command args)
340 (car res))
340 (car res))
341 (cdr res))))
341 (cdr res))))
342
342
343 (defmacro hg-do-across-repo (path &rest body)
343 (defmacro hg-do-across-repo (path &rest body)
344 (let ((root-name (make-symbol "root-"))
344 (let ((root-name (make-symbol "root-"))
345 (buf-name (make-symbol "buf-")))
345 (buf-name (make-symbol "buf-")))
346 `(let ((,root-name (hg-root ,path)))
346 `(let ((,root-name (hg-root ,path)))
347 (save-excursion
347 (save-excursion
348 (dolist (,buf-name (buffer-list))
348 (dolist (,buf-name (buffer-list))
349 (set-buffer ,buf-name)
349 (set-buffer ,buf-name)
350 (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
350 (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
351 ,@body))))))
351 ,@body))))))
352
352
353 (put 'hg-do-across-repo 'lisp-indent-function 1)
353 (put 'hg-do-across-repo 'lisp-indent-function 1)
354
354
355 (defun hg-sync-buffers (path)
355 (defun hg-sync-buffers (path)
356 "Sync buffers visiting PATH with their on-disk copies.
356 "Sync buffers visiting PATH with their on-disk copies.
357 If PATH is not being visited, but is under the repository root, sync
357 If PATH is not being visited, but is under the repository root, sync
358 all buffers visiting files in the repository."
358 all buffers visiting files in the repository."
359 (let ((buf (find-buffer-visiting path)))
359 (let ((buf (find-buffer-visiting path)))
360 (if buf
360 (if buf
361 (with-current-buffer buf
361 (with-current-buffer buf
362 (vc-buffer-sync))
362 (vc-buffer-sync))
363 (hg-do-across-repo path
363 (hg-do-across-repo path
364 (vc-buffer-sync)))))
364 (vc-buffer-sync)))))
365
365
366 (defun hg-buffer-commands (pnt)
366 (defun hg-buffer-commands (pnt)
367 "Use the properties of a character to do something sensible."
367 "Use the properties of a character to do something sensible."
368 (interactive "d")
368 (interactive "d")
369 (let ((rev (get-char-property pnt 'rev))
369 (let ((rev (get-char-property pnt 'rev))
370 (file (get-char-property pnt 'file)))
370 (file (get-char-property pnt 'file)))
371 (cond
371 (cond
372 (file
372 (file
373 (find-file-other-window file))
373 (find-file-other-window file))
374 (rev
374 (rev
375 (hg-diff hg-view-file-name rev rev))
375 (hg-diff hg-view-file-name rev rev))
376 ((message "I don't know how to do that yet")))))
376 ((message "I don't know how to do that yet")))))
377
377
378 (defsubst hg-event-point (event)
378 (defsubst hg-event-point (event)
379 "Return the character position of the mouse event EVENT."
379 "Return the character position of the mouse event EVENT."
380 (hg-feature-cond (xemacs (event-point event))
380 (hg-feature-cond (xemacs (event-point event))
381 (t (posn-point (event-start event)))))
381 (t (posn-point (event-start event)))))
382
382
383 (defsubst hg-event-window (event)
383 (defsubst hg-event-window (event)
384 "Return the window over which mouse event EVENT occurred."
384 "Return the window over which mouse event EVENT occurred."
385 (hg-feature-cond (xemacs (event-window event))
385 (hg-feature-cond (xemacs (event-window event))
386 (t (posn-window (event-start event)))))
386 (t (posn-window (event-start event)))))
387
387
388 (defun hg-buffer-mouse-clicked (event)
388 (defun hg-buffer-mouse-clicked (event)
389 "Translate the mouse clicks in a HG log buffer to character events.
389 "Translate the mouse clicks in a HG log buffer to character events.
390 These are then handed off to `hg-buffer-commands'.
390 These are then handed off to `hg-buffer-commands'.
391
391
392 Handle frickin' frackin' gratuitous event-related incompatibilities."
392 Handle frickin' frackin' gratuitous event-related incompatibilities."
393 (interactive "e")
393 (interactive "e")
394 (select-window (hg-event-window event))
394 (select-window (hg-event-window event))
395 (hg-buffer-commands (hg-event-point event)))
395 (hg-buffer-commands (hg-event-point event)))
396
396
397 (defsubst hg-abbrev-file-name (file)
397 (defsubst hg-abbrev-file-name (file)
398 "Portable wrapper around abbreviate-file-name."
398 "Portable wrapper around abbreviate-file-name."
399 (hg-feature-cond (xemacs (abbreviate-file-name file t))
399 (hg-feature-cond (xemacs (abbreviate-file-name file t))
400 (t (abbreviate-file-name file))))
400 (t (abbreviate-file-name file))))
401
401
402 (defun hg-read-file-name (&optional prompt default)
402 (defun hg-read-file-name (&optional prompt default)
403 "Read a file or directory name, or a pattern, to use with a command."
403 "Read a file or directory name, or a pattern, to use with a command."
404 (save-excursion
404 (save-excursion
405 (while hg-prev-buffer
405 (while hg-prev-buffer
406 (set-buffer hg-prev-buffer))
406 (set-buffer hg-prev-buffer))
407 (let ((path (or default
407 (let ((path (or default
408 (buffer-file-name)
408 (buffer-file-name)
409 (expand-file-name default-directory))))
409 (expand-file-name default-directory))))
410 (if (or (not path) current-prefix-arg)
410 (if (or (not path) current-prefix-arg)
411 (expand-file-name
411 (expand-file-name
412 (eval (list* 'read-file-name
412 (eval (list* 'read-file-name
413 (format "File, directory or pattern%s: "
413 (format "File, directory or pattern%s: "
414 (or prompt ""))
414 (or prompt ""))
415 (and path (file-name-directory path))
415 (and path (file-name-directory path))
416 nil nil
416 nil nil
417 (and path (file-name-nondirectory path))
417 (and path (file-name-nondirectory path))
418 (hg-feature-cond
418 (hg-feature-cond
419 (xemacs (cons (quote 'hg-file-history) nil))
419 (xemacs (cons (quote 'hg-file-history) nil))
420 (t nil)))))
420 (t nil)))))
421 path))))
421 path))))
422
422
423 (defun hg-read-number (&optional prompt default)
423 (defun hg-read-number (&optional prompt default)
424 "Read a integer value."
424 "Read a integer value."
425 (save-excursion
425 (save-excursion
426 (if (or (not default) current-prefix-arg)
426 (if (or (not default) current-prefix-arg)
427 (string-to-number
427 (string-to-number
428 (eval (list* 'read-string
428 (eval (list* 'read-string
429 (or prompt "")
429 (or prompt "")
430 (if default (cons (format "%d" default) nil) nil))))
430 (if default (cons (format "%d" default) nil) nil))))
431 default)))
431 default)))
432
432
433 (defun hg-read-config ()
433 (defun hg-read-config ()
434 "Return an alist of (key . value) pairs of Mercurial config data.
434 "Return an alist of (key . value) pairs of Mercurial config data.
435 Each key is of the form (section . name)."
435 Each key is of the form (section . name)."
436 (let (items)
436 (let (items)
437 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
437 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
438 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
438 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
439 (let* ((left (substring line (match-beginning 1) (match-end 1)))
439 (let* ((left (substring line (match-beginning 1) (match-end 1)))
440 (right (substring line (match-beginning 2) (match-end 2)))
440 (right (substring line (match-beginning 2) (match-end 2)))
441 (key (split-string left "\\."))
441 (key (split-string left "\\."))
442 (value (hg-replace-in-string right "\\\\n" "\n" t)))
442 (value (hg-replace-in-string right "\\\\n" "\n" t)))
443 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
443 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
444
444
445 (defun hg-config-section (section config)
445 (defun hg-config-section (section config)
446 "Return an alist of (name . value) pairs for SECTION of CONFIG."
446 "Return an alist of (name . value) pairs for SECTION of CONFIG."
447 (let (items)
447 (let (items)
448 (dolist (item config items)
448 (dolist (item config items)
449 (when (equal (caar item) section)
449 (when (equal (caar item) section)
450 (setq items (cons (cons (cdar item) (cdr item)) items))))))
450 (setq items (cons (cons (cdar item) (cdr item)) items))))))
451
451
452 (defun hg-string-starts-with (sub str)
452 (defun hg-string-starts-with (sub str)
453 "Indicate whether string STR starts with the substring or character SUB."
453 "Indicate whether string STR starts with the substring or character SUB."
454 (if (not (stringp sub))
454 (if (not (stringp sub))
455 (and (> (length str) 0) (equal (elt str 0) sub))
455 (and (> (length str) 0) (equal (elt str 0) sub))
456 (let ((sub-len (length sub)))
456 (let ((sub-len (length sub)))
457 (and (<= sub-len (length str))
457 (and (<= sub-len (length str))
458 (string= sub (substring str 0 sub-len))))))
458 (string= sub (substring str 0 sub-len))))))
459
459
460 (defun hg-complete-repo (string predicate all)
460 (defun hg-complete-repo (string predicate all)
461 "Attempt to complete a repository name.
461 "Attempt to complete a repository name.
462 We complete on either symbolic names from Mercurial's config or real
462 We complete on either symbolic names from Mercurial's config or real
463 directory names from the file system. We do not penalise URLs."
463 directory names from the file system. We do not penalise URLs."
464 (or (if all
464 (or (if all
465 (all-completions string hg-repo-completion-table predicate)
465 (all-completions string hg-repo-completion-table predicate)
466 (try-completion string hg-repo-completion-table predicate))
466 (try-completion string hg-repo-completion-table predicate))
467 (let* ((str (expand-file-name string))
467 (let* ((str (expand-file-name string))
468 (dir (file-name-directory str))
468 (dir (file-name-directory str))
469 (file (file-name-nondirectory str)))
469 (file (file-name-nondirectory str)))
470 (if all
470 (if all
471 (let (completions)
471 (let (completions)
472 (dolist (name (delete "./" (file-name-all-completions file dir))
472 (dolist (name (delete "./" (file-name-all-completions file dir))
473 completions)
473 completions)
474 (let ((path (concat dir name)))
474 (let ((path (concat dir name)))
475 (when (file-directory-p path)
475 (when (file-directory-p path)
476 (setq completions (cons name completions))))))
476 (setq completions (cons name completions))))))
477 (let ((comp (file-name-completion file dir)))
477 (let ((comp (file-name-completion file dir)))
478 (if comp
478 (if comp
479 (hg-abbrev-file-name (concat dir comp))))))))
479 (hg-abbrev-file-name (concat dir comp))))))))
480
480
481 (defun hg-read-repo-name (&optional prompt initial-contents default)
481 (defun hg-read-repo-name (&optional prompt initial-contents default)
482 "Read the location of a repository."
482 "Read the location of a repository."
483 (save-excursion
483 (save-excursion
484 (while hg-prev-buffer
484 (while hg-prev-buffer
485 (set-buffer hg-prev-buffer))
485 (set-buffer hg-prev-buffer))
486 (let (hg-repo-completion-table)
486 (let (hg-repo-completion-table)
487 (if current-prefix-arg
487 (if current-prefix-arg
488 (progn
488 (progn
489 (dolist (path (hg-config-section "paths" (hg-read-config)))
489 (dolist (path (hg-config-section "paths" (hg-read-config)))
490 (setq hg-repo-completion-table
490 (setq hg-repo-completion-table
491 (cons (cons (car path) t) hg-repo-completion-table))
491 (cons (cons (car path) t) hg-repo-completion-table))
492 (unless (hg-string-starts-with (hg-feature-cond
492 (unless (hg-string-starts-with (hg-feature-cond
493 (xemacs directory-sep-char)
493 (xemacs directory-sep-char)
494 (t ?/))
494 (t ?/))
495 (cdr path))
495 (cdr path))
496 (setq hg-repo-completion-table
496 (setq hg-repo-completion-table
497 (cons (cons (cdr path) t) hg-repo-completion-table))))
497 (cons (cons (cdr path) t) hg-repo-completion-table))))
498 (completing-read (format "Repository%s: " (or prompt ""))
498 (completing-read (format "Repository%s: " (or prompt ""))
499 'hg-complete-repo
499 'hg-complete-repo
500 nil
500 nil
501 nil
501 nil
502 initial-contents
502 initial-contents
503 'hg-repo-history
503 'hg-repo-history
504 default))
504 default))
505 default))))
505 default))))
506
506
507 (defun hg-read-rev (&optional prompt default)
507 (defun hg-read-rev (&optional prompt default)
508 "Read a revision or tag, offering completions."
508 "Read a revision or tag, offering completions."
509 (save-excursion
509 (save-excursion
510 (while hg-prev-buffer
510 (while hg-prev-buffer
511 (set-buffer hg-prev-buffer))
511 (set-buffer hg-prev-buffer))
512 (let ((rev (or default "tip")))
512 (let ((rev (or default "tip")))
513 (if current-prefix-arg
513 (if current-prefix-arg
514 (let ((revs (split-string
514 (let ((revs (split-string
515 (hg-chomp
515 (hg-chomp
516 (hg-run0 "-q" "log" "-l"
516 (hg-run0 "-q" "log" "-l"
517 (format "%d" hg-rev-completion-limit)))
517 (format "%d" hg-rev-completion-limit)))
518 "[\n:]")))
518 "[\n:]")))
519 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
519 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
520 (setq revs (cons (car (split-string line "\\s-")) revs)))
520 (setq revs (cons (car (split-string line "\\s-")) revs)))
521 (completing-read (format "Revision%s (%s): "
521 (completing-read (format "Revision%s (%s): "
522 (or prompt "")
522 (or prompt "")
523 (or default "tip"))
523 (or default "tip"))
524 (map 'list 'cons revs revs)
524 (map 'list 'cons revs revs)
525 nil
525 nil
526 nil
526 nil
527 nil
527 nil
528 'hg-rev-history
528 'hg-rev-history
529 (or default "tip")))
529 (or default "tip")))
530 rev))))
530 rev))))
531
531
532 (defun hg-parents-for-mode-line (root)
532 (defun hg-parents-for-mode-line (root)
533 "Format the parents of the working directory for the mode line."
533 "Format the parents of the working directory for the mode line."
534 (let ((parents (split-string (hg-chomp
534 (let ((parents (split-string (hg-chomp
535 (hg-run0 "--cwd" root "parents" "--template"
535 (hg-run0 "--cwd" root "parents" "--template"
536 "{rev}\n")) "\n")))
536 "{rev}\n")) "\n")))
537 (mapconcat 'identity parents "+")))
537 (mapconcat 'identity parents "+")))
538
538
539 (defun hg-buffers-visiting-repo (&optional path)
539 (defun hg-buffers-visiting-repo (&optional path)
540 "Return a list of buffers visiting the repository containing PATH."
540 "Return a list of buffers visiting the repository containing PATH."
541 (let ((root-name (hg-root (or path (buffer-file-name))))
541 (let ((root-name (hg-root (or path (buffer-file-name))))
542 bufs)
542 bufs)
543 (save-excursion
543 (save-excursion
544 (dolist (buf (buffer-list) bufs)
544 (dolist (buf (buffer-list) bufs)
545 (set-buffer buf)
545 (set-buffer buf)
546 (let ((name (buffer-file-name)))
546 (let ((name (buffer-file-name)))
547 (when (and hg-status name (equal (hg-root name) root-name))
547 (when (and hg-status name (equal (hg-root name) root-name))
548 (setq bufs (cons buf bufs))))))))
548 (setq bufs (cons buf bufs))))))))
549
549
550 (defun hg-update-mode-lines (path)
550 (defun hg-update-mode-lines (path)
551 "Update the mode lines of all buffers visiting the same repository as PATH."
551 "Update the mode lines of all buffers visiting the same repository as PATH."
552 (let* ((root (hg-root path))
552 (let* ((root (hg-root path))
553 (parents (hg-parents-for-mode-line root)))
553 (parents (hg-parents-for-mode-line root)))
554 (save-excursion
554 (save-excursion
555 (dolist (info (hg-path-status
555 (dolist (info (hg-path-status
556 root
556 root
557 (mapcar
557 (mapcar
558 (function
558 (function
559 (lambda (buf)
559 (lambda (buf)
560 (substring (buffer-file-name buf) (length root))))
560 (substring (buffer-file-name buf) (length root))))
561 (hg-buffers-visiting-repo root))))
561 (hg-buffers-visiting-repo root))))
562 (let* ((name (car info))
562 (let* ((name (car info))
563 (status (cdr info))
563 (status (cdr info))
564 (buf (find-buffer-visiting (concat root name))))
564 (buf (find-buffer-visiting (concat root name))))
565 (when buf
565 (when buf
566 (set-buffer buf)
566 (set-buffer buf)
567 (hg-mode-line-internal status parents)))))))
567 (hg-mode-line-internal status parents)))))))
568
568
569
569
570 ;;; View mode bits.
570 ;;; View mode bits.
571
571
572 (defun hg-exit-view-mode (buf)
572 (defun hg-exit-view-mode (buf)
573 "Exit from hg-view-mode.
573 "Exit from hg-view-mode.
574 We delete the current window if entering hg-view-mode split the
574 We delete the current window if entering hg-view-mode split the
575 current frame."
575 current frame."
576 (when (and (eq buf (current-buffer))
576 (when (and (eq buf (current-buffer))
577 (> (length (window-list)) 1))
577 (> (length (window-list)) 1))
578 (delete-window))
578 (delete-window))
579 (when (buffer-live-p buf)
579 (when (buffer-live-p buf)
580 (kill-buffer buf)))
580 (kill-buffer buf)))
581
581
582 (defun hg-view-mode (prev-buffer &optional file-name)
582 (defun hg-view-mode (prev-buffer &optional file-name)
583 (goto-char (point-min))
583 (goto-char (point-min))
584 (set-buffer-modified-p nil)
584 (set-buffer-modified-p nil)
585 (toggle-read-only t)
585 (toggle-read-only t)
586 (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
586 (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
587 (t (view-mode-enter nil 'hg-exit-view-mode)))
587 (t (view-mode-enter nil 'hg-exit-view-mode)))
588 (setq hg-view-mode t)
588 (setq hg-view-mode t)
589 (setq truncate-lines t)
589 (setq truncate-lines t)
590 (when file-name
590 (when file-name
591 (setq hg-view-file-name
591 (setq hg-view-file-name
592 (hg-abbrev-file-name file-name))))
592 (hg-abbrev-file-name file-name))))
593
593
594 (defun hg-file-status (file)
594 (defun hg-file-status (file)
595 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
595 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
596 (let* ((s (hg-run "status" file))
596 (let* ((s (hg-run "status" file))
597 (exit (car s))
597 (exit (car s))
598 (output (cdr s)))
598 (output (cdr s)))
599 (if (= exit 0)
599 (if (= exit 0)
600 (let ((state (and (>= (length output) 2)
600 (let ((state (and (>= (length output) 2)
601 (= (aref output 1) ? )
601 (= (aref output 1) ? )
602 (assq (aref output 0) hg-state-alist))))
602 (assq (aref output 0) hg-state-alist))))
603 (if state
603 (if state
604 (cdr state)
604 (cdr state)
605 'normal)))))
605 'normal)))))
606
606
607 (defun hg-path-status (root paths)
607 (defun hg-path-status (root paths)
608 "Return status of PATHS in repo ROOT as an alist.
608 "Return status of PATHS in repo ROOT as an alist.
609 Each entry is a pair (FILE-NAME . STATUS)."
609 Each entry is a pair (FILE-NAME . STATUS)."
610 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
610 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
611 result)
611 result)
612 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
612 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
613 (let (state name)
613 (let (state name)
614 (cond ((= (aref entry 1) ? )
614 (cond ((= (aref entry 1) ? )
615 (setq state (assq (aref entry 0) hg-state-alist)
615 (setq state (assq (aref entry 0) hg-state-alist)
616 name (substring entry 2)))
616 name (substring entry 2)))
617 ((string-match "\\(.*\\): " entry)
617 ((string-match "\\(.*\\): " entry)
618 (setq name (match-string 1 entry))))
618 (setq name (match-string 1 entry))))
619 (setq result (cons (cons name state) result))))))
619 (setq result (cons (cons name state) result))))))
620
620
621 (defmacro hg-view-output (args &rest body)
621 (defmacro hg-view-output (args &rest body)
622 "Execute BODY in a clean buffer, then quickly display that buffer.
622 "Execute BODY in a clean buffer, then quickly display that buffer.
623 If the buffer contains one line, its contents are displayed in the
623 If the buffer contains one line, its contents are displayed in the
624 minibuffer. Otherwise, the buffer is displayed in view-mode.
624 minibuffer. Otherwise, the buffer is displayed in view-mode.
625 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
625 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
626 the name of the buffer to create, and FILE is the name of the file
626 the name of the buffer to create, and FILE is the name of the file
627 being viewed."
627 being viewed."
628 (let ((prev-buf (make-symbol "prev-buf-"))
628 (let ((prev-buf (make-symbol "prev-buf-"))
629 (v-b-name (car args))
629 (v-b-name (car args))
630 (v-m-rest (cdr args)))
630 (v-m-rest (cdr args)))
631 `(let ((view-buf-name ,v-b-name)
631 `(let ((view-buf-name ,v-b-name)
632 (,prev-buf (current-buffer)))
632 (,prev-buf (current-buffer)))
633 (get-buffer-create view-buf-name)
633 (get-buffer-create view-buf-name)
634 (kill-buffer view-buf-name)
634 (kill-buffer view-buf-name)
635 (get-buffer-create view-buf-name)
635 (get-buffer-create view-buf-name)
636 (set-buffer view-buf-name)
636 (set-buffer view-buf-name)
637 (save-excursion
637 (save-excursion
638 ,@body)
638 ,@body)
639 (case (count-lines (point-min) (point-max))
639 (case (count-lines (point-min) (point-max))
640 ((0)
640 ((0)
641 (kill-buffer view-buf-name)
641 (kill-buffer view-buf-name)
642 (message "(No output)"))
642 (message "(No output)"))
643 ((1)
643 ((1)
644 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
644 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
645 (kill-buffer view-buf-name)
645 (kill-buffer view-buf-name)
646 (message "%s" msg)))
646 (message "%s" msg)))
647 (t
647 (t
648 (pop-to-buffer view-buf-name)
648 (pop-to-buffer view-buf-name)
649 (setq hg-prev-buffer ,prev-buf)
649 (setq hg-prev-buffer ,prev-buf)
650 (hg-view-mode ,prev-buf ,@v-m-rest))))))
650 (hg-view-mode ,prev-buf ,@v-m-rest))))))
651
651
652 (put 'hg-view-output 'lisp-indent-function 1)
652 (put 'hg-view-output 'lisp-indent-function 1)
653
653
654 ;;; Context save and restore across revert and other operations.
654 ;;; Context save and restore across revert and other operations.
655
655
656 (defun hg-position-context (pos)
656 (defun hg-position-context (pos)
657 "Return information to help find the given position again."
657 "Return information to help find the given position again."
658 (let* ((end (min (point-max) (+ pos 98))))
658 (let* ((end (min (point-max) (+ pos 98))))
659 (list pos
659 (list pos
660 (buffer-substring (max (point-min) (- pos 2)) end)
660 (buffer-substring (max (point-min) (- pos 2)) end)
661 (- end pos))))
661 (- end pos))))
662
662
663 (defun hg-buffer-context ()
663 (defun hg-buffer-context ()
664 "Return information to help restore a user's editing context.
664 "Return information to help restore a user's editing context.
665 This is useful across reverts and merges, where a context is likely
665 This is useful across reverts and merges, where a context is likely
666 to have moved a little, but not really changed."
666 to have moved a little, but not really changed."
667 (let ((point-context (hg-position-context (point)))
667 (let ((point-context (hg-position-context (point)))
668 (mark-context (let ((mark (mark-marker)))
668 (mark-context (let ((mark (mark-marker)))
669 (and mark (hg-position-context mark)))))
669 (and mark (hg-position-context mark)))))
670 (list point-context mark-context)))
670 (list point-context mark-context)))
671
671
672 (defun hg-find-context (ctx)
672 (defun hg-find-context (ctx)
673 "Attempt to find a context in the given buffer.
673 "Attempt to find a context in the given buffer.
674 Always returns a valid, hopefully sane, position."
674 Always returns a valid, hopefully sane, position."
675 (let ((pos (nth 0 ctx))
675 (let ((pos (nth 0 ctx))
676 (str (nth 1 ctx))
676 (str (nth 1 ctx))
677 (fixup (nth 2 ctx)))
677 (fixup (nth 2 ctx)))
678 (save-excursion
678 (save-excursion
679 (goto-char (max (point-min) (- pos 15000)))
679 (goto-char (max (point-min) (- pos 15000)))
680 (if (and (not (equal str ""))
680 (if (and (not (equal str ""))
681 (search-forward str nil t))
681 (search-forward str nil t))
682 (- (point) fixup)
682 (- (point) fixup)
683 (max pos (point-min))))))
683 (max pos (point-min))))))
684
684
685 (defun hg-restore-context (ctx)
685 (defun hg-restore-context (ctx)
686 "Attempt to restore the user's editing context."
686 "Attempt to restore the user's editing context."
687 (let ((point-context (nth 0 ctx))
687 (let ((point-context (nth 0 ctx))
688 (mark-context (nth 1 ctx)))
688 (mark-context (nth 1 ctx)))
689 (goto-char (hg-find-context point-context))
689 (goto-char (hg-find-context point-context))
690 (when mark-context
690 (when mark-context
691 (set-mark (hg-find-context mark-context)))))
691 (set-mark (hg-find-context mark-context)))))
692
692
693
693
694 ;;; Hooks.
694 ;;; Hooks.
695
695
696 (defun hg-mode-line-internal (status parents)
696 (defun hg-mode-line-internal (status parents)
697 (setq hg-status status
697 (setq hg-status status
698 hg-mode (and status (concat " Hg:"
698 hg-mode (and status (concat " Hg:"
699 parents
699 parents
700 (cdr (assq status
700 (cdr (assq status
701 '((normal . "")
701 '((normal . "")
702 (removed . "r")
702 (removed . "r")
703 (added . "a")
703 (added . "a")
704 (deleted . "!")
704 (deleted . "!")
705 (modified . "m"))))))))
705 (modified . "m"))))))))
706
706
707 (defun hg-mode-line (&optional force)
707 (defun hg-mode-line (&optional force)
708 "Update the modeline with the current status of a file.
708 "Update the modeline with the current status of a file.
709 An update occurs if optional argument FORCE is non-nil,
709 An update occurs if optional argument FORCE is non-nil,
710 hg-update-modeline is non-nil, or we have not yet checked the state of
710 hg-update-modeline is non-nil, or we have not yet checked the state of
711 the file."
711 the file."
712 (let ((root (hg-root)))
712 (let ((root (hg-root)))
713 (when (and root (or force hg-update-modeline (not hg-mode)))
713 (when (and root (or force hg-update-modeline (not hg-mode)))
714 (let ((status (hg-file-status buffer-file-name))
714 (let ((status (hg-file-status buffer-file-name))
715 (parents (hg-parents-for-mode-line root)))
715 (parents (hg-parents-for-mode-line root)))
716 (hg-mode-line-internal status parents)
716 (hg-mode-line-internal status parents)
717 status))))
717 status))))
718
718
719 (defun hg-mode (&optional toggle)
719 (defun hg-mode (&optional toggle)
720 "Minor mode for Mercurial distributed SCM integration.
720 "Minor mode for Mercurial distributed SCM integration.
721
721
722 The Mercurial mode user interface is based on that of VC mode, so if
722 The Mercurial mode user interface is based on that of VC mode, so if
723 you're already familiar with VC, the same keybindings and functions
723 you're already familiar with VC, the same keybindings and functions
724 will generally work.
724 will generally work.
725
725
726 Below is a list of many common SCM tasks. In the list, `G/L\'
726 Below is a list of many common SCM tasks. In the list, `G/L\'
727 indicates whether a key binding is global (G) to a repository or
727 indicates whether a key binding is global (G) to a repository or
728 local (L) to a file. Many commands take a prefix argument.
728 local (L) to a file. Many commands take a prefix argument.
729
729
730 SCM Task G/L Key Binding Command Name
730 SCM Task G/L Key Binding Command Name
731 -------- --- ----------- ------------
731 -------- --- ----------- ------------
732 Help overview (what you are reading) G C-c h h hg-help-overview
732 Help overview (what you are reading) G C-c h h hg-help-overview
733
733
734 Tell Mercurial to manage a file G C-c h a hg-add
734 Tell Mercurial to manage a file G C-c h a hg-add
735 Commit changes to current file only L C-x v n hg-commit-start
735 Commit changes to current file only L C-x v n hg-commit-start
736 Undo changes to file since commit L C-x v u hg-revert-buffer
736 Undo changes to file since commit L C-x v u hg-revert-buffer
737
737
738 Diff file vs last checkin L C-x v = hg-diff
738 Diff file vs last checkin L C-x v = hg-diff
739
739
740 View file change history L C-x v l hg-log
740 View file change history L C-x v l hg-log
741 View annotated file L C-x v a hg-annotate
741 View annotated file L C-x v a hg-annotate
742
742
743 Diff repo vs last checkin G C-c h = hg-diff-repo
743 Diff repo vs last checkin G C-c h = hg-diff-repo
744 View status of files in repo G C-c h s hg-status
744 View status of files in repo G C-c h s hg-status
745 Commit all changes G C-c h c hg-commit-start
745 Commit all changes G C-c h c hg-commit-start
746
746
747 Undo all changes since last commit G C-c h U hg-revert
747 Undo all changes since last commit G C-c h U hg-revert
748 View repo change history G C-c h l hg-log-repo
748 View repo change history G C-c h l hg-log-repo
749
749
750 See changes that can be pulled G C-c h , hg-incoming
750 See changes that can be pulled G C-c h , hg-incoming
751 Pull changes G C-c h < hg-pull
751 Pull changes G C-c h < hg-pull
752 Update working directory after pull G C-c h u hg-update
752 Update working directory after pull G C-c h u hg-update
753 See changes that can be pushed G C-c h . hg-outgoing
753 See changes that can be pushed G C-c h . hg-outgoing
754 Push changes G C-c h > hg-push"
754 Push changes G C-c h > hg-push"
755 (unless vc-make-backup-files
755 (unless vc-make-backup-files
756 (set (make-local-variable 'backup-inhibited) t))
756 (set (make-local-variable 'backup-inhibited) t))
757 (run-hooks 'hg-mode-hook))
757 (run-hooks 'hg-mode-hook))
758
758
759 (defun hg-find-file-hook ()
759 (defun hg-find-file-hook ()
760 (ignore-errors
760 (ignore-errors
761 (when (hg-mode-line)
761 (when (hg-mode-line)
762 (hg-mode))))
762 (hg-mode))))
763
763
764 (add-hook 'find-file-hooks 'hg-find-file-hook)
764 (add-hook 'find-file-hooks 'hg-find-file-hook)
765
765
766 (defun hg-after-save-hook ()
766 (defun hg-after-save-hook ()
767 (ignore-errors
767 (ignore-errors
768 (let ((old-status hg-status))
768 (let ((old-status hg-status))
769 (hg-mode-line)
769 (hg-mode-line)
770 (if (and (not old-status) hg-status)
770 (if (and (not old-status) hg-status)
771 (hg-mode)))))
771 (hg-mode)))))
772
772
773 (add-hook 'after-save-hook 'hg-after-save-hook)
773 (add-hook 'after-save-hook 'hg-after-save-hook)
774
774
775
775
776 ;;; User interface functions.
776 ;;; User interface functions.
777
777
778 (defun hg-help-overview ()
778 (defun hg-help-overview ()
779 "This is an overview of the Mercurial SCM mode for Emacs.
779 "This is an overview of the Mercurial SCM mode for Emacs.
780
780
781 You can find the source code, license (GPL v2), and credits for this
781 You can find the source code, license (GPL v2), and credits for this
782 code by typing `M-x find-library mercurial RET'."
782 code by typing `M-x find-library mercurial RET'."
783 (interactive)
783 (interactive)
784 (hg-view-output ("Mercurial Help Overview")
784 (hg-view-output ("Mercurial Help Overview")
785 (insert (documentation 'hg-help-overview))
785 (insert (documentation 'hg-help-overview))
786 (let ((pos (point)))
786 (let ((pos (point)))
787 (insert (documentation 'hg-mode))
787 (insert (documentation 'hg-mode))
788 (goto-char pos)
788 (goto-char pos)
789 (end-of-line 1)
789 (end-of-line 1)
790 (delete-region pos (point)))
790 (delete-region pos (point)))
791 (let ((hg-root-dir (hg-root)))
791 (let ((hg-root-dir (hg-root)))
792 (if (not hg-root-dir)
792 (if (not hg-root-dir)
793 (error "error: %s: directory is not part of a Mercurial repository."
793 (error "error: %s: directory is not part of a Mercurial repository."
794 default-directory)
794 default-directory)
795 (cd hg-root-dir)))))
795 (cd hg-root-dir)))))
796
796
797 (defun hg-fix-paths ()
797 (defun hg-fix-paths ()
798 "Fix paths reported by some Mercurial commands."
798 "Fix paths reported by some Mercurial commands."
799 (save-excursion
799 (save-excursion
800 (goto-char (point-min))
800 (goto-char (point-min))
801 (while (re-search-forward " \\.\\.." nil t)
801 (while (re-search-forward " \\.\\.." nil t)
802 (replace-match " " nil nil))))
802 (replace-match " " nil nil))))
803
803
804 (defun hg-add (path)
804 (defun hg-add (path)
805 "Add PATH to the Mercurial repository on the next commit.
805 "Add PATH to the Mercurial repository on the next commit.
806 With a prefix argument, prompt for the path to add."
806 With a prefix argument, prompt for the path to add."
807 (interactive (list (hg-read-file-name " to add")))
807 (interactive (list (hg-read-file-name " to add")))
808 (let ((buf (current-buffer))
808 (let ((buf (current-buffer))
809 (update (equal buffer-file-name path)))
809 (update (equal buffer-file-name path)))
810 (hg-view-output (hg-output-buffer-name)
810 (hg-view-output (hg-output-buffer-name)
811 (apply 'call-process (hg-binary) nil t nil (list "add" path))
811 (apply 'call-process (hg-binary) nil t nil (list "add" path))
812 (hg-fix-paths)
812 (hg-fix-paths)
813 (goto-char (point-min))
813 (goto-char (point-min))
814 (cd (hg-root path)))
814 (cd (hg-root path)))
815 (when update
815 (when update
816 (unless vc-make-backup-files
816 (unless vc-make-backup-files
817 (set (make-local-variable 'backup-inhibited) t))
817 (set (make-local-variable 'backup-inhibited) t))
818 (with-current-buffer buf
818 (with-current-buffer buf
819 (hg-mode-line)))))
819 (hg-mode-line)))))
820
820
821 (defun hg-addremove ()
821 (defun hg-addremove ()
822 (interactive)
822 (interactive)
823 (error "not implemented"))
823 (error "not implemented"))
824
824
825 (defun hg-annotate ()
825 (defun hg-annotate ()
826 (interactive)
826 (interactive)
827 (error "not implemented"))
827 (error "not implemented"))
828
828
829 (defun hg-commit-toggle-file (pos)
829 (defun hg-commit-toggle-file (pos)
830 "Toggle whether or not the file at POS will be committed."
830 "Toggle whether or not the file at POS will be committed."
831 (interactive "d")
831 (interactive "d")
832 (save-excursion
832 (save-excursion
833 (goto-char pos)
833 (goto-char pos)
834 (let ((face (get-text-property pos 'face))
834 (let ((face (get-text-property pos 'face))
835 (inhibit-read-only t)
835 (inhibit-read-only t)
836 bol)
836 bol)
837 (beginning-of-line)
837 (beginning-of-line)
838 (setq bol (+ (point) 4))
838 (setq bol (+ (point) 4))
839 (end-of-line)
839 (end-of-line)
840 (if (eq face 'bold)
840 (if (eq face 'bold)
841 (progn
841 (progn
842 (remove-text-properties bol (point) '(face nil))
842 (remove-text-properties bol (point) '(face nil))
843 (message "%s will not be committed"
843 (message "%s will not be committed"
844 (buffer-substring bol (point))))
844 (buffer-substring bol (point))))
845 (add-text-properties bol (point) '(face bold))
845 (add-text-properties bol (point) '(face bold))
846 (message "%s will be committed"
846 (message "%s will be committed"
847 (buffer-substring bol (point)))))))
847 (buffer-substring bol (point)))))))
848
848
849 (defun hg-commit-mouse-clicked (event)
849 (defun hg-commit-mouse-clicked (event)
850 "Toggle whether or not the file at POS will be committed."
850 "Toggle whether or not the file at POS will be committed."
851 (interactive "@e")
851 (interactive "@e")
852 (hg-commit-toggle-file (hg-event-point event)))
852 (hg-commit-toggle-file (hg-event-point event)))
853
853
854 (defun hg-commit-kill ()
854 (defun hg-commit-kill ()
855 "Kill the commit currently being prepared."
855 "Kill the commit currently being prepared."
856 (interactive)
856 (interactive)
857 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
857 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
858 (let ((buf hg-prev-buffer))
858 (let ((buf hg-prev-buffer))
859 (kill-buffer nil)
859 (kill-buffer nil)
860 (switch-to-buffer buf))))
860 (switch-to-buffer buf))))
861
861
862 (defun hg-commit-finish ()
862 (defun hg-commit-finish ()
863 "Finish preparing a commit, and perform the actual commit.
863 "Finish preparing a commit, and perform the actual commit.
864 The hook hg-pre-commit-hook is run before anything else is done. If
864 The hook hg-pre-commit-hook is run before anything else is done. If
865 the commit message is empty and hg-commit-allow-empty-message is nil,
865 the commit message is empty and hg-commit-allow-empty-message is nil,
866 an error is raised. If the list of files to commit is empty and
866 an error is raised. If the list of files to commit is empty and
867 hg-commit-allow-empty-file-list is nil, an error is raised."
867 hg-commit-allow-empty-file-list is nil, an error is raised."
868 (interactive)
868 (interactive)
869 (let ((root hg-root))
869 (let ((root hg-root))
870 (save-excursion
870 (save-excursion
871 (run-hooks 'hg-pre-commit-hook)
871 (run-hooks 'hg-pre-commit-hook)
872 (goto-char (point-min))
872 (goto-char (point-min))
873 (search-forward hg-commit-message-start)
873 (search-forward hg-commit-message-start)
874 (let (message files)
874 (let (message files)
875 (let ((start (point)))
875 (let ((start (point)))
876 (goto-char (point-max))
876 (goto-char (point-max))
877 (search-backward hg-commit-message-end)
877 (search-backward hg-commit-message-end)
878 (setq message (hg-strip (buffer-substring start (point)))))
878 (setq message (hg-strip (buffer-substring start (point)))))
879 (when (and (= (length message) 0)
879 (when (and (= (length message) 0)
880 (not hg-commit-allow-empty-message))
880 (not hg-commit-allow-empty-message))
881 (error "Cannot proceed - commit message is empty"))
881 (error "Cannot proceed - commit message is empty"))
882 (forward-line 1)
882 (forward-line 1)
883 (beginning-of-line)
883 (beginning-of-line)
884 (while (< (point) (point-max))
884 (while (< (point) (point-max))
885 (let ((pos (+ (point) 4)))
885 (let ((pos (+ (point) 4)))
886 (end-of-line)
886 (end-of-line)
887 (when (eq (get-text-property pos 'face) 'bold)
887 (when (eq (get-text-property pos 'face) 'bold)
888 (end-of-line)
888 (end-of-line)
889 (setq files (cons (buffer-substring pos (point)) files))))
889 (setq files (cons (buffer-substring pos (point)) files))))
890 (forward-line 1))
890 (forward-line 1))
891 (when (and (= (length files) 0)
891 (when (and (= (length files) 0)
892 (not hg-commit-allow-empty-file-list))
892 (not hg-commit-allow-empty-file-list))
893 (error "Cannot proceed - no files to commit"))
893 (error "Cannot proceed - no files to commit"))
894 (setq message (concat message "\n"))
894 (setq message (concat message "\n"))
895 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
895 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
896 (let ((buf hg-prev-buffer))
896 (let ((buf hg-prev-buffer))
897 (kill-buffer nil)
897 (kill-buffer nil)
898 (switch-to-buffer buf))
898 (switch-to-buffer buf))
899 (hg-update-mode-lines root))))
899 (hg-update-mode-lines root))))
900
900
901 (defun hg-commit-mode ()
901 (defun hg-commit-mode ()
902 "Mode for describing a commit of changes to a Mercurial repository.
902 "Mode for describing a commit of changes to a Mercurial repository.
903 This involves two actions: describing the changes with a commit
903 This involves two actions: describing the changes with a commit
904 message, and choosing the files to commit.
904 message, and choosing the files to commit.
905
905
906 To describe the commit, simply type some text in the designated area.
906 To describe the commit, simply type some text in the designated area.
907
907
908 By default, all modified, added and removed files are selected for
908 By default, all modified, added and removed files are selected for
909 committing. Files that will be committed are displayed in bold face\;
909 committing. Files that will be committed are displayed in bold face\;
910 those that will not are displayed in normal face.
910 those that will not are displayed in normal face.
911
911
912 To toggle whether a file will be committed, move the cursor over a
912 To toggle whether a file will be committed, move the cursor over a
913 particular file and hit space or return. Alternatively, middle click
913 particular file and hit space or return. Alternatively, middle click
914 on the file.
914 on the file.
915
915
916 Key bindings
916 Key bindings
917 ------------
917 ------------
918 \\[hg-commit-finish] proceed with commit
918 \\[hg-commit-finish] proceed with commit
919 \\[hg-commit-kill] kill commit
919 \\[hg-commit-kill] kill commit
920
920
921 \\[hg-diff-repo] view diff of pending changes"
921 \\[hg-diff-repo] view diff of pending changes"
922 (interactive)
922 (interactive)
923 (use-local-map hg-commit-mode-map)
923 (use-local-map hg-commit-mode-map)
924 (set-syntax-table text-mode-syntax-table)
924 (set-syntax-table text-mode-syntax-table)
925 (setq local-abbrev-table text-mode-abbrev-table
925 (setq local-abbrev-table text-mode-abbrev-table
926 major-mode 'hg-commit-mode
926 major-mode 'hg-commit-mode
927 mode-name "Hg-Commit")
927 mode-name "Hg-Commit")
928 (set-buffer-modified-p nil)
928 (set-buffer-modified-p nil)
929 (setq buffer-undo-list nil)
929 (setq buffer-undo-list nil)
930 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
930 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
931
931
932 (defun hg-commit-start ()
932 (defun hg-commit-start ()
933 "Prepare a commit of changes to the repository containing the current file."
933 "Prepare a commit of changes to the repository containing the current file."
934 (interactive)
934 (interactive)
935 (while hg-prev-buffer
935 (while hg-prev-buffer
936 (set-buffer hg-prev-buffer))
936 (set-buffer hg-prev-buffer))
937 (let ((root (hg-root))
937 (let ((root (hg-root))
938 (prev-buffer (current-buffer))
938 (prev-buffer (current-buffer))
939 modified-files)
939 modified-files)
940 (unless root
940 (unless root
941 (error "Cannot commit outside a repository!"))
941 (error "Cannot commit outside a repository!"))
942 (hg-sync-buffers root)
942 (hg-sync-buffers root)
943 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
943 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
944 (when (and (= (length modified-files) 0)
944 (when (and (= (length modified-files) 0)
945 (not hg-commit-allow-empty-file-list))
945 (not hg-commit-allow-empty-file-list))
946 (error "No pending changes to commit"))
946 (error "No pending changes to commit"))
947 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
947 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
948 (pop-to-buffer (get-buffer-create buf-name))
948 (pop-to-buffer (get-buffer-create buf-name))
949 (when (= (point-min) (point-max))
949 (when (= (point-min) (point-max))
950 (set (make-local-variable 'hg-root) root)
950 (set (make-local-variable 'hg-root) root)
951 (setq hg-prev-buffer prev-buffer)
951 (setq hg-prev-buffer prev-buffer)
952 (insert "\n")
952 (insert "\n")
953 (let ((bol (point)))
953 (let ((bol (point)))
954 (insert hg-commit-message-end)
954 (insert hg-commit-message-end)
955 (add-text-properties bol (point) '(face bold-italic)))
955 (add-text-properties bol (point) '(face bold-italic)))
956 (let ((file-area (point)))
956 (let ((file-area (point)))
957 (insert modified-files)
957 (insert modified-files)
958 (goto-char file-area)
958 (goto-char file-area)
959 (while (< (point) (point-max))
959 (while (< (point) (point-max))
960 (let ((bol (point)))
960 (let ((bol (point)))
961 (forward-char 1)
961 (forward-char 1)
962 (insert " ")
962 (insert " ")
963 (end-of-line)
963 (end-of-line)
964 (add-text-properties (+ bol 4) (point)
964 (add-text-properties (+ bol 4) (point)
965 '(face bold mouse-face highlight)))
965 '(face bold mouse-face highlight)))
966 (forward-line 1))
966 (forward-line 1))
967 (goto-char file-area)
967 (goto-char file-area)
968 (add-text-properties (point) (point-max)
968 (add-text-properties (point) (point-max)
969 `(keymap ,hg-commit-mode-file-map))
969 `(keymap ,hg-commit-mode-file-map))
970 (goto-char (point-min))
970 (goto-char (point-min))
971 (insert hg-commit-message-start)
971 (insert hg-commit-message-start)
972 (add-text-properties (point-min) (point) '(face bold-italic))
972 (add-text-properties (point-min) (point) '(face bold-italic))
973 (insert "\n\n")
973 (insert "\n\n")
974 (forward-line -1)
974 (forward-line -1)
975 (save-excursion
975 (save-excursion
976 (goto-char (point-max))
976 (goto-char (point-max))
977 (search-backward hg-commit-message-end)
977 (search-backward hg-commit-message-end)
978 (add-text-properties (match-beginning 0) (point-max)
978 (add-text-properties (match-beginning 0) (point-max)
979 '(read-only t))
979 '(read-only t))
980 (goto-char (point-min))
980 (goto-char (point-min))
981 (search-forward hg-commit-message-start)
981 (search-forward hg-commit-message-start)
982 (add-text-properties (match-beginning 0) (match-end 0)
982 (add-text-properties (match-beginning 0) (match-end 0)
983 '(read-only t)))
983 '(read-only t)))
984 (hg-commit-mode)
984 (hg-commit-mode)
985 (cd root))))))
985 (cd root))))))
986
986
987 (defun hg-diff (path &optional rev1 rev2)
987 (defun hg-diff (path &optional rev1 rev2)
988 "Show the differences between REV1 and REV2 of PATH.
988 "Show the differences between REV1 and REV2 of PATH.
989 When called interactively, the default behaviour is to treat REV1 as
989 When called interactively, the default behaviour is to treat REV1 as
990 the \"parent\" revision, REV2 as the current edited version of the file, and
990 the \"parent\" revision, REV2 as the current edited version of the file, and
991 PATH as the file edited in the current buffer.
991 PATH as the file edited in the current buffer.
992 With a prefix argument, prompt for all of these."
992 With a prefix argument, prompt for all of these."
993 (interactive (list (hg-read-file-name " to diff")
993 (interactive (list (hg-read-file-name " to diff")
994 (let ((rev1 (hg-read-rev " to start with" 'parent)))
994 (let ((rev1 (hg-read-rev " to start with" 'parent)))
995 (and (not (eq rev1 'parent)) rev1))
995 (and (not (eq rev1 'parent)) rev1))
996 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
996 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
997 (and (not (eq rev2 'working-dir)) rev2))))
997 (and (not (eq rev2 'working-dir)) rev2))))
998 (hg-sync-buffers path)
998 (hg-sync-buffers path)
999 (let ((a-path (hg-abbrev-file-name path))
999 (let ((a-path (hg-abbrev-file-name path))
1000 ;; none revision is specified explicitly
1000 ;; none revision is specified explicitly
1001 (none (and (not rev1) (not rev2)))
1001 (none (and (not rev1) (not rev2)))
1002 ;; only one revision is specified explicitly
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 (and (not rev1) rev2)))
1004 (and (not rev1) rev2)))
1005 diff)
1005 diff)
1006 (hg-view-output ((cond
1006 (hg-view-output ((cond
1007 (none
1007 (none
1008 (format "Mercurial: Diff against parent of %s" a-path))
1008 (format "Mercurial: Diff against parent of %s" a-path))
1009 (one
1009 (one
1010 (format "Mercurial: Diff of rev %s of %s" one a-path))
1010 (format "Mercurial: Diff of rev %s of %s" one a-path))
1011 (t
1011 (t
1012 (format "Mercurial: Diff from rev %s to %s of %s"
1012 (format "Mercurial: Diff from rev %s to %s of %s"
1013 rev1 rev2 a-path))))
1013 rev1 rev2 a-path))))
1014 (cond
1014 (cond
1015 (none
1015 (none
1016 (call-process (hg-binary) nil t nil "diff" path))
1016 (call-process (hg-binary) nil t nil "diff" path))
1017 (one
1017 (one
1018 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1018 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1019 (t
1019 (t
1020 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1020 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1021 (diff-mode)
1021 (diff-mode)
1022 (setq diff (not (= (point-min) (point-max))))
1022 (setq diff (not (= (point-min) (point-max))))
1023 (font-lock-fontify-buffer)
1023 (font-lock-fontify-buffer)
1024 (cd (hg-root path)))
1024 (cd (hg-root path)))
1025 diff))
1025 diff))
1026
1026
1027 (defun hg-diff-repo (path &optional rev1 rev2)
1027 (defun hg-diff-repo (path &optional rev1 rev2)
1028 "Show the differences between REV1 and REV2 of repository containing PATH.
1028 "Show the differences between REV1 and REV2 of repository containing PATH.
1029 When called interactively, the default behaviour is to treat REV1 as
1029 When called interactively, the default behaviour is to treat REV1 as
1030 the \"parent\" revision, REV2 as the current edited version of the file, and
1030 the \"parent\" revision, REV2 as the current edited version of the file, and
1031 PATH as the `hg-root' of the current buffer.
1031 PATH as the `hg-root' of the current buffer.
1032 With a prefix argument, prompt for all of these."
1032 With a prefix argument, prompt for all of these."
1033 (interactive (list (hg-read-file-name " to diff")
1033 (interactive (list (hg-read-file-name " to diff")
1034 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1034 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1035 (and (not (eq rev1 'parent)) rev1))
1035 (and (not (eq rev1 'parent)) rev1))
1036 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1036 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1037 (and (not (eq rev2 'working-dir)) rev2))))
1037 (and (not (eq rev2 'working-dir)) rev2))))
1038 (hg-diff (hg-root path) rev1 rev2))
1038 (hg-diff (hg-root path) rev1 rev2))
1039
1039
1040 (defun hg-forget (path)
1040 (defun hg-forget (path)
1041 "Lose track of PATH, which has been added, but not yet committed.
1041 "Lose track of PATH, which has been added, but not yet committed.
1042 This will prevent the file from being incorporated into the Mercurial
1042 This will prevent the file from being incorporated into the Mercurial
1043 repository on the next commit.
1043 repository on the next commit.
1044 With a prefix argument, prompt for the path to forget."
1044 With a prefix argument, prompt for the path to forget."
1045 (interactive (list (hg-read-file-name " to forget")))
1045 (interactive (list (hg-read-file-name " to forget")))
1046 (let ((buf (current-buffer))
1046 (let ((buf (current-buffer))
1047 (update (equal buffer-file-name path)))
1047 (update (equal buffer-file-name path)))
1048 (hg-view-output (hg-output-buffer-name)
1048 (hg-view-output (hg-output-buffer-name)
1049 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1049 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1050 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1050 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1051 (hg-fix-paths)
1051 (hg-fix-paths)
1052 (goto-char (point-min))
1052 (goto-char (point-min))
1053 (cd (hg-root path)))
1053 (cd (hg-root path)))
1054 (when update
1054 (when update
1055 (with-current-buffer buf
1055 (with-current-buffer buf
1056 (when (local-variable-p 'backup-inhibited)
1056 (when (local-variable-p 'backup-inhibited)
1057 (kill-local-variable 'backup-inhibited))
1057 (kill-local-variable 'backup-inhibited))
1058 (hg-mode-line)))))
1058 (hg-mode-line)))))
1059
1059
1060 (defun hg-incoming (&optional repo)
1060 (defun hg-incoming (&optional repo)
1061 "Display changesets present in REPO that are not present locally."
1061 "Display changesets present in REPO that are not present locally."
1062 (interactive (list (hg-read-repo-name " where changes would come from")))
1062 (interactive (list (hg-read-repo-name " where changes would come from")))
1063 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1063 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1064 (hg-abbrev-file-name (hg-root))
1064 (hg-abbrev-file-name (hg-root))
1065 (hg-abbrev-file-name
1065 (hg-abbrev-file-name
1066 (or repo hg-incoming-repository))))
1066 (or repo hg-incoming-repository))))
1067 (call-process (hg-binary) nil t nil "incoming"
1067 (call-process (hg-binary) nil t nil "incoming"
1068 (or repo hg-incoming-repository))
1068 (or repo hg-incoming-repository))
1069 (hg-log-mode)
1069 (hg-log-mode)
1070 (cd (hg-root))))
1070 (cd (hg-root))))
1071
1071
1072 (defun hg-init ()
1072 (defun hg-init ()
1073 (interactive)
1073 (interactive)
1074 (error "not implemented"))
1074 (error "not implemented"))
1075
1075
1076 (defun hg-log-mode ()
1076 (defun hg-log-mode ()
1077 "Mode for viewing a Mercurial change log."
1077 "Mode for viewing a Mercurial change log."
1078 (goto-char (point-min))
1078 (goto-char (point-min))
1079 (when (looking-at "^searching for changes.*$")
1079 (when (looking-at "^searching for changes.*$")
1080 (delete-region (match-beginning 0) (match-end 0)))
1080 (delete-region (match-beginning 0) (match-end 0)))
1081 (run-hooks 'hg-log-mode-hook))
1081 (run-hooks 'hg-log-mode-hook))
1082
1082
1083 (defun hg-log (path &optional rev1 rev2 log-limit)
1083 (defun hg-log (path &optional rev1 rev2 log-limit)
1084 "Display the revision history of PATH.
1084 "Display the revision history of PATH.
1085 History is displayed between REV1 and REV2.
1085 History is displayed between REV1 and REV2.
1086 Number of displayed changesets is limited to LOG-LIMIT.
1086 Number of displayed changesets is limited to LOG-LIMIT.
1087 REV1 defaults to the tip, while REV2 defaults to 0.
1087 REV1 defaults to the tip, while REV2 defaults to 0.
1088 LOG-LIMIT defaults to `hg-log-limit'.
1088 LOG-LIMIT defaults to `hg-log-limit'.
1089 With a prefix argument, prompt for each parameter."
1089 With a prefix argument, prompt for each parameter."
1090 (interactive (list (hg-read-file-name " to log")
1090 (interactive (list (hg-read-file-name " to log")
1091 (hg-read-rev " to start with"
1091 (hg-read-rev " to start with"
1092 "tip")
1092 "tip")
1093 (hg-read-rev " to end with"
1093 (hg-read-rev " to end with"
1094 "0")
1094 "0")
1095 (hg-read-number "Output limited to: "
1095 (hg-read-number "Output limited to: "
1096 hg-log-limit)))
1096 hg-log-limit)))
1097 (let ((a-path (hg-abbrev-file-name path))
1097 (let ((a-path (hg-abbrev-file-name path))
1098 (r1 (or rev1 "tip"))
1098 (r1 (or rev1 "tip"))
1099 (r2 (or rev2 "0"))
1099 (r2 (or rev2 "0"))
1100 (limit (format "%d" (or log-limit hg-log-limit))))
1100 (limit (format "%d" (or log-limit hg-log-limit))))
1101 (hg-view-output ((if (equal r1 r2)
1101 (hg-view-output ((if (equal r1 r2)
1102 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1102 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1103 (format
1103 (format
1104 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1104 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1105 limit r1 r2 a-path)))
1105 limit r1 r2 a-path)))
1106 (eval (list* 'call-process (hg-binary) nil t nil
1106 (eval (list* 'call-process (hg-binary) nil t nil
1107 "log"
1107 "log"
1108 "-r" (format "%s:%s" r1 r2)
1108 "-r" (format "%s:%s" r1 r2)
1109 "-l" limit
1109 "-l" limit
1110 (if (> (length path) (length (hg-root path)))
1110 (if (> (length path) (length (hg-root path)))
1111 (cons path nil)
1111 (cons path nil)
1112 nil)))
1112 nil)))
1113 (hg-log-mode)
1113 (hg-log-mode)
1114 (cd (hg-root path)))))
1114 (cd (hg-root path)))))
1115
1115
1116 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1116 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1117 "Display the revision history of the repository containing PATH.
1117 "Display the revision history of the repository containing PATH.
1118 History is displayed between REV1 and REV2.
1118 History is displayed between REV1 and REV2.
1119 Number of displayed changesets is limited to LOG-LIMIT,
1119 Number of displayed changesets is limited to LOG-LIMIT,
1120 REV1 defaults to the tip, while REV2 defaults to 0.
1120 REV1 defaults to the tip, while REV2 defaults to 0.
1121 LOG-LIMIT defaults to `hg-log-limit'.
1121 LOG-LIMIT defaults to `hg-log-limit'.
1122 With a prefix argument, prompt for each parameter."
1122 With a prefix argument, prompt for each parameter."
1123 (interactive (list (hg-read-file-name " to log")
1123 (interactive (list (hg-read-file-name " to log")
1124 (hg-read-rev " to start with"
1124 (hg-read-rev " to start with"
1125 "tip")
1125 "tip")
1126 (hg-read-rev " to end with"
1126 (hg-read-rev " to end with"
1127 "0")
1127 "0")
1128 (hg-read-number "Output limited to: "
1128 (hg-read-number "Output limited to: "
1129 hg-log-limit)))
1129 hg-log-limit)))
1130 (hg-log (hg-root path) rev1 rev2 log-limit))
1130 (hg-log (hg-root path) rev1 rev2 log-limit))
1131
1131
1132 (defun hg-outgoing (&optional repo)
1132 (defun hg-outgoing (&optional repo)
1133 "Display changesets present locally that are not present in REPO."
1133 "Display changesets present locally that are not present in REPO."
1134 (interactive (list (hg-read-repo-name " where changes would go to" nil
1134 (interactive (list (hg-read-repo-name " where changes would go to" nil
1135 hg-outgoing-repository)))
1135 hg-outgoing-repository)))
1136 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1136 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1137 (hg-abbrev-file-name (hg-root))
1137 (hg-abbrev-file-name (hg-root))
1138 (hg-abbrev-file-name
1138 (hg-abbrev-file-name
1139 (or repo hg-outgoing-repository))))
1139 (or repo hg-outgoing-repository))))
1140 (call-process (hg-binary) nil t nil "outgoing"
1140 (call-process (hg-binary) nil t nil "outgoing"
1141 (or repo hg-outgoing-repository))
1141 (or repo hg-outgoing-repository))
1142 (hg-log-mode)
1142 (hg-log-mode)
1143 (cd (hg-root))))
1143 (cd (hg-root))))
1144
1144
1145 (defun hg-pull (&optional repo)
1145 (defun hg-pull (&optional repo)
1146 "Pull changes from repository REPO.
1146 "Pull changes from repository REPO.
1147 This does not update the working directory."
1147 This does not update the working directory."
1148 (interactive (list (hg-read-repo-name " to pull from")))
1148 (interactive (list (hg-read-repo-name " to pull from")))
1149 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1149 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1150 (hg-abbrev-file-name (hg-root))
1150 (hg-abbrev-file-name (hg-root))
1151 (hg-abbrev-file-name
1151 (hg-abbrev-file-name
1152 (or repo hg-incoming-repository))))
1152 (or repo hg-incoming-repository))))
1153 (call-process (hg-binary) nil t nil "pull"
1153 (call-process (hg-binary) nil t nil "pull"
1154 (or repo hg-incoming-repository))
1154 (or repo hg-incoming-repository))
1155 (cd (hg-root))))
1155 (cd (hg-root))))
1156
1156
1157 (defun hg-push (&optional repo)
1157 (defun hg-push (&optional repo)
1158 "Push changes to repository REPO."
1158 "Push changes to repository REPO."
1159 (interactive (list (hg-read-repo-name " to push to")))
1159 (interactive (list (hg-read-repo-name " to push to")))
1160 (hg-view-output ((format "Mercurial: Push from %s to %s"
1160 (hg-view-output ((format "Mercurial: Push from %s to %s"
1161 (hg-abbrev-file-name (hg-root))
1161 (hg-abbrev-file-name (hg-root))
1162 (hg-abbrev-file-name
1162 (hg-abbrev-file-name
1163 (or repo hg-outgoing-repository))))
1163 (or repo hg-outgoing-repository))))
1164 (call-process (hg-binary) nil t nil "push"
1164 (call-process (hg-binary) nil t nil "push"
1165 (or repo hg-outgoing-repository))
1165 (or repo hg-outgoing-repository))
1166 (cd (hg-root))))
1166 (cd (hg-root))))
1167
1167
1168 (defun hg-revert-buffer-internal ()
1168 (defun hg-revert-buffer-internal ()
1169 (let ((ctx (hg-buffer-context)))
1169 (let ((ctx (hg-buffer-context)))
1170 (message "Reverting %s..." buffer-file-name)
1170 (message "Reverting %s..." buffer-file-name)
1171 (hg-run0 "revert" buffer-file-name)
1171 (hg-run0 "revert" buffer-file-name)
1172 (revert-buffer t t t)
1172 (revert-buffer t t t)
1173 (hg-restore-context ctx)
1173 (hg-restore-context ctx)
1174 (hg-mode-line)
1174 (hg-mode-line)
1175 (message "Reverting %s...done" buffer-file-name)))
1175 (message "Reverting %s...done" buffer-file-name)))
1176
1176
1177 (defun hg-revert-buffer ()
1177 (defun hg-revert-buffer ()
1178 "Revert current buffer's file back to the latest committed version.
1178 "Revert current buffer's file back to the latest committed version.
1179 If the file has not changed, nothing happens. Otherwise, this
1179 If the file has not changed, nothing happens. Otherwise, this
1180 displays a diff and asks for confirmation before reverting."
1180 displays a diff and asks for confirmation before reverting."
1181 (interactive)
1181 (interactive)
1182 (let ((vc-suppress-confirm nil)
1182 (let ((vc-suppress-confirm nil)
1183 (obuf (current-buffer))
1183 (obuf (current-buffer))
1184 diff)
1184 diff)
1185 (vc-buffer-sync)
1185 (vc-buffer-sync)
1186 (unwind-protect
1186 (unwind-protect
1187 (setq diff (hg-diff buffer-file-name))
1187 (setq diff (hg-diff buffer-file-name))
1188 (when diff
1188 (when diff
1189 (unless (yes-or-no-p "Discard changes? ")
1189 (unless (yes-or-no-p "Discard changes? ")
1190 (error "Revert cancelled")))
1190 (error "Revert cancelled")))
1191 (when diff
1191 (when diff
1192 (let ((buf (current-buffer)))
1192 (let ((buf (current-buffer)))
1193 (delete-window (selected-window))
1193 (delete-window (selected-window))
1194 (kill-buffer buf))))
1194 (kill-buffer buf))))
1195 (set-buffer obuf)
1195 (set-buffer obuf)
1196 (when diff
1196 (when diff
1197 (hg-revert-buffer-internal))))
1197 (hg-revert-buffer-internal))))
1198
1198
1199 (defun hg-root (&optional path)
1199 (defun hg-root (&optional path)
1200 "Return the root of the repository that contains the given path.
1200 "Return the root of the repository that contains the given path.
1201 If the path is outside a repository, return nil.
1201 If the path is outside a repository, return nil.
1202 When called interactively, the root is printed. A prefix argument
1202 When called interactively, the root is printed. A prefix argument
1203 prompts for a path to check."
1203 prompts for a path to check."
1204 (interactive (list (hg-read-file-name)))
1204 (interactive (list (hg-read-file-name)))
1205 (if (or path (not hg-root))
1205 (if (or path (not hg-root))
1206 (let ((root (do ((prev nil dir)
1206 (let ((root (do ((prev nil dir)
1207 (dir (file-name-directory
1207 (dir (file-name-directory
1208 (or
1208 (or
1209 path
1209 path
1210 buffer-file-name
1210 buffer-file-name
1211 (expand-file-name default-directory)))
1211 (expand-file-name default-directory)))
1212 (file-name-directory (directory-file-name dir))))
1212 (file-name-directory (directory-file-name dir))))
1213 ((equal prev dir))
1213 ((equal prev dir))
1214 (when (file-directory-p (concat dir ".hg"))
1214 (when (file-directory-p (concat dir ".hg"))
1215 (return dir)))))
1215 (return dir)))))
1216 (when (interactive-p)
1216 (when (interactive-p)
1217 (if root
1217 (if root
1218 (message "The root of this repository is `%s'." root)
1218 (message "The root of this repository is `%s'." root)
1219 (message "The path `%s' is not in a Mercurial repository."
1219 (message "The path `%s' is not in a Mercurial repository."
1220 (hg-abbrev-file-name path))))
1220 (hg-abbrev-file-name path))))
1221 root)
1221 root)
1222 hg-root))
1222 hg-root))
1223
1223
1224 (defun hg-cwd (&optional path)
1224 (defun hg-cwd (&optional path)
1225 "Return the current directory of PATH within the repository."
1225 "Return the current directory of PATH within the repository."
1226 (do ((stack nil (cons (file-name-nondirectory
1226 (do ((stack nil (cons (file-name-nondirectory
1227 (directory-file-name dir))
1227 (directory-file-name dir))
1228 stack))
1228 stack))
1229 (prev nil dir)
1229 (prev nil dir)
1230 (dir (file-name-directory (or path buffer-file-name
1230 (dir (file-name-directory (or path buffer-file-name
1231 (expand-file-name default-directory)))
1231 (expand-file-name default-directory)))
1232 (file-name-directory (directory-file-name dir))))
1232 (file-name-directory (directory-file-name dir))))
1233 ((equal prev dir))
1233 ((equal prev dir))
1234 (when (file-directory-p (concat dir ".hg"))
1234 (when (file-directory-p (concat dir ".hg"))
1235 (let ((cwd (mapconcat 'identity stack "/")))
1235 (let ((cwd (mapconcat 'identity stack "/")))
1236 (unless (equal cwd "")
1236 (unless (equal cwd "")
1237 (return (file-name-as-directory cwd)))))))
1237 (return (file-name-as-directory cwd)))))))
1238
1238
1239 (defun hg-status (path)
1239 (defun hg-status (path)
1240 "Print revision control status of a file or directory.
1240 "Print revision control status of a file or directory.
1241 With prefix argument, prompt for the path to give status for.
1241 With prefix argument, prompt for the path to give status for.
1242 Names are displayed relative to the repository root."
1242 Names are displayed relative to the repository root."
1243 (interactive (list (hg-read-file-name " for status" (hg-root))))
1243 (interactive (list (hg-read-file-name " for status" (hg-root))))
1244 (let ((root (hg-root)))
1244 (let ((root (hg-root)))
1245 (hg-view-output ((format "Mercurial: Status of %s in %s"
1245 (hg-view-output ((format "Mercurial: Status of %s in %s"
1246 (let ((name (substring (expand-file-name path)
1246 (let ((name (substring (expand-file-name path)
1247 (length root))))
1247 (length root))))
1248 (if (> (length name) 0)
1248 (if (> (length name) 0)
1249 name
1249 name
1250 "*"))
1250 "*"))
1251 (hg-abbrev-file-name root)))
1251 (hg-abbrev-file-name root)))
1252 (apply 'call-process (hg-binary) nil t nil
1252 (apply 'call-process (hg-binary) nil t nil
1253 (list "--cwd" root "status" path))
1253 (list "--cwd" root "status" path))
1254 (cd (hg-root path)))))
1254 (cd (hg-root path)))))
1255
1255
1256 (defun hg-undo ()
1256 (defun hg-undo ()
1257 (interactive)
1257 (interactive)
1258 (error "not implemented"))
1258 (error "not implemented"))
1259
1259
1260 (defun hg-update ()
1260 (defun hg-update ()
1261 (interactive)
1261 (interactive)
1262 (error "not implemented"))
1262 (error "not implemented"))
1263
1263
1264 (defun hg-version-other-window ()
1264 (defun hg-version-other-window ()
1265 (interactive)
1265 (interactive)
1266 (error "not implemented"))
1266 (error "not implemented"))
1267
1267
1268
1268
1269 (provide 'mercurial)
1269 (provide 'mercurial)
1270
1270
1271
1271
1272 ;;; Local Variables:
1272 ;;; Local Variables:
1273 ;;; prompt-to-byte-compile: nil
1273 ;;; prompt-to-byte-compile: nil
1274 ;;; end:
1274 ;;; end:
@@ -1,410 +1,410 b''
1 ;;; mq.el --- Emacs support for Mercurial Queues
1 ;;; mq.el --- Emacs support for Mercurial Queues
2
2
3 ;; Copyright (C) 2006 Bryan O'Sullivan
3 ;; Copyright (C) 2006 Bryan O'Sullivan
4
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
6
7 ;; mq.el is free software; you can redistribute it and/or modify it
7 ;; mq.el is free software; you can redistribute it and/or modify it
8 ;; under the terms of version 2 of the GNU General Public License as
8 ;; under the terms of version 2 of the GNU General Public License as
9 ;; published by the Free Software Foundation.
9 ;; published by the Free Software Foundation.
10
10
11 ;; mq.el is distributed in the hope that it will be useful, but
11 ;; mq.el is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; General Public License for more details.
14 ;; General Public License for more details.
15
15
16 ;; You should have received a copy of the GNU General Public License
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with mq.el, GNU Emacs, or XEmacs; see the file COPYING (`C-h
17 ;; along with mq.el, GNU Emacs, or XEmacs; see the file COPYING (`C-h
18 ;; C-l'). If not, write to the Free Software Foundation, Inc., 59
18 ;; C-l'). If not, write to the Free Software Foundation, Inc., 59
19 ;; Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 ;; Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
20
21 (require 'mercurial)
21 (require 'mercurial)
22
22
23
23
24 (defcustom mq-mode-hook nil
24 (defcustom mq-mode-hook nil
25 "Hook run when a buffer enters mq-mode."
25 "Hook run when a buffer enters mq-mode."
26 :type 'sexp
26 :type 'sexp
27 :group 'mercurial)
27 :group 'mercurial)
28
28
29 (defcustom mq-global-prefix "\C-cq"
29 (defcustom mq-global-prefix "\C-cq"
30 "The global prefix for Mercurial Queues keymap bindings."
30 "The global prefix for Mercurial Queues keymap bindings."
31 :type 'sexp
31 :type 'sexp
32 :group 'mercurial)
32 :group 'mercurial)
33
33
34 (defcustom mq-edit-mode-hook nil
34 (defcustom mq-edit-mode-hook nil
35 "Hook run after a buffer is populated to edit a patch description."
35 "Hook run after a buffer is populated to edit a patch description."
36 :type 'sexp
36 :type 'sexp
37 :group 'mercurial)
37 :group 'mercurial)
38
38
39 (defcustom mq-edit-finish-hook nil
39 (defcustom mq-edit-finish-hook nil
40 "Hook run before a patch description is finished up with."
40 "Hook run before a patch description is finished up with."
41 :type 'sexp
41 :type 'sexp
42 :group 'mercurial)
42 :group 'mercurial)
43
43
44 (defcustom mq-signoff-address nil
44 (defcustom mq-signoff-address nil
45 "Address with which to sign off on a patch."
45 "Address with which to sign off on a patch."
46 :type 'string
46 :type 'string
47 :group 'mercurial)
47 :group 'mercurial)
48
48
49
49
50 ;;; Internal variables.
50 ;;; Internal variables.
51
51
52 (defvar mq-mode nil
52 (defvar mq-mode nil
53 "Is this file managed by MQ?")
53 "Is this file managed by MQ?")
54 (make-variable-buffer-local 'mq-mode)
54 (make-variable-buffer-local 'mq-mode)
55 (put 'mq-mode 'permanent-local t)
55 (put 'mq-mode 'permanent-local t)
56
56
57 (defvar mq-patch-history nil)
57 (defvar mq-patch-history nil)
58
58
59 (defvar mq-top-patch '(nil))
59 (defvar mq-top-patch '(nil))
60
60
61 (defvar mq-prev-buffer nil)
61 (defvar mq-prev-buffer nil)
62 (make-variable-buffer-local 'mq-prev-buffer)
62 (make-variable-buffer-local 'mq-prev-buffer)
63 (put 'mq-prev-buffer 'permanent-local t)
63 (put 'mq-prev-buffer 'permanent-local t)
64
64
65
65
66 ;;; Global keymap.
66 ;;; Global keymap.
67
67
68 (defvar mq-global-map (make-sparse-keymap))
68 (defvar mq-global-map (make-sparse-keymap))
69 (fset 'mq-global-map mq-global-map)
69 (fset 'mq-global-map mq-global-map)
70 (global-set-key mq-global-prefix 'mq-global-map)
70 (global-set-key mq-global-prefix 'mq-global-map)
71 (define-key mq-global-map "." 'mq-push)
71 (define-key mq-global-map "." 'mq-push)
72 (define-key mq-global-map ">" 'mq-push-all)
72 (define-key mq-global-map ">" 'mq-push-all)
73 (define-key mq-global-map "," 'mq-pop)
73 (define-key mq-global-map "," 'mq-pop)
74 (define-key mq-global-map "<" 'mq-pop-all)
74 (define-key mq-global-map "<" 'mq-pop-all)
75 (define-key mq-global-map "=" 'mq-diff)
75 (define-key mq-global-map "=" 'mq-diff)
76 (define-key mq-global-map "r" 'mq-refresh)
76 (define-key mq-global-map "r" 'mq-refresh)
77 (define-key mq-global-map "e" 'mq-refresh-edit)
77 (define-key mq-global-map "e" 'mq-refresh-edit)
78 (define-key mq-global-map "i" 'mq-new)
78 (define-key mq-global-map "i" 'mq-new)
79 (define-key mq-global-map "n" 'mq-next)
79 (define-key mq-global-map "n" 'mq-next)
80 (define-key mq-global-map "o" 'mq-signoff)
80 (define-key mq-global-map "o" 'mq-signoff)
81 (define-key mq-global-map "p" 'mq-previous)
81 (define-key mq-global-map "p" 'mq-previous)
82 (define-key mq-global-map "s" 'mq-edit-series)
82 (define-key mq-global-map "s" 'mq-edit-series)
83 (define-key mq-global-map "t" 'mq-top)
83 (define-key mq-global-map "t" 'mq-top)
84
84
85 (add-minor-mode 'mq-mode 'mq-mode)
85 (add-minor-mode 'mq-mode 'mq-mode)
86
86
87
87
88 ;;; Refresh edit mode keymap.
88 ;;; Refresh edit mode keymap.
89
89
90 (defvar mq-edit-mode-map (make-sparse-keymap))
90 (defvar mq-edit-mode-map (make-sparse-keymap))
91 (define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
91 (define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
92 (define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
92 (define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
93 (define-key mq-edit-mode-map "\C-c\C-s" 'mq-signoff)
93 (define-key mq-edit-mode-map "\C-c\C-s" 'mq-signoff)
94
94
95
95
96 ;;; Helper functions.
96 ;;; Helper functions.
97
97
98 (defun mq-read-patch-name (&optional source prompt force)
98 (defun mq-read-patch-name (&optional source prompt force)
99 "Read a patch name to use with a command.
99 "Read a patch name to use with a command.
100 May return nil, meaning \"use the default\"."
100 May return nil, meaning \"use the default\"."
101 (let ((patches (split-string
101 (let ((patches (split-string
102 (hg-chomp (hg-run0 (or source "qseries"))) "\n")))
102 (hg-chomp (hg-run0 (or source "qseries"))) "\n")))
103 (when force
103 (when force
104 (completing-read (format "Patch%s: " (or prompt ""))
104 (completing-read (format "Patch%s: " (or prompt ""))
105 (map 'list 'cons patches patches)
105 (map 'list 'cons patches patches)
106 nil
106 nil
107 nil
107 nil
108 nil
108 nil
109 'mq-patch-history))))
109 'mq-patch-history))))
110
110
111 (defun mq-refresh-buffers (root)
111 (defun mq-refresh-buffers (root)
112 (save-excursion
112 (save-excursion
113 (dolist (buf (hg-buffers-visiting-repo root))
113 (dolist (buf (hg-buffers-visiting-repo root))
114 (when (not (verify-visited-file-modtime buf))
114 (when (not (verify-visited-file-modtime buf))
115 (set-buffer buf)
115 (set-buffer buf)
116 (let ((ctx (hg-buffer-context)))
116 (let ((ctx (hg-buffer-context)))
117 (message "Refreshing %s..." (buffer-name))
117 (message "Refreshing %s..." (buffer-name))
118 (revert-buffer t t t)
118 (revert-buffer t t t)
119 (hg-restore-context ctx)
119 (hg-restore-context ctx)
120 (message "Refreshing %s...done" (buffer-name))))))
120 (message "Refreshing %s...done" (buffer-name))))))
121 (hg-update-mode-lines root)
121 (hg-update-mode-lines root)
122 (mq-update-mode-lines root))
122 (mq-update-mode-lines root))
123
123
124 (defun mq-last-line ()
124 (defun mq-last-line ()
125 (goto-char (point-max))
125 (goto-char (point-max))
126 (beginning-of-line)
126 (beginning-of-line)
127 (when (looking-at "^$")
127 (when (looking-at "^$")
128 (forward-line -1))
128 (forward-line -1))
129 (let ((bol (point)))
129 (let ((bol (point)))
130 (end-of-line)
130 (end-of-line)
131 (let ((line (buffer-substring bol (point))))
131 (let ((line (buffer-substring bol (point))))
132 (when (> (length line) 0)
132 (when (> (length line) 0)
133 line))))
133 line))))
134
134
135 (defun mq-push (&optional patch)
135 (defun mq-push (&optional patch)
136 "Push patches until PATCH is reached.
136 "Push patches until PATCH is reached.
137 If PATCH is nil, push at most one patch."
137 If PATCH is nil, push at most one patch."
138 (interactive (list (mq-read-patch-name "qunapplied" " to push"
138 (interactive (list (mq-read-patch-name "qunapplied" " to push"
139 current-prefix-arg)))
139 current-prefix-arg)))
140 (let ((root (hg-root))
140 (let ((root (hg-root))
141 (prev-buf (current-buffer))
141 (prev-buf (current-buffer))
142 last-line ok)
142 last-line ok)
143 (unless root
143 (unless root
144 (error "Cannot push outside a repository!"))
144 (error "Cannot push outside a repository!"))
145 (hg-sync-buffers root)
145 (hg-sync-buffers root)
146 (let ((buf-name (format "MQ: Push %s" (or patch "next patch"))))
146 (let ((buf-name (format "MQ: Push %s" (or patch "next patch"))))
147 (kill-buffer (get-buffer-create buf-name))
147 (kill-buffer (get-buffer-create buf-name))
148 (split-window-vertically)
148 (split-window-vertically)
149 (other-window 1)
149 (other-window 1)
150 (switch-to-buffer (get-buffer-create buf-name))
150 (switch-to-buffer (get-buffer-create buf-name))
151 (cd root)
151 (cd root)
152 (message "Pushing...")
152 (message "Pushing...")
153 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpush"
153 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpush"
154 (if patch (list patch))))
154 (if patch (list patch))))
155 last-line (mq-last-line))
155 last-line (mq-last-line))
156 (let ((lines (count-lines (point-min) (point-max))))
156 (let ((lines (count-lines (point-min) (point-max))))
157 (if (or (<= lines 1)
157 (if (or (<= lines 1)
158 (and (equal lines 2) (string-match "Now at:" last-line)))
158 (and (equal lines 2) (string-match "Now at:" last-line)))
159 (progn
159 (progn
160 (kill-buffer (current-buffer))
160 (kill-buffer (current-buffer))
161 (delete-window))
161 (delete-window))
162 (hg-view-mode prev-buf))))
162 (hg-view-mode prev-buf))))
163 (mq-refresh-buffers root)
163 (mq-refresh-buffers root)
164 (sit-for 0)
164 (sit-for 0)
165 (when last-line
165 (when last-line
166 (if ok
166 (if ok
167 (message "Pushing... %s" last-line)
167 (message "Pushing... %s" last-line)
168 (error "Pushing... %s" last-line)))))
168 (error "Pushing... %s" last-line)))))
169
169
170 (defun mq-push-all ()
170 (defun mq-push-all ()
171 "Push patches until all are applied."
171 "Push patches until all are applied."
172 (interactive)
172 (interactive)
173 (mq-push "-a"))
173 (mq-push "-a"))
174
174
175 (defun mq-pop (&optional patch)
175 (defun mq-pop (&optional patch)
176 "Pop patches until PATCH is reached.
176 "Pop patches until PATCH is reached.
177 If PATCH is nil, pop at most one patch."
177 If PATCH is nil, pop at most one patch."
178 (interactive (list (mq-read-patch-name "qapplied" " to pop to"
178 (interactive (list (mq-read-patch-name "qapplied" " to pop to"
179 current-prefix-arg)))
179 current-prefix-arg)))
180 (let ((root (hg-root))
180 (let ((root (hg-root))
181 last-line ok)
181 last-line ok)
182 (unless root
182 (unless root
183 (error "Cannot pop outside a repository!"))
183 (error "Cannot pop outside a repository!"))
184 (hg-sync-buffers root)
184 (hg-sync-buffers root)
185 (set-buffer (generate-new-buffer "qpop"))
185 (set-buffer (generate-new-buffer "qpop"))
186 (cd root)
186 (cd root)
187 (message "Popping...")
187 (message "Popping...")
188 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpop"
188 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpop"
189 (if patch (list patch))))
189 (if patch (list patch))))
190 last-line (mq-last-line))
190 last-line (mq-last-line))
191 (kill-buffer (current-buffer))
191 (kill-buffer (current-buffer))
192 (mq-refresh-buffers root)
192 (mq-refresh-buffers root)
193 (sit-for 0)
193 (sit-for 0)
194 (when last-line
194 (when last-line
195 (if ok
195 (if ok
196 (message "Popping... %s" last-line)
196 (message "Popping... %s" last-line)
197 (error "Popping... %s" last-line)))))
197 (error "Popping... %s" last-line)))))
198
198
199 (defun mq-pop-all ()
199 (defun mq-pop-all ()
200 "Push patches until none are applied."
200 "Push patches until none are applied."
201 (interactive)
201 (interactive)
202 (mq-pop "-a"))
202 (mq-pop "-a"))
203
203
204 (defun mq-refresh-internal (root &rest args)
204 (defun mq-refresh-internal (root &rest args)
205 (hg-sync-buffers root)
205 (hg-sync-buffers root)
206 (let ((patch (mq-patch-info "qtop")))
206 (let ((patch (mq-patch-info "qtop")))
207 (message "Refreshing %s..." patch)
207 (message "Refreshing %s..." patch)
208 (let ((ret (apply 'hg-run "qrefresh" args)))
208 (let ((ret (apply 'hg-run "qrefresh" args)))
209 (if (equal (car ret) 0)
209 (if (equal (car ret) 0)
210 (message "Refreshing %s... done." patch)
210 (message "Refreshing %s... done." patch)
211 (error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
211 (error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
212
212
213 (defun mq-refresh (&optional git)
213 (defun mq-refresh (&optional git)
214 "Refresh the topmost applied patch.
214 "Refresh the topmost applied patch.
215 With a prefix argument, generate a git-compatible patch."
215 With a prefix argument, generate a git-compatible patch."
216 (interactive "P")
216 (interactive "P")
217 (let ((root (hg-root)))
217 (let ((root (hg-root)))
218 (unless root
218 (unless root
219 (error "Cannot refresh outside of a repository!"))
219 (error "Cannot refresh outside of a repository!"))
220 (apply 'mq-refresh-internal root (if git '("--git")))))
220 (apply 'mq-refresh-internal root (if git '("--git")))))
221
221
222 (defun mq-patch-info (cmd &optional msg)
222 (defun mq-patch-info (cmd &optional msg)
223 (let* ((ret (hg-run cmd))
223 (let* ((ret (hg-run cmd))
224 (info (hg-chomp (cdr ret))))
224 (info (hg-chomp (cdr ret))))
225 (if (equal (car ret) 0)
225 (if (equal (car ret) 0)
226 (if msg
226 (if msg
227 (message "%s patch: %s" msg info)
227 (message "%s patch: %s" msg info)
228 info)
228 info)
229 (error "%s" info))))
229 (error "%s" info))))
230
230
231 (defun mq-top ()
231 (defun mq-top ()
232 "Print the name of the topmost applied patch."
232 "Print the name of the topmost applied patch."
233 (interactive)
233 (interactive)
234 (mq-patch-info "qtop" "Top"))
234 (mq-patch-info "qtop" "Top"))
235
235
236 (defun mq-next ()
236 (defun mq-next ()
237 "Print the name of the next patch to be pushed."
237 "Print the name of the next patch to be pushed."
238 (interactive)
238 (interactive)
239 (mq-patch-info "qnext" "Next"))
239 (mq-patch-info "qnext" "Next"))
240
240
241 (defun mq-previous ()
241 (defun mq-previous ()
242 "Print the name of the first patch below the topmost applied patch.
242 "Print the name of the first patch below the topmost applied patch.
243 This would become the active patch if popped to."
243 This would become the active patch if popped to."
244 (interactive)
244 (interactive)
245 (mq-patch-info "qprev" "Previous"))
245 (mq-patch-info "qprev" "Previous"))
246
246
247 (defun mq-edit-finish ()
247 (defun mq-edit-finish ()
248 "Finish editing the description of this patch, and refresh the patch."
248 "Finish editing the description of this patch, and refresh the patch."
249 (interactive)
249 (interactive)
250 (unless (equal (mq-patch-info "qtop") mq-top)
250 (unless (equal (mq-patch-info "qtop") mq-top)
251 (error "Topmost patch has changed!"))
251 (error "Topmost patch has changed!"))
252 (hg-sync-buffers hg-root)
252 (hg-sync-buffers hg-root)
253 (run-hooks 'mq-edit-finish-hook)
253 (run-hooks 'mq-edit-finish-hook)
254 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
254 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
255 (let ((buf mq-prev-buffer))
255 (let ((buf mq-prev-buffer))
256 (kill-buffer nil)
256 (kill-buffer nil)
257 (switch-to-buffer buf)))
257 (switch-to-buffer buf)))
258
258
259 (defun mq-edit-kill ()
259 (defun mq-edit-kill ()
260 "Kill the edit currently being prepared."
260 "Kill the edit currently being prepared."
261 (interactive)
261 (interactive)
262 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
262 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
263 (let ((buf mq-prev-buffer))
263 (let ((buf mq-prev-buffer))
264 (kill-buffer nil)
264 (kill-buffer nil)
265 (switch-to-buffer buf))))
265 (switch-to-buffer buf))))
266
266
267 (defun mq-get-top (root)
267 (defun mq-get-top (root)
268 (let ((entry (assoc root mq-top-patch)))
268 (let ((entry (assoc root mq-top-patch)))
269 (if entry
269 (if entry
270 (cdr entry))))
270 (cdr entry))))
271
271
272 (defun mq-set-top (root patch)
272 (defun mq-set-top (root patch)
273 (let ((entry (assoc root mq-top-patch)))
273 (let ((entry (assoc root mq-top-patch)))
274 (if entry
274 (if entry
275 (if patch
275 (if patch
276 (setcdr entry patch)
276 (setcdr entry patch)
277 (setq mq-top-patch (delq entry mq-top-patch)))
277 (setq mq-top-patch (delq entry mq-top-patch)))
278 (setq mq-top-patch (cons (cons root patch) mq-top-patch)))))
278 (setq mq-top-patch (cons (cons root patch) mq-top-patch)))))
279
279
280 (defun mq-update-mode-lines (root)
280 (defun mq-update-mode-lines (root)
281 (let ((cwd default-directory))
281 (let ((cwd default-directory))
282 (cd root)
282 (cd root)
283 (condition-case nil
283 (condition-case nil
284 (mq-set-top root (mq-patch-info "qtop"))
284 (mq-set-top root (mq-patch-info "qtop"))
285 (error (mq-set-top root nil)))
285 (error (mq-set-top root nil)))
286 (cd cwd))
286 (cd cwd))
287 (let ((patch (mq-get-top root)))
287 (let ((patch (mq-get-top root)))
288 (save-excursion
288 (save-excursion
289 (dolist (buf (hg-buffers-visiting-repo root))
289 (dolist (buf (hg-buffers-visiting-repo root))
290 (set-buffer buf)
290 (set-buffer buf)
291 (if mq-mode
291 (if mq-mode
292 (setq mq-mode (or (and patch (concat " MQ:" patch)) " MQ")))))))
292 (setq mq-mode (or (and patch (concat " MQ:" patch)) " MQ")))))))
293
293
294 (defun mq-mode (&optional arg)
294 (defun mq-mode (&optional arg)
295 "Minor mode for Mercurial repositories with an MQ patch queue"
295 "Minor mode for Mercurial repositories with an MQ patch queue"
296 (interactive "i")
296 (interactive "i")
297 (cond ((hg-root)
297 (cond ((hg-root)
298 (setq mq-mode (if (null arg) (not mq-mode)
298 (setq mq-mode (if (null arg) (not mq-mode)
299 arg))
299 arg))
300 (mq-update-mode-lines (hg-root))))
300 (mq-update-mode-lines (hg-root))))
301 (run-hooks 'mq-mode-hook))
301 (run-hooks 'mq-mode-hook))
302
302
303 (defun mq-edit-mode ()
303 (defun mq-edit-mode ()
304 "Mode for editing the description of a patch.
304 "Mode for editing the description of a patch.
305
305
306 Key bindings
306 Key bindings
307 ------------
307 ------------
308 \\[mq-edit-finish] use this description
308 \\[mq-edit-finish] use this description
309 \\[mq-edit-kill] abandon this description"
309 \\[mq-edit-kill] abandon this description"
310 (interactive)
310 (interactive)
311 (use-local-map mq-edit-mode-map)
311 (use-local-map mq-edit-mode-map)
312 (set-syntax-table text-mode-syntax-table)
312 (set-syntax-table text-mode-syntax-table)
313 (setq local-abbrev-table text-mode-abbrev-table
313 (setq local-abbrev-table text-mode-abbrev-table
314 major-mode 'mq-edit-mode
314 major-mode 'mq-edit-mode
315 mode-name "MQ-Edit")
315 mode-name "MQ-Edit")
316 (set-buffer-modified-p nil)
316 (set-buffer-modified-p nil)
317 (setq buffer-undo-list nil)
317 (setq buffer-undo-list nil)
318 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
318 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
319
319
320 (defun mq-refresh-edit ()
320 (defun mq-refresh-edit ()
321 "Refresh the topmost applied patch, editing the patch description."
321 "Refresh the topmost applied patch, editing the patch description."
322 (interactive)
322 (interactive)
323 (while mq-prev-buffer
323 (while mq-prev-buffer
324 (set-buffer mq-prev-buffer))
324 (set-buffer mq-prev-buffer))
325 (let ((root (hg-root))
325 (let ((root (hg-root))
326 (prev-buffer (current-buffer))
326 (prev-buffer (current-buffer))
327 (patch (mq-patch-info "qtop")))
327 (patch (mq-patch-info "qtop")))
328 (hg-sync-buffers root)
328 (hg-sync-buffers root)
329 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
329 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
330 (switch-to-buffer (get-buffer-create buf-name))
330 (switch-to-buffer (get-buffer-create buf-name))
331 (when (= (point-min) (point-max))
331 (when (= (point-min) (point-max))
332 (set (make-local-variable 'hg-root) root)
332 (set (make-local-variable 'hg-root) root)
333 (set (make-local-variable 'mq-top) patch)
333 (set (make-local-variable 'mq-top) patch)
334 (setq mq-prev-buffer prev-buffer)
334 (setq mq-prev-buffer prev-buffer)
335 (insert (hg-run0 "qheader"))
335 (insert (hg-run0 "qheader"))
336 (goto-char (point-min)))
336 (goto-char (point-min)))
337 (mq-edit-mode)
337 (mq-edit-mode)
338 (cd root)))
338 (cd root)))
339 (message "Type `C-c C-c' to finish editing and refresh the patch."))
339 (message "Type `C-c C-c' to finish editing and refresh the patch."))
340
340
341 (defun mq-new (name)
341 (defun mq-new (name)
342 "Create a new empty patch named NAME.
342 "Create a new empty patch named NAME.
343 The patch is applied on top of the current topmost patch.
343 The patch is applied on top of the current topmost patch.
344 With a prefix argument, forcibly create the patch even if the working
344 With a prefix argument, forcibly create the patch even if the working
345 directory is modified."
345 directory is modified."
346 (interactive (list (mq-read-patch-name "qseries" " to create" t)))
346 (interactive (list (mq-read-patch-name "qseries" " to create" t)))
347 (message "Creating patch...")
347 (message "Creating patch...")
348 (let ((ret (if current-prefix-arg
348 (let ((ret (if current-prefix-arg
349 (hg-run "qnew" "-f" name)
349 (hg-run "qnew" "-f" name)
350 (hg-run "qnew" name))))
350 (hg-run "qnew" name))))
351 (if (equal (car ret) 0)
351 (if (equal (car ret) 0)
352 (progn
352 (progn
353 (hg-update-mode-lines (buffer-file-name))
353 (hg-update-mode-lines (buffer-file-name))
354 (message "Creating patch... done."))
354 (message "Creating patch... done."))
355 (error "Creating patch... %s" (hg-chomp (cdr ret))))))
355 (error "Creating patch... %s" (hg-chomp (cdr ret))))))
356
356
357 (defun mq-edit-series ()
357 (defun mq-edit-series ()
358 "Edit the MQ series file directly."
358 "Edit the MQ series file directly."
359 (interactive)
359 (interactive)
360 (let ((root (hg-root)))
360 (let ((root (hg-root)))
361 (unless root
361 (unless root
362 (error "Not in an MQ repository!"))
362 (error "Not in an MQ repository!"))
363 (find-file (concat root ".hg/patches/series"))))
363 (find-file (concat root ".hg/patches/series"))))
364
364
365 (defun mq-diff (&optional git)
365 (defun mq-diff (&optional git)
366 "Display a diff of the topmost applied patch.
366 "Display a diff of the topmost applied patch.
367 With a prefix argument, display a git-compatible diff."
367 With a prefix argument, display a git-compatible diff."
368 (interactive "P")
368 (interactive "P")
369 (hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
369 (hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
370 (if git
370 (if git
371 (call-process (hg-binary) nil t nil "qdiff" "--git")
371 (call-process (hg-binary) nil t nil "qdiff" "--git")
372 (call-process (hg-binary) nil t nil "qdiff"))
372 (call-process (hg-binary) nil t nil "qdiff"))
373 (diff-mode)
373 (diff-mode)
374 (font-lock-fontify-buffer)))
374 (font-lock-fontify-buffer)))
375
375
376 (defun mq-signoff ()
376 (defun mq-signoff ()
377 "Sign off on the current patch, in the style used by the Linux kernel.
377 "Sign off on the current patch, in the style used by the Linux kernel.
378 If the variable mq-signoff-address is non-nil, it will be used, otherwise
378 If the variable mq-signoff-address is non-nil, it will be used, otherwise
379 the value of the ui.username item from your hgrc will be used."
379 the value of the ui.username item from your hgrc will be used."
380 (interactive)
380 (interactive)
381 (let ((was-editing (eq major-mode 'mq-edit-mode))
381 (let ((was-editing (eq major-mode 'mq-edit-mode))
382 signed)
382 signed)
383 (unless was-editing
383 (unless was-editing
384 (mq-refresh-edit))
384 (mq-refresh-edit))
385 (save-excursion
385 (save-excursion
386 (let* ((user (or mq-signoff-address
386 (let* ((user (or mq-signoff-address
387 (hg-run0 "debugconfig" "ui.username")))
387 (hg-run0 "debugconfig" "ui.username")))
388 (signoff (concat "Signed-off-by: " user)))
388 (signoff (concat "Signed-off-by: " user)))
389 (if (search-forward signoff nil t)
389 (if (search-forward signoff nil t)
390 (message "You have already signed off on this patch.")
390 (message "You have already signed off on this patch.")
391 (goto-char (point-max))
391 (goto-char (point-max))
392 (let ((case-fold-search t))
392 (let ((case-fold-search t))
393 (if (re-search-backward "^Signed-off-by: " nil t)
393 (if (re-search-backward "^Signed-off-by: " nil t)
394 (forward-line 1)
394 (forward-line 1)
395 (insert "\n")))
395 (insert "\n")))
396 (insert signoff)
396 (insert signoff)
397 (message "%s" signoff)
397 (message "%s" signoff)
398 (setq signed t))))
398 (setq signed t))))
399 (unless was-editing
399 (unless was-editing
400 (if signed
400 (if signed
401 (mq-edit-finish)
401 (mq-edit-finish)
402 (mq-edit-kill)))))
402 (mq-edit-kill)))))
403
403
404
404
405 (provide 'mq)
405 (provide 'mq)
406
406
407
407
408 ;;; Local Variables:
408 ;;; Local Variables:
409 ;;; prompt-to-byte-compile: nil
409 ;;; prompt-to-byte-compile: nil
410 ;;; end:
410 ;;; end:
@@ -1,562 +1,562 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # Copyright (C) 2004, 2005 Canonical Ltd
2 # Copyright (C) 2004, 2005 Canonical Ltd
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
18
18
19 # mbp: "you know that thing where cvs gives you conflict markers?"
19 # mbp: "you know that thing where cvs gives you conflict markers?"
20 # s: "i hate that."
20 # s: "i hate that."
21
21
22 from mercurial import demandimport
22 from mercurial import demandimport
23 demandimport.enable()
23 demandimport.enable()
24
24
25 from mercurial import util, mdiff, fancyopts
25 from mercurial import util, mdiff, fancyopts
26 from mercurial.i18n import _
26 from mercurial.i18n import _
27
27
28
28
29 class CantReprocessAndShowBase(Exception):
29 class CantReprocessAndShowBase(Exception):
30 pass
30 pass
31
31
32
32
33 def warn(message):
33 def warn(message):
34 sys.stdout.flush()
34 sys.stdout.flush()
35 sys.stderr.write(message)
35 sys.stderr.write(message)
36 sys.stderr.flush()
36 sys.stderr.flush()
37
37
38
38
39 def intersect(ra, rb):
39 def intersect(ra, rb):
40 """Given two ranges return the range where they intersect or None.
40 """Given two ranges return the range where they intersect or None.
41
41
42 >>> intersect((0, 10), (0, 6))
42 >>> intersect((0, 10), (0, 6))
43 (0, 6)
43 (0, 6)
44 >>> intersect((0, 10), (5, 15))
44 >>> intersect((0, 10), (5, 15))
45 (5, 10)
45 (5, 10)
46 >>> intersect((0, 10), (10, 15))
46 >>> intersect((0, 10), (10, 15))
47 >>> intersect((0, 9), (10, 15))
47 >>> intersect((0, 9), (10, 15))
48 >>> intersect((0, 9), (7, 15))
48 >>> intersect((0, 9), (7, 15))
49 (7, 9)
49 (7, 9)
50 """
50 """
51 assert ra[0] <= ra[1]
51 assert ra[0] <= ra[1]
52 assert rb[0] <= rb[1]
52 assert rb[0] <= rb[1]
53
53
54 sa = max(ra[0], rb[0])
54 sa = max(ra[0], rb[0])
55 sb = min(ra[1], rb[1])
55 sb = min(ra[1], rb[1])
56 if sa < sb:
56 if sa < sb:
57 return sa, sb
57 return sa, sb
58 else:
58 else:
59 return None
59 return None
60
60
61
61
62 def compare_range(a, astart, aend, b, bstart, bend):
62 def compare_range(a, astart, aend, b, bstart, bend):
63 """Compare a[astart:aend] == b[bstart:bend], without slicing.
63 """Compare a[astart:aend] == b[bstart:bend], without slicing.
64 """
64 """
65 if (aend-astart) != (bend-bstart):
65 if (aend-astart) != (bend-bstart):
66 return False
66 return False
67 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
67 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
68 if a[ia] != b[ib]:
68 if a[ia] != b[ib]:
69 return False
69 return False
70 else:
70 else:
71 return True
71 return True
72
72
73
73
74
74
75
75
76 class Merge3Text(object):
76 class Merge3Text(object):
77 """3-way merge of texts.
77 """3-way merge of texts.
78
78
79 Given strings BASE, OTHER, THIS, tries to produce a combined text
79 Given strings BASE, OTHER, THIS, tries to produce a combined text
80 incorporating the changes from both BASE->OTHER and BASE->THIS."""
80 incorporating the changes from both BASE->OTHER and BASE->THIS."""
81 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
81 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
82 self.basetext = basetext
82 self.basetext = basetext
83 self.atext = atext
83 self.atext = atext
84 self.btext = btext
84 self.btext = btext
85 if base is None:
85 if base is None:
86 base = mdiff.splitnewlines(basetext)
86 base = mdiff.splitnewlines(basetext)
87 if a is None:
87 if a is None:
88 a = mdiff.splitnewlines(atext)
88 a = mdiff.splitnewlines(atext)
89 if b is None:
89 if b is None:
90 b = mdiff.splitnewlines(btext)
90 b = mdiff.splitnewlines(btext)
91 self.base = base
91 self.base = base
92 self.a = a
92 self.a = a
93 self.b = b
93 self.b = b
94
94
95
95
96
96
97 def merge_lines(self,
97 def merge_lines(self,
98 name_a=None,
98 name_a=None,
99 name_b=None,
99 name_b=None,
100 name_base=None,
100 name_base=None,
101 start_marker='<<<<<<<',
101 start_marker='<<<<<<<',
102 mid_marker='=======',
102 mid_marker='=======',
103 end_marker='>>>>>>>',
103 end_marker='>>>>>>>',
104 base_marker=None,
104 base_marker=None,
105 reprocess=False):
105 reprocess=False):
106 """Return merge in cvs-like form.
106 """Return merge in cvs-like form.
107 """
107 """
108 self.conflicts = False
108 self.conflicts = False
109 newline = '\n'
109 newline = '\n'
110 if len(self.a) > 0:
110 if len(self.a) > 0:
111 if self.a[0].endswith('\r\n'):
111 if self.a[0].endswith('\r\n'):
112 newline = '\r\n'
112 newline = '\r\n'
113 elif self.a[0].endswith('\r'):
113 elif self.a[0].endswith('\r'):
114 newline = '\r'
114 newline = '\r'
115 if base_marker and reprocess:
115 if base_marker and reprocess:
116 raise CantReprocessAndShowBase()
116 raise CantReprocessAndShowBase()
117 if name_a:
117 if name_a:
118 start_marker = start_marker + ' ' + name_a
118 start_marker = start_marker + ' ' + name_a
119 if name_b:
119 if name_b:
120 end_marker = end_marker + ' ' + name_b
120 end_marker = end_marker + ' ' + name_b
121 if name_base and base_marker:
121 if name_base and base_marker:
122 base_marker = base_marker + ' ' + name_base
122 base_marker = base_marker + ' ' + name_base
123 merge_regions = self.merge_regions()
123 merge_regions = self.merge_regions()
124 if reprocess is True:
124 if reprocess is True:
125 merge_regions = self.reprocess_merge_regions(merge_regions)
125 merge_regions = self.reprocess_merge_regions(merge_regions)
126 for t in merge_regions:
126 for t in merge_regions:
127 what = t[0]
127 what = t[0]
128 if what == 'unchanged':
128 if what == 'unchanged':
129 for i in range(t[1], t[2]):
129 for i in range(t[1], t[2]):
130 yield self.base[i]
130 yield self.base[i]
131 elif what == 'a' or what == 'same':
131 elif what == 'a' or what == 'same':
132 for i in range(t[1], t[2]):
132 for i in range(t[1], t[2]):
133 yield self.a[i]
133 yield self.a[i]
134 elif what == 'b':
134 elif what == 'b':
135 for i in range(t[1], t[2]):
135 for i in range(t[1], t[2]):
136 yield self.b[i]
136 yield self.b[i]
137 elif what == 'conflict':
137 elif what == 'conflict':
138 self.conflicts = True
138 self.conflicts = True
139 yield start_marker + newline
139 yield start_marker + newline
140 for i in range(t[3], t[4]):
140 for i in range(t[3], t[4]):
141 yield self.a[i]
141 yield self.a[i]
142 if base_marker is not None:
142 if base_marker is not None:
143 yield base_marker + newline
143 yield base_marker + newline
144 for i in range(t[1], t[2]):
144 for i in range(t[1], t[2]):
145 yield self.base[i]
145 yield self.base[i]
146 yield mid_marker + newline
146 yield mid_marker + newline
147 for i in range(t[5], t[6]):
147 for i in range(t[5], t[6]):
148 yield self.b[i]
148 yield self.b[i]
149 yield end_marker + newline
149 yield end_marker + newline
150 else:
150 else:
151 raise ValueError(what)
151 raise ValueError(what)
152
152
153
153
154
154
155
155
156
156
157 def merge_annotated(self):
157 def merge_annotated(self):
158 """Return merge with conflicts, showing origin of lines.
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 for t in self.merge_regions():
162 for t in self.merge_regions():
163 what = t[0]
163 what = t[0]
164 if what == 'unchanged':
164 if what == 'unchanged':
165 for i in range(t[1], t[2]):
165 for i in range(t[1], t[2]):
166 yield 'u | ' + self.base[i]
166 yield 'u | ' + self.base[i]
167 elif what == 'a' or what == 'same':
167 elif what == 'a' or what == 'same':
168 for i in range(t[1], t[2]):
168 for i in range(t[1], t[2]):
169 yield what[0] + ' | ' + self.a[i]
169 yield what[0] + ' | ' + self.a[i]
170 elif what == 'b':
170 elif what == 'b':
171 for i in range(t[1], t[2]):
171 for i in range(t[1], t[2]):
172 yield 'b | ' + self.b[i]
172 yield 'b | ' + self.b[i]
173 elif what == 'conflict':
173 elif what == 'conflict':
174 yield '<<<<\n'
174 yield '<<<<\n'
175 for i in range(t[3], t[4]):
175 for i in range(t[3], t[4]):
176 yield 'A | ' + self.a[i]
176 yield 'A | ' + self.a[i]
177 yield '----\n'
177 yield '----\n'
178 for i in range(t[5], t[6]):
178 for i in range(t[5], t[6]):
179 yield 'B | ' + self.b[i]
179 yield 'B | ' + self.b[i]
180 yield '>>>>\n'
180 yield '>>>>\n'
181 else:
181 else:
182 raise ValueError(what)
182 raise ValueError(what)
183
183
184
184
185
185
186
186
187
187
188 def merge_groups(self):
188 def merge_groups(self):
189 """Yield sequence of line groups. Each one is a tuple:
189 """Yield sequence of line groups. Each one is a tuple:
190
190
191 'unchanged', lines
191 'unchanged', lines
192 Lines unchanged from base
192 Lines unchanged from base
193
193
194 'a', lines
194 'a', lines
195 Lines taken from a
195 Lines taken from a
196
196
197 'same', lines
197 'same', lines
198 Lines taken from a (and equal to b)
198 Lines taken from a (and equal to b)
199
199
200 'b', lines
200 'b', lines
201 Lines taken from b
201 Lines taken from b
202
202
203 'conflict', base_lines, a_lines, b_lines
203 'conflict', base_lines, a_lines, b_lines
204 Lines from base were changed to either a or b and conflict.
204 Lines from base were changed to either a or b and conflict.
205 """
205 """
206 for t in self.merge_regions():
206 for t in self.merge_regions():
207 what = t[0]
207 what = t[0]
208 if what == 'unchanged':
208 if what == 'unchanged':
209 yield what, self.base[t[1]:t[2]]
209 yield what, self.base[t[1]:t[2]]
210 elif what == 'a' or what == 'same':
210 elif what == 'a' or what == 'same':
211 yield what, self.a[t[1]:t[2]]
211 yield what, self.a[t[1]:t[2]]
212 elif what == 'b':
212 elif what == 'b':
213 yield what, self.b[t[1]:t[2]]
213 yield what, self.b[t[1]:t[2]]
214 elif what == 'conflict':
214 elif what == 'conflict':
215 yield (what,
215 yield (what,
216 self.base[t[1]:t[2]],
216 self.base[t[1]:t[2]],
217 self.a[t[3]:t[4]],
217 self.a[t[3]:t[4]],
218 self.b[t[5]:t[6]])
218 self.b[t[5]:t[6]])
219 else:
219 else:
220 raise ValueError(what)
220 raise ValueError(what)
221
221
222
222
223 def merge_regions(self):
223 def merge_regions(self):
224 """Return sequences of matching and conflicting regions.
224 """Return sequences of matching and conflicting regions.
225
225
226 This returns tuples, where the first value says what kind we
226 This returns tuples, where the first value says what kind we
227 have:
227 have:
228
228
229 'unchanged', start, end
229 'unchanged', start, end
230 Take a region of base[start:end]
230 Take a region of base[start:end]
231
231
232 'same', astart, aend
232 'same', astart, aend
233 b and a are different from base but give the same result
233 b and a are different from base but give the same result
234
234
235 'a', start, end
235 'a', start, end
236 Non-clashing insertion from a[start:end]
236 Non-clashing insertion from a[start:end]
237
237
238 Method is as follows:
238 Method is as follows:
239
239
240 The two sequences align only on regions which match the base
240 The two sequences align only on regions which match the base
241 and both descendents. These are found by doing a two-way diff
241 and both descendents. These are found by doing a two-way diff
242 of each one against the base, and then finding the
242 of each one against the base, and then finding the
243 intersections between those regions. These "sync regions"
243 intersections between those regions. These "sync regions"
244 are by definition unchanged in both and easily dealt with.
244 are by definition unchanged in both and easily dealt with.
245
245
246 The regions in between can be in any of three cases:
246 The regions in between can be in any of three cases:
247 conflicted, or changed on only one side.
247 conflicted, or changed on only one side.
248 """
248 """
249
249
250 # section a[0:ia] has been disposed of, etc
250 # section a[0:ia] has been disposed of, etc
251 iz = ia = ib = 0
251 iz = ia = ib = 0
252
252
253 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
253 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
254 #print 'match base [%d:%d]' % (zmatch, zend)
254 #print 'match base [%d:%d]' % (zmatch, zend)
255
255
256 matchlen = zend - zmatch
256 matchlen = zend - zmatch
257 assert matchlen >= 0
257 assert matchlen >= 0
258 assert matchlen == (aend - amatch)
258 assert matchlen == (aend - amatch)
259 assert matchlen == (bend - bmatch)
259 assert matchlen == (bend - bmatch)
260
260
261 len_a = amatch - ia
261 len_a = amatch - ia
262 len_b = bmatch - ib
262 len_b = bmatch - ib
263 len_base = zmatch - iz
263 len_base = zmatch - iz
264 assert len_a >= 0
264 assert len_a >= 0
265 assert len_b >= 0
265 assert len_b >= 0
266 assert len_base >= 0
266 assert len_base >= 0
267
267
268 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
268 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
269
269
270 if len_a or len_b:
270 if len_a or len_b:
271 # try to avoid actually slicing the lists
271 # try to avoid actually slicing the lists
272 equal_a = compare_range(self.a, ia, amatch,
272 equal_a = compare_range(self.a, ia, amatch,
273 self.base, iz, zmatch)
273 self.base, iz, zmatch)
274 equal_b = compare_range(self.b, ib, bmatch,
274 equal_b = compare_range(self.b, ib, bmatch,
275 self.base, iz, zmatch)
275 self.base, iz, zmatch)
276 same = compare_range(self.a, ia, amatch,
276 same = compare_range(self.a, ia, amatch,
277 self.b, ib, bmatch)
277 self.b, ib, bmatch)
278
278
279 if same:
279 if same:
280 yield 'same', ia, amatch
280 yield 'same', ia, amatch
281 elif equal_a and not equal_b:
281 elif equal_a and not equal_b:
282 yield 'b', ib, bmatch
282 yield 'b', ib, bmatch
283 elif equal_b and not equal_a:
283 elif equal_b and not equal_a:
284 yield 'a', ia, amatch
284 yield 'a', ia, amatch
285 elif not equal_a and not equal_b:
285 elif not equal_a and not equal_b:
286 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
286 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
287 else:
287 else:
288 raise AssertionError("can't handle a=b=base but unmatched")
288 raise AssertionError("can't handle a=b=base but unmatched")
289
289
290 ia = amatch
290 ia = amatch
291 ib = bmatch
291 ib = bmatch
292 iz = zmatch
292 iz = zmatch
293
293
294 # if the same part of the base was deleted on both sides
294 # if the same part of the base was deleted on both sides
295 # that's OK, we can just skip it.
295 # that's OK, we can just skip it.
296
296
297
297
298 if matchlen > 0:
298 if matchlen > 0:
299 assert ia == amatch
299 assert ia == amatch
300 assert ib == bmatch
300 assert ib == bmatch
301 assert iz == zmatch
301 assert iz == zmatch
302
302
303 yield 'unchanged', zmatch, zend
303 yield 'unchanged', zmatch, zend
304 iz = zend
304 iz = zend
305 ia = aend
305 ia = aend
306 ib = bend
306 ib = bend
307
307
308
308
309 def reprocess_merge_regions(self, merge_regions):
309 def reprocess_merge_regions(self, merge_regions):
310 """Where there are conflict regions, remove the agreed lines.
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 eliminated.
313 eliminated.
314 """
314 """
315 for region in merge_regions:
315 for region in merge_regions:
316 if region[0] != "conflict":
316 if region[0] != "conflict":
317 yield region
317 yield region
318 continue
318 continue
319 type, iz, zmatch, ia, amatch, ib, bmatch = region
319 type, iz, zmatch, ia, amatch, ib, bmatch = region
320 a_region = self.a[ia:amatch]
320 a_region = self.a[ia:amatch]
321 b_region = self.b[ib:bmatch]
321 b_region = self.b[ib:bmatch]
322 matches = mdiff.get_matching_blocks(''.join(a_region),
322 matches = mdiff.get_matching_blocks(''.join(a_region),
323 ''.join(b_region))
323 ''.join(b_region))
324 next_a = ia
324 next_a = ia
325 next_b = ib
325 next_b = ib
326 for region_ia, region_ib, region_len in matches[:-1]:
326 for region_ia, region_ib, region_len in matches[:-1]:
327 region_ia += ia
327 region_ia += ia
328 region_ib += ib
328 region_ib += ib
329 reg = self.mismatch_region(next_a, region_ia, next_b,
329 reg = self.mismatch_region(next_a, region_ia, next_b,
330 region_ib)
330 region_ib)
331 if reg is not None:
331 if reg is not None:
332 yield reg
332 yield reg
333 yield 'same', region_ia, region_len+region_ia
333 yield 'same', region_ia, region_len+region_ia
334 next_a = region_ia + region_len
334 next_a = region_ia + region_len
335 next_b = region_ib + region_len
335 next_b = region_ib + region_len
336 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
336 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
337 if reg is not None:
337 if reg is not None:
338 yield reg
338 yield reg
339
339
340
340
341 def mismatch_region(next_a, region_ia, next_b, region_ib):
341 def mismatch_region(next_a, region_ia, next_b, region_ib):
342 if next_a < region_ia or next_b < region_ib:
342 if next_a < region_ia or next_b < region_ib:
343 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
343 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
344 mismatch_region = staticmethod(mismatch_region)
344 mismatch_region = staticmethod(mismatch_region)
345
345
346
346
347 def find_sync_regions(self):
347 def find_sync_regions(self):
348 """Return a list of sync regions, where both descendents match the base.
348 """Return a list of sync regions, where both descendents match the base.
349
349
350 Generates a list of (base1, base2, a1, a2, b1, b2). There is
350 Generates a list of (base1, base2, a1, a2, b1, b2). There is
351 always a zero-length sync region at the end of all the files.
351 always a zero-length sync region at the end of all the files.
352 """
352 """
353
353
354 ia = ib = 0
354 ia = ib = 0
355 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
355 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
356 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
356 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
357 len_a = len(amatches)
357 len_a = len(amatches)
358 len_b = len(bmatches)
358 len_b = len(bmatches)
359
359
360 sl = []
360 sl = []
361
361
362 while ia < len_a and ib < len_b:
362 while ia < len_a and ib < len_b:
363 abase, amatch, alen = amatches[ia]
363 abase, amatch, alen = amatches[ia]
364 bbase, bmatch, blen = bmatches[ib]
364 bbase, bmatch, blen = bmatches[ib]
365
365
366 # there is an unconflicted block at i; how long does it
366 # there is an unconflicted block at i; how long does it
367 # extend? until whichever one ends earlier.
367 # extend? until whichever one ends earlier.
368 i = intersect((abase, abase+alen), (bbase, bbase+blen))
368 i = intersect((abase, abase+alen), (bbase, bbase+blen))
369 if i:
369 if i:
370 intbase = i[0]
370 intbase = i[0]
371 intend = i[1]
371 intend = i[1]
372 intlen = intend - intbase
372 intlen = intend - intbase
373
373
374 # found a match of base[i[0], i[1]]; this may be less than
374 # found a match of base[i[0], i[1]]; this may be less than
375 # the region that matches in either one
375 # the region that matches in either one
376 assert intlen <= alen
376 assert intlen <= alen
377 assert intlen <= blen
377 assert intlen <= blen
378 assert abase <= intbase
378 assert abase <= intbase
379 assert bbase <= intbase
379 assert bbase <= intbase
380
380
381 asub = amatch + (intbase - abase)
381 asub = amatch + (intbase - abase)
382 bsub = bmatch + (intbase - bbase)
382 bsub = bmatch + (intbase - bbase)
383 aend = asub + intlen
383 aend = asub + intlen
384 bend = bsub + intlen
384 bend = bsub + intlen
385
385
386 assert self.base[intbase:intend] == self.a[asub:aend], \
386 assert self.base[intbase:intend] == self.a[asub:aend], \
387 (self.base[intbase:intend], self.a[asub:aend])
387 (self.base[intbase:intend], self.a[asub:aend])
388
388
389 assert self.base[intbase:intend] == self.b[bsub:bend]
389 assert self.base[intbase:intend] == self.b[bsub:bend]
390
390
391 sl.append((intbase, intend,
391 sl.append((intbase, intend,
392 asub, aend,
392 asub, aend,
393 bsub, bend))
393 bsub, bend))
394
394
395 # advance whichever one ends first in the base text
395 # advance whichever one ends first in the base text
396 if (abase + alen) < (bbase + blen):
396 if (abase + alen) < (bbase + blen):
397 ia += 1
397 ia += 1
398 else:
398 else:
399 ib += 1
399 ib += 1
400
400
401 intbase = len(self.base)
401 intbase = len(self.base)
402 abase = len(self.a)
402 abase = len(self.a)
403 bbase = len(self.b)
403 bbase = len(self.b)
404 sl.append((intbase, intbase, abase, abase, bbase, bbase))
404 sl.append((intbase, intbase, abase, abase, bbase, bbase))
405
405
406 return sl
406 return sl
407
407
408
408
409
409
410 def find_unconflicted(self):
410 def find_unconflicted(self):
411 """Return a list of ranges in base that are not conflicted."""
411 """Return a list of ranges in base that are not conflicted."""
412 am = mdiff.get_matching_blocks(self.basetext, self.atext)
412 am = mdiff.get_matching_blocks(self.basetext, self.atext)
413 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
413 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
414
414
415 unc = []
415 unc = []
416
416
417 while am and bm:
417 while am and bm:
418 # there is an unconflicted block at i; how long does it
418 # there is an unconflicted block at i; how long does it
419 # extend? until whichever one ends earlier.
419 # extend? until whichever one ends earlier.
420 a1 = am[0][0]
420 a1 = am[0][0]
421 a2 = a1 + am[0][2]
421 a2 = a1 + am[0][2]
422 b1 = bm[0][0]
422 b1 = bm[0][0]
423 b2 = b1 + bm[0][2]
423 b2 = b1 + bm[0][2]
424 i = intersect((a1, a2), (b1, b2))
424 i = intersect((a1, a2), (b1, b2))
425 if i:
425 if i:
426 unc.append(i)
426 unc.append(i)
427
427
428 if a2 < b2:
428 if a2 < b2:
429 del am[0]
429 del am[0]
430 else:
430 else:
431 del bm[0]
431 del bm[0]
432
432
433 return unc
433 return unc
434
434
435
435
436 # bzr compatible interface, for the tests
436 # bzr compatible interface, for the tests
437 class Merge3(Merge3Text):
437 class Merge3(Merge3Text):
438 """3-way merge of texts.
438 """3-way merge of texts.
439
439
440 Given BASE, OTHER, THIS, tries to produce a combined text
440 Given BASE, OTHER, THIS, tries to produce a combined text
441 incorporating the changes from both BASE->OTHER and BASE->THIS.
441 incorporating the changes from both BASE->OTHER and BASE->THIS.
442 All three will typically be sequences of lines."""
442 All three will typically be sequences of lines."""
443 def __init__(self, base, a, b):
443 def __init__(self, base, a, b):
444 basetext = '\n'.join([i.strip('\n') for i in base] + [''])
444 basetext = '\n'.join([i.strip('\n') for i in base] + [''])
445 atext = '\n'.join([i.strip('\n') for i in a] + [''])
445 atext = '\n'.join([i.strip('\n') for i in a] + [''])
446 btext = '\n'.join([i.strip('\n') for i in b] + [''])
446 btext = '\n'.join([i.strip('\n') for i in b] + [''])
447 if util.binary(basetext) or util.binary(atext) or util.binary(btext):
447 if util.binary(basetext) or util.binary(atext) or util.binary(btext):
448 raise util.Abort(_("don't know how to merge binary files"))
448 raise util.Abort(_("don't know how to merge binary files"))
449 Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
449 Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
450
450
451
451
452 def simplemerge(local, base, other, **opts):
452 def simplemerge(local, base, other, **opts):
453 def readfile(filename):
453 def readfile(filename):
454 f = open(filename, "rb")
454 f = open(filename, "rb")
455 text = f.read()
455 text = f.read()
456 f.close()
456 f.close()
457 if util.binary(text):
457 if util.binary(text):
458 msg = _("%s looks like a binary file.") % filename
458 msg = _("%s looks like a binary file.") % filename
459 if not opts.get('text'):
459 if not opts.get('text'):
460 raise util.Abort(msg)
460 raise util.Abort(msg)
461 elif not opts.get('quiet'):
461 elif not opts.get('quiet'):
462 warn(_('warning: %s\n') % msg)
462 warn(_('warning: %s\n') % msg)
463 return text
463 return text
464
464
465 name_a = local
465 name_a = local
466 name_b = other
466 name_b = other
467 labels = opts.get('label', [])
467 labels = opts.get('label', [])
468 if labels:
468 if labels:
469 name_a = labels.pop(0)
469 name_a = labels.pop(0)
470 if labels:
470 if labels:
471 name_b = labels.pop(0)
471 name_b = labels.pop(0)
472 if labels:
472 if labels:
473 raise util.Abort(_("can only specify two labels."))
473 raise util.Abort(_("can only specify two labels."))
474
474
475 localtext = readfile(local)
475 localtext = readfile(local)
476 basetext = readfile(base)
476 basetext = readfile(base)
477 othertext = readfile(other)
477 othertext = readfile(other)
478
478
479 orig = local
479 orig = local
480 local = os.path.realpath(local)
480 local = os.path.realpath(local)
481 if not opts.get('print'):
481 if not opts.get('print'):
482 opener = util.opener(os.path.dirname(local))
482 opener = util.opener(os.path.dirname(local))
483 out = opener(os.path.basename(local), "w", atomictemp=True)
483 out = opener(os.path.basename(local), "w", atomictemp=True)
484 else:
484 else:
485 out = sys.stdout
485 out = sys.stdout
486
486
487 reprocess = not opts.get('no_minimal')
487 reprocess = not opts.get('no_minimal')
488
488
489 m3 = Merge3Text(basetext, localtext, othertext)
489 m3 = Merge3Text(basetext, localtext, othertext)
490 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
490 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
491 reprocess=reprocess):
491 reprocess=reprocess):
492 out.write(line)
492 out.write(line)
493
493
494 if not opts.get('print'):
494 if not opts.get('print'):
495 out.rename()
495 out.rename()
496
496
497 if m3.conflicts:
497 if m3.conflicts:
498 if not opts.get('quiet'):
498 if not opts.get('quiet'):
499 warn(_("warning: conflicts during merge.\n"))
499 warn(_("warning: conflicts during merge.\n"))
500 return 1
500 return 1
501
501
502 options = [('L', 'label', [], _('labels to use on conflict markers')),
502 options = [('L', 'label', [], _('labels to use on conflict markers')),
503 ('a', 'text', None, _('treat all files as text')),
503 ('a', 'text', None, _('treat all files as text')),
504 ('p', 'print', None,
504 ('p', 'print', None,
505 _('print results instead of overwriting LOCAL')),
505 _('print results instead of overwriting LOCAL')),
506 ('', 'no-minimal', None,
506 ('', 'no-minimal', None,
507 _('do not try to minimize conflict regions')),
507 _('do not try to minimize conflict regions')),
508 ('h', 'help', None, _('display help and exit')),
508 ('h', 'help', None, _('display help and exit')),
509 ('q', 'quiet', None, _('suppress output'))]
509 ('q', 'quiet', None, _('suppress output'))]
510
510
511 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER
511 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER
512
512
513 Simple three-way file merge utility with a minimal feature set.
513 Simple three-way file merge utility with a minimal feature set.
514
514
515 Apply to LOCAL the changes necessary to go from BASE to OTHER.
515 Apply to LOCAL the changes necessary to go from BASE to OTHER.
516
516
517 By default, LOCAL is overwritten with the results of this operation.
517 By default, LOCAL is overwritten with the results of this operation.
518 ''')
518 ''')
519
519
520 def showhelp():
520 def showhelp():
521 sys.stdout.write(usage)
521 sys.stdout.write(usage)
522 sys.stdout.write('\noptions:\n')
522 sys.stdout.write('\noptions:\n')
523
523
524 out_opts = []
524 out_opts = []
525 for shortopt, longopt, default, desc in options:
525 for shortopt, longopt, default, desc in options:
526 out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
526 out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
527 longopt and ' --%s' % longopt),
527 longopt and ' --%s' % longopt),
528 '%s' % desc))
528 '%s' % desc))
529 opts_len = max([len(opt[0]) for opt in out_opts])
529 opts_len = max([len(opt[0]) for opt in out_opts])
530 for first, second in out_opts:
530 for first, second in out_opts:
531 sys.stdout.write(' %-*s %s\n' % (opts_len, first, second))
531 sys.stdout.write(' %-*s %s\n' % (opts_len, first, second))
532
532
533 class ParseError(Exception):
533 class ParseError(Exception):
534 """Exception raised on errors in parsing the command line."""
534 """Exception raised on errors in parsing the command line."""
535
535
536 def main(argv):
536 def main(argv):
537 try:
537 try:
538 opts = {}
538 opts = {}
539 try:
539 try:
540 args = fancyopts.fancyopts(argv[1:], options, opts)
540 args = fancyopts.fancyopts(argv[1:], options, opts)
541 except fancyopts.getopt.GetoptError, e:
541 except fancyopts.getopt.GetoptError, e:
542 raise ParseError(e)
542 raise ParseError(e)
543 if opts['help']:
543 if opts['help']:
544 showhelp()
544 showhelp()
545 return 0
545 return 0
546 if len(args) != 3:
546 if len(args) != 3:
547 raise ParseError(_('wrong number of arguments'))
547 raise ParseError(_('wrong number of arguments'))
548 return simplemerge(*args, **opts)
548 return simplemerge(*args, **opts)
549 except ParseError, e:
549 except ParseError, e:
550 sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
550 sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
551 showhelp()
551 showhelp()
552 return 1
552 return 1
553 except util.Abort, e:
553 except util.Abort, e:
554 sys.stderr.write("abort: %s\n" % e)
554 sys.stderr.write("abort: %s\n" % e)
555 return 255
555 return 255
556 except KeyboardInterrupt:
556 except KeyboardInterrupt:
557 return 255
557 return 255
558
558
559 if __name__ == '__main__':
559 if __name__ == '__main__':
560 import sys
560 import sys
561 import os
561 import os
562 sys.exit(main(sys.argv))
562 sys.exit(main(sys.argv))
@@ -1,93 +1,93 b''
1 " vim600: set foldmethod=marker:
1 " vim600: set foldmethod=marker:
2 " =============================================================================
2 " =============================================================================
3 " Name Of File: hg-menu.vim
3 " Name Of File: hg-menu.vim
4 " Description: Interface to Mercurial Version Control.
4 " Description: Interface to Mercurial Version Control.
5 " Author: Steve Borho (modified Jeff Lanzarotta's RCS script)
5 " Author: Steve Borho (modified Jeff Lanzarotta's RCS script)
6 " Date: Wednesday, October 5, 2005
6 " Date: Wednesday, October 5, 2005
7 " Version: 0.1.0
7 " Version: 0.1.0
8 " Copyright: None.
8 " Copyright: None.
9 " Usage: These command and gui menu displays useful hg functions
9 " Usage: These command and gui menu displays useful hg functions
10 " Configuration: Your hg executable must be in your path.
10 " Configuration: Your hg executable must be in your path.
11 " =============================================================================
11 " =============================================================================
12
12
13 " Section: Init {{{1
13 " Section: Init {{{1
14 if exists("loaded_hg_menu")
14 if exists("loaded_hg_menu")
15 finish
15 finish
16 endif
16 endif
17 let loaded_hg_menu = 1
17 let loaded_hg_menu = 1
18
18
19 " Section: Menu Options {{{1
19 " Section: Menu Options {{{1
20 if has("gui")
20 if has("gui")
21 " amenu H&G.Commit\ File<Tab>,ci :!hg commit %<CR>:e!<CR>
21 " amenu H&G.Commit\ File<Tab>,ci :!hg commit %<CR>:e!<CR>
22 " amenu H&G.Commit\ All<Tab>,call :!hg commit<CR>:e!<CR>
22 " amenu H&G.Commit\ All<Tab>,call :!hg commit<CR>:e!<CR>
23 " amenu H&G.-SEP1- <nul>
23 " amenu H&G.-SEP1- <nul>
24 amenu H&G.Add<Tab>\\add :!hg add %<CR><CR>
24 amenu H&G.Add<Tab>\\add :!hg add %<CR><CR>
25 amenu H&G.Forget\ Add<Tab>\\fgt :!hg forget %<CR><CR>
25 amenu H&G.Forget\ Add<Tab>\\fgt :!hg forget %<CR><CR>
26 amenu H&G.Show\ Differences<Tab>\\diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
26 amenu H&G.Show\ Differences<Tab>\\diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
27 amenu H&G.Revert\ to\ Last\ Version<Tab>\\revert :!hg revert %<CR>:e!<CR>
27 amenu H&G.Revert\ to\ Last\ Version<Tab>\\revert :!hg revert %<CR>:e!<CR>
28 amenu H&G.Show\ History<Tab>\\log :call ShowResults("FileLog", "hg\ log")<CR><CR>
28 amenu H&G.Show\ History<Tab>\\log :call ShowResults("FileLog", "hg\ log")<CR><CR>
29 amenu H&G.Annotate<Tab>\\an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
29 amenu H&G.Annotate<Tab>\\an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
30 amenu H&G.-SEP1- <nul>
30 amenu H&G.-SEP1- <nul>
31 amenu H&G.Repo\ Status<Tab>\\stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
31 amenu H&G.Repo\ Status<Tab>\\stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
32 amenu H&G.Pull<Tab>\\pull :!hg pull<CR>:e!<CR>
32 amenu H&G.Pull<Tab>\\pull :!hg pull<CR>:e!<CR>
33 amenu H&G.Update<Tab>\\upd :!hg update<CR>:e!<CR>
33 amenu H&G.Update<Tab>\\upd :!hg update<CR>:e!<CR>
34 endif
34 endif
35
35
36 " Section: Mappings {{{1
36 " Section: Mappings {{{1
37 if(v:version >= 600)
37 if(v:version >= 600)
38 " The default Leader is \ 'backslash'
38 " The default Leader is \ 'backslash'
39 map <Leader>add :!hg add %<CR><CR>
39 map <Leader>add :!hg add %<CR><CR>
40 map <Leader>fgt :!hg forget %<CR><CR>
40 map <Leader>fgt :!hg forget %<CR><CR>
41 map <Leader>diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
41 map <Leader>diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
42 map <Leader>revert :!hg revert %<CR>:e!<CR>
42 map <Leader>revert :!hg revert %<CR>:e!<CR>
43 map <Leader>log :call ShowResults("FileLog", "hg\ log")<CR><CR>
43 map <Leader>log :call ShowResults("FileLog", "hg\ log")<CR><CR>
44 map <Leader>an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
44 map <Leader>an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
45 map <Leader>stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
45 map <Leader>stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
46 map <Leader>upd :!hg update<CR>:e!<CR>
46 map <Leader>upd :!hg update<CR>:e!<CR>
47 map <Leader>pull :!hg pull<CR>:e!<CR>
47 map <Leader>pull :!hg pull<CR>:e!<CR>
48 else
48 else
49 " pre 6.0, the default Leader was a comma
49 " pre 6.0, the default Leader was a comma
50 map ,add :!hg add %<CR><CR>
50 map ,add :!hg add %<CR><CR>
51 map ,fgt :!hg forget %<CR><CR>
51 map ,fgt :!hg forget %<CR><CR>
52 map ,diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
52 map ,diff :call ShowResults("FileDiff", "hg\ diff")<CR><CR>
53 map ,revert :!hg revert<CR>:e!<CR>
53 map ,revert :!hg revert<CR>:e!<CR>
54 map ,log :call ShowResults("FileLog", "hg\ log")<CR><CR>
54 map ,log :call ShowResults("FileLog", "hg\ log")<CR><CR>
55 map ,an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
55 map ,an :call ShowResults("annotate", "hg\ annotate")<CR><CR>
56 map ,stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
56 map ,stat :call ShowResults("RepoStatus", "hg\ status")<CR><CR>
57 map ,upd :!hg update<CR>:e!<CR>
57 map ,upd :!hg update<CR>:e!<CR>
58 map ,pull :!hg pull<CR>:e!<CR>
58 map ,pull :!hg pull<CR>:e!<CR>
59 endif
59 endif
60
60
61 " Section: Functions {{{1
61 " Section: Functions {{{1
62 " Show the log results of the current file with a revision control system.
62 " Show the log results of the current file with a revision control system.
63 function! ShowResults(bufferName, cmdName)
63 function! ShowResults(bufferName, cmdName)
64 " Modify the shortmess option:
64 " Modify the shortmess option:
65 " A don't give the "ATTENTION" message when an existing swap file is
65 " A don't give the "ATTENTION" message when an existing swap file is
66 " found.
66 " found.
67 set shortmess+=A
67 set shortmess+=A
68
68
69 " Get the name of the current buffer.
69 " Get the name of the current buffer.
70 let currentBuffer = bufname("%")
70 let currentBuffer = bufname("%")
71
71
72 " If a buffer with the name rlog exists, delete it.
72 " If a buffer with the name rlog exists, delete it.
73 if bufexists(a:bufferName)
73 if bufexists(a:bufferName)
74 execute 'bd! ' a:bufferName
74 execute 'bd! ' a:bufferName
75 endif
75 endif
76
76
77 " Create a new buffer.
77 " Create a new buffer.
78 execute 'new ' a:bufferName
78 execute 'new ' a:bufferName
79
79
80 " Execute the command.
80 " Execute the command.
81 execute 'r!' a:cmdName ' ' currentBuffer
81 execute 'r!' a:cmdName ' ' currentBuffer
82
82
83 " Make is so that the file can't be edited.
83 " Make is so that the file can't be edited.
84 setlocal nomodified
84 setlocal nomodified
85 setlocal nomodifiable
85 setlocal nomodifiable
86 setlocal readonly
86 setlocal readonly
87
87
88 " Go to the beginning of the buffer.
88 " Go to the beginning of the buffer.
89 execute "normal 1G"
89 execute "normal 1G"
90
90
91 " Restore the shortmess option.
91 " Restore the shortmess option.
92 set shortmess-=A
92 set shortmess-=A
93 endfunction
93 endfunction
@@ -1,112 +1,112 b''
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>Mercurial for Windows</title>
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 <style type="text/css">
6 <style type="text/css">
7 <!--
7 <!--
8 .indented
8 .indented
9 {
9 {
10 padding-left: 10pt;
10 padding-left: 10pt;
11 }
11 }
12 -->
12 -->
13 </style>
13 </style>
14 </head>
14 </head>
15
15
16 <body>
16 <body>
17 <h1>Mercurial for Windows</h1>
17 <h1>Mercurial for Windows</h1>
18
18
19 <p>Welcome to Mercurial for Windows!</p>
19 <p>Welcome to Mercurial for Windows!</p>
20
20
21 <p>Mercurial is a command-line application. You must run it from
21 <p>Mercurial is a command-line application. You must run it from
22 the Windows command prompt (or if you're hard core, a <a
22 the Windows command prompt (or if you're hard core, a <a
23 href="http://www.mingw.org/">MinGW</a> shell).</p>
23 href="http://www.mingw.org/">MinGW</a> shell).</p>
24
24
25 <p><div class="indented"><i>Note: the standard <a
25 <p><div class="indented"><i>Note: the standard <a
26 href="http://www.mingw.org/">MinGW</a> msys startup script uses
26 href="http://www.mingw.org/">MinGW</a> msys startup script uses
27 rxvt which has problems setting up standard input and output.
27 rxvt which has problems setting up standard input and output.
28 Running bash directly works correctly.</i></div>
28 Running bash directly works correctly.</i></div>
29
29
30 <p>For documentation, please visit the <a
30 <p>For documentation, please visit the <a
31 href="http://www.selenic.com/mercurial">Mercurial web site</a>.
31 href="http://www.selenic.com/mercurial">Mercurial web site</a>.
32 You can also download a free book, <a
32 You can also download a free book, <a
33 href="http://hgbook.red-bean.com/">Distributed revision control
33 href="http://hgbook.red-bean.com/">Distributed revision control
34 with Mercurial</a>.</p>
34 with Mercurial</a>.</p>
35
35
36 <p>By default, Mercurial installs to <tt>C:\Mercurial</tt>. The
36 <p>By default, Mercurial installs to <tt>C:\Mercurial</tt>. The
37 Mercurial command is called <tt>hg.exe</tt>.</p>
37 Mercurial command is called <tt>hg.exe</tt>.</p>
38
38
39 <h1>Testing Mercurial after you've installed it</h1>
39 <h1>Testing Mercurial after you've installed it</h1>
40
40
41 <p>The easiest way to check that Mercurial is installed properly is to
41 <p>The easiest way to check that Mercurial is installed properly is to
42 just type the following at the command prompt:</p>
42 just type the following at the command prompt:</p>
43
43
44 <pre>
44 <pre>
45 hg
45 hg
46 </pre>
46 </pre>
47
47
48 <p>This command should print a useful help message. If it does,
48 <p>This command should print a useful help message. If it does,
49 other Mercurial commands should work fine for you.</p>
49 other Mercurial commands should work fine for you.</p>
50
50
51 <h1>Configuration notes</h1>
51 <h1>Configuration notes</h1>
52 <h4>Default editor</h4>
52 <h4>Default editor</h4>
53 The default editor for commit messages is 'notepad'. You can set the EDITOR
53 The default editor for commit messages is 'notepad'. You can set the EDITOR
54 (or HGEDITOR) environment variable to specify your preference or set it in
54 (or HGEDITOR) environment variable to specify your preference or set it in
55 mercurial.ini:
55 mercurial.ini:
56 <pre>
56 <pre>
57 [ui]
57 [ui]
58 editor = whatever
58 editor = whatever
59 </pre>
59 </pre>
60
60
61 <h4>Configuring a Merge program</h4>
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 Merge at the file level, neither does it make any attempt to Resolve the conflicts.
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 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)
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 <h1>Reporting problems</h1>
68 <h1>Reporting problems</h1>
69
69
70 <p>Before you report any problems, please consult the <a
70 <p>Before you report any problems, please consult the <a
71 href="http://www.selenic.com/mercurial">Mercurial web site</a> and
71 href="http://www.selenic.com/mercurial">Mercurial web site</a> and
72 see if your question is already in our list of <a
72 see if your question is already in our list of <a
73 href="http://www.selenic.com/mercurial/wiki/index.cgi/FAQ">Frequently
73 href="http://www.selenic.com/mercurial/wiki/index.cgi/FAQ">Frequently
74 Answered Questions</a> (the "FAQ").
74 Answered Questions</a> (the "FAQ").
75
75
76 <p>If you cannot find an answer to your question, please feel
76 <p>If you cannot find an answer to your question, please feel
77 free to send mail to the Mercurial mailing list, at <a
77 free to send mail to the Mercurial mailing list, at <a
78 href="mailto:mercurial@selenic.com">mercurial@selenic.com</a>.
78 href="mailto:mercurial@selenic.com">mercurial@selenic.com</a>.
79 <b>Remember</b>, the more useful information you include in your
79 <b>Remember</b>, the more useful information you include in your
80 report, the easier it will be for us to help you!</p>
80 report, the easier it will be for us to help you!</p>
81
81
82 <p>If you are IRC-savvy, that's usually the fastest way to get
82 <p>If you are IRC-savvy, that's usually the fastest way to get
83 help. Go to <tt>#mercurial</tt> on
83 help. Go to <tt>#mercurial</tt> on
84 <tt>irc.freenode.net</tt>.</p>
84 <tt>irc.freenode.net</tt>.</p>
85
85
86 <h1>Author and copyright information</h1>
86 <h1>Author and copyright information</h1>
87
87
88 <p>Mercurial was written by <a href="http://www.selenic.com">Matt
88 <p>Mercurial was written by <a href="http://www.selenic.com">Matt
89 Mackall</a>, and is maintained by Matt and a team of
89 Mackall</a>, and is maintained by Matt and a team of
90 volunteers.</p>
90 volunteers.</p>
91
91
92 <p>The Windows installer was written by <a
92 <p>The Windows installer was written by <a
93 href="http://www.serpentine.com/blog">Bryan
93 href="http://www.serpentine.com/blog">Bryan
94 O'Sullivan</a>.</p>
94 O'Sullivan</a>.</p>
95
95
96 <p>Mercurial is Copyright 2005-2007 Matt Mackall and others.
96 <p>Mercurial is Copyright 2005-2007 Matt Mackall and others.
97 See the <tt>Contributors.txt</tt> file for a list of contributors.</p>
97 See the <tt>Contributors.txt</tt> file for a list of contributors.</p>
98
98
99 <p>Mercurial is free software; you can redistribute it and/or
99 <p>Mercurial is free software; you can redistribute it and/or
100 modify it under the terms of the <a
100 modify it under the terms of the <a
101 href="http://www.gnu.org/copyleft/gpl.html">GNU General Public
101 href="http://www.gnu.org/copyleft/gpl.html">GNU General Public
102 License</a> as published by the Free Software Foundation; either
102 License</a> as published by the Free Software Foundation; either
103 version 2 of the License, or (at your option) any later
103 version 2 of the License, or (at your option) any later
104 version.</p>
104 version.</p>
105
105
106 <p>Mercurial is distributed in the hope that it will be useful,
106 <p>Mercurial is distributed in the hope that it will be useful,
107 but <b>without any warranty</b>; without even the implied
107 but <b>without any warranty</b>; without even the implied
108 warranty of <b>merchantability</b> or <b>fitness for a
108 warranty of <b>merchantability</b> or <b>fitness for a
109 particular purpose</b>. See the GNU General Public License for
109 particular purpose</b>. See the GNU General Public License for
110 more details.</p>
110 more details.</p>
111 </body>
111 </body>
112 </html>
112 </html>
@@ -1,41 +1,41 b''
1 ; System-wide Mercurial config file. To override these settings on a
1 ; System-wide Mercurial config file. To override these settings on a
2 ; per-user basis, please edit the following file instead, where
2 ; per-user basis, please edit the following file instead, where
3 ; USERNAME is your Windows user name:
3 ; USERNAME is your Windows user name:
4 ; C:\Documents and Settings\USERNAME\Mercurial.ini
4 ; C:\Documents and Settings\USERNAME\Mercurial.ini
5
5
6 [ui]
6 [ui]
7 editor = notepad
7 editor = notepad
8
8
9 ; By default, we try to encode and decode all files that do not
9 ; By default, we try to encode and decode all files that do not
10 ; contain ASCII NUL characters. What this means is that we try to set
10 ; contain ASCII NUL characters. What this means is that we try to set
11 ; line endings to Windows style on update, and to Unix style on
11 ; line endings to Windows style on update, and to Unix style on
12 ; commit. This lets us cooperate with Linux and Unix users, so
12 ; commit. This lets us cooperate with Linux and Unix users, so
13 ; everybody sees files with their native line endings.
13 ; everybody sees files with their native line endings.
14
14
15 [extensions]
15 [extensions]
16 ; The win32text extension is available and installed by default. It
16 ; The win32text extension is available and installed by default. It
17 ; provides built-in Python hooks to perform line ending conversions.
17 ; provides built-in Python hooks to perform line ending conversions.
18 ; This is normally much faster than running an external program.
18 ; This is normally much faster than running an external program.
19 hgext.win32text =
19 hgext.win32text =
20
20
21
21
22 [encode]
22 [encode]
23 ; Encode files that don't contain NUL characters.
23 ; Encode files that don't contain NUL characters.
24
24
25 ; ** = cleverencode:
25 ; ** = cleverencode:
26
26
27 ; Alternatively, you can explicitly specify each file extension that
27 ; Alternatively, you can explicitly specify each file extension that
28 ; you want encoded (any you omit will be left untouched), like this:
28 ; you want encoded (any you omit will be left untouched), like this:
29
29
30 ; *.txt = dumbencode:
30 ; *.txt = dumbencode:
31
31
32
32
33 [decode]
33 [decode]
34 ; Decode files that don't contain NUL characters.
34 ; Decode files that don't contain NUL characters.
35
35
36 ; ** = cleverdecode:
36 ; ** = cleverdecode:
37
37
38 ; Alternatively, you can explicitly specify each file extension that
38 ; Alternatively, you can explicitly specify each file extension that
39 ; you want decoded (any you omit will be left untouched), like this:
39 ; you want decoded (any you omit will be left untouched), like this:
40
40
41 ; **.txt = dumbdecode:
41 ; **.txt = dumbdecode:
@@ -1,71 +1,71 b''
1 The standalone Windows installer for Mercurial is built in a somewhat
1 The standalone Windows installer for Mercurial is built in a somewhat
2 jury-rigged fashion.
2 jury-rigged fashion.
3
3
4 It has the following prerequisites, at least as I build it:
4 It has the following prerequisites, at least as I build it:
5
5
6 Python for Windows
6 Python for Windows
7 http://www.python.org/ftp/python/2.4.3/python-2.4.3.msi
7 http://www.python.org/ftp/python/2.4.3/python-2.4.3.msi
8
8
9 MinGW
9 MinGW
10 http://www.mingw.org/
10 http://www.mingw.org/
11
11
12 Python for Windows Extensions
12 Python for Windows Extensions
13 http://sourceforge.net/projects/pywin32/
13 http://sourceforge.net/projects/pywin32/
14
14
15 mfc71.dll (just download, don't install)
15 mfc71.dll (just download, don't install)
16 http://starship.python.net/crew/mhammond/win32/
16 http://starship.python.net/crew/mhammond/win32/
17
17
18 The py2exe distutils extension
18 The py2exe distutils extension
19 http://sourceforge.net/projects/py2exe/
19 http://sourceforge.net/projects/py2exe/
20
20
21 Inno Setup
21 Inno Setup
22 http://www.jrsoftware.org/isinfo.php
22 http://www.jrsoftware.org/isinfo.php
23
23
24 ISTool - optional
24 ISTool - optional
25 http://www.istool.org/default.aspx/
25 http://www.istool.org/default.aspx/
26
26
27 add_path (you need only add_path.exe in the zip file)
27 add_path (you need only add_path.exe in the zip file)
28 http://www.barisione.org/apps.html#add_path
28 http://www.barisione.org/apps.html#add_path
29
29
30 And, of course, Mercurial itself.
30 And, of course, Mercurial itself.
31
31
32 Once you have all this installed and built, clone a copy of the
32 Once you have all this installed and built, clone a copy of the
33 Mercurial repository you want to package, and name the repo
33 Mercurial repository you want to package, and name the repo
34 C:\hg\hg-release.
34 C:\hg\hg-release.
35
35
36 In a shell, build a standalone copy of the hg.exe program:
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 python setup.py py2exe -b 1
39 python setup.py py2exe -b 1
40
40
41 Note: the previously suggested combined command of "python setup.py build -c
41 Note: the previously suggested combined command of "python setup.py build -c
42 mingw32 py2exe -b 1" doesn't work correctly anymore as it doesn't include the
42 mingw32 py2exe -b 1" doesn't work correctly anymore as it doesn't include the
43 extensions in the mercurial subdirectory.
43 extensions in the mercurial subdirectory.
44
44
45 If you want to create a file named setup.cfg with the contents:
45 If you want to create a file named setup.cfg with the contents:
46
46
47 [build]
47 [build]
48 compiler=mingw32
48 compiler=mingw32
49
49
50 you can skip the first build step.
50 you can skip the first build step.
51
51
52 Copy mfc71.dll and add_path.exe into the dist directory that just got created.
52 Copy mfc71.dll and add_path.exe into the dist directory that just got created.
53
53
54 If you use ISTool, you open the C:\hg\hg-release\contrib\win32\mercurial.iss
54 If you use ISTool, you open the C:\hg\hg-release\contrib\win32\mercurial.iss
55 file and type Ctrl-F9 to compile the installer file.
55 file and type Ctrl-F9 to compile the installer file.
56
56
57 Otherwise you run the Inno Setup compiler. Assuming it's on the path you run:
57 Otherwise you run the Inno Setup compiler. Assuming it's on the path you run:
58
58
59 iscc contrib\win32\mercurial.iss
59 iscc contrib\win32\mercurial.iss
60
60
61 The actual installer will be in the C:\hg\hg-release\Output directory.
61 The actual installer will be in the C:\hg\hg-release\Output directory.
62
62
63 To automate the steps above you may want to create a batchfile based on the
63 To automate the steps above you may want to create a batchfile based on the
64 following:
64 following:
65
65
66 echo [build] > setup.cfg
66 echo [build] > setup.cfg
67 echo compiler=mingw32 >> setup.cfg
67 echo compiler=mingw32 >> setup.cfg
68 python setup.py py2exe -b 1
68 python setup.py py2exe -b 1
69 iscc contrib\win32\mercurial.iss
69 iscc contrib\win32\mercurial.iss
70
70
71 and run it from the root of the hg repository (c:\hg\hg-release).
71 and run it from the root of the hg repository (c:\hg\hg-release).
@@ -1,579 +1,579 b''
1 HGRC(5)
1 HGRC(5)
2 =======
2 =======
3 Bryan O'Sullivan <bos@serpentine.com>
3 Bryan O'Sullivan <bos@serpentine.com>
4
4
5 NAME
5 NAME
6 ----
6 ----
7 hgrc - configuration files for Mercurial
7 hgrc - configuration files for Mercurial
8
8
9 SYNOPSIS
9 SYNOPSIS
10 --------
10 --------
11
11
12 The Mercurial system uses a set of configuration files to control
12 The Mercurial system uses a set of configuration files to control
13 aspects of its behaviour.
13 aspects of its behaviour.
14
14
15 FILES
15 FILES
16 -----
16 -----
17
17
18 Mercurial reads configuration data from several files, if they exist.
18 Mercurial reads configuration data from several files, if they exist.
19 The names of these files depend on the system on which Mercurial is
19 The names of these files depend on the system on which Mercurial is
20 installed.
20 installed.
21
21
22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 (Unix) <install-root>/etc/mercurial/hgrc::
23 (Unix) <install-root>/etc/mercurial/hgrc::
24 Per-installation configuration files, searched for in the
24 Per-installation configuration files, searched for in the
25 directory where Mercurial is installed. For example, if installed
25 directory where Mercurial is installed. For example, if installed
26 in /shared/tools, Mercurial will look in
26 in /shared/tools, Mercurial will look in
27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 all Mercurial commands executed by any user in any directory.
28 all Mercurial commands executed by any user in any directory.
29
29
30 (Unix) /etc/mercurial/hgrc.d/*.rc::
30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 (Unix) /etc/mercurial/hgrc::
31 (Unix) /etc/mercurial/hgrc::
32 (Windows) C:\Mercurial\Mercurial.ini::
32 (Windows) C:\Mercurial\Mercurial.ini::
33 Per-system configuration files, for the system on which Mercurial
33 Per-system configuration files, for the system on which Mercurial
34 is running. Options in these files apply to all Mercurial
34 is running. Options in these files apply to all Mercurial
35 commands executed by any user in any directory. Options in these
35 commands executed by any user in any directory. Options in these
36 files override per-installation options.
36 files override per-installation options.
37
37
38 (Unix) $HOME/.hgrc::
38 (Unix) $HOME/.hgrc::
39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
40 (Windows) $HOME\Mercurial.ini::
40 (Windows) $HOME\Mercurial.ini::
41 Per-user configuration file, for the user running Mercurial.
41 Per-user configuration file, for the user running Mercurial.
42 Options in this file apply to all Mercurial commands executed by
42 Options in this file apply to all Mercurial commands executed by
43 any user in any directory. Options in this file override
43 any user in any directory. Options in this file override
44 per-installation and per-system options.
44 per-installation and per-system options.
45 On Windows system, one of these is chosen exclusively according
45 On Windows system, one of these is chosen exclusively according
46 to definition of HOME environment variable.
46 to definition of HOME environment variable.
47
47
48 (Unix, Windows) <repo>/.hg/hgrc::
48 (Unix, Windows) <repo>/.hg/hgrc::
49 Per-repository configuration options that only apply in a
49 Per-repository configuration options that only apply in a
50 particular repository. This file is not version-controlled, and
50 particular repository. This file is not version-controlled, and
51 will not get transferred during a "clone" operation. Options in
51 will not get transferred during a "clone" operation. Options in
52 this file override options in all other configuration files.
52 this file override options in all other configuration files.
53 On Unix, most of this file will be ignored if it doesn't belong
53 On Unix, most of this file will be ignored if it doesn't belong
54 to a trusted user or to a trusted group. See the documentation
54 to a trusted user or to a trusted group. See the documentation
55 for the trusted section below for more details.
55 for the trusted section below for more details.
56
56
57 SYNTAX
57 SYNTAX
58 ------
58 ------
59
59
60 A configuration file consists of sections, led by a "[section]" header
60 A configuration file consists of sections, led by a "[section]" header
61 and followed by "name: value" entries; "name=value" is also accepted.
61 and followed by "name: value" entries; "name=value" is also accepted.
62
62
63 [spam]
63 [spam]
64 eggs=ham
64 eggs=ham
65 green=
65 green=
66 eggs
66 eggs
67
67
68 Each line contains one entry. If the lines that follow are indented,
68 Each line contains one entry. If the lines that follow are indented,
69 they are treated as continuations of that entry.
69 they are treated as continuations of that entry.
70
70
71 Leading whitespace is removed from values. Empty lines are skipped.
71 Leading whitespace is removed from values. Empty lines are skipped.
72
72
73 The optional values can contain format strings which refer to other
73 The optional values can contain format strings which refer to other
74 values in the same section, or values in a special DEFAULT section.
74 values in the same section, or values in a special DEFAULT section.
75
75
76 Lines beginning with "#" or ";" are ignored and may be used to provide
76 Lines beginning with "#" or ";" are ignored and may be used to provide
77 comments.
77 comments.
78
78
79 SECTIONS
79 SECTIONS
80 --------
80 --------
81
81
82 This section describes the different sections that may appear in a
82 This section describes the different sections that may appear in a
83 Mercurial "hgrc" file, the purpose of each section, its possible
83 Mercurial "hgrc" file, the purpose of each section, its possible
84 keys, and their possible values.
84 keys, and their possible values.
85
85
86 decode/encode::
86 decode/encode::
87 Filters for transforming files on checkout/checkin. This would
87 Filters for transforming files on checkout/checkin. This would
88 typically be used for newline processing or other
88 typically be used for newline processing or other
89 localization/canonicalization of files.
89 localization/canonicalization of files.
90
90
91 Filters consist of a filter pattern followed by a filter command.
91 Filters consist of a filter pattern followed by a filter command.
92 Filter patterns are globs by default, rooted at the repository
92 Filter patterns are globs by default, rooted at the repository
93 root. For example, to match any file ending in ".txt" in the root
93 root. For example, to match any file ending in ".txt" in the root
94 directory only, use the pattern "*.txt". To match any file ending
94 directory only, use the pattern "*.txt". To match any file ending
95 in ".c" anywhere in the repository, use the pattern "**.c".
95 in ".c" anywhere in the repository, use the pattern "**.c".
96
96
97 The filter command can start with a specifier, either "pipe:" or
97 The filter command can start with a specifier, either "pipe:" or
98 "tempfile:". If no specifier is given, "pipe:" is used by default.
98 "tempfile:". If no specifier is given, "pipe:" is used by default.
99
99
100 A "pipe:" command must accept data on stdin and return the
100 A "pipe:" command must accept data on stdin and return the
101 transformed data on stdout.
101 transformed data on stdout.
102
102
103 Pipe example:
103 Pipe example:
104
104
105 [encode]
105 [encode]
106 # uncompress gzip files on checkin to improve delta compression
106 # uncompress gzip files on checkin to improve delta compression
107 # note: not necessarily a good idea, just an example
107 # note: not necessarily a good idea, just an example
108 *.gz = pipe: gunzip
108 *.gz = pipe: gunzip
109
109
110 [decode]
110 [decode]
111 # recompress gzip files when writing them to the working dir (we
111 # recompress gzip files when writing them to the working dir (we
112 # can safely omit "pipe:", because it's the default)
112 # can safely omit "pipe:", because it's the default)
113 *.gz = gzip
113 *.gz = gzip
114
114
115 A "tempfile:" command is a template. The string INFILE is replaced
115 A "tempfile:" command is a template. The string INFILE is replaced
116 with the name of a temporary file that contains the data to be
116 with the name of a temporary file that contains the data to be
117 filtered by the command. The string OUTFILE is replaced with the
117 filtered by the command. The string OUTFILE is replaced with the
118 name of an empty temporary file, where the filtered data must be
118 name of an empty temporary file, where the filtered data must be
119 written by the command.
119 written by the command.
120
120
121 NOTE: the tempfile mechanism is recommended for Windows systems,
121 NOTE: the tempfile mechanism is recommended for Windows systems,
122 where the standard shell I/O redirection operators often have
122 where the standard shell I/O redirection operators often have
123 strange effects. In particular, if you are doing line ending
123 strange effects. In particular, if you are doing line ending
124 conversion on Windows using the popular dos2unix and unix2dos
124 conversion on Windows using the popular dos2unix and unix2dos
125 programs, you *must* use the tempfile mechanism, as using pipes will
125 programs, you *must* use the tempfile mechanism, as using pipes will
126 corrupt the contents of your files.
126 corrupt the contents of your files.
127
127
128 Tempfile example:
128 Tempfile example:
129
129
130 [encode]
130 [encode]
131 # convert files to unix line ending conventions on checkin
131 # convert files to unix line ending conventions on checkin
132 **.txt = tempfile: dos2unix -n INFILE OUTFILE
132 **.txt = tempfile: dos2unix -n INFILE OUTFILE
133
133
134 [decode]
134 [decode]
135 # convert files to windows line ending conventions when writing
135 # convert files to windows line ending conventions when writing
136 # them to the working dir
136 # them to the working dir
137 **.txt = tempfile: unix2dos -n INFILE OUTFILE
137 **.txt = tempfile: unix2dos -n INFILE OUTFILE
138
138
139 defaults::
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 default options/arguments to pass to the specified commands.
141 default options/arguments to pass to the specified commands.
142
142
143 The following example makes 'hg log' run in verbose mode, and
143 The following example makes 'hg log' run in verbose mode, and
144 'hg status' show only the modified files, by default.
144 'hg status' show only the modified files, by default.
145
145
146 [defaults]
146 [defaults]
147 log = -v
147 log = -v
148 status = -m
148 status = -m
149
149
150 The actual commands, instead of their aliases, must be used when
150 The actual commands, instead of their aliases, must be used when
151 defining command defaults. The command defaults will also be
151 defining command defaults. The command defaults will also be
152 applied to the aliases of the commands defined.
152 applied to the aliases of the commands defined.
153
153
154 diff::
154 diff::
155 Settings used when displaying diffs. They are all boolean and
155 Settings used when displaying diffs. They are all boolean and
156 defaults to False.
156 defaults to False.
157 git;;
157 git;;
158 Use git extended diff format.
158 Use git extended diff format.
159 nodates;;
159 nodates;;
160 Don't include dates in diff headers.
160 Don't include dates in diff headers.
161 showfunc;;
161 showfunc;;
162 Show which function each change is in.
162 Show which function each change is in.
163 ignorews;;
163 ignorews;;
164 Ignore white space when comparing lines.
164 Ignore white space when comparing lines.
165 ignorewsamount;;
165 ignorewsamount;;
166 Ignore changes in the amount of white space.
166 Ignore changes in the amount of white space.
167 ignoreblanklines;;
167 ignoreblanklines;;
168 Ignore changes whose lines are all blank.
168 Ignore changes whose lines are all blank.
169
169
170 email::
170 email::
171 Settings for extensions that send email messages.
171 Settings for extensions that send email messages.
172 from;;
172 from;;
173 Optional. Email address to use in "From" header and SMTP envelope
173 Optional. Email address to use in "From" header and SMTP envelope
174 of outgoing messages.
174 of outgoing messages.
175 to;;
175 to;;
176 Optional. Comma-separated list of recipients' email addresses.
176 Optional. Comma-separated list of recipients' email addresses.
177 cc;;
177 cc;;
178 Optional. Comma-separated list of carbon copy recipients'
178 Optional. Comma-separated list of carbon copy recipients'
179 email addresses.
179 email addresses.
180 bcc;;
180 bcc;;
181 Optional. Comma-separated list of blind carbon copy
181 Optional. Comma-separated list of blind carbon copy
182 recipients' email addresses. Cannot be set interactively.
182 recipients' email addresses. Cannot be set interactively.
183 method;;
183 method;;
184 Optional. Method to use to send email messages. If value is
184 Optional. Method to use to send email messages. If value is
185 "smtp" (default), use SMTP (see section "[smtp]" for
185 "smtp" (default), use SMTP (see section "[smtp]" for
186 configuration). Otherwise, use as name of program to run that
186 configuration). Otherwise, use as name of program to run that
187 acts like sendmail (takes "-f" option for sender, list of
187 acts like sendmail (takes "-f" option for sender, list of
188 recipients on command line, message on stdin). Normally, setting
188 recipients on command line, message on stdin). Normally, setting
189 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
189 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
190 sendmail to send messages.
190 sendmail to send messages.
191
191
192 Email example:
192 Email example:
193
193
194 [email]
194 [email]
195 from = Joseph User <joe.user@example.com>
195 from = Joseph User <joe.user@example.com>
196 method = /usr/sbin/sendmail
196 method = /usr/sbin/sendmail
197
197
198 extensions::
198 extensions::
199 Mercurial has an extension mechanism for adding new features. To
199 Mercurial has an extension mechanism for adding new features. To
200 enable an extension, create an entry for it in this section.
200 enable an extension, create an entry for it in this section.
201
201
202 If you know that the extension is already in Python's search path,
202 If you know that the extension is already in Python's search path,
203 you can give the name of the module, followed by "=", with nothing
203 you can give the name of the module, followed by "=", with nothing
204 after the "=".
204 after the "=".
205
205
206 Otherwise, give a name that you choose, followed by "=", followed by
206 Otherwise, give a name that you choose, followed by "=", followed by
207 the path to the ".py" file (including the file name extension) that
207 the path to the ".py" file (including the file name extension) that
208 defines the extension.
208 defines the extension.
209
209
210 Example for ~/.hgrc:
210 Example for ~/.hgrc:
211
211
212 [extensions]
212 [extensions]
213 # (the mq extension will get loaded from mercurial's path)
213 # (the mq extension will get loaded from mercurial's path)
214 hgext.mq =
214 hgext.mq =
215 # (this extension will get loaded from the file specified)
215 # (this extension will get loaded from the file specified)
216 myfeature = ~/.hgext/myfeature.py
216 myfeature = ~/.hgext/myfeature.py
217
217
218 format::
218 format::
219
219
220 usestore;;
220 usestore;;
221 Enable or disable the "store" repository format which improves
221 Enable or disable the "store" repository format which improves
222 compatibility with systems that fold case or otherwise mangle
222 compatibility with systems that fold case or otherwise mangle
223 filenames. Enabled by default. Disabling this option will allow
223 filenames. Enabled by default. Disabling this option will allow
224 you to store longer filenames in some situations at the expense of
224 you to store longer filenames in some situations at the expense of
225 compatibility.
225 compatibility.
226
226
227 hooks::
227 hooks::
228 Commands or Python functions that get automatically executed by
228 Commands or Python functions that get automatically executed by
229 various actions such as starting or finishing a commit. Multiple
229 various actions such as starting or finishing a commit. Multiple
230 hooks can be run for the same action by appending a suffix to the
230 hooks can be run for the same action by appending a suffix to the
231 action. Overriding a site-wide hook can be done by changing its
231 action. Overriding a site-wide hook can be done by changing its
232 value or setting it to an empty string.
232 value or setting it to an empty string.
233
233
234 Example .hg/hgrc:
234 Example .hg/hgrc:
235
235
236 [hooks]
236 [hooks]
237 # do not use the site-wide hook
237 # do not use the site-wide hook
238 incoming =
238 incoming =
239 incoming.email = /my/email/hook
239 incoming.email = /my/email/hook
240 incoming.autobuild = /my/build/hook
240 incoming.autobuild = /my/build/hook
241
241
242 Most hooks are run with environment variables set that give added
242 Most hooks are run with environment variables set that give added
243 useful information. For each hook below, the environment variables
243 useful information. For each hook below, the environment variables
244 it is passed are listed with names of the form "$HG_foo".
244 it is passed are listed with names of the form "$HG_foo".
245
245
246 changegroup;;
246 changegroup;;
247 Run after a changegroup has been added via push, pull or
247 Run after a changegroup has been added via push, pull or
248 unbundle. ID of the first new changeset is in $HG_NODE. URL from
248 unbundle. ID of the first new changeset is in $HG_NODE. URL from
249 which changes came is in $HG_URL.
249 which changes came is in $HG_URL.
250 commit;;
250 commit;;
251 Run after a changeset has been created in the local repository.
251 Run after a changeset has been created in the local repository.
252 ID of the newly created changeset is in $HG_NODE. Parent
252 ID of the newly created changeset is in $HG_NODE. Parent
253 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
253 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
254 incoming;;
254 incoming;;
255 Run after a changeset has been pulled, pushed, or unbundled into
255 Run after a changeset has been pulled, pushed, or unbundled into
256 the local repository. The ID of the newly arrived changeset is in
256 the local repository. The ID of the newly arrived changeset is in
257 $HG_NODE. URL that was source of changes came is in $HG_URL.
257 $HG_NODE. URL that was source of changes came is in $HG_URL.
258 outgoing;;
258 outgoing;;
259 Run after sending changes from local repository to another. ID of
259 Run after sending changes from local repository to another. ID of
260 first changeset sent is in $HG_NODE. Source of operation is in
260 first changeset sent is in $HG_NODE. Source of operation is in
261 $HG_SOURCE; see "preoutgoing" hook for description.
261 $HG_SOURCE; see "preoutgoing" hook for description.
262 post-<command>;;
262 post-<command>;;
263 Run after successful invocations of the associated command. The
263 Run after successful invocations of the associated command. The
264 contents of the command line are passed as $HG_ARGS and the result
264 contents of the command line are passed as $HG_ARGS and the result
265 code in $HG_RESULT. Hook failure is ignored.
265 code in $HG_RESULT. Hook failure is ignored.
266 pre-<command>;;
266 pre-<command>;;
267 Run before executing the associated command. The contents of the
267 Run before executing the associated command. The contents of the
268 command line are passed as $HG_ARGS. If the hook returns failure,
268 command line are passed as $HG_ARGS. If the hook returns failure,
269 the command doesn't execute and Mercurial returns the failure code.
269 the command doesn't execute and Mercurial returns the failure code.
270 prechangegroup;;
270 prechangegroup;;
271 Run before a changegroup is added via push, pull or unbundle.
271 Run before a changegroup is added via push, pull or unbundle.
272 Exit status 0 allows the changegroup to proceed. Non-zero status
272 Exit status 0 allows the changegroup to proceed. Non-zero status
273 will cause the push, pull or unbundle to fail. URL from which
273 will cause the push, pull or unbundle to fail. URL from which
274 changes will come is in $HG_URL.
274 changes will come is in $HG_URL.
275 precommit;;
275 precommit;;
276 Run before starting a local commit. Exit status 0 allows the
276 Run before starting a local commit. Exit status 0 allows the
277 commit to proceed. Non-zero status will cause the commit to fail.
277 commit to proceed. Non-zero status will cause the commit to fail.
278 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
278 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
279 preoutgoing;;
279 preoutgoing;;
280 Run before computing changes to send from the local repository to
280 Run before computing changes to send from the local repository to
281 another. Non-zero status will cause failure. This lets you
281 another. Non-zero status will cause failure. This lets you
282 prevent pull over http or ssh. Also prevents against local pull,
282 prevent pull over http or ssh. Also prevents against local pull,
283 push (outbound) or bundle commands, but not effective, since you
283 push (outbound) or bundle commands, but not effective, since you
284 can just copy files instead then. Source of operation is in
284 can just copy files instead then. Source of operation is in
285 $HG_SOURCE. If "serve", operation is happening on behalf of
285 $HG_SOURCE. If "serve", operation is happening on behalf of
286 remote ssh or http repository. If "push", "pull" or "bundle",
286 remote ssh or http repository. If "push", "pull" or "bundle",
287 operation is happening on behalf of repository on same system.
287 operation is happening on behalf of repository on same system.
288 pretag;;
288 pretag;;
289 Run before creating a tag. Exit status 0 allows the tag to be
289 Run before creating a tag. Exit status 0 allows the tag to be
290 created. Non-zero status will cause the tag to fail. ID of
290 created. Non-zero status will cause the tag to fail. ID of
291 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
291 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
292 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
292 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
293 pretxnchangegroup;;
293 pretxnchangegroup;;
294 Run after a changegroup has been added via push, pull or unbundle,
294 Run after a changegroup has been added via push, pull or unbundle,
295 but before the transaction has been committed. Changegroup is
295 but before the transaction has been committed. Changegroup is
296 visible to hook program. This lets you validate incoming changes
296 visible to hook program. This lets you validate incoming changes
297 before accepting them. Passed the ID of the first new changeset
297 before accepting them. Passed the ID of the first new changeset
298 in $HG_NODE. Exit status 0 allows the transaction to commit.
298 in $HG_NODE. Exit status 0 allows the transaction to commit.
299 Non-zero status will cause the transaction to be rolled back and
299 Non-zero status will cause the transaction to be rolled back and
300 the push, pull or unbundle will fail. URL that was source of
300 the push, pull or unbundle will fail. URL that was source of
301 changes is in $HG_URL.
301 changes is in $HG_URL.
302 pretxncommit;;
302 pretxncommit;;
303 Run after a changeset has been created but the transaction not yet
303 Run after a changeset has been created but the transaction not yet
304 committed. Changeset is visible to hook program. This lets you
304 committed. Changeset is visible to hook program. This lets you
305 validate commit message and changes. Exit status 0 allows the
305 validate commit message and changes. Exit status 0 allows the
306 commit to proceed. Non-zero status will cause the transaction to
306 commit to proceed. Non-zero status will cause the transaction to
307 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
307 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
308 IDs are in $HG_PARENT1 and $HG_PARENT2.
308 IDs are in $HG_PARENT1 and $HG_PARENT2.
309 preupdate;;
309 preupdate;;
310 Run before updating the working directory. Exit status 0 allows
310 Run before updating the working directory. Exit status 0 allows
311 the update to proceed. Non-zero status will prevent the update.
311 the update to proceed. Non-zero status will prevent the update.
312 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
312 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
313 of second new parent is in $HG_PARENT2.
313 of second new parent is in $HG_PARENT2.
314 tag;;
314 tag;;
315 Run after a tag is created. ID of tagged changeset is in
315 Run after a tag is created. ID of tagged changeset is in
316 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
316 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
317 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
317 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
318 update;;
318 update;;
319 Run after updating the working directory. Changeset ID of first
319 Run after updating the working directory. Changeset ID of first
320 new parent is in $HG_PARENT1. If merge, ID of second new parent
320 new parent is in $HG_PARENT1. If merge, ID of second new parent
321 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
321 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
322 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
322 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
323
323
324 Note: it is generally better to use standard hooks rather than the
324 Note: it is generally better to use standard hooks rather than the
325 generic pre- and post- command hooks as they are guaranteed to be
325 generic pre- and post- command hooks as they are guaranteed to be
326 called in the appropriate contexts for influencing transactions.
326 called in the appropriate contexts for influencing transactions.
327 Also, hooks like "commit" will be called in all contexts that
327 Also, hooks like "commit" will be called in all contexts that
328 generate a commit (eg. tag) and not just the commit command.
328 generate a commit (eg. tag) and not just the commit command.
329
329
330 Note2: Environment variables with empty values may not be passed to
330 Note2: Environment variables with empty values may not be passed to
331 hooks on platforms like Windows. For instance, $HG_PARENT2 will
331 hooks on platforms like Windows. For instance, $HG_PARENT2 will
332 not be available under Windows for non-merge changesets while being
332 not be available under Windows for non-merge changesets while being
333 set to an empty value under Unix-like systems.
333 set to an empty value under Unix-like systems.
334
334
335 The syntax for Python hooks is as follows:
335 The syntax for Python hooks is as follows:
336
336
337 hookname = python:modulename.submodule.callable
337 hookname = python:modulename.submodule.callable
338
338
339 Python hooks are run within the Mercurial process. Each hook is
339 Python hooks are run within the Mercurial process. Each hook is
340 called with at least three keyword arguments: a ui object (keyword
340 called with at least three keyword arguments: a ui object (keyword
341 "ui"), a repository object (keyword "repo"), and a "hooktype"
341 "ui"), a repository object (keyword "repo"), and a "hooktype"
342 keyword that tells what kind of hook is used. Arguments listed as
342 keyword that tells what kind of hook is used. Arguments listed as
343 environment variables above are passed as keyword arguments, with no
343 environment variables above are passed as keyword arguments, with no
344 "HG_" prefix, and names in lower case.
344 "HG_" prefix, and names in lower case.
345
345
346 If a Python hook returns a "true" value or raises an exception, this
346 If a Python hook returns a "true" value or raises an exception, this
347 is treated as failure of the hook.
347 is treated as failure of the hook.
348
348
349 http_proxy::
349 http_proxy::
350 Used to access web-based Mercurial repositories through a HTTP
350 Used to access web-based Mercurial repositories through a HTTP
351 proxy.
351 proxy.
352 host;;
352 host;;
353 Host name and (optional) port of the proxy server, for example
353 Host name and (optional) port of the proxy server, for example
354 "myproxy:8000".
354 "myproxy:8000".
355 no;;
355 no;;
356 Optional. Comma-separated list of host names that should bypass
356 Optional. Comma-separated list of host names that should bypass
357 the proxy.
357 the proxy.
358 passwd;;
358 passwd;;
359 Optional. Password to authenticate with at the proxy server.
359 Optional. Password to authenticate with at the proxy server.
360 user;;
360 user;;
361 Optional. User name to authenticate with at the proxy server.
361 Optional. User name to authenticate with at the proxy server.
362
362
363 smtp::
363 smtp::
364 Configuration for extensions that need to send email messages.
364 Configuration for extensions that need to send email messages.
365 host;;
365 host;;
366 Host name of mail server, e.g. "mail.example.com".
366 Host name of mail server, e.g. "mail.example.com".
367 port;;
367 port;;
368 Optional. Port to connect to on mail server. Default: 25.
368 Optional. Port to connect to on mail server. Default: 25.
369 tls;;
369 tls;;
370 Optional. Whether to connect to mail server using TLS. True or
370 Optional. Whether to connect to mail server using TLS. True or
371 False. Default: False.
371 False. Default: False.
372 username;;
372 username;;
373 Optional. User name to authenticate to SMTP server with.
373 Optional. User name to authenticate to SMTP server with.
374 If username is specified, password must also be specified.
374 If username is specified, password must also be specified.
375 Default: none.
375 Default: none.
376 password;;
376 password;;
377 Optional. Password to authenticate to SMTP server with.
377 Optional. Password to authenticate to SMTP server with.
378 If username is specified, password must also be specified.
378 If username is specified, password must also be specified.
379 Default: none.
379 Default: none.
380 local_hostname;;
380 local_hostname;;
381 Optional. It's the hostname that the sender can use to identify itself
381 Optional. It's the hostname that the sender can use to identify itself
382 to the MTA.
382 to the MTA.
383
383
384 paths::
384 paths::
385 Assigns symbolic names to repositories. The left side is the
385 Assigns symbolic names to repositories. The left side is the
386 symbolic name, and the right gives the directory or URL that is the
386 symbolic name, and the right gives the directory or URL that is the
387 location of the repository. Default paths can be declared by
387 location of the repository. Default paths can be declared by
388 setting the following entries.
388 setting the following entries.
389 default;;
389 default;;
390 Directory or URL to use when pulling if no source is specified.
390 Directory or URL to use when pulling if no source is specified.
391 Default is set to repository from which the current repository
391 Default is set to repository from which the current repository
392 was cloned.
392 was cloned.
393 default-push;;
393 default-push;;
394 Optional. Directory or URL to use when pushing if no destination
394 Optional. Directory or URL to use when pushing if no destination
395 is specified.
395 is specified.
396
396
397 server::
397 server::
398 Controls generic server settings.
398 Controls generic server settings.
399 uncompressed;;
399 uncompressed;;
400 Whether to allow clients to clone a repo using the uncompressed
400 Whether to allow clients to clone a repo using the uncompressed
401 streaming protocol. This transfers about 40% more data than a
401 streaming protocol. This transfers about 40% more data than a
402 regular clone, but uses less memory and CPU on both server and
402 regular clone, but uses less memory and CPU on both server and
403 client. Over a LAN (100Mbps or better) or a very fast WAN, an
403 client. Over a LAN (100Mbps or better) or a very fast WAN, an
404 uncompressed streaming clone is a lot faster (~10x) than a regular
404 uncompressed streaming clone is a lot faster (~10x) than a regular
405 clone. Over most WAN connections (anything slower than about
405 clone. Over most WAN connections (anything slower than about
406 6Mbps), uncompressed streaming is slower, because of the extra
406 6Mbps), uncompressed streaming is slower, because of the extra
407 data transfer overhead. Default is False.
407 data transfer overhead. Default is False.
408
408
409 trusted::
409 trusted::
410 For security reasons, Mercurial will not use the settings in
410 For security reasons, Mercurial will not use the settings in
411 the .hg/hgrc file from a repository if it doesn't belong to a
411 the .hg/hgrc file from a repository if it doesn't belong to a
412 trusted user or to a trusted group. The main exception is the
412 trusted user or to a trusted group. The main exception is the
413 web interface, which automatically uses some safe settings, since
413 web interface, which automatically uses some safe settings, since
414 it's common to serve repositories from different users.
414 it's common to serve repositories from different users.
415
415
416 This section specifies what users and groups are trusted. The
416 This section specifies what users and groups are trusted. The
417 current user is always trusted. To trust everybody, list a user
417 current user is always trusted. To trust everybody, list a user
418 or a group with name "*".
418 or a group with name "*".
419
419
420 users;;
420 users;;
421 Comma-separated list of trusted users.
421 Comma-separated list of trusted users.
422 groups;;
422 groups;;
423 Comma-separated list of trusted groups.
423 Comma-separated list of trusted groups.
424
424
425 ui::
425 ui::
426 User interface controls.
426 User interface controls.
427 debug;;
427 debug;;
428 Print debugging information. True or False. Default is False.
428 Print debugging information. True or False. Default is False.
429 editor;;
429 editor;;
430 The editor to use during a commit. Default is $EDITOR or "vi".
430 The editor to use during a commit. Default is $EDITOR or "vi".
431 fallbackencoding;;
431 fallbackencoding;;
432 Encoding to try if it's not possible to decode the changelog using
432 Encoding to try if it's not possible to decode the changelog using
433 UTF-8. Default is ISO-8859-1.
433 UTF-8. Default is ISO-8859-1.
434 ignore;;
434 ignore;;
435 A file to read per-user ignore patterns from. This file should be in
435 A file to read per-user ignore patterns from. This file should be in
436 the same format as a repository-wide .hgignore file. This option
436 the same format as a repository-wide .hgignore file. This option
437 supports hook syntax, so if you want to specify multiple ignore
437 supports hook syntax, so if you want to specify multiple ignore
438 files, you can do so by setting something like
438 files, you can do so by setting something like
439 "ignore.other = ~/.hgignore2". For details of the ignore file
439 "ignore.other = ~/.hgignore2". For details of the ignore file
440 format, see the hgignore(5) man page.
440 format, see the hgignore(5) man page.
441 interactive;;
441 interactive;;
442 Allow to prompt the user. True or False. Default is True.
442 Allow to prompt the user. True or False. Default is True.
443 logtemplate;;
443 logtemplate;;
444 Template string for commands that print changesets.
444 Template string for commands that print changesets.
445 merge;;
445 merge;;
446 The conflict resolution program to use during a manual merge.
446 The conflict resolution program to use during a manual merge.
447 Default is "hgmerge".
447 Default is "hgmerge".
448 patch;;
448 patch;;
449 command to use to apply patches. Look for 'gpatch' or 'patch' in PATH if
449 command to use to apply patches. Look for 'gpatch' or 'patch' in PATH if
450 unset.
450 unset.
451 quiet;;
451 quiet;;
452 Reduce the amount of output printed. True or False. Default is False.
452 Reduce the amount of output printed. True or False. Default is False.
453 remotecmd;;
453 remotecmd;;
454 remote command to use for clone/push/pull operations. Default is 'hg'.
454 remote command to use for clone/push/pull operations. Default is 'hg'.
455 report_untrusted;;
455 report_untrusted;;
456 Warn if a .hg/hgrc file is ignored due to not being owned by a
456 Warn if a .hg/hgrc file is ignored due to not being owned by a
457 trusted user or group. True or False. Default is True.
457 trusted user or group. True or False. Default is True.
458 slash;;
458 slash;;
459 Display paths using a slash ("/") as the path separator. This only
459 Display paths using a slash ("/") as the path separator. This only
460 makes a difference on systems where the default path separator is not
460 makes a difference on systems where the default path separator is not
461 the slash character (e.g. Windows uses the backslash character ("\")).
461 the slash character (e.g. Windows uses the backslash character ("\")).
462 Default is False.
462 Default is False.
463 ssh;;
463 ssh;;
464 command to use for SSH connections. Default is 'ssh'.
464 command to use for SSH connections. Default is 'ssh'.
465 strict;;
465 strict;;
466 Require exact command names, instead of allowing unambiguous
466 Require exact command names, instead of allowing unambiguous
467 abbreviations. True or False. Default is False.
467 abbreviations. True or False. Default is False.
468 style;;
468 style;;
469 Name of style to use for command output.
469 Name of style to use for command output.
470 timeout;;
470 timeout;;
471 The timeout used when a lock is held (in seconds), a negative value
471 The timeout used when a lock is held (in seconds), a negative value
472 means no timeout. Default is 600.
472 means no timeout. Default is 600.
473 username;;
473 username;;
474 The committer of a changeset created when running "commit".
474 The committer of a changeset created when running "commit".
475 Typically a person's name and email address, e.g. "Fred Widget
475 Typically a person's name and email address, e.g. "Fred Widget
476 <fred@example.com>". Default is $EMAIL or username@hostname.
476 <fred@example.com>". Default is $EMAIL or username@hostname.
477 If the username in hgrc is empty, it has to be specified manually or
477 If the username in hgrc is empty, it has to be specified manually or
478 in a different hgrc file (e.g. $HOME/.hgrc, if the admin set "username ="
478 in a different hgrc file (e.g. $HOME/.hgrc, if the admin set "username ="
479 in the system hgrc).
479 in the system hgrc).
480 verbose;;
480 verbose;;
481 Increase the amount of output printed. True or False. Default is False.
481 Increase the amount of output printed. True or False. Default is False.
482
482
483
483
484 web::
484 web::
485 Web interface configuration.
485 Web interface configuration.
486 accesslog;;
486 accesslog;;
487 Where to output the access log. Default is stdout.
487 Where to output the access log. Default is stdout.
488 address;;
488 address;;
489 Interface address to bind to. Default is all.
489 Interface address to bind to. Default is all.
490 allow_archive;;
490 allow_archive;;
491 List of archive format (bz2, gz, zip) allowed for downloading.
491 List of archive format (bz2, gz, zip) allowed for downloading.
492 Default is empty.
492 Default is empty.
493 allowbz2;;
493 allowbz2;;
494 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
494 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
495 Default is false.
495 Default is false.
496 allowgz;;
496 allowgz;;
497 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
497 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
498 Default is false.
498 Default is false.
499 allowpull;;
499 allowpull;;
500 Whether to allow pulling from the repository. Default is true.
500 Whether to allow pulling from the repository. Default is true.
501 allow_push;;
501 allow_push;;
502 Whether to allow pushing to the repository. If empty or not set,
502 Whether to allow pushing to the repository. If empty or not set,
503 push is not allowed. If the special value "*", any remote user
503 push is not allowed. If the special value "*", any remote user
504 can push, including unauthenticated users. Otherwise, the remote
504 can push, including unauthenticated users. Otherwise, the remote
505 user must have been authenticated, and the authenticated user name
505 user must have been authenticated, and the authenticated user name
506 must be present in this list (separated by whitespace or ",").
506 must be present in this list (separated by whitespace or ",").
507 The contents of the allow_push list are examined after the
507 The contents of the allow_push list are examined after the
508 deny_push list.
508 deny_push list.
509 allowzip;;
509 allowzip;;
510 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
510 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
511 Default is false. This feature creates temporary files.
511 Default is false. This feature creates temporary files.
512 baseurl;;
512 baseurl;;
513 Base URL to use when publishing URLs in other locations, so
513 Base URL to use when publishing URLs in other locations, so
514 third-party tools like email notification hooks can construct URLs.
514 third-party tools like email notification hooks can construct URLs.
515 Example: "http://hgserver/repos/"
515 Example: "http://hgserver/repos/"
516 contact;;
516 contact;;
517 Name or email address of the person in charge of the repository.
517 Name or email address of the person in charge of the repository.
518 Default is "unknown".
518 Default is "unknown".
519 deny_push;;
519 deny_push;;
520 Whether to deny pushing to the repository. If empty or not set,
520 Whether to deny pushing to the repository. If empty or not set,
521 push is not denied. If the special value "*", all remote users
521 push is not denied. If the special value "*", all remote users
522 are denied push. Otherwise, unauthenticated users are all denied,
522 are denied push. Otherwise, unauthenticated users are all denied,
523 and any authenticated user name present in this list (separated by
523 and any authenticated user name present in this list (separated by
524 whitespace or ",") is also denied. The contents of the deny_push
524 whitespace or ",") is also denied. The contents of the deny_push
525 list are examined before the allow_push list.
525 list are examined before the allow_push list.
526 description;;
526 description;;
527 Textual description of the repository's purpose or contents.
527 Textual description of the repository's purpose or contents.
528 Default is "unknown".
528 Default is "unknown".
529 encoding;;
529 encoding;;
530 Character encoding name.
530 Character encoding name.
531 Example: "UTF-8"
531 Example: "UTF-8"
532 errorlog;;
532 errorlog;;
533 Where to output the error log. Default is stderr.
533 Where to output the error log. Default is stderr.
534 hidden;;
534 hidden;;
535 Whether to hide the repository in the hgwebdir index. Default is false.
535 Whether to hide the repository in the hgwebdir index. Default is false.
536 ipv6;;
536 ipv6;;
537 Whether to use IPv6. Default is false.
537 Whether to use IPv6. Default is false.
538 name;;
538 name;;
539 Repository name to use in the web interface. Default is current
539 Repository name to use in the web interface. Default is current
540 working directory.
540 working directory.
541 maxchanges;;
541 maxchanges;;
542 Maximum number of changes to list on the changelog. Default is 10.
542 Maximum number of changes to list on the changelog. Default is 10.
543 maxfiles;;
543 maxfiles;;
544 Maximum number of files to list per changeset. Default is 10.
544 Maximum number of files to list per changeset. Default is 10.
545 port;;
545 port;;
546 Port to listen on. Default is 8000.
546 Port to listen on. Default is 8000.
547 push_ssl;;
547 push_ssl;;
548 Whether to require that inbound pushes be transported over SSL to
548 Whether to require that inbound pushes be transported over SSL to
549 prevent password sniffing. Default is true.
549 prevent password sniffing. Default is true.
550 staticurl;;
550 staticurl;;
551 Base URL to use for static files. If unset, static files (e.g.
551 Base URL to use for static files. If unset, static files (e.g.
552 the hgicon.png favicon) will be served by the CGI script itself.
552 the hgicon.png favicon) will be served by the CGI script itself.
553 Use this setting to serve them directly with the HTTP server.
553 Use this setting to serve them directly with the HTTP server.
554 Example: "http://hgserver/static/"
554 Example: "http://hgserver/static/"
555 stripes;;
555 stripes;;
556 How many lines a "zebra stripe" should span in multiline output.
556 How many lines a "zebra stripe" should span in multiline output.
557 Default is 1; set to 0 to disable.
557 Default is 1; set to 0 to disable.
558 style;;
558 style;;
559 Which template map style to use.
559 Which template map style to use.
560 templates;;
560 templates;;
561 Where to find the HTML templates. Default is install path.
561 Where to find the HTML templates. Default is install path.
562
562
563
563
564 AUTHOR
564 AUTHOR
565 ------
565 ------
566 Bryan O'Sullivan <bos@serpentine.com>.
566 Bryan O'Sullivan <bos@serpentine.com>.
567
567
568 Mercurial was written by Matt Mackall <mpm@selenic.com>.
568 Mercurial was written by Matt Mackall <mpm@selenic.com>.
569
569
570 SEE ALSO
570 SEE ALSO
571 --------
571 --------
572 hg(1), hgignore(5)
572 hg(1), hgignore(5)
573
573
574 COPYING
574 COPYING
575 -------
575 -------
576 This manual page is copyright 2005 Bryan O'Sullivan.
576 This manual page is copyright 2005 Bryan O'Sullivan.
577 Mercurial is copyright 2005-2007 Matt Mackall.
577 Mercurial is copyright 2005-2007 Matt Mackall.
578 Free use of this software is granted under the terms of the GNU General
578 Free use of this software is granted under the terms of the GNU General
579 Public License (GPL).
579 Public License (GPL).
@@ -1,583 +1,583 b''
1 #
1 #
2 # docbook.conf
2 # docbook.conf
3 #
3 #
4 # Asciidoc configuration file.
4 # Asciidoc configuration file.
5 # Modified docbook backend for Japanese.
5 # Modified docbook backend for Japanese.
6 #
6 #
7
7
8 [miscellaneous]
8 [miscellaneous]
9 outfilesuffix=.xml
9 outfilesuffix=.xml
10 # Printable page width in pts.
10 # Printable page width in pts.
11 pagewidth=380
11 pagewidth=380
12 pageunits=pt
12 pageunits=pt
13
13
14 [attributes]
14 [attributes]
15 basebackend=docbook
15 basebackend=docbook
16 basebackend-docbook=
16 basebackend-docbook=
17
17
18 [replacements]
18 [replacements]
19 # Line break markup is dropped (there is no DocBook line break tag).
19 # Line break markup is dropped (there is no DocBook line break tag).
20 (?m)^(.*)\s\+$=\1
20 (?m)^(.*)\s\+$=\1
21 # Superscripts.
21 # Superscripts.
22 \^(.+?)\^=<superscript>\1</superscript>
22 \^(.+?)\^=<superscript>\1</superscript>
23 # Subscripts.
23 # Subscripts.
24 ~(.+?)~=<subscript>\1</subscript>
24 ~(.+?)~=<subscript>\1</subscript>
25
25
26 [ruler-blockmacro]
26 [ruler-blockmacro]
27 # Only applies to HTML so don't output anything.
27 # Only applies to HTML so don't output anything.
28
28
29 [image-inlinemacro]
29 [image-inlinemacro]
30 <inlinemediaobject>
30 <inlinemediaobject>
31 <imageobject>
31 <imageobject>
32 <imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
32 <imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
33 </imageobject>
33 </imageobject>
34 <textobject><phrase>{1={target}}</phrase></textobject>
34 <textobject><phrase>{1={target}}</phrase></textobject>
35 </inlinemediaobject>
35 </inlinemediaobject>
36
36
37 [image-blockmacro]
37 [image-blockmacro]
38 <figure{id? id="{id}"}><title>{title}</title>
38 <figure{id? id="{id}"}><title>{title}</title>
39 {title%}<informalfigure{id? id="{id}"}>
39 {title%}<informalfigure{id? id="{id}"}>
40 <mediaobject>
40 <mediaobject>
41 <imageobject>
41 <imageobject>
42 <imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
42 <imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
43 </imageobject>
43 </imageobject>
44 <textobject><phrase>{1={target}}</phrase></textobject>
44 <textobject><phrase>{1={target}}</phrase></textobject>
45 </mediaobject>
45 </mediaobject>
46 {title#}</figure>
46 {title#}</figure>
47 {title%}</informalfigure>
47 {title%}</informalfigure>
48
48
49 [indexterm-inlinemacro]
49 [indexterm-inlinemacro]
50 # Inline index term.
50 # Inline index term.
51 # Generate separate index entries for primary, secondary and tertiary
51 # Generate separate index entries for primary, secondary and tertiary
52 # descriptions.
52 # descriptions.
53 # Primary only.
53 # Primary only.
54 {2%}<indexterm>
54 {2%}<indexterm>
55 {2%} <primary>{1}</primary>
55 {2%} <primary>{1}</primary>
56 {2%}</indexterm>
56 {2%}</indexterm>
57 # Primary and secondary.
57 # Primary and secondary.
58 {2#}{3%}<indexterm>
58 {2#}{3%}<indexterm>
59 {2#}{3%} <primary>{1}</primary><secondary>{2}</secondary>
59 {2#}{3%} <primary>{1}</primary><secondary>{2}</secondary>
60 {2#}{3%}</indexterm>
60 {2#}{3%}</indexterm>
61 {2#}{3%}<indexterm>
61 {2#}{3%}<indexterm>
62 {2#}{3%} <primary>{2}</primary>
62 {2#}{3%} <primary>{2}</primary>
63 {2#}{3%}</indexterm>
63 {2#}{3%}</indexterm>
64 # Primary, secondary and tertiary.
64 # Primary, secondary and tertiary.
65 {3#}<indexterm>
65 {3#}<indexterm>
66 <primary>{1}</primary><secondary>{2}</secondary><tertiary>{3}</tertiary>
66 <primary>{1}</primary><secondary>{2}</secondary><tertiary>{3}</tertiary>
67 {3#}</indexterm>
67 {3#}</indexterm>
68 {3#}<indexterm>
68 {3#}<indexterm>
69 <primary>{2}</primary><secondary>{3}</secondary>
69 <primary>{2}</primary><secondary>{3}</secondary>
70 {3#}</indexterm>
70 {3#}</indexterm>
71 {3#}<indexterm>
71 {3#}<indexterm>
72 <primary>{3}</primary>
72 <primary>{3}</primary>
73 {3#}</indexterm>
73 {3#}</indexterm>
74
74
75 [indexterm2-inlinemacro]
75 [indexterm2-inlinemacro]
76 # Inline index term.
76 # Inline index term.
77 # Single entry index term that is visible in the primary text flow.
77 # Single entry index term that is visible in the primary text flow.
78 <indexterm>
78 <indexterm>
79 <primary>{1}</primary>
79 <primary>{1}</primary>
80 </indexterm>
80 </indexterm>
81 {1}
81 {1}
82
82
83 [footnote-inlinemacro]
83 [footnote-inlinemacro]
84 # Inline footnote.
84 # Inline footnote.
85 <footnote><simpara>{0}</simpara></footnote>
85 <footnote><simpara>{0}</simpara></footnote>
86
86
87 [callout-inlinemacro]
87 [callout-inlinemacro]
88 # Inline callout.
88 # Inline callout.
89 <co id="{coid}"/>
89 <co id="{coid}"/>
90
90
91 [tags]
91 [tags]
92 # Bulleted, numbered and labeled list tags.
92 # Bulleted, numbered and labeled list tags.
93 ilist=<itemizedlist{id? id="{id}"}>{title?<title>{title}</title>}|</itemizedlist>
93 ilist=<itemizedlist{id? id="{id}"}>{title?<title>{title}</title>}|</itemizedlist>
94 ilistitem=<listitem>|</listitem>
94 ilistitem=<listitem>|</listitem>
95 ilisttext=<simpara>|</simpara>
95 ilisttext=<simpara>|</simpara>
96 olist=<orderedlist{id? id="{id}"}>{title?<title>{title}</title>}|</orderedlist>
96 olist=<orderedlist{id? id="{id}"}>{title?<title>{title}</title>}|</orderedlist>
97 olist2=<orderedlist{id? id="{id}"} numeration="loweralpha">|</orderedlist>
97 olist2=<orderedlist{id? id="{id}"} numeration="loweralpha">|</orderedlist>
98 olistitem=<listitem>|</listitem>
98 olistitem=<listitem>|</listitem>
99 olisttext=<simpara>|</simpara>
99 olisttext=<simpara>|</simpara>
100 vlist=<variablelist{id? id="{id}"}>{title?<title>{title}</title>}|</variablelist>
100 vlist=<variablelist{id? id="{id}"}>{title?<title>{title}</title>}|</variablelist>
101 vlistentry=<varlistentry>|</varlistentry>
101 vlistentry=<varlistentry>|</varlistentry>
102 vlistterm=<term>|</term>
102 vlistterm=<term>|</term>
103 vlisttext=<simpara>|</simpara>
103 vlisttext=<simpara>|</simpara>
104 vlistitem=<listitem>|</listitem>
104 vlistitem=<listitem>|</listitem>
105 # Horizontal labeled list (implemented with two column table).
105 # Horizontal labeled list (implemented with two column table).
106 # Hardwired column widths to 30%,70% because the current crop of PDF
106 # Hardwired column widths to 30%,70% because the current crop of PDF
107 # generators do not auto calculate column widths.
107 # generators do not auto calculate column widths.
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}>
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 hlistentry=<row>|</row>
109 hlistentry=<row>|</row>
110 hlisttext=<simpara>|</simpara>
110 hlisttext=<simpara>|</simpara>
111 hlistterm=<entry><simpara>|</simpara></entry>
111 hlistterm=<entry><simpara>|</simpara></entry>
112 hlistitem=<entry>|</entry>
112 hlistitem=<entry>|</entry>
113
113
114 # Question and Answer list.
114 # Question and Answer list.
115 qlist=<qandaset{id? id="{id}"}>{title?<title>{title}</title>}|</qandaset>
115 qlist=<qandaset{id? id="{id}"}>{title?<title>{title}</title>}|</qandaset>
116 qlistentry=<qandaentry>|</qandaentry>
116 qlistentry=<qandaentry>|</qandaentry>
117 qlistterm=<question><simpara>|</simpara></question>
117 qlistterm=<question><simpara>|</simpara></question>
118 qlistitem=<answer>|</answer>
118 qlistitem=<answer>|</answer>
119 qlisttext=<simpara>|</simpara>
119 qlisttext=<simpara>|</simpara>
120 # Bibliography list.
120 # Bibliography list.
121 blist=|
121 blist=|
122 blistitem=<bibliomixed>|</bibliomixed>
122 blistitem=<bibliomixed>|</bibliomixed>
123 blisttext=<bibliomisc>|</bibliomisc>
123 blisttext=<bibliomisc>|</bibliomisc>
124 # Glossary list.
124 # Glossary list.
125 glist=|
125 glist=|
126 glistentry=<glossentry>|</glossentry>
126 glistentry=<glossentry>|</glossentry>
127 glistterm=<glossterm>|</glossterm>
127 glistterm=<glossterm>|</glossterm>
128 glistitem=<glossdef>|</glossdef>
128 glistitem=<glossdef>|</glossdef>
129 glisttext=<simpara>|</simpara>
129 glisttext=<simpara>|</simpara>
130 # Callout list.
130 # Callout list.
131 colist=<calloutlist{id? id="{id}"}>{title?<title>{title}</title>}|</calloutlist>
131 colist=<calloutlist{id? id="{id}"}>{title?<title>{title}</title>}|</calloutlist>
132 colistitem=<callout arearefs="{coids}">|</callout>
132 colistitem=<callout arearefs="{coids}">|</callout>
133 colisttext=<simpara>|</simpara>
133 colisttext=<simpara>|</simpara>
134
134
135 # Quoted text
135 # Quoted text
136 emphasis=<emphasis>|</emphasis>
136 emphasis=<emphasis>|</emphasis>
137 strong=<emphasis role="strong">|</emphasis>
137 strong=<emphasis role="strong">|</emphasis>
138 monospaced=<literal>|</literal>
138 monospaced=<literal>|</literal>
139 quoted={amp}#8220;|{amp}#8221;
139 quoted={amp}#8220;|{amp}#8221;
140
140
141 # Inline macros
141 # Inline macros
142 [http-inlinemacro]
142 [http-inlinemacro]
143 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
143 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
144 [https-inlinemacro]
144 [https-inlinemacro]
145 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
145 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
146 [ftp-inlinemacro]
146 [ftp-inlinemacro]
147 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
147 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
148 [file-inlinemacro]
148 [file-inlinemacro]
149 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
149 <ulink url="{name}:{target}">{0={name}:{target}}</ulink>
150 [mailto-inlinemacro]
150 [mailto-inlinemacro]
151 <ulink url="{name}:{target}">{0={target}}</ulink>
151 <ulink url="{name}:{target}">{0={target}}</ulink>
152 #<email>{target}</email>
152 #<email>{target}</email>
153 [link-inlinemacro]
153 [link-inlinemacro]
154 <ulink url="{target}">{0={target}}</ulink>
154 <ulink url="{target}">{0={target}}</ulink>
155 # anchor:id[text]
155 # anchor:id[text]
156 [anchor-inlinemacro]
156 [anchor-inlinemacro]
157 <anchor id="{target}" xreflabel="{0=[{target}]}"/>
157 <anchor id="{target}" xreflabel="{0=[{target}]}"/>
158 # [[id,text]]
158 # [[id,text]]
159 [anchor2-inlinemacro]
159 [anchor2-inlinemacro]
160 <anchor id="{1}" xreflabel="{2=[{1}]}"/>
160 <anchor id="{1}" xreflabel="{2=[{1}]}"/>
161 # [[[id]]]
161 # [[[id]]]
162 [anchor3-inlinemacro]
162 [anchor3-inlinemacro]
163 <anchor id="{1}" xreflabel="[{1}]"/>[{1}]
163 <anchor id="{1}" xreflabel="[{1}]"/>[{1}]
164 # xref:id[text]
164 # xref:id[text]
165 [xref-inlinemacro]
165 [xref-inlinemacro]
166 <link linkend="{target}">{0}</link>
166 <link linkend="{target}">{0}</link>
167 {2%}<xref linkend="{target}"/>
167 {2%}<xref linkend="{target}"/>
168 # <<id,text>>
168 # <<id,text>>
169 [xref2-inlinemacro]
169 [xref2-inlinemacro]
170 <link linkend="{1}">{2}</link>
170 <link linkend="{1}">{2}</link>
171 {2%}<xref linkend="{1}"/>
171 {2%}<xref linkend="{1}"/>
172
172
173
173
174 # Special word macros
174 # Special word macros
175 [emphasizedwords]
175 [emphasizedwords]
176 <emphasis>{words}</emphasis>
176 <emphasis>{words}</emphasis>
177 [monospacedwords]
177 [monospacedwords]
178 <literal>{words}</literal>
178 <literal>{words}</literal>
179 [strongwords]
179 [strongwords]
180 <emphasis role="strong">{words}</emphasis>
180 <emphasis role="strong">{words}</emphasis>
181
181
182 # Paragraph substitution.
182 # Paragraph substitution.
183 [paragraph]
183 [paragraph]
184 <formalpara{id? id="{id}"}><title>{title}</title><para>
184 <formalpara{id? id="{id}"}><title>{title}</title><para>
185 {title%}<simpara{id? id="{id}"}>
185 {title%}<simpara{id? id="{id}"}>
186 |
186 |
187 {title%}</simpara>
187 {title%}</simpara>
188 {title#}</para></formalpara>
188 {title#}</para></formalpara>
189 {empty}
189 {empty}
190
190
191 [admonitionparagraph]
191 [admonitionparagraph]
192 <{name}{id? id="{id}"}><simpara>|</simpara></{name}>
192 <{name}{id? id="{id}"}><simpara>|</simpara></{name}>
193
193
194 [literalparagraph]
194 [literalparagraph]
195 # The literal block employs the same markup.
195 # The literal block employs the same markup.
196 template::[literalblock]
196 template::[literalblock]
197
197
198 [verseparagraph]
198 [verseparagraph]
199 template::[verseblock]
199 template::[verseblock]
200
200
201 # Delimited blocks.
201 # Delimited blocks.
202 [literalblock]
202 [literalblock]
203 <example><title>{title}</title>
203 <example><title>{title}</title>
204 <literallayout{id? id="{id}"} class="{font=monospaced}">
204 <literallayout{id? id="{id}"} class="{font=monospaced}">
205 |
205 |
206 </literallayout>
206 </literallayout>
207 {title#}</example>
207 {title#}</example>
208
208
209 [listingblock]
209 [listingblock]
210 <example><title>{title}</title>
210 <example><title>{title}</title>
211 <screen>
211 <screen>
212 |
212 |
213 </screen>
213 </screen>
214 {title#}</example>
214 {title#}</example>
215
215
216 [verseblock]
216 [verseblock]
217 <formalpara{id? id="{id}"}><title>{title}</title><para>
217 <formalpara{id? id="{id}"}><title>{title}</title><para>
218 {title%}<literallayout{id? id="{id}"}>
218 {title%}<literallayout{id? id="{id}"}>
219 {title#}<literallayout>
219 {title#}<literallayout>
220 |
220 |
221 </literallayout>
221 </literallayout>
222 {title#}</para></formalpara>
222 {title#}</para></formalpara>
223
223
224 [sidebarblock]
224 [sidebarblock]
225 <sidebar{id? id="{id}"}>
225 <sidebar{id? id="{id}"}>
226 <title>{title}</title>
226 <title>{title}</title>
227 |
227 |
228 </sidebar>
228 </sidebar>
229
229
230 [backendblock]
230 [backendblock]
231 |
231 |
232
232
233 [quoteblock]
233 [quoteblock]
234 # The epigraph element may be more appropriate than blockquote.
234 # The epigraph element may be more appropriate than blockquote.
235 <blockquote{id? id="{id}"}>
235 <blockquote{id? id="{id}"}>
236 <title>{title}</title>
236 <title>{title}</title>
237 <attribution>
237 <attribution>
238 {attribution}
238 {attribution}
239 <citetitle>{citetitle}</citetitle>
239 <citetitle>{citetitle}</citetitle>
240 </attribution>
240 </attribution>
241 |
241 |
242 </blockquote>
242 </blockquote>
243
243
244 [exampleblock]
244 [exampleblock]
245 <{title?example}{title!informalexample}{id? id="{id}"}>
245 <{title?example}{title!informalexample}{id? id="{id}"}>
246 <title>{title}</title>
246 <title>{title}</title>
247 |
247 |
248 </{title?example}{title!informalexample}>
248 </{title?example}{title!informalexample}>
249
249
250 [admonitionblock]
250 [admonitionblock]
251 <{name}{id? id="{id}"}>
251 <{name}{id? id="{id}"}>
252 <title>{title}</title>
252 <title>{title}</title>
253 |
253 |
254 </{name}>
254 </{name}>
255
255
256 # Tables.
256 # Tables.
257 [tabledef-default]
257 [tabledef-default]
258 template=table
258 template=table
259 colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}"/>
259 colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}"/>
260 bodyrow=<row>|</row>
260 bodyrow=<row>|</row>
261 bodydata=<entry>|</entry>
261 bodydata=<entry>|</entry>
262
262
263 [table]
263 [table]
264 <{title?table}{title!informaltable}{id? id="{id}"} pgwide="0"
264 <{title?table}{title!informaltable}{id? id="{id}"} pgwide="0"
265 frame="{frame=topbot}"
265 frame="{frame=topbot}"
266 {grid%rowsep="0" colsep="0"}
266 {grid%rowsep="0" colsep="0"}
267 {eval:\{"none":"rowsep=\"0\" colsep=\"0\"", "cols":"rowsep=\"0\" colsep=\"1\"", "all":"rowsep=\"1\" colsep=\"1\"", "rows":"rowsep=\"1\" colsep=\"0\"" \}["{grid}"]}
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 <title>{title}</title>
269 <title>{title}</title>
270 <tgroup cols="{cols}">
270 <tgroup cols="{cols}">
271 {colspecs}
271 {colspecs}
272 {headrows#}<thead>
272 {headrows#}<thead>
273 {headrows}
273 {headrows}
274 {headrows#}</thead>
274 {headrows#}</thead>
275 {footrows#}<tfoot>
275 {footrows#}<tfoot>
276 {footrows}
276 {footrows}
277 {footrows#}</tfoot>
277 {footrows#}</tfoot>
278 <tbody>
278 <tbody>
279 {bodyrows}
279 {bodyrows}
280 </tbody>
280 </tbody>
281 </tgroup>
281 </tgroup>
282 </{title?table}{title!informaltable}>
282 </{title?table}{title!informaltable}>
283
283
284 [specialsections]
284 [specialsections]
285 ifdef::doctype-article[]
285 ifdef::doctype-article[]
286 ^Abstract$=sect-abstract
286 ^Abstract$=sect-abstract
287 endif::doctype-article[]
287 endif::doctype-article[]
288
288
289 ifdef::doctype-book[]
289 ifdef::doctype-book[]
290 ^Colophon$=sect-colophon
290 ^Colophon$=sect-colophon
291 ^Dedication$=sect-dedication
291 ^Dedication$=sect-dedication
292 ^Preface$=sect-preface
292 ^Preface$=sect-preface
293 endif::doctype-book[]
293 endif::doctype-book[]
294
294
295 ^Index$=sect-index
295 ^Index$=sect-index
296 ^(Bibliography|References)$=sect-bibliography
296 ^(Bibliography|References)$=sect-bibliography
297 ^Glossary$=sect-glossary
297 ^Glossary$=sect-glossary
298 ^Appendix [A-Z][:.](?P<title>.*)$=sect-appendix
298 ^Appendix [A-Z][:.](?P<title>.*)$=sect-appendix
299
299
300 # Special sections.
300 # Special sections.
301 [sect-preface]
301 [sect-preface]
302 <preface{id? id="{id}"}>
302 <preface{id? id="{id}"}>
303 <title>{title}</title>
303 <title>{title}</title>
304 |
304 |
305 </preface>
305 </preface>
306
306
307 [sect-index]
307 [sect-index]
308 <index{id? id="{id}"}>
308 <index{id? id="{id}"}>
309 <title>{title}</title>
309 <title>{title}</title>
310 |
310 |
311 </index>
311 </index>
312
312
313 [sect-bibliography]
313 [sect-bibliography]
314 <bibliography{id? id="{id}"}>
314 <bibliography{id? id="{id}"}>
315 <title>{title}</title>
315 <title>{title}</title>
316 |
316 |
317 </bibliography>
317 </bibliography>
318
318
319 [sect-glossary]
319 [sect-glossary]
320 <glossary{id? id="{id}"}>
320 <glossary{id? id="{id}"}>
321 <title>{title}</title>
321 <title>{title}</title>
322 |
322 |
323 </glossary>
323 </glossary>
324
324
325 [sect-appendix]
325 [sect-appendix]
326 <appendix{id? id="{id}"}>
326 <appendix{id? id="{id}"}>
327 <title>{title}</title>
327 <title>{title}</title>
328 |
328 |
329 </appendix>
329 </appendix>
330
330
331
331
332 [header-declarations]
332 [header-declarations]
333 <?xml version="1.0" encoding="{encoding}"?>
333 <?xml version="1.0" encoding="{encoding}"?>
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">
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 # article document type
337 # article document type
338 #-------------------------
338 #-------------------------
339 ifdef::doctype-article[]
339 ifdef::doctype-article[]
340
340
341 [header]
341 [header]
342 template::[header-declarations]
342 template::[header-declarations]
343
343
344 <article lang="ja">
344 <article lang="ja">
345 {doctitle#}<articleinfo>
345 {doctitle#}<articleinfo>
346 <title>{doctitle}</title>
346 <title>{doctitle}</title>
347 <date>{date}</date>
347 <date>{date}</date>
348 {authored#}<author>
348 {authored#}<author>
349 <firstname>{firstname}</firstname>
349 <firstname>{firstname}</firstname>
350 <othername>{middlename}</othername>
350 <othername>{middlename}</othername>
351 <surname>{lastname}</surname>
351 <surname>{lastname}</surname>
352 <affiliation><address><email>{email}</email></address></affiliation>
352 <affiliation><address><email>{email}</email></address></affiliation>
353 {authored#}</author>
353 {authored#}</author>
354
354
355 # If file named like source document with -revhistory.xml suffix exists
355 # If file named like source document with -revhistory.xml suffix exists
356 # include it as the document history, otherwise use current revision.
356 # include it as the document history, otherwise use current revision.
357 {revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
357 {revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
358 {revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
358 {revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
359
359
360 <corpname>{companyname}</corpname>
360 <corpname>{companyname}</corpname>
361 {doctitle#}</articleinfo>
361 {doctitle#}</articleinfo>
362
362
363 [footer]
363 [footer]
364 </article>
364 </article>
365
365
366 [preamble]
366 [preamble]
367 # Untitled elements between header and first section title.
367 # Untitled elements between header and first section title.
368 |
368 |
369
369
370 [sect-abstract]
370 [sect-abstract]
371 <abstract{id? id="{id}"}>
371 <abstract{id? id="{id}"}>
372 |
372 |
373 </abstract>
373 </abstract>
374
374
375 [sect1]
375 [sect1]
376 <section{id? id="{id}"}>
376 <section{id? id="{id}"}>
377 <title>{title}</title>
377 <title>{title}</title>
378 |
378 |
379 </section>
379 </section>
380
380
381 [sect2]
381 [sect2]
382 <section{id? id="{id}"}>
382 <section{id? id="{id}"}>
383 <title>{title}</title>
383 <title>{title}</title>
384 |
384 |
385 </section>
385 </section>
386
386
387 [sect3]
387 [sect3]
388 <section{id? id="{id}"}>
388 <section{id? id="{id}"}>
389 <title>{title}</title>
389 <title>{title}</title>
390 |
390 |
391 </section>
391 </section>
392
392
393 [sect4]
393 [sect4]
394 <section{id? id="{id}"}>
394 <section{id? id="{id}"}>
395 <title>{title}</title>
395 <title>{title}</title>
396 |
396 |
397 </section>
397 </section>
398
398
399 endif::doctype-article[]
399 endif::doctype-article[]
400
400
401 #-------------------------
401 #-------------------------
402 # manpage document type
402 # manpage document type
403 #-------------------------
403 #-------------------------
404 ifdef::doctype-manpage[]
404 ifdef::doctype-manpage[]
405
405
406 [replacements]
406 [replacements]
407 # The roff format does not substitute special characters so just print them as
407 # The roff format does not substitute special characters so just print them as
408 # text.
408 # text.
409 \(C\)=(C)
409 \(C\)=(C)
410 \(TM\)=(TM)
410 \(TM\)=(TM)
411
411
412 [header]
412 [header]
413 template::[header-declarations]
413 template::[header-declarations]
414 <refentry>
414 <refentry>
415 <refmeta>
415 <refmeta>
416 <refentrytitle>{mantitle}</refentrytitle>
416 <refentrytitle>{mantitle}</refentrytitle>
417 <manvolnum>{manvolnum}</manvolnum>
417 <manvolnum>{manvolnum}</manvolnum>
418 </refmeta>
418 </refmeta>
419 <refnamediv>
419 <refnamediv>
420 <refname>{manname}</refname>
420 <refname>{manname}</refname>
421 <refpurpose>{manpurpose}</refpurpose>
421 <refpurpose>{manpurpose}</refpurpose>
422 </refnamediv>
422 </refnamediv>
423
423
424 [footer]
424 [footer]
425 </refentry>
425 </refentry>
426
426
427 # Section macros
427 # Section macros
428 [sect-synopsis]
428 [sect-synopsis]
429 <refsynopsisdiv{id? id="{id}"}>
429 <refsynopsisdiv{id? id="{id}"}>
430 |
430 |
431 </refsynopsisdiv>
431 </refsynopsisdiv>
432
432
433 [sect1]
433 [sect1]
434 <refsect1{id? id="{id}"}>
434 <refsect1{id? id="{id}"}>
435 <title>{title}</title>
435 <title>{title}</title>
436 |
436 |
437 </refsect1>
437 </refsect1>
438
438
439 [sect2]
439 [sect2]
440 <refsect2{id? id="{id}"}>
440 <refsect2{id? id="{id}"}>
441 <title>{title}</title>
441 <title>{title}</title>
442 |
442 |
443 </refsect2>
443 </refsect2>
444
444
445 [sect3]
445 [sect3]
446 <refsect3{id? id="{id}"}>
446 <refsect3{id? id="{id}"}>
447 <title>{title}</title>
447 <title>{title}</title>
448 |
448 |
449 </refsect3>
449 </refsect3>
450
450
451 endif::doctype-manpage[]
451 endif::doctype-manpage[]
452
452
453 #-------------------------
453 #-------------------------
454 # book document type
454 # book document type
455 #-------------------------
455 #-------------------------
456 ifdef::doctype-book[]
456 ifdef::doctype-book[]
457
457
458 [header]
458 [header]
459 template::[header-declarations]
459 template::[header-declarations]
460
460
461 <book lang="ja">
461 <book lang="ja">
462 {doctitle#}<bookinfo>
462 {doctitle#}<bookinfo>
463 <title>{doctitle}</title>
463 <title>{doctitle}</title>
464 <date>{date}</date>
464 <date>{date}</date>
465 {authored#}<author>
465 {authored#}<author>
466 <firstname>{firstname}</firstname>
466 <firstname>{firstname}</firstname>
467 <othername>{middlename}</othername>
467 <othername>{middlename}</othername>
468 <surname>{lastname}</surname>
468 <surname>{lastname}</surname>
469 <affiliation><address><email>{email}</email></address></affiliation>
469 <affiliation><address><email>{email}</email></address></affiliation>
470 {authored#}</author>
470 {authored#}</author>
471
471
472 # If file named like source document with -revhistory.xml suffix exists
472 # If file named like source document with -revhistory.xml suffix exists
473 # include it as the document history, otherwise use current revision.
473 # include it as the document history, otherwise use current revision.
474 {revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
474 {revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
475 {revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
475 {revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
476
476
477 <corpname>{companyname}</corpname>
477 <corpname>{companyname}</corpname>
478 {doctitle#}</bookinfo>
478 {doctitle#}</bookinfo>
479
479
480 [footer]
480 [footer]
481 </book>
481 </book>
482
482
483 [preamble]
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 <preface{id? id="{id}"}>
485 <preface{id? id="{id}"}>
486 <title>Preface</title>
486 <title>Preface</title>
487 |
487 |
488 </preface>
488 </preface>
489
489
490 [sect-dedication]
490 [sect-dedication]
491 <dedication{id? id="{id}"}>
491 <dedication{id? id="{id}"}>
492 |
492 |
493 </dedication>
493 </dedication>
494
494
495 [sect-colophon]
495 [sect-colophon]
496 <colophon{id? id="{id}"}>
496 <colophon{id? id="{id}"}>
497 |
497 |
498 </colophon>
498 </colophon>
499
499
500 [sect0]
500 [sect0]
501 <part{id? id="{id}"}>
501 <part{id? id="{id}"}>
502 <title>{title}</title>
502 <title>{title}</title>
503 |
503 |
504 </part>
504 </part>
505
505
506 [sect1]
506 [sect1]
507 <chapter{id? id="{id}"}>
507 <chapter{id? id="{id}"}>
508 <title>{title}</title>
508 <title>{title}</title>
509 |
509 |
510 </chapter>
510 </chapter>
511
511
512 [sect2]
512 [sect2]
513 <section{id? id="{id}"}>
513 <section{id? id="{id}"}>
514 <title>{title}</title>
514 <title>{title}</title>
515 |
515 |
516 </section>
516 </section>
517
517
518 [sect3]
518 [sect3]
519 <section{id? id="{id}"}>
519 <section{id? id="{id}"}>
520 <title>{title}</title>
520 <title>{title}</title>
521 |
521 |
522 </section>
522 </section>
523
523
524 [sect4]
524 [sect4]
525 <section{id? id="{id}"}>
525 <section{id? id="{id}"}>
526 <title>{title}</title>
526 <title>{title}</title>
527 |
527 |
528 </section>
528 </section>
529
529
530 endif::doctype-book[]
530 endif::doctype-book[]
531
531
532 ifdef::sgml[]
532 ifdef::sgml[]
533 #
533 #
534 # Optional DocBook SGML.
534 # Optional DocBook SGML.
535 #
535 #
536 # Most of the differences between DocBook XML and DocBook SGML boils
536 # Most of the differences between DocBook XML and DocBook SGML boils
537 # down to the empty element syntax: SGML does not like the XML empty
537 # down to the empty element syntax: SGML does not like the XML empty
538 # element <.../> syntax, use <...> instead.
538 # element <.../> syntax, use <...> instead.
539 #
539 #
540 [miscellaneous]
540 [miscellaneous]
541 outfilesuffix=.sgml
541 outfilesuffix=.sgml
542
542
543 [header-declarations]
543 [header-declarations]
544 <!DOCTYPE {eval:\{"article":"article", "book":"book", "manpage":"refentry"\}["{doctype}"]} PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
544 <!DOCTYPE {eval:\{"article":"article", "book":"book", "manpage":"refentry"\}["{doctype}"]} PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
545
545
546 [tabledef-default]
546 [tabledef-default]
547 colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}">
547 colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}">
548
548
549 [image-inlinemacro]
549 [image-inlinemacro]
550 <inlinemediaobject>
550 <inlinemediaobject>
551 <imageobject>
551 <imageobject>
552 <imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
552 <imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
553 </imageobject>
553 </imageobject>
554 <textobject><phrase>{1={target}}</phrase></textobject>
554 <textobject><phrase>{1={target}}</phrase></textobject>
555 </inlinemediaobject>
555 </inlinemediaobject>
556
556
557 [image-blockmacro]
557 [image-blockmacro]
558 <figure><title>{title}</title>
558 <figure><title>{title}</title>
559 {title%}<informalfigure>
559 {title%}<informalfigure>
560 <mediaobject>
560 <mediaobject>
561 <imageobject>
561 <imageobject>
562 <imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
562 <imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
563 </imageobject>
563 </imageobject>
564 <textobject><phrase>{1={target}}</phrase></textobject>
564 <textobject><phrase>{1={target}}</phrase></textobject>
565 </mediaobject>
565 </mediaobject>
566 {title#}</figure>
566 {title#}</figure>
567 {title%}</informalfigure>
567 {title%}</informalfigure>
568
568
569 # Inline macros
569 # Inline macros
570 [xref-inlinemacro]
570 [xref-inlinemacro]
571 <link linkend="{target}">{0}</link>
571 <link linkend="{target}">{0}</link>
572 {2%}<xref linkend="{target}">
572 {2%}<xref linkend="{target}">
573 [xref2-inlinemacro]
573 [xref2-inlinemacro]
574 # <<id,text>>
574 # <<id,text>>
575 <link linkend="{1}">{2}</link>
575 <link linkend="{1}">{2}</link>
576 {2%}<xref linkend="{1}">
576 {2%}<xref linkend="{1}">
577 [anchor-inlinemacro]
577 [anchor-inlinemacro]
578 <anchor id="{target}" xreflabel="{0=[{target}]}">
578 <anchor id="{target}" xreflabel="{0=[{target}]}">
579 [anchor2-inlinemacro]
579 [anchor2-inlinemacro]
580 # [[id,text]]
580 # [[id,text]]
581 <anchor id="{1}" xreflabel="{2=[{1}]}">
581 <anchor id="{1}" xreflabel="{2=[{1}]}">
582
582
583 endif::sgml[]
583 endif::sgml[]
@@ -1,867 +1,867 b''
1 HG(1)
1 HG(1)
2 =====
2 =====
3 Matt Mackall <mpm@selenic.com>
3 Matt Mackall <mpm@selenic.com>
4
4
5 名前
5 名前
6 --
6 --
7 hg - Mercurial ソースコード管理システム
7 hg - Mercurial ソースコード管理システム
8
8
9 書式
9 書式
10 --
10 --
11 'hg' [-v -d -q -y] <command> [command options] [files]
11 'hg' [-v -d -q -y] <command> [command options] [files]
12
12
13 説明
13 説明
14 --
14 --
15 hg(1) コマンドは Mercurial システムへのコマンドラインインターフェ
15 hg(1) コマンドは Mercurial システムへのコマンドラインインターフェ
16 イスを提供します。
16 イスを提供します。
17
17
18 オプション
18 オプション
19 ----
19 ----
20
20
21 -R, --repository::
21 -R, --repository::
22 リポジトリのルートディレクトリを指定します。
22 リポジトリのルートディレクトリを指定します。
23
23
24 --cwd::
24 --cwd::
25 作業ディレクトリを変更します。
25 作業ディレクトリを変更します。
26
26
27 -y, --noninteractive::
27 -y, --noninteractive::
28 プロンプトを出さずに、要求された答えが全て 'yes' であると仮定
28 プロンプトを出さずに、要求された答えが全て 'yes' であると仮定
29 します。
29 します。
30
30
31 -q, --quiet::
31 -q, --quiet::
32 出力を抑制します。
32 出力を抑制します。
33
33
34 -v, --verbose::
34 -v, --verbose::
35 さらなる出力を可能にします。
35 さらなる出力を可能にします。
36
36
37 7--debug::
37 7--debug::
38 デバッグ出力を可能にします。
38 デバッグ出力を可能にします。
39
39
40 --traceback::
40 --traceback::
41 例外時にトレースバックを表示します。
41 例外時にトレースバックを表示します。
42
42
43 --time::
43 --time::
44 コマンドにどのくらい時間がかかるかを表示します。
44 コマンドにどのくらい時間がかかるかを表示します。
45
45
46 --profile::
46 --profile::
47 コマンドを実行したときのプロファイルを表示します。
47 コマンドを実行したときのプロファイルを表示します。
48
48
49 --version::
49 --version::
50 バージョン情報を表示して終了します。
50 バージョン情報を表示して終了します。
51
51
52 -h, --help::
52 -h, --help::
53 ヘルプを表示して終了します。
53 ヘルプを表示して終了します。
54
54
55 コマンドの要素
55 コマンドの要素
56 -------
56 -------
57
57
58 files ...::
58 files ...::
59 1つ以上のファイル名か相対的なパスを表します; パターンマッチン
59 1つ以上のファイル名か相対的なパスを表します; パターンマッチン
60 グの情報は「ファイル名のパターン」を参照してください。
60 グの情報は「ファイル名のパターン」を参照してください。
61
61
62 path::
62 path::
63 ローカルマシン上のパスを表します
63 ローカルマシン上のパスを表します
64
64
65 revision::
65 revision::
66 チェンジセットのリビジョンナンバー, タグ, チェンジセットのハッ
66 チェンジセットのリビジョンナンバー, タグ, チェンジセットのハッ
67 シュ値のユニークな部分文字列により指定できるチェンジセットを表
67 シュ値のユニークな部分文字列により指定できるチェンジセットを表
68 します
68 します
69
69
70 repository path::
70 repository path::
71 ローカルのリポジトリのパス名かリモートのリポジトリの URI を表
71 ローカルのリポジトリのパス名かリモートのリポジトリの URI を表
72 します。URI のプロトコルとしては現在 2 つが利用可能です。
72 します。URI のプロトコルとしては現在 2 つが利用可能です。
73 http:// は高速で、static-http:// は遅いですがウェブのホストに特別
73 http:// は高速で、static-http:// は遅いですがウェブのホストに特別
74 なサーバを必要としません。
74 なサーバを必要としません。
75
75
76 コマンド
76 コマンド
77 ----
77 ----
78
78
79 add [options] [files ...]::
79 add [options] [files ...]::
80 ファイルをバージョン管理下に置きリポジトリに追加することを予定
80 ファイルをバージョン管理下に置きリポジトリに追加することを予定
81 します。
81 します。
82
82
83 ファイルは次にコミット時にリポジトリに追加されます。
83 ファイルは次にコミット時にリポジトリに追加されます。
84
84
85 ファイル名が与えられなければ、現在のディレクトリとサブディレク
85 ファイル名が与えられなければ、現在のディレクトリとサブディレク
86 トリの全てのファイルを追加します。
86 トリの全てのファイルを追加します。
87
87
88 addremove [options] [files ...]::
88 addremove [options] [files ...]::
89 新しいファイルを全て追加し無くなったファイルを全てリポジトリか
89 新しいファイルを全て追加し無くなったファイルを全てリポジトリか
90 ら取り除きます。
90 ら取り除きます。
91
91
92 新しいファイルは .hgignore 中のパターンにマッチした場合無視さ
92 新しいファイルは .hgignore 中のパターンにマッチした場合無視さ
93 れます。add のようにこの変更は次のコミット時に効果を持ちます。
93 れます。add のようにこの変更は次のコミット時に効果を持ちます。
94
94
95 annotate [-r <rev> -u -n -c] [files ...]::
95 annotate [-r <rev> -u -n -c] [files ...]::
96 ファイル中の変更を列挙し、各行の原因であるリビジョン id を表示
96 ファイル中の変更を列挙し、各行の原因であるリビジョン id を表示
97 します。
97 します。
98
98
99 このコマンドある変更が生じた際に誰がその変更をしたかを発見する
99 このコマンドある変更が生じた際に誰がその変更をしたかを発見する
100 のに役に立ちます。
100 のに役に立ちます。
101
101
102 -a オプションが無いと、annotate はバイナリとして検出されたファ
102 -a オプションが無いと、annotate はバイナリとして検出されたファ
103 イルを避けるようになります。-a があると、annotate はとくかく注
103 イルを避けるようになります。-a があると、annotate はとくかく注
104 釈を生成し、おそらく望ましくない結果になるでしょう。
104 釈を生成し、おそらく望ましくない結果になるでしょう。
105
105
106 オプション:
106 オプション:
107 -a, --text 全てのファイルをテキストとして扱います
107 -a, --text 全てのファイルをテキストとして扱います
108 -I, --include <pat> 与えられたパターンにマッチした名前を含め
108 -I, --include <pat> 与えられたパターンにマッチした名前を含め
109 ます
109 ます
110 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
110 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
111 します
111 します
112 -r, --revision <rev> 指定されたリビジョンの注釈を生成します
112 -r, --revision <rev> 指定されたリビジョンの注釈を生成します
113 -u, --user 著者を列挙します
113 -u, --user 著者を列挙します
114 -c, --changeset チェンジセットを列挙します
114 -c, --changeset チェンジセットを列挙します
115 -n, --number リビジョンナンバーを列挙します
115 -n, --number リビジョンナンバーを列挙します
116 (デフォルト)
116 (デフォルト)
117
117
118 bundle <file> <other>::
118 bundle <file> <other>::
119 (実験的)
119 (実験的)
120
120
121 他のリポジトリには見付からなかった全てのチェンジセットを集めた、
121 他のリポジトリには見付からなかった全てのチェンジセットを集めた、
122 圧縮済みチェンジグループファイルを生成します。
122 圧縮済みチェンジグループファイルを生成します。
123
123
124 このファイルは従来の方法で転送することができ、他のリポジトリに
124 このファイルは従来の方法で転送することができ、他のリポジトリに
125 unbundle コマンドで適用できます。これは push と pull が使えな
125 unbundle コマンドで適用できます。これは push と pull が使えな
126 いか、リポジトリ全体をエクスポートしてしまうことが望ましくない
126 いか、リポジトリ全体をエクスポートしてしまうことが望ましくない
127 ときに便利です。標準のファイル拡張子は ".hg" です。
127 ときに便利です。標準のファイル拡張子は ".hg" です。
128
128
129 import/export と違って、これはパーミッション、名前変更のデータ、
129 import/export と違って、これはパーミッション、名前変更のデータ、
130 リビジョンの履歴を含めたチェンジセットの内容全てを保存します。
130 リビジョンの履歴を含めたチェンジセットの内容全てを保存します。
131
131
132 cat [options] <file ...>::
132 cat [options] <file ...>::
133 指定されたファイルを与えられたリビジョンの内容で表示します。リ
133 指定されたファイルを与えられたリビジョンの内容で表示します。リ
134 ビジョンが指定されなかった場合は tip が使われます。
134 ビジョンが指定されなかった場合は tip が使われます。
135
135
136 出力はファイルに対しても可能です。その場合、ファイル名はフォー
136 出力はファイルに対しても可能です。その場合、ファイル名はフォー
137 マット文字列により指定されます。フォーマット規則は export コマ
137 マット文字列により指定されます。フォーマット規則は export コマ
138 ンドと同じですが、さらに次のものが追加されます。
138 ンドと同じですが、さらに次のものが追加されます。
139
139
140 %s 出力されるファイルのベース名
140 %s 出力されるファイルのベース名
141 %d 出力されるファイルのディレクトリ名か、リポジトリのルート
141 %d 出力されるファイルのディレクトリ名か、リポジトリのルート
142 にいる場合は "."
142 にいる場合は "."
143 %p 出力されるファイルのルートからの相対パス
143 %p 出力されるファイルのルートからの相対パス
144
144
145 オプション:
145 オプション:
146 -I, --include <pat> 与えられたパターンにマッチした名前
146 -I, --include <pat> 与えられたパターンにマッチした名前
147 を含めます
147 を含めます
148 -X, --exclude <pat> 与えられたパターンにマッチした名前
148 -X, --exclude <pat> 与えられたパターンにマッチした名前
149 を除外します
149 を除外します
150 -o, --output <filespec> 整形された名前でファイルに出力しま
150 -o, --output <filespec> 整形された名前でファイルに出力しま
151
151
152 -r, --rev <rev> 与えられたリビジョンを表示します
152 -r, --rev <rev> 与えられたリビジョンを表示します
153
153
154 clone [-U] <source> [dest]::
154 clone [-U] <source> [dest]::
155 既存のリポジトリのコピーを新しいディレクトリに作成します
155 既存のリポジトリのコピーを新しいディレクトリに作成します
156
156
157 コピー先のディレクトリ名が指定されなければ、デフォルトでソース
157 コピー先のディレクトリ名が指定されなければ、デフォルトでソース
158 のベース名を使用します。
158 のベース名を使用します。
159
159
160 今後の pull に使えるように、コピー元が新しいリポジトリの
160 今後の pull に使えるように、コピー元が新しいリポジトリの
161 .hg/hgrc に追加されます。
161 .hg/hgrc に追加されます。
162
162
163 効率のために、コピー元とコピー先が同じファイルシステム上にある
163 効率のために、コピー元とコピー先が同じファイルシステム上にある
164 場合はハードリンクが使われます。
164 場合はハードリンクが使われます。
165
165
166 オプションン:
166 オプションン:
167 -U, --noupdate 新しい作業ディレクトリで update を行いません
167 -U, --noupdate 新しい作業ディレクトリで update を行いません
168 -e, --ssh 使用される ssh コマンドを指定します
168 -e, --ssh 使用される ssh コマンドを指定します
169 --remotecmd リモート側で実行する hg コマンドを指定します
169 --remotecmd リモート側で実行する hg コマンドを指定します
170
170
171 commit [options] [files...]::
171 commit [options] [files...]::
172 指定されたファイルの変更をリポジトリにコミットします。
172 指定されたファイルの変更をリポジトリにコミットします。
173
173
174 もしファイルのリストが省略されたら、リポジトリのルートから実行
174 もしファイルのリストが省略されたら、リポジトリのルートから実行
175 した"hg status" で報告される全ての変更がコミットされます。
175 した"hg status" で報告される全ての変更がコミットされます。
176
176
177 HGEDITOR や EDITOR 環境変数はコミット時のコメントを追加するエ
177 HGEDITOR や EDITOR 環境変数はコミット時のコメントを追加するエ
178 ディタを起動するために使われます。
178 ディタを起動するために使われます。
179
179
180 オプション:
180 オプション:
181
181
182 -A, --addremove コミット中に addremove を実行します
182 -A, --addremove コミット中に addremove を実行します
183 -I, --include <pat> 与えられたパターンにマッチした名前を含め
183 -I, --include <pat> 与えられたパターンにマッチした名前を含め
184 ます
184 ます
185 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
185 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
186 します
186 します
187 -m, --message <text> <text> をコミットメッセージとして使いま
187 -m, --message <text> <text> をコミットメッセージとして使いま
188
188
189 -l, --logfile <file> <file> からコミットメッセージを読み込み
189 -l, --logfile <file> <file> からコミットメッセージを読み込み
190 ます
190 ます
191 -d, --date <datecode> datecode をコミットした日付として記録し
191 -d, --date <datecode> datecode をコミットした日付として記録し
192 ます
192 ます
193 -u, --user <user> user をコミッタとして記録します。
193 -u, --user <user> user をコミッタとして記録します。
194
194
195 別名: ci
195 別名: ci
196
196
197 copy <source ...> <dest>::
197 copy <source ...> <dest>::
198 コピー先がコピー元のファイルのコピーを持っていると印を付けます。
198 コピー先がコピー元のファイルのコピーを持っていると印を付けます。
199 もしコピー先がディレクトリなら、コピーはディレクトリ中に置かれ
199 もしコピー先がディレクトリなら、コピーはディレクトリ中に置かれ
200 ます。もしコピー先がファイルなら、コピー元は1つのみ指定可能で
200 ます。もしコピー先がファイルなら、コピー元は1つのみ指定可能で
201 す。
201 す。
202
202
203 デフォルトでは、このコマンドはファイルがその作業ディレクトリに
203 デフォルトでは、このコマンドはファイルがその作業ディレクトリに
204 あるものとしてその内容をコピーします。もし --after と一緒に呼
204 あるものとしてその内容をコピーします。もし --after と一緒に呼
205 び出されれば、操作は記録されますが、コピーは実行されません。
205 び出されれば、操作は記録されますが、コピーは実行されません。
206
206
207 このコマンドは次のコミット時に効果を持ちます。
207 このコマンドは次のコミット時に効果を持ちます。
208
208
209 注意: このコマンドは実験的です。リネームされたファイルを適切に
209 注意: このコマンドは実験的です。リネームされたファイルを適切に
210 記録できますが、この情報はマージによってまだ完全には使われませ
210 記録できますが、この情報はマージによってまだ完全には使われませ
211 んし、ログで完全に報告されることもありません。
211 んし、ログで完全に報告されることもありません。
212
212
213 オプション:
213 オプション:
214 -A, --after 既に発生したコピーを記録します。
214 -A, --after 既に発生したコピーを記録します。
215 -I, --include <pat> 与えられたパターンにマッチした名前を含め
215 -I, --include <pat> 与えられたパターンにマッチした名前を含め
216 ます
216 ます
217 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
217 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
218 します
218 します
219 -f, --force 既存の変更されたファイルに無理矢理コピー
219 -f, --force 既存の変更されたファイルに無理矢理コピー
220 します
220 します
221 -p, --parents コピー先にコピー元のパスを追加します
221 -p, --parents コピー先にコピー元のパスを追加します
222
222
223 別名: cp
223 別名: cp
224
224
225 diff [-a] [-r revision] [-r revision] [files ...]::
225 diff [-a] [-r revision] [-r revision] [files ...]::
226 指定されたファイルのリビジョン間の差分を表示します。
226 指定されたファイルのリビジョン間の差分を表示します。
227
227
228 ファイル間の差分は unified diff 形式で表示されます。
228 ファイル間の差分は unified diff 形式で表示されます。
229
229
230 2つのリビジョンが引数として指定された場合、それらのリビジョン
230 2つのリビジョンが引数として指定された場合、それらのリビジョン
231 間の差分が表示されます。1つのリビジョンしか指定されなければ、
231 間の差分が表示されます。1つのリビジョンしか指定されなければ、
232 そのリビジョンは作業ディレクトリと比較されます。そして リビジョ
232 そのリビジョンは作業ディレクトリと比較されます。そして リビジョ
233 ンが指定されなければ、作業ディレクトリのファイルがその親と比較
233 ンが指定されなければ、作業ディレクトリのファイルがその親と比較
234 されます。
234 されます。
235
235
236 -a オプション無しでは、diff はバイナリファイルを検出したときに
236 -a オプション無しでは、diff はバイナリファイルを検出したときに
237 その差分を生成するのを避けます。-a オプションでは、diff はとに
237 その差分を生成するのを避けます。-a オプションでは、diff はとに
238 かく差分を生成し、恐らく望ましくない結果をもたらすでしょう。
238 かく差分を生成し、恐らく望ましくない結果をもたらすでしょう。
239
239
240 オプション:
240 オプション:
241 -a, --text 全てのファイルをテキストとして扱います
241 -a, --text 全てのファイルをテキストとして扱います
242 -I, --include <pat> 与えられたパターンにマッチした名前を含め
242 -I, --include <pat> 与えられたパターンにマッチした名前を含め
243 ます
243 ます
244 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
244 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
245 します
245 します
246
246
247 export [-o filespec] [revision] ...::
247 export [-o filespec] [revision] ...::
248 1つ以上のリビジョンのチェンジセットのヘッダと差分を出力します。
248 1つ以上のリビジョンのチェンジセットのヘッダと差分を出力します。
249
249
250 チェンジセットのヘッダに表示される情報は: 著者、チェンジセット
250 チェンジセットのヘッダに表示される情報は: 著者、チェンジセット
251 のハッシュ、親、コミット時のコメントです。
251 のハッシュ、親、コミット時のコメントです。
252
252
253 出力はファイルに対しても可能です。その場合、ファイル名はフォー
253 出力はファイルに対しても可能です。その場合、ファイル名はフォー
254 マット文字列により指定されます。フォーマット規則は下記の通りで
254 マット文字列により指定されます。フォーマット規則は下記の通りで
255 す:
255 す:
256
256
257 %% そのままの "%" 文字
257 %% そのままの "%" 文字
258 %H チェンジセットのハッシュ (40 バイトの 16 進数)
258 %H チェンジセットのハッシュ (40 バイトの 16 進数)
259 %N 生成されているパッチの番号
259 %N 生成されているパッチの番号
260 %R チェンジセットのリビジョンナンバー
260 %R チェンジセットのリビジョンナンバー
261 %b エクスポートしているリポジトリのメース名
261 %b エクスポートしているリポジトリのメース名
262 %h 短い形式のチェンジセットのハッシュ (12 バイトの 16 進数)
262 %h 短い形式のチェンジセットのハッシュ (12 バイトの 16 進数)
263 %n 0 で 詰められた 1 から始まる連番
263 %n 0 で 詰められた 1 から始まる連番
264 %r 0 で 詰められたリビジョンナンバー
264 %r 0 で 詰められたリビジョンナンバー
265
265
266 -a オプション無しでは、diff はバイナリファイルを検出したときに
266 -a オプション無しでは、diff はバイナリファイルを検出したときに
267 その差分を生成するのを避けます。-a オプションでは、diff はとに
267 その差分を生成するのを避けます。-a オプションでは、diff はとに
268 かく差分を生成し、恐らく望ましくない結果をもたらすでしょう。
268 かく差分を生成し、恐らく望ましくない結果をもたらすでしょう。
269
269
270 オプション:
270 オプション:
271 -a, --text 全てのファイルをテキストとして扱います
271 -a, --text 全てのファイルをテキストとして扱います
272 -o, --output <filespec> 整形された名前でファイルに出力します
272 -o, --output <filespec> 整形された名前でファイルに出力します
273
273
274 forget [options] [files]::
274 forget [options] [files]::
275 次のコミット時に予定された 'hg add' を取り消します。
275 次のコミット時に予定された 'hg add' を取り消します。
276
276
277 オプション:
277 オプション:
278 -I, --include <pat> 与えられたパターンにマッチした名前を含めま
278 -I, --include <pat> 与えられたパターンにマッチした名前を含めま
279 -すX, --exclude <pat> 与えられたパターンにマッチした名前を除外
279 -すX, --exclude <pat> 与えられたパターンにマッチした名前を除外
280 -します
280 -します
281
281
282 grep [options] pattern [files]::
282 grep [options] pattern [files]::
283 正規表現によりファイルのリビジョンを検索します。
283 正規表現によりファイルのリビジョンを検索します。
284
284
285 このコマンドは Unix の grep とは違う振舞いをします。これは
285 このコマンドは Unix の grep とは違う振舞いをします。これは
286 Python/Perl の正規表現だけを受けつけます。これは作業ディレクト
286 Python/Perl の正規表現だけを受けつけます。これは作業ディレクト
287 リではなくリポジトリの履歴を検索します。これは常にマッチしたも
287 リではなくリポジトリの履歴を検索します。これは常にマッチしたも
288 のが現れたリビジョンナンバーを表示します。
288 のが現れたリビジョンナンバーを表示します。
289
289
290 デフォルトでは、grep はマッチしたものが見つかったファイルの最
290 デフォルトでは、grep はマッチしたものが見つかったファイルの最
291 初のリビジョンを出力します。マッチ状況の変化("-" はマッチが非
291 初のリビジョンを出力します。マッチ状況の変化("-" はマッチが非
292 マッチに、"+" は非マッチがマッチに)を含んだ各リビジョンを表示
292 マッチに、"+" は非マッチがマッチに)を含んだ各リビジョンを表示
293 するには、--all フラグを使ってください。
293 するには、--all フラグを使ってください。
294
294
295 オプション:
295 オプション:
296 -0, --print0 ファイル名を NUL で終えます。
296 -0, --print0 ファイル名を NUL で終えます。
297 -I, --include <pat> 与えられたパターンにマッチした名前
297 -I, --include <pat> 与えられたパターンにマッチした名前
298 を含めます
298 を含めます
299 -X, --exclude <pat> 与えられたパターンにマッチした名前
299 -X, --exclude <pat> 与えられたパターンにマッチした名前
300 を除外します
300 を除外します
301 --all マッチした全てのリビジョンを表示し
301 --all マッチした全てのリビジョンを表示し
302 ます
302 ます
303 -i, --ignore-case マッチのときに英大文字と小文字を区
303 -i, --ignore-case マッチのときに英大文字と小文字を区
304 別しないようにします
304 別しないようにします
305 -l, --files-with-matches マッチしたファイル名とリビジョンの
305 -l, --files-with-matches マッチしたファイル名とリビジョンの
306 みを表示します
306 みを表示します
307 -n, --line-number マッチした行番号を表示します
307 -n, --line-number マッチした行番号を表示します
308 -r, --rev <rev> 指定されたリビジョンの間で検索しま
308 -r, --rev <rev> 指定されたリビジョンの間で検索しま
309
309
310 -u, --user その変更をコミットしたユーザを表示
310 -u, --user その変更をコミットしたユーザを表示
311 します
311 します
312
312
313 heads::
313 heads::
314 リポジトリの先頭のチェンジセットを全て表示します。
314 リポジトリの先頭のチェンジセットを全て表示します。
315
315
316 リポジトリの「先頭」とは子のチェンジセットを持っていないチェン
316 リポジトリの「先頭」とは子のチェンジセットを持っていないチェン
317 ジセットです。それらは大抵開発が行われる場所で、通常 update と
317 ジセットです。それらは大抵開発が行われる場所で、通常 update と
318 merge 操作の対象となるところです。
318 merge 操作の対象となるところです。
319
319
320 identify::
320 identify::
321 レポジトリの現在の状態の短いサマリを表示します。
321 レポジトリの現在の状態の短いサマリを表示します。
322
322
323 このサマリはリポジトリの状態を1つまたは2つの親のハッシュ識別子
323 このサマリはリポジトリの状態を1つまたは2つの親のハッシュ識別子
324 を使って識別します。親のハッシュ識別子はもし作業ディレクトリに
324 を使って識別します。親のハッシュ識別子はもし作業ディレクトリに
325 コミットされていない変更があれば後ろに + が付き、更にその後に
325 コミットされていない変更があれば後ろに + が付き、更にその後に
326 このリビジョンのタグのリストが付きます。
326 このリビジョンのタグのリストが付きます。
327
327
328 別名: id
328 別名: id
329
329
330 import [-p <n> -b <base> -f] <patches>::
330 import [-p <n> -b <base> -f] <patches>::
331 一連のパッチをインポートし、それぞれ個別にコミットします。
331 一連のパッチをインポートし、それぞれ個別にコミットします。
332
332
333 作業ディレクトリに未解決の変更があった場合、import は -f フラ
333 作業ディレクトリに未解決の変更があった場合、import は -f フラ
334 グが指定されてなければ中断します。
334 グが指定されてなければ中断します。
335
335
336 もしパッチがメールのよう(最初の行が "From " か RFC 822 ヘッダ
336 もしパッチがメールのよう(最初の行が "From " か RFC 822 ヘッダ
337 のよう) であれば、-f オプションが使われていない限りそれは適用
337 のよう) であれば、-f オプションが使われていない限りそれは適用
338 されません。インポート機構はメールのヘッダをパースもしなければ
338 されません。インポート機構はメールのヘッダをパースもしなければ
339 破棄もしないので、本物のメールをインポートしないようにする「メー
339 破棄もしないので、本物のメールをインポートしないようにする「メー
340 ルのようなものの」健全性チェックを上書きするためだけに -f を使っ
340 ルのようなものの」健全性チェックを上書きするためだけに -f を使っ
341 てください。
341 てください。
342
342
343 オプション:
343 オプション:
344 -p, --strip <n> patch の ディレクトリ除去オプションです。これ
344 -p, --strip <n> patch の ディレクトリ除去オプションです。これ
345 は関連する patch のオプションと同じ意味を持ち
345 は関連する patch のオプションと同じ意味を持ち
346 ます
346 ます
347 -b <path> パッチを読み込むベースとなるディレクトリを指
347 -b <path> パッチを読み込むベースとなるディレクトリを指
348 定します
348 定します
349 -f, --force 未解決でまだコミットされていない変更のチェッ
349 -f, --force 未解決でまだコミットされていない変更のチェッ
350 クを省略します
350 クを省略します
351
351
352 別名: patch
352 別名: patch
353
353
354 incoming [-p] [source]::
354 incoming [-p] [source]::
355 指定されたリポジトリか、デフォルトで pull するリポジトリ中に見
355 指定されたリポジトリか、デフォルトで pull するリポジトリ中に見
356 つかった新しいチェンジセットを表示します。これらは pull が要求
356 つかった新しいチェンジセットを表示します。これらは pull が要求
357 されたときにpull されるチェンジセットです。
357 されたときにpull されるチェンジセットです。
358
358
359 現在はローカルのリポジトリのみがサポートされています。
359 現在はローカルのリポジトリのみがサポートされています。
360
360
361 オプション:
361 オプション:
362 -p, --patch パッチを表示します
362 -p, --patch パッチを表示します
363
363
364 別名: in
364 別名: in
365
365
366 init [dest]::
366 init [dest]::
367 指定されたディレクトリ中に新しいリポジトリを初期化します。指定
367 指定されたディレクトリ中に新しいリポジトリを初期化します。指定
368 されたディレクトリが存在しない場合は作成されます。
368 されたディレクトリが存在しない場合は作成されます。
369
369
370 ディレクトリが指定されなければ、現在のディレクトリが使用されま
370 ディレクトリが指定されなければ、現在のディレクトリが使用されま
371 す。
371 す。
372
372
373 locate [options] [files]::
373 locate [options] [files]::
374 Mercurial の管理下にあるファイルで名前が指定されたパターンにマッ
374 Mercurial の管理下にあるファイルで名前が指定されたパターンにマッ
375 チしたものを全て表示します。
375 チしたものを全て表示します。
376
376
377 このコマンドは現在のディレクトリとサブディレクトリを検索します。
377 このコマンドは現在のディレクトリとサブディレクトリを検索します。
378 リポジトリ全体を検索するには、リポジトリのルートに移動してくだ
378 リポジトリ全体を検索するには、リポジトリのルートに移動してくだ
379 さい。
379 さい。
380
380
381 もしマッチするためのパターンが与えられなければ、このコマンドは
381 もしマッチするためのパターンが与えられなければ、このコマンドは
382 全てのファイルの名前を表示します。
382 全てのファイルの名前を表示します。
383
383
384 もしこのコマンドの出力を "xargs" コマンドに送りたいなら、
384 もしこのコマンドの出力を "xargs" コマンドに送りたいなら、
385 "-0" オプションをこのコマンドと "xargs" コマンドの両方で使用し
385 "-0" オプションをこのコマンドと "xargs" コマンドの両方で使用し
386 てください。これは "xargs" がスペースの入ったファイル名を複数
386 てください。これは "xargs" がスペースの入ったファイル名を複数
387 のファイル名として扱わないようにします。
387 のファイル名として扱わないようにします。
388
388
389 オプション:
389 オプション:
390
390
391 -0, --print0 xargs と一緒に使うために、ファイル名を
391 -0, --print0 xargs と一緒に使うために、ファイル名を
392 NUL で終えます
392 NUL で終えます
393 -f, --fullpath ファイルシステムのルートからの完全なパ
393 -f, --fullpath ファイルシステムのルートからの完全なパ
394 スを表示します
394 スを表示します
395 -I, --include <pat> 与えられたパターンにマッチした名前を含
395 -I, --include <pat> 与えられたパターンにマッチした名前を含
396 めます
396 めます
397 -r, --rev <rev> rev のときのリポジトリを検索します
397 -r, --rev <rev> rev のときのリポジトリを検索します
398 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
398 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
399 します
399 します
400
400
401 log [-r revision ...] [-p] [files]::
401 log [-r revision ...] [-p] [files]::
402 指定されたファイルかプロジェクト全体のリビジョンの履歴を表示し
402 指定されたファイルかプロジェクト全体のリビジョンの履歴を表示し
403 ます。
403 ます。
404
404
405 デフォルトではこのコマンドは次のものを出力します: チェンジセッ
405 デフォルトではこのコマンドは次のものを出力します: チェンジセッ
406 トのid とハッシュ、タグ、親、ユーザ、日付、各コミットのサマ
406 トのid とハッシュ、タグ、親、ユーザ、日付、各コミットのサマ
407 リ。-v スイッチは変更されたファイルやマニフェストのハッシュ、
407 リ。-v スイッチは変更されたファイルやマニフェストのハッシュ、
408 メッセージのシグネチャといったより詳しい情報を追加します。
408 メッセージのシグネチャといったより詳しい情報を追加します。
409
409
410 オプション:
410 オプション:
411 -I, --include <pat> 与えられたパターンにマッチした名前を含め
411 -I, --include <pat> 与えられたパターンにマッチした名前を含め
412 ます
412 ます
413 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
413 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外
414 します
414 します
415 -r, --rev <A> 指定されたリビジョンまたは範囲を表示しま
415 -r, --rev <A> 指定されたリビジョンまたは範囲を表示しま
416
416
417 -p, --patch パッチを表示します
417 -p, --patch パッチを表示します
418
418
419 別名: history
419 別名: history
420
420
421 manifest [revision]::
421 manifest [revision]::
422 指定されたリビジョンでバージョン管理されているファイルのリスト
422 指定されたリビジョンでバージョン管理されているファイルのリスト
423 を表示します。
423 を表示します。
424
424
425 manifest はバージョン管理されているファイルのリストです。もし
425 manifest はバージョン管理されているファイルのリストです。もし
426 リビジョンが指定されなければ、tip が使われます。
426 リビジョンが指定されなければ、tip が使われます。
427
427
428 outgoing [-p] [dest]::
428 outgoing [-p] [dest]::
429 指定された行き先のリポジトリかデフォルトで push するリポジトリ
429 指定された行き先のリポジトリかデフォルトで push するリポジトリ
430 中に見付からなかったチェンジセットを表示します。これらは push
430 中に見付からなかったチェンジセットを表示します。これらは push
431 が要求されたときに push されるであろうチェンジセットです。
431 が要求されたときに push されるであろうチェンジセットです。
432
432
433 オプション:
433 オプション:
434 -p, --patch パッチを表示します
434 -p, --patch パッチを表示します
435
435
436 別名: out
436 別名: out
437
437
438 parents::
438 parents::
439 作業ディレクトリの親リビジョンを表示します。
439 作業ディレクトリの親リビジョンを表示します。
440
440
441 paths [NAME]::
441 paths [NAME]::
442 シンボルのパス名である NAME の行き先を表示します。もしシンボル
442 シンボルのパス名である NAME の行き先を表示します。もしシンボル
443 名が指定されなければ、利用可能なシンボル名の行き先を表示します。
443 名が指定されなければ、利用可能なシンボル名の行き先を表示します。
444
444
445 パス名は /etc/mercurial/hgrc と $HOME/.hgrc の [paths] セクショ
445 パス名は /etc/mercurial/hgrc と $HOME/.hgrc の [paths] セクショ
446 ンで定義されます。もしリポジトリの内部で実行された場
446 ンで定義されます。もしリポジトリの内部で実行された場
447 合、.hg/hgrc も使用されます。
447 合、.hg/hgrc も使用されます。
448
448
449 pull <repository path>::
449 pull <repository path>::
450 リモートのリポジトリの変更をローカルのリポジトリに pull します。
450 リモートのリポジトリの変更をローカルのリポジトリに pull します。
451
451
452 これは指定されたパスや URL にあるリポジトリの全ての変更を見つ
452 これは指定されたパスや URL にあるリポジトリの全ての変更を見つ
453 けて、それらをローカルのリポジトリに追加します。デフォルトでは、
453 けて、それらをローカルのリポジトリに追加します。デフォルトでは、
454 これは作業ディレクトリのプロジェクトのコピーを更新しません。
454 これは作業ディレクトリのプロジェクトのコピーを更新しません。
455
455
456 有効な URL の次の形式です:
456 有効な URL の次の形式です:
457
457
458 local/filesystem/path
458 local/filesystem/path
459 http://[user@]host[:port][/path]
459 http://[user@]host[:port][/path]
460 https://[user@]host[:port][/path]
460 https://[user@]host[:port][/path]
461 ssh://[user@]host[:port][/path]
461 ssh://[user@]host[:port][/path]
462
462
463 SSH は行き先のマシンのシェルアカウントと、リモートのパスにhg
463 SSH は行き先のマシンのシェルアカウントと、リモートのパスにhg
464 のコピーが必要になります。SSH を使用すると、パスはデフォルトで
464 のコピーが必要になります。SSH を使用すると、パスはデフォルトで
465 はリモートのユーザのホームディレクトリの相対パスになります; ファ
465 はリモートのユーザのホームディレクトリの相対パスになります; ファ
466 イルシステムのルートからの相対パスであることを指定するには、パ
466 イルシステムのルートからの相対パスであることを指定するには、パ
467 スの最初にスラッシュを 2つ使用してください。
467 スの最初にスラッシュを 2つ使用してください。
468
468
469 オプション:
469 オプション:
470 -u, --update pull の後に作業ディレクトリを tip に更新します
470 -u, --update pull の後に作業ディレクトリを tip に更新します
471 -e, --ssh 使用する ssh コマンドを指定します
471 -e, --ssh 使用する ssh コマンドを指定します
472 --remotecmd リモート側で使われる hg コマンドを指定します
472 --remotecmd リモート側で使われる hg コマンドを指定します
473
473
474 push <destination>::
474 push <destination>::
475 ローカルのリポジトリの変更を指定された行き先に push します。
475 ローカルのリポジトリの変更を指定された行き先に push します。
476
476
477 これは pull と対称的な操作です。これは現在のリポジトリの変更を
477 これは pull と対称的な操作です。これは現在のリポジトリの変更を
478 他のリポジトリへ移すのに役立ちます。もし行き先がローカルであれ
478 他のリポジトリへ移すのに役立ちます。もし行き先がローカルであれ
479 ば、これはそのディレクトリから現在のディレクトリに対して pull
479 ば、これはそのディレクトリから現在のディレクトリに対して pull
480 するのと同じです。
480 するのと同じです。
481
481
482 デフォルトでは、push は実行した結果リモートのヘッドの数が増え
482 デフォルトでは、push は実行した結果リモートのヘッドの数が増え
483 るならば、実行を拒否します。これはたいていクライアントが push
483 るならば、実行を拒否します。これはたいていクライアントが push
484 する前に sync とmerge を忘れていることを意味します。
484 する前に sync とmerge を忘れていることを意味します。
485
485
486 有効な URL は次の形式です:
486 有効な URL は次の形式です:
487
487
488 local/filesystem/path
488 local/filesystem/path
489 ssh://[user@]host[:port][/path]
489 ssh://[user@]host[:port][/path]
490
490
491 SSH は行き先のマシンのシェルアカウントと、リモートのパスに hg
491 SSH は行き先のマシンのシェルアカウントと、リモートのパスに hg
492 のコピーが必要になります。
492 のコピーが必要になります。
493
493
494 オプション:
494 オプション:
495
495
496 -f, --force update を強行します
496 -f, --force update を強行します
497 -e, --ssh 使用される ssh コマンドを指定します
497 -e, --ssh 使用される ssh コマンドを指定します
498 --remotecmd リモート側で実行される hg コマンドを指定します
498 --remotecmd リモート側で実行される hg コマンドを指定します
499
499
500 rawcommit [-p -d -u -F -m -l]::
500 rawcommit [-p -d -u -F -m -l]::
501 低レベルのコミットで、ヘルパースクリプト中で使用されます。
501 低レベルのコミットで、ヘルパースクリプト中で使用されます。
502
502
503 このコマンドは通常のユーザに使われることは想定していません。こ
503 このコマンドは通常のユーザに使われることは想定していません。こ
504 れは主に他の SCM からインポートするときに便利です。
504 れは主に他の SCM からインポートするときに便利です。
505
505
506 recover::
506 recover::
507 中断された commit や pull から復帰します。
507 中断された commit や pull から復帰します。
508
508
509 このコマンドは中断された操作からリポジトリの状態を修整しようと
509 このコマンドは中断された操作からリポジトリの状態を修整しようと
510 試みます。これは Mercurial がそうするよう提案したときのみ必要
510 試みます。これは Mercurial がそうするよう提案したときのみ必要
511 でしょう。
511 でしょう。
512
512
513 remove [options] [files ...]::
513 remove [options] [files ...]::
514 指定されたファイルをリポジトリから削除することを予定します。
514 指定されたファイルをリポジトリから削除することを予定します。
515
515
516 このコマンドはファイルを次のコミット時に削除することを予定しま
516 このコマンドはファイルを次のコミット時に削除することを予定しま
517 す。このコマンドはファイルを現在の枝から取り除くだけで、プロジェ
517 す。このコマンドはファイルを現在の枝から取り除くだけで、プロジェ
518 クトの履歴全体からは削除しません。もしファイルが作業ディレクト
518 クトの履歴全体からは削除しません。もしファイルが作業ディレクト
519 リ中にまだ存在していれば、それらは作業ディレクトリから削除され
519 リ中にまだ存在していれば、それらは作業ディレクトリから削除され
520 ます。
520 ます。
521
521
522 別名: rm
522 別名: rm
523
523
524 rename <source ...> <dest>::
524 rename <source ...> <dest>::
525 コピー先をコピー元のコピーのコピーであると印をつけます; コピー
525 コピー先をコピー元のコピーのコピーであると印をつけます; コピー
526 元に削除の印をつけます。もしコピー先がディレクトリであれば、コ
526 元に削除の印をつけます。もしコピー先がディレクトリであれば、コ
527 ピーはそのディレクトリ中に置かれます。もしコピー先がファイルな
527 ピーはそのディレクトリ中に置かれます。もしコピー先がファイルな
528 ら、コピー元は 1 つのみ指定可能です。
528 ら、コピー元は 1 つのみ指定可能です。
529
529
530 デフォルトでは、このコマンドはファイルがその作業ディレクトリに
530 デフォルトでは、このコマンドはファイルがその作業ディレクトリに
531 あるものとしてその内容をコピーします。もし --after と一緒に呼
531 あるものとしてその内容をコピーします。もし --after と一緒に呼
532 び出されれば、操作は記録されますが、コピーは実行されません。
532 び出されれば、操作は記録されますが、コピーは実行されません。
533
533
534 このコマンドは次のコミット時に効果を持ちます。
534 このコマンドは次のコミット時に効果を持ちます。
535
535
536 注意: このコマンドは実験的です。リネームされたファイルを適切に
536 注意: このコマンドは実験的です。リネームされたファイルを適切に
537 記録できますが、この情報はマージによってまだ完全には使われませ
537 記録できますが、この情報はマージによってまだ完全には使われませ
538 んし、ログで完全に報告されることもありません。
538 んし、ログで完全に報告されることもありません。
539
539
540 オプション:
540 オプション:
541 -A, --after 既に発生したリネームを記録します
541 -A, --after 既に発生したリネームを記録します
542 -f, --force 既存の変更されたファイルに無理矢理コピーし
542 -f, --force 既存の変更されたファイルに無理矢理コピーし
543 ます
543 ます
544 -p, --parents コピー先にコピー元のパスを追加します
544 -p, --parents コピー先にコピー元のパスを追加します
545
545
546 別名: mv
546 別名: mv
547
547
548 revert [names ...]::
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 -r, --rev <rev> 元に戻す先のリビジョンを指定します
563 -r, --rev <rev> 元に戻す先のリビジョンを指定します
564 -n, --nonrecursive サブディレクトリを再帰的に辿らないように
564 -n, --nonrecursive サブディレクトリを再帰的に辿らないように
565 します
565 します
566
566
567 root::
567 root::
568 現在のリポジトリのルートディレクトリを表示します。
568 現在のリポジトリのルートディレクトリを表示します。
569
569
570 serve [options]::
570 serve [options]::
571 ローカルの HTTP リポジトリと pull サーバを起動します。
571 ローカルの HTTP リポジトリと pull サーバを起動します。
572
572
573 デフォルトでは、サーバはアクセスログを標準出力に、エラーログを
573 デフォルトでは、サーバはアクセスログを標準出力に、エラーログを
574 標準エラー出力に出力します。ファイルにログを取るには "-A" と
574 標準エラー出力に出力します。ファイルにログを取るには "-A" と
575 "-E" オプションを使ってください。
575 "-E" オプションを使ってください。
576
576
577 オプション:
577 オプション:
578 -A, --accesslog <file> アクセスログが出力されるファイルの名前
578 -A, --accesslog <file> アクセスログが出力されるファイルの名前
579 を指定します
579 を指定します
580 -E, --errorlog <file> エラーログが出力されるファイルの名前を
580 -E, --errorlog <file> エラーログが出力されるファイルの名前を
581 指定します
581 指定します
582 -a, --address <addr> 使用するアドレスを指定します
582 -a, --address <addr> 使用するアドレスを指定します
583 -p, --port <n> 使用するポートを指定します
583 -p, --port <n> 使用するポートを指定します
584 (デフォルト: 8000)
584 (デフォルト: 8000)
585 -n, --name <name> ウェブページで表示する名前を指定します
585 -n, --name <name> ウェブページで表示する名前を指定します
586 (デフォルト: working dir)
586 (デフォルト: working dir)
587 -t, --templatedir <path> 使用するウェブの雛型を指定します
587 -t, --templatedir <path> 使用するウェブの雛型を指定します
588 -6, --ipv6 IPv4 に加えて IPv6 も使用します
588 -6, --ipv6 IPv4 に加えて IPv6 も使用します
589
589
590 status [options] [files]::
590 status [options] [files]::
591 作業ディレクトリ中の変更されたファイルを表示します。名前が指定
591 作業ディレクトリ中の変更されたファイルを表示します。名前が指定
592 されなければ、全てのファイルが表示されます。名前が指定されれば、
592 されなければ、全てのファイルが表示されます。名前が指定されれば、
593 指定された名前にマッチしたファイルのみが表示されます。
593 指定された名前にマッチしたファイルのみが表示されます。
594
594
595 ファイルの状態を表示するのに使われる記号:
595 ファイルの状態を表示するのに使われる記号:
596
596
597 M = 変更されました
597 M = 変更されました
598 A = 追加されました
598 A = 追加されました
599 R = 削除されました
599 R = 削除されました
600 ? = バージョン管理下にありません
600 ? = バージョン管理下にありません
601
601
602 オプション:
602 オプション:
603
603
604 -m, --modified 変更されたファイルのみを表示します
604 -m, --modified 変更されたファイルのみを表示します
605 -a, --added 追加されたファイルのみを表示します
605 -a, --added 追加されたファイルのみを表示します
606 -r, --removed 削除されたファイルのみを表示します
606 -r, --removed 削除されたファイルのみを表示します
607 -u, --unknown 不明な(バージョン管理下にない)ファイルのみ
607 -u, --unknown 不明な(バージョン管理下にない)ファイルのみ
608 を表示します
608 を表示します
609 -n, --no-status 状態を示す接頭辞を隠します
609 -n, --no-status 状態を示す接頭辞を隠します
610 -0, --print0 xargs と一緒に使うために、ファイル名を NUL
610 -0, --print0 xargs と一緒に使うために、ファイル名を NUL
611 で終えます
611 で終えます
612 -I, --include <pat> 与えられたパターンにマッチした名前を含めま
612 -I, --include <pat> 与えられたパターンにマッチした名前を含めま
613
613
614 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外し
614 -X, --exclude <pat> 与えられたパターンにマッチした名前を除外し
615 ます
615 ます
616
616
617 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
617 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
618 特定のリビジョンに <name> を使って名前を付けます。
618 特定のリビジョンに <name> を使って名前を付けます。
619
619
620 タグはリポジトリの特定のリビジョンに名前を付けるために使われ、
620 タグはリポジトリの特定のリビジョンに名前を付けるために使われ、
621 そして異なるリビジョンを比較したり、重要な以前のバージョンに戻っ
621 そして異なるリビジョンを比較したり、重要な以前のバージョンに戻っ
622 たり、リリース等の分岐点に印をつけたりするのに便利です。
622 たり、リリース等の分岐点に印をつけたりするのに便利です。
623
623
624 もしバージョンが指定されなければ、tip が使われます。
624 もしバージョンが指定されなければ、tip が使われます。
625
625
626 バージョン管理、配布、タグのマージを楽にするために、それらは
626 バージョン管理、配布、タグのマージを楽にするために、それらは
627 ".hgtags" という名前のファイルに格納され、他のプロジェクトのファ
627 ".hgtags" という名前のファイルに格納され、他のプロジェクトのファ
628 イルと同様に扱ったり、必要であれば手で編集できます。
628 イルと同様に扱ったり、必要であれば手で編集できます。
629
629
630 オプション:
630 オプション:
631 -l, --local タグをローカルにします
631 -l, --local タグをローカルにします
632 -m, --message <text> タグをコミットしたときのログのエントリの
632 -m, --message <text> タグをコミットしたときのログのエントリの
633 メッセージを指定します
633 メッセージを指定します
634 -d, --date <datecode> コミットの日付を指定します
634 -d, --date <datecode> コミットの日付を指定します
635 -u, --user <user> コミットするユーザを指定します
635 -u, --user <user> コミットするユーザを指定します
636
636
637 注意: ローカルのタグはバージョン管理や配布されることはなく、ま
637 注意: ローカルのタグはバージョン管理や配布されることはなく、ま
638 た. hg/localtags ファイルに格納されます。もし同じ名前のローカ
638 た. hg/localtags ファイルに格納されます。もし同じ名前のローカ
639 ルのタグと公開されたタグがあれば、ローカルのタグが使われます。
639 ルのタグと公開されたタグがあれば、ローカルのタグが使われます。
640
640
641 tags::
641 tags::
642 リポジトリのタグを列挙します。
642 リポジトリのタグを列挙します。
643
643
644 これは通常のタグとローカルのタグを両方列挙します。
644 これは通常のタグとローカルのタグを両方列挙します。
645
645
646 tip::
646 tip::
647 tip のリビジョンを表示します。
647 tip のリビジョンを表示します。
648
648
649 unbundle <file>::
649 unbundle <file>::
650 (実験的)
650 (実験的)
651
651
652 bundle コマンドで生成された、圧縮済みチェンジグループファイル
652 bundle コマンドで生成された、圧縮済みチェンジグループファイル
653 を適用します。
653 を適用します。
654
654
655 undo::
655 undo::
656 最後の commit や pull の処理を取り消します。
656 最後の commit や pull の処理を取り消します。
657
657
658 リポジトリの最後の pull や commit 処理を巻戻し、プロジェクトを
658 リポジトリの最後の pull や commit 処理を巻戻し、プロジェクトを
659 それより前の状態に戻します。
659 それより前の状態に戻します。
660
660
661 このコマンドは注意して使ってください。まだ 1回の undo だけで、
661 このコマンドは注意して使ってください。まだ 1回の undo だけで、
662 redo はありません。
662 redo はありません。
663
663
664 このコマンドは公開したリポジトリで使われることは想定していませ
664 このコマンドは公開したリポジトリで使われることは想定していませ
665 ん。いったん他のユーザから pull で変更が見えるようになれば、ロー
665 ん。いったん他のユーザから pull で変更が見えるようになれば、ロー
666 カルでそれを取り消しても意味がありません。
666 カルでそれを取り消しても意味がありません。
667
667
668 update [-m -C] [revision]::
668 update [-m -C] [revision]::
669 作業ディレクトリを指定されたリビジョンに更新します。
669 作業ディレクトリを指定されたリビジョンに更新します。
670
670
671 デフォルトでは、更新によりローカルの変更をマージしたり破棄した
671 デフォルトでは、更新によりローカルの変更をマージしたり破棄した
672 りすることが必要となるとき、update はそれを拒否します。
672 りすることが必要となるとき、update はそれを拒否します。
673
673
674 -m オプションで、マージが実行されます。
674 -m オプションで、マージが実行されます。
675
675
676 -C オプションで、ローカルの変更が失われます。
676 -C オプションで、ローカルの変更が失われます。
677
677
678 オプション:
678 オプション:
679 -m, --merge 枝のマージを許可します
679 -m, --merge 枝のマージを許可します
680 -C, --clean ローカルで変更されたファイルを上書きします
680 -C, --clean ローカルで変更されたファイルを上書きします
681
681
682 別名: up checkout co
682 別名: up checkout co
683
683
684 verify::
684 verify::
685 現在のリポジトリの整合性を検証します。
685 現在のリポジトリの整合性を検証します。
686
686
687 これはリポジトリの整合性を全面的にチェックし、チェンジログの各
687 これはリポジトリの整合性を全面的にチェックし、チェンジログの各
688 エントリ、manifest, 管理下のファイルのハッシュとチェックサムを
688 エントリ、manifest, 管理下のファイルのハッシュとチェックサムを
689 検証し、またクロスリンクとインデクスの整合性も検証します。
689 検証し、またクロスリンクとインデクスの整合性も検証します。
690
690
691 ファイル名とパターン
691 ファイル名とパターン
692 ---------
692 ---------
693
693
694 Mercurial では同時に複数のファイルを識別するのに複数の記法が使
694 Mercurial では同時に複数のファイルを識別するのに複数の記法が使
695 えます。
695 えます。
696
696
697 デフォルトでは、Mercurial はファイル名をシェルのスタイルの拡張
697 デフォルトでは、Mercurial はファイル名をシェルのスタイルの拡張
698 glob パターンとして扱います。
698 glob パターンとして扱います。
699
699
700 別のパターン表記は明示的に指定する必要があります。
700 別のパターン表記は明示的に指定する必要があります。
701
701
702 パターンマッチングなしの単純なパス名を使うには、パス名を
702 パターンマッチングなしの単純なパス名を使うには、パス名を
703 "path:" で始めてください。これらのパス名は、現在のリポジトリの
703 "path:" で始めてください。これらのパス名は、現在のリポジトリの
704 ルートから完全にマッチしている必要があります。
704 ルートから完全にマッチしている必要があります。
705
705
706 拡張 glob を使うには、名前を "glob:" で始めてください。glob は
706 拡張 glob を使うには、名前を "glob:" で始めてください。glob は
707 現在のディレクトリのみに適用されます: "*.c" といった glob は現
707 現在のディレクトリのみに適用されます: "*.c" といった glob は現
708 在のディレクトリ中の ".c" で終わるファイルのみにマッチします。
708 在のディレクトリ中の ".c" で終わるファイルのみにマッチします。
709
709
710 サポートされている glob 文法の拡張はパスの分離記号を越えて全て
710 サポートされている glob 文法の拡張はパスの分離記号を越えて全て
711 の文字列にマッチする "**" と、"a または b" を意味する "{a,b}"
711 の文字列にマッチする "**" と、"a または b" を意味する "{a,b}"
712 です。
712 です。
713
713
714 Perl/Python の正規表現を使うには、名前を "re:" で始めてくださ
714 Perl/Python の正規表現を使うには、名前を "re:" で始めてくださ
715 い。正規表現によるマッチはリポジトリのルートの固定されています。
715 い。正規表現によるマッチはリポジトリのルートの固定されています。
716
716
717 単純な例:
717 単純な例:
718
718
719 path:foo/bar リポジトリのルートにある foo というディレクトリ
719 path:foo/bar リポジトリのルートにある foo というディレクトリ
720 の bar という名前
720 の bar という名前
721 path:path:name "path:name" という名前のファイルまたはディレク
721 path:path:name "path:name" という名前のファイルまたはディレク
722 トリ
722 トリ
723
723
724 Glob の例:
724 Glob の例:
725
725
726 glob:*.c 現在のディレクトリ中の ".c" で終わる全ての名前
726 glob:*.c 現在のディレクトリ中の ".c" で終わる全ての名前
727 *.c 現在のディレクトリ中の ".c" で終わる全ての名前
727 *.c 現在のディレクトリ中の ".c" で終わる全ての名前
728 **.c 現在のディレクトリと全てのサブディレクトリ中の
728 **.c 現在のディレクトリと全てのサブディレクトリ中の
729 ".c" で終わる全ての名前
729 ".c" で終わる全ての名前
730 foo/*.c ディレクトリ foo 中の ".c" で終わる全ての名前
730 foo/*.c ディレクトリ foo 中の ".c" で終わる全ての名前
731 foo/**.c ディレクトリ foo とその全てのサブディレクトリ中
731 foo/**.c ディレクトリ foo とその全てのサブディレクトリ中
732 の ".c" で終わる全ての名前
732 の ".c" で終わる全ての名前
733
733
734 正規表現の例:
734 正規表現の例:
735
735
736 re:.*\.c$ リポジトリ全体の中の ".c" で終わる全ての名前
736 re:.*\.c$ リポジトリ全体の中の ".c" で終わる全ての名前
737
737
738
738
739 単一のリビジョンの指定法
739 単一のリビジョンの指定法
740 -----------
740 -----------
741
741
742 Mercurial では個々のリビジョンを識別するのに複数の記法が使えま
742 Mercurial では個々のリビジョンを識別するのに複数の記法が使えま
743 す。
743 す。
744
744
745 単純な整数はリビジョンナンバーとして扱われます。負の整数はtip
745 単純な整数はリビジョンナンバーとして扱われます。負の整数はtip
746 からのオフセットとして扱われ、-1 が tip を表します。
746 からのオフセットとして扱われ、-1 が tip を表します。
747
747
748 40 桁の 16 進数の文字列はユニークなリビジョン識別子として扱わ
748 40 桁の 16 進数の文字列はユニークなリビジョン識別子として扱わ
749 れます。
749 れます。
750
750
751 40 桁より少ない 16 進数の文字列はユニークなリビジョン識別子と
751 40 桁より少ない 16 進数の文字列はユニークなリビジョン識別子と
752 して扱われ、短い形式の識別子と呼ばれます。短い形式の識別子はそ
752 して扱われ、短い形式の識別子と呼ばれます。短い形式の識別子はそ
753 れが完全な長さの識別子の接頭語であるときだけ有効です。
753 れが完全な長さの識別子の接頭語であるときだけ有効です。
754
754
755 他の文字列は全てタグ名として扱われます。タグはあるリビジョン識
755 他の文字列は全てタグ名として扱われます。タグはあるリビジョン識
756 別子に関連付けられたシンボル名です。タグ名は ":" 文字を含んで
756 別子に関連付けられたシンボル名です。タグ名は ":" 文字を含んで
757 はいけません。
757 はいけません。
758
758
759 リビジョン名 "tip" は特別なタグで、常に一番最新のリビジョンを
759 リビジョン名 "tip" は特別なタグで、常に一番最新のリビジョンを
760 指します。
760 指します。
761
761
762 複数のリビジョンの指定法
762 複数のリビジョンの指定法
763 -----------
763 -----------
764
764
765 Mercurial が 1つより多くのリビジョンを受けいれるとき、それらは
765 Mercurial が 1つより多くのリビジョンを受けいれるとき、それらは
766 別々に指定されるか、連続した範囲として ":" 文字で区切って与え
766 別々に指定されるか、連続した範囲として ":" 文字で区切って与え
767 られるかもれません。
767 られるかもれません。
768
768
769 範囲表記の構文は [BEGIN]:[END] で BEGIN と END はリビジョンの
769 範囲表記の構文は [BEGIN]:[END] で BEGIN と END はリビジョンの
770 識別子です。BEGIN も END も両方とも任意です。もし BEGIN が指定
770 識別子です。BEGIN も END も両方とも任意です。もし BEGIN が指定
771 されなければ、デフォルトでリビジョンナンバー 0 になります。も
771 されなければ、デフォルトでリビジョンナンバー 0 になります。も
772 し END が指定されなければ、デフォルトで tip になります。従って
772 し END が指定されなければ、デフォルトで tip になります。従って
773 範囲 ":" は "全てのリビジョン" を意味します。
773 範囲 ":" は "全てのリビジョン" を意味します。
774
774
775 もし BEGIN が END より大きければ、リビジョンは逆の順序として扱
775 もし BEGIN が END より大きければ、リビジョンは逆の順序として扱
776 われます。
776 われます。
777
777
778 範囲は閉区間として動作します。これは範囲が 3:5 は 3,4,5 になる
778 範囲は閉区間として動作します。これは範囲が 3:5 は 3,4,5 になる
779 ことを意味します。同様に、範囲 4:2 は 4,3,2 になります。
779 ことを意味します。同様に、範囲 4:2 は 4,3,2 になります。
780
780
781 環境変数
781 環境変数
782 ----
782 ----
783
783
784 HGEDITOR::
784 HGEDITOR::
785 これはコミッチ時に使われるエディタの名前です。デフォルトでは
785 これはコミッチ時に使われるエディタの名前です。デフォルトでは
786 EDITOR の値が使われます。
786 EDITOR の値が使われます。
787
787
788 (廃止予定です, .hgrc を使ってください)
788 (廃止予定です, .hgrc を使ってください)
789
789
790 HGMERGE::
790 HGMERGE::
791 merge 時の衝突を解決するのに使われる実行ファイルです。プログラ
791 merge 時の衝突を解決するのに使われる実行ファイルです。プログラ
792 ムは3 つの引数で実行されます: ローカルのファイル、リモートのファ
792 ムは3 つの引数で実行されます: ローカルのファイル、リモートのファ
793 イル、1 世代前のファイルです。
793 イル、1 世代前のファイルです。
794
794
795 デフォルトのプログラムは "hgmerge" で、これは Mercurial によっ
795 デフォルトのプログラムは "hgmerge" で、これは Mercurial によっ
796 て提供される常識的な設定のシェルスクリプトです。
796 て提供される常識的な設定のシェルスクリプトです。
797
797
798 (廃止予定です, .hgrc を使ってください)
798 (廃止予定です, .hgrc を使ってください)
799
799
800 HGUSER::
800 HGUSER::
801 これはコミット時の著者として使われる文字列です。
801 これはコミット時の著者として使われる文字列です。
802
802
803 (廃止予定です, .hgrc を使ってください)
803 (廃止予定です, .hgrc を使ってください)
804
804
805 EMAIL::
805 EMAIL::
806 もし HGUSER が設定されていなければ、これがコミット時の著者とし
806 もし HGUSER が設定されていなければ、これがコミット時の著者とし
807 て使われます。
807 て使われます。
808
808
809 LOGNAME::
809 LOGNAME::
810 もし HGUSER も EMAIL も設定されていなければ、コミット時の著者
810 もし HGUSER も EMAIL も設定されていなければ、コミット時の著者
811 としてLOGNAME が('@hostname' を付けた形で)使われます。
811 としてLOGNAME が('@hostname' を付けた形で)使われます。
812
812
813 EDITOR::
813 EDITOR::
814 これは hgmerge スクリプト中で使われるエディタの名前です。もし
814 これは hgmerge スクリプト中で使われるエディタの名前です。もし
815 HGEDITOR が設定されていなければ、コミット時のメッセージに使わ
815 HGEDITOR が設定されていなければ、コミット時のメッセージに使わ
816 れます。デフォルトは 'vi' です。
816 れます。デフォルトは 'vi' です。
817
817
818 PYTHONPATH::
818 PYTHONPATH::
819 これはインポートされるモジュールを見つけるために Python によっ
819 これはインポートされるモジュールを見つけるために Python によっ
820 て使われ、Mercurial がシステム全体にインストールされていなけれ
820 て使われ、Mercurial がシステム全体にインストールされていなけれ
821 ば、適切に設定される必要があるでしょう。
821 ば、適切に設定される必要があるでしょう。
822
822
823 ファイル
823 ファイル
824 ----
824 ----
825 .hgignore::
825 .hgignore::
826 このファイルは(1行ごとに) hg によって無視されるべきファイルを
826 このファイルは(1行ごとに) hg によって無視されるべきファイルを
827 記述した正規表現を含みます。
827 記述した正規表現を含みます。
828
828
829 .hgtags::
829 .hgtags::
830 このファイルはリポジトリの内容のタグ付けされたバージョンに一致
830 このファイルはリポジトリの内容のタグ付けされたバージョンに一致
831 したハッシュ値とテキストのタグ名(それぞれは空白で区切られます)を
831 したハッシュ値とテキストのタグ名(それぞれは空白で区切られます)を
832 含みます。
832 含みます。
833
833
834 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
834 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
835 このファイルはデフォルトの設定を含みます。.hg/hgrc の値は
835 このファイルはデフォルトの設定を含みます。.hg/hgrc の値は
836 $HOME/.hgrc の設定を上書きし、$HOME/.hgrc の設定はグローバルな
836 $HOME/.hgrc の設定を上書きし、$HOME/.hgrc の設定はグローバルな
837 /etc/mercurial/hgrc の設定を上書きします。これらのファイルの内
837 /etc/mercurial/hgrc の設定を上書きします。これらのファイルの内
838 容と書式の詳細については hgrc(5) を参照してください。
838 容と書式の詳細については hgrc(5) を参照してください。
839
839
840 バグ
840 バグ
841 --
841 --
842 沢山あるでしょうから、もしバグを見つけたらそれをメーリングリスト
842 沢山あるでしょうから、もしバグを見つけたらそれをメーリングリスト
843 (下の情報源を参照)に送ってください。
843 (下の情報源を参照)に送ってください。
844
844
845 関連項目
845 関連項目
846 ----
846 ----
847 hgrc(5)
847 hgrc(5)
848
848
849 著者
849 著者
850 --
850 --
851 Matt Mackall <mpm@selenic.com> により書かれました。
851 Matt Mackall <mpm@selenic.com> により書かれました。
852
852
853 情報源
853 情報源
854 ---
854 ---
855 http://selenic.com/mercurial[主なウェブサイト]
855 http://selenic.com/mercurial[主なウェブサイト]
856
856
857 http://www.serpentine.com/mercurial[Wiki サイト]
857 http://www.serpentine.com/mercurial[Wiki サイト]
858
858
859 http://selenic.com/hg[ソースコードのリポジトリ]
859 http://selenic.com/hg[ソースコードのリポジトリ]
860
860
861 http://selenic.com/mailman/listinfo/mercurial[メーリングリスト]
861 http://selenic.com/mailman/listinfo/mercurial[メーリングリスト]
862
862
863 著作権情報
863 著作権情報
864 -----
864 -----
865 Copyright (C) 2005-2007 Matt Mackall.
865 Copyright (C) 2005-2007 Matt Mackall.
866 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
866 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
867 認められます。
867 認められます。
@@ -1,204 +1,204 b''
1 HGRC(5)
1 HGRC(5)
2 =======
2 =======
3 Bryan O'Sullivan <bos@serpentine.com>
3 Bryan O'Sullivan <bos@serpentine.com>
4
4
5 名前
5 名前
6 --
6 --
7 hgrc - Mercurial の設定ファイル
7 hgrc - Mercurial の設定ファイル
8
8
9 書式
9 書式
10 --
10 --
11
11
12 Mercurial システムはその振舞いの正常を制御するのに、一連の設定ファ
12 Mercurial システムはその振舞いの正常を制御するのに、一連の設定ファ
13 イルを使用します。
13 イルを使用します。
14
14
15 ファイル
15 ファイル
16 ----
16 ----
17
17
18 Mercurial は 3つのファイルから設定を読みます:
18 Mercurial は 3つのファイルから設定を読みます:
19
19
20 /etc/mercurial/hgrc::
20 /etc/mercurial/hgrc::
21 このグローバルの設定ファイルのオプションは実行したユーザ、ディ
21 このグローバルの設定ファイルのオプションは実行したユーザ、ディ
22 レクトリを問わず全ての Mercurial コマンドに適用されます。
22 レクトリを問わず全ての Mercurial コマンドに適用されます。
23
23
24 $HOME/.hgrc::
24 $HOME/.hgrc::
25 ユーザ毎の設定オプションで、ディレクトリを問わず全ての
25 ユーザ毎の設定オプションで、ディレクトリを問わず全ての
26 Mercurial コマンドに適用されます。このファイルの値はグローバル
26 Mercurial コマンドに適用されます。このファイルの値はグローバル
27 の設定を上書きします。
27 の設定を上書きします。
28
28
29 <repo>/.hg/hgrc::
29 <repo>/.hg/hgrc::
30 リポジトリ毎の設定オプションで、そのリポジトリのみに適用されま
30 リポジトリ毎の設定オプションで、そのリポジトリのみに適用されま
31 す。このファイルはバージョン管理されず、 "clone" 操作で転送さ
31 す。このファイルはバージョン管理されず、 "clone" 操作で転送さ
32 れることもありません。このファイルの値はグローバルの設定とユー
32 れることもありません。このファイルの値はグローバルの設定とユー
33 ザ毎の設定を上書きします。
33 ザ毎の設定を上書きします。
34
34
35 構文
35 構文
36 --
36 --
37
37
38 設定ファイルは "[セクション]" ヘッダから始まるセクションと、それに
38 設定ファイルは "[セクション]" ヘッダから始まるセクションと、それに
39 続く"名前: 値"のエントリから成ります: "名前=値"も認められます。
39 続く"名前: 値"のエントリから成ります: "名前=値"も認められます。
40
40
41 [spam]
41 [spam]
42 eggs=ham
42 eggs=ham
43 green=
43 green=
44 eggs
44 eggs
45
45
46 各行は1つのエントリを含みます。もし次の行がインデントされていた場
46 各行は1つのエントリを含みます。もし次の行がインデントされていた場
47 合、それは前の行の続きとして扱われます。
47 合、それは前の行の続きとして扱われます。
48
48
49 先行する空白は値から取り除かれます。空行は読み飛ばされます。
49 先行する空白は値から取り除かれます。空行は読み飛ばされます。
50
50
51 オプションの値は同じセクションや、特別な DEFAULT セクションの別の
51 オプションの値は同じセクションや、特別な DEFAULT セクションの別の
52 値を参照するフォーマット文字列を含むことができます。
52 値を参照するフォーマット文字列を含むことができます。
53
53
54 "#" や ";" で始まる行は無視されるので、コメントとして使うことがで
54 "#" や ";" で始まる行は無視されるので、コメントとして使うことがで
55 きます。
55 きます。
56
56
57 セクション
57 セクション
58 -----
58 -----
59
59
60 このセクションは Merucurial の "hgrc" に使うことができる異なったセ
60 このセクションは Merucurial の "hgrc" に使うことができる異なったセ
61 クションのそれぞれの目的や可能なキー、そして取り得る値について記述
61 クションのそれぞれの目的や可能なキー、そして取り得る値について記述
62 します。
62 します。
63
63
64 decode/encode::
64 decode/encode::
65 checkout/checkin でファイルを転送するときのフィルターです。これ
65 checkout/checkin でファイルを転送するときのフィルターです。これ
66 は典型的には改行を処理したり、他の地域化/標準化に使われるでしょ
66 は典型的には改行を処理したり、他の地域化/標準化に使われるでしょ
67 う。
67 う。
68
68
69 フィルターはフィルターパターンとそれに続くフィルターコマンドから
69 フィルターはフィルターパターンとそれに続くフィルターコマンドから
70 なります。コマンドは標準入力からのデータを受け付け、変換したデー
70 なります。コマンドは標準入力からのデータを受け付け、変換したデー
71 タを標準出力に返す必要があります。
71 タを標準出力に返す必要があります。
72
72
73 例:
73 例:
74
74
75 [encode]
75 [encode]
76 # delta 圧縮を改善するためにチェックイン時に gzip ファイルを
76 # delta 圧縮を改善するためにチェックイン時に gzip ファイルを
77 # 伸長します
77 # 伸長します
78 # 注意: 必ずしも良いアイディアではありません。ただの例です
78 # 注意: 必ずしも良いアイディアではありません。ただの例です
79 *.gz = gunzip
79 *.gz = gunzip
80
80
81 [decode]
81 [decode]
82 # 作業ディレクトリに書き出すときにファイルを gzip で再圧縮します
82 # 作業ディレクトリに書き出すときにファイルを gzip で再圧縮します
83 *.gz = gzip
83 *.gz = gzip
84
84
85 hooks::
85 hooks::
86 コミットの開始、終了時など様々なアクションで自動的に実行されるコ
86 コミットの開始、終了時など様々なアクションで自動的に実行されるコ
87 マンドです。
87 マンドです。
88 changegroup;;
88 changegroup;;
89 push や pull でチェンジグループが加えられたあとに起動します。
89 push や pull でチェンジグループが加えられたあとに起動します。
90 commit;;
90 commit;;
91 チェンジセットが作成された後に起動します。新しく作成されたチェ
91 チェンジセットが作成された後に起動します。新しく作成されたチェ
92 ンジセットの ID が渡されます。
92 ンジセットの ID が渡されます。
93 precommit;;
93 precommit;;
94 コミット前に起動します。終了ステータス 0 によりコミットを続行
94 コミット前に起動します。終了ステータス 0 によりコミットを続行
95 します。非ゼロのステータスでコミットは失敗します。
95 します。非ゼロのステータスでコミットは失敗します。
96
96
97 http_proxy::
97 http_proxy::
98 HTTP プロキシを通してウェブを使った Mercurial のリポジトリにアク
98 HTTP プロキシを通してウェブを使った Mercurial のリポジトリにアク
99 セスするのに使われます。
99 セスするのに使われます。
100 host;;
100 host;;
101 プロキシサーバのホスト名と(オプションの)ポートで、例えば
101 プロキシサーバのホスト名と(オプションの)ポートで、例えば
102 "myproxy:8000"などです。
102 "myproxy:8000"などです。
103 no;;
103 no;;
104 オプションです。コンマで区切られたプロキシを通過すべきホスト名
104 オプションです。コンマで区切られたプロキシを通過すべきホスト名
105 のリストです。
105 のリストです。
106 passwd;;
106 passwd;;
107 オプションです。プロキシサーバの認証用のパスワードです。
107 オプションです。プロキシサーバの認証用のパスワードです。
108 user;;
108 user;;
109 オプションです。プロキシサーバの認証用のユーザ名です。
109 オプションです。プロキシサーバの認証用のユーザ名です。
110
110
111 paths::
111 paths::
112 リポジトリにシンボル名を割当てます。左側がシンボル名で、右側がリ
112 リポジトリにシンボル名を割当てます。左側がシンボル名で、右側がリ
113 ポジトリの場所を示すディレクトリや URL です。
113 ポジトリの場所を示すディレクトリや URL です。
114
114
115 ui::
115 ui::
116 ユーザインターフェースの設定です。
116 ユーザインターフェースの設定です。
117 debug;;
117 debug;;
118 デバッグ情報を表示します。True か False を取ります。デフォルト
118 デバッグ情報を表示します。True か False を取ります。デフォルト
119 では False です。
119 では False です。
120 editor;;
120 editor;;
121 コミット中に使用するエディタです。デフォルトは $EDITOR か
121 コミット中に使用するエディタです。デフォルトは $EDITOR か
122 "vi" です。
122 "vi" です。
123 interactive;;
123 interactive;;
124 ユーザに対してプロンプトを出すようにします。True か False を取
124 ユーザに対してプロンプトを出すようにします。True か False を取
125 ります。デフォルトでは True です。
125 ります。デフォルトでは True です。
126 merge;;
126 merge;;
127 手動での merge 中に衝突を解決するために使われるプログラムです。
127 手動での merge 中に衝突を解決するために使われるプログラムです。
128 デフォルトは "hgmerge" です。
128 デフォルトは "hgmerge" です。
129 quiet;;
129 quiet;;
130 表示される出力の量を減らします。True か False を取ります。デフォ
130 表示される出力の量を減らします。True か False を取ります。デフォ
131 ルトは False です。
131 ルトは False です。
132 remotecmd;;
132 remotecmd;;
133 clone/push/pull 操作で使われるリモートのコマンドです。デフォル
133 clone/push/pull 操作で使われるリモートのコマンドです。デフォル
134 トは 'hg' です。
134 トは 'hg' です。
135 ssh;;
135 ssh;;
136 SSH 接続で使われるコマンドです。デフォルトは 'ssh' です。
136 SSH 接続で使われるコマンドです。デフォルトは 'ssh' です。
137 username;;
137 username;;
138 コミットを実行したときに作成されるチェンジセットのコミッタです。
138 コミットを実行したときに作成されるチェンジセットのコミッタです。
139 一般的には人名と電子メールアドレスで、例えば "Fred Widget
139 一般的には人名と電子メールアドレスで、例えば "Fred Widget
140 <fred@example.com>" などです。デフォルトは $EMAIL か
140 <fred@example.com>" などです。デフォルトは $EMAIL か
141 username@hostname です。
141 username@hostname です。
142 verbose;;
142 verbose;;
143 表示される出力の量を増やします。True か False を取ります。デフォ
143 表示される出力の量を増やします。True か False を取ります。デフォ
144 ルトは False です。
144 ルトは False です。
145
145
146 web::
146 web::
147 ウェブインターフェイスの設定です。
147 ウェブインターフェイスの設定です。
148 accesslog;;
148 accesslog;;
149 アクセスログの出力先です。デフォルトでは標準出力です。
149 アクセスログの出力先です。デフォルトでは標準出力です。
150 address;;
150 address;;
151 バインドするインターフェイスアドレスです。デフォルトでは全てで
151 バインドするインターフェイスアドレスです。デフォルトでは全てで
152 す。
152 す。
153 allowbz2;;
153 allowbz2;;
154 リポジトリのリビジョンから .tar.bz2 をダウンロードさせるかどう
154 リポジトリのリビジョンから .tar.bz2 をダウンロードさせるかどう
155 かです。デフォルトでは false です。
155 かです。デフォルトでは false です。
156 allowgz;;
156 allowgz;;
157 リポジトリのリビジョンから .tar.gz をダウンロードさせるかどう
157 リポジトリのリビジョンから .tar.gz をダウンロードさせるかどう
158 かです。デフォルトでは false です。
158 かです。デフォルトでは false です。
159 allowpull;;
159 allowpull;;
160 リポジトリから pull させるかどうかです。デフォルトでは true で
160 リポジトリから pull させるかどうかです。デフォルトでは true で
161 す。
161 す。
162 allowzip;;
162 allowzip;;
163 リポジトリのリビジョンから .zip をダウンロードさせるかどうかで
163 リポジトリのリビジョンから .zip をダウンロードさせるかどうかで
164 す。デフォルトでは false です。この機能は一時ファイルを作成し
164 す。デフォルトでは false です。この機能は一時ファイルを作成し
165 ます。
165 ます。
166 description;;
166 description;;
167 リポジトリの目的や内容についてのテキストによる説明です。デフォ
167 リポジトリの目的や内容についてのテキストによる説明です。デフォ
168 ルトでは"unknown" です。
168 ルトでは"unknown" です。
169 errorlog;;
169 errorlog;;
170 エラーログの出力先です。デフォルトでは標準エラー出力です。
170 エラーログの出力先です。デフォルトでは標準エラー出力です。
171 ipv6;;
171 ipv6;;
172 IPv6 を使うかどうかです。デフォルトでは false です。
172 IPv6 を使うかどうかです。デフォルトでは false です。
173 name;;
173 name;;
174 ウェブインターフェイスを使うときのリポジトリの名前です。デフォ
174 ウェブインターフェイスを使うときのリポジトリの名前です。デフォ
175 ルトは現在の作業ディレクトリです。
175 ルトは現在の作業ディレクトリです。
176 maxchanges;;
176 maxchanges;;
177 チェンジログに記載する変更の最大数です。デフォルトでは 10 です。
177 チェンジログに記載する変更の最大数です。デフォルトでは 10 です。
178 maxfiles;;
178 maxfiles;;
179 チェンジセットに記載するファイルの最大数です。デフォルトでは
179 チェンジセットに記載するファイルの最大数です。デフォルトでは
180 10 です。
180 10 です。
181 port;;
181 port;;
182 リスンするポートです。デフォルト は 8000 です。
182 リスンするポートです。デフォルト は 8000 です。
183 style;;
183 style;;
184 使用するテンプレートマップのスタイルです。
184 使用するテンプレートマップのスタイルです。
185 templates;;
185 templates;;
186 HTML テンプレートの在処です。デフォルトではインストールしたと
186 HTML テンプレートの在処です。デフォルトではインストールしたと
187 きのパスです。
187 きのパスです。
188
188
189 著者
189 著者
190 --
190 --
191 Bryan O'Sullivan <bos@serpentine.com>.
191 Bryan O'Sullivan <bos@serpentine.com>.
192
192
193 Mercurial は Matt Mackall <mpm@selenic.com> により書かれました。
193 Mercurial は Matt Mackall <mpm@selenic.com> により書かれました。
194
194
195 関連項目
195 関連項目
196 ----
196 ----
197 hg(1)
197 hg(1)
198
198
199 COPYING
199 COPYING
200 -------
200 -------
201 このマニュアルの著作権は 2005 Bryan O'Sullivan です。
201 このマニュアルの著作権は 2005 Bryan O'Sullivan です。
202 Mercurial の著作権は 2005 Matt Mackall です。
202 Mercurial の著作権は 2005 Matt Mackall です。
203 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
203 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
204 認められます。
204 認められます。
@@ -1,168 +1,168 b''
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 #
2 #
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 # that removes files not known to mercurial
4 # that removes files not known to mercurial
5 #
5 #
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 # (http://www.red-bean.com/cvsutils/).
7 # (http://www.red-bean.com/cvsutils/).
8 #
8 #
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 # [extensions]
10 # [extensions]
11 # hgext.purge =
11 # hgext.purge =
12 #
12 #
13 # For help on the usage of "hg purge" use:
13 # For help on the usage of "hg purge" use:
14 # hg help purge
14 # hg help purge
15 #
15 #
16 # This program is free software; you can redistribute it and/or modify
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
19 # (at your option) any later version.
20 #
20 #
21 # This program is distributed in the hope that it will be useful,
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
24 # GNU General Public License for more details.
25 #
25 #
26 # You should have received a copy of the GNU General Public License
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
29
30 from mercurial import hg, util
30 from mercurial import hg, util
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 import os
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 abort_on_err=False, eol='\n',
35 abort_on_err=False, eol='\n',
36 force=False, include=None, exclude=None):
36 force=False, include=None, exclude=None):
37 def error(msg):
37 def error(msg):
38 if abort_on_err:
38 if abort_on_err:
39 raise util.Abort(msg)
39 raise util.Abort(msg)
40 else:
40 else:
41 ui.warn(_('warning: %s\n') % msg)
41 ui.warn(_('warning: %s\n') % msg)
42
42
43 def remove(remove_func, name):
43 def remove(remove_func, name):
44 if act:
44 if act:
45 try:
45 try:
46 remove_func(os.path.join(repo.root, name))
46 remove_func(os.path.join(repo.root, name))
47 except OSError, e:
47 except OSError, e:
48 error(_('%s cannot be removed') % name)
48 error(_('%s cannot be removed') % name)
49 else:
49 else:
50 ui.write('%s%s' % (name, eol))
50 ui.write('%s%s' % (name, eol))
51
51
52 directories = []
52 directories = []
53 files = []
53 files = []
54 missing = []
54 missing = []
55 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs,
55 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs,
56 include, exclude)
56 include, exclude)
57 for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
57 for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
58 ignored=ignored, directories=True):
58 ignored=ignored, directories=True):
59 if src == 'd':
59 if src == 'd':
60 directories.append(f)
60 directories.append(f)
61 elif src == 'm':
61 elif src == 'm':
62 missing.append(f)
62 missing.append(f)
63 elif src == 'f' and f not in repo.dirstate:
63 elif src == 'f' and f not in repo.dirstate:
64 files.append(f)
64 files.append(f)
65
65
66 _check_missing(ui, repo, missing, force)
66 _check_missing(ui, repo, missing, force)
67
67
68 directories.sort()
68 directories.sort()
69
69
70 for f in files:
70 for f in files:
71 if f not in repo.dirstate:
71 if f not in repo.dirstate:
72 ui.note(_('Removing file %s\n') % f)
72 ui.note(_('Removing file %s\n') % f)
73 remove(os.remove, f)
73 remove(os.remove, f)
74
74
75 for f in directories[::-1]:
75 for f in directories[::-1]:
76 if match(f) and not os.listdir(repo.wjoin(f)):
76 if match(f) and not os.listdir(repo.wjoin(f)):
77 ui.note(_('Removing directory %s\n') % f)
77 ui.note(_('Removing directory %s\n') % f)
78 remove(os.rmdir, f)
78 remove(os.rmdir, f)
79
79
80 def _check_missing(ui, repo, missing, force=False):
80 def _check_missing(ui, repo, missing, force=False):
81 """Abort if there is the chance of having problems with name-mangling fs
81 """Abort if there is the chance of having problems with name-mangling fs
82
82
83 In a name mangling filesystem (e.g. a case insensitive one)
83 In a name mangling filesystem (e.g. a case insensitive one)
84 dirstate.walk() can yield filenames different from the ones
84 dirstate.walk() can yield filenames different from the ones
85 stored in the dirstate. This already confuses the status and
85 stored in the dirstate. This already confuses the status and
86 add commands, but with purge this may cause data loss.
86 add commands, but with purge this may cause data loss.
87
87
88 To prevent this, _check_missing will abort if there are missing
88 To prevent this, _check_missing will abort if there are missing
89 files. The force option will let the user skip the check if he
89 files. The force option will let the user skip the check if he
90 knows it is safe.
90 knows it is safe.
91
91
92 Even with the force option this function will check if any of the
92 Even with the force option this function will check if any of the
93 missing files is still available in the working dir: if so there
93 missing files is still available in the working dir: if so there
94 may be some problem with the underlying filesystem, so it
94 may be some problem with the underlying filesystem, so it
95 aborts unconditionally."""
95 aborts unconditionally."""
96
96
97 found = [f for f in missing if util.lexists(repo.wjoin(f))]
97 found = [f for f in missing if util.lexists(repo.wjoin(f))]
98
98
99 if found:
99 if found:
100 if not ui.quiet:
100 if not ui.quiet:
101 ui.warn(_("The following tracked files weren't listed by the "
101 ui.warn(_("The following tracked files weren't listed by the "
102 "filesystem, but could still be found:\n"))
102 "filesystem, but could still be found:\n"))
103 for f in found:
103 for f in found:
104 ui.warn("%s\n" % f)
104 ui.warn("%s\n" % f)
105 if util.checkfolding(repo.path):
105 if util.checkfolding(repo.path):
106 ui.warn(_("This is probably due to a case-insensitive "
106 ui.warn(_("This is probably due to a case-insensitive "
107 "filesystem\n"))
107 "filesystem\n"))
108 raise util.Abort(_("purging on name mangling filesystems is not "
108 raise util.Abort(_("purging on name mangling filesystems is not "
109 "yet fully supported"))
109 "yet fully supported"))
110
110
111 if missing and not force:
111 if missing and not force:
112 raise util.Abort(_("there are missing files in the working dir and "
112 raise util.Abort(_("there are missing files in the working dir and "
113 "purge still has problems with them due to name "
113 "purge still has problems with them due to name "
114 "mangling filesystems. "
114 "mangling filesystems. "
115 "Use --force if you know what you are doing"))
115 "Use --force if you know what you are doing"))
116
116
117
117
118 def purge(ui, repo, *dirs, **opts):
118 def purge(ui, repo, *dirs, **opts):
119 '''removes files not tracked by mercurial
119 '''removes files not tracked by mercurial
120
120
121 Delete files not known to mercurial, this is useful to test local and
121 Delete files not known to mercurial, this is useful to test local and
122 uncommitted changes in the otherwise clean source tree.
122 uncommitted changes in the otherwise clean source tree.
123
123
124 This means that purge will delete:
124 This means that purge will delete:
125 - Unknown files: files marked with "?" by "hg status"
125 - Unknown files: files marked with "?" by "hg status"
126 - Ignored files: files usually ignored by Mercurial because they match
126 - Ignored files: files usually ignored by Mercurial because they match
127 a pattern in a ".hgignore" file
127 a pattern in a ".hgignore" file
128 - Empty directories: in fact Mercurial ignores directories unless they
128 - Empty directories: in fact Mercurial ignores directories unless they
129 contain files under source control managment
129 contain files under source control managment
130 But it will leave untouched:
130 But it will leave untouched:
131 - Unmodified tracked files
131 - Unmodified tracked files
132 - Modified tracked files
132 - Modified tracked files
133 - New files added to the repository (with "hg add")
133 - New files added to the repository (with "hg add")
134
134
135 If directories are given on the command line, only files in these
135 If directories are given on the command line, only files in these
136 directories are considered.
136 directories are considered.
137
137
138 Be careful with purge, you could irreversibly delete some files you
138 Be careful with purge, you could irreversibly delete some files you
139 forgot to add to the repository. If you only want to print the list of
139 forgot to add to the repository. If you only want to print the list of
140 files that this program would delete use the --print option.
140 files that this program would delete use the --print option.
141 '''
141 '''
142 act = not opts['print']
142 act = not opts['print']
143 ignored = bool(opts['all'])
143 ignored = bool(opts['all'])
144 abort_on_err = bool(opts['abort_on_err'])
144 abort_on_err = bool(opts['abort_on_err'])
145 eol = opts['print0'] and '\0' or '\n'
145 eol = opts['print0'] and '\0' or '\n'
146 if eol == '\0':
146 if eol == '\0':
147 # --print0 implies --print
147 # --print0 implies --print
148 act = False
148 act = False
149 force = bool(opts['force'])
149 force = bool(opts['force'])
150 include = opts['include']
150 include = opts['include']
151 exclude = opts['exclude']
151 exclude = opts['exclude']
152 dopurge(ui, repo, dirs, act, ignored, abort_on_err,
152 dopurge(ui, repo, dirs, act, ignored, abort_on_err,
153 eol, force, include, exclude)
153 eol, force, include, exclude)
154
154
155
155
156 cmdtable = {
156 cmdtable = {
157 'purge|clean':
157 'purge|clean':
158 (purge,
158 (purge,
159 [('a', 'abort-on-err', None, _('abort if an error occurs')),
159 [('a', 'abort-on-err', None, _('abort if an error occurs')),
160 ('', 'all', None, _('purge ignored files too')),
160 ('', 'all', None, _('purge ignored files too')),
161 ('f', 'force', None, _('purge even when missing files are detected')),
161 ('f', 'force', None, _('purge even when missing files are detected')),
162 ('p', 'print', None, _('print the file names instead of deleting them')),
162 ('p', 'print', None, _('print the file names instead of deleting them')),
163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
164 ' (implies -p)')),
164 ' (implies -p)')),
165 ('I', 'include', [], _('include names matching the given patterns')),
165 ('I', 'include', [], _('include names matching the given patterns')),
166 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
166 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
167 _('hg purge [OPTION]... [DIR]...'))
167 _('hg purge [OPTION]... [DIR]...'))
168 }
168 }
@@ -1,1945 +1,1945 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import repo, changegroup
10 import repo, changegroup
11 import changelog, dirstate, filelog, manifest, context
11 import changelog, dirstate, filelog, manifest, context
12 import re, lock, transaction, tempfile, stat, mdiff, errno, ui
12 import re, lock, transaction, tempfile, stat, mdiff, errno, ui
13 import os, revlog, time, util, extensions, hook
13 import os, revlog, time, util, extensions, hook
14
14
15 class localrepository(repo.repository):
15 class localrepository(repo.repository):
16 capabilities = ('lookup', 'changegroupsubset')
16 capabilities = ('lookup', 'changegroupsubset')
17 supported = ('revlogv1', 'store')
17 supported = ('revlogv1', 'store')
18
18
19 def __del__(self):
19 def __del__(self):
20 self.transhandle = None
20 self.transhandle = None
21 def __init__(self, parentui, path=None, create=0):
21 def __init__(self, parentui, path=None, create=0):
22 repo.repository.__init__(self)
22 repo.repository.__init__(self)
23 self.path = path
23 self.path = path
24 self.root = os.path.realpath(path)
24 self.root = os.path.realpath(path)
25 self.path = os.path.join(self.root, ".hg")
25 self.path = os.path.join(self.root, ".hg")
26 self.origroot = path
26 self.origroot = path
27 self.opener = util.opener(self.path)
27 self.opener = util.opener(self.path)
28 self.wopener = util.opener(self.root)
28 self.wopener = util.opener(self.root)
29
29
30 if not os.path.isdir(self.path):
30 if not os.path.isdir(self.path):
31 if create:
31 if create:
32 if not os.path.exists(path):
32 if not os.path.exists(path):
33 os.mkdir(path)
33 os.mkdir(path)
34 os.mkdir(self.path)
34 os.mkdir(self.path)
35 requirements = ["revlogv1"]
35 requirements = ["revlogv1"]
36 if parentui.configbool('format', 'usestore', True):
36 if parentui.configbool('format', 'usestore', True):
37 os.mkdir(os.path.join(self.path, "store"))
37 os.mkdir(os.path.join(self.path, "store"))
38 requirements.append("store")
38 requirements.append("store")
39 # create an invalid changelog
39 # create an invalid changelog
40 self.opener("00changelog.i", "a").write(
40 self.opener("00changelog.i", "a").write(
41 '\0\0\0\2' # represents revlogv2
41 '\0\0\0\2' # represents revlogv2
42 ' dummy changelog to prevent using the old repo layout'
42 ' dummy changelog to prevent using the old repo layout'
43 )
43 )
44 reqfile = self.opener("requires", "w")
44 reqfile = self.opener("requires", "w")
45 for r in requirements:
45 for r in requirements:
46 reqfile.write("%s\n" % r)
46 reqfile.write("%s\n" % r)
47 reqfile.close()
47 reqfile.close()
48 else:
48 else:
49 raise repo.RepoError(_("repository %s not found") % path)
49 raise repo.RepoError(_("repository %s not found") % path)
50 elif create:
50 elif create:
51 raise repo.RepoError(_("repository %s already exists") % path)
51 raise repo.RepoError(_("repository %s already exists") % path)
52 else:
52 else:
53 # find requirements
53 # find requirements
54 try:
54 try:
55 requirements = self.opener("requires").read().splitlines()
55 requirements = self.opener("requires").read().splitlines()
56 except IOError, inst:
56 except IOError, inst:
57 if inst.errno != errno.ENOENT:
57 if inst.errno != errno.ENOENT:
58 raise
58 raise
59 requirements = []
59 requirements = []
60 # check them
60 # check them
61 for r in requirements:
61 for r in requirements:
62 if r not in self.supported:
62 if r not in self.supported:
63 raise repo.RepoError(_("requirement '%s' not supported") % r)
63 raise repo.RepoError(_("requirement '%s' not supported") % r)
64
64
65 # setup store
65 # setup store
66 if "store" in requirements:
66 if "store" in requirements:
67 self.encodefn = util.encodefilename
67 self.encodefn = util.encodefilename
68 self.decodefn = util.decodefilename
68 self.decodefn = util.decodefilename
69 self.spath = os.path.join(self.path, "store")
69 self.spath = os.path.join(self.path, "store")
70 else:
70 else:
71 self.encodefn = lambda x: x
71 self.encodefn = lambda x: x
72 self.decodefn = lambda x: x
72 self.decodefn = lambda x: x
73 self.spath = self.path
73 self.spath = self.path
74 self.sopener = util.encodedopener(util.opener(self.spath), self.encodefn)
74 self.sopener = util.encodedopener(util.opener(self.spath), self.encodefn)
75
75
76 self.ui = ui.ui(parentui=parentui)
76 self.ui = ui.ui(parentui=parentui)
77 try:
77 try:
78 self.ui.readconfig(self.join("hgrc"), self.root)
78 self.ui.readconfig(self.join("hgrc"), self.root)
79 extensions.loadall(self.ui)
79 extensions.loadall(self.ui)
80 except IOError:
80 except IOError:
81 pass
81 pass
82
82
83 self.tagscache = None
83 self.tagscache = None
84 self.branchcache = None
84 self.branchcache = None
85 self.nodetagscache = None
85 self.nodetagscache = None
86 self.filterpats = {}
86 self.filterpats = {}
87 self.transhandle = None
87 self.transhandle = None
88
88
89 def __getattr__(self, name):
89 def __getattr__(self, name):
90 if name == 'changelog':
90 if name == 'changelog':
91 self.changelog = changelog.changelog(self.sopener)
91 self.changelog = changelog.changelog(self.sopener)
92 self.sopener.defversion = self.changelog.version
92 self.sopener.defversion = self.changelog.version
93 return self.changelog
93 return self.changelog
94 if name == 'manifest':
94 if name == 'manifest':
95 self.changelog
95 self.changelog
96 self.manifest = manifest.manifest(self.sopener)
96 self.manifest = manifest.manifest(self.sopener)
97 return self.manifest
97 return self.manifest
98 if name == 'dirstate':
98 if name == 'dirstate':
99 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
99 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
100 return self.dirstate
100 return self.dirstate
101 else:
101 else:
102 raise AttributeError, name
102 raise AttributeError, name
103
103
104 def url(self):
104 def url(self):
105 return 'file:' + self.root
105 return 'file:' + self.root
106
106
107 def hook(self, name, throw=False, **args):
107 def hook(self, name, throw=False, **args):
108 return hook.hook(self.ui, self, name, throw, **args)
108 return hook.hook(self.ui, self, name, throw, **args)
109
109
110 tag_disallowed = ':\r\n'
110 tag_disallowed = ':\r\n'
111
111
112 def _tag(self, name, node, message, local, user, date, parent=None):
112 def _tag(self, name, node, message, local, user, date, parent=None):
113 use_dirstate = parent is None
113 use_dirstate = parent is None
114
114
115 for c in self.tag_disallowed:
115 for c in self.tag_disallowed:
116 if c in name:
116 if c in name:
117 raise util.Abort(_('%r cannot be used in a tag name') % c)
117 raise util.Abort(_('%r cannot be used in a tag name') % c)
118
118
119 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
119 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
120
120
121 def writetag(fp, name, munge, prevtags):
121 def writetag(fp, name, munge, prevtags):
122 if prevtags and prevtags[-1] != '\n':
122 if prevtags and prevtags[-1] != '\n':
123 fp.write('\n')
123 fp.write('\n')
124 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
124 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
125 fp.close()
125 fp.close()
126 self.hook('tag', node=hex(node), tag=name, local=local)
126 self.hook('tag', node=hex(node), tag=name, local=local)
127
127
128 prevtags = ''
128 prevtags = ''
129 if local:
129 if local:
130 try:
130 try:
131 fp = self.opener('localtags', 'r+')
131 fp = self.opener('localtags', 'r+')
132 except IOError, err:
132 except IOError, err:
133 fp = self.opener('localtags', 'a')
133 fp = self.opener('localtags', 'a')
134 else:
134 else:
135 prevtags = fp.read()
135 prevtags = fp.read()
136
136
137 # local tags are stored in the current charset
137 # local tags are stored in the current charset
138 writetag(fp, name, None, prevtags)
138 writetag(fp, name, None, prevtags)
139 return
139 return
140
140
141 if use_dirstate:
141 if use_dirstate:
142 try:
142 try:
143 fp = self.wfile('.hgtags', 'rb+')
143 fp = self.wfile('.hgtags', 'rb+')
144 except IOError, err:
144 except IOError, err:
145 fp = self.wfile('.hgtags', 'ab')
145 fp = self.wfile('.hgtags', 'ab')
146 else:
146 else:
147 prevtags = fp.read()
147 prevtags = fp.read()
148 else:
148 else:
149 try:
149 try:
150 prevtags = self.filectx('.hgtags', parent).data()
150 prevtags = self.filectx('.hgtags', parent).data()
151 except revlog.LookupError:
151 except revlog.LookupError:
152 pass
152 pass
153 fp = self.wfile('.hgtags', 'wb')
153 fp = self.wfile('.hgtags', 'wb')
154
154
155 # committed tags are stored in UTF-8
155 # committed tags are stored in UTF-8
156 writetag(fp, name, util.fromlocal, prevtags)
156 writetag(fp, name, util.fromlocal, prevtags)
157
157
158 if use_dirstate and self.dirstate.state('.hgtags') == '?':
158 if use_dirstate and self.dirstate.state('.hgtags') == '?':
159 self.add(['.hgtags'])
159 self.add(['.hgtags'])
160
160
161 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent)
161 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent)
162
162
163 self.hook('tag', node=hex(node), tag=name, local=local)
163 self.hook('tag', node=hex(node), tag=name, local=local)
164
164
165 return tagnode
165 return tagnode
166
166
167 def tag(self, name, node, message, local, user, date):
167 def tag(self, name, node, message, local, user, date):
168 '''tag a revision with a symbolic name.
168 '''tag a revision with a symbolic name.
169
169
170 if local is True, the tag is stored in a per-repository file.
170 if local is True, the tag is stored in a per-repository file.
171 otherwise, it is stored in the .hgtags file, and a new
171 otherwise, it is stored in the .hgtags file, and a new
172 changeset is committed with the change.
172 changeset is committed with the change.
173
173
174 keyword arguments:
174 keyword arguments:
175
175
176 local: whether to store tag in non-version-controlled file
176 local: whether to store tag in non-version-controlled file
177 (default False)
177 (default False)
178
178
179 message: commit message to use if committing
179 message: commit message to use if committing
180
180
181 user: name of user to use if committing
181 user: name of user to use if committing
182
182
183 date: date tuple to use if committing'''
183 date: date tuple to use if committing'''
184
184
185 for x in self.status()[:5]:
185 for x in self.status()[:5]:
186 if '.hgtags' in x:
186 if '.hgtags' in x:
187 raise util.Abort(_('working copy of .hgtags is changed '
187 raise util.Abort(_('working copy of .hgtags is changed '
188 '(please commit .hgtags manually)'))
188 '(please commit .hgtags manually)'))
189
189
190
190
191 self._tag(name, node, message, local, user, date)
191 self._tag(name, node, message, local, user, date)
192
192
193 def tags(self):
193 def tags(self):
194 '''return a mapping of tag to node'''
194 '''return a mapping of tag to node'''
195 if self.tagscache:
195 if self.tagscache:
196 return self.tagscache
196 return self.tagscache
197
197
198 globaltags = {}
198 globaltags = {}
199
199
200 def readtags(lines, fn):
200 def readtags(lines, fn):
201 filetags = {}
201 filetags = {}
202 count = 0
202 count = 0
203
203
204 def warn(msg):
204 def warn(msg):
205 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
205 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
206
206
207 for l in lines:
207 for l in lines:
208 count += 1
208 count += 1
209 if not l:
209 if not l:
210 continue
210 continue
211 s = l.split(" ", 1)
211 s = l.split(" ", 1)
212 if len(s) != 2:
212 if len(s) != 2:
213 warn(_("cannot parse entry"))
213 warn(_("cannot parse entry"))
214 continue
214 continue
215 node, key = s
215 node, key = s
216 key = util.tolocal(key.strip()) # stored in UTF-8
216 key = util.tolocal(key.strip()) # stored in UTF-8
217 try:
217 try:
218 bin_n = bin(node)
218 bin_n = bin(node)
219 except TypeError:
219 except TypeError:
220 warn(_("node '%s' is not well formed") % node)
220 warn(_("node '%s' is not well formed") % node)
221 continue
221 continue
222 if bin_n not in self.changelog.nodemap:
222 if bin_n not in self.changelog.nodemap:
223 warn(_("tag '%s' refers to unknown node") % key)
223 warn(_("tag '%s' refers to unknown node") % key)
224 continue
224 continue
225
225
226 h = []
226 h = []
227 if key in filetags:
227 if key in filetags:
228 n, h = filetags[key]
228 n, h = filetags[key]
229 h.append(n)
229 h.append(n)
230 filetags[key] = (bin_n, h)
230 filetags[key] = (bin_n, h)
231
231
232 for k, nh in filetags.items():
232 for k, nh in filetags.items():
233 if k not in globaltags:
233 if k not in globaltags:
234 globaltags[k] = nh
234 globaltags[k] = nh
235 continue
235 continue
236 # we prefer the global tag if:
236 # we prefer the global tag if:
237 # it supercedes us OR
237 # it supercedes us OR
238 # mutual supercedes and it has a higher rank
238 # mutual supercedes and it has a higher rank
239 # otherwise we win because we're tip-most
239 # otherwise we win because we're tip-most
240 an, ah = nh
240 an, ah = nh
241 bn, bh = globaltags[k]
241 bn, bh = globaltags[k]
242 if (bn != an and an in bh and
242 if (bn != an and an in bh and
243 (bn not in ah or len(bh) > len(ah))):
243 (bn not in ah or len(bh) > len(ah))):
244 an = bn
244 an = bn
245 ah.extend([n for n in bh if n not in ah])
245 ah.extend([n for n in bh if n not in ah])
246 globaltags[k] = an, ah
246 globaltags[k] = an, ah
247
247
248 # read the tags file from each head, ending with the tip
248 # read the tags file from each head, ending with the tip
249 f = None
249 f = None
250 for rev, node, fnode in self._hgtagsnodes():
250 for rev, node, fnode in self._hgtagsnodes():
251 f = (f and f.filectx(fnode) or
251 f = (f and f.filectx(fnode) or
252 self.filectx('.hgtags', fileid=fnode))
252 self.filectx('.hgtags', fileid=fnode))
253 readtags(f.data().splitlines(), f)
253 readtags(f.data().splitlines(), f)
254
254
255 try:
255 try:
256 data = util.fromlocal(self.opener("localtags").read())
256 data = util.fromlocal(self.opener("localtags").read())
257 # localtags are stored in the local character set
257 # localtags are stored in the local character set
258 # while the internal tag table is stored in UTF-8
258 # while the internal tag table is stored in UTF-8
259 readtags(data.splitlines(), "localtags")
259 readtags(data.splitlines(), "localtags")
260 except IOError:
260 except IOError:
261 pass
261 pass
262
262
263 self.tagscache = {}
263 self.tagscache = {}
264 for k,nh in globaltags.items():
264 for k,nh in globaltags.items():
265 n = nh[0]
265 n = nh[0]
266 if n != nullid:
266 if n != nullid:
267 self.tagscache[k] = n
267 self.tagscache[k] = n
268 self.tagscache['tip'] = self.changelog.tip()
268 self.tagscache['tip'] = self.changelog.tip()
269
269
270 return self.tagscache
270 return self.tagscache
271
271
272 def _hgtagsnodes(self):
272 def _hgtagsnodes(self):
273 heads = self.heads()
273 heads = self.heads()
274 heads.reverse()
274 heads.reverse()
275 last = {}
275 last = {}
276 ret = []
276 ret = []
277 for node in heads:
277 for node in heads:
278 c = self.changectx(node)
278 c = self.changectx(node)
279 rev = c.rev()
279 rev = c.rev()
280 try:
280 try:
281 fnode = c.filenode('.hgtags')
281 fnode = c.filenode('.hgtags')
282 except revlog.LookupError:
282 except revlog.LookupError:
283 continue
283 continue
284 ret.append((rev, node, fnode))
284 ret.append((rev, node, fnode))
285 if fnode in last:
285 if fnode in last:
286 ret[last[fnode]] = None
286 ret[last[fnode]] = None
287 last[fnode] = len(ret) - 1
287 last[fnode] = len(ret) - 1
288 return [item for item in ret if item]
288 return [item for item in ret if item]
289
289
290 def tagslist(self):
290 def tagslist(self):
291 '''return a list of tags ordered by revision'''
291 '''return a list of tags ordered by revision'''
292 l = []
292 l = []
293 for t, n in self.tags().items():
293 for t, n in self.tags().items():
294 try:
294 try:
295 r = self.changelog.rev(n)
295 r = self.changelog.rev(n)
296 except:
296 except:
297 r = -2 # sort to the beginning of the list if unknown
297 r = -2 # sort to the beginning of the list if unknown
298 l.append((r, t, n))
298 l.append((r, t, n))
299 l.sort()
299 l.sort()
300 return [(t, n) for r, t, n in l]
300 return [(t, n) for r, t, n in l]
301
301
302 def nodetags(self, node):
302 def nodetags(self, node):
303 '''return the tags associated with a node'''
303 '''return the tags associated with a node'''
304 if not self.nodetagscache:
304 if not self.nodetagscache:
305 self.nodetagscache = {}
305 self.nodetagscache = {}
306 for t, n in self.tags().items():
306 for t, n in self.tags().items():
307 self.nodetagscache.setdefault(n, []).append(t)
307 self.nodetagscache.setdefault(n, []).append(t)
308 return self.nodetagscache.get(node, [])
308 return self.nodetagscache.get(node, [])
309
309
310 def _branchtags(self):
310 def _branchtags(self):
311 partial, last, lrev = self._readbranchcache()
311 partial, last, lrev = self._readbranchcache()
312
312
313 tiprev = self.changelog.count() - 1
313 tiprev = self.changelog.count() - 1
314 if lrev != tiprev:
314 if lrev != tiprev:
315 self._updatebranchcache(partial, lrev+1, tiprev+1)
315 self._updatebranchcache(partial, lrev+1, tiprev+1)
316 self._writebranchcache(partial, self.changelog.tip(), tiprev)
316 self._writebranchcache(partial, self.changelog.tip(), tiprev)
317
317
318 return partial
318 return partial
319
319
320 def branchtags(self):
320 def branchtags(self):
321 if self.branchcache is not None:
321 if self.branchcache is not None:
322 return self.branchcache
322 return self.branchcache
323
323
324 self.branchcache = {} # avoid recursion in changectx
324 self.branchcache = {} # avoid recursion in changectx
325 partial = self._branchtags()
325 partial = self._branchtags()
326
326
327 # the branch cache is stored on disk as UTF-8, but in the local
327 # the branch cache is stored on disk as UTF-8, but in the local
328 # charset internally
328 # charset internally
329 for k, v in partial.items():
329 for k, v in partial.items():
330 self.branchcache[util.tolocal(k)] = v
330 self.branchcache[util.tolocal(k)] = v
331 return self.branchcache
331 return self.branchcache
332
332
333 def _readbranchcache(self):
333 def _readbranchcache(self):
334 partial = {}
334 partial = {}
335 try:
335 try:
336 f = self.opener("branch.cache")
336 f = self.opener("branch.cache")
337 lines = f.read().split('\n')
337 lines = f.read().split('\n')
338 f.close()
338 f.close()
339 except (IOError, OSError):
339 except (IOError, OSError):
340 return {}, nullid, nullrev
340 return {}, nullid, nullrev
341
341
342 try:
342 try:
343 last, lrev = lines.pop(0).split(" ", 1)
343 last, lrev = lines.pop(0).split(" ", 1)
344 last, lrev = bin(last), int(lrev)
344 last, lrev = bin(last), int(lrev)
345 if not (lrev < self.changelog.count() and
345 if not (lrev < self.changelog.count() and
346 self.changelog.node(lrev) == last): # sanity check
346 self.changelog.node(lrev) == last): # sanity check
347 # invalidate the cache
347 # invalidate the cache
348 raise ValueError('Invalid branch cache: unknown tip')
348 raise ValueError('Invalid branch cache: unknown tip')
349 for l in lines:
349 for l in lines:
350 if not l: continue
350 if not l: continue
351 node, label = l.split(" ", 1)
351 node, label = l.split(" ", 1)
352 partial[label.strip()] = bin(node)
352 partial[label.strip()] = bin(node)
353 except (KeyboardInterrupt, util.SignalInterrupt):
353 except (KeyboardInterrupt, util.SignalInterrupt):
354 raise
354 raise
355 except Exception, inst:
355 except Exception, inst:
356 if self.ui.debugflag:
356 if self.ui.debugflag:
357 self.ui.warn(str(inst), '\n')
357 self.ui.warn(str(inst), '\n')
358 partial, last, lrev = {}, nullid, nullrev
358 partial, last, lrev = {}, nullid, nullrev
359 return partial, last, lrev
359 return partial, last, lrev
360
360
361 def _writebranchcache(self, branches, tip, tiprev):
361 def _writebranchcache(self, branches, tip, tiprev):
362 try:
362 try:
363 f = self.opener("branch.cache", "w", atomictemp=True)
363 f = self.opener("branch.cache", "w", atomictemp=True)
364 f.write("%s %s\n" % (hex(tip), tiprev))
364 f.write("%s %s\n" % (hex(tip), tiprev))
365 for label, node in branches.iteritems():
365 for label, node in branches.iteritems():
366 f.write("%s %s\n" % (hex(node), label))
366 f.write("%s %s\n" % (hex(node), label))
367 f.rename()
367 f.rename()
368 except (IOError, OSError):
368 except (IOError, OSError):
369 pass
369 pass
370
370
371 def _updatebranchcache(self, partial, start, end):
371 def _updatebranchcache(self, partial, start, end):
372 for r in xrange(start, end):
372 for r in xrange(start, end):
373 c = self.changectx(r)
373 c = self.changectx(r)
374 b = c.branch()
374 b = c.branch()
375 partial[b] = c.node()
375 partial[b] = c.node()
376
376
377 def lookup(self, key):
377 def lookup(self, key):
378 if key == '.':
378 if key == '.':
379 key, second = self.dirstate.parents()
379 key, second = self.dirstate.parents()
380 if key == nullid:
380 if key == nullid:
381 raise repo.RepoError(_("no revision checked out"))
381 raise repo.RepoError(_("no revision checked out"))
382 if second != nullid:
382 if second != nullid:
383 self.ui.warn(_("warning: working directory has two parents, "
383 self.ui.warn(_("warning: working directory has two parents, "
384 "tag '.' uses the first\n"))
384 "tag '.' uses the first\n"))
385 elif key == 'null':
385 elif key == 'null':
386 return nullid
386 return nullid
387 n = self.changelog._match(key)
387 n = self.changelog._match(key)
388 if n:
388 if n:
389 return n
389 return n
390 if key in self.tags():
390 if key in self.tags():
391 return self.tags()[key]
391 return self.tags()[key]
392 if key in self.branchtags():
392 if key in self.branchtags():
393 return self.branchtags()[key]
393 return self.branchtags()[key]
394 n = self.changelog._partialmatch(key)
394 n = self.changelog._partialmatch(key)
395 if n:
395 if n:
396 return n
396 return n
397 raise repo.RepoError(_("unknown revision '%s'") % key)
397 raise repo.RepoError(_("unknown revision '%s'") % key)
398
398
399 def dev(self):
399 def dev(self):
400 return os.lstat(self.path).st_dev
400 return os.lstat(self.path).st_dev
401
401
402 def local(self):
402 def local(self):
403 return True
403 return True
404
404
405 def join(self, f):
405 def join(self, f):
406 return os.path.join(self.path, f)
406 return os.path.join(self.path, f)
407
407
408 def sjoin(self, f):
408 def sjoin(self, f):
409 f = self.encodefn(f)
409 f = self.encodefn(f)
410 return os.path.join(self.spath, f)
410 return os.path.join(self.spath, f)
411
411
412 def wjoin(self, f):
412 def wjoin(self, f):
413 return os.path.join(self.root, f)
413 return os.path.join(self.root, f)
414
414
415 def file(self, f):
415 def file(self, f):
416 if f[0] == '/':
416 if f[0] == '/':
417 f = f[1:]
417 f = f[1:]
418 return filelog.filelog(self.sopener, f)
418 return filelog.filelog(self.sopener, f)
419
419
420 def changectx(self, changeid=None):
420 def changectx(self, changeid=None):
421 return context.changectx(self, changeid)
421 return context.changectx(self, changeid)
422
422
423 def workingctx(self):
423 def workingctx(self):
424 return context.workingctx(self)
424 return context.workingctx(self)
425
425
426 def parents(self, changeid=None):
426 def parents(self, changeid=None):
427 '''
427 '''
428 get list of changectxs for parents of changeid or working directory
428 get list of changectxs for parents of changeid or working directory
429 '''
429 '''
430 if changeid is None:
430 if changeid is None:
431 pl = self.dirstate.parents()
431 pl = self.dirstate.parents()
432 else:
432 else:
433 n = self.changelog.lookup(changeid)
433 n = self.changelog.lookup(changeid)
434 pl = self.changelog.parents(n)
434 pl = self.changelog.parents(n)
435 if pl[1] == nullid:
435 if pl[1] == nullid:
436 return [self.changectx(pl[0])]
436 return [self.changectx(pl[0])]
437 return [self.changectx(pl[0]), self.changectx(pl[1])]
437 return [self.changectx(pl[0]), self.changectx(pl[1])]
438
438
439 def filectx(self, path, changeid=None, fileid=None):
439 def filectx(self, path, changeid=None, fileid=None):
440 """changeid can be a changeset revision, node, or tag.
440 """changeid can be a changeset revision, node, or tag.
441 fileid can be a file revision or node."""
441 fileid can be a file revision or node."""
442 return context.filectx(self, path, changeid, fileid)
442 return context.filectx(self, path, changeid, fileid)
443
443
444 def getcwd(self):
444 def getcwd(self):
445 return self.dirstate.getcwd()
445 return self.dirstate.getcwd()
446
446
447 def pathto(self, f, cwd=None):
447 def pathto(self, f, cwd=None):
448 return self.dirstate.pathto(f, cwd)
448 return self.dirstate.pathto(f, cwd)
449
449
450 def wfile(self, f, mode='r'):
450 def wfile(self, f, mode='r'):
451 return self.wopener(f, mode)
451 return self.wopener(f, mode)
452
452
453 def _link(self, f):
453 def _link(self, f):
454 return os.path.islink(self.wjoin(f))
454 return os.path.islink(self.wjoin(f))
455
455
456 def _filter(self, filter, filename, data):
456 def _filter(self, filter, filename, data):
457 if filter not in self.filterpats:
457 if filter not in self.filterpats:
458 l = []
458 l = []
459 for pat, cmd in self.ui.configitems(filter):
459 for pat, cmd in self.ui.configitems(filter):
460 mf = util.matcher(self.root, "", [pat], [], [])[1]
460 mf = util.matcher(self.root, "", [pat], [], [])[1]
461 l.append((mf, cmd))
461 l.append((mf, cmd))
462 self.filterpats[filter] = l
462 self.filterpats[filter] = l
463
463
464 for mf, cmd in self.filterpats[filter]:
464 for mf, cmd in self.filterpats[filter]:
465 if mf(filename):
465 if mf(filename):
466 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
466 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
467 data = util.filter(data, cmd)
467 data = util.filter(data, cmd)
468 break
468 break
469
469
470 return data
470 return data
471
471
472 def wread(self, filename):
472 def wread(self, filename):
473 if self._link(filename):
473 if self._link(filename):
474 data = os.readlink(self.wjoin(filename))
474 data = os.readlink(self.wjoin(filename))
475 else:
475 else:
476 data = self.wopener(filename, 'r').read()
476 data = self.wopener(filename, 'r').read()
477 return self._filter("encode", filename, data)
477 return self._filter("encode", filename, data)
478
478
479 def wwrite(self, filename, data, flags):
479 def wwrite(self, filename, data, flags):
480 data = self._filter("decode", filename, data)
480 data = self._filter("decode", filename, data)
481 if "l" in flags:
481 if "l" in flags:
482 self.wopener.symlink(data, filename)
482 self.wopener.symlink(data, filename)
483 else:
483 else:
484 try:
484 try:
485 if self._link(filename):
485 if self._link(filename):
486 os.unlink(self.wjoin(filename))
486 os.unlink(self.wjoin(filename))
487 except OSError:
487 except OSError:
488 pass
488 pass
489 self.wopener(filename, 'w').write(data)
489 self.wopener(filename, 'w').write(data)
490 util.set_exec(self.wjoin(filename), "x" in flags)
490 util.set_exec(self.wjoin(filename), "x" in flags)
491
491
492 def wwritedata(self, filename, data):
492 def wwritedata(self, filename, data):
493 return self._filter("decode", filename, data)
493 return self._filter("decode", filename, data)
494
494
495 def transaction(self):
495 def transaction(self):
496 tr = self.transhandle
496 tr = self.transhandle
497 if tr != None and tr.running():
497 if tr != None and tr.running():
498 return tr.nest()
498 return tr.nest()
499
499
500 # save dirstate for rollback
500 # save dirstate for rollback
501 try:
501 try:
502 ds = self.opener("dirstate").read()
502 ds = self.opener("dirstate").read()
503 except IOError:
503 except IOError:
504 ds = ""
504 ds = ""
505 self.opener("journal.dirstate", "w").write(ds)
505 self.opener("journal.dirstate", "w").write(ds)
506
506
507 renames = [(self.sjoin("journal"), self.sjoin("undo")),
507 renames = [(self.sjoin("journal"), self.sjoin("undo")),
508 (self.join("journal.dirstate"), self.join("undo.dirstate"))]
508 (self.join("journal.dirstate"), self.join("undo.dirstate"))]
509 tr = transaction.transaction(self.ui.warn, self.sopener,
509 tr = transaction.transaction(self.ui.warn, self.sopener,
510 self.sjoin("journal"),
510 self.sjoin("journal"),
511 aftertrans(renames))
511 aftertrans(renames))
512 self.transhandle = tr
512 self.transhandle = tr
513 return tr
513 return tr
514
514
515 def recover(self):
515 def recover(self):
516 l = self.lock()
516 l = self.lock()
517 if os.path.exists(self.sjoin("journal")):
517 if os.path.exists(self.sjoin("journal")):
518 self.ui.status(_("rolling back interrupted transaction\n"))
518 self.ui.status(_("rolling back interrupted transaction\n"))
519 transaction.rollback(self.sopener, self.sjoin("journal"))
519 transaction.rollback(self.sopener, self.sjoin("journal"))
520 self.invalidate()
520 self.invalidate()
521 return True
521 return True
522 else:
522 else:
523 self.ui.warn(_("no interrupted transaction available\n"))
523 self.ui.warn(_("no interrupted transaction available\n"))
524 return False
524 return False
525
525
526 def rollback(self, wlock=None, lock=None):
526 def rollback(self, wlock=None, lock=None):
527 if not wlock:
527 if not wlock:
528 wlock = self.wlock()
528 wlock = self.wlock()
529 if not lock:
529 if not lock:
530 lock = self.lock()
530 lock = self.lock()
531 if os.path.exists(self.sjoin("undo")):
531 if os.path.exists(self.sjoin("undo")):
532 self.ui.status(_("rolling back last transaction\n"))
532 self.ui.status(_("rolling back last transaction\n"))
533 transaction.rollback(self.sopener, self.sjoin("undo"))
533 transaction.rollback(self.sopener, self.sjoin("undo"))
534 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
534 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
535 self.invalidate()
535 self.invalidate()
536 self.dirstate.invalidate()
536 self.dirstate.invalidate()
537 else:
537 else:
538 self.ui.warn(_("no rollback information available\n"))
538 self.ui.warn(_("no rollback information available\n"))
539
539
540 def invalidate(self):
540 def invalidate(self):
541 for a in "changelog manifest".split():
541 for a in "changelog manifest".split():
542 if hasattr(self, a):
542 if hasattr(self, a):
543 self.__delattr__(a)
543 self.__delattr__(a)
544 self.tagscache = None
544 self.tagscache = None
545 self.nodetagscache = None
545 self.nodetagscache = None
546
546
547 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
547 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
548 desc=None):
548 desc=None):
549 try:
549 try:
550 l = lock.lock(lockname, 0, releasefn, desc=desc)
550 l = lock.lock(lockname, 0, releasefn, desc=desc)
551 except lock.LockHeld, inst:
551 except lock.LockHeld, inst:
552 if not wait:
552 if not wait:
553 raise
553 raise
554 self.ui.warn(_("waiting for lock on %s held by %r\n") %
554 self.ui.warn(_("waiting for lock on %s held by %r\n") %
555 (desc, inst.locker))
555 (desc, inst.locker))
556 # default to 600 seconds timeout
556 # default to 600 seconds timeout
557 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
557 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
558 releasefn, desc=desc)
558 releasefn, desc=desc)
559 if acquirefn:
559 if acquirefn:
560 acquirefn()
560 acquirefn()
561 return l
561 return l
562
562
563 def lock(self, wait=1):
563 def lock(self, wait=1):
564 return self.do_lock(self.sjoin("lock"), wait,
564 return self.do_lock(self.sjoin("lock"), wait,
565 acquirefn=self.invalidate,
565 acquirefn=self.invalidate,
566 desc=_('repository %s') % self.origroot)
566 desc=_('repository %s') % self.origroot)
567
567
568 def wlock(self, wait=1):
568 def wlock(self, wait=1):
569 return self.do_lock(self.join("wlock"), wait, self.dirstate.write,
569 return self.do_lock(self.join("wlock"), wait, self.dirstate.write,
570 self.dirstate.invalidate,
570 self.dirstate.invalidate,
571 desc=_('working directory of %s') % self.origroot)
571 desc=_('working directory of %s') % self.origroot)
572
572
573 def filecommit(self, fn, manifest1, manifest2, linkrev, transaction, changelist):
573 def filecommit(self, fn, manifest1, manifest2, linkrev, transaction, changelist):
574 """
574 """
575 commit an individual file as part of a larger transaction
575 commit an individual file as part of a larger transaction
576 """
576 """
577
577
578 t = self.wread(fn)
578 t = self.wread(fn)
579 fl = self.file(fn)
579 fl = self.file(fn)
580 fp1 = manifest1.get(fn, nullid)
580 fp1 = manifest1.get(fn, nullid)
581 fp2 = manifest2.get(fn, nullid)
581 fp2 = manifest2.get(fn, nullid)
582
582
583 meta = {}
583 meta = {}
584 cp = self.dirstate.copied(fn)
584 cp = self.dirstate.copied(fn)
585 if cp:
585 if cp:
586 # Mark the new revision of this file as a copy of another
586 # Mark the new revision of this file as a copy of another
587 # file. This copy data will effectively act as a parent
587 # file. This copy data will effectively act as a parent
588 # of this new revision. If this is a merge, the first
588 # of this new revision. If this is a merge, the first
589 # parent will be the nullid (meaning "look up the copy data")
589 # parent will be the nullid (meaning "look up the copy data")
590 # and the second one will be the other parent. For example:
590 # and the second one will be the other parent. For example:
591 #
591 #
592 # 0 --- 1 --- 3 rev1 changes file foo
592 # 0 --- 1 --- 3 rev1 changes file foo
593 # \ / rev2 renames foo to bar and changes it
593 # \ / rev2 renames foo to bar and changes it
594 # \- 2 -/ rev3 should have bar with all changes and
594 # \- 2 -/ rev3 should have bar with all changes and
595 # should record that bar descends from
595 # should record that bar descends from
596 # bar in rev2 and foo in rev1
596 # bar in rev2 and foo in rev1
597 #
597 #
598 # this allows this merge to succeed:
598 # this allows this merge to succeed:
599 #
599 #
600 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
600 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
601 # \ / merging rev3 and rev4 should use bar@rev2
601 # \ / merging rev3 and rev4 should use bar@rev2
602 # \- 2 --- 4 as the merge base
602 # \- 2 --- 4 as the merge base
603 #
603 #
604 meta["copy"] = cp
604 meta["copy"] = cp
605 if not manifest2: # not a branch merge
605 if not manifest2: # not a branch merge
606 meta["copyrev"] = hex(manifest1.get(cp, nullid))
606 meta["copyrev"] = hex(manifest1.get(cp, nullid))
607 fp2 = nullid
607 fp2 = nullid
608 elif fp2 != nullid: # copied on remote side
608 elif fp2 != nullid: # copied on remote side
609 meta["copyrev"] = hex(manifest1.get(cp, nullid))
609 meta["copyrev"] = hex(manifest1.get(cp, nullid))
610 elif fp1 != nullid: # copied on local side, reversed
610 elif fp1 != nullid: # copied on local side, reversed
611 meta["copyrev"] = hex(manifest2.get(cp))
611 meta["copyrev"] = hex(manifest2.get(cp))
612 fp2 = fp1
612 fp2 = fp1
613 else: # directory rename
613 else: # directory rename
614 meta["copyrev"] = hex(manifest1.get(cp, nullid))
614 meta["copyrev"] = hex(manifest1.get(cp, nullid))
615 self.ui.debug(_(" %s: copy %s:%s\n") %
615 self.ui.debug(_(" %s: copy %s:%s\n") %
616 (fn, cp, meta["copyrev"]))
616 (fn, cp, meta["copyrev"]))
617 fp1 = nullid
617 fp1 = nullid
618 elif fp2 != nullid:
618 elif fp2 != nullid:
619 # is one parent an ancestor of the other?
619 # is one parent an ancestor of the other?
620 fpa = fl.ancestor(fp1, fp2)
620 fpa = fl.ancestor(fp1, fp2)
621 if fpa == fp1:
621 if fpa == fp1:
622 fp1, fp2 = fp2, nullid
622 fp1, fp2 = fp2, nullid
623 elif fpa == fp2:
623 elif fpa == fp2:
624 fp2 = nullid
624 fp2 = nullid
625
625
626 # is the file unmodified from the parent? report existing entry
626 # is the file unmodified from the parent? report existing entry
627 if fp2 == nullid and not fl.cmp(fp1, t):
627 if fp2 == nullid and not fl.cmp(fp1, t):
628 return fp1
628 return fp1
629
629
630 changelist.append(fn)
630 changelist.append(fn)
631 return fl.add(t, meta, transaction, linkrev, fp1, fp2)
631 return fl.add(t, meta, transaction, linkrev, fp1, fp2)
632
632
633 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None, extra={}):
633 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None, extra={}):
634 if p1 is None:
634 if p1 is None:
635 p1, p2 = self.dirstate.parents()
635 p1, p2 = self.dirstate.parents()
636 return self.commit(files=files, text=text, user=user, date=date,
636 return self.commit(files=files, text=text, user=user, date=date,
637 p1=p1, p2=p2, wlock=wlock, extra=extra)
637 p1=p1, p2=p2, wlock=wlock, extra=extra)
638
638
639 def commit(self, files=None, text="", user=None, date=None,
639 def commit(self, files=None, text="", user=None, date=None,
640 match=util.always, force=False, lock=None, wlock=None,
640 match=util.always, force=False, lock=None, wlock=None,
641 force_editor=False, p1=None, p2=None, extra={}):
641 force_editor=False, p1=None, p2=None, extra={}):
642
642
643 commit = []
643 commit = []
644 remove = []
644 remove = []
645 changed = []
645 changed = []
646 use_dirstate = (p1 is None) # not rawcommit
646 use_dirstate = (p1 is None) # not rawcommit
647 extra = extra.copy()
647 extra = extra.copy()
648
648
649 if use_dirstate:
649 if use_dirstate:
650 if files:
650 if files:
651 for f in files:
651 for f in files:
652 s = self.dirstate.state(f)
652 s = self.dirstate.state(f)
653 if s in 'nmai':
653 if s in 'nmai':
654 commit.append(f)
654 commit.append(f)
655 elif s == 'r':
655 elif s == 'r':
656 remove.append(f)
656 remove.append(f)
657 else:
657 else:
658 self.ui.warn(_("%s not tracked!\n") % f)
658 self.ui.warn(_("%s not tracked!\n") % f)
659 else:
659 else:
660 changes = self.status(match=match)[:5]
660 changes = self.status(match=match)[:5]
661 modified, added, removed, deleted, unknown = changes
661 modified, added, removed, deleted, unknown = changes
662 commit = modified + added
662 commit = modified + added
663 remove = removed
663 remove = removed
664 else:
664 else:
665 commit = files
665 commit = files
666
666
667 if use_dirstate:
667 if use_dirstate:
668 p1, p2 = self.dirstate.parents()
668 p1, p2 = self.dirstate.parents()
669 update_dirstate = True
669 update_dirstate = True
670 else:
670 else:
671 p1, p2 = p1, p2 or nullid
671 p1, p2 = p1, p2 or nullid
672 update_dirstate = (self.dirstate.parents()[0] == p1)
672 update_dirstate = (self.dirstate.parents()[0] == p1)
673
673
674 c1 = self.changelog.read(p1)
674 c1 = self.changelog.read(p1)
675 c2 = self.changelog.read(p2)
675 c2 = self.changelog.read(p2)
676 m1 = self.manifest.read(c1[0]).copy()
676 m1 = self.manifest.read(c1[0]).copy()
677 m2 = self.manifest.read(c2[0])
677 m2 = self.manifest.read(c2[0])
678
678
679 if use_dirstate:
679 if use_dirstate:
680 branchname = self.workingctx().branch()
680 branchname = self.workingctx().branch()
681 try:
681 try:
682 branchname = branchname.decode('UTF-8').encode('UTF-8')
682 branchname = branchname.decode('UTF-8').encode('UTF-8')
683 except UnicodeDecodeError:
683 except UnicodeDecodeError:
684 raise util.Abort(_('branch name not in UTF-8!'))
684 raise util.Abort(_('branch name not in UTF-8!'))
685 else:
685 else:
686 branchname = ""
686 branchname = ""
687
687
688 if use_dirstate:
688 if use_dirstate:
689 oldname = c1[5].get("branch") # stored in UTF-8
689 oldname = c1[5].get("branch") # stored in UTF-8
690 if (not commit and not remove and not force and p2 == nullid
690 if (not commit and not remove and not force and p2 == nullid
691 and branchname == oldname):
691 and branchname == oldname):
692 self.ui.status(_("nothing changed\n"))
692 self.ui.status(_("nothing changed\n"))
693 return None
693 return None
694
694
695 xp1 = hex(p1)
695 xp1 = hex(p1)
696 if p2 == nullid: xp2 = ''
696 if p2 == nullid: xp2 = ''
697 else: xp2 = hex(p2)
697 else: xp2 = hex(p2)
698
698
699 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
699 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
700
700
701 if not wlock:
701 if not wlock:
702 wlock = self.wlock()
702 wlock = self.wlock()
703 if not lock:
703 if not lock:
704 lock = self.lock()
704 lock = self.lock()
705 tr = self.transaction()
705 tr = self.transaction()
706
706
707 # check in files
707 # check in files
708 new = {}
708 new = {}
709 linkrev = self.changelog.count()
709 linkrev = self.changelog.count()
710 commit.sort()
710 commit.sort()
711 is_exec = util.execfunc(self.root, m1.execf)
711 is_exec = util.execfunc(self.root, m1.execf)
712 is_link = util.linkfunc(self.root, m1.linkf)
712 is_link = util.linkfunc(self.root, m1.linkf)
713 for f in commit:
713 for f in commit:
714 self.ui.note(f + "\n")
714 self.ui.note(f + "\n")
715 try:
715 try:
716 new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
716 new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
717 new_exec = is_exec(f)
717 new_exec = is_exec(f)
718 new_link = is_link(f)
718 new_link = is_link(f)
719 if not changed or changed[-1] != f:
719 if not changed or changed[-1] != f:
720 # mention the file in the changelog if some flag changed,
720 # mention the file in the changelog if some flag changed,
721 # even if there was no content change.
721 # even if there was no content change.
722 old_exec = m1.execf(f)
722 old_exec = m1.execf(f)
723 old_link = m1.linkf(f)
723 old_link = m1.linkf(f)
724 if old_exec != new_exec or old_link != new_link:
724 if old_exec != new_exec or old_link != new_link:
725 changed.append(f)
725 changed.append(f)
726 m1.set(f, new_exec, new_link)
726 m1.set(f, new_exec, new_link)
727 except (OSError, IOError):
727 except (OSError, IOError):
728 if use_dirstate:
728 if use_dirstate:
729 self.ui.warn(_("trouble committing %s!\n") % f)
729 self.ui.warn(_("trouble committing %s!\n") % f)
730 raise
730 raise
731 else:
731 else:
732 remove.append(f)
732 remove.append(f)
733
733
734 # update manifest
734 # update manifest
735 m1.update(new)
735 m1.update(new)
736 remove.sort()
736 remove.sort()
737 removed = []
737 removed = []
738
738
739 for f in remove:
739 for f in remove:
740 if f in m1:
740 if f in m1:
741 del m1[f]
741 del m1[f]
742 removed.append(f)
742 removed.append(f)
743 elif f in m2:
743 elif f in m2:
744 removed.append(f)
744 removed.append(f)
745 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed))
745 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed))
746
746
747 # add changeset
747 # add changeset
748 new = new.keys()
748 new = new.keys()
749 new.sort()
749 new.sort()
750
750
751 user = user or self.ui.username()
751 user = user or self.ui.username()
752 if not text or force_editor:
752 if not text or force_editor:
753 edittext = []
753 edittext = []
754 if text:
754 if text:
755 edittext.append(text)
755 edittext.append(text)
756 edittext.append("")
756 edittext.append("")
757 edittext.append("HG: user: %s" % user)
757 edittext.append("HG: user: %s" % user)
758 if p2 != nullid:
758 if p2 != nullid:
759 edittext.append("HG: branch merge")
759 edittext.append("HG: branch merge")
760 if branchname:
760 if branchname:
761 edittext.append("HG: branch %s" % util.tolocal(branchname))
761 edittext.append("HG: branch %s" % util.tolocal(branchname))
762 edittext.extend(["HG: changed %s" % f for f in changed])
762 edittext.extend(["HG: changed %s" % f for f in changed])
763 edittext.extend(["HG: removed %s" % f for f in removed])
763 edittext.extend(["HG: removed %s" % f for f in removed])
764 if not changed and not remove:
764 if not changed and not remove:
765 edittext.append("HG: no files changed")
765 edittext.append("HG: no files changed")
766 edittext.append("")
766 edittext.append("")
767 # run editor in the repository root
767 # run editor in the repository root
768 olddir = os.getcwd()
768 olddir = os.getcwd()
769 os.chdir(self.root)
769 os.chdir(self.root)
770 text = self.ui.edit("\n".join(edittext), user)
770 text = self.ui.edit("\n".join(edittext), user)
771 os.chdir(olddir)
771 os.chdir(olddir)
772
772
773 lines = [line.rstrip() for line in text.rstrip().splitlines()]
773 lines = [line.rstrip() for line in text.rstrip().splitlines()]
774 while lines and not lines[0]:
774 while lines and not lines[0]:
775 del lines[0]
775 del lines[0]
776 if not lines:
776 if not lines:
777 return None
777 return None
778 text = '\n'.join(lines)
778 text = '\n'.join(lines)
779 if branchname:
779 if branchname:
780 extra["branch"] = branchname
780 extra["branch"] = branchname
781 n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
781 n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
782 user, date, extra)
782 user, date, extra)
783 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
783 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
784 parent2=xp2)
784 parent2=xp2)
785 tr.close()
785 tr.close()
786
786
787 if self.branchcache and "branch" in extra:
787 if self.branchcache and "branch" in extra:
788 self.branchcache[util.tolocal(extra["branch"])] = n
788 self.branchcache[util.tolocal(extra["branch"])] = n
789
789
790 if use_dirstate or update_dirstate:
790 if use_dirstate or update_dirstate:
791 self.dirstate.setparents(n)
791 self.dirstate.setparents(n)
792 if use_dirstate:
792 if use_dirstate:
793 self.dirstate.update(new, "n")
793 self.dirstate.update(new, "n")
794 self.dirstate.forget(removed)
794 self.dirstate.forget(removed)
795
795
796 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
796 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
797 return n
797 return n
798
798
799 def walk(self, node=None, files=[], match=util.always, badmatch=None):
799 def walk(self, node=None, files=[], match=util.always, badmatch=None):
800 '''
800 '''
801 walk recursively through the directory tree or a given
801 walk recursively through the directory tree or a given
802 changeset, finding all files matched by the match
802 changeset, finding all files matched by the match
803 function
803 function
804
804
805 results are yielded in a tuple (src, filename), where src
805 results are yielded in a tuple (src, filename), where src
806 is one of:
806 is one of:
807 'f' the file was found in the directory tree
807 'f' the file was found in the directory tree
808 'm' the file was only in the dirstate and not in the tree
808 'm' the file was only in the dirstate and not in the tree
809 'b' file was not found and matched badmatch
809 'b' file was not found and matched badmatch
810 '''
810 '''
811
811
812 if node:
812 if node:
813 fdict = dict.fromkeys(files)
813 fdict = dict.fromkeys(files)
814 # for dirstate.walk, files=['.'] means "walk the whole tree".
814 # for dirstate.walk, files=['.'] means "walk the whole tree".
815 # follow that here, too
815 # follow that here, too
816 fdict.pop('.', None)
816 fdict.pop('.', None)
817 mdict = self.manifest.read(self.changelog.read(node)[0])
817 mdict = self.manifest.read(self.changelog.read(node)[0])
818 mfiles = mdict.keys()
818 mfiles = mdict.keys()
819 mfiles.sort()
819 mfiles.sort()
820 for fn in mfiles:
820 for fn in mfiles:
821 for ffn in fdict:
821 for ffn in fdict:
822 # match if the file is the exact name or a directory
822 # match if the file is the exact name or a directory
823 if ffn == fn or fn.startswith("%s/" % ffn):
823 if ffn == fn or fn.startswith("%s/" % ffn):
824 del fdict[ffn]
824 del fdict[ffn]
825 break
825 break
826 if match(fn):
826 if match(fn):
827 yield 'm', fn
827 yield 'm', fn
828 ffiles = fdict.keys()
828 ffiles = fdict.keys()
829 ffiles.sort()
829 ffiles.sort()
830 for fn in ffiles:
830 for fn in ffiles:
831 if badmatch and badmatch(fn):
831 if badmatch and badmatch(fn):
832 if match(fn):
832 if match(fn):
833 yield 'b', fn
833 yield 'b', fn
834 else:
834 else:
835 self.ui.warn(_('%s: No such file in rev %s\n')
835 self.ui.warn(_('%s: No such file in rev %s\n')
836 % (self.pathto(fn), short(node)))
836 % (self.pathto(fn), short(node)))
837 else:
837 else:
838 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
838 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
839 yield src, fn
839 yield src, fn
840
840
841 def status(self, node1=None, node2=None, files=[], match=util.always,
841 def status(self, node1=None, node2=None, files=[], match=util.always,
842 wlock=None, list_ignored=False, list_clean=False):
842 wlock=None, list_ignored=False, list_clean=False):
843 """return status of files between two nodes or node and working directory
843 """return status of files between two nodes or node and working directory
844
844
845 If node1 is None, use the first dirstate parent instead.
845 If node1 is None, use the first dirstate parent instead.
846 If node2 is None, compare node1 with working directory.
846 If node2 is None, compare node1 with working directory.
847 """
847 """
848
848
849 def fcmp(fn, getnode):
849 def fcmp(fn, getnode):
850 t1 = self.wread(fn)
850 t1 = self.wread(fn)
851 return self.file(fn).cmp(getnode(fn), t1)
851 return self.file(fn).cmp(getnode(fn), t1)
852
852
853 def mfmatches(node):
853 def mfmatches(node):
854 change = self.changelog.read(node)
854 change = self.changelog.read(node)
855 mf = self.manifest.read(change[0]).copy()
855 mf = self.manifest.read(change[0]).copy()
856 for fn in mf.keys():
856 for fn in mf.keys():
857 if not match(fn):
857 if not match(fn):
858 del mf[fn]
858 del mf[fn]
859 return mf
859 return mf
860
860
861 modified, added, removed, deleted, unknown = [], [], [], [], []
861 modified, added, removed, deleted, unknown = [], [], [], [], []
862 ignored, clean = [], []
862 ignored, clean = [], []
863
863
864 compareworking = False
864 compareworking = False
865 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
865 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
866 compareworking = True
866 compareworking = True
867
867
868 if not compareworking:
868 if not compareworking:
869 # read the manifest from node1 before the manifest from node2,
869 # read the manifest from node1 before the manifest from node2,
870 # so that we'll hit the manifest cache if we're going through
870 # so that we'll hit the manifest cache if we're going through
871 # all the revisions in parent->child order.
871 # all the revisions in parent->child order.
872 mf1 = mfmatches(node1)
872 mf1 = mfmatches(node1)
873
873
874 mywlock = False
874 mywlock = False
875
875
876 # are we comparing the working directory?
876 # are we comparing the working directory?
877 if not node2:
877 if not node2:
878 (lookup, modified, added, removed, deleted, unknown,
878 (lookup, modified, added, removed, deleted, unknown,
879 ignored, clean) = self.dirstate.status(files, match,
879 ignored, clean) = self.dirstate.status(files, match,
880 list_ignored, list_clean)
880 list_ignored, list_clean)
881
881
882 # are we comparing working dir against its parent?
882 # are we comparing working dir against its parent?
883 if compareworking:
883 if compareworking:
884 if lookup:
884 if lookup:
885 # do a full compare of any files that might have changed
885 # do a full compare of any files that might have changed
886 mnode = self.changelog.read(self.dirstate.parents()[0])[0]
886 mnode = self.changelog.read(self.dirstate.parents()[0])[0]
887 getnode = lambda fn: (self.manifest.find(mnode, fn)[0] or
887 getnode = lambda fn: (self.manifest.find(mnode, fn)[0] or
888 nullid)
888 nullid)
889 for f in lookup:
889 for f in lookup:
890 if fcmp(f, getnode):
890 if fcmp(f, getnode):
891 modified.append(f)
891 modified.append(f)
892 else:
892 else:
893 if list_clean:
893 if list_clean:
894 clean.append(f)
894 clean.append(f)
895 if not wlock and not mywlock:
895 if not wlock and not mywlock:
896 mywlock = True
896 mywlock = True
897 try:
897 try:
898 wlock = self.wlock(wait=0)
898 wlock = self.wlock(wait=0)
899 except lock.LockException:
899 except lock.LockException:
900 pass
900 pass
901 if wlock:
901 if wlock:
902 self.dirstate.update([f], "n")
902 self.dirstate.update([f], "n")
903 else:
903 else:
904 # we are comparing working dir against non-parent
904 # we are comparing working dir against non-parent
905 # generate a pseudo-manifest for the working dir
905 # generate a pseudo-manifest for the working dir
906 # XXX: create it in dirstate.py ?
906 # XXX: create it in dirstate.py ?
907 mf2 = mfmatches(self.dirstate.parents()[0])
907 mf2 = mfmatches(self.dirstate.parents()[0])
908 is_exec = util.execfunc(self.root, mf2.execf)
908 is_exec = util.execfunc(self.root, mf2.execf)
909 is_link = util.linkfunc(self.root, mf2.linkf)
909 is_link = util.linkfunc(self.root, mf2.linkf)
910 for f in lookup + modified + added:
910 for f in lookup + modified + added:
911 mf2[f] = ""
911 mf2[f] = ""
912 mf2.set(f, is_exec(f), is_link(f))
912 mf2.set(f, is_exec(f), is_link(f))
913 for f in removed:
913 for f in removed:
914 if f in mf2:
914 if f in mf2:
915 del mf2[f]
915 del mf2[f]
916
916
917 if mywlock and wlock:
917 if mywlock and wlock:
918 wlock.release()
918 wlock.release()
919 else:
919 else:
920 # we are comparing two revisions
920 # we are comparing two revisions
921 mf2 = mfmatches(node2)
921 mf2 = mfmatches(node2)
922
922
923 if not compareworking:
923 if not compareworking:
924 # flush lists from dirstate before comparing manifests
924 # flush lists from dirstate before comparing manifests
925 modified, added, clean = [], [], []
925 modified, added, clean = [], [], []
926
926
927 # make sure to sort the files so we talk to the disk in a
927 # make sure to sort the files so we talk to the disk in a
928 # reasonable order
928 # reasonable order
929 mf2keys = mf2.keys()
929 mf2keys = mf2.keys()
930 mf2keys.sort()
930 mf2keys.sort()
931 getnode = lambda fn: mf1.get(fn, nullid)
931 getnode = lambda fn: mf1.get(fn, nullid)
932 for fn in mf2keys:
932 for fn in mf2keys:
933 if mf1.has_key(fn):
933 if mf1.has_key(fn):
934 if (mf1.flags(fn) != mf2.flags(fn) or
934 if (mf1.flags(fn) != mf2.flags(fn) or
935 (mf1[fn] != mf2[fn] and
935 (mf1[fn] != mf2[fn] and
936 (mf2[fn] != "" or fcmp(fn, getnode)))):
936 (mf2[fn] != "" or fcmp(fn, getnode)))):
937 modified.append(fn)
937 modified.append(fn)
938 elif list_clean:
938 elif list_clean:
939 clean.append(fn)
939 clean.append(fn)
940 del mf1[fn]
940 del mf1[fn]
941 else:
941 else:
942 added.append(fn)
942 added.append(fn)
943
943
944 removed = mf1.keys()
944 removed = mf1.keys()
945
945
946 # sort and return results:
946 # sort and return results:
947 for l in modified, added, removed, deleted, unknown, ignored, clean:
947 for l in modified, added, removed, deleted, unknown, ignored, clean:
948 l.sort()
948 l.sort()
949 return (modified, added, removed, deleted, unknown, ignored, clean)
949 return (modified, added, removed, deleted, unknown, ignored, clean)
950
950
951 def add(self, list, wlock=None):
951 def add(self, list, wlock=None):
952 if not wlock:
952 if not wlock:
953 wlock = self.wlock()
953 wlock = self.wlock()
954 for f in list:
954 for f in list:
955 p = self.wjoin(f)
955 p = self.wjoin(f)
956 try:
956 try:
957 st = os.lstat(p)
957 st = os.lstat(p)
958 except:
958 except:
959 self.ui.warn(_("%s does not exist!\n") % f)
959 self.ui.warn(_("%s does not exist!\n") % f)
960 continue
960 continue
961 if st.st_size > 10000000:
961 if st.st_size > 10000000:
962 self.ui.warn(_("%s: files over 10MB may cause memory and"
962 self.ui.warn(_("%s: files over 10MB may cause memory and"
963 " performance problems\n"
963 " performance problems\n"
964 "(use 'hg revert %s' to unadd the file)\n")
964 "(use 'hg revert %s' to unadd the file)\n")
965 % (f, f))
965 % (f, f))
966 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
966 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
967 self.ui.warn(_("%s not added: only files and symlinks "
967 self.ui.warn(_("%s not added: only files and symlinks "
968 "supported currently\n") % f)
968 "supported currently\n") % f)
969 elif self.dirstate.state(f) in 'an':
969 elif self.dirstate.state(f) in 'an':
970 self.ui.warn(_("%s already tracked!\n") % f)
970 self.ui.warn(_("%s already tracked!\n") % f)
971 else:
971 else:
972 self.dirstate.update([f], "a")
972 self.dirstate.update([f], "a")
973
973
974 def forget(self, list, wlock=None):
974 def forget(self, list, wlock=None):
975 if not wlock:
975 if not wlock:
976 wlock = self.wlock()
976 wlock = self.wlock()
977 for f in list:
977 for f in list:
978 if self.dirstate.state(f) not in 'ai':
978 if self.dirstate.state(f) not in 'ai':
979 self.ui.warn(_("%s not added!\n") % f)
979 self.ui.warn(_("%s not added!\n") % f)
980 else:
980 else:
981 self.dirstate.forget([f])
981 self.dirstate.forget([f])
982
982
983 def remove(self, list, unlink=False, wlock=None):
983 def remove(self, list, unlink=False, wlock=None):
984 if unlink:
984 if unlink:
985 for f in list:
985 for f in list:
986 try:
986 try:
987 util.unlink(self.wjoin(f))
987 util.unlink(self.wjoin(f))
988 except OSError, inst:
988 except OSError, inst:
989 if inst.errno != errno.ENOENT:
989 if inst.errno != errno.ENOENT:
990 raise
990 raise
991 if not wlock:
991 if not wlock:
992 wlock = self.wlock()
992 wlock = self.wlock()
993 for f in list:
993 for f in list:
994 if unlink and os.path.exists(self.wjoin(f)):
994 if unlink and os.path.exists(self.wjoin(f)):
995 self.ui.warn(_("%s still exists!\n") % f)
995 self.ui.warn(_("%s still exists!\n") % f)
996 elif self.dirstate.state(f) == 'a':
996 elif self.dirstate.state(f) == 'a':
997 self.dirstate.forget([f])
997 self.dirstate.forget([f])
998 elif f not in self.dirstate:
998 elif f not in self.dirstate:
999 self.ui.warn(_("%s not tracked!\n") % f)
999 self.ui.warn(_("%s not tracked!\n") % f)
1000 else:
1000 else:
1001 self.dirstate.update([f], "r")
1001 self.dirstate.update([f], "r")
1002
1002
1003 def undelete(self, list, wlock=None):
1003 def undelete(self, list, wlock=None):
1004 p = self.dirstate.parents()[0]
1004 p = self.dirstate.parents()[0]
1005 mn = self.changelog.read(p)[0]
1005 mn = self.changelog.read(p)[0]
1006 m = self.manifest.read(mn)
1006 m = self.manifest.read(mn)
1007 if not wlock:
1007 if not wlock:
1008 wlock = self.wlock()
1008 wlock = self.wlock()
1009 for f in list:
1009 for f in list:
1010 if self.dirstate.state(f) not in "r":
1010 if self.dirstate.state(f) not in "r":
1011 self.ui.warn("%s not removed!\n" % f)
1011 self.ui.warn("%s not removed!\n" % f)
1012 else:
1012 else:
1013 t = self.file(f).read(m[f])
1013 t = self.file(f).read(m[f])
1014 self.wwrite(f, t, m.flags(f))
1014 self.wwrite(f, t, m.flags(f))
1015 self.dirstate.update([f], "n")
1015 self.dirstate.update([f], "n")
1016
1016
1017 def copy(self, source, dest, wlock=None):
1017 def copy(self, source, dest, wlock=None):
1018 p = self.wjoin(dest)
1018 p = self.wjoin(dest)
1019 if not (os.path.exists(p) or os.path.islink(p)):
1019 if not (os.path.exists(p) or os.path.islink(p)):
1020 self.ui.warn(_("%s does not exist!\n") % dest)
1020 self.ui.warn(_("%s does not exist!\n") % dest)
1021 elif not (os.path.isfile(p) or os.path.islink(p)):
1021 elif not (os.path.isfile(p) or os.path.islink(p)):
1022 self.ui.warn(_("copy failed: %s is not a file or a "
1022 self.ui.warn(_("copy failed: %s is not a file or a "
1023 "symbolic link\n") % dest)
1023 "symbolic link\n") % dest)
1024 else:
1024 else:
1025 if not wlock:
1025 if not wlock:
1026 wlock = self.wlock()
1026 wlock = self.wlock()
1027 if self.dirstate.state(dest) == '?':
1027 if self.dirstate.state(dest) == '?':
1028 self.dirstate.update([dest], "a")
1028 self.dirstate.update([dest], "a")
1029 self.dirstate.copy(source, dest)
1029 self.dirstate.copy(source, dest)
1030
1030
1031 def heads(self, start=None):
1031 def heads(self, start=None):
1032 heads = self.changelog.heads(start)
1032 heads = self.changelog.heads(start)
1033 # sort the output in rev descending order
1033 # sort the output in rev descending order
1034 heads = [(-self.changelog.rev(h), h) for h in heads]
1034 heads = [(-self.changelog.rev(h), h) for h in heads]
1035 heads.sort()
1035 heads.sort()
1036 return [n for (r, n) in heads]
1036 return [n for (r, n) in heads]
1037
1037
1038 def branchheads(self, branch, start=None):
1038 def branchheads(self, branch, start=None):
1039 branches = self.branchtags()
1039 branches = self.branchtags()
1040 if branch not in branches:
1040 if branch not in branches:
1041 return []
1041 return []
1042 # The basic algorithm is this:
1042 # The basic algorithm is this:
1043 #
1043 #
1044 # Start from the branch tip since there are no later revisions that can
1044 # Start from the branch tip since there are no later revisions that can
1045 # possibly be in this branch, and the tip is a guaranteed head.
1045 # possibly be in this branch, and the tip is a guaranteed head.
1046 #
1046 #
1047 # Remember the tip's parents as the first ancestors, since these by
1047 # Remember the tip's parents as the first ancestors, since these by
1048 # definition are not heads.
1048 # definition are not heads.
1049 #
1049 #
1050 # Step backwards from the brach tip through all the revisions. We are
1050 # Step backwards from the brach tip through all the revisions. We are
1051 # guaranteed by the rules of Mercurial that we will now be visiting the
1051 # guaranteed by the rules of Mercurial that we will now be visiting the
1052 # nodes in reverse topological order (children before parents).
1052 # nodes in reverse topological order (children before parents).
1053 #
1053 #
1054 # If a revision is one of the ancestors of a head then we can toss it
1054 # If a revision is one of the ancestors of a head then we can toss it
1055 # out of the ancestors set (we've already found it and won't be
1055 # out of the ancestors set (we've already found it and won't be
1056 # visiting it again) and put its parents in the ancestors set.
1056 # visiting it again) and put its parents in the ancestors set.
1057 #
1057 #
1058 # Otherwise, if a revision is in the branch it's another head, since it
1058 # Otherwise, if a revision is in the branch it's another head, since it
1059 # wasn't in the ancestor list of an existing head. So add it to the
1059 # wasn't in the ancestor list of an existing head. So add it to the
1060 # head list, and add its parents to the ancestor list.
1060 # head list, and add its parents to the ancestor list.
1061 #
1061 #
1062 # If it is not in the branch ignore it.
1062 # If it is not in the branch ignore it.
1063 #
1063 #
1064 # Once we have a list of heads, use nodesbetween to filter out all the
1064 # Once we have a list of heads, use nodesbetween to filter out all the
1065 # heads that cannot be reached from startrev. There may be a more
1065 # heads that cannot be reached from startrev. There may be a more
1066 # efficient way to do this as part of the previous algorithm.
1066 # efficient way to do this as part of the previous algorithm.
1067
1067
1068 set = util.set
1068 set = util.set
1069 heads = [self.changelog.rev(branches[branch])]
1069 heads = [self.changelog.rev(branches[branch])]
1070 # Don't care if ancestors contains nullrev or not.
1070 # Don't care if ancestors contains nullrev or not.
1071 ancestors = set(self.changelog.parentrevs(heads[0]))
1071 ancestors = set(self.changelog.parentrevs(heads[0]))
1072 for rev in xrange(heads[0] - 1, nullrev, -1):
1072 for rev in xrange(heads[0] - 1, nullrev, -1):
1073 if rev in ancestors:
1073 if rev in ancestors:
1074 ancestors.update(self.changelog.parentrevs(rev))
1074 ancestors.update(self.changelog.parentrevs(rev))
1075 ancestors.remove(rev)
1075 ancestors.remove(rev)
1076 elif self.changectx(rev).branch() == branch:
1076 elif self.changectx(rev).branch() == branch:
1077 heads.append(rev)
1077 heads.append(rev)
1078 ancestors.update(self.changelog.parentrevs(rev))
1078 ancestors.update(self.changelog.parentrevs(rev))
1079 heads = [self.changelog.node(rev) for rev in heads]
1079 heads = [self.changelog.node(rev) for rev in heads]
1080 if start is not None:
1080 if start is not None:
1081 heads = self.changelog.nodesbetween([start], heads)[2]
1081 heads = self.changelog.nodesbetween([start], heads)[2]
1082 return heads
1082 return heads
1083
1083
1084 def branches(self, nodes):
1084 def branches(self, nodes):
1085 if not nodes:
1085 if not nodes:
1086 nodes = [self.changelog.tip()]
1086 nodes = [self.changelog.tip()]
1087 b = []
1087 b = []
1088 for n in nodes:
1088 for n in nodes:
1089 t = n
1089 t = n
1090 while 1:
1090 while 1:
1091 p = self.changelog.parents(n)
1091 p = self.changelog.parents(n)
1092 if p[1] != nullid or p[0] == nullid:
1092 if p[1] != nullid or p[0] == nullid:
1093 b.append((t, n, p[0], p[1]))
1093 b.append((t, n, p[0], p[1]))
1094 break
1094 break
1095 n = p[0]
1095 n = p[0]
1096 return b
1096 return b
1097
1097
1098 def between(self, pairs):
1098 def between(self, pairs):
1099 r = []
1099 r = []
1100
1100
1101 for top, bottom in pairs:
1101 for top, bottom in pairs:
1102 n, l, i = top, [], 0
1102 n, l, i = top, [], 0
1103 f = 1
1103 f = 1
1104
1104
1105 while n != bottom:
1105 while n != bottom:
1106 p = self.changelog.parents(n)[0]
1106 p = self.changelog.parents(n)[0]
1107 if i == f:
1107 if i == f:
1108 l.append(n)
1108 l.append(n)
1109 f = f * 2
1109 f = f * 2
1110 n = p
1110 n = p
1111 i += 1
1111 i += 1
1112
1112
1113 r.append(l)
1113 r.append(l)
1114
1114
1115 return r
1115 return r
1116
1116
1117 def findincoming(self, remote, base=None, heads=None, force=False):
1117 def findincoming(self, remote, base=None, heads=None, force=False):
1118 """Return list of roots of the subsets of missing nodes from remote
1118 """Return list of roots of the subsets of missing nodes from remote
1119
1119
1120 If base dict is specified, assume that these nodes and their parents
1120 If base dict is specified, assume that these nodes and their parents
1121 exist on the remote side and that no child of a node of base exists
1121 exist on the remote side and that no child of a node of base exists
1122 in both remote and self.
1122 in both remote and self.
1123 Furthermore base will be updated to include the nodes that exists
1123 Furthermore base will be updated to include the nodes that exists
1124 in self and remote but no children exists in self and remote.
1124 in self and remote but no children exists in self and remote.
1125 If a list of heads is specified, return only nodes which are heads
1125 If a list of heads is specified, return only nodes which are heads
1126 or ancestors of these heads.
1126 or ancestors of these heads.
1127
1127
1128 All the ancestors of base are in self and in remote.
1128 All the ancestors of base are in self and in remote.
1129 All the descendants of the list returned are missing in self.
1129 All the descendants of the list returned are missing in self.
1130 (and so we know that the rest of the nodes are missing in remote, see
1130 (and so we know that the rest of the nodes are missing in remote, see
1131 outgoing)
1131 outgoing)
1132 """
1132 """
1133 m = self.changelog.nodemap
1133 m = self.changelog.nodemap
1134 search = []
1134 search = []
1135 fetch = {}
1135 fetch = {}
1136 seen = {}
1136 seen = {}
1137 seenbranch = {}
1137 seenbranch = {}
1138 if base == None:
1138 if base == None:
1139 base = {}
1139 base = {}
1140
1140
1141 if not heads:
1141 if not heads:
1142 heads = remote.heads()
1142 heads = remote.heads()
1143
1143
1144 if self.changelog.tip() == nullid:
1144 if self.changelog.tip() == nullid:
1145 base[nullid] = 1
1145 base[nullid] = 1
1146 if heads != [nullid]:
1146 if heads != [nullid]:
1147 return [nullid]
1147 return [nullid]
1148 return []
1148 return []
1149
1149
1150 # assume we're closer to the tip than the root
1150 # assume we're closer to the tip than the root
1151 # and start by examining the heads
1151 # and start by examining the heads
1152 self.ui.status(_("searching for changes\n"))
1152 self.ui.status(_("searching for changes\n"))
1153
1153
1154 unknown = []
1154 unknown = []
1155 for h in heads:
1155 for h in heads:
1156 if h not in m:
1156 if h not in m:
1157 unknown.append(h)
1157 unknown.append(h)
1158 else:
1158 else:
1159 base[h] = 1
1159 base[h] = 1
1160
1160
1161 if not unknown:
1161 if not unknown:
1162 return []
1162 return []
1163
1163
1164 req = dict.fromkeys(unknown)
1164 req = dict.fromkeys(unknown)
1165 reqcnt = 0
1165 reqcnt = 0
1166
1166
1167 # search through remote branches
1167 # search through remote branches
1168 # a 'branch' here is a linear segment of history, with four parts:
1168 # a 'branch' here is a linear segment of history, with four parts:
1169 # head, root, first parent, second parent
1169 # head, root, first parent, second parent
1170 # (a branch always has two parents (or none) by definition)
1170 # (a branch always has two parents (or none) by definition)
1171 unknown = remote.branches(unknown)
1171 unknown = remote.branches(unknown)
1172 while unknown:
1172 while unknown:
1173 r = []
1173 r = []
1174 while unknown:
1174 while unknown:
1175 n = unknown.pop(0)
1175 n = unknown.pop(0)
1176 if n[0] in seen:
1176 if n[0] in seen:
1177 continue
1177 continue
1178
1178
1179 self.ui.debug(_("examining %s:%s\n")
1179 self.ui.debug(_("examining %s:%s\n")
1180 % (short(n[0]), short(n[1])))
1180 % (short(n[0]), short(n[1])))
1181 if n[0] == nullid: # found the end of the branch
1181 if n[0] == nullid: # found the end of the branch
1182 pass
1182 pass
1183 elif n in seenbranch:
1183 elif n in seenbranch:
1184 self.ui.debug(_("branch already found\n"))
1184 self.ui.debug(_("branch already found\n"))
1185 continue
1185 continue
1186 elif n[1] and n[1] in m: # do we know the base?
1186 elif n[1] and n[1] in m: # do we know the base?
1187 self.ui.debug(_("found incomplete branch %s:%s\n")
1187 self.ui.debug(_("found incomplete branch %s:%s\n")
1188 % (short(n[0]), short(n[1])))
1188 % (short(n[0]), short(n[1])))
1189 search.append(n) # schedule branch range for scanning
1189 search.append(n) # schedule branch range for scanning
1190 seenbranch[n] = 1
1190 seenbranch[n] = 1
1191 else:
1191 else:
1192 if n[1] not in seen and n[1] not in fetch:
1192 if n[1] not in seen and n[1] not in fetch:
1193 if n[2] in m and n[3] in m:
1193 if n[2] in m and n[3] in m:
1194 self.ui.debug(_("found new changeset %s\n") %
1194 self.ui.debug(_("found new changeset %s\n") %
1195 short(n[1]))
1195 short(n[1]))
1196 fetch[n[1]] = 1 # earliest unknown
1196 fetch[n[1]] = 1 # earliest unknown
1197 for p in n[2:4]:
1197 for p in n[2:4]:
1198 if p in m:
1198 if p in m:
1199 base[p] = 1 # latest known
1199 base[p] = 1 # latest known
1200
1200
1201 for p in n[2:4]:
1201 for p in n[2:4]:
1202 if p not in req and p not in m:
1202 if p not in req and p not in m:
1203 r.append(p)
1203 r.append(p)
1204 req[p] = 1
1204 req[p] = 1
1205 seen[n[0]] = 1
1205 seen[n[0]] = 1
1206
1206
1207 if r:
1207 if r:
1208 reqcnt += 1
1208 reqcnt += 1
1209 self.ui.debug(_("request %d: %s\n") %
1209 self.ui.debug(_("request %d: %s\n") %
1210 (reqcnt, " ".join(map(short, r))))
1210 (reqcnt, " ".join(map(short, r))))
1211 for p in xrange(0, len(r), 10):
1211 for p in xrange(0, len(r), 10):
1212 for b in remote.branches(r[p:p+10]):
1212 for b in remote.branches(r[p:p+10]):
1213 self.ui.debug(_("received %s:%s\n") %
1213 self.ui.debug(_("received %s:%s\n") %
1214 (short(b[0]), short(b[1])))
1214 (short(b[0]), short(b[1])))
1215 unknown.append(b)
1215 unknown.append(b)
1216
1216
1217 # do binary search on the branches we found
1217 # do binary search on the branches we found
1218 while search:
1218 while search:
1219 n = search.pop(0)
1219 n = search.pop(0)
1220 reqcnt += 1
1220 reqcnt += 1
1221 l = remote.between([(n[0], n[1])])[0]
1221 l = remote.between([(n[0], n[1])])[0]
1222 l.append(n[1])
1222 l.append(n[1])
1223 p = n[0]
1223 p = n[0]
1224 f = 1
1224 f = 1
1225 for i in l:
1225 for i in l:
1226 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1226 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1227 if i in m:
1227 if i in m:
1228 if f <= 2:
1228 if f <= 2:
1229 self.ui.debug(_("found new branch changeset %s\n") %
1229 self.ui.debug(_("found new branch changeset %s\n") %
1230 short(p))
1230 short(p))
1231 fetch[p] = 1
1231 fetch[p] = 1
1232 base[i] = 1
1232 base[i] = 1
1233 else:
1233 else:
1234 self.ui.debug(_("narrowed branch search to %s:%s\n")
1234 self.ui.debug(_("narrowed branch search to %s:%s\n")
1235 % (short(p), short(i)))
1235 % (short(p), short(i)))
1236 search.append((p, i))
1236 search.append((p, i))
1237 break
1237 break
1238 p, f = i, f * 2
1238 p, f = i, f * 2
1239
1239
1240 # sanity check our fetch list
1240 # sanity check our fetch list
1241 for f in fetch.keys():
1241 for f in fetch.keys():
1242 if f in m:
1242 if f in m:
1243 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1243 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1244
1244
1245 if base.keys() == [nullid]:
1245 if base.keys() == [nullid]:
1246 if force:
1246 if force:
1247 self.ui.warn(_("warning: repository is unrelated\n"))
1247 self.ui.warn(_("warning: repository is unrelated\n"))
1248 else:
1248 else:
1249 raise util.Abort(_("repository is unrelated"))
1249 raise util.Abort(_("repository is unrelated"))
1250
1250
1251 self.ui.debug(_("found new changesets starting at ") +
1251 self.ui.debug(_("found new changesets starting at ") +
1252 " ".join([short(f) for f in fetch]) + "\n")
1252 " ".join([short(f) for f in fetch]) + "\n")
1253
1253
1254 self.ui.debug(_("%d total queries\n") % reqcnt)
1254 self.ui.debug(_("%d total queries\n") % reqcnt)
1255
1255
1256 return fetch.keys()
1256 return fetch.keys()
1257
1257
1258 def findoutgoing(self, remote, base=None, heads=None, force=False):
1258 def findoutgoing(self, remote, base=None, heads=None, force=False):
1259 """Return list of nodes that are roots of subsets not in remote
1259 """Return list of nodes that are roots of subsets not in remote
1260
1260
1261 If base dict is specified, assume that these nodes and their parents
1261 If base dict is specified, assume that these nodes and their parents
1262 exist on the remote side.
1262 exist on the remote side.
1263 If a list of heads is specified, return only nodes which are heads
1263 If a list of heads is specified, return only nodes which are heads
1264 or ancestors of these heads, and return a second element which
1264 or ancestors of these heads, and return a second element which
1265 contains all remote heads which get new children.
1265 contains all remote heads which get new children.
1266 """
1266 """
1267 if base == None:
1267 if base == None:
1268 base = {}
1268 base = {}
1269 self.findincoming(remote, base, heads, force=force)
1269 self.findincoming(remote, base, heads, force=force)
1270
1270
1271 self.ui.debug(_("common changesets up to ")
1271 self.ui.debug(_("common changesets up to ")
1272 + " ".join(map(short, base.keys())) + "\n")
1272 + " ".join(map(short, base.keys())) + "\n")
1273
1273
1274 remain = dict.fromkeys(self.changelog.nodemap)
1274 remain = dict.fromkeys(self.changelog.nodemap)
1275
1275
1276 # prune everything remote has from the tree
1276 # prune everything remote has from the tree
1277 del remain[nullid]
1277 del remain[nullid]
1278 remove = base.keys()
1278 remove = base.keys()
1279 while remove:
1279 while remove:
1280 n = remove.pop(0)
1280 n = remove.pop(0)
1281 if n in remain:
1281 if n in remain:
1282 del remain[n]
1282 del remain[n]
1283 for p in self.changelog.parents(n):
1283 for p in self.changelog.parents(n):
1284 remove.append(p)
1284 remove.append(p)
1285
1285
1286 # find every node whose parents have been pruned
1286 # find every node whose parents have been pruned
1287 subset = []
1287 subset = []
1288 # find every remote head that will get new children
1288 # find every remote head that will get new children
1289 updated_heads = {}
1289 updated_heads = {}
1290 for n in remain:
1290 for n in remain:
1291 p1, p2 = self.changelog.parents(n)
1291 p1, p2 = self.changelog.parents(n)
1292 if p1 not in remain and p2 not in remain:
1292 if p1 not in remain and p2 not in remain:
1293 subset.append(n)
1293 subset.append(n)
1294 if heads:
1294 if heads:
1295 if p1 in heads:
1295 if p1 in heads:
1296 updated_heads[p1] = True
1296 updated_heads[p1] = True
1297 if p2 in heads:
1297 if p2 in heads:
1298 updated_heads[p2] = True
1298 updated_heads[p2] = True
1299
1299
1300 # this is the set of all roots we have to push
1300 # this is the set of all roots we have to push
1301 if heads:
1301 if heads:
1302 return subset, updated_heads.keys()
1302 return subset, updated_heads.keys()
1303 else:
1303 else:
1304 return subset
1304 return subset
1305
1305
1306 def pull(self, remote, heads=None, force=False, lock=None):
1306 def pull(self, remote, heads=None, force=False, lock=None):
1307 mylock = False
1307 mylock = False
1308 if not lock:
1308 if not lock:
1309 lock = self.lock()
1309 lock = self.lock()
1310 mylock = True
1310 mylock = True
1311
1311
1312 try:
1312 try:
1313 fetch = self.findincoming(remote, force=force)
1313 fetch = self.findincoming(remote, force=force)
1314 if fetch == [nullid]:
1314 if fetch == [nullid]:
1315 self.ui.status(_("requesting all changes\n"))
1315 self.ui.status(_("requesting all changes\n"))
1316
1316
1317 if not fetch:
1317 if not fetch:
1318 self.ui.status(_("no changes found\n"))
1318 self.ui.status(_("no changes found\n"))
1319 return 0
1319 return 0
1320
1320
1321 if heads is None:
1321 if heads is None:
1322 cg = remote.changegroup(fetch, 'pull')
1322 cg = remote.changegroup(fetch, 'pull')
1323 else:
1323 else:
1324 if 'changegroupsubset' not in remote.capabilities:
1324 if 'changegroupsubset' not in remote.capabilities:
1325 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1325 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1326 cg = remote.changegroupsubset(fetch, heads, 'pull')
1326 cg = remote.changegroupsubset(fetch, heads, 'pull')
1327 return self.addchangegroup(cg, 'pull', remote.url())
1327 return self.addchangegroup(cg, 'pull', remote.url())
1328 finally:
1328 finally:
1329 if mylock:
1329 if mylock:
1330 lock.release()
1330 lock.release()
1331
1331
1332 def push(self, remote, force=False, revs=None):
1332 def push(self, remote, force=False, revs=None):
1333 # there are two ways to push to remote repo:
1333 # there are two ways to push to remote repo:
1334 #
1334 #
1335 # addchangegroup assumes local user can lock remote
1335 # addchangegroup assumes local user can lock remote
1336 # repo (local filesystem, old ssh servers).
1336 # repo (local filesystem, old ssh servers).
1337 #
1337 #
1338 # unbundle assumes local user cannot lock remote repo (new ssh
1338 # unbundle assumes local user cannot lock remote repo (new ssh
1339 # servers, http servers).
1339 # servers, http servers).
1340
1340
1341 if remote.capable('unbundle'):
1341 if remote.capable('unbundle'):
1342 return self.push_unbundle(remote, force, revs)
1342 return self.push_unbundle(remote, force, revs)
1343 return self.push_addchangegroup(remote, force, revs)
1343 return self.push_addchangegroup(remote, force, revs)
1344
1344
1345 def prepush(self, remote, force, revs):
1345 def prepush(self, remote, force, revs):
1346 base = {}
1346 base = {}
1347 remote_heads = remote.heads()
1347 remote_heads = remote.heads()
1348 inc = self.findincoming(remote, base, remote_heads, force=force)
1348 inc = self.findincoming(remote, base, remote_heads, force=force)
1349
1349
1350 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1350 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1351 if revs is not None:
1351 if revs is not None:
1352 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1352 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1353 else:
1353 else:
1354 bases, heads = update, self.changelog.heads()
1354 bases, heads = update, self.changelog.heads()
1355
1355
1356 if not bases:
1356 if not bases:
1357 self.ui.status(_("no changes found\n"))
1357 self.ui.status(_("no changes found\n"))
1358 return None, 1
1358 return None, 1
1359 elif not force:
1359 elif not force:
1360 # check if we're creating new remote heads
1360 # check if we're creating new remote heads
1361 # to be a remote head after push, node must be either
1361 # to be a remote head after push, node must be either
1362 # - unknown locally
1362 # - unknown locally
1363 # - a local outgoing head descended from update
1363 # - a local outgoing head descended from update
1364 # - a remote head that's known locally and not
1364 # - a remote head that's known locally and not
1365 # ancestral to an outgoing head
1365 # ancestral to an outgoing head
1366
1366
1367 warn = 0
1367 warn = 0
1368
1368
1369 if remote_heads == [nullid]:
1369 if remote_heads == [nullid]:
1370 warn = 0
1370 warn = 0
1371 elif not revs and len(heads) > len(remote_heads):
1371 elif not revs and len(heads) > len(remote_heads):
1372 warn = 1
1372 warn = 1
1373 else:
1373 else:
1374 newheads = list(heads)
1374 newheads = list(heads)
1375 for r in remote_heads:
1375 for r in remote_heads:
1376 if r in self.changelog.nodemap:
1376 if r in self.changelog.nodemap:
1377 desc = self.changelog.heads(r, heads)
1377 desc = self.changelog.heads(r, heads)
1378 l = [h for h in heads if h in desc]
1378 l = [h for h in heads if h in desc]
1379 if not l:
1379 if not l:
1380 newheads.append(r)
1380 newheads.append(r)
1381 else:
1381 else:
1382 newheads.append(r)
1382 newheads.append(r)
1383 if len(newheads) > len(remote_heads):
1383 if len(newheads) > len(remote_heads):
1384 warn = 1
1384 warn = 1
1385
1385
1386 if warn:
1386 if warn:
1387 self.ui.warn(_("abort: push creates new remote branches!\n"))
1387 self.ui.warn(_("abort: push creates new remote branches!\n"))
1388 self.ui.status(_("(did you forget to merge?"
1388 self.ui.status(_("(did you forget to merge?"
1389 " use push -f to force)\n"))
1389 " use push -f to force)\n"))
1390 return None, 1
1390 return None, 1
1391 elif inc:
1391 elif inc:
1392 self.ui.warn(_("note: unsynced remote changes!\n"))
1392 self.ui.warn(_("note: unsynced remote changes!\n"))
1393
1393
1394
1394
1395 if revs is None:
1395 if revs is None:
1396 cg = self.changegroup(update, 'push')
1396 cg = self.changegroup(update, 'push')
1397 else:
1397 else:
1398 cg = self.changegroupsubset(update, revs, 'push')
1398 cg = self.changegroupsubset(update, revs, 'push')
1399 return cg, remote_heads
1399 return cg, remote_heads
1400
1400
1401 def push_addchangegroup(self, remote, force, revs):
1401 def push_addchangegroup(self, remote, force, revs):
1402 lock = remote.lock()
1402 lock = remote.lock()
1403
1403
1404 ret = self.prepush(remote, force, revs)
1404 ret = self.prepush(remote, force, revs)
1405 if ret[0] is not None:
1405 if ret[0] is not None:
1406 cg, remote_heads = ret
1406 cg, remote_heads = ret
1407 return remote.addchangegroup(cg, 'push', self.url())
1407 return remote.addchangegroup(cg, 'push', self.url())
1408 return ret[1]
1408 return ret[1]
1409
1409
1410 def push_unbundle(self, remote, force, revs):
1410 def push_unbundle(self, remote, force, revs):
1411 # local repo finds heads on server, finds out what revs it
1411 # local repo finds heads on server, finds out what revs it
1412 # must push. once revs transferred, if server finds it has
1412 # must push. once revs transferred, if server finds it has
1413 # different heads (someone else won commit/push race), server
1413 # different heads (someone else won commit/push race), server
1414 # aborts.
1414 # aborts.
1415
1415
1416 ret = self.prepush(remote, force, revs)
1416 ret = self.prepush(remote, force, revs)
1417 if ret[0] is not None:
1417 if ret[0] is not None:
1418 cg, remote_heads = ret
1418 cg, remote_heads = ret
1419 if force: remote_heads = ['force']
1419 if force: remote_heads = ['force']
1420 return remote.unbundle(cg, remote_heads, 'push')
1420 return remote.unbundle(cg, remote_heads, 'push')
1421 return ret[1]
1421 return ret[1]
1422
1422
1423 def changegroupinfo(self, nodes):
1423 def changegroupinfo(self, nodes):
1424 self.ui.note(_("%d changesets found\n") % len(nodes))
1424 self.ui.note(_("%d changesets found\n") % len(nodes))
1425 if self.ui.debugflag:
1425 if self.ui.debugflag:
1426 self.ui.debug(_("List of changesets:\n"))
1426 self.ui.debug(_("List of changesets:\n"))
1427 for node in nodes:
1427 for node in nodes:
1428 self.ui.debug("%s\n" % hex(node))
1428 self.ui.debug("%s\n" % hex(node))
1429
1429
1430 def changegroupsubset(self, bases, heads, source):
1430 def changegroupsubset(self, bases, heads, source):
1431 """This function generates a changegroup consisting of all the nodes
1431 """This function generates a changegroup consisting of all the nodes
1432 that are descendents of any of the bases, and ancestors of any of
1432 that are descendents of any of the bases, and ancestors of any of
1433 the heads.
1433 the heads.
1434
1434
1435 It is fairly complex as determining which filenodes and which
1435 It is fairly complex as determining which filenodes and which
1436 manifest nodes need to be included for the changeset to be complete
1436 manifest nodes need to be included for the changeset to be complete
1437 is non-trivial.
1437 is non-trivial.
1438
1438
1439 Another wrinkle is doing the reverse, figuring out which changeset in
1439 Another wrinkle is doing the reverse, figuring out which changeset in
1440 the changegroup a particular filenode or manifestnode belongs to."""
1440 the changegroup a particular filenode or manifestnode belongs to."""
1441
1441
1442 self.hook('preoutgoing', throw=True, source=source)
1442 self.hook('preoutgoing', throw=True, source=source)
1443
1443
1444 # Set up some initial variables
1444 # Set up some initial variables
1445 # Make it easy to refer to self.changelog
1445 # Make it easy to refer to self.changelog
1446 cl = self.changelog
1446 cl = self.changelog
1447 # msng is short for missing - compute the list of changesets in this
1447 # msng is short for missing - compute the list of changesets in this
1448 # changegroup.
1448 # changegroup.
1449 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1449 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1450 self.changegroupinfo(msng_cl_lst)
1450 self.changegroupinfo(msng_cl_lst)
1451 # Some bases may turn out to be superfluous, and some heads may be
1451 # Some bases may turn out to be superfluous, and some heads may be
1452 # too. nodesbetween will return the minimal set of bases and heads
1452 # too. nodesbetween will return the minimal set of bases and heads
1453 # necessary to re-create the changegroup.
1453 # necessary to re-create the changegroup.
1454
1454
1455 # Known heads are the list of heads that it is assumed the recipient
1455 # Known heads are the list of heads that it is assumed the recipient
1456 # of this changegroup will know about.
1456 # of this changegroup will know about.
1457 knownheads = {}
1457 knownheads = {}
1458 # We assume that all parents of bases are known heads.
1458 # We assume that all parents of bases are known heads.
1459 for n in bases:
1459 for n in bases:
1460 for p in cl.parents(n):
1460 for p in cl.parents(n):
1461 if p != nullid:
1461 if p != nullid:
1462 knownheads[p] = 1
1462 knownheads[p] = 1
1463 knownheads = knownheads.keys()
1463 knownheads = knownheads.keys()
1464 if knownheads:
1464 if knownheads:
1465 # Now that we know what heads are known, we can compute which
1465 # Now that we know what heads are known, we can compute which
1466 # changesets are known. The recipient must know about all
1466 # changesets are known. The recipient must know about all
1467 # changesets required to reach the known heads from the null
1467 # changesets required to reach the known heads from the null
1468 # changeset.
1468 # changeset.
1469 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1469 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1470 junk = None
1470 junk = None
1471 # Transform the list into an ersatz set.
1471 # Transform the list into an ersatz set.
1472 has_cl_set = dict.fromkeys(has_cl_set)
1472 has_cl_set = dict.fromkeys(has_cl_set)
1473 else:
1473 else:
1474 # If there were no known heads, the recipient cannot be assumed to
1474 # If there were no known heads, the recipient cannot be assumed to
1475 # know about any changesets.
1475 # know about any changesets.
1476 has_cl_set = {}
1476 has_cl_set = {}
1477
1477
1478 # Make it easy to refer to self.manifest
1478 # Make it easy to refer to self.manifest
1479 mnfst = self.manifest
1479 mnfst = self.manifest
1480 # We don't know which manifests are missing yet
1480 # We don't know which manifests are missing yet
1481 msng_mnfst_set = {}
1481 msng_mnfst_set = {}
1482 # Nor do we know which filenodes are missing.
1482 # Nor do we know which filenodes are missing.
1483 msng_filenode_set = {}
1483 msng_filenode_set = {}
1484
1484
1485 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1485 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1486 junk = None
1486 junk = None
1487
1487
1488 # A changeset always belongs to itself, so the changenode lookup
1488 # A changeset always belongs to itself, so the changenode lookup
1489 # function for a changenode is identity.
1489 # function for a changenode is identity.
1490 def identity(x):
1490 def identity(x):
1491 return x
1491 return x
1492
1492
1493 # A function generating function. Sets up an environment for the
1493 # A function generating function. Sets up an environment for the
1494 # inner function.
1494 # inner function.
1495 def cmp_by_rev_func(revlog):
1495 def cmp_by_rev_func(revlog):
1496 # Compare two nodes by their revision number in the environment's
1496 # Compare two nodes by their revision number in the environment's
1497 # revision history. Since the revision number both represents the
1497 # revision history. Since the revision number both represents the
1498 # most efficient order to read the nodes in, and represents a
1498 # most efficient order to read the nodes in, and represents a
1499 # topological sorting of the nodes, this function is often useful.
1499 # topological sorting of the nodes, this function is often useful.
1500 def cmp_by_rev(a, b):
1500 def cmp_by_rev(a, b):
1501 return cmp(revlog.rev(a), revlog.rev(b))
1501 return cmp(revlog.rev(a), revlog.rev(b))
1502 return cmp_by_rev
1502 return cmp_by_rev
1503
1503
1504 # If we determine that a particular file or manifest node must be a
1504 # If we determine that a particular file or manifest node must be a
1505 # node that the recipient of the changegroup will already have, we can
1505 # node that the recipient of the changegroup will already have, we can
1506 # also assume the recipient will have all the parents. This function
1506 # also assume the recipient will have all the parents. This function
1507 # prunes them from the set of missing nodes.
1507 # prunes them from the set of missing nodes.
1508 def prune_parents(revlog, hasset, msngset):
1508 def prune_parents(revlog, hasset, msngset):
1509 haslst = hasset.keys()
1509 haslst = hasset.keys()
1510 haslst.sort(cmp_by_rev_func(revlog))
1510 haslst.sort(cmp_by_rev_func(revlog))
1511 for node in haslst:
1511 for node in haslst:
1512 parentlst = [p for p in revlog.parents(node) if p != nullid]
1512 parentlst = [p for p in revlog.parents(node) if p != nullid]
1513 while parentlst:
1513 while parentlst:
1514 n = parentlst.pop()
1514 n = parentlst.pop()
1515 if n not in hasset:
1515 if n not in hasset:
1516 hasset[n] = 1
1516 hasset[n] = 1
1517 p = [p for p in revlog.parents(n) if p != nullid]
1517 p = [p for p in revlog.parents(n) if p != nullid]
1518 parentlst.extend(p)
1518 parentlst.extend(p)
1519 for n in hasset:
1519 for n in hasset:
1520 msngset.pop(n, None)
1520 msngset.pop(n, None)
1521
1521
1522 # This is a function generating function used to set up an environment
1522 # This is a function generating function used to set up an environment
1523 # for the inner function to execute in.
1523 # for the inner function to execute in.
1524 def manifest_and_file_collector(changedfileset):
1524 def manifest_and_file_collector(changedfileset):
1525 # This is an information gathering function that gathers
1525 # This is an information gathering function that gathers
1526 # information from each changeset node that goes out as part of
1526 # information from each changeset node that goes out as part of
1527 # the changegroup. The information gathered is a list of which
1527 # the changegroup. The information gathered is a list of which
1528 # manifest nodes are potentially required (the recipient may
1528 # manifest nodes are potentially required (the recipient may
1529 # already have them) and total list of all files which were
1529 # already have them) and total list of all files which were
1530 # changed in any changeset in the changegroup.
1530 # changed in any changeset in the changegroup.
1531 #
1531 #
1532 # We also remember the first changenode we saw any manifest
1532 # We also remember the first changenode we saw any manifest
1533 # referenced by so we can later determine which changenode 'owns'
1533 # referenced by so we can later determine which changenode 'owns'
1534 # the manifest.
1534 # the manifest.
1535 def collect_manifests_and_files(clnode):
1535 def collect_manifests_and_files(clnode):
1536 c = cl.read(clnode)
1536 c = cl.read(clnode)
1537 for f in c[3]:
1537 for f in c[3]:
1538 # This is to make sure we only have one instance of each
1538 # This is to make sure we only have one instance of each
1539 # filename string for each filename.
1539 # filename string for each filename.
1540 changedfileset.setdefault(f, f)
1540 changedfileset.setdefault(f, f)
1541 msng_mnfst_set.setdefault(c[0], clnode)
1541 msng_mnfst_set.setdefault(c[0], clnode)
1542 return collect_manifests_and_files
1542 return collect_manifests_and_files
1543
1543
1544 # Figure out which manifest nodes (of the ones we think might be part
1544 # Figure out which manifest nodes (of the ones we think might be part
1545 # of the changegroup) the recipient must know about and remove them
1545 # of the changegroup) the recipient must know about and remove them
1546 # from the changegroup.
1546 # from the changegroup.
1547 def prune_manifests():
1547 def prune_manifests():
1548 has_mnfst_set = {}
1548 has_mnfst_set = {}
1549 for n in msng_mnfst_set:
1549 for n in msng_mnfst_set:
1550 # If a 'missing' manifest thinks it belongs to a changenode
1550 # If a 'missing' manifest thinks it belongs to a changenode
1551 # the recipient is assumed to have, obviously the recipient
1551 # the recipient is assumed to have, obviously the recipient
1552 # must have that manifest.
1552 # must have that manifest.
1553 linknode = cl.node(mnfst.linkrev(n))
1553 linknode = cl.node(mnfst.linkrev(n))
1554 if linknode in has_cl_set:
1554 if linknode in has_cl_set:
1555 has_mnfst_set[n] = 1
1555 has_mnfst_set[n] = 1
1556 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1556 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1557
1557
1558 # Use the information collected in collect_manifests_and_files to say
1558 # Use the information collected in collect_manifests_and_files to say
1559 # which changenode any manifestnode belongs to.
1559 # which changenode any manifestnode belongs to.
1560 def lookup_manifest_link(mnfstnode):
1560 def lookup_manifest_link(mnfstnode):
1561 return msng_mnfst_set[mnfstnode]
1561 return msng_mnfst_set[mnfstnode]
1562
1562
1563 # A function generating function that sets up the initial environment
1563 # A function generating function that sets up the initial environment
1564 # the inner function.
1564 # the inner function.
1565 def filenode_collector(changedfiles):
1565 def filenode_collector(changedfiles):
1566 next_rev = [0]
1566 next_rev = [0]
1567 # This gathers information from each manifestnode included in the
1567 # This gathers information from each manifestnode included in the
1568 # changegroup about which filenodes the manifest node references
1568 # changegroup about which filenodes the manifest node references
1569 # so we can include those in the changegroup too.
1569 # so we can include those in the changegroup too.
1570 #
1570 #
1571 # It also remembers which changenode each filenode belongs to. It
1571 # It also remembers which changenode each filenode belongs to. It
1572 # does this by assuming the a filenode belongs to the changenode
1572 # does this by assuming the a filenode belongs to the changenode
1573 # the first manifest that references it belongs to.
1573 # the first manifest that references it belongs to.
1574 def collect_msng_filenodes(mnfstnode):
1574 def collect_msng_filenodes(mnfstnode):
1575 r = mnfst.rev(mnfstnode)
1575 r = mnfst.rev(mnfstnode)
1576 if r == next_rev[0]:
1576 if r == next_rev[0]:
1577 # If the last rev we looked at was the one just previous,
1577 # If the last rev we looked at was the one just previous,
1578 # we only need to see a diff.
1578 # we only need to see a diff.
1579 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1579 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1580 # For each line in the delta
1580 # For each line in the delta
1581 for dline in delta.splitlines():
1581 for dline in delta.splitlines():
1582 # get the filename and filenode for that line
1582 # get the filename and filenode for that line
1583 f, fnode = dline.split('\0')
1583 f, fnode = dline.split('\0')
1584 fnode = bin(fnode[:40])
1584 fnode = bin(fnode[:40])
1585 f = changedfiles.get(f, None)
1585 f = changedfiles.get(f, None)
1586 # And if the file is in the list of files we care
1586 # And if the file is in the list of files we care
1587 # about.
1587 # about.
1588 if f is not None:
1588 if f is not None:
1589 # Get the changenode this manifest belongs to
1589 # Get the changenode this manifest belongs to
1590 clnode = msng_mnfst_set[mnfstnode]
1590 clnode = msng_mnfst_set[mnfstnode]
1591 # Create the set of filenodes for the file if
1591 # Create the set of filenodes for the file if
1592 # there isn't one already.
1592 # there isn't one already.
1593 ndset = msng_filenode_set.setdefault(f, {})
1593 ndset = msng_filenode_set.setdefault(f, {})
1594 # And set the filenode's changelog node to the
1594 # And set the filenode's changelog node to the
1595 # manifest's if it hasn't been set already.
1595 # manifest's if it hasn't been set already.
1596 ndset.setdefault(fnode, clnode)
1596 ndset.setdefault(fnode, clnode)
1597 else:
1597 else:
1598 # Otherwise we need a full manifest.
1598 # Otherwise we need a full manifest.
1599 m = mnfst.read(mnfstnode)
1599 m = mnfst.read(mnfstnode)
1600 # For every file in we care about.
1600 # For every file in we care about.
1601 for f in changedfiles:
1601 for f in changedfiles:
1602 fnode = m.get(f, None)
1602 fnode = m.get(f, None)
1603 # If it's in the manifest
1603 # If it's in the manifest
1604 if fnode is not None:
1604 if fnode is not None:
1605 # See comments above.
1605 # See comments above.
1606 clnode = msng_mnfst_set[mnfstnode]
1606 clnode = msng_mnfst_set[mnfstnode]
1607 ndset = msng_filenode_set.setdefault(f, {})
1607 ndset = msng_filenode_set.setdefault(f, {})
1608 ndset.setdefault(fnode, clnode)
1608 ndset.setdefault(fnode, clnode)
1609 # Remember the revision we hope to see next.
1609 # Remember the revision we hope to see next.
1610 next_rev[0] = r + 1
1610 next_rev[0] = r + 1
1611 return collect_msng_filenodes
1611 return collect_msng_filenodes
1612
1612
1613 # We have a list of filenodes we think we need for a file, lets remove
1613 # We have a list of filenodes we think we need for a file, lets remove
1614 # all those we now the recipient must have.
1614 # all those we now the recipient must have.
1615 def prune_filenodes(f, filerevlog):
1615 def prune_filenodes(f, filerevlog):
1616 msngset = msng_filenode_set[f]
1616 msngset = msng_filenode_set[f]
1617 hasset = {}
1617 hasset = {}
1618 # If a 'missing' filenode thinks it belongs to a changenode we
1618 # If a 'missing' filenode thinks it belongs to a changenode we
1619 # assume the recipient must have, then the recipient must have
1619 # assume the recipient must have, then the recipient must have
1620 # that filenode.
1620 # that filenode.
1621 for n in msngset:
1621 for n in msngset:
1622 clnode = cl.node(filerevlog.linkrev(n))
1622 clnode = cl.node(filerevlog.linkrev(n))
1623 if clnode in has_cl_set:
1623 if clnode in has_cl_set:
1624 hasset[n] = 1
1624 hasset[n] = 1
1625 prune_parents(filerevlog, hasset, msngset)
1625 prune_parents(filerevlog, hasset, msngset)
1626
1626
1627 # A function generator function that sets up the a context for the
1627 # A function generator function that sets up the a context for the
1628 # inner function.
1628 # inner function.
1629 def lookup_filenode_link_func(fname):
1629 def lookup_filenode_link_func(fname):
1630 msngset = msng_filenode_set[fname]
1630 msngset = msng_filenode_set[fname]
1631 # Lookup the changenode the filenode belongs to.
1631 # Lookup the changenode the filenode belongs to.
1632 def lookup_filenode_link(fnode):
1632 def lookup_filenode_link(fnode):
1633 return msngset[fnode]
1633 return msngset[fnode]
1634 return lookup_filenode_link
1634 return lookup_filenode_link
1635
1635
1636 # Now that we have all theses utility functions to help out and
1636 # Now that we have all theses utility functions to help out and
1637 # logically divide up the task, generate the group.
1637 # logically divide up the task, generate the group.
1638 def gengroup():
1638 def gengroup():
1639 # The set of changed files starts empty.
1639 # The set of changed files starts empty.
1640 changedfiles = {}
1640 changedfiles = {}
1641 # Create a changenode group generator that will call our functions
1641 # Create a changenode group generator that will call our functions
1642 # back to lookup the owning changenode and collect information.
1642 # back to lookup the owning changenode and collect information.
1643 group = cl.group(msng_cl_lst, identity,
1643 group = cl.group(msng_cl_lst, identity,
1644 manifest_and_file_collector(changedfiles))
1644 manifest_and_file_collector(changedfiles))
1645 for chnk in group:
1645 for chnk in group:
1646 yield chnk
1646 yield chnk
1647
1647
1648 # The list of manifests has been collected by the generator
1648 # The list of manifests has been collected by the generator
1649 # calling our functions back.
1649 # calling our functions back.
1650 prune_manifests()
1650 prune_manifests()
1651 msng_mnfst_lst = msng_mnfst_set.keys()
1651 msng_mnfst_lst = msng_mnfst_set.keys()
1652 # Sort the manifestnodes by revision number.
1652 # Sort the manifestnodes by revision number.
1653 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1653 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1654 # Create a generator for the manifestnodes that calls our lookup
1654 # Create a generator for the manifestnodes that calls our lookup
1655 # and data collection functions back.
1655 # and data collection functions back.
1656 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1656 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1657 filenode_collector(changedfiles))
1657 filenode_collector(changedfiles))
1658 for chnk in group:
1658 for chnk in group:
1659 yield chnk
1659 yield chnk
1660
1660
1661 # These are no longer needed, dereference and toss the memory for
1661 # These are no longer needed, dereference and toss the memory for
1662 # them.
1662 # them.
1663 msng_mnfst_lst = None
1663 msng_mnfst_lst = None
1664 msng_mnfst_set.clear()
1664 msng_mnfst_set.clear()
1665
1665
1666 changedfiles = changedfiles.keys()
1666 changedfiles = changedfiles.keys()
1667 changedfiles.sort()
1667 changedfiles.sort()
1668 # Go through all our files in order sorted by name.
1668 # Go through all our files in order sorted by name.
1669 for fname in changedfiles:
1669 for fname in changedfiles:
1670 filerevlog = self.file(fname)
1670 filerevlog = self.file(fname)
1671 # Toss out the filenodes that the recipient isn't really
1671 # Toss out the filenodes that the recipient isn't really
1672 # missing.
1672 # missing.
1673 if msng_filenode_set.has_key(fname):
1673 if msng_filenode_set.has_key(fname):
1674 prune_filenodes(fname, filerevlog)
1674 prune_filenodes(fname, filerevlog)
1675 msng_filenode_lst = msng_filenode_set[fname].keys()
1675 msng_filenode_lst = msng_filenode_set[fname].keys()
1676 else:
1676 else:
1677 msng_filenode_lst = []
1677 msng_filenode_lst = []
1678 # If any filenodes are left, generate the group for them,
1678 # If any filenodes are left, generate the group for them,
1679 # otherwise don't bother.
1679 # otherwise don't bother.
1680 if len(msng_filenode_lst) > 0:
1680 if len(msng_filenode_lst) > 0:
1681 yield changegroup.genchunk(fname)
1681 yield changegroup.genchunk(fname)
1682 # Sort the filenodes by their revision #
1682 # Sort the filenodes by their revision #
1683 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1683 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1684 # Create a group generator and only pass in a changenode
1684 # Create a group generator and only pass in a changenode
1685 # lookup function as we need to collect no information
1685 # lookup function as we need to collect no information
1686 # from filenodes.
1686 # from filenodes.
1687 group = filerevlog.group(msng_filenode_lst,
1687 group = filerevlog.group(msng_filenode_lst,
1688 lookup_filenode_link_func(fname))
1688 lookup_filenode_link_func(fname))
1689 for chnk in group:
1689 for chnk in group:
1690 yield chnk
1690 yield chnk
1691 if msng_filenode_set.has_key(fname):
1691 if msng_filenode_set.has_key(fname):
1692 # Don't need this anymore, toss it to free memory.
1692 # Don't need this anymore, toss it to free memory.
1693 del msng_filenode_set[fname]
1693 del msng_filenode_set[fname]
1694 # Signal that no more groups are left.
1694 # Signal that no more groups are left.
1695 yield changegroup.closechunk()
1695 yield changegroup.closechunk()
1696
1696
1697 if msng_cl_lst:
1697 if msng_cl_lst:
1698 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1698 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1699
1699
1700 return util.chunkbuffer(gengroup())
1700 return util.chunkbuffer(gengroup())
1701
1701
1702 def changegroup(self, basenodes, source):
1702 def changegroup(self, basenodes, source):
1703 """Generate a changegroup of all nodes that we have that a recipient
1703 """Generate a changegroup of all nodes that we have that a recipient
1704 doesn't.
1704 doesn't.
1705
1705
1706 This is much easier than the previous function as we can assume that
1706 This is much easier than the previous function as we can assume that
1707 the recipient has any changenode we aren't sending them."""
1707 the recipient has any changenode we aren't sending them."""
1708
1708
1709 self.hook('preoutgoing', throw=True, source=source)
1709 self.hook('preoutgoing', throw=True, source=source)
1710
1710
1711 cl = self.changelog
1711 cl = self.changelog
1712 nodes = cl.nodesbetween(basenodes, None)[0]
1712 nodes = cl.nodesbetween(basenodes, None)[0]
1713 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1713 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1714 self.changegroupinfo(nodes)
1714 self.changegroupinfo(nodes)
1715
1715
1716 def identity(x):
1716 def identity(x):
1717 return x
1717 return x
1718
1718
1719 def gennodelst(revlog):
1719 def gennodelst(revlog):
1720 for r in xrange(0, revlog.count()):
1720 for r in xrange(0, revlog.count()):
1721 n = revlog.node(r)
1721 n = revlog.node(r)
1722 if revlog.linkrev(n) in revset:
1722 if revlog.linkrev(n) in revset:
1723 yield n
1723 yield n
1724
1724
1725 def changed_file_collector(changedfileset):
1725 def changed_file_collector(changedfileset):
1726 def collect_changed_files(clnode):
1726 def collect_changed_files(clnode):
1727 c = cl.read(clnode)
1727 c = cl.read(clnode)
1728 for fname in c[3]:
1728 for fname in c[3]:
1729 changedfileset[fname] = 1
1729 changedfileset[fname] = 1
1730 return collect_changed_files
1730 return collect_changed_files
1731
1731
1732 def lookuprevlink_func(revlog):
1732 def lookuprevlink_func(revlog):
1733 def lookuprevlink(n):
1733 def lookuprevlink(n):
1734 return cl.node(revlog.linkrev(n))
1734 return cl.node(revlog.linkrev(n))
1735 return lookuprevlink
1735 return lookuprevlink
1736
1736
1737 def gengroup():
1737 def gengroup():
1738 # construct a list of all changed files
1738 # construct a list of all changed files
1739 changedfiles = {}
1739 changedfiles = {}
1740
1740
1741 for chnk in cl.group(nodes, identity,
1741 for chnk in cl.group(nodes, identity,
1742 changed_file_collector(changedfiles)):
1742 changed_file_collector(changedfiles)):
1743 yield chnk
1743 yield chnk
1744 changedfiles = changedfiles.keys()
1744 changedfiles = changedfiles.keys()
1745 changedfiles.sort()
1745 changedfiles.sort()
1746
1746
1747 mnfst = self.manifest
1747 mnfst = self.manifest
1748 nodeiter = gennodelst(mnfst)
1748 nodeiter = gennodelst(mnfst)
1749 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1749 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1750 yield chnk
1750 yield chnk
1751
1751
1752 for fname in changedfiles:
1752 for fname in changedfiles:
1753 filerevlog = self.file(fname)
1753 filerevlog = self.file(fname)
1754 nodeiter = gennodelst(filerevlog)
1754 nodeiter = gennodelst(filerevlog)
1755 nodeiter = list(nodeiter)
1755 nodeiter = list(nodeiter)
1756 if nodeiter:
1756 if nodeiter:
1757 yield changegroup.genchunk(fname)
1757 yield changegroup.genchunk(fname)
1758 lookup = lookuprevlink_func(filerevlog)
1758 lookup = lookuprevlink_func(filerevlog)
1759 for chnk in filerevlog.group(nodeiter, lookup):
1759 for chnk in filerevlog.group(nodeiter, lookup):
1760 yield chnk
1760 yield chnk
1761
1761
1762 yield changegroup.closechunk()
1762 yield changegroup.closechunk()
1763
1763
1764 if nodes:
1764 if nodes:
1765 self.hook('outgoing', node=hex(nodes[0]), source=source)
1765 self.hook('outgoing', node=hex(nodes[0]), source=source)
1766
1766
1767 return util.chunkbuffer(gengroup())
1767 return util.chunkbuffer(gengroup())
1768
1768
1769 def addchangegroup(self, source, srctype, url):
1769 def addchangegroup(self, source, srctype, url):
1770 """add changegroup to repo.
1770 """add changegroup to repo.
1771
1771
1772 return values:
1772 return values:
1773 - nothing changed or no source: 0
1773 - nothing changed or no source: 0
1774 - more heads than before: 1+added heads (2..n)
1774 - more heads than before: 1+added heads (2..n)
1775 - less heads than before: -1-removed heads (-2..-n)
1775 - less heads than before: -1-removed heads (-2..-n)
1776 - number of heads stays the same: 1
1776 - number of heads stays the same: 1
1777 """
1777 """
1778 def csmap(x):
1778 def csmap(x):
1779 self.ui.debug(_("add changeset %s\n") % short(x))
1779 self.ui.debug(_("add changeset %s\n") % short(x))
1780 return cl.count()
1780 return cl.count()
1781
1781
1782 def revmap(x):
1782 def revmap(x):
1783 return cl.rev(x)
1783 return cl.rev(x)
1784
1784
1785 if not source:
1785 if not source:
1786 return 0
1786 return 0
1787
1787
1788 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1788 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1789
1789
1790 changesets = files = revisions = 0
1790 changesets = files = revisions = 0
1791
1791
1792 tr = self.transaction()
1792 tr = self.transaction()
1793
1793
1794 # write changelog data to temp files so concurrent readers will not see
1794 # write changelog data to temp files so concurrent readers will not see
1795 # inconsistent view
1795 # inconsistent view
1796 cl = self.changelog
1796 cl = self.changelog
1797 cl.delayupdate()
1797 cl.delayupdate()
1798 oldheads = len(cl.heads())
1798 oldheads = len(cl.heads())
1799
1799
1800 # pull off the changeset group
1800 # pull off the changeset group
1801 self.ui.status(_("adding changesets\n"))
1801 self.ui.status(_("adding changesets\n"))
1802 cor = cl.count() - 1
1802 cor = cl.count() - 1
1803 chunkiter = changegroup.chunkiter(source)
1803 chunkiter = changegroup.chunkiter(source)
1804 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1804 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1805 raise util.Abort(_("received changelog group is empty"))
1805 raise util.Abort(_("received changelog group is empty"))
1806 cnr = cl.count() - 1
1806 cnr = cl.count() - 1
1807 changesets = cnr - cor
1807 changesets = cnr - cor
1808
1808
1809 # pull off the manifest group
1809 # pull off the manifest group
1810 self.ui.status(_("adding manifests\n"))
1810 self.ui.status(_("adding manifests\n"))
1811 chunkiter = changegroup.chunkiter(source)
1811 chunkiter = changegroup.chunkiter(source)
1812 # no need to check for empty manifest group here:
1812 # no need to check for empty manifest group here:
1813 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1813 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1814 # no new manifest will be created and the manifest group will
1814 # no new manifest will be created and the manifest group will
1815 # be empty during the pull
1815 # be empty during the pull
1816 self.manifest.addgroup(chunkiter, revmap, tr)
1816 self.manifest.addgroup(chunkiter, revmap, tr)
1817
1817
1818 # process the files
1818 # process the files
1819 self.ui.status(_("adding file changes\n"))
1819 self.ui.status(_("adding file changes\n"))
1820 while 1:
1820 while 1:
1821 f = changegroup.getchunk(source)
1821 f = changegroup.getchunk(source)
1822 if not f:
1822 if not f:
1823 break
1823 break
1824 self.ui.debug(_("adding %s revisions\n") % f)
1824 self.ui.debug(_("adding %s revisions\n") % f)
1825 fl = self.file(f)
1825 fl = self.file(f)
1826 o = fl.count()
1826 o = fl.count()
1827 chunkiter = changegroup.chunkiter(source)
1827 chunkiter = changegroup.chunkiter(source)
1828 if fl.addgroup(chunkiter, revmap, tr) is None:
1828 if fl.addgroup(chunkiter, revmap, tr) is None:
1829 raise util.Abort(_("received file revlog group is empty"))
1829 raise util.Abort(_("received file revlog group is empty"))
1830 revisions += fl.count() - o
1830 revisions += fl.count() - o
1831 files += 1
1831 files += 1
1832
1832
1833 # make changelog see real files again
1833 # make changelog see real files again
1834 cl.finalize(tr)
1834 cl.finalize(tr)
1835
1835
1836 newheads = len(self.changelog.heads())
1836 newheads = len(self.changelog.heads())
1837 heads = ""
1837 heads = ""
1838 if oldheads and newheads != oldheads:
1838 if oldheads and newheads != oldheads:
1839 heads = _(" (%+d heads)") % (newheads - oldheads)
1839 heads = _(" (%+d heads)") % (newheads - oldheads)
1840
1840
1841 self.ui.status(_("added %d changesets"
1841 self.ui.status(_("added %d changesets"
1842 " with %d changes to %d files%s\n")
1842 " with %d changes to %d files%s\n")
1843 % (changesets, revisions, files, heads))
1843 % (changesets, revisions, files, heads))
1844
1844
1845 if changesets > 0:
1845 if changesets > 0:
1846 self.hook('pretxnchangegroup', throw=True,
1846 self.hook('pretxnchangegroup', throw=True,
1847 node=hex(self.changelog.node(cor+1)), source=srctype,
1847 node=hex(self.changelog.node(cor+1)), source=srctype,
1848 url=url)
1848 url=url)
1849
1849
1850 tr.close()
1850 tr.close()
1851
1851
1852 if changesets > 0:
1852 if changesets > 0:
1853 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1853 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1854 source=srctype, url=url)
1854 source=srctype, url=url)
1855
1855
1856 for i in xrange(cor + 1, cnr + 1):
1856 for i in xrange(cor + 1, cnr + 1):
1857 self.hook("incoming", node=hex(self.changelog.node(i)),
1857 self.hook("incoming", node=hex(self.changelog.node(i)),
1858 source=srctype, url=url)
1858 source=srctype, url=url)
1859
1859
1860 # never return 0 here:
1860 # never return 0 here:
1861 if newheads < oldheads:
1861 if newheads < oldheads:
1862 return newheads - oldheads - 1
1862 return newheads - oldheads - 1
1863 else:
1863 else:
1864 return newheads - oldheads + 1
1864 return newheads - oldheads + 1
1865
1865
1866
1866
1867 def stream_in(self, remote):
1867 def stream_in(self, remote):
1868 fp = remote.stream_out()
1868 fp = remote.stream_out()
1869 l = fp.readline()
1869 l = fp.readline()
1870 try:
1870 try:
1871 resp = int(l)
1871 resp = int(l)
1872 except ValueError:
1872 except ValueError:
1873 raise util.UnexpectedOutput(
1873 raise util.UnexpectedOutput(
1874 _('Unexpected response from remote server:'), l)
1874 _('Unexpected response from remote server:'), l)
1875 if resp == 1:
1875 if resp == 1:
1876 raise util.Abort(_('operation forbidden by server'))
1876 raise util.Abort(_('operation forbidden by server'))
1877 elif resp == 2:
1877 elif resp == 2:
1878 raise util.Abort(_('locking the remote repository failed'))
1878 raise util.Abort(_('locking the remote repository failed'))
1879 elif resp != 0:
1879 elif resp != 0:
1880 raise util.Abort(_('the server sent an unknown error code'))
1880 raise util.Abort(_('the server sent an unknown error code'))
1881 self.ui.status(_('streaming all changes\n'))
1881 self.ui.status(_('streaming all changes\n'))
1882 l = fp.readline()
1882 l = fp.readline()
1883 try:
1883 try:
1884 total_files, total_bytes = map(int, l.split(' ', 1))
1884 total_files, total_bytes = map(int, l.split(' ', 1))
1885 except ValueError, TypeError:
1885 except ValueError, TypeError:
1886 raise util.UnexpectedOutput(
1886 raise util.UnexpectedOutput(
1887 _('Unexpected response from remote server:'), l)
1887 _('Unexpected response from remote server:'), l)
1888 self.ui.status(_('%d files to transfer, %s of data\n') %
1888 self.ui.status(_('%d files to transfer, %s of data\n') %
1889 (total_files, util.bytecount(total_bytes)))
1889 (total_files, util.bytecount(total_bytes)))
1890 start = time.time()
1890 start = time.time()
1891 for i in xrange(total_files):
1891 for i in xrange(total_files):
1892 # XXX doesn't support '\n' or '\r' in filenames
1892 # XXX doesn't support '\n' or '\r' in filenames
1893 l = fp.readline()
1893 l = fp.readline()
1894 try:
1894 try:
1895 name, size = l.split('\0', 1)
1895 name, size = l.split('\0', 1)
1896 size = int(size)
1896 size = int(size)
1897 except ValueError, TypeError:
1897 except ValueError, TypeError:
1898 raise util.UnexpectedOutput(
1898 raise util.UnexpectedOutput(
1899 _('Unexpected response from remote server:'), l)
1899 _('Unexpected response from remote server:'), l)
1900 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1900 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1901 ofp = self.sopener(name, 'w')
1901 ofp = self.sopener(name, 'w')
1902 for chunk in util.filechunkiter(fp, limit=size):
1902 for chunk in util.filechunkiter(fp, limit=size):
1903 ofp.write(chunk)
1903 ofp.write(chunk)
1904 ofp.close()
1904 ofp.close()
1905 elapsed = time.time() - start
1905 elapsed = time.time() - start
1906 if elapsed <= 0:
1906 if elapsed <= 0:
1907 elapsed = 0.001
1907 elapsed = 0.001
1908 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1908 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1909 (util.bytecount(total_bytes), elapsed,
1909 (util.bytecount(total_bytes), elapsed,
1910 util.bytecount(total_bytes / elapsed)))
1910 util.bytecount(total_bytes / elapsed)))
1911 self.invalidate()
1911 self.invalidate()
1912 return len(self.heads()) + 1
1912 return len(self.heads()) + 1
1913
1913
1914 def clone(self, remote, heads=[], stream=False):
1914 def clone(self, remote, heads=[], stream=False):
1915 '''clone remote repository.
1915 '''clone remote repository.
1916
1916
1917 keyword arguments:
1917 keyword arguments:
1918 heads: list of revs to clone (forces use of pull)
1918 heads: list of revs to clone (forces use of pull)
1919 stream: use streaming clone if possible'''
1919 stream: use streaming clone if possible'''
1920
1920
1921 # now, all clients that can request uncompressed clones can
1921 # now, all clients that can request uncompressed clones can
1922 # read repo formats supported by all servers that can serve
1922 # read repo formats supported by all servers that can serve
1923 # them.
1923 # them.
1924
1924
1925 # if revlog format changes, client will have to check version
1925 # if revlog format changes, client will have to check version
1926 # and format flags on "stream" capability, and use
1926 # and format flags on "stream" capability, and use
1927 # uncompressed only if compatible.
1927 # uncompressed only if compatible.
1928
1928
1929 if stream and not heads and remote.capable('stream'):
1929 if stream and not heads and remote.capable('stream'):
1930 return self.stream_in(remote)
1930 return self.stream_in(remote)
1931 return self.pull(remote, heads)
1931 return self.pull(remote, heads)
1932
1932
1933 # used to avoid circular references so destructors work
1933 # used to avoid circular references so destructors work
1934 def aftertrans(files):
1934 def aftertrans(files):
1935 renamefiles = [tuple(t) for t in files]
1935 renamefiles = [tuple(t) for t in files]
1936 def a():
1936 def a():
1937 for src, dest in renamefiles:
1937 for src, dest in renamefiles:
1938 util.rename(src, dest)
1938 util.rename(src, dest)
1939 return a
1939 return a
1940
1940
1941 def instance(ui, path, create):
1941 def instance(ui, path, create):
1942 return localrepository(ui, util.drop_scheme('file', path), create)
1942 return localrepository(ui, util.drop_scheme('file', path), create)
1943
1943
1944 def islocal(path):
1944 def islocal(path):
1945 return True
1945 return True
@@ -1,81 +1,81 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Test the running system for features availability. Exit with zero
2 """Test the running system for features availability. Exit with zero
3 if all features are there, non-zero otherwise.
3 if all features are there, non-zero otherwise.
4 """
4 """
5 import optparse
5 import optparse
6 import os
6 import os
7 import sys
7 import sys
8 import tempfile
8 import tempfile
9
9
10 def has_symlink():
10 def has_symlink():
11 return hasattr(os, "symlink")
11 return hasattr(os, "symlink")
12
12
13 def has_fifo():
13 def has_fifo():
14 return hasattr(os, "mkfifo")
14 return hasattr(os, "mkfifo")
15
15
16 def has_executablebit():
16 def has_executablebit():
17 fd, path = tempfile.mkstemp()
17 fd, path = tempfile.mkstemp()
18 os.close(fd)
18 os.close(fd)
19 try:
19 try:
20 s = os.lstat(path).st_mode
20 s = os.lstat(path).st_mode
21 os.chmod(path, s | 0100)
21 os.chmod(path, s | 0100)
22 return (os.lstat(path).st_mode & 0100 != 0)
22 return (os.lstat(path).st_mode & 0100 != 0)
23 finally:
23 finally:
24 os.remove(path)
24 os.remove(path)
25
25
26 def has_eol_in_paths():
26 def has_eol_in_paths():
27 try:
27 try:
28 fd, path = tempfile.mkstemp(suffix='\n\r')
28 fd, path = tempfile.mkstemp(suffix='\n\r')
29 os.close(fd)
29 os.close(fd)
30 os.remove(path)
30 os.remove(path)
31 return True
31 return True
32 except:
32 except:
33 return False
33 return False
34
34
35 checks = {
35 checks = {
36 "symlink": (has_symlink, "symbolic links"),
36 "symlink": (has_symlink, "symbolic links"),
37 "fifo": (has_fifo, "named pipes"),
37 "fifo": (has_fifo, "named pipes"),
38 "execbit": (has_executablebit, "executable bit"),
38 "execbit": (has_executablebit, "executable bit"),
39 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
39 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
40 }
40 }
41
41
42 def list_features():
42 def list_features():
43 for name, feature in checks.iteritems():
43 for name, feature in checks.iteritems():
44 desc = feature[1]
44 desc = feature[1]
45 print name + ':', desc
45 print name + ':', desc
46
46
47 parser = optparse.OptionParser("%prog [options] [features]")
47 parser = optparse.OptionParser("%prog [options] [features]")
48 parser.add_option("--list-features", action="store_true",
48 parser.add_option("--list-features", action="store_true",
49 help="list available features")
49 help="list available features")
50 parser.add_option("-q", "--quiet", action="store_true",
50 parser.add_option("-q", "--quiet", action="store_true",
51 help="check features silently")
51 help="check features silently")
52
52
53 if __name__ == '__main__':
53 if __name__ == '__main__':
54 options, args = parser.parse_args()
54 options, args = parser.parse_args()
55 if options.list_features:
55 if options.list_features:
56 list_features()
56 list_features()
57 sys.exit(0)
57 sys.exit(0)
58
58
59 quiet = options.quiet
59 quiet = options.quiet
60
60
61 failures = 0
61 failures = 0
62
62
63 def error(msg):
63 def error(msg):
64 global failures
64 global failures
65 if not quiet:
65 if not quiet:
66 sys.stderr.write(msg + '\n')
66 sys.stderr.write(msg + '\n')
67 failures += 1
67 failures += 1
68
68
69 for feature in args:
69 for feature in args:
70 if feature not in checks:
70 if feature not in checks:
71 error('hghave: unknown feature: ' + feature)
71 error('hghave: unknown feature: ' + feature)
72 continue
72 continue
73
73
74 check, desc = checks[feature]
74 check, desc = checks[feature]
75 if not check():
75 if not check():
76 error('hghave: missing feature: ' + desc)
76 error('hghave: missing feature: ' + desc)
77
77
78 if failures != 0:
78 if failures != 0:
79 sys.exit(1)
79 sys.exit(1)
80
80
81
81
@@ -1,463 +1,463 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # run-tests.py - Run a set of tests on Mercurial
3 # run-tests.py - Run a set of tests on Mercurial
4 #
4 #
5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
8 # of the GNU General Public License, incorporated herein by reference.
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import optparse
12 import optparse
13 import os
13 import os
14 import popen2
14 import popen2
15 import re
15 import re
16 import shutil
16 import shutil
17 import signal
17 import signal
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 import time
20 import time
21
21
22 # hghave reserved exit code to skip test
22 # hghave reserved exit code to skip test
23 SKIPPED_STATUS = 80
23 SKIPPED_STATUS = 80
24
24
25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
26
26
27 parser = optparse.OptionParser("%prog [options] [tests]")
27 parser = optparse.OptionParser("%prog [options] [tests]")
28 parser.add_option("-v", "--verbose", action="store_true",
28 parser.add_option("-v", "--verbose", action="store_true",
29 help="output verbose messages")
29 help="output verbose messages")
30 parser.add_option("-t", "--timeout", type="int",
30 parser.add_option("-t", "--timeout", type="int",
31 help="kill errant tests after TIMEOUT seconds")
31 help="kill errant tests after TIMEOUT seconds")
32 parser.add_option("-c", "--cover", action="store_true",
32 parser.add_option("-c", "--cover", action="store_true",
33 help="print a test coverage report")
33 help="print a test coverage report")
34 parser.add_option("-s", "--cover_stdlib", action="store_true",
34 parser.add_option("-s", "--cover_stdlib", action="store_true",
35 help="print a test coverage report inc. standard libraries")
35 help="print a test coverage report inc. standard libraries")
36 parser.add_option("-C", "--annotate", action="store_true",
36 parser.add_option("-C", "--annotate", action="store_true",
37 help="output files annotated with coverage")
37 help="output files annotated with coverage")
38 parser.add_option("-r", "--retest", action="store_true",
38 parser.add_option("-r", "--retest", action="store_true",
39 help="retest failed tests")
39 help="retest failed tests")
40 parser.add_option("-f", "--first", action="store_true",
40 parser.add_option("-f", "--first", action="store_true",
41 help="exit on the first test failure")
41 help="exit on the first test failure")
42 parser.add_option("-R", "--restart", action="store_true",
42 parser.add_option("-R", "--restart", action="store_true",
43 help="restart at last error")
43 help="restart at last error")
44 parser.add_option("-i", "--interactive", action="store_true",
44 parser.add_option("-i", "--interactive", action="store_true",
45 help="prompt to accept changed output")
45 help="prompt to accept changed output")
46
46
47 parser.set_defaults(timeout=180)
47 parser.set_defaults(timeout=180)
48 (options, args) = parser.parse_args()
48 (options, args) = parser.parse_args()
49 verbose = options.verbose
49 verbose = options.verbose
50 coverage = options.cover or options.cover_stdlib or options.annotate
50 coverage = options.cover or options.cover_stdlib or options.annotate
51 python = sys.executable
51 python = sys.executable
52
52
53 def vlog(*msg):
53 def vlog(*msg):
54 if verbose:
54 if verbose:
55 for m in msg:
55 for m in msg:
56 print m,
56 print m,
57 print
57 print
58
58
59 def splitnewlines(text):
59 def splitnewlines(text):
60 '''like str.splitlines, but only split on newlines.
60 '''like str.splitlines, but only split on newlines.
61 keep line endings.'''
61 keep line endings.'''
62 i = 0
62 i = 0
63 lines = []
63 lines = []
64 while True:
64 while True:
65 n = text.find('\n', i)
65 n = text.find('\n', i)
66 if n == -1:
66 if n == -1:
67 last = text[i:]
67 last = text[i:]
68 if last:
68 if last:
69 lines.append(last)
69 lines.append(last)
70 return lines
70 return lines
71 lines.append(text[i:n+1])
71 lines.append(text[i:n+1])
72 i = n + 1
72 i = n + 1
73
73
74 def extract_missing_features(lines):
74 def extract_missing_features(lines):
75 '''Extract missing/unknown features log lines as a list'''
75 '''Extract missing/unknown features log lines as a list'''
76 missing = []
76 missing = []
77 for line in lines:
77 for line in lines:
78 if not line.startswith('hghave: '):
78 if not line.startswith('hghave: '):
79 continue
79 continue
80 line = line.splitlines()[0]
80 line = line.splitlines()[0]
81 missing.append(line[8:])
81 missing.append(line[8:])
82
82
83 return missing
83 return missing
84
84
85 def show_diff(expected, output):
85 def show_diff(expected, output):
86 for line in difflib.unified_diff(expected, output,
86 for line in difflib.unified_diff(expected, output,
87 "Expected output", "Test output"):
87 "Expected output", "Test output"):
88 sys.stdout.write(line)
88 sys.stdout.write(line)
89
89
90 def find_program(program):
90 def find_program(program):
91 """Search PATH for a executable program"""
91 """Search PATH for a executable program"""
92 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
92 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
93 name = os.path.join(p, program)
93 name = os.path.join(p, program)
94 if os.access(name, os.X_OK):
94 if os.access(name, os.X_OK):
95 return name
95 return name
96 return None
96 return None
97
97
98 def check_required_tools():
98 def check_required_tools():
99 # Before we go any further, check for pre-requisite tools
99 # Before we go any further, check for pre-requisite tools
100 # stuff from coreutils (cat, rm, etc) are not tested
100 # stuff from coreutils (cat, rm, etc) are not tested
101 for p in required_tools:
101 for p in required_tools:
102 if os.name == 'nt':
102 if os.name == 'nt':
103 p += '.exe'
103 p += '.exe'
104 found = find_program(p)
104 found = find_program(p)
105 if found:
105 if found:
106 vlog("# Found prerequisite", p, "at", found)
106 vlog("# Found prerequisite", p, "at", found)
107 else:
107 else:
108 print "WARNING: Did not find prerequisite tool: "+p
108 print "WARNING: Did not find prerequisite tool: "+p
109
109
110 def cleanup_exit():
110 def cleanup_exit():
111 if verbose:
111 if verbose:
112 print "# Cleaning up HGTMP", HGTMP
112 print "# Cleaning up HGTMP", HGTMP
113 shutil.rmtree(HGTMP, True)
113 shutil.rmtree(HGTMP, True)
114
114
115 def use_correct_python():
115 def use_correct_python():
116 # some tests run python interpreter. they must use same
116 # some tests run python interpreter. they must use same
117 # interpreter we use or bad things will happen.
117 # interpreter we use or bad things will happen.
118 exedir, exename = os.path.split(sys.executable)
118 exedir, exename = os.path.split(sys.executable)
119 if exename == 'python':
119 if exename == 'python':
120 path = find_program('python')
120 path = find_program('python')
121 if os.path.dirname(path) == exedir:
121 if os.path.dirname(path) == exedir:
122 return
122 return
123 vlog('# Making python executable in test path use correct Python')
123 vlog('# Making python executable in test path use correct Python')
124 my_python = os.path.join(BINDIR, 'python')
124 my_python = os.path.join(BINDIR, 'python')
125 try:
125 try:
126 os.symlink(sys.executable, my_python)
126 os.symlink(sys.executable, my_python)
127 except AttributeError:
127 except AttributeError:
128 # windows fallback
128 # windows fallback
129 shutil.copyfile(sys.executable, my_python)
129 shutil.copyfile(sys.executable, my_python)
130 shutil.copymode(sys.executable, my_python)
130 shutil.copymode(sys.executable, my_python)
131
131
132 def install_hg():
132 def install_hg():
133 global python
133 global python
134 vlog("# Performing temporary installation of HG")
134 vlog("# Performing temporary installation of HG")
135 installerrs = os.path.join("tests", "install.err")
135 installerrs = os.path.join("tests", "install.err")
136
136
137 os.chdir("..") # Get back to hg root
137 os.chdir("..") # Get back to hg root
138 cmd = ('%s setup.py clean --all'
138 cmd = ('%s setup.py clean --all'
139 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
139 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
140 % (sys.executable, INST, PYTHONDIR, installerrs))
140 % (sys.executable, INST, PYTHONDIR, installerrs))
141 vlog("# Running", cmd)
141 vlog("# Running", cmd)
142 if os.system(cmd) == 0:
142 if os.system(cmd) == 0:
143 if not verbose:
143 if not verbose:
144 os.remove(installerrs)
144 os.remove(installerrs)
145 else:
145 else:
146 f = open(installerrs)
146 f = open(installerrs)
147 for line in f:
147 for line in f:
148 print line,
148 print line,
149 f.close()
149 f.close()
150 sys.exit(1)
150 sys.exit(1)
151 os.chdir(TESTDIR)
151 os.chdir(TESTDIR)
152
152
153 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
153 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
154 os.environ["PYTHONPATH"] = PYTHONDIR
154 os.environ["PYTHONPATH"] = PYTHONDIR
155
155
156 use_correct_python()
156 use_correct_python()
157
157
158 if coverage:
158 if coverage:
159 vlog("# Installing coverage wrapper")
159 vlog("# Installing coverage wrapper")
160 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
160 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
161 if os.path.exists(COVERAGE_FILE):
161 if os.path.exists(COVERAGE_FILE):
162 os.unlink(COVERAGE_FILE)
162 os.unlink(COVERAGE_FILE)
163 # Create a wrapper script to invoke hg via coverage.py
163 # Create a wrapper script to invoke hg via coverage.py
164 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
164 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
165 f = open(os.path.join(BINDIR, 'hg'), 'w')
165 f = open(os.path.join(BINDIR, 'hg'), 'w')
166 f.write('#!' + sys.executable + '\n')
166 f.write('#!' + sys.executable + '\n')
167 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
167 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
168 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
168 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
169 (os.path.join(TESTDIR, 'coverage.py'),
169 (os.path.join(TESTDIR, 'coverage.py'),
170 os.path.join(BINDIR, '_hg.py')))
170 os.path.join(BINDIR, '_hg.py')))
171 f.close()
171 f.close()
172 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
172 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
173 python = '"%s" "%s" -x' % (sys.executable,
173 python = '"%s" "%s" -x' % (sys.executable,
174 os.path.join(TESTDIR,'coverage.py'))
174 os.path.join(TESTDIR,'coverage.py'))
175
175
176 def output_coverage():
176 def output_coverage():
177 vlog("# Producing coverage report")
177 vlog("# Producing coverage report")
178 omit = [BINDIR, TESTDIR, PYTHONDIR]
178 omit = [BINDIR, TESTDIR, PYTHONDIR]
179 if not options.cover_stdlib:
179 if not options.cover_stdlib:
180 # Exclude as system paths (ignoring empty strings seen on win)
180 # Exclude as system paths (ignoring empty strings seen on win)
181 omit += [x for x in sys.path if x != '']
181 omit += [x for x in sys.path if x != '']
182 omit = ','.join(omit)
182 omit = ','.join(omit)
183 os.chdir(PYTHONDIR)
183 os.chdir(PYTHONDIR)
184 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
184 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
185 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
185 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
186 vlog("# Running: "+cmd)
186 vlog("# Running: "+cmd)
187 os.system(cmd)
187 os.system(cmd)
188 if options.annotate:
188 if options.annotate:
189 adir = os.path.join(TESTDIR, 'annotated')
189 adir = os.path.join(TESTDIR, 'annotated')
190 if not os.path.isdir(adir):
190 if not os.path.isdir(adir):
191 os.mkdir(adir)
191 os.mkdir(adir)
192 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
192 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
193 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
193 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
194 adir, omit)
194 adir, omit)
195 vlog("# Running: "+cmd)
195 vlog("# Running: "+cmd)
196 os.system(cmd)
196 os.system(cmd)
197
197
198 class Timeout(Exception):
198 class Timeout(Exception):
199 pass
199 pass
200
200
201 def alarmed(signum, frame):
201 def alarmed(signum, frame):
202 raise Timeout
202 raise Timeout
203
203
204 def run(cmd):
204 def run(cmd):
205 """Run command in a sub-process, capturing the output (stdout and stderr).
205 """Run command in a sub-process, capturing the output (stdout and stderr).
206 Return the exist code, and output."""
206 Return the exist code, and output."""
207 # TODO: Use subprocess.Popen if we're running on Python 2.4
207 # TODO: Use subprocess.Popen if we're running on Python 2.4
208 if os.name == 'nt':
208 if os.name == 'nt':
209 tochild, fromchild = os.popen4(cmd)
209 tochild, fromchild = os.popen4(cmd)
210 tochild.close()
210 tochild.close()
211 output = fromchild.read()
211 output = fromchild.read()
212 ret = fromchild.close()
212 ret = fromchild.close()
213 if ret == None:
213 if ret == None:
214 ret = 0
214 ret = 0
215 else:
215 else:
216 proc = popen2.Popen4(cmd)
216 proc = popen2.Popen4(cmd)
217 try:
217 try:
218 output = ''
218 output = ''
219 proc.tochild.close()
219 proc.tochild.close()
220 output = proc.fromchild.read()
220 output = proc.fromchild.read()
221 ret = proc.wait()
221 ret = proc.wait()
222 if os.WIFEXITED(ret):
222 if os.WIFEXITED(ret):
223 ret = os.WEXITSTATUS(ret)
223 ret = os.WEXITSTATUS(ret)
224 except Timeout:
224 except Timeout:
225 vlog('# Process %d timed out - killing it' % proc.pid)
225 vlog('# Process %d timed out - killing it' % proc.pid)
226 os.kill(proc.pid, signal.SIGTERM)
226 os.kill(proc.pid, signal.SIGTERM)
227 ret = proc.wait()
227 ret = proc.wait()
228 if ret == 0:
228 if ret == 0:
229 ret = signal.SIGTERM << 8
229 ret = signal.SIGTERM << 8
230 output += ("\n### Abort: timeout after %d seconds.\n"
230 output += ("\n### Abort: timeout after %d seconds.\n"
231 % options.timeout)
231 % options.timeout)
232 return ret, splitnewlines(output)
232 return ret, splitnewlines(output)
233
233
234 def run_one(test):
234 def run_one(test):
235 '''tristate output:
235 '''tristate output:
236 None -> skipped
236 None -> skipped
237 True -> passed
237 True -> passed
238 False -> failed'''
238 False -> failed'''
239
239
240 vlog("# Test", test)
240 vlog("# Test", test)
241 if not verbose:
241 if not verbose:
242 sys.stdout.write('.')
242 sys.stdout.write('.')
243 sys.stdout.flush()
243 sys.stdout.flush()
244
244
245 # create a fresh hgrc
245 # create a fresh hgrc
246 hgrc = file(HGRCPATH, 'w+')
246 hgrc = file(HGRCPATH, 'w+')
247 hgrc.write('[ui]\n')
247 hgrc.write('[ui]\n')
248 hgrc.write('slash = True\n')
248 hgrc.write('slash = True\n')
249 hgrc.close()
249 hgrc.close()
250
250
251 err = os.path.join(TESTDIR, test+".err")
251 err = os.path.join(TESTDIR, test+".err")
252 ref = os.path.join(TESTDIR, test+".out")
252 ref = os.path.join(TESTDIR, test+".out")
253 testpath = os.path.join(TESTDIR, test)
253 testpath = os.path.join(TESTDIR, test)
254
254
255 if os.path.exists(err):
255 if os.path.exists(err):
256 os.remove(err) # Remove any previous output files
256 os.remove(err) # Remove any previous output files
257
257
258 # Make a tmp subdirectory to work in
258 # Make a tmp subdirectory to work in
259 tmpd = os.path.join(HGTMP, test)
259 tmpd = os.path.join(HGTMP, test)
260 os.mkdir(tmpd)
260 os.mkdir(tmpd)
261 os.chdir(tmpd)
261 os.chdir(tmpd)
262
262
263 try:
263 try:
264 tf = open(testpath)
264 tf = open(testpath)
265 firstline = tf.readline().rstrip()
265 firstline = tf.readline().rstrip()
266 tf.close()
266 tf.close()
267 except:
267 except:
268 firstline = ''
268 firstline = ''
269 lctest = test.lower()
269 lctest = test.lower()
270
270
271 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
271 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
272 cmd = '%s "%s"' % (python, testpath)
272 cmd = '%s "%s"' % (python, testpath)
273 elif lctest.endswith('.bat'):
273 elif lctest.endswith('.bat'):
274 # do not run batch scripts on non-windows
274 # do not run batch scripts on non-windows
275 if os.name != 'nt':
275 if os.name != 'nt':
276 print '\nSkipping %s: batch script' % test
276 print '\nSkipping %s: batch script' % test
277 return None
277 return None
278 # To reliably get the error code from batch files on WinXP,
278 # To reliably get the error code from batch files on WinXP,
279 # the "cmd /c call" prefix is needed. Grrr
279 # the "cmd /c call" prefix is needed. Grrr
280 cmd = 'cmd /c call "%s"' % testpath
280 cmd = 'cmd /c call "%s"' % testpath
281 else:
281 else:
282 # do not run shell scripts on windows
282 # do not run shell scripts on windows
283 if os.name == 'nt':
283 if os.name == 'nt':
284 print '\nSkipping %s: shell script' % test
284 print '\nSkipping %s: shell script' % test
285 return None
285 return None
286 # do not try to run non-executable programs
286 # do not try to run non-executable programs
287 if not os.access(testpath, os.X_OK):
287 if not os.access(testpath, os.X_OK):
288 print '\nSkipping %s: not executable' % test
288 print '\nSkipping %s: not executable' % test
289 return None
289 return None
290 cmd = '"%s"' % testpath
290 cmd = '"%s"' % testpath
291
291
292 if options.timeout > 0:
292 if options.timeout > 0:
293 signal.alarm(options.timeout)
293 signal.alarm(options.timeout)
294
294
295 vlog("# Running", cmd)
295 vlog("# Running", cmd)
296 ret, out = run(cmd)
296 ret, out = run(cmd)
297 vlog("# Ret was:", ret)
297 vlog("# Ret was:", ret)
298
298
299 if options.timeout > 0:
299 if options.timeout > 0:
300 signal.alarm(0)
300 signal.alarm(0)
301
301
302 skipped = (ret == SKIPPED_STATUS)
302 skipped = (ret == SKIPPED_STATUS)
303 diffret = 0
303 diffret = 0
304 # If reference output file exists, check test output against it
304 # If reference output file exists, check test output against it
305 if os.path.exists(ref):
305 if os.path.exists(ref):
306 f = open(ref, "r")
306 f = open(ref, "r")
307 ref_out = splitnewlines(f.read())
307 ref_out = splitnewlines(f.read())
308 f.close()
308 f.close()
309 else:
309 else:
310 ref_out = []
310 ref_out = []
311 if not skipped and out != ref_out:
311 if not skipped and out != ref_out:
312 diffret = 1
312 diffret = 1
313 print "\nERROR: %s output changed" % (test)
313 print "\nERROR: %s output changed" % (test)
314 show_diff(ref_out, out)
314 show_diff(ref_out, out)
315 if skipped:
315 if skipped:
316 missing = extract_missing_features(out)
316 missing = extract_missing_features(out)
317 if not missing:
317 if not missing:
318 missing = ['irrelevant']
318 missing = ['irrelevant']
319 print '\nSkipping %s: %s' % (test, missing[-1])
319 print '\nSkipping %s: %s' % (test, missing[-1])
320 elif ret:
320 elif ret:
321 print "\nERROR: %s failed with error code %d" % (test, ret)
321 print "\nERROR: %s failed with error code %d" % (test, ret)
322 elif diffret:
322 elif diffret:
323 ret = diffret
323 ret = diffret
324
324
325 if ret != 0 and not skipped:
325 if ret != 0 and not skipped:
326 # Save errors to a file for diagnosis
326 # Save errors to a file for diagnosis
327 f = open(err, "wb")
327 f = open(err, "wb")
328 for line in out:
328 for line in out:
329 f.write(line)
329 f.write(line)
330 f.close()
330 f.close()
331
331
332 # Kill off any leftover daemon processes
332 # Kill off any leftover daemon processes
333 try:
333 try:
334 fp = file(DAEMON_PIDS)
334 fp = file(DAEMON_PIDS)
335 for line in fp:
335 for line in fp:
336 try:
336 try:
337 pid = int(line)
337 pid = int(line)
338 except ValueError:
338 except ValueError:
339 continue
339 continue
340 try:
340 try:
341 os.kill(pid, 0)
341 os.kill(pid, 0)
342 vlog('# Killing daemon process %d' % pid)
342 vlog('# Killing daemon process %d' % pid)
343 os.kill(pid, signal.SIGTERM)
343 os.kill(pid, signal.SIGTERM)
344 time.sleep(0.25)
344 time.sleep(0.25)
345 os.kill(pid, 0)
345 os.kill(pid, 0)
346 vlog('# Daemon process %d is stuck - really killing it' % pid)
346 vlog('# Daemon process %d is stuck - really killing it' % pid)
347 os.kill(pid, signal.SIGKILL)
347 os.kill(pid, signal.SIGKILL)
348 except OSError, err:
348 except OSError, err:
349 if err.errno != errno.ESRCH:
349 if err.errno != errno.ESRCH:
350 raise
350 raise
351 fp.close()
351 fp.close()
352 os.unlink(DAEMON_PIDS)
352 os.unlink(DAEMON_PIDS)
353 except IOError:
353 except IOError:
354 pass
354 pass
355
355
356 os.chdir(TESTDIR)
356 os.chdir(TESTDIR)
357 shutil.rmtree(tmpd, True)
357 shutil.rmtree(tmpd, True)
358 if skipped:
358 if skipped:
359 return None
359 return None
360 return ret == 0
360 return ret == 0
361
361
362
362
363 os.umask(022)
363 os.umask(022)
364
364
365 check_required_tools()
365 check_required_tools()
366
366
367 # Reset some environment variables to well-known values so that
367 # Reset some environment variables to well-known values so that
368 # the tests produce repeatable output.
368 # the tests produce repeatable output.
369 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
369 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
370 os.environ['TZ'] = 'GMT'
370 os.environ['TZ'] = 'GMT'
371
371
372 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
372 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
373 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
373 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
374 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
374 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
375 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
375 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
376
376
377 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
377 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
378 os.environ["HGMERGE"] = ('python "%s" -L my -L other'
378 os.environ["HGMERGE"] = ('python "%s" -L my -L other'
379 % os.path.join(TESTDIR, os.path.pardir, 'contrib',
379 % os.path.join(TESTDIR, os.path.pardir, 'contrib',
380 'simplemerge'))
380 'simplemerge'))
381 os.environ["HGUSER"] = "test"
381 os.environ["HGUSER"] = "test"
382 os.environ["HGENCODING"] = "ascii"
382 os.environ["HGENCODING"] = "ascii"
383 os.environ["HGENCODINGMODE"] = "strict"
383 os.environ["HGENCODINGMODE"] = "strict"
384
384
385 vlog("# Using TESTDIR", TESTDIR)
385 vlog("# Using TESTDIR", TESTDIR)
386 vlog("# Using HGTMP", HGTMP)
386 vlog("# Using HGTMP", HGTMP)
387
387
388 INST = os.path.join(HGTMP, "install")
388 INST = os.path.join(HGTMP, "install")
389 BINDIR = os.path.join(INST, "bin")
389 BINDIR = os.path.join(INST, "bin")
390 PYTHONDIR = os.path.join(INST, "lib", "python")
390 PYTHONDIR = os.path.join(INST, "lib", "python")
391 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
391 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
392
392
393 try:
393 try:
394 try:
394 try:
395 install_hg()
395 install_hg()
396
396
397 if options.timeout > 0:
397 if options.timeout > 0:
398 try:
398 try:
399 signal.signal(signal.SIGALRM, alarmed)
399 signal.signal(signal.SIGALRM, alarmed)
400 vlog('# Running tests with %d-second timeout' %
400 vlog('# Running tests with %d-second timeout' %
401 options.timeout)
401 options.timeout)
402 except AttributeError:
402 except AttributeError:
403 print 'WARNING: cannot run tests with timeouts'
403 print 'WARNING: cannot run tests with timeouts'
404 options.timeout = 0
404 options.timeout = 0
405
405
406 tested = 0
406 tested = 0
407 failed = 0
407 failed = 0
408 skipped = 0
408 skipped = 0
409
409
410 if len(args) == 0:
410 if len(args) == 0:
411 args = os.listdir(".")
411 args = os.listdir(".")
412 args.sort()
412 args.sort()
413
413
414
414
415 tests = []
415 tests = []
416 for test in args:
416 for test in args:
417 if (test.startswith("test-") and '~' not in test and
417 if (test.startswith("test-") and '~' not in test and
418 ('.' not in test or test.endswith('.py') or
418 ('.' not in test or test.endswith('.py') or
419 test.endswith('.bat'))):
419 test.endswith('.bat'))):
420 tests.append(test)
420 tests.append(test)
421
421
422 if options.restart:
422 if options.restart:
423 orig = list(tests)
423 orig = list(tests)
424 while tests:
424 while tests:
425 if os.path.exists(tests[0] + ".err"):
425 if os.path.exists(tests[0] + ".err"):
426 break
426 break
427 tests.pop(0)
427 tests.pop(0)
428 if not tests:
428 if not tests:
429 print "running all tests"
429 print "running all tests"
430 tests = orig
430 tests = orig
431
431
432 for test in tests:
432 for test in tests:
433 if options.retest and not os.path.exists(test + ".err"):
433 if options.retest and not os.path.exists(test + ".err"):
434 skipped += 1
434 skipped += 1
435 continue
435 continue
436 ret = run_one(test)
436 ret = run_one(test)
437 if ret is None:
437 if ret is None:
438 skipped += 1
438 skipped += 1
439 elif not ret:
439 elif not ret:
440 if options.interactive:
440 if options.interactive:
441 print "Accept this change? [n] ",
441 print "Accept this change? [n] ",
442 answer = sys.stdin.readline().strip()
442 answer = sys.stdin.readline().strip()
443 if answer.lower() in "y yes".split():
443 if answer.lower() in "y yes".split():
444 os.rename(test + ".err", test + ".out")
444 os.rename(test + ".err", test + ".out")
445 tested += 1
445 tested += 1
446 continue
446 continue
447 failed += 1
447 failed += 1
448 if options.first:
448 if options.first:
449 break
449 break
450 tested += 1
450 tested += 1
451
451
452 print "\n# Ran %d tests, %d skipped, %d failed." % (tested, skipped,
452 print "\n# Ran %d tests, %d skipped, %d failed." % (tested, skipped,
453 failed)
453 failed)
454 if coverage:
454 if coverage:
455 output_coverage()
455 output_coverage()
456 except KeyboardInterrupt:
456 except KeyboardInterrupt:
457 failed = True
457 failed = True
458 print "\ninterrupted!"
458 print "\ninterrupted!"
459 finally:
459 finally:
460 cleanup_exit()
460 cleanup_exit()
461
461
462 if failed:
462 if failed:
463 sys.exit(1)
463 sys.exit(1)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now