Showing 7 changed files with 192 additions and 185 deletions
+2 -1
.gitignore
... ...
@@ -27,4 +27,5 @@ user/templates/*
27 27
 !user/templates/.gitignore
28 28
 db/*
29 29
 !db/.gitignore
30
-extlib
30
+extlib/*
31
+rep/*
+12 -19
lib/Gitprep.pm
... ...
@@ -10,6 +10,7 @@ use Validator::Custom;
10 10
 use Encode qw/encode decode/;
11 11
 use Mojo::JSON;
12 12
 use Gitprep::API;
13
+use Carp 'croak';
13 14
 
14 15
 has 'git';
15 16
 has 'dbi';
... ...
@@ -19,19 +20,12 @@ sub startup {
19 20
   my $self = shift;
20 21
   
21 22
   # Config
22
-  my $conf_file = $ENV{GITPREP_CONFIG_FILE}
23
-    || $self->home->rel_file('gitprep.conf');
24
-  $self->plugin('JSONConfigLoose', {file => $conf_file}) if -f $conf_file;
23
+  #my $conf_file = $ENV{GITPREP_CONFIG_FILE}
24
+  #  || $self->home->rel_file('gitprep.conf');
25
+  #$self->plugin('JSONConfigLoose', {file => $conf_file}) if -f $conf_file;
25 26
   my $conf = $self->config;
26
-  $conf->{search_dirs} ||= ['/git/pub', '/home'];
27
-  $conf->{search_max_depth} ||= 10;
28
-  $conf->{logo_link} ||= "https://github.com/yuki-kimoto/gitprep";
29
-  $conf->{hypnotoad} ||= {listen => ["http://*:10010"]};
30
-  $conf->{prevent_xss} ||= 0;
31
-  $conf->{encoding} ||= 'UTF-8';
32
-  $conf->{text_exts} ||= ['txt'];
33
-  $conf->{root} ||= '/gitprep';
34
-  $conf->{ssh_port} ||= '';
27
+  $conf->{root} = $self->home->rel_file('rep');
28
+  $conf->{hypnotoad} ||= {listen => ["http://*:10020"]};
35 29
   
36 30
   # Added public directory
37 31
   push @{$self->static->paths}, $conf->{root};
... ...
@@ -42,10 +36,12 @@ sub startup {
42 36
   die qq/Can't detect git command. set "git_bin" in gitprep.conf/
43 37
     unless $git_bin;
44 38
   $git->bin($git_bin);
45
-  $git->search_dirs($conf->{search_dirs});
46
-  $git->search_max_depth($conf->{search_max_depth});
47
-  $git->encoding($conf->{encoding});
48
-  $git->text_exts($conf->{text_exts});
39
+  my $rep_home = $self->home->rel_file('rep');
40
+  $git->rep_home($rep_home);
41
+  unless (-d $rep_home) {
42
+    mkdir $rep_home
43
+      or croak "Can't create directory $rep_home: $!";
44
+  }
49 45
   $self->git($git);
50 46
 
51 47
   # Reverse proxy support
... ...
@@ -126,9 +122,6 @@ sub startup {
126 122
     $r->get('/compare/(#rev1)...(#rev2)')->to('#compare');
127 123
   }
128 124
   
129
-  # File cache
130
-  $git->search_projects;
131
-  
132 125
   # DBI
133 126
   my $db_file = $self->home->rel_file('db/gitprep.db');
134 127
   my $dbi = DBIx::Custom->connect(
+57 -77
lib/Gitprep/Git.pm
... ...
@@ -5,6 +5,12 @@ use Carp 'croak';
5 5
 use File::Find 'find';
6 6
 use File::Basename qw/basename dirname/;
7 7
 use Fcntl ':mode';
8
+use File::Path 'mkpath';
9
+
10
+# Attributes
11
+has 'bin';
12
+has 'rep_home';
13
+has 'encoding' => 'UTF-8';
8 14
 
9 15
 # Encode
10 16
 use Encode qw/encode decode/;
... ...
@@ -27,13 +33,6 @@ sub dec {
27 33
   return $@ ? $str : $new_str;
28 34
 }
29 35
 
30
-# Attributes
31
-has 'bin';
32
-has 'search_dirs';
33
-has 'search_max_depth';
34
-has 'encoding';
35
-has 'text_exts';
36
-
37 36
 sub authors {
38 37
   my ($self, $rep, $ref, $file) = @_;
39 38
   
... ...
@@ -178,6 +177,47 @@ sub cmd {
178 177
   return ($self->bin, "--git-dir=$project");
179 178
 }
180 179
 
180
+sub create_repository {
181
+  my ($self, $user, $project, $opts) = @_;
182
+  
183
+  # Repository
184
+  my $rep_home = $self->rep_home;
185
+  my $rep = "$rep_home/$user/$project.git";
186
+  mkpath $rep;
187
+    
188
+  # Git init
189
+  my @git_init_cmd = ($self->cmd($rep), 'init', '--bare');
190
+  warn "@git_init_cmd";
191
+  system(@git_init_cmd) == 0
192
+    or croak "Can't execute git init";
193
+  
194
+  # Description
195
+  if (my $description = $opts->{description}) {
196
+    my $file = "$rep/description";
197
+    open my $fh, '>', $file
198
+      or croak "Can't open $file: $!";
199
+    print $fh $description
200
+      or croak "Can't write $file: $!";
201
+    close $fh;
202
+  }
203
+}
204
+
205
+sub branch_exists {
206
+  my ($self, $user, $project) = @_;
207
+  
208
+  my $home = $self->rep_home;
209
+  my $rep = "$home/$user/$project.git";
210
+
211
+  my @cmd = ($self->cmd($rep), 'branch');
212
+  open my $fh, "-|", @cmd
213
+    or croak 'git branch failed';
214
+  
215
+  local $/;
216
+  my $branches = <$fh>;
217
+  
218
+  return $branches eq '' ? 0 : 1;
219
+}
220
+
181 221
 sub branch_commits {
182 222
   my ($self, $rep, $rev1, $rev2) = @_;
183 223
   
... ...
@@ -601,9 +641,10 @@ sub fill_projects {
601 641
 }
602 642
 
603 643
 sub projects {
604
-  my ($self, $dir, %opt) = @_;
644
+  my ($self, $user, $opts) = @_;
605 645
   
606
-  my $filter = $opt{filter};
646
+  my $home = $self->rep_home;
647
+  my $dir = "$home/$user";
607 648
   
608 649
   # Repositories
609 650
   opendir my $dh, $self->enc($dir)
... ...
@@ -611,8 +652,6 @@ sub projects {
611 652
   my @reps;
612 653
   while (my $rep = readdir $dh) {
613 654
     next unless $rep =~ /\.git$/;
614
-    next unless $self->check_head_link("$dir/$rep");
615
-    next if defined $filter && $rep !~ /\Q$filter\E/;
616 655
     my $rep_name = $rep;
617 656
     $rep_name =~ s/\.git$//;
618 657
     push @reps, { name => $rep_name, path => $rep };
... ...
@@ -620,14 +659,14 @@ sub projects {
620 659
 
621 660
   # Fill repositroies information
622 661
   for my $rep (@reps) {
623
-    my (@activity) = $self->last_activity("$dir/$rep->{path}");
624
-    next unless @activity;
625
-    ($rep->{age}, $rep->{age_string}) = @activity;
626
-    if (!defined $rep->{descr}) {
627
-      my $descr = $self->project_description("$dir/$rep->{path}") || '';
628
-      $rep->{descr_long} = $descr;
629
-      $rep->{descr} = $self->_chop_str($descr, 25, 5);
662
+    my @activity = $self->last_activity("$dir/$rep->{path}");
663
+    
664
+    if (@activity) {
665
+      ($rep->{age}, $rep->{age_string}) = @activity;
630 666
     }
667
+
668
+    my $description = $self->project_description("$dir/$rep->{path}") || '';
669
+    $rep->{description} = $self->_chop_str($description, 25, 5);
631 670
   }
632 671
 
633 672
   return \@reps;
... ...
@@ -1074,65 +1113,6 @@ sub search_bin {
1074 1113
   return;
1075 1114
 }
1076 1115
 
1077
-sub search_projects {
1078
-  my ($self, %opt) = @_;
1079
-  my $dirs = $self->search_dirs;
1080
-  my $max_depth = $self->search_max_depth;
1081
-  
1082
-  # Search
1083
-  my @projects;
1084
-  for my $dir (@$dirs) {
1085
-    next unless -d $dir;
1086
-  
1087
-    $dir =~ s/\/$//;
1088
-    my $prefix_length = length($dir);
1089
-    my $prefix_depth = 0;
1090
-    for my $c (split //, $dir) {
1091
-      $prefix_depth++ if $c eq '/';
1092
-    }
1093
-    
1094
-    no warnings 'File::Find';
1095
-    File::Find::find({
1096
-      follow_fast => 1,
1097
-      follow_skip => 2,
1098
-      dangling_symlinks => 0,
1099
-      wanted => sub {
1100
-        my $path = $File::Find::name;
1101
-        my $base_path = $_;
1102
-        
1103
-        return if (m!^[/.]$!);
1104
-        return unless -d $base_path;
1105
-        
1106
-        if ($base_path eq '.git') {
1107
-          $File::Find::prune = 1;
1108
-          return;
1109
-        };
1110
-        
1111
-        my $depth = 0;
1112
-        for my $c (split //, $dir) {
1113
-          $depth++ if $c eq '/';
1114
-        }
1115
-        
1116
-        if ($depth - $prefix_depth > $max_depth) {
1117
-          $File::Find::prune = 1;
1118
-          return;
1119
-        }
1120
-        
1121
-        if (-d $path) {
1122
-          if ($self->check_head_link($path)) {
1123
-            my $home = dirname $path;
1124
-            my $name = basename $path;
1125
-            push @projects, {home => $home, name => $name};
1126
-            $File::Find::prune = 1;
1127
-          }
1128
-        }
1129
-      },
1130
-    }, $dir);
1131
-  }
1132
-  
1133
-  return \@projects;
1134
-}
1135
-
1136 1116
 sub snapshot_name {
1137 1117
   my ($self, $project, $cid) = @_;
1138 1118
 
+15 -18
templates/admin/create.html.ep
... ...
@@ -8,21 +8,18 @@
8 8
     
9 9
     # Validation
10 10
     my $params = $api->params;
11
-    my $repository_name_check = sub {
11
+    my $project_check = sub {
12 12
       my $value = shift;
13 13
       
14 14
       return ($value || '') =~ /^[a-zA-Z0-9_\-]+$/
15 15
     };
16 16
     my $rule = [
17
-      repository_name => [
17
+      project => [
18 18
         ['not_blank' => 'Repository name is empty'],
19
-        [$repository_name_check => 'Invalid repository name']
19
+        [$project_check => 'Invalid repository name']
20 20
       ],
21 21
       description => [
22 22
         'any'
23
-      ],
24
-      readme_contain => {require => 0} => [
25
-        'any'
26 23
       ]
27 24
     ];
28 25
     my $validator = app->validator;
... ...
@@ -31,13 +28,19 @@
31 28
     if ($vresult->is_ok) {
32 29
       my $user = session('user_id');
33 30
       my $data = $vresult->data;
34
-      my $rep = $data->{repository_name};
35
-      $self->redirect_to("/$user/$rep");
31
+      my $project = $data->{project};
32
+      my $description = $data->{description};
33
+      
34
+      app->git->create_repository(
35
+        $user,
36
+        $project,
37
+        {description => $description}
38
+      );
39
+      
40
+      $self->redirect_to("/$user/$project");
36 41
       return 1;
37 42
     }
38
-    else {
39
-      $errors = $vresult->messages;
40
-    }
43
+    else { $errors = $vresult->messages }
41 44
   }
42 45
 %>
43 46
 
... ...
@@ -56,17 +59,11 @@
56 59
     % }
57 60
     <form action="<%= url_for->query(op => 'create') %>" method="post">
58 61
       <div><b>Repository name</b></div>
59
-      <div><%= input_tag 'repository_name', type => 'text', style => 'width:300px' %></div>
62
+      <div><%= input_tag 'project', type => 'text', style => 'width:300px' %></div>
60 63
 
61 64
       <div><b>Description</b> (optional)</div>
62 65
       <div><%= input_tag 'description', type => 'text', style => 'width:600px' %></div>
63 66
 
64
-      <label class="checkbox">
65
-        <%= check_box 'readme_contain' => 1 %> <b>Initialize this repository with a README</b>
66
-      </label>
67
-      <div style="margin-bottom:10px">
68
-        This will allow you to git clone the repository immediately.
69
-      </div>
70 67
       <input type="submit" class="btn" value="Create repository">
71 68
     </form>
72 69
   </div>
+1 -3
templates/main/home.html.ep
... ...
@@ -1,7 +1,5 @@
1 1
 <%
2
-  use Gitprep::API;
3
-  my $api = Gitprep::API->new($self);
4
-  
2
+  my $api = gitprep_api;
5 3
   my $users = $api->users;
6 4
 %>
7 5
 
+101 -63
templates/main/project.html.ep
... ...
@@ -1,5 +1,7 @@
1 1
 <%
2 2
   use Gitprep::API;
3
+  
4
+  my $state;
3 5
 
4 6
   # API
5 7
   my $api = Gitprep::API->new($self);
... ...
@@ -21,84 +23,120 @@
21 23
 
22 24
   # Git
23 25
   my $git = app->git;
24
-
25
-  # Id and directory
26
-  my ($id, $dir) = $git->parse_object($rep, $object);
27
-  
28
-  # Tree id
29
-  my $tid;
30
-  my $commit = $git->parse_commit($rep, $id);
31
-  if (defined $dir && $dir ne '') {
32
-    $tid = $git->id_by_path($rep, $id, $dir, 'tree');
33
-  }
34
-  else { $tid = $commit->{tree} }
35
-  $self->render_not_found unless defined $tid;
36
-
37
-  # Commit log
38
-  my $latest_commit_log = $git->latest_commit_log($rep, $rev);
39
-  
40
-  # Tree
41
-  my $trees = $git->trees($rep, $tid, $rev);
42 26
   
43
-  # Repository description
44
-  my $desc = $git->project_description($rep);
27
+  # Check exsitence
28
+  my $desc;
29
+  my $commits_number;
30
+  my $readme;
45 31
   
46
-  # Commits number
47
-  my $commits_number = $git->commits_number($rep, $rev);
48
-  
49
-  # README
50
-  my $readme = $git->blob_plain($rep, $rev, 'README');
51
-  
52
-  # Variable for included template
53
-  stash(
54
-    commit => $commit,
55
-    latest_commit_log => $latest_commit_log,
56
-    trees => $trees,
57
-    dir => $dir,
58
-    rev => $rev,
59
-    title => "$user/$project"
60
-  );
32
+  if ($git->branch_exists($user, $project)) {
33
+    # Id and directory
34
+    my ($id, $dir) = $git->parse_object($rep, $object);
35
+    
36
+    # Tree id
37
+    my $tid;
38
+    my $commit = $git->parse_commit($rep, $id);
39
+    if (defined $dir && $dir ne '') {
40
+      $tid = $git->id_by_path($rep, $id, $dir, 'tree');
41
+    }
42
+    else { $tid = $commit->{tree} }
43
+    $self->render_not_found unless defined $tid;
44
+
45
+    # Commit log
46
+    my $latest_commit_log = $git->latest_commit_log($rep, $rev);
47
+    
48
+    # Tree
49
+    my $trees = $git->trees($rep, $tid, $rev);
50
+    
51
+    # Repository description
52
+    my $desc = $git->project_description($rep);
53
+    
54
+    # Commits number
55
+    my $commits_number = $git->commits_number($rep, $rev);
56
+    
57
+    # README
58
+    my $readme = $git->blob_plain($rep, $rev, 'README');
59
+    
60
+    # Variable for included template
61
+    stash(
62
+      commit => $commit,
63
+      latest_commit_log => $latest_commit_log,
64
+      trees => $trees,
65
+      dir => $dir,
66
+      rev => $rev,
67
+      title => "$user/$project"
68
+    );
69
+    
70
+    $state = 'display';
71
+  }
72
+  else { $state = 'init' }
61 73
 %>
62 74
 
63 75
 % layout 'common';
64 76
   
65
-  %= stylesheet begin
66
-  % end
67
-
68 77
   %= include '/include/header';
69 78
 
70 79
   <div class="container">
71 80
     %= include '/include/project_header';
72 81
     
73
-    <h4 style="margin-top:0px">
74
-      <%= $desc %>
75
-    </h4>
82
+    % if ($state eq 'display') {
83
+      <h4 style="margin-top:0px">
84
+        <%= $desc %>
85
+      </h4>
76 86
 
77
-    <div class="breadcrumb" style="background-color:white;border:1px solid #ccc;padding:0 5px;">
78
-      <a class="btn" href="<%= url_for("/$user/$project/archive/master.zip") %>"><i class="icon-arrow-down"></i>ZIP</a>
79
-      <a class="btn corner-right-none" href="#" >HTTP</a><a class="btn corner-left-none corner-right-none" href="#">SSH</a><input type="text" style="border-top-left-radius:0;border-bottom-left-radius:0;margin-left:0;margin-top:10px;">
80
-      <span>Read-only access</span>
81
-    </div>
82
-    
83
-    %= include '/include/code_menu', display => 'files';
84
-    
85
-    <div class="row">
86
-      <div class="span6">
87
-        <a href="<%= url_for %>"><%= $project %></a>
87
+      <div class="breadcrumb" style="background-color:white;border:1px solid #ccc;padding:0 5px;">
88
+        <a class="btn" href="<%= url_for("/$user/$project/archive/master.zip") %>"><i class="icon-arrow-down"></i>ZIP</a>
89
+        <a class="btn corner-right-none" href="#" >HTTP</a><a class="btn corner-left-none corner-right-none" href="#">SSH</a><input type="text" style="border-top-left-radius:0;border-bottom-left-radius:0;margin-left:0;margin-top:10px;">
90
+        <span>Read-only access</span>
88 91
       </div>
89
-      <div class="span6 text-right">
90
-        <a href="<%= url_for("/$user/$project/commits/master") %>">
91
-          <%= $commits_number %> commits
92
-        </a>
92
+      
93
+      %= include '/include/code_menu', display => 'files';
94
+      
95
+      <div class="row">
96
+        <div class="span6">
97
+          <a href="<%= url_for %>"><%= $project %></a>
98
+        </div>
99
+        <div class="span6 text-right">
100
+          <a href="<%= url_for("/$user/$project/commits/master") %>">
101
+            <%= $commits_number %> commits
102
+          </a>
103
+        </div>
93 104
       </div>
94
-    </div>
95
-    
96
-    %= include '/include/tree';
105
+      
106
+      %= include '/include/tree';
107
+      
108
+      <div>
109
+        <h3>README</h3>
110
+        <pre><%= $readme %></pre>
111
+      </div>
112
+    % } elsif ($state eq 'init') {
97 113
     
98
-    <div>
99
-      <h3>README</h3>
100
-      <pre><%= $readme %></pre>
101
-    </div>
114
+      <div class="text-center" style="margin-bottom:10px">
115
+        <b>Create a new repository on the command line</b>
116
+      </div>
117
+      
118
+      % my $url = url_for->to_abs;
119
+      % my $ssh_port = config->{ssh_port};
120
+      % my $rep_home = app->git->rep_home;
121
+      % my $ssh_url = 'ssh://kimoto@' . $url->host
122
+      %   . ($ssh_port ? ":$ssh_port" : '') . "$rep_home/$user/$project.git";
123
+      
124
+      <pre style="margin-bottom:30px">
125
+touch README
126
+git init
127
+git add README
128
+git commit -m "first commit"
129
+git remote add origin <%= $ssh_url %>
130
+git push -u origin master</pre>
131
+      
132
+      <div class="text-center" style="margin-bottom:10px">
133
+        <b>Push an existing repository from the command line</b>
134
+      </div>
135
+      
136
+      <pre style="margin-bottom:30px">
137
+git remote add origin <%= $ssh_url %>
138
+git push -u origin master</pre>
139
+    % }
102 140
   </div>
103 141
   
104 142
   %= include '/include/footer';
+4 -4
templates/main/user.html.ep
... ...
@@ -1,9 +1,8 @@
1 1
 <%
2
-  my $root = config->{root};
3 2
   my $user = param('user');
4 3
   
5 4
   # Projects
6
-  my $reps = app->git->projects("/$root/$user");
5
+  my $reps = app->git->projects($user);
7 6
 %>
8 7
 
9 8
 % layout 'common';
... ...
@@ -29,9 +28,10 @@
29 28
                 </a>
30 29
               </td>
31 30
               <td>
32
-                <%= $rep->{descr} %>
31
+                <%= $rep->{description} %>
33 32
               </td>
34
-              <td class="muted">last updated <%= $rep->{age_string} %></td>
33
+              % my $age = $rep->{age_string};
34
+              <td class="muted"><%= $age ? "last updated $age" : 'new repository' %></td>
35 35
             </div>
36 36
           </tr>
37 37
         % }