1
1
//! Sync git API for fetching a file blame
2
2
3
3
use super :: { utils, CommitId , RepoPath } ;
4
- use crate :: {
5
- error:: { Error , Result } ,
6
- sync:: { get_commits_info, repository:: repo} ,
7
- } ;
8
- use git2:: BlameOptions ;
4
+ use crate :: { error:: Result , sync:: get_commits_info} ;
9
5
use scopetime:: scope_time;
10
6
use std:: collections:: { HashMap , HashSet } ;
11
- use std:: io:: { BufRead , BufReader } ;
12
- use std:: path:: Path ;
13
7
14
8
/// A `BlameHunk` contains all the information that will be shown to the user.
15
9
#[ derive( Clone , Hash , Debug , PartialEq , Eq ) ]
@@ -40,19 +34,6 @@ pub struct FileBlame {
40
34
pub lines : Vec < ( Option < BlameHunk > , String ) > ,
41
35
}
42
36
43
- /// fixup `\` windows path separators to git compatible `/`
44
- fn fixup_windows_path ( path : & str ) -> String {
45
- #[ cfg( windows) ]
46
- {
47
- path. replace ( '\\' , "/" )
48
- }
49
-
50
- #[ cfg( not( windows) ) ]
51
- {
52
- path. to_string ( )
53
- }
54
- }
55
-
56
37
///
57
38
pub fn blame_file (
58
39
repo_path : & RepoPath ,
@@ -61,35 +42,52 @@ pub fn blame_file(
61
42
) -> Result < FileBlame > {
62
43
scope_time ! ( "blame_file" ) ;
63
44
64
- let repo = repo ( repo_path) ?;
45
+ let repo: gix:: Repository =
46
+ gix:: ThreadSafeRepository :: discover_with_environment_overrides ( repo_path. gitpath ( ) )
47
+ . map ( Into :: into) ?;
48
+ let tip: gix:: ObjectId = match commit_id {
49
+ Some ( commit_id) => gix:: ObjectId :: from_bytes_or_panic (
50
+ commit_id. get_oid ( ) . as_bytes ( ) ,
51
+ ) ,
52
+ _ => repo. head ( ) ?. peel_to_commit_in_place ( ) ?. id ,
53
+ } ;
54
+ let traverse = gix:: traverse:: commit:: topo:: Builder :: from_iters (
55
+ & repo. objects ,
56
+ [ tip] ,
57
+ None :: < Vec < gix:: ObjectId > > ,
58
+ )
59
+ . build ( )
60
+ . expect ( "TODO" ) ;
61
+
62
+ let mut resource_cache =
63
+ repo. diff_resource_cache_for_tree_diff ( ) . expect ( "TODO" ) ;
64
+
65
+ let outcome = gix_blame:: file (
66
+ & repo. objects ,
67
+ traverse,
68
+ & mut resource_cache,
69
+ file_path. into ( ) ,
70
+ None ,
71
+ )
72
+ . expect ( "TODO" ) ;
65
73
66
74
let commit_id = if let Some ( commit_id) = commit_id {
67
75
commit_id
68
76
} else {
77
+ let repo = crate :: sync:: repo ( repo_path) ?;
78
+
69
79
utils:: get_head_repo ( & repo) ?
70
80
} ;
71
81
72
- let spec =
73
- format ! ( "{}:{}" , commit_id, fixup_windows_path( file_path) ) ;
74
-
75
- let object = repo. revparse_single ( & spec) ?;
76
- let blob = repo. find_blob ( object. id ( ) ) ?;
77
-
78
- if blob. is_binary ( ) {
79
- return Err ( Error :: NoBlameOnBinaryFile ) ;
80
- }
81
-
82
- let mut opts = BlameOptions :: new ( ) ;
83
- opts. newest_commit ( commit_id. into ( ) ) ;
84
-
85
- let blame =
86
- repo. blame_file ( Path :: new ( file_path) , Some ( & mut opts) ) ?;
87
-
88
- let reader = BufReader :: new ( blob. content ( ) ) ;
89
-
90
- let unique_commit_ids: HashSet < _ > = blame
82
+ let unique_commit_ids: HashSet < _ > = outcome
83
+ . entries
91
84
. iter ( )
92
- . map ( |hunk| CommitId :: new ( hunk. final_commit_id ( ) ) )
85
+ . map ( |entry| {
86
+ CommitId :: new (
87
+ git2:: Oid :: from_bytes ( entry. commit_id . as_bytes ( ) )
88
+ . expect ( "TODO" ) ,
89
+ )
90
+ } )
93
91
. collect ( ) ;
94
92
let mut commit_ids = Vec :: with_capacity ( unique_commit_ids. len ( ) ) ;
95
93
commit_ids. extend ( unique_commit_ids) ;
@@ -100,40 +98,45 @@ pub fn blame_file(
100
98
. map ( |commit_info| ( commit_info. id , commit_info) )
101
99
. collect ( ) ;
102
100
103
- let lines: Vec < ( Option < BlameHunk > , String ) > = reader
104
- . lines ( )
105
- . enumerate ( )
106
- . map ( |( i, line) | {
107
- // Line indices in a `FileBlame` are 1-based.
108
- let corresponding_hunk = blame. get_line ( i + 1 ) ;
109
-
110
- if let Some ( hunk) = corresponding_hunk {
111
- let commit_id = CommitId :: new ( hunk. final_commit_id ( ) ) ;
112
- // Line indices in a `BlameHunk` are 1-based.
113
- let start_line =
114
- hunk. final_start_line ( ) . saturating_sub ( 1 ) ;
115
- let end_line =
116
- start_line. saturating_add ( hunk. lines_in_hunk ( ) ) ;
117
-
118
- if let Some ( commit_info) =
119
- unique_commit_infos. get ( & commit_id)
120
- {
121
- let hunk = BlameHunk {
122
- commit_id,
123
- author : commit_info. author . clone ( ) ,
124
- time : commit_info. time ,
125
- start_line,
126
- end_line,
101
+ // TODO
102
+ // The shape of data as returned by `entries_with_lines` is preferable to the one chosen here
103
+ // because the former is much closer to what the UI is going to need in the end.
104
+ let lines: Vec < ( Option < BlameHunk > , String ) > = outcome
105
+ . entries_with_lines ( )
106
+ . flat_map ( |( entry, lines) | {
107
+ let commit_id = CommitId :: new (
108
+ git2:: Oid :: from_bytes ( entry. commit_id . as_bytes ( ) )
109
+ . expect ( "TODO" ) ,
110
+ ) ;
111
+ let start_in_blamed_file =
112
+ entry. start_in_blamed_file as usize ;
113
+
114
+ lines
115
+ . iter ( )
116
+ . enumerate ( )
117
+ . map ( |( i, line) | {
118
+ // TODO
119
+ let trimmed_line =
120
+ line. to_string ( ) . trim_end ( ) . to_string ( ) ;
121
+
122
+ if let Some ( commit_info) =
123
+ unique_commit_infos. get ( & commit_id)
124
+ {
125
+ return (
126
+ Some ( BlameHunk {
127
+ commit_id,
128
+ author : commit_info. author . clone ( ) ,
129
+ time : commit_info. time ,
130
+ start_line : start_in_blamed_file + i,
131
+ end_line : start_in_blamed_file + i,
132
+ } ) ,
133
+ trimmed_line,
134
+ ) ;
127
135
} ;
128
136
129
- return (
130
- Some ( hunk) ,
131
- line. unwrap_or_else ( |_| String :: new ( ) ) ,
132
- ) ;
133
- }
134
- }
135
-
136
- ( None , line. unwrap_or_else ( |_| String :: new ( ) ) )
137
+ ( None , trimmed_line)
138
+ } )
139
+ . collect :: < Vec < _ > > ( )
137
140
} )
138
141
. collect ( ) ;
139
142
0 commit comments