merge(3p/git): Merge git upstream at v2.26.2

This commit is contained in:
Vincent Ambo 2020-05-22 17:46:45 +01:00
commit 5229c9b232
1006 changed files with 149006 additions and 60819 deletions

View file

@ -20,6 +20,7 @@
#include "bulk-checkin.h"
#include "argv-array.h"
#include "submodule.h"
#include "add-interactive.h"
static const char * const builtin_add_usage[] = {
N_("git add [<options>] [--] <pathspec>..."),
@ -28,6 +29,9 @@ static const char * const builtin_add_usage[] = {
static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
static int add_renormalize;
static int pathspec_file_nul;
static const char *pathspec_from_file;
static int legacy_stash_p; /* support for the scripted `git stash` */
struct update_callback_data {
int flags;
@ -185,6 +189,34 @@ int run_add_interactive(const char *revision, const char *patch_mode,
{
int status, i;
struct argv_array argv = ARGV_ARRAY_INIT;
int use_builtin_add_i =
git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
if (use_builtin_add_i < 0)
git_config_get_bool("add.interactive.usebuiltin",
&use_builtin_add_i);
if (use_builtin_add_i == 1) {
enum add_p_mode mode;
if (!patch_mode)
return !!run_add_i(the_repository, pathspec);
if (!strcmp(patch_mode, "--patch"))
mode = ADD_P_ADD;
else if (!strcmp(patch_mode, "--patch=stash"))
mode = ADD_P_STASH;
else if (!strcmp(patch_mode, "--patch=reset"))
mode = ADD_P_RESET;
else if (!strcmp(patch_mode, "--patch=checkout"))
mode = ADD_P_CHECKOUT;
else if (!strcmp(patch_mode, "--patch=worktree"))
mode = ADD_P_WORKTREE;
else
die("'%s' not supported", patch_mode);
return !!run_add_p(the_repository, mode, revision, pathspec);
}
argv_array_push(&argv, "add--interactive");
if (patch_mode)
@ -309,6 +341,10 @@ static struct option builtin_add_options[] = {
N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
N_("warn when adding an embedded repository")),
OPT_HIDDEN_BOOL(0, "legacy-stash-p", &legacy_stash_p,
N_("backend for `git stash -p`")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
};
@ -319,6 +355,7 @@ static int add_config(const char *var, const char *value, void *cb)
ignore_add_errors = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value, cb);
}
@ -369,7 +406,10 @@ static int add_files(struct dir_struct *dir, int flags)
fprintf(stderr, _(ignore_error));
for (i = 0; i < dir->ignored_nr; i++)
fprintf(stderr, "%s\n", dir->ignored[i]->name);
fprintf(stderr, _("Use -f if you really want to add them.\n"));
if (advice_add_ignored_file)
advise(_("Use -f if you really want to add them.\n"
"Turn this message off by running\n"
"\"git config advice.addIgnoredFile false\""));
exit_status = 1;
}
@ -402,11 +442,28 @@ int cmd_add(int argc, const char **argv, const char *prefix)
builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
if (patch_interactive)
add_interactive = 1;
if (add_interactive)
if (add_interactive) {
if (pathspec_from_file)
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
}
if (legacy_stash_p) {
struct pathspec pathspec;
if (edit_interactive)
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH |
PATHSPEC_PREFIX_ORIGIN,
prefix, argv);
return run_add_interactive(NULL, "--patch=stash", &pathspec);
}
if (edit_interactive) {
if (pathspec_from_file)
die(_("--pathspec-from-file is incompatible with --edit"));
return(edit_patch(argc, argv, prefix));
}
argc--;
argv++;
@ -418,10 +475,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (addremove && take_worktree_changes)
die(_("-A and -u are mutually incompatible"));
if (!take_worktree_changes && addremove_explicit < 0 && argc)
/* Turn "git add pathspec..." to "git add -A pathspec..." */
addremove = 1;
if (!show_only && ignore_missing)
die(_("Option --ignore-missing can only be used together with --dry-run"));
@ -434,19 +487,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
(show_only ? ADD_CACHE_PRETEND : 0) |
(intent_to_add ? ADD_CACHE_INTENT : 0) |
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
(!(addremove || take_worktree_changes)
? ADD_CACHE_IGNORE_REMOVAL : 0));
if (require_pathspec && argc == 0) {
fprintf(stderr, _("Nothing specified, nothing added.\n"));
fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
return 0;
}
/*
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
@ -456,6 +496,38 @@ int cmd_add(int argc, const char **argv, const char *prefix)
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
if (pathspec_from_file) {
if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
if (require_pathspec && pathspec.nr == 0) {
fprintf(stderr, _("Nothing specified, nothing added.\n"));
if (advice_add_empty_pathspec)
advise( _("Maybe you wanted to say 'git add .'?\n"
"Turn this message off by running\n"
"\"git config advice.addEmptyPathspec false\""));
return 0;
}
if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
/* Turn "git add pathspec..." to "git add -A pathspec..." */
addremove = 1;
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
(show_only ? ADD_CACHE_PRETEND : 0) |
(intent_to_add ? ADD_CACHE_INTENT : 0) |
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
(!(addremove || take_worktree_changes)
? ADD_CACHE_IGNORE_REMOVAL : 0));
if (read_cache_preload(&pathspec) < 0)
die(_("index file corrupt"));

View file

@ -24,7 +24,6 @@
#include "sequencer.h"
#include "revision.h"
#include "merge-recursive.h"
#include "revision.h"
#include "log-tree.h"
#include "notes-utils.h"
#include "rerere.h"
@ -82,6 +81,11 @@ enum signoff_type {
SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
};
enum show_patch_type {
SHOW_PATCH_RAW = 0,
SHOW_PATCH_DIFF = 1,
};
struct am_state {
/* state directory path */
char *dir;
@ -1071,19 +1075,6 @@ static const char *msgnum(const struct am_state *state)
return sb.buf;
}
/**
* Refresh and write index.
*/
static void refresh_and_write_cache(void)
{
struct lock_file lock_file = LOCK_INIT;
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write index file"));
}
/**
* Dies with a user-friendly message on how to proceed after resolving the
* problem. This message can be overridden with state->resolvemsg.
@ -1272,7 +1263,9 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
ident_line = find_commit_header(buffer, "author", &ident_len);
if (!ident_line)
die(_("missing author line in commit %s"),
oid_to_hex(&commit->object.oid));
if (split_ident_line(&id, ident_line, ident_len) < 0)
die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);
@ -1538,7 +1531,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
o.branch1 = "HEAD";
their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
o.branch2 = their_tree_name;
o.detect_directory_renames = 0;
o.detect_directory_renames = MERGE_DIRECTORY_RENAMES_NONE;
if (state->quiet)
o.verbosity = 0;
@ -1703,7 +1696,8 @@ static void am_run(struct am_state *state, int resume)
unlink(am_path(state, "dirtyindex"));
refresh_and_write_cache();
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0)
die(_("unable to write index file"));
if (repo_index_has_changes(the_repository, NULL, &sb)) {
write_state_bool(state, "dirtyindex", 1);
@ -1774,7 +1768,7 @@ static void am_run(struct am_state *state, int resume)
linelen(state->msg), state->msg);
if (advice_amworkdir)
advise(_("Use 'git am --show-current-patch' to see the failed patch"));
advise(_("Use 'git am --show-current-patch=diff' to see the failed patch"));
die_user_resolve(state);
}
@ -2072,7 +2066,7 @@ static void am_abort(struct am_state *state)
am_destroy(state);
}
static int show_patch(struct am_state *state)
static int show_patch(struct am_state *state, enum show_patch_type sub_mode)
{
struct strbuf sb = STRBUF_INIT;
const char *patch_path;
@ -2089,7 +2083,17 @@ static int show_patch(struct am_state *state)
return ret;
}
patch_path = am_path(state, msgnum(state));
switch (sub_mode) {
case SHOW_PATCH_RAW:
patch_path = am_path(state, msgnum(state));
break;
case SHOW_PATCH_DIFF:
patch_path = am_path(state, "patch");
break;
default:
BUG("invalid mode for --show-current-patch");
}
len = strbuf_read_file(&sb, patch_path, 0);
if (len < 0)
die_errno(_("failed to read '%s'"), patch_path);
@ -2129,7 +2133,7 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
return 0;
}
enum resume_mode {
enum resume_type {
RESUME_FALSE = 0,
RESUME_APPLY,
RESUME_RESOLVED,
@ -2139,6 +2143,45 @@ enum resume_mode {
RESUME_SHOW_PATCH
};
struct resume_mode {
enum resume_type mode;
enum show_patch_type sub_mode;
};
static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset)
{
int *opt_value = opt->value;
struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode);
/*
* Please update $__git_showcurrentpatch in git-completion.bash
* when you add new options
*/
const char *valid_modes[] = {
[SHOW_PATCH_DIFF] = "diff",
[SHOW_PATCH_RAW] = "raw"
};
int new_value = SHOW_PATCH_RAW;
if (arg) {
for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) {
if (!strcmp(arg, valid_modes[new_value]))
break;
}
if (new_value >= ARRAY_SIZE(valid_modes))
return error(_("Invalid value for --show-current-patch: %s"), arg);
}
if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
return error(_("--show-current-patch=%s is incompatible with "
"--show-current-patch=%s"),
arg, valid_modes[resume->sub_mode]);
resume->mode = RESUME_SHOW_PATCH;
resume->sub_mode = new_value;
return 0;
}
static int git_am_config(const char *k, const char *v, void *cb)
{
int status;
@ -2156,7 +2199,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
int binary = -1;
int keep_cr = -1;
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
struct resume_mode resume = { .mode = RESUME_FALSE };
int in_progress;
int ret = 0;
@ -2225,24 +2268,26 @@ int cmd_am(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG),
OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
N_("override error message when patch failure occurs")),
OPT_CMDMODE(0, "continue", &resume,
OPT_CMDMODE(0, "continue", &resume.mode,
N_("continue applying patches after resolving a conflict"),
RESUME_RESOLVED),
OPT_CMDMODE('r', "resolved", &resume,
OPT_CMDMODE('r', "resolved", &resume.mode,
N_("synonyms for --continue"),
RESUME_RESOLVED),
OPT_CMDMODE(0, "skip", &resume,
OPT_CMDMODE(0, "skip", &resume.mode,
N_("skip the current patch"),
RESUME_SKIP),
OPT_CMDMODE(0, "abort", &resume,
OPT_CMDMODE(0, "abort", &resume.mode,
N_("restore the original branch and abort the patching operation."),
RESUME_ABORT),
OPT_CMDMODE(0, "quit", &resume,
OPT_CMDMODE(0, "quit", &resume.mode,
N_("abort the patching operation but keep HEAD where it is."),
RESUME_QUIT),
OPT_CMDMODE(0, "show-current-patch", &resume,
N_("show the patch being applied."),
RESUME_SHOW_PATCH),
{ OPTION_CALLBACK, 0, "show-current-patch", &resume.mode,
"(diff|raw)",
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH },
OPT_BOOL(0, "committer-date-is-author-date",
&state.committer_date_is_author_date,
N_("lie about committer date")),
@ -2292,12 +2337,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
* intend to feed us a patch but wanted to continue
* unattended.
*/
if (argc || (resume == RESUME_FALSE && !isatty(0)))
if (argc || (resume.mode == RESUME_FALSE && !isatty(0)))
die(_("previous rebase directory %s still exists but mbox given."),
state.dir);
if (resume == RESUME_FALSE)
resume = RESUME_APPLY;
if (resume.mode == RESUME_FALSE)
resume.mode = RESUME_APPLY;
if (state.signoff == SIGNOFF_EXPLICIT)
am_append_signoff(&state);
@ -2311,7 +2356,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
* stray directories.
*/
if (file_exists(state.dir) && !state.rebasing) {
if (resume == RESUME_ABORT || resume == RESUME_QUIT) {
if (resume.mode == RESUME_ABORT || resume.mode == RESUME_QUIT) {
am_destroy(&state);
am_state_release(&state);
return 0;
@ -2322,7 +2367,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
state.dir);
}
if (resume)
if (resume.mode)
die(_("Resolve operation not in progress, we are not resuming."));
for (i = 0; i < argc; i++) {
@ -2340,7 +2385,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
argv_array_clear(&paths);
}
switch (resume) {
switch (resume.mode) {
case RESUME_FALSE:
am_run(&state, 0);
break;
@ -2361,7 +2406,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
am_destroy(&state);
break;
case RESUME_SHOW_PATCH:
ret = show_patch(&state);
ret = show_patch(&state, resume.sub_mode);
break;
default:
BUG("invalid resume value");

View file

@ -52,8 +52,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
terms->term_bad = xstrdup(bad);
}
static const char *vocab_bad = "bad|new";
static const char *vocab_good = "good|old";
static const char vocab_bad[] = "bad|new";
static const char vocab_good[] = "good|old";
/*
* Check whether the string `term` belongs to the set of strings
@ -169,11 +169,12 @@ static int bisect_reset(const char *commit)
argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
error(_("could not check out original"
" HEAD '%s'. Try 'git bisect"
" reset <commit>'."), branch.buf);
strbuf_release(&branch);
argv_array_clear(&argv);
return error(_("could not check out original"
" HEAD '%s'. Try 'git bisect"
" reset <commit>'."), branch.buf);
return -1;
}
argv_array_clear(&argv);
}
@ -205,31 +206,31 @@ static int bisect_write(const char *state, const char *rev,
struct object_id oid;
struct commit *commit;
FILE *fp = NULL;
int retval = 0;
int res = 0;
if (!strcmp(state, terms->term_bad)) {
strbuf_addf(&tag, "refs/bisect/%s", state);
} else if (one_of(state, terms->term_good, "skip", NULL)) {
strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev);
} else {
retval = error(_("Bad bisect_write argument: %s"), state);
res = error(_("Bad bisect_write argument: %s"), state);
goto finish;
}
if (get_oid(rev, &oid)) {
retval = error(_("couldn't get the oid of the rev '%s'"), rev);
res = error(_("couldn't get the oid of the rev '%s'"), rev);
goto finish;
}
if (update_ref(NULL, tag.buf, &oid, NULL, 0,
UPDATE_REFS_MSG_ON_ERR)) {
retval = -1;
res = -1;
goto finish;
}
fp = fopen(git_path_bisect_log(), "a");
if (!fp) {
retval = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
res = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
goto finish;
}
@ -243,7 +244,7 @@ finish:
if (fp)
fclose(fp);
strbuf_release(&tag);
return retval;
return res;
}
static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
@ -281,35 +282,23 @@ static int mark_good(const char *refname, const struct object_id *oid,
return 1;
}
static const char *need_bad_and_good_revision_warning =
static const char need_bad_and_good_revision_warning[] =
N_("You need to give me at least one %s and %s revision.\n"
"You can use \"git bisect %s\" and \"git bisect %s\" for that.");
static const char *need_bisect_start_warning =
static const char need_bisect_start_warning[] =
N_("You need to start by \"git bisect start\".\n"
"You then need to give me at least one %s and %s revision.\n"
"You can use \"git bisect %s\" and \"git bisect %s\" for that.");
static int bisect_next_check(const struct bisect_terms *terms,
const char *current_term)
static int decide_next(const struct bisect_terms *terms,
const char *current_term, int missing_good,
int missing_bad)
{
int missing_good = 1, missing_bad = 1, retval = 0;
const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
const char *good_glob = xstrfmt("%s-*", terms->term_good);
if (ref_exists(bad_ref))
missing_bad = 0;
for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
(void *) &missing_good);
if (!missing_good && !missing_bad)
goto finish;
if (!current_term) {
retval = -1;
goto finish;
}
return 0;
if (!current_term)
return -1;
if (missing_good && !missing_bad &&
!strcmp(current_term, terms->term_good)) {
@ -320,7 +309,7 @@ static int bisect_next_check(const struct bisect_terms *terms,
*/
warning(_("bisecting only with a %s commit"), terms->term_bad);
if (!isatty(0))
goto finish;
return 0;
/*
* TRANSLATORS: Make sure to include [Y] and [n] in your
* translation. The program will only accept English input
@ -328,21 +317,35 @@ static int bisect_next_check(const struct bisect_terms *terms,
*/
yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO);
if (starts_with(yesno, "N") || starts_with(yesno, "n"))
retval = -1;
goto finish;
}
if (!is_empty_or_missing_file(git_path_bisect_start())) {
retval = error(_(need_bad_and_good_revision_warning),
vocab_bad, vocab_good, vocab_bad, vocab_good);
} else {
retval = error(_(need_bisect_start_warning),
vocab_good, vocab_bad, vocab_good, vocab_bad);
return -1;
return 0;
}
finish:
free((void *) good_glob);
free((void *) bad_ref);
return retval;
if (!is_empty_or_missing_file(git_path_bisect_start()))
return error(_(need_bad_and_good_revision_warning),
vocab_bad, vocab_good, vocab_bad, vocab_good);
else
return error(_(need_bisect_start_warning),
vocab_good, vocab_bad, vocab_good, vocab_bad);
}
static int bisect_next_check(const struct bisect_terms *terms,
const char *current_term)
{
int missing_good = 1, missing_bad = 1;
char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
char *good_glob = xstrfmt("%s-*", terms->term_good);
if (ref_exists(bad_ref))
missing_bad = 0;
for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
(void *) &missing_good);
free(good_glob);
free(bad_ref);
return decide_next(terms, current_term, missing_good, missing_bad);
}
static int get_terms(struct bisect_terms *terms)
@ -396,7 +399,7 @@ static int bisect_terms(struct bisect_terms *terms, const char *option)
static int bisect_append_log_quoted(const char **argv)
{
int retval = 0;
int res = 0;
FILE *fp = fopen(git_path_bisect_log(), "a");
struct strbuf orig_args = STRBUF_INIT;
@ -404,25 +407,25 @@ static int bisect_append_log_quoted(const char **argv)
return -1;
if (fprintf(fp, "git bisect start") < 1) {
retval = -1;
res = -1;
goto finish;
}
sq_quote_argv(&orig_args, argv);
if (fprintf(fp, "%s\n", orig_args.buf) < 1)
retval = -1;
res = -1;
finish:
fclose(fp);
strbuf_release(&orig_args);
return retval;
return res;
}
static int bisect_start(struct bisect_terms *terms, int no_checkout,
const char **argv, int argc)
{
int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
int flags, pathspec_pos, retval = 0;
int flags, pathspec_pos, res = 0;
struct string_list revs = STRING_LIST_INIT_DUP;
struct string_list states = STRING_LIST_INIT_DUP;
struct strbuf start_head = STRBUF_INIT;
@ -523,7 +526,7 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
argv_array_pushl(&argv, "checkout", start_head.buf,
"--", NULL);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
retval = error(_("checking out '%s' failed."
res = error(_("checking out '%s' failed."
" Try 'git bisect start "
"<valid-branch>'."),
start_head.buf);
@ -571,12 +574,12 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
if (no_checkout) {
if (get_oid(start_head.buf, &oid) < 0) {
retval = error(_("invalid ref: '%s'"), start_head.buf);
res = error(_("invalid ref: '%s'"), start_head.buf);
goto finish;
}
if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
UPDATE_REFS_MSG_ON_ERR)) {
retval = -1;
res = -1;
goto finish;
}
}
@ -588,26 +591,26 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
for (i = 0; i < states.nr; i++)
if (bisect_write(states.items[i].string,
revs.items[i].string, terms, 1)) {
retval = -1;
res = -1;
goto finish;
}
if (must_write_terms && write_terms(terms->term_bad,
terms->term_good)) {
retval = -1;
res = -1;
goto finish;
}
retval = bisect_append_log_quoted(argv);
if (retval)
retval = -1;
res = bisect_append_log_quoted(argv);
if (res)
res = -1;
finish:
string_list_clear(&revs, 0);
string_list_clear(&states, 0);
strbuf_release(&start_head);
strbuf_release(&bisect_names);
return retval;
return res;
}
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
@ -663,7 +666,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
switch (cmdmode) {
case NEXT_ALL:
return bisect_next_all(the_repository, prefix, no_checkout);
res = bisect_next_all(the_repository, prefix, no_checkout);
break;
case WRITE_TERMS:
if (argc != 2)
return error(_("--write-terms requires two arguments"));
@ -710,5 +714,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
return error("BUG: unknown subcommand '%d'", cmdmode);
}
free_terms(&terms);
return !!res;
/*
* Handle early success
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
*/
if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE)
res = BISECT_OK;
return abs(res);
}

View file

@ -26,7 +26,6 @@
#include "progress.h"
#include "object-store.h"
#include "blame.h"
#include "string-list.h"
#include "refs.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@ -320,18 +319,18 @@ static const char *format_time(timestamp_t time, const char *tz_str,
return time_buf.buf;
}
#define OUTPUT_ANNOTATE_COMPAT 001
#define OUTPUT_LONG_OBJECT_NAME 002
#define OUTPUT_RAW_TIMESTAMP 004
#define OUTPUT_PORCELAIN 010
#define OUTPUT_SHOW_NAME 020
#define OUTPUT_SHOW_NUMBER 040
#define OUTPUT_SHOW_SCORE 0100
#define OUTPUT_NO_AUTHOR 0200
#define OUTPUT_SHOW_EMAIL 0400
#define OUTPUT_LINE_PORCELAIN 01000
#define OUTPUT_COLOR_LINE 02000
#define OUTPUT_SHOW_AGE_WITH_COLOR 04000
#define OUTPUT_ANNOTATE_COMPAT (1U<<0)
#define OUTPUT_LONG_OBJECT_NAME (1U<<1)
#define OUTPUT_RAW_TIMESTAMP (1U<<2)
#define OUTPUT_PORCELAIN (1U<<3)
#define OUTPUT_SHOW_NAME (1U<<4)
#define OUTPUT_SHOW_NUMBER (1U<<5)
#define OUTPUT_SHOW_SCORE (1U<<6)
#define OUTPUT_NO_AUTHOR (1U<<7)
#define OUTPUT_SHOW_EMAIL (1U<<8)
#define OUTPUT_LINE_PORCELAIN (1U<<9)
#define OUTPUT_COLOR_LINE (1U<<10)
#define OUTPUT_SHOW_AGE_WITH_COLOR (1U<<11)
static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
{
@ -460,7 +459,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? the_hash_algo->hexsz : abbrev;
if (opt & OUTPUT_COLOR_LINE) {
if (cnt > 0) {
@ -862,14 +861,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")),
OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
/*
* The following two options are parsed by parse_revision_opt()
* and are only included here to get included in the "-h"
* output:
*/
{ OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, NULL, 0, parse_opt_unknown_cb },
OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
@ -885,6 +876,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct range_set ranges;
unsigned int range_i;
long anchor;
const int hexsz = the_hash_algo->hexsz;
setup_default_color_by_age();
git_config(git_blame_config, &output_option);
@ -931,11 +923,11 @@ parse_done:
} else if (show_progress < 0)
show_progress = isatty(2);
if (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)
if (0 < abbrev && abbrev < hexsz)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
else if (!abbrev)
abbrev = GIT_SHA1_HEXSZ;
abbrev = hexsz;
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);

View file

@ -624,7 +624,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")),
OPT__COLOR(&branch_use_color, N_("use colored output")),
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
FILTER_REFS_REMOTES),

View file

@ -1,4 +1,6 @@
#include "builtin.h"
#include "argv-array.h"
#include "parse-options.h"
#include "cache.h"
#include "bundle.h"
@ -9,59 +11,184 @@
* bundle supporting "fetch", "pull", and "ls-remote".
*/
static const char builtin_bundle_usage[] =
"git bundle create <file> <git-rev-list args>\n"
" or: git bundle verify <file>\n"
" or: git bundle list-heads <file> [<refname>...]\n"
" or: git bundle unbundle <file> [<refname>...]";
static const char * const builtin_bundle_usage[] = {
N_("git bundle create [<options>] <file> <git-rev-list args>"),
N_("git bundle verify [<options>] <file>"),
N_("git bundle list-heads <file> [<refname>...]"),
N_("git bundle unbundle <file> [<refname>...]"),
NULL
};
static const char * const builtin_bundle_create_usage[] = {
N_("git bundle create [<options>] <file> <git-rev-list args>"),
NULL
};
static const char * const builtin_bundle_verify_usage[] = {
N_("git bundle verify [<options>] <file>"),
NULL
};
static const char * const builtin_bundle_list_heads_usage[] = {
N_("git bundle list-heads <file> [<refname>...]"),
NULL
};
static const char * const builtin_bundle_unbundle_usage[] = {
N_("git bundle unbundle <file> [<refname>...]"),
NULL
};
static int verbose;
static int parse_options_cmd_bundle(int argc,
const char **argv,
const char* prefix,
const char * const usagestr[],
const struct option options[],
const char **bundle_file) {
int newargc;
newargc = parse_options(argc, argv, NULL, options, usagestr,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc < 1)
usage_with_options(usagestr, options);
*bundle_file = prefix_filename(prefix, argv[0]);
return newargc;
}
static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
int all_progress_implied = 0;
int progress = isatty(STDERR_FILENO);
struct argv_array pack_opts;
struct option options[] = {
OPT_SET_INT('q', "quiet", &progress,
N_("do not show progress meter"), 0),
OPT_SET_INT(0, "progress", &progress,
N_("show progress meter"), 1),
OPT_SET_INT(0, "all-progress", &progress,
N_("show progress meter during object writing phase"), 2),
OPT_BOOL(0, "all-progress-implied",
&all_progress_implied,
N_("similar to --all-progress when progress meter is shown")),
OPT_END()
};
const char* bundle_file;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_create_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
argv_array_init(&pack_opts);
if (progress == 0)
argv_array_push(&pack_opts, "--quiet");
else if (progress == 1)
argv_array_push(&pack_opts, "--progress");
else if (progress == 2)
argv_array_push(&pack_opts, "--all-progress");
if (progress && all_progress_implied)
argv_array_push(&pack_opts, "--all-progress-implied");
if (!startup_info->have_repository)
die(_("Need a repository to create a bundle."));
return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts);
}
static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
struct bundle_header header;
int bundle_fd = -1;
int quiet = 0;
struct option options[] = {
OPT_BOOL('q', "quiet", &quiet,
N_("do not show bundle details")),
OPT_END()
};
const char* bundle_file;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_verify_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
memset(&header, 0, sizeof(header));
if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0)
return 1;
close(bundle_fd);
if (verify_bundle(the_repository, &header, !quiet))
return 1;
fprintf(stderr, _("%s is okay\n"), bundle_file);
return 0;
}
static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix) {
struct bundle_header header;
int bundle_fd = -1;
struct option options[] = {
OPT_END()
};
const char* bundle_file;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_list_heads_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
memset(&header, 0, sizeof(header));
if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0)
return 1;
close(bundle_fd);
return !!list_bundle_refs(&header, argc, argv);
}
static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) {
struct bundle_header header;
int bundle_fd = -1;
struct option options[] = {
OPT_END()
};
const char* bundle_file;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_unbundle_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
memset(&header, 0, sizeof(header));
if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0)
return 1;
if (!startup_info->have_repository)
die(_("Need a repository to unbundle."));
return !!unbundle(the_repository, &header, bundle_fd, 0) ||
list_bundle_refs(&header, argc, argv);
}
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
struct bundle_header header;
const char *cmd, *bundle_file;
int bundle_fd = -1;
struct option options[] = {
OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
OPT_END()
};
int result;
if (argc < 3)
usage(builtin_bundle_usage);
argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
cmd = argv[1];
bundle_file = prefix_filename(prefix, argv[2]);
argc -= 2;
argv += 2;
packet_trace_identity("bundle");
memset(&header, 0, sizeof(header));
if (strcmp(cmd, "create") && (bundle_fd =
read_bundle_header(bundle_file, &header)) < 0)
return 1;
if (argc < 2)
usage_with_options(builtin_bundle_usage, options);
if (!strcmp(cmd, "verify")) {
close(bundle_fd);
if (argc != 1) {
usage(builtin_bundle_usage);
return 1;
}
if (verify_bundle(the_repository, &header, 1))
return 1;
fprintf(stderr, _("%s is okay\n"), bundle_file);
return 0;
else if (!strcmp(argv[0], "create"))
result = cmd_bundle_create(argc, argv, prefix);
else if (!strcmp(argv[0], "verify"))
result = cmd_bundle_verify(argc, argv, prefix);
else if (!strcmp(argv[0], "list-heads"))
result = cmd_bundle_list_heads(argc, argv, prefix);
else if (!strcmp(argv[0], "unbundle"))
result = cmd_bundle_unbundle(argc, argv, prefix);
else {
error(_("Unknown subcommand: %s"), argv[0]);
usage_with_options(builtin_bundle_usage, options);
}
if (!strcmp(cmd, "list-heads")) {
close(bundle_fd);
return !!list_bundle_refs(&header, argc, argv);
}
if (!strcmp(cmd, "create")) {
if (argc < 2) {
usage(builtin_bundle_usage);
return 1;
}
if (!startup_info->have_repository)
die(_("Need a repository to create a bundle."));
return !!create_bundle(the_repository, bundle_file, argc, argv);
} else if (!strcmp(cmd, "unbundle")) {
if (!startup_info->have_repository)
die(_("Need a repository to unbundle."));
return !!unbundle(the_repository, &header, bundle_fd, 0) ||
list_bundle_refs(&header, argc, argv);
} else
usage(builtin_bundle_usage);
return result ? 1 : 0;
}

View file

@ -15,6 +15,7 @@
#include "sha1-array.h"
#include "packfile.h"
#include "object-store.h"
#include "promisor-remote.h"
struct batch_options {
int enabled;
@ -261,7 +262,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
strbuf_addstr(sb, data->rest);
} else if (is_atom("deltabase", atom, len)) {
if (data->mark_query)
data->info.delta_base_sha1 = data->delta_base_oid.hash;
data->info.delta_base_oid = &data->delta_base_oid;
else
strbuf_addstr(sb,
oid_to_hex(&data->delta_base_oid));
@ -524,8 +525,8 @@ static int batch_objects(struct batch_options *opt)
if (opt->all_objects) {
struct object_cb_data cb;
if (repository_format_partial_clone)
warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
if (has_promisor_remote())
warning("This repository uses promisor remotes. Some objects may not be loaded.");
cb.opt = opt;
cb.expand = &data;

View file

@ -32,19 +32,19 @@ static const struct option check_ignore_options[] = {
OPT_END()
};
static void output_exclude(const char *path, struct exclude *exclude)
static void output_pattern(const char *path, struct path_pattern *pattern)
{
char *bang = (exclude && exclude->flags & EXC_FLAG_NEGATIVE) ? "!" : "";
char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : "";
char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : "";
char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
if (!nul_term_line) {
if (!verbose) {
write_name_quoted(path, stdout, '\n');
} else {
if (exclude) {
quote_c_style(exclude->el->src, NULL, stdout, 0);
if (pattern) {
quote_c_style(pattern->pl->src, NULL, stdout, 0);
printf(":%d:%s%s%s\t",
exclude->srcpos,
bang, exclude->pattern, slash);
pattern->srcpos,
bang, pattern->pattern, slash);
}
else {
printf("::\t");
@ -56,11 +56,11 @@ static void output_exclude(const char *path, struct exclude *exclude)
if (!verbose) {
printf("%s%c", path, '\0');
} else {
if (exclude)
if (pattern)
printf("%s%c%d%c%s%s%s%c%s%c",
exclude->el->src, '\0',
exclude->srcpos, '\0',
bang, exclude->pattern, slash, '\0',
pattern->pl->src, '\0',
pattern->srcpos, '\0',
bang, pattern->pattern, slash, '\0',
path, '\0');
else
printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0');
@ -74,7 +74,7 @@ static int check_ignore(struct dir_struct *dir,
const char *full_path;
char *seen;
int num_ignored = 0, i;
struct exclude *exclude;
struct path_pattern *pattern;
struct pathspec pathspec;
if (!argc) {
@ -103,15 +103,18 @@ static int check_ignore(struct dir_struct *dir,
seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
for (i = 0; i < pathspec.nr; i++) {
full_path = pathspec.items[i].match;
exclude = NULL;
pattern = NULL;
if (!seen[i]) {
int dtype = DT_UNKNOWN;
exclude = last_exclude_matching(dir, &the_index,
pattern = last_matching_pattern(dir, &the_index,
full_path, &dtype);
if (!verbose && pattern &&
pattern->flags & PATTERN_FLAG_NEGATIVE)
pattern = NULL;
}
if (!quiet && (exclude || show_non_matching))
output_exclude(pathspec.items[i].original, exclude);
if (exclude)
if (!quiet && (pattern || show_non_matching))
output_pattern(pathspec.items[i].original, pattern);
if (pattern)
num_ignored++;
}
free(seen);

View file

@ -70,6 +70,8 @@ struct checkout_opts {
int checkout_worktree;
const char *ignore_unmerged_opt;
int ignore_unmerged;
int pathspec_file_nul;
const char *pathspec_from_file;
const char *new_branch;
const char *new_branch_force;
@ -126,6 +128,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
if (pos >= 0) {
struct cache_entry *old = active_cache[pos];
if (ce->ce_mode == old->ce_mode &&
!ce_intent_to_add(old) &&
oideq(&ce->oid, &old->oid)) {
old->ce_flags |= CE_UPDATE;
discard_cache_entry(ce);
@ -521,6 +524,8 @@ static int checkout_paths(const struct checkout_opts *opts,
/* Now we are committed to check them out */
if (opts->checkout_worktree)
errs |= checkout_worktree(opts);
else
remove_marked_cache_entries(&the_index, 1);
/*
* Allow updating the index when checking out from the index.
@ -708,11 +713,11 @@ static int merge_working_tree(const struct checkout_opts *opts,
* give up or do a real merge, depending on
* whether the merge flag was used.
*/
struct tree *result;
struct tree *work;
struct tree *old_tree;
struct merge_options o;
struct strbuf sb = STRBUF_INIT;
struct strbuf old_commit_shortname = STRBUF_INIT;
if (!opts->merge)
return 1;
@ -730,13 +735,6 @@ static int merge_working_tree(const struct checkout_opts *opts,
"the following files:\n%s"), sb.buf);
strbuf_release(&sb);
if (repo_index_has_changes(the_repository,
get_commit_tree(old_branch_info->commit),
&sb))
warning(_("staged changes in the following files may be lost: %s"),
sb.buf);
strbuf_release(&sb);
/* Do more real merge */
/*
@ -760,7 +758,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
*/
init_merge_options(&o, the_repository);
o.verbosity = 0;
work = write_tree_from_memory(&o);
work = write_in_core_index_as_tree(the_repository);
ret = reset_tree(new_tree,
opts, 1,
@ -768,19 +766,25 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (ret)
return ret;
o.ancestor = old_branch_info->name;
if (old_branch_info->name == NULL) {
strbuf_add_unique_abbrev(&old_commit_shortname,
&old_branch_info->commit->object.oid,
DEFAULT_ABBREV);
o.ancestor = old_commit_shortname.buf;
}
o.branch1 = new_branch_info->name;
o.branch2 = "local";
ret = merge_trees(&o,
new_tree,
work,
old_tree,
&result);
old_tree);
if (ret < 0)
exit(128);
ret = reset_tree(new_tree,
opts, 0,
writeout_error);
strbuf_release(&o.obuf);
strbuf_release(&old_commit_shortname);
if (ret)
return ret;
}
@ -861,7 +865,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
strbuf_addf(&msg, "checkout: moving from %s to %s",
old_desc ? old_desc : "(invalid)", new_branch_info->name);
else
strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
strbuf_insertstr(&msg, 0, reflog_msg);
if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
/* Nothing to do. */
@ -1113,12 +1117,43 @@ static void setup_new_branch_info_and_source_tree(
}
}
static const char *parse_remote_branch(const char *arg,
struct object_id *rev,
int could_be_checkout_paths)
{
int num_matches = 0;
const char *remote = unique_tracking_name(arg, rev, &num_matches);
if (remote && could_be_checkout_paths) {
die(_("'%s' could be both a local file and a tracking branch.\n"
"Please use -- (and optionally --no-guess) to disambiguate"),
arg);
}
if (!remote && num_matches > 1) {
if (advice_checkout_ambiguous_remote_branch_name) {
advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
" git checkout --track origin/<name>\n"
"\n"
"If you'd like to always have checkouts of an ambiguous <name> prefer\n"
"one remote, e.g. the 'origin' remote, consider setting\n"
"checkout.defaultRemote=origin in your config."));
}
die(_("'%s' matched multiple (%d) remote tracking branches"),
arg, num_matches);
}
return remote;
}
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
struct branch_info *new_branch_info,
struct checkout_opts *opts,
struct object_id *rev,
int *dwim_remotes_matched)
struct object_id *rev)
{
const char **new_branch = &opts->new_branch;
int argcount = 0;
@ -1223,13 +1258,9 @@ static int parse_branchname_arg(int argc, const char **argv,
recover_with_dwim = 0;
if (recover_with_dwim) {
const char *remote = unique_tracking_name(arg, rev,
dwim_remotes_matched);
const char *remote = parse_remote_branch(arg, rev,
could_be_checkout_paths);
if (remote) {
if (could_be_checkout_paths)
die(_("'%s' could be both a local file and a tracking branch.\n"
"Please use -- (and optionally --no-guess) to disambiguate"),
arg);
*new_branch = arg;
arg = remote;
/* DWIMmed to create local branch, case (3).(b) */
@ -1480,6 +1511,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
N_("do not limit pathspecs to sparse entries only")),
OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@ -1492,7 +1525,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
const char * const usagestr[])
{
struct branch_info new_branch_info;
int dwim_remotes_matched = 0;
int parseopt_flags = 0;
memset(&new_branch_info, 0, sizeof(new_branch_info));
@ -1600,8 +1632,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
opts->track == BRANCH_TRACK_UNSPECIFIED &&
!opts->new_branch;
int n = parse_branchname_arg(argc, argv, dwim_ok,
&new_branch_info, opts, &rev,
&dwim_remotes_matched);
&new_branch_info, opts, &rev);
argv += n;
argc -= n;
} else if (!opts->accept_ref && opts->from_treeish) {
@ -1618,10 +1649,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
die(_("reference is not a tree: %s"), opts->from_treeish);
}
if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
!opts->patch_mode) /* patch mode is special */
die(_("you must specify path(s) to restore"));
if (argc) {
parse_pathspec(&opts->pathspec, 0,
opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@ -1641,10 +1668,33 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
if (opts->force_detach)
die(_("git checkout: --detach does not take a path argument '%s'"),
argv[0]);
}
if (opts->pathspec_from_file) {
if (opts->pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
if (opts->force_detach)
die(_("--pathspec-from-file is incompatible with --detach"));
if (opts->patch_mode)
die(_("--pathspec-from-file is incompatible with --patch"));
parse_pathspec_file(&opts->pathspec, 0,
0,
prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
} else if (opts->pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
if (opts->pathspec.nr) {
if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
"checking out of the index."));
} else {
if (opts->accept_pathspec && !opts->empty_pathspec_ok &&
!opts->patch_mode) /* patch mode is special */
die(_("you must specify path(s) to restore"));
}
if (opts->new_branch) {
@ -1659,28 +1709,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
}
UNLEAK(opts);
if (opts->patch_mode || opts->pathspec.nr) {
int ret = checkout_paths(opts, new_branch_info.name);
if (ret && dwim_remotes_matched > 1 &&
advice_checkout_ambiguous_remote_branch_name)
advise(_("'%s' matched more than one remote tracking branch.\n"
"We found %d remotes with a reference that matched. So we fell back\n"
"on trying to resolve the argument as a path, but failed there too!\n"
"\n"
"If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
" git checkout --track origin/<name>\n"
"\n"
"If you'd like to always have checkouts of an ambiguous <name> prefer\n"
"one remote, e.g. the 'origin' remote, consider setting\n"
"checkout.defaultRemote=origin in your config."),
argv[0],
dwim_remotes_matched);
return ret;
} else {
if (opts->patch_mode || opts->pathspec.nr)
return checkout_paths(opts, new_branch_info.name);
else
return checkout_branch(opts, &new_branch_info);
}
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
@ -1714,6 +1746,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.checkout_index = -2; /* default on */
opts.checkout_worktree = -2; /* default on */
if (argc == 3 && !strcmp(argv[1], "-b")) {
/*
* User ran 'git checkout -b <branch>' and expects
* the same behavior as 'git switch -c <branch>'.
*/
opts.switch_branch_doing_nothing_is_ok = 0;
opts.only_merge_on_switching_branches = 1;
}
options = parse_options_dup(checkout_options);
options = add_common_options(&opts, options);
options = add_common_switch_branch_options(&opts, options);

View file

@ -158,7 +158,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
*dir_gone = 1;
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
is_nonbare_repository_dir(path)) {
if (!quiet) {
quote_path_relative(path->buf, prefix, &quoted);
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
@ -648,7 +649,7 @@ static int filter_by_patterns_cmd(void)
struct strbuf confirm = STRBUF_INIT;
struct strbuf **ignore_list;
struct string_list_item *item;
struct exclude_list *el;
struct pattern_list *pl;
int changed = -1, i;
for (;;) {
@ -671,7 +672,7 @@ static int filter_by_patterns_cmd(void)
break;
memset(&dir, 0, sizeof(dir));
el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude");
ignore_list = strbuf_split_max(&confirm, ' ', 0);
for (i = 0; ignore_list[i]; i++) {
@ -679,7 +680,7 @@ static int filter_by_patterns_cmd(void)
if (!ignore_list[i]->len)
continue;
add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1));
}
changed = 0;
@ -901,7 +902,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct pathspec pathspec;
struct strbuf buf = STRBUF_INIT;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct exclude_list *el;
struct pattern_list *pl;
struct string_list_item *item;
const char *qname;
struct option options[] = {
@ -946,9 +947,19 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (force > 1)
rm_flags = 0;
else
dir.flags |= DIR_SKIP_NESTED_GIT;
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
if (argc) {
/*
* Remaining args implies pathspecs specified, and we should
* recurse within those.
*/
remove_directories = 1;
}
if (remove_directories)
dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
@ -958,9 +969,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (!ignored)
setup_standard_excludes(&dir);
el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
for (i = 0; i < exclude_list.nr; i++)
add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1));
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD,
@ -1007,6 +1018,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
for_each_string_list_item(item, &del_list) {
struct stat st;
strbuf_reset(&abs_path);
if (prefix)
strbuf_addstr(&abs_path, prefix);
@ -1040,7 +1052,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
}
strbuf_reset(&abs_path);
}
strbuf_release(&abs_path);

View file

@ -32,7 +32,6 @@
#include "connected.h"
#include "packfile.h"
#include "list-objects-filter-options.h"
#include "object-store.h"
/*
* Overall FIXMEs:
@ -60,6 +59,7 @@ static const char *real_git_dir;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
static int option_sparse_checkout;
static enum transport_family family;
static struct string_list option_config = STRING_LIST_INIT_NODUP;
static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
@ -147,6 +147,8 @@ static struct option builtin_clone_options[] = {
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
N_("any cloned submodules will use their remote-tracking branch")),
OPT_BOOL(0, "sparse", &option_sparse_checkout,
N_("initialize sparse-checkout file to include only files at root")),
OPT_END()
};
@ -671,7 +673,7 @@ static void update_remote_refs(const struct ref *refs,
const char *msg,
struct transport *transport,
int check_connectivity,
int check_refs_only)
int check_refs_are_promisor_objects_only)
{
const struct ref *rm = mapped_refs;
@ -680,7 +682,8 @@ static void update_remote_refs(const struct ref *refs,
opt.transport = transport;
opt.progress = transport->progress;
opt.check_refs_only = !!check_refs_only;
opt.check_refs_are_promisor_objects_only =
!!check_refs_are_promisor_objects_only;
if (check_connected(iterate_ref_map, &rm, &opt))
die(_("remote did not send all necessary objects"));
@ -734,6 +737,27 @@ static void update_head(const struct ref *our, const struct ref *remote,
}
}
static int git_sparse_checkout_init(const char *repo)
{
struct argv_array argv = ARGV_ARRAY_INIT;
int result = 0;
argv_array_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
/*
* We must apply the setting in the current process
* for the later checkout to use the sparse-checkout file.
*/
core_apply_sparse_checkout = 1;
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
error(_("failed to initialize sparse-checkout"));
result = 1;
}
argv_array_clear(&argv);
return result;
}
static int checkout(int submodule_progress)
{
struct object_id oid;
@ -785,12 +809,12 @@ static int checkout(int submodule_progress)
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
err |= run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
oid_to_hex(&oid), "1", NULL);
if (!err && (option_recurse_submodules.nr > 0)) {
struct argv_array args = ARGV_ARRAY_INIT;
argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
if (option_shallow_submodules == 1)
argv_array_push(&args, "--depth=1");
@ -809,6 +833,11 @@ static int checkout(int submodule_progress)
argv_array_push(&args, "--no-fetch");
}
if (option_single_branch >= 0)
argv_array_push(&args, option_single_branch ?
"--single-branch" :
"--no-single-branch");
err = run_command_v_opt(args.argv, RUN_GIT_CMD);
argv_array_clear(&args);
}
@ -900,7 +929,7 @@ static void dissociate_from_references(void)
free(alternates);
}
static int dir_exists(const char *path)
static int path_exists(const char *path)
{
struct stat sb;
return !stat(path, &sb);
@ -928,8 +957,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
fetch_if_missing = 0;
packet_trace_identity("clone");
argc = parse_options(argc, argv, prefix, builtin_clone_options,
builtin_clone_usage, 0);
@ -982,7 +1009,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
dir = guess_dir_name(repo_name, is_bundle, option_bare);
strip_trailing_slashes(dir);
dest_exists = dir_exists(dir);
dest_exists = path_exists(dir);
if (dest_exists && !is_empty_dir(dir))
die(_("destination path '%s' already exists and is not "
"an empty directory."), dir);
@ -993,7 +1020,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
work_tree = NULL;
else {
work_tree = getenv("GIT_WORK_TREE");
if (work_tree && dir_exists(work_tree))
if (work_tree && path_exists(work_tree))
die(_("working tree '%s' already exists."), work_tree);
}
@ -1021,7 +1048,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
if (real_git_dir) {
if (dir_exists(real_git_dir))
if (path_exists(real_git_dir))
junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
junk_git_dir = real_git_dir;
} else {
@ -1107,6 +1134,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
if (option_sparse_checkout && git_sparse_checkout_init(dir))
return 1;
remote = remote_get(option_origin);
strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
@ -1160,13 +1190,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport->server_options = &server_options;
if (filter_options.choice) {
struct strbuf expanded_filter_spec = STRBUF_INIT;
expand_list_objects_filter_spec(&filter_options,
&expanded_filter_spec);
const char *spec =
expand_list_objects_filter_spec(&filter_options);
transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
expanded_filter_spec.buf);
spec);
transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
strbuf_release(&expanded_filter_spec);
}
if (transport->smart_options && !deepen && !filter_options.choice)
@ -1268,7 +1296,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
junk_mode = JUNK_LEAVE_REPO;
fetch_if_missing = 1;
err = checkout(submodule_progress);
strbuf_release(&reflog_msg);

View file

@ -8,25 +8,18 @@
#include "object-store.h"
static char const * const builtin_commit_graph_usage[] = {
N_("git commit-graph [--object-dir <objdir>]"),
N_("git commit-graph read [--object-dir <objdir>]"),
N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
NULL
};
static const char * const builtin_commit_graph_verify_usage[] = {
N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
NULL
};
static const char * const builtin_commit_graph_read_usage[] = {
N_("git commit-graph read [--object-dir <objdir>]"),
N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
NULL
};
static const char * const builtin_commit_graph_write_usage[] = {
N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
NULL
};
@ -38,11 +31,32 @@ static struct opts_commit_graph {
int append;
int split;
int shallow;
int progress;
} opts;
static struct object_directory *find_odb(struct repository *r,
const char *obj_dir)
{
struct object_directory *odb;
char *obj_dir_real = real_pathdup(obj_dir, 1);
prepare_alt_odb(r);
for (odb = r->objects->odb; odb; odb = odb->next) {
if (!strcmp(obj_dir_real, real_path(odb->path)))
break;
}
free(obj_dir_real);
if (!odb)
die(_("could not find object directory matching %s"), obj_dir);
return odb;
}
static int graph_verify(int argc, const char **argv)
{
struct commit_graph *graph = NULL;
struct object_directory *odb = NULL;
char *graph_name;
int open_ok;
int fd;
@ -55,9 +69,13 @@ static int graph_verify(int argc, const char **argv)
N_("The object directory to store the graph")),
OPT_BOOL(0, "shallow", &opts.shallow,
N_("if the commit-graph is split, only verify the tip file")),
OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
OPT_END(),
};
trace2_cmd_mode("verify");
opts.progress = isatty(2);
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_verify_options,
builtin_commit_graph_verify_usage, 0);
@ -66,8 +84,11 @@ static int graph_verify(int argc, const char **argv)
opts.obj_dir = get_object_directory();
if (opts.shallow)
flags |= COMMIT_GRAPH_VERIFY_SHALLOW;
if (opts.progress)
flags |= COMMIT_GRAPH_WRITE_PROGRESS;
graph_name = get_commit_graph_filename(opts.obj_dir);
odb = find_odb(the_repository, opts.obj_dir);
graph_name = get_commit_graph_filename(odb);
open_ok = open_commit_graph(graph_name, &fd, &st);
if (!open_ok && errno != ENOENT)
die_errno(_("Could not open commit-graph '%s'"), graph_name);
@ -75,9 +96,9 @@ static int graph_verify(int argc, const char **argv)
FREE_AND_NULL(graph_name);
if (open_ok)
graph = load_commit_graph_one_fd_st(fd, &st);
else
graph = read_commit_graph_one(the_repository, opts.obj_dir);
graph = load_commit_graph_one_fd_st(fd, &st, odb);
else
graph = read_commit_graph_one(the_repository, odb);
/* Return failure if open_ok predicted success */
if (!graph)
@ -87,64 +108,6 @@ static int graph_verify(int argc, const char **argv)
return verify_commit_graph(the_repository, graph, flags);
}
static int graph_read(int argc, const char **argv)
{
struct commit_graph *graph = NULL;
char *graph_name;
int open_ok;
int fd;
struct stat st;
static struct option builtin_commit_graph_read_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("The object directory to store the graph")),
OPT_END(),
};
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_read_options,
builtin_commit_graph_read_usage, 0);
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
graph_name = get_commit_graph_filename(opts.obj_dir);
open_ok = open_commit_graph(graph_name, &fd, &st);
if (!open_ok)
die_errno(_("Could not open commit-graph '%s'"), graph_name);
graph = load_commit_graph_one_fd_st(fd, &st);
if (!graph)
return 1;
FREE_AND_NULL(graph_name);
printf("header: %08x %d %d %d %d\n",
ntohl(*(uint32_t*)graph->data),
*(unsigned char*)(graph->data + 4),
*(unsigned char*)(graph->data + 5),
*(unsigned char*)(graph->data + 6),
*(unsigned char*)(graph->data + 7));
printf("num_commits: %u\n", graph->num_commits);
printf("chunks:");
if (graph->chunk_oid_fanout)
printf(" oid_fanout");
if (graph->chunk_oid_lookup)
printf(" oid_lookup");
if (graph->chunk_commit_data)
printf(" commit_metadata");
if (graph->chunk_extra_edges)
printf(" extra_edges");
printf("\n");
UNLEAK(graph);
return 0;
}
extern int read_replace_refs;
static struct split_commit_graph_opts split_opts;
@ -152,9 +115,10 @@ static int graph_write(int argc, const char **argv)
{
struct string_list *pack_indexes = NULL;
struct string_list *commit_hex = NULL;
struct object_directory *odb = NULL;
struct string_list lines;
int result = 0;
unsigned int flags = COMMIT_GRAPH_PROGRESS;
enum commit_graph_write_flags flags = 0;
static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
@ -168,6 +132,7 @@ static int graph_write(int argc, const char **argv)
N_("start walk at commits listed by stdin")),
OPT_BOOL(0, "append", &opts.append,
N_("include all commits already in the commit-graph file")),
OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
OPT_BOOL(0, "split", &opts.split,
N_("allow writing an incremental commit-graph file")),
OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
@ -179,10 +144,13 @@ static int graph_write(int argc, const char **argv)
OPT_END(),
};
opts.progress = isatty(2);
split_opts.size_multiple = 2;
split_opts.max_commits = 0;
split_opts.expire_time = 0;
trace2_cmd_mode("write");
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_write_options,
builtin_commit_graph_write_usage, 0);
@ -192,14 +160,17 @@ static int graph_write(int argc, const char **argv)
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
if (opts.append)
flags |= COMMIT_GRAPH_APPEND;
flags |= COMMIT_GRAPH_WRITE_APPEND;
if (opts.split)
flags |= COMMIT_GRAPH_SPLIT;
flags |= COMMIT_GRAPH_WRITE_SPLIT;
if (opts.progress)
flags |= COMMIT_GRAPH_WRITE_PROGRESS;
read_replace_refs = 0;
odb = find_odb(the_repository, opts.obj_dir);
if (opts.reachable) {
if (write_commit_graph_reachable(opts.obj_dir, flags, &split_opts))
if (write_commit_graph_reachable(odb, flags, &split_opts))
return 1;
return 0;
}
@ -213,13 +184,15 @@ static int graph_write(int argc, const char **argv)
if (opts.stdin_packs)
pack_indexes = &lines;
if (opts.stdin_commits)
if (opts.stdin_commits) {
commit_hex = &lines;
flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS;
}
UNLEAK(buf);
}
if (write_commit_graph(opts.obj_dir,
if (write_commit_graph(odb,
pack_indexes,
commit_hex,
flags,
@ -249,9 +222,9 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
builtin_commit_graph_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
save_commit_buffer = 0;
if (argc > 0) {
if (!strcmp(argv[0], "read"))
return graph_read(argc, argv);
if (!strcmp(argv[0], "verify"))
return graph_verify(argc, argv);
if (!strcmp(argv[0], "write"))

View file

@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message;
static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
static char *sign_commit;
static char *sign_commit, *pathspec_from_file;
/*
* The default commit message cleanup mode will remove the lines
@ -343,11 +343,31 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
PATHSPEC_PREFER_FULL,
prefix, argv);
if (pathspec_from_file) {
if (interactive)
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
if (all)
die(_("--pathspec-from-file with -a does not make sense"));
if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));
if (read_cache_preload(&pathspec) < 0)
die(_("index file corrupt"));
if (interactive) {
char *old_index_env = NULL;
char *old_index_env = NULL, *old_repo_index_file;
hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
@ -355,12 +375,16 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to create temporary index"));
old_repo_index_file = the_repository->index_file;
the_repository->index_file =
(char *)get_lock_file_path(&index_lock);
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
die(_("interactive add failed"));
the_repository->index_file = old_repo_index_file;
if (old_index_env && *old_index_env)
setenv(INDEX_ENVIRONMENT, old_index_env, 1);
else
@ -510,7 +534,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->nowarn = nowarn;
s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
if (!s->is_initial)
hashcpy(s->sha1_commit, oid.hash);
oidcpy(&s->oid_commit, &oid);
s->status_format = status_format;
s->ignore_submodule_arg = ignore_submodule_arg;
@ -537,7 +561,7 @@ static void export_one(const char *var, const char *s, const char *e, int hack)
struct strbuf buf = STRBUF_INIT;
if (hack)
strbuf_addch(&buf, hack);
strbuf_addf(&buf, "%.*s", (int)(e - s), s);
strbuf_add(&buf, s, e - s);
setenv(var, buf.buf, 1);
strbuf_release(&buf);
}
@ -944,6 +968,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
*/
if (!committable && whence != FROM_MERGE && !allow_empty &&
!(amend && is_a_merge(current_head))) {
s->hints = advice_status_hints;
s->display_comment_prefix = old_display_comment_prefix;
run_status(stdout, index_file, prefix, 0, s);
if (amend)
@ -1198,8 +1223,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (also + only + all + interactive > 1)
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
if (argc == 0 && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
handle_untracked_files_arg(s);
@ -1406,7 +1429,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
if (!s.is_initial)
hashcpy(s.sha1_commit, oid.hash);
oidcpy(&s.oid_commit, &oid);
s.ignore_submodule_arg = ignore_submodule_arg;
s.status_format = status_format;
@ -1463,28 +1486,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
return git_status_config(k, v, s);
}
int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
{
struct argv_array hook_env = ARGV_ARRAY_INIT;
va_list args;
int ret;
argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
/*
* Let the hook know that no editor will be launched.
*/
if (!editor_is_used)
argv_array_push(&hook_env, "GIT_EDITOR=:");
va_start(args, name);
ret = run_hook_ve(hook_env.argv,name, args);
va_end(args);
argv_array_clear(&hook_env);
return ret;
}
int cmd_commit(int argc, const char **argv, const char *prefix)
{
const char *argv_gc_auto[] = {"gc", "--auto", NULL};
@ -1535,6 +1536,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
/* end commit contents options */
OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
@ -1690,7 +1693,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"not exceeded, and then \"git restore --staged :/\" to recover."));
if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
write_commit_graph_reachable(get_object_directory(), 0, NULL))
write_commit_graph_reachable(the_repository->objects->odb, 0, NULL))
return 1;
repo_rerere(the_repository, 0);

View file

@ -29,10 +29,11 @@ static int use_worktree_config;
static struct git_config_source given_config_source;
static int actions, type;
static char *default_value;
static int end_null;
static int end_nul;
static int respect_includes_opt = -1;
static struct config_options config_options;
static int show_origin;
static int show_scope;
#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
@ -151,10 +152,11 @@ static struct option builtin_config_options[] = {
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
OPT_GROUP(N_("Other")),
OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_END(),
};
@ -178,22 +180,34 @@ static void check_argc(int argc, int min, int max)
static void show_config_origin(struct strbuf *buf)
{
const char term = end_null ? '\0' : '\t';
const char term = end_nul ? '\0' : '\t';
strbuf_addstr(buf, current_config_origin_type());
strbuf_addch(buf, ':');
if (end_null)
if (end_nul)
strbuf_addstr(buf, current_config_name());
else
quote_c_style(current_config_name(), buf, NULL, 0);
strbuf_addch(buf, term);
}
static void show_config_scope(struct strbuf *buf)
{
const char term = end_nul ? '\0' : '\t';
const char *scope = config_scope_name(current_config_scope());
strbuf_addstr(buf, N_(scope));
strbuf_addch(buf, term);
}
static int show_all_config(const char *key_, const char *value_, void *cb)
{
if (show_origin) {
if (show_origin || show_scope) {
struct strbuf buf = STRBUF_INIT;
show_config_origin(&buf);
if (show_scope)
show_config_scope(&buf);
if (show_origin)
show_config_origin(&buf);
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf);
@ -213,6 +227,8 @@ struct strbuf_list {
static int format_config(struct strbuf *buf, const char *key_, const char *value_)
{
if (show_scope)
show_config_scope(buf);
if (show_origin)
show_config_origin(buf);
if (show_keys)
@ -622,6 +638,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
!strcmp(given_config_source.file, "-")) {
given_config_source.file = NULL;
given_config_source.use_stdin = 1;
given_config_source.scope = CONFIG_SCOPE_COMMAND;
}
if (use_global_config) {
@ -637,6 +654,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
*/
die(_("$HOME not set"));
given_config_source.scope = CONFIG_SCOPE_GLOBAL;
if (access_or_warn(user_config, R_OK, 0) &&
xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
given_config_source.file = xdg_config;
@ -646,11 +665,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
free(xdg_config);
}
}
else if (use_system_config)
else if (use_system_config) {
given_config_source.file = git_etc_gitconfig();
else if (use_local_config)
given_config_source.scope = CONFIG_SCOPE_SYSTEM;
} else if (use_local_config) {
given_config_source.file = git_pathdup("config");
else if (use_worktree_config) {
given_config_source.scope = CONFIG_SCOPE_LOCAL;
} else if (use_worktree_config) {
struct worktree **worktrees = get_worktrees(0);
if (repository_format_worktree_config)
given_config_source.file = git_pathdup("config.worktree");
@ -662,13 +683,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)
"section in \"git help worktree\" for details"));
else
given_config_source.file = git_pathdup("config");
given_config_source.scope = CONFIG_SCOPE_LOCAL;
free_worktrees(worktrees);
} else if (given_config_source.file) {
if (!is_absolute_path(given_config_source.file) && prefix)
given_config_source.file =
prefix_filename(prefix, given_config_source.file);
given_config_source.scope = CONFIG_SCOPE_COMMAND;
} else if (given_config_source.blob) {
given_config_source.scope = CONFIG_SCOPE_COMMAND;
}
if (respect_includes_opt == -1)
config_options.respect_includes = !given_config_source.file;
else
@ -678,7 +704,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
config_options.git_dir = get_git_dir();
}
if (end_null) {
if (end_nul) {
term = '\0';
delim = '\n';
key_delim = '\n';

View file

@ -15,7 +15,6 @@
#include "argv-array.h"
#include "run-command.h"
#include "object-store.h"
#include "revision.h"
#include "list-objects.h"
#include "commit-slab.h"
@ -64,19 +63,22 @@ static const char *prio_names[] = {
};
static int commit_name_neq(const void *unused_cmp_data,
const void *entry,
const void *entry_or_key,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *peeled)
{
const struct commit_name *cn1 = entry;
const struct commit_name *cn2 = entry_or_key;
const struct commit_name *cn1, *cn2;
cn1 = container_of(eptr, const struct commit_name, entry);
cn2 = container_of(entry_or_key, const struct commit_name, entry);
return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled);
}
static inline struct commit_name *find_commit_name(const struct object_id *peeled)
{
return hashmap_get_from_hash(&names, oidhash(peeled), peeled);
return hashmap_get_entry_from_hash(&names, oidhash(peeled), peeled,
struct commit_name, entry);
}
static int replace_name(struct commit_name *e,
@ -123,8 +125,8 @@ static void add_to_known_names(const char *path,
if (!e) {
e = xmalloc(sizeof(struct commit_name));
oidcpy(&e->peeled, peeled);
hashmap_entry_init(e, oidhash(peeled));
hashmap_add(&names, e);
hashmap_entry_init(&e->entry, oidhash(peeled));
hashmap_add(&names, &e->entry);
e->path = NULL;
}
e->tag = tag;
@ -313,7 +315,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
*/
append_name(n, dst);
if (longformat)
append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst);
if (suffix)
strbuf_addstr(dst, suffix);
return;
@ -330,8 +332,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
struct commit_name *n;
init_commit_names(&commit_names);
n = hashmap_iter_first(&names, &iter);
for (; n; n = hashmap_iter_next(&iter)) {
hashmap_for_each_entry(&names, &iter, n,
entry /* member name */) {
c = lookup_commit_reference_gently(the_repository,
&n->peeled, 1);
if (c)
@ -374,11 +376,25 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
if (!(c->object.flags & t->flag_within))
t->depth++;
}
/* Stop if last remaining path already covered by best candidate(s) */
if (annotated_cnt && !list) {
if (debug)
fprintf(stderr, _("finished search at %s\n"),
oid_to_hex(&c->object.oid));
break;
int best_depth = INT_MAX;
unsigned best_within = 0;
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
struct possible_tag *t = &all_matches[cur_match];
if (t->depth < best_depth) {
best_depth = t->depth;
best_within = t->flag_within;
} else if (t->depth == best_depth) {
best_within |= t->flag_within;
}
}
if ((c->object.flags & best_within) == best_within) {
if (debug)
fprintf(stderr, _("finished search at %s\n"),
oid_to_hex(&c->object.oid));
break;
}
}
while (parents) {
struct commit *p = parents->item;

View file

@ -125,12 +125,15 @@ struct working_tree_entry {
};
static int working_tree_entry_cmp(const void *unused_cmp_data,
const void *entry,
const void *entry_or_key,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
const struct working_tree_entry *a = entry;
const struct working_tree_entry *b = entry_or_key;
const struct working_tree_entry *a, *b;
a = container_of(eptr, const struct working_tree_entry, entry);
b = container_of(entry_or_key, const struct working_tree_entry, entry);
return strcmp(a->path, b->path);
}
@ -145,12 +148,14 @@ struct pair_entry {
};
static int pair_cmp(const void *unused_cmp_data,
const void *entry,
const void *entry_or_key,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
const struct pair_entry *a = entry;
const struct pair_entry *b = entry_or_key;
const struct pair_entry *a, *b;
a = container_of(eptr, const struct pair_entry, entry);
b = container_of(entry_or_key, const struct pair_entry, entry);
return strcmp(a->path, b->path);
}
@ -161,14 +166,14 @@ static void add_left_or_right(struct hashmap *map, const char *path,
struct pair_entry *e, *existing;
FLEX_ALLOC_STR(e, path, path);
hashmap_entry_init(e, strhash(path));
existing = hashmap_get(map, e, NULL);
hashmap_entry_init(&e->entry, strhash(path));
existing = hashmap_get_entry(map, e, entry, NULL);
if (existing) {
free(e);
e = existing;
} else {
e->left[0] = e->right[0] = '\0';
hashmap_add(map, e);
hashmap_add(map, &e->entry);
}
strlcpy(is_right ? e->right : e->left, content, PATH_MAX);
}
@ -179,12 +184,14 @@ struct path_entry {
};
static int path_entry_cmp(const void *unused_cmp_data,
const void *entry,
const void *entry_or_key,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *key)
{
const struct path_entry *a = entry;
const struct path_entry *b = entry_or_key;
const struct path_entry *a, *b;
a = container_of(eptr, const struct path_entry, entry);
b = container_of(entry_or_key, const struct path_entry, entry);
return strcmp(a->path, key ? key : b->path);
}
@ -234,8 +241,8 @@ static void changed_files(struct hashmap *result, const char *index_path,
while (!strbuf_getline_nul(&buf, fp)) {
struct path_entry *entry;
FLEX_ALLOC_STR(entry, path, buf.buf);
hashmap_entry_init(entry, strhash(buf.buf));
hashmap_add(result, entry);
hashmap_entry_init(&entry->entry, strhash(buf.buf));
hashmap_add(result, &entry->entry);
}
fclose(fp);
if (finish_command(&diff_files))
@ -461,12 +468,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
/* Avoid duplicate working_tree entries */
FLEX_ALLOC_STR(entry, path, dst_path);
hashmap_entry_init(entry, strhash(dst_path));
if (hashmap_get(&working_tree_dups, entry, NULL)) {
hashmap_entry_init(&entry->entry, strhash(dst_path));
if (hashmap_get(&working_tree_dups, &entry->entry,
NULL)) {
free(entry);
continue;
}
hashmap_add(&working_tree_dups, entry);
hashmap_add(&working_tree_dups, &entry->entry);
if (!use_wt_file(workdir, dst_path, &roid)) {
if (checkout_path(rmode, &roid, dst_path,
@ -530,8 +538,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* temporary file to both the left and right directories to show the
* change in the recorded SHA1 for the submodule.
*/
hashmap_iter_init(&submodules, &iter);
while ((entry = hashmap_iter_next(&iter))) {
hashmap_for_each_entry(&submodules, &iter, entry,
entry /* member name */) {
if (*entry->left) {
add_path(&ldir, ldir_len, entry->path);
ensure_leading_directories(ldir.buf);
@ -549,8 +557,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* shows only the link itself, not the contents of the link target.
* This loop replicates that behavior.
*/
hashmap_iter_init(&symlinks2, &iter);
while ((entry = hashmap_iter_next(&iter))) {
hashmap_for_each_entry(&symlinks2, &iter, entry,
entry /* member name */) {
if (*entry->left) {
add_path(&ldir, ldir_len, entry->path);
ensure_leading_directories(ldir.buf);

View file

@ -40,6 +40,7 @@ static int no_data;
static int full_tree;
static int reference_excluded_commits;
static int show_original_ids;
static int mark_tags;
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
static struct refspec refspecs = REFSPEC_INIT_FETCH;
@ -126,10 +127,15 @@ struct anonymized_entry {
};
static int anonymized_entry_cmp(const void *unused_cmp_data,
const void *va, const void *vb,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
const struct anonymized_entry *a = va, *b = vb;
const struct anonymized_entry *a, *b;
a = container_of(eptr, const struct anonymized_entry, hash);
b = container_of(entry_or_key, const struct anonymized_entry, hash);
return a->orig_len != b->orig_len ||
memcmp(a->orig, b->orig, a->orig_len);
}
@ -148,10 +154,10 @@ static const void *anonymize_mem(struct hashmap *map,
if (!map->cmpfn)
hashmap_init(map, anonymized_entry_cmp, NULL, 0);
hashmap_entry_init(&key, memhash(orig, *len));
hashmap_entry_init(&key.hash, memhash(orig, *len));
key.orig = orig;
key.orig_len = *len;
ret = hashmap_get(map, &key, NULL);
ret = hashmap_get_entry(map, &key, hash, NULL);
if (!ret) {
ret = xmalloc(sizeof(*ret));
@ -160,7 +166,7 @@ static const void *anonymize_mem(struct hashmap *map,
ret->orig_len = *len;
ret->anon = generate(orig, len);
ret->anon_len = *len;
hashmap_put(map, ret);
hashmap_put(map, &ret->hash);
}
*len = ret->anon_len;
@ -287,7 +293,8 @@ static void export_blob(const struct object_id *oid)
buf = read_object_file(oid, &type, &size);
if (!buf)
die("could not read blob %s", oid_to_hex(oid));
if (check_object_signature(oid, buf, size, type_name(type)) < 0)
if (check_object_signature(the_repository, oid, buf, size,
type_name(type)) < 0)
die("oid mismatch in blob %s", oid_to_hex(oid));
object = parse_object_buffer(the_repository, oid, type,
size, buf, &eaten);
@ -842,25 +849,39 @@ static void handle_tag(const char *name, struct tag *tag)
free(buf);
return;
case REWRITE:
if (tagged->type != OBJ_COMMIT) {
die("tag %s tags unexported %s!",
oid_to_hex(&tag->object.oid),
type_name(tagged->type));
if (tagged->type == OBJ_TAG && !mark_tags) {
die(_("Error: Cannot export nested tags unless --mark-tags is specified."));
} else if (tagged->type == OBJ_COMMIT) {
p = rewrite_commit((struct commit *)tagged);
if (!p) {
printf("reset %s\nfrom %s\n\n",
name, oid_to_hex(&null_oid));
free(buf);
return;
}
tagged_mark = get_object_mark(&p->object);
} else {
/* tagged->type is either OBJ_BLOB or OBJ_TAG */
tagged_mark = get_object_mark(tagged);
}
p = rewrite_commit((struct commit *)tagged);
if (!p) {
printf("reset %s\nfrom %s\n\n",
name, oid_to_hex(&null_oid));
free(buf);
return;
}
tagged_mark = get_object_mark(&p->object);
}
}
if (starts_with(name, "refs/tags/"))
name += 10;
printf("tag %s\nfrom :%d\n", name, tagged_mark);
if (tagged->type == OBJ_TAG) {
printf("reset %s\nfrom %s\n\n",
name, oid_to_hex(&null_oid));
}
skip_prefix(name, "refs/tags/", &name);
printf("tag %s\n", name);
if (mark_tags) {
mark_next_object(&tag->object);
printf("mark :%"PRIu32"\n", last_idnum);
}
if (tagged_mark)
printf("from :%d\n", tagged_mark);
else
printf("from %s\n", oid_to_hex(&tagged->oid));
if (show_original_ids)
printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
printf("%.*s%sdata %d\n%.*s\n",
@ -1047,11 +1068,16 @@ static void export_marks(char *file)
error("Unable to write marks file %s.", file);
}
static void import_marks(char *input_file)
static void import_marks(char *input_file, int check_exists)
{
char line[512];
FILE *f = xfopen(input_file, "r");
FILE *f;
struct stat sb;
if (check_exists && stat(input_file, &sb))
return;
f = xfopen(input_file, "r");
while (fgets(line, sizeof(line), f)) {
uint32_t mark;
char *line_end, *mark_end;
@ -1115,7 +1141,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
struct rev_info revs;
struct object_array commits = OBJECT_ARRAY_INIT;
struct commit *commit;
char *export_filename = NULL, *import_filename = NULL;
char *export_filename = NULL,
*import_filename = NULL,
*import_filename_if_exists = NULL;
uint32_t lastimportid;
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
@ -1135,6 +1163,10 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
N_("Dump marks to this file")),
OPT_STRING(0, "import-marks", &import_filename, N_("file"),
N_("Import marks from this file")),
OPT_STRING(0, "import-marks-if-exists",
&import_filename_if_exists,
N_("file"),
N_("Import marks from this file if it exists")),
OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
N_("Fake a tagger when tags lack one")),
OPT_BOOL(0, "full-tree", &full_tree,
@ -1149,6 +1181,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
&reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
OPT_BOOL(0, "show-original-ids", &show_original_ids,
N_("Show original object ids of blobs/commits")),
OPT_BOOL(0, "mark-tags", &mark_tags,
N_("Label tags with mark ids")),
OPT_END()
};
@ -1182,8 +1216,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (use_done_feature)
printf("feature done\n");
if (import_filename && import_filename_if_exists)
die(_("Cannot pass both --import-marks and --import-marks-if-exists"));
if (import_filename)
import_marks(import_filename);
import_marks(import_filename, 0);
else if (import_filename_if_exists)
import_marks(import_filename_if_exists, 1);
lastimportid = last_idnum;
if (import_filename && revs.prune_data.nr)

View file

@ -7,6 +7,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-store.h"
#include "oidset.h"
#include "commit.h"
#include "builtin.h"
#include "string-list.h"
@ -23,6 +24,9 @@
#include "packfile.h"
#include "list-objects-filter-options.h"
#include "commit-reach.h"
#include "branch.h"
#include "promisor-remote.h"
#include "commit-graph.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@ -50,11 +54,13 @@ static int fetch_prune_tags_config = -1; /* unspecified */
static int prune_tags = -1; /* unspecified */
#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
static int all, append, dry_run, force, keep, multiple, update_head_ok;
static int verbosity, deepen_relative, set_upstream;
static int progress = -1;
static int enable_auto_gc = 1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
static int max_children = 1;
static int max_jobs = -1, submodule_fetch_jobs_config = -1;
static int fetch_parallel_config = 1;
static enum transport_family family;
static const char *depth;
static const char *deepen_since;
@ -71,6 +77,7 @@ static struct refspec refmap = REFSPEC_INIT_FETCH;
static struct list_objects_filter_options filter_options;
static struct string_list server_options = STRING_LIST_INIT_DUP;
static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
static int fetch_write_commit_graph = -1;
static int git_fetch_config(const char *k, const char *v, void *cb)
{
@ -96,13 +103,20 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
}
if (!strcmp(k, "submodule.fetchjobs")) {
max_children = parse_submodule_fetchjobs(k, v);
submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
return 0;
} else if (!strcmp(k, "fetch.recursesubmodules")) {
recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
return 0;
}
if (!strcmp(k, "fetch.parallel")) {
fetch_parallel_config = git_config_int(k, v);
if (fetch_parallel_config < 0)
die(_("fetch.parallel cannot be negative"));
return 0;
}
return git_default_config(k, v, cb);
}
@ -123,6 +137,8 @@ static struct option builtin_fetch_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "all", &all,
N_("fetch from all remotes")),
OPT_BOOL(0, "set-upstream", &set_upstream,
N_("set upstream for git pull/fetch")),
OPT_BOOL('a', "append", &append,
N_("append to .git/FETCH_HEAD instead of overwriting")),
OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
@ -134,7 +150,7 @@ static struct option builtin_fetch_options[] = {
N_("fetch all tags and associated objects"), TAGS_SET),
OPT_SET_INT('n', NULL, &tags,
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
OPT_INTEGER('j', "jobs", &max_children,
OPT_INTEGER('j', "jobs", &max_jobs,
N_("number of submodules fetched in parallel")),
OPT_BOOL('p', "prune", &prune,
N_("prune remote-tracking branches no longer on remote")),
@ -183,6 +199,8 @@ static struct option builtin_fetch_options[] = {
N_("run 'gc --auto' after fetching")),
OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
N_("check for forced-updates on all updated branches")),
OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
N_("write the commit-graph after fetching")),
OPT_END()
};
@ -239,32 +257,31 @@ static void add_merge_config(struct ref **head,
}
}
static int will_fetch(struct ref **head, const unsigned char *sha1)
static void create_fetch_oidset(struct ref **head, struct oidset *out)
{
struct ref *rm = *head;
while (rm) {
if (hasheq(rm->old_oid.hash, sha1))
return 1;
oidset_insert(out, &rm->old_oid);
rm = rm->next;
}
return 0;
}
struct refname_hash_entry {
struct hashmap_entry ent; /* must be the first member */
struct hashmap_entry ent;
struct object_id oid;
int ignore;
char refname[FLEX_ARRAY];
};
static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,
const void *e1_,
const void *e2_,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
const void *keydata)
{
const struct refname_hash_entry *e1 = e1_;
const struct refname_hash_entry *e2 = e2_;
const struct refname_hash_entry *e1, *e2;
e1 = container_of(eptr, const struct refname_hash_entry, ent);
e2 = container_of(entry_or_key, const struct refname_hash_entry, ent);
return strcmp(e1->refname, keydata ? keydata : e2->refname);
}
@ -276,9 +293,9 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
size_t len = strlen(refname);
FLEX_ALLOC_MEM(ent, refname, refname, len);
hashmap_entry_init(ent, strhash(refname));
hashmap_entry_init(&ent->ent, strhash(refname));
oidcpy(&ent->oid, oid);
hashmap_add(map, ent);
hashmap_add(map, &ent->ent);
return ent;
}
@ -313,13 +330,16 @@ static void find_non_local_tags(const struct ref *refs,
{
struct hashmap existing_refs;
struct hashmap remote_refs;
struct oidset fetch_oids = OIDSET_INIT;
struct string_list remote_refs_list = STRING_LIST_INIT_NODUP;
struct string_list_item *remote_ref_item;
const struct ref *ref;
struct refname_hash_entry *item = NULL;
const int quick_flags = OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT;
refname_hash_init(&existing_refs);
refname_hash_init(&remote_refs);
create_fetch_oidset(head, &fetch_oids);
for_each_ref(add_one_refname, &existing_refs);
for (ref = refs; ref; ref = ref->next) {
@ -334,11 +354,10 @@ static void find_non_local_tags(const struct ref *refs,
*/
if (ends_with(ref->name, "^{}")) {
if (item &&
!has_object_file_with_flags(&ref->old_oid,
OBJECT_INFO_QUICK) &&
!will_fetch(head, ref->old_oid.hash) &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
!has_object_file_with_flags(&ref->old_oid, quick_flags) &&
!oidset_contains(&fetch_oids, &ref->old_oid) &&
!has_object_file_with_flags(&item->oid, quick_flags) &&
!oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
item = NULL;
continue;
@ -351,8 +370,8 @@ static void find_non_local_tags(const struct ref *refs,
* fetch.
*/
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
!has_object_file_with_flags(&item->oid, quick_flags) &&
!oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
item = NULL;
@ -365,15 +384,15 @@ static void find_non_local_tags(const struct ref *refs,
item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
string_list_insert(&remote_refs_list, ref->name);
}
hashmap_free(&existing_refs, 1);
hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
/*
* We may have a final lightweight tag that needs to be
* checked to see if it needs fetching.
*/
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
!has_object_file_with_flags(&item->oid, quick_flags) &&
!oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
/*
@ -383,8 +402,10 @@ static void find_non_local_tags(const struct ref *refs,
for_each_string_list_item(remote_ref_item, &remote_refs_list) {
const char *refname = remote_ref_item->string;
struct ref *rm;
unsigned int hash = strhash(refname);
item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
item = hashmap_get_entry_from_hash(&remote_refs, hash, refname,
struct refname_hash_entry, ent);
if (!item)
BUG("unseen remote ref?");
@ -398,8 +419,9 @@ static void find_non_local_tags(const struct ref *refs,
**tail = rm;
*tail = &rm->next;
}
hashmap_free(&remote_refs, 1);
hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);
string_list_clear(&remote_refs_list, 0);
oidset_clear(&fetch_oids);
}
static struct ref *get_ref_map(struct remote *remote,
@ -516,17 +538,18 @@ static struct ref *get_ref_map(struct remote *remote,
if (rm->peer_ref) {
const char *refname = rm->peer_ref->name;
struct refname_hash_entry *peer_item;
unsigned int hash = strhash(refname);
peer_item = hashmap_get_from_hash(&existing_refs,
strhash(refname),
refname);
peer_item = hashmap_get_entry_from_hash(&existing_refs,
hash, refname,
struct refname_hash_entry, ent);
if (peer_item) {
struct object_id *old_oid = &peer_item->oid;
oidcpy(&rm->peer_ref->old_oid, old_oid);
}
}
}
hashmap_free(&existing_refs, 1);
hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
return ref_map;
}
@ -883,8 +906,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
url = xstrdup("foreign");
if (!connectivity_checked) {
struct check_connected_options opt = CHECK_CONNECTED_INIT;
if (filter_options.choice)
/*
* Since a filter is specified, objects indirectly
* referenced by refs are allowed to be absent.
*/
opt.check_refs_are_promisor_objects_only = 1;
rm = ref_map;
if (check_connected(iterate_ref_map, &rm, NULL)) {
if (check_connected(iterate_ref_map, &rm, &opt)) {
rc = error(_("%s did not send all necessary objects\n"), url);
goto abort;
}
@ -934,18 +966,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
kind = "";
what = "";
}
else if (starts_with(rm->name, "refs/heads/")) {
else if (skip_prefix(rm->name, "refs/heads/", &what))
kind = "branch";
what = rm->name + 11;
}
else if (starts_with(rm->name, "refs/tags/")) {
else if (skip_prefix(rm->name, "refs/tags/", &what))
kind = "tag";
what = rm->name + 10;
}
else if (starts_with(rm->name, "refs/remotes/")) {
else if (skip_prefix(rm->name, "refs/remotes/", &what))
kind = "remote-tracking branch";
what = rm->name + 13;
}
else {
kind = "";
what = rm->name;
@ -1054,7 +1080,8 @@ static int check_exist_and_connected(struct ref *ref_map)
* we need all direct targets to exist.
*/
for (r = rm; r; r = r->next) {
if (!has_object_file(&r->old_oid))
if (!has_object_file_with_flags(&r->old_oid,
OBJECT_INFO_SKIP_FETCH_OBJECT))
return -1;
}
@ -1065,8 +1092,11 @@ static int check_exist_and_connected(struct ref *ref_map)
static int fetch_refs(struct transport *transport, struct ref *ref_map)
{
int ret = check_exist_and_connected(ref_map);
if (ret)
if (ret) {
trace2_region_enter("fetch", "fetch_refs", the_repository);
ret = transport_fetch_refs(transport, ref_map);
trace2_region_leave("fetch", "fetch_refs", the_repository);
}
if (!ret)
/*
* Keep the new pack's ".keep" file around to allow the caller
@ -1082,11 +1112,14 @@ static int consume_refs(struct transport *transport, struct ref *ref_map)
{
int connectivity_checked = transport->smart_options
? transport->smart_options->connectivity_checked : 0;
int ret = store_updated_refs(transport->url,
transport->remote->name,
connectivity_checked,
ref_map);
int ret;
trace2_region_enter("fetch", "consume_refs", the_repository);
ret = store_updated_refs(transport->url,
transport->remote->name,
connectivity_checked,
ref_map);
transport_unlock_pack(transport);
trace2_region_leave("fetch", "consume_refs", the_repository);
return ret;
}
@ -1238,13 +1271,10 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
if (update_shallow)
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
if (filter_options.choice) {
struct strbuf expanded_filter_spec = STRBUF_INIT;
expand_list_objects_filter_spec(&filter_options,
&expanded_filter_spec);
set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
expanded_filter_spec.buf);
const char *spec =
expand_list_objects_filter_spec(&filter_options);
set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec);
set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
strbuf_release(&expanded_filter_spec);
}
if (negotiation_tip.nr) {
if (transport->smart_options)
@ -1334,9 +1364,11 @@ static int do_fetch(struct transport *transport,
argv_array_push(&ref_prefixes, "refs/tags/");
}
if (must_list_refs)
if (must_list_refs) {
trace2_region_enter("fetch", "remote_refs", the_repository);
remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
else
trace2_region_leave("fetch", "remote_refs", the_repository);
} else
remote_refs = NULL;
argv_array_clear(&ref_prefixes);
@ -1367,6 +1399,51 @@ static int do_fetch(struct transport *transport,
retcode = 1;
goto cleanup;
}
if (set_upstream) {
struct branch *branch = branch_get("HEAD");
struct ref *rm;
struct ref *source_ref = NULL;
/*
* We're setting the upstream configuration for the
* current branch. The relevant upstream is the
* fetched branch that is meant to be merged with the
* current one, i.e. the one fetched to FETCH_HEAD.
*
* When there are several such branches, consider the
* request ambiguous and err on the safe side by doing
* nothing and just emit a warning.
*/
for (rm = ref_map; rm; rm = rm->next) {
if (!rm->peer_ref) {
if (source_ref) {
warning(_("multiple branches detected, incompatible with --set-upstream"));
goto skip;
} else {
source_ref = rm;
}
}
}
if (source_ref) {
if (!strcmp(source_ref->name, "HEAD") ||
starts_with(source_ref->name, "refs/heads/"))
install_branch_config(0,
branch->name,
transport->remote->name,
source_ref->name);
else if (starts_with(source_ref->name, "refs/remotes/"))
warning(_("not setting upstream for a remote remote-tracking branch"));
else if (starts_with(source_ref->name, "refs/tags/"))
warning(_("not setting upstream for a remote tag"));
else
warning(_("unknown branch type"));
} else {
warning(_("no source branch found.\n"
"you need to specify exactly one branch with the --set-upstream option."));
}
}
skip:
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@ -1463,7 +1540,62 @@ static void add_options_to_argv(struct argv_array *argv)
}
static int fetch_multiple(struct string_list *list)
/* Fetch multiple remotes in parallel */
struct parallel_fetch_state {
const char **argv;
struct string_list *remotes;
int next, result;
};
static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
void *cb, void **task_cb)
{
struct parallel_fetch_state *state = cb;
char *remote;
if (state->next < 0 || state->next >= state->remotes->nr)
return 0;
remote = state->remotes->items[state->next++].string;
*task_cb = remote;
argv_array_pushv(&cp->args, state->argv);
argv_array_push(&cp->args, remote);
cp->git_cmd = 1;
if (verbosity >= 0)
printf(_("Fetching %s\n"), remote);
return 1;
}
static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
{
struct parallel_fetch_state *state = cb;
const char *remote = task_cb;
state->result = error(_("Could not fetch %s"), remote);
return 0;
}
static int fetch_finished(int result, struct strbuf *out,
void *cb, void *task_cb)
{
struct parallel_fetch_state *state = cb;
const char *remote = task_cb;
if (result) {
strbuf_addf(out, _("could not fetch '%s' (exit code: %d)\n"),
remote, result);
state->result = -1;
}
return 0;
}
static int fetch_multiple(struct string_list *list, int max_children)
{
int i, result = 0;
struct argv_array argv = ARGV_ARRAY_INIT;
@ -1474,23 +1606,38 @@ static int fetch_multiple(struct string_list *list)
return errcode;
}
argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc",
"--no-write-commit-graph", NULL);
add_options_to_argv(&argv);
for (i = 0; i < list->nr; i++) {
const char *name = list->items[i].string;
argv_array_push(&argv, name);
if (verbosity >= 0)
printf(_("Fetching %s\n"), name);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
error(_("Could not fetch %s"), name);
result = 1;
if (max_children != 1 && list->nr != 1) {
struct parallel_fetch_state state = { argv.argv, list, 0, 0 };
argv_array_push(&argv, "--end-of-options");
result = run_processes_parallel_tr2(max_children,
&fetch_next_remote,
&fetch_failed_to_start,
&fetch_finished,
&state,
"fetch", "parallel/fetch");
if (!result)
result = state.result;
} else
for (i = 0; i < list->nr; i++) {
const char *name = list->items[i].string;
argv_array_push(&argv, name);
if (verbosity >= 0)
printf(_("Fetching %s\n"), name);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
error(_("Could not fetch %s"), name);
result = 1;
}
argv_array_pop(&argv);
}
argv_array_pop(&argv);
}
argv_array_clear(&argv);
return result;
return !!result;
}
/*
@ -1510,37 +1657,27 @@ static inline void fetch_one_setup_partial(struct remote *remote)
* If no prior partial clone/fetch and the current fetch DID NOT
* request a partial-fetch, do a normal fetch.
*/
if (!repository_format_partial_clone && !filter_options.choice)
if (!has_promisor_remote() && !filter_options.choice)
return;
/*
* If this is the FIRST partial-fetch request, we enable partial
* on this repo and remember the given filter-spec as the default
* for subsequent fetches to this remote.
* If this is a partial-fetch request, we enable partial on
* this repo if not already enabled and remember the given
* filter-spec as the default for subsequent fetches to this
* remote.
*/
if (!repository_format_partial_clone && filter_options.choice) {
if (filter_options.choice) {
partial_clone_register(remote->name, &filter_options);
return;
}
/*
* We are currently limited to only ONE promisor remote and only
* allow partial-fetches from the promisor remote.
*/
if (strcmp(remote->name, repository_format_partial_clone)) {
if (filter_options.choice)
die(_("--filter can only be used with the remote "
"configured in extensions.partialClone"));
return;
}
/*
* Do a partial-fetch from the promisor remote using either the
* explicitly given filter-spec or inherit the filter-spec from
* the config.
*/
if (!filter_options.choice)
partial_clone_get_default_filter_spec(&filter_options);
partial_clone_get_default_filter_spec(&filter_options, remote->name);
return;
}
@ -1626,14 +1763,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
packet_trace_identity("fetch");
fetch_if_missing = 0;
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
fetch_config_from_gitmodules(&max_children, &recurse_submodules);
fetch_config_from_gitmodules(&submodule_fetch_jobs_config,
&recurse_submodules);
git_config(git_fetch_config, NULL);
argc = parse_options(argc, argv, prefix,
@ -1661,7 +1797,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth || deepen_since || deepen_not.nr)
deepen = 1;
if (filter_options.choice && !repository_format_partial_clone)
if (filter_options.choice && !has_promisor_remote())
die("--filter can only be used when extensions.partialClone is set");
if (all) {
@ -1695,19 +1831,31 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
if (remote) {
if (filter_options.choice || repository_format_partial_clone)
if (filter_options.choice || has_promisor_remote())
fetch_one_setup_partial(remote);
result = fetch_one(remote, argc, argv, prune_tags_ok);
} else {
int max_children = max_jobs;
if (filter_options.choice)
die(_("--filter can only be used with the remote "
"configured in extensions.partialclone"));
if (max_children < 0)
max_children = fetch_parallel_config;
/* TODO should this also die if we have a previous partial-clone? */
result = fetch_multiple(&list);
result = fetch_multiple(&list, max_children);
}
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
struct argv_array options = ARGV_ARRAY_INIT;
int max_children = max_jobs;
if (max_children < 0)
max_children = submodule_fetch_jobs_config;
if (max_children < 0)
max_children = fetch_parallel_config;
add_options_to_argv(&options);
result = fetch_populated_submodules(the_repository,
@ -1722,6 +1870,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
string_list_clear(&list, 0);
prepare_repo_settings(the_repository);
if (fetch_write_commit_graph > 0 ||
(fetch_write_commit_graph < 0 &&
the_repository->settings.fetch_write_commit_graph)) {
int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
if (progress)
commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
write_commit_graph_reachable(the_repository->objects->odb,
commit_graph_flags,
NULL);
}
close_object_store(the_repository->objects);
if (enable_auto_gc) {

View file

@ -106,7 +106,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
int i, len = strlen(line);
struct origin_data *origin_data;
char *src;
const char *origin;
const char *origin, *tag_name;
struct src_data *src_data;
struct string_list_item *item;
int pulling_head = 0;
@ -162,14 +162,13 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
if (pulling_head) {
origin = src;
src_data->head_status |= 1;
} else if (starts_with(line, "branch ")) {
} else if (skip_prefix(line, "branch ", &origin)) {
origin_data->is_local_branch = 1;
origin = line + 7;
string_list_append(&src_data->branch, origin);
src_data->head_status |= 2;
} else if (starts_with(line, "tag ")) {
} else if (skip_prefix(line, "tag ", &tag_name)) {
origin = line;
string_list_append(&src_data->tag, origin + 4);
string_list_append(&src_data->tag, tag_name);
src_data->head_status |= 2;
} else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
string_list_append(&src_data->r_branch, origin);

View file

@ -50,40 +50,20 @@ static int name_objects;
#define ERROR_REFS 010
#define ERROR_COMMIT_GRAPH 020
static const char *describe_object(struct object *obj)
static const char *describe_object(const struct object_id *oid)
{
static struct strbuf bufs[] = {
STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
};
static int b = 0;
struct strbuf *buf;
char *name = NULL;
if (name_objects)
name = lookup_decoration(fsck_walk_options.object_names, obj);
buf = bufs + b;
b = (b + 1) % ARRAY_SIZE(bufs);
strbuf_reset(buf);
strbuf_addstr(buf, oid_to_hex(&obj->oid));
if (name)
strbuf_addf(buf, " (%s)", name);
return buf->buf;
return fsck_describe_object(&fsck_walk_options, oid);
}
static const char *printable_type(struct object *obj)
static const char *printable_type(const struct object_id *oid,
enum object_type type)
{
const char *ret;
if (obj->type == OBJ_NONE) {
enum object_type type = oid_object_info(the_repository,
&obj->oid, NULL);
if (type > 0)
object_as_type(the_repository, obj, type, 0);
}
if (type == OBJ_NONE)
type = oid_object_info(the_repository, oid, NULL);
ret = type_name(obj->type);
ret = type_name(type);
if (!ret)
ret = _("unknown");
@ -118,26 +98,32 @@ static int objerror(struct object *obj, const char *err)
errors_found |= ERROR_OBJECT;
/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
fprintf_ln(stderr, _("error in %s %s: %s"),
printable_type(obj), describe_object(obj), err);
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid), err);
return -1;
}
static int fsck_error_func(struct fsck_options *o,
struct object *obj, int type, const char *message)
const struct object_id *oid,
enum object_type object_type,
int msg_type, const char *message)
{
switch (type) {
switch (msg_type) {
case FSCK_WARN:
/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
fprintf_ln(stderr, _("warning in %s %s: %s"),
printable_type(obj), describe_object(obj), message);
printable_type(oid, object_type),
describe_object(oid), message);
return 0;
case FSCK_ERROR:
/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
fprintf_ln(stderr, _("error in %s %s: %s"),
printable_type(obj), describe_object(obj), message);
printable_type(oid, object_type),
describe_object(oid), message);
return 1;
default:
BUG("%d (FSCK_IGNORE?) should never trigger this callback", type);
BUG("%d (FSCK_IGNORE?) should never trigger this callback",
msg_type);
}
}
@ -155,7 +141,8 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (!obj) {
/* ... these references to parent->fld are safe here */
printf_ln(_("broken link from %7s %s"),
printable_type(parent), describe_object(parent));
printable_type(&parent->oid, parent->type),
describe_object(&parent->oid));
printf_ln(_("broken link from %7s %s"),
(type == OBJ_ANY ? _("unknown") : type_name(type)),
_("unknown"));
@ -183,10 +170,10 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (parent && !has_object_file(&obj->oid)) {
printf_ln(_("broken link from %7s %s\n"
" to %7s %s"),
printable_type(parent),
describe_object(parent),
printable_type(obj),
describe_object(obj));
printable_type(&parent->oid, parent->type),
describe_object(&parent->oid),
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid));
errors_found |= ERROR_REACHABLE;
}
return 1;
@ -292,8 +279,9 @@ static void check_reachable_object(struct object *obj)
return;
if (has_object_pack(&obj->oid))
return; /* it is in pack - forget about it */
printf_ln(_("missing %s %s"), printable_type(obj),
describe_object(obj));
printf_ln(_("missing %s %s"),
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid));
errors_found |= ERROR_REACHABLE;
return;
}
@ -318,8 +306,9 @@ static void check_unreachable_object(struct object *obj)
* since this is something that is prunable.
*/
if (show_unreachable) {
printf_ln(_("unreachable %s %s"), printable_type(obj),
describe_object(obj));
printf_ln(_("unreachable %s %s"),
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid));
return;
}
@ -337,12 +326,13 @@ static void check_unreachable_object(struct object *obj)
*/
if (!(obj->flags & USED)) {
if (show_dangling)
printf_ln(_("dangling %s %s"), printable_type(obj),
describe_object(obj));
printf_ln(_("dangling %s %s"),
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid));
if (write_lost_and_found) {
char *filename = git_pathdup("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
describe_object(obj));
describe_object(&obj->oid));
FILE *f;
if (safe_create_leading_directories_const(filename)) {
@ -355,7 +345,7 @@ static void check_unreachable_object(struct object *obj)
if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
die_errno(_("could not write '%s'"), filename);
} else
fprintf(f, "%s\n", describe_object(obj));
fprintf(f, "%s\n", describe_object(&obj->oid));
if (fclose(f))
die_errno(_("could not finish '%s'"),
filename);
@ -374,7 +364,7 @@ static void check_unreachable_object(struct object *obj)
static void check_object(struct object *obj)
{
if (verbose)
fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid));
if (obj->flags & REACHABLE)
check_reachable_object(obj);
@ -432,7 +422,8 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
if (verbose)
fprintf_ln(stderr, _("Checking %s %s"),
printable_type(obj), describe_object(obj));
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid));
if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, _("broken links"));
@ -445,7 +436,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
if (!commit->parents && show_root)
printf_ln(_("root %s"),
describe_object(&commit->object));
describe_object(&commit->object.oid));
}
if (obj->type == OBJ_TAG) {
@ -453,10 +444,10 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
if (show_tags && tag->tagged) {
printf_ln(_("tagged %s %s (%s) in %s"),
printable_type(tag->tagged),
describe_object(tag->tagged),
printable_type(&tag->tagged->oid, tag->tagged->type),
describe_object(&tag->tagged->oid),
tag->tag,
describe_object(&tag->object));
describe_object(&tag->object.oid));
}
}
@ -499,10 +490,10 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
if (!is_null_oid(oid)) {
obj = lookup_object(the_repository, oid);
if (obj && (obj->flags & HAS_OBJ)) {
if (timestamp && name_objects)
add_decoration(fsck_walk_options.object_names,
obj,
xstrfmt("%s@{%"PRItime"}", refname, timestamp));
if (timestamp)
fsck_put_object_name(&fsck_walk_options, oid,
"%s@{%"PRItime"}",
refname, timestamp);
obj->flags |= USED;
mark_object_reachable(obj);
} else if (!is_promisor_object(oid)) {
@ -566,9 +557,8 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
}
default_refs++;
obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(refname));
fsck_put_object_name(&fsck_walk_options,
oid, "%s", refname);
mark_object_reachable(obj);
return 0;
@ -742,9 +732,7 @@ static int fsck_cache_tree(struct cache_tree *it)
return 1;
}
obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(":"));
fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
mark_object_reachable(obj);
if (obj->type != OBJ_TREE)
err |= objerror(obj, _("non-tree in cache-tree"));
@ -830,8 +818,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
if (name_objects)
fsck_walk_options.object_names =
xcalloc(1, sizeof(struct decoration));
fsck_enable_object_names(&fsck_walk_options);
git_config(fsck_config, NULL);
@ -890,9 +877,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(arg));
fsck_put_object_name(&fsck_walk_options, &oid,
"%s", arg);
mark_object_reachable(obj);
continue;
}
@ -928,10 +914,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
continue;
obj = &blob->object;
obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj,
xstrfmt(":%s", active_cache[i]->name));
fsck_put_object_name(&fsck_walk_options, &obj->oid,
":%s", active_cache[i]->name);
mark_object_reachable(obj);
}
if (active_cache_tree)

View file

@ -27,6 +27,7 @@
#include "pack-objects.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
#define FAILED_RUN "failed to run %s"
@ -41,7 +42,6 @@ static int aggressive_depth = 50;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static int gc_write_commit_graph;
static int detach_auto = 1;
static timestamp_t gc_log_expire_time;
static const char *gc_log_expire = "1.day.ago";
@ -148,7 +148,6 @@ static void gc_config(void)
git_config_get_int("gc.aggressivedepth", &aggressive_depth);
git_config_get_int("gc.auto", &gc_auto_threshold);
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph);
git_config_get_bool("gc.autodetach", &detach_auto);
git_config_get_expiry("gc.pruneexpire", &prune_expire);
git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
@ -459,7 +458,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
/*
* Returns 0 if there was no previous error and gc can proceed, 1 if
* gc should not proceed due to an error in the last run. Prints a
* message and returns -1 if an error occured while reading gc.log
* message and returns -1 if an error occurred while reading gc.log
*/
static int report_last_gc_error(void)
{
@ -602,7 +601,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (detach_auto) {
int ret = report_last_gc_error();
if (ret < 0)
/* an I/O error occured, already reported */
/* an I/O error occurred, already reported */
exit(128);
if (ret == 1)
/* Last gc --auto failed. Skip this one. */
@ -661,7 +660,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_push(&prune, prune_expire);
if (quiet)
argv_array_push(&prune, "--no-progress");
if (repository_format_partial_clone)
if (has_promisor_remote())
argv_array_push(&prune,
"--exclude-promisor-objects");
if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
@ -685,11 +684,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
clean_pack_garbage();
}
if (gc_write_commit_graph &&
write_commit_graph_reachable(get_object_directory(),
!quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0,
NULL))
return 1;
prepare_repo_settings(the_repository);
if (the_repository->settings.gc_write_commit_graph == 1)
write_commit_graph_reachable(the_repository->objects->odb,
!quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
NULL);
if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "

View file

@ -24,6 +24,7 @@
#include "submodule.h"
#include "submodule-config.h"
#include "object-store.h"
#include "packfile.h"
static char const * const grep_usage[] = {
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
@ -32,7 +33,6 @@ static char const * const grep_usage[] = {
static int recurse_submodules;
#define GREP_NUM_THREADS_DEFAULT 8
static int num_threads;
static pthread_t *threads;
@ -91,8 +91,11 @@ static pthread_cond_t cond_result;
static int skip_first_line;
static void add_work(struct grep_opt *opt, const struct grep_source *gs)
static void add_work(struct grep_opt *opt, struct grep_source *gs)
{
if (opt->binary != GREP_BINARY_TEXT)
grep_source_load_driver(gs, opt->repo->index);
grep_lock();
while ((todo_end+1) % ARRAY_SIZE(todo) == todo_done) {
@ -100,9 +103,6 @@ static void add_work(struct grep_opt *opt, const struct grep_source *gs)
}
todo[todo_end].source = *gs;
if (opt->binary != GREP_BINARY_TEXT)
grep_source_load_driver(&todo[todo_end].source,
opt->repo->index);
todo[todo_end].done = 0;
strbuf_reset(&todo[todo_end].out);
todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
@ -200,12 +200,12 @@ static void start_threads(struct grep_opt *opt)
int i;
pthread_mutex_init(&grep_mutex, NULL);
pthread_mutex_init(&grep_read_mutex, NULL);
pthread_mutex_init(&grep_attr_mutex, NULL);
pthread_cond_init(&cond_add, NULL);
pthread_cond_init(&cond_write, NULL);
pthread_cond_init(&cond_result, NULL);
grep_use_locks = 1;
enable_obj_read_lock();
for (i = 0; i < ARRAY_SIZE(todo); i++) {
strbuf_init(&todo[i].out, 0);
@ -257,12 +257,12 @@ static int wait_all(void)
free(threads);
pthread_mutex_destroy(&grep_mutex);
pthread_mutex_destroy(&grep_read_mutex);
pthread_mutex_destroy(&grep_attr_mutex);
pthread_cond_destroy(&cond_add);
pthread_cond_destroy(&cond_write);
pthread_cond_destroy(&cond_result);
grep_use_locks = 0;
disable_obj_read_lock();
return hit;
}
@ -295,16 +295,6 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)
return st;
}
static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
{
void *data;
grep_read_lock();
data = read_object_file(oid, type, size);
grep_read_unlock();
return data;
}
static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
const char *filename, int tree_name_len,
const char *path)
@ -403,34 +393,32 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
static int grep_submodule(struct grep_opt *opt,
const struct pathspec *pathspec,
const struct object_id *oid,
const char *filename, const char *path)
const char *filename, const char *path, int cached)
{
struct repository subrepo;
struct repository *superproject = opt->repo;
const struct submodule *sub = submodule_from_path(superproject,
&null_oid, path);
const struct submodule *sub;
struct grep_opt subopt;
int hit;
sub = submodule_from_path(superproject, &null_oid, path);
if (!is_submodule_active(superproject, path))
return 0;
if (repo_submodule_init(&subrepo, superproject, sub))
return 0;
/*
* NEEDSWORK: submodules functions need to be protected because they
* access the object store via config_from_gitmodules(): the latter
* uses get_oid() which, for now, relies on the global the_repository
* object.
* NEEDSWORK: repo_read_gitmodules() might call
* add_to_alternates_memory() via config_from_gitmodules(). This
* operation causes a race condition with concurrent object readings
* performed by the worker threads. That's why we need obj_read_lock()
* here. It should be removed once it's no longer necessary to add the
* subrepo's odbs to the in-memory alternates list.
*/
grep_read_lock();
if (!is_submodule_active(superproject, path)) {
grep_read_unlock();
return 0;
}
if (repo_submodule_init(&subrepo, superproject, sub)) {
grep_read_unlock();
return 0;
}
repo_read_gitmodules(&subrepo);
obj_read_lock();
repo_read_gitmodules(&subrepo, 0);
/*
* NEEDSWORK: This adds the submodule's object directory to the list of
@ -443,7 +431,7 @@ static int grep_submodule(struct grep_opt *opt,
* object.
*/
add_to_alternates_memory(subrepo.objects->odb->path);
grep_read_unlock();
obj_read_unlock();
memcpy(&subopt, opt, sizeof(subopt));
subopt.repo = &subrepo;
@ -455,14 +443,12 @@ static int grep_submodule(struct grep_opt *opt,
unsigned long size;
struct strbuf base = STRBUF_INIT;
obj_read_lock();
object = parse_object_or_die(oid, oid_to_hex(oid));
grep_read_lock();
obj_read_unlock();
data = read_object_with_reference(&subrepo,
&object->oid, tree_type,
&size, NULL);
grep_read_unlock();
if (!data)
die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
@ -475,7 +461,7 @@ static int grep_submodule(struct grep_opt *opt,
strbuf_release(&base);
free(data);
} else {
hit = grep_cache(&subopt, pathspec, 1);
hit = grep_cache(&subopt, pathspec, cached);
}
repo_clear(&subrepo);
@ -523,7 +509,8 @@ static int grep_cache(struct grep_opt *opt,
}
} else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
submodule_path_match(repo->index, pathspec, name.buf, NULL)) {
hit |= grep_submodule(opt, pathspec, NULL, ce->name, ce->name);
hit |= grep_submodule(opt, pathspec, NULL, ce->name,
ce->name, cached);
} else {
continue;
}
@ -586,7 +573,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
void *data;
unsigned long size;
data = lock_and_read_oid_file(&entry.oid, &type, &size);
data = read_object_file(&entry.oid, &type, &size);
if (!data)
die(_("unable to read tree (%s)"),
oid_to_hex(&entry.oid));
@ -598,7 +585,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
free(data);
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
hit |= grep_submodule(opt, pathspec, &entry.oid,
base->buf, base->buf + tn_len);
base->buf, base->buf + tn_len,
1); /* ignored */
}
strbuf_setlen(base, old_baselen);
@ -623,12 +611,9 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
struct strbuf base;
int hit, len;
grep_read_lock();
data = read_object_with_reference(opt->repo,
&obj->oid, tree_type,
&size, NULL);
grep_read_unlock();
if (!data)
die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
@ -657,13 +642,18 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
for (i = 0; i < nr; i++) {
struct object *real_obj;
obj_read_lock();
real_obj = deref_tag(opt->repo, list->objects[i].item,
NULL, 0);
obj_read_unlock();
/* load the gitmodules file for this rev */
if (recurse_submodules) {
submodule_free(opt->repo);
obj_read_lock();
gitmodules_config_oid(&real_obj->oid);
obj_read_unlock();
}
if (grep_object(opt, pathspec, real_obj, list->objects[i].name,
list->objects[i].path)) {
@ -956,6 +946,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
/* die the same way as if we did it at the beginning */
setup_git_directory();
}
/* Ignore --recurse-submodules if --no-index is given or implied */
if (!use_index)
recurse_submodules = 0;
/*
* skip a -- separator; we know it cannot be
@ -1060,7 +1053,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
pathspec.recursive = 1;
pathspec.recurse_submodules = !!recurse_submodules;
if (list.nr || cached || show_in_pager) {
if (recurse_submodules && untracked)
die(_("--untracked not supported with --recurse-submodules"));
if (show_in_pager) {
if (num_threads > 1)
warning(_("invalid option combination, ignoring --threads"));
num_threads = 1;
@ -1070,7 +1066,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
} else if (num_threads < 0)
die(_("invalid number of threads specified (%d)"), num_threads);
else if (num_threads == 0)
num_threads = HAVE_THREADS ? GREP_NUM_THREADS_DEFAULT : 1;
num_threads = HAVE_THREADS ? online_cpus() : 1;
if (num_threads > 1) {
if (!HAVE_THREADS)
@ -1079,6 +1075,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
&& (opt.pre_context || opt.post_context ||
opt.file_break || opt.funcbody))
skip_first_line = 1;
/*
* Pre-read gitmodules (if not read already) and force eager
* initialization of packed_git to prevent racy lazy
* reading/initialization once worker threads are started.
*/
if (recurse_submodules)
repo_read_gitmodules(the_repository, 1);
if (startup_info->have_repository)
(void)get_packed_git(the_repository);
start_threads(&opt);
} else {
/*
@ -1108,14 +1115,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
strbuf_addf(&buf, "+/%s%s",
strcmp("less", pager) ? "" : "*",
opt.pattern_list->pattern);
string_list_append(&path_list, buf.buf);
strbuf_detach(&buf, NULL);
string_list_append(&path_list,
strbuf_detach(&buf, NULL));
}
}
if (recurse_submodules && (!use_index || untracked))
die(_("option not supported with --recurse-submodules"));
if (!show_in_pager && !opt.status_only)
setup_pager();
@ -1145,5 +1149,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
run_pager(&opt, prefix);
clear_pathspec(&pathspec);
free_grep_patterns(&opt);
grep_destroy();
return !hit;
}

View file

@ -14,7 +14,7 @@
#include "thread-utils.h"
#include "packfile.h"
#include "object-store.h"
#include "fetch-object.h"
#include "promisor-remote.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@ -757,7 +757,8 @@ static int check_collison(struct object_entry *entry)
memset(&data, 0, sizeof(data));
data.entry = entry;
data.st = open_istream(&entry->idx.oid, &type, &size, NULL);
data.st = open_istream(the_repository, &entry->idx.oid, &type, &size,
NULL);
if (!data.st)
return -1;
if (size != entry->size || type != entry->type)
@ -948,7 +949,7 @@ static void resolve_delta(struct object_entry *delta_obj,
free(delta_data);
if (!result->data)
bad_object(delta_obj->idx.offset, _("failed to apply delta"));
hash_object_file(result->data, result->size,
hash_object_file(the_hash_algo, result->data, result->size,
type_name(delta_obj->real_type), &delta_obj->idx.oid);
sha1_object(result->data, NULL, result->size, delta_obj->real_type,
&delta_obj->idx.oid);
@ -1003,7 +1004,9 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base,
if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
base->obj->real_type))
BUG("child->real_type != OBJ_REF_DELTA");
die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)",
(uintmax_t)child->idx.offset,
oid_to_hex(&base->obj->idx.oid));
resolve_delta(child, base, result);
if (base->ref_first == base->ref_last && base->ofs_last == -1)
@ -1352,7 +1355,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
sorted_by_pos[i] = &ref_deltas[i];
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
if (repository_format_partial_clone) {
if (has_promisor_remote()) {
/*
* Prefetch the delta bases.
*/
@ -1366,8 +1369,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
oid_array_append(&to_fetch, &d->oid);
}
if (to_fetch.nr)
fetch_objects(repository_format_partial_clone,
to_fetch.oid, to_fetch.nr);
promisor_remote_get_direct(the_repository,
to_fetch.oid, to_fetch.nr);
oid_array_clear(&to_fetch);
}
@ -1383,8 +1386,9 @@ static void fix_unresolved_deltas(struct hashfile *f)
if (!base_obj->data)
continue;
if (check_object_signature(&d->oid, base_obj->data,
base_obj->size, type_name(type)))
if (check_object_signature(the_repository, &d->oid,
base_obj->data, base_obj->size,
type_name(type)))
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
base_obj->obj = append_obj_to_pack(f, d->oid.hash,
base_obj->data, base_obj->size, type);
@ -1490,11 +1494,11 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
}
if (!from_stdin) {
printf("%s\n", sha1_to_hex(hash));
printf("%s\n", hash_to_hex(hash));
} else {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(hash));
strbuf_addf(&buf, "%s\t%s\n", report, hash_to_hex(hash));
write_or_die(1, buf.buf, buf.len);
strbuf_release(&buf);

View file

@ -37,6 +37,7 @@
#include "range-diff.h"
#define MAIL_DEFAULT_WRAP 72
#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@ -207,7 +208,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
rev->show_notes = 1;
if (rev->show_notes)
init_display_notes(&rev->notes_opt);
load_display_notes(&rev->notes_opt);
if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
rev->diffopt.filter || rev->diffopt.flags.follow_renames)
@ -627,6 +628,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
break;
case OBJ_TAG: {
struct tag *t = (struct tag *)o;
struct object_id *oid = get_tagged_oid(t);
if (rev.shown_one)
putchar('\n');
@ -638,10 +640,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.shown_one = 1;
if (ret)
break;
o = parse_object(the_repository, &t->tagged->oid);
o = parse_object(the_repository, oid);
if (!o)
ret = error(_("could not read object %s"),
oid_to_hex(&t->tagged->oid));
oid_to_hex(oid));
objects[i].item = o;
i--;
break;
@ -764,28 +766,56 @@ static void add_header(const char *value)
item->string[len] = '\0';
}
#define THREAD_SHALLOW 1
#define THREAD_DEEP 2
static int thread;
static int do_signoff;
static int base_auto;
static char *from;
static const char *signature = git_version_string;
static const char *signature_file;
static int config_cover_letter;
static const char *config_output_directory;
enum {
enum cover_setting {
COVER_UNSET,
COVER_OFF,
COVER_ON,
COVER_AUTO
};
enum thread_level {
THREAD_UNSET,
THREAD_SHALLOW,
THREAD_DEEP
};
enum cover_from_description {
COVER_FROM_NONE,
COVER_FROM_MESSAGE,
COVER_FROM_SUBJECT,
COVER_FROM_AUTO
};
static enum thread_level thread;
static int do_signoff;
static int base_auto;
static char *from;
static const char *signature = git_version_string;
static const char *signature_file;
static enum cover_setting config_cover_letter;
static const char *config_output_directory;
static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
static int show_notes;
static struct display_notes_opt notes_opt;
static enum cover_from_description parse_cover_from_description(const char *arg)
{
if (!arg || !strcmp(arg, "default"))
return COVER_FROM_MESSAGE;
else if (!strcmp(arg, "none"))
return COVER_FROM_NONE;
else if (!strcmp(arg, "message"))
return COVER_FROM_MESSAGE;
else if (!strcmp(arg, "subject"))
return COVER_FROM_SUBJECT;
else if (!strcmp(arg, "auto"))
return COVER_FROM_AUTO;
else
die(_("%s: invalid cover from description mode"), arg);
}
static int git_format_config(const char *var, const char *value, void *cb)
{
struct rev_info *rev = cb;
if (!strcmp(var, "format.headers")) {
if (!value)
die(_("format.headers without value"));
@ -835,7 +865,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
thread = THREAD_SHALLOW;
return 0;
}
thread = git_config_bool(var, value) && THREAD_SHALLOW;
thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
return 0;
}
if (!strcmp(var, "format.signoff")) {
@ -872,19 +902,17 @@ static int git_format_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "format.notes")) {
struct strbuf buf = STRBUF_INIT;
int b = git_parse_maybe_bool(value);
if (!b)
return 0;
rev->show_notes = 1;
if (b < 0) {
strbuf_addstr(&buf, value);
expand_notes_ref(&buf);
string_list_append(&rev->notes_opt.extra_notes_refs,
strbuf_detach(&buf, NULL));
} else {
rev->notes_opt.use_default_notes = 1;
}
if (b < 0)
enable_ref_display_notes(&notes_opt, &show_notes, value);
else if (b)
enable_default_display_notes(&notes_opt, &show_notes);
else
disable_display_notes(&notes_opt, &show_notes);
return 0;
}
if (!strcmp(var, "format.coverfromdescription")) {
cover_from_description_mode = parse_cover_from_description(value);
return 0;
}
@ -993,20 +1021,6 @@ static void print_signature(FILE *file)
putc('\n', file);
}
static void add_branch_description(struct strbuf *buf, const char *branch_name)
{
struct strbuf desc = STRBUF_INIT;
if (!branch_name || !*branch_name)
return;
read_branch_desc(&desc, branch_name);
if (desc.len) {
strbuf_addch(buf, '\n');
strbuf_addbuf(buf, &desc);
strbuf_addch(buf, '\n');
}
strbuf_release(&desc);
}
static char *find_branch_name(struct rev_info *rev)
{
int i, positive = -1;
@ -1053,6 +1067,63 @@ static void show_diffstat(struct rev_info *rev,
fprintf(rev->diffopt.file, "\n");
}
static void prepare_cover_text(struct pretty_print_context *pp,
const char *branch_name,
struct strbuf *sb,
const char *encoding,
int need_8bit_cte)
{
const char *subject = "*** SUBJECT HERE ***";
const char *body = "*** BLURB HERE ***";
struct strbuf description_sb = STRBUF_INIT;
struct strbuf subject_sb = STRBUF_INIT;
if (cover_from_description_mode == COVER_FROM_NONE)
goto do_pp;
if (branch_name && *branch_name)
read_branch_desc(&description_sb, branch_name);
if (!description_sb.len)
goto do_pp;
if (cover_from_description_mode == COVER_FROM_SUBJECT ||
cover_from_description_mode == COVER_FROM_AUTO)
body = format_subject(&subject_sb, description_sb.buf, " ");
if (cover_from_description_mode == COVER_FROM_MESSAGE ||
(cover_from_description_mode == COVER_FROM_AUTO &&
subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
body = description_sb.buf;
else
subject = subject_sb.buf;
do_pp:
pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
pp_remainder(pp, &body, sb, 0);
strbuf_release(&description_sb);
strbuf_release(&subject_sb);
}
static int get_notes_refs(struct string_list_item *item, void *arg)
{
argv_array_pushf(arg, "--notes=%s", item->string);
return 0;
}
static void get_notes_args(struct argv_array *arg, struct rev_info *rev)
{
if (!rev->show_notes) {
argv_array_push(arg, "--no-notes");
} else if (rev->notes_opt.use_default_notes > 0 ||
(rev->notes_opt.use_default_notes == -1 &&
!rev->notes_opt.extra_notes_refs.nr)) {
argv_array_push(arg, "--notes");
} else {
for_each_string_list(&rev->notes_opt.extra_notes_refs, get_notes_refs, arg);
}
}
static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct commit *origin,
int nr, struct commit **list,
@ -1060,8 +1131,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
int quiet)
{
const char *committer;
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
const char *msg;
struct shortlog log;
struct strbuf sb = STRBUF_INIT;
int i;
@ -1091,15 +1160,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
if (!branch_name)
branch_name = find_branch_name(rev);
msg = body;
pp.fmt = CMIT_FMT_EMAIL;
pp.date_mode.type = DATE_RFC2822;
pp.rev = rev;
pp.print_email_subject = 1;
pp_user_info(&pp, NULL, &sb, committer, encoding);
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
add_branch_description(&sb, branch_name);
prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
fprintf(rev->diffopt.file, "%s\n", sb.buf);
strbuf_release(&sb);
@ -1130,13 +1196,16 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
* can be added later if deemed desirable.
*/
struct diff_options opts;
struct argv_array other_arg = ARGV_ARRAY_INIT;
diff_setup(&opts);
opts.file = rev->diffopt.file;
opts.use_color = rev->diffopt.use_color;
diff_setup_done(&opts);
fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
get_notes_args(&other_arg, rev);
show_range_diff(rev->rdiff1, rev->rdiff2,
rev->creation_factor, 1, &opts);
rev->creation_factor, 1, &opts, &other_arg);
argv_array_clear(&other_arg);
}
}
@ -1248,9 +1317,9 @@ static int output_directory_callback(const struct option *opt, const char *arg,
static int thread_callback(const struct option *opt, const char *arg, int unset)
{
int *thread = (int *)opt->value;
enum thread_level *thread = (enum thread_level *)opt->value;
if (unset)
*thread = 0;
*thread = THREAD_UNSET;
else if (!arg || !strcmp(arg, "shallow"))
*thread = THREAD_SHALLOW;
else if (!strcmp(arg, "deep"))
@ -1297,7 +1366,7 @@ static int header_callback(const struct option *opt, const char *arg, int unset)
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
} else {
add_header(arg);
add_header(arg);
}
return 0;
}
@ -1353,7 +1422,7 @@ static struct commit *get_base_commit(const char *base_commit,
base = lookup_commit_reference_by_name(base_commit);
if (!base)
die(_("unknown commit %s"), base_commit);
} else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
} else if ((base_commit && !strcmp(base_commit, "auto"))) {
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
if (upstream) {
@ -1541,6 +1610,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int use_patch_format = 0;
int quiet = 0;
int reroll_count = -1;
char *cover_from_description_arg = NULL;
char *branch_name = NULL;
char *base_commit = NULL;
struct base_tree_info bases;
@ -1577,6 +1647,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "rfc", &rev, NULL,
N_("Use [RFC PATCH] instead of [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
N_("cover-from-description-mode"),
N_("generate parts of a cover letter based on a branch's description")),
{ OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
N_("Use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback },
@ -1640,8 +1713,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
init_log_defaults();
init_display_notes(&notes_opt);
git_config(git_format_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
git_config(git_format_config, &rev);
rev.show_notes = show_notes;
memcpy(&rev.notes_opt, &notes_opt, sizeof(notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
@ -1653,6 +1729,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
if (base_auto)
base_commit = "auto";
if (default_attach) {
rev.mime_boundary = default_attach;
rev.no_inline = 1;
@ -1668,6 +1747,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
PARSE_OPT_KEEP_DASHDASH);
if (cover_from_description_arg)
cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
if (0 < reroll_count) {
struct strbuf sprefix = STRBUF_INIT;
strbuf_addf(&sprefix, "%s v%d",
@ -1754,7 +1836,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.flags.binary = 1;
if (rev.show_notes)
init_display_notes(&rev.notes_opt);
load_display_notes(&rev.notes_opt);
if (!output_directory && !use_stdout)
output_directory = config_output_directory;
@ -1765,10 +1847,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
setup_pager();
if (output_directory) {
int saved;
if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
rev.diffopt.use_color = GIT_COLOR_NEVER;
if (use_stdout)
die(_("standard output, or directory, which one?"));
/*
* We consider <outdir> as 'outside of gitdir', therefore avoid
* applying adjust_shared_perm in s-c-l-d.
*/
saved = get_shared_repository();
set_shared_repository(0);
switch (safe_create_leading_directories_const(output_directory)) {
case SCLD_OK:
case SCLD_EXISTS:
break;
default:
die(_("could not create leading directories "
"of '%s'"), output_directory);
}
set_shared_repository(saved);
if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
die_errno(_("could not create directory '%s'"),
output_directory);
@ -1897,7 +1995,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
memset(&bases, 0, sizeof(bases));
if (base_commit || base_auto) {
if (base_commit) {
struct commit *base = get_base_commit(base_commit, list, nr);
reset_revision_walk();
clear_object_flags(UNINTERESTING);

View file

@ -492,7 +492,7 @@ static int option_parse_exclude_from(const struct option *opt,
BUG_ON_OPT_NEG(unset);
exc_given = 1;
add_excludes_from_file(dir, arg);
add_patterns_from_file(dir, arg);
return 0;
}
@ -516,7 +516,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
int require_work_tree = 0, show_tag = 0, i;
const char *max_prefix;
struct dir_struct dir;
struct exclude_list *el;
struct pattern_list *pl;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct option builtin_ls_files_options[] = {
/* Think twice before adding "--nul" synonym to this */
@ -594,9 +594,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
ls_files_usage, 0);
el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
for (i = 0; i < exclude_list.nr; i++) {
add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
add_pattern(exclude_list.items[i].string, "", 0, pl, --exclude_args);
}
if (show_tag || show_valid_bit || show_fsmonitor_bit) {
tag_cached = "H ";

View file

@ -1,3 +1,4 @@
#include "cache.h"
#include "builtin.h"
#include "commit.h"
#include "tag.h"
@ -63,6 +64,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
if (argc - i != 3) /* "--" "<head>" "<remote>" */
die(_("not handling anything other than two heads merge."));
if (repo_read_index_unmerged(the_repository))
die_resolve_conflict("merge");
o.branch1 = argv[++i];
o.branch2 = argv[++i];

View file

@ -180,8 +180,9 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const stru
static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
{
char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
return make_traverse_path(path, info, n);
struct strbuf buf = STRBUF_INIT;
strbuf_make_traverse_path(&buf, info, n->path, n->pathlen);
return strbuf_detach(&buf, NULL);
}
static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)

View file

@ -62,6 +62,7 @@ static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = -1;
static int option_edit = -1;
static int allow_trivial = 1, have_message, verify_signatures;
static int check_trust_level = 1;
static int overwrite_ignore = 1;
static struct strbuf merge_msg = STRBUF_INIT;
static struct strategy **use_strategies;
@ -81,7 +82,7 @@ static int show_progress = -1;
static int default_to_upstream = 1;
static int signoff;
static const char *sign_commit;
static int verify_msg = 1;
static int no_verify;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@ -287,7 +288,7 @@ static struct option builtin_merge_options[] = {
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
OPT_END()
};
@ -631,6 +632,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
} else if (!strcmp(k, "commit.gpgsign")) {
sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
} else if (!strcmp(k, "gpg.mintrustlevel")) {
check_trust_level = 0;
}
status = fmt_merge_msg_config(k, v, cb);
@ -688,16 +691,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
struct commit *head)
{
struct lock_file lock = LOCK_INIT;
const char *head_arg = "HEAD";
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
return error(_("Unable to write index."));
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
struct lock_file lock = LOCK_INIT;
int clean, x;
struct commit *result;
struct commit_list *reversed = NULL;
@ -816,6 +816,18 @@ static void write_merge_heads(struct commit_list *);
static void prepare_to_commit(struct commit_list *remoteheads)
{
struct strbuf msg = STRBUF_INIT;
const char *index_file = get_index_file();
if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL))
abort_commit(remoteheads, NULL);
/*
* Re-read the index as pre-merge-commit hook could have updated it,
* and write it out as a tree. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
if (find_hook("pre-merge-commit"))
discard_cache();
read_cache_from(index_file);
strbuf_addbuf(&msg, &merge_msg);
if (squash)
BUG("the control must not reach here under --squash");
@ -842,7 +854,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
abort_commit(remoteheads, NULL);
}
if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(),
"commit-msg",
git_path_merge_msg(the_repository), NULL))
abort_commit(remoteheads, NULL);
@ -860,12 +872,8 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
struct object_id result_tree, result_commit;
struct commit_list *parents, **pptr = &parents;
struct lock_file lock = LOCK_INIT;
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
return error(_("Unable to write index."));
write_tree_trivial(&result_tree);
@ -1392,7 +1400,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("Can merge only exactly one commit into empty head"));
if (verify_signatures)
verify_merge_signature(remoteheads->item, verbosity);
verify_merge_signature(remoteheads->item, verbosity,
check_trust_level);
remote_head_oid = &remoteheads->item->object.oid;
read_empty(remote_head_oid, 0);
@ -1415,7 +1424,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (verify_signatures) {
for (p = remoteheads; p; p = p->next) {
verify_merge_signature(p->item, verbosity);
verify_merge_signature(p->item, verbosity,
check_trust_level);
}
}

View file

@ -29,8 +29,11 @@ static int verify_object(const struct object_id *oid, const char *expected_type)
const struct object_id *repl = lookup_replace_object(the_repository, oid);
if (buffer) {
if (type == type_from_string(expected_type))
ret = check_object_signature(repl, buffer, size, expected_type);
if (type == type_from_string(expected_type)) {
ret = check_object_signature(the_repository, repl,
buffer, size,
expected_type);
}
free(buffer);
}
return ret;

View file

@ -6,21 +6,25 @@
#include "trace2.h"
static char const * const builtin_multi_pack_index_usage[] = {
N_("git multi-pack-index [--object-dir=<dir>] (write|verify|expire|repack --batch-size=<size>)"),
N_("git multi-pack-index [<options>] (write|verify|expire|repack --batch-size=<size>)"),
NULL
};
static struct opts_multi_pack_index {
const char *object_dir;
unsigned long batch_size;
int progress;
} opts;
int cmd_multi_pack_index(int argc, const char **argv,
const char *prefix)
{
unsigned flags = 0;
static struct option builtin_multi_pack_index_options[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
OPT_END(),
@ -28,12 +32,15 @@ int cmd_multi_pack_index(int argc, const char **argv,
git_config(git_default_config, NULL);
opts.progress = isatty(2);
argc = parse_options(argc, argv, prefix,
builtin_multi_pack_index_options,
builtin_multi_pack_index_usage, 0);
if (!opts.object_dir)
opts.object_dir = get_object_directory();
if (opts.progress)
flags |= MIDX_PROGRESS;
if (argc == 0)
usage_with_options(builtin_multi_pack_index_usage,
@ -47,16 +54,17 @@ int cmd_multi_pack_index(int argc, const char **argv,
trace2_cmd_mode(argv[0]);
if (!strcmp(argv[0], "repack"))
return midx_repack(the_repository, opts.object_dir, (size_t)opts.batch_size);
return midx_repack(the_repository, opts.object_dir,
(size_t)opts.batch_size, flags);
if (opts.batch_size)
die(_("--batch-size option is only for 'repack' subcommand"));
if (!strcmp(argv[0], "write"))
return write_midx_file(opts.object_dir);
return write_midx_file(opts.object_dir, flags);
if (!strcmp(argv[0], "verify"))
return verify_midx_file(the_repository, opts.object_dir);
return verify_midx_file(the_repository, opts.object_dir, flags);
if (!strcmp(argv[0], "expire"))
return expire_midx_packs(the_repository, opts.object_dir);
return expire_midx_packs(the_repository, opts.object_dir, flags);
die(_("unrecognized subcommand: %s"), argv[0]);
}

View file

@ -6,20 +6,25 @@
#include "tag.h"
#include "refs.h"
#include "parse-options.h"
#include "prio-queue.h"
#include "sha1-lookup.h"
#include "commit-slab.h"
#define CUTOFF_DATE_SLOP 86400 /* one day */
/*
* One day. See the 'name a rev shortly after epoch' test in t6120 when
* changing this value
*/
#define CUTOFF_DATE_SLOP 86400
typedef struct rev_name {
const char *tip_name;
struct rev_name {
char *tip_name;
timestamp_t taggerdate;
int generation;
int distance;
int from_tag;
} rev_name;
};
define_commit_slab(commit_rev_name, struct rev_name *);
define_commit_slab(commit_rev_name, struct rev_name);
static timestamp_t cutoff = TIME_MAX;
static struct commit_rev_name rev_names;
@ -27,16 +32,16 @@ static struct commit_rev_name rev_names;
/* How many generations are maximally preferred over _one_ merge traversal? */
#define MERGE_TRAVERSAL_WEIGHT 65535
static struct rev_name *get_commit_rev_name(struct commit *commit)
static int is_valid_rev_name(const struct rev_name *name)
{
struct rev_name **slot = commit_rev_name_peek(&rev_names, commit);
return slot ? *slot : NULL;
return name && (name->generation || name->tip_name);
}
static void set_commit_rev_name(struct commit *commit, struct rev_name *name)
static struct rev_name *get_commit_rev_name(const struct commit *commit)
{
*commit_rev_name_at(&rev_names, commit) = name;
struct rev_name *name = commit_rev_name_peek(&rev_names, commit);
return is_valid_rev_name(name) ? name : NULL;
}
static int is_better_name(struct rev_name *name,
@ -75,68 +80,135 @@ static int is_better_name(struct rev_name *name,
return 0;
}
static void name_rev(struct commit *commit,
const char *tip_name, timestamp_t taggerdate,
int generation, int distance, int from_tag,
int deref)
static struct rev_name *create_or_update_name(struct commit *commit,
timestamp_t taggerdate,
int generation, int distance,
int from_tag)
{
struct rev_name *name = get_commit_rev_name(commit);
struct commit_list *parents;
int parent_number = 1;
char *to_free = NULL;
struct rev_name *name = commit_rev_name_at(&rev_names, commit);
parse_commit(commit);
if (is_valid_rev_name(name)) {
if (!is_better_name(name, taggerdate, distance, from_tag))
return NULL;
if (commit->date < cutoff)
return;
if (deref) {
tip_name = to_free = xstrfmt("%s^0", tip_name);
if (generation)
die("generation: %d, but deref?", generation);
/*
* This string might still be shared with ancestors
* (generation > 0). We can release it here regardless,
* because the new name that has just won will be better
* for them as well, so name_rev() will replace these
* stale pointers when it processes the parents.
*/
if (!name->generation)
free(name->tip_name);
}
if (name == NULL) {
name = xmalloc(sizeof(rev_name));
set_commit_rev_name(commit, name);
goto copy_data;
} else if (is_better_name(name, taggerdate, distance, from_tag)) {
copy_data:
name->tip_name = tip_name;
name->taggerdate = taggerdate;
name->generation = generation;
name->distance = distance;
name->from_tag = from_tag;
name->taggerdate = taggerdate;
name->generation = generation;
name->distance = distance;
name->from_tag = from_tag;
return name;
}
static char *get_parent_name(const struct rev_name *name, int parent_number)
{
struct strbuf sb = STRBUF_INIT;
size_t len;
strip_suffix(name->tip_name, "^0", &len);
if (name->generation > 0) {
strbuf_grow(&sb, len +
1 + decimal_width(name->generation) +
1 + decimal_width(parent_number));
strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
name->generation, parent_number);
} else {
free(to_free);
strbuf_grow(&sb, len +
1 + decimal_width(parent_number));
strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
parent_number);
}
return strbuf_detach(&sb, NULL);
}
static void name_rev(struct commit *start_commit,
const char *tip_name, timestamp_t taggerdate,
int from_tag, int deref)
{
struct prio_queue queue;
struct commit *commit;
struct commit **parents_to_queue = NULL;
size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
struct rev_name *start_name;
parse_commit(start_commit);
if (start_commit->date < cutoff)
return;
}
for (parents = commit->parents;
parents;
parents = parents->next, parent_number++) {
if (parent_number > 1) {
size_t len;
char *new_name;
start_name = create_or_update_name(start_commit, taggerdate, 0, 0,
from_tag);
if (!start_name)
return;
if (deref)
start_name->tip_name = xstrfmt("%s^0", tip_name);
else
start_name->tip_name = xstrdup(tip_name);
strip_suffix(tip_name, "^0", &len);
if (generation > 0)
new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
generation, parent_number);
else
new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
parent_number);
memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */
prio_queue_put(&queue, start_commit);
name_rev(parents->item, new_name, taggerdate, 0,
distance + MERGE_TRAVERSAL_WEIGHT,
from_tag, 0);
} else {
name_rev(parents->item, tip_name, taggerdate,
generation + 1, distance + 1,
from_tag, 0);
while ((commit = prio_queue_get(&queue))) {
struct rev_name *name = get_commit_rev_name(commit);
struct commit_list *parents;
int parent_number = 1;
parents_to_queue_nr = 0;
for (parents = commit->parents;
parents;
parents = parents->next, parent_number++) {
struct commit *parent = parents->item;
struct rev_name *parent_name;
int generation, distance;
parse_commit(parent);
if (parent->date < cutoff)
continue;
if (parent_number > 1) {
generation = 0;
distance = name->distance + MERGE_TRAVERSAL_WEIGHT;
} else {
generation = name->generation + 1;
distance = name->distance + 1;
}
parent_name = create_or_update_name(parent, taggerdate,
generation,
distance, from_tag);
if (parent_name) {
if (parent_number > 1)
parent_name->tip_name =
get_parent_name(name,
parent_number);
else
parent_name->tip_name = name->tip_name;
ALLOC_GROW(parents_to_queue,
parents_to_queue_nr + 1,
parents_to_queue_alloc);
parents_to_queue[parents_to_queue_nr] = parent;
parents_to_queue_nr++;
}
}
/* The first parent must come out first from the prio_queue */
while (parents_to_queue_nr)
prio_queue_put(&queue,
parents_to_queue[--parents_to_queue_nr]);
}
clear_prio_queue(&queue);
free(parents_to_queue);
}
static int subpath_matches(const char *path, const char *filter)
@ -157,10 +229,10 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
{
if (shorten_unambiguous)
refname = shorten_unambiguous_ref(refname, 0);
else if (starts_with(refname, "refs/heads/"))
refname = refname + 11;
else if (starts_with(refname, "refs/"))
refname = refname + 5;
else if (skip_prefix(refname, "refs/heads/", &refname))
; /* refname already advanced */
else
skip_prefix(refname, "refs/", &refname);
return refname;
}
@ -175,6 +247,10 @@ static struct tip_table {
struct tip_table_entry {
struct object_id oid;
const char *refname;
struct commit *commit;
timestamp_t taggerdate;
unsigned int from_tag:1;
unsigned int deref:1;
} *table;
int nr;
int alloc;
@ -182,13 +258,18 @@ static struct tip_table {
} tip_table;
static void add_to_tip_table(const struct object_id *oid, const char *refname,
int shorten_unambiguous)
int shorten_unambiguous, struct commit *commit,
timestamp_t taggerdate, int from_tag, int deref)
{
refname = name_ref_abbrev(refname, shorten_unambiguous);
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
oidcpy(&tip_table.table[tip_table.nr].oid, oid);
tip_table.table[tip_table.nr].refname = xstrdup(refname);
tip_table.table[tip_table.nr].commit = commit;
tip_table.table[tip_table.nr].taggerdate = taggerdate;
tip_table.table[tip_table.nr].from_tag = from_tag;
tip_table.table[tip_table.nr].deref = deref;
tip_table.nr++;
tip_table.sorted = 0;
}
@ -199,12 +280,30 @@ static int tipcmp(const void *a_, const void *b_)
return oidcmp(&a->oid, &b->oid);
}
static int cmp_by_tag_and_age(const void *a_, const void *b_)
{
const struct tip_table_entry *a = a_, *b = b_;
int cmp;
/* Prefer tags. */
cmp = b->from_tag - a->from_tag;
if (cmp)
return cmp;
/* Older is better. */
if (a->taggerdate < b->taggerdate)
return -1;
return a->taggerdate != b->taggerdate;
}
static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
{
struct object *o = parse_object(the_repository, oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
int from_tag = 0;
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
if (data->tags_only && !starts_with(path, "refs/tags/"))
@ -253,8 +352,6 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
return 0;
}
add_to_tip_table(oid, path, can_abbreviate_output);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
@ -264,18 +361,35 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
taggerdate = t->date;
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
int from_tag = starts_with(path, "refs/tags/");
commit = (struct commit *)o;
from_tag = starts_with(path, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = ((struct commit *)o)->date;
path = name_ref_abbrev(path, can_abbreviate_output);
name_rev(commit, xstrdup(path), taggerdate, 0, 0,
from_tag, deref);
taggerdate = commit->date;
}
add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
from_tag, deref);
return 0;
}
static void name_tips(void)
{
int i;
/*
* Try to set better names first, so that worse ones spread
* less.
*/
QSORT(tip_table.table, tip_table.nr, cmp_by_tag_and_age);
for (i = 0; i < tip_table.nr; i++) {
struct tip_table_entry *e = &tip_table.table[i];
if (e->commit) {
name_rev(e->commit, e->refname, e->taggerdate,
e->from_tag, e->deref);
}
}
}
static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
{
struct tip_table_entry *table = table_;
@ -305,11 +419,11 @@ static const char *get_exact_ref_match(const struct object *o)
static const char *get_rev_name(const struct object *o, struct strbuf *buf)
{
struct rev_name *n;
struct commit *c;
const struct commit *c;
if (o->type != OBJ_COMMIT)
return get_exact_ref_match(o);
c = (struct commit *) o;
c = (const struct commit *) o;
n = get_commit_rev_name(c);
if (!n)
return NULL;
@ -317,11 +431,10 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
if (!n->generation)
return n->tip_name;
else {
int len = strlen(n->tip_name);
if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
len -= 2;
strbuf_reset(buf);
strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
strbuf_addstr(buf, n->tip_name);
strbuf_strip_suffix(buf, "^0");
strbuf_addf(buf, "~%d", n->generation);
return buf->buf;
}
}
@ -481,9 +594,15 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
add_object_array(object, *argv, &revs);
}
if (cutoff)
cutoff = cutoff - CUTOFF_DATE_SLOP;
if (cutoff) {
/* check for undeflow */
if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP)
cutoff = cutoff - CUTOFF_DATE_SLOP;
else
cutoff = TIME_MIN;
}
for_each_ref(name_ref, &data);
name_tips();
if (transform_stdin) {
char buffer[2048];

View file

@ -513,7 +513,7 @@ static int copy(int argc, const char **argv, const char *prefix)
}
}
if (argc < 2) {
if (argc < 1) {
error(_("too few parameters"));
usage_with_options(git_notes_copy_usage, options);
}
@ -622,7 +622,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
strbuf_grow(&d.buf, size + 1);
if (d.buf.len && prev_buf && size)
strbuf_insert(&d.buf, 0, "\n", 1);
strbuf_insertstr(&d.buf, 0, "\n");
if (prev_buf && size)
strbuf_insert(&d.buf, 0, prev_buf, size);
free(prev_buf);
@ -745,7 +745,7 @@ static int merge_commit(struct notes_merge_options *o)
memset(&pretty_ctx, 0, sizeof(pretty_ctx));
format_commit_message(partial, "%s", &msg, &pretty_ctx);
strbuf_trim(&msg);
strbuf_insert(&msg, 0, "notes: ", 7);
strbuf_insertstr(&msg, 0, "notes: ");
update_ref(msg.buf, o->local_ref, &oid,
is_null_oid(&parent_oid) ? NULL : &parent_oid,
0, UPDATE_REFS_DIE_ON_ERR);

View file

@ -92,10 +92,11 @@ static struct progress *progress_state;
static struct packed_git *reuse_packfile;
static uint32_t reuse_packfile_objects;
static off_t reuse_packfile_offset;
static struct bitmap *reuse_packfile_bitmap;
static int use_bitmap_index_default = 1;
static int use_bitmap_index = -1;
static int allow_pack_reuse = 1;
static enum {
WRITE_BITMAP_FALSE = 0,
WRITE_BITMAP_QUIET,
@ -163,7 +164,7 @@ static void *get_delta(struct object_entry *entry)
delta_buf = diff_delta(base_buf, base_size,
buf, size, &delta_size, 0);
/*
* We succesfully computed this delta once but dropped it for
* We successfully computed this delta once but dropped it for
* memory reasons. Something is very wrong if this time we
* recompute and create a different delta.
*/
@ -303,7 +304,8 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
if (!usable_delta) {
if (oe_type(entry) == OBJ_BLOB &&
oe_size_greater_than(&to_pack, entry, big_file_threshold) &&
(st = open_istream(&entry->idx.oid, &type, &size, NULL)) != NULL)
(st = open_istream(the_repository, &entry->idx.oid, &type,
&size, NULL)) != NULL)
buf = NULL;
else {
buf = read_object_file(&entry->idx.oid, &type, &size);
@ -610,12 +612,12 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag,
void *cb_data)
{
struct object_id peeled;
struct object_entry *entry = packlist_find(&to_pack, oid, NULL);
struct object_entry *entry = packlist_find(&to_pack, oid);
if (entry)
entry->tagged = 1;
if (!peel_ref(path, &peeled)) {
entry = packlist_find(&to_pack, &peeled, NULL);
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
}
@ -784,57 +786,186 @@ static struct object_entry **compute_write_order(void)
return wo;
}
static off_t write_reused_pack(struct hashfile *f)
/*
* A reused set of objects. All objects in a chunk have the same
* relative position in the original packfile and the generated
* packfile.
*/
static struct reused_chunk {
/* The offset of the first object of this chunk in the original
* packfile. */
off_t original;
/* The offset of the first object of this chunk in the generated
* packfile minus "original". */
off_t difference;
} *reused_chunks;
static int reused_chunks_nr;
static int reused_chunks_alloc;
static void record_reused_object(off_t where, off_t offset)
{
unsigned char buffer[8192];
off_t to_write, total;
int fd;
if (reused_chunks_nr && reused_chunks[reused_chunks_nr-1].difference == offset)
return;
if (!is_pack_valid(reuse_packfile))
die(_("packfile is invalid: %s"), reuse_packfile->pack_name);
ALLOC_GROW(reused_chunks, reused_chunks_nr + 1,
reused_chunks_alloc);
reused_chunks[reused_chunks_nr].original = where;
reused_chunks[reused_chunks_nr].difference = offset;
reused_chunks_nr++;
}
fd = git_open(reuse_packfile->pack_name);
if (fd < 0)
die_errno(_("unable to open packfile for reuse: %s"),
reuse_packfile->pack_name);
if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1)
die_errno(_("unable to seek in reused packfile"));
if (reuse_packfile_offset < 0)
reuse_packfile_offset = reuse_packfile->pack_size - the_hash_algo->rawsz;
total = to_write = reuse_packfile_offset - sizeof(struct pack_header);
while (to_write) {
int read_pack = xread(fd, buffer, sizeof(buffer));
if (read_pack <= 0)
die_errno(_("unable to read from reused packfile"));
if (read_pack > to_write)
read_pack = to_write;
hashwrite(f, buffer, read_pack);
to_write -= read_pack;
/*
* We don't know the actual number of objects written,
* only how many bytes written, how many bytes total, and
* how many objects total. So we can fake it by pretending all
* objects we are writing are the same size. This gives us a
* smooth progress meter, and at the end it matches the true
* answer.
*/
written = reuse_packfile_objects *
(((double)(total - to_write)) / total);
display_progress(progress_state, written);
/*
* Binary search to find the chunk that "where" is in. Note
* that we're not looking for an exact match, just the first
* chunk that contains it (which implicitly ends at the start
* of the next chunk.
*/
static off_t find_reused_offset(off_t where)
{
int lo = 0, hi = reused_chunks_nr;
while (lo < hi) {
int mi = lo + ((hi - lo) / 2);
if (where == reused_chunks[mi].original)
return reused_chunks[mi].difference;
if (where < reused_chunks[mi].original)
hi = mi;
else
lo = mi + 1;
}
close(fd);
written = reuse_packfile_objects;
display_progress(progress_state, written);
return reuse_packfile_offset - sizeof(struct pack_header);
/*
* The first chunk starts at zero, so we can't have gone below
* there.
*/
assert(lo);
return reused_chunks[lo-1].difference;
}
static void write_reused_pack_one(size_t pos, struct hashfile *out,
struct pack_window **w_curs)
{
off_t offset, next, cur;
enum object_type type;
unsigned long size;
offset = reuse_packfile->revindex[pos].offset;
next = reuse_packfile->revindex[pos + 1].offset;
record_reused_object(offset, offset - hashfile_total(out));
cur = offset;
type = unpack_object_header(reuse_packfile, w_curs, &cur, &size);
assert(type >= 0);
if (type == OBJ_OFS_DELTA) {
off_t base_offset;
off_t fixup;
unsigned char header[MAX_PACK_OBJECT_HEADER];
unsigned len;
base_offset = get_delta_base(reuse_packfile, w_curs, &cur, type, offset);
assert(base_offset != 0);
/* Convert to REF_DELTA if we must... */
if (!allow_ofs_delta) {
int base_pos = find_revindex_position(reuse_packfile, base_offset);
struct object_id base_oid;
nth_packed_object_id(&base_oid, reuse_packfile,
reuse_packfile->revindex[base_pos].nr);
len = encode_in_pack_object_header(header, sizeof(header),
OBJ_REF_DELTA, size);
hashwrite(out, header, len);
hashwrite(out, base_oid.hash, 20);
copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur);
return;
}
/* Otherwise see if we need to rewrite the offset... */
fixup = find_reused_offset(offset) -
find_reused_offset(base_offset);
if (fixup) {
unsigned char ofs_header[10];
unsigned i, ofs_len;
off_t ofs = offset - base_offset - fixup;
len = encode_in_pack_object_header(header, sizeof(header),
OBJ_OFS_DELTA, size);
i = sizeof(ofs_header) - 1;
ofs_header[i] = ofs & 127;
while (ofs >>= 7)
ofs_header[--i] = 128 | (--ofs & 127);
ofs_len = sizeof(ofs_header) - i;
hashwrite(out, header, len);
hashwrite(out, ofs_header + sizeof(ofs_header) - ofs_len, ofs_len);
copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur);
return;
}
/* ...otherwise we have no fixup, and can write it verbatim */
}
copy_pack_data(out, reuse_packfile, w_curs, offset, next - offset);
}
static size_t write_reused_pack_verbatim(struct hashfile *out,
struct pack_window **w_curs)
{
size_t pos = 0;
while (pos < reuse_packfile_bitmap->word_alloc &&
reuse_packfile_bitmap->words[pos] == (eword_t)~0)
pos++;
if (pos) {
off_t to_write;
written = (pos * BITS_IN_EWORD);
to_write = reuse_packfile->revindex[written].offset
- sizeof(struct pack_header);
/* We're recording one chunk, not one object. */
record_reused_object(sizeof(struct pack_header), 0);
hashflush(out);
copy_pack_data(out, reuse_packfile, w_curs,
sizeof(struct pack_header), to_write);
display_progress(progress_state, written);
}
return pos;
}
static void write_reused_pack(struct hashfile *f)
{
size_t i = 0;
uint32_t offset;
struct pack_window *w_curs = NULL;
if (allow_ofs_delta)
i = write_reused_pack_verbatim(f, &w_curs);
for (; i < reuse_packfile_bitmap->word_alloc; ++i) {
eword_t word = reuse_packfile_bitmap->words[i];
size_t pos = (i * BITS_IN_EWORD);
for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
if ((word >> offset) == 0)
break;
offset += ewah_bit_ctz64(word >> offset);
write_reused_pack_one(pos + offset, f, &w_curs);
display_progress(progress_state, ++written);
}
}
unuse_pack(&w_curs);
}
static const char no_split_warning[] = N_(
@ -867,11 +998,9 @@ static void write_pack_file(void)
offset = write_pack_header(f, nr_remaining);
if (reuse_packfile) {
off_t packfile_size;
assert(pack_to_stdout);
packfile_size = write_reused_pack(f);
offset += packfile_size;
write_reused_pack(f);
offset = hashfile_total(f);
}
nr_written = 0;
@ -996,12 +1125,15 @@ static int no_try_delta(const char *path)
* few lines later when we want to add the new entry.
*/
static int have_duplicate_entry(const struct object_id *oid,
int exclude,
uint32_t *index_pos)
int exclude)
{
struct object_entry *entry;
entry = packlist_find(&to_pack, oid, index_pos);
if (reuse_packfile_bitmap &&
bitmap_walk_contains(bitmap_git, reuse_packfile_bitmap, oid))
return 1;
entry = packlist_find(&to_pack, oid);
if (!entry)
return 0;
@ -1141,13 +1273,12 @@ static void create_object_entry(const struct object_id *oid,
uint32_t hash,
int exclude,
int no_try_delta,
uint32_t index_pos,
struct packed_git *found_pack,
off_t found_offset)
{
struct object_entry *entry;
entry = packlist_alloc(&to_pack, oid->hash, index_pos);
entry = packlist_alloc(&to_pack, oid);
entry->hash = hash;
oe_set_type(entry, type);
if (exclude)
@ -1171,11 +1302,10 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
{
struct packed_git *found_pack = NULL;
off_t found_offset = 0;
uint32_t index_pos;
display_progress(progress_state, ++nr_seen);
if (have_duplicate_entry(oid, exclude, &index_pos))
if (have_duplicate_entry(oid, exclude))
return 0;
if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
@ -1190,7 +1320,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
create_object_entry(oid, type, pack_name_hash(name),
exclude, name && no_try_delta(name),
index_pos, found_pack, found_offset);
found_pack, found_offset);
return 1;
}
@ -1199,17 +1329,15 @@ static int add_object_entry_from_bitmap(const struct object_id *oid,
int flags, uint32_t name_hash,
struct packed_git *pack, off_t offset)
{
uint32_t index_pos;
display_progress(progress_state, ++nr_seen);
if (have_duplicate_entry(oid, 0, &index_pos))
if (have_duplicate_entry(oid, 0))
return 0;
if (!want_object_in_pack(oid, 0, &pack, &offset))
return 0;
create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
create_object_entry(oid, type, name_hash, 0, 0, pack, offset);
return 1;
}
@ -1491,23 +1619,17 @@ static void cleanup_preferred_base(void)
* deltify other objects against, in order to avoid
* circular deltas.
*/
static int can_reuse_delta(const unsigned char *base_sha1,
static int can_reuse_delta(const struct object_id *base_oid,
struct object_entry *delta,
struct object_entry **base_out)
{
struct object_entry *base;
struct object_id base_oid;
if (!base_sha1)
return 0;
oidread(&base_oid, base_sha1);
/*
* First see if we're already sending the base (or it's explicitly in
* our "excluded" list).
*/
base = packlist_find(&to_pack, &base_oid, NULL);
base = packlist_find(&to_pack, base_oid);
if (base) {
if (!in_same_island(&delta->idx.oid, &base->idx.oid))
return 0;
@ -1520,9 +1642,9 @@ static int can_reuse_delta(const unsigned char *base_sha1,
* even if it was buried too deep in history to make it into the
* packing list.
*/
if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, &base_oid)) {
if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, base_oid)) {
if (use_delta_islands) {
if (!in_same_island(&delta->idx.oid, &base_oid))
if (!in_same_island(&delta->idx.oid, base_oid))
return 0;
}
*base_out = NULL;
@ -1539,7 +1661,8 @@ static void check_object(struct object_entry *entry)
if (IN_PACK(entry)) {
struct packed_git *p = IN_PACK(entry);
struct pack_window *w_curs = NULL;
const unsigned char *base_ref = NULL;
int have_base = 0;
struct object_id base_ref;
struct object_entry *base_entry;
unsigned long used, used_0;
unsigned long avail;
@ -1580,9 +1703,13 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
return;
case OBJ_REF_DELTA:
if (reuse_delta && !entry->preferred_base)
base_ref = use_pack(p, &w_curs,
entry->in_pack_offset + used, NULL);
if (reuse_delta && !entry->preferred_base) {
oidread(&base_ref,
use_pack(p, &w_curs,
entry->in_pack_offset + used,
NULL));
have_base = 1;
}
entry->in_pack_header_size = used + the_hash_algo->rawsz;
break;
case OBJ_OFS_DELTA:
@ -1612,13 +1739,15 @@ static void check_object(struct object_entry *entry)
revidx = find_pack_revindex(p, ofs);
if (!revidx)
goto give_up;
base_ref = nth_packed_object_sha1(p, revidx->nr);
if (!nth_packed_object_id(&base_ref, p, revidx->nr))
have_base = 1;
}
entry->in_pack_header_size = used + used_0;
break;
}
if (can_reuse_delta(base_ref, entry, &base_entry)) {
if (have_base &&
can_reuse_delta(&base_ref, entry, &base_entry)) {
oe_set_type(entry, entry->in_pack_type);
SET_SIZE(entry, in_pack_size); /* delta size */
SET_DELTA_SIZE(entry, in_pack_size);
@ -1628,7 +1757,7 @@ static void check_object(struct object_entry *entry)
entry->delta_sibling_idx = base_entry->delta_child_idx;
SET_DELTA_CHILD(base_entry, entry);
} else {
SET_DELTA_EXT(entry, base_ref);
SET_DELTA_EXT(entry, &base_ref);
}
unuse_pack(&w_curs);
@ -2342,15 +2471,6 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
free(array);
}
static void try_to_free_from_threads(size_t size)
{
packing_data_lock(&to_pack);
release_pack_memory(size);
packing_data_unlock(&to_pack);
}
static try_to_free_t old_try_to_free_routine;
/*
* The main object list is split into smaller lists, each is handed to
* one worker.
@ -2391,12 +2511,10 @@ static void init_threaded_search(void)
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
set_try_to_free_routine(old_try_to_free_routine);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&cache_mutex);
pthread_mutex_destroy(&progress_mutex);
@ -2568,6 +2686,13 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
free(p);
}
static int obj_is_packed(const struct object_id *oid)
{
return packlist_find(&to_pack, oid) ||
(reuse_packfile_bitmap &&
bitmap_walk_contains(bitmap_git, reuse_packfile_bitmap, oid));
}
static void add_tag_chain(const struct object_id *oid)
{
struct tag *tag;
@ -2579,7 +2704,7 @@ static void add_tag_chain(const struct object_id *oid)
* it was included via bitmaps, we would not have parsed it
* previously).
*/
if (packlist_find(&to_pack, oid, NULL))
if (obj_is_packed(oid))
return;
tag = lookup_tag(the_repository, oid);
@ -2603,7 +2728,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
if (starts_with(path, "refs/tags/") && /* is a tag? */
!peel_ref(path, &peeled) && /* peelable? */
packlist_find(&to_pack, &peeled, NULL)) /* object packed? */
obj_is_packed(&peeled)) /* object packed? */
add_tag_chain(oid);
return 0;
}
@ -2671,6 +2796,7 @@ static void prepare_pack(int window, int depth)
if (nr_deltas && n > 1) {
unsigned nr_done = 0;
if (progress)
progress_state = start_progress(_("Compressing objects"),
nr_deltas);
@ -2715,8 +2841,8 @@ static int git_pack_config(const char *k, const char *v, void *cb)
use_bitmap_index_default = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "pack.usesparse")) {
sparse = git_config_bool(k, v);
if (!strcmp(k, "pack.allowpackreuse")) {
allow_pack_reuse = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "pack.threads")) {
@ -2803,7 +2929,7 @@ static void show_object(struct object *obj, const char *name, void *data)
for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
depth++;
ent = packlist_find(&to_pack, &obj->oid, NULL);
ent = packlist_find(&to_pack, &obj->oid);
if (ent && depth > oe_tree_depth(&to_pack, ent))
oe_set_tree_depth(&to_pack, ent, depth);
}
@ -2929,7 +3055,7 @@ static void add_objects_in_unpacked_packs(void)
in_pack.alloc);
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
nth_packed_object_id(&oid, p, i);
o = lookup_unknown_object(&oid);
if (!(o->flags & OBJECT_ADDED))
mark_in_pack_object(o, p, &in_pack);
@ -3033,8 +3159,8 @@ static void loosen_unused_packed_objects(void)
die(_("cannot open pack index"));
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
if (!packlist_find(&to_pack, &oid, NULL) &&
nth_packed_object_id(&oid, p, i);
if (!packlist_find(&to_pack, &oid) &&
!has_sha1_pack_kept_or_nonlocal(&oid) &&
!loosened_object_can_be_discarded(&oid, p->mtime))
if (force_object_loose(&oid, p->mtime))
@ -3050,8 +3176,8 @@ static void loosen_unused_packed_objects(void)
*/
static int pack_options_allow_reuse(void)
{
return pack_to_stdout &&
allow_ofs_delta &&
return allow_pack_reuse &&
pack_to_stdout &&
!ignore_packed_keep_on_disk &&
!ignore_packed_keep_in_core &&
(!local || !have_non_local_packs) &&
@ -3060,7 +3186,7 @@ static int pack_options_allow_reuse(void)
static int get_object_list_from_bitmap(struct rev_info *revs)
{
if (!(bitmap_git = prepare_bitmap_walk(revs)))
if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options)))
return -1;
if (pack_options_allow_reuse() &&
@ -3068,13 +3194,14 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
bitmap_git,
&reuse_packfile,
&reuse_packfile_objects,
&reuse_packfile_offset)) {
&reuse_packfile_bitmap)) {
assert(reuse_packfile_objects);
nr_result += reuse_packfile_objects;
display_progress(progress_state, nr_result);
}
traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
traverse_bitmap_commit_list(bitmap_git, revs,
&add_object_entry_from_bitmap);
return 0;
}
@ -3343,6 +3470,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
read_replace_refs = 0;
sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0);
prepare_repo_settings(the_repository);
if (!sparse && the_repository->settings.pack_use_sparse != -1)
sparse = the_repository->settings.pack_use_sparse;
reset_pack_idx_option(&pack_idx_opts);
git_config(git_pack_config, NULL);
@ -3434,7 +3565,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (filter_options.choice) {
if (!pack_to_stdout)
die(_("cannot use --filter without --stdout"));
use_bitmap_index = 0;
}
/*
@ -3525,7 +3655,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (progress)
fprintf_ln(stderr,
_("Total %"PRIu32" (delta %"PRIu32"),"
" reused %"PRIu32" (delta %"PRIu32")"),
written, written_delta, reused, reused_delta);
" reused %"PRIu32" (delta %"PRIu32"),"
" pack-reused %"PRIu32),
written, written_delta, reused, reused_delta,
reuse_packfile_objects);
return 0;
}

View file

@ -1,16 +1,12 @@
#include "cache.h"
#include "builtin.h"
#include "config.h"
#include "diff.h"
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
char name[50];
if (!patchlen)
return;
memcpy(name, oid_to_hex(id), GIT_SHA1_HEXSZ + 1);
printf("%s %s\n", oid_to_hex(result), name);
if (patchlen)
printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
}
static int remove_space(char *line)
@ -60,9 +56,9 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
{
int patchlen = 0, found_next = 0;
int before = -1, after = -1;
git_SHA_CTX ctx;
git_hash_ctx ctx;
git_SHA1_Init(&ctx);
the_hash_algo->init_fn(&ctx);
oidclr(result);
while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
@ -122,7 +118,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
/* Compute the sha without whitespace */
len = remove_space(line);
patchlen += len;
git_SHA1_Update(&ctx, line, len);
the_hash_algo->update_fn(&ctx, line, len);
}
if (!found_next)

View file

@ -15,6 +15,7 @@
#include "sha1-array.h"
#include "remote.h"
#include "dir.h"
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
#include "revision.h"
@ -26,15 +27,6 @@
#include "commit-reach.h"
#include "sequencer.h"
enum rebase_type {
REBASE_INVALID = -1,
REBASE_FALSE = 0,
REBASE_TRUE,
REBASE_PRESERVE,
REBASE_MERGES,
REBASE_INTERACTIVE
};
/**
* Parses the value of --rebase. If value is a false value, returns
* REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
@ -45,22 +37,9 @@ enum rebase_type {
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
{
int v = git_parse_maybe_bool(value);
if (!v)
return REBASE_FALSE;
else if (v > 0)
return REBASE_TRUE;
else if (!strcmp(value, "preserve") || !strcmp(value, "p"))
return REBASE_PRESERVE;
else if (!strcmp(value, "merges") || !strcmp(value, "m"))
return REBASE_MERGES;
else if (!strcmp(value, "interactive") || !strcmp(value, "i"))
return REBASE_INTERACTIVE;
/*
* Please update _git_config() in git-completion.bash when you
* add new rebase modes.
*/
enum rebase_type v = rebase_parse_value(value);
if (v != REBASE_INVALID)
return v;
if (fatal)
die(_("Invalid value for %s: %s"), key, value);
@ -107,6 +86,7 @@ static char *opt_ff;
static char *opt_verify_signatures;
static int opt_autostash = -1;
static int config_autostash;
static int check_trust_level = 1;
static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
static char *opt_gpg_sign;
@ -129,6 +109,7 @@ static char *opt_refmap;
static char *opt_ipv4;
static char *opt_ipv6;
static int opt_show_forced_updates = -1;
static char *set_upstream;
static struct option pull_options[] = {
/* Shared options */
@ -243,6 +224,9 @@ static struct option pull_options[] = {
PARSE_OPT_NOARG),
OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
N_("check for forced-updates on all updated branches")),
OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
N_("set upstream for git pull/fetch"),
PARSE_OPT_NOARG),
OPT_END()
};
@ -351,6 +335,8 @@ static enum rebase_type config_get_rebase(void)
*/
static int git_pull_config(const char *var, const char *value, void *cb)
{
int status;
if (!strcmp(var, "rebase.autostash")) {
config_autostash = git_config_bool(var, value);
return 0;
@ -358,7 +344,14 @@ static int git_pull_config(const char *var, const char *value, void *cb)
recurse_submodules = git_config_bool(var, value) ?
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
return 0;
} else if (!strcmp(var, "gpg.mintrustlevel")) {
check_trust_level = 0;
}
status = git_gpg_config(var, value, cb);
if (status)
return status;
return git_default_config(var, value, cb);
}
@ -556,6 +549,8 @@ static int run_fetch(const char *repo, const char **refspecs)
argv_array_push(&args, "--show-forced-updates");
else if (opt_show_forced_updates == 0)
argv_array_push(&args, "--no-show-forced-updates");
if (set_upstream)
argv_array_push(&args, set_upstream);
if (repo) {
argv_array_push(&args, repo);
@ -581,7 +576,8 @@ static int pull_into_void(const struct object_id *merge_head,
die(_("unable to access commit %s"),
oid_to_hex(merge_head));
verify_merge_signature(commit, opt_verbosity);
verify_merge_signature(commit, opt_verbosity,
check_trust_level);
}
/*

View file

@ -64,6 +64,7 @@ static struct string_list push_options_config = STRING_LIST_INIT_DUP;
static const char *map_refspec(const char *ref,
struct remote *remote, struct ref *local_refs)
{
const char *branch_name;
struct ref *matched = NULL;
/* Does "ref" uniquely name our ref? */
@ -84,8 +85,8 @@ static const char *map_refspec(const char *ref,
}
if (push_default == PUSH_DEFAULT_UPSTREAM &&
starts_with(matched->name, "refs/heads/")) {
struct branch *branch = branch_get(matched->name + 11);
skip_prefix(matched->name, "refs/heads/", &branch_name)) {
struct branch *branch = branch_get(branch_name);
if (branch->merge_nr == 1 && branch->merge[0]->src) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s:%s",
@ -143,8 +144,8 @@ static int push_url_of_remote(struct remote *remote, const char ***url_p)
return remote->url_nr;
}
static NORETURN int die_push_simple(struct branch *branch,
struct remote *remote)
static NORETURN void die_push_simple(struct branch *branch,
struct remote *remote)
{
/*
* There's no point in using shorten_unambiguous_ref here,
@ -357,8 +358,10 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
if (verbosity > 0)
fprintf(stderr, _("Pushing to %s\n"), transport->url);
trace2_region_enter("push", "transport_push", the_repository);
err = transport_push(the_repository, transport,
rs, flags, &reject_reasons);
trace2_region_leave("push", "transport_push", the_repository);
if (err != 0) {
fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
error(_("failed to push some refs to '%s'"), transport->url);
@ -385,30 +388,14 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
}
static int do_push(const char *repo, int flags,
const struct string_list *push_options)
const struct string_list *push_options,
struct remote *remote)
{
int i, errs;
struct remote *remote = pushremote_get(repo);
const char **url;
int url_nr;
struct refspec *push_refspec = &rs;
if (!remote) {
if (repo)
die(_("bad repository '%s'"), repo);
die(_("No configured push destination.\n"
"Either specify the URL from the command-line or configure a remote repository using\n"
"\n"
" git remote add <name> <url>\n"
"\n"
"and then push using the remote name\n"
"\n"
" git push <name>\n"));
}
if (remote->mirror)
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
if (push_options->nr)
flags |= TRANSPORT_PUSH_OPTIONS;
@ -548,6 +535,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
struct string_list *push_options;
const struct string_list_item *item;
struct remote *remote;
struct option options[] = {
OPT__VERBOSITY(&verbosity),
@ -602,20 +590,6 @@ int cmd_push(int argc, const char **argv, const char *prefix)
die(_("--delete is incompatible with --all, --mirror and --tags"));
if (deleterefs && argc < 2)
die(_("--delete doesn't make sense without any refs"));
if (flags & TRANSPORT_PUSH_ALL) {
if (tags)
die(_("--all and --tags are incompatible"));
if (argc >= 2)
die(_("--all can't be combined with refspecs"));
}
if (flags & TRANSPORT_PUSH_MIRROR) {
if (tags)
die(_("--mirror and --tags are incompatible"));
if (argc >= 2)
die(_("--mirror can't be combined with refspecs"));
}
if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
die(_("--all and --mirror are incompatible"));
if (recurse_submodules == RECURSE_SUBMODULES_CHECK)
flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
@ -632,11 +606,43 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_refspecs(argv + 1, argc - 1, repo);
}
remote = pushremote_get(repo);
if (!remote) {
if (repo)
die(_("bad repository '%s'"), repo);
die(_("No configured push destination.\n"
"Either specify the URL from the command-line or configure a remote repository using\n"
"\n"
" git remote add <name> <url>\n"
"\n"
"and then push using the remote name\n"
"\n"
" git push <name>\n"));
}
if (remote->mirror)
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
if (flags & TRANSPORT_PUSH_ALL) {
if (tags)
die(_("--all and --tags are incompatible"));
if (argc >= 2)
die(_("--all can't be combined with refspecs"));
}
if (flags & TRANSPORT_PUSH_MIRROR) {
if (tags)
die(_("--mirror and --tags are incompatible"));
if (argc >= 2)
die(_("--mirror can't be combined with refspecs"));
}
if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
die(_("--all and --mirror are incompatible"));
for_each_string_list_item(item, push_options)
if (strchr(item->string, '\n'))
die(_("push options must not have new line characters"));
rc = do_push(repo, flags, push_options);
rc = do_push(repo, flags, push_options, remote);
string_list_clear(&push_options_cmdline, 0);
string_list_clear(&push_options_config, 0);
if (rc == -1)

View file

@ -15,12 +15,16 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
{
int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
struct diff_options diffopt = { NULL };
struct argv_array other_arg = ARGV_ARRAY_INIT;
int simple_color = -1;
struct option range_diff_options[] = {
OPT_INTEGER(0, "creation-factor", &creation_factor,
N_("Percentage by which creation is weighted")),
OPT_BOOL(0, "no-dual-color", &simple_color,
N_("use simple diff colors")),
OPT_PASSTHRU_ARGV(0, "notes", &other_arg,
N_("notes"), N_("passed to 'git log'"),
PARSE_OPT_OPTARG),
OPT_END()
};
struct option *options;
@ -78,8 +82,9 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
FREE_AND_NULL(options);
res = show_range_diff(range1.buf, range2.buf, creation_factor,
simple_color < 1, &diffopt);
simple_color < 1, &diffopt, &other_arg);
argv_array_clear(&other_arg);
strbuf_release(&range1);
strbuf_release(&range2);

View file

@ -185,7 +185,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (opts.reset || opts.merge || opts.prefix) {
if (read_cache_unmerged() && (opts.prefix || opts.merge))
die("You need to resolve your current index first");
die(_("You need to resolve your current index first"));
stage = opts.merge = 1;
}
resolve_undo_clear();

View file

@ -29,8 +29,8 @@
#include "rebase-interactive.h"
static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
"[<upstream>] [<branch>]"),
N_("git rebase [-i] [options] [--exec <cmd>] "
"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
"--root [<branch>]"),
N_("git rebase --continue | --abort | --skip | --edit-todo"),
@ -44,14 +44,22 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
enum rebase_type {
REBASE_UNSPECIFIED = -1,
REBASE_AM,
REBASE_APPLY,
REBASE_MERGE,
REBASE_INTERACTIVE,
REBASE_PRESERVE_MERGES
};
enum empty_type {
EMPTY_UNSPECIFIED = -1,
EMPTY_DROP,
EMPTY_KEEP,
EMPTY_ASK
};
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
const char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@ -62,7 +70,7 @@ struct rebase_options {
const char *onto_name;
const char *revisions;
const char *switch_to;
int root;
int root, root_with_onto;
struct object_id *squash_onto;
struct commit *restrict_revision;
int dont_finish_rebase;
@ -77,7 +85,6 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
int keep_empty;
int autosquash;
char *gpg_sign_opt;
int autostash;
@ -92,6 +99,8 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@ -110,6 +119,9 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
replay.allow_empty = 1;
replay.allow_empty_message = opts->allow_empty_message;
replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
@ -117,6 +129,11 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
if (opts->strategy_opts)
parse_strategy_opts(&replay, opts->strategy_opts);
if (opts->squash_onto) {
oidcpy(&replay.squash_onto, opts->squash_onto);
replay.have_squash_onto = 1;
}
return replay;
}
@ -241,21 +258,17 @@ static int edit_todo_file(unsigned flags)
}
static int get_revision_ranges(struct commit *upstream, struct commit *onto,
const char **head_hash,
struct object_id *orig_head, const char **head_hash,
char **revisions, char **shortrevisions)
{
struct commit *base_rev = upstream ? upstream : onto;
const char *shorthead;
struct object_id orig_head;
if (get_oid("HEAD", &orig_head))
return error(_("no HEAD?"));
*head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
*head_hash = find_unique_abbrev(orig_head, GIT_MAX_HEXSZ);
*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
*head_hash);
shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV);
if (upstream) {
const char *shortrev;
@ -309,12 +322,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
struct replay_opts replay = get_replay_opts(opts);
struct string_list commands = STRING_LIST_INIT_DUP;
if (prepare_branch_to_be_rebased(the_repository, &replay,
opts->switch_to))
return -1;
if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
&revisions, &shortrevisions))
if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head,
&head_hash, &revisions, &shortrevisions))
return -1;
if (init_basic_state(&replay,
@ -332,8 +341,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
argv_array_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
argv_array_push(&make_script_args,
oid_to_hex(&opts->restrict_revision->object.oid));
argv_array_pushf(&make_script_args, "^%s",
oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.argc, make_script_args.argv,
@ -362,7 +371,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
return ret;
}
static int run_rebase_interactive(struct rebase_options *opts,
static int run_sequencer_rebase(struct rebase_options *opts,
enum action command)
{
unsigned flags = 0;
@ -370,10 +379,10 @@ static int run_rebase_interactive(struct rebase_options *opts,
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
switch (command) {
@ -433,6 +442,21 @@ static int run_rebase_interactive(struct rebase_options *opts,
return ret;
}
static int parse_opt_keep_empty(const struct option *opt, const char *arg,
int unset)
{
struct rebase_options *opts = opt->value;
BUG_ON_OPT_ARG(arg);
/*
* If we ever want to remap --keep-empty to --empty=keep, insert:
* opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP;
*/
opts->type = REBASE_MERGE;
return 0;
}
static const char * const builtin_rebase_interactive_usage[] = {
N_("git rebase--interactive [<options>]"),
NULL
@ -446,9 +470,13 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages")),
{ OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
N_("(DEPRECATED) keep empty commits"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
parse_opt_keep_empty },
OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages"),
PARSE_OPT_HIDDEN),
OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
N_("keep original branch points of cousins")),
@ -518,28 +546,26 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
warning(_("--[no-]rebase-cousins has no effect without "
"--rebase-merges"));
return !!run_rebase_interactive(&opts, command);
return !!run_sequencer_rebase(&opts, command);
}
static int is_interactive(struct rebase_options *opts)
static int is_merge(struct rebase_options *opts)
{
return opts->type == REBASE_INTERACTIVE ||
return opts->type == REBASE_MERGE ||
opts->type == REBASE_PRESERVE_MERGES;
}
static void imply_interactive(struct rebase_options *opts, const char *option)
static void imply_merge(struct rebase_options *opts, const char *option)
{
switch (opts->type) {
case REBASE_AM:
case REBASE_APPLY:
die(_("%s requires an interactive rebase"), option);
break;
case REBASE_INTERACTIVE:
case REBASE_MERGE:
case REBASE_PRESERVE_MERGES:
break;
case REBASE_MERGE:
/* we now implement --merge via --interactive */
default:
opts->type = REBASE_INTERACTIVE; /* implied */
opts->type = REBASE_MERGE; /* implied */
break;
}
}
@ -665,8 +691,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
write_file(state_dir_path("orig-head", opts), "%s",
oid_to_hex(&opts->orig_head));
write_file(state_dir_path("quiet", opts), "%s",
opts->flags & REBASE_NO_QUIET ? "" : "t");
if (!(opts->flags & REBASE_NO_QUIET))
write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
if (opts->strategy)
@ -684,7 +710,7 @@ static int rebase_write_basic_state(struct rebase_options *opts)
write_file(state_dir_path("gpg_sign_opt", opts), "%s",
opts->gpg_sign_opt);
if (opts->signoff)
write_file(state_dir_path("strategy", opts), "--signoff");
write_file(state_dir_path("signoff", opts), "--signoff");
return 0;
}
@ -748,7 +774,7 @@ static int finish_rebase(struct rebase_options *opts)
* user should see them.
*/
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
if (opts->type == REBASE_INTERACTIVE) {
if (opts->type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
@ -1011,7 +1037,8 @@ static int run_am(struct rebase_options *opts)
argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only",
"--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
"--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL);
"--no-cover-letter", "--pretty=mboxrd", "--topo-order",
"--no-base", NULL);
if (opts->git_format_patch_opt.len)
argv_array_split(&format_patch.args,
opts->git_format_patch_opt.buf);
@ -1080,8 +1107,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
int status;
const char *backend, *backend_func;
if (opts->type == REBASE_INTERACTIVE) {
/* Run builtin interactive rebase */
if (opts->type == REBASE_MERGE) {
/* Run sequencer-based rebase */
setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
@ -1094,11 +1121,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->gpg_sign_opt = tmp;
}
status = run_rebase_interactive(opts, action);
status = run_sequencer_rebase(opts, action);
goto finished_rebase;
}
if (opts->type == REBASE_AM) {
if (opts->type == REBASE_APPLY) {
status = run_am(opts);
goto finished_rebase;
}
@ -1118,8 +1145,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "revisions", opts->revisions);
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
add_var(&script_snippet, "GIT_QUIET",
opts->flags & REBASE_NO_QUIET ? "" : "t");
sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
add_var(&script_snippet, "git_am_opt", buf.buf);
strbuf_release(&buf);
@ -1137,7 +1162,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
@ -1155,7 +1179,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "git_format_patch_opt",
opts->git_format_patch_opt.buf);
if (is_interactive(opts) &&
if (is_merge(opts) &&
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
strbuf_addstr(&script_snippet,
"GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
@ -1180,8 +1204,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
finished_rebase:
if (opts->dont_finish_rebase)
; /* do nothing */
else if (opts->type == REBASE_INTERACTIVE)
; /* interactive rebase cleans up after itself */
else if (opts->type == REBASE_MERGE)
; /* merge backend cleans up after itself */
else if (status == 0) {
if (!file_exists(state_dir_path("stopped-sha", opts)))
finish_rebase(opts);
@ -1239,6 +1263,10 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
if (!strcmp(var, "rebase.backend")) {
return git_config_string(&opts->default_backend, var, value);
}
return git_default_config(var, value, data);
}
@ -1260,28 +1288,60 @@ static int is_linear_history(struct commit *from, struct commit *to)
return 1;
}
static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
struct object_id *merge_base)
static int can_fast_forward(struct commit *onto, struct commit *upstream,
struct commit *restrict_revision,
struct object_id *head_oid, struct object_id *merge_base)
{
struct commit *head = lookup_commit(the_repository, head_oid);
struct commit_list *merge_bases;
int res;
struct commit_list *merge_bases = NULL;
int res = 0;
if (!head)
return 0;
goto done;
merge_bases = get_merge_bases(onto, head);
if (merge_bases && !merge_bases->next) {
oidcpy(merge_base, &merge_bases->item->object.oid);
res = oideq(merge_base, &onto->object.oid);
} else {
if (!merge_bases || merge_bases->next) {
oidcpy(merge_base, &null_oid);
res = 0;
goto done;
}
oidcpy(merge_base, &merge_bases->item->object.oid);
if (!oideq(merge_base, &onto->object.oid))
goto done;
if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base))
goto done;
if (!upstream)
goto done;
free_commit_list(merge_bases);
merge_bases = get_merge_bases(upstream, head);
if (!merge_bases || merge_bases->next)
goto done;
if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
goto done;
res = 1;
done:
free_commit_list(merge_bases);
return res && is_linear_history(onto, head);
}
static int parse_opt_am(const struct option *opt, const char *arg, int unset)
{
struct rebase_options *opts = opt->value;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
opts->type = REBASE_APPLY;
return 0;
}
/* -i followed by -m is still -i */
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
{
@ -1290,7 +1350,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
if (!is_interactive(opts))
if (!is_merge(opts))
opts->type = REBASE_MERGE;
return 0;
@ -1305,12 +1365,35 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
opts->type = REBASE_INTERACTIVE;
opts->type = REBASE_MERGE;
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
return 0;
}
static enum empty_type parse_empty_value(const char *value)
{
if (!strcasecmp(value, "drop"))
return EMPTY_DROP;
else if (!strcasecmp(value, "keep"))
return EMPTY_KEEP;
else if (!strcasecmp(value, "ask"))
return EMPTY_ASK;
die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
}
static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
{
struct rebase_options *options = opt->value;
enum empty_type value = parse_empty_value(arg);
BUG_ON_OPT_NEG(unset);
options->empty = value;
return 0;
}
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@ -1346,14 +1429,14 @@ static void set_reflog_action(struct rebase_options *options)
const char *env;
struct strbuf buf = STRBUF_INIT;
if (!is_interactive(options))
if (!is_merge(options))
return;
env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
if (env && strcmp("rebase", env))
return; /* only override it if it is "rebase" */
strbuf_addf(&buf, "rebase -i (%s)", options->action);
strbuf_addf(&buf, "rebase (%s)", options->action);
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
strbuf_release(&buf);
}
@ -1376,6 +1459,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct rebase_options options = REBASE_OPTIONS_INIT;
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
int keep_base = 0;
int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
@ -1390,15 +1474,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id squash_onto;
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
N_("rebase onto given branch instead of upstream")),
OPT_BOOL(0, "keep-base", &keep_base,
N_("use the merge-base of upstream and branch as the current base")),
OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
N_("be quiet. implies --no-stat"),
REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
OPT_BIT('v', "verbose", &options.flags,
N_("display a diffstat of what changed upstream"),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
@ -1439,6 +1526,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
{ OPTION_CALLBACK, 0, "apply", &options, NULL,
N_("use apply strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_am },
{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
@ -1447,12 +1538,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("let the user edit the list of commits to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive },
OPT_SET_INT('p', "preserve-merges", &options.type,
N_("(DEPRECATED) try to recreate merges instead of "
"ignoring them"), REBASE_PRESERVE_MERGES),
OPT_SET_INT_F('p', "preserve-merges", &options.type,
N_("(DEPRECATED) try to recreate merges instead of "
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_BOOL('k', "keep-empty", &options.keep_empty,
N_("preserve empty commits during rebase")),
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
N_("how to handle commits that become empty"),
PARSE_OPT_NONEG, parse_opt_empty),
{ OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
N_("(DEPRECATED) keep empty commits"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
parse_opt_keep_empty },
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
@ -1464,9 +1561,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
OPT_BOOL(0, "allow-empty-message",
&options.allow_empty_message,
N_("allow rebasing commits with empty messages")),
OPT_BOOL_F(0, "allow-empty-message",
&options.allow_empty_message,
N_("allow rebasing commits with empty messages"),
PARSE_OPT_HIDDEN),
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
N_("mode"),
N_("try to rebase merges instead of skipping them"),
@ -1506,7 +1604,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("It looks like 'git am' is in progress. Cannot rebase."));
if (is_directory(apply_dir())) {
options.type = REBASE_AM;
options.type = REBASE_APPLY;
options.state_dir = apply_dir();
} else if (is_directory(merge_dir())) {
strbuf_reset(&buf);
@ -1518,7 +1616,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/interactive", merge_dir());
if(file_exists(buf.buf)) {
options.type = REBASE_INTERACTIVE;
options.type = REBASE_MERGE;
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
} else
options.type = REBASE_MERGE;
@ -1547,16 +1645,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
warning(_("git rebase --preserve-merges is deprecated. "
"Use --rebase-merges instead."));
if (keep_base) {
if (options.onto_name)
die(_("cannot combine '--keep-base' with '--onto'"));
if (options.root)
die(_("cannot combine '--keep-base' with '--root'"));
}
if (action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
if (action == ACTION_EDIT_TODO && !is_interactive(&options))
if (action == ACTION_EDIT_TODO && !is_merge(&options))
die(_("The --edit-todo action can only be used during "
"interactive rebase."));
if (trace2_is_enabled()) {
if (is_interactive(&options))
if (is_merge(&options))
trace2_cmd_mode("interactive");
else if (exec.nr)
trace2_cmd_mode("interactive-exec");
@ -1632,7 +1737,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
goto cleanup;
}
case ACTION_QUIT: {
if (options.type == REBASE_INTERACTIVE) {
if (options.type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
@ -1681,13 +1786,20 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
state_dir_base, cmd_live_rebase, buf.buf);
}
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
(action != ACTION_NONE) ||
(exec.nr > 0) ||
options.autosquash) {
allow_preemptive_ff = 0;
}
for (i = 0; i < options.git_am_opts.argc; i++) {
const char *option = options.git_am_opts.argv[i], *p;
if (!strcmp(option, "--committer-date-is-author-date") ||
!strcmp(option, "--ignore-date") ||
!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
options.flags |= REBASE_FORCE;
allow_preemptive_ff = 0;
else if (skip_prefix(option, "-C", &p)) {
while (*p)
if (!isdigit(*(p++)))
@ -1707,8 +1819,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
argv_array_push(&options.git_am_opts, "-q");
if (options.keep_empty)
imply_interactive(&options, "--keep-empty");
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
if (gpg_sign) {
free(options.gpg_sign_opt);
@ -1718,7 +1830,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (exec.nr) {
int i;
imply_interactive(&options, "--exec");
imply_merge(&options, "--exec");
strbuf_reset(&buf);
for (i = 0; i < exec.nr; i++)
@ -1734,7 +1846,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
else if (strcmp("no-rebase-cousins", rebase_merges))
die(_("Unknown mode: %s"), rebase_merges);
options.rebase_merges = 1;
imply_interactive(&options, "--rebase-merges");
imply_merge(&options, "--rebase-merges");
}
if (strategy_options.nr) {
@ -1753,10 +1865,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.strategy) {
options.strategy = xstrdup(options.strategy);
switch (options.type) {
case REBASE_AM:
case REBASE_APPLY:
die(_("--strategy requires --merge or --interactive"));
case REBASE_MERGE:
case REBASE_INTERACTIVE:
case REBASE_PRESERVE_MERGES:
/* compatible */
break;
@ -1769,47 +1880,65 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
if (options.type == REBASE_MERGE)
imply_interactive(&options, "--merge");
imply_merge(&options, "--merge");
if (options.root && !options.onto_name)
imply_interactive(&options, "--root without --onto");
imply_merge(&options, "--root without --onto");
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
case REBASE_PRESERVE_MERGES:
options.state_dir = merge_dir();
break;
case REBASE_AM:
options.state_dir = apply_dir();
break;
default:
/* the default rebase backend is `--am` */
options.type = REBASE_AM;
options.state_dir = apply_dir();
break;
}
if (reschedule_failed_exec > 0 && !is_interactive(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
if (options.git_am_opts.argc) {
/* all am options except -q are compatible only with --am */
if (options.git_am_opts.argc || options.type == REBASE_APPLY) {
/* all am options except -q are compatible only with --apply */
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
if (strcmp(options.git_am_opts.argv[i], "-q"))
break;
if (is_interactive(&options) && i >= 0)
die(_("cannot combine am options with either "
"interactive or merge options"));
if (i >= 0) {
if (is_merge(&options))
die(_("cannot combine apply options with "
"merge options"));
else
options.type = REBASE_APPLY;
}
}
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
imply_merge(&options, "--merge");
else if (!strcmp(options.default_backend, "apply"))
options.type = REBASE_APPLY;
else
die(_("Unknown rebase backend: %s"),
options.default_backend);
}
switch (options.type) {
case REBASE_MERGE:
case REBASE_PRESERVE_MERGES:
options.state_dir = merge_dir();
break;
case REBASE_APPLY:
options.state_dir = apply_dir();
break;
default:
BUG("options.type was just set above; should be unreachable.");
}
if (options.empty == EMPTY_UNSPECIFIED) {
if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
options.empty = EMPTY_ASK;
else if (exec.nr > 0)
options.empty = EMPTY_KEEP;
else
options.empty = EMPTY_DROP;
}
if (reschedule_failed_exec > 0 && !is_merge(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
if (options.signoff) {
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
@ -1833,15 +1962,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"'--reschedule-failed-exec'"));
}
if (options.rebase_merges) {
if (strategy_options.nr)
die(_("cannot combine '--rebase-merges' with "
"'--strategy-option'"));
if (options.strategy)
die(_("cannot combine '--rebase-merges' with "
"'--strategy'"));
}
if (!options.root) {
if (argc < 1) {
struct branch *branch;
@ -1872,7 +1992,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.squash_onto = &squash_onto;
options.onto_name = squash_onto_name =
xstrdup(oid_to_hex(&squash_onto));
}
} else
options.root_with_onto = 1;
options.upstream_name = NULL;
options.upstream = NULL;
if (argc > 1)
@ -1882,12 +2004,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
/* Make sure the branch to rebase onto is valid. */
if (!options.onto_name)
if (keep_base) {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.upstream_name);
strbuf_addstr(&buf, "...");
options.onto_name = xstrdup(buf.buf);
} else if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
if (get_oid_mb(options.onto_name, &merge_base) < 0)
die(_("'%s': need exactly one merge base"),
options.onto_name);
if (get_oid_mb(options.onto_name, &merge_base) < 0) {
if (keep_base)
die(_("'%s': need exactly one merge base with branch"),
options.upstream_name);
else
die(_("'%s': need exactly one merge base"),
options.onto_name);
}
options.onto = lookup_commit_or_die(&merge_base,
options.onto_name);
} else {
@ -1912,10 +2044,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/* Is it a local branch? */
strbuf_reset(&buf);
strbuf_addf(&buf, "refs/heads/%s", branch_name);
if (!read_ref(buf.buf, &options.orig_head))
if (!read_ref(buf.buf, &options.orig_head)) {
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
/* If not is it a valid ref (branch or commit)? */
else if (!get_oid(branch_name, &options.orig_head))
} else if (!get_oid(branch_name, &options.orig_head))
options.head_name = NULL;
else
die(_("fatal: no such branch/commit '%s'"),
@ -1968,9 +2101,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
state_dir_path("autostash", &options);
struct child_process stash = CHILD_PROCESS_INIT;
struct object_id oid;
struct commit *head =
lookup_commit_reference(the_repository,
&options.orig_head);
argv_array_pushl(&stash.args,
"stash", "create", "autostash", NULL);
@ -1991,17 +2121,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.state_dir);
write_file(autostash, "%s", oid_to_hex(&oid));
printf(_("Created autostash: %s\n"), buf.buf);
if (reset_head(&head->object.oid, "reset --hard",
if (reset_head(NULL, "reset --hard",
NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
die(_("could not reset --hard"));
printf(_("HEAD is now at %s"),
find_unique_abbrev(&head->object.oid,
DEFAULT_ABBREV));
strbuf_reset(&buf);
pp_commit_easy(CMIT_FMT_ONELINE, head, &buf);
if (buf.len > 0)
printf(" %s", buf.buf);
putchar('\n');
if (discard_index(the_repository->index) < 0 ||
repo_read_index(the_repository) < 0)
@ -2022,31 +2144,25 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
* Check if we are already based on onto with linear history,
* but this should be done only when upstream and onto are the same
* and if this is not an interactive rebase.
* in which case we could fast-forward without replacing the commits
* with new commits recreated by replaying their changes.
*
* Note that can_fast_forward() initializes merge_base, so we have to
* call it before checking allow_preemptive_ff.
*/
if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
!is_interactive(&options) && !options.restrict_revision &&
options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
&options.orig_head, &merge_base) &&
allow_preemptive_ff) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
/* Lazily switch to the target branch if needed... */
if (options.switch_to) {
struct object_id oid;
if (get_oid(options.switch_to, &oid) < 0) {
ret = !!error(_("could not parse '%s'"),
options.switch_to);
goto cleanup;
}
strbuf_reset(&buf);
strbuf_addf(&buf, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
options.switch_to);
if (reset_head(&oid, "checkout",
if (reset_head(&options.orig_head, "checkout",
options.head_name,
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
NULL, buf.buf) < 0) {
@ -2111,7 +2227,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
diff_flush(&opts);
}
if (is_interactive(&options))
if (is_merge(&options))
goto run_rebase;
/* Detach HEAD and reset the tree */

View file

@ -27,6 +27,7 @@
#include "object-store.h"
#include "protocol.h"
#include "commit-reach.h"
#include "worktree.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
@ -417,24 +418,22 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
#define HMAC_BLOCK_SIZE 64
static void hmac_sha1(unsigned char *out,
static void hmac(unsigned char *out,
const char *key_in, size_t key_len,
const char *text, size_t text_len)
{
unsigned char key[HMAC_BLOCK_SIZE];
unsigned char k_ipad[HMAC_BLOCK_SIZE];
unsigned char k_opad[HMAC_BLOCK_SIZE];
unsigned char key[GIT_MAX_BLKSZ];
unsigned char k_ipad[GIT_MAX_BLKSZ];
unsigned char k_opad[GIT_MAX_BLKSZ];
int i;
git_SHA_CTX ctx;
git_hash_ctx ctx;
/* RFC 2104 2. (1) */
memset(key, '\0', HMAC_BLOCK_SIZE);
if (HMAC_BLOCK_SIZE < key_len) {
git_SHA1_Init(&ctx);
git_SHA1_Update(&ctx, key_in, key_len);
git_SHA1_Final(key, &ctx);
memset(key, '\0', GIT_MAX_BLKSZ);
if (the_hash_algo->blksz < key_len) {
the_hash_algo->init_fn(&ctx);
the_hash_algo->update_fn(&ctx, key_in, key_len);
the_hash_algo->final_fn(key, &ctx);
} else {
memcpy(key, key_in, key_len);
}
@ -446,29 +445,29 @@ static void hmac_sha1(unsigned char *out,
}
/* RFC 2104 2. (3) & (4) */
git_SHA1_Init(&ctx);
git_SHA1_Update(&ctx, k_ipad, sizeof(k_ipad));
git_SHA1_Update(&ctx, text, text_len);
git_SHA1_Final(out, &ctx);
the_hash_algo->init_fn(&ctx);
the_hash_algo->update_fn(&ctx, k_ipad, sizeof(k_ipad));
the_hash_algo->update_fn(&ctx, text, text_len);
the_hash_algo->final_fn(out, &ctx);
/* RFC 2104 2. (6) & (7) */
git_SHA1_Init(&ctx);
git_SHA1_Update(&ctx, k_opad, sizeof(k_opad));
git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ);
git_SHA1_Final(out, &ctx);
the_hash_algo->init_fn(&ctx);
the_hash_algo->update_fn(&ctx, k_opad, sizeof(k_opad));
the_hash_algo->update_fn(&ctx, out, the_hash_algo->rawsz);
the_hash_algo->final_fn(out, &ctx);
}
static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
{
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[GIT_SHA1_RAWSZ];
unsigned char hash[GIT_MAX_RAWSZ];
strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
hmac(hash, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
strbuf_release(&buf);
/* RFC 2104 5. HMAC-SHA1-80 */
strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1));
strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, (int)the_hash_algo->hexsz, hash_to_hex(hash));
return strbuf_detach(&buf, NULL);
}
@ -818,16 +817,6 @@ static int run_update_hook(struct command *cmd)
return finish_command(&proc);
}
static int is_ref_checked_out(const char *ref)
{
if (is_bare_repository())
return 0;
if (!head_name)
return 0;
return !strcmp(head_name, ref);
}
static char *refuse_unconfigured_deny_msg =
N_("By default, updating the current branch in a non-bare repository\n"
"is denied, because it will make the index and work tree inconsistent\n"
@ -970,7 +959,7 @@ static const char *push_to_deploy(unsigned char *sha1,
if (run_command(&child))
return "Working directory has staged changes";
read_tree[3] = sha1_to_hex(sha1);
read_tree[3] = hash_to_hex(sha1);
child_process_init(&child);
child.argv = read_tree;
child.env = env->argv;
@ -987,28 +976,38 @@ static const char *push_to_deploy(unsigned char *sha1,
static const char *push_to_checkout_hook = "push-to-checkout";
static const char *push_to_checkout(unsigned char *sha1,
static const char *push_to_checkout(unsigned char *hash,
struct argv_array *env,
const char *work_tree)
{
argv_array_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
if (run_hook_le(env->argv, push_to_checkout_hook,
sha1_to_hex(sha1), NULL))
hash_to_hex(hash), NULL))
return "push-to-checkout hook declined";
else
return NULL;
}
static const char *update_worktree(unsigned char *sha1)
static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
{
const char *retval;
const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
const char *retval, *work_tree, *git_dir = NULL;
struct argv_array env = ARGV_ARRAY_INIT;
if (worktree && worktree->path)
work_tree = worktree->path;
else if (git_work_tree_cfg)
work_tree = git_work_tree_cfg;
else
work_tree = "..";
if (is_bare_repository())
return "denyCurrentBranch = updateInstead needs a worktree";
if (worktree)
git_dir = get_worktree_git_dir(worktree);
if (!git_dir)
git_dir = get_git_dir();
argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
if (!find_hook(push_to_checkout_hook))
retval = push_to_deploy(sha1, &env, work_tree);
@ -1028,6 +1027,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct object_id *old_oid = &cmd->old_oid;
struct object_id *new_oid = &cmd->new_oid;
int do_update_worktree = 0;
const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
/* only refs/... are allowed */
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@ -1039,7 +1039,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
free(namespaced_name);
namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
if (is_ref_checked_out(namespaced_name)) {
if (worktree) {
switch (deny_current_branch) {
case DENY_IGNORE:
break;
@ -1071,7 +1071,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
return "deletion prohibited";
}
if (head_name && !strcmp(namespaced_name, head_name)) {
if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
switch (deny_delete_current) {
case DENY_IGNORE:
break;
@ -1120,7 +1120,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
if (do_update_worktree) {
ret = update_worktree(new_oid->hash);
ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
if (ret)
return ret;
}

View file

@ -560,15 +560,16 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
flags |= EXPIRE_REFLOGS_DRY_RUN;
else if (starts_with(arg, "--expire=")) {
if (parse_expiry_date(arg + 9, &cb.cmd.expire_total))
else if (skip_prefix(arg, "--expire=", &arg)) {
if (parse_expiry_date(arg, &cb.cmd.expire_total))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_TOTAL;
}
else if (starts_with(arg, "--expire-unreachable=")) {
if (parse_expiry_date(arg + 21, &cb.cmd.expire_unreachable))
else if (skip_prefix(arg, "--expire-unreachable=", &arg)) {
if (parse_expiry_date(arg, &cb.cmd.expire_unreachable))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_UNREACH;
}

View file

@ -6,6 +6,7 @@
#include "string-list.h"
#include "strbuf.h"
#include "run-command.h"
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
#include "object-store.h"
@ -248,9 +249,8 @@ static int add(int argc, const char **argv)
struct branch_info {
char *remote_name;
struct string_list merge;
enum {
NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE, REBASE_MERGES
} rebase;
enum rebase_type rebase;
char *push_remote_name;
};
static struct string_list branch_list = STRING_LIST_INIT_NODUP;
@ -264,59 +264,69 @@ static const char *abbrev_ref(const char *name, const char *prefix)
static int config_read_branches(const char *key, const char *value, void *cb)
{
if (starts_with(key, "branch.")) {
const char *orig_key = key;
char *name;
struct string_list_item *item;
struct branch_info *info;
enum { REMOTE, MERGE, REBASE } type;
size_t key_len;
const char *orig_key = key;
char *name;
struct string_list_item *item;
struct branch_info *info;
enum { REMOTE, MERGE, REBASE, PUSH_REMOTE } type;
size_t key_len;
key += 7;
if (strip_suffix(key, ".remote", &key_len)) {
name = xmemdupz(key, key_len);
type = REMOTE;
} else if (strip_suffix(key, ".merge", &key_len)) {
name = xmemdupz(key, key_len);
type = MERGE;
} else if (strip_suffix(key, ".rebase", &key_len)) {
name = xmemdupz(key, key_len);
type = REBASE;
} else
return 0;
if (!starts_with(key, "branch."))
return 0;
item = string_list_insert(&branch_list, name);
key += strlen("branch.");
if (strip_suffix(key, ".remote", &key_len))
type = REMOTE;
else if (strip_suffix(key, ".merge", &key_len))
type = MERGE;
else if (strip_suffix(key, ".rebase", &key_len))
type = REBASE;
else if (strip_suffix(key, ".pushremote", &key_len))
type = PUSH_REMOTE;
else
return 0;
name = xmemdupz(key, key_len);
if (!item->util)
item->util = xcalloc(1, sizeof(struct branch_info));
info = item->util;
if (type == REMOTE) {
if (info->remote_name)
warning(_("more than one %s"), orig_key);
info->remote_name = xstrdup(value);
} else if (type == MERGE) {
char *space = strchr(value, ' ');
value = abbrev_branch(value);
while (space) {
char *merge;
merge = xstrndup(value, space - value);
string_list_append(&info->merge, merge);
value = abbrev_branch(space + 1);
space = strchr(value, ' ');
}
string_list_append(&info->merge, xstrdup(value));
} else {
int v = git_parse_maybe_bool(value);
if (v >= 0)
info->rebase = v;
else if (!strcmp(value, "preserve"))
info->rebase = NORMAL_REBASE;
else if (!strcmp(value, "merges"))
info->rebase = REBASE_MERGES;
else if (!strcmp(value, "interactive"))
info->rebase = INTERACTIVE_REBASE;
item = string_list_insert(&branch_list, name);
if (!item->util)
item->util = xcalloc(1, sizeof(struct branch_info));
info = item->util;
switch (type) {
case REMOTE:
if (info->remote_name)
warning(_("more than one %s"), orig_key);
info->remote_name = xstrdup(value);
break;
case MERGE: {
char *space = strchr(value, ' ');
value = abbrev_branch(value);
while (space) {
char *merge;
merge = xstrndup(value, space - value);
string_list_append(&info->merge, merge);
value = abbrev_branch(space + 1);
space = strchr(value, ' ');
}
string_list_append(&info->merge, xstrdup(value));
break;
}
case REBASE:
/*
* Consider invalid values as false and check the
* truth value with >= REBASE_TRUE.
*/
info->rebase = rebase_parse_value(value);
break;
case PUSH_REMOTE:
if (info->push_remote_name)
warning(_("more than one %s"), orig_key);
info->push_remote_name = xstrdup(value);
break;
default:
BUG("unexpected type=%d", type);
}
return 0;
}
@ -605,6 +615,56 @@ static int migrate_file(struct remote *remote)
return 0;
}
struct push_default_info
{
const char *old_name;
enum config_scope scope;
struct strbuf origin;
int linenr;
};
static int config_read_push_default(const char *key, const char *value,
void *cb)
{
struct push_default_info* info = cb;
if (strcmp(key, "remote.pushdefault") ||
!value || strcmp(value, info->old_name))
return 0;
info->scope = current_config_scope();
strbuf_reset(&info->origin);
strbuf_addstr(&info->origin, current_config_name());
info->linenr = current_config_line();
return 0;
}
static void handle_push_default(const char* old_name, const char* new_name)
{
struct push_default_info push_default = {
old_name, CONFIG_SCOPE_UNKNOWN, STRBUF_INIT, -1 };
git_config(config_read_push_default, &push_default);
if (push_default.scope >= CONFIG_SCOPE_COMMAND)
; /* pass */
else if (push_default.scope >= CONFIG_SCOPE_LOCAL) {
int result = git_config_set_gently("remote.pushDefault",
new_name);
if (new_name && result && result != CONFIG_NOTHING_SET)
die(_("could not set '%s'"), "remote.pushDefault");
else if (!new_name && result && result != CONFIG_NOTHING_SET)
die(_("could not unset '%s'"), "remote.pushDefault");
} else if (push_default.scope >= CONFIG_SCOPE_SYSTEM) {
/* warn */
warning(_("The %s configuration remote.pushDefault in:\n"
"\t%s:%d\n"
"now names the non-existent remote '%s'"),
config_scope_name(push_default.scope),
push_default.origin.buf, push_default.linenr,
old_name);
}
}
static int mv(int argc, const char **argv)
{
struct option options[] = {
@ -680,6 +740,11 @@ static int mv(int argc, const char **argv)
strbuf_addf(&buf, "branch.%s.remote", item->string);
git_config_set(buf.buf, rename.new_name);
}
if (info->push_remote_name && !strcmp(info->push_remote_name, rename.old_name)) {
strbuf_reset(&buf);
strbuf_addf(&buf, "branch.%s.pushremote", item->string);
git_config_set(buf.buf, rename.new_name);
}
}
if (!refspec_updated)
@ -693,9 +758,8 @@ static int mv(int argc, const char **argv)
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;
int flag = 0;
struct object_id oid;
read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
read_ref_full(item->string, RESOLVE_REF_READING, NULL, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
@ -736,6 +800,9 @@ static int mv(int argc, const char **argv)
die(_("creating '%s' failed"), buf.buf);
}
string_list_clear(&remote_branches, 1);
handle_push_default(rename.old_name, rename.new_name);
return 0;
}
@ -782,6 +849,13 @@ static int rm(int argc, const char **argv)
die(_("could not unset '%s'"), buf.buf);
}
}
if (info->push_remote_name && !strcmp(info->push_remote_name, remote->name)) {
strbuf_reset(&buf);
strbuf_addf(&buf, "branch.%s.pushremote", item->string);
result = git_config_set_gently(buf.buf, NULL);
if (result && result != CONFIG_NOTHING_SET)
die(_("could not unset '%s'"), buf.buf);
}
}
/*
@ -814,6 +888,8 @@ static int rm(int argc, const char **argv)
strbuf_addf(&buf, "remote.%s", remote->name);
if (git_config_rename_section(buf.buf, NULL) < 1)
return error(_("Could not remove config section '%s'"), buf.buf);
handle_push_default(remote->name, NULL);
}
return result;
@ -944,7 +1020,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
return 0;
if ((n = strlen(branch_item->string)) > show_info->width)
show_info->width = n;
if (branch_info->rebase)
if (branch_info->rebase >= REBASE_TRUE)
show_info->any_rebase = 1;
item = string_list_insert(show_info->list, branch_item->string);
@ -961,16 +1037,16 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
int width = show_info->width + 4;
int i;
if (branch_info->rebase && branch_info->merge.nr > 1) {
if (branch_info->rebase >= REBASE_TRUE && branch_info->merge.nr > 1) {
error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
item->string);
return 0;
}
printf(" %-*s ", show_info->width, item->string);
if (branch_info->rebase) {
if (branch_info->rebase >= REBASE_TRUE) {
const char *msg;
if (branch_info->rebase == INTERACTIVE_REBASE)
if (branch_info->rebase == REBASE_INTERACTIVE)
msg = _("rebases interactively onto remote %s");
else if (branch_info->rebase == REBASE_MERGES)
msg = _("rebases interactively (with merges) onto "

View file

@ -11,6 +11,7 @@
#include "midx.h"
#include "packfile.h"
#include "object-store.h"
#include "promisor-remote.h"
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
@ -190,7 +191,7 @@ static int write_oid(const struct object_id *oid, struct packed_git *pack,
die(_("could not start pack-objects to repack promisor objects"));
}
xwrite(cmd->in, oid_to_hex(oid), GIT_SHA1_HEXSZ);
xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz);
xwrite(cmd->in, "\n", 1);
return 0;
}
@ -232,6 +233,13 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
/*
* pack-objects creates the .pack and .idx files, but not the
* .promisor file. Create the .promisor file, which is empty.
*
* NEEDSWORK: fetch-pack sometimes generates non-empty
* .promisor files containing the ref names and associated
* hashes at the point of generation of the corresponding
* packfile, but this would not preserve their contents. Maybe
* concatenate the contents of all .promisor files instead of
* just creating a new empty file.
*/
promisor_name = mkpathdup("%s-%s.promisor", packtmp,
line.buf);
@ -361,7 +369,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd.args, "--all");
argv_array_push(&cmd.args, "--reflog");
argv_array_push(&cmd.args, "--indexed-objects");
if (repository_format_partial_clone)
if (has_promisor_remote())
argv_array_push(&cmd.args, "--exclude-promisor-objects");
if (write_bitmaps > 0)
argv_array_push(&cmd.args, "--write-bitmap-index");
@ -561,7 +569,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
remove_temporary_files();
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
write_midx_file(get_object_directory());
write_midx_file(get_object_directory(), 0);
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);

View file

@ -272,7 +272,7 @@ static int import_object(struct object_id *oid, enum object_type type,
return error(_("unable to spawn mktree"));
}
if (strbuf_read(&result, cmd.out, 41) < 0) {
if (strbuf_read(&result, cmd.out, the_hash_algo->hexsz + 1) < 0) {
error_errno(_("unable to read from mktree"));
close(fd);
close(cmd.out);
@ -358,14 +358,15 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv)
struct strbuf new_parents = STRBUF_INIT;
const char *parent_start, *parent_end;
int i;
const unsigned hexsz = the_hash_algo->hexsz;
/* find existing parents */
parent_start = buf->buf;
parent_start += GIT_SHA1_HEXSZ + 6; /* "tree " + "hex sha1" + "\n" */
parent_start += hexsz + 6; /* "tree " + "hex sha1" + "\n" */
parent_end = parent_start;
while (starts_with(parent_end, "parent "))
parent_end += 48; /* "parent " + "hex sha1" + "\n" */
parent_end += hexsz + 8; /* "parent " + "hex sha1" + "\n" */
/* prepare new parents */
for (i = 0; i < argc; i++) {
@ -408,7 +409,8 @@ static int check_one_mergetag(struct commit *commit,
struct tag *tag;
int i;
hash_object_file(extra->value, extra->len, type_name(OBJ_TAG), &tag_oid);
hash_object_file(the_hash_algo, extra->value, extra->len,
type_name(OBJ_TAG), &tag_oid);
tag = lookup_tag(the_repository, &tag_oid);
if (!tag)
return error(_("bad mergetag in commit '%s'"), ref);
@ -421,7 +423,7 @@ static int check_one_mergetag(struct commit *commit,
if (get_oid(mergetag_data->argv[i], &oid) < 0)
return error(_("not a valid object name: '%s'"),
mergetag_data->argv[i]);
if (oideq(&tag->tagged->oid, &oid))
if (oideq(get_tagged_oid(tag), &oid))
return 0; /* found */
}

View file

@ -30,8 +30,9 @@
static const char * const git_reset_usage[] = {
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
NULL
};
@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int reset_type = NONE, update_ref_status = 0, quiet = 0;
int patch_mode = 0, unborn;
const char *rev;
int patch_mode = 0, pathspec_file_nul = 0, unborn;
const char *rev, *pathspec_from_file = NULL;
struct object_id oid;
struct pathspec pathspec;
int intent_to_add = 0;
@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END()
};
@ -316,11 +319,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
if (pathspec_from_file) {
if (patch_mode)
die(_("--pathspec-from-file is incompatible with --patch"));
if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
oidcpy(&oid, the_hash_algo->empty_tree);
} else if (!pathspec.nr) {
} else if (!pathspec.nr && !patch_mode) {
struct commit *commit;
if (get_oid_committish(rev, &oid))
die(_("Failed to resolve '%s' as a valid revision."), rev);

View file

@ -18,7 +18,6 @@
#include "reflog-walk.h"
#include "oidset.h"
#include "packfile.h"
#include "object-store.h"
static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@ -254,11 +253,26 @@ static int finish_object(struct object *obj, const char *name, void *cb_data)
static void show_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
struct rev_info *revs = info->revs;
if (finish_object(obj, name, cb_data))
return;
display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
if (revs->count) {
/*
* The object count is always accumulated in the .count_right
* field for traversal that is not a left-right traversal,
* and cmd_rev_list() made sure that a .count request that
* wants to count non-commit objects, which is handled by
* the show_object() callback, does not ask for .left_right.
*/
revs->count_right++;
return;
}
if (arg_show_object_names)
show_object_with_name(stdout, obj, name);
else
@ -365,6 +379,79 @@ static inline int parse_missing_action_value(const char *value)
return 0;
}
static int try_bitmap_count(struct rev_info *revs,
struct list_objects_filter_options *filter)
{
uint32_t commit_count = 0,
tag_count = 0,
tree_count = 0,
blob_count = 0;
int max_count;
struct bitmap_index *bitmap_git;
/* This function only handles counting, not general traversal. */
if (!revs->count)
return -1;
/*
* A bitmap result can't know left/right, etc, because we don't
* actually traverse.
*/
if (revs->left_right || revs->cherry_mark)
return -1;
/*
* If we're counting reachable objects, we can't handle a max count of
* commits to traverse, since we don't know which objects go with which
* commit.
*/
if (revs->max_count >= 0 &&
(revs->tag_objects || revs->tree_objects || revs->blob_objects))
return -1;
/*
* This must be saved before doing any walking, since the revision
* machinery will count it down to zero while traversing.
*/
max_count = revs->max_count;
bitmap_git = prepare_bitmap_walk(revs, filter);
if (!bitmap_git)
return -1;
count_bitmap_commit_list(bitmap_git, &commit_count,
revs->tree_objects ? &tree_count : NULL,
revs->blob_objects ? &blob_count : NULL,
revs->tag_objects ? &tag_count : NULL);
if (max_count >= 0 && max_count < commit_count)
commit_count = max_count;
printf("%d\n", commit_count + tree_count + blob_count + tag_count);
free_bitmap_index(bitmap_git);
return 0;
}
static int try_bitmap_traversal(struct rev_info *revs,
struct list_objects_filter_options *filter)
{
struct bitmap_index *bitmap_git;
/*
* We can't use a bitmap result with a traversal limit, since the set
* of commits we'd get would be essentially random.
*/
if (revs->max_count >= 0)
return -1;
bitmap_git = prepare_bitmap_walk(revs, filter);
if (!bitmap_git)
return -1;
traverse_bitmap_commit_list(bitmap_git, revs, &show_object_fast);
free_bitmap_index(bitmap_git);
return 0;
}
int cmd_rev_list(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@ -471,10 +558,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
parse_list_objects_filter(&filter_options, arg);
if (filter_options.choice && !revs.blob_objects)
die(_("object filtering requires --objects"));
if (filter_options.choice == LOFC_SPARSE_OID &&
!filter_options.sparse_oid_value)
die(_("invalid sparse value '%s'"),
filter_options.filter_spec);
continue;
}
if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
@ -526,8 +609,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (revs.show_notes)
die(_("rev-list does not support display of notes"));
if (filter_options.choice && use_bitmap_index)
die(_("cannot combine --use-bitmap-index with object filtering"));
if (revs.count &&
(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
(revs.left_right || revs.cherry_mark))
die(_("marked counting is incompatible with --objects"));
save_commit_buffer = (revs.verbose_header ||
revs.grep_filter.pattern_list ||
@ -538,28 +623,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (show_progress)
progress = start_delayed_progress(show_progress, 0);
if (use_bitmap_index && !revs.prune) {
if (revs.count && !revs.left_right && !revs.cherry_mark) {
uint32_t commit_count;
int max_count = revs.max_count;
struct bitmap_index *bitmap_git;
if ((bitmap_git = prepare_bitmap_walk(&revs))) {
count_bitmap_commit_list(bitmap_git, &commit_count, NULL, NULL, NULL);
if (max_count >= 0 && max_count < commit_count)
commit_count = max_count;
printf("%d\n", commit_count);
free_bitmap_index(bitmap_git);
return 0;
}
} else if (revs.max_count < 0 &&
revs.tag_objects && revs.tree_objects && revs.blob_objects) {
struct bitmap_index *bitmap_git;
if ((bitmap_git = prepare_bitmap_walk(&revs))) {
traverse_bitmap_commit_list(bitmap_git, &show_object_fast);
free_bitmap_index(bitmap_git);
return 0;
}
}
if (use_bitmap_index) {
if (!try_bitmap_count(&revs, &filter_options))
return 0;
if (!try_bitmap_traversal(&revs, &filter_options))
return 0;
}
if (prepare_revision_walk(&revs))

View file

@ -593,6 +593,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
const char *name = NULL;
struct object_context unused;
struct strbuf buf = STRBUF_INIT;
const int hexsz = the_hash_algo->hexsz;
if (argc > 1 && !strcmp("--parseopt", argv[1]))
return cmd_parseopt(argc - 1, argv + 1, prefix);
@ -730,8 +731,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
abbrev = strtoul(arg, NULL, 10);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
else if (40 <= abbrev)
abbrev = 40;
else if (hexsz <= abbrev)
abbrev = hexsz;
continue;
}
if (!strcmp(arg, "--sq")) {
@ -802,6 +803,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
const char *work_tree = get_git_work_tree();
if (work_tree)
puts(work_tree);
else
die("this operation must be run in a work tree");
continue;
}
if (!strcmp(arg, "--show-superproject-working-tree")) {
@ -918,6 +921,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
show_datestring("--min-age=", arg);
continue;
}
if (opt_with_value(arg, "--show-object-format", &arg)) {
const char *val = arg ? arg : "storage";
if (strcmp(val, "storage") &&
strcmp(val, "input") &&
strcmp(val, "output"))
die("unknown mode for --show-object-format: %s",
arg);
puts(the_hash_algo->name);
continue;
}
if (show_flag(arg) && verify)
die_no_single_rev(quiet);
continue;

View file

@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)
}
static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
static int ignore_unmatch = 0;
static int ignore_unmatch = 0, pathspec_file_nul;
static char *pathspec_from_file;
static struct option builtin_rm_options[] = {
OPT__DRY_RUN(&show_only, N_("dry run")),
@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {
OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")),
OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
N_("exit with a zero status even if nothing matched")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
};
@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, builtin_rm_options,
builtin_rm_usage, 0);
if (!argc)
usage_with_options(builtin_rm_usage, builtin_rm_options);
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD,
prefix, argv);
if (pathspec_from_file) {
if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_CWD,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
if (!pathspec.nr)
die(_("No pathspec was given. Which files should I remove?"));
if (!index_only)
setup_work_tree();
@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die(_("index file corrupt"));
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD,
prefix, argv);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
seen = xcalloc(pathspec.nr, 1);

View file

@ -536,7 +536,7 @@ static void append_one_rev(const char *av)
append_ref(av, &revkey, 0);
return;
}
if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
if (strpbrk(av, "*?[")) {
/* glob style match */
int saved_matches = ref_name_cnt;

View file

@ -11,6 +11,7 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
unsigned nr;
unsigned int version;
static unsigned int top_index[256];
const unsigned hashsz = the_hash_algo->rawsz;
if (argc != 1)
usage(show_index_usage);
@ -36,23 +37,23 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
}
if (version == 1) {
for (i = 0; i < nr; i++) {
unsigned int offset, entry[6];
unsigned int offset, entry[(GIT_MAX_RAWSZ + 4) / sizeof(unsigned int)];
if (fread(entry, 4 + 20, 1, stdin) != 1)
if (fread(entry, 4 + hashsz, 1, stdin) != 1)
die("unable to read entry %u/%u", i, nr);
offset = ntohl(entry[0]);
printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
printf("%u %s\n", offset, hash_to_hex((void *)(entry+1)));
}
} else {
unsigned off64_nr = 0;
struct {
unsigned char sha1[20];
struct object_id oid;
uint32_t crc;
uint32_t off;
} *entries;
ALLOC_ARRAY(entries, nr);
for (i = 0; i < nr; i++)
if (fread(entries[i].sha1, 20, 1, stdin) != 1)
if (fread(entries[i].oid.hash, hashsz, 1, stdin) != 1)
die("unable to read sha1 %u/%u", i, nr);
for (i = 0; i < nr; i++)
if (fread(&entries[i].crc, 4, 1, stdin) != 1)
@ -77,7 +78,7 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
}
printf("%" PRIuMAX " %s (%08"PRIx32")\n",
(uintmax_t) offset,
sha1_to_hex(entries[i].sha1),
oid_to_hex(&entries[i].oid),
ntohl(entries[i].crc));
}
free(entries);

View file

@ -0,0 +1,631 @@
#include "builtin.h"
#include "config.h"
#include "dir.h"
#include "parse-options.h"
#include "pathspec.h"
#include "repository.h"
#include "run-command.h"
#include "strbuf.h"
#include "string-list.h"
#include "cache.h"
#include "cache-tree.h"
#include "lockfile.h"
#include "resolve-undo.h"
#include "unpack-trees.h"
#include "wt-status.h"
#include "quote.h"
static const char *empty_base = "";
static char const * const builtin_sparse_checkout_usage[] = {
N_("git sparse-checkout (init|list|set|add|disable) <options>"),
NULL
};
static char *get_sparse_checkout_filename(void)
{
return git_pathdup("info/sparse-checkout");
}
static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
{
int i;
for (i = 0; i < pl->nr; i++) {
struct path_pattern *p = pl->patterns[i];
if (p->flags & PATTERN_FLAG_NEGATIVE)
fprintf(fp, "!");
fprintf(fp, "%s", p->pattern);
if (p->flags & PATTERN_FLAG_MUSTBEDIR)
fprintf(fp, "/");
fprintf(fp, "\n");
}
}
static int sparse_checkout_list(int argc, const char **argv)
{
struct pattern_list pl;
char *sparse_filename;
int res;
memset(&pl, 0, sizeof(pl));
pl.use_cone_patterns = core_sparse_checkout_cone;
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
free(sparse_filename);
if (res < 0) {
warning(_("this worktree is not sparse (sparse-checkout file may not exist)"));
return 0;
}
if (pl.use_cone_patterns) {
int i;
struct pattern_entry *pe;
struct hashmap_iter iter;
struct string_list sl = STRING_LIST_INIT_DUP;
hashmap_for_each_entry(&pl.recursive_hashmap, &iter, pe, ent) {
/* pe->pattern starts with "/", skip it */
string_list_insert(&sl, pe->pattern + 1);
}
string_list_sort(&sl);
for (i = 0; i < sl.nr; i++) {
quote_c_style(sl.items[i].string, NULL, stdout, 0);
printf("\n");
}
return 0;
}
write_patterns_to_file(stdout, &pl);
clear_pattern_list(&pl);
return 0;
}
static int update_working_directory(struct pattern_list *pl)
{
int result = 0;
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
struct object_id oid;
struct tree *tree;
struct tree_desc t;
struct repository *r = the_repository;
if (repo_read_index_unmerged(r))
die(_("you need to resolve your current index first"));
if (get_oid("HEAD", &oid))
return 0;
tree = parse_tree_indirect(&oid);
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
memset(&o, 0, sizeof(o));
o.verbose_update = isatty(2);
o.merge = 1;
o.update = 1;
o.fn = oneway_merge;
o.head_idx = -1;
o.src_index = r->index;
o.dst_index = r->index;
o.skip_sparse_checkout = 0;
o.pl = pl;
o.keep_pattern_list = !!pl;
resolve_undo_clear_index(r->index);
setup_work_tree();
cache_tree_free(&r->index->cache_tree);
repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
core_apply_sparse_checkout = 1;
result = unpack_trees(1, &t, &o);
if (!result) {
prime_cache_tree(r, r->index, tree);
write_locked_index(r->index, &lock_file, COMMIT_LOCK);
} else
rollback_lock_file(&lock_file);
return result;
}
static char *escaped_pattern(char *pattern)
{
char *p = pattern;
struct strbuf final = STRBUF_INIT;
while (*p) {
if (is_glob_special(*p))
strbuf_addch(&final, '\\');
strbuf_addch(&final, *p);
p++;
}
return strbuf_detach(&final, NULL);
}
static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
{
int i;
struct pattern_entry *pe;
struct hashmap_iter iter;
struct string_list sl = STRING_LIST_INIT_DUP;
struct strbuf parent_pattern = STRBUF_INIT;
hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) {
if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL))
continue;
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern,
&parent_pattern))
string_list_insert(&sl, pe->pattern);
}
string_list_sort(&sl);
string_list_remove_duplicates(&sl, 0);
fprintf(fp, "/*\n!/*/\n");
for (i = 0; i < sl.nr; i++) {
char *pattern = escaped_pattern(sl.items[i].string);
if (strlen(pattern))
fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
free(pattern);
}
string_list_clear(&sl, 0);
hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern,
&parent_pattern))
string_list_insert(&sl, pe->pattern);
}
strbuf_release(&parent_pattern);
string_list_sort(&sl);
string_list_remove_duplicates(&sl, 0);
for (i = 0; i < sl.nr; i++) {
char *pattern = escaped_pattern(sl.items[i].string);
fprintf(fp, "%s/\n", pattern);
free(pattern);
}
}
static int write_patterns_and_update(struct pattern_list *pl)
{
char *sparse_filename;
FILE *fp;
int fd;
struct lock_file lk = LOCK_INIT;
int result;
sparse_filename = get_sparse_checkout_filename();
if (safe_create_leading_directories(sparse_filename))
die(_("failed to create directory for sparse-checkout file"));
fd = hold_lock_file_for_update(&lk, sparse_filename,
LOCK_DIE_ON_ERROR);
result = update_working_directory(pl);
if (result) {
rollback_lock_file(&lk);
free(sparse_filename);
clear_pattern_list(pl);
update_working_directory(NULL);
return result;
}
fp = xfdopen(fd, "w");
if (core_sparse_checkout_cone)
write_cone_to_file(fp, pl);
else
write_patterns_to_file(fp, pl);
fflush(fp);
commit_lock_file(&lk);
free(sparse_filename);
clear_pattern_list(pl);
return 0;
}
enum sparse_checkout_mode {
MODE_NO_PATTERNS = 0,
MODE_ALL_PATTERNS = 1,
MODE_CONE_PATTERNS = 2,
};
static int set_config(enum sparse_checkout_mode mode)
{
const char *config_path;
if (git_config_set_gently("extensions.worktreeConfig", "true")) {
error(_("failed to set extensions.worktreeConfig setting"));
return 1;
}
config_path = git_path("config.worktree");
git_config_set_in_file_gently(config_path,
"core.sparseCheckout",
mode ? "true" : NULL);
git_config_set_in_file_gently(config_path,
"core.sparseCheckoutCone",
mode == MODE_CONE_PATTERNS ? "true" : NULL);
return 0;
}
static char const * const builtin_sparse_checkout_init_usage[] = {
N_("git sparse-checkout init [--cone]"),
NULL
};
static struct sparse_checkout_init_opts {
int cone_mode;
} init_opts;
static int sparse_checkout_init(int argc, const char **argv)
{
struct pattern_list pl;
char *sparse_filename;
int res;
struct object_id oid;
int mode;
struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
OPT_BOOL(0, "cone", &init_opts.cone_mode,
N_("initialize the sparse-checkout in cone mode")),
OPT_END(),
};
repo_read_index(the_repository);
require_clean_work_tree(the_repository,
N_("initialize sparse-checkout"), NULL, 1, 0);
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_init_options,
builtin_sparse_checkout_init_usage, 0);
if (init_opts.cone_mode) {
mode = MODE_CONE_PATTERNS;
core_sparse_checkout_cone = 1;
} else
mode = MODE_ALL_PATTERNS;
if (set_config(mode))
return 1;
memset(&pl, 0, sizeof(pl));
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
core_apply_sparse_checkout = 1;
return update_working_directory(NULL);
}
if (get_oid("HEAD", &oid)) {
FILE *fp;
/* assume we are in a fresh repo, but update the sparse-checkout file */
fp = xfopen(sparse_filename, "w");
if (!fp)
die(_("failed to open '%s'"), sparse_filename);
free(sparse_filename);
fprintf(fp, "/*\n!/*/\n");
fclose(fp);
return 0;
}
strbuf_addstr(&pattern, "/*");
add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
strbuf_addstr(&pattern, "!/*/");
add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
return write_patterns_and_update(&pl);
}
static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
{
struct pattern_entry *e = xmalloc(sizeof(*e));
e->patternlen = path->len;
e->pattern = strbuf_detach(path, NULL);
hashmap_entry_init(&e->ent,
ignore_case ?
strihash(e->pattern) :
strhash(e->pattern));
hashmap_add(&pl->recursive_hashmap, &e->ent);
while (e->patternlen) {
char *slash = strrchr(e->pattern, '/');
char *oldpattern = e->pattern;
size_t newlen;
if (slash == e->pattern)
break;
newlen = slash - e->pattern;
e = xmalloc(sizeof(struct pattern_entry));
e->patternlen = newlen;
e->pattern = xstrndup(oldpattern, newlen);
hashmap_entry_init(&e->ent,
ignore_case ?
strihash(e->pattern) :
strhash(e->pattern));
if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
hashmap_add(&pl->parent_hashmap, &e->ent);
}
}
static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
{
strbuf_trim(line);
strbuf_trim_trailing_dir_sep(line);
if (strbuf_normalize_path(line))
die(_("could not normalize path %s"), line->buf);
if (!line->len)
return;
if (line->buf[0] != '/')
strbuf_insertstr(line, 0, "/");
insert_recursive_pattern(pl, line);
}
static char const * const builtin_sparse_checkout_set_usage[] = {
N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
NULL
};
static struct sparse_checkout_set_opts {
int use_stdin;
} set_opts;
static void add_patterns_from_input(struct pattern_list *pl,
int argc, const char **argv)
{
int i;
if (core_sparse_checkout_cone) {
struct strbuf line = STRBUF_INIT;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
if (set_opts.use_stdin) {
struct strbuf unquoted = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
if (line.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, line.buf, NULL))
die(_("unable to unquote C-style string '%s'"),
line.buf);
strbuf_swap(&unquoted, &line);
}
strbuf_to_cone_pattern(&line, pl);
}
strbuf_release(&unquoted);
} else {
for (i = 0; i < argc; i++) {
strbuf_setlen(&line, 0);
strbuf_addstr(&line, argv[i]);
strbuf_to_cone_pattern(&line, pl);
}
}
} else {
if (set_opts.use_stdin) {
struct strbuf line = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
size_t len;
char *buf = strbuf_detach(&line, &len);
add_pattern(buf, empty_base, 0, pl, 0);
}
} else {
for (i = 0; i < argc; i++)
add_pattern(argv[i], empty_base, 0, pl, 0);
}
}
}
enum modify_type {
REPLACE,
ADD,
};
static void add_patterns_cone_mode(int argc, const char **argv,
struct pattern_list *pl)
{
struct strbuf buffer = STRBUF_INIT;
struct pattern_entry *pe;
struct hashmap_iter iter;
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
add_patterns_from_input(pl, argc, argv);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
&existing, NULL))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern, &buffer) ||
!hashmap_contains_parent(&pl->parent_hashmap,
pe->pattern, &buffer)) {
strbuf_reset(&buffer);
strbuf_addstr(&buffer, pe->pattern);
insert_recursive_pattern(pl, &buffer);
}
}
clear_pattern_list(&existing);
strbuf_release(&buffer);
}
static void add_patterns_literal(int argc, const char **argv,
struct pattern_list *pl)
{
char *sparse_filename = get_sparse_checkout_filename();
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
pl, NULL))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
add_patterns_from_input(pl, argc, argv);
}
static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
{
int result;
int changed_config = 0;
struct pattern_list pl;
memset(&pl, 0, sizeof(pl));
switch (m) {
case ADD:
if (core_sparse_checkout_cone)
add_patterns_cone_mode(argc, argv, &pl);
else
add_patterns_literal(argc, argv, &pl);
break;
case REPLACE:
add_patterns_from_input(&pl, argc, argv);
break;
}
if (!core_apply_sparse_checkout) {
set_config(MODE_ALL_PATTERNS);
core_apply_sparse_checkout = 1;
changed_config = 1;
}
result = write_patterns_and_update(&pl);
if (result && changed_config)
set_config(MODE_NO_PATTERNS);
clear_pattern_list(&pl);
return result;
}
static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
enum modify_type m)
{
static struct option builtin_sparse_checkout_set_options[] = {
OPT_BOOL(0, "stdin", &set_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
};
repo_read_index(the_repository);
require_clean_work_tree(the_repository,
N_("set sparse-checkout patterns"), NULL, 1, 0);
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
PARSE_OPT_KEEP_UNKNOWN);
return modify_pattern_list(argc, argv, m);
}
static int sparse_checkout_disable(int argc, const char **argv)
{
struct pattern_list pl;
struct strbuf match_all = STRBUF_INIT;
repo_read_index(the_repository);
require_clean_work_tree(the_repository,
N_("disable sparse-checkout"), NULL, 1, 0);
memset(&pl, 0, sizeof(pl));
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl.use_cone_patterns = 0;
core_apply_sparse_checkout = 1;
strbuf_addstr(&match_all, "/*");
add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
if (update_working_directory(&pl))
die(_("error while refreshing working directory"));
clear_pattern_list(&pl);
return set_config(MODE_NO_PATTERNS);
}
int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
{
static struct option builtin_sparse_checkout_options[] = {
OPT_END(),
};
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_sparse_checkout_usage,
builtin_sparse_checkout_options);
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_options,
builtin_sparse_checkout_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
git_config(git_default_config, NULL);
if (argc > 0) {
if (!strcmp(argv[0], "list"))
return sparse_checkout_list(argc, argv);
if (!strcmp(argv[0], "init"))
return sparse_checkout_init(argc, argv);
if (!strcmp(argv[0], "set"))
return sparse_checkout_set(argc, argv, prefix, REPLACE);
if (!strcmp(argv[0], "add"))
return sparse_checkout_set(argc, argv, prefix, ADD);
if (!strcmp(argv[0], "disable"))
return sparse_checkout_disable(argc, argv);
}
usage_with_options(builtin_sparse_checkout_usage,
builtin_sparse_checkout_options);
}

View file

@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {
N_("git stash clear"),
N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
" [--] [<pathspec>...]]"),
N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [<message>]"),
@ -396,7 +397,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
const struct object_id *bases[1];
read_cache_preload(NULL);
if (refresh_cache(REFRESH_QUIET))
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
return -1;
if (write_cache_as_tree(&c_tree, 0, NULL))
@ -427,6 +428,8 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
return error(_("could not save index tree"));
reset_head();
discard_cache();
read_cache();
}
}
@ -481,13 +484,12 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
if (ret)
return -1;
/* read back the result of update_index() back from the disk */
discard_cache();
read_cache();
}
if (quiet) {
if (refresh_cache(REFRESH_QUIET))
warning("could not refresh index");
} else {
if (!quiet) {
struct child_process cp = CHILD_PROCESS_INIT;
/*
@ -497,6 +499,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
*/
cp.git_cmd = 1;
cp.dir = prefix;
argv_array_pushf(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT"=%s",
absolute_path(get_git_work_tree()));
argv_array_pushf(&cp.env_array, GIT_DIR_ENVIRONMENT"=%s",
absolute_path(get_git_dir()));
argv_array_push(&cp.args, "status");
run_command(&cp);
}
@ -993,9 +999,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
{
int ret = 0;
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
struct child_process cp_add_i = CHILD_PROCESS_INIT;
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
struct index_state istate = { NULL };
char *old_index_env = NULL, *old_repo_index_file;
remove_path(stash_index_path.buf);
@ -1009,16 +1015,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
}
/* Find out what the user wants. */
cp_add_i.git_cmd = 1;
argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
"--", NULL);
add_pathspecs(&cp_add_i.args, ps);
argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
if (run_command(&cp_add_i)) {
ret = -1;
goto done;
}
old_repo_index_file = the_repository->index_file;
the_repository->index_file = stash_index_path.buf;
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
ret = run_add_interactive(NULL, "--patch=stash", ps);
the_repository->index_file = old_repo_index_file;
if (old_index_env && *old_index_env)
setenv(INDEX_ENVIRONMENT, old_index_env, 1);
else
unsetenv(INDEX_ENVIRONMENT);
FREE_AND_NULL(old_index_env);
/* State of the working tree. */
if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
@ -1082,8 +1091,9 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
}
cp_upd_index.git_cmd = 1;
argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
"--remove", "--stdin", NULL);
argv_array_pushl(&cp_upd_index.args, "update-index",
"--ignore-skip-worktree-entries",
"-z", "--add", "--remove", "--stdin", NULL);
argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
@ -1129,7 +1139,10 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
prepare_fallback_ident("git stash", "git@stash");
read_cache_preload(NULL);
refresh_cache(REFRESH_QUIET);
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) {
ret = -1;
goto done;
}
if (get_oid("HEAD", &info->b_commit)) {
if (!quiet)
@ -1290,7 +1303,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
free(ps_matched);
}
if (refresh_cache(REFRESH_QUIET)) {
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) {
ret = -1;
goto done;
}
@ -1383,7 +1396,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
argv_array_pushl(&cp.args, "reset", "--hard", "-q",
NULL);
"--no-recurse-submodules", NULL);
if (run_command(&cp)) {
ret = -1;
goto done;
@ -1439,13 +1452,17 @@ done:
return ret;
}
static int push_stash(int argc, const char **argv, const char *prefix)
static int push_stash(int argc, const char **argv, const char *prefix,
int push_assumed)
{
int force_assume = 0;
int keep_index = -1;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
int pathspec_file_nul = 0;
const char *stash_msg = NULL;
const char *pathspec_from_file = NULL;
struct pathspec ps;
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
@ -1459,16 +1476,45 @@ static int push_stash(int argc, const char **argv, const char *prefix)
N_("include ignore files"), 2),
OPT_STRING('m', "message", &stash_msg, N_("message"),
N_("stash message")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END()
};
if (argc)
if (argc) {
force_assume = !strcmp(argv[0], "-p");
argc = parse_options(argc, argv, prefix, options,
git_stash_push_usage,
0);
PARSE_OPT_KEEP_DASHDASH);
}
if (argc) {
if (!strcmp(argv[0], "--")) {
argc--;
argv++;
} else if (push_assumed && !force_assume) {
die("subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'",
argv[0]);
}
}
parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
prefix, argv);
if (pathspec_from_file) {
if (patch_mode)
die(_("--pathspec-from-file is incompatible with --patch"));
if (ps.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
parse_pathspec_file(&ps, 0,
PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
include_untracked);
}
@ -1538,7 +1584,6 @@ static int use_builtin_stash(void)
int cmd_stash(int argc, const char **argv, const char *prefix)
{
int i = -1;
pid_t pid = getpid();
const char *index_file;
struct argv_array args = ARGV_ARRAY_INIT;
@ -1571,7 +1616,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
(uintmax_t)pid);
if (!argc)
return !!push_stash(0, NULL, prefix);
return !!push_stash(0, NULL, prefix, 0);
else if (!strcmp(argv[0], "apply"))
return !!apply_stash(argc, argv, prefix);
else if (!strcmp(argv[0], "clear"))
@ -1591,45 +1636,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
else if (!strcmp(argv[0], "create"))
return !!create_stash(argc, argv, prefix);
else if (!strcmp(argv[0], "push"))
return !!push_stash(argc, argv, prefix);
return !!push_stash(argc, argv, prefix, 0);
else if (!strcmp(argv[0], "save"))
return !!save_stash(argc, argv, prefix);
else if (*argv[0] != '-')
usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
git_stash_usage, options);
if (strcmp(argv[0], "-p")) {
while (++i < argc && strcmp(argv[i], "--")) {
/*
* `akpqu` is a string which contains all short options,
* except `-m` which is verified separately.
*/
if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
strchr("akpqu", argv[i][1]))
continue;
if (!strcmp(argv[i], "--all") ||
!strcmp(argv[i], "--keep-index") ||
!strcmp(argv[i], "--no-keep-index") ||
!strcmp(argv[i], "--patch") ||
!strcmp(argv[i], "--quiet") ||
!strcmp(argv[i], "--include-untracked"))
continue;
/*
* `-m` and `--message=` are verified separately because
* they need to be immediately followed by a string
* (i.e.`-m"foobar"` or `--message="foobar"`).
*/
if (starts_with(argv[i], "-m") ||
starts_with(argv[i], "--message="))
continue;
usage_with_options(git_stash_usage, options);
}
}
/* Assume 'stash push' */
argv_array_push(&args, "push");
argv_array_pushv(&args, argv);
return !!push_stash(args.argc, args.argv, prefix);
return !!push_stash(args.argc, args.argv, prefix, 1);
}

View file

@ -19,6 +19,8 @@
#include "diffcore.h"
#include "diff.h"
#include "object-store.h"
#include "dir.h"
#include "advice.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
@ -424,7 +426,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
const struct cache_entry *ce = list.entries[i];
if (ce_stage(ce))
printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
printf("%06o %s U\t", ce->ce_mode, oid_to_hex(&null_oid));
else
printf("%06o %s %d\t", ce->ce_mode,
oid_to_hex(&ce->oid), ce_stage(ce));
@ -780,6 +782,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
struct argv_array diff_files_args = ARGV_ARRAY_INIT;
struct rev_info rev;
int diff_files_result;
struct strbuf buf = STRBUF_INIT;
const char *git_dir;
if (!submodule_from_path(the_repository, &null_oid, path))
die(_("no submodule mapping found in .gitmodules for path '%s'"),
@ -792,17 +796,26 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
goto cleanup;
}
if (!is_submodule_active(the_repository, path)) {
strbuf_addf(&buf, "%s/.git", path);
git_dir = read_gitfile(buf.buf);
if (!git_dir)
git_dir = buf.buf;
if (!is_submodule_active(the_repository, path) ||
!is_git_directory(git_dir)) {
print_status(flags, '-', path, ce_oid, displaypath);
strbuf_release(&buf);
goto cleanup;
}
strbuf_release(&buf);
argv_array_pushl(&diff_files_args, "diff-files",
"--ignore-submodules=dirty", "--quiet", "--",
path, NULL);
git_config(git_diff_basic_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
repo_init_revisions(the_repository, &rev, NULL);
rev.abbrev = 0;
diff_files_args.argc = setup_revisions(diff_files_args.argc,
diff_files_args.argv,
@ -1222,7 +1235,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, struct string_list *reference, int dissociate,
int quiet, int progress)
int quiet, int progress, int single_branch)
{
struct child_process cp = CHILD_PROCESS_INIT;
@ -1244,6 +1257,10 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
argv_array_push(&cp.args, "--dissociate");
if (gitdir && *gitdir)
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
if (single_branch >= 0)
argv_array_push(&cp.args, single_branch ?
"--single-branch" :
"--no-single-branch");
argv_array_push(&cp.args, "--");
argv_array_push(&cp.args, url);
@ -1268,6 +1285,13 @@ struct submodule_alternate_setup {
#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
static const char alternate_error_advice[] = N_(
"An alternate computed from a superproject's alternate is invalid.\n"
"To allow Git to clone without an alternate in such a case, set\n"
"submodule.alternateErrorStrategy to 'info' or, equivalently, clone with\n"
"'--reference-if-able' instead of '--reference'."
);
static int add_possible_reference_from_superproject(
struct object_directory *odb, void *sas_cb)
{
@ -1299,6 +1323,8 @@ static int add_possible_reference_from_superproject(
} else {
switch (sas->error_mode) {
case SUBMODULE_ALTERNATE_ERROR_DIE:
if (advice_submodule_alternate_error_strategy_die)
advise(_(alternate_error_advice));
die(_("submodule '%s' cannot add alternate: %s"),
sas->submodule_name, err.buf);
case SUBMODULE_ALTERNATE_ERROR_INFO:
@ -1359,8 +1385,9 @@ static int module_clone(int argc, const char **argv, const char *prefix)
char *p, *path = NULL, *sm_gitdir;
struct strbuf sb = STRBUF_INIT;
struct string_list reference = STRING_LIST_INIT_NODUP;
int dissociate = 0;
int dissociate = 0, require_init = 0;
char *sm_alternate = NULL, *error_strategy = NULL;
int single_branch = -1;
struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &prefix,
@ -1386,12 +1413,17 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
OPT_BOOL(0, "progress", &progress,
N_("force cloning progress")),
OPT_BOOL(0, "require-init", &require_init,
N_("disallow cloning into non-empty directory")),
OPT_BOOL(0, "single-branch", &single_branch,
N_("clone only one branch, HEAD or --branch")),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
"[--reference <repository>] [--name <name>] [--depth <depth>] "
"[--single-branch] "
"--url <url> --path <path>"),
NULL
};
@ -1413,6 +1445,10 @@ static int module_clone(int argc, const char **argv, const char *prefix)
} else
path = xstrdup(path);
if (validate_submodule_git_dir(sm_gitdir, name) < 0)
die(_("refusing to create/use '%s' in another submodule's "
"git dir"), sm_gitdir);
if (!file_exists(sm_gitdir)) {
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
@ -1420,10 +1456,12 @@ static int module_clone(int argc, const char **argv, const char *prefix)
prepare_possible_alternates(name, &reference);
if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,
quiet, progress))
quiet, progress, single_branch))
die(_("clone of '%s' into submodule path '%s' failed"),
url, path);
} else {
if (require_init && !access(path, X_OK) && !is_empty_dir(path))
die(_("directory not empty: '%s'"), path);
if (safe_create_leading_directories_const(path) < 0)
die(_("could not create directory '%s'"), path);
strbuf_addf(&sb, "%s/index", sm_gitdir);
@ -1478,6 +1516,8 @@ static void determine_submodule_update_strategy(struct repository *r,
die(_("Invalid update mode '%s' configured for submodule path '%s'"),
val, path);
} else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
if (sub->update_strategy.type == SM_UPDATE_COMMAND)
BUG("how did we read update = !command from .gitmodules?");
out->type = sub->update_strategy.type;
out->command = sub->update_strategy.command;
} else
@ -1536,9 +1576,11 @@ struct submodule_update_clone {
int recommend_shallow;
struct string_list references;
int dissociate;
unsigned require_init;
const char *depth;
const char *recursive_prefix;
const char *prefix;
int single_branch;
/* to be consumed by git-submodule.sh */
struct update_clone_data *update_clone;
@ -1553,10 +1595,14 @@ struct submodule_update_clone {
int max_jobs;
};
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
NULL, NULL, NULL, \
NULL, 0, 0, 0, NULL, 0, 0, 1}
#define SUBMODULE_UPDATE_CLONE_INIT { \
.list = MODULE_LIST_INIT, \
.update = SUBMODULE_UPDATE_STRATEGY_INIT, \
.recommend_shallow = -1, \
.references = STRING_LIST_INIT_DUP, \
.single_branch = -1, \
.max_jobs = 1, \
}
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@ -1681,6 +1727,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
if (suc->recommend_shallow && sub->recommend_shallow == 1)
argv_array_push(&child->args, "--depth=1");
if (suc->require_init)
argv_array_push(&child->args, "--require-init");
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
argv_array_pushl(&child->args, "--url", url, NULL);
@ -1693,6 +1741,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_push(&child->args, "--dissociate");
if (suc->depth)
argv_array_push(&child->args, suc->depth);
if (suc->single_branch >= 0)
argv_array_push(&child->args, suc->single_branch ?
"--single-branch" :
"--no-single-branch");
cleanup:
strbuf_reset(&displaypath_sb);
@ -1870,11 +1922,15 @@ static int update_clone(int argc, const char **argv, const char *prefix)
OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
OPT_BOOL(0, "progress", &suc.progress,
N_("force cloning progress")),
OPT_BOOL(0, "require-init", &suc.require_init,
N_("disallow cloning into non-empty directory")),
OPT_BOOL(0, "single-branch", &suc.single_branch,
N_("clone only one branch, HEAD or --branch")),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
NULL
};
suc.prefix = prefix;

View file

@ -24,6 +24,7 @@ static off_t consumed_bytes;
static off_t max_input_size;
static git_hash_ctx ctx;
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
static struct progress *progress;
/*
* When running under --strict mode, objects whose reachability are
@ -92,6 +93,7 @@ static void use(int bytes)
consumed_bytes += bytes;
if (max_input_size && consumed_bytes > max_input_size)
die(_("pack exceeds maximum allowed size"));
display_throughput(progress, consumed_bytes);
}
static void *get_data(unsigned long size)
@ -263,7 +265,8 @@ static void write_object(unsigned nr, enum object_type type,
} else {
struct object *obj;
int eaten;
hash_object_file(buf, size, type_name(type), &obj_list[nr].oid);
hash_object_file(the_hash_algo, buf, size, type_name(type),
&obj_list[nr].oid);
added_object(nr, type, buf, size);
obj = parse_object_buffer(the_repository, &obj_list[nr].oid,
type, size, buf,
@ -484,7 +487,6 @@ static void unpack_one(unsigned nr)
static void unpack_all(void)
{
int i;
struct progress *progress = NULL;
struct pack_header *hdr = fill(sizeof(struct pack_header));
nr_objects = ntohl(hdr->hdr_entries);

View file

@ -35,6 +35,7 @@ static int verbose;
static int mark_valid_only;
static int mark_skip_worktree_only;
static int mark_fsmonitor_only;
static int ignore_skip_worktree_entries;
#define MARK_FLAG 1
#define UNMARK_FLAG 2
static struct strbuf mtime_dir = STRBUF_INIT;
@ -381,7 +382,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
* so updating it does not make sense.
* On the other hand, removing it from index should work
*/
if (allow_remove && remove_file_from_cache(path))
if (!ignore_skip_worktree_entries && allow_remove &&
remove_file_from_cache(path))
return error("%s: cannot remove from the index", path);
return 0;
}
@ -966,6 +968,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
struct parse_opt_ctx_t ctx;
strbuf_getline_fn getline_fn;
int parseopt_state = PARSE_OPT_UNKNOWN;
struct repository *r = the_repository;
struct option options[] = {
OPT_BIT('q', NULL, &refresh_args.flags,
N_("continue refresh even when index needs update"),
@ -1013,6 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
{OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
N_("clear skip-worktree bit"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries,
N_("do not touch index-only entries")),
OPT_SET_INT(0, "info-only", &info_only,
N_("add to index only; do not add content to object database"), 1),
OPT_SET_INT(0, "force-remove", &force_remove,
@ -1180,11 +1185,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
remove_split_index(&the_index);
}
prepare_repo_settings(r);
switch (untracked_cache) {
case UC_UNSPECIFIED:
break;
case UC_DISABLE:
if (git_config_get_untracked_cache() == 1)
if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
warning(_("core.untrackedCache is set to true; "
"remove or change it, if you really want to "
"disable the untracked cache"));
@ -1196,7 +1202,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
return !test_if_untracked_cache_is_supported();
case UC_ENABLE:
case UC_FORCE:
if (git_config_get_untracked_cache() == 0)
if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE)
warning(_("core.untrackedCache is set to false; "
"remove or change it, if you really want to "
"enable the untracked cache"));

View file

@ -10,7 +10,6 @@
#include "run-command.h"
#include "sigchain.h"
#include "submodule.h"
#include "refs.h"
#include "utf8.h"
#include "worktree.h"
@ -235,14 +234,7 @@ static void validate_worktree_add(const char *path, const struct add_opts *opts)
die(_("'%s' already exists"), path);
worktrees = get_worktrees(0);
/*
* find_worktree()'s suffix matching may undesirably find the main
* rather than a linked worktree (for instance, when the basenames
* of the main worktree and the one being created are the same).
* We're only interested in linked worktrees, so skip the main
* worktree with +1.
*/
wt = find_worktree(worktrees + 1, NULL, path);
wt = find_worktree_by_path(worktrees, path);
if (!wt)
goto done;
@ -350,7 +342,7 @@ static int add_worktree(const char *path, const char *refname,
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, "%s", sha1_to_hex(null_sha1));
write_file(sb.buf, "%s", oid_to_hex(&null_oid));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
@ -377,7 +369,7 @@ static int add_worktree(const char *path, const char *refname,
if (opts->checkout) {
cp.argv = NULL;
argv_array_clear(&cp.args);
argv_array_pushl(&cp.args, "reset", "--hard", NULL);
argv_array_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
argv_array_push(&cp.args, "--quiet");
cp.env = child_env.argv;
@ -880,7 +872,7 @@ static void check_clean_worktree(struct worktree *wt,
original_path);
ret = xread(cp.out, buf, sizeof(buf));
if (ret)
die(_("'%s' is dirty, use --force to delete it"),
die(_("'%s' contains modified or untracked files, use --force to delete it"),
original_path);
close(cp.out);
ret = finish_command(&cp);