1 #!/usr/bin/env ruby |
1 #!/usr/bin/env ruby |
2 exit if ENV['CTAGS_SKIP'] |
2 exit if ENV['CTAGS_SKIP'] |
3 ARGV.each { |o| exec('perldoc', $0) if o.match('help') } |
3 ARGV.each { |o| exec('perldoc', $0) if o.match('help') } |
4 |
4 |
5 ctags = 'ctags' |
5 ctags_bin = ENV['CTAGS_BIN'] || 'ctags' |
6 |
6 |
7 dir = `git rev-parse --show-toplevel`.chomp |
7 if (dir = `git rev-parse --show-toplevel 2>/dev/null`.chomp) != '' |
|
8 list_cmd = 'git ls-files' |
|
9 conf = "#{dir}/.git/ctags.conf" |
|
10 elsif (dir = `hg root 2>/dev/null`.chomp) != '' |
|
11 list_cmd = 'hg stat -Aqn' |
|
12 conf = "#{dir}/.hg/ctags.conf" |
|
13 else |
|
14 abort 'not an hg or git repository' |
|
15 end |
8 |
16 |
9 conf = "#{dir}/.git/ctags.conf" |
|
10 target = "#{dir}/.tags" |
17 target = "#{dir}/.tags" |
11 tmp = "#{dir}/.tags.#{$$}~" |
18 tmp = "#{dir}/.tags.#{$$}~" |
12 |
19 |
13 opts = File.exists?(conf) ? "--options=#{conf}" : '' |
20 opts = File.exists?(conf) ? "--options=#{conf}" : '' |
14 |
21 |
15 sleep 5 if ENV['CTAGS_HOOK'] |
22 if ENV['CTAGS_HOOK'] |
|
23 fork and exit |
|
24 sleep 5 |
|
25 end |
16 |
26 |
17 open(target, File::RDONLY|File::CREAT, 0644) do |f| |
27 open(target, File::RDONLY|File::CREAT, 0644) do |f| |
18 if ENV['CTAGS_HOOK'] |
28 if ENV['CTAGS_HOOK'] |
19 exit unless f.flock(File::LOCK_EX|File::LOCK_NB) |
29 exit unless f.flock(File::LOCK_EX|File::LOCK_NB) |
20 exit unless (Time.now - f.mtime) > 60 |
30 exit unless (Time.now - f.mtime) > 60 |
21 end |
31 end |
22 |
32 |
23 system(<<-CMD) or exit $?.exitstatus |
33 system(<<-CMD) or exit $?.exitstatus |
24 git ls-files \ |
34 #{list_cmd} \ |
25 | #{ctags} --tag-relative -L - -f"#{tmp}" #{opts} \ |
35 | #{ctags_bin} --tag-relative -L - -f"#{tmp}" #{opts} \ |
26 && mv #{tmp} #{target} |
36 && mv #{tmp} #{target} |
27 CMD |
37 CMD |
28 end |
38 end |
29 |
39 |
30 exit 0 |
40 exit 0 |
36 |
46 |
37 =head1 SYNOPSIS |
47 =head1 SYNOPSIS |
38 |
48 |
39 git ctags |
49 git ctags |
40 |
50 |
41 echo 'CTAGS_HOOK=1 git ctags &' >> .git/hooks/post-checkout |
51 echo 'CTAGS_HOOK=1 git ctags' >> .git/hooks/post-checkout |
42 |
52 |
43 git checkout some/branch |
53 git checkout some/branch |
44 CTAGS_SKIP=1 git checkout some/branch |
54 CTAGS_SKIP=1 git checkout some/branch |
45 |
55 |
46 =head1 DESCRIPTION |
56 =head1 DESCRIPTION |
47 |
57 |
48 Create a .tags file (target) using git's list of tracked files. If |
58 Create a .tags file (target) using git's list of tracked files. If |
49 C<.git/ctags.conf> exists in the repo it is passed to the ctags invocation. |
59 C<.git/ctags.conf> exists in the repo it is passed to the ctags invocation. |
50 |
60 |
51 When run w/o env, immediately run ctags and replace tags file. |
61 When run with C<CTAGS_HOOK> set, additional behavior is enabled to avoid excess |
|
62 re-runs during multiple VCS operations, and the work is moved to background as |
|
63 well. |
52 |
64 |
53 When run with C<CTAGS_HOOK>, it's assumed to be a BG process and we want to |
65 =head2 Mercurial |
54 wait for git to be done applying changes no matter how the hook is triggered. |
66 |
55 Wait five seconds, then bail if we can't get a lock on the tags file or if it's |
67 Mercurial repositories are supported too, with git taking priority when finding |
56 been updated within the last minute. Only then, run ctags and replace the tags |
68 a repo root. To add a hook, in the repository C<.hg/hgrc>, add: |
57 file. |
69 |
|
70 [hooks] |
|
71 update = CTAGS_HOOK=1 git ctags |
|
72 |
|
73 In a Mercurial repo, additional options can be put in C<.hg/ctags.conf>. |
|
74 |
|
75 =head1 ENVIRONMENT |
|
76 |
|
77 =head2 CTAGS_SKIP |
|
78 |
|
79 If true, exit immediately. |
|
80 |
|
81 =head2 CTAGS_HOOK |
|
82 |
|
83 If true, run as a VCS hook. This causes git-ctags to fork and exit, wait five |
|
84 seconds, then try to lock and update the tags file if it hasn't been updated in |
|
85 the last minute. |
|
86 |
|
87 =head2 CTAGS_BIN |
|
88 |
|
89 If set, use this to invoke ctags rather than assuming C<ctags> can be found in |
|
90 path. |
58 |
91 |
59 =cut |
92 =cut |