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
c5d41d46
Unverified
Commit
c5d41d46
authored
Aug 03, 2020
by
Edward Thomson
Committed by
GitHub
Aug 03, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5563 from pks-t/pks/worktree-heads
Access HEAD via the refdb backends
parents
52ccbc5d
ce4cb073
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
119 additions
and
245 deletions
+119
-245
src/branch.c
+18
-20
src/refs.c
+25
-99
src/refs.h
+0
-18
src/repository.c
+42
-44
src/repository.h
+4
-27
tests/worktree/refs.c
+4
-1
tests/worktree/repository.c
+4
-1
tests/worktree/worktree.c
+22
-35
No files found.
src/branch.c
View file @
c5d41d46
...
...
@@ -134,39 +134,37 @@ int git_branch_create_from_annotated(
repository
,
branch_name
,
commit
->
commit
,
commit
->
description
,
force
);
}
static
int
branch_
equals
(
git_repository
*
repo
,
const
char
*
path
,
void
*
payload
)
static
int
branch_
is_checked_out
(
git_repository
*
worktree
,
void
*
payload
)
{
git_reference
*
branch
=
(
git_reference
*
)
payload
;
git_reference
*
head
=
NULL
;
int
e
qual
=
0
;
int
e
rror
;
if
(
git_reference__read_head
(
&
head
,
repo
,
path
)
<
0
||
git_reference_type
(
head
)
!=
GIT_REFERENCE_SYMBOLIC
)
goto
done
;
if
(
git_repository_is_bare
(
worktree
))
return
0
;
equal
=
!
git__strcmp
(
head
->
target
.
symbolic
,
branch
->
name
);
if
((
error
=
git_reference_lookup
(
&
head
,
worktree
,
GIT_HEAD_FILE
))
<
0
)
{
if
(
error
==
GIT_ENOTFOUND
)
error
=
0
;
goto
out
;
}
done:
if
(
git_reference_type
(
head
)
!=
GIT_REFERENCE_SYMBOLIC
)
goto
out
;
error
=
!
git__strcmp
(
head
->
target
.
symbolic
,
branch
->
name
);
out:
git_reference_free
(
head
);
return
e
qual
;
return
e
rror
;
}
int
git_branch_is_checked_out
(
const
git_reference
*
branch
)
{
git_repository
*
repo
;
int
flags
=
0
;
assert
(
branch
);
if
(
!
git_reference_is_branch
(
branch
))
return
0
;
repo
=
git_reference_owner
(
branch
);
if
(
git_repository_is_bare
(
repo
))
flags
|=
GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO
;
return
git_repository_foreach_head
(
repo
,
branch_equals
,
flags
,
(
void
*
)
branch
)
==
1
;
return
git_repository_foreach_worktree
(
git_reference_owner
(
branch
),
branch_is_checked_out
,
(
void
*
)
branch
)
==
1
;
}
int
git_branch_delete
(
git_reference
*
branch
)
...
...
src/refs.c
View file @
c5d41d46
...
...
@@ -237,40 +237,6 @@ int git_reference_lookup_resolved(
return
0
;
}
int
git_reference__read_head
(
git_reference
**
out
,
git_repository
*
repo
,
const
char
*
path
)
{
git_buf
reference
=
GIT_BUF_INIT
;
char
*
name
=
NULL
;
int
error
;
if
((
error
=
git_futils_readbuffer
(
&
reference
,
path
))
<
0
)
goto
out
;
git_buf_rtrim
(
&
reference
);
if
(
git__strncmp
(
reference
.
ptr
,
GIT_SYMREF
,
strlen
(
GIT_SYMREF
))
==
0
)
{
git_buf_consume
(
&
reference
,
reference
.
ptr
+
strlen
(
GIT_SYMREF
));
name
=
git_path_basename
(
path
);
if
((
*
out
=
git_reference__alloc_symbolic
(
name
,
reference
.
ptr
))
==
NULL
)
{
error
=
-
1
;
goto
out
;
}
}
else
{
if
((
error
=
git_reference_lookup
(
out
,
repo
,
reference
.
ptr
))
<
0
)
goto
out
;
}
out:
git__free
(
name
);
git_buf_dispose
(
&
reference
);
return
error
;
}
int
git_reference_dwim
(
git_reference
**
out
,
git_repository
*
repo
,
const
char
*
refname
)
{
int
error
=
0
,
i
;
...
...
@@ -605,84 +571,33 @@ int git_reference_symbolic_set_target(
typedef
struct
{
const
char
*
old_name
;
git_refname_t
new_name
;
}
re
name_cb_data
;
}
re
fs_update_head_payload
;
static
int
update_wt_heads
(
git_repository
*
repo
,
const
char
*
path
,
void
*
payload
)
static
int
refs_update_head
(
git_repository
*
worktree
,
void
*
_
payload
)
{
rename_cb_data
*
data
=
(
rename_cb_data
*
)
payload
;
git_reference
*
head
=
NULL
;
char
*
gitdir
=
NULL
;
refs_update_head_payload
*
payload
=
(
refs_update_head_payload
*
)
_payload
;
git_reference
*
head
=
NULL
,
*
updated
=
NULL
;
int
error
;
if
((
error
=
git_reference__read_head
(
&
head
,
repo
,
path
))
<
0
)
{
git_error_set
(
GIT_ERROR_REFERENCE
,
"could not read HEAD when renaming references"
);
goto
out
;
}
if
((
gitdir
=
git_path_dirname
(
path
))
==
NULL
)
{
error
=
-
1
;
if
((
error
=
git_reference_lookup
(
&
head
,
worktree
,
GIT_HEAD_FILE
))
<
0
)
goto
out
;
}
if
(
git_reference_type
(
head
)
!=
GIT_REFERENCE_SYMBOLIC
||
git__strcmp
(
head
->
target
.
symbolic
,
data
->
old_name
)
!=
0
)
{
error
=
0
;
git__strcmp
(
git_reference_symbolic_target
(
head
),
payload
->
old_name
)
!=
0
)
goto
out
;
}
/* Update HEAD it was pointing to the reference being renamed */
if
((
error
=
git_re
pository_create_head
(
gitdir
,
data
->
new_name
))
<
0
)
{
/* Update HEAD i
f i
t was pointing to the reference being renamed */
if
((
error
=
git_re
ference_symbolic_set_target
(
&
updated
,
head
,
payload
->
new_name
,
NULL
))
<
0
)
{
git_error_set
(
GIT_ERROR_REFERENCE
,
"failed to update HEAD after renaming reference"
);
goto
out
;
}
out:
git_reference_free
(
updated
);
git_reference_free
(
head
);
git__free
(
gitdir
);
return
error
;
}
static
int
reference__rename
(
git_reference
**
out
,
git_reference
*
ref
,
const
char
*
new_name
,
int
force
,
const
git_signature
*
signature
,
const
char
*
message
)
{
git_repository
*
repo
;
git_refname_t
normalized
;
bool
should_head_be_updated
=
false
;
int
error
=
0
;
assert
(
ref
&&
new_name
&&
signature
);
repo
=
git_reference_owner
(
ref
);
if
((
error
=
reference_normalize_for_repo
(
normalized
,
repo
,
new_name
,
true
))
<
0
)
return
error
;
/* Check if we have to update HEAD. */
if
((
error
=
git_branch_is_head
(
ref
))
<
0
)
return
error
;
should_head_be_updated
=
(
error
>
0
);
if
((
error
=
git_refdb_rename
(
out
,
ref
->
db
,
ref
->
name
,
normalized
,
force
,
signature
,
message
))
<
0
)
return
error
;
/* Update HEAD if it was pointing to the reference being renamed */
if
(
should_head_be_updated
)
{
error
=
git_repository_set_head
(
ref
->
db
->
repo
,
normalized
);
}
else
{
rename_cb_data
payload
;
payload
.
old_name
=
ref
->
name
;
memcpy
(
&
payload
.
new_name
,
&
normalized
,
sizeof
(
normalized
));
error
=
git_repository_foreach_head
(
repo
,
update_wt_heads
,
0
,
&
payload
);
}
return
error
;
}
int
git_reference_rename
(
git_reference
**
out
,
git_reference
*
ref
,
...
...
@@ -690,17 +605,28 @@ int git_reference_rename(
int
force
,
const
char
*
log_message
)
{
git_signature
*
who
;
refs_update_head_payload
payload
;
git_signature
*
signature
;
git_repository
*
repo
;
int
error
;
assert
(
out
&&
ref
);
if
((
error
=
git_reference__log_signature
(
&
who
,
ref
->
db
->
repo
))
<
0
)
return
error
;
repo
=
git_reference_owner
(
ref
);
error
=
reference__rename
(
out
,
ref
,
new_name
,
force
,
who
,
log_message
);
git_signature_free
(
who
);
if
((
error
=
git_reference__log_signature
(
&
signature
,
repo
))
<
0
||
(
error
=
reference_normalize_for_repo
(
payload
.
new_name
,
repo
,
new_name
,
true
))
<
0
||
(
error
=
git_refdb_rename
(
out
,
ref
->
db
,
ref
->
name
,
payload
.
new_name
,
force
,
signature
,
log_message
))
<
0
)
goto
out
;
payload
.
old_name
=
ref
->
name
;
/* We may have to update any HEAD that was pointing to the renamed reference. */
if
((
error
=
git_repository_foreach_worktree
(
repo
,
refs_update_head
,
&
payload
))
<
0
)
goto
out
;
out:
git_signature_free
(
signature
);
return
error
;
}
...
...
src/refs.h
View file @
c5d41d46
...
...
@@ -116,24 +116,6 @@ int git_reference_lookup_resolved(
const
char
*
name
,
int
max_deref
);
/**
* Read reference from a file.
*
* This function will read in the file at `path`. If it is a
* symref, it will return a new unresolved symbolic reference
* with the given name pointing to the reference pointed to by
* the file. If it is not a symbolic reference, it will return
* the resolved reference.
*
* Note that because the refdb is not involved for symbolic references, they
* won't be owned, hence you should either not make the returned reference
* 'externally visible', or perform the lookup before returning it to the user.
*/
int
git_reference__read_head
(
git_reference
**
out
,
git_repository
*
repo
,
const
char
*
path
);
int
git_reference__log_signature
(
git_signature
**
out
,
git_repository
*
repo
);
/** Update a reference after a commit. */
...
...
src/repository.c
View file @
c5d41d46
...
...
@@ -2177,12 +2177,6 @@ int git_repository_head_detached(git_repository *repo)
return
exists
;
}
static
int
get_worktree_file_path
(
git_buf
*
out
,
git_repository
*
repo
,
const
char
*
worktree
,
const
char
*
file
)
{
git_buf_clear
(
out
);
return
git_buf_printf
(
out
,
"%s/worktrees/%s/%s"
,
repo
->
commondir
,
worktree
,
file
);
}
int
git_repository_head_detached_for_worktree
(
git_repository
*
repo
,
const
char
*
name
)
{
git_reference
*
ref
=
NULL
;
...
...
@@ -2223,7 +2217,8 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
int
git_repository_head_for_worktree
(
git_reference
**
out
,
git_repository
*
repo
,
const
char
*
name
)
{
git_buf
path
=
GIT_BUF_INIT
;
git_repository
*
worktree_repo
=
NULL
;
git_worktree
*
worktree
=
NULL
;
git_reference
*
head
=
NULL
;
int
error
;
...
...
@@ -2231,65 +2226,68 @@ int git_repository_head_for_worktree(git_reference **out, git_repository *repo,
*
out
=
NULL
;
if
((
error
=
get_worktree_file_path
(
&
path
,
repo
,
name
,
GIT_HEAD_FILE
))
<
0
||
(
error
=
git_reference__read_head
(
&
head
,
repo
,
path
.
ptr
))
<
0
)
if
((
error
=
git_worktree_lookup
(
&
worktree
,
repo
,
name
))
<
0
||
(
error
=
git_repository_open_from_worktree
(
&
worktree_repo
,
worktree
))
<
0
||
(
error
=
git_reference_lookup
(
&
head
,
worktree_repo
,
GIT_HEAD_FILE
))
<
0
)
goto
out
;
if
(
git_reference_type
(
head
)
!=
GIT_REFERENCE_DIRECT
)
{
git_reference
*
resolved
;
error
=
git_reference_lookup_resolved
(
&
resolved
,
repo
,
git_reference_symbolic_target
(
head
),
-
1
);
git_reference_free
(
head
)
;
head
=
resolved
;
if
((
error
=
git_reference_lookup_resolved
(
out
,
worktree_repo
,
git_reference_symbolic_target
(
head
),
-
1
))
<
0
)
goto
out
;
}
else
{
*
out
=
head
;
head
=
NULL
;
}
*
out
=
head
;
out:
if
(
error
)
git_reference_free
(
head
);
git_buf_dispose
(
&
path
);
git_reference_free
(
head
);
git_worktree_free
(
worktree
);
git_repository_free
(
worktree_repo
);
return
error
;
}
int
git_repository_foreach_
head
(
git_repository
*
repo
,
git_repository_foreach_head
_cb
cb
,
int
flags
,
void
*
payload
)
int
git_repository_foreach_
worktree
(
git_repository
*
repo
,
git_repository_foreach_worktree
_cb
cb
,
void
*
payload
)
{
git_strarray
worktrees
=
GIT_VECTOR_INIT
;
git_buf
path
=
GIT_BUF_INIT
;
int
error
=
0
;
git_strarray
worktrees
=
{
0
};
git_repository
*
worktree_repo
=
NULL
;
git_worktree
*
worktree
=
NULL
;
int
error
;
size_t
i
;
if
((
error
=
git_repository_open
(
&
worktree_repo
,
repo
->
commondir
))
<
0
||
(
error
=
cb
(
worktree_repo
,
payload
)
!=
0
))
goto
out
;
if
(
!
(
flags
&
GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO
))
{
/* Gather HEAD of main repository */
if
((
error
=
git_buf_joinpath
(
&
path
,
repo
->
commondir
,
GIT_HEAD_FILE
))
<
0
||
(
error
=
cb
(
repo
,
path
.
ptr
,
payload
)
!=
0
))
goto
out
;
}
git_repository_free
(
worktree_repo
);
worktree_repo
=
NULL
;
if
(
!
(
flags
&
GIT_REPOSITORY_FOREACH_HEAD_SKIP_WORKTREES
))
{
if
((
error
=
git_worktree_list
(
&
worktrees
,
repo
))
<
0
)
{
error
=
0
;
goto
out
;
}
if
((
error
=
git_worktree_list
(
&
worktrees
,
repo
))
<
0
)
goto
out
;
/* Gather HEADs of all worktrees */
for
(
i
=
0
;
i
<
worktrees
.
count
;
i
++
)
{
if
(
get_worktree_file_path
(
&
path
,
repo
,
worktrees
.
strings
[
i
],
GIT_HEAD_FILE
)
<
0
)
continue
;
for
(
i
=
0
;
i
<
worktrees
.
count
;
i
++
)
{
git_repository_free
(
worktree_repo
);
worktree_repo
=
NULL
;
git_worktree_free
(
worktree
);
worktree
=
NULL
;
if
((
error
=
cb
(
repo
,
path
.
ptr
,
payload
))
!=
0
)
if
((
error
=
git_worktree_lookup
(
&
worktree
,
repo
,
worktrees
.
strings
[
i
])
<
0
)
||
(
error
=
git_repository_open_from_worktree
(
&
worktree_repo
,
worktree
))
<
0
)
{
if
(
error
!=
GIT_ENOTFOUND
)
goto
out
;
error
=
0
;
continue
;
}
if
((
error
=
cb
(
worktree_repo
,
payload
))
!=
0
)
goto
out
;
}
out:
git_buf_dispose
(
&
path
);
git_strarray_dispose
(
&
worktrees
);
git_repository_free
(
worktree_repo
);
git_worktree_free
(
worktree
);
return
error
;
}
...
...
src/repository.h
View file @
c5d41d46
...
...
@@ -166,34 +166,11 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
int
git_repository_head_tree
(
git_tree
**
tree
,
git_repository
*
repo
);
int
git_repository_create_head
(
const
char
*
git_dir
,
const
char
*
ref_name
);
/*
* Called for each HEAD.
*
* Can return either 0, causing the iteration over HEADs to
* continue, or a non-0 value causing the iteration to abort. The
* return value is passed back to the caller of
* `git_repository_foreach_head`
*/
typedef
int
(
*
git_repository_foreach_head_cb
)(
git_repository
*
repo
,
const
char
*
path
,
void
*
payload
);
typedef
int
(
*
git_repository_foreach_worktree_cb
)(
git_repository
*
,
void
*
);
enum
{
/* Skip enumeration of the main repository HEAD */
GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO
=
(
1u
<<
0
),
/* Skip enumeration of worktree HEADs */
GIT_REPOSITORY_FOREACH_HEAD_SKIP_WORKTREES
=
(
1u
<<
1
),
};
/*
* Iterate over repository and all worktree HEADs.
*
* This function will be called for the repository HEAD and for
* all HEADS of linked worktrees. For each HEAD, the callback is
* executed with the given payload. The return value equals the
* return value of the last executed callback function.
*/
int
git_repository_foreach_head
(
git_repository
*
repo
,
git_repository_foreach_head_cb
cb
,
int
flags
,
void
*
payload
);
int
git_repository_foreach_worktree
(
git_repository
*
repo
,
git_repository_foreach_worktree_cb
cb
,
void
*
payload
);
/*
* Weak pointers to repository internals.
...
...
tests/worktree/refs.c
View file @
c5d41d46
...
...
@@ -163,7 +163,10 @@ void test_worktree_refs__renaming_reference_updates_worktree_heads(void)
cl_git_pass
(
git_branch_lookup
(
&
branch
,
fixture
.
repo
,
"testrepo-worktree"
,
GIT_BRANCH_LOCAL
));
cl_git_pass
(
git_reference_rename
(
&
renamed
,
branch
,
"refs/heads/renamed"
,
0
,
NULL
));
cl_git_pass
(
git_repository_head
(
&
head
,
fixture
.
worktree
));
cl_git_pass
(
git_reference_lookup
(
&
head
,
fixture
.
worktree
,
GIT_HEAD_FILE
));
cl_assert_equal_i
(
git_reference_type
(
head
),
GIT_REFERENCE_SYMBOLIC
);
cl_assert_equal_s
(
git_reference_symbolic_target
(
head
),
"refs/heads/renamed"
);
git_reference_free
(
head
);
git_reference_free
(
branch
);
...
...
tests/worktree/repository.c
View file @
c5d41d46
...
...
@@ -50,9 +50,12 @@ void test_worktree_repository__head_detached(void)
cl_assert
(
git_repository_head_detached
(
fixture
.
worktree
));
cl_assert
(
git_repository_head_detached_for_worktree
(
fixture
.
repo
,
"testrepo-worktree"
));
cl_git_fail
(
git_repository_head_for_worktree
(
&
head
,
fixture
.
repo
,
"testrepo-worktree"
));
cl_git_pass
(
git_repository_head_for_worktree
(
&
head
,
fixture
.
repo
,
"testrepo-worktree"
));
cl_assert_equal_oid
(
&
ref
->
target
.
oid
,
&
head
->
target
.
oid
);
git_reference_free
(
ref
);
git_reference_free
(
head
);
}
void
test_worktree_repository__head_detached_fails_for_invalid_worktree
(
void
)
...
...
tests/worktree/worktree.c
View file @
c5d41d46
...
...
@@ -581,45 +581,32 @@ void test_worktree_worktree__prune_worktree(void)
git_worktree_free
(
wt
);
}
static
int
read_head_ref
(
git_repository
*
repo
,
const
char
*
path
,
void
*
payload
)
{
git_vector
*
refs
=
(
git_vector
*
)
payload
;
git_reference
*
head
;
GIT_UNUSED
(
repo
);
cl_git_pass
(
git_reference__read_head
(
&
head
,
repo
,
path
));
static
int
foreach_worktree_cb
(
git_repository
*
worktree
,
void
*
payload
)
{
int
*
counter
=
(
int
*
)
payload
;
switch
(
*
counter
)
{
case
0
:
cl_assert_equal_s
(
git_repository_path
(
fixture
.
repo
),
git_repository_path
(
worktree
));
cl_assert
(
!
git_repository_is_worktree
(
worktree
));
break
;
case
1
:
cl_assert_equal_s
(
git_repository_path
(
fixture
.
worktree
),
git_repository_path
(
worktree
));
cl_assert
(
git_repository_is_worktree
(
worktree
));
break
;
default:
cl_fail
(
"more worktrees found than expected"
);
}
git_vector_insert
(
refs
,
head
)
;
(
*
counter
)
++
;
return
0
;
}
void
test_worktree_worktree__foreach_
head_gives_same_results_in_wt_and_repo
(
void
)
void
test_worktree_worktree__foreach_
worktree_lists_all_worktrees
(
void
)
{
git_vector
repo_refs
=
GIT_VECTOR_INIT
,
worktree_refs
=
GIT_VECTOR_INIT
;
git_reference
*
heads
[
2
];
size_t
i
;
cl_git_pass
(
git_reference_lookup
(
&
heads
[
0
],
fixture
.
repo
,
GIT_HEAD_FILE
));
cl_git_pass
(
git_reference_lookup
(
&
heads
[
1
],
fixture
.
worktree
,
GIT_HEAD_FILE
));
cl_git_pass
(
git_repository_foreach_head
(
fixture
.
repo
,
read_head_ref
,
0
,
&
repo_refs
));
cl_git_pass
(
git_repository_foreach_head
(
fixture
.
worktree
,
read_head_ref
,
0
,
&
worktree_refs
));
cl_assert_equal_i
(
repo_refs
.
length
,
ARRAY_SIZE
(
heads
));
cl_assert_equal_i
(
worktree_refs
.
length
,
ARRAY_SIZE
(
heads
));
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
heads
);
i
++
)
{
cl_assert_equal_s
(
heads
[
i
]
->
name
,
((
git_reference
*
)
repo_refs
.
contents
[
i
])
->
name
);
cl_assert_equal_s
(
heads
[
i
]
->
name
,
((
git_reference
*
)
repo_refs
.
contents
[
i
])
->
name
);
cl_assert_equal_s
(
heads
[
i
]
->
name
,
((
git_reference
*
)
worktree_refs
.
contents
[
i
])
->
name
);
git_reference_free
(
heads
[
i
]);
git_reference_free
(
repo_refs
.
contents
[
i
]);
git_reference_free
(
worktree_refs
.
contents
[
i
]);
}
git_vector_free
(
&
repo_refs
);
git_vector_free
(
&
worktree_refs
);
int
counter
=
0
;
cl_git_pass
(
git_repository_foreach_worktree
(
fixture
.
repo
,
foreach_worktree_cb
,
&
counter
));
}
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