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
3aa443a9
Commit
3aa443a9
authored
Aug 20, 2012
by
nulltoken
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
checkout: introduce git_checkout_tree()
parent
e8776d30
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
431 additions
and
245 deletions
+431
-245
include/git2/checkout.h
+27
-2
src/checkout.c
+260
-143
src/clone.c
+1
-2
src/filter.c
+8
-24
src/filter.h
+3
-4
tests-clar/checkout/tree.c
+132
-70
No files found.
include/git2/checkout.h
View file @
3aa443a9
...
@@ -32,10 +32,16 @@ typedef struct git_checkout_opts {
...
@@ -32,10 +32,16 @@ typedef struct git_checkout_opts {
int
dir_mode
;
/* default is 0755 */
int
dir_mode
;
/* default is 0755 */
int
file_mode
;
/* default is 0644 */
int
file_mode
;
/* default is 0644 */
int
file_open_flags
;
/* default is O_CREAT | O_TRUNC | O_WRONLY */
int
file_open_flags
;
/* default is O_CREAT | O_TRUNC | O_WRONLY */
/* when not NULL, arrays of fnmatch pattern specifying
* which paths should be taken into account
*/
git_strarray
*
paths
;
}
git_checkout_opts
;
}
git_checkout_opts
;
/**
/**
* Updates files in the working tree to match the commit pointed to by HEAD.
* Updates files in the index and the working tree to match the content of the
* commit pointed at by HEAD.
*
*
* @param repo repository to check out (must be non-bare)
* @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL)
* @param opts specifies checkout options (may be NULL)
...
@@ -49,7 +55,9 @@ GIT_EXTERN(int) git_checkout_head(
...
@@ -49,7 +55,9 @@ GIT_EXTERN(int) git_checkout_head(
git_indexer_stats
*
stats
);
git_indexer_stats
*
stats
);
/**
/**
* Updates files in the working tree to match a commit pointed to by a ref.
* Updates files in the index and the working tree to match the content of the
* commit pointed at by the reference.
*
*
*
* @param ref reference to follow to a commit
* @param ref reference to follow to a commit
* @param opts specifies checkout options (may be NULL)
* @param opts specifies checkout options (may be NULL)
...
@@ -62,6 +70,23 @@ GIT_EXTERN(int) git_checkout_reference(
...
@@ -62,6 +70,23 @@ GIT_EXTERN(int) git_checkout_reference(
git_checkout_opts
*
opts
,
git_checkout_opts
*
opts
,
git_indexer_stats
*
stats
);
git_indexer_stats
*
stats
);
/**
* Updates files in the index and working tree to match the content of the
* tree pointed at by the treeish.
*
* @param repo repository to check out (must be non-bare)
* @param treeish a commit, tag or tree which content will be used to update
* the working directory
* @param opts specifies checkout options (may be NULL)
* @param stats structure through which progress information is reported
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
*/
GIT_EXTERN
(
int
)
git_checkout_tree
(
git_repository
*
repo
,
git_object
*
treeish
,
git_checkout_opts
*
opts
,
git_indexer_stats
*
stats
);
/** @} */
/** @} */
GIT_END_DECL
GIT_END_DECL
...
...
src/checkout.c
View file @
3aa443a9
...
@@ -11,9 +11,9 @@
...
@@ -11,9 +11,9 @@
#include "git2/repository.h"
#include "git2/repository.h"
#include "git2/refs.h"
#include "git2/refs.h"
#include "git2/tree.h"
#include "git2/tree.h"
#include "git2/commit.h"
#include "git2/blob.h"
#include "git2/blob.h"
#include "git2/config.h"
#include "git2/config.h"
#include "git2/diff.h"
#include "common.h"
#include "common.h"
#include "refs.h"
#include "refs.h"
...
@@ -22,204 +22,321 @@
...
@@ -22,204 +22,321 @@
#include "filter.h"
#include "filter.h"
#include "blob.h"
#include "blob.h"
typedef
struct
tree_walk
_data
struct
checkout_diff
_data
{
{
git_buf
*
path
;
int
workdir_len
;
git_checkout_opts
*
checkout_opts
;
git_indexer_stats
*
stats
;
git_indexer_stats
*
stats
;
git_checkout_opts
*
opts
;
git_repository
*
owner
;
git_repository
*
repo
;
bool
can_symlink
;
git_odb
*
odb
;
};
bool
no_symlinks
;
}
tree_walk_data
;
static
int
buffer_to_file
(
git_buf
*
buffer
,
const
char
*
path
,
int
dir_mode
,
int
file_open_flags
,
mode_t
file_mode
)
{
int
fd
,
error_write
,
error_close
;
if
(
git_futils_mkpath2file
(
path
,
dir_mode
)
<
0
)
return
-
1
;
static
int
blob_contents_to_link
(
tree_walk_data
*
data
,
git_buf
*
fnbuf
,
if
((
fd
=
p_open
(
path
,
file_open_flags
,
file_mode
))
<
0
)
const
git_oid
*
id
)
return
-
1
;
{
int
retcode
=
GIT_ERROR
;
git_blob
*
blob
;
/* Get the link target */
error_write
=
p_write
(
fd
,
git_buf_cstr
(
buffer
),
git_buf_len
(
buffer
));
if
(
!
(
retcode
=
git_blob_lookup
(
&
blob
,
data
->
repo
,
id
)))
{
error_close
=
p_close
(
fd
);
git_buf
linktarget
=
GIT_BUF_INIT
;
if
(
!
(
retcode
=
git_blob__getbuf
(
&
linktarget
,
blob
)))
{
/* Create the link */
const
char
*
new
=
git_buf_cstr
(
&
linktarget
),
*
old
=
git_buf_cstr
(
fnbuf
);
retcode
=
data
->
no_symlinks
?
git_futils_fake_symlink
(
new
,
old
)
:
p_symlink
(
new
,
old
);
}
git_buf_free
(
&
linktarget
);
git_blob_free
(
blob
);
}
return
retcod
e
;
return
error_write
?
error_write
:
error_clos
e
;
}
}
static
int
blob_content_to_file
(
static
int
blob_contents_to_file
(
git_repository
*
repo
,
git_buf
*
fnbuf
,
git_blob
*
blob
,
const
git_tree_entry
*
entry
,
tree_walk_data
*
data
)
const
char
*
path
,
unsigned
int
entry_filemode
,
git_checkout_opts
*
opts
)
{
{
int
retcode
=
GIT_ERROR
;
int
retcode
;
int
fd
=
-
1
;
git_buf
content
=
GIT_BUF_INIT
;
git_buf
contents
=
GIT_BUF_INIT
;
int
file_mode
=
opts
->
file_mode
;
const
git_oid
*
id
=
git_tree_entry_id
(
entry
);
int
file_mode
=
data
->
opts
->
file_mode
;
/* Deal with pre-existing files */
if
(
git_path_exists
(
git_buf_cstr
(
fnbuf
))
&&
data
->
opts
->
existing_file_action
==
GIT_CHECKOUT_SKIP_EXISTING
)
return
0
;
/* Allow disabling of filters */
/* Allow disabling of filters */
if
(
data
->
opts
->
disable_filters
)
{
if
(
opts
->
disable_filters
)
git_blob
*
blob
;
retcode
=
git_blob__getbuf
(
&
content
,
blob
);
if
(
!
(
retcode
=
git_blob_lookup
(
&
blob
,
repo
,
id
)))
{
else
retcode
=
git_blob__getbuf
(
&
contents
,
blob
);
retcode
=
git_filter_blob_content
(
&
content
,
blob
,
path
);
git_blob_free
(
blob
);
}
if
(
retcode
<
0
)
}
else
{
goto
cleanup
;
retcode
=
git_filter_blob_contents
(
&
contents
,
repo
,
id
,
git_buf_cstr
(
fnbuf
));
}
if
(
retcode
<
0
)
goto
bctf_cleanup
;
/* Allow overriding of file mode */
/* Allow overriding of file mode */
if
(
!
file_mode
)
if
(
!
file_mode
)
file_mode
=
git_tree_entry_filemode
(
entry
);
file_mode
=
entry_filemode
;
retcode
=
buffer_to_file
(
&
content
,
path
,
opts
->
dir_mode
,
opts
->
file_open_flags
,
file_mode
);
cleanup:
git_buf_free
(
&
content
);
return
retcode
;
}
if
((
retcode
=
git_futils_mkpath2file
(
git_buf_cstr
(
fnbuf
),
data
->
opts
->
dir_mode
))
<
0
)
static
int
blob_content_to_link
(
git_blob
*
blob
,
const
char
*
path
,
bool
can_symlink
)
goto
bctf_cleanup
;
{
git_buf
linktarget
=
GIT_BUF_INIT
;
int
error
;
fd
=
p_open
(
git_buf_cstr
(
fnbuf
),
data
->
opts
->
file_open_flags
,
file_mode
);
if
(
git_blob__getbuf
(
&
linktarget
,
blob
)
<
0
)
if
(
fd
<
0
)
goto
bctf_cleanup
;
return
-
1
;
if
(
!
p_write
(
fd
,
git_buf_cstr
(
&
contents
),
git_buf_len
(
&
contents
))
)
if
(
can_symlink
)
retcode
=
0
;
error
=
p_symlink
(
git_buf_cstr
(
&
linktarget
),
path
)
;
else
else
retcode
=
GIT_ERROR
;
error
=
git_futils_fake_symlink
(
git_buf_cstr
(
&
linktarget
),
path
);
p_close
(
fd
);
bctf_cleanup:
git_buf_free
(
&
linktarget
);
git_buf_free
(
&
contents
);
return
retcode
;
return
error
;
}
}
static
int
checkout_walker
(
const
char
*
path
,
const
git_tree_entry
*
entry
,
void
*
payload
)
static
int
checkout_blob
(
git_repository
*
repo
,
git_oid
*
blob_oid
,
const
char
*
path
,
unsigned
int
filemode
,
bool
can_symlink
,
git_checkout_opts
*
opts
)
{
{
int
retcode
=
0
;
git_blob
*
blob
;
tree_walk_data
*
data
=
(
tree_walk_data
*
)
payload
;
int
error
;
int
attr
=
git_tree_entry_filemode
(
entry
);
git_buf
fnbuf
=
GIT_BUF_INIT
;
if
(
git_blob_lookup
(
&
blob
,
repo
,
blob_oid
)
<
0
)
git_buf_join_n
(
&
fnbuf
,
'/'
,
3
,
return
-
1
;
/* Add an error message */
git_repository_workdir
(
data
->
repo
),
path
,
if
(
S_ISLNK
(
filemode
))
git_tree_entry_name
(
entry
));
error
=
blob_content_to_link
(
blob
,
path
,
can_symlink
);
else
switch
(
git_tree_entry_type
(
entry
))
error
=
blob_content_to_file
(
blob
,
path
,
filemode
,
opts
);
{
case
GIT_OBJ_TREE
:
git_blob_free
(
blob
);
/* Nothing to do; the blob handling creates necessary directories. */
break
;
return
error
;
}
static
int
checkout_diff_fn
(
void
*
cb_data
,
git_diff_delta
*
delta
,
float
progress
)
{
struct
checkout_diff_data
*
data
;
int
error
=
-
1
;
data
=
(
struct
checkout_diff_data
*
)
cb_data
;
data
->
stats
->
processed
=
(
unsigned
int
)(
data
->
stats
->
total
*
progress
);
git_buf_truncate
(
data
->
path
,
data
->
workdir_len
);
if
(
git_buf_joinpath
(
data
->
path
,
git_buf_cstr
(
data
->
path
),
delta
->
new_file
.
path
)
<
0
)
return
-
1
;
case
GIT_OBJ_COMMIT
:
switch
(
delta
->
status
)
{
/* Submodule */
case
GIT_DELTA_UNTRACKED
:
git_futils_mkpath2file
(
git_buf_cstr
(
&
fnbuf
),
data
->
opts
->
dir_mode
);
if
(
!
git__suffixcmp
(
delta
->
new_file
.
path
,
"/"
))
retcode
=
p_mkdir
(
git_buf_cstr
(
&
fnbuf
),
data
->
opts
->
dir_mode
);
error
=
git_futils_rmdir_r
(
git_buf_cstr
(
data
->
path
),
GIT_DIRREMOVAL_FILES_AND_DIRS
);
else
error
=
p_unlink
(
git_buf_cstr
(
data
->
path
));
break
;
break
;
case
GIT_OBJ_BLOB
:
case
GIT_DELTA_MODIFIED
:
if
(
S_ISLNK
(
attr
))
{
/* Deal with pre-existing files */
retcode
=
blob_contents_to_link
(
data
,
&
fnbuf
,
if
(
data
->
checkout_opts
->
existing_file_action
==
GIT_CHECKOUT_SKIP_EXISTING
)
git_tree_entry_id
(
entry
));
return
0
;
}
else
{
retcode
=
blob_contents_to_file
(
data
->
repo
,
&
fnbuf
,
entry
,
data
);
case
GIT_DELTA_DELETED
:
}
if
(
checkout_blob
(
data
->
owner
,
&
delta
->
old_file
.
oid
,
git_buf_cstr
(
data
->
path
),
delta
->
old_file
.
mode
,
data
->
can_symlink
,
data
->
checkout_opts
)
<
0
)
goto
cleanup
;
break
;
break
;
default:
default:
retcode
=
-
1
;
giterr_set
(
GITERR_INVALID
,
"Unexpected status (%d) for path '%s'."
,
delta
->
status
,
delta
->
new_file
.
path
)
;
break
;
goto
cleanup
;
}
}
git_buf_free
(
&
fnbuf
);
error
=
0
;
data
->
stats
->
processed
++
;
return
retcode
;
}
cleanup:
return
error
;
}
int
git_checkout_head
(
git_repository
*
repo
,
git_checkout_opts
*
opts
,
git_indexer_stats
*
stats
)
static
int
retrieve_symlink_capabilities
(
git_repository
*
repo
,
bool
*
can_symlink
)
{
{
int
retcode
=
GIT_ERROR
;
git_indexer_stats
dummy_stats
;
git_checkout_opts
default_opts
=
{
0
};
git_tree
*
tree
;
tree_walk_data
payload
;
git_config
*
cfg
;
git_config
*
cfg
;
int
error
;
assert
(
repo
);
if
(
git_repository_config__weakptr
(
&
cfg
,
repo
)
<
0
)
if
(
!
opts
)
opts
=
&
default_opts
;
return
-
1
;
if
(
!
stats
)
stats
=
&
dummy_stats
;
error
=
git_config_get_bool
((
int
*
)
can_symlink
,
cfg
,
"core.symlinks"
);
/*
* When no "core.symlinks" entry is found in any of the configuration
* store (local, global or system), default value is "true".
*/
if
(
error
==
GIT_ENOTFOUND
)
{
*
can_symlink
=
true
;
error
=
0
;
}
return
error
;
}
static
void
normalize_options
(
git_checkout_opts
*
normalized
,
git_checkout_opts
*
proposed
)
{
assert
(
normalized
);
if
(
!
proposed
)
memset
(
normalized
,
0
,
sizeof
(
git_checkout_opts
));
else
memmove
(
normalized
,
proposed
,
sizeof
(
git_checkout_opts
));
/* Default options */
/* Default options */
if
(
!
opts
->
existing_file_action
)
if
(
!
normalized
->
existing_file_action
)
opts
->
existing_file_action
=
GIT_CHECKOUT_OVERWRITE_EXISTING
;
normalized
->
existing_file_action
=
GIT_CHECKOUT_OVERWRITE_EXISTING
;
/* opts->disable_filters is false by default */
/* opts->disable_filters is false by default */
if
(
!
opts
->
dir_mode
)
opts
->
dir_mode
=
GIT_DIR_MODE
;
if
(
!
normalized
->
dir_mode
)
if
(
!
opts
->
file_open_flags
)
normalized
->
dir_mode
=
GIT_DIR_MODE
;
opts
->
file_open_flags
=
O_CREAT
|
O_TRUNC
|
O_WRONLY
;
if
(
!
normalized
->
file_open_flags
)
normalized
->
file_open_flags
=
O_CREAT
|
O_TRUNC
|
O_WRONLY
;
}
int
git_checkout_tree
(
git_repository
*
repo
,
git_object
*
treeish
,
git_checkout_opts
*
opts
,
git_indexer_stats
*
stats
)
{
git_index
*
index
=
NULL
;
git_tree
*
tree
=
NULL
;
git_diff_list
*
diff
=
NULL
;
git_indexer_stats
dummy_stats
;
git_diff_options
diff_opts
=
{
0
};
git_checkout_opts
checkout_opts
;
struct
checkout_diff_data
data
;
git_buf
workdir
=
GIT_BUF_INIT
;
int
error
;
assert
(
repo
&&
treeish
);
if
(
git_repository_is_bare
(
repo
))
{
if
((
git_repository__ensure_not_bare
(
repo
,
"checkout"
))
<
0
)
giterr_set
(
GITERR_INVALID
,
"Checkout is not allowed for bare repositories"
);
return
GIT_EBAREREPO
;
if
(
git_object_peel
((
git_object
**
)
&
tree
,
treeish
,
GIT_OBJ_TREE
)
<
0
)
{
giterr_set
(
GITERR_INVALID
,
"Provided treeish cannot be peeled into a tree."
);
return
GIT_ERROR
;
return
GIT_ERROR
;
}
}
memset
(
&
payload
,
0
,
sizeof
(
payload
));
if
((
error
=
git_repository_index
(
&
index
,
repo
))
<
0
)
goto
cleanup
;
/* Determine if symlinks should be handled */
if
((
error
=
git_index_read_tree
(
index
,
tree
,
NULL
))
<
0
)
if
(
!
git_repository_config__weakptr
(
&
cfg
,
repo
))
{
goto
cleanup
;
int
temp
=
true
;
if
(
!
git_config_get_bool
(
&
temp
,
cfg
,
"core.symlinks"
))
{
payload
.
no_symlinks
=
!
temp
;
}
}
stats
->
total
=
stats
->
processed
=
0
;
if
((
error
=
git_index_write
(
index
))
<
0
)
payload
.
stats
=
stats
;
goto
cleanup
;
payload
.
opts
=
opts
;
payload
.
repo
=
repo
;
diff_opts
.
flags
=
GIT_DIFF_INCLUDE_UNTRACKED
;
if
(
git_repository_odb
(
&
payload
.
odb
,
repo
)
<
0
)
return
GIT_ERROR
;
if
(
opts
&&
opts
->
paths
)
{
if
(
!
git_repository_head_tree
(
&
tree
,
repo
))
{
diff_opts
.
pathspec
.
strings
=
opts
->
paths
->
strings
;
git_index
*
idx
;
diff_opts
.
pathspec
.
count
=
opts
->
paths
->
count
;
if
(
!
(
retcode
=
git_repository_index
(
&
idx
,
repo
)))
{
if
(
!
(
retcode
=
git_index_read_tree
(
idx
,
tree
,
stats
)))
{
git_index_write
(
idx
);
retcode
=
git_tree_walk
(
tree
,
checkout_walker
,
GIT_TREEWALK_POST
,
&
payload
);
}
git_index_free
(
idx
);
}
git_tree_free
(
tree
);
}
}
git_odb_free
(
payload
.
odb
);
if
((
error
=
git_diff_workdir_to_index
(
repo
,
&
diff_opts
,
&
diff
))
<
0
)
return
retcode
;
goto
cleanup
;
if
((
error
=
git_buf_puts
(
&
workdir
,
git_repository_workdir
(
repo
)))
<
0
)
goto
cleanup
;
normalize_options
(
&
checkout_opts
,
opts
);
if
(
!
stats
)
stats
=
&
dummy_stats
;
stats
->
processed
=
0
;
stats
->
total
=
git_index_entrycount
(
index
);
memset
(
&
data
,
0
,
sizeof
(
data
));
data
.
path
=
&
workdir
;
data
.
workdir_len
=
git_buf_len
(
&
workdir
);
data
.
checkout_opts
=
&
checkout_opts
;
data
.
stats
=
stats
;
data
.
owner
=
repo
;
if
((
error
=
retrieve_symlink_capabilities
(
repo
,
&
data
.
can_symlink
))
<
0
)
goto
cleanup
;
error
=
git_diff_foreach
(
diff
,
&
data
,
checkout_diff_fn
,
NULL
,
NULL
);
cleanup:
git_diff_list_free
(
diff
);
git_index_free
(
index
);
git_tree_free
(
tree
);
git_buf_free
(
&
workdir
);
return
error
;
}
}
int
git_checkout_head
(
git_repository
*
repo
,
git_checkout_opts
*
opts
,
git_indexer_stats
*
stats
)
{
int
error
;
git_tree
*
tree
=
NULL
;
assert
(
repo
);
if
(
git_repository_head_tree
(
&
tree
,
repo
)
<
0
)
return
-
1
;
error
=
git_checkout_tree
(
repo
,
(
git_object
*
)
tree
,
opts
,
stats
);
git_tree_free
(
tree
);
int
git_checkout_reference
(
git_reference
*
ref
,
return
error
;
}
int
git_checkout_reference
(
git_reference
*
ref
,
git_checkout_opts
*
opts
,
git_checkout_opts
*
opts
,
git_indexer_stats
*
stats
)
git_indexer_stats
*
stats
)
{
{
git_repository
*
repo
=
git_reference_owner
(
ref
);
git_repository
*
repo
=
git_reference_owner
(
ref
);
git_reference
*
head
=
NULL
;
git_reference
*
head
=
NULL
;
int
retcode
=
GIT_ERROR
;
int
error
;
if
((
retcode
=
git_reference_create_symbolic
(
&
head
,
repo
,
GIT_HEAD_FILE
,
if
((
error
=
git_reference_create_symbolic
(
git_reference_name
(
ref
),
true
))
<
0
)
&
head
,
repo
,
GIT_HEAD_FILE
,
git_reference_name
(
ref
),
true
))
<
0
)
return
retcode
;
return
error
;
retcode
=
git_checkout_head
(
git_reference_owner
(
ref
),
opts
,
stats
);
error
=
git_checkout_head
(
git_reference_owner
(
ref
),
opts
,
stats
);
git_reference_free
(
head
);
git_reference_free
(
head
);
return
retcode
;
return
error
;
}
}
src/clone.c
View file @
3aa443a9
...
@@ -235,9 +235,8 @@ int git_clone(git_repository **out,
...
@@ -235,9 +235,8 @@ int git_clone(git_repository **out,
assert
(
out
&&
origin_url
&&
workdir_path
);
assert
(
out
&&
origin_url
&&
workdir_path
);
if
(
!
(
retcode
=
clone_internal
(
out
,
origin_url
,
workdir_path
,
fetch_stats
,
0
)))
{
if
(
!
(
retcode
=
clone_internal
(
out
,
origin_url
,
workdir_path
,
fetch_stats
,
0
)))
retcode
=
git_checkout_head
(
*
out
,
checkout_opts
,
checkout_stats
);
retcode
=
git_checkout_head
(
*
out
,
checkout_opts
,
checkout_stats
);
}
return
retcode
;
return
retcode
;
}
}
src/filter.c
View file @
3aa443a9
...
@@ -165,37 +165,21 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
...
@@ -165,37 +165,21 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
return
0
;
return
0
;
}
}
static
int
unfiltered_blob_contents
(
git_buf
*
out
,
git_repository
*
repo
,
const
git_oid
*
blob_id
)
int
git_filter_blob_content
(
git_buf
*
out
,
git_blob
*
blob
,
const
char
*
hintpath
)
{
{
int
retcode
=
GIT_ERROR
;
git_blob
*
blob
;
if
(
!
(
retcode
=
git_blob_lookup
(
&
blob
,
repo
,
blob_id
)))
{
retcode
=
git_blob__getbuf
(
out
,
blob
);
git_blob_free
(
blob
);
}
return
retcode
;
}
int
git_filter_blob_contents
(
git_buf
*
out
,
git_repository
*
repo
,
const
git_oid
*
oid
,
const
char
*
path
)
{
int
retcode
=
GIT_ERROR
;
git_buf
unfiltered
=
GIT_BUF_INIT
;
git_buf
unfiltered
=
GIT_BUF_INIT
;
if
(
!
unfiltered_blob_contents
(
&
unfiltered
,
repo
,
oid
))
{
git_vector
filters
=
GIT_VECTOR_INIT
;
git_vector
filters
=
GIT_VECTOR_INIT
;
if
(
git_filters_load
(
&
filters
,
int
retcode
;
repo
,
path
,
GIT_FILTER_TO_WORKTREE
)
>=
0
)
{
retcode
=
git_blob__getbuf
(
&
unfiltered
,
blob
);
git_buf_clear
(
out
);
git_buf_clear
(
out
);
if
(
git_filters_load
(
&
filters
,
git_object_owner
((
git_object
*
)
blob
),
hintpath
,
GIT_FILTER_TO_WORKTREE
)
>=
0
)
retcode
=
git_filters_apply
(
out
,
&
unfiltered
,
&
filters
);
retcode
=
git_filters_apply
(
out
,
&
unfiltered
,
&
filters
);
}
git_filters_free
(
&
filters
);
git_filters_free
(
&
filters
);
}
git_buf_free
(
&
unfiltered
);
git_buf_free
(
&
unfiltered
);
return
retcode
;
return
retcode
;
}
}
src/filter.h
View file @
3aa443a9
...
@@ -124,11 +124,10 @@ extern int git_text_is_binary(git_text_stats *stats);
...
@@ -124,11 +124,10 @@ extern int git_text_is_binary(git_text_stats *stats);
* Get the content of a blob after all filters have been run.
* Get the content of a blob after all filters have been run.
*
*
* @param out buffer to receive the contents
* @param out buffer to receive the contents
* @param repo repository containing the blob
* @param hintpath path to the blob's output file, relative to the workdir root.
* @param oid object id for the blob
* Used to determine what git filters should be applied to the content.
* @param path path to the blob's output file, relative to the workdir root
* @return 0 on success, an error code otherwise
* @return 0 on success, an error code otherwise
*/
*/
extern
int
git_filter_blob_content
s
(
git_buf
*
out
,
git_repository
*
repo
,
const
git_oid
*
oid
,
const
char
*
path
);
extern
int
git_filter_blob_content
(
git_buf
*
out
,
git_blob
*
blob
,
const
char
*
hint
path
);
#endif
#endif
tests-clar/checkout/
checkout
.c
→
tests-clar/checkout/
tree
.c
View file @
3aa443a9
...
@@ -5,21 +5,28 @@
...
@@ -5,21 +5,28 @@
static
git_repository
*
g_repo
;
static
git_repository
*
g_repo
;
static
git_object
*
g_treeish
;
static
git_checkout_opts
g_opts
;
void
test_checkout_
checkout
__initialize
(
void
)
void
test_checkout_
tree
__initialize
(
void
)
{
{
const
char
*
attributes
=
"* text eol=lf
\n
"
;
memset
(
&
g_opts
,
0
,
sizeof
(
g_opts
))
;
g_repo
=
cl_git_sandbox_init
(
"testrepo"
);
g_repo
=
cl_git_sandbox_init
(
"testrepo"
);
cl_git_mkfile
(
"./testrepo/.gitattributes"
,
attributes
);
cl_git_rewritefile
(
"./testrepo/.gitattributes"
,
"* text eol=lf
\n
"
);
cl_git_pass
(
git_repository_head_tree
((
git_tree
**
)
&
g_treeish
,
g_repo
));
}
}
void
test_checkout_
checkout
__cleanup
(
void
)
void
test_checkout_
tree
__cleanup
(
void
)
{
{
git_object_free
(
g_treeish
);
cl_git_sandbox_cleanup
();
cl_git_sandbox_cleanup
();
}
}
static
void
test_file_contents
(
const
char
*
path
,
const
char
*
expectedcontents
)
static
void
test_file_contents
(
const
char
*
path
,
const
char
*
expectedcontents
)
{
{
int
fd
;
int
fd
;
...
@@ -37,70 +44,105 @@ static void test_file_contents(const char *path, const char *expectedcontents)
...
@@ -37,70 +44,105 @@ static void test_file_contents(const char *path, const char *expectedcontents)
cl_assert_equal_s
(
buffer
,
expectedcontents
);
cl_assert_equal_s
(
buffer
,
expectedcontents
);
}
}
void
test_checkout_tree__cannot_checkout_a_bare_repository
(
void
)
void
test_checkout_checkout__bare
(
void
)
{
{
cl_git_sandbox_cleanup
();
test_checkout_tree__cleanup
();
memset
(
&
g_opts
,
0
,
sizeof
(
g_opts
));
g_repo
=
cl_git_sandbox_init
(
"testrepo.git"
);
g_repo
=
cl_git_sandbox_init
(
"testrepo.git"
);
cl_git_fail
(
git_checkout_head
(
g_repo
,
NULL
,
NULL
));
cl_git_pass
(
git_repository_head_tree
((
git_tree
**
)
&
g_treeish
,
g_repo
));
cl_git_fail
(
git_checkout_tree
(
g_repo
,
g_treeish
,
NULL
,
NULL
));
}
}
void
test_checkout_
checkout__default
(
void
)
void
test_checkout_
tree__update_the_content_of_workdir_with_missing_files
(
void
)
{
{
cl_git_pass
(
git_checkout_head
(
g_repo
,
NULL
,
NULL
));
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/README"
));
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/branch_file.txt"
));
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/new.txt"
));
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
NULL
,
NULL
));
test_file_contents
(
"./testrepo/README"
,
"hey there
\n
"
);
test_file_contents
(
"./testrepo/README"
,
"hey there
\n
"
);
test_file_contents
(
"./testrepo/branch_file.txt"
,
"hi
\n
bye!
\n
"
);
test_file_contents
(
"./testrepo/branch_file.txt"
,
"hi
\n
bye!
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
}
}
void
test_checkout_tree__honor_the_specified_pathspecs
(
void
)
{
git_strarray
paths
;
char
*
entries
[]
=
{
"*.txt"
};
paths
.
strings
=
entries
;
paths
.
count
=
1
;
g_opts
.
paths
=
&
paths
;
void
test_checkout_checkout__crlf
(
void
)
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/README"
));
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/branch_file.txt"
));
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/new.txt"
));
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
cl_assert_equal_i
(
false
,
git_path_isfile
(
"./testrepo/README"
));
test_file_contents
(
"./testrepo/branch_file.txt"
,
"hi
\n
bye!
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
}
static
void
set_config_entry_to
(
const
char
*
entry_name
,
bool
value
)
{
git_config
*
cfg
;
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_set_bool
(
cfg
,
entry_name
,
value
));
git_config_free
(
cfg
);
}
static
void
set_core_autocrlf_to
(
bool
value
)
{
set_config_entry_to
(
"core.autocrlf"
,
value
);
}
void
test_checkout_tree__honor_the_gitattributes_directives
(
void
)
{
{
const
char
*
attributes
=
const
char
*
attributes
=
"branch_file.txt text eol=crlf
\n
"
"branch_file.txt text eol=crlf
\n
"
"new.txt text eol=lf
\n
"
;
"new.txt text eol=lf
\n
"
;
git_config
*
cfg
;
cl_git_pass
(
git_repository_config__weakptr
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_set_bool
(
cfg
,
"core.autocrlf"
,
false
));
cl_git_mkfile
(
"./testrepo/.gitattributes"
,
attributes
);
cl_git_mkfile
(
"./testrepo/.gitattributes"
,
attributes
);
set_core_autocrlf_to
(
false
);
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
NULL
,
NULL
));
cl_git_pass
(
git_checkout_head
(
g_repo
,
NULL
,
NULL
));
test_file_contents
(
"./testrepo/README"
,
"hey there
\n
"
);
test_file_contents
(
"./testrepo/README"
,
"hey there
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
test_file_contents
(
"./testrepo/branch_file.txt"
,
"hi
\r\n
bye!
\r\n
"
);
test_file_contents
(
"./testrepo/branch_file.txt"
,
"hi
\r\n
bye!
\r\n
"
);
}
}
void
test_checkout_tree__honor_coreautocrlf_setting_set_to_true
(
void
)
void
test_checkout_checkout__win32_autocrlf
(
void
)
{
{
#ifdef GIT_WIN32
#ifdef GIT_WIN32
git_config
*
cfg
;
const
char
*
expected_readme_text
=
"hey there
\r\n
"
;
const
char
*
expected_readme_text
=
"hey there
\r\n
"
;
cl_must_pass
(
p_unlink
(
"./testrepo/.gitattributes"
));
cl_git_pass
(
p_unlink
(
"./testrepo/.gitattributes"
));
cl_git_pass
(
git_repository_config__weakptr
(
&
cfg
,
g_repo
));
set_core_autocrlf_to
(
true
);
cl_git_pass
(
git_config_set_bool
(
cfg
,
"core.autocrlf"
,
true
));
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
NULL
,
NULL
));
cl_git_pass
(
git_checkout_head
(
g_repo
,
NULL
,
NULL
));
test_file_contents
(
"./testrepo/README"
,
expected_readme_text
);
test_file_contents
(
"./testrepo/README"
,
expected_readme_text
);
#endif
#endif
}
}
static
void
set_repo_symlink_handling_cap_to
(
bool
value
)
static
void
enable_symlinks
(
bool
enable
)
{
{
git_config
*
cfg
;
set_config_entry_to
(
"core.symlinks"
,
value
);
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_set_bool
(
cfg
,
"core.symlinks"
,
enable
));
git_config_free
(
cfg
);
}
}
void
test_checkout_
checkout__symlinks
(
void
)
void
test_checkout_
tree__honor_coresymlinks_setting_set_to_true
(
void
)
{
{
/* First try with symlinks forced on */
set_repo_symlink_handling_cap_to
(
true
);
enable_symlinks
(
true
);
cl_git_pass
(
git_checkout_
head
(
g_repo
,
NULL
,
NULL
));
cl_git_pass
(
git_checkout_
tree
(
g_repo
,
g_treeish
,
NULL
,
NULL
));
#ifdef GIT_WIN32
#ifdef GIT_WIN32
test_file_contents
(
"./testrepo/link_to_new.txt"
,
"new.txt"
);
test_file_contents
(
"./testrepo/link_to_new.txt"
,
"new.txt"
);
...
@@ -116,56 +158,67 @@ void test_checkout_checkout__symlinks(void)
...
@@ -116,56 +158,67 @@ void test_checkout_checkout__symlinks(void)
test_file_contents
(
"./testrepo/link_to_new.txt"
,
"my new file
\n
"
);
test_file_contents
(
"./testrepo/link_to_new.txt"
,
"my new file
\n
"
);
}
}
#endif
#endif
}
/* Now with symlinks forced off */
void
test_checkout_tree__honor_coresymlinks_setting_set_to_false
(
void
)
cl_git_sandbox_cleanup
();
{
g_repo
=
cl_git_sandbox_init
(
"testrepo"
);
set_repo_symlink_handling_cap_to
(
false
);
enable_symlinks
(
false
);
cl_git_pass
(
git_checkout_
head
(
g_repo
,
NULL
,
NULL
));
cl_git_pass
(
git_checkout_
tree
(
g_repo
,
g_treeish
,
NULL
,
NULL
));
test_file_contents
(
"./testrepo/link_to_new.txt"
,
"new.txt"
);
test_file_contents
(
"./testrepo/link_to_new.txt"
,
"new.txt"
);
}
}
void
test_checkout_
checkout__existing_file_skip
(
void
)
void
test_checkout_
tree__options_skip_existing_file
(
void
)
{
{
git_checkout_opts
opts
=
{
0
};
cl_git_mkfile
(
"./testrepo/new.txt"
,
"This isn't what's stored!"
);
cl_git_mkfile
(
"./testrepo/new.txt"
,
"This isn't what's stored!"
);
opts
.
existing_file_action
=
GIT_CHECKOUT_SKIP_EXISTING
;
g_opts
.
existing_file_action
=
GIT_CHECKOUT_SKIP_EXISTING
;
cl_git_pass
(
git_checkout_head
(
g_repo
,
&
opts
,
NULL
));
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
test_file_contents
(
"./testrepo/new.txt"
,
"This isn't what's stored!"
);
test_file_contents
(
"./testrepo/new.txt"
,
"This isn't what's stored!"
);
}
}
void
test_checkout_
checkout__existing_file_overwrit
e
(
void
)
void
test_checkout_
tree__options_overwrite_existing_fil
e
(
void
)
{
{
git_checkout_opts
opts
=
{
0
};
cl_git_mkfile
(
"./testrepo/new.txt"
,
"This isn't what's stored!"
);
cl_git_mkfile
(
"./testrepo/new.txt"
,
"This isn't what's stored!"
);
opts
.
existing_file_action
=
GIT_CHECKOUT_OVERWRITE_EXISTING
;
g_opts
.
existing_file_action
=
GIT_CHECKOUT_OVERWRITE_EXISTING
;
cl_git_pass
(
git_checkout_head
(
g_repo
,
&
opts
,
NULL
));
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
}
}
void
test_checkout_
checkout_
_disable_filters
(
void
)
void
test_checkout_
tree__options
_disable_filters
(
void
)
{
{
git_checkout_opts
opts
=
{
0
};
cl_git_mkfile
(
"./testrepo/.gitattributes"
,
"*.txt text eol=crlf
\n
"
);
cl_git_mkfile
(
"./testrepo/.gitattributes"
,
"*.txt text eol=crlf
\n
"
);
/* TODO cl_git_pass(git_checkout_head(g_repo, &opts, NULL));*/
/* TODO test_file_contents("./testrepo/new.txt", "my new file\r\n");*/
g_opts
.
disable_filters
=
false
;
opts
.
disable_filters
=
true
;
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
cl_git_pass
(
git_checkout_head
(
g_repo
,
&
opts
,
NULL
));
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\r\n
"
);
p_unlink
(
"./testrepo/new.txt"
);
g_opts
.
disable_filters
=
true
;
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"my new file
\n
"
);
}
}
void
test_checkout_
checkout_
_dir_modes
(
void
)
void
test_checkout_
tree__options
_dir_modes
(
void
)
{
{
#ifndef GIT_WIN32
#ifndef GIT_WIN32
git_checkout_opts
opts
=
{
0
};
struct
stat
st
;
struct
stat
st
;
git_reference
*
ref
;
git_oid
oid
;
git_commit
*
commit
;
cl_git_pass
(
git_reference_lookup
(
&
ref
,
g_repo
,
"refs/heads/dir"
));
cl_git_pass
(
git_reference_name_to_oid
(
&
oid
,
g_repo
,
"refs/heads/dir"
));
cl_git_pass
(
git_commit_lookup
(
&
commit
,
g_repo
,
&
oid
));
g_opts
.
dir_mode
=
0701
;
cl_git_pass
(
git_checkout_tree
(
g_repo
,
(
git_object
*
)
commit
,
&
g_opts
,
NULL
));
opts
.
dir_mode
=
0701
;
cl_git_pass
(
git_checkout_reference
(
ref
,
&
opts
,
NULL
));
cl_git_pass
(
p_stat
(
"./testrepo/a"
,
&
st
));
cl_git_pass
(
p_stat
(
"./testrepo/a"
,
&
st
));
cl_assert_equal_i
(
st
.
st_mode
&
0777
,
0701
);
cl_assert_equal_i
(
st
.
st_mode
&
0777
,
0701
);
...
@@ -173,34 +226,43 @@ void test_checkout_checkout__dir_modes(void)
...
@@ -173,34 +226,43 @@ void test_checkout_checkout__dir_modes(void)
cl_git_pass
(
p_stat
(
"./testrepo/a/b.txt"
,
&
st
));
cl_git_pass
(
p_stat
(
"./testrepo/a/b.txt"
,
&
st
));
cl_assert_equal_i
(
st
.
st_mode
&
0777
,
0755
);
cl_assert_equal_i
(
st
.
st_mode
&
0777
,
0755
);
git_
reference_free
(
ref
);
git_
commit_free
(
commit
);
#endif
#endif
}
}
void
test_checkout_
checkout_
_override_file_modes
(
void
)
void
test_checkout_
tree__options
_override_file_modes
(
void
)
{
{
#ifndef GIT_WIN32
#ifndef GIT_WIN32
git_checkout_opts
opts
=
{
0
};
struct
stat
st
;
struct
stat
st
;
opts
.
file_mode
=
0700
;
g_opts
.
file_mode
=
0700
;
cl_git_pass
(
git_checkout_head
(
g_repo
,
&
opts
,
NULL
));
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
cl_git_pass
(
p_stat
(
"./testrepo/new.txt"
,
&
st
));
cl_git_pass
(
p_stat
(
"./testrepo/new.txt"
,
&
st
));
cl_assert_equal_i
(
st
.
st_mode
&
0777
,
0700
);
cl_assert_equal_i
(
st
.
st_mode
&
0777
,
0700
);
#endif
#endif
}
}
void
test_checkout_
checkout_
_open_flags
(
void
)
void
test_checkout_
tree__options
_open_flags
(
void
)
{
{
git_checkout_opts
opts
=
{
0
};
cl_git_mkfile
(
"./testrepo/new.txt"
,
"hi
\n
"
);
cl_git_mkfile
(
"./testrepo/new.txt"
,
"hi
\n
"
);
opts
.
file_open_flags
=
O_CREAT
|
O_RDWR
|
O_APPEND
;
cl_git_pass
(
git_checkout_head
(
g_repo
,
&
opts
,
NULL
));
g_opts
.
file_open_flags
=
O_CREAT
|
O_RDWR
|
O_APPEND
;
cl_git_pass
(
git_checkout_tree
(
g_repo
,
g_treeish
,
&
g_opts
,
NULL
));
test_file_contents
(
"./testrepo/new.txt"
,
"hi
\n
my new file
\n
"
);
test_file_contents
(
"./testrepo/new.txt"
,
"hi
\n
my new file
\n
"
);
}
}
void
test_checkout_
checkout__detached_head
(
void
)
void
test_checkout_
tree__cannot_checkout_a_non_treeish
(
void
)
{
{
/* TODO: write this when git_checkout_commit is implemented. */
git_oid
oid
;
git_blob
*
blob
;
cl_git_pass
(
git_oid_fromstr
(
&
oid
,
"a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"
));
cl_git_pass
(
git_blob_lookup
(
&
blob
,
g_repo
,
&
oid
));
cl_git_fail
(
git_checkout_tree
(
g_repo
,
(
git_object
*
)
blob
,
NULL
,
NULL
));
git_blob_free
(
blob
);
}
}
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