summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c115
-rw-r--r--builtin/am.c285
-rw-r--r--builtin/apply.c4
-rw-r--r--builtin/archive.c2
-rw-r--r--builtin/bisect--helper.c137
-rw-r--r--builtin/blame.c2160
-rw-r--r--builtin/branch.c165
-rw-r--r--builtin/cat-file.c29
-rw-r--r--builtin/check-attr.c1
-rw-r--r--builtin/check-ignore.c13
-rw-r--r--builtin/check-mailmap.c1
-rw-r--r--builtin/check-ref-format.c7
-rw-r--r--builtin/checkout-index.c11
-rw-r--r--builtin/checkout.c348
-rw-r--r--builtin/clean.c39
-rw-r--r--builtin/clone.c114
-rw-r--r--builtin/column.c1
-rw-r--r--builtin/commit-tree.c12
-rw-r--r--builtin/commit.c456
-rw-r--r--builtin/config.c64
-rw-r--r--builtin/count-objects.c2
-rw-r--r--builtin/credential.c4
-rw-r--r--builtin/describe.c203
-rw-r--r--builtin/diff-files.c7
-rw-r--r--builtin/diff-index.c8
-rw-r--r--builtin/diff-tree.c35
-rw-r--r--builtin/diff.c33
-rw-r--r--builtin/difftool.c41
-rw-r--r--builtin/fast-export.c91
-rw-r--r--builtin/fetch-pack.c18
-rw-r--r--builtin/fetch.c243
-rw-r--r--builtin/fmt-merge-msg.c19
-rw-r--r--builtin/for-each-ref.c29
-rw-r--r--builtin/fsck.c140
-rw-r--r--builtin/gc.c26
-rw-r--r--builtin/get-tar-commit-id.c9
-rw-r--r--builtin/grep.c499
-rw-r--r--builtin/hash-object.c14
-rw-r--r--builtin/help.c16
-rw-r--r--builtin/index-pack.c311
-rw-r--r--builtin/init-db.c33
-rw-r--r--builtin/interpret-trailers.c107
-rw-r--r--builtin/log.c120
-rw-r--r--builtin/ls-files.c366
-rw-r--r--builtin/ls-remote.c7
-rw-r--r--builtin/ls-tree.c7
-rw-r--r--builtin/merge-base.c51
-rw-r--r--builtin/merge-file.c1
-rw-r--r--builtin/merge-ours.c16
-rw-r--r--builtin/merge-tree.c26
-rw-r--r--builtin/merge.c158
-rw-r--r--builtin/mktag.c6
-rw-r--r--builtin/mktree.c14
-rw-r--r--builtin/mv.c16
-rw-r--r--builtin/name-rev.c48
-rw-r--r--builtin/notes.c170
-rw-r--r--builtin/pack-objects.c409
-rw-r--r--builtin/pack-redundant.c55
-rw-r--r--builtin/patch-id.c1
-rw-r--r--builtin/prune-packed.c6
-rw-r--r--builtin/prune.c26
-rw-r--r--builtin/pull.c132
-rw-r--r--builtin/push.c35
-rw-r--r--builtin/read-tree.c41
-rw-r--r--builtin/rebase--helper.c43
-rw-r--r--builtin/receive-pack.c80
-rw-r--r--builtin/reflog.c104
-rw-r--r--builtin/remote-ext.c7
-rw-r--r--builtin/remote-fd.c5
-rw-r--r--builtin/remote.c94
-rw-r--r--builtin/repack.c14
-rw-r--r--builtin/replace.c70
-rw-r--r--builtin/rerere.c3
-rw-r--r--builtin/reset.c102
-rw-r--r--builtin/rev-list.c205
-rw-r--r--builtin/rev-parse.c112
-rw-r--r--builtin/revert.c7
-rw-r--r--builtin/rm.c20
-rw-r--r--builtin/send-pack.c3
-rw-r--r--builtin/shortlog.c57
-rw-r--r--builtin/show-branch.c46
-rw-r--r--builtin/show-ref.c4
-rw-r--r--builtin/stripspace.c1
-rw-r--r--builtin/submodule--helper.c765
-rw-r--r--builtin/symbolic-ref.c6
-rw-r--r--builtin/tag.c156
-rw-r--r--builtin/unpack-file.c15
-rw-r--r--builtin/unpack-objects.c85
-rw-r--r--builtin/update-index.c71
-rw-r--r--builtin/update-ref.c74
-rw-r--r--builtin/update-server-info.c3
-rw-r--r--builtin/upload-archive.c5
-rw-r--r--builtin/var.c1
-rw-r--r--builtin/verify-commit.c15
-rw-r--r--builtin/verify-pack.c1
-rw-r--r--builtin/verify-tag.c22
-rw-r--r--builtin/worktree.c351
-rw-r--r--builtin/write-tree.c1
98 files changed, 5012 insertions, 5099 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 9f53f020d0..9ef7fb02d5 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -4,6 +4,7 @@
* Copyright (C) 2006 Linus Torvalds
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "lockfile.h"
#include "dir.h"
@@ -17,6 +18,7 @@
#include "revision.h"
#include "bulk-checkin.h"
#include "argv-array.h"
+#include "submodule.h"
static const char * const builtin_add_usage[] = {
N_("git add [<options>] [--] <pathspec>..."),
@@ -24,13 +26,14 @@ 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;
int add_errors;
};
-static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
+static void chmod_pathspec(struct pathspec *pathspec, char flip)
{
int i;
@@ -40,8 +43,8 @@ static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
if (pathspec && !ce_path_match(ce, pathspec, NULL))
continue;
- if (chmod_cache_entry(ce, force_mode) < 0)
- fprintf(stderr, "cannot chmod '%s'", ce->name);
+ if (chmod_cache_entry(ce, flip) < 0)
+ fprintf(stderr, "cannot chmod %cx '%s'\n", flip, ce->name);
}
}
@@ -114,11 +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.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;
@@ -135,7 +159,7 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
*dst++ = entry;
}
dir->nr = dst - dir->entries;
- add_pathspec_matches_against_index(pathspec, seen);
+ add_pathspec_matches_against_index(pathspec, &the_index, seen);
return seen;
}
@@ -214,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);
@@ -248,6 +272,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n");
static int verbose, show_only, ignored_too, refresh_only;
static int ignore_add_errors, intent_to_add, ignore_missing;
+static int warn_on_embedded_repo = 1;
#define ADDREMOVE_DEFAULT 1
static int addremove = ADDREMOVE_DEFAULT;
@@ -269,8 +294,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,
@@ -281,6 +307,8 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
+ OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
+ N_("warn when adding an embedded repository")),
OPT_END(),
};
@@ -294,6 +322,45 @@ static int add_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
+static const char embedded_advice[] = N_(
+"You've added another git repository inside your current repository.\n"
+"Clones of the outer repository will not contain the contents of\n"
+"the embedded repository and will not know how to obtain it.\n"
+"If you meant to add a submodule, use:\n"
+"\n"
+" git submodule add <url> %s\n"
+"\n"
+"If you added this path by mistake, you can remove it from the\n"
+"index with:\n"
+"\n"
+" git rm --cached %s\n"
+"\n"
+"See \"git help submodule\" for more information."
+);
+
+static void check_embedded_repo(const char *path)
+{
+ struct strbuf name = STRBUF_INIT;
+
+ if (!warn_on_embedded_repo)
+ return;
+ if (!ends_with(path, "/"))
+ return;
+
+ /* Drop trailing slash for aesthetics */
+ strbuf_addstr(&name, path);
+ strbuf_strip_suffix(&name, "/");
+
+ warning(_("adding embedded git repository: %s"), name.buf);
+ if (advice_add_embedded_repo) {
+ advise(embedded_advice, name.buf, name.buf);
+ /* there may be multiple entries; advise only once */
+ advice_add_embedded_repo = 0;
+ }
+
+ strbuf_release(&name);
+}
+
static int add_files(struct dir_struct *dir, int flags)
{
int i, exit_status = 0;
@@ -306,12 +373,14 @@ static int add_files(struct dir_struct *dir, int flags)
exit_status = 1;
}
- for (i = 0; i < dir->nr; i++)
+ for (i = 0; i < dir->nr; i++) {
+ check_embedded_repo(dir->entries[i]->name);
if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
}
+ }
return exit_status;
}
@@ -358,7 +427,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);
@@ -379,16 +448,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die(_("index file corrupt"));
+ die_in_unpopulated_submodule(&the_index, prefix);
+
/*
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
*/
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
- PATHSPEC_SYMLINK_LEADING_PATH |
- PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE,
+ PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
+ die_path_inside_submodule(&the_index, &pathspec);
+
if (add_new_files) {
int baselen;
@@ -400,7 +472,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
/* This picks up the paths that are not tracked */
- baselen = fill_directory(&dir, &pathspec);
+ baselen = fill_directory(&dir, &the_index, &pathspec);
if (pathspec.nr)
seen = prune_directory(&dir, &pathspec, baselen);
}
@@ -414,7 +486,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int i;
if (!seen)
- seen = find_pathspecs_matching_against_index(&pathspec);
+ seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
/*
* file_exists() assumes exact match
@@ -436,8 +508,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
!file_exists(path))) {
if (ignore_missing) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, path, &dtype))
- dir_add_ignored(&dir, path, pathspec.items[i].len);
+ if (is_excluded(&dir, &the_index, path, &dtype))
+ dir_add_ignored(&dir, &the_index,
+ path, pathspec.items[i].len);
} else
die(_("pathspec '%s' did not match any files"),
pathspec.items[i].original);
@@ -448,7 +521,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);
@@ -458,10 +534,11 @@ 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);
return exit_status;
}
diff --git a/builtin/am.c b/builtin/am.c
index 593c1b5dda..1151b5c73a 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -4,6 +4,7 @@
* Based on git-am.sh by Junio C Hamano.
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "exec_cmd.h"
#include "parse-options.h"
@@ -30,6 +31,7 @@
#include "mailinfo.h"
#include "apply.h"
#include "string-list.h"
+#include "packfile.h"
/**
* Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -430,6 +432,14 @@ static void am_load(struct am_state *state)
read_state_file(&sb, state, "utf8", 1);
state->utf8 = !strcmp(sb.buf, "t");
+ if (file_exists(am_path(state, "rerere-autoupdate"))) {
+ read_state_file(&sb, state, "rerere-autoupdate", 1);
+ state->allow_rerere_autoupdate = strcmp(sb.buf, "t") ?
+ RERERE_NOAUTOUPDATE : RERERE_AUTOUPDATE;
+ } else {
+ state->allow_rerere_autoupdate = 0;
+ }
+
read_state_file(&sb, state, "keep", 1);
if (!strcmp(sb.buf, "t"))
state->keep = KEEP_TRUE;
@@ -483,8 +493,7 @@ static int run_applypatch_msg_hook(struct am_state *state)
ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
if (!ret) {
- free(state->msg);
- state->msg = NULL;
+ FREE_AND_NULL(state->msg);
if (read_commit_msg(state) < 0)
die(_("'%s' was deleted by the applypatch-msg hook"),
am_path(state, "final-commit"));
@@ -563,7 +572,7 @@ static int copy_notes_for_rebase(const struct am_state *state)
goto finish;
}
- if (copy_note_for_rewrite(c, from_obj.hash, to_obj.hash))
+ if (copy_note_for_rewrite(c, &from_obj, &to_obj))
ret = error(_("Failed to copy notes from '%s' to '%s'"),
oid_to_hex(&from_obj), oid_to_hex(&to_obj));
}
@@ -662,9 +671,7 @@ static int detect_patch_format(const char **paths)
goto done;
}
- strbuf_reset(&l2);
strbuf_getline(&l2, fp);
- strbuf_reset(&l3);
strbuf_getline(&l3, fp);
/*
@@ -687,6 +694,8 @@ static int detect_patch_format(const char **paths)
done:
fclose(fp);
strbuf_release(&l1);
+ strbuf_release(&l2);
+ strbuf_release(&l3);
return ret;
}
@@ -699,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");
@@ -712,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;
}
/**
@@ -872,6 +885,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
{
struct strbuf sb = STRBUF_INIT;
+ int rc = 0;
while (!strbuf_getline_lf(&sb, in)) {
const char *str;
@@ -879,25 +893,33 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
if (skip_prefix(sb.buf, "# User ", &str))
fprintf(out, "From: %s\n", str);
else if (skip_prefix(sb.buf, "# Date ", &str)) {
- unsigned long timestamp;
+ timestamp_t timestamp;
long tz, tz2;
char *end;
errno = 0;
- timestamp = strtoul(str, &end, 10);
- if (errno)
- return error(_("invalid timestamp"));
+ timestamp = parse_timestamp(str, &end, 10);
+ if (errno) {
+ rc = error(_("invalid timestamp"));
+ goto exit;
+ }
- if (!skip_prefix(end, " ", &str))
- return error(_("invalid Date line"));
+ if (!skip_prefix(end, " ", &str)) {
+ rc = error(_("invalid Date line"));
+ goto exit;
+ }
errno = 0;
tz = strtol(str, &end, 10);
- if (errno)
- return error(_("invalid timezone offset"));
+ if (errno) {
+ rc = error(_("invalid timezone offset"));
+ goto exit;
+ }
- if (*end)
- return error(_("invalid Date line"));
+ if (*end) {
+ rc = error(_("invalid Date line"));
+ goto exit;
+ }
/*
* mercurial's timezone is in seconds west of UTC,
@@ -922,9 +944,9 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
fwrite(sb.buf, 1, sb.len, out);
strbuf_reset(&sb);
}
-
+exit:
strbuf_release(&sb);
- return 0;
+ return rc;
}
/**
@@ -989,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);
@@ -1003,6 +1026,10 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
write_state_bool(state, "sign", state->signoff);
write_state_bool(state, "utf8", state->utf8);
+ if (state->allow_rerere_autoupdate)
+ write_state_bool(state, "rerere-autoupdate",
+ state->allow_rerere_autoupdate == RERERE_AUTOUPDATE);
+
switch (state->keep) {
case KEEP_FALSE:
str = "f";
@@ -1035,7 +1062,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
}
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)
@@ -1046,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)
@@ -1073,17 +1100,10 @@ static void am_next(struct am_state *state)
{
struct object_id head;
- free(state->author_name);
- state->author_name = NULL;
-
- free(state->author_email);
- state->author_email = NULL;
-
- free(state->author_date);
- state->author_date = NULL;
-
- free(state->msg);
- state->msg = NULL;
+ FREE_AND_NULL(state->author_name);
+ FREE_AND_NULL(state->author_email);
+ FREE_AND_NULL(state->author_date);
+ FREE_AND_NULL(state->msg);
state->msg_len = 0;
unlink(am_path(state, "author-script"));
@@ -1091,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));
@@ -1119,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_sha1_tree("HEAD", head.hash)) {
- 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.hash, &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.
*/
@@ -1188,34 +1172,10 @@ static void NORETURN die_user_resolve(const struct am_state *state)
*/
static void am_append_signoff(struct am_state *state)
{
- char *cp;
- struct strbuf mine = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
-
- /* our sign-off */
- strbuf_addf(&mine, "\n%s%s\n",
- sign_off_header,
- fmt_name(getenv("GIT_COMMITTER_NAME"),
- getenv("GIT_COMMITTER_EMAIL")));
-
- /* Does sb end with it already? */
- if (mine.len < sb.len &&
- !strcmp(mine.buf, sb.buf + sb.len - mine.len))
- goto exit; /* no need to duplicate */
-
- /* Does it have any Signed-off-by: in the text */
- for (cp = sb.buf;
- cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
- cp = strchr(cp, '\n')) {
- if (sb.buf == cp || cp[-1] == '\n')
- break;
- }
-
- strbuf_addstr(&sb, mine.buf + !!cp);
-exit:
- strbuf_release(&mine);
+ append_signoff(&sb, 0, 0);
state->msg = strbuf_detach(&sb, &state->msg_len);
}
@@ -1275,12 +1235,8 @@ static int parse_mail(struct am_state *state, const char *mail)
die("BUG: invalid value for state->scissors");
}
- mi.input = fopen(mail, "r");
- if (!mi.input)
- die("could not open input");
- mi.output = fopen(am_path(state, "info"), "w");
- if (!mi.output)
- die("could not open output 'info'");
+ mi.input = xfopen(mail, "r");
+ mi.output = xfopen(am_path(state, "info"), "w");
if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
die("could not parse patch");
@@ -1422,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;
@@ -1443,10 +1399,10 @@ static void write_index_patch(const struct am_state *state)
struct rev_info rev_info;
FILE *fp;
- if (!get_sha1_tree("HEAD", head.hash))
- tree = lookup_tree(head.hash);
+ if (!get_oid_tree("HEAD", &head))
+ tree = lookup_tree(&head);
else
- tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+ tree = lookup_tree(the_hash_algo->empty_tree);
fp = xfopen(am_path(state, "patch"), "w");
init_revisions(&rev_info, NULL);
@@ -1479,7 +1435,7 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
if (get_mail_commit_oid(&commit_oid, mail) < 0)
die(_("could not parse %s"), mail);
- commit = lookup_commit_or_die(commit_oid.hash, mail);
+ commit = lookup_commit_or_die(&commit_oid, mail);
get_commit_info(state, commit);
@@ -1487,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;
}
@@ -1501,11 +1459,10 @@ 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))
+ if (init_apply_state(&apply_state, NULL))
die("BUG: init_apply_state() failed");
argv_array_push(&apply_opts, "apply");
@@ -1609,7 +1566,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
init_revisions(&rev_info, NULL);
rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
- add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
+ add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1);
}
@@ -1672,9 +1629,9 @@ static void do_commit(const struct am_state *state)
if (write_cache_as_tree(tree.hash, 0, NULL))
die(_("git write-tree failed to write a tree"));
- if (!get_sha1_commit("HEAD", parent.hash)) {
+ if (!get_oid_commit("HEAD", &parent)) {
old_oid = &parent;
- commit_list_insert(lookup_commit(parent.hash), &parents);
+ commit_list_insert(lookup_commit(&parent), &parents);
} else {
old_oid = NULL;
say(state, stderr, _("applying to an empty history"));
@@ -1688,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");
@@ -1699,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");
@@ -1878,8 +1835,7 @@ static void am_run(struct am_state *state, int resume)
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"));
+ printf_ln(_("Use 'git am --show-current-patch' to see the failed patch"));
die_user_resolve(state);
}
@@ -1959,15 +1915,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);
@@ -1983,11 +1938,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;
@@ -1999,15 +1954,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;
@@ -2018,11 +1972,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,11 +1991,11 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
struct tree *head_tree, *remote_tree, *index_tree;
struct object_id index;
- head_tree = parse_tree_indirect(head->hash);
+ head_tree = parse_tree_indirect(head);
if (!head_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(head));
- remote_tree = parse_tree_indirect(remote->hash);
+ remote_tree = parse_tree_indirect(remote);
if (!remote_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(remote));
@@ -2053,7 +2007,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (write_cache_as_tree(index.hash, 0, NULL))
return -1;
- index_tree = parse_tree_indirect(index.hash);
+ index_tree = parse_tree_indirect(&index);
if (!index_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(&index));
@@ -2118,6 +2072,7 @@ static int safe_to_abort(const struct am_state *state)
die(_("could not parse %s"), am_path(state, "abort-safety"));
} else
oidclr(&abort_safety);
+ strbuf_release(&sb);
if (get_oid("HEAD", &head))
oidclr(&head);
@@ -2147,7 +2102,7 @@ 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);
@@ -2159,16 +2114,44 @@ static void am_abort(struct am_state *state)
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`.
@@ -2197,7 +2180,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)
@@ -2219,6 +2204,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>)...]"),
@@ -2297,6 +2283,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")),
@@ -2365,7 +2357,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;
@@ -2407,11 +2399,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");
}
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 ca9ebe40e7..9dcb367b90 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -6,21 +6,14 @@
*/
#include "cache.h"
-#include "refs.h"
+#include "config.h"
#include "builtin.h"
-#include "blob.h"
#include "commit.h"
-#include "tag.h"
-#include "tree-walk.h"
#include "diff.h"
-#include "diffcore.h"
#include "revision.h"
#include "quote.h"
-#include "xdiff-interface.h"
-#include "cache-tree.h"
#include "string-list.h"
#include "mailmap.h"
-#include "mergesort.h"
#include "parse-options.h"
#include "prio-queue.h"
#include "utf8.h"
@@ -29,6 +22,7 @@
#include "line-log.h"
#include "dir.h"
#include "progress.h"
+#include "blame.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -62,1497 +56,21 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;
#define DEBUG 0
#endif
-/* stats */
-static int num_read_blob;
-static int num_get_patch;
-static int num_commits;
-
-#define PICKAXE_BLAME_MOVE 01
-#define PICKAXE_BLAME_COPY 02
-#define PICKAXE_BLAME_COPY_HARDER 04
-#define PICKAXE_BLAME_COPY_HARDEST 010
-
-/*
- * blame for a blame_entry with score lower than these thresholds
- * is not passed to the parent using move/copy logic.
- */
static unsigned blame_move_score;
static unsigned blame_copy_score;
-#define BLAME_DEFAULT_MOVE_SCORE 20
-#define BLAME_DEFAULT_COPY_SCORE 40
/* Remember to update object flag allocation in object.h */
#define METAINFO_SHOWN (1u<<12)
#define MORE_THAN_ONE_PATH (1u<<13)
-/*
- * One blob in a commit that is being suspected
- */
-struct origin {
- int refcnt;
- /* Record preceding blame record for this blob */
- struct origin *previous;
- /* origins are put in a list linked via `next' hanging off the
- * corresponding commit's util field in order to make finding
- * them fast. The presence in this chain does not count
- * towards the origin's reference count. It is tempting to
- * let it count as long as the commit is pending examination,
- * but even under circumstances where the commit will be
- * present multiple times in the priority queue of unexamined
- * commits, processing the first instance will not leave any
- * work requiring the origin data for the second instance. An
- * interspersed commit changing that would have to be
- * preexisting with a different ancestry and with the same
- * commit date in order to wedge itself between two instances
- * of the same commit in the priority queue _and_ produce
- * blame entries relevant for it. While we don't want to let
- * us get tripped up by this case, it certainly does not seem
- * worth optimizing for.
- */
- struct origin *next;
- struct commit *commit;
- /* `suspects' contains blame entries that may be attributed to
- * this origin's commit or to parent commits. When a commit
- * is being processed, all suspects will be moved, either by
- * assigning them to an origin in a different commit, or by
- * shipping them to the scoreboard's ent list because they
- * cannot be attributed to a different commit.
- */
- struct blame_entry *suspects;
- mmfile_t file;
- struct object_id blob_oid;
- unsigned mode;
- /* guilty gets set when shipping any suspects to the final
- * blame list instead of other commits
- */
- char guilty;
- char path[FLEX_ARRAY];
-};
-
struct progress_info {
struct progress *progress;
int blamed_lines;
};
-static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
- xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
-{
- xpparam_t xpp = {0};
- xdemitconf_t xecfg = {0};
- xdemitcb_t ecb = {NULL};
-
- xpp.flags = xdl_opts;
- xecfg.hunk_func = hunk_func;
- ecb.priv = cb_data;
- return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
-}
-
-/*
- * Prepare diff_filespec and convert it using diff textconv API
- * if the textconv driver exists.
- * Return 1 if the conversion succeeds, 0 otherwise.
- */
-int textconv_object(const char *path,
- unsigned mode,
- const struct object_id *oid,
- int oid_valid,
- char **buf,
- unsigned long *buf_size)
-{
- struct diff_filespec *df;
- struct userdiff_driver *textconv;
-
- df = alloc_filespec(path);
- fill_filespec(df, oid->hash, oid_valid, mode);
- textconv = get_textconv(df);
- if (!textconv) {
- free_filespec(df);
- return 0;
- }
-
- *buf_size = fill_textconv(textconv, df, buf);
- free_filespec(df);
- return 1;
-}
-
-/*
- * Given an origin, prepare mmfile_t structure to be used by the
- * diff machinery
- */
-static void fill_origin_blob(struct diff_options *opt,
- struct origin *o, mmfile_t *file)
-{
- if (!o->file.ptr) {
- enum object_type type;
- unsigned long file_size;
-
- num_read_blob++;
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
- ;
- else
- file->ptr = read_sha1_file(o->blob_oid.hash, &type,
- &file_size);
- file->size = file_size;
-
- if (!file->ptr)
- die("Cannot read blob %s for path %s",
- oid_to_hex(&o->blob_oid),
- o->path);
- o->file = *file;
- }
- else
- *file = o->file;
-}
-
-/*
- * Origin is refcounted and usually we keep the blob contents to be
- * reused.
- */
-static inline struct origin *origin_incref(struct origin *o)
-{
- if (o)
- o->refcnt++;
- return o;
-}
-
-static void origin_decref(struct origin *o)
-{
- if (o && --o->refcnt <= 0) {
- struct origin *p, *l = NULL;
- if (o->previous)
- origin_decref(o->previous);
- free(o->file.ptr);
- /* Should be present exactly once in commit chain */
- for (p = o->commit->util; p; l = p, p = p->next) {
- if (p == o) {
- if (l)
- l->next = p->next;
- else
- o->commit->util = p->next;
- free(o);
- return;
- }
- }
- die("internal error in blame::origin_decref");
- }
-}
-
-static void drop_origin_blob(struct origin *o)
-{
- if (o->file.ptr) {
- free(o->file.ptr);
- o->file.ptr = NULL;
- }
-}
-
-/*
- * Each group of lines is described by a blame_entry; it can be split
- * as we pass blame to the parents. They are arranged in linked lists
- * kept as `suspects' of some unprocessed origin, or entered (when the
- * blame origin has been finalized) into the scoreboard structure.
- * While the scoreboard structure is only sorted at the end of
- * processing (according to final image line number), the lists
- * attached to an origin are sorted by the target line number.
- */
-struct blame_entry {
- struct blame_entry *next;
-
- /* the first line of this group in the final image;
- * internally all line numbers are 0 based.
- */
- int lno;
-
- /* how many lines this group has */
- int num_lines;
-
- /* the commit that introduced this group into the final image */
- struct origin *suspect;
-
- /* the line number of the first line of this group in the
- * suspect's file; internally all line numbers are 0 based.
- */
- int s_lno;
-
- /* how significant this entry is -- cached to avoid
- * scanning the lines over and over.
- */
- unsigned score;
-};
-
-/*
- * Any merge of blames happens on lists of blames that arrived via
- * different parents in a single suspect. In this case, we want to
- * sort according to the suspect line numbers as opposed to the final
- * image line numbers. The function body is somewhat longish because
- * it avoids unnecessary writes.
- */
-
-static struct blame_entry *blame_merge(struct blame_entry *list1,
- struct blame_entry *list2)
-{
- struct blame_entry *p1 = list1, *p2 = list2,
- **tail = &list1;
-
- if (!p1)
- return p2;
- if (!p2)
- return p1;
-
- if (p1->s_lno <= p2->s_lno) {
- do {
- tail = &p1->next;
- if ((p1 = *tail) == NULL) {
- *tail = p2;
- return list1;
- }
- } while (p1->s_lno <= p2->s_lno);
- }
- for (;;) {
- *tail = p2;
- do {
- tail = &p2->next;
- if ((p2 = *tail) == NULL) {
- *tail = p1;
- return list1;
- }
- } while (p1->s_lno > p2->s_lno);
- *tail = p1;
- do {
- tail = &p1->next;
- if ((p1 = *tail) == NULL) {
- *tail = p2;
- return list1;
- }
- } while (p1->s_lno <= p2->s_lno);
- }
-}
-
-static void *get_next_blame(const void *p)
-{
- return ((struct blame_entry *)p)->next;
-}
-
-static void set_next_blame(void *p1, void *p2)
-{
- ((struct blame_entry *)p1)->next = p2;
-}
-
-/*
- * Final image line numbers are all different, so we don't need a
- * three-way comparison here.
- */
-
-static int compare_blame_final(const void *p1, const void *p2)
-{
- return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
- ? 1 : -1;
-}
-
-static int compare_blame_suspect(const void *p1, const void *p2)
-{
- const struct blame_entry *s1 = p1, *s2 = p2;
- /*
- * to allow for collating suspects, we sort according to the
- * respective pointer value as the primary sorting criterion.
- * The actual relation is pretty unimportant as long as it
- * establishes a total order. Comparing as integers gives us
- * that.
- */
- if (s1->suspect != s2->suspect)
- return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
- if (s1->s_lno == s2->s_lno)
- return 0;
- return s1->s_lno > s2->s_lno ? 1 : -1;
-}
-
-static struct blame_entry *blame_sort(struct blame_entry *head,
- int (*compare_fn)(const void *, const void *))
-{
- return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);
-}
-
-static int compare_commits_by_reverse_commit_date(const void *a,
- const void *b,
- void *c)
-{
- return -compare_commits_by_commit_date(a, b, c);
-}
-
-/*
- * The current state of the blame assignment.
- */
-struct scoreboard {
- /* the final commit (i.e. where we started digging from) */
- struct commit *final;
- /* Priority queue for commits with unassigned blame records */
- struct prio_queue commits;
- struct rev_info *revs;
- const char *path;
-
- /*
- * The contents in the final image.
- * Used by many functions to obtain contents of the nth line,
- * indexed with scoreboard.lineno[blame_entry.lno].
- */
- const char *final_buf;
- unsigned long final_buf_size;
-
- /* linked list of blames */
- struct blame_entry *ent;
-
- /* look-up a line in the final buffer */
- int num_lines;
- int *lineno;
-};
-
-static void sanity_check_refcnt(struct scoreboard *);
-
-/*
- * If two blame entries that are next to each other came from
- * contiguous lines in the same origin (i.e. <commit, path> pair),
- * merge them together.
- */
-static void coalesce(struct scoreboard *sb)
-{
- struct blame_entry *ent, *next;
-
- for (ent = sb->ent; ent && (next = ent->next); ent = next) {
- if (ent->suspect == next->suspect &&
- ent->s_lno + ent->num_lines == next->s_lno) {
- ent->num_lines += next->num_lines;
- ent->next = next->next;
- origin_decref(next->suspect);
- free(next);
- ent->score = 0;
- next = ent; /* again */
- }
- }
-
- if (DEBUG) /* sanity */
- sanity_check_refcnt(sb);
-}
-
-/*
- * Merge the given sorted list of blames into a preexisting origin.
- * If there were no previous blames to that commit, it is entered into
- * the commit priority queue of the score board.
- */
-
-static void queue_blames(struct scoreboard *sb, struct origin *porigin,
- struct blame_entry *sorted)
-{
- if (porigin->suspects)
- porigin->suspects = blame_merge(porigin->suspects, sorted);
- else {
- struct origin *o;
- for (o = porigin->commit->util; o; o = o->next) {
- if (o->suspects) {
- porigin->suspects = sorted;
- return;
- }
- }
- porigin->suspects = sorted;
- prio_queue_put(&sb->commits, porigin->commit);
- }
-}
-
-/*
- * Given a commit and a path in it, create a new origin structure.
- * The callers that add blame to the scoreboard should use
- * get_origin() to obtain shared, refcounted copy instead of calling
- * this function directly.
- */
-static struct origin *make_origin(struct commit *commit, const char *path)
-{
- struct origin *o;
- FLEX_ALLOC_STR(o, path, path);
- o->commit = commit;
- o->refcnt = 1;
- o->next = commit->util;
- commit->util = o;
- return o;
-}
-
-/*
- * Locate an existing origin or create a new one.
- * This moves the origin to front position in the commit util list.
- */
-static struct origin *get_origin(struct scoreboard *sb,
- struct commit *commit,
- const char *path)
-{
- struct origin *o, *l;
-
- for (o = commit->util, l = NULL; o; l = o, o = o->next) {
- if (!strcmp(o->path, path)) {
- /* bump to front */
- if (l) {
- l->next = o->next;
- o->next = commit->util;
- commit->util = o;
- }
- return origin_incref(o);
- }
- }
- return make_origin(commit, path);
-}
-
-/*
- * Fill the blob_sha1 field of an origin if it hasn't, so that later
- * call to fill_origin_blob() can use it to locate the data. blob_sha1
- * for an origin is also used to pass the blame for the entire file to
- * the parent to detect the case where a child's blob is identical to
- * that of its parent's.
- *
- * This also fills origin->mode for corresponding tree path.
- */
-static int fill_blob_sha1_and_mode(struct origin *origin)
-{
- if (!is_null_oid(&origin->blob_oid))
- return 0;
- if (get_tree_entry(origin->commit->object.oid.hash,
- origin->path,
- origin->blob_oid.hash, &origin->mode))
- goto error_out;
- if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
- goto error_out;
- return 0;
- error_out:
- oidclr(&origin->blob_oid);
- origin->mode = S_IFINVALID;
- return -1;
-}
-
-/*
- * We have an origin -- check if the same path exists in the
- * parent and return an origin structure to represent it.
- */
-static struct origin *find_origin(struct scoreboard *sb,
- struct commit *parent,
- struct origin *origin)
-{
- struct origin *porigin;
- struct diff_options diff_opts;
- const char *paths[2];
-
- /* First check any existing origins */
- for (porigin = parent->util; porigin; porigin = porigin->next)
- if (!strcmp(porigin->path, origin->path)) {
- /*
- * The same path between origin and its parent
- * without renaming -- the most common case.
- */
- return origin_incref (porigin);
- }
-
- /* See if the origin->path is different between parent
- * and origin first. Most of the time they are the
- * same and diff-tree is fairly efficient about this.
- */
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.detect_rename = 0;
- diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
- paths[0] = origin->path;
- paths[1] = NULL;
-
- parse_pathspec(&diff_opts.pathspec,
- PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
- PATHSPEC_LITERAL_PATH, "", paths);
- diff_setup_done(&diff_opts);
-
- if (is_null_oid(&origin->commit->object.oid))
- do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
- else
- diff_tree_sha1(parent->tree->object.oid.hash,
- origin->commit->tree->object.oid.hash,
- "", &diff_opts);
- diffcore_std(&diff_opts);
-
- if (!diff_queued_diff.nr) {
- /* The path is the same as parent */
- porigin = get_origin(sb, parent, origin->path);
- oidcpy(&porigin->blob_oid, &origin->blob_oid);
- porigin->mode = origin->mode;
- } else {
- /*
- * Since origin->path is a pathspec, if the parent
- * commit had it as a directory, we will see a whole
- * bunch of deletion of files in the directory that we
- * do not care about.
- */
- int i;
- struct diff_filepair *p = NULL;
- for (i = 0; i < diff_queued_diff.nr; i++) {
- const char *name;
- p = diff_queued_diff.queue[i];
- name = p->one->path ? p->one->path : p->two->path;
- if (!strcmp(name, origin->path))
- break;
- }
- if (!p)
- die("internal error in blame::find_origin");
- switch (p->status) {
- default:
- die("internal error in blame::find_origin (%c)",
- p->status);
- case 'M':
- porigin = get_origin(sb, parent, origin->path);
- oidcpy(&porigin->blob_oid, &p->one->oid);
- porigin->mode = p->one->mode;
- break;
- case 'A':
- case 'T':
- /* Did not exist in parent, or type changed */
- break;
- }
- }
- diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
- return porigin;
-}
-
-/*
- * We have an origin -- find the path that corresponds to it in its
- * parent and return an origin structure to represent it.
- */
-static struct origin *find_rename(struct scoreboard *sb,
- struct commit *parent,
- struct origin *origin)
-{
- struct origin *porigin = NULL;
- struct diff_options diff_opts;
- int i;
-
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.detect_rename = DIFF_DETECT_RENAME;
- diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_opts.single_follow = origin->path;
- diff_setup_done(&diff_opts);
-
- if (is_null_oid(&origin->commit->object.oid))
- do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
- else
- diff_tree_sha1(parent->tree->object.oid.hash,
- origin->commit->tree->object.oid.hash,
- "", &diff_opts);
- diffcore_std(&diff_opts);
-
- for (i = 0; i < diff_queued_diff.nr; i++) {
- struct diff_filepair *p = diff_queued_diff.queue[i];
- if ((p->status == 'R' || p->status == 'C') &&
- !strcmp(p->two->path, origin->path)) {
- porigin = get_origin(sb, parent, p->one->path);
- oidcpy(&porigin->blob_oid, &p->one->oid);
- porigin->mode = p->one->mode;
- break;
- }
- }
- diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
- return porigin;
-}
-
-/*
- * Append a new blame entry to a given output queue.
- */
-static void add_blame_entry(struct blame_entry ***queue,
- const struct blame_entry *src)
-{
- struct blame_entry *e = xmalloc(sizeof(*e));
- memcpy(e, src, sizeof(*e));
- origin_incref(e->suspect);
-
- e->next = **queue;
- **queue = e;
- *queue = &e->next;
-}
-
-/*
- * src typically is on-stack; we want to copy the information in it to
- * a malloced blame_entry that gets added to the given queue. The
- * origin of dst loses a refcnt.
- */
-static void dup_entry(struct blame_entry ***queue,
- struct blame_entry *dst, struct blame_entry *src)
-{
- origin_incref(src->suspect);
- origin_decref(dst->suspect);
- memcpy(dst, src, sizeof(*src));
- dst->next = **queue;
- **queue = dst;
- *queue = &dst->next;
-}
-
-static const char *nth_line(struct scoreboard *sb, long lno)
-{
- return sb->final_buf + sb->lineno[lno];
-}
-
static const char *nth_line_cb(void *data, long lno)
{
- return nth_line((struct scoreboard *)data, lno);
-}
-
-/*
- * It is known that lines between tlno to same came from parent, and e
- * has an overlap with that range. it also is known that parent's
- * line plno corresponds to e's line tlno.
- *
- * <---- e ----->
- * <------>
- * <------------>
- * <------------>
- * <------------------>
- *
- * Split e into potentially three parts; before this chunk, the chunk
- * to be blamed for the parent, and after that portion.
- */
-static void split_overlap(struct blame_entry *split,
- struct blame_entry *e,
- int tlno, int plno, int same,
- struct origin *parent)
-{
- int chunk_end_lno;
- memset(split, 0, sizeof(struct blame_entry [3]));
-
- if (e->s_lno < tlno) {
- /* there is a pre-chunk part not blamed on parent */
- split[0].suspect = origin_incref(e->suspect);
- split[0].lno = e->lno;
- split[0].s_lno = e->s_lno;
- split[0].num_lines = tlno - e->s_lno;
- split[1].lno = e->lno + tlno - e->s_lno;
- split[1].s_lno = plno;
- }
- else {
- split[1].lno = e->lno;
- split[1].s_lno = plno + (e->s_lno - tlno);
- }
-
- if (same < e->s_lno + e->num_lines) {
- /* there is a post-chunk part not blamed on parent */
- split[2].suspect = origin_incref(e->suspect);
- split[2].lno = e->lno + (same - e->s_lno);
- split[2].s_lno = e->s_lno + (same - e->s_lno);
- split[2].num_lines = e->s_lno + e->num_lines - same;
- chunk_end_lno = split[2].lno;
- }
- else
- chunk_end_lno = e->lno + e->num_lines;
- split[1].num_lines = chunk_end_lno - split[1].lno;
-
- /*
- * if it turns out there is nothing to blame the parent for,
- * forget about the splitting. !split[1].suspect signals this.
- */
- if (split[1].num_lines < 1)
- return;
- split[1].suspect = origin_incref(parent);
-}
-
-/*
- * split_overlap() divided an existing blame e into up to three parts
- * in split. Any assigned blame is moved to queue to
- * reflect the split.
- */
-static void split_blame(struct blame_entry ***blamed,
- struct blame_entry ***unblamed,
- struct blame_entry *split,
- struct blame_entry *e)
-{
- if (split[0].suspect && split[2].suspect) {
- /* The first part (reuse storage for the existing entry e) */
- dup_entry(unblamed, e, &split[0]);
-
- /* The last part -- me */
- add_blame_entry(unblamed, &split[2]);
-
- /* ... and the middle part -- parent */
- add_blame_entry(blamed, &split[1]);
- }
- else if (!split[0].suspect && !split[2].suspect)
- /*
- * The parent covers the entire area; reuse storage for
- * e and replace it with the parent.
- */
- dup_entry(blamed, e, &split[1]);
- else if (split[0].suspect) {
- /* me and then parent */
- dup_entry(unblamed, e, &split[0]);
- add_blame_entry(blamed, &split[1]);
- }
- else {
- /* parent and then me */
- dup_entry(blamed, e, &split[1]);
- add_blame_entry(unblamed, &split[2]);
- }
-}
-
-/*
- * After splitting the blame, the origins used by the
- * on-stack blame_entry should lose one refcnt each.
- */
-static void decref_split(struct blame_entry *split)
-{
- int i;
-
- for (i = 0; i < 3; i++)
- origin_decref(split[i].suspect);
-}
-
-/*
- * reverse_blame reverses the list given in head, appending tail.
- * That allows us to build lists in reverse order, then reverse them
- * afterwards. This can be faster than building the list in proper
- * order right away. The reason is that building in proper order
- * requires writing a link in the _previous_ element, while building
- * in reverse order just requires placing the list head into the
- * _current_ element.
- */
-
-static struct blame_entry *reverse_blame(struct blame_entry *head,
- struct blame_entry *tail)
-{
- while (head) {
- struct blame_entry *next = head->next;
- head->next = tail;
- tail = head;
- head = next;
- }
- return tail;
-}
-
-/*
- * Process one hunk from the patch between the current suspect for
- * blame_entry e and its parent. This first blames any unfinished
- * entries before the chunk (which is where target and parent start
- * differing) on the parent, and then splits blame entries at the
- * start and at the end of the difference region. Since use of -M and
- * -C options may lead to overlapping/duplicate source line number
- * ranges, all we can rely on from sorting/merging is the order of the
- * first suspect line number.
- */
-static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
- int tlno, int offset, int same,
- struct origin *parent)
-{
- struct blame_entry *e = **srcq;
- struct blame_entry *samep = NULL, *diffp = NULL;
-
- while (e && e->s_lno < tlno) {
- struct blame_entry *next = e->next;
- /*
- * current record starts before differing portion. If
- * it reaches into it, we need to split it up and
- * examine the second part separately.
- */
- if (e->s_lno + e->num_lines > tlno) {
- /* Move second half to a new record */
- int len = tlno - e->s_lno;
- struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
- n->suspect = e->suspect;
- n->lno = e->lno + len;
- n->s_lno = e->s_lno + len;
- n->num_lines = e->num_lines - len;
- e->num_lines = len;
- e->score = 0;
- /* Push new record to diffp */
- n->next = diffp;
- diffp = n;
- } else
- origin_decref(e->suspect);
- /* Pass blame for everything before the differing
- * chunk to the parent */
- e->suspect = origin_incref(parent);
- e->s_lno += offset;
- e->next = samep;
- samep = e;
- e = next;
- }
- /*
- * As we don't know how much of a common stretch after this
- * diff will occur, the currently blamed parts are all that we
- * can assign to the parent for now.
- */
-
- if (samep) {
- **dstq = reverse_blame(samep, **dstq);
- *dstq = &samep->next;
- }
- /*
- * Prepend the split off portions: everything after e starts
- * after the blameable portion.
- */
- e = reverse_blame(diffp, e);
-
- /*
- * Now retain records on the target while parts are different
- * from the parent.
- */
- samep = NULL;
- diffp = NULL;
- while (e && e->s_lno < same) {
- struct blame_entry *next = e->next;
-
- /*
- * If current record extends into sameness, need to split.
- */
- if (e->s_lno + e->num_lines > same) {
- /*
- * Move second half to a new record to be
- * processed by later chunks
- */
- int len = same - e->s_lno;
- struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
- n->suspect = origin_incref(e->suspect);
- n->lno = e->lno + len;
- n->s_lno = e->s_lno + len;
- n->num_lines = e->num_lines - len;
- e->num_lines = len;
- e->score = 0;
- /* Push new record to samep */
- n->next = samep;
- samep = n;
- }
- e->next = diffp;
- diffp = e;
- e = next;
- }
- **srcq = reverse_blame(diffp, reverse_blame(samep, e));
- /* Move across elements that are in the unblamable portion */
- if (diffp)
- *srcq = &diffp->next;
-}
-
-struct blame_chunk_cb_data {
- struct origin *parent;
- long offset;
- struct blame_entry **dstq;
- struct blame_entry **srcq;
-};
-
-/* diff chunks are from parent to target */
-static int blame_chunk_cb(long start_a, long count_a,
- long start_b, long count_b, void *data)
-{
- struct blame_chunk_cb_data *d = data;
- if (start_a - start_b != d->offset)
- die("internal error in blame::blame_chunk_cb");
- blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
- start_b + count_b, d->parent);
- d->offset = start_a + count_a - (start_b + count_b);
- return 0;
-}
-
-/*
- * We are looking at the origin 'target' and aiming to pass blame
- * for the lines it is suspected to its parent. Run diff to find
- * which lines came from parent and pass blame for them.
- */
-static void pass_blame_to_parent(struct scoreboard *sb,
- struct origin *target,
- struct origin *parent)
-{
- mmfile_t file_p, file_o;
- struct blame_chunk_cb_data d;
- struct blame_entry *newdest = NULL;
-
- if (!target->suspects)
- return; /* nothing remains for this target */
-
- d.parent = parent;
- d.offset = 0;
- d.dstq = &newdest; d.srcq = &target->suspects;
-
- fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
- fill_origin_blob(&sb->revs->diffopt, target, &file_o);
- num_get_patch++;
-
- if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))
- die("unable to generate diff (%s -> %s)",
- oid_to_hex(&parent->commit->object.oid),
- oid_to_hex(&target->commit->object.oid));
- /* The rest are the same as the parent */
- blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
- *d.dstq = NULL;
- queue_blames(sb, parent, newdest);
-
- return;
-}
-
-/*
- * The lines in blame_entry after splitting blames many times can become
- * very small and trivial, and at some point it becomes pointless to
- * blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any
- * ordinary C program, and it is not worth to say it was copied from
- * totally unrelated file in the parent.
- *
- * Compute how trivial the lines in the blame_entry are.
- */
-static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
-{
- unsigned score;
- const char *cp, *ep;
-
- if (e->score)
- return e->score;
-
- score = 1;
- cp = nth_line(sb, e->lno);
- ep = nth_line(sb, e->lno + e->num_lines);
- while (cp < ep) {
- unsigned ch = *((unsigned char *)cp);
- if (isalnum(ch))
- score++;
- cp++;
- }
- e->score = score;
- return score;
-}
-
-/*
- * best_so_far[] and this[] are both a split of an existing blame_entry
- * that passes blame to the parent. Maintain best_so_far the best split
- * so far, by comparing this and best_so_far and copying this into
- * bst_so_far as needed.
- */
-static void copy_split_if_better(struct scoreboard *sb,
- struct blame_entry *best_so_far,
- struct blame_entry *this)
-{
- int i;
-
- if (!this[1].suspect)
- return;
- if (best_so_far[1].suspect) {
- if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
- return;
- }
-
- for (i = 0; i < 3; i++)
- origin_incref(this[i].suspect);
- decref_split(best_so_far);
- memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
-}
-
-/*
- * We are looking at a part of the final image represented by
- * ent (tlno and same are offset by ent->s_lno).
- * tlno is where we are looking at in the final image.
- * up to (but not including) same match preimage.
- * plno is where we are looking at in the preimage.
- *
- * <-------------- final image ---------------------->
- * <------ent------>
- * ^tlno ^same
- * <---------preimage----->
- * ^plno
- *
- * All line numbers are 0-based.
- */
-static void handle_split(struct scoreboard *sb,
- struct blame_entry *ent,
- int tlno, int plno, int same,
- struct origin *parent,
- struct blame_entry *split)
-{
- if (ent->num_lines <= tlno)
- return;
- if (tlno < same) {
- struct blame_entry this[3];
- tlno += ent->s_lno;
- same += ent->s_lno;
- split_overlap(this, ent, tlno, plno, same, parent);
- copy_split_if_better(sb, split, this);
- decref_split(this);
- }
-}
-
-struct handle_split_cb_data {
- struct scoreboard *sb;
- struct blame_entry *ent;
- struct origin *parent;
- struct blame_entry *split;
- long plno;
- long tlno;
-};
-
-static int handle_split_cb(long start_a, long count_a,
- long start_b, long count_b, void *data)
-{
- struct handle_split_cb_data *d = data;
- handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
- d->split);
- d->plno = start_a + count_a;
- d->tlno = start_b + count_b;
- return 0;
-}
-
-/*
- * Find the lines from parent that are the same as ent so that
- * we can pass blames to it. file_p has the blob contents for
- * the parent.
- */
-static void find_copy_in_blob(struct scoreboard *sb,
- struct blame_entry *ent,
- struct origin *parent,
- struct blame_entry *split,
- mmfile_t *file_p)
-{
- const char *cp;
- mmfile_t file_o;
- struct handle_split_cb_data d;
-
- memset(&d, 0, sizeof(d));
- d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
- /*
- * Prepare mmfile that contains only the lines in ent.
- */
- cp = nth_line(sb, ent->lno);
- file_o.ptr = (char *) cp;
- file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
-
- /*
- * file_o is a part of final image we are annotating.
- * file_p partially may match that image.
- */
- memset(split, 0, sizeof(struct blame_entry [3]));
- if (diff_hunks(file_p, &file_o, handle_split_cb, &d))
- die("unable to generate diff (%s)",
- oid_to_hex(&parent->commit->object.oid));
- /* remainder, if any, all match the preimage */
- handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
-}
-
-/* Move all blame entries from list *source that have a score smaller
- * than score_min to the front of list *small.
- * Returns a pointer to the link pointing to the old head of the small list.
- */
-
-static struct blame_entry **filter_small(struct scoreboard *sb,
- struct blame_entry **small,
- struct blame_entry **source,
- unsigned score_min)
-{
- struct blame_entry *p = *source;
- struct blame_entry *oldsmall = *small;
- while (p) {
- if (ent_score(sb, p) <= score_min) {
- *small = p;
- small = &p->next;
- p = *small;
- } else {
- *source = p;
- source = &p->next;
- p = *source;
- }
- }
- *small = oldsmall;
- *source = NULL;
- return small;
-}
-
-/*
- * See if lines currently target is suspected for can be attributed to
- * parent.
- */
-static void find_move_in_parent(struct scoreboard *sb,
- struct blame_entry ***blamed,
- struct blame_entry **toosmall,
- struct origin *target,
- struct origin *parent)
-{
- struct blame_entry *e, split[3];
- struct blame_entry *unblamed = target->suspects;
- struct blame_entry *leftover = NULL;
- mmfile_t file_p;
-
- if (!unblamed)
- return; /* nothing remains for this target */
-
- fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
- if (!file_p.ptr)
- return;
-
- /* At each iteration, unblamed has a NULL-terminated list of
- * entries that have not yet been tested for blame. leftover
- * contains the reversed list of entries that have been tested
- * without being assignable to the parent.
- */
- do {
- struct blame_entry **unblamedtail = &unblamed;
- struct blame_entry *next;
- for (e = unblamed; e; e = next) {
- next = e->next;
- find_copy_in_blob(sb, e, parent, split, &file_p);
- if (split[1].suspect &&
- blame_move_score < ent_score(sb, &split[1])) {
- split_blame(blamed, &unblamedtail, split, e);
- } else {
- e->next = leftover;
- leftover = e;
- }
- decref_split(split);
- }
- *unblamedtail = NULL;
- toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);
- } while (unblamed);
- target->suspects = reverse_blame(leftover, NULL);
-}
-
-struct blame_list {
- struct blame_entry *ent;
- struct blame_entry split[3];
-};
-
-/*
- * Count the number of entries the target is suspected for,
- * and prepare a list of entry and the best split.
- */
-static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
- int *num_ents_p)
-{
- struct blame_entry *e;
- int num_ents, i;
- struct blame_list *blame_list = NULL;
-
- for (e = unblamed, num_ents = 0; e; e = e->next)
- num_ents++;
- if (num_ents) {
- blame_list = xcalloc(num_ents, sizeof(struct blame_list));
- for (e = unblamed, i = 0; e; e = e->next)
- blame_list[i++].ent = e;
- }
- *num_ents_p = num_ents;
- return blame_list;
-}
-
-/*
- * For lines target is suspected for, see if we can find code movement
- * across file boundary from the parent commit. porigin is the path
- * in the parent we already tried.
- */
-static void find_copy_in_parent(struct scoreboard *sb,
- struct blame_entry ***blamed,
- struct blame_entry **toosmall,
- struct origin *target,
- struct commit *parent,
- struct origin *porigin,
- int opt)
-{
- struct diff_options diff_opts;
- int i, j;
- struct blame_list *blame_list;
- int num_ents;
- struct blame_entry *unblamed = target->suspects;
- struct blame_entry *leftover = NULL;
-
- if (!unblamed)
- return; /* nothing remains for this target */
-
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-
- diff_setup_done(&diff_opts);
-
- /* Try "find copies harder" on new path if requested;
- * we do not want to use diffcore_rename() actually to
- * match things up; find_copies_harder is set only to
- * force diff_tree_sha1() to feed all filepairs to diff_queue,
- * and this code needs to be after diff_setup_done(), which
- * usually makes find-copies-harder imply copy detection.
- */
- if ((opt & PICKAXE_BLAME_COPY_HARDEST)
- || ((opt & PICKAXE_BLAME_COPY_HARDER)
- && (!porigin || strcmp(target->path, porigin->path))))
- DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
-
- if (is_null_oid(&target->commit->object.oid))
- do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
- else
- diff_tree_sha1(parent->tree->object.oid.hash,
- target->commit->tree->object.oid.hash,
- "", &diff_opts);
-
- if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
- diffcore_std(&diff_opts);
-
- do {
- struct blame_entry **unblamedtail = &unblamed;
- blame_list = setup_blame_list(unblamed, &num_ents);
-
- for (i = 0; i < diff_queued_diff.nr; i++) {
- struct diff_filepair *p = diff_queued_diff.queue[i];
- struct origin *norigin;
- mmfile_t file_p;
- struct blame_entry this[3];
-
- if (!DIFF_FILE_VALID(p->one))
- continue; /* does not exist in parent */
- if (S_ISGITLINK(p->one->mode))
- continue; /* ignore git links */
- if (porigin && !strcmp(p->one->path, porigin->path))
- /* find_move already dealt with this path */
- continue;
-
- norigin = get_origin(sb, parent, p->one->path);
- oidcpy(&norigin->blob_oid, &p->one->oid);
- norigin->mode = p->one->mode;
- fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
- if (!file_p.ptr)
- continue;
-
- for (j = 0; j < num_ents; j++) {
- find_copy_in_blob(sb, blame_list[j].ent,
- norigin, this, &file_p);
- copy_split_if_better(sb, blame_list[j].split,
- this);
- decref_split(this);
- }
- origin_decref(norigin);
- }
-
- for (j = 0; j < num_ents; j++) {
- struct blame_entry *split = blame_list[j].split;
- if (split[1].suspect &&
- blame_copy_score < ent_score(sb, &split[1])) {
- split_blame(blamed, &unblamedtail, split,
- blame_list[j].ent);
- } else {
- blame_list[j].ent->next = leftover;
- leftover = blame_list[j].ent;
- }
- decref_split(split);
- }
- free(blame_list);
- *unblamedtail = NULL;
- toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);
- } while (unblamed);
- target->suspects = reverse_blame(leftover, NULL);
- diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
-}
-
-/*
- * The blobs of origin and porigin exactly match, so everything
- * origin is suspected for can be blamed on the parent.
- */
-static void pass_whole_blame(struct scoreboard *sb,
- struct origin *origin, struct origin *porigin)
-{
- struct blame_entry *e, *suspects;
-
- if (!porigin->file.ptr && origin->file.ptr) {
- /* Steal its file */
- porigin->file = origin->file;
- origin->file.ptr = NULL;
- }
- suspects = origin->suspects;
- origin->suspects = NULL;
- for (e = suspects; e; e = e->next) {
- origin_incref(porigin);
- origin_decref(e->suspect);
- e->suspect = porigin;
- }
- queue_blames(sb, porigin, suspects);
-}
-
-/*
- * We pass blame from the current commit to its parents. We keep saying
- * "parent" (and "porigin"), but what we mean is to find scapegoat to
- * exonerate ourselves.
- */
-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
-{
- if (!reverse) {
- if (revs->first_parent_only &&
- commit->parents &&
- commit->parents->next) {
- free_commit_list(commit->parents->next);
- commit->parents->next = NULL;
- }
- return commit->parents;
- }
- return lookup_decoration(&revs->children, &commit->object);
-}
-
-static int num_scapegoats(struct rev_info *revs, struct commit *commit)
-{
- struct commit_list *l = first_scapegoat(revs, commit);
- return commit_list_count(l);
-}
-
-/* Distribute collected unsorted blames to the respected sorted lists
- * in the various origins.
- */
-static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
-{
- blamed = blame_sort(blamed, compare_blame_suspect);
- while (blamed)
- {
- struct origin *porigin = blamed->suspect;
- struct blame_entry *suspects = NULL;
- do {
- struct blame_entry *next = blamed->next;
- blamed->next = suspects;
- suspects = blamed;
- blamed = next;
- } while (blamed && blamed->suspect == porigin);
- suspects = reverse_blame(suspects, NULL);
- queue_blames(sb, porigin, suspects);
- }
-}
-
-#define MAXSG 16
-
-static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
-{
- struct rev_info *revs = sb->revs;
- int i, pass, num_sg;
- struct commit *commit = origin->commit;
- struct commit_list *sg;
- struct origin *sg_buf[MAXSG];
- struct origin *porigin, **sg_origin = sg_buf;
- struct blame_entry *toosmall = NULL;
- struct blame_entry *blames, **blametail = &blames;
-
- num_sg = num_scapegoats(revs, commit);
- if (!num_sg)
- goto finish;
- else if (num_sg < ARRAY_SIZE(sg_buf))
- memset(sg_buf, 0, sizeof(sg_buf));
- else
- sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
-
- /*
- * The first pass looks for unrenamed path to optimize for
- * common cases, then we look for renames in the second pass.
- */
- for (pass = 0; pass < 2 - no_whole_file_rename; pass++) {
- struct origin *(*find)(struct scoreboard *,
- struct commit *, struct origin *);
- find = pass ? find_rename : find_origin;
-
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct commit *p = sg->item;
- int j, same;
-
- if (sg_origin[i])
- continue;
- if (parse_commit(p))
- continue;
- porigin = find(sb, p, origin);
- if (!porigin)
- continue;
- if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
- pass_whole_blame(sb, origin, porigin);
- origin_decref(porigin);
- goto finish;
- }
- for (j = same = 0; j < i; j++)
- if (sg_origin[j] &&
- !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {
- same = 1;
- break;
- }
- if (!same)
- sg_origin[i] = porigin;
- else
- origin_decref(porigin);
- }
- }
-
- num_commits++;
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct origin *porigin = sg_origin[i];
- if (!porigin)
- continue;
- if (!origin->previous) {
- origin_incref(porigin);
- origin->previous = porigin;
- }
- pass_blame_to_parent(sb, origin, porigin);
- if (!origin->suspects)
- goto finish;
- }
-
- /*
- * Optionally find moves in parents' files.
- */
- if (opt & PICKAXE_BLAME_MOVE) {
- filter_small(sb, &toosmall, &origin->suspects, blame_move_score);
- if (origin->suspects) {
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct origin *porigin = sg_origin[i];
- if (!porigin)
- continue;
- find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
- if (!origin->suspects)
- break;
- }
- }
- }
-
- /*
- * Optionally find copies from parents' files.
- */
- if (opt & PICKAXE_BLAME_COPY) {
- if (blame_copy_score > blame_move_score)
- filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
- else if (blame_copy_score < blame_move_score) {
- origin->suspects = blame_merge(origin->suspects, toosmall);
- toosmall = NULL;
- filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
- }
- if (!origin->suspects)
- goto finish;
-
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct origin *porigin = sg_origin[i];
- find_copy_in_parent(sb, &blametail, &toosmall,
- origin, sg->item, porigin, opt);
- if (!origin->suspects)
- goto finish;
- }
- }
-
-finish:
- *blametail = NULL;
- distribute_blame(sb, blames);
- /*
- * prepend toosmall to origin->suspects
- *
- * There is no point in sorting: this ends up on a big
- * unsorted list in the caller anyway.
- */
- if (toosmall) {
- struct blame_entry **tail = &toosmall;
- while (*tail)
- tail = &(*tail)->next;
- *tail = origin->suspects;
- origin->suspects = toosmall;
- }
- for (i = 0; i < num_sg; i++) {
- if (sg_origin[i]) {
- drop_origin_blob(sg_origin[i]);
- origin_decref(sg_origin[i]);
- }
- }
- drop_origin_blob(origin);
- if (sg_buf != sg_origin)
- free(sg_origin);
+ return blame_nth_line((struct blame_scoreboard *)data, lno);
}
/*
@@ -1561,13 +79,13 @@ finish:
struct commit_info {
struct strbuf author;
struct strbuf author_mail;
- unsigned long author_time;
+ timestamp_t author_time;
struct strbuf author_tz;
/* filled only when asked for details */
struct strbuf committer;
struct strbuf committer_mail;
- unsigned long committer_time;
+ timestamp_t committer_time;
struct strbuf committer_tz;
struct strbuf summary;
@@ -1578,7 +96,7 @@ struct commit_info {
*/
static void get_ac_line(const char *inbuf, const char *what,
struct strbuf *name, struct strbuf *mail,
- unsigned long *time, struct strbuf *tz)
+ timestamp_t *time, struct strbuf *tz)
{
struct ident_split ident;
size_t len, maillen, namelen;
@@ -1699,10 +217,10 @@ static void get_commit_info(struct commit *commit,
* To allow LF and other nonportable characters in pathnames,
* they are c-style quoted as needed.
*/
-static void write_filename_info(struct origin *suspect)
+static void write_filename_info(struct blame_origin *suspect)
{
if (suspect->previous) {
- struct origin *prev = suspect->previous;
+ struct blame_origin *prev = suspect->previous;
printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
write_name_quoted(prev->path, stdout, '\n');
}
@@ -1716,7 +234,7 @@ static void write_filename_info(struct origin *suspect)
* the first time each commit appears in the output (unless the
* user has specifically asked for us to repeat).
*/
-static int emit_one_suspect_detail(struct origin *suspect, int repeat)
+static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
{
struct commit_info ci;
@@ -1727,11 +245,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
get_commit_info(suspect->commit, &ci, 1);
printf("author %s\n", ci.author.buf);
printf("author-mail %s\n", ci.author_mail.buf);
- printf("author-time %lu\n", ci.author_time);
+ printf("author-time %"PRItime"\n", ci.author_time);
printf("author-tz %s\n", ci.author_tz.buf);
printf("committer %s\n", ci.committer.buf);
printf("committer-mail %s\n", ci.committer_mail.buf);
- printf("committer-time %lu\n", ci.committer_time);
+ printf("committer-time %"PRItime"\n", ci.committer_time);
printf("committer-tz %s\n", ci.committer_tz.buf);
printf("summary %s\n", ci.summary.buf);
if (suspect->commit->object.flags & UNINTERESTING)
@@ -1746,11 +264,12 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
* The blame_entry is found to be guilty for the range.
* Show it in incremental output.
*/
-static void found_guilty_entry(struct blame_entry *ent,
- struct progress_info *pi)
+static void found_guilty_entry(struct blame_entry *ent, void *data)
{
+ struct progress_info *pi = (struct progress_info *)data;
+
if (incremental) {
- struct origin *suspect = ent->suspect;
+ struct blame_origin *suspect = ent->suspect;
printf("%s %d %d %d\n",
oid_to_hex(&suspect->commit->object.oid),
@@ -1763,88 +282,14 @@ static void found_guilty_entry(struct blame_entry *ent,
display_progress(pi->progress, pi->blamed_lines);
}
-/*
- * The main loop -- while we have blobs with lines whose true origin
- * is still unknown, pick one blob, and allow its lines to pass blames
- * to its parents. */
-static void assign_blame(struct scoreboard *sb, int opt)
-{
- struct rev_info *revs = sb->revs;
- struct commit *commit = prio_queue_get(&sb->commits);
- struct progress_info pi = { NULL, 0 };
-
- if (show_progress)
- pi.progress = start_progress_delay(_("Blaming lines"),
- sb->num_lines, 50, 1);
-
- while (commit) {
- struct blame_entry *ent;
- struct origin *suspect = commit->util;
-
- /* find one suspect to break down */
- while (suspect && !suspect->suspects)
- suspect = suspect->next;
-
- if (!suspect) {
- commit = prio_queue_get(&sb->commits);
- continue;
- }
-
- assert(commit == suspect->commit);
-
- /*
- * We will use this suspect later in the loop,
- * so hold onto it in the meantime.
- */
- origin_incref(suspect);
- parse_commit(commit);
- if (reverse ||
- (!(commit->object.flags & UNINTERESTING) &&
- !(revs->max_age != -1 && commit->date < revs->max_age)))
- pass_blame(sb, suspect, opt);
- else {
- commit->object.flags |= UNINTERESTING;
- if (commit->object.parsed)
- mark_parents_uninteresting(commit);
- }
- /* treat root commit as boundary */
- if (!commit->parents && !show_root)
- commit->object.flags |= UNINTERESTING;
-
- /* Take responsibility for the remaining entries */
- ent = suspect->suspects;
- if (ent) {
- suspect->guilty = 1;
- for (;;) {
- struct blame_entry *next = ent->next;
- found_guilty_entry(ent, &pi);
- if (next) {
- ent = next;
- continue;
- }
- ent->next = sb->ent;
- sb->ent = suspect->suspects;
- suspect->suspects = NULL;
- break;
- }
- }
- origin_decref(suspect);
-
- if (DEBUG) /* sanity */
- sanity_check_refcnt(sb);
- }
-
- stop_progress(&pi.progress);
-}
-
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
int show_raw_time)
{
static struct strbuf time_buf = STRBUF_INIT;
strbuf_reset(&time_buf);
if (show_raw_time) {
- strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+ strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
}
else {
const char *time_str;
@@ -1876,20 +321,20 @@ static const char *format_time(unsigned long time, const char *tz_str,
#define OUTPUT_SHOW_EMAIL 0400
#define OUTPUT_LINE_PORCELAIN 01000
-static void emit_porcelain_details(struct origin *suspect, int repeat)
+static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
{
if (emit_one_suspect_detail(suspect, repeat) ||
(suspect->commit->object.flags & MORE_THAN_ONE_PATH))
write_filename_info(suspect);
}
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
int opt)
{
int repeat = opt & OUTPUT_LINE_PORCELAIN;
int cnt;
const char *cp;
- struct origin *suspect = ent->suspect;
+ struct blame_origin *suspect = ent->suspect;
char hex[GIT_MAX_HEXSZ + 1];
oid_to_hex_r(hex, &suspect->commit->object.oid);
@@ -1900,7 +345,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
ent->num_lines);
emit_porcelain_details(suspect, repeat);
- cp = nth_line(sb, ent->lno);
+ cp = blame_nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
if (cnt) {
@@ -1922,11 +367,11 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
putchar('\n');
}
-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)
{
int cnt;
const char *cp;
- struct origin *suspect = ent->suspect;
+ struct blame_origin *suspect = ent->suspect;
struct commit_info ci;
char hex[GIT_MAX_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
@@ -1934,7 +379,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
get_commit_info(suspect->commit, &ci, 1);
oid_to_hex_r(hex, &suspect->commit->object.oid);
- cp = nth_line(sb, ent->lno);
+ cp = blame_nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
@@ -2001,14 +446,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
commit_info_destroy(&ci);
}
-static void output(struct scoreboard *sb, int option)
+static void output(struct blame_scoreboard *sb, int option)
{
struct blame_entry *ent;
if (option & OUTPUT_PORCELAIN) {
for (ent = sb->ent; ent; ent = ent->next) {
int count = 0;
- struct origin *suspect;
+ struct blame_origin *suspect;
struct commit *commit = ent->suspect->commit;
if (commit->object.flags & MORE_THAN_ONE_PATH)
continue;
@@ -2030,40 +475,6 @@ static void output(struct scoreboard *sb, int option)
}
}
-static const char *get_next_line(const char *start, const char *end)
-{
- const char *nl = memchr(start, '\n', end - start);
- return nl ? nl + 1 : end;
-}
-
-/*
- * To allow quick access to the contents of nth line in the
- * final image, prepare an index in the scoreboard.
- */
-static int prepare_lines(struct scoreboard *sb)
-{
- const char *buf = sb->final_buf;
- unsigned long len = sb->final_buf_size;
- const char *end = buf + len;
- const char *p;
- int *lineno;
- int num = 0;
-
- for (p = buf; p < end; p = get_next_line(p, end))
- num++;
-
- ALLOC_ARRAY(sb->lineno, num + 1);
- lineno = sb->lineno;
-
- for (p = buf; p < end; p = get_next_line(p, end))
- *lineno++ = p - buf;
-
- *lineno = len;
-
- sb->num_lines = num;
- return sb->num_lines;
-}
-
/*
* Add phony grafts for use with -S; this is primarily to
* support git's cvsserver that wants to give a linear history
@@ -2071,13 +482,13 @@ static int prepare_lines(struct scoreboard *sb)
*/
static int read_ancestry(const char *graft_file)
{
- FILE *fp = fopen(graft_file, "r");
+ FILE *fp = fopen_or_warn(graft_file, "r");
struct strbuf buf = STRBUF_INIT;
if (!fp)
return -1;
while (!strbuf_getwholeline(&buf, fp, '\n')) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
- struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+ struct commit_graft *graft = read_graft_line(&buf);
if (graft)
register_commit_graft(graft, 0);
}
@@ -2086,7 +497,7 @@ static int read_ancestry(const char *graft_file)
return 0;
}
-static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
+static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
{
const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
auto_abbrev);
@@ -2100,7 +511,7 @@ static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
* How many columns do we need to show line numbers, authors,
* and filenames?
*/
-static void find_alignment(struct scoreboard *sb, int *option)
+static void find_alignment(struct blame_scoreboard *sb, int *option)
{
int longest_src_lines = 0;
int longest_dst_lines = 0;
@@ -2110,7 +521,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
int auto_abbrev = DEFAULT_ABBREV;
for (e = sb->ent; e; e = e->next) {
- struct origin *suspect = e->suspect;
+ struct blame_origin *suspect = e->suspect;
int num;
if (compute_auto_abbrev)
@@ -2138,8 +549,8 @@ static void find_alignment(struct scoreboard *sb, int *option)
num = e->lno + e->num_lines;
if (longest_dst_lines < num)
longest_dst_lines = num;
- if (largest_score < ent_score(sb, e))
- largest_score = ent_score(sb, e);
+ if (largest_score < blame_entry_score(sb, e))
+ largest_score = blame_entry_score(sb, e);
}
max_orig_digits = decimal_width(longest_src_lines);
max_digits = decimal_width(longest_dst_lines);
@@ -2150,31 +561,12 @@ static void find_alignment(struct scoreboard *sb, int *option)
abbrev = auto_abbrev + 1;
}
-/*
- * For debugging -- origin is refcounted, and this asserts that
- * we do not underflow.
- */
-static void sanity_check_refcnt(struct scoreboard *sb)
+static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa)
{
- int baa = 0;
- struct blame_entry *ent;
-
- for (ent = sb->ent; ent; ent = ent->next) {
- /* Nobody should have zero or negative refcnt */
- if (ent->suspect->refcnt <= 0) {
- fprintf(stderr, "%s in %s has negative refcnt %d\n",
- ent->suspect->path,
- oid_to_hex(&ent->suspect->commit->object.oid),
- ent->suspect->refcnt);
- baa = 1;
- }
- }
- if (baa) {
- int opt = 0160;
- find_alignment(sb, &opt);
- output(sb, opt);
- die("Baa %d!", baa);
- }
+ int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME;
+ find_alignment(sb, &opt);
+ output(sb, opt);
+ die("Baa %d!", baa);
}
static unsigned parse_score(const char *arg)
@@ -2224,301 +616,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-static void verify_working_tree_path(struct commit *work_tree, const char *path)
-{
- struct commit_list *parents;
- int pos;
-
- for (parents = work_tree->parents; parents; parents = parents->next) {
- const struct object_id *commit_oid = &parents->item->object.oid;
- struct object_id blob_oid;
- unsigned mode;
-
- if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
- sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
- return;
- }
-
- pos = cache_name_pos(path, strlen(path));
- if (pos >= 0)
- ; /* path is in the index */
- else if (-1 - pos < active_nr &&
- !strcmp(active_cache[-1 - pos]->name, path))
- ; /* path is in the index, unmerged */
- else
- die("no such path '%s' in HEAD", path);
-}
-
-static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
-{
- struct commit *parent;
-
- parent = lookup_commit_reference(oid->hash);
- if (!parent)
- die("no such commit %s", oid_to_hex(oid));
- return &commit_list_insert(parent, tail)->next;
-}
-
-static void append_merge_parents(struct commit_list **tail)
-{
- int merge_head;
- struct strbuf line = STRBUF_INIT;
-
- merge_head = open(git_path_merge_head(), O_RDONLY);
- if (merge_head < 0) {
- if (errno == ENOENT)
- return;
- die("cannot open '%s' for reading", git_path_merge_head());
- }
-
- while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
- struct object_id oid;
- if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
- die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
- tail = append_parent(tail, &oid);
- }
- close(merge_head);
- strbuf_release(&line);
-}
-
-/*
- * This isn't as simple as passing sb->buf and sb->len, because we
- * want to transfer ownership of the buffer to the commit (so we
- * must use detach).
- */
-static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
-{
- size_t len;
- void *buf = strbuf_detach(sb, &len);
- set_commit_buffer(c, buf, len);
-}
-
-/*
- * Prepare a dummy commit that represents the work tree (or staged) item.
- * Note that annotating work tree item never works in the reverse.
- */
-static struct commit *fake_working_tree_commit(struct diff_options *opt,
- const char *path,
- const char *contents_from)
-{
- struct commit *commit;
- struct origin *origin;
- struct commit_list **parent_tail, *parent;
- struct object_id head_oid;
- struct strbuf buf = STRBUF_INIT;
- const char *ident;
- time_t now;
- int size, len;
- struct cache_entry *ce;
- unsigned mode;
- struct strbuf msg = STRBUF_INIT;
-
- read_cache();
- time(&now);
- commit = alloc_commit_node();
- commit->object.parsed = 1;
- commit->date = now;
- parent_tail = &commit->parents;
-
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
- die("no such ref: HEAD");
-
- parent_tail = append_parent(parent_tail, &head_oid);
- append_merge_parents(parent_tail);
- verify_working_tree_path(commit, path);
-
- origin = make_origin(commit, path);
-
- ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
- strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
- for (parent = commit->parents; parent; parent = parent->next)
- strbuf_addf(&msg, "parent %s\n",
- oid_to_hex(&parent->item->object.oid));
- strbuf_addf(&msg,
- "author %s\n"
- "committer %s\n\n"
- "Version of %s from %s\n",
- ident, ident, path,
- (!contents_from ? path :
- (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
- set_commit_buffer_from_strbuf(commit, &msg);
-
- if (!contents_from || strcmp("-", contents_from)) {
- struct stat st;
- const char *read_from;
- char *buf_ptr;
- unsigned long buf_len;
-
- if (contents_from) {
- if (stat(contents_from, &st) < 0)
- die_errno("Cannot stat '%s'", contents_from);
- read_from = contents_from;
- }
- else {
- if (lstat(path, &st) < 0)
- die_errno("Cannot lstat '%s'", path);
- read_from = path;
- }
- mode = canon_mode(st.st_mode);
-
- switch (st.st_mode & S_IFMT) {
- case S_IFREG:
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
- strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
- else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
- die_errno("cannot open or read '%s'", read_from);
- break;
- case S_IFLNK:
- if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
- die_errno("cannot readlink '%s'", read_from);
- break;
- default:
- die("unsupported file type %s", read_from);
- }
- }
- else {
- /* Reading from stdin */
- mode = 0;
- if (strbuf_read(&buf, 0, 0) < 0)
- die_errno("failed to read from stdin");
- }
- convert_to_git(path, buf.buf, buf.len, &buf, 0);
- origin->file.ptr = buf.buf;
- origin->file.size = buf.len;
- pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
-
- /*
- * Read the current index, replace the path entry with
- * origin->blob_sha1 without mucking with its mode or type
- * bits; we are not going to write this index out -- we just
- * want to run "diff-index --cached".
- */
- discard_cache();
- read_cache();
-
- len = strlen(path);
- if (!mode) {
- int pos = cache_name_pos(path, len);
- if (0 <= pos)
- mode = active_cache[pos]->ce_mode;
- else
- /* Let's not bother reading from HEAD tree */
- mode = S_IFREG | 0644;
- }
- size = cache_entry_size(len);
- ce = xcalloc(1, size);
- oidcpy(&ce->oid, &origin->blob_oid);
- memcpy(ce->name, path, len);
- ce->ce_flags = create_ce_flags(0);
- ce->ce_namelen = len;
- ce->ce_mode = create_ce_mode(mode);
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
-
- cache_tree_invalidate_path(&the_index, path);
-
- return commit;
-}
-
-static struct commit *find_single_final(struct rev_info *revs,
- const char **name_p)
-{
- int i;
- struct commit *found = NULL;
- const char *name = NULL;
-
- for (i = 0; i < revs->pending.nr; i++) {
- struct object *obj = revs->pending.objects[i].item;
- if (obj->flags & UNINTERESTING)
- continue;
- obj = deref_tag(obj, NULL, 0);
- if (obj->type != OBJ_COMMIT)
- die("Non commit %s?", revs->pending.objects[i].name);
- if (found)
- die("More than one commit to dig from %s and %s?",
- revs->pending.objects[i].name, name);
- found = (struct commit *)obj;
- name = revs->pending.objects[i].name;
- }
- if (name_p)
- *name_p = name;
- return found;
-}
-
-static char *prepare_final(struct scoreboard *sb)
-{
- const char *name;
- sb->final = find_single_final(sb->revs, &name);
- return xstrdup_or_null(name);
-}
-
-static const char *dwim_reverse_initial(struct scoreboard *sb)
-{
- /*
- * DWIM "git blame --reverse ONE -- PATH" as
- * "git blame --reverse ONE..HEAD -- PATH" but only do so
- * when it makes sense.
- */
- struct object *obj;
- struct commit *head_commit;
- unsigned char head_sha1[20];
-
- if (sb->revs->pending.nr != 1)
- return NULL;
-
- /* Is that sole rev a committish? */
- obj = sb->revs->pending.objects[0].item;
- obj = deref_tag(obj, NULL, 0);
- if (obj->type != OBJ_COMMIT)
- return NULL;
-
- /* Do we have HEAD? */
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
- return NULL;
- head_commit = lookup_commit_reference_gently(head_sha1, 1);
- if (!head_commit)
- return NULL;
-
- /* Turn "ONE" into "ONE..HEAD" then */
- obj->flags |= UNINTERESTING;
- add_pending_object(sb->revs, &head_commit->object, "HEAD");
-
- sb->final = (struct commit *)obj;
- return sb->revs->pending.objects[0].name;
-}
-
-static char *prepare_initial(struct scoreboard *sb)
-{
- int i;
- const char *final_commit_name = NULL;
- struct rev_info *revs = sb->revs;
-
- /*
- * There must be one and only one negative commit, and it must be
- * the boundary.
- */
- for (i = 0; i < revs->pending.nr; i++) {
- struct object *obj = revs->pending.objects[i].item;
- if (!(obj->flags & UNINTERESTING))
- continue;
- obj = deref_tag(obj, NULL, 0);
- if (obj->type != OBJ_COMMIT)
- die("Non commit %s?", revs->pending.objects[i].name);
- if (sb->final)
- die("More than one commit to dig up from, %s and %s?",
- revs->pending.objects[i].name,
- final_commit_name);
- sb->final = (struct commit *) obj;
- final_commit_name = revs->pending.objects[i].name;
- }
-
- if (!final_commit_name)
- final_commit_name = dwim_reverse_initial(sb);
- if (!final_commit_name)
- die("No commit to dig up from?");
- return xstrdup(final_commit_name);
-}
-
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
{
int *opt = option->value;
@@ -2552,17 +649,24 @@ 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 < sha1_object_info(oid.hash, NULL);
+}
+
int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
const char *path;
- struct scoreboard sb;
- struct origin *o;
+ struct blame_scoreboard sb;
+ struct blame_origin *o;
struct blame_entry *ent = NULL;
long dashdash_pos, lno;
- char *final_commit_name = NULL;
- enum object_type type;
- struct commit *final_commit = NULL;
+ struct progress_info pi = { NULL, 0 };
struct string_list range_list = STRING_LIST_INIT_NODUP;
int output_option = 0, opt = 0;
@@ -2613,8 +717,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
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;
@@ -2639,9 +743,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)) {
@@ -2708,15 +812,10 @@ 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);
- if (!blame_move_score)
- blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
- if (!blame_copy_score)
- blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
-
/*
* We have collected options unknown to us in argv[1..unk]
* which are to be passed to revision machinery if we are
@@ -2755,108 +854,26 @@ 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;
setup_revisions(argc, argv, &revs, NULL);
- memset(&sb, 0, sizeof(sb));
+ init_scoreboard(&sb);
sb.revs = &revs;
- if (!reverse) {
- final_commit_name = prepare_final(&sb);
- sb.commits.compare = compare_commits_by_commit_date;
- }
- else if (contents_from)
- die(_("--contents and --reverse do not blend well."));
- else {
- final_commit_name = prepare_initial(&sb);
- sb.commits.compare = compare_commits_by_reverse_commit_date;
- if (revs.first_parent_only)
- revs.children.name = NULL;
- }
-
- if (!sb.final) {
- /*
- * "--not A B -- path" without anything positive;
- * do not default to HEAD, but use the working tree
- * or "--contents".
- */
- setup_work_tree();
- sb.final = fake_working_tree_commit(&sb.revs->diffopt,
- path, contents_from);
- add_pending_object(&revs, &(sb.final->object), ":");
- }
- else if (contents_from)
- die(_("cannot use --contents with final commit object name"));
-
- if (reverse && revs.first_parent_only) {
- final_commit = find_single_final(sb.revs, NULL);
- if (!final_commit)
- die(_("--reverse and --first-parent together require specified latest commit"));
- }
-
- /*
- * If we have bottom, this will mark the ancestors of the
- * bottom commits we would reach while traversing as
- * uninteresting.
- */
- if (prepare_revision_walk(&revs))
- die(_("revision walk setup failed"));
-
- if (reverse && revs.first_parent_only) {
- struct commit *c = final_commit;
-
- sb.revs->children.name = "children";
- while (c->parents &&
- oidcmp(&c->object.oid, &sb.final->object.oid)) {
- struct commit_list *l = xcalloc(1, sizeof(*l));
-
- l->item = c;
- if (add_decoration(&sb.revs->children,
- &c->parents->item->object, l))
- die("BUG: not unique item in first-parent chain");
- c = c->parents->item;
- }
-
- if (oidcmp(&c->object.oid, &sb.final->object.oid))
- die(_("--reverse --first-parent together require range along first-parent chain"));
- }
-
- if (is_null_oid(&sb.final->object.oid)) {
- o = sb.final->util;
- sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
- sb.final_buf_size = o->file.size;
- }
- else {
- o = get_origin(&sb, sb.final, path);
- if (fill_blob_sha1_and_mode(o))
- die(_("no such path %s in %s"), path, final_commit_name);
-
- if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
- textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf,
- &sb.final_buf_size))
- ;
- else
- sb.final_buf = read_sha1_file(o->blob_oid.hash, &type,
- &sb.final_buf_size);
-
- if (!sb.final_buf)
- die(_("cannot read blob %s for path %s"),
- oid_to_hex(&o->blob_oid),
- path);
- }
- num_read_blob++;
- lno = prepare_lines(&sb);
+ sb.contents_from = contents_from;
+ sb.reverse = reverse;
+ setup_scoreboard(&sb, path, &o);
+ lno = sb.num_lines;
if (lno && !range_list.nr)
string_list_append(&range_list, "1");
@@ -2885,22 +902,13 @@ parse_done:
for (range_i = ranges.nr; range_i > 0; --range_i) {
const struct range *r = &ranges.ranges[range_i - 1];
- long bottom = r->start;
- long top = r->end;
- struct blame_entry *next = ent;
- ent = xcalloc(1, sizeof(*ent));
- ent->lno = bottom;
- ent->num_lines = top - bottom;
- ent->suspect = o;
- ent->s_lno = bottom;
- ent->next = next;
- origin_incref(o);
+ ent = blame_entry_prepend(ent, r->start, r->end, o);
}
o->suspects = ent;
prio_queue_put(&sb.commits, o->commit);
- origin_decref(o);
+ blame_origin_decref(o);
range_set_release(&ranges);
string_list_clear(&range_list, 0);
@@ -2908,21 +916,37 @@ parse_done:
sb.ent = NULL;
sb.path = path;
+ if (blame_move_score)
+ sb.move_score = blame_move_score;
+ if (blame_copy_score)
+ sb.copy_score = blame_copy_score;
+
+ sb.debug = DEBUG;
+ sb.on_sanity_fail = &sanity_check_on_fail;
+
+ sb.show_root = show_root;
+ sb.xdl_opts = xdl_opts;
+ sb.no_whole_file_rename = no_whole_file_rename;
+
read_mailmap(&mailmap, NULL);
+ sb.found_guilty_entry = &found_guilty_entry;
+ sb.found_guilty_entry_data = &pi;
+ if (show_progress)
+ pi.progress = start_delayed_progress(_("Blaming lines"), sb.num_lines);
+
assign_blame(&sb, opt);
+ stop_progress(&pi.progress);
+
if (!incremental)
setup_pager();
-
- free(final_commit_name);
-
- if (incremental)
+ else
return 0;
- sb.ent = blame_sort(sb.ent, compare_blame_final);
+ blame_sort_final(&sb);
- coalesce(&sb);
+ blame_coalesce(&sb);
if (!(output_option & OUTPUT_PORCELAIN))
find_alignment(&sb, &output_option);
@@ -2936,9 +960,9 @@ parse_done:
}
if (show_stats) {
- printf("num read blob: %d\n", num_read_blob);
- printf("num get patch: %d\n", num_get_patch);
- printf("num commits: %d\n", num_commits);
+ printf("num read blob: %d\n", sb.num_read_blob);
+ printf("num get patch: %d\n", sb.num_get_patch);
+ printf("num commits: %d\n", sb.num_commits);
}
return 0;
}
diff --git a/builtin/branch.c b/builtin/branch.c
index a3bd2262b3..6d0cea9d4b 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -6,6 +6,7 @@
*/
#include "cache.h"
+#include "config.h"
#include "color.h"
#include "refs.h"
#include "commit.h"
@@ -27,6 +28,7 @@ static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
+ N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
N_("git branch [<options>] [-r | -a] [--points-at]"),
N_("git branch [<options>] [-r | -a] [--format]"),
NULL
@@ -123,8 +125,8 @@ 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)
- reference_rev = lookup_commit_reference(oid.hash);
+ &oid, NULL)) != NULL)
+ reference_rev = lookup_commit_reference(&oid);
}
if (!reference_rev)
reference_rev = head_rev;
@@ -157,7 +159,7 @@ static int check_branch_commit(const char *branchname, const char *refname,
const struct object_id *oid, struct commit *head_rev,
int kinds, int force)
{
- struct commit *rev = lookup_commit_reference(oid->hash);
+ struct commit *rev = lookup_commit_reference(oid);
if (!rev) {
error(_("Couldn't look up commit object for '%s'"), refname);
return -1;
@@ -211,11 +213,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
if (!force) {
- head_rev = lookup_commit_reference(head_oid.hash);
+ head_rev = lookup_commit_reference(&head_oid);
if (!head_rev)
die(_("Couldn't look up commit object for HEAD"));
}
- for (i = 0; i < argc; i++, strbuf_release(&bname)) {
+ for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
int flags = 0;
@@ -239,7 +241,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.")
@@ -255,8 +257,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'"),
@@ -280,8 +282,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
free(name);
+ strbuf_release(&bname);
- return(ret);
+ return ret;
}
static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
@@ -351,7 +354,7 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
- strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
+ strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET));
strbuf_addf(&local, " %s ", obname.buf);
if (filter->verbose > 1)
@@ -382,7 +385,7 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
return strbuf_detach(&fmt, NULL);
}
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
{
int i;
struct ref_array array;
@@ -406,14 +409,17 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (filter->verbose)
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
- if (!format)
- format = to_free = build_format(filter, maxwidth, remote_prefix);
- verify_ref_format(format);
+ if (!format->format)
+ format->format = to_free = build_format(filter, maxwidth, remote_prefix);
+ format->use_color = branch_use_color;
+
+ if (verify_ref_format(format))
+ die(_("unable to parse format string"));
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
- format_ref_array_item(array.items[i], format, 0, &out);
+ format_ref_array_item(array.items[i], format, &out);
if (column_active(colopts)) {
assert(!filter->verbose && "--column and --verbose are incompatible");
/* format to a string_list to let print_columns() do its job */
@@ -452,15 +458,20 @@ static void reject_rebase_or_bisect_branch(const char *target)
free_worktrees(worktrees);
}
-static void rename_branch(const char *oldname, const char *newname, int force)
+static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
{
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)
- die(_("cannot rename the current branch while not on any."));
+ if (!oldname) {
+ if (copy)
+ die(_("cannot copy the current branch while not on any."));
+ else
+ die(_("cannot rename the current branch while not on any."));
+ }
if (strbuf_check_branch_ref(&oldref, oldname)) {
/*
@@ -477,32 +488,53 @@ static void rename_branch(const char *oldname, const char *newname, int force)
* 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);
- strbuf_addf(&logmsg, "Branch: renamed %s to %s",
- oldref.buf, newref.buf);
+ if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
+ !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
+ die("BUG: expected prefix missing for refs");
+ }
+
+ if (copy)
+ strbuf_addf(&logmsg, "Branch: copied %s to %s",
+ oldref.buf, newref.buf);
+ else
+ strbuf_addf(&logmsg, "Branch: renamed %s to %s",
+ oldref.buf, newref.buf);
- if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
+ if (!copy && rename_ref(oldref.buf, newref.buf, logmsg.buf))
die(_("Branch rename failed"));
+ if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+ die(_("Branch copy failed"));
- if (recovery)
- warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
+ if (recovery) {
+ if (copy)
+ warning(_("Created a copy of a misnamed branch '%s'"),
+ interpreted_oldname);
+ else
+ warning(_("Renamed a misnamed branch '%s' away"),
+ interpreted_oldname);
+ }
- if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
+ if (!copy &&
+ replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
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 (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
+ if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
die(_("Branch is renamed, but update of config-file failed"));
+ if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+ die(_("Branch is copied, but update of config-file failed"));
strbuf_release(&oldsection);
strbuf_release(&newsection);
}
@@ -540,7 +572,7 @@ static int edit_branch_description(const char *branch_name)
int cmd_branch(int argc, const char **argv, const char *prefix)
{
- int delete = 0, rename = 0, force = 0, list = 0;
+ int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
int reflog = 0, edit_description = 0;
int quiet = 0, unset_upstream = 0;
const char *new_upstream = NULL;
@@ -548,7 +580,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
struct ref_filter filter;
int icase = 0;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- const char *format = NULL;
+ struct ref_format format = REF_FORMAT_INIT;
struct option options[] = {
OPT_GROUP(N_("Generic options")),
@@ -557,8 +589,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),
- OPT_SET_INT( 0, "set-upstream", &track, N_("change upstream info"),
- BRANCH_TRACK_OVERRIDE),
+ { OPTION_SET_INT, 0, "set-upstream", &track, NULL, N_("do not use"),
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, BRANCH_TRACK_OVERRIDE },
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")),
@@ -577,11 +609,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+ OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
+ OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
OPT_BOOL(0, "list", &list, N_("list branch names")),
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")),
@@ -592,7 +626,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
N_("print only branches of the object"), 0, parse_opt_object_name
},
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
- OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
+ OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END(),
};
@@ -609,7 +643,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"))
@@ -620,14 +654,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
- if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
+ if (!delete && !rename && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)
list = 1;
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
filter.no_commit)
list = 1;
- if (!!delete + !!rename + !!new_upstream +
+ if (!!delete + !!rename + !!copy + !!new_upstream +
list + unset_upstream > 1)
usage_with_options(builtin_branch_usage, options);
@@ -645,8 +679,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (force) {
delete *= 2;
rename *= 2;
+ copy *= 2;
}
+ if (list)
+ setup_auto_pager("branch", 1);
+
if (delete) {
if (!argc)
die(_("branch name required"));
@@ -666,7 +704,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!sorting)
sorting = ref_default_sorting();
sorting->ignore_case = icase;
- print_ref_list(&filter, sorting, format);
+ print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
return 0;
@@ -699,20 +737,29 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (edit_branch_description(branch_name))
return 1;
+ } else if (copy) {
+ if (!argc)
+ die(_("branch name required"));
+ else if (argc == 1)
+ copy_or_rename_branch(head, argv[0], 1, copy > 1);
+ else if (argc == 2)
+ copy_or_rename_branch(argv[0], argv[1], 1, copy > 1);
+ else
+ die(_("too many branches for a copy operation"));
} else if (rename) {
if (!argc)
die(_("branch name required"));
else if (argc == 1)
- rename_branch(head, argv[0], rename > 1);
+ copy_or_rename_branch(head, argv[0], 0, rename > 1);
else if (argc == 2)
- rename_branch(argv[0], argv[1], rename > 1);
+ copy_or_rename_branch(argv[0], argv[1], 0, rename > 1);
else
- die(_("too many branches for a rename operation"));
+ die(_("too many arguments for a rename operation"));
} else if (new_upstream) {
struct branch *branch = branch_get(argv[0]);
if (argc > 1)
- die(_("too many branches to set new upstream"));
+ die(_("too many arguments to set new upstream"));
if (!branch) {
if (!argc || !strcmp(argv[0], "HEAD"))
@@ -735,7 +782,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
if (argc > 1)
- die(_("too many branches to unset upstream"));
+ die(_("too many arguments to unset upstream"));
if (!branch) {
if (!argc || !strcmp(argv[0], "HEAD"))
@@ -755,11 +802,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
} else if (argc > 0 && argc <= 2) {
struct branch *branch = branch_get(argv[0]);
- int branch_existed = 0, remote_tracking = 0;
- struct strbuf buf = STRBUF_INIT;
-
- if (!strcmp(argv[0], "HEAD"))
- die(_("it does not make sense to create 'HEAD' manually"));
if (!branch)
die(_("no such branch '%s'"), argv[0]);
@@ -768,27 +810,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
if (track == BRANCH_TRACK_OVERRIDE)
- fprintf(stderr, _("The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to\n"));
+ die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
- strbuf_addf(&buf, "refs/remotes/%s", branch->name);
- remote_tracking = ref_exists(buf.buf);
- strbuf_release(&buf);
-
- branch_existed = ref_exists(branch->refname);
create_branch(argv[0], (argc == 2) ? argv[1] : head,
- force, reflog, 0, quiet, track);
-
- /*
- * We only show the instructions if the user gave us
- * one branch which doesn't exist locally, but is the
- * name of a remote-tracking branch.
- */
- if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
- !branch_existed && remote_tracking) {
- fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
- fprintf(stderr, " git branch -d %s\n", branch->name);
- fprintf(stderr, " git branch --set-upstream-to %s\n", branch->name);
- }
+ 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 73c81f0cb1..d90170f070 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -4,12 +4,15 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
+#include "diff.h"
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
#include "tree-walk.h"
#include "sha1-array.h"
+#include "packfile.h"
struct batch_options {
int enabled;
@@ -55,14 +58,14 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
struct object_context obj_context;
struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
- unsigned flags = LOOKUP_REPLACE_OBJECT;
+ unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
const char *path = force_path;
if (unknown_type)
- flags |= LOOKUP_UNKNOWN_OBJECT;
+ flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
- if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
- oid.hash, &obj_context))
+ if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
+ &oid, &obj_context))
die("Not a valid object name %s", obj_name);
if (!path)
@@ -73,7 +76,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
buf = NULL;
switch (opt) {
case 't':
- oi.typename = &sb;
+ oi.type_name = &sb;
if (sha1_object_info_extended(oid.hash, &oi, flags) < 0)
die("git cat-file: could not get object info");
if (sb.len) {
@@ -94,7 +97,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
return !has_object_file(&oid);
case 'w':
- if (!path[0])
+ if (!path)
die("git cat-file --filters %s: <object> must be "
"<sha1:path>", obj_name);
@@ -104,12 +107,13 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
break;
case 'c':
- if (!path[0])
+ if (!path)
die("git cat-file --textconv %s: <object> must be <sha1:path>",
obj_name);
if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size))
break;
+ /* else fallthrough */
case 'p':
type = sha1_object_info(oid.hash, NULL);
@@ -225,7 +229,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;
@@ -336,7 +340,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, LOOKUP_REPLACE_OBJECT) < 0) {
+ sha1_object_info_extended(data->oid.hash, &data->info,
+ OBJECT_INFO_LOOKUP_REPLACE) < 0) {
printf("%s missing\n",
obj_name ? obj_name : oid_to_hex(&data->oid));
fflush(stdout);
@@ -358,10 +363,10 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt,
struct expand_data *data)
{
struct object_context ctx;
- int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
+ int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
enum follow_symlinks_result result;
- result = get_sha1_with_context(obj_name, flags, data->oid.hash, &ctx);
+ result = get_oid_with_context(obj_name, flags, &data->oid, &ctx);
if (result != FOUND) {
switch (result) {
case MISSING_OBJECT:
@@ -470,6 +475,8 @@ static int batch_objects(struct batch_options *opt)
for_each_loose_object(batch_loose_object, &sa, 0);
for_each_packed_object(batch_packed_object, &sa, 0);
+ if (repository_format_partial_clone)
+ warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
cb.opt = opt;
cb.expand = &data;
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 4d01ca0c8b..91444dc044 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "attr.h"
#include "quote.h"
#include "parse-options.h"
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 1d73d3ca3d..ec9a959e08 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,9 +1,11 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "dir.h"
#include "quote.h"
#include "pathspec.h"
#include "parse-options.h"
+#include "submodule.h"
static int quiet, verbose, stdin_paths, show_non_matching, no_index;
static const char * const check_ignore_usage[] = {
@@ -70,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;
@@ -87,21 +89,24 @@ static int check_ignore(struct dir_struct *dir,
parse_pathspec(&pathspec,
PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
PATHSPEC_SYMLINK_LEADING_PATH |
- PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE |
PATHSPEC_KEEP_ORDER,
prefix, argv);
+ die_path_inside_submodule(&the_index, &pathspec);
+
/*
* look for pathspecs matching entries in the index, since these
* should not be ignored, in order to be consistent with
* 'git status', 'git add' etc.
*/
- seen = find_pathspecs_matching_against_index(&pathspec);
+ seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
for (i = 0; i < pathspec.nr; i++) {
full_path = pathspec.items[i].match;
exclude = NULL;
if (!seen[i]) {
- exclude = last_exclude_matching(dir, full_path, &dtype);
+ int dtype = DT_UNKNOWN;
+ exclude = last_exclude_matching(dir, &the_index,
+ full_path, &dtype);
}
if (!quiet && (exclude || show_non_matching))
output_exclude(pathspec.items[i].original, exclude);
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index cf0f54f6b9..cdce144f3b 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "mailmap.h"
#include "parse-options.h"
#include "string-list.h"
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index eac499450f..bc67d3f0a8 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -39,12 +39,15 @@ 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 07631d0c9c..a730f6a1aa 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -5,6 +5,7 @@
*
*/
#include "builtin.h"
+#include "config.h"
#include "lockfile.h"
#include "quote.h"
#include "cache-tree.h"
@@ -128,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)
{
@@ -149,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;
@@ -158,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,
@@ -205,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 */
@@ -250,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 b360943455..d76e13c852 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,6 @@
#include "builtin.h"
+#include "config.h"
+#include "checkout.h"
#include "lockfile.h"
#include "parse-options.h"
#include "refs.h"
@@ -21,31 +23,12 @@
#include "submodule-config.h"
#include "submodule.h"
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
N_("git checkout [<options>] [<branch>] -- <file>..."),
NULL,
};
-static int option_parse_recurse_submodules(const struct option *opt,
- const char *arg, int unset)
-{
- if (unset) {
- recurse_submodules = RECURSE_SUBMODULES_OFF;
- return 0;
- }
- if (arg)
- recurse_submodules =
- parse_update_recurse_submodules_arg(opt->long_name,
- arg);
- else
- recurse_submodules = RECURSE_SUBMODULES_ON;
-
- return 0;
-}
-
struct checkout_opts {
int patch_mode;
int quiet;
@@ -71,14 +54,14 @@ 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. */
}
@@ -244,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);
@@ -265,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");
@@ -293,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,6 +356,8 @@ static int checkout_paths(const struct checkout_opts *opts,
state.force = 1;
state.refresh_cache = 1;
state.istate = &the_index;
+
+ enable_delayed_checkout(&state);
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
@@ -390,12 +372,13 @@ static int checkout_paths(const struct checkout_opts *opts,
pos = skip_same_name(ce, pos) - 1;
}
}
+ 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);
- head = lookup_commit_reference_gently(rev.hash, 1);
+ read_ref_full("HEAD", 0, &rev, NULL);
+ head = lookup_commit_reference_gently(&rev, 1);
errs |= post_checkout_hook(head, head, 0);
return errs;
@@ -417,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.hash, DEFAULT_ABBREV), sb.buf);
+ } else {
+ fprintf(stderr, "%s %s %s\n", msg,
+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
+ }
strbuf_release(&sb);
}
@@ -451,6 +440,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
* update paths in the work tree, and we cannot revert
* them.
*/
+ /* fallthrough */
case 0:
return 0;
default:
@@ -481,20 +471,20 @@ 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(new_branch_info->commit->tree, opts, 1, writeout_error);
if (ret)
return ret;
} else {
@@ -520,7 +510,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) {
@@ -528,11 +518,11 @@ 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.hash :
- EMPTY_TREE_SHA1_BIN);
+ 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.hash);
+ 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);
@@ -549,10 +539,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 */
@@ -580,18 +570,18 @@ 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(new_branch_info->commit->tree, 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, new_branch_info->commit->tree, work,
+ old_branch_info->commit->tree, &result);
if (ret < 0)
exit(128);
- ret = reset_tree(new->commit->tree, opts, 0,
+ ret = reset_tree(new_branch_info->commit->tree, opts, 0,
writeout_error);
strbuf_release(&o.obuf);
if (ret)
@@ -605,29 +595,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;
@@ -654,76 +644,76 @@ 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,
const struct object_id *oid,
int flags, void *cb_data)
{
- add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
+ add_pending_oid(cb_data, refname, oid, UNINTERESTING);
return 0;
}
@@ -796,11 +786,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);
@@ -809,60 +798,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_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
-
- refs = revs.pending;
- revs.leak_pending = 1;
+ add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
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);
- clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
- free(refs.objects);
+ /* Clean up objects used, as they will be reused. */
+ 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.hash, 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;
}
@@ -876,54 +862,14 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
}
if (starts_with(var, "submodule."))
- return parse_submodule_config_option(var, value);
+ return git_default_submodule_config(var, value, NULL);
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)
{
@@ -1041,22 +987,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->hash, 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->hash);
+ *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 = new_branch_info->commit->tree;
}
if (!*source_tree) /* case (1): want a tree */
@@ -1096,7 +1042,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"));
@@ -1125,36 +1071,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) {
- struct object_id oid;
int flag;
- char *head_ref = resolve_refdup("HEAD", 0, oid.hash, &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[] = {
@@ -1172,9 +1117,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,20 +1132,19 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
N_("second guess 'git checkout <no-such-branch>'")),
OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
N_("do not check if another worktree is holding the given ref")),
- { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+ { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
"checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
OPT_END(),
};
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;
- gitmodules_config();
git_config(git_checkout_config, &opts);
opts.track = BRANCH_TRACK_UNSPECIFIED;
@@ -1217,12 +1164,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
}
- if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
- git_config(submodule_config, NULL);
- if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
- set_config_update_recurse_submodules(recurse_submodules);
- }
-
if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1271,7 +1212,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;
}
@@ -1304,16 +1245,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 937eb17b66..fad533a0a7 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -8,6 +8,7 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "dir.h"
#include "parse-options.h"
#include "string-list.h"
@@ -32,15 +33,6 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
-static int clean_use_color = -1;
-static char clean_colors[][COLOR_MAXLEN] = {
- GIT_COLOR_RESET,
- GIT_COLOR_NORMAL, /* PLAIN */
- GIT_COLOR_BOLD_BLUE, /* PROMPT */
- GIT_COLOR_BOLD, /* HEADER */
- GIT_COLOR_BOLD_RED, /* HELP */
- GIT_COLOR_BOLD_RED, /* ERROR */
-};
enum color_clean {
CLEAN_COLOR_RESET = 0,
CLEAN_COLOR_PLAIN = 1,
@@ -50,6 +42,16 @@ enum color_clean {
CLEAN_COLOR_ERROR = 5
};
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+ [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
+ [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD,
+ [CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED,
+ [CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL,
+ [CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE,
+ [CLEAN_COLOR_RESET] = GIT_COLOR_RESET,
+};
+
#define MENU_OPTS_SINGLETON 01
#define MENU_OPTS_IMMEDIATE 02
#define MENU_OPTS_LIST_ONLY 04
@@ -166,7 +168,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
}
*dir_gone = 0;
- return 0;
+ goto out;
}
dir = opendir(path->buf);
@@ -180,7 +182,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
}
- return res;
+ ret = res;
+ goto out;
}
strbuf_complete(path, '/');
@@ -248,6 +251,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
for (i = 0; i < dels.nr; i++)
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
}
+out:
+ strbuf_release(&quoted);
string_list_clear(&dels, 0);
return ret;
}
@@ -683,7 +688,7 @@ static int filter_by_patterns_cmd(void)
for_each_string_list_item(item, &del_list) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, item->string, &dtype)) {
+ if (is_excluded(&dir, &the_index, item->string, &dtype)) {
*item->string = '\0';
changed++;
}
@@ -837,8 +842,7 @@ static void interactive_main_loop(void)
int ret;
ret = menus[*chosen].fn();
if (ret != MENU_RETURN_NO_LOOP) {
- free(chosen);
- chosen = NULL;
+ FREE_AND_NULL(chosen);
if (!del_list.nr) {
clean_print_color(CLEAN_COLOR_ERROR);
printf_ln(_("No more files to clean, exiting."));
@@ -851,8 +855,7 @@ static void interactive_main_loop(void)
quit_cmd();
}
- free(chosen);
- chosen = NULL;
+ FREE_AND_NULL(chosen);
break;
}
}
@@ -906,7 +909,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")),
@@ -965,7 +968,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_CWD,
prefix, argv);
- fill_directory(&dir, &pathspec);
+ fill_directory(&dir, &the_index, &pathspec);
correct_untracked_entries(&dir);
for (i = 0; i < dir.nr; i++) {
diff --git a/builtin/clone.c b/builtin/clone.c
index a6ae7d6180..101c27a593 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -9,6 +9,7 @@
*/
#include "builtin.h"
+#include "config.h"
#include "lockfile.h"
#include "parse-options.h"
#include "fetch-pack.h"
@@ -24,6 +25,8 @@
#include "remote.h"
#include "run-command.h"
#include "connected.h"
+#include "packfile.h"
+#include "list-objects-filter-options.h"
/*
* Overall FIXMEs:
@@ -40,6 +43,7 @@ static const char * const builtin_clone_usage[] = {
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared;
+static int option_no_tags;
static int option_shallow_submodules;
static int deepen;
static char *option_template, *option_depth, *option_since;
@@ -57,6 +61,7 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
static int max_jobs = -1;
static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
+static struct list_objects_filter_options filter_options;
static int recurse_submodules_cb(const struct option *opt,
const char *arg, int unset)
@@ -120,6 +125,8 @@ static struct option builtin_clone_options[] = {
N_("deepen history of shallow clone, excluding rev")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "no-tags", &option_no_tags,
+ N_("don't clone any tags, and make later fetches not to follow them")),
OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
@@ -130,6 +137,7 @@ static struct option builtin_clone_options[] = {
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
TRANSPORT_FAMILY_IPV6),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
@@ -357,7 +365,7 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
* to turn entries with paths relative to the original
* absolute, so that they can be used in the new repository.
*/
- FILE *in = fopen(src->buf, "r");
+ FILE *in = xfopen(src->buf, "r");
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, in) != EOF) {
@@ -447,7 +455,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 {
@@ -467,7 +476,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,
@@ -496,14 +507,14 @@ 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);
- strbuf_reset(&sb);
+ remove_dir_recursively(&sb, junk_work_tree_flags);
}
+ strbuf_release(&sb);
}
static void remove_junk_on_signal(int signo)
@@ -563,7 +574,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
} else
get_fetch_map(refs, refspec, &tail, 0);
- if (!option_mirror && !option_single_branch)
+ if (!option_mirror && !option_single_branch && !option_no_tags)
get_fetch_map(refs, tag_refspec, &tail, 0);
return local_refs;
@@ -583,7 +594,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);
}
@@ -605,12 +616,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;
@@ -625,7 +636,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;
}
@@ -652,7 +663,7 @@ static void update_remote_refs(const struct ref *refs,
if (refs) {
write_remote_refs(mapped_refs);
- if (option_single_branch)
+ if (option_single_branch && !option_no_tags)
write_followtags(refs, msg);
}
@@ -677,23 +688,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.hash);
+ 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);
}
}
@@ -701,7 +712,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;
@@ -710,7 +721,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"));
@@ -728,8 +739,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;
@@ -739,13 +749,13 @@ static int checkout(int submodule_progress)
opts.src_index = &the_index;
opts.dst_index = &the_index;
- tree = parse_tree_indirect(oid.hash);
+ tree = parse_tree_indirect(&oid);
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
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),
@@ -764,6 +774,9 @@ static int checkout(int submodule_progress)
if (submodule_progress)
argv_array_push(&args, "--progress");
+ if (option_verbosity < 0)
+ argv_array_push(&args, "--quiet");
+
err = run_command_v_opt(args.argv, RUN_GIT_CMD);
argv_array_clear(&args);
}
@@ -855,10 +868,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;
@@ -878,6 +896,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct refspec *refspec;
const char *fetch_pattern;
+ fetch_if_missing = 0;
+
packet_trace_identity("clone");
argc = parse_options(argc, argv, prefix, builtin_clone_options,
builtin_clone_usage, 0);
@@ -930,7 +950,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);
@@ -941,7 +961,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);
}
@@ -959,14 +979,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);
@@ -1037,6 +1067,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, repo);
strbuf_reset(&key);
+ if (option_no_tags) {
+ strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+ git_config_set(key.buf, "--no-tags");
+ strbuf_reset(&key);
+ }
+
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
@@ -1059,6 +1095,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
warning(_("--shallow-since is ignored in local clones; use file:// instead."));
if (option_not.nr)
warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
+ if (filter_options.choice)
+ warning(_("--filter is ignored in local clones; use file:// instead."));
if (!access(mkpath("%s/shallow", path), F_OK)) {
if (option_local > 0)
warning(_("source repository is shallow, ignoring --local"));
@@ -1069,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)
@@ -1090,7 +1125,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_set_option(transport, TRANS_OPT_UPLOADPACK,
option_upload_pack);
- if (transport->smart_options && !deepen)
+ if (filter_options.choice) {
+ transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+ filter_options.filter_spec);
+ transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+ }
+
+ if (transport->smart_options && !deepen && !filter_options.choice)
transport->smart_options->check_self_contained_and_connected = 1;
refs = transport_get_remote_refs(transport);
@@ -1150,13 +1191,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
write_refspec_config(src_ref_prefix, our_head_points_at,
remote_head_points_at, &branch_top);
+ if (filter_options.choice)
+ partial_clone_register("origin", &filter_options);
+
if (is_local)
clone_local(path, git_dir);
else if (refs && complete_refs_before_fetch)
transport_fetch_refs(transport, mapped_refs);
update_remote_refs(refs, mapped_refs, remote_head_points_at,
- branch_top.buf, reflog_msg.buf, transport, !is_local);
+ branch_top.buf, reflog_msg.buf, transport,
+ !is_local && !filter_options.choice);
update_head(our_head_points_at, remote_head, reflog_msg.buf);
@@ -1177,6 +1222,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
junk_mode = JUNK_LEAVE_REPO;
+ fetch_if_missing = 1;
err = checkout(submodule_progress);
strbuf_release(&reflog_msg);
diff --git a/builtin/column.c b/builtin/column.c
index 33314b4d71..0c3223d64b 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "strbuf.h"
#include "parse-options.h"
#include "string-list.h"
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 605017261c..e5bdf57b1e 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "tree.h"
#include "builtin.h"
@@ -55,10 +56,10 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
struct object_id oid;
if (argc <= ++i)
usage(commit_tree_usage);
- if (get_sha1_commit(argv[i], oid.hash))
+ if (get_oid_commit(argv[i], &oid))
die("Not a valid object name %s", argv[i]);
assert_sha1_type(oid.hash, OBJ_COMMIT);
- new_parent(lookup_commit(oid.hash), &parents);
+ new_parent(lookup_commit(&oid), &parents);
continue;
}
@@ -101,11 +102,10 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (fd && close(fd))
die_errno("git commit-tree: failed to close '%s'",
argv[i]);
- strbuf_complete_line(&buffer);
continue;
}
- if (get_sha1_tree(arg, tree_oid.hash))
+ if (get_oid_tree(arg, &tree_oid))
die("Not a valid object name %s", arg);
if (got_tree)
die("Cannot give more than one trees");
@@ -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 aff6bf7aad..37fcb55ab0 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -6,6 +6,7 @@
*/
#include "cache.h"
+#include "config.h"
#include "lockfile.h"
#include "cache-tree.h"
#include "color.h"
@@ -30,9 +31,7 @@
#include "gpg-interface.h"
#include "column.h"
#include "sequencer.h"
-#include "notes-utils.h"
#include "mailmap.h"
-#include "sigchain.h"
static const char * const builtin_commit_usage[] = {
N_("git commit [<options>] [--] <pathspec>..."),
@@ -44,31 +43,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"
@@ -92,8 +66,6 @@ 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 *use_message_buffer;
static struct lock_file index_lock; /* real index */
static struct lock_file false_lock; /* used only for partial commits */
@@ -117,7 +89,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;
/*
@@ -127,19 +99,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 const char *only_include_assumed;
+static int have_option_m;
static struct strbuf message = STRBUF_INIT;
static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
@@ -195,7 +161,6 @@ 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);
- gitmodules_config();
git_config(fn, s);
determine_whence(s);
init_diff_ui_defaults();
@@ -253,7 +218,8 @@ static int list_paths(struct string_list *list, const char *with_tree,
if (with_tree) {
char *max_prefix = common_prefix(pattern);
- overlay_tree_on_cache(with_tree, max_prefix ? max_prefix : prefix);
+ overlay_tree_on_index(&the_index, with_tree,
+ max_prefix ? max_prefix : prefix);
free(max_prefix);
}
@@ -313,7 +279,7 @@ static void create_base_index(const struct commit *current_head)
opts.dst_index = &the_index;
opts.fn = oneway_merge;
- tree = parse_tree_indirect(current_head->object.oid.hash);
+ tree = parse_tree_indirect(&current_head->object.oid);
if (!tree)
die(_("failed to unpack HEAD tree object"));
parse_tree(tree);
@@ -335,7 +301,7 @@ static void refresh_cache_or_die(int refresh_flags)
static const char *prepare_index(int argc, const char **argv, const char *prefix,
const struct commit *current_head, int is_status)
{
- struct string_list partial;
+ struct string_list partial = STRING_LIST_INIT_DUP;
struct pathspec pathspec;
int refresh_flags = REFRESH_QUIET;
const char *ret;
@@ -355,7 +321,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,13 +340,14 @@ 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"));
commit_style = COMMIT_NORMAL;
- return get_lock_file_path(&index_lock);
+ ret = get_lock_file_path(&index_lock);
+ goto out;
}
/*
@@ -400,10 +367,11 @@ 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;
- return get_lock_file_path(&index_lock);
+ ret = get_lock_file_path(&index_lock);
+ goto out;
}
/*
@@ -421,15 +389,12 @@ 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;
- return get_index_file();
+ ret = get_index_file();
+ goto out;
}
/*
@@ -460,7 +425,6 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
die(_("cannot do a partial commit during a cherry-pick."));
}
- string_list_init(&partial, 1);
if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
exit(1);
@@ -472,7 +436,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,
@@ -484,12 +448,15 @@ 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();
ret = get_lock_file_path(&false_lock);
read_cache_from(ret);
+out:
+ string_list_clear(&partial, 0);
+ clear_pathspec(&pathspec);
return ret;
}
@@ -509,7 +476,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->index_file = index_file;
s->fp = fp;
s->nowarn = nowarn;
- s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
+ s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
if (!s->is_initial)
hashcpy(s->sha1_commit, oid.hash);
s->status_format = status_format;
@@ -668,7 +635,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 */
@@ -696,7 +663,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, "-")) {
@@ -726,6 +693,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)) {
/*
@@ -807,7 +776,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
@@ -827,23 +796,21 @@ 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"
"with '%c' will be kept; you may remove them"
" yourself if you want to.\n"
"An empty message aborts the commit.\n"), comment_line_char);
- if (only_include_assumed)
- status_printf_ln(s, GIT_COLOR_NORMAL,
- "%s", only_include_assumed);
/*
* These should never fail because they come from our own
@@ -877,8 +844,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
(int)(ci.name_end - ci.name_begin), ci.name_begin,
(int)(ci.mail_end - ci.mail_begin), ci.mail_begin);
- if (ident_shown)
- status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
+ status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); /* Add new line for clarity */
saved_color_setting = s->use_color;
s->use_color = 0;
@@ -894,7 +860,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (amend)
parent = "HEAD^1";
- if (get_sha1(parent, oid.hash)) {
+ if (get_oid(parent, &oid)) {
int i, ita_nr = 0;
for (i = 0; i < active_nr; i++)
@@ -911,11 +877,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);
@@ -943,13 +910,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- /*
- * Re-read the index as pre-commit hook could have updated it,
- * and write it out as a tree. We must do this before we invoke
- * the editor and after we invoke run_status above.
- */
- discard_cache();
+ if (!no_verify && find_hook("pre-commit")) {
+ /*
+ * Re-read the index as pre-commit hook could have updated it,
+ * and write it out as a tree. We must do this before we invoke
+ * the editor and after we invoke run_status above.
+ */
+ discard_cache();
+ }
read_cache_from(index_file);
+
if (update_main_cache_tree(0)) {
error(_("Error building trees"));
return 0;
@@ -979,65 +949,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;
@@ -1071,6 +982,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)
{
@@ -1133,6 +1057,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[],
@@ -1179,8 +1106,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)
@@ -1208,18 +1135,18 @@ static int parse_and_validate_options(int argc, const char *argv[],
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
if (argc == 0 && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));
- if (argc > 0 && !also && !only)
- only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths...");
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);
@@ -1263,6 +1190,10 @@ static int parse_status_slot(const char *slot)
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;
}
@@ -1291,6 +1222,10 @@ static int git_status_config(const char *k, const char *v, void *cb)
status_deferred_config.show_branch = git_config_bool(k, v);
return 0;
}
+ if (!strcmp(k, "status.showstash")) {
+ s->show_stash = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
s->use_color = git_config_colorbool(k, v);
return 0;
@@ -1339,6 +1274,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
N_("show status concisely"), STATUS_FORMAT_SHORT),
OPT_BOOL('b', "branch", &s.show_branch,
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 },
@@ -1351,8 +1290,10 @@ 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" },
@@ -1371,8 +1312,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);
@@ -1380,9 +1325,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)
read_cache_preload(&s.pathspec);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
- fd = hold_locked_index(&index_lock, 0);
+ if (use_optional_locks())
+ fd = hold_locked_index(&index_lock, 0);
+ else
+ fd = -1;
- s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
+ s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
if (!s.is_initial)
hashcpy(s.sha1_commit, oid.hash);
@@ -1402,97 +1350,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;
- struct object_id junk_oid;
- 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->hash);
- 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, junk_oid.hash, 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;
@@ -1522,37 +1379,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;
@@ -1577,6 +1403,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")),
@@ -1612,6 +1439,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,
@@ -1635,26 +1464,25 @@ 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"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
status_init_config(&s, git_commit_config);
+ s.commit_template = 1;
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
s.colopts = 0;
- if (get_sha1("HEAD", oid.hash))
+ if (get_oid("HEAD", &oid))
current_head = NULL;
else {
- current_head = lookup_commit_or_die(oid.hash, "HEAD");
+ current_head = lookup_commit_or_die(&oid, "HEAD");
if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
@@ -1695,10 +1523,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!reflog_msg)
reflog_msg = "commit (merge)";
pptr = commit_list_append(current_head, pptr);
- fp = fopen(git_path_merge_head(), "r");
- if (fp == NULL)
- die_errno(_("could not open '%s' for reading"),
- git_path_merge_head());
+ fp = xfopen(git_path_merge_head(), "r");
while (strbuf_getline_lf(&m, fp) != EOF) {
struct commit *parent;
@@ -1716,7 +1541,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)
@@ -1734,19 +1559,19 @@ 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 != COMMIT_MSG_CLEANUP_NONE)
+ strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
- if (cleanup_mode != CLEANUP_NONE)
- strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
- if (template_untouched(&sb) && !allow_empty_message) {
+ if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
rollback_index_files();
- fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+ fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
exit(1);
}
- if (message_is_empty(&sb) && !allow_empty_message) {
+ if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
rollback_index_files();
- fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
+ fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
exit(1);
}
@@ -1758,33 +1583,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->sha1,
- 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());
@@ -1799,20 +1611,22 @@ 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.hash, oid.hash);
- 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);
- strbuf_release(&err);
+ UNLEAK(err);
+ UNLEAK(sb);
return 0;
}
diff --git a/builtin/config.c b/builtin/config.c
index 7f6c25d4d9..01169dd628 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "color.h"
#include "parse-options.h"
#include "urlmatch.h"
@@ -47,10 +48,18 @@ static int show_origin;
#define ACTION_GET_COLORBOOL (1<<14)
#define ACTION_GET_URLMATCH (1<<15)
+/*
+ * 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<<0)
#define TYPE_INT (1<<1)
#define TYPE_BOOL_OR_INT (1<<2)
#define TYPE_PATH (1<<3)
+#define TYPE_EXPIRY_DATE (1<<4)
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
@@ -79,6 +88,7 @@ static struct option builtin_config_options[] = {
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_BIT(0, "expiry-date", &types, 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")),
@@ -158,6 +168,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
return -1;
strbuf_addstr(buf, v);
free((char *)v);
+ } else if (types == TYPE_EXPIRY_DATE) {
+ timestamp_t t;
+ if (git_config_expiry_date(&t, key_, value_) < 0)
+ return -1;
+ strbuf_addf(buf, "%"PRItime, t);
} else if (value_) {
strbuf_addstr(buf, value_);
} else {
@@ -214,8 +229,7 @@ static int get_value(const char *key_, const char *regex_)
key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(key_regexp, key, REG_EXTENDED)) {
error("invalid key pattern: %s", key_);
- free(key_regexp);
- key_regexp = NULL;
+ FREE_AND_NULL(key_regexp);
ret = CONFIG_INVALID_PATTERN;
goto free_strings;
}
@@ -235,15 +249,14 @@ static int get_value(const char *key_, const char *regex_)
regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(regexp, regex_, REG_EXTENDED)) {
error("invalid pattern: %s", regex_);
- free(regexp);
- regexp = NULL;
+ FREE_AND_NULL(regexp);
ret = CONFIG_INVALID_PATTERN;
goto free_strings;
}
}
- git_config_with_options(collect_config, &values,
- &given_config_source, &config_options);
+ config_with_options(collect_config, &values,
+ &given_config_source, &config_options);
ret = !values.nr;
@@ -274,12 +287,13 @@ static char *normalize_value(const char *key, const char *value)
if (!value)
return NULL;
- if (types == 0 || types == TYPE_PATH)
+ if (types == 0 || types == TYPE_PATH || types == 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)
@@ -320,8 +334,8 @@ static void get_color(const char *var, const char *def_color)
get_color_slot = var;
get_color_found = 0;
parsed_color[0] = '\0';
- git_config_with_options(git_get_color_config, NULL,
- &given_config_source, &config_options);
+ config_with_options(git_get_color_config, NULL,
+ &given_config_source, &config_options);
if (!get_color_found && def_color) {
if (color_parse(def_color, parsed_color) < 0)
@@ -352,8 +366,8 @@ static int get_colorbool(const char *var, int print)
get_colorbool_found = -1;
get_diff_color_found = -1;
get_color_ui_found = -1;
- git_config_with_options(git_get_colorbool_config, NULL,
- &given_config_source, &config_options);
+ config_with_options(git_get_colorbool_config, NULL,
+ &given_config_source, &config_options);
if (get_colorbool_found < 0) {
if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -441,8 +455,8 @@ static int get_urlmatch(const char *var, const char *url)
show_keys = 1;
}
- git_config_with_options(urlmatch_config_entry, &config,
- &given_config_source, &config_options);
+ config_with_options(urlmatch_config_entry, &config,
+ &given_config_source, &config_options);
ret = !values.nr;
@@ -519,10 +533,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
die("$HOME not set");
if (access_or_warn(user_config, R_OK, 0) &&
- xdg_config && !access_or_warn(xdg_config, R_OK, 0))
+ xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
given_config_source.file = xdg_config;
- else
+ free(user_config);
+ } else {
given_config_source.file = user_config;
+ free(xdg_config);
+ }
}
else if (use_system_config)
given_config_source.file = git_etc_gitconfig();
@@ -538,6 +555,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)
config_options.respect_includes = !given_config_source.file;
else
config_options.respect_includes = respect_includes_opt;
+ if (!nongit) {
+ config_options.commondir = get_git_common_dir();
+ config_options.git_dir = get_git_dir();
+ }
if (end_null) {
term = '\0';
@@ -580,11 +601,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
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 (git_config_with_options(show_all_config, NULL,
- &given_config_source,
- &config_options) < 0) {
+ if (config_with_options(show_all_config, NULL,
+ &given_config_source,
+ &config_options) < 0) {
if (given_config_source.file)
die_errno("unable to read config file '%s'",
given_config_source.file);
@@ -625,6 +649,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
+ UNLEAK(value);
ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
@@ -635,6 +660,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
+ UNLEAK(value);
return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[2], 0);
}
@@ -642,6 +668,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
+ UNLEAK(value);
return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value,
CONFIG_REGEX_NONE, 0);
@@ -650,6 +677,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
+ UNLEAK(value);
return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[2], 1);
}
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index acb05940fc..33343818c8 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -5,10 +5,12 @@
*/
#include "cache.h"
+#include "config.h"
#include "dir.h"
#include "builtin.h"
#include "parse-options.h"
#include "quote.h"
+#include "packfile.h"
static unsigned long garbage;
static off_t size_garbage;
diff --git a/builtin/credential.c b/builtin/credential.c
index 0412fa00f0..879acfbcda 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -10,9 +10,9 @@ int cmd_credential(int argc, const char **argv, const char *prefix)
const char *op;
struct credential c = CREDENTIAL_INIT;
- op = argv[1];
- if (!op)
+ if (argc != 2 || !strcmp(argv[1], "-h"))
usage(usage_msg);
+ op = argv[1];
if (credential_read(&c, stdin) < 0)
die("unable to read credential from stdin");
diff --git a/builtin/describe.c b/builtin/describe.c
index a5cd8c513f..e4869df7b4 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,17 +1,21 @@
#include "cache.h"
+#include "config.h"
#include "lockfile.h"
#include "commit.h"
#include "tag.h"
+#include "blob.h"
#include "refs.h"
#include "builtin.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"
-#define SEEN (1u << 0)
#define MAX_TAGS (FLAG_BITS - 1)
static const char * const describe_usage[] = {
@@ -53,9 +57,14 @@ static const char *prio_names[] = {
N_("head"), N_("lightweight"), N_("annotated"),
};
-static int commit_name_cmp(const struct commit_name *cn1,
- const struct commit_name *cn2, const void *peeled)
+static int commit_name_cmp(const void *unused_cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *peeled)
{
+ const struct commit_name *cn1 = entry;
+ const struct commit_name *cn2 = entry_or_key;
+
return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
}
@@ -79,13 +88,13 @@ static int replace_name(struct commit_name *e,
struct tag *t;
if (!e->tag) {
- t = lookup_tag(e->oid.hash);
+ t = lookup_tag(&e->oid);
if (!t || parse_tag(t))
return 1;
e->tag = t;
}
- t = lookup_tag(oid->hash);
+ t = lookup_tag(oid);
if (!t || parse_tag(t))
return 0;
*tag = t;
@@ -123,13 +132,24 @@ static void add_to_known_names(const char *path,
static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
{
- int is_tag = starts_with(path, "refs/tags/");
+ int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
-
- /* Reject anything outside refs/tags/ unless --all */
- if (!all && !is_tag)
+ const char *path_to_match = NULL;
+
+ if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+ is_tag = 1;
+ } else if (all) {
+ if ((exclude_patterns.nr || patterns.nr) &&
+ !skip_prefix(path, "refs/heads/", &path_to_match) &&
+ !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+ /* Only accept reference of known type if there are match/exclude patterns */
+ return 0;
+ }
+ } else {
+ /* Reject anything outside refs/tags/ unless --all */
return 0;
+ }
/*
* If we're given exclude patterns, first exclude any tag which match
@@ -138,11 +158,8 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
if (exclude_patterns.nr) {
struct string_list_item *item;
- if (!is_tag)
- return 0;
-
for_each_string_list_item(item, &exclude_patterns) {
- if (!wildmatch(item->string, path + 10, 0, NULL))
+ if (!wildmatch(item->string, path_to_match, 0))
return 0;
}
}
@@ -152,22 +169,22 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
* pattern.
*/
if (patterns.nr) {
+ int found = 0;
struct string_list_item *item;
- if (!is_tag)
- return 0;
-
for_each_string_list_item(item, &patterns) {
- if (!wildmatch(item->string, path + 10, 0, NULL))
+ if (!wildmatch(item->string, path_to_match, 0)) {
+ found = 1;
break;
+ }
+ }
- /* If we get here, no pattern matched. */
+ if (!found)
return 0;
- }
}
/* Is it annotated? */
- if (!peel_ref(path, peeled.hash)) {
+ if (!peel_ref(path, &peeled)) {
is_annotated = !!oidcmp(oid, &peeled);
} else {
oidcpy(&peeled, oid);
@@ -242,10 +259,10 @@ 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.hash);
+ n->tag = lookup_tag(&n->oid);
if (!n->tag || parse_tag(n->tag))
die(_("annotated tag %s not available"), n->path);
}
@@ -257,20 +274,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->hash, 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;
@@ -279,37 +298,32 @@ 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.hash);
- 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);
for (; n; n = hashmap_iter_next(&iter)) {
- c = lookup_commit_reference_gently(n->peeled.hash, 1);
+ c = lookup_commit_reference_gently(&n->peeled, 1);
if (c)
c->util = n;
}
@@ -367,22 +381,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->hash, 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);
@@ -420,15 +433,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 (sha1_object_info(oid.hash, 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)
@@ -500,9 +584,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
return cmd_name_rev(args.argc, args.argv, prefix);
}
- hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0);
+ hashmap_init(&names, commit_name_cmp, NULL, 0);
for_each_rawref(get_name, NULL);
- if (!names.size && !always)
+ if (!hashmap_get_size(&names) && !always)
die(_("No names found, cannot describe anything."));
if (argc == 0) {
@@ -529,7 +613,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
}
} else if (dirty) {
static struct lock_file index_lock;
- int fd;
+ 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,
@@ -538,8 +624,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-files.c b/builtin/diff-files.c
index 15c61fd8d1..e88493ffe5 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
@@ -20,9 +21,11 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
int result;
unsigned options = 0;
- init_revisions(&rev, prefix);
- gitmodules_config();
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage(diff_files_usage);
+
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+ init_revisions(&rev, prefix);
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 1af373d002..522f4fdffd 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "config.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
@@ -17,9 +18,11 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
int i;
int result;
- init_revisions(&rev, prefix);
- gitmodules_config();
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage(diff_cache_usage);
+
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+ init_revisions(&rev, prefix);
rev.abbrev = 0;
precompose_argv(argc, argv);
@@ -53,5 +56,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
return -1;
}
result = run_diff_index(&rev, cached);
+ UNLEAK(rev);
return diff_result_code(&rev.diffopt, result);
}
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a570fea55b..473615117e 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "config.h"
#include "diff.h"
#include "commit.h"
#include "log-tree.h"
@@ -7,9 +8,9 @@
static struct rev_info log_tree_opt;
-static int diff_tree_commit_sha1(const struct object_id *oid)
+static int diff_tree_commit_oid(const struct object_id *oid)
{
- struct commit *commit = lookup_commit_reference(oid->hash);
+ struct commit *commit = lookup_commit_reference(oid);
if (!commit)
return -1;
return log_tree_commit(&log_tree_opt, commit);
@@ -23,7 +24,7 @@ static int stdin_diff_commit(struct commit *commit, const char *p)
/* Graft the fake parents locally to the commit */
while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
- struct commit *parent = lookup_commit(oid.hash);
+ struct commit *parent = lookup_commit(&oid);
if (!pptr) {
/* Free the real parent list */
free_commit_list(commit->parents);
@@ -44,13 +45,13 @@ static int stdin_diff_trees(struct tree *tree1, const char *p)
struct tree *tree2;
if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
return error("Need exactly two trees, separated by a space");
- tree2 = lookup_tree(oid.hash);
+ tree2 = lookup_tree(&oid);
if (!tree2 || parse_tree(tree2))
return -1;
printf("%s %s\n", oid_to_hex(&tree1->object.oid),
oid_to_hex(&tree2->object.oid));
- diff_tree_sha1(tree1->object.oid.hash, tree2->object.oid.hash,
- "", &log_tree_opt.diffopt);
+ diff_tree_oid(&tree1->object.oid, &tree2->object.oid,
+ "", &log_tree_opt.diffopt);
log_tree_diff_flush(&log_tree_opt);
return 0;
}
@@ -67,7 +68,7 @@ static int diff_tree_stdin(char *line)
line[len-1] = 0;
if (parse_oid_hex(line, &oid, &p))
return -1;
- obj = parse_object(oid.hash);
+ obj = parse_object(&oid);
if (!obj)
return -1;
if (obj->type == OBJ_COMMIT)
@@ -75,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;
}
@@ -98,16 +99,19 @@ static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt
int cmd_diff_tree(int argc, const char **argv, const char *prefix)
{
- int nr_sha1;
char line[1000];
struct object *tree1, *tree2;
static struct rev_info *opt = &log_tree_opt;
struct setup_revision_opt s_r_opt;
int read_stdin = 0;
- init_revisions(opt, prefix);
- gitmodules_config();
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage(diff_tree_usage);
+
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;
@@ -134,15 +138,14 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
* second one is marked UNINTERESTING, we recover the original
* order the user gave, i.e. "a..b", by swapping the trees.
*/
- nr_sha1 = opt->pending.nr;
- switch (nr_sha1) {
+ switch (opt->pending.nr) {
case 0:
if (!read_stdin)
usage(diff_tree_usage);
break;
case 1:
tree1 = opt->pending.objects[0].item;
- diff_tree_commit_sha1(&tree1->oid);
+ diff_tree_commit_oid(&tree1->oid);
break;
case 2:
tree1 = opt->pending.objects[0].item;
@@ -150,9 +153,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
if (tree2->flags & UNINTERESTING) {
SWAP(tree2, tree1);
}
- diff_tree_sha1(tree1->oid.hash,
- tree2->oid.hash,
- "", &opt->diffopt);
+ diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
log_tree_diff_flush(opt);
break;
}
diff --git a/builtin/diff.c b/builtin/diff.c
index 5e7c6428c9..16bfb22f73 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -4,6 +4,7 @@
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
+#include "config.h"
#include "lockfile.h"
#include "color.h"
#include "commit.h"
@@ -43,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);
@@ -56,8 +57,8 @@ static void stuff_change(struct diff_options *opt,
one = alloc_filespec(old_path);
two = alloc_filespec(new_path);
- fill_filespec(one, old_oid->hash, old_oid_valid, old_mode);
- fill_filespec(two, new_oid->hash, new_oid_valid, new_mode);
+ fill_filespec(one, old_oid, old_oid_valid, old_mode);
+ fill_filespec(two, new_oid, new_oid_valid, new_mode);
diff_queue(&diff_queued_diff, one, two);
}
@@ -174,7 +175,7 @@ static int builtin_diff_tree(struct rev_info *revs,
swap = 1;
oid[swap] = &ent0->item->oid;
oid[1 - swap] = &ent1->item->oid;
- diff_tree_sha1(oid[0]->hash, oid[1]->hash, "", &revs->diffopt);
+ diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
log_tree_diff_flush(revs);
return 0;
}
@@ -194,7 +195,7 @@ static int builtin_diff_combined(struct rev_info *revs,
revs->dense_combined_merges = revs->combine_merges = 1;
for (i = 1; i < ents; i++)
oid_array_append(&parents, &ent[i].item->oid);
- diff_tree_combined(ent[0].item->oid.hash, &parents,
+ diff_tree_combined(&ent[0].item->oid, &parents,
revs->dense_combined_merges, revs);
oid_array_clear(&parents);
return 0;
@@ -202,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)
@@ -314,8 +314,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
no_index = DIFF_NO_INDEX_IMPLICIT;
}
- if (!no_index)
- gitmodules_config();
init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
precompose_argv(argc, argv);
@@ -351,8 +349,8 @@ 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;
if (nongit)
die(_("Not a git repository"));
@@ -362,7 +360,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);
@@ -381,7 +379,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_SHA1_BIN);
+ tree = lookup_tree(the_hash_algo->empty_tree);
add_pending_object(&rev, &tree->object, "HEAD");
}
break;
@@ -395,7 +393,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
const char *name = entry->name;
int flags = (obj->flags & UNINTERESTING);
if (!obj->parsed)
- obj = parse_object(obj->oid.hash);
+ obj = parse_object(&obj->oid);
obj = deref_tag(obj, NULL, 0);
if (!obj)
die(_("invalid object '%s' given."), name);
@@ -465,5 +463,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
result = diff_result_code(&rev.diffopt, result);
if (1 < rev.diffopt.skip_stat_unmatch)
refresh_index_quietly();
+ UNLEAK(rev);
+ UNLEAK(ent);
+ UNLEAK(blob);
return result;
}
diff --git a/builtin/difftool.c b/builtin/difftool.c
index b9a892f269..bcc79d1888 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -12,6 +12,7 @@
* Copyright (C) 2016 Johannes Schindelin
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "run-command.h"
#include "exec_cmd.h"
@@ -110,7 +111,7 @@ static int use_wt_file(const char *workdir, const char *name,
int fd = open(buf.buf, O_RDONLY);
if (fd >= 0 &&
- !index_fd(wt_oid.hash, fd, &st, OBJ_BLOB, name, 0)) {
+ !index_fd(&wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
if (is_null_oid(oid)) {
oidcpy(oid, &wt_oid);
use = 1;
@@ -129,9 +130,13 @@ struct working_tree_entry {
char path[FLEX_ARRAY];
};
-static int working_tree_entry_cmp(struct working_tree_entry *a,
- struct working_tree_entry *b, void *keydata)
+static int working_tree_entry_cmp(const void *unused_cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *unused_keydata)
{
+ const struct working_tree_entry *a = entry;
+ const struct working_tree_entry *b = entry_or_key;
return strcmp(a->path, b->path);
}
@@ -145,8 +150,14 @@ struct pair_entry {
const char path[FLEX_ARRAY];
};
-static int pair_cmp(struct pair_entry *a, struct pair_entry *b, void *keydata)
+static int pair_cmp(const void *unused_cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *unused_keydata)
{
+ const struct pair_entry *a = entry;
+ const struct pair_entry *b = entry_or_key;
+
return strcmp(a->path, b->path);
}
@@ -173,8 +184,14 @@ struct path_entry {
char path[FLEX_ARRAY];
};
-static int path_entry_cmp(struct path_entry *a, struct path_entry *b, void *key)
+static int path_entry_cmp(const void *unused_cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *key)
{
+ const struct path_entry *a = entry;
+ const struct path_entry *b = entry_or_key;
+
return strcmp(a->path, key ? key : b->path);
}
@@ -365,10 +382,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
rdir_len = rdir.len;
wtdir_len = wtdir.len;
- hashmap_init(&working_tree_dups,
- (hashmap_cmp_fn)working_tree_entry_cmp, 0);
- hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, 0);
- hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, 0);
+ hashmap_init(&working_tree_dups, working_tree_entry_cmp, NULL, 0);
+ hashmap_init(&submodules, pair_cmp, NULL, 0);
+ hashmap_init(&symlinks2, pair_cmp, NULL, 0);
child.no_stdin = 1;
child.git_cmd = 1;
@@ -578,10 +594,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* in the common case of --symlinks and the difftool updating
* files through the symlink.
*/
- hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp,
- wtindex.cache_nr);
- hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp,
- wtindex.cache_nr);
+ hashmap_init(&wt_modified, path_entry_cmp, NULL, wtindex.cache_nr);
+ hashmap_init(&tmp_modified, path_entry_cmp, NULL, wtindex.cache_nr);
for (i = 0; i < wtindex.cache_nr; i++) {
struct hashmap_entry dummy;
@@ -602,7 +616,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
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);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 64617ad8e3..27b2cc138e 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -5,6 +5,7 @@
*/
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "commit.h"
#include "object.h"
@@ -92,8 +93,9 @@ struct anonymized_entry {
size_t anon_len;
};
-static int anonymized_entry_cmp(const void *va, const void *vb,
- const void *data)
+static int anonymized_entry_cmp(const void *unused_cmp_data,
+ const void *va, const void *vb,
+ const void *unused_keydata)
{
const struct anonymized_entry *a = va, *b = vb;
return a->orig_len != b->orig_len ||
@@ -112,7 +114,7 @@ static const void *anonymize_mem(struct hashmap *map,
struct anonymized_entry key, *ret;
if (!map->cmpfn)
- hashmap_init(map, anonymized_entry_cmp, 0);
+ hashmap_init(map, anonymized_entry_cmp, NULL, 0);
hashmap_entry_init(&key, memhash(orig, *len));
key.orig = orig;
@@ -232,15 +234,15 @@ static void export_blob(const struct object_id *oid)
if (anonymize) {
buf = anonymize_blob(&size);
- object = (struct object *)lookup_blob(oid->hash);
+ object = (struct object *)lookup_blob(oid);
eaten = 0;
} else {
buf = read_sha1_file(oid->hash, &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_sha1_signature(oid->hash, buf, size, type_name(type)) < 0)
die("sha1 mismatch in blob %s", oid_to_hex(oid));
- object = parse_object_buffer(oid->hash, type, size, buf, &eaten);
+ object = parse_object_buffer(oid, type, size, buf, &eaten);
}
if (!object)
@@ -342,6 +344,7 @@ static void show_filemodify(struct diff_queue_struct *q,
struct diff_options *options, void *data)
{
int i;
+ struct string_list *changed = data;
/*
* Handle files below a directory first, in case they are all deleted
@@ -357,20 +360,31 @@ static void show_filemodify(struct diff_queue_struct *q,
case DIFF_STATUS_DELETED:
printf("D ");
print_path(spec->path);
+ string_list_insert(changed, spec->path);
putchar('\n');
break;
case DIFF_STATUS_COPIED:
case DIFF_STATUS_RENAMED:
- printf("%c ", q->queue[i]->status);
- print_path(ospec->path);
- putchar(' ');
- print_path(spec->path);
- putchar('\n');
-
- if (!oidcmp(&ospec->oid, &spec->oid) &&
- ospec->mode == spec->mode)
- break;
+ /*
+ * If a change in the file corresponding to ospec->path
+ * has been observed, we cannot trust its contents
+ * because the diff is calculated based on the prior
+ * contents, not the current contents. So, declare a
+ * copy or rename only if there was no change observed.
+ */
+ if (!string_list_has_string(changed, ospec->path)) {
+ printf("%c ", q->queue[i]->status);
+ print_path(ospec->path);
+ putchar(' ');
+ print_path(spec->path);
+ string_list_insert(changed, spec->path);
+ putchar('\n');
+
+ if (!oidcmp(&ospec->oid, &spec->oid) &&
+ ospec->mode == spec->mode)
+ break;
+ }
/* fallthrough */
case DIFF_STATUS_TYPE_CHANGED:
@@ -391,6 +405,7 @@ static void show_filemodify(struct diff_queue_struct *q,
get_object_mark(object));
}
print_path(spec->path);
+ string_list_insert(changed, spec->path);
putchar('\n');
break;
@@ -526,7 +541,8 @@ static void anonymize_ident_line(const char **beg, const char **end)
*end = out->buf + out->len;
}
-static void handle_commit(struct commit *commit, struct rev_info *rev)
+static void handle_commit(struct commit *commit, struct rev_info *rev,
+ struct string_list *paths_of_changed_objects)
{
int saved_output_format = rev->diffopt.output_format;
const char *commit_buffer;
@@ -562,12 +578,12 @@ 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_sha1(commit->parents->item->tree->object.oid.hash,
- commit->tree->object.oid.hash, "", &rev->diffopt);
+ diff_tree_oid(&commit->parents->item->tree->object.oid,
+ &commit->tree->object.oid, "", &rev->diffopt);
}
else
- diff_root_tree_sha1(commit->tree->object.oid.hash,
- "", &rev->diffopt);
+ diff_root_tree_oid(&commit->tree->object.oid,
+ "", &rev->diffopt);
/* Export the referenced blobs, and remember the marks. */
for (i = 0; i < diff_queued_diff.nr; i++)
@@ -613,6 +629,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
if (full_tree)
printf("deleteall\n");
log_tree_diff_flush(rev);
+ string_list_clear(paths_of_changed_objects, 0);
rev->diffopt.output_format = saved_output_format;
printf("\n");
@@ -628,15 +645,15 @@ static void *anonymize_tag(const void *old, size_t *len)
return strbuf_detach(&out, len);
}
-static void handle_tail(struct object_array *commits, struct rev_info *revs)
+static void handle_tail(struct object_array *commits, struct rev_info *revs,
+ struct string_list *paths_of_changed_objects)
{
struct commit *commit;
while (commits->nr) {
- commit = (struct commit *)commits->objects[commits->nr - 1].item;
+ commit = (struct commit *)object_array_pop(commits);
if (has_unshown_parent(commit))
return;
- handle_commit(commit, revs);
- commits->nr--;
+ handle_commit(commit, revs, paths_of_changed_objects);
}
}
@@ -740,7 +757,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 (;;) {
@@ -779,7 +796,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
/* handle nested tags */
while (tag && tag->object.type == OBJ_TAG) {
- parse_object(tag->object.oid.hash);
+ parse_object(&tag->object.oid);
string_list_append(&extra_refs, full_name)->util = tag;
tag = (struct tag *)tag->tagged;
}
@@ -806,7 +823,7 @@ 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) {
@@ -822,7 +839,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;
}
@@ -834,7 +851,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;
}
@@ -878,7 +895,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;
@@ -907,9 +924,7 @@ static void export_marks(char *file)
static void import_marks(char *input_file)
{
char line[512];
- FILE *f = fopen(input_file, "r");
- if (!f)
- die_errno("cannot read '%s'", input_file);
+ FILE *f = xfopen(input_file, "r");
while (fgets(line, sizeof(line), f)) {
uint32_t mark;
@@ -940,7 +955,7 @@ static void import_marks(char *input_file)
/* only commits */
continue;
- commit = lookup_commit(oid.hash);
+ commit = lookup_commit(&oid);
if (!commit)
die("not a commit? can't happen: %s", oid_to_hex(&oid));
@@ -977,6 +992,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
char *export_filename = NULL, *import_filename = NULL;
uint32_t lastimportid;
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
+ struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
struct option options[] = {
OPT_INTEGER(0, "progress", &progress,
N_("show progress after <n> objects")),
@@ -1049,14 +1065,15 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
- DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+ revs.diffopt.format_callback_data = &paths_of_changed_objects;
+ revs.diffopt.flags.recursive = 1;
while ((commit = get_revision(&revs))) {
if (has_unshown_parent(commit)) {
add_object_array(&commit->object, NULL, &commits);
}
else {
- handle_commit(commit, &revs);
- handle_tail(&commits, &revs);
+ handle_commit(commit, &revs, &paths_of_changed_objects);
+ handle_tail(&commits, &revs, &paths_of_changed_objects);
}
}
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 366b9d13f9..a7bc1366ab 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -53,6 +53,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
struct oid_array shallow = OID_ARRAY_INIT;
struct string_list deepen_not = STRING_LIST_INIT_DUP;
+ fetch_if_missing = 0;
+
packet_trace_identity("fetch-pack");
memset(&args, 0, sizeof(args));
@@ -143,6 +145,22 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.update_shallow = 1;
continue;
}
+ if (!strcmp("--from-promisor", arg)) {
+ args.from_promisor = 1;
+ continue;
+ }
+ if (!strcmp("--no-dependents", arg)) {
+ args.no_dependents = 1;
+ continue;
+ }
+ if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
+ parse_list_objects_filter(&args.filter_options, arg);
+ continue;
+ }
+ if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
+ list_objects_filter_set_no_filter(&args.filter_options);
+ continue;
+ }
usage(fetch_pack_usage);
}
if (deepen_not.nr)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5f2c2ab23e..6d73656a48 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -2,6 +2,8 @@
* "git fetch"
*/
#include "cache.h"
+#include "config.h"
+#include "repository.h"
#include "refs.h"
#include "commit.h"
#include "builtin.h"
@@ -16,6 +18,8 @@
#include "connected.h"
#include "argv-array.h"
#include "utf8.h"
+#include "packfile.h"
+#include "list-objects-filter-options.h"
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -35,10 +39,14 @@ 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, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int progress = -1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_children = -1;
+static int max_children = 1;
static enum transport_family family;
static const char *depth;
static const char *deepen_since;
@@ -48,24 +56,12 @@ static struct strbuf default_rla = STRBUF_INIT;
static struct transport *gtransport;
static struct transport *gsecondary;
static const char *submodule_prefix = "";
-static const char *recurse_submodules_default;
+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 int option_parse_recurse_submodules(const struct option *opt,
- const char *arg, int unset)
-{
- if (unset) {
- recurse_submodules = RECURSE_SUBMODULES_OFF;
- } else {
- if (arg)
- recurse_submodules = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
- else
- recurse_submodules = RECURSE_SUBMODULES_ON;
- }
- return 0;
-}
+static struct list_objects_filter_options filter_options;
static int git_fetch_config(const char *k, const char *v, void *cb)
{
@@ -73,9 +69,42 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
fetch_prune_config = git_config_bool(k, v);
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;
+ recurse_submodules = r;
+ }
+
+ if (!strcmp(k, "submodule.fetchjobs")) {
+ max_children = parse_submodule_fetchjobs(k, v);
+ return 0;
+ } else if (!strcmp(k, "fetch.recursesubmodules")) {
+ recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+ return 0;
+ }
+
return git_default_config(k, v, cb);
}
+static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "submodule.fetchjobs")) {
+ max_children = parse_submodule_fetchjobs(var, value);
+ return 0;
+ } else if (!strcmp(var, "fetch.recursesubmodules")) {
+ recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
+ return 0;
+ }
+
+ return 0;
+}
+
static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
{
ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
@@ -97,7 +126,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,
@@ -108,9 +137,11 @@ 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")),
- { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
+ 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_parse_recurse_submodules },
+ PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
OPT_BOOL(0, "dry-run", &dry_run,
N_("dry run")),
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
@@ -130,9 +161,11 @@ static struct option builtin_fetch_options[] = {
PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
- { OPTION_STRING, 0, "recurse-submodules-default",
- &recurse_submodules_default, NULL,
- N_("default mode for recursion"), PARSE_OPT_HIDDEN },
+ { OPTION_CALLBACK, 0, "recurse-submodules-default",
+ &recurse_submodules_default, N_("on-demand"),
+ N_("default for recursive fetching of submodules "
+ "(lower priority than config files)"),
+ PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules },
OPT_BOOL(0, "update-shallow", &update_shallow,
N_("accept refs that update .git/shallow")),
{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
@@ -141,6 +174,7 @@ static struct option builtin_fetch_options[] = {
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
TRANSPORT_FAMILY_IPV6),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
@@ -242,9 +276,11 @@ static void find_non_local_tags(struct transport *transport,
*/
if (ends_with(ref->name, "^{}")) {
if (item &&
- !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) &&
+ !has_object_file_with_flags(&ref->old_oid,
+ OBJECT_INFO_QUICK) &&
!will_fetch(head, ref->old_oid.hash) &&
- !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
+ !has_sha1_file_with_flags(item->util,
+ OBJECT_INFO_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
item = NULL;
@@ -258,7 +294,7 @@ static void find_non_local_tags(struct transport *transport,
* fetch.
*/
if (item &&
- !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
+ !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
@@ -279,7 +315,7 @@ static void find_non_local_tags(struct transport *transport,
* checked to see if it needs fetching.
*/
if (item &&
- !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
+ !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
@@ -436,8 +472,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;
@@ -636,8 +672,8 @@ static int update_local_ref(struct ref *ref,
return r;
}
- current = lookup_commit_reference_gently(ref->old_oid.hash, 1);
- updated = lookup_commit_reference_gently(ref->new_oid.hash, 1);
+ current = lookup_commit_reference_gently(&ref->old_oid, 1);
+ updated = lookup_commit_reference_gently(&ref->new_oid, 1);
if (!current || !updated) {
const char *msg;
const char *what;
@@ -706,7 +742,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;
@@ -716,7 +752,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;
}
@@ -770,7 +806,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
}
- commit = lookup_commit_reference_gently(rm->old_oid.hash, 1);
+ commit = lookup_commit_reference_gently(&rm->old_oid,
+ 1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -940,7 +977,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs(&refnames, 0);
+ result = delete_refs("fetch: prune", &refnames, 0);
string_list_clear(&refnames, 0);
}
@@ -1022,6 +1059,11 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
if (update_shallow)
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
+ if (filter_options.choice) {
+ set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+ filter_options.filter_spec);
+ set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+ }
return transport;
}
@@ -1072,9 +1114,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();
@@ -1192,6 +1231,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)
@@ -1245,12 +1286,65 @@ static int fetch_multiple(struct string_list *list)
return result;
}
-static int fetch_one(struct remote *remote, int argc, const char **argv)
+/*
+ * Fetching from the promisor remote should use the given filter-spec
+ * or inherit the default filter-spec from the config.
+ */
+static inline void fetch_one_setup_partial(struct remote *remote)
+{
+ /*
+ * Explicit --no-filter argument overrides everything, regardless
+ * of any prior partial clones and fetches.
+ */
+ if (filter_options.no_filter)
+ return;
+
+ /*
+ * If no prior partial clone/fetch and the current fetch DID NOT
+ * request a partial-fetch, do a normal fetch.
+ */
+ if (!repository_format_partial_clone && !filter_options.choice)
+ return;
+
+ /*
+ * If this is the FIRST partial-fetch request, we enable partial
+ * on this repo and remember the given filter-spec as the default
+ * for subsequent fetches to this remote.
+ */
+ if (!repository_format_partial_clone && filter_options.choice) {
+ partial_clone_register(remote->name, &filter_options);
+ return;
+ }
+
+ /*
+ * We are currently limited to only ONE promisor remote and only
+ * allow partial-fetches from the promisor remote.
+ */
+ if (strcmp(remote->name, repository_format_partial_clone)) {
+ if (filter_options.choice)
+ die(_("--filter can only be used with the remote configured in core.partialClone"));
+ return;
+ }
+
+ /*
+ * Do a partial-fetch from the promisor remote using either the
+ * explicitly given filter-spec or inherit the filter-spec from
+ * the config.
+ */
+ if (!filter_options.choice)
+ partial_clone_get_default_filter_spec(&filter_options);
+ return;
+}
+
+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;
+ int j = 0;
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"
@@ -1260,18 +1354,39 @@ 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 (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)
+ add_prune_tags_to_fetch_refspec(remote);
+
+ if (argc > 0 || (maybe_prune_tags && !remote_via_config)) {
+ size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);
+ refs = xcalloc(nr_alloc, sizeof(const char *));
+ if (maybe_prune_tags) {
+ refs[j++] = xstrdup("refs/tags/*:refs/tags/*");
+ ref_nr++;
+ }
+ }
+
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++;
@@ -1281,9 +1396,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
argv[i], argv[i]);
} else
refs[j++] = argv[i];
+ ref_nr++;
}
- refs[j] = NULL;
- ref_nr = j;
}
sigchain_push_common(unlock_pack_on_signal);
@@ -1300,17 +1414,21 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
struct string_list list = STRING_LIST_INIT_DUP;
- struct remote *remote;
+ 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");
+ fetch_if_missing = 0;
+
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
+ config_from_gitmodules(gitmodules_fetch_config, NULL);
git_config(git_fetch_config, NULL);
argc = parse_options(argc, argv, prefix,
@@ -1338,14 +1456,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth || deepen_since || deepen_not.nr)
deepen = 1;
- if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
- if (recurse_submodules_default) {
- int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
- set_config_fetch_recurse_submodules(arg);
- }
- gitmodules_config();
- git_config(submodule_config, NULL);
- }
+ if (filter_options.choice && !repository_format_partial_clone)
+ die("--filter can only be used when extensions.partialClone is set");
if (all) {
if (argc == 1)
@@ -1353,17 +1465,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
else if (argc > 1)
die(_("fetch --all does not make sense with refspecs"));
(void) for_each_remote(get_one_remote_for_fetch, &list);
- result = fetch_multiple(&list);
} else if (argc == 0) {
/* No arguments -- use default remote */
remote = remote_get(NULL);
- result = fetch_one(remote, argc, argv);
} else if (multiple) {
/* All arguments are assumed to be remotes or groups */
for (i = 0; i < argc; i++)
if (!add_remote_or_group(argv[i], &list))
die(_("No such remote or remote group: %s"), argv[i]);
- result = fetch_multiple(&list);
} else {
/* Single remote or group */
(void) add_remote_or_group(argv[0], &list);
@@ -1371,21 +1480,35 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
/* More than one remote */
if (argc > 1)
die(_("Fetching a group and specifying refspecs does not make sense"));
- result = fetch_multiple(&list);
} else {
/* Zero or one remotes */
remote = remote_get(argv[0]);
- result = fetch_one(remote, argc-1, argv+1);
+ prune_tags_ok = (argc == 1);
+ argc--;
+ argv++;
}
}
+ if (remote) {
+ if (filter_options.choice || repository_format_partial_clone)
+ fetch_one_setup_partial(remote);
+ 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"));
+ /* TODO should this also die if we have a previous partial-clone? */
+ result = fetch_multiple(&list);
+ }
+
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
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,
verbosity < 0,
max_children);
argv_array_clear(&options);
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 6faa3c0d24..8e8a15ea4a 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "commit.h"
#include "diff.h"
@@ -341,7 +342,7 @@ static void shortlog(const char *name,
const struct object_id *oid = &origin_data->oid;
int limit = opts->shortlog_len;
- branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ);
+ branch = deref_tag(parse_object(oid), oid_to_hex(oid), GIT_SHA1_HEXSZ);
if (!branch || branch->type != OBJ_COMMIT)
return;
@@ -376,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)
@@ -407,7 +409,8 @@ static void shortlog(const char *name,
}
static void fmt_merge_msg_title(struct strbuf *out,
- const char *current_branch) {
+ const char *current_branch)
+{
int i = 0;
char *sep = "";
@@ -559,17 +562,17 @@ static void find_merge_parents(struct merge_parents *result,
* "name" here and we do not want to contaminate its
* util field yet.
*/
- obj = parse_object(oid.hash);
+ obj = parse_object(&oid);
parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
if (!parent)
continue;
commit_list_insert(parent, &parents);
add_merge_parent(result, &obj->oid, &parent->object.oid);
}
- head_commit = lookup_commit(head->hash);
+ 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);
@@ -601,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/"))
@@ -633,7 +636,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct commit *head;
struct rev_info rev;
- head = lookup_commit_or_die(head_oid.hash, "HEAD");
+ head = lookup_commit_or_die(&head_oid, "HEAD");
init_revisions(&rev, NULL);
rev.commit_format = CMIT_FMT_ONELINE;
rev.ignore_merges = 1;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index eca365bf89..e931be9ce4 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "object.h"
#include "parse-options.h"
@@ -16,25 +17,26 @@ static char const * const for_each_ref_usage[] = {
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
int i;
- const char *format = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- int maxcount = 0, quote_style = 0, icase = 0;
+ int maxcount = 0, icase = 0;
struct ref_array array;
struct ref_filter filter;
+ struct ref_format format = REF_FORMAT_INIT;
struct option opts[] = {
- OPT_BIT('s', "shell", &quote_style,
+ OPT_BIT('s', "shell", &format.quote_style,
N_("quote placeholders suitably for shells"), QUOTE_SHELL),
- OPT_BIT('p', "perl", &quote_style,
+ OPT_BIT('p', "perl", &format.quote_style,
N_("quote placeholders suitably for perl"), QUOTE_PERL),
- OPT_BIT(0 , "python", &quote_style,
+ OPT_BIT(0 , "python", &format.quote_style,
N_("quote placeholders suitably for python"), QUOTE_PYTHON),
- OPT_BIT(0 , "tcl", &quote_style,
+ OPT_BIT(0 , "tcl", &format.quote_style,
N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
OPT_GROUP(""),
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
- OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
+ OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
+ OPT__COLOR(&format.use_color, N_("respect format colors")),
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
N_("field name to sort on"), &parse_opt_ref_sorting),
OPT_CALLBACK(0, "points-at", &filter.points_at,
@@ -51,16 +53,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
memset(&array, 0, sizeof(array));
memset(&filter, 0, sizeof(filter));
+ format.format = "%(objectname) %(objecttype)\t%(refname)";
+
+ git_config(git_default_config, NULL);
+
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
if (maxcount < 0) {
error("invalid --count argument: `%d'", maxcount);
usage_with_options(for_each_ref_usage, opts);
}
- if (HAS_MULTI_BITS(quote_style)) {
+ if (HAS_MULTI_BITS(format.quote_style)) {
error("more than one quoting style?");
usage_with_options(for_each_ref_usage, opts);
}
- if (verify_ref_format(format))
+ if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts);
if (!sorting)
@@ -68,9 +74,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
sorting->ignore_case = icase;
filter.ignore_case = icase;
- /* for warn_ambiguous_refs */
- git_config(git_default_config, NULL);
-
filter.name_patterns = argv;
filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
@@ -79,7 +82,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (!maxcount || array.nr < maxcount)
maxcount = array.nr;
for (i = 0; i < maxcount; i++)
- show_ref_array_item(array.items[i], format, quote_style);
+ show_ref_array_item(array.items[i], &format);
ref_array_clear(&array);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b5e13a4556..ef78c6c00c 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
@@ -14,10 +15,13 @@
#include "progress.h"
#include "streaming.h"
#include "decorate.h"
+#include "packfile.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
#define HAS_OBJ 0x0004
+/* This flag is set if something points to this object. */
+#define USED 0x0008
static int show_root;
static int show_tags;
@@ -66,7 +70,7 @@ static const char *printable_type(struct object *obj)
object_as_type(obj, type, 0);
}
- ret = typename(obj->type);
+ ret = type_name(obj->type);
if (!ret)
ret = "unknown";
@@ -133,7 +137,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;
}
@@ -145,6 +149,15 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (obj->flags & REACHABLE)
return 0;
obj->flags |= REACHABLE;
+
+ if (is_promisor_object(&obj->oid))
+ /*
+ * Further recursion does not need to be performed on this
+ * object since it is a promisor object (so it does not need to
+ * be added to "pending").
+ */
+ return 0;
+
if (!(obj->flags & HAS_OBJ)) {
if (parent && !has_object_file(&obj->oid)) {
printf("broken link from %7s %s\n",
@@ -167,17 +180,12 @@ static void mark_object_reachable(struct object *obj)
static int traverse_one_object(struct object *obj)
{
- int result;
- struct tree *tree = NULL;
+ int result = fsck_walk(obj, obj, &fsck_walk_options);
if (obj->type == OBJ_TREE) {
- tree = (struct tree *)obj;
- if (parse_tree(tree) < 0)
- return 1; /* error already displayed */
- }
- result = fsck_walk(obj, obj, &fsck_walk_options);
- if (tree)
+ struct tree *tree = (struct tree *)obj;
free_tree_buffer(tree);
+ }
return result;
}
@@ -187,14 +195,9 @@ static int traverse_reachable(void)
unsigned int nr = 0;
int result = 0;
if (show_progress)
- progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
+ progress = start_delayed_progress(_("Checking connectivity"), 0);
while (pending.nr) {
- struct object_array_entry *entry;
- struct object *obj;
-
- entry = pending.objects + --pending.nr;
- obj = entry->item;
- result |= traverse_one_object(obj);
+ result |= traverse_one_object(object_array_pop(&pending));
display_progress(progress, ++nr);
}
stop_progress(&progress);
@@ -205,7 +208,7 @@ static int mark_used(struct object *obj, int type, void *data, struct fsck_optio
{
if (!obj)
return 1;
- obj->used = 1;
+ obj->flags |= USED;
return 0;
}
@@ -220,6 +223,8 @@ static void check_reachable_object(struct object *obj)
* do a full fsck
*/
if (!(obj->flags & HAS_OBJ)) {
+ if (is_promisor_object(&obj->oid))
+ return;
if (has_sha1_pack(obj->oid.hash))
return; /* it is in pack - forget about it */
printf("missing %s %s\n", printable_type(obj),
@@ -254,7 +259,7 @@ static void check_unreachable_object(struct object *obj)
}
/*
- * "!used" means that nothing at all points to it, including
+ * "!USED" means that nothing at all points to it, including
* other unreachable objects. In other words, it's the "tip"
* of some set of unreachable objects, usually a commit that
* got dropped.
@@ -265,7 +270,7 @@ static void check_unreachable_object(struct object *obj)
* deleted a branch by mistake, this is a prime candidate to
* start looking at, for example.
*/
- if (!obj->used) {
+ if (!(obj->flags & USED)) {
if (show_dangling)
printf("dangling %s %s\n", printable_type(obj),
describe_object(obj));
@@ -280,8 +285,7 @@ static void check_unreachable_object(struct object *obj)
free(filename);
return;
}
- if (!(f = fopen(filename, "w")))
- die_errno("Could not open '%s'", filename);
+ f = xfopen(filename, "w");
if (obj->type == OBJ_BLOB) {
if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
die_errno("Could not write '%s'", filename);
@@ -335,6 +339,8 @@ static void check_connectivity(void)
static int fsck_obj(struct object *obj)
{
+ int err;
+
if (obj->flags & SEEN)
return 0;
obj->flags |= SEEN;
@@ -345,20 +351,13 @@ static int fsck_obj(struct object *obj)
if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, "broken links");
- if (fsck_object(obj, NULL, 0, &fsck_obj_options))
- return -1;
-
- if (obj->type == OBJ_TREE) {
- struct tree *item = (struct tree *) obj;
-
- free_tree_buffer(item);
- }
+ err = fsck_object(obj, NULL, 0, &fsck_obj_options);
+ if (err)
+ goto out;
if (obj->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *) obj;
- free_commit_buffer(commit);
-
if (!commit->parents && show_root)
printf("root %s\n", describe_object(&commit->object));
}
@@ -374,10 +373,15 @@ static int fsck_obj(struct object *obj)
}
}
- return 0;
+out:
+ if (obj->type == OBJ_TREE)
+ free_tree_buffer((struct tree *)obj);
+ if (obj->type == OBJ_COMMIT)
+ free_commit_buffer((struct commit *)obj);
+ return err;
}
-static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
+static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
unsigned long size, void *buffer, int *eaten)
{
/*
@@ -385,19 +389,20 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
* verify_packfile(), data_valid variable for details.
*/
struct object *obj;
- obj = parse_object_buffer(sha1, type, size, buffer, eaten);
+ obj = parse_object_buffer(oid, type, size, buffer, eaten);
if (!obj) {
errors_found |= ERROR_OBJECT;
- return error("%s: object corrupt or missing", sha1_to_hex(sha1));
+ return error("%s: object corrupt or missing", oid_to_hex(oid));
}
- obj->flags = HAS_OBJ;
+ obj->flags &= ~(REACHABLE | SEEN);
+ obj->flags |= HAS_OBJ;
return fsck_obj(obj);
}
static int default_refs;
static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
- unsigned long timestamp)
+ timestamp_t timestamp)
{
struct object *obj;
@@ -407,10 +412,10 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
if (timestamp && name_objects)
add_decoration(fsck_walk_options.object_names,
obj,
- xstrfmt("%s@{%ld}", refname, timestamp));
- obj->used = 1;
+ xstrfmt("%s@{%"PRItime"}", refname, timestamp));
+ obj->flags |= USED;
mark_object_reachable(obj);
- } else {
+ } else if (!is_promisor_object(oid)) {
error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
errors_found |= ERROR_REACHABLE;
}
@@ -418,7 +423,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
}
static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
const char *refname = cb_data;
@@ -444,8 +449,16 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
{
struct object *obj;
- obj = parse_object(oid->hash);
+ obj = parse_object(oid);
if (!obj) {
+ if (is_promisor_object(oid)) {
+ /*
+ * Increment default_refs anyway, because this is a
+ * valid ref.
+ */
+ default_refs++;
+ return 0;
+ }
error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
@@ -456,7 +469,7 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
errors_found |= ERROR_REFS;
}
default_refs++;
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(refname));
@@ -506,7 +519,7 @@ static struct object *parse_loose_object(const struct object_id *oid,
if (!contents && type != OBJ_BLOB)
die("BUG: read_loose_object streamed a non-blob");
- obj = parse_object_buffer(oid->hash, type, size, contents, &eaten);
+ obj = parse_object_buffer(oid, type, size, contents, &eaten);
if (!eaten)
free(contents);
@@ -524,7 +537,8 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
return 0; /* keep checking other objects */
}
- obj->flags = HAS_OBJ;
+ obj->flags &= ~(REACHABLE | SEEN);
+ obj->flags |= HAS_OBJ;
if (fsck_obj(obj))
errors_found |= ERROR_OBJECT;
return 0;
@@ -537,7 +551,7 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
return 0;
}
-static int fsck_subdir(int nr, const char *path, void *progress)
+static int fsck_subdir(unsigned int nr, const char *path, void *progress)
{
display_progress(progress, nr + 1);
return 0;
@@ -566,7 +580,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");
@@ -599,14 +613,14 @@ static int fsck_cache_tree(struct cache_tree *it)
fprintf(stderr, "Checking cache tree\n");
if (0 <= it->entry_count) {
- struct object *obj = parse_object(it->sha1);
+ struct object *obj = parse_object(&it->oid);
if (!obj) {
error("%s: invalid sha1 pointer in cache-tree",
- sha1_to_hex(it->sha1));
+ oid_to_hex(&it->oid));
errors_found |= ERROR_REFS;
return 1;
}
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(":"));
@@ -667,9 +681,12 @@ static struct option fsck_opts[] = {
int cmd_fsck(int argc, const char **argv, const char *prefix)
{
- int i, heads;
+ int i;
struct alternate_object_database *alt;
+ /* fsck knows how to handle missing promisor objects */
+ fetch_if_missing = 0;
+
errors_found = 0;
check_replace_refs = 0;
@@ -735,25 +752,25 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
}
- heads = 0;
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- unsigned char sha1[20];
- if (!get_sha1(arg, sha1)) {
- struct object *obj = lookup_object(sha1);
+ struct object_id oid;
+ if (!get_oid(arg, &oid)) {
+ struct object *obj = lookup_object(oid.hash);
if (!obj || !(obj->flags & HAS_OBJ)) {
- error("%s: object missing", sha1_to_hex(sha1));
+ if (is_promisor_object(&oid))
+ continue;
+ error("%s: object missing", oid_to_hex(&oid));
errors_found |= ERROR_OBJECT;
continue;
}
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj, xstrdup(arg));
mark_object_reachable(obj);
- heads++;
continue;
}
error("invalid parameter: expected sha1, got '%s'", arg);
@@ -772,6 +789,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;
@@ -781,11 +799,11 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
mode = active_cache[i]->ce_mode;
if (S_ISGITLINK(mode))
continue;
- blob = lookup_blob(active_cache[i]->oid.hash);
+ blob = lookup_blob(&active_cache[i]->oid);
if (!blob)
continue;
obj = &blob->object;
- obj->used = 1;
+ obj->flags |= USED;
if (name_objects)
add_decoration(fsck_walk_options.object_names,
obj,
diff --git a/builtin/gc.c b/builtin/gc.c
index 2d2027d8b0..f51e5a6500 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,6 +11,7 @@
*/
#include "builtin.h"
+#include "config.h"
#include "tempfile.h"
#include "lockfile.h"
#include "parse-options.h"
@@ -18,6 +19,7 @@
#include "sigchain.h"
#include "argv-array.h"
#include "commit.h"
+#include "packfile.h"
#define FAILED_RUN "failed to run %s"
@@ -33,7 +35,7 @@ static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+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";
@@ -45,7 +47,7 @@ static struct argv_array prune = ARGV_ARRAY_INIT;
static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
static struct argv_array rerere = ARGV_ARRAY_INIT;
-static struct tempfile pidfile;
+static struct tempfile *pidfile;
static struct lock_file log_lock;
static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
@@ -76,7 +78,7 @@ static void process_log_file(void)
*/
int saved_errno = errno;
fprintf(stderr, _("Failed to fstat %s: %s"),
- get_tempfile_path(&log_lock.tempfile),
+ get_tempfile_path(log_lock.tempfile),
strerror(saved_errno));
fflush(stderr);
commit_lock_file(&log_lock);
@@ -148,7 +150,7 @@ static int too_many_loose_objects(void)
if (!dir)
return 0;
- auto_threshold = (gc_auto_threshold + 255) / 256;
+ auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256);
while ((ent = readdir(dir)) != NULL) {
if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
ent->d_name[38] != '\0')
@@ -240,7 +242,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
int fd;
char *pidfile_path;
- if (is_tempfile_active(&pidfile))
+ if (is_tempfile_active(pidfile))
/* already locked */
return NULL;
@@ -256,7 +258,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
int should_exit;
if (!scan_fmt)
- scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
+ scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX);
fp = fopen(pidfile_path, "r");
memset(locking_host, 0, sizeof(locking_host));
should_exit =
@@ -291,7 +293,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
commit_lock_file(&lock);
- register_tempfile(&pidfile, pidfile_path);
+ pidfile = register_tempfile(pidfile_path);
free(pidfile_path);
return NULL;
}
@@ -358,8 +360,11 @@ 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_END()
};
@@ -456,6 +461,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_push(&prune, prune_expire);
if (quiet)
argv_array_push(&prune, "--no-progress");
+ if (repository_format_partial_clone)
+ argv_array_push(&prune,
+ "--exclude-promisor-objects");
if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
return error(FAILED_RUN, prune.argv[0]);
}
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index e21c5416cd..2706fcfaf2 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -26,15 +26,16 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
usage(builtin_get_tar_commit_id_usage);
n = read_in_full(0, buffer, HEADERSIZE);
- if (n < HEADERSIZE)
- die("git get-tar-commit-id: read error");
+ if (n < 0)
+ die_errno("git get-tar-commit-id: read error");
+ if (n != HEADERSIZE)
+ die_errno("git get-tar-commit-id: EOF before reading tar header");
if (header->typeflag[0] != 'g')
return 1;
if (!skip_prefix(content, "52 comment=", &comment))
return 1;
- n = write_in_full(1, comment, 41);
- if (n < 41)
+ if (write_in_full(1, comment, 41) < 0)
die_errno("git get-tar-commit-id: write error");
return 0;
diff --git a/builtin/grep.c b/builtin/grep.c
index 254c1c7849..789a89133a 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -4,6 +4,8 @@
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
+#include "repository.h"
+#include "config.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
@@ -26,13 +28,7 @@ static char const * const grep_usage[] = {
NULL
};
-static const char *super_prefix;
static int recurse_submodules;
-static struct argv_array submodule_options = ARGV_ARRAY_INIT;
-static const char *parent_basename;
-
-static int grep_submodule_launch(struct grep_opt *opt,
- const struct grep_source *gs);
#define GREP_NUM_THREADS_DEFAULT 8
static int num_threads;
@@ -73,14 +69,14 @@ static pthread_mutex_t grep_mutex;
static inline void grep_lock(void)
{
- if (num_threads)
- pthread_mutex_lock(&grep_mutex);
+ assert(num_threads);
+ pthread_mutex_lock(&grep_mutex);
}
static inline void grep_unlock(void)
{
- if (num_threads)
- pthread_mutex_unlock(&grep_mutex);
+ assert(num_threads);
+ pthread_mutex_unlock(&grep_mutex);
}
/* Signalled when a new work_item is added to todo. */
@@ -96,8 +92,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();
@@ -105,7 +100,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;
@@ -184,10 +179,7 @@ static void *run(void *arg)
break;
opt->output_priv = w;
- if (w->source.type == GREP_SOURCE_SUBMODULE)
- hit |= grep_submodule_launch(opt, &w->source);
- else
- hit |= grep_source(opt, &w->source);
+ hit |= grep_source(opt, &w->source);
grep_source_clear_data(&w->source);
work_done(w);
}
@@ -224,7 +216,8 @@ static void start_threads(struct grep_opt *opt)
int err;
struct grep_opt *o = grep_opt_dup(opt);
o->output = strbuf_out;
- o->debug = 0;
+ if (i)
+ o->debug = 0;
compile_grep_patterns(o);
err = pthread_create(&threads[i], NULL, run, o);
@@ -289,8 +282,22 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)
if (num_threads < 0)
die(_("invalid number of threads specified (%d) for %s"),
num_threads, var);
+#ifdef NO_PTHREADS
+ else if (num_threads && num_threads != 1) {
+ /*
+ * TRANSLATORS: %s is the configuration
+ * variable for tweaking threads, currently
+ * grep.threads
+ */
+ warning(_("no threads support, ignoring %s"), var);
+ num_threads = 0;
+ }
+#endif
}
+ if (!strcmp(var, "submodule.recurse"))
+ recurse_submodules = git_config_bool(var, value);
+
return st;
}
@@ -309,35 +316,31 @@ 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 (super_prefix) {
- strbuf_add(&pathbuf, filename, tree_name_len);
- strbuf_addstr(&pathbuf, super_prefix);
- strbuf_addstr(&pathbuf, filename + tree_name_len);
+ if (opt->relative && opt->prefix_length) {
+ quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
+ strbuf_insert(&pathbuf, 0, filename, tree_name_len);
} else {
strbuf_addstr(&pathbuf, filename);
}
- if (opt->relative && opt->prefix_length) {
- char *name = strbuf_detach(&pathbuf, NULL);
- quote_path_relative(name + tree_name_len, opt->prefix, &pathbuf);
- strbuf_insert(&pathbuf, 0, name, tree_name_len);
- free(name);
- }
+ 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_SHA1, 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_SHA1, pathbuf.buf, path, oid);
- strbuf_release(&pathbuf);
hit = grep_source(opt, &gs);
grep_source_clear(&gs);
@@ -348,30 +351,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 (super_prefix)
- strbuf_addstr(&buf, super_prefix);
- strbuf_addstr(&buf, filename);
+ if (opt->relative && opt->prefix_length)
+ quote_path_relative(filename, opt->prefix, &buf);
+ else
+ strbuf_addstr(&buf, filename);
- if (opt->relative && opt->prefix_length) {
- char *name = strbuf_detach(&buf, NULL);
- quote_path_relative(name, opt->prefix, &buf);
- free(name);
- }
+ 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);
@@ -404,282 +406,91 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
exit(status);
}
-static void compile_submodule_options(const struct grep_opt *opt,
- const char **argv,
- int cached, int untracked,
- int opt_exclude, int use_index,
- int pattern_type_arg)
-{
- struct grep_pat *pattern;
-
- if (recurse_submodules)
- argv_array_push(&submodule_options, "--recurse-submodules");
-
- if (cached)
- argv_array_push(&submodule_options, "--cached");
- if (!use_index)
- argv_array_push(&submodule_options, "--no-index");
- if (untracked)
- argv_array_push(&submodule_options, "--untracked");
- if (opt_exclude > 0)
- argv_array_push(&submodule_options, "--exclude-standard");
-
- if (opt->invert)
- argv_array_push(&submodule_options, "-v");
- if (opt->ignore_case)
- argv_array_push(&submodule_options, "-i");
- if (opt->word_regexp)
- argv_array_push(&submodule_options, "-w");
- switch (opt->binary) {
- case GREP_BINARY_NOMATCH:
- argv_array_push(&submodule_options, "-I");
- break;
- case GREP_BINARY_TEXT:
- argv_array_push(&submodule_options, "-a");
- break;
- default:
- break;
- }
- if (opt->allow_textconv)
- argv_array_push(&submodule_options, "--textconv");
- if (opt->max_depth != -1)
- argv_array_pushf(&submodule_options, "--max-depth=%d",
- opt->max_depth);
- if (opt->linenum)
- argv_array_push(&submodule_options, "-n");
- if (!opt->pathname)
- argv_array_push(&submodule_options, "-h");
- if (!opt->relative)
- argv_array_push(&submodule_options, "--full-name");
- if (opt->name_only)
- argv_array_push(&submodule_options, "-l");
- if (opt->unmatch_name_only)
- argv_array_push(&submodule_options, "-L");
- if (opt->null_following_name)
- argv_array_push(&submodule_options, "-z");
- if (opt->count)
- argv_array_push(&submodule_options, "-c");
- if (opt->file_break)
- argv_array_push(&submodule_options, "--break");
- if (opt->heading)
- argv_array_push(&submodule_options, "--heading");
- if (opt->pre_context)
- argv_array_pushf(&submodule_options, "--before-context=%d",
- opt->pre_context);
- if (opt->post_context)
- argv_array_pushf(&submodule_options, "--after-context=%d",
- opt->post_context);
- if (opt->funcname)
- argv_array_push(&submodule_options, "-p");
- if (opt->funcbody)
- argv_array_push(&submodule_options, "-W");
- if (opt->all_match)
- argv_array_push(&submodule_options, "--all-match");
- if (opt->debug)
- argv_array_push(&submodule_options, "--debug");
- if (opt->status_only)
- argv_array_push(&submodule_options, "-q");
-
- switch (pattern_type_arg) {
- case GREP_PATTERN_TYPE_BRE:
- argv_array_push(&submodule_options, "-G");
- break;
- case GREP_PATTERN_TYPE_ERE:
- argv_array_push(&submodule_options, "-E");
- break;
- case GREP_PATTERN_TYPE_FIXED:
- argv_array_push(&submodule_options, "-F");
- break;
- case GREP_PATTERN_TYPE_PCRE:
- argv_array_push(&submodule_options, "-P");
- break;
- case GREP_PATTERN_TYPE_UNSPECIFIED:
- break;
- }
-
- for (pattern = opt->pattern_list; pattern != NULL;
- pattern = pattern->next) {
- switch (pattern->token) {
- case GREP_PATTERN:
- argv_array_pushf(&submodule_options, "-e%s",
- pattern->pattern);
- break;
- case GREP_AND:
- case GREP_OPEN_PAREN:
- case GREP_CLOSE_PAREN:
- case GREP_NOT:
- case GREP_OR:
- argv_array_push(&submodule_options, pattern->pattern);
- break;
- /* BODY and HEAD are not used by git-grep */
- case GREP_PATTERN_BODY:
- case GREP_PATTERN_HEAD:
- break;
- }
- }
-
- /*
- * Limit number of threads for child process to use.
- * This is to prevent potential fork-bomb behavior of git-grep as each
- * submodule process has its own thread pool.
- */
- argv_array_pushf(&submodule_options, "--threads=%d",
- (num_threads + 1) / 2);
-
- /* Add Pathspecs */
- argv_array_push(&submodule_options, "--");
- for (; *argv; argv++)
- argv_array_push(&submodule_options, *argv);
-}
+static int grep_cache(struct grep_opt *opt, struct repository *repo,
+ const struct pathspec *pathspec, int cached);
+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
+ struct tree_desc *tree, struct strbuf *base, int tn_len,
+ int check_attr, struct repository *repo);
-/*
- * Launch child process to grep contents of a submodule
- */
-static int grep_submodule_launch(struct grep_opt *opt,
- const struct grep_source *gs)
+static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
+ const struct pathspec *pathspec,
+ const struct object_id *oid,
+ const char *filename, const char *path)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- int status, i;
- const char *end_of_base;
- const char *name;
- struct strbuf child_output = STRBUF_INIT;
-
- end_of_base = strchr(gs->name, ':');
- if (gs->identifier && end_of_base)
- name = end_of_base + 1;
- else
- name = gs->name;
+ struct repository submodule;
+ int hit;
- prepare_submodule_repo_env(&cp.env_array);
- argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
+ if (!is_submodule_active(superproject, path))
+ return 0;
- if (opt->relative && opt->prefix_length)
- argv_array_pushf(&cp.env_array, "%s=%s",
- GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
- opt->prefix);
+ if (repo_submodule_init(&submodule, superproject, path))
+ return 0;
- /* Add super prefix */
- argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
- super_prefix ? super_prefix : "",
- name);
- argv_array_push(&cp.args, "grep");
+ repo_read_gitmodules(&submodule);
/*
- * Add basename of parent project
- * When performing grep on a tree object the filename is prefixed
- * with the object's name: 'tree-name:filename'. In order to
- * provide uniformity of output we want to pass the name of the
- * parent project's object name to the submodule so the submodule can
- * prefix its output with the parent's name and not its own SHA1.
+ * NEEDSWORK: This adds the submodule's object directory to the list of
+ * alternates for the single in-memory object store. This has some bad
+ * consequences for memory (processed objects will never be freed) and
+ * performance (this increases the number of pack files git has to pay
+ * attention to, to the sum of the number of pack files in all the
+ * repositories processed so far). This can be removed once the object
+ * store is no longer global and instead is a member of the repository
+ * object.
*/
- if (gs->identifier && end_of_base)
- argv_array_pushf(&cp.args, "--parent-basename=%.*s",
- (int) (end_of_base - gs->name),
- gs->name);
+ grep_read_lock();
+ add_to_alternates_memory(submodule.objectdir);
+ grep_read_unlock();
- /* Add options */
- for (i = 0; i < submodule_options.argc; i++) {
- /*
- * If there is a tree identifier for the submodule, add the
- * rev after adding the submodule options but before the
- * pathspecs. To do this we listen for the '--' and insert the
- * sha1 before pushing the '--' onto the child process argv
- * array.
- */
- if (gs->identifier &&
- !strcmp("--", submodule_options.argv[i])) {
- argv_array_push(&cp.args, sha1_to_hex(gs->identifier));
- }
+ if (oid) {
+ struct object *object;
+ struct tree_desc tree;
+ void *data;
+ unsigned long size;
+ struct strbuf base = STRBUF_INIT;
- argv_array_push(&cp.args, submodule_options.argv[i]);
- }
+ object = parse_object_or_die(oid, oid_to_hex(oid));
- cp.git_cmd = 1;
- cp.dir = gs->path;
+ grep_read_lock();
+ data = read_object_with_reference(object->oid.hash, tree_type,
+ &size, NULL);
+ grep_read_unlock();
- /*
- * Capture output to output buffer and check the return code from the
- * child process. A '0' indicates a hit, a '1' indicates no hit and
- * anything else is an error.
- */
- status = capture_command(&cp, &child_output, 0);
- if (status && (status != 1)) {
- /* flush the buffer */
- write_or_die(1, child_output.buf, child_output.len);
- die("process for submodule '%s' failed with exit code: %d",
- gs->name, status);
- }
+ if (!data)
+ die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
- opt->output(opt, child_output.buf, child_output.len);
- strbuf_release(&child_output);
- /* invert the return code to make a hit equal to 1 */
- return !status;
-}
+ strbuf_addstr(&base, filename);
+ strbuf_addch(&base, '/');
-/*
- * Prep grep structures for a submodule grep
- * sha1: the sha1 of the submodule or NULL if using the working tree
- * filename: name of the submodule including tree name of parent
- * path: location of the submodule
- */
-static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
- const char *filename, const char *path)
-{
- if (!is_submodule_initialized(path))
- return 0;
- if (!is_submodule_populated_gently(path, NULL)) {
- /*
- * If searching history, check for the presense of the
- * submodule's gitdir before skipping the submodule.
- */
- if (sha1) {
- const struct submodule *sub =
- submodule_from_path(null_sha1, path);
- if (sub)
- path = git_path("modules/%s", sub->name);
-
- if (!(is_directory(path) && is_git_directory(path)))
- return 0;
- } else {
- return 0;
- }
+ init_tree_desc(&tree, data, size);
+ hit = grep_tree(opt, pathspec, &tree, &base, base.len,
+ object->type == OBJ_COMMIT, &submodule);
+ strbuf_release(&base);
+ free(data);
+ } else {
+ hit = grep_cache(opt, &submodule, pathspec, 1);
}
-#ifndef NO_PTHREADS
- if (num_threads) {
- add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, sha1);
- return 0;
- } else
-#endif
- {
- struct grep_source gs;
- int hit;
-
- grep_source_init(&gs, GREP_SOURCE_SUBMODULE,
- filename, path, sha1);
- hit = grep_submodule_launch(opt, &gs);
-
- grep_source_clear(&gs);
- return hit;
- }
+ repo_clear(&submodule);
+ return hit;
}
-static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
- int cached)
+static int grep_cache(struct grep_opt *opt, struct repository *repo,
+ const struct pathspec *pathspec, int cached)
{
int hit = 0;
int nr;
struct strbuf name = STRBUF_INIT;
int name_base_len = 0;
- if (super_prefix) {
- name_base_len = strlen(super_prefix);
- strbuf_addstr(&name, super_prefix);
+ if (repo->submodule_prefix) {
+ name_base_len = strlen(repo->submodule_prefix);
+ strbuf_addstr(&name, repo->submodule_prefix);
}
- read_cache();
+ repo_read_index(repo);
- for (nr = 0; nr < active_nr; nr++) {
- const struct cache_entry *ce = active_cache[nr];
+ for (nr = 0; nr < repo->index->cache_nr; nr++) {
+ const struct cache_entry *ce = repo->index->cache[nr];
strbuf_setlen(&name, name_base_len);
strbuf_addstr(&name, ce->name);
@@ -696,14 +507,14 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
ce_skip_worktree(ce)) {
if (ce_stage(ce) || ce_intent_to_add(ce))
continue;
- hit |= grep_oid(opt, &ce->oid, ce->name,
- 0, ce->name);
+ hit |= grep_oid(opt, &ce->oid, name.buf,
+ 0, name.buf);
} else {
- hit |= grep_file(opt, ce->name);
+ hit |= grep_file(opt, name.buf);
}
} else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
submodule_path_match(pathspec, name.buf, NULL)) {
- hit |= grep_submodule(opt, NULL, ce->name, ce->name);
+ hit |= grep_submodule(opt, repo, pathspec, NULL, ce->name, ce->name);
} else {
continue;
}
@@ -711,8 +522,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
if (ce_stage(ce)) {
do {
nr++;
- } while (nr < active_nr &&
- !strcmp(ce->name, active_cache[nr]->name));
+ } while (nr < repo->index->cache_nr &&
+ !strcmp(ce->name, repo->index->cache[nr]->name));
nr--; /* compensate for loop control */
}
if (hit && opt->status_only)
@@ -725,7 +536,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
struct tree_desc *tree, struct strbuf *base, int tn_len,
- int check_attr)
+ int check_attr, struct repository *repo)
{
int hit = 0;
enum interesting match = entry_not_interesting;
@@ -733,8 +544,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
int old_baselen = base->len;
struct strbuf name = STRBUF_INIT;
int name_base_len = 0;
- if (super_prefix) {
- strbuf_addstr(&name, super_prefix);
+ if (repo->submodule_prefix) {
+ strbuf_addstr(&name, repo->submodule_prefix);
name_base_len = name.len;
}
@@ -772,11 +583,11 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
strbuf_addch(base, '/');
init_tree_desc(&sub, data, size);
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
- check_attr);
+ check_attr, repo);
free(data);
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
- hit |= grep_submodule(opt, entry.oid->hash, base->buf,
- base->buf + tn_len);
+ hit |= grep_submodule(opt, repo, pathspec, entry.oid,
+ base->buf, base->buf + tn_len);
}
strbuf_setlen(base, old_baselen);
@@ -790,7 +601,8 @@ 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 object *obj, const char *name, const char *path,
+ struct repository *repo)
{
if (obj->type == OBJ_BLOB)
return grep_oid(opt, &obj->oid, name, 0, path);
@@ -809,10 +621,6 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
if (!data)
die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
- /* Use parent's name as base when recursing submodules */
- if (recurse_submodules && parent_basename)
- name = parent_basename;
-
len = name ? strlen(name) : 0;
strbuf_init(&base, PATH_MAX + len + 1);
if (len) {
@@ -821,15 +629,16 @@ 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);
+ obj->type == OBJ_COMMIT, repo);
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;
@@ -843,9 +652,10 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
/* load the gitmodules file for this rev */
if (recurse_submodules) {
submodule_free();
- gitmodules_config_sha1(real_obj->oid.hash);
+ gitmodules_config_oid(&real_obj->oid);
}
- if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
+ if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path,
+ repo)) {
hit = 1;
if (opt->status_only)
break;
@@ -866,7 +676,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
if (exc_std)
setup_standard_excludes(&dir);
- fill_directory(&dir, pathspec);
+ fill_directory(&dir, &the_index, pathspec);
for (i = 0; i < dir.nr; i++) {
if (!dir_path_match(dir.entries[i], pathspec, 0, NULL))
continue;
@@ -986,9 +796,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
N_("ignore files specified via '.gitignore'"), 1),
OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
N_("recursively search in each submodule")),
- OPT_STRING(0, "parent-basename", &parent_basename,
- N_("basename"),
- N_("prepend parent project's basename to output")),
OPT_GROUP(""),
OPT_BOOL('v', "invert-match", &opt.invert,
N_("show non-matching lines")),
@@ -1032,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")),
@@ -1084,16 +892,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
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()
};
init_grep_defaults();
git_config(grep_cmd_config, NULL);
grep_init(&opt, prefix);
- super_prefix = get_super_prefix();
/*
* If there is no -- then the paths must exist in the working
@@ -1151,10 +960,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!opt.pattern_list)
die(_("no pattern given."));
- if (!opt.fixed && opt.ignore_case)
- opt.regflags |= REG_ICASE;
-
- compile_grep_patterns(&opt);
/*
* We have to find "--" in a separate pass, because its presence
@@ -1190,14 +995,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
break;
}
- if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH,
- oid.hash, &oc)) {
+ if (get_oid_with_context(arg, GET_OID_RECORD_PATH,
+ &oid, &oc)) {
if (seen_dashdash)
die(_("unable to resolve revision: %s"), arg);
break;
}
- object = parse_object_or_die(oid.hash, arg);
+ object = parse_object_or_die(&oid, arg);
if (!seen_dashdash)
verify_non_filename(prefix, arg);
add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
@@ -1220,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)
@@ -1228,10 +1034,23 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
num_threads = GREP_NUM_THREADS_DEFAULT;
else if (num_threads < 0)
die(_("invalid number of threads specified (%d)"), num_threads);
+ if (num_threads == 1)
+ num_threads = 0;
#else
+ if (num_threads)
+ warning(_("no threads support, ignoring --threads"));
num_threads = 0;
#endif
+ if (!num_threads)
+ /*
+ * The compiled patterns on the main path are only
+ * used when not using threading. Otherwise
+ * start_threads() below calls compile_grep_patterns()
+ * for each thread.
+ */
+ compile_grep_patterns(&opt);
+
#ifndef NO_PTHREADS
if (num_threads) {
if (!(opt.name_only || opt.unmatch_name_only || opt.count)
@@ -1242,13 +1061,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
#endif
- if (recurse_submodules) {
- gitmodules_config();
- compile_submodule_options(&opt, argv + i, cached, untracked,
- opt_exclude, use_index,
- pattern_type_arg);
- }
-
if (show_in_pager && (cached || list.nr))
die(_("--open-files-in-pager only works on the worktree"));
@@ -1290,11 +1102,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!cached)
setup_work_tree();
- hit = grep_cache(&opt, &pathspec, cached);
+ hit = grep_cache(&opt, the_repository, &pathspec, cached);
} else {
if (cached)
die(_("both --cached and trees are given."));
- hit = grep_objects(&opt, &pathspec, &list);
+
+ hit = grep_objects(&opt, &pathspec, the_repository, &list);
}
if (num_threads)
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index bbeaf20bcc..526da5c185 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -5,6 +5,7 @@
* Copyright (C) Junio C Hamano, 2005
*/
#include "builtin.h"
+#include "config.h"
#include "blob.h"
#include "quote.h"
#include "parse-options.h"
@@ -15,7 +16,7 @@
* needs to bypass the data conversion performed by, and the type
* limitation imposed by, index_fd() and its callees.
*/
-static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigned flags)
+static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)
{
struct strbuf buf = STRBUF_INIT;
int ret;
@@ -23,7 +24,8 @@ static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigne
if (strbuf_read(&buf, fd, 4096) < 0)
ret = -1;
else
- ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags);
+ ret = hash_object_file_literally(buf.buf, buf.len, type, oid,
+ flags);
strbuf_release(&buf);
return ret;
}
@@ -32,16 +34,16 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
int literally)
{
struct stat st;
- unsigned char sha1[20];
+ struct object_id oid;
if (fstat(fd, &st) < 0 ||
(literally
- ? hash_literally(sha1, fd, type, flags)
- : index_fd(sha1, fd, &st, type_from_string(type), path, flags)))
+ ? hash_literally(&oid, fd, type, flags)
+ : index_fd(&oid, fd, &st, type_from_string(type), path, flags)))
die((flags & HASH_WRITE_OBJECT)
? "Unable to add %s to database"
: "Unable to hash %s", path);
- printf("%s\n", sha1_to_hex(sha1));
+ printf("%s\n", oid_to_hex(&oid));
maybe_flush_or_die(stdout, "hash to stdout");
}
diff --git a/builtin/help.c b/builtin/help.c
index 49f7a07f85..598867cfea 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -2,6 +2,7 @@
* Builtin help command
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "exec_cmd.h"
#include "parse-options.h"
@@ -130,6 +131,7 @@ static void exec_woman_emacs(const char *path, const char *page)
strbuf_addf(&man_page, "(woman \"%s\")", page);
execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
warning_errno(_("failed to exec '%s'"), path);
+ strbuf_release(&man_page);
}
}
@@ -151,6 +153,7 @@ static void exec_man_konqueror(const char *path, const char *page)
strbuf_addf(&man_page, "man:%s(1)", page);
execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
warning_errno(_("failed to exec '%s'"), path);
+ strbuf_release(&man_page);
}
}
@@ -168,6 +171,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
warning(_("failed to exec '%s'"), cmd);
+ strbuf_release(&shell_cmd);
}
static void add_man_viewer(const char *name)
@@ -190,11 +194,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,
@@ -437,7 +441,7 @@ static const char *check_git_cmd(const char* cmd)
alias = alias_lookup(cmd);
if (alias) {
- printf_ln(_("`git %s' is aliased to `%s'"), cmd, alias);
+ printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
free(alias);
exit(0);
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 4ff567db47..bda84a92ef 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "delta.h"
#include "pack.h"
#include "csum-file.h"
@@ -11,6 +12,7 @@
#include "exec_cmd.h"
#include "streaming.h"
#include "thread-utils.h"
+#include "packfile.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>])";
@@ -47,6 +49,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)
@@ -89,7 +92,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;
@@ -226,7 +229,7 @@ static unsigned check_object(struct object *obj)
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;
}
@@ -251,7 +254,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;
}
@@ -324,7 +327,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;
}
@@ -388,8 +391,7 @@ static struct base_data *alloc_base_data(void)
static void free_base_data(struct base_data *c)
{
if (c->data) {
- free(c->data);
- c->data = NULL;
+ FREE_AND_NULL(c->data);
get_thread_data()->base_cache_used -= c->size;
}
}
@@ -436,22 +438,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
@@ -468,8 +470,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);
@@ -478,15 +480,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;
@@ -514,8 +516,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);
@@ -545,7 +547,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;
}
@@ -605,8 +607,7 @@ static void *unpack_data(struct object_entry *obj,
git_inflate_end(&stream);
free(inbuf);
if (consume) {
- free(data);
- data = NULL;
+ FREE_AND_NULL(data);
}
return data;
}
@@ -633,7 +634,7 @@ static int find_ofs_delta(const off_t offset, enum object_type type)
int first = 0, last = nr_ofs_deltas;
while (first < last) {
- int next = (first + last) / 2;
+ int next = first + (last - first) / 2;
struct ofs_delta_entry *delta = &ofs_deltas[next];
int cmp;
@@ -687,7 +688,7 @@ static int find_ref_delta(const unsigned char *sha1, enum object_type type)
int first = 0, last = nr_ref_deltas;
while (first < last) {
- int next = (first + last) / 2;
+ int next = first + (last - first) / 2;
struct ref_delta_entry *delta = &ref_deltas[next];
int cmp;
@@ -747,13 +748,13 @@ static int compare_objects(const unsigned char *buf, unsigned long size,
ssize_t len = read_istream(data->st, data->buf, size);
if (len == 0)
die(_("SHA1 COLLISION FOUND WITH %s !"),
- sha1_to_hex(data->entry->idx.sha1));
+ oid_to_hex(&data->entry->idx.oid));
if (len < 0)
die(_("unable to read %s"),
- sha1_to_hex(data->entry->idx.sha1));
+ oid_to_hex(&data->entry->idx.oid));
if (memcmp(buf, data->buf, len))
die(_("SHA1 COLLISION FOUND WITH %s !"),
- sha1_to_hex(data->entry->idx.sha1));
+ oid_to_hex(&data->entry->idx.oid));
size -= len;
buf += len;
}
@@ -771,12 +772,12 @@ static int check_collison(struct object_entry *entry)
memset(&data, 0, sizeof(data));
data.entry = entry;
- data.st = open_istream(entry->idx.sha1, &type, &size, NULL);
+ data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL);
if (!data.st)
return -1;
if (size != entry->size || type != entry->type)
die(_("SHA1 COLLISION FOUND WITH %s !"),
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
unpack_data(entry, compare_objects, &data);
close_istream(data.st);
free(data.buf);
@@ -785,7 +786,7 @@ static int check_collison(struct object_entry *entry)
static void sha1_object(const void *data, struct object_entry *obj_entry,
unsigned long size, enum object_type type,
- const unsigned char *sha1)
+ const struct object_id *oid)
{
void *new_data = NULL;
int collision_test_needed = 0;
@@ -794,7 +795,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
if (startup_info->have_repository) {
read_lock();
- collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
+ collision_test_needed =
+ has_sha1_file_with_flags(oid->hash, OBJECT_INFO_QUICK);
read_unlock();
}
@@ -809,31 +811,31 @@ 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(sha1, &has_size);
+ has_type = sha1_object_info(oid->hash, &has_size);
if (has_type < 0)
- die(_("cannot read existing object info %s"), sha1_to_hex(sha1));
+ die(_("cannot read existing object info %s"), oid_to_hex(oid));
if (has_type != type || has_size != size)
- die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
- has_data = read_sha1_file(sha1, &has_type, &has_size);
+ die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
+ has_data = read_sha1_file(oid->hash, &has_type, &has_size);
read_unlock();
if (!data)
data = new_data = get_data_from_pack(obj_entry);
if (!has_data)
- die(_("cannot read existing object %s"), sha1_to_hex(sha1));
+ die(_("cannot read existing object %s"), oid_to_hex(oid));
if (size != has_size || type != has_type ||
memcmp(data, has_data, size) != 0)
- die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
+ die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
free(has_data);
}
- if (strict) {
+ if (strict || do_fsck_object) {
read_lock();
if (type == OBJ_BLOB) {
- struct blob *blob = lookup_blob(sha1);
+ struct blob *blob = lookup_blob(oid);
if (blob)
blob->object.flags |= FLAG_CHECKED;
else
- die(_("invalid blob object %s"), sha1_to_hex(sha1));
+ die(_("invalid blob object %s"), oid_to_hex(oid));
} else {
struct object *obj;
int eaten;
@@ -845,13 +847,14 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
* we do not need to free the memory here, as the
* buf is deleted by the caller.
*/
- obj = parse_object_buffer(sha1, type, size, buf, &eaten);
+ 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))
+ 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) {
@@ -956,10 +959,10 @@ 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.sha1);
+ 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.sha1);
+ &delta_obj->idx.oid);
counter_lock();
nr_resolved_deltas++;
counter_unlock();
@@ -989,7 +992,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.sha1,
+ find_ref_delta_children(base->obj->idx.oid.hash,
&base->ref_first, &base->ref_last,
OBJ_REF_DELTA);
@@ -1116,11 +1119,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)
@@ -1130,7 +1133,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.sha1);
+ &ref_delta_oid,
+ &obj->idx.oid);
obj->real_type = obj->type;
if (obj->type == OBJ_OFS_DELTA) {
nr_ofs_deltas++;
@@ -1138,7 +1142,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);
+ hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_oid.hash);
ref_deltas[nr_ref_deltas].obj_no = i;
nr_ref_deltas++;
} else if (!data) {
@@ -1146,7 +1150,8 @@ static void parse_pack_objects(unsigned char *sha1)
obj->real_type = OBJ_BAD;
nr_delays++;
} else
- sha1_object(data, NULL, obj->size, obj->type, obj->idx.sha1);
+ sha1_object(data, NULL, obj->size, obj->type,
+ &obj->idx.oid);
free(data);
display_progress(progress, i+1);
}
@@ -1155,10 +1160,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))
@@ -1172,7 +1177,8 @@ static void parse_pack_objects(unsigned char *sha1)
if (obj->real_type != OBJ_BAD)
continue;
obj->real_type = obj->type;
- sha1_object(NULL, obj, obj->size, obj->type, obj->idx.sha1);
+ sha1_object(NULL, obj, obj->size, obj->type,
+ &obj->idx.oid);
nr_delays--;
}
if (nr_delays)
@@ -1233,21 +1239,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;
@@ -1256,7 +1262,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",
@@ -1264,12 +1270,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,
+ hashclose(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);
}
@@ -1280,7 +1286,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;
@@ -1294,7 +1300,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)
@@ -1304,7 +1310,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)
{
@@ -1321,7 +1327,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;
@@ -1329,8 +1335,8 @@ 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);
- hashcpy(obj->idx.sha1, sha1);
+ hashflush(f);
+ hashcpy(obj->idx.oid.hash, sha1);
return obj;
}
@@ -1341,7 +1347,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;
@@ -1373,7 +1379,7 @@ static void fix_unresolved_deltas(struct sha1file *f)
continue;
if (check_sha1_signature(d->sha1, base_obj->data,
- base_obj->size, typename(type)))
+ base_obj->size, type_name(type)))
die(_("local object %s is corrupt"), sha1_to_hex(d->sha1));
base_obj->obj = append_obj_to_pack(f, d->sha1,
base_obj->data, base_obj->size, type);
@@ -1383,15 +1389,60 @@ static void fix_unresolved_deltas(struct sha1file *f)
free(sorted_by_pos);
}
+static const char *derive_filename(const char *pack_name, const char *suffix,
+ struct strbuf *buf)
+{
+ size_t len;
+ if (!strip_suffix(pack_name, ".pack", &len))
+ die(_("packfile name '%s' does not end with '.pack'"),
+ pack_name);
+ strbuf_add(buf, pack_name, len);
+ strbuf_addch(buf, '.');
+ strbuf_addstr(buf, suffix);
+ return buf->buf;
+}
+
+static void write_special_file(const char *suffix, const char *msg,
+ const char *pack_name, const unsigned char *hash,
+ const char **report)
+{
+ struct strbuf name_buf = STRBUF_INIT;
+ const char *filename;
+ int fd;
+ int msg_len = strlen(msg);
+
+ if (pack_name)
+ filename = derive_filename(pack_name, suffix, &name_buf);
+ else
+ filename = odb_pack_name(&name_buf, hash, suffix);
+
+ fd = odb_pack_keep(filename);
+ if (fd < 0) {
+ if (errno != EEXIST)
+ die_errno(_("cannot write %s file '%s'"),
+ suffix, filename);
+ } else {
+ if (msg_len > 0) {
+ write_or_die(fd, msg, msg_len);
+ write_or_die(fd, "\n", 1);
+ }
+ if (close(fd) != 0)
+ die_errno(_("cannot close written %s file '%s'"),
+ suffix, filename);
+ if (report)
+ *report = suffix;
+ }
+ strbuf_release(&name_buf);
+}
+
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_name, const char *keep_msg,
- unsigned char *sha1)
+ const char *keep_msg, const char *promisor_msg,
+ unsigned char *hash)
{
const char *report = "pack";
struct strbuf pack_name = STRBUF_INIT;
struct strbuf index_name = STRBUF_INIT;
- struct strbuf keep_name_buf = STRBUF_INIT;
int err;
if (!from_stdin) {
@@ -1403,32 +1454,16 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
die_errno(_("error while closing pack file"));
}
- if (keep_msg) {
- int keep_fd, keep_msg_len = strlen(keep_msg);
-
- if (!keep_name)
- keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
-
- keep_fd = odb_pack_keep(keep_name);
- if (keep_fd < 0) {
- if (errno != EEXIST)
- die_errno(_("cannot write keep file '%s'"),
- keep_name);
- } else {
- if (keep_msg_len > 0) {
- write_or_die(keep_fd, keep_msg, keep_msg_len);
- write_or_die(keep_fd, "\n", 1);
- }
- if (close(keep_fd) != 0)
- die_errno(_("cannot close written keep file '%s'"),
- keep_name);
- report = "keep";
- }
- }
+ if (keep_msg)
+ write_special_file("keep", keep_msg, final_pack_name, hash,
+ &report);
+ if (promisor_msg)
+ write_special_file("promisor", promisor_msg, final_pack_name,
+ 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)
@@ -1436,18 +1471,18 @@ 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 (!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);
@@ -1466,7 +1501,6 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
strbuf_release(&index_name);
strbuf_release(&pack_name);
- strbuf_release(&keep_name_buf);
}
static int git_index_pack_config(const char *k, const char *v, void *cb)
@@ -1581,13 +1615,14 @@ static void show_pack_info(int stat_only)
if (stat_only)
continue;
printf("%s %-6s %lu %lu %"PRIuMAX,
- sha1_to_hex(obj->idx.sha1),
- typename(obj->real_type), obj->size,
+ oid_to_hex(&obj->idx.oid),
+ 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)) {
struct object_entry *bobj = &objects[obj_stat[i].base_object_no];
- printf(" %u %s", obj_stat[i].delta_depth, sha1_to_hex(bobj->idx.sha1));
+ printf(" %u %s", obj_stat[i].delta_depth,
+ oid_to_hex(&bobj->idx.oid));
}
putchar('\n');
}
@@ -1608,32 +1643,26 @@ static void show_pack_info(int stat_only)
}
}
-static const char *derive_filename(const char *pack_name, const char *suffix,
- struct strbuf *buf)
-{
- size_t len;
- if (!strip_suffix(pack_name, ".pack", &len))
- die(_("packfile name '%s' does not end with '.pack'"),
- pack_name);
- strbuf_add(buf, pack_name, len);
- strbuf_addstr(buf, suffix);
- return buf->buf;
-}
-
int cmd_index_pack(int argc, const char **argv, const char *prefix)
{
int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
const char *curr_index;
const char *index_name = NULL, *pack_name = NULL;
- const char *keep_name = NULL, *keep_msg = NULL;
- struct strbuf index_name_buf = STRBUF_INIT,
- keep_name_buf = STRBUF_INIT;
+ const char *keep_msg = NULL;
+ const char *promisor_msg = NULL;
+ 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;
+ /*
+ * index-pack never needs to fetch missing objects, since it only
+ * accesses the repo to do hash collision checks
+ */
+ fetch_if_missing = 0;
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
@@ -1653,16 +1682,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")) {
@@ -1672,10 +1700,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 (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);
@@ -1738,9 +1766,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (from_stdin && !startup_info->have_repository)
die(_("--stdin requires a git repository"));
if (!index_name && pack_name)
- index_name = derive_filename(pack_name, ".idx", &index_name_buf);
- if (keep_msg && !keep_name && pack_name)
- keep_name = derive_filename(pack_name, ".keep", &keep_name_buf);
+ index_name = derive_filename(pack_name, "idx", &index_name_buf);
if (verify) {
if (!index_name)
@@ -1766,11 +1792,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)
@@ -1782,19 +1808,18 @@ 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_name, keep_msg,
- pack_sha1);
+ keep_msg, promisor_msg,
+ pack_hash);
else
close(input_fd);
free(objects);
strbuf_release(&index_name_buf);
- strbuf_release(&keep_name_buf);
if (pack_name == NULL)
free((void *) curr_pack);
if (index_name == NULL)
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 8a6acb0ec6..68ff4ad75a 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "builtin.h"
#include "exec_cmd.h"
@@ -23,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
@@ -43,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);
@@ -56,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);
}
}
@@ -578,6 +579,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
set_git_work_tree(work_tree);
}
+ UNLEAK(real_git_dir);
+
flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, flags);
}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 175f14797b..b742539d4d 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -16,34 +16,119 @@ static const char * const git_interpret_trailers_usage[] = {
NULL
};
+static enum trailer_where where;
+static enum trailer_if_exists if_exists;
+static enum trailer_if_missing if_missing;
+
+static int option_parse_where(const struct option *opt,
+ const char *arg, int unset)
+{
+ return trailer_set_where(&where, arg);
+}
+
+static int option_parse_if_exists(const struct option *opt,
+ const char *arg, int unset)
+{
+ return trailer_set_if_exists(&if_exists, arg);
+}
+
+static int option_parse_if_missing(const struct option *opt,
+ const char *arg, int unset)
+{
+ return trailer_set_if_missing(&if_missing, arg);
+}
+
+static void new_trailers_clear(struct list_head *trailers)
+{
+ struct list_head *pos, *tmp;
+ struct new_trailer_item *item;
+
+ list_for_each_safe(pos, tmp, trailers) {
+ item = list_entry(pos, struct new_trailer_item, list);
+ list_del(pos);
+ free(item);
+ }
+}
+
+static int option_parse_trailer(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct list_head *trailers = opt->value;
+ struct new_trailer_item *item;
+
+ if (unset) {
+ new_trailers_clear(trailers);
+ return 0;
+ }
+
+ if (!arg)
+ return -1;
+
+ item = xmalloc(sizeof(*item));
+ item->text = arg;
+ item->where = where;
+ item->if_exists = if_exists;
+ item->if_missing = if_missing;
+ list_add_tail(&item->list, trailers);
+ return 0;
+}
+
+static int parse_opt_parse(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct process_trailer_options *v = opt->value;
+ v->only_trailers = 1;
+ v->only_input = 1;
+ v->unfold = 1;
+ return 0;
+}
+
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
{
- int in_place = 0;
- int trim_empty = 0;
- struct string_list trailers = STRING_LIST_INIT_NODUP;
+ struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+ LIST_HEAD(trailers);
struct option options[] = {
- OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
- OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
- OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
- N_("trailer(s) to add")),
+ OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
+ OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
+
+ OPT_CALLBACK(0, "where", NULL, N_("action"),
+ N_("where to place the new trailer"), option_parse_where),
+ OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
+ N_("action if trailer already exists"), option_parse_if_exists),
+ OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
+ N_("action if trailer is missing"), option_parse_if_missing),
+
+ OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
+ OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
+ OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
+ { OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+ OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
+ N_("trailer(s) to add"), option_parse_trailer),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options,
git_interpret_trailers_usage, 0);
+ if (opts.only_input && !list_empty(&trailers))
+ usage_msg_opt(
+ _("--trailer with --only-input does not make sense"),
+ git_interpret_trailers_usage,
+ options);
+
if (argc) {
int i;
for (i = 0; i < argc; i++)
- process_trailers(argv[i], in_place, trim_empty, &trailers);
+ process_trailers(argv[i], &opts, &trailers);
} else {
- if (in_place)
+ if (opts.in_place)
die(_("no input file given for in-place editing"));
- process_trailers(NULL, in_place, trim_empty, &trailers);
+ process_trailers(NULL, &opts, &trailers);
}
- string_list_clear(&trailers, 0);
+ new_trailers_clear(&trailers);
return 0;
}
diff --git a/builtin/log.c b/builtin/log.c
index 57ce470f50..94ee177d56 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -5,6 +5,7 @@
* 2006 Junio Hamano
*/
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "color.h"
#include "commit.h"
@@ -26,6 +27,9 @@
#include "version.h"
#include "mailmap.h"
#include "gpg-interface.h"
+#include "progress.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;
@@ -57,9 +61,9 @@ static int auto_decoration_style(void)
return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
}
-static int parse_decoration_style(const char *var, const char *value)
+static int parse_decoration_style(const char *value)
{
- switch (git_config_maybe_bool(var, value)) {
+ switch (git_parse_maybe_bool(value)) {
case 1:
return DECORATE_SHORT_REFS;
case 0:
@@ -81,7 +85,7 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
if (unset)
decoration_style = 0;
else if (arg)
- decoration_style = parse_decoration_style("command line", arg);
+ decoration_style = parse_decoration_style(arg);
else
decoration_style = DECORATE_SHORT_REFS;
@@ -119,20 +123,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,
@@ -141,11 +144,19 @@ 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};
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",
@@ -179,8 +190,8 @@ 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)
@@ -204,7 +215,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)
@@ -371,11 +382,14 @@ static int cmd_log_walk(struct rev_info *rev)
*/
rev->max_count++;
if (!rev->reflog_info) {
- /* we allow cycles in reflog ancestry */
+ /*
+ * We may show a given commit multiple times when
+ * walking the reflogs.
+ */
free_commit_buffer(commit);
+ free_commit_list(commit->parents);
+ commit->parents = NULL;
}
- free_commit_list(commit->parents);
- commit->parents = NULL;
if (saved_nrl < rev->diffopt.needed_rename_limit)
saved_nrl = rev->diffopt.needed_rename_limit;
if (rev->diffopt.degraded_cc_to_c)
@@ -387,7 +401,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);
@@ -408,7 +422,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
if (!strcmp(var, "log.decorate")) {
- decoration_style = parse_decoration_style(var, value);
+ decoration_style = parse_decoration_style(value);
if (decoration_style < 0)
decoration_style = 0; /* maybe warn? */
return 0;
@@ -479,12 +493,12 @@ 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_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
- oidc.hash, &obj_context))
+ if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
+ &oidc, &obj_context))
die(_("Not a valid object name %s"), obj_name);
if (!obj_context.path ||
!textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
@@ -600,7 +614,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.shown_one = 1;
if (ret)
break;
- o = parse_object(t->tagged->oid.hash);
+ o = parse_object(&t->tagged->oid);
if (!o)
ret = error(_("Could not read object %s"),
oid_to_hex(&t->tagged->oid));
@@ -662,9 +676,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)
@@ -820,7 +834,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "format.from")) {
- int b = git_config_maybe_bool(var, value);
+ int b = git_parse_maybe_bool(value);
free(from);
if (b < 0)
from = xstrdup(value);
@@ -846,8 +860,10 @@ static int open_next_file(struct commit *commit, const char *subject,
if (output_directory) {
strbuf_addstr(&filename, output_directory);
if (filename.len >=
- PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
+ PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) {
+ strbuf_release(&filename);
return error(_("name of output directory is too long"));
+ }
strbuf_complete(&filename, '/');
}
@@ -861,8 +877,11 @@ static int open_next_file(struct commit *commit, const char *subject,
if (!quiet)
printf("%s\n", filename.buf + outdir_offset);
- if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
- return error(_("Cannot open patch file %s"), filename.buf);
+ if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) {
+ error_errno(_("Cannot open patch file %s"), filename.buf);
+ strbuf_release(&filename);
+ return -1;
+ }
strbuf_release(&filename);
return 0;
@@ -882,8 +901,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
o2 = rev->pending.objects[1].item;
flags1 = o1->flags;
flags2 = o2->flags;
- c1 = lookup_commit_reference(o1->oid.hash);
- c2 = lookup_commit_reference(o2->oid.hash);
+ c1 = lookup_commit_reference(&o1->oid);
+ c2 = lookup_commit_reference(&o2->oid);
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die(_("Not a range."));
@@ -914,8 +933,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
static void gen_message_id(struct rev_info *info, char *base)
{
struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s.%lu.git.%s", base,
- (unsigned long) time(NULL),
+ strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
+ (timestamp_t) time(NULL),
git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
info->message_id = strbuf_detach(&buf, NULL);
}
@@ -965,7 +984,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);
@@ -1027,7 +1046,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;
@@ -1044,12 +1063,13 @@ 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_sha1(origin->tree->object.oid.hash,
- head->tree->object.oid.hash,
- "", &opts);
+ diff_tree_oid(&origin->tree->object.oid,
+ &head->tree->object.oid,
+ "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
@@ -1267,7 +1287,7 @@ static struct commit *get_base_commit(const char *base_commit,
if (get_oid(upstream, &oid))
die(_("Failed to resolve '%s' as a valid ref."), upstream);
- commit = lookup_commit_or_die(oid.hash, "upstream base");
+ commit = lookup_commit_or_die(&oid, "upstream base");
base_list = get_merge_bases_many(commit, total, list);
/* There should be one and only one merge base. */
if (!base_list || base_list->next)
@@ -1302,7 +1322,7 @@ static struct commit *get_base_commit(const char *base_commit,
if (rev_nr % 2)
rev[i] = rev[2 * i];
- rev_nr = (rev_nr + 1) / 2;
+ rev_nr = DIV_ROUND_UP(rev_nr, 2);
}
if (!in_merge_bases(base, rev[0]))
@@ -1331,7 +1351,7 @@ static void prepare_bases(struct base_tree_info *bases,
return;
diff_setup(&diffopt);
- DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diffopt.flags.recursive = 1;
diff_setup_done(&diffopt);
oidcpy(&bases->base_commit, &base->object.oid);
@@ -1358,7 +1378,7 @@ static void prepare_bases(struct base_tree_info *bases,
struct object_id *patch_id;
if (commit->util)
continue;
- if (commit_patch_id(commit, &diffopt, oid.hash, 0))
+ if (commit_patch_id(commit, &diffopt, &oid, 0))
die(_("cannot get patch id"));
ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
patch_id = bases->patch_id + bases->nr_patch_id;
@@ -1413,6 +1433,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
char *branch_name = NULL;
char *base_commit = NULL;
struct base_tree_info bases;
+ int show_progress = 0;
+ struct progress *progress = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
@@ -1484,6 +1506,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
OPT_FILENAME(0, "signature-file", &signature_file,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
+ OPT_BOOL(0, "progress", &show_progress,
+ N_("show progress while generating patches")),
OPT_END()
};
@@ -1498,7 +1522,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";
@@ -1593,14 +1617,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);
@@ -1646,10 +1672,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
check_head = 1;
if (check_head) {
- struct object_id oid;
const char *ref, *v;
ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- oid.hash, NULL);
+ NULL, NULL);
if (ref && skip_prefix(ref, "refs/heads/", &v))
branch_name = xstrdup(v);
else
@@ -1743,8 +1768,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
start_number--;
}
rev.add_signoff = do_signoff;
+
+ if (show_progress)
+ progress = start_delayed_progress(_("Generating patches"), total);
while (0 <= --nr) {
int shown;
+ display_progress(progress, total - nr);
commit = list[nr];
rev.nr = total - nr + (start_number - 1);
/* Make the second and subsequent mails replies to the first */
@@ -1809,6 +1838,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!use_stdout)
fclose(rev.diffopt.file);
}
+ stop_progress(&progress);
free(list);
free(branch_name);
string_list_clear(&extra_to, 0);
@@ -1823,7 +1853,7 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
{
struct object_id oid;
if (get_oid(arg, &oid) == 0) {
- struct commit *commit = lookup_commit_reference(oid.hash);
+ struct commit *commit = lookup_commit_reference(&oid);
if (commit) {
commit->object.flags |= flags;
add_pending_object(revs, &commit->object, arg);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a6c70dbe9e..2fc836e330 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -5,7 +5,10 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
+#include "repository.h"
+#include "config.h"
#include "quote.h"
#include "dir.h"
#include "builtin.h"
@@ -16,6 +19,7 @@
#include "pathspec.h"
#include "run-command.h"
#include "submodule.h"
+#include "submodule-config.h"
static int abbrev;
static int show_deleted;
@@ -27,14 +31,13 @@ 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;
static int recurse_submodules;
-static struct argv_array submodule_options = ARGV_ARRAY_INIT;
static const char *prefix;
-static const char *super_prefix;
static int max_prefix_len;
static int prefix_len;
static struct pathspec pathspec;
@@ -53,17 +56,17 @@ static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
-static void write_eolinfo(const struct cache_entry *ce, const char *path)
+static void write_eolinfo(const struct index_state *istate,
+ const struct cache_entry *ce, const char *path)
{
- if (!show_eol)
- return;
- else {
+ if (show_eol) {
struct stat st;
const char *i_txt = "";
const char *w_txt = "";
const char *a_txt = get_convert_attr_ascii(path);
if (ce && S_ISREG(ce->ce_mode))
- i_txt = get_cached_convert_stats_ascii(ce->name);
+ i_txt = get_cached_convert_stats_ascii(istate,
+ ce->name);
if (!lstat(path, &st) && S_ISREG(st.st_mode))
w_txt = get_wt_convert_stats_ascii(path);
printf("i/%-5s w/%-5s attr/%-17s\t", i_txt, w_txt, a_txt);
@@ -73,54 +76,81 @@ static void write_eolinfo(const struct cache_entry *ce, const char *path)
static void write_name(const char *name)
{
/*
- * Prepend the super_prefix to name to construct the full_name to be
- * written.
- */
- struct strbuf full_name = STRBUF_INIT;
- if (super_prefix) {
- strbuf_addstr(&full_name, super_prefix);
- strbuf_addstr(&full_name, name);
- name = full_name.buf;
- }
-
- /*
* With "--full-name", prefix_len=0; this caller needs to pass
* an empty string in that case (a NULL is good for "").
*/
write_name_quoted_relative(name, prefix_len ? prefix : NULL,
stdout, line_terminator);
+}
+
+static const char *get_tag(const struct cache_entry *ce, const char *tag)
+{
+ static char alttag[4];
- strbuf_release(&full_name);
+ 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])) {
+ alttag[0] = tolower(tag[0]);
+ } else if (tag[0] == '?') {
+ alttag[0] = '!';
+ } else {
+ alttag[0] = 'v';
+ alttag[1] = tag[0];
+ alttag[2] = ' ';
+ alttag[3] = 0;
+ }
+
+ tag = alttag;
+ }
+
+ return tag;
+}
+
+static void print_debug(const struct cache_entry *ce)
+{
+ if (debug_mode) {
+ const struct stat_data *sd = &ce->ce_stat_data;
+
+ printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+ printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+ printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+ printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
+ }
}
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = max_prefix_len;
- if (len >= ent->len)
+ if (len > ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
if (!dir_path_match(ent, &pathspec, len, ps_matched))
return;
fputs(tag, stdout);
- write_eolinfo(NULL, ent->name);
+ write_eolinfo(NULL, NULL, ent->name);
write_name(ent->name);
}
-static void show_other_files(struct dir_struct *dir)
+static void show_other_files(const struct index_state *istate,
+ const struct dir_struct *dir)
{
int i;
for (i = 0; i < dir->nr; i++) {
struct dir_entry *ent = dir->entries[i];
- if (!cache_name_is_other(ent->name, ent->len))
+ if (!index_name_is_other(istate, ent->name, ent->len))
continue;
show_dir_entry(tag_other, ent);
}
}
-static void show_killed_files(struct dir_struct *dir)
+static void show_killed_files(const struct index_state *istate,
+ const struct dir_struct *dir)
{
int i;
for (i = 0; i < dir->nr; i++) {
@@ -134,29 +164,29 @@ static void show_killed_files(struct dir_struct *dir)
/* If ent->name is prefix of an entry in the
* cache, it will be killed.
*/
- pos = cache_name_pos(ent->name, ent->len);
+ pos = index_name_pos(istate, ent->name, ent->len);
if (0 <= pos)
die("BUG: killed-file %.*s not found",
ent->len, ent->name);
pos = -pos - 1;
- while (pos < active_nr &&
- ce_stage(active_cache[pos]))
+ while (pos < istate->cache_nr &&
+ ce_stage(istate->cache[pos]))
pos++; /* skip unmerged */
- if (active_nr <= pos)
+ if (istate->cache_nr <= pos)
break;
/* pos points at a name immediately after
* ent->name in the cache. Does it expect
* ent->name to be a directory?
*/
- len = ce_namelen(active_cache[pos]);
+ len = ce_namelen(istate->cache[pos]);
if ((ent->len < len) &&
- !strncmp(active_cache[pos]->name,
+ !strncmp(istate->cache[pos]->name,
ent->name, ent->len) &&
- active_cache[pos]->name[ent->len] == '/')
+ istate->cache[pos]->name[ent->len] == '/')
killed = 1;
break;
}
- if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
+ if (0 <= index_name_pos(istate, ent->name, sp - ent->name)) {
/* If any of the leading directories in
* ent->name is registered in the cache,
* ent->name will be killed.
@@ -170,100 +200,39 @@ static void show_killed_files(struct dir_struct *dir)
}
}
-/*
- * Compile an argv_array with all of the options supported by --recurse_submodules
- */
-static void compile_submodule_options(const char **argv,
- const struct dir_struct *dir,
- int show_tag)
-{
- if (line_terminator == '\0')
- argv_array_push(&submodule_options, "-z");
- if (show_tag)
- argv_array_push(&submodule_options, "-t");
- if (show_valid_bit)
- argv_array_push(&submodule_options, "-v");
- if (show_cached)
- argv_array_push(&submodule_options, "--cached");
- if (show_eol)
- argv_array_push(&submodule_options, "--eol");
- if (debug_mode)
- argv_array_push(&submodule_options, "--debug");
-
- /* Add Pathspecs */
- argv_array_push(&submodule_options, "--");
- for (; *argv; argv++)
- argv_array_push(&submodule_options, *argv);
-}
+static void show_files(struct repository *repo, struct dir_struct *dir);
-/**
- * Recursively call ls-files on a submodule
- */
-static void show_gitlink(const struct cache_entry *ce)
+static void show_submodule(struct repository *superproject,
+ struct dir_struct *dir, const char *path)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- int status;
- char *dir;
-
- prepare_submodule_repo_env(&cp.env_array);
- argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
-
- if (prefix_len)
- argv_array_pushf(&cp.env_array, "%s=%s",
- GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
- prefix);
- argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
- super_prefix ? super_prefix : "",
- ce->name);
- argv_array_push(&cp.args, "ls-files");
- argv_array_push(&cp.args, "--recurse-submodules");
-
- /* add supported options */
- argv_array_pushv(&cp.args, submodule_options.argv);
-
- cp.git_cmd = 1;
- dir = mkpathdup("%s/%s", get_git_work_tree(), ce->name);
- cp.dir = dir;
- status = run_command(&cp);
- free(dir);
- if (status)
- exit(status);
+ struct repository submodule;
+
+ if (repo_submodule_init(&submodule, superproject, path))
+ return;
+
+ if (repo_read_index(&submodule) < 0)
+ die("index file corrupt");
+
+ show_files(&submodule, dir);
+
+ repo_clear(&submodule);
}
-static void show_ce_entry(const char *tag, const struct cache_entry *ce)
+static void show_ce(struct repository *repo, struct dir_struct *dir,
+ const struct cache_entry *ce, const char *fullname,
+ const char *tag)
{
- struct strbuf name = STRBUF_INIT;
- int len = max_prefix_len;
- if (super_prefix)
- strbuf_addstr(&name, super_prefix);
- strbuf_addstr(&name, ce->name);
-
- if (len >= ce_namelen(ce))
+ if (max_prefix_len > strlen(fullname))
die("git ls-files: internal error - cache entry not superset of prefix");
if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
- submodule_path_match(&pathspec, name.buf, ps_matched)) {
- show_gitlink(ce);
- } else if (match_pathspec(&pathspec, name.buf, name.len,
- len, ps_matched,
+ is_submodule_active(repo, ce->name)) {
+ show_submodule(repo, dir, ce->name);
+ } else if (match_pathspec(&pathspec, fullname, strlen(fullname),
+ max_prefix_len, ps_matched,
S_ISDIR(ce->ce_mode) ||
S_ISGITLINK(ce->ce_mode))) {
- if (tag && *tag && show_valid_bit &&
- (ce->ce_flags & CE_VALID)) {
- static char alttag[4];
- memcpy(alttag, tag, 3);
- if (isalpha(tag[0]))
- alttag[0] = tolower(tag[0]);
- else if (tag[0] == '?')
- alttag[0] = '!';
- else {
- alttag[0] = 'v';
- alttag[1] = tag[0];
- alttag[2] = ' ';
- alttag[3] = 0;
- }
- tag = alttag;
- }
+ tag = get_tag(ce, tag);
if (!show_stage) {
fputs(tag, stdout);
@@ -274,30 +243,20 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
find_unique_abbrev(ce->oid.hash, abbrev),
ce_stage(ce));
}
- write_eolinfo(ce, ce->name);
- write_name(ce->name);
- if (debug_mode) {
- const struct stat_data *sd = &ce->ce_stat_data;
-
- printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
- printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
- printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
- printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
- printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
- }
+ write_eolinfo(repo->index, ce, fullname);
+ write_name(fullname);
+ print_debug(ce);
}
-
- strbuf_release(&name);
}
-static void show_ru_info(void)
+static void show_ru_info(const struct index_state *istate)
{
struct string_list_item *item;
- if (!the_index.resolve_undo)
+ if (!istate->resolve_undo)
return;
- for_each_string_list_item(item, the_index.resolve_undo) {
+ for_each_string_list_item(item, istate->resolve_undo) {
const char *path = item->string;
struct resolve_undo_info *ui = item->util;
int i, len;
@@ -319,88 +278,128 @@ static void show_ru_info(void)
}
}
-static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce)
+static int ce_excluded(struct dir_struct *dir, struct index_state *istate,
+ const char *fullname, const struct cache_entry *ce)
{
int dtype = ce_to_dtype(ce);
- return is_excluded(dir, ce->name, &dtype);
+ return is_excluded(dir, istate, fullname, &dtype);
+}
+
+static void construct_fullname(struct strbuf *out, const struct repository *repo,
+ const struct cache_entry *ce)
+{
+ strbuf_reset(out);
+ if (repo->submodule_prefix)
+ strbuf_addstr(out, repo->submodule_prefix);
+ strbuf_addstr(out, ce->name);
}
-static void show_files(struct dir_struct *dir)
+static void show_files(struct repository *repo, struct dir_struct *dir)
{
int i;
+ struct strbuf fullname = STRBUF_INIT;
/* For cached/deleted files we don't need to even do the readdir */
if (show_others || show_killed) {
if (!show_others)
dir->flags |= DIR_COLLECT_KILLED_ONLY;
- fill_directory(dir, &pathspec);
+ fill_directory(dir, repo->index, &pathspec);
if (show_others)
- show_other_files(dir);
+ show_other_files(repo->index, dir);
if (show_killed)
- show_killed_files(dir);
+ show_killed_files(repo->index, dir);
}
if (show_cached || show_stage) {
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < repo->index->cache_nr; i++) {
+ const struct cache_entry *ce = repo->index->cache[i];
+
+ construct_fullname(&fullname, repo, ce);
+
if ((dir->flags & DIR_SHOW_IGNORED) &&
- !ce_excluded(dir, ce))
+ !ce_excluded(dir, repo->index, fullname.buf, ce))
continue;
if (show_unmerged && !ce_stage(ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
- show_ce_entry(ce_stage(ce) ? tag_unmerged :
- (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
+ show_ce(repo, dir, ce, fullname.buf,
+ ce_stage(ce) ? tag_unmerged :
+ (ce_skip_worktree(ce) ? tag_skip_worktree :
+ tag_cached));
}
}
if (show_deleted || show_modified) {
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < repo->index->cache_nr; i++) {
+ const struct cache_entry *ce = repo->index->cache[i];
struct stat st;
int err;
+
+ construct_fullname(&fullname, repo, ce);
+
if ((dir->flags & DIR_SHOW_IGNORED) &&
- !ce_excluded(dir, ce))
+ !ce_excluded(dir, repo->index, fullname.buf, ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
if (ce_skip_worktree(ce))
continue;
- err = lstat(ce->name, &st);
+ err = lstat(fullname.buf, &st);
if (show_deleted && err)
- show_ce_entry(tag_removed, ce);
- if (show_modified && ce_modified(ce, &st, 0))
- show_ce_entry(tag_modified, ce);
+ show_ce(repo, dir, ce, fullname.buf, tag_removed);
+ if (show_modified && ie_modified(repo->index, ce, &st, 0))
+ show_ce(repo, dir, ce, fullname.buf, tag_modified);
}
}
+
+ strbuf_release(&fullname);
}
/*
* Prune the index to only contain stuff starting with "prefix"
*/
-static void prune_cache(const char *prefix, size_t prefixlen)
+static void prune_index(struct index_state *istate,
+ const char *prefix, size_t prefixlen)
{
int pos;
unsigned int first, last;
- if (!prefix)
+ if (!prefix || !istate->cache_nr)
return;
- pos = cache_name_pos(prefix, prefixlen);
+ pos = index_name_pos(istate, prefix, prefixlen);
if (pos < 0)
pos = -pos-1;
first = pos;
- last = active_nr;
+ last = istate->cache_nr;
while (last > first) {
int next = (last + first) >> 1;
- const struct cache_entry *ce = active_cache[next];
+ const struct cache_entry *ce = istate->cache[next];
if (!strncmp(ce->name, prefix, prefixlen)) {
first = next+1;
continue;
}
last = next;
}
- memmove(active_cache, active_cache + pos,
- (last - pos) * sizeof(struct cache_entry *));
- active_nr = last - pos;
+ MOVE_ARRAY(istate->cache, istate->cache + pos, last - pos);
+ istate->cache_nr = last - pos;
+}
+
+static int get_common_prefix_len(const char *common_prefix)
+{
+ int common_prefix_len;
+
+ if (!common_prefix)
+ return 0;
+
+ common_prefix_len = strlen(common_prefix);
+
+ /*
+ * If the prefix has a trailing slash, strip it so that submodules wont
+ * be pruned from the index.
+ */
+ if (common_prefix[common_prefix_len - 1] == '/')
+ common_prefix_len--;
+
+ return common_prefix_len;
}
/*
@@ -411,23 +410,24 @@ static void prune_cache(const char *prefix, size_t prefixlen)
* that were given from the command line. We are not
* going to write this index out.
*/
-void overlay_tree_on_cache(const char *tree_name, const char *prefix)
+void overlay_tree_on_index(struct index_state *istate,
+ const char *tree_name, const char *prefix)
{
struct tree *tree;
- unsigned char sha1[20];
+ struct object_id oid;
struct pathspec pathspec;
struct cache_entry *last_stage0 = NULL;
int i;
- if (get_sha1(tree_name, sha1))
+ if (get_oid(tree_name, &oid))
die("tree-ish %s not found.", tree_name);
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(&oid);
if (!tree)
die("bad tree-ish %s", tree_name);
/* Hoist the unmerged entries up to stage #3 to make room */
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
if (!ce_stage(ce))
continue;
ce->ce_flags |= CE_STAGEMASK;
@@ -440,11 +440,11 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
PATHSPEC_PREFER_CWD, prefix, matchbuf);
} else
memset(&pathspec, 0, sizeof(pathspec));
- if (read_tree(tree, 1, &pathspec))
+ if (read_tree(tree, 1, &pathspec, istate))
die("unable to read tree entries %s", tree_name);
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
switch (ce_stage(ce)) {
case 0:
last_stage0 = ce;
@@ -517,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,
@@ -575,10 +577,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
- super_prefix = get_super_prefix();
git_config(git_default_config, NULL);
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die("index file corrupt");
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
@@ -587,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 ";
@@ -611,9 +612,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
- if (recurse_submodules)
- compile_submodule_options(argv, &dir, show_tag);
-
if (recurse_submodules &&
(show_stage || show_deleted || show_others || show_unmerged ||
show_killed || show_modified || show_resolve_undo || with_tree))
@@ -624,20 +622,24 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
"--error-unmatch");
parse_pathspec(&pathspec, 0,
- PATHSPEC_PREFER_CWD |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ PATHSPEC_PREFER_CWD,
prefix, argv);
/*
* Find common prefix for all pathspec's
* This is used as a performance optimization which unfortunately cannot
- * be done when recursing into submodules
+ * be done when recursing into submodules because when a pathspec is
+ * given which spans repository boundaries you can't simply remove the
+ * submodule entry because the pathspec may match something inside the
+ * submodule.
*/
if (recurse_submodules)
max_prefix = NULL;
else
max_prefix = common_prefix(&pathspec);
- max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
+ max_prefix_len = get_common_prefix_len(max_prefix);
+
+ prune_index(the_repository->index, max_prefix, max_prefix_len);
/* Treat unmatching pathspec elements as errors */
if (pathspec.nr && error_unmatch)
@@ -651,7 +653,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
show_killed || show_modified || show_resolve_undo))
show_cached = 1;
- prune_cache(max_prefix, max_prefix_len);
if (with_tree) {
/*
* Basic sanity check; show-stages and show-unmerged
@@ -659,11 +660,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
*/
if (show_stage || show_unmerged)
die("ls-files --with-tree is incompatible with -s or -u");
- overlay_tree_on_cache(with_tree, max_prefix);
+ overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
}
- show_files(&dir);
+
+ show_files(the_repository, &dir);
+
if (show_resolve_undo)
- show_ru_info();
+ show_ru_info(the_repository->index);
if (ps_matched) {
int bad;
@@ -674,5 +677,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
return bad ? 1 : 0;
}
+ UNLEAK(dir);
return 0;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index b2d7d5ce68..540d56429f 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -24,7 +24,7 @@ static int tail_match(const char **pattern, const char *path)
pathbuf = xstrfmt("/%s", path);
while ((p = *(pattern++)) != NULL) {
- if (!wildmatch(p, pathbuf, 0, NULL)) {
+ if (!wildmatch(p, pathbuf, 0)) {
free(pathbuf);
return 1;
}
@@ -60,8 +60,9 @@ 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_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_END()
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index d7ebeb4ce6..ef965408e8 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
@@ -119,7 +120,7 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,
int cmd_ls_tree(int argc, const char **argv, const char *prefix)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct tree *tree;
int i, full_tree = 0;
const struct option ls_tree_options[] = {
@@ -164,7 +165,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
if (argc < 1)
usage_with_options(ls_tree_usage, ls_tree_options);
- if (get_sha1(argv[0], sha1))
+ if (get_oid(argv[0], &oid))
die("Not a valid object name %s", argv[0]);
/*
@@ -180,7 +181,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
for (i = 0; i < pathspec.nr; i++)
pathspec.items[i].nowildcard_len = pathspec.items[i].len;
pathspec.has_wildcard = 0;
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(&oid);
if (!tree)
die("not a tree object");
return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f8..3b7600150b 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "refs.h"
#include "diff.h"
@@ -8,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;
}
@@ -41,7 +42,7 @@ static struct commit *get_commit_reference(const char *arg)
if (get_oid(arg, &revkey))
die("Not a valid object name %s", arg);
- r = lookup_commit_reference(revkey.hash);
+ r = lookup_commit_reference(&revkey);
if (!r)
die("Not a valid commit name %s", arg);
@@ -50,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;
}
@@ -120,7 +123,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
if (is_null_oid(oid))
return;
- commit = lookup_commit(oid->hash);
+ commit = lookup_commit(oid);
if (!commit ||
(commit->object.flags & TMP_MARK) ||
parse_commit(commit))
@@ -132,7 +135,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
}
static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *ident, unsigned long timestamp,
+ const char *ident, timestamp_t timestamp,
int tz, const char *message, void *cbdata)
{
struct rev_collect *revs = cbdata;
@@ -155,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:
@@ -168,7 +171,7 @@ static int handle_fork_point(int argc, const char **argv)
if (get_oid(commitname, &oid))
die("Not a valid object name: '%s'", commitname);
- derived = lookup_commit_reference(oid.hash);
+ derived = lookup_commit_reference(&oid);
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 47dde7c39c..b08803e611 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
#include "parse-options.h"
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 5b7ab9b967..d01ddecf66 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -161,14 +161,14 @@ static int both_empty(struct name_entry *a, struct name_entry *b)
return !(a->oid || b->oid);
}
-static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
+static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
{
struct merge_list *res = xcalloc(1, sizeof(*res));
res->stage = stage;
res->path = path;
res->mode = mode;
- res->blob = lookup_blob(sha1);
+ res->blob = lookup_blob(oid);
return res;
}
@@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
return;
path = traverse_path(info, result);
- orig = create_entry(2, ours->mode, ours->oid->hash, path);
- final = create_entry(0, result->mode, result->oid->hash, path);
+ orig = create_entry(2, ours->mode, ours->oid, path);
+ final = create_entry(0, result->mode, result->oid, path);
final->link = orig;
@@ -213,11 +213,11 @@ static void unresolved_directory(const struct traverse_info *info,
newbase = traverse_path(info, p);
-#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL)
- buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
- buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
- buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
-#undef ENTRY_SHA1
+#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid : NULL)
+ buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
+ buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
+ buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
+#undef ENTRY_OID
merge_trees(t, newbase);
@@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
path = entry->path;
else
path = traverse_path(info, n);
- link = create_entry(stage, n->mode, n->oid->hash, path);
+ link = create_entry(stage, n->mode, n->oid, path);
link->link = entry;
return link;
}
@@ -347,12 +347,12 @@ static void merge_trees(struct tree_desc t[3], const char *base)
static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
{
- unsigned char sha1[20];
+ struct object_id oid;
void *buf;
- if (get_sha1(rev, sha1))
+ if (get_oid(rev, &oid))
die("unknown rev %s", rev);
- buf = fill_tree_descriptor(desc, sha1);
+ buf = fill_tree_descriptor(desc, &oid);
if (!buf)
die("%s is not a tree", rev);
return buf;
diff --git a/builtin/merge.c b/builtin/merge.c
index 703827f006..ee050a47f3 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -7,6 +7,7 @@
*/
#include "cache.h"
+#include "config.h"
#include "parse-options.h"
#include "builtin.h"
#include "lockfile.h"
@@ -31,6 +32,8 @@
#include "gpg-interface.h"
#include "sequencer.h"
#include "string-list.h"
+#include "packfile.h"
+#include "tag.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -69,7 +72,9 @@ static int continue_current_merge;
static int allow_unrelated_histories;
static int show_progress = -1;
static int default_to_upstream = 1;
+static int signoff;
static const char *sign_commit;
+static int verify_msg = 1;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -232,6 +237,8 @@ static struct option builtin_merge_options[] = {
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+ OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
+ OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
OPT_END()
};
@@ -249,6 +256,7 @@ static int save_state(struct object_id *stash)
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
const char *argv[] = {"stash", "create", NULL};
+ int rc = -1;
cp.argv = argv;
cp.out = -1;
@@ -262,11 +270,14 @@ static int save_state(struct object_id *stash)
if (finish_command(&cp) || len < 0)
die(_("stash failed"));
else if (!len) /* no changes */
- return -1;
+ goto out;
strbuf_setlen(&buffer, buffer.len-1);
if (get_oid(buffer.buf, stash))
die(_("not a valid object: %s"), buffer.buf);
- return 0;
+ rc = 0;
+out:
+ strbuf_release(&buffer);
+ return rc;
}
static void read_empty(unsigned const char *sha1, int verbose)
@@ -395,9 +406,8 @@ 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.
@@ -415,7 +425,7 @@ static void finish(struct commit *head_commit,
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
opts.detect_rename = DIFF_DETECT_RENAME;
diff_setup_done(&opts);
- diff_tree_sha1(head->hash, new_head->hash, "", &opts);
+ diff_tree_oid(head, new_head, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
}
@@ -445,7 +455,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);
@@ -511,7 +521,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
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),
+ type_name(desc->obj->type),
remote);
goto cleanup;
}
@@ -536,7 +546,7 @@ static void parse_branch_merge_options(char *bmo)
die(_("Bad branch.%s.mergeoptions string: %s"), branch,
split_cmdline_strerror(argc));
REALLOC_ARRAY(argv, argc + 2);
- memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+ MOVE_ARRAY(argv + 1, argv, argc + 1);
argc++;
argv[0] = "branch.*.mergeoptions";
parse_options(argc, argv, NULL, builtin_merge_options,
@@ -558,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"))
@@ -565,7 +577,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
else if (!strcmp(k, "merge.renormalize"))
option_renormalize = git_config_bool(k, v);
else if (!strcmp(k, "merge.ff")) {
- int boolval = git_config_maybe_bool(k, v);
+ int boolval = git_parse_maybe_bool(v);
if (0 <= boolval) {
fast_forward = boolval ? FF_ALLOW : FF_NO;
} else if (v && !strcmp(v, "only")) {
@@ -605,13 +617,13 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
opts.merge = 1;
- trees[nr_trees] = parse_tree_indirect(common->hash);
+ trees[nr_trees] = parse_tree_indirect(common);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(head->hash);
+ trees[nr_trees] = parse_tree_indirect(head);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(one->hash);
+ trees[nr_trees] = parse_tree_indirect(one);
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
@@ -640,10 +652,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
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;
@@ -680,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,
@@ -755,13 +765,19 @@ N_("Please enter a commit message to explain why this merge is necessary,\n"
"Lines starting with '%c' will be ignored, and an empty message aborts\n"
"the commit.\n");
+static void write_merge_heads(struct commit_list *);
static void prepare_to_commit(struct commit_list *remoteheads)
{
struct strbuf msg = STRBUF_INIT;
strbuf_addbuf(&msg, &merge_msg);
strbuf_addch(&msg, '\n');
+ if (squash)
+ BUG("the control must not reach here under --squash");
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
+ if (signoff)
+ append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+ write_merge_heads(remoteheads);
write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
git_path_merge_msg(), "merge", NULL))
@@ -770,6 +786,12 @@ static void prepare_to_commit(struct commit_list *remoteheads)
if (launch_editor(git_path_merge_msg(), NULL, NULL))
abort_commit(remoteheads, NULL);
}
+
+ if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
+ "commit-msg",
+ git_path_merge_msg(), NULL))
+ abort_commit(remoteheads, NULL);
+
read_merge_msg(&msg);
strbuf_stripspace(&msg, 0 < option_edit);
if (!msg.len)
@@ -787,18 +809,17 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
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();
@@ -822,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);
@@ -839,9 +860,7 @@ static int suggest_conflicts(void)
struct strbuf msgbuf = STRBUF_INIT;
filename = git_path_merge_msg();
- fp = fopen(filename, "a");
- if (!fp)
- die_errno(_("Could not open '%s' for writing"), filename);
+ fp = xfopen(filename, "a");
append_conflicts_hint(&msgbuf);
fputs(msgbuf.buf, fp);
@@ -905,7 +924,7 @@ static int setup_with_upstream(const char ***argv)
return i;
}
-static void write_merge_state(struct commit_list *remoteheads)
+static void write_merge_heads(struct commit_list *remoteheads)
{
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
@@ -921,13 +940,19 @@ static void write_merge_state(struct commit_list *remoteheads)
strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
}
write_file_buf(git_path_merge_head(), buf.buf, buf.len);
- strbuf_addch(&merge_msg, '\n');
- write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
strbuf_reset(&buf);
if (fast_forward == FF_NO)
strbuf_addstr(&buf, "no-ff");
write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
+ strbuf_release(&buf);
+}
+
+static void write_merge_state(struct commit_list *remoteheads)
+{
+ write_merge_heads(remoteheads);
+ strbuf_addch(&merge_msg, '\n');
+ write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
}
static int default_edit_option(void)
@@ -941,7 +966,7 @@ static int default_edit_option(void)
return 0;
if (e) {
- int v = git_config_maybe_bool(name, e);
+ int v = git_parse_maybe_bool(e);
if (v < 0)
die(_("Bad value '%s' in environment '%s'"), e, name);
return v;
@@ -973,6 +998,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;
@@ -1097,6 +1123,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;
@@ -1117,13 +1180,13 @@ 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);
- if (branch && starts_with(branch, "refs/heads/"))
- branch += 11;
+ branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ if (branch)
+ skip_prefix(branch, "refs/heads/", &branch);
if (!branch || is_null_oid(&head_oid))
head_commit = NULL;
else
- head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
+ head_commit = lookup_commit_or_die(&head_oid, "HEAD");
init_diff_ui_defaults();
git_config(git_merge_config, NULL);
@@ -1235,8 +1298,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
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);
+ update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
goto done;
}
@@ -1294,10 +1357,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;
}
@@ -1331,8 +1391,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. */
@@ -1346,7 +1406,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* If head can reach all the merge then we are up to date.
* but first the most common case of merging one remote.
*/
- finish_up_to_date(_("Already up-to-date."));
+ finish_up_to_date(_("Already up to date."));
goto done;
} else if (fast_forward != FF_NO && !remoteheads->next &&
!common->next &&
@@ -1372,8 +1432,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
- if (checkout_fast_forward(head_commit->object.oid.hash,
- commit->object.oid.hash,
+ if (checkout_fast_forward(&head_commit->object.oid,
+ &commit->object.oid,
overwrite_ignore)) {
ret = 1;
goto done;
@@ -1429,7 +1489,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
}
if (up_to_date) {
- finish_up_to_date(_("Already up-to-date. Yeeah!"));
+ finish_up_to_date(_("Already up to date. Yeeah!"));
goto done;
}
}
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 031b750f06..beb552847b 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -151,7 +151,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 +165,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..f5f3c0eea1 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -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;
@@ -57,7 +57,7 @@ static void write_tree(unsigned char *sha1)
strbuf_add(&buf, ent->sha1, 20);
}
- write_sha1_file(buf.buf, buf.len, tree_type, sha1);
+ write_object_file(buf.buf, buf.len, tree_type, oid);
strbuf_release(&buf);
}
@@ -112,7 +112,7 @@ 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 */
@@ -131,7 +131,7 @@ 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, sha1_to_hex(sha1), type_name(obj_type), type_name(mode_type));
}
}
@@ -142,7 +142,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
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 61d20037ad..6d141f7a53 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -4,6 +4,7 @@
* Copyright (C) 2006 Johannes Schindelin
*/
#include "builtin.h"
+#include "config.h"
#include "pathspec.h"
#include "lockfile.h"
#include "dir.h"
@@ -80,7 +81,7 @@ static void prepare_move_submodule(const char *src, int first,
struct strbuf submodule_dotgit = STRBUF_INIT;
if (!S_ISGITLINK(active_cache[first]->ce_mode))
die(_("Directory %s is in index and no submodule?"), src);
- if (!is_staging_gitmodules_ok())
+ if (!is_staging_gitmodules_ok(&the_index))
die(_("Please stage your changes to .gitmodules or stash them to proceed"));
strbuf_addf(&submodule_dotgit, "%s/.git", src);
*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
@@ -121,7 +122,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,7 +132,6 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
- gitmodules_config();
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, builtin_mv_options,
@@ -286,15 +287,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 1767af750d..9e088ebd11 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "tag.h"
#include "refs.h"
@@ -10,20 +11,20 @@
typedef struct rev_name {
const char *tip_name;
- unsigned long taggerdate;
+ timestamp_t taggerdate;
int generation;
int distance;
int from_tag;
} rev_name;
-static long cutoff = LONG_MAX;
+static timestamp_t cutoff = TIME_MAX;
/* How many generations are maximally preferred over _one_ merge traversal? */
#define MERGE_TRAVERSAL_WEIGHT 65535
static int is_better_name(struct rev_name *name,
const char *tip_name,
- unsigned long taggerdate,
+ timestamp_t taggerdate,
int generation,
int distance,
int from_tag)
@@ -60,7 +61,7 @@ static int is_better_name(struct rev_name *name,
}
static void name_rev(struct commit *commit,
- const char *tip_name, unsigned long taggerdate,
+ const char *tip_name, timestamp_t taggerdate,
int generation, int distance, int from_tag,
int deref)
{
@@ -129,7 +130,7 @@ static int subpath_matches(const char *path, const char *filter)
const char *subpath = path;
while (subpath) {
- if (!wildmatch(filter, subpath, 0, NULL))
+ if (!wildmatch(filter, subpath, 0))
return subpath - path;
subpath = strchr(subpath, '/');
if (subpath)
@@ -158,7 +159,7 @@ struct name_ref_data {
static struct tip_table {
struct tip_table_entry {
- unsigned char sha1[20];
+ struct object_id oid;
const char *refname;
} *table;
int nr;
@@ -166,13 +167,13 @@ static struct tip_table {
int sorted;
} tip_table;
-static void add_to_tip_table(const unsigned char *sha1, const char *refname,
+static void add_to_tip_table(const struct object_id *oid, const char *refname,
int shorten_unambiguous)
{
refname = name_ref_abbrev(refname, shorten_unambiguous);
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
- hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
+ oidcpy(&tip_table.table[tip_table.nr].oid, oid);
tip_table.table[tip_table.nr].refname = xstrdup(refname);
tip_table.nr++;
tip_table.sorted = 0;
@@ -181,16 +182,16 @@ static void add_to_tip_table(const unsigned char *sha1, const char *refname,
static int tipcmp(const void *a_, const void *b_)
{
const struct tip_table_entry *a = a_, *b = b_;
- return hashcmp(a->sha1, b->sha1);
+ return oidcmp(&a->oid, &b->oid);
}
static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
{
- struct object *o = parse_object(oid->hash);
+ struct object *o = parse_object(oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
- unsigned long taggerdate = ULONG_MAX;
+ timestamp_t taggerdate = TIME_MAX;
if (data->tags_only && !starts_with(path, "refs/tags/"))
return 0;
@@ -238,13 +239,13 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
return 0;
}
- add_to_tip_table(oid->hash, path, can_abbreviate_output);
+ add_to_tip_table(oid, path, can_abbreviate_output);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
break; /* broken repository */
- o = parse_object(t->tagged->oid.hash);
+ o = parse_object(&t->tagged->oid);
deref = 1;
taggerdate = t->date;
}
@@ -252,7 +253,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
struct commit *commit = (struct commit *)o;
int from_tag = starts_with(path, "refs/tags/");
- if (taggerdate == ULONG_MAX)
+ if (taggerdate == TIME_MAX)
taggerdate = ((struct commit *)o)->date;
path = name_ref_abbrev(path, can_abbreviate_output);
name_rev(commit, xstrdup(path), taggerdate, 0, 0,
@@ -264,7 +265,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
{
struct tip_table_entry *table = table_;
- return table[ix].sha1;
+ return table[ix].oid.hash;
}
static const char *get_exact_ref_match(const struct object *o)
@@ -349,9 +350,9 @@ static void name_rev_line(char *p, struct name_ref_data *data)
#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
if (!ishex(*p))
forty = 0;
- else if (++forty == 40 &&
+ else if (++forty == GIT_SHA1_HEXSZ &&
!ishex(*(p+1))) {
- unsigned char sha1[40];
+ struct object_id oid;
const char *name = NULL;
char c = *(p+1);
int p_len = p - p_start + 1;
@@ -359,9 +360,9 @@ static void name_rev_line(char *p, struct name_ref_data *data)
forty = 0;
*(p+1) = 0;
- if (!get_sha1(p - 39, sha1)) {
+ if (!get_oid(p - (GIT_SHA1_HEXSZ - 1), &oid)) {
struct object *o =
- lookup_object(sha1);
+ lookup_object(oid.hash);
if (o)
name = get_rev_name(o, &buf);
}
@@ -371,7 +372,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
continue;
if (data->name_only)
- printf("%.*s%s", p_len - 40, p_start, name);
+ printf("%.*s%s", p_len - GIT_SHA1_HEXSZ, p_start, name);
else
printf("%.*s (%s)", p_len, p_start, name);
p_start = p + 1;
@@ -422,18 +423,18 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
cutoff = 0;
for (; argc; argc--, argv++) {
- unsigned char sha1[20];
+ struct object_id oid;
struct object *object;
struct commit *commit;
- if (get_sha1(*argv, sha1)) {
+ if (get_oid(*argv, &oid)) {
fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
*argv);
continue;
}
commit = NULL;
- object = parse_object(sha1);
+ object = parse_object(&oid);
if (object) {
struct object *peeled = deref_tag(object, *argv, 0);
if (peeled && peeled->type == OBJ_COMMIT)
@@ -493,5 +494,6 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
always, allow_undefined, data.name_only);
}
+ UNLEAK(revs);
return 0;
}
diff --git a/builtin/notes.c b/builtin/notes.c
index fb856e53b6..6d2fda4a7d 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -8,10 +8,11 @@
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "notes.h"
#include "blob.h"
-#include "commit.h"
+#include "pretty.h"
#include "refs.h"
#include "exec_cmd.h"
#include "run-command.h"
@@ -32,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
};
@@ -109,11 +110,11 @@ static void free_note_data(struct note_data *d)
strbuf_release(&d->buf);
}
-static int list_each_note(const unsigned char *object_sha1,
- const unsigned char *note_sha1, char *note_path,
+static int list_each_note(const struct object_id *object_oid,
+ const struct object_id *note_oid, char *note_path,
void *cb_data)
{
- printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
+ printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid));
return 0;
}
@@ -129,10 +130,10 @@ static void copy_obj_to_fd(int fd, const unsigned char *sha1)
}
}
-static void write_commented_object(int fd, const unsigned char *object)
+static void write_commented_object(int fd, const struct object_id *object)
{
const char *show_args[5] =
- {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
+ {"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
struct child_process show = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
struct strbuf cbuf = STRBUF_INIT;
@@ -145,7 +146,7 @@ static void write_commented_object(int fd, const unsigned char *object)
show.git_cmd = 1;
if (start_command(&show))
die(_("unable to start 'show' for object '%s'"),
- sha1_to_hex(object));
+ oid_to_hex(object));
if (strbuf_read(&buf, show.out, 0) < 0)
die_errno(_("could not read 'show' output"));
@@ -157,10 +158,10 @@ static void write_commented_object(int fd, const unsigned char *object)
if (finish_command(&show))
die(_("failed to finish 'show' for object '%s'"),
- sha1_to_hex(object));
+ oid_to_hex(object));
}
-static void prepare_note_data(const unsigned char *object, struct note_data *d,
+static void prepare_note_data(const struct object_id *object, struct note_data *d,
const unsigned char *old_note)
{
if (d->use_editor || !d->given) {
@@ -197,9 +198,9 @@ static void prepare_note_data(const unsigned char *object, struct note_data *d,
}
}
-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"),
@@ -243,16 +244,16 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
char *buf;
- unsigned char object[20];
+ struct object_id object;
enum object_type type;
unsigned long len;
if (d->buf.len)
strbuf_addch(&d->buf, '\n');
- if (get_sha1(arg, object))
+ if (get_oid(arg, &object))
die(_("failed to resolve '%s' as a valid ref."), arg);
- if (!(buf = read_sha1_file(object, &type, &len))) {
+ if (!(buf = read_sha1_file(object.hash, &type, &len))) {
free(buf);
die(_("failed to read object '%s'."), arg);
}
@@ -292,7 +293,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
}
while (strbuf_getline_lf(&buf, stdin) != EOF) {
- unsigned char from_obj[20], to_obj[20];
+ struct object_id from_obj, to_obj;
struct strbuf **split;
int err;
@@ -301,15 +302,15 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
die(_("malformed input line: '%s'."), buf.buf);
strbuf_rtrim(split[0]);
strbuf_rtrim(split[1]);
- if (get_sha1(split[0]->buf, from_obj))
+ if (get_oid(split[0]->buf, &from_obj))
die(_("failed to resolve '%s' as a valid ref."), split[0]->buf);
- if (get_sha1(split[1]->buf, to_obj))
+ if (get_oid(split[1]->buf, &to_obj))
die(_("failed to resolve '%s' as a valid ref."), split[1]->buf);
if (rewrite_cmd)
- err = copy_note_for_rewrite(c, from_obj, to_obj);
+ err = copy_note_for_rewrite(c, &from_obj, &to_obj);
else
- err = copy_note(t, from_obj, to_obj, force,
+ err = copy_note(t, &from_obj, &to_obj, force,
combine_notes_overwrite);
if (err) {
@@ -327,6 +328,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
} else {
finish_copy_notes_for_rewrite(c, msg);
}
+ strbuf_release(&buf);
return ret;
}
@@ -352,8 +354,8 @@ static struct notes_tree *init_notes_check(const char *subcommand,
static int list(int argc, const char **argv, const char *prefix)
{
struct notes_tree *t;
- unsigned char object[20];
- const unsigned char *note;
+ struct object_id object;
+ const struct object_id *note;
int retval = -1;
struct option options[] = {
OPT_END()
@@ -370,15 +372,15 @@ static int list(int argc, const char **argv, const char *prefix)
t = init_notes_check("list", 0);
if (argc) {
- if (get_sha1(argv[0], object))
+ if (get_oid(argv[0], &object))
die(_("failed to resolve '%s' as a valid ref."), argv[0]);
- note = get_note(t, object);
+ note = get_note(t, &object);
if (note) {
- puts(sha1_to_hex(note));
+ puts(oid_to_hex(note));
retval = 0;
} else
retval = error(_("no note found for object %s."),
- sha1_to_hex(object));
+ oid_to_hex(&object));
} else
retval = for_each_note(t, 0, list_each_note, NULL);
@@ -393,8 +395,8 @@ static int add(int argc, const char **argv, const char *prefix)
int force = 0, allow_empty = 0;
const char *object_ref;
struct notes_tree *t;
- unsigned char object[20], new_note[20];
- const unsigned char *note;
+ struct object_id object, new_note;
+ const struct object_id *note;
struct note_data d = { 0, 0, NULL, STRBUF_INIT };
struct option options[] = {
{ OPTION_CALLBACK, 'm', "message", &d, N_("message"),
@@ -411,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()
};
@@ -425,11 +427,11 @@ static int add(int argc, const char **argv, const char *prefix)
object_ref = argc > 1 ? argv[1] : "HEAD";
- if (get_sha1(object_ref, object))
+ if (get_oid(object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check("add", NOTES_INIT_WRITABLE);
- note = get_note(t, object);
+ note = get_note(t, &object);
if (note) {
if (!force) {
@@ -439,7 +441,7 @@ static int add(int argc, const char **argv, const char *prefix)
return error(_("Cannot add notes. "
"Found existing notes for object %s. "
"Use '-f' to overwrite existing notes"),
- sha1_to_hex(object));
+ oid_to_hex(&object));
}
/*
* Redirect to "edit" subcommand.
@@ -452,19 +454,19 @@ static int add(int argc, const char **argv, const char *prefix)
return append_edit(argc, argv, prefix);
}
fprintf(stderr, _("Overwriting existing notes for object %s\n"),
- sha1_to_hex(object));
+ oid_to_hex(&object));
}
- prepare_note_data(object, &d, note);
+ prepare_note_data(&object, &d, note ? note->hash : NULL);
if (d.buf.len || allow_empty) {
- write_note_data(&d, new_note);
- if (add_note(t, object, new_note, combine_notes_overwrite))
+ write_note_data(&d, &new_note);
+ if (add_note(t, &object, &new_note, combine_notes_overwrite))
die("BUG: combine_notes_overwrite failed");
commit_notes(t, "Notes added by 'git notes add'");
} else {
fprintf(stderr, _("Removing note for object %s\n"),
- sha1_to_hex(object));
- remove_note(t, object);
+ oid_to_hex(&object));
+ remove_note(t, object.hash);
commit_notes(t, "Notes removed by 'git notes add'");
}
@@ -476,13 +478,13 @@ static int add(int argc, const char **argv, const char *prefix)
static int copy(int argc, const char **argv, const char *prefix)
{
int retval = 0, force = 0, from_stdin = 0;
- const unsigned char *from_note, *note;
+ const struct object_id *from_note, *note;
const char *object_ref;
- unsigned char object[20], from_obj[20];
+ struct object_id object, from_obj;
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 "
@@ -511,37 +513,37 @@ static int copy(int argc, const char **argv, const char *prefix)
usage_with_options(git_notes_copy_usage, options);
}
- if (get_sha1(argv[0], from_obj))
+ if (get_oid(argv[0], &from_obj))
die(_("failed to resolve '%s' as a valid ref."), argv[0]);
object_ref = 1 < argc ? argv[1] : "HEAD";
- if (get_sha1(object_ref, object))
+ if (get_oid(object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check("copy", NOTES_INIT_WRITABLE);
- note = get_note(t, object);
+ note = get_note(t, &object);
if (note) {
if (!force) {
retval = error(_("Cannot copy notes. Found existing "
"notes for object %s. Use '-f' to "
"overwrite existing notes"),
- sha1_to_hex(object));
+ oid_to_hex(&object));
goto out;
}
fprintf(stderr, _("Overwriting existing notes for object %s\n"),
- sha1_to_hex(object));
+ oid_to_hex(&object));
}
- from_note = get_note(t, from_obj);
+ from_note = get_note(t, &from_obj);
if (!from_note) {
retval = error(_("missing notes on source object %s. Cannot "
- "copy."), sha1_to_hex(from_obj));
+ "copy."), oid_to_hex(&from_obj));
goto out;
}
- if (add_note(t, object, from_note, combine_notes_overwrite))
+ if (add_note(t, &object, from_note, combine_notes_overwrite))
die("BUG: combine_notes_overwrite failed");
commit_notes(t, "Notes added by 'git notes copy'");
out:
@@ -554,8 +556,8 @@ static int append_edit(int argc, const char **argv, const char *prefix)
int allow_empty = 0;
const char *object_ref;
struct notes_tree *t;
- unsigned char object[20], new_note[20];
- const unsigned char *note;
+ struct object_id object, new_note;
+ const struct object_id *note;
char *logmsg;
const char * const *usage;
struct note_data d = { 0, 0, NULL, STRBUF_INIT };
@@ -594,19 +596,19 @@ static int append_edit(int argc, const char **argv, const char *prefix)
object_ref = 1 < argc ? argv[1] : "HEAD";
- if (get_sha1(object_ref, object))
+ if (get_oid(object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
- note = get_note(t, object);
+ note = get_note(t, &object);
- prepare_note_data(object, &d, edit ? note : NULL);
+ prepare_note_data(&object, &d, edit && note ? note->hash : NULL);
if (note && !edit) {
/* Append buf to previous note contents */
unsigned long size;
enum object_type type;
- char *prev_buf = read_sha1_file(note, &type, &size);
+ char *prev_buf = read_sha1_file(note->hash, &type, &size);
strbuf_grow(&d.buf, size + 1);
if (d.buf.len && prev_buf && size)
@@ -617,14 +619,14 @@ static int append_edit(int argc, const char **argv, const char *prefix)
}
if (d.buf.len || allow_empty) {
- write_note_data(&d, new_note);
- if (add_note(t, object, new_note, combine_notes_overwrite))
+ write_note_data(&d, &new_note);
+ if (add_note(t, &object, &new_note, combine_notes_overwrite))
die("BUG: combine_notes_overwrite failed");
logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
} else {
fprintf(stderr, _("Removing note for object %s\n"),
- sha1_to_hex(object));
- remove_note(t, object);
+ oid_to_hex(&object));
+ remove_note(t, object.hash);
logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
}
commit_notes(t, logmsg);
@@ -639,8 +641,8 @@ static int show(int argc, const char **argv, const char *prefix)
{
const char *object_ref;
struct notes_tree *t;
- unsigned char object[20];
- const unsigned char *note;
+ struct object_id object;
+ const struct object_id *note;
int retval;
struct option options[] = {
OPT_END()
@@ -656,17 +658,17 @@ static int show(int argc, const char **argv, const char *prefix)
object_ref = argc ? argv[0] : "HEAD";
- if (get_sha1(object_ref, object))
+ if (get_oid(object_ref, &object))
die(_("failed to resolve '%s' as a valid ref."), object_ref);
t = init_notes_check("show", 0);
- note = get_note(t, object);
+ note = get_note(t, &object);
if (!note)
retval = error(_("no note found for object %s."),
- sha1_to_hex(object));
+ oid_to_hex(&object));
else {
- const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
+ const char *show_args[3] = {"show", oid_to_hex(note), NULL};
retval = execv_git_cmd(show_args);
}
free_notes(t);
@@ -684,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"));
@@ -708,7 +710,7 @@ static int merge_commit(struct notes_merge_options *o)
if (get_oid("NOTES_MERGE_PARTIAL", &oid))
die(_("failed to read ref NOTES_MERGE_PARTIAL"));
- else if (!(partial = lookup_commit_reference(oid.hash)))
+ else if (!(partial = lookup_commit_reference(&oid)))
die(_("could not find commit from NOTES_MERGE_PARTIAL."));
else if (parse_commit(partial))
die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
@@ -722,11 +724,11 @@ 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"));
- if (notes_merge_commit(o, t, partial, oid.hash))
+ if (notes_merge_commit(o, t, partial, &oid))
die(_("failed to finalize notes merge"));
/* Reuse existing commit message in reflog message */
@@ -734,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);
@@ -762,7 +764,7 @@ static int git_config_get_notes_strategy(const char *key,
static int merge(int argc, const char **argv, const char *prefix)
{
struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
- unsigned char result_sha1[20];
+ struct object_id result_oid;
struct notes_tree *t;
struct notes_merge_options o;
int do_merge = 0, do_commit = 0, do_abort = 0;
@@ -844,16 +846,16 @@ static int merge(int argc, const char **argv, const char *prefix)
remote_ref.buf, default_notes_ref());
strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */
- result = notes_merge(&o, t, result_sha1);
+ result = notes_merge(&o, t, &result_oid);
- if (result >= 0) /* Merge resulted (trivially) in result_sha1 */
+ if (result >= 0) /* Merge resulted (trivially) in result_oid */
/* Update default notes ref with new commit */
- update_ref(msg.buf, default_notes_ref(), result_sha1, 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_sha1, 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());
@@ -863,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);
@@ -880,10 +882,10 @@ static int merge(int argc, const char **argv, const char *prefix)
static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
{
int status;
- unsigned char sha1[20];
- if (get_sha1(name, sha1))
+ struct object_id oid;
+ if (get_oid(name, &oid))
return error(_("Failed to resolve '%s' as a valid ref."), name);
- status = remove_note(t, sha1);
+ status = remove_note(t, oid.hash);
if (status)
fprintf(stderr, _("Object %s has no note\n"), name);
else
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 50e01aa80e..e9d3cfb9e3 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "attr.h"
#include "object.h"
#include "blob.h"
@@ -14,6 +15,8 @@
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
+#include "list-objects-filter.h"
+#include "list-objects-filter-options.h"
#include "pack-objects.h"
#include "progress.h"
#include "refs.h"
@@ -23,7 +26,8 @@
#include "reachable.h"
#include "sha1-array.h"
#include "argv-array.h"
-#include "mru.h"
+#include "list.h"
+#include "packfile.h"
static const char *pack_usage[] = {
N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
@@ -44,7 +48,7 @@ static uint32_t nr_result, nr_written;
static int non_empty;
static int reuse_delta = 1, reuse_object = 1;
static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
static int pack_loose_unreachable;
static int local;
static int have_non_local_packs;
@@ -71,12 +75,24 @@ static int use_bitmap_index = -1;
static int write_bitmap_index;
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 cache_max_small_delta_size = 1000;
static unsigned long window_memory_limit = 0;
+static struct list_objects_filter_options filter_options;
+
+enum missing_action {
+ MA_ERROR = 0, /* fail if any missing objects are encountered */
+ MA_ALLOW_ANY, /* silently allow ALL missing objects */
+ MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
+};
+static enum missing_action arg_missing_action;
+static show_object_fn fn_show_object;
+
/*
* stats
*/
@@ -106,12 +122,14 @@ static void *get_delta(struct object_entry *entry)
void *buf, *base_buf, *delta_buf;
enum object_type type;
- buf = read_sha1_file(entry->idx.sha1, &type, &size);
+ buf = read_sha1_file(entry->idx.oid.hash, &type, &size);
if (!buf)
- die("unable to read %s", sha1_to_hex(entry->idx.sha1));
- base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
+ die("unable to read %s", oid_to_hex(&entry->idx.oid));
+ base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type,
+ &base_size);
if (!base_buf)
- die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
+ die("unable to read %s",
+ oid_to_hex(&entry->delta->idx.oid));
delta_buf = diff_delta(base_buf, base_size,
buf, size, &delta_size, 0);
if (!delta_buf || delta_size != entry->delta_size)
@@ -146,8 +164,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];
@@ -161,7 +179,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;
@@ -170,7 +188,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)
@@ -215,7 +233,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,
@@ -228,14 +246,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;
@@ -249,19 +267,20 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
if (!usable_delta) {
if (entry->type == OBJ_BLOB &&
entry->size > big_file_threshold &&
- (st = open_istream(entry->idx.sha1, &type, &size, NULL)) != NULL)
+ (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL)
buf = NULL;
else {
- buf = read_sha1_file(entry->idx.sha1, &type, &size);
+ buf = read_sha1_file(entry->idx.oid.hash, &type,
+ &size);
if (!buf)
- die(_("unable to read %s"), sha1_to_hex(entry->idx.sha1));
+ die(_("unable to read %s"),
+ oid_to_hex(&entry->idx.oid));
}
/*
* make sure no cached delta data remains from a
* previous attempt before a pack split occurred.
*/
- free(entry->delta_data);
- entry->delta_data = NULL;
+ FREE_AND_NULL(entry->delta_data);
entry->z_delta_size = 0;
} else if (entry->delta_data) {
size = entry->delta_size;
@@ -307,8 +326,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
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) {
/*
@@ -321,8 +340,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
free(buf);
return 0;
}
- sha1write(f, header, hdrlen);
- sha1write(f, entry->delta->idx.sha1, 20);
+ hashwrite(f, header, hdrlen);
+ hashwrite(f, entry->delta->idx.oid.hash, 20);
hdrlen += 20;
} else {
if (limit && hdrlen + datalen + 20 >= limit) {
@@ -331,13 +350,13 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
free(buf);
return 0;
}
- sha1write(f, header, hdrlen);
+ hashwrite(f, header, hdrlen);
}
if (st) {
- datalen = write_large_blob_data(st, f, entry->idx.sha1);
+ datalen = write_large_blob_data(st, f, &entry->idx.oid);
close_istream(st);
} else {
- sha1write(f, buf, datalen);
+ hashwrite(f, buf, datalen);
free(buf);
}
@@ -345,7 +364,7 @@ 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;
@@ -369,7 +388,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
datalen = revidx[1].offset - offset;
if (!pack_to_stdout && p->index_version > 1 &&
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
- error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+ error("bad packed object CRC for %s",
+ oid_to_hex(&entry->idx.oid));
unuse_pack(&w_curs);
return write_no_reuse_object(f, entry, limit, usable_delta);
}
@@ -379,7 +399,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
if (!pack_to_stdout && p->index_version == 1 &&
check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
- error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
+ error("corrupt packed object for %s",
+ oid_to_hex(&entry->idx.oid));
unuse_pack(&w_curs);
return write_no_reuse_object(f, entry, limit, usable_delta);
}
@@ -394,8 +415,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
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) {
@@ -403,8 +424,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
unuse_pack(&w_curs);
return 0;
}
- sha1write(f, header, hdrlen);
- sha1write(f, entry->delta->idx.sha1, 20);
+ hashwrite(f, header, hdrlen);
+ hashwrite(f, entry->delta->idx.oid.hash, 20);
hdrlen += 20;
reused_delta++;
} else {
@@ -412,7 +433,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
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);
@@ -421,7 +442,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)
{
@@ -494,7 +515,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)
{
@@ -509,7 +530,7 @@ static enum write_one_status write_one(struct sha1file *f,
recursing = (e->idx.offset == 1);
if (recursing) {
warning("recursive delta detected for object %s",
- sha1_to_hex(e->idx.sha1));
+ oid_to_hex(&e->idx.oid));
return WRITE_ONE_RECURSIVE;
} else if (e->idx.offset || e->preferred_base) {
/* offset is non zero if object is written already. */
@@ -550,13 +571,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;
}
@@ -713,7 +734,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;
@@ -744,7 +765,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;
/*
@@ -773,7 +794,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;
@@ -785,11 +806,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);
@@ -816,13 +837,13 @@ static void write_pack_file(void)
* If so, rewrite it like in fast-import
*/
if (pack_to_stdout) {
- sha1close(f, sha1, CSUM_CLOSE);
+ hashclose(f, oid.hash, CSUM_CLOSE);
} else if (nr_written == nr_remaining) {
- sha1close(f, sha1, CSUM_FSYNC);
+ hashclose(f, oid.hash, CSUM_FSYNC);
} else {
- int fd = sha1close(f, sha1, 0);
- fixup_pack_header_footer(fd, sha1, pack_tmp_name,
- nr_written, sha1, offset);
+ int fd = hashclose(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));
@@ -856,16 +877,16 @@ static void write_pack_file(void)
strbuf_addf(&tmpname, "%s-", base_name);
if (write_bitmap_index) {
- bitmap_writer_set_checksum(sha1);
+ bitmap_writer_set_checksum(oid.hash);
bitmap_writer_build_type_index(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);
@@ -880,7 +901,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 */
@@ -921,13 +942,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;
@@ -983,15 +1004,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->hash))
return 0;
/*
@@ -1005,14 +1026,14 @@ static int want_object_in_pack(const unsigned char *sha1,
return want;
}
- for (entry = packed_git_mru->head; entry; entry = entry->next) {
- struct packed_git *p = entry->item;
+ list_for_each(pos, &packed_git_mru) {
+ 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) {
@@ -1023,7 +1044,7 @@ 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, &packed_git_mru);
if (want != -1)
return want;
}
@@ -1032,7 +1053,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,
@@ -1043,7 +1064,7 @@ 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;
@@ -1063,17 +1084,17 @@ 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))
+ 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));
@@ -1082,7 +1103,7 @@ 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);
@@ -1090,27 +1111,27 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
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))
+ if (have_duplicate_entry(oid, 0, &index_pos))
return 0;
- if (!want_object_in_pack(sha1, 0, &pack, &offset))
+ if (!want_object_in_pack(oid, 0, &pack, &offset))
return 0;
- create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
+ create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
display_progress(progress_state, nr_result);
return 1;
}
struct pbase_tree_cache {
- unsigned char sha1[20];
+ struct object_id oid;
int ref;
int temporary;
void *tree_data;
@@ -1118,9 +1139,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)
{
@@ -1137,14 +1158,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.
@@ -1153,7 +1174,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;
}
@@ -1169,7 +1190,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_sha1_file(oid->hash, &type, &size);
if (!data)
return NULL;
if (type != OBJ_TREE) {
@@ -1195,7 +1216,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;
@@ -1240,7 +1261,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;
@@ -1251,7 +1272,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);
@@ -1270,7 +1291,7 @@ static int done_pbase_path_pos(unsigned hash)
int lo = 0;
int hi = done_pbase_paths_num;
while (lo < hi) {
- int mi = (hi + lo) / 2;
+ int mi = lo + (hi - lo) / 2;
if (done_pbase_paths[mi] == hash)
return mi;
if (done_pbase_paths[mi] < hash)
@@ -1283,7 +1304,7 @@ static int done_pbase_path_pos(unsigned hash)
static int check_pbase_path(unsigned hash)
{
- int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
+ int pos = done_pbase_path_pos(hash);
if (0 <= pos)
return 1;
pos = -pos - 1;
@@ -1292,9 +1313,8 @@ static int check_pbase_path(unsigned hash)
done_pbase_paths_alloc);
done_pbase_paths_num++;
if (pos < done_pbase_paths_num)
- memmove(done_pbase_paths + pos + 1,
- done_pbase_paths + pos,
- (done_pbase_paths_num - pos - 1) * sizeof(unsigned));
+ MOVE_ARRAY(done_pbase_paths + pos + 1, done_pbase_paths + pos,
+ done_pbase_paths_num - pos - 1);
done_pbase_paths[pos] = hash;
return 0;
}
@@ -1311,7 +1331,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;
@@ -1321,22 +1341,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->hash, tree_type, &size, tree_oid.hash);
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;
}
@@ -1346,7 +1366,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;
}
@@ -1359,22 +1379,20 @@ 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++) {
if (!pbase_tree_cache[i])
continue;
free(pbase_tree_cache[i]->tree_data);
- free(pbase_tree_cache[i]);
- pbase_tree_cache[i] = NULL;
+ FREE_AND_NULL(pbase_tree_cache[i]);
}
- free(done_pbase_paths);
- done_pbase_paths = NULL;
+ FREE_AND_NULL(done_pbase_paths);
done_pbase_paths_num = done_pbase_paths_alloc = 0;
}
@@ -1432,7 +1450,7 @@ static void check_object(struct object_entry *entry)
ofs += 1;
if (!ofs || MSB(ofs, 7)) {
error("delta base offset overflow in pack for %s",
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
goto give_up;
}
c = buf[used_0++];
@@ -1441,7 +1459,7 @@ static void check_object(struct object_entry *entry)
ofs = entry->in_pack_offset - ofs;
if (ofs <= 0 || ofs >= entry->in_pack_offset) {
error("delta base offset out of bound for %s",
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
goto give_up;
}
if (reuse_delta && !entry->preferred_base) {
@@ -1498,7 +1516,7 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
}
- entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
+ 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
@@ -1514,7 +1532,7 @@ static int pack_offset_sort(const void *_a, const void *_b)
/* avoid filesystem trashing with loose objects */
if (!a->in_pack && !b->in_pack)
- return hashcmp(a->idx.sha1, b->idx.sha1);
+ return oidcmp(&a->idx.oid, &b->idx.oid);
if (a->in_pack < b->in_pack)
return -1;
@@ -1560,7 +1578,8 @@ static void drop_reused_delta(struct object_entry *entry)
* And if that fails, the error will be recorded in entry->type
* and dealt with in prepare_pack().
*/
- entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
+ entry->type = sha1_object_info(entry->idx.oid.hash,
+ &entry->size);
}
}
@@ -1852,26 +1871,29 @@ 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.sha1, &type, &sz);
+ trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type,
+ &sz);
read_unlock();
if (!trg->data)
die("object %s cannot be read",
- sha1_to_hex(trg_entry->idx.sha1));
+ oid_to_hex(&trg_entry->idx.oid));
if (sz != trg_size)
die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
+ oid_to_hex(&trg_entry->idx.oid), sz,
+ trg_size);
*mem_usage += sz;
}
if (!src->data) {
read_lock();
- src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
+ src->data = read_sha1_file(src_entry->idx.oid.hash, &type,
+ &sz);
read_unlock();
if (!src->data) {
if (src_entry->preferred_base) {
static int warned = 0;
if (!warned++)
warning("object %s cannot be read",
- sha1_to_hex(src_entry->idx.sha1));
+ oid_to_hex(&src_entry->idx.oid));
/*
* Those objects are not included in the
* resulting pack. Be resilient and ignore
@@ -1881,11 +1903,12 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
}
die("object %s cannot be read",
- sha1_to_hex(src_entry->idx.sha1));
+ oid_to_hex(&src_entry->idx.oid));
}
if (sz != src_size)
die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(src_entry->idx.sha1), sz, src_size);
+ oid_to_hex(&src_entry->idx.oid), sz,
+ src_size);
*mem_usage += sz;
}
if (!src->index) {
@@ -1959,8 +1982,7 @@ static unsigned long free_unpacked(struct unpacked *n)
n->index = NULL;
if (n->data) {
freed_mem += n->entry->size;
- free(n->data);
- n->data = NULL;
+ FREE_AND_NULL(n->data);
}
n->entry = NULL;
n->depth = 0;
@@ -2163,7 +2185,10 @@ static void *threaded_find_deltas(void *arg)
{
struct thread_params *me = arg;
+ progress_lock();
while (me->remaining) {
+ progress_unlock();
+
find_deltas(me->list, &me->remaining,
me->window, me->depth, me->processed);
@@ -2185,7 +2210,10 @@ static void *threaded_find_deltas(void *arg)
pthread_cond_wait(&me->cond, &me->mutex);
me->data_ready = 0;
pthread_mutex_unlock(&me->mutex);
+
+ progress_lock();
}
+ progress_unlock();
/* leave ->working 1 so that this doesn't get more work assigned */
return NULL;
}
@@ -2337,13 +2365,13 @@ static void add_tag_chain(const struct object_id *oid)
if (packlist_find(&to_pack, oid->hash, NULL))
return;
- tag = lookup_tag(oid->hash);
+ tag = lookup_tag(oid);
while (1) {
if (!tag || parse_tag(tag) || !tag->tagged)
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;
@@ -2357,7 +2385,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;
@@ -2406,7 +2434,7 @@ static void prepare_pack(int window, int depth)
nr_deltas++;
if (entry->type < 0)
die("unable to get type of object %s",
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
} else {
if (entry->type < 0) {
/*
@@ -2472,8 +2500,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)
die("invalid number of threads specified (%d)",
delta_search_threads);
#ifdef NO_PTHREADS
- if (delta_search_threads != 1)
+ if (delta_search_threads != 1) {
warning("no threads support, ignoring %s", k);
+ delta_search_threads = 0;
+ }
#endif
return 0;
}
@@ -2489,8 +2519,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)) {
@@ -2504,25 +2535,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, 0, 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)
@@ -2532,13 +2564,71 @@ 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;
}
+static void show_object__ma_allow_any(struct object *obj, const char *name, void *data)
+{
+ assert(arg_missing_action == MA_ALLOW_ANY);
+
+ /*
+ * Quietly ignore ALL missing objects. This avoids problems with
+ * staging them now and getting an odd error later.
+ */
+ if (!has_object_file(&obj->oid))
+ return;
+
+ show_object(obj, name, data);
+}
+
+static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data)
+{
+ assert(arg_missing_action == MA_ALLOW_PROMISOR);
+
+ /*
+ * Quietly ignore EXPECTED missing objects. This avoids problems with
+ * staging them now and getting an odd error later.
+ */
+ if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid))
+ return;
+
+ show_object(obj, name, data);
+}
+
+static int option_parse_missing_action(const struct option *opt,
+ const char *arg, int unset)
+{
+ assert(arg);
+ assert(!unset);
+
+ if (!strcmp(arg, "error")) {
+ arg_missing_action = MA_ERROR;
+ fn_show_object = show_object;
+ return 0;
+ }
+
+ if (!strcmp(arg, "allow-any")) {
+ arg_missing_action = MA_ALLOW_ANY;
+ fetch_if_missing = 0;
+ fn_show_object = show_object__ma_allow_any;
+ return 0;
+ }
+
+ if (!strcmp(arg, "allow-promisor")) {
+ arg_missing_action = MA_ALLOW_PROMISOR;
+ fetch_if_missing = 0;
+ fn_show_object = show_object__ma_allow_promisor;
+ return 0;
+ }
+
+ die(_("invalid value for --missing"));
+ return 0;
+}
+
static void show_edge(struct commit *commit)
{
- add_preferred_base(commit->object.oid.hash);
+ add_preferred_base(&commit->object.oid);
}
struct in_pack_object {
@@ -2547,8 +2637,8 @@ struct in_pack_object {
};
struct in_pack {
- int alloc;
- int nr;
+ unsigned int alloc;
+ unsigned int nr;
struct in_pack_object *array;
};
@@ -2585,7 +2675,7 @@ 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;
+ struct object_id oid;
struct object *o;
if (!p->pack_local || p->pack_keep)
@@ -2598,8 +2688,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;
@@ -2610,7 +2700,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);
@@ -2626,7 +2716,7 @@ static int add_loose_object(const struct object_id *oid, const char *path,
return 0;
}
- add_object_entry(oid->hash, type, "", 0);
+ add_object_entry(oid, type, "", 0);
return 0;
}
@@ -2642,7 +2732,7 @@ 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;
@@ -2651,7 +2741,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
while (p) {
if ((!p->pack_local || p->pack_keep) &&
- find_pack_entry_one(sha1, p)) {
+ find_pack_entry_one(oid->hash, p)) {
last_found = p;
return 1;
}
@@ -2675,7 +2765,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
static struct oid_array recent_objects;
static int loosened_object_can_be_discarded(const struct object_id *oid,
- unsigned long mtime)
+ timestamp_t mtime)
{
if (!unpack_unreachable_expiration)
return 0;
@@ -2702,9 +2792,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");
}
}
@@ -2781,10 +2871,10 @@ static void get_object_list(int ac, const char **av)
continue;
}
if (starts_with(line, "--shallow ")) {
- unsigned char sha1[20];
- if (get_sha1_hex(line + 10, sha1))
+ struct object_id oid;
+ if (get_oid_hex(line + 10, &oid))
die("not an SHA-1 '%s'", line + 10);
- register_shallow(sha1);
+ register_shallow(&oid);
use_bitmap_index = 0;
continue;
}
@@ -2800,7 +2890,12 @@ static void get_object_list(int ac, const char **av)
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(&revs, show_edge);
- traverse_commit_list(&revs, show_commit, show_object, NULL);
+
+ if (!fn_show_object)
+ fn_show_object = show_object;
+ traverse_commit_list_filtered(&filter_options, &revs,
+ show_commit, fn_show_object, NULL,
+ NULL);
if (unpack_unreachable_expiration) {
revs.ignore_missing_links = 1;
@@ -2936,6 +3031,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
N_("use a bitmap index if available to speed up counting objects")),
OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
N_("write a bitmap index together with the pack index")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ { OPTION_CALLBACK, 0, "missing", NULL, N_("action"),
+ N_("handling for missing objects"), PARSE_OPT_NONEG,
+ option_parse_missing_action },
+ OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+ N_("do not pack objects in promisor packfiles")),
OPT_END(),
};
@@ -2981,6 +3082,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
argv_array_push(&rp, "--unpacked");
}
+ if (exclude_promisor_objects) {
+ use_internal_rev_list = 1;
+ fetch_if_missing = 0;
+ argv_array_push(&rp, "--exclude-promisor-objects");
+ }
+
if (!reuse_object)
reuse_delta = 0;
if (pack_compression_level == -1)
@@ -3012,6 +3119,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!rev_list_all || !rev_list_reflog || !rev_list_index)
unpack_unreachable_expiration = 0;
+ if (filter_options.choice) {
+ if (!pack_to_stdout)
+ die("cannot use --filter without --stdout.");
+ use_bitmap_index = 0;
+ }
+
/*
* "soft" reasons not to use bitmaps - for on-disk repack by default we want
*
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index cb1df1c761..991e1bb76f 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -7,6 +7,7 @@
*/
#include "builtin.h"
+#include "packfile.h"
#define BLKSIZE 512
@@ -47,17 +48,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)
@@ -79,26 +80,26 @@ 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->sha1 = list->front->sha1;
- 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->sha1 = old_item->sha1;
+ old_item = old_item->next;
}
- new->next = NULL;
- ret->back = new;
+ new_item->next = NULL;
+ ret->back = new_item;
return ret;
}
@@ -107,24 +108,24 @@ static inline struct llist_item *llist_insert(struct llist *list,
struct llist_item *after,
const unsigned char *sha1)
{
- struct llist_item *new = llist_item_get();
- new->sha1 = sha1;
- new->next = NULL;
+ struct llist_item *new_item = llist_item_get();
+ new_item->sha1 = sha1;
+ 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,
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 81552e02e4..970d0d30b4 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index c026299e78..419238171d 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -2,6 +2,7 @@
#include "cache.h"
#include "progress.h"
#include "parse-options.h"
+#include "packfile.h"
static const char * const prune_packed_usage[] = {
N_("git prune-packed [-n | --dry-run] [-q | --quiet]"),
@@ -10,7 +11,7 @@ static const char * const prune_packed_usage[] = {
static struct progress *progress;
-static int prune_subdir(int nr, const char *path, void *data)
+static int prune_subdir(unsigned int nr, const char *path, void *data)
{
int *opts = data;
display_progress(progress, nr + 1);
@@ -37,8 +38,7 @@ static int prune_object(const struct object_id *oid, const char *path,
void prune_packed_objects(int opts)
{
if (opts & PRUNE_PACKED_VERBOSE)
- progress = start_progress_delay(_("Removing duplicate objects"),
- 256, 95, 2);
+ progress = start_delayed_progress(_("Removing duplicate objects"), 256);
for_each_loose_file_in_objdir(get_object_directory(),
prune_object, NULL, prune_subdir, &opts);
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e..4394d01c93 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -8,12 +8,12 @@
#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;
static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
static int show_progress = -1;
static int prune_tmp_file(const char *fullpath)
@@ -52,7 +52,7 @@ static int prune_object(const struct object_id *oid, const char *fullpath,
if (show_only || verbose) {
enum object_type type = sha1_object_info(oid->hash, 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);
@@ -68,7 +68,7 @@ static int prune_cruft(const char *basename, const char *path, void *data)
return 0;
}
-static int prune_subdir(int nr, const char *path, void *data)
+static int prune_subdir(unsigned int nr, const char *path, void *data)
{
if (!show_only)
rmdir(path);
@@ -101,17 +101,20 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct progress *progress = NULL;
+ int exclude_promisor_objects = 0;
const struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned objects")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire objects older than <time>")),
+ OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+ N_("limit traversal to objects outside promisor packfiles")),
OPT_END()
};
char *s;
- expire = ULONG_MAX;
+ expire = TIME_MAX;
save_commit_buffer = 0;
check_replace_refs = 0;
ref_paranoia = 1;
@@ -123,11 +126,12 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
die(_("cannot prune in a precious-objects repo"));
while (argc--) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *name = *argv++;
- if (!get_sha1(name, sha1)) {
- struct object *object = parse_object_or_die(sha1, name);
+ if (!get_oid(name, &oid)) {
+ struct object *object = parse_object_or_die(&oid,
+ name);
add_pending_object(&revs, object, "");
}
else
@@ -137,7 +141,11 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
if (show_progress == -1)
show_progress = isatty(2);
if (show_progress)
- progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
+ progress = start_delayed_progress(_("Checking connectivity"), 0);
+ if (exclude_promisor_objects) {
+ fetch_if_missing = 0;
+ revs.exclude_promisor_objects = 1;
+ }
mark_reachable_objects(&revs, 1, expire, progress);
stop_progress(&progress);
diff --git a/builtin/pull.c b/builtin/pull.c
index 42f0560252..e32d6cd5b4 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -6,6 +6,7 @@
* Fetch one or more remote refs and merge it/them into the current HEAD.
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "parse-options.h"
#include "exec_cmd.h"
@@ -15,6 +16,8 @@
#include "dir.h"
#include "refs.h"
#include "revision.h"
+#include "submodule.h"
+#include "submodule-config.h"
#include "tempfile.h"
#include "lockfile.h"
#include "wt-status.h"
@@ -36,7 +39,7 @@ enum rebase_type {
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
{
- int v = git_config_maybe_bool("pull.rebase", value);
+ int v = git_parse_maybe_bool(value);
if (!v)
return REBASE_FALSE;
@@ -77,11 +80,13 @@ static const char * const pull_usage[] = {
/* Shared options */
static int opt_verbosity;
static char *opt_progress;
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
/* Options passed to git-merge or git-rebase */
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;
@@ -101,7 +106,6 @@ static char *opt_upload_pack;
static int opt_force;
static char *opt_tags;
static char *opt_prune;
-static char *opt_recurse_submodules;
static char *max_children;
static int opt_dry_run;
static char *opt_keep;
@@ -109,6 +113,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 */
@@ -116,6 +122,10 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
N_("force progress reporting"),
PARSE_OPT_NOARG),
+ { OPTION_CALLBACK, 0, "recurse-submodules",
+ &recurse_submodules, N_("on-demand"),
+ N_("control for recursive fetching of submodules"),
+ PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
@@ -135,6 +145,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),
@@ -180,17 +193,13 @@ 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),
OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
N_("prune remote-tracking branches no longer on remote"),
PARSE_OPT_NOARG),
- OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules,
- N_("on-demand"),
- N_("control recursive fetching of submodules"),
- PARSE_OPT_OPTARG),
OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
N_("number of submodules pulled in parallel"),
PARSE_OPT_OPTARG),
@@ -211,6 +220,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()
};
@@ -271,7 +286,7 @@ static const char *config_get_ff(void)
if (git_config_get_value("pull.ff", &value))
return NULL;
- switch (git_config_maybe_bool("pull.ff", value)) {
+ switch (git_parse_maybe_bool(value)) {
case 0:
return "--no-ff";
case 1:
@@ -322,6 +337,10 @@ static int git_pull_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "rebase.autostash")) {
config_autostash = git_config_bool(var, value);
return 0;
+ } else if (!strcmp(var, "submodule.recurse")) {
+ recurse_submodules = git_config_bool(var, value) ?
+ RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+ return 0;
}
return git_default_config(var, value, cb);
}
@@ -337,8 +356,7 @@ static void get_merge_heads(struct oid_array *merge_heads)
struct strbuf sb = STRBUF_INIT;
struct object_id oid;
- if (!(fp = fopen(filename, "r")))
- die_errno(_("could not open '%s' for reading"), filename);
+ fp = xfopen(filename, "r");
while (strbuf_getline_lf(&sb, fp) != EOF) {
if (get_oid_hex(sb.buf, &oid))
continue; /* invalid line: does not start with SHA1 */
@@ -484,8 +502,20 @@ static int run_fetch(const char *repo, const char **refspecs)
argv_array_push(&args, opt_tags);
if (opt_prune)
argv_array_push(&args, opt_prune);
- if (opt_recurse_submodules)
- argv_array_push(&args, opt_recurse_submodules);
+ if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+ switch (recurse_submodules) {
+ case RECURSE_SUBMODULES_ON:
+ argv_array_push(&args, "--recurse-submodules=on");
+ break;
+ case RECURSE_SUBMODULES_OFF:
+ argv_array_push(&args, "--recurse-submodules=no");
+ break;
+ case RECURSE_SUBMODULES_ON_DEMAND:
+ argv_array_push(&args, "--recurse-submodules=on-demand");
+ break;
+ default:
+ BUG("submodule recursion option not understood");
+ }
if (max_children)
argv_array_push(&args, max_children);
if (opt_dry_run)
@@ -500,6 +530,10 @@ 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);
@@ -523,15 +557,41 @@ 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_SHA1_BIN, merge_head->hash, 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;
}
+static int rebase_submodules(void)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+ cp.no_stdin = 1;
+ argv_array_pushl(&cp.args, "submodule", "update",
+ "--recursive", "--rebase", NULL);
+ argv_push_verbosity(&cp.args);
+
+ return run_command(&cp);
+}
+
+static int update_submodules(void)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+ cp.no_stdin = 1;
+ argv_array_pushl(&cp.args, "submodule", "update",
+ "--recursive", "--checkout", NULL);
+ argv_push_verbosity(&cp.args);
+
+ return run_command(&cp);
+}
+
/**
* Runs git-merge, returning its exit status.
*/
@@ -552,6 +612,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)
@@ -698,17 +760,20 @@ static int get_octopus_merge_base(struct object_id *merge_base,
{
struct commit_list *revs = NULL, *result;
- commit_list_insert(lookup_commit_reference(curr_head->hash), &revs);
- commit_list_insert(lookup_commit_reference(merge_head->hash), &revs);
+ commit_list_insert(lookup_commit_reference(curr_head), &revs);
+ commit_list_insert(lookup_commit_reference(merge_head), &revs);
if (!is_null_oid(fork_point))
- commit_list_insert(lookup_commit_reference(fork_point->hash), &revs);
+ 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,6 +842,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);
+ git_config(git_pull_config, NULL);
+
argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
parse_repo_refspecs(argc, argv, &repo, &refspecs);
@@ -787,8 +854,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (opt_rebase < 0)
opt_rebase = config_get_rebase();
- git_config(git_pull_config, NULL);
-
if (read_cache_unmerged())
die_resolve_conflict("pull");
@@ -840,7 +905,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
"fast-forwarding your working tree from\n"
"commit %s."), oid_to_hex(&orig_head));
- if (checkout_fast_forward(orig_head.hash, curr_head.hash, 0))
+ if (checkout_fast_forward(&orig_head, &curr_head, 0))
die(_("Cannot fast-forward your working tree.\n"
"After making sure that you saved anything precious from\n"
"$ git diff %s\n"
@@ -863,21 +928,36 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
die(_("Cannot rebase onto multiple branches."));
if (opt_rebase) {
+ int ret = 0;
+ if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
+ recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
+ submodule_touches_in_range(&rebase_fork_point, &curr_head))
+ die(_("cannot rebase with locally recorded submodule modifications"));
if (!autostash) {
struct commit_list *list = NULL;
struct commit *merge_head, *head;
- head = lookup_commit_reference(orig_head.hash);
+ head = lookup_commit_reference(&orig_head);
commit_list_insert(head, &list);
- merge_head = lookup_commit_reference(merge_heads.oid[0].hash);
+ merge_head = lookup_commit_reference(&merge_heads.oid[0]);
if (is_descendant_of(merge_head, list)) {
/* we can fast-forward this without invoking rebase */
opt_ff = "--ff-only";
- return run_merge();
+ ret = run_merge();
}
}
- return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+ ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+
+ if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+ recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+ ret = rebase_submodules();
+
+ return ret;
} else {
- return run_merge();
+ int ret = run_merge();
+ if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+ recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+ ret = update_submodules();
+ return ret;
}
}
diff --git a/builtin/push.c b/builtin/push.c
index a597759d8f..013c20d616 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -2,6 +2,7 @@
* "git push"
*/
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "run-command.h"
#include "builtin.h"
@@ -31,6 +32,8 @@ static const char **refspec;
static int refspec_nr;
static int refspec_alloc;
+static struct string_list push_options_config = STRING_LIST_INIT_DUP;
+
static void add_refspec(const char *ref)
{
refspec_nr++;
@@ -480,7 +483,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
} else if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
- switch (git_config_maybe_bool("push.gpgsign", value)) {
+ switch (git_parse_maybe_bool(value)) {
case 0:
set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
break;
@@ -498,6 +501,19 @@ static int git_push_config(const char *k, const char *v, void *cb)
const char *value;
if (!git_config_get_value("push.recursesubmodules", &value))
recurse_submodules = parse_push_recurse_submodules_arg(k, value);
+ } else if (!strcmp(k, "submodule.recurse")) {
+ 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;
}
return git_default_config(k, v, NULL);
@@ -510,7 +526,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[] = {
@@ -531,7 +548,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"),
@@ -546,7 +563,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"),
@@ -557,6 +574,9 @@ 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))))
@@ -579,12 +599,13 @@ int cmd_push(int argc, const char **argv, const char *prefix)
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 a52a9e11bb..bf87a2710b 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -5,6 +5,7 @@
*/
#include "cache.h"
+#include "config.h"
#include "lockfile.h"
#include "object.h"
#include "tree.h"
@@ -21,15 +22,14 @@
static int nr_trees;
static int read_empty;
static struct tree *trees[MAX_UNPACK_TREES];
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int list_tree(unsigned char *sha1)
+static int list_tree(struct object_id *oid)
{
struct tree *tree;
if (nr_trees >= MAX_UNPACK_TREES)
die("I cannot read more than %d trees", MAX_UNPACK_TREES);
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(oid);
if (!tree)
return -1;
trees[nr_trees++] = tree;
@@ -99,21 +99,12 @@ static int debug_merge(const struct cache_entry * const *stages,
return 0;
}
-static int option_parse_recurse_submodules(const struct option *opt,
- const char *arg, int unset)
+static int git_read_tree_config(const char *var, const char *value, void *cb)
{
- if (unset) {
- recurse_submodules = RECURSE_SUBMODULES_OFF;
- return 0;
- }
- if (arg)
- recurse_submodules =
- parse_update_recurse_submodules_arg(opt->long_name,
- arg);
- else
- recurse_submodules = RECURSE_SUBMODULES_ON;
+ if (!strcmp(var, "submodule.recurse"))
+ return git_default_submodule_config(var, value, cb);
- return 0;
+ return git_default_config(var, value, cb);
}
static struct lock_file lock_file;
@@ -121,7 +112,7 @@ static struct lock_file lock_file;
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, stage = 0;
- unsigned char sha1[20];
+ struct object_id oid;
struct tree_desc t[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
int prefix_set = 0;
@@ -157,9 +148,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
N_("skip applying sparse checkout filter")),
OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
N_("debug unpack-trees")),
- { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+ { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
"checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
OPT_END()
};
@@ -168,19 +159,13 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
opts.src_index = &the_index;
opts.dst_index = &the_index;
- git_config(git_default_config, NULL);
+ git_config(git_read_tree_config, NULL);
argc = parse_options(argc, argv, unused_prefix, read_tree_options,
read_tree_usage, 0);
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
- if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) {
- gitmodules_config();
- git_config(submodule_config, NULL);
- set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON);
- }
-
prefix_set = opts.prefix ? 1 : 0;
if (1 < opts.merge + opts.reset + prefix_set)
die("Which one? -m, --reset, or --prefix?");
@@ -204,9 +189,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- if (get_sha1(arg, sha1))
+ if (get_oid(arg, &oid))
die("Not a valid object name %s", arg);
- if (list_tree(sha1) < 0)
+ if (list_tree(&oid) < 0)
die("failed to unpack tree object %s", arg);
stage++;
}
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index ca1ebb2fa1..ad074705bb 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "parse-options.h"
#include "sequencer.h"
@@ -11,19 +12,41 @@ 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;
+ unsigned flags = 0, keep_empty = 0;
+ int abbreviate_commands = 0;
enum {
- CONTINUE = 1, ABORT
+ 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_CMDMODE(0, "continue", &command, N_("continue rebase"),
CONTINUE),
OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
ABORT),
+ OPT_CMDMODE(0, "make-script", &command,
+ N_("make rebase script"), MAKE_SCRIPT),
+ OPT_CMDMODE(0, "shorten-ids", &command,
+ N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
+ OPT_CMDMODE(0, "expand-ids", &command,
+ 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;
@@ -32,9 +55,25 @@ 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 |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+
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(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 da9a3a2c9d..75e7f18ace 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "lockfile.h"
#include "pack.h"
#include "refs.h"
@@ -22,6 +23,8 @@
#include "fsck.h"
#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>"),
@@ -66,7 +69,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;
@@ -78,7 +81,7 @@ static const char *NONCE_OK = "OK";
static const char *NONCE_SLOP = "SLOP";
static const char *nonce_status;
static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
static struct ref_transaction *transaction;
static enum {
@@ -454,17 +457,17 @@ static void hmac_sha1(unsigned char *out,
git_SHA1_Final(out, &ctx);
}
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
{
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- strbuf_addf(&buf, "%s:%lu", path, stamp);
+ 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, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+ strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
return strbuf_detach(&buf, NULL);
}
@@ -499,7 +502,7 @@ static char *find_header(const char *msg, size_t len, const char *key,
static const char *check_nonce(const char *buf, size_t len)
{
char *nonce = find_header(buf, len, "nonce", NULL);
- unsigned long stamp, ostamp;
+ timestamp_t stamp, ostamp;
char *bohmac, *expect = NULL;
const char *retval = NONCE_BAD;
@@ -537,7 +540,7 @@ static const char *check_nonce(const char *buf, size_t len)
retval = NONCE_BAD;
goto leave;
}
- stamp = strtoul(nonce, &bohmac, 10);
+ stamp = parse_timestamp(nonce, &bohmac, 10);
if (bohmac == nonce || bohmac[0] != '-') {
retval = NONCE_BAD;
goto leave;
@@ -555,7 +558,7 @@ static const char *check_nonce(const char *buf, size_t len)
* would mean it was issued by another server with its clock
* skewed in the future.
*/
- ostamp = strtoul(push_cert_nonce, NULL, 10);
+ ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
nonce_stamp_slop = (long)ostamp - (long)stamp;
if (nonce_stamp_slop_limit &&
@@ -630,8 +633,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';
@@ -652,9 +656,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",
@@ -741,7 +745,7 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
size_t n;
if (feed(feed_state, &buf, &n))
break;
- if (write_in_full(proc.in, buf, n) != n)
+ if (write_in_full(proc.in, buf, n) < 0)
break;
}
close(proc.in);
@@ -868,7 +872,7 @@ 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;
@@ -900,7 +904,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
* not lose these new roots..
*/
for (i = 0; i < extra.nr; i++)
- register_shallow(extra.oid[i].hash);
+ register_shallow(&extra.oid[i]);
si->shallow_ref[cmd->index] = 0;
oid_array_clear(&extra);
@@ -918,9 +922,9 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
*/
static int head_has_history(void)
{
- unsigned char sha1[20];
+ struct object_id oid;
- return !get_sha1("HEAD", sha1);
+ return !get_oid("HEAD", &oid);
}
static const char *push_to_deploy(unsigned char *sha1,
@@ -1102,8 +1106,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct object *old_object, *new_object;
struct commit *old_commit, *new_commit;
- old_object = parse_object(old_oid->hash);
- new_object = parse_object(new_oid->hash);
+ old_object = parse_object(old_oid);
+ new_object = parse_object(new_oid);
if (!old_object || !new_object ||
old_object->type != OBJ_COMMIT ||
@@ -1126,7 +1130,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (is_null_oid(new_oid)) {
struct strbuf err = STRBUF_INIT;
- if (!parse_object(old_oid->hash)) {
+ if (!parse_object(old_oid)) {
old_oid = NULL;
if (ref_exists(name)) {
rp_warning("Allowing deletion of corrupt ref.");
@@ -1137,7 +1141,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
if (ref_transaction_delete(transaction,
namespaced_name,
- old_oid->hash,
+ old_oid,
0, "push", &err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
@@ -1154,7 +1158,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);
@@ -1205,11 +1209,10 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
const char *dst_name;
struct string_list_item *item;
struct command *dst_cmd;
- unsigned char sha1[GIT_MAX_RAWSZ];
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -1269,7 +1272,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;
@@ -1277,7 +1280,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;
}
@@ -1308,7 +1311,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;
@@ -1319,7 +1322,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;
}
@@ -1457,7 +1460,6 @@ static void execute_commands(struct command *commands,
{
struct check_connected_options opt = CHECK_CONNECTED_INIT;
struct command *cmd;
- struct object_id oid;
struct iterate_data data;
struct async muxer;
int err_fd = 0;
@@ -1514,7 +1516,7 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL);
+ head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
if (use_atomic)
execute_commands_atomic(commands, si);
@@ -1805,7 +1807,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
static void prepare_shallow_update(struct command *commands,
struct shallow_info *si)
{
- int i, j, k, bitmap_size = (si->ref->nr + 31) / 32;
+ int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32);
ALLOC_ARRAY(si->used_shallow, si->shallow->nr);
assign_shallow_commits_to_refs(si, si->used_shallow, NULL);
@@ -1961,6 +1963,22 @@ 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_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();
}
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 7472775778..4719a5354c 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "lockfile.h"
#include "commit.h"
#include "refs.h"
@@ -16,14 +17,14 @@ static const char reflog_delete_usage[] =
static const char reflog_exists_usage[] =
"git reflog exists <ref>";
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
struct rev_info revs;
int stalefix;
- unsigned long expire_total;
- unsigned long expire_unreachable;
+ timestamp_t expire_total;
+ timestamp_t expire_unreachable;
int recno;
};
@@ -41,7 +42,7 @@ struct expire_reflog_policy_cb {
};
struct collected_reflog {
- unsigned char sha1[20];
+ struct object_id oid;
char reflog[FLEX_ARRAY];
};
@@ -51,18 +52,19 @@ 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)
-static int tree_is_complete(const unsigned char *sha1)
+static int tree_is_complete(const struct object_id *oid)
{
struct tree_desc desc;
struct name_entry entry;
int complete;
struct tree *tree;
- tree = lookup_tree(sha1);
+ tree = lookup_tree(oid);
if (!tree)
return 0;
if (tree->object.flags & SEEN)
@@ -73,7 +75,7 @@ static int tree_is_complete(const unsigned char *sha1)
if (!tree->buffer) {
enum object_type type;
unsigned long size;
- void *data = read_sha1_file(sha1, &type, &size);
+ void *data = read_sha1_file(oid->hash, &type, &size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;
@@ -85,7 +87,7 @@ static int tree_is_complete(const unsigned char *sha1)
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!has_sha1_file(entry.oid->hash) ||
- (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) {
+ (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid))) {
tree->object.flags |= INCOMPLETE;
complete = 0;
}
@@ -125,8 +127,8 @@ static int commit_is_complete(struct commit *commit)
struct commit *c;
struct commit_list *parent;
- c = (struct commit *)study.objects[--study.nr].item;
- if (!c->object.parsed && !parse_object(c->object.oid.hash))
+ c = (struct commit *)object_array_pop(&study);
+ if (!c->object.parsed && !parse_object(&c->object.oid))
c->object.flags |= INCOMPLETE;
if (c->object.flags & INCOMPLETE) {
@@ -152,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.hash)) {
+ if (!tree_is_complete(&c->tree->object.oid)) {
is_incomplete = 1;
c->object.flags |= INCOMPLETE;
}
@@ -181,18 +183,18 @@ static int commit_is_complete(struct commit *commit)
found.objects[i].item->flags |= SEEN;
}
/* free object arrays */
- free(study.objects);
- free(found.objects);
+ object_array_clear(&study);
+ object_array_clear(&found);
return !is_incomplete;
}
-static int keep_entry(struct commit **it, unsigned char *sha1)
+static int keep_entry(struct commit **it, struct object_id *oid)
{
struct commit *commit;
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return 1;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
if (!commit)
return 0;
@@ -219,7 +221,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
static void mark_reachable(struct expire_reflog_policy_cb *cb)
{
struct commit_list *pending;
- unsigned long expire_limit = cb->mark_limit;
+ timestamp_t expire_limit = cb->mark_limit;
struct commit_list *leftover = NULL;
for (pending = cb->mark_list; pending; pending = pending->next)
@@ -251,17 +253,17 @@ static void mark_reachable(struct expire_reflog_policy_cb *cb)
cb->mark_list = leftover;
}
-static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, unsigned char *sha1)
+static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
{
/*
* We may or may not have the commit yet - if not, look it
* up using the supplied sha1.
*/
if (!commit) {
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return 0;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
/* Not a commit -- keep it */
if (!commit)
@@ -283,25 +285,25 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
/*
* Return true iff the specified reflog entry should be expired.
*/
-static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
- const char *email, unsigned long timestamp, int tz,
+static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
+ const char *email, timestamp_t timestamp, int tz,
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, osha1) || !keep_entry(&new, nsha1)))
+ (!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, osha1) || unreachable(cb, new, nsha1))
+ if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
return 1;
}
@@ -318,7 +320,7 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid,
struct commit *tip_commit;
if (flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(oid->hash, 1);
+ tip_commit = lookup_commit_reference_gently(oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
@@ -326,7 +328,7 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid,
}
static void reflog_expiry_prepare(const char *refname,
- const unsigned char *sha1,
+ const struct object_id *oid,
void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
@@ -335,7 +337,7 @@ static void reflog_expiry_prepare(const char *refname,
cb->tip_commit = NULL;
cb->unreachable_expire_kind = UE_HEAD;
} else {
- cb->tip_commit = lookup_commit_reference_gently(sha1, 1);
+ cb->tip_commit = lookup_commit_reference_gently(oid, 1);
if (!cb->tip_commit)
cb->unreachable_expire_kind = UE_ALWAYS;
else
@@ -384,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;
@@ -392,8 +394,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
static struct reflog_expire_cfg {
struct reflog_expire_cfg *next;
- unsigned long expire_total;
- unsigned long expire_unreachable;
+ timestamp_t expire_total;
+ timestamp_t expire_unreachable;
char pattern[FLEX_ARRAY];
} *reflog_expire_cfg, **reflog_expire_cfg_tail;
@@ -415,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, unsigned long *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
@@ -433,7 +425,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
{
const char *pattern, *key;
int pattern_len;
- unsigned long expire;
+ timestamp_t expire;
int slot;
struct reflog_expire_cfg *ent;
@@ -442,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);
@@ -485,7 +477,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
return; /* both given explicitly -- nothing to tweak */
for (ent = reflog_expire_cfg; ent; ent = ent->next) {
- if (!wildmatch(ent->pattern, ref, 0, NULL)) {
+ if (!wildmatch(ent->pattern, ref, 0)) {
if (!(slot & EXPIRE_TOTAL))
cb->expire_total = ent->expire_total;
if (!(slot & EXPIRE_UNREACH))
@@ -515,7 +507,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
struct expire_reflog_policy_cb cb;
- unsigned long now = time(NULL);
+ timestamp_t now = time(NULL);
int i, status, do_all;
int explicit_expiry = 0;
unsigned int flags = 0;
@@ -588,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,
@@ -600,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,
@@ -616,7 +608,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
}
static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
@@ -658,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;
@@ -667,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;
}
@@ -682,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-ext.c b/builtin/remote-ext.c
index 11b48bfb41..6a9127a33c 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -3,6 +3,9 @@
#include "run-command.h"
#include "pkt-line.h"
+static const char usage_msg[] =
+ "git remote-ext <remote> <url>";
+
/*
* URL syntax:
* 'command [arg1 [arg2 [...]]]' Invoke command with given arguments.
@@ -54,7 +57,7 @@ static char *strip_escapes(const char *str, const char *service,
special = str[rpos];
if (rpos == 1)
break;
- /* Fall-through to error. */
+ /* fallthrough */
default:
die("Bad remote-ext placeholder '%%%c'.",
str[rpos]);
@@ -193,7 +196,7 @@ static int command_loop(const char *child)
int cmd_remote_ext(int argc, const char **argv, const char *prefix)
{
if (argc != 3)
- die("Expected two arguments");
+ usage(usage_msg);
return command_loop(argv[2]);
}
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
index 08d7121b6d..91dfe07e06 100644
--- a/builtin/remote-fd.c
+++ b/builtin/remote-fd.c
@@ -1,6 +1,9 @@
#include "builtin.h"
#include "transport.h"
+static const char usage_msg[] =
+ "git remote-fd <remote> <url>";
+
/*
* URL syntax:
* 'fd::<inoutfd>[/<anything>]' Read/write socket pair
@@ -57,7 +60,7 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix)
char *end;
if (argc != 3)
- die("Expected two arguments");
+ usage(usage_msg);
input_fd = (int)strtoul(argv[2], &end, 10);
diff --git a/builtin/remote.c b/builtin/remote.c
index 9054e2858e..805ffc05cd 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "parse-options.h"
#include "transport.h"
#include "remote.h"
@@ -167,7 +168,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()
};
@@ -300,7 +301,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
}
string_list_append(&info->merge, xstrdup(value));
} else {
- int v = git_config_maybe_bool(orig_key, value);
+ int v = git_parse_maybe_bool(value);
if (v >= 0)
info->rebase = v;
else if (!strcmp(value, "preserve"))
@@ -321,7 +322,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;
};
@@ -336,12 +337,12 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
die(_("Could not get fetch map for refspec %s"),
states->remote->fetch_refspec[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));
}
@@ -355,7 +356,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);
@@ -545,8 +546,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;
};
@@ -557,19 +558,19 @@ static int read_remote_branches(const char *refname,
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
int flag;
- struct object_id orig_oid;
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,
- orig_oid.hash, &flag);
- if (flag & REF_ISSYMREF)
+ NULL, &flag);
+ if (symref && (flag & REF_ISSYMREF))
item->util = xstrdup(symref);
else
item->util = NULL;
}
+ strbuf_release(&buf);
return 0;
}
@@ -594,6 +595,7 @@ static int migrate_file(struct remote *remote)
unlink_or_warn(git_path("remotes/%s", remote->name));
else if (remote->origin == REMOTE_BRANCHES)
unlink_or_warn(git_path("branches/%s", remote->name));
+ strbuf_release(&buf);
return 0;
}
@@ -613,36 +615,36 @@ 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);
+ strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
char *ptr;
@@ -653,8 +655,8 @@ static int mv(int argc, const char **argv)
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"
@@ -668,10 +670,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);
}
}
@@ -688,10 +690,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++) {
@@ -701,8 +703,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);
@@ -716,12 +718,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);
@@ -786,7 +788,7 @@ static int rm(int argc, const char **argv)
strbuf_release(&buf);
if (!result)
- result = delete_refs(&branches, REF_NODEREF);
+ result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
string_list_clear(&branches, 0);
if (skipped.nr) {
@@ -820,7 +822,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);
@@ -905,7 +907,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))
@@ -1174,7 +1176,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)
@@ -1253,7 +1255,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);
@@ -1304,7 +1306,7 @@ static int prune_remote(const char *remote, int dry_run)
string_list_sort(&refs_to_prune);
if (!dry_run)
- result |= delete_refs(&refs_to_prune, 0);
+ result |= delete_refs("remote: prune", &refs_to_prune, 0);
for_each_string_list_item(item, &states.stale) {
const char *refname = item->util;
@@ -1562,9 +1564,7 @@ static int set_url(int argc, const char **argv)
"^$", 0);
else
git_config_set(name_buf.buf, newurl);
- strbuf_release(&name_buf);
-
- return 0;
+ goto out;
}
/* Old URL specified. Demand that one matches. */
@@ -1587,6 +1587,8 @@ static int set_url(int argc, const char **argv)
git_config_set_multivar(name_buf.buf, newurl, oldurl, 0);
else
git_config_set_multivar(name_buf.buf, NULL, oldurl, 1);
+out:
+ strbuf_release(&name_buf);
return 0;
}
diff --git a/builtin/repack.c b/builtin/repack.c
index 677bc7c81a..7bdb40142f 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "dir.h"
#include "parse-options.h"
#include "run-command.h"
@@ -82,7 +83,8 @@ static void remove_pack_on_signal(int signo)
/*
* Adds all packs hex strings to the fname list, which do not
- * have a corresponding .keep file.
+ * 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)
{
@@ -100,7 +102,8 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
fname = xmemdupz(e->d_name, len);
- if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
+ if (!file_exists(mkpath("%s/%s.keep", packdir, fname)) &&
+ !file_exists(mkpath("%s/%s.promisor", packdir, fname)))
string_list_append_nodup(fname_list, fname);
else
free(fname);
@@ -155,6 +158,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int keep_unreachable = 0;
const char *window = NULL, *window_memory = NULL;
const char *depth = NULL;
+ const char *threads = NULL;
const char *max_pack_size = NULL;
int no_reuse_delta = 0, no_reuse_object = 0;
int no_update_server_info = 0;
@@ -190,6 +194,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("same as the above, but limit memory size instead of entries count")),
OPT_STRING(0, "depth", &depth, N_("n"),
N_("limits the maximum delta depth")),
+ OPT_STRING(0, "threads", &threads, N_("n"),
+ N_("limits the maximum number of threads")),
OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"),
N_("maximum size of each packfile")),
OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
@@ -228,12 +234,16 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd.args, "--all");
argv_array_push(&cmd.args, "--reflog");
argv_array_push(&cmd.args, "--indexed-objects");
+ if (repository_format_partial_clone)
+ argv_array_push(&cmd.args, "--exclude-promisor-objects");
if (window)
argv_array_pushf(&cmd.args, "--window=%s", window);
if (window_memory)
argv_array_pushf(&cmd.args, "--window-memory=%s", window_memory);
if (depth)
argv_array_pushf(&cmd.args, "--depth=%s", depth);
+ if (threads)
+ argv_array_pushf(&cmd.args, "--threads=%s", threads);
if (max_pack_size)
argv_array_pushf(&cmd.args, "--max-pack-size=%s", max_pack_size);
if (no_reuse_delta)
diff --git a/builtin/replace.c b/builtin/replace.c
index ab17668f43..482f12018f 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -9,6 +9,7 @@
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "refs.h"
#include "parse-options.h"
@@ -40,7 +41,7 @@ static int show_reference(const char *refname, const struct object_id *oid,
{
struct show_data *data = cb_data;
- if (!wildmatch(data->pattern, refname, 0, NULL)) {
+ if (!wildmatch(data->pattern, refname, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
printf("%s\n", refname);
else if (data->format == REPLACE_FORMAT_MEDIUM)
@@ -49,14 +50,14 @@ static int show_reference(const char *refname, const struct object_id *oid,
struct object_id object;
enum object_type obj_type, repl_type;
- if (get_sha1(refname, object.hash))
+ 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);
- 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));
}
}
@@ -112,7 +113,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;
@@ -127,7 +128,7 @@ 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;
@@ -143,7 +144,7 @@ static void check_ref_valid(struct object_id *object,
if (check_refname_format(ref->buf, 0))
die("'%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);
@@ -167,14 +168,14 @@ static int replace_object_oid(const char *object_ref,
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));
+ object_ref, type_name(obj_type),
+ replace_ref, type_name(repl_type));
check_ref_valid(object, &prev, &ref, force);
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);
@@ -214,7 +215,7 @@ static void export_object(const struct object_id *oid, enum object_type type,
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));
@@ -268,7 +269,7 @@ static void import_object(struct object_id *oid, enum object_type type,
if (fstat(fd, &st) < 0)
die_errno("unable to fstat %s", filename);
- if (index_fd(oid->hash, fd, &st, type, NULL, flags) < 0)
+ if (index_fd(oid, fd, &st, type, NULL, flags) < 0)
die("unable to write object to database");
/* index_fd close()s fd for us */
}
@@ -283,30 +284,30 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
{
char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
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)
+ if (get_oid(object_ref, &old_oid) < 0)
die("Not a valid object name: '%s'", object_ref);
- type = sha1_object_info(old.hash, NULL);
+ type = sha1_object_info(old_oid.hash, NULL);
if (type < 0)
- die("unable to get object type for %s", oid_to_hex(&old));
+ die("unable to get object type for %s", oid_to_hex(&old_oid));
- check_ref_valid(&old, &prev, &ref, force);
+ check_ref_valid(&old_oid, &prev, &ref, force);
strbuf_release(&ref);
- export_object(&old, type, raw, tmpfile);
+ export_object(&old_oid, type, raw, tmpfile);
if (launch_editor(tmpfile, NULL, NULL) < 0)
die("editing object file failed");
- import_object(&new, type, raw, tmpfile);
+ import_object(&new_oid, type, raw, tmpfile);
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)
@@ -328,7 +329,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
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.hash, argv[i]);
+ lookup_commit_or_die(&oid, argv[i]);
strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
}
@@ -354,8 +355,8 @@ 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);
- tag = lookup_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);
if (parse_tag_buffer(tag, extra->value, extra->len))
@@ -364,7 +365,7 @@ static void check_one_mergetag(struct commit *commit,
/* iterate over new parents */
for (i = 1; i < mergetag_data->argc; i++) {
struct object_id oid;
- if (get_sha1(mergetag_data->argv[i], oid.hash) < 0)
+ if (get_oid(mergetag_data->argv[i], &oid) < 0)
die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]);
if (!oidcmp(&tag->tagged->oid, &oid))
return; /* found */
@@ -385,16 +386,16 @@ static void check_mergetags(struct commit *commit, int argc, const char **argv)
static int create_graft(int argc, const char **argv, int force)
{
- 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)
+ if (get_oid(old_ref, &old_oid) < 0)
die(_("Not a valid object name: '%s'"), old_ref);
- commit = lookup_commit_or_die(old.hash, old_ref);
+ commit = lookup_commit_or_die(&old_oid, old_ref);
buffer = get_commit_buffer(commit, &size);
strbuf_add(&buf, buffer, size);
@@ -409,15 +410,15 @@ static int create_graft(int argc, const char **argv, int force)
check_mergetags(commit, argc, argv);
- if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash))
+ if (write_object_file(buf.buf, buf.len, commit_type, &new_oid))
die(_("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))
+ 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);
}
int cmd_replace(int argc, const char **argv, const char *prefix)
@@ -438,7 +439,8 @@ 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_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()
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 1bf72423bf..0bc40298c2 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "dir.h"
#include "parse-options.h"
#include "string-list.h"
@@ -17,7 +18,7 @@ static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
{
int i;
for (i = 0; i < nbuf; i++)
- if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
+ if (write_in_full(1, ptr[i].ptr, ptr[i].size) < 0)
return -1;
return 0;
}
diff --git a/builtin/reset.c b/builtin/reset.c
index fc3b906c47..5da0f75de9 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -8,10 +8,11 @@
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
*/
#include "builtin.h"
+#include "config.h"
#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"
@@ -21,6 +22,8 @@
#include "parse-options.h"
#include "unpack-trees.h"
#include "cache-tree.h"
+#include "submodule.h"
+#include "submodule-config.h"
static const char * const git_reset_usage[] = {
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
@@ -41,10 +44,11 @@ static inline int is_merge(void)
static int reset_index(const struct object_id *oid, int reset_type, int quiet)
{
- int nr = 1;
+ int i, nr = 0;
struct tree_desc desc[2];
struct tree *tree;
struct unpack_trees_options opts;
+ int ret = -1;
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
@@ -72,45 +76,46 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
struct object_id head_oid;
if (get_oid("HEAD", &head_oid))
return error(_("You do not have a valid HEAD."));
- if (!fill_tree_descriptor(desc, head_oid.hash))
+ if (!fill_tree_descriptor(desc + nr, &head_oid))
return error(_("Failed to find tree of HEAD."));
nr++;
opts.fn = twoway_merge;
}
- if (!fill_tree_descriptor(desc + nr - 1, oid->hash))
- return error(_("Failed to find tree of %s."), oid_to_hex(oid));
+ if (!fill_tree_descriptor(desc + nr, oid)) {
+ error(_("Failed to find tree of %s."), oid_to_hex(oid));
+ goto out;
+ }
+ nr++;
+
if (unpack_trees(nr, desc, &opts))
- return -1;
+ goto out;
if (reset_type == MIXED || reset_type == HARD) {
- tree = parse_tree_indirect(oid->hash);
+ tree = parse_tree_indirect(oid);
prime_cache_tree(&the_index, tree);
}
- return 0;
+ ret = 0;
+
+out:
+ for (i = 0; i < nr; i++)
+ free((void *)desc[i].buffer);
+ return ret;
}
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.hash, 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,
@@ -153,8 +158,9 @@ 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.override_submodule_config = 1;
- if (do_diff_cache(tree_oid->hash, &opt))
+ if (do_diff_cache(tree_oid, &opt))
return 1;
diffcore_std(&opt);
diff_flush(&opt);
@@ -216,8 +222,8 @@ static void parse_args(struct pathspec *pathspec,
* has to be unambiguous. If there is a single argument, it
* can not be a tree
*/
- else if ((!argv[1] && !get_sha1_committish(argv[0], unused.hash)) ||
- (argv[1] && !get_sha1_treeish(argv[0], unused.hash))) {
+ else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) ||
+ (argv[1] && !get_oid_treeish(argv[0], &unused))) {
/*
* Ok, argv[0] looks like a commit/tree; it should not
* be a filename.
@@ -236,7 +242,6 @@ static void parse_args(struct pathspec *pathspec,
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP |
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
prefix, argv);
}
@@ -253,17 +258,25 @@ 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;
}
+static int git_reset_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "submodule.recurse"))
+ return git_default_submodule_config(var, value, cb);
+
+ return git_default_config(var, value, cb);
+}
+
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -283,35 +296,38 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
N_("reset HEAD, index and working tree"), MERGE),
OPT_SET_INT(0, "keep", &reset_type,
N_("reset HEAD but keep local changes"), KEEP),
+ { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+ "reset", "control recursive updating of submodules",
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
OPT_END()
};
- git_config(git_default_config, NULL);
+ git_config(git_reset_config, NULL);
argc = parse_options(argc, argv, prefix, options, git_reset_usage,
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
- unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash);
+ 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);
} else if (!pathspec.nr) {
struct commit *commit;
- if (get_sha1_committish(rev, oid.hash))
+ if (get_oid_committish(rev, &oid))
die(_("Failed to resolve '%s' as a valid revision."), rev);
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
if (!commit)
die(_("Could not parse object '%s'."), rev);
oidcpy(&oid, &commit->object.oid);
} else {
struct tree *tree;
- if (get_sha1_treeish(rev, oid.hash))
+ if (get_oid_treeish(rev, &oid))
die(_("Failed to resolve '%s' as a valid tree."), rev);
- tree = parse_tree_indirect(oid.hash);
+ tree = parse_tree_indirect(&oid);
if (!tree)
die(_("Could not parse object '%s'."), rev);
oidcpy(&oid, &tree->object.oid);
@@ -353,8 +369,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die_if_unmerged_cache(reset_type);
if (reset_type != SOFT) {
- struct lock_file *lock = xcalloc(1, sizeof(*lock));
- hold_locked_index(lock, LOCK_DIE_ON_ERROR);
+ struct lock_file lock = LOCK_INIT;
+ hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
if (reset_type == MIXED) {
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
if (read_from_tree(&pathspec, &oid, intent_to_add))
@@ -370,7 +386,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die(_("Could not reset index file to revision '%s'."), rev);
}
- if (write_locked_index(&the_index, lock, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
die(_("Could not write new index file."));
}
@@ -380,7 +396,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
update_ref_status = reset_refs(rev, &oid);
if (reset_type == HARD && !update_ref_status && !quiet)
- print_new_head_line(lookup_commit_reference(oid.hash));
+ print_new_head_line(lookup_commit_reference(&oid));
}
if (!pathspec.nr)
remove_branch_state();
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bcf77f0b8a..6f5b9b0847 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -1,8 +1,11 @@
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
+#include "list-objects-filter.h"
+#include "list-objects-filter-options.h"
#include "pack.h"
#include "pack-bitmap.h"
#include "builtin.h"
@@ -10,6 +13,9 @@
#include "graph.h"
#include "bisect.h"
#include "progress.h"
+#include "reflog-walk.h"
+#include "oidset.h"
+#include "packfile.h"
static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -53,6 +59,21 @@ static const char rev_list_usage[] =
static struct progress *progress;
static unsigned progress_counter;
+static struct list_objects_filter_options filter_options;
+static struct oidset omitted_objects;
+static int arg_print_omitted; /* print objects omitted by filter */
+
+static struct oidset missing_objects;
+enum missing_action {
+ MA_ERROR = 0, /* fail if any missing objects are encountered */
+ MA_ALLOW_ANY, /* silently allow ALL missing objects */
+ MA_PRINT, /* print ALL missing objects in special section */
+ MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
+};
+static enum missing_action arg_missing_action;
+
+#define DEFAULT_OIDSET_SIZE (16*1024)
+
static void finish_commit(struct commit *commit, void *data);
static void show_commit(struct commit *commit, void *data)
{
@@ -80,7 +101,7 @@ static void show_commit(struct commit *commit, void *data)
}
if (info->show_timestamp)
- printf("%lu ", commit->date);
+ printf("%"PRItime" ", commit->date);
if (info->header_prefix)
fputs(info->header_prefix, stdout);
@@ -113,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;
@@ -121,6 +142,7 @@ static void show_commit(struct commit *commit, void *data)
ctx.date_mode_explicit = revs->date_mode_explicit;
ctx.fmt = revs->commit_format;
ctx.output_encoding = get_log_output_encoding();
+ ctx.color = revs->diffopt.use_color;
pretty_print_commit(&ctx, commit, &buf);
if (buf.len) {
if (revs->commit_format != CMIT_FMT_ONELINE)
@@ -175,19 +197,56 @@ static void finish_commit(struct commit *commit, void *data)
free_commit_buffer(commit);
}
-static void finish_object(struct object *obj, const char *name, void *cb_data)
+static inline void finish_object__ma(struct object *obj)
{
- struct rev_list_info *info = cb_data;
- if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
+ /*
+ * Whether or not we try to dynamically fetch missing objects
+ * from the server, we currently DO NOT have the object. We
+ * can either print, allow (ignore), or conditionally allow
+ * (ignore) them.
+ */
+ switch (arg_missing_action) {
+ case MA_ERROR:
die("missing blob object '%s'", oid_to_hex(&obj->oid));
+ return;
+
+ case MA_ALLOW_ANY:
+ return;
+
+ case MA_PRINT:
+ oidset_insert(&missing_objects, &obj->oid);
+ return;
+
+ case MA_ALLOW_PROMISOR:
+ if (is_promisor_object(&obj->oid))
+ return;
+ die("unexpected missing blob object '%s'",
+ oid_to_hex(&obj->oid));
+ return;
+
+ default:
+ BUG("unhandled missing_action");
+ return;
+ }
+}
+
+static int finish_object(struct object *obj, const char *name, void *cb_data)
+{
+ struct rev_list_info *info = cb_data;
+ if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
+ finish_object__ma(obj);
+ return 1;
+ }
if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
- parse_object(obj->oid.hash);
+ parse_object(&obj->oid);
+ return 0;
}
static void show_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
- finish_object(obj, name, cb_data);
+ if (finish_object(obj, name, cb_data))
+ return;
display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
@@ -255,17 +314,45 @@ 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;
}
+static inline int parse_missing_action_value(const char *value)
+{
+ if (!strcmp(value, "error")) {
+ arg_missing_action = MA_ERROR;
+ return 1;
+ }
+
+ if (!strcmp(value, "allow-any")) {
+ arg_missing_action = MA_ALLOW_ANY;
+ fetch_if_missing = 0;
+ return 1;
+ }
+
+ if (!strcmp(value, "print")) {
+ arg_missing_action = MA_PRINT;
+ fetch_if_missing = 0;
+ return 1;
+ }
+
+ if (!strcmp(value, "allow-promisor")) {
+ arg_missing_action = MA_ALLOW_PROMISOR;
+ fetch_if_missing = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
int cmd_rev_list(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -277,10 +364,42 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int use_bitmap_index = 0;
const char *show_progress = NULL;
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage(rev_list_usage);
+
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
revs.abbrev = DEFAULT_ABBREV;
revs.commit_format = CMIT_FMT_UNSPECIFIED;
+
+ /*
+ * Scan the argument list before invoking setup_revisions(), so that we
+ * know if fetch_if_missing needs to be set to 0.
+ *
+ * "--exclude-promisor-objects" acts as a pre-filter on missing objects
+ * by not crossing the boundary from realized objects to promisor
+ * objects.
+ *
+ * Let "--missing" to conditionally set fetch_if_missing.
+ */
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--exclude-promisor-objects")) {
+ fetch_if_missing = 0;
+ revs.exclude_promisor_objects = 1;
+ break;
+ }
+ }
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (skip_prefix(arg, "--missing=", &arg)) {
+ if (revs.exclude_promisor_objects)
+ die(_("cannot combine --exclude-promisor-objects and --missing"));
+ if (parse_missing_action_value(arg))
+ break;
+ }
+ }
+
argc = setup_revisions(argc, argv, &revs, NULL);
memset(&info, 0, sizeof(info));
@@ -288,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];
@@ -329,6 +448,31 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
show_progress = arg;
continue;
}
+
+ if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
+ parse_list_objects_filter(&filter_options, arg);
+ if (filter_options.choice && !revs.blob_objects)
+ die(_("object filtering requires --objects"));
+ if (filter_options.choice == LOFC_SPARSE_OID &&
+ !filter_options.sparse_oid_value)
+ die(_("invalid sparse value '%s'"),
+ filter_options.filter_spec);
+ continue;
+ }
+ if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
+ list_objects_filter_set_no_filter(&filter_options);
+ continue;
+ }
+ if (!strcmp(arg, "--filter-print-omitted")) {
+ arg_print_omitted = 1;
+ continue;
+ }
+
+ if (!strcmp(arg, "--exclude-promisor-objects"))
+ continue; /* already handled above */
+ if (skip_prefix(arg, "--missing=", &arg))
+ continue; /* already handled above */
+
usage(rev_list_usage);
}
@@ -344,15 +488,19 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
/* Only --header was specified */
revs.commit_format = CMIT_FMT_RAW;
- if ((!revs.commits &&
+ if ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&
(!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
- !revs.pending.nr)) ||
+ !revs.pending.nr) &&
+ !revs.rev_input_given) ||
revs.diff)
usage(rev_list_usage);
if (revs.show_notes)
die(_("rev-list does not support display of notes"));
+ if (filter_options.choice && use_bitmap_index)
+ die(_("cannot combine --use-bitmap-index with object filtering"));
+
save_commit_buffer = (revs.verbose_header ||
revs.grep_filter.pattern_list ||
revs.grep_filter.header_list);
@@ -360,7 +508,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
revs.limited = 1;
if (show_progress)
- progress = start_progress_delay(show_progress, 0, 0, 2);
+ progress = start_delayed_progress(show_progress, 0);
if (use_bitmap_index && !revs.prune) {
if (revs.count && !revs.left_right && !revs.cherry_mark) {
@@ -388,16 +536,39 @@ 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);
}
- traverse_commit_list(&revs, show_commit, show_object, &info);
+ if (arg_print_omitted)
+ oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
+ if (arg_missing_action == MA_PRINT)
+ oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+
+ traverse_commit_list_filtered(
+ &filter_options, &revs, show_commit, show_object, &info,
+ (arg_print_omitted ? &omitted_objects : NULL));
+
+ if (arg_print_omitted) {
+ struct oidset_iter iter;
+ struct object_id *oid;
+ oidset_iter_init(&omitted_objects, &iter);
+ while ((oid = oidset_iter_next(&iter)))
+ printf("~%s\n", oid_to_hex(oid));
+ oidset_clear(&omitted_objects);
+ }
+ if (arg_missing_action == MA_PRINT) {
+ struct oidset_iter iter;
+ struct object_id *oid;
+ oidset_iter_init(&missing_objects, &iter);
+ while ((oid = oidset_iter_next(&iter)))
+ printf("?%s\n", oid_to_hex(oid));
+ oidset_clear(&missing_objects);
+ }
stop_progress(&progress);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 0513330910..a1e680b5e9 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "refs.h"
#include "quote.h"
@@ -121,7 +122,7 @@ static void show_with_type(int type, const char *arg)
}
/* Output a revision, only if filter allows it */
-static void show_rev(int type, const unsigned char *sha1, const char *name)
+static void show_rev(int type, const struct object_id *oid, const char *name)
{
if (!(filter & DO_REVS))
return;
@@ -129,10 +130,10 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
if ((symbolic || abbrev_ref) && name) {
if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
- unsigned char discard[20];
+ struct object_id discard;
char *full;
- switch (dwim_ref(name, strlen(name), discard, &full)) {
+ switch (dwim_ref(name, strlen(name), &discard, &full)) {
case 0:
/*
* Not found -- not a ref. We could
@@ -158,9 +159,9 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
}
}
else if (abbrev)
- show_with_type(type, find_unique_abbrev(sha1, abbrev));
+ show_with_type(type, find_unique_abbrev(oid->hash, abbrev));
else
- show_with_type(type, sha1_to_hex(sha1));
+ show_with_type(type, oid_to_hex(oid));
}
/* Output a flag, only if filter allows it. */
@@ -180,11 +181,11 @@ static int show_default(void)
const char *s = def;
if (s) {
- unsigned char sha1[20];
+ struct object_id oid;
def = NULL;
- if (!get_sha1(s, sha1)) {
- show_rev(NORMAL, sha1, s);
+ if (!get_oid(s, &oid)) {
+ show_rev(NORMAL, &oid, s);
return 1;
}
}
@@ -195,19 +196,19 @@ static int show_reference(const char *refname, const struct object_id *oid, int
{
if (ref_excluded(ref_excludes, refname))
return 0;
- show_rev(NORMAL, oid->hash, refname);
+ show_rev(NORMAL, oid, refname);
return 0;
}
static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
- show_rev(REVERSED, oid->hash, refname);
+ show_rev(REVERSED, oid, refname);
return 0;
}
static int show_abbrev(const struct object_id *oid, void *cb_data)
{
- show_rev(NORMAL, oid->hash, NULL);
+ show_rev(NORMAL, oid, NULL);
return 0;
}
@@ -218,7 +219,7 @@ static void show_datestring(const char *flag, const char *datestr)
/* date handling requires both flags and revs */
if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
return;
- buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
+ buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
show(buffer);
free(buffer);
}
@@ -242,28 +243,28 @@ static int show_file(const char *arg, int output_prefix)
static int try_difference(const char *arg)
{
char *dotdot;
- unsigned char sha1[20];
- unsigned char end[20];
- 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
@@ -273,18 +274,18 @@ static int try_difference(const char *arg)
return 0;
}
- if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
- show_rev(NORMAL, end, next);
- show_rev(symmetric ? NORMAL : REVERSED, sha1, 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(sha1);
- b = lookup_commit_reference(end);
+ a = lookup_commit_reference(&start_oid);
+ b = lookup_commit_reference(&end_oid);
exclude = get_merge_bases(a, b);
while (exclude) {
struct commit *commit = pop_commit(&exclude);
- show_rev(REVERSED, commit->object.oid.hash, NULL);
+ show_rev(REVERSED, &commit->object.oid, NULL);
}
}
*dotdot = '.';
@@ -297,7 +298,7 @@ static int try_difference(const char *arg)
static int try_parent_shorthands(const char *arg)
{
char *dotdot;
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *commit;
struct commit_list *parents;
int parent_number;
@@ -327,12 +328,12 @@ static int try_parent_shorthands(const char *arg)
return 0;
*dotdot = 0;
- if (get_sha1_committish(arg, sha1)) {
+ if (get_oid_committish(arg, &oid)) {
*dotdot = '^';
return 0;
}
- commit = lookup_commit_reference(sha1);
+ commit = lookup_commit_reference(&oid);
if (exclude_parent &&
exclude_parent > commit_list_count(commit->parents)) {
*dotdot = '^';
@@ -340,7 +341,7 @@ static int try_parent_shorthands(const char *arg)
}
if (include_rev)
- show_rev(NORMAL, sha1, arg);
+ show_rev(NORMAL, &oid, arg);
for (parents = commit->parents, parent_number = 1;
parents;
parents = parents->next, parent_number++) {
@@ -352,7 +353,7 @@ static int try_parent_shorthands(const char *arg)
if (symbolic)
name = xstrfmt("%s^%d", arg, parent_number);
show_rev(include_parents ? NORMAL : REVERSED,
- parents->item->object.oid.hash, name);
+ &parents->item->object.oid, name);
free(name);
}
@@ -386,6 +387,14 @@ static const char *skipspaces(const char *s)
return s;
}
+static char *findspace(const char *s)
+{
+ for (; *s; s++)
+ if (isspace(*s))
+ return (char*)s;
+ return NULL;
+}
+
static int cmd_parseopt(int argc, const char **argv, const char *prefix)
{
static int keep_dashdash = 0, stop_at_non_option = 0;
@@ -433,7 +442,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
while (strbuf_getline(&sb, stdin) != EOF) {
const char *s;
- const char *help;
+ char *help;
struct option *o;
if (!sb.len)
@@ -443,15 +452,17 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
memset(opts + onb, 0, sizeof(opts[onb]));
o = &opts[onb++];
- help = strchr(sb.buf, ' ');
- if (!help || *sb.buf == ' ') {
+ help = findspace(sb.buf);
+ if (!help || sb.buf == help) {
o->type = OPTION_GROUP;
o->help = xstrdup(skipspaces(sb.buf));
continue;
}
+ *help = '\0';
+
o->type = OPTION_CALLBACK;
- o->help = xstrdup(skipspaces(help));
+ o->help = xstrdup(skipspaces(help+1));
o->value = &parsed;
o->flags = PARSE_OPT_NOARG;
o->callback = &parseopt_dump;
@@ -505,7 +516,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;
}
@@ -515,7 +526,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);
@@ -571,7 +582,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
int did_repo_setup = 0;
int has_dashdash = 0;
int output_prefix = 0;
- unsigned char sha1[20];
+ struct object_id oid;
unsigned int flags = 0;
const char *name = NULL;
struct object_context unused;
@@ -701,7 +712,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
- flags |= GET_SHA1_QUIETLY;
+ flags |= GET_OID_QUIETLY;
continue;
}
if (opt_with_value(arg, "--short", &arg)) {
@@ -756,8 +767,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_ref_in("refs/bisect/bad", show_reference, NULL);
- for_each_ref_in("refs/bisect/good", anti_reference, NULL);
+ for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
+ for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
@@ -867,6 +878,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
: "false");
continue;
}
+ if (!strcmp(arg, "--is-shallow-repository")) {
+ printf("%s\n", is_repository_shallow() ? "true"
+ : "false");
+ continue;
+ }
if (!strcmp(arg, "--shared-index-path")) {
if (read_cache() < 0)
die(_("Could not read the index"));
@@ -910,11 +926,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
name++;
type = REVERSED;
}
- if (!get_sha1_with_context(name, flags, sha1, &unused)) {
+ if (!get_oid_with_context(name, flags, &oid, &unused)) {
if (verify)
revs_count++;
else
- show_rev(type, sha1, name);
+ show_rev(type, &oid, name);
continue;
}
if (verify)
@@ -929,7 +945,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
if (verify) {
if (revs_count == 1) {
- show_rev(type, sha1, name);
+ show_rev(type, &oid, name);
return 0;
} else if (revs_count == 0 && show_default())
return 0;
diff --git a/builtin/revert.c b/builtin/revert.c
index 345d9586a7..76f0a35b07 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "parse-options.h"
#include "diff.h"
@@ -154,6 +155,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
"--strategy-option", opts->xopts ? 1 : 0,
"-x", opts->record_origin,
"--ff", opts->allow_ff,
+ "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
+ "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
NULL);
}
@@ -205,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"));
@@ -218,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 fb79dcab18..4447bb4d0f 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds 2006
*/
#include "builtin.h"
+#include "config.h"
#include "lockfile.h"
#include "dir.h"
#include "cache-tree.h"
@@ -129,7 +130,7 @@ static int check_local_mod(struct object_id *head, int index_only)
ce = active_cache[pos];
if (lstat(ce->name, &st) < 0) {
- if (errno != ENOENT && errno != ENOTDIR)
+ if (!is_missing_file_error(errno))
warning_errno(_("failed to stat '%s'"), ce->name);
/* It already vanished from the working tree */
continue;
@@ -241,7 +242,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")),
@@ -254,7 +255,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
struct pathspec pathspec;
char *seen;
- gitmodules_config();
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, builtin_rm_options,
@@ -271,8 +271,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
die(_("index file corrupt"));
parse_pathspec(&pathspec, 0,
- PATHSPEC_PREFER_CWD |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ PATHSPEC_PREFER_CWD,
prefix, argv);
refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
@@ -286,7 +285,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
list.entry[list.nr].name = xstrdup(ce->name);
list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
if (list.entry[list.nr++].is_submodule &&
- !is_staging_gitmodules_ok())
+ !is_staging_gitmodules_ok(&the_index))
die (_("Please stage your changes to .gitmodules or stash them to proceed"));
}
@@ -383,13 +382,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 b8e2e74fe0..fc4f0bb5fb 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "commit.h"
#include "refs.h"
#include "pkt-line.h"
@@ -104,7 +105,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
- switch (git_config_maybe_bool("push.gpgsign", value)) {
+ switch (git_parse_maybe_bool(value)) {
case 0:
args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
break;
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 7cff1839fc..e29875b843 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "commit.h"
#include "diff.h"
#include "string-list.h"
@@ -51,26 +52,8 @@ static void insert_one_record(struct shortlog *log,
const char *oneline)
{
struct string_list_item *item;
- const char *mailbuf, *namebuf;
- size_t namelen, maillen;
- struct strbuf namemailbuf = STRBUF_INIT;
- struct ident_split ident;
- if (split_ident_line(&ident, author, strlen(author)))
- return;
-
- namebuf = ident.name_begin;
- mailbuf = ident.mail_begin;
- namelen = ident.name_end - ident.name_begin;
- maillen = ident.mail_end - ident.mail_begin;
-
- map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
- strbuf_add(&namemailbuf, namebuf, namelen);
-
- if (log->email)
- strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
-
- item = string_list_insert(&log->list, namemailbuf.buf);
+ item = string_list_insert(&log->list, author);
if (log->summary)
item->util = (void *)(UTIL_TO_INT(item) + 1);
@@ -113,9 +96,33 @@ static void insert_one_record(struct shortlog *log,
}
}
+static int parse_stdin_author(struct shortlog *log,
+ struct strbuf *out, const char *in)
+{
+ const char *mailbuf, *namebuf;
+ size_t namelen, maillen;
+ struct ident_split ident;
+
+ if (split_ident_line(&ident, in, strlen(in)))
+ return -1;
+
+ namebuf = ident.name_begin;
+ mailbuf = ident.mail_begin;
+ namelen = ident.name_end - ident.name_begin;
+ maillen = ident.mail_end - ident.mail_begin;
+
+ map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
+ strbuf_add(out, namebuf, namelen);
+ if (log->email)
+ strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf);
+
+ return 0;
+}
+
static void read_from_stdin(struct shortlog *log)
{
struct strbuf author = STRBUF_INIT;
+ struct strbuf mapped_author = STRBUF_INIT;
struct strbuf oneline = STRBUF_INIT;
static const char *author_match[2] = { "Author: ", "author " };
static const char *committer_match[2] = { "Commit: ", "committer " };
@@ -133,9 +140,15 @@ static void read_from_stdin(struct shortlog *log)
while (strbuf_getline_lf(&oneline, stdin) != EOF &&
!oneline.len)
; /* discard blanks */
- insert_one_record(log, v, oneline.buf);
+
+ strbuf_reset(&mapped_author);
+ if (parse_stdin_author(log, &mapped_author, v) < 0)
+ continue;
+
+ insert_one_record(log, mapped_author.buf, oneline.buf);
}
strbuf_release(&author);
+ strbuf_release(&mapped_author);
strbuf_release(&oneline);
}
@@ -152,7 +165,9 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
ctx.date_mode.type = DATE_NORMAL;
ctx.output_encoding = get_log_output_encoding();
- fmt = log->committer ? "%cn <%ce>" : "%an <%ae>";
+ fmt = log->committer ?
+ (log->email ? "%cN <%cE>" : "%cN") :
+ (log->email ? "%aN <%aE>" : "%aN");
format_commit_message(commit, fmt, &author, &ctx);
if (!log->summary) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d5..e8a4aa40cb 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -1,10 +1,12 @@
#include "cache.h"
-#include "commit.h"
+#include "config.h"
+#include "pretty.h"
#include "refs.h"
#include "builtin.h"
#include "color.h"
#include "argv-array.h"
#include "parse-options.h"
+#include "dir.h"
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
@@ -358,7 +360,7 @@ static void sort_ref_range(int bottom, int top)
static int append_ref(const char *refname, const struct object_id *oid,
int allow_dups)
{
- struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid, 1);
int i;
if (!commit)
@@ -391,7 +393,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
+ if (get_oid(refname + ofs, &tmp) || oidcmp(&tmp, oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
}
@@ -406,7 +408,7 @@ static int append_remote_ref(const char *refname, const struct object_id *oid,
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
+ if (get_oid(refname + ofs, &tmp) || oidcmp(&tmp, oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
}
@@ -421,14 +423,6 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
-static int count_slash(const char *s)
-{
- int cnt = 0;
- while (*s)
- if (*s++ == '/')
- cnt++;
- return cnt;
-}
static int append_matching_ref(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
@@ -438,13 +432,13 @@ static int append_matching_ref(const char *refname, const struct object_id *oid,
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
- int slash = count_slash(refname);
+ int slash = count_slashes(refname);
for (tail = refname; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
- if (wildmatch(match_ref_pattern, tail, 0, NULL))
+ if (wildmatch(match_ref_pattern, tail, 0))
return 0;
if (starts_with(refname, "refs/heads/"))
return append_head_ref(refname, oid, flag, cb_data);
@@ -520,7 +514,7 @@ static int show_independent(struct commit **rev,
static void append_one_rev(const char *av)
{
struct object_id revkey;
- if (!get_sha1(av, revkey.hash)) {
+ if (!get_oid(av, &revkey)) {
append_ref(av, &revkey, 0);
return;
}
@@ -529,7 +523,7 @@ static void append_one_rev(const char *av)
int saved_matches = ref_name_cnt;
match_ref_pattern = av;
- match_ref_slash = count_slash(av);
+ match_ref_slash = count_slashes(av);
for_each_ref(append_matching_ref, NULL);
if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS)
@@ -711,8 +705,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;
@@ -726,7 +720,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? */
@@ -735,9 +729,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
base = strtoul(reflog_base, &ep, 10);
if (*ep) {
/* Ah, that is a date spec... */
- unsigned long at;
+ 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);
}
}
@@ -746,10 +740,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
char *logmsg;
char *nth_desc;
const char *msg;
- unsigned long timestamp;
+ 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;
@@ -781,7 +775,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;
@@ -814,9 +808,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
die(Q_("cannot handle more than %d rev.",
"cannot handle more than %d revs.",
MAX_REVS), MAX_REVS);
- if (get_sha1(ref_name[num_rev], revkey.hash))
+ if (get_oid(ref_name[num_rev], &revkey))
die(_("'%s' is not a valid ref."), ref_name[num_rev]);
- commit = lookup_commit_reference(revkey.hash);
+ commit = lookup_commit_reference(&revkey);
if (!commit)
die(_("cannot find commit %s (%s)"),
ref_name[num_rev], oid_to_hex(&revkey));
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 013d241abc..41e5e71cad 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -38,7 +38,7 @@ static void show_one(const char *refname, const struct object_id *oid)
if (!deref_tags)
return;
- if (!peel_ref(refname, peeled.hash)) {
+ if (!peel_ref(refname, &peeled)) {
hex = find_unique_abbrev(peeled.hash, 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/stripspace.c b/builtin/stripspace.c
index 1e62a008cb..bdf0328869 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "parse-options.h"
#include "strbuf.h"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b4b4d29d82..4f35c98bb9 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,5 +1,7 @@
#include "builtin.h"
+#include "repository.h"
#include "cache.h"
+#include "config.h"
#include "parse-options.h"
#include "quote.h"
#include "pathspec.h"
@@ -11,13 +13,23 @@
#include "remote.h"
#include "refs.h"
#include "connect.h"
+#include "revision.h"
+#include "diffcore.h"
+#include "diff.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)
{
char *dest = NULL, *ret;
- unsigned char sha1[20];
struct strbuf sb = STRBUF_INIT;
- const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+ const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
if (!refname)
die(_("No such ref: %s"), "HEAD");
@@ -39,6 +51,20 @@ static char *get_default_remote(void)
return ret;
}
+static int print_default_remote(int argc, const char **argv, const char *prefix)
+{
+ const char *remote;
+
+ if (argc != 1)
+ die(_("submodule--helper print-default-remote takes no arguments"));
+
+ remote = get_default_remote();
+ if (remote)
+ printf("%s\n", remote);
+
+ return 0;
+}
+
static int starts_with_dot_slash(const char *str)
{
return str[0] == '.' && is_dir_sep(str[1]);
@@ -218,6 +244,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;
@@ -232,8 +316,7 @@ static int module_list_compute(int argc, const char **argv,
int i, result = 0;
char *ps_matched = NULL;
parse_pathspec(pathspec, 0,
- PATHSPEC_PREFER_FULL |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ PATHSPEC_PREFER_FULL,
prefix, argv);
if (pathspec->nr)
@@ -274,12 +357,10 @@ static void module_list_active(struct module_list *list)
int i;
struct module_list active_modules = MODULE_LIST_INIT;
- gitmodules_config();
-
for (i = 0; i < list->nr; i++) {
const struct cache_entry *ce = list->entries[i];
- if (!is_submodule_initialized(ce->name))
+ if (!is_submodule_active(the_repository, ce->name))
continue;
ALLOC_GROW(active_modules.entries,
@@ -292,6 +373,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;
@@ -330,26 +430,31 @@ 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 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;
- /* Only loads from .gitmodules, no overlay with .git/config */
- gitmodules_config();
-
- 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_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -361,10 +466,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
*
* Set active flag for the submodule being initialized
*/
- if (!is_submodule_initialized(path)) {
- strbuf_reset(&sb);
+ if (!is_submodule_active(the_repository, path)) {
strbuf_addf(&sb, "submodule.%s.active", sub->name);
git_config_set_gently(sb.buf, "true");
+ strbuf_reset(&sb);
}
/*
@@ -372,7 +477,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)
@@ -404,14 +508,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) {
@@ -431,12 +535,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")),
@@ -461,8 +571,165 @@ 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 == '+')
+ printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
+
+ 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(&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;
+
+ if (refs_head_ref(get_submodule_ref_store(path),
+ 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;
}
@@ -474,8 +741,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
if (argc != 2)
usage(_("git submodule--helper name <path>"));
- gitmodules_config();
- sub = submodule_from_path(null_sha1, argv[1]);
+ sub = submodule_from_path(&null_oid, argv[1]);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@ -486,6 +752,309 @@ 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(&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(&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,
int quiet, int progress)
@@ -779,6 +1348,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
struct strbuf *out)
{
const struct submodule *sub = NULL;
+ const char *url = NULL;
+ const char *update_string;
+ enum submodule_update_type update_type;
+ char *key;
struct strbuf displaypath_sb = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
const char *displaypath = NULL;
@@ -794,7 +1367,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
- sub = submodule_from_path(null_sha1, ce->name);
+ sub = submodule_from_path(&null_oid, ce->name);
if (suc->recursive_prefix)
displaypath = relative_path(suc->recursive_prefix,
@@ -807,21 +1380,34 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
+ key = xstrfmt("submodule.%s.update", sub->name);
+ if (!repo_config_get_string_const(the_repository, key, &update_string)) {
+ update_type = parse_submodule_update_type(update_string);
+ } else {
+ update_type = sub->update_strategy.type;
+ }
+ free(key);
+
if (suc->update.type == SM_UPDATE_NONE
|| (suc->update.type == SM_UPDATE_UNSPECIFIED
- && sub->update_strategy.type == SM_UPDATE_NONE)) {
+ && update_type == SM_UPDATE_NONE)) {
strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
strbuf_addch(out, '\n');
goto cleanup;
}
/* Check if the submodule has been initialized. */
- if (!is_submodule_initialized(ce->name)) {
+ if (!is_submodule_active(the_repository, ce->name)) {
next_submodule_warn_missing(suc, out, displaypath);
goto cleanup;
}
strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ if (repo_config_get_string_const(the_repository, sb.buf, &url))
+ url = sub->url;
+
+ strbuf_reset(&sb);
strbuf_addf(&sb, "%s/.git", ce->name);
needs_cloning = !file_exists(sb.buf);
@@ -850,7 +1436,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_push(&child->args, "--depth=1");
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
- argv_array_pushl(&child->args, "--url", sub->url, NULL);
+ argv_array_pushl(&child->args, "--url", url, NULL);
if (suc->references.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &suc->references)
@@ -929,7 +1515,7 @@ static int update_clone_task_finished(int result,
const struct cache_entry *ce;
struct submodule_update_clone *suc = suc_cb;
- int *idxP = *(int**)idx_task_cb;
+ int *idxP = idx_task_cb;
int idx = *idxP;
free(idxP);
@@ -959,10 +1545,19 @@ static int update_clone_task_finished(int result,
return 0;
}
+static int gitmodules_update_clone_config(const char *var, const char *value,
+ void *cb)
+{
+ int *max_jobs = cb;
+ if (!strcmp(var, "submodule.fetchjobs"))
+ *max_jobs = parse_submodule_fetchjobs(var, value);
+ return 0;
+}
+
static int update_clone(int argc, const char **argv, const char *prefix)
{
const char *update = NULL;
- int max_jobs = -1;
+ int max_jobs = 1;
struct string_list_item *item;
struct pathspec pathspec;
struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
@@ -999,6 +1594,9 @@ static int update_clone(int argc, const char **argv, const char *prefix)
};
suc.prefix = prefix;
+ config_from_gitmodules(gitmodules_update_clone_config, &max_jobs);
+ git_config(gitmodules_update_clone_config, &max_jobs);
+
argc = parse_options(argc, argv, prefix, module_update_clone_options,
git_submodule_helper_usage, 0);
@@ -1012,13 +1610,6 @@ static int update_clone(int argc, const char **argv, const char *prefix)
if (pathspec.nr)
suc.warn_if_uninitialized = 1;
- /* Overlay the parsed .gitmodules file with .git/config */
- gitmodules_config();
- git_config(submodule_config, NULL);
-
- if (max_jobs < 0)
- max_jobs = parallel_submodules();
-
run_processes_parallel(max_jobs,
update_clone_get_next_task,
update_clone_start_failure,
@@ -1056,19 +1647,23 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix
static const char *remote_submodule_branch(const char *path)
{
const struct submodule *sub;
- gitmodules_config();
- git_config(submodule_config, NULL);
+ const char *branch = NULL;
+ char *key;
- sub = submodule_from_path(null_sha1, path);
+ sub = submodule_from_path(&null_oid, path);
if (!sub)
return NULL;
- if (!sub->branch)
+ key = xstrfmt("submodule.%s.branch", sub->name);
+ if (repo_config_get_string_const(the_repository, key, &branch))
+ branch = sub->branch;
+ free(key);
+
+ if (!branch)
return "master";
- if (!strcmp(sub->branch, ".")) {
- unsigned char sha1[20];
- const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+ if (!strcmp(branch, ".")) {
+ const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
if (!refname)
die(_("No such ref: %s"), "HEAD");
@@ -1084,7 +1679,7 @@ static const char *remote_submodule_branch(const char *path)
return refname;
}
- return sub->branch;
+ return branch;
}
static int resolve_remote_submodule_branch(int argc, const char **argv,
@@ -1107,9 +1702,28 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
static int push_check(int argc, const char **argv, const char *prefix)
{
struct remote *remote;
+ const char *superproject_head;
+ char *head;
+ int detached_head = 0;
+ struct object_id head_oid;
- if (argc < 2)
- die("submodule--helper push-check requires at least 1 argument");
+ if (argc < 3)
+ die("submodule--helper push-check requires at least 2 arguments");
+
+ /*
+ * superproject's resolved head ref.
+ * if HEAD then the superproject is in a detached head state, otherwise
+ * it will be the resolved head ref.
+ */
+ superproject_head = argv[1];
+ argv++;
+ argc--;
+ /* Get the submodule's head ref and determine if it is detached */
+ head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+ if (!head)
+ die(_("Failed to resolve HEAD as a valid ref."));
+ if (!strcmp(head, "HEAD"))
+ detached_head = 1;
/*
* The remote must be configured.
@@ -1132,18 +1746,31 @@ static int push_check(int argc, const char **argv, const char *prefix)
if (rs->pattern || rs->matching)
continue;
- /*
- * LHS must match a single ref
- * NEEDSWORK: add logic to special case 'HEAD' once
- * working with submodules in a detached head state
- * ceases to be the norm.
- */
- if (count_refspec_match(rs->src, local_refs, NULL) != 1)
+ /* LHS must match a single ref */
+ switch (count_refspec_match(rs->src, local_refs, NULL)) {
+ case 1:
+ break;
+ case 0:
+ /*
+ * If LHS matches 'HEAD' then we need to ensure
+ * that it matches the same named branch
+ * checked out in the superproject.
+ */
+ if (!strcmp(rs->src, "HEAD")) {
+ if (!detached_head &&
+ !strcmp(head, superproject_head))
+ break;
+ die("HEAD does not match the named branch in the superproject");
+ }
+ /* fallthrough */
+ default:
die("src refspec '%s' must name a ref",
rs->src);
+ }
}
free_refspec(refspec_nr, refspec);
}
+ free(head);
return 0;
}
@@ -1172,9 +1799,6 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, embed_gitdir_options,
git_submodule_helper_usage, 0);
- gitmodules_config();
- git_config(submodule_config, NULL);
-
if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
return 1;
@@ -1190,9 +1814,7 @@ static int is_active(int argc, const char **argv, const char *prefix)
if (argc != 2)
die("submodule--helper is-active takes exactly 1 argument");
- gitmodules_config();
-
- return !is_submodule_initialized(argv[1]);
+ return !is_submodule_active(the_repository, argv[1]);
}
/*
@@ -1235,6 +1857,10 @@ static struct cmd_struct commands[] = {
{"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0},
{"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},
@@ -1245,9 +1871,8 @@ static struct cmd_struct commands[] = {
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
{
int i;
- if (argc < 2)
- die(_("submodule--helper subcommand must be "
- "called with a subcommand"));
+ if (argc < 2 || !strcmp(argv[1], "-h"))
+ usage("git submodule--helper <command>");
for (i = 0; i < ARRAY_SIZE(commands); i++) {
if (!strcmp(argv[1], commands[i].cmd)) {
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index 70addef158..80237f0df1 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "config.h"
#include "cache.h"
#include "refs.h"
#include "parse-options.h"
@@ -11,9 +12,8 @@ static const char * const git_symbolic_ref_usage[] = {
static int check_symref(const char *HEAD, int quiet, int shorten, int print)
{
- unsigned char sha1[20];
int flag;
- const char *refname = resolve_ref_unsafe(HEAD, 0, sha1, &flag);
+ const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag);
if (!refname)
die("No such ref: %s", HEAD);
@@ -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 bdf1e88e93..da186691ed 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -7,6 +7,7 @@
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "refs.h"
#include "tag.h"
@@ -31,7 +32,8 @@ static const char * const git_tag_usage[] = {
static unsigned int colopts;
static int force_sign_annotate;
-static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
+ struct ref_format *format)
{
struct ref_array array;
char *to_free = NULL;
@@ -42,23 +44,24 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
if (filter->lines == -1)
filter->lines = 0;
- if (!format) {
+ if (!format->format) {
if (filter->lines) {
to_free = xstrfmt("%s %%(contents:lines=%d)",
"%(align:15)%(refname:lstrip=2)%(end)",
filter->lines);
- format = to_free;
+ format->format = to_free;
} else
- format = "%(refname:lstrip=2)";
+ format->format = "%(refname:lstrip=2)";
}
- verify_ref_format(format);
+ if (verify_ref_format(format))
+ die(_("unable to parse format string"));
filter->with_commit_tag_algo = 1;
filter_refs(&array, filter, FILTER_REFS_TAGS);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++)
- show_ref_array_item(array.items[i], format, 0);
+ show_ref_array_item(array.items[i], format);
ref_array_clear(&array);
free(to_free);
@@ -66,7 +69,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
}
typedef int (*each_tag_name_fn)(const char *name, const char *ref,
- const unsigned char *sha1, const void *cb_data);
+ const struct object_id *oid, const void *cb_data);
static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
const void *cb_data)
@@ -74,17 +77,17 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
const char **p;
struct strbuf ref = STRBUF_INIT;
int had_error = 0;
- unsigned char sha1[20];
+ struct object_id oid;
for (p = argv; *p; p++) {
strbuf_reset(&ref);
strbuf_addf(&ref, "refs/tags/%s", *p);
- if (read_ref(ref.buf, sha1)) {
+ if (read_ref(ref.buf, &oid)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
}
- if (fn(*p, ref.buf, sha1, cb_data))
+ if (fn(*p, ref.buf, &oid, cb_data))
had_error = 1;
}
strbuf_release(&ref);
@@ -92,29 +95,29 @@ 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 unsigned char *sha1, const void *cb_data)
+ const struct object_id *oid, const void *cb_data)
{
- if (delete_ref(NULL, ref, sha1, 0))
+ if (delete_ref(NULL, ref, oid, 0))
return 1;
- printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
return 0;
}
static int verify_tag(const char *name, const char *ref,
- const unsigned char *sha1, const void *cb_data)
+ const struct object_id *oid, const void *cb_data)
{
int flags;
- const char *fmt_pretty = cb_data;
+ const struct ref_format *format = cb_data;
flags = GPG_VERIFY_VERBOSE;
- if (fmt_pretty)
+ if (format->format)
flags = GPG_VERIFY_OMIT_STATUS;
- if (gpg_verify_tag(sha1, name, flags))
+ if (gpg_verify_tag(oid, name, flags))
return -1;
- if (fmt_pretty)
- pretty_print_ref(name, sha1, fmt_pretty);
+ if (format->format)
+ pretty_print_ref(name, oid->hash, format);
return 0;
}
@@ -133,30 +136,6 @@ static const char tag_template_nocleanup[] =
"Lines starting with '%c' will be kept; you may remove them"
" yourself if you want to.\n");
-/* Parse arg given and add it the ref_sorting array */
-static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
-{
- struct ref_sorting *s;
- int len;
-
- s = xcalloc(1, sizeof(*s));
- s->next = *sorting_tail;
- *sorting_tail = s;
-
- if (*arg == '-') {
- s->reverse = 1;
- arg++;
- }
- if (skip_prefix(arg, "version:", &arg) ||
- skip_prefix(arg, "v:", &arg))
- s->version = 1;
-
- len = strlen(arg);
- s->atom = parse_ref_filter_atom(arg, arg+len);
-
- return 0;
-}
-
static int git_tag_config(const char *var, const char *value, void *cb)
{
int status;
@@ -165,7 +144,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_sorting_string(value, sorting_tail);
+ parse_ref_sorting(sorting_tail, value);
return 0;
}
@@ -179,16 +158,16 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (starts_with(var, "column."))
return git_column_config(var, value, "tag", &colopts);
- return git_default_config(var, value, cb);
+ return git_color_default_config(var, value, cb);
}
-static void write_tag_body(int fd, const unsigned char *sha1)
+static void write_tag_body(int fd, const struct object_id *oid)
{
unsigned long size;
enum object_type type;
char *buf, *sp;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf)
return;
/* skip header */
@@ -204,17 +183,18 @@ static void write_tag_body(int fd, const unsigned char *sha1)
free(buf);
}
-static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
+static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
{
if (sign && do_sign(buf) < 0)
return error(_("unable to sign the tag"));
- if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 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,
@@ -223,15 +203,15 @@ struct create_tag_options {
} cleanup_mode;
};
-static void create_tag(const unsigned char *object, const char *tag,
+static void create_tag(const struct object_id *object, const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- unsigned char *prev, unsigned char *result)
+ struct object_id *prev, struct object_id *result)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
char *path = NULL;
- type = sha1_object_info(object, NULL);
+ type = sha1_object_info(object->hash, NULL);
if (type <= OBJ_NONE)
die(_("bad object type."));
@@ -240,12 +220,12 @@ static void create_tag(const unsigned char *object, const char *tag,
"type %s\n"
"tag %s\n"
"tagger %s\n\n",
- sha1_to_hex(object),
- typename(type),
+ oid_to_hex(object),
+ 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: */
@@ -254,7 +234,10 @@ static void create_tag(const unsigned char *object, const char *tag,
if (fd < 0)
die_errno(_("could not create file '%s'"), path);
- if (!is_null_sha1(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;
@@ -296,7 +279,7 @@ static void create_tag(const unsigned char *object, const char *tag,
}
}
-static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
+static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
{
enum object_type type;
struct commit *c;
@@ -310,17 +293,17 @@ static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
strbuf_addstr(sb, rla);
} else {
strbuf_addstr(sb, "tag: tagging ");
- strbuf_add_unique_abbrev(sb, sha1, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV);
}
strbuf_addstr(sb, " (");
- type = sha1_object_info(sha1, NULL);
+ type = sha1_object_info(oid->hash, NULL);
switch (type) {
default:
strbuf_addstr(sb, "object of unknown type");
break;
case OBJ_COMMIT:
- if ((buf = read_sha1_file(sha1, &type, &size)) != NULL) {
+ if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) {
subject_len = find_commit_subject(buf, &subject_start);
strbuf_insert(sb, sb->len, subject_start, subject_len);
} else {
@@ -328,7 +311,7 @@ static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
}
free(buf);
- if ((c = lookup_commit_reference(sha1)) != NULL)
+ if ((c = lookup_commit_reference(oid)) != NULL)
strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
break;
case OBJ_TREE:
@@ -378,7 +361,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
struct strbuf ref = STRBUF_INIT;
struct strbuf reflog_msg = STRBUF_INIT;
- unsigned char object[20], prev[20];
+ struct object_id object, prev;
const char *object_ref, *tag;
struct create_tag_options opt;
char *cleanup_arg = NULL;
@@ -391,8 +374,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf err = STRBUF_INIT;
struct ref_filter filter;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- const char *format = NULL;
+ 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"),
@@ -407,12 +391,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")),
@@ -430,7 +415,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
parse_opt_object_name, (intptr_t) "HEAD"
},
- OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
+ OPT_STRING( 0 , "format", &format.format, N_("format"),
+ N_("format to use for the output")),
+ OPT__COLOR(&format.use_color, N_("respect format colors")),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END()
};
@@ -460,6 +447,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
cmdmode = 'l';
}
+ if (cmdmode == 'l')
+ setup_auto_pager("tag", 1);
+
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
@@ -482,7 +472,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
run_column_filter(colopts, &copts);
}
filter.name_patterns = argv;
- ret = list_tags(&filter, sorting, format);
+ ret = list_tags(&filter, sorting, &format);
if (column_active(colopts))
stop_column_filter();
return ret;
@@ -500,9 +490,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (cmdmode == 'd')
return for_each_tag_name(argv, delete_tag, NULL);
if (cmdmode == 'v') {
- if (format)
- verify_ref_format(format);
- return for_each_tag_name(argv, verify_tag, format);
+ if (format.format && verify_ref_format(&format))
+ usage_with_options(git_tag_usage, options);
+ return for_each_tag_name(argv, verify_tag, &format);
}
if (msg.given || msgfile) {
@@ -528,18 +518,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (argc > 2)
die(_("too many params"));
- if (get_sha1(object_ref, object))
+ if (get_oid(object_ref, &object))
die(_("Failed to resolve '%s' as a valid ref."), object_ref);
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (read_ref(ref.buf, prev))
- hashclr(prev);
+ 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;
@@ -550,28 +541,29 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
else
die(_("Invalid cleanup mode %s"), cleanup_arg);
- create_reflog_msg(object, &reflog_msg);
+ create_reflog_msg(&object, &reflog_msg);
if (create_tag_object) {
if (force_sign_annotate && !annotate)
opt.sign = 1;
- create_tag(object, tag, &buf, &opt, prev, object);
+ create_tag(&object, tag, &buf, &opt, &prev, &object);
}
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, ref.buf, object, prev,
+ 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_sha1(prev) && hashcmp(prev, object))
- printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
-
- strbuf_release(&err);
- strbuf_release(&buf);
- strbuf_release(&ref);
- strbuf_release(&reflog_msg);
+ if (force && !is_null_oid(&prev) && oidcmp(&prev, &object))
+ printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV));
+
+ UNLEAK(buf);
+ UNLEAK(ref);
+ UNLEAK(reflog_msg);
+ UNLEAK(msg);
+ UNLEAK(err);
return 0;
}
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 6fc6bcdf7f..32e0155577 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -1,6 +1,7 @@
#include "builtin.h"
+#include "config.h"
-static char *create_temp_file(unsigned char *sha1)
+static char *create_temp_file(struct object_id *oid)
{
static char path[50];
void *buf;
@@ -8,13 +9,13 @@ static char *create_temp_file(unsigned char *sha1)
unsigned long size;
int fd;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf || type != OBJ_BLOB)
- die("unable to read blob object %s", sha1_to_hex(sha1));
+ die("unable to read blob object %s", oid_to_hex(oid));
xsnprintf(path, sizeof(path), ".merge_file_XXXXXX");
fd = xmkstemp(path);
- if (write_in_full(fd, buf, size) != size)
+ if (write_in_full(fd, buf, size) < 0)
die_errno("unable to write temp-file");
close(fd);
return path;
@@ -22,15 +23,15 @@ static char *create_temp_file(unsigned char *sha1)
int cmd_unpack_file(int argc, const char **argv, const char *prefix)
{
- unsigned char sha1[20];
+ struct object_id oid;
if (argc != 2 || !strcmp(argv[1], "-h"))
usage("git unpack-file <sha1>");
- if (get_sha1(argv[1], sha1))
+ if (get_oid(argv[1], &oid))
die("Not a valid object name %s", argv[1]);
git_config(git_default_config, NULL);
- puts(create_temp_file(sha1));
+ puts(create_temp_file(&oid));
return 0;
}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 4532aa0831..6620feec68 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "object.h"
#include "delta.h"
#include "pack.h"
@@ -20,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;
/*
@@ -61,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;
}
@@ -112,8 +113,7 @@ static void *get_data(unsigned long size)
break;
if (ret != Z_OK) {
error("inflate returned %d", ret);
- free(buf);
- buf = NULL;
+ FREE_AND_NULL(buf);
if (!recover)
exit(1);
has_errors = 1;
@@ -127,7 +127,7 @@ static void *get_data(unsigned long size)
}
struct delta_info {
- unsigned char base_sha1[20];
+ struct object_id base_oid;
unsigned nr;
off_t base_offset;
unsigned long size;
@@ -137,13 +137,13 @@ struct delta_info {
static struct delta_info *delta_list;
-static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
+static void add_delta_to_list(unsigned nr, const struct object_id *base_oid,
off_t base_offset,
void *delta, unsigned long size)
{
struct delta_info *info = xmalloc(sizeof(*info));
- hashcpy(info->base_sha1, base_sha1);
+ oidcpy(&info->base_oid, base_oid);
info->base_offset = base_offset;
info->size = size;
info->delta = delta;
@@ -154,10 +154,11 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
struct obj_info {
off_t offset;
- unsigned char sha1[20];
+ struct object_id oid;
struct object *obj;
};
+/* Remember to update object flag allocation in object.h */
#define FLAG_OPEN (1u<<20)
#define FLAG_WRITTEN (1u<<21)
@@ -170,9 +171,10 @@ static unsigned nr_objects;
*/
static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
{
- unsigned char sha1[20];
+ struct object_id oid;
- if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 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;
}
@@ -237,19 +239,21 @@ 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].sha1) < 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].sha1) < 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);
- blob = lookup_blob(obj_list[nr].sha1);
+ blob = lookup_blob(&obj_list[nr].oid);
if (blob)
blob->object.flags |= FLAG_WRITTEN;
else
@@ -258,11 +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].sha1);
+ 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].sha1, type, size, buf, &eaten);
+ 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;
@@ -296,7 +301,7 @@ static void added_object(unsigned nr, enum object_type type,
struct delta_info *info;
while ((info = *p) != NULL) {
- if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
+ if (!oidcmp(&info->base_oid, &obj_list[nr].oid) ||
info->base_offset == obj_list[nr].offset) {
*p = info->next;
p = &delta_list;
@@ -320,12 +325,12 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
free(buf);
}
-static int resolve_against_held(unsigned nr, const unsigned char *base,
+static int resolve_against_held(unsigned nr, const struct object_id *base,
void *delta_data, unsigned long delta_size)
{
struct object *obj;
struct obj_buffer *obj_buffer;
- obj = lookup_object(base);
+ obj = lookup_object(base->hash);
if (!obj)
return 0;
obj_buffer = lookup_object_buffer(obj);
@@ -341,25 +346,25 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
{
void *delta_data, *base;
unsigned long base_size;
- unsigned char base_sha1[20];
+ struct object_id base_oid;
if (type == OBJ_REF_DELTA) {
- hashcpy(base_sha1, fill(20));
- use(20);
+ 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);
return;
}
- if (has_sha1_file(base_sha1))
+ if (has_object_file(&base_oid))
; /* Ok we have this one */
- else if (resolve_against_held(nr, base_sha1,
+ else if (resolve_against_held(nr, &base_oid,
delta_data, delta_size))
return; /* we are done */
else {
/* cannot resolve yet --- queue it */
- hashclr(obj_list[nr].sha1);
- add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
+ oidclr(&obj_list[nr].oid);
+ add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size);
return;
}
} else {
@@ -393,14 +398,14 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
lo = 0;
hi = nr;
while (lo < hi) {
- mid = (lo + hi)/2;
+ mid = lo + (hi - lo) / 2;
if (base_offset < obj_list[mid].offset) {
hi = mid;
} else if (base_offset > obj_list[mid].offset) {
lo = mid + 1;
} else {
- hashcpy(base_sha1, obj_list[mid].sha1);
- base_found = !is_null_sha1(base_sha1);
+ oidcpy(&base_oid, &obj_list[mid].oid);
+ base_found = !is_null_oid(&base_oid);
break;
}
}
@@ -409,19 +414,19 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
* The delta base object is itself a delta that
* has not been resolved yet.
*/
- hashclr(obj_list[nr].sha1);
- add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
+ oidclr(&obj_list[nr].oid);
+ add_delta_to_list(nr, &null_oid, base_offset, delta_data, delta_size);
return;
}
}
- if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
+ if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
return;
- base = read_sha1_file(base_sha1, &type, &base_size);
+ base = read_sha1_file(base_oid.hash, &type, &base_size);
if (!base) {
error("failed to read delta-pack base object %s",
- sha1_to_hex(base_sha1));
+ oid_to_hex(&base_oid));
if (!recover)
exit(1);
has_errors = 1;
@@ -505,7 +510,7 @@ static void unpack_all(void)
int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
{
int i;
- unsigned char sha1[20];
+ struct object_id oid;
check_replace_refs = 0;
@@ -563,15 +568,15 @@ 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(sha1, &ctx);
+ the_hash_algo->update_fn(&ctx, buffer, offset);
+ the_hash_algo->final_fn(oid.hash, &ctx);
if (strict)
write_rest();
- if (hashcmp(fill(20), sha1))
+ if (hashcmp(fill(the_hash_algo->rawsz), oid.hash))
die("final sha1 did not match");
- use(20);
+ 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 19216595fb..1af8a00b88 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "config.h"
#include "lockfile.h"
#include "quote.h"
#include "cache-tree.h"
@@ -15,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
@@ -31,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;
@@ -227,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
@@ -257,7 +261,7 @@ static int remove_one_path(const char *path)
*/
static int process_lstat_error(const char *path, int err)
{
- if (err == ENOENT || err == ENOTDIR)
+ if (is_missing_file_error(err))
return remove_one_path(path);
return error("lstat(\"%s\"): %s", path, strerror(err));
}
@@ -279,15 +283,17 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
fill_stat_cache_info(ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(ce->oid.hash, path, st,
+ if (index_path(&ce->oid, path, st,
info_only ? 0 : HASH_WRITE_OBJECT)) {
free(ce);
return -1;
}
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
- if (add_cache_entry(ce, option))
+ if (add_cache_entry(ce, option)) {
+ free(ce);
return error("%s: cannot add to the index - missing --add option?", path);
+ }
return 0;
}
@@ -325,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);
@@ -351,7 +357,7 @@ 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. */
@@ -445,7 +451,8 @@ static void update_one(const char *path)
int stat_errno = 0;
struct stat st;
- if (mark_valid_only || mark_skip_worktree_only || force_remove)
+ 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;
@@ -466,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))
@@ -685,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);
}
@@ -727,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.
*/
@@ -923,7 +935,9 @@ 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;
- struct lock_file *lock_file;
+ 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;
int parseopt_state = PARSE_OPT_UNKNOWN;
@@ -1014,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()
};
@@ -1022,11 +1046,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL);
- /* We can't free this memory, it becomes part of a linked list parsed atexit() */
- lock_file = xcalloc(1, sizeof(struct lock_file));
-
/* we will diagnose later if it turns out that we need to update it */
- newfd = hold_locked_index(lock_file, 0);
+ newfd = hold_locked_index(&lock_file, 0);
if (newfd < 0)
lock_error = errno;
@@ -1155,17 +1176,33 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
die("BUG: bad untracked_cache value: %d", untracked_cache);
}
- if (active_cache_changed) {
+ 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 || force_write) {
if (newfd < 0) {
if (refresh_args.flags & REFRESH_QUIET)
exit(128);
unable_to_lock_die(get_index_file(), lock_error);
}
- 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");
}
- rollback_lock_file(lock_file);
+ rollback_lock_file(&lock_file);
return has_errors ? 1 : 0;
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 0b2ecf41ae..4b4714b3fd 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "config.h"
#include "refs.h"
#include "builtin.h"
#include "parse-options.h"
@@ -93,10 +94,10 @@ static char *parse_refname(struct strbuf *input, const char **next)
* provided but cannot be converted to a SHA-1, die. flags can
* include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY.
*/
-static int parse_next_sha1(struct strbuf *input, const char **next,
- unsigned char *sha1,
- const char *command, const char *refname,
- int flags)
+static int parse_next_oid(struct strbuf *input, const char **next,
+ struct object_id *oid,
+ const char *command, const char *refname,
+ int flags)
{
struct strbuf arg = STRBUF_INIT;
int ret = 0;
@@ -114,11 +115,11 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
(*next)++;
*next = parse_arg(*next, &arg);
if (arg.len) {
- if (get_sha1(arg.buf, sha1))
+ if (get_oid(arg.buf, oid))
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
- hashclr(sha1);
+ oidclr(oid);
}
} else {
/* With -z, read the next NUL-terminated line */
@@ -132,13 +133,13 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
*next += arg.len;
if (arg.len) {
- if (get_sha1(arg.buf, sha1))
+ if (get_oid(arg.buf, oid))
goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */
warning("%s %s: missing <newvalue>, treating as zero",
command, refname);
- hashclr(sha1);
+ oidclr(oid);
} else {
/*
* With -z, an empty non-required value means
@@ -181,26 +182,25 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char new_sha1[20];
- unsigned char old_sha1[20];
+ struct object_id new_oid, old_oid;
int have_old;
refname = parse_refname(input, &next);
if (!refname)
die("update: missing <ref>");
- if (parse_next_sha1(input, &next, new_sha1, "update", refname,
- PARSE_SHA1_ALLOW_EMPTY))
+ if (parse_next_oid(input, &next, &new_oid, "update", refname,
+ PARSE_SHA1_ALLOW_EMPTY))
die("update %s: missing <newvalue>", refname);
- have_old = !parse_next_sha1(input, &next, old_sha1, "update", refname,
- PARSE_SHA1_OLD);
+ have_old = !parse_next_oid(input, &next, &old_oid, "update", refname,
+ PARSE_SHA1_OLD);
if (*next != line_termination)
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname,
- new_sha1, have_old ? old_sha1 : NULL,
+ &new_oid, have_old ? &old_oid : NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -217,22 +217,22 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char new_sha1[20];
+ struct object_id new_oid;
refname = parse_refname(input, &next);
if (!refname)
die("create: missing <ref>");
- if (parse_next_sha1(input, &next, new_sha1, "create", refname, 0))
+ if (parse_next_oid(input, &next, &new_oid, "create", refname, 0))
die("create %s: missing <newvalue>", refname);
- if (is_null_sha1(new_sha1))
+ if (is_null_oid(&new_oid))
die("create %s: zero <newvalue>", refname);
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
- if (ref_transaction_create(transaction, refname, new_sha1,
+ if (ref_transaction_create(transaction, refname, &new_oid,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@@ -249,18 +249,18 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char old_sha1[20];
+ struct object_id old_oid;
int have_old;
refname = parse_refname(input, &next);
if (!refname)
die("delete: missing <ref>");
- if (parse_next_sha1(input, &next, old_sha1, "delete", refname,
- PARSE_SHA1_OLD)) {
+ if (parse_next_oid(input, &next, &old_oid, "delete", refname,
+ PARSE_SHA1_OLD)) {
have_old = 0;
} else {
- if (is_null_sha1(old_sha1))
+ if (is_null_oid(&old_oid))
die("delete %s: zero <oldvalue>", refname);
have_old = 1;
}
@@ -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_sha1 : NULL,
+ have_old ? &old_oid : NULL,
update_flags, msg, &err))
die("%s", err.buf);
@@ -285,20 +285,20 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
{
struct strbuf err = STRBUF_INIT;
char *refname;
- unsigned char old_sha1[20];
+ struct object_id old_oid;
refname = parse_refname(input, &next);
if (!refname)
die("verify: missing <ref>");
- if (parse_next_sha1(input, &next, old_sha1, "verify", refname,
- PARSE_SHA1_OLD))
- hashclr(old_sha1);
+ if (parse_next_oid(input, &next, &old_oid, "verify", refname,
+ PARSE_SHA1_OLD))
+ oidclr(&old_oid);
if (*next != line_termination)
die("verify %s: extra input: %s", refname, next);
- if (ref_transaction_verify(transaction, refname, old_sha1,
+ if (ref_transaction_verify(transaction, refname, &old_oid,
update_flags, &err))
die("%s", err.buf);
@@ -312,7 +312,7 @@ 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;
+ update_flags |= REF_NO_DEREF;
else
die("option unknown: %s", next);
return next + 8;
@@ -354,7 +354,7 @@ static void update_refs_stdin(struct ref_transaction *transaction)
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname, *oldval;
- unsigned char sha1[20], oldsha1[20];
+ struct object_id oid, oldoid;
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
unsigned int flags = 0;
int create_reflog = 0;
@@ -411,7 +411,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
refname = argv[0];
value = argv[1];
oldval = argv[2];
- if (get_sha1(value, sha1))
+ if (get_oid(value, &oid))
die("%s: not a valid SHA1", value);
}
@@ -421,23 +421,23 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
* The empty string implies that the reference
* must not already exist:
*/
- hashclr(oldsha1);
- else if (get_sha1(oldval, oldsha1))
+ oidclr(&oldoid);
+ else if (get_oid(oldval, &oldoid))
die("%s: not a valid old SHA1", oldval);
}
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_sha1(oldsha1)) ? oldsha1 : NULL,
+ (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
flags);
else
- return update_ref(msg, refname, sha1, oldval ? oldsha1 : 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 6c8cc3edc1..4321a34456 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "parse-options.h"
@@ -11,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-archive.c b/builtin/upload-archive.c
index cde06977b7..84532ae9a9 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -22,7 +22,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
struct argv_array sent_argv = ARGV_ARRAY_INIT;
const char *arg_cmd = "argument ";
- if (argc != 2)
+ if (argc != 2 || !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
if (!enter_repo(argv[1], 0))
@@ -76,6 +76,9 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
struct child_process writer = { argv };
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage(upload_archive_usage);
+
/*
* Set up sideband subprocess.
*
diff --git a/builtin/var.c b/builtin/var.c
index aedbb53a2d..6c6f46b4ae 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -4,6 +4,7 @@
* Copyright (C) Eric Biederman, 2005
*/
#include "builtin.h"
+#include "config.h"
static const char var_usage[] = "git var (-l | <variable>)";
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 38bedf8f9f..05315ea7c9 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -6,6 +6,7 @@
* Based on git-verify-tag
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "commit.h"
#include "run-command.h"
@@ -18,14 +19,14 @@ static const char * const verify_commit_usage[] = {
NULL
};
-static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, unsigned flags)
+static int run_gpg_verify(const struct object_id *oid, const char *buf, unsigned long size, unsigned flags)
{
struct signature_check signature_check;
int ret;
memset(&signature_check, 0, sizeof(signature_check));
- ret = check_commit_signature(lookup_commit(sha1), &signature_check);
+ ret = check_commit_signature(lookup_commit(oid), &signature_check);
print_signature_buffer(&signature_check, flags);
signature_check_clear(&signature_check);
@@ -35,22 +36,22 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l
static int verify_commit(const char *name, unsigned flags)
{
enum object_type type;
- unsigned char sha1[20];
+ struct object_id oid;
char *buf;
unsigned long size;
int ret;
- if (get_sha1(name, sha1))
+ if (get_oid(name, &oid))
return error("commit '%s' not found.", name);
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid.hash, &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(sha1, buf, size, flags);
+ ret = run_gpg_verify(&oid, buf, size, flags);
free(buf);
return ret;
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index c94e156932..c2a1a5c504 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "run-command.h"
#include "parse-options.h"
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 5199553d91..ad7b79fa5c 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -6,6 +6,7 @@
* Based on git-verify-tag.sh
*/
#include "cache.h"
+#include "config.h"
#include "builtin.h"
#include "tag.h"
#include "run-command.h"
@@ -31,11 +32,11 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
unsigned flags = 0;
- char *fmt_pretty = NULL;
+ struct ref_format format = REF_FORMAT_INIT;
const struct option verify_tag_options[] = {
OPT__VERBOSE(&verbose, N_("print tag contents")),
OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW),
- OPT_STRING( 0 , "format", &fmt_pretty, N_("format"), N_("format to use for the output")),
+ OPT_STRING(0, "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END()
};
@@ -49,26 +50,29 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
if (verbose)
flags |= GPG_VERIFY_VERBOSE;
- if (fmt_pretty) {
- verify_ref_format(fmt_pretty);
+ if (format.format) {
+ if (verify_ref_format(&format))
+ usage_with_options(verify_tag_usage,
+ verify_tag_options);
flags |= GPG_VERIFY_OMIT_STATUS;
}
while (i < argc) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *name = argv[i++];
- if (get_sha1(name, sha1)) {
+
+ if (get_oid(name, &oid)) {
had_error = !!error("tag '%s' not found.", name);
continue;
}
- if (gpg_verify_tag(sha1, name, flags)) {
+ if (gpg_verify_tag(&oid, name, flags)) {
had_error = 1;
continue;
}
- if (fmt_pretty)
- pretty_print_ref(name, sha1, fmt_pretty);
+ if (format.format)
+ pretty_print_ref(name, oid.hash, &format);
}
return had_error;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index ff5dfd2b10..670555dedd 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -1,4 +1,6 @@
#include "cache.h"
+#include "checkout.h"
+#include "config.h"
#include "builtin.h"
#include "dir.h"
#include "parse-options.h"
@@ -12,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
};
@@ -31,13 +35,26 @@ struct add_opts {
static int show_only;
static int verbose;
-static unsigned long expire;
+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;
char *path;
- int fd, len;
+ int fd;
+ size_t len;
+ ssize_t read_result;
if (!is_directory(git_path("worktrees/%s", id))) {
strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
@@ -55,10 +72,26 @@ static int prune_worktree(const char *id, struct strbuf *reason)
id, strerror(errno));
return 1;
}
- len = st.st_size;
+ len = xsize_t(st.st_size);
path = xmallocz(len);
- read_in_full(fd, path, len);
+
+ read_result = read_in_full(fd, path, len);
+ if (read_result < 0) {
+ strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
+ id, strerror(errno));
+ close(fd);
+ free(path);
+ return 1;
+ }
close(fd);
+
+ if (read_result != len) {
+ strbuf_addf(reason,
+ _("Removing worktrees/%s: short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
+ id, (uintmax_t)len, (uintmax_t)read_result);
+ free(path);
+ return 1;
+ }
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
len--;
if (!len) {
@@ -131,7 +164,7 @@ static int prune(int ac, const char **av, const char *prefix)
OPT_END()
};
- expire = ULONG_MAX;
+ expire = TIME_MAX;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (ac)
usage_with_options(worktree_usage, options);
@@ -199,20 +232,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);
@@ -277,7 +311,7 @@ static int add_worktree(const char *path, const char *refname,
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
@@ -299,10 +333,8 @@ static int add_worktree(const char *path, const char *refname,
}
is_junk = 0;
- free(junk_work_tree);
- free(junk_git_dir);
- junk_work_tree = NULL;
- junk_git_dir = NULL;
+ FREE_AND_NULL(junk_work_tree);
+ FREE_AND_NULL(junk_git_dir);
done:
if (ret || !opts->keep_locked) {
@@ -310,6 +342,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);
@@ -324,8 +379,11 @@ static int add(int ac, const char **av, const char *prefix)
const char *new_branch_force = NULL;
char *path;
const char *branch;
+ const char *opt_track = NULL;
struct option options[] = {
- OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
+ OPT__FORCE(&opts.force,
+ N_("checkout <branch> even if already checked out in other worktree"),
+ PARSE_OPT_NOCOMPLETE),
OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
@@ -333,6 +391,11 @@ static int add(int ac, const char **av, const char *prefix)
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()
};
@@ -367,6 +430,28 @@ static int add(int ac, const char **av, const char *prefix)
int n;
const char *s = worktree_basename(path, &n);
opts.new_branch = xstrndup(s, n);
+ if (guess_remote) {
+ struct object_id oid;
+ const char *remote =
+ unique_tracking_name(opts.new_branch, &oid);
+ if (remote)
+ branch = remote;
+ }
+ }
+
+ if (ac == 2 && !opts.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) {
+ opts.new_branch = branch;
+ branch = remote;
+ }
+ }
}
if (opts.new_branch) {
@@ -377,11 +462,17 @@ static int add(int ac, const char **av, const char *prefix)
argv_array_push(&cp.args, "--force");
argv_array_push(&cp.args, opts.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;
+ } else if (opt_track) {
+ die(_("--[no-]track can only be used if a new branch is created"));
}
+ UNLEAK(path);
+ UNLEAK(opts);
return add_worktree(path, branch, &opts);
}
@@ -391,7 +482,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)
@@ -411,7 +502,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.hash, DEFAULT_ABBREV));
if (wt->is_detached)
strbuf_addstr(&sb, "(detached HEAD)");
else if (wt->head_ref) {
@@ -436,7 +527,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.hash, *abbrev));
if (sha1_len > *abbrev)
*abbrev = sha1_len;
}
@@ -532,13 +623,227 @@ 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_BOOL(0, "force", &force,
+ N_("force removing even if the worktree is dirty")),
+ 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);
@@ -554,5 +859,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 084c0df783..bd0a78aa3c 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -5,6 +5,7 @@
*/
#include "builtin.h"
#include "cache.h"
+#include "config.h"
#include "tree.h"
#include "cache-tree.h"
#include "parse-options.h"