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
f8e8c8d4
Commit
f8e8c8d4
authored
Nov 07, 2011
by
Vicent Martí
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #472 from libgit2/new-references
References! References! References!
parents
d3104fa0
62dd6d16
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
940 additions
and
753 deletions
+940
-753
include/git2/refs.h
+57
-11
src/commit.c
+16
-4
src/reflog.c
+23
-9
src/refs.c
+616
-666
src/refs.h
+6
-4
src/repository.c
+35
-14
src/tag.c
+20
-4
src/transports/local.c
+5
-2
tests/t04-commit.c
+2
-0
tests/t08-tag.c
+5
-1
tests/t10-refs.c
+151
-38
tests/t12-repo.c
+4
-0
No files found.
include/git2/refs.h
View file @
f8e8c8d4
...
...
@@ -23,8 +23,7 @@ GIT_BEGIN_DECL
/**
* Lookup a reference by its name in a repository.
*
* The generated reference is owned by the repository and
* should not be freed by the user.
* The generated reference must be freed by the user.
*
* @param reference_out pointer to the looked-up reference
* @param repo the repository to look up the reference
...
...
@@ -39,8 +38,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito
* The reference will be created in the repository and written
* to the disk.
*
* This reference is owned by the repository and shall not
* be free'd by the user.
* The generated reference must be freed by the user.
*
* If `force` is true and there already exists a reference
* with the same name, it will be overwritten.
...
...
@@ -60,8 +58,7 @@ GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repos
* The reference will be created in the repository and written
* to the disk.
*
* This reference is owned by the repository and shall not
* be free'd by the user.
* The generated reference must be freed by the user.
*
* If `force` is true and there already exists a reference
* with the same name, it will be overwritten.
...
...
@@ -119,8 +116,13 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
* Thie method iteratively peels a symbolic reference
* until it resolves to a direct reference to an OID.
*
* The peeled reference is returned in the `resolved_ref`
* argument, and must be freed manually once it's no longer
* needed.
*
* If a direct reference is passed as an argument,
* that reference is returned immediately
* a copy of that reference is returned. This copy must
* be manually freed too.
*
* @param resolved_ref Pointer to the peeled reference
* @param ref The reference
...
...
@@ -173,9 +175,19 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
* The new name will be checked for validity and may be
* modified into a normalized form.
*
* The refernece will be immediately renamed in-memory
* The given git_reference will be updated in place.
*
* The reference will be immediately renamed in-memory
* and on disk.
*
* If the `force` flag is not enabled, and there's already
* a reference with the given name, the renaming will fail.
*
* @param ref The reference to rename
* @param new_name The new name for the reference
* @param force Overwrite an existing reference
* @return GIT_SUCCESS or an error code
*
*/
GIT_EXTERN
(
int
)
git_reference_rename
(
git_reference
*
ref
,
const
char
*
new_name
,
int
force
);
...
...
@@ -187,6 +199,8 @@ GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, i
* The reference will be immediately removed on disk and from
* memory. The given reference pointer will no longer be valid.
*
* @param ref The reference to remove
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN
(
int
)
git_reference_delete
(
git_reference
*
ref
);
...
...
@@ -200,9 +214,6 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
* Once the `packed-refs` file has been written properly,
* the loose references will be removed from disk.
*
* WARNING: calling this method may invalidate any existing
* references previously loaded on the cache.
*
* @param repo Repository where the loose refs will be packed
* @return GIT_SUCCESS or an error code
*/
...
...
@@ -253,6 +264,41 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo,
*/
GIT_EXTERN
(
int
)
git_reference_foreach
(
git_repository
*
repo
,
unsigned
int
list_flags
,
int
(
*
callback
)(
const
char
*
,
void
*
),
void
*
payload
);
/**
* Check if a reference has been loaded from a packfile
*
* @param ref A git reference
* @return 0 in case it's not packed; 1 otherwise
*/
GIT_EXTERN
(
int
)
git_reference_is_packed
(
git_reference
*
ref
);
/**
* Reload a reference from disk
*
* Reference pointers may become outdated if the Git
* repository is accessed simultaneously by other clients
* whilt the library is open.
*
* This method forces a reload of the reference from disk,
* to ensure that the provided information is still
* reliable.
*
* If the reload fails (e.g. the reference no longer exists
* on disk, or has become corrupted), an error code will be
* returned and the reference pointer will be invalidated.
*
* @param ref The reference to reload
* @return GIT_SUCCESS on success, or an error code
*/
GIT_EXTERN
(
int
)
git_reference_reload
(
git_reference
*
ref
);
/**
* Free the given reference
*
* @param ref git_reference
*/
GIT_EXTERN
(
void
)
git_reference_free
(
git_reference
*
ref
);
/** @} */
GIT_END_DECL
#endif
src/commit.c
View file @
f8e8c8d4
...
...
@@ -137,15 +137,18 @@ int git_commit_create(
if
(
error
==
GIT_SUCCESS
&&
update_ref
!=
NULL
)
{
git_reference
*
head
;
git_reference
*
target
;
error
=
git_reference_lookup
(
&
head
,
repo
,
update_ref
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to create commit"
);
error
=
git_reference_resolve
(
&
head
,
head
);
error
=
git_reference_resolve
(
&
target
,
head
);
if
(
error
<
GIT_SUCCESS
)
{
if
(
error
!=
GIT_ENOTFOUND
)
if
(
error
!=
GIT_ENOTFOUND
)
{
git_reference_free
(
head
);
return
git__rethrow
(
error
,
"Failed to create commit"
);
}
/*
* The target of the reference was not found. This can happen
* just after a repository has been initialized (the master
...
...
@@ -153,10 +156,19 @@ int git_commit_create(
* point to) or after an orphan checkout, so if the target
* branch doesn't exist yet, create it and return.
*/
return
git_reference_create_oid
(
&
head
,
repo
,
git_reference_target
(
head
),
oid
,
1
);
error
=
git_reference_create_oid
(
&
target
,
repo
,
git_reference_target
(
head
),
oid
,
1
);
git_reference_free
(
head
);
if
(
error
==
GIT_SUCCESS
)
git_reference_free
(
target
);
return
error
;
}
error
=
git_reference_set_oid
(
head
,
oid
);
error
=
git_reference_set_oid
(
target
,
oid
);
git_reference_free
(
head
);
git_reference_free
(
target
);
}
if
(
error
<
GIT_SUCCESS
)
...
...
src/reflog.c
View file @
f8e8c8d4
...
...
@@ -215,23 +215,37 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
const
git_oid
*
oid
;
if
((
error
=
git_reference_resolve
(
&
r
,
ref
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to write reflog. Cannot resolve reference `%s`"
,
ref
->
name
);
return
git__rethrow
(
error
,
"Failed to write reflog. Cannot resolve reference `%s`"
,
ref
->
name
);
oid
=
git_reference_oid
(
r
);
if
(
oid
==
NULL
)
return
git__throw
(
GIT_ERROR
,
"Failed to write reflog. Cannot resolve reference `%s`"
,
r
->
name
);
if
(
oid
==
NULL
)
{
git_reference_free
(
r
);
return
git__throw
(
GIT_ERROR
,
"Failed to write reflog. Cannot resolve reference `%s`"
,
r
->
name
);
}
git_oid_to_string
(
new
,
GIT_OID_HEXSZ
+
1
,
oid
);
git_path_join_n
(
log_path
,
3
,
ref
->
owner
->
path_repository
,
GIT_REFLOG_DIR
,
ref
->
name
);
git_path_join_n
(
log_path
,
3
,
ref
->
owner
->
path_repository
,
GIT_REFLOG_DIR
,
ref
->
name
);
git_reference_free
(
r
);
if
(
git_futils_exists
(
log_path
))
{
if
((
error
=
git_futils_mkpath2file
(
log_path
,
GIT_REFLOG_DIR_MODE
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to write reflog. Cannot create reflog directory"
);
error
=
git_futils_mkpath2file
(
log_path
,
GIT_REFLOG_DIR_MODE
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to write reflog. Cannot create reflog directory"
);
}
else
if
(
git_futils_isfile
(
log_path
))
{
return
git__throw
(
GIT_ERROR
,
"Failed to write reflog. `%s` is directory"
,
log_path
);
}
else
if
(
oid_old
==
NULL
)
return
git__throw
(
GIT_ERROR
,
"Failed to write reflog. Old OID cannot be NULL for existing reference"
);
return
git__throw
(
GIT_ERROR
,
"Failed to write reflog. `%s` is directory"
,
log_path
);
}
else
if
(
oid_old
==
NULL
)
{
return
git__throw
(
GIT_ERROR
,
"Failed to write reflog. Old OID cannot be NULL for existing reference"
);
}
if
(
oid_old
)
git_oid_to_string
(
old
,
GIT_OID_HEXSZ
+
1
,
oid_old
);
...
...
src/refs.c
View file @
f8e8c8d4
...
...
@@ -16,16 +16,17 @@
#define MAX_NESTING_LEVEL 5
typedef
struct
{
git_reference
ref
;
git_oid
oid
;
git_oid
peel_target
;
}
reference_oid
;
enum
{
GIT_PACKREF_HAS_PEEL
=
1
,
GIT_PACKREF_WAS_LOOSE
=
2
};
typedef
struct
{
git_reference
ref
;
char
*
target
;
}
reference_symbolic
;
struct
packref
{
git_oid
oid
;
git_oid
peel
;
char
flags
;
char
name
[
GIT_FLEX_ARRAY
];
};
static
const
int
default_table_size
=
32
;
...
...
@@ -40,48 +41,55 @@ static uint32_t reftable_hash(const void *key, int hash_id)
return
git__hash
(
key
,
strlen
((
const
char
*
)
key
),
hash_seeds
[
hash_id
]);
}
static
void
reference_free
(
git_reference
*
reference
);
static
int
reference_create
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
git_rtype
type
);
static
int
reference_read
(
git_fbuffer
*
file_content
,
time_t
*
mtime
,
const
char
*
repo_path
,
const
char
*
ref_name
,
int
*
updated
);
static
int
reference_read
(
git_fbuffer
*
file_content
,
time_t
*
mtime
,
const
char
*
repo_path
,
const
char
*
ref_name
,
int
*
updated
);
/* loose refs */
static
int
loose_parse_symbolic
(
git_reference
*
ref
,
git_fbuffer
*
file_content
);
static
int
loose_parse_oid
(
git_reference
*
ref
,
git_fbuffer
*
file_content
);
static
int
loose_lookup
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
int
skip_symbolic
);
static
int
loose_parse_oid
(
git_oid
*
ref
,
git_fbuffer
*
file_content
);
static
int
loose_lookup
(
git_reference
*
ref
);
static
int
loose_lookup_to_packfile
(
struct
packref
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
);
static
int
loose_write
(
git_reference
*
ref
);
static
int
loose_update
(
git_reference
*
ref
);
/* packed refs */
static
int
packed_parse_peel
(
reference_oid
*
tag_ref
,
const
char
**
buffer_out
,
const
char
*
buffer_end
);
static
int
packed_parse_oid
(
reference_oid
**
ref_out
,
git_repository
*
repo
,
const
char
**
buffer_out
,
const
char
*
buffer_end
);
static
int
packed_parse_peel
(
struct
packref
*
tag_ref
,
const
char
**
buffer_out
,
const
char
*
buffer_end
);
static
int
packed_parse_oid
(
struct
packref
**
ref_out
,
const
char
**
buffer_out
,
const
char
*
buffer_end
);
static
int
packed_load
(
git_repository
*
repo
);
static
int
packed_loadloose
(
git_repository
*
repository
);
static
int
packed_write_ref
(
reference_oid
*
ref
,
git_filebuf
*
file
);
static
int
packed_find_peel
(
reference_oid
*
ref
);
static
int
packed_write_ref
(
struct
packref
*
ref
,
git_filebuf
*
file
);
static
int
packed_find_peel
(
git_repository
*
repo
,
struct
packref
*
ref
);
static
int
packed_remove_loose
(
git_repository
*
repo
,
git_vector
*
packing_list
);
static
int
packed_sort
(
const
void
*
a
,
const
void
*
b
);
static
int
packed_lookup
(
git_reference
*
ref
);
static
int
packed_write
(
git_repository
*
repo
);
/* internal helpers */
static
int
reference_available
(
git_repository
*
repo
,
const
char
*
ref
,
const
char
*
old_ref
);
static
int
reference_available
(
git_repository
*
repo
,
const
char
*
ref
,
const
char
*
old_ref
);
static
int
reference_delete
(
git_reference
*
ref
);
static
int
reference_lookup
(
git_reference
*
ref
);
/* name normalization */
static
int
check_valid_ref_char
(
char
ch
);
static
int
normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
,
int
is_oid_ref
);
static
int
normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
,
int
is_oid_ref
);
/*****************************************
* Internal methods - Constructor/destructor
*****************************************/
static
void
reference_free
(
git_reference
*
reference
)
void
git_reference_free
(
git_reference
*
reference
)
{
if
(
reference
==
NULL
)
return
;
if
(
reference
->
name
)
git__free
(
reference
->
name
);
git__free
(
reference
->
name
);
if
(
reference
->
type
==
GIT_REF_SYMBOLIC
)
git__free
(
((
reference_symbolic
*
)
reference
)
->
target
);
if
(
reference
->
flags
&
GIT_REF_SYMBOLIC
)
git__free
(
reference
->
target
.
symbolic
);
git__free
(
reference
);
}
...
...
@@ -89,48 +97,27 @@ static void reference_free(git_reference *reference)
static
int
reference_create
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
git_rtype
type
)
const
char
*
name
)
{
char
normalized
[
GIT_REFNAME_MAX
];
int
error
=
GIT_SUCCESS
,
size
;
git_reference
*
reference
=
NULL
;
assert
(
ref_out
&&
repo
&&
name
);
if
(
type
==
GIT_REF_SYMBOLIC
)
size
=
sizeof
(
reference_symbolic
);
else
if
(
type
==
GIT_REF_OID
)
size
=
sizeof
(
reference_oid
);
else
return
git__throw
(
GIT_EINVALIDARGS
,
"Invalid reference type. Use either GIT_REF_OID or GIT_REF_SYMBOLIC as type specifier"
);
reference
=
git__malloc
(
size
);
reference
=
git__malloc
(
sizeof
(
git_reference
));
if
(
reference
==
NULL
)
return
GIT_ENOMEM
;
memset
(
reference
,
0x0
,
size
);
memset
(
reference
,
0x0
,
size
of
(
git_reference
)
);
reference
->
owner
=
repo
;
reference
->
type
=
type
;
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
name
,
(
type
&
GIT_REF_OID
));
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
reference
->
name
=
git__strdup
(
n
ormalized
);
reference
->
name
=
git__strdup
(
n
ame
);
if
(
reference
->
name
==
NULL
)
{
error
=
GIT_ENOMEM
;
goto
cleanup
;
free
(
reference
)
;
return
GIT_ENOMEM
;
}
*
ref_out
=
reference
;
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create reference"
);
cleanup:
reference_free
(
reference
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create reference"
);
return
GIT_SUCCESS
;
}
static
int
reference_read
(
git_fbuffer
*
file_content
,
time_t
*
mtime
,
const
char
*
repo_path
,
const
char
*
ref_name
,
int
*
updated
)
...
...
@@ -145,62 +132,13 @@ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *
return
git_futils_readbuffer_updated
(
file_content
,
path
,
mtime
,
updated
);
}
/*****************************************
* Internal methods - Loose references
*****************************************/
static
int
loose_update
(
git_reference
*
ref
)
{
int
error
,
updated
;
git_fbuffer
ref_file
=
GIT_FBUFFER_INIT
;
if
(
ref
->
type
&
GIT_REF_PACKED
)
return
packed_load
(
ref
->
owner
);
/* error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name);
if (error < GIT_SUCCESS)
goto cleanup;
if (ref_time == ref->mtime)
return GIT_SUCCESS;
*/
error
=
reference_read
(
&
ref_file
,
&
ref
->
mtime
,
ref
->
owner
->
path_repository
,
ref
->
name
,
&
updated
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
!
updated
)
goto
cleanup
;
if
(
ref
->
type
==
GIT_REF_SYMBOLIC
)
error
=
loose_parse_symbolic
(
ref
,
&
ref_file
);
else
if
(
ref
->
type
==
GIT_REF_OID
)
error
=
loose_parse_oid
(
ref
,
&
ref_file
);
else
error
=
git__throw
(
GIT_EOBJCORRUPTED
,
"Invalid reference type (%d) for loose reference"
,
ref
->
type
);
cleanup:
git_futils_freebuffer
(
&
ref_file
);
if
(
error
!=
GIT_SUCCESS
)
{
reference_free
(
ref
);
git_hashtable_remove
(
ref
->
owner
->
references
.
loose_cache
,
ref
->
name
);
}
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to update loose reference"
);
}
static
int
loose_parse_symbolic
(
git_reference
*
ref
,
git_fbuffer
*
file_content
)
{
const
unsigned
int
header_len
=
strlen
(
GIT_SYMREF
);
const
char
*
refname_start
;
char
*
eol
;
reference_symbolic
*
ref_sym
;
refname_start
=
(
const
char
*
)
file_content
->
data
;
ref_sym
=
(
reference_symbolic
*
)
ref
;
if
(
file_content
->
len
<
(
header_len
+
1
))
return
git__throw
(
GIT_EOBJCORRUPTED
,
...
...
@@ -213,13 +151,12 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
refname_start
+=
header_len
;
git__free
(
ref_sym
->
target
);
ref_sym
->
target
=
git__strdup
(
refname_start
);
if
(
ref_sym
->
target
==
NULL
)
ref
->
target
.
symbolic
=
git__strdup
(
refname_start
);
if
(
ref
->
target
.
symbolic
==
NULL
)
return
GIT_ENOMEM
;
/* remove newline at the end of file */
eol
=
strchr
(
ref
_sym
->
target
,
'\n'
);
eol
=
strchr
(
ref
->
target
.
symbolic
,
'\n'
);
if
(
eol
==
NULL
)
return
git__throw
(
GIT_EOBJCORRUPTED
,
"Failed to parse loose reference. Missing EOL"
);
...
...
@@ -231,21 +168,19 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
return
GIT_SUCCESS
;
}
static
int
loose_parse_oid
(
git_
reference
*
ref
,
git_fbuffer
*
file_content
)
static
int
loose_parse_oid
(
git_
oid
*
oid
,
git_fbuffer
*
file_content
)
{
int
error
;
reference_oid
*
ref_oid
;
char
*
buffer
;
buffer
=
(
char
*
)
file_content
->
data
;
ref_oid
=
(
reference_oid
*
)
ref
;
/* File format: 40 chars (OID) + newline */
if
(
file_content
->
len
<
GIT_OID_HEXSZ
+
1
)
return
git__throw
(
GIT_EOBJCORRUPTED
,
"Failed to parse loose reference. Reference too short"
);
if
((
error
=
git_oid_fromstr
(
&
ref_oid
->
oid
,
buffer
))
<
GIT_SUCCESS
)
if
((
error
=
git_oid_fromstr
(
oid
,
buffer
))
<
GIT_SUCCESS
)
return
git__rethrow
(
GIT_EOBJCORRUPTED
,
"Failed to parse loose reference."
);
buffer
=
buffer
+
GIT_OID_HEXSZ
;
...
...
@@ -259,7 +194,6 @@ static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content)
return
GIT_SUCCESS
;
}
static
git_rtype
loose_guess_rtype
(
const
char
*
full_path
)
{
git_fbuffer
ref_file
=
GIT_FBUFFER_INIT
;
...
...
@@ -278,52 +212,75 @@ static git_rtype loose_guess_rtype(const char *full_path)
return
type
;
}
static
int
loose_lookup
(
git_reference
**
ref_out
,
static
int
loose_lookup
(
git_reference
*
ref
)
{
int
error
=
GIT_SUCCESS
,
updated
;
git_fbuffer
ref_file
=
GIT_FBUFFER_INIT
;
if
(
reference_read
(
&
ref_file
,
&
ref
->
mtime
,
ref
->
owner
->
path_repository
,
ref
->
name
,
&
updated
)
<
GIT_SUCCESS
)
return
git__throw
(
GIT_ENOTFOUND
,
"Failed to lookup loose reference"
);
if
(
!
updated
)
return
GIT_SUCCESS
;
if
(
ref
->
flags
&
GIT_REF_SYMBOLIC
)
free
(
ref
->
target
.
symbolic
);
ref
->
flags
=
0
;
if
(
git__prefixcmp
((
const
char
*
)(
ref_file
.
data
),
GIT_SYMREF
)
==
0
)
{
ref
->
flags
|=
GIT_REF_SYMBOLIC
;
error
=
loose_parse_symbolic
(
ref
,
&
ref_file
);
}
else
{
ref
->
flags
|=
GIT_REF_OID
;
error
=
loose_parse_oid
(
&
ref
->
target
.
oid
,
&
ref_file
);
}
git_futils_freebuffer
(
&
ref_file
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to lookup loose reference"
);
return
GIT_SUCCESS
;
}
static
int
loose_lookup_to_packfile
(
struct
packref
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
int
skip_symbolic
)
const
char
*
name
)
{
int
error
=
GIT_SUCCESS
;
git_fbuffer
ref_file
=
GIT_FBUFFER_INIT
;
git_reference
*
ref
=
NULL
;
time_t
ref_time
=
0
;
struct
packref
*
ref
=
NULL
;
size_t
name_len
;
*
ref_out
=
NULL
;
error
=
reference_read
(
&
ref_file
,
&
ref_time
,
repo
->
path_repository
,
name
,
NULL
);
error
=
reference_read
(
&
ref_file
,
NULL
,
repo
->
path_repository
,
name
,
NULL
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
git__prefixcmp
((
const
char
*
)(
ref_file
.
data
),
GIT_SYMREF
)
==
0
)
{
if
(
skip_symbolic
)
return
GIT_SUCCESS
;
error
=
reference_create
(
&
ref
,
repo
,
name
,
GIT_REF_SYMBOLIC
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
error
=
loose_parse_symbolic
(
ref
,
&
ref_file
);
}
else
{
error
=
reference_create
(
&
ref
,
repo
,
name
,
GIT_REF_OID
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
name_len
=
strlen
(
name
);
ref
=
git__malloc
(
sizeof
(
struct
packref
)
+
name_len
+
1
);
error
=
loose_parse_oid
(
ref
,
&
ref_file
);
}
memcpy
(
ref
->
name
,
name
,
name_len
);
ref
->
name
[
name_len
]
=
0
;
error
=
loose_parse_oid
(
&
ref
->
oid
,
&
ref_file
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
ref
->
mtime
=
ref_time
;
ref
->
flags
=
GIT_PACKREF_WAS_LOOSE
;
*
ref_out
=
ref
;
git_futils_freebuffer
(
&
ref_file
);
return
GIT_SUCCESS
;
cleanup:
git_futils_freebuffer
(
&
ref_file
);
reference_
free
(
ref
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to lookup loose reference"
);
free
(
ref
);
return
git__rethrow
(
error
,
"Failed to lookup loose reference"
);
}
static
int
loose_write
(
git_reference
*
ref
)
...
...
@@ -338,49 +295,36 @@ static int loose_write(git_reference *ref)
if
((
error
=
git_filebuf_open
(
&
file
,
ref_path
,
GIT_FILEBUF_FORCE
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to write loose reference"
);
if
(
ref
->
type
&
GIT_REF_OID
)
{
reference_oid
*
ref_oid
=
(
reference_oid
*
)
ref
;
if
(
ref
->
flags
&
GIT_REF_OID
)
{
char
oid
[
GIT_OID_HEXSZ
+
1
];
memset
(
oid
,
0x0
,
sizeof
(
oid
));
git_oid_fmt
(
oid
,
&
ref
->
target
.
oid
);
oid
[
GIT_OID_HEXSZ
]
=
'\0'
;
git_oid_fmt
(
oid
,
&
ref_oid
->
oid
);
error
=
git_filebuf_printf
(
&
file
,
"%s
\n
"
,
oid
);
if
(
error
<
GIT_SUCCESS
)
goto
unlock
;
}
else
if
(
ref
->
type
&
GIT_REF_SYMBOLIC
)
{
/* GIT_REF_SYMBOLIC */
reference_symbolic
*
ref_sym
=
(
reference_symbolic
*
)
ref
;
error
=
git_filebuf_printf
(
&
file
,
GIT_SYMREF
"%s
\n
"
,
ref_sym
->
target
);
}
else
if
(
ref
->
flags
&
GIT_REF_SYMBOLIC
)
{
/* GIT_REF_SYMBOLIC */
error
=
git_filebuf_printf
(
&
file
,
GIT_SYMREF
"%s
\n
"
,
ref
->
target
.
symbolic
);
}
else
{
error
=
git__throw
(
GIT_EOBJCORRUPTED
,
"Failed to write reference. Invalid reference type"
);
error
=
git__throw
(
GIT_EOBJCORRUPTED
,
"Failed to write reference. Invalid reference type"
);
goto
unlock
;
}
error
=
git_filebuf_commit
(
&
file
,
GIT_REFS_FILE_MODE
);
if
(
p_stat
(
ref_path
,
&
st
)
==
GIT_SUCCESS
)
ref
->
mtime
=
st
.
st_mtime
;
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to write loose reference"
);
return
git_filebuf_commit
(
&
file
,
GIT_REFS_FILE_MODE
);
unlock:
git_filebuf_cleanup
(
&
file
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to write loose reference"
);
return
git__rethrow
(
error
,
"Failed to write loose reference"
);
}
/*****************************************
* Internal methods - Packed references
*****************************************/
static
int
packed_parse_peel
(
reference_oid
*
tag_ref
,
struct
packref
*
tag_ref
,
const
char
**
buffer_out
,
const
char
*
buffer_end
)
{
...
...
@@ -390,47 +334,48 @@ static int packed_parse_peel(
/* Ensure it's not the first entry of the file */
if
(
tag_ref
==
NULL
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Reference is the first entry of the file"
);
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. "
"Reference is the first entry of the file"
);
/* Ensure reference is a tag */
if
(
git__prefixcmp
(
tag_ref
->
ref
.
name
,
GIT_REFS_TAGS_DIR
)
!=
0
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Reference is not a tag"
);
if
(
git__prefixcmp
(
tag_ref
->
name
,
GIT_REFS_TAGS_DIR
)
!=
0
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Reference is not a tag"
);
if
(
buffer
+
GIT_OID_HEXSZ
>=
buffer_end
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Buffer too small"
);
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Buffer too small"
);
/* Is this a valid object id? */
if
(
git_oid_fromstr
(
&
tag_ref
->
peel_target
,
buffer
)
<
GIT_SUCCESS
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Not a valid object ID"
);
if
(
git_oid_fromstr
(
&
tag_ref
->
peel
,
buffer
)
<
GIT_SUCCESS
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Not a valid object ID"
);
buffer
=
buffer
+
GIT_OID_HEXSZ
;
if
(
*
buffer
==
'\r'
)
buffer
++
;
if
(
*
buffer
!=
'\n'
)
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Buffer not terminated correctly"
);
return
git__throw
(
GIT_EPACKEDREFSCORRUPTED
,
"Failed to parse packed reference. Buffer not terminated correctly"
);
*
buffer_out
=
buffer
+
1
;
tag_ref
->
ref
.
type
|=
GIT_REF_HAS_PEEL
;
return
GIT_SUCCESS
;
}
static
int
packed_parse_oid
(
reference_oid
**
ref_out
,
git_repository
*
repo
,
struct
packref
**
ref_out
,
const
char
**
buffer_out
,
const
char
*
buffer_end
)
{
git_reference
*
_ref
=
NULL
;
reference_oid
*
ref
=
NULL
;
struct
packref
*
ref
=
NULL
;
const
char
*
buffer
=
*
buffer_out
;
const
char
*
refname_begin
,
*
refname_end
;
int
error
=
GIT_SUCCESS
;
int
refname_len
;
char
refname
[
GIT_REFNAME_MAX
];
size_t
refname_len
;
git_oid
id
;
refname_begin
=
(
buffer
+
GIT_OID_HEXSZ
+
1
);
...
...
@@ -450,22 +395,19 @@ static int packed_parse_oid(
goto
cleanup
;
}
refname_len
=
refname_end
-
refname_begin
;
memcpy
(
refname
,
refname_begin
,
refname_len
);
refname
[
refname_len
]
=
0
;
if
(
refname_end
[
-
1
]
==
'\r'
)
refname_end
--
;
if
(
refname
[
refname_len
-
1
]
==
'\r'
)
refname
[
refname_len
-
1
]
=
0
;
refname_len
=
refname_end
-
refname_begin
;
error
=
reference_create
(
&
_ref
,
repo
,
refname
,
GIT_REF_OID
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
ref
=
git__malloc
(
sizeof
(
struct
packref
)
+
refname_len
+
1
);
ref
=
(
reference_oid
*
)
_ref
;
memcpy
(
ref
->
name
,
refname_begin
,
refname_len
);
ref
->
name
[
refname_len
]
=
0
;
git_oid_cpy
(
&
ref
->
oid
,
&
id
);
ref
->
ref
.
type
|=
GIT_REF_PACKED
;
ref
->
flags
=
0
;
*
ref_out
=
ref
;
*
buffer_out
=
refname_end
+
1
;
...
...
@@ -473,8 +415,8 @@ static int packed_parse_oid(
return
GIT_SUCCESS
;
cleanup:
reference_free
((
git_reference
*
)
ref
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to parse OID of packed reference"
);
free
(
ref
);
return
git__rethrow
(
error
,
"Failed to parse OID of packed reference"
);
}
static
int
packed_load
(
git_repository
*
repo
)
...
...
@@ -489,7 +431,7 @@ static int packed_load(git_repository *repo)
ref_cache
->
packfile
=
git_hashtable_alloc
(
default_table_size
,
reftable_hash
,
(
git_hash_keyeq_ptr
)
(
&
git__strcmp_cb
)
);
(
git_hash_keyeq_ptr
)
&
git__strcmp_cb
);
if
(
ref_cache
->
packfile
==
NULL
)
{
error
=
GIT_ENOMEM
;
...
...
@@ -498,7 +440,7 @@ static int packed_load(git_repository *repo)
}
error
=
reference_read
(
&
packfile
,
&
ref_cache
->
packfile_time
,
repo
->
path_repository
,
GIT_PACKEDREFS_FILE
,
&
updated
);
repo
->
path_repository
,
GIT_PACKEDREFS_FILE
,
&
updated
);
/*
* If we couldn't find the file, we need to clear the table and
...
...
@@ -536,9 +478,9 @@ static int packed_load(git_repository *repo)
}
while
(
buffer_start
<
buffer_end
)
{
reference_oid
*
ref
=
NULL
;
struct
packref
*
ref
=
NULL
;
error
=
packed_parse_oid
(
&
ref
,
repo
,
&
buffer_start
,
buffer_end
);
error
=
packed_parse_oid
(
&
ref
,
&
buffer_start
,
buffer_end
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
...
...
@@ -548,9 +490,9 @@ static int packed_load(git_repository *repo)
goto
cleanup
;
}
error
=
git_hashtable_insert
(
ref_cache
->
packfile
,
ref
->
ref
.
name
,
ref
);
error
=
git_hashtable_insert
(
ref_cache
->
packfile
,
ref
->
name
,
ref
);
if
(
error
<
GIT_SUCCESS
)
{
reference_free
((
git_reference
*
)
ref
);
free
(
ref
);
goto
cleanup
;
}
}
...
...
@@ -562,12 +504,10 @@ cleanup:
git_hashtable_free
(
ref_cache
->
packfile
);
ref_cache
->
packfile
=
NULL
;
git_futils_freebuffer
(
&
packfile
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to load packed references"
);
return
git__rethrow
(
error
,
"Failed to load packed references"
);
}
struct
dirent_list_data
{
git_repository
*
repo
;
size_t
repo_path_len
;
...
...
@@ -583,7 +523,8 @@ static int _dirent_loose_listall(void *_data, char *full_path)
char
*
file_path
=
full_path
+
data
->
repo_path_len
;
if
(
git_futils_isdir
(
full_path
)
==
GIT_SUCCESS
)
return
git_futils_direach
(
full_path
,
GIT_PATH_MAX
,
_dirent_loose_listall
,
_data
);
return
git_futils_direach
(
full_path
,
GIT_PATH_MAX
,
_dirent_loose_listall
,
_data
);
/* do not add twice a reference that exists already in the packfile */
if
((
data
->
list_flags
&
GIT_REF_PACKED
)
!=
0
&&
...
...
@@ -601,29 +542,35 @@ static int _dirent_loose_listall(void *_data, char *full_path)
static
int
_dirent_loose_load
(
void
*
data
,
char
*
full_path
)
{
git_repository
*
repository
=
(
git_repository
*
)
data
;
git_reference
*
reference
;
void
*
old_ref
=
NULL
;
struct
packref
*
ref
;
char
*
file_path
;
int
error
;
if
(
git_futils_isdir
(
full_path
)
==
GIT_SUCCESS
)
return
git_futils_direach
(
full_path
,
GIT_PATH_MAX
,
_dirent_loose_load
,
repository
);
return
git_futils_direach
(
full_path
,
GIT_PATH_MAX
,
_dirent_loose_load
,
repository
);
file_path
=
full_path
+
strlen
(
repository
->
path_repository
);
error
=
loose_lookup
(
&
reference
,
repository
,
file_path
,
1
);
if
(
error
==
GIT_SUCCESS
&&
reference
!=
NULL
)
{
reference
->
type
|=
GIT_REF_PACKED
;
error
=
loose_lookup_to_packfile
(
&
ref
,
repository
,
file_path
);
if
(
git_hashtable_insert2
(
repository
->
references
.
packfile
,
reference
->
name
,
reference
,
&
old_ref
)
<
GIT_SUCCESS
)
{
reference_free
(
reference
);
if
(
error
==
GIT_SUCCESS
)
{
if
(
git_hashtable_insert2
(
repository
->
references
.
packfile
,
ref
->
name
,
ref
,
&
old_ref
)
<
GIT_SUCCESS
)
{
free
(
ref
);
return
GIT_ENOMEM
;
}
if
(
old_ref
!=
NULL
)
reference_free
((
git_reference
*
)
old_ref
);
free
(
old_ref
);
}
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to load loose dirent"
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to load loose references into packfile"
);
}
/*
...
...
@@ -641,30 +588,20 @@ static int packed_loadloose(git_repository *repository)
git_path_join
(
refs_path
,
repository
->
path_repository
,
GIT_REFS_DIR
);
/* Remove any loose references from the cache */
{
const
void
*
GIT_UNUSED
(
_unused
);
git_reference
*
reference
;
GIT_HASHTABLE_FOREACH
(
repository
->
references
.
loose_cache
,
_unused
,
reference
,
reference_free
(
reference
);
);
}
git_hashtable_clear
(
repository
->
references
.
loose_cache
);
/*
* Load all the loose files from disk into the Packfile table.
* This will overwrite any old packed entries with their
* updated loose versions
*/
return
git_futils_direach
(
refs_path
,
GIT_PATH_MAX
,
_dirent_loose_load
,
repository
);
return
git_futils_direach
(
refs_path
,
GIT_PATH_MAX
,
_dirent_loose_load
,
repository
);
}
/*
* Write a single reference into a packfile
*/
static
int
packed_write_ref
(
reference_oid
*
ref
,
git_filebuf
*
file
)
static
int
packed_write_ref
(
struct
packref
*
ref
,
git_filebuf
*
file
)
{
int
error
;
char
oid
[
GIT_OID_HEXSZ
+
1
];
...
...
@@ -682,17 +619,19 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file)
* This obviously only applies to tags.
* The required peels have already been loaded into `ref->peel_target`.
*/
if
(
ref
->
ref
.
type
&
GIT_
REF_HAS_PEEL
)
{
if
(
ref
->
flags
&
GIT_PACK
REF_HAS_PEEL
)
{
char
peel
[
GIT_OID_HEXSZ
+
1
];
git_oid_fmt
(
peel
,
&
ref
->
peel
_target
);
git_oid_fmt
(
peel
,
&
ref
->
peel
);
peel
[
GIT_OID_HEXSZ
]
=
0
;
error
=
git_filebuf_printf
(
file
,
"%s %s
\n
^%s
\n
"
,
oid
,
ref
->
ref
.
name
,
peel
);
error
=
git_filebuf_printf
(
file
,
"%s %s
\n
^%s
\n
"
,
oid
,
ref
->
name
,
peel
);
}
else
{
error
=
git_filebuf_printf
(
file
,
"%s %s
\n
"
,
oid
,
ref
->
ref
.
name
);
error
=
git_filebuf_printf
(
file
,
"%s %s
\n
"
,
oid
,
ref
->
name
);
}
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to write packed reference"
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to write packed reference"
);
}
/*
...
...
@@ -703,25 +642,25 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file)
* cache on the packfile the OID of the object to
* which that 'big tag' is pointing to.
*/
static
int
packed_find_peel
(
reference_oid
*
ref
)
static
int
packed_find_peel
(
git_repository
*
repo
,
struct
packref
*
ref
)
{
git_object
*
object
;
int
error
;
if
(
ref
->
ref
.
type
&
GIT_
REF_HAS_PEEL
)
if
(
ref
->
flags
&
GIT_PACK
REF_HAS_PEEL
)
return
GIT_SUCCESS
;
/*
* Only applies to tags, i.e. references
* in the /refs/tags folder
*/
if
(
git__prefixcmp
(
ref
->
ref
.
name
,
GIT_REFS_TAGS_DIR
)
!=
0
)
if
(
git__prefixcmp
(
ref
->
name
,
GIT_REFS_TAGS_DIR
)
!=
0
)
return
GIT_SUCCESS
;
/*
* Find the tagged object in the repository
*/
error
=
git_object_lookup
(
&
object
,
re
f
->
ref
.
owner
,
&
ref
->
oid
,
GIT_OBJ_ANY
);
error
=
git_object_lookup
(
&
object
,
re
po
,
&
ref
->
oid
,
GIT_OBJ_ANY
);
if
(
error
<
GIT_SUCCESS
)
return
git__throw
(
GIT_EOBJCORRUPTED
,
"Failed to find packed reference"
);
...
...
@@ -736,8 +675,8 @@ static int packed_find_peel(reference_oid *ref)
/*
* Find the object pointed at by this tag
*/
git_oid_cpy
(
&
ref
->
peel
_target
,
git_tag_target_oid
(
tag
));
ref
->
ref
.
type
|=
GIT_
REF_HAS_PEEL
;
git_oid_cpy
(
&
ref
->
peel
,
git_tag_target_oid
(
tag
));
ref
->
flags
|=
GIT_PACK
REF_HAS_PEEL
;
/*
* The reference has now cached the resolved OID, and is
...
...
@@ -747,7 +686,6 @@ static int packed_find_peel(reference_oid *ref)
}
git_object_close
(
object
);
return
GIT_SUCCESS
;
}
...
...
@@ -767,16 +705,11 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
unsigned
int
i
;
char
full_path
[
GIT_PATH_MAX
];
int
error
=
GIT_SUCCESS
;
git_reference
*
reference
;
for
(
i
=
0
;
i
<
packing_list
->
length
;
++
i
)
{
git_reference
*
ref
=
git_vector_get
(
packing_list
,
i
);
struct
packref
*
ref
=
git_vector_get
(
packing_list
,
i
);
/* Ensure the packed reference doesn't exist
* in a (more up-to-date?) state as a loose reference
*/
reference
=
git_hashtable_lookup
(
ref
->
owner
->
references
.
loose_cache
,
ref
->
name
);
if
(
reference
!=
NULL
)
if
((
ref
->
flags
&
GIT_PACKREF_WAS_LOOSE
)
==
0
)
continue
;
git_path_join
(
full_path
,
repo
->
path_repository
,
ref
->
name
);
...
...
@@ -790,19 +723,18 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
* but we should keep going and remove as many as possible.
* After we've removed as many files as possible, we return
* the error code anyway.
*
* TODO: mark this with a very special error code?
* GIT_EFAILTORMLOOSE
*/
}
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to remove loose packed reference"
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to remove loose packed reference"
);
}
static
int
packed_sort
(
const
void
*
a
,
const
void
*
b
)
{
const
git_reference
*
ref_a
=
(
const
git_reference
*
)
a
;
const
git_reference
*
ref_b
=
(
const
git_reference
*
)
b
;
const
struct
packref
*
ref_a
=
(
const
struct
packref
*
)
a
;
const
struct
packref
*
ref_b
=
(
const
struct
packref
*
)
b
;
return
strcmp
(
ref_a
->
name
,
ref_b
->
name
);
}
...
...
@@ -823,16 +755,18 @@ static int packed_write(git_repository *repo)
assert
(
repo
&&
repo
->
references
.
packfile
);
total_refs
=
repo
->
references
.
packfile
->
key_count
;
if
((
error
=
git_vector_init
(
&
packing_list
,
total_refs
,
packed_sort
))
<
GIT_SUCCESS
)
if
((
error
=
git_vector_init
(
&
packing_list
,
total_refs
,
packed_sort
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to write packed reference"
);
/* Load all the packfile into a vector */
{
git_reference
*
reference
;
struct
packref
*
reference
;
const
void
*
GIT_UNUSED
(
_unused
);
GIT_HASHTABLE_FOREACH
(
repo
->
references
.
packfile
,
_unused
,
reference
,
git_vector_insert
(
&
packing_list
,
reference
);
/* cannot fail: vector already has the right size */
/* cannot fail: vector already has the right size */
git_vector_insert
(
&
packing_list
,
reference
);
);
}
...
...
@@ -847,22 +781,19 @@ static int packed_write(git_repository *repo)
/* Packfiles have a header... apparently
* This is in fact not required, but we might as well print it
* just for kicks */
if
((
error
=
git_filebuf_printf
(
&
pack_file
,
"%s
\n
"
,
GIT_PACKEDREFS_HEADER
))
<
GIT_SUCCESS
)
if
((
error
=
git_filebuf_printf
(
&
pack_file
,
"%s
\n
"
,
GIT_PACKEDREFS_HEADER
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to write packed reference"
);
for
(
i
=
0
;
i
<
packing_list
.
length
;
++
i
)
{
reference_oid
*
ref
=
(
reference_oid
*
)
git_vector_get
(
&
packing_list
,
i
);
/* only direct references go to the packfile; otherwise
* this is a disaster */
assert
(
ref
->
ref
.
type
&
GIT_REF_OID
);
struct
packref
*
ref
=
(
struct
packref
*
)
git_vector_get
(
&
packing_list
,
i
);
if
((
error
=
packed_find_peel
(
ref
))
<
GIT_SUCCESS
)
{
error
=
git__throw
(
GIT_EOBJCORRUPTED
,
"A reference cannot be peeled"
);
if
((
error
=
packed_find_peel
(
repo
,
ref
))
<
GIT_SUCCESS
)
{
error
=
git__throw
(
GIT_EOBJCORRUPTED
,
"A reference cannot be peeled"
);
goto
cleanup
;
}
if
((
error
=
packed_write_ref
(
ref
,
&
pack_file
))
<
GIT_SUCCESS
)
goto
cleanup
;
}
...
...
@@ -888,20 +819,22 @@ cleanup:
git_vector_free
(
&
packing_list
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to write packed reference"
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to write packed reference"
);
}
static
int
_reference_available_cb
(
const
char
*
ref
,
void
*
data
)
{
const
char
*
new
,
*
old
;
git_vector
*
refs
;
const
char
*
*
refs
;
assert
(
ref
&&
data
);
refs
=
(
git_vector
*
)
data
;
refs
=
(
const
char
*
*
)
data
;
new
=
(
const
char
*
)
git_vector_get
(
refs
,
0
)
;
old
=
(
const
char
*
)
git_vector_get
(
refs
,
1
)
;
new
=
(
const
char
*
)
refs
[
0
]
;
old
=
(
const
char
*
)
refs
[
1
]
;
if
(
!
old
||
strcmp
(
old
,
ref
))
{
int
reflen
=
strlen
(
ref
);
...
...
@@ -917,35 +850,168 @@ static int _reference_available_cb(const char *ref, void *data)
return
GIT_SUCCESS
;
}
static
int
reference_available
(
git_repository
*
repo
,
const
char
*
ref
,
const
char
*
old_ref
)
static
int
reference_available
(
git_repository
*
repo
,
const
char
*
ref
,
const
char
*
old_ref
)
{
const
char
*
refs
[
2
];
refs
[
0
]
=
ref
;
refs
[
1
]
=
old_ref
;
if
(
git_reference_foreach
(
repo
,
GIT_REF_LISTALL
,
_reference_available_cb
,
(
void
*
)
refs
)
<
0
)
{
return
git__throw
(
GIT_EEXISTS
,
"Reference name `%s` conflicts with existing reference"
,
ref
);
}
return
GIT_SUCCESS
;
}
static
int
reference_exists
(
int
*
exists
,
git_repository
*
repo
,
const
char
*
ref_name
)
{
int
error
;
git_vector
refs
;
char
ref_path
[
GIT_PATH_MAX
]
;
if
(
git_vector_init
(
&
refs
,
2
,
NULL
)
<
GIT_SUCCESS
)
return
GIT_ENOMEM
;
error
=
packed_load
(
repo
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Cannot resolve if a reference exists"
);
git_vector_insert
(
&
refs
,
(
void
*
)
ref
);
git_vector_insert
(
&
refs
,
(
void
*
)
old_ref
);
git_path_join
(
ref_path
,
repo
->
path_repository
,
ref_name
);
error
=
git_reference_foreach
(
repo
,
GIT_REF_LISTALL
,
_reference_available_cb
,
(
void
*
)
&
refs
);
if
(
git_futils_isfile
(
ref_path
)
==
GIT_SUCCESS
||
git_hashtable_lookup
(
repo
->
references
.
packfile
,
ref_path
)
!=
NULL
)
{
*
exists
=
1
;
}
else
{
*
exists
=
0
;
}
git_vector_free
(
&
refs
);
return
GIT_SUCCESS
;
}
static
int
packed_lookup
(
git_reference
*
ref
)
{
int
error
;
struct
packref
*
pack_ref
=
NULL
;
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__throw
(
GIT_EEXISTS
,
"Reference name `%s` conflicts with existing reference"
,
ref
);
error
=
packed_load
(
ref
->
owner
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to lookup reference from packfile"
);
if
(
ref
->
flags
&
GIT_REF_PACKED
&&
ref
->
mtime
==
ref
->
owner
->
references
.
packfile_time
)
return
GIT_SUCCESS
;
if
(
ref
->
flags
&
GIT_REF_SYMBOLIC
)
free
(
ref
->
target
.
symbolic
);
/* Look up on the packfile */
pack_ref
=
git_hashtable_lookup
(
ref
->
owner
->
references
.
packfile
,
ref
->
name
);
if
(
pack_ref
==
NULL
)
return
git__throw
(
GIT_ENOTFOUND
,
"Failed to lookup reference from packfile"
);
ref
->
flags
=
GIT_REF_OID
|
GIT_REF_PACKED
;
ref
->
mtime
=
ref
->
owner
->
references
.
packfile_time
;
git_oid_cpy
(
&
ref
->
target
.
oid
,
&
pack_ref
->
oid
);
return
GIT_SUCCESS
;
}
/*****************************************
* External Library API
*****************************************/
static
int
reference_lookup
(
git_reference
*
ref
)
{
int
error_loose
,
error_packed
;
/**
* Constructors
error_loose
=
loose_lookup
(
ref
);
if
(
error_loose
==
GIT_SUCCESS
)
return
GIT_SUCCESS
;
error_packed
=
packed_lookup
(
ref
);
if
(
error_packed
==
GIT_SUCCESS
)
return
GIT_SUCCESS
;
git_reference_free
(
ref
);
if
(
error_loose
!=
GIT_ENOTFOUND
)
return
git__rethrow
(
error_loose
,
"Failed to lookup reference"
);
if
(
error_packed
!=
GIT_ENOTFOUND
)
return
git__rethrow
(
error_packed
,
"Failed to lookup reference"
);
return
git__throw
(
GIT_ENOTFOUND
,
"Reference not found"
);
}
/*
* Delete a reference.
* This is an internal method; the reference is removed
* from disk or the packfile, but the pointer is not freed
*/
int
git_reference_lookup
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
)
static
int
reference_delete
(
git_reference
*
ref
)
{
int
error
;
assert
(
ref
);
/* If the reference is packed, this is an expensive operation.
* We need to reload the packfile, remove the reference from the
* packing list, and repack */
if
(
ref
->
flags
&
GIT_REF_PACKED
)
{
/* load the existing packfile */
if
((
error
=
packed_load
(
ref
->
owner
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to delete reference"
);
if
(
git_hashtable_remove
(
ref
->
owner
->
references
.
packfile
,
ref
->
name
)
<
GIT_SUCCESS
)
return
git__throw
(
GIT_ENOTFOUND
,
"Reference not found"
);
error
=
packed_write
(
ref
->
owner
);
/* If the reference is loose, we can just remove the reference
* from the filesystem */
}
else
{
char
full_path
[
GIT_PATH_MAX
];
git_reference
*
ref_in_pack
;
git_path_join
(
full_path
,
ref
->
owner
->
path_repository
,
ref
->
name
);
error
=
p_unlink
(
full_path
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
/* When deleting a loose reference, we have to ensure that an older
* packed version of it doesn't exist */
if
(
git_reference_lookup
(
&
ref_in_pack
,
ref
->
owner
,
ref
->
name
)
==
GIT_SUCCESS
)
{
assert
((
ref_in_pack
->
flags
&
GIT_REF_PACKED
)
!=
0
);
error
=
git_reference_delete
(
ref_in_pack
);
}
}
cleanup:
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to delete reference"
);
}
int
git_reference_delete
(
git_reference
*
ref
)
{
int
error
=
reference_delete
(
ref
);
if
(
error
<
GIT_SUCCESS
)
return
error
;
git_reference_free
(
ref
);
return
GIT_SUCCESS
;
}
int
git_reference_lookup
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
)
{
int
error
;
char
normalized_name
[
GIT_REFNAME_MAX
];
git_reference
*
ref
=
NULL
;
assert
(
ref_out
&&
repo
&&
name
);
...
...
@@ -955,39 +1021,16 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to lookup reference"
);
/* First, check has been previously loaded and cached */
*
ref_out
=
git_hashtable_lookup
(
repo
->
references
.
loose_cache
,
normalized_name
);
if
(
*
ref_out
!=
NULL
)
return
loose_update
(
*
ref_out
);
/* Then check if there is a loose file for that reference.*/
error
=
loose_lookup
(
ref_out
,
repo
,
normalized_name
,
0
);
/* If the file exists, we store it on the cache */
if
(
error
==
GIT_SUCCESS
)
return
git_hashtable_insert
(
repo
->
references
.
loose_cache
,
(
*
ref_out
)
->
name
,
(
*
ref_out
));
/* The loose lookup has failed, but not because the reference wasn't found;
* probably the loose reference is corrupted. this is bad. */
if
(
error
!=
GIT_ENOTFOUND
)
error
=
reference_create
(
&
ref
,
repo
,
normalized_name
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to lookup reference"
);
/*
* If we cannot find a loose reference, we look into the packfile
* Load the packfile first if it hasn't been loaded
*/
/* load all the packed references */
error
=
packed_load
(
repo
);
error
=
reference_lookup
(
ref
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to lookup reference"
);
/* Look up on the packfile */
*
ref_out
=
git_hashtable_lookup
(
repo
->
references
.
packfile
,
normalized_name
);
if
(
*
ref_out
!=
NULL
)
return
GIT_SUCCESS
;
/* The reference doesn't exist anywhere */
return
git__throw
(
GIT_ENOTFOUND
,
"Failed to lookup reference. Reference doesn't exist"
);
*
ref_out
=
ref
;
return
GIT_SUCCESS
;
}
/**
...
...
@@ -997,15 +1040,21 @@ git_rtype git_reference_type(git_reference *ref)
{
assert
(
ref
);
if
(
ref
->
type
&
GIT_REF_OID
)
if
(
ref
->
flags
&
GIT_REF_OID
)
return
GIT_REF_OID
;
if
(
ref
->
type
&
GIT_REF_SYMBOLIC
)
if
(
ref
->
flags
&
GIT_REF_SYMBOLIC
)
return
GIT_REF_SYMBOLIC
;
return
GIT_REF_INVALID
;
}
int
git_reference_is_packed
(
git_reference
*
ref
)
{
assert
(
ref
);
return
!!
(
ref
->
flags
&
GIT_REF_PACKED
);
}
const
char
*
git_reference_name
(
git_reference
*
ref
)
{
assert
(
ref
);
...
...
@@ -1022,219 +1071,154 @@ const git_oid *git_reference_oid(git_reference *ref)
{
assert
(
ref
);
if
((
ref
->
type
&
GIT_REF_OID
)
==
0
)
return
NULL
;
if
(
loose_update
(
ref
)
<
GIT_SUCCESS
)
if
((
ref
->
flags
&
GIT_REF_OID
)
==
0
)
return
NULL
;
return
&
((
reference_oid
*
)
ref
)
->
oid
;
return
&
ref
->
target
.
oid
;
}
const
char
*
git_reference_target
(
git_reference
*
ref
)
{
assert
(
ref
);
if
((
ref
->
type
&
GIT_REF_SYMBOLIC
)
==
0
)
return
NULL
;
if
(
loose_update
(
ref
)
<
GIT_SUCCESS
)
if
((
ref
->
flags
&
GIT_REF_SYMBOLIC
)
==
0
)
return
NULL
;
return
((
reference_symbolic
*
)
ref
)
->
target
;
return
ref
->
target
.
symbolic
;
}
int
git_reference_create_symbolic
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
const
char
*
target
,
int
force
)
int
git_reference_create_symbolic
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
const
char
*
target
,
int
force
)
{
char
normalized
[
GIT_REFNAME_MAX
];
int
error
=
GIT_SUCCESS
,
updated
=
0
;
int
ref_exists
,
error
=
GIT_SUCCESS
;
git_reference
*
ref
=
NULL
;
void
*
old_ref
=
NULL
;
if
(
git_reference_lookup
(
&
ref
,
repo
,
name
)
==
GIT_SUCCESS
&&
!
force
)
return
git__throw
(
GIT_EEXISTS
,
"Failed to create symbolic reference. Reference already exists"
);
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
name
,
0
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
/*
* If they old ref was of the same type, then we can just update
* it (once we've checked that the target is valid). Otherwise we
* need a new reference because we can't make a symbolic ref out
* of an oid one.
* If if didn't exist, then we need to create a new one anyway.
*/
if
(
ref
&&
ref
->
type
&
GIT_REF_SYMBOLIC
){
updated
=
1
;
}
else
{
ref
=
NULL
;
error
=
reference_create
(
&
ref
,
repo
,
name
,
GIT_REF_SYMBOLIC
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
}
if
((
error
=
reference_exists
(
&
ref_exists
,
repo
,
normalized
)
<
GIT_SUCCESS
))
return
git__rethrow
(
error
,
"Failed to create symbolic reference"
);
/* The target can aither be the name of an object id reference or the name of another symbolic reference */
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
target
,
0
);
if
(
ref_exists
&&
!
force
)
return
git__throw
(
GIT_EEXISTS
,
"Failed to create symbolic reference. Reference already exists"
);
error
=
reference_create
(
&
ref
,
repo
,
normalized
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
/* set the target; this will write the reference on disk */
error
=
git_reference_set_target
(
ref
,
normalized
);
ref
->
flags
|=
GIT_REF_SYMBOLIC
;
/* set the target; this will normalize the name automatically
* and write the reference on disk */
error
=
git_reference_set_target
(
ref
,
target
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
/*
* If we didn't update the ref, then we need to insert or replace
* it in the loose cache. If we replaced a ref, free it.
*/
if
(
!
updated
){
error
=
git_hashtable_insert2
(
repo
->
references
.
loose_cache
,
ref
->
name
,
ref
,
&
old_ref
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
old_ref
!=
NULL
)
reference_free
((
git_reference
*
)
old_ref
);
if
(
ref_out
==
NULL
)
{
git_reference_free
(
ref
);
}
else
{
*
ref_out
=
ref
;
}
*
ref_out
=
ref
;
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create symbolic reference"
);
return
GIT_SUCCESS
;
cleanup:
reference_free
(
ref
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create symbolic reference"
);
git_
reference_free
(
ref
);
return
git__rethrow
(
error
,
"Failed to create symbolic reference"
);
}
int
git_reference_create_oid
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
const
git_oid
*
id
,
int
force
)
int
git_reference_create_oid
(
git_reference
**
ref_out
,
git_repository
*
repo
,
const
char
*
name
,
const
git_oid
*
id
,
int
force
)
{
int
error
=
GIT_SUCCESS
,
updated
=
0
;
int
error
=
GIT_SUCCESS
,
ref_exists
;
git_reference
*
ref
=
NULL
;
void
*
old_ref
=
NULL
;
char
normalized
[
GIT_REFNAME_MAX
];
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
name
,
1
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
git_reference_lookup
(
&
ref
,
repo
,
name
)
==
GIT_SUCCESS
&&
!
force
)
return
git__throw
(
GIT_EEXISTS
,
"Failed to create reference OID. Reference already exists"
);
if
((
error
=
reference_exists
(
&
ref_exists
,
repo
,
normalized
)
<
GIT_SUCCESS
))
return
git__rethrow
(
error
,
"Failed to create OID reference"
);
if
(
ref_exists
&&
!
force
)
return
git__throw
(
GIT_EEXISTS
,
"Failed to create OID reference. Reference already exists"
);
if
((
error
=
reference_available
(
repo
,
name
,
NULL
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to create reference"
);
/*
* If they old ref was of the same type, then we can just update
* it (once we've checked that the target is valid). Otherwise we
* need a new reference because we can't make a symbolic ref out
* of an oid one.
* If if didn't exist, then we need to create a new one anyway.
*/
if
(
ref
&&
ref
->
type
&
GIT_REF_OID
){
updated
=
1
;
}
else
{
ref
=
NULL
;
error
=
reference_create
(
&
ref
,
repo
,
name
,
GIT_REF_OID
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
}
error
=
reference_create
(
&
ref
,
repo
,
name
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
ref
->
flags
|=
GIT_REF_OID
;
/* set the oid; this will write the reference on disk */
error
=
git_reference_set_oid
(
ref
,
id
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
!
updated
){
error
=
git_hashtable_insert2
(
repo
->
references
.
loose_cache
,
ref
->
name
,
ref
,
&
old_ref
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
old_ref
!=
NULL
)
reference_free
((
git_reference
*
)
old_ref
);
if
(
ref_out
==
NULL
)
{
git_reference_free
(
ref
);
}
else
{
*
ref_out
=
ref
;
}
*
ref_out
=
ref
;
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create reference OID"
);
return
GIT_SUCCESS
;
cleanup:
reference_free
(
ref
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create reference OID"
);
git_
reference_free
(
ref
);
return
git__rethrow
(
error
,
"Failed to create reference OID"
);
}
/**
* Setters
*/
/*
* Change the OID target of a reference.
*
* For loose references, just change the oid in memory
* and overwrite the file in disk.
*
* For packed files, this is not pretty:
* For performance reasons, we write the new reference
* loose on disk (it replaces the old on the packfile),
* but we cannot invalidate the pointer to the reference,
* and most importantly, the `packfile` object must stay
* consistent with the representation of the packfile
* on disk. This is what we need to:
* For both loose and packed references, just change
* the oid in memory and (over)write the file in disk.
*
* 1. Copy the reference
* 2. Change the oid on the original
* 3. Write the original to disk
* 4. Write the original to the loose cache
* 5. Replace the original with the copy (old reference) in the packfile cache
* We do not repack packed references because of performance
* reasons.
*/
int
git_reference_set_oid
(
git_reference
*
ref
,
const
git_oid
*
id
)
{
reference_oid
*
ref_oid
;
reference_oid
*
ref_old
=
NULL
;
int
error
=
GIT_SUCCESS
;
if
((
ref
->
type
&
GIT_REF_OID
)
==
0
)
return
git__throw
(
GIT_EINVALIDREFSTATE
,
"Failed to set OID target of reference. Not an OID reference"
);
ref_oid
=
(
reference_oid
*
)
ref
;
if
((
ref
->
flags
&
GIT_REF_OID
)
==
0
)
return
git__throw
(
GIT_EINVALIDREFSTATE
,
"Failed to set OID target of reference. Not an OID reference"
);
assert
(
ref
->
owner
);
/* Don't let the user create references to OIDs that
* don't exist in the ODB */
if
(
!
git_odb_exists
(
git_repository_database
(
ref
->
owner
),
id
))
return
git__throw
(
GIT_ENOTFOUND
,
"Failed to set OID target of reference. OID doesn't exist in ODB"
);
/* duplicate the reference;
* this copy will stay on the packfile cache */
if
(
ref
->
type
&
GIT_REF_PACKED
)
{
ref_old
=
git__malloc
(
sizeof
(
reference_oid
));
if
(
ref_old
==
NULL
)
return
GIT_ENOMEM
;
ref_old
->
ref
.
name
=
git__strdup
(
ref
->
name
);
if
(
ref_old
->
ref
.
name
==
NULL
)
{
git__free
(
ref_old
);
return
GIT_ENOMEM
;
}
}
return
git__throw
(
GIT_ENOTFOUND
,
"Failed to set OID target of reference. OID doesn't exist in ODB"
);
git_oid_cpy
(
&
ref_oid
->
oid
,
id
);
ref
->
type
&=
~
GIT_REF_HAS_PEEL
;
/* Update the OID value on `ref` */
git_oid_cpy
(
&
ref
->
target
.
oid
,
id
)
;
error
=
loose_write
(
ref
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
ref
->
type
&
GIT_REF_PACKED
)
{
/* insert the original on the loose cache */
error
=
git_hashtable_insert
(
ref
->
owner
->
references
.
loose_cache
,
ref
->
name
,
ref
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
ref
->
type
&=
~
GIT_REF_PACKED
;
/* replace the original in the packfile with the copy */
error
=
git_hashtable_insert
(
ref
->
owner
->
references
.
packfile
,
ref_old
->
ref
.
name
,
ref_old
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
}
return
GIT_SUCCESS
;
cleanup:
reference_free
((
git_reference
*
)
ref_old
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to set OID target of reference"
);
return
git__rethrow
(
error
,
"Failed to set OID target of reference"
);
}
/*
...
...
@@ -1246,99 +1230,85 @@ cleanup:
*/
int
git_reference_set_target
(
git_reference
*
ref
,
const
char
*
target
)
{
reference_symbolic
*
ref_sym
;
int
error
;
char
normalized
[
GIT_REFNAME_MAX
];
if
((
ref
->
type
&
GIT_REF_SYMBOLIC
)
==
0
)
return
git__throw
(
GIT_EINVALIDREFSTATE
,
"Failed to set reference target. Not a symbolic reference"
);
if
((
ref
->
flags
&
GIT_REF_SYMBOLIC
)
==
0
)
return
git__throw
(
GIT_EINVALIDREFSTATE
,
"Failed to set reference target. Not a symbolic reference"
);
ref_sym
=
(
reference_symbolic
*
)
ref
;
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
target
,
0
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to set reference target. Invalid target name"
);
git__free
(
ref
_sym
->
target
);
ref
_sym
->
target
=
git__strdup
(
target
);
if
(
ref
_sym
->
target
==
NULL
)
git__free
(
ref
->
target
.
symbolic
);
ref
->
target
.
symbolic
=
git__strdup
(
normalized
);
if
(
ref
->
target
.
symbolic
==
NULL
)
return
GIT_ENOMEM
;
return
loose_write
(
ref
);
}
/**
* Other
*/
int
git_reference_rename
(
git_reference
*
ref
,
const
char
*
new_name
,
int
force
)
{
int
error
;
char
*
old_name
=
NULL
;
char
aux_path
[
GIT_PATH_MAX
];
char
normalized
[
GIT_REFNAME_MAX
];
const
char
*
target_ref
=
NULL
;
const
char
*
head_target
=
NULL
;
const
git_oid
*
target_oid
=
NULL
;
git_reference
*
new_ref
=
NULL
,
*
head
=
NULL
;
git_reference
*
existing_ref
=
NULL
,
*
head
=
NULL
;
assert
(
ref
);
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
new_name
,
ref
->
flags
&
GIT_REF_OID
);
error
=
normalize_name
(
normalized
,
sizeof
(
normalized
),
new_name
,
ref
->
type
&
GIT_REF_OID
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to rename reference. Invalid name"
);
new_name
=
normalized
;
error
=
git_reference_lookup
(
&
new_ref
,
ref
->
owner
,
new_name
);
if
(
error
==
GIT_SUCCESS
)
{
if
(
!
force
)
return
git__throw
(
GIT_EEXISTS
,
"Failed to rename reference. Reference already exists"
);
error
=
git_reference_delete
(
new_ref
);
}
/* If we are forcing the rename, try to lookup a reference with the
* new one. If the lookup succeeds, we need to delete that ref
* before the renaming can proceed */
if
(
force
)
{
error
=
git_reference_lookup
(
&
existing_ref
,
ref
->
owner
,
new_name
);
if
(
error
<
GIT_SUCCESS
)
{
git_path_join
(
aux_path
,
ref
->
owner
->
path_repository
,
new_name
);
/* If we couldn't read the reference because it doesn't
* exist it's ok - otherwise return */
if
(
git_futils_isfile
(
aux_path
)
==
GIT_SUCCESS
)
if
(
error
==
GIT_SUCCESS
)
{
error
=
git_reference_delete
(
existing_ref
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to rename reference. "
"The existing reference cannot be deleted"
);
}
else
if
(
error
!=
GIT_ENOTFOUND
)
goto
cleanup
;
}
if
((
error
=
reference_available
(
ref
->
owner
,
new_name
,
ref
->
name
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to rename reference. Reference already exists"
);
/*
* First, we backup the reference targets. Just keeping the old
* reference won't work, since we may have to remove it to create
* the new reference, e.g. when renaming foo/bar -> foo.
*/
old_name
=
git__strdup
(
ref
->
name
);
if
(
ref
->
type
&
GIT_REF_SYMBOLIC
)
{
if
((
target_ref
=
git_reference_target
(
ref
))
==
NULL
)
goto
cleanup
;
/* If we're not forcing the rename, check if the reference exists.
* If it does, renaming cannot continue */
}
else
{
if
((
target_oid
=
git_reference_oid
(
ref
))
==
NULL
)
int
exists
;
error
=
reference_exists
(
&
exists
,
ref
->
owner
,
normalized
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
if
(
exists
)
return
git__throw
(
GIT_EEXISTS
,
"Failed to rename reference. Reference already exists"
);
}
if
((
error
=
reference_available
(
ref
->
owner
,
new_name
,
ref
->
name
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to rename reference. Reference already exists"
);
/*
* Now delete the old ref and remove an possibly existing directory
* named `new_name`.
* named `new_name`. Note that using the internal `reference_delete`
* method deletes the ref from disk but doesn't free the pointer, so
* we can still access the ref's attributes for creating the new one
*/
if
(
ref
->
type
&
GIT_REF_PACKED
)
{
ref
->
type
&=
~
GIT_REF_PACKED
;
git_hashtable_remove
(
ref
->
owner
->
references
.
packfile
,
old_name
);
if
((
error
=
packed_write
(
ref
->
owner
))
<
GIT_SUCCESS
)
goto
rollback
;
}
else
{
git_path_join
(
aux_path
,
ref
->
owner
->
path_repository
,
old_name
);
if
((
error
=
p_unlink
(
aux_path
))
<
GIT_SUCCESS
)
goto
cleanup
;
git_hashtable_remove
(
ref
->
owner
->
references
.
loose_cache
,
old_name
);
}
if
((
error
=
reference_delete
(
ref
))
<
GIT_SUCCESS
)
goto
cleanup
;
git_path_join
(
aux_path
,
ref
->
owner
->
path_repository
,
new_name
);
if
(
git_futils_exists
(
aux_path
)
==
GIT_SUCCESS
)
{
...
...
@@ -1364,8 +1334,7 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
* TODO
*
*/
git_path_join_n
(
aux_path
,
3
,
ref
->
owner
->
path_repository
,
"logs"
,
old_name
);
git_path_join_n
(
aux_path
,
3
,
ref
->
owner
->
path_repository
,
"logs"
,
ref
->
name
);
if
(
git_futils_isfile
(
aux_path
)
==
GIT_SUCCESS
)
{
if
((
error
=
p_unlink
(
aux_path
))
<
GIT_SUCCESS
)
goto
rollback
;
...
...
@@ -1374,138 +1343,107 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
/*
* Finally we can create the new reference.
*/
if
(
ref
->
type
&
GIT_REF_SYMBOLIC
)
{
if
((
error
=
git_reference_create_symbolic
(
&
new_ref
,
ref
->
owner
,
new_name
,
target_ref
,
0
))
<
GIT_SUCCESS
)
goto
rollback
;
if
(
ref
->
flags
&
GIT_REF_SYMBOLIC
)
{
error
=
git_reference_create_symbolic
(
NULL
,
ref
->
owner
,
new_name
,
ref
->
target
.
symbolic
,
0
)
;
}
else
{
if
((
error
=
git_reference_create_oid
(
&
new_ref
,
ref
->
owner
,
new_name
,
target_oid
,
0
))
<
GIT_SUCCESS
)
goto
rollback
;
error
=
git_reference_create_oid
(
NULL
,
ref
->
owner
,
new_name
,
&
ref
->
target
.
oid
,
0
)
;
}
git__free
(
ref
->
name
);
ref
->
name
=
new_ref
->
name
;
/*
* No need in new_ref anymore. We created it to fix the change on disk.
* TODO: Refactoring required.
*/
new_ref
->
name
=
NULL
;
reference_free
(
new_ref
);
if
((
error
=
git_hashtable_insert
(
ref
->
owner
->
references
.
loose_cache
,
ref
->
name
,
ref
))
<
GIT_SUCCESS
)
goto
rollback
;
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
/*
* Check if we have to update HEAD.
*/
if
(
(
error
=
git_reference_lookup
(
&
head
,
ref
->
owner
,
GIT_HEAD_FILE
))
<
GIT_SUCCESS
)
error
=
git_reference_lookup
(
&
head
,
ref
->
owner
,
GIT_HEAD_FILE
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
head_target
=
git_reference_target
(
head
);
if
(
head_target
&&
!
strcmp
(
head_target
,
old_name
))
if
((
error
=
git_reference_create_symbolic
(
&
head
,
ref
->
owner
,
"HEAD"
,
ref
->
name
,
1
))
<
GIT_SUCCESS
)
goto
rollback
;
if
(
head_target
&&
!
strcmp
(
head_target
,
ref
->
name
))
{
error
=
git_reference_create_symbolic
(
&
head
,
ref
->
owner
,
"HEAD"
,
new_name
,
1
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
}
/*
* Change the name of the reference given by the user.
*/
git__free
(
ref
->
name
);
ref
->
name
=
git__strdup
(
new_name
);
/* The reference is no longer packed */
ref
->
flags
&=
~
GIT_REF_PACKED
;
cleanup:
git__free
(
old_name
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to rename reference"
);
/* We no longer need the newly created reference nor the head */
git_reference_free
(
head
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to rename reference"
);
rollback:
/*
* Try to create the old reference again.
*/
if
(
ref
->
type
&
GIT_REF_SYMBOLIC
)
error
=
git_reference_create_symbolic
(
&
new_ref
,
ref
->
owner
,
old_name
,
target_ref
,
0
);
if
(
ref
->
flags
&
GIT_REF_SYMBOLIC
)
error
=
git_reference_create_symbolic
(
NULL
,
ref
->
owner
,
ref
->
name
,
ref
->
target
.
symbolic
,
0
);
else
error
=
git_reference_create_oid
(
&
new_ref
,
ref
->
owner
,
old_name
,
target_oid
,
0
);
ref
->
name
=
old_name
;
error
=
git_reference_create_oid
(
NULL
,
ref
->
owner
,
ref
->
name
,
&
ref
->
target
.
oid
,
0
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to rename reference. Failed to rollback"
);
return
error
==
GIT_SUCCESS
?
git__rethrow
(
GIT_ERROR
,
"Failed to rename reference. Did rollback"
)
:
git__rethrow
(
error
,
"Failed to rename reference. Failed to rollback"
);
}
/*
* Delete a reference.
*
* If the reference is packed, this is an expensive
* operation. We need to remove the reference from
* the memory cache and then rewrite the whole pack
*
* If the reference is loose, we remove it on
* the filesystem and update the in-memory cache
* accordingly. We also make sure that an older version
* of it doesn't exist as a packed reference. If this
* is the case, this packed reference is removed as well.
*
* This obviously invalidates the `ref` pointer.
*/
int
git_reference_delete
(
git_reference
*
ref
)
int
git_reference_resolve
(
git_reference
**
ref_out
,
git_reference
*
ref
)
{
int
error
;
git_re
ference
*
reference
;
int
error
,
i
=
0
;
git_re
pository
*
repo
;
assert
(
ref
);
if
(
ref
->
type
&
GIT_REF_PACKED
)
{
/* load the existing packfile */
if
((
error
=
packed_load
(
ref
->
owner
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to delete reference"
);
if
(
git_hashtable_remove
(
ref
->
owner
->
references
.
packfile
,
ref
->
name
)
<
GIT_SUCCESS
)
return
git__throw
(
GIT_ENOTFOUND
,
"Reference not found"
);
error
=
packed_write
(
ref
->
owner
);
}
else
{
char
full_path
[
GIT_PATH_MAX
];
git_path_join
(
full_path
,
ref
->
owner
->
path_repository
,
ref
->
name
);
git_hashtable_remove
(
ref
->
owner
->
references
.
loose_cache
,
ref
->
name
);
error
=
p_unlink
(
full_path
);
if
(
error
<
GIT_SUCCESS
)
goto
cleanup
;
/* When deleting a loose reference, we have to ensure that an older
* packed version of it doesn't exist
*/
if
(
!
git_reference_lookup
(
&
reference
,
ref
->
owner
,
ref
->
name
))
{
assert
((
reference
->
type
&
GIT_REF_PACKED
)
!=
0
);
error
=
git_reference_delete
(
reference
);
}
}
cleanup:
reference_free
(
ref
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to delete reference"
);
}
int
git_reference_resolve
(
git_reference
**
resolved_ref
,
git_reference
*
ref
)
{
git_repository
*
repo
;
int
error
,
i
;
*
ref_out
=
NULL
;
repo
=
ref
->
owner
;
assert
(
resolved_ref
&&
ref
);
*
resolved_ref
=
NULL
;
/* If the reference is already resolved, we need to return a
* copy. Instead of duplicating `ref`, we look it up again to
* ensure the copy is out to date */
if
(
ref
->
flags
&
GIT_REF_OID
)
return
git_reference_lookup
(
ref_out
,
ref
->
owner
,
ref
->
name
);
if
((
error
=
loose_update
(
ref
))
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to resolve reference"
);
/* Otherwise, keep iterating until the reference is resolved */
for
(
i
=
0
;
i
<
MAX_NESTING_LEVEL
;
++
i
)
{
git_reference
*
new_ref
;
repo
=
ref
->
owner
;
error
=
git_reference_lookup
(
&
new_ref
,
repo
,
ref
->
target
.
symbolic
);
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
error
,
"Failed to resolve reference"
);
for
(
i
=
0
;
i
<
MAX_NESTING_LEVEL
;
++
i
)
{
reference_symbolic
*
ref_sym
;
/* Free intermediate references, except for the original one
* we've received */
if
(
i
>
0
)
git_reference_free
(
ref
);
*
resolved_ref
=
ref
;
ref
=
new_
ref
;
if
(
ref
->
type
&
GIT_REF_OID
)
/* When the reference we've just looked up is an OID, we've
* successfully resolved the symbolic ref */
if
(
ref
->
flags
&
GIT_REF_OID
)
{
*
ref_out
=
ref
;
return
GIT_SUCCESS
;
ref_sym
=
(
reference_symbolic
*
)
ref
;
if
((
error
=
git_reference_lookup
(
&
ref
,
repo
,
ref_sym
->
target
))
<
GIT_SUCCESS
)
return
error
;
}
}
return
git__throw
(
GIT_ENOMEM
,
"Failed to resolve reference. Reference is too nested"
);
return
git__throw
(
GIT_ENOMEM
,
"Failed to resolve reference. Reference is too nested"
);
}
int
git_reference_packall
(
git_repository
*
repo
)
...
...
@@ -1524,7 +1462,11 @@ int git_reference_packall(git_repository *repo)
return
packed_write
(
repo
);
}
int
git_reference_foreach
(
git_repository
*
repo
,
unsigned
int
list_flags
,
int
(
*
callback
)(
const
char
*
,
void
*
),
void
*
payload
)
int
git_reference_foreach
(
git_repository
*
repo
,
unsigned
int
list_flags
,
int
(
*
callback
)(
const
char
*
,
void
*
),
void
*
payload
)
{
int
error
;
struct
dirent_list_data
data
;
...
...
@@ -1540,7 +1482,8 @@ int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*c
GIT_HASHTABLE_FOREACH
(
repo
->
references
.
packfile
,
ref_name
,
_unused
,
if
((
error
=
callback
(
ref_name
,
payload
))
<
GIT_SUCCESS
)
return
git__throw
(
error
,
"Failed to list references. User callback failed"
);
return
git__throw
(
error
,
"Failed to list references. User callback failed"
);
);
}
...
...
@@ -1553,7 +1496,6 @@ int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*c
data
.
callback
=
callback
;
data
.
callback_payload
=
payload
;
git_path_join
(
refs_path
,
repo
->
path_repository
,
GIT_REFS_DIR
);
return
git_futils_direach
(
refs_path
,
GIT_PATH_MAX
,
_dirent_loose_listall
,
&
data
);
}
...
...
@@ -1563,7 +1505,10 @@ static int cb__reflist_add(const char *ref, void *data)
return
git_vector_insert
((
git_vector
*
)
data
,
git__strdup
(
ref
));
}
int
git_reference_listall
(
git_strarray
*
array
,
git_repository
*
repo
,
unsigned
int
list_flags
)
int
git_reference_listall
(
git_strarray
*
array
,
git_repository
*
repo
,
unsigned
int
list_flags
)
{
int
error
;
git_vector
ref_list
;
...
...
@@ -1576,7 +1521,8 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in
if
(
git_vector_init
(
&
ref_list
,
8
,
NULL
)
<
GIT_SUCCESS
)
return
GIT_ENOMEM
;
error
=
git_reference_foreach
(
repo
,
list_flags
,
&
cb__reflist_add
,
(
void
*
)
&
ref_list
);
error
=
git_reference_foreach
(
repo
,
list_flags
,
&
cb__reflist_add
,
(
void
*
)
&
ref_list
);
if
(
error
<
GIT_SUCCESS
)
{
git_vector_free
(
&
ref_list
);
...
...
@@ -1588,59 +1534,39 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in
return
GIT_SUCCESS
;
}
/*****************************************
* Init/free (repository API)
*****************************************/
int
git_repository__refcache_init
(
git_refcache
*
refs
)
int
git_reference_reload
(
git_reference
*
ref
)
{
assert
(
refs
);
refs
->
loose_cache
=
git_hashtable_alloc
(
default_table_size
,
reftable_hash
,
(
git_hash_keyeq_ptr
)(
&
git__strcmp_cb
));
int
error
=
reference_lookup
(
ref
);
/* packfile loaded lazily */
refs
->
packfile
=
NULL
;
refs
->
packfile_time
=
0
;
if
(
error
<
GIT_SUCCESS
)
{
git_reference_free
(
ref
);
return
git__rethrow
(
error
,
"Failed to reload reference"
);
}
return
(
refs
->
loose_cache
)
?
GIT_SUCCESS
:
GIT_ENOMEM
;
return
GIT_SUCCESS
;
}
void
git_repository__refcache_free
(
git_refcache
*
refs
)
{
git_reference
*
reference
;
const
void
*
GIT_UNUSED
(
_unused
);
assert
(
refs
);
GIT_HASHTABLE_FOREACH
(
refs
->
loose_cache
,
_unused
,
reference
,
reference_free
(
reference
);
);
git_hashtable_free
(
refs
->
loose_cache
);
if
(
refs
->
packfile
)
{
const
void
*
GIT_UNUSED
(
_unused
);
struct
packref
*
reference
;
GIT_HASHTABLE_FOREACH
(
refs
->
packfile
,
_unused
,
reference
,
reference_
free
(
reference
);
free
(
reference
);
);
git_hashtable_free
(
refs
->
packfile
);
}
}
/*****************************************
* Name normalization
*****************************************/
static
int
check_valid_ref_char
(
char
ch
)
static
int
is_valid_ref_char
(
char
ch
)
{
if
((
unsigned
)
ch
<=
' '
)
return
GIT_ERROR
;
return
0
;
switch
(
ch
)
{
case
'~'
:
...
...
@@ -1650,13 +1576,17 @@ static int check_valid_ref_char(char ch)
case
'?'
:
case
'['
:
case
'*'
:
return
GIT_ERROR
;
return
0
;
default:
return
GIT_SUCCESS
;
return
1
;
}
}
static
int
normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
,
int
is_oid_ref
)
static
int
normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
,
int
is_oid_ref
)
{
const
char
*
name_end
,
*
buffer_out_start
;
const
char
*
current
;
...
...
@@ -1673,26 +1603,33 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i
/* A refname can not be empty */
if
(
name_end
==
name
)
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name is empty"
);
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name is empty"
);
/* A refname can not end with a dot or a slash */
if
(
*
(
name_end
-
1
)
==
'.'
||
*
(
name_end
-
1
)
==
'/'
)
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name ends with dot or slash"
);
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name ends with dot or slash"
);
while
(
current
<
name_end
&&
out_size
)
{
if
(
check_valid_ref_char
(
*
current
))
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name contains invalid characters"
);
if
(
!
is_valid_ref_char
(
*
current
))
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. "
"Reference name contains invalid characters"
);
if
(
buffer_out
>
buffer_out_start
)
{
char
prev
=
*
(
buffer_out
-
1
);
/* A refname can not start with a dot nor contain a double dot */
if
(
*
current
==
'.'
&&
((
prev
==
'.'
)
||
(
prev
==
'/'
)))
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name starts with a dot or contains a double dot"
);
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. "
"Reference name starts with a dot or contains a double dot"
);
/* '@{' is forbidden within a refname */
if
(
*
current
==
'{'
&&
prev
==
'@'
)
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name contains '@{'"
);
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name contains '@{'"
);
/* Prevent multiple slashes from being added to the output */
if
(
*
current
==
'/'
&&
prev
==
'/'
)
{
...
...
@@ -1714,13 +1651,18 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i
/* Object id refname have to contain at least one slash, except
* for HEAD in a detached state or MERGE_HEAD if we're in the
* middle of a merge */
if
(
is_oid_ref
&&
!
contains_a_slash
&&
(
strcmp
(
name
,
GIT_HEAD_FILE
)
&&
strcmp
(
name
,
GIT_MERGE_HEAD_FILE
)
&&
strcmp
(
name
,
GIT_FETCH_HEAD_FILE
)))
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name contains no slashes"
);
if
(
is_oid_ref
&&
!
contains_a_slash
&&
strcmp
(
name
,
GIT_HEAD_FILE
)
!=
0
&&
strcmp
(
name
,
GIT_MERGE_HEAD_FILE
)
!=
0
&&
strcmp
(
name
,
GIT_FETCH_HEAD_FILE
)
!=
0
)
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name contains no slashes"
);
/* A refname can not end with ".lock" */
if
(
!
git__suffixcmp
(
name
,
GIT_FILELOCK_EXTENSION
))
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name ends with '.lock'"
);
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name ends with '.lock'"
);
*
buffer_out
=
'\0'
;
...
...
@@ -1730,17 +1672,25 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i
*/
if
(
is_oid_ref
&&
!
(
git__prefixcmp
(
buffer_out_start
,
GIT_REFS_DIR
)
||
strcmp
(
buffer_out_start
,
GIT_HEAD_FILE
)))
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. Reference name does not start with 'refs/'"
);
return
git__throw
(
GIT_EINVALIDREFNAME
,
"Failed to normalize name. "
"Reference name does not start with 'refs/'"
);
return
GIT_SUCCESS
;
}
int
git_reference__normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
)
int
git_reference__normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
)
{
return
normalize_name
(
buffer_out
,
out_size
,
name
,
0
);
}
int
git_reference__normalize_name_oid
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
)
int
git_reference__normalize_name_oid
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
)
{
return
normalize_name
(
buffer_out
,
out_size
,
name
,
1
);
}
src/refs.h
View file @
f8e8c8d4
...
...
@@ -33,21 +33,23 @@
#define GIT_REFNAME_MAX 1024
struct
git_reference
{
unsigned
int
flags
;
git_repository
*
owner
;
char
*
name
;
unsigned
int
type
;
time_t
mtime
;
union
{
git_oid
oid
;
char
*
symbolic
;
}
target
;
};
typedef
struct
{
git_hashtable
*
packfile
;
git_hashtable
*
loose_cache
;
time_t
packfile_time
;
}
git_refcache
;
void
git_repository__refcache_free
(
git_refcache
*
refs
);
int
git_repository__refcache_init
(
git_refcache
*
refs
);
int
git_reference__normalize_name
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
);
int
git_reference__normalize_name_oid
(
char
*
buffer_out
,
size_t
out_size
,
const
char
*
name
);
...
...
src/repository.c
View file @
f8e8c8d4
...
...
@@ -172,11 +172,6 @@ static git_repository *repository_alloc(void)
return
NULL
;
}
if
(
git_repository__refcache_init
(
&
repo
->
references
)
<
GIT_SUCCESS
)
{
git__free
(
repo
);
return
NULL
;
}
return
repo
;
}
...
...
@@ -603,8 +598,14 @@ static int repo_init_reinit(const char *repository_path, int is_bare)
static
int
repo_init_createhead
(
git_repository
*
repo
)
{
int
error
;
git_reference
*
head_reference
;
return
git_reference_create_symbolic
(
&
head_reference
,
repo
,
GIT_HEAD_FILE
,
GIT_REFS_HEADS_MASTER_FILE
,
0
);
error
=
git_reference_create_symbolic
(
&
head_reference
,
repo
,
GIT_HEAD_FILE
,
GIT_REFS_HEADS_MASTER_FILE
,
0
);
git_reference_free
(
head_reference
);
return
error
;
}
static
int
repo_init_structure
(
const
char
*
git_dir
,
int
is_bare
)
...
...
@@ -715,10 +716,15 @@ int git_repository_head_detached(git_repository *repo)
if
(
error
<
GIT_SUCCESS
)
return
error
;
if
(
git_reference_type
(
ref
)
==
GIT_REF_SYMBOLIC
)
if
(
git_reference_type
(
ref
)
==
GIT_REF_SYMBOLIC
)
{
git_reference_free
(
ref
);
return
0
;
}
error
=
git_odb_read_header
(
&
_size
,
&
type
,
repo
->
db
,
git_reference_oid
(
ref
));
git_reference_free
(
ref
);
if
(
error
<
GIT_SUCCESS
)
return
error
;
...
...
@@ -730,7 +736,7 @@ int git_repository_head_detached(git_repository *repo)
int
git_repository_head
(
git_reference
**
head_out
,
git_repository
*
repo
)
{
git_reference
*
ref
;
git_reference
*
ref
,
*
resolved_ref
;
int
error
;
*
head_out
=
NULL
;
...
...
@@ -739,11 +745,15 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
if
(
error
<
GIT_SUCCESS
)
return
git__rethrow
(
GIT_ENOTAREPO
,
"Failed to locate the HEAD"
);
error
=
git_reference_resolve
(
&
ref
,
ref
);
if
(
error
<
GIT_SUCCESS
)
error
=
git_reference_resolve
(
&
resolved_ref
,
ref
);
if
(
error
<
GIT_SUCCESS
)
{
git_reference_free
(
ref
);
return
git__rethrow
(
error
,
"Failed to resolve the HEAD"
);
}
git_reference_free
(
ref
);
*
head_out
=
ref
;
*
head_out
=
re
solved_re
f
;
return
GIT_SUCCESS
;
}
...
...
@@ -754,25 +764,36 @@ int git_repository_head_orphan(git_repository *repo)
error
=
git_repository_head
(
&
ref
,
repo
);
if
(
error
==
GIT_SUCCESS
)
git_reference_free
(
ref
);
return
error
==
GIT_ENOTFOUND
?
1
:
error
;
}
int
git_repository_is_empty
(
git_repository
*
repo
)
{
git_reference
*
head
,
*
branch
;
git_reference
*
head
=
NULL
,
*
branch
=
NULL
;
int
error
;
error
=
git_reference_lookup
(
&
head
,
repo
,
"HEAD"
);
if
(
error
<
GIT_SUCCESS
)
return
git__throw
(
error
,
"Corrupted repository. HEAD does not exist"
);
if
(
git_reference_type
(
head
)
!=
GIT_REF_SYMBOLIC
)
if
(
git_reference_type
(
head
)
!=
GIT_REF_SYMBOLIC
)
{
git_reference_free
(
head
);
return
0
;
}
if
(
strcmp
(
git_reference_target
(
head
),
"refs/heads/master"
)
!=
0
)
if
(
strcmp
(
git_reference_target
(
head
),
"refs/heads/master"
)
!=
0
)
{
git_reference_free
(
head
);
return
0
;
}
error
=
git_reference_resolve
(
&
branch
,
head
);
git_reference_free
(
head
);
git_reference_free
(
branch
);
return
error
==
GIT_ENOTFOUND
?
1
:
error
;
}
...
...
src/tag.c
View file @
f8e8c8d4
...
...
@@ -153,6 +153,8 @@ static int retrieve_tag_reference(git_reference **tag_reference_out, char *ref_n
git_reference
*
tag_ref
;
int
error
;
*
tag_reference_out
=
NULL
;
git_path_join
(
ref_name_out
,
GIT_REFS_TAGS_DIR
,
tag_name
);
error
=
git_reference_lookup
(
&
tag_ref
,
repo
,
ref_name_out
);
if
(
error
<
GIT_SUCCESS
)
...
...
@@ -224,6 +226,7 @@ static int git_tag_create__internal(
break
;
default:
git_reference_free
(
new_ref
);
return
git__rethrow
(
error
,
"Failed to create tag"
);
}
...
...
@@ -232,6 +235,7 @@ static int git_tag_create__internal(
if
(
new_ref
!=
NULL
)
{
if
(
!
allow_ref_overwrite
)
{
git_oid_cpy
(
oid
,
git_reference_oid
(
new_ref
));
git_reference_free
(
new_ref
);
return
git__throw
(
GIT_EEXISTS
,
"Tag already exists"
);
}
else
{
should_update_ref
=
1
;
...
...
@@ -239,8 +243,10 @@ static int git_tag_create__internal(
}
if
(
create_tag_annotation
)
{
if
((
error
=
write_tag_annotation
(
oid
,
repo
,
tag_name
,
target
,
tagger
,
message
))
<
GIT_SUCCESS
)
if
((
error
=
write_tag_annotation
(
oid
,
repo
,
tag_name
,
target
,
tagger
,
message
))
<
GIT_SUCCESS
)
{
git_reference_free
(
new_ref
);
return
error
;
}
}
else
git_oid_cpy
(
oid
,
git_object_id
(
target
));
...
...
@@ -249,6 +255,8 @@ static int git_tag_create__internal(
else
error
=
git_reference_set_oid
(
new_ref
,
oid
);
git_reference_free
(
new_ref
);
return
error
==
GIT_SUCCESS
?
GIT_SUCCESS
:
git__rethrow
(
error
,
"Failed to create tag"
);
}
...
...
@@ -281,7 +289,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
git_odb_stream
*
stream
;
git_odb_object
*
target_obj
;
git_reference
*
new_ref
;
git_reference
*
new_ref
=
NULL
;
char
ref_name
[
GIT_REFNAME_MAX
];
assert
(
oid
&&
buffer
);
...
...
@@ -309,6 +317,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
break
;
default:
git_reference_free
(
new_ref
);
return
git__rethrow
(
error
,
"Failed to create tag"
);
}
...
...
@@ -317,6 +326,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
if
(
new_ref
!=
NULL
)
{
if
(
!
allow_ref_overwrite
)
{
git_oid_cpy
(
oid
,
git_reference_oid
(
new_ref
));
git_reference_free
(
new_ref
);
return
git__throw
(
GIT_EEXISTS
,
"Tag already exists"
);
}
else
{
should_update_ref
=
1
;
...
...
@@ -324,22 +334,28 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
}
/* write the buffer */
if
((
error
=
git_odb_open_wstream
(
&
stream
,
repo
->
db
,
strlen
(
buffer
),
GIT_OBJ_TAG
))
<
GIT_SUCCESS
)
if
((
error
=
git_odb_open_wstream
(
&
stream
,
repo
->
db
,
strlen
(
buffer
),
GIT_OBJ_TAG
))
<
GIT_SUCCESS
)
{
git_reference_free
(
new_ref
);
return
git__rethrow
(
error
,
"Failed to create tag"
);
}
stream
->
write
(
stream
,
buffer
,
strlen
(
buffer
));
error
=
stream
->
finalize_write
(
oid
,
stream
);
stream
->
free
(
stream
);
if
(
error
<
GIT_SUCCESS
)
if
(
error
<
GIT_SUCCESS
)
{
git_reference_free
(
new_ref
);
return
git__rethrow
(
error
,
"Failed to create tag"
);
}
if
(
!
should_update_ref
)
error
=
git_reference_create_oid
(
&
new_ref
,
repo
,
ref_name
,
oid
,
0
);
else
error
=
git_reference_set_oid
(
new_ref
,
oid
);
git_reference_free
(
new_ref
);
git_signature_free
(
tag
.
tagger
);
git__free
(
tag
.
tag_name
);
git__free
(
tag
.
message
);
...
...
src/transports/local.c
View file @
f8e8c8d4
...
...
@@ -54,7 +54,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
{
const
char
peeled
[]
=
"^{}"
;
git_remote_head
*
head
;
git_reference
*
ref
;
git_reference
*
ref
,
*
resolved_ref
;
git_object
*
obj
=
NULL
;
int
error
=
GIT_SUCCESS
,
peel_len
,
ret
;
...
...
@@ -72,7 +72,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
if
(
error
<
GIT_SUCCESS
)
goto
out
;
error
=
git_reference_resolve
(
&
ref
,
ref
);
error
=
git_reference_resolve
(
&
re
solved_re
f
,
ref
);
if
(
error
<
GIT_SUCCESS
)
goto
out
;
...
...
@@ -111,6 +111,9 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
goto
out
;
out:
git_reference_free
(
ref
);
git_reference_free
(
resolved_ref
);
git_object_close
(
obj
);
if
(
error
<
GIT_SUCCESS
)
{
git__free
(
head
->
name
);
...
...
tests/t04-commit.c
View file @
f8e8c8d4
...
...
@@ -766,6 +766,8 @@ BEGIN_TEST(root0, "create a root commit")
git__free
(
head_old
);
git_commit_close
(
commit
);
git_repository_free
(
repo
);
git_reference_free
(
head
);
END_TEST
BEGIN_SUITE
(
commit
)
...
...
tests/t08-tag.c
View file @
f8e8c8d4
...
...
@@ -197,7 +197,6 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again")
git_tag_close
(
tag
);
git_repository_free
(
repo
);
END_TEST
BEGIN_TEST
(
write2
,
"Attempt to write a tag bearing the same name than an already existing tag"
)
...
...
@@ -266,6 +265,7 @@ BEGIN_TEST(write3, "Replace an already existing tag")
close_temp_repo
(
repo
);
git_reference_free
(
ref_tag
);
END_TEST
BEGIN_TEST
(
write4
,
"write a lightweight tag to the repository and read it again"
)
...
...
@@ -296,6 +296,8 @@ BEGIN_TEST(write4, "write a lightweight tag to the repository and read it again"
must_pass
(
git_tag_delete
(
repo
,
"light-tag"
));
git_repository_free
(
repo
);
git_reference_free
(
ref_tag
);
END_TEST
BEGIN_TEST
(
write5
,
"Attempt to write a lightweight tag bearing the same name than an already existing tag"
)
...
...
@@ -334,6 +336,8 @@ BEGIN_TEST(delete0, "Delete an already existing tag")
must_fail
(
git_reference_lookup
(
&
ref_tag
,
repo
,
"refs/tags/e90810b"
));
close_temp_repo
(
repo
);
git_reference_free
(
ref_tag
);
END_TEST
BEGIN_SUITE
(
tag
)
...
...
tests/t10-refs.c
View file @
f8e8c8d4
...
...
@@ -42,8 +42,8 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference")
must_pass
(
git_repository_open
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
loose_tag_ref_name
));
must_be_true
(
reference
->
type
&
GIT_REF_OID
);
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_type
(
reference
)
&
GIT_REF_OID
);
must_be_true
(
git_reference_is_packed
(
reference
)
==
0
);
must_be_true
(
strcmp
(
reference
->
name
,
loose_tag_ref_name
)
==
0
);
must_pass
(
git_object_lookup
(
&
object
,
repo
,
git_reference_oid
(
reference
),
GIT_OBJ_ANY
));
...
...
@@ -56,6 +56,8 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference")
git_object_close
(
object
);
git_repository_free
(
repo
);
git_reference_free
(
reference
);
END_TEST
BEGIN_TEST
(
readtag1
,
"lookup a loose tag reference that doesn't exist"
)
...
...
@@ -66,6 +68,8 @@ BEGIN_TEST(readtag1, "lookup a loose tag reference that doesn't exist")
must_fail
(
git_reference_lookup
(
&
reference
,
repo
,
non_existing_tag_ref_name
));
git_repository_free
(
repo
);
git_reference_free
(
reference
);
END_TEST
static
const
char
*
head_tracker_sym_ref_name
=
"head-tracker"
;
...
...
@@ -81,12 +85,12 @@ BEGIN_TEST(readsym0, "lookup a symbolic reference")
must_pass
(
git_repository_open
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
GIT_HEAD_FILE
));
must_be_true
(
reference
->
type
&
GIT_REF_SYMBOLIC
);
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_type
(
reference
)
&
GIT_REF_SYMBOLIC
);
must_be_true
(
git_reference_is_packed
(
reference
)
==
0
);
must_be_true
(
strcmp
(
reference
->
name
,
GIT_HEAD_FILE
)
==
0
);
must_pass
(
git_reference_resolve
(
&
resolved_ref
,
reference
));
must_be_true
(
resolved_ref
->
type
==
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
resolved_ref
)
==
GIT_REF_OID
);
must_pass
(
git_object_lookup
(
&
object
,
repo
,
git_reference_oid
(
resolved_ref
),
GIT_OBJ_ANY
));
must_be_true
(
object
!=
NULL
);
...
...
@@ -97,6 +101,9 @@ BEGIN_TEST(readsym0, "lookup a symbolic reference")
git_object_close
(
object
);
git_repository_free
(
repo
);
git_reference_free
(
reference
);
git_reference_free
(
resolved_ref
);
END_TEST
BEGIN_TEST
(
readsym1
,
"lookup a nested symbolic reference"
)
...
...
@@ -108,12 +115,12 @@ BEGIN_TEST(readsym1, "lookup a nested symbolic reference")
must_pass
(
git_repository_open
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
head_tracker_sym_ref_name
));
must_be_true
(
reference
->
type
&
GIT_REF_SYMBOLIC
);
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_type
(
reference
)
&
GIT_REF_SYMBOLIC
);
must_be_true
(
git_reference_is_packed
(
reference
)
==
0
);
must_be_true
(
strcmp
(
reference
->
name
,
head_tracker_sym_ref_name
)
==
0
);
must_pass
(
git_reference_resolve
(
&
resolved_ref
,
reference
));
must_be_true
(
resolved_ref
->
type
==
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
resolved_ref
)
==
GIT_REF_OID
);
must_pass
(
git_object_lookup
(
&
object
,
repo
,
git_reference_oid
(
resolved_ref
),
GIT_OBJ_ANY
));
must_be_true
(
object
!=
NULL
);
...
...
@@ -124,6 +131,9 @@ BEGIN_TEST(readsym1, "lookup a nested symbolic reference")
git_object_close
(
object
);
git_repository_free
(
repo
);
git_reference_free
(
reference
);
git_reference_free
(
resolved_ref
);
END_TEST
BEGIN_TEST
(
readsym2
,
"lookup the HEAD and resolve the master branch"
)
...
...
@@ -133,17 +143,22 @@ BEGIN_TEST(readsym2, "lookup the HEAD and resolve the master branch")
must_pass
(
git_repository_open
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
head_tracker_sym_ref_name
));
must_pass
(
git_reference_resolve
(
&
resolved
_ref
,
reference
));
comp_base_ref
=
resolved_ref
;
must_pass
(
git_reference_resolve
(
&
comp_base
_ref
,
reference
));
git_reference_free
(
reference
)
;
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
GIT_HEAD_FILE
));
must_pass
(
git_reference_resolve
(
&
resolved_ref
,
reference
));
must_pass
(
git_oid_cmp
(
git_reference_oid
(
comp_base_ref
),
git_reference_oid
(
resolved_ref
)));
git_reference_free
(
reference
);
git_reference_free
(
resolved_ref
);
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
current_head_target
));
must_pass
(
git_reference_resolve
(
&
resolved_ref
,
reference
));
must_pass
(
git_oid_cmp
(
git_reference_oid
(
comp_base_ref
),
git_reference_oid
(
resolved_ref
)));
git_reference_free
(
reference
);
git_reference_free
(
resolved_ref
);
git_reference_free
(
comp_base_ref
);
git_repository_free
(
repo
);
END_TEST
...
...
@@ -160,6 +175,10 @@ BEGIN_TEST(readsym3, "lookup the master branch and then the HEAD")
must_pass
(
git_oid_cmp
(
git_reference_oid
(
master_ref
),
git_reference_oid
(
resolved_ref
)));
git_repository_free
(
repo
);
git_reference_free
(
reference
);
git_reference_free
(
resolved_ref
);
git_reference_free
(
master_ref
);
END_TEST
static
const
char
*
packed_head_name
=
"refs/heads/packed"
;
...
...
@@ -173,8 +192,8 @@ BEGIN_TEST(readpacked0, "lookup a packed reference")
must_pass
(
git_repository_open
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
packed_head_name
));
must_be_true
(
reference
->
type
&
GIT_REF_OID
);
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
!=
0
);
must_be_true
(
git_reference_type
(
reference
)
&
GIT_REF_OID
);
must_be_true
(
git_reference_is_packed
(
reference
)
);
must_be_true
(
strcmp
(
reference
->
name
,
packed_head_name
)
==
0
);
must_pass
(
git_object_lookup
(
&
object
,
repo
,
git_reference_oid
(
reference
),
GIT_OBJ_ANY
));
...
...
@@ -183,6 +202,8 @@ BEGIN_TEST(readpacked0, "lookup a packed reference")
git_object_close
(
object
);
git_repository_free
(
repo
);
git_reference_free
(
reference
);
END_TEST
BEGIN_TEST
(
readpacked1
,
"assure that a loose reference is looked up before a packed reference"
)
...
...
@@ -192,11 +213,13 @@ BEGIN_TEST(readpacked1, "assure that a loose reference is looked up before a pac
must_pass
(
git_repository_open
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
packed_head_name
));
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
packed_test_head_name
));
must_be_true
(
reference
->
type
&
GIT_REF_OID
);
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_type
(
reference
)
&
GIT_REF_OID
);
must_be_true
(
git_reference_is_packed
(
reference
)
==
0
);
must_be_true
(
strcmp
(
reference
->
name
,
packed_test_head_name
)
==
0
);
git_repository_free
(
repo
);
git_reference_free
(
reference
);
END_TEST
BEGIN_TEST
(
create0
,
"create a new symbolic reference"
)
...
...
@@ -219,13 +242,13 @@ BEGIN_TEST(create0, "create a new symbolic reference")
/* Ensure the reference can be looked-up... */
must_pass
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
new_head_tracker
));
must_be_true
(
looked_up_ref
->
type
&
GIT_REF_SYMBOLIC
);
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_type
(
looked_up_ref
)
&
GIT_REF_SYMBOLIC
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
==
0
);
must_be_true
(
strcmp
(
looked_up_ref
->
name
,
new_head_tracker
)
==
0
);
/* ...peeled.. */
must_pass
(
git_reference_resolve
(
&
resolved_ref
,
looked_up_ref
));
must_be_true
(
resolved_ref
->
type
==
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
resolved_ref
)
==
GIT_REF_OID
);
/* ...and that it points to the current master tip */
must_be_true
(
git_oid_cmp
(
&
id
,
git_reference_oid
(
resolved_ref
))
==
0
);
...
...
@@ -240,6 +263,10 @@ BEGIN_TEST(create0, "create a new symbolic reference")
must_be_true
(
git_oid_cmp
(
&
id
,
git_reference_oid
(
resolved_ref
))
==
0
);
close_temp_repo
(
repo2
);
git_reference_free
(
new_reference
);
git_reference_free
(
looked_up_ref
);
git_reference_free
(
resolved_ref
);
END_TEST
BEGIN_TEST
(
create1
,
"create a deep symbolic reference"
)
...
...
@@ -261,6 +288,10 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
must_be_true
(
git_oid_cmp
(
&
id
,
git_reference_oid
(
resolved_ref
))
==
0
);
close_temp_repo
(
repo
);
git_reference_free
(
new_reference
);
git_reference_free
(
looked_up_ref
);
git_reference_free
(
resolved_ref
);
END_TEST
BEGIN_TEST
(
create2
,
"create a new OID reference"
)
...
...
@@ -283,8 +314,8 @@ BEGIN_TEST(create2, "create a new OID reference")
/* Ensure the reference can be looked-up... */
must_pass
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
new_head
));
must_be_true
(
looked_up_ref
->
type
&
GIT_REF_OID
);
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_type
(
looked_up_ref
)
&
GIT_REF_OID
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
==
0
);
must_be_true
(
strcmp
(
looked_up_ref
->
name
,
new_head
)
==
0
);
/* ...and that it points to the current master tip */
...
...
@@ -299,6 +330,9 @@ BEGIN_TEST(create2, "create a new OID reference")
must_be_true
(
git_oid_cmp
(
&
id
,
git_reference_oid
(
looked_up_ref
))
==
0
);
close_temp_repo
(
repo2
);
git_reference_free
(
new_reference
);
git_reference_free
(
looked_up_ref
);
END_TEST
BEGIN_TEST
(
create3
,
"Can not create a new OID reference which targets at an unknown id"
)
...
...
@@ -349,6 +383,9 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference")
must_be_true
(
!
strcmp
(
git_reference_target
(
ref
),
ref_master_name
));
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
git_reference_free
(
branch_ref
);
END_TEST
BEGIN_TEST
(
overwrite1
,
"Overwrite an existing object id reference"
)
...
...
@@ -359,14 +396,14 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference")
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_master_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
/* Create it */
must_pass
(
git_reference_create_oid
(
&
ref
,
repo
,
ref_name
,
&
id
,
0
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_test_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
/* Ensure we can't overwrite unless we force it */
...
...
@@ -378,6 +415,8 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference")
must_be_true
(
!
git_oid_cmp
(
&
id
,
git_reference_oid
(
ref
)));
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
END_TEST
BEGIN_TEST
(
overwrite2
,
"Overwrite an existing object id reference with a symbolic one"
)
...
...
@@ -388,7 +427,7 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_master_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
must_pass
(
git_reference_create_oid
(
&
ref
,
repo
,
ref_name
,
&
id
,
0
));
...
...
@@ -401,6 +440,8 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli
must_be_true
(
!
strcmp
(
git_reference_target
(
ref
),
ref_master_name
));
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
END_TEST
BEGIN_TEST
(
overwrite3
,
"Overwrite an existing symbolic reference with an object id one"
)
...
...
@@ -411,7 +452,7 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_master_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
/* Create the symbolic ref */
...
...
@@ -426,6 +467,8 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object
must_be_true
(
!
git_oid_cmp
(
git_reference_oid
(
ref
),
&
id
));
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
END_TEST
BEGIN_TEST
(
pack0
,
"create a packfile for an empty folder"
)
...
...
@@ -451,7 +494,7 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
/* Ensure a known loose ref can be looked up */
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
loose_tag_ref_name
));
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
reference
)
==
0
);
must_be_true
(
strcmp
(
reference
->
name
,
loose_tag_ref_name
)
==
0
);
/*
...
...
@@ -467,7 +510,7 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
/* Ensure the known ref can still be looked up but is now packed */
must_pass
(
git_reference_lookup
(
&
reference
,
repo
,
loose_tag_ref_name
));
must_be_true
(
(
reference
->
type
&
GIT_REF_PACKED
)
!=
0
);
must_be_true
(
git_reference_is_packed
(
reference
)
);
must_be_true
(
strcmp
(
reference
->
name
,
loose_tag_ref_name
)
==
0
);
/* Ensure the known ref has been removed from the loose folder structure */
...
...
@@ -475,6 +518,8 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
must_pass
(
!
git_futils_exists
(
temp_path
));
close_temp_repo
(
repo
);
git_reference_free
(
reference
);
END_TEST
BEGIN_TEST
(
rename0
,
"rename a loose reference"
)
...
...
@@ -493,7 +538,7 @@ BEGIN_TEST(rename0, "rename a loose reference")
must_pass
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
loose_tag_ref_name
));
/* ... which is indeed loose */
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
==
0
);
/* Now that the reference is renamed... */
must_pass
(
git_reference_rename
(
looked_up_ref
,
new_name
,
0
));
...
...
@@ -507,14 +552,17 @@ BEGIN_TEST(rename0, "rename a loose reference")
must_be_true
(
!
strcmp
(
another_looked_up_ref
->
name
,
new_name
));
/* .. the ref is still loose... */
must_be_true
(
(
another_looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
another_looked_up_ref
)
==
0
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
==
0
);
/* ...and the ref can be found in the file system */
git_path_join
(
temp_path
,
repo
->
path_repository
,
new_name
);
must_pass
(
git_futils_exists
(
temp_path
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
git_reference_free
(
another_looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename1
,
"rename a packed reference (should make it loose)"
)
...
...
@@ -533,7 +581,7 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
must_pass
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
packed_head_name
));
/* .. and it's packed */
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
!=
0
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
!=
0
);
/* Now that the reference is renamed... */
must_pass
(
git_reference_rename
(
looked_up_ref
,
brand_new_name
,
0
));
...
...
@@ -547,14 +595,17 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
must_be_true
(
!
strcmp
(
another_looked_up_ref
->
name
,
brand_new_name
));
/* .. the ref is no longer packed... */
must_be_true
(
(
another_looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
another_looked_up_ref
)
==
0
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
==
0
);
/* ...and the ref now happily lives in the file system */
git_path_join
(
temp_path
,
repo
->
path_repository
,
brand_new_name
);
must_pass
(
git_futils_exists
(
temp_path
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
git_reference_free
(
another_looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename2
,
"renaming a packed reference does not pack another reference which happens to be in both loose and pack state"
)
...
...
@@ -573,13 +624,13 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference
must_pass
(
git_reference_lookup
(
&
another_looked_up_ref
,
repo
,
packed_test_head_name
));
/* Ensure it's loose */
must_be_true
(
(
another_looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
another_looked_up_ref
)
==
0
);
/* Lookup the reference to rename */
must_pass
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
packed_head_name
));
/* Ensure it's packed */
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
!=
0
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
!=
0
);
/* Now that the reference is renamed... */
must_pass
(
git_reference_rename
(
looked_up_ref
,
brand_new_name
,
0
));
...
...
@@ -588,12 +639,15 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference
must_pass
(
git_reference_lookup
(
&
another_looked_up_ref
,
repo
,
packed_test_head_name
));
/* Ensure it's loose */
must_be_true
(
(
another_looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
another_looked_up_ref
)
==
0
);
/* Ensure the other ref still exists on the file system */
must_pass
(
git_futils_exists
(
temp_path
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
git_reference_free
(
another_looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename3
,
"can not rename a reference with the name of an existing reference"
)
...
...
@@ -613,6 +667,8 @@ BEGIN_TEST(rename3, "can not rename a reference with the name of an existing ref
must_be_true
(
!
strcmp
(
looked_up_ref
->
name
,
packed_head_name
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename4
,
"can not rename a reference with an invalid name"
)
...
...
@@ -635,6 +691,8 @@ BEGIN_TEST(rename4, "can not rename a reference with an invalid name")
must_be_true
(
!
strcmp
(
looked_up_ref
->
name
,
packed_test_head_name
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename5
,
"can force-rename a packed reference with the name of an existing loose and packed reference"
)
...
...
@@ -660,6 +718,8 @@ BEGIN_TEST(rename5, "can force-rename a packed reference with the name of an exi
must_fail
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
packed_head_name
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename6
,
"can force-rename a loose reference with the name of an existing loose reference"
)
...
...
@@ -685,6 +745,8 @@ BEGIN_TEST(rename6, "can force-rename a loose reference with the name of an exis
must_fail
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
"refs/heads/br2"
));
close_temp_repo
(
repo
);
git_reference_free
(
looked_up_ref
);
END_TEST
static
const
char
*
ref_one_name
=
"refs/heads/one/branch"
;
...
...
@@ -699,7 +761,7 @@ BEGIN_TEST(rename7, "can not overwrite name of existing reference")
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_master_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
...
...
@@ -717,6 +779,11 @@ BEGIN_TEST(rename7, "can not overwrite name of existing reference")
must_fail
(
git_reference_lookup
(
&
ref_one_new
,
repo
,
ref_one_name_new
));
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
git_reference_free
(
ref_one
);
git_reference_free
(
ref_one_new
);
git_reference_free
(
ref_two
);
END_TEST
static
const
char
*
ref_two_name_new
=
"refs/heads/two/two"
;
...
...
@@ -729,7 +796,7 @@ BEGIN_TEST(rename8, "can be renamed to a new name prefixed with the old name")
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_master_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
...
...
@@ -748,6 +815,10 @@ BEGIN_TEST(rename8, "can be renamed to a new name prefixed with the old name")
must_fail
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
ref_two_name
));
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
git_reference_free
(
ref_two
);
git_reference_free
(
looked_up_ref
);
END_TEST
BEGIN_TEST
(
rename9
,
"can move a reference to a upper reference hierarchy"
)
...
...
@@ -758,7 +829,7 @@ BEGIN_TEST(rename9, "can move a reference to a upper reference hierarchy")
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
ref_master_name
));
must_be_true
(
ref
->
type
&
GIT_REF_OID
);
must_be_true
(
git_reference_type
(
ref
)
&
GIT_REF_OID
);
git_oid_cpy
(
&
id
,
git_reference_oid
(
ref
));
...
...
@@ -794,7 +865,7 @@ BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove
must_pass
(
git_reference_lookup
(
&
looked_up_ref
,
repo
,
packed_test_head_name
));
/* Ensure it's the loose version that has been found */
must_be_true
(
(
looked_up_ref
->
type
&
GIT_REF_PACKED
)
==
0
);
must_be_true
(
git_reference_is_packed
(
looked_up_ref
)
==
0
);
/* Now that the reference is deleted... */
must_pass
(
git_reference_delete
(
looked_up_ref
));
...
...
@@ -806,6 +877,42 @@ BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove
must_pass
(
!
git_futils_exists
(
temp_path
));
close_temp_repo
(
repo
);
git_reference_free
(
another_looked_up_ref
);
END_TEST
BEGIN_TEST
(
delete1
,
"can delete a just packed reference"
)
git_reference
*
ref
;
git_repository
*
repo
;
git_oid
id
;
const
char
*
new_ref
=
"refs/heads/new_ref"
;
git_oid_fromstr
(
&
id
,
current_master_tip
);
must_pass
(
open_temp_repo
(
&
repo
,
REPOSITORY_FOLDER
));
/* Create and write the new object id reference */
must_pass
(
git_reference_create_oid
(
&
ref
,
repo
,
new_ref
,
&
id
,
0
));
/* Lookup the reference */
must_pass
(
git_reference_lookup
(
&
ref
,
repo
,
new_ref
));
/* Ensure it's a loose reference */
must_be_true
(
git_reference_is_packed
(
ref
)
==
0
);
/* Pack all existing references */
must_pass
(
git_reference_packall
(
repo
));
/* Reload the reference from disk */
must_pass
(
git_reference_reload
(
ref
));
/* Ensure it's a packed reference */
must_be_true
(
git_reference_is_packed
(
ref
)
==
1
);
/* This should pass */
must_pass
(
git_reference_delete
(
ref
));
close_temp_repo
(
repo
);
END_TEST
static
int
ensure_refname_normalized
(
int
is_oid_ref
,
const
char
*
input_refname
,
const
char
*
expected_refname
)
...
...
@@ -1101,6 +1208,9 @@ BEGIN_TEST(reflog0, "write a reflog for a given reference and ensure it can be r
git_signature_free
(
committer
);
git_reflog_free
(
reflog
);
close_temp_repo
(
repo2
);
git_reference_free
(
ref
);
git_reference_free
(
lookedup_ref
);
END_TEST
BEGIN_TEST
(
reflog1
,
"avoid writing an obviously wrong reflog"
)
...
...
@@ -1129,6 +1239,8 @@ BEGIN_TEST(reflog1, "avoid writing an obviously wrong reflog")
git_signature_free
(
committer
);
close_temp_repo
(
repo
);
git_reference_free
(
ref
);
END_TEST
BEGIN_SUITE
(
refs
)
...
...
@@ -1172,6 +1284,7 @@ BEGIN_SUITE(refs)
ADD_TEST
(
rename9
);
ADD_TEST
(
delete0
);
ADD_TEST
(
delete1
);
ADD_TEST
(
list0
);
ADD_TEST
(
list1
);
...
...
tests/t12-repo.c
View file @
f8e8c8d4
...
...
@@ -286,6 +286,8 @@ BEGIN_TEST(detached0, "test if HEAD is detached")
must_be_true
(
git_repository_head_detached
(
repo
)
==
0
);
git_repository_free
(
repo
);
git_reference_free
(
ref
);
END_TEST
BEGIN_TEST
(
orphan0
,
"test if HEAD is orphan"
)
...
...
@@ -305,6 +307,8 @@ BEGIN_TEST(orphan0, "test if HEAD is orphan")
must_be_true
(
git_repository_head_orphan
(
repo
)
==
0
);
git_repository_free
(
repo
);
git_reference_free
(
ref
);
END_TEST
#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git"
...
...
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