Skip to content

Commit 9cfbaf5

Browse files
committed
Merge 'kw/patch-ids-optim'
Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 7707328 + bb5e532 commit 9cfbaf5

File tree

8 files changed

+128
-87
lines changed

8 files changed

+128
-87
lines changed

builtin/log.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,7 @@ static void prepare_bases(struct base_tree_info *bases,
13151315
struct object_id *patch_id;
13161316
if (commit->util)
13171317
continue;
1318-
if (commit_patch_id(commit, &diffopt, sha1))
1318+
if (commit_patch_id(commit, &diffopt, sha1, 0))
13191319
die(_("cannot get patch id"));
13201320
ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
13211321
patch_id = bases->patch_id + bases->nr_patch_id;

diff.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4449,7 +4449,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
44494449
}
44504450

44514451
/* returns 0 upon success, and writes result into sha1 */
4452-
static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
4452+
static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
44534453
{
44544454
struct diff_queue_struct *q = &diff_queued_diff;
44554455
int i;
@@ -4484,9 +4484,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
44844484

44854485
diff_fill_sha1_info(p->one);
44864486
diff_fill_sha1_info(p->two);
4487-
if (fill_mmfile(&mf1, p->one) < 0 ||
4488-
fill_mmfile(&mf2, p->two) < 0)
4489-
return error("unable to read files to diff");
44904487

44914488
len1 = remove_space(p->one->path, strlen(p->one->path));
44924489
len2 = remove_space(p->two->path, strlen(p->two->path));
@@ -4521,6 +4518,13 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
45214518
len2, p->two->path);
45224519
git_SHA1_Update(&ctx, buffer, len1);
45234520

4521+
if (diff_header_only)
4522+
continue;
4523+
4524+
if (fill_mmfile(&mf1, p->one) < 0 ||
4525+
fill_mmfile(&mf2, p->two) < 0)
4526+
return error("unable to read files to diff");
4527+
45244528
if (diff_filespec_is_binary(p->one) ||
45254529
diff_filespec_is_binary(p->two)) {
45264530
git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
@@ -4541,11 +4545,11 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
45414545
return 0;
45424546
}
45434547

4544-
int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
4548+
int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
45454549
{
45464550
struct diff_queue_struct *q = &diff_queued_diff;
45474551
int i;
4548-
int result = diff_get_patch_id(options, sha1);
4552+
int result = diff_get_patch_id(options, sha1, diff_header_only);
45494553

45504554
for (i = 0; i < q->nr; i++)
45514555
diff_free_filepair(q->queue[i]);

diff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ extern int run_diff_files(struct rev_info *revs, unsigned int option);
342342
extern int run_diff_index(struct rev_info *revs, int cached);
343343

344344
extern int do_diff_cache(const unsigned char *, struct diff_options *);
345-
extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
345+
extern int diff_flush_patch_id(struct diff_options *, unsigned char *, int);
346346

347347
extern int diff_result_code(struct diff_options *, int);
348348

patch-ids.c

Lines changed: 51 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,101 +5,94 @@
55
#include "patch-ids.h"
66

77
int commit_patch_id(struct commit *commit, struct diff_options *options,
8-
unsigned char *sha1)
8+
unsigned char *sha1, int diff_header_only)
99
{
1010
if (commit->parents)
1111
diff_tree_sha1(commit->parents->item->object.oid.hash,
1212
commit->object.oid.hash, "", options);
1313
else
1414
diff_root_tree_sha1(commit->object.oid.hash, "", options);
1515
diffcore_std(options);
16-
return diff_flush_patch_id(options, sha1);
16+
return diff_flush_patch_id(options, sha1, diff_header_only);
1717
}
1818

19-
static const unsigned char *patch_id_access(size_t index, void *table)
19+
/*
20+
* When we cannot load the full patch-id for both commits for whatever
21+
* reason, the function returns -1 (i.e. return error(...)). Despite
22+
* the "cmp" in the name of this function, the caller only cares about
23+
* the return value being zero (a and b are equivalent) or non-zero (a
24+
* and b are different), and returning non-zero would keep both in the
25+
* result, even if they actually were equivalent, in order to err on
26+
* the side of safety. The actual value being negative does not have
27+
* any significance; only that it is non-zero matters.
28+
*/
29+
static int patch_id_cmp(struct patch_id *a,
30+
struct patch_id *b,
31+
struct diff_options *opt)
2032
{
21-
struct patch_id **id_table = table;
22-
return id_table[index]->patch_id;
33+
if (is_null_sha1(a->patch_id) &&
34+
commit_patch_id(a->commit, opt, a->patch_id, 0))
35+
return error("Could not get patch ID for %s",
36+
oid_to_hex(&a->commit->object.oid));
37+
if (is_null_sha1(b->patch_id) &&
38+
commit_patch_id(b->commit, opt, b->patch_id, 0))
39+
return error("Could not get patch ID for %s",
40+
oid_to_hex(&b->commit->object.oid));
41+
return hashcmp(a->patch_id, b->patch_id);
2342
}
2443

25-
static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
26-
{
27-
return sha1_pos(id, table, nr, patch_id_access);
28-
}
29-
30-
#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
31-
struct patch_id_bucket {
32-
struct patch_id_bucket *next;
33-
int nr;
34-
struct patch_id bucket[BUCKET_SIZE];
35-
};
36-
3744
int init_patch_ids(struct patch_ids *ids)
3845
{
3946
memset(ids, 0, sizeof(*ids));
4047
diff_setup(&ids->diffopts);
4148
DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
4249
diff_setup_done(&ids->diffopts);
50+
hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp, 256);
4351
return 0;
4452
}
4553

4654
int free_patch_ids(struct patch_ids *ids)
4755
{
48-
struct patch_id_bucket *next, *patches;
49-
50-
free(ids->table);
51-
for (patches = ids->patches; patches; patches = next) {
52-
next = patches->next;
53-
free(patches);
54-
}
56+
hashmap_free(&ids->patches, 1);
5557
return 0;
5658
}
5759

58-
static struct patch_id *add_commit(struct commit *commit,
59-
struct patch_ids *ids,
60-
int no_add)
60+
static int init_patch_id_entry(struct patch_id *patch,
61+
struct commit *commit,
62+
struct patch_ids *ids)
6163
{
62-
struct patch_id_bucket *bucket;
63-
struct patch_id *ent;
64-
unsigned char sha1[20];
65-
int pos;
64+
unsigned char header_only_patch_id[GIT_SHA1_RAWSZ];
6665

67-
if (commit_patch_id(commit, &ids->diffopts, sha1))
68-
return NULL;
69-
pos = patch_pos(ids->table, ids->nr, sha1);
70-
if (0 <= pos)
71-
return ids->table[pos];
72-
if (no_add)
73-
return NULL;
66+
patch->commit = commit;
67+
if (commit_patch_id(commit, &ids->diffopts, header_only_patch_id, 1))
68+
return -1;
7469

75-
pos = -1 - pos;
76-
77-
bucket = ids->patches;
78-
if (!bucket || (BUCKET_SIZE <= bucket->nr)) {
79-
bucket = xcalloc(1, sizeof(*bucket));
80-
bucket->next = ids->patches;
81-
ids->patches = bucket;
82-
}
83-
ent = &bucket->bucket[bucket->nr++];
84-
hashcpy(ent->patch_id, sha1);
85-
86-
ALLOC_GROW(ids->table, ids->nr + 1, ids->alloc);
87-
if (pos < ids->nr)
88-
memmove(ids->table + pos + 1, ids->table + pos,
89-
sizeof(ent) * (ids->nr - pos));
90-
ids->nr++;
91-
ids->table[pos] = ent;
92-
return ids->table[pos];
70+
hashmap_entry_init(patch, sha1hash(header_only_patch_id));
71+
return 0;
9372
}
9473

9574
struct patch_id *has_commit_patch_id(struct commit *commit,
9675
struct patch_ids *ids)
9776
{
98-
return add_commit(commit, ids, 1);
77+
struct patch_id patch;
78+
79+
memset(&patch, 0, sizeof(patch));
80+
if (init_patch_id_entry(&patch, commit, ids))
81+
return NULL;
82+
83+
return hashmap_get(&ids->patches, &patch, &ids->diffopts);
9984
}
10085

10186
struct patch_id *add_commit_patch_id(struct commit *commit,
10287
struct patch_ids *ids)
10388
{
104-
return add_commit(commit, ids, 0);
89+
struct patch_id *key = xcalloc(1, sizeof(*key));
90+
91+
if (init_patch_id_entry(key, commit, ids)) {
92+
free(key);
93+
return NULL;
94+
}
95+
96+
hashmap_add(&ids->patches, key);
97+
return key;
10598
}

patch-ids.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@
22
#define PATCH_IDS_H
33

44
struct patch_id {
5-
unsigned char patch_id[20];
6-
char seen;
5+
struct hashmap_entry ent;
6+
unsigned char patch_id[GIT_SHA1_RAWSZ];
7+
struct commit *commit;
78
};
89

910
struct patch_ids {
11+
struct hashmap patches;
1012
struct diff_options diffopts;
11-
int nr, alloc;
12-
struct patch_id **table;
13-
struct patch_id_bucket *patches;
1413
};
1514

1615
int commit_patch_id(struct commit *commit, struct diff_options *options,
17-
unsigned char *sha1);
16+
unsigned char *sha1, int);
1817
int init_patch_ids(struct patch_ids *);
1918
int free_patch_ids(struct patch_ids *);
2019
struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);

revision.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
846846
*/
847847
if (left_first != !!(flags & SYMMETRIC_LEFT))
848848
continue;
849-
commit->util = add_commit_patch_id(commit, &ids);
849+
add_commit_patch_id(commit, &ids);
850850
}
851851

852852
/* either cherry_mark or cherry_pick are true */
@@ -873,21 +873,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
873873
id = has_commit_patch_id(commit, &ids);
874874
if (!id)
875875
continue;
876-
id->seen = 1;
877-
commit->object.flags |= cherry_flag;
878-
}
879876

880-
/* Now check the original side for seen ones */
881-
for (p = list; p; p = p->next) {
882-
struct commit *commit = p->item;
883-
struct patch_id *ent;
884-
885-
ent = commit->util;
886-
if (!ent)
887-
continue;
888-
if (ent->seen)
889-
commit->object.flags |= cherry_flag;
890-
commit->util = NULL;
877+
commit->object.flags |= cherry_flag;
878+
id->commit->object.flags |= cherry_flag;
891879
}
892880

893881
free_patch_ids(&ids);

t/perf/p3400-rebase.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/sh
2+
3+
test_description='Tests rebase performance'
4+
. ./perf-lib.sh
5+
6+
test_perf_default_repo
7+
8+
test_expect_success 'setup' '
9+
git checkout -f -b base &&
10+
git checkout -b to-rebase &&
11+
git checkout -b upstream &&
12+
for i in $(seq 100)
13+
do
14+
# simulate huge diffs
15+
echo change$i >unrelated-file$i &&
16+
seq 1000 >>unrelated-file$i &&
17+
git add unrelated-file$i &&
18+
test_tick &&
19+
git commit -m commit$i unrelated-file$i &&
20+
echo change$i >unrelated-file$i &&
21+
seq 1000 | tac >>unrelated-file$i &&
22+
git add unrelated-file$i &&
23+
test_tick &&
24+
git commit -m commit$i-reverse unrelated-file$i ||
25+
break
26+
done &&
27+
git checkout to-rebase &&
28+
test_commit our-patch interesting-file
29+
'
30+
31+
test_perf 'rebase on top of a lot of unrelated changes' '
32+
git rebase --onto upstream HEAD^ &&
33+
git rebase --onto base HEAD^
34+
'
35+
36+
test_done

t/t6007-rev-list-cherry-pick-file.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,25 @@ test_expect_success '--count --left-right' '
207207
test_cmp expect actual
208208
'
209209

210+
# Corrupt the object store deliberately to make sure
211+
# the object is not even checked for its existence.
212+
remove_loose_object () {
213+
sha1="$(git rev-parse "$1")" &&
214+
remainder=${sha1#??} &&
215+
firsttwo=${sha1%$remainder} &&
216+
rm .git/objects/$firsttwo/$remainder
217+
}
218+
219+
test_expect_success '--cherry-pick avoids looking at full diffs' '
220+
git checkout -b shy-diff &&
221+
test_commit dont-look-at-me &&
222+
echo Hello >dont-look-at-me.t &&
223+
test_tick &&
224+
git commit -m tip dont-look-at-me.t &&
225+
git checkout -b mainline HEAD^ &&
226+
test_commit to-cherry-pick &&
227+
remove_loose_object shy-diff^:dont-look-at-me.t &&
228+
git rev-list --cherry-pick ...shy-diff
229+
'
230+
210231
test_done

0 commit comments

Comments
 (0)