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
d34f6826
Commit
d34f6826
authored
Apr 08, 2014
by
Edward Thomson
Committed by
Edward Thomson
May 26, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Patch parsing from patch files
parent
7cb904ba
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1461 additions
and
8 deletions
+1461
-8
include/git2/diff.h
+8
-6
include/git2/patch.h
+13
-0
src/buffer.c
+75
-0
src/buffer.h
+5
-0
src/patch.c
+758
-0
src/path.c
+19
-0
src/path.h
+6
-0
src/util.c
+20
-2
src/util.h
+10
-0
tests/apply/apply_common.h
+189
-0
tests/apply/fromfile.c
+301
-0
tests/buf/quote.c
+57
-0
No files found.
include/git2/diff.h
View file @
d34f6826
...
...
@@ -448,6 +448,8 @@ typedef int (*git_diff_file_cb)(
float
progress
,
void
*
payload
);
#define GIT_DIFF_HUNK_HEADER_SIZE 128
/**
* When producing a binary diff, the binary data returned will be
* either the deflated full ("literal") contents of the file, or
...
...
@@ -499,12 +501,12 @@ typedef int(*git_diff_binary_cb)(
* Structure describing a hunk of a diff.
*/
typedef
struct
{
int
old_start
;
/**
<
Starting line number in old_file */
int
old_lines
;
/**
<
Number of lines in old_file */
int
new_start
;
/**
<
Starting line number in new_file */
int
new_lines
;
/**
<
Number of lines in new_file */
size_t
header_len
;
/**
<
Number of bytes in header text */
char
header
[
128
];
/**<
Header text, NUL-byte terminated */
int
old_start
;
/** Starting line number in old_file */
int
old_lines
;
/** Number of lines in old_file */
int
new_start
;
/** Starting line number in new_file */
int
new_lines
;
/** Number of lines in new_file */
size_t
header_len
;
/** Number of bytes in header text */
char
header
[
GIT_DIFF_HUNK_HEADER_SIZE
];
/**
Header text, NUL-byte terminated */
}
git_diff_hunk
;
/**
...
...
include/git2/patch.h
View file @
d34f6826
...
...
@@ -267,6 +267,19 @@ GIT_EXTERN(int) git_patch_to_buf(
git_buf
*
out
,
git_patch
*
patch
);
/**
* Create a patch from the contents of a patch file.
*
* @param out The patch to be created
* @param patchfile The contents of a patch file
* @param patchfile_len The length of the patch file
* @return 0 on success, <0 on failure.
*/
GIT_EXTERN
(
int
)
git_patch_from_patchfile
(
git_patch
**
out
,
const
char
*
patchfile
,
size_t
patchfile_len
);
GIT_END_DECL
/**@}*/
...
...
src/buffer.c
View file @
d34f6826
...
...
@@ -766,3 +766,78 @@ int git_buf_splice(
buf
->
ptr
[
buf
->
size
]
=
'\0'
;
return
0
;
}
/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
int
git_buf_unquote
(
git_buf
*
buf
)
{
size_t
i
,
j
;
char
ch
;
git_buf_rtrim
(
buf
);
if
(
buf
->
size
<
2
||
buf
->
ptr
[
0
]
!=
'"'
||
buf
->
ptr
[
buf
->
size
-
1
]
!=
'"'
)
goto
invalid
;
for
(
i
=
0
,
j
=
1
;
j
<
buf
->
size
-
1
;
i
++
,
j
++
)
{
ch
=
buf
->
ptr
[
j
];
if
(
ch
==
'\\'
)
{
if
(
j
==
buf
->
size
-
2
)
goto
invalid
;
ch
=
buf
->
ptr
[
++
j
];
switch
(
ch
)
{
/* \" or \\ simply copy the char in */
case
'"'
:
case
'\\'
:
break
;
/* add the appropriate escaped char */
case
'a'
:
ch
=
'\a'
;
break
;
case
'b'
:
ch
=
'\b'
;
break
;
case
'f'
:
ch
=
'\f'
;
break
;
case
'n'
:
ch
=
'\n'
;
break
;
case
'r'
:
ch
=
'\r'
;
break
;
case
't'
:
ch
=
'\t'
;
break
;
case
'v'
:
ch
=
'\v'
;
break
;
/* \xyz digits convert to the char*/
case
'0'
:
case
'1'
:
case
'2'
:
if
(
j
==
buf
->
size
-
3
)
{
giterr_set
(
GITERR_INVALID
,
"Truncated quoted character
\\
%c"
,
ch
);
return
-
1
;
}
if
(
buf
->
ptr
[
j
+
1
]
<
'0'
||
buf
->
ptr
[
j
+
1
]
>
'7'
||
buf
->
ptr
[
j
+
2
]
<
'0'
||
buf
->
ptr
[
j
+
2
]
>
'7'
)
{
giterr_set
(
GITERR_INVALID
,
"Truncated quoted character
\\
%c%c%c"
,
buf
->
ptr
[
j
],
buf
->
ptr
[
j
+
1
],
buf
->
ptr
[
j
+
2
]);
return
-
1
;
}
ch
=
((
buf
->
ptr
[
j
]
-
'0'
)
<<
6
)
|
((
buf
->
ptr
[
j
+
1
]
-
'0'
)
<<
3
)
|
(
buf
->
ptr
[
j
+
2
]
-
'0'
);
j
+=
2
;
break
;
default:
giterr_set
(
GITERR_INVALID
,
"Invalid quoted character
\\
%c"
,
ch
);
return
-
1
;
}
}
buf
->
ptr
[
i
]
=
ch
;
}
buf
->
ptr
[
i
]
=
'\0'
;
buf
->
size
=
i
;
return
0
;
invalid:
giterr_set
(
GITERR_INVALID
,
"Invalid quoted line"
);
return
-
1
;
}
src/buffer.h
View file @
d34f6826
...
...
@@ -173,6 +173,11 @@ void git_buf_rtrim(git_buf *buf);
int
git_buf_cmp
(
const
git_buf
*
a
,
const
git_buf
*
b
);
/* Unquote a buffer as specified in
* http://marc.info/?l=git&m=112927316408690&w=2
*/
int
git_buf_unquote
(
git_buf
*
buf
);
/* Write data as base64 encoded in buffer */
int
git_buf_encode_base64
(
git_buf
*
buf
,
const
char
*
data
,
size_t
len
);
/* Decode the given bas64 and write the result to the buffer */
...
...
src/patch.c
0 → 100644
View file @
d34f6826
#include "git2/patch.h"
#include "diff_patch.h"
#define parse_err(...) \
( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
typedef
struct
{
const
char
*
content
;
size_t
content_len
;
const
char
*
line
;
size_t
line_len
;
size_t
line_num
;
size_t
remain
;
char
*
header_new_path
;
char
*
header_old_path
;
}
patch_parse_ctx
;
static
void
parse_advance_line
(
patch_parse_ctx
*
ctx
)
{
ctx
->
line
+=
ctx
->
line_len
;
ctx
->
remain
-=
ctx
->
line_len
;
ctx
->
line_len
=
git__linenlen
(
ctx
->
line
,
ctx
->
remain
);
ctx
->
line_num
++
;
}
static
void
parse_advance_chars
(
patch_parse_ctx
*
ctx
,
size_t
char_cnt
)
{
ctx
->
line
+=
char_cnt
;
ctx
->
remain
-=
char_cnt
;
ctx
->
line_len
-=
char_cnt
;
}
static
int
parse_advance_expected
(
patch_parse_ctx
*
ctx
,
const
char
*
expected
,
size_t
expected_len
)
{
if
(
ctx
->
line_len
<
expected_len
)
return
-
1
;
if
(
memcmp
(
ctx
->
line
,
expected
,
expected_len
)
!=
0
)
return
-
1
;
parse_advance_chars
(
ctx
,
expected_len
);
return
0
;
}
static
int
parse_advance_ws
(
patch_parse_ctx
*
ctx
)
{
int
ret
=
-
1
;
while
(
ctx
->
line_len
>
0
&&
ctx
->
line
[
0
]
!=
'\n'
&&
git__isspace
(
ctx
->
line
[
0
]))
{
ctx
->
line
++
;
ctx
->
line_len
--
;
ctx
->
remain
--
;
ret
=
0
;
}
return
ret
;
}
static
int
header_path_len
(
patch_parse_ctx
*
ctx
)
{
bool
inquote
=
0
;
bool
quoted
=
(
ctx
->
line_len
>
0
&&
ctx
->
line
[
0
]
==
'"'
);
size_t
len
;
for
(
len
=
quoted
;
len
<
ctx
->
line_len
;
len
++
)
{
if
(
!
quoted
&&
git__isspace
(
ctx
->
line
[
len
]))
break
;
else
if
(
quoted
&&
!
inquote
&&
ctx
->
line
[
len
]
==
'"'
)
{
len
++
;
break
;
}
inquote
=
(
!
inquote
&&
ctx
->
line
[
len
]
==
'\\'
);
}
return
len
;
}
static
int
parse_header_path_buf
(
git_buf
*
path
,
patch_parse_ctx
*
ctx
)
{
int
path_len
,
error
=
0
;
path_len
=
header_path_len
(
ctx
);
if
((
error
=
git_buf_put
(
path
,
ctx
->
line
,
path_len
))
<
0
)
goto
done
;
parse_advance_chars
(
ctx
,
path_len
);
git_buf_rtrim
(
path
);
if
(
path
->
size
>
0
&&
path
->
ptr
[
0
]
==
'"'
)
error
=
git_buf_unquote
(
path
);
if
(
error
<
0
)
goto
done
;
git_path_squash_slashes
(
path
);
done:
return
error
;
}
static
int
parse_header_path
(
char
**
out
,
patch_parse_ctx
*
ctx
)
{
git_buf
path
=
GIT_BUF_INIT
;
int
error
=
parse_header_path_buf
(
&
path
,
ctx
);
*
out
=
git_buf_detach
(
&
path
);
return
error
;
}
static
int
parse_header_git_oldpath
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
return
parse_header_path
((
char
**
)
&
patch
->
ofile
.
file
->
path
,
ctx
);
}
static
int
parse_header_git_newpath
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
return
parse_header_path
((
char
**
)
&
patch
->
nfile
.
file
->
path
,
ctx
);
}
static
int
parse_header_mode
(
uint16_t
*
mode
,
patch_parse_ctx
*
ctx
)
{
const
char
*
end
;
int32_t
m
;
int
ret
;
if
(
ctx
->
line_len
<
1
||
!
git__isdigit
(
ctx
->
line
[
0
]))
return
parse_err
(
"invalid file mode at line %d"
,
ctx
->
line_num
);
if
((
ret
=
git__strntol32
(
&
m
,
ctx
->
line
,
ctx
->
line_len
,
&
end
,
8
))
<
0
)
return
ret
;
if
(
m
>
UINT16_MAX
)
return
-
1
;
*
mode
=
(
uint16_t
)
m
;
parse_advance_chars
(
ctx
,
(
end
-
ctx
->
line
));
return
ret
;
}
static
int
parse_header_oid
(
git_oid
*
oid
,
size_t
*
oid_len
,
patch_parse_ctx
*
ctx
)
{
size_t
len
;
for
(
len
=
0
;
len
<
ctx
->
line_len
&&
len
<
GIT_OID_HEXSZ
;
len
++
)
{
if
(
!
git__isxdigit
(
ctx
->
line
[
len
]))
break
;
}
if
(
len
<
GIT_OID_MINPREFIXLEN
||
git_oid_fromstrn
(
oid
,
ctx
->
line
,
len
)
<
0
)
return
parse_err
(
"invalid hex formatted object id at line %d"
,
ctx
->
line_num
);
parse_advance_chars
(
ctx
,
len
);
*
oid_len
=
len
;
return
0
;
}
static
int
parse_header_git_index
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
/*
* TODO: we read the prefix provided in the diff into the delta's id
* field, but do not mark is at an abbreviated id.
*/
size_t
oid_len
,
nid_len
;
if
(
parse_header_oid
(
&
patch
->
delta
->
old_file
.
id
,
&
oid_len
,
ctx
)
<
0
||
parse_advance_expected
(
ctx
,
".."
,
2
)
<
0
||
parse_header_oid
(
&
patch
->
delta
->
new_file
.
id
,
&
nid_len
,
ctx
)
<
0
)
return
-
1
;
if
(
ctx
->
line_len
>
0
&&
ctx
->
line
[
0
]
==
' '
)
{
uint16_t
mode
;
parse_advance_chars
(
ctx
,
1
);
if
(
parse_header_mode
(
&
mode
,
ctx
)
<
0
)
return
-
1
;
if
(
!
patch
->
delta
->
new_file
.
mode
)
patch
->
delta
->
new_file
.
mode
=
mode
;
if
(
!
patch
->
delta
->
old_file
.
mode
)
patch
->
delta
->
old_file
.
mode
=
mode
;
}
return
0
;
}
static
int
parse_header_git_oldmode
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
return
parse_header_mode
(
&
patch
->
ofile
.
file
->
mode
,
ctx
);
}
static
int
parse_header_git_newmode
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
return
parse_header_mode
(
&
patch
->
nfile
.
file
->
mode
,
ctx
);
}
static
int
parse_header_git_deletedfilemode
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
git__free
((
char
*
)
patch
->
ofile
.
file
->
path
);
patch
->
ofile
.
file
->
path
=
NULL
;
patch
->
delta
->
status
=
GIT_DELTA_DELETED
;
return
parse_header_mode
(
&
patch
->
ofile
.
file
->
mode
,
ctx
);
}
static
int
parse_header_git_newfilemode
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
git__free
((
char
*
)
patch
->
nfile
.
file
->
path
);
patch
->
nfile
.
file
->
path
=
NULL
;
patch
->
delta
->
status
=
GIT_DELTA_ADDED
;
return
parse_header_mode
(
&
patch
->
nfile
.
file
->
mode
,
ctx
);
}
static
int
parse_header_rename
(
char
**
out
,
char
**
header_path
,
patch_parse_ctx
*
ctx
)
{
git_buf
path
=
GIT_BUF_INIT
;
size_t
header_path_len
,
prefix_len
;
if
(
*
header_path
==
NULL
)
return
parse_err
(
"rename without proper git diff header at line %d"
,
ctx
->
line_num
);
header_path_len
=
strlen
(
*
header_path
);
if
(
parse_header_path_buf
(
&
path
,
ctx
)
<
0
)
return
-
1
;
if
(
header_path_len
<
git_buf_len
(
&
path
))
return
parse_err
(
"rename path is invalid at line %d"
,
ctx
->
line_num
);
/* This sanity check exists because git core uses the data in the
* "rename from" / "rename to" lines, but it's formatted differently
* than the other paths and lacks the normal prefix. This irregularity
* causes us to ignore these paths (we always store the prefixed paths)
* but instead validate that they match the suffix of the paths we parsed
* since we would behave differently from git core if they ever differed.
* Instead, we raise an error, rather than parsing differently.
*/
prefix_len
=
header_path_len
-
path
.
size
;
if
(
strncmp
(
*
header_path
+
prefix_len
,
path
.
ptr
,
path
.
size
)
!=
0
||
(
prefix_len
>
0
&&
(
*
header_path
)[
prefix_len
-
1
]
!=
'/'
))
return
parse_err
(
"rename path does not match header at line %d"
,
ctx
->
line_num
);
*
out
=
*
header_path
;
*
header_path
=
NULL
;
git_buf_free
(
&
path
);
return
0
;
}
static
int
parse_header_renamefrom
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
patch
->
delta
->
status
|=
GIT_DELTA_RENAMED
;
return
parse_header_rename
(
(
char
**
)
&
patch
->
ofile
.
file
->
path
,
&
ctx
->
header_old_path
,
ctx
);
}
static
int
parse_header_renameto
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
patch
->
delta
->
status
|=
GIT_DELTA_RENAMED
;
return
parse_header_rename
(
(
char
**
)
&
patch
->
nfile
.
file
->
path
,
&
ctx
->
header_new_path
,
ctx
);
}
static
int
parse_header_percent
(
uint16_t
*
out
,
patch_parse_ctx
*
ctx
)
{
int32_t
val
;
const
char
*
end
;
if
(
ctx
->
line_len
<
1
||
!
git__isdigit
(
ctx
->
line
[
0
])
||
git__strntol32
(
&
val
,
ctx
->
line
,
ctx
->
line_len
,
&
end
,
10
)
<
0
)
return
-
1
;
parse_advance_chars
(
ctx
,
(
end
-
ctx
->
line
));
if
(
parse_advance_expected
(
ctx
,
"%"
,
1
)
<
0
)
return
-
1
;
if
(
val
>
100
)
return
-
1
;
*
out
=
val
;
return
0
;
}
static
int
parse_header_similarity
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
if
(
parse_header_percent
(
&
patch
->
delta
->
similarity
,
ctx
)
<
0
)
return
parse_err
(
"invalid similarity percentage at line %d"
,
ctx
->
line_num
);
return
0
;
}
static
int
parse_header_dissimilarity
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
uint16_t
dissimilarity
;
if
(
parse_header_percent
(
&
dissimilarity
,
ctx
)
<
0
)
return
parse_err
(
"invalid similarity percentage at line %d"
,
ctx
->
line_num
);
patch
->
delta
->
similarity
=
100
-
dissimilarity
;
return
0
;
}
typedef
struct
{
const
char
*
str
;
int
(
*
fn
)(
git_patch
*
,
patch_parse_ctx
*
);
}
header_git_op
;
static
const
header_git_op
header_git_ops
[]
=
{
{
"@@ -"
,
NULL
},
{
"--- "
,
parse_header_git_oldpath
},
{
"+++ "
,
parse_header_git_newpath
},
{
"index "
,
parse_header_git_index
},
{
"old mode "
,
parse_header_git_oldmode
},
{
"new mode "
,
parse_header_git_newmode
},
{
"deleted file mode "
,
parse_header_git_deletedfilemode
},
{
"new file mode "
,
parse_header_git_newfilemode
},
{
"rename from "
,
parse_header_renamefrom
},
{
"rename to "
,
parse_header_renameto
},
{
"rename old "
,
parse_header_renamefrom
},
{
"rename new "
,
parse_header_renameto
},
{
"similarity index "
,
parse_header_similarity
},
{
"dissimilarity index "
,
parse_header_dissimilarity
},
};
static
int
parse_header_git
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
size_t
i
;
int
error
=
0
;
/* Parse the diff --git line */
if
(
parse_advance_expected
(
ctx
,
"diff --git "
,
11
)
<
0
)
return
parse_err
(
"corrupt git diff header at line %d"
,
ctx
->
line_num
);
if
(
parse_header_path
(
&
ctx
->
header_old_path
,
ctx
)
<
0
)
return
parse_err
(
"corrupt old path in git diff header at line %d"
,
ctx
->
line_num
);
if
(
parse_advance_ws
(
ctx
)
<
0
||
parse_header_path
(
&
ctx
->
header_new_path
,
ctx
)
<
0
)
return
parse_err
(
"corrupt new path in git diff header at line %d"
,
ctx
->
line_num
);
/* Parse remaining header lines */
for
(
parse_advance_line
(
ctx
);
ctx
->
remain
>
0
;
parse_advance_line
(
ctx
))
{
if
(
ctx
->
line_len
==
0
||
ctx
->
line
[
ctx
->
line_len
-
1
]
!=
'\n'
)
break
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
header_git_ops
);
i
++
)
{
const
header_git_op
*
op
=
&
header_git_ops
[
i
];
size_t
op_len
=
strlen
(
op
->
str
);
if
(
memcmp
(
ctx
->
line
,
op
->
str
,
min
(
op_len
,
ctx
->
line_len
))
!=
0
)
continue
;
/* Do not advance if this is the patch separator */
if
(
op
->
fn
==
NULL
)
goto
done
;
parse_advance_chars
(
ctx
,
op_len
);
if
((
error
=
op
->
fn
(
patch
,
ctx
))
<
0
)
goto
done
;
parse_advance_ws
(
ctx
);
parse_advance_expected
(
ctx
,
"
\n
"
,
1
);
if
(
ctx
->
line_len
>
0
)
{
error
=
parse_err
(
"trailing data at line %d"
,
ctx
->
line_num
);
goto
done
;
}
break
;
}
}
done:
return
error
;
}
static
int
parse_number
(
int
*
out
,
patch_parse_ctx
*
ctx
)
{
const
char
*
end
;
int64_t
num
;
if
(
!
git__isdigit
(
ctx
->
line
[
0
]))
return
-
1
;
if
(
git__strntol64
(
&
num
,
ctx
->
line
,
ctx
->
line_len
,
&
end
,
10
)
<
0
)
return
-
1
;
if
(
num
<
0
)
return
-
1
;
*
out
=
(
int
)
num
;
parse_advance_chars
(
ctx
,
(
end
-
ctx
->
line
));
return
0
;
}
static
int
parse_hunk_header
(
diff_patch_hunk
*
hunk
,
patch_parse_ctx
*
ctx
)
{
const
char
*
header_start
=
ctx
->
line
;
hunk
->
hunk
.
old_lines
=
1
;
hunk
->
hunk
.
new_lines
=
1
;
if
(
parse_advance_expected
(
ctx
,
"@@ -"
,
4
)
<
0
||
parse_number
(
&
hunk
->
hunk
.
old_start
,
ctx
)
<
0
)
goto
fail
;
if
(
ctx
->
line_len
>
0
&&
ctx
->
line
[
0
]
==
','
)
{
if
(
parse_advance_expected
(
ctx
,
","
,
1
)
<
0
||
parse_number
(
&
hunk
->
hunk
.
old_lines
,
ctx
)
<
0
)
goto
fail
;
}
if
(
parse_advance_expected
(
ctx
,
" +"
,
2
)
<
0
||
parse_number
(
&
hunk
->
hunk
.
new_start
,
ctx
)
<
0
)
goto
fail
;
if
(
ctx
->
line_len
>
0
&&
ctx
->
line
[
0
]
==
','
)
{
if
(
parse_advance_expected
(
ctx
,
","
,
1
)
<
0
||
parse_number
(
&
hunk
->
hunk
.
new_lines
,
ctx
)
<
0
)
goto
fail
;
}
if
(
parse_advance_expected
(
ctx
,
" @@"
,
3
)
<
0
)
goto
fail
;
parse_advance_line
(
ctx
);
if
(
!
hunk
->
hunk
.
old_lines
&&
!
hunk
->
hunk
.
new_lines
)
goto
fail
;
hunk
->
hunk
.
header_len
=
ctx
->
line
-
header_start
;
if
(
hunk
->
hunk
.
header_len
>
(
GIT_DIFF_HUNK_HEADER_SIZE
-
1
))
return
parse_err
(
"oversized patch hunk header at line %d"
,
ctx
->
line_num
);
memcpy
(
hunk
->
hunk
.
header
,
header_start
,
hunk
->
hunk
.
header_len
);
hunk
->
hunk
.
header
[
hunk
->
hunk
.
header_len
]
=
'\0'
;
return
0
;
fail:
giterr_set
(
GITERR_PATCH
,
"invalid patch hunk header at line %d"
,
ctx
->
line_num
);
return
-
1
;
}
static
int
parse_hunk_body
(
git_patch
*
patch
,
diff_patch_hunk
*
hunk
,
patch_parse_ctx
*
ctx
)
{
git_diff_line
*
line
;
int
error
=
0
;
int
oldlines
=
hunk
->
hunk
.
old_lines
;
int
newlines
=
hunk
->
hunk
.
new_lines
;
for
(;
ctx
->
remain
>
4
&&
(
oldlines
||
newlines
)
&&
memcmp
(
ctx
->
line
,
"@@ -"
,
4
)
!=
0
;
parse_advance_line
(
ctx
))
{
int
origin
;
int
prefix
=
1
;
if
(
ctx
->
line_len
==
0
||
ctx
->
line
[
ctx
->
line_len
-
1
]
!=
'\n'
)
{
error
=
parse_err
(
"invalid patch instruction at line %d"
,
ctx
->
line_num
);
goto
done
;
}
switch
(
ctx
->
line
[
0
])
{
case
'\n'
:
prefix
=
0
;
case
' '
:
origin
=
GIT_DIFF_LINE_CONTEXT
;
oldlines
--
;
newlines
--
;
break
;
case
'-'
:
origin
=
GIT_DIFF_LINE_DELETION
;
oldlines
--
;
break
;
case
'+'
:
origin
=
GIT_DIFF_LINE_ADDITION
;
newlines
--
;
break
;
default:
error
=
parse_err
(
"invalid patch hunk at line %d"
,
ctx
->
line_num
);
goto
done
;
}
line
=
git_array_alloc
(
patch
->
lines
);
GITERR_CHECK_ALLOC
(
line
);
memset
(
line
,
0x0
,
sizeof
(
git_diff_line
));
line
->
content
=
ctx
->
line
+
prefix
;
line
->
content_len
=
ctx
->
line_len
-
prefix
;
line
->
content_offset
=
ctx
->
content_len
-
ctx
->
remain
;
line
->
origin
=
origin
;
hunk
->
line_count
++
;
}
if
(
oldlines
||
newlines
)
{
error
=
parse_err
(
"invalid patch hunk, expected %d old lines and %d new lines"
,
hunk
->
hunk
.
old_lines
,
hunk
->
hunk
.
new_lines
);
goto
done
;
}
/* Handle "\ No newline at end of file". Only expect the leading
* backslash, though, because the rest of the string could be
* localized. Because `diff` optimizes for the case where you
* want to apply the patch by hand.
*/
if
(
ctx
->
line_len
>=
2
&&
memcmp
(
ctx
->
line
,
"
\\
"
,
2
)
==
0
&&
git_array_size
(
patch
->
lines
)
>
0
)
{
line
=
git_array_get
(
patch
->
lines
,
git_array_size
(
patch
->
lines
)
-
1
);
if
(
line
->
content_len
<
1
)
{
error
=
parse_err
(
"cannot trim trailing newline of empty line"
);
goto
done
;
}
line
->
content_len
--
;
parse_advance_line
(
ctx
);
}
done:
return
error
;
}
static
int
parse_header_traditional
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
GIT_UNUSED
(
patch
);
GIT_UNUSED
(
ctx
);
return
1
;
}
static
int
parse_patch_header
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
int
error
=
0
;
for
(
ctx
->
line
=
ctx
->
content
;
ctx
->
remain
>
0
;
parse_advance_line
(
ctx
))
{
/* This line is too short to be a patch header. */
if
(
ctx
->
line_len
<
6
)
continue
;
/* This might be a hunk header without a patch header, provide a
* sensible error message. */
if
(
memcmp
(
ctx
->
line
,
"@@ -"
,
4
)
==
0
)
{
size_t
line_num
=
ctx
->
line_num
;
diff_patch_hunk
hunk
;
/* If this cannot be parsed as a hunk header, it's just leading
* noise, continue.
*/
if
(
parse_hunk_header
(
&
hunk
,
ctx
)
<
0
)
{
giterr_clear
();
continue
;
}
error
=
parse_err
(
"invalid hunk header outside patch at line %d"
,
line_num
);
goto
done
;
}
/* This buffer is too short to contain a patch. */
if
(
ctx
->
remain
<
ctx
->
line_len
+
6
)
break
;
/* A proper git patch */
if
(
ctx
->
line_len
>=
11
&&
memcmp
(
ctx
->
line
,
"diff --git "
,
11
)
==
0
)
{
if
((
error
=
parse_header_git
(
patch
,
ctx
))
<
0
)
goto
done
;
/* For modechange only patches, it does not include filenames;
* instead we need to use the paths in the diff --git header.
*/
if
(
!
patch
->
ofile
.
file
->
path
&&
!
patch
->
nfile
.
file
->
path
)
{
if
(
!
ctx
->
header_old_path
||
!
ctx
->
header_new_path
)
{
error
=
parse_err
(
"git diff header lacks old / new paths"
);
goto
done
;
}
patch
->
ofile
.
file
->
path
=
ctx
->
header_old_path
;
ctx
->
header_old_path
=
NULL
;
patch
->
nfile
.
file
->
path
=
ctx
->
header_new_path
;
ctx
->
header_new_path
=
NULL
;
}
goto
done
;
}
if
((
error
=
parse_header_traditional
(
patch
,
ctx
))
<=
0
)
goto
done
;
error
=
0
;
continue
;
}
error
=
parse_err
(
"no header in patch file"
);
done:
return
error
;
}
static
int
parse_patch_body
(
git_patch
*
patch
,
patch_parse_ctx
*
ctx
)
{
diff_patch_hunk
*
hunk
;
int
error
=
0
;
for
(;
ctx
->
line_len
>
4
&&
memcmp
(
ctx
->
line
,
"@@ -"
,
4
)
==
0
;
)
{
hunk
=
git_array_alloc
(
patch
->
hunks
);
GITERR_CHECK_ALLOC
(
hunk
);
memset
(
hunk
,
0
,
sizeof
(
diff_patch_hunk
));
hunk
->
line_start
=
git_array_size
(
patch
->
lines
);
hunk
->
line_count
=
0
;
if
((
error
=
parse_hunk_header
(
hunk
,
ctx
))
<
0
||
(
error
=
parse_hunk_body
(
patch
,
hunk
,
ctx
))
<
0
)
goto
done
;
}
done:
return
error
;
}
static
int
check_patch
(
git_patch
*
patch
)
{
if
(
!
patch
->
ofile
.
file
->
path
&&
patch
->
delta
->
status
!=
GIT_DELTA_ADDED
)
return
parse_err
(
"missing old file path"
);
if
(
!
patch
->
nfile
.
file
->
path
&&
patch
->
delta
->
status
!=
GIT_DELTA_DELETED
)
return
parse_err
(
"missing new file path"
);
if
(
patch
->
ofile
.
file
->
path
&&
patch
->
nfile
.
file
->
path
)
{
if
(
!
patch
->
nfile
.
file
->
mode
)
patch
->
nfile
.
file
->
mode
=
patch
->
ofile
.
file
->
mode
;
}
if
(
patch
->
delta
->
status
==
GIT_DELTA_MODIFIED
&&
patch
->
nfile
.
file
->
mode
==
patch
->
ofile
.
file
->
mode
&&
git_array_size
(
patch
->
hunks
)
==
0
)
return
parse_err
(
"patch with no hunks"
);
return
0
;
}
int
git_patch_from_patchfile
(
git_patch
**
out
,
const
char
*
content
,
size_t
content_len
)
{
patch_parse_ctx
ctx
=
{
0
};
git_patch
*
patch
;
int
error
=
0
;
*
out
=
NULL
;
patch
=
git__calloc
(
1
,
sizeof
(
git_patch
));
GITERR_CHECK_ALLOC
(
patch
);
patch
->
delta
=
git__calloc
(
1
,
sizeof
(
git_diff_delta
));
patch
->
ofile
.
file
=
git__calloc
(
1
,
sizeof
(
git_diff_file
));
patch
->
nfile
.
file
=
git__calloc
(
1
,
sizeof
(
git_diff_file
));
patch
->
delta
->
status
=
GIT_DELTA_MODIFIED
;
ctx
.
content
=
content
;
ctx
.
content_len
=
content_len
;
ctx
.
remain
=
content_len
;
if
((
error
=
parse_patch_header
(
patch
,
&
ctx
))
<
0
||
(
error
=
parse_patch_body
(
patch
,
&
ctx
))
<
0
||
(
error
=
check_patch
(
patch
))
<
0
)
goto
done
;
*
out
=
patch
;
done:
git__free
(
ctx
.
header_old_path
);
git__free
(
ctx
.
header_new_path
);
return
error
;
}
src/path.c
View file @
d34f6826
...
...
@@ -306,6 +306,25 @@ int git_path_join_unrooted(
return
0
;
}
void
git_path_squash_slashes
(
git_buf
*
path
)
{
char
*
p
,
*
q
;
if
(
path
->
size
==
0
)
return
;
for
(
p
=
path
->
ptr
,
q
=
path
->
ptr
;
*
q
;
p
++
,
q
++
)
{
*
p
=
*
q
;
while
(
*
q
==
'/'
&&
*
(
q
+
1
)
==
'/'
)
{
path
->
size
--
;
q
++
;
}
}
*
p
=
'\0'
;
}
int
git_path_prettify
(
git_buf
*
path_out
,
const
char
*
path
,
const
char
*
base
)
{
char
buf
[
GIT_PATH_MAX
];
...
...
src/path.h
View file @
d34f6826
...
...
@@ -244,6 +244,12 @@ extern int git_path_join_unrooted(
git_buf
*
path_out
,
const
char
*
path
,
const
char
*
base
,
ssize_t
*
root_at
);
/**
* Removes multiple occurrences of '/' in a row, squashing them into a
* single '/'.
*/
extern
void
git_path_squash_slashes
(
git_buf
*
path
);
/**
* Clean up path, prepending base if it is not already rooted.
*/
extern
int
git_path_prettify
(
git_buf
*
path_out
,
const
char
*
path
,
const
char
*
base
);
...
...
src/util.c
View file @
d34f6826
...
...
@@ -66,6 +66,12 @@ int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
int
git__strtol64
(
int64_t
*
result
,
const
char
*
nptr
,
const
char
**
endptr
,
int
base
)
{
return
git__strntol64
(
result
,
nptr
,
(
size_t
)
-
1
,
endptr
,
base
);
}
int
git__strntol64
(
int64_t
*
result
,
const
char
*
nptr
,
size_t
nptr_len
,
const
char
**
endptr
,
int
base
)
{
const
char
*
p
;
int64_t
n
,
nn
;
int
c
,
ovfl
,
v
,
neg
,
ndig
;
...
...
@@ -111,7 +117,7 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba
/*
* Non-empty sequence of digits
*/
for
(;
;
p
++
,
ndig
++
)
{
for
(;
nptr_len
>
0
;
p
++
,
ndig
++
,
nptr_len
--
)
{
c
=
*
p
;
v
=
base
;
if
(
'0'
<=
c
&&
c
<=
'9'
)
...
...
@@ -148,11 +154,17 @@ Return:
int
git__strtol32
(
int32_t
*
result
,
const
char
*
nptr
,
const
char
**
endptr
,
int
base
)
{
return
git__strntol32
(
result
,
nptr
,
(
size_t
)
-
1
,
endptr
,
base
);
}
int
git__strntol32
(
int32_t
*
result
,
const
char
*
nptr
,
size_t
nptr_len
,
const
char
**
endptr
,
int
base
)
{
int
error
;
int32_t
tmp_int
;
int64_t
tmp_long
;
if
((
error
=
git__str
tol64
(
&
tmp_long
,
nptr
,
endptr
,
base
))
<
0
)
if
((
error
=
git__str
ntol64
(
&
tmp_long
,
nptr
,
nptr_len
,
endptr
,
base
))
<
0
)
return
error
;
tmp_int
=
tmp_long
&
0xFFFFFFFF
;
...
...
@@ -321,6 +333,12 @@ char *git__strsep(char **end, const char *sep)
return
NULL
;
}
size_t
git__linenlen
(
const
char
*
buffer
,
size_t
buffer_len
)
{
char
*
nl
=
memchr
(
buffer
,
'\n'
,
buffer_len
);
return
nl
?
(
size_t
)(
nl
-
buffer
)
+
1
:
buffer_len
;
}
void
git__hexdump
(
const
char
*
buffer
,
size_t
len
)
{
static
const
size_t
LINE_WIDTH
=
16
;
...
...
src/util.h
View file @
d34f6826
...
...
@@ -263,7 +263,10 @@ GIT_INLINE(int) git__signum(int val)
}
extern
int
git__strtol32
(
int32_t
*
n
,
const
char
*
buff
,
const
char
**
end_buf
,
int
base
);
extern
int
git__strntol32
(
int32_t
*
n
,
const
char
*
buff
,
size_t
buff_len
,
const
char
**
end_buf
,
int
base
);
extern
int
git__strtol64
(
int64_t
*
n
,
const
char
*
buff
,
const
char
**
end_buf
,
int
base
);
extern
int
git__strntol64
(
int64_t
*
n
,
const
char
*
buff
,
size_t
buff_len
,
const
char
**
end_buf
,
int
base
);
extern
void
git__hexdump
(
const
char
*
buffer
,
size_t
n
);
extern
uint32_t
git__hash
(
const
void
*
key
,
int
len
,
uint32_t
seed
);
...
...
@@ -290,6 +293,8 @@ GIT_INLINE(int) git__tolower(int c)
# define git__tolower(a) tolower(a)
#endif
extern
size_t
git__linenlen
(
const
char
*
buffer
,
size_t
buffer_len
);
GIT_INLINE
(
const
char
*
)
git__next_line
(
const
char
*
s
)
{
while
(
*
s
&&
*
s
!=
'\n'
)
s
++
;
...
...
@@ -466,6 +471,11 @@ GIT_INLINE(bool) git__iswildcard(int c)
return
(
c
==
'*'
||
c
==
'?'
||
c
==
'['
);
}
GIT_INLINE
(
bool
)
git__isxdigit
(
int
c
)
{
return
((
c
>=
'0'
&&
c
<=
'9'
)
||
(
c
>=
'a'
&&
c
<=
'f'
)
||
(
c
>=
'A'
&&
c
<=
'F'
));
}
/*
* Parse a string value as a boolean, just like Core Git does.
*
...
...
tests/apply/apply_common.h
View file @
d34f6826
...
...
@@ -284,3 +284,192 @@
" and this\n" \
" is additional context\n" \
" below it!\n"
#define PATCH_RENAME_EXACT_QUOTEDNAME \
"diff --git a/file.txt \"b/foo\\\"bar.txt\"\n" \
"similarity index 100%\n" \
"rename from file.txt\n" \
"rename to \"foo\\\"bar.txt\"\n"
#define PATCH_RENAME_SIMILAR_QUOTEDNAME \
"diff --git a/file.txt \"b/foo\\\"bar.txt\"\n" \
"similarity index 77%\n" \
"rename from file.txt\n" \
"rename to \"foo\\\"bar.txt\"\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"+++ \"b/foo\\\"bar.txt\"\n" \
"@@ -3,7 +3,7 @@ this is some context!\n" \
" around some lines\n" \
" that will change\n" \
" yes it is!\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n" \
" and this\n" \
" is additional context\n" \
" below it!\n"
#define PATCH_MODECHANGE_UNCHANGED \
"diff --git a/file.txt b/file.txt\n" \
"old mode 100644\n" \
"new mode 100755\n"
#define PATCH_MODECHANGE_MODIFIED \
"diff --git a/file.txt b/file.txt\n" \
"old mode 100644\n" \
"new mode 100755\n" \
"index 9432026..cd8fd12\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -3,7 +3,7 @@ this is some context!\n" \
" around some lines\n" \
" that will change\n" \
" yes it is!\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n" \
" and this\n" \
" is additional context\n" \
" below it!\n"
#define PATCH_NOISY \
"This is some\nleading noise\n@@ - that\nlooks like a hunk header\n" \
"but actually isn't and should parse ok\n" \
PATCH_ORIGINAL_TO_CHANGE_MIDDLE \
"plus some trailing garbage for good measure\n"
#define PATCH_NOISY_NOCONTEXT \
"This is some\nleading noise\n@@ - that\nlooks like a hunk header\n" \
"but actually isn't and should parse ok\n" \
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT \
"plus some trailing garbage for good measure\n"
#define PATCH_TRUNCATED_1 \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -3,7 +3,7 @@ this is some context!\n" \
" around some lines\n" \
" that will change\n" \
" yes it is!\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n" \
" and this\n"
#define PATCH_TRUNCATED_2 \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -3,7 +3,7 @@ this is some context!\n" \
" around some lines\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n" \
" and this\n" \
" is additional context\n" \
" below it!\n"
#define PATCH_TRUNCATED_3 \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -3,7 +3,7 @@ this is some context!\n" \
" around some lines\n" \
" that will change\n" \
" yes it is!\n" \
"+(THIS line is changed!)\n" \
" and this\n" \
" is additional context\n" \
" below it!\n"
#define FILE_EMPTY_CONTEXT_ORIGINAL \
"this\nhas\nan\n\nempty\ncontext\nline\n"
#define FILE_EMPTY_CONTEXT_MODIFIED \
"this\nhas\nan\n\nempty...\ncontext\nline\n"
#define PATCH_EMPTY_CONTEXT \
"diff --git a/file.txt b/file.txt\n" \
"index 398d2df..bb15234 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -2,6 +2,6 @@ this\n" \
" has\n" \
" an\n" \
"\n" \
"-empty\n" \
"+empty...\n" \
" context\n" \
" line\n"
#define FILE_APPEND_NO_NL \
"hey!\n" \
"this is some context!\n" \
"around some lines\n" \
"that will change\n" \
"yes it is!\n" \
"(this line is changed)\n" \
"and this\n" \
"is additional context\n" \
"below it!\n" \
"added line with no nl"
#define PATCH_APPEND_NO_NL \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..83759c0 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -7,3 +7,4 @@ yes it is!\n" \
" and this\n" \
" is additional context\n" \
" below it!\n" \
"+added line with no nl\n" \
"\\ No newline at end of file\n"
#define PATCH_CORRUPT_GIT_HEADER \
"diff --git a/file.txt\n" \
"index 9432026..0f39b9a 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -0,0 +1 @@\n" \
"+insert at front\n"
#define PATCH_CORRUPT_MISSING_NEW_FILE \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"@@ -6 +6 @@ yes it is!\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n"
#define PATCH_CORRUPT_MISSING_OLD_FILE \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"+++ b/file.txt\n" \
"@@ -6 +6 @@ yes it is!\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n"
#define PATCH_CORRUPT_NO_CHANGES \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"@@ -0,0 +0,0 @@ yes it is!\n"
#define PATCH_CORRUPT_MISSING_HUNK_HEADER \
"diff --git a/file.txt b/file.txt\n" \
"index 9432026..cd8fd12 100644\n" \
"--- a/file.txt\n" \
"+++ b/file.txt\n" \
"-(this line is changed)\n" \
"+(THIS line is changed!)\n"
#define PATCH_NOT_A_PATCH \
"+++this is not\n" \
"--actually even\n" \
" a legitimate \n" \
"+patch file\n" \
"-it's something else\n" \
" entirely!"
tests/apply/fromfile.c
0 → 100644
View file @
d34f6826
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "apply.h"
#include "repository.h"
#include "buf_text.h"
#include "apply_common.h"
static
git_repository
*
repo
=
NULL
;
void
test_apply_fromfile__initialize
(
void
)
{
repo
=
cl_git_sandbox_init
(
"renames"
);
}
void
test_apply_fromfile__cleanup
(
void
)
{
cl_git_sandbox_cleanup
();
}
static
int
apply_patchfile
(
const
char
*
old
,
const
char
*
new
,
const
char
*
patchfile
,
const
char
*
filename_expected
,
unsigned
int
mode_expected
)
{
git_patch
*
patch
;
git_buf
result
=
GIT_BUF_INIT
;
git_buf
patchbuf
=
GIT_BUF_INIT
;
char
*
filename
;
unsigned
int
mode
;
int
error
;
cl_git_pass
(
git_patch_from_patchfile
(
&
patch
,
patchfile
,
strlen
(
patchfile
)));
error
=
git_apply__patch
(
&
result
,
&
filename
,
&
mode
,
old
,
old
?
strlen
(
old
)
:
0
,
patch
);
if
(
error
==
0
)
{
if
(
new
==
NULL
)
cl_assert_equal_i
(
0
,
result
.
size
);
else
cl_assert_equal_s
(
new
,
result
.
ptr
);
cl_assert_equal_s
(
filename_expected
,
filename
);
cl_assert_equal_i
(
mode_expected
,
mode
);
}
git__free
(
filename
);
git_buf_free
(
&
result
);
git_buf_free
(
&
patchbuf
);
git_patch_free
(
patch
);
return
error
;
}
static
int
validate_and_apply_patchfile
(
const
char
*
old
,
const
char
*
new
,
const
char
*
patchfile
,
const
git_diff_options
*
diff_opts
,
const
char
*
filename_expected
,
unsigned
int
mode_expected
)
{
git_patch
*
patch_fromdiff
;
git_buf
validated
=
GIT_BUF_INIT
;
int
error
;
cl_git_pass
(
git_patch_from_buffers
(
&
patch_fromdiff
,
old
,
old
?
strlen
(
old
)
:
0
,
"file.txt"
,
new
,
new
?
strlen
(
new
)
:
0
,
"file.txt"
,
diff_opts
));
cl_git_pass
(
git_patch_to_buf
(
&
validated
,
patch_fromdiff
));
cl_assert_equal_s
(
patchfile
,
validated
.
ptr
);
error
=
apply_patchfile
(
old
,
new
,
patchfile
,
filename_expected
,
mode_expected
);
git_buf_free
(
&
validated
);
git_patch_free
(
patch_fromdiff
);
return
error
;
}
void
test_apply_fromfile__change_middle
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_ORIGINAL_TO_CHANGE_MIDDLE
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__change_middle_nocontext
(
void
)
{
git_diff_options
diff_opts
=
GIT_DIFF_OPTIONS_INIT
;
diff_opts
.
context_lines
=
0
;
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT
,
&
diff_opts
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__change_firstline
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_FIRSTLINE
,
PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__lastline
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_LASTLINE
,
PATCH_ORIGINAL_TO_CHANGE_LASTLINE
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__prepend
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_PREPEND
,
PATCH_ORIGINAL_TO_PREPEND
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__prepend_nocontext
(
void
)
{
git_diff_options
diff_opts
=
GIT_DIFF_OPTIONS_INIT
;
diff_opts
.
context_lines
=
0
;
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_PREPEND
,
PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT
,
&
diff_opts
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__append
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_APPEND
,
PATCH_ORIGINAL_TO_APPEND
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__append_nocontext
(
void
)
{
git_diff_options
diff_opts
=
GIT_DIFF_OPTIONS_INIT
;
diff_opts
.
context_lines
=
0
;
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_APPEND
,
PATCH_ORIGINAL_TO_APPEND_NOCONTEXT
,
&
diff_opts
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__prepend_and_append
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_PREPEND_AND_APPEND
,
PATCH_ORIGINAL_TO_PREPEND_AND_APPEND
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__to_empty_file
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
""
,
PATCH_ORIGINAL_TO_EMPTY_FILE
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__from_empty_file
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
""
,
FILE_ORIGINAL
,
PATCH_EMPTY_FILE_TO_ORIGINAL
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__add
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
NULL
,
FILE_ORIGINAL
,
PATCH_ADD_ORIGINAL
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__delete
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
NULL
,
PATCH_DELETE_ORIGINAL
,
NULL
,
NULL
,
0
));
}
void
test_apply_fromfile__rename_exact
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_ORIGINAL
,
PATCH_RENAME_EXACT
,
"b/newfile.txt"
,
0100644
));
}
void
test_apply_fromfile__rename_similar
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_RENAME_SIMILAR
,
"b/newfile.txt"
,
0100644
));
}
void
test_apply_fromfile__rename_similar_quotedname
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_RENAME_SIMILAR_QUOTEDNAME
,
"b/foo
\"
bar.txt"
,
0100644
));
}
void
test_apply_fromfile__modechange
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_ORIGINAL
,
PATCH_MODECHANGE_UNCHANGED
,
"b/file.txt"
,
0100755
));
}
void
test_apply_fromfile__modechange_with_modification
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_MODECHANGE_MODIFIED
,
"b/file.txt"
,
0100755
));
}
void
test_apply_fromfile__noisy
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_NOISY
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__noisy_nocontext
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_ORIGINAL
,
FILE_CHANGE_MIDDLE
,
PATCH_NOISY_NOCONTEXT
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__fail_truncated_1
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_TRUNCATED_1
,
strlen
(
PATCH_TRUNCATED_1
)));
}
void
test_apply_fromfile__fail_truncated_2
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_TRUNCATED_2
,
strlen
(
PATCH_TRUNCATED_2
)));
}
void
test_apply_fromfile__fail_truncated_3
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_TRUNCATED_3
,
strlen
(
PATCH_TRUNCATED_3
)));
}
void
test_apply_fromfile__fail_corrupt_githeader
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_CORRUPT_GIT_HEADER
,
strlen
(
PATCH_CORRUPT_GIT_HEADER
)));
}
void
test_apply_fromfile__empty_context
(
void
)
{
cl_git_pass
(
apply_patchfile
(
FILE_EMPTY_CONTEXT_ORIGINAL
,
FILE_EMPTY_CONTEXT_MODIFIED
,
PATCH_EMPTY_CONTEXT
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__append_no_nl
(
void
)
{
cl_git_pass
(
validate_and_apply_patchfile
(
FILE_ORIGINAL
,
FILE_APPEND_NO_NL
,
PATCH_APPEND_NO_NL
,
NULL
,
"b/file.txt"
,
0100644
));
}
void
test_apply_fromfile__fail_missing_new_file
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_CORRUPT_MISSING_NEW_FILE
,
strlen
(
PATCH_CORRUPT_MISSING_NEW_FILE
)));
}
void
test_apply_fromfile__fail_missing_old_file
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_CORRUPT_MISSING_OLD_FILE
,
strlen
(
PATCH_CORRUPT_MISSING_OLD_FILE
)));
}
void
test_apply_fromfile__fail_no_changes
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_CORRUPT_NO_CHANGES
,
strlen
(
PATCH_CORRUPT_NO_CHANGES
)));
}
void
test_apply_fromfile__fail_missing_hunk_header
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_CORRUPT_MISSING_HUNK_HEADER
,
strlen
(
PATCH_CORRUPT_MISSING_HUNK_HEADER
)));
}
void
test_apply_fromfile__fail_not_a_patch
(
void
)
{
git_patch
*
patch
;
cl_git_fail
(
git_patch_from_patchfile
(
&
patch
,
PATCH_NOT_A_PATCH
,
strlen
(
PATCH_NOT_A_PATCH
)));
}
tests/buf/quote.c
0 → 100644
View file @
d34f6826
#include "clar_libgit2.h"
#include "buffer.h"
static
void
expect_pass
(
const
char
*
expected
,
const
char
*
quoted
)
{
git_buf
buf
=
GIT_BUF_INIT
;
cl_git_pass
(
git_buf_puts
(
&
buf
,
quoted
));
cl_git_pass
(
git_buf_unquote
(
&
buf
));
cl_assert_equal_s
(
expected
,
git_buf_cstr
(
&
buf
));
cl_assert_equal_i
(
strlen
(
expected
),
git_buf_len
(
&
buf
));
git_buf_free
(
&
buf
);
}
static
void
expect_fail
(
const
char
*
quoted
)
{
git_buf
buf
=
GIT_BUF_INIT
;
cl_git_pass
(
git_buf_puts
(
&
buf
,
quoted
));
cl_git_fail
(
git_buf_unquote
(
&
buf
));
git_buf_free
(
&
buf
);
}
void
test_buf_quote__unquote_succeeds
(
void
)
{
expect_pass
(
""
,
"
\"\"
"
);
expect_pass
(
" "
,
"
\"
\"
"
);
expect_pass
(
"foo"
,
"
\"
foo
\"
"
);
expect_pass
(
"foo bar"
,
"
\"
foo bar
\"
"
);
expect_pass
(
"foo
\"
bar"
,
"
\"
foo
\\\"
bar
\"
"
);
expect_pass
(
"foo
\\
bar"
,
"
\"
foo
\\\\
bar
\"
"
);
expect_pass
(
"foo
\t
bar"
,
"
\"
foo
\\
tbar
\"
"
);
expect_pass
(
"
\v
foo
\t
bar
\n
"
,
"
\"\\
vfoo
\\
tbar
\\
n
\"
"
);
expect_pass
(
"foo
\n
bar"
,
"
\"
foo
\\
012bar
\"
"
);
expect_pass
(
"foo
\r\n
bar"
,
"
\"
foo
\\
015
\\
012bar
\"
"
);
expect_pass
(
"foo
\r\n
bar"
,
"
\"\\
146
\\
157
\\
157
\\
015
\\
012
\\
142
\\
141
\\
162
\"
"
);
expect_pass
(
"newline:
\n
"
,
"
\"
newline:
\\
012
\"
"
);
}
void
test_buf_quote__unquote_fails
(
void
)
{
expect_fail
(
"no quotes at all"
);
expect_fail
(
"
\"
no trailing quote"
);
expect_fail
(
"no leading quote
\"
"
);
expect_fail
(
"
\"
invalid
\\
z escape char
\"
"
);
expect_fail
(
"
\"\\
q invalid escape char
\"
"
);
expect_fail
(
"
\"
invalid escape char
\\
p
\"
"
);
expect_fail
(
"
\"
invalid
\\
1 escape char
\"
"
);
expect_fail
(
"
\"
invalid
\\
14 escape char
\"
"
);
expect_fail
(
"
\"
invalid
\\
411 escape char
\"
"
);
expect_fail
(
"
\"
truncated escape char
\\\"
"
);
expect_fail
(
"
\"
truncated escape char
\\
0
\"
"
);
expect_fail
(
"
\"
truncated escape char
\\
01
\"
"
);
}
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