summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c46
-rw-r--r--builtin/am.c217
-rw-r--r--builtin/apply.c4
-rw-r--r--builtin/archive.c2
-rw-r--r--builtin/bisect--helper.c137
-rw-r--r--builtin/blame.c165
-rw-r--r--builtin/branch.c89
-rw-r--r--builtin/cat-file.c38
-rw-r--r--builtin/check-ignore.c3
-rw-r--r--builtin/check-ref-format.c6
-rw-r--r--builtin/checkout-index.c10
-rw-r--r--builtin/checkout.c321
-rw-r--r--builtin/clean.c31
-rw-r--r--builtin/clone.c86
-rw-r--r--builtin/column.c1
-rw-r--r--builtin/commit-graph.c171
-rw-r--r--builtin/commit-tree.c6
-rw-r--r--builtin/commit.c454
-rw-r--r--builtin/config.c159
-rw-r--r--builtin/count-objects.c9
-rw-r--r--builtin/describe.c165
-rw-r--r--builtin/diff-tree.c4
-rw-r--r--builtin/diff.c26
-rw-r--r--builtin/difftool.c16
-rw-r--r--builtin/fast-export.c76
-rw-r--r--builtin/fetch-pack.c20
-rw-r--r--builtin/fetch.c208
-rw-r--r--builtin/fmt-merge-msg.c11
-rw-r--r--builtin/fsck.c80
-rw-r--r--builtin/gc.c187
-rw-r--r--builtin/grep.c83
-rw-r--r--builtin/hash-object.c5
-rw-r--r--builtin/help.c68
-rw-r--r--builtin/index-pack.c222
-rw-r--r--builtin/init-db.c36
-rw-r--r--builtin/log.c85
-rw-r--r--builtin/ls-files.c20
-rw-r--r--builtin/ls-remote.c54
-rw-r--r--builtin/ls-tree.c8
-rw-r--r--builtin/merge-base.c42
-rw-r--r--builtin/merge-ours.c16
-rw-r--r--builtin/merge-tree.c7
-rw-r--r--builtin/merge.c154
-rw-r--r--builtin/mktag.c27
-rw-r--r--builtin/mktree.c36
-rw-r--r--builtin/mv.c20
-rw-r--r--builtin/name-rev.c25
-rw-r--r--builtin/notes.c74
-rw-r--r--builtin/pack-objects.c740
-rw-r--r--builtin/pack-redundant.c118
-rw-r--r--builtin/pack-refs.c3
-rw-r--r--builtin/prune-packed.c2
-rw-r--r--builtin/prune.c7
-rw-r--r--builtin/pull.c62
-rw-r--r--builtin/push.c150
-rw-r--r--builtin/read-tree.c3
-rw-r--r--builtin/rebase--helper.c42
-rw-r--r--builtin/receive-pack.c75
-rw-r--r--builtin/reflog.c45
-rw-r--r--builtin/remote.c135
-rw-r--r--builtin/repack.c21
-rw-r--r--builtin/replace.c286
-rw-r--r--builtin/reset.c40
-rw-r--r--builtin/rev-list.c15
-rw-r--r--builtin/rev-parse.c54
-rw-r--r--builtin/revert.c4
-rw-r--r--builtin/rm.c16
-rw-r--r--builtin/send-pack.c44
-rw-r--r--builtin/serve.c30
-rw-r--r--builtin/shortlog.c9
-rw-r--r--builtin/show-branch.c60
-rw-r--r--builtin/show-index.c86
-rw-r--r--builtin/show-ref.c8
-rw-r--r--builtin/submodule--helper.c834
-rw-r--r--builtin/symbolic-ref.c2
-rw-r--r--builtin/tag.c43
-rw-r--r--builtin/unpack-file.c2
-rw-r--r--builtin/unpack-objects.c43
-rw-r--r--builtin/update-index.c85
-rw-r--r--builtin/update-ref.c41
-rw-r--r--builtin/update-server-info.c2
-rw-r--r--builtin/upload-pack.c74
-rw-r--r--builtin/verify-commit.c4
-rw-r--r--builtin/verify-tag.c2
-rw-r--r--builtin/worktree.c409
-rw-r--r--builtin/write-tree.c6
86 files changed, 4987 insertions, 2345 deletions
diff --git a/builtin/add.c b/builtin/add.c
index a648cf4c56..8a155dd41e 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -9,7 +9,7 @@
#include "lockfile.h"
#include "dir.h"
#include "pathspec.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "cache-tree.h"
#include "run-command.h"
#include "parse-options.h"
@@ -26,6 +26,7 @@ static const char * const builtin_add_usage[] = {
};
static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
+static int add_renormalize;
struct update_callback_data {
int flags;
@@ -116,13 +117,32 @@ int add_files_to_cache(const char *prefix,
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
rev.diffopt.format_callback_data = &data;
- rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ rev.diffopt.flags.override_submodule_config = 1;
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
clear_pathspec(&rev.prune_data);
return !!data.add_errors;
}
+static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
+{
+ int i, retval = 0;
+
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+
+ if (ce_stage(ce))
+ continue; /* do not touch unmerged paths */
+ if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
+ continue; /* do not touch non blobs */
+ if (pathspec && !ce_path_match(ce, pathspec, NULL))
+ continue;
+ retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE);
+ }
+
+ return retval;
+}
+
static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
{
char *seen;
@@ -218,7 +238,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
argc = setup_revisions(argc, argv, &rev, NULL);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
rev.diffopt.use_color = 0;
- DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
+ rev.diffopt.flags.ignore_dirty_submodules = 1;
out = open(file, O_CREAT | O_WRONLY, 0666);
if (out < 0)
die(_("Could not open '%s' for writing."), file);
@@ -245,8 +265,6 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
return 0;
}
-static struct lock_file lock_file;
-
static const char ignore_error[] =
N_("The following paths are ignored by one of your .gitignore files:\n");
@@ -274,8 +292,9 @@ static struct option builtin_add_options[] = {
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
- OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
+ OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
+ OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")),
OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
{ OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
@@ -372,6 +391,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int add_new_files;
int require_pathspec;
char *seen = NULL;
+ struct lock_file lock_file = LOCK_INIT;
git_config(add_config, NULL);
@@ -406,7 +426,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
chmod_arg[1] != 'x' || chmod_arg[2]))
die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
- add_new_files = !take_worktree_changes && !refresh_only;
+ add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
@@ -500,7 +520,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
plug_bulk_checkin();
- exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+ if (add_renormalize)
+ exit_status |= renormalize_tracked_files(&pathspec, flags);
+ else
+ exit_status |= add_files_to_cache(prefix, &pathspec, flags);
if (add_new_files)
exit_status |= add_files(&dir, flags);
@@ -510,10 +533,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
unplug_bulk_checkin();
finish:
- if (active_cache_changed) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
+ if (write_locked_index(&the_index, &lock_file,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ die(_("Unable to write new index file"));
UNLEAK(pathspec);
UNLEAK(dir);
diff --git a/builtin/am.c b/builtin/am.c
index d7513f5375..6273ea5195 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -6,7 +6,7 @@
#include "cache.h"
#include "config.h"
#include "builtin.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "parse-options.h"
#include "dir.h"
#include "run-command.h"
@@ -403,11 +403,11 @@ static void am_load(struct am_state *state)
struct strbuf sb = STRBUF_INIT;
if (read_state_file(&sb, state, "next", 1) < 0)
- die("BUG: state file 'next' does not exist");
+ BUG("state file 'next' does not exist");
state->cur = strtol(sb.buf, NULL, 10);
if (read_state_file(&sb, state, "last", 1) < 0)
- die("BUG: state file 'last' does not exist");
+ BUG("state file 'last' does not exist");
state->last = strtol(sb.buf, NULL, 10);
if (read_author_script(state) < 0)
@@ -708,6 +708,7 @@ static int split_mail_mbox(struct am_state *state, const char **paths,
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf last = STRBUF_INIT;
+ int ret;
cp.git_cmd = 1;
argv_array_push(&cp.args, "mailsplit");
@@ -721,13 +722,16 @@ static int split_mail_mbox(struct am_state *state, const char **paths,
argv_array_push(&cp.args, "--");
argv_array_pushv(&cp.args, paths);
- if (capture_command(&cp, &last, 8))
- return -1;
+ ret = capture_command(&cp, &last, 8);
+ if (ret)
+ goto exit;
state->cur = 1;
state->last = strtol(last.buf, NULL, 10);
- return 0;
+exit:
+ strbuf_release(&last);
+ return ret ? -1 : 0;
}
/**
@@ -982,7 +986,7 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
case PATCH_FORMAT_MBOXRD:
return split_mail_mbox(state, paths, keep_cr, 1);
default:
- die("BUG: invalid patch_format");
+ BUG("invalid patch_format");
}
return -1;
}
@@ -1007,6 +1011,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
die_errno(_("failed to create directory '%s'"), state->dir);
+ delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
if (split_mail(state, patch_format, paths, keep_cr) < 0) {
am_destroy(state);
@@ -1036,7 +1041,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
str = "b";
break;
default:
- die("BUG: invalid value for state->keep");
+ BUG("invalid value for state->keep");
}
write_state_text(state, "keep", str);
@@ -1053,11 +1058,11 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
str = "t";
break;
default:
- die("BUG: invalid value for state->scissors");
+ BUG("invalid value for state->scissors");
}
write_state_text(state, "scissors", str);
- sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
+ sq_quote_argv(&sb, state->git_apply_opts.argv);
write_state_text(state, "apply-opt", sb.buf);
if (state->rebasing)
@@ -1068,8 +1073,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
if (!get_oid("HEAD", &curr_head)) {
write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
if (!state->rebasing)
- update_ref_oid("am", "ORIG_HEAD", &curr_head, NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
} else {
write_state_text(state, "abort-safety", "");
if (!state->rebasing)
@@ -1106,6 +1111,7 @@ static void am_next(struct am_state *state)
oidclr(&state->orig_commit);
unlink(am_path(state, "original-commit"));
+ delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
if (!get_oid("HEAD", &head))
write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1134,52 +1140,15 @@ static const char *msgnum(const struct am_state *state)
*/
static void refresh_and_write_cache(void)
{
- struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock_file = LOCK_INIT;
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write index file"));
}
/**
- * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
- * branch, returns 1 if there are entries in the index, 0 otherwise. If an
- * strbuf is provided, the space-separated list of files that differ will be
- * appended to it.
- */
-static int index_has_changes(struct strbuf *sb)
-{
- struct object_id head;
- int i;
-
- if (!get_oid_tree("HEAD", &head)) {
- struct diff_options opt;
-
- diff_setup(&opt);
- DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
- if (!sb)
- DIFF_OPT_SET(&opt, QUICK);
- do_diff_cache(&head, &opt);
- diffcore_std(&opt);
- for (i = 0; sb && i < diff_queued_diff.nr; i++) {
- if (i)
- strbuf_addch(sb, ' ');
- strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
- }
- diff_flush(&opt);
- return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
- } else {
- for (i = 0; sb && i < active_nr; i++) {
- if (i)
- strbuf_addch(sb, ' ');
- strbuf_addstr(sb, active_cache[i]->name);
- }
- return !!active_nr;
- }
-}
-
-/**
* Dies with a user-friendly message on how to proceed after resolving the
* problem. This message can be overridden with state->resolvemsg.
*/
@@ -1247,7 +1216,7 @@ static int parse_mail(struct am_state *state, const char *mail)
mi.keep_non_patch_brackets_in_subject = 1;
break;
default:
- die("BUG: invalid value for state->keep");
+ BUG("invalid value for state->keep");
}
if (state->message_id)
@@ -1263,7 +1232,7 @@ static int parse_mail(struct am_state *state, const char *mail)
mi.use_scissors = 1;
break;
default:
- die("BUG: invalid value for state->scissors");
+ BUG("invalid value for state->scissors");
}
mi.input = xfopen(mail, "r");
@@ -1409,8 +1378,8 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
rev_info.show_root_diff = 1;
rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
rev_info.no_commit_id = 1;
- DIFF_OPT_SET(&rev_info.diffopt, BINARY);
- DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
+ rev_info.diffopt.flags.binary = 1;
+ rev_info.diffopt.flags.full_index = 1;
rev_info.diffopt.use_color = 0;
rev_info.diffopt.file = fp;
rev_info.diffopt.close_file = 1;
@@ -1433,7 +1402,7 @@ static void write_index_patch(const struct am_state *state)
if (!get_oid_tree("HEAD", &head))
tree = lookup_tree(&head);
else
- tree = lookup_tree(&empty_tree_oid);
+ tree = lookup_tree(the_hash_algo->empty_tree);
fp = xfopen(am_path(state, "patch"), "w");
init_revisions(&rev_info, NULL);
@@ -1474,6 +1443,8 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
oidcpy(&state->orig_commit, &commit_oid);
write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
+ update_ref("am", "REBASE_HEAD", &commit_oid,
+ NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
return 0;
}
@@ -1488,12 +1459,11 @@ static int run_apply(const struct am_state *state, const char *index_file)
struct argv_array apply_opts = ARGV_ARRAY_INIT;
struct apply_state apply_state;
int res, opts_left;
- static struct lock_file lock_file;
int force_apply = 0;
int options = 0;
- if (init_apply_state(&apply_state, NULL, &lock_file))
- die("BUG: init_apply_state() failed");
+ if (init_apply_state(&apply_state, NULL))
+ BUG("init_apply_state() failed");
argv_array_push(&apply_opts, "apply");
argv_array_pushv(&apply_opts, state->git_apply_opts.argv);
@@ -1519,7 +1489,7 @@ static int run_apply(const struct am_state *state, const char *index_file)
apply_state.apply_verbosity = verbosity_silent;
if (check_apply_state(&apply_state, force_apply))
- die("BUG: check_apply_state() failed");
+ BUG("check_apply_state() failed");
argv_array_push(&apply_paths, am_path(state, "patch"));
@@ -1572,7 +1542,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
char *their_tree_name;
if (get_oid("HEAD", &our_tree) < 0)
- hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
+ oidcpy(&our_tree, the_hash_algo->empty_tree);
if (build_fake_ancestor(state, index_path))
return error("could not build fake ancestor");
@@ -1580,7 +1550,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
discard_cache();
read_cache_from(index_path);
- if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1605,7 +1575,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
return error(_("Did you hand edit your patch?\n"
"It does not apply to blobs recorded in its index."));
- if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
return error("could not write tree");
say(state, stdout, _("Falling back to patching base and 3-way merge..."));
@@ -1656,7 +1626,7 @@ static void do_commit(const struct am_state *state)
if (run_hook_le(NULL, "pre-applypatch", NULL))
exit(1);
- if (write_cache_as_tree(tree.hash, 0, NULL))
+ if (write_cache_as_tree(&tree, 0, NULL))
die(_("git write-tree failed to write a tree"));
if (!get_oid_commit("HEAD", &parent)) {
@@ -1675,8 +1645,8 @@ static void do_commit(const struct am_state *state)
setenv("GIT_COMMITTER_DATE",
state->ignore_date ? "" : state->author_date, 1);
- if (commit_tree(state->msg, state->msg_len, tree.hash, parents, commit.hash,
- author, state->sign_commit))
+ if (commit_tree(state->msg, state->msg_len, &tree, parents, &commit,
+ author, state->sign_commit))
die(_("failed to write commit object"));
reflog_msg = getenv("GIT_REFLOG_ACTION");
@@ -1686,8 +1656,8 @@ static void do_commit(const struct am_state *state)
strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
state->msg);
- update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
+ UPDATE_REFS_DIE_ON_ERR);
if (state->rebasing) {
FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@ -1857,16 +1827,11 @@ static void am_run(struct am_state *state, int resume)
}
if (apply_status) {
- int advice_amworkdir = 1;
-
printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
linelen(state->msg), state->msg);
- git_config_get_bool("advice.amworkdir", &advice_amworkdir);
-
if (advice_amworkdir)
- printf_ln(_("The copy of the patch that failed is found in: %s"),
- am_path(state, "patch"));
+ advise(_("Use 'git am --show-current-patch' to see the failed patch"));
die_user_resolve(state);
}
@@ -1893,7 +1858,7 @@ next:
*/
if (!state->rebasing) {
am_destroy(state);
- close_all_packs();
+ close_all_packs(the_repository->objects);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
@@ -1946,15 +1911,14 @@ next:
*/
static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
{
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
struct unpack_trees_options opts;
struct tree_desc t[2];
if (parse_tree(head) || parse_tree(remote))
return -1;
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
@@ -1970,11 +1934,11 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
init_tree_desc(&t[1], remote->buffer, remote->size);
if (unpack_trees(2, t, &opts)) {
- rollback_lock_file(lock_file);
+ rollback_lock_file(&lock_file);
return -1;
}
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
@@ -1986,15 +1950,14 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
*/
static int merge_tree(struct tree *tree)
{
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
struct unpack_trees_options opts;
struct tree_desc t[1];
if (parse_tree(tree))
return -1;
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
@@ -2005,11 +1968,11 @@ static int merge_tree(struct tree *tree)
init_tree_desc(&t[0], tree->buffer, tree->size);
if (unpack_trees(1, t, &opts)) {
- rollback_lock_file(lock_file);
+ rollback_lock_file(&lock_file);
return -1;
}
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
@@ -2037,7 +2000,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (fast_forward_to(head_tree, head_tree, 1))
return -1;
- if (write_cache_as_tree(index.hash, 0, NULL))
+ if (write_cache_as_tree(&index, 0, NULL))
return -1;
index_tree = parse_tree_indirect(&index);
@@ -2075,7 +2038,7 @@ static void am_skip(struct am_state *state)
am_rerere_clear();
if (get_oid("HEAD", &head))
- hashcpy(head.hash, EMPTY_TREE_SHA1_BIN);
+ oidcpy(&head, the_hash_algo->empty_tree);
if (clean_index(&head, &head))
die(_("failed to clean index"));
@@ -2135,28 +2098,56 @@ static void am_abort(struct am_state *state)
am_rerere_clear();
- curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
+ curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
has_curr_head = curr_branch && !is_null_oid(&curr_head);
if (!has_curr_head)
- hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
+ oidcpy(&curr_head, the_hash_algo->empty_tree);
has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
if (!has_orig_head)
- hashcpy(orig_head.hash, EMPTY_TREE_SHA1_BIN);
+ oidcpy(&orig_head, the_hash_algo->empty_tree);
clean_index(&curr_head, &orig_head);
if (has_orig_head)
- update_ref_oid("am --abort", "HEAD", &orig_head,
- has_curr_head ? &curr_head : NULL, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ update_ref("am --abort", "HEAD", &orig_head,
+ has_curr_head ? &curr_head : NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
else if (curr_branch)
- delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
+ delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
free(curr_branch);
am_destroy(state);
}
+static int show_patch(struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *patch_path;
+ int len;
+
+ if (!is_null_oid(&state->orig_commit)) {
+ const char *av[4] = { "show", NULL, "--", NULL };
+ char *new_oid_str;
+ int ret;
+
+ av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));
+ ret = run_command_v_opt(av, RUN_GIT_CMD);
+ free(new_oid_str);
+ return ret;
+ }
+
+ patch_path = am_path(state, msgnum(state));
+ len = strbuf_read_file(&sb, patch_path, 0);
+ if (len < 0)
+ die_errno(_("failed to read '%s'"), patch_path);
+
+ setup_pager();
+ write_in_full(1, sb.buf, sb.len);
+ strbuf_release(&sb);
+ return 0;
+}
+
/**
* parse_options() callback that validates and sets opt->value to the
* PATCH_FORMAT_* enum value corresponding to `arg`.
@@ -2185,7 +2176,9 @@ enum resume_mode {
RESUME_APPLY,
RESUME_RESOLVED,
RESUME_SKIP,
- RESUME_ABORT
+ RESUME_ABORT,
+ RESUME_QUIT,
+ RESUME_SHOW_PATCH
};
static int git_am_config(const char *k, const char *v, void *cb)
@@ -2207,6 +2200,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
int in_progress;
+ int ret = 0;
const char * const usage[] = {
N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
@@ -2233,12 +2227,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
OPT_BOOL('m', "message-id", &state.message_id,
N_("pass -m flag to git-mailinfo")),
- { OPTION_SET_INT, 0, "keep-cr", &keep_cr, NULL,
- N_("pass --keep-cr flag to git-mailsplit for mbox format"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
- { OPTION_SET_INT, 0, "no-keep-cr", &keep_cr, NULL,
- N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
+ OPT_SET_INT_F(0, "keep-cr", &keep_cr,
+ N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+ 1, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "no-keep-cr", &keep_cr,
+ N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
+ 0, PARSE_OPT_NONEG),
OPT_BOOL('c', "scissors", &state.scissors,
N_("strip everything before a scissors line")),
OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"),
@@ -2285,6 +2279,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "abort", &resume,
N_("restore the original branch and abort the patching operation."),
RESUME_ABORT),
+ OPT_CMDMODE(0, "quit", &resume,
+ 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),
OPT_BOOL(0, "committer-date-is-author-date",
&state.committer_date_is_author_date,
N_("lie about committer date")),
@@ -2353,7 +2353,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) {
+ if (resume == RESUME_ABORT || resume == RESUME_QUIT) {
am_destroy(&state);
am_state_release(&state);
return 0;
@@ -2395,11 +2395,18 @@ int cmd_am(int argc, const char **argv, const char *prefix)
case RESUME_ABORT:
am_abort(&state);
break;
+ case RESUME_QUIT:
+ am_rerere_clear();
+ am_destroy(&state);
+ break;
+ case RESUME_SHOW_PATCH:
+ ret = show_patch(&state);
+ break;
default:
- die("BUG: invalid resume value");
+ BUG("invalid resume value");
}
am_state_release(&state);
- return 0;
+ return ret;
}
diff --git a/builtin/apply.c b/builtin/apply.c
index 81b9a61c37..48d3989331 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -9,8 +9,6 @@ static const char * const apply_usage[] = {
NULL
};
-static struct lock_file lock_file;
-
int cmd_apply(int argc, const char **argv, const char *prefix)
{
int force_apply = 0;
@@ -18,7 +16,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
int ret;
struct apply_state state;
- if (init_apply_state(&state, prefix, &lock_file))
+ if (init_apply_state(&state, prefix))
exit(128);
argc = apply_parse_options(argc, argv,
diff --git a/builtin/archive.c b/builtin/archive.c
index f863465a0f..73971d0dd2 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -55,7 +55,7 @@ static int run_remote_archiver(int argc, const char **argv,
buf = packet_read_line(fd[0], NULL);
if (!buf)
- die(_("git archive: expected ACK/NAK, got EOF"));
+ die(_("git archive: expected ACK/NAK, got a flush packet"));
if (strcmp(buf, "ACK")) {
if (starts_with(buf, "NACK "))
die(_("git archive: NACK %s"), buf + 5);
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 3324229025..4b5fadcbe1 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -2,19 +2,128 @@
#include "cache.h"
#include "parse-options.h"
#include "bisect.h"
+#include "refs.h"
+
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --next-all [--no-checkout]"),
+ N_("git bisect--helper --write-terms <bad_term> <good_term>"),
+ N_("git bisect--helper --bisect-clean-state"),
NULL
};
+/*
+ * Check whether the string `term` belongs to the set of strings
+ * included in the variable arguments.
+ */
+LAST_ARG_MUST_BE_NULL
+static int one_of(const char *term, ...)
+{
+ int res = 0;
+ va_list matches;
+ const char *match;
+
+ va_start(matches, term);
+ while (!res && (match = va_arg(matches, const char *)))
+ res = !strcmp(term, match);
+ va_end(matches);
+
+ return res;
+}
+
+static int check_term_format(const char *term, const char *orig_term)
+{
+ int res;
+ char *new_term = xstrfmt("refs/bisect/%s", term);
+
+ res = check_refname_format(new_term, 0);
+ free(new_term);
+
+ if (res)
+ return error(_("'%s' is not a valid term"), term);
+
+ if (one_of(term, "help", "start", "skip", "next", "reset",
+ "visualize", "view", "replay", "log", "run", "terms", NULL))
+ return error(_("can't use the builtin command '%s' as a term"), term);
+
+ /*
+ * In theory, nothing prevents swapping completely good and bad,
+ * but this situation could be confusing and hasn't been tested
+ * enough. Forbid it for now.
+ */
+
+ if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) ||
+ (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL)))
+ return error(_("can't change the meaning of the term '%s'"), term);
+
+ return 0;
+}
+
+static int write_terms(const char *bad, const char *good)
+{
+ FILE *fp = NULL;
+ int res;
+
+ if (!strcmp(bad, good))
+ return error(_("please use two different terms"));
+
+ if (check_term_format(bad, "bad") || check_term_format(good, "good"))
+ return -1;
+
+ fp = fopen(git_path_bisect_terms(), "w");
+ if (!fp)
+ return error_errno(_("could not open the file BISECT_TERMS"));
+
+ res = fprintf(fp, "%s\n%s\n", bad, good);
+ res |= fclose(fp);
+ return (res < 0) ? -1 : 0;
+}
+
+static int is_expected_rev(const char *expected_hex)
+{
+ struct strbuf actual_hex = STRBUF_INIT;
+ int res = 0;
+ if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
+ strbuf_trim(&actual_hex);
+ res = !strcmp(actual_hex.buf, expected_hex);
+ }
+ strbuf_release(&actual_hex);
+ return res;
+}
+
+static void check_expected_revs(const char **revs, int rev_nr)
+{
+ int i;
+
+ for (i = 0; i < rev_nr; i++) {
+ if (!is_expected_rev(revs[i])) {
+ unlink_or_warn(git_path_bisect_ancestors_ok());
+ unlink_or_warn(git_path_bisect_expected_rev());
+ }
+ }
+}
+
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
- int next_all = 0;
+ enum {
+ NEXT_ALL = 1,
+ WRITE_TERMS,
+ BISECT_CLEAN_STATE,
+ CHECK_EXPECTED_REVS
+ } cmdmode = 0;
int no_checkout = 0;
struct option options[] = {
- OPT_BOOL(0, "next-all", &next_all,
- N_("perform 'git bisect next'")),
+ OPT_CMDMODE(0, "next-all", &cmdmode,
+ N_("perform 'git bisect next'"), NEXT_ALL),
+ OPT_CMDMODE(0, "write-terms", &cmdmode,
+ N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
+ OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
+ N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
+ OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
+ N_("check for expected revs"), CHECK_EXPECTED_REVS),
OPT_BOOL(0, "no-checkout", &no_checkout,
N_("update BISECT_HEAD instead of checking out the current commit")),
OPT_END()
@@ -23,9 +132,25 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
git_bisect_helper_usage, 0);
- if (!next_all)
+ if (!cmdmode)
usage_with_options(git_bisect_helper_usage, options);
- /* next-all */
- return bisect_next_all(prefix, no_checkout);
+ switch (cmdmode) {
+ case NEXT_ALL:
+ return bisect_next_all(prefix, no_checkout);
+ case WRITE_TERMS:
+ if (argc != 2)
+ return error(_("--write-terms requires two arguments"));
+ return write_terms(argv[0], argv[1]);
+ case BISECT_CLEAN_STATE:
+ if (argc != 0)
+ return error(_("--bisect-clean-state requires no arguments"));
+ return bisect_clean_state();
+ case CHECK_EXPECTED_REVS:
+ check_expected_revs(argv, argc);
+ return 0;
+ default:
+ return error("BUG: unknown subcommand '%d'", cmdmode);
+ }
+ return 0;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 67adaef4d8..5a0388aaef 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -7,6 +7,7 @@
#include "cache.h"
#include "config.h"
+#include "color.h"
#include "builtin.h"
#include "commit.h"
#include "diff.h"
@@ -23,6 +24,7 @@
#include "dir.h"
#include "progress.h"
#include "blame.h"
+#include "string-list.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -46,6 +48,8 @@ static int xdl_opts;
static int abbrev = -1;
static int no_whole_file_rename;
static int show_progress;
+static char repeated_meta_color[COLOR_MAXLEN];
+static int coloring_mode;
static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
@@ -316,10 +320,12 @@ static const char *format_time(timestamp_t time, const char *tz_str,
#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_SCORE 0100
+#define OUTPUT_NO_AUTHOR 0200
#define OUTPUT_SHOW_EMAIL 0400
-#define OUTPUT_LINE_PORCELAIN 01000
+#define OUTPUT_LINE_PORCELAIN 01000
+#define OUTPUT_COLOR_LINE 02000
+#define OUTPUT_SHOW_AGE_WITH_COLOR 04000
static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
{
@@ -367,6 +373,64 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
putchar('\n');
}
+static struct color_field {
+ timestamp_t hop;
+ char col[COLOR_MAXLEN];
+} *colorfield;
+static int colorfield_nr, colorfield_alloc;
+
+static void parse_color_fields(const char *s)
+{
+ struct string_list l = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+ enum { EXPECT_DATE, EXPECT_COLOR } next = EXPECT_COLOR;
+
+ colorfield_nr = 0;
+
+ /* Ideally this would be stripped and split at the same time? */
+ string_list_split(&l, s, ',', -1);
+ ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc);
+
+ for_each_string_list_item(item, &l) {
+ switch (next) {
+ case EXPECT_DATE:
+ colorfield[colorfield_nr].hop = approxidate(item->string);
+ next = EXPECT_COLOR;
+ colorfield_nr++;
+ ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc);
+ break;
+ case EXPECT_COLOR:
+ if (color_parse(item->string, colorfield[colorfield_nr].col))
+ die(_("expecting a color: %s"), item->string);
+ next = EXPECT_DATE;
+ break;
+ }
+ }
+
+ if (next == EXPECT_COLOR)
+ die (_("must end with a color"));
+
+ colorfield[colorfield_nr].hop = TIME_MAX;
+ string_list_clear(&l, 0);
+}
+
+static void setup_default_color_by_age(void)
+{
+ parse_color_fields("blue,12 month ago,white,1 month ago,red");
+}
+
+static void determine_line_heat(struct blame_entry *ent, const char **dest_color)
+{
+ int i = 0;
+ struct commit_info ci;
+ get_commit_info(ent->suspect->commit, &ci, 1);
+
+ while (i < colorfield_nr && ci.author_time > colorfield[i].hop)
+ i++;
+
+ *dest_color = colorfield[i].col;
+}
+
static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)
{
int cnt;
@@ -375,15 +439,35 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
struct commit_info ci;
char hex[GIT_MAX_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
+ const char *default_color = NULL, *color = NULL, *reset = NULL;
get_commit_info(suspect->commit, &ci, 1);
oid_to_hex_r(hex, &suspect->commit->object.oid);
cp = blame_nth_line(sb, ent->lno);
+
+ if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) {
+ determine_line_heat(ent, &default_color);
+ color = default_color;
+ reset = GIT_COLOR_RESET;
+ }
+
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
+ if (opt & OUTPUT_COLOR_LINE) {
+ if (cnt > 0) {
+ color = repeated_meta_color;
+ reset = GIT_COLOR_RESET;
+ } else {
+ color = default_color ? default_color : NULL;
+ reset = default_color ? GIT_COLOR_RESET : NULL;
+ }
+ }
+ if (color)
+ fputs(color, stdout);
+
if (suspect->commit->object.flags & UNINTERESTING) {
if (blank_boundary)
memset(hex, ' ', length);
@@ -433,6 +517,8 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
printf(" %*d) ",
max_digits, ent->lno + 1 + cnt);
}
+ if (reset)
+ fputs(reset, stdout);
do {
ch = *cp++;
putchar(ch);
@@ -457,7 +543,7 @@ static void output(struct blame_scoreboard *sb, int option)
struct commit *commit = ent->suspect->commit;
if (commit->object.flags & MORE_THAN_ONE_PATH)
continue;
- for (suspect = commit->util; suspect; suspect = suspect->next) {
+ for (suspect = get_blame_suspects(commit); suspect; suspect = suspect->next) {
if (suspect->guilty && count++) {
commit->object.flags |= MORE_THAN_ONE_PATH;
break;
@@ -499,7 +585,7 @@ static int read_ancestry(const char *graft_file)
static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
{
- const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
+ const char *uniq = find_unique_abbrev(&suspect->commit->object.oid,
auto_abbrev);
int len = strlen(uniq);
if (auto_abbrev < len)
@@ -607,6 +693,30 @@ static int git_blame_config(const char *var, const char *value, void *cb)
parse_date_format(value, &blame_date_mode);
return 0;
}
+ if (!strcmp(var, "color.blame.repeatedlines")) {
+ if (color_parse_mem(value, strlen(value), repeated_meta_color))
+ warning(_("invalid color '%s' in color.blame.repeatedLines"),
+ value);
+ return 0;
+ }
+ if (!strcmp(var, "color.blame.highlightrecent")) {
+ parse_color_fields(value);
+ return 0;
+ }
+
+ if (!strcmp(var, "blame.coloring")) {
+ if (!strcmp(value, "repeatedLines")) {
+ coloring_mode |= OUTPUT_COLOR_LINE;
+ } else if (!strcmp(value, "highlightRecent")) {
+ coloring_mode |= OUTPUT_SHOW_AGE_WITH_COLOR;
+ } else if (!strcmp(value, "none")) {
+ coloring_mode &= ~(OUTPUT_COLOR_LINE |
+ OUTPUT_SHOW_AGE_WITH_COLOR);
+ } else {
+ warning(_("invalid value for blame.coloring"));
+ return 0;
+ }
+ }
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
@@ -649,6 +759,15 @@ static int blame_move_callback(const struct option *option, const char *arg, int
return 0;
}
+static int is_a_rev(const char *name)
+{
+ struct object_id oid;
+
+ if (get_oid(name, &oid))
+ return 0;
+ return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
+}
+
int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -681,6 +800,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+ 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()
@@ -705,11 +826,12 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
unsigned int range_i;
long anchor;
+ setup_default_color_by_age();
git_config(git_blame_config, &output_option);
init_revisions(&revs, NULL);
revs.date_mode = blame_date_mode;
- DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
- DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES);
+ revs.diffopt.flags.allow_textconv = 1;
+ revs.diffopt.flags.follow_renames = 1;
save_commit_buffer = 0;
dashdash_pos = 0;
@@ -720,6 +842,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
for (;;) {
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
case PARSE_OPT_HELP:
+ case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_DONE:
if (ctx.argv[0])
@@ -734,9 +857,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
}
parse_done:
- no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
+ no_whole_file_rename = !revs.diffopt.flags.follow_renames;
xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
- DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
+ revs.diffopt.flags.follow_renames = 0;
argc = parse_options_end(&ctx);
if (incremental || (output_option & OUTPUT_PORCELAIN)) {
@@ -803,7 +926,7 @@ parse_done:
}
blame_date_width -= 1; /* strip the null */
- if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
+ if (revs.diffopt.flags.find_copies_harder)
opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
PICKAXE_BLAME_COPY_HARDER);
@@ -845,16 +968,15 @@ parse_done:
} else {
if (argc < 2)
usage_with_options(blame_opt_usage, options);
- path = add_prefix(prefix, argv[argc - 1]);
- if (argc == 3 && !file_exists(path)) { /* (2b) */
+ if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */
path = add_prefix(prefix, argv[1]);
argv[1] = argv[2];
+ } else { /* (2a) */
+ if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree())
+ die("missing <path> to blame");
+ path = add_prefix(prefix, argv[argc - 1]);
}
argv[argc - 1] = "--";
-
- setup_work_tree();
- if (!file_exists(path))
- die_errno("cannot stat path '%s'", path);
}
revs.disable_stdin = 1;
@@ -940,8 +1062,17 @@ parse_done:
blame_coalesce(&sb);
- if (!(output_option & OUTPUT_PORCELAIN))
+ if (!(output_option & (OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR)))
+ output_option |= coloring_mode;
+
+ if (!(output_option & OUTPUT_PORCELAIN)) {
find_alignment(&sb, &output_option);
+ if (!*repeated_meta_color &&
+ (output_option & OUTPUT_COLOR_LINE))
+ strcpy(repeated_meta_color, GIT_COLOR_CYAN);
+ }
+ if (output_option & OUTPUT_ANNOTATE_COMPAT)
+ output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR);
output(&sb, output_option);
free((void *)sb.final_buf);
diff --git a/builtin/branch.c b/builtin/branch.c
index 79dc9181fd..1876ca9e79 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -22,6 +22,7 @@
#include "wt-status.h"
#include "ref-filter.h"
#include "worktree.h"
+#include "help.h"
static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
@@ -55,25 +56,19 @@ enum color_branch {
BRANCH_COLOR_UPSTREAM = 5
};
+static const char *color_branch_slots[] = {
+ [BRANCH_COLOR_RESET] = "reset",
+ [BRANCH_COLOR_PLAIN] = "plain",
+ [BRANCH_COLOR_REMOTE] = "remote",
+ [BRANCH_COLOR_LOCAL] = "local",
+ [BRANCH_COLOR_CURRENT] = "current",
+ [BRANCH_COLOR_UPSTREAM] = "upstream",
+};
+
static struct string_list output = STRING_LIST_INIT_DUP;
static unsigned int colopts;
-static int parse_branch_color_slot(const char *slot)
-{
- if (!strcasecmp(slot, "plain"))
- return BRANCH_COLOR_PLAIN;
- if (!strcasecmp(slot, "reset"))
- return BRANCH_COLOR_RESET;
- if (!strcasecmp(slot, "remote"))
- return BRANCH_COLOR_REMOTE;
- if (!strcasecmp(slot, "local"))
- return BRANCH_COLOR_LOCAL;
- if (!strcasecmp(slot, "current"))
- return BRANCH_COLOR_CURRENT;
- if (!strcasecmp(slot, "upstream"))
- return BRANCH_COLOR_UPSTREAM;
- return -1;
-}
+define_list_config_array(color_branch_slots);
static int git_branch_config(const char *var, const char *value, void *cb)
{
@@ -86,7 +81,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
return 0;
}
if (skip_prefix(var, "color.branch.", &slot_name)) {
- int slot = parse_branch_color_slot(slot_name);
+ int slot = LOOKUP_CONFIG(color_branch_slots, slot_name);
if (slot < 0)
return 0;
if (!value)
@@ -125,7 +120,7 @@ static int branch_merged(int kind, const char *name,
if (upstream &&
(reference_name = reference_name_to_free =
resolve_refdup(upstream, RESOLVE_REF_READING,
- oid.hash, NULL)) != NULL)
+ &oid, NULL)) != NULL)
reference_rev = lookup_commit_reference(&oid);
}
if (!reference_rev)
@@ -241,7 +236,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
RESOLVE_REF_READING
| RESOLVE_REF_NO_RECURSE
| RESOLVE_REF_ALLOW_BAD_NAME,
- oid.hash, &flags);
+ &oid, &flags);
if (!target) {
error(remote_branch
? _("remote-tracking branch '%s' not found.")
@@ -257,8 +252,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
goto next;
}
- if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash,
- REF_NODEREF)) {
+ if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
+ REF_NO_DEREF)) {
error(remote_branch
? _("Error deleting remote-tracking branch '%s'")
: _("Error deleting branch '%s'"),
@@ -273,7 +268,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
bname.buf,
(flags & REF_ISBROKEN) ? "broken"
: (flags & REF_ISSYMREF) ? target
- : find_unique_abbrev(oid.hash, DEFAULT_ABBREV));
+ : find_unique_abbrev(&oid, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
@@ -391,7 +386,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
struct ref_array array;
int maxwidth = 0;
const char *remote_prefix = "";
- struct strbuf out = STRBUF_INIT;
char *to_free = NULL;
/*
@@ -419,7 +413,10 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
- format_ref_array_item(array.items[i], format, &out);
+ struct strbuf out = STRBUF_INIT;
+ struct strbuf err = STRBUF_INIT;
+ if (format_ref_array_item(array.items[i], format, &out, &err))
+ die("%s", err.buf);
if (column_active(colopts)) {
assert(!filter->verbose && "--column and --verbose are incompatible");
/* format to a string_list to let print_columns() do its job */
@@ -428,6 +425,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
fwrite(out.buf, 1, out.len, stdout);
putchar('\n');
}
+ strbuf_release(&err);
strbuf_release(&out);
}
@@ -462,8 +460,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
+ const char *interpreted_oldname = NULL;
+ const char *interpreted_newname = NULL;
int recovery = 0;
- int clobber_head_ok;
if (!oldname) {
if (copy)
@@ -487,12 +486,18 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
* A command like "git branch -M currentbranch currentbranch" cannot
* cause the worktree to become inconsistent with HEAD, so allow it.
*/
- clobber_head_ok = !strcmp(oldname, newname);
-
- validate_new_branchname(newname, &newref, force, clobber_head_ok);
+ if (!strcmp(oldname, newname))
+ validate_branchname(newname, &newref);
+ else
+ validate_new_branchname(newname, &newref, force);
reject_rebase_or_bisect_branch(oldref.buf);
+ if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
+ !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
+ BUG("expected prefix missing for refs");
+ }
+
if (copy)
strbuf_addf(&logmsg, "Branch: copied %s to %s",
oldref.buf, newref.buf);
@@ -507,11 +512,11 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
if (recovery) {
if (copy)
- warning(_("Copied a misnamed branch '%s' away"),
- oldref.buf + 11);
+ warning(_("Created a copy of a misnamed branch '%s'"),
+ interpreted_oldname);
else
warning(_("Renamed a misnamed branch '%s' away"),
- oldref.buf + 11);
+ interpreted_oldname);
}
if (!copy &&
@@ -520,9 +525,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
strbuf_release(&logmsg);
- strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
+ strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
strbuf_release(&oldref);
- strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
+ strbuf_addf(&newsection, "branch.%s", interpreted_newname);
strbuf_release(&newref);
if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
die(_("Branch is renamed, but update of config-file failed"));
@@ -582,8 +587,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, N_("suppress informational messages")),
OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"),
BRANCH_TRACK_EXPLICIT),
- { OPTION_SET_INT, 0, "set-upstream", &track, NULL, N_("do not use"),
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, BRANCH_TRACK_OVERRIDE },
+ 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__COLOR(&branch_use_color, N_("use colored output")),
@@ -608,7 +613,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
OPT_BOOL(0, "edit-description", &edit_description,
N_("edit the description for the branch")),
- OPT__FORCE(&force, N_("force creation, move/rename, deletion")),
+ OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
OPT_MERGED(&filter, N_("print only branches that are merged")),
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
@@ -636,7 +641,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+ head = resolve_refdup("HEAD", 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
@@ -675,6 +680,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
copy *= 2;
}
+ if (list)
+ setup_auto_pager("branch", 1);
+
if (delete) {
if (!argc)
die(_("branch name required"));
@@ -688,7 +696,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
* If no sorting parameter is given then we default to sorting
* by 'refname'. This would give us an alphabetically sorted
* array with the 'HEAD' ref at the beginning followed by
- * local branches 'refs/heads/...' and finally remote-tacking
+ * local branches 'refs/heads/...' and finally remote-tracking
* branches 'refs/remotes/...'.
*/
if (!sorting)
@@ -793,9 +801,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
} else if (argc > 0 && argc <= 2) {
struct branch *branch = branch_get(argv[0]);
- if (!strcmp(argv[0], "HEAD"))
- die(_("it does not make sense to create 'HEAD' manually"));
-
if (!branch)
die(_("no such branch '%s'"), argv[0]);
@@ -806,7 +811,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
create_branch(argv[0], (argc == 2) ? argv[1] : head,
- force, reflog, 0, quiet, track);
+ force, 0, reflog, quiet, track);
} else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index cf9ea5c796..665b581949 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -32,7 +32,7 @@ static int filter_object(const char *path, unsigned mode,
{
enum object_type type;
- *buf = read_sha1_file(oid->hash, &type, size);
+ *buf = read_object_file(oid, &type, size);
if (!*buf)
return error(_("cannot read object %s '%s'"),
oid_to_hex(oid), path);
@@ -76,8 +76,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
buf = NULL;
switch (opt) {
case 't':
- oi.typename = &sb;
- if (sha1_object_info_extended(oid.hash, &oi, flags) < 0)
+ oi.type_name = &sb;
+ if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
die("git cat-file: could not get object info");
if (sb.len) {
printf("%s\n", sb.buf);
@@ -88,7 +88,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 's':
oi.sizep = &size;
- if (sha1_object_info_extended(oid.hash, &oi, flags) < 0)
+ if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
die("git cat-file: could not get object info");
printf("%lu\n", size);
return 0;
@@ -116,7 +116,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
/* else fallthrough */
case 'p':
- type = sha1_object_info(oid.hash, NULL);
+ type = oid_object_info(the_repository, &oid, NULL);
if (type < 0)
die("Not a valid object name %s", obj_name);
@@ -130,7 +130,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (type == OBJ_BLOB)
return stream_blob_to_fd(1, &oid, NULL, 0);
- buf = read_sha1_file(oid.hash, &type, &size);
+ buf = read_object_file(&oid, &type, &size);
if (!buf)
die("Cannot read object %s", obj_name);
@@ -140,8 +140,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 0:
if (type_from_string(exp_type) == OBJ_BLOB) {
struct object_id blob_oid;
- if (sha1_object_info(oid.hash, NULL) == OBJ_TAG) {
- char *buffer = read_sha1_file(oid.hash, &type, &size);
+ if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) {
+ char *buffer = read_object_file(&oid, &type,
+ &size);
const char *target;
if (!skip_prefix(buffer, "object ", &target) ||
get_oid_hex(target, &blob_oid))
@@ -150,7 +151,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
} else
oidcpy(&blob_oid, &oid);
- if (sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
+ if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
return stream_blob_to_fd(1, &blob_oid, NULL, 0);
/*
* we attempted to dereference a tag to a blob
@@ -159,7 +160,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
* fall-back to the usual case.
*/
}
- buf = read_object_with_reference(oid.hash, exp_type, &size, NULL);
+ buf = read_object_with_reference(&oid, exp_type, &size, NULL);
break;
default:
@@ -229,7 +230,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
if (data->mark_query)
data->info.typep = &data->type;
else
- strbuf_addstr(sb, typename(data->type));
+ strbuf_addstr(sb, type_name(data->type));
} else if (is_atom("objectsize", atom, len)) {
if (data->mark_query)
data->info.sizep = &data->size;
@@ -304,13 +305,14 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
enum object_type type;
if (!textconv_object(data->rest, 0100644, oid,
1, &contents, &size))
- contents = read_sha1_file(oid->hash, &type,
- &size);
+ contents = read_object_file(oid,
+ &type,
+ &size);
if (!contents)
die("could not convert '%s' %s",
oid_to_hex(oid), data->rest);
} else
- die("BUG: invalid cmdmode: %c", opt->cmdmode);
+ BUG("invalid cmdmode: %c", opt->cmdmode);
batch_write(opt, contents, size);
free(contents);
} else if (stream_blob_to_fd(1, oid, NULL, 0) < 0)
@@ -321,7 +323,7 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
unsigned long size;
void *contents;
- contents = read_sha1_file(oid->hash, &type, &size);
+ contents = read_object_file(oid, &type, &size);
if (!contents)
die("object %s disappeared", oid_to_hex(oid));
if (type != data->type)
@@ -340,8 +342,8 @@ static void batch_object_write(const char *obj_name, struct batch_options *opt,
struct strbuf buf = STRBUF_INIT;
if (!data->skip_object_info &&
- sha1_object_info_extended(data->oid.hash, &data->info,
- OBJECT_INFO_LOOKUP_REPLACE) < 0) {
+ oid_object_info_extended(the_repository, &data->oid, &data->info,
+ OBJECT_INFO_LOOKUP_REPLACE) < 0) {
printf("%s missing\n",
obj_name ? obj_name : oid_to_hex(&data->oid));
fflush(stdout);
@@ -385,7 +387,7 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt,
(uintmax_t)strlen(obj_name), obj_name);
break;
default:
- die("BUG: unknown get_sha1_with_context result %d\n",
+ BUG("unknown get_sha1_with_context result %d\n",
result);
break;
}
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 3e280b9c7a..ec9a959e08 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -72,7 +72,7 @@ static int check_ignore(struct dir_struct *dir,
{
const char *full_path;
char *seen;
- int num_ignored = 0, dtype = DT_UNKNOWN, i;
+ int num_ignored = 0, i;
struct exclude *exclude;
struct pathspec pathspec;
@@ -104,6 +104,7 @@ static int check_ignore(struct dir_struct *dir,
full_path = pathspec.items[i].match;
exclude = NULL;
if (!seen[i]) {
+ int dtype = DT_UNKNOWN;
exclude = last_exclude_matching(dir, &the_index,
full_path, &dtype);
}
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index 6c40ff110b..bc67d3f0a8 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -39,12 +39,14 @@ static char *collapse_slashes(const char *refname)
static int check_ref_format_branch(const char *arg)
{
struct strbuf sb = STRBUF_INIT;
+ const char *name;
int nongit;
setup_git_directory_gently(&nongit);
- if (strbuf_check_branch_ref(&sb, arg))
+ if (strbuf_check_branch_ref(&sb, arg) ||
+ !skip_prefix(sb.buf, "refs/heads/", &name))
die("'%s' is not a valid branch name", arg);
- printf("%s\n", sb.buf + 11);
+ printf("%s\n", name);
strbuf_release(&sb);
return 0;
}
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 39c8be05dc..a730f6a1aa 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -129,8 +129,6 @@ static const char * const builtin_checkout_index_usage[] = {
NULL
};
-static struct lock_file lock_file;
-
static int option_parse_stage(const struct option *opt,
const char *arg, int unset)
{
@@ -150,7 +148,7 @@ static int option_parse_stage(const struct option *opt,
int cmd_checkout_index(int argc, const char **argv, const char *prefix)
{
int i;
- int newfd = -1;
+ struct lock_file lock_file = LOCK_INIT;
int all = 0;
int read_from_stdin = 0;
int prefix_length;
@@ -159,7 +157,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
struct option builtin_checkout_index_options[] = {
OPT_BOOL('a', "all", &all,
N_("check out all files in the index")),
- OPT__FORCE(&force, N_("force overwrite of existing files")),
+ OPT__FORCE(&force, N_("force overwrite of existing files"), 0),
OPT__QUIET(&quiet,
N_("no warning for existing files and files not in index")),
OPT_BOOL('n', "no-create", &not_new,
@@ -206,7 +204,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
if (index_opt && !state.base_dir_len && !to_tempfile) {
state.refresh_cache = 1;
state.istate = &the_index;
- newfd = hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
}
/* Check out named files first */
@@ -251,7 +249,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
if (all)
checkout_all(prefix, prefix_length);
- if (0 <= newfd &&
+ if (is_lock_file_locked(&lock_file) &&
write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
return 0;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index fc4f8fd2ea..2e1d2376d2 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "config.h"
+#include "checkout.h"
#include "lockfile.h"
#include "parse-options.h"
#include "refs.h"
@@ -53,19 +54,19 @@ struct checkout_opts {
struct tree *source_tree;
};
-static int post_checkout_hook(struct commit *old, struct commit *new,
+static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
int changed)
{
return run_hook_le(NULL, "post-checkout",
- oid_to_hex(old ? &old->object.oid : &null_oid),
- oid_to_hex(new ? &new->object.oid : &null_oid),
+ oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid),
+ oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid),
changed ? "1" : "0", NULL);
- /* "new" can be NULL when checking out from the index before
+ /* "new_commit" can be NULL when checking out from the index before
a commit exists. */
}
-static int update_some(const unsigned char *sha1, struct strbuf *base,
+static int update_some(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode, int stage, void *context)
{
int len;
@@ -77,7 +78,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base,
len = base->len + strlen(pathname);
ce = xcalloc(1, cache_entry_size(len));
- hashcpy(ce->oid.hash, sha1);
+ oidcpy(&ce->oid, oid);
memcpy(ce->name, base->buf, base->len);
memcpy(ce->name + base->len, pathname, len - base->len);
ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
@@ -226,8 +227,7 @@ static int checkout_merged(int pos, const struct checkout *state)
* (it also writes the merge result to the object database even
* when it may contain conflicts).
*/
- if (write_sha1_file(result_buf.ptr, result_buf.size,
- blob_type, oid.hash))
+ if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid))
die(_("Unable to add merge result for '%s'"), path);
free(result_buf.ptr);
ce = make_cache_entry(mode, oid.hash, path, 2, 0);
@@ -247,7 +247,7 @@ static int checkout_paths(const struct checkout_opts *opts,
struct object_id rev;
struct commit *head;
int errs = 0;
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with updating paths"), "--track");
@@ -275,9 +275,7 @@ static int checkout_paths(const struct checkout_opts *opts,
return run_add_interactive(revision, "--patch=checkout",
&opts->pathspec);
- lock_file = xcalloc(1, sizeof(struct lock_file));
-
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(&opts->pathspec) < 0)
return error(_("index file corrupt"));
@@ -376,10 +374,10 @@ static int checkout_paths(const struct checkout_opts *opts,
}
errs |= finish_delayed_checkout(&state);
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- read_ref_full("HEAD", 0, rev.hash, NULL);
+ read_ref_full("HEAD", 0, &rev, NULL);
head = lookup_commit_reference_gently(&rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -402,10 +400,16 @@ static void show_local_changes(struct object *head,
static void describe_detached_head(const char *msg, struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
+
if (!parse_commit(commit))
pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
- fprintf(stderr, "%s %s... %s\n", msg,
- find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
+ if (print_sha1_ellipsis()) {
+ fprintf(stderr, "%s %s... %s\n", msg,
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+ } else {
+ fprintf(stderr, "%s %s %s\n", msg,
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+ }
strbuf_release(&sb);
}
@@ -467,20 +471,21 @@ static void setup_branch_path(struct branch_info *branch)
}
static int merge_working_tree(const struct checkout_opts *opts,
- struct branch_info *old,
- struct branch_info *new,
+ struct branch_info *old_branch_info,
+ struct branch_info *new_branch_info,
int *writeout_error)
{
int ret;
- struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock_file = LOCK_INIT;
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(NULL) < 0)
return error(_("index file corrupt"));
resolve_undo_clear();
if (opts->force) {
- ret = reset_tree(new->commit->tree, opts, 1, writeout_error);
+ ret = reset_tree(get_commit_tree(new_branch_info->commit),
+ opts, 1, writeout_error);
if (ret)
return ret;
} else {
@@ -506,7 +511,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
topts.initial_checkout = is_cache_unborn();
topts.update = 1;
topts.merge = 1;
- topts.gently = opts->merge && old->commit;
+ topts.gently = opts->merge && old_branch_info->commit;
topts.verbose_update = opts->show_progress;
topts.fn = twoway_merge;
if (opts->overwrite_ignore) {
@@ -514,14 +519,15 @@ static int merge_working_tree(const struct checkout_opts *opts,
topts.dir->flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(topts.dir);
}
- tree = parse_tree_indirect(old->commit ?
- &old->commit->object.oid :
- &empty_tree_oid);
+ tree = parse_tree_indirect(old_branch_info->commit ?
+ &old_branch_info->commit->object.oid :
+ the_hash_algo->empty_tree);
init_tree_desc(&trees[0], tree->buffer, tree->size);
- tree = parse_tree_indirect(&new->commit->object.oid);
+ tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
init_tree_desc(&trees[1], tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
+ clear_unpack_trees_porcelain(&topts);
if (ret == -1) {
/*
* Unpack couldn't do a trivial merge; either
@@ -535,10 +541,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
return 1;
/*
- * Without old->commit, the below is the same as
+ * Without old_branch_info->commit, the below is the same as
* the two-tree unpack we already tried and failed.
*/
- if (!old->commit)
+ if (!old_branch_info->commit)
return 1;
/* Do more real merge */
@@ -566,18 +572,23 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.verbosity = 0;
work = write_tree_from_memory(&o);
- ret = reset_tree(new->commit->tree, opts, 1,
+ ret = reset_tree(get_commit_tree(new_branch_info->commit),
+ opts, 1,
writeout_error);
if (ret)
return ret;
- o.ancestor = old->name;
- o.branch1 = new->name;
+ o.ancestor = old_branch_info->name;
+ o.branch1 = new_branch_info->name;
o.branch2 = "local";
- ret = merge_trees(&o, new->commit->tree, work,
- old->commit->tree, &result);
+ ret = merge_trees(&o,
+ get_commit_tree(new_branch_info->commit),
+ work,
+ get_commit_tree(old_branch_info->commit),
+ &result);
if (ret < 0)
exit(128);
- ret = reset_tree(new->commit->tree, opts, 0,
+ ret = reset_tree(get_commit_tree(new_branch_info->commit),
+ opts, 0,
writeout_error);
strbuf_release(&o.obuf);
if (ret)
@@ -591,29 +602,29 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (!cache_tree_fully_valid(active_cache_tree))
cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
if (!opts->force && !opts->quiet)
- show_local_changes(&new->commit->object, &opts->diff_options);
+ show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
return 0;
}
-static void report_tracking(struct branch_info *new)
+static void report_tracking(struct branch_info *new_branch_info)
{
struct strbuf sb = STRBUF_INIT;
- struct branch *branch = branch_get(new->name);
+ struct branch *branch = branch_get(new_branch_info->name);
- if (!format_tracking_info(branch, &sb))
+ if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))
return;
fputs(sb.buf, stdout);
strbuf_release(&sb);
}
static void update_refs_for_switch(const struct checkout_opts *opts,
- struct branch_info *old,
- struct branch_info *new)
+ struct branch_info *old_branch_info,
+ struct branch_info *new_branch_info)
{
struct strbuf msg = STRBUF_INIT;
const char *old_desc, *reflog_msg;
@@ -640,69 +651,69 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
free(refname);
}
else
- create_branch(opts->new_branch, new->name,
+ create_branch(opts->new_branch, new_branch_info->name,
opts->new_branch_force ? 1 : 0,
- opts->new_branch_log,
opts->new_branch_force ? 1 : 0,
+ opts->new_branch_log,
opts->quiet,
opts->track);
- new->name = opts->new_branch;
- setup_branch_path(new);
+ new_branch_info->name = opts->new_branch;
+ setup_branch_path(new_branch_info);
}
- old_desc = old->name;
- if (!old_desc && old->commit)
- old_desc = oid_to_hex(&old->commit->object.oid);
+ old_desc = old_branch_info->name;
+ if (!old_desc && old_branch_info->commit)
+ old_desc = oid_to_hex(&old_branch_info->commit->object.oid);
reflog_msg = getenv("GIT_REFLOG_ACTION");
if (!reflog_msg)
strbuf_addf(&msg, "checkout: moving from %s to %s",
- old_desc ? old_desc : "(invalid)", new->name);
+ old_desc ? old_desc : "(invalid)", new_branch_info->name);
else
strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
- if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
+ if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
/* Nothing to do. */
- } else if (opts->force_detach || !new->path) { /* No longer on any branch. */
- update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
- REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+ } else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */
+ update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
if (!opts->quiet) {
- if (old->path &&
+ if (old_branch_info->path &&
advice_detached_head && !opts->force_detach)
- detach_advice(new->name);
- describe_detached_head(_("HEAD is now at"), new->commit);
+ detach_advice(new_branch_info->name);
+ describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
}
- } else if (new->path) { /* Switch branches. */
- if (create_symref("HEAD", new->path, msg.buf) < 0)
+ } else if (new_branch_info->path) { /* Switch branches. */
+ if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
die(_("unable to update HEAD"));
if (!opts->quiet) {
- if (old->path && !strcmp(new->path, old->path)) {
+ if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
if (opts->new_branch_force)
fprintf(stderr, _("Reset branch '%s'\n"),
- new->name);
+ new_branch_info->name);
else
fprintf(stderr, _("Already on '%s'\n"),
- new->name);
+ new_branch_info->name);
} else if (opts->new_branch) {
if (opts->branch_exists)
- fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
+ fprintf(stderr, _("Switched to and reset branch '%s'\n"), new_branch_info->name);
else
- fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name);
+ fprintf(stderr, _("Switched to a new branch '%s'\n"), new_branch_info->name);
} else {
fprintf(stderr, _("Switched to branch '%s'\n"),
- new->name);
+ new_branch_info->name);
}
}
- if (old->path && old->name) {
- if (!ref_exists(old->path) && reflog_exists(old->path))
- delete_reflog(old->path);
+ if (old_branch_info->path && old_branch_info->name) {
+ if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
+ delete_reflog(old_branch_info->path);
}
}
remove_branch_state();
strbuf_release(&msg);
if (!opts->quiet &&
- (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
- report_tracking(new);
+ (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
+ report_tracking(new_branch_info);
}
static int add_pending_uninteresting_ref(const char *refname,
@@ -716,7 +727,7 @@ static int add_pending_uninteresting_ref(const char *refname,
static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
{
strbuf_addstr(sb, " ");
- strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
strbuf_addch(sb, ' ');
if (!parse_commit(commit))
pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@ -774,7 +785,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
" git branch <new-branch-name> %s\n\n",
/* Give ngettext() the count */
lost),
- find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
}
/*
@@ -782,11 +793,10 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
* HEAD. If it is not reachable from any ref, this is the last chance
* for the user to do so without resorting to reflog.
*/
-static void orphaned_commit_warning(struct commit *old, struct commit *new)
+static void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit)
{
struct rev_info revs;
- struct object *object = &old->object;
- struct object_array refs;
+ struct object *object = &old_commit->object;
init_revisions(&revs, NULL);
setup_revisions(0, NULL, &revs, NULL);
@@ -795,67 +805,57 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
add_pending_object(&revs, object, oid_to_hex(&object->oid));
for_each_ref(add_pending_uninteresting_ref, &revs);
- add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
-
- /* Save pending objects, so they can be cleaned up later. */
- refs = revs.pending;
- revs.leak_pending = 1;
+ add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
- /*
- * prepare_revision_walk (together with .leak_pending = 1) makes us
- * the sole owner of the list of pending objects.
- */
if (prepare_revision_walk(&revs))
die(_("internal error in revision walk"));
- if (!(old->object.flags & UNINTERESTING))
- suggest_reattach(old, &revs);
+ if (!(old_commit->object.flags & UNINTERESTING))
+ suggest_reattach(old_commit, &revs);
else
- describe_detached_head(_("Previous HEAD position was"), old);
+ describe_detached_head(_("Previous HEAD position was"), old_commit);
/* Clean up objects used, as they will be reused. */
- clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-
- object_array_clear(&refs);
+ clear_commit_marks_all(ALL_REV_FLAGS);
}
static int switch_branches(const struct checkout_opts *opts,
- struct branch_info *new)
+ struct branch_info *new_branch_info)
{
int ret = 0;
- struct branch_info old;
+ struct branch_info old_branch_info;
void *path_to_free;
struct object_id rev;
int flag, writeout_error = 0;
- memset(&old, 0, sizeof(old));
- old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
- if (old.path)
- old.commit = lookup_commit_reference_gently(&rev, 1);
+ memset(&old_branch_info, 0, sizeof(old_branch_info));
+ old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
+ if (old_branch_info.path)
+ old_branch_info.commit = lookup_commit_reference_gently(&rev, 1);
if (!(flag & REF_ISSYMREF))
- old.path = NULL;
+ old_branch_info.path = NULL;
- if (old.path)
- skip_prefix(old.path, "refs/heads/", &old.name);
+ if (old_branch_info.path)
+ skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
- if (!new->name) {
- new->name = "HEAD";
- new->commit = old.commit;
- if (!new->commit)
+ if (!new_branch_info->name) {
+ new_branch_info->name = "HEAD";
+ new_branch_info->commit = old_branch_info.commit;
+ if (!new_branch_info->commit)
die(_("You are on a branch yet to be born"));
- parse_commit_or_die(new->commit);
+ parse_commit_or_die(new_branch_info->commit);
}
- ret = merge_working_tree(opts, &old, new, &writeout_error);
+ ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
if (ret) {
free(path_to_free);
return ret;
}
- if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
- orphaned_commit_warning(old.commit, new->commit);
+ if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit)
+ orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit);
- update_refs_for_switch(opts, &old, new);
+ update_refs_for_switch(opts, &old_branch_info, new_branch_info);
- ret = post_checkout_hook(old.commit, new->commit, 1);
+ ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
free(path_to_free);
return ret || writeout_error;
}
@@ -874,49 +874,9 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
return git_xmerge_config(var, value, NULL);
}
-struct tracking_name_data {
- /* const */ char *src_ref;
- char *dst_ref;
- struct object_id *dst_oid;
- int unique;
-};
-
-static int check_tracking_name(struct remote *remote, void *cb_data)
-{
- struct tracking_name_data *cb = cb_data;
- struct refspec query;
- memset(&query, 0, sizeof(struct refspec));
- query.src = cb->src_ref;
- if (remote_find_tracking(remote, &query) ||
- get_oid(query.dst, cb->dst_oid)) {
- free(query.dst);
- return 0;
- }
- if (cb->dst_ref) {
- free(query.dst);
- cb->unique = 0;
- return 0;
- }
- cb->dst_ref = query.dst;
- return 0;
-}
-
-static const char *unique_tracking_name(const char *name, struct object_id *oid)
-{
- struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
- cb_data.src_ref = xstrfmt("refs/heads/%s", name);
- cb_data.dst_oid = oid;
- for_each_remote(check_tracking_name, &cb_data);
- free(cb_data.src_ref);
- if (cb_data.unique)
- return cb_data.dst_ref;
- free(cb_data.dst_ref);
- return NULL;
-}
-
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
- struct branch_info *new,
+ struct branch_info *new_branch_info,
struct checkout_opts *opts,
struct object_id *rev)
{
@@ -1034,22 +994,22 @@ static int parse_branchname_arg(int argc, const char **argv,
argv++;
argc--;
- new->name = arg;
- setup_branch_path(new);
+ new_branch_info->name = arg;
+ setup_branch_path(new_branch_info);
- if (!check_refname_format(new->path, 0) &&
- !read_ref(new->path, branch_rev.hash))
+ if (!check_refname_format(new_branch_info->path, 0) &&
+ !read_ref(new_branch_info->path, &branch_rev))
oidcpy(rev, &branch_rev);
else
- new->path = NULL; /* not an existing branch */
+ new_branch_info->path = NULL; /* not an existing branch */
- new->commit = lookup_commit_reference_gently(rev, 1);
- if (!new->commit) {
+ new_branch_info->commit = lookup_commit_reference_gently(rev, 1);
+ if (!new_branch_info->commit) {
/* not a commit */
*source_tree = parse_tree_indirect(rev);
} else {
- parse_commit_or_die(new->commit);
- *source_tree = new->commit->tree;
+ parse_commit_or_die(new_branch_info->commit);
+ *source_tree = get_commit_tree(new_branch_info->commit);
}
if (!*source_tree) /* case (1): want a tree */
@@ -1089,7 +1049,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
}
static int checkout_branch(struct checkout_opts *opts,
- struct branch_info *new)
+ struct branch_info *new_branch_info)
{
if (opts->pathspec.nr)
die(_("paths cannot be used with switching branches"));
@@ -1118,35 +1078,35 @@ static int checkout_branch(struct checkout_opts *opts,
} else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
opts->track = git_branch_track;
- if (new->name && !new->commit)
+ if (new_branch_info->name && !new_branch_info->commit)
die(_("Cannot switch branch to a non-commit '%s'"),
- new->name);
+ new_branch_info->name);
- if (new->path && !opts->force_detach && !opts->new_branch &&
+ if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
!opts->ignore_other_worktrees) {
int flag;
char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
if (head_ref &&
- (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
- die_if_checked_out(new->path, 1);
+ (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
+ die_if_checked_out(new_branch_info->path, 1);
free(head_ref);
}
- if (!new->commit && opts->new_branch) {
+ if (!new_branch_info->commit && opts->new_branch) {
struct object_id rev;
int flag;
- if (!read_ref_full("HEAD", 0, rev.hash, &flag) &&
+ if (!read_ref_full("HEAD", 0, &rev, &flag) &&
(flag & REF_ISSYMREF) && is_null_oid(&rev))
return switch_unborn_to_new_branch(opts);
}
- return switch_branches(opts, new);
+ return switch_branches(opts, new_branch_info);
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
- struct branch_info new;
+ struct branch_info new_branch_info;
char *conflict_style = NULL;
int dwim_new_local_branch = 1;
struct option options[] = {
@@ -1164,9 +1124,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
3),
- OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")),
+ OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+ PARSE_OPT_NOCOMPLETE),
OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
- OPT_BOOL(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")),
+ OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+ N_("update ignored files (default)"),
+ PARSE_OPT_NOCOMPLETE),
OPT_STRING(0, "conflict", &conflict_style, N_("style"),
N_("conflict style (merge or diff3)")),
OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
@@ -1184,7 +1147,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
};
memset(&opts, 0, sizeof(opts));
- memset(&new, 0, sizeof(new));
+ memset(&new_branch_info, 0, sizeof(new_branch_info));
opts.overwrite_ignore = 1;
opts.prefix = prefix;
opts.show_progress = -1;
@@ -1256,7 +1219,7 @@ int cmd_checkout(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, &opts, &rev);
+ &new_branch_info, &opts, &rev);
argv += n;
argc -= n;
}
@@ -1289,17 +1252,17 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT;
- opts.branch_exists =
- validate_new_branchname(opts.new_branch, &buf,
- !!opts.new_branch_force,
- !!opts.new_branch_force);
-
+ if (opts.new_branch_force)
+ opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+ else
+ opts.branch_exists =
+ validate_new_branchname(opts.new_branch, &buf, 0);
strbuf_release(&buf);
}
UNLEAK(opts);
if (opts.patch_mode || opts.pathspec.nr)
- return checkout_paths(&opts, new.name);
+ return checkout_paths(&opts, new_branch_info.name);
else
- return checkout_branch(&opts, &new);
+ return checkout_branch(&opts, &new_branch_info);
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 189e20628c..ab402c204c 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -16,6 +16,7 @@
#include "column.h"
#include "color.h"
#include "pathspec.h"
+#include "help.h"
static int force = -1; /* unset */
static int interactive;
@@ -42,6 +43,15 @@ enum color_clean {
CLEAN_COLOR_ERROR = 5
};
+static const char *color_interactive_slots[] = {
+ [CLEAN_COLOR_ERROR] = "error",
+ [CLEAN_COLOR_HEADER] = "header",
+ [CLEAN_COLOR_HELP] = "help",
+ [CLEAN_COLOR_PLAIN] = "plain",
+ [CLEAN_COLOR_PROMPT] = "prompt",
+ [CLEAN_COLOR_RESET] = "reset",
+};
+
static int clean_use_color = -1;
static char clean_colors[][COLOR_MAXLEN] = {
[CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
@@ -82,22 +92,7 @@ struct menu_stuff {
void *stuff;
};
-static int parse_clean_color_slot(const char *var)
-{
- if (!strcasecmp(var, "reset"))
- return CLEAN_COLOR_RESET;
- if (!strcasecmp(var, "plain"))
- return CLEAN_COLOR_PLAIN;
- if (!strcasecmp(var, "prompt"))
- return CLEAN_COLOR_PROMPT;
- if (!strcasecmp(var, "header"))
- return CLEAN_COLOR_HEADER;
- if (!strcasecmp(var, "help"))
- return CLEAN_COLOR_HELP;
- if (!strcasecmp(var, "error"))
- return CLEAN_COLOR_ERROR;
- return -1;
-}
+define_list_config_array(color_interactive_slots);
static int git_clean_config(const char *var, const char *value, void *cb)
{
@@ -113,7 +108,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
return 0;
}
if (skip_prefix(var, "color.interactive.", &slot_name)) {
- int slot = parse_clean_color_slot(slot_name);
+ int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
if (slot < 0)
return 0;
if (!value)
@@ -909,7 +904,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print names of files removed")),
OPT__DRY_RUN(&dry_run, N_("dry run")),
- OPT__FORCE(&force, N_("force")),
+ OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
OPT_BOOL('d', NULL, &remove_directories,
N_("remove whole directories")),
diff --git a/builtin/clone.c b/builtin/clone.c
index f519bd49b0..74a804f2e8 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -14,6 +14,7 @@
#include "parse-options.h"
#include "fetch-pack.h"
#include "refs.h"
+#include "refspec.h"
#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
@@ -27,6 +28,7 @@
#include "connected.h"
#include "packfile.h"
#include "list-objects-filter-options.h"
+#include "object-store.h"
/*
* Overall FIXMEs:
@@ -455,7 +457,8 @@ static void clone_local(const char *src_repo, const char *dest_repo)
{
if (option_shared) {
struct strbuf alt = STRBUF_INIT;
- strbuf_addf(&alt, "%s/objects", src_repo);
+ get_common_dir(&alt, src_repo);
+ strbuf_addstr(&alt, "/objects");
add_to_alternates_file(alt.buf);
strbuf_release(&alt);
} else {
@@ -475,7 +478,9 @@ static void clone_local(const char *src_repo, const char *dest_repo)
}
static const char *junk_work_tree;
+static int junk_work_tree_flags;
static const char *junk_git_dir;
+static int junk_git_dir_flags;
static enum {
JUNK_LEAVE_NONE,
JUNK_LEAVE_REPO,
@@ -504,12 +509,12 @@ static void remove_junk(void)
if (junk_git_dir) {
strbuf_addstr(&sb, junk_git_dir);
- remove_dir_recursively(&sb, 0);
+ remove_dir_recursively(&sb, junk_git_dir_flags);
strbuf_reset(&sb);
}
if (junk_work_tree) {
strbuf_addstr(&sb, junk_work_tree);
- remove_dir_recursively(&sb, 0);
+ remove_dir_recursively(&sb, junk_work_tree_flags);
}
strbuf_release(&sb);
}
@@ -542,7 +547,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch
}
static struct ref *wanted_peer_refs(const struct ref *refs,
- struct refspec *refspec)
+ struct refspec_item *refspec)
{
struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
struct ref *local_refs = head;
@@ -591,7 +596,7 @@ static void write_remote_refs(const struct ref *local_refs)
for (r = local_refs; r; r = r->next) {
if (!r->peer_ref)
continue;
- if (ref_transaction_create(t, r->peer_ref->name, r->old_oid.hash,
+ if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
0, NULL, &err))
die("%s", err.buf);
}
@@ -613,12 +618,12 @@ static void write_followtags(const struct ref *refs, const char *msg)
continue;
if (!has_object_file(&ref->old_oid))
continue;
- update_ref(msg, ref->name, ref->old_oid.hash,
- NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
}
}
-static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
@@ -633,7 +638,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
if (!ref)
return -1;
- hashcpy(sha1, ref->old_oid.hash);
+ oidcpy(oid, &ref->old_oid);
*rm = ref->next;
return 0;
}
@@ -685,23 +690,23 @@ static void update_head(const struct ref *our, const struct ref *remote,
if (create_symref("HEAD", our->name, NULL) < 0)
die(_("unable to update HEAD"));
if (!option_bare) {
- update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
+ update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
install_branch_config(0, head, option_origin, our->name);
}
} else if (our) {
struct commit *c = lookup_commit_reference(&our->old_oid);
/* --branch specifies a non-branch (i.e. tags), detach HEAD */
- update_ref(msg, "HEAD", c->object.oid.hash,
- NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+ update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_DIE_ON_ERR);
} else if (remote) {
/*
* We know remote HEAD points to a non-branch, or
* HEAD points to a branch but we don't know which one.
* Detach HEAD in all these cases.
*/
- update_ref(msg, "HEAD", remote->old_oid.hash,
- NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+ update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_DIE_ON_ERR);
}
}
@@ -709,7 +714,7 @@ static int checkout(int submodule_progress)
{
struct object_id oid;
char *head;
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
struct unpack_trees_options opts;
struct tree *tree;
struct tree_desc t;
@@ -718,7 +723,7 @@ static int checkout(int submodule_progress)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout.\n"));
@@ -736,8 +741,7 @@ static int checkout(int submodule_progress)
/* We need to be in the new work tree for the checkout */
setup_work_tree();
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof opts);
opts.update = 1;
@@ -753,7 +757,7 @@ static int checkout(int submodule_progress)
if (unpack_trees(1, &t, &opts) < 0)
die(_("unable to checkout working tree"));
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ 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),
@@ -820,7 +824,7 @@ static void write_refspec_config(const char *src_ref_prefix,
} else if (remote_head_points_at) {
const char *head = remote_head_points_at->name;
if (!skip_prefix(head, "refs/heads/", &head))
- die("BUG: remote HEAD points at non-head?");
+ BUG("remote HEAD points at non-head?");
strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name,
branch_top->buf, head);
@@ -866,10 +870,15 @@ static void dissociate_from_references(void)
free(alternates);
}
+static int dir_exists(const char *path)
+{
+ struct stat sb;
+ return !stat(path, &sb);
+}
+
int cmd_clone(int argc, const char **argv, const char *prefix)
{
int is_bundle = 0, is_local;
- struct stat buf;
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir;
int dest_exists;
@@ -886,8 +895,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
- struct refspec *refspec;
- const char *fetch_pattern;
+ struct refspec_item refspec;
fetch_if_missing = 0;
@@ -943,7 +951,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 = !stat(dir, &buf);
+ dest_exists = dir_exists(dir);
if (dest_exists && !is_empty_dir(dir))
die(_("destination path '%s' already exists and is not "
"an empty directory."), dir);
@@ -954,7 +962,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 && !stat(work_tree, &buf))
+ if (work_tree && dir_exists(work_tree))
die(_("working tree '%s' already exists."), work_tree);
}
@@ -972,14 +980,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (safe_create_leading_directories_const(work_tree) < 0)
die_errno(_("could not create leading directories of '%s'"),
work_tree);
- if (!dest_exists && mkdir(work_tree, 0777))
+ if (dest_exists)
+ junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+ else if (mkdir(work_tree, 0777))
die_errno(_("could not create work tree dir '%s'"),
work_tree);
junk_work_tree = work_tree;
set_git_work_tree(work_tree);
}
- junk_git_dir = real_git_dir ? real_git_dir : git_dir;
+ if (real_git_dir) {
+ if (dir_exists(real_git_dir))
+ junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+ junk_git_dir = real_git_dir;
+ } else {
+ if (dest_exists)
+ junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+ junk_git_dir = git_dir;
+ }
if (safe_create_leading_directories_const(git_dir) < 0)
die(_("could not create leading directories of '%s'"), git_dir);
@@ -1059,8 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
- fetch_pattern = value.buf;
- refspec = parse_fetch_refspec(1, &fetch_pattern);
+ refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);
strbuf_reset(&value);
@@ -1090,9 +1107,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
warning(_("--local is ignored"));
transport->cloning = 1;
- if (!transport->get_refs_list || (!is_local && !transport->fetch))
- die(_("Don't know how to clone %s"), transport->url);
-
transport_set_option(transport, TRANS_OPT_KEEP, "yes");
if (option_depth)
@@ -1120,10 +1134,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (transport->smart_options && !deepen && !filter_options.choice)
transport->smart_options->check_self_contained_and_connected = 1;
- refs = transport_get_remote_refs(transport);
+ refs = transport_get_remote_refs(transport, NULL);
if (refs) {
- mapped_refs = wanted_peer_refs(refs, refspec);
+ mapped_refs = wanted_peer_refs(refs, &refspec);
/*
* transport_get_remote_refs() may return refs with null sha-1
* in mapped_refs (see struct transport->get_refs_list
@@ -1203,7 +1217,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_disconnect(transport);
if (option_dissociate) {
- close_all_packs();
+ close_all_packs(the_repository->objects);
dissociate_from_references();
}
@@ -1217,6 +1231,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_release(&value);
junk_mode = JUNK_LEAVE_ALL;
- free(refspec);
+ refspec_item_clear(&refspec);
return err;
}
diff --git a/builtin/column.c b/builtin/column.c
index 0c3223d64b..5228ccf37a 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -42,7 +42,6 @@ int cmd_column(int argc, const char **argv, const char *prefix)
git_config(column_config, NULL);
memset(&copts, 0, sizeof(copts));
- copts.width = term_columns();
copts.padding = 1;
argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
if (argc)
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
new file mode 100644
index 0000000000..37420ae0fd
--- /dev/null
+++ b/builtin/commit-graph.c
@@ -0,0 +1,171 @@
+#include "builtin.h"
+#include "config.h"
+#include "dir.h"
+#include "lockfile.h"
+#include "parse-options.h"
+#include "commit-graph.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 write [--object-dir <objdir>] [--append] [--stdin-packs|--stdin-commits]"),
+ NULL
+};
+
+static const char * const builtin_commit_graph_read_usage[] = {
+ N_("git commit-graph read [--object-dir <objdir>]"),
+ NULL
+};
+
+static const char * const builtin_commit_graph_write_usage[] = {
+ N_("git commit-graph write [--object-dir <objdir>] [--append] [--stdin-packs|--stdin-commits]"),
+ NULL
+};
+
+static struct opts_commit_graph {
+ const char *obj_dir;
+ int stdin_packs;
+ int stdin_commits;
+ int append;
+} opts;
+
+static int graph_read(int argc, const char **argv)
+{
+ struct commit_graph *graph = NULL;
+ char *graph_name;
+
+ 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);
+ graph = load_commit_graph_one(graph_name);
+
+ if (!graph)
+ die("graph file %s does not exist", graph_name);
+ 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_large_edges)
+ printf(" large_edges");
+ printf("\n");
+
+ return 0;
+}
+
+static int graph_write(int argc, const char **argv)
+{
+ const char **pack_indexes = NULL;
+ int packs_nr = 0;
+ const char **commit_hex = NULL;
+ int commits_nr = 0;
+ const char **lines = NULL;
+ int lines_nr = 0;
+ int lines_alloc = 0;
+
+ static struct option builtin_commit_graph_write_options[] = {
+ OPT_STRING(0, "object-dir", &opts.obj_dir,
+ N_("dir"),
+ N_("The object directory to store the graph")),
+ OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
+ N_("scan pack-indexes listed by stdin for commits")),
+ OPT_BOOL(0, "stdin-commits", &opts.stdin_commits,
+ 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_END(),
+ };
+
+ argc = parse_options(argc, argv, NULL,
+ builtin_commit_graph_write_options,
+ builtin_commit_graph_write_usage, 0);
+
+ if (opts.stdin_packs && opts.stdin_commits)
+ die(_("cannot use both --stdin-commits and --stdin-packs"));
+ if (!opts.obj_dir)
+ opts.obj_dir = get_object_directory();
+
+ if (opts.stdin_packs || opts.stdin_commits) {
+ struct strbuf buf = STRBUF_INIT;
+ lines_nr = 0;
+ lines_alloc = 128;
+ ALLOC_ARRAY(lines, lines_alloc);
+
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ ALLOC_GROW(lines, lines_nr + 1, lines_alloc);
+ lines[lines_nr++] = strbuf_detach(&buf, NULL);
+ }
+
+ if (opts.stdin_packs) {
+ pack_indexes = lines;
+ packs_nr = lines_nr;
+ }
+ if (opts.stdin_commits) {
+ commit_hex = lines;
+ commits_nr = lines_nr;
+ }
+ }
+
+ write_commit_graph(opts.obj_dir,
+ pack_indexes,
+ packs_nr,
+ commit_hex,
+ commits_nr,
+ opts.append);
+
+ return 0;
+}
+
+int cmd_commit_graph(int argc, const char **argv, const char *prefix)
+{
+ static struct option builtin_commit_graph_options[] = {
+ OPT_STRING(0, "object-dir", &opts.obj_dir,
+ N_("dir"),
+ N_("The object directory to store the graph")),
+ OPT_END(),
+ };
+
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage_with_options(builtin_commit_graph_usage,
+ builtin_commit_graph_options);
+
+ git_config(git_default_config, NULL);
+ argc = parse_options(argc, argv, prefix,
+ builtin_commit_graph_options,
+ builtin_commit_graph_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (argc > 0) {
+ if (!strcmp(argv[0], "read"))
+ return graph_read(argc, argv);
+ if (!strcmp(argv[0], "write"))
+ return graph_write(argc, argv);
+ }
+
+ usage_with_options(builtin_commit_graph_usage,
+ builtin_commit_graph_options);
+}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 2177251e24..ecf42191da 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -58,7 +58,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
usage(commit_tree_usage);
if (get_oid_commit(argv[i], &oid))
die("Not a valid object name %s", argv[i]);
- assert_sha1_type(oid.hash, OBJ_COMMIT);
+ assert_oid_type(&oid, OBJ_COMMIT);
new_parent(lookup_commit(&oid), &parents);
continue;
}
@@ -117,8 +117,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
die_errno("git commit-tree: failed to read");
}
- if (commit_tree(buffer.buf, buffer.len, tree_oid.hash, parents,
- commit_oid.hash, NULL, sign_commit)) {
+ if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,
+ NULL, sign_commit)) {
strbuf_release(&buffer);
return 1;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index d75b3805ea..9bcbb0c25c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,8 @@
#include "gpg-interface.h"
#include "column.h"
#include "sequencer.h"
-#include "notes-utils.h"
#include "mailmap.h"
-#include "sigchain.h"
+#include "help.h"
static const char * const builtin_commit_usage[] = {
N_("git commit [<options>] [--] <pathspec>..."),
@@ -45,31 +44,6 @@ static const char * const builtin_status_usage[] = {
NULL
};
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-" git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-" git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-" git config --global user.name \"Your Name\"\n"
-" git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-" git commit --amend --reset-author\n");
-
static const char empty_amend_advice[] =
N_("You asked to amend the most recent commit, but doing so would make\n"
"it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -93,7 +67,17 @@ N_("If you wish to skip this commit, use:\n"
"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
"the remaining commits.\n");
-static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
+static const char *color_status_slots[] = {
+ [WT_STATUS_HEADER] = "header",
+ [WT_STATUS_UPDATED] = "updated",
+ [WT_STATUS_CHANGED] = "changed",
+ [WT_STATUS_UNTRACKED] = "untracked",
+ [WT_STATUS_NOBRANCH] = "noBranch",
+ [WT_STATUS_UNMERGED] = "unmerged",
+ [WT_STATUS_LOCAL_BRANCH] = "localBranch",
+ [WT_STATUS_REMOTE_BRANCH] = "remoteBranch",
+ [WT_STATUS_ONBRANCH] = "branch",
+};
static const char *use_message_buffer;
static struct lock_file index_lock; /* real index */
@@ -118,7 +102,7 @@ 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 char *untracked_files_arg, *force_date, *ignore_submodule_arg;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
static char *sign_commit;
/*
@@ -128,18 +112,13 @@ static char *sign_commit;
* if editor is used, and only the whitespaces if the message
* is specified explicitly.
*/
-static enum {
- CLEANUP_SPACE,
- CLEANUP_NONE,
- CLEANUP_SCISSORS,
- CLEANUP_ALL
-} cleanup_mode;
+static enum commit_msg_cleanup_mode cleanup_mode;
static const char *cleanup_arg;
static enum commit_whence whence;
static int sequencer_in_use;
static int use_editor = 1, include_status = 1;
-static int show_ignored_in_status, have_option_m;
+static int have_option_m;
static struct strbuf message = STRBUF_INIT;
static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
@@ -177,6 +156,16 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset)
+{
+ const char **value = opt->value;
+ if (arg != NULL && *arg == '=')
+ arg = arg + 1;
+
+ *value = arg;
+ return 0;
+}
+
static void determine_whence(struct wt_status *s)
{
if (file_exists(git_path_merge_head()))
@@ -195,9 +184,9 @@ static void determine_whence(struct wt_status *s)
static void status_init_config(struct wt_status *s, config_fn_t fn)
{
wt_status_prepare(s);
+ init_diff_ui_defaults();
git_config(fn, s);
determine_whence(s);
- init_diff_ui_defaults();
s->hints = advice_status_hints; /* must come after git_config() */
}
@@ -252,8 +241,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
if (with_tree) {
char *max_prefix = common_prefix(pattern);
- overlay_tree_on_index(&the_index, with_tree,
- max_prefix ? max_prefix : prefix);
+ overlay_tree_on_index(&the_index, with_tree, max_prefix);
free(max_prefix);
}
@@ -355,7 +343,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
refresh_cache_or_die(refresh_flags);
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to create temporary index"));
old_index_env = getenv(INDEX_ENVIRONMENT);
@@ -374,7 +362,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to update temporary index"));
} else
warning(_("Failed to update main cache tree"));
@@ -401,7 +389,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
refresh_cache_or_die(refresh_flags);
update_main_cache_tree(WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
ret = get_lock_file_path(&index_lock);
@@ -423,13 +411,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
if (active_cache_changed
|| !cache_tree_fully_valid(active_cache_tree))
update_main_cache_tree(WRITE_TREE_SILENT);
- if (active_cache_changed) {
- if (write_locked_index(&the_index, &index_lock,
- COMMIT_LOCK))
- die(_("unable to write new_index file"));
- } else {
- rollback_lock_file(&index_lock);
- }
+ if (write_locked_index(&the_index, &index_lock,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ die(_("unable to write new_index file"));
commit_style = COMMIT_AS_IS;
ret = get_index_file();
goto out;
@@ -474,7 +458,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
update_main_cache_tree(WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
hold_lock_file_for_update(&false_lock,
@@ -486,7 +470,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &false_lock, 0))
die(_("unable to write temporary index file"));
discard_cache();
@@ -534,7 +518,7 @@ static int is_a_merge(const struct commit *current_head)
static void assert_split_ident(struct ident_split *id, const struct strbuf *buf)
{
if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin)
- die("BUG: unable to parse our own ident: %s", buf->buf);
+ BUG("unable to parse our own ident: %s", buf->buf);
}
static void export_one(const char *var, const char *s, const char *e, int hack)
@@ -673,7 +657,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct strbuf sb = STRBUF_INIT;
const char *hook_arg1 = NULL;
const char *hook_arg2 = NULL;
- int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+ int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
int old_display_comment_prefix;
/* This checks and barfs if author is badly specified */
@@ -701,7 +685,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
}
- if (have_option_m) {
+ if (have_option_m && !fixup_message) {
strbuf_addbuf(&sb, &message);
hook_arg1 = "message";
} else if (logfile && !strcmp(logfile, "-")) {
@@ -731,6 +715,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
ctx.output_encoding = get_commit_output_encoding();
format_commit_message(commit, "fixup! %s\n\n",
&sb, &ctx);
+ if (have_option_m)
+ strbuf_addbuf(&sb, &message);
hook_arg1 = "message";
} else if (!stat(git_path_merge_msg(), &statbuf)) {
/*
@@ -812,7 +798,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct ident_split ci, ai;
if (whence != FROM_COMMIT) {
- if (cleanup_mode == CLEANUP_SCISSORS)
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
wt_status_add_cut_line(s->fp);
status_printf_ln(s, GIT_COLOR_NORMAL,
whence == FROM_MERGE
@@ -832,14 +818,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
fprintf(s->fp, "\n");
- if (cleanup_mode == CLEANUP_ALL)
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
status_printf(s, GIT_COLOR_NORMAL,
_("Please enter the commit message for your changes."
" Lines starting\nwith '%c' will be ignored, and an empty"
" message aborts the commit.\n"), comment_line_char);
- else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+ else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+ whence == FROM_COMMIT)
wt_status_add_cut_line(s->fp);
- else /* CLEANUP_SPACE, that is. */
+ else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
status_printf(s, GIT_COLOR_NORMAL,
_("Please enter the commit message for your changes."
" Lines starting\n"
@@ -912,11 +899,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
* submodules which were manually staged, which would
* be really confusing.
*/
- int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ struct diff_flags flags = DIFF_FLAGS_INIT;
+ flags.override_submodule_config = 1;
if (ignore_submodule_arg &&
!strcmp(ignore_submodule_arg, "all"))
- diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
- commitable = index_differs_from(parent, diff_flags, 1);
+ flags.ignore_submodules = 1;
+ commitable = index_differs_from(parent, &flags, 1);
}
}
strbuf_release(&committer_ident);
@@ -983,65 +971,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 1;
}
-static int rest_is_empty(struct strbuf *sb, int start)
-{
- int i, eol;
- const char *nl;
-
- /* Check if the rest is just whitespace and Signed-off-by's. */
- for (i = start; i < sb->len; i++) {
- nl = memchr(sb->buf + i, '\n', sb->len - i);
- if (nl)
- eol = nl - sb->buf;
- else
- eol = sb->len;
-
- if (strlen(sign_off_header) <= eol - i &&
- starts_with(sb->buf + i, sign_off_header)) {
- i = eol;
- continue;
- }
- while (i < eol)
- if (!isspace(sb->buf[i++]))
- return 0;
- }
-
- return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
- if (cleanup_mode == CLEANUP_NONE && sb->len)
- return 0;
- return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
- struct strbuf tmpl = STRBUF_INIT;
- const char *start;
-
- if (cleanup_mode == CLEANUP_NONE && sb->len)
- return 0;
-
- if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
- return 0;
-
- strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
- if (!skip_prefix(sb->buf, tmpl.buf, &start))
- start = sb->buf;
- strbuf_release(&tmpl);
- return rest_is_empty(sb, start - sb->buf);
-}
-
static const char *find_author_by_nickname(const char *name)
{
struct rev_info revs;
@@ -1075,6 +1004,19 @@ static const char *find_author_by_nickname(const char *name)
die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
}
+static void handle_ignored_arg(struct wt_status *s)
+{
+ if (!ignored_arg)
+ ; /* default already initialized */
+ else if (!strcmp(ignored_arg, "traditional"))
+ s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;
+ else if (!strcmp(ignored_arg, "no"))
+ s->show_ignored_mode = SHOW_NO_IGNORED;
+ else if (!strcmp(ignored_arg, "matching"))
+ s->show_ignored_mode = SHOW_MATCHING_IGNORED;
+ else
+ die(_("Invalid ignored mode '%s'"), ignored_arg);
+}
static void handle_untracked_files_arg(struct wt_status *s)
{
@@ -1137,6 +1079,9 @@ static void finalize_deferred_config(struct wt_status *s)
s->show_branch = status_deferred_config.show_branch;
if (s->show_branch < 0)
s->show_branch = 0;
+
+ if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
+ s->ahead_behind_flags = AHEAD_BEHIND_FULL;
}
static int parse_and_validate_options(int argc, const char *argv[],
@@ -1183,8 +1128,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
f++;
if (f > 1)
die(_("Only one of -c/-C/-F/--fixup can be used."));
- if (have_option_m && f > 0)
- die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
+ if (have_option_m && (edit_message || use_message || logfile))
+ die((_("Option -m cannot be combined with -c/-C/-F.")));
if (f || have_option_m)
template_file = NULL;
if (edit_message)
@@ -1213,15 +1158,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (argc == 0 && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));
if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
- cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+ cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
+ COMMIT_MSG_CLEANUP_SPACE;
else if (!strcmp(cleanup_arg, "verbatim"))
- cleanup_mode = CLEANUP_NONE;
+ cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
else if (!strcmp(cleanup_arg, "whitespace"))
- cleanup_mode = CLEANUP_SPACE;
+ cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
else if (!strcmp(cleanup_arg, "strip"))
- cleanup_mode = CLEANUP_ALL;
+ cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
else if (!strcmp(cleanup_arg, "scissors"))
- cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
+ cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+ COMMIT_MSG_CLEANUP_SPACE;
else
die(_("Invalid cleanup mode %s"), cleanup_arg);
@@ -1249,27 +1196,14 @@ static int dry_run_commit(int argc, const char **argv, const char *prefix,
return commitable ? 0 : 1;
}
+define_list_config_array_extra(color_status_slots, {"added"});
+
static int parse_status_slot(const char *slot)
{
- if (!strcasecmp(slot, "header"))
- return WT_STATUS_HEADER;
- if (!strcasecmp(slot, "branch"))
- return WT_STATUS_ONBRANCH;
- if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
+ if (!strcasecmp(slot, "added"))
return WT_STATUS_UPDATED;
- if (!strcasecmp(slot, "changed"))
- return WT_STATUS_CHANGED;
- if (!strcasecmp(slot, "untracked"))
- return WT_STATUS_UNTRACKED;
- if (!strcasecmp(slot, "nobranch"))
- return WT_STATUS_NOBRANCH;
- if (!strcasecmp(slot, "unmerged"))
- return WT_STATUS_UNMERGED;
- if (!strcasecmp(slot, "localBranch"))
- return WT_STATUS_LOCAL_BRANCH;
- if (!strcasecmp(slot, "remoteBranch"))
- return WT_STATUS_REMOTE_BRANCH;
- return -1;
+
+ return LOOKUP_CONFIG(color_status_slots, slot);
}
static int git_status_config(const char *k, const char *v, void *cb)
@@ -1335,11 +1269,31 @@ static int git_status_config(const char *k, const char *v, void *cb)
return error(_("Invalid untracked files mode '%s'"), v);
return 0;
}
+ if (!strcmp(k, "diff.renamelimit")) {
+ if (s->rename_limit == -1)
+ s->rename_limit = git_config_int(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "status.renamelimit")) {
+ s->rename_limit = git_config_int(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "diff.renames")) {
+ if (s->detect_rename == -1)
+ s->detect_rename = git_config_rename(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "status.renames")) {
+ s->detect_rename = git_config_rename(k, v);
+ return 0;
+ }
return git_diff_ui_config(k, v, NULL);
}
int cmd_status(int argc, const char **argv, const char *prefix)
{
+ static int no_renames = -1;
+ static const char *rename_score_arg = (const char *)-1;
static struct wt_status s;
int fd;
struct object_id oid;
@@ -1351,6 +1305,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
N_("show branch information")),
OPT_BOOL(0, "show-stash", &s.show_stash,
N_("show stash information")),
+ OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
+ N_("compute full ahead/behind values")),
{ OPTION_CALLBACK, 0, "porcelain", &status_format,
N_("version"), N_("machine-readable output"),
PARSE_OPT_OPTARG, opt_parse_porcelain },
@@ -1363,12 +1319,18 @@ int cmd_status(int argc, const char **argv, const char *prefix)
N_("mode"),
N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_BOOL(0, "ignored", &show_ignored_in_status,
- N_("show ignored files")),
+ { OPTION_STRING, 0, "ignored", &ignored_arg,
+ N_("mode"),
+ N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" },
{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
+ OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
+ { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg,
+ N_("n"), N_("detect renames, optionally set similarity index"),
+ PARSE_OPT_OPTARG, opt_parse_rename_score },
OPT_END(),
};
@@ -1383,8 +1345,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)
finalize_deferred_config(&s);
handle_untracked_files_arg(&s);
- if (show_ignored_in_status)
- s.show_ignored_files = 1;
+ handle_ignored_arg(&s);
+
+ if (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&
+ s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)
+ die(_("Unsupported combination of ignored and untracked-files arguments"));
+
parse_pathspec(&s.pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, argv);
@@ -1404,6 +1370,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
s.ignore_submodule_arg = ignore_submodule_arg;
s.status_format = status_format;
s.verbose = verbose;
+ if (no_renames != -1)
+ s.detect_rename = !no_renames;
+ if ((intptr_t)rename_score_arg != -1) {
+ if (s.detect_rename < DIFF_DETECT_RENAME)
+ s.detect_rename = DIFF_DETECT_RENAME;
+ if (rename_score_arg)
+ s.rename_score = parse_rename_score(&rename_score_arg);
+ }
wt_status_collect(&s);
@@ -1417,96 +1391,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
return 0;
}
-static const char *implicit_ident_advice(void)
-{
- char *user_config = expand_user_path("~/.gitconfig", 0);
- char *xdg_config = xdg_config_home("config");
- int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
- free(user_config);
- free(xdg_config);
-
- if (config_exists)
- return _(implicit_ident_advice_config);
- else
- return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
- int initial_commit)
-{
- struct rev_info rev;
- struct commit *commit;
- struct strbuf format = STRBUF_INIT;
- const char *head;
- struct pretty_print_context pctx = {0};
- struct strbuf author_ident = STRBUF_INIT;
- struct strbuf committer_ident = STRBUF_INIT;
-
- commit = lookup_commit(oid);
- if (!commit)
- die(_("couldn't look up newly created commit"));
- if (parse_commit(commit))
- die(_("could not parse newly created commit"));
-
- strbuf_addstr(&format, "format:%h] %s");
-
- format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
- format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
- if (strbuf_cmp(&author_ident, &committer_ident)) {
- strbuf_addstr(&format, "\n Author: ");
- strbuf_addbuf_percentquote(&format, &author_ident);
- }
- if (author_date_is_interesting()) {
- struct strbuf date = STRBUF_INIT;
- format_commit_message(commit, "%ad", &date, &pctx);
- strbuf_addstr(&format, "\n Date: ");
- strbuf_addbuf_percentquote(&format, &date);
- strbuf_release(&date);
- }
- if (!committer_ident_sufficiently_given()) {
- strbuf_addstr(&format, "\n Committer: ");
- strbuf_addbuf_percentquote(&format, &committer_ident);
- if (advice_implicit_identity) {
- strbuf_addch(&format, '\n');
- strbuf_addstr(&format, implicit_ident_advice());
- }
- }
- strbuf_release(&author_ident);
- strbuf_release(&committer_ident);
-
- init_revisions(&rev, prefix);
- setup_revisions(0, NULL, &rev, NULL);
-
- rev.diff = 1;
- rev.diffopt.output_format =
- DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
- rev.verbose_header = 1;
- rev.show_root_diff = 1;
- get_commit_format(format.buf, &rev);
- rev.always_show_header = 0;
- rev.diffopt.detect_rename = 1;
- rev.diffopt.break_opt = 0;
- diff_setup_done(&rev.diffopt);
-
- head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
- if (!strcmp(head, "HEAD"))
- head = _("detached HEAD");
- else
- skip_prefix(head, "refs/heads/", &head);
- printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
- if (!log_tree_commit(&rev, commit)) {
- rev.always_show_header = 1;
- rev.use_terminator = 1;
- log_tree_commit(&rev, commit);
- }
-
- strbuf_release(&format);
-}
-
static int git_commit_config(const char *k, const char *v, void *cb)
{
struct wt_status *s = cb;
@@ -1536,37 +1420,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
return git_status_config(k, v, s);
}
-static int run_rewrite_hook(const struct object_id *oldoid,
- const struct object_id *newoid)
-{
- struct child_process proc = CHILD_PROCESS_INIT;
- const char *argv[3];
- int code;
- struct strbuf sb = STRBUF_INIT;
-
- argv[0] = find_hook("post-rewrite");
- if (!argv[0])
- return 0;
-
- argv[1] = "amend";
- argv[2] = NULL;
-
- proc.argv = argv;
- proc.in = -1;
- proc.stdout_to_stderr = 1;
-
- code = start_command(&proc);
- if (code)
- return code;
- strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
- sigchain_push(SIGPIPE, SIG_IGN);
- write_in_full(proc.in, sb.buf, sb.len);
- close(proc.in);
- strbuf_release(&sb);
- sigchain_pop(SIGPIPE);
- return finish_command(&proc);
-}
-
int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
{
struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1591,6 +1444,7 @@ int run_commit_hook(int editor_is_used, const char *index_file, const char *name
int cmd_commit(int argc, const char **argv, const char *prefix)
{
+ const char *argv_gc_auto[] = {"gc", "--auto", NULL};
static struct wt_status s;
static struct option builtin_commit_options[] = {
OPT__QUIET(&quiet, N_("suppress summary after successful commit")),
@@ -1626,6 +1480,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
STATUS_FORMAT_SHORT),
OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
+ OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
+ N_("compute full ahead/behind values")),
OPT_SET_INT(0, "porcelain", &status_format,
N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
OPT_SET_INT(0, "long", &status_format,
@@ -1649,13 +1505,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct strbuf sb = STRBUF_INIT;
struct strbuf author_ident = STRBUF_INIT;
const char *index_file, *reflog_msg;
- char *nl;
struct object_id oid;
struct commit_list *parents = NULL;
struct stat statbuf;
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
- struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1728,7 +1582,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
allow_fast_forward = 0;
}
if (allow_fast_forward)
- parents = reduce_heads(parents);
+ reduce_heads_replace(&parents);
} else {
if (!reflog_msg)
reflog_msg = (whence == FROM_CHERRY_PICK)
@@ -1746,17 +1600,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
if (verbose || /* Truncate the message just before the diff, if any. */
- cleanup_mode == CLEANUP_SCISSORS)
+ cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
- if (cleanup_mode != CLEANUP_NONE)
- strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+ if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+ strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
- if (message_is_empty(&sb) && !allow_empty_message) {
+ if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
exit(1);
}
- if (template_untouched(&sb) && !allow_empty_message) {
+ if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
exit(1);
@@ -1770,33 +1624,20 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
- if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->oid.hash,
- parents, oid.hash, author_ident.buf, sign_commit, extra)) {
+ if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid,
+ parents, &oid, author_ident.buf, sign_commit,
+ extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
strbuf_release(&author_ident);
free_commit_extra_headers(extra);
- nl = strchr(sb.buf, '\n');
- if (nl)
- strbuf_setlen(&sb, nl + 1 - sb.buf);
- else
- strbuf_addch(&sb, '\n');
- strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
- strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
- transaction = ref_transaction_begin(&err);
- if (!transaction ||
- ref_transaction_update(transaction, "HEAD", oid.hash,
- current_head
- ? current_head->object.oid.hash : null_sha1,
- 0, sb.buf, &err) ||
- ref_transaction_commit(transaction, &err)) {
+ if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+ &err)) {
rollback_index_files();
die("%s", err.buf);
}
- ref_transaction_free(transaction);
unlink(git_path_cherry_pick_head());
unlink(git_path_revert_head());
@@ -1811,19 +1652,20 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"not exceeded, and then \"git reset HEAD\" to recover."));
rerere(0);
+ run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
- struct notes_rewrite_cfg *cfg;
- cfg = init_copy_notes_for_rewrite("amend");
- if (cfg) {
- /* we are amending, so current_head is not NULL */
- copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
- finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
- }
- run_rewrite_hook(&current_head->object.oid, &oid);
+ commit_post_rewrite(current_head, &oid);
+ }
+ if (!quiet) {
+ unsigned int flags = 0;
+
+ if (!current_head)
+ flags |= SUMMARY_INITIAL_COMMIT;
+ if (author_date_is_interesting())
+ flags |= SUMMARY_SHOW_AUTHOR_DATE;
+ print_commit_summary(prefix, &oid, flags);
}
- if (!quiet)
- print_summary(prefix, &oid, !current_head);
UNLEAK(err);
UNLEAK(sb);
diff --git a/builtin/config.c b/builtin/config.c
index d13daeeb55..b29d26dede 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -25,7 +25,8 @@ static char term = '\n';
static int use_global_config, use_system_config, use_local_config;
static struct git_config_source given_config_source;
-static int actions, types;
+static int actions, type;
+static char *default_value;
static int end_null;
static int respect_includes_opt = -1;
static struct config_options config_options;
@@ -48,10 +49,75 @@ static int show_origin;
#define ACTION_GET_COLORBOOL (1<<14)
#define ACTION_GET_URLMATCH (1<<15)
-#define TYPE_BOOL (1<<0)
-#define TYPE_INT (1<<1)
-#define TYPE_BOOL_OR_INT (1<<2)
-#define TYPE_PATH (1<<3)
+/*
+ * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
+ * one line of output and which should therefore be paged.
+ */
+#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
+ ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
+
+#define TYPE_BOOL 1
+#define TYPE_INT 2
+#define TYPE_BOOL_OR_INT 3
+#define TYPE_PATH 4
+#define TYPE_EXPIRY_DATE 5
+#define TYPE_COLOR 6
+
+#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
+ { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
+ PARSE_OPT_NONEG, option_parse_type, (i) }
+
+static struct option builtin_config_options[];
+
+static int option_parse_type(const struct option *opt, const char *arg,
+ int unset)
+{
+ int new_type, *to_type;
+
+ if (unset) {
+ *((int *) opt->value) = 0;
+ return 0;
+ }
+
+ /*
+ * To support '--<type>' style flags, begin with new_type equal to
+ * opt->defval.
+ */
+ new_type = opt->defval;
+ if (!new_type) {
+ if (!strcmp(arg, "bool"))
+ new_type = TYPE_BOOL;
+ else if (!strcmp(arg, "int"))
+ new_type = TYPE_INT;
+ else if (!strcmp(arg, "bool-or-int"))
+ new_type = TYPE_BOOL_OR_INT;
+ else if (!strcmp(arg, "path"))
+ new_type = TYPE_PATH;
+ else if (!strcmp(arg, "expiry-date"))
+ new_type = TYPE_EXPIRY_DATE;
+ else if (!strcmp(arg, "color"))
+ new_type = TYPE_COLOR;
+ else
+ die(_("unrecognized --type argument, %s"), arg);
+ }
+
+ to_type = opt->value;
+ if (*to_type && *to_type != new_type) {
+ /*
+ * Complain when there is a new type not equal to the old type.
+ * This allows for combinations like '--int --type=int' and
+ * '--type=int --type=int', but disallows ones like '--type=bool
+ * --int' and '--type=bool
+ * --type=int'.
+ */
+ error("only one type at a time.");
+ usage_with_options(builtin_config_usage,
+ builtin_config_options);
+ }
+ *to_type = new_type;
+
+ return 0;
+}
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
@@ -76,15 +142,18 @@ static struct option builtin_config_options[] = {
OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
OPT_GROUP(N_("Type")),
- OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL),
- OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
- OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
- OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
+ OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type),
+ OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
+ OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
+ OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
+ 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(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_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_END(),
};
@@ -140,25 +209,35 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
if (show_keys)
strbuf_addch(buf, key_delim);
- if (types == TYPE_INT)
+ if (type == TYPE_INT)
strbuf_addf(buf, "%"PRId64,
git_config_int64(key_, value_ ? value_ : ""));
- else if (types == TYPE_BOOL)
+ else if (type == TYPE_BOOL)
strbuf_addstr(buf, git_config_bool(key_, value_) ?
"true" : "false");
- else if (types == TYPE_BOOL_OR_INT) {
+ else if (type == TYPE_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key_, value_, &is_bool);
if (is_bool)
strbuf_addstr(buf, v ? "true" : "false");
else
strbuf_addf(buf, "%d", v);
- } else if (types == TYPE_PATH) {
+ } else if (type == TYPE_PATH) {
const char *v;
if (git_config_pathname(&v, key_, value_) < 0)
return -1;
strbuf_addstr(buf, v);
free((char *)v);
+ } else if (type == TYPE_EXPIRY_DATE) {
+ timestamp_t t;
+ if (git_config_expiry_date(&t, key_, value_) < 0)
+ return -1;
+ strbuf_addf(buf, "%"PRItime, t);
+ } else if (type == TYPE_COLOR) {
+ char v[COLOR_MAXLEN];
+ if (git_config_color(v, key_, value_) < 0)
+ return -1;
+ strbuf_addstr(buf, v);
} else if (value_) {
strbuf_addstr(buf, value_);
} else {
@@ -244,6 +323,16 @@ static int get_value(const char *key_, const char *regex_)
config_with_options(collect_config, &values,
&given_config_source, &config_options);
+ if (!values.nr && default_value) {
+ struct strbuf *item;
+ ALLOC_GROW(values.items, values.nr + 1, values.alloc);
+ item = &values.items[values.nr++];
+ strbuf_init(item, 0);
+ if (format_config(item, key_, default_value) < 0)
+ die(_("failed to format default config value: %s"),
+ default_value);
+ }
+
ret = !values.nr;
for (i = 0; i < values.nr; i++) {
@@ -273,19 +362,20 @@ static char *normalize_value(const char *key, const char *value)
if (!value)
return NULL;
- if (types == 0 || types == TYPE_PATH)
+ if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)
/*
* We don't do normalization for TYPE_PATH here: If
* the path is like ~/foobar/, we prefer to store
* "~/foobar/" in the config file, and to expand the ~
* when retrieving the value.
+ * Also don't do normalization for expiry dates.
*/
return xstrdup(value);
- if (types == TYPE_INT)
+ if (type == TYPE_INT)
return xstrfmt("%"PRId64, git_config_int64(key, value));
- if (types == TYPE_BOOL)
+ if (type == TYPE_BOOL)
return xstrdup(git_config_bool(key, value) ? "true" : "false");
- if (types == TYPE_BOOL_OR_INT) {
+ if (type == TYPE_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key, value, &is_bool);
if (!is_bool)
@@ -293,8 +383,22 @@ static char *normalize_value(const char *key, const char *value)
else
return xstrdup(v ? "true" : "false");
}
+ if (type == TYPE_COLOR) {
+ char v[COLOR_MAXLEN];
+ if (git_config_color(v, key, value))
+ die("cannot parse color '%s'", value);
+
+ /*
+ * The contents of `v` now contain an ANSI escape
+ * sequence, not suitable for including within a
+ * configuration file. Treat the above as a
+ * "sanity-check", and return the given value, which we
+ * know is representable as valid color code.
+ */
+ return xstrdup(value);
+ }
- die("BUG: cannot normalize type %d", types);
+ BUG("cannot normalize type %d", type);
}
static int get_color_found;
@@ -498,6 +602,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (use_local_config && nongit)
die(_("--local can only be used inside a git repository"));
+ if (given_config_source.blob && nongit)
+ die(_("--blob can only be used inside a git repository"));
+
if (given_config_source.file &&
!strcmp(given_config_source.file, "-")) {
given_config_source.file = NULL;
@@ -551,12 +658,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
key_delim = '\n';
}
- if (HAS_MULTI_BITS(types)) {
- error("only one type at a time.");
- usage_with_options(builtin_config_usage, builtin_config_options);
- }
-
- if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {
+ if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
error("--get-color and variable type are incoherent");
usage_with_options(builtin_config_usage, builtin_config_options);
}
@@ -586,6 +688,15 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_config_usage, builtin_config_options);
}
+ if (default_value && !(actions & ACTION_GET)) {
+ error("--default is only applicable to --get");
+ usage_with_options(builtin_config_usage,
+ builtin_config_options);
+ }
+
+ if (actions & PAGING_ACTIONS)
+ setup_auto_pager("config", 1);
+
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
if (config_with_options(show_all_config, NULL,
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 33343818c8..d51e2ce1ec 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -7,10 +7,12 @@
#include "cache.h"
#include "config.h"
#include "dir.h"
+#include "repository.h"
#include "builtin.h"
#include "parse-options.h"
#include "quote.h"
#include "packfile.h"
+#include "object-store.h"
static unsigned long garbage;
static off_t size_garbage;
@@ -64,7 +66,7 @@ static int count_loose(const struct object_id *oid, const char *path, void *data
else {
loose_size += on_disk_bytes(st);
loose++;
- if (verbose && has_sha1_pack(oid->hash))
+ if (verbose && has_object_pack(oid))
packed_loose++;
}
return 0;
@@ -120,9 +122,8 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
struct strbuf loose_buf = STRBUF_INIT;
struct strbuf pack_buf = STRBUF_INIT;
struct strbuf garbage_buf = STRBUF_INIT;
- if (!packed_git)
- prepare_packed_git();
- for (p = packed_git; p; p = p->next) {
+
+ for (p = get_packed_git(the_repository); p; p = p->next) {
if (!p->pack_local)
continue;
if (open_pack_index(p))
diff --git a/builtin/describe.c b/builtin/describe.c
index 29075dbd0f..bec2513b66 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -3,18 +3,24 @@
#include "lockfile.h"
#include "commit.h"
#include "tag.h"
+#include "blob.h"
#include "refs.h"
#include "builtin.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "parse-options.h"
+#include "revision.h"
#include "diff.h"
#include "hashmap.h"
#include "argv-array.h"
#include "run-command.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "commit-slab.h"
-#define SEEN (1u << 0)
#define MAX_TAGS (FLAG_BITS - 1)
+define_commit_slab(commit_names, struct commit_name *);
+
static const char * const describe_usage[] = {
N_("git describe [<options>] [<commit-ish>...]"),
N_("git describe [<options>] --dirty"),
@@ -34,6 +40,7 @@ static struct string_list patterns = STRING_LIST_INIT_NODUP;
static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
static int always;
static const char *suffix, *dirty, *broken;
+static struct commit_names commit_names;
/* diff-index command arguments to check if working tree is dirty. */
static const char *diff_index_args[] = {
@@ -181,7 +188,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
}
/* Is it annotated? */
- if (!peel_ref(path, peeled.hash)) {
+ if (!peel_ref(path, &peeled)) {
is_annotated = !!oidcmp(oid, &peeled);
} else {
oidcpy(&peeled, oid);
@@ -256,7 +263,7 @@ static unsigned long finish_depth_computation(
return seen_commits;
}
-static void display_name(struct commit_name *n)
+static void append_name(struct commit_name *n, struct strbuf *dst)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(&n->oid);
@@ -271,20 +278,22 @@ static void display_name(struct commit_name *n)
n->name_checked = 1;
}
- if (n->tag)
- printf("%s", n->tag->tag);
- else
- printf("%s", n->path);
+ if (n->tag) {
+ if (all)
+ strbuf_addstr(dst, "tags/");
+ strbuf_addstr(dst, n->tag->tag);
+ } else {
+ strbuf_addstr(dst, n->path);
+ }
}
-static void show_suffix(int depth, const struct object_id *oid)
+static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
{
- printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
+ strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev));
}
-static void describe(const char *arg, int last_one)
+static void describe_commit(struct object_id *oid, struct strbuf *dst)
{
- struct object_id oid;
struct commit *cmit, *gave_up_on = NULL;
struct commit_list *list;
struct commit_name *n;
@@ -293,39 +302,37 @@ static void describe(const char *arg, int last_one)
unsigned long seen_commits = 0;
unsigned int unannotated_cnt = 0;
- if (get_oid(arg, &oid))
- die(_("Not a valid object name %s"), arg);
- cmit = lookup_commit_reference(&oid);
- if (!cmit)
- die(_("%s is not a valid '%s' object"), arg, commit_type);
+ cmit = lookup_commit_reference(oid);
n = find_commit_name(&cmit->object.oid);
if (n && (tags || all || n->prio == 2)) {
/*
* Exact match to an existing ref.
*/
- display_name(n);
+ append_name(n, dst);
if (longformat)
- show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
+ append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
if (suffix)
- printf("%s", suffix);
- printf("\n");
+ strbuf_addstr(dst, suffix);
return;
}
if (!max_candidates)
die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
if (debug)
- fprintf(stderr, _("searching to describe %s\n"), arg);
+ fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
if (!have_util) {
struct hashmap_iter iter;
struct commit *c;
- struct commit_name *n = hashmap_iter_first(&names, &iter);
+ struct commit_name *n;
+
+ init_commit_names(&commit_names);
+ n = hashmap_iter_first(&names, &iter);
for (; n; n = hashmap_iter_next(&iter)) {
c = lookup_commit_reference_gently(&n->peeled, 1);
if (c)
- c->util = n;
+ *commit_names_at(&commit_names, c) = n;
}
have_util = 1;
}
@@ -336,8 +343,11 @@ static void describe(const char *arg, int last_one)
while (list) {
struct commit *c = pop_commit(&list);
struct commit_list *parents = c->parents;
+ struct commit_name **slot;
+
seen_commits++;
- n = c->util;
+ slot = commit_names_peek(&commit_names, c);
+ n = slot ? *slot : NULL;
if (n) {
if (!tags && !all && n->prio < 2) {
unannotated_cnt++;
@@ -381,22 +391,21 @@ static void describe(const char *arg, int last_one)
}
if (!match_cnt) {
- struct object_id *oid = &cmit->object.oid;
+ struct object_id *cmit_oid = &cmit->object.oid;
if (always) {
- printf("%s", find_unique_abbrev(oid->hash, abbrev));
+ strbuf_add_unique_abbrev(dst, cmit_oid, abbrev);
if (suffix)
- printf("%s", suffix);
- printf("\n");
+ strbuf_addstr(dst, suffix);
return;
}
if (unannotated_cnt)
die(_("No annotated tags can describe '%s'.\n"
"However, there were unannotated tags: try --tags."),
- oid_to_hex(oid));
+ oid_to_hex(cmit_oid));
else
die(_("No tags can describe '%s'.\n"
"Try --always, or create some tags."),
- oid_to_hex(oid));
+ oid_to_hex(cmit_oid));
}
QSORT(all_matches, match_cnt, compare_pt);
@@ -434,15 +443,86 @@ static void describe(const char *arg, int last_one)
}
}
- display_name(all_matches[0].name);
+ append_name(all_matches[0].name, dst);
if (abbrev)
- show_suffix(all_matches[0].depth, &cmit->object.oid);
+ append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
if (suffix)
- printf("%s", suffix);
- printf("\n");
+ strbuf_addstr(dst, suffix);
+}
+
+struct process_commit_data {
+ struct object_id current_commit;
+ struct object_id looking_for;
+ struct strbuf *dst;
+ struct rev_info *revs;
+};
+
+static void process_commit(struct commit *commit, void *data)
+{
+ struct process_commit_data *pcd = data;
+ pcd->current_commit = commit->object.oid;
+}
+
+static void process_object(struct object *obj, const char *path, void *data)
+{
+ struct process_commit_data *pcd = data;
+
+ if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
+ reset_revision_walk();
+ describe_commit(&pcd->current_commit, pcd->dst);
+ strbuf_addf(pcd->dst, ":%s", path);
+ free_commit_list(pcd->revs->commits);
+ pcd->revs->commits = NULL;
+ }
+}
+
+static void describe_blob(struct object_id oid, struct strbuf *dst)
+{
+ struct rev_info revs;
+ struct argv_array args = ARGV_ARRAY_INIT;
+ struct process_commit_data pcd = { null_oid, oid, dst, &revs};
+
+ argv_array_pushl(&args, "internal: The first arg is not parsed",
+ "--objects", "--in-commit-order", "--reverse", "HEAD",
+ NULL);
+
+ init_revisions(&revs, NULL);
+ if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
+ BUG("setup_revisions could not handle all args?");
+
+ if (prepare_revision_walk(&revs))
+ die("revision walk setup failed");
+
+ traverse_commit_list(&revs, process_commit, process_object, &pcd);
+ reset_revision_walk();
+}
+
+static void describe(const char *arg, int last_one)
+{
+ struct object_id oid;
+ struct commit *cmit;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (debug)
+ fprintf(stderr, _("describe %s\n"), arg);
+
+ if (get_oid(arg, &oid))
+ die(_("Not a valid object name %s"), arg);
+ cmit = lookup_commit_reference_gently(&oid, 1);
+
+ if (cmit)
+ describe_commit(&oid, &sb);
+ else if (oid_object_info(the_repository, &oid, NULL) == OBJ_BLOB)
+ describe_blob(oid, &sb);
+ else
+ die(_("%s is neither a commit nor blob"), arg);
+
+ puts(sb.buf);
if (!last_one)
clear_commit_marks(cmit, -1);
+
+ strbuf_release(&sb);
}
int cmd_describe(int argc, const char **argv, const char *prefix)
@@ -542,8 +622,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
suffix = broken;
}
} else if (dirty) {
- static struct lock_file index_lock;
- int fd;
+ struct lock_file index_lock = LOCK_INIT;
+ struct rev_info revs;
+ struct argv_array args = ARGV_ARRAY_INIT;
+ int fd, result;
read_cache_preload(NULL);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
@@ -552,8 +634,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (0 <= fd)
update_index_if_able(&the_index, &index_lock);
- if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
- diff_index_args, prefix))
+ init_revisions(&revs, prefix);
+ argv_array_pushv(&args, diff_index_args);
+ if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
+ BUG("malformed internal diff-index command line");
+ result = run_diff_index(&revs, 0);
+
+ if (!diff_result_code(&revs.diffopt, result))
suffix = NULL;
else
suffix = dirty;
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index d66499909e..473615117e 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -76,7 +76,7 @@ static int diff_tree_stdin(char *line)
if (obj->type == OBJ_TREE)
return stdin_diff_trees((struct tree *)obj, p);
error("Object %s is a %s, not a commit or tree",
- oid_to_hex(&oid), typename(obj->type));
+ oid_to_hex(&oid), type_name(obj->type));
return -1;
}
@@ -110,6 +110,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(opt, prefix);
+ if (read_cache() < 0)
+ die(_("index file corrupt"));
opt->abbrev = 0;
opt->diff = 1;
opt->disable_stdin = 1;
diff --git a/builtin/diff.c b/builtin/diff.c
index f5bbd4d757..b709b6e984 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -44,7 +44,7 @@ static void stuff_change(struct diff_options *opt,
!oidcmp(old_oid, new_oid) && (old_mode == new_mode))
return;
- if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
+ if (opt->flags.reverse_diff) {
SWAP(old_mode, new_mode);
SWAP(old_oid, new_oid);
SWAP(old_path, new_path);
@@ -203,17 +203,16 @@ static int builtin_diff_combined(struct rev_info *revs,
static void refresh_index_quietly(void)
{
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
int fd;
- lock_file = xcalloc(1, sizeof(struct lock_file));
- fd = hold_locked_index(lock_file, 0);
+ fd = hold_locked_index(&lock_file, 0);
if (fd < 0)
return;
discard_cache();
read_cache();
refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
- update_index_if_able(&the_index, lock_file);
+ update_index_if_able(&the_index, &lock_file);
}
static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
@@ -350,8 +349,15 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
rev.diffopt.stat_graph_width = -1;
/* Default to let external and textconv be used */
- DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
- DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+ rev.diffopt.flags.allow_external = 1;
+ rev.diffopt.flags.allow_textconv = 1;
+
+ /*
+ * Default to intent-to-add entries invisible in the
+ * index. This makes them show up as new files in diff-files
+ * and not at all in diff-cached.
+ */
+ rev.diffopt.ita_invisible_in_index = 1;
if (nongit)
die(_("Not a git repository"));
@@ -361,7 +367,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
diff_setup_done(&rev.diffopt);
}
- DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+ rev.diffopt.flags.recursive = 1;
setup_diff_pager(&rev.diffopt);
@@ -380,7 +386,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
add_head_to_pending(&rev);
if (!rev.pending.nr) {
struct tree *tree;
- tree = lookup_tree(&empty_tree_oid);
+ tree = lookup_tree(the_hash_algo->empty_tree);
add_pending_object(&rev, &tree->object, "HEAD");
}
break;
@@ -399,7 +405,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!obj)
die(_("invalid object '%s' given."), name);
if (obj->type == OBJ_COMMIT)
- obj = &((struct commit *)obj)->tree->object;
+ obj = &get_commit_tree(((struct commit *)obj))->object;
if (obj->type == OBJ_TREE) {
obj->flags |= flags;
diff --git a/builtin/difftool.c b/builtin/difftool.c
index b2d3ba7539..bc97d4aef2 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -15,7 +15,7 @@
#include "config.h"
#include "builtin.h"
#include "run-command.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "parse-options.h"
#include "argv-array.h"
#include "strbuf.h"
@@ -306,7 +306,7 @@ static char *get_symlink(const struct object_id *oid, const char *path)
} else {
enum object_type type;
unsigned long size;
- data = read_sha1_file(oid->hash, &type, &size);
+ data = read_object_file(oid, &type, &size);
if (!data)
die(_("could not read object %s for symlink %s"),
oid_to_hex(oid), path);
@@ -610,13 +610,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
continue;
if (!indices_loaded) {
- static struct lock_file lock;
+ struct lock_file lock = LOCK_INIT;
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/wtindex", tmpdir);
if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
ret = error("could not write %s", buf.buf);
- rollback_lock_file(&lock);
goto finish;
}
changed_files(&wt_modified, buf.buf, workdir);
@@ -696,12 +695,11 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
N_("use `diff.guitool` instead of `diff.tool`")),
OPT_BOOL('d', "dir-diff", &dir_diff,
N_("perform a full-directory diff")),
- { OPTION_SET_INT, 'y', "no-prompt", &prompt, NULL,
+ OPT_SET_INT_F('y', "no-prompt", &prompt,
N_("do not prompt before launching a diff tool"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
- { OPTION_SET_INT, 0, "prompt", &prompt, NULL, NULL,
- PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
- NULL, 1 },
+ 0, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "prompt", &prompt, NULL,
+ 1, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN),
OPT_BOOL(0, "symlinks", &symlinks,
N_("use symlinks in dir-diff mode")),
OPT_STRING('t', "tool", &difftool_cmd, N_("<tool>"),
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 2fb60d6d48..73d12c1020 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -7,6 +7,7 @@
#include "cache.h"
#include "config.h"
#include "refs.h"
+#include "refspec.h"
#include "commit.h"
#include "object.h"
#include "tag.h"
@@ -21,6 +22,7 @@
#include "quote.h"
#include "remote.h"
#include "blob.h"
+#include "commit-slab.h"
static const char *fast_export_usage[] = {
N_("git fast-export [rev-list-opts]"),
@@ -35,9 +37,9 @@ static int use_done_feature;
static int no_data;
static int full_tree;
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
-static struct refspec *refspecs;
-static int refspecs_nr;
+static struct refspec refspecs = REFSPEC_INIT_FETCH;
static int anonymize;
+static struct revision_sources revision_sources;
static int parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset)
@@ -156,15 +158,14 @@ static void anonymize_path(struct strbuf *out, const char *path,
}
}
-/* Since intptr_t is C99, we do not use it here */
-static inline uint32_t *mark_to_ptr(uint32_t mark)
+static inline void *mark_to_ptr(uint32_t mark)
{
- return ((uint32_t *)NULL) + mark;
+ return (void *)(uintptr_t)mark;
}
static inline uint32_t ptr_to_mark(void * mark)
{
- return (uint32_t *)mark - (uint32_t *)NULL;
+ return (uint32_t)(uintptr_t)mark;
}
static inline void mark_object(struct object *object, uint32_t mark)
@@ -237,10 +238,10 @@ static void export_blob(const struct object_id *oid)
object = (struct object *)lookup_blob(oid);
eaten = 0;
} else {
- buf = read_sha1_file(oid->hash, &type, &size);
+ buf = read_object_file(oid, &type, &size);
if (!buf)
die ("Could not read blob %s", oid_to_hex(oid));
- if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0)
+ if (check_object_signature(oid, buf, size, type_name(type)) < 0)
die("sha1 mismatch in blob %s", oid_to_hex(oid));
object = parse_object_buffer(oid, type, size, buf, &eaten);
}
@@ -517,7 +518,7 @@ static void anonymize_ident_line(const char **beg, const char **end)
/* skip "committer", "author", "tagger", etc */
end_of_header = strchr(*beg, ' ');
if (!end_of_header)
- die("BUG: malformed line fed to anonymize_ident_line: %.*s",
+ BUG("malformed line fed to anonymize_ident_line: %.*s",
(int)(*end - *beg), *beg);
end_of_header++;
strbuf_add(out, *beg, end_of_header - *beg);
@@ -578,11 +579,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
get_object_mark(&commit->parents->item->object) != 0 &&
!full_tree) {
parse_commit_or_die(commit->parents->item);
- diff_tree_oid(&commit->parents->item->tree->object.oid,
- &commit->tree->object.oid, "", &rev->diffopt);
+ diff_tree_oid(get_commit_tree_oid(commit->parents->item),
+ get_commit_tree_oid(commit), "", &rev->diffopt);
}
else
- diff_root_tree_oid(&commit->tree->object.oid,
+ diff_root_tree_oid(get_commit_tree_oid(commit),
"", &rev->diffopt);
/* Export the referenced blobs, and remember the marks. */
@@ -590,7 +591,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
export_blob(&diff_queued_diff.queue[i]->two->oid);
- refname = commit->util;
+ refname = *revision_sources_at(&revision_sources, commit);
if (anonymize) {
refname = anonymize_refname(refname);
anonymize_ident_line(&committer, &committer_end);
@@ -651,8 +652,11 @@ static void handle_tail(struct object_array *commits, struct rev_info *revs,
struct commit *commit;
while (commits->nr) {
commit = (struct commit *)object_array_pop(commits);
- if (has_unshown_parent(commit))
+ if (has_unshown_parent(commit)) {
+ /* Queue again, to be handled later */
+ add_object_array(&commit->object, NULL, commits);
return;
+ }
handle_commit(commit, revs, paths_of_changed_objects);
}
}
@@ -682,7 +686,7 @@ static void handle_tag(const char *name, struct tag *tag)
return;
}
- buf = read_sha1_file(tag->object.oid.hash, &type, &size);
+ buf = read_object_file(&tag->object.oid, &type, &size);
if (!buf)
die ("Could not read tag %s", oid_to_hex(&tag->object.oid));
message = memmem(buf, size, "\n\n", 2);
@@ -757,7 +761,7 @@ static void handle_tag(const char *name, struct tag *tag)
if (tagged->type != OBJ_COMMIT) {
die ("Tag %s tags unexported %s!",
oid_to_hex(&tag->object.oid),
- typename(tagged->type));
+ type_name(tagged->type));
}
p = (struct commit *)tagged;
for (;;) {
@@ -823,12 +827,12 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
if (e->flags & UNINTERESTING)
continue;
- if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1)
+ if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
continue;
- if (refspecs) {
+ if (refspecs.nr) {
char *private;
- private = apply_refspecs(refspecs, refspecs_nr, full_name);
+ private = apply_refspecs(&refspecs, full_name);
if (private) {
free(full_name);
full_name = private;
@@ -839,7 +843,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
if (!commit) {
warning("%s: Unexpected object of type %s, skipping.",
e->name,
- typename(e->item->type));
+ type_name(e->item->type));
continue;
}
@@ -851,7 +855,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
continue;
default: /* OBJ_TAG (nested tags) is already handled */
warning("Tag points to object of unexpected type %s, skipping.",
- typename(commit->object.type));
+ type_name(commit->object.type));
continue;
}
@@ -859,10 +863,11 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
* This ref will not be updated through a commit, lets make
* sure it gets properly updated eventually.
*/
- if (commit->util || commit->object.flags & SHOWN)
+ if (*revision_sources_at(&revision_sources, commit) ||
+ commit->object.flags & SHOWN)
string_list_append(&extra_refs, full_name)->util = commit;
- if (!commit->util)
- commit->util = full_name;
+ if (!*revision_sources_at(&revision_sources, commit))
+ *revision_sources_at(&revision_sources, commit) = full_name;
}
}
@@ -895,7 +900,7 @@ static void export_marks(char *file)
{
unsigned int i;
uint32_t mark;
- struct object_decoration *deco = idnums.hash;
+ struct decoration_entry *deco = idnums.entries;
FILE *f;
int e = 0;
@@ -947,7 +952,7 @@ static void import_marks(char *input_file)
if (last_idnum < mark)
last_idnum = mark;
- type = sha1_object_info(oid.hash, NULL);
+ type = oid_object_info(the_repository, &oid, NULL);
if (type < 0)
die("object not found: %s", oid_to_hex(&oid));
@@ -974,8 +979,8 @@ static void import_marks(char *input_file)
static void handle_deletes(void)
{
int i;
- for (i = 0; i < refspecs_nr; i++) {
- struct refspec *refspec = &refspecs[i];
+ for (i = 0; i < refspecs.nr; i++) {
+ struct refspec_item *refspec = &refspecs.items[i];
if (*refspec->src)
continue;
@@ -1026,8 +1031,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
+ init_revision_sources(&revision_sources);
revs.topo_order = 1;
- revs.show_source = 1;
+ revs.sources = &revision_sources;
revs.rewrite_parents = 1;
argc = parse_options(argc, argv, prefix, options, fast_export_usage,
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
@@ -1036,18 +1042,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
usage_with_options (fast_export_usage, options);
if (refspecs_list.nr) {
- const char **refspecs_str;
int i;
- ALLOC_ARRAY(refspecs_str, refspecs_list.nr);
for (i = 0; i < refspecs_list.nr; i++)
- refspecs_str[i] = refspecs_list.items[i].string;
-
- refspecs_nr = refspecs_list.nr;
- refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);
+ refspec_append(&refspecs, refspecs_list.items[i].string);
string_list_clear(&refspecs_list, 1);
- free(refspecs_str);
}
if (use_done_feature)
@@ -1066,7 +1066,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
revs.diffopt.format_callback_data = &paths_of_changed_objects;
- DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+ revs.diffopt.flags.recursive = 1;
while ((commit = get_revision(&revs))) {
if (has_unshown_parent(commit)) {
add_object_array(&commit->object, NULL, &commits);
@@ -1086,7 +1086,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (use_done_feature)
printf("done\n");
- free_refspec(refspecs_nr, refspecs);
+ refspec_clear(&refspecs);
return 0;
}
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index a7bc1366ab..1a1bc63566 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -4,6 +4,7 @@
#include "remote.h"
#include "connect.h"
#include "sha1-array.h"
+#include "protocol.h"
static const char fetch_pack_usage[] =
"git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] "
@@ -52,6 +53,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
struct fetch_pack_args args;
struct oid_array shallow = OID_ARRAY_INIT;
struct string_list deepen_not = STRING_LIST_INIT_DUP;
+ struct packet_reader reader;
fetch_if_missing = 0;
@@ -211,10 +213,24 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
if (!conn)
return args.diag_url ? 0 : 1;
}
- get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow);
+
+ packet_reader_init(&reader, fd[0], NULL, 0,
+ PACKET_READ_CHOMP_NEWLINE |
+ PACKET_READ_GENTLE_ON_EOF);
+
+ switch (discover_version(&reader)) {
+ case protocol_v2:
+ die("support for protocol v2 not implemented yet");
+ case protocol_v1:
+ case protocol_v0:
+ get_remote_heads(&reader, &ref, 0, NULL, &shallow);
+ break;
+ case protocol_unknown_version:
+ BUG("unknown protocol version");
+ }
ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
- &shallow, pack_lockfile_ptr);
+ &shallow, pack_lockfile_ptr, protocol_v0);
if (pack_lockfile) {
printf("lock %s\n", pack_lockfile);
fflush(stdout);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 79c866c225..ea5b9669ad 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -3,7 +3,9 @@
*/
#include "cache.h"
#include "config.h"
+#include "repository.h"
#include "refs.h"
+#include "refspec.h"
#include "commit.h"
#include "builtin.h"
#include "string-list.h"
@@ -38,6 +40,10 @@ static int fetch_prune_config = -1; /* unspecified */
static int prune = -1; /* unspecified */
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
+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 progress = -1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
@@ -54,9 +60,9 @@ static const char *submodule_prefix = "";
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
static int shown_url = 0;
-static int refmap_alloc, refmap_nr;
-static const char **refmap_array;
+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 int git_fetch_config(const char *k, const char *v, void *cb)
{
@@ -65,6 +71,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
return 0;
}
+ if (!strcmp(k, "fetch.prunetags")) {
+ fetch_prune_tags_config = git_config_bool(k, v);
+ return 0;
+ }
+
if (!strcmp(k, "submodule.recurse")) {
int r = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
@@ -97,14 +108,12 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
{
- ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
-
/*
* "git fetch --refmap='' origin foo"
* can be used to tell the command not to store anywhere
*/
- if (*arg)
- refmap_array[refmap_nr++] = arg;
+ refspec_append(&refmap, arg);
+
return 0;
}
@@ -116,7 +125,7 @@ static struct option builtin_fetch_options[] = {
N_("append to .git/FETCH_HEAD instead of overwriting")),
OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
N_("path to upload pack on remote end")),
- OPT__FORCE(&force, N_("force overwrite of local branch")),
+ OPT__FORCE(&force, N_("force overwrite of local branch"), 0),
OPT_BOOL('m', "multiple", &multiple,
N_("fetch from multiple remotes")),
OPT_SET_INT('t', "tags", &tags,
@@ -127,6 +136,8 @@ static struct option builtin_fetch_options[] = {
N_("number of submodules fetched in parallel")),
OPT_BOOL('p', "prune", &prune,
N_("prune remote-tracking branches no longer on remote")),
+ OPT_BOOL('P', "prune-tags", &prune_tags,
+ N_("prune local tags no longer on remote and clobber changed tags")),
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
N_("control recursive fetching of submodules"),
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
@@ -144,9 +155,9 @@ static struct option builtin_fetch_options[] = {
N_("deepen history of shallow clone, excluding rev")),
OPT_INTEGER(0, "deepen", &deepen_relative,
N_("deepen history of shallow clone")),
- { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
- N_("convert to a complete repository"),
- PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
+ OPT_SET_INT_F(0, "unshallow", &unshallow,
+ N_("convert to a complete repository"),
+ 1, PARSE_OPT_NONEG),
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
{ OPTION_CALLBACK, 0, "recurse-submodules-default",
@@ -158,6 +169,7 @@ static struct option builtin_fetch_options[] = {
N_("accept refs that update .git/shallow")),
{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+ OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -190,7 +202,7 @@ static void add_merge_config(struct ref **head,
for (i = 0; i < branch->merge_nr; i++) {
struct ref *rm, **old_tail = *tail;
- struct refspec refspec;
+ struct refspec_item refspec;
for (rm = *head; rm; rm = rm->next) {
if (branch_merge_matches(branch, i, rm->name)) {
@@ -252,7 +264,7 @@ static void find_non_local_tags(struct transport *transport,
struct string_list_item *item = NULL;
for_each_ref(add_existing, &existing_refs);
- for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
+ for (ref = transport_get_remote_refs(transport, NULL); ref; ref = ref->next) {
if (!starts_with(ref->name, "refs/tags/"))
continue;
@@ -327,26 +339,40 @@ static void find_non_local_tags(struct transport *transport,
}
static struct ref *get_ref_map(struct transport *transport,
- struct refspec *refspecs, int refspec_count,
+ struct refspec *rs,
int tags, int *autotags)
{
int i;
struct ref *rm;
struct ref *ref_map = NULL;
struct ref **tail = &ref_map;
+ struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
/* opportunistically-updated references: */
struct ref *orefs = NULL, **oref_tail = &orefs;
- const struct ref *remote_refs = transport_get_remote_refs(transport);
+ const struct ref *remote_refs;
+
+ if (rs->nr)
+ refspec_ref_prefixes(rs, &ref_prefixes);
+ else if (transport->remote && transport->remote->fetch.nr)
+ refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
- if (refspec_count) {
+ if (ref_prefixes.argc &&
+ (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {
+ argv_array_push(&ref_prefixes, "refs/tags/");
+ }
+
+ remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+
+ argv_array_clear(&ref_prefixes);
+
+ if (rs->nr) {
struct refspec *fetch_refspec;
- int fetch_refspec_nr;
- for (i = 0; i < refspec_count; i++) {
- get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
- if (refspecs[i].dst && refspecs[i].dst[0])
+ for (i = 0; i < rs->nr; i++) {
+ get_fetch_map(remote_refs, &rs->items[i], &tail, 0);
+ if (rs->items[i].dst && rs->items[i].dst[0])
*autotags = 1;
}
/* Merge everything on the command line (but not --tags) */
@@ -373,17 +399,14 @@ static struct ref *get_ref_map(struct transport *transport,
* by ref_remove_duplicates() in favor of one of these
* opportunistic entries with FETCH_HEAD_IGNORE.
*/
- if (refmap_array) {
- fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);
- fetch_refspec_nr = refmap_nr;
- } else {
- fetch_refspec = transport->remote->fetch;
- fetch_refspec_nr = transport->remote->fetch_refspec_nr;
- }
+ if (refmap.nr)
+ fetch_refspec = &refmap;
+ else
+ fetch_refspec = &transport->remote->fetch;
- for (i = 0; i < fetch_refspec_nr; i++)
- get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
- } else if (refmap_array) {
+ for (i = 0; i < fetch_refspec->nr; i++)
+ get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
+ } else if (refmap.nr) {
die("--refmap option is only meaningful with command-line refspec(s).");
} else {
/* Use the defaults */
@@ -391,16 +414,16 @@ static struct ref *get_ref_map(struct transport *transport,
struct branch *branch = branch_get(NULL);
int has_merge = branch_has_merge_config(branch);
if (remote &&
- (remote->fetch_refspec_nr ||
+ (remote->fetch.nr ||
/* Note: has_merge implies non-NULL branch->remote_name */
(has_merge && !strcmp(branch->remote_name, remote->name)))) {
- for (i = 0; i < remote->fetch_refspec_nr; i++) {
- get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
- if (remote->fetch[i].dst &&
- remote->fetch[i].dst[0])
+ for (i = 0; i < remote->fetch.nr; i++) {
+ get_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0);
+ if (remote->fetch.items[i].dst &&
+ remote->fetch.items[i].dst[0])
*autotags = 1;
if (!i && !has_merge && ref_map &&
- !remote->fetch[0].pattern)
+ !remote->fetch.items[0].pattern)
ref_map->fetch_head_status = FETCH_HEAD_MERGE;
}
/*
@@ -460,8 +483,8 @@ static int s_update_ref(const char *action,
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref->name,
- ref->new_oid.hash,
- check_old ? ref->old_oid.hash : NULL,
+ &ref->new_oid,
+ check_old ? &ref->old_oid : NULL,
0, msg, &err))
goto fail;
@@ -625,7 +648,7 @@ static int update_local_ref(struct ref *ref,
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
- type = sha1_object_info(ref->new_oid.hash, NULL);
+ type = oid_object_info(the_repository, &ref->new_oid, NULL);
if (type < 0)
die(_("object %s not found"), oid_to_hex(&ref->new_oid));
@@ -696,9 +719,9 @@ static int update_local_ref(struct ref *ref,
if (in_merge_bases(current, updated)) {
struct strbuf quickref = STRBUF_INIT;
int r;
- strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
strbuf_addstr(&quickref, "..");
- strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(&ref->new_oid);
@@ -711,9 +734,9 @@ static int update_local_ref(struct ref *ref,
} else if (force || ref->force) {
struct strbuf quickref = STRBUF_INIT;
int r;
- strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
strbuf_addstr(&quickref, "...");
- strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(&ref->new_oid);
@@ -730,7 +753,7 @@ static int update_local_ref(struct ref *ref,
}
}
-static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
@@ -740,7 +763,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
if (!ref)
return -1; /* end of the list */
*rm = ref->next;
- hashcpy(sha1, ref->old_oid.hash);
+ oidcpy(oid, &ref->old_oid);
return 0;
}
@@ -935,11 +958,11 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
return ret;
}
-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
- const char *raw_url)
+static int prune_refs(struct refspec *rs, struct ref *ref_map,
+ const char *raw_url)
{
int url_len, i, result = 0;
- struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+ struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
char *url;
int summary_width = transport_summary_width(stale_refs);
const char *dangling_msg = dry_run
@@ -1085,7 +1108,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
}
static int do_fetch(struct transport *transport,
- struct refspec *refs, int ref_count)
+ struct refspec *rs)
{
struct string_list existing_refs = STRING_LIST_INIT_DUP;
struct ref *ref_map;
@@ -1102,9 +1125,6 @@ static int do_fetch(struct transport *transport,
tags = TAGS_UNSET;
}
- if (!transport->get_refs_list || !transport->fetch)
- die(_("Don't know how to fetch from %s"), transport->url);
-
/* if not appending, truncate FETCH_HEAD */
if (!append && !dry_run) {
retcode = truncate_fetch_head();
@@ -1112,7 +1132,7 @@ static int do_fetch(struct transport *transport,
goto cleanup;
}
- ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
+ ref_map = get_ref_map(transport, rs, tags, &autotags);
if (!update_head_ok)
check_not_current_branch(ref_map);
@@ -1136,11 +1156,10 @@ static int do_fetch(struct transport *transport,
* explicitly (via command line or configuration); we
* don't care whether --tags was specified.
*/
- if (ref_count) {
- prune_refs(refs, ref_count, ref_map, transport->url);
+ if (rs->nr) {
+ prune_refs(rs, ref_map, transport->url);
} else {
- prune_refs(transport->remote->fetch,
- transport->remote->fetch_refspec_nr,
+ prune_refs(&transport->remote->fetch,
ref_map,
transport->url);
}
@@ -1222,6 +1241,8 @@ static void add_options_to_argv(struct argv_array *argv)
argv_array_push(argv, "--dry-run");
if (prune != -1)
argv_array_push(argv, prune ? "--prune" : "--no-prune");
+ if (prune_tags != -1)
+ argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
if (update_head_ok)
argv_array_push(argv, "--update-head-ok");
if (force)
@@ -1325,12 +1346,13 @@ static inline void fetch_one_setup_partial(struct remote *remote)
return;
}
-static int fetch_one(struct remote *remote, int argc, const char **argv)
+static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
{
- static const char **refs = NULL;
- struct refspec *refspec;
- int ref_nr = 0;
+ struct refspec rs = REFSPEC_INIT_FETCH;
+ int i;
int exit_code;
+ int maybe_prune_tags;
+ int remote_via_config = remote_is_configured(remote, 0);
if (!remote)
die(_("No remote repository specified. Please, specify either a URL or a\n"
@@ -1340,37 +1362,54 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
if (prune < 0) {
/* no command line request */
- if (0 <= gtransport->remote->prune)
- prune = gtransport->remote->prune;
+ if (0 <= remote->prune)
+ prune = remote->prune;
else if (0 <= fetch_prune_config)
prune = fetch_prune_config;
else
prune = PRUNE_BY_DEFAULT;
}
- if (argc > 0) {
- int j = 0;
- int i;
- refs = xcalloc(st_add(argc, 1), sizeof(const char *));
- for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "tag")) {
- i++;
- if (i >= argc)
- die(_("You need to specify a tag name."));
- refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s",
- argv[i], argv[i]);
- } else
- refs[j++] = argv[i];
+ if (prune_tags < 0) {
+ /* no command line request */
+ if (0 <= remote->prune_tags)
+ prune_tags = remote->prune_tags;
+ else if (0 <= fetch_prune_tags_config)
+ prune_tags = fetch_prune_tags_config;
+ else
+ prune_tags = PRUNE_TAGS_BY_DEFAULT;
+ }
+
+ maybe_prune_tags = prune_tags_ok && prune_tags;
+ if (maybe_prune_tags && remote_via_config)
+ refspec_append(&remote->fetch, TAG_REFSPEC);
+
+ if (maybe_prune_tags && (argc || !remote_via_config))
+ refspec_append(&rs, TAG_REFSPEC);
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "tag")) {
+ char *tag;
+ i++;
+ if (i >= argc)
+ die(_("You need to specify a tag name."));
+
+ tag = xstrfmt("refs/tags/%s:refs/tags/%s",
+ argv[i], argv[i]);
+ refspec_append(&rs, tag);
+ free(tag);
+ } else {
+ refspec_append(&rs, argv[i]);
}
- refs[j] = NULL;
- ref_nr = j;
}
+ if (server_options.nr)
+ gtransport->server_options = &server_options;
+
sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack);
- refspec = parse_fetch_refspec(ref_nr, refs);
- exit_code = do_fetch(gtransport, refspec, ref_nr);
- free_refspec(ref_nr, refspec);
+ exit_code = do_fetch(gtransport, &rs);
+ refspec_clear(&rs);
transport_disconnect(gtransport);
gtransport = NULL;
return exit_code;
@@ -1382,6 +1421,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote = NULL;
int result = 0;
+ int prune_tags_ok = 1;
struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
packet_trace_identity("fetch");
@@ -1448,6 +1488,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
} else {
/* Zero or one remotes */
remote = remote_get(argv[0]);
+ prune_tags_ok = (argc == 1);
argc--;
argv++;
}
@@ -1456,7 +1497,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (remote) {
if (filter_options.choice || repository_format_partial_clone)
fetch_one_setup_partial(remote);
- result = fetch_one(remote, argc, argv);
+ result = fetch_one(remote, argc, argv, prune_tags_ok);
} else {
if (filter_options.choice)
die(_("--filter can only be used with the remote configured in core.partialClone"));
@@ -1468,7 +1509,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
struct argv_array options = ARGV_ARRAY_INIT;
add_options_to_argv(&options);
- result = fetch_populated_submodules(&options,
+ result = fetch_populated_submodules(the_repository,
+ &options,
submodule_prefix,
recurse_submodules,
recurse_submodules_default,
@@ -1479,7 +1521,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
string_list_clear(&list, 0);
- close_all_packs();
+ close_all_packs(the_repository->objects);
argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
if (verbosity < 0)
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index e99b5ddbf9..bd680be687 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -377,7 +377,8 @@ static void shortlog(const char *name,
string_list_append(&subjects,
oid_to_hex(&commit->object.oid));
else
- string_list_append(&subjects, strbuf_detach(&sb, NULL));
+ string_list_append_nodup(&subjects,
+ strbuf_detach(&sb, NULL));
}
if (opts->credit_people)
@@ -484,10 +485,10 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
struct strbuf tagbuf = STRBUF_INIT;
for (i = 0; i < origins.nr; i++) {
- unsigned char *sha1 = origins.items[i].util;
+ struct object_id *oid = origins.items[i].util;
enum object_type type;
unsigned long size, len;
- char *buf = read_sha1_file(sha1, &type, &size);
+ char *buf = read_object_file(oid, &type, &size);
struct strbuf sig = STRBUF_INIT;
if (!buf || type != OBJ_TAG)
@@ -571,7 +572,7 @@ static void find_merge_parents(struct merge_parents *result,
head_commit = lookup_commit(head);
if (head_commit)
commit_list_insert(head_commit, &parents);
- parents = reduce_heads(parents);
+ reduce_heads_replace(&parents);
while (parents) {
struct commit *cmit = pop_commit(&parents);
@@ -603,7 +604,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* get current branch */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
+ resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
if (!current_branch)
die("No current branch");
if (starts_with(current_branch, "refs/heads/"))
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 3b76c0ef0f..3ad4f160f9 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "commit.h"
#include "tree.h"
@@ -16,6 +17,7 @@
#include "streaming.h"
#include "decorate.h"
#include "packfile.h"
+#include "object-store.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -65,12 +67,13 @@ static const char *printable_type(struct object *obj)
const char *ret;
if (obj->type == OBJ_NONE) {
- enum object_type type = sha1_object_info(obj->oid.hash, NULL);
+ enum object_type type = oid_object_info(the_repository,
+ &obj->oid, NULL);
if (type > 0)
object_as_type(obj, type, 0);
}
- ret = typename(obj->type);
+ ret = type_name(obj->type);
if (!ret)
ret = "unknown";
@@ -137,7 +140,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
printf("broken link from %7s %s\n",
printable_type(parent), describe_object(parent));
printf("broken link from %7s %s\n",
- (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
+ (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown");
errors_found |= ERROR_REACHABLE;
return 1;
}
@@ -180,7 +183,13 @@ static void mark_object_reachable(struct object *obj)
static int traverse_one_object(struct object *obj)
{
- return fsck_walk(obj, obj, &fsck_walk_options);
+ int result = fsck_walk(obj, obj, &fsck_walk_options);
+
+ if (obj->type == OBJ_TREE) {
+ struct tree *tree = (struct tree *)obj;
+ free_tree_buffer(tree);
+ }
+ return result;
}
static int traverse_reachable(void)
@@ -219,7 +228,7 @@ static void check_reachable_object(struct object *obj)
if (!(obj->flags & HAS_OBJ)) {
if (is_promisor_object(&obj->oid))
return;
- if (has_sha1_pack(obj->oid.hash))
+ if (has_object_pack(&obj->oid))
return; /* it is in pack - forget about it */
printf("missing %s %s\n", printable_type(obj),
describe_object(obj));
@@ -331,7 +340,7 @@ static void check_connectivity(void)
}
}
-static int fsck_obj(struct object *obj)
+static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
{
int err;
@@ -345,7 +354,7 @@ static int fsck_obj(struct object *obj)
if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, "broken links");
- err = fsck_object(obj, NULL, 0, &fsck_obj_options);
+ err = fsck_object(obj, buffer, size, &fsck_obj_options);
if (err)
goto out;
@@ -390,7 +399,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
}
obj->flags &= ~(REACHABLE | SEEN);
obj->flags |= HAS_OBJ;
- return fsck_obj(obj);
+ return fsck_obj(obj, buffer, size);
}
static int default_refs;
@@ -498,44 +507,42 @@ static void get_default_heads(void)
}
}
-static struct object *parse_loose_object(const struct object_id *oid,
- const char *path)
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
{
struct object *obj;
- void *contents;
enum object_type type;
unsigned long size;
+ void *contents;
int eaten;
- if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0)
- return NULL;
+ if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+ errors_found |= ERROR_OBJECT;
+ error("%s: object corrupt or missing: %s",
+ oid_to_hex(oid), path);
+ return 0; /* keep checking other objects */
+ }
if (!contents && type != OBJ_BLOB)
- die("BUG: read_loose_object streamed a non-blob");
+ BUG("read_loose_object streamed a non-blob");
obj = parse_object_buffer(oid, type, size, contents, &eaten);
-
- if (!eaten)
- free(contents);
- return obj;
-}
-
-static int fsck_loose(const struct object_id *oid, const char *path, void *data)
-{
- struct object *obj = parse_loose_object(oid, path);
-
if (!obj) {
errors_found |= ERROR_OBJECT;
- error("%s: object corrupt or missing: %s",
+ error("%s: object could not be parsed: %s",
oid_to_hex(oid), path);
+ if (!eaten)
+ free(contents);
return 0; /* keep checking other objects */
}
obj->flags &= ~(REACHABLE | SEEN);
obj->flags |= HAS_OBJ;
- if (fsck_obj(obj))
+ if (fsck_obj(obj, contents, size))
errors_found |= ERROR_OBJECT;
- return 0;
+
+ if (!eaten)
+ free(contents);
+ return 0; /* keep checking other objects, even if we saw an error */
}
static int fsck_cruft(const char *basename, const char *path, void *data)
@@ -574,7 +581,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
if (!head_points_at) {
errors_found |= ERROR_REFS;
return error("Invalid HEAD");
@@ -713,9 +720,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
} else {
+ struct alternate_object_database *alt_odb_list;
+
fsck_object_dir(get_object_directory());
- prepare_alt_odb();
+ prepare_alt_odb(the_repository);
+ alt_odb_list = the_repository->objects->alt_odb_list;
for (alt = alt_odb_list; alt; alt = alt->next)
fsck_object_dir(alt->path);
@@ -724,10 +734,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
uint32_t total = 0, count = 0;
struct progress *progress = NULL;
- prepare_packed_git();
-
if (show_progress) {
- for (p = packed_git; p; p = p->next) {
+ for (p = get_packed_git(the_repository); p;
+ p = p->next) {
if (open_pack_index(p))
continue;
total += p->num_objects;
@@ -735,7 +744,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
progress = start_progress(_("Checking objects"), total);
}
- for (p = packed_git; p; p = p->next) {
+ for (p = get_packed_git(the_repository); p;
+ p = p->next) {
/* verify gives error messages itself */
if (verify_pack(p, fsck_obj_buffer,
progress, count))
@@ -744,6 +754,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
stop_progress(&progress);
}
+
+ if (fsck_finish(&fsck_obj_options))
+ errors_found |= ERROR_OBJECT;
}
for (i = 0; i < argc; i++) {
@@ -783,6 +796,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (keep_cache_objects) {
verify_index_checksum = 1;
+ verify_ce_order = 1;
read_cache();
for (i = 0; i < active_nr; i++) {
unsigned int mode;
diff --git a/builtin/gc.c b/builtin/gc.c
index 77fa720bd0..ccfb1ceaeb 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,6 +11,7 @@
*/
#include "builtin.h"
+#include "repository.h"
#include "config.h"
#include "tempfile.h"
#include "lockfile.h"
@@ -20,6 +21,11 @@
#include "argv-array.h"
#include "commit.h"
#include "packfile.h"
+#include "object-store.h"
+#include "pack.h"
+#include "pack-objects.h"
+#include "blob.h"
+#include "tree.h"
#define FAILED_RUN "failed to run %s"
@@ -39,6 +45,8 @@ static timestamp_t gc_log_expire_time;
static const char *gc_log_expire = "1.day.ago";
static const char *prune_expire = "2.weeks.ago";
static const char *prune_worktrees_expire = "3.months.ago";
+static unsigned long big_pack_threshold;
+static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
static struct argv_array reflog = ARGV_ARRAY_INIT;
@@ -126,6 +134,9 @@ static void gc_config(void)
git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
git_config_get_expiry("gc.logexpiry", &gc_log_expire);
+ git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold);
+ git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size);
+
git_config(git_default_config, NULL);
}
@@ -164,6 +175,28 @@ static int too_many_loose_objects(void)
return needed;
}
+static struct packed_git *find_base_packs(struct string_list *packs,
+ unsigned long limit)
+{
+ struct packed_git *p, *base = NULL;
+
+ for (p = get_packed_git(the_repository); p; p = p->next) {
+ if (!p->pack_local)
+ continue;
+ if (limit) {
+ if (p->pack_size >= limit)
+ string_list_append(packs, p->pack_name);
+ } else if (!base || base->pack_size < p->pack_size) {
+ base = p;
+ }
+ }
+
+ if (base)
+ string_list_append(packs, base->pack_name);
+
+ return base;
+}
+
static int too_many_packs(void)
{
struct packed_git *p;
@@ -172,8 +205,7 @@ static int too_many_packs(void)
if (gc_auto_pack_limit <= 0)
return 0;
- prepare_packed_git();
- for (cnt = 0, p = packed_git; p; p = p->next) {
+ for (cnt = 0, p = get_packed_git(the_repository); p; p = p->next) {
if (!p->pack_local)
continue;
if (p->pack_keep)
@@ -187,7 +219,86 @@ static int too_many_packs(void)
return gc_auto_pack_limit < cnt;
}
-static void add_repack_all_option(void)
+static uint64_t total_ram(void)
+{
+#if defined(HAVE_SYSINFO)
+ struct sysinfo si;
+
+ if (!sysinfo(&si))
+ return si.totalram;
+#elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM))
+ int64_t physical_memory;
+ int mib[2];
+ size_t length;
+
+ mib[0] = CTL_HW;
+# if defined(HW_MEMSIZE)
+ mib[1] = HW_MEMSIZE;
+# else
+ mib[1] = HW_PHYSMEM;
+# endif
+ length = sizeof(int64_t);
+ if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0))
+ return physical_memory;
+#elif defined(GIT_WINDOWS_NATIVE)
+ MEMORYSTATUSEX memInfo;
+
+ memInfo.dwLength = sizeof(MEMORYSTATUSEX);
+ if (GlobalMemoryStatusEx(&memInfo))
+ return memInfo.ullTotalPhys;
+#endif
+ return 0;
+}
+
+static uint64_t estimate_repack_memory(struct packed_git *pack)
+{
+ unsigned long nr_objects = approximate_object_count();
+ size_t os_cache, heap;
+
+ if (!pack || !nr_objects)
+ return 0;
+
+ /*
+ * First we have to scan through at least one pack.
+ * Assume enough room in OS file cache to keep the entire pack
+ * or we may accidentally evict data of other processes from
+ * the cache.
+ */
+ os_cache = pack->pack_size + pack->index_size;
+ /* then pack-objects needs lots more for book keeping */
+ heap = sizeof(struct object_entry) * nr_objects;
+ /*
+ * internal rev-list --all --objects takes up some memory too,
+ * let's say half of it is for blobs
+ */
+ heap += sizeof(struct blob) * nr_objects / 2;
+ /*
+ * and the other half is for trees (commits and tags are
+ * usually insignificant)
+ */
+ heap += sizeof(struct tree) * nr_objects / 2;
+ /* and then obj_hash[], underestimated in fact */
+ heap += sizeof(struct object *) * nr_objects;
+ /* revindex is used also */
+ heap += sizeof(struct revindex_entry) * nr_objects;
+ /*
+ * read_sha1_file() (either at delta calculation phase, or
+ * writing phase) also fills up the delta base cache
+ */
+ heap += delta_base_cache_limit;
+ /* and of course pack-objects has its own delta cache */
+ heap += max_delta_cache_size;
+
+ return os_cache + heap;
+}
+
+static int keep_one_pack(struct string_list_item *item, void *data)
+{
+ argv_array_pushf(&repack, "--keep-pack=%s", basename(item->string));
+ return 0;
+}
+
+static void add_repack_all_option(struct string_list *keep_pack)
{
if (prune_expire && !strcmp(prune_expire, "now"))
argv_array_push(&repack, "-a");
@@ -196,6 +307,9 @@ static void add_repack_all_option(void)
if (prune_expire)
argv_array_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
}
+
+ if (keep_pack)
+ for_each_string_list(keep_pack, keep_one_pack, NULL);
}
static void add_repack_incremental_option(void)
@@ -218,9 +332,35 @@ static int need_to_gc(void)
* we run "repack -A -d -l". Otherwise we tell the caller
* there is no need.
*/
- if (too_many_packs())
- add_repack_all_option();
- else if (too_many_loose_objects())
+ if (too_many_packs()) {
+ struct string_list keep_pack = STRING_LIST_INIT_NODUP;
+
+ if (big_pack_threshold) {
+ find_base_packs(&keep_pack, big_pack_threshold);
+ if (keep_pack.nr >= gc_auto_pack_limit) {
+ big_pack_threshold = 0;
+ string_list_clear(&keep_pack, 0);
+ find_base_packs(&keep_pack, 0);
+ }
+ } else {
+ struct packed_git *p = find_base_packs(&keep_pack, 0);
+ uint64_t mem_have, mem_want;
+
+ mem_have = total_ram();
+ mem_want = estimate_repack_memory(p);
+
+ /*
+ * Only allow 1/2 of memory for pack-objects, leave
+ * the rest for the OS and other processes in the
+ * system.
+ */
+ if (!mem_have || mem_want < mem_have / 2)
+ string_list_clear(&keep_pack, 0);
+ }
+
+ add_repack_all_option(&keep_pack);
+ string_list_clear(&keep_pack, 0);
+ } else if (too_many_loose_objects())
add_repack_incremental_option();
else
return 0;
@@ -233,7 +373,7 @@ static int need_to_gc(void)
/* return NULL on success, else hostname running the gc */
static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
{
- static struct lock_file lock;
+ struct lock_file lock = LOCK_INIT;
char my_host[HOST_NAME_MAX + 1];
struct strbuf sb = STRBUF_INIT;
struct stat st;
@@ -353,6 +493,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
const char *name;
pid_t pid;
int daemonized = 0;
+ int keep_base_pack = -1;
+ timestamp_t dummy;
struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -360,8 +502,13 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
N_("prune unreferenced objects"),
PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
- OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")),
- OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")),
+ OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_BOOL_F(0, "force", &force,
+ N_("force running gc even if there may be another gc running"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_BOOL(0, "keep-largest-pack", &keep_base_pack,
+ N_("repack all other packs except the largest pack")),
OPT_END()
};
@@ -378,7 +525,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
/* default expiry time, overwritten in gc_config */
gc_config();
if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
- die(_("Failed to parse gc.logexpiry value %s"), gc_log_expire);
+ die(_("failed to parse gc.logexpiry value %s"), gc_log_expire);
if (pack_refs < 0)
pack_refs = !is_bare_repository();
@@ -388,6 +535,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (argc > 0)
usage_with_options(builtin_gc_usage, builtin_gc_options);
+ if (prune_expire && parse_expiry_date(prune_expire, &dummy))
+ die(_("failed to parse prune expiry value %s"), prune_expire);
+
if (aggressive) {
argv_array_push(&repack, "-f");
if (aggressive_depth > 0)
@@ -427,8 +577,19 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
*/
daemonized = !daemonize();
}
- } else
- add_repack_all_option();
+ } else {
+ struct string_list keep_pack = STRING_LIST_INIT_NODUP;
+
+ if (keep_base_pack != -1) {
+ if (keep_base_pack)
+ find_base_packs(&keep_pack, 0);
+ } else if (big_pack_threshold) {
+ find_base_packs(&keep_pack, big_pack_threshold);
+ }
+
+ add_repack_all_option(&keep_pack);
+ string_list_clear(&keep_pack, 0);
+ }
name = lock_repo_for_gc(force, &pid);
if (name) {
@@ -476,7 +637,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
return error(FAILED_RUN, rerere.argv[0]);
report_garbage = report_pack_garbage;
- reprepare_packed_git();
+ reprepare_packed_git(the_repository);
if (pack_garbage.nr > 0)
clean_pack_garbage();
diff --git a/builtin/grep.c b/builtin/grep.c
index 2d65f27d01..ee753a403e 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -22,6 +22,7 @@
#include "pathspec.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "object-store.h"
static char const * const grep_usage[] = {
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
@@ -92,8 +93,7 @@ static pthread_cond_t cond_result;
static int skip_first_line;
-static void add_work(struct grep_opt *opt, enum grep_source_type type,
- const char *name, const char *path, const void *id)
+static void add_work(struct grep_opt *opt, const struct grep_source *gs)
{
grep_lock();
@@ -101,7 +101,7 @@ static void add_work(struct grep_opt *opt, enum grep_source_type type,
pthread_cond_wait(&cond_write, &grep_mutex);
}
- grep_source_init(&todo[todo_end].source, type, name, path, id);
+ todo[todo_end].source = *gs;
if (opt->binary != GREP_BINARY_TEXT)
grep_source_load_driver(&todo[todo_end].source);
todo[todo_end].done = 0;
@@ -307,7 +307,7 @@ static void *lock_and_read_oid_file(const struct object_id *oid, enum object_typ
void *data;
grep_read_lock();
- data = read_sha1_file(oid->hash, type, size);
+ data = read_object_file(oid, type, size);
grep_read_unlock();
return data;
}
@@ -317,6 +317,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
const char *path)
{
struct strbuf pathbuf = STRBUF_INIT;
+ struct grep_source gs;
if (opt->relative && opt->prefix_length) {
quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
@@ -325,19 +326,22 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
strbuf_addstr(&pathbuf, filename);
}
+ grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
+ strbuf_release(&pathbuf);
+
#ifndef NO_PTHREADS
if (num_threads) {
- add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid);
- strbuf_release(&pathbuf);
+ /*
+ * add_work() copies gs and thus assumes ownership of
+ * its fields, so do not call grep_source_clear()
+ */
+ add_work(opt, &gs);
return 0;
} else
#endif
{
- struct grep_source gs;
int hit;
- grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
- strbuf_release(&pathbuf);
hit = grep_source(opt, &gs);
grep_source_clear(&gs);
@@ -348,25 +352,29 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
static int grep_file(struct grep_opt *opt, const char *filename)
{
struct strbuf buf = STRBUF_INIT;
+ struct grep_source gs;
if (opt->relative && opt->prefix_length)
quote_path_relative(filename, opt->prefix, &buf);
else
strbuf_addstr(&buf, filename);
+ grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
+ strbuf_release(&buf);
+
#ifndef NO_PTHREADS
if (num_threads) {
- add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
- strbuf_release(&buf);
+ /*
+ * add_work() copies gs and thus assumes ownership of
+ * its fields, so do not call grep_source_clear()
+ */
+ add_work(opt, &gs);
return 0;
} else
#endif
{
- struct grep_source gs;
int hit;
- grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
- strbuf_release(&buf);
hit = grep_source(opt, &gs);
grep_source_clear(&gs);
@@ -431,7 +439,9 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
* store is no longer global and instead is a member of the repository
* object.
*/
- add_to_alternates_memory(submodule.objectdir);
+ grep_read_lock();
+ add_to_alternates_memory(submodule.objects->objectdir);
+ grep_read_unlock();
if (oid) {
struct object *object;
@@ -443,7 +453,7 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
object = parse_object_or_die(oid, oid_to_hex(oid));
grep_read_lock();
- data = read_object_with_reference(object->oid.hash, tree_type,
+ data = read_object_with_reference(&object->oid, tree_type,
&size, NULL);
grep_read_unlock();
@@ -478,7 +488,8 @@ static int grep_cache(struct grep_opt *opt, struct repository *repo,
strbuf_addstr(&name, repo->submodule_prefix);
}
- repo_read_index(repo);
+ if (repo_read_index(repo) < 0)
+ die("index file corrupt");
for (nr = 0; nr < repo->index->cache_nr; nr++) {
const struct cache_entry *ce = repo->index->cache[nr];
@@ -592,8 +603,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
}
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
- struct object *obj, const char *name, const char *path,
- struct repository *repo)
+ struct object *obj, const char *name, const char *path)
{
if (obj->type == OBJ_BLOB)
return grep_oid(opt, &obj->oid, name, 0, path);
@@ -605,7 +615,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
int hit, len;
grep_read_lock();
- data = read_object_with_reference(obj->oid.hash, tree_type,
+ data = read_object_with_reference(&obj->oid, tree_type,
&size, NULL);
grep_read_unlock();
@@ -620,16 +630,15 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
}
init_tree_desc(&tree, data, size);
hit = grep_tree(opt, pathspec, &tree, &base, base.len,
- obj->type == OBJ_COMMIT, repo);
+ obj->type == OBJ_COMMIT, the_repository);
strbuf_release(&base);
free(data);
return hit;
}
- die(_("unable to grep from object of type %s"), typename(obj->type));
+ die(_("unable to grep from object of type %s"), type_name(obj->type));
}
static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
- struct repository *repo,
const struct object_array *list)
{
unsigned int i;
@@ -642,11 +651,11 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
/* load the gitmodules file for this rev */
if (recurse_submodules) {
- submodule_free();
+ submodule_free(the_repository);
gitmodules_config_oid(&real_obj->oid);
}
- if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path,
- repo)) {
+ if (grep_object(opt, pathspec, real_obj, list->objects[i].name,
+ list->objects[i].path)) {
hit = 1;
if (opt->status_only)
break;
@@ -830,8 +839,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_BOOL('L', "files-without-match",
&opt.unmatch_name_only,
N_("show only the names of files without match")),
- OPT_BOOL('z', "null", &opt.null_following_name,
- N_("print NUL after filenames")),
+ OPT_BOOL_F('z', "null", &opt.null_following_name,
+ N_("print NUL after filenames"),
+ PARSE_OPT_NOCOMPLETE),
OPT_BOOL('c', "count", &opt.count,
N_("show the number of matches instead of matching lines")),
OPT__COLOR(&opt.color, N_("highlight matches")),
@@ -876,15 +886,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
N_("indicate hit with exit status without output")),
OPT_BOOL(0, "all-match", &opt.all_match,
N_("show only matches from files that match all patterns")),
- { OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
- N_("show parse tree for grep expression"),
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },
+ OPT_SET_INT_F(0, "debug", &opt.debug,
+ N_("show parse tree for grep expression"),
+ 1, PARSE_OPT_HIDDEN),
OPT_GROUP(""),
{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
N_("pager"), N_("show matching files in the pager"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
- OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
- N_("allow calling of grep(1) (ignored by this build)")),
+ PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
+ NULL, (intptr_t)default_pager },
+ OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
+ N_("allow calling of grep(1) (ignored by this build)"),
+ PARSE_OPT_NOCOMPLETE),
OPT_END()
};
@@ -1013,6 +1025,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
prefix, argv + i);
pathspec.max_depth = opt.max_depth;
pathspec.recursive = 1;
+ pathspec.recurse_submodules = !!recurse_submodules;
#ifndef NO_PTHREADS
if (list.nr || cached || show_in_pager)
@@ -1094,7 +1107,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (cached)
die(_("both --cached and trees are given."));
- hit = grep_objects(&opt, &pathspec, the_repository, &list);
+ hit = grep_objects(&opt, &pathspec, &list);
}
if (num_threads)
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index c532ff9320..a9a3a198c3 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -9,7 +9,7 @@
#include "blob.h"
#include "quote.h"
#include "parse-options.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
/*
* This is to create corrupt objects for debugging and as such it
@@ -24,7 +24,8 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
if (strbuf_read(&buf, fd, 4096) < 0)
ret = -1;
else
- ret = hash_sha1_file_literally(buf.buf, buf.len, type, oid, flags);
+ ret = hash_object_file_literally(buf.buf, buf.len, type, oid,
+ flags);
strbuf_release(&buf);
return ret;
}
diff --git a/builtin/help.c b/builtin/help.c
index d3c8fc4082..8d4f6dd301 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -4,11 +4,12 @@
#include "cache.h"
#include "config.h"
#include "builtin.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "parse-options.h"
#include "run-command.h"
#include "column.h"
#include "help.h"
+#include "alias.h"
#ifndef DEFAULT_HELP_FORMAT
#define DEFAULT_HELP_FORMAT "man"
@@ -36,6 +37,8 @@ static const char *html_path;
static int show_all = 0;
static int show_guides = 0;
+static int show_config;
+static int verbose;
static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static int exclude_guides;
@@ -43,11 +46,14 @@ static struct option builtin_help_options[] = {
OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
+ OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
+ OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
HELP_FORMAT_WEB),
OPT_SET_INT('i', "info", &help_format, N_("show info page"),
HELP_FORMAT_INFO),
+ OPT__VERBOSE(&verbose, N_("print command description")),
OPT_END(),
};
@@ -194,11 +200,11 @@ static void do_add_man_viewer_info(const char *name,
size_t len,
const char *value)
{
- struct man_viewer_info_list *new;
- FLEX_ALLOC_MEM(new, name, name, len);
- new->info = xstrdup(value);
- new->next = man_viewer_info_list;
- man_viewer_info_list = new;
+ struct man_viewer_info_list *new_man_viewer;
+ FLEX_ALLOC_MEM(new_man_viewer, name, name, len);
+ new_man_viewer->info = xstrdup(value);
+ new_man_viewer->next = man_viewer_info_list;
+ man_viewer_info_list = new_man_viewer;
}
static int add_man_viewer_path(const char *name,
@@ -400,38 +406,6 @@ static void show_html_page(const char *git_cmd)
open_html(page_path.buf);
}
-static struct {
- const char *name;
- const char *help;
-} common_guides[] = {
- { "attributes", N_("Defining attributes per path") },
- { "everyday", N_("Everyday Git With 20 Commands Or So") },
- { "glossary", N_("A Git glossary") },
- { "ignore", N_("Specifies intentionally untracked files to ignore") },
- { "modules", N_("Defining submodule properties") },
- { "revisions", N_("Specifying revisions and ranges for Git") },
- { "tutorial", N_("A tutorial introduction to Git (for version 1.5.1 or newer)") },
- { "workflows", N_("An overview of recommended workflows with Git") },
-};
-
-static void list_common_guides_help(void)
-{
- int i, longest = 0;
-
- for (i = 0; i < ARRAY_SIZE(common_guides); i++) {
- if (longest < strlen(common_guides[i].name))
- longest = strlen(common_guides[i].name);
- }
-
- puts(_("The common Git guides are:\n"));
- for (i = 0; i < ARRAY_SIZE(common_guides); i++) {
- printf(" %s ", common_guides[i].name);
- mput_char(' ', longest - strlen(common_guides[i].name));
- puts(_(common_guides[i].help));
- }
- putchar('\n');
-}
-
static const char *check_git_cmd(const char* cmd)
{
char *alias;
@@ -463,11 +437,29 @@ int cmd_help(int argc, const char **argv, const char *prefix)
if (show_all) {
git_config(git_help_config, NULL);
+ if (verbose) {
+ setup_pager();
+ list_all_cmds_help();
+ return 0;
+ }
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
load_command_list("git-", &main_cmds, &other_cmds);
list_commands(colopts, &main_cmds, &other_cmds);
}
+ if (show_config) {
+ int for_human = show_config == 1;
+
+ if (!for_human) {
+ list_config_help(for_human);
+ return 0;
+ }
+ setup_pager();
+ list_config_help(for_human);
+ printf("\n%s\n", _("'git help config' for more information"));
+ return 0;
+ }
+
if (show_guides)
list_common_guides_help();
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index a0a35e69c0..74fe2973e1 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -9,10 +9,11 @@
#include "tree.h"
#include "progress.h"
#include "fsck.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "streaming.h"
#include "thread-utils.h"
#include "packfile.h"
+#include "object-store.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>])";
@@ -49,6 +50,7 @@ struct thread_local {
int pack_fd;
};
+/* Remember to update object flag allocation in object.h */
#define FLAG_LINK (1u<<20)
#define FLAG_CHECKED (1u<<21)
@@ -58,7 +60,7 @@ struct ofs_delta_entry {
};
struct ref_delta_entry {
- unsigned char sha1[20];
+ struct object_id oid;
int obj_no;
};
@@ -91,7 +93,7 @@ static unsigned int input_offset, input_len;
static off_t consumed_bytes;
static off_t max_input_size;
static unsigned deepest_delta;
-static git_SHA_CTX input_ctx;
+static git_hash_ctx input_ctx;
static uint32_t input_crc32;
static int input_fd, output_fd;
static const char *curr_pack;
@@ -221,14 +223,14 @@ static unsigned check_object(struct object *obj)
if (!(obj->flags & FLAG_CHECKED)) {
unsigned long size;
- int type = sha1_object_info(obj->oid.hash, &size);
+ int type = oid_object_info(the_repository, &obj->oid, &size);
if (type <= 0)
die(_("did not receive expected object %s"),
oid_to_hex(&obj->oid));
if (type != obj->type)
die(_("object %s: expected type %s, found %s"),
oid_to_hex(&obj->oid),
- typename(obj->type), typename(type));
+ type_name(obj->type), type_name(type));
obj->flags |= FLAG_CHECKED;
return 1;
}
@@ -253,7 +255,7 @@ static void flush(void)
if (input_offset) {
if (output_fd >= 0)
write_or_die(output_fd, input_buffer, input_offset);
- git_SHA1_Update(&input_ctx, input_buffer, input_offset);
+ the_hash_algo->update_fn(&input_ctx, input_buffer, input_offset);
memmove(input_buffer, input_buffer + input_offset, input_len);
input_offset = 0;
}
@@ -326,7 +328,7 @@ static const char *open_pack_file(const char *pack_name)
output_fd = -1;
nothread_data.pack_fd = input_fd;
}
- git_SHA1_Init(&input_ctx);
+ the_hash_algo->init_fn(&input_ctx);
return pack_name;
}
@@ -437,22 +439,22 @@ static int is_delta_type(enum object_type type)
}
static void *unpack_entry_data(off_t offset, unsigned long size,
- enum object_type type, unsigned char *sha1)
+ enum object_type type, struct object_id *oid)
{
static char fixed_buf[8192];
int status;
git_zstream stream;
void *buf;
- git_SHA_CTX c;
+ git_hash_ctx c;
char hdr[32];
int hdrlen;
if (!is_delta_type(type)) {
- hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1;
- git_SHA1_Init(&c);
- git_SHA1_Update(&c, hdr, hdrlen);
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), size) + 1;
+ the_hash_algo->init_fn(&c);
+ the_hash_algo->update_fn(&c, hdr, hdrlen);
} else
- sha1 = NULL;
+ oid = NULL;
if (type == OBJ_BLOB && size > big_file_threshold)
buf = fixed_buf;
else
@@ -469,8 +471,8 @@ static void *unpack_entry_data(off_t offset, unsigned long size,
stream.avail_in = input_len;
status = git_inflate(&stream, 0);
use(input_len - stream.avail_in);
- if (sha1)
- git_SHA1_Update(&c, last_out, stream.next_out - last_out);
+ if (oid)
+ the_hash_algo->update_fn(&c, last_out, stream.next_out - last_out);
if (buf == fixed_buf) {
stream.next_out = buf;
stream.avail_out = sizeof(fixed_buf);
@@ -479,15 +481,15 @@ static void *unpack_entry_data(off_t offset, unsigned long size,
if (stream.total_out != size || status != Z_STREAM_END)
bad_object(offset, _("inflate returned %d"), status);
git_inflate_end(&stream);
- if (sha1)
- git_SHA1_Final(sha1, &c);
+ if (oid)
+ the_hash_algo->final_fn(oid->hash, &c);
return buf == fixed_buf ? NULL : buf;
}
static void *unpack_raw_entry(struct object_entry *obj,
off_t *ofs_offset,
- unsigned char *ref_sha1,
- unsigned char *sha1)
+ struct object_id *ref_oid,
+ struct object_id *oid)
{
unsigned char *p;
unsigned long size, c;
@@ -515,8 +517,8 @@ static void *unpack_raw_entry(struct object_entry *obj,
switch (obj->type) {
case OBJ_REF_DELTA:
- hashcpy(ref_sha1, fill(20));
- use(20);
+ hashcpy(ref_oid->hash, fill(the_hash_algo->rawsz));
+ use(the_hash_algo->rawsz);
break;
case OBJ_OFS_DELTA:
p = fill(1);
@@ -546,7 +548,7 @@ static void *unpack_raw_entry(struct object_entry *obj,
}
obj->hdr_size = consumed_bytes - obj->idx.offset;
- data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, sha1);
+ data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, oid);
obj->idx.crc32 = input_crc32;
return data;
}
@@ -671,18 +673,18 @@ static void find_ofs_delta_children(off_t offset,
*last_index = last;
}
-static int compare_ref_delta_bases(const unsigned char *sha1,
- const unsigned char *sha2,
+static int compare_ref_delta_bases(const struct object_id *oid1,
+ const struct object_id *oid2,
enum object_type type1,
enum object_type type2)
{
int cmp = type1 - type2;
if (cmp)
return cmp;
- return hashcmp(sha1, sha2);
+ return oidcmp(oid1, oid2);
}
-static int find_ref_delta(const unsigned char *sha1, enum object_type type)
+static int find_ref_delta(const struct object_id *oid, enum object_type type)
{
int first = 0, last = nr_ref_deltas;
@@ -691,7 +693,7 @@ static int find_ref_delta(const unsigned char *sha1, enum object_type type)
struct ref_delta_entry *delta = &ref_deltas[next];
int cmp;
- cmp = compare_ref_delta_bases(sha1, delta->sha1,
+ cmp = compare_ref_delta_bases(oid, &delta->oid,
type, objects[delta->obj_no].type);
if (!cmp)
return next;
@@ -704,11 +706,11 @@ static int find_ref_delta(const unsigned char *sha1, enum object_type type)
return -first-1;
}
-static void find_ref_delta_children(const unsigned char *sha1,
+static void find_ref_delta_children(const struct object_id *oid,
int *first_index, int *last_index,
enum object_type type)
{
- int first = find_ref_delta(sha1, type);
+ int first = find_ref_delta(oid, type);
int last = first;
int end = nr_ref_deltas - 1;
@@ -717,9 +719,9 @@ static void find_ref_delta_children(const unsigned char *sha1,
*last_index = -1;
return;
}
- while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1))
+ while (first > 0 && !oidcmp(&ref_deltas[first - 1].oid, oid))
--first;
- while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1))
+ while (last < end && !oidcmp(&ref_deltas[last + 1].oid, oid))
++last;
*first_index = first;
*last_index = last;
@@ -771,7 +773,7 @@ static int check_collison(struct object_entry *entry)
memset(&data, 0, sizeof(data));
data.entry = entry;
- data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL);
+ data.st = open_istream(&entry->idx.oid, &type, &size, NULL);
if (!data.st)
return -1;
if (size != entry->size || type != entry->type)
@@ -810,12 +812,12 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
enum object_type has_type;
unsigned long has_size;
read_lock();
- has_type = sha1_object_info(oid->hash, &has_size);
+ has_type = oid_object_info(the_repository, oid, &has_size);
if (has_type < 0)
die(_("cannot read existing object info %s"), oid_to_hex(oid));
if (has_type != type || has_size != size)
die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
- has_data = read_sha1_file(oid->hash, &has_type, &has_size);
+ has_data = read_object_file(oid, &has_type, &has_size);
read_unlock();
if (!data)
data = new_data = get_data_from_pack(obj_entry);
@@ -827,7 +829,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
free(has_data);
}
- if (strict) {
+ if (strict || do_fsck_object) {
read_lock();
if (type == OBJ_BLOB) {
struct blob *blob = lookup_blob(oid);
@@ -835,6 +837,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
blob->object.flags |= FLAG_CHECKED;
else
die(_("invalid blob object %s"), oid_to_hex(oid));
+ if (do_fsck_object &&
+ fsck_object(&blob->object, (void *)data, size, &fsck_options))
+ die(_("fsck error in packed object"));
} else {
struct object *obj;
int eaten;
@@ -849,11 +854,11 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
obj = parse_object_buffer(oid, type, size, buf,
&eaten);
if (!obj)
- die(_("invalid %s"), typename(type));
+ die(_("invalid %s"), type_name(type));
if (do_fsck_object &&
fsck_object(obj, buf, size, &fsck_options))
- die(_("Error in object"));
- if (fsck_walk(obj, NULL, &fsck_options))
+ die(_("fsck error in packed object"));
+ if (strict && fsck_walk(obj, NULL, &fsck_options))
die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
if (obj->type == OBJ_TREE) {
@@ -864,7 +869,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
if (obj->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *) obj;
if (detach_commit_buffer(commit, NULL) != data)
- die("BUG: parse_object_buffer transmogrified our buffer");
+ BUG("parse_object_buffer transmogrified our buffer");
}
obj->flags |= FLAG_CHECKED;
}
@@ -958,9 +963,8 @@ 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_sha1_file(result->data, result->size,
- typename(delta_obj->real_type),
- delta_obj->idx.oid.hash);
+ hash_object_file(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);
counter_lock();
@@ -992,7 +996,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base,
struct base_data *prev_base)
{
if (base->ref_last == -1 && base->ofs_last == -1) {
- find_ref_delta_children(base->obj->idx.oid.hash,
+ find_ref_delta_children(&base->obj->idx.oid,
&base->ref_first, &base->ref_last,
OBJ_REF_DELTA);
@@ -1014,7 +1018,7 @@ 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))
- die("BUG: child->real_type != OBJ_REF_DELTA");
+ BUG("child->real_type != OBJ_REF_DELTA");
resolve_delta(child, base, result);
if (base->ref_first == base->ref_last && base->ofs_last == -1)
@@ -1076,7 +1080,7 @@ static int compare_ref_delta_entry(const void *a, const void *b)
const struct ref_delta_entry *delta_a = a;
const struct ref_delta_entry *delta_b = b;
- return hashcmp(delta_a->sha1, delta_b->sha1);
+ return oidcmp(&delta_a->oid, &delta_b->oid);
}
static void resolve_base(struct object_entry *obj)
@@ -1119,11 +1123,11 @@ static void *threaded_second_pass(void *data)
* - calculate SHA1 of all non-delta objects;
* - remember base (SHA1 or offset) for all deltas.
*/
-static void parse_pack_objects(unsigned char *sha1)
+static void parse_pack_objects(unsigned char *hash)
{
int i, nr_delays = 0;
struct ofs_delta_entry *ofs_delta = ofs_deltas;
- unsigned char ref_delta_sha1[20];
+ struct object_id ref_delta_oid;
struct stat st;
if (verbose)
@@ -1133,8 +1137,8 @@ static void parse_pack_objects(unsigned char *sha1)
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
void *data = unpack_raw_entry(obj, &ofs_delta->offset,
- ref_delta_sha1,
- obj->idx.oid.hash);
+ &ref_delta_oid,
+ &obj->idx.oid);
obj->real_type = obj->type;
if (obj->type == OBJ_OFS_DELTA) {
nr_ofs_deltas++;
@@ -1142,7 +1146,7 @@ static void parse_pack_objects(unsigned char *sha1)
ofs_delta++;
} else if (obj->type == OBJ_REF_DELTA) {
ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
- hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_sha1);
+ oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid);
ref_deltas[nr_ref_deltas].obj_no = i;
nr_ref_deltas++;
} else if (!data) {
@@ -1160,10 +1164,10 @@ static void parse_pack_objects(unsigned char *sha1)
/* Check pack integrity */
flush();
- git_SHA1_Final(sha1, &input_ctx);
- if (hashcmp(fill(20), sha1))
+ the_hash_algo->final_fn(hash, &input_ctx);
+ if (hashcmp(fill(the_hash_algo->rawsz), hash))
die(_("pack is corrupted (SHA1 mismatch)"));
- use(20);
+ use(the_hash_algo->rawsz);
/* If input_fd is a file, we should have reached its end now. */
if (fstat(input_fd, &st))
@@ -1239,21 +1243,21 @@ static void resolve_deltas(void)
/*
* Third pass:
* - append objects to convert thin pack to full pack if required
- * - write the final 20-byte SHA-1
+ * - write the final pack hash
*/
-static void fix_unresolved_deltas(struct sha1file *f);
-static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1)
+static void fix_unresolved_deltas(struct hashfile *f);
+static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_hash)
{
if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) {
stop_progress(&progress);
- /* Flush remaining pack final 20-byte SHA1. */
+ /* Flush remaining pack final hash. */
flush();
return;
}
if (fix_thin_pack) {
- struct sha1file *f;
- unsigned char read_sha1[20], tail_sha1[20];
+ struct hashfile *f;
+ unsigned char read_hash[GIT_MAX_RAWSZ], tail_hash[GIT_MAX_RAWSZ];
struct strbuf msg = STRBUF_INIT;
int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas;
int nr_objects_initial = nr_objects;
@@ -1262,7 +1266,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
memset(objects + nr_objects + 1, 0,
nr_unresolved * sizeof(*objects));
- f = sha1fd(output_fd, curr_pack);
+ f = hashfd(output_fd, curr_pack);
fix_unresolved_deltas(f);
strbuf_addf(&msg, Q_("completed with %d local object",
"completed with %d local objects",
@@ -1270,12 +1274,12 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
nr_objects - nr_objects_initial);
stop_progress_msg(&progress, msg.buf);
strbuf_release(&msg);
- sha1close(f, tail_sha1, 0);
- hashcpy(read_sha1, pack_sha1);
- fixup_pack_header_footer(output_fd, pack_sha1,
+ finalize_hashfile(f, tail_hash, 0);
+ hashcpy(read_hash, pack_hash);
+ fixup_pack_header_footer(output_fd, pack_hash,
curr_pack, nr_objects,
- read_sha1, consumed_bytes-20);
- if (hashcmp(read_sha1, tail_sha1) != 0)
+ read_hash, consumed_bytes-the_hash_algo->rawsz);
+ if (hashcmp(read_hash, tail_hash) != 0)
die(_("Unexpected tail checksum for %s "
"(disk corruption?)"), curr_pack);
}
@@ -1286,7 +1290,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas);
}
-static int write_compressed(struct sha1file *f, void *in, unsigned int size)
+static int write_compressed(struct hashfile *f, void *in, unsigned int size)
{
git_zstream stream;
int status;
@@ -1300,7 +1304,7 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size)
stream.next_out = outbuf;
stream.avail_out = sizeof(outbuf);
status = git_deflate(&stream, Z_FINISH);
- sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
+ hashwrite(f, outbuf, sizeof(outbuf) - stream.avail_out);
} while (status == Z_OK);
if (status != Z_STREAM_END)
@@ -1310,7 +1314,7 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size)
return size;
}
-static struct object_entry *append_obj_to_pack(struct sha1file *f,
+static struct object_entry *append_obj_to_pack(struct hashfile *f,
const unsigned char *sha1, void *buf,
unsigned long size, enum object_type type)
{
@@ -1327,7 +1331,7 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f,
}
header[n++] = c;
crc32_begin(f);
- sha1write(f, header, n);
+ hashwrite(f, header, n);
obj[0].size = size;
obj[0].hdr_size = n;
obj[0].type = type;
@@ -1335,7 +1339,7 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f,
obj[1].idx.offset = obj[0].idx.offset + n;
obj[1].idx.offset += write_compressed(f, buf, size);
obj[0].idx.crc32 = crc32_end(f);
- sha1flush(f);
+ hashflush(f);
hashcpy(obj->idx.oid.hash, sha1);
return obj;
}
@@ -1347,7 +1351,7 @@ static int delta_pos_compare(const void *_a, const void *_b)
return a->obj_no - b->obj_no;
}
-static void fix_unresolved_deltas(struct sha1file *f)
+static void fix_unresolved_deltas(struct hashfile *f)
{
struct ref_delta_entry **sorted_by_pos;
int i;
@@ -1374,14 +1378,15 @@ static void fix_unresolved_deltas(struct sha1file *f)
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
continue;
- base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size);
+ base_obj->data = read_object_file(&d->oid, &type,
+ &base_obj->size);
if (!base_obj->data)
continue;
- if (check_sha1_signature(d->sha1, base_obj->data,
- base_obj->size, typename(type)))
- die(_("local object %s is corrupt"), sha1_to_hex(d->sha1));
- base_obj->obj = append_obj_to_pack(f, d->sha1,
+ if (check_object_signature(&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);
find_unresolved_deltas(base_obj);
display_progress(progress, nr_resolved_deltas);
@@ -1403,7 +1408,7 @@ static const char *derive_filename(const char *pack_name, const char *suffix,
}
static void write_special_file(const char *suffix, const char *msg,
- const char *pack_name, const unsigned char *sha1,
+ const char *pack_name, const unsigned char *hash,
const char **report)
{
struct strbuf name_buf = STRBUF_INIT;
@@ -1414,7 +1419,7 @@ static void write_special_file(const char *suffix, const char *msg,
if (pack_name)
filename = derive_filename(pack_name, suffix, &name_buf);
else
- filename = odb_pack_name(&name_buf, sha1, suffix);
+ filename = odb_pack_name(&name_buf, hash, suffix);
fd = odb_pack_keep(filename);
if (fd < 0) {
@@ -1438,7 +1443,7 @@ static void write_special_file(const char *suffix, const char *msg,
static void final(const char *final_pack_name, const char *curr_pack_name,
const char *final_index_name, const char *curr_index_name,
const char *keep_msg, const char *promisor_msg,
- unsigned char *sha1)
+ unsigned char *hash)
{
const char *report = "pack";
struct strbuf pack_name = STRBUF_INIT;
@@ -1455,15 +1460,15 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
}
if (keep_msg)
- write_special_file("keep", keep_msg, final_pack_name, sha1,
+ write_special_file("keep", keep_msg, final_pack_name, hash,
&report);
if (promisor_msg)
write_special_file("promisor", promisor_msg, final_pack_name,
- sha1, NULL);
+ hash, NULL);
if (final_pack_name != curr_pack_name) {
if (!final_pack_name)
- final_pack_name = odb_pack_name(&pack_name, sha1, "pack");
+ final_pack_name = odb_pack_name(&pack_name, hash, "pack");
if (finalize_object_file(curr_pack_name, final_pack_name))
die(_("cannot store pack file"));
} else if (from_stdin)
@@ -1471,18 +1476,25 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
if (final_index_name != curr_index_name) {
if (!final_index_name)
- final_index_name = odb_pack_name(&index_name, sha1, "idx");
+ final_index_name = odb_pack_name(&index_name, hash, "idx");
if (finalize_object_file(curr_index_name, final_index_name))
die(_("cannot store index file"));
} else
chmod(final_index_name, 0444);
+ if (do_fsck_object) {
+ struct packed_git *p;
+ p = add_packed_git(final_index_name, strlen(final_index_name), 0);
+ if (p)
+ install_packed_git(the_repository, p);
+ }
+
if (!from_stdin) {
- printf("%s\n", sha1_to_hex(sha1));
+ printf("%s\n", sha1_to_hex(hash));
} else {
struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(sha1));
+ strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(hash));
write_or_die(1, buf.buf, buf.len);
strbuf_release(&buf);
@@ -1541,12 +1553,13 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
{
const uint32_t *idx1, *idx2;
uint32_t i;
+ const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t);
/* The address of the 4-byte offset table */
idx1 = (((const uint32_t *)p->index_data)
+ 2 /* 8-byte header */
+ 256 /* fan out */
- + 5 * p->num_objects /* 20-byte SHA-1 table */
+ + hashwords * p->num_objects /* object ID table */
+ p->num_objects /* CRC32 table */
);
@@ -1591,7 +1604,7 @@ static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
/*
* Get rid of the idx file as we do not need it anymore.
* NEEDSWORK: extract this bit from free_pack_by_name() in
- * sha1_file.c, perhaps? It shouldn't matter very much as we
+ * sha1-file.c, perhaps? It shouldn't matter very much as we
* know we haven't installed this pack (hence we never have
* read anything from it).
*/
@@ -1616,7 +1629,7 @@ static void show_pack_info(int stat_only)
continue;
printf("%s %-6s %lu %lu %"PRIuMAX,
oid_to_hex(&obj->idx.oid),
- typename(obj->real_type), obj->size,
+ type_name(obj->real_type), obj->size,
(unsigned long)(obj[1].idx.offset - obj->idx.offset),
(uintmax_t)obj->idx.offset);
if (is_delta_type(obj->type)) {
@@ -1653,7 +1666,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
struct strbuf index_name_buf = STRBUF_INIT;
struct pack_idx_entry **idx_objects;
struct pack_idx_option opts;
- unsigned char pack_sha1[20];
+ unsigned char pack_hash[GIT_MAX_RAWSZ];
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
int report_end_of_input = 0;
@@ -1682,16 +1695,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
from_stdin = 1;
} else if (!strcmp(arg, "--fix-thin")) {
fix_thin_pack = 1;
- } else if (!strcmp(arg, "--strict")) {
- strict = 1;
- do_fsck_object = 1;
- } else if (skip_prefix(arg, "--strict=", &arg)) {
+ } else if (skip_to_optional_arg(arg, "--strict", &arg)) {
strict = 1;
do_fsck_object = 1;
fsck_set_msg_types(&fsck_options, arg);
} else if (!strcmp(arg, "--check-self-contained-and-connected")) {
strict = 1;
check_self_contained_and_connected = 1;
+ } else if (!strcmp(arg, "--fsck-objects")) {
+ do_fsck_object = 1;
} else if (!strcmp(arg, "--verify")) {
verify = 1;
} else if (!strcmp(arg, "--verify-stat")) {
@@ -1701,14 +1713,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
verify = 1;
show_stat = 1;
stat_only = 1;
- } else if (!strcmp(arg, "--keep")) {
- keep_msg = "";
- } else if (starts_with(arg, "--keep=")) {
- keep_msg = arg + 7;
- } else if (!strcmp(arg, "--promisor")) {
- promisor_msg = "";
- } else if (starts_with(arg, "--promisor=")) {
- promisor_msg = arg + strlen("--promisor=");
+ } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
+ ; /* nothing to do */
+ } else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) {
+ ; /* already parsed */
} else if (starts_with(arg, "--threads=")) {
char *end;
nr_threads = strtoul(arg+10, &end, 0);
@@ -1797,11 +1805,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (show_stat)
obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat));
ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
- parse_pack_objects(pack_sha1);
+ parse_pack_objects(pack_hash);
if (report_end_of_input)
write_in_full(2, "\0", 1);
resolve_deltas();
- conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
+ conclude_pack(fix_thin_pack, curr_pack, pack_hash);
free(ofs_deltas);
free(ref_deltas);
if (strict)
@@ -1813,16 +1821,20 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
ALLOC_ARRAY(idx_objects, nr_objects);
for (i = 0; i < nr_objects; i++)
idx_objects[i] = &objects[i].idx;
- curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_sha1);
+ curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash);
free(idx_objects);
if (!verify)
final(pack_name, curr_pack,
index_name, curr_index,
keep_msg, promisor_msg,
- pack_sha1);
+ pack_hash);
else
close(input_fd);
+
+ if (do_fsck_object && fsck_finish(&fsck_options))
+ die(_("fsck error in pack objects"));
+
free(objects);
strbuf_release(&index_name_buf);
if (pack_name == NULL)
diff --git a/builtin/init-db.c b/builtin/init-db.c
index c9b7946bad..4ecf909368 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -7,7 +7,7 @@
#include "config.h"
#include "refs.h"
#include "builtin.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "parse-options.h"
#ifndef DEFAULT_GIT_TEMPLATE_DIR
@@ -24,11 +24,11 @@ static int init_is_bare_repository = 0;
static int init_shared_repository = -1;
static const char *init_db_template_dir;
-static void copy_templates_1(struct strbuf *path, struct strbuf *template,
+static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
DIR *dir)
{
size_t path_baselen = path->len;
- size_t template_baselen = template->len;
+ size_t template_baselen = template_path->len;
struct dirent *de;
/* Note: if ".git/hooks" file exists in the repository being
@@ -44,12 +44,12 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template,
int exists = 0;
strbuf_setlen(path, path_baselen);
- strbuf_setlen(template, template_baselen);
+ strbuf_setlen(template_path, template_baselen);
if (de->d_name[0] == '.')
continue;
strbuf_addstr(path, de->d_name);
- strbuf_addstr(template, de->d_name);
+ strbuf_addstr(template_path, de->d_name);
if (lstat(path->buf, &st_git)) {
if (errno != ENOENT)
die_errno(_("cannot stat '%s'"), path->buf);
@@ -57,36 +57,36 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template,
else
exists = 1;
- if (lstat(template->buf, &st_template))
- die_errno(_("cannot stat template '%s'"), template->buf);
+ if (lstat(template_path->buf, &st_template))
+ die_errno(_("cannot stat template '%s'"), template_path->buf);
if (S_ISDIR(st_template.st_mode)) {
- DIR *subdir = opendir(template->buf);
+ DIR *subdir = opendir(template_path->buf);
if (!subdir)
- die_errno(_("cannot opendir '%s'"), template->buf);
+ die_errno(_("cannot opendir '%s'"), template_path->buf);
strbuf_addch(path, '/');
- strbuf_addch(template, '/');
- copy_templates_1(path, template, subdir);
+ strbuf_addch(template_path, '/');
+ copy_templates_1(path, template_path, subdir);
closedir(subdir);
}
else if (exists)
continue;
else if (S_ISLNK(st_template.st_mode)) {
struct strbuf lnk = STRBUF_INIT;
- if (strbuf_readlink(&lnk, template->buf, 0) < 0)
- die_errno(_("cannot readlink '%s'"), template->buf);
+ if (strbuf_readlink(&lnk, template_path->buf, 0) < 0)
+ die_errno(_("cannot readlink '%s'"), template_path->buf);
if (symlink(lnk.buf, path->buf))
die_errno(_("cannot symlink '%s' '%s'"),
lnk.buf, path->buf);
strbuf_release(&lnk);
}
else if (S_ISREG(st_template.st_mode)) {
- if (copy_file(path->buf, template->buf, st_template.st_mode))
+ if (copy_file(path->buf, template_path->buf, st_template.st_mode))
die_errno(_("cannot copy '%s' to '%s'"),
- template->buf, path->buf);
+ template_path->buf, path->buf);
}
else
- error(_("ignoring template %s"), template->buf);
+ error(_("ignoring template %s"), template_path->buf);
}
}
@@ -117,7 +117,7 @@ static void copy_templates(const char *template_dir)
dir = opendir(template_path.buf);
if (!dir) {
- warning(_("templates not found %s"), template_dir);
+ warning(_("templates not found in %s"), template_dir);
goto free_return;
}
@@ -391,7 +391,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
else if (get_shared_repository() == PERM_EVERYBODY)
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
else
- die("BUG: invalid value for shared_repository");
+ BUG("invalid value for shared_repository");
git_config_set("core.sharedrepository", buf);
git_config_set("receive.denyNonFastforwards", "true");
}
diff --git a/builtin/log.c b/builtin/log.c
index d81a09051e..0583f7f383 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -28,6 +28,9 @@
#include "mailmap.h"
#include "gpg-interface.h"
#include "progress.h"
+#include "commit-slab.h"
+
+#define MAIL_DEFAULT_WRAP 72
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -121,20 +124,19 @@ static void cmd_log_init_defaults(struct rev_info *rev)
if (fmt_pretty)
get_commit_format(fmt_pretty, rev);
if (default_follow)
- DIFF_OPT_SET(&rev->diffopt, DEFAULT_FOLLOW_RENAMES);
+ rev->diffopt.flags.default_follow_renames = 1;
rev->verbose_header = 1;
- DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+ rev->diffopt.flags.recursive = 1;
rev->diffopt.stat_width = -1; /* use full terminal width */
rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
rev->abbrev_commit = default_abbrev_commit;
rev->show_root_diff = default_show_root;
rev->subject_prefix = fmt_patch_subject_prefix;
rev->show_signature = default_show_signature;
- DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
+ rev->diffopt.flags.allow_textconv = 1;
if (default_date_mode)
parse_date_format(default_date_mode, &rev->date_mode);
- rev->diffopt.touched_flags = 0;
}
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
@@ -143,11 +145,20 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
struct userformat_want w;
int quiet = 0, source = 0, mailmap = 0;
static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
+ static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+ static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+ struct decoration_filter decoration_filter = {&decorate_refs_include,
+ &decorate_refs_exclude};
+ static struct revision_sources revision_sources;
const struct option builtin_log_options[] = {
OPT__QUIET(&quiet, N_("suppress diff output")),
OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
+ OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
+ N_("pattern"), N_("only decorate refs that match <pattern>")),
+ OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
+ N_("pattern"), N_("do not decorate refs that match <pattern>")),
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback},
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
@@ -181,12 +192,14 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (rev->show_notes)
init_display_notes(&rev->notes_opt);
- if (rev->diffopt.pickaxe || rev->diffopt.filter ||
- DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
+ if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
+ rev->diffopt.filter || rev->diffopt.flags.follow_renames)
rev->always_show_header = 0;
- if (source)
- rev->show_source = 1;
+ if (source) {
+ init_revision_sources(&revision_sources);
+ rev->sources = &revision_sources;
+ }
if (mailmap) {
rev->mailmap = xcalloc(1, sizeof(struct string_list));
@@ -206,7 +219,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (decoration_style) {
rev->show_decorations = 1;
- load_ref_decorations(decoration_style);
+ load_ref_decorations(&decoration_filter, decoration_style);
}
if (rev->line_level_traverse)
@@ -392,7 +405,7 @@ static int cmd_log_walk(struct rev_info *rev)
fclose(rev->diffopt.file);
if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
- DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+ rev->diffopt.flags.check_failed) {
return 02;
}
return diff_result_code(&rev->diffopt, 0);
@@ -484,8 +497,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
unsigned long size;
fflush(rev->diffopt.file);
- if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
- !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
+ if (!rev->diffopt.flags.textconv_set_via_cmdline ||
+ !rev->diffopt.flags.allow_textconv)
return stream_blob_to_fd(1, oid, NULL, 0);
if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
@@ -509,7 +522,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
{
unsigned long size;
enum object_type type;
- char *buf = read_sha1_file(oid->hash, &type, &size);
+ char *buf = read_object_file(oid, &type, &size);
int offset = 0;
if (!buf)
@@ -532,7 +545,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
return 0;
}
-static int show_tree_object(const unsigned char *sha1,
+static int show_tree_object(const struct object_id *oid,
struct strbuf *base,
const char *pathname, unsigned mode, int stage, void *context)
{
@@ -667,9 +680,9 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
static void log_setup_revisions_tweak(struct rev_info *rev,
struct setup_revision_opt *opt)
{
- if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
+ if (rev->diffopt.flags.default_follow_renames &&
rev->prune_data.nr == 1)
- DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);
+ rev->diffopt.flags.follow_renames = 1;
/* Turn --cc/-c into -p --cc/-c when -p was not given */
if (!rev->diffopt.output_format && rev->combine_merges)
@@ -975,7 +988,7 @@ static char *find_branch_name(struct rev_info *rev)
return NULL;
ref = rev->cmdline.rev[positive].name;
tip_oid = &rev->cmdline.rev[positive].item->oid;
- if (dwim_ref(ref, strlen(ref), branch_oid.hash, &full_ref) &&
+ if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
skip_prefix(full_ref, "refs/heads/", &v) &&
!oidcmp(tip_oid, &branch_oid))
branch = xstrdup(v);
@@ -1010,7 +1023,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
return;
- log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte);
+ log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
for (i = 0; !need_8bit_cte && i < nr; i++) {
const char *buf = get_commit_buffer(list[i], NULL);
@@ -1037,7 +1050,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
shortlog_init(&log);
log.wrap_lines = 1;
- log.wrap = 72;
+ log.wrap = MAIL_DEFAULT_WRAP;
log.in1 = 2;
log.in2 = 4;
log.file = rev->diffopt.file;
@@ -1054,11 +1067,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
memcpy(&opts, &rev->diffopt, sizeof(opts));
opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+ opts.stat_width = MAIL_DEFAULT_WRAP;
diff_setup_done(&opts);
- diff_tree_oid(&origin->tree->object.oid,
- &head->tree->object.oid,
+ diff_tree_oid(get_commit_tree_oid(origin),
+ get_commit_tree_oid(head),
"", &opts);
diffcore_std(&opts);
diff_flush(&opts);
@@ -1327,6 +1341,8 @@ static struct commit *get_base_commit(const char *base_commit,
return base;
}
+define_commit_slab(commit_base, int);
+
static void prepare_bases(struct base_tree_info *bases,
struct commit *base,
struct commit **list,
@@ -1335,13 +1351,15 @@ static void prepare_bases(struct base_tree_info *bases,
struct commit *commit;
struct rev_info revs;
struct diff_options diffopt;
+ struct commit_base commit_base;
int i;
if (!base)
return;
+ init_commit_base(&commit_base);
diff_setup(&diffopt);
- DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diffopt.flags.recursive = 1;
diff_setup_done(&diffopt);
oidcpy(&bases->base_commit, &base->object.oid);
@@ -1352,7 +1370,7 @@ static void prepare_bases(struct base_tree_info *bases,
for (i = 0; i < total; i++) {
list[i]->object.flags &= ~UNINTERESTING;
add_pending_object(&revs, &list[i]->object, "rev_list");
- list[i]->util = (void *)1;
+ *commit_base_at(&commit_base, list[i]) = 1;
}
base->object.flags |= UNINTERESTING;
add_pending_object(&revs, &base->object, "base");
@@ -1366,7 +1384,7 @@ static void prepare_bases(struct base_tree_info *bases,
while ((commit = get_revision(&revs)) != NULL) {
struct object_id oid;
struct object_id *patch_id;
- if (commit->util)
+ if (*commit_base_at(&commit_base, commit))
continue;
if (commit_patch_id(commit, &diffopt, &oid, 0))
die(_("cannot get patch id"));
@@ -1375,6 +1393,7 @@ static void prepare_bases(struct base_tree_info *bases,
oidcpy(patch_id, &oid);
bases->nr_patch_id++;
}
+ clear_commit_base(&commit_base);
}
static void print_bases(struct base_tree_info *bases, FILE *file)
@@ -1464,9 +1483,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("output all-zero hash in From header")),
OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
N_("don't include a patch matching a commit upstream")),
- { OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL,
- N_("show patch format instead of default (patch + stat)"),
- PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1},
+ OPT_SET_INT_F('p', "no-stat", &use_patch_format,
+ N_("show patch format instead of default (patch + stat)"),
+ 1, PARSE_OPT_NONEG),
OPT_GROUP(N_("Messaging")),
{ OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
N_("add email header"), 0, header_callback },
@@ -1512,7 +1531,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.verbose_header = 1;
rev.diff = 1;
rev.max_parents = 1;
- DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+ rev.diffopt.flags.recursive = 1;
rev.subject_prefix = fmt_patch_subject_prefix;
memset(&s_r_opt, 0, sizeof(s_r_opt));
s_r_opt.def = "HEAD";
@@ -1607,14 +1626,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
(!rev.diffopt.output_format ||
rev.diffopt.output_format == DIFF_FORMAT_PATCH))
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY;
+ if (!rev.diffopt.stat_width)
+ rev.diffopt.stat_width = MAIL_DEFAULT_WRAP;
/* Always generate a patch */
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
rev.zero_commit = zero_commit;
- if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
- DIFF_OPT_SET(&rev.diffopt, BINARY);
+ if (!rev.diffopt.flags.text && !no_binary_diff)
+ rev.diffopt.flags.binary = 1;
if (rev.show_notes)
init_display_notes(&rev.notes_opt);
@@ -1861,12 +1882,12 @@ static void print_commit(char sign, struct commit *commit, int verbose,
{
if (!verbose) {
fprintf(file, "%c %s\n", sign,
- find_unique_abbrev(commit->object.oid.hash, abbrev));
+ find_unique_abbrev(&commit->object.oid, abbrev));
} else {
struct strbuf buf = STRBUF_INIT;
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
fprintf(file, "%c %s %s\n", sign,
- find_unique_abbrev(commit->object.oid.hash, abbrev),
+ find_unique_abbrev(&commit->object.oid, abbrev),
buf.buf);
strbuf_release(&buf);
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 8c713c47ac..88bb2019ad 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -31,6 +31,7 @@ static int show_resolve_undo;
static int show_modified;
static int show_killed;
static int show_valid_bit;
+static int show_fsmonitor_bit;
static int line_terminator = '\n';
static int debug_mode;
static int show_eol;
@@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag)
{
static char alttag[4];
- if (tag && *tag && show_valid_bit && (ce->ce_flags & CE_VALID)) {
+ if (tag && *tag && ((show_valid_bit && (ce->ce_flags & CE_VALID)) ||
+ (show_fsmonitor_bit && (ce->ce_flags & CE_FSMONITOR_VALID)))) {
memcpy(alttag, tag, 3);
if (isalpha(tag[0])) {
@@ -164,7 +166,7 @@ static void show_killed_files(const struct index_state *istate,
*/
pos = index_name_pos(istate, ent->name, ent->len);
if (0 <= pos)
- die("BUG: killed-file %.*s not found",
+ BUG("killed-file %.*s not found",
ent->len, ent->name);
pos = -pos - 1;
while (pos < istate->cache_nr &&
@@ -238,7 +240,7 @@ static void show_ce(struct repository *repo, struct dir_struct *dir,
printf("%s%06o %s %d\t",
tag,
ce->ce_mode,
- find_unique_abbrev(ce->oid.hash, abbrev),
+ find_unique_abbrev(&ce->oid, abbrev),
ce_stage(ce));
}
write_eolinfo(repo->index, ce, fullname);
@@ -269,7 +271,7 @@ static void show_ru_info(const struct index_state *istate)
if (!ui->mode[i])
continue;
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
- find_unique_abbrev(ui->sha1[i], abbrev),
+ find_unique_abbrev(&ui->oid[i], abbrev),
i + 1);
write_name(path);
}
@@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("identify the file status with tags")),
OPT_BOOL('v', NULL, &show_valid_bit,
N_("use lowercase letters for 'assume unchanged' files")),
+ OPT_BOOL('f', NULL, &show_fsmonitor_bit,
+ N_("use lowercase letters for 'fsmonitor clean' files")),
OPT_BOOL('c', "cached", &show_cached,
N_("show cached files in the output (default)")),
OPT_BOOL('d', "deleted", &show_deleted,
@@ -552,9 +556,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
N_("add the standard git exclusions"),
PARSE_OPT_NOARG, option_parse_exclude_standard },
- { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
- N_("make the output relative to the project top directory"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
+ OPT_SET_INT_F(0, "full-name", &prefix_len,
+ N_("make the output relative to the project top directory"),
+ 0, PARSE_OPT_NONEG),
OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
N_("recurse through submodules")),
OPT_BOOL(0, "error-unmatch", &error_unmatch,
@@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
for (i = 0; i < exclude_list.nr; i++) {
add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
}
- if (show_tag || show_valid_bit) {
+ if (show_tag || show_valid_bit || show_fsmonitor_bit) {
tag_cached = "H ";
tag_unmerged = "M ";
tag_removed = "R ";
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index c4be98ab9e..1a25df7ee1 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -1,7 +1,9 @@
#include "builtin.h"
#include "cache.h"
#include "transport.h"
+#include "ref-filter.h"
#include "remote.h"
+#include "refs.h"
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
@@ -43,10 +45,15 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
int show_symref_target = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
+ struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ int i;
+ struct string_list server_options = STRING_LIST_INIT_DUP;
struct remote *remote;
struct transport *transport;
const struct ref *ref;
+ struct ref_array ref_array;
+ static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print remote URL")),
@@ -60,13 +67,19 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
OPT_BOOL(0, "get-url", &get_url,
N_("take url.<base>.insteadOf into account")),
- OPT_SET_INT(0, "exit-code", &status,
- N_("exit with exit code 2 if no matching refs are found"), 2),
+ OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
+ N_("field name to sort on"), &parse_opt_ref_sorting),
+ OPT_SET_INT_F(0, "exit-code", &status,
+ N_("exit with exit code 2 if no matching refs are found"),
+ 2, PARSE_OPT_NOCOMPLETE),
OPT_BOOL(0, "symref", &show_symref_target,
N_("show underlying ref in addition to the object pointed by it")),
+ OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
OPT_END()
};
+ memset(&ref_array, 0, sizeof(ref_array));
+
argc = parse_options(argc, argv, prefix, options, ls_remote_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
dest = argv[0];
@@ -74,8 +87,17 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (argc > 1) {
int i;
pattern = xcalloc(argc, sizeof(const char *));
- for (i = 1; i < argc; i++)
+ for (i = 1; i < argc; i++) {
+ const char *glob;
pattern[i - 1] = xstrfmt("*/%s", argv[i]);
+
+ glob = strchr(argv[i], '*');
+ if (glob)
+ argv_array_pushf(&ref_prefixes, "%.*s",
+ (int)(glob - argv[i]), argv[i]);
+ else
+ expand_ref_prefix(&ref_prefixes, argv[i]);
+ }
}
remote = remote_get(dest);
@@ -89,28 +111,46 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (get_url) {
printf("%s\n", *remote->url);
+ UNLEAK(sorting);
return 0;
}
transport = transport_get(remote, NULL);
if (uploadpack != NULL)
transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
+ if (server_options.nr)
+ transport->server_options = &server_options;
- ref = transport_get_remote_refs(transport);
- if (transport_disconnect(transport))
+ ref = transport_get_remote_refs(transport, &ref_prefixes);
+ if (transport_disconnect(transport)) {
+ UNLEAK(sorting);
return 1;
+ }
if (!dest && !quiet)
fprintf(stderr, "From %s\n", *remote->url);
for ( ; ref; ref = ref->next) {
+ struct ref_array_item *item;
if (!check_ref_type(ref, flags))
continue;
if (!tail_match(pattern, ref->name))
continue;
+ item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+ item->symref = xstrdup_or_null(ref->symref);
+ }
+
+ if (sorting)
+ ref_array_sort(sorting, &ref_array);
+
+ for (i = 0; i < ref_array.nr; i++) {
+ const struct ref_array_item *ref = ref_array.items[i];
if (show_symref_target && ref->symref)
- printf("ref: %s\t%s\n", ref->symref, ref->name);
- printf("%s\t%s\n", oid_to_hex(&ref->old_oid), ref->name);
+ printf("ref: %s\t%s\n", ref->symref, ref->refname);
+ printf("%s\t%s\n", oid_to_hex(&ref->objectname), ref->refname);
status = 0; /* we found something */
}
+
+ UNLEAK(sorting);
+ UNLEAK(ref_array);
return status;
}
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index ef965408e8..409da4e835 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -60,7 +60,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
return 0;
}
-static int show_tree(const unsigned char *sha1, struct strbuf *base,
+static int show_tree(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode, int stage, void *context)
{
int retval = 0;
@@ -94,7 +94,7 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,
char size_text[24];
if (!strcmp(type, blob_type)) {
unsigned long size;
- if (sha1_object_info(sha1, &size) == OBJ_BAD)
+ if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
xsnprintf(size_text, sizeof(size_text),
"BAD");
else
@@ -103,11 +103,11 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,
} else
xsnprintf(size_text, sizeof(size_text), "-");
printf("%06o %s %s %7s\t", mode, type,
- find_unique_abbrev(sha1, abbrev),
+ find_unique_abbrev(oid, abbrev),
size_text);
} else
printf("%06o %s %s\t", mode, type,
- find_unique_abbrev(sha1, abbrev));
+ find_unique_abbrev(oid, abbrev));
}
baselen = base->len;
strbuf_addstr(base, pathname);
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 6dbd167d3b..3b7600150b 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -9,20 +9,20 @@
static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
{
- struct commit_list *result;
+ struct commit_list *result, *r;
result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
if (!result)
return 1;
- while (result) {
- printf("%s\n", oid_to_hex(&result->item->object.oid));
+ for (r = result; r; r = r->next) {
+ printf("%s\n", oid_to_hex(&r->item->object.oid));
if (!show_all)
- return 0;
- result = result->next;
+ break;
}
+ free_commit_list(result);
return 0;
}
@@ -51,45 +51,47 @@ static struct commit *get_commit_reference(const char *arg)
static int handle_independent(int count, const char **args)
{
- struct commit_list *revs = NULL;
- struct commit_list *result;
+ struct commit_list *revs = NULL, *rev;
int i;
for (i = count - 1; i >= 0; i--)
commit_list_insert(get_commit_reference(args[i]), &revs);
- result = reduce_heads(revs);
- if (!result)
+ reduce_heads_replace(&revs);
+
+ if (!revs)
return 1;
- while (result) {
- printf("%s\n", oid_to_hex(&result->item->object.oid));
- result = result->next;
- }
+ for (rev = revs; rev; rev = rev->next)
+ printf("%s\n", oid_to_hex(&rev->item->object.oid));
+
+ free_commit_list(revs);
return 0;
}
static int handle_octopus(int count, const char **args, int show_all)
{
struct commit_list *revs = NULL;
- struct commit_list *result;
+ struct commit_list *result, *rev;
int i;
for (i = count - 1; i >= 0; i--)
commit_list_insert(get_commit_reference(args[i]), &revs);
- result = reduce_heads(get_octopus_merge_bases(revs));
+ result = get_octopus_merge_bases(revs);
+ free_commit_list(revs);
+ reduce_heads_replace(&result);
if (!result)
return 1;
- while (result) {
- printf("%s\n", oid_to_hex(&result->item->object.oid));
+ for (rev = result; rev; rev = rev->next) {
+ printf("%s\n", oid_to_hex(&rev->item->object.oid));
if (!show_all)
- return 0;
- result = result->next;
+ break;
}
+ free_commit_list(result);
return 0;
}
@@ -156,7 +158,7 @@ static int handle_fork_point(int argc, const char **argv)
struct commit_list *bases;
int i, ret = 0;
- switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) {
+ switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
case 0:
die("No such ref: '%s'", argv[0]);
case 1:
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
index 684411694f..c84c6e05e9 100644
--- a/builtin/merge-ours.c
+++ b/builtin/merge-ours.c
@@ -9,26 +9,24 @@
*/
#include "git-compat-util.h"
#include "builtin.h"
+#include "diff.h"
static const char builtin_merge_ours_usage[] =
"git merge-ours <base>... -- HEAD <remote>...";
-static const char *diff_index_args[] = {
- "diff-index", "--quiet", "--cached", "HEAD", "--", NULL
-};
-#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
-
int cmd_merge_ours(int argc, const char **argv, const char *prefix)
{
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_merge_ours_usage);
/*
- * We need to exit with 2 if the index does not match our HEAD tree,
- * because the current index is what we will be committing as the
- * merge result.
+ * The contents of the current index becomes the tree we
+ * commit. The index must match HEAD, or this merge cannot go
+ * through.
*/
- if (cmd_diff_index(NARGS, diff_index_args, prefix))
+ if (read_cache() < 0)
+ die_errno("read_cache failed");
+ if (index_differs_from("HEAD", NULL, 0))
exit(2);
exit(0);
}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index d01ddecf66..bf01e05808 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -2,7 +2,7 @@
#include "tree-walk.h"
#include "xdiff-interface.h"
#include "blob.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "merge-blobs.h"
static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
@@ -60,7 +60,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
const char *path = entry->path;
if (!entry->stage)
- return read_sha1_file(entry->blob->object.oid.hash, &type, size);
+ return read_object_file(&entry->blob->object.oid, &type, size);
base = NULL;
if (entry->stage == 1) {
base = entry->blob;
@@ -82,7 +82,8 @@ static void *origin(struct merge_list *entry, unsigned long *size)
enum object_type type;
while (entry) {
if (entry->stage == 2)
- return read_sha1_file(entry->blob->object.oid.hash, &type, size);
+ return read_object_file(&entry->blob->object.oid,
+ &type, size);
entry = entry->link;
}
return NULL;
diff --git a/builtin/merge.c b/builtin/merge.c
index ab5ffe85e8..4a4c09496c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -14,6 +14,7 @@
#include "run-command.h"
#include "diff.h"
#include "refs.h"
+#include "refspec.h"
#include "commit.h"
#include "diffcore.h"
#include "revision.h"
@@ -33,6 +34,8 @@
#include "sequencer.h"
#include "string-list.h"
#include "packfile.h"
+#include "tag.h"
+#include "alias.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -212,9 +215,9 @@ static struct option builtin_merge_options[] = {
OPT_BOOL('e', "edit", &option_edit,
N_("edit message before committing")),
OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
- { OPTION_SET_INT, 0, "ff-only", &fast_forward, NULL,
- N_("abort if fast-forward is not possible"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY },
+ OPT_SET_INT_F(0, "ff-only", &fast_forward,
+ N_("abort if fast-forward is not possible"),
+ FF_ONLY, PARSE_OPT_NONEG),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
OPT_BOOL(0, "verify-signatures", &verify_signatures,
N_("verify that the named commit has a valid GPG signature")),
@@ -279,7 +282,7 @@ out:
return rc;
}
-static void read_empty(unsigned const char *sha1, int verbose)
+static void read_empty(const struct object_id *oid, int verbose)
{
int i = 0;
const char *args[7];
@@ -289,15 +292,15 @@ static void read_empty(unsigned const char *sha1, int verbose)
args[i++] = "-v";
args[i++] = "-m";
args[i++] = "-u";
- args[i++] = EMPTY_TREE_SHA1_HEX;
- args[i++] = sha1_to_hex(sha1);
+ args[i++] = empty_tree_oid_hex();
+ args[i++] = oid_to_hex(oid);
args[i] = NULL;
if (run_command_v_opt(args, RUN_GIT_CMD))
die(_("read-tree failed"));
}
-static void reset_hard(unsigned const char *sha1, int verbose)
+static void reset_hard(const struct object_id *oid, int verbose)
{
int i = 0;
const char *args[6];
@@ -307,7 +310,7 @@ static void reset_hard(unsigned const char *sha1, int verbose)
args[i++] = "-v";
args[i++] = "--reset";
args[i++] = "-u";
- args[i++] = sha1_to_hex(sha1);
+ args[i++] = oid_to_hex(oid);
args[i] = NULL;
if (run_command_v_opt(args, RUN_GIT_CMD))
@@ -323,7 +326,7 @@ static void restore_state(const struct object_id *head,
if (is_null_oid(stash))
return;
- reset_hard(head->hash, 1);
+ reset_hard(head, 1);
args[2] = oid_to_hex(stash);
@@ -405,14 +408,13 @@ static void finish(struct commit *head_commit,
printf(_("No merge message -- not updating HEAD\n"));
else {
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
- update_ref(reflog_message.buf, "HEAD",
- new_head->hash, head->hash, 0,
- UPDATE_REFS_DIE_ON_ERR);
+ update_ref(reflog_message.buf, "HEAD", new_head, head,
+ 0, UPDATE_REFS_DIE_ON_ERR);
/*
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
- close_all_packs();
+ close_all_packs(the_repository->objects);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
@@ -443,6 +445,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
struct object_id branch_head;
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
+ struct merge_remote_desc *desc;
const char *ptr;
char *found_ref;
int len, early;
@@ -455,7 +458,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
- if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) {
+ if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref) > 0) {
if (starts_with(found_ref, "refs/heads/")) {
strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
oid_to_hex(&branch_head), remote);
@@ -515,16 +518,13 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_release(&truname);
}
- if (remote_head->util) {
- struct merge_remote_desc *desc;
- desc = merge_remote_util(remote_head);
- if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
- strbuf_addf(msg, "%s\t\t%s '%s'\n",
- oid_to_hex(&desc->obj->oid),
- typename(desc->obj->type),
- remote);
- goto cleanup;
- }
+ desc = merge_remote_util(remote_head);
+ if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
+ strbuf_addf(msg, "%s\t\t%s '%s'\n",
+ oid_to_hex(&desc->obj->oid),
+ type_name(desc->obj->type),
+ remote);
+ goto cleanup;
}
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
@@ -568,6 +568,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
show_diffstat = git_config_bool(k, v);
+ else if (!strcmp(k, "merge.verifysignatures"))
+ verify_signatures = git_config_bool(k, v);
else if (!strcmp(k, "pull.twohead"))
return git_config_string(&pull_twohead, k, v);
else if (!strcmp(k, "pull.octopus"))
@@ -637,7 +639,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
static void write_tree_trivial(struct object_id *oid)
{
- if (write_cache_as_tree(oid->hash, 0, NULL))
+ if (write_cache_as_tree(oid, 0, NULL))
die(_("git write-tree failed to write a tree"));
}
@@ -645,15 +647,14 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
struct commit *head)
{
- static struct lock_file lock;
+ struct lock_file lock = LOCK_INIT;
const char *head_arg = "HEAD";
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
- if (active_cache_changed &&
- write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
return error(_("Unable to write index."));
- rollback_lock_file(&lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
int clean, x;
@@ -690,10 +691,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
remoteheads->item, reversed, &result);
if (clean < 0)
exit(128);
- if (active_cache_changed &&
- write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
die (_("unable to write %s"), get_index_file());
- rollback_lock_file(&lock);
return clean ? 0 : 1;
} else {
return try_merge_command(strategy, xopts_nr, xopts,
@@ -805,22 +805,21 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
struct object_id result_tree, result_commit;
struct commit_list *parents, **pptr = &parents;
- static struct lock_file lock;
+ struct lock_file lock = LOCK_INIT;
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
- if (active_cache_changed &&
- write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
return error(_("Unable to write index."));
- rollback_lock_file(&lock);
write_tree_trivial(&result_tree);
printf(_("Wonderful.\n"));
pptr = commit_list_append(head, pptr);
pptr = commit_list_append(remoteheads->item, pptr);
prepare_to_commit(remoteheads);
- if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents,
- result_commit.hash, NULL, sign_commit))
+ if (commit_tree(merge_msg.buf, merge_msg.len, &result_tree, parents,
+ &result_commit, NULL, sign_commit))
die(_("failed to write commit object"));
finish(head, remoteheads, &result_commit, "In-index merge");
drop_save();
@@ -844,8 +843,8 @@ static int finish_automerge(struct commit *head,
commit_list_insert(head, &parents);
strbuf_addch(&merge_msg, '\n');
prepare_to_commit(remoteheads);
- if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents,
- result_commit.hash, NULL, sign_commit))
+ if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
+ &result_commit, NULL, sign_commit))
die(_("failed to write commit object"));
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
finish(head, remoteheads, &result_commit, buf.buf);
@@ -933,8 +932,11 @@ static void write_merge_heads(struct commit_list *remoteheads)
for (j = remoteheads; j; j = j->next) {
struct object_id *oid;
struct commit *c = j->item;
- if (c->util && merge_remote_util(c)->obj) {
- oid = &merge_remote_util(c)->obj->oid;
+ struct merge_remote_desc *desc;
+
+ desc = merge_remote_util(c);
+ if (desc && desc->obj) {
+ oid = &desc->obj->oid;
} else {
oid = &c->object.oid;
}
@@ -999,6 +1001,7 @@ static struct commit_list *reduce_parents(struct commit *head_commit,
/* Find what parents to record by checking independent ones. */
parents = reduce_heads(remoteheads);
+ free_commit_list(remoteheads);
remoteheads = NULL;
remotes = &remoteheads;
@@ -1123,6 +1126,43 @@ static struct commit_list *collect_parents(struct commit *head_commit,
return remoteheads;
}
+static int merging_a_throwaway_tag(struct commit *commit)
+{
+ char *tag_ref;
+ struct object_id oid;
+ int is_throwaway_tag = 0;
+
+ /* Are we merging a tag? */
+ if (!merge_remote_util(commit) ||
+ !merge_remote_util(commit)->obj ||
+ merge_remote_util(commit)->obj->type != OBJ_TAG)
+ return is_throwaway_tag;
+
+ /*
+ * Now we know we are merging a tag object. Are we downstream
+ * and following the tags from upstream? If so, we must have
+ * the tag object pointed at by "refs/tags/$T" where $T is the
+ * tagname recorded in the tag object. We want to allow such
+ * a "just to catch up" merge to fast-forward.
+ *
+ * Otherwise, we are playing an integrator's role, making a
+ * merge with a throw-away tag from a contributor with
+ * something like "git pull $contributor $signed_tag".
+ * We want to forbid such a merge from fast-forwarding
+ * by default; otherwise we would not keep the signature
+ * anywhere.
+ */
+ tag_ref = xstrfmt("refs/tags/%s",
+ ((struct tag *)merge_remote_util(commit)->obj)->tag);
+ if (!read_ref(tag_ref, &oid) &&
+ !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))
+ is_throwaway_tag = 0;
+ else
+ is_throwaway_tag = 1;
+ free(tag_ref);
+ return is_throwaway_tag;
+}
+
int cmd_merge(int argc, const char **argv, const char *prefix)
{
struct object_id result_tree, stash, head_oid;
@@ -1143,17 +1183,18 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
if (branch)
skip_prefix(branch, "refs/heads/", &branch);
+
+ init_diff_ui_defaults();
+ git_config(git_merge_config, NULL);
+
if (!branch || is_null_oid(&head_oid))
head_commit = NULL;
else
head_commit = lookup_commit_or_die(&head_oid, "HEAD");
- init_diff_ui_defaults();
- git_config(git_merge_config, NULL);
-
if (branch_mergeoptions)
parse_branch_merge_options(branch_mergeoptions);
argc = parse_options(argc, argv, prefix, builtin_merge_options,
@@ -1260,9 +1301,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (remoteheads->next)
die(_("Can merge only exactly one commit into empty head"));
remote_head_oid = &remoteheads->item->object.oid;
- read_empty(remote_head_oid->hash, 0);
- update_ref("initial pull", "HEAD", remote_head_oid->hash,
- NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ read_empty(remote_head_oid, 0);
+ update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
goto done;
}
@@ -1287,7 +1328,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
check_commit_signature(commit, &signature_check);
- find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV);
+ find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
switch (signature_check.result) {
case 'G':
break;
@@ -1320,10 +1361,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
oid_to_hex(&commit->object.oid));
setenv(buf.buf, merge_remote_util(commit)->name, 1);
strbuf_reset(&buf);
- if (fast_forward != FF_ONLY &&
- merge_remote_util(commit) &&
- merge_remote_util(commit)->obj &&
- merge_remote_util(commit)->obj->type == OBJ_TAG)
+ if (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))
fast_forward = FF_NO;
}
@@ -1357,8 +1395,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
free(list);
}
- update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
- NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+ update_ref("updating ORIG_HEAD", "ORIG_HEAD",
+ &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
if (remoteheads && !common) {
/* No common ancestors found. */
@@ -1383,9 +1421,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (verbosity >= 0) {
printf(_("Updating %s..%s\n"),
- find_unique_abbrev(head_commit->object.oid.hash,
+ find_unique_abbrev(&head_commit->object.oid,
DEFAULT_ABBREV),
- find_unique_abbrev(remoteheads->item->object.oid.hash,
+ find_unique_abbrev(&remoteheads->item->object.oid,
DEFAULT_ABBREV));
}
strbuf_addstr(&msg, "Fast-forward");
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 031b750f06..82a6e86077 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "tag.h"
+#include "replace-object.h"
/*
* A signature file has a very simple fixed format: four lines
@@ -18,17 +19,17 @@
/*
* We refuse to tag something we can't verify. Just because.
*/
-static int verify_object(const unsigned char *sha1, const char *expected_type)
+static int verify_object(const struct object_id *oid, const char *expected_type)
{
int ret = -1;
enum object_type type;
unsigned long size;
- void *buffer = read_sha1_file(sha1, &type, &size);
- const unsigned char *repl = lookup_replace_object(sha1);
+ void *buffer = read_object_file(oid, &type, &size);
+ const struct object_id *repl = lookup_replace_object(the_repository, oid);
if (buffer) {
if (type == type_from_string(expected_type))
- ret = check_sha1_signature(repl, buffer, size, expected_type);
+ ret = check_object_signature(repl, buffer, size, expected_type);
free(buffer);
}
return ret;
@@ -38,8 +39,8 @@ static int verify_tag(char *buffer, unsigned long size)
{
int typelen;
char type[20];
- unsigned char sha1[20];
- const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb;
+ struct object_id oid;
+ const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;
size_t len;
if (size < 84)
@@ -52,11 +53,11 @@ static int verify_tag(char *buffer, unsigned long size)
if (memcmp(object, "object ", 7))
return error("char%d: does not start with \"object \"", 0);
- if (get_sha1_hex(object + 7, sha1))
+ if (parse_oid_hex(object + 7, &oid, &p))
return error("char%d: could not get SHA1 hash", 7);
/* Verify type line */
- type_line = object + 48;
+ type_line = p + 1;
if (memcmp(type_line - 1, "\ntype ", 6))
return error("char%d: could not find \"\\ntype \"", 47);
@@ -80,8 +81,8 @@ static int verify_tag(char *buffer, unsigned long size)
type[typelen] = 0;
/* Verify that the object matches */
- if (verify_object(sha1, type))
- return error("char%d: could not verify object %s", 7, sha1_to_hex(sha1));
+ if (verify_object(&oid, type))
+ return error("char%d: could not verify object %s", 7, oid_to_hex(&oid));
/* Verify the tag-name: we don't allow control characters or spaces in it */
tag_line += 4;
@@ -151,7 +152,7 @@ static int verify_tag(char *buffer, unsigned long size)
int cmd_mktag(int argc, const char **argv, const char *prefix)
{
struct strbuf buf = STRBUF_INIT;
- unsigned char result_sha1[20];
+ struct object_id result;
if (argc != 1)
usage("git mktag");
@@ -165,10 +166,10 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)
if (verify_tag(buf.buf, buf.len) < 0)
die("invalid tag signature file");
- if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0)
+ if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
die("unable to write tag file");
strbuf_release(&buf);
- printf("%s\n", sha1_to_hex(result_sha1));
+ printf("%s\n", oid_to_hex(&result));
return 0;
}
diff --git a/builtin/mktree.c b/builtin/mktree.c
index da0fd8cd70..bb76b469fd 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -10,13 +10,13 @@
static struct treeent {
unsigned mode;
- unsigned char sha1[20];
+ struct object_id oid;
int len;
char name[FLEX_ARRAY];
} **entries;
static int alloc, used;
-static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
+static void append_to_tree(unsigned mode, struct object_id *oid, char *path)
{
struct treeent *ent;
size_t len = strlen(path);
@@ -26,7 +26,7 @@ static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
FLEX_ALLOC_MEM(ent, name, path, len);
ent->mode = mode;
ent->len = len;
- hashcpy(ent->sha1, sha1);
+ oidcpy(&ent->oid, oid);
ALLOC_GROW(entries, used + 1, alloc);
entries[used++] = ent;
@@ -40,7 +40,7 @@ static int ent_compare(const void *a_, const void *b_)
b->name, b->len, b->mode);
}
-static void write_tree(unsigned char *sha1)
+static void write_tree(struct object_id *oid)
{
struct strbuf buf;
size_t size;
@@ -54,10 +54,10 @@ static void write_tree(unsigned char *sha1)
for (i = 0; i < used; i++) {
struct treeent *ent = entries[i];
strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0');
- strbuf_add(&buf, ent->sha1, 20);
+ strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz);
}
- write_sha1_file(buf.buf, buf.len, tree_type, sha1);
+ write_object_file(buf.buf, buf.len, tree_type, oid);
strbuf_release(&buf);
}
@@ -69,11 +69,12 @@ static const char *mktree_usage[] = {
static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_missing)
{
char *ptr, *ntr;
+ const char *p;
unsigned mode;
enum object_type mode_type; /* object type derived from mode */
enum object_type obj_type; /* object type derived from sha */
char *path, *to_free = NULL;
- unsigned char sha1[20];
+ struct object_id oid;
ptr = buf;
/*
@@ -85,9 +86,8 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
die("input format error: %s", buf);
ptr = ntr + 1; /* type */
ntr = strchr(ptr, ' ');
- if (!ntr || buf + len <= ntr + 40 ||
- ntr[41] != '\t' ||
- get_sha1_hex(ntr + 1, sha1))
+ if (!ntr || parse_oid_hex(ntr + 1, &oid, &p) ||
+ *p != '\t')
die("input format error: %s", buf);
/* It is perfectly normal if we do not have a commit from a submodule */
@@ -112,16 +112,16 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
mode_type = object_type(mode);
if (mode_type != type_from_string(ptr)) {
die("entry '%s' object type (%s) doesn't match mode type (%s)",
- path, ptr, typename(mode_type));
+ path, ptr, type_name(mode_type));
}
/* Check the type of object identified by sha1 */
- obj_type = sha1_object_info(sha1, NULL);
+ obj_type = oid_object_info(the_repository, &oid, NULL);
if (obj_type < 0) {
if (allow_missing) {
; /* no problem - missing objects are presumed to be of the right type */
} else {
- die("entry '%s' object %s is unavailable", path, sha1_to_hex(sha1));
+ die("entry '%s' object %s is unavailable", path, oid_to_hex(&oid));
}
} else {
if (obj_type != mode_type) {
@@ -131,18 +131,18 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
* because the new tree entry will never be correct.
*/
die("entry '%s' object %s is a %s but specified type was (%s)",
- path, sha1_to_hex(sha1), typename(obj_type), typename(mode_type));
+ path, oid_to_hex(&oid), type_name(obj_type), type_name(mode_type));
}
}
- append_to_tree(mode, sha1, path);
+ append_to_tree(mode, &oid, path);
free(to_free);
}
int cmd_mktree(int ac, const char **av, const char *prefix)
{
struct strbuf sb = STRBUF_INIT;
- unsigned char sha1[20];
+ struct object_id oid;
int nul_term_line = 0;
int allow_missing = 0;
int is_batch_mode = 0;
@@ -181,8 +181,8 @@ int cmd_mktree(int ac, const char **av, const char *prefix)
*/
; /* skip creating an empty tree */
} else {
- write_tree(sha1);
- puts(sha1_to_hex(sha1));
+ write_tree(&oid);
+ puts(oid_to_hex(&oid));
fflush(stdout);
}
used=0; /* reset tree entry buffer for re-use in batch mode */
diff --git a/builtin/mv.c b/builtin/mv.c
index ffdd5f01a1..80bb967a63 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -72,7 +72,6 @@ static const char *add_slash(const char *path)
return path;
}
-static struct lock_file lock_file;
#define SUBMODULE_WITH_GITDIR ((const char *)1)
static void prepare_move_submodule(const char *src, int first,
@@ -122,7 +121,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct option builtin_mv_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT__DRY_RUN(&show_only, N_("dry run")),
- OPT__FORCE(&force, N_("force move/rename even if target exists")),
+ OPT__FORCE(&force, N_("force move/rename even if target exists"),
+ PARSE_OPT_NOCOMPLETE),
OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
OPT_END(),
};
@@ -130,6 +130,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
+ struct lock_file lock_file = LOCK_INIT;
git_config(git_default_config, NULL);
@@ -275,10 +276,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
die_errno(_("renaming '%s' failed"), src);
}
if (submodule_gitfile[i]) {
- if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
- connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
if (!update_path_in_gitmodules(src, dst))
gitmodules_modified = 1;
+ if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
+ connect_work_tree_and_git_dir(dst,
+ submodule_gitfile[i],
+ 1);
}
if (mode == WORKING_DIRECTORY)
@@ -286,15 +289,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
pos = cache_name_pos(src, strlen(src));
assert(pos >= 0);
- if (!show_only)
- rename_cache_entry_at(pos, dst);
+ rename_cache_entry_at(pos, dst);
}
if (gitmodules_modified)
- stage_updated_gitmodules();
+ stage_updated_gitmodules(&the_index);
- if (active_cache_changed &&
- write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
return 0;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 9e088ebd11..0eb440359d 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -6,6 +6,7 @@
#include "refs.h"
#include "parse-options.h"
#include "sha1-lookup.h"
+#include "commit-slab.h"
#define CUTOFF_DATE_SLOP 86400 /* one day */
@@ -17,11 +18,26 @@ typedef struct rev_name {
int from_tag;
} rev_name;
+define_commit_slab(commit_rev_name, struct rev_name *);
+
static timestamp_t cutoff = TIME_MAX;
+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)
+{
+ struct rev_name **slot = commit_rev_name_peek(&rev_names, commit);
+
+ return slot ? *slot : NULL;
+}
+
+static void set_commit_rev_name(struct commit *commit, struct rev_name *name)
+{
+ *commit_rev_name_at(&rev_names, commit) = name;
+}
+
static int is_better_name(struct rev_name *name,
const char *tip_name,
timestamp_t taggerdate,
@@ -65,7 +81,7 @@ static void name_rev(struct commit *commit,
int generation, int distance, int from_tag,
int deref)
{
- struct rev_name *name = (struct rev_name *)commit->util;
+ struct rev_name *name = get_commit_rev_name(commit);
struct commit_list *parents;
int parent_number = 1;
char *to_free = NULL;
@@ -84,7 +100,7 @@ static void name_rev(struct commit *commit,
if (name == NULL) {
name = xmalloc(sizeof(rev_name));
- commit->util = name;
+ set_commit_rev_name(commit, name);
goto copy_data;
} else if (is_better_name(name, tip_name, taggerdate,
generation, distance, from_tag)) {
@@ -296,7 +312,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
if (o->type != OBJ_COMMIT)
return get_exact_ref_match(o);
c = (struct commit *) o;
- n = c->util;
+ n = get_commit_rev_name(c);
if (!n)
return NULL;
@@ -328,7 +344,7 @@ static void show_name(const struct object *obj,
else if (allow_undefined)
printf("undefined\n");
else if (always)
- printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
+ printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV));
else
die("cannot describe '%s'", oid_to_hex(oid));
strbuf_release(&buf);
@@ -413,6 +429,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ init_commit_rev_name(&rev_names);
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
if (all + transform_stdin + !!argc > 1) {
diff --git a/builtin/notes.c b/builtin/notes.c
index 8e54f2d146..6981e2d906 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -12,9 +12,9 @@
#include "builtin.h"
#include "notes.h"
#include "blob.h"
-#include "commit.h"
+#include "pretty.h"
#include "refs.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "run-command.h"
#include "parse-options.h"
#include "string-list.h"
@@ -33,7 +33,7 @@ static const char * const git_notes_usage[] = {
N_("git notes merge --commit [-v | -q]"),
N_("git notes merge --abort [-v | -q]"),
N_("git notes [--ref <notes-ref>] remove [<object>...]"),
- N_("git notes [--ref <notes-ref>] prune [-n | -v]"),
+ N_("git notes [--ref <notes-ref>] prune [-n] [-v]"),
N_("git notes [--ref <notes-ref>] get-ref"),
NULL
};
@@ -118,11 +118,11 @@ static int list_each_note(const struct object_id *object_oid,
return 0;
}
-static void copy_obj_to_fd(int fd, const unsigned char *sha1)
+static void copy_obj_to_fd(int fd, const struct object_id *oid)
{
unsigned long size;
enum object_type type;
- char *buf = read_sha1_file(sha1, &type, &size);
+ char *buf = read_object_file(oid, &type, &size);
if (buf) {
if (size)
write_or_die(fd, buf, size);
@@ -162,7 +162,7 @@ static void write_commented_object(int fd, const struct object_id *object)
}
static void prepare_note_data(const struct object_id *object, struct note_data *d,
- const unsigned char *old_note)
+ const struct object_id *old_note)
{
if (d->use_editor || !d->given) {
int fd;
@@ -198,9 +198,9 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
}
}
-static void write_note_data(struct note_data *d, unsigned char *sha1)
+static void write_note_data(struct note_data *d, struct object_id *oid)
{
- if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) {
+ if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
error(_("unable to write note object"));
if (d->edit_path)
error(_("the note contents have been left in %s"),
@@ -253,7 +253,7 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
if (get_oid(arg, &object))
die(_("failed to resolve '%s' as a valid ref."), arg);
- if (!(buf = read_sha1_file(object.hash, &type, &len))) {
+ if (!(buf = read_object_file(&object, &type, &len))) {
free(buf);
die(_("failed to read object '%s'."), arg);
}
@@ -413,7 +413,7 @@ static int add(int argc, const char **argv, const char *prefix)
parse_reuse_arg},
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
- OPT__FORCE(&force, N_("replace existing notes")),
+ OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
OPT_END()
};
@@ -457,11 +457,11 @@ static int add(int argc, const char **argv, const char *prefix)
oid_to_hex(&object));
}
- prepare_note_data(&object, &d, note ? note->hash : NULL);
+ prepare_note_data(&object, &d, note);
if (d.buf.len || allow_empty) {
- write_note_data(&d, new_note.hash);
+ write_note_data(&d, &new_note);
if (add_note(t, &object, &new_note, combine_notes_overwrite))
- die("BUG: combine_notes_overwrite failed");
+ BUG("combine_notes_overwrite failed");
commit_notes(t, "Notes added by 'git notes add'");
} else {
fprintf(stderr, _("Removing note for object %s\n"),
@@ -484,7 +484,7 @@ static int copy(int argc, const char **argv, const char *prefix)
struct notes_tree *t;
const char *rewrite_cmd = NULL;
struct option options[] = {
- OPT__FORCE(&force, N_("replace existing notes")),
+ OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")),
OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
N_("load rewriting config for <command> (implies "
@@ -544,7 +544,7 @@ static int copy(int argc, const char **argv, const char *prefix)
}
if (add_note(t, &object, from_note, combine_notes_overwrite))
- die("BUG: combine_notes_overwrite failed");
+ BUG("combine_notes_overwrite failed");
commit_notes(t, "Notes added by 'git notes copy'");
out:
free_notes(t);
@@ -602,13 +602,13 @@ static int append_edit(int argc, const char **argv, const char *prefix)
t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
note = get_note(t, &object);
- prepare_note_data(&object, &d, edit && note ? note->hash : NULL);
+ prepare_note_data(&object, &d, edit && note ? note : NULL);
if (note && !edit) {
/* Append buf to previous note contents */
unsigned long size;
enum object_type type;
- char *prev_buf = read_sha1_file(note->hash, &type, &size);
+ char *prev_buf = read_object_file(note, &type, &size);
strbuf_grow(&d.buf, size + 1);
if (d.buf.len && prev_buf && size)
@@ -619,9 +619,9 @@ static int append_edit(int argc, const char **argv, const char *prefix)
}
if (d.buf.len || allow_empty) {
- write_note_data(&d, new_note.hash);
+ write_note_data(&d, &new_note);
if (add_note(t, &object, &new_note, combine_notes_overwrite))
- die("BUG: combine_notes_overwrite failed");
+ BUG("combine_notes_overwrite failed");
logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
} else {
fprintf(stderr, _("Removing note for object %s\n"),
@@ -686,7 +686,7 @@ static int merge_abort(struct notes_merge_options *o)
if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
- if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NODEREF))
+ if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
ret += error(_("failed to delete ref NOTES_MERGE_REF"));
if (notes_merge_abort(o))
ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -724,7 +724,7 @@ static int merge_commit(struct notes_merge_options *o)
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL);
+ resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
if (!o->local_ref)
die(_("failed to resolve NOTES_MERGE_REF"));
@@ -736,8 +736,8 @@ static int merge_commit(struct notes_merge_options *o)
format_commit_message(partial, "%s", &msg, &pretty_ctx);
strbuf_trim(&msg);
strbuf_insert(&msg, 0, "notes: ", 7);
- update_ref(msg.buf, o->local_ref, oid.hash,
- is_null_oid(&parent_oid) ? NULL : parent_oid.hash,
+ update_ref(msg.buf, o->local_ref, &oid,
+ is_null_oid(&parent_oid) ? NULL : &parent_oid,
0, UPDATE_REFS_DIE_ON_ERR);
free_notes(t);
@@ -778,13 +778,13 @@ static int merge(int argc, const char **argv, const char *prefix)
N_("resolve notes conflicts using the given strategy "
"(manual/ours/theirs/union/cat_sort_uniq)")),
OPT_GROUP(N_("Committing unmerged notes")),
- { OPTION_SET_INT, 0, "commit", &do_commit, NULL,
- N_("finalize notes merge by committing unmerged notes"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
+ OPT_SET_INT_F(0, "commit", &do_commit,
+ N_("finalize notes merge by committing unmerged notes"),
+ 1, PARSE_OPT_NONEG),
OPT_GROUP(N_("Aborting notes merge resolution")),
- { OPTION_SET_INT, 0, "abort", &do_abort, NULL,
- N_("abort notes merge"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
+ OPT_SET_INT_F(0, "abort", &do_abort,
+ N_("abort notes merge"),
+ 1, PARSE_OPT_NONEG),
OPT_END()
};
@@ -831,7 +831,7 @@ static int merge(int argc, const char **argv, const char *prefix)
const char *short_ref = NULL;
if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
- die("BUG: local ref %s is outside of refs/notes/",
+ BUG("local ref %s is outside of refs/notes/",
o.local_ref);
strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref);
@@ -850,12 +850,12 @@ static int merge(int argc, const char **argv, const char *prefix)
if (result >= 0) /* Merge resulted (trivially) in result_oid */
/* Update default notes ref with new commit */
- update_ref(msg.buf, default_notes_ref(), result_oid.hash, NULL,
- 0, UPDATE_REFS_DIE_ON_ERR);
+ update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
- update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_oid.hash, NULL,
+ update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
@@ -865,10 +865,10 @@ static int merge(int argc, const char **argv, const char *prefix)
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
die(_("failed to store link to current notes ref (%s)"),
default_notes_ref());
- printf(_("Automatic notes merge failed. Fix conflicts in %s and "
- "commit the result with 'git notes merge --commit', or "
- "abort the merge with 'git notes merge --abort'.\n"),
- git_path(NOTES_MERGE_WORKTREE));
+ fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
+ "and commit the result with 'git notes merge --commit', "
+ "or abort the merge with 'git notes merge --abort'.\n"),
+ git_path(NOTES_MERGE_WORKTREE));
}
free_notes(t);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index f5fc40177c..71056d8294 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "repository.h"
#include "config.h"
#include "attr.h"
#include "object.h"
@@ -26,8 +27,22 @@
#include "reachable.h"
#include "sha1-array.h"
#include "argv-array.h"
-#include "mru.h"
+#include "list.h"
#include "packfile.h"
+#include "object-store.h"
+#include "dir.h"
+
+#define IN_PACK(obj) oe_in_pack(&to_pack, obj)
+#define SIZE(obj) oe_size(&to_pack, obj)
+#define SET_SIZE(obj,size) oe_set_size(&to_pack, obj, size)
+#define DELTA_SIZE(obj) oe_delta_size(&to_pack, obj)
+#define DELTA(obj) oe_delta(&to_pack, obj)
+#define DELTA_CHILD(obj) oe_delta_child(&to_pack, obj)
+#define DELTA_SIBLING(obj) oe_delta_sibling(&to_pack, obj)
+#define SET_DELTA(obj, val) oe_set_delta(&to_pack, obj, val)
+#define SET_DELTA_SIZE(obj, val) oe_set_delta_size(&to_pack, obj, val)
+#define SET_DELTA_CHILD(obj, val) oe_set_delta_child(&to_pack, obj, val)
+#define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val)
static const char *pack_usage[] = {
N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
@@ -43,7 +58,7 @@ static const char *pack_usage[] = {
static struct packing_data to_pack;
static struct pack_idx_entry **written_list;
-static uint32_t nr_result, nr_written;
+static uint32_t nr_result, nr_written, nr_seen;
static int non_empty;
static int reuse_delta = 1, reuse_object = 1;
@@ -53,7 +68,8 @@ static int pack_loose_unreachable;
static int local;
static int have_non_local_packs;
static int incremental;
-static int ignore_packed_keep;
+static int ignore_packed_keep_on_disk;
+static int ignore_packed_keep_in_core;
static int allow_ofs_delta;
static struct pack_idx_option pack_idx_opts;
static const char *base_name;
@@ -78,7 +94,7 @@ static uint16_t write_bitmap_options;
static int exclude_promisor_objects;
static unsigned long delta_cache_size = 0;
-static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
+static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
static unsigned long cache_max_small_delta_size = 1000;
static unsigned long window_memory_limit = 0;
@@ -122,17 +138,17 @@ static void *get_delta(struct object_entry *entry)
void *buf, *base_buf, *delta_buf;
enum object_type type;
- buf = read_sha1_file(entry->idx.oid.hash, &type, &size);
+ buf = read_object_file(&entry->idx.oid, &type, &size);
if (!buf)
die("unable to read %s", oid_to_hex(&entry->idx.oid));
- base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type,
- &base_size);
+ base_buf = read_object_file(&DELTA(entry)->idx.oid, &type,
+ &base_size);
if (!base_buf)
die("unable to read %s",
- oid_to_hex(&entry->delta->idx.oid));
+ oid_to_hex(&DELTA(entry)->idx.oid));
delta_buf = diff_delta(base_buf, base_size,
buf, size, &delta_size, 0);
- if (!delta_buf || delta_size != entry->delta_size)
+ if (!delta_buf || delta_size != DELTA_SIZE(entry))
die("delta size changed");
free(buf);
free(base_buf);
@@ -164,8 +180,8 @@ static unsigned long do_compress(void **pptr, unsigned long size)
return stream.total_out;
}
-static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f,
- const unsigned char *sha1)
+static unsigned long write_large_blob_data(struct git_istream *st, struct hashfile *f,
+ const struct object_id *oid)
{
git_zstream stream;
unsigned char ibuf[1024 * 16];
@@ -179,7 +195,7 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi
int zret = Z_OK;
readlen = read_istream(st, ibuf, sizeof(ibuf));
if (readlen == -1)
- die(_("unable to read %s"), sha1_to_hex(sha1));
+ die(_("unable to read %s"), oid_to_hex(oid));
stream.next_in = ibuf;
stream.avail_in = readlen;
@@ -188,7 +204,7 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi
stream.next_out = obuf;
stream.avail_out = sizeof(obuf);
zret = git_deflate(&stream, readlen ? 0 : Z_FINISH);
- sha1write(f, obuf, stream.next_out - obuf);
+ hashwrite(f, obuf, stream.next_out - obuf);
olen += stream.next_out - obuf;
}
if (stream.avail_in)
@@ -233,7 +249,7 @@ static int check_pack_inflate(struct packed_git *p,
stream.total_in == len) ? 0 : -1;
}
-static void copy_pack_data(struct sha1file *f,
+static void copy_pack_data(struct hashfile *f,
struct packed_git *p,
struct pack_window **w_curs,
off_t offset,
@@ -246,14 +262,14 @@ static void copy_pack_data(struct sha1file *f,
in = use_pack(p, w_curs, offset, &avail);
if (avail > len)
avail = (unsigned long)len;
- sha1write(f, in, avail);
+ hashwrite(f, in, avail);
offset += avail;
len -= avail;
}
}
/* Return 0 if we will bust the pack-size limit */
-static unsigned long write_no_reuse_object(struct sha1file *f, struct object_entry *entry,
+static unsigned long write_no_reuse_object(struct hashfile *f, struct object_entry *entry,
unsigned long limit, int usable_delta)
{
unsigned long size, datalen;
@@ -263,15 +279,15 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
enum object_type type;
void *buf;
struct git_istream *st = NULL;
+ const unsigned hashsz = the_hash_algo->rawsz;
if (!usable_delta) {
- if (entry->type == OBJ_BLOB &&
- entry->size > big_file_threshold &&
- (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL)
+ 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)
buf = NULL;
else {
- buf = read_sha1_file(entry->idx.oid.hash, &type,
- &size);
+ buf = read_object_file(&entry->idx.oid, &type, &size);
if (!buf)
die(_("unable to read %s"),
oid_to_hex(&entry->idx.oid));
@@ -283,15 +299,15 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
FREE_AND_NULL(entry->delta_data);
entry->z_delta_size = 0;
} else if (entry->delta_data) {
- size = entry->delta_size;
+ size = DELTA_SIZE(entry);
buf = entry->delta_data;
entry->delta_data = NULL;
- type = (allow_ofs_delta && entry->delta->idx.offset) ?
+ type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
OBJ_OFS_DELTA : OBJ_REF_DELTA;
} else {
buf = get_delta(entry);
- size = entry->delta_size;
- type = (allow_ofs_delta && entry->delta->idx.offset) ?
+ size = DELTA_SIZE(entry);
+ type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
OBJ_OFS_DELTA : OBJ_REF_DELTA;
}
@@ -315,48 +331,48 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
* encoding of the relative offset for the delta
* base from this object's position in the pack.
*/
- off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+ off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset;
unsigned pos = sizeof(dheader) - 1;
dheader[pos] = ofs & 127;
while (ofs >>= 7)
dheader[--pos] = 128 | (--ofs & 127);
- if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+ if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) {
if (st)
close_istream(st);
free(buf);
return 0;
}
- sha1write(f, header, hdrlen);
- sha1write(f, dheader + pos, sizeof(dheader) - pos);
+ hashwrite(f, header, hdrlen);
+ hashwrite(f, dheader + pos, sizeof(dheader) - pos);
hdrlen += sizeof(dheader) - pos;
} else if (type == OBJ_REF_DELTA) {
/*
* Deltas with a base reference contain
- * an additional 20 bytes for the base sha1.
+ * additional bytes for the base object ID.
*/
- if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+ if (limit && hdrlen + hashsz + datalen + hashsz >= limit) {
if (st)
close_istream(st);
free(buf);
return 0;
}
- sha1write(f, header, hdrlen);
- sha1write(f, entry->delta->idx.oid.hash, 20);
- hdrlen += 20;
+ hashwrite(f, header, hdrlen);
+ hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz);
+ hdrlen += hashsz;
} else {
- if (limit && hdrlen + datalen + 20 >= limit) {
+ if (limit && hdrlen + datalen + hashsz >= limit) {
if (st)
close_istream(st);
free(buf);
return 0;
}
- sha1write(f, header, hdrlen);
+ hashwrite(f, header, hdrlen);
}
if (st) {
- datalen = write_large_blob_data(st, f, entry->idx.oid.hash);
+ datalen = write_large_blob_data(st, f, &entry->idx.oid);
close_istream(st);
} else {
- sha1write(f, buf, datalen);
+ hashwrite(f, buf, datalen);
free(buf);
}
@@ -364,24 +380,26 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
}
/* Return 0 if we will bust the pack-size limit */
-static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
+static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry,
unsigned long limit, int usable_delta)
{
- struct packed_git *p = entry->in_pack;
+ struct packed_git *p = IN_PACK(entry);
struct pack_window *w_curs = NULL;
struct revindex_entry *revidx;
off_t offset;
- enum object_type type = entry->type;
+ enum object_type type = oe_type(entry);
off_t datalen;
unsigned char header[MAX_PACK_OBJECT_HEADER],
dheader[MAX_PACK_OBJECT_HEADER];
unsigned hdrlen;
+ const unsigned hashsz = the_hash_algo->rawsz;
+ unsigned long entry_size = SIZE(entry);
- if (entry->delta)
- type = (allow_ofs_delta && entry->delta->idx.offset) ?
+ if (DELTA(entry))
+ type = (allow_ofs_delta && DELTA(entry)->idx.offset) ?
OBJ_OFS_DELTA : OBJ_REF_DELTA;
hdrlen = encode_in_pack_object_header(header, sizeof(header),
- type, entry->size);
+ type, entry_size);
offset = entry->in_pack_offset;
revidx = find_pack_revindex(p, offset);
@@ -398,7 +416,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
datalen -= entry->in_pack_header_size;
if (!pack_to_stdout && p->index_version == 1 &&
- check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
+ check_pack_inflate(p, &w_curs, offset, datalen, entry_size)) {
error("corrupt packed object for %s",
oid_to_hex(&entry->idx.oid));
unuse_pack(&w_curs);
@@ -406,34 +424,34 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
}
if (type == OBJ_OFS_DELTA) {
- off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+ off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset;
unsigned pos = sizeof(dheader) - 1;
dheader[pos] = ofs & 127;
while (ofs >>= 7)
dheader[--pos] = 128 | (--ofs & 127);
- if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+ if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) {
unuse_pack(&w_curs);
return 0;
}
- sha1write(f, header, hdrlen);
- sha1write(f, dheader + pos, sizeof(dheader) - pos);
+ hashwrite(f, header, hdrlen);
+ hashwrite(f, dheader + pos, sizeof(dheader) - pos);
hdrlen += sizeof(dheader) - pos;
reused_delta++;
} else if (type == OBJ_REF_DELTA) {
- if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+ if (limit && hdrlen + hashsz + datalen + hashsz >= limit) {
unuse_pack(&w_curs);
return 0;
}
- sha1write(f, header, hdrlen);
- sha1write(f, entry->delta->idx.oid.hash, 20);
- hdrlen += 20;
+ hashwrite(f, header, hdrlen);
+ hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz);
+ hdrlen += hashsz;
reused_delta++;
} else {
- if (limit && hdrlen + datalen + 20 >= limit) {
+ if (limit && hdrlen + datalen + hashsz >= limit) {
unuse_pack(&w_curs);
return 0;
}
- sha1write(f, header, hdrlen);
+ hashwrite(f, header, hdrlen);
}
copy_pack_data(f, p, &w_curs, offset, datalen);
unuse_pack(&w_curs);
@@ -442,7 +460,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
}
/* Return 0 if we will bust the pack-size limit */
-static off_t write_object(struct sha1file *f,
+static off_t write_object(struct hashfile *f,
struct object_entry *entry,
off_t write_offset)
{
@@ -465,28 +483,29 @@ static off_t write_object(struct sha1file *f,
else
limit = pack_size_limit - write_offset;
- if (!entry->delta)
+ if (!DELTA(entry))
usable_delta = 0; /* no delta */
else if (!pack_size_limit)
usable_delta = 1; /* unlimited packfile */
- else if (entry->delta->idx.offset == (off_t)-1)
+ else if (DELTA(entry)->idx.offset == (off_t)-1)
usable_delta = 0; /* base was written to another pack */
- else if (entry->delta->idx.offset)
+ else if (DELTA(entry)->idx.offset)
usable_delta = 1; /* base already exists in this pack */
else
usable_delta = 0; /* base could end up in another pack */
if (!reuse_object)
to_reuse = 0; /* explicit */
- else if (!entry->in_pack)
+ else if (!IN_PACK(entry))
to_reuse = 0; /* can't reuse what we don't have */
- else if (entry->type == OBJ_REF_DELTA || entry->type == OBJ_OFS_DELTA)
+ else if (oe_type(entry) == OBJ_REF_DELTA ||
+ oe_type(entry) == OBJ_OFS_DELTA)
/* check_object() decided it for us ... */
to_reuse = usable_delta;
/* ... but pack split may override that */
- else if (entry->type != entry->in_pack_type)
+ else if (oe_type(entry) != entry->in_pack_type)
to_reuse = 0; /* pack has delta which is unusable */
- else if (entry->delta)
+ else if (DELTA(entry))
to_reuse = 0; /* we want to pack afresh */
else
to_reuse = 1; /* we have it in-pack undeltified,
@@ -515,7 +534,7 @@ enum write_one_status {
WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
};
-static enum write_one_status write_one(struct sha1file *f,
+static enum write_one_status write_one(struct hashfile *f,
struct object_entry *e,
off_t *offset)
{
@@ -538,12 +557,12 @@ static enum write_one_status write_one(struct sha1file *f,
}
/* if we are deltified, write out base object first. */
- if (e->delta) {
+ if (DELTA(e)) {
e->idx.offset = 1; /* now recurse */
- switch (write_one(f, e->delta, offset)) {
+ switch (write_one(f, DELTA(e), offset)) {
case WRITE_ONE_RECURSIVE:
/* we cannot depend on this one */
- e->delta = NULL;
+ SET_DELTA(e, NULL);
break;
default:
break;
@@ -571,13 +590,13 @@ static enum write_one_status write_one(struct sha1file *f,
static int mark_tagged(const char *path, const struct object_id *oid, int flag,
void *cb_data)
{
- unsigned char peeled[20];
+ struct object_id peeled;
struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
if (entry)
entry->tagged = 1;
- if (!peel_ref(path, peeled)) {
- entry = packlist_find(&to_pack, peeled, NULL);
+ if (!peel_ref(path, &peeled)) {
+ entry = packlist_find(&to_pack, peeled.hash, NULL);
if (entry)
entry->tagged = 1;
}
@@ -605,34 +624,34 @@ static void add_descendants_to_write_order(struct object_entry **wo,
/* add this node... */
add_to_write_order(wo, endp, e);
/* all its siblings... */
- for (s = e->delta_sibling; s; s = s->delta_sibling) {
+ for (s = DELTA_SIBLING(e); s; s = DELTA_SIBLING(s)) {
add_to_write_order(wo, endp, s);
}
}
/* drop down a level to add left subtree nodes if possible */
- if (e->delta_child) {
+ if (DELTA_CHILD(e)) {
add_to_order = 1;
- e = e->delta_child;
+ e = DELTA_CHILD(e);
} else {
add_to_order = 0;
/* our sibling might have some children, it is next */
- if (e->delta_sibling) {
- e = e->delta_sibling;
+ if (DELTA_SIBLING(e)) {
+ e = DELTA_SIBLING(e);
continue;
}
/* go back to our parent node */
- e = e->delta;
- while (e && !e->delta_sibling) {
+ e = DELTA(e);
+ while (e && !DELTA_SIBLING(e)) {
/* we're on the right side of a subtree, keep
* going up until we can go right again */
- e = e->delta;
+ e = DELTA(e);
}
if (!e) {
/* done- we hit our original root node */
return;
}
/* pass it off to sibling at this level */
- e = e->delta_sibling;
+ e = DELTA_SIBLING(e);
}
};
}
@@ -643,7 +662,7 @@ static void add_family_to_write_order(struct object_entry **wo,
{
struct object_entry *root;
- for (root = e; root->delta; root = root->delta)
+ for (root = e; DELTA(root); root = DELTA(root))
; /* nothing */
add_descendants_to_write_order(wo, endp, root);
}
@@ -658,8 +677,8 @@ static struct object_entry **compute_write_order(void)
for (i = 0; i < to_pack.nr_objects; i++) {
objects[i].tagged = 0;
objects[i].filled = 0;
- objects[i].delta_child = NULL;
- objects[i].delta_sibling = NULL;
+ SET_DELTA_CHILD(&objects[i], NULL);
+ SET_DELTA_SIBLING(&objects[i], NULL);
}
/*
@@ -669,11 +688,11 @@ static struct object_entry **compute_write_order(void)
*/
for (i = to_pack.nr_objects; i > 0;) {
struct object_entry *e = &objects[--i];
- if (!e->delta)
+ if (!DELTA(e))
continue;
/* Mark me as the first child */
- e->delta_sibling = e->delta->delta_child;
- e->delta->delta_child = e;
+ e->delta_sibling_idx = DELTA(e)->delta_child_idx;
+ SET_DELTA_CHILD(DELTA(e), e);
}
/*
@@ -705,8 +724,8 @@ static struct object_entry **compute_write_order(void)
* And then all remaining commits and tags.
*/
for (i = last_untagged; i < to_pack.nr_objects; i++) {
- if (objects[i].type != OBJ_COMMIT &&
- objects[i].type != OBJ_TAG)
+ if (oe_type(&objects[i]) != OBJ_COMMIT &&
+ oe_type(&objects[i]) != OBJ_TAG)
continue;
add_to_write_order(wo, &wo_end, &objects[i]);
}
@@ -715,7 +734,7 @@ static struct object_entry **compute_write_order(void)
* And then all the trees.
*/
for (i = last_untagged; i < to_pack.nr_objects; i++) {
- if (objects[i].type != OBJ_TREE)
+ if (oe_type(&objects[i]) != OBJ_TREE)
continue;
add_to_write_order(wo, &wo_end, &objects[i]);
}
@@ -734,7 +753,7 @@ static struct object_entry **compute_write_order(void)
return wo;
}
-static off_t write_reused_pack(struct sha1file *f)
+static off_t write_reused_pack(struct hashfile *f)
{
unsigned char buffer[8192];
off_t to_write, total;
@@ -752,7 +771,7 @@ static off_t write_reused_pack(struct sha1file *f)
die_errno("unable to seek in reused packfile");
if (reuse_packfile_offset < 0)
- reuse_packfile_offset = reuse_packfile->pack_size - 20;
+ reuse_packfile_offset = reuse_packfile->pack_size - the_hash_algo->rawsz;
total = to_write = reuse_packfile_offset - sizeof(struct pack_header);
@@ -765,7 +784,7 @@ static off_t write_reused_pack(struct sha1file *f)
if (read_pack > to_write)
read_pack = to_write;
- sha1write(f, buffer, read_pack);
+ hashwrite(f, buffer, read_pack);
to_write -= read_pack;
/*
@@ -794,7 +813,7 @@ static const char no_split_warning[] = N_(
static void write_pack_file(void)
{
uint32_t i = 0, j;
- struct sha1file *f;
+ struct hashfile *f;
off_t offset;
uint32_t nr_remaining = nr_result;
time_t last_mtime = 0;
@@ -806,11 +825,11 @@ static void write_pack_file(void)
write_order = compute_write_order();
do {
- unsigned char sha1[20];
+ struct object_id oid;
char *pack_tmp_name = NULL;
if (pack_to_stdout)
- f = sha1fd_throughput(1, "<stdout>", progress_state);
+ f = hashfd_throughput(1, "<stdout>", progress_state);
else
f = create_tmp_packfile(&pack_tmp_name);
@@ -837,13 +856,13 @@ static void write_pack_file(void)
* If so, rewrite it like in fast-import
*/
if (pack_to_stdout) {
- sha1close(f, sha1, CSUM_CLOSE);
+ finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE);
} else if (nr_written == nr_remaining) {
- sha1close(f, sha1, CSUM_FSYNC);
+ finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
} else {
- int fd = sha1close(f, sha1, 0);
- fixup_pack_header_footer(fd, sha1, pack_tmp_name,
- nr_written, sha1, offset);
+ int fd = finalize_hashfile(f, oid.hash, 0);
+ fixup_pack_header_footer(fd, oid.hash, pack_tmp_name,
+ nr_written, oid.hash, offset);
close(fd);
if (write_bitmap_index) {
warning(_(no_split_warning));
@@ -877,16 +896,17 @@ static void write_pack_file(void)
strbuf_addf(&tmpname, "%s-", base_name);
if (write_bitmap_index) {
- bitmap_writer_set_checksum(sha1);
- bitmap_writer_build_type_index(written_list, nr_written);
+ bitmap_writer_set_checksum(oid.hash);
+ bitmap_writer_build_type_index(
+ &to_pack, written_list, nr_written);
}
finish_tmp_packfile(&tmpname, pack_tmp_name,
written_list, nr_written,
- &pack_idx_opts, sha1);
+ &pack_idx_opts, oid.hash);
if (write_bitmap_index) {
- strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1));
+ strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid));
stop_progress(&progress_state);
@@ -901,7 +921,7 @@ static void write_pack_file(void)
strbuf_release(&tmpname);
free(pack_tmp_name);
- puts(sha1_to_hex(sha1));
+ puts(oid_to_hex(&oid));
}
/* mark written objects as written to previous pack */
@@ -942,13 +962,13 @@ static int no_try_delta(const char *path)
* found the item, since that saves us from having to look it up again a
* few lines later when we want to add the new entry.
*/
-static int have_duplicate_entry(const unsigned char *sha1,
+static int have_duplicate_entry(const struct object_id *oid,
int exclude,
uint32_t *index_pos)
{
struct object_entry *entry;
- entry = packlist_find(&to_pack, sha1, index_pos);
+ entry = packlist_find(&to_pack, oid->hash, index_pos);
if (!entry)
return 0;
@@ -982,13 +1002,16 @@ static int want_found_object(int exclude, struct packed_git *p)
* Otherwise, we signal "-1" at the end to tell the caller that we do
* not know either way, and it needs to check more packs.
*/
- if (!ignore_packed_keep &&
+ if (!ignore_packed_keep_on_disk &&
+ !ignore_packed_keep_in_core &&
(!local || !have_non_local_packs))
return 1;
if (local && !p->pack_local)
return 0;
- if (ignore_packed_keep && p->pack_local && p->pack_keep)
+ if (p->pack_local &&
+ ((ignore_packed_keep_on_disk && p->pack_keep) ||
+ (ignore_packed_keep_in_core && p->pack_keep_in_core)))
return 0;
/* we don't know yet; keep looking for more packs */
@@ -1004,15 +1027,15 @@ static int want_found_object(int exclude, struct packed_git *p)
* function finds if there is any pack that has the object and returns the pack
* and its offset in these variables.
*/
-static int want_object_in_pack(const unsigned char *sha1,
+static int want_object_in_pack(const struct object_id *oid,
int exclude,
struct packed_git **found_pack,
off_t *found_offset)
{
- struct mru_entry *entry;
int want;
+ struct list_head *pos;
- if (!exclude && local && has_loose_object_nonlocal(sha1))
+ if (!exclude && local && has_loose_object_nonlocal(oid))
return 0;
/*
@@ -1025,15 +1048,14 @@ static int want_object_in_pack(const unsigned char *sha1,
if (want != -1)
return want;
}
-
- for (entry = packed_git_mru.head; entry; entry = entry->next) {
- struct packed_git *p = entry->item;
+ list_for_each(pos, get_packed_git_mru(the_repository)) {
+ struct packed_git *p = list_entry(pos, struct packed_git, mru);
off_t offset;
if (p == *found_pack)
offset = *found_offset;
else
- offset = find_pack_entry_one(sha1, p);
+ offset = find_pack_entry_one(oid->hash, p);
if (offset) {
if (!*found_pack) {
@@ -1044,7 +1066,8 @@ static int want_object_in_pack(const unsigned char *sha1,
}
want = want_found_object(exclude, p);
if (!exclude && want > 0)
- mru_mark(&packed_git_mru, entry);
+ list_move(&p->mru,
+ get_packed_git_mru(the_repository));
if (want != -1)
return want;
}
@@ -1053,7 +1076,7 @@ static int want_object_in_pack(const unsigned char *sha1,
return 1;
}
-static void create_object_entry(const unsigned char *sha1,
+static void create_object_entry(const struct object_id *oid,
enum object_type type,
uint32_t hash,
int exclude,
@@ -1064,16 +1087,15 @@ static void create_object_entry(const unsigned char *sha1,
{
struct object_entry *entry;
- entry = packlist_alloc(&to_pack, sha1, index_pos);
+ entry = packlist_alloc(&to_pack, oid->hash, index_pos);
entry->hash = hash;
- if (type)
- entry->type = type;
+ oe_set_type(entry, type);
if (exclude)
entry->preferred_base = 1;
else
nr_result++;
if (found_pack) {
- entry->in_pack = found_pack;
+ oe_set_in_pack(&to_pack, entry, found_pack);
entry->in_pack_offset = found_offset;
}
@@ -1084,17 +1106,19 @@ static const char no_closure_warning[] = N_(
"disabling bitmap writing, as some objects are not being packed"
);
-static int add_object_entry(const unsigned char *sha1, enum object_type type,
+static int add_object_entry(const struct object_id *oid, enum object_type type,
const char *name, int exclude)
{
struct packed_git *found_pack = NULL;
off_t found_offset = 0;
uint32_t index_pos;
- if (have_duplicate_entry(sha1, exclude, &index_pos))
+ display_progress(progress_state, ++nr_seen);
+
+ if (have_duplicate_entry(oid, exclude, &index_pos))
return 0;
- if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) {
+ if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
/* The pack is missing an object, so it will not have closure */
if (write_bitmap_index) {
warning(_(no_closure_warning));
@@ -1103,35 +1127,33 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
return 0;
}
- create_object_entry(sha1, type, pack_name_hash(name),
+ create_object_entry(oid, type, pack_name_hash(name),
exclude, name && no_try_delta(name),
index_pos, found_pack, found_offset);
-
- display_progress(progress_state, nr_result);
return 1;
}
-static int add_object_entry_from_bitmap(const unsigned char *sha1,
+static int add_object_entry_from_bitmap(const struct object_id *oid,
enum object_type type,
int flags, uint32_t name_hash,
struct packed_git *pack, off_t offset)
{
uint32_t index_pos;
- if (have_duplicate_entry(sha1, 0, &index_pos))
- return 0;
+ display_progress(progress_state, ++nr_seen);
- if (!want_object_in_pack(sha1, 0, &pack, &offset))
+ if (have_duplicate_entry(oid, 0, &index_pos))
return 0;
- create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
+ if (!want_object_in_pack(oid, 0, &pack, &offset))
+ return 0;
- display_progress(progress_state, nr_result);
+ create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
return 1;
}
struct pbase_tree_cache {
- unsigned char sha1[20];
+ struct object_id oid;
int ref;
int temporary;
void *tree_data;
@@ -1139,9 +1161,9 @@ struct pbase_tree_cache {
};
static struct pbase_tree_cache *(pbase_tree_cache[256]);
-static int pbase_tree_cache_ix(const unsigned char *sha1)
+static int pbase_tree_cache_ix(const struct object_id *oid)
{
- return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
+ return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache);
}
static int pbase_tree_cache_ix_incr(int ix)
{
@@ -1158,14 +1180,14 @@ static struct pbase_tree {
struct pbase_tree_cache pcache;
} *pbase_tree;
-static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
+static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
{
struct pbase_tree_cache *ent, *nent;
void *data;
unsigned long size;
enum object_type type;
int neigh;
- int my_ix = pbase_tree_cache_ix(sha1);
+ int my_ix = pbase_tree_cache_ix(oid);
int available_ix = -1;
/* pbase-tree-cache acts as a limited hashtable.
@@ -1174,7 +1196,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
*/
for (neigh = 0; neigh < 8; neigh++) {
ent = pbase_tree_cache[my_ix];
- if (ent && !hashcmp(ent->sha1, sha1)) {
+ if (ent && !oidcmp(&ent->oid, oid)) {
ent->ref++;
return ent;
}
@@ -1190,7 +1212,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
/* Did not find one. Either we got a bogus request or
* we need to read and perhaps cache.
*/
- data = read_sha1_file(sha1, &type, &size);
+ data = read_object_file(oid, &type, &size);
if (!data)
return NULL;
if (type != OBJ_TREE) {
@@ -1216,7 +1238,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
free(ent->tree_data);
nent = ent;
}
- hashcpy(nent->sha1, sha1);
+ oidcpy(&nent->oid, oid);
nent->tree_data = data;
nent->tree_size = size;
nent->ref = 1;
@@ -1261,7 +1283,7 @@ static void add_pbase_object(struct tree_desc *tree,
if (cmp < 0)
return;
if (name[cmplen] != '/') {
- add_object_entry(entry.oid->hash,
+ add_object_entry(entry.oid,
object_type(entry.mode),
fullname, 1);
return;
@@ -1272,7 +1294,7 @@ static void add_pbase_object(struct tree_desc *tree,
const char *down = name+cmplen+1;
int downlen = name_cmp_len(down);
- tree = pbase_tree_get(entry.oid->hash);
+ tree = pbase_tree_get(entry.oid);
if (!tree)
return;
init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@ -1331,7 +1353,7 @@ static void add_preferred_base_object(const char *name)
cmplen = name_cmp_len(name);
for (it = pbase_tree; it; it = it->next) {
if (cmplen == 0) {
- add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
+ add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1);
}
else {
struct tree_desc tree;
@@ -1341,22 +1363,22 @@ static void add_preferred_base_object(const char *name)
}
}
-static void add_preferred_base(unsigned char *sha1)
+static void add_preferred_base(struct object_id *oid)
{
struct pbase_tree *it;
void *data;
unsigned long size;
- unsigned char tree_sha1[20];
+ struct object_id tree_oid;
if (window <= num_preferred_base++)
return;
- data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
+ data = read_object_with_reference(oid, tree_type, &size, &tree_oid);
if (!data)
return;
for (it = pbase_tree; it; it = it->next) {
- if (!hashcmp(it->pcache.sha1, tree_sha1)) {
+ if (!oidcmp(&it->pcache.oid, &tree_oid)) {
free(data);
return;
}
@@ -1366,7 +1388,7 @@ static void add_preferred_base(unsigned char *sha1)
it->next = pbase_tree;
pbase_tree = it;
- hashcpy(it->pcache.sha1, tree_sha1);
+ oidcpy(&it->pcache.oid, &tree_oid);
it->pcache.tree_data = data;
it->pcache.tree_size = size;
}
@@ -1379,10 +1401,10 @@ static void cleanup_preferred_base(void)
it = pbase_tree;
pbase_tree = NULL;
while (it) {
- struct pbase_tree *this = it;
- it = this->next;
- free(this->pcache.tree_data);
- free(this);
+ struct pbase_tree *tmp = it;
+ it = tmp->next;
+ free(tmp->pcache.tree_data);
+ free(tmp);
}
for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
@@ -1398,8 +1420,10 @@ static void cleanup_preferred_base(void)
static void check_object(struct object_entry *entry)
{
- if (entry->in_pack) {
- struct packed_git *p = entry->in_pack;
+ unsigned long canonical_size;
+
+ if (IN_PACK(entry)) {
+ struct packed_git *p = IN_PACK(entry);
struct pack_window *w_curs = NULL;
const unsigned char *base_ref = NULL;
struct object_entry *base_entry;
@@ -1407,6 +1431,8 @@ static void check_object(struct object_entry *entry)
unsigned long avail;
off_t ofs;
unsigned char *buf, c;
+ enum object_type type;
+ unsigned long in_pack_size;
buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
@@ -1415,11 +1441,15 @@ static void check_object(struct object_entry *entry)
* since non-delta representations could still be reused.
*/
used = unpack_object_header_buffer(buf, avail,
- &entry->in_pack_type,
- &entry->size);
+ &type,
+ &in_pack_size);
if (used == 0)
goto give_up;
+ if (type < 0)
+ BUG("invalid type %d", type);
+ entry->in_pack_type = type;
+
/*
* Determine if this is a delta and if so whether we can
* reuse it or not. Otherwise let's find out as cheaply as
@@ -1428,9 +1458,10 @@ static void check_object(struct object_entry *entry)
switch (entry->in_pack_type) {
default:
/* Not a delta hence we've already got all we need. */
- entry->type = entry->in_pack_type;
+ oe_set_type(entry, entry->in_pack_type);
+ SET_SIZE(entry, in_pack_size);
entry->in_pack_header_size = used;
- if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
+ if (oe_type(entry) < OBJ_COMMIT || oe_type(entry) > OBJ_BLOB)
goto give_up;
unuse_pack(&w_curs);
return;
@@ -1438,7 +1469,7 @@ static void check_object(struct object_entry *entry)
if (reuse_delta && !entry->preferred_base)
base_ref = use_pack(p, &w_curs,
entry->in_pack_offset + used, NULL);
- entry->in_pack_header_size = used + 20;
+ entry->in_pack_header_size = used + the_hash_algo->rawsz;
break;
case OBJ_OFS_DELTA:
buf = use_pack(p, &w_curs,
@@ -1484,25 +1515,29 @@ static void check_object(struct object_entry *entry)
* deltify other objects against, in order to avoid
* circular deltas.
*/
- entry->type = entry->in_pack_type;
- entry->delta = base_entry;
- entry->delta_size = entry->size;
- entry->delta_sibling = base_entry->delta_child;
- base_entry->delta_child = entry;
+ oe_set_type(entry, entry->in_pack_type);
+ SET_SIZE(entry, in_pack_size); /* delta size */
+ SET_DELTA(entry, base_entry);
+ SET_DELTA_SIZE(entry, in_pack_size);
+ entry->delta_sibling_idx = base_entry->delta_child_idx;
+ SET_DELTA_CHILD(base_entry, entry);
unuse_pack(&w_curs);
return;
}
- if (entry->type) {
+ if (oe_type(entry)) {
+ off_t delta_pos;
+
/*
* This must be a delta and we already know what the
* final object type is. Let's extract the actual
* object size from the delta header.
*/
- entry->size = get_size_from_delta(p, &w_curs,
- entry->in_pack_offset + entry->in_pack_header_size);
- if (entry->size == 0)
+ delta_pos = entry->in_pack_offset + entry->in_pack_header_size;
+ canonical_size = get_size_from_delta(p, &w_curs, delta_pos);
+ if (canonical_size == 0)
goto give_up;
+ SET_SIZE(entry, canonical_size);
unuse_pack(&w_curs);
return;
}
@@ -1516,27 +1551,34 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
}
- entry->type = sha1_object_info(entry->idx.oid.hash, &entry->size);
- /*
- * The error condition is checked in prepare_pack(). This is
- * to permit a missing preferred base object to be ignored
- * as a preferred base. Doing so can result in a larger
- * pack file, but the transfer will still take place.
- */
+ oe_set_type(entry,
+ oid_object_info(the_repository, &entry->idx.oid, &canonical_size));
+ if (entry->type_valid) {
+ SET_SIZE(entry, canonical_size);
+ } else {
+ /*
+ * Bad object type is checked in prepare_pack(). This is
+ * to permit a missing preferred base object to be ignored
+ * as a preferred base. Doing so can result in a larger
+ * pack file, but the transfer will still take place.
+ */
+ }
}
static int pack_offset_sort(const void *_a, const void *_b)
{
const struct object_entry *a = *(struct object_entry **)_a;
const struct object_entry *b = *(struct object_entry **)_b;
+ const struct packed_git *a_in_pack = IN_PACK(a);
+ const struct packed_git *b_in_pack = IN_PACK(b);
/* avoid filesystem trashing with loose objects */
- if (!a->in_pack && !b->in_pack)
+ if (!a_in_pack && !b_in_pack)
return oidcmp(&a->idx.oid, &b->idx.oid);
- if (a->in_pack < b->in_pack)
+ if (a_in_pack < b_in_pack)
return -1;
- if (a->in_pack > b->in_pack)
+ if (a_in_pack > b_in_pack)
return 1;
return a->in_pack_offset < b->in_pack_offset ? -1 :
(a->in_pack_offset > b->in_pack_offset);
@@ -1557,30 +1599,37 @@ static int pack_offset_sort(const void *_a, const void *_b)
*/
static void drop_reused_delta(struct object_entry *entry)
{
- struct object_entry **p = &entry->delta->delta_child;
+ unsigned *idx = &to_pack.objects[entry->delta_idx - 1].delta_child_idx;
struct object_info oi = OBJECT_INFO_INIT;
+ enum object_type type;
+ unsigned long size;
+
+ while (*idx) {
+ struct object_entry *oe = &to_pack.objects[*idx - 1];
- while (*p) {
- if (*p == entry)
- *p = (*p)->delta_sibling;
+ if (oe == entry)
+ *idx = oe->delta_sibling_idx;
else
- p = &(*p)->delta_sibling;
+ idx = &oe->delta_sibling_idx;
}
- entry->delta = NULL;
+ SET_DELTA(entry, NULL);
entry->depth = 0;
- oi.sizep = &entry->size;
- oi.typep = &entry->type;
- if (packed_object_info(entry->in_pack, entry->in_pack_offset, &oi) < 0) {
+ oi.sizep = &size;
+ oi.typep = &type;
+ if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) {
/*
* We failed to get the info from this pack for some reason;
* fall back to sha1_object_info, which may find another copy.
- * And if that fails, the error will be recorded in entry->type
+ * And if that fails, the error will be recorded in oe_type(entry)
* and dealt with in prepare_pack().
*/
- entry->type = sha1_object_info(entry->idx.oid.hash,
- &entry->size);
+ oe_set_type(entry,
+ oid_object_info(the_repository, &entry->idx.oid, &size));
+ } else {
+ oe_set_type(entry, type);
}
+ SET_SIZE(entry, size);
}
/*
@@ -1604,7 +1653,7 @@ static void break_delta_chains(struct object_entry *entry)
for (cur = entry, total_depth = 0;
cur;
- cur = cur->delta, total_depth++) {
+ cur = DELTA(cur), total_depth++) {
if (cur->dfs_state == DFS_DONE) {
/*
* We've already seen this object and know it isn't
@@ -1621,7 +1670,7 @@ static void break_delta_chains(struct object_entry *entry)
* is a bug.
*/
if (cur->dfs_state != DFS_NONE)
- die("BUG: confusing delta dfs state in first pass: %d",
+ BUG("confusing delta dfs state in first pass: %d",
cur->dfs_state);
/*
@@ -1629,7 +1678,7 @@ static void break_delta_chains(struct object_entry *entry)
* it's not a delta, we're done traversing, but we'll mark it
* done to save time on future traversals.
*/
- if (!cur->delta) {
+ if (!DELTA(cur)) {
cur->dfs_state = DFS_DONE;
break;
}
@@ -1652,7 +1701,7 @@ static void break_delta_chains(struct object_entry *entry)
* We keep all commits in the chain that we examined.
*/
cur->dfs_state = DFS_ACTIVE;
- if (cur->delta->dfs_state == DFS_ACTIVE) {
+ if (DELTA(cur)->dfs_state == DFS_ACTIVE) {
drop_reused_delta(cur);
cur->dfs_state = DFS_DONE;
break;
@@ -1667,7 +1716,7 @@ static void break_delta_chains(struct object_entry *entry)
* an extra "next" pointer to keep going after we reset cur->delta.
*/
for (cur = entry; cur; cur = next) {
- next = cur->delta;
+ next = DELTA(cur);
/*
* We should have a chain of zero or more ACTIVE states down to
@@ -1678,7 +1727,7 @@ static void break_delta_chains(struct object_entry *entry)
if (cur->dfs_state == DFS_DONE)
break;
else if (cur->dfs_state != DFS_ACTIVE)
- die("BUG: confusing delta dfs state in second pass: %d",
+ BUG("confusing delta dfs state in second pass: %d",
cur->dfs_state);
/*
@@ -1712,6 +1761,10 @@ static void get_object_details(void)
uint32_t i;
struct object_entry **sorted_by_offset;
+ if (progress)
+ progress_state = start_progress(_("Counting objects"),
+ to_pack.nr_objects);
+
sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));
for (i = 0; i < to_pack.nr_objects; i++)
sorted_by_offset[i] = to_pack.objects + i;
@@ -1720,9 +1773,12 @@ static void get_object_details(void)
for (i = 0; i < to_pack.nr_objects; i++) {
struct object_entry *entry = sorted_by_offset[i];
check_object(entry);
- if (big_file_threshold < entry->size)
+ if (entry->type_valid &&
+ oe_size_greater_than(&to_pack, entry, big_file_threshold))
entry->no_try_delta = 1;
+ display_progress(progress_state, i + 1);
}
+ stop_progress(&progress_state);
/*
* This must happen in a second pass, since we rely on the delta
@@ -1747,10 +1803,14 @@ static int type_size_sort(const void *_a, const void *_b)
{
const struct object_entry *a = *(struct object_entry **)_a;
const struct object_entry *b = *(struct object_entry **)_b;
+ enum object_type a_type = oe_type(a);
+ enum object_type b_type = oe_type(b);
+ unsigned long a_size = SIZE(a);
+ unsigned long b_size = SIZE(b);
- if (a->type > b->type)
+ if (a_type > b_type)
return -1;
- if (a->type < b->type)
+ if (a_type < b_type)
return 1;
if (a->hash > b->hash)
return -1;
@@ -1760,9 +1820,9 @@ static int type_size_sort(const void *_a, const void *_b)
return -1;
if (a->preferred_base < b->preferred_base)
return 1;
- if (a->size > b->size)
+ if (a_size > b_size)
return -1;
- if (a->size < b->size)
+ if (a_size < b_size)
return 1;
return a < b ? -1 : (a > b); /* newest first */
}
@@ -1815,6 +1875,46 @@ static pthread_mutex_t progress_mutex;
#endif
+/*
+ * Return the size of the object without doing any delta
+ * reconstruction (so non-deltas are true object sizes, but deltas
+ * return the size of the delta data).
+ */
+unsigned long oe_get_size_slow(struct packing_data *pack,
+ const struct object_entry *e)
+{
+ struct packed_git *p;
+ struct pack_window *w_curs;
+ unsigned char *buf;
+ enum object_type type;
+ unsigned long used, avail, size;
+
+ if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) {
+ read_lock();
+ if (oid_object_info(the_repository, &e->idx.oid, &size) < 0)
+ die(_("unable to get size of %s"),
+ oid_to_hex(&e->idx.oid));
+ read_unlock();
+ return size;
+ }
+
+ p = oe_in_pack(pack, e);
+ if (!p)
+ BUG("when e->type is a delta, it must belong to a pack");
+
+ read_lock();
+ w_curs = NULL;
+ buf = use_pack(p, &w_curs, e->in_pack_offset, &avail);
+ used = unpack_object_header_buffer(buf, avail, &type, &size);
+ if (used == 0)
+ die(_("unable to parse object header of %s"),
+ oid_to_hex(&e->idx.oid));
+
+ unuse_pack(&w_curs);
+ read_unlock();
+ return size;
+}
+
static int try_delta(struct unpacked *trg, struct unpacked *src,
unsigned max_depth, unsigned long *mem_usage)
{
@@ -1826,7 +1926,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
void *delta_buf;
/* Don't bother doing diffs between different types */
- if (trg_entry->type != src_entry->type)
+ if (oe_type(trg_entry) != oe_type(src_entry))
return -1;
/*
@@ -1837,8 +1937,8 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
* it, we will still save the transfer cost, as we already know
* the other side has it and we won't send src_entry at all.
*/
- if (reuse_delta && trg_entry->in_pack &&
- trg_entry->in_pack == src_entry->in_pack &&
+ if (reuse_delta && IN_PACK(trg_entry) &&
+ IN_PACK(trg_entry) == IN_PACK(src_entry) &&
!src_entry->preferred_base &&
trg_entry->in_pack_type != OBJ_REF_DELTA &&
trg_entry->in_pack_type != OBJ_OFS_DELTA)
@@ -1849,19 +1949,19 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
/* Now some size filtering heuristics. */
- trg_size = trg_entry->size;
- if (!trg_entry->delta) {
- max_size = trg_size/2 - 20;
+ trg_size = SIZE(trg_entry);
+ if (!DELTA(trg_entry)) {
+ max_size = trg_size/2 - the_hash_algo->rawsz;
ref_depth = 1;
} else {
- max_size = trg_entry->delta_size;
+ max_size = DELTA_SIZE(trg_entry);
ref_depth = trg->depth;
}
max_size = (uint64_t)max_size * (max_depth - src->depth) /
(max_depth - ref_depth + 1);
if (max_size == 0)
return 0;
- src_size = src_entry->size;
+ src_size = SIZE(src_entry);
sizediff = src_size < trg_size ? trg_size - src_size : 0;
if (sizediff >= max_size)
return 0;
@@ -1871,8 +1971,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
/* Load data if not already done */
if (!trg->data) {
read_lock();
- trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type,
- &sz);
+ trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
read_unlock();
if (!trg->data)
die("object %s cannot be read",
@@ -1885,8 +1984,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
}
if (!src->data) {
read_lock();
- src->data = read_sha1_file(src_entry->idx.oid.hash, &type,
- &sz);
+ src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
read_unlock();
if (!src->data) {
if (src_entry->preferred_base) {
@@ -1925,10 +2023,14 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
if (!delta_buf)
return 0;
+ if (delta_size >= (1U << OE_DELTA_SIZE_BITS)) {
+ free(delta_buf);
+ return 0;
+ }
- if (trg_entry->delta) {
+ if (DELTA(trg_entry)) {
/* Prefer only shallower same-sized deltas. */
- if (delta_size == trg_entry->delta_size &&
+ if (delta_size == DELTA_SIZE(trg_entry) &&
src->depth + 1 >= trg->depth) {
free(delta_buf);
return 0;
@@ -1943,7 +2045,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
free(trg_entry->delta_data);
cache_lock();
if (trg_entry->delta_data) {
- delta_cache_size -= trg_entry->delta_size;
+ delta_cache_size -= DELTA_SIZE(trg_entry);
trg_entry->delta_data = NULL;
}
if (delta_cacheable(src_size, trg_size, delta_size)) {
@@ -1955,8 +2057,8 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
free(delta_buf);
}
- trg_entry->delta = src_entry;
- trg_entry->delta_size = delta_size;
+ SET_DELTA(trg_entry, src_entry);
+ SET_DELTA_SIZE(trg_entry, delta_size);
trg->depth = src->depth + 1;
return 1;
@@ -1964,13 +2066,13 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
{
- struct object_entry *child = me->delta_child;
+ struct object_entry *child = DELTA_CHILD(me);
unsigned int m = n;
while (child) {
unsigned int c = check_delta_limit(child, n + 1);
if (m < c)
m = c;
- child = child->delta_sibling;
+ child = DELTA_SIBLING(child);
}
return m;
}
@@ -1981,7 +2083,7 @@ static unsigned long free_unpacked(struct unpacked *n)
free_delta_index(n->index);
n->index = NULL;
if (n->data) {
- freed_mem += n->entry->size;
+ freed_mem += SIZE(n->entry);
FREE_AND_NULL(n->data);
}
n->entry = NULL;
@@ -2039,7 +2141,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
* otherwise they would become too deep.
*/
max_depth = depth;
- if (entry->delta_child) {
+ if (DELTA_CHILD(entry)) {
max_depth -= check_delta_limit(entry, 0);
if (max_depth <= 0)
goto next;
@@ -2077,19 +2179,26 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
* between writes at that moment.
*/
if (entry->delta_data && !pack_to_stdout) {
- entry->z_delta_size = do_compress(&entry->delta_data,
- entry->delta_size);
- cache_lock();
- delta_cache_size -= entry->delta_size;
- delta_cache_size += entry->z_delta_size;
- cache_unlock();
+ unsigned long size;
+
+ size = do_compress(&entry->delta_data, DELTA_SIZE(entry));
+ if (size < (1U << OE_Z_DELTA_BITS)) {
+ entry->z_delta_size = size;
+ cache_lock();
+ delta_cache_size -= DELTA_SIZE(entry);
+ delta_cache_size += entry->z_delta_size;
+ cache_unlock();
+ } else {
+ FREE_AND_NULL(entry->delta_data);
+ entry->z_delta_size = 0;
+ }
}
/* if we made n a delta, and if n is already at max
* depth, leaving it in the window is pointless. we
* should evict it first.
*/
- if (entry->delta && max_depth <= n->depth)
+ if (DELTA(entry) && max_depth <= n->depth)
continue;
/*
@@ -2097,7 +2206,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
* currently deltified object, to keep it longer. It will
* be the first base object to be attempted next.
*/
- if (entry->delta) {
+ if (DELTA(entry)) {
struct unpacked swap = array[best_base];
int dist = (window + idx - best_base) % window;
int dst = best_base;
@@ -2371,7 +2480,7 @@ static void add_tag_chain(const struct object_id *oid)
die("unable to pack objects reachable from tag %s",
oid_to_hex(oid));
- add_object_entry(tag->object.oid.hash, OBJ_TAG, NULL, 0);
+ add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0);
if (tag->tagged->type != OBJ_TAG)
return;
@@ -2385,7 +2494,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
struct object_id peeled;
if (starts_with(path, "refs/tags/") && /* is a tag? */
- !peel_ref(path, peeled.hash) && /* peelable? */
+ !peel_ref(path, &peeled) && /* peelable? */
packlist_find(&to_pack, peeled.hash, NULL)) /* object packed? */
add_tag_chain(oid);
return 0;
@@ -2418,13 +2527,14 @@ static void prepare_pack(int window, int depth)
for (i = 0; i < to_pack.nr_objects; i++) {
struct object_entry *entry = to_pack.objects + i;
- if (entry->delta)
+ if (DELTA(entry))
/* This happens if we decided to reuse existing
* delta from a pack. "reuse_delta &&" is implied.
*/
continue;
- if (entry->size < 50)
+ if (!entry->type_valid ||
+ oe_size_less_than(&to_pack, entry, 50))
continue;
if (entry->no_try_delta)
@@ -2432,11 +2542,11 @@ static void prepare_pack(int window, int depth)
if (!entry->preferred_base) {
nr_deltas++;
- if (entry->type < 0)
+ if (oe_type(entry) < 0)
die("unable to get type of object %s",
oid_to_hex(&entry->idx.oid));
} else {
- if (entry->type < 0) {
+ if (oe_type(entry) < 0) {
/*
* This object is not found, but we
* don't have to include it anyway.
@@ -2519,8 +2629,9 @@ static int git_pack_config(const char *k, const char *v, void *cb)
static void read_object_list_from_stdin(void)
{
- char line[40 + 1 + PATH_MAX + 2];
- unsigned char sha1[20];
+ char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
+ struct object_id oid;
+ const char *p;
for (;;) {
if (!fgets(line, sizeof(line), stdin)) {
@@ -2534,25 +2645,26 @@ static void read_object_list_from_stdin(void)
continue;
}
if (line[0] == '-') {
- if (get_sha1_hex(line+1, sha1))
- die("expected edge sha1, got garbage:\n %s",
+ if (get_oid_hex(line+1, &oid))
+ die("expected edge object ID, got garbage:\n %s",
line);
- add_preferred_base(sha1);
+ add_preferred_base(&oid);
continue;
}
- if (get_sha1_hex(line, sha1))
- die("expected sha1, got garbage:\n %s", line);
+ if (parse_oid_hex(line, &oid, &p))
+ die("expected object ID, got garbage:\n %s", line);
- add_preferred_base_object(line+41);
- add_object_entry(sha1, 0, line+41, 0);
+ add_preferred_base_object(p + 1);
+ add_object_entry(&oid, OBJ_NONE, p + 1, 0);
}
}
+/* Remember to update object flag allocation in object.h */
#define OBJECT_ADDED (1u<<20)
static void show_commit(struct commit *commit, void *data)
{
- add_object_entry(commit->object.oid.hash, OBJ_COMMIT, NULL, 0);
+ add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
commit->object.flags |= OBJECT_ADDED;
if (write_bitmap_index)
@@ -2562,7 +2674,7 @@ static void show_commit(struct commit *commit, void *data)
static void show_object(struct object *obj, const char *name, void *data)
{
add_preferred_base_object(name);
- add_object_entry(obj->oid.hash, obj->type, name, 0);
+ add_object_entry(&obj->oid, obj->type, name, 0);
obj->flags |= OBJECT_ADDED;
}
@@ -2626,7 +2738,7 @@ static int option_parse_missing_action(const struct option *opt,
static void show_edge(struct commit *commit)
{
- add_preferred_base(commit->object.oid.hash);
+ add_preferred_base(&commit->object.oid);
}
struct in_pack_object {
@@ -2672,11 +2784,11 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
memset(&in_pack, 0, sizeof(in_pack));
- for (p = packed_git; p; p = p->next) {
- const unsigned char *sha1;
+ for (p = get_packed_git(the_repository); p; p = p->next) {
+ struct object_id oid;
struct object *o;
- if (!p->pack_local || p->pack_keep)
+ if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
continue;
if (open_pack_index(p))
die("cannot open pack index");
@@ -2686,8 +2798,8 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
in_pack.alloc);
for (i = 0; i < p->num_objects; i++) {
- sha1 = nth_packed_object_sha1(p, i);
- o = lookup_unknown_object(sha1);
+ nth_packed_object_oid(&oid, p, i);
+ o = lookup_unknown_object(oid.hash);
if (!(o->flags & OBJECT_ADDED))
mark_in_pack_object(o, p, &in_pack);
o->flags |= OBJECT_ADDED;
@@ -2698,7 +2810,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
QSORT(in_pack.array, in_pack.nr, ofscmp);
for (i = 0; i < in_pack.nr; i++) {
struct object *o = in_pack.array[i].object;
- add_object_entry(o->oid.hash, o->type, "", 0);
+ add_object_entry(&o->oid, o->type, "", 0);
}
}
free(in_pack.array);
@@ -2707,14 +2819,14 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
static int add_loose_object(const struct object_id *oid, const char *path,
void *data)
{
- enum object_type type = sha1_object_info(oid->hash, NULL);
+ enum object_type type = oid_object_info(the_repository, oid, NULL);
if (type < 0) {
warning("loose object at %s could not be examined", path);
return 0;
}
- add_object_entry(oid->hash, type, "", 0);
+ add_object_entry(oid, type, "", 0);
return 0;
}
@@ -2730,21 +2842,23 @@ static void add_unreachable_loose_objects(void)
NULL, NULL, NULL);
}
-static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
+static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
{
static struct packed_git *last_found = (void *)1;
struct packed_git *p;
- p = (last_found != (void *)1) ? last_found : packed_git;
+ p = (last_found != (void *)1) ? last_found :
+ get_packed_git(the_repository);
while (p) {
- if ((!p->pack_local || p->pack_keep) &&
- find_pack_entry_one(sha1, p)) {
+ if ((!p->pack_local || p->pack_keep ||
+ p->pack_keep_in_core) &&
+ find_pack_entry_one(oid->hash, p)) {
last_found = p;
return 1;
}
if (p == last_found)
- p = packed_git;
+ p = get_packed_git(the_repository);
else
p = p->next;
if (p == last_found)
@@ -2780,8 +2894,8 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
uint32_t i;
struct object_id oid;
- for (p = packed_git; p; p = p->next) {
- if (!p->pack_local || p->pack_keep)
+ for (p = get_packed_git(the_repository); p; p = p->next) {
+ if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
continue;
if (open_pack_index(p))
@@ -2790,9 +2904,9 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
if (!packlist_find(&to_pack, oid.hash, NULL) &&
- !has_sha1_pack_kept_or_nonlocal(oid.hash) &&
+ !has_sha1_pack_kept_or_nonlocal(&oid) &&
!loosened_object_can_be_discarded(&oid, p->mtime))
- if (force_object_loose(oid.hash, p->mtime))
+ if (force_object_loose(&oid, p->mtime))
die("unable to force loose object");
}
}
@@ -2807,7 +2921,8 @@ static int pack_options_allow_reuse(void)
{
return pack_to_stdout &&
allow_ofs_delta &&
- !ignore_packed_keep &&
+ !ignore_packed_keep_on_disk &&
+ !ignore_packed_keep_in_core &&
(!local || !have_non_local_packs) &&
!incremental;
}
@@ -2916,6 +3031,32 @@ static void get_object_list(int ac, const char **av)
oid_array_clear(&recent_objects);
}
+static void add_extra_kept_packs(const struct string_list *names)
+{
+ struct packed_git *p;
+
+ if (!names->nr)
+ return;
+
+ for (p = get_packed_git(the_repository); p; p = p->next) {
+ const char *name = basename(p->pack_name);
+ int i;
+
+ if (!p->pack_local)
+ continue;
+
+ for (i = 0; i < names->nr; i++)
+ if (!fspathcmp(name, names->items[i].string))
+ break;
+
+ if (i < names->nr) {
+ p->pack_keep_in_core = 1;
+ ignore_packed_keep_in_core = 1;
+ continue;
+ }
+ }
+}
+
static int option_parse_index_version(const struct option *opt,
const char *arg, int unset)
{
@@ -2955,6 +3096,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
struct argv_array rp = ARGV_ARRAY_INIT;
int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
int rev_list_index = 0;
+ struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
struct option pack_objects_options[] = {
OPT_SET_INT('q', "quiet", &progress,
N_("do not show progress meter"), 0),
@@ -2992,18 +3134,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
N_("do not create an empty pack output")),
OPT_BOOL(0, "revs", &use_internal_rev_list,
N_("read revision arguments from standard input")),
- { OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL,
- N_("limit the objects to those that are not yet packed"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
- { OPTION_SET_INT, 0, "all", &rev_list_all, NULL,
- N_("include objects reachable from any reference"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
- { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
- N_("include objects referred by reflog entries"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
- { OPTION_SET_INT, 0, "indexed-objects", &rev_list_index, NULL,
- N_("include objects referred to by the index"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+ OPT_SET_INT_F(0, "unpacked", &rev_list_unpacked,
+ N_("limit the objects to those that are not yet packed"),
+ 1, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "all", &rev_list_all,
+ N_("include objects reachable from any reference"),
+ 1, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "reflog", &rev_list_reflog,
+ N_("include objects referred by reflog entries"),
+ 1, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "indexed-objects", &rev_list_index,
+ N_("include objects referred to by the index"),
+ 1, PARSE_OPT_NONEG),
OPT_BOOL(0, "stdout", &pack_to_stdout,
N_("output pack to stdout")),
OPT_BOOL(0, "include-tag", &include_tag,
@@ -3019,8 +3161,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
N_("create thin packs")),
OPT_BOOL(0, "shallow", &shallow,
N_("create packs suitable for shallow fetches")),
- OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
+ OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep_on_disk,
N_("ignore packs that have companion .keep file")),
+ OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
+ N_("ignore this pack")),
OPT_INTEGER(0, "compression", &pack_compression_level,
N_("pack compression level")),
OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
@@ -3038,6 +3182,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS))
+ BUG("too many dfs states, increase OE_DFS_STATE_BITS");
+
check_replace_refs = 0;
reset_pack_idx_option(&pack_idx_opts);
@@ -3054,6 +3201,17 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (pack_to_stdout != !base_name || argc)
usage_with_options(pack_usage, pack_objects_options);
+ if (depth >= (1 << OE_DEPTH_BITS)) {
+ warning(_("delta chain depth %d is too deep, forcing %d"),
+ depth, (1 << OE_DEPTH_BITS) - 1);
+ depth = (1 << OE_DEPTH_BITS) - 1;
+ }
+ if (cache_max_small_delta_size >= (1U << OE_Z_DELTA_BITS)) {
+ warning(_("pack.deltaCacheLimit is too high, forcing %d"),
+ (1U << OE_Z_DELTA_BITS) - 1);
+ cache_max_small_delta_size = (1U << OE_Z_DELTA_BITS) - 1;
+ }
+
argv_array_push(&rp, "pack-objects");
if (thin) {
use_internal_rev_list = 1;
@@ -3085,6 +3243,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
fetch_if_missing = 0;
argv_array_push(&rp, "--exclude-promisor-objects");
}
+ if (unpack_unreachable || keep_unreachable || pack_loose_unreachable)
+ use_internal_rev_list = 1;
if (!reuse_object)
reuse_delta = 0;
@@ -3148,23 +3308,23 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (progress && all_progress_implied)
progress = 2;
- prepare_packed_git();
- if (ignore_packed_keep) {
+ add_extra_kept_packs(&keep_pack_list);
+ if (ignore_packed_keep_on_disk) {
struct packed_git *p;
- for (p = packed_git; p; p = p->next)
+ for (p = get_packed_git(the_repository); p; p = p->next)
if (p->pack_local && p->pack_keep)
break;
if (!p) /* no keep-able packs found */
- ignore_packed_keep = 0;
+ ignore_packed_keep_on_disk = 0;
}
if (local) {
/*
- * unlike ignore_packed_keep above, we do not want to
- * unset "local" based on looking at packs, as it
- * also covers non-local objects
+ * unlike ignore_packed_keep_on_disk above, we do not
+ * want to unset "local" based on looking at packs, as
+ * it also covers non-local objects
*/
struct packed_git *p;
- for (p = packed_git; p; p = p->next) {
+ for (p = get_packed_git(the_repository); p; p = p->next) {
if (!p->pack_local) {
have_non_local_packs = 1;
break;
@@ -3172,8 +3332,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
}
+ prepare_packing_data(&to_pack);
+
if (progress)
- progress_state = start_progress(_("Counting objects"), 0);
+ progress_state = start_progress(_("Enumerating objects"), 0);
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index aaa8136322..0494dceff7 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -7,7 +7,9 @@
*/
#include "builtin.h"
+#include "repository.h"
#include "packfile.h"
+#include "object-store.h"
#define BLKSIZE 512
@@ -18,7 +20,7 @@ static int load_all_packs, verbose, alt_odb;
struct llist_item {
struct llist_item *next;
- const unsigned char *sha1;
+ const struct object_id *oid;
};
static struct llist {
struct llist_item *front;
@@ -48,17 +50,17 @@ static inline void llist_item_put(struct llist_item *item)
static inline struct llist_item *llist_item_get(void)
{
- struct llist_item *new;
+ struct llist_item *new_item;
if ( free_nodes ) {
- new = free_nodes;
+ new_item = free_nodes;
free_nodes = free_nodes->next;
} else {
int i = 1;
- ALLOC_ARRAY(new, BLKSIZE);
+ ALLOC_ARRAY(new_item, BLKSIZE);
for (; i < BLKSIZE; i++)
- llist_item_put(&new[i]);
+ llist_item_put(&new_item[i]);
}
- return new;
+ return new_item;
}
static void llist_free(struct llist *list)
@@ -80,70 +82,70 @@ static inline void llist_init(struct llist **list)
static struct llist * llist_copy(struct llist *list)
{
struct llist *ret;
- struct llist_item *new, *old, *prev;
+ struct llist_item *new_item, *old_item, *prev;
llist_init(&ret);
if ((ret->size = list->size) == 0)
return ret;
- new = ret->front = llist_item_get();
- new->sha1 = list->front->sha1;
+ new_item = ret->front = llist_item_get();
+ new_item->oid = list->front->oid;
- old = list->front->next;
- while (old) {
- prev = new;
- new = llist_item_get();
- prev->next = new;
- new->sha1 = old->sha1;
- old = old->next;
+ old_item = list->front->next;
+ while (old_item) {
+ prev = new_item;
+ new_item = llist_item_get();
+ prev->next = new_item;
+ new_item->oid = old_item->oid;
+ old_item = old_item->next;
}
- new->next = NULL;
- ret->back = new;
+ new_item->next = NULL;
+ ret->back = new_item;
return ret;
}
static inline struct llist_item *llist_insert(struct llist *list,
struct llist_item *after,
- const unsigned char *sha1)
+ const struct object_id *oid)
{
- struct llist_item *new = llist_item_get();
- new->sha1 = sha1;
- new->next = NULL;
+ struct llist_item *new_item = llist_item_get();
+ new_item->oid = oid;
+ new_item->next = NULL;
if (after != NULL) {
- new->next = after->next;
- after->next = new;
+ new_item->next = after->next;
+ after->next = new_item;
if (after == list->back)
- list->back = new;
+ list->back = new_item;
} else {/* insert in front */
if (list->size == 0)
- list->back = new;
+ list->back = new_item;
else
- new->next = list->front;
- list->front = new;
+ new_item->next = list->front;
+ list->front = new_item;
}
list->size++;
- return new;
+ return new_item;
}
static inline struct llist_item *llist_insert_back(struct llist *list,
- const unsigned char *sha1)
+ const struct object_id *oid)
{
- return llist_insert(list, list->back, sha1);
+ return llist_insert(list, list->back, oid);
}
static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
- const unsigned char *sha1, struct llist_item *hint)
+ const struct object_id *oid, struct llist_item *hint)
{
struct llist_item *prev = NULL, *l;
l = (hint == NULL) ? list->front : hint;
while (l) {
- int cmp = hashcmp(l->sha1, sha1);
+ int cmp = oidcmp(l->oid, oid);
if (cmp > 0) { /* we insert before this entry */
- return llist_insert(list, prev, sha1);
+ return llist_insert(list, prev, oid);
}
if (!cmp) { /* already exists */
return l;
@@ -152,11 +154,11 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
l = l->next;
}
/* insert at the end */
- return llist_insert_back(list, sha1);
+ return llist_insert_back(list, oid);
}
/* returns a pointer to an item in front of sha1 */
-static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint)
+static inline struct llist_item * llist_sorted_remove(struct llist *list, const struct object_id *oid, struct llist_item *hint)
{
struct llist_item *prev, *l;
@@ -164,7 +166,7 @@ redo_from_start:
l = (hint == NULL) ? list->front : hint;
prev = NULL;
while (l) {
- int cmp = hashcmp(l->sha1, sha1);
+ int cmp = oidcmp(l->oid, oid);
if (cmp > 0) /* not in list, since sorted */
return prev;
if (!cmp) { /* found */
@@ -199,7 +201,7 @@ static void llist_sorted_difference_inplace(struct llist *A,
b = B->front;
while (b) {
- hint = llist_sorted_remove(A, b->sha1, hint);
+ hint = llist_sorted_remove(A, b->oid, hint);
b = b->next;
}
}
@@ -250,13 +252,14 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
const unsigned char *p1_base, *p2_base;
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
+ const unsigned int hashsz = the_hash_algo->rawsz;
p1_base = p1->pack->index_data;
p2_base = p2->pack->index_data;
p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
- p1_step = (p1->pack->index_version < 2) ? 24 : 20;
- p2_step = (p2->pack->index_version < 2) ? 24 : 20;
+ p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0);
+ p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0);
while (p1_off < p1->pack->num_objects * p1_step &&
p2_off < p2->pack->num_objects * p2_step)
@@ -265,9 +268,11 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
/* cmp ~ p1 - p2 */
if (cmp == 0) {
p1_hint = llist_sorted_remove(p1->unique_objects,
- p1_base + p1_off, p1_hint);
+ (const struct object_id *)(p1_base + p1_off),
+ p1_hint);
p2_hint = llist_sorted_remove(p2->unique_objects,
- p1_base + p1_off, p2_hint);
+ (const struct object_id *)(p1_base + p1_off),
+ p2_hint);
p1_off += p1_step;
p2_off += p2_step;
continue;
@@ -357,13 +362,14 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
size_t ret = 0;
unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
const unsigned char *p1_base, *p2_base;
+ const unsigned int hashsz = the_hash_algo->rawsz;
p1_base = p1->index_data;
p2_base = p2->index_data;
p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
- p1_step = (p1->index_version < 2) ? 24 : 20;
- p2_step = (p2->index_version < 2) ? 24 : 20;
+ p1_step = hashsz + ((p1->index_version < 2) ? 4 : 0);
+ p2_step = hashsz + ((p2->index_version < 2) ? 4 : 0);
while (p1_off < p1->num_objects * p1_step &&
p2_off < p2->num_objects * p2_step)
@@ -497,7 +503,7 @@ static void load_all_objects(void)
l = pl->all_objects->front;
while (l) {
hint = llist_insert_sorted_unique(all_objects,
- l->sha1, hint);
+ l->oid, hint);
l = l->next;
}
pl = pl->next;
@@ -556,9 +562,9 @@ static struct pack_list * add_pack(struct packed_git *p)
base = p->index_data;
base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
- step = (p->index_version < 2) ? 24 : 20;
+ step = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0);
while (off < p->num_objects * step) {
- llist_insert_back(l.all_objects, base + off);
+ llist_insert_back(l.all_objects, (const struct object_id *)(base + off));
off += step;
}
/* this list will be pruned in cmp_two_packs later */
@@ -571,7 +577,7 @@ static struct pack_list * add_pack(struct packed_git *p)
static struct pack_list * add_pack_file(const char *filename)
{
- struct packed_git *p = packed_git;
+ struct packed_git *p = get_packed_git(the_repository);
if (strlen(filename) < 40)
die("Bad pack filename: %s", filename);
@@ -586,7 +592,7 @@ static struct pack_list * add_pack_file(const char *filename)
static void load_all(void)
{
- struct packed_git *p = packed_git;
+ struct packed_git *p = get_packed_git(the_repository);
while (p) {
add_pack(p);
@@ -599,8 +605,8 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
int i;
struct pack_list *min, *red, *pl;
struct llist *ignore;
- unsigned char *sha1;
- char buf[42]; /* 40 byte sha1 + \n + \0 */
+ struct object_id *oid;
+ char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(pack_redundant_usage);
@@ -629,8 +635,6 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
break;
}
- prepare_packed_git();
-
if (load_all_packs)
load_all();
else
@@ -650,10 +654,10 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
llist_init(&ignore);
if (!isatty(0)) {
while (fgets(buf, sizeof(buf), stdin)) {
- sha1 = xmalloc(20);
- if (get_sha1_hex(buf, sha1))
- die("Bad sha1 on stdin: %s", buf);
- llist_insert_sorted_unique(ignore, sha1, NULL);
+ oid = xmalloc(sizeof(*oid));
+ if (get_oid_hex(buf, oid))
+ die("Bad object ID on stdin: %s", buf);
+ llist_insert_sorted_unique(ignore, oid, NULL);
}
}
llist_sorted_difference_inplace(all_objects, ignore);
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index b106a392a4..f3353564f9 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "parse-options.h"
#include "refs.h"
+#include "repository.h"
static char const * const pack_refs_usage[] = {
N_("git pack-refs [<options>]"),
@@ -17,5 +18,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
};
if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
usage_with_options(pack_refs_usage, opts);
- return refs_pack_refs(get_main_ref_store(), flags);
+ return refs_pack_refs(get_main_ref_store(the_repository), flags);
}
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index 419238171d..4ff525e50f 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -25,7 +25,7 @@ static int prune_object(const struct object_id *oid, const char *path,
{
int *opts = data;
- if (!has_sha1_pack(oid->hash))
+ if (!has_object_pack(oid))
return 0;
if (*opts & PRUNE_PACKED_DRY_RUN)
diff --git a/builtin/prune.c b/builtin/prune.c
index be34645dcf..518ffbea13 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -8,7 +8,7 @@
#include "progress.h"
static const char * const prune_usage[] = {
- N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
+ N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
NULL
};
static int show_only;
@@ -50,9 +50,10 @@ static int prune_object(const struct object_id *oid, const char *fullpath,
if (st.st_mtime > expire)
return 0;
if (show_only || verbose) {
- enum object_type type = sha1_object_info(oid->hash, NULL);
+ enum object_type type = oid_object_info(the_repository, oid,
+ NULL);
printf("%s %s\n", oid_to_hex(oid),
- (type > 0) ? typename(type) : "unknown");
+ (type > 0) ? type_name(type) : "unknown");
}
if (!show_only)
unlink_or_warn(fullpath);
diff --git a/builtin/pull.c b/builtin/pull.c
index 6f772e8a22..fe002a72f8 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -9,12 +9,13 @@
#include "config.h"
#include "builtin.h"
#include "parse-options.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "run-command.h"
#include "sha1-array.h"
#include "remote.h"
#include "dir.h"
#include "refs.h"
+#include "refspec.h"
#include "revision.h"
#include "submodule.h"
#include "submodule-config.h"
@@ -27,14 +28,16 @@ enum rebase_type {
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
- * "preserve", returns REBASE_PRESERVE. If value is a invalid value, dies with
- * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
+ * "merges", returns REBASE_MERGES. If value is "preserve", returns
+ * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if
+ * fatal is true, otherwise returns REBASE_INVALID.
*/
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
@@ -47,6 +50,8 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,
return REBASE_TRUE;
else if (!strcmp(value, "preserve"))
return REBASE_PRESERVE;
+ else if (!strcmp(value, "merges"))
+ return REBASE_MERGES;
else if (!strcmp(value, "interactive"))
return REBASE_INTERACTIVE;
@@ -86,6 +91,7 @@ static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static enum rebase_type opt_rebase = -1;
static char *opt_diffstat;
static char *opt_log;
+static char *opt_signoff;
static char *opt_squash;
static char *opt_commit;
static char *opt_edit;
@@ -112,6 +118,8 @@ static char *opt_depth;
static char *opt_unshallow;
static char *opt_update_shallow;
static char *opt_refmap;
+static char *opt_ipv4;
+static char *opt_ipv6;
static struct option pull_options[] = {
/* Shared options */
@@ -127,7 +135,7 @@ static struct option pull_options[] = {
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
{ OPTION_CALLBACK, 'r', "rebase", &opt_rebase,
- "false|true|preserve|interactive",
+ "false|true|merges|preserve|interactive",
N_("incorporate changes by rebasing rather than merging"),
PARSE_OPT_OPTARG, parse_opt_rebase },
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
@@ -142,6 +150,9 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
N_("add (at most <n>) entries from shortlog to merge commit message"),
PARSE_OPT_OPTARG),
+ OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
+ N_("add Signed-off-by:"),
+ PARSE_OPT_OPTARG),
OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
N_("create a single commit instead of doing a merge"),
PARSE_OPT_NOARG),
@@ -187,7 +198,7 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
N_("path to upload pack on remote end"),
0),
- OPT__FORCE(&opt_force, N_("force overwrite of local branch")),
+ OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0),
OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
N_("fetch all tags and associated objects"),
PARSE_OPT_NOARG),
@@ -214,6 +225,12 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
N_("specify fetch refmap"),
PARSE_OPT_NONEG),
+ OPT_PASSTHRU('4', "ipv4", &opt_ipv4, NULL,
+ N_("use IPv4 addresses only"),
+ PARSE_OPT_NOARG),
+ OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
+ N_("use IPv6 addresses only"),
+ PARSE_OPT_NOARG),
OPT_END()
};
@@ -518,12 +535,16 @@ static int run_fetch(const char *repo, const char **refspecs)
argv_array_push(&args, opt_update_shallow);
if (opt_refmap)
argv_array_push(&args, opt_refmap);
+ if (opt_ipv4)
+ argv_array_push(&args, opt_ipv4);
+ if (opt_ipv6)
+ argv_array_push(&args, opt_ipv6);
if (repo) {
argv_array_push(&args, repo);
argv_array_pushv(&args, refspecs);
} else if (*refspecs)
- die("BUG: refspecs without repo?");
+ BUG("refspecs without repo?");
ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
argv_array_clear(&args);
return ret;
@@ -541,10 +562,10 @@ static int pull_into_void(const struct object_id *merge_head,
* index/worktree changes that the user already made on the unborn
* branch.
*/
- if (checkout_fast_forward(&empty_tree_oid, merge_head, 0))
+ if (checkout_fast_forward(the_hash_algo->empty_tree, merge_head, 0))
return 1;
- if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR))
+ if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
return 1;
return 0;
@@ -558,6 +579,7 @@ static int rebase_submodules(void)
cp.no_stdin = 1;
argv_array_pushl(&cp.args, "submodule", "update",
"--recursive", "--rebase", NULL);
+ argv_push_verbosity(&cp.args);
return run_command(&cp);
}
@@ -570,6 +592,7 @@ static int update_submodules(void)
cp.no_stdin = 1;
argv_array_pushl(&cp.args, "submodule", "update",
"--recursive", "--checkout", NULL);
+ argv_push_verbosity(&cp.args);
return run_command(&cp);
}
@@ -594,6 +617,8 @@ static int run_merge(void)
argv_array_push(&args, opt_diffstat);
if (opt_log)
argv_array_push(&args, opt_log);
+ if (opt_signoff)
+ argv_array_push(&args, opt_signoff);
if (opt_squash)
argv_array_push(&args, opt_squash);
if (opt_commit)
@@ -648,19 +673,19 @@ static const char *get_upstream_branch(const char *remote)
}
/**
- * Derives the remote tracking branch from the remote and refspec.
+ * Derives the remote-tracking branch from the remote and refspec.
*
* FIXME: The current implementation assumes the default mapping of
* refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>.
*/
static const char *get_tracking_branch(const char *remote, const char *refspec)
{
- struct refspec *spec;
+ struct refspec_item spec;
const char *spec_src;
const char *merge_branch;
- spec = parse_fetch_refspec(1, &refspec);
- spec_src = spec->src;
+ refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);
+ spec_src = spec.src;
if (!*spec_src || !strcmp(spec_src, "HEAD"))
spec_src = "HEAD";
else if (skip_prefix(spec_src, "heads/", &spec_src))
@@ -680,13 +705,13 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)
} else
merge_branch = NULL;
- free_refspec(1, spec);
+ refspec_item_clear(&spec);
return merge_branch;
}
/**
* Given the repo and refspecs, sets fork_point to the point at which the
- * current branch forked from its remote tracking branch. Returns 0 on success,
+ * current branch forked from its remote-tracking branch. Returns 0 on success,
* -1 on failure.
*/
static int get_rebase_fork_point(struct object_id *fork_point, const char *repo,
@@ -745,12 +770,15 @@ static int get_octopus_merge_base(struct object_id *merge_base,
if (!is_null_oid(fork_point))
commit_list_insert(lookup_commit_reference(fork_point), &revs);
- result = reduce_heads(get_octopus_merge_bases(revs));
+ result = get_octopus_merge_bases(revs);
free_commit_list(revs);
+ reduce_heads_replace(&result);
+
if (!result)
return 1;
oidcpy(merge_base, &result->item->object.oid);
+ free_commit_list(result);
return 0;
}
@@ -777,7 +805,9 @@ static int run_rebase(const struct object_id *curr_head,
argv_push_verbosity(&args);
/* Options passed to git-rebase */
- if (opt_rebase == REBASE_PRESERVE)
+ if (opt_rebase == REBASE_MERGES)
+ argv_array_push(&args, "--rebase-merges");
+ else if (opt_rebase == REBASE_PRESERVE)
argv_array_push(&args, "--preserve-merges");
else if (opt_rebase == REBASE_INTERACTIVE)
argv_array_push(&args, "--interactive");
diff --git a/builtin/push.c b/builtin/push.c
index 2ac8104229..9cd8e8cd56 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "config.h"
#include "refs.h"
+#include "refspec.h"
#include "run-command.h"
#include "builtin.h"
#include "remote.h"
@@ -12,12 +13,40 @@
#include "submodule.h"
#include "submodule-config.h"
#include "send-pack.h"
+#include "color.h"
static const char * const push_usage[] = {
N_("git push [<options>] [<repository> [<refspec>...]]"),
NULL,
};
+static int push_use_color = -1;
+static char push_colors[][COLOR_MAXLEN] = {
+ GIT_COLOR_RESET,
+ GIT_COLOR_RED, /* ERROR */
+};
+
+enum color_push {
+ PUSH_COLOR_RESET = 0,
+ PUSH_COLOR_ERROR = 1
+};
+
+static int parse_push_color_slot(const char *slot)
+{
+ if (!strcasecmp(slot, "reset"))
+ return PUSH_COLOR_RESET;
+ if (!strcasecmp(slot, "error"))
+ return PUSH_COLOR_ERROR;
+ return -1;
+}
+
+static const char *push_get_color(enum color_push ix)
+{
+ if (want_color_stderr(push_use_color))
+ return push_colors[ix];
+ return "";
+}
+
static int thin = 1;
static int deleterefs;
static const char *receivepack;
@@ -28,16 +57,9 @@ static enum transport_family family;
static struct push_cas_option cas;
-static const char **refspec;
-static int refspec_nr;
-static int refspec_alloc;
+static struct refspec rs = REFSPEC_INIT_PUSH;
-static void add_refspec(const char *ref)
-{
- refspec_nr++;
- ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
- refspec[refspec_nr-1] = ref;
-}
+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)
@@ -48,12 +70,11 @@ static const char *map_refspec(const char *ref,
if (count_refspec_match(ref, local_refs, &matched) != 1)
return ref;
- if (remote->push) {
- struct refspec query;
- memset(&query, 0, sizeof(struct refspec));
+ if (remote->push.nr) {
+ struct refspec_item query;
+ memset(&query, 0, sizeof(struct refspec_item));
query.src = matched->name;
- if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
- query.dst) {
+ if (!query_refspecs(&remote->push, &query) && query.dst) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s%s:%s",
query.force ? "+" : "",
@@ -108,7 +129,7 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
}
ref = map_refspec(ref, remote, local_refs);
}
- add_refspec(ref);
+ refspec_append(&rs, ref);
}
}
@@ -196,7 +217,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
}
strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src);
- add_refspec(refspec.buf);
+ refspec_append(&rs, refspec.buf);
}
static void setup_push_current(struct remote *remote, struct branch *branch)
@@ -206,7 +227,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch)
if (!branch)
die(_(message_detached_head_die), remote->name);
strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname);
- add_refspec(refspec.buf);
+ refspec_append(&rs, refspec.buf);
}
static int is_workflow_triangular(struct remote *remote)
@@ -223,7 +244,7 @@ static void setup_default_push_refspecs(struct remote *remote)
switch (push_default) {
default:
case PUSH_DEFAULT_MATCHING:
- add_refspec(":");
+ refspec_append(&rs, ":");
break;
case PUSH_DEFAULT_UNSPECIFIED:
@@ -311,7 +332,8 @@ static void advise_ref_needs_force(void)
advise(_(message_advice_ref_needs_force));
}
-static int push_with_options(struct transport *transport, int flags)
+static int push_with_options(struct transport *transport, struct refspec *rs,
+ int flags)
{
int err;
unsigned int reject_reasons;
@@ -333,10 +355,12 @@ static int push_with_options(struct transport *transport, int flags)
if (verbosity > 0)
fprintf(stderr, _("Pushing to %s\n"), transport->url);
- err = transport_push(transport, refspec_nr, refspec, flags,
- &reject_reasons);
- if (err != 0)
+ err = transport_push(transport, rs, flags, &reject_reasons);
+ if (err != 0) {
+ fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
error(_("failed to push some refs to '%s'"), transport->url);
+ fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET));
+ }
err |= transport_disconnect(transport);
if (!err)
@@ -364,6 +388,7 @@ static int do_push(const char *repo, int flags,
struct remote *remote = pushremote_get(repo);
const char **url;
int url_nr;
+ struct refspec *push_refspec = &rs;
if (!remote) {
if (repo)
@@ -384,27 +409,9 @@ static int do_push(const char *repo, int flags,
if (push_options->nr)
flags |= TRANSPORT_PUSH_OPTIONS;
- if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
- if (!strcmp(*refspec, "refs/tags/*"))
- return error(_("--all and --tags are incompatible"));
- return error(_("--all can't be combined with refspecs"));
- }
-
- if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
- if (!strcmp(*refspec, "refs/tags/*"))
- return error(_("--mirror and --tags are incompatible"));
- return error(_("--mirror can't be combined with refspecs"));
- }
-
- if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
- (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
- return error(_("--all and --mirror are incompatible"));
- }
-
- if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
- if (remote->push_refspec_nr) {
- refspec = remote->push_refspec;
- refspec_nr = remote->push_refspec_nr;
+ if (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) {
+ if (remote->push.nr) {
+ push_refspec = &remote->push;
} else if (!(flags & TRANSPORT_PUSH_MIRROR))
setup_default_push_refspecs(remote);
}
@@ -416,7 +423,7 @@ static int do_push(const char *repo, int flags,
transport_get(remote, url[i]);
if (flags & TRANSPORT_PUSH_OPTIONS)
transport->push_options = push_options;
- if (push_with_options(transport, flags))
+ if (push_with_options(transport, push_refspec, flags))
errs++;
}
} else {
@@ -424,7 +431,7 @@ static int do_push(const char *repo, int flags,
transport_get(remote, NULL);
if (flags & TRANSPORT_PUSH_OPTIONS)
transport->push_options = push_options;
- if (push_with_options(transport, flags))
+ if (push_with_options(transport, push_refspec, flags))
errs++;
}
return !!errs;
@@ -465,6 +472,7 @@ static void set_push_cert_flags(int *flags, int v)
static int git_push_config(const char *k, const char *v, void *cb)
{
+ const char *slot_name;
int *flags = cb;
int status;
@@ -503,6 +511,25 @@ static int git_push_config(const char *k, const char *v, void *cb)
int val = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
recurse_submodules = val;
+ } else if (!strcmp(k, "push.pushoption")) {
+ if (!v)
+ return config_error_nonbool(k);
+ else
+ if (!*v)
+ string_list_clear(&push_options_config, 0);
+ else
+ string_list_append(&push_options_config, v);
+ return 0;
+ } else if (!strcmp(k, "color.push")) {
+ push_use_color = git_config_colorbool(k, v);
+ return 0;
+ } else if (skip_prefix(k, "color.push.", &slot_name)) {
+ int slot = parse_push_color_slot(slot_name);
+ if (slot < 0)
+ return 0;
+ if (!v)
+ return config_error_nonbool(k);
+ return color_parse(v, push_colors[slot]);
}
return git_default_config(k, v, NULL);
@@ -515,7 +542,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
int push_cert = -1;
int rc;
const char *repo = NULL; /* default repository */
- struct string_list push_options = STRING_LIST_INIT_DUP;
+ struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
+ struct string_list *push_options;
const struct string_list_item *item;
struct option options[] = {
@@ -536,7 +564,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "check|on-demand|no",
N_("control recursive pushing of submodules"),
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
- OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")),
+ OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE),
OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")),
OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"),
@@ -551,7 +579,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
PARSE_OPT_OPTARG, option_parse_push_signed },
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
- OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
+ OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -562,12 +590,29 @@ int cmd_push(int argc, const char **argv, const char *prefix)
packet_trace_identity("push");
git_config(git_push_config, &flags);
argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+ push_options = (push_options_cmdline.nr
+ ? &push_options_cmdline
+ : &push_options_config);
set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
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;
@@ -577,19 +622,20 @@ int cmd_push(int argc, const char **argv, const char *prefix)
flags |= TRANSPORT_RECURSE_SUBMODULES_ONLY;
if (tags)
- add_refspec("refs/tags/*");
+ refspec_append(&rs, "refs/tags/*");
if (argc > 0) {
repo = argv[0];
set_refspecs(argv + 1, argc - 1, repo);
}
- for_each_string_list_item(item, &push_options)
+ 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);
- string_list_clear(&push_options, 0);
+ rc = do_push(repo, flags, push_options);
+ string_list_clear(&push_options_cmdline, 0);
+ string_list_clear(&push_options_config, 0);
if (rc == -1)
usage_with_options(push_usage, options);
else
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index bf87a2710b..ebc43eb805 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -107,8 +107,6 @@ static int git_read_tree_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-static struct lock_file lock_file;
-
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, stage = 0;
@@ -116,6 +114,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
struct tree_desc t[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
int prefix_set = 0;
+ struct lock_file lock_file = LOCK_INIT;
const struct option read_tree_options[] = {
{ OPTION_CALLBACK, 0, "index-output", NULL, N_("file"),
N_("write resulting index to <file>"),
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f8519363a3..f7c2a5fdc8 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -12,14 +12,21 @@ static const char * const builtin_rebase_helper_usage[] = {
int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
{
struct replay_opts opts = REPLAY_OPTS_INIT;
- int keep_empty = 0;
+ unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
+ int abbreviate_commands = 0, rebase_cousins = -1;
enum {
- CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_SHA1S, EXPAND_SHA1S,
- CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH
+ CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
+ CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
+ ADD_EXEC
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
+ OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
+ N_("allow commits with empty messages")),
+ OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
+ OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
+ N_("keep original branch points of cousins")),
OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
CONTINUE),
OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
@@ -27,19 +34,22 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "make-script", &command,
N_("make rebase script"), MAKE_SCRIPT),
OPT_CMDMODE(0, "shorten-ids", &command,
- N_("shorten SHA-1s in the todo list"), SHORTEN_SHA1S),
+ N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
OPT_CMDMODE(0, "expand-ids", &command,
- N_("expand SHA-1s in the todo list"), EXPAND_SHA1S),
+ N_("expand commit ids in the todo list"), EXPAND_OIDS),
OPT_CMDMODE(0, "check-todo-list", &command,
N_("check the todo list"), CHECK_TODO_LIST),
OPT_CMDMODE(0, "skip-unnecessary-picks", &command,
N_("skip unnecessary picks"), SKIP_UNNECESSARY_PICKS),
OPT_CMDMODE(0, "rearrange-squash", &command,
N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
+ OPT_CMDMODE(0, "add-exec-commands", &command,
+ N_("insert exec commands in todo list"), ADD_EXEC),
OPT_END()
};
- git_config(git_default_config, NULL);
+ sequencer_init_config(&opts);
+ git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
opts.action = REPLAY_INTERACTIVE_REBASE;
opts.allow_ff = 1;
@@ -48,21 +58,31 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, NULL, options,
builtin_rebase_helper_usage, PARSE_OPT_KEEP_ARGV0);
+ flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+ flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
+ flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
+ flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
+ flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+
+ if (rebase_cousins >= 0 && !rebase_merges)
+ warning(_("--[no-]rebase-cousins has no effect without "
+ "--rebase-merges"));
+
if (command == CONTINUE && argc == 1)
return !!sequencer_continue(&opts);
if (command == ABORT && argc == 1)
return !!sequencer_remove_state(&opts);
if (command == MAKE_SCRIPT && argc > 1)
- return !!sequencer_make_script(keep_empty, stdout, argc, argv);
- if (command == SHORTEN_SHA1S && argc == 1)
- return !!transform_todo_ids(1);
- if (command == EXPAND_SHA1S && argc == 1)
- return !!transform_todo_ids(0);
+ return !!sequencer_make_script(stdout, argc, argv, flags);
+ if ((command == SHORTEN_OIDS || command == EXPAND_OIDS) && argc == 1)
+ return !!transform_todos(flags);
if (command == CHECK_TODO_LIST && argc == 1)
return !!check_todo_list();
if (command == SKIP_UNNECESSARY_PICKS && argc == 1)
return !!skip_unnecessary_picks();
if (command == REARRANGE_SQUASH && argc == 1)
return !!rearrange_squash();
+ if (command == ADD_EXEC && argc == 2)
+ return !!sequencer_add_exec_commands(argv[1]);
usage_with_options(builtin_rebase_helper_usage, options);
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index cc48767405..68d36e0a56 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "repository.h"
#include "config.h"
#include "lockfile.h"
#include "pack.h"
@@ -6,7 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
-#include "exec_cmd.h"
+#include "exec-cmd.h"
#include "commit.h"
#include "object.h"
#include "remote.h"
@@ -24,6 +25,7 @@
#include "tmp-objdir.h"
#include "oidset.h"
#include "packfile.h"
+#include "protocol.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
@@ -68,7 +70,7 @@ static int sent_capabilities;
static int shallow_update;
static const char *alt_shallow_file;
static struct strbuf push_cert = STRBUF_INIT;
-static unsigned char push_cert_sha1[20];
+static struct object_id push_cert_oid;
static struct signature_check sigcheck;
static const char *push_cert_nonce;
static const char *cert_nonce_seed;
@@ -452,21 +454,21 @@ static void hmac_sha1(unsigned char *out,
/* RFC 2104 2. (6) & (7) */
git_SHA1_Init(&ctx);
git_SHA1_Update(&ctx, k_opad, sizeof(k_opad));
- git_SHA1_Update(&ctx, out, 20);
+ git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ);
git_SHA1_Final(out, &ctx);
}
static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
{
struct strbuf buf = STRBUF_INIT;
- unsigned char sha1[20];
+ unsigned char sha1[GIT_SHA1_RAWSZ];
strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
hmac_sha1(sha1, 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, 20, sha1_to_hex(sha1));
+ strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1));
return strbuf_detach(&buf, NULL);
}
@@ -632,8 +634,9 @@ static void prepare_push_cert_sha1(struct child_process *proc)
int bogs /* beginning_of_gpg_sig */;
already_done = 1;
- if (write_sha1_file(push_cert.buf, push_cert.len, "blob", push_cert_sha1))
- hashclr(push_cert_sha1);
+ if (write_object_file(push_cert.buf, push_cert.len, "blob",
+ &push_cert_oid))
+ oidclr(&push_cert_oid);
memset(&sigcheck, '\0', sizeof(sigcheck));
sigcheck.result = 'N';
@@ -654,9 +657,9 @@ static void prepare_push_cert_sha1(struct child_process *proc)
strbuf_release(&gpg_status);
nonce_status = check_nonce(push_cert.buf, bogs);
}
- if (!is_null_sha1(push_cert_sha1)) {
+ if (!is_null_oid(&push_cert_oid)) {
argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT=%s",
- sha1_to_hex(push_cert_sha1));
+ oid_to_hex(&push_cert_oid));
argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s",
sigcheck.signer ? sigcheck.signer : "");
argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s",
@@ -870,10 +873,10 @@ static void refuse_unconfigured_deny_delete_current(void)
rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
}
-static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
+static int command_singleton_iterator(void *cb_data, struct object_id *oid);
static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
{
- static struct lock_file shallow_lock;
+ struct lock_file shallow_lock = LOCK_INIT;
struct oid_array extra = OID_ARRAY_INIT;
struct check_connected_options opt = CHECK_CONNECTED_INIT;
uint32_t mask = 1 << (cmd->index % 32);
@@ -965,7 +968,7 @@ static const char *push_to_deploy(unsigned char *sha1,
return "Working directory has unstaged changes";
/* diff-index with either HEAD or an empty tree */
- diff_index[4] = head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX;
+ diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex();
child_process_init(&child);
child.argv = diff_index;
@@ -1139,7 +1142,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
if (ref_transaction_delete(transaction,
namespaced_name,
- old_oid ? old_oid->hash : NULL,
+ old_oid,
0, "push", &err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
@@ -1156,7 +1159,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (ref_transaction_update(transaction,
namespaced_name,
- new_oid->hash, old_oid->hash,
+ new_oid, old_oid,
0, "push",
&err)) {
rp_error("%s", err.buf);
@@ -1240,11 +1243,11 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
" its target '%s' (%s..%s)",
cmd->ref_name,
- find_unique_abbrev(cmd->old_oid.hash, DEFAULT_ABBREV),
- find_unique_abbrev(cmd->new_oid.hash, DEFAULT_ABBREV),
+ find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV),
+ find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV),
dst_cmd->ref_name,
- find_unique_abbrev(dst_cmd->old_oid.hash, DEFAULT_ABBREV),
- find_unique_abbrev(dst_cmd->new_oid.hash, DEFAULT_ABBREV));
+ find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV),
+ find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV));
cmd->error_string = dst_cmd->error_string =
"inconsistent aliased update";
@@ -1270,7 +1273,7 @@ static void check_aliased_updates(struct command *commands)
string_list_clear(&ref_list, 0);
}
-static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
+static int command_singleton_iterator(void *cb_data, struct object_id *oid)
{
struct command **cmd_list = cb_data;
struct command *cmd = *cmd_list;
@@ -1278,7 +1281,7 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
if (!cmd || is_null_oid(&cmd->new_oid))
return -1; /* end of list */
*cmd_list = NULL; /* this returns only one */
- hashcpy(sha1, cmd->new_oid.hash);
+ oidcpy(oid, &cmd->new_oid);
return 0;
}
@@ -1309,7 +1312,7 @@ struct iterate_data {
struct shallow_info *si;
};
-static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
+static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
{
struct iterate_data *data = cb_data;
struct command **cmd_list = &data->cmds;
@@ -1320,7 +1323,7 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
/* to be checked in update_shallow_ref() */
continue;
if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
- hashcpy(sha1, cmd->new_oid.hash);
+ oidcpy(oid, &cmd->new_oid);
*cmd_list = cmd->next;
return 0;
}
@@ -1375,7 +1378,7 @@ static void warn_if_skipped_connectivity_check(struct command *commands,
}
}
if (!checked_connectivity)
- die("BUG: connectivity check skipped???");
+ BUG("connectivity check skipped???");
}
static void execute_commands_non_atomic(struct command *commands,
@@ -1776,7 +1779,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
status = finish_command(&child);
if (status)
return "index-pack abnormal exit";
- reprepare_packed_git();
+ reprepare_packed_git(the_repository);
}
return NULL;
}
@@ -1961,6 +1964,28 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
else if (0 <= receive_unpack_limit)
unpack_limit = receive_unpack_limit;
+ switch (determine_protocol_version_server()) {
+ case protocol_v2:
+ /*
+ * push support for protocol v2 has not been implemented yet,
+ * so ignore the request to use v2 and fallback to using v0.
+ */
+ break;
+ case protocol_v1:
+ /*
+ * v1 is just the original protocol with a version string,
+ * so just fall through after writing the version string.
+ */
+ if (advertise_refs || !stateless_rpc)
+ packet_write_fmt(1, "version 1\n");
+
+ /* fallthrough */
+ case protocol_v0:
+ break;
+ case protocol_unknown_version:
+ BUG("unknown protocol version");
+ }
+
if (advertise_refs || !stateless_rpc) {
write_head_info();
}
@@ -2009,7 +2034,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
proc.git_cmd = 1;
proc.argv = argv_gc_auto;
- close_all_packs();
+ close_all_packs(the_repository->objects);
if (!start_command(&proc)) {
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 2067cca5b1..a48984d37e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -42,7 +42,7 @@ struct expire_reflog_policy_cb {
};
struct collected_reflog {
- unsigned char sha1[20];
+ struct object_id oid;
char reflog[FLEX_ARRAY];
};
@@ -52,6 +52,7 @@ struct collect_reflog_cb {
int nr;
};
+/* Remember to update object flag allocation in object.h */
#define INCOMPLETE (1u<<10)
#define STUDYING (1u<<11)
#define REACHABLE (1u<<12)
@@ -74,7 +75,7 @@ static int tree_is_complete(const struct object_id *oid)
if (!tree->buffer) {
enum object_type type;
unsigned long size;
- void *data = read_sha1_file(oid->hash, &type, &size);
+ void *data = read_object_file(oid, &type, &size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;
@@ -153,7 +154,7 @@ static int commit_is_complete(struct commit *commit)
for (i = 0; i < found.nr; i++) {
struct commit *c =
(struct commit *)found.objects[i].item;
- if (!tree_is_complete(&c->tree->object.oid)) {
+ if (!tree_is_complete(get_commit_tree_oid(c))) {
is_incomplete = 1;
c->object.flags |= INCOMPLETE;
}
@@ -289,20 +290,20 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no
const char *message, void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
- struct commit *old, *new;
+ struct commit *old_commit, *new_commit;
if (timestamp < cb->cmd.expire_total)
return 1;
- old = new = NULL;
+ old_commit = new_commit = NULL;
if (cb->cmd.stalefix &&
- (!keep_entry(&old, ooid) || !keep_entry(&new, noid)))
+ (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
return 1;
if (timestamp < cb->cmd.expire_unreachable) {
if (cb->unreachable_expire_kind == UE_ALWAYS)
return 1;
- if (unreachable(cb, old, ooid) || unreachable(cb, new, noid))
+ if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
return 1;
}
@@ -385,7 +386,7 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
struct collect_reflog_cb *cb = cb_data;
FLEX_ALLOC_STR(e, reflog, ref);
- hashcpy(e->sha1, oid->hash);
+ oidcpy(&e->oid, oid);
ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
cb->e[cb->nr++] = e;
return 0;
@@ -416,16 +417,6 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
return ent;
}
-static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
-{
- if (!value)
- return config_error_nonbool(var);
- if (parse_expiry_date(value, expire))
- return error(_("'%s' for '%s' is not a valid timestamp"),
- value, var);
- return 0;
-}
-
/* expiry timer slot */
#define EXPIRE_TOTAL 01
#define EXPIRE_UNREACH 02
@@ -443,11 +434,11 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
if (!strcmp(key, "reflogexpire")) {
slot = EXPIRE_TOTAL;
- if (parse_expire_cfg_value(var, value, &expire))
+ if (git_config_expiry_date(&expire, var, value))
return -1;
} else if (!strcmp(key, "reflogexpireunreachable")) {
slot = EXPIRE_UNREACH;
- if (parse_expire_cfg_value(var, value, &expire))
+ if (git_config_expiry_date(&expire, var, value))
return -1;
} else
return git_default_config(var, value, cb);
@@ -589,7 +580,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for (i = 0; i < collected.nr; i++) {
struct collected_reflog *e = collected.e[i];
set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
- status |= reflog_expire(e->reflog, e->sha1, flags,
+ status |= reflog_expire(e->reflog, &e->oid, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -601,13 +592,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for (; i < argc; i++) {
char *ref;
- unsigned char sha1[20];
- if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
+ struct object_id oid;
+ if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
status |= error("%s points nowhere!", argv[i]);
continue;
}
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
- status |= reflog_expire(ref, sha1, flags,
+ status |= reflog_expire(ref, &oid, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -659,7 +650,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
for ( ; i < argc; i++) {
const char *spec = strstr(argv[i], "@{");
- unsigned char sha1[20];
+ struct object_id oid;
char *ep, *ref;
int recno;
@@ -668,7 +659,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
continue;
}
- if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
+ if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
status |= error("no reflog for '%s'", argv[i]);
continue;
}
@@ -683,7 +674,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
cb.cmd.expire_total = 0;
}
- status |= reflog_expire(ref, sha1, flags,
+ status |= reflog_expire(ref, &oid, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
diff --git a/builtin/remote.c b/builtin/remote.c
index 4f5cac96b0..1a82d850a2 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -7,6 +7,7 @@
#include "strbuf.h"
#include "run-command.h"
#include "refs.h"
+#include "refspec.h"
#include "argv-array.h"
static const char * const builtin_remote_usage[] = {
@@ -168,7 +169,7 @@ static int add(int argc, const char **argv)
OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
{ OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"),
N_("set up remote as a mirror to push to or fetch from"),
- PARSE_OPT_OPTARG, parse_mirror_opt },
+ PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt },
OPT_END()
};
@@ -245,7 +246,9 @@ 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;
+ enum {
+ NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE, REBASE_MERGES
+ } rebase;
};
static struct string_list branch_list = STRING_LIST_INIT_NODUP;
@@ -306,6 +309,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
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;
}
@@ -322,7 +327,7 @@ static void read_branches(void)
struct ref_states {
struct remote *remote;
- struct string_list new, stale, tracked, heads, push;
+ struct string_list new_refs, stale, tracked, heads, push;
int queried;
};
@@ -332,22 +337,21 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
struct ref *ref, *stale_refs;
int i;
- for (i = 0; i < states->remote->fetch_refspec_nr; i++)
- if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
+ for (i = 0; i < states->remote->fetch.nr; i++)
+ if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1))
die(_("Could not get fetch map for refspec %s"),
- states->remote->fetch_refspec[i]);
+ states->remote->fetch.raw[i]);
- states->new.strdup_strings = 1;
+ states->new_refs.strdup_strings = 1;
states->tracked.strdup_strings = 1;
states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
- string_list_append(&states->new, abbrev_branch(ref->name));
+ string_list_append(&states->new_refs, abbrev_branch(ref->name));
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
}
- stale_refs = get_stale_heads(states->remote->fetch,
- states->remote->fetch_refspec_nr, fetch_map);
+ stale_refs = get_stale_heads(&states->remote->fetch, fetch_map);
for (ref = stale_refs; ref; ref = ref->next) {
struct string_list_item *item =
string_list_append(&states->stale, abbrev_branch(ref->name));
@@ -356,7 +360,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
free_refs(stale_refs);
free_refs(fetch_map);
- string_list_sort(&states->new);
+ string_list_sort(&states->new_refs);
string_list_sort(&states->tracked);
string_list_sort(&states->stale);
@@ -387,8 +391,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
local_refs = get_local_heads();
push_map = copy_ref_list(remote_refs);
- match_push_refs(local_refs, &push_map, remote->push_refspec_nr,
- remote->push_refspec, MATCH_REFS_NONE);
+ match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);
states->push.strdup_strings = 1;
for (ref = push_map; ref; ref = ref->next) {
@@ -434,14 +437,14 @@ static int get_push_ref_states_noquery(struct ref_states *states)
return 0;
states->push.strdup_strings = 1;
- if (!remote->push_refspec_nr) {
+ if (!remote->push.nr) {
item = string_list_append(&states->push, _("(matching)"));
info = item->util = xcalloc(1, sizeof(struct push_info));
info->status = PUSH_STATUS_NOTQUERIED;
info->dest = xstrdup(item->string);
}
- for (i = 0; i < remote->push_refspec_nr; i++) {
- struct refspec *spec = remote->push + i;
+ for (i = 0; i < remote->push.nr; i++) {
+ const struct refspec_item *spec = &remote->push.items[i];
if (spec->matching)
item = string_list_append(&states->push, _("(matching)"));
else if (strlen(spec->src))
@@ -461,7 +464,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
{
struct ref *ref, *matches;
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
- struct refspec refspec;
+ struct refspec_item refspec;
refspec.force = 0;
refspec.pattern = 1;
@@ -514,7 +517,7 @@ static int add_branch_for_removal(const char *refname,
const struct object_id *oid, int flags, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
- struct refspec refspec;
+ struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
@@ -546,8 +549,8 @@ static int add_branch_for_removal(const char *refname,
}
struct rename_info {
- const char *old;
- const char *new;
+ const char *old_name;
+ const char *new_name;
struct string_list *remote_branches;
};
@@ -560,12 +563,12 @@ static int read_remote_branches(const char *refname,
int flag;
const char *symref;
- strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
+ strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
NULL, &flag);
- if (flag & REF_ISSYMREF)
+ if (symref && (flag & REF_ISSYMREF))
item->util = xstrdup(symref);
else
item->util = NULL;
@@ -585,12 +588,12 @@ static int migrate_file(struct remote *remote)
git_config_set_multivar(buf.buf, remote->url[i], "^$", 0);
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.push", remote->name);
- for (i = 0; i < remote->push_refspec_nr; i++)
- git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0);
+ for (i = 0; i < remote->push.raw_nr; i++)
+ git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0);
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.fetch", remote->name);
- for (i = 0; i < remote->fetch_refspec_nr; i++)
- git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0);
+ for (i = 0; i < remote->fetch.raw_nr; i++)
+ git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0);
if (remote->origin == REMOTE_REMOTES)
unlink_or_warn(git_path("remotes/%s", remote->name));
else if (remote->origin == REMOTE_BRANCHES)
@@ -615,48 +618,48 @@ static int mv(int argc, const char **argv)
if (argc != 3)
usage_with_options(builtin_remote_rename_usage, options);
- rename.old = argv[1];
- rename.new = argv[2];
+ rename.old_name = argv[1];
+ rename.new_name = argv[2];
rename.remote_branches = &remote_branches;
- oldremote = remote_get(rename.old);
+ oldremote = remote_get(rename.old_name);
if (!remote_is_configured(oldremote, 1))
- die(_("No such remote: %s"), rename.old);
+ die(_("No such remote: %s"), rename.old_name);
- if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
+ if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
return migrate_file(oldremote);
- newremote = remote_get(rename.new);
+ newremote = remote_get(rename.new_name);
if (remote_is_configured(newremote, 1))
- die(_("remote %s already exists."), rename.new);
+ die(_("remote %s already exists."), rename.new_name);
- strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
+ strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
if (!valid_fetch_refspec(buf.buf))
- die(_("'%s' is not a valid remote name"), rename.new);
+ die(_("'%s' is not a valid remote name"), rename.new_name);
strbuf_reset(&buf);
- strbuf_addf(&buf, "remote.%s", rename.old);
- strbuf_addf(&buf2, "remote.%s", rename.new);
+ strbuf_addf(&buf, "remote.%s", rename.old_name);
+ strbuf_addf(&buf2, "remote.%s", rename.new_name);
if (git_config_rename_section(buf.buf, buf2.buf) < 1)
return error(_("Could not rename config section '%s' to '%s'"),
buf.buf, buf2.buf);
strbuf_reset(&buf);
- strbuf_addf(&buf, "remote.%s.fetch", rename.new);
+ strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
git_config_set_multivar(buf.buf, NULL, NULL, 1);
- strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
- for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
+ strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
+ for (i = 0; i < oldremote->fetch.raw_nr; i++) {
char *ptr;
strbuf_reset(&buf2);
- strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
+ strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
ptr = strstr(buf2.buf, old_remote_context.buf);
if (ptr) {
refspec_updated = 1;
strbuf_splice(&buf2,
ptr-buf2.buf + strlen(":refs/remotes/"),
- strlen(rename.old), rename.new,
- strlen(rename.new));
+ strlen(rename.old_name), rename.new_name,
+ strlen(rename.new_name));
} else
warning(_("Not updating non-default fetch refspec\n"
"\t%s\n"
@@ -670,10 +673,10 @@ static int mv(int argc, const char **argv)
for (i = 0; i < branch_list.nr; i++) {
struct string_list_item *item = branch_list.items + i;
struct branch_info *info = item->util;
- if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
+ if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) {
strbuf_reset(&buf);
strbuf_addf(&buf, "branch.%s.remote", item->string);
- git_config_set(buf.buf, rename.new);
+ git_config_set(buf.buf, rename.new_name);
}
}
@@ -690,10 +693,10 @@ static int mv(int argc, const char **argv)
int flag = 0;
struct object_id oid;
- read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag);
+ read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
if (!(flag & REF_ISSYMREF))
continue;
- if (delete_ref(NULL, item->string, NULL, REF_NODEREF))
+ if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
die(_("deleting '%s' failed"), item->string);
}
for (i = 0; i < remote_branches.nr; i++) {
@@ -703,8 +706,8 @@ static int mv(int argc, const char **argv)
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, item->string);
- strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
- rename.new, strlen(rename.new));
+ strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
+ rename.new_name, strlen(rename.new_name));
strbuf_reset(&buf2);
strbuf_addf(&buf2, "remote: renamed %s to %s",
item->string, buf.buf);
@@ -718,12 +721,12 @@ static int mv(int argc, const char **argv)
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, item->string);
- strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
- rename.new, strlen(rename.new));
+ strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
+ rename.new_name, strlen(rename.new_name));
strbuf_reset(&buf2);
strbuf_addstr(&buf2, item->util);
- strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
- rename.new, strlen(rename.new));
+ strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name),
+ rename.new_name, strlen(rename.new_name));
strbuf_reset(&buf3);
strbuf_addf(&buf3, "remote: renamed %s to %s",
item->string, buf.buf);
@@ -788,7 +791,7 @@ static int rm(int argc, const char **argv)
strbuf_release(&buf);
if (!result)
- result = delete_refs("remote: remove", &branches, REF_NODEREF);
+ result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
string_list_clear(&branches, 0);
if (skipped.nr) {
@@ -822,7 +825,7 @@ static void clear_push_info(void *util, const char *string)
static void free_remote_ref_states(struct ref_states *states)
{
- string_list_clear(&states->new, 0);
+ string_list_clear(&states->new_refs, 0);
string_list_clear(&states->stale, 1);
string_list_clear(&states->tracked, 0);
string_list_clear(&states->heads, 0);
@@ -833,7 +836,7 @@ static int append_ref_to_tracked_list(const char *refname,
const struct object_id *oid, int flags, void *cb_data)
{
struct ref_states *states = cb_data;
- struct refspec refspec;
+ struct refspec_item refspec;
if (flags & REF_ISSYMREF)
return 0;
@@ -862,7 +865,7 @@ static int get_remote_ref_states(const char *name,
if (query) {
transport = transport_get(states->remote, states->remote->url_nr > 0 ?
states->remote->url[0] : NULL);
- remote_refs = transport_get_remote_refs(transport);
+ remote_refs = transport_get_remote_refs(transport, NULL);
transport_disconnect(transport);
states->queried = 1;
@@ -907,7 +910,7 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
if (states->queried) {
const char *fmt = "%s";
const char *arg = "";
- if (string_list_has_string(&states->new, name)) {
+ if (string_list_has_string(&states->new_refs, name)) {
fmt = _(" new (next fetch will store in remotes/%s)");
arg = states->remote->name;
} else if (string_list_has_string(&states->tracked, name))
@@ -963,9 +966,15 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
printf(" %-*s ", show_info->width, item->string);
if (branch_info->rebase) {
- printf_ln(branch_info->rebase == INTERACTIVE_REBASE
- ? _("rebases interactively onto remote %s")
- : _("rebases onto remote %s"), merge->items[0].string);
+ const char *msg;
+ if (branch_info->rebase == INTERACTIVE_REBASE)
+ msg = _("rebases interactively onto remote %s");
+ else if (branch_info->rebase == REBASE_MERGES)
+ msg = _("rebases interactively (with merges) onto "
+ "remote %s");
+ else
+ msg = _("rebases onto remote %s");
+ printf_ln(msg, merge->items[0].string);
return 0;
} else if (show_info->any_rebase) {
printf_ln(_(" merges with remote %s"), merge->items[0].string);
@@ -1176,7 +1185,7 @@ static int show(int argc, const char **argv)
/* remote branch info */
info.width = 0;
- for_each_string_list(&states.new, add_remote_to_show_info, &info);
+ for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
for_each_string_list(&states.stale, add_remote_to_show_info, &info);
if (info.list->nr)
@@ -1255,7 +1264,7 @@ static int set_head(int argc, const char **argv)
head_name = xstrdup(states.heads.items[0].string);
free_remote_ref_states(&states);
} else if (opt_d && !opt_a && argc == 1) {
- if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF))
+ if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
result |= error(_("Could not delete %s"), buf.buf);
} else
usage_with_options(builtin_remote_sethead_usage, options);
diff --git a/builtin/repack.c b/builtin/repack.c
index 7bdb40142f..6c636e159e 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -86,7 +86,8 @@ static void remove_pack_on_signal(int signo)
* have a corresponding .keep or .promisor file. These packs are not to
* be kept if we are going to pack everything into one file.
*/
-static void get_non_kept_pack_filenames(struct string_list *fname_list)
+static void get_non_kept_pack_filenames(struct string_list *fname_list,
+ const struct string_list *extra_keep)
{
DIR *dir;
struct dirent *e;
@@ -97,6 +98,14 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
while ((e = readdir(dir)) != NULL) {
size_t len;
+ int i;
+
+ for (i = 0; i < extra_keep->nr; i++)
+ if (!fspathcmp(e->d_name, extra_keep->items[i].string))
+ break;
+ if (extra_keep->nr > 0 && i < extra_keep->nr)
+ continue;
+
if (!strip_suffix(e->d_name, ".pack", &len))
continue;
@@ -148,7 +157,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
struct string_list rollback = STRING_LIST_INIT_NODUP;
struct string_list existing_packs = STRING_LIST_INIT_DUP;
struct strbuf line = STRBUF_INIT;
- int ext, ret, failed;
+ int i, ext, ret, failed;
FILE *out;
/* variables to be filled by option parsing */
@@ -160,6 +169,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
const char *depth = NULL;
const char *threads = NULL;
const char *max_pack_size = NULL;
+ struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
int no_reuse_delta = 0, no_reuse_object = 0;
int no_update_server_info = 0;
int quiet = 0;
@@ -200,6 +210,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("maximum size of each packfile")),
OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
N_("repack objects in packs marked with .keep")),
+ OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
+ N_("do not repack this pack")),
OPT_END()
};
@@ -230,6 +242,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd.args, "--keep-true-parents");
if (!pack_kept_objects)
argv_array_push(&cmd.args, "--honor-pack-keep");
+ for (i = 0; i < keep_pack_list.nr; i++)
+ argv_array_pushf(&cmd.args, "--keep-pack=%s",
+ keep_pack_list.items[i].string);
argv_array_push(&cmd.args, "--non-empty");
argv_array_push(&cmd.args, "--all");
argv_array_push(&cmd.args, "--reflog");
@@ -254,7 +269,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd.args, "--write-bitmap-index");
if (pack_everything & ALL_INTO_ONE) {
- get_non_kept_pack_filenames(&existing_packs);
+ get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
if (existing_packs.nr && delete_redundant) {
if (unpack_unreachable) {
diff --git a/builtin/replace.c b/builtin/replace.c
index 3e71a77152..6da2411e14 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -14,12 +14,15 @@
#include "refs.h"
#include "parse-options.h"
#include "run-command.h"
+#include "object-store.h"
+#include "repository.h"
#include "tag.h"
static const char * const git_replace_usage[] = {
N_("git replace [-f] <object> <replacement>"),
N_("git replace [-f] --edit <object>"),
N_("git replace [-f] --graft <commit> [<parent>...]"),
+ N_("git replace [-f] --convert-graft-file"),
N_("git replace -d <object>..."),
N_("git replace [--format=<format>] [-l [<pattern>]]"),
NULL
@@ -53,11 +56,12 @@ static int show_reference(const char *refname, const struct object_id *oid,
if (get_oid(refname, &object))
return error("Failed to resolve '%s' as a valid ref.", refname);
- obj_type = sha1_object_info(object.hash, NULL);
- repl_type = sha1_object_info(oid->hash, NULL);
+ obj_type = oid_object_info(the_repository, &object,
+ NULL);
+ repl_type = oid_object_info(the_repository, oid, NULL);
- printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
- oid_to_hex(oid), typename(repl_type));
+ printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
+ oid_to_hex(oid), type_name(repl_type));
}
}
@@ -79,11 +83,11 @@ static int list_replace_refs(const char *pattern, const char *format)
else if (!strcmp(format, "long"))
data.format = REPLACE_FORMAT_LONG;
else
- die("invalid replace format '%s'\n"
- "valid formats are 'short', 'medium' and 'long'\n",
- format);
+ return error("invalid replace format '%s'\n"
+ "valid formats are 'short', 'medium' and 'long'\n",
+ format);
- for_each_replace_ref(show_reference, (void *)&data);
+ for_each_replace_ref(the_repository, show_reference, (void *)&data);
return 0;
}
@@ -113,7 +117,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
strbuf_addstr(&ref, oid_to_hex(&oid));
full_hex = ref.buf + base_len;
- if (read_ref(ref.buf, oid.hash)) {
+ if (read_ref(ref.buf, &oid)) {
error("replace ref '%s' not found.", full_hex);
had_error = 1;
continue;
@@ -128,13 +132,13 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
static int delete_replace_ref(const char *name, const char *ref,
const struct object_id *oid)
{
- if (delete_ref(NULL, ref, oid->hash, 0))
+ if (delete_ref(NULL, ref, oid, 0))
return 1;
printf("Deleted replace ref '%s'\n", name);
return 0;
}
-static void check_ref_valid(struct object_id *object,
+static int check_ref_valid(struct object_id *object,
struct object_id *prev,
struct strbuf *ref,
int force)
@@ -142,12 +146,13 @@ static void check_ref_valid(struct object_id *object,
strbuf_reset(ref);
strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object));
if (check_refname_format(ref->buf, 0))
- die("'%s' is not a valid ref name.", ref->buf);
+ return error("'%s' is not a valid ref name.", ref->buf);
- if (read_ref(ref->buf, prev->hash))
+ if (read_ref(ref->buf, prev))
oidclr(prev);
else if (!force)
- die("replace ref '%s' already exists", ref->buf);
+ return error("replace ref '%s' already exists", ref->buf);
+ return 0;
}
static int replace_object_oid(const char *object_ref,
@@ -161,28 +166,33 @@ static int replace_object_oid(const char *object_ref,
struct strbuf ref = STRBUF_INIT;
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
+ int res = 0;
- obj_type = sha1_object_info(object->hash, NULL);
- repl_type = sha1_object_info(repl->hash, NULL);
+ obj_type = oid_object_info(the_repository, object, NULL);
+ repl_type = oid_object_info(the_repository, repl, NULL);
if (!force && obj_type != repl_type)
- die("Objects must be of the same type.\n"
- "'%s' points to a replaced object of type '%s'\n"
- "while '%s' points to a replacement object of type '%s'.",
- object_ref, typename(obj_type),
- replace_ref, typename(repl_type));
-
- check_ref_valid(object, &prev, &ref, force);
+ return error("Objects must be of the same type.\n"
+ "'%s' points to a replaced object of type '%s'\n"
+ "while '%s' points to a replacement object of "
+ "type '%s'.",
+ object_ref, type_name(obj_type),
+ replace_ref, type_name(repl_type));
+
+ if (check_ref_valid(object, &prev, &ref, force)) {
+ strbuf_release(&ref);
+ return -1;
+ }
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, ref.buf, repl->hash, prev.hash,
+ ref_transaction_update(transaction, ref.buf, repl, &prev,
0, NULL, &err) ||
ref_transaction_commit(transaction, &err))
- die("%s", err.buf);
+ res = error("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&ref);
- return 0;
+ return res;
}
static int replace_object(const char *object_ref, const char *replace_ref, int force)
@@ -190,9 +200,11 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
struct object_id object, repl;
if (get_oid(object_ref, &object))
- die("Failed to resolve '%s' as a valid ref.", object_ref);
+ return error("Failed to resolve '%s' as a valid ref.",
+ object_ref);
if (get_oid(replace_ref, &repl))
- die("Failed to resolve '%s' as a valid ref.", replace_ref);
+ return error("Failed to resolve '%s' as a valid ref.",
+ replace_ref);
return replace_object_oid(object_ref, &object, replace_ref, &repl, force);
}
@@ -202,7 +214,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
* If "raw" is true, then the object's raw contents are printed according to
* "type". Otherwise, we pretty-print the contents for human editing.
*/
-static void export_object(const struct object_id *oid, enum object_type type,
+static int export_object(const struct object_id *oid, enum object_type type,
int raw, const char *filename)
{
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -210,12 +222,12 @@ static void export_object(const struct object_id *oid, enum object_type type,
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0)
- die_errno("unable to open %s for writing", filename);
+ return error_errno("unable to open %s for writing", filename);
argv_array_push(&cmd.args, "--no-replace-objects");
argv_array_push(&cmd.args, "cat-file");
if (raw)
- argv_array_push(&cmd.args, typename(type));
+ argv_array_push(&cmd.args, type_name(type));
else
argv_array_push(&cmd.args, "-p");
argv_array_push(&cmd.args, oid_to_hex(oid));
@@ -223,7 +235,8 @@ static void export_object(const struct object_id *oid, enum object_type type,
cmd.out = fd;
if (run_command(&cmd))
- die("cat-file reported failure");
+ return error("cat-file reported failure");
+ return 0;
}
/*
@@ -231,14 +244,14 @@ static void export_object(const struct object_id *oid, enum object_type type,
* interpreting it as "type", and writing the result to the object database.
* The sha1 of the written object is returned via sha1.
*/
-static void import_object(struct object_id *oid, enum object_type type,
+static int import_object(struct object_id *oid, enum object_type type,
int raw, const char *filename)
{
int fd;
fd = open(filename, O_RDONLY);
if (fd < 0)
- die_errno("unable to open %s for reading", filename);
+ return error_errno("unable to open %s for reading", filename);
if (!raw && type == OBJ_TREE) {
const char *argv[] = { "mktree", NULL };
@@ -250,27 +263,40 @@ static void import_object(struct object_id *oid, enum object_type type,
cmd.in = fd;
cmd.out = -1;
- if (start_command(&cmd))
- die("unable to spawn mktree");
+ if (start_command(&cmd)) {
+ close(fd);
+ return error("unable to spawn mktree");
+ }
- if (strbuf_read(&result, cmd.out, 41) < 0)
- die_errno("unable to read from mktree");
+ if (strbuf_read(&result, cmd.out, 41) < 0) {
+ error_errno("unable to read from mktree");
+ close(fd);
+ close(cmd.out);
+ return -1;
+ }
close(cmd.out);
- if (finish_command(&cmd))
- die("mktree reported failure");
- if (get_oid_hex(result.buf, oid) < 0)
- die("mktree did not return an object name");
+ if (finish_command(&cmd)) {
+ strbuf_release(&result);
+ return error("mktree reported failure");
+ }
+ if (get_oid_hex(result.buf, oid) < 0) {
+ strbuf_release(&result);
+ return error("mktree did not return an object name");
+ }
strbuf_release(&result);
} else {
struct stat st;
int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;
- if (fstat(fd, &st) < 0)
- die_errno("unable to fstat %s", filename);
+ if (fstat(fd, &st) < 0) {
+ error_errno("unable to fstat %s", filename);
+ close(fd);
+ return -1;
+ }
if (index_fd(oid, fd, &st, type, NULL, flags) < 0)
- die("unable to write object to database");
+ return error("unable to write object to database");
/* index_fd close()s fd for us */
}
@@ -278,39 +304,52 @@ static void import_object(struct object_id *oid, enum object_type type,
* No need to close(fd) here; both run-command and index-fd
* will have done it for us.
*/
+ return 0;
}
static int edit_and_replace(const char *object_ref, int force, int raw)
{
- char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
+ char *tmpfile;
enum object_type type;
- struct object_id old, new, prev;
+ struct object_id old_oid, new_oid, prev;
struct strbuf ref = STRBUF_INIT;
- if (get_oid(object_ref, &old) < 0)
- die("Not a valid object name: '%s'", object_ref);
+ if (get_oid(object_ref, &old_oid) < 0)
+ return error("Not a valid object name: '%s'", object_ref);
- type = sha1_object_info(old.hash, NULL);
+ type = oid_object_info(the_repository, &old_oid, NULL);
if (type < 0)
- die("unable to get object type for %s", oid_to_hex(&old));
+ return error("unable to get object type for %s",
+ oid_to_hex(&old_oid));
- check_ref_valid(&old, &prev, &ref, force);
+ if (check_ref_valid(&old_oid, &prev, &ref, force)) {
+ strbuf_release(&ref);
+ return -1;
+ }
strbuf_release(&ref);
- export_object(&old, type, raw, tmpfile);
- if (launch_editor(tmpfile, NULL, NULL) < 0)
- die("editing object file failed");
- import_object(&new, type, raw, tmpfile);
-
+ tmpfile = git_pathdup("REPLACE_EDITOBJ");
+ if (export_object(&old_oid, type, raw, tmpfile)) {
+ free(tmpfile);
+ return -1;
+ }
+ if (launch_editor(tmpfile, NULL, NULL) < 0) {
+ free(tmpfile);
+ return error("editing object file failed");
+ }
+ if (import_object(&new_oid, type, raw, tmpfile)) {
+ free(tmpfile);
+ return -1;
+ }
free(tmpfile);
- if (!oidcmp(&old, &new))
- return error("new object is the same as the old one: '%s'", oid_to_hex(&old));
+ if (!oidcmp(&old_oid, &new_oid))
+ return error("new object is the same as the old one: '%s'", oid_to_hex(&old_oid));
- return replace_object_oid(object_ref, &old, "replacement", &new, force);
+ return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force);
}
-static void replace_parents(struct strbuf *buf, int argc, const char **argv)
+static int replace_parents(struct strbuf *buf, int argc, const char **argv)
{
struct strbuf new_parents = STRBUF_INIT;
const char *parent_start, *parent_end;
@@ -327,9 +366,15 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
/* prepare new parents */
for (i = 0; i < argc; i++) {
struct object_id oid;
- if (get_oid(argv[i], &oid) < 0)
- die(_("Not a valid object name: '%s'"), argv[i]);
- lookup_commit_or_die(&oid, argv[i]);
+ if (get_oid(argv[i], &oid) < 0) {
+ strbuf_release(&new_parents);
+ return error(_("Not a valid object name: '%s'"),
+ argv[i]);
+ }
+ if (!lookup_commit_reference(&oid)) {
+ strbuf_release(&new_parents);
+ return error(_("could not parse %s"), argv[i]);
+ }
strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
}
@@ -338,6 +383,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
new_parents.buf, new_parents.len);
strbuf_release(&new_parents);
+ return 0;
}
struct check_mergetag_data {
@@ -345,7 +391,7 @@ struct check_mergetag_data {
const char **argv;
};
-static void check_one_mergetag(struct commit *commit,
+static int check_one_mergetag(struct commit *commit,
struct commit_extra_header *extra,
void *data)
{
@@ -355,70 +401,120 @@ static void check_one_mergetag(struct commit *commit,
struct tag *tag;
int i;
- hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash);
+ hash_object_file(extra->value, extra->len, type_name(OBJ_TAG), &tag_oid);
tag = lookup_tag(&tag_oid);
if (!tag)
- die(_("bad mergetag in commit '%s'"), ref);
+ return error(_("bad mergetag in commit '%s'"), ref);
if (parse_tag_buffer(tag, extra->value, extra->len))
- die(_("malformed mergetag in commit '%s'"), ref);
+ return error(_("malformed mergetag in commit '%s'"), ref);
/* iterate over new parents */
for (i = 1; i < mergetag_data->argc; i++) {
struct object_id oid;
if (get_oid(mergetag_data->argv[i], &oid) < 0)
- die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]);
+ return error(_("Not a valid object name: '%s'"),
+ mergetag_data->argv[i]);
if (!oidcmp(&tag->tagged->oid, &oid))
- return; /* found */
+ return 0; /* found */
}
- die(_("original commit '%s' contains mergetag '%s' that is discarded; "
- "use --edit instead of --graft"), ref, oid_to_hex(&tag_oid));
+ return error(_("original commit '%s' contains mergetag '%s' that is "
+ "discarded; use --edit instead of --graft"), ref,
+ oid_to_hex(&tag_oid));
}
-static void check_mergetags(struct commit *commit, int argc, const char **argv)
+static int check_mergetags(struct commit *commit, int argc, const char **argv)
{
struct check_mergetag_data mergetag_data;
mergetag_data.argc = argc;
mergetag_data.argv = argv;
- for_each_mergetag(check_one_mergetag, commit, &mergetag_data);
+ return for_each_mergetag(check_one_mergetag, commit, &mergetag_data);
}
-static int create_graft(int argc, const char **argv, int force)
+static int create_graft(int argc, const char **argv, int force, int gentle)
{
- struct object_id old, new;
+ struct object_id old_oid, new_oid;
const char *old_ref = argv[0];
struct commit *commit;
struct strbuf buf = STRBUF_INIT;
const char *buffer;
unsigned long size;
- if (get_oid(old_ref, &old) < 0)
- die(_("Not a valid object name: '%s'"), old_ref);
- commit = lookup_commit_or_die(&old, old_ref);
+ if (get_oid(old_ref, &old_oid) < 0)
+ return error(_("Not a valid object name: '%s'"), old_ref);
+ commit = lookup_commit_reference(&old_oid);
+ if (!commit)
+ return error(_("could not parse %s"), old_ref);
buffer = get_commit_buffer(commit, &size);
strbuf_add(&buf, buffer, size);
unuse_commit_buffer(commit, buffer);
- replace_parents(&buf, argc - 1, &argv[1]);
+ if (replace_parents(&buf, argc - 1, &argv[1]) < 0) {
+ strbuf_release(&buf);
+ return -1;
+ }
if (remove_signature(&buf)) {
warning(_("the original commit '%s' has a gpg signature."), old_ref);
warning(_("the signature will be removed in the replacement commit!"));
}
- check_mergetags(commit, argc, argv);
+ if (check_mergetags(commit, argc, argv)) {
+ strbuf_release(&buf);
+ return -1;
+ }
- if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash))
- die(_("could not write replacement commit for: '%s'"), old_ref);
+ if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) {
+ strbuf_release(&buf);
+ return error(_("could not write replacement commit for: '%s'"),
+ old_ref);
+ }
strbuf_release(&buf);
- if (!oidcmp(&old, &new))
- return error("new commit is the same as the old one: '%s'", oid_to_hex(&old));
+ if (!oidcmp(&old_oid, &new_oid)) {
+ if (gentle) {
+ warning("graft for '%s' unnecessary", oid_to_hex(&old_oid));
+ return 0;
+ }
+ return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid));
+ }
- return replace_object_oid(old_ref, &old, "replacement", &new, force);
+ return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force);
+}
+
+static int convert_graft_file(int force)
+{
+ const char *graft_file = get_graft_file();
+ FILE *fp = fopen_or_warn(graft_file, "r");
+ struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT;
+ struct argv_array args = ARGV_ARRAY_INIT;
+
+ if (!fp)
+ return -1;
+
+ while (strbuf_getline(&buf, fp) != EOF) {
+ if (*buf.buf == '#')
+ continue;
+
+ argv_array_split(&args, buf.buf);
+ if (args.argc && create_graft(args.argc, args.argv, force, 1))
+ strbuf_addf(&err, "\n\t%s", buf.buf);
+ argv_array_clear(&args);
+ }
+ fclose(fp);
+
+ strbuf_release(&buf);
+
+ if (!err.len)
+ return unlink_or_warn(graft_file);
+
+ warning(_("could not convert the following graft(s):\n%s"), err.buf);
+ strbuf_release(&err);
+
+ return -1;
}
int cmd_replace(int argc, const char **argv, const char *prefix)
@@ -432,6 +528,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
MODE_DELETE,
MODE_EDIT,
MODE_GRAFT,
+ MODE_CONVERT_GRAFT_FILE,
MODE_REPLACE
} cmdmode = MODE_UNSPECIFIED;
struct option options[] = {
@@ -439,7 +536,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
- OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+ OPT_CMDMODE(0, "convert-graft-file", &cmdmode, N_("convert existing graft file"), MODE_CONVERT_GRAFT_FILE),
+ OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"),
+ PARSE_OPT_NOCOMPLETE),
OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
OPT_END()
@@ -460,7 +559,8 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
if (force &&
cmdmode != MODE_REPLACE &&
cmdmode != MODE_EDIT &&
- cmdmode != MODE_GRAFT)
+ cmdmode != MODE_GRAFT &&
+ cmdmode != MODE_CONVERT_GRAFT_FILE)
usage_msg_opt("-f only makes sense when writing a replacement",
git_replace_usage, options);
@@ -491,7 +591,13 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
if (argc < 1)
usage_msg_opt("-g needs at least one argument",
git_replace_usage, options);
- return create_graft(argc, argv, force);
+ return create_graft(argc, argv, force, 0);
+
+ case MODE_CONVERT_GRAFT_FILE:
+ if (argc != 0)
+ usage_msg_opt("--convert-graft-file takes no argument",
+ git_replace_usage, options);
+ return !!convert_graft_file(force);
case MODE_LIST:
if (argc > 1)
@@ -500,6 +606,6 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
return list_replace_refs(argv[0], format);
default:
- die("BUG: invalid cmdmode %d", (int)cmdmode);
+ BUG("invalid cmdmode %d", (int)cmdmode);
}
}
diff --git a/builtin/reset.c b/builtin/reset.c
index 9cd89b2305..a862c70fab 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -12,7 +12,7 @@
#include "lockfile.h"
#include "tag.h"
#include "object.h"
-#include "commit.h"
+#include "pretty.h"
#include "run-command.h"
#include "refs.h"
#include "diff.h"
@@ -106,24 +106,16 @@ out:
static void print_new_head_line(struct commit *commit)
{
- const char *hex, *body;
- const char *msg;
-
- hex = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
- printf(_("HEAD is now at %s"), hex);
- msg = logmsg_reencode(commit, NULL, get_log_output_encoding());
- body = strstr(msg, "\n\n");
- if (body) {
- const char *eol;
- size_t len;
- body = skip_blank_lines(body + 2);
- eol = strchr(body, '\n');
- len = eol ? eol - body : strlen(body);
- printf(" %.*s\n", (int) len, body);
- }
- else
- printf("\n");
- unuse_commit_buffer(commit, msg);
+ struct strbuf buf = STRBUF_INIT;
+
+ printf(_("HEAD is now at %s"),
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
+ if (buf.len > 0)
+ printf(" %s", buf.buf);
+ putchar('\n');
+ strbuf_release(&buf);
}
static void update_index_from_diff(struct diff_queue_struct *q,
@@ -166,7 +158,7 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.output_format = DIFF_FORMAT_CALLBACK;
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
- opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ opt.flags.override_submodule_config = 1;
if (do_diff_cache(tree_oid, &opt))
return 1;
@@ -266,12 +258,12 @@ static int reset_refs(const char *rev, const struct object_id *oid)
if (!get_oid("HEAD", &oid_orig)) {
orig = &oid_orig;
set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
- update_ref_oid(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
+ update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
UPDATE_REFS_MSG_ON_ERR);
} else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig->hash, 0);
+ delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
set_reflog_message(&msg, "updating HEAD", rev);
- update_ref_status = update_ref_oid(msg.buf, "HEAD", oid, orig, 0,
+ update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
UPDATE_REFS_MSG_ON_ERR);
strbuf_release(&msg);
return update_ref_status;
@@ -322,7 +314,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
- hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN);
+ oidcpy(&oid, the_hash_algo->empty_tree);
} else if (!pathspec.nr) {
struct commit *commit;
if (get_oid_committish(rev, &oid))
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 8503dea42d..fadd3ec14c 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -108,7 +108,7 @@ static void show_commit(struct commit *commit, void *data)
if (!revs->graph)
fputs(get_revision_mark(revs, commit), stdout);
if (revs->abbrev_commit && revs->abbrev)
- fputs(find_unique_abbrev(commit->object.oid.hash, revs->abbrev),
+ fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
stdout);
else
fputs(oid_to_hex(&commit->object.oid), stdout);
@@ -134,7 +134,7 @@ static void show_commit(struct commit *commit, void *data)
else
putchar('\n');
- if (revs->verbose_header && get_cached_commit_buffer(commit, NULL)) {
+ if (revs->verbose_header) {
struct strbuf buf = STRBUF_INIT;
struct pretty_print_context ctx = {0};
ctx.abbrev = revs->abbrev;
@@ -314,14 +314,14 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
}
static int show_object_fast(
- const unsigned char *sha1,
+ const struct object_id *oid,
enum object_type type,
int exclude,
uint32_t name_hash,
struct packed_git *found_pack,
off_t found_offset)
{
- fprintf(stdout, "%s\n", sha1_to_hex(sha1));
+ fprintf(stdout, "%s\n", oid_to_hex(oid));
return 1;
}
@@ -407,7 +407,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (revs.bisect)
bisect_list = 1;
- if (DIFF_OPT_TST(&revs.diffopt, QUICK))
+ if (revs.diffopt.flags.quick)
info.flags |= REV_LIST_QUIET;
for (i = 1 ; i < argc; i++) {
const char *arg = argv[i];
@@ -536,10 +536,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
mark_edges_uninteresting(&revs, show_edge);
if (bisect_list) {
- int reaches = reaches, all = all;
+ int reaches, all;
- revs.commits = find_bisection(revs.commits, &reaches, &all,
- bisect_find_all);
+ find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
if (bisect_show_vars)
return show_bisect_vars(&info, reaches, all);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index a8d7e6f7ae..4f49e96bfd 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -133,7 +133,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
struct object_id discard;
char *full;
- switch (dwim_ref(name, strlen(name), discard.hash, &full)) {
+ switch (dwim_ref(name, strlen(name), &discard, &full)) {
case 0:
/*
* Not found -- not a ref. We could
@@ -159,7 +159,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
}
}
else if (abbrev)
- show_with_type(type, find_unique_abbrev(oid->hash, abbrev));
+ show_with_type(type, find_unique_abbrev(oid, abbrev));
else
show_with_type(type, oid_to_hex(oid));
}
@@ -243,28 +243,28 @@ static int show_file(const char *arg, int output_prefix)
static int try_difference(const char *arg)
{
char *dotdot;
- struct object_id oid;
- struct object_id end;
- const char *next;
- const char *this;
+ struct object_id start_oid;
+ struct object_id end_oid;
+ const char *end;
+ const char *start;
int symmetric;
static const char head_by_default[] = "HEAD";
if (!(dotdot = strstr(arg, "..")))
return 0;
- next = dotdot + 2;
- this = arg;
- symmetric = (*next == '.');
+ end = dotdot + 2;
+ start = arg;
+ symmetric = (*end == '.');
*dotdot = 0;
- next += symmetric;
+ end += symmetric;
- if (!*next)
- next = head_by_default;
+ if (!*end)
+ end = head_by_default;
if (dotdot == arg)
- this = head_by_default;
+ start = head_by_default;
- if (this == head_by_default && next == head_by_default &&
+ if (start == head_by_default && end == head_by_default &&
!symmetric) {
/*
* Just ".."? That is not a range but the
@@ -274,14 +274,18 @@ static int try_difference(const char *arg)
return 0;
}
- if (!get_oid_committish(this, &oid) && !get_oid_committish(next, &end)) {
- show_rev(NORMAL, &end, next);
- show_rev(symmetric ? NORMAL : REVERSED, &oid, this);
+ if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) {
+ show_rev(NORMAL, &end_oid, end);
+ show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
if (symmetric) {
struct commit_list *exclude;
struct commit *a, *b;
- a = lookup_commit_reference(&oid);
- b = lookup_commit_reference(&end);
+ a = lookup_commit_reference(&start_oid);
+ b = lookup_commit_reference(&end_oid);
+ if (!a || !b) {
+ *dotdot = '.';
+ return 0;
+ }
exclude = get_merge_bases(a, b);
while (exclude) {
struct commit *commit = pop_commit(&exclude);
@@ -328,12 +332,12 @@ static int try_parent_shorthands(const char *arg)
return 0;
*dotdot = 0;
- if (get_oid_committish(arg, &oid)) {
+ if (get_oid_committish(arg, &oid) ||
+ !(commit = lookup_commit_reference(&oid))) {
*dotdot = '^';
return 0;
}
- commit = lookup_commit_reference(&oid);
if (exclude_parent &&
exclude_parent > commit_list_count(commit->parents)) {
*dotdot = '^';
@@ -516,7 +520,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
PARSE_OPT_SHELL_EVAL);
strbuf_addstr(&parsed, " --");
- sq_quote_argv(&parsed, argv, 0);
+ sq_quote_argv(&parsed, argv);
puts(parsed.buf);
return 0;
}
@@ -526,7 +530,7 @@ static int cmd_sq_quote(int argc, const char **argv)
struct strbuf buf = STRBUF_INIT;
if (argc)
- sq_quote_argv(&buf, argv, 0);
+ sq_quote_argv(&buf, argv);
printf("%s\n", buf.buf);
strbuf_release(&buf);
@@ -887,8 +891,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die(_("Could not read the index"));
if (the_index.split_index) {
- const unsigned char *sha1 = the_index.split_index->base_sha1;
- const char *path = git_path("sharedindex.%s", sha1_to_hex(sha1));
+ const struct object_id *oid = &the_index.split_index->base_oid;
+ const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
strbuf_reset(&buf);
puts(relative_path(path, prefix, &buf));
}
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09..76f0a35b07 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -208,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
if (isatty(0))
opts.edit = 1;
opts.action = REPLAY_REVERT;
- git_config(git_default_config, NULL);
+ sequencer_init_config(&opts);
res = run_sequencer(argc, argv, &opts);
if (res < 0)
die(_("revert failed"));
@@ -221,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
int res;
opts.action = REPLAY_PICK;
- git_config(git_default_config, NULL);
+ sequencer_init_config(&opts);
res = run_sequencer(argc, argv, &opts);
if (res < 0)
die(_("cherry-pick failed"));
diff --git a/builtin/rm.c b/builtin/rm.c
index d91451fea1..65b448ef8e 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -178,7 +178,7 @@ static int check_local_mod(struct object_id *head, int index_only)
* way as changed from the HEAD.
*/
if (no_head
- || get_tree_entry(head->hash, name, oid.hash, &mode)
+ || get_tree_entry(head, name, &oid, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| oidcmp(&ce->oid, &oid))
staged_changes = 1;
@@ -233,8 +233,6 @@ static int check_local_mod(struct object_id *head, int index_only)
return errs;
}
-static struct lock_file lock_file;
-
static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
static int ignore_unmatch = 0;
@@ -242,7 +240,7 @@ static struct option builtin_rm_options[] = {
OPT__DRY_RUN(&show_only, N_("dry run")),
OPT__QUIET(&quiet, N_("do not list removed files")),
OPT_BOOL( 0 , "cached", &index_only, N_("only remove from the index")),
- OPT__FORCE(&force, N_("override the up-to-date check")),
+ OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE),
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")),
@@ -251,6 +249,7 @@ static struct option builtin_rm_options[] = {
int cmd_rm(int argc, const char **argv, const char *prefix)
{
+ struct lock_file lock_file = LOCK_INIT;
int i;
struct pathspec pathspec;
char *seen;
@@ -382,13 +381,12 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
if (gitmodules_modified)
- stage_updated_gitmodules();
+ stage_updated_gitmodules(&the_index);
}
- if (active_cache_changed) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
+ if (write_locked_index(&the_index, &lock_file,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ die(_("Unable to write new index file"));
return 0;
}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index fc4f0bb5fb..4923b1058c 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -14,6 +14,7 @@
#include "sha1-array.h"
#include "gpg-interface.h"
#include "gettext.h"
+#include "protocol.h"
static const char * const send_pack_usage[] = {
N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
@@ -125,8 +126,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
int cmd_send_pack(int argc, const char **argv, const char *prefix)
{
- int i, nr_refspecs = 0;
- const char **refspecs = NULL;
+ struct refspec rs = REFSPEC_INIT_PUSH;
const char *remote_name = NULL;
struct remote *remote = NULL;
const char *dest = NULL;
@@ -154,6 +154,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
int progress = -1;
int from_stdin = 0;
struct push_cas_option cas = {0};
+ struct packet_reader reader;
struct option options[] = {
OPT__VERBOSITY(&verbose),
@@ -187,8 +188,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);
if (argc > 0) {
dest = argv[0];
- refspecs = (const char **)(argv + 1);
- nr_refspecs = argc - 1;
+ refspec_appendn(&rs, argv + 1, argc - 1);
}
if (!dest)
@@ -207,31 +207,23 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.push_options = push_options.nr ? &push_options : NULL;
if (from_stdin) {
- struct argv_array all_refspecs = ARGV_ARRAY_INIT;
-
- for (i = 0; i < nr_refspecs; i++)
- argv_array_push(&all_refspecs, refspecs[i]);
-
if (args.stateless_rpc) {
const char *buf;
while ((buf = packet_read_line(0, NULL)))
- argv_array_push(&all_refspecs, buf);
+ refspec_append(&rs, buf);
} else {
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, stdin) != EOF)
- argv_array_push(&all_refspecs, line.buf);
+ refspec_append(&rs, line.buf);
strbuf_release(&line);
}
-
- refspecs = all_refspecs.argv;
- nr_refspecs = all_refspecs.argc;
}
/*
* --all and --mirror are incompatible; neither makes sense
* with any refspecs.
*/
- if ((nr_refspecs > 0 && (send_all || args.send_mirror)) ||
+ if ((rs.nr > 0 && (send_all || args.send_mirror)) ||
(send_all && args.send_mirror))
usage_with_options(send_pack_usage, options);
@@ -256,10 +248,22 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.verbose ? CONNECT_VERBOSE : 0);
}
- get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL,
- &extra_have, &shallow);
-
- transport_verify_remote_names(nr_refspecs, refspecs);
+ packet_reader_init(&reader, fd[0], NULL, 0,
+ PACKET_READ_CHOMP_NEWLINE |
+ PACKET_READ_GENTLE_ON_EOF);
+
+ switch (discover_version(&reader)) {
+ case protocol_v2:
+ die("support for protocol v2 not implemented yet");
+ break;
+ case protocol_v1:
+ case protocol_v0:
+ get_remote_heads(&reader, &remote_refs, REF_NORMAL,
+ &extra_have, &shallow);
+ break;
+ case protocol_unknown_version:
+ BUG("unknown protocol version");
+ }
local_refs = get_local_heads();
@@ -271,7 +275,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
flags |= MATCH_REFS_MIRROR;
/* match them up */
- if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
+ if (match_push_refs(local_refs, &remote_refs, &rs, flags))
return -1;
if (!is_empty_cas(&cas))
diff --git a/builtin/serve.c b/builtin/serve.c
new file mode 100644
index 0000000000..d3fd240bb3
--- /dev/null
+++ b/builtin/serve.c
@@ -0,0 +1,30 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "serve.h"
+
+static char const * const serve_usage[] = {
+ N_("git serve [<options>]"),
+ NULL
+};
+
+int cmd_serve(int argc, const char **argv, const char *prefix)
+{
+ struct serve_options opts = SERVE_OPTIONS_INIT;
+
+ struct option options[] = {
+ OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+ N_("quit after a single request/response exchange")),
+ OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities,
+ N_("exit immediately after advertising capabilities")),
+ OPT_END()
+ };
+
+ /* ignore all unknown cmdline switches for now */
+ argc = parse_options(argc, argv, prefix, options, serve_usage,
+ PARSE_OPT_KEEP_DASHDASH |
+ PARSE_OPT_KEEP_UNKNOWN);
+ serve(&opts);
+
+ return 0;
+}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index e29875b843..608d6ba77b 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -11,7 +11,8 @@
#include "parse-options.h"
static char const * const shortlog_usage[] = {
- N_("git shortlog [<options>] [<revision-range>] [[--] [<path>...]]"),
+ N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"),
+ N_("git log --pretty=short | git shortlog [<options>]"),
NULL
};
@@ -283,6 +284,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
for (;;) {
switch (parse_options_step(&ctx, options, shortlog_usage)) {
case PARSE_OPT_HELP:
+ case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_DONE:
goto parse_done;
@@ -292,6 +294,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
parse_done:
argc = parse_options_end(&ctx);
+ if (nongit && argc > 1) {
+ error(_("too many arguments given outside repository"));
+ usage_with_options(shortlog_usage, options);
+ }
+
if (setup_revisions(argc, argv, &rev, NULL) != 1) {
error(_("unrecognized argument: %s"), argv[1]);
usage_with_options(shortlog_usage, options);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 6fa1f62a88..f2e985c00a 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -1,12 +1,13 @@
#include "cache.h"
#include "config.h"
-#include "commit.h"
+#include "pretty.h"
#include "refs.h"
#include "builtin.h"
#include "color.h"
#include "argv-array.h"
#include "parse-options.h"
#include "dir.h"
+#include "commit-slab.h"
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
@@ -21,6 +22,11 @@ static int showbranch_use_color = -1;
static struct argv_array default_args = ARGV_ARRAY_INIT;
+/*
+ * TODO: convert this use of commit->object.flags to commit-slab
+ * instead to store a pointer to ref name directly. Then use the same
+ * UNINTERESTING definition from revision.h here.
+ */
#define UNINTERESTING 01
#define REV_SHIFT 2
@@ -59,15 +65,27 @@ struct commit_name {
int generation; /* how many parents away from head_name */
};
+define_commit_slab(commit_name_slab, struct commit_name *);
+static struct commit_name_slab name_slab;
+
+static struct commit_name *commit_to_name(struct commit *commit)
+{
+ return *commit_name_slab_at(&name_slab, commit);
+}
+
+
/* Name the commit as nth generation ancestor of head_name;
* we count only the first-parent relationship for naming purposes.
*/
static void name_commit(struct commit *commit, const char *head_name, int nth)
{
struct commit_name *name;
- if (!commit->util)
- commit->util = xmalloc(sizeof(struct commit_name));
- name = commit->util;
+
+ name = *commit_name_slab_at(&name_slab, commit);
+ if (!name) {
+ name = xmalloc(sizeof(*name));
+ *commit_name_slab_at(&name_slab, commit) = name;
+ }
name->head_name = head_name;
name->generation = nth;
}
@@ -79,8 +97,8 @@ static void name_commit(struct commit *commit, const char *head_name, int nth)
*/
static void name_parent(struct commit *commit, struct commit *parent)
{
- struct commit_name *commit_name = commit->util;
- struct commit_name *parent_name = parent->util;
+ struct commit_name *commit_name = commit_to_name(commit);
+ struct commit_name *parent_name = commit_to_name(parent);
if (!commit_name)
return;
if (!parent_name ||
@@ -94,12 +112,12 @@ static int name_first_parent_chain(struct commit *c)
int i = 0;
while (c) {
struct commit *p;
- if (!c->util)
+ if (!commit_to_name(c))
break;
if (!c->parents)
break;
p = c->parents->item;
- if (!p->util) {
+ if (!commit_to_name(p)) {
name_parent(c, p);
i++;
}
@@ -122,7 +140,7 @@ static void name_commits(struct commit_list *list,
/* First give names to the given heads */
for (cl = list; cl; cl = cl->next) {
c = cl->item;
- if (c->util)
+ if (commit_to_name(c))
continue;
for (i = 0; i < num_rev; i++) {
if (rev[i] == c) {
@@ -148,9 +166,9 @@ static void name_commits(struct commit_list *list,
struct commit_name *n;
int nth;
c = cl->item;
- if (!c->util)
+ if (!commit_to_name(c))
continue;
- n = c->util;
+ n = commit_to_name(c);
parents = c->parents;
nth = 0;
while (parents) {
@@ -158,7 +176,7 @@ static void name_commits(struct commit_list *list,
struct strbuf newname = STRBUF_INIT;
parents = parents->next;
nth++;
- if (p->util)
+ if (commit_to_name(p))
continue;
switch (n->generation) {
case 0:
@@ -271,7 +289,7 @@ static void show_one_commit(struct commit *commit, int no_name)
{
struct strbuf pretty = STRBUF_INIT;
const char *pretty_str = "(unavailable)";
- struct commit_name *name = commit->util;
+ struct commit_name *name = commit_to_name(commit);
if (commit->object.parsed) {
pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
@@ -292,7 +310,7 @@ static void show_one_commit(struct commit *commit, int no_name)
}
else
printf("[%s] ",
- find_unique_abbrev(commit->object.oid.hash,
+ find_unique_abbrev(&commit->object.oid,
DEFAULT_ABBREV));
}
puts(pretty_str);
@@ -660,6 +678,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
OPT_END()
};
+ init_commit_name_slab(&name_slab);
+
git_config(git_show_branch_config, NULL);
/* If nothing is specified, try the default first */
@@ -705,8 +725,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
static const char *fake_av[2];
fake_av[0] = resolve_refdup("HEAD",
- RESOLVE_REF_READING,
- oid.hash, NULL);
+ RESOLVE_REF_READING, &oid,
+ NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -720,7 +740,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
die(Q_("only %d entry can be shown at one time.",
"only %d entries can be shown at one time.",
MAX_REVS), MAX_REVS);
- if (!dwim_ref(*av, strlen(*av), oid.hash, &ref))
+ if (!dwim_ref(*av, strlen(*av), &oid, &ref))
die(_("no such ref %s"), *av);
/* Has the base been specified? */
@@ -731,7 +751,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
/* Ah, that is a date spec... */
timestamp_t at;
at = approxidate(reflog_base);
- read_ref_at(ref, flags, at, -1, oid.hash, NULL,
+ read_ref_at(ref, flags, at, -1, &oid, NULL,
NULL, NULL, &base);
}
}
@@ -743,7 +763,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
timestamp_t timestamp;
int tz;
- if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
+ if (read_ref_at(ref, flags, 0, base + i, &oid, &logmsg,
&timestamp, &tz, NULL)) {
reflog = i;
break;
@@ -775,7 +795,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
head = resolve_refdup("HEAD", RESOLVE_REF_READING,
- head_oid.hash, NULL);
+ &head_oid, NULL);
if (with_current_branch && head) {
int has_head = 0;
diff --git a/builtin/show-index.c b/builtin/show-index.c
new file mode 100644
index 0000000000..a6e678809e
--- /dev/null
+++ b/builtin/show-index.c
@@ -0,0 +1,86 @@
+#include "builtin.h"
+#include "cache.h"
+#include "pack.h"
+
+static const char show_index_usage[] =
+"git show-index";
+
+int cmd_show_index(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ unsigned nr;
+ unsigned int version;
+ static unsigned int top_index[256];
+
+ if (argc != 1)
+ usage(show_index_usage);
+ if (fread(top_index, 2 * 4, 1, stdin) != 1)
+ die("unable to read header");
+ if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
+ version = ntohl(top_index[1]);
+ if (version < 2 || version > 2)
+ die("unknown index version");
+ if (fread(top_index, 256 * 4, 1, stdin) != 1)
+ die("unable to read index");
+ } else {
+ version = 1;
+ if (fread(&top_index[2], 254 * 4, 1, stdin) != 1)
+ die("unable to read index");
+ }
+ nr = 0;
+ for (i = 0; i < 256; i++) {
+ unsigned n = ntohl(top_index[i]);
+ if (n < nr)
+ die("corrupt index file");
+ nr = n;
+ }
+ if (version == 1) {
+ for (i = 0; i < nr; i++) {
+ unsigned int offset, entry[6];
+
+ if (fread(entry, 4 + 20, 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)));
+ }
+ } else {
+ unsigned off64_nr = 0;
+ struct {
+ unsigned char sha1[20];
+ 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)
+ die("unable to read sha1 %u/%u", i, nr);
+ for (i = 0; i < nr; i++)
+ if (fread(&entries[i].crc, 4, 1, stdin) != 1)
+ die("unable to read crc %u/%u", i, nr);
+ for (i = 0; i < nr; i++)
+ if (fread(&entries[i].off, 4, 1, stdin) != 1)
+ die("unable to read 32b offset %u/%u", i, nr);
+ for (i = 0; i < nr; i++) {
+ uint64_t offset;
+ uint32_t off = ntohl(entries[i].off);
+ if (!(off & 0x80000000)) {
+ offset = off;
+ } else {
+ uint32_t off64[2];
+ if ((off & 0x7fffffff) != off64_nr)
+ die("inconsistent 64b offset index");
+ if (fread(off64, 8, 1, stdin) != 1)
+ die("unable to read 64b offset %u", off64_nr);
+ offset = (((uint64_t)ntohl(off64[0])) << 32) |
+ ntohl(off64[1]);
+ off64_nr++;
+ }
+ printf("%" PRIuMAX " %s (%08"PRIx32")\n",
+ (uintmax_t) offset,
+ sha1_to_hex(entries[i].sha1),
+ ntohl(entries[i].crc));
+ }
+ free(entries);
+ }
+ return 0;
+}
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 013d241abc..f2eb1a7724 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -29,7 +29,7 @@ static void show_one(const char *refname, const struct object_id *oid)
if (quiet)
return;
- hex = find_unique_abbrev(oid->hash, abbrev);
+ hex = find_unique_abbrev(oid, abbrev);
if (hash_only)
printf("%s\n", hex);
else
@@ -38,8 +38,8 @@ static void show_one(const char *refname, const struct object_id *oid)
if (!deref_tags)
return;
- if (!peel_ref(refname, peeled.hash)) {
- hex = find_unique_abbrev(peeled.hash, abbrev);
+ if (!peel_ref(refname, &peeled)) {
+ hex = find_unique_abbrev(&peeled, abbrev);
printf("%s %s^{}\n", hex, refname);
}
}
@@ -197,7 +197,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
struct object_id oid;
if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) &&
- !read_ref(*pattern, oid.hash)) {
+ !read_ref(*pattern, &oid)) {
show_one(*pattern, &oid);
}
else if (!quiet)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 06ed02f994..20ae9191ca 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -12,7 +12,20 @@
#include "run-command.h"
#include "remote.h"
#include "refs.h"
+#include "refspec.h"
#include "connect.h"
+#include "revision.h"
+#include "diffcore.h"
+#include "diff.h"
+#include "object-store.h"
+
+#define OPT_QUIET (1 << 0)
+#define OPT_CACHED (1 << 1)
+#define OPT_RECURSIVE (1 << 2)
+#define OPT_FORCE (1 << 3)
+
+typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
+ void *cb_data);
static char *get_default_remote(void)
{
@@ -40,6 +53,21 @@ static char *get_default_remote(void)
return ret;
}
+static int print_default_remote(int argc, const char **argv, const char *prefix)
+{
+ char *remote;
+
+ if (argc != 1)
+ die(_("submodule--helper print-default-remote takes no arguments"));
+
+ remote = get_default_remote();
+ if (remote)
+ printf("%s\n", remote);
+
+ free(remote);
+ return 0;
+}
+
static int starts_with_dot_slash(const char *str)
{
return str[0] == '.' && is_dir_sep(str[1]);
@@ -219,6 +247,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
return 0;
}
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix)
+{
+ const char *super_prefix = get_super_prefix();
+
+ if (prefix && super_prefix) {
+ BUG("cannot have prefix '%s' and superprefix '%s'",
+ prefix, super_prefix);
+ } else if (prefix) {
+ struct strbuf sb = STRBUF_INIT;
+ char *displaypath = xstrdup(relative_path(path, prefix, &sb));
+ strbuf_release(&sb);
+ return displaypath;
+ } else if (super_prefix) {
+ return xstrfmt("%s%s", super_prefix, path);
+ } else {
+ return xstrdup(path);
+ }
+}
+
+static char *compute_rev_name(const char *sub_path, const char* object_id)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char ***d;
+
+ static const char *describe_bare[] = { NULL };
+
+ static const char *describe_tags[] = { "--tags", NULL };
+
+ static const char *describe_contains[] = { "--contains", NULL };
+
+ static const char *describe_all_always[] = { "--all", "--always", NULL };
+
+ static const char **describe_argv[] = { describe_bare, describe_tags,
+ describe_contains,
+ describe_all_always, NULL };
+
+ for (d = describe_argv; *d; d++) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.dir = sub_path;
+ cp.git_cmd = 1;
+ cp.no_stderr = 1;
+
+ argv_array_push(&cp.args, "describe");
+ argv_array_pushv(&cp.args, *d);
+ argv_array_push(&cp.args, object_id);
+
+ if (!capture_command(&cp, &sb, 0)) {
+ strbuf_strip_suffix(&sb, "\n");
+ return strbuf_detach(&sb, NULL);
+ }
+ }
+
+ strbuf_release(&sb);
+ return NULL;
+}
+
struct module_list {
const struct cache_entry **entries;
int alloc, nr;
@@ -290,6 +376,25 @@ static void module_list_active(struct module_list *list)
*list = active_modules;
}
+static char *get_up_path(const char *path)
+{
+ int i;
+ struct strbuf sb = STRBUF_INIT;
+
+ for (i = count_slashes(path); i; i--)
+ strbuf_addstr(&sb, "../");
+
+ /*
+ * Check if 'path' ends with slash or not
+ * for having the same output for dir/sub_dir
+ * and dir/sub_dir/
+ */
+ if (!is_dir_sep(path[strlen(path) - 1]))
+ strbuf_addstr(&sb, "../");
+
+ return strbuf_detach(&sb, NULL);
+}
+
static int module_list(int argc, const char **argv, const char *prefix)
{
int i;
@@ -328,23 +433,174 @@ static int module_list(int argc, const char **argv, const char *prefix)
return 0;
}
-static void init_submodule(const char *path, const char *prefix, int quiet)
+static void for_each_listed_submodule(const struct module_list *list,
+ each_submodule_fn fn, void *cb_data)
+{
+ int i;
+ for (i = 0; i < list->nr; i++)
+ fn(list->entries[i], cb_data);
+}
+
+struct cb_foreach {
+ int argc;
+ const char **argv;
+ const char *prefix;
+ int quiet;
+ int recursive;
+};
+#define CB_FOREACH_INIT { 0 }
+
+static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
+ void *cb_data)
+{
+ struct cb_foreach *info = cb_data;
+ const char *path = list_item->name;
+ const struct object_id *ce_oid = &list_item->oid;
+
+ const struct submodule *sub;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ char *displaypath;
+
+ displaypath = get_submodule_displaypath(path, info->prefix);
+
+ sub = submodule_from_path(the_repository, &null_oid, path);
+
+ if (!sub)
+ die(_("No url found for submodule path '%s' in .gitmodules"),
+ displaypath);
+
+ if (!is_submodule_populated_gently(path, NULL))
+ goto cleanup;
+
+ prepare_submodule_repo_env(&cp.env_array);
+
+ /*
+ * For the purpose of executing <command> in the submodule,
+ * separate shell is used for the purpose of running the
+ * child process.
+ */
+ cp.use_shell = 1;
+ cp.dir = path;
+
+ /*
+ * NEEDSWORK: the command currently has access to the variables $name,
+ * $sm_path, $displaypath, $sha1 and $toplevel only when the command
+ * contains a single argument. This is done for maintaining a faithful
+ * translation from shell script.
+ */
+ if (info->argc == 1) {
+ char *toplevel = xgetcwd();
+ struct strbuf sb = STRBUF_INIT;
+
+ argv_array_pushf(&cp.env_array, "name=%s", sub->name);
+ argv_array_pushf(&cp.env_array, "sm_path=%s", path);
+ argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
+ argv_array_pushf(&cp.env_array, "sha1=%s",
+ oid_to_hex(ce_oid));
+ argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
+
+ /*
+ * Since the path variable was accessible from the script
+ * before porting, it is also made available after porting.
+ * The environment variable "PATH" has a very special purpose
+ * on windows. And since environment variables are
+ * case-insensitive in windows, it interferes with the
+ * existing PATH variable. Hence, to avoid that, we expose
+ * path via the args argv_array and not via env_array.
+ */
+ sq_quote_buf(&sb, path);
+ argv_array_pushf(&cp.args, "path=%s; %s",
+ sb.buf, info->argv[0]);
+ strbuf_release(&sb);
+ free(toplevel);
+ } else {
+ argv_array_pushv(&cp.args, info->argv);
+ }
+
+ if (!info->quiet)
+ printf(_("Entering '%s'\n"), displaypath);
+
+ if (info->argv[0] && run_command(&cp))
+ die(_("run_command returned non-zero status for %s\n."),
+ displaypath);
+
+ if (info->recursive) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = path;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_pushl(&cpr.args, "--super-prefix", NULL);
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
+ argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
+ NULL);
+
+ if (info->quiet)
+ argv_array_push(&cpr.args, "--quiet");
+
+ argv_array_pushv(&cpr.args, info->argv);
+
+ if (run_command(&cpr))
+ die(_("run_command returned non-zero status while"
+ "recursing in the nested submodules of %s\n."),
+ displaypath);
+ }
+
+cleanup:
+ free(displaypath);
+}
+
+static int module_foreach(int argc, const char **argv, const char *prefix)
+{
+ struct cb_foreach info = CB_FOREACH_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+
+ struct option module_foreach_options[] = {
+ OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
+ OPT_BOOL(0, "recursive", &info.recursive,
+ N_("Recurse into nested submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_foreach_options,
+ git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
+
+ if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.argc = argc;
+ info.argv = argv;
+ info.prefix = prefix;
+
+ for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
+
+ return 0;
+}
+
+struct init_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+
+#define INIT_CB_INIT { NULL, 0 }
+
+static void init_submodule(const char *path, const char *prefix,
+ unsigned int flags)
{
const struct submodule *sub;
struct strbuf sb = STRBUF_INIT;
char *upd = NULL, *url = NULL, *displaypath;
- if (prefix && get_super_prefix())
- die("BUG: cannot have prefix and superprefix");
- else if (prefix)
- displaypath = xstrdup(relative_path(path, prefix, &sb));
- else if (get_super_prefix()) {
- strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
- displaypath = strbuf_detach(&sb, NULL);
- } else
- displaypath = xstrdup(path);
+ displaypath = get_submodule_displaypath(path, prefix);
- sub = submodule_from_path(&null_oid, path);
+ sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub)
die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -357,9 +613,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
* Set active flag for the submodule being initialized
*/
if (!is_submodule_active(the_repository, path)) {
- strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.active", sub->name);
git_config_set_gently(sb.buf, "true");
+ strbuf_reset(&sb);
}
/*
@@ -367,7 +623,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
* To look up the url in .git/config, we must not fall back to
* .gitmodules, so look it up directly.
*/
- strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
if (git_config_get_string(sb.buf, &url)) {
if (!sub->url)
@@ -399,14 +654,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
if (git_config_set_gently(sb.buf, url))
die(_("Failed to register url for submodule path '%s'"),
displaypath);
- if (!quiet)
+ if (!(flags & OPT_QUIET))
fprintf(stderr,
_("Submodule '%s' (%s) registered for path '%s'\n"),
sub->name, url, displaypath);
}
+ strbuf_reset(&sb);
/* Copy "update" setting when it is not set yet */
- strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.update", sub->name);
if (git_config_get_string(sb.buf, &upd) &&
sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
@@ -426,12 +681,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
free(upd);
}
+static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+ struct init_cb *info = cb_data;
+ init_submodule(list_item->name, info->prefix, info->flags);
+}
+
static int module_init(int argc, const char **argv, const char *prefix)
{
+ struct init_cb info = INIT_CB_INIT;
struct pathspec pathspec;
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
- int i;
struct option module_init_options[] = {
OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
@@ -456,8 +717,173 @@ static int module_init(int argc, const char **argv, const char *prefix)
if (!argc && git_config_get_value_multi("submodule.active"))
module_list_active(&list);
- for (i = 0; i < list.nr; i++)
- init_submodule(list.entries[i]->name, prefix, quiet);
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+
+ for_each_listed_submodule(&list, init_submodule_cb, &info);
+
+ return 0;
+}
+
+struct status_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+
+#define STATUS_CB_INIT { NULL, 0 }
+
+static void print_status(unsigned int flags, char state, const char *path,
+ const struct object_id *oid, const char *displaypath)
+{
+ if (flags & OPT_QUIET)
+ return;
+
+ printf("%c%s %s", state, oid_to_hex(oid), displaypath);
+
+ if (state == ' ' || state == '+') {
+ const char *name = compute_rev_name(path, oid_to_hex(oid));
+
+ if (name)
+ printf(" (%s)", name);
+ }
+
+ printf("\n");
+}
+
+static int handle_submodule_head_ref(const char *refname,
+ const struct object_id *oid, int flags,
+ void *cb_data)
+{
+ struct object_id *output = cb_data;
+ if (oid)
+ oidcpy(output, oid);
+
+ return 0;
+}
+
+static void status_submodule(const char *path, const struct object_id *ce_oid,
+ unsigned int ce_flags, const char *prefix,
+ unsigned int flags)
+{
+ char *displaypath;
+ struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+ struct rev_info rev;
+ int diff_files_result;
+
+ if (!submodule_from_path(the_repository, &null_oid, path))
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ path);
+
+ displaypath = get_submodule_displaypath(path, prefix);
+
+ if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
+ print_status(flags, 'U', path, &null_oid, displaypath);
+ goto cleanup;
+ }
+
+ if (!is_submodule_active(the_repository, path)) {
+ print_status(flags, '-', path, ce_oid, displaypath);
+ goto cleanup;
+ }
+
+ argv_array_pushl(&diff_files_args, "diff-files",
+ "--ignore-submodules=dirty", "--quiet", "--",
+ path, NULL);
+
+ git_config(git_diff_basic_config, NULL);
+ init_revisions(&rev, prefix);
+ rev.abbrev = 0;
+ diff_files_args.argc = setup_revisions(diff_files_args.argc,
+ diff_files_args.argv,
+ &rev, NULL);
+ diff_files_result = run_diff_files(&rev, 0);
+
+ if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+ print_status(flags, ' ', path, ce_oid,
+ displaypath);
+ } else if (!(flags & OPT_CACHED)) {
+ struct object_id oid;
+ struct ref_store *refs = get_submodule_ref_store(path);
+
+ if (!refs) {
+ print_status(flags, '-', path, ce_oid, displaypath);
+ goto cleanup;
+ }
+ if (refs_head_ref(refs, handle_submodule_head_ref, &oid))
+ die(_("could not resolve HEAD ref inside the "
+ "submodule '%s'"), path);
+
+ print_status(flags, '+', path, &oid, displaypath);
+ } else {
+ print_status(flags, '+', path, ce_oid, displaypath);
+ }
+
+ if (flags & OPT_RECURSIVE) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = path;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_push(&cpr.args, "--super-prefix");
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
+ argv_array_pushl(&cpr.args, "submodule--helper", "status",
+ "--recursive", NULL);
+
+ if (flags & OPT_CACHED)
+ argv_array_push(&cpr.args, "--cached");
+
+ if (flags & OPT_QUIET)
+ argv_array_push(&cpr.args, "--quiet");
+
+ if (run_command(&cpr))
+ die(_("failed to recurse into submodule '%s'"), path);
+ }
+
+cleanup:
+ argv_array_clear(&diff_files_args);
+ free(displaypath);
+}
+
+static void status_submodule_cb(const struct cache_entry *list_item,
+ void *cb_data)
+{
+ struct status_cb *info = cb_data;
+ status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
+ info->prefix, info->flags);
+}
+
+static int module_status(int argc, const char **argv, const char *prefix)
+{
+ struct status_cb info = STATUS_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+
+ struct option module_status_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+ OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
+ OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_status_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+
+ for_each_listed_submodule(&list, status_submodule_cb, &info);
return 0;
}
@@ -469,7 +895,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
if (argc != 2)
usage(_("git submodule--helper name <path>"));
- sub = submodule_from_path(&null_oid, argv[1]);
+ sub = submodule_from_path(the_repository, &null_oid, argv[1]);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@ -480,8 +906,311 @@ static int module_name(int argc, const char **argv, const char *prefix)
return 0;
}
+struct sync_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+
+#define SYNC_CB_INIT { NULL, 0 }
+
+static void sync_submodule(const char *path, const char *prefix,
+ unsigned int flags)
+{
+ const struct submodule *sub;
+ char *remote_key = NULL;
+ char *sub_origin_url, *super_config_url, *displaypath;
+ struct strbuf sb = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ char *sub_config_path = NULL;
+
+ if (!is_submodule_active(the_repository, path))
+ return;
+
+ sub = submodule_from_path(the_repository, &null_oid, path);
+
+ if (sub && sub->url) {
+ if (starts_with_dot_dot_slash(sub->url) ||
+ starts_with_dot_slash(sub->url)) {
+ char *remote_url, *up_path;
+ char *remote = get_default_remote();
+ strbuf_addf(&sb, "remote.%s.url", remote);
+
+ if (git_config_get_string(sb.buf, &remote_url))
+ remote_url = xgetcwd();
+
+ up_path = get_up_path(path);
+ sub_origin_url = relative_url(remote_url, sub->url, up_path);
+ super_config_url = relative_url(remote_url, sub->url, NULL);
+
+ free(remote);
+ free(up_path);
+ free(remote_url);
+ } else {
+ sub_origin_url = xstrdup(sub->url);
+ super_config_url = xstrdup(sub->url);
+ }
+ } else {
+ sub_origin_url = xstrdup("");
+ super_config_url = xstrdup("");
+ }
+
+ displaypath = get_submodule_displaypath(path, prefix);
+
+ if (!(flags & OPT_QUIET))
+ printf(_("Synchronizing submodule url for '%s'\n"),
+ displaypath);
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ if (git_config_set_gently(sb.buf, super_config_url))
+ die(_("failed to register url for submodule path '%s'"),
+ displaypath);
+
+ if (!is_submodule_populated_gently(path, NULL))
+ goto cleanup;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.dir = path;
+ argv_array_pushl(&cp.args, "submodule--helper",
+ "print-default-remote", NULL);
+
+ strbuf_reset(&sb);
+ if (capture_command(&cp, &sb, 0))
+ die(_("failed to get the default remote for submodule '%s'"),
+ path);
+
+ strbuf_strip_suffix(&sb, "\n");
+ remote_key = xstrfmt("remote.%s.url", sb.buf);
+
+ strbuf_reset(&sb);
+ submodule_to_gitdir(&sb, path);
+ strbuf_addstr(&sb, "/config");
+
+ if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+ die(_("failed to update remote for submodule '%s'"),
+ path);
+
+ if (flags & OPT_RECURSIVE) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = path;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_push(&cpr.args, "--super-prefix");
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
+ argv_array_pushl(&cpr.args, "submodule--helper", "sync",
+ "--recursive", NULL);
+
+ if (flags & OPT_QUIET)
+ argv_array_push(&cpr.args, "--quiet");
+
+ if (run_command(&cpr))
+ die(_("failed to recurse into submodule '%s'"),
+ path);
+ }
+
+cleanup:
+ free(super_config_url);
+ free(sub_origin_url);
+ strbuf_release(&sb);
+ free(remote_key);
+ free(displaypath);
+ free(sub_config_path);
+}
+
+static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+ struct sync_cb *info = cb_data;
+ sync_submodule(list_item->name, info->prefix, info->flags);
+
+}
+
+static int module_sync(int argc, const char **argv, const char *prefix)
+{
+ struct sync_cb info = SYNC_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int recursive = 0;
+
+ struct option module_sync_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
+ OPT_BOOL(0, "recursive", &recursive,
+ N_("Recurse into nested submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_sync_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+ if (recursive)
+ info.flags |= OPT_RECURSIVE;
+
+ for_each_listed_submodule(&list, sync_submodule_cb, &info);
+
+ return 0;
+}
+
+struct deinit_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+#define DEINIT_CB_INIT { NULL, 0 }
+
+static void deinit_submodule(const char *path, const char *prefix,
+ unsigned int flags)
+{
+ const struct submodule *sub;
+ char *displaypath = NULL;
+ struct child_process cp_config = CHILD_PROCESS_INIT;
+ struct strbuf sb_config = STRBUF_INIT;
+ char *sub_git_dir = xstrfmt("%s/.git", path);
+
+ sub = submodule_from_path(the_repository, &null_oid, path);
+
+ if (!sub || !sub->name)
+ goto cleanup;
+
+ displaypath = get_submodule_displaypath(path, prefix);
+
+ /* remove the submodule work tree (unless the user already did it) */
+ if (is_directory(path)) {
+ struct strbuf sb_rm = STRBUF_INIT;
+ const char *format;
+
+ /*
+ * protect submodules containing a .git directory
+ * NEEDSWORK: instead of dying, automatically call
+ * absorbgitdirs and (possibly) warn.
+ */
+ if (is_directory(sub_git_dir))
+ die(_("Submodule work tree '%s' contains a .git "
+ "directory (use 'rm -rf' if you really want "
+ "to remove it including all of its history)"),
+ displaypath);
+
+ if (!(flags & OPT_FORCE)) {
+ struct child_process cp_rm = CHILD_PROCESS_INIT;
+ cp_rm.git_cmd = 1;
+ argv_array_pushl(&cp_rm.args, "rm", "-qn",
+ path, NULL);
+
+ if (run_command(&cp_rm))
+ die(_("Submodule work tree '%s' contains local "
+ "modifications; use '-f' to discard them"),
+ displaypath);
+ }
+
+ strbuf_addstr(&sb_rm, path);
+
+ if (!remove_dir_recursively(&sb_rm, 0))
+ format = _("Cleared directory '%s'\n");
+ else
+ format = _("Could not remove submodule work tree '%s'\n");
+
+ if (!(flags & OPT_QUIET))
+ printf(format, displaypath);
+
+ strbuf_release(&sb_rm);
+ }
+
+ if (mkdir(path, 0777))
+ printf(_("could not create empty submodule directory %s"),
+ displaypath);
+
+ cp_config.git_cmd = 1;
+ argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
+ argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
+
+ /* remove the .git/config entries (unless the user already did it) */
+ if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
+ char *sub_key = xstrfmt("submodule.%s", sub->name);
+ /*
+ * remove the whole section so we have a clean state when
+ * the user later decides to init this submodule again
+ */
+ git_config_rename_section_in_file(NULL, sub_key, NULL);
+ if (!(flags & OPT_QUIET))
+ printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
+ sub->name, sub->url, displaypath);
+ free(sub_key);
+ }
+
+cleanup:
+ free(displaypath);
+ free(sub_git_dir);
+ strbuf_release(&sb_config);
+}
+
+static void deinit_submodule_cb(const struct cache_entry *list_item,
+ void *cb_data)
+{
+ struct deinit_cb *info = cb_data;
+ deinit_submodule(list_item->name, info->prefix, info->flags);
+}
+
+static int module_deinit(int argc, const char **argv, const char *prefix)
+{
+ struct deinit_cb info = DEINIT_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int force = 0;
+ int all = 0;
+
+ struct option module_deinit_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+ OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
+ OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_deinit_options,
+ git_submodule_helper_usage, 0);
+
+ if (all && argc) {
+ error("pathspec and --all are incompatible");
+ usage_with_options(git_submodule_helper_usage,
+ module_deinit_options);
+ }
+
+ if (!argc && !all)
+ die(_("Use '--all' if you really want to deinitialize all submodules"));
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+ if (force)
+ info.flags |= OPT_FORCE;
+
+ for_each_listed_submodule(&list, deinit_submodule_cb, &info);
+
+ return 0;
+}
+
static int clone_submodule(const char *path, const char *gitdir, const char *url,
- const char *depth, struct string_list *reference,
+ const char *depth, struct string_list *reference, int dissociate,
int quiet, int progress)
{
struct child_process cp = CHILD_PROCESS_INIT;
@@ -500,6 +1229,8 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
argv_array_pushl(&cp.args, "--reference",
item->string, NULL);
}
+ if (dissociate)
+ argv_array_push(&cp.args, "--dissociate");
if (gitdir && *gitdir)
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
@@ -615,6 +1346,7 @@ 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;
char *sm_alternate = NULL, *error_strategy = NULL;
struct option module_clone_options[] = {
@@ -633,6 +1365,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST(0, "reference", &reference,
N_("repo"),
N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &dissociate,
+ N_("use --reference only while cloning")),
OPT_STRING(0, "depth", &depth,
N_("string"),
N_("depth for shallow clones")),
@@ -672,7 +1406,7 @@ 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,
+ if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,
quiet, progress))
die(_("clone of '%s' into submodule path '%s' failed"),
url, path);
@@ -684,8 +1418,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
strbuf_reset(&sb);
}
- /* Connect module worktree and git dir */
- connect_work_tree_and_git_dir(path, sm_gitdir);
+ connect_work_tree_and_git_dir(path, sm_gitdir, 0);
p = git_pathdup_submodule(path, "config");
if (!p)
@@ -725,6 +1458,7 @@ struct submodule_update_clone {
int quiet;
int recommend_shallow;
struct string_list references;
+ int dissociate;
const char *depth;
const char *recursive_prefix;
const char *prefix;
@@ -740,7 +1474,7 @@ struct submodule_update_clone {
int failed_clones_nr, failed_clones_alloc;
};
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
- SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
+ SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
NULL, NULL, NULL, \
STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
@@ -792,7 +1526,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
- sub = submodule_from_path(&null_oid, ce->name);
+ sub = submodule_from_path(the_repository, &null_oid, ce->name);
if (suc->recursive_prefix)
displaypath = relative_path(suc->recursive_prefix,
@@ -867,6 +1601,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
for_each_string_list_item(item, &suc->references)
argv_array_pushl(&child->args, "--reference", item->string, NULL);
}
+ if (suc->dissociate)
+ argv_array_push(&child->args, "--dissociate");
if (suc->depth)
argv_array_push(&child->args, suc->depth);
@@ -1000,6 +1736,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
N_("rebase, merge, checkout or none")),
OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &suc.dissociate,
+ N_("use --reference only while cloning")),
OPT_STRING(0, "depth", &suc.depth, "<depth>",
N_("Create a shallow clone truncated to the "
"specified number of revisions")),
@@ -1075,7 +1813,7 @@ static const char *remote_submodule_branch(const char *path)
const char *branch = NULL;
char *key;
- sub = submodule_from_path(&null_oid, path);
+ sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub)
return NULL;
@@ -1144,7 +1882,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
argv++;
argc--;
/* Get the submodule's head ref and determine if it is detached */
- head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+ head = resolve_refdup("HEAD", 0, &head_oid, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
@@ -1160,13 +1898,14 @@ static int push_check(int argc, const char **argv, const char *prefix)
/* Check the refspec */
if (argc > 2) {
- int i, refspec_nr = argc - 2;
+ int i;
struct ref *local_refs = get_local_heads();
- struct refspec *refspec = parse_push_refspec(refspec_nr,
- argv + 2);
+ struct refspec refspec = REFSPEC_INIT_PUSH;
- for (i = 0; i < refspec_nr; i++) {
- struct refspec *rs = refspec + i;
+ refspec_appendn(&refspec, argv + 2, argc - 2);
+
+ for (i = 0; i < refspec.nr; i++) {
+ const struct refspec_item *rs = &refspec.items[i];
if (rs->pattern || rs->matching)
continue;
@@ -1193,7 +1932,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
rs->src);
}
}
- free_refspec(refspec_nr, refspec);
+ refspec_clear(&refspec);
}
free(head);
@@ -1242,6 +1981,29 @@ static int is_active(int argc, const char **argv, const char *prefix)
return !is_submodule_active(the_repository, argv[1]);
}
+/*
+ * Exit non-zero if any of the submodule names given on the command line is
+ * invalid. If no names are given, filter stdin to print only valid names
+ * (which is primarily intended for testing).
+ */
+static int check_name(int argc, const char **argv, const char *prefix)
+{
+ if (argc > 1) {
+ while (*++argv) {
+ if (check_submodule_name(*argv) < 0)
+ return 1;
+ }
+ } else {
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!check_submodule_name(buf.buf))
+ printf("%s\n", buf.buf);
+ }
+ strbuf_release(&buf);
+ }
+ return 0;
+}
+
#define SUPPORT_SUPER_PREFIX (1<<0)
struct cmd_struct {
@@ -1258,11 +2020,17 @@ static struct cmd_struct commands[] = {
{"relative-path", resolve_relative_path, 0},
{"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0},
+ {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
{"init", module_init, SUPPORT_SUPER_PREFIX},
+ {"status", module_status, SUPPORT_SUPER_PREFIX},
+ {"print-default-remote", print_default_remote, 0},
+ {"sync", module_sync, SUPPORT_SUPER_PREFIX},
+ {"deinit", module_deinit, 0},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
{"is-active", is_active, 0},
+ {"check-name", check_name, 0},
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index 17aabaa679..80237f0df1 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -58,7 +58,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
die("Cannot delete %s, not a symbolic ref", argv[0]);
if (!strcmp(argv[0], "HEAD"))
die("deleting '%s' is not allowed", argv[0]);
- return delete_ref(NULL, argv[0], NULL, REF_NODEREF);
+ return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
}
switch (argc) {
diff --git a/builtin/tag.c b/builtin/tag.c
index b38329b593..5d0dd11240 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -82,7 +82,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
for (p = argv; *p; p++) {
strbuf_reset(&ref);
strbuf_addf(&ref, "refs/tags/%s", *p);
- if (read_ref(ref.buf, oid.hash)) {
+ if (read_ref(ref.buf, &oid)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
@@ -97,9 +97,10 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
static int delete_tag(const char *name, const char *ref,
const struct object_id *oid, const void *cb_data)
{
- if (delete_ref(NULL, ref, oid->hash, 0))
+ if (delete_ref(NULL, ref, oid, 0))
return 1;
- printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
+ printf(_("Deleted tag '%s' (was %s)\n"), name,
+ find_unique_abbrev(oid, DEFAULT_ABBREV));
return 0;
}
@@ -117,7 +118,7 @@ static int verify_tag(const char *name, const char *ref,
return -1;
if (format->format)
- pretty_print_ref(name, oid->hash, format);
+ pretty_print_ref(name, oid, format);
return 0;
}
@@ -167,7 +168,7 @@ static void write_tag_body(int fd, const struct object_id *oid)
enum object_type type;
char *buf, *sp;
- buf = read_sha1_file(oid->hash, &type, &size);
+ buf = read_object_file(oid, &type, &size);
if (!buf)
return;
/* skip header */
@@ -187,13 +188,14 @@ static int build_tag_object(struct strbuf *buf, int sign, struct object_id *resu
{
if (sign && do_sign(buf) < 0)
return error(_("unable to sign the tag"));
- if (write_sha1_file(buf->buf, buf->len, tag_type, result->hash) < 0)
+ if (write_object_file(buf->buf, buf->len, tag_type, result) < 0)
return error(_("unable to write tag file"));
return 0;
}
struct create_tag_options {
unsigned int message_given:1;
+ unsigned int use_editor:1;
unsigned int sign;
enum {
CLEANUP_NONE,
@@ -210,7 +212,7 @@ static void create_tag(const struct object_id *object, const char *tag,
struct strbuf header = STRBUF_INIT;
char *path = NULL;
- type = sha1_object_info(object->hash, NULL);
+ type = oid_object_info(the_repository, object, NULL);
if (type <= OBJ_NONE)
die(_("bad object type."));
@@ -220,11 +222,11 @@ static void create_tag(const struct object_id *object, const char *tag,
"tag %s\n"
"tagger %s\n\n",
oid_to_hex(object),
- typename(type),
+ type_name(type),
tag,
git_committer_info(IDENT_STRICT));
- if (!opt->message_given) {
+ if (!opt->message_given || opt->use_editor) {
int fd;
/* write the template message before editing: */
@@ -233,7 +235,10 @@ static void create_tag(const struct object_id *object, const char *tag,
if (fd < 0)
die_errno(_("could not create file '%s'"), path);
- if (!is_null_oid(prev)) {
+ if (opt->message_given) {
+ write_or_die(fd, buf->buf, buf->len);
+ strbuf_reset(buf);
+ } else if (!is_null_oid(prev)) {
write_tag_body(fd, prev);
} else {
struct strbuf buf = STRBUF_INIT;
@@ -289,17 +294,17 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
strbuf_addstr(sb, rla);
} else {
strbuf_addstr(sb, "tag: tagging ");
- strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(sb, oid, DEFAULT_ABBREV);
}
strbuf_addstr(sb, " (");
- type = sha1_object_info(oid->hash, NULL);
+ type = oid_object_info(the_repository, oid, NULL);
switch (type) {
default:
strbuf_addstr(sb, "object of unknown type");
break;
case OBJ_COMMIT:
- if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) {
+ if ((buf = read_object_file(oid, &type, &size)) != NULL) {
subject_len = find_commit_subject(buf, &subject_start);
strbuf_insert(sb, sb->len, subject_start, subject_len);
} else {
@@ -372,6 +377,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
struct ref_format format = REF_FORMAT_INIT;
int icase = 0;
+ int edit_flag = 0;
struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -386,12 +392,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK('m', "message", &msg, N_("message"),
N_("tag message"), parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+ OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
N_("how to strip spaces and #comments from message")),
OPT_STRING('u', "local-user", &keyid, N_("key-id"),
N_("use another key to sign the tag")),
- OPT__FORCE(&force, N_("replace the tag if exists")),
+ OPT__FORCE(&force, N_("replace the tag if exists"), 0),
OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")),
OPT_GROUP(N_("Tag listing options")),
@@ -518,12 +525,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (read_ref(ref.buf, prev.hash))
+ if (read_ref(ref.buf, &prev))
oidclr(&prev);
else if (!force)
die(_("tag '%s' already exists"), tag);
opt.message_given = msg.given || msgfile;
+ opt.use_editor = edit_flag;
if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
opt.cleanup_mode = CLEANUP_ALL;
@@ -544,14 +552,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, ref.buf, object.hash, prev.hash,
+ ref_transaction_update(transaction, ref.buf, &object, &prev,
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
reflog_msg.buf, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
if (force && !is_null_oid(&prev) && oidcmp(&prev, &object))
- printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV));
+ printf(_("Updated tag '%s' (was %s)\n"), tag,
+ find_unique_abbrev(&prev, DEFAULT_ABBREV));
UNLEAK(buf);
UNLEAK(ref);
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 32e0155577..300eb59657 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -9,7 +9,7 @@ static char *create_temp_file(struct object_id *oid)
unsigned long size;
int fd;
- buf = read_sha1_file(oid->hash, &type, &size);
+ buf = read_object_file(oid, &type, &size);
if (!buf || type != OBJ_BLOB)
die("unable to read blob object %s", oid_to_hex(oid));
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 62ea264c46..6e81ca8ca2 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -21,7 +21,7 @@ static unsigned char buffer[4096];
static unsigned int offset, len;
static off_t consumed_bytes;
static off_t max_input_size;
-static git_SHA_CTX ctx;
+static git_hash_ctx ctx;
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
/*
@@ -62,7 +62,7 @@ static void *fill(int min)
if (min > sizeof(buffer))
die("cannot fill %d bytes", min);
if (offset) {
- git_SHA1_Update(&ctx, buffer, offset);
+ the_hash_algo->update_fn(&ctx, buffer, offset);
memmove(buffer, buffer + offset, len);
offset = 0;
}
@@ -158,6 +158,7 @@ struct obj_info {
struct object *obj;
};
+/* Remember to update object flag allocation in object.h */
#define FLAG_OPEN (1u<<20)
#define FLAG_WRITTEN (1u<<21)
@@ -172,7 +173,8 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
{
struct object_id oid;
- if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), oid.hash) < 0)
+ if (write_object_file(obj_buf->buffer, obj_buf->size,
+ type_name(obj->type), &oid) < 0)
die("failed to write object %s", oid_to_hex(&obj->oid));
obj->flags |= FLAG_WRITTEN;
}
@@ -197,7 +199,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op
if (!(obj->flags & FLAG_OPEN)) {
unsigned long size;
- int type = sha1_object_info(obj->oid.hash, &size);
+ int type = oid_object_info(the_repository, &obj->oid, &size);
if (type != obj->type || type <= 0)
die("object of unexpected type");
obj->flags |= FLAG_WRITTEN;
@@ -208,7 +210,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op
if (!obj_buf)
die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid));
if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))
- die("Error in object");
+ die("fsck error in packed object");
fsck_options.walk = check_object;
if (fsck_walk(obj, NULL, &fsck_options))
die("Error on reachable objects of %s", oid_to_hex(&obj->oid));
@@ -237,14 +239,16 @@ static void write_object(unsigned nr, enum object_type type,
void *buf, unsigned long size)
{
if (!strict) {
- if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0)
+ if (write_object_file(buf, size, type_name(type),
+ &obj_list[nr].oid) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
free(buf);
obj_list[nr].obj = NULL;
} else if (type == OBJ_BLOB) {
struct blob *blob;
- if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0)
+ if (write_object_file(buf, size, type_name(type),
+ &obj_list[nr].oid) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
free(buf);
@@ -258,12 +262,12 @@ static void write_object(unsigned nr, enum object_type type,
} else {
struct object *obj;
int eaten;
- hash_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash);
+ hash_object_file(buf, size, type_name(type), &obj_list[nr].oid);
added_object(nr, type, buf, size);
obj = parse_object_buffer(&obj_list[nr].oid, type, size, buf,
&eaten);
if (!obj)
- die("invalid %s", typename(type));
+ die("invalid %s", type_name(type));
add_object_buffer(obj, buf, size);
obj->flags |= FLAG_OPEN;
obj_list[nr].obj = obj;
@@ -345,8 +349,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
struct object_id base_oid;
if (type == OBJ_REF_DELTA) {
- hashcpy(base_oid.hash, fill(GIT_SHA1_RAWSZ));
- use(GIT_SHA1_RAWSZ);
+ hashcpy(base_oid.hash, fill(the_hash_algo->rawsz));
+ use(the_hash_algo->rawsz);
delta_data = get_data(delta_size);
if (dry_run || !delta_data) {
free(delta_data);
@@ -419,7 +423,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
return;
- base = read_sha1_file(base_oid.hash, &type, &base_size);
+ base = read_object_file(&base_oid, &type, &base_size);
if (!base) {
error("failed to read delta-pack base object %s",
oid_to_hex(&base_oid));
@@ -564,15 +568,18 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
/* We don't take any non-flag arguments now.. Maybe some day */
usage(unpack_usage);
}
- git_SHA1_Init(&ctx);
+ the_hash_algo->init_fn(&ctx);
unpack_all();
- git_SHA1_Update(&ctx, buffer, offset);
- git_SHA1_Final(oid.hash, &ctx);
- if (strict)
+ the_hash_algo->update_fn(&ctx, buffer, offset);
+ the_hash_algo->final_fn(oid.hash, &ctx);
+ if (strict) {
write_rest();
- if (hashcmp(fill(GIT_SHA1_RAWSZ), oid.hash))
+ if (fsck_finish(&fsck_options))
+ die(_("fsck error in pack objects"));
+ }
+ if (hashcmp(fill(the_hash_algo->rawsz), oid.hash))
die("final sha1 did not match");
- use(GIT_SHA1_RAWSZ);
+ use(the_hash_algo->rawsz);
/* Write the last part of the buffer to stdout */
while (len) {
diff --git a/builtin/update-index.c b/builtin/update-index.c
index bf7420b808..a8709a26ec 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -16,6 +16,7 @@
#include "pathspec.h"
#include "dir.h"
#include "split-index.h"
+#include "fsmonitor.h"
/*
* Default to not allowing changes to the list of files. The
@@ -32,6 +33,7 @@ static int force_remove;
static int verbose;
static int mark_valid_only;
static int mark_skip_worktree_only;
+static int mark_fsmonitor_only;
#define MARK_FLAG 1
#define UNMARK_FLAG 2
static struct strbuf mtime_dir = STRBUF_INIT;
@@ -228,6 +230,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)
int namelen = strlen(path);
int pos = cache_name_pos(path, namelen);
if (0 <= pos) {
+ mark_fsmonitor_invalid(&the_index, active_cache[pos]);
if (mark)
active_cache[pos]->ce_flags |= flag;
else
@@ -328,7 +331,7 @@ static int process_directory(const char *path, int len, struct stat *st)
if (S_ISGITLINK(ce->ce_mode)) {
/* Do nothing to the index if there is no HEAD! */
- if (resolve_gitlink_ref(path, "HEAD", oid.hash) < 0)
+ if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
return 0;
return add_one_path(ce, path, len, st);
@@ -354,17 +357,16 @@ static int process_directory(const char *path, int len, struct stat *st)
}
/* No match - should we add it as a gitlink? */
- if (!resolve_gitlink_ref(path, "HEAD", oid.hash))
+ if (!resolve_gitlink_ref(path, "HEAD", &oid))
return add_one_path(NULL, path, len, st);
/* Error out. */
return error("%s: is a directory - add files inside instead", path);
}
-static int process_path(const char *path)
+static int process_path(const char *path, struct stat *st, int stat_errno)
{
int pos, len;
- struct stat st;
const struct cache_entry *ce;
len = strlen(path);
@@ -388,13 +390,13 @@ static int process_path(const char *path)
* First things first: get the stat information, to decide
* what to do about the pathname!
*/
- if (lstat(path, &st) < 0)
- return process_lstat_error(path, errno);
+ if (stat_errno)
+ return process_lstat_error(path, stat_errno);
- if (S_ISDIR(st.st_mode))
- return process_directory(path, len, &st);
+ if (S_ISDIR(st->st_mode))
+ return process_directory(path, len, st);
- return add_one_path(ce, path, len, &st);
+ return add_one_path(ce, path, len, st);
}
static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
@@ -403,7 +405,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
int size, len, option;
struct cache_entry *ce;
- if (!verify_path(path))
+ if (!verify_path(path, mode))
return error("Invalid path '%s'", path);
len = strlen(path);
@@ -446,7 +448,18 @@ static void chmod_path(char flip, const char *path)
static void update_one(const char *path)
{
- if (!verify_path(path)) {
+ int stat_errno = 0;
+ struct stat st;
+
+ if (mark_valid_only || mark_skip_worktree_only || force_remove ||
+ mark_fsmonitor_only)
+ st.st_mode = 0;
+ else if (lstat(path, &st) < 0) {
+ st.st_mode = 0;
+ stat_errno = errno;
+ } /* else stat is valid */
+
+ if (!verify_path(path, st.st_mode)) {
fprintf(stderr, "Ignoring path %s\n", path);
return;
}
@@ -460,6 +473,11 @@ static void update_one(const char *path)
die("Unable to mark file %s", path);
return;
}
+ if (mark_fsmonitor_only) {
+ if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG))
+ die("Unable to mark file %s", path);
+ return;
+ }
if (force_remove) {
if (remove_file_from_cache(path))
@@ -467,7 +485,7 @@ static void update_one(const char *path)
report("remove '%s'", path);
return;
}
- if (process_path(path))
+ if (process_path(path, &st, stat_errno))
die("Unable to process path %s", path);
report("add '%s'", path);
}
@@ -537,7 +555,7 @@ static void read_index_info(int nul_term_line)
path_name = uq.buf;
}
- if (!verify_path(path_name)) {
+ if (!verify_path(path_name, mode)) {
fprintf(stderr, "Ignoring path %s\n", path_name);
continue;
}
@@ -584,7 +602,7 @@ static struct cache_entry *read_one_ent(const char *which,
int size;
struct cache_entry *ce;
- if (get_tree_entry(ent->hash, path, oid.hash, &mode)) {
+ if (get_tree_entry(ent, path, &oid, &mode)) {
if (which)
error("%s: not in %s branch.", path, which);
return NULL;
@@ -679,9 +697,9 @@ static int unresolve_one(const char *path)
static void read_head_pointers(void)
{
- if (read_ref("HEAD", head_oid.hash))
+ if (read_ref("HEAD", &head_oid))
die("No HEAD -- no initial commit yet?");
- if (read_ref("MERGE_HEAD", merge_head_oid.hash)) {
+ if (read_ref("MERGE_HEAD", &merge_head_oid)) {
fprintf(stderr, "Not in the middle of a merge.\n");
exit(0);
}
@@ -721,7 +739,7 @@ static int do_reupdate(int ac, const char **av,
PATHSPEC_PREFER_CWD,
prefix, av + 1);
- if (read_ref("HEAD", head_oid.hash))
+ if (read_ref("HEAD", &head_oid))
/* If there is no HEAD, that means it is an initial
* commit. Update everything in the index.
*/
@@ -917,6 +935,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
struct refresh_params refresh_args = {0, &has_errors};
int lock_error = 0;
int split_index = -1;
+ int force_write = 0;
+ int fsmonitor = -1;
struct lock_file lock_file = LOCK_INIT;
struct parse_opt_ctx_t ctx;
strbuf_getline_fn getline_fn;
@@ -1008,6 +1028,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
N_("test if the filesystem supports untracked cache"), UC_TEST),
OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
N_("enable untracked cache without testing the filesystem"), UC_FORCE),
+ OPT_SET_INT(0, "force-write-index", &force_write,
+ N_("write out the index even if is not flagged as changed"), 1),
+ OPT_BOOL(0, "fsmonitor", &fsmonitor,
+ N_("enable or disable file system monitor")),
+ {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL,
+ N_("mark files as fsmonitor valid"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+ {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL,
+ N_("clear fsmonitor valid bit"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
OPT_END()
};
@@ -1039,6 +1069,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
break;
switch (parseopt_state) {
case PARSE_OPT_HELP:
+ case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_NON_OPTION:
case PARSE_OPT_DONE:
@@ -1143,10 +1174,26 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
break;
default:
- die("BUG: bad untracked_cache value: %d", untracked_cache);
+ BUG("bad untracked_cache value: %d", untracked_cache);
+ }
+
+ if (fsmonitor > 0) {
+ if (git_config_get_fsmonitor() == 0)
+ warning(_("core.fsmonitor is unset; "
+ "set it if you really want to "
+ "enable fsmonitor"));
+ add_fsmonitor(&the_index);
+ report(_("fsmonitor enabled"));
+ } else if (!fsmonitor) {
+ if (git_config_get_fsmonitor() == 1)
+ warning(_("core.fsmonitor is set; "
+ "remove it if you really want to "
+ "disable fsmonitor"));
+ remove_fsmonitor(&the_index);
+ report(_("fsmonitor disabled"));
}
- if (active_cache_changed) {
+ if (active_cache_changed || force_write) {
if (newfd < 0) {
if (refresh_args.flags & REFRESH_QUIET)
exit(128);
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6b90c5dead..4fa3c0a86f 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -200,7 +200,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname,
- new_oid.hash, have_old ? old_oid.hash : NULL,
+ &new_oid, have_old ? &old_oid : NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -232,7 +232,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
- if (ref_transaction_create(transaction, refname, new_oid.hash,
+ if (ref_transaction_create(transaction, refname, &new_oid,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -269,7 +269,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
die("delete %s: extra input: %s", refname, next);
if (ref_transaction_delete(transaction, refname,
- have_old ? old_oid.hash : NULL,
+ have_old ? &old_oid : NULL,
update_flags, msg, &err))
die("%s", err.buf);
@@ -298,7 +298,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
if (*next != line_termination)
die("verify %s: extra input: %s", refname, next);
- if (ref_transaction_verify(transaction, refname, old_oid.hash,
+ if (ref_transaction_verify(transaction, refname, &old_oid,
update_flags, &err))
die("%s", err.buf);
@@ -311,11 +311,12 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
static const char *parse_cmd_option(struct strbuf *input, const char *next)
{
- if (!strncmp(next, "no-deref", 8) && next[8] == line_termination)
- update_flags |= REF_NODEREF;
+ const char *rest;
+ if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
+ update_flags |= REF_NO_DEREF;
else
die("option unknown: %s", next);
- return next + 8;
+ return rest;
}
static void update_refs_stdin(struct ref_transaction *transaction)
@@ -332,16 +333,16 @@ static void update_refs_stdin(struct ref_transaction *transaction)
die("empty command in input");
else if (isspace(*next))
die("whitespace before command: %s", next);
- else if (starts_with(next, "update "))
- next = parse_cmd_update(transaction, &input, next + 7);
- else if (starts_with(next, "create "))
- next = parse_cmd_create(transaction, &input, next + 7);
- else if (starts_with(next, "delete "))
- next = parse_cmd_delete(transaction, &input, next + 7);
- else if (starts_with(next, "verify "))
- next = parse_cmd_verify(transaction, &input, next + 7);
- else if (starts_with(next, "option "))
- next = parse_cmd_option(&input, next + 7);
+ else if (skip_prefix(next, "update ", &next))
+ next = parse_cmd_update(transaction, &input, next);
+ else if (skip_prefix(next, "create ", &next))
+ next = parse_cmd_create(transaction, &input, next);
+ else if (skip_prefix(next, "delete ", &next))
+ next = parse_cmd_delete(transaction, &input, next);
+ else if (skip_prefix(next, "verify ", &next))
+ next = parse_cmd_verify(transaction, &input, next);
+ else if (skip_prefix(next, "option ", &next))
+ next = parse_cmd_option(&input, next);
else
die("unknown command: %s", next);
@@ -427,17 +428,17 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
}
if (no_deref)
- flags = REF_NODEREF;
+ flags = REF_NO_DEREF;
if (delete)
/*
* For purposes of backwards compatibility, we treat
* NULL_SHA1 as "don't care" here:
*/
return delete_ref(msg, refname,
- (oldval && !is_null_oid(&oldoid)) ? oldoid.hash : NULL,
+ (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
flags);
else
- return update_ref(msg, refname, oid.hash, oldval ? oldoid.hash : NULL,
+ return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
flags | create_reflog_flag,
UPDATE_REFS_DIE_ON_ERR);
}
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index 873070e517..4321a34456 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -12,7 +12,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix)
{
int force = 0;
struct option options[] = {
- OPT__FORCE(&force, N_("update the info files from scratch")),
+ OPT__FORCE(&force, N_("update the info files from scratch"), 0),
OPT_END()
};
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
new file mode 100644
index 0000000000..decde5a3b1
--- /dev/null
+++ b/builtin/upload-pack.c
@@ -0,0 +1,74 @@
+#include "cache.h"
+#include "builtin.h"
+#include "exec-cmd.h"
+#include "pkt-line.h"
+#include "parse-options.h"
+#include "protocol.h"
+#include "upload-pack.h"
+#include "serve.h"
+
+static const char * const upload_pack_usage[] = {
+ N_("git upload-pack [<options>] <dir>"),
+ NULL
+};
+
+int cmd_upload_pack(int argc, const char **argv, const char *prefix)
+{
+ const char *dir;
+ int strict = 0;
+ struct upload_pack_options opts = { 0 };
+ struct serve_options serve_opts = SERVE_OPTIONS_INIT;
+ struct option options[] = {
+ OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+ N_("quit after a single request/response exchange")),
+ OPT_BOOL(0, "advertise-refs", &opts.advertise_refs,
+ N_("exit immediately after initial ref advertisement")),
+ OPT_BOOL(0, "strict", &strict,
+ N_("do not try <directory>/.git/ if <directory> is no Git directory")),
+ OPT_INTEGER(0, "timeout", &opts.timeout,
+ N_("interrupt transfer after <n> seconds of inactivity")),
+ OPT_END()
+ };
+
+ packet_trace_identity("upload-pack");
+ check_replace_refs = 0;
+
+ argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
+
+ if (argc != 1)
+ usage_with_options(upload_pack_usage, options);
+
+ if (opts.timeout)
+ opts.daemon_mode = 1;
+
+ setup_path();
+
+ dir = argv[0];
+
+ if (!enter_repo(dir, strict))
+ die("'%s' does not appear to be a git repository", dir);
+
+ switch (determine_protocol_version_server()) {
+ case protocol_v2:
+ serve_opts.advertise_capabilities = opts.advertise_refs;
+ serve_opts.stateless_rpc = opts.stateless_rpc;
+ serve(&serve_opts);
+ break;
+ case protocol_v1:
+ /*
+ * v1 is just the original protocol with a version string,
+ * so just fall through after writing the version string.
+ */
+ if (opts.advertise_refs || !opts.stateless_rpc)
+ packet_write_fmt(1, "version 1\n");
+
+ /* fallthrough */
+ case protocol_v0:
+ upload_pack(&opts);
+ break;
+ case protocol_unknown_version:
+ BUG("unknown protocol version");
+ }
+
+ return 0;
+}
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index ba38ac9b15..dcdaada111 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -44,12 +44,12 @@ static int verify_commit(const char *name, unsigned flags)
if (get_oid(name, &oid))
return error("commit '%s' not found.", name);
- buf = read_sha1_file(oid.hash, &type, &size);
+ buf = read_object_file(&oid, &type, &size);
if (!buf)
return error("%s: unable to read file.", name);
if (type != OBJ_COMMIT)
return error("%s: cannot verify a non-commit object of type %s.",
- name, typename(type));
+ name, type_name(type));
ret = run_gpg_verify(&oid, buf, size, flags);
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index ad7b79fa5c..6fa04b751a 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -72,7 +72,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
}
if (format.format)
- pretty_print_ref(name, oid.hash, &format);
+ pretty_print_ref(name, &oid, &format);
}
return had_error;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7b9307aa58..5c7d2bb180 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "checkout.h"
#include "config.h"
#include "builtin.h"
#include "dir.h"
@@ -13,10 +14,12 @@
#include "worktree.h"
static const char * const worktree_usage[] = {
- N_("git worktree add [<options>] <path> [<branch>]"),
+ N_("git worktree add [<options>] <path> [<commit-ish>]"),
N_("git worktree list [<options>]"),
N_("git worktree lock [<options>] <path>"),
+ N_("git worktree move <worktree> <new-path>"),
N_("git worktree prune [<options>]"),
+ N_("git worktree remove [<options>] <worktree>"),
N_("git worktree unlock <path>"),
NULL
};
@@ -26,14 +29,23 @@ struct add_opts {
int detach;
int checkout;
int keep_locked;
- const char *new_branch;
- int force_new_branch;
};
static int show_only;
static int verbose;
+static int guess_remote;
static timestamp_t expire;
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "worktree.guessremote")) {
+ guess_remote = git_config_bool(var, value);
+ return 0;
+ }
+
+ return git_default_config(var, value, cb);
+}
+
static int prune_worktree(const char *id, struct strbuf *reason)
{
struct stat st;
@@ -87,16 +99,9 @@ static int prune_worktree(const char *id, struct strbuf *reason)
}
path[len] = '\0';
if (!file_exists(path)) {
- struct stat st_link;
free(path);
- /*
- * the repo is moved manually and has not been
- * accessed since?
- */
- if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
- st_link.st_nlink > 1)
- return 0;
- if (st.st_mtime <= expire) {
+ if (stat(git_path("worktrees/%s/index", id), &st) ||
+ st.st_mtime <= expire) {
strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
return 1;
} else {
@@ -218,20 +223,21 @@ static int add_worktree(const char *path, const char *refname,
int counter = 0, len, ret;
struct strbuf symref = STRBUF_INIT;
struct commit *commit = NULL;
+ int is_branch = 0;
if (file_exists(path) && !is_empty_dir(path))
die(_("'%s' already exists"), path);
/* is 'refname' a branch or commit? */
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
- ref_exists(symref.buf)) { /* it's a branch */
+ ref_exists(symref.buf)) {
+ is_branch = 1;
if (!opts->force)
die_if_checked_out(symref.buf, 0);
- } else { /* must be a commit */
- commit = lookup_commit_reference_by_name(refname);
- if (!commit)
- die(_("invalid reference: %s"), refname);
}
+ commit = lookup_commit_reference_by_name(refname);
+ if (!commit)
+ die(_("invalid reference: %s"), refname);
name = worktree_basename(path, &len);
git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
@@ -290,13 +296,11 @@ static int add_worktree(const char *path, const char *refname,
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
- fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name);
-
argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
- if (commit)
+ if (!is_branch)
argv_array_pushl(&cp.args, "update-ref", "HEAD",
oid_to_hex(&commit->object.oid), NULL);
else
@@ -327,6 +331,29 @@ done:
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
unlink_or_warn(sb.buf);
}
+
+ /*
+ * Hook failure does not warrant worktree deletion, so run hook after
+ * is_junk is cleared, but do return appropriate code when hook fails.
+ */
+ if (!ret && opts->checkout) {
+ const char *hook = find_hook("post-checkout");
+ if (hook) {
+ const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
+ cp.git_cmd = 0;
+ cp.no_stdin = 1;
+ cp.stdout_to_stderr = 1;
+ cp.dir = path;
+ cp.env = env;
+ cp.argv = NULL;
+ argv_array_pushl(&cp.args, absolute_path(hook),
+ oid_to_hex(&null_oid),
+ oid_to_hex(&commit->object.oid),
+ "1", NULL);
+ ret = run_command(&cp);
+ }
+ }
+
argv_array_clear(&child_env);
strbuf_release(&sb);
strbuf_release(&symref);
@@ -335,28 +362,93 @@ done:
return ret;
}
+static void print_preparing_worktree_line(int detach,
+ const char *branch,
+ const char *new_branch,
+ int force_new_branch)
+{
+ if (force_new_branch) {
+ struct commit *commit = lookup_commit_reference_by_name(new_branch);
+ if (!commit)
+ printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+ else
+ printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
+ new_branch,
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+ } else if (new_branch) {
+ printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+ } else {
+ struct strbuf s = STRBUF_INIT;
+ if (!detach && !strbuf_check_branch_ref(&s, branch) &&
+ ref_exists(s.buf))
+ printf_ln(_("Preparing worktree (checking out '%s')"),
+ branch);
+ else {
+ struct commit *commit = lookup_commit_reference_by_name(branch);
+ if (!commit)
+ die(_("invalid reference: %s"), branch);
+ printf_ln(_("Preparing worktree (detached HEAD %s)"),
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+ }
+ strbuf_release(&s);
+ }
+}
+
+static const char *dwim_branch(const char *path, const char **new_branch)
+{
+ int n;
+ const char *s = worktree_basename(path, &n);
+ const char *branchname = xstrndup(s, n);
+ struct strbuf ref = STRBUF_INIT;
+
+ UNLEAK(branchname);
+ if (!strbuf_check_branch_ref(&ref, branchname) &&
+ ref_exists(ref.buf)) {
+ strbuf_release(&ref);
+ return branchname;
+ }
+
+ *new_branch = branchname;
+ if (guess_remote) {
+ struct object_id oid;
+ const char *remote =
+ unique_tracking_name(*new_branch, &oid);
+ return remote;
+ }
+ return NULL;
+}
+
static int add(int ac, const char **av, const char *prefix)
{
struct add_opts opts;
const char *new_branch_force = NULL;
char *path;
const char *branch;
+ const char *new_branch = NULL;
+ const char *opt_track = NULL;
struct option options[] = {
- OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
- OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+ OPT__FORCE(&opts.force,
+ N_("checkout <branch> even if already checked out in other worktree"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_STRING('b', NULL, &new_branch, N_("branch"),
N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+ OPT_PASSTHRU(0, "track", &opt_track, NULL,
+ N_("set up tracking mode (see git-branch(1))"),
+ PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
+ OPT_BOOL(0, "guess-remote", &guess_remote,
+ N_("try to match the new branch name with a remote-tracking branch")),
OPT_END()
};
memset(&opts, 0, sizeof(opts));
opts.checkout = 1;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
- if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)
+ if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
die(_("-b, -B, and --detach are mutually exclusive"));
if (ac < 1 || ac > 2)
usage_with_options(worktree_usage, options);
@@ -367,36 +459,56 @@ static int add(int ac, const char **av, const char *prefix)
if (!strcmp(branch, "-"))
branch = "@{-1}";
- opts.force_new_branch = !!new_branch_force;
- if (opts.force_new_branch) {
+ if (new_branch_force) {
struct strbuf symref = STRBUF_INIT;
- opts.new_branch = new_branch_force;
+ new_branch = new_branch_force;
if (!opts.force &&
- !strbuf_check_branch_ref(&symref, opts.new_branch) &&
+ !strbuf_check_branch_ref(&symref, new_branch) &&
ref_exists(symref.buf))
die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
}
- if (ac < 2 && !opts.new_branch && !opts.detach) {
- int n;
- const char *s = worktree_basename(path, &n);
- opts.new_branch = xstrndup(s, n);
+ if (ac < 2 && !new_branch && !opts.detach) {
+ const char *s = dwim_branch(path, &new_branch);
+ if (s)
+ branch = s;
+ }
+
+ if (ac == 2 && !new_branch && !opts.detach) {
+ struct object_id oid;
+ struct commit *commit;
+ const char *remote;
+
+ commit = lookup_commit_reference_by_name(branch);
+ if (!commit) {
+ remote = unique_tracking_name(branch, &oid);
+ if (remote) {
+ new_branch = branch;
+ branch = remote;
+ }
+ }
}
- if (opts.new_branch) {
+ print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
+
+ if (new_branch) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
argv_array_push(&cp.args, "branch");
- if (opts.force_new_branch)
+ if (new_branch_force)
argv_array_push(&cp.args, "--force");
- argv_array_push(&cp.args, opts.new_branch);
+ argv_array_push(&cp.args, new_branch);
argv_array_push(&cp.args, branch);
+ if (opt_track)
+ argv_array_push(&cp.args, opt_track);
if (run_command(&cp))
return -1;
- branch = opts.new_branch;
+ branch = new_branch;
+ } else if (opt_track) {
+ die(_("--[no-]track can only be used if a new branch is created"));
}
UNLEAK(path);
@@ -410,7 +522,7 @@ static void show_worktree_porcelain(struct worktree *wt)
if (wt->is_bare)
printf("bare\n");
else {
- printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
+ printf("HEAD %s\n", oid_to_hex(&wt->head_oid));
if (wt->is_detached)
printf("detached\n");
else if (wt->head_ref)
@@ -430,7 +542,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
strbuf_addstr(&sb, "(bare)");
else {
strbuf_addf(&sb, "%-*s ", abbrev_len,
- find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
+ find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
if (wt->is_detached)
strbuf_addstr(&sb, "(detached HEAD)");
else if (wt->head_ref) {
@@ -455,7 +567,7 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
if (path_len > *maxlen)
*maxlen = path_len;
- sha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev));
+ sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
if (sha1_len > *abbrev)
*abbrev = sha1_len;
}
@@ -551,13 +663,228 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
return ret;
}
+static void validate_no_submodules(const struct worktree *wt)
+{
+ struct index_state istate = { NULL };
+ int i, found_submodules = 0;
+
+ if (read_index_from(&istate, worktree_git_path(wt, "index"),
+ get_worktree_git_dir(wt)) > 0) {
+ for (i = 0; i < istate.cache_nr; i++) {
+ struct cache_entry *ce = istate.cache[i];
+
+ if (S_ISGITLINK(ce->ce_mode)) {
+ found_submodules = 1;
+ break;
+ }
+ }
+ }
+ discard_index(&istate);
+
+ if (found_submodules)
+ die(_("working trees containing submodules cannot be moved or removed"));
+}
+
+static int move_worktree(int ac, const char **av, const char *prefix)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ struct worktree **worktrees, *wt;
+ struct strbuf dst = STRBUF_INIT;
+ struct strbuf errmsg = STRBUF_INIT;
+ const char *reason;
+ char *path;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac != 2)
+ usage_with_options(worktree_usage, options);
+
+ path = prefix_filename(prefix, av[1]);
+ strbuf_addstr(&dst, path);
+ free(path);
+
+ worktrees = get_worktrees(0);
+ wt = find_worktree(worktrees, prefix, av[0]);
+ if (!wt)
+ die(_("'%s' is not a working tree"), av[0]);
+ if (is_main_worktree(wt))
+ die(_("'%s' is a main working tree"), av[0]);
+ if (is_directory(dst.buf)) {
+ const char *sep = find_last_dir_sep(wt->path);
+
+ if (!sep)
+ die(_("could not figure out destination name from '%s'"),
+ wt->path);
+ strbuf_trim_trailing_dir_sep(&dst);
+ strbuf_addstr(&dst, sep);
+ }
+ if (file_exists(dst.buf))
+ die(_("target '%s' already exists"), dst.buf);
+
+ validate_no_submodules(wt);
+
+ reason = is_worktree_locked(wt);
+ if (reason) {
+ if (*reason)
+ die(_("cannot move a locked working tree, lock reason: %s"),
+ reason);
+ die(_("cannot move a locked working tree"));
+ }
+ if (validate_worktree(wt, &errmsg, 0))
+ die(_("validation failed, cannot move working tree: %s"),
+ errmsg.buf);
+ strbuf_release(&errmsg);
+
+ if (rename(wt->path, dst.buf) == -1)
+ die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf);
+
+ update_worktree_location(wt, dst.buf);
+
+ strbuf_release(&dst);
+ free_worktrees(worktrees);
+ return 0;
+}
+
+/*
+ * Note, "git status --porcelain" is used to determine if it's safe to
+ * delete a whole worktree. "git status" does not ignore user
+ * configuration, so if a normal "git status" shows "clean" for the
+ * user, then it's ok to remove it.
+ *
+ * This assumption may be a bad one. We may want to ignore
+ * (potentially bad) user settings and only delete a worktree when
+ * it's absolutely safe to do so from _our_ point of view because we
+ * know better.
+ */
+static void check_clean_worktree(struct worktree *wt,
+ const char *original_path)
+{
+ struct argv_array child_env = ARGV_ARRAY_INIT;
+ struct child_process cp;
+ char buf[1];
+ int ret;
+
+ /*
+ * Until we sort this out, all submodules are "dirty" and
+ * will abort this function.
+ */
+ validate_no_submodules(wt);
+
+ argv_array_pushf(&child_env, "%s=%s/.git",
+ GIT_DIR_ENVIRONMENT, wt->path);
+ argv_array_pushf(&child_env, "%s=%s",
+ GIT_WORK_TREE_ENVIRONMENT, wt->path);
+ memset(&cp, 0, sizeof(cp));
+ argv_array_pushl(&cp.args, "status",
+ "--porcelain", "--ignore-submodules=none",
+ NULL);
+ cp.env = child_env.argv;
+ cp.git_cmd = 1;
+ cp.dir = wt->path;
+ cp.out = -1;
+ ret = start_command(&cp);
+ if (ret)
+ die_errno(_("failed to run 'git status' on '%s'"),
+ original_path);
+ ret = xread(cp.out, buf, sizeof(buf));
+ if (ret)
+ die(_("'%s' is dirty, use --force to delete it"),
+ original_path);
+ close(cp.out);
+ ret = finish_command(&cp);
+ if (ret)
+ die_errno(_("failed to run 'git status' on '%s', code %d"),
+ original_path, ret);
+}
+
+static int delete_git_work_tree(struct worktree *wt)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addstr(&sb, wt->path);
+ if (remove_dir_recursively(&sb, 0)) {
+ error_errno(_("failed to delete '%s'"), sb.buf);
+ ret = -1;
+ }
+ strbuf_release(&sb);
+ return ret;
+}
+
+static int delete_git_dir(struct worktree *wt)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addstr(&sb, git_common_path("worktrees/%s", wt->id));
+ if (remove_dir_recursively(&sb, 0)) {
+ error_errno(_("failed to delete '%s'"), sb.buf);
+ ret = -1;
+ }
+ strbuf_release(&sb);
+ return ret;
+}
+
+static int remove_worktree(int ac, const char **av, const char *prefix)
+{
+ int force = 0;
+ struct option options[] = {
+ OPT__FORCE(&force,
+ N_("force removing even if the worktree is dirty"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT_END()
+ };
+ struct worktree **worktrees, *wt;
+ struct strbuf errmsg = STRBUF_INIT;
+ const char *reason;
+ int ret = 0;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac != 1)
+ usage_with_options(worktree_usage, options);
+
+ worktrees = get_worktrees(0);
+ wt = find_worktree(worktrees, prefix, av[0]);
+ if (!wt)
+ die(_("'%s' is not a working tree"), av[0]);
+ if (is_main_worktree(wt))
+ die(_("'%s' is a main working tree"), av[0]);
+ reason = is_worktree_locked(wt);
+ if (reason) {
+ if (*reason)
+ die(_("cannot remove a locked working tree, lock reason: %s"),
+ reason);
+ die(_("cannot remove a locked working tree"));
+ }
+ if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
+ die(_("validation failed, cannot remove working tree: %s"),
+ errmsg.buf);
+ strbuf_release(&errmsg);
+
+ if (file_exists(wt->path)) {
+ if (!force)
+ check_clean_worktree(wt, av[0]);
+
+ ret |= delete_git_work_tree(wt);
+ }
+ /*
+ * continue on even if ret is non-zero, there's no going back
+ * from here.
+ */
+ ret |= delete_git_dir(wt);
+
+ free_worktrees(worktrees);
+ return ret;
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
OPT_END()
};
- git_config(git_default_config, NULL);
+ git_config(git_worktree_config, NULL);
if (ac < 2)
usage_with_options(worktree_usage, options);
@@ -573,5 +900,9 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
return lock_worktree(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "unlock"))
return unlock_worktree(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "move"))
+ return move_worktree(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "remove"))
+ return remove_worktree(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index bd0a78aa3c..c9d3c544e7 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -19,7 +19,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
{
int flags = 0, ret;
const char *prefix = NULL;
- unsigned char sha1[20];
+ struct object_id oid;
const char *me = "git-write-tree";
struct option write_tree_options[] = {
OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"),
@@ -38,10 +38,10 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
argc = parse_options(argc, argv, unused_prefix, write_tree_options,
write_tree_usage, 0);
- ret = write_cache_as_tree(sha1, flags, prefix);
+ ret = write_cache_as_tree(&oid, flags, prefix);
switch (ret) {
case 0:
- printf("%s\n", sha1_to_hex(sha1));
+ printf("%s\n", oid_to_hex(&oid));
break;
case WRITE_TREE_UNREADABLE_INDEX:
die("%s: error reading the index", me);