Skip to content

Commit 853ba20

Browse files
author
Edward Thomson
authored
Merge pull request libgit2#6073 from libgit2/ethomson/attr_lookups
2 parents a6f6a9f + 091bd73 commit 853ba20

File tree

7 files changed

+123
-29
lines changed

7 files changed

+123
-29
lines changed

include/git2/repository.h

+7-5
Original file line numberDiff line numberDiff line change
@@ -762,13 +762,15 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(
762762
*
763763
* @param out Output value of calculated SHA
764764
* @param repo Repository pointer
765-
* @param path Path to file on disk whose contents should be hashed. If the
766-
* repository is not NULL, this can be a relative path.
765+
* @param path Path to file on disk whose contents should be hashed. This
766+
* may be an absolute path or a relative path, in which case it
767+
* will be treated as a path within the working directory.
767768
* @param type The object type to hash as (e.g. GIT_OBJECT_BLOB)
768769
* @param as_path The path to use to look up filtering rules. If this is
769-
* NULL, then the `path` parameter will be used instead. If
770-
* this is passed as the empty string, then no filters will be
771-
* applied when calculating the hash.
770+
* an empty string then no filters will be applied when
771+
* calculating the hash. If this is `NULL` and the `path`
772+
* parameter is a file within the repository's working
773+
* directory, then the `path` will be used.
772774
* @return 0 on success, or an error code
773775
*/
774776
GIT_EXTERN(int) git_repository_hashfile(

src/attr.c

+2
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,8 @@ static int collect_attr_files(
629629
const char *workdir = git_repository_workdir(repo);
630630
attr_walk_up_info info = { NULL };
631631

632+
GIT_ASSERT(!git_path_is_absolute(path));
633+
632634
if ((error = attr_setup(repo, attr_session, opts)) < 0)
633635
return error;
634636

src/blob.c

+4-5
Original file line numberDiff line numberDiff line change
@@ -277,21 +277,20 @@ int git_blob_create_from_disk(
277277
{
278278
int error;
279279
git_buf full_path = GIT_BUF_INIT;
280-
const char *workdir, *hintpath;
280+
const char *workdir, *hintpath = NULL;
281281

282282
if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
283283
git_buf_dispose(&full_path);
284284
return error;
285285
}
286286

287-
hintpath = git_buf_cstr(&full_path);
288287
workdir = git_repository_workdir(repo);
289288

290-
if (workdir && !git__prefixcmp(hintpath, workdir))
291-
hintpath += strlen(workdir);
289+
if (workdir && !git__prefixcmp(full_path.ptr, workdir))
290+
hintpath = full_path.ptr + strlen(workdir);
292291

293292
error = git_blob__create_from_paths(
294-
id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
293+
id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, !!hintpath);
295294

296295
git_buf_dispose(&full_path);
297296
return error;

src/checkout.c

+6-7
Original file line numberDiff line numberDiff line change
@@ -1520,8 +1520,7 @@ static int blob_content_to_file(
15201520
int fd;
15211521
int error = 0;
15221522

1523-
if (hint_path == NULL)
1524-
hint_path = path;
1523+
GIT_ASSERT(hint_path != NULL);
15251524

15261525
if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
15271526
return error;
@@ -1789,7 +1788,7 @@ static int checkout_blob(
17891788
}
17901789

17911790
error = checkout_write_content(
1792-
data, &file->id, fullpath->ptr, NULL, file->mode, &st);
1791+
data, &file->id, fullpath->ptr, file->path, file->mode, &st);
17931792

17941793
/* update the index unless prevented */
17951794
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
@@ -1975,7 +1974,7 @@ static int checkout_write_entry(
19751974
checkout_conflictdata *conflict,
19761975
const git_index_entry *side)
19771976
{
1978-
const char *hint_path = NULL, *suffix;
1977+
const char *hint_path, *suffix;
19791978
git_buf *fullpath;
19801979
struct stat st;
19811980
int error;
@@ -1998,10 +1997,10 @@ static int checkout_write_entry(
19981997

19991998
if (checkout_path_suffixed(fullpath, suffix) < 0)
20001999
return -1;
2001-
2002-
hint_path = side->path;
20032000
}
20042001

2002+
hint_path = side->path;
2003+
20052004
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
20062005
(error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
20072006
return error;
@@ -2118,7 +2117,7 @@ static int checkout_write_merge(
21182117
filter_session.temp_buf = &data->tmp;
21192118

21202119
if ((error = git_filter_list__load(
2121-
&fl, data->repo, NULL, git_buf_cstr(&path_workdir),
2120+
&fl, data->repo, NULL, result.path,
21222121
GIT_FILTER_TO_WORKTREE, &filter_session)) < 0 ||
21232122
(error = git_filter_list__convert_buf(&out_data, fl, &in_data)) < 0)
21242123
goto done;

src/repository.c

+13-11
Original file line numberDiff line numberDiff line change
@@ -2840,34 +2840,36 @@ int git_repository_hashfile(
28402840
git_file fd = -1;
28412841
uint64_t len;
28422842
git_buf full_path = GIT_BUF_INIT;
2843+
const char *workdir = git_repository_workdir(repo);
28432844

28442845
/* as_path can be NULL */
28452846
GIT_ASSERT_ARG(out);
28462847
GIT_ASSERT_ARG(path);
28472848
GIT_ASSERT_ARG(repo);
28482849

2849-
/* At some point, it would be nice if repo could be NULL to just
2850-
* apply filter rules defined in system and global files, but for
2851-
* now that is not possible because git_filters_load() needs it.
2852-
*/
2853-
2854-
if ((error = git_path_join_unrooted(
2855-
&full_path, path, git_repository_workdir(repo), NULL)) < 0 ||
2850+
if ((error = git_path_join_unrooted(&full_path, path, workdir, NULL)) < 0 ||
28562851
(error = git_path_validate_workdir_buf(repo, &full_path)) < 0)
28572852
return error;
28582853

2859-
if (!as_path)
2860-
as_path = path;
2854+
/*
2855+
* NULL as_path means that we should derive it from the
2856+
* given path.
2857+
*/
2858+
if (!as_path) {
2859+
if (workdir && !git__prefixcmp(full_path.ptr, workdir))
2860+
as_path = full_path.ptr + strlen(workdir);
2861+
else
2862+
as_path = "";
2863+
}
28612864

28622865
/* passing empty string for "as_path" indicated --no-filters */
28632866
if (strlen(as_path) > 0) {
28642867
error = git_filter_list_load(
28652868
&fl, repo, NULL, as_path,
28662869
GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
2870+
28672871
if (error < 0)
28682872
return error;
2869-
} else {
2870-
error = 0;
28712873
}
28722874

28732875
/* at this point, error is a count of the number of loaded filters */

src/win32/posix_w32.c

+3
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,8 @@ int p_getcwd(char *buffer_out, size_t size)
648648
if (!cwd)
649649
return -1;
650650

651+
git_win32_path_remove_namespace(cwd, wcslen(cwd));
652+
651653
/* Convert the working directory back to UTF-8 */
652654
if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
653655
DWORD code = GetLastError();
@@ -660,6 +662,7 @@ int p_getcwd(char *buffer_out, size_t size)
660662
return -1;
661663
}
662664

665+
git_path_mkposix(buffer_out);
663666
return 0;
664667
}
665668

tests/repo/hashfile.c

+88-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ void test_repo_hashfile__initialize(void)
1010

1111
void test_repo_hashfile__cleanup(void)
1212
{
13+
cl_fixture_cleanup("absolute");
1314
cl_git_sandbox_cleanup();
1415
_repo = NULL;
1516
}
@@ -38,10 +39,18 @@ void test_repo_hashfile__simple(void)
3839
git_buf_dispose(&full);
3940
}
4041

41-
void test_repo_hashfile__filtered(void)
42+
void test_repo_hashfile__filtered_in_workdir(void)
4243
{
44+
git_buf root = GIT_BUF_INIT, txt = GIT_BUF_INIT, bin = GIT_BUF_INIT;
45+
char cwd[GIT_PATH_MAX];
4346
git_oid a, b;
4447

48+
cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
49+
cl_must_pass(p_mkdir("absolute", 0777));
50+
cl_git_pass(git_buf_joinpath(&root, cwd, "status"));
51+
cl_git_pass(git_buf_joinpath(&txt, root.ptr, "testfile.txt"));
52+
cl_git_pass(git_buf_joinpath(&bin, root.ptr, "testfile.bin"));
53+
4554
cl_repo_set_bool(_repo, "core.autocrlf", true);
4655

4756
cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
@@ -55,21 +64,41 @@ void test_repo_hashfile__filtered(void)
5564
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, NULL));
5665
cl_assert(git_oid_cmp(&a, &b));
5766

67+
/* not equal hashes because of filtering when specified by absolute path */
68+
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
69+
cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL));
70+
cl_assert(git_oid_cmp(&a, &b));
71+
5872
/* equal hashes because filter is binary */
5973
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
6074
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, NULL));
6175
cl_assert_equal_oid(&a, &b);
6276

77+
/* equal hashes because filter is binary when specified by absolute path */
78+
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
79+
cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL));
80+
cl_assert_equal_oid(&a, &b);
81+
6382
/* equal hashes when 'as_file' points to binary filtering */
6483
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
6584
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, "foo.bin"));
6685
cl_assert_equal_oid(&a, &b);
6786

87+
/* equal hashes when 'as_file' points to binary filtering (absolute path) */
88+
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
89+
cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "foo.bin"));
90+
cl_assert_equal_oid(&a, &b);
91+
6892
/* not equal hashes when 'as_file' points to text filtering */
6993
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
7094
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, "foo.txt"));
7195
cl_assert(git_oid_cmp(&a, &b));
7296

97+
/* not equal hashes when 'as_file' points to text filtering */
98+
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
99+
cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "foo.txt"));
100+
cl_assert(git_oid_cmp(&a, &b));
101+
73102
/* equal hashes when 'as_file' is empty and turns off filtering */
74103
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
75104
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, ""));
@@ -79,7 +108,65 @@ void test_repo_hashfile__filtered(void)
79108
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, ""));
80109
cl_assert_equal_oid(&a, &b);
81110

111+
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
112+
cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, ""));
113+
cl_assert_equal_oid(&a, &b);
114+
115+
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
116+
cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, ""));
117+
cl_assert_equal_oid(&a, &b);
118+
82119
/* some hash type failures */
83120
cl_git_fail(git_odb_hashfile(&a, "status/testfile.txt", 0));
84121
cl_git_fail(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_ANY, NULL));
122+
123+
git_buf_dispose(&txt);
124+
git_buf_dispose(&bin);
125+
git_buf_dispose(&root);
126+
}
127+
128+
void test_repo_hashfile__filtered_outside_workdir(void)
129+
{
130+
git_buf root = GIT_BUF_INIT, txt = GIT_BUF_INIT, bin = GIT_BUF_INIT;
131+
char cwd[GIT_PATH_MAX];
132+
git_oid a, b;
133+
134+
cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
135+
cl_must_pass(p_mkdir("absolute", 0777));
136+
cl_git_pass(git_buf_joinpath(&root, cwd, "absolute"));
137+
cl_git_pass(git_buf_joinpath(&txt, root.ptr, "testfile.txt"));
138+
cl_git_pass(git_buf_joinpath(&bin, root.ptr, "testfile.bin"));
139+
140+
cl_repo_set_bool(_repo, "core.autocrlf", true);
141+
cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
142+
143+
/* create some sample content with CRLF in it */
144+
cl_git_mkfile("absolute/testfile.txt", "content\r\n");
145+
cl_git_mkfile("absolute/testfile.bin", "other\r\nstuff\r\n");
146+
147+
/* not equal hashes because of filtering */
148+
cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB));
149+
cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "testfile.txt"));
150+
cl_assert(git_oid_cmp(&a, &b));
151+
152+
/* equal hashes because filter is binary */
153+
cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB));
154+
cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "testfile.bin"));
155+
cl_assert_equal_oid(&a, &b);
156+
157+
/*
158+
* equal hashes because no filtering occurs for absolute paths outside the working
159+
* directory unless as_path is specified
160+
*/
161+
cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB));
162+
cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL));
163+
cl_assert_equal_oid(&a, &b);
164+
165+
cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB));
166+
cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL));
167+
cl_assert_equal_oid(&a, &b);
168+
169+
git_buf_dispose(&txt);
170+
git_buf_dispose(&bin);
171+
git_buf_dispose(&root);
85172
}

0 commit comments

Comments
 (0)