Merge branch 'jl/fetch-submodule-recursive' into next

* jl/fetch-submodule-recursive:
  Submodules: Add the new "fetch" config option for fetch and pull
  fetch/pull: Recursively fetch populated submodules
This commit is contained in:
Junio C Hamano
2010-09-10 10:25:59 -07:00
9 changed files with 210 additions and 5 deletions

View File

@@ -1787,6 +1787,12 @@ submodule.<name>.update::
URL and other values found in the `.gitmodules` file. See
linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
submodule.<name>.fetch::
A boolean to enable/disable recursive fetching of this submodule. It can
be overriden by using the --[no-]recursive command line option to "git
fetch" and "git pull". This setting overrides any setting made in
.gitmodules for this submodule.
submodule.<name>.ignore::
Defines under what circumstances "git status" and the diff family show
a submodule as modified. When set to "all", it will never be considered

View File

@@ -64,6 +64,12 @@ endif::git-pull[]
specified with the remote.<name>.tagopt setting. See
linkgit:git-config[1].
--[no-]recursive::
By default new commits of all populated submodules will be fetched
too. This option can be used to disable/enable recursive fetching of
submodules regardless of the 'fetch' configuration setting (see
linkgit:git-config[1] or linkgit:gitmodules[5]).
-u::
--update-head-ok::
By default 'git fetch' refuses to update the head which

View File

@@ -44,6 +44,14 @@ submodule.<name>.update::
This config option is overridden if 'git submodule update' is given
the '--merge' or '--rebase' options.
submodule.<name>.fetch::
A boolean to enable/disable recursive fetching of this submodule.
If this option is also present in the submodules entry in .git/config of
the superproject, the setting there will override the one found in
.gitmodules.
Both settings can be overriden on the command line by using the
"--[no-]recursive" option to "git fetch" and "git pull"..
submodule.<name>.ignore::
Defines under what circumstances "git status" and the diff family show
a submodule as modified. When set to "all", it will never be considered

View File

@@ -12,6 +12,7 @@
#include "parse-options.h"
#include "sigchain.h"
#include "transport.h"
#include "submodule.h"
static const char * const builtin_fetch_usage[] = {
"git fetch [<options>] [<repository> [<refspec>...]]",
@@ -27,7 +28,13 @@ enum {
TAGS_SET = 2
};
static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
enum {
RECURSIVE_UNSET = 0,
RECURSIVE_DEFAULT = 1,
RECURSIVE_SET = 2
};
static int all, append, dry_run, force, keep, multiple, prune, recursive = RECURSIVE_DEFAULT, update_head_ok, verbosity;
static int progress;
static int tags = TAGS_DEFAULT;
static const char *depth;
@@ -53,6 +60,8 @@ static struct option builtin_fetch_options[] = {
"do not fetch all tags (--no-tags)", TAGS_UNSET),
OPT_BOOLEAN('p', "prune", &prune,
"prune tracking branches no longer on remote"),
OPT_SET_INT(0, "recursive", &recursive,
"control recursive fetching of submodules", RECURSIVE_SET),
OPT_BOOLEAN(0, "dry-run", &dry_run,
"dry run"),
OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
@@ -926,6 +935,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
}
if (!result && recursive) {
gitmodules_config();
git_config(submodule_config, NULL);
result = fetch_populated_submodules(recursive == RECURSIVE_SET);
}
/* All names were strdup()ed or strndup()ed */
list.strdup_strings = 1;
string_list_clear(&list, 0);

View File

@@ -38,7 +38,7 @@ test -z "$(git ls-files -u)" || die_conflict
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
log_arg= verbosity= progress=
log_arg= verbosity= progress= recursive=
merge_args=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short="${curr_branch#refs/heads/}"
@@ -105,6 +105,12 @@ do
--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
rebase=false
;;
--recursive)
recursive=--recursive
;;
--no-recursive)
recursive=--no-recursive
;;
--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
dry_run=--dry-run
;;
@@ -220,7 +226,7 @@ test true = "$rebase" && {
done
}
orig_head=$(git rev-parse -q --verify HEAD)
git fetch $verbosity $progress $dry_run --update-head-ok "$@" || exit 1
git fetch $verbosity $progress $dry_run $recursive --update-head-ok "$@" || exit 1
test -z "$dry_run" || exit 0
curr_head=$(git rev-parse -q --verify HEAD)

View File

@@ -10,6 +10,7 @@
#include "string-list.h"
struct string_list config_name_for_path;
struct string_list config_fetch_for_name;
struct string_list config_ignore_for_name;
static int add_submodule_odb(const char *path)
@@ -63,7 +64,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
}
}
static int submodule_config(const char *var, const char *value, void *cb)
int submodule_config(const char *var, const char *value, void *cb)
{
if (!prefixcmp(var, "submodule."))
return parse_submodule_config_option(var, value);
@@ -100,6 +101,14 @@ int parse_submodule_config_option(const char *var, const char *value)
config = string_list_append(&config_name_for_path, xstrdup(value));
config->util = strbuf_detach(&submodname, NULL);
strbuf_release(&submodname);
} else if ((len > 5) && !strcmp(var + len - 6, ".fetch")) {
strbuf_add(&submodname, var, len - 6);
config = unsorted_string_list_lookup(&config_fetch_for_name, submodname.buf);
if (!config)
config = string_list_append(&config_fetch_for_name,
strbuf_detach(&submodname, NULL));
config->util = git_config_bool(var, value) ? (void *)1 : NULL;
strbuf_release(&submodname);
} else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
strcmp(value, "all") && strcmp(value, "none")) {
@@ -229,6 +238,55 @@ void show_submodule_summary(FILE *f, const char *path,
strbuf_release(&sb);
}
int fetch_populated_submodules(int forced)
{
int result = 0;
struct child_process cp;
const char *argv[] = {
"fetch",
NULL,
};
struct string_list_item *name_for_path;
const char *work_tree = get_git_work_tree();
if (!work_tree)
return 0;
memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.out = -1;
for_each_string_list_item(name_for_path, &config_name_for_path) {
struct strbuf submodule_path = STRBUF_INIT;
struct strbuf submodule_git_dir = STRBUF_INIT;
const char *git_dir;
if (!forced) {
struct string_list_item *fetch_option;
fetch_option = unsorted_string_list_lookup(&config_fetch_for_name, name_for_path->util);
if (fetch_option && !fetch_option->util)
continue;
}
strbuf_addf(&submodule_path, "%s/%s", work_tree, name_for_path->string);
strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
git_dir = read_gitfile_gently(submodule_git_dir.buf);
if (!git_dir)
git_dir = submodule_git_dir.buf;
if (is_directory(git_dir)) {
printf("Fetching submodule %s\n", name_for_path->string);
cp.dir = submodule_path.buf;
if (run_command(&cp))
result = 1;
}
strbuf_release(&submodule_path);
strbuf_release(&submodule_git_dir);
}
return result;
}
unsigned is_submodule_modified(const char *path, int ignore_untracked)
{
ssize_t len;

View File

@@ -5,6 +5,7 @@ struct diff_options;
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
const char *path);
int submodule_config(const char *var, const char *value, void *cb);
void gitmodules_config();
int parse_submodule_config_option(const char *var, const char *value);
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
@@ -12,6 +13,7 @@ void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
unsigned dirty_submodule,
const char *del, const char *add, const char *reset);
int fetch_populated_submodules(int forced);
unsigned is_submodule_modified(const char *path, int ignore_untracked);
int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
const unsigned char a[20], const unsigned char b[20]);

104
t/t5526-fetch-submodules.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/bin/sh
# Copyright (c) 2010, Jens Lehmann
test_description='Recursive "git fetch" for submodules'
. ./test-lib.sh
pwd=$(pwd)
add_upstream_commit() {
(
cd submodule &&
head1=$(git rev-parse --short HEAD) &&
echo new >> subfile &&
test_tick &&
git add subfile &&
git commit -m new subfile &&
head2=$(git rev-parse --short HEAD) &&
echo "From $pwd/submodule" > ../expect.err
echo " $head1..$head2 master -> origin/master" >> ../expect.err
)
}
test_expect_success setup '
mkdir submodule &&
(
cd submodule &&
git init &&
echo subcontent > subfile &&
git add subfile &&
git commit -m new subfile
) &&
git submodule add "$pwd/submodule" submodule &&
git commit -am initial &&
git clone . downstream &&
(
cd downstream &&
git submodule init &&
git submodule update
) &&
echo "Fetching submodule submodule" > expect.out
'
test_expect_success "fetch recurses into submodules" '
add_upstream_commit &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
test_cmp expect.out actual.out &&
test_cmp expect.err actual.err
'
test_expect_success "fetch --no-recursive only fetches superproject" '
(
cd downstream &&
git fetch --no-recursive >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
! test -s actual.err
'
test_expect_success "using fetch=false in .gitmodules only fetches superproject" '
(
cd downstream &&
git config -f .gitmodules submodule.submodule.fetch false &&
git fetch >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
! test -s actual.err
'
test_expect_success "--recursive overrides .gitmodules config" '
add_upstream_commit &&
(
cd downstream &&
git fetch --recursive >../actual.out 2>../actual.err
) &&
test_cmp expect.out actual.out &&
test_cmp expect.err actual.err
'
test_expect_success "using fetch=true in .git/config overrides setting in .gitmodules" '
add_upstream_commit &&
(
cd downstream &&
git config submodule.submodule.fetch true &&
git fetch >../actual.out 2>../actual.err
) &&
test_cmp expect.out actual.out &&
test_cmp expect.err actual.err
'
test_expect_success "--no-recursive overrides fetch setting from .git/config" '
add_upstream_commit &&
(
cd downstream &&
git fetch --no-recursive >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
! test -s actual.err
'
test_done

View File

@@ -50,7 +50,7 @@ test_expect_success 'change submodule url' '
test_expect_success '"git submodule sync" should update submodule URLs' '
(cd super-clone &&
git pull &&
git pull --no-recursive &&
git submodule sync
) &&
test -d "$(git config -f super-clone/submodule/.git/config \