Skip to content

Commit 68e65de

Browse files
committed
Merge branch 'jk/check-connected-with-alternates'
The tips of refs from the alternate object store can be used as starting point for reachability computation now. * jk/check-connected-with-alternates: check_everything_connected: assume alternate ref tips are valid object-store.h: move for_each_alternate_ref() from transport.h
2 parents 1eb0a12 + 39b44ba commit 68e65de

File tree

10 files changed

+224
-100
lines changed

10 files changed

+224
-100
lines changed

Documentation/rev-list-options.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ explicitly.
182182
Pretend as if all objects mentioned by reflogs are listed on the
183183
command line as `<commit>`.
184184

185+
--alternate-refs::
186+
Pretend as if all objects mentioned as ref tips of alternate
187+
repositories were listed on the command line. An alternate
188+
repository is any repository whose object directory is specified
189+
in `objects/info/alternates`. The set of included objects may
190+
be modified by `core.alternateRefsCommand`, etc. See
191+
linkgit:git-config[1].
192+
185193
--single-worktree::
186194
By default, all working trees will be examined by the
187195
following options when there are more than one (see

builtin/receive-pack.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "object.h"
1313
#include "remote.h"
1414
#include "connect.h"
15-
#include "transport.h"
1615
#include "string-list.h"
1716
#include "sha1-array.h"
1817
#include "connected.h"

connected.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
8080
argv_array_push(&rev_list.args, "--all");
8181
}
8282
argv_array_push(&rev_list.args, "--quiet");
83+
argv_array_push(&rev_list.args, "--alternate-refs");
8384
if (opt->progress)
8485
argv_array_pushf(&rev_list.args, "--progress=%s",
8586
_("Checking connectivity"));

object-store.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ void prepare_alt_odb(struct repository *r);
3333
char *compute_alternate_path(const char *path, struct strbuf *err);
3434
typedef int alt_odb_fn(struct object_directory *, void *);
3535
int foreach_alt_odb(alt_odb_fn, void*);
36+
typedef void alternate_ref_fn(const struct object_id *oid, void *);
37+
void for_each_alternate_ref(alternate_ref_fn, void *);
3638

3739
/*
3840
* Add the directory to the on-disk alternates file; the new entry will also

revision.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,32 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
15541554
free_worktrees(worktrees);
15551555
}
15561556

1557+
struct add_alternate_refs_data {
1558+
struct rev_info *revs;
1559+
unsigned int flags;
1560+
};
1561+
1562+
static void add_one_alternate_ref(const struct object_id *oid,
1563+
void *vdata)
1564+
{
1565+
const char *name = ".alternate";
1566+
struct add_alternate_refs_data *data = vdata;
1567+
struct object *obj;
1568+
1569+
obj = get_reference(data->revs, name, oid, data->flags);
1570+
add_rev_cmdline(data->revs, obj, name, REV_CMD_REV, data->flags);
1571+
add_pending_object(data->revs, obj, name);
1572+
}
1573+
1574+
static void add_alternate_refs_to_pending(struct rev_info *revs,
1575+
unsigned int flags)
1576+
{
1577+
struct add_alternate_refs_data data;
1578+
data.revs = revs;
1579+
data.flags = flags;
1580+
for_each_alternate_ref(add_one_alternate_ref, &data);
1581+
}
1582+
15571583
static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
15581584
int exclude_parent)
15591585
{
@@ -1956,6 +1982,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
19561982
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
19571983
!strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
19581984
!strcmp(arg, "--indexed-objects") ||
1985+
!strcmp(arg, "--alternate-refs") ||
19591986
starts_with(arg, "--exclude=") ||
19601987
starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
19611988
starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
@@ -2442,6 +2469,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
24422469
add_reflogs_to_pending(revs, *flags);
24432470
} else if (!strcmp(arg, "--indexed-objects")) {
24442471
add_index_objects_to_pending(revs, *flags);
2472+
} else if (!strcmp(arg, "--alternate-refs")) {
2473+
add_alternate_refs_to_pending(revs, *flags);
24452474
} else if (!strcmp(arg, "--not")) {
24462475
*flags ^= UNINTERESTING | BOTTOM;
24472476
} else if (!strcmp(arg, "--no-walk")) {

sha1-file.c

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,103 @@ char *compute_alternate_path(const char *path, struct strbuf *err)
743743
return ref_git;
744744
}
745745

746+
static void fill_alternate_refs_command(struct child_process *cmd,
747+
const char *repo_path)
748+
{
749+
const char *value;
750+
751+
if (!git_config_get_value("core.alternateRefsCommand", &value)) {
752+
cmd->use_shell = 1;
753+
754+
argv_array_push(&cmd->args, value);
755+
argv_array_push(&cmd->args, repo_path);
756+
} else {
757+
cmd->git_cmd = 1;
758+
759+
argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
760+
argv_array_push(&cmd->args, "for-each-ref");
761+
argv_array_push(&cmd->args, "--format=%(objectname)");
762+
763+
if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
764+
argv_array_push(&cmd->args, "--");
765+
argv_array_split(&cmd->args, value);
766+
}
767+
}
768+
769+
cmd->env = local_repo_env;
770+
cmd->out = -1;
771+
}
772+
773+
static void read_alternate_refs(const char *path,
774+
alternate_ref_fn *cb,
775+
void *data)
776+
{
777+
struct child_process cmd = CHILD_PROCESS_INIT;
778+
struct strbuf line = STRBUF_INIT;
779+
FILE *fh;
780+
781+
fill_alternate_refs_command(&cmd, path);
782+
783+
if (start_command(&cmd))
784+
return;
785+
786+
fh = xfdopen(cmd.out, "r");
787+
while (strbuf_getline_lf(&line, fh) != EOF) {
788+
struct object_id oid;
789+
const char *p;
790+
791+
if (parse_oid_hex(line.buf, &oid, &p) || *p) {
792+
warning(_("invalid line while parsing alternate refs: %s"),
793+
line.buf);
794+
break;
795+
}
796+
797+
cb(&oid, data);
798+
}
799+
800+
fclose(fh);
801+
finish_command(&cmd);
802+
}
803+
804+
struct alternate_refs_data {
805+
alternate_ref_fn *fn;
806+
void *data;
807+
};
808+
809+
static int refs_from_alternate_cb(struct object_directory *e,
810+
void *data)
811+
{
812+
struct strbuf path = STRBUF_INIT;
813+
size_t base_len;
814+
struct alternate_refs_data *cb = data;
815+
816+
if (!strbuf_realpath(&path, e->path, 0))
817+
goto out;
818+
if (!strbuf_strip_suffix(&path, "/objects"))
819+
goto out;
820+
base_len = path.len;
821+
822+
/* Is this a git repository with refs? */
823+
strbuf_addstr(&path, "/refs");
824+
if (!is_directory(path.buf))
825+
goto out;
826+
strbuf_setlen(&path, base_len);
827+
828+
read_alternate_refs(path.buf, cb->fn, cb->data);
829+
830+
out:
831+
strbuf_release(&path);
832+
return 0;
833+
}
834+
835+
void for_each_alternate_ref(alternate_ref_fn fn, void *data)
836+
{
837+
struct alternate_refs_data cb;
838+
cb.fn = fn;
839+
cb.data = data;
840+
foreach_alt_odb(refs_from_alternate_cb, &cb);
841+
}
842+
746843
int foreach_alt_odb(alt_odb_fn fn, void *cb)
747844
{
748845
struct object_directory *ent;

t/perf/p5600-clone-reference.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
3+
test_description='speed of clone --reference'
4+
. ./perf-lib.sh
5+
6+
test_perf_default_repo
7+
8+
test_expect_success 'create shareable repository' '
9+
git clone --bare . shared.git
10+
'
11+
12+
test_expect_success 'advance base repository' '
13+
# Do not use test_commit here; its test_tick will
14+
# use some ancient hard-coded date. The resulting clock
15+
# skew will cause pack-objects to traverse in a very
16+
# sub-optimal order, skewing the results.
17+
echo content >new-file-that-does-not-exist &&
18+
git add new-file-that-does-not-exist &&
19+
git commit -m "new commit"
20+
'
21+
22+
test_perf 'clone --reference' '
23+
rm -rf dst.git &&
24+
git clone --no-local --bare --reference shared.git . dst.git
25+
'
26+
27+
test_done

t/t5618-alternate-refs.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/sh
2+
3+
test_description='test handling of --alternate-refs traversal'
4+
. ./test-lib.sh
5+
6+
# Avoid test_commit because we want a specific and known set of refs:
7+
#
8+
# base -- one
9+
# \ \
10+
# two -- merged
11+
#
12+
# where "one" and "two" are on separate refs, and "merged" is available only in
13+
# the dependent child repository.
14+
test_expect_success 'set up local refs' '
15+
git checkout -b one &&
16+
test_tick &&
17+
git commit --allow-empty -m base &&
18+
test_tick &&
19+
git commit --allow-empty -m one &&
20+
git checkout -b two HEAD^ &&
21+
test_tick &&
22+
git commit --allow-empty -m two
23+
'
24+
25+
# We'll enter the child repository after it's set up since that's where
26+
# all of the subsequent tests will want to run (and it's easy to forget a
27+
# "-C child" and get nonsense results).
28+
test_expect_success 'set up shared clone' '
29+
git clone -s . child &&
30+
cd child &&
31+
git merge origin/one
32+
'
33+
34+
test_expect_success 'rev-list --alternate-refs' '
35+
git rev-list --remotes=origin >expect &&
36+
git rev-list --alternate-refs >actual &&
37+
test_cmp expect actual
38+
'
39+
40+
test_expect_success 'rev-list --not --alternate-refs' '
41+
git rev-parse HEAD >expect &&
42+
git rev-list HEAD --not --alternate-refs >actual &&
43+
test_cmp expect actual
44+
'
45+
46+
test_expect_success 'limiting with alternateRefsPrefixes' '
47+
test_config core.alternateRefsPrefixes refs/heads/one &&
48+
git rev-list origin/one >expect &&
49+
git rev-list --alternate-refs >actual &&
50+
test_cmp expect actual
51+
'
52+
53+
test_expect_success 'log --source shows .alternate marker' '
54+
git log --oneline --source --remotes=origin >expect.orig &&
55+
sed "s/origin.* /.alternate /" <expect.orig >expect &&
56+
git log --oneline --source --alternate-refs >actual &&
57+
test_cmp expect actual
58+
'
59+
60+
test_done

transport.c

Lines changed: 0 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,100 +1380,3 @@ char *transport_anonymize_url(const char *url)
13801380
literal_copy:
13811381
return xstrdup(url);
13821382
}
1383-
1384-
static void fill_alternate_refs_command(struct child_process *cmd,
1385-
const char *repo_path)
1386-
{
1387-
const char *value;
1388-
1389-
if (!git_config_get_value("core.alternateRefsCommand", &value)) {
1390-
cmd->use_shell = 1;
1391-
1392-
argv_array_push(&cmd->args, value);
1393-
argv_array_push(&cmd->args, repo_path);
1394-
} else {
1395-
cmd->git_cmd = 1;
1396-
1397-
argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
1398-
argv_array_push(&cmd->args, "for-each-ref");
1399-
argv_array_push(&cmd->args, "--format=%(objectname)");
1400-
1401-
if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
1402-
argv_array_push(&cmd->args, "--");
1403-
argv_array_split(&cmd->args, value);
1404-
}
1405-
}
1406-
1407-
cmd->env = local_repo_env;
1408-
cmd->out = -1;
1409-
}
1410-
1411-
static void read_alternate_refs(const char *path,
1412-
alternate_ref_fn *cb,
1413-
void *data)
1414-
{
1415-
struct child_process cmd = CHILD_PROCESS_INIT;
1416-
struct strbuf line = STRBUF_INIT;
1417-
FILE *fh;
1418-
1419-
fill_alternate_refs_command(&cmd, path);
1420-
1421-
if (start_command(&cmd))
1422-
return;
1423-
1424-
fh = xfdopen(cmd.out, "r");
1425-
while (strbuf_getline_lf(&line, fh) != EOF) {
1426-
struct object_id oid;
1427-
const char *p;
1428-
1429-
if (parse_oid_hex(line.buf, &oid, &p) || *p) {
1430-
warning(_("invalid line while parsing alternate refs: %s"),
1431-
line.buf);
1432-
break;
1433-
}
1434-
1435-
cb(&oid, data);
1436-
}
1437-
1438-
fclose(fh);
1439-
finish_command(&cmd);
1440-
}
1441-
1442-
struct alternate_refs_data {
1443-
alternate_ref_fn *fn;
1444-
void *data;
1445-
};
1446-
1447-
static int refs_from_alternate_cb(struct object_directory *e,
1448-
void *data)
1449-
{
1450-
struct strbuf path = STRBUF_INIT;
1451-
size_t base_len;
1452-
struct alternate_refs_data *cb = data;
1453-
1454-
if (!strbuf_realpath(&path, e->path, 0))
1455-
goto out;
1456-
if (!strbuf_strip_suffix(&path, "/objects"))
1457-
goto out;
1458-
base_len = path.len;
1459-
1460-
/* Is this a git repository with refs? */
1461-
strbuf_addstr(&path, "/refs");
1462-
if (!is_directory(path.buf))
1463-
goto out;
1464-
strbuf_setlen(&path, base_len);
1465-
1466-
read_alternate_refs(path.buf, cb->fn, cb->data);
1467-
1468-
out:
1469-
strbuf_release(&path);
1470-
return 0;
1471-
}
1472-
1473-
void for_each_alternate_ref(alternate_ref_fn fn, void *data)
1474-
{
1475-
struct alternate_refs_data cb;
1476-
cb.fn = fn;
1477-
cb.data = data;
1478-
foreach_alt_odb(refs_from_alternate_cb, &cb);
1479-
}

transport.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,4 @@ int transport_refs_pushed(struct ref *ref);
262262
void transport_print_push_status(const char *dest, struct ref *refs,
263263
int verbose, int porcelain, unsigned int *reject_reasons);
264264

265-
typedef void alternate_ref_fn(const struct object_id *oid, void *);
266-
void for_each_alternate_ref(alternate_ref_fn, void *);
267265
#endif

0 commit comments

Comments
 (0)