Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
O
OHR Support
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
97
Issues
97
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
OHR Support
Commits
21ae869d
Commit
21ae869d
authored
Apr 20, 2012
by
Enrique García Cota
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
increased scm file download in scm views
parent
c4f80d5e
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
395 additions
and
361 deletions
+395
-361
repositories_controller.rb
app/controllers/repositories_controller.rb
+38
-31
repository.rb
app/models/repository.rb
+4
-0
abstract_adapter.rb
lib/redmine/scm/adapters/abstract_adapter.rb
+52
-17
bazaar_adapter.rb
lib/redmine/scm/adapters/bazaar_adapter.rb
+1
-1
cvs_adapter.rb
lib/redmine/scm/adapters/cvs_adapter.rb
+1
-1
darcs_adapter.rb
lib/redmine/scm/adapters/darcs_adapter.rb
+1
-1
filesystem_adapter.rb
lib/redmine/scm/adapters/filesystem_adapter.rb
+9
-1
git_adapter.rb
lib/redmine/scm/adapters/git_adapter.rb
+73
-79
mercurial_adapter.rb
lib/redmine/scm/adapters/mercurial_adapter.rb
+122
-120
subversion_adapter.rb
lib/redmine/scm/adapters/subversion_adapter.rb
+94
-110
No files found.
app/controllers/repositories_controller.rb
View file @
21ae869d
...
...
@@ -118,41 +118,48 @@ class RepositoriesController < ApplicationController
# If the entry is a dir, show the browser
(
show
;
return
)
if
@entry
.
is_dir?
@content
=
@repository
.
cat
(
@path
,
@rev
)
(
show_error_not_found
;
return
)
unless
@content
if
'raw'
==
params
[
:format
]
||
(
@content
.
size
&&
@content
.
size
>
Setting
.
file_max_size_displayed
.
to_i
.
kilobyte
)
||
!
is_entry_text_data?
(
@content
,
@path
)
# Force the download
send_opt
=
{
:filename
=>
filename_for_content_disposition
(
@path
.
split
(
'/'
).
last
)
}
send_type
=
Redmine
::
MimeType
.
of
(
@path
)
send_opt
[
:type
]
=
send_type
.
to_s
if
send_type
send_data
@content
,
send_opt
else
# Prevent empty lines when displaying a file with Windows style eol
# TODO: UTF-16
# Is this needs? AttachmentsController reads file simply.
@content
.
gsub!
(
"
\r\n
"
,
"
\n
"
)
@changeset
=
@repository
.
find_changeset_by_name
(
@rev
)
@repository
.
cat_to_tempfile
(
@path
,
@rev
)
do
|
f
|
if
params
[
:format
]
==
'raw'
||
is_too_large_to_show?
(
f
)
||
is_binary?
(
f
,
@path
)
send_type
=
Redmine
::
MimeType
.
of
(
@path
)
options
=
{
:filename
=>
filename_for_content_disposition
(
@path
.
split
(
'/'
).
last
),
:disposition
=>
'attachment'
,
:x_sendfile
=>
false
# x_sendfile does not work well with tempfiles
}
options
[
:type
]
=
send_type
.
to_s
if
send_type
send_file
(
f
.
path
,
options
)
else
@content
=
f
.
read
@changeset
=
@repository
.
find_changeset_by_name
(
@rev
)
end
end
rescue
Errno
::
ENOENT
show_error_not_found
end
def
is_entry_text_data?
(
ent
,
path
)
# UTF-16 contains "\x00".
# It is very strict that file contains less than 30% of ascii symbols
# in non Western Europe.
return
true
if
Redmine
::
MimeType
.
is_type?
(
'text'
,
path
)
# Ruby 1.8.6 has a bug of integer divisions.
# http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
if
ent
.
respond_to?
(
"is_binary_data?"
)
&&
ent
.
is_binary_data?
# Ruby 1.8.x and <1.9.2
return
false
elsif
ent
.
respond_to?
(
:force_encoding
)
&&
(
ent
.
dup
.
force_encoding
(
"UTF-8"
)
!=
ent
.
dup
.
force_encoding
(
"BINARY"
)
)
# Ruby 1.9.2
# TODO: need to handle edge cases of non-binary content that isn't UTF-8
return
false
end
true
def
is_too_large_to_show?
(
f
)
f
.
size
>
Setting
.
file_max_size_displayed
.
to_i
.
kilobyte
end
private
:is_too_large_to_show?
def
is_binary?
(
file
,
path
)
return
false
if
Redmine
::
MimeType
.
is_type?
(
'text'
,
path
)
# First block of the file is examined for odd
# characters such as strange control codes or char-
# acters with the high bit set. If too many strange
# characters (>30%) are found, it's a binary file,
# otherwise it's a text file. Also, any file con-
# taining null in the first block is considered a
# binary file.
blk
=
file
.
read
(
Setting
.
file_max_size_displayed
.
to_i
.
kilobyte
)
return
blk
.
size
==
0
||
blk
.
count
(
"^ -~"
,
"^
\r\n
"
)
/
blk
.
size
>
0.3
||
blk
.
count
(
"
\x00
"
)
>
0
end
private
:is_
entry_text_data
?
private
:is_
binary
?
def
annotate
@entry
=
@repository
.
entry
(
@path
,
@rev
)
...
...
app/models/repository.rb
View file @
21ae869d
...
...
@@ -104,6 +104,10 @@ class Repository < ActiveRecord::Base
scm
.
cat
(
path
,
identifier
)
end
def
cat_to_tempfile
(
path
,
identifier
,
&
block
)
scm
.
cat_to_tempfile
(
path
,
identifier
,
&
block
)
end
def
diff
(
path
,
rev
,
rev_to
)
scm
.
diff
(
path
,
rev
,
rev_to
)
end
...
...
lib/redmine/scm/adapters/abstract_adapter.rb
View file @
21ae869d
...
...
@@ -69,7 +69,7 @@ module Redmine
end
def
adapter_name
'Abstract'
self
.
class
.
name
.
gsub
(
"Adapter"
,
""
)
end
def
supports_cat?
...
...
@@ -143,6 +143,19 @@ module Redmine
return
nil
end
def
cat_to_tempfile
(
path
,
identifier
,
&
block
)
prefix
=
path
.
split
(
"/"
).
last
tmp_path
=
Rails
.
root
.
join
(
'tmp'
)
Tempfile
.
open
(
prefix
,
tmp_path
)
do
|
f
|
save_entry_in_file
(
f
,
path
,
identifier
)
block
.
call
(
f
)
end
end
def
save_entry_in_file
(
file
,
path
,
identifier
)
return
nil
end
def
with_leading_slash
(
path
)
path
||=
''
(
path
[
0
,
1
]
!=
"/"
)
?
"/
#{
path
}
"
:
path
...
...
@@ -183,34 +196,56 @@ module Redmine
self
.
class
.
logger
end
def
shellout
(
cmd
,
&
block
)
self
.
class
.
shellout
(
cmd
,
&
block
)
def
shellout
(
cmd
,
output_path
=
nil
,
&
block
)
self
.
class
.
shellout
(
cmd
,
output_path
,
&
block
)
end
def
build_scm_cmd
(
cmd_args
)
([
self
.
class
.
sq_bin
]
+
cmd_args
).
join
(
' '
)
end
def
scm_cmd
(
cmd_args
,
output_path
=
nil
,
&
block
)
cmd
=
build_scm_cmd
(
cmd_args
)
begin
ret
=
shellout
(
cmd
,
output_path
,
&
block
)
rescue
Exception
=>
e
msg
=
strip_credential
(
e
.
message
)
cmd
=
strip_credential
(
cmd
)
logger
.
error
(
"Error executing
#{
adapter_name
}
command [
#{
cmd
}
]:
#{
msg
}
"
)
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
ret
end
def
self
.
logger
RAILS_DEFAULT_LOGGER
end
def
self
.
shellout
(
cmd
,
&
block
)
logger
.
debug
"Shelling out:
#{
strip_credential
(
cmd
)
}
"
if
logger
&&
logger
.
debug?
if
Rails
.
env
==
'development'
# Capture stderr when running in dev environment
cmd
=
"
#{
cmd
}
2>>
#{
RAILS_ROOT
}
/log/scm.stderr.log"
end
def
self
.
process_cmd
(
cmd
,
output_path
)
cmd
=
Rails
.
env
==
'development'
?
"
#{
cmd
}
2>>
#{
RAILS_ROOT
}
/log/scm.stderr.log"
:
cmd
cmd
=
"
#{
cmd
}
>>
#{
output_path
}
"
if
output_path
.
present?
cmd
end
def
self
.
get_reading_mode_for_ruby_version
RUBY_VERSION
<
'1.9'
?
'r+'
:
'r+:ASCII-8BIT'
end
def
self
.
shellout
(
cmd
,
output_path
=
nil
,
&
block
)
logger
.
debug
(
"Shelling out:
#{
strip_credential
(
cmd
)
}
"
)
if
logger
&&
logger
.
respond_to?
(
:debug
)
cmd
=
process_cmd
(
cmd
,
output_path
)
mode
=
get_reading_mode_for_ruby_version
begin
if
RUBY_VERSION
<
'1.9'
mode
=
"r+"
else
mode
=
"r+:ASCII-8BIT"
end
result
=
nil
IO
.
popen
(
cmd
,
mode
)
do
|
io
|
io
.
close_write
block
.
call
(
io
)
if
block_given?
result
=
block
.
call
(
io
)
if
block_given?
end
result
rescue
Errno
::
ENOENT
=>
e
msg
=
strip_credential
(
e
.
message
)
# The command failed, log it and re-raise
logger
.
error
(
"SCM command failed, make sure that your SCM binary (eg. svn) is in PATH (
#{
ENV
[
'PATH'
]
}
):
#{
strip_credential
(
cmd
)
}
\n
with:
#{
msg
}
"
)
cmd
=
strip_credential
(
cmd
)
logger
.
error
(
"SCM command failed, make sure that your SCM binary (eg. svn) is in PATH (
#{
ENV
[
'PATH'
]
}
):
#{
cmd
}
\n
with:
#{
msg
}
"
)
raise
CommandFailed
.
new
(
msg
)
end
end
...
...
lib/redmine/scm/adapters/bazaar_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
module
Redmine
module
Scm
...
...
lib/redmine/scm/adapters/cvs_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
module
Redmine
module
Scm
...
...
lib/redmine/scm/adapters/darcs_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
require
'rexml/document'
module
Redmine
...
...
lib/redmine/scm/adapters/filesystem_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
require
'find'
module
Redmine
...
...
@@ -91,6 +91,14 @@ module Redmine
raise
CommandFailed
.
new
(
err
.
message
)
end
def
save_entry_in_file
(
f
,
path
,
identifier
)
p
=
scm_iconv
(
@path_encoding
,
'UTF-8'
,
target
(
path
))
FileUtils
.
cp
(
p
,
f
.
path
)
rescue
=>
err
logger
.
error
"scm: filesystem: error:
#{
err
.
message
}
"
raise
CommandFailed
.
new
(
err
.
message
)
end
private
# AbstractAdapter::target is implicitly made to quote paths.
...
...
lib/redmine/scm/adapters/git_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
module
Redmine
module
Scm
...
...
@@ -24,9 +24,6 @@ module Redmine
# Git executable name
GIT_BIN
=
Redmine
::
Configuration
[
'scm_git_command'
]
||
"git"
unless
defined?
(
GIT_BIN
)
# raised if scm command exited with error, e.g. unknown revision.
class
ScmCommandAborted
<
CommandFailed
;
end
class
<<
self
def
client_command
@@bin
||=
GIT_BIN
...
...
@@ -75,26 +72,22 @@ module Redmine
def
branches
return
@branches
if
@branches
@branches
=
[]
cmd_args
=
%w|branch --no-color|
scm_cmd
(
*
cmd_args
)
do
|
io
|
scm_cmd
(
cmd_args
)
do
|
io
|
@branches
=
[]
io
.
each_line
do
|
line
|
@branches
<<
line
.
match
(
'\s*\*?\s*(.*)$'
)[
1
]
end
@branches
.
sort!
end
@branches
.
sort!
rescue
ScmCommandAborted
nil
end
def
tags
return
@tags
if
@tags
cmd_args
=
%w|tag|
scm_cmd
(
*
cmd_args
)
do
|
io
|
scm_cmd
(
cmd_args
)
do
|
io
|
@tags
=
io
.
readlines
.
sort!
.
map
{
|
t
|
t
.
strip
}
end
rescue
ScmCommandAborted
nil
end
def
default_branch
...
...
@@ -106,11 +99,14 @@ module Redmine
def
entries
(
path
=
nil
,
identifier
=
nil
)
path
||=
''
p
=
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
entries
=
Entries
.
new
cmd_args
=
%w|ls-tree -l|
cmd_args
<<
"HEAD:
#{
p
}
"
if
identifier
.
nil?
cmd_args
<<
"
#{
identifier
}
:
#{
p
}
"
if
identifier
scm_cmd
(
*
cmd_args
)
do
|
io
|
scm_cmd
(
cmd_args
)
do
|
io
|
entries
=
Entries
.
new
io
.
each_line
do
|
line
|
e
=
line
.
chomp
.
to_s
if
e
=~
/^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/
...
...
@@ -124,18 +120,20 @@ module Redmine
full_path
=
p
.
empty?
?
name
:
"
#{
p
}
/
#{
name
}
"
n
=
scm_iconv
(
'UTF-8'
,
@path_encoding
,
name
)
full_p
=
scm_iconv
(
'UTF-8'
,
@path_encoding
,
full_path
)
entries
<<
Entry
.
new
({
:name
=>
n
,
:path
=>
full_p
,
:kind
=>
(
type
==
"tree"
)
?
'dir'
:
'file'
,
:size
=>
(
type
==
"tree"
)
?
nil
:
size
,
:lastrev
=>
@flag_report_last_commit
?
lastrev
(
full_path
,
identifier
)
:
Revision
.
new
})
unless
entries
.
detect
{
|
entry
|
entry
.
name
==
name
}
unless
entries
.
detect
{
|
entry
|
entry
.
name
==
name
}
entries
<<
Entry
.
new
({
:name
=>
n
,
:path
=>
full_p
,
:kind
=>
(
type
==
"tree"
)
?
'dir'
:
'file'
,
:size
=>
(
type
==
"tree"
)
?
nil
:
size
,
:lastrev
=>
@flag_report_last_commit
?
lastrev
(
full_path
,
identifier
)
:
Revision
.
new
})
end
end
end
entries
.
sort_by_name
end
entries
.
sort_by_name
rescue
ScmCommandAborted
nil
end
def
lastrev
(
path
,
rev
)
...
...
@@ -143,9 +141,9 @@ module Redmine
cmd_args
=
%w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1|
cmd_args
<<
rev
if
rev
cmd_args
<<
"--"
<<
path
unless
path
.
empty?
lines
=
[]
scm_cmd
(
*
cmd_args
)
{
|
io
|
lines
=
io
.
readlines
}
begin
lines
=
scm_cmd
(
cmd_args
)
{
|
io
|
io
.
readlines
}
if
lines
begin
id
=
lines
[
0
].
split
[
1
]
author
=
lines
[
1
].
match
(
'Author:\s+(.*)$'
)[
1
]
time
=
Time
.
parse
(
lines
[
4
].
match
(
'CommitDate:\s+(.*)$'
)[
1
])
...
...
@@ -157,17 +155,17 @@ module Redmine
:time
=>
time
,
:message
=>
nil
,
:paths
=>
nil
})
rescue
NoMethodError
=>
e
})
rescue
NoMethodError
=>
e
logger
.
error
(
"The revision '
#{
path
}
' has a wrong format"
)
return
nil
end
end
rescue
ScmCommandAborted
nil
end
def
revisions
(
path
,
identifier_from
,
identifier_to
,
options
=
{})
revisions
=
Revisions
.
new
cmd_args
=
%w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller|
cmd_args
<<
"--reverse"
if
options
[
:reverse
]
cmd_args
<<
"--all"
if
options
[
:all
]
...
...
@@ -179,7 +177,7 @@ module Redmine
cmd_args
<<
"--since='
#{
options
[
:since
].
strftime
(
"%Y-%m-%d %H:%M:%S"
)
}
'"
if
options
[
:since
]
cmd_args
<<
"--"
<<
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
if
path
&&
!
path
.
empty?
scm_cmd
*
cmd_args
do
|
io
|
scm_cmd
cmd_args
do
|
io
|
files
=
[]
changeset
=
{}
parsing_descr
=
0
#0: not parsing desc or files, 1: parsing desc, 2: parsing files
...
...
@@ -255,10 +253,9 @@ module Redmine
revisions
<<
revision
end
end
revisions
end
revisions
rescue
ScmCommandAborted
revisions
end
def
diff
(
path
,
identifier_from
,
identifier_to
=
nil
)
...
...
@@ -271,14 +268,12 @@ module Redmine
end
cmd_args
<<
"--"
<<
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
unless
path
.
empty?
diff
=
[]
scm_cmd
*
cmd_args
do
|
io
|
scm_cmd
cmd_args
do
|
io
|
io
.
each_line
do
|
line
|
diff
<<
line
end
diff
end
diff
rescue
ScmCommandAborted
nil
end
def
annotate
(
path
,
identifier
=
nil
)
...
...
@@ -286,34 +281,36 @@ module Redmine
cmd_args
=
%w|blame|
cmd_args
<<
"-p"
<<
identifier
<<
"--"
<<
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
blame
=
Annotate
.
new
content
=
nil
scm_cmd
(
*
cmd_args
)
{
|
io
|
io
.
binmode
;
content
=
io
.
read
}
# git annotates binary files
if
content
.
respond_to?
(
"is_binary_data?"
)
&&
content
.
is_binary_data?
# Ruby 1.8.x and <1.9.2
return
nil
elsif
content
.
respond_to?
(
:force_encoding
)
&&
(
content
.
dup
.
force_encoding
(
"UTF-8"
)
!=
content
.
dup
.
force_encoding
(
"BINARY"
))
# Ruby 1.9.2
# TODO: need to handle edge cases of non-binary content that isn't UTF-8
return
nil
content
=
scm_cmd
(
cmd_args
)
do
|
io
|
io
.
binmode
io
.
read
end
identifier
=
''
# git shows commit author on the first occurrence only
authors_by_commit
=
{}
content
.
split
(
"
\n
"
).
each
do
|
line
|
if
line
=~
/^([0-9a-f]{39,40})\s.*/
identifier
=
$1
elsif
line
=~
/^author (.+)/
authors_by_commit
[
identifier
]
=
$1
.
strip
elsif
line
=~
/^\t(.*)/
blame
.
add_line
(
$1
,
Revision
.
new
(
:identifier
=>
identifier
,
:author
=>
authors_by_commit
[
identifier
]))
identifier
=
''
author
=
''
if
content
# git annotates binary files
if
content
.
respond_to?
(
"is_binary_data?"
)
&&
content
.
is_binary_data?
# Ruby 1.8.x and <1.9.2
return
nil
elsif
content
.
respond_to?
(
:force_encoding
)
&&
(
content
.
dup
.
force_encoding
(
"UTF-8"
)
!=
content
.
dup
.
force_encoding
(
"BINARY"
))
# Ruby 1.9.2
# TODO: need to handle edge cases of non-binary content that isn't UTF-8
return
nil
end
identifier
=
''
# git shows commit author on the first occurrence only
authors_by_commit
=
{}
content
.
split
(
"
\n
"
).
each
do
|
line
|
if
line
=~
/^([0-9a-f]{39,40})\s.*/
identifier
=
$1
elsif
line
=~
/^author (.+)/
authors_by_commit
[
identifier
]
=
$1
.
strip
elsif
line
=~
/^\t(.*)/
blame
.
add_line
(
$1
,
Revision
.
new
(
:identifier
=>
identifier
,
:author
=>
authors_by_commit
[
identifier
]))
identifier
=
''
author
=
''
end
end
blame
end
blame
rescue
ScmCommandAborted
nil
end
def
cat
(
path
,
identifier
=
nil
)
...
...
@@ -322,14 +319,16 @@ module Redmine
end
cmd_args
=
%w|show --no-color|
cmd_args
<<
"
#{
identifier
}
:
#{
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
}
"
cat
=
nil
scm_cmd
(
*
cmd_args
)
do
|
io
|
scm_cmd
(
cmd_args
)
do
|
io
|
io
.
binmode
cat
=
io
.
read
io
.
read
end
cat
rescue
ScmCommandAborted
nil
end
def
save_entry_in_file
(
f
,
path
,
identifier
)
cmd_args
=
%w|show --no-color|
cmd_args
<<
"
#{
identifier
}
:
#{
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
}
"
scm_cmd
(
cmd_args
,
f
.
path
)
end
class
Revision
<
Redmine
::
Scm
::
Adapters
::
Revision
...
...
@@ -339,20 +338,15 @@ module Redmine
end
end
def
scm_cmd
(
*
args
,
&
block
)
repo_path
=
root_url
||
url
full_args
=
[
GIT_BIN
,
'--git-dir'
,
repo_path
]
private
# returns the string that will represent the command for shelling out
def
build_scm_cmd
(
args
)
full_args
=
[
GIT_BIN
,
'--git-dir'
,
root_url
||
url
]
if
self
.
class
.
client_version_above?
([
1
,
7
,
2
])
full_args
<<
'-c'
<<
'core.quotepath=false'
end
full_args
+=
args
ret
=
shellout
(
full_args
.
map
{
|
e
|
shell_quote
e
.
to_s
}.
join
(
' '
),
&
block
)
if
$?
&&
$?
.
exitstatus
!=
0
raise
ScmCommandAborted
,
"git exited with non-zero status:
#{
$?
.
exitstatus
}
"
end
ret
(
full_args
+
args
).
map
{
|
e
|
shell_quote
e
.
to_s
}.
join
(
' '
)
end
private
:scm_cmd
end
end
end
...
...
lib/redmine/scm/adapters/mercurial_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
require
'cgi'
module
Redmine
...
...
@@ -27,9 +27,6 @@ module Redmine
TEMPLATE_NAME
=
"hg-template"
TEMPLATE_EXTENSION
=
"tmpl"
# raised if hg command exited with error, e.g. unknown revision.
class
HgCommandAborted
<
CommandFailed
;
end
class
<<
self
def
client_command
@@bin
||=
HG_BIN
...
...
@@ -51,10 +48,7 @@ module Redmine
# The hg version is expressed either as a
# release number (eg 0.9.5 or 1.0) or as a revision
# id composed of 12 hexa characters.
theversion
=
hgversion_from_command_line
.
dup
if
theversion
.
respond_to?
(
:force_encoding
)
theversion
.
force_encoding
(
'ASCII-8BIT'
)
end
theversion
=
to_ascii
(
hgversion_from_command_line
.
dup
)
if
m
=
theversion
.
match
(
%r{
\A
(.*?)((
\d
+
\.
)+
\d
+)}
)
m
[
2
].
scan
(
%r{
\d
+}
).
collect
(
&
:to_i
)
end
...
...
@@ -114,56 +108,43 @@ module Redmine
Hash
[
*
alist
.
flatten
]
end
def
summary
return
@summary
if
@summary
hg
'rhsummary'
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
@summary
=
ActiveSupport
::
XmlMini
.
parse
(
output
)[
'rhsummary'
]
rescue
end
end
end
private
:summary
def
entries
(
path
=
nil
,
identifier
=
nil
)
p1
=
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
manifest
=
hg
(
'rhmanifest'
,
'-r'
,
CGI
.
escape
(
hgrev
(
identifier
)),
CGI
.
escape
(
without_leading_slash
(
p1
.
to_s
)))
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
ActiveSupport
::
XmlMini
.
parse
(
output
)[
'rhmanifest'
][
'repository'
][
'manifest'
]
rescue
end
cmd_args
=
[
'rhmanifest'
,
'-r'
,
CGI
.
escape
(
hgrev
(
identifier
)),
CGI
.
escape
(
without_leading_slash
(
p1
.
to_s
)
)
]
manifest
=
scm_cmd
cmd_args
do
|
io
|
output
=
to_utf8
(
io
.
read
)
ActiveSupport
::
XmlMini
.
parse
(
output
)[
'rhmanifest'
][
'repository'
][
'manifest'
]
end
path_prefix
=
path
.
blank?
?
''
:
with_trailling_slash
(
path
)
entries
=
Entries
.
new
as_ary
(
manifest
[
'dir'
]).
each
do
|
e
|
n
=
scm_iconv
(
'UTF-8'
,
@path_encoding
,
CGI
.
unescape
(
e
[
'name'
]))
p
=
"
#{
path_prefix
}#{
n
}
"
entries
<<
Entry
.
new
(
:name
=>
n
,
:path
=>
p
,
:kind
=>
'dir'
)
end
if
manifest
path_prefix
=
path
.
blank?
?
''
:
with_trailling_slash
(
path
)
as_ary
(
manifest
[
'file'
]).
each
do
|
e
|
n
=
scm_iconv
(
'UTF-8'
,
@path_encoding
,
CGI
.
unescape
(
e
[
'name'
]))
p
=
"
#{
path_prefix
}#{
n
}
"
lr
=
Revision
.
new
(
:revision
=>
e
[
'revision'
],
:scmid
=>
e
[
'node'
],
:identifier
=>
e
[
'node'
],
:time
=>
Time
.
at
(
e
[
'time'
].
to_i
))
entries
<<
Entry
.
new
(
:name
=>
n
,
:path
=>
p
,
:kind
=>
'file'
,
:size
=>
e
[
'size'
].
to_i
,
:lastrev
=>
lr
)
end
entries
=
Entries
.
new
as_ary
(
manifest
[
'dir'
]).
each
do
|
e
|
n
=
unescape
(
e
[
'name'
])
p
=
"
#{
path_prefix
}#{
n
}
"
entries
<<
Entry
.
new
(
:name
=>
n
,
:path
=>
p
,
:kind
=>
'dir'
)
end
as_ary
(
manifest
[
'file'
]).
each
do
|
e
|
n
=
unescape
(
e
[
'name'
])
p
=
"
#{
path_prefix
}#{
n
}
"
lr
=
Revision
.
new
(
:revision
=>
e
[
'revision'
],
:scmid
=>
e
[
'node'
],
:identifier
=>
e
[
'node'
],
:time
=>
Time
.
at
(
e
[
'time'
].
to_i
))
entries
<<
Entry
.
new
(
:name
=>
n
,
:path
=>
p
,
:kind
=>
'file'
,
:size
=>
e
[
'size'
].
to_i
,
:lastrev
=>
lr
)
end
entries
rescue
HgCommandAborted
nil
# means not found
entries
end
end
def
revisions
(
path
=
nil
,
identifier_from
=
nil
,
identifier_to
=
nil
,
options
=
{})
...
...
@@ -175,103 +156,100 @@ module Redmine
# Iterates the revisions by using a template file that
# makes Mercurial produce a xml output.
def
each_revision
(
path
=
nil
,
identifier_from
=
nil
,
identifier_to
=
nil
,
options
=
{})
hg_args
=
[
'log'
,
'--debug'
,
'-C'
,
'--style'
,
self
.
class
.
template_path
]
hg_args
<<
'-r'
<<
"
#{
hgrev
(
identifier_from
)
}
:
#{
hgrev
(
identifier_to
)
}
"
hg_args
<<
'--limit'
<<
options
[
:limit
]
if
options
[
:limit
]
hg_args
<<
hgtarget
(
path
)
unless
path
.
blank?
log
=
hg
(
*
hg_args
)
do
|
io
|
cmd_args
=
[
'log'
,
'--debug'
,
'-C'
,
'--style'
,
self
.
class
.
template_path
]
cmd_args
<<
'-r'
<<
"
#{
hgrev
(
identifier_from
)
}
:
#{
hgrev
(
identifier_to
)
}
"
cmd_args
<<
'--limit'
<<
options
[
:limit
]
if
options
[
:limit
]
cmd_args
<<
hgtarget
(
path
)
unless
path
.
blank?
log
=
scm_cmd
(
cmd_args
)
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
# Mercurial < 1.5 does not support footer template for '</log>'
ActiveSupport
::
XmlMini
.
parse
(
"
#{
output
}
</log>"
)[
'log'
]
rescue
end
ActiveSupport
::
XmlMini
.
parse
(
"
#{
output
}
</log>"
)[
'log'
]
end
as_ary
(
log
[
'logentry'
]).
each
do
|
le
|
cpalist
=
as_ary
(
le
[
'paths'
][
'path-copied'
]).
map
do
|
e
|
[
e
[
'__content__'
],
e
[
'copyfrom-path'
]].
map
do
|
s
|
scm_iconv
(
'UTF-8'
,
@path_encoding
,
CGI
.
unescape
(
s
))
if
log
as_ary
(
log
[
'logentry'
]).
each
do
|
le
|
cpalist
=
as_ary
(
le
[
'paths'
][
'path-copied'
]).
map
do
|
e
|
[
e
[
'__content__'
],
e
[
'copyfrom-path'
]].
map
do
|
s
|
unescape
s
end
end
cpmap
=
Hash
[
*
cpalist
.
flatten
]
paths
=
as_ary
(
le
[
'paths'
][
'path'
]).
map
do
|
e
|
p
=
unescape
e
[
'__content__'
]
{
:action
=>
e
[
'action'
],
:path
=>
with_leading_slash
(
p
),
:from_path
=>
(
cpmap
.
member?
(
p
)
?
with_leading_slash
(
cpmap
[
p
])
:
nil
),
:from_revision
=>
(
cpmap
.
member?
(
p
)
?
le
[
'revision'
]
:
nil
)}
end
.
sort
{
|
a
,
b
|
a
[
:path
]
<=>
b
[
:path
]
}
yield
Revision
.
new
(
:revision
=>
le
[
'revision'
],
:scmid
=>
le
[
'node'
],
:author
=>
(
le
[
'author'
][
'__content__'
]
rescue
''
),
:time
=>
Time
.
parse
(
le
[
'date'
][
'__content__'
]),
:message
=>
le
[
'msg'
][
'__content__'
],
:paths
=>
paths
)
end
cpmap
=
Hash
[
*
cpalist
.
flatten
]
paths
=
as_ary
(
le
[
'paths'
][
'path'
]).
map
do
|
e
|
p
=
scm_iconv
(
'UTF-8'
,
@path_encoding
,
CGI
.
unescape
(
e
[
'__content__'
])
)
{
:action
=>
e
[
'action'
],
:path
=>
with_leading_slash
(
p
),
:from_path
=>
(
cpmap
.
member?
(
p
)
?
with_leading_slash
(
cpmap
[
p
])
:
nil
),
:from_revision
=>
(
cpmap
.
member?
(
p
)
?
le
[
'revision'
]
:
nil
)}
end
.
sort
{
|
a
,
b
|
a
[
:path
]
<=>
b
[
:path
]
}
yield
Revision
.
new
(
:revision
=>
le
[
'revision'
],
:scmid
=>
le
[
'node'
],
:author
=>
(
le
[
'author'
][
'__content__'
]
rescue
''
),
:time
=>
Time
.
parse
(
le
[
'date'
][
'__content__'
]),
:message
=>
le
[
'msg'
][
'__content__'
],
:paths
=>
paths
)
self
end
self
end
# Returns list of nodes in the specified branch
def
nodes_in_branch
(
branch
,
options
=
{})
hg_args
=
[
'rhlog'
,
'--template'
,
'{node|short}\n'
,
'--rhbranch'
,
CGI
.
escape
(
branch
)]
hg_args
<<
'--from'
<<
CGI
.
escape
(
branch
)
hg_args
<<
'--to'
<<
'0'
hg_args
<<
'--limit'
<<
options
[
:limit
]
if
options
[
:limit
]
hg
(
*
hg_args
)
{
|
io
|
io
.
readlines
.
map
{
|
e
|
e
.
chomp
}
}
cmd_args
=
[
'rhlog'
,
'--template'
,
'{node|short}\n'
,
'--rhbranch'
,
CGI
.
escape
(
branch
)]
cmd_args
<<
'--from'
<<
CGI
.
escape
(
branch
)
cmd_args
<<
'--to'
<<
'0'
cmd_args
<<
'--limit'
<<
options
[
:limit
]
if
options
[
:limit
]
scm_cmd
(
cmd_args
)
do
|
io
|
io
.
readlines
.
map
{
|
e
|
e
.
chomp
}
end
end
def
diff
(
path
,
identifier_from
,
identifier_to
=
nil
)
hg
_args
=
%w|rhdiff|
cmd
_args
=
%w|rhdiff|
if
identifier_to
hg
_args
<<
'-r'
<<
hgrev
(
identifier_to
)
<<
'-r'
<<
hgrev
(
identifier_from
)
cmd
_args
<<
'-r'
<<
hgrev
(
identifier_to
)
<<
'-r'
<<
hgrev
(
identifier_from
)
else
hg
_args
<<
'-c'
<<
hgrev
(
identifier_from
)
cmd
_args
<<
'-c'
<<
hgrev
(
identifier_from
)
end
unless
path
.
blank?
p
=
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
)
hg
_args
<<
CGI
.
escape
(
hgtarget
(
p
))
cmd
_args
<<
CGI
.
escape
(
hgtarget
(
p
))
end
diff
=
[]
hg
*
hg_args
do
|
io
|
io
.
each_line
do
|
line
|
diff
<<
line
end
scm_cmd
cmd_args
do
|
io
|
diff
=
[]
io
.
each_line
{
|
line
|
diff
<<
line
}
diff
end
diff
rescue
HgCommandAborted
nil
# means not found
end
def
cat
(
path
,
identifier
=
nil
)
p
=
CGI
.
escape
(
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
))
hg
'rhcat'
,
'-r'
,
CGI
.
escape
(
hgrev
(
identifier
)),
hgtarget
(
p
)
do
|
io
|
p
=
escape
(
path
)
cmd_args
=
[
'rhcat'
,
'-r'
,
CGI
.
escape
(
hgrev
(
identifier
)),
hgtarget
(
p
)]
scm_cmd
cmd_args
do
|
io
|
io
.
binmode
io
.
read
end
rescue
HgCommandAborted
nil
# means not found
end
def
annotate
(
path
,
identifier
=
nil
)
p
=
CGI
.
escape
(
scm_iconv
(
@path_encoding
,
'UTF-8'
,
path
))
blame
=
Annotate
.
new
hg
'rhannotate'
,
'-ncu'
,
'-r'
,
CGI
.
escape
(
hgrev
(
identifier
)),
hgtarget
(
p
)
do
|
io
|
p
=
escape
(
path
)
cmd_args
=
[
'rhannotate'
,
'-ncu'
,
'-r'
,
CGI
.
escape
(
hgrev
(
identifier
)),
hgtarget
(
p
)]
scm_cmd
cmd_args
do
|
io
|
blame
=
Annotate
.
new
io
.
each_line
do
|
line
|
line
.
force_encoding
(
'ASCII-8BIT'
)
if
line
.
respond_to?
(
:force_encoding
)
to_ascii
(
line
)
next
unless
line
=~
%r{^([^:]+)
\s
(
\d
+)
\s
([0-9a-f]+):
\s
(.*)$}
r
=
Revision
.
new
(
:author
=>
$1
.
strip
,
:revision
=>
$2
,
:scmid
=>
$3
,
:identifier
=>
$3
)
blame
.
add_line
(
$4
.
rstrip
,
r
)
end
blame
end
blame
rescue
HgCommandAborted
nil
# means not found or cannot be annotated
end
class
Revision
<
Redmine
::
Scm
::
Adapters
::
Revision
...
...
@@ -281,20 +259,16 @@ module Redmine
end
end
# Runs 'hg' command with the given args
def
hg
(
*
args
,
&
block
)
private
def
build_scm_cmd
(
args
)
repo_path
=
root_url
||
url
full_args
=
[
HG_BIN
,
'-R'
,
repo_path
,
'--encoding'
,
'utf-8'
]
full_args
<<
'--config'
<<
"extensions.redminehelper=
#{
HG_HELPER_EXT
}
"
full_args
<<
'--config'
<<
'diff.git=false'
full_args
+=
args
ret
=
shellout
(
full_args
.
map
{
|
e
|
shell_quote
e
.
to_s
}.
join
(
' '
),
&
block
)
if
$?
&&
$?
.
exitstatus
!=
0
raise
HgCommandAborted
,
"hg exited with non-zero status:
#{
$?
.
exitstatus
}
"
end
ret
full_args
.
map
{
|
e
|
shell_quote
e
.
to_s
}.
join
(
' '
)
end
private
:hg
# Returns correct revision identifier
def
hgrev
(
identifier
,
sq
=
false
)
...
...
@@ -302,19 +276,47 @@ module Redmine
rev
=
shell_quote
(
rev
)
if
sq
rev
end
private
:hgrev
def
hgtarget
(
path
)
path
||=
''
root_url
+
'/'
+
without_leading_slash
(
path
)
end
private
:hgtarget
def
as_ary
(
o
)
return
[]
unless
o
o
.
is_a?
(
Array
)
?
o
:
Array
[
o
]
end
private
:as_ary
def
summary
return
@summary
if
@summary
scm_cmd
[
'rhsummary'
]
do
|
io
|
output
=
to_utf8
(
io
.
read
)
@summary
=
ActiveSupport
::
XmlMini
.
parse
(
output
)[
'rhsummary'
]
end
end
def
to_utf8
(
str
)
str
.
force_encoding
(
'UTF-8'
)
if
str
.
respond_to?
(
:force_encoding
)
str
end
def
self
.
to_ascii
(
str
)
str
.
force_encoding
(
'ASCII-8BIT'
)
if
str
.
respond_to?
(
:force_encoding
)
str
end
def
to_ascii
(
str
)
self
.
class
.
to_ascii
(
str
)
end
def
escape
(
str
)
CGI
.
escape
(
scm_iconv
(
@path_encoding
,
'UTF-8'
,
str
))
end
def
unescape
(
str
)
scm_iconv
(
'UTF-8'
,
@path_encoding
,
CGI
.
unescape
(
str
))
end
end
end
end
...
...
lib/redmine/scm/adapters/subversion_adapter.rb
View file @
21ae869d
...
...
@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require
'redmine/scm/adapters/abstract_adapter'
require
_dependency
'redmine/scm/adapters/abstract_adapter'
require
'uri'
module
Redmine
...
...
@@ -57,194 +57,178 @@ module Redmine
# Get info about the svn repository
def
info
cmd
=
"
#{
self
.
class
.
sq_bin
}
info --xml
#{
target
}
"
cmd
<<
credentials_string
info
=
nil
shellout
(
cmd
)
do
|
io
|
cmd_args
=
[
'info'
,
'--xml'
,
target
,
credentials_string
]
scm_cmd
(
cmd_args
)
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
#root_url = doc.elements["info/entry/repository/root"].text
info
=
Info
.
new
({
:root_url
=>
doc
[
'info'
][
'entry'
][
'repository'
][
'root'
][
'__content__'
],
:lastrev
=>
Revision
.
new
({
:identifier
=>
doc
[
'info'
][
'entry'
][
'commit'
][
'revision'
],
:time
=>
Time
.
parse
(
doc
[
'info'
][
'entry'
][
'commit'
][
'date'
][
'__content__'
]).
localtime
,
:author
=>
(
doc
[
'info'
][
'entry'
][
'commit'
][
'author'
]
?
doc
[
'info'
][
'entry'
][
'commit'
][
'author'
][
'__content__'
]
:
""
)
})
})
rescue
end
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
#root_url = doc.elements["info/entry/repository/root"].text
Info
.
new
({
:root_url
=>
doc
[
'info'
][
'entry'
][
'repository'
][
'root'
][
'__content__'
],
:lastrev
=>
Revision
.
new
({
:identifier
=>
doc
[
'info'
][
'entry'
][
'commit'
][
'revision'
],
:time
=>
Time
.
parse
(
doc
[
'info'
][
'entry'
][
'commit'
][
'date'
][
'__content__'
]).
localtime
,
:author
=>
(
doc
[
'info'
][
'entry'
][
'commit'
][
'author'
]
?
doc
[
'info'
][
'entry'
][
'commit'
][
'author'
][
'__content__'
]
:
""
)
})
})
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
info
rescue
CommandFailed
return
nil
end
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def
entries
(
path
=
nil
,
identifier
=
nil
)
path
||=
''
identifier
=
(
identifier
and
identifier
.
to_i
>
0
)
?
identifier
.
to_i
:
"HEAD"
identifier
=
initialize_identifier
(
identifier
)
entries
=
Entries
.
new
cmd
=
"
#{
self
.
class
.
sq_bin
}
list --xml
#{
target
(
path
)
}
@
#{
identifier
}
"
cmd
<<
credentials_string
shellout
(
cmd
)
do
|
io
|
cmd_args
=
[
'list'
,
'--xml'
,
"
#{
target
(
path
)
}
@
#{
identifier
}
"
,
credentials_string
]
scm_cmd
(
cmd_args
)
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
each_xml_element
(
doc
[
'lists'
][
'list'
],
'entry'
)
do
|
entry
|
commit
=
entry
[
'commit'
]
commit_date
=
commit
[
'date'
]
# Skip directory if there is no commit date (usually that
# means that we don't have read access to it)
next
if
entry
[
'kind'
]
==
'dir'
&&
commit_date
.
nil?
name
=
entry
[
'name'
][
'__content__'
]
entries
<<
Entry
.
new
({
:name
=>
URI
.
unescape
(
name
),
:path
=>
((
path
.
empty?
?
""
:
"
#{
path
}
/"
)
+
name
),
:kind
=>
entry
[
'kind'
],
:size
=>
((
s
=
entry
[
'size'
])
?
s
[
'__content__'
].
to_i
:
nil
),
:lastrev
=>
Revision
.
new
({
:identifier
=>
commit
[
'revision'
],
:time
=>
Time
.
parse
(
commit_date
[
'__content__'
].
to_s
).
localtime
,
:author
=>
((
a
=
commit
[
'author'
])
?
a
[
'__content__'
]
:
nil
)
})
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
each_xml_element
(
doc
[
'lists'
][
'list'
],
'entry'
)
do
|
entry
|
commit
=
entry
[
'commit'
]
commit_date
=
commit
[
'date'
]
# Skip directory if there is no commit date (usually that
# means that we don't have read access to it)
next
if
entry
[
'kind'
]
==
'dir'
&&
commit_date
.
nil?
name
=
entry
[
'name'
][
'__content__'
]
entries
<<
Entry
.
new
({
:name
=>
URI
.
unescape
(
name
),
:path
=>
((
path
.
empty?
?
""
:
"
#{
path
}
/"
)
+
name
),
:kind
=>
entry
[
'kind'
],
:size
=>
((
s
=
entry
[
'size'
])
?
s
[
'__content__'
].
to_i
:
nil
),
:lastrev
=>
Revision
.
new
({
:identifier
=>
commit
[
'revision'
],
:time
=>
Time
.
parse
(
commit_date
[
'__content__'
].
to_s
).
localtime
,
:author
=>
((
a
=
commit
[
'author'
])
?
a
[
'__content__'
]
:
nil
)
})
end
rescue
Exception
=>
e
logger
.
error
(
"Error parsing svn output:
#{
e
.
message
}
"
)
logger
.
error
(
"Output was:
\n
#{
output
}
"
)
})
end
logger
.
debug
(
"Found
#{
entries
.
size
}
entries in the repository for
#{
target
(
path
)
}
"
)
if
logger
&&
logger
.
debug?
entries
.
sort_by_name
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
logger
.
debug
(
"Found
#{
entries
.
size
}
entries in the repository for
#{
target
(
path
)
}
"
)
if
logger
&&
logger
.
debug?
entries
.
sort_by_name
end
def
properties
(
path
,
identifier
=
nil
)
# proplist xml output supported in svn 1.5.0 and higher
return
nil
unless
self
.
class
.
client_version_above?
([
1
,
5
,
0
])
identifier
=
(
identifier
and
identifier
.
to_i
>
0
)
?
identifier
.
to_i
:
"HEAD"
cmd
=
"
#{
self
.
class
.
sq_bin
}
proplist --verbose --xml
#{
target
(
path
)
}
@
#{
identifier
}
"
cmd
<<
credentials_string
identifier
=
initialize_identifier
(
identifier
)
cmd_args
=
[
'proplist'
,
'--verbose'
,
'--xml'
,
"
#{
target
(
path
)
}
@
#{
identifier
}
"
,
credentials_string
]
properties
=
{}
s
hellout
(
cmd
)
do
|
io
|
s
cm_cmd
(
cmd_args
)
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
each_xml_element
(
doc
[
'properties'
][
'target'
],
'property'
)
do
|
property
|
properties
[
property
[
'name'
]
]
=
property
[
'__content__'
].
to_s
end
rescue
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
each_xml_element
(
doc
[
'properties'
][
'target'
],
'property'
)
do
|
property
|
properties
[
property
[
'name'
]
]
=
property
[
'__content__'
].
to_s
end
properties
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
properties
end
def
revisions
(
path
=
nil
,
identifier_from
=
nil
,
identifier_to
=
nil
,
options
=
{})
path
||=
''
identifier_from
=
(
identifier_from
&&
identifier_from
.
to_i
>
0
)
?
identifier_from
.
to_i
:
"HEAD"
identifier_to
=
(
identifier_to
&&
identifier_to
.
to_i
>
0
)
?
identifier_to
.
to_i
:
1
identifier_from
=
initialize_identifier
(
identifier_from
)
identifier_to
=
initialize_identifier
(
identifier_to
,
1
)
cmd_args
=
[
'log'
,
'--xml'
,
'-r'
,
"
#{
identifier_from
}
:
#{
identifier_to
}
"
,
credentials_string
]
cmd_args
<<
" --verbose "
if
options
[
:with_paths
]
cmd_args
<<
" --limit
#{
options
[
:limit
].
to_i
}
"
if
options
[
:limit
]
cmd_args
<<
target
(
path
)
revisions
=
Revisions
.
new
cmd
=
"
#{
self
.
class
.
sq_bin
}
log --xml -r
#{
identifier_from
}
:
#{
identifier_to
}
"
cmd
<<
credentials_string
cmd
<<
" --verbose "
if
options
[
:with_paths
]
cmd
<<
" --limit
#{
options
[
:limit
].
to_i
}
"
if
options
[
:limit
]
cmd
<<
' '
+
target
(
path
)
shellout
(
cmd
)
do
|
io
|
scm_cmd
(
cmd_args
)
do
|
io
|
output
=
io
.
read
if
output
.
respond_to?
(
:force_encoding
)
output
.
force_encoding
(
'UTF-8'
)
end
begin
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
each_xml_element
(
doc
[
'log'
],
'logentry'
)
do
|
logentry
|
paths
=
[]
doc
=
ActiveSupport
::
XmlMini
.
parse
(
output
)
each_xml_element
(
doc
[
'log'
],
'logentry'
)
do
|
logentry
|
paths
=
[]
if
logentry
[
'paths'
]
&&
logentry
[
'paths'
][
'path'
]
each_xml_element
(
logentry
[
'paths'
],
'path'
)
do
|
path
|
paths
<<
{
:action
=>
path
[
'action'
],
:path
=>
path
[
'__content__'
],
:from_path
=>
path
[
'copyfrom-path'
],
:from_revision
=>
path
[
'copyfrom-rev'
]
}
end
if
logentry
[
'paths'
]
&&
logentry
[
'paths'
][
'path'
]
paths
.
sort!
{
|
x
,
y
|
x
[
:path
]
<=>
y
[
:path
]
}
revisions
<<
Revision
.
new
({
:identifier
=>
logentry
[
'revision'
],
:author
=>
(
logentry
[
'author'
]
?
logentry
[
'author'
][
'__content__'
]
:
""
),
:time
=>
Time
.
parse
(
logentry
[
'date'
][
'__content__'
].
to_s
).
localtime
,
:message
=>
logentry
[
'msg'
][
'__content__'
],
:paths
=>
paths
})
end
end
rescue
paths
.
sort!
{
|
x
,
y
|
x
[
:path
]
<=>
y
[
:path
]
}
revisions
<<
Revision
.
new
({
:identifier
=>
logentry
[
'revision'
],
:author
=>
(
logentry
[
'author'
]
?
logentry
[
'author'
][
'__content__'
]
:
""
),
:time
=>
Time
.
parse
(
logentry
[
'date'
][
'__content__'
].
to_s
).
localtime
,
:message
=>
logentry
[
'msg'
][
'__content__'
],
:paths
=>
paths
})
end
revisions
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
revisions
end
def
diff
(
path
,
identifier_from
,
identifier_to
=
nil
,
type
=
"inline"
)
path
||=
''
identifier_from
=
(
identifier_from
and
identifier_from
.
to_i
>
0
)
?
identifier_from
.
to_i
:
''
identifier_to
=
(
identifier_to
and
identifier_to
.
to_i
>
0
)
?
identifier_to
.
to_i
:
(
identifier_from
.
to_i
-
1
)
identifier_from
=
initialize_identifier
(
identifier_from
,
''
)
identifier_to
=
initialize_identifier
(
identifier_to
,
identifier_from
.
to_i
-
1
)
cmd
=
"
#{
self
.
class
.
sq_bin
}
diff -r "
cmd
<<
"
#{
identifier_to
}
:"
cmd
<<
"
#{
identifier_from
}
"
cmd
<<
"
#{
target
(
path
)
}
@
#{
identifier_from
}
"
cmd
<<
credentials_string
cmd_args
=
[
"diff -r"
,
"
#{
identifier_to
}
:
#{
identifier_from
}
"
,
"
#{
target
(
path
)
}
@
#{
identifier_from
}
"
,
credentials_string
]
diff
=
[]
s
hellout
(
cmd
)
do
|
io
|
s
cm_cmd
(
cmd_args
)
do
|
io
|
io
.
each_line
do
|
line
|
diff
<<
line
end
diff
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
diff
end
def
cat
(
path
,
identifier
=
nil
)
identifier
=
(
identifier
and
identifier
.
to_i
>
0
)
?
identifier
.
to_i
:
"HEAD"
cmd
=
"
#{
self
.
class
.
sq_bin
}
cat
#{
target
(
path
)
}
@
#{
identifier
}
"
cmd
<<
credentials_string
cat
=
nil
shellout
(
cmd
)
do
|
io
|
identifier
=
initialize_identifier
(
identifier
)
cmd_args
=
[
'cat'
,
"
#{
target
(
path
)
}
@
#{
identifier
}
"
,
credentials_string
]
scm_cmd
(
cmd_args
)
do
|
io
|
io
.
binmode
cat
=
io
.
read
io
.
read
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
cat
end
def
save_entry_in_file
(
f
,
path
,
identifier
)
identifier
=
initialize_identifier
(
identifier
)
cmd_args
=
[
'cat'
,
"
#{
target
(
path
)
}
@
#{
identifier
}
"
,
credentials_string
]
scm_cmd
(
cmd_args
,
f
.
path
)
end
def
annotate
(
path
,
identifier
=
nil
)
identifier
=
(
identifier
and
identifier
.
to_i
>
0
)
?
identifier
.
to_i
:
"HEAD"
cmd
=
"
#{
self
.
class
.
sq_bin
}
blame
#{
target
(
path
)
}
@
#{
identifier
}
"
cmd
<<
credentials_string
identifier
=
initialize_identifier
(
identifier
)
cmd_args
=
[
'blame'
,
"
#{
target
(
path
)
}
@
#{
identifier
}
"
,
credentials_string
]
blame
=
Annotate
.
new
s
hellout
(
cmd
)
do
|
io
|
s
cm_cmd
(
cmd_args
)
do
|
io
|
io
.
each_line
do
|
line
|
next
unless
line
=~
%r{^
\s
*(
\d
+)
\s
*(
\S
+)
\s
(.*)$}
blame
.
add_line
(
$3
.
rstrip
,
Revision
.
new
(
:identifier
=>
$1
.
to_i
,
:author
=>
$2
.
strip
))
end
blame
end
return
nil
if
$?
&&
$?
.
exitstatus
!=
0
blame
end
private
def
initialize_identifier
(
identifier
,
default
=
"HEAD"
)
(
identifier
&&
identifier
.
to_i
>
0
)
?
identifier
.
to_i
:
default
end
def
credentials_string
str
=
''
str
<<
" --username
#{
shell_quote
(
@login
)
}
"
unless
@login
.
blank?
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment