Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
49620359
Commit
49620359
authored
Mar 17, 2018
by
Nika Layzell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mailmap: Clean up mailmap parser, and finish API
parent
7a169390
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
377 additions
and
173 deletions
+377
-173
include/git2/mailmap.h
+103
-16
src/mailmap.c
+274
-157
No files found.
include/git2/mailmap.h
View file @
49620359
...
...
@@ -4,16 +4,16 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_mailmap_h__
#define INCLUDE_mailmap_h__
#ifndef INCLUDE_
git_
mailmap_h__
#define INCLUDE_
git_
mailmap_h__
#include "common.h"
#include "
repository
.h"
#include "
tree
.h"
/**
* @file git2/mailmap.h
* @brief Mailmap
access subroutines.
* @defgroup git_
rebase Git merge
routines
* @brief Mailmap
parsing routines
* @defgroup git_
mailmap Git mailmap
routines
* @ingroup Git
* @{
*/
...
...
@@ -21,19 +21,106 @@ GIT_BEGIN_DECL
typedef
struct
git_mailmap
git_mailmap
;
struct
git_mailmap_entry
{
const
char
*
name
;
const
char
*
email
;
};
/**
* A single entry parsed from a mailmap.
*/
typedef
struct
git_mailmap_entry
{
const
char
*
real_name
;
/**< the real name (may be NULL) */
const
char
*
real_email
;
/**< the real email (may be NULL) */
const
char
*
replace_name
;
/**< the name to replace (may be NULL) */
const
char
*
replace_email
;
/**< the email to replace */
}
git_mailmap_entry
;
/**
* Create a mailmap object by parsing a mailmap file.
*
* The mailmap must be freed with 'git_mailmap_free'.
*
* @param out Pointer to store the mailmap
* @param data raw data buffer to parse
* @param size size of the raw data buffer
* @return 0 on success
*/
GIT_EXTERN
(
int
)
git_mailmap_parse
(
git_mailmap
**
out
,
const
char
*
data
,
size_t
size
);
/**
* Create a mailmap object by parsing the ".mailmap" file in the tree root.
*
* The mailmap must be freed with 'git_mailmap_free'.
*
* @param out pointer to store the mailmap
* @param treeish root object that can be peeled to a tree
* @return 0 on success; GIT_ENOTFOUND if .mailmap does not exist.
*/
GIT_EXTERN
(
int
)
git_mailmap_from_tree
(
git_mailmap
**
out
,
const
git_object
*
treeish
);
GIT_EXTERN
(
int
)
git_mailmap_create
(
git_mailmap
**
,
git_repository
*
);
GIT_EXTERN
(
void
)
git_mailmap_free
(
git_mailmap
*
);
GIT_EXTERN
(
struct
git_mailmap_entry
)
git_mailmap_lookup
(
git_mailmap
*
map
,
const
char
*
name
,
const
char
*
email
);
/**
* Create a mailmap object by parsing the ".mailmap" file in the repository's
* HEAD's tree root.
*
* The mailmap must be freed with 'git_mailmap_free'.
*
* @param out pointer to store the mailmap
* @param repo repository to find the .mailmap in
* @return 0 on success; GIT_ENOTFOUND if .mailmap does not exist.
*/
GIT_EXTERN
(
int
)
git_mailmap_from_repo
(
git_mailmap
**
out
,
git_repository
*
repo
);
/**
* Free a mailmap created by 'git_mailmap_parse', 'git_mailmap_from_tree' or
* 'git_mailmap_from_repo'.
*/
GIT_EXTERN
(
void
)
git_mailmap_free
(
git_mailmap
*
mailmap
);
/**
* Resolve a name and email to the corresponding real name and email.
*
* @param name_out either 'name', or the real name to use.
* You should NOT free this value.
* @param email_out either 'email' or the real email to use,
* You should NOT free this value.
* @param mailmap the mailmap to perform the lookup in.
* @param name the name to resolve.
* @param email the email to resolve.
*/
GIT_EXTERN
(
void
)
git_mailmap_resolve
(
const
char
**
name_out
,
const
char
**
email_out
,
git_mailmap
*
mailmap
,
const
char
*
name
,
const
char
*
email
);
/**
* Get the number of mailmap entries.
*/
GIT_EXTERN
(
size_t
)
git_mailmap_entry_count
(
git_mailmap
*
mailmap
);
/**
* Lookup a mailmap entry by index.
*
* Do not free the mailmap entry, it is owned by the mailmap.
*/
GIT_EXTERN
(
git_mailmap_entry
*
)
git_mailmap_entry_byindex
(
git_mailmap
*
mailmap
,
size_t
idx
);
/**
* Lookup a mailmap entry by name/email pair.
*
* Do not free the mailmap entry, it is owned by the mailmap.
*/
GIT_EXTERN
(
git_mailmap_entry
*
)
git_mailmap_entry_lookup
(
git_mailmap
*
mailmap
,
const
char
*
name
,
const
char
*
email
);
/** @} */
GIT_END_DECL
#endif
src/mailmap.c
View file @
49620359
...
...
@@ -14,195 +14,312 @@
#include "git2/revparse.h"
#include "git2/sys/commit.h"
struct
mailmap_entry
{
char
*
to_name
;
char
*
to_email
;
char
*
from_name
;
char
*
from_email
;
};
/**
* Helper type and methods for the mailmap parser
*/
typedef
struct
char_range
{
const
char
*
p
;
size_t
len
;
}
char_range
;
static
const
char_range
NULL_RANGE
=
{
0
};
/* Split a range at the first instance of 'c'. Returns whether 'c' was found */
static
bool
range_split
(
char_range
range
,
char
c
,
char_range
*
before
,
char_range
*
after
)
{
const
char
*
off
;
*
before
=
*
after
=
NULL_RANGE
;
before
->
p
=
range
.
p
;
off
=
memchr
(
range
.
p
,
c
,
range
.
len
);
if
(
!
off
)
{
before
->
len
=
range
.
len
;
return
false
;
}
before
->
len
=
off
-
range
.
p
;
after
->
p
=
off
+
1
;
after
->
len
=
(
range
.
p
+
range
.
len
)
-
after
->
p
;
return
true
;
}
/* Trim whitespace from the beginning and end of the range */
static
void
range_trim
(
char_range
*
range
)
{
while
(
range
->
len
>
0
&&
git__isspace
(
range
->
p
[
0
]))
{
++
range
->
p
;
--
range
->
len
;
}
while
(
range
->
len
>
0
&&
git__isspace
(
range
->
p
[
range
->
len
-
1
]))
--
range
->
len
;
}
/**
* If `buf` is not NULL, copies range into it with a '\0', and bumps buf.
* If `size` is not NULL, adds the number of bytes to be written to it.
* returns a pointer to the copied string, or NULL.
*/
static
const
char
*
range_copyz
(
char
**
buf
,
size_t
*
size
,
char_range
src
)
{
char
*
s
=
NULL
;
if
(
src
.
p
==
NULL
)
return
NULL
;
if
(
size
)
*
size
+=
src
.
len
+
1
;
if
(
buf
)
{
s
=
*
buf
;
memcpy
(
s
,
src
.
p
,
src
.
len
);
s
[
src
.
len
]
=
'\0'
;
*
buf
+=
src
.
len
+
1
;
}
return
s
;
}
struct
git_mailmap
{
git_vector
lin
es
;
git_vector
entri
es
;
};
// Returns -1 on failure, length of the string scanned successfully on success,
// guaranteed to be less that `length`.
ssize_t
parse_name_and_email
(
const
char
*
line
,
size_t
length
,
const
char
**
name
,
size_t
*
name_len
,
const
char
**
email
,
size_t
*
email_len
,
bool
allow_empty_email
)
/**
* Parse a single entry out of a mailmap file.
* Advances the `file` range past the parsed entry.
*/
static
int
git_mailmap_parse_single
(
char_range
*
file
,
bool
*
found
,
char_range
*
real_name
,
char_range
*
real_email
,
char_range
*
replace_name
,
char_range
*
replace_email
)
{
const
char
*
email_start
;
const
char
*
email_end
;
const
char
*
name_start
;
const
char
*
name_end
;
char_range
line
,
comment
,
name_a
,
email_a
,
name_b
,
email_b
;
bool
two_emails
=
false
;
*
found
=
false
;
*
real_name
=
NULL_RANGE
;
*
real_email
=
NULL_RANGE
;
*
replace_name
=
NULL_RANGE
;
*
replace_email
=
NULL_RANGE
;
while
(
file
->
len
)
{
/* Get the line, and remove any comments */
range_split
(
*
file
,
'\n'
,
&
line
,
file
);
range_split
(
line
,
'#'
,
&
line
,
&
comment
);
/* Skip blank lines */
range_trim
(
&
line
);
if
(
line
.
len
==
0
)
continue
;
email_start
=
memchr
(
line
,
'<'
,
length
);
if
(
!
email_start
)
return
-
1
;
email_end
=
memchr
(
email_start
,
'>'
,
length
-
(
email_start
-
line
));
if
(
!
email_end
)
return
-
1
;
assert
(
email_end
>
email_start
);
/* Get the first name and email */
if
(
!
range_split
(
line
,
'<'
,
&
name_a
,
&
line
))
return
-
1
;
/* garbage in line */
if
(
!
range_split
(
line
,
'>'
,
&
email_a
,
&
line
))
return
-
1
;
/* unfinished <> pair */
/* Get an optional second name and/or email */
two_emails
=
range_split
(
line
,
'<'
,
&
name_b
,
&
line
);
if
(
two_emails
&&
!
range_split
(
line
,
'>'
,
&
email_b
,
&
line
))
return
-
1
;
/* unfinished <> pair */
if
(
line
.
len
>
0
)
return
-
1
;
/* junk at end of line */
/* Trim whitespace from around names */
range_trim
(
&
name_a
);
range_trim
(
&
name_b
);
*
found
=
true
;
if
(
name_a
.
len
>
0
)
*
real_name
=
name_a
;
if
(
two_emails
)
{
*
real_email
=
email_a
;
*
replace_email
=
email_b
;
if
(
name_b
.
len
>
0
)
*
replace_name
=
name_b
;
}
else
{
*
replace_email
=
email_a
;
}
break
;
}
*
email_len
=
email_end
-
email_start
-
1
;
*
email
=
email_start
+
1
;
if
(
*
email
==
email_end
&&
!
allow_empty_email
)
return
-
1
;
return
0
;
}
// Now look for the name.
name_start
=
line
;
while
(
name_start
<
email_start
&&
isspace
(
*
name_start
))
++
name_start
;
int
git_mailmap_parse
(
git_mailmap
**
mailmap
,
const
char
*
data
,
size_t
size
)
{
char_range
file
=
{
data
,
size
};
git_mailmap_entry
*
entry
=
NULL
;
int
error
=
0
;
*
name
=
name_start
;
*
mailmap
=
git__calloc
(
1
,
sizeof
(
git_mailmap
));
if
(
!*
mailmap
)
return
-
1
;
name_end
=
email_start
;
while
(
name_end
>
name_start
&&
isspace
(
*
(
name_end
-
1
)))
name_end
--
;
/* XXX: Is it worth it to precompute the size? */
error
=
git_vector_init
(
&
(
*
mailmap
)
->
entries
,
0
,
NULL
);
if
(
error
<
0
)
goto
cleanup
;
while
(
file
.
len
>
0
)
{
bool
found
=
false
;
char_range
real_name
,
real_email
,
replace_name
,
replace_email
;
size_t
size
=
0
;
char
*
buf
=
NULL
;
error
=
git_mailmap_parse_single
(
&
file
,
&
found
,
&
real_name
,
&
real_email
,
&
replace_name
,
&
replace_email
);
if
(
error
<
0
)
goto
cleanup
;
if
(
!
found
)
break
;
/* Compute how much space we'll need to store our entry */
size
=
sizeof
(
git_mailmap_entry
);
range_copyz
(
NULL
,
&
size
,
real_name
);
range_copyz
(
NULL
,
&
size
,
real_email
);
range_copyz
(
NULL
,
&
size
,
replace_name
);
range_copyz
(
NULL
,
&
size
,
replace_email
);
entry
=
git__malloc
(
size
);
if
(
!
entry
)
{
error
=
-
1
;
goto
cleanup
;
}
buf
=
(
char
*
)(
entry
+
1
);
entry
->
real_name
=
range_copyz
(
&
buf
,
NULL
,
real_name
);
entry
->
real_email
=
range_copyz
(
&
buf
,
NULL
,
real_email
);
entry
->
replace_name
=
range_copyz
(
&
buf
,
NULL
,
replace_name
);
entry
->
replace_email
=
range_copyz
(
&
buf
,
NULL
,
replace_email
);
assert
(
buf
==
((
char
*
)
entry
)
+
size
);
error
=
git_vector_insert
(
&
(
*
mailmap
)
->
entries
,
entry
);
if
(
error
<
0
)
goto
cleanup
;
entry
=
NULL
;
}
assert
(
name_end
>=
name_start
);
*
name_len
=
name_end
-
name_start
;
cleanup:
if
(
entry
)
git__free
(
entry
);
if
(
error
<
0
&&
*
mailmap
)
git_mailmap_free
(
*
mailmap
);
return
error
;
}
return
email_end
-
line
;
void
git_mailmap_free
(
git_mailmap
*
mailmap
)
{
git_vector_free_deep
(
&
mailmap
->
entries
);
git__free
(
mailmap
);
}
static
void
git_mailmap_parse_line
(
git_mailmap
*
mailmap
,
const
char
*
contents
,
size_t
size
)
void
git_mailmap_resolve
(
const
char
**
name_out
,
const
char
**
email_out
,
git_mailmap
*
mailmap
,
const
char
*
name
,
const
char
*
email
)
{
struct
mailmap_entry
*
entry
;
const
char
*
to_name
;
size_t
to_name_length
;
const
char
*
to_email
;
size_t
to_email_length
;
const
char
*
from_name
;
size_t
from_name_length
;
const
char
*
from_email
;
size_t
from_email_length
;
ssize_t
ret
;
if
(
!
size
)
return
;
if
(
contents
[
0
]
==
'#'
)
return
;
ret
=
parse_name_and_email
(
contents
,
size
,
&
to_name
,
&
to_name_length
,
&
to_email
,
&
to_email_length
,
false
);
if
(
ret
<
0
)
return
;
ret
=
parse_name_and_email
(
contents
+
ret
+
1
,
size
-
ret
-
1
,
&
from_name
,
&
from_name_length
,
&
from_email
,
&
from_email_length
,
true
);
if
(
ret
<
0
)
return
;
entry
=
git__malloc
(
sizeof
(
struct
mailmap_entry
));
entry
->
to_name
=
git__strndup
(
to_name
,
to_name_length
);
entry
->
to_email
=
git__strndup
(
to_email
,
to_email_length
);
entry
->
from_name
=
git__strndup
(
from_name
,
from_name_length
);
entry
->
from_email
=
git__strndup
(
from_email
,
from_email_length
);
printf
(
"%s <%s>
\"
%s
\"
<%s>
\n
"
,
entry
->
to_name
,
entry
->
to_email
,
entry
->
from_name
,
entry
->
from_email
);
git_vector_insert
(
&
mailmap
->
lines
,
entry
);
git_mailmap_entry
*
entry
=
NULL
;
*
name_out
=
name
;
*
email_out
=
email
;
entry
=
git_mailmap_entry_lookup
(
mailmap
,
name
,
email
);
if
(
entry
)
{
if
(
entry
->
real_name
)
*
name_out
=
entry
->
real_name
;
if
(
entry
->
real_email
)
*
email_out
=
entry
->
real_email
;
}
}
static
void
git_mailmap_parse
(
git_mailmap
*
mailmap
,
const
char
*
contents
,
size_t
size
)
git_mailmap_entry
*
git_mailmap_entry_lookup
(
git_mailmap
*
mailmap
,
const
char
*
name
,
const
char
*
email
)
{
size_t
start
=
0
;
size_t
i
;
for
(
i
=
0
;
i
<
size
;
++
i
)
{
if
(
contents
[
i
]
!=
'\n'
)
continue
;
git_mailmap_parse_line
(
mailmap
,
contents
+
start
,
i
-
start
);
start
=
i
+
1
;
git_mailmap_entry
*
entry
;
assert
(
mailmap
&&
name
&&
email
);
git_vector_foreach
(
&
mailmap
->
entries
,
i
,
entry
)
{
if
(
!
git__strcmp
(
email
,
entry
->
replace_email
)
&&
(
!
entry
->
replace_name
||
!
git__strcmp
(
name
,
entry
->
replace_name
)))
{
return
entry
;
}
}
return
NULL
;
}
int
git_mailmap_create
(
git_mailmap
**
mailmap
,
git_repository
*
repo
)
git_mailmap_entry
*
git_mailmap_entry_byindex
(
git_mailmap
*
mailmap
,
size_t
idx
)
{
git_commit
*
head
=
NULL
;
git_blob
*
mailmap_blob
=
NULL
;
git_off_t
size
=
0
;
const
char
*
contents
=
NULL
;
int
ret
;
*
mailmap
=
git__malloc
(
sizeof
(
struct
git_mailmap
));
git_vector_init
(
&
(
*
mailmap
)
->
lines
,
0
,
NULL
);
return
git_vector_get
(
&
mailmap
->
entries
,
idx
);
}
ret
=
git_revparse_single
((
git_object
**
)
&
head
,
repo
,
"HEAD"
);
if
(
ret
)
goto
error
;
size_t
git_mailmap_entry_count
(
git_mailmap
*
mailmap
)
{
return
git_vector_length
(
&
mailmap
->
entries
);
}
ret
=
git_object_lookup_bypath
(
(
git_object
**
)
&
mailmap_blob
,
(
const
git_object
*
)
head
,
".mailmap"
,
GIT_OBJ_BLOB
);
if
(
ret
)
goto
error
;
int
git_mailmap_from_tree
(
git_mailmap
**
mailmap
,
const
git_object
*
treeish
)
{
git_blob
*
blob
=
NULL
;
const
char
*
content
=
NULL
;
git_off_t
size
=
0
;
int
error
;
contents
=
git_blob_rawcontent
(
mailmap_blob
);
size
=
git_blob_rawsize
(
mailmap_blob
);
*
mailmap
=
NULL
;
git_mailmap_parse
(
*
mailmap
,
contents
,
size
);
error
=
git_object_lookup_bypath
(
(
git_object
**
)
&
blob
,
treeish
,
".mailmap"
,
GIT_OBJ_BLOB
);
if
(
error
<
0
)
goto
cleanup
;
return
0
;
content
=
git_blob_rawcontent
(
blob
);
size
=
git_blob_rawsize
(
blob
);
error:
assert
(
ret
);
error
=
git_mailmap_parse
(
mailmap
,
content
,
size
);
if
(
mailmap_blob
)
git_blob_free
(
mailmap_blob
);
if
(
head
)
git_commit_free
(
head
);
git_mailmap_free
(
*
mailmap
);
return
ret
;
cleanup:
if
(
blob
!=
NULL
)
git_blob_free
(
blob
);
return
error
;
}
void
git_mailmap_free
(
struct
git_mailmap
*
mailmap
)
int
git_mailmap_from_repo
(
git_mailmap
**
mailmap
,
git_repository
*
repo
)
{
size_t
i
;
struct
mailmap_entry
*
line
;
git_vector_foreach
(
&
mailmap
->
lines
,
i
,
line
)
{
git__free
((
char
*
)
line
->
to_name
);
git__free
((
char
*
)
line
->
to_email
);
git__free
((
char
*
)
line
->
from_name
);
git__free
((
char
*
)
line
->
from_email
);
git__free
(
line
);
}
git_object
*
head
=
NULL
;
int
error
;
git_vector_clear
(
&
mailmap
->
lines
);
git__free
(
mailmap
);
*
mailmap
=
NULL
;
error
=
git_revparse_single
(
&
head
,
repo
,
"HEAD"
);
if
(
error
<
0
)
goto
cleanup
;
error
=
git_mailmap_from_tree
(
mailmap
,
head
);
cleanup:
if
(
head
)
git_object_free
(
head
);
return
error
;
}
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