Skip to content

Commit e2fa50f

Browse files
checkout: add --fetch to fetch remote before resolving start-point
Add a --fetch option to git checkout and git switch, plus a checkout.autoFetch config to enable it by default. When set and the start-point argument names a configured remote (either bare, like "origin", or prefixed, like "origin/foo"), fetch that remote before resolving the ref. Aborts the checkout if the fetch fails. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
1 parent 94f0577 commit e2fa50f

3 files changed

Lines changed: 98 additions & 2 deletions

File tree

builtin/checkout.c

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
#include "repo-settings.h"
3131
#include "resolve-undo.h"
3232
#include "revision.h"
33+
#include "run-command.h"
3334
#include "setup.h"
35+
#include "strvec.h"
3436
#include "submodule.h"
3537
#include "symlinks.h"
3638
#include "trace2.h"
@@ -61,6 +63,7 @@ struct checkout_opts {
6163
int count_checkout_paths;
6264
int overlay_mode;
6365
int dwim_new_local_branch;
66+
int auto_fetch;
6467
int discard_changes;
6568
int accept_ref;
6669
int accept_pathspec;
@@ -112,6 +115,34 @@ struct branch_info {
112115
char *checkout;
113116
};
114117

118+
static void fetch_remote_for_start_point(const char *arg)
119+
{
120+
const char *slash;
121+
char *remote_name;
122+
struct remote *remote;
123+
struct child_process cmd = CHILD_PROCESS_INIT;
124+
125+
if (!arg || !*arg)
126+
return;
127+
128+
slash = strchr(arg, '/');
129+
if (slash == arg)
130+
return;
131+
remote_name = slash ? xstrndup(arg, slash - arg) : xstrdup(arg);
132+
133+
remote = remote_get(remote_name);
134+
if (!remote || !remote_is_configured(remote, 1)) {
135+
free(remote_name);
136+
return;
137+
}
138+
139+
strvec_pushl(&cmd.args, "fetch", remote_name, NULL);
140+
cmd.git_cmd = 1;
141+
free(remote_name);
142+
if (run_command(&cmd))
143+
die(_("failed to fetch start-point '%s'"), arg);
144+
}
145+
115146
static void branch_info_release(struct branch_info *info)
116147
{
117148
free(info->name);
@@ -1237,6 +1268,10 @@ static int git_checkout_config(const char *var, const char *value,
12371268
opts->dwim_new_local_branch = git_config_bool(var, value);
12381269
return 0;
12391270
}
1271+
if (!strcmp(var, "checkout.autofetch")) {
1272+
opts->auto_fetch = git_config_bool(var, value);
1273+
return 0;
1274+
}
12401275

12411276
if (starts_with(var, "submodule."))
12421277
return git_default_submodule_config(var, value, NULL);
@@ -1942,8 +1977,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
19421977
opts->dwim_new_local_branch &&
19431978
opts->track == BRANCH_TRACK_UNSPECIFIED &&
19441979
!opts->new_branch;
1945-
int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
1946-
&new_branch_info, opts, &rev);
1980+
int n;
1981+
1982+
if (opts->auto_fetch)
1983+
fetch_remote_for_start_point(argv[0]);
1984+
1985+
n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
1986+
&new_branch_info, opts, &rev);
19471987
argv += n;
19481988
argc -= n;
19491989
} else if (!opts->accept_ref && opts->from_treeish) {
@@ -2052,6 +2092,8 @@ int cmd_checkout(int argc,
20522092
OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
20532093
OPT_BOOL(0, "auto-advance", &opts.auto_advance,
20542094
N_("auto advance to the next file when selecting hunks interactively")),
2095+
OPT_BOOL(0, "fetch", &opts.auto_fetch,
2096+
N_("fetch from the remote first if <start-point> is a remote-tracking ref")),
20552097
OPT_END()
20562098
};
20572099

@@ -2102,6 +2144,8 @@ int cmd_switch(int argc,
21022144
N_("second guess 'git switch <no-such-branch>'")),
21032145
OPT_BOOL(0, "discard-changes", &opts.discard_changes,
21042146
N_("throw away local modifications")),
2147+
OPT_BOOL(0, "fetch", &opts.auto_fetch,
2148+
N_("fetch from the remote first if <start-point> is a remote-tracking ref")),
21052149
OPT_END()
21062150
};
21072151

t/t7201-co.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,4 +801,55 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
801801
test_cmp_config "" --default "" branch.main2.merge
802802
'
803803

804+
test_expect_success 'setup upstream for --fetch tests' '
805+
git checkout main &&
806+
git init fetch_upstream &&
807+
test_commit -C fetch_upstream u_main &&
808+
git remote add fetch_upstream fetch_upstream &&
809+
git fetch fetch_upstream &&
810+
git -C fetch_upstream checkout -b fetch_new &&
811+
test_commit -C fetch_upstream u_new
812+
'
813+
814+
test_expect_success 'checkout --fetch -b picks up branch created upstream after clone' '
815+
git checkout main &&
816+
test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new &&
817+
git checkout --fetch -b local_new fetch_upstream/fetch_new &&
818+
test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD
819+
'
820+
821+
test_expect_success 'checkout --fetch with bare remote name fetches the remote' '
822+
git checkout main &&
823+
git -C fetch_upstream checkout -b fetch_new2 &&
824+
test_commit -C fetch_upstream u_new2 &&
825+
test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2 &&
826+
git checkout --fetch -b local_from_remote fetch_upstream &&
827+
git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2
828+
'
829+
830+
test_expect_success 'checkout --fetch aborts and does not create branch on fetch failure' '
831+
git checkout main &&
832+
test_might_fail git branch -D bogus &&
833+
test_must_fail git checkout --fetch -b bogus fetch_upstream/does_not_exist &&
834+
test_must_fail git rev-parse --verify refs/heads/bogus
835+
'
836+
837+
test_expect_success 'checkout.autoFetch=true enables fetching without --fetch' '
838+
git checkout main &&
839+
git -C fetch_upstream checkout -b fetch_cfg &&
840+
test_commit -C fetch_upstream u_cfg &&
841+
test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_cfg &&
842+
git -c checkout.autoFetch=true checkout -b local_cfg fetch_upstream/fetch_cfg &&
843+
test_cmp_rev refs/remotes/fetch_upstream/fetch_cfg HEAD
844+
'
845+
846+
test_expect_success 'switch --fetch -c picks up branch created upstream after clone' '
847+
git checkout main &&
848+
git -C fetch_upstream checkout -b fetch_switch &&
849+
test_commit -C fetch_upstream u_switch &&
850+
test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_switch &&
851+
git switch --fetch -c local_switch fetch_upstream/fetch_switch &&
852+
test_cmp_rev refs/remotes/fetch_upstream/fetch_switch HEAD
853+
'
854+
804855
test_done

t/t9902-completion.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,6 +2602,7 @@ test_expect_success 'double dash "git checkout"' '
26022602
--ignore-other-worktrees Z
26032603
--recurse-submodules Z
26042604
--auto-advance Z
2605+
--fetch Z
26052606
--progress Z
26062607
--guess Z
26072608
--no-guess Z

0 commit comments

Comments
 (0)