Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Documentation/RelNotes/2.53.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ Fixes since v2.52
handling stateful ISO/IEC 2022 encoded strings.
(merge cee341e9dd rs/macos-iconv-workaround later to maint).

* Running "git diff" with "--name-only" and other options that allows
us not to look at the blob contents, while objects that are lazily
fetched from a promisor remote, caused use-after-free, which has
been corrected.

* The ort merge machinery hit an assertion failure in a history with
criss-cross merges renamed a directory and a non-directory, which
has been corrected.
(merge 979ee83e8a en/ort-recursive-d-f-conflict-fix later to maint).

* Other code cleanup, docfix, build fix, etc.
(merge 46207a54cc qj/doc-http-bad-want-response later to maint).
(merge df90eccd93 kh/doc-commit-extra-references later to maint).
Expand All @@ -252,3 +262,4 @@ Fixes since v2.52
(merge 93f894c001 bc/checkout-error-message-fix later to maint).
(merge abf05d856f rs/show-branch-prio-queue later to maint).
(merge 06188ea5f3 rs/parse-config-expiry-simplify later to maint).
(merge 861dbb1586 dd/t5403-modernise later to maint).
6 changes: 3 additions & 3 deletions builtin/describe.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ static int replace_name(struct commit_name *e,

if (!e->tag) {
t = lookup_tag(the_repository, &e->oid);
if (!t || parse_tag(t))
if (!t || parse_tag(the_repository, t))
return 1;
e->tag = t;
}

t = lookup_tag(the_repository, oid);
if (!t || parse_tag(t))
if (!t || parse_tag(the_repository, t))
return 0;
*tag = t;

Expand Down Expand Up @@ -335,7 +335,7 @@ static void append_name(struct commit_name *n, struct strbuf *dst)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(the_repository, &n->oid);
if (!n->tag || parse_tag(n->tag))
if (!n->tag || parse_tag(the_repository, n->tag))
die(_("annotated tag %s not available"), n->path);
}
if (n->tag && !n->name_checked) {
Expand Down
2 changes: 1 addition & 1 deletion builtin/pack-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -3293,7 +3293,7 @@ static void add_tag_chain(const struct object_id *oid)

tag = lookup_tag(the_repository, oid);
while (1) {
if (!tag || parse_tag(tag) || !tag->tagged)
if (!tag || parse_tag(the_repository, tag) || !tag->tagged)
die(_("unable to pack objects reachable from tag %s"),
oid_to_hex(oid));

Expand Down
2 changes: 1 addition & 1 deletion builtin/tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
if (format->format)
flags = GPG_VERIFY_OMIT_STATUS;

if (gpg_verify_tag(oid, name, flags))
if (gpg_verify_tag(the_repository, oid, name, flags))
return -1;

if (format->format)
Expand Down
2 changes: 1 addition & 1 deletion builtin/verify-tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ int cmd_verify_tag(int argc,
continue;
}

if (gpg_verify_tag(&oid, name, flags)) {
if (gpg_verify_tag(repo, &oid, name, flags)) {
had_error = 1;
continue;
}
Expand Down
5 changes: 5 additions & 0 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -7098,6 +7098,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
if (!diffopt->flags.no_index)
diffopt->skip_stat_unmatch++;
diff_free_filepair(p);
q->queue[i] = NULL;
}
}
free(q->queue);
Expand Down Expand Up @@ -7141,6 +7142,10 @@ void diff_queued_diff_prefetch(void *repository)

for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];

if (!p)
continue;

diff_add_if_missing(repo, &to_fetch, p->one);
diff_add_if_missing(repo, &to_fetch, p->two);
}
Expand Down
2 changes: 1 addition & 1 deletion fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio
{
const char *name = fsck_get_object_name(options, &tag->object.oid);

if (parse_tag(tag))
if (parse_tag(the_repository, tag))
return -1;
if (name)
fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
Expand Down
35 changes: 34 additions & 1 deletion merge-ort.c
Original file line number Diff line number Diff line change
Expand Up @@ -1502,11 +1502,44 @@ static void resolve_trivial_directory_merge(struct conflict_info *ci, int side)
VERIFY_CI(ci);
assert((side == 1 && ci->match_mask == 5) ||
(side == 2 && ci->match_mask == 3));

/*
* Since ci->stages[0] matches ci->stages[3-side], resolve merge in
* favor of ci->stages[side].
*/
oidcpy(&ci->merged.result.oid, &ci->stages[side].oid);
ci->merged.result.mode = ci->stages[side].mode;
ci->merged.is_null = is_null_oid(&ci->stages[side].oid);

/*
* Because we resolved in favor of "side", we are no longer
* considering the paths which matched (i.e. had the same hash) any
* more. Strip the matching paths from both dirmask & filemask.
* Another consequence of merging in favor of side is that we can no
* longer have a directory/file conflict either..but there's a slight
* nuance we consider before clearing it.
*
* In most cases, resolving in favor of the other side means there's
* no conflict at all, but if we had a directory/file conflict to
* start, and the directory is resolved away, the remaining file could
* still be part of a rename. If the remaining file is part of a
* rename, then it may also be part of a rename conflict (e.g.
* rename/delete or rename/rename(1to2)), so we can't
* mark it as a clean merge if we started with a directory/file
* conflict and still have a file left.
*
* In contrast, if we started with a directory/file conflict and
* still have a directory left, no file under that directory can be
* part of a rename, otherwise we would have had to recurse into the
* directory and would have never ended up within
* resolve_trivial_directory_merge() for that directory.
*/
ci->dirmask &= (~ci->match_mask);
ci->filemask &= (~ci->match_mask);
assert(!ci->filemask || !ci->dirmask);
ci->match_mask = 0;
ci->merged.clean = 1; /* (ci->filemask == 0); */
ci->merged.clean = !ci->df_conflict || ci->dirmask;
ci->df_conflict = 0;
}

static int handle_deferred_entries(struct merge_options *opt,
Expand Down
2 changes: 1 addition & 1 deletion object-name.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
} else if (type == OBJ_TAG) {
struct tag *tag = lookup_tag(ds->repo, oid);

if (!parse_tag(tag) && tag->tag) {
if (!parse_tag(ds->repo, tag) && tag->tag) {
/*
* TRANSLATORS: This is a line of ambiguous
* tag object output. E.g.:
Expand Down
2 changes: 1 addition & 1 deletion ref-filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -2866,7 +2866,7 @@ static int match_points_at(struct oid_array *points_at,
while (obj && obj->type == OBJ_TAG) {
struct tag *tag = (struct tag *)obj;

if (parse_tag(tag) < 0) {
if (parse_tag(the_repository, tag) < 0) {
obj = NULL;
break;
}
Expand Down
35 changes: 35 additions & 0 deletions t/t4067-diff-partial-clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,41 @@ test_expect_success 'diff with rename detection batches blobs' '
test_line_count = 1 done_lines
'

test_expect_success 'diff succeeds even if entries are removed from queue' '
test_when_finished "rm -rf server client trace" &&

test_create_repo server &&
for l in a c e g i p
do
echo $l >server/$l &&
git -C server add $l || return 1
done &&
git -C server commit -m x &&

for l in a e i
do
git -C server rm $l || return 1
done &&

for l in b d f i
do
echo $l$l >server/$l &&
git -C server add $l || return 1
done &&
git -C server commit -a -m x &&

test_config -C server uploadpack.allowfilter 1 &&
test_config -C server uploadpack.allowanysha1inwant 1 &&
git clone --filter=blob:limit=0 "file://$(pwd)/server" client &&

for file in $(ls client)
do
cat client/$file >$file &&
mv $file client/$file || return 1
done &&
git -C client diff --name-only --relative HEAD^
'

test_expect_success 'diff does not fetch anything if inexact rename detection is not needed' '
test_when_finished "rm -rf server client trace" &&

Expand Down
2 changes: 1 addition & 1 deletion t/t5403-post-checkout-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ test_expect_success 'post-checkout hook is triggered by clone' '
echo "$@" >"$GIT_DIR/post-checkout.args"
EOF
git clone --template=templates . clone3 &&
test -f clone3/.git/post-checkout.args
test_path_is_file clone3/.git/post-checkout.args
'

test_done
86 changes: 86 additions & 0 deletions t/t6422-merge-rename-corner-cases.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1439,4 +1439,90 @@ test_expect_success 'rename/rename(1to2) with a binary file' '
)
'

# Testcase preliminary submodule/directory conflict and submodule rename
# Commit O: <empty, or additional irrelevant stuff>
# Commit A1: introduce "folder" (as a tree)
# Commit B1: introduce "folder" (as a submodule)
# Commit A2: merge B1 into A1, but keep folder as a tree
# Commit B2: merge A1 into B1, but keep folder as a submodule
# Merge A2 & B2
test_setup_submodule_directory_preliminary_conflict () {
git init submodule_directory_preliminary_conflict &&
(
cd submodule_directory_preliminary_conflict &&

# Trying to do the A2 and B2 merges above is slightly more
# challenging with a local submodule (because checking out
# another commit has the submodule in the way). Instead,
# first create the commits with the wrong parents but right
# trees, in the order A1, A2, B1, B2...
#
# Then go back and create new A2 & B2 with the correct
# parents and the same trees.

git commit --allow-empty -m orig &&

git branch A &&
git branch B &&

git checkout B &&
mkdir folder &&
echo A>folder/A &&
echo B>folder/B &&
echo C>folder/C &&
echo D>folder/D &&
echo E>folder/E &&
git add folder &&
git commit -m B1 &&

git commit --allow-empty -m B2 &&

git checkout A &&
git init folder &&
(
cd folder &&
>Z &&
>Y &&
git add Z Y &&
git commit -m "original submodule commit"
) &&
git add folder &&
git commit -m A1 &&

git commit --allow-empty -m A2 &&

NewA2=$(git commit-tree -p A^ -p B^ -m "Merge B into A" A^{tree}) &&
NewB2=$(git commit-tree -p B^ -p A^ -m "Merge A into B" B^{tree}) &&
git update-ref refs/heads/A $NewA2 &&
git update-ref refs/heads/B $NewB2
)
}

test_expect_success 'submodule/directory preliminary conflict' '
test_setup_submodule_directory_preliminary_conflict &&
(
cd submodule_directory_preliminary_conflict &&

git checkout A^0 &&

test_expect_code 1 git merge B^0 &&

# Make sure the index has the right number of entries
git ls-files -s >actual &&
test_line_count = 2 actual &&

# The "folder" as directory should have been resolved away
# as part of the merge. The "folder" as submodule got
# renamed to "folder~Temporary merge branch 2" in the
# virtual merge base, resulting in a
# "folder~Temporary merge branch 2" -> "folder"
# rename in the outermerge for the submodule, which then
# becomes part of a rename/delete conflict (because "folder"
# as a submodule was deleted in A2).
submod=$(git rev-parse A:folder) &&
printf "160000 $submod 1\tfolder\n160000 $submod 2\tfolder\n" >expect &&
test_cmp expect actual
)
'

test_done
27 changes: 14 additions & 13 deletions tag.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS

#include "git-compat-util.h"
Expand All @@ -13,6 +12,7 @@
#include "gpg-interface.h"
#include "hex.h"
#include "packfile.h"
#include "repository.h"

const char *tag_type = "tag";

Expand Down Expand Up @@ -44,28 +44,28 @@ static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
return ret;
}

int gpg_verify_tag(const struct object_id *oid, const char *name_to_report,
unsigned flags)
int gpg_verify_tag(struct repository *r, const struct object_id *oid,
const char *name_to_report, unsigned flags)
{
enum object_type type;
char *buf;
unsigned long size;
int ret;

type = odb_read_object_info(the_repository->objects, oid, NULL);
type = odb_read_object_info(r->objects, oid, NULL);
if (type != OBJ_TAG)
return error("%s: cannot verify a non-tag object of type %s.",
name_to_report ?
name_to_report :
repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV),
oid_to_hex(oid),
type_name(type));

buf = odb_read_object(the_repository->objects, oid, &type, &size);
buf = odb_read_object(r->objects, oid, &type, &size);
if (!buf)
return error("%s: unable to read file.",
name_to_report ?
name_to_report :
repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
oid_to_hex(oid));

ret = run_gpg_verify(buf, size, flags);

Expand Down Expand Up @@ -148,9 +148,11 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
FREE_AND_NULL(item->tag);
}

if (size < the_hash_algo->hexsz + 24)
if (size < r->hash_algo->hexsz + 24)
return -1;
if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n')
if (memcmp("object ", bufptr, 7) ||
parse_oid_hex_algop(bufptr + 7, &oid, &bufptr, r->hash_algo) ||
*bufptr++ != '\n')
return -1;

if (!starts_with(bufptr, "type "))
Expand Down Expand Up @@ -201,7 +203,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
return 0;
}

int parse_tag(struct tag *item)
int parse_tag(struct repository *r, struct tag *item)
{
enum object_type type;
void *data;
Expand All @@ -210,8 +212,7 @@ int parse_tag(struct tag *item)

if (item->object.parsed)
return 0;
data = odb_read_object(the_repository->objects, &item->object.oid,
&type, &size);
data = odb_read_object(r->objects, &item->object.oid, &type, &size);
if (!data)
return error("Could not read %s",
oid_to_hex(&item->object.oid));
Expand All @@ -220,7 +221,7 @@ int parse_tag(struct tag *item)
return error("Object %s not a tag",
oid_to_hex(&item->object.oid));
}
ret = parse_tag_buffer(the_repository, item, data, size);
ret = parse_tag_buffer(r, item, data, size);
free(data);
return ret;
}
Expand Down
Loading