... | ... |
@@ -224,8 +224,8 @@ sub startup { |
224 | 224 |
# Network Graph |
225 | 225 |
$r->get('/network/graph/(*rev1)...(*rev2_abs)' => template '/network/graph'); |
226 | 226 |
|
227 |
- # Pull |
|
228 |
- $r->get('/pull/(*rev1)...(*rev2_abs)' => template '/pull'); |
|
227 |
+ # Import branch |
|
228 |
+ $r->any('/import-branch/(*rev1)...(*rev2_abs)' => template '/import-branch'); |
|
229 | 229 |
|
230 | 230 |
# Get branches and tags |
231 | 231 |
$r->get('/api/revs' => template '/api/revs'); |
... | ... |
@@ -1405,6 +1405,28 @@ sub parse_ls_tree_line { |
1405 | 1405 |
return \%res; |
1406 | 1406 |
} |
1407 | 1407 |
|
1408 |
+sub import_branch { |
|
1409 |
+ my ($self, $user, $project, $branch, $remote_user, $remote_project, $remote_branch) = @_; |
|
1410 |
+ |
|
1411 |
+ # Git pull |
|
1412 |
+ my $remote_rep = $self->rep($remote_user, $remote_project); |
|
1413 |
+ my @cmd = $self->cmd( |
|
1414 |
+ $user, |
|
1415 |
+ $project, |
|
1416 |
+ 'fetch', |
|
1417 |
+ $remote_rep, |
|
1418 |
+ "refs/heads/$remote_branch:refs/heads/$branch" |
|
1419 |
+ ); |
|
1420 |
+ open my $fh, '-|', @cmd |
|
1421 |
+ or croak 'Open git fetch failed'; |
|
1422 |
+ |
|
1423 |
+ # Output |
|
1424 |
+ local $/ = "\0"; |
|
1425 |
+ my $content = $self->_dec(scalar <$fh>); |
|
1426 |
+ warn $content; |
|
1427 |
+ close $fh or croak "Can't read git fetch result"; |
|
1428 |
+} |
|
1429 |
+ |
|
1408 | 1430 |
sub search_bin { |
1409 | 1431 |
my $self = shift; |
1410 | 1432 |
|
... | ... |
@@ -1486,6 +1508,73 @@ sub snapshot_name { |
1486 | 1508 |
return wantarray ? ($name, $name) : $name; |
1487 | 1509 |
} |
1488 | 1510 |
|
1511 |
+sub timestamp { |
|
1512 |
+ my ($self, $date) = @_; |
|
1513 |
+ |
|
1514 |
+ # Time stamp |
|
1515 |
+ my $strtime = $date->{rfc2822}; |
|
1516 |
+ my $localtime_format = '(%02d:%02d %s)'; |
|
1517 |
+ if ($date->{hour_local} < 6) { $localtime_format = '(%02d:%02d %s)' } |
|
1518 |
+ $strtime .= ' ' . sprintf( |
|
1519 |
+ $localtime_format, |
|
1520 |
+ $date->{hour_local}, |
|
1521 |
+ $date->{minute_local}, |
|
1522 |
+ $date->{tz_local} |
|
1523 |
+ ); |
|
1524 |
+ |
|
1525 |
+ return $strtime; |
|
1526 |
+} |
|
1527 |
+ |
|
1528 |
+sub trees { |
|
1529 |
+ my ($self, $user, $project, $rev, $dir) = @_; |
|
1530 |
+ $dir = '' unless defined $dir; |
|
1531 |
+ |
|
1532 |
+ # Get tree |
|
1533 |
+ my $tid; |
|
1534 |
+ if (defined $dir && $dir ne '') { |
|
1535 |
+ $tid = $self->path_to_hash($user, $project, $rev, $dir, 'tree'); |
|
1536 |
+ } |
|
1537 |
+ else { |
|
1538 |
+ my $commit = $self->get_commit($user, $project, $rev); |
|
1539 |
+ $tid = $commit->{tree}; |
|
1540 |
+ } |
|
1541 |
+ my @entries = (); |
|
1542 |
+ my $show_sizes = 0; |
|
1543 |
+ my @cmd = $self->cmd( |
|
1544 |
+ $user, |
|
1545 |
+ $project, |
|
1546 |
+ 'ls-tree', |
|
1547 |
+ '-z', |
|
1548 |
+ ($show_sizes ? '-l' : ()), |
|
1549 |
+ $tid |
|
1550 |
+ ); |
|
1551 |
+ open my $fh, '-|', @cmd |
|
1552 |
+ or $self->croak('Open git-ls-tree failed'); |
|
1553 |
+ { |
|
1554 |
+ local $/ = "\0"; |
|
1555 |
+ @entries = map { chomp; $self->_dec($_) } <$fh>; |
|
1556 |
+ } |
|
1557 |
+ close $fh |
|
1558 |
+ or $self->croak(404, "Reading tree failed"); |
|
1559 |
+ |
|
1560 |
+ # Parse tree |
|
1561 |
+ my $trees; |
|
1562 |
+ for my $line (@entries) { |
|
1563 |
+ my $tree = $self->parse_ls_tree_line($line, -z => 1, -l => $show_sizes); |
|
1564 |
+ $tree->{mode_str} = $self->_mode_str($tree->{mode}); |
|
1565 |
+ |
|
1566 |
+ # Commit log |
|
1567 |
+ my $path = defined $dir && $dir ne '' ? "$dir/$tree->{name}" : $tree->{name}; |
|
1568 |
+ my $commit = $self->last_change_commit($user, $project, $rev, $path); |
|
1569 |
+ $tree->{commit} = $commit; |
|
1570 |
+ |
|
1571 |
+ push @$trees, $tree; |
|
1572 |
+ } |
|
1573 |
+ $trees = [sort {$b->{type} cmp $a->{type} || $a->{name} cmp $b->{name}} @$trees]; |
|
1574 |
+ |
|
1575 |
+ return $trees; |
|
1576 |
+} |
|
1577 |
+ |
|
1489 | 1578 |
sub _age_string { |
1490 | 1579 |
my ($self, $age) = @_; |
1491 | 1580 |
my $age_str; |
... | ... |
@@ -1569,73 +1658,6 @@ sub _chop_str { |
1569 | 1658 |
} |
1570 | 1659 |
} |
1571 | 1660 |
|
1572 |
-sub timestamp { |
|
1573 |
- my ($self, $date) = @_; |
|
1574 |
- |
|
1575 |
- # Time stamp |
|
1576 |
- my $strtime = $date->{rfc2822}; |
|
1577 |
- my $localtime_format = '(%02d:%02d %s)'; |
|
1578 |
- if ($date->{hour_local} < 6) { $localtime_format = '(%02d:%02d %s)' } |
|
1579 |
- $strtime .= ' ' . sprintf( |
|
1580 |
- $localtime_format, |
|
1581 |
- $date->{hour_local}, |
|
1582 |
- $date->{minute_local}, |
|
1583 |
- $date->{tz_local} |
|
1584 |
- ); |
|
1585 |
- |
|
1586 |
- return $strtime; |
|
1587 |
-} |
|
1588 |
- |
|
1589 |
-sub trees { |
|
1590 |
- my ($self, $user, $project, $rev, $dir) = @_; |
|
1591 |
- $dir = '' unless defined $dir; |
|
1592 |
- |
|
1593 |
- # Get tree |
|
1594 |
- my $tid; |
|
1595 |
- if (defined $dir && $dir ne '') { |
|
1596 |
- $tid = $self->path_to_hash($user, $project, $rev, $dir, 'tree'); |
|
1597 |
- } |
|
1598 |
- else { |
|
1599 |
- my $commit = $self->get_commit($user, $project, $rev); |
|
1600 |
- $tid = $commit->{tree}; |
|
1601 |
- } |
|
1602 |
- my @entries = (); |
|
1603 |
- my $show_sizes = 0; |
|
1604 |
- my @cmd = $self->cmd( |
|
1605 |
- $user, |
|
1606 |
- $project, |
|
1607 |
- 'ls-tree', |
|
1608 |
- '-z', |
|
1609 |
- ($show_sizes ? '-l' : ()), |
|
1610 |
- $tid |
|
1611 |
- ); |
|
1612 |
- open my $fh, '-|', @cmd |
|
1613 |
- or $self->croak('Open git-ls-tree failed'); |
|
1614 |
- { |
|
1615 |
- local $/ = "\0"; |
|
1616 |
- @entries = map { chomp; $self->_dec($_) } <$fh>; |
|
1617 |
- } |
|
1618 |
- close $fh |
|
1619 |
- or $self->croak(404, "Reading tree failed"); |
|
1620 |
- |
|
1621 |
- # Parse tree |
|
1622 |
- my $trees; |
|
1623 |
- for my $line (@entries) { |
|
1624 |
- my $tree = $self->parse_ls_tree_line($line, -z => 1, -l => $show_sizes); |
|
1625 |
- $tree->{mode_str} = $self->_mode_str($tree->{mode}); |
|
1626 |
- |
|
1627 |
- # Commit log |
|
1628 |
- my $path = defined $dir && $dir ne '' ? "$dir/$tree->{name}" : $tree->{name}; |
|
1629 |
- my $commit = $self->last_change_commit($user, $project, $rev, $path); |
|
1630 |
- $tree->{commit} = $commit; |
|
1631 |
- |
|
1632 |
- push @$trees, $tree; |
|
1633 |
- } |
|
1634 |
- $trees = [sort {$b->{type} cmp $a->{type} || $a->{name} cmp $b->{name}} @$trees]; |
|
1635 |
- |
|
1636 |
- return $trees; |
|
1637 |
-} |
|
1638 |
- |
|
1639 | 1661 |
sub _dec { |
1640 | 1662 |
my ($self, $str) = @_; |
1641 | 1663 |
|
... | ... |
@@ -0,0 +1,129 @@ |
1 |
+<% |
|
2 |
+ my $user = param('user'); |
|
3 |
+ my $project = param('project'); |
|
4 |
+ my $branch = param('rev1'); |
|
5 |
+ my $rev2_abs = param('rev2_abs'); |
|
6 |
+ my ($remote_user, $remote_project, $remote_branch) = split /\//, $rev2_abs, 3; |
|
7 |
+ |
|
8 |
+ # Branches |
|
9 |
+ my $git = app->git; |
|
10 |
+ my $branches = $git->branches($user, $project); |
|
11 |
+ my $branch_names = [map { $_->{name} } @$branches]; |
|
12 |
+ my $remote_branches = $git->branches($remote_user, $remote_project); |
|
13 |
+ my $remote_branch_names = [map { $_->{name} } @$remote_branches]; |
|
14 |
+ |
|
15 |
+ my $op = param('op') || ''; |
|
16 |
+ my $errors; |
|
17 |
+ if ($op eq 'pull' && lc $self->req->method eq 'post') { |
|
18 |
+ |
|
19 |
+ # Validation |
|
20 |
+ my $api = gitprep_api; |
|
21 |
+ my $params = $api->params; |
|
22 |
+ my $rule = [ |
|
23 |
+ user => [ |
|
24 |
+ 'user_name' |
|
25 |
+ ], |
|
26 |
+ project => [ |
|
27 |
+ 'project_name' |
|
28 |
+ ], |
|
29 |
+ branch => [ |
|
30 |
+ 'any' |
|
31 |
+ ], |
|
32 |
+ remote_user => [ |
|
33 |
+ 'user_name' |
|
34 |
+ ], |
|
35 |
+ remote_project => [ |
|
36 |
+ 'project_name' |
|
37 |
+ ], |
|
38 |
+ remote_branch => [ |
|
39 |
+ 'any' |
|
40 |
+ ] |
|
41 |
+ ]; |
|
42 |
+ my $vresult = app->validator->validate($params, $rule); |
|
43 |
+ |
|
44 |
+ if ($vresult->is_ok) { |
|
45 |
+ my $safe_params = $vresult->data; |
|
46 |
+ |
|
47 |
+ my $user = $safe_params->{user}; |
|
48 |
+ my $project = $safe_params->{project}; |
|
49 |
+ my $branch = $safe_params->{branch}; |
|
50 |
+ my $remote_user = $safe_params->{remote_user}; |
|
51 |
+ my $remote_project = $safe_params->{remote_project}; |
|
52 |
+ my $remote_branch = $safe_params->{remote_branch}; |
|
53 |
+ |
|
54 |
+ $git->import_branch( |
|
55 |
+ $user, |
|
56 |
+ $project, |
|
57 |
+ $branch, |
|
58 |
+ $remote_user, |
|
59 |
+ $remote_project, |
|
60 |
+ $remote_branch |
|
61 |
+ ); |
|
62 |
+ } |
|
63 |
+ else { |
|
64 |
+ $errors = ['Parameter is invalid']; |
|
65 |
+ } |
|
66 |
+ } |
|
67 |
+%> |
|
68 |
+ |
|
69 |
+% layout 'common', title => "Import branch"; |
|
70 |
+ %= include 'include/header'; |
|
71 |
+ |
|
72 |
+ %= javascript begin |
|
73 |
+ $('document').ready(function () { |
|
74 |
+ |
|
75 |
+ // Select remote branch |
|
76 |
+ $('[name=copy-branch-name]').on('click', function () { |
|
77 |
+ $('[name=branch]').val($('[name=remote_branch]').val()); |
|
78 |
+ return false; |
|
79 |
+ }); |
|
80 |
+ }); |
|
81 |
+ % end |
|
82 |
+ |
|
83 |
+ <div class="container"> |
|
84 |
+ % if ($errors) { |
|
85 |
+ <div class="alert alert-error"> |
|
86 |
+ <button type="button" class="close" data-dismiss="alert">×</button> |
|
87 |
+ % for my $error (@$errors) { |
|
88 |
+ <p><%= $error %></p> |
|
89 |
+ % } |
|
90 |
+ </div> |
|
91 |
+ % } |
|
92 |
+ <h3>Import branch</h3> |
|
93 |
+ <form action="<%= url_for->query(op => 'import') %>" method="post"> |
|
94 |
+ <div class="row" style="font-size:22px"> |
|
95 |
+ <div class="span5"> |
|
96 |
+ <div class="well" style="text-align:center"> |
|
97 |
+ <div style="color:blue;margin-bottom:15px"> |
|
98 |
+ %= "$user / $project"; |
|
99 |
+ </div> |
|
100 |
+ <div> |
|
101 |
+ %= text_field 'branch'; |
|
102 |
+ </div> |
|
103 |
+ <button name="copy-branch-name", class="btn">Copy Branch Name</button> |
|
104 |
+ %= submit_button 'Import', style => "width:100px", class => "btn"; |
|
105 |
+ </div> |
|
106 |
+ </div> |
|
107 |
+ <div class="span2"> |
|
108 |
+ <div style="padding: 19px;text-align:center;font-size:26px"> |
|
109 |
+ ⇐ |
|
110 |
+ </div> |
|
111 |
+ </div> |
|
112 |
+ <div class="span5"> |
|
113 |
+ <div class="well" style="text-align:center"> |
|
114 |
+ <div style="color:green;margin-bottom:15px"> |
|
115 |
+ %= "$remote_user / $remote_project"; |
|
116 |
+ </div> |
|
117 |
+ % param(remote_branch => $remote_branch); |
|
118 |
+ %= select_field 'remote_branch' => $remote_branch_names; |
|
119 |
+ </div> |
|
120 |
+ </div> |
|
121 |
+ </div> |
|
122 |
+ %= hidden_field user => $user; |
|
123 |
+ %= hidden_field project => $user; |
|
124 |
+ %= hidden_field remote_user => $remote_user; |
|
125 |
+ %= hidden_field remote_project => $remote_project; |
|
126 |
+ </form> |
|
127 |
+ </div> |
|
128 |
+ |
|
129 |
+ %= include '/include/footer'; |
... | ... |
@@ -36,15 +36,15 @@ |
36 | 36 |
+ remote_member + '/' + remote_project + '/' + remote_branch; |
37 | 37 |
}); |
38 | 38 |
|
39 |
- // Click pull button |
|
40 |
- $('[name=pull-btn]').on('click', function () { |
|
39 |
+ // Click import button |
|
40 |
+ $('[name=import-btn]').on('click', function () { |
|
41 | 41 |
var branch = $('[name=branch]').val(); |
42 | 42 |
var remote = $(this).closest('[name=remote]'); |
43 | 43 |
var remote_member = remote.find('[name=remote-member]').text(); |
44 | 44 |
var remote_project = remote.find('[name=remote-project]').text(); |
45 | 45 |
var remote_branch = remote.find('[name=remote-branch]').val(); |
46 | 46 |
|
47 |
- location.href = '<%= url_for("/$user/$project/pull/") %>' + branch + '...' |
|
47 |
+ location.href = '<%= url_for("/$user/$project/import-branch/") %>' + branch + '...' |
|
48 | 48 |
+ remote_member + '/' + remote_project + '/' + remote_branch; |
49 | 49 |
}); |
50 | 50 |
}); |
... | ... |
@@ -80,7 +80,7 @@ |
80 | 80 |
</div> |
81 | 81 |
<div class="text-right"> |
82 | 82 |
<button name="compare-btn" class="btn" style="margin-top:5px">Compare</button> |
83 |
- <button name="pull-btn" class="btn" style="margin-top:5px">Pull</button> |
|
83 |
+ <button name="import-btn" class="btn" style="margin-top:5px">Import</button> |
|
84 | 84 |
</div> |
85 | 85 |
</div> |
86 | 86 |
<hr style="margin:0"> |
... | ... |
@@ -1,89 +0,0 @@ |
1 |
-<% |
|
2 |
- my $user = param('user'); |
|
3 |
- my $project = param('project'); |
|
4 |
- my $branch = param('rev1'); |
|
5 |
- my $rev2_abs = param('rev2_abs'); |
|
6 |
- my ($remote_user, $remote_project, $remote_branch) = split /\//, $rev2_abs, 3; |
|
7 |
- |
|
8 |
- # Branches |
|
9 |
- my $git = app->git; |
|
10 |
- my $branches = $git->branches($user, $project); |
|
11 |
- my $branch_names = [map { $_->{name} } @$branches]; |
|
12 |
- my $remote_branches = $git->branches($remote_user, $remote_project); |
|
13 |
- my $remote_branch_names = [map { $_->{name} } @$remote_branches]; |
|
14 |
-%> |
|
15 |
- |
|
16 |
-% layout 'common', title => "Pull $user/$project/$branch...$rev2_abs"; |
|
17 |
- %= include 'include/header'; |
|
18 |
- |
|
19 |
- %= javascript begin |
|
20 |
- $('document').ready(function () { |
|
21 |
- |
|
22 |
- // New button click |
|
23 |
- $('[name=new]').on('click', function () { |
|
24 |
- $('[name=branch_select]').val(''); |
|
25 |
- $('[name=branch]').val(''); |
|
26 |
- |
|
27 |
- return false; |
|
28 |
- }); |
|
29 |
- |
|
30 |
- // Select branch |
|
31 |
- $('[name=branch_select]').on('change', function () { |
|
32 |
- var val = $(this).val(); |
|
33 |
- if (val === '(New)') { |
|
34 |
- $('[name=branch]').val(''); |
|
35 |
- } |
|
36 |
- else if (val === '(Same name)') { |
|
37 |
- $('[name=branch]').val($('[name=remote_branch]').val()); |
|
38 |
- } |
|
39 |
- else { |
|
40 |
- $('[name=branch]').val($(this).val()); |
|
41 |
- } |
|
42 |
- }); |
|
43 |
- |
|
44 |
- // Select remote branch |
|
45 |
- $('[name=remote_branch]').on('change', function () { |
|
46 |
- if ($('[name=branch_select]').val() === '(Same name)') { |
|
47 |
- $('[name=branch]').val($(this).val()); |
|
48 |
- } |
|
49 |
- }); |
|
50 |
- }); |
|
51 |
- % end |
|
52 |
- |
|
53 |
- <div class="container"> |
|
54 |
- <h3>Pull</h3> |
|
55 |
- <form action="<%= url_for->query(op => 'pull') %>" method="post"> |
|
56 |
- <div class="row" style="font-size:22px"> |
|
57 |
- <div class="span5"> |
|
58 |
- <div class="well" style="text-align:center"> |
|
59 |
- <div style="color:blue;margin-bottom:15px"> |
|
60 |
- %= "$user / $project"; |
|
61 |
- </div> |
|
62 |
- % param(branch_select => $rev1); |
|
63 |
- %= select_field 'branch_select' => ['(New)', '(Same name)', @$branch_names]; |
|
64 |
- <div> |
|
65 |
- % param(branch => $rev1); |
|
66 |
- %= text_field 'branch'; |
|
67 |
- </div> |
|
68 |
- %= submit_button 'Pull', style => "width:100px", class => "btn", ; |
|
69 |
- </div> |
|
70 |
- </div> |
|
71 |
- <div class="span2"> |
|
72 |
- <div style="padding: 19px;text-align:center;font-size:26px"> |
|
73 |
- ⇐ |
|
74 |
- </div> |
|
75 |
- </div> |
|
76 |
- <div class="span5"> |
|
77 |
- <div class="well" style="text-align:center"> |
|
78 |
- <div style="color:green;margin-bottom:15px"> |
|
79 |
- %= "$remote_user / $remote_project"; |
|
80 |
- </div> |
|
81 |
- % param(remote_branch => $remote_branch); |
|
82 |
- %= select_field 'remote_branch' => $remote_branch_names; |
|
83 |
- </div> |
|
84 |
- </div> |
|
85 |
- </div> |
|
86 |
- </form> |
|
87 |
- </div> |
|
88 |
- |
|
89 |
- %= include '/include/footer'; |