summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c41
-rw-r--r--builtin/am.c2415
-rw-r--r--builtin/apply.c1510
-rw-r--r--builtin/blame.c261
-rw-r--r--builtin/branch.c632
-rw-r--r--builtin/cat-file.c243
-rw-r--r--builtin/check-attr.c24
-rw-r--r--builtin/check-ignore.c22
-rw-r--r--builtin/check-mailmap.c2
-rw-r--r--builtin/check-ref-format.c2
-rw-r--r--builtin/checkout-index.c105
-rw-r--r--builtin/checkout.c139
-rw-r--r--builtin/clean.c18
-rw-r--r--builtin/clone.c248
-rw-r--r--builtin/column.c2
-rw-r--r--builtin/commit-tree.c10
-rw-r--r--builtin/commit.c130
-rw-r--r--builtin/config.c194
-rw-r--r--builtin/count-objects.c30
-rw-r--r--builtin/describe.c40
-rw-r--r--builtin/diff-files.c1
-rw-r--r--builtin/diff-index.c1
-rw-r--r--builtin/diff-tree.c14
-rw-r--r--builtin/diff.c16
-rw-r--r--builtin/fast-export.c49
-rw-r--r--builtin/fetch-pack.c41
-rw-r--r--builtin/fetch.c368
-rw-r--r--builtin/fmt-merge-msg.c16
-rw-r--r--builtin/for-each-ref.c1109
-rw-r--r--builtin/fsck.c361
-rw-r--r--builtin/gc.c151
-rw-r--r--builtin/get-tar-commit-id.c2
-rw-r--r--builtin/grep.c107
-rw-r--r--builtin/hash-object.c15
-rw-r--r--builtin/help.c53
-rw-r--r--builtin/index-pack.c414
-rw-r--r--builtin/init-db.c266
-rw-r--r--builtin/interpret-trailers.c15
-rw-r--r--builtin/log.c386
-rw-r--r--builtin/ls-files.c40
-rw-r--r--builtin/ls-remote.c94
-rw-r--r--builtin/ls-tree.c9
-rw-r--r--builtin/mailinfo.c1055
-rw-r--r--builtin/mailsplit.c67
-rw-r--r--builtin/merge-base.c10
-rw-r--r--builtin/merge-file.c13
-rw-r--r--builtin/merge-index.c6
-rw-r--r--builtin/merge-recursive.c22
-rw-r--r--builtin/merge-tree.c26
-rw-r--r--builtin/merge.c484
-rw-r--r--builtin/mktag.c2
-rw-r--r--builtin/mktree.c23
-rw-r--r--builtin/mv.c39
-rw-r--r--builtin/name-rev.c57
-rw-r--r--builtin/notes.c116
-rw-r--r--builtin/pack-objects.c173
-rw-r--r--builtin/pack-redundant.c2
-rw-r--r--builtin/patch-id.c59
-rw-r--r--builtin/prune.c5
-rw-r--r--builtin/pull.c931
-rw-r--r--builtin/push.c140
-rw-r--r--builtin/read-tree.c4
-rw-r--r--builtin/receive-pack.c371
-rw-r--r--builtin/reflog.c71
-rw-r--r--builtin/remote-ext.c62
-rw-r--r--builtin/remote.c252
-rw-r--r--builtin/repack.c33
-rw-r--r--builtin/replace.c29
-rw-r--r--builtin/rerere.c24
-rw-r--r--builtin/reset.c40
-rw-r--r--builtin/rev-list.c46
-rw-r--r--builtin/rev-parse.c138
-rw-r--r--builtin/revert.c13
-rw-r--r--builtin/rm.c11
-rw-r--r--builtin/send-pack.c195
-rw-r--r--builtin/shortlog.c203
-rw-r--r--builtin/show-branch.c133
-rw-r--r--builtin/show-ref.c42
-rw-r--r--builtin/stripspace.c124
-rw-r--r--builtin/submodule--helper.c986
-rw-r--r--builtin/symbolic-ref.c4
-rw-r--r--builtin/tag.c426
-rw-r--r--builtin/unpack-file.c2
-rw-r--r--builtin/unpack-objects.c32
-rw-r--r--builtin/update-index.c259
-rw-r--r--builtin/update-ref.c35
-rw-r--r--builtin/upload-archive.c12
-rw-r--r--builtin/verify-commit.c25
-rw-r--r--builtin/verify-tag.c57
-rw-r--r--builtin/worktree.c546
90 files changed, 10513 insertions, 6488 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 4bd98b7..b1dddb4 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -26,7 +26,7 @@ static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
struct update_callback_data {
- int flags;
+ int flags, force_mode;
int add_errors;
};
@@ -65,7 +65,8 @@ static void update_callback(struct diff_queue_struct *q,
die(_("unexpected diff status %c"), p->status);
case DIFF_STATUS_MODIFIED:
case DIFF_STATUS_TYPE_CHANGED:
- if (add_file_to_index(&the_index, path, data->flags)) {
+ if (add_file_to_index(&the_index, path,
+ data->flags, data->force_mode)) {
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
die(_("updating files failed"));
data->add_errors++;
@@ -83,14 +84,15 @@ static void update_callback(struct diff_queue_struct *q,
}
}
-int add_files_to_cache(const char *prefix,
- const struct pathspec *pathspec, int flags)
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
+ int flags, int force_mode)
{
struct update_callback_data data;
struct rev_info rev;
memset(&data, 0, sizeof(data));
data.flags = flags;
+ data.force_mode = force_mode;
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
@@ -238,6 +240,8 @@ static int ignore_add_errors, intent_to_add, ignore_missing;
static int addremove = ADDREMOVE_DEFAULT;
static int addremove_explicit = -1; /* unspecified */
+static char *chmod_arg;
+
static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
{
/* if we are told to ignore, we are not adding removals */
@@ -263,6 +267,7 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
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_END(),
};
@@ -276,7 +281,7 @@ static int add_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-static int add_files(struct dir_struct *dir, int flags)
+static int add_files(struct dir_struct *dir, int flags, int force_mode)
{
int i, exit_status = 0;
@@ -289,7 +294,8 @@ static int add_files(struct dir_struct *dir, int flags)
}
for (i = 0; i < dir->nr; i++)
- if (add_file_to_cache(dir->entries[i]->name, flags)) {
+ if (add_file_to_index(&the_index, dir->entries[i]->name,
+ flags, force_mode)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
@@ -302,7 +308,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int exit_status = 0;
struct pathspec pathspec;
struct dir_struct dir;
- int flags;
+ int flags, force_mode;
int add_new_files;
int require_pathspec;
char *seen = NULL;
@@ -336,14 +342,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (!show_only && ignore_missing)
die(_("Option --ignore-missing can only be used together with --dry-run"));
- if ((0 < addremove_explicit || take_worktree_changes) && !argc) {
- static const char *whole[2] = { ":/", NULL };
- argc = 1;
- argv = whole;
- }
+ if (!chmod_arg)
+ force_mode = 0;
+ else if (!strcmp(chmod_arg, "-x"))
+ force_mode = 0666;
+ else if (!strcmp(chmod_arg, "+x"))
+ force_mode = 0777;
+ else
+ die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
add_new_files = !take_worktree_changes && !refresh_only;
- require_pathspec = !take_worktree_changes;
+ require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
hold_locked_index(&lock_file, 1);
@@ -375,7 +384,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (add_new_files) {
int baselen;
- struct pathspec empty_pathspec;
/* Set up the default git porcelain excludes */
memset(&dir, 0, sizeof(dir));
@@ -384,7 +392,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
setup_standard_excludes(&dir);
}
- memset(&empty_pathspec, 0, sizeof(empty_pathspec));
/* This picks up the paths that are not tracked */
baselen = fill_directory(&dir, &pathspec);
if (pathspec.nr)
@@ -434,10 +441,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
plug_bulk_checkin();
- exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+ exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
if (add_new_files)
- exit_status |= add_files(&dir, flags);
+ exit_status |= add_files(&dir, flags, force_mode);
unplug_bulk_checkin();
diff --git a/builtin/am.c b/builtin/am.c
new file mode 100644
index 0000000..9e2ae5c
--- /dev/null
+++ b/builtin/am.c
@@ -0,0 +1,2415 @@
+/*
+ * Builtin "git am"
+ *
+ * Based on git-am.sh by Junio C Hamano.
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "parse-options.h"
+#include "dir.h"
+#include "run-command.h"
+#include "quote.h"
+#include "tempfile.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "refs.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "unpack-trees.h"
+#include "branch.h"
+#include "sequencer.h"
+#include "revision.h"
+#include "merge-recursive.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "notes-utils.h"
+#include "rerere.h"
+#include "prompt.h"
+#include "mailinfo.h"
+
+/**
+ * Returns 1 if the file is empty or does not exist, 0 otherwise.
+ */
+static int is_empty_file(const char *filename)
+{
+ struct stat st;
+
+ if (stat(filename, &st) < 0) {
+ if (errno == ENOENT)
+ return 1;
+ die_errno(_("could not stat %s"), filename);
+ }
+
+ return !st.st_size;
+}
+
+/**
+ * Returns the length of the first line of msg.
+ */
+static int linelen(const char *msg)
+{
+ return strchrnul(msg, '\n') - msg;
+}
+
+/**
+ * Returns true if `str` consists of only whitespace, false otherwise.
+ */
+static int str_isspace(const char *str)
+{
+ for (; *str; str++)
+ if (!isspace(*str))
+ return 0;
+
+ return 1;
+}
+
+enum patch_format {
+ PATCH_FORMAT_UNKNOWN = 0,
+ PATCH_FORMAT_MBOX,
+ PATCH_FORMAT_STGIT,
+ PATCH_FORMAT_STGIT_SERIES,
+ PATCH_FORMAT_HG,
+ PATCH_FORMAT_MBOXRD
+};
+
+enum keep_type {
+ KEEP_FALSE = 0,
+ KEEP_TRUE, /* pass -k flag to git-mailinfo */
+ KEEP_NON_PATCH /* pass -b flag to git-mailinfo */
+};
+
+enum scissors_type {
+ SCISSORS_UNSET = -1,
+ SCISSORS_FALSE = 0, /* pass --no-scissors to git-mailinfo */
+ SCISSORS_TRUE /* pass --scissors to git-mailinfo */
+};
+
+enum signoff_type {
+ SIGNOFF_FALSE = 0,
+ SIGNOFF_TRUE = 1,
+ SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
+};
+
+struct am_state {
+ /* state directory path */
+ char *dir;
+
+ /* current and last patch numbers, 1-indexed */
+ int cur;
+ int last;
+
+ /* commit metadata and message */
+ char *author_name;
+ char *author_email;
+ char *author_date;
+ char *msg;
+ size_t msg_len;
+
+ /* when --rebasing, records the original commit the patch came from */
+ unsigned char orig_commit[GIT_SHA1_RAWSZ];
+
+ /* number of digits in patch filename */
+ int prec;
+
+ /* various operating modes and command line options */
+ int interactive;
+ int threeway;
+ int quiet;
+ int signoff; /* enum signoff_type */
+ int utf8;
+ int keep; /* enum keep_type */
+ int message_id;
+ int scissors; /* enum scissors_type */
+ struct argv_array git_apply_opts;
+ const char *resolvemsg;
+ int committer_date_is_author_date;
+ int ignore_date;
+ int allow_rerere_autoupdate;
+ const char *sign_commit;
+ int rebasing;
+};
+
+/**
+ * Initializes am_state with the default values. The state directory is set to
+ * dir.
+ */
+static void am_state_init(struct am_state *state, const char *dir)
+{
+ int gpgsign;
+
+ memset(state, 0, sizeof(*state));
+
+ assert(dir);
+ state->dir = xstrdup(dir);
+
+ state->prec = 4;
+
+ git_config_get_bool("am.threeway", &state->threeway);
+
+ state->utf8 = 1;
+
+ git_config_get_bool("am.messageid", &state->message_id);
+
+ state->scissors = SCISSORS_UNSET;
+
+ argv_array_init(&state->git_apply_opts);
+
+ if (!git_config_get_bool("commit.gpgsign", &gpgsign))
+ state->sign_commit = gpgsign ? "" : NULL;
+}
+
+/**
+ * Releases memory allocated by an am_state.
+ */
+static void am_state_release(struct am_state *state)
+{
+ free(state->dir);
+ free(state->author_name);
+ free(state->author_email);
+ free(state->author_date);
+ free(state->msg);
+ argv_array_clear(&state->git_apply_opts);
+}
+
+/**
+ * Returns path relative to the am_state directory.
+ */
+static inline const char *am_path(const struct am_state *state, const char *path)
+{
+ return mkpath("%s/%s", state->dir, path);
+}
+
+/**
+ * For convenience to call write_file()
+ */
+static void write_state_text(const struct am_state *state,
+ const char *name, const char *string)
+{
+ write_file(am_path(state, name), "%s", string);
+}
+
+static void write_state_count(const struct am_state *state,
+ const char *name, int value)
+{
+ write_file(am_path(state, name), "%d", value);
+}
+
+static void write_state_bool(const struct am_state *state,
+ const char *name, int value)
+{
+ write_state_text(state, name, value ? "t" : "f");
+}
+
+/**
+ * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
+ * at the end.
+ */
+static void say(const struct am_state *state, FILE *fp, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (!state->quiet) {
+ vfprintf(fp, fmt, ap);
+ putc('\n', fp);
+ }
+ va_end(ap);
+}
+
+/**
+ * Returns 1 if there is an am session in progress, 0 otherwise.
+ */
+static int am_in_progress(const struct am_state *state)
+{
+ struct stat st;
+
+ if (lstat(state->dir, &st) < 0 || !S_ISDIR(st.st_mode))
+ return 0;
+ if (lstat(am_path(state, "last"), &st) || !S_ISREG(st.st_mode))
+ return 0;
+ if (lstat(am_path(state, "next"), &st) || !S_ISREG(st.st_mode))
+ return 0;
+ return 1;
+}
+
+/**
+ * Reads the contents of `file` in the `state` directory into `sb`. Returns the
+ * number of bytes read on success, -1 if the file does not exist. If `trim` is
+ * set, trailing whitespace will be removed.
+ */
+static int read_state_file(struct strbuf *sb, const struct am_state *state,
+ const char *file, int trim)
+{
+ strbuf_reset(sb);
+
+ if (strbuf_read_file(sb, am_path(state, file), 0) >= 0) {
+ if (trim)
+ strbuf_trim(sb);
+
+ return sb->len;
+ }
+
+ if (errno == ENOENT)
+ return -1;
+
+ die_errno(_("could not read '%s'"), am_path(state, file));
+}
+
+/**
+ * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE
+ * as a newly-allocated string. VALUE must be a quoted string, and the KEY must
+ * match `key`. Returns NULL on failure.
+ *
+ * This is used by read_author_script() to read the GIT_AUTHOR_* variables from
+ * the author-script.
+ */
+static char *read_shell_var(FILE *fp, const char *key)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *str;
+
+ if (strbuf_getline_lf(&sb, fp))
+ goto fail;
+
+ if (!skip_prefix(sb.buf, key, &str))
+ goto fail;
+
+ if (!skip_prefix(str, "=", &str))
+ goto fail;
+
+ strbuf_remove(&sb, 0, str - sb.buf);
+
+ str = sq_dequote(sb.buf);
+ if (!str)
+ goto fail;
+
+ return strbuf_detach(&sb, NULL);
+
+fail:
+ strbuf_release(&sb);
+ return NULL;
+}
+
+/**
+ * Reads and parses the state directory's "author-script" file, and sets
+ * state->author_name, state->author_email and state->author_date accordingly.
+ * Returns 0 on success, -1 if the file could not be parsed.
+ *
+ * The author script is of the format:
+ *
+ * GIT_AUTHOR_NAME='$author_name'
+ * GIT_AUTHOR_EMAIL='$author_email'
+ * GIT_AUTHOR_DATE='$author_date'
+ *
+ * where $author_name, $author_email and $author_date are quoted. We are strict
+ * with our parsing, as the file was meant to be eval'd in the old git-am.sh
+ * script, and thus if the file differs from what this function expects, it is
+ * better to bail out than to do something that the user does not expect.
+ */
+static int read_author_script(struct am_state *state)
+{
+ const char *filename = am_path(state, "author-script");
+ FILE *fp;
+
+ assert(!state->author_name);
+ assert(!state->author_email);
+ assert(!state->author_date);
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ if (errno == ENOENT)
+ return 0;
+ die_errno(_("could not open '%s' for reading"), filename);
+ }
+
+ state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME");
+ if (!state->author_name) {
+ fclose(fp);
+ return -1;
+ }
+
+ state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL");
+ if (!state->author_email) {
+ fclose(fp);
+ return -1;
+ }
+
+ state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE");
+ if (!state->author_date) {
+ fclose(fp);
+ return -1;
+ }
+
+ if (fgetc(fp) != EOF) {
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+/**
+ * Saves state->author_name, state->author_email and state->author_date in the
+ * state directory's "author-script" file.
+ */
+static void write_author_script(const struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_addstr(&sb, "GIT_AUTHOR_NAME=");
+ sq_quote_buf(&sb, state->author_name);
+ strbuf_addch(&sb, '\n');
+
+ strbuf_addstr(&sb, "GIT_AUTHOR_EMAIL=");
+ sq_quote_buf(&sb, state->author_email);
+ strbuf_addch(&sb, '\n');
+
+ strbuf_addstr(&sb, "GIT_AUTHOR_DATE=");
+ sq_quote_buf(&sb, state->author_date);
+ strbuf_addch(&sb, '\n');
+
+ write_state_text(state, "author-script", sb.buf);
+
+ strbuf_release(&sb);
+}
+
+/**
+ * Reads the commit message from the state directory's "final-commit" file,
+ * setting state->msg to its contents and state->msg_len to the length of its
+ * contents in bytes.
+ *
+ * Returns 0 on success, -1 if the file does not exist.
+ */
+static int read_commit_msg(struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ assert(!state->msg);
+
+ if (read_state_file(&sb, state, "final-commit", 0) < 0) {
+ strbuf_release(&sb);
+ return -1;
+ }
+
+ state->msg = strbuf_detach(&sb, &state->msg_len);
+ return 0;
+}
+
+/**
+ * Saves state->msg in the state directory's "final-commit" file.
+ */
+static void write_commit_msg(const struct am_state *state)
+{
+ const char *filename = am_path(state, "final-commit");
+ write_file_buf(filename, state->msg, state->msg_len);
+}
+
+/**
+ * Loads state from disk.
+ */
+static void am_load(struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ if (read_state_file(&sb, state, "next", 1) < 0)
+ die("BUG: state file 'next' does not exist");
+ state->cur = strtol(sb.buf, NULL, 10);
+
+ if (read_state_file(&sb, state, "last", 1) < 0)
+ die("BUG: state file 'last' does not exist");
+ state->last = strtol(sb.buf, NULL, 10);
+
+ if (read_author_script(state) < 0)
+ die(_("could not parse author script"));
+
+ read_commit_msg(state);
+
+ if (read_state_file(&sb, state, "original-commit", 1) < 0)
+ hashclr(state->orig_commit);
+ else if (get_sha1_hex(sb.buf, state->orig_commit) < 0)
+ die(_("could not parse %s"), am_path(state, "original-commit"));
+
+ read_state_file(&sb, state, "threeway", 1);
+ state->threeway = !strcmp(sb.buf, "t");
+
+ read_state_file(&sb, state, "quiet", 1);
+ state->quiet = !strcmp(sb.buf, "t");
+
+ read_state_file(&sb, state, "sign", 1);
+ state->signoff = !strcmp(sb.buf, "t");
+
+ read_state_file(&sb, state, "utf8", 1);
+ state->utf8 = !strcmp(sb.buf, "t");
+
+ read_state_file(&sb, state, "keep", 1);
+ if (!strcmp(sb.buf, "t"))
+ state->keep = KEEP_TRUE;
+ else if (!strcmp(sb.buf, "b"))
+ state->keep = KEEP_NON_PATCH;
+ else
+ state->keep = KEEP_FALSE;
+
+ read_state_file(&sb, state, "messageid", 1);
+ state->message_id = !strcmp(sb.buf, "t");
+
+ read_state_file(&sb, state, "scissors", 1);
+ if (!strcmp(sb.buf, "t"))
+ state->scissors = SCISSORS_TRUE;
+ else if (!strcmp(sb.buf, "f"))
+ state->scissors = SCISSORS_FALSE;
+ else
+ state->scissors = SCISSORS_UNSET;
+
+ read_state_file(&sb, state, "apply-opt", 1);
+ argv_array_clear(&state->git_apply_opts);
+ if (sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) < 0)
+ die(_("could not parse %s"), am_path(state, "apply-opt"));
+
+ state->rebasing = !!file_exists(am_path(state, "rebasing"));
+
+ strbuf_release(&sb);
+}
+
+/**
+ * Removes the am_state directory, forcefully terminating the current am
+ * session.
+ */
+static void am_destroy(const struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_addstr(&sb, state->dir);
+ remove_dir_recursively(&sb, 0);
+ strbuf_release(&sb);
+}
+
+/**
+ * Runs applypatch-msg hook. Returns its exit code.
+ */
+static int run_applypatch_msg_hook(struct am_state *state)
+{
+ int ret;
+
+ assert(state->msg);
+ ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
+
+ if (!ret) {
+ free(state->msg);
+ state->msg = NULL;
+ if (read_commit_msg(state) < 0)
+ die(_("'%s' was deleted by the applypatch-msg hook"),
+ am_path(state, "final-commit"));
+ }
+
+ return ret;
+}
+
+/**
+ * Runs post-rewrite hook. Returns it exit code.
+ */
+static int run_post_rewrite_hook(const struct am_state *state)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ const char *hook = find_hook("post-rewrite");
+ int ret;
+
+ if (!hook)
+ return 0;
+
+ argv_array_push(&cp.args, hook);
+ argv_array_push(&cp.args, "rebase");
+
+ cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
+ cp.stdout_to_stderr = 1;
+
+ ret = run_command(&cp);
+
+ close(cp.in);
+ return ret;
+}
+
+/**
+ * Reads the state directory's "rewritten" file, and copies notes from the old
+ * commits listed in the file to their rewritten commits.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int copy_notes_for_rebase(const struct am_state *state)
+{
+ struct notes_rewrite_cfg *c;
+ struct strbuf sb = STRBUF_INIT;
+ const char *invalid_line = _("Malformed input line: '%s'.");
+ const char *msg = "Notes added by 'git rebase'";
+ FILE *fp;
+ int ret = 0;
+
+ assert(state->rebasing);
+
+ c = init_copy_notes_for_rewrite("rebase");
+ if (!c)
+ return 0;
+
+ fp = xfopen(am_path(state, "rewritten"), "r");
+
+ while (!strbuf_getline_lf(&sb, fp)) {
+ unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ];
+
+ if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
+ ret = error(invalid_line, sb.buf);
+ goto finish;
+ }
+
+ if (get_sha1_hex(sb.buf, from_obj)) {
+ ret = error(invalid_line, sb.buf);
+ goto finish;
+ }
+
+ if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
+ ret = error(invalid_line, sb.buf);
+ goto finish;
+ }
+
+ if (get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ + 1, to_obj)) {
+ ret = error(invalid_line, sb.buf);
+ goto finish;
+ }
+
+ if (copy_note_for_rewrite(c, from_obj, to_obj))
+ ret = error(_("Failed to copy notes from '%s' to '%s'"),
+ sha1_to_hex(from_obj), sha1_to_hex(to_obj));
+ }
+
+finish:
+ finish_copy_notes_for_rewrite(c, msg);
+ fclose(fp);
+ strbuf_release(&sb);
+ return ret;
+}
+
+/**
+ * Determines if the file looks like a piece of RFC2822 mail by grabbing all
+ * non-indented lines and checking if they look like they begin with valid
+ * header field names.
+ *
+ * Returns 1 if the file looks like a piece of mail, 0 otherwise.
+ */
+static int is_mail(FILE *fp)
+{
+ const char *header_regex = "^[!-9;-~]+:";
+ struct strbuf sb = STRBUF_INIT;
+ regex_t regex;
+ int ret = 1;
+
+ if (fseek(fp, 0L, SEEK_SET))
+ die_errno(_("fseek failed"));
+
+ if (regcomp(&regex, header_regex, REG_NOSUB | REG_EXTENDED))
+ die("invalid pattern: %s", header_regex);
+
+ while (!strbuf_getline(&sb, fp)) {
+ if (!sb.len)
+ break; /* End of header */
+
+ /* Ignore indented folded lines */
+ if (*sb.buf == '\t' || *sb.buf == ' ')
+ continue;
+
+ /* It's a header if it matches header_regex */
+ if (regexec(&regex, sb.buf, 0, NULL, 0)) {
+ ret = 0;
+ goto done;
+ }
+ }
+
+done:
+ regfree(&regex);
+ strbuf_release(&sb);
+ return ret;
+}
+
+/**
+ * Attempts to detect the patch_format of the patches contained in `paths`,
+ * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if
+ * detection fails.
+ */
+static int detect_patch_format(const char **paths)
+{
+ enum patch_format ret = PATCH_FORMAT_UNKNOWN;
+ struct strbuf l1 = STRBUF_INIT;
+ struct strbuf l2 = STRBUF_INIT;
+ struct strbuf l3 = STRBUF_INIT;
+ FILE *fp;
+
+ /*
+ * We default to mbox format if input is from stdin and for directories
+ */
+ if (!*paths || !strcmp(*paths, "-") || is_directory(*paths))
+ return PATCH_FORMAT_MBOX;
+
+ /*
+ * Otherwise, check the first few lines of the first patch, starting
+ * from the first non-blank line, to try to detect its format.
+ */
+
+ fp = xfopen(*paths, "r");
+
+ while (!strbuf_getline(&l1, fp)) {
+ if (l1.len)
+ break;
+ }
+
+ if (starts_with(l1.buf, "From ") || starts_with(l1.buf, "From: ")) {
+ ret = PATCH_FORMAT_MBOX;
+ goto done;
+ }
+
+ if (starts_with(l1.buf, "# This series applies on GIT commit")) {
+ ret = PATCH_FORMAT_STGIT_SERIES;
+ goto done;
+ }
+
+ if (!strcmp(l1.buf, "# HG changeset patch")) {
+ ret = PATCH_FORMAT_HG;
+ goto done;
+ }
+
+ strbuf_reset(&l2);
+ strbuf_getline(&l2, fp);
+ strbuf_reset(&l3);
+ strbuf_getline(&l3, fp);
+
+ /*
+ * If the second line is empty and the third is a From, Author or Date
+ * entry, this is likely an StGit patch.
+ */
+ if (l1.len && !l2.len &&
+ (starts_with(l3.buf, "From:") ||
+ starts_with(l3.buf, "Author:") ||
+ starts_with(l3.buf, "Date:"))) {
+ ret = PATCH_FORMAT_STGIT;
+ goto done;
+ }
+
+ if (l1.len && is_mail(fp)) {
+ ret = PATCH_FORMAT_MBOX;
+ goto done;
+ }
+
+done:
+ fclose(fp);
+ strbuf_release(&l1);
+ return ret;
+}
+
+/**
+ * Splits out individual email patches from `paths`, where each path is either
+ * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
+ */
+static int split_mail_mbox(struct am_state *state, const char **paths,
+ int keep_cr, int mboxrd)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf last = STRBUF_INIT;
+
+ cp.git_cmd = 1;
+ argv_array_push(&cp.args, "mailsplit");
+ argv_array_pushf(&cp.args, "-d%d", state->prec);
+ argv_array_pushf(&cp.args, "-o%s", state->dir);
+ argv_array_push(&cp.args, "-b");
+ if (keep_cr)
+ argv_array_push(&cp.args, "--keep-cr");
+ if (mboxrd)
+ argv_array_push(&cp.args, "--mboxrd");
+ argv_array_push(&cp.args, "--");
+ argv_array_pushv(&cp.args, paths);
+
+ if (capture_command(&cp, &last, 8))
+ return -1;
+
+ state->cur = 1;
+ state->last = strtol(last.buf, NULL, 10);
+
+ return 0;
+}
+
+/**
+ * Callback signature for split_mail_conv(). The foreign patch should be
+ * read from `in`, and the converted patch (in RFC2822 mail format) should be
+ * written to `out`. Return 0 on success, or -1 on failure.
+ */
+typedef int (*mail_conv_fn)(FILE *out, FILE *in, int keep_cr);
+
+/**
+ * Calls `fn` for each file in `paths` to convert the foreign patch to the
+ * RFC2822 mail format suitable for parsing with git-mailinfo.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
+ const char **paths, int keep_cr)
+{
+ static const char *stdin_only[] = {"-", NULL};
+ int i;
+
+ if (!*paths)
+ paths = stdin_only;
+
+ for (i = 0; *paths; paths++, i++) {
+ FILE *in, *out;
+ const char *mail;
+ int ret;
+
+ if (!strcmp(*paths, "-"))
+ in = stdin;
+ else
+ in = fopen(*paths, "r");
+
+ if (!in)
+ return error_errno(_("could not open '%s' for reading"),
+ *paths);
+
+ mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
+
+ out = fopen(mail, "w");
+ if (!out)
+ return error_errno(_("could not open '%s' for writing"),
+ mail);
+
+ ret = fn(out, in, keep_cr);
+
+ fclose(out);
+ fclose(in);
+
+ if (ret)
+ return error(_("could not parse patch '%s'"), *paths);
+ }
+
+ state->cur = 1;
+ state->last = i;
+ return 0;
+}
+
+/**
+ * A split_mail_conv() callback that converts an StGit patch to an RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int subject_printed = 0;
+
+ while (!strbuf_getline_lf(&sb, in)) {
+ const char *str;
+
+ if (str_isspace(sb.buf))
+ continue;
+ else if (skip_prefix(sb.buf, "Author:", &str))
+ fprintf(out, "From:%s\n", str);
+ else if (starts_with(sb.buf, "From") || starts_with(sb.buf, "Date"))
+ fprintf(out, "%s\n", sb.buf);
+ else if (!subject_printed) {
+ fprintf(out, "Subject: %s\n", sb.buf);
+ subject_printed = 1;
+ } else {
+ fprintf(out, "\n%s\n", sb.buf);
+ break;
+ }
+ }
+
+ strbuf_reset(&sb);
+ while (strbuf_fread(&sb, 8192, in) > 0) {
+ fwrite(sb.buf, 1, sb.len, out);
+ strbuf_reset(&sb);
+ }
+
+ strbuf_release(&sb);
+ return 0;
+}
+
+/**
+ * This function only supports a single StGit series file in `paths`.
+ *
+ * Given an StGit series file, converts the StGit patches in the series into
+ * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in
+ * the state directory.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_stgit_series(struct am_state *state, const char **paths,
+ int keep_cr)
+{
+ const char *series_dir;
+ char *series_dir_buf;
+ FILE *fp;
+ struct argv_array patches = ARGV_ARRAY_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ int ret;
+
+ if (!paths[0] || paths[1])
+ return error(_("Only one StGIT patch series can be applied at once"));
+
+ series_dir_buf = xstrdup(*paths);
+ series_dir = dirname(series_dir_buf);
+
+ fp = fopen(*paths, "r");
+ if (!fp)
+ return error_errno(_("could not open '%s' for reading"), *paths);
+
+ while (!strbuf_getline_lf(&sb, fp)) {
+ if (*sb.buf == '#')
+ continue; /* skip comment lines */
+
+ argv_array_push(&patches, mkpath("%s/%s", series_dir, sb.buf));
+ }
+
+ fclose(fp);
+ strbuf_release(&sb);
+ free(series_dir_buf);
+
+ ret = split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr);
+
+ argv_array_clear(&patches);
+ return ret;
+}
+
+/**
+ * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ while (!strbuf_getline_lf(&sb, in)) {
+ const char *str;
+
+ if (skip_prefix(sb.buf, "# User ", &str))
+ fprintf(out, "From: %s\n", str);
+ else if (skip_prefix(sb.buf, "# Date ", &str)) {
+ unsigned long timestamp;
+ long tz, tz2;
+ char *end;
+
+ errno = 0;
+ timestamp = strtoul(str, &end, 10);
+ if (errno)
+ return error(_("invalid timestamp"));
+
+ if (!skip_prefix(end, " ", &str))
+ return error(_("invalid Date line"));
+
+ errno = 0;
+ tz = strtol(str, &end, 10);
+ if (errno)
+ return error(_("invalid timezone offset"));
+
+ if (*end)
+ return error(_("invalid Date line"));
+
+ /*
+ * mercurial's timezone is in seconds west of UTC,
+ * however git's timezone is in hours + minutes east of
+ * UTC. Convert it.
+ */
+ tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60;
+ if (tz > 0)
+ tz2 = -tz2;
+
+ fprintf(out, "Date: %s\n", show_date(timestamp, tz2, DATE_MODE(RFC2822)));
+ } else if (starts_with(sb.buf, "# ")) {
+ continue;
+ } else {
+ fprintf(out, "\n%s\n", sb.buf);
+ break;
+ }
+ }
+
+ strbuf_reset(&sb);
+ while (strbuf_fread(&sb, 8192, in) > 0) {
+ fwrite(sb.buf, 1, sb.len, out);
+ strbuf_reset(&sb);
+ }
+
+ strbuf_release(&sb);
+ return 0;
+}
+
+/**
+ * Splits a list of files/directories into individual email patches. Each path
+ * in `paths` must be a file/directory that is formatted according to
+ * `patch_format`.
+ *
+ * Once split out, the individual email patches will be stored in the state
+ * directory, with each patch's filename being its index, padded to state->prec
+ * digits.
+ *
+ * state->cur will be set to the index of the first mail, and state->last will
+ * be set to the index of the last mail.
+ *
+ * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1
+ * to disable this behavior, -1 to use the default configured setting.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail(struct am_state *state, enum patch_format patch_format,
+ const char **paths, int keep_cr)
+{
+ if (keep_cr < 0) {
+ keep_cr = 0;
+ git_config_get_bool("am.keepcr", &keep_cr);
+ }
+
+ switch (patch_format) {
+ case PATCH_FORMAT_MBOX:
+ return split_mail_mbox(state, paths, keep_cr, 0);
+ case PATCH_FORMAT_STGIT:
+ return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
+ case PATCH_FORMAT_STGIT_SERIES:
+ return split_mail_stgit_series(state, paths, keep_cr);
+ case PATCH_FORMAT_HG:
+ return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
+ case PATCH_FORMAT_MBOXRD:
+ return split_mail_mbox(state, paths, keep_cr, 1);
+ default:
+ die("BUG: invalid patch_format");
+ }
+ return -1;
+}
+
+/**
+ * Setup a new am session for applying patches
+ */
+static void am_setup(struct am_state *state, enum patch_format patch_format,
+ const char **paths, int keep_cr)
+{
+ unsigned char curr_head[GIT_SHA1_RAWSZ];
+ const char *str;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (!patch_format)
+ patch_format = detect_patch_format(paths);
+
+ if (!patch_format) {
+ fprintf_ln(stderr, _("Patch format detection failed."));
+ exit(128);
+ }
+
+ if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
+ die_errno(_("failed to create directory '%s'"), state->dir);
+
+ if (split_mail(state, patch_format, paths, keep_cr) < 0) {
+ am_destroy(state);
+ die(_("Failed to split patches."));
+ }
+
+ if (state->rebasing)
+ state->threeway = 1;
+
+ write_state_bool(state, "threeway", state->threeway);
+ write_state_bool(state, "quiet", state->quiet);
+ write_state_bool(state, "sign", state->signoff);
+ write_state_bool(state, "utf8", state->utf8);
+
+ switch (state->keep) {
+ case KEEP_FALSE:
+ str = "f";
+ break;
+ case KEEP_TRUE:
+ str = "t";
+ break;
+ case KEEP_NON_PATCH:
+ str = "b";
+ break;
+ default:
+ die("BUG: invalid value for state->keep");
+ }
+
+ write_state_text(state, "keep", str);
+ write_state_bool(state, "messageid", state->message_id);
+
+ switch (state->scissors) {
+ case SCISSORS_UNSET:
+ str = "";
+ break;
+ case SCISSORS_FALSE:
+ str = "f";
+ break;
+ case SCISSORS_TRUE:
+ str = "t";
+ break;
+ default:
+ die("BUG: invalid value for state->scissors");
+ }
+ write_state_text(state, "scissors", str);
+
+ sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
+ write_state_text(state, "apply-opt", sb.buf);
+
+ if (state->rebasing)
+ write_state_text(state, "rebasing", "");
+ else
+ write_state_text(state, "applying", "");
+
+ if (!get_sha1("HEAD", curr_head)) {
+ write_state_text(state, "abort-safety", sha1_to_hex(curr_head));
+ if (!state->rebasing)
+ update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
+ UPDATE_REFS_DIE_ON_ERR);
+ } else {
+ write_state_text(state, "abort-safety", "");
+ if (!state->rebasing)
+ delete_ref("ORIG_HEAD", NULL, 0);
+ }
+
+ /*
+ * NOTE: Since the "next" and "last" files determine if an am_state
+ * session is in progress, they should be written last.
+ */
+
+ write_state_count(state, "next", state->cur);
+ write_state_count(state, "last", state->last);
+
+ strbuf_release(&sb);
+}
+
+/**
+ * Increments the patch pointer, and cleans am_state for the application of the
+ * next patch.
+ */
+static void am_next(struct am_state *state)
+{
+ unsigned char head[GIT_SHA1_RAWSZ];
+
+ 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;
+ state->msg_len = 0;
+
+ unlink(am_path(state, "author-script"));
+ unlink(am_path(state, "final-commit"));
+
+ hashclr(state->orig_commit);
+ unlink(am_path(state, "original-commit"));
+
+ if (!get_sha1("HEAD", head))
+ write_state_text(state, "abort-safety", sha1_to_hex(head));
+ else
+ write_state_text(state, "abort-safety", "");
+
+ state->cur++;
+ write_state_count(state, "next", state->cur);
+}
+
+/**
+ * Returns the filename of the current patch email.
+ */
+static const char *msgnum(const struct am_state *state)
+{
+ static struct strbuf sb = STRBUF_INIT;
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%0*d", state->prec, state->cur);
+
+ return sb.buf;
+}
+
+/**
+ * Refresh and write index.
+ */
+static void refresh_and_write_cache(void)
+{
+ struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+ hold_locked_index(lock_file, 1);
+ refresh_cache(REFRESH_QUIET);
+ 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)
+{
+ unsigned char head[GIT_SHA1_RAWSZ];
+ int i;
+
+ if (!get_sha1_tree("HEAD", head)) {
+ struct diff_options opt;
+
+ diff_setup(&opt);
+ DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
+ if (!sb)
+ DIFF_OPT_SET(&opt, QUICK);
+ do_diff_cache(head, &opt);
+ diffcore_std(&opt);
+ for (i = 0; sb && i < diff_queued_diff.nr; i++) {
+ if (i)
+ strbuf_addch(sb, ' ');
+ strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
+ }
+ diff_flush(&opt);
+ return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
+ } else {
+ for (i = 0; sb && i < active_nr; i++) {
+ if (i)
+ strbuf_addch(sb, ' ');
+ strbuf_addstr(sb, active_cache[i]->name);
+ }
+ return !!active_nr;
+ }
+}
+
+/**
+ * Dies with a user-friendly message on how to proceed after resolving the
+ * problem. This message can be overridden with state->resolvemsg.
+ */
+static void NORETURN die_user_resolve(const struct am_state *state)
+{
+ if (state->resolvemsg) {
+ printf_ln("%s", state->resolvemsg);
+ } else {
+ const char *cmdline = state->interactive ? "git am -i" : "git am";
+
+ printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
+ printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+ printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+ }
+
+ exit(128);
+}
+
+static void am_signoff(struct strbuf *sb)
+{
+ char *cp;
+ struct strbuf mine = STRBUF_INIT;
+
+ /* Does it end with our own sign-off? */
+ strbuf_addf(&mine, "\n%s%s\n",
+ sign_off_header,
+ fmt_name(getenv("GIT_COMMITTER_NAME"),
+ getenv("GIT_COMMITTER_EMAIL")));
+ 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);
+}
+
+/**
+ * Appends signoff to the "msg" field of the am_state.
+ */
+static void am_append_signoff(struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
+ am_signoff(&sb);
+ state->msg = strbuf_detach(&sb, &state->msg_len);
+}
+
+/**
+ * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
+ * state->msg will be set to the patch message. state->author_name,
+ * state->author_email and state->author_date will be set to the patch author's
+ * name, email and date respectively. The patch body will be written to the
+ * state directory's "patch" file.
+ *
+ * Returns 1 if the patch should be skipped, 0 otherwise.
+ */
+static int parse_mail(struct am_state *state, const char *mail)
+{
+ FILE *fp;
+ struct strbuf sb = STRBUF_INIT;
+ struct strbuf msg = STRBUF_INIT;
+ struct strbuf author_name = STRBUF_INIT;
+ struct strbuf author_date = STRBUF_INIT;
+ struct strbuf author_email = STRBUF_INIT;
+ int ret = 0;
+ struct mailinfo mi;
+
+ setup_mailinfo(&mi);
+
+ if (state->utf8)
+ mi.metainfo_charset = get_commit_output_encoding();
+ else
+ mi.metainfo_charset = NULL;
+
+ switch (state->keep) {
+ case KEEP_FALSE:
+ break;
+ case KEEP_TRUE:
+ mi.keep_subject = 1;
+ break;
+ case KEEP_NON_PATCH:
+ mi.keep_non_patch_brackets_in_subject = 1;
+ break;
+ default:
+ die("BUG: invalid value for state->keep");
+ }
+
+ if (state->message_id)
+ mi.add_message_id = 1;
+
+ switch (state->scissors) {
+ case SCISSORS_UNSET:
+ break;
+ case SCISSORS_FALSE:
+ mi.use_scissors = 0;
+ break;
+ case SCISSORS_TRUE:
+ mi.use_scissors = 1;
+ break;
+ default:
+ 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'");
+ if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
+ die("could not parse patch");
+
+ fclose(mi.input);
+ fclose(mi.output);
+
+ /* Extract message and author information */
+ fp = xfopen(am_path(state, "info"), "r");
+ while (!strbuf_getline_lf(&sb, fp)) {
+ const char *x;
+
+ if (skip_prefix(sb.buf, "Subject: ", &x)) {
+ if (msg.len)
+ strbuf_addch(&msg, '\n');
+ strbuf_addstr(&msg, x);
+ } else if (skip_prefix(sb.buf, "Author: ", &x))
+ strbuf_addstr(&author_name, x);
+ else if (skip_prefix(sb.buf, "Email: ", &x))
+ strbuf_addstr(&author_email, x);
+ else if (skip_prefix(sb.buf, "Date: ", &x))
+ strbuf_addstr(&author_date, x);
+ }
+ fclose(fp);
+
+ /* Skip pine's internal folder data */
+ if (!strcmp(author_name.buf, "Mail System Internal Data")) {
+ ret = 1;
+ goto finish;
+ }
+
+ if (is_empty_file(am_path(state, "patch"))) {
+ printf_ln(_("Patch is empty. Was it split wrong?"));
+ die_user_resolve(state);
+ }
+
+ strbuf_addstr(&msg, "\n\n");
+ strbuf_addbuf(&msg, &mi.log_message);
+ strbuf_stripspace(&msg, 0);
+
+ if (state->signoff)
+ am_signoff(&msg);
+
+ assert(!state->author_name);
+ state->author_name = strbuf_detach(&author_name, NULL);
+
+ assert(!state->author_email);
+ state->author_email = strbuf_detach(&author_email, NULL);
+
+ assert(!state->author_date);
+ state->author_date = strbuf_detach(&author_date, NULL);
+
+ assert(!state->msg);
+ state->msg = strbuf_detach(&msg, &state->msg_len);
+
+finish:
+ strbuf_release(&msg);
+ strbuf_release(&author_date);
+ strbuf_release(&author_email);
+ strbuf_release(&author_name);
+ strbuf_release(&sb);
+ clear_mailinfo(&mi);
+ return ret;
+}
+
+/**
+ * Sets commit_id to the commit hash where the mail was generated from.
+ * Returns 0 on success, -1 on failure.
+ */
+static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail)
+{
+ struct strbuf sb = STRBUF_INIT;
+ FILE *fp = xfopen(mail, "r");
+ const char *x;
+
+ if (strbuf_getline_lf(&sb, fp))
+ return -1;
+
+ if (!skip_prefix(sb.buf, "From ", &x))
+ return -1;
+
+ if (get_sha1_hex(x, commit_id) < 0)
+ return -1;
+
+ strbuf_release(&sb);
+ fclose(fp);
+ return 0;
+}
+
+/**
+ * Sets state->msg, state->author_name, state->author_email, state->author_date
+ * to the commit's respective info.
+ */
+static void get_commit_info(struct am_state *state, struct commit *commit)
+{
+ const char *buffer, *ident_line, *author_date, *msg;
+ size_t ident_len;
+ struct ident_split ident_split;
+ struct strbuf sb = STRBUF_INIT;
+
+ buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+
+ ident_line = find_commit_header(buffer, "author", &ident_len);
+
+ if (split_ident_line(&ident_split, ident_line, ident_len) < 0) {
+ strbuf_add(&sb, ident_line, ident_len);
+ die(_("invalid ident line: %s"), sb.buf);
+ }
+
+ assert(!state->author_name);
+ if (ident_split.name_begin) {
+ strbuf_add(&sb, ident_split.name_begin,
+ ident_split.name_end - ident_split.name_begin);
+ state->author_name = strbuf_detach(&sb, NULL);
+ } else
+ state->author_name = xstrdup("");
+
+ assert(!state->author_email);
+ if (ident_split.mail_begin) {
+ strbuf_add(&sb, ident_split.mail_begin,
+ ident_split.mail_end - ident_split.mail_begin);
+ state->author_email = strbuf_detach(&sb, NULL);
+ } else
+ state->author_email = xstrdup("");
+
+ author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL));
+ strbuf_addstr(&sb, author_date);
+ assert(!state->author_date);
+ state->author_date = strbuf_detach(&sb, NULL);
+
+ assert(!state->msg);
+ msg = strstr(buffer, "\n\n");
+ if (!msg)
+ die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
+ state->msg = xstrdup(msg + 2);
+ state->msg_len = strlen(state->msg);
+}
+
+/**
+ * Writes `commit` as a patch to the state directory's "patch" file.
+ */
+static void write_commit_patch(const struct am_state *state, struct commit *commit)
+{
+ struct rev_info rev_info;
+ FILE *fp;
+
+ fp = xfopen(am_path(state, "patch"), "w");
+ init_revisions(&rev_info, NULL);
+ rev_info.diff = 1;
+ rev_info.abbrev = 0;
+ rev_info.disable_stdin = 1;
+ 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.use_color = 0;
+ rev_info.diffopt.file = fp;
+ rev_info.diffopt.close_file = 1;
+ add_pending_object(&rev_info, &commit->object, "");
+ diff_setup_done(&rev_info.diffopt);
+ log_tree_commit(&rev_info, commit);
+}
+
+/**
+ * Writes the diff of the index against HEAD as a patch to the state
+ * directory's "patch" file.
+ */
+static void write_index_patch(const struct am_state *state)
+{
+ struct tree *tree;
+ unsigned char head[GIT_SHA1_RAWSZ];
+ struct rev_info rev_info;
+ FILE *fp;
+
+ if (!get_sha1_tree("HEAD", head))
+ tree = lookup_tree(head);
+ else
+ tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+
+ fp = xfopen(am_path(state, "patch"), "w");
+ init_revisions(&rev_info, NULL);
+ rev_info.diff = 1;
+ rev_info.disable_stdin = 1;
+ rev_info.no_commit_id = 1;
+ rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+ rev_info.diffopt.use_color = 0;
+ rev_info.diffopt.file = fp;
+ rev_info.diffopt.close_file = 1;
+ add_pending_object(&rev_info, &tree->object, "");
+ diff_setup_done(&rev_info.diffopt);
+ run_diff_index(&rev_info, 1);
+}
+
+/**
+ * Like parse_mail(), but parses the mail by looking up its commit ID
+ * directly. This is used in --rebasing mode to bypass git-mailinfo's munging
+ * of patches.
+ *
+ * state->orig_commit will be set to the original commit ID.
+ *
+ * Will always return 0 as the patch should never be skipped.
+ */
+static int parse_mail_rebase(struct am_state *state, const char *mail)
+{
+ struct commit *commit;
+ unsigned char commit_sha1[GIT_SHA1_RAWSZ];
+
+ if (get_mail_commit_sha1(commit_sha1, mail) < 0)
+ die(_("could not parse %s"), mail);
+
+ commit = lookup_commit_or_die(commit_sha1, mail);
+
+ get_commit_info(state, commit);
+
+ write_commit_patch(state, commit);
+
+ hashcpy(state->orig_commit, commit_sha1);
+ write_state_text(state, "original-commit", sha1_to_hex(commit_sha1));
+
+ return 0;
+}
+
+/**
+ * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
+ * `index_file` is not NULL, the patch will be applied to that index.
+ */
+static int run_apply(const struct am_state *state, const char *index_file)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+
+ if (index_file)
+ argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
+
+ /*
+ * If we are allowed to fall back on 3-way merge, don't give false
+ * errors during the initial attempt.
+ */
+ if (state->threeway && !index_file) {
+ cp.no_stdout = 1;
+ cp.no_stderr = 1;
+ }
+
+ argv_array_push(&cp.args, "apply");
+
+ argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+
+ if (index_file)
+ argv_array_push(&cp.args, "--cached");
+ else
+ argv_array_push(&cp.args, "--index");
+
+ argv_array_push(&cp.args, am_path(state, "patch"));
+
+ if (run_command(&cp))
+ return -1;
+
+ /* Reload index as git-apply will have modified it. */
+ discard_cache();
+ read_cache_from(index_file ? index_file : get_index_file());
+
+ return 0;
+}
+
+/**
+ * Builds an index that contains just the blobs needed for a 3way merge.
+ */
+static int build_fake_ancestor(const struct am_state *state, const char *index_file)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+ argv_array_push(&cp.args, "apply");
+ argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+ argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
+ argv_array_push(&cp.args, am_path(state, "patch"));
+
+ if (run_command(&cp))
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Attempt a threeway merge, using index_path as the temporary index.
+ */
+static int fall_back_threeway(const struct am_state *state, const char *index_path)
+{
+ struct object_id orig_tree, their_tree, our_tree;
+ const struct object_id *bases[1] = { &orig_tree };
+ struct merge_options o;
+ struct commit *result;
+ char *their_tree_name;
+
+ if (get_oid("HEAD", &our_tree) < 0)
+ hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
+
+ if (build_fake_ancestor(state, index_path))
+ return error("could not build fake ancestor");
+
+ discard_cache();
+ read_cache_from(index_path);
+
+ if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))
+ return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
+
+ say(state, stdout, _("Using index info to reconstruct a base tree..."));
+
+ if (!state->quiet) {
+ /*
+ * List paths that needed 3-way fallback, so that the user can
+ * review them with extra care to spot mismerges.
+ */
+ struct rev_info rev_info;
+ const char *diff_filter_str = "--diff-filter=AM";
+
+ 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);
+ diff_setup_done(&rev_info.diffopt);
+ run_diff_index(&rev_info, 1);
+ }
+
+ if (run_apply(state, index_path))
+ return error(_("Did you hand edit your patch?\n"
+ "It does not apply to blobs recorded in its index."));
+
+ if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
+ return error("could not write tree");
+
+ say(state, stdout, _("Falling back to patching base and 3-way merge..."));
+
+ discard_cache();
+ read_cache();
+
+ /*
+ * This is not so wrong. Depending on which base we picked, orig_tree
+ * may be wildly different from ours, but their_tree has the same set of
+ * wildly different changes in parts the patch did not touch, so
+ * recursive ends up canceling them, saying that we reverted all those
+ * changes.
+ */
+
+ init_merge_options(&o);
+
+ o.branch1 = "HEAD";
+ their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+ o.branch2 = their_tree_name;
+
+ if (state->quiet)
+ o.verbosity = 0;
+
+ if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
+ rerere(state->allow_rerere_autoupdate);
+ free(their_tree_name);
+ return error(_("Failed to merge in the changes."));
+ }
+
+ free(their_tree_name);
+ return 0;
+}
+
+/**
+ * Commits the current index with state->msg as the commit message and
+ * state->author_name, state->author_email and state->author_date as the author
+ * information.
+ */
+static void do_commit(const struct am_state *state)
+{
+ unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],
+ commit[GIT_SHA1_RAWSZ];
+ unsigned char *ptr;
+ struct commit_list *parents = NULL;
+ const char *reflog_msg, *author;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (run_hook_le(NULL, "pre-applypatch", NULL))
+ exit(1);
+
+ if (write_cache_as_tree(tree, 0, NULL))
+ die(_("git write-tree failed to write a tree"));
+
+ if (!get_sha1_commit("HEAD", parent)) {
+ ptr = parent;
+ commit_list_insert(lookup_commit(parent), &parents);
+ } else {
+ ptr = NULL;
+ say(state, stderr, _("applying to an empty history"));
+ }
+
+ author = fmt_ident(state->author_name, state->author_email,
+ state->ignore_date ? NULL : state->author_date,
+ IDENT_STRICT);
+
+ if (state->committer_date_is_author_date)
+ setenv("GIT_COMMITTER_DATE",
+ state->ignore_date ? "" : state->author_date, 1);
+
+ 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");
+ if (!reflog_msg)
+ reflog_msg = "am";
+
+ strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
+ state->msg);
+
+ update_ref(sb.buf, "HEAD", commit, ptr, 0, UPDATE_REFS_DIE_ON_ERR);
+
+ if (state->rebasing) {
+ FILE *fp = xfopen(am_path(state, "rewritten"), "a");
+
+ assert(!is_null_sha1(state->orig_commit));
+ fprintf(fp, "%s ", sha1_to_hex(state->orig_commit));
+ fprintf(fp, "%s\n", sha1_to_hex(commit));
+ fclose(fp);
+ }
+
+ run_hook_le(NULL, "post-applypatch", NULL);
+
+ strbuf_release(&sb);
+}
+
+/**
+ * Validates the am_state for resuming -- the "msg" and authorship fields must
+ * be filled up.
+ */
+static void validate_resume_state(const struct am_state *state)
+{
+ if (!state->msg)
+ die(_("cannot resume: %s does not exist."),
+ am_path(state, "final-commit"));
+
+ if (!state->author_name || !state->author_email || !state->author_date)
+ die(_("cannot resume: %s does not exist."),
+ am_path(state, "author-script"));
+}
+
+/**
+ * Interactively prompt the user on whether the current patch should be
+ * applied.
+ *
+ * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to
+ * skip it.
+ */
+static int do_interactive(struct am_state *state)
+{
+ assert(state->msg);
+
+ if (!isatty(0))
+ die(_("cannot be interactive without stdin connected to a terminal."));
+
+ for (;;) {
+ const char *reply;
+
+ puts(_("Commit Body is:"));
+ puts("--------------------------");
+ printf("%s", state->msg);
+ puts("--------------------------");
+
+ /*
+ * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+ * in your translation. The program will only accept English
+ * input at this point.
+ */
+ reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
+
+ if (!reply) {
+ continue;
+ } else if (*reply == 'y' || *reply == 'Y') {
+ return 0;
+ } else if (*reply == 'a' || *reply == 'A') {
+ state->interactive = 0;
+ return 0;
+ } else if (*reply == 'n' || *reply == 'N') {
+ return 1;
+ } else if (*reply == 'e' || *reply == 'E') {
+ struct strbuf msg = STRBUF_INIT;
+
+ if (!launch_editor(am_path(state, "final-commit"), &msg, NULL)) {
+ free(state->msg);
+ state->msg = strbuf_detach(&msg, &state->msg_len);
+ }
+ strbuf_release(&msg);
+ } else if (*reply == 'v' || *reply == 'V') {
+ const char *pager = git_pager(1);
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ if (!pager)
+ pager = "cat";
+ prepare_pager_args(&cp, pager);
+ argv_array_push(&cp.args, am_path(state, "patch"));
+ run_command(&cp);
+ }
+ }
+}
+
+/**
+ * Applies all queued mail.
+ *
+ * If `resume` is true, we are "resuming". The "msg" and authorship fields, as
+ * well as the state directory's "patch" file is used as-is for applying the
+ * patch and committing it.
+ */
+static void am_run(struct am_state *state, int resume)
+{
+ const char *argv_gc_auto[] = {"gc", "--auto", NULL};
+ struct strbuf sb = STRBUF_INIT;
+
+ unlink(am_path(state, "dirtyindex"));
+
+ refresh_and_write_cache();
+
+ if (index_has_changes(&sb)) {
+ write_state_bool(state, "dirtyindex", 1);
+ die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
+ }
+
+ strbuf_release(&sb);
+
+ while (state->cur <= state->last) {
+ const char *mail = am_path(state, msgnum(state));
+ int apply_status;
+
+ reset_ident_date();
+
+ if (!file_exists(mail))
+ goto next;
+
+ if (resume) {
+ validate_resume_state(state);
+ } else {
+ int skip;
+
+ if (state->rebasing)
+ skip = parse_mail_rebase(state, mail);
+ else
+ skip = parse_mail(state, mail);
+
+ if (skip)
+ goto next; /* mail should be skipped */
+
+ write_author_script(state);
+ write_commit_msg(state);
+ }
+
+ if (state->interactive && do_interactive(state))
+ goto next;
+
+ if (run_applypatch_msg_hook(state))
+ exit(1);
+
+ say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
+
+ apply_status = run_apply(state, NULL);
+
+ if (apply_status && state->threeway) {
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_addstr(&sb, am_path(state, "patch-merge-index"));
+ apply_status = fall_back_threeway(state, sb.buf);
+ strbuf_release(&sb);
+
+ /*
+ * Applying the patch to an earlier tree and merging
+ * the result may have produced the same tree as ours.
+ */
+ if (!apply_status && !index_has_changes(NULL)) {
+ say(state, stdout, _("No changes -- Patch already applied."));
+ goto next;
+ }
+ }
+
+ if (apply_status) {
+ int advice_amworkdir = 1;
+
+ printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
+ linelen(state->msg), state->msg);
+
+ git_config_get_bool("advice.amworkdir", &advice_amworkdir);
+
+ if (advice_amworkdir)
+ printf_ln(_("The copy of the patch that failed is found in: %s"),
+ am_path(state, "patch"));
+
+ die_user_resolve(state);
+ }
+
+ do_commit(state);
+
+next:
+ am_next(state);
+
+ if (resume)
+ am_load(state);
+ resume = 0;
+ }
+
+ if (!is_empty_file(am_path(state, "rewritten"))) {
+ assert(state->rebasing);
+ copy_notes_for_rebase(state);
+ run_post_rewrite_hook(state);
+ }
+
+ /*
+ * In rebasing mode, it's up to the caller to take care of
+ * housekeeping.
+ */
+ if (!state->rebasing) {
+ am_destroy(state);
+ close_all_packs();
+ run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+ }
+}
+
+/**
+ * Resume the current am session after patch application failure. The user did
+ * all the hard work, and we do not have to do any patch application. Just
+ * trust and commit what the user has in the index and working tree.
+ */
+static void am_resolve(struct am_state *state)
+{
+ validate_resume_state(state);
+
+ say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
+
+ if (!index_has_changes(NULL)) {
+ printf_ln(_("No changes - did you forget to use 'git add'?\n"
+ "If there is nothing left to stage, chances are that something else\n"
+ "already introduced the same changes; you might want to skip this patch."));
+ die_user_resolve(state);
+ }
+
+ if (unmerged_cache()) {
+ printf_ln(_("You still have unmerged paths in your index.\n"
+ "Did you forget to use 'git add'?"));
+ die_user_resolve(state);
+ }
+
+ if (state->interactive) {
+ write_index_patch(state);
+ if (do_interactive(state))
+ goto next;
+ }
+
+ rerere(0);
+
+ do_commit(state);
+
+next:
+ am_next(state);
+ am_load(state);
+ am_run(state, 0);
+}
+
+/**
+ * Performs a checkout fast-forward from `head` to `remote`. If `reset` is
+ * true, any unmerged entries will be discarded. Returns 0 on success, -1 on
+ * failure.
+ */
+static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
+{
+ struct lock_file *lock_file;
+ 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, 1);
+
+ refresh_cache(REFRESH_QUIET);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.update = 1;
+ opts.merge = 1;
+ opts.reset = reset;
+ opts.fn = twoway_merge;
+ init_tree_desc(&t[0], head->buffer, head->size);
+ init_tree_desc(&t[1], remote->buffer, remote->size);
+
+ if (unpack_trees(2, t, &opts)) {
+ rollback_lock_file(lock_file);
+ return -1;
+ }
+
+ if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ die(_("unable to write new index file"));
+
+ return 0;
+}
+
+/**
+ * Merges a tree into the index. The index's stat info will take precedence
+ * over the merged tree's. Returns 0 on success, -1 on failure.
+ */
+static int merge_tree(struct tree *tree)
+{
+ struct lock_file *lock_file;
+ 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, 1);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.merge = 1;
+ opts.fn = oneway_merge;
+ init_tree_desc(&t[0], tree->buffer, tree->size);
+
+ if (unpack_trees(1, t, &opts)) {
+ rollback_lock_file(lock_file);
+ return -1;
+ }
+
+ if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ die(_("unable to write new index file"));
+
+ return 0;
+}
+
+/**
+ * Clean the index without touching entries that are not modified between
+ * `head` and `remote`.
+ */
+static int clean_index(const unsigned char *head, const unsigned char *remote)
+{
+ struct tree *head_tree, *remote_tree, *index_tree;
+ unsigned char index[GIT_SHA1_RAWSZ];
+
+ head_tree = parse_tree_indirect(head);
+ if (!head_tree)
+ return error(_("Could not parse object '%s'."), sha1_to_hex(head));
+
+ remote_tree = parse_tree_indirect(remote);
+ if (!remote_tree)
+ return error(_("Could not parse object '%s'."), sha1_to_hex(remote));
+
+ read_cache_unmerged();
+
+ if (fast_forward_to(head_tree, head_tree, 1))
+ return -1;
+
+ if (write_cache_as_tree(index, 0, NULL))
+ return -1;
+
+ index_tree = parse_tree_indirect(index);
+ if (!index_tree)
+ return error(_("Could not parse object '%s'."), sha1_to_hex(index));
+
+ if (fast_forward_to(index_tree, remote_tree, 0))
+ return -1;
+
+ if (merge_tree(remote_tree))
+ return -1;
+
+ remove_branch_state();
+
+ return 0;
+}
+
+/**
+ * Resets rerere's merge resolution metadata.
+ */
+static void am_rerere_clear(void)
+{
+ struct string_list merge_rr = STRING_LIST_INIT_DUP;
+ rerere_clear(&merge_rr);
+ string_list_clear(&merge_rr, 1);
+}
+
+/**
+ * Resume the current am session by skipping the current patch.
+ */
+static void am_skip(struct am_state *state)
+{
+ unsigned char head[GIT_SHA1_RAWSZ];
+
+ am_rerere_clear();
+
+ if (get_sha1("HEAD", head))
+ hashcpy(head, EMPTY_TREE_SHA1_BIN);
+
+ if (clean_index(head, head))
+ die(_("failed to clean index"));
+
+ am_next(state);
+ am_load(state);
+ am_run(state, 0);
+}
+
+/**
+ * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.
+ *
+ * It is not safe to reset HEAD when:
+ * 1. git-am previously failed because the index was dirty.
+ * 2. HEAD has moved since git-am previously failed.
+ */
+static int safe_to_abort(const struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+ unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];
+
+ if (file_exists(am_path(state, "dirtyindex")))
+ return 0;
+
+ if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
+ if (get_sha1_hex(sb.buf, abort_safety))
+ die(_("could not parse %s"), am_path(state, "abort_safety"));
+ } else
+ hashclr(abort_safety);
+
+ if (get_sha1("HEAD", head))
+ hashclr(head);
+
+ if (!hashcmp(head, abort_safety))
+ return 1;
+
+ error(_("You seem to have moved HEAD since the last 'am' failure.\n"
+ "Not rewinding to ORIG_HEAD"));
+
+ return 0;
+}
+
+/**
+ * Aborts the current am session if it is safe to do so.
+ */
+static void am_abort(struct am_state *state)
+{
+ unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
+ int has_curr_head, has_orig_head;
+ char *curr_branch;
+
+ if (!safe_to_abort(state)) {
+ am_destroy(state);
+ return;
+ }
+
+ am_rerere_clear();
+
+ curr_branch = resolve_refdup("HEAD", 0, curr_head, NULL);
+ has_curr_head = !is_null_sha1(curr_head);
+ if (!has_curr_head)
+ hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
+
+ has_orig_head = !get_sha1("ORIG_HEAD", orig_head);
+ if (!has_orig_head)
+ hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
+
+ clean_index(curr_head, orig_head);
+
+ if (has_orig_head)
+ 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(curr_branch, NULL, REF_NODEREF);
+
+ free(curr_branch);
+ am_destroy(state);
+}
+
+/**
+ * parse_options() callback that validates and sets opt->value to the
+ * PATCH_FORMAT_* enum value corresponding to `arg`.
+ */
+static int parse_opt_patchformat(const struct option *opt, const char *arg, int unset)
+{
+ int *opt_value = opt->value;
+
+ if (!strcmp(arg, "mbox"))
+ *opt_value = PATCH_FORMAT_MBOX;
+ else if (!strcmp(arg, "stgit"))
+ *opt_value = PATCH_FORMAT_STGIT;
+ else if (!strcmp(arg, "stgit-series"))
+ *opt_value = PATCH_FORMAT_STGIT_SERIES;
+ else if (!strcmp(arg, "hg"))
+ *opt_value = PATCH_FORMAT_HG;
+ else if (!strcmp(arg, "mboxrd"))
+ *opt_value = PATCH_FORMAT_MBOXRD;
+ else
+ return error(_("Invalid value for --patch-format: %s"), arg);
+ return 0;
+}
+
+enum resume_mode {
+ RESUME_FALSE = 0,
+ RESUME_APPLY,
+ RESUME_RESOLVED,
+ RESUME_SKIP,
+ RESUME_ABORT
+};
+
+static int git_am_config(const char *k, const char *v, void *cb)
+{
+ int status;
+
+ status = git_gpg_config(k, v, NULL);
+ if (status)
+ return status;
+
+ return git_default_config(k, v, NULL);
+}
+
+int cmd_am(int argc, const char **argv, const char *prefix)
+{
+ struct am_state state;
+ int binary = -1;
+ int keep_cr = -1;
+ int patch_format = PATCH_FORMAT_UNKNOWN;
+ enum resume_mode resume = RESUME_FALSE;
+ int in_progress;
+
+ const char * const usage[] = {
+ N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
+ N_("git am [<options>] (--continue | --skip | --abort)"),
+ NULL
+ };
+
+ struct option options[] = {
+ OPT_BOOL('i', "interactive", &state.interactive,
+ N_("run interactively")),
+ OPT_HIDDEN_BOOL('b', "binary", &binary,
+ N_("historical option -- no-op")),
+ OPT_BOOL('3', "3way", &state.threeway,
+ N_("allow fall back on 3way merging if needed")),
+ OPT__QUIET(&state.quiet, N_("be quiet")),
+ OPT_SET_INT('s', "signoff", &state.signoff,
+ N_("add a Signed-off-by line to the commit message"),
+ SIGNOFF_EXPLICIT),
+ OPT_BOOL('u', "utf8", &state.utf8,
+ N_("recode into utf8 (default)")),
+ OPT_SET_INT('k', "keep", &state.keep,
+ N_("pass -k flag to git-mailinfo"), KEEP_TRUE),
+ OPT_SET_INT(0, "keep-non-patch", &state.keep,
+ N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
+ OPT_BOOL('m', "message-id", &state.message_id,
+ N_("pass -m flag to git-mailinfo")),
+ { OPTION_SET_INT, 0, "keep-cr", &keep_cr, NULL,
+ N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
+ { OPTION_SET_INT, 0, "no-keep-cr", &keep_cr, NULL,
+ N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
+ OPT_BOOL('c', "scissors", &state.scissors,
+ N_("strip everything before a scissors line")),
+ OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "ignore-space-change", &state.git_apply_opts, NULL,
+ N_("pass it through git-apply"),
+ PARSE_OPT_NOARG),
+ OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &state.git_apply_opts, NULL,
+ N_("pass it through git-apply"),
+ PARSE_OPT_NOARG),
+ OPT_PASSTHRU_ARGV(0, "directory", &state.git_apply_opts, N_("root"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "exclude", &state.git_apply_opts, N_("path"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "include", &state.git_apply_opts, N_("path"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts, N_("n"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts, N_("num"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
+ N_("format the patch(es) are in"),
+ parse_opt_patchformat),
+ OPT_PASSTHRU_ARGV(0, "reject", &state.git_apply_opts, NULL,
+ N_("pass it through git-apply"),
+ PARSE_OPT_NOARG),
+ OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
+ N_("override error message when patch failure occurs")),
+ OPT_CMDMODE(0, "continue", &resume,
+ N_("continue applying patches after resolving a conflict"),
+ RESUME_RESOLVED),
+ OPT_CMDMODE('r', "resolved", &resume,
+ N_("synonyms for --continue"),
+ RESUME_RESOLVED),
+ OPT_CMDMODE(0, "skip", &resume,
+ N_("skip the current patch"),
+ RESUME_SKIP),
+ OPT_CMDMODE(0, "abort", &resume,
+ N_("restore the original branch and abort the patching operation."),
+ RESUME_ABORT),
+ OPT_BOOL(0, "committer-date-is-author-date",
+ &state.committer_date_is_author_date,
+ N_("lie about committer date")),
+ OPT_BOOL(0, "ignore-date", &state.ignore_date,
+ N_("use current timestamp for author date")),
+ OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),
+ { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
+ N_("GPG-sign commits"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
+ N_("(internal use for git-rebase)")),
+ OPT_END()
+ };
+
+ git_config(git_am_config, NULL);
+
+ am_state_init(&state, git_path("rebase-apply"));
+
+ in_progress = am_in_progress(&state);
+ if (in_progress)
+ am_load(&state);
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (binary >= 0)
+ fprintf_ln(stderr, _("The -b/--binary option has been a no-op for long time, and\n"
+ "it will be removed. Please do not use it anymore."));
+
+ /* Ensure a valid committer ident can be constructed */
+ git_committer_info(IDENT_STRICT);
+
+ if (read_index_preload(&the_index, NULL) < 0)
+ die(_("failed to read the index"));
+
+ if (in_progress) {
+ /*
+ * Catch user error to feed us patches when there is a session
+ * in progress:
+ *
+ * 1. mbox path(s) are provided on the command-line.
+ * 2. stdin is not a tty: the user is trying to feed us a patch
+ * from standard input. This is somewhat unreliable -- stdin
+ * could be /dev/null for example and the caller did not
+ * intend to feed us a patch but wanted to continue
+ * unattended.
+ */
+ if (argc || (resume == RESUME_FALSE && !isatty(0)))
+ die(_("previous rebase directory %s still exists but mbox given."),
+ state.dir);
+
+ if (resume == RESUME_FALSE)
+ resume = RESUME_APPLY;
+
+ if (state.signoff == SIGNOFF_EXPLICIT)
+ am_append_signoff(&state);
+ } else {
+ struct argv_array paths = ARGV_ARRAY_INIT;
+ int i;
+
+ /*
+ * Handle stray state directory in the independent-run case. In
+ * the --rebasing case, it is up to the caller to take care of
+ * stray directories.
+ */
+ if (file_exists(state.dir) && !state.rebasing) {
+ if (resume == RESUME_ABORT) {
+ am_destroy(&state);
+ am_state_release(&state);
+ return 0;
+ }
+
+ die(_("Stray %s directory found.\n"
+ "Use \"git am --abort\" to remove it."),
+ state.dir);
+ }
+
+ if (resume)
+ die(_("Resolve operation not in progress, we are not resuming."));
+
+ for (i = 0; i < argc; i++) {
+ if (is_absolute_path(argv[i]) || !prefix)
+ argv_array_push(&paths, argv[i]);
+ else
+ argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
+ }
+
+ am_setup(&state, patch_format, paths.argv, keep_cr);
+
+ argv_array_clear(&paths);
+ }
+
+ switch (resume) {
+ case RESUME_FALSE:
+ am_run(&state, 0);
+ break;
+ case RESUME_APPLY:
+ am_run(&state, 1);
+ break;
+ case RESUME_RESOLVED:
+ am_resolve(&state);
+ break;
+ case RESUME_SKIP:
+ am_skip(&state);
+ break;
+ case RESUME_ABORT:
+ am_abort(&state);
+ break;
+ default:
+ die("BUG: invalid resume value");
+ }
+
+ am_state_release(&state);
+
+ return 0;
+}
diff --git a/builtin/apply.c b/builtin/apply.c
index 0769b09..1a488f9 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -21,133 +21,161 @@
#include "ll-merge.h"
#include "rerere.h"
-/*
- * --check turns on checking that the working tree matches the
- * files that are being modified, but doesn't apply the patch
- * --stat does just a diffstat, and doesn't actually apply
- * --numstat does numeric diffstat, and doesn't actually apply
- * --index-info shows the old and new index info for paths if available.
- * --index updates the cache as well.
- * --cached updates only the cache without ever touching the working tree.
- */
-static const char *prefix;
-static int prefix_length = -1;
-static int newfd = -1;
-
-static int unidiff_zero;
-static int p_value = 1;
-static int p_value_known;
-static int check_index;
-static int update_index;
-static int cached;
-static int diffstat;
-static int numstat;
-static int summary;
-static int check;
-static int apply = 1;
-static int apply_in_reverse;
-static int apply_with_reject;
-static int apply_verbosely;
-static int allow_overlap;
-static int no_add;
-static int threeway;
-static int unsafe_paths;
-static const char *fake_ancestor;
-static int line_termination = '\n';
-static unsigned int p_context = UINT_MAX;
-static const char * const apply_usage[] = {
- N_("git apply [<options>] [<patch>...]"),
- NULL
-};
-
-static enum ws_error_action {
+enum ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
correct_ws_error
-} ws_error_action = warn_on_ws_error;
-static int whitespace_error;
-static int squelch_whitespace_errors = 5;
-static int applied_after_fixing_ws;
+};
+
-static enum ws_ignore {
+enum ws_ignore {
ignore_ws_none,
ignore_ws_change
-} ws_ignore_action = ignore_ws_none;
+};
+
+/*
+ * We need to keep track of how symlinks in the preimage are
+ * manipulated by the patches. A patch to add a/b/c where a/b
+ * is a symlink should not be allowed to affect the directory
+ * the symlink points at, but if the same patch removes a/b,
+ * it is perfectly fine, as the patch removes a/b to make room
+ * to create a directory a/b so that a/b/c can be created.
+ *
+ * See also "struct string_list symlink_changes" in "struct
+ * apply_state".
+ */
+#define SYMLINK_GOES_AWAY 01
+#define SYMLINK_IN_RESULT 02
+
+struct apply_state {
+ const char *prefix;
+ int prefix_length;
+
+ /* These are lock_file related */
+ struct lock_file *lock_file;
+ int newfd;
+
+ /* These control what gets looked at and modified */
+ int apply; /* this is not a dry-run */
+ int cached; /* apply to the index only */
+ int check; /* preimage must match working tree, don't actually apply */
+ int check_index; /* preimage must match the indexed version */
+ int update_index; /* check_index && apply */
+
+ /* These control cosmetic aspect of the output */
+ int diffstat; /* just show a diffstat, and don't actually apply */
+ int numstat; /* just show a numeric diffstat, and don't actually apply */
+ int summary; /* just report creation, deletion, etc, and don't actually apply */
+
+ /* These boolean parameters control how the apply is done */
+ int allow_overlap;
+ int apply_in_reverse;
+ int apply_with_reject;
+ int apply_verbosely;
+ int no_add;
+ int threeway;
+ int unidiff_zero;
+ int unsafe_paths;
+
+ /* Other non boolean parameters */
+ const char *fake_ancestor;
+ const char *patch_input_file;
+ int line_termination;
+ struct strbuf root;
+ int p_value;
+ int p_value_known;
+ unsigned int p_context;
+
+ /* Exclude and include path parameters */
+ struct string_list limit_by_name;
+ int has_include;
+
+ /* Various "current state" */
+ int linenr; /* current line number */
+ struct string_list symlink_changes; /* we have to track symlinks */
+ /*
+ * For "diff-stat" like behaviour, we keep track of the biggest change
+ * we've seen, and the longest filename. That allows us to do simple
+ * scaling.
+ */
+ int max_change;
+ int max_len;
+
+ /*
+ * Records filenames that have been touched, in order to handle
+ * the case where more than one patches touch the same file.
+ */
+ struct string_list fn_table;
+
+ /* These control whitespace errors */
+ enum ws_error_action ws_error_action;
+ enum ws_ignore ws_ignore_action;
+ const char *whitespace_option;
+ int whitespace_error;
+ int squelch_whitespace_errors;
+ int applied_after_fixing_ws;
+};
-static const char *patch_input_file;
-static const char *root;
-static int root_len;
-static int read_stdin = 1;
-static int options;
+static const char * const apply_usage[] = {
+ N_("git apply [<options>] [<patch>...]"),
+ NULL
+};
-static void parse_whitespace_option(const char *option)
+static void parse_whitespace_option(struct apply_state *state, const char *option)
{
if (!option) {
- ws_error_action = warn_on_ws_error;
+ state->ws_error_action = warn_on_ws_error;
return;
}
if (!strcmp(option, "warn")) {
- ws_error_action = warn_on_ws_error;
+ state->ws_error_action = warn_on_ws_error;
return;
}
if (!strcmp(option, "nowarn")) {
- ws_error_action = nowarn_ws_error;
+ state->ws_error_action = nowarn_ws_error;
return;
}
if (!strcmp(option, "error")) {
- ws_error_action = die_on_ws_error;
+ state->ws_error_action = die_on_ws_error;
return;
}
if (!strcmp(option, "error-all")) {
- ws_error_action = die_on_ws_error;
- squelch_whitespace_errors = 0;
+ state->ws_error_action = die_on_ws_error;
+ state->squelch_whitespace_errors = 0;
return;
}
if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
- ws_error_action = correct_ws_error;
+ state->ws_error_action = correct_ws_error;
return;
}
die(_("unrecognized whitespace option '%s'"), option);
}
-static void parse_ignorewhitespace_option(const char *option)
+static void parse_ignorewhitespace_option(struct apply_state *state,
+ const char *option)
{
if (!option || !strcmp(option, "no") ||
!strcmp(option, "false") || !strcmp(option, "never") ||
!strcmp(option, "none")) {
- ws_ignore_action = ignore_ws_none;
+ state->ws_ignore_action = ignore_ws_none;
return;
}
if (!strcmp(option, "change")) {
- ws_ignore_action = ignore_ws_change;
+ state->ws_ignore_action = ignore_ws_change;
return;
}
die(_("unrecognized whitespace ignore option '%s'"), option);
}
-static void set_default_whitespace_mode(const char *whitespace_option)
+static void set_default_whitespace_mode(struct apply_state *state)
{
- if (!whitespace_option && !apply_default_whitespace)
- ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
+ if (!state->whitespace_option && !apply_default_whitespace)
+ state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
}
/*
- * For "diff-stat" like behaviour, we keep track of the biggest change
- * we've seen, and the longest filename. That allows us to do simple
- * scaling.
- */
-static int max_change, max_len;
-
-/*
- * Various "current state", notably line numbers and what
- * file (and how) we're patching right now.. The "is_xxxx"
- * things are flags, where -1 means "don't know yet".
- */
-static int linenr = 1;
-
-/*
* This represents one "hunk" from a patch, starting with
* "@@ -oldpos,oldlines +newpos,newlines @@" marker. The
* patch text is pointed at by patch, and its byte length
@@ -208,7 +236,7 @@ struct patch {
struct patch *next;
/* three-way fallback result */
- unsigned char threeway_stage[3][20];
+ struct object_id threeway_stage[3];
};
static void free_fragment_list(struct fragment *list)
@@ -266,13 +294,6 @@ struct image {
struct line *line;
};
-/*
- * Records filenames that have been touched, in order to handle
- * the case where more than one patches touch the same file.
- */
-
-static struct string_list fn_table;
-
static uint32_t hash_line(const char *cp, size_t len)
{
size_t i;
@@ -443,7 +464,7 @@ static int is_dev_null(const char *str)
#define TERM_SPACE 1
#define TERM_TAB 2
-static int name_terminate(const char *name, int namelen, int c, int terminate)
+static int name_terminate(int c, int terminate)
{
if (c == ' ' && !(terminate & TERM_SPACE))
return 0;
@@ -470,7 +491,10 @@ static char *squash_slash(char *name)
return name;
}
-static char *find_name_gnu(const char *line, const char *def, int p_value)
+static char *find_name_gnu(struct apply_state *state,
+ const char *line,
+ const char *def,
+ int p_value)
{
struct strbuf name = STRBUF_INIT;
char *cp;
@@ -494,8 +518,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)
}
strbuf_remove(&name, 0, cp - name.buf);
- if (root)
- strbuf_insert(&name, 0, root, root_len);
+ if (state->root.len)
+ strbuf_insert(&name, 0, state->root.buf, state->root.len);
return squash_slash(strbuf_detach(&name, NULL));
}
@@ -658,8 +682,12 @@ static size_t diff_timestamp_len(const char *line, size_t len)
return line + len - end;
}
-static char *find_name_common(const char *line, const char *def,
- int p_value, const char *end, int terminate)
+static char *find_name_common(struct apply_state *state,
+ const char *line,
+ const char *def,
+ int p_value,
+ const char *end,
+ int terminate)
{
int len;
const char *start = NULL;
@@ -672,7 +700,7 @@ static char *find_name_common(const char *line, const char *def,
if (!end && isspace(c)) {
if (c == '\n')
break;
- if (name_terminate(start, line-start, c, terminate))
+ if (name_terminate(c, terminate))
break;
}
line++;
@@ -697,35 +725,39 @@ static char *find_name_common(const char *line, const char *def,
return squash_slash(xstrdup(def));
}
- if (root) {
- char *ret = xmalloc(root_len + len + 1);
- strcpy(ret, root);
- memcpy(ret + root_len, start, len);
- ret[root_len + len] = '\0';
+ if (state->root.len) {
+ char *ret = xstrfmt("%s%.*s", state->root.buf, len, start);
return squash_slash(ret);
}
return squash_slash(xmemdupz(start, len));
}
-static char *find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name(struct apply_state *state,
+ const char *line,
+ char *def,
+ int p_value,
+ int terminate)
{
if (*line == '"') {
- char *name = find_name_gnu(line, def, p_value);
+ char *name = find_name_gnu(state, line, def, p_value);
if (name)
return name;
}
- return find_name_common(line, def, p_value, NULL, terminate);
+ return find_name_common(state, line, def, p_value, NULL, terminate);
}
-static char *find_name_traditional(const char *line, char *def, int p_value)
+static char *find_name_traditional(struct apply_state *state,
+ const char *line,
+ char *def,
+ int p_value)
{
size_t len;
size_t date_len;
if (*line == '"') {
- char *name = find_name_gnu(line, def, p_value);
+ char *name = find_name_gnu(state, line, def, p_value);
if (name)
return name;
}
@@ -733,10 +765,10 @@ static char *find_name_traditional(const char *line, char *def, int p_value)
len = strchrnul(line, '\n') - line;
date_len = diff_timestamp_len(line, len);
if (!date_len)
- return find_name_common(line, def, p_value, NULL, TERM_TAB);
+ return find_name_common(state, line, def, p_value, NULL, TERM_TAB);
len -= date_len;
- return find_name_common(line, def, p_value, line + len, 0);
+ return find_name_common(state, line, def, p_value, line + len, 0);
}
static int count_slashes(const char *cp)
@@ -754,30 +786,30 @@ static int count_slashes(const char *cp)
* Given the string after "--- " or "+++ ", guess the appropriate
* p_value for the given patch.
*/
-static int guess_p_value(const char *nameline)
+static int guess_p_value(struct apply_state *state, const char *nameline)
{
char *name, *cp;
int val = -1;
if (is_dev_null(nameline))
return -1;
- name = find_name_traditional(nameline, NULL, 0);
+ name = find_name_traditional(state, nameline, NULL, 0);
if (!name)
return -1;
cp = strchr(name, '/');
if (!cp)
val = 0;
- else if (prefix) {
+ else if (state->prefix) {
/*
* Does it begin with "a/$our-prefix" and such? Then this is
* very likely to apply to our directory.
*/
- if (!strncmp(name, prefix, prefix_length))
- val = count_slashes(prefix);
+ if (!strncmp(name, state->prefix, state->prefix_length))
+ val = count_slashes(state->prefix);
else {
cp++;
- if (!strncmp(cp, prefix, prefix_length))
- val = count_slashes(prefix) + 1;
+ if (!strncmp(cp, state->prefix, state->prefix_length))
+ val = count_slashes(state->prefix) + 1;
}
}
free(name);
@@ -785,7 +817,7 @@ static int guess_p_value(const char *nameline)
}
/*
- * Does the ---/+++ line has the POSIX timestamp after the last HT?
+ * Does the ---/+++ line have the POSIX timestamp after the last HT?
* GNU diff puts epoch there to signal a creation/deletion event. Is
* this such a timestamp?
*/
@@ -864,36 +896,39 @@ static int has_epoch_timestamp(const char *nameline)
* files, we can happily check the index for a match, but for creating a
* new file we should try to match whatever "patch" does. I have no idea.
*/
-static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
+static void parse_traditional_patch(struct apply_state *state,
+ const char *first,
+ const char *second,
+ struct patch *patch)
{
char *name;
first += 4; /* skip "--- " */
second += 4; /* skip "+++ " */
- if (!p_value_known) {
+ if (!state->p_value_known) {
int p, q;
- p = guess_p_value(first);
- q = guess_p_value(second);
+ p = guess_p_value(state, first);
+ q = guess_p_value(state, second);
if (p < 0) p = q;
if (0 <= p && p == q) {
- p_value = p;
- p_value_known = 1;
+ state->p_value = p;
+ state->p_value_known = 1;
}
}
if (is_dev_null(first)) {
patch->is_new = 1;
patch->is_delete = 0;
- name = find_name_traditional(second, NULL, p_value);
+ name = find_name_traditional(state, second, NULL, state->p_value);
patch->new_name = name;
} else if (is_dev_null(second)) {
patch->is_new = 0;
patch->is_delete = 1;
- name = find_name_traditional(first, NULL, p_value);
+ name = find_name_traditional(state, first, NULL, state->p_value);
patch->old_name = name;
} else {
char *first_name;
- first_name = find_name_traditional(first, NULL, p_value);
- name = find_name_traditional(second, first_name, p_value);
+ first_name = find_name_traditional(state, first, NULL, state->p_value);
+ name = find_name_traditional(state, second, first_name, state->p_value);
free(first_name);
if (has_epoch_timestamp(first)) {
patch->is_new = 1;
@@ -909,10 +944,12 @@ static void parse_traditional_patch(const char *first, const char *second, struc
}
}
if (!name)
- die(_("unable to find filename in patch at line %d"), linenr);
+ die(_("unable to find filename in patch at line %d"), state->linenr);
}
-static int gitdiff_hdrend(const char *line, struct patch *patch)
+static int gitdiff_hdrend(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
return -1;
}
@@ -929,116 +966,135 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
#define DIFF_OLD_NAME 0
#define DIFF_NEW_NAME 1
-static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
+static void gitdiff_verify_name(struct apply_state *state,
+ const char *line,
+ int isnull,
+ char **name,
+ int side)
{
- if (!orig_name && !isnull)
- return find_name(line, NULL, p_value, TERM_TAB);
+ if (!*name && !isnull) {
+ *name = find_name(state, line, NULL, state->p_value, TERM_TAB);
+ return;
+ }
- if (orig_name) {
- int len;
- const char *name;
+ if (*name) {
+ int len = strlen(*name);
char *another;
- name = orig_name;
- len = strlen(name);
if (isnull)
- die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
- another = find_name(line, NULL, p_value, TERM_TAB);
- if (!another || memcmp(another, name, len + 1))
+ die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
+ *name, state->linenr);
+ another = find_name(state, line, NULL, state->p_value, TERM_TAB);
+ if (!another || memcmp(another, *name, len + 1))
die((side == DIFF_NEW_NAME) ?
_("git apply: bad git-diff - inconsistent new filename on line %d") :
- _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
+ _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
free(another);
- return orig_name;
- }
- else {
+ } else {
/* expect "/dev/null" */
if (memcmp("/dev/null", line, 9) || line[9] != '\n')
- die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
- return NULL;
+ die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
}
}
-static int gitdiff_oldname(const char *line, struct patch *patch)
+static int gitdiff_oldname(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
- char *orig = patch->old_name;
- patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
- DIFF_OLD_NAME);
- if (orig != patch->old_name)
- free(orig);
+ gitdiff_verify_name(state, line,
+ patch->is_new, &patch->old_name,
+ DIFF_OLD_NAME);
return 0;
}
-static int gitdiff_newname(const char *line, struct patch *patch)
+static int gitdiff_newname(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
- char *orig = patch->new_name;
- patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
- DIFF_NEW_NAME);
- if (orig != patch->new_name)
- free(orig);
+ gitdiff_verify_name(state, line,
+ patch->is_delete, &patch->new_name,
+ DIFF_NEW_NAME);
return 0;
}
-static int gitdiff_oldmode(const char *line, struct patch *patch)
+static int gitdiff_oldmode(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->old_mode = strtoul(line, NULL, 8);
return 0;
}
-static int gitdiff_newmode(const char *line, struct patch *patch)
+static int gitdiff_newmode(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->new_mode = strtoul(line, NULL, 8);
return 0;
}
-static int gitdiff_delete(const char *line, struct patch *patch)
+static int gitdiff_delete(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_delete = 1;
free(patch->old_name);
patch->old_name = xstrdup_or_null(patch->def_name);
- return gitdiff_oldmode(line, patch);
+ return gitdiff_oldmode(state, line, patch);
}
-static int gitdiff_newfile(const char *line, struct patch *patch)
+static int gitdiff_newfile(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_new = 1;
free(patch->new_name);
patch->new_name = xstrdup_or_null(patch->def_name);
- return gitdiff_newmode(line, patch);
+ return gitdiff_newmode(state, line, patch);
}
-static int gitdiff_copysrc(const char *line, struct patch *patch)
+static int gitdiff_copysrc(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_copy = 1;
free(patch->old_name);
- patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_copydst(const char *line, struct patch *patch)
+static int gitdiff_copydst(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_copy = 1;
free(patch->new_name);
- patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_renamesrc(const char *line, struct patch *patch)
+static int gitdiff_renamesrc(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_rename = 1;
free(patch->old_name);
- patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_renamedst(const char *line, struct patch *patch)
+static int gitdiff_renamedst(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_rename = 1;
free(patch->new_name);
- patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_similarity(const char *line, struct patch *patch)
+static int gitdiff_similarity(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
unsigned long val = strtoul(line, NULL, 10);
if (val <= 100)
@@ -1046,7 +1102,9 @@ static int gitdiff_similarity(const char *line, struct patch *patch)
return 0;
}
-static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+static int gitdiff_dissimilarity(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
unsigned long val = strtoul(line, NULL, 10);
if (val <= 100)
@@ -1054,7 +1112,9 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
return 0;
}
-static int gitdiff_index(const char *line, struct patch *patch)
+static int gitdiff_index(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
/*
* index line is N hexadecimal, "..", N hexadecimal,
@@ -1091,7 +1151,9 @@ static int gitdiff_index(const char *line, struct patch *patch)
* This is normal for a diff that doesn't change anything: we'll fall through
* into the next diff. Tell the parser to break out.
*/
-static int gitdiff_unrecognized(const char *line, struct patch *patch)
+static int gitdiff_unrecognized(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
return -1;
}
@@ -1100,15 +1162,17 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
* Skip p_value leading components from "line"; as we do not accept
* absolute paths, return NULL in that case.
*/
-static const char *skip_tree_prefix(const char *line, int llen)
+static const char *skip_tree_prefix(struct apply_state *state,
+ const char *line,
+ int llen)
{
int nslash;
int i;
- if (!p_value)
+ if (!state->p_value)
return (llen && line[0] == '/') ? NULL : line;
- nslash = p_value;
+ nslash = state->p_value;
for (i = 0; i < llen; i++) {
int ch = line[i];
if (ch == '/' && --nslash <= 0)
@@ -1125,7 +1189,9 @@ static const char *skip_tree_prefix(const char *line, int llen)
* creation or deletion of an empty file. In any of these cases,
* both sides are the same name under a/ and b/ respectively.
*/
-static char *git_header_name(const char *line, int llen)
+static char *git_header_name(struct apply_state *state,
+ const char *line,
+ int llen)
{
const char *name;
const char *second = NULL;
@@ -1143,7 +1209,7 @@ static char *git_header_name(const char *line, int llen)
goto free_and_fail1;
/* strip the a/b prefix including trailing slash */
- cp = skip_tree_prefix(first.buf, first.len);
+ cp = skip_tree_prefix(state, first.buf, first.len);
if (!cp)
goto free_and_fail1;
strbuf_remove(&first, 0, cp - first.buf);
@@ -1160,7 +1226,7 @@ static char *git_header_name(const char *line, int llen)
if (*second == '"') {
if (unquote_c_style(&sp, second, NULL))
goto free_and_fail1;
- cp = skip_tree_prefix(sp.buf, sp.len);
+ cp = skip_tree_prefix(state, sp.buf, sp.len);
if (!cp)
goto free_and_fail1;
/* They must match, otherwise ignore */
@@ -1171,7 +1237,7 @@ static char *git_header_name(const char *line, int llen)
}
/* unquoted second */
- cp = skip_tree_prefix(second, line + llen - second);
+ cp = skip_tree_prefix(state, second, line + llen - second);
if (!cp)
goto free_and_fail1;
if (line + llen - cp != first.len ||
@@ -1186,7 +1252,7 @@ static char *git_header_name(const char *line, int llen)
}
/* unquoted first name */
- name = skip_tree_prefix(line, llen);
+ name = skip_tree_prefix(state, line, llen);
if (!name)
return NULL;
@@ -1202,7 +1268,7 @@ static char *git_header_name(const char *line, int llen)
if (unquote_c_style(&sp, second, NULL))
goto free_and_fail2;
- np = skip_tree_prefix(sp.buf, sp.len);
+ np = skip_tree_prefix(state, sp.buf, sp.len);
if (!np)
goto free_and_fail2;
@@ -1246,7 +1312,7 @@ static char *git_header_name(const char *line, int llen)
*/
if (!name[len + 1])
return NULL; /* no postimage name */
- second = skip_tree_prefix(name + len + 1,
+ second = skip_tree_prefix(state, name + len + 1,
line_len - (len + 1));
if (!second)
return NULL;
@@ -1262,7 +1328,11 @@ static char *git_header_name(const char *line, int llen)
}
/* Verify that we recognize the lines following a git header */
-static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
+static int parse_git_header(struct apply_state *state,
+ const char *line,
+ int len,
+ unsigned int size,
+ struct patch *patch)
{
unsigned long offset;
@@ -1276,20 +1346,20 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
* or removing or adding empty files), so we get
* the default name from the header.
*/
- patch->def_name = git_header_name(line, len);
- if (patch->def_name && root) {
- char *s = xstrfmt("%s%s", root, patch->def_name);
+ patch->def_name = git_header_name(state, line, len);
+ if (patch->def_name && state->root.len) {
+ char *s = xstrfmt("%s%s", state->root.buf, patch->def_name);
free(patch->def_name);
patch->def_name = s;
}
line += len;
size -= len;
- linenr++;
- for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
+ state->linenr++;
+ for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {
static const struct opentry {
const char *str;
- int (*fn)(const char *, struct patch *);
+ int (*fn)(struct apply_state *, const char *, struct patch *);
} optable[] = {
{ "@@ -", gitdiff_hdrend },
{ "--- ", gitdiff_oldname },
@@ -1319,7 +1389,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
int oplen = strlen(p->str);
if (len < oplen || memcmp(p->str, line, oplen))
continue;
- if (p->fn(line + oplen, patch) < 0)
+ if (p->fn(state, line + oplen, patch) < 0)
return offset;
break;
}
@@ -1444,7 +1514,11 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra
return offset;
}
-static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
+static int find_header(struct apply_state *state,
+ const char *line,
+ unsigned long size,
+ int *hdrsize,
+ struct patch *patch)
{
unsigned long offset, len;
@@ -1453,7 +1527,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
patch->is_new = patch->is_delete = -1;
patch->old_mode = patch->new_mode = 0;
patch->old_name = patch->new_name = NULL;
- for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
+ for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) {
unsigned long nextlen;
len = linelen(line, size);
@@ -1474,7 +1548,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
if (parse_fragment_header(line, len, &dummy) < 0)
continue;
die(_("patch fragment without header at line %d: %.*s"),
- linenr, (int)len-1, line);
+ state->linenr, (int)len-1, line);
}
if (size < len + 6)
@@ -1485,7 +1559,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
* or mode change, so we handle that specially
*/
if (!memcmp("diff --git ", line, 11)) {
- int git_hdr_len = parse_git_header(line, len, size, patch);
+ int git_hdr_len = parse_git_header(state, line, len, size, patch);
if (git_hdr_len <= len)
continue;
if (!patch->old_name && !patch->new_name) {
@@ -1494,14 +1568,14 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
"%d leading pathname component (line %d)",
"git diff header lacks filename information when removing "
"%d leading pathname components (line %d)",
- p_value),
- p_value, linenr);
+ state->p_value),
+ state->p_value, state->linenr);
patch->old_name = xstrdup(patch->def_name);
patch->new_name = xstrdup(patch->def_name);
}
if (!patch->is_delete && !patch->new_name)
die("git diff header lacks filename information "
- "(line %d)", linenr);
+ "(line %d)", state->linenr);
patch->is_toplevel_relative = 1;
*hdrsize = git_hdr_len;
return offset;
@@ -1521,37 +1595,44 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
continue;
/* Ok, we'll consider it a patch */
- parse_traditional_patch(line, line+len, patch);
+ parse_traditional_patch(state, line, line+len, patch);
*hdrsize = len + nextlen;
- linenr += 2;
+ state->linenr += 2;
return offset;
}
return -1;
}
-static void record_ws_error(unsigned result, const char *line, int len, int linenr)
+static void record_ws_error(struct apply_state *state,
+ unsigned result,
+ const char *line,
+ int len,
+ int linenr)
{
char *err;
if (!result)
return;
- whitespace_error++;
- if (squelch_whitespace_errors &&
- squelch_whitespace_errors < whitespace_error)
+ state->whitespace_error++;
+ if (state->squelch_whitespace_errors &&
+ state->squelch_whitespace_errors < state->whitespace_error)
return;
err = whitespace_error_string(result);
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
- patch_input_file, linenr, err, len, line);
+ state->patch_input_file, linenr, err, len, line);
free(err);
}
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void check_whitespace(struct apply_state *state,
+ const char *line,
+ int len,
+ unsigned ws_rule)
{
unsigned result = ws_check(line + 1, len - 1, ws_rule);
- record_ws_error(result, line + 1, len - 2, linenr);
+ record_ws_error(state, result, line + 1, len - 2, state->linenr);
}
/*
@@ -1560,8 +1641,11 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule)
* between a "---" that is part of a patch, and a "---" that starts
* the next patch is to look at the line counts..
*/
-static int parse_fragment(const char *line, unsigned long size,
- struct patch *patch, struct fragment *fragment)
+static int parse_fragment(struct apply_state *state,
+ const char *line,
+ unsigned long size,
+ struct patch *patch,
+ struct fragment *fragment)
{
int added, deleted;
int len = linelen(line, size), offset;
@@ -1581,11 +1665,11 @@ static int parse_fragment(const char *line, unsigned long size,
/* Parse the thing.. */
line += len;
size -= len;
- linenr++;
+ state->linenr++;
added = deleted = 0;
for (offset = len;
0 < size;
- offset += len, size -= len, line += len, linenr++) {
+ offset += len, size -= len, line += len, state->linenr++) {
if (!oldlines && !newlines)
break;
len = linelen(line, size);
@@ -1601,22 +1685,22 @@ static int parse_fragment(const char *line, unsigned long size,
if (!deleted && !added)
leading++;
trailing++;
- if (!apply_in_reverse &&
- ws_error_action == correct_ws_error)
- check_whitespace(line, len, patch->ws_rule);
+ if (!state->apply_in_reverse &&
+ state->ws_error_action == correct_ws_error)
+ check_whitespace(state, line, len, patch->ws_rule);
break;
case '-':
- if (apply_in_reverse &&
- ws_error_action != nowarn_ws_error)
- check_whitespace(line, len, patch->ws_rule);
+ if (state->apply_in_reverse &&
+ state->ws_error_action != nowarn_ws_error)
+ check_whitespace(state, line, len, patch->ws_rule);
deleted++;
oldlines--;
trailing = 0;
break;
case '+':
- if (!apply_in_reverse &&
- ws_error_action != nowarn_ws_error)
- check_whitespace(line, len, patch->ws_rule);
+ if (!state->apply_in_reverse &&
+ state->ws_error_action != nowarn_ws_error)
+ check_whitespace(state, line, len, patch->ws_rule);
added++;
newlines--;
trailing = 0;
@@ -1638,6 +1722,9 @@ static int parse_fragment(const char *line, unsigned long size,
}
if (oldlines || newlines)
return -1;
+ if (!deleted && !added)
+ return -1;
+
fragment->leading = leading;
fragment->trailing = trailing;
@@ -1667,7 +1754,10 @@ static int parse_fragment(const char *line, unsigned long size,
* The (fragment->patch, fragment->size) pair points into the memory given
* by the caller, not a copy, when we return.
*/
-static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
+static int parse_single_patch(struct apply_state *state,
+ const char *line,
+ unsigned long size,
+ struct patch *patch)
{
unsigned long offset = 0;
unsigned long oldlines = 0, newlines = 0, context = 0;
@@ -1678,10 +1768,10 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch
int len;
fragment = xcalloc(1, sizeof(*fragment));
- fragment->linenr = linenr;
- len = parse_fragment(line, size, patch, fragment);
+ fragment->linenr = state->linenr;
+ len = parse_fragment(state, line, size, patch, fragment);
if (len <= 0)
- die(_("corrupt patch at line %d"), linenr);
+ die(_("corrupt patch at line %d"), state->linenr);
fragment->patch = line;
fragment->size = len;
oldlines += fragment->oldlines;
@@ -1767,7 +1857,8 @@ static char *inflate_it(const void *data, unsigned long size,
* points at an allocated memory that the caller must free, so
* it is marked as "->free_patch = 1".
*/
-static struct fragment *parse_binary_hunk(char **buf_p,
+static struct fragment *parse_binary_hunk(struct apply_state *state,
+ char **buf_p,
unsigned long *sz_p,
int *status_p,
int *used_p)
@@ -1809,13 +1900,13 @@ static struct fragment *parse_binary_hunk(char **buf_p,
else
return NULL;
- linenr++;
+ state->linenr++;
buffer += llen;
while (1) {
int byte_length, max_byte_length, newsize;
llen = linelen(buffer, size);
used += llen;
- linenr++;
+ state->linenr++;
if (llen == 1) {
/* consume the blank line */
buffer++;
@@ -1869,11 +1960,19 @@ static struct fragment *parse_binary_hunk(char **buf_p,
free(data);
*status_p = -1;
error(_("corrupt binary patch at line %d: %.*s"),
- linenr-1, llen-1, buffer);
+ state->linenr-1, llen-1, buffer);
return NULL;
}
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+/*
+ * Returns:
+ * -1 in case of error,
+ * the length of the parsed binary patch otherwise
+ */
+static int parse_binary(struct apply_state *state,
+ char *buffer,
+ unsigned long size,
+ struct patch *patch)
{
/*
* We have read "GIT binary patch\n"; what follows is a line
@@ -1894,15 +1993,15 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
int status;
int used, used_1;
- forward = parse_binary_hunk(&buffer, &size, &status, &used);
+ forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
if (!forward && !status)
/* there has to be one hunk (forward hunk) */
- return error(_("unrecognized binary patch at line %d"), linenr-1);
+ return error(_("unrecognized binary patch at line %d"), state->linenr-1);
if (status)
/* otherwise we already gave an error message */
return status;
- reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+ reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1);
if (reverse)
used += used_1;
else if (status) {
@@ -1920,53 +2019,53 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
return used;
}
-static void prefix_one(char **name)
+static void prefix_one(struct apply_state *state, char **name)
{
char *old_name = *name;
if (!old_name)
return;
- *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+ *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
free(old_name);
}
-static void prefix_patch(struct patch *p)
+static void prefix_patch(struct apply_state *state, struct patch *p)
{
- if (!prefix || p->is_toplevel_relative)
+ if (!state->prefix || p->is_toplevel_relative)
return;
- prefix_one(&p->new_name);
- prefix_one(&p->old_name);
+ prefix_one(state, &p->new_name);
+ prefix_one(state, &p->old_name);
}
/*
* include/exclude
*/
-static struct string_list limit_by_name;
-static int has_include;
-static void add_name_limit(const char *name, int exclude)
+static void add_name_limit(struct apply_state *state,
+ const char *name,
+ int exclude)
{
struct string_list_item *it;
- it = string_list_append(&limit_by_name, name);
+ it = string_list_append(&state->limit_by_name, name);
it->util = exclude ? NULL : (void *) 1;
}
-static int use_patch(struct patch *p)
+static int use_patch(struct apply_state *state, struct patch *p)
{
const char *pathname = p->new_name ? p->new_name : p->old_name;
int i;
/* Paths outside are not touched regardless of "--include" */
- if (0 < prefix_length) {
+ if (0 < state->prefix_length) {
int pathlen = strlen(pathname);
- if (pathlen <= prefix_length ||
- memcmp(prefix, pathname, prefix_length))
+ if (pathlen <= state->prefix_length ||
+ memcmp(state->prefix, pathname, state->prefix_length))
return 0;
}
/* See if it matches any of exclude/include rule */
- for (i = 0; i < limit_by_name.nr; i++) {
- struct string_list_item *it = &limit_by_name.items[i];
+ for (i = 0; i < state->limit_by_name.nr; i++) {
+ struct string_list_item *it = &state->limit_by_name.items[i];
if (!wildmatch(it->string, pathname, 0, NULL))
return (it->util != NULL);
}
@@ -1976,7 +2075,7 @@ static int use_patch(struct patch *p)
* not used. Otherwise, we saw bunch of exclude rules (or none)
* and such a path is used.
*/
- return !has_include;
+ return !state->has_include;
}
@@ -1987,25 +2086,27 @@ static int use_patch(struct patch *p)
* Return the number of bytes consumed, so that the caller can call us
* again for the next patch.
*/
-static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
+static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch)
{
int hdrsize, patchsize;
- int offset = find_header(buffer, size, &hdrsize, patch);
+ int offset = find_header(state, buffer, size, &hdrsize, patch);
if (offset < 0)
return offset;
- prefix_patch(patch);
+ prefix_patch(state, patch);
- if (!use_patch(patch))
+ if (!use_patch(state, patch))
patch->ws_rule = 0;
else
patch->ws_rule = whitespace_rule(patch->new_name
? patch->new_name
: patch->old_name);
- patchsize = parse_single_patch(buffer + offset + hdrsize,
- size - offset - hdrsize, patch);
+ patchsize = parse_single_patch(state,
+ buffer + offset + hdrsize,
+ size - offset - hdrsize,
+ patch);
if (!patchsize) {
static const char git_binary[] = "GIT binary patch\n";
@@ -2015,9 +2116,11 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
if (llen == sizeof(git_binary) - 1 &&
!memcmp(git_binary, buffer + hd, llen)) {
int used;
- linenr++;
- used = parse_binary(buffer + hd + llen,
+ state->linenr++;
+ used = parse_binary(state, buffer + hd + llen,
size - hd - llen, patch);
+ if (used < 0)
+ return -1;
if (used)
patchsize = used + llen;
else
@@ -2034,7 +2137,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
int len = strlen(binhdr[i]);
if (len < size - hd &&
!memcmp(binhdr[i], buffer + hd, len)) {
- linenr++;
+ state->linenr++;
patch->is_binary = 1;
patchsize = llen;
break;
@@ -2046,9 +2149,9 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
* without metadata change. A binary patch appears
* empty to us here.
*/
- if ((apply || check) &&
+ if ((state->apply || state->check) &&
(!patch->is_binary && !metadata_changes(patch)))
- die(_("patch with only garbage at line %d"), linenr);
+ die(_("patch with only garbage at line %d"), state->linenr);
}
return offset + hdrsize + patchsize;
@@ -2086,7 +2189,7 @@ static const char pluses[] =
static const char minuses[]=
"----------------------------------------------------------------------";
-static void show_stats(struct patch *patch)
+static void show_stats(struct apply_state *state, struct patch *patch)
{
struct strbuf qname = STRBUF_INIT;
char *cp = patch->new_name ? patch->new_name : patch->old_name;
@@ -2097,7 +2200,7 @@ static void show_stats(struct patch *patch)
/*
* "scale" the filename
*/
- max = max_len;
+ max = state->max_len;
if (max > 50)
max = 50;
@@ -2120,13 +2223,13 @@ static void show_stats(struct patch *patch)
/*
* scale the add/delete
*/
- max = max + max_change > 70 ? 70 - max : max_change;
+ max = max + state->max_change > 70 ? 70 - max : state->max_change;
add = patch->lines_added;
del = patch->lines_deleted;
- if (max_change > 0) {
- int total = ((add + del) * max + max_change / 2) / max_change;
- add = (add * max + max_change / 2) / max_change;
+ if (state->max_change > 0) {
+ int total = ((add + del) * max + state->max_change / 2) / state->max_change;
+ add = (add * max + state->max_change / 2) / state->max_change;
del = total - add;
}
printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
@@ -2197,17 +2300,17 @@ static void update_pre_post_images(struct image *preimage,
fixed = preimage->buf;
for (i = reduced = ctx = 0; i < postimage->nr; i++) {
- size_t len = postimage->line[i].len;
+ size_t l_len = postimage->line[i].len;
if (!(postimage->line[i].flag & LINE_COMMON)) {
/* an added line -- no counterparts in preimage */
- memmove(new, old, len);
- old += len;
- new += len;
+ memmove(new, old, l_len);
+ old += l_len;
+ new += l_len;
continue;
}
/* a common context -- skip it in the original postimage */
- old += len;
+ old += l_len;
/* and find the corresponding one in the fixed preimage */
while (ctx < preimage->nr &&
@@ -2226,11 +2329,11 @@ static void update_pre_post_images(struct image *preimage,
}
/* and copy it in, while fixing the line length */
- len = preimage->line[ctx].len;
- memcpy(new, fixed, len);
- new += len;
- fixed += len;
- postimage->line[i].len = len;
+ l_len = preimage->line[ctx].len;
+ memcpy(new, fixed, l_len);
+ new += l_len;
+ fixed += l_len;
+ postimage->line[i].len = l_len;
ctx++;
}
@@ -2245,7 +2348,76 @@ static void update_pre_post_images(struct image *preimage,
postimage->nr -= reduced;
}
-static int match_fragment(struct image *img,
+static int line_by_line_fuzzy_match(struct image *img,
+ struct image *preimage,
+ struct image *postimage,
+ unsigned long try,
+ int try_lno,
+ int preimage_limit)
+{
+ int i;
+ size_t imgoff = 0;
+ size_t preoff = 0;
+ size_t postlen = postimage->len;
+ size_t extra_chars;
+ char *buf;
+ char *preimage_eof;
+ char *preimage_end;
+ struct strbuf fixed;
+ char *fixed_buf;
+ size_t fixed_len;
+
+ for (i = 0; i < preimage_limit; i++) {
+ size_t prelen = preimage->line[i].len;
+ size_t imglen = img->line[try_lno+i].len;
+
+ if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+ preimage->buf + preoff, prelen))
+ return 0;
+ if (preimage->line[i].flag & LINE_COMMON)
+ postlen += imglen - prelen;
+ imgoff += imglen;
+ preoff += prelen;
+ }
+
+ /*
+ * Ok, the preimage matches with whitespace fuzz.
+ *
+ * imgoff now holds the true length of the target that
+ * matches the preimage before the end of the file.
+ *
+ * Count the number of characters in the preimage that fall
+ * beyond the end of the file and make sure that all of them
+ * are whitespace characters. (This can only happen if
+ * we are removing blank lines at the end of the file.)
+ */
+ buf = preimage_eof = preimage->buf + preoff;
+ for ( ; i < preimage->nr; i++)
+ preoff += preimage->line[i].len;
+ preimage_end = preimage->buf + preoff;
+ for ( ; buf < preimage_end; buf++)
+ if (!isspace(*buf))
+ return 0;
+
+ /*
+ * Update the preimage and the common postimage context
+ * lines to use the same whitespace as the target.
+ * If whitespace is missing in the target (i.e.
+ * if the preimage extends beyond the end of the file),
+ * use the whitespace from the preimage.
+ */
+ extra_chars = preimage_end - preimage_eof;
+ strbuf_init(&fixed, imgoff + extra_chars);
+ strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, preimage_eof, extra_chars);
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
+ update_pre_post_images(preimage, postimage,
+ fixed_buf, fixed_len, postlen);
+ return 1;
+}
+
+static int match_fragment(struct apply_state *state,
+ struct image *img,
struct image *preimage,
struct image *postimage,
unsigned long try,
@@ -2266,7 +2438,7 @@ static int match_fragment(struct image *img,
preimage_limit = preimage->nr;
if (match_end && (preimage->nr + try_lno != img->nr))
return 0;
- } else if (ws_error_action == correct_ws_error &&
+ } else if (state->ws_error_action == correct_ws_error &&
(ws_rule & WS_BLANK_AT_EOF)) {
/*
* This hunk extends beyond the end of img, and we are
@@ -2334,63 +2506,11 @@ static int match_fragment(struct image *img,
* fuzzy matching. We collect all the line length information because
* we need it to adjust whitespace if we match.
*/
- if (ws_ignore_action == ignore_ws_change) {
- size_t imgoff = 0;
- size_t preoff = 0;
- size_t postlen = postimage->len;
- size_t extra_chars;
- char *preimage_eof;
- char *preimage_end;
- for (i = 0; i < preimage_limit; i++) {
- size_t prelen = preimage->line[i].len;
- size_t imglen = img->line[try_lno+i].len;
-
- if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
- preimage->buf + preoff, prelen))
- return 0;
- if (preimage->line[i].flag & LINE_COMMON)
- postlen += imglen - prelen;
- imgoff += imglen;
- preoff += prelen;
- }
+ if (state->ws_ignore_action == ignore_ws_change)
+ return line_by_line_fuzzy_match(img, preimage, postimage,
+ try, try_lno, preimage_limit);
- /*
- * Ok, the preimage matches with whitespace fuzz.
- *
- * imgoff now holds the true length of the target that
- * matches the preimage before the end of the file.
- *
- * Count the number of characters in the preimage that fall
- * beyond the end of the file and make sure that all of them
- * are whitespace characters. (This can only happen if
- * we are removing blank lines at the end of the file.)
- */
- buf = preimage_eof = preimage->buf + preoff;
- for ( ; i < preimage->nr; i++)
- preoff += preimage->line[i].len;
- preimage_end = preimage->buf + preoff;
- for ( ; buf < preimage_end; buf++)
- if (!isspace(*buf))
- return 0;
-
- /*
- * Update the preimage and the common postimage context
- * lines to use the same whitespace as the target.
- * If whitespace is missing in the target (i.e.
- * if the preimage extends beyond the end of the file),
- * use the whitespace from the preimage.
- */
- extra_chars = preimage_end - preimage_eof;
- strbuf_init(&fixed, imgoff + extra_chars);
- strbuf_add(&fixed, img->buf + try, imgoff);
- strbuf_add(&fixed, preimage_eof, extra_chars);
- fixed_buf = strbuf_detach(&fixed, &fixed_len);
- update_pre_post_images(preimage, postimage,
- fixed_buf, fixed_len, postlen);
- return 1;
- }
-
- if (ws_error_action != correct_ws_error)
+ if (state->ws_error_action != correct_ws_error)
return 0;
/*
@@ -2502,7 +2622,8 @@ static int match_fragment(struct image *img,
return 0;
}
-static int find_pos(struct image *img,
+static int find_pos(struct apply_state *state,
+ struct image *img,
struct image *preimage,
struct image *postimage,
int line,
@@ -2546,7 +2667,7 @@ static int find_pos(struct image *img,
try_lno = line;
for (i = 0; ; i++) {
- if (match_fragment(img, preimage, postimage,
+ if (match_fragment(state, img, preimage, postimage,
try, try_lno, ws_rule,
match_beginning, match_end))
return try_lno;
@@ -2597,7 +2718,8 @@ static void remove_last_line(struct image *img)
* apply at applied_pos (counts in line numbers) in "img".
* Update "img" to remove "preimage" and replace it with "postimage".
*/
-static void update_image(struct image *img,
+static void update_image(struct apply_state *state,
+ struct image *img,
int applied_pos,
struct image *preimage,
struct image *postimage)
@@ -2633,7 +2755,7 @@ static void update_image(struct image *img,
insert_count = postimage->len;
/* Adjust the contents */
- result = xmalloc(img->len + insert_count - remove_count + 1);
+ result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1));
memcpy(result, img->buf, applied_at);
memcpy(result + applied_at, postimage->buf, postimage->len);
memcpy(result + applied_at + postimage->len,
@@ -2662,7 +2784,7 @@ static void update_image(struct image *img,
memcpy(img->line + applied_pos,
postimage->line,
postimage->nr * sizeof(*img->line));
- if (!allow_overlap)
+ if (!state->allow_overlap)
for (i = 0; i < postimage->nr; i++)
img->line[applied_pos + i].flag |= LINE_PATCHED;
img->nr = nr;
@@ -2673,7 +2795,8 @@ static void update_image(struct image *img,
* postimage) for the hunk. Find lines that match "preimage" in "img" and
* replace the part of "img" with "postimage" text.
*/
-static int apply_one_fragment(struct image *img, struct fragment *frag,
+static int apply_one_fragment(struct apply_state *state,
+ struct image *img, struct fragment *frag,
int inaccurate_eof, unsigned ws_rule,
int nth_fragment)
{
@@ -2718,7 +2841,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (len < size && patch[len] == '\\')
plen--;
first = *patch;
- if (apply_in_reverse) {
+ if (state->apply_in_reverse) {
if (first == '-')
first = '+';
else if (first == '+')
@@ -2751,17 +2874,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* Fall-through for ' ' */
case '+':
/* --no-add does not add new lines */
- if (first == '+' && no_add)
+ if (first == '+' && state->no_add)
break;
start = newlines.len;
if (first != '+' ||
- !whitespace_error ||
- ws_error_action != correct_ws_error) {
+ !state->whitespace_error ||
+ state->ws_error_action != correct_ws_error) {
strbuf_add(&newlines, patch + 1, plen);
}
else {
- ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+ ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws);
}
add_line_info(&postimage, newlines.buf + start, newlines.len - start,
(first == '+' ? 0 : LINE_COMMON));
@@ -2774,7 +2897,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* Ignore it, we already handled it */
break;
default:
- if (apply_verbosely)
+ if (state->apply_verbosely)
error(_("invalid start of line: '%c'"), first);
applied_pos = -1;
goto out;
@@ -2815,7 +2938,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
* without leading context must match at the beginning.
*/
match_beginning = (!frag->oldpos ||
- (frag->oldpos == 1 && !unidiff_zero));
+ (frag->oldpos == 1 && !state->unidiff_zero));
/*
* A hunk without trailing lines must match at the end.
@@ -2823,7 +2946,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
* from the lack of trailing lines if the patch was generated
* with unidiff without any context.
*/
- match_end = !unidiff_zero && !trailing;
+ match_end = !state->unidiff_zero && !trailing;
pos = frag->newpos ? (frag->newpos - 1) : 0;
preimage.buf = oldlines;
@@ -2835,14 +2958,14 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
for (;;) {
- applied_pos = find_pos(img, &preimage, &postimage, pos,
+ applied_pos = find_pos(state, img, &preimage, &postimage, pos,
ws_rule, match_beginning, match_end);
if (applied_pos >= 0)
break;
/* Am I at my context limits? */
- if ((leading <= p_context) && (trailing <= p_context))
+ if ((leading <= state->p_context) && (trailing <= state->p_context))
break;
if (match_beginning || match_end) {
match_beginning = match_end = 0;
@@ -2871,10 +2994,10 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (new_blank_lines_at_end &&
preimage.nr + applied_pos >= img->nr &&
(ws_rule & WS_BLANK_AT_EOF) &&
- ws_error_action != nowarn_ws_error) {
- record_ws_error(WS_BLANK_AT_EOF, "+", 1,
+ state->ws_error_action != nowarn_ws_error) {
+ record_ws_error(state, WS_BLANK_AT_EOF, "+", 1,
found_new_blank_lines_at_end);
- if (ws_error_action == correct_ws_error) {
+ if (state->ws_error_action == correct_ws_error) {
while (new_blank_lines_at_end--)
remove_last_line(&postimage);
}
@@ -2885,13 +3008,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
* apply_patch->check_patch_list->check_patch->
* apply_data->apply_fragments->apply_one_fragment
*/
- if (ws_error_action == die_on_ws_error)
- apply = 0;
+ if (state->ws_error_action == die_on_ws_error)
+ state->apply = 0;
}
- if (apply_verbosely && applied_pos != pos) {
+ if (state->apply_verbosely && applied_pos != pos) {
int offset = applied_pos - pos;
- if (apply_in_reverse)
+ if (state->apply_in_reverse)
offset = 0 - offset;
fprintf_ln(stderr,
Q_("Hunk #%d succeeded at %d (offset %d line).",
@@ -2909,9 +3032,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
" to apply fragment at %d"),
leading, trailing, applied_pos+1);
- update_image(img, applied_pos, &preimage, &postimage);
+ update_image(state, img, applied_pos, &preimage, &postimage);
} else {
- if (apply_verbosely)
+ if (state->apply_verbosely)
error(_("while searching for:\n%.*s"),
(int)(old - oldlines), oldlines);
}
@@ -2925,7 +3048,9 @@ out:
return (applied_pos < 0);
}
-static int apply_binary_fragment(struct image *img, struct patch *patch)
+static int apply_binary_fragment(struct apply_state *state,
+ struct image *img,
+ struct patch *patch)
{
struct fragment *fragment = patch->fragments;
unsigned long len;
@@ -2938,7 +3063,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
patch->old_name);
/* Binary patch is irreversible without the optional second hunk */
- if (apply_in_reverse) {
+ if (state->apply_in_reverse) {
if (!fragment->next)
return error("cannot reverse-apply a binary patch "
"without the reverse hunk to '%s'",
@@ -2971,7 +3096,9 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
* but the preimage prepared by the caller in "img" is freed here
* or in the helper function apply_binary_fragment() this calls.
*/
-static int apply_binary(struct image *img, struct patch *patch)
+static int apply_binary(struct apply_state *state,
+ struct image *img,
+ struct patch *patch)
{
const char *name = patch->old_name ? patch->old_name : patch->new_name;
unsigned char sha1[20];
@@ -3032,7 +3159,7 @@ static int apply_binary(struct image *img, struct patch *patch)
* apply the patch data to it, which is stored
* in the patch->fragments->{patch,size}.
*/
- if (apply_binary_fragment(img, patch))
+ if (apply_binary_fragment(state, img, patch))
return error(_("binary patch does not apply to '%s'"),
name);
@@ -3046,7 +3173,7 @@ static int apply_binary(struct image *img, struct patch *patch)
return 0;
}
-static int apply_fragments(struct image *img, struct patch *patch)
+static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch)
{
struct fragment *frag = patch->fragments;
const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -3055,13 +3182,13 @@ static int apply_fragments(struct image *img, struct patch *patch)
int nth = 0;
if (patch->is_binary)
- return apply_binary(img, patch);
+ return apply_binary(state, img, patch);
while (frag) {
nth++;
- if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
+ if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) {
error(_("patch failed: %s:%ld"), name, frag->oldpos);
- if (!apply_with_reject)
+ if (!state->apply_with_reject)
return -1;
frag->rejected = 1;
}
@@ -3096,14 +3223,14 @@ static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf
return read_blob_object(buf, ce->sha1, ce->ce_mode);
}
-static struct patch *in_fn_table(const char *name)
+static struct patch *in_fn_table(struct apply_state *state, const char *name)
{
struct string_list_item *item;
if (name == NULL)
return NULL;
- item = string_list_lookup(&fn_table, name);
+ item = string_list_lookup(&state->fn_table, name);
if (item != NULL)
return (struct patch *)item->util;
@@ -3135,7 +3262,7 @@ static int was_deleted(struct patch *patch)
return patch == PATH_WAS_DELETED;
}
-static void add_to_fn_table(struct patch *patch)
+static void add_to_fn_table(struct apply_state *state, struct patch *patch)
{
struct string_list_item *item;
@@ -3145,7 +3272,7 @@ static void add_to_fn_table(struct patch *patch)
* file creations and copies
*/
if (patch->new_name != NULL) {
- item = string_list_insert(&fn_table, patch->new_name);
+ item = string_list_insert(&state->fn_table, patch->new_name);
item->util = patch;
}
@@ -3154,12 +3281,12 @@ static void add_to_fn_table(struct patch *patch)
* later chunks shouldn't patch old names
*/
if ((patch->new_name == NULL) || (patch->is_rename)) {
- item = string_list_insert(&fn_table, patch->old_name);
+ item = string_list_insert(&state->fn_table, patch->old_name);
item->util = PATH_WAS_DELETED;
}
}
-static void prepare_fn_table(struct patch *patch)
+static void prepare_fn_table(struct apply_state *state, struct patch *patch)
{
/*
* store information about incoming file deletion
@@ -3167,7 +3294,7 @@ static void prepare_fn_table(struct patch *patch)
while (patch) {
if ((patch->new_name == NULL) || (patch->is_rename)) {
struct string_list_item *item;
- item = string_list_insert(&fn_table, patch->old_name);
+ item = string_list_insert(&state->fn_table, patch->old_name);
item->util = PATH_TO_BE_DELETED;
}
patch = patch->next;
@@ -3188,7 +3315,9 @@ static int checkout_target(struct index_state *istate,
return 0;
}
-static struct patch *previous_patch(struct patch *patch, int *gone)
+static struct patch *previous_patch(struct apply_state *state,
+ struct patch *patch,
+ int *gone)
{
struct patch *previous;
@@ -3196,7 +3325,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone)
if (patch->is_copy || patch->is_rename)
return NULL; /* "git" patches do not depend on the order */
- previous = in_fn_table(patch->old_name);
+ previous = in_fn_table(state, patch->old_name);
if (!previous)
return NULL;
@@ -3221,15 +3350,16 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)
#define SUBMODULE_PATCH_WITHOUT_INDEX 1
-static int load_patch_target(struct strbuf *buf,
+static int load_patch_target(struct apply_state *state,
+ struct strbuf *buf,
const struct cache_entry *ce,
struct stat *st,
const char *name,
unsigned expected_mode)
{
- if (cached || check_index) {
+ if (state->cached || state->check_index) {
if (read_file_or_gitlink(ce, buf))
- return error(_("read of %s failed"), name);
+ return error(_("failed to read %s"), name);
} else if (name) {
if (S_ISGITLINK(expected_mode)) {
if (ce)
@@ -3240,7 +3370,7 @@ static int load_patch_target(struct strbuf *buf,
return error(_("reading from '%s' beyond a symbolic link"), name);
} else {
if (read_old_data(st, name, buf))
- return error(_("read of %s failed"), name);
+ return error(_("failed to read %s"), name);
}
}
return 0;
@@ -3253,7 +3383,8 @@ static int load_patch_target(struct strbuf *buf,
* applying a non-git patch that incrementally updates the tree,
* we read from the result of a previous diff.
*/
-static int load_preimage(struct image *image,
+static int load_preimage(struct apply_state *state,
+ struct image *image,
struct patch *patch, struct stat *st,
const struct cache_entry *ce)
{
@@ -3263,7 +3394,7 @@ static int load_preimage(struct image *image,
struct patch *previous;
int status;
- previous = previous_patch(patch, &status);
+ previous = previous_patch(state, patch, &status);
if (status)
return error(_("path %s has been renamed/deleted"),
patch->old_name);
@@ -3271,7 +3402,7 @@ static int load_preimage(struct image *image,
/* We have a patched copy in memory; use that. */
strbuf_add(&buf, previous->result, previous->resultsize);
} else {
- status = load_patch_target(&buf, ce, st,
+ status = load_patch_target(state, &buf, ce, st,
patch->old_name, patch->old_mode);
if (status < 0)
return status;
@@ -3285,7 +3416,7 @@ static int load_preimage(struct image *image,
free_fragment_list(patch->fragments);
patch->fragments = NULL;
} else if (status) {
- return error(_("read of %s failed"), patch->old_name);
+ return error(_("failed to read %s"), patch->old_name);
}
}
@@ -3330,7 +3461,9 @@ static int three_way_merge(struct image *image,
* the current contents of the new_name. In no cases other than that
* this function will be called.
*/
-static int load_current(struct image *image, struct patch *patch)
+static int load_current(struct apply_state *state,
+ struct image *image,
+ struct patch *patch)
{
struct strbuf buf = STRBUF_INIT;
int status, pos;
@@ -3357,7 +3490,7 @@ static int load_current(struct image *image, struct patch *patch)
if (verify_index_match(ce, &st))
return error(_("%s: does not match index"), name);
- status = load_patch_target(&buf, ce, &st, name, mode);
+ status = load_patch_target(state, &buf, ce, &st, name, mode);
if (status < 0)
return status;
else if (status)
@@ -3367,8 +3500,11 @@ static int load_current(struct image *image, struct patch *patch)
return 0;
}
-static int try_threeway(struct image *image, struct patch *patch,
- struct stat *st, const struct cache_entry *ce)
+static int try_threeway(struct apply_state *state,
+ struct image *image,
+ struct patch *patch,
+ struct stat *st,
+ const struct cache_entry *ce)
{
unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
struct strbuf buf = STRBUF_INIT;
@@ -3394,7 +3530,7 @@ static int try_threeway(struct image *image, struct patch *patch,
img = strbuf_detach(&buf, &len);
prepare_image(&tmp_image, img, len, 1);
/* Apply the patch to get the post image */
- if (apply_fragments(&tmp_image, patch) < 0) {
+ if (apply_fragments(state, &tmp_image, patch) < 0) {
clear_image(&tmp_image);
return -1;
}
@@ -3404,11 +3540,11 @@ static int try_threeway(struct image *image, struct patch *patch,
/* our_sha1[] is ours */
if (patch->is_new) {
- if (load_current(&tmp_image, patch))
+ if (load_current(state, &tmp_image, patch))
return error("cannot read the current contents of '%s'",
patch->new_name);
} else {
- if (load_preimage(&tmp_image, patch, st, ce))
+ if (load_preimage(state, &tmp_image, patch, st, ce))
return error("cannot read the current contents of '%s'",
patch->old_name);
}
@@ -3426,11 +3562,11 @@ static int try_threeway(struct image *image, struct patch *patch,
if (status) {
patch->conflicted_threeway = 1;
if (patch->is_new)
- hashclr(patch->threeway_stage[0]);
+ oidclr(&patch->threeway_stage[0]);
else
- hashcpy(patch->threeway_stage[0], pre_sha1);
- hashcpy(patch->threeway_stage[1], our_sha1);
- hashcpy(patch->threeway_stage[2], post_sha1);
+ hashcpy(patch->threeway_stage[0].hash, pre_sha1);
+ hashcpy(patch->threeway_stage[1].hash, our_sha1);
+ hashcpy(patch->threeway_stage[2].hash, post_sha1);
fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
} else {
fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
@@ -3438,22 +3574,23 @@ static int try_threeway(struct image *image, struct patch *patch,
return 0;
}
-static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
+static int apply_data(struct apply_state *state, struct patch *patch,
+ struct stat *st, const struct cache_entry *ce)
{
struct image image;
- if (load_preimage(&image, patch, st, ce) < 0)
+ if (load_preimage(state, &image, patch, st, ce) < 0)
return -1;
if (patch->direct_to_threeway ||
- apply_fragments(&image, patch) < 0) {
+ apply_fragments(state, &image, patch) < 0) {
/* Note: with --reject, apply_fragments() returns 0 */
- if (!threeway || try_threeway(&image, patch, st, ce) < 0)
+ if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0)
return -1;
}
patch->result = image.buf;
patch->resultsize = image.len;
- add_to_fn_table(patch);
+ add_to_fn_table(state, patch);
free(image.line_allocated);
if (0 < patch->is_delete && patch->resultsize)
@@ -3473,7 +3610,10 @@ static int apply_data(struct patch *patch, struct stat *st, const struct cache_e
* check_patch() separately makes sure (and errors out otherwise) that
* the path the patch creates does not exist in the current tree.
*/
-static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
+static int check_preimage(struct apply_state *state,
+ struct patch *patch,
+ struct cache_entry **ce,
+ struct stat *st)
{
const char *old_name = patch->old_name;
struct patch *previous = NULL;
@@ -3484,19 +3624,19 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
return 0;
assert(patch->is_new <= 0);
- previous = previous_patch(patch, &status);
+ previous = previous_patch(state, patch, &status);
if (status)
return error(_("path %s has been renamed/deleted"), old_name);
if (previous) {
st_mode = previous->new_mode;
- } else if (!cached) {
+ } else if (!state->cached) {
stat_ret = lstat(old_name, st);
if (stat_ret && errno != ENOENT)
return error(_("%s: %s"), old_name, strerror(errno));
}
- if (check_index && !previous) {
+ if (state->check_index && !previous) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0) {
if (patch->is_new < 0)
@@ -3508,9 +3648,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (checkout_target(&the_index, *ce, st))
return -1;
}
- if (!cached && verify_index_match(*ce, st))
+ if (!state->cached && verify_index_match(*ce, st))
return error(_("%s: does not match index"), old_name);
- if (cached)
+ if (state->cached)
st_mode = (*ce)->ce_mode;
} else if (stat_ret < 0) {
if (patch->is_new < 0)
@@ -3518,7 +3658,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
return error(_("%s: %s"), old_name, strerror(errno));
}
- if (!cached && !previous)
+ if (!state->cached && !previous)
st_mode = ce_mode_from_stat(*ce, st->st_mode);
if (patch->is_new < 0)
@@ -3546,15 +3686,17 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
#define EXISTS_IN_INDEX 1
#define EXISTS_IN_WORKTREE 2
-static int check_to_create(const char *new_name, int ok_if_exists)
+static int check_to_create(struct apply_state *state,
+ const char *new_name,
+ int ok_if_exists)
{
struct stat nst;
- if (check_index &&
+ if (state->check_index &&
cache_name_pos(new_name, strlen(new_name)) >= 0 &&
!ok_if_exists)
return EXISTS_IN_INDEX;
- if (cached)
+ if (state->cached)
return 0;
if (!lstat(new_name, &nst)) {
@@ -3577,56 +3719,46 @@ static int check_to_create(const char *new_name, int ok_if_exists)
return 0;
}
-/*
- * We need to keep track of how symlinks in the preimage are
- * manipulated by the patches. A patch to add a/b/c where a/b
- * is a symlink should not be allowed to affect the directory
- * the symlink points at, but if the same patch removes a/b,
- * it is perfectly fine, as the patch removes a/b to make room
- * to create a directory a/b so that a/b/c can be created.
- */
-static struct string_list symlink_changes;
-#define SYMLINK_GOES_AWAY 01
-#define SYMLINK_IN_RESULT 02
-
-static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
+static uintptr_t register_symlink_changes(struct apply_state *state,
+ const char *path,
+ uintptr_t what)
{
struct string_list_item *ent;
- ent = string_list_lookup(&symlink_changes, path);
+ ent = string_list_lookup(&state->symlink_changes, path);
if (!ent) {
- ent = string_list_insert(&symlink_changes, path);
+ ent = string_list_insert(&state->symlink_changes, path);
ent->util = (void *)0;
}
ent->util = (void *)(what | ((uintptr_t)ent->util));
return (uintptr_t)ent->util;
}
-static uintptr_t check_symlink_changes(const char *path)
+static uintptr_t check_symlink_changes(struct apply_state *state, const char *path)
{
struct string_list_item *ent;
- ent = string_list_lookup(&symlink_changes, path);
+ ent = string_list_lookup(&state->symlink_changes, path);
if (!ent)
return 0;
return (uintptr_t)ent->util;
}
-static void prepare_symlink_changes(struct patch *patch)
+static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
{
for ( ; patch; patch = patch->next) {
if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
(patch->is_rename || patch->is_delete))
/* the symlink at patch->old_name is removed */
- register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
+ register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY);
if (patch->new_name && S_ISLNK(patch->new_mode))
/* the symlink at patch->new_name is created or remains */
- register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
+ register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT);
}
}
-static int path_is_beyond_symlink_1(struct strbuf *name)
+static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name)
{
do {
unsigned int change;
@@ -3636,7 +3768,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
if (!name->len)
break;
name->buf[name->len] = '\0';
- change = check_symlink_changes(name->buf);
+ change = check_symlink_changes(state, name->buf);
if (change & SYMLINK_IN_RESULT)
return 1;
if (change & SYMLINK_GOES_AWAY)
@@ -3647,7 +3779,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
continue;
/* otherwise, check the preimage */
- if (check_index) {
+ if (state->check_index) {
struct cache_entry *ce;
ce = cache_file_exists(name->buf, name->len, ignore_case);
@@ -3662,14 +3794,14 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
return 0;
}
-static int path_is_beyond_symlink(const char *name_)
+static int path_is_beyond_symlink(struct apply_state *state, const char *name_)
{
int ret;
struct strbuf name = STRBUF_INIT;
assert(*name_ != '\0');
strbuf_addstr(&name, name_);
- ret = path_is_beyond_symlink_1(&name);
+ ret = path_is_beyond_symlink_1(state, &name);
strbuf_release(&name);
return ret;
@@ -3696,7 +3828,7 @@ static void die_on_unsafe_path(struct patch *patch)
* Check and apply the patch in-core; leave the result in patch->result
* for the caller to write it out to the final destination.
*/
-static int check_patch(struct patch *patch)
+static int check_patch(struct apply_state *state, struct patch *patch)
{
struct stat st;
const char *old_name = patch->old_name;
@@ -3709,7 +3841,7 @@ static int check_patch(struct patch *patch)
patch->rejected = 1; /* we will drop this after we succeed */
- status = check_preimage(patch, &ce, &st);
+ status = check_preimage(state, patch, &ce, &st);
if (status)
return status;
old_name = patch->old_name;
@@ -3728,7 +3860,7 @@ static int check_patch(struct patch *patch)
* B and rename from A to B is handled the same way by asking
* was_deleted().
*/
- if ((tpatch = in_fn_table(new_name)) &&
+ if ((tpatch = in_fn_table(state, new_name)) &&
(was_deleted(tpatch) || to_be_deleted(tpatch)))
ok_if_exists = 1;
else
@@ -3736,9 +3868,9 @@ static int check_patch(struct patch *patch)
if (new_name &&
((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
- int err = check_to_create(new_name, ok_if_exists);
+ int err = check_to_create(state, new_name, ok_if_exists);
- if (err && threeway) {
+ if (err && state->threeway) {
patch->direct_to_threeway = 1;
} else switch (err) {
case 0:
@@ -3779,7 +3911,7 @@ static int check_patch(struct patch *patch)
}
}
- if (!unsafe_paths)
+ if (!state->unsafe_paths)
die_on_unsafe_path(patch);
/*
@@ -3791,27 +3923,27 @@ static int check_patch(struct patch *patch)
* is not deposited to a path that is beyond a symbolic link
* here.
*/
- if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
+ if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name))
return error(_("affected file '%s' is beyond a symbolic link"),
patch->new_name);
- if (apply_data(patch, &st, ce) < 0)
+ if (apply_data(state, patch, &st, ce) < 0)
return error(_("%s: patch does not apply"), name);
patch->rejected = 0;
return 0;
}
-static int check_patch_list(struct patch *patch)
+static int check_patch_list(struct apply_state *state, struct patch *patch)
{
int err = 0;
- prepare_symlink_changes(patch);
- prepare_fn_table(patch);
+ prepare_symlink_changes(state, patch);
+ prepare_fn_table(state, patch);
while (patch) {
- if (apply_verbosely)
+ if (state->apply_verbosely)
say_patch_name(stderr,
_("Checking patch %s..."), patch);
- err |= check_patch(patch);
+ err |= check_patch(state, patch);
patch = patch->next;
}
return err;
@@ -3915,7 +4047,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
discard_index(&result);
}
-static void stat_patch_list(struct patch *patch)
+static void stat_patch_list(struct apply_state *state, struct patch *patch)
{
int files, adds, dels;
@@ -3923,13 +4055,14 @@ static void stat_patch_list(struct patch *patch)
files++;
adds += patch->lines_added;
dels += patch->lines_deleted;
- show_stats(patch);
+ show_stats(state, patch);
}
print_stat_summary(stdout, files, adds, dels);
}
-static void numstat_patch_list(struct patch *patch)
+static void numstat_patch_list(struct apply_state *state,
+ struct patch *patch)
{
for ( ; patch; patch = patch->next) {
const char *name;
@@ -3938,7 +4071,7 @@ static void numstat_patch_list(struct patch *patch)
printf("-\t-\t");
else
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
- write_name_quoted(name, stdout, line_termination);
+ write_name_quoted(name, stdout, state->line_termination);
}
}
@@ -4020,49 +4153,53 @@ static void summary_patch_list(struct patch *patch)
}
}
-static void patch_stats(struct patch *patch)
+static void patch_stats(struct apply_state *state, struct patch *patch)
{
int lines = patch->lines_added + patch->lines_deleted;
- if (lines > max_change)
- max_change = lines;
+ if (lines > state->max_change)
+ state->max_change = lines;
if (patch->old_name) {
int len = quote_c_style(patch->old_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->old_name);
- if (len > max_len)
- max_len = len;
+ if (len > state->max_len)
+ state->max_len = len;
}
if (patch->new_name) {
int len = quote_c_style(patch->new_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->new_name);
- if (len > max_len)
- max_len = len;
+ if (len > state->max_len)
+ state->max_len = len;
}
}
-static void remove_file(struct patch *patch, int rmdir_empty)
+static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
{
- if (update_index) {
+ if (state->update_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die(_("unable to remove %s from index"), patch->old_name);
}
- if (!cached) {
+ if (!state->cached) {
if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
remove_path(patch->old_name);
}
}
}
-static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
+static void add_index_file(struct apply_state *state,
+ const char *path,
+ unsigned mode,
+ void *buf,
+ unsigned long size)
{
struct stat st;
struct cache_entry *ce;
int namelen = strlen(path);
unsigned ce_size = cache_entry_size(namelen);
- if (!update_index)
+ if (!state->update_index)
return;
ce = xcalloc(1, ce_size);
@@ -4077,7 +4214,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
get_sha1_hex(s, ce->sha1))
die(_("corrupt patch for submodule %s"), path);
} else {
- if (!cached) {
+ if (!state->cached) {
if (lstat(path, &st) < 0)
die_errno(_("unable to stat newly created file '%s'"),
path);
@@ -4129,9 +4266,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
* which is true 99% of the time anyway. If they don't,
* we create them and try again.
*/
-static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
+static void create_one_file(struct apply_state *state,
+ char *path,
+ unsigned mode,
+ const char *buf,
+ unsigned long size)
{
- if (cached)
+ if (state->cached)
return;
if (!try_create_file(path, mode, buf, size))
return;
@@ -4172,13 +4313,14 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
die_errno(_("unable to write file '%s' mode %o"), path, mode);
}
-static void add_conflicted_stages_file(struct patch *patch)
+static void add_conflicted_stages_file(struct apply_state *state,
+ struct patch *patch)
{
int stage, namelen;
unsigned ce_size, mode;
struct cache_entry *ce;
- if (!update_index)
+ if (!state->update_index)
return;
namelen = strlen(patch->new_name);
ce_size = cache_entry_size(namelen);
@@ -4186,20 +4328,20 @@ static void add_conflicted_stages_file(struct patch *patch)
remove_file_from_cache(patch->new_name);
for (stage = 1; stage < 4; stage++) {
- if (is_null_sha1(patch->threeway_stage[stage - 1]))
+ if (is_null_oid(&patch->threeway_stage[stage - 1]))
continue;
ce = xcalloc(1, ce_size);
memcpy(ce->name, patch->new_name, namelen);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = create_ce_flags(stage);
ce->ce_namelen = namelen;
- hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+ hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
die(_("unable to add cache entry for %s"), patch->new_name);
}
}
-static void create_file(struct patch *patch)
+static void create_file(struct apply_state *state, struct patch *patch)
{
char *path = patch->new_name;
unsigned mode = patch->new_mode;
@@ -4208,25 +4350,27 @@ static void create_file(struct patch *patch)
if (!mode)
mode = S_IFREG | 0644;
- create_one_file(path, mode, buf, size);
+ create_one_file(state, path, mode, buf, size);
if (patch->conflicted_threeway)
- add_conflicted_stages_file(patch);
+ add_conflicted_stages_file(state, patch);
else
- add_index_file(path, mode, buf, size);
+ add_index_file(state, path, mode, buf, size);
}
/* phase zero is to remove, phase one is to create */
-static void write_out_one_result(struct patch *patch, int phase)
+static void write_out_one_result(struct apply_state *state,
+ struct patch *patch,
+ int phase)
{
if (patch->is_delete > 0) {
if (phase == 0)
- remove_file(patch, 1);
+ remove_file(state, patch, 1);
return;
}
if (patch->is_new > 0 || patch->is_copy) {
if (phase == 1)
- create_file(patch);
+ create_file(state, patch);
return;
}
/*
@@ -4234,12 +4378,12 @@ static void write_out_one_result(struct patch *patch, int phase)
* thing: remove the old, write the new
*/
if (phase == 0)
- remove_file(patch, patch->is_rename);
+ remove_file(state, patch, patch->is_rename);
if (phase == 1)
- create_file(patch);
+ create_file(state, patch);
}
-static int write_out_one_reject(struct patch *patch)
+static int write_out_one_reject(struct apply_state *state, struct patch *patch)
{
FILE *rej;
char namebuf[PATH_MAX];
@@ -4254,7 +4398,7 @@ static int write_out_one_reject(struct patch *patch)
}
if (!cnt) {
- if (apply_verbosely)
+ if (state->apply_verbosely)
say_patch_name(stderr,
_("Applied patch %s cleanly."), patch);
return 0;
@@ -4310,7 +4454,7 @@ static int write_out_one_reject(struct patch *patch)
return -1;
}
-static int write_out_results(struct patch *list)
+static int write_out_results(struct apply_state *state, struct patch *list)
{
int phase;
int errs = 0;
@@ -4323,9 +4467,9 @@ static int write_out_results(struct patch *list)
if (l->rejected)
errs = 1;
else {
- write_out_one_result(l, phase);
+ write_out_one_result(state, l, phase);
if (phase == 1) {
- if (write_out_one_reject(l))
+ if (write_out_one_reject(state, l))
errs = 1;
if (l->conflicted_threeway) {
string_list_append(&cpath, l->new_name);
@@ -4356,14 +4500,17 @@ static struct lock_file lock_file;
#define INACCURATE_EOF (1<<0)
#define RECOUNT (1<<1)
-static int apply_patch(int fd, const char *filename, int options)
+static int apply_patch(struct apply_state *state,
+ int fd,
+ const char *filename,
+ int options)
{
size_t offset;
struct strbuf buf = STRBUF_INIT; /* owns the patch text */
struct patch *list = NULL, **listp = &list;
int skipped_patch = 0;
- patch_input_file = filename;
+ state->patch_input_file = filename;
read_patch_file(&buf, fd);
offset = 0;
while (offset < buf.len) {
@@ -4373,17 +4520,21 @@ static int apply_patch(int fd, const char *filename, int options)
patch = xcalloc(1, sizeof(*patch));
patch->inaccurate_eof = !!(options & INACCURATE_EOF);
patch->recount = !!(options & RECOUNT);
- nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
- if (nr < 0)
+ nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch);
+ if (nr < 0) {
+ free_patch(patch);
break;
- if (apply_in_reverse)
+ }
+ if (state->apply_in_reverse)
reverse_patches(patch);
- if (use_patch(patch)) {
- patch_stats(patch);
+ if (use_patch(state, patch)) {
+ patch_stats(state, patch);
*listp = patch;
listp = &patch->next;
}
else {
+ if (state->apply_verbosely)
+ say_patch_name(stderr, _("Skipped patch '%s'."), patch);
free_patch(patch);
skipped_patch++;
}
@@ -4393,45 +4544,45 @@ static int apply_patch(int fd, const char *filename, int options)
if (!list && !skipped_patch)
die(_("unrecognized input"));
- if (whitespace_error && (ws_error_action == die_on_ws_error))
- apply = 0;
+ if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
+ state->apply = 0;
- update_index = check_index && apply;
- if (update_index && newfd < 0)
- newfd = hold_locked_index(&lock_file, 1);
+ state->update_index = state->check_index && state->apply;
+ if (state->update_index && state->newfd < 0)
+ state->newfd = hold_locked_index(state->lock_file, 1);
- if (check_index) {
+ if (state->check_index) {
if (read_cache() < 0)
die(_("unable to read index file"));
}
- if ((check || apply) &&
- check_patch_list(list) < 0 &&
- !apply_with_reject)
+ if ((state->check || state->apply) &&
+ check_patch_list(state, list) < 0 &&
+ !state->apply_with_reject)
exit(1);
- if (apply && write_out_results(list)) {
- if (apply_with_reject)
+ if (state->apply && write_out_results(state, list)) {
+ if (state->apply_with_reject)
exit(1);
/* with --3way, we still need to write the index out */
return 1;
}
- if (fake_ancestor)
- build_fake_ancestor(list, fake_ancestor);
+ if (state->fake_ancestor)
+ build_fake_ancestor(list, state->fake_ancestor);
- if (diffstat)
- stat_patch_list(list);
+ if (state->diffstat)
+ stat_patch_list(state, list);
- if (numstat)
- numstat_patch_list(list);
+ if (state->numstat)
+ numstat_patch_list(state, list);
- if (summary)
+ if (state->summary)
summary_patch_list(list);
free_patch_list(list);
strbuf_release(&buf);
- string_list_clear(&fn_table, 0);
+ string_list_clear(&state->fn_table, 0);
return 0;
}
@@ -4445,235 +4596,284 @@ static void git_apply_config(void)
static int option_parse_exclude(const struct option *opt,
const char *arg, int unset)
{
- add_name_limit(arg, 1);
+ struct apply_state *state = opt->value;
+ add_name_limit(state, arg, 1);
return 0;
}
static int option_parse_include(const struct option *opt,
const char *arg, int unset)
{
- add_name_limit(arg, 0);
- has_include = 1;
+ struct apply_state *state = opt->value;
+ add_name_limit(state, arg, 0);
+ state->has_include = 1;
return 0;
}
static int option_parse_p(const struct option *opt,
- const char *arg, int unset)
-{
- p_value = atoi(arg);
- p_value_known = 1;
- return 0;
-}
-
-static int option_parse_z(const struct option *opt,
- const char *arg, int unset)
+ const char *arg,
+ int unset)
{
- if (unset)
- line_termination = '\n';
- else
- line_termination = 0;
+ struct apply_state *state = opt->value;
+ state->p_value = atoi(arg);
+ state->p_value_known = 1;
return 0;
}
static int option_parse_space_change(const struct option *opt,
- const char *arg, int unset)
+ const char *arg, int unset)
{
+ struct apply_state *state = opt->value;
if (unset)
- ws_ignore_action = ignore_ws_none;
+ state->ws_ignore_action = ignore_ws_none;
else
- ws_ignore_action = ignore_ws_change;
+ state->ws_ignore_action = ignore_ws_change;
return 0;
}
static int option_parse_whitespace(const struct option *opt,
const char *arg, int unset)
{
- const char **whitespace_option = opt->value;
-
- *whitespace_option = arg;
- parse_whitespace_option(arg);
+ struct apply_state *state = opt->value;
+ state->whitespace_option = arg;
+ parse_whitespace_option(state, arg);
return 0;
}
static int option_parse_directory(const struct option *opt,
const char *arg, int unset)
{
- root_len = strlen(arg);
- if (root_len && arg[root_len - 1] != '/') {
- char *new_root;
- root = new_root = xmalloc(root_len + 2);
- strcpy(new_root, arg);
- strcpy(new_root + root_len++, "/");
- } else
- root = arg;
+ struct apply_state *state = opt->value;
+ strbuf_reset(&state->root);
+ strbuf_addstr(&state->root, arg);
+ strbuf_complete(&state->root, '/');
return 0;
}
-int cmd_apply(int argc, const char **argv, const char *prefix_)
+static void init_apply_state(struct apply_state *state,
+ const char *prefix,
+ struct lock_file *lock_file)
+{
+ memset(state, 0, sizeof(*state));
+ state->prefix = prefix;
+ state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
+ state->lock_file = lock_file;
+ state->newfd = -1;
+ state->apply = 1;
+ state->line_termination = '\n';
+ state->p_value = 1;
+ state->p_context = UINT_MAX;
+ state->squelch_whitespace_errors = 5;
+ state->ws_error_action = warn_on_ws_error;
+ state->ws_ignore_action = ignore_ws_none;
+ state->linenr = 1;
+ string_list_init(&state->fn_table, 0);
+ string_list_init(&state->limit_by_name, 0);
+ string_list_init(&state->symlink_changes, 0);
+ strbuf_init(&state->root, 0);
+
+ git_apply_config();
+ if (apply_default_whitespace)
+ parse_whitespace_option(state, apply_default_whitespace);
+ if (apply_default_ignorewhitespace)
+ parse_ignorewhitespace_option(state, apply_default_ignorewhitespace);
+}
+
+static void clear_apply_state(struct apply_state *state)
+{
+ string_list_clear(&state->limit_by_name, 0);
+ string_list_clear(&state->symlink_changes, 0);
+ strbuf_release(&state->root);
+
+ /* &state->fn_table is cleared at the end of apply_patch() */
+}
+
+static void check_apply_state(struct apply_state *state, int force_apply)
+{
+ int is_not_gitdir = !startup_info->have_repository;
+
+ if (state->apply_with_reject && state->threeway)
+ die("--reject and --3way cannot be used together.");
+ if (state->cached && state->threeway)
+ die("--cached and --3way cannot be used together.");
+ if (state->threeway) {
+ if (is_not_gitdir)
+ die(_("--3way outside a repository"));
+ state->check_index = 1;
+ }
+ if (state->apply_with_reject)
+ state->apply = state->apply_verbosely = 1;
+ if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
+ state->apply = 0;
+ if (state->check_index && is_not_gitdir)
+ die(_("--index outside a repository"));
+ if (state->cached) {
+ if (is_not_gitdir)
+ die(_("--cached outside a repository"));
+ state->check_index = 1;
+ }
+ if (state->check_index)
+ state->unsafe_paths = 0;
+ if (!state->lock_file)
+ die("BUG: state->lock_file should not be NULL");
+}
+
+static int apply_all_patches(struct apply_state *state,
+ int argc,
+ const char **argv,
+ int options)
{
int i;
int errs = 0;
- int is_not_gitdir = !startup_info->have_repository;
- int force_apply = 0;
+ int read_stdin = 1;
- const char *whitespace_option = NULL;
+ for (i = 0; i < argc; i++) {
+ const char *arg = argv[i];
+ int fd;
+
+ if (!strcmp(arg, "-")) {
+ errs |= apply_patch(state, 0, "<stdin>", options);
+ read_stdin = 0;
+ continue;
+ } else if (0 < state->prefix_length)
+ arg = prefix_filename(state->prefix,
+ state->prefix_length,
+ arg);
+
+ fd = open(arg, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("can't open patch '%s'"), arg);
+ read_stdin = 0;
+ set_default_whitespace_mode(state);
+ errs |= apply_patch(state, fd, arg, options);
+ close(fd);
+ }
+ set_default_whitespace_mode(state);
+ if (read_stdin)
+ errs |= apply_patch(state, 0, "<stdin>", options);
+
+ if (state->whitespace_error) {
+ if (state->squelch_whitespace_errors &&
+ state->squelch_whitespace_errors < state->whitespace_error) {
+ int squelched =
+ state->whitespace_error - state->squelch_whitespace_errors;
+ warning(Q_("squelched %d whitespace error",
+ "squelched %d whitespace errors",
+ squelched),
+ squelched);
+ }
+ if (state->ws_error_action == die_on_ws_error)
+ die(Q_("%d line adds whitespace errors.",
+ "%d lines add whitespace errors.",
+ state->whitespace_error),
+ state->whitespace_error);
+ if (state->applied_after_fixing_ws && state->apply)
+ warning("%d line%s applied after"
+ " fixing whitespace errors.",
+ state->applied_after_fixing_ws,
+ state->applied_after_fixing_ws == 1 ? "" : "s");
+ else if (state->whitespace_error)
+ warning(Q_("%d line adds whitespace errors.",
+ "%d lines add whitespace errors.",
+ state->whitespace_error),
+ state->whitespace_error);
+ }
+
+ if (state->update_index) {
+ if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK))
+ die(_("Unable to write new index file"));
+ state->newfd = -1;
+ }
+
+ return !!errs;
+}
+
+int cmd_apply(int argc, const char **argv, const char *prefix)
+{
+ int force_apply = 0;
+ int options = 0;
+ int ret;
+ struct apply_state state;
struct option builtin_apply_options[] = {
- { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
+ { OPTION_CALLBACK, 0, "exclude", &state, N_("path"),
N_("don't apply changes matching the given path"),
0, option_parse_exclude },
- { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
+ { OPTION_CALLBACK, 0, "include", &state, N_("path"),
N_("apply changes matching the given path"),
0, option_parse_include },
- { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
+ { OPTION_CALLBACK, 'p', NULL, &state, N_("num"),
N_("remove <num> leading slashes from traditional diff paths"),
0, option_parse_p },
- OPT_BOOL(0, "no-add", &no_add,
+ OPT_BOOL(0, "no-add", &state.no_add,
N_("ignore additions made by the patch")),
- OPT_BOOL(0, "stat", &diffstat,
+ OPT_BOOL(0, "stat", &state.diffstat,
N_("instead of applying the patch, output diffstat for the input")),
OPT_NOOP_NOARG(0, "allow-binary-replacement"),
OPT_NOOP_NOARG(0, "binary"),
- OPT_BOOL(0, "numstat", &numstat,
+ OPT_BOOL(0, "numstat", &state.numstat,
N_("show number of added and deleted lines in decimal notation")),
- OPT_BOOL(0, "summary", &summary,
+ OPT_BOOL(0, "summary", &state.summary,
N_("instead of applying the patch, output a summary for the input")),
- OPT_BOOL(0, "check", &check,
+ OPT_BOOL(0, "check", &state.check,
N_("instead of applying the patch, see if the patch is applicable")),
- OPT_BOOL(0, "index", &check_index,
+ OPT_BOOL(0, "index", &state.check_index,
N_("make sure the patch is applicable to the current index")),
- OPT_BOOL(0, "cached", &cached,
+ OPT_BOOL(0, "cached", &state.cached,
N_("apply a patch without touching the working tree")),
- OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
+ OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths,
N_("accept a patch that touches outside the working area")),
OPT_BOOL(0, "apply", &force_apply,
N_("also apply the patch (use with --stat/--summary/--check)")),
- OPT_BOOL('3', "3way", &threeway,
+ OPT_BOOL('3', "3way", &state.threeway,
N_( "attempt three-way merge if a patch does not apply")),
- OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
+ OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor,
N_("build a temporary index based on embedded index information")),
- { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
- N_("paths are separated with NUL character"),
- PARSE_OPT_NOARG, option_parse_z },
- OPT_INTEGER('C', NULL, &p_context,
+ /* Think twice before adding "--nul" synonym to this */
+ OPT_SET_INT('z', NULL, &state.line_termination,
+ N_("paths are separated with NUL character"), '\0'),
+ OPT_INTEGER('C', NULL, &state.p_context,
N_("ensure at least <n> lines of context match")),
- { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
+ { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"),
N_("detect new or modified lines that have whitespace errors"),
0, option_parse_whitespace },
- { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+ { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL,
N_("ignore changes in whitespace when finding context"),
PARSE_OPT_NOARG, option_parse_space_change },
- { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+ { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL,
N_("ignore changes in whitespace when finding context"),
PARSE_OPT_NOARG, option_parse_space_change },
- OPT_BOOL('R', "reverse", &apply_in_reverse,
+ OPT_BOOL('R', "reverse", &state.apply_in_reverse,
N_("apply the patch in reverse")),
- OPT_BOOL(0, "unidiff-zero", &unidiff_zero,
+ OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero,
N_("don't expect at least one line of context")),
- OPT_BOOL(0, "reject", &apply_with_reject,
+ OPT_BOOL(0, "reject", &state.apply_with_reject,
N_("leave the rejected hunks in corresponding *.rej files")),
- OPT_BOOL(0, "allow-overlap", &allow_overlap,
+ OPT_BOOL(0, "allow-overlap", &state.allow_overlap,
N_("allow overlapping hunks")),
- OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
+ OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")),
OPT_BIT(0, "inaccurate-eof", &options,
N_("tolerate incorrectly detected missing new-line at the end of file"),
INACCURATE_EOF),
OPT_BIT(0, "recount", &options,
N_("do not trust the line counts in the hunk headers"),
RECOUNT),
- { OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
+ { OPTION_CALLBACK, 0, "directory", &state, N_("root"),
N_("prepend <root> to all filenames"),
0, option_parse_directory },
OPT_END()
};
- prefix = prefix_;
- prefix_length = prefix ? strlen(prefix) : 0;
- git_apply_config();
- if (apply_default_whitespace)
- parse_whitespace_option(apply_default_whitespace);
- if (apply_default_ignorewhitespace)
- parse_ignorewhitespace_option(apply_default_ignorewhitespace);
+ init_apply_state(&state, prefix, &lock_file);
- argc = parse_options(argc, argv, prefix, builtin_apply_options,
+ argc = parse_options(argc, argv, state.prefix, builtin_apply_options,
apply_usage, 0);
- if (apply_with_reject && threeway)
- die("--reject and --3way cannot be used together.");
- if (cached && threeway)
- die("--cached and --3way cannot be used together.");
- if (threeway) {
- if (is_not_gitdir)
- die(_("--3way outside a repository"));
- check_index = 1;
- }
- if (apply_with_reject)
- apply = apply_verbosely = 1;
- if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
- apply = 0;
- if (check_index && is_not_gitdir)
- die(_("--index outside a repository"));
- if (cached) {
- if (is_not_gitdir)
- die(_("--cached outside a repository"));
- check_index = 1;
- }
- if (check_index)
- unsafe_paths = 0;
+ check_apply_state(&state, force_apply);
- for (i = 0; i < argc; i++) {
- const char *arg = argv[i];
- int fd;
+ ret = apply_all_patches(&state, argc, argv, options);
- if (!strcmp(arg, "-")) {
- errs |= apply_patch(0, "<stdin>", options);
- read_stdin = 0;
- continue;
- } else if (0 < prefix_length)
- arg = prefix_filename(prefix, prefix_length, arg);
+ clear_apply_state(&state);
- fd = open(arg, O_RDONLY);
- if (fd < 0)
- die_errno(_("can't open patch '%s'"), arg);
- read_stdin = 0;
- set_default_whitespace_mode(whitespace_option);
- errs |= apply_patch(fd, arg, options);
- close(fd);
- }
- set_default_whitespace_mode(whitespace_option);
- if (read_stdin)
- errs |= apply_patch(0, "<stdin>", options);
- if (whitespace_error) {
- if (squelch_whitespace_errors &&
- squelch_whitespace_errors < whitespace_error) {
- int squelched =
- whitespace_error - squelch_whitespace_errors;
- warning(Q_("squelched %d whitespace error",
- "squelched %d whitespace errors",
- squelched),
- squelched);
- }
- if (ws_error_action == die_on_ws_error)
- die(Q_("%d line adds whitespace errors.",
- "%d lines add whitespace errors.",
- whitespace_error),
- whitespace_error);
- if (applied_after_fixing_ws && apply)
- warning("%d line%s applied after"
- " fixing whitespace errors.",
- applied_after_fixing_ws,
- applied_after_fixing_ws == 1 ? "" : "s");
- else if (whitespace_error)
- warning(Q_("%d line adds whitespace errors.",
- "%d lines add whitespace errors.",
- whitespace_error),
- whitespace_error);
- }
-
- if (update_index) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
-
- return !!errs;
+ return ret;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 048ed53..a5bbf91 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -6,6 +6,7 @@
*/
#include "cache.h"
+#include "refs.h"
#include "builtin.h"
#include "blob.h"
#include "commit.h"
@@ -27,6 +28,7 @@
#include "line-range.h"
#include "line-log.h"
#include "dir.h"
+#include "progress.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -49,11 +51,12 @@ static int incremental;
static int xdl_opts;
static int abbrev = -1;
static int no_whole_file_rename;
+static int show_progress;
-static enum date_mode blame_date_mode = DATE_ISO8601;
+static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
-static struct string_list mailmap;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
#ifndef DEBUG
#define DEBUG 0
@@ -126,7 +129,12 @@ struct origin {
char path[FLEX_ARRAY];
};
-static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
+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};
@@ -134,7 +142,6 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
xdemitcb_t ecb = {NULL};
xpp.flags = xdl_opts;
- xecfg.ctxlen = ctxlen;
xecfg.hunk_func = hunk_func;
ecb.priv = cb_data;
return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
@@ -458,12 +465,11 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,
static struct origin *make_origin(struct commit *commit, const char *path)
{
struct origin *o;
- o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
+ FLEX_ALLOC_STR(o, path, path);
o->commit = commit;
o->refcnt = 1;
o->next = commit->util;
commit->util = o;
- strcpy(o->path, path);
return o;
}
@@ -504,7 +510,7 @@ static int fill_blob_sha1_and_mode(struct origin *origin)
{
if (!is_null_sha1(origin->blob_sha1))
return 0;
- if (get_tree_entry(origin->commit->object.sha1,
+ if (get_tree_entry(origin->commit->object.oid.hash,
origin->path,
origin->blob_sha1, &origin->mode))
goto error_out;
@@ -555,11 +561,11 @@ static struct origin *find_origin(struct scoreboard *sb,
PATHSPEC_LITERAL_PATH, "", paths);
diff_setup_done(&diff_opts);
- if (is_null_sha1(origin->commit->object.sha1))
- do_diff_cache(parent->tree->object.sha1, &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.sha1,
- origin->commit->tree->object.sha1,
+ diff_tree_sha1(parent->tree->object.oid.hash,
+ origin->commit->tree->object.oid.hash,
"", &diff_opts);
diffcore_std(&diff_opts);
@@ -592,7 +598,7 @@ static struct origin *find_origin(struct scoreboard *sb,
p->status);
case 'M':
porigin = get_origin(sb, parent, origin->path);
- hashcpy(porigin->blob_sha1, p->one->sha1);
+ hashcpy(porigin->blob_sha1, p->one->oid.hash);
porigin->mode = p->one->mode;
break;
case 'A':
@@ -602,7 +608,7 @@ static struct origin *find_origin(struct scoreboard *sb,
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
@@ -625,11 +631,11 @@ static struct origin *find_rename(struct scoreboard *sb,
diff_opts.single_follow = origin->path;
diff_setup_done(&diff_opts);
- if (is_null_sha1(origin->commit->object.sha1))
- do_diff_cache(parent->tree->object.sha1, &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.sha1,
- origin->commit->tree->object.sha1,
+ diff_tree_sha1(parent->tree->object.oid.hash,
+ origin->commit->tree->object.oid.hash,
"", &diff_opts);
diffcore_std(&diff_opts);
@@ -638,13 +644,13 @@ static struct origin *find_rename(struct scoreboard *sb,
if ((p->status == 'R' || p->status == 'C') &&
!strcmp(p->two->path, origin->path)) {
porigin = get_origin(sb, parent, p->one->path);
- hashcpy(porigin->blob_sha1, p->one->sha1);
+ hashcpy(porigin->blob_sha1, p->one->oid.hash);
porigin->mode = p->one->mode;
break;
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
@@ -973,10 +979,10 @@ static void pass_blame_to_parent(struct scoreboard *sb,
fill_origin_blob(&sb->revs->diffopt, target, &file_o);
num_get_patch++;
- if (diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d))
+ if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))
die("unable to generate diff (%s -> %s)",
- sha1_to_hex(parent->commit->object.sha1),
- sha1_to_hex(target->commit->object.sha1));
+ 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;
@@ -1122,9 +1128,9 @@ static void find_copy_in_blob(struct scoreboard *sb,
* file_p partially may match that image.
*/
memset(split, 0, sizeof(struct blame_entry [3]));
- if (diff_hunks(file_p, &file_o, 1, handle_split_cb, &d))
+ if (diff_hunks(file_p, &file_o, handle_split_cb, &d))
die("unable to generate diff (%s)",
- sha1_to_hex(parent->commit->object.sha1));
+ 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);
}
@@ -1273,11 +1279,11 @@ static void find_copy_in_parent(struct scoreboard *sb,
&& (!porigin || strcmp(target->path, porigin->path))))
DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
- if (is_null_sha1(target->commit->object.sha1))
- do_diff_cache(parent->tree->object.sha1, &diff_opts);
+ 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.sha1,
- target->commit->tree->object.sha1,
+ 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))
@@ -1302,7 +1308,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
continue;
norigin = get_origin(sb, parent, p->one->path);
- hashcpy(norigin->blob_sha1, p->one->sha1);
+ hashcpy(norigin->blob_sha1, p->one->oid.hash);
norigin->mode = p->one->mode;
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
@@ -1336,7 +1342,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
} while (unblamed);
target->suspects = reverse_blame(leftover, NULL);
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
}
/*
@@ -1370,8 +1376,15 @@ static void pass_whole_blame(struct scoreboard *sb,
*/
static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
{
- if (!reverse)
+ 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);
}
@@ -1681,7 +1694,7 @@ static void get_commit_info(struct commit *commit,
if (len)
strbuf_add(&ret->summary, subject, len);
else
- strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
+ strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
unuse_commit_buffer(commit, message);
}
@@ -1724,7 +1737,7 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
printf("boundary\n");
if (suspect->previous) {
struct origin *prev = suspect->previous;
- printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
+ printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
write_name_quoted(prev->path, stdout, '\n');
}
@@ -1737,18 +1750,21 @@ 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)
+static void found_guilty_entry(struct blame_entry *ent,
+ struct progress_info *pi)
{
if (incremental) {
struct origin *suspect = ent->suspect;
printf("%s %d %d %d\n",
- sha1_to_hex(suspect->commit->object.sha1),
+ oid_to_hex(&suspect->commit->object.oid),
ent->s_lno + 1, ent->lno + 1, ent->num_lines);
emit_one_suspect_detail(suspect, 0);
write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout");
}
+ pi->blamed_lines += ent->num_lines;
+ display_progress(pi->progress, pi->blamed_lines);
}
/*
@@ -1759,6 +1775,11 @@ 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;
@@ -1800,7 +1821,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
suspect->guilty = 1;
for (;;) {
struct blame_entry *next = ent->next;
- found_guilty_entry(ent);
+ found_guilty_entry(ent, &pi);
if (next) {
ent = next;
continue;
@@ -1816,6 +1837,8 @@ static void assign_blame(struct scoreboard *sb, int opt)
if (DEBUG) /* sanity */
sanity_check_refcnt(sb);
}
+
+ stop_progress(&pi.progress);
}
static const char *format_time(unsigned long time, const char *tz_str,
@@ -1832,7 +1855,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
size_t time_width;
int tz;
tz = atoi(tz_str);
- time_str = show_date(time, tz, blame_date_mode);
+ time_str = show_date(time, tz, &blame_date_mode);
strbuf_addstr(&time_buf, time_str);
/*
* Add space paddings to time_buf to display a fixed width
@@ -1871,9 +1894,9 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
int cnt;
const char *cp;
struct origin *suspect = ent->suspect;
- char hex[41];
+ char hex[GIT_SHA1_HEXSZ + 1];
- strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+ sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
printf("%s %d %d %d\n",
hex,
ent->s_lno + 1,
@@ -1909,11 +1932,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
const char *cp;
struct origin *suspect = ent->suspect;
struct commit_info ci;
- char hex[41];
+ char hex[GIT_SHA1_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
get_commit_info(suspect->commit, &ci, 1);
- strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+ sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
@@ -2033,7 +2056,8 @@ static int prepare_lines(struct scoreboard *sb)
for (p = buf; p < end; p = get_next_line(p, end))
num++;
- sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
+ ALLOC_ARRAY(sb->lineno, num + 1);
+ lineno = sb->lineno;
for (p = buf; p < end; p = get_next_line(p, end))
*lineno++ = p - buf;
@@ -2068,7 +2092,7 @@ static int read_ancestry(const char *graft_file)
static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
{
- const char *uniq = find_unique_abbrev(suspect->commit->object.sha1,
+ const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
auto_abbrev);
int len = strlen(uniq);
if (auto_abbrev < len)
@@ -2144,7 +2168,7 @@ static void sanity_check_refcnt(struct scoreboard *sb)
if (ent->suspect->refcnt <= 0) {
fprintf(stderr, "%s in %s has negative refcnt %d\n",
ent->suspect->path,
- sha1_to_hex(ent->suspect->commit->object.sha1),
+ oid_to_hex(&ent->suspect->commit->object.oid),
ent->suspect->refcnt);
baa = 1;
}
@@ -2181,10 +2205,18 @@ static int git_blame_config(const char *var, const char *value, void *cb)
blank_boundary = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "blame.showemail")) {
+ int *output_option = cb;
+ if (git_config_bool(var, value))
+ *output_option |= OUTPUT_SHOW_EMAIL;
+ else
+ *output_option &= ~OUTPUT_SHOW_EMAIL;
+ return 0;
+ }
if (!strcmp(var, "blame.date")) {
if (!value)
return config_error_nonbool(var);
- blame_date_mode = parse_date_format(value);
+ parse_date_format(value, &blame_date_mode);
return 0;
}
@@ -2197,9 +2229,10 @@ static int git_blame_config(const char *var, const char *value, void *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 unsigned char *commit_sha1 = parents->item->object.sha1;
+ const unsigned char *commit_sha1 = parents->item->object.oid.hash;
unsigned char blob_sha1[20];
unsigned mode;
@@ -2207,7 +2240,15 @@ static void verify_working_tree_path(struct commit *work_tree, const char *path)
sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
return;
}
- die("no such path '%s' in HEAD", path);
+
+ 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 unsigned char *sha1)
@@ -2223,20 +2264,19 @@ static struct commit_list **append_parent(struct commit_list **tail, const unsig
static void append_merge_parents(struct commit_list **tail)
{
int merge_head;
- const char *merge_head_file = git_path("MERGE_HEAD");
struct strbuf line = STRBUF_INIT;
- merge_head = open(merge_head_file, O_RDONLY);
+ merge_head = open(git_path_merge_head(), O_RDONLY);
if (merge_head < 0) {
if (errno == ENOENT)
return;
- die("cannot open '%s' for reading", merge_head_file);
+ die("cannot open '%s' for reading", git_path_merge_head());
}
while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
unsigned char sha1[20];
if (line.len < 40 || get_sha1_hex(line.buf, sha1))
- die("unknown line in '%s': %s", merge_head_file, line.buf);
+ die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
tail = append_parent(tail, sha1);
}
close(merge_head);
@@ -2275,6 +2315,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
unsigned mode;
struct strbuf msg = STRBUF_INIT;
+ read_cache();
time(&now);
commit = alloc_commit_node();
commit->object.parsed = 1;
@@ -2294,7 +2335,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
for (parent = commit->parents; parent; parent = parent->next)
strbuf_addf(&msg, "parent %s\n",
- sha1_to_hex(parent->item->object.sha1));
+ oid_to_hex(&parent->item->object.oid));
strbuf_addf(&msg,
"author %s\n"
"committer %s\n\n"
@@ -2376,42 +2417,41 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
ce->ce_mode = create_ce_mode(mode);
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
- /*
- * We are not going to write this out, so this does not matter
- * right now, but someday we might optimize diff-index --cached
- * with cache-tree information.
- */
cache_tree_invalidate_path(&the_index, path);
return commit;
}
-static char *prepare_final(struct scoreboard *sb)
+static struct commit *find_single_final(struct rev_info *revs,
+ const char **name_p)
{
int i;
- const char *final_commit_name = NULL;
- struct rev_info *revs = sb->revs;
+ struct commit *found = NULL;
+ const char *name = NULL;
- /*
- * There must be one and only one positive commit in the
- * revs->pending array.
- */
for (i = 0; i < revs->pending.nr; i++) {
struct object *obj = revs->pending.objects[i].item;
if (obj->flags & UNINTERESTING)
continue;
- while (obj->type == OBJ_TAG)
- obj = deref_tag(obj, NULL, 0);
+ obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
- if (sb->final)
+ if (found)
die("More than one commit to dig 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;
+ revs->pending.objects[i].name, name);
+ found = (struct commit *)obj;
+ name = revs->pending.objects[i].name;
}
- return xstrdup_or_null(final_commit_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 char *prepare_initial(struct scoreboard *sb)
@@ -2428,8 +2468,7 @@ static char *prepare_initial(struct scoreboard *sb)
struct object *obj = revs->pending.objects[i].item;
if (!(obj->flags & UNINTERESTING))
continue;
- while (obj->type == OBJ_TAG)
- obj = deref_tag(obj, NULL, 0);
+ obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (sb->final)
@@ -2487,17 +2526,19 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
long dashdash_pos, lno;
char *final_commit_name = NULL;
enum object_type type;
-
- static struct string_list range_list;
- static int output_option = 0, opt = 0;
- static int show_stats = 0;
- static const char *revs_file = NULL;
- static const char *contents_from = NULL;
- static const struct option options[] = {
+ struct commit *final_commit = NULL;
+
+ struct string_list range_list = STRING_LIST_INIT_NODUP;
+ int output_option = 0, opt = 0;
+ int show_stats = 0;
+ const char *revs_file = NULL;
+ const char *contents_from = NULL;
+ const struct option options[] = {
OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
+ OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
@@ -2525,7 +2566,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
unsigned int range_i;
long anchor;
- git_config(git_blame_config, NULL);
+ git_config(git_blame_config, &output_option);
init_revisions(&revs, NULL);
revs.date_mode = blame_date_mode;
DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
@@ -2533,6 +2574,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
dashdash_pos = 0;
+ show_progress = -1;
parse_options_start(&ctx, argc, argv, prefix, options,
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
@@ -2557,6 +2599,13 @@ parse_done:
DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
argc = parse_options_end(&ctx);
+ if (incremental || (output_option & OUTPUT_PORCELAIN)) {
+ if (show_progress > 0)
+ die("--progress can't be used with --incremental or porcelain formats");
+ show_progress = 0;
+ } else if (show_progress < 0)
+ show_progress = isatty(2);
+
if (0 < abbrev)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
@@ -2566,13 +2615,13 @@ parse_done:
if (cmd_is_annotate) {
output_option |= OUTPUT_ANNOTATE_COMPAT;
- blame_date_mode = DATE_ISO8601;
+ blame_date_mode.type = DATE_ISO8601;
} else {
blame_date_mode = revs.date_mode;
}
/* The maximum width used to show the dates */
- switch (blame_date_mode) {
+ switch (blame_date_mode.type) {
case DATE_RFC2822:
blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
break;
@@ -2585,6 +2634,9 @@ parse_done:
case DATE_RAW:
blame_date_width = sizeof("1161298804 -0700");
break;
+ case DATE_UNIX:
+ blame_date_width = sizeof("1161298804");
+ break;
case DATE_SHORT:
blame_date_width = sizeof("2006-10-19");
break;
@@ -2597,10 +2649,12 @@ parse_done:
fewer display columns. */
blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
break;
- case DATE_LOCAL:
case DATE_NORMAL:
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
break;
+ case DATE_STRFTIME:
+ blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
+ break;
}
blame_date_width -= 1; /* strip the null */
@@ -2673,10 +2727,12 @@ parse_done:
sb.commits.compare = compare_commits_by_commit_date;
}
else if (contents_from)
- die("--contents and --children do not blend well.");
+ 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) {
@@ -2693,6 +2749,12 @@ parse_done:
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
@@ -2701,7 +2763,26 @@ parse_done:
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
- if (is_null_sha1(sb.final->object.sha1)) {
+ 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;
@@ -2728,7 +2809,7 @@ parse_done:
lno = prepare_lines(&sb);
if (lno && !range_list.nr)
- string_list_append(&range_list, xstrdup("1"));
+ string_list_append(&range_list, "1");
anchor = 1;
range_set_init(&ranges, range_list.nr);
@@ -2777,11 +2858,11 @@ parse_done:
read_mailmap(&mailmap, NULL);
+ assign_blame(&sb, opt);
+
if (!incremental)
setup_pager();
- assign_blame(&sb, opt);
-
free(final_commit_name);
if (incremental)
diff --git a/builtin/branch.c b/builtin/branch.c
index a0a03fc..7df0543 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -19,18 +19,18 @@
#include "column.h"
#include "utf8.h"
#include "wt-status.h"
+#include "ref-filter.h"
+#include "worktree.h"
static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
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>] [-r | -a] [--points-at]"),
NULL
};
-#define REF_LOCAL_BRANCH 0x01
-#define REF_REMOTE_BRANCH 0x02
-
static const char *head;
static unsigned char head_sha1[20];
@@ -52,13 +52,6 @@ enum color_branch {
BRANCH_COLOR_UPSTREAM = 5
};
-static enum merge_filter {
- NO_FILTER = 0,
- SHOW_NOT_MERGED,
- SHOW_MERGED
-} merge_filter;
-static unsigned char merge_filter_ref[20];
-
static struct string_list output = STRING_LIST_INIT_DUP;
static unsigned int colopts;
@@ -121,16 +114,14 @@ static int branch_merged(int kind, const char *name,
void *reference_name_to_free = NULL;
int merged;
- if (kind == REF_LOCAL_BRANCH) {
+ if (kind == FILTER_REFS_BRANCHES) {
struct branch *branch = branch_get(name);
+ const char *upstream = branch_get_upstream(branch, NULL);
unsigned char sha1[20];
- if (branch &&
- branch->merge &&
- branch->merge[0] &&
- branch->merge[0]->dst &&
+ if (upstream &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+ resolve_refdup(upstream, RESOLVE_REF_READING,
sha1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
@@ -162,7 +153,7 @@ static int branch_merged(int kind, const char *name,
}
static int check_branch_commit(const char *branchname, const char *refname,
- unsigned char *sha1, struct commit *head_rev,
+ const unsigned char *sha1, struct commit *head_rev,
int kinds, int force)
{
struct commit *rev = lookup_commit_reference(sha1);
@@ -201,14 +192,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
struct strbuf bname = STRBUF_INIT;
switch (kinds) {
- case REF_REMOTE_BRANCH:
+ case FILTER_REFS_REMOTES:
fmt = "refs/remotes/%s";
/* For subsequent UI messages */
remote_branch = 1;
force = 1;
break;
- case REF_LOCAL_BRANCH:
+ case FILTER_REFS_BRANCHES:
fmt = "refs/heads/%s";
break;
default:
@@ -221,25 +212,30 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
die(_("Couldn't look up commit object for HEAD"));
}
for (i = 0; i < argc; i++, strbuf_release(&bname)) {
- const char *target;
+ char *target = NULL;
int flags = 0;
strbuf_branchname(&bname, argv[i]);
- if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
- error(_("Cannot delete the branch '%s' "
- "which you are currently on."), bname.buf);
- ret = 1;
- continue;
- }
-
free(name);
-
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name,
- RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE
- | RESOLVE_REF_ALLOW_BAD_NAME,
- sha1, &flags);
+
+ if (kinds == FILTER_REFS_BRANCHES) {
+ const struct worktree *wt =
+ find_shared_symref("HEAD", name);
+ if (wt) {
+ error(_("Cannot delete branch '%s' "
+ "checked out at '%s'"),
+ bname.buf, wt->path);
+ ret = 1;
+ continue;
+ }
+ }
+
+ target = resolve_refdup(name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ sha1, &flags);
if (!target) {
error(remote_branch
? _("remote-tracking branch '%s' not found.")
@@ -252,16 +248,17 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
force)) {
ret = 1;
- continue;
+ goto next;
}
- if (delete_ref(name, sha1, REF_NODEREF)) {
+ if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
+ REF_NODEREF)) {
error(remote_branch
? _("Error deleting remote-tracking branch '%s'")
: _("Error deleting branch '%s'"),
bname.buf);
ret = 1;
- continue;
+ goto next;
}
if (!quiet) {
printf(remote_branch
@@ -273,6 +270,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
+
+ next:
+ free(target);
}
free(name);
@@ -280,172 +280,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
return(ret);
}
-struct ref_item {
- char *name;
- char *dest;
- unsigned int kind, width;
- struct commit *commit;
- int ignore;
-};
-
-struct ref_list {
- struct rev_info revs;
- int index, alloc, maxwidth, verbose, abbrev;
- struct ref_item *list;
- struct commit_list *with_commit;
- int kinds;
-};
-
-static char *resolve_symref(const char *src, const char *prefix)
-{
- unsigned char sha1[20];
- int flag;
- const char *dst;
-
- dst = resolve_ref_unsafe(src, 0, sha1, &flag);
- if (!(dst && (flag & REF_ISSYMREF)))
- return NULL;
- if (prefix)
- skip_prefix(dst, prefix, &dst);
- return xstrdup(dst);
-}
-
-struct append_ref_cb {
- struct ref_list *ref_list;
- const char **pattern;
- int ret;
-};
-
-static int match_patterns(const char **pattern, const char *refname)
-{
- if (!*pattern)
- return 1; /* no pattern always matches */
- while (*pattern) {
- if (!wildmatch(*pattern, refname, 0, NULL))
- return 1;
- pattern++;
- }
- return 0;
-}
-
-static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
-{
- struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
- struct ref_list *ref_list = cb->ref_list;
- struct ref_item *newitem;
- struct commit *commit;
- int kind, i;
- const char *prefix, *orig_refname = refname;
-
- static struct {
- int kind;
- const char *prefix;
- } ref_kind[] = {
- { REF_LOCAL_BRANCH, "refs/heads/" },
- { REF_REMOTE_BRANCH, "refs/remotes/" },
- };
-
- /* Detect kind */
- for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
- prefix = ref_kind[i].prefix;
- if (skip_prefix(refname, prefix, &refname)) {
- kind = ref_kind[i].kind;
- break;
- }
- }
- if (ARRAY_SIZE(ref_kind) <= i)
- return 0;
-
- /* Don't add types the caller doesn't want */
- if ((kind & ref_list->kinds) == 0)
- return 0;
-
- if (!match_patterns(cb->pattern, refname))
- return 0;
-
- commit = NULL;
- if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
- commit = lookup_commit_reference_gently(sha1, 1);
- if (!commit) {
- cb->ret = error(_("branch '%s' does not point at a commit"), refname);
- return 0;
- }
-
- /* Filter with with_commit if specified */
- if (!is_descendant_of(commit, ref_list->with_commit))
- return 0;
-
- if (merge_filter != NO_FILTER)
- add_pending_object(&ref_list->revs,
- (struct object *)commit, refname);
- }
-
- ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc);
-
- /* Record the new item */
- newitem = &(ref_list->list[ref_list->index++]);
- newitem->name = xstrdup(refname);
- newitem->kind = kind;
- newitem->commit = commit;
- newitem->width = utf8_strwidth(refname);
- newitem->dest = resolve_symref(orig_refname, prefix);
- newitem->ignore = 0;
- /* adjust for "remotes/" */
- if (newitem->kind == REF_REMOTE_BRANCH &&
- ref_list->kinds != REF_REMOTE_BRANCH)
- newitem->width += 8;
- if (newitem->width > ref_list->maxwidth)
- ref_list->maxwidth = newitem->width;
-
- return 0;
-}
-
-static void free_ref_list(struct ref_list *ref_list)
-{
- int i;
-
- for (i = 0; i < ref_list->index; i++) {
- free(ref_list->list[i].name);
- free(ref_list->list[i].dest);
- }
- free(ref_list->list);
-}
-
-static int ref_cmp(const void *r1, const void *r2)
-{
- struct ref_item *c1 = (struct ref_item *)(r1);
- struct ref_item *c2 = (struct ref_item *)(r2);
-
- if (c1->kind != c2->kind)
- return c1->kind - c2->kind;
- return strcmp(c1->name, c2->name);
-}
-
static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
int show_upstream_ref)
{
int ours, theirs;
char *ref = NULL;
struct branch *branch = branch_get(branch_name);
+ const char *upstream;
struct strbuf fancy = STRBUF_INIT;
int upstream_is_gone = 0;
int added_decoration = 1;
- switch (stat_tracking_info(branch, &ours, &theirs)) {
- case 0:
- /* no base */
- return;
- case -1:
- /* with "gone" base */
+ if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
+ if (!upstream)
+ return;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
if (show_upstream_ref) {
- ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+ ref = shorten_unambiguous_ref(upstream, 0);
if (want_color(branch_use_color))
strbuf_addf(&fancy, "%s%s%s",
branch_get_color(BRANCH_COLOR_UPSTREAM),
@@ -489,8 +342,8 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
free(ref);
}
-static void add_verbose_info(struct strbuf *out, struct ref_item *item,
- int verbose, int abbrev)
+static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
+ struct ref_filter *filter, const char *refname)
{
struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
const char *sub = _(" **** invalid ref ****");
@@ -501,32 +354,79 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
sub = subject.buf;
}
- if (item->kind == REF_LOCAL_BRANCH)
- fill_tracking_info(&stat, item->name, verbose > 1);
+ if (item->kind == FILTER_REFS_BRANCHES)
+ fill_tracking_info(&stat, refname, filter->verbose > 1);
strbuf_addf(out, " %s %s%s",
- find_unique_abbrev(item->commit->object.sha1, abbrev),
+ find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
stat.buf, sub);
strbuf_release(&stat);
strbuf_release(&subject);
}
-static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
- int abbrev, int current, char *prefix)
+static char *get_head_description(void)
+{
+ struct strbuf desc = STRBUF_INIT;
+ struct wt_status_state state;
+ memset(&state, 0, sizeof(state));
+ wt_status_get_state(&state, 1);
+ if (state.rebase_in_progress ||
+ state.rebase_interactive_in_progress)
+ strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+ state.branch);
+ else if (state.bisect_in_progress)
+ strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+ state.branch);
+ else if (state.detached_from) {
+ if (state.detached_at)
+ /* TRANSLATORS: make sure this matches
+ "HEAD detached at " in wt-status.c */
+ strbuf_addf(&desc, _("(HEAD detached at %s)"),
+ state.detached_from);
+ else
+ /* TRANSLATORS: make sure this matches
+ "HEAD detached from " in wt-status.c */
+ strbuf_addf(&desc, _("(HEAD detached from %s)"),
+ state.detached_from);
+ }
+ else
+ strbuf_addstr(&desc, _("(no branch)"));
+ free(state.branch);
+ free(state.onto);
+ free(state.detached_from);
+ return strbuf_detach(&desc, NULL);
+}
+
+static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
+ struct ref_filter *filter, const char *remote_prefix)
{
char c;
+ int current = 0;
int color;
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
-
- if (item->ignore)
- return;
+ const char *prefix_to_show = "";
+ const char *prefix_to_skip = NULL;
+ const char *desc = item->refname;
+ char *to_free = NULL;
switch (item->kind) {
- case REF_LOCAL_BRANCH:
- color = BRANCH_COLOR_LOCAL;
+ case FILTER_REFS_BRANCHES:
+ prefix_to_skip = "refs/heads/";
+ skip_prefix(desc, prefix_to_skip, &desc);
+ if (!filter->detached && !strcmp(desc, head))
+ current = 1;
+ else
+ color = BRANCH_COLOR_LOCAL;
break;
- case REF_REMOTE_BRANCH:
+ case FILTER_REFS_REMOTES:
+ prefix_to_skip = "refs/remotes/";
+ skip_prefix(desc, prefix_to_skip, &desc);
color = BRANCH_COLOR_REMOTE;
+ prefix_to_show = remote_prefix;
+ break;
+ case FILTER_REFS_DETACHED_HEAD:
+ desc = to_free = get_head_description();
+ current = 1;
break;
default:
color = BRANCH_COLOR_PLAIN;
@@ -539,8 +439,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
color = BRANCH_COLOR_CURRENT;
}
- strbuf_addf(&name, "%s%s", prefix, item->name);
- if (verbose) {
+ strbuf_addf(&name, "%s%s", prefix_to_show, desc);
+ if (filter->verbose) {
int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
maxwidth + utf8_compensation, name.buf,
@@ -549,155 +449,107 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
name.buf, branch_get_color(BRANCH_COLOR_RESET));
- if (item->dest)
- strbuf_addf(&out, " -> %s", item->dest);
- else if (verbose)
+ if (item->symref) {
+ const char *symref = item->symref;
+ if (prefix_to_skip)
+ skip_prefix(symref, prefix_to_skip, &symref);
+ strbuf_addf(&out, " -> %s", symref);
+ }
+ else if (filter->verbose)
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
- add_verbose_info(&out, item, verbose, abbrev);
+ add_verbose_info(&out, item, filter, desc);
if (column_active(colopts)) {
- assert(!verbose && "--column and --verbose are incompatible");
+ assert(!filter->verbose && "--column and --verbose are incompatible");
string_list_append(&output, out.buf);
} else {
printf("%s\n", out.buf);
}
strbuf_release(&name);
strbuf_release(&out);
+ free(to_free);
}
-static int calc_maxwidth(struct ref_list *refs)
+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
{
- int i, w = 0;
- for (i = 0; i < refs->index; i++) {
- if (refs->list[i].ignore)
- continue;
- if (refs->list[i].width > w)
- w = refs->list[i].width;
+ int i, max = 0;
+ for (i = 0; i < refs->nr; i++) {
+ struct ref_array_item *it = refs->items[i];
+ const char *desc = it->refname;
+ int w;
+
+ skip_prefix(it->refname, "refs/heads/", &desc);
+ skip_prefix(it->refname, "refs/remotes/", &desc);
+ w = utf8_strwidth(desc);
+
+ if (it->kind == FILTER_REFS_REMOTES)
+ w += remote_bonus;
+ if (w > max)
+ max = w;
}
- return w;
+ return max;
}
-static char *get_head_description(void)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
{
- struct strbuf desc = STRBUF_INIT;
- struct wt_status_state state;
- memset(&state, 0, sizeof(state));
- wt_status_get_state(&state, 1);
- if (state.rebase_in_progress ||
- state.rebase_interactive_in_progress)
- strbuf_addf(&desc, _("(no branch, rebasing %s)"),
- state.branch);
- else if (state.bisect_in_progress)
- strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
- state.branch);
- else if (state.detached_from) {
- /* TRANSLATORS: make sure these match _("HEAD detached at ")
- and _("HEAD detached from ") in wt-status.c */
- if (state.detached_at)
- strbuf_addf(&desc, _("(HEAD detached at %s)"),
- state.detached_from);
- else
- strbuf_addf(&desc, _("(HEAD detached from %s)"),
- state.detached_from);
- }
- else
- strbuf_addstr(&desc, _("(no branch)"));
- free(state.branch);
- free(state.onto);
- free(state.detached_from);
- return strbuf_detach(&desc, NULL);
-}
+ int i;
+ struct ref_array array;
+ int maxwidth = 0;
+ const char *remote_prefix = "";
-static void show_detached(struct ref_list *ref_list)
-{
- struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
-
- if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
- struct ref_item item;
- item.name = get_head_description();
- item.width = utf8_strwidth(item.name);
- item.kind = REF_LOCAL_BRANCH;
- item.dest = NULL;
- item.commit = head_commit;
- item.ignore = 0;
- if (item.width > ref_list->maxwidth)
- ref_list->maxwidth = item.width;
- print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
- free(item.name);
- }
+ /*
+ * If we are listing more than just remote branches,
+ * then remote branches will have a "remotes/" prefix.
+ * We need to account for this in the width.
+ */
+ if (filter->kind != FILTER_REFS_REMOTES)
+ remote_prefix = "remotes/";
+
+ memset(&array, 0, sizeof(array));
+
+ verify_ref_format("%(refname)%(symref)");
+ filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
+
+ if (filter->verbose)
+ maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
+
+ /*
+ * If no sorting parameter is given then we default to sorting
+ * by 'refname'. This would give us an alphabetically sorted
+ * array with the 'HEAD' ref at the beginning followed by
+ * local branches 'refs/heads/...' and finally remote-tacking
+ * branches 'refs/remotes/...'.
+ */
+ if (!sorting)
+ sorting = ref_default_sorting();
+ ref_array_sort(sorting, &array);
+
+ for (i = 0; i < array.nr; i++)
+ format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
+
+ ref_array_clear(&array);
}
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
+static void reject_rebase_or_bisect_branch(const char *target)
{
+ struct worktree **worktrees = get_worktrees();
int i;
- struct append_ref_cb cb;
- struct ref_list ref_list;
-
- memset(&ref_list, 0, sizeof(ref_list));
- ref_list.kinds = kinds;
- ref_list.verbose = verbose;
- ref_list.abbrev = abbrev;
- ref_list.with_commit = with_commit;
- if (merge_filter != NO_FILTER)
- init_revisions(&ref_list.revs, NULL);
- cb.ref_list = &ref_list;
- cb.pattern = pattern;
- cb.ret = 0;
- for_each_rawref(append_ref, &cb);
- if (merge_filter != NO_FILTER) {
- struct commit *filter;
- filter = lookup_commit_reference_gently(merge_filter_ref, 0);
- if (!filter)
- die(_("object '%s' does not point to a commit"),
- sha1_to_hex(merge_filter_ref));
-
- filter->object.flags |= UNINTERESTING;
- add_pending_object(&ref_list.revs,
- (struct object *) filter, "");
- ref_list.revs.limited = 1;
-
- if (prepare_revision_walk(&ref_list.revs))
- die(_("revision walk setup failed"));
-
- for (i = 0; i < ref_list.index; i++) {
- struct ref_item *item = &ref_list.list[i];
- struct commit *commit = item->commit;
- int is_merged = !!(commit->object.flags & UNINTERESTING);
- item->ignore = is_merged != (merge_filter == SHOW_MERGED);
- }
- for (i = 0; i < ref_list.index; i++) {
- struct ref_item *item = &ref_list.list[i];
- clear_commit_marks(item->commit, ALL_REV_FLAGS);
- }
- clear_commit_marks(filter, ALL_REV_FLAGS);
-
- if (verbose)
- ref_list.maxwidth = calc_maxwidth(&ref_list);
- }
+ for (i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
- qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
-
- detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached && match_patterns(pattern, "HEAD"))
- show_detached(&ref_list);
-
- for (i = 0; i < ref_list.index; i++) {
- int current = !detached &&
- (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
- !strcmp(ref_list.list[i].name, head);
- char *prefix = (kinds != REF_REMOTE_BRANCH &&
- ref_list.list[i].kind == REF_REMOTE_BRANCH)
- ? "remotes/" : "";
- print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
- abbrev, current, prefix);
- }
+ if (!wt->is_detached)
+ continue;
- free_ref_list(&ref_list);
+ if (is_worktree_being_rebased(wt, target))
+ die(_("Branch %s is being rebased at %s"),
+ target, wt->path);
- if (cb.ret)
- error(_("some refs could not be read"));
+ if (is_worktree_being_bisected(wt, target))
+ die(_("Branch %s is being bisected at %s"),
+ target, wt->path);
+ }
- return cb.ret;
+ free_worktrees(worktrees);
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -729,6 +581,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
validate_new_branchname(newname, &newref, force, clobber_head_ok);
+ reject_rebase_or_bisect_branch(oldref.buf);
+
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
@@ -739,8 +593,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)
if (recovery)
warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
- /* no need to pass logmsg here as HEAD didn't really move */
- if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
+ if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
@@ -753,26 +606,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)
strbuf_release(&newsection);
}
-static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
-{
- merge_filter = ((opt->long_name[0] == 'n')
- ? SHOW_NOT_MERGED
- : SHOW_MERGED);
- if (unset)
- merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
- if (!arg)
- arg = "HEAD";
- if (get_sha1(arg, merge_filter_ref))
- die(_("malformed object name %s"), arg);
- return 0;
-}
-
static const char edit_description[] = "BRANCH_DESCRIPTION";
static int edit_branch_description(const char *branch_name)
{
- FILE *fp;
- int status;
struct strbuf buf = STRBUF_INIT;
struct strbuf name = STRBUF_INIT;
@@ -780,73 +617,57 @@ static int edit_branch_description(const char *branch_name)
if (!buf.len || buf.buf[buf.len-1] != '\n')
strbuf_addch(&buf, '\n');
strbuf_commented_addf(&buf,
- "Please edit the description for the branch\n"
- " %s\n"
- "Lines starting with '%c' will be stripped.\n",
+ _("Please edit the description for the branch\n"
+ " %s\n"
+ "Lines starting with '%c' will be stripped.\n"),
branch_name, comment_line_char);
- fp = fopen(git_path(edit_description), "w");
- if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
- strbuf_release(&buf);
- return error(_("could not write branch description template: %s"),
- strerror(errno));
- }
+ write_file_buf(git_path(edit_description), buf.buf, buf.len);
strbuf_reset(&buf);
if (launch_editor(git_path(edit_description), &buf, NULL)) {
strbuf_release(&buf);
return -1;
}
- stripspace(&buf, 1);
+ strbuf_stripspace(&buf, 1);
strbuf_addf(&name, "branch.%s.description", branch_name);
- status = git_config_set(name.buf, buf.len ? buf.buf : NULL);
+ git_config_set(name.buf, buf.len ? buf.buf : NULL);
strbuf_release(&name);
strbuf_release(&buf);
- return status;
+ return 0;
}
int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, rename = 0, force = 0, list = 0;
- int verbose = 0, abbrev = -1, detached = 0;
int reflog = 0, edit_description = 0;
int quiet = 0, unset_upstream = 0;
const char *new_upstream = NULL;
enum branch_track track;
- int kinds = REF_LOCAL_BRANCH;
- struct commit_list *with_commit = NULL;
+ struct ref_filter filter;
+ static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
struct option options[] = {
OPT_GROUP(N_("Generic options")),
- OPT__VERBOSE(&verbose,
+ OPT__VERBOSE(&filter.verbose,
N_("show hash and subject, give twice for upstream branch")),
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),
- OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
+ OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
OPT__COLOR(&branch_use_color, N_("use colored output")),
- OPT_SET_INT('r', "remotes", &kinds, N_("act on remote-tracking branches"),
- REF_REMOTE_BRANCH),
- {
- OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
- N_("print only branches that contain the commit"),
- PARSE_OPT_LASTARG_DEFAULT,
- parse_opt_with_commit, (intptr_t)"HEAD",
- },
- {
- OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
- N_("print only branches that contain the commit"),
- PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
- parse_opt_with_commit, (intptr_t) "HEAD",
- },
- OPT__ABBREV(&abbrev),
+ OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
+ FILTER_REFS_REMOTES),
+ OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
+ OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
+ OPT__ABBREV(&filter.abbrev),
OPT_GROUP(N_("Specific git-branch actions:")),
- OPT_SET_INT('a', "all", &kinds, N_("list both remote-tracking and local branches"),
- REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
+ OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
+ FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES),
OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
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),
@@ -856,22 +677,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "edit-description", &edit_description,
N_("edit the description for the branch")),
OPT__FORCE(&force, N_("force creation, move/rename, deletion")),
+ 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")),
+ OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
+ N_("field name to sort on"), &parse_opt_ref_sorting),
{
- OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
- N_("commit"), N_("print only not merged branches"),
- PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
- opt_parse_merge_filter, (intptr_t) "HEAD",
- },
- {
- OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
- N_("commit"), N_("print only merged branches"),
- PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
- opt_parse_merge_filter, (intptr_t) "HEAD",
+ OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
+ N_("print only branches of the object"), 0, parse_opt_object_name
},
- OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
OPT_END(),
};
+ memset(&filter, 0, sizeof(filter));
+ filter.kind = FILTER_REFS_BRANCHES;
+ filter.abbrev = -1;
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_branch_usage, options);
@@ -883,11 +704,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
- detached = 1;
+ filter.detached = 1;
else if (!skip_prefix(head, "refs/heads/", &head))
die(_("HEAD not found below refs/heads!"));
- hashcpy(merge_filter_ref, head_sha1);
-
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
@@ -895,17 +714,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
list = 1;
- if (with_commit || merge_filter != NO_FILTER)
+ if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
list = 1;
if (!!delete + !!rename + !!new_upstream +
list + unset_upstream > 1)
usage_with_options(builtin_branch_usage, options);
- if (abbrev == -1)
- abbrev = DEFAULT_ABBREV;
+ if (filter.abbrev == -1)
+ filter.abbrev = DEFAULT_ABBREV;
finalize_colopts(&colopts, -1);
- if (verbose) {
+ if (filter.verbose) {
if (explicitly_enable_column(colopts))
die(_("--column and --verbose are incompatible"));
colopts = 0;
@@ -919,20 +738,23 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete) {
if (!argc)
die(_("branch name required"));
- return delete_branches(argc, argv, delete > 1, kinds, quiet);
+ return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
} else if (list) {
- int ret = print_ref_list(kinds, detached, verbose, abbrev,
- with_commit, argv);
+ /* git branch --local also shows HEAD when it is detached */
+ if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
+ filter.kind |= FILTER_REFS_DETACHED_HEAD;
+ filter.name_patterns = argv;
+ print_ref_list(&filter, sorting);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
- return ret;
+ return 0;
}
else if (edit_description) {
const char *branch_name;
struct strbuf branch_ref = STRBUF_INIT;
if (!argc) {
- if (detached)
+ if (filter.detached)
die(_("Cannot give description to detached HEAD"));
branch_name = head;
} else if (argc == 1)
@@ -1020,7 +842,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!branch)
die(_("no such branch '%s'"), argv[0]);
- if (kinds != REF_LOCAL_BRANCH)
+ if (filter.kind != FILTER_REFS_BRANCHES)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
if (track == BRANCH_TRACK_OVERRIDE)
@@ -1042,8 +864,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
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);
+ fprintf(stderr, " git branch -d %s\n", branch->name);
+ fprintf(stderr, " git branch --set-upstream-to %s\n", branch->name);
}
} else
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..560f6c2 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -8,14 +8,32 @@
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
+#include "tree-walk.h"
+#include "sha1-array.h"
-static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
+struct batch_options {
+ int enabled;
+ int follow_symlinks;
+ int print_contents;
+ int buffer_output;
+ int all_objects;
+ const char *format;
+};
+
+static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
+ int unknown_type)
{
unsigned char sha1[20];
enum object_type type;
char *buf;
unsigned long size;
struct object_context obj_context;
+ struct object_info oi = {NULL};
+ struct strbuf sb = STRBUF_INIT;
+ unsigned flags = LOOKUP_REPLACE_OBJECT;
+
+ if (unknown_type)
+ flags |= LOOKUP_UNKNOWN_OBJECT;
if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
die("Not a valid object name %s", obj_name);
@@ -23,20 +41,22 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
buf = NULL;
switch (opt) {
case 't':
- type = sha1_object_info(sha1, NULL);
- if (type > 0) {
- printf("%s\n", typename(type));
+ oi.typename = &sb;
+ if (sha1_object_info_extended(sha1, &oi, flags) < 0)
+ die("git cat-file: could not get object info");
+ if (sb.len) {
+ printf("%s\n", sb.buf);
+ strbuf_release(&sb);
return 0;
}
break;
case 's':
- type = sha1_object_info(sha1, &size);
- if (type > 0) {
- printf("%lu\n", size);
- return 0;
- }
- break;
+ oi.sizep = &size;
+ if (sha1_object_info_extended(sha1, &oi, flags) < 0)
+ die("git cat-file: could not get object info");
+ printf("%lu\n", size);
+ return 0;
case 'e':
return !has_sha1_file(sha1);
@@ -111,7 +131,7 @@ struct expand_data {
unsigned char sha1[20];
enum object_type type;
unsigned long size;
- unsigned long disk_size;
+ off_t disk_size;
const char *rest;
unsigned char delta_base_sha1[20];
@@ -134,6 +154,13 @@ struct expand_data {
* elements above, so you can retrieve the response from there.
*/
struct object_info info;
+
+ /*
+ * This flag will be true if the requested batch format and options
+ * don't require us to call sha1_object_info, which can then be
+ * optimized out.
+ */
+ unsigned skip_object_info : 1;
};
static int is_atom(const char *atom, const char *s, int slen)
@@ -164,7 +191,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
if (data->mark_query)
data->info.disk_sizep = &data->disk_size;
else
- strbuf_addf(sb, "%lu", data->disk_size);
+ strbuf_addf(sb, "%"PRIuMAX, (uintmax_t)data->disk_size);
} else if (is_atom("rest", atom, len)) {
if (data->mark_query)
data->split_on_whitespace = 1;
@@ -194,14 +221,25 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data)
return end - start + 1;
}
-static void print_object_or_die(int fd, struct expand_data *data)
+static void batch_write(struct batch_options *opt, const void *data, int len)
+{
+ if (opt->buffer_output) {
+ if (fwrite(data, 1, len, stdout) != len)
+ die_errno("unable to write to stdout");
+ } else
+ write_or_die(1, data, len);
+}
+
+static void print_object_or_die(struct batch_options *opt, struct expand_data *data)
{
const unsigned char *sha1 = data->sha1;
assert(data->info.typep);
if (data->type == OBJ_BLOB) {
- if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
+ if (opt->buffer_output)
+ fflush(stdout);
+ if (stream_blob_to_fd(1, sha1, NULL, 0) < 0)
die("unable to stream %s to stdout", sha1_to_hex(sha1));
}
else {
@@ -217,46 +255,105 @@ static void print_object_or_die(int fd, struct expand_data *data)
if (data->info.sizep && size != data->size)
die("object %s changed size!?", sha1_to_hex(sha1));
- write_or_die(fd, contents, size);
+ batch_write(opt, contents, size);
free(contents);
}
}
-struct batch_options {
- int enabled;
- int print_contents;
- const char *format;
-};
-
-static int batch_one_object(const char *obj_name, struct batch_options *opt,
- struct expand_data *data)
+static void batch_object_write(const char *obj_name, struct batch_options *opt,
+ struct expand_data *data)
{
struct strbuf buf = STRBUF_INIT;
- if (!obj_name)
- return 1;
-
- if (get_sha1(obj_name, data->sha1)) {
- printf("%s missing\n", obj_name);
- fflush(stdout);
- return 0;
- }
-
- if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
- printf("%s missing\n", obj_name);
+ if (!data->skip_object_info &&
+ sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
+ printf("%s missing\n", obj_name ? obj_name : sha1_to_hex(data->sha1));
fflush(stdout);
- return 0;
+ return;
}
strbuf_expand(&buf, opt->format, expand_format, data);
strbuf_addch(&buf, '\n');
- write_or_die(1, buf.buf, buf.len);
+ batch_write(opt, buf.buf, buf.len);
strbuf_release(&buf);
if (opt->print_contents) {
- print_object_or_die(1, data);
- write_or_die(1, "\n", 1);
+ print_object_or_die(opt, data);
+ batch_write(opt, "\n", 1);
+ }
+}
+
+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;
+ enum follow_symlinks_result result;
+
+ result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx);
+ if (result != FOUND) {
+ switch (result) {
+ case MISSING_OBJECT:
+ printf("%s missing\n", obj_name);
+ break;
+ case DANGLING_SYMLINK:
+ printf("dangling %"PRIuMAX"\n%s\n",
+ (uintmax_t)strlen(obj_name), obj_name);
+ break;
+ case SYMLINK_LOOP:
+ printf("loop %"PRIuMAX"\n%s\n",
+ (uintmax_t)strlen(obj_name), obj_name);
+ break;
+ case NOT_DIR:
+ printf("notdir %"PRIuMAX"\n%s\n",
+ (uintmax_t)strlen(obj_name), obj_name);
+ break;
+ default:
+ die("BUG: unknown get_sha1_with_context result %d\n",
+ result);
+ break;
+ }
+ fflush(stdout);
+ return;
}
+
+ if (ctx.mode == 0) {
+ printf("symlink %"PRIuMAX"\n%s\n",
+ (uintmax_t)ctx.symlink_path.len,
+ ctx.symlink_path.buf);
+ fflush(stdout);
+ return;
+ }
+
+ batch_object_write(obj_name, opt, data);
+}
+
+struct object_cb_data {
+ struct batch_options *opt;
+ struct expand_data *expand;
+};
+
+static void batch_object_cb(const unsigned char sha1[20], void *vdata)
+{
+ struct object_cb_data *data = vdata;
+ hashcpy(data->expand->sha1, sha1);
+ batch_object_write(NULL, data->opt, data->expand);
+}
+
+static int batch_loose_object(const unsigned char *sha1,
+ const char *path,
+ void *data)
+{
+ sha1_array_append(data, sha1);
+ return 0;
+}
+
+static int batch_packed_object(const unsigned char *sha1,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *data)
+{
+ sha1_array_append(data, sha1);
return 0;
}
@@ -280,6 +377,13 @@ static int batch_objects(struct batch_options *opt)
strbuf_expand(&buf, opt->format, expand_format, &data);
data.mark_query = 0;
+ if (opt->all_objects) {
+ struct object_info empty;
+ memset(&empty, 0, sizeof(empty));
+ if (!memcmp(&data.info, &empty, sizeof(empty)))
+ data.skip_object_info = 1;
+ }
+
/*
* If we are printing out the object, then always fill in the type,
* since we will want to decide whether or not to stream.
@@ -287,6 +391,21 @@ static int batch_objects(struct batch_options *opt)
if (opt->print_contents)
data.info.typep = &data.type;
+ if (opt->all_objects) {
+ struct sha1_array sa = SHA1_ARRAY_INIT;
+ struct object_cb_data cb;
+
+ for_each_loose_object(batch_loose_object, &sa, 0);
+ for_each_packed_object(batch_packed_object, &sa, 0);
+
+ cb.opt = opt;
+ cb.expand = &data;
+ sha1_array_for_each_unique(&sa, batch_object_cb, &cb);
+
+ sha1_array_clear(&sa);
+ return 0;
+ }
+
/*
* We are going to call get_sha1 on a potentially very large number of
* objects. In most large cases, these will be actual object sha1s. The
@@ -297,7 +416,7 @@ static int batch_objects(struct batch_options *opt)
save_warning = warn_on_object_refname_ambiguity;
warn_on_object_refname_ambiguity = 0;
- while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+ while (strbuf_getline(&buf, stdin) != EOF) {
if (data.split_on_whitespace) {
/*
* Split at first whitespace, tying off the beginning
@@ -312,9 +431,7 @@ static int batch_objects(struct batch_options *opt)
data.rest = p;
}
- retval = batch_one_object(buf.buf, opt, &data);
- if (retval)
- break;
+ batch_one_object(buf.buf, opt, &data);
}
strbuf_release(&buf);
@@ -323,8 +440,8 @@ static int batch_objects(struct batch_options *opt)
}
static const char * const cat_file_usage[] = {
- N_("git cat-file (-t | -s | -e | -p | <type> | --textconv) <object>"),
- N_("git cat-file (--batch | --batch-check) < <list-of-objects>"),
+ N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv) <object>"),
+ N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
NULL
};
@@ -342,9 +459,8 @@ static int batch_option_callback(const struct option *opt,
{
struct batch_options *bo = opt->value;
- if (unset) {
- memset(bo, 0, sizeof(*bo));
- return 0;
+ if (bo->enabled) {
+ return 1;
}
bo->enabled = 1;
@@ -359,30 +475,36 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
int opt = 0;
const char *exp_type = NULL, *obj_name = NULL;
struct batch_options batch = {0};
+ int unknown_type = 0;
const struct option options[] = {
OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
- OPT_SET_INT('t', NULL, &opt, N_("show object type"), 't'),
- OPT_SET_INT('s', NULL, &opt, N_("show object size"), 's'),
- OPT_SET_INT('e', NULL, &opt,
+ OPT_CMDMODE('t', NULL, &opt, N_("show object type"), 't'),
+ OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
+ OPT_CMDMODE('e', NULL, &opt,
N_("exit with zero when there's no error"), 'e'),
- OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
- OPT_SET_INT(0, "textconv", &opt,
+ OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
+ OPT_CMDMODE(0, "textconv", &opt,
N_("for blob objects, run textconv on object's content"), 'c'),
+ OPT_BOOL(0, "allow-unknown-type", &unknown_type,
+ N_("allow -s and -t to work with broken/corrupt objects")),
+ OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
{ OPTION_CALLBACK, 0, "batch", &batch, "format",
N_("show info and content of objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
N_("show info about objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
+ OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
+ N_("follow in-tree symlinks (used with --batch or --batch-check)")),
+ OPT_BOOL(0, "batch-all-objects", &batch.all_objects,
+ N_("show all objects with --batch or --batch-check")),
OPT_END()
};
git_config(git_cat_file_config, NULL);
- if (argc != 3 && argc != 2)
- usage_with_options(cat_file_usage, options);
-
+ batch.buffer_output = -1;
argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
if (opt) {
@@ -402,8 +524,17 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
usage_with_options(cat_file_usage, options);
}
+ if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) {
+ usage_with_options(cat_file_usage, options);
+ }
+
+ if (batch.buffer_output < 0)
+ batch.buffer_output = batch.all_objects;
+
if (batch.enabled)
return batch_objects(&batch);
- return cat_one_file(opt, exp_type, obj_name);
+ if (unknown_type && opt != 't' && opt != 's')
+ die("git cat-file --allow-unknown-type: use with -s or -t");
+ return cat_one_file(opt, exp_type, obj_name, unknown_type);
}
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 21d2bed..53a5a18 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -9,7 +9,7 @@ static int cached_attrs;
static int stdin_paths;
static const char * const check_attr_usage[] = {
N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
-N_("git check-attr --stdin [-z] [-a | --all | <attr>...] < <list-of-paths>"),
+N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
NULL
};
@@ -72,23 +72,23 @@ static void check_attr(const char *prefix, int cnt,
static void check_attr_stdin_paths(const char *prefix, int cnt,
struct git_attr_check *check)
{
- struct strbuf buf, nbuf;
- int line_termination = nul_term_line ? 0 : '\n';
-
- strbuf_init(&buf, 0);
- strbuf_init(&nbuf, 0);
- while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
- if (line_termination && buf.buf[0] == '"') {
- strbuf_reset(&nbuf);
- if (unquote_c_style(&nbuf, buf.buf, NULL))
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf unquoted = STRBUF_INIT;
+ strbuf_getline_fn getline_fn;
+
+ getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+ while (getline_fn(&buf, stdin) != EOF) {
+ if (!nul_term_line && buf.buf[0] == '"') {
+ strbuf_reset(&unquoted);
+ if (unquote_c_style(&unquoted, buf.buf, NULL))
die("line is badly quoted");
- strbuf_swap(&buf, &nbuf);
+ strbuf_swap(&buf, &unquoted);
}
check_attr(prefix, cnt, check, buf.buf);
maybe_flush_or_die(stdout, "attribute to stdout");
}
strbuf_release(&buf);
- strbuf_release(&nbuf);
+ strbuf_release(&unquoted);
}
static NORETURN void error_with_usage(const char *msg)
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index dc8d97c..1d73d3c 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -8,7 +8,7 @@
static int quiet, verbose, stdin_paths, show_non_matching, no_index;
static const char * const check_ignore_usage[] = {
"git check-ignore [<options>] <pathname>...",
-"git check-ignore [<options>] --stdin < <list-of-paths>",
+"git check-ignore [<options>] --stdin",
NULL
};
@@ -115,19 +115,19 @@ static int check_ignore(struct dir_struct *dir,
static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
{
- struct strbuf buf, nbuf;
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf unquoted = STRBUF_INIT;
char *pathspec[2] = { NULL, NULL };
- int line_termination = nul_term_line ? 0 : '\n';
+ strbuf_getline_fn getline_fn;
int num_ignored = 0;
- strbuf_init(&buf, 0);
- strbuf_init(&nbuf, 0);
- while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
- if (line_termination && buf.buf[0] == '"') {
- strbuf_reset(&nbuf);
- if (unquote_c_style(&nbuf, buf.buf, NULL))
+ getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+ while (getline_fn(&buf, stdin) != EOF) {
+ if (!nul_term_line && buf.buf[0] == '"') {
+ strbuf_reset(&unquoted);
+ if (unquote_c_style(&unquoted, buf.buf, NULL))
die("line is badly quoted");
- strbuf_swap(&buf, &nbuf);
+ strbuf_swap(&buf, &unquoted);
}
pathspec[0] = buf.buf;
num_ignored += check_ignore(dir, prefix,
@@ -135,7 +135,7 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
maybe_flush_or_die(stdout, "check-ignore to stdout");
}
strbuf_release(&buf);
- strbuf_release(&nbuf);
+ strbuf_release(&unquoted);
return num_ignored;
}
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index eaaea54..cf0f54f 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -54,7 +54,7 @@ int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
if (use_stdin) {
struct strbuf buf = STRBUF_INIT;
- while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+ while (strbuf_getline_lf(&buf, stdin) != EOF) {
check_mailmap(&mailmap, buf.buf);
maybe_flush_or_die(stdout, "stdout");
}
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index fd915d5..eac4994 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -20,7 +20,7 @@ static const char builtin_check_ref_format_usage[] =
*/
static char *collapse_slashes(const char *refname)
{
- char *ret = xmalloc(strlen(refname) + 1);
+ char *ret = xmallocz(strlen(refname));
char ch;
char prev = '/';
char *cp = ret;
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 9ca2da1..92c6967 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -11,7 +11,7 @@
#include "parse-options.h"
#define CHECKOUT_ALL 4
-static int line_termination = '\n';
+static int nul_term_line;
static int checkout_stage; /* default to checkout stage0 */
static int to_tempfile;
static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
@@ -35,7 +35,8 @@ static void write_tempfile_record(const char *name, const char *prefix)
fputs(topath[checkout_stage], stdout);
putchar('\t');
- write_name_quoted_relative(name, prefix, stdout, line_termination);
+ write_name_quoted_relative(name, prefix, stdout,
+ nul_term_line ? '\0' : '\n');
for (i = 0; i < 4; i++) {
topath[i][0] = 0;
@@ -129,36 +130,6 @@ static const char * const builtin_checkout_index_usage[] = {
static struct lock_file lock_file;
-static int option_parse_u(const struct option *opt,
- const char *arg, int unset)
-{
- int *newfd = opt->value;
-
- state.refresh_cache = 1;
- state.istate = &the_index;
- if (*newfd < 0)
- *newfd = hold_locked_index(&lock_file, 1);
- return 0;
-}
-
-static int option_parse_z(const struct option *opt,
- const char *arg, int unset)
-{
- if (unset)
- line_termination = '\n';
- else
- line_termination = 0;
- return 0;
-}
-
-static int option_parse_prefix(const struct option *opt,
- const char *arg, int unset)
-{
- state.base_dir = arg;
- state.base_dir_len = strlen(arg);
- return 0;
-}
-
static int option_parse_stage(const struct option *opt,
const char *arg, int unset)
{
@@ -170,7 +141,7 @@ static int option_parse_stage(const struct option *opt,
if ('1' <= ch && ch <= '3')
checkout_stage = arg[0] - '0';
else
- die("stage should be between 1 and 3 or all");
+ die(_("stage should be between 1 and 3 or all"));
}
return 0;
}
@@ -183,6 +154,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
int read_from_stdin = 0;
int prefix_length;
int force = 0, quiet = 0, not_new = 0;
+ int index_opt = 0;
struct option builtin_checkout_index_options[] = {
OPT_BOOL('a', "all", &all,
N_("check out all files in the index")),
@@ -191,22 +163,19 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
N_("no warning for existing files and files not in index")),
OPT_BOOL('n', "no-create", &not_new,
N_("don't checkout new files")),
- { OPTION_CALLBACK, 'u', "index", &newfd, NULL,
- N_("update stat information in the index file"),
- PARSE_OPT_NOARG, option_parse_u },
- { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
- N_("paths are separated with NUL character"),
- PARSE_OPT_NOARG, option_parse_z },
+ OPT_BOOL('u', "index", &index_opt,
+ N_("update stat information in the index file")),
+ OPT_BOOL('z', NULL, &nul_term_line,
+ N_("paths are separated with NUL character")),
OPT_BOOL(0, "stdin", &read_from_stdin,
N_("read list of paths from the standard input")),
OPT_BOOL(0, "temp", &to_tempfile,
N_("write the content to temporary files")),
- OPT_CALLBACK(0, "prefix", NULL, N_("string"),
- N_("when creating files, prepend <string>"),
- option_parse_prefix),
- OPT_CALLBACK(0, "stage", NULL, NULL,
+ OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
+ N_("when creating files, prepend <string>")),
+ { OPTION_CALLBACK, 0, "stage", NULL, "1-3|all",
N_("copy out the files from named stage"),
- option_parse_stage),
+ PARSE_OPT_NONEG, option_parse_stage },
OPT_END()
};
@@ -214,7 +183,6 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_checkout_index_usage,
builtin_checkout_index_options);
git_config(git_default_config, NULL);
- state.base_dir = "";
prefix_length = prefix ? strlen(prefix) : 0;
if (read_cache() < 0) {
@@ -227,21 +195,23 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
state.quiet = quiet;
state.not_new = not_new;
- if (state.base_dir_len || to_tempfile) {
- /* when --prefix is specified we do not
- * want to update cache.
- */
- if (state.refresh_cache) {
- rollback_lock_file(&lock_file);
- newfd = -1;
- }
- state.refresh_cache = 0;
+ if (!state.base_dir)
+ state.base_dir = "";
+ state.base_dir_len = strlen(state.base_dir);
+
+ /*
+ * when --prefix is specified we do not want to update cache.
+ */
+ if (index_opt && !state.base_dir_len && !to_tempfile) {
+ state.refresh_cache = 1;
+ state.istate = &the_index;
+ newfd = hold_locked_index(&lock_file, 1);
}
/* Check out named files first */
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- const char *p;
+ char *p;
if (all)
die("git checkout-index: don't mix '--all' and explicit filenames");
@@ -249,30 +219,31 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
die("git checkout-index: don't mix '--stdin' and explicit filenames");
p = prefix_path(prefix, prefix_length, arg);
checkout_file(p, prefix);
- if (p < arg || p > arg + strlen(arg))
- free((char *)p);
+ free(p);
}
if (read_from_stdin) {
- struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf unquoted = STRBUF_INIT;
+ strbuf_getline_fn getline_fn;
if (all)
die("git checkout-index: don't mix '--all' and '--stdin'");
- while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
- const char *p;
- if (line_termination && buf.buf[0] == '"') {
- strbuf_reset(&nbuf);
- if (unquote_c_style(&nbuf, buf.buf, NULL))
+ getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+ while (getline_fn(&buf, stdin) != EOF) {
+ char *p;
+ if (!nul_term_line && buf.buf[0] == '"') {
+ strbuf_reset(&unquoted);
+ if (unquote_c_style(&unquoted, buf.buf, NULL))
die("line is badly quoted");
- strbuf_swap(&buf, &nbuf);
+ strbuf_swap(&buf, &unquoted);
}
p = prefix_path(prefix, prefix_length, buf.buf);
checkout_file(p, prefix);
- if (p < buf.buf || p > buf.buf + buf.len)
- free((char *)p);
+ free(p);
}
- strbuf_release(&nbuf);
+ strbuf_release(&unquoted);
strbuf_release(&buf);
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3e141fc..afbff3e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -18,8 +18,8 @@
#include "xdiff-interface.h"
#include "ll-merge.h"
#include "resolve-undo.h"
+#include "submodule-config.h"
#include "submodule.h"
-#include "argv-array.h"
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
@@ -36,6 +36,8 @@ struct checkout_opts {
int writeout_stage;
int overwrite_ignore;
int ignore_skipworktree;
+ int ignore_other_worktrees;
+ int show_progress;
const char *new_branch;
const char *new_branch_force;
@@ -54,8 +56,8 @@ static int post_checkout_hook(struct commit *old, struct commit *new,
int changed)
{
return run_hook_le(NULL, "post-checkout",
- sha1_to_hex(old ? old->object.sha1 : null_sha1),
- sha1_to_hex(new ? new->object.sha1 : null_sha1),
+ sha1_to_hex(old ? old->object.oid.hash : null_sha1),
+ sha1_to_hex(new ? new->object.oid.hash : null_sha1),
changed ? "1" : "0", NULL);
/* "new" can be NULL when checking out from the index before
a commit exists. */
@@ -152,8 +154,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
return 0;
}
-static int checkout_stage(int stage, struct cache_entry *ce, int pos,
- struct checkout *state)
+static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
+ const struct checkout *state)
{
while (pos < active_nr &&
!strcmp(active_cache[pos]->name, ce->name)) {
@@ -167,7 +169,7 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos,
return error(_("path '%s' does not have their version"), ce->name);
}
-static int checkout_merged(int pos, struct checkout *state)
+static int checkout_merged(int pos, const struct checkout *state)
{
struct cache_entry *ce = active_cache[pos];
const char *path = ce->name;
@@ -240,7 +242,6 @@ static int checkout_paths(const struct checkout_opts *opts,
struct checkout state;
static char *ps_matched;
unsigned char rev[20];
- int flag;
struct commit *head;
int errs = 0;
struct lock_file *lock_file;
@@ -275,12 +276,12 @@ static int checkout_paths(const struct checkout_opts *opts,
hold_locked_index(lock_file, 1);
if (read_cache_preload(&opts->pathspec) < 0)
- return error(_("corrupt index file"));
+ return error(_("index file corrupt"));
if (opts->source_tree)
read_tree_some(opts->source_tree, &opts->pathspec);
- ps_matched = xcalloc(1, opts->pathspec.nr);
+ ps_matched = xcalloc(opts->pathspec.nr, 1);
/*
* Make sure all pathspecs participated in locating the paths
@@ -373,7 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- read_ref_full("HEAD", 0, rev, &flag);
+ read_ref_full("HEAD", 0, rev, NULL);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -399,7 +400,7 @@ static void describe_detached_head(const char *msg, struct commit *commit)
if (!parse_commit(commit))
pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
fprintf(stderr, "%s %s... %s\n", msg,
- find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
strbuf_release(&sb);
}
@@ -416,7 +417,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.reset = 1;
opts.merge = 1;
opts.fn = oneway_merge;
- opts.verbose_update = !o->quiet && isatty(2);
+ opts.verbose_update = o->show_progress;
opts.src_index = &the_index;
opts.dst_index = &the_index;
parse_tree(tree);
@@ -441,6 +442,11 @@ struct branch_info {
const char *name; /* The short name used */
const char *path; /* The full name of a real branch */
struct commit *commit; /* The named commit */
+ /*
+ * if not null the branch is detached because it's already
+ * checked out in this checkout
+ */
+ char *checkout;
};
static void setup_branch_path(struct branch_info *branch)
@@ -464,7 +470,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
hold_locked_index(lock_file, 1);
if (read_cache_preload(NULL) < 0)
- return error(_("corrupt index file"));
+ return error(_("index file corrupt"));
resolve_undo_clear();
if (opts->force) {
@@ -495,7 +501,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
topts.update = 1;
topts.merge = 1;
topts.gently = opts->merge && old->commit;
- topts.verbose_update = !opts->quiet && isatty(2);
+ topts.verbose_update = opts->show_progress;
topts.fn = twoway_merge;
if (opts->overwrite_ignore) {
topts.dir = xcalloc(1, sizeof(*topts.dir));
@@ -503,10 +509,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
setup_standard_excludes(topts.dir);
}
tree = parse_tree_indirect(old->commit ?
- old->commit->object.sha1 :
+ old->commit->object.oid.hash :
EMPTY_TREE_SHA1_BIN);
init_tree_desc(&trees[0], tree->buffer, tree->size);
- tree = parse_tree_indirect(new->commit->object.sha1);
+ tree = parse_tree_indirect(new->commit->object.oid.hash);
init_tree_desc(&trees[1], tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
@@ -542,7 +548,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
* entries in the index.
*/
- add_files_to_cache(NULL, NULL, 0);
+ add_files_to_cache(NULL, NULL, 0, 0);
/*
* NEEDSWORK: carrying over local changes
* when branches have different end-of-line
@@ -561,10 +567,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.ancestor = old->name;
o.branch1 = new->name;
o.branch2 = "local";
- merge_trees(&o, new->commit->tree, work,
+ ret = merge_trees(&o, new->commit->tree, work,
old->commit->tree, &result);
+ if (ret < 0)
+ exit(128);
ret = reset_tree(new->commit->tree, opts, 0,
writeout_error);
+ strbuf_release(&o.obuf);
if (ret)
return ret;
}
@@ -605,19 +614,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (opts->new_branch) {
if (opts->new_orphan_branch) {
if (opts->new_branch_log && !log_all_ref_updates) {
- int temp;
- char log_file[PATH_MAX];
- char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
-
- temp = log_all_ref_updates;
- log_all_ref_updates = 1;
- if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
- fprintf(stderr, _("Can not do reflog for '%s'\n"),
- opts->new_orphan_branch);
- log_all_ref_updates = temp;
+ int ret;
+ char *refname;
+ struct strbuf err = STRBUF_INIT;
+
+ refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
+ ret = safe_create_reflog(refname, 1, &err);
+ free(refname);
+ if (ret) {
+ fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
+ opts->new_orphan_branch, err.buf);
+ strbuf_release(&err);
return;
}
- log_all_ref_updates = temp;
+ strbuf_release(&err);
}
}
else
@@ -633,7 +643,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
old_desc = old->name;
if (!old_desc && old->commit)
- old_desc = sha1_to_hex(old->commit->object.sha1);
+ old_desc = oid_to_hex(&old->commit->object.oid);
reflog_msg = getenv("GIT_REFLOG_ACTION");
if (!reflog_msg)
@@ -645,15 +655,17 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
if (!strcmp(new->name, "HEAD") && !new->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.sha1, NULL,
+ update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
if (!opts->quiet) {
- if (old->path && advice_detached_head)
+ if (old->path &&
+ advice_detached_head && !opts->force_detach)
detach_advice(new->name);
describe_detached_head(_("HEAD is now at"), new->commit);
}
} else if (new->path) { /* Switch branches. */
- create_symref("HEAD", new->path, msg.buf);
+ if (create_symref("HEAD", new->path, msg.buf) < 0)
+ die(_("unable to update HEAD"));
if (!opts->quiet) {
if (old->path && !strcmp(new->path, old->path)) {
if (opts->new_branch_force)
@@ -685,18 +697,17 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
}
static int add_pending_uninteresting_ref(const char *refname,
- const unsigned char *sha1,
+ const struct object_id *oid,
int flags, void *cb_data)
{
- add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
+ add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
return 0;
}
static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
{
strbuf_addstr(sb, " ");
- strbuf_addstr(sb,
- find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+ strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV);
strbuf_addch(sb, ' ');
if (!parse_commit(commit))
pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@ -743,11 +754,18 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
if (advice_detached_head)
fprintf(stderr,
- _(
+ Q_(
+ /* The singular version */
+ "If you want to keep it by creating a new branch, "
+ "this may be a good time\nto do so with:\n\n"
+ " git branch <new-branch-name> %s\n\n",
+ /* The plural version */
"If you want to keep them by creating a new branch, "
"this may be a good time\nto do so with:\n\n"
- " git branch <new-branch-name> %s\n\n"),
- find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+ " git branch <new-branch-name> %s\n\n",
+ /* Give ngettext() the count */
+ lost),
+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
}
/*
@@ -765,10 +783,10 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
setup_revisions(0, NULL, &revs, NULL);
object->flags &= ~UNINTERESTING;
- add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+ 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.sha1, UNINTERESTING);
+ add_pending_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
refs = revs.pending;
revs.leak_pending = 1;
@@ -883,10 +901,11 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
struct branch_info *new,
- struct tree **source_tree,
- unsigned char rev[20],
- const char **new_branch)
+ struct checkout_opts *opts,
+ unsigned char rev[20])
{
+ struct tree **source_tree = &opts->source_tree;
+ const char **new_branch = &opts->new_branch;
int argcount = 0;
unsigned char branch_rev[20];
const char *arg;
@@ -965,7 +984,8 @@ static int parse_branchname_arg(int argc, const char **argv,
*/
int recover_with_dwim = dwim_new_local_branch_ok;
- if (check_filename(NULL, arg) && !has_dash_dash)
+ if (!has_dash_dash &&
+ (check_filename(NULL, arg) || !no_wildcard(arg)))
recover_with_dwim = 0;
/*
* Accept "git checkout foo" and "git checkout foo --"
@@ -1086,6 +1106,17 @@ static int checkout_branch(struct checkout_opts *opts,
die(_("Cannot switch branch to a non-commit '%s'"),
new->name);
+ if (new->path && !opts->force_detach && !opts->new_branch &&
+ !opts->ignore_other_worktrees) {
+ unsigned char sha1[20];
+ int flag;
+ char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
+ if (head_ref &&
+ (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
+ die_if_checked_out(new->path, 1);
+ free(head_ref);
+ }
+
if (!new->commit && opts->new_branch) {
unsigned char rev[20];
int flag;
@@ -1110,7 +1141,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
N_("create/reset and checkout a branch")),
OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
- OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
+ OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"),
BRANCH_TRACK_EXPLICIT),
OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
@@ -1128,6 +1159,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
N_("do not limit pathspecs to sparse entries only")),
OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
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")),
+ OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
OPT_END(),
};
@@ -1135,6 +1169,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
memset(&new, 0, sizeof(new));
opts.overwrite_ignore = 1;
opts.prefix = prefix;
+ opts.show_progress = -1;
gitmodules_config();
git_config(git_checkout_config, &opts);
@@ -1144,6 +1179,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ if (opts.show_progress < 0) {
+ if (opts.quiet)
+ opts.show_progress = 0;
+ else
+ opts.show_progress = isatty(2);
+ }
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
@@ -1197,8 +1239,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.source_tree,
- rev, &opts.new_branch);
+ &new, &opts, rev);
argv += n;
argc -= n;
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 6dcb72e..0371010 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -10,7 +10,6 @@
#include "cache.h"
#include "dir.h"
#include "parse-options.h"
-#include "refs.h"
#include "string-list.h"
#include "quote.h"
#include "column.h"
@@ -155,13 +154,11 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
struct strbuf quoted = STRBUF_INIT;
struct dirent *e;
int res = 0, ret = 0, gone = 1, original_len = path->len, len;
- unsigned char submodule_head[20];
struct string_list dels = STRING_LIST_INIT_DUP;
*dir_gone = 1;
- if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
- !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+ if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
if (!quiet) {
quote_path_relative(path->buf, prefix, &quoted);
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
@@ -184,8 +181,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
return res;
}
- if (path->buf[original_len - 1] != '/')
- strbuf_addch(path, '/');
+ strbuf_complete(path, '/');
len = path->len;
while ((e = readdir(dir)) != NULL) {
@@ -547,7 +543,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
int eof = 0;
int i;
- chosen = xmalloc(sizeof(int) * stuff->nr);
+ ALLOC_ARRAY(chosen, stuff->nr);
/* set chosen as uninitialized */
for (i = 0; i < stuff->nr; i++)
chosen[i] = -1;
@@ -574,7 +570,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
clean_get_color(CLEAN_COLOR_RESET));
}
- if (strbuf_getline(&choice, stdin, '\n') != EOF) {
+ if (strbuf_getline_lf(&choice, stdin) != EOF) {
strbuf_trim(&choice);
} else {
eof = 1;
@@ -619,7 +615,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
nr += chosen[i];
}
- result = xcalloc(nr + 1, sizeof(int));
+ result = xcalloc(st_add(nr, 1), sizeof(int));
for (i = 0; i < stuff->nr && j < nr; i++) {
if (chosen[i])
result[j++] = i;
@@ -656,7 +652,7 @@ static int filter_by_patterns_cmd(void)
clean_print_color(CLEAN_COLOR_PROMPT);
printf(_("Input ignore patterns>> "));
clean_print_color(CLEAN_COLOR_RESET);
- if (strbuf_getline(&confirm, stdin, '\n') != EOF)
+ if (strbuf_getline_lf(&confirm, stdin) != EOF)
strbuf_trim(&confirm);
else
putchar('\n');
@@ -754,7 +750,7 @@ static int ask_each_cmd(void)
qname = quote_path_relative(item->string, NULL, &buf);
/* TRANSLATORS: Make sure to keep [y/N] as is */
printf(_("Remove %s [y/N]? "), qname);
- if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+ if (strbuf_getline_lf(&confirm, stdin) != EOF) {
strbuf_trim(&confirm);
} else {
putchar('\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index e18839d..f044a8c 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -40,6 +40,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, option_recursive;
+static int option_shallow_submodules;
static char *option_template, *option_depth;
static char *option_origin = NULL;
static char *option_branch = NULL;
@@ -47,9 +48,11 @@ static const char *real_git_dir;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
-static struct string_list option_config;
-static struct string_list option_reference;
+static enum transport_family family;
+static struct string_list option_config = STRING_LIST_INIT_NODUP;
+static struct string_list option_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
+static int max_jobs = -1;
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
@@ -72,6 +75,8 @@ static struct option builtin_clone_options[] = {
N_("initialize submodules in the clone")),
OPT_BOOL(0, "recurse-submodules", &option_recursive,
N_("initialize submodules in the clone")),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
N_("directory from which templates will be used")),
OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
@@ -88,93 +93,153 @@ static struct option builtin_clone_options[] = {
N_("create a shallow clone of that depth")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ 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"),
N_("separate git dir from working tree")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
N_("set config inside the new repository")),
+ 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"),
+ TRANSPORT_FAMILY_IPV6),
OPT_END()
};
-static const char *argv_submodule[] = {
- "submodule", "update", "--init", "--recursive", NULL
-};
-
-static char *get_repo_path(const char *repo, int *is_bundle)
+static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
static char *bundle_suffix[] = { ".bundle", "" };
+ size_t baselen = path->len;
struct stat st;
int i;
for (i = 0; i < ARRAY_SIZE(suffix); i++) {
- const char *path;
- path = mkpath("%s%s", repo, suffix[i]);
- if (stat(path, &st))
+ strbuf_setlen(path, baselen);
+ strbuf_addstr(path, suffix[i]);
+ if (stat(path->buf, &st))
continue;
- if (S_ISDIR(st.st_mode) && is_git_directory(path)) {
+ if (S_ISDIR(st.st_mode) && is_git_directory(path->buf)) {
*is_bundle = 0;
- return xstrdup(absolute_path(path));
+ return path->buf;
} else if (S_ISREG(st.st_mode) && st.st_size > 8) {
/* Is it a "gitfile"? */
char signature[8];
- int len, fd = open(path, O_RDONLY);
+ const char *dst;
+ int len, fd = open(path->buf, O_RDONLY);
if (fd < 0)
continue;
len = read_in_full(fd, signature, 8);
close(fd);
if (len != 8 || strncmp(signature, "gitdir: ", 8))
continue;
- path = read_gitfile(path);
- if (path) {
+ dst = read_gitfile(path->buf);
+ if (dst) {
*is_bundle = 0;
- return xstrdup(absolute_path(path));
+ return dst;
}
}
}
for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
- const char *path;
- path = mkpath("%s%s", repo, bundle_suffix[i]);
- if (!stat(path, &st) && S_ISREG(st.st_mode)) {
+ strbuf_setlen(path, baselen);
+ strbuf_addstr(path, bundle_suffix[i]);
+ if (!stat(path->buf, &st) && S_ISREG(st.st_mode)) {
*is_bundle = 1;
- return xstrdup(absolute_path(path));
+ return path->buf;
}
}
return NULL;
}
+static char *get_repo_path(const char *repo, int *is_bundle)
+{
+ struct strbuf path = STRBUF_INIT;
+ const char *raw;
+ char *canon;
+
+ strbuf_addstr(&path, repo);
+ raw = get_repo_path_1(&path, is_bundle);
+ canon = raw ? xstrdup(absolute_path(raw)) : NULL;
+ strbuf_release(&path);
+ return canon;
+}
+
static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
{
- const char *end = repo + strlen(repo), *start;
+ const char *end = repo + strlen(repo), *start, *ptr;
size_t len;
char *dir;
/*
+ * Skip scheme.
+ */
+ start = strstr(repo, "://");
+ if (start == NULL)
+ start = repo;
+ else
+ start += 3;
+
+ /*
+ * Skip authentication data. The stripping does happen
+ * greedily, such that we strip up to the last '@' inside
+ * the host part.
+ */
+ for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+ if (*ptr == '@')
+ start = ptr + 1;
+ }
+
+ /*
* Strip trailing spaces, slashes and /.git
*/
- while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+ while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
end--;
- if (end - repo > 5 && is_dir_sep(end[-5]) &&
+ if (end - start > 5 && is_dir_sep(end[-5]) &&
!strncmp(end - 4, ".git", 4)) {
end -= 5;
- while (repo < end && is_dir_sep(end[-1]))
+ while (start < end && is_dir_sep(end[-1]))
end--;
}
/*
- * Find last component, but be prepared that repo could have
- * the form "remote.example.com:foo.git", i.e. no slash
- * in the directory part.
+ * Strip trailing port number if we've got only a
+ * hostname (that is, there is no dir separator but a
+ * colon). This check is required such that we do not
+ * strip URI's like '/foo/bar:2222.git', which should
+ * result in a dir '2222' being guessed due to backwards
+ * compatibility.
+ */
+ if (memchr(start, '/', end - start) == NULL
+ && memchr(start, ':', end - start) != NULL) {
+ ptr = end;
+ while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+ ptr--;
+ if (start < ptr && ptr[-1] == ':')
+ end = ptr - 1;
+ }
+
+ /*
+ * Find last component. To remain backwards compatible we
+ * also regard colons as path separators, such that
+ * cloning a repository 'foo:bar.git' would result in a
+ * directory 'bar' being guessed.
*/
- start = end;
- while (repo < start && !is_dir_sep(start[-1]) && start[-1] != ':')
- start--;
+ ptr = end;
+ while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+ ptr--;
+ start = ptr;
/*
* Strip .{bundle,git}.
*/
- strip_suffix(start, is_bundle ? ".bundle" : ".git" , &len);
+ len = end - start;
+ strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+ if (!len || (len == 1 && *start == '/'))
+ die(_("No directory name could be guessed.\n"
+ "Please specify a directory on the command line"));
if (is_bare)
dir = xstrfmt("%.*s.git", (int)len, start);
@@ -236,9 +301,14 @@ static int add_one_reference(struct string_list_item *item, void *cb_data)
char *ref_git_git = mkpathdup("%s/.git", ref_git);
free(ref_git);
ref_git = ref_git_git;
- } else if (!is_directory(mkpath("%s/objects", ref_git)))
+ } else if (!is_directory(mkpath("%s/objects", ref_git))) {
+ struct strbuf sb = STRBUF_INIT;
+ if (get_common_dir(&sb, ref_git))
+ die(_("reference repository '%s' as a linked checkout is not supported yet."),
+ item->string);
die(_("reference repository '%s' is not a local repository."),
item->string);
+ }
if (!access(mkpath("%s/shallow", ref_git), F_OK))
die(_("reference repository '%s' is shallow"), item->string);
@@ -276,17 +346,18 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
FILE *in = fopen(src->buf, "r");
struct strbuf line = STRBUF_INIT;
- while (strbuf_getline(&line, in, '\n') != EOF) {
- char *abs_path, abs_buf[PATH_MAX];
+ while (strbuf_getline(&line, in) != EOF) {
+ char *abs_path;
if (!line.len || line.buf[0] == '#')
continue;
if (is_absolute_path(line.buf)) {
add_to_alternates_file(line.buf);
continue;
}
- abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
- normalize_path_copy(abs_buf, abs_path);
- add_to_alternates_file(abs_buf);
+ abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
+ normalize_path_copy(abs_path, abs_path);
+ add_to_alternates_file(abs_path);
+ free(abs_path);
}
strbuf_release(&line);
fclose(in);
@@ -365,8 +436,10 @@ static void clone_local(const char *src_repo, const char *dest_repo)
} else {
struct strbuf src = STRBUF_INIT;
struct strbuf dest = STRBUF_INIT;
- strbuf_addf(&src, "%s/objects", src_repo);
- strbuf_addf(&dest, "%s/objects", dest_repo);
+ get_common_dir(&src, src_repo);
+ get_common_dir(&dest, dest_repo);
+ strbuf_addstr(&src, "/objects");
+ strbuf_addstr(&dest, "/objects");
copy_or_link_directory(&src, &dest, src_repo, src.len);
strbuf_release(&src);
strbuf_release(&dest);
@@ -483,16 +556,26 @@ static void write_remote_refs(const struct ref *local_refs)
{
const struct ref *r;
- lock_packed_refs(LOCK_DIE_ON_ERROR);
+ struct ref_transaction *t;
+ struct strbuf err = STRBUF_INIT;
+
+ t = ref_transaction_begin(&err);
+ if (!t)
+ die("%s", err.buf);
for (r = local_refs; r; r = r->next) {
if (!r->peer_ref)
continue;
- add_packed_ref(r->peer_ref->name, r->old_sha1);
+ if (ref_transaction_create(t, r->peer_ref->name, r->old_oid.hash,
+ 0, NULL, &err))
+ die("%s", err.buf);
}
- if (commit_packed_refs())
- die_errno("unable to overwrite old ref-pack file");
+ if (initial_ref_transaction_commit(t, &err))
+ die("%s", err.buf);
+
+ strbuf_release(&err);
+ ref_transaction_free(t);
}
static void write_followtags(const struct ref *refs, const char *msg)
@@ -503,9 +586,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
continue;
if (ends_with(ref->name, "^{}"))
continue;
- if (!has_sha1_file(ref->old_sha1))
+ if (!has_object_file(&ref->old_oid))
continue;
- update_ref(msg, ref->name, ref->old_sha1,
+ update_ref(msg, ref->name, ref->old_oid.hash,
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
}
}
@@ -525,7 +608,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
if (!ref)
return -1;
- hashcpy(sha1, ref->old_sha1);
+ hashcpy(sha1, ref->old_oid.hash);
*rm = ref->next;
return 0;
}
@@ -541,13 +624,13 @@ static void update_remote_refs(const struct ref *refs,
const struct ref *rm = mapped_refs;
if (check_connectivity) {
- if (transport->progress)
- fprintf(stderr, _("Checking connectivity... "));
- if (check_everything_connected_with_transport(iterate_ref_map,
- 0, &rm, transport))
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+ opt.transport = transport;
+ opt.progress = transport->progress;
+
+ if (check_connected(iterate_ref_map, &rm, &opt))
die(_("remote did not send all necessary objects"));
- if (transport->progress)
- fprintf(stderr, _("done.\n"));
}
if (refs) {
@@ -560,9 +643,11 @@ static void update_remote_refs(const struct ref *refs,
struct strbuf head_ref = STRBUF_INIT;
strbuf_addstr(&head_ref, branch_top);
strbuf_addstr(&head_ref, "HEAD");
- create_symref(head_ref.buf,
- remote_head_points_at->peer_ref->name,
- msg);
+ if (create_symref(head_ref.buf,
+ remote_head_points_at->peer_ref->name,
+ msg) < 0)
+ die(_("unable to update %s"), head_ref.buf);
+ strbuf_release(&head_ref);
}
}
@@ -572,16 +657,17 @@ static void update_head(const struct ref *our, const struct ref *remote,
const char *head;
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
/* Local default branch link */
- create_symref("HEAD", our->name, NULL);
+ if (create_symref("HEAD", our->name, NULL) < 0)
+ die(_("unable to update HEAD"));
if (!option_bare) {
- update_ref(msg, "HEAD", our->old_sha1, NULL, 0,
+ update_ref(msg, "HEAD", our->old_oid.hash, 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_sha1);
+ struct commit *c = lookup_commit_reference(our->old_oid.hash);
/* --branch specifies a non-branch (i.e. tags), detach HEAD */
- update_ref(msg, "HEAD", c->object.sha1,
+ update_ref(msg, "HEAD", c->object.oid.hash,
NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
} else if (remote) {
/*
@@ -589,7 +675,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
* HEAD points to a branch but we don't know which one.
* Detach HEAD in all these cases.
*/
- update_ref(msg, "HEAD", remote->old_sha1,
+ update_ref(msg, "HEAD", remote->old_oid.hash,
NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
}
}
@@ -648,15 +734,26 @@ static int checkout(void)
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(sha1), "1", NULL);
- if (!err && option_recursive)
- err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+ if (!err && option_recursive) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+ argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+ if (option_shallow_submodules == 1)
+ argv_array_push(&args, "--depth=1");
+
+ if (max_jobs != -1)
+ argv_array_pushf(&args, "--jobs=%d", max_jobs);
+
+ err = run_command_v_opt(args.argv, RUN_GIT_CMD);
+ argv_array_clear(&args);
+ }
return err;
}
static int write_one_config(const char *key, const char *value, void *data)
{
- return git_config_set_multivar(key, value ? value : "true", "^$", 0);
+ return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0);
}
static void write_config(struct string_list *config)
@@ -666,7 +763,7 @@ static void write_config(struct string_list *config)
for (i = 0; i < config->nr; i++) {
if (git_config_parse_parameter(config->items[i].string,
write_one_config, NULL) < 0)
- die("unable to write parameters to config file");
+ die(_("unable to write parameters to config file"));
}
}
@@ -725,11 +822,15 @@ static void write_refspec_config(const char *src_ref_prefix,
static void dissociate_from_references(void)
{
static const char* argv[] = { "repack", "-a", "-d", NULL };
+ char *alternates = git_pathdup("objects/info/alternates");
- if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
- die(_("cannot repack to clean up"));
- if (unlink(git_path("objects/info/alternates")) && errno != ENOENT)
- die_errno(_("cannot unlink temporary alternates file"));
+ if (!access(alternates, F_OK)) {
+ if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
+ die(_("cannot repack to clean up"));
+ if (unlink(alternates) && errno != ENOENT)
+ die_errno(_("cannot unlink temporary alternates file"));
+ }
+ free(alternates);
}
int cmd_clone(int argc, const char **argv, const char *prefix)
@@ -878,10 +979,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_reference.nr)
setup_reference();
- else if (option_dissociate) {
- warning(_("--dissociate given, but there is no --reference"));
- option_dissociate = 0;
- }
fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern);
@@ -891,6 +988,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
transport_set_verbosity(transport, option_verbosity, option_progress);
+ transport->family = family;
path = get_repo_path(remote->url[0], &is_bundle);
is_local = option_local != 0 && path && !is_bundle;
@@ -940,7 +1038,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* remote HEAD check.
*/
for (ref = refs; ref; ref = ref->next)
- if (is_null_sha1(ref->old_sha1)) {
+ if (is_null_oid(&ref->old_oid)) {
complete_refs_before_fetch = 0;
break;
}
@@ -995,8 +1093,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_unlock_pack(transport);
transport_disconnect(transport);
- if (option_dissociate)
+ if (option_dissociate) {
+ close_all_packs();
dissociate_from_references();
+ }
junk_mode = JUNK_LEAVE_REPO;
err = checkout();
diff --git a/builtin/column.c b/builtin/column.c
index 449413c..33314b4 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -51,7 +51,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
die(_("--command must be the first argument"));
}
finalize_colopts(&colopts, -1);
- while (!strbuf_getline(&sb, stdin, '\n'))
+ while (!strbuf_getline(&sb, stdin))
string_list_append(&list, sb.buf);
print_columns(&list, colopts, &copts);
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 25aa2cd..8a674bc 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -10,17 +10,17 @@
#include "utf8.h"
#include "gpg-interface.h"
-static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog";
+static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1>";
static const char *sign_commit;
static void new_parent(struct commit *parent, struct commit_list **parents_p)
{
- unsigned char *sha1 = parent->object.sha1;
+ struct object_id *oid = &parent->object.oid;
struct commit_list *parents;
for (parents = *parents_p; parents; parents = parents->next) {
if (parents->item == parent) {
- error("duplicate parent %s ignored", sha1_to_hex(sha1));
+ error("duplicate parent %s ignored", oid_to_hex(oid));
return;
}
parents_p = &parents->next;
@@ -33,10 +33,6 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, NULL);
if (status)
return status;
- if (!strcmp(var, "commit.gpgsign")) {
- sign_commit = git_config_bool(var, value) ? "" : NULL;
- return 0;
- }
return git_default_config(var, value, cb);
}
diff --git a/builtin/commit.c b/builtin/commit.c
index c2ebea4..77e3dc8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -32,6 +32,7 @@
#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>..."),
@@ -91,8 +92,9 @@ 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 const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
static struct lock_file false_lock; /* used only for partial commits */
static enum {
@@ -113,6 +115,7 @@ static char *fixup_message, *squash_message;
static int all, also, interactive, patch_interactive, only, amend, signoff;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
static char *sign_commit;
@@ -166,11 +169,11 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
static void determine_whence(struct wt_status *s)
{
- if (file_exists(git_path("MERGE_HEAD")))
+ if (file_exists(git_path_merge_head()))
whence = FROM_MERGE;
- else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
+ else if (file_exists(git_path_cherry_pick_head())) {
whence = FROM_CHERRY_PICK;
- if (file_exists(git_path("sequencer")))
+ if (file_exists(git_path(SEQ_DIR)))
sequencer_in_use = 1;
}
else
@@ -185,6 +188,7 @@ static void status_init_config(struct wt_status *s, config_fn_t fn)
gitmodules_config();
git_config(fn, s);
determine_whence(s);
+ init_diff_ui_defaults();
s->hints = advice_status_hints; /* must come after git_config() */
}
@@ -299,7 +303,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.sha1);
+ tree = parse_tree_indirect(current_head->object.oid.hash);
if (!tree)
die(_("failed to unpack HEAD tree object"));
parse_tree(tree);
@@ -324,6 +328,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
struct string_list partial;
struct pathspec pathspec;
int refresh_flags = REFRESH_QUIET;
+ const char *ret;
if (is_status)
refresh_flags |= REFRESH_UNMERGED;
@@ -344,7 +349,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
die(_("unable to create temporary index"));
old_index_env = getenv(INDEX_ENVIRONMENT);
- setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
+ setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
die(_("interactive add failed"));
@@ -355,7 +360,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
unsetenv(INDEX_ENVIRONMENT);
discard_cache();
- read_cache_from(index_lock.filename.buf);
+ read_cache_from(get_lock_file_path(&index_lock));
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
@@ -365,7 +370,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
warning(_("Failed to update main cache tree"));
commit_style = COMMIT_NORMAL;
- return index_lock.filename.buf;
+ return get_lock_file_path(&index_lock);
}
/*
@@ -382,13 +387,13 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
*/
if (all || (also && pathspec.nr)) {
hold_locked_index(&index_lock, 1);
- add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+ add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
refresh_cache_or_die(refresh_flags);
update_main_cache_tree(WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
- return index_lock.filename.buf;
+ return get_lock_file_path(&index_lock);
}
/*
@@ -404,10 +409,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
if (active_cache_changed
- || !cache_tree_fully_valid(active_cache_tree)) {
+ || !cache_tree_fully_valid(active_cache_tree))
update_main_cache_tree(WRITE_TREE_SILENT);
- active_cache_changed = 1;
- }
if (active_cache_changed) {
if (write_locked_index(&the_index, &index_lock,
COMMIT_LOCK))
@@ -475,9 +478,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
die(_("unable to write temporary index file"));
discard_cache();
- read_cache_from(false_lock.filename.buf);
-
- return false_lock.filename.buf;
+ ret = get_lock_file_path(&false_lock);
+ read_cache_from(ret);
+ return ret;
}
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
@@ -694,7 +697,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
}
- if (message.len) {
+ if (have_option_m) {
strbuf_addbuf(&sb, &message);
hook_arg1 = "message";
} else if (logfile && !strcmp(logfile, "-")) {
@@ -712,7 +715,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
char *buffer;
buffer = strstr(use_message_buffer, "\n\n");
if (buffer)
- strbuf_addstr(&sb, buffer + 2);
+ strbuf_addstr(&sb, skip_blank_lines(buffer + 2));
hook_arg1 = "commit";
hook_arg2 = use_message;
} else if (fixup_message) {
@@ -725,12 +728,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
format_commit_message(commit, "fixup! %s\n\n",
&sb, &ctx);
hook_arg1 = "message";
- } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
- if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
+ } else if (!stat(git_path_merge_msg(), &statbuf)) {
+ /*
+ * prepend SQUASH_MSG here if it exists and a
+ * "merge --squash" was originally performed
+ */
+ if (!stat(git_path_squash_msg(), &statbuf)) {
+ if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
+ die_errno(_("could not read SQUASH_MSG"));
+ hook_arg1 = "squash";
+ } else
+ hook_arg1 = "merge";
+ if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0)
die_errno(_("could not read MERGE_MSG"));
- hook_arg1 = "merge";
- } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
- if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
+ } else if (!stat(git_path_squash_msg(), &statbuf)) {
+ if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
die_errno(_("could not read SQUASH_MSG"));
hook_arg1 = "squash";
} else if (template_file) {
@@ -761,9 +773,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
hook_arg2 = "";
}
- s->fp = fopen(git_path(commit_editmsg), "w");
+ s->fp = fopen_for_writing(git_path_commit_editmsg());
if (s->fp == NULL)
- die_errno(_("could not open '%s'"), git_path(commit_editmsg));
+ die_errno(_("could not open '%s'"), git_path_commit_editmsg());
/* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
old_display_comment_prefix = s->display_comment_prefix;
@@ -776,7 +788,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
s->hints = 0;
if (clean_message_contents)
- stripspace(&sb, 0);
+ strbuf_stripspace(&sb, 0);
if (signoff)
append_signoff(&sb, ignore_non_trailer(&sb), 0);
@@ -856,7 +868,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
_("%s"
"Date: %s"),
ident_shown++ ? "" : "\n",
- show_ident_date(&ai, DATE_NORMAL));
+ show_ident_date(&ai, DATE_MODE(NORMAL)));
if (!committer_ident_sufficiently_given())
status_printf_ln(s, GIT_COLOR_NORMAL,
@@ -940,7 +952,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
- git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+ git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL))
return 0;
if (use_editor) {
@@ -948,7 +960,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
const char *env[2] = { NULL };
env[0] = index;
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
- if (launch_editor(git_path(commit_editmsg), NULL, env)) {
+ if (launch_editor(git_path_commit_editmsg(), NULL, env)) {
fprintf(stderr,
_("Please supply the message using either -m or -F option.\n"));
exit(1);
@@ -956,7 +968,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
if (!no_verify &&
- run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+ run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) {
return 0;
}
@@ -1015,7 +1027,7 @@ static int template_untouched(struct strbuf *sb)
if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
return 0;
- stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+ strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
if (!skip_prefix(sb->buf, tmpl.buf, &start))
start = sb->buf;
strbuf_release(&tmpl);
@@ -1046,7 +1058,7 @@ static const char *find_author_by_nickname(const char *name)
commit = get_revision(&revs);
if (commit) {
struct pretty_print_context ctx = {0};
- ctx.date_mode = DATE_NORMAL;
+ ctx.date_mode.type = DATE_NORMAL;
strbuf_release(&buf);
format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
clear_mailmap(&mailmap);
@@ -1162,9 +1174,9 @@ 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 (message.len && f > 0)
+ if (have_option_m && f > 0)
die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
- if (f || message.len)
+ if (f || have_option_m)
template_file = NULL;
if (edit_message)
use_message = edit_message;
@@ -1366,13 +1378,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
fd = hold_locked_index(&index_lock, 0);
- if (0 <= fd)
- update_index_if_able(&the_index, &index_lock);
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(&s);
+ if (0 <= fd)
+ update_index_if_able(&the_index, &index_lock);
+
if (s.relative_paths)
s.prefix = prefix;
@@ -1504,6 +1517,11 @@ static int git_commit_config(const char *k, const char *v, void *cb)
sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
}
+ if (!strcmp(k, "commit.verbose")) {
+ int is_bool;
+ config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+ return 0;
+ }
status = git_gpg_config(k, v, NULL);
if (status)
@@ -1537,8 +1555,10 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
return code;
n = snprintf(buf, sizeof(buf), "%s %s\n",
sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+ sigchain_push(SIGPIPE, SIG_IGN);
write_in_full(proc.in, buf, n);
close(proc.in);
+ sigchain_pop(SIGPIPE);
return finish_command(&proc);
}
@@ -1597,7 +1617,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
OPT_BOOL('o', "only", &only, N_("commit only specified files")),
- OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
+ OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
STATUS_FORMAT_SHORT),
@@ -1648,9 +1668,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
+ verbose = -1; /* unspecified */
argc = parse_and_validate_options(argc, argv, builtin_commit_options,
builtin_commit_usage,
prefix, current_head, &s);
+ if (verbose == -1)
+ verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
+
if (dry_run)
return dry_run_commit(argc, argv, prefix, current_head, &s);
index_file = prepare_index(argc, argv, prefix, current_head, 0);
@@ -1683,11 +1707,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!reflog_msg)
reflog_msg = "commit (merge)";
pptr = &commit_list_insert(current_head, pptr)->next;
- fp = fopen(git_path("MERGE_HEAD"), "r");
+ fp = fopen(git_path_merge_head(), "r");
if (fp == NULL)
die_errno(_("could not open '%s' for reading"),
- git_path("MERGE_HEAD"));
- while (strbuf_getline(&m, fp, '\n') != EOF) {
+ git_path_merge_head());
+ while (strbuf_getline_lf(&m, fp) != EOF) {
struct commit *parent;
parent = get_merge_parent(m.buf);
@@ -1697,8 +1721,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
fclose(fp);
strbuf_release(&m);
- if (!stat(git_path("MERGE_MODE"), &statbuf)) {
- if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
+ if (!stat(git_path_merge_mode(), &statbuf)) {
+ if (strbuf_read_file(&sb, git_path_merge_mode(), 0) < 0)
die_errno(_("could not read MERGE_MODE"));
if (!strcmp(sb.buf, "no-ff"))
allow_fast_forward = 0;
@@ -1715,7 +1739,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
/* Finally, get the commit message */
strbuf_reset(&sb);
- if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+ if (strbuf_read_file(&sb, git_path_commit_editmsg(), 0) < 0) {
int saved_errno = errno;
rollback_index_files();
die(_("could not read commit message: %s"), strerror(saved_errno));
@@ -1726,7 +1750,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
wt_status_truncate_message_at_cut_line(&sb);
if (cleanup_mode != CLEANUP_NONE)
- stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+ strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
if (template_untouched(&sb) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
@@ -1766,7 +1790,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!transaction ||
ref_transaction_update(transaction, "HEAD", sha1,
current_head
- ? current_head->object.sha1 : null_sha1,
+ ? current_head->object.oid.hash : null_sha1,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
rollback_index_files();
@@ -1774,12 +1798,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
ref_transaction_free(transaction);
- unlink(git_path("CHERRY_PICK_HEAD"));
- unlink(git_path("REVERT_HEAD"));
- unlink(git_path("MERGE_HEAD"));
- unlink(git_path("MERGE_MSG"));
- unlink(git_path("MERGE_MODE"));
- unlink(git_path("SQUASH_MSG"));
+ unlink(git_path_cherry_pick_head());
+ unlink(git_path_revert_head());
+ unlink(git_path_merge_head());
+ unlink(git_path_merge_msg());
+ unlink(git_path_merge_mode());
+ unlink(git_path_squash_msg());
if (commit_index_files())
die (_("Repository has been updated, but unable to write\n"
@@ -1793,10 +1817,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
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.sha1, sha1);
+ copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1);
finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
}
- run_rewrite_hook(current_head->object.sha1, sha1);
+ run_rewrite_hook(current_head->object.oid.hash, sha1);
}
if (!quiet)
print_summary(prefix, sha1, !current_head);
diff --git a/builtin/config.c b/builtin/config.c
index a58f99c..6cbf733 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -3,6 +3,7 @@
#include "color.h"
#include "parse-options.h"
#include "urlmatch.h"
+#include "quote.h"
static const char *const builtin_config_usage[] = {
N_("git config [<options>]"),
@@ -13,6 +14,7 @@ static char *key;
static regex_t *key_regexp;
static regex_t *regexp;
static int show_keys;
+static int omit_values;
static int use_key_regexp;
static int do_all;
static int do_not_match;
@@ -23,9 +25,9 @@ static char term = '\n';
static int use_global_config, use_system_config, use_local_config;
static struct git_config_source given_config_source;
static int actions, types;
-static const char *get_color_slot, *get_colorbool_slot;
static int end_null;
static int respect_includes = -1;
+static int show_origin;
#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
@@ -78,7 +80,9 @@ static struct option builtin_config_options[] = {
OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
OPT_GROUP(N_("Other")),
OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
+ OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
+ OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_END(),
};
@@ -89,9 +93,29 @@ static void check_argc(int argc, int min, int max) {
usage_with_options(builtin_config_usage, builtin_config_options);
}
+static void show_config_origin(struct strbuf *buf)
+{
+ const char term = end_null ? '\0' : '\t';
+
+ strbuf_addstr(buf, current_config_origin_type());
+ strbuf_addch(buf, ':');
+ if (end_null)
+ strbuf_addstr(buf, current_config_name());
+ else
+ quote_c_style(current_config_name(), buf, NULL, 0);
+ strbuf_addch(buf, term);
+}
+
static int show_all_config(const char *key_, const char *value_, void *cb)
{
- if (value_)
+ if (show_origin) {
+ struct strbuf buf = STRBUF_INIT;
+ show_config_origin(&buf);
+ /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
+ fwrite(buf.buf, 1, buf.len, stdout);
+ strbuf_release(&buf);
+ }
+ if (!omit_values && value_)
printf("%s%c%s%c", key_, delim, value_, term);
else
printf("%s%c", key_, term);
@@ -106,48 +130,42 @@ struct strbuf_list {
static int format_config(struct strbuf *buf, const char *key_, const char *value_)
{
- int must_free_vptr = 0;
- int must_print_delim = 0;
- char value[256];
- const char *vptr = value;
-
- strbuf_init(buf, 0);
-
- if (show_keys) {
+ if (show_origin)
+ show_config_origin(buf);
+ if (show_keys)
strbuf_addstr(buf, key_);
- must_print_delim = 1;
- }
- if (types == TYPE_INT)
- sprintf(value, "%"PRId64,
- git_config_int64(key_, value_ ? value_ : ""));
- else if (types == TYPE_BOOL)
- vptr = git_config_bool(key_, value_) ? "true" : "false";
- else if (types == TYPE_BOOL_OR_INT) {
- int is_bool, v;
- v = git_config_bool_or_int(key_, value_, &is_bool);
- if (is_bool)
- vptr = v ? "true" : "false";
- else
- sprintf(value, "%d", v);
- } else if (types == TYPE_PATH) {
- if (git_config_pathname(&vptr, key_, value_) < 0)
- return -1;
- must_free_vptr = 1;
- } else if (value_) {
- vptr = value_;
- } else {
- /* Just show the key name */
- vptr = "";
- must_print_delim = 0;
- }
+ if (!omit_values) {
+ if (show_keys)
+ strbuf_addch(buf, key_delim);
- if (must_print_delim)
- strbuf_addch(buf, key_delim);
- strbuf_addstr(buf, vptr);
+ if (types == TYPE_INT)
+ strbuf_addf(buf, "%"PRId64,
+ git_config_int64(key_, value_ ? value_ : ""));
+ else if (types == TYPE_BOOL)
+ strbuf_addstr(buf, git_config_bool(key_, value_) ?
+ "true" : "false");
+ else if (types == TYPE_BOOL_OR_INT) {
+ int is_bool, v;
+ v = git_config_bool_or_int(key_, value_, &is_bool);
+ if (is_bool)
+ strbuf_addstr(buf, v ? "true" : "false");
+ else
+ strbuf_addf(buf, "%d", v);
+ } else if (types == TYPE_PATH) {
+ const char *v;
+ if (git_config_pathname(&v, key_, value_) < 0)
+ return -1;
+ strbuf_addstr(buf, v);
+ free((char *)v);
+ } else if (value_) {
+ strbuf_addstr(buf, value_);
+ } else {
+ /* Just show the key name; back out delimiter */
+ if (show_keys)
+ strbuf_setlen(buf, buf->len - 1);
+ }
+ }
strbuf_addch(buf, term);
-
- if (must_free_vptr)
- free((char *)vptr);
return 0;
}
@@ -164,6 +182,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)
return 0;
ALLOC_GROW(values->items, values->nr + 1, values->alloc);
+ strbuf_init(&values->items[values->nr], 0);
return format_config(&values->items[values->nr++], key_, value_);
}
@@ -193,7 +212,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)) {
- fprintf(stderr, "Invalid key pattern: %s\n", key_);
+ error("invalid key pattern: %s", key_);
free(key_regexp);
key_regexp = NULL;
ret = CONFIG_INVALID_PATTERN;
@@ -214,7 +233,7 @@ static int get_value(const char *key_, const char *regex_)
regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(regexp, regex_, REG_EXTENDED)) {
- fprintf(stderr, "Invalid pattern: %s\n", regex_);
+ error("invalid pattern: %s", regex_);
free(regexp);
regexp = NULL;
ret = CONFIG_INVALID_PATTERN;
@@ -251,8 +270,6 @@ free_strings:
static char *normalize_value(const char *key, const char *value)
{
- char *normalized;
-
if (!value)
return NULL;
@@ -263,27 +280,21 @@ static char *normalize_value(const char *key, const char *value)
* "~/foobar/" in the config file, and to expand the ~
* when retrieving the value.
*/
- normalized = xstrdup(value);
- else {
- normalized = xmalloc(64);
- if (types == TYPE_INT) {
- int64_t v = git_config_int64(key, value);
- sprintf(normalized, "%"PRId64, v);
- }
- else if (types == TYPE_BOOL)
- sprintf(normalized, "%s",
- git_config_bool(key, value) ? "true" : "false");
- else if (types == TYPE_BOOL_OR_INT) {
- int is_bool, v;
- v = git_config_bool_or_int(key, value, &is_bool);
- if (!is_bool)
- sprintf(normalized, "%d", v);
- else
- sprintf(normalized, "%s", v ? "true" : "false");
- }
+ return xstrdup(value);
+ if (types == TYPE_INT)
+ return xstrfmt("%"PRId64, git_config_int64(key, value));
+ if (types == TYPE_BOOL)
+ return xstrdup(git_config_bool(key, value) ? "true" : "false");
+ if (types == TYPE_BOOL_OR_INT) {
+ int is_bool, v;
+ v = git_config_bool_or_int(key, value, &is_bool);
+ if (!is_bool)
+ return xstrfmt("%d", v);
+ else
+ return xstrdup(v ? "true" : "false");
}
- return normalized;
+ die("BUG: cannot normalize type %d", types);
}
static int get_color_found;
@@ -365,6 +376,9 @@ static int get_colorbool(const char *var, int print)
static void check_write(void)
{
+ if (!given_config_source.file && !startup_info->have_repository)
+ die("not in a git directory");
+
if (given_config_source.use_stdin)
die("writing to stdin is not supported");
@@ -402,6 +416,7 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
static int get_urlmatch(const char *var, const char *url)
{
+ int ret;
char *section_tail;
struct string_list_item *item;
struct urlmatch_config config = { STRING_LIST_INIT_DUP };
@@ -428,16 +443,15 @@ static int get_urlmatch(const char *var, const char *url)
git_config_with_options(urlmatch_config_entry, &config,
&given_config_source, respect_includes);
+ ret = !values.nr;
+
for_each_string_list_item(item, &values) {
struct urlmatch_current_candidate_value *matched = item->util;
- struct strbuf key = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
- strbuf_addstr(&key, item->string);
- format_config(&buf, key.buf,
+ format_config(&buf, item->string,
matched->value_is_null ? NULL : matched->value.buf);
fwrite(buf.buf, 1, buf.len, stdout);
- strbuf_release(&key);
strbuf_release(&buf);
strbuf_release(&matched->value);
@@ -447,7 +461,7 @@ static int get_urlmatch(const char *var, const char *url)
free(config.url.url);
free((void *)config.section);
- return 0;
+ return ret;
}
static char *default_user_config(void)
@@ -549,6 +563,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)
default:
usage_with_options(builtin_config_usage, builtin_config_options);
}
+ if (omit_values &&
+ !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
+ error("--name-only is only applicable to --list or --get-regexp");
+ usage_with_options(builtin_config_usage, builtin_config_options);
+ }
+
+ if (show_origin && !(actions &
+ (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
+ error("--show-origin is only applicable to --get, --get-all, "
+ "--get-regexp, and --list.");
+ usage_with_options(builtin_config_usage, builtin_config_options);
+ }
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
@@ -577,7 +603,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
given_config_source.file : git_path("config"));
if (use_global_config) {
int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd) {
+ if (fd >= 0) {
char *content = default_user_config();
write_str_in_full(fd, content);
free(content);
@@ -594,7 +620,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]);
- ret = git_config_set_in_file(given_config_source.file, argv[0], 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"
" Use a regexp, --add or --replace-all to change %s.", argv[0]);
@@ -604,23 +630,23 @@ 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]);
- return git_config_set_multivar_in_file(given_config_source.file,
- argv[0], value, argv[2], 0);
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2], 0);
}
else if (actions == ACTION_ADD) {
check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
- return git_config_set_multivar_in_file(given_config_source.file,
- argv[0], value,
- CONFIG_REGEX_NONE, 0);
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value,
+ CONFIG_REGEX_NONE, 0);
}
else if (actions == ACTION_REPLACE_ALL) {
check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
- return git_config_set_multivar_in_file(given_config_source.file,
- argv[0], value, argv[2], 1);
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2], 1);
}
else if (actions == ACTION_GET) {
check_argc(argc, 1, 2);
@@ -646,17 +672,17 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write();
check_argc(argc, 1, 2);
if (argc == 2)
- return git_config_set_multivar_in_file(given_config_source.file,
- argv[0], NULL, argv[1], 0);
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], NULL, argv[1], 0);
else
- return git_config_set_in_file(given_config_source.file,
- argv[0], NULL);
+ return git_config_set_in_file_gently(given_config_source.file,
+ argv[0], NULL);
}
else if (actions == ACTION_UNSET_ALL) {
check_write();
check_argc(argc, 1, 2);
- return git_config_set_multivar_in_file(given_config_source.file,
- argv[0], NULL, argv[1], 1);
+ return git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], NULL, argv[1], 1);
}
else if (actions == ACTION_RENAME_SECTION) {
int ret;
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index e47ef0b..ba92919 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -15,9 +15,31 @@ static int verbose;
static unsigned long loose, packed, packed_loose;
static off_t loose_size;
-static void real_report_garbage(const char *desc, const char *path)
+static const char *bits_to_msg(unsigned seen_bits)
+{
+ switch (seen_bits) {
+ case 0:
+ return "no corresponding .idx or .pack";
+ case PACKDIR_FILE_GARBAGE:
+ return "garbage found";
+ case PACKDIR_FILE_PACK:
+ return "no corresponding .idx";
+ case PACKDIR_FILE_IDX:
+ return "no corresponding .pack";
+ case PACKDIR_FILE_PACK|PACKDIR_FILE_IDX:
+ default:
+ return NULL;
+ }
+}
+
+static void real_report_garbage(unsigned seen_bits, const char *path)
{
struct stat st;
+ const char *desc = bits_to_msg(seen_bits);
+
+ if (!desc)
+ return;
+
if (!stat(path, &st))
size_garbage += st.st_size;
warning("%s: %s", desc, path);
@@ -27,7 +49,7 @@ static void real_report_garbage(const char *desc, const char *path)
static void loose_garbage(const char *path)
{
if (verbose)
- report_garbage("garbage found", path);
+ report_garbage(PACKDIR_FILE_GARBAGE, path);
}
static int count_loose(const unsigned char *sha1, const char *path, void *data)
@@ -70,8 +92,10 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
/* we do not take arguments other than flags for now */
if (argc)
usage_with_options(count_objects_usage, opts);
- if (verbose)
+ if (verbose) {
report_garbage = real_report_garbage;
+ report_linked_checkout_garbage();
+ }
for_each_loose_file_in_objdir(get_object_directory(),
count_loose, count_cruft, NULL, NULL);
diff --git a/builtin/describe.c b/builtin/describe.c
index e00a75b..8a25abe 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -119,10 +119,10 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+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/");
- unsigned char peeled[20];
+ struct object_id peeled;
int is_annotated, prio;
/* Reject anything outside refs/tags/ unless --all */
@@ -134,10 +134,10 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
return 0;
/* Is it annotated? */
- if (!peel_ref(path, peeled)) {
- is_annotated = !!hashcmp(sha1, peeled);
+ if (!peel_ref(path, peeled.hash)) {
+ is_annotated = !!oidcmp(oid, &peeled);
} else {
- hashcpy(peeled, sha1);
+ oidcpy(&peeled, oid);
is_annotated = 0;
}
@@ -154,7 +154,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1);
+ add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash);
return 0;
}
@@ -252,14 +252,14 @@ static void describe(const char *arg, int last_one)
if (!cmit)
die(_("%s is not a valid '%s' object"), arg, commit_type);
- n = find_commit_name(cmit->object.sha1);
+ n = find_commit_name(cmit->object.oid.hash);
if (n && (tags || all || n->prio == 2)) {
/*
* Exact match to an existing ref.
*/
display_name(n);
if (longformat)
- show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
+ show_suffix(0, n->tag ? n->tag->tagged->oid.hash : sha1);
if (dirty)
printf("%s", dirty);
printf("\n");
@@ -267,7 +267,7 @@ static void describe(const char *arg, int last_one)
}
if (!max_candidates)
- die(_("no tag exactly matches '%s'"), sha1_to_hex(cmit->object.sha1));
+ die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
if (debug)
fprintf(stderr, _("searching to describe %s\n"), arg);
@@ -317,7 +317,7 @@ static void describe(const char *arg, int last_one)
if (annotated_cnt && !list) {
if (debug)
fprintf(stderr, _("finished search at %s\n"),
- sha1_to_hex(c->object.sha1));
+ oid_to_hex(&c->object.oid));
break;
}
while (parents) {
@@ -334,9 +334,9 @@ static void describe(const char *arg, int last_one)
}
if (!match_cnt) {
- const unsigned char *sha1 = cmit->object.sha1;
+ struct object_id *oid = &cmit->object.oid;
if (always) {
- printf("%s", find_unique_abbrev(sha1, abbrev));
+ printf("%s", find_unique_abbrev(oid->hash, abbrev));
if (dirty)
printf("%s", dirty);
printf("\n");
@@ -345,11 +345,11 @@ static void describe(const char *arg, int last_one)
if (unannotated_cnt)
die(_("No annotated tags can describe '%s'.\n"
"However, there were unannotated tags: try --tags."),
- sha1_to_hex(sha1));
+ oid_to_hex(oid));
else
die(_("No tags can describe '%s'.\n"
"Try --always, or create some tags."),
- sha1_to_hex(sha1));
+ oid_to_hex(oid));
}
qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
@@ -374,13 +374,13 @@ static void describe(const char *arg, int last_one)
_("more than %i tags found; listed %i most recent\n"
"gave up search at %s\n"),
max_candidates, max_candidates,
- sha1_to_hex(gave_up_on->object.sha1));
+ oid_to_hex(&gave_up_on->object.oid));
}
}
display_name(all_matches[0].name);
if (abbrev)
- show_suffix(all_matches[0].depth, cmit->object.sha1);
+ show_suffix(all_matches[0].depth, cmit->object.oid.hash);
if (dirty)
printf("%s", dirty);
printf("\n");
@@ -443,10 +443,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (pattern)
argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
}
- while (*argv) {
- argv_array_push(&args, *argv);
- argv++;
- }
+ if (argc)
+ argv_array_pushv(&args, argv);
+ else
+ argv_array_push(&args, "HEAD");
return cmd_name_rev(args.argc, args.argv, prefix);
}
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 8ed2eb8..15c61fd 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -24,6 +24,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
+ precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, &rev, NULL);
while (1 < argc && argv[1][0] == '-') {
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index d979824..1af373d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -21,6 +21,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
+ precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 12b683d..806dd7a 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -49,9 +49,9 @@ static int stdin_diff_trees(struct tree *tree1, char *line, int len)
tree2 = lookup_tree(sha1);
if (!tree2 || parse_tree(tree2))
return -1;
- printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
- sha1_to_hex(tree2->object.sha1));
- diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
+ 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);
log_tree_diff_flush(&log_tree_opt);
return 0;
@@ -114,6 +114,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
opt->disable_stdin = 1;
memset(&s_r_opt, 0, sizeof(s_r_opt));
s_r_opt.tweak = diff_tree_tweak_rev;
+
+ precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, opt, &s_r_opt);
while (--argc > 0) {
@@ -139,7 +141,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
break;
case 1:
tree1 = opt->pending.objects[0].item;
- diff_tree_commit_sha1(tree1->sha1);
+ diff_tree_commit_sha1(tree1->oid.hash);
break;
case 2:
tree1 = opt->pending.objects[0].item;
@@ -149,8 +151,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
tree2 = tree1;
tree1 = tmp;
}
- diff_tree_sha1(tree1->sha1,
- tree2->sha1,
+ diff_tree_sha1(tree1->oid.hash,
+ tree2->oid.hash,
"", &opt->diffopt);
log_tree_diff_flush(opt);
break;
diff --git a/builtin/diff.c b/builtin/diff.c
index 4326fa5..b7a9405 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -175,8 +175,8 @@ static int builtin_diff_tree(struct rev_info *revs,
*/
if (ent1->item->flags & UNINTERESTING)
swap = 1;
- sha1[swap] = ent0->item->sha1;
- sha1[1 - swap] = ent1->item->sha1;
+ sha1[swap] = ent0->item->oid.hash;
+ sha1[1 - swap] = ent1->item->oid.hash;
diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
log_tree_diff_flush(revs);
return 0;
@@ -196,8 +196,8 @@ static int builtin_diff_combined(struct rev_info *revs,
if (!revs->dense_combined_merges && !revs->combine_merges)
revs->dense_combined_merges = revs->combine_merges = 1;
for (i = 1; i < ents; i++)
- sha1_array_append(&parents, ent[i].item->sha1);
- diff_tree_combined(ent[0].item->sha1, &parents,
+ sha1_array_append(&parents, ent[i].item->oid.hash);
+ diff_tree_combined(ent[0].item->oid.hash, &parents,
revs->dense_combined_merges, revs);
sha1_array_clear(&parents);
return 0;
@@ -318,7 +318,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!no_index)
gitmodules_config();
+ init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
+ precompose_argv(argc, argv);
init_revisions(&rev, prefix);
@@ -341,7 +343,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
}
if (no_index)
/* If this is a no-index diff, just run it and exit there. */
- diff_no_index(&rev, argc, argv, prefix);
+ diff_no_index(&rev, argc, argv);
/* Otherwise, we are doing the usual "git" diff */
rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
@@ -395,7 +397,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->sha1);
+ obj = parse_object(obj->oid.hash);
obj = deref_tag(obj, NULL, 0);
if (!obj)
die(_("invalid object '%s' given."), name);
@@ -408,7 +410,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
} else if (obj->type == OBJ_BLOB) {
if (2 <= blobs)
die(_("more than two blobs given: '%s'"), name);
- hashcpy(blob[blobs].sha1, obj->sha1);
+ hashcpy(blob[blobs].sha1, obj->oid.hash);
blob[blobs].name = name;
blob[blobs].mode = entry->mode;
blobs++;
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index b8182c2..c0652a7 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -5,6 +5,7 @@
*/
#include "builtin.h"
#include "cache.h"
+#include "refs.h"
#include "commit.h"
#include "object.h"
#include "tag.h"
@@ -367,7 +368,7 @@ static void show_filemodify(struct diff_queue_struct *q,
print_path(spec->path);
putchar('\n');
- if (!hashcmp(ospec->sha1, spec->sha1) &&
+ if (!oidcmp(&ospec->oid, &spec->oid) &&
ospec->mode == spec->mode)
break;
/* fallthrough */
@@ -382,10 +383,10 @@ static void show_filemodify(struct diff_queue_struct *q,
if (no_data || S_ISGITLINK(spec->mode))
printf("M %06o %s ", spec->mode,
sha1_to_hex(anonymize ?
- anonymize_sha1(spec->sha1) :
- spec->sha1));
+ anonymize_sha1(spec->oid.hash) :
+ spec->oid.hash));
else {
- struct object *object = lookup_object(spec->sha1);
+ struct object *object = lookup_object(spec->oid.hash);
printf("M %06o :%d ", spec->mode,
get_object_mark(object));
}
@@ -543,13 +544,13 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
author = strstr(commit_buffer, "\nauthor ");
if (!author)
die ("Could not find author in commit %s",
- sha1_to_hex(commit->object.sha1));
+ oid_to_hex(&commit->object.oid));
author++;
author_end = strchrnul(author, '\n');
committer = strstr(author_end, "\ncommitter ");
if (!committer)
die ("Could not find committer in commit %s",
- sha1_to_hex(commit->object.sha1));
+ oid_to_hex(&commit->object.oid));
committer++;
committer_end = strchrnul(committer, '\n');
message = strstr(committer_end, "\n\n");
@@ -561,17 +562,17 @@ 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.sha1,
- commit->tree->object.sha1, "", &rev->diffopt);
+ diff_tree_sha1(commit->parents->item->tree->object.oid.hash,
+ commit->tree->object.oid.hash, "", &rev->diffopt);
}
else
- diff_root_tree_sha1(commit->tree->object.sha1,
+ diff_root_tree_sha1(commit->tree->object.oid.hash,
"", &rev->diffopt);
/* Export the referenced blobs, and remember the marks. */
for (i = 0; i < diff_queued_diff.nr; i++)
if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
- export_blob(diff_queued_diff.queue[i]->two->sha1);
+ export_blob(diff_queued_diff.queue[i]->two->oid.hash);
refname = commit->util;
if (anonymize) {
@@ -660,13 +661,13 @@ static void handle_tag(const char *name, struct tag *tag)
}
if (tagged->type == OBJ_TREE) {
warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
- sha1_to_hex(tag->object.sha1));
+ oid_to_hex(&tag->object.oid));
return;
}
- buf = read_sha1_file(tag->object.sha1, &type, &size);
+ buf = read_sha1_file(tag->object.oid.hash, &type, &size);
if (!buf)
- die ("Could not read tag %s", sha1_to_hex(tag->object.sha1));
+ die ("Could not read tag %s", oid_to_hex(&tag->object.oid));
message = memmem(buf, size, "\n\n", 2);
if (message) {
message += 2;
@@ -705,16 +706,16 @@ static void handle_tag(const char *name, struct tag *tag)
case ABORT:
die ("Encountered signed tag %s; use "
"--signed-tags=<mode> to handle it.",
- sha1_to_hex(tag->object.sha1));
+ oid_to_hex(&tag->object.oid));
case WARN:
warning ("Exporting signed tag %s",
- sha1_to_hex(tag->object.sha1));
+ oid_to_hex(&tag->object.oid));
/* fallthru */
case VERBATIM:
break;
case WARN_STRIP:
warning ("Stripping signature from tag %s",
- sha1_to_hex(tag->object.sha1));
+ oid_to_hex(&tag->object.oid));
/* fallthru */
case STRIP:
message_size = signature + 1 - message;
@@ -730,14 +731,14 @@ static void handle_tag(const char *name, struct tag *tag)
case ABORT:
die ("Tag %s tags unexported object; use "
"--tag-of-filtered-object=<mode> to handle it.",
- sha1_to_hex(tag->object.sha1));
+ oid_to_hex(&tag->object.oid));
case DROP:
/* Ignore this tag altogether */
return;
case REWRITE:
if (tagged->type != OBJ_COMMIT) {
die ("Tag %s tags unexported %s!",
- sha1_to_hex(tag->object.sha1),
+ oid_to_hex(&tag->object.oid),
typename(tagged->type));
}
p = (struct commit *)tagged;
@@ -750,7 +751,7 @@ static void handle_tag(const char *name, struct tag *tag)
break;
if (!p->parents)
die ("Can't find replacement commit for tag %s\n",
- sha1_to_hex(tag->object.sha1));
+ oid_to_hex(&tag->object.oid));
p = p->parents->item;
}
tagged_mark = get_object_mark(&p->object);
@@ -776,7 +777,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.sha1);
+ parse_object(tag->object.oid.hash);
string_list_append(&extra_refs, full_name)->util = tag;
tag = (struct tag *)tag->tagged;
}
@@ -827,7 +828,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
case OBJ_COMMIT:
break;
case OBJ_BLOB:
- export_blob(commit->object.sha1);
+ export_blob(commit->object.oid.hash);
continue;
default: /* OBJ_TAG (nested tags) is already handled */
warning("Tag points to object of unexpected type %s, skipping.",
@@ -879,7 +880,7 @@ static void export_marks(char *file)
FILE *f;
int e = 0;
- f = fopen(file, "w");
+ f = fopen_for_writing(file);
if (!f)
die_errno("Unable to open marks file %s for writing.", file);
@@ -887,7 +888,7 @@ static void export_marks(char *file)
if (deco->base && deco->base->type == 1) {
mark = ptr_to_mark(deco->decoration);
if (fprintf(f, ":%"PRIu32" %s\n", mark,
- sha1_to_hex(deco->base->sha1)) < 0) {
+ oid_to_hex(&deco->base->oid)) < 0) {
e = 1;
break;
}
@@ -1020,7 +1021,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
const char **refspecs_str;
int i;
- refspecs_str = xmalloc(sizeof(*refspecs_str) * refspecs_list.nr);
+ ALLOC_ARRAY(refspecs_str, refspecs_list.nr);
for (i = 0; i < refspecs_list.nr; i++)
refspecs_str[i] = refspecs_list.items[i].string;
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 4a6b340..bfd0be4 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -10,31 +10,34 @@ static const char fetch_pack_usage[] =
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
-static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
- const char *name, int namelen)
+static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
+ const char *name)
{
- struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
- unsigned char sha1[20];
-
- if (namelen > 41 && name[40] == ' ' && !get_sha1_hex(name, sha1)) {
- hashcpy(ref->old_sha1, sha1);
- name += 41;
- namelen -= 41;
+ struct ref *ref;
+ struct object_id oid;
+
+ if (!get_oid_hex(name, &oid)) {
+ if (name[GIT_SHA1_HEXSZ] == ' ') {
+ /* <sha1> <ref>, find refname */
+ name += GIT_SHA1_HEXSZ + 1;
+ } else if (name[GIT_SHA1_HEXSZ] == '\0') {
+ ; /* <sha1>, leave sha1 as name */
+ } else {
+ /* <ref>, clear cruft from oid */
+ oidclr(&oid);
+ }
+ } else {
+ /* <ref>, clear cruft from get_oid_hex */
+ oidclr(&oid);
}
- memcpy(ref->name, name, namelen);
- ref->name[namelen] = '\0';
+ ref = alloc_ref(name);
+ oidcpy(&ref->old_oid, &oid);
(*nr)++;
ALLOC_GROW(*sought, *nr, *alloc);
(*sought)[*nr - 1] = ref;
}
-static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
- const char *string)
-{
- add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
-}
-
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
{
int i, ret;
@@ -156,7 +159,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
else {
/* read from stdin one ref per line, until EOF */
struct strbuf line = STRBUF_INIT;
- while (strbuf_getline(&line, stdin, '\n') != EOF)
+ while (strbuf_getline_lf(&line, stdin) != EOF)
add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
strbuf_release(&line);
}
@@ -210,7 +213,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
while (ref) {
printf("%s %s\n",
- sha1_to_hex(ref->old_sha1), ref->name);
+ oid_to_hex(&ref->old_oid), ref->name);
ref = ref->next;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f951265..164623b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -11,9 +11,11 @@
#include "run-command.h"
#include "parse-options.h"
#include "sigchain.h"
+#include "submodule-config.h"
#include "submodule.h"
#include "connected.h"
#include "argv-array.h"
+#include "utf8.h"
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -36,6 +38,8 @@ static int prune = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int tags = TAGS_DEFAULT, unshallow, update_shallow;
+static int max_children = -1;
+static enum transport_family family;
static const char *depth;
static const char *upload_pack;
static struct strbuf default_rla = STRBUF_INIT;
@@ -98,6 +102,8 @@ static struct option builtin_fetch_options[] = {
N_("fetch all tags and associated objects"), TAGS_SET),
OPT_SET_INT('n', NULL, &tags,
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+ OPT_INTEGER('j', "jobs", &max_children,
+ 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"),
@@ -123,6 +129,10 @@ static struct option builtin_fetch_options[] = {
N_("accept refs that update .git/shallow")),
{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+ OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+ TRANSPORT_FAMILY_IPV4),
+ OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+ TRANSPORT_FAMILY_IPV6),
OPT_END()
};
@@ -179,13 +189,15 @@ static void add_merge_config(struct ref **head,
}
}
-static int add_existing(const char *refname, const unsigned char *sha1,
+static int add_existing(const char *refname, const struct object_id *oid,
int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
struct string_list_item *item = string_list_insert(list, refname);
- item->util = xmalloc(20);
- hashcpy(item->util, sha1);
+ struct object_id *old_oid = xmalloc(sizeof(*old_oid));
+
+ oidcpy(old_oid, oid);
+ item->util = old_oid;
return 0;
}
@@ -193,7 +205,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
{
struct ref *rm = *head;
while (rm) {
- if (!hashcmp(rm->old_sha1, sha1))
+ if (!hashcmp(rm->old_oid.hash, sha1))
return 1;
rm = rm->next;
}
@@ -221,8 +233,8 @@ static void find_non_local_tags(struct transport *transport,
* as one to ignore by setting util to NULL.
*/
if (ends_with(ref->name, "^{}")) {
- if (item && !has_sha1_file(ref->old_sha1) &&
- !will_fetch(head, ref->old_sha1) &&
+ if (item && !has_object_file(&ref->old_oid) &&
+ !will_fetch(head, ref->old_oid.hash) &&
!has_sha1_file(item->util) &&
!will_fetch(head, item->util))
item->util = NULL;
@@ -248,7 +260,7 @@ static void find_non_local_tags(struct transport *transport,
continue;
item = string_list_insert(&remote_refs, ref->name);
- item->util = (void *)ref->old_sha1;
+ item->util = (void *)&ref->old_oid;
}
string_list_clear(&existing_refs, 1);
@@ -270,7 +282,7 @@ static void find_non_local_tags(struct transport *transport,
{
struct ref *rm = alloc_ref(item->string);
rm->peer_ref = alloc_ref(item->string);
- hashcpy(rm->old_sha1, item->util);
+ oidcpy(&rm->old_oid, item->util);
**tail = rm;
*tail = &rm->next;
}
@@ -416,8 +428,8 @@ static int s_update_ref(const char *action,
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref->name,
- ref->new_sha1,
- check_old ? ref->old_sha1 : NULL,
+ ref->new_oid.hash,
+ check_old ? ref->old_oid.hash : NULL,
0, msg, &err))
goto fail;
@@ -438,7 +450,132 @@ fail:
: STORE_REF_ERROR_OTHER;
}
-#define REFCOL_WIDTH 10
+static int refcol_width = 10;
+static int compact_format;
+
+static void adjust_refcol_width(const struct ref *ref)
+{
+ int max, rlen, llen, len;
+
+ /* uptodate lines are only shown on high verbosity level */
+ if (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))
+ return;
+
+ max = term_columns();
+ rlen = utf8_strwidth(prettify_refname(ref->name));
+
+ llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+ /*
+ * rough estimation to see if the output line is too long and
+ * should not be counted (we can't do precise calculation
+ * anyway because we don't know if the error explanation part
+ * will be printed in update_local_ref)
+ */
+ if (compact_format) {
+ llen = 0;
+ max = max * 2 / 3;
+ }
+ len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+ if (len >= max)
+ return;
+
+ /*
+ * Not precise calculation for compact mode because '*' can
+ * appear on the left hand side of '->' and shrink the column
+ * back.
+ */
+ if (refcol_width < rlen)
+ refcol_width = rlen;
+}
+
+static void prepare_format_display(struct ref *ref_map)
+{
+ struct ref *rm;
+ const char *format = "full";
+
+ git_config_get_string_const("fetch.output", &format);
+ if (!strcasecmp(format, "full"))
+ compact_format = 0;
+ else if (!strcasecmp(format, "compact"))
+ compact_format = 1;
+ else
+ die(_("configuration fetch.output contains invalid value %s"),
+ format);
+
+ for (rm = ref_map; rm; rm = rm->next) {
+ if (rm->status == REF_STATUS_REJECT_SHALLOW ||
+ !rm->peer_ref ||
+ !strcmp(rm->name, "HEAD"))
+ continue;
+
+ adjust_refcol_width(rm);
+ }
+}
+
+static void print_remote_to_local(struct strbuf *display,
+ const char *remote, const char *local)
+{
+ strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+}
+
+static int find_and_replace(struct strbuf *haystack,
+ const char *needle,
+ const char *placeholder)
+{
+ const char *p = strstr(haystack->buf, needle);
+ int plen, nlen;
+
+ if (!p)
+ return 0;
+
+ if (p > haystack->buf && p[-1] != '/')
+ return 0;
+
+ plen = strlen(p);
+ nlen = strlen(needle);
+ if (plen > nlen && p[nlen] != '/')
+ return 0;
+
+ strbuf_splice(haystack, p - haystack->buf, nlen,
+ placeholder, strlen(placeholder));
+ return 1;
+}
+
+static void print_compact(struct strbuf *display,
+ const char *remote, const char *local)
+{
+ struct strbuf r = STRBUF_INIT;
+ struct strbuf l = STRBUF_INIT;
+
+ if (!strcmp(remote, local)) {
+ strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+ return;
+ }
+
+ strbuf_addstr(&r, remote);
+ strbuf_addstr(&l, local);
+
+ if (!find_and_replace(&r, local, "*"))
+ find_and_replace(&l, remote, "*");
+ print_remote_to_local(display, r.buf, l.buf);
+
+ strbuf_release(&r);
+ strbuf_release(&l);
+}
+
+static void format_display(struct strbuf *display, char code,
+ const char *summary, const char *error,
+ const char *remote, const char *local)
+{
+ strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary));
+ if (!compact_format)
+ print_remote_to_local(display, remote, local);
+ else
+ print_compact(display, remote, local);
+ if (error)
+ strbuf_addf(display, " (%s)", error);
+}
static int update_local_ref(struct ref *ref,
const char *remote,
@@ -450,47 +587,43 @@ static int update_local_ref(struct ref *ref,
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
- type = sha1_object_info(ref->new_sha1, NULL);
+ type = sha1_object_info(ref->new_oid.hash, NULL);
if (type < 0)
- die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
+ die(_("object %s not found"), oid_to_hex(&ref->new_oid));
- if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
+ if (!oidcmp(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
- strbuf_addf(display, "= %-*s %-*s -> %s",
- TRANSPORT_SUMMARY(_("[up to date]")),
- REFCOL_WIDTH, remote, pretty_ref);
+ format_display(display, '=', _("[up to date]"), NULL,
+ remote, pretty_ref);
return 0;
}
if (current_branch &&
!strcmp(ref->name, current_branch->name) &&
!(update_head_ok || is_bare_repository()) &&
- !is_null_sha1(ref->old_sha1)) {
+ !is_null_oid(&ref->old_oid)) {
/*
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- strbuf_addf(display,
- _("! %-*s %-*s -> %s (can't fetch in current branch)"),
- TRANSPORT_SUMMARY(_("[rejected]")),
- REFCOL_WIDTH, remote, pretty_ref);
+ format_display(display, '!', _("[rejected]"),
+ _("can't fetch in current branch"),
+ remote, pretty_ref);
return 1;
}
- if (!is_null_sha1(ref->old_sha1) &&
+ if (!is_null_oid(&ref->old_oid) &&
starts_with(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : '-',
- TRANSPORT_SUMMARY(_("[tag update]")),
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : 't', _("[tag update]"),
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
- current = lookup_commit_reference_gently(ref->old_sha1, 1);
- updated = lookup_commit_reference_gently(ref->new_sha1, 1);
+ current = lookup_commit_reference_gently(ref->old_oid.hash, 1);
+ updated = lookup_commit_reference_gently(ref->new_oid.hash, 1);
if (!current || !updated) {
const char *msg;
const char *what;
@@ -514,53 +647,47 @@ static int update_local_ref(struct ref *ref,
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
- check_for_new_submodule_commits(ref->new_sha1);
+ check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref(msg, ref, 0);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : '*',
- TRANSPORT_SUMMARY(what),
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : '*', what,
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
if (in_merge_bases(current, updated)) {
- char quickref[83];
+ struct strbuf quickref = STRBUF_INIT;
int r;
- strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
- strcat(quickref, "..");
- strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+ strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
+ strbuf_addstr(&quickref, "..");
+ strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
- check_for_new_submodule_commits(ref->new_sha1);
+ check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref("fast-forward", ref, 1);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref,
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : ' ', quickref.buf,
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
+ strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
- char quickref[84];
+ struct strbuf quickref = STRBUF_INIT;
int r;
- strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
- strcat(quickref, "...");
- strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+ strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
+ strbuf_addstr(&quickref, "...");
+ strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
- check_for_new_submodule_commits(ref->new_sha1);
+ check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref("forced-update", ref, 1);
- strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
- r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref,
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _("unable to update local ref") : _("forced update"));
+ format_display(display, r ? '!' : '+', quickref.buf,
+ r ? _("unable to update local ref") : _("forced update"),
+ remote, pretty_ref);
+ strbuf_release(&quickref);
return r;
} else {
- strbuf_addf(display, "! %-*s %-*s -> %s %s",
- TRANSPORT_SUMMARY(_("[rejected]")),
- REFCOL_WIDTH, remote, pretty_ref,
- _("(non-fast-forward)"));
+ format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
+ remote, pretty_ref);
return 1;
}
}
@@ -575,7 +702,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_sha1);
+ hashcpy(sha1, ref->old_oid.hash);
return 0;
}
@@ -588,12 +715,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
- char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+ char *url;
+ const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
int want_status;
fp = fopen(filename, "a");
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
if (raw_url)
url = transport_anonymize_url(raw_url);
@@ -601,11 +729,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
url = xstrdup("foreign");
rm = ref_map;
- if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+ if (check_connected(iterate_ref_map, &rm, NULL)) {
rc = error(_("%s did not send all necessary objects\n"), url);
goto abort;
}
+ prepare_format_display(ref_map);
+
/*
* We do a pass for each fetch_head_status type in their enum order, so
* merged entries are written before not-for-merge. That lets readers
@@ -625,7 +755,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
}
- commit = lookup_commit_reference_gently(rm->old_sha1, 1);
+ commit = lookup_commit_reference_gently(rm->old_oid.hash, 1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -633,10 +763,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
if (rm->peer_ref) {
- ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
- strcpy(ref->name, rm->peer_ref->name);
- hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
- hashcpy(ref->new_sha1, rm->old_sha1);
+ ref = alloc_ref(rm->peer_ref->name);
+ oidcpy(&ref->old_oid, &rm->peer_ref->old_oid);
+ oidcpy(&ref->new_oid, &rm->old_oid);
ref->force = rm->peer_ref->force;
}
@@ -681,7 +810,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
/* fall-through */
case FETCH_HEAD_MERGE:
fprintf(fp, "%s\t%s\t%s",
- sha1_to_hex(rm->old_sha1),
+ oid_to_hex(&rm->old_oid),
merge_status_marker,
note.buf);
for (i = 0; i < url_len; ++i)
@@ -701,11 +830,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
rc |= update_local_ref(ref, what, rm, &note);
free(ref);
} else
- strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH,
- *kind ? kind : "branch",
- REFCOL_WIDTH,
- *what ? what : "HEAD");
+ format_display(&note, '*',
+ *kind ? kind : "branch", NULL,
+ *what ? what : "HEAD",
+ "FETCH_HEAD");
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
@@ -738,6 +866,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
static int quickfetch(struct ref *ref_map)
{
struct ref *rm = ref_map;
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
/*
* If we are deepening a shallow clone we already have these
@@ -748,7 +877,8 @@ static int quickfetch(struct ref *ref_map)
*/
if (depth)
return -1;
- return check_everything_connected(iterate_ref_map, 1, &rm);
+ opt.quiet = 1;
+ return check_connected(iterate_ref_map, &rm, &opt);
}
static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@ -787,20 +917,31 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
if (4 < i && !strncmp(".git", url + i - 3, 4))
url_len = i - 3;
- for (ref = stale_refs; ref; ref = ref->next) {
- if (!dry_run)
- result |= delete_ref(ref->name, NULL, 0);
- if (verbosity >= 0 && !shown_url) {
- fprintf(stderr, _("From %.*s\n"), url_len, url);
- shown_url = 1;
- }
- if (verbosity >= 0) {
- fprintf(stderr, " x %-*s %-*s -> %s\n",
- TRANSPORT_SUMMARY(_("[deleted]")),
- REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
+ if (!dry_run) {
+ struct string_list refnames = STRING_LIST_INIT_NODUP;
+
+ for (ref = stale_refs; ref; ref = ref->next)
+ string_list_append(&refnames, ref->name);
+
+ result = delete_refs(&refnames, 0);
+ string_list_clear(&refnames, 0);
+ }
+
+ if (verbosity >= 0) {
+ for (ref = stale_refs; ref; ref = ref->next) {
+ struct strbuf sb = STRBUF_INIT;
+ if (!shown_url) {
+ fprintf(stderr, _("From %.*s\n"), url_len, url);
+ shown_url = 1;
+ }
+ format_display(&sb, '-', _("[deleted]"), NULL,
+ _("(none)"), prettify_refname(ref->name));
+ fprintf(stderr, " %s\n",sb.buf);
+ strbuf_release(&sb);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
+
free(url);
free_refs(stale_refs);
return result;
@@ -822,11 +963,11 @@ static void check_not_current_branch(struct ref *ref_map)
static int truncate_fetch_head(void)
{
- char *filename = git_path("FETCH_HEAD");
- FILE *fp = fopen(filename, "w");
+ const char *filename = git_path_fetch_head();
+ FILE *fp = fopen_for_writing(filename);
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
fclose(fp);
return 0;
}
@@ -847,6 +988,7 @@ static struct transport *prepare_transport(struct remote *remote)
struct transport *transport;
transport = transport_get(remote, NULL);
transport_set_verbosity(transport, verbosity, progress);
+ transport->family = family;
if (upload_pack)
set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
if (keep)
@@ -912,9 +1054,10 @@ static int do_fetch(struct transport *transport,
struct string_list_item *peer_item =
string_list_lookup(&existing_refs,
rm->peer_ref->name);
- if (peer_item)
- hashcpy(rm->peer_ref->old_sha1,
- peer_item->util);
+ if (peer_item) {
+ struct object_id *old_oid = peer_item->util;
+ oidcpy(&rm->peer_ref->old_oid, old_oid);
+ }
}
}
@@ -975,17 +1118,15 @@ static int get_remote_group(const char *key, const char *value, void *priv)
{
struct remote_group_data *g = priv;
- if (starts_with(key, "remotes.") &&
- !strcmp(key + 8, g->name)) {
+ if (skip_prefix(key, "remotes.", &key) && !strcmp(key, g->name)) {
/* split list by white space */
- int space = strcspn(value, " \t\n");
while (*value) {
- if (space > 1) {
- string_list_append(g->list,
- xstrndup(value, space));
- }
- value += space + (value[space] != '\0');
- space = strcspn(value, " \t\n");
+ size_t wordlen = strcspn(value, " \t\n");
+
+ if (wordlen >= 1)
+ string_list_append_nodup(g->list,
+ xstrndup(value, wordlen));
+ value += wordlen + (value[wordlen] != '\0');
}
}
@@ -1000,10 +1141,9 @@ static int add_remote_or_group(const char *name, struct string_list *list)
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
- struct remote *remote;
- if (!remote_is_configured(name))
+ struct remote *remote = remote_get(name);
+ if (!remote_is_configured(remote))
return 0;
- remote = remote_get(name);
string_list_append(list, remote->name);
}
return 1;
@@ -1094,7 +1234,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
if (argc > 0) {
int j = 0;
int i;
- refs = xcalloc(argc + 1, sizeof(const char *));
+ refs = xcalloc(st_add(argc, 1), sizeof(const char *));
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "tag")) {
i++;
@@ -1122,7 +1262,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
- struct string_list list = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote;
int result = 0;
struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
@@ -1144,11 +1284,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
die(_("--depth and --unshallow cannot be used together"));
else if (!is_repository_shallow())
die(_("--unshallow on a complete repository does not make sense"));
- else {
- static char inf_depth[12];
- sprintf(inf_depth, "%d", INFINITE_DEPTH);
- depth = inf_depth;
- }
+ else
+ depth = xstrfmt("%d", INFINITE_DEPTH);
}
/* no need to be strict, transport_set_option() will validate it again */
@@ -1203,14 +1340,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
result = fetch_populated_submodules(&options,
submodule_prefix,
recurse_submodules,
- verbosity < 0);
+ verbosity < 0,
+ max_children);
argv_array_clear(&options);
}
- /* All names were strdup()ed or strndup()ed */
- list.strdup_strings = 1;
string_list_clear(&list, 0);
+ close_all_packs();
+
argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
if (verbosity < 0)
argv_array_push(&argv_gc_auto, "--quiet");
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 05f4c26..ac84e99 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "refs.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
@@ -271,7 +272,7 @@ static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
static void add_people_count(struct strbuf *out, struct string_list *people)
{
if (people->nr == 1)
- strbuf_addf(out, "%s", people->items[0].string);
+ strbuf_addstr(out, people->items[0].string);
else if (people->nr == 2)
strbuf_addf(out, "%s (%d) and %s (%d)",
people->items[0].string,
@@ -377,7 +378,7 @@ static void shortlog(const char *name,
if (!sb.len)
string_list_append(&subjects,
- sha1_to_hex(commit->object.sha1));
+ oid_to_hex(&commit->object.oid));
else
string_list_append(&subjects, strbuf_detach(&sb, NULL));
}
@@ -536,7 +537,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
static void find_merge_parents(struct merge_parents *result,
struct strbuf *in, unsigned char *head)
{
- struct commit_list *parents, *next;
+ struct commit_list *parents;
struct commit *head_commit;
int pos = 0, i, j;
@@ -567,7 +568,7 @@ static void find_merge_parents(struct merge_parents *result,
if (!parent)
continue;
commit_list_insert(parent, &parents);
- add_merge_parent(result, obj->sha1, parent->object.sha1);
+ add_merge_parent(result, obj->oid.hash, parent->object.oid.hash);
}
head_commit = lookup_commit(head);
if (head_commit)
@@ -575,13 +576,10 @@ static void find_merge_parents(struct merge_parents *result,
parents = reduce_heads(parents);
while (parents) {
+ struct commit *cmit = pop_commit(&parents);
for (i = 0; i < result->nr; i++)
- if (!hashcmp(result->item[i].commit,
- parents->item->object.sha1))
+ if (!hashcmp(result->item[i].commit, cmit->object.oid.hash))
result->item[i].used = 1;
- next = parents->next;
- free(parents);
- parents = next;
}
for (i = j = 0; i < result->nr; i++) {
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 13d2172..4e9f6c2 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -2,1081 +2,25 @@
#include "cache.h"
#include "refs.h"
#include "object.h"
-#include "tag.h"
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "quote.h"
#include "parse-options.h"
-#include "remote.h"
-#include "color.h"
-
-/* Quoting styles */
-#define QUOTE_NONE 0
-#define QUOTE_SHELL 1
-#define QUOTE_PERL 2
-#define QUOTE_PYTHON 4
-#define QUOTE_TCL 8
-
-typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
-
-struct atom_value {
- const char *s;
- unsigned long ul; /* used for sorting when not FIELD_STR */
-};
-
-struct ref_sort {
- struct ref_sort *next;
- int atom; /* index into used_atom array */
- unsigned reverse : 1;
-};
-
-struct refinfo {
- char *refname;
- unsigned char objectname[20];
- int flag;
- const char *symref;
- struct atom_value *value;
-};
-
-static struct {
- const char *name;
- cmp_type cmp_type;
-} valid_atom[] = {
- { "refname" },
- { "objecttype" },
- { "objectsize", FIELD_ULONG },
- { "objectname" },
- { "tree" },
- { "parent" },
- { "numparent", FIELD_ULONG },
- { "object" },
- { "type" },
- { "tag" },
- { "author" },
- { "authorname" },
- { "authoremail" },
- { "authordate", FIELD_TIME },
- { "committer" },
- { "committername" },
- { "committeremail" },
- { "committerdate", FIELD_TIME },
- { "tagger" },
- { "taggername" },
- { "taggeremail" },
- { "taggerdate", FIELD_TIME },
- { "creator" },
- { "creatordate", FIELD_TIME },
- { "subject" },
- { "body" },
- { "contents" },
- { "contents:subject" },
- { "contents:body" },
- { "contents:signature" },
- { "upstream" },
- { "symref" },
- { "flag" },
- { "HEAD" },
- { "color" },
-};
-
-/*
- * An atom is a valid field atom listed above, possibly prefixed with
- * a "*" to denote deref_tag().
- *
- * We parse given format string and sort specifiers, and make a list
- * of properties that we need to extract out of objects. refinfo
- * structure will hold an array of values extracted that can be
- * indexed with the "atom number", which is an index into this
- * array.
- */
-static const char **used_atom;
-static cmp_type *used_atom_type;
-static int used_atom_cnt, need_tagged, need_symref;
-static int need_color_reset_at_eol;
-
-/*
- * Used to parse format string and sort specifiers
- */
-static int parse_atom(const char *atom, const char *ep)
-{
- const char *sp;
- int i, at;
-
- sp = atom;
- if (*sp == '*' && sp < ep)
- sp++; /* deref */
- if (ep <= sp)
- die("malformed field name: %.*s", (int)(ep-atom), atom);
-
- /* Do we have the atom already used elsewhere? */
- for (i = 0; i < used_atom_cnt; i++) {
- int len = strlen(used_atom[i]);
- if (len == ep - atom && !memcmp(used_atom[i], atom, len))
- return i;
- }
-
- /* Is the atom a valid one? */
- for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
- int len = strlen(valid_atom[i].name);
- /*
- * If the atom name has a colon, strip it and everything after
- * it off - it specifies the format for this entry, and
- * shouldn't be used for checking against the valid_atom
- * table.
- */
- const char *formatp = strchr(sp, ':');
- if (!formatp || ep < formatp)
- formatp = ep;
- if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
- break;
- }
-
- if (ARRAY_SIZE(valid_atom) <= i)
- die("unknown field name: %.*s", (int)(ep-atom), atom);
-
- /* Add it in, including the deref prefix */
- at = used_atom_cnt;
- used_atom_cnt++;
- REALLOC_ARRAY(used_atom, used_atom_cnt);
- REALLOC_ARRAY(used_atom_type, used_atom_cnt);
- used_atom[at] = xmemdupz(atom, ep - atom);
- used_atom_type[at] = valid_atom[i].cmp_type;
- if (*atom == '*')
- need_tagged = 1;
- if (!strcmp(used_atom[at], "symref"))
- need_symref = 1;
- return at;
-}
-
-/*
- * In a format string, find the next occurrence of %(atom).
- */
-static const char *find_next(const char *cp)
-{
- while (*cp) {
- if (*cp == '%') {
- /*
- * %( is the start of an atom;
- * %% is a quoted per-cent.
- */
- if (cp[1] == '(')
- return cp;
- else if (cp[1] == '%')
- cp++; /* skip over two % */
- /* otherwise this is a singleton, literal % */
- }
- cp++;
- }
- return NULL;
-}
-
-/*
- * Make sure the format string is well formed, and parse out
- * the used atoms.
- */
-static int verify_format(const char *format)
-{
- const char *cp, *sp;
-
- need_color_reset_at_eol = 0;
- for (cp = format; *cp && (sp = find_next(cp)); ) {
- const char *color, *ep = strchr(sp, ')');
- int at;
-
- if (!ep)
- return error("malformed format string %s", sp);
- /* sp points at "%(" and ep points at the closing ")" */
- at = parse_atom(sp + 2, ep);
- cp = ep + 1;
-
- if (skip_prefix(used_atom[at], "color:", &color))
- need_color_reset_at_eol = !!strcmp(color, "reset");
- }
- return 0;
-}
-
-/*
- * Given an object name, read the object data and size, and return a
- * "struct object". If the object data we are returning is also borrowed
- * by the "struct object" representation, set *eaten as well---it is a
- * signal from parse_object_buffer to us not to free the buffer.
- */
-static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
-{
- enum object_type type;
- void *buf = read_sha1_file(sha1, &type, sz);
-
- if (buf)
- *obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
- else
- *obj = NULL;
- return buf;
-}
-
-static int grab_objectname(const char *name, const unsigned char *sha1,
- struct atom_value *v)
-{
- if (!strcmp(name, "objectname")) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(sha1));
- v->s = s;
- return 1;
- }
- if (!strcmp(name, "objectname:short")) {
- v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
- return 1;
- }
- return 0;
-}
-
-/* See grab_values */
-static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
- int i;
-
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &val[i];
- if (!!deref != (*name == '*'))
- continue;
- if (deref)
- name++;
- if (!strcmp(name, "objecttype"))
- v->s = typename(obj->type);
- else if (!strcmp(name, "objectsize")) {
- char *s = xmalloc(40);
- sprintf(s, "%lu", sz);
- v->ul = sz;
- v->s = s;
- }
- else if (deref)
- grab_objectname(name, obj->sha1, v);
- }
-}
-
-/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
- int i;
- struct tag *tag = (struct tag *) obj;
-
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &val[i];
- if (!!deref != (*name == '*'))
- continue;
- if (deref)
- name++;
- if (!strcmp(name, "tag"))
- v->s = tag->tag;
- else if (!strcmp(name, "type") && tag->tagged)
- v->s = typename(tag->tagged->type);
- else if (!strcmp(name, "object") && tag->tagged) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(tag->tagged->sha1));
- v->s = s;
- }
- }
-}
-
-/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
- int i;
- struct commit *commit = (struct commit *) obj;
-
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &val[i];
- if (!!deref != (*name == '*'))
- continue;
- if (deref)
- name++;
- if (!strcmp(name, "tree")) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(commit->tree->object.sha1));
- v->s = s;
- }
- if (!strcmp(name, "numparent")) {
- char *s = xmalloc(40);
- v->ul = commit_list_count(commit->parents);
- sprintf(s, "%lu", v->ul);
- v->s = s;
- }
- else if (!strcmp(name, "parent")) {
- int num = commit_list_count(commit->parents);
- int i;
- struct commit_list *parents;
- char *s = xmalloc(41 * num + 1);
- v->s = s;
- for (i = 0, parents = commit->parents;
- parents;
- parents = parents->next, i = i + 41) {
- struct commit *parent = parents->item;
- strcpy(s+i, sha1_to_hex(parent->object.sha1));
- if (parents->next)
- s[i+40] = ' ';
- }
- if (!i)
- *s = '\0';
- }
- }
-}
-
-static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
-{
- const char *eol;
- while (*buf) {
- if (!strncmp(buf, who, wholen) &&
- buf[wholen] == ' ')
- return buf + wholen + 1;
- eol = strchr(buf, '\n');
- if (!eol)
- return "";
- eol++;
- if (*eol == '\n')
- return ""; /* end of header */
- buf = eol;
- }
- return "";
-}
-
-static const char *copy_line(const char *buf)
-{
- const char *eol = strchrnul(buf, '\n');
- return xmemdupz(buf, eol - buf);
-}
-
-static const char *copy_name(const char *buf)
-{
- const char *cp;
- for (cp = buf; *cp && *cp != '\n'; cp++) {
- if (!strncmp(cp, " <", 2))
- return xmemdupz(buf, cp - buf);
- }
- return "";
-}
-
-static const char *copy_email(const char *buf)
-{
- const char *email = strchr(buf, '<');
- const char *eoemail;
- if (!email)
- return "";
- eoemail = strchr(email, '>');
- if (!eoemail)
- return "";
- return xmemdupz(email, eoemail + 1 - email);
-}
-
-static char *copy_subject(const char *buf, unsigned long len)
-{
- char *r = xmemdupz(buf, len);
- int i;
-
- for (i = 0; i < len; i++)
- if (r[i] == '\n')
- r[i] = ' ';
-
- return r;
-}
-
-static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
-{
- const char *eoemail = strstr(buf, "> ");
- char *zone;
- unsigned long timestamp;
- long tz;
- enum date_mode date_mode = DATE_NORMAL;
- const char *formatp;
-
- /*
- * We got here because atomname ends in "date" or "date<something>";
- * it's not possible that <something> is not ":<format>" because
- * parse_atom() wouldn't have allowed it, so we can assume that no
- * ":" means no format is specified, and use the default.
- */
- formatp = strchr(atomname, ':');
- if (formatp != NULL) {
- formatp++;
- date_mode = parse_date_format(formatp);
- }
-
- if (!eoemail)
- goto bad;
- timestamp = strtoul(eoemail + 2, &zone, 10);
- if (timestamp == ULONG_MAX)
- goto bad;
- tz = strtol(zone, NULL, 10);
- if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
- goto bad;
- v->s = xstrdup(show_date(timestamp, tz, date_mode));
- v->ul = timestamp;
- return;
- bad:
- v->s = "";
- v->ul = 0;
-}
-
-/* See grab_values */
-static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
- int i;
- int wholen = strlen(who);
- const char *wholine = NULL;
-
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &val[i];
- if (!!deref != (*name == '*'))
- continue;
- if (deref)
- name++;
- if (strncmp(who, name, wholen))
- continue;
- if (name[wholen] != 0 &&
- strcmp(name + wholen, "name") &&
- strcmp(name + wholen, "email") &&
- !starts_with(name + wholen, "date"))
- continue;
- if (!wholine)
- wholine = find_wholine(who, wholen, buf, sz);
- if (!wholine)
- return; /* no point looking for it */
- if (name[wholen] == 0)
- v->s = copy_line(wholine);
- else if (!strcmp(name + wholen, "name"))
- v->s = copy_name(wholine);
- else if (!strcmp(name + wholen, "email"))
- v->s = copy_email(wholine);
- else if (starts_with(name + wholen, "date"))
- grab_date(wholine, v, name);
- }
-
- /*
- * For a tag or a commit object, if "creator" or "creatordate" is
- * requested, do something special.
- */
- if (strcmp(who, "tagger") && strcmp(who, "committer"))
- return; /* "author" for commit object is not wanted */
- if (!wholine)
- wholine = find_wholine(who, wholen, buf, sz);
- if (!wholine)
- return;
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &val[i];
- if (!!deref != (*name == '*'))
- continue;
- if (deref)
- name++;
-
- if (starts_with(name, "creatordate"))
- grab_date(wholine, v, name);
- else if (!strcmp(name, "creator"))
- v->s = copy_line(wholine);
- }
-}
-
-static void find_subpos(const char *buf, unsigned long sz,
- const char **sub, unsigned long *sublen,
- const char **body, unsigned long *bodylen,
- unsigned long *nonsiglen,
- const char **sig, unsigned long *siglen)
-{
- const char *eol;
- /* skip past header until we hit empty line */
- while (*buf && *buf != '\n') {
- eol = strchrnul(buf, '\n');
- if (*eol)
- eol++;
- buf = eol;
- }
- /* skip any empty lines */
- while (*buf == '\n')
- buf++;
-
- /* parse signature first; we might not even have a subject line */
- *sig = buf + parse_signature(buf, strlen(buf));
- *siglen = strlen(*sig);
-
- /* subject is first non-empty line */
- *sub = buf;
- /* subject goes to first empty line */
- while (buf < *sig && *buf && *buf != '\n') {
- eol = strchrnul(buf, '\n');
- if (*eol)
- eol++;
- buf = eol;
- }
- *sublen = buf - *sub;
- /* drop trailing newline, if present */
- if (*sublen && (*sub)[*sublen - 1] == '\n')
- *sublen -= 1;
-
- /* skip any empty lines */
- while (*buf == '\n')
- buf++;
- *body = buf;
- *bodylen = strlen(buf);
- *nonsiglen = *sig - buf;
-}
-
-/* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
- int i;
- const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
- unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
-
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &val[i];
- if (!!deref != (*name == '*'))
- continue;
- if (deref)
- name++;
- if (strcmp(name, "subject") &&
- strcmp(name, "body") &&
- strcmp(name, "contents") &&
- strcmp(name, "contents:subject") &&
- strcmp(name, "contents:body") &&
- strcmp(name, "contents:signature"))
- continue;
- if (!subpos)
- find_subpos(buf, sz,
- &subpos, &sublen,
- &bodypos, &bodylen, &nonsiglen,
- &sigpos, &siglen);
-
- if (!strcmp(name, "subject"))
- v->s = copy_subject(subpos, sublen);
- else if (!strcmp(name, "contents:subject"))
- v->s = copy_subject(subpos, sublen);
- else if (!strcmp(name, "body"))
- v->s = xmemdupz(bodypos, bodylen);
- else if (!strcmp(name, "contents:body"))
- v->s = xmemdupz(bodypos, nonsiglen);
- else if (!strcmp(name, "contents:signature"))
- v->s = xmemdupz(sigpos, siglen);
- else if (!strcmp(name, "contents"))
- v->s = xstrdup(subpos);
- }
-}
-
-/*
- * We want to have empty print-string for field requests
- * that do not apply (e.g. "authordate" for a tag object)
- */
-static void fill_missing_values(struct atom_value *val)
-{
- int i;
- for (i = 0; i < used_atom_cnt; i++) {
- struct atom_value *v = &val[i];
- if (v->s == NULL)
- v->s = "";
- }
-}
-
-/*
- * val is a list of atom_value to hold returned values. Extract
- * the values for atoms in used_atom array out of (obj, buf, sz).
- * when deref is false, (obj, buf, sz) is the object that is
- * pointed at by the ref itself; otherwise it is the object the
- * ref (which is a tag) refers to.
- */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
- grab_common_values(val, deref, obj, buf, sz);
- switch (obj->type) {
- case OBJ_TAG:
- grab_tag_values(val, deref, obj, buf, sz);
- grab_sub_body_contents(val, deref, obj, buf, sz);
- grab_person("tagger", val, deref, obj, buf, sz);
- break;
- case OBJ_COMMIT:
- grab_commit_values(val, deref, obj, buf, sz);
- grab_sub_body_contents(val, deref, obj, buf, sz);
- grab_person("author", val, deref, obj, buf, sz);
- grab_person("committer", val, deref, obj, buf, sz);
- break;
- case OBJ_TREE:
- /* grab_tree_values(val, deref, obj, buf, sz); */
- break;
- case OBJ_BLOB:
- /* grab_blob_values(val, deref, obj, buf, sz); */
- break;
- default:
- die("Eh? Object of type %d?", obj->type);
- }
-}
-
-static inline char *copy_advance(char *dst, const char *src)
-{
- while (*src)
- *dst++ = *src++;
- return dst;
-}
-
-/*
- * Parse the object referred by ref, and grab needed value.
- */
-static void populate_value(struct refinfo *ref)
-{
- void *buf;
- struct object *obj;
- int eaten, i;
- unsigned long size;
- const unsigned char *tagged;
-
- ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
-
- if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
- unsigned char unused1[20];
- ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
- unused1, NULL);
- if (!ref->symref)
- ref->symref = "";
- }
-
- /* Fill in specials first */
- for (i = 0; i < used_atom_cnt; i++) {
- const char *name = used_atom[i];
- struct atom_value *v = &ref->value[i];
- int deref = 0;
- const char *refname;
- const char *formatp;
- struct branch *branch = NULL;
-
- if (*name == '*') {
- deref = 1;
- name++;
- }
-
- if (starts_with(name, "refname"))
- refname = ref->refname;
- else if (starts_with(name, "symref"))
- refname = ref->symref ? ref->symref : "";
- else if (starts_with(name, "upstream")) {
- /* only local branches may have an upstream */
- if (!starts_with(ref->refname, "refs/heads/"))
- continue;
- branch = branch_get(ref->refname + 11);
-
- if (!branch || !branch->merge || !branch->merge[0] ||
- !branch->merge[0]->dst)
- continue;
- refname = branch->merge[0]->dst;
- } else if (starts_with(name, "color:")) {
- char color[COLOR_MAXLEN] = "";
-
- if (color_parse(name + 6, color) < 0)
- die(_("unable to parse format"));
- v->s = xstrdup(color);
- continue;
- } else if (!strcmp(name, "flag")) {
- char buf[256], *cp = buf;
- if (ref->flag & REF_ISSYMREF)
- cp = copy_advance(cp, ",symref");
- if (ref->flag & REF_ISPACKED)
- cp = copy_advance(cp, ",packed");
- if (cp == buf)
- v->s = "";
- else {
- *cp = '\0';
- v->s = xstrdup(buf + 1);
- }
- continue;
- } else if (!deref && grab_objectname(name, ref->objectname, v)) {
- continue;
- } else if (!strcmp(name, "HEAD")) {
- const char *head;
- unsigned char sha1[20];
-
- head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- sha1, NULL);
- if (!strcmp(ref->refname, head))
- v->s = "*";
- else
- v->s = " ";
- continue;
- } else
- continue;
-
- formatp = strchr(name, ':');
- if (formatp) {
- int num_ours, num_theirs;
-
- formatp++;
- if (!strcmp(formatp, "short"))
- refname = shorten_unambiguous_ref(refname,
- warn_ambiguous_refs);
- else if (!strcmp(formatp, "track") &&
- starts_with(name, "upstream")) {
- char buf[40];
-
- if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
- continue;
-
- if (!num_ours && !num_theirs)
- v->s = "";
- else if (!num_ours) {
- sprintf(buf, "[behind %d]", num_theirs);
- v->s = xstrdup(buf);
- } else if (!num_theirs) {
- sprintf(buf, "[ahead %d]", num_ours);
- v->s = xstrdup(buf);
- } else {
- sprintf(buf, "[ahead %d, behind %d]",
- num_ours, num_theirs);
- v->s = xstrdup(buf);
- }
- continue;
- } else if (!strcmp(formatp, "trackshort") &&
- starts_with(name, "upstream")) {
- assert(branch);
-
- if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
- continue;
-
- if (!num_ours && !num_theirs)
- v->s = "=";
- else if (!num_ours)
- v->s = "<";
- else if (!num_theirs)
- v->s = ">";
- else
- v->s = "<>";
- continue;
- } else
- die("unknown %.*s format %s",
- (int)(formatp - name), name, formatp);
- }
-
- if (!deref)
- v->s = refname;
- else {
- int len = strlen(refname);
- char *s = xmalloc(len + 4);
- sprintf(s, "%s^{}", refname);
- v->s = s;
- }
- }
-
- for (i = 0; i < used_atom_cnt; i++) {
- struct atom_value *v = &ref->value[i];
- if (v->s == NULL)
- goto need_obj;
- }
- return;
-
- need_obj:
- buf = get_obj(ref->objectname, &obj, &size, &eaten);
- if (!buf)
- die("missing object %s for %s",
- sha1_to_hex(ref->objectname), ref->refname);
- if (!obj)
- die("parse_object_buffer failed on %s for %s",
- sha1_to_hex(ref->objectname), ref->refname);
-
- grab_values(ref->value, 0, obj, buf, size);
- if (!eaten)
- free(buf);
-
- /*
- * If there is no atom that wants to know about tagged
- * object, we are done.
- */
- if (!need_tagged || (obj->type != OBJ_TAG))
- return;
-
- /*
- * If it is a tag object, see if we use a value that derefs
- * the object, and if we do grab the object it refers to.
- */
- tagged = ((struct tag *)obj)->tagged->sha1;
-
- /*
- * NEEDSWORK: This derefs tag only once, which
- * is good to deal with chains of trust, but
- * is not consistent with what deref_tag() does
- * which peels the onion to the core.
- */
- buf = get_obj(tagged, &obj, &size, &eaten);
- if (!buf)
- die("missing object %s for %s",
- sha1_to_hex(tagged), ref->refname);
- if (!obj)
- die("parse_object_buffer failed on %s for %s",
- sha1_to_hex(tagged), ref->refname);
- grab_values(ref->value, 1, obj, buf, size);
- if (!eaten)
- free(buf);
-}
-
-/*
- * Given a ref, return the value for the atom. This lazily gets value
- * out of the object by calling populate value.
- */
-static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
-{
- if (!ref->value) {
- populate_value(ref);
- fill_missing_values(ref->value);
- }
- *v = &ref->value[atom];
-}
-
-struct grab_ref_cbdata {
- struct refinfo **grab_array;
- const char **grab_pattern;
- int grab_cnt;
-};
-
-/*
- * A call-back given to for_each_ref(). Filter refs and keep them for
- * later object processing.
- */
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
- struct grab_ref_cbdata *cb = cb_data;
- struct refinfo *ref;
- int cnt;
-
- if (flag & REF_BAD_NAME) {
- warning("ignoring ref with broken name %s", refname);
- return 0;
- }
-
- if (flag & REF_ISBROKEN) {
- warning("ignoring broken ref %s", refname);
- return 0;
- }
-
- if (*cb->grab_pattern) {
- const char **pattern;
- int namelen = strlen(refname);
- for (pattern = cb->grab_pattern; *pattern; pattern++) {
- const char *p = *pattern;
- int plen = strlen(p);
-
- if ((plen <= namelen) &&
- !strncmp(refname, p, plen) &&
- (refname[plen] == '\0' ||
- refname[plen] == '/' ||
- p[plen-1] == '/'))
- break;
- if (!wildmatch(p, refname, WM_PATHNAME, NULL))
- break;
- }
- if (!*pattern)
- return 0;
- }
-
- /*
- * We do not open the object yet; sort may only need refname
- * to do its job and the resulting list may yet to be pruned
- * by maxcount logic.
- */
- ref = xcalloc(1, sizeof(*ref));
- ref->refname = xstrdup(refname);
- hashcpy(ref->objectname, sha1);
- ref->flag = flag;
-
- cnt = cb->grab_cnt;
- REALLOC_ARRAY(cb->grab_array, cnt + 1);
- cb->grab_array[cnt++] = ref;
- cb->grab_cnt = cnt;
- return 0;
-}
-
-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
-{
- struct atom_value *va, *vb;
- int cmp;
- cmp_type cmp_type = used_atom_type[s->atom];
-
- get_value(a, s->atom, &va);
- get_value(b, s->atom, &vb);
- switch (cmp_type) {
- case FIELD_STR:
- cmp = strcmp(va->s, vb->s);
- break;
- default:
- if (va->ul < vb->ul)
- cmp = -1;
- else if (va->ul == vb->ul)
- cmp = 0;
- else
- cmp = 1;
- break;
- }
- return (s->reverse) ? -cmp : cmp;
-}
-
-static struct ref_sort *ref_sort;
-static int compare_refs(const void *a_, const void *b_)
-{
- struct refinfo *a = *((struct refinfo **)a_);
- struct refinfo *b = *((struct refinfo **)b_);
- struct ref_sort *s;
-
- for (s = ref_sort; s; s = s->next) {
- int cmp = cmp_ref_sort(s, a, b);
- if (cmp)
- return cmp;
- }
- return 0;
-}
-
-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
-{
- ref_sort = sort;
- qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
-}
-
-static void print_value(struct atom_value *v, int quote_style)
-{
- struct strbuf sb = STRBUF_INIT;
- switch (quote_style) {
- case QUOTE_NONE:
- fputs(v->s, stdout);
- break;
- case QUOTE_SHELL:
- sq_quote_buf(&sb, v->s);
- break;
- case QUOTE_PERL:
- perl_quote_buf(&sb, v->s);
- break;
- case QUOTE_PYTHON:
- python_quote_buf(&sb, v->s);
- break;
- case QUOTE_TCL:
- tcl_quote_buf(&sb, v->s);
- break;
- }
- if (quote_style != QUOTE_NONE) {
- fputs(sb.buf, stdout);
- strbuf_release(&sb);
- }
-}
-
-static int hex1(char ch)
-{
- if ('0' <= ch && ch <= '9')
- return ch - '0';
- else if ('a' <= ch && ch <= 'f')
- return ch - 'a' + 10;
- else if ('A' <= ch && ch <= 'F')
- return ch - 'A' + 10;
- return -1;
-}
-static int hex2(const char *cp)
-{
- if (cp[0] && cp[1])
- return (hex1(cp[0]) << 4) | hex1(cp[1]);
- else
- return -1;
-}
-
-static void emit(const char *cp, const char *ep)
-{
- while (*cp && (!ep || cp < ep)) {
- if (*cp == '%') {
- if (cp[1] == '%')
- cp++;
- else {
- int ch = hex2(cp + 1);
- if (0 <= ch) {
- putchar(ch);
- cp += 3;
- continue;
- }
- }
- }
- putchar(*cp);
- cp++;
- }
-}
-
-static void show_ref(struct refinfo *info, const char *format, int quote_style)
-{
- const char *cp, *sp, *ep;
-
- for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
- struct atom_value *atomv;
-
- ep = strchr(sp, ')');
- if (cp < sp)
- emit(cp, sp);
- get_value(info, parse_atom(sp + 2, ep), &atomv);
- print_value(atomv, quote_style);
- }
- if (*cp) {
- sp = cp + strlen(cp);
- emit(cp, sp);
- }
- if (need_color_reset_at_eol) {
- struct atom_value resetv;
- char color[COLOR_MAXLEN] = "";
-
- if (color_parse("reset", color) < 0)
- die("BUG: couldn't parse 'reset' as a color");
- resetv.s = color;
- print_value(&resetv, quote_style);
- }
- putchar('\n');
-}
-
-static struct ref_sort *default_sort(void)
-{
- static const char cstr_name[] = "refname";
-
- struct ref_sort *sort = xcalloc(1, sizeof(*sort));
-
- sort->next = NULL;
- sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
- return sort;
-}
-
-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
-{
- struct ref_sort **sort_tail = opt->value;
- struct ref_sort *s;
- int len;
-
- if (!arg) /* should --no-sort void the list ? */
- return -1;
-
- s = xcalloc(1, sizeof(*s));
- s->next = *sort_tail;
- *sort_tail = s;
-
- if (*arg == '-') {
- s->reverse = 1;
- arg++;
- }
- len = strlen(arg);
- s->atom = parse_atom(arg, arg+len);
- return 0;
-}
+#include "ref-filter.h"
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
+ N_("git for-each-ref [--points-at <object>]"),
+ N_("git for-each-ref [(--merged | --no-merged) [<object>]]"),
+ N_("git for-each-ref [--contains [<object>]]"),
NULL
};
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
- int i, num_refs;
+ int i;
const char *format = "%(objectname) %(objecttype)\t%(refname)";
- struct ref_sort *sort = NULL, **sort_tail = &sort;
+ struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
int maxcount = 0, quote_style = 0;
- struct refinfo **refs;
- struct grab_ref_cbdata cbdata;
+ struct ref_array array;
+ struct ref_filter filter;
struct option opts[] = {
OPT_BIT('s', "shell", &quote_style,
@@ -1091,11 +35,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
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_CALLBACK(0 , "sort", sort_tail, N_("key"),
- N_("field name to sort on"), &opt_parse_sort),
+ 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,
+ N_("object"), N_("print only refs which points at the given object"),
+ parse_opt_object_name),
+ OPT_MERGED(&filter, N_("print only refs that are merged")),
+ OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
+ OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
OPT_END(),
};
+ memset(&array, 0, sizeof(array));
+ memset(&filter, 0, sizeof(filter));
+
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
if (maxcount < 0) {
error("invalid --count argument: `%d'", maxcount);
@@ -1105,26 +58,24 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
error("more than one quoting style?");
usage_with_options(for_each_ref_usage, opts);
}
- if (verify_format(format))
+ if (verify_ref_format(format))
usage_with_options(for_each_ref_usage, opts);
- if (!sort)
- sort = default_sort();
+ if (!sorting)
+ sorting = ref_default_sorting();
/* for warn_ambiguous_refs */
git_config(git_default_config, NULL);
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.grab_pattern = argv;
- for_each_rawref(grab_single_ref, &cbdata);
- refs = cbdata.grab_array;
- num_refs = cbdata.grab_cnt;
-
- sort_refs(sort, refs, num_refs);
+ filter.name_patterns = argv;
+ filter.match_as_path = 1;
+ filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
+ ref_array_sort(sorting, &array);
- if (!maxcount || num_refs < maxcount)
- maxcount = num_refs;
+ if (!maxcount || array.nr < maxcount)
+ maxcount = array.nr;
for (i = 0; i < maxcount; i++)
- show_ref(refs[i], format, quote_style);
+ show_ref_array_item(array.items[i], format, quote_style);
+ ref_array_clear(&array);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 6b6f319..2de272e 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -13,6 +13,7 @@
#include "dir.h"
#include "progress.h"
#include "streaming.h"
+#include "decorate.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -23,60 +24,85 @@ static int show_tags;
static int show_unreachable;
static int include_reflogs = 1;
static int check_full = 1;
+static int connectivity_only;
static int check_strict;
static int keep_cache_objects;
-static unsigned char head_sha1[20];
+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
+static struct object_id head_oid;
static const char *head_points_at;
static int errors_found;
static int write_lost_and_found;
static int verbose;
static int show_progress = -1;
static int show_dangling = 1;
+static int name_objects;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
#define ERROR_PACK 04
+#define ERROR_REFS 010
-#ifdef NO_D_INO_IN_DIRENT
-#define SORT_DIRENT 0
-#define DIRENT_SORT_HINT(de) 0
-#else
-#define SORT_DIRENT 1
-#define DIRENT_SORT_HINT(de) ((de)->d_ino)
-#endif
+static const char *describe_object(struct object *obj)
+{
+ static struct strbuf buf = STRBUF_INIT;
+ char *name = name_objects ?
+ lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
+
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, oid_to_hex(&obj->oid));
+ if (name)
+ strbuf_addf(&buf, " (%s)", name);
+
+ return buf.buf;
+}
+
+static int fsck_config(const char *var, const char *value, void *cb)
+{
+ if (strcmp(var, "fsck.skiplist") == 0) {
+ const char *path;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (git_config_pathname(&path, var, value))
+ return 1;
+ strbuf_addf(&sb, "skiplist=%s", path);
+ free((char *)path);
+ fsck_set_msg_types(&fsck_obj_options, sb.buf);
+ strbuf_release(&sb);
+ return 0;
+ }
-static void objreport(struct object *obj, const char *severity,
- const char *err, va_list params)
+ if (skip_prefix(var, "fsck.", &var)) {
+ fsck_set_msg_type(&fsck_obj_options, var, value);
+ return 0;
+ }
+
+ return git_default_config(var, value, cb);
+}
+
+static void objreport(struct object *obj, const char *msg_type,
+ const char *err)
{
- fprintf(stderr, "%s in %s %s: ",
- severity, typename(obj->type), sha1_to_hex(obj->sha1));
- vfprintf(stderr, err, params);
- fputs("\n", stderr);
+ fprintf(stderr, "%s in %s %s: %s\n",
+ msg_type, typename(obj->type), describe_object(obj), err);
}
-__attribute__((format (printf, 2, 3)))
-static int objerror(struct object *obj, const char *err, ...)
+static int objerror(struct object *obj, const char *err)
{
- va_list params;
- va_start(params, err);
errors_found |= ERROR_OBJECT;
- objreport(obj, "error", err, params);
- va_end(params);
+ objreport(obj, "error", err);
return -1;
}
-__attribute__((format (printf, 3, 4)))
-static int fsck_error_func(struct object *obj, int type, const char *err, ...)
+static int fsck_error_func(struct fsck_options *o,
+ struct object *obj, int type, const char *message)
{
- va_list params;
- va_start(params, err);
- objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
- va_end(params);
+ objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
return (type == FSCK_WARN) ? 0 : 1;
}
static struct object_array pending;
-static int mark_object(struct object *obj, int type, void *data)
+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)
{
struct object *parent = data;
@@ -88,7 +114,7 @@ static int mark_object(struct object *obj, int type, void *data)
if (!obj) {
/* ... these references to parent->fld are safe here */
printf("broken link from %7s %s\n",
- typename(parent->type), sha1_to_hex(parent->sha1));
+ typename(parent->type), describe_object(parent));
printf("broken link from %7s %s\n",
(type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
errors_found |= ERROR_REACHABLE;
@@ -103,11 +129,11 @@ static int mark_object(struct object *obj, int type, void *data)
return 0;
obj->flags |= REACHABLE;
if (!(obj->flags & HAS_OBJ)) {
- if (parent && !has_sha1_file(obj->sha1)) {
+ if (parent && !has_object_file(&obj->oid)) {
printf("broken link from %7s %s\n",
- typename(parent->type), sha1_to_hex(parent->sha1));
+ typename(parent->type), describe_object(parent));
printf(" to %7s %s\n",
- typename(obj->type), sha1_to_hex(obj->sha1));
+ typename(obj->type), describe_object(obj));
errors_found |= ERROR_REACHABLE;
}
return 1;
@@ -119,7 +145,7 @@ static int mark_object(struct object *obj, int type, void *data)
static void mark_object_reachable(struct object *obj)
{
- mark_object(obj, OBJ_ANY, NULL);
+ mark_object(obj, OBJ_ANY, NULL, NULL);
}
static int traverse_one_object(struct object *obj)
@@ -132,7 +158,7 @@ static int traverse_one_object(struct object *obj)
if (parse_tree(tree) < 0)
return 1; /* error already displayed */
}
- result = fsck_walk(obj, mark_object, obj);
+ result = fsck_walk(obj, obj, &fsck_walk_options);
if (tree)
free_tree_buffer(tree);
return result;
@@ -158,7 +184,7 @@ static int traverse_reachable(void)
return !!result;
}
-static int mark_used(struct object *obj, int type, void *data)
+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)
{
if (!obj)
return 1;
@@ -177,9 +203,12 @@ static void check_reachable_object(struct object *obj)
* do a full fsck
*/
if (!(obj->flags & HAS_OBJ)) {
- if (has_sha1_pack(obj->sha1))
+ if (has_sha1_pack(obj->oid.hash))
return; /* it is in pack - forget about it */
- printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+ if (connectivity_only && has_object_file(&obj->oid))
+ return;
+ printf("missing %s %s\n", typename(obj->type),
+ describe_object(obj));
errors_found |= ERROR_REACHABLE;
return;
}
@@ -204,7 +233,8 @@ static void check_unreachable_object(struct object *obj)
* since this is something that is prunable.
*/
if (show_unreachable) {
- printf("unreachable %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+ printf("unreachable %s %s\n", typename(obj->type),
+ describe_object(obj));
return;
}
@@ -223,27 +253,29 @@ static void check_unreachable_object(struct object *obj)
if (!obj->used) {
if (show_dangling)
printf("dangling %s %s\n", typename(obj->type),
- sha1_to_hex(obj->sha1));
+ describe_object(obj));
if (write_lost_and_found) {
- char *filename = git_path("lost-found/%s/%s",
+ char *filename = git_pathdup("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
- sha1_to_hex(obj->sha1));
+ describe_object(obj));
FILE *f;
- if (safe_create_leading_directories(filename)) {
+ if (safe_create_leading_directories_const(filename)) {
error("Could not create lost-found");
+ free(filename);
return;
}
if (!(f = fopen(filename, "w")))
die_errno("Could not open '%s'", filename);
if (obj->type == OBJ_BLOB) {
- if (stream_blob_to_fd(fileno(f), obj->sha1, NULL, 1))
+ if (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))
die_errno("Could not write '%s'", filename);
} else
- fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
+ fprintf(f, "%s\n", describe_object(obj));
if (fclose(f))
die_errno("Could not finish '%s'",
filename);
+ free(filename);
}
return;
}
@@ -258,7 +290,7 @@ static void check_unreachable_object(struct object *obj)
static void check_object(struct object *obj)
{
if (verbose)
- fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
+ fprintf(stderr, "Checking %s\n", describe_object(obj));
if (obj->flags & REACHABLE)
check_reachable_object(obj);
@@ -294,11 +326,11 @@ static int fsck_obj(struct object *obj)
if (verbose)
fprintf(stderr, "Checking %s %s\n",
- typename(obj->type), sha1_to_hex(obj->sha1));
+ typename(obj->type), describe_object(obj));
- if (fsck_walk(obj, mark_used, NULL))
+ if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, "broken links");
- if (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))
+ if (fsck_object(obj, NULL, 0, &fsck_obj_options))
return -1;
if (obj->type == OBJ_TREE) {
@@ -313,15 +345,17 @@ static int fsck_obj(struct object *obj)
free_commit_buffer(commit);
if (!commit->parents && show_root)
- printf("root %s\n", sha1_to_hex(commit->object.sha1));
+ printf("root %s\n", describe_object(&commit->object));
}
if (obj->type == OBJ_TAG) {
struct tag *tag = (struct tag *) obj;
if (show_tags && tag->tagged) {
- printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
- printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
+ printf("tagged %s %s", typename(tag->tagged->type),
+ describe_object(tag->tagged));
+ printf(" (%s) in %s\n", tag->tag,
+ describe_object(&tag->object));
}
}
@@ -343,6 +377,10 @@ static int fsck_sha1(const unsigned char *sha1)
static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
unsigned long size, void *buffer, int *eaten)
{
+ /*
+ * Note, buffer may be NULL if type is OBJ_BLOB. See
+ * verify_packfile(), data_valid variable for details.
+ */
struct object *obj;
obj = parse_object_buffer(sha1, type, size, buffer, eaten);
if (!obj) {
@@ -353,111 +391,20 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
return fsck_obj(obj);
}
-/*
- * This is the sorting chunk size: make it reasonably
- * big so that we can sort well..
- */
-#define MAX_SHA1_ENTRIES (1024)
-
-struct sha1_entry {
- unsigned long ino;
- unsigned char sha1[20];
-};
-
-static struct {
- unsigned long nr;
- struct sha1_entry *entry[MAX_SHA1_ENTRIES];
-} sha1_list;
-
-static int ino_compare(const void *_a, const void *_b)
-{
- const struct sha1_entry *a = _a, *b = _b;
- unsigned long ino1 = a->ino, ino2 = b->ino;
- return ino1 < ino2 ? -1 : ino1 > ino2 ? 1 : 0;
-}
-
-static void fsck_sha1_list(void)
-{
- int i, nr = sha1_list.nr;
-
- if (SORT_DIRENT)
- qsort(sha1_list.entry, nr,
- sizeof(struct sha1_entry *), ino_compare);
- for (i = 0; i < nr; i++) {
- struct sha1_entry *entry = sha1_list.entry[i];
- unsigned char *sha1 = entry->sha1;
-
- sha1_list.entry[i] = NULL;
- if (fsck_sha1(sha1))
- errors_found |= ERROR_OBJECT;
- free(entry);
- }
- sha1_list.nr = 0;
-}
-
-static void add_sha1_list(unsigned char *sha1, unsigned long ino)
-{
- struct sha1_entry *entry = xmalloc(sizeof(*entry));
- int nr;
-
- entry->ino = ino;
- hashcpy(entry->sha1, sha1);
- nr = sha1_list.nr;
- if (nr == MAX_SHA1_ENTRIES) {
- fsck_sha1_list();
- nr = 0;
- }
- sha1_list.entry[nr] = entry;
- sha1_list.nr = ++nr;
-}
-
-static inline int is_loose_object_file(struct dirent *de,
- char *name, unsigned char *sha1)
-{
- if (strlen(de->d_name) != 38)
- return 0;
- memcpy(name + 2, de->d_name, 39);
- return !get_sha1_hex(name, sha1);
-}
-
-static void fsck_dir(int i, char *path)
-{
- DIR *dir = opendir(path);
- struct dirent *de;
- char name[100];
-
- if (!dir)
- return;
-
- if (verbose)
- fprintf(stderr, "Checking directory %s\n", path);
-
- sprintf(name, "%02x", i);
- while ((de = readdir(dir)) != NULL) {
- unsigned char sha1[20];
-
- if (is_dot_or_dotdot(de->d_name))
- continue;
- if (is_loose_object_file(de, name, sha1)) {
- add_sha1_list(sha1, DIRENT_SORT_HINT(de));
- continue;
- }
- if (starts_with(de->d_name, "tmp_obj_"))
- continue;
- fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
- }
- closedir(dir);
-}
-
static int default_refs;
-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
+static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
+ unsigned long timestamp)
{
struct object *obj;
if (!is_null_sha1(sha1)) {
obj = lookup_object(sha1);
if (obj) {
+ if (timestamp && name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj,
+ xstrfmt("%s@{%ld}", refname, timestamp));
obj->used = 1;
mark_object_reachable(obj);
} else {
@@ -477,32 +424,39 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
fprintf(stderr, "Checking reflog %s->%s\n",
sha1_to_hex(osha1), sha1_to_hex(nsha1));
- fsck_handle_reflog_sha1(refname, osha1);
- fsck_handle_reflog_sha1(refname, nsha1);
+ fsck_handle_reflog_sha1(refname, osha1, 0);
+ fsck_handle_reflog_sha1(refname, nsha1, timestamp);
return 0;
}
-static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
+static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
+ int flag, void *cb_data)
{
for_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);
return 0;
}
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int fsck_handle_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct object *obj;
- obj = parse_object(sha1);
+ obj = parse_object(oid->hash);
if (!obj) {
- error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1));
+ error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
- if (obj->type != OBJ_COMMIT && is_branch(refname))
+ if (obj->type != OBJ_COMMIT && is_branch(refname)) {
error("%s: not a commit", refname);
+ errors_found |= ERROR_REFS;
+ }
default_refs++;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(refname));
mark_object_reachable(obj);
return 0;
@@ -510,8 +464,8 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
static void get_default_heads(void)
{
- if (head_points_at && !is_null_sha1(head_sha1))
- fsck_handle_ref("HEAD", head_sha1, 0, NULL);
+ if (head_points_at && !is_null_oid(&head_oid))
+ fsck_handle_ref("HEAD", &head_oid, 0, NULL);
for_each_rawref(fsck_handle_ref, NULL);
if (include_reflogs)
for_each_reflog(fsck_handle_reflog, NULL);
@@ -534,9 +488,28 @@ static void get_default_heads(void)
}
}
+static int fsck_loose(const unsigned char *sha1, const char *path, void *data)
+{
+ if (fsck_sha1(sha1))
+ errors_found |= ERROR_OBJECT;
+ return 0;
+}
+
+static int fsck_cruft(const char *basename, const char *path, void *data)
+{
+ if (!starts_with(basename, "tmp_obj_"))
+ fprintf(stderr, "bad sha1 file: %s\n", path);
+ return 0;
+}
+
+static int fsck_subdir(int nr, const char *path, void *progress)
+{
+ display_progress(progress, nr + 1);
+ return 0;
+}
+
static void fsck_object_dir(const char *path)
{
- int i;
struct progress *progress = NULL;
if (verbose)
@@ -544,36 +517,38 @@ static void fsck_object_dir(const char *path)
if (show_progress)
progress = start_progress(_("Checking object directories"), 256);
- for (i = 0; i < 256; i++) {
- static char dir[4096];
- sprintf(dir, "%s/%02x", path, i);
- fsck_dir(i, dir);
- display_progress(progress, i+1);
- }
+
+ for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
+ progress);
+ display_progress(progress, 256);
stop_progress(&progress);
- fsck_sha1_list();
}
static int fsck_head_link(void)
{
- int flag;
int null_is_error = 0;
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", 0, head_sha1, &flag);
- if (!head_points_at)
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
+ if (!head_points_at) {
+ errors_found |= ERROR_REFS;
return error("Invalid HEAD");
+ }
if (!strcmp(head_points_at, "HEAD"))
/* detached HEAD */
null_is_error = 1;
- else if (!starts_with(head_points_at, "refs/heads/"))
+ else if (!starts_with(head_points_at, "refs/heads/")) {
+ errors_found |= ERROR_REFS;
return error("HEAD points to something strange (%s)",
head_points_at);
- if (is_null_sha1(head_sha1)) {
- if (null_is_error)
+ }
+ if (is_null_oid(&head_oid)) {
+ if (null_is_error) {
+ errors_found |= ERROR_REFS;
return error("HEAD: detached HEAD points at nothing");
+ }
fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
head_points_at + 11);
}
@@ -593,9 +568,13 @@ static int fsck_cache_tree(struct cache_tree *it)
if (!obj) {
error("%s: invalid sha1 pointer in cache-tree",
sha1_to_hex(it->sha1));
+ errors_found |= ERROR_REFS;
return 1;
}
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(":"));
mark_object_reachable(obj);
if (obj->type != OBJ_TREE)
err |= objerror(obj, "non-tree in cache-tree");
@@ -619,10 +598,12 @@ static struct option fsck_opts[] = {
OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")),
OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")),
OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")),
+ OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")),
OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")),
OPT_BOOL(0, "lost-found", &write_lost_and_found,
N_("write dangling objects in .git/lost-found")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+ OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
OPT_END(),
};
@@ -636,6 +617,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
+ fsck_walk_options.walk = mark_object;
+ fsck_obj_options.walk = mark_used;
+ fsck_obj_options.error_func = fsck_error_func;
+ if (check_strict)
+ fsck_obj_options.strict = 1;
+
if (show_progress == -1)
show_progress = isatty(2);
if (verbose)
@@ -646,16 +633,25 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
include_reflogs = 0;
}
+ if (name_objects)
+ fsck_walk_options.object_names =
+ xcalloc(1, sizeof(struct decoration));
+
+ git_config(fsck_config, NULL);
+
fsck_head_link();
- fsck_object_dir(get_object_directory());
-
- prepare_alt_odb();
- for (alt = alt_odb_list; alt; alt = alt->next) {
- char namebuf[PATH_MAX];
- int namelen = alt->name - alt->base;
- memcpy(namebuf, alt->base, namelen);
- namebuf[namelen - 1] = 0;
- fsck_object_dir(namebuf);
+ if (!connectivity_only) {
+ fsck_object_dir(get_object_directory());
+
+ prepare_alt_odb();
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ /* directory name, minus trailing slash */
+ size_t namelen = alt->name - alt->base - 1;
+ struct strbuf name = STRBUF_INIT;
+ strbuf_add(&name, alt->base, namelen);
+ fsck_object_dir(name.buf);
+ strbuf_release(&name);
+ }
}
if (check_full) {
@@ -696,6 +692,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
continue;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(arg));
mark_object_reachable(obj);
heads++;
continue;
@@ -728,6 +727,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
continue;
obj = &blob->object;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj,
+ xstrfmt(":%s", active_cache[i]->name));
mark_object_reachable(obj);
}
if (active_cache_tree)
diff --git a/builtin/gc.c b/builtin/gc.c
index 0b8efc9..069950d 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,6 +11,7 @@
*/
#include "builtin.h"
+#include "tempfile.h"
#include "lockfile.h"
#include "parse-options.h"
#include "run-command.h"
@@ -33,24 +34,63 @@ static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static int detach_auto = 1;
static const char *prune_expire = "2.weeks.ago";
+static const char *prune_worktrees_expire = "3.months.ago";
static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
static struct argv_array reflog = ARGV_ARRAY_INIT;
static struct argv_array repack = ARGV_ARRAY_INIT;
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 char *pidfile;
+static struct tempfile pidfile;
+static struct lock_file log_lock;
-static void remove_pidfile(void)
+static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
+
+static void clean_pack_garbage(void)
{
- if (pidfile)
- unlink(pidfile);
+ int i;
+ for (i = 0; i < pack_garbage.nr; i++)
+ unlink_or_warn(pack_garbage.items[i].string);
+ string_list_clear(&pack_garbage, 0);
}
-static void remove_pidfile_on_signal(int signo)
+static void report_pack_garbage(unsigned seen_bits, const char *path)
{
- remove_pidfile();
+ if (seen_bits == PACKDIR_FILE_IDX)
+ string_list_append(&pack_garbage, path);
+}
+
+static void git_config_date_string(const char *key, const char **output)
+{
+ if (git_config_get_string_const(key, output))
+ return;
+ if (strcmp(*output, "now")) {
+ unsigned long now = approxidate("now");
+ if (approxidate(*output) >= now)
+ git_die_config(key, _("Invalid %s: '%s'"), key, *output);
+ }
+}
+
+static void process_log_file(void)
+{
+ struct stat st;
+ if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size)
+ commit_lock_file(&log_lock);
+ else
+ rollback_lock_file(&log_lock);
+}
+
+static void process_log_file_at_exit(void)
+{
+ fflush(stderr);
+ process_log_file();
+}
+
+static void process_log_file_on_signal(int signo)
+{
+ process_log_file();
sigchain_pop(signo);
raise(signo);
}
@@ -71,16 +111,8 @@ static void gc_config(void)
git_config_get_int("gc.auto", &gc_auto_threshold);
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
git_config_get_bool("gc.autodetach", &detach_auto);
-
- if (!git_config_get_string_const("gc.pruneexpire", &prune_expire)) {
- if (strcmp(prune_expire, "now")) {
- unsigned long now = approxidate("now");
- if (approxidate(prune_expire) >= now) {
- git_die_config("gc.pruneexpire", _("Invalid gc.pruneexpire: '%s'"),
- prune_expire);
- }
- }
- }
+ git_config_date_string("gc.pruneexpire", &prune_expire);
+ git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
git_config(git_default_config, NULL);
}
@@ -145,7 +177,7 @@ static int too_many_packs(void)
*/
cnt++;
}
- return gc_auto_pack_limit <= cnt;
+ return gc_auto_pack_limit < cnt;
}
static void add_repack_all_option(void)
@@ -194,20 +226,22 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
uintmax_t pid;
FILE *fp;
int fd;
+ char *pidfile_path;
- if (pidfile)
+ if (is_tempfile_active(&pidfile))
/* already locked */
return NULL;
if (gethostname(my_host, sizeof(my_host)))
- strcpy(my_host, "unknown");
+ xsnprintf(my_host, sizeof(my_host), "unknown");
- fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
+ pidfile_path = git_pathdup("gc.pid");
+ fd = hold_lock_file_for_update(&lock, pidfile_path,
LOCK_DIE_ON_ERROR);
if (!force) {
static char locking_host[128];
int should_exit;
- fp = fopen(git_path("gc.pid"), "r");
+ fp = fopen(pidfile_path, "r");
memset(locking_host, 0, sizeof(locking_host));
should_exit =
fp != NULL &&
@@ -222,7 +256,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
* running.
*/
time(NULL) - st.st_mtime <= 12 * 3600 &&
- fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
+ fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 &&
/* be gentle to concurrent "gc" on remote hosts */
(strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
if (fp != NULL)
@@ -231,6 +265,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
if (fd >= 0)
rollback_lock_file(&lock);
*ret_pid = pid;
+ free(pidfile_path);
return locking_host;
}
}
@@ -240,14 +275,29 @@ 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);
-
- pidfile = git_pathdup("gc.pid");
- sigchain_push_common(remove_pidfile_on_signal);
- atexit(remove_pidfile);
-
+ register_tempfile(&pidfile, pidfile_path);
+ free(pidfile_path);
return NULL;
}
+static int report_last_gc_error(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int ret;
+
+ ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
+ if (ret > 0)
+ return error(_("The last gc run reported the following. "
+ "Please correct the root cause\n"
+ "and remove %s.\n"
+ "Automatic cleanup will not be performed "
+ "until the file is removed.\n\n"
+ "%s"),
+ git_path("gc.log"), sb.buf);
+ strbuf_release(&sb);
+ return 0;
+}
+
static int gc_before_repack(void)
{
if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
@@ -269,6 +319,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int force = 0;
const char *name;
pid_t pid;
+ int daemonized = 0;
struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -287,7 +338,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
- argv_array_pushl(&prune, "prune", "--expire", NULL );
+ argv_array_pushl(&prune, "prune", "--expire", NULL);
+ argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
gc_config();
@@ -324,13 +376,16 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
}
if (detach_auto) {
+ if (report_last_gc_error())
+ return -1;
+
if (gc_before_repack())
return -1;
/*
* failure to daemonize is ok, we'll continue
* in foreground
*/
- daemonize();
+ daemonized = !daemonize();
}
} else
add_repack_all_option();
@@ -343,23 +398,45 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
name, (uintmax_t)pid);
}
+ if (daemonized) {
+ hold_lock_file_for_update(&log_lock,
+ git_path("gc.log"),
+ LOCK_DIE_ON_ERROR);
+ dup2(get_lock_file_fd(&log_lock), 2);
+ sigchain_push_common(process_log_file_on_signal);
+ atexit(process_log_file_at_exit);
+ }
+
if (gc_before_repack())
return -1;
- if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, repack.argv[0]);
+ if (!repository_format_precious_objects) {
+ if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, repack.argv[0]);
- if (prune_expire) {
- argv_array_push(&prune, prune_expire);
- if (quiet)
- argv_array_push(&prune, "--no-progress");
- if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, prune.argv[0]);
+ if (prune_expire) {
+ argv_array_push(&prune, prune_expire);
+ if (quiet)
+ argv_array_push(&prune, "--no-progress");
+ if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, prune.argv[0]);
+ }
+ }
+
+ if (prune_worktrees_expire) {
+ argv_array_push(&prune_worktrees, prune_worktrees_expire);
+ if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, prune_worktrees.argv[0]);
}
if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
return error(FAILED_RUN, rerere.argv[0]);
+ report_garbage = report_pack_garbage;
+ reprepare_packed_git();
+ if (pack_garbage.nr > 0)
+ clean_pack_garbage();
+
if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 6f4147a..e21c541 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -8,7 +8,7 @@
#include "quote.h"
static const char builtin_get_tar_commit_id_usage[] =
-"git get-tar-commit-id < <tarfile>";
+"git get-tar-commit-id";
/* ustar header + extended global header content */
#define RECORDSIZE (512)
diff --git a/builtin/grep.c b/builtin/grep.c
index d04f440..ae73831 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -24,11 +24,11 @@ static char const * const grep_usage[] = {
NULL
};
-static int use_threads = 1;
+#define GREP_NUM_THREADS_DEFAULT 8
+static int num_threads;
#ifndef NO_PTHREADS
-#define THREADS 8
-static pthread_t threads[THREADS];
+static pthread_t *threads;
/* We use one producer thread and THREADS consumer
* threads. The producer adds struct work_items to 'todo' and the
@@ -63,13 +63,13 @@ static pthread_mutex_t grep_mutex;
static inline void grep_lock(void)
{
- if (use_threads)
+ if (num_threads)
pthread_mutex_lock(&grep_mutex);
}
static inline void grep_unlock(void)
{
- if (use_threads)
+ if (num_threads)
pthread_mutex_unlock(&grep_mutex);
}
@@ -206,7 +206,8 @@ static void start_threads(struct grep_opt *opt)
strbuf_init(&todo[i].out, 0);
}
- for (i = 0; i < ARRAY_SIZE(threads); i++) {
+ threads = xcalloc(num_threads, sizeof(*threads));
+ for (i = 0; i < num_threads; i++) {
int err;
struct grep_opt *o = grep_opt_dup(opt);
o->output = strbuf_out;
@@ -238,12 +239,14 @@ static int wait_all(void)
pthread_cond_broadcast(&cond_add);
grep_unlock();
- for (i = 0; i < ARRAY_SIZE(threads); i++) {
+ for (i = 0; i < num_threads; i++) {
void *h;
pthread_join(threads[i], &h);
hit |= (int) (intptr_t) h;
}
+ free(threads);
+
pthread_mutex_destroy(&grep_mutex);
pthread_mutex_destroy(&grep_read_mutex);
pthread_mutex_destroy(&grep_attr_mutex);
@@ -267,6 +270,14 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)
int st = grep_config(var, value, cb);
if (git_color_default_config(var, value, cb) < 0)
st = -1;
+
+ if (!strcmp(var, "grep.threads")) {
+ num_threads = git_config_int(var, value);
+ if (num_threads < 0)
+ die(_("invalid number of threads specified (%d) for %s"),
+ num_threads, var);
+ }
+
return st;
}
@@ -294,7 +305,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
}
#ifndef NO_PTHREADS
- if (use_threads) {
+ if (num_threads) {
add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
strbuf_release(&pathbuf);
return 0;
@@ -323,7 +334,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
strbuf_addstr(&buf, filename);
#ifndef NO_PTHREADS
- if (use_threads) {
+ if (num_threads) {
add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
strbuf_release(&buf);
return 0;
@@ -354,17 +365,17 @@ static void append_path(struct grep_opt *opt, const void *data, size_t len)
static void run_pager(struct grep_opt *opt, const char *prefix)
{
struct string_list *path_list = opt->output_priv;
- const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
+ struct child_process child = CHILD_PROCESS_INIT;
int i, status;
for (i = 0; i < path_list->nr; i++)
- argv[i] = path_list->items[i].string;
- argv[path_list->nr] = NULL;
+ argv_array_push(&child.args, path_list->items[i].string);
+ child.dir = prefix;
+ child.use_shell = 1;
- status = run_command_v_opt_cd_env(argv, RUN_USING_SHELL, prefix, NULL);
+ status = run_command(&child);
if (status)
exit(status);
- free(argv);
}
static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
@@ -385,7 +396,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
* cache version instead
*/
if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
- if (ce_stage(ce))
+ if (ce_stage(ce) || ce_intent_to_add(ce))
continue;
hit |= grep_sha1(opt, ce->sha1, ce->name, 0, ce->name);
}
@@ -427,7 +438,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
strbuf_add(base, entry.path, te_len);
if (S_ISREG(entry.mode)) {
- hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len,
+ hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
check_attr ? base->buf + tn_len : NULL);
}
else if (S_ISDIR(entry.mode)) {
@@ -436,10 +447,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
void *data;
unsigned long size;
- data = lock_and_read_sha1_file(entry.sha1, &type, &size);
+ data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
if (!data)
die(_("unable to read tree (%s)"),
- sha1_to_hex(entry.sha1));
+ oid_to_hex(entry.oid));
strbuf_addch(base, '/');
init_tree_desc(&sub, data, size);
@@ -459,7 +470,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
struct object *obj, const char *name, const char *path)
{
if (obj->type == OBJ_BLOB)
- return grep_sha1(opt, obj->sha1, name, 0, path);
+ return grep_sha1(opt, obj->oid.hash, name, 0, path);
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
struct tree_desc tree;
void *data;
@@ -468,12 +479,12 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
int hit, len;
grep_read_lock();
- data = read_object_with_reference(obj->sha1, tree_type,
+ data = read_object_with_reference(obj->oid.hash, tree_type,
&size, NULL);
grep_read_unlock();
if (!data)
- die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
+ die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
len = name ? strlen(name) : 0;
strbuf_init(&base, PATH_MAX + len + 1);
@@ -511,12 +522,14 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
}
static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
- int exc_std)
+ int exc_std, int use_index)
{
struct dir_struct dir;
int i, hit = 0;
memset(&dir, 0, sizeof(dir));
+ if (!use_index)
+ dir.flags |= DIR_NO_GITLINKS;
if (exc_std)
setup_standard_excludes(&dir);
@@ -562,7 +575,7 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
patterns = from_stdin ? stdin : fopen(arg, "r");
if (!patterns)
die_errno(_("cannot open '%s'"), arg);
- while (strbuf_getline(&sb, patterns, '\n') == 0) {
+ while (strbuf_getline(&sb, patterns) == 0) {
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
@@ -612,11 +625,6 @@ static int pattern_callback(const struct option *opt, const char *arg,
return 0;
}
-static int help_callback(const struct option *opt, const char *arg, int unset)
-{
- return -1;
-}
-
int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
@@ -702,6 +710,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
N_("show <n> context lines before matches")),
OPT_INTEGER('A', "after-context", &opt.post_context,
N_("show <n> context lines after matches")),
+ OPT_INTEGER(0, "threads", &num_threads,
+ N_("use <n> worker threads")),
OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
context_callback),
OPT_BOOL('p', "show-function", &opt.funcname,
@@ -738,18 +748,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
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)")),
- { OPTION_CALLBACK, 0, "help-all", NULL, NULL, N_("show usage"),
- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
OPT_END()
};
- /*
- * 'git grep -h', unlike 'git grep -h <pattern>', is a request
- * to show usage information and exit.
- */
- if (argc == 2 && !strcmp(argv[1], "-h"))
- usage_with_options(grep_usage, options);
-
init_grep_defaults();
git_config(grep_cmd_config, NULL);
grep_init(&opt, prefix);
@@ -766,13 +767,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
*/
argc = parse_options(argc, argv, prefix, options, grep_usage,
PARSE_OPT_KEEP_DASHDASH |
- PARSE_OPT_STOP_AT_NON_OPTION |
- PARSE_OPT_NO_INTERNAL_HELP);
+ PARSE_OPT_STOP_AT_NON_OPTION);
grep_commit_pattern_type(pattern_type_arg, &opt);
- if (use_index && !startup_info->have_repository)
- /* die the same way as if we did it at the beginning */
- setup_git_directory();
+ if (use_index && !startup_info->have_repository) {
+ int fallback = 0;
+ git_config_get_bool("grep.fallbacktonoindex", &fallback);
+ if (fallback)
+ use_index = 0;
+ else
+ /* die the same way as if we did it at the beginning */
+ setup_git_directory();
+ }
/*
* skip a -- separator; we know it cannot be
@@ -801,7 +807,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
opt.output_priv = &path_list;
opt.output = append_path;
string_list_append(&path_list, show_in_pager);
- use_threads = 0;
}
if (!opt.pattern_list)
@@ -832,14 +837,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
#ifndef NO_PTHREADS
- if (list.nr || cached || online_cpus() == 1)
- use_threads = 0;
+ if (list.nr || cached || show_in_pager)
+ num_threads = 0;
+ else if (num_threads == 0)
+ num_threads = GREP_NUM_THREADS_DEFAULT;
+ else if (num_threads < 0)
+ die(_("invalid number of threads specified (%d)"), num_threads);
#else
- use_threads = 0;
+ num_threads = 0;
#endif
#ifndef NO_PTHREADS
- if (use_threads) {
+ if (num_threads) {
if (!(opt.name_only || opt.unmatch_name_only || opt.count)
&& (opt.pre_context || opt.post_context ||
opt.file_break || opt.funcbody))
@@ -895,7 +904,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
if (list.nr)
die(_("--no-index or --untracked cannot be used with revs."));
- hit = grep_directory(&opt, &pathspec, use_exclude);
+ hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
} else if (0 <= opt_exclude) {
die(_("--[no-]exclude-standard cannot be used for tracked contents."));
} else if (!list.nr) {
@@ -909,7 +918,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
hit = grep_objects(&opt, &pathspec, &list);
}
- if (use_threads)
+ if (num_threads)
hit |= wait_all();
if (hit && show_in_pager)
run_pager(&opt, prefix);
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 07fef3c..f7d3567 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -58,27 +58,28 @@ static void hash_object(const char *path, const char *type, const char *vpath,
static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
int literally)
{
- struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf unquoted = STRBUF_INIT;
- while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+ while (strbuf_getline(&buf, stdin) != EOF) {
if (buf.buf[0] == '"') {
- strbuf_reset(&nbuf);
- if (unquote_c_style(&nbuf, buf.buf, NULL))
+ strbuf_reset(&unquoted);
+ if (unquote_c_style(&unquoted, buf.buf, NULL))
die("line is badly quoted");
- strbuf_swap(&buf, &nbuf);
+ strbuf_swap(&buf, &unquoted);
}
hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,
literally);
}
strbuf_release(&buf);
- strbuf_release(&nbuf);
+ strbuf_release(&unquoted);
}
int cmd_hash_object(int argc, const char **argv, const char *prefix)
{
static const char * const hash_object_usage[] = {
N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
- N_("git hash-object --stdin-paths < <list-of-paths>"),
+ N_("git hash-object --stdin-paths"),
NULL
};
const char *type = blob_type;
diff --git a/builtin/help.c b/builtin/help.c
index 3422e73..e8f79d7 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -127,7 +127,7 @@ static void exec_woman_emacs(const char *path, const char *page)
path = "emacsclient";
strbuf_addf(&man_page, "(woman \"%s\")", page);
execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
}
@@ -140,22 +140,15 @@ static void exec_man_konqueror(const char *path, const char *page)
/* It's simpler to launch konqueror using kfmclient. */
if (path) {
- const char *file = strrchr(path, '/');
- if (file && !strcmp(file + 1, "konqueror")) {
- char *new = xstrdup(path);
- char *dest = strrchr(new, '/');
-
- /* strlen("konqueror") == strlen("kfmclient") */
- strcpy(dest + 1, "kfmclient");
- path = new;
- }
- if (file)
- filename = file;
+ size_t len;
+ if (strip_suffix(path, "/konqueror", &len))
+ path = xstrfmt("%.*s/kfmclient", (int)len, path);
+ filename = basename((char *)path);
} else
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
}
@@ -164,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
if (!path)
path = "man";
execlp(path, "man", page, (char *)NULL);
- warning(_("failed to exec '%s': %s"), path, strerror(errno));
+ warning_errno(_("failed to exec '%s'"), path);
}
static void exec_man_cmd(const char *cmd, const char *page)
@@ -172,18 +165,16 @@ static void exec_man_cmd(const char *cmd, const char *page)
struct strbuf shell_cmd = STRBUF_INIT;
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
- warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
+ warning(_("failed to exec '%s'"), cmd);
}
static void add_man_viewer(const char *name)
{
struct man_viewer_list **p = &man_viewer_list;
- size_t len = strlen(name);
while (*p)
p = &((*p)->next);
- *p = xcalloc(1, (sizeof(**p) + len + 1));
- strncpy((*p)->name, name, len);
+ FLEX_ALLOC_STR(*p, name, name);
}
static int supported_man_viewer(const char *name, size_t len)
@@ -197,9 +188,8 @@ static void do_add_man_viewer_info(const char *name,
size_t len,
const char *value)
{
- struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
-
- strncpy(new->name, name, len);
+ 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;
@@ -295,16 +285,6 @@ static int is_git_command(const char *s)
is_in_cmdlist(&other_cmds, s);
}
-static const char *prepend(const char *prefix, const char *cmd)
-{
- size_t pre_len = strlen(prefix);
- size_t cmd_len = strlen(cmd);
- char *p = xmalloc(pre_len + cmd_len + 1);
- memcpy(p, prefix, pre_len);
- strcpy(p + pre_len, cmd);
- return p;
-}
-
static const char *cmd_to_page(const char *git_cmd)
{
if (!git_cmd)
@@ -312,9 +292,9 @@ static const char *cmd_to_page(const char *git_cmd)
else if (starts_with(git_cmd, "git"))
return git_cmd;
else if (is_git_command(git_cmd))
- return prepend("git-", git_cmd);
+ return xstrfmt("git-%s", git_cmd);
else
- return prepend("git", git_cmd);
+ return xstrfmt("git%s", git_cmd);
}
static void setup_man_path(void)
@@ -399,17 +379,10 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
free(to_free);
}
-/*
- * If open_html is not defined in a platform-specific way (see for
- * example compat/mingw.h), we use the script web--browse to display
- * HTML.
- */
-#ifndef open_html
static void open_html(const char *path)
{
execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL);
}
-#endif
static void show_html_page(const char *git_cmd)
{
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 723fe8e..1d2ea58 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -18,16 +18,14 @@ static const char index_pack_usage[] =
struct object_entry {
struct pack_idx_entry idx;
unsigned long size;
- unsigned int hdr_size;
- enum object_type type;
- enum object_type real_type;
- unsigned delta_depth;
- int base_object_no;
+ unsigned char hdr_size;
+ signed char type;
+ signed char real_type;
};
-union delta_base {
- unsigned char sha1[20];
- off_t offset;
+struct object_stat {
+ unsigned delta_depth;
+ int base_object_no;
};
struct base_data {
@@ -49,32 +47,37 @@ struct thread_local {
int pack_fd;
};
-/*
- * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
- * to memcmp() only the first 20 bytes.
- */
-#define UNION_BASE_SZ 20
-
#define FLAG_LINK (1u<<20)
#define FLAG_CHECKED (1u<<21)
-struct delta_entry {
- union delta_base base;
+struct ofs_delta_entry {
+ off_t offset;
+ int obj_no;
+};
+
+struct ref_delta_entry {
+ unsigned char sha1[20];
int obj_no;
};
static struct object_entry *objects;
-static struct delta_entry *deltas;
+static struct object_stat *obj_stat;
+static struct ofs_delta_entry *ofs_deltas;
+static struct ref_delta_entry *ref_deltas;
static struct thread_local nothread_data;
static int nr_objects;
-static int nr_deltas;
+static int nr_ofs_deltas;
+static int nr_ref_deltas;
+static int ref_deltas_alloc;
static int nr_resolved_deltas;
static int nr_threads;
static int from_stdin;
static int strict;
static int do_fsck_object;
+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
static int verbose;
+static int show_resolving_progress;
static int show_stat;
static int check_self_contained_and_connected;
@@ -191,13 +194,13 @@ static void cleanup_thread(void)
#endif
-static int mark_link(struct object *obj, int type, void *data)
+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)
{
if (!obj)
return -1;
if (type != OBJ_ANY && obj->type != type)
- die(_("object type mismatch at %s"), sha1_to_hex(obj->sha1));
+ die(_("object type mismatch at %s"), oid_to_hex(&obj->oid));
obj->flags |= FLAG_LINK;
return 0;
@@ -215,13 +218,13 @@ static unsigned check_object(struct object *obj)
if (!(obj->flags & FLAG_CHECKED)) {
unsigned long size;
- int type = sha1_object_info(obj->sha1, &size);
+ int type = sha1_object_info(obj->oid.hash, &size);
if (type <= 0)
die(_("did not receive expected object %s"),
- sha1_to_hex(obj->sha1));
+ oid_to_hex(&obj->oid));
if (type != obj->type)
die(_("object %s: expected type %s, found %s"),
- sha1_to_hex(obj->sha1),
+ oid_to_hex(&obj->oid),
typename(obj->type), typename(type));
obj->flags |= FLAG_CHECKED;
return 1;
@@ -336,10 +339,10 @@ static void parse_pack_header(void)
use(sizeof(struct pack_header));
}
-static NORETURN void bad_object(unsigned long offset, const char *format,
+static NORETURN void bad_object(off_t offset, const char *format,
...) __attribute__((format (printf, 2, 3)));
-static NORETURN void bad_object(unsigned long offset, const char *format, ...)
+static NORETURN void bad_object(off_t offset, const char *format, ...)
{
va_list params;
char buf[1024];
@@ -347,7 +350,8 @@ static NORETURN void bad_object(unsigned long offset, const char *format, ...)
va_start(params, format);
vsnprintf(buf, sizeof(buf), format, params);
va_end(params);
- die(_("pack has bad object at offset %lu: %s"), offset, buf);
+ die(_("pack has bad object at offset %"PRIuMAX": %s"),
+ (uintmax_t)offset, buf);
}
static inline struct thread_local *get_thread_data(void)
@@ -427,7 +431,7 @@ static int is_delta_type(enum object_type type)
return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
}
-static void *unpack_entry_data(unsigned long offset, unsigned long size,
+static void *unpack_entry_data(off_t offset, unsigned long size,
enum object_type type, unsigned char *sha1)
{
static char fixed_buf[8192];
@@ -439,7 +443,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size,
int hdrlen;
if (!is_delta_type(type)) {
- hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1;
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1;
git_SHA1_Init(&c);
git_SHA1_Update(&c, hdr, hdrlen);
} else
@@ -476,7 +480,8 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size,
}
static void *unpack_raw_entry(struct object_entry *obj,
- union delta_base *delta_base,
+ off_t *ofs_offset,
+ unsigned char *ref_sha1,
unsigned char *sha1)
{
unsigned char *p;
@@ -505,11 +510,10 @@ static void *unpack_raw_entry(struct object_entry *obj,
switch (obj->type) {
case OBJ_REF_DELTA:
- hashcpy(delta_base->sha1, fill(20));
+ hashcpy(ref_sha1, fill(20));
use(20);
break;
case OBJ_OFS_DELTA:
- memset(delta_base, 0, sizeof(*delta_base));
p = fill(1);
c = *p;
use(1);
@@ -523,8 +527,8 @@ static void *unpack_raw_entry(struct object_entry *obj,
use(1);
base_offset = (base_offset << 7) + (c & 127);
}
- delta_base->offset = obj->idx.offset - base_offset;
- if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
+ *ofs_offset = obj->idx.offset - base_offset;
+ if (*ofs_offset <= 0 || *ofs_offset >= obj->idx.offset)
bad_object(obj->idx.offset, _("delta base offset is out of bound"));
break;
case OBJ_COMMIT:
@@ -547,13 +551,13 @@ static void *unpack_data(struct object_entry *obj,
void *cb_data)
{
off_t from = obj[0].idx.offset + obj[0].hdr_size;
- unsigned long len = obj[1].idx.offset - from;
+ off_t len = obj[1].idx.offset - from;
unsigned char *data, *inbuf;
git_zstream stream;
int status;
data = xmallocz(consume ? 64*1024 : obj->size);
- inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+ inbuf = xmalloc((len < 64*1024) ? (int)len : 64*1024);
memset(&stream, 0, sizeof(stream));
git_inflate_init(&stream);
@@ -561,15 +565,15 @@ static void *unpack_data(struct object_entry *obj,
stream.avail_out = consume ? 64*1024 : obj->size;
do {
- ssize_t n = (len < 64*1024) ? len : 64*1024;
+ ssize_t n = (len < 64*1024) ? (ssize_t)len : 64*1024;
n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
if (n < 0)
die_errno(_("cannot pread pack file"));
if (!n)
- die(Q_("premature end of pack file, %lu byte missing",
- "premature end of pack file, %lu bytes missing",
- len),
- len);
+ die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
+ "premature end of pack file, %"PRIuMAX" bytes missing",
+ (unsigned int)len),
+ (uintmax_t)len);
from += n;
len -= n;
stream.next_in = inbuf;
@@ -608,55 +612,110 @@ static void *get_data_from_pack(struct object_entry *obj)
return unpack_data(obj, NULL, NULL);
}
-static int compare_delta_bases(const union delta_base *base1,
- const union delta_base *base2,
- enum object_type type1,
- enum object_type type2)
+static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
+ enum object_type type1,
+ enum object_type type2)
{
int cmp = type1 - type2;
if (cmp)
return cmp;
- return memcmp(base1, base2, UNION_BASE_SZ);
+ return offset1 < offset2 ? -1 :
+ offset1 > offset2 ? 1 :
+ 0;
}
-static int find_delta(const union delta_base *base, enum object_type type)
+static int find_ofs_delta(const off_t offset, enum object_type type)
{
- int first = 0, last = nr_deltas;
-
- while (first < last) {
- int next = (first + last) / 2;
- struct delta_entry *delta = &deltas[next];
- int cmp;
-
- cmp = compare_delta_bases(base, &delta->base,
- type, objects[delta->obj_no].type);
- if (!cmp)
- return next;
- if (cmp < 0) {
- last = next;
- continue;
- }
- first = next+1;
- }
- return -first-1;
+ int first = 0, last = nr_ofs_deltas;
+
+ while (first < last) {
+ int next = (first + last) / 2;
+ struct ofs_delta_entry *delta = &ofs_deltas[next];
+ int cmp;
+
+ cmp = compare_ofs_delta_bases(offset, delta->offset,
+ type, objects[delta->obj_no].type);
+ if (!cmp)
+ return next;
+ if (cmp < 0) {
+ last = next;
+ continue;
+ }
+ first = next+1;
+ }
+ return -first-1;
}
-static void find_delta_children(const union delta_base *base,
- int *first_index, int *last_index,
- enum object_type type)
+static void find_ofs_delta_children(off_t offset,
+ int *first_index, int *last_index,
+ enum object_type type)
{
- int first = find_delta(base, type);
+ int first = find_ofs_delta(offset, type);
int last = first;
- int end = nr_deltas - 1;
+ int end = nr_ofs_deltas - 1;
if (first < 0) {
*first_index = 0;
*last_index = -1;
return;
}
- while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
+ while (first > 0 && ofs_deltas[first - 1].offset == offset)
--first;
- while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
+ while (last < end && ofs_deltas[last + 1].offset == offset)
+ ++last;
+ *first_index = first;
+ *last_index = last;
+}
+
+static int compare_ref_delta_bases(const unsigned char *sha1,
+ const unsigned char *sha2,
+ enum object_type type1,
+ enum object_type type2)
+{
+ int cmp = type1 - type2;
+ if (cmp)
+ return cmp;
+ return hashcmp(sha1, sha2);
+}
+
+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;
+ struct ref_delta_entry *delta = &ref_deltas[next];
+ int cmp;
+
+ cmp = compare_ref_delta_bases(sha1, delta->sha1,
+ type, objects[delta->obj_no].type);
+ if (!cmp)
+ return next;
+ if (cmp < 0) {
+ last = next;
+ continue;
+ }
+ first = next+1;
+ }
+ return -first-1;
+}
+
+static void find_ref_delta_children(const unsigned char *sha1,
+ int *first_index, int *last_index,
+ enum object_type type)
+{
+ int first = find_ref_delta(sha1, type);
+ int last = first;
+ int end = nr_ref_deltas - 1;
+
+ if (first < 0) {
+ *first_index = 0;
+ *last_index = -1;
+ return;
+ }
+ while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1))
+ --first;
+ while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1))
++last;
*first_index = first;
*last_index = last;
@@ -782,11 +841,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
if (!obj)
die(_("invalid %s"), typename(type));
if (do_fsck_object &&
- fsck_object(obj, buf, size, 1,
- fsck_error_function))
+ fsck_object(obj, buf, size, &fsck_options))
die(_("Error in object"));
- if (fsck_walk(obj, mark_link, NULL))
- die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
+ if (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) {
struct tree *item = (struct tree *) obj;
@@ -873,13 +931,15 @@ static void resolve_delta(struct object_entry *delta_obj,
void *base_data, *delta_data;
if (show_stat) {
- delta_obj->delta_depth = base->obj->delta_depth + 1;
+ int i = delta_obj - objects;
+ int j = base->obj - objects;
+ obj_stat[i].delta_depth = obj_stat[j].delta_depth + 1;
deepest_delta_lock();
- if (deepest_delta < delta_obj->delta_depth)
- deepest_delta = delta_obj->delta_depth;
+ if (deepest_delta < obj_stat[i].delta_depth)
+ deepest_delta = obj_stat[i].delta_depth;
deepest_delta_unlock();
+ obj_stat[i].base_object_no = j;
}
- delta_obj->base_object_no = base->obj - objects;
delta_data = get_data_from_pack(delta_obj);
base_data = get_base_data(base);
result->obj = delta_obj;
@@ -902,7 +962,7 @@ static void resolve_delta(struct object_entry *delta_obj,
* "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
* and return false.
*/
-static int compare_and_swap_type(enum object_type *type,
+static int compare_and_swap_type(signed char *type,
enum object_type want,
enum object_type set)
{
@@ -921,16 +981,13 @@ 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) {
- union delta_base base_spec;
+ find_ref_delta_children(base->obj->idx.sha1,
+ &base->ref_first, &base->ref_last,
+ OBJ_REF_DELTA);
- hashcpy(base_spec.sha1, base->obj->idx.sha1);
- find_delta_children(&base_spec,
- &base->ref_first, &base->ref_last, OBJ_REF_DELTA);
-
- memset(&base_spec, 0, sizeof(base_spec));
- base_spec.offset = base->obj->idx.offset;
- find_delta_children(&base_spec,
- &base->ofs_first, &base->ofs_last, OBJ_OFS_DELTA);
+ find_ofs_delta_children(base->obj->idx.offset,
+ &base->ofs_first, &base->ofs_last,
+ OBJ_OFS_DELTA);
if (base->ref_last == -1 && base->ofs_last == -1) {
free(base->data);
@@ -941,7 +998,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base,
}
if (base->ref_first <= base->ref_last) {
- struct object_entry *child = objects + deltas[base->ref_first].obj_no;
+ struct object_entry *child = objects + ref_deltas[base->ref_first].obj_no;
struct base_data *result = alloc_base_data();
if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
@@ -957,7 +1014,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base,
}
if (base->ofs_first <= base->ofs_last) {
- struct object_entry *child = objects + deltas[base->ofs_first].obj_no;
+ struct object_entry *child = objects + ofs_deltas[base->ofs_first].obj_no;
struct base_data *result = alloc_base_data();
assert(child->real_type == OBJ_OFS_DELTA);
@@ -993,15 +1050,22 @@ static void find_unresolved_deltas(struct base_data *base)
}
}
-static int compare_delta_entry(const void *a, const void *b)
+static int compare_ofs_delta_entry(const void *a, const void *b)
{
- const struct delta_entry *delta_a = a;
- const struct delta_entry *delta_b = b;
+ const struct ofs_delta_entry *delta_a = a;
+ const struct ofs_delta_entry *delta_b = b;
- /* group by type (ref vs ofs) and then by value (sha-1 or offset) */
- return compare_delta_bases(&delta_a->base, &delta_b->base,
- objects[delta_a->obj_no].type,
- objects[delta_b->obj_no].type);
+ return delta_a->offset < delta_b->offset ? -1 :
+ delta_a->offset > delta_b->offset ? 1 :
+ 0;
+}
+
+static int compare_ref_delta_entry(const void *a, const void *b)
+{
+ const struct ref_delta_entry *delta_a = a;
+ const struct ref_delta_entry *delta_b = b;
+
+ return hashcmp(delta_a->sha1, delta_b->sha1);
}
static void resolve_base(struct object_entry *obj)
@@ -1047,7 +1111,8 @@ static void *threaded_second_pass(void *data)
static void parse_pack_objects(unsigned char *sha1)
{
int i, nr_delays = 0;
- struct delta_entry *delta = deltas;
+ struct ofs_delta_entry *ofs_delta = ofs_deltas;
+ unsigned char ref_delta_sha1[20];
struct stat st;
if (verbose)
@@ -1056,12 +1121,18 @@ static void parse_pack_objects(unsigned char *sha1)
nr_objects);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
- void *data = unpack_raw_entry(obj, &delta->base, obj->idx.sha1);
+ void *data = unpack_raw_entry(obj, &ofs_delta->offset,
+ ref_delta_sha1, obj->idx.sha1);
obj->real_type = obj->type;
- if (is_delta_type(obj->type)) {
- nr_deltas++;
- delta->obj_no = i;
- delta++;
+ if (obj->type == OBJ_OFS_DELTA) {
+ nr_ofs_deltas++;
+ ofs_delta->obj_no = i;
+ 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);
+ ref_deltas[nr_ref_deltas].obj_no = i;
+ nr_ref_deltas++;
} else if (!data) {
/* large blobs, check later */
obj->real_type = OBJ_BAD;
@@ -1112,15 +1183,18 @@ static void resolve_deltas(void)
{
int i;
- if (!nr_deltas)
+ if (!nr_ofs_deltas && !nr_ref_deltas)
return;
/* Sort deltas by base SHA1/offset for fast searching */
- qsort(deltas, nr_deltas, sizeof(struct delta_entry),
- compare_delta_entry);
+ qsort(ofs_deltas, nr_ofs_deltas, sizeof(struct ofs_delta_entry),
+ compare_ofs_delta_entry);
+ qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
+ compare_ref_delta_entry);
- if (verbose)
- progress = start_progress(_("Resolving deltas"), nr_deltas);
+ if (verbose || show_resolving_progress)
+ progress = start_progress(_("Resolving deltas"),
+ nr_ref_deltas + nr_ofs_deltas);
#ifndef NO_PTHREADS
nr_dispatched = 0;
@@ -1155,10 +1229,10 @@ static void resolve_deltas(void)
* - append objects to convert thin pack to full pack if required
* - write the final 20-byte SHA-1
*/
-static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved);
+static void fix_unresolved_deltas(struct sha1file *f);
static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1)
{
- if (nr_deltas == nr_resolved_deltas) {
+ if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) {
stop_progress(&progress);
/* Flush remaining pack final 20-byte SHA1. */
flush();
@@ -1169,7 +1243,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
struct sha1file *f;
unsigned char read_sha1[20], tail_sha1[20];
struct strbuf msg = STRBUF_INIT;
- int nr_unresolved = nr_deltas - nr_resolved_deltas;
+ int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas;
int nr_objects_initial = nr_objects;
if (nr_unresolved <= 0)
die(_("confusion beyond insanity"));
@@ -1177,8 +1251,10 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
memset(objects + nr_objects + 1, 0,
nr_unresolved * sizeof(*objects));
f = sha1fd(output_fd, curr_pack);
- fix_unresolved_deltas(f, nr_unresolved);
- strbuf_addf(&msg, _("completed with %d local objects"),
+ fix_unresolved_deltas(f);
+ strbuf_addf(&msg, Q_("completed with %d local object",
+ "completed with %d local objects",
+ nr_objects - nr_objects_initial),
nr_objects - nr_objects_initial);
stop_progress_msg(&progress, msg.buf);
strbuf_release(&msg);
@@ -1191,11 +1267,11 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
die(_("Unexpected tail checksum for %s "
"(disk corruption?)"), curr_pack);
}
- if (nr_deltas != nr_resolved_deltas)
+ if (nr_ofs_deltas + nr_ref_deltas != nr_resolved_deltas)
die(Q_("pack has %d unresolved delta",
"pack has %d unresolved deltas",
- nr_deltas - nr_resolved_deltas),
- nr_deltas - nr_resolved_deltas);
+ nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas),
+ nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas);
}
static int write_compressed(struct sha1file *f, void *in, unsigned int size)
@@ -1254,15 +1330,15 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f,
static int delta_pos_compare(const void *_a, const void *_b)
{
- struct delta_entry *a = *(struct delta_entry **)_a;
- struct delta_entry *b = *(struct delta_entry **)_b;
+ struct ref_delta_entry *a = *(struct ref_delta_entry **)_a;
+ struct ref_delta_entry *b = *(struct ref_delta_entry **)_b;
return a->obj_no - b->obj_no;
}
-static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
+static void fix_unresolved_deltas(struct sha1file *f)
{
- struct delta_entry **sorted_by_pos;
- int i, n = 0;
+ struct ref_delta_entry **sorted_by_pos;
+ int i;
/*
* Since many unresolved deltas may well be themselves base objects
@@ -1274,29 +1350,26 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
* before deltas depending on them, a good heuristic is to start
* resolving deltas in the same order as their position in the pack.
*/
- sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos));
- for (i = 0; i < nr_deltas; i++) {
- if (objects[deltas[i].obj_no].real_type != OBJ_REF_DELTA)
- continue;
- sorted_by_pos[n++] = &deltas[i];
- }
- qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare);
+ ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas);
+ for (i = 0; i < nr_ref_deltas; i++)
+ sorted_by_pos[i] = &ref_deltas[i];
+ qsort(sorted_by_pos, nr_ref_deltas, sizeof(*sorted_by_pos), delta_pos_compare);
- for (i = 0; i < n; i++) {
- struct delta_entry *d = sorted_by_pos[i];
+ for (i = 0; i < nr_ref_deltas; i++) {
+ struct ref_delta_entry *d = sorted_by_pos[i];
enum object_type type;
struct base_data *base_obj = alloc_base_data();
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
continue;
- base_obj->data = read_sha1_file(d->base.sha1, &type, &base_obj->size);
+ base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size);
if (!base_obj->data)
continue;
- if (check_sha1_signature(d->base.sha1, base_obj->data,
+ if (check_sha1_signature(d->sha1, base_obj->data,
base_obj->size, typename(type)))
- die(_("local object %s is corrupt"), sha1_to_hex(d->base.sha1));
- base_obj->obj = append_obj_to_pack(f, d->base.sha1,
+ 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);
find_unresolved_deltas(base_obj);
display_progress(progress, nr_resolved_deltas);
@@ -1352,7 +1425,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
get_object_directory(), sha1_to_hex(sha1));
final_pack_name = name;
}
- if (move_temp_to_file(curr_pack_name, final_pack_name))
+ if (finalize_object_file(curr_pack_name, final_pack_name))
die(_("cannot store pack file"));
} else if (from_stdin)
chmod(final_pack_name, 0444);
@@ -1363,7 +1436,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
get_object_directory(), sha1_to_hex(sha1));
final_index_name = name;
}
- if (move_temp_to_file(curr_index_name, final_index_name))
+ if (finalize_object_file(curr_index_name, final_index_name))
die(_("cannot store index file"));
} else
chmod(final_index_name, 0444);
@@ -1445,6 +1518,7 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
if (!(off & 0x80000000))
continue;
off = off & 0x7fffffff;
+ check_pack_index_ptr(p, &idx2[off * 2]);
if (idx2[off * 2])
continue;
/*
@@ -1488,7 +1562,7 @@ static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
static void show_pack_info(int stat_only)
{
- int i, baseobjects = nr_objects - nr_deltas;
+ int i, baseobjects = nr_objects - nr_ref_deltas - nr_ofs_deltas;
unsigned long *chain_histogram = NULL;
if (deepest_delta)
@@ -1498,7 +1572,7 @@ static void show_pack_info(int stat_only)
struct object_entry *obj = &objects[i];
if (is_delta_type(obj->type))
- chain_histogram[obj->delta_depth - 1]++;
+ chain_histogram[obj_stat[i].delta_depth - 1]++;
if (stat_only)
continue;
printf("%s %-6s %lu %lu %"PRIuMAX,
@@ -1507,8 +1581,8 @@ static void show_pack_info(int stat_only)
(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->base_object_no];
- printf(" %u %s", obj->delta_depth, sha1_to_hex(bobj->idx.sha1));
+ 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));
}
putchar('\n');
}
@@ -1529,6 +1603,18 @@ 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;
@@ -1541,11 +1627,13 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
struct pack_idx_option opts;
unsigned char pack_sha1[20];
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
+ int report_end_of_input = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
check_replace_refs = 0;
+ fsck_options.walk = mark_link;
reset_pack_idx_option(&opts);
git_config(git_index_pack_config, &opts);
@@ -1563,6 +1651,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
} else if (!strcmp(arg, "--strict")) {
strict = 1;
do_fsck_object = 1;
+ } else if (skip_prefix(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;
@@ -1605,6 +1697,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
input_len = sizeof(*hdr);
} else if (!strcmp(arg, "-v")) {
verbose = 1;
+ } else if (!strcmp(arg, "--show-resolving-progress")) {
+ show_resolving_progress = 1;
+ } else if (!strcmp(arg, "--report-end-of-input")) {
+ report_end_of_input = 1;
} else if (!strcmp(arg, "-o")) {
if (index_name || (i+1) >= argc)
usage(index_pack_usage);
@@ -1632,24 +1728,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
usage(index_pack_usage);
if (fix_thin_pack && !from_stdin)
die(_("--fix-thin cannot be used without --stdin"));
- if (!index_name && pack_name) {
- size_t len;
- if (!strip_suffix(pack_name, ".pack", &len))
- die(_("packfile name '%s' does not end with '.pack'"),
- pack_name);
- strbuf_add(&index_name_buf, pack_name, len);
- strbuf_addstr(&index_name_buf, ".idx");
- index_name = index_name_buf.buf;
- }
- if (keep_msg && !keep_name && pack_name) {
- size_t len;
- if (!strip_suffix(pack_name, ".pack", &len))
- die(_("packfile name '%s' does not end with '.pack'"),
- pack_name);
- strbuf_add(&keep_name_buf, pack_name, len);
- strbuf_addstr(&keep_name_buf, ".idx");
- keep_name = keep_name_buf.buf;
- }
+ 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);
+
if (verify) {
if (!index_name)
die(_("--verify with no packfile name given"));
@@ -1670,19 +1753,24 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
curr_pack = open_pack_file(pack_name);
parse_pack_header();
- objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
- deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
+ objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry));
+ 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);
+ if (report_end_of_input)
+ write_in_full(2, "\0", 1);
resolve_deltas();
conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
- free(deltas);
+ free(ofs_deltas);
+ free(ref_deltas);
if (strict)
foreign_nr = check_objects();
if (show_stat)
show_pack_info(stat_only);
- idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
+ 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);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index ab9f86b..3a45f0b 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "refs.h"
#include "builtin.h"
#include "exec_cmd.h"
#include "parse-options.h"
@@ -23,22 +24,11 @@ static int init_shared_repository = -1;
static const char *init_db_template_dir;
static const char *git_link;
-static void safe_create_dir(const char *dir, int share)
-{
- if (mkdir(dir, 0777) < 0) {
- if (errno != EEXIST) {
- perror(dir);
- exit(1);
- }
- }
- else if (share && adjust_shared_perm(dir))
- die(_("Could not make %s writable by group"), dir);
-}
-
-static void copy_templates_1(char *path, int baselen,
- char *template, int template_baselen,
+static void copy_templates_1(struct strbuf *path, struct strbuf *template,
DIR *dir)
{
+ size_t path_baselen = path->len;
+ size_t template_baselen = template->len;
struct dirent *de;
/* Note: if ".git/hooks" file exists in the repository being
@@ -48,77 +38,66 @@ static void copy_templates_1(char *path, int baselen,
* with the way the namespace under .git/ is organized, should
* be really carefully chosen.
*/
- safe_create_dir(path, 1);
+ safe_create_dir(path->buf, 1);
while ((de = readdir(dir)) != NULL) {
struct stat st_git, st_template;
- int namelen;
int exists = 0;
+ strbuf_setlen(path, path_baselen);
+ strbuf_setlen(template, template_baselen);
+
if (de->d_name[0] == '.')
continue;
- namelen = strlen(de->d_name);
- if ((PATH_MAX <= baselen + namelen) ||
- (PATH_MAX <= template_baselen + namelen))
- die(_("insanely long template name %s"), de->d_name);
- memcpy(path + baselen, de->d_name, namelen+1);
- memcpy(template + template_baselen, de->d_name, namelen+1);
- if (lstat(path, &st_git)) {
+ strbuf_addstr(path, de->d_name);
+ strbuf_addstr(template, de->d_name);
+ if (lstat(path->buf, &st_git)) {
if (errno != ENOENT)
- die_errno(_("cannot stat '%s'"), path);
+ die_errno(_("cannot stat '%s'"), path->buf);
}
else
exists = 1;
- if (lstat(template, &st_template))
- die_errno(_("cannot stat template '%s'"), template);
+ if (lstat(template->buf, &st_template))
+ die_errno(_("cannot stat template '%s'"), template->buf);
if (S_ISDIR(st_template.st_mode)) {
- DIR *subdir = opendir(template);
- int baselen_sub = baselen + namelen;
- int template_baselen_sub = template_baselen + namelen;
+ DIR *subdir = opendir(template->buf);
if (!subdir)
- die_errno(_("cannot opendir '%s'"), template);
- path[baselen_sub++] =
- template[template_baselen_sub++] = '/';
- path[baselen_sub] =
- template[template_baselen_sub] = 0;
- copy_templates_1(path, baselen_sub,
- template, template_baselen_sub,
- subdir);
+ die_errno(_("cannot opendir '%s'"), template->buf);
+ strbuf_addch(path, '/');
+ strbuf_addch(template, '/');
+ copy_templates_1(path, template, subdir);
closedir(subdir);
}
else if (exists)
continue;
else if (S_ISLNK(st_template.st_mode)) {
- char lnk[256];
- int len;
- len = readlink(template, lnk, sizeof(lnk));
- if (len < 0)
- die_errno(_("cannot readlink '%s'"), template);
- if (sizeof(lnk) <= len)
- die(_("insanely long symlink %s"), template);
- lnk[len] = 0;
- if (symlink(lnk, path))
- die_errno(_("cannot symlink '%s' '%s'"), lnk, path);
+ struct strbuf lnk = STRBUF_INIT;
+ if (strbuf_readlink(&lnk, template->buf, 0) < 0)
+ die_errno(_("cannot readlink '%s'"), template->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, template, st_template.st_mode))
- die_errno(_("cannot copy '%s' to '%s'"), template,
- path);
+ if (copy_file(path->buf, template->buf, st_template.st_mode))
+ die_errno(_("cannot copy '%s' to '%s'"),
+ template->buf, path->buf);
}
else
- error(_("ignoring template %s"), template);
+ error(_("ignoring template %s"), template->buf);
}
}
static void copy_templates(const char *template_dir)
{
- char path[PATH_MAX];
- char template_path[PATH_MAX];
- int template_len;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf template_path = STRBUF_INIT;
+ size_t template_len;
+ struct repository_format template_format;
+ struct strbuf err = STRBUF_INIT;
DIR *dir;
- const char *git_dir = get_git_dir();
- int len = strlen(git_dir);
char *to_free = NULL;
if (!template_dir)
@@ -131,47 +110,43 @@ static void copy_templates(const char *template_dir)
free(to_free);
return;
}
- template_len = strlen(template_dir);
- if (PATH_MAX <= (template_len+strlen("/config")))
- die(_("insanely long template path %s"), template_dir);
- strcpy(template_path, template_dir);
- if (template_path[template_len-1] != '/') {
- template_path[template_len++] = '/';
- template_path[template_len] = 0;
- }
- dir = opendir(template_path);
+
+ strbuf_addstr(&template_path, template_dir);
+ strbuf_complete(&template_path, '/');
+ template_len = template_path.len;
+
+ dir = opendir(template_path.buf);
if (!dir) {
warning(_("templates not found %s"), template_dir);
goto free_return;
}
/* Make sure that template is from the correct vintage */
- strcpy(template_path + template_len, "config");
- repository_format_version = 0;
- git_config_from_file(check_repository_format_version,
- template_path, NULL);
- template_path[template_len] = 0;
-
- if (repository_format_version &&
- repository_format_version != GIT_REPO_VERSION) {
- warning(_("not copying templates of "
- "a wrong format version %d from '%s'"),
- repository_format_version,
- template_dir);
+ strbuf_addstr(&template_path, "config");
+ read_repository_format(&template_format, template_path.buf);
+ strbuf_setlen(&template_path, template_len);
+
+ /*
+ * No mention of version at all is OK, but anything else should be
+ * verified.
+ */
+ if (template_format.version >= 0 &&
+ verify_repository_format(&template_format, &err) < 0) {
+ warning(_("not copying templates from '%s': %s"),
+ template_dir, err.buf);
+ strbuf_release(&err);
goto close_free_return;
}
- memcpy(path, git_dir, len);
- if (len && path[len - 1] != '/')
- path[len++] = '/';
- path[len] = 0;
- copy_templates_1(path, len,
- template_path, template_len,
- dir);
+ strbuf_addstr(&path, get_git_dir());
+ strbuf_complete(&path, '/');
+ copy_templates_1(&path, &template_path, dir);
close_free_return:
closedir(dir);
free_return:
free(to_free);
+ strbuf_release(&path);
+ strbuf_release(&template_path);
}
static int git_init_db_config(const char *k, const char *v, void *cb)
@@ -198,28 +173,20 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
static int create_default_files(const char *template_path)
{
- const char *git_dir = get_git_dir();
- unsigned len = strlen(git_dir);
- static char path[PATH_MAX];
struct stat st1;
+ struct strbuf buf = STRBUF_INIT;
+ char *path;
char repo_version_string[10];
char junk[2];
int reinit;
int filemode;
- if (len > sizeof(path)-50)
- die(_("insane git directory %s"), git_dir);
- memcpy(path, git_dir, len);
-
- if (len && path[len-1] != '/')
- path[len++] = '/';
-
/*
* Create .git/refs/{heads,tags}
*/
- safe_create_dir(git_path("refs"), 1);
- safe_create_dir(git_path("refs/heads"), 1);
- safe_create_dir(git_path("refs/tags"), 1);
+ safe_create_dir(git_path_buf(&buf, "refs"), 1);
+ safe_create_dir(git_path_buf(&buf, "refs/heads"), 1);
+ safe_create_dir(git_path_buf(&buf, "refs/tags"), 1);
/* Just look for `init.templatedir` */
git_config(git_init_db_config, NULL);
@@ -235,24 +202,24 @@ static int create_default_files(const char *template_path)
/* reading existing config may have overwrote it */
if (init_shared_repository != -1)
- shared_repository = init_shared_repository;
+ set_shared_repository(init_shared_repository);
/*
* We would have created the above under user's umask -- under
* shared-repository settings, we would need to fix them up.
*/
- if (shared_repository) {
+ if (get_shared_repository()) {
adjust_shared_perm(get_git_dir());
- adjust_shared_perm(git_path("refs"));
- adjust_shared_perm(git_path("refs/heads"));
- adjust_shared_perm(git_path("refs/tags"));
+ adjust_shared_perm(git_path_buf(&buf, "refs"));
+ adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
+ adjust_shared_perm(git_path_buf(&buf, "refs/tags"));
}
/*
* Create the default symlink from ".git/HEAD" to the "master"
* branch, if it does not exist yet.
*/
- strcpy(path + len, "HEAD");
+ path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK)
|| readlink(path, junk, sizeof(junk)-1) != -1);
if (!reinit) {
@@ -261,13 +228,12 @@ static int create_default_files(const char *template_path)
}
/* This forces creation of new config file */
- sprintf(repo_version_string, "%d", GIT_REPO_VERSION);
+ xsnprintf(repo_version_string, sizeof(repo_version_string),
+ "%d", GIT_REPO_VERSION);
git_config_set("core.repositoryformatversion", repo_version_string);
- path[len] = 0;
- strcpy(path + len, "config");
-
/* Check filemode trustability */
+ path = git_path_buf(&buf, "config");
filemode = TEST_FILEMODE;
if (TEST_FILEMODE && !lstat(path, &st1)) {
struct stat st2;
@@ -287,15 +253,14 @@ static int create_default_files(const char *template_path)
git_config_set("core.bare", "false");
/* allow template config file to override the default */
if (log_all_ref_updates == -1)
- git_config_set("core.logallrefupdates", "true");
- if (needs_work_tree_config(git_dir, work_tree))
+ git_config_set("core.logallrefupdates", "true");
+ if (needs_work_tree_config(get_git_dir(), work_tree))
git_config_set("core.worktree", work_tree);
}
if (!reinit) {
/* Check if symlink is supported in the work tree */
- path[len] = 0;
- strcpy(path + len, "tXXXXXX");
+ path = git_path_buf(&buf, "tXXXXXX");
if (!close(xmkstemp(path)) &&
!unlink(path) &&
!symlink("testing", path) &&
@@ -306,31 +271,35 @@ static int create_default_files(const char *template_path)
git_config_set("core.symlinks", "false");
/* Check if the filesystem is case-insensitive */
- path[len] = 0;
- strcpy(path + len, "CoNfIg");
+ path = git_path_buf(&buf, "CoNfIg");
if (!access(path, F_OK))
git_config_set("core.ignorecase", "true");
- probe_utf8_pathname_composition(path, len);
+ probe_utf8_pathname_composition();
}
+ strbuf_release(&buf);
return reinit;
}
static void create_object_directory(void)
{
- const char *object_directory = get_object_directory();
- int len = strlen(object_directory);
- char *path = xmalloc(len + 40);
+ struct strbuf path = STRBUF_INIT;
+ size_t baselen;
+
+ strbuf_addstr(&path, get_object_directory());
+ baselen = path.len;
- memcpy(path, object_directory, len);
+ safe_create_dir(path.buf, 1);
- safe_create_dir(object_directory, 1);
- strcpy(path+len, "/pack");
- safe_create_dir(path, 1);
- strcpy(path+len, "/info");
- safe_create_dir(path, 1);
+ strbuf_setlen(&path, baselen);
+ strbuf_addstr(&path, "/pack");
+ safe_create_dir(path.buf, 1);
- free(path);
+ strbuf_setlen(&path, baselen);
+ strbuf_addstr(&path, "/info");
+ safe_create_dir(path.buf, 1);
+
+ strbuf_release(&path);
}
int set_git_dir_init(const char *git_dir, const char *real_git_dir,
@@ -356,13 +325,13 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir,
set_git_dir(real_path(git_dir));
git_link = NULL;
}
+ startup_info->have_repository = 1;
return 0;
}
static void separate_git_dir(const char *git_dir)
{
struct stat st;
- FILE *fp;
if (!stat(git_link, &st)) {
const char *src;
@@ -378,11 +347,7 @@ static void separate_git_dir(const char *git_dir)
die_errno(_("unable to move %s to %s"), src, git_dir);
}
- fp = fopen(git_link, "w");
- if (!fp)
- die(_("Could not create git link %s"), git_link);
- fprintf(fp, "gitdir: %s\n", git_dir);
- fclose(fp);
+ write_file(git_link, "gitdir: %s", git_dir);
}
int init_db(const char *template_dir, unsigned int flags)
@@ -408,7 +373,7 @@ int init_db(const char *template_dir, unsigned int flags)
create_object_directory();
- if (shared_repository) {
+ if (get_shared_repository()) {
char buf[10];
/* We do not spell "group" and such, so that
* the configuration can be read by older version
@@ -416,15 +381,15 @@ int init_db(const char *template_dir, unsigned int flags)
* and compatibility values for PERM_GROUP and
* PERM_EVERYBODY.
*/
- if (shared_repository < 0)
+ if (get_shared_repository() < 0)
/* force to the mode value */
- sprintf(buf, "0%o", -shared_repository);
- else if (shared_repository == PERM_GROUP)
- sprintf(buf, "%d", OLD_PERM_GROUP);
- else if (shared_repository == PERM_EVERYBODY)
- sprintf(buf, "%d", OLD_PERM_EVERYBODY);
+ xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
+ else if (get_shared_repository() == PERM_GROUP)
+ xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
+ else if (get_shared_repository() == PERM_EVERYBODY)
+ xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
else
- die("oops");
+ die("BUG: invalid value for shared_repository");
git_config_set("core.sharedrepository", buf);
git_config_set("receive.denyNonFastforwards", "true");
}
@@ -432,13 +397,16 @@ int init_db(const char *template_dir, unsigned int flags)
if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);
- /* TRANSLATORS: The first '%s' is either "Reinitialized
- existing" or "Initialized empty", the second " shared" or
- "", and the last '%s%s' is the verbatim directory name. */
- printf(_("%s%s Git repository in %s%s\n"),
- reinit ? _("Reinitialized existing") : _("Initialized empty"),
- shared_repository ? _(" shared") : "",
- git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+ if (reinit)
+ printf(get_shared_repository()
+ ? _("Reinitialized existing shared Git repository in %s%s\n")
+ : _("Reinitialized existing Git repository in %s%s\n"),
+ git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+ else
+ printf(get_shared_repository()
+ ? _("Initialized empty shared Git repository in %s%s\n")
+ : _("Initialized empty Git repository in %s%s\n"),
+ git_dir, len && git_dir[len-1] != '/' ? "/" : "");
}
return 0;
@@ -532,8 +500,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
* and we know shared_repository should always be 0;
* but just in case we play safe.
*/
- saved = shared_repository;
- shared_repository = 0;
+ saved = get_shared_repository();
+ set_shared_repository(0);
switch (safe_create_leading_directories_const(argv[0])) {
case SCLD_OK:
case SCLD_PERMS:
@@ -545,7 +513,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
die_errno(_("cannot mkdir %s"), argv[0]);
break;
}
- shared_repository = saved;
+ set_shared_repository(saved);
if (mkdir(argv[0], 0777) < 0)
die_errno(_("cannot mkdir %s"), argv[0]);
mkdir_tried = 1;
@@ -563,7 +531,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
}
if (init_shared_repository != -1)
- shared_repository = init_shared_repository;
+ set_shared_repository(init_shared_repository);
/*
* GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 46838d2..175f147 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -12,16 +12,18 @@
#include "trailer.h"
static const char * const git_interpret_trailers_usage[] = {
- N_("git interpret-trailers [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
+ N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
NULL
};
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_DUP;
+ struct string_list trailers = STRING_LIST_INIT_NODUP;
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")),
@@ -34,9 +36,12 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
if (argc) {
int i;
for (i = 0; i < argc; i++)
- process_trailers(argv[i], trim_empty, &trailers);
- } else
- process_trailers(NULL, trim_empty, &trailers);
+ process_trailers(argv[i], in_place, trim_empty, &trailers);
+ } else {
+ if (in_place)
+ die(_("no input file given for in-place editing"));
+ process_trailers(NULL, in_place, trim_empty, &trailers);
+ }
string_list_clear(&trailers, 0);
diff --git a/builtin/log.c b/builtin/log.c
index 7b343c1..cd9c4a4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -5,6 +5,7 @@
* 2006 Junio Hamano
*/
#include "cache.h"
+#include "refs.h"
#include "color.h"
#include "commit.h"
#include "diff.h"
@@ -31,6 +32,8 @@ static const char *default_date_mode = NULL;
static int default_abbrev_commit;
static int default_show_root = 1;
+static int default_follow;
+static int default_show_signature;
static int decoration_style;
static int decoration_given;
static int use_mailmap_config;
@@ -98,10 +101,18 @@ static int log_line_range_callback(const struct option *option, const char *arg,
return 0;
}
+static void init_log_defaults(void)
+{
+ init_grep_defaults();
+ init_diff_ui_defaults();
+}
+
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->verbose_header = 1;
DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
rev->diffopt.stat_width = -1; /* use full terminal width */
@@ -109,10 +120,11 @@ static void cmd_log_init_defaults(struct rev_info *rev)
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);
if (default_date_mode)
- rev->date_mode = parse_date_format(default_date_mode);
+ parse_date_format(default_date_mode, &rev->date_mode);
rev->diffopt.touched_flags = 0;
}
@@ -226,16 +238,17 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)
if (rev->commit_format != CMIT_FMT_ONELINE)
putchar(rev->diffopt.line_termination);
}
- printf(_("Final output: %d %s\n"), nr, stage);
+ fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage);
}
static struct itimerval early_output_timer;
static void log_show_early(struct rev_info *revs, struct commit_list *list)
{
- int i = revs->early_output;
+ int i = revs->early_output, close_file = revs->diffopt.close_file;
int show_header = 1;
+ revs->diffopt.close_file = 0;
sort_in_topological_order(&list, revs->sort_order);
while (list && i) {
struct commit *commit = list->item;
@@ -252,14 +265,19 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
case commit_ignore:
break;
case commit_error:
+ if (close_file)
+ fclose(revs->diffopt.file);
return;
}
list = list->next;
}
/* Did we already get enough commits for the early output? */
- if (!i)
+ if (!i) {
+ if (close_file)
+ fclose(revs->diffopt.file);
return;
+ }
/*
* ..if no, then repeat it twice a second until we
@@ -321,7 +339,7 @@ static int cmd_log_walk(struct rev_info *rev)
{
struct commit *commit;
int saved_nrl = 0;
- int saved_dcctc = 0;
+ int saved_dcctc = 0, close_file = rev->diffopt.close_file;
if (rev->early_output)
setup_early_output(rev);
@@ -337,9 +355,9 @@ static int cmd_log_walk(struct rev_info *rev)
* and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
* retain that state information if replacing rev->diffopt in this loop
*/
+ rev->diffopt.close_file = 0;
while ((commit = get_revision(rev)) != NULL) {
- if (!log_tree_commit(rev, commit) &&
- rev->max_count >= 0)
+ if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
/*
* We decremented max_count in get_revision,
* but we didn't actually show the commit.
@@ -358,6 +376,8 @@ static int cmd_log_walk(struct rev_info *rev)
}
rev->diffopt.degraded_cc_to_c = saved_dcctc;
rev->diffopt.needed_rename_limit = saved_nrl;
+ if (close_file)
+ fclose(rev->diffopt.file);
if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
@@ -390,12 +410,20 @@ static int git_log_config(const char *var, const char *value, void *cb)
default_show_root = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "log.follow")) {
+ default_follow = git_config_bool(var, value);
+ return 0;
+ }
if (skip_prefix(var, "color.decorate.", &slot_name))
return parse_decorate_color_config(var, slot_name, value);
if (!strcmp(var, "log.mailmap")) {
use_mailmap_config = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "log.showsignature")) {
+ default_show_signature = git_config_bool(var, value);
+ return 0;
+ }
if (grep_config(var, value, cb) < 0)
return -1;
@@ -409,7 +437,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -432,7 +460,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
pp.fmt = rev->commit_format;
pp.date_mode = rev->date_mode;
pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
- printf("%s", out.buf);
+ fprintf(rev->diffopt.file, "%s", out.buf);
strbuf_release(&out);
}
@@ -443,7 +471,7 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con
char *buf;
unsigned long size;
- fflush(stdout);
+ fflush(rev->diffopt.file);
if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
!DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
return stream_blob_to_fd(1, sha1, NULL, 0);
@@ -483,7 +511,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
}
if (offset < size)
- fwrite(buf + offset, size - offset, 1, stdout);
+ fwrite(buf + offset, size - offset, 1, rev->diffopt.file);
free(buf);
return 0;
}
@@ -492,11 +520,13 @@ static int show_tree_object(const unsigned char *sha1,
struct strbuf *base,
const char *pathname, unsigned mode, int stage, void *context)
{
- printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+ FILE *file = context;
+ fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
return 0;
}
-static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+static void show_setup_revisions_tweak(struct rev_info *rev,
+ struct setup_revision_opt *opt)
{
if (rev->ignore_merges) {
/* There was no "-m" on the command line */
@@ -519,7 +549,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
struct pathspec match_all;
int i, count, ret = 0;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
memset(&match_all, 0, sizeof(match_all));
@@ -531,7 +561,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
- opt.tweak = show_rev_tweak_rev;
+ opt.tweak = show_setup_revisions_tweak;
cmd_log_init(argc, argv, prefix, &rev, &opt);
if (!rev.no_walk)
@@ -544,25 +574,25 @@ int cmd_show(int argc, const char **argv, const char *prefix)
const char *name = objects[i].name;
switch (o->type) {
case OBJ_BLOB:
- ret = show_blob_object(o->sha1, &rev, name);
+ ret = show_blob_object(o->oid.hash, &rev, name);
break;
case OBJ_TAG: {
struct tag *t = (struct tag *)o;
if (rev.shown_one)
putchar('\n');
- printf("%stag %s%s\n",
+ fprintf(rev.diffopt.file, "%stag %s%s\n",
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
t->tag,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
- ret = show_tag_object(o->sha1, &rev);
+ ret = show_tag_object(o->oid.hash, &rev);
rev.shown_one = 1;
if (ret)
break;
- o = parse_object(t->tagged->sha1);
+ o = parse_object(t->tagged->oid.hash);
if (!o)
ret = error(_("Could not read object %s"),
- sha1_to_hex(t->tagged->sha1));
+ oid_to_hex(&t->tagged->oid));
objects[i].item = o;
i--;
break;
@@ -570,12 +600,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
case OBJ_TREE:
if (rev.shown_one)
putchar('\n');
- printf("%stree %s%s\n\n",
+ fprintf(rev.diffopt.file, "%stree %s%s\n\n",
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
name,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
- show_tree_object, NULL);
+ show_tree_object, rev.diffopt.file);
rev.shown_one = 1;
break;
case OBJ_COMMIT:
@@ -600,7 +630,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -618,12 +648,28 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
return cmd_log_walk(&rev);
}
+static void log_setup_revisions_tweak(struct rev_info *rev,
+ struct setup_revision_opt *opt)
+{
+ if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
+ rev->prune_data.nr == 1)
+ DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);
+
+ /* Turn --cc/-c into -p --cc/-c when -p was not given */
+ if (!rev->diffopt.output_format && rev->combine_merges)
+ rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ /* Turn -m on when --cc/-c was given */
+ if (rev->combine_merges)
+ rev->ignore_merges = 0;
+}
+
int cmd_log(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
struct setup_revision_opt opt;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_log_config, NULL);
init_revisions(&rev, prefix);
@@ -631,6 +677,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
opt.revarg_opt = REVARG_COMMITTISH;
+ opt.tweak = log_setup_revisions_tweak;
cmd_log_init(argc, argv, prefix, &rev, &opt);
return cmd_log_walk(&rev);
}
@@ -643,9 +690,9 @@ static int auto_number = 1;
static char *default_attach = NULL;
-static struct string_list extra_hdr;
-static struct string_list extra_to;
-static struct string_list extra_cc;
+static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
+static struct string_list extra_to = STRING_LIST_INIT_NODUP;
+static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
static void add_header(const char *value)
{
@@ -671,9 +718,12 @@ static void add_header(const char *value)
#define THREAD_DEEP 2
static int thread;
static int do_signoff;
+static int base_auto;
+static char *from;
static const char *signature = git_version_string;
static const char *signature_file;
static int config_cover_letter;
+static const char *config_output_directory;
enum {
COVER_UNSET,
@@ -752,15 +802,31 @@ static int git_format_config(const char *var, const char *value, void *cb)
config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
return 0;
}
+ if (!strcmp(var, "format.outputdirectory"))
+ return git_config_string(&config_output_directory, var, value);
+ if (!strcmp(var, "format.useautobase")) {
+ base_auto = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "format.from")) {
+ int b = git_config_maybe_bool(var, value);
+ free(from);
+ if (b < 0)
+ from = xstrdup(value);
+ else if (b)
+ from = xstrdup(git_committer_info(IDENT_NO_DATE));
+ else
+ from = NULL;
+ return 0;
+ }
return git_log_config(var, value, cb);
}
-static FILE *realstdout = NULL;
static const char *output_directory = NULL;
static int outdir_offset;
-static int reopen_stdout(struct commit *commit, const char *subject,
+static int open_next_file(struct commit *commit, const char *subject,
struct rev_info *rev, int quiet)
{
struct strbuf filename = STRBUF_INIT;
@@ -771,8 +837,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,
if (filename.len >=
PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
return error(_("name of output directory is too long"));
- if (filename.buf[filename.len - 1] != '/')
- strbuf_addch(&filename, '/');
+ strbuf_complete(&filename, '/');
}
if (rev->numbered_files)
@@ -783,9 +848,9 @@ static int reopen_stdout(struct commit *commit, const char *subject,
fmt_output_subject(&filename, subject, rev);
if (!quiet)
- fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
+ printf("%s\n", filename.buf + outdir_offset);
- if (freopen(filename.buf, "w", stdout) == NULL)
+ if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
return error(_("Cannot open patch file %s"), filename.buf);
strbuf_release(&filename);
@@ -806,8 +871,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->sha1);
- c2 = lookup_commit_reference(o2->sha1);
+ c1 = lookup_commit_reference(o1->oid.hash);
+ c2 = lookup_commit_reference(o2->oid.hash);
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die(_("Not a range."));
@@ -844,15 +909,15 @@ static void gen_message_id(struct rev_info *info, char *base)
info->message_id = strbuf_detach(&buf, NULL);
}
-static void print_signature(void)
+static void print_signature(FILE *file)
{
if (!signature || !*signature)
return;
- printf("-- \n%s", signature);
+ fprintf(file, "-- \n%s", signature);
if (signature[strlen(signature)-1] != '\n')
- putchar('\n');
- putchar('\n');
+ putc('\n', file);
+ putc('\n', file);
}
static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@ -872,8 +937,8 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
static char *find_branch_name(struct rev_info *rev)
{
int i, positive = -1;
- unsigned char branch_sha1[20];
- const unsigned char *tip_sha1;
+ struct object_id branch_oid;
+ const struct object_id *tip_oid;
const char *ref, *v;
char *full_ref, *branch = NULL;
@@ -888,10 +953,10 @@ static char *find_branch_name(struct rev_info *rev)
if (positive < 0)
return NULL;
ref = rev->cmdline.rev[positive].name;
- tip_sha1 = rev->cmdline.rev[positive].item->sha1;
- if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
+ tip_oid = &rev->cmdline.rev[positive].item->oid;
+ if (dwim_ref(ref, strlen(ref), branch_oid.hash, &full_ref) &&
skip_prefix(full_ref, "refs/heads/", &v) &&
- !hashcmp(tip_sha1, branch_sha1))
+ !oidcmp(tip_oid, &branch_oid))
branch = xstrdup(v);
free(full_ref);
return branch;
@@ -915,13 +980,13 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct pretty_print_context pp = {0};
struct commit *head = list[0];
- if (rev->commit_format != CMIT_FMT_EMAIL)
+ if (!cmit_fmt_is_mail(rev->commit_format))
die(_("Cover letter needs email format"));
committer = git_committer_info(0);
if (!use_stdout &&
- reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
+ open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
return;
log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
@@ -939,12 +1004,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
msg = body;
pp.fmt = CMIT_FMT_EMAIL;
- pp.date_mode = DATE_RFC2822;
+ pp.date_mode.type = DATE_RFC2822;
pp_user_info(&pp, NULL, &sb, committer, encoding);
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
add_branch_description(&sb, branch_name);
- printf("%s\n", sb.buf);
+ fprintf(rev->diffopt.file, "%s\n", sb.buf);
strbuf_release(&sb);
@@ -953,6 +1018,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
log.wrap = 72;
log.in1 = 2;
log.in2 = 4;
+ log.file = rev->diffopt.file;
for (i = 0; i < nr; i++)
shortlog_add_commit(&log, list[i]);
@@ -969,14 +1035,13 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
diff_setup_done(&opts);
- diff_tree_sha1(origin->tree->object.sha1,
- head->tree->object.sha1,
+ diff_tree_sha1(origin->tree->object.oid.hash,
+ head->tree->object.oid.hash,
"", &opts);
diffcore_std(&opts);
diff_flush(&opts);
- printf("\n");
- print_signature();
+ fprintf(rev->diffopt.file, "\n");
}
static const char *clean_message_id(const char *msg_id)
@@ -1158,6 +1223,155 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+struct base_tree_info {
+ struct object_id base_commit;
+ int nr_patch_id, alloc_patch_id;
+ struct object_id *patch_id;
+};
+
+static struct commit *get_base_commit(const char *base_commit,
+ struct commit **list,
+ int total)
+{
+ struct commit *base = NULL;
+ struct commit **rev;
+ int i = 0, rev_nr = 0;
+
+ if (base_commit && strcmp(base_commit, "auto")) {
+ base = lookup_commit_reference_by_name(base_commit);
+ if (!base)
+ die(_("Unknown commit %s"), base_commit);
+ } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+ struct branch *curr_branch = branch_get(NULL);
+ const char *upstream = branch_get_upstream(curr_branch, NULL);
+ if (upstream) {
+ struct commit_list *base_list;
+ struct commit *commit;
+ unsigned char sha1[20];
+
+ if (get_sha1(upstream, sha1))
+ die(_("Failed to resolve '%s' as a valid ref."), upstream);
+ commit = lookup_commit_or_die(sha1, "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)
+ die(_("Could not find exact merge base."));
+ base = base_list->item;
+ free_commit_list(base_list);
+ } else {
+ die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+ "please use git branch --set-upstream-to to track a remote branch.\n"
+ "Or you could specify base commit by --base=<base-commit-id> manually."));
+ }
+ }
+
+ ALLOC_ARRAY(rev, total);
+ for (i = 0; i < total; i++)
+ rev[i] = list[i];
+
+ rev_nr = total;
+ /*
+ * Get merge base through pair-wise computations
+ * and store it in rev[0].
+ */
+ while (rev_nr > 1) {
+ for (i = 0; i < rev_nr / 2; i++) {
+ struct commit_list *merge_base;
+ merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+ if (!merge_base || merge_base->next)
+ die(_("Failed to find exact merge base"));
+
+ rev[i] = merge_base->item;
+ }
+
+ if (rev_nr % 2)
+ rev[i] = rev[2 * i];
+ rev_nr = (rev_nr + 1) / 2;
+ }
+
+ if (!in_merge_bases(base, rev[0]))
+ die(_("base commit should be the ancestor of revision list"));
+
+ for (i = 0; i < total; i++) {
+ if (base == list[i])
+ die(_("base commit shouldn't be in revision list"));
+ }
+
+ free(rev);
+ return base;
+}
+
+static void prepare_bases(struct base_tree_info *bases,
+ struct commit *base,
+ struct commit **list,
+ int total)
+{
+ struct commit *commit;
+ struct rev_info revs;
+ struct diff_options diffopt;
+ int i;
+
+ if (!base)
+ return;
+
+ diff_setup(&diffopt);
+ DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diff_setup_done(&diffopt);
+
+ oidcpy(&bases->base_commit, &base->object.oid);
+
+ init_revisions(&revs, NULL);
+ revs.max_parents = 1;
+ revs.topo_order = 1;
+ for (i = 0; i < total; i++) {
+ list[i]->object.flags &= ~UNINTERESTING;
+ add_pending_object(&revs, &list[i]->object, "rev_list");
+ list[i]->util = (void *)1;
+ }
+ base->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &base->object, "base");
+
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+ /*
+ * Traverse the commits list, get prerequisite patch ids
+ * and stuff them in bases structure.
+ */
+ while ((commit = get_revision(&revs)) != NULL) {
+ unsigned char sha1[20];
+ struct object_id *patch_id;
+ if (commit->util)
+ continue;
+ if (commit_patch_id(commit, &diffopt, sha1, 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;
+ hashcpy(patch_id->hash, sha1);
+ bases->nr_patch_id++;
+ }
+}
+
+static void print_bases(struct base_tree_info *bases, FILE *file)
+{
+ int i;
+
+ /* Only do this once, either for the cover or for the first one */
+ if (is_null_oid(&bases->base_commit))
+ return;
+
+ /* Show the base commit */
+ fprintf(file, "\nbase-commit: %s\n", oid_to_hex(&bases->base_commit));
+
+ /* Show the prerequisite patches */
+ for (i = bases->nr_patch_id - 1; i >= 0; i--)
+ fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+
+ free(bases->patch_id);
+ bases->nr_patch_id = 0;
+ bases->alloc_patch_id = 0;
+ oidclr(&bases->base_commit);
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1172,6 +1386,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int cover_letter = -1;
int boundary_count = 0;
int no_binary_diff = 0;
+ int zero_commit = 0;
struct commit *origin = NULL;
const char *in_reply_to = NULL;
struct patch_ids ids;
@@ -1180,7 +1395,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int quiet = 0;
int reroll_count = -1;
char *branch_name = NULL;
- char *from = NULL;
+ char *base_commit = NULL;
+ struct base_tree_info bases;
+
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
N_("use [PATCH n/m] even with a single patch"),
@@ -1212,6 +1429,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
OPT_BOOL(0, "no-binary", &no_binary_diff,
N_("don't output binary diffs")),
+ OPT_BOOL(0, "zero-commit", &zero_commit,
+ N_("output all-zero hash in From header")),
OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
N_("don't include a patch matching a commit upstream")),
{ OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL,
@@ -1241,6 +1460,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, thread_callback },
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
+ OPT_STRING(0, "base", &base_commit, N_("base-commit"),
+ N_("add prerequisite tree info to the patch series")),
OPT_FILENAME(0, "signature-file", &signature_file,
N_("add a signature from a file")),
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
@@ -1250,10 +1471,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_hdr.strdup_strings = 1;
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
- init_grep_defaults();
+ init_log_defaults();
git_config(git_format_config, NULL);
init_revisions(&rev, prefix);
rev.commit_format = CMIT_FMT_EMAIL;
+ rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
rev.diff = 1;
rev.max_parents = 1;
@@ -1356,18 +1578,25 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
/* 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.show_notes)
init_display_notes(&rev.notes_opt);
+ if (!output_directory && !use_stdout)
+ output_directory = config_output_directory;
+
if (!use_stdout)
output_directory = set_outdir(prefix, output_directory);
else
setup_pager();
if (output_directory) {
+ if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
+ rev.diffopt.use_color = GIT_COLOR_NEVER;
if (use_stdout)
die(_("standard output, or directory, which one?"));
if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
@@ -1419,15 +1648,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
/* Don't say anything if head and upstream are the same. */
if (rev.pending.nr == 2) {
struct object_array_entry *o = rev.pending.objects;
- if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+ if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
return 0;
}
get_patch_ids(&rev, &ids);
}
- if (!use_stdout)
- realstdout = xfdopen(xdup(1), "w");
-
if (prepare_revision_walk(&rev))
die(_("revision walk setup failed"));
rev.boundary = 1;
@@ -1438,8 +1664,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
continue;
}
- if (ignore_if_in_upstream &&
- has_commit_patch_id(commit, &ids))
+ if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids))
continue;
nr++;
@@ -1473,6 +1698,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
signature = strbuf_detach(&buf, NULL);
}
+ memset(&bases, 0, sizeof(bases));
+ if (base_commit || base_auto) {
+ struct commit *base = get_base_commit(base_commit, list, nr);
+ reset_revision_walk();
+ prepare_bases(&bases, base, list, nr);
+ }
+
if (in_reply_to || thread || cover_letter)
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
if (in_reply_to) {
@@ -1486,6 +1718,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout,
origin, nr, list, branch_name, quiet);
+ print_bases(&bases, rev.diffopt.file);
+ print_signature(rev.diffopt.file);
total++;
start_number--;
}
@@ -1527,11 +1761,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
string_list_append(rev.ref_message_ids,
rev.message_id);
}
- gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
+ gen_message_id(&rev, oid_to_hex(&commit->object.oid));
}
if (!use_stdout &&
- reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
+ open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
die(_("Failed to create output files"));
shown = log_tree_commit(&rev, commit);
free_commit_buffer(commit);
@@ -1545,15 +1779,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!use_stdout)
rev.shown_one = 0;
if (shown) {
+ print_bases(&bases, rev.diffopt.file);
if (rev.mime_boundary)
- printf("\n--%s%s--\n\n\n",
+ fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n",
mime_boundary_leader,
rev.mime_boundary);
else
- print_signature();
+ print_signature(rev.diffopt.file);
}
if (!use_stdout)
- fclose(stdout);
+ fclose(rev.diffopt.file);
}
free(list);
free(branch_name);
@@ -1585,16 +1820,16 @@ static const char * const cherry_usage[] = {
};
static void print_commit(char sign, struct commit *commit, int verbose,
- int abbrev)
+ int abbrev, FILE *file)
{
if (!verbose) {
- printf("%c %s\n", sign,
- find_unique_abbrev(commit->object.sha1, abbrev));
+ fprintf(file, "%c %s\n", sign,
+ find_unique_abbrev(commit->object.oid.hash, abbrev));
} else {
struct strbuf buf = STRBUF_INIT;
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
- printf("%c %s %s\n", sign,
- find_unique_abbrev(commit->object.sha1, abbrev),
+ fprintf(file, "%c %s %s\n", sign,
+ find_unique_abbrev(commit->object.oid.hash, abbrev),
buf.buf);
strbuf_release(&buf);
}
@@ -1632,16 +1867,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
break;
default:
current_branch = branch_get(NULL);
- if (!current_branch || !current_branch->merge
- || !current_branch->merge[0]
- || !current_branch->merge[0]->dst) {
+ upstream = branch_get_upstream(current_branch, NULL);
+ if (!upstream) {
fprintf(stderr, _("Could not find a tracked"
" remote branch, please"
" specify <upstream> manually.\n"));
usage_with_options(cherry_usage, options);
}
-
- upstream = current_branch->merge[0]->dst;
}
init_revisions(&revs, prefix);
@@ -1655,7 +1887,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
/* Don't say anything if head and upstream are the same. */
if (revs.pending.nr == 2) {
struct object_array_entry *o = revs.pending.objects;
- if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+ if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
return 0;
}
@@ -1677,7 +1909,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
commit = list->item;
if (has_commit_patch_id(commit, &ids))
sign = '-';
- print_commit(sign, commit, verbose, abbrev);
+ print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
list = list->next;
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 6fa2205..00ea91a 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -27,6 +27,7 @@ static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
static int debug_mode;
+static int show_eol;
static const char *prefix;
static int max_prefix_len;
@@ -47,6 +48,23 @@ 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)
+{
+ if (!show_eol)
+ return;
+ else {
+ 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);
+ 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);
+ }
+}
+
static void write_name(const char *name)
{
/*
@@ -68,6 +86,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
return;
fputs(tag, stdout);
+ write_eolinfo(NULL, ent->name);
write_name(ent->name);
}
@@ -99,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)
*/
pos = cache_name_pos(ent->name, ent->len);
if (0 <= pos)
- die("bug in show-killed-files");
+ die("BUG: killed-file %.*s not found",
+ ent->len, ent->name);
pos = -pos - 1;
while (pos < active_nr &&
ce_stage(active_cache[pos]))
@@ -170,6 +190,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
find_unique_abbrev(ce->sha1,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;
@@ -359,14 +380,6 @@ static const char * const ls_files_usage[] = {
NULL
};
-static int option_parse_z(const struct option *opt,
- const char *arg, int unset)
-{
- line_terminator = unset ? '\n' : '\0';
-
- return 0;
-}
-
static int option_parse_exclude(const struct option *opt,
const char *arg, int unset)
{
@@ -408,9 +421,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
struct exclude_list *el;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct option builtin_ls_files_options[] = {
- { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
- N_("paths are separated with NUL character"),
- PARSE_OPT_NOARG, option_parse_z },
+ /* Think twice before adding "--nul" synonym to this */
+ OPT_SET_INT('z', NULL, &line_terminator,
+ N_("paths are separated with NUL character"), '\0'),
OPT_BOOL('t', NULL, &show_tag,
N_("identify the file status with tags")),
OPT_BOOL('v', NULL, &show_valid_bit,
@@ -433,6 +446,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
OPT_BIT(0, "directory", &dir.flags,
N_("show 'other' directories' names only"),
DIR_SHOW_OTHER_DIRECTORIES),
+ OPT_BOOL(0, "eol", &show_eol, N_("show line endings of files")),
OPT_NEGBIT(0, "empty-directory", &dir.flags,
N_("don't show empty directories"),
DIR_HIDE_EMPTY_DIRECTORIES),
@@ -516,7 +530,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
/* Treat unmatching pathspec elements as errors */
if (pathspec.nr && error_unmatch)
- ps_matched = xcalloc(1, pathspec.nr);
+ ps_matched = xcalloc(pathspec.nr, 1);
if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
die("ls-files --ignored needs some exclude pattern");
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 4554dbc..66cdd45 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -3,9 +3,12 @@
#include "transport.h"
#include "remote.h"
-static const char ls_remote_usage[] =
-"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n"
-" [-q | --quiet] [--exit-code] [--get-url] [<repository> [<refs>...]]";
+static const char * const ls_remote_usage[] = {
+ N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
+ " [-q | --quiet] [--exit-code] [--get-url]\n"
+ " [--symref] [<repository> [<refs>...]]"),
+ NULL
+};
/*
* Is there one among the list of patterns that match the tail part
@@ -30,12 +33,12 @@ static int tail_match(const char **pattern, const char *path)
int cmd_ls_remote(int argc, const char **argv, const char *prefix)
{
- int i;
const char *dest = NULL;
unsigned flags = 0;
int get_url = 0;
int quiet = 0;
int status = 0;
+ int show_symref_target = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
@@ -43,63 +46,36 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
struct transport *transport;
const struct ref *ref;
- if (argc == 2 && !strcmp("-h", argv[1]))
- usage(ls_remote_usage);
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("do not print remote URL")),
+ OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"),
+ N_("path of git-upload-pack on the remote host")),
+ { OPTION_STRING, 0, "exec", &uploadpack, N_("exec"),
+ N_("path of git-upload-pack on the remote host"),
+ PARSE_OPT_HIDDEN },
+ OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS),
+ OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS),
+ 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_BOOL(0, "symref", &show_symref_target,
+ N_("show underlying ref in addition to the object pointed by it")),
+ OPT_END()
+ };
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ argc = parse_options(argc, argv, prefix, options, ls_remote_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ dest = argv[0];
- if (*arg == '-') {
- if (starts_with(arg, "--upload-pack=")) {
- uploadpack = arg + 14;
- continue;
- }
- if (starts_with(arg, "--exec=")) {
- uploadpack = arg + 7;
- continue;
- }
- if (!strcmp("--tags", arg) || !strcmp("-t", arg)) {
- flags |= REF_TAGS;
- continue;
- }
- if (!strcmp("--heads", arg) || !strcmp("-h", arg)) {
- flags |= REF_HEADS;
- continue;
- }
- if (!strcmp("--refs", arg)) {
- flags |= REF_NORMAL;
- continue;
- }
- if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
- quiet = 1;
- continue;
- }
- if (!strcmp("--get-url", arg)) {
- get_url = 1;
- continue;
- }
- if (!strcmp("--exit-code", arg)) {
- /* return this code if no refs are reported */
- status = 2;
- continue;
- }
- usage(ls_remote_usage);
- }
- dest = arg;
- i++;
- break;
+ if (argc > 1) {
+ int i;
+ pattern = xcalloc(argc, sizeof(const char *));
+ for (i = 1; i < argc; i++)
+ pattern[i - 1] = xstrfmt("*/%s", argv[i]);
}
- if (argv[i]) {
- int j;
- pattern = xcalloc(argc - i + 1, sizeof(const char *));
- for (j = i; j < argc; j++) {
- int len = strlen(argv[j]);
- char *p = xmalloc(len + 3);
- sprintf(p, "*/%s", argv[j]);
- pattern[j - i] = p;
- }
- }
remote = remote_get(dest);
if (!remote) {
if (dest)
@@ -129,7 +105,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
continue;
if (!tail_match(pattern, ref->name))
continue;
- printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name);
+ if (show_symref_target && ref->symref)
+ printf("ref: %s\t%s\n", ref->symref, ref->name);
+ printf("%s\t%s\n", oid_to_hex(&ref->old_oid), ref->name);
status = 0; /* we found something */
}
return status;
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 3b04a0f..0e30d86 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -96,12 +96,13 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,
if (!strcmp(type, blob_type)) {
unsigned long size;
if (sha1_object_info(sha1, &size) == OBJ_BAD)
- strcpy(size_text, "BAD");
+ xsnprintf(size_text, sizeof(size_text),
+ "BAD");
else
- snprintf(size_text, sizeof(size_text),
- "%lu", size);
+ xsnprintf(size_text, sizeof(size_text),
+ "%lu", size);
} else
- strcpy(size_text, "-");
+ xsnprintf(size_text, sizeof(size_text), "-");
printf("%06o %s %s %7s\t", mode, type,
find_unique_abbrev(sha1, abbrev),
size_text);
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 999a525..f6df274 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -6,1029 +6,7 @@
#include "builtin.h"
#include "utf8.h"
#include "strbuf.h"
-
-static FILE *cmitmsg, *patchfile, *fin, *fout;
-
-static int keep_subject;
-static int keep_non_patch_brackets_in_subject;
-static const char *metainfo_charset;
-static struct strbuf line = STRBUF_INIT;
-static struct strbuf name = STRBUF_INIT;
-static struct strbuf email = STRBUF_INIT;
-static char *message_id;
-
-static enum {
- TE_DONTCARE, TE_QP, TE_BASE64
-} transfer_encoding;
-
-static struct strbuf charset = STRBUF_INIT;
-static int patch_lines;
-static struct strbuf **p_hdr_data, **s_hdr_data;
-static int use_scissors;
-static int add_message_id;
-static int use_inbody_headers = 1;
-
-#define MAX_HDR_PARSED 10
-#define MAX_BOUNDARIES 5
-
-static void cleanup_space(struct strbuf *sb);
-
-
-static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
-{
- struct strbuf *src = name;
- if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
- strchr(name->buf, '<') || strchr(name->buf, '>'))
- src = email;
- else if (name == out)
- return;
- strbuf_reset(out);
- strbuf_addbuf(out, src);
-}
-
-static void parse_bogus_from(const struct strbuf *line)
-{
- /* John Doe <johndoe> */
-
- char *bra, *ket;
- /* This is fallback, so do not bother if we already have an
- * e-mail address.
- */
- if (email.len)
- return;
-
- bra = strchr(line->buf, '<');
- if (!bra)
- return;
- ket = strchr(bra, '>');
- if (!ket)
- return;
-
- strbuf_reset(&email);
- strbuf_add(&email, bra + 1, ket - bra - 1);
-
- strbuf_reset(&name);
- strbuf_add(&name, line->buf, bra - line->buf);
- strbuf_trim(&name);
- get_sane_name(&name, &name, &email);
-}
-
-static void handle_from(const struct strbuf *from)
-{
- char *at;
- size_t el;
- struct strbuf f;
-
- strbuf_init(&f, from->len);
- strbuf_addbuf(&f, from);
-
- at = strchr(f.buf, '@');
- if (!at) {
- parse_bogus_from(from);
- return;
- }
-
- /*
- * If we already have one email, don't take any confusing lines
- */
- if (email.len && strchr(at + 1, '@')) {
- strbuf_release(&f);
- return;
- }
-
- /* Pick up the string around '@', possibly delimited with <>
- * pair; that is the email part.
- */
- while (at > f.buf) {
- char c = at[-1];
- if (isspace(c))
- break;
- if (c == '<') {
- at[-1] = ' ';
- break;
- }
- at--;
- }
- el = strcspn(at, " \n\t\r\v\f>");
- strbuf_reset(&email);
- strbuf_add(&email, at, el);
- strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
-
- /* The remainder is name. It could be
- *
- * - "John Doe <john.doe@xz>" (a), or
- * - "john.doe@xz (John Doe)" (b), or
- * - "John (zzz) Doe <john.doe@xz> (Comment)" (c)
- *
- * but we have removed the email part, so
- *
- * - remove extra spaces which could stay after email (case 'c'), and
- * - trim from both ends, possibly removing the () pair at the end
- * (cases 'a' and 'b').
- */
- cleanup_space(&f);
- strbuf_trim(&f);
- if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
- strbuf_remove(&f, 0, 1);
- strbuf_setlen(&f, f.len - 1);
- }
-
- get_sane_name(&name, &f, &email);
- strbuf_release(&f);
-}
-
-static void handle_header(struct strbuf **out, const struct strbuf *line)
-{
- if (!*out) {
- *out = xmalloc(sizeof(struct strbuf));
- strbuf_init(*out, line->len);
- } else
- strbuf_reset(*out);
-
- strbuf_addbuf(*out, line);
-}
-
-/* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists. For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
-{
- const char *ends, *ap = strcasestr(line, name);
- size_t sz;
-
- strbuf_setlen(attr, 0);
- if (!ap)
- return 0;
- ap += strlen(name);
- if (*ap == '"') {
- ap++;
- ends = "\"";
- }
- else
- ends = "; \t";
- sz = strcspn(ap, ends);
- strbuf_add(attr, ap, sz);
- return 1;
-}
-
-static struct strbuf *content[MAX_BOUNDARIES];
-
-static struct strbuf **content_top = content;
-
-static void handle_content_type(struct strbuf *line)
-{
- struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
- strbuf_init(boundary, line->len);
-
- if (slurp_attr(line->buf, "boundary=", boundary)) {
- strbuf_insert(boundary, 0, "--", 2);
- if (++content_top > &content[MAX_BOUNDARIES]) {
- fprintf(stderr, "Too many boundaries to handle\n");
- exit(1);
- }
- *content_top = boundary;
- boundary = NULL;
- }
- slurp_attr(line->buf, "charset=", &charset);
-
- if (boundary) {
- strbuf_release(boundary);
- free(boundary);
- }
-}
-
-static void handle_message_id(const struct strbuf *line)
-{
- if (add_message_id)
- message_id = strdup(line->buf);
-}
-
-static void handle_content_transfer_encoding(const struct strbuf *line)
-{
- if (strcasestr(line->buf, "base64"))
- transfer_encoding = TE_BASE64;
- else if (strcasestr(line->buf, "quoted-printable"))
- transfer_encoding = TE_QP;
- else
- transfer_encoding = TE_DONTCARE;
-}
-
-static int is_multipart_boundary(const struct strbuf *line)
-{
- return (((*content_top)->len <= line->len) &&
- !memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
-}
-
-static void cleanup_subject(struct strbuf *subject)
-{
- size_t at = 0;
-
- while (at < subject->len) {
- char *pos;
- size_t remove;
-
- switch (subject->buf[at]) {
- case 'r': case 'R':
- if (subject->len <= at + 3)
- break;
- if ((subject->buf[at + 1] == 'e' ||
- subject->buf[at + 1] == 'E') &&
- subject->buf[at + 2] == ':') {
- strbuf_remove(subject, at, 3);
- continue;
- }
- at++;
- break;
- case ' ': case '\t': case ':':
- strbuf_remove(subject, at, 1);
- continue;
- case '[':
- pos = strchr(subject->buf + at, ']');
- if (!pos)
- break;
- remove = pos - subject->buf + at + 1;
- if (!keep_non_patch_brackets_in_subject ||
- (7 <= remove &&
- memmem(subject->buf + at, remove, "PATCH", 5)))
- strbuf_remove(subject, at, remove);
- else {
- at += remove;
- /*
- * If the input had a space after the ], keep
- * it. We don't bother with finding the end of
- * the space, since we later normalize it
- * anyway.
- */
- if (isspace(subject->buf[at]))
- at += 1;
- }
- continue;
- }
- break;
- }
- strbuf_trim(subject);
-}
-
-static void cleanup_space(struct strbuf *sb)
-{
- size_t pos, cnt;
- for (pos = 0; pos < sb->len; pos++) {
- if (isspace(sb->buf[pos])) {
- sb->buf[pos] = ' ';
- for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
- strbuf_remove(sb, pos + 1, cnt);
- }
- }
-}
-
-static void decode_header(struct strbuf *line);
-static const char *header[MAX_HDR_PARSED] = {
- "From","Subject","Date",
-};
-
-static inline int cmp_header(const struct strbuf *line, const char *hdr)
-{
- int len = strlen(hdr);
- return !strncasecmp(line->buf, hdr, len) && line->len > len &&
- line->buf[len] == ':' && isspace(line->buf[len + 1]);
-}
-
-static int is_format_patch_separator(const char *line, int len)
-{
- static const char SAMPLE[] =
- "From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
- const char *cp;
-
- if (len != strlen(SAMPLE))
- return 0;
- if (!skip_prefix(line, "From ", &cp))
- return 0;
- if (strspn(cp, "0123456789abcdef") != 40)
- return 0;
- cp += 40;
- return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
-}
-
-static int check_header(const struct strbuf *line,
- struct strbuf *hdr_data[], int overwrite)
-{
- int i, ret = 0, len;
- struct strbuf sb = STRBUF_INIT;
- /* search for the interesting parts */
- for (i = 0; header[i]; i++) {
- int len = strlen(header[i]);
- if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
- /* Unwrap inline B and Q encoding, and optionally
- * normalize the meta information to utf8.
- */
- strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
- decode_header(&sb);
- handle_header(&hdr_data[i], &sb);
- ret = 1;
- goto check_header_out;
- }
- }
-
- /* Content stuff */
- if (cmp_header(line, "Content-Type")) {
- len = strlen("Content-Type: ");
- strbuf_add(&sb, line->buf + len, line->len - len);
- decode_header(&sb);
- strbuf_insert(&sb, 0, "Content-Type: ", len);
- handle_content_type(&sb);
- ret = 1;
- goto check_header_out;
- }
- if (cmp_header(line, "Content-Transfer-Encoding")) {
- len = strlen("Content-Transfer-Encoding: ");
- strbuf_add(&sb, line->buf + len, line->len - len);
- decode_header(&sb);
- handle_content_transfer_encoding(&sb);
- ret = 1;
- goto check_header_out;
- }
- if (cmp_header(line, "Message-Id")) {
- len = strlen("Message-Id: ");
- strbuf_add(&sb, line->buf + len, line->len - len);
- decode_header(&sb);
- handle_message_id(&sb);
- ret = 1;
- goto check_header_out;
- }
-
- /* for inbody stuff */
- if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
- ret = is_format_patch_separator(line->buf + 1, line->len - 1);
- goto check_header_out;
- }
- if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
- for (i = 0; header[i]; i++) {
- if (!strcmp("Subject", header[i])) {
- handle_header(&hdr_data[i], line);
- ret = 1;
- goto check_header_out;
- }
- }
- }
-
-check_header_out:
- strbuf_release(&sb);
- return ret;
-}
-
-static int is_rfc2822_header(const struct strbuf *line)
-{
- /*
- * The section that defines the loosest possible
- * field name is "3.6.8 Optional fields".
- *
- * optional-field = field-name ":" unstructured CRLF
- * field-name = 1*ftext
- * ftext = %d33-57 / %59-126
- */
- int ch;
- char *cp = line->buf;
-
- /* Count mbox From headers as headers */
- if (starts_with(cp, "From ") || starts_with(cp, ">From "))
- return 1;
-
- while ((ch = *cp++)) {
- if (ch == ':')
- return 1;
- if ((33 <= ch && ch <= 57) ||
- (59 <= ch && ch <= 126))
- continue;
- break;
- }
- return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
- /* Get the first part of the line. */
- if (strbuf_getline(line, in, '\n'))
- return 0;
-
- /*
- * Is it an empty line or not a valid rfc2822 header?
- * If so, stop here, and return false ("not a header")
- */
- strbuf_rtrim(line);
- if (!line->len || !is_rfc2822_header(line)) {
- /* Re-add the newline */
- strbuf_addch(line, '\n');
- return 0;
- }
-
- /*
- * Now we need to eat all the continuation lines..
- * Yuck, 2822 header "folding"
- */
- for (;;) {
- int peek;
- struct strbuf continuation = STRBUF_INIT;
-
- peek = fgetc(in); ungetc(peek, in);
- if (peek != ' ' && peek != '\t')
- break;
- if (strbuf_getline(&continuation, in, '\n'))
- break;
- continuation.buf[0] = ' ';
- strbuf_rtrim(&continuation);
- strbuf_addbuf(line, &continuation);
- }
-
- return 1;
-}
-
-static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
-{
- const char *in = q_seg->buf;
- int c;
- struct strbuf *out = xmalloc(sizeof(struct strbuf));
- strbuf_init(out, q_seg->len);
-
- while ((c = *in++) != 0) {
- if (c == '=') {
- int d = *in++;
- if (d == '\n' || !d)
- break; /* drop trailing newline */
- strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
- continue;
- }
- if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
- c = 0x20;
- strbuf_addch(out, c);
- }
- return out;
-}
-
-static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
-{
- /* Decode in..ep, possibly in-place to ot */
- int c, pos = 0, acc = 0;
- const char *in = b_seg->buf;
- struct strbuf *out = xmalloc(sizeof(struct strbuf));
- strbuf_init(out, b_seg->len);
-
- while ((c = *in++) != 0) {
- if (c == '+')
- c = 62;
- else if (c == '/')
- c = 63;
- else if ('A' <= c && c <= 'Z')
- c -= 'A';
- else if ('a' <= c && c <= 'z')
- c -= 'a' - 26;
- else if ('0' <= c && c <= '9')
- c -= '0' - 52;
- else
- continue; /* garbage */
- switch (pos++) {
- case 0:
- acc = (c << 2);
- break;
- case 1:
- strbuf_addch(out, (acc | (c >> 4)));
- acc = (c & 15) << 4;
- break;
- case 2:
- strbuf_addch(out, (acc | (c >> 2)));
- acc = (c & 3) << 6;
- break;
- case 3:
- strbuf_addch(out, (acc | c));
- acc = pos = 0;
- break;
- }
- }
- return out;
-}
-
-static void convert_to_utf8(struct strbuf *line, const char *charset)
-{
- char *out;
-
- if (!charset || !*charset)
- return;
-
- if (same_encoding(metainfo_charset, charset))
- return;
- out = reencode_string(line->buf, metainfo_charset, charset);
- if (!out)
- die("cannot convert from %s to %s",
- charset, metainfo_charset);
- strbuf_attach(line, out, strlen(out), strlen(out));
-}
-
-static int decode_header_bq(struct strbuf *it)
-{
- char *in, *ep, *cp;
- struct strbuf outbuf = STRBUF_INIT, *dec;
- struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
- int rfc2047 = 0;
-
- in = it->buf;
- while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
- int encoding;
- strbuf_reset(&charset_q);
- strbuf_reset(&piecebuf);
- rfc2047 = 1;
-
- if (in != ep) {
- /*
- * We are about to process an encoded-word
- * that begins at ep, but there is something
- * before the encoded word.
- */
- char *scan;
- for (scan = in; scan < ep; scan++)
- if (!isspace(*scan))
- break;
-
- if (scan != ep || in == it->buf) {
- /*
- * We should not lose that "something",
- * unless we have just processed an
- * encoded-word, and there is only LWS
- * before the one we are about to process.
- */
- strbuf_add(&outbuf, in, ep - in);
- }
- }
- /* E.g.
- * ep : "=?iso-2022-jp?B?GyR...?= foo"
- * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
- */
- ep += 2;
-
- if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
- goto decode_header_bq_out;
-
- if (cp + 3 - it->buf > it->len)
- goto decode_header_bq_out;
- strbuf_add(&charset_q, ep, cp - ep);
-
- encoding = cp[1];
- if (!encoding || cp[2] != '?')
- goto decode_header_bq_out;
- ep = strstr(cp + 3, "?=");
- if (!ep)
- goto decode_header_bq_out;
- strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
- switch (tolower(encoding)) {
- default:
- goto decode_header_bq_out;
- case 'b':
- dec = decode_b_segment(&piecebuf);
- break;
- case 'q':
- dec = decode_q_segment(&piecebuf, 1);
- break;
- }
- if (metainfo_charset)
- convert_to_utf8(dec, charset_q.buf);
-
- strbuf_addbuf(&outbuf, dec);
- strbuf_release(dec);
- free(dec);
- in = ep + 2;
- }
- strbuf_addstr(&outbuf, in);
- strbuf_reset(it);
- strbuf_addbuf(it, &outbuf);
-decode_header_bq_out:
- strbuf_release(&outbuf);
- strbuf_release(&charset_q);
- strbuf_release(&piecebuf);
- return rfc2047;
-}
-
-static void decode_header(struct strbuf *it)
-{
- if (decode_header_bq(it))
- return;
- /* otherwise "it" is a straight copy of the input.
- * This can be binary guck but there is no charset specified.
- */
- if (metainfo_charset)
- convert_to_utf8(it, "");
-}
-
-static void decode_transfer_encoding(struct strbuf *line)
-{
- struct strbuf *ret;
-
- switch (transfer_encoding) {
- case TE_QP:
- ret = decode_q_segment(line, 0);
- break;
- case TE_BASE64:
- ret = decode_b_segment(line);
- break;
- case TE_DONTCARE:
- default:
- return;
- }
- strbuf_reset(line);
- strbuf_addbuf(line, ret);
- strbuf_release(ret);
- free(ret);
-}
-
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
- while (!strbuf_getline(&line, fin, '\n')) {
- if (*content_top && is_multipart_boundary(&line))
- return 1;
- }
- return 0;
-}
-
-static int handle_boundary(void)
-{
- struct strbuf newline = STRBUF_INIT;
-
- strbuf_addch(&newline, '\n');
-again:
- if (line.len >= (*content_top)->len + 2 &&
- !memcmp(line.buf + (*content_top)->len, "--", 2)) {
- /* we hit an end boundary */
- /* pop the current boundary off the stack */
- strbuf_release(*content_top);
- free(*content_top);
- *content_top = NULL;
-
- /* technically won't happen as is_multipart_boundary()
- will fail first. But just in case..
- */
- if (--content_top < content) {
- fprintf(stderr, "Detected mismatched boundaries, "
- "can't recover\n");
- exit(1);
- }
- handle_filter(&newline);
- strbuf_release(&newline);
-
- /* skip to the next boundary */
- if (!find_boundary())
- return 0;
- goto again;
- }
-
- /* set some defaults */
- transfer_encoding = TE_DONTCARE;
- strbuf_reset(&charset);
-
- /* slurp in this section's info */
- while (read_one_header_line(&line, fin))
- check_header(&line, p_hdr_data, 0);
-
- strbuf_release(&newline);
- /* replenish line */
- if (strbuf_getline(&line, fin, '\n'))
- return 0;
- strbuf_addch(&line, '\n');
- return 1;
-}
-
-static inline int patchbreak(const struct strbuf *line)
-{
- size_t i;
-
- /* Beginning of a "diff -" header? */
- if (starts_with(line->buf, "diff -"))
- return 1;
-
- /* CVS "Index: " line? */
- if (starts_with(line->buf, "Index: "))
- return 1;
-
- /*
- * "--- <filename>" starts patches without headers
- * "---<sp>*" is a manual separator
- */
- if (line->len < 4)
- return 0;
-
- if (starts_with(line->buf, "---")) {
- /* space followed by a filename? */
- if (line->buf[3] == ' ' && !isspace(line->buf[4]))
- return 1;
- /* Just whitespace? */
- for (i = 3; i < line->len; i++) {
- unsigned char c = line->buf[i];
- if (c == '\n')
- return 1;
- if (!isspace(c))
- break;
- }
- return 0;
- }
- return 0;
-}
-
-static int is_scissors_line(const struct strbuf *line)
-{
- size_t i, len = line->len;
- int scissors = 0, gap = 0;
- int first_nonblank = -1;
- int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
- const char *buf = line->buf;
-
- for (i = 0; i < len; i++) {
- if (isspace(buf[i])) {
- if (in_perforation) {
- perforation++;
- gap++;
- }
- continue;
- }
- last_nonblank = i;
- if (first_nonblank < 0)
- first_nonblank = i;
- if (buf[i] == '-') {
- in_perforation = 1;
- perforation++;
- continue;
- }
- if (i + 1 < len &&
- (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
- !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
- in_perforation = 1;
- perforation += 2;
- scissors += 2;
- i++;
- continue;
- }
- in_perforation = 0;
- }
-
- /*
- * The mark must be at least 8 bytes long (e.g. "-- >8 --").
- * Even though there can be arbitrary cruft on the same line
- * (e.g. "cut here"), in order to avoid misidentification, the
- * perforation must occupy more than a third of the visible
- * width of the line, and dashes and scissors must occupy more
- * than half of the perforation.
- */
-
- visible = last_nonblank - first_nonblank + 1;
- return (scissors && 8 <= visible &&
- visible < perforation * 3 &&
- gap * 2 < perforation);
-}
-
-static int handle_commit_msg(struct strbuf *line)
-{
- static int still_looking = 1;
-
- if (!cmitmsg)
- return 0;
-
- if (still_looking) {
- if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
- return 0;
- }
-
- if (use_inbody_headers && still_looking) {
- still_looking = check_header(line, s_hdr_data, 0);
- if (still_looking)
- return 0;
- } else
- /* Only trim the first (blank) line of the commit message
- * when ignoring in-body headers.
- */
- still_looking = 0;
-
- /* normalize the log message to UTF-8. */
- if (metainfo_charset)
- convert_to_utf8(line, charset.buf);
-
- if (use_scissors && is_scissors_line(line)) {
- int i;
- if (fseek(cmitmsg, 0L, SEEK_SET))
- die_errno("Could not rewind output message file");
- if (ftruncate(fileno(cmitmsg), 0))
- die_errno("Could not truncate output message file at scissors");
- still_looking = 1;
-
- /*
- * We may have already read "secondary headers"; purge
- * them to give ourselves a clean restart.
- */
- for (i = 0; header[i]; i++) {
- if (s_hdr_data[i])
- strbuf_release(s_hdr_data[i]);
- s_hdr_data[i] = NULL;
- }
- return 0;
- }
-
- if (patchbreak(line)) {
- if (message_id)
- fprintf(cmitmsg, "Message-Id: %s\n", message_id);
- fclose(cmitmsg);
- cmitmsg = NULL;
- return 1;
- }
-
- fputs(line->buf, cmitmsg);
- return 0;
-}
-
-static void handle_patch(const struct strbuf *line)
-{
- fwrite(line->buf, 1, line->len, patchfile);
- patch_lines++;
-}
-
-static void handle_filter(struct strbuf *line)
-{
- static int filter = 0;
-
- /* filter tells us which part we left off on */
- switch (filter) {
- case 0:
- if (!handle_commit_msg(line))
- break;
- filter++;
- case 1:
- handle_patch(line);
- break;
- }
-}
-
-static void handle_body(void)
-{
- struct strbuf prev = STRBUF_INIT;
-
- /* Skip up to the first boundary */
- if (*content_top) {
- if (!find_boundary())
- goto handle_body_out;
- }
-
- do {
- /* process any boundary lines */
- if (*content_top && is_multipart_boundary(&line)) {
- /* flush any leftover */
- if (prev.len) {
- handle_filter(&prev);
- strbuf_reset(&prev);
- }
- if (!handle_boundary())
- goto handle_body_out;
- }
-
- /* Unwrap transfer encoding */
- decode_transfer_encoding(&line);
-
- switch (transfer_encoding) {
- case TE_BASE64:
- case TE_QP:
- {
- struct strbuf **lines, **it, *sb;
-
- /* Prepend any previous partial lines */
- strbuf_insert(&line, 0, prev.buf, prev.len);
- strbuf_reset(&prev);
-
- /*
- * This is a decoded line that may contain
- * multiple new lines. Pass only one chunk
- * at a time to handle_filter()
- */
- lines = strbuf_split(&line, '\n');
- for (it = lines; (sb = *it); it++) {
- if (*(it + 1) == NULL) /* The last line */
- if (sb->buf[sb->len - 1] != '\n') {
- /* Partial line, save it for later. */
- strbuf_addbuf(&prev, sb);
- break;
- }
- handle_filter(sb);
- }
- /*
- * The partial chunk is saved in "prev" and will be
- * appended by the next iteration of read_line_with_nul().
- */
- strbuf_list_free(lines);
- break;
- }
- default:
- handle_filter(&line);
- }
-
- } while (!strbuf_getwholeline(&line, fin, '\n'));
-
-handle_body_out:
- strbuf_release(&prev);
-}
-
-static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
-{
- const char *sp = data->buf;
- while (1) {
- char *ep = strchr(sp, '\n');
- int len;
- if (!ep)
- len = strlen(sp);
- else
- len = ep - sp;
- fprintf(fout, "%s: %.*s\n", hdr, len, sp);
- if (!ep)
- break;
- sp = ep + 1;
- }
-}
-
-static void handle_info(void)
-{
- struct strbuf *hdr;
- int i;
-
- for (i = 0; header[i]; i++) {
- /* only print inbody headers if we output a patch file */
- if (patch_lines && s_hdr_data[i])
- hdr = s_hdr_data[i];
- else if (p_hdr_data[i])
- hdr = p_hdr_data[i];
- else
- continue;
-
- if (!strcmp(header[i], "Subject")) {
- if (!keep_subject) {
- cleanup_subject(hdr);
- cleanup_space(hdr);
- }
- output_header_lines(fout, "Subject", hdr);
- } else if (!strcmp(header[i], "From")) {
- cleanup_space(hdr);
- handle_from(hdr);
- fprintf(fout, "Author: %s\n", name.buf);
- fprintf(fout, "Email: %s\n", email.buf);
- } else {
- cleanup_space(hdr);
- fprintf(fout, "%s: %s\n", header[i], hdr->buf);
- }
- }
- fprintf(fout, "\n");
-}
-
-static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
-{
- int peek;
- fin = in;
- fout = out;
-
- cmitmsg = fopen(msg, "w");
- if (!cmitmsg) {
- perror(msg);
- return -1;
- }
- patchfile = fopen(patch, "w");
- if (!patchfile) {
- perror(patch);
- fclose(cmitmsg);
- return -1;
- }
-
- p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
- s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
-
- do {
- peek = fgetc(in);
- } while (isspace(peek));
- ungetc(peek, in);
-
- /* process the email header */
- while (read_one_header_line(&line, fin))
- check_header(&line, p_hdr_data, 1);
-
- handle_body();
- handle_info();
-
- return 0;
-}
-
-static int git_mailinfo_config(const char *var, const char *value, void *unused)
-{
- if (!starts_with(var, "mailinfo."))
- return git_default_config(var, value, unused);
- if (!strcmp(var, "mailinfo.scissors")) {
- use_scissors = git_config_bool(var, value);
- return 0;
- }
- /* perhaps others here */
- return 0;
-}
+#include "mailinfo.h"
static const char mailinfo_usage[] =
"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
@@ -1036,34 +14,36 @@ static const char mailinfo_usage[] =
int cmd_mailinfo(int argc, const char **argv, const char *prefix)
{
const char *def_charset;
+ struct mailinfo mi;
+ int status;
/* NEEDSWORK: might want to do the optional .git/ directory
* discovery
*/
- git_config(git_mailinfo_config, NULL);
+ setup_mailinfo(&mi);
def_charset = get_commit_output_encoding();
- metainfo_charset = def_charset;
+ mi.metainfo_charset = def_charset;
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "-k"))
- keep_subject = 1;
+ mi.keep_subject = 1;
else if (!strcmp(argv[1], "-b"))
- keep_non_patch_brackets_in_subject = 1;
+ mi.keep_non_patch_brackets_in_subject = 1;
else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
- add_message_id = 1;
+ mi.add_message_id = 1;
else if (!strcmp(argv[1], "-u"))
- metainfo_charset = def_charset;
+ mi.metainfo_charset = def_charset;
else if (!strcmp(argv[1], "-n"))
- metainfo_charset = NULL;
+ mi.metainfo_charset = NULL;
else if (starts_with(argv[1], "--encoding="))
- metainfo_charset = argv[1] + 11;
+ mi.metainfo_charset = argv[1] + 11;
else if (!strcmp(argv[1], "--scissors"))
- use_scissors = 1;
+ mi.use_scissors = 1;
else if (!strcmp(argv[1], "--no-scissors"))
- use_scissors = 0;
+ mi.use_scissors = 0;
else if (!strcmp(argv[1], "--no-inbody-headers"))
- use_inbody_headers = 0;
+ mi.use_inbody_headers = 0;
else
usage(mailinfo_usage);
argc--; argv++;
@@ -1072,5 +52,10 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
if (argc != 3)
usage(mailinfo_usage);
- return !!mailinfo(stdin, stdout, argv[1], argv[2]);
+ mi.input = stdin;
+ mi.output = stdout;
+ status = !!mailinfo(&mi, argv[1], argv[2]);
+ clear_mailinfo(&mi);
+
+ return status;
}
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 8e02ea1..3068168 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -45,6 +45,19 @@ static int is_from_line(const char *line, int len)
static struct strbuf buf = STRBUF_INIT;
static int keep_cr;
+static int mboxrd;
+
+static int is_gtfrom(const struct strbuf *buf)
+{
+ size_t min = strlen(">From ");
+ size_t ngt;
+
+ if (buf->len < min)
+ return 0;
+
+ ngt = strspn(buf->buf, ">");
+ return ngt && starts_with(buf->buf + ngt, "From ");
+}
/* Called with the first line (potentially partial)
* already in buf[] -- normally that should begin with
@@ -77,6 +90,9 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
strbuf_addch(&buf, '\n');
}
+ if (mboxrd && is_gtfrom(&buf))
+ strbuf_remove(&buf, 0, 1);
+
if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
die_errno("cannot write output");
@@ -98,30 +114,37 @@ static int populate_maildir_list(struct string_list *list, const char *path)
{
DIR *dir;
struct dirent *dent;
- char name[PATH_MAX];
+ char *name = NULL;
char *subs[] = { "cur", "new", NULL };
char **sub;
+ int ret = -1;
for (sub = subs; *sub; ++sub) {
- snprintf(name, sizeof(name), "%s/%s", path, *sub);
+ free(name);
+ name = xstrfmt("%s/%s", path, *sub);
if ((dir = opendir(name)) == NULL) {
if (errno == ENOENT)
continue;
- error("cannot opendir %s (%s)", name, strerror(errno));
- return -1;
+ error_errno("cannot opendir %s", name);
+ goto out;
}
while ((dent = readdir(dir)) != NULL) {
if (dent->d_name[0] == '.')
continue;
- snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
+ free(name);
+ name = xstrfmt("%s/%s", *sub, dent->d_name);
string_list_insert(list, name);
}
closedir(dir);
}
- return 0;
+ ret = 0;
+
+out:
+ free(name);
+ return ret;
}
static int maildir_filename_cmp(const char *a, const char *b)
@@ -148,8 +171,8 @@ static int maildir_filename_cmp(const char *a, const char *b)
static int split_maildir(const char *maildir, const char *dir,
int nr_prec, int skip)
{
- char file[PATH_MAX];
- char name[PATH_MAX];
+ char *file = NULL;
+ FILE *f = NULL;
int ret = -1;
int i;
struct string_list list = STRING_LIST_INIT_DUP;
@@ -160,27 +183,35 @@ static int split_maildir(const char *maildir, const char *dir,
goto out;
for (i = 0; i < list.nr; i++) {
- FILE *f;
- snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].string);
+ char *name;
+
+ free(file);
+ file = xstrfmt("%s/%s", maildir, list.items[i].string);
+
f = fopen(file, "r");
if (!f) {
- error("cannot open mail %s (%s)", file, strerror(errno));
+ error_errno("cannot open mail %s", file);
goto out;
}
if (strbuf_getwholeline(&buf, f, '\n')) {
- error("cannot read mail %s (%s)", file, strerror(errno));
+ error_errno("cannot read mail %s", file);
goto out;
}
- sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
split_one(f, name, 1);
+ free(name);
fclose(f);
+ f = NULL;
}
ret = skip;
out:
+ if (f)
+ fclose(f);
+ free(file);
string_list_clear(&list, 1);
return ret;
}
@@ -188,7 +219,6 @@ out:
static int split_mbox(const char *file, const char *dir, int allow_bare,
int nr_prec, int skip)
{
- char name[PATH_MAX];
int ret = -1;
int peek;
@@ -196,7 +226,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
int file_done = 0;
if (!f) {
- error("cannot open mbox %s", file);
+ error_errno("cannot open mbox %s", file);
goto out;
}
@@ -215,8 +245,9 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
}
while (!file_done) {
- sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ char *name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
file_done = split_one(f, name, allow_bare);
+ free(name);
}
if (f != stdin)
@@ -256,6 +287,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
keep_cr = 1;
} else if ( arg[1] == 'o' && arg[2] ) {
dir = arg+2;
+ } else if (!strcmp(arg, "--mboxrd")) {
+ mboxrd = 1;
} else if ( arg[1] == '-' && !arg[2] ) {
argp++; /* -- marks end of options */
break;
@@ -303,7 +336,7 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
}
if (stat(arg, &argstat) == -1) {
- error("cannot stat %s (%s)", arg, strerror(errno));
+ error_errno("cannot stat %s", arg);
return 1;
}
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 08a8217..c0d1822 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -16,7 +16,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
return 1;
while (result) {
- printf("%s\n", sha1_to_hex(result->item->object.sha1));
+ printf("%s\n", oid_to_hex(&result->item->object.oid));
if (!show_all)
return 0;
result = result->next;
@@ -62,7 +62,7 @@ static int handle_independent(int count, const char **args)
return 1;
while (result) {
- printf("%s\n", sha1_to_hex(result->item->object.sha1));
+ printf("%s\n", oid_to_hex(&result->item->object.oid));
result = result->next;
}
return 0;
@@ -83,7 +83,7 @@ static int handle_octopus(int count, const char **args, int show_all)
return 1;
while (result) {
- printf("%s\n", sha1_to_hex(result->item->object.sha1));
+ printf("%s\n", oid_to_hex(&result->item->object.oid));
if (!show_all)
return 0;
result = result->next;
@@ -196,7 +196,7 @@ static int handle_fork_point(int argc, const char **argv)
goto cleanup_return;
}
- printf("%s\n", sha1_to_hex(bases->item->object.sha1));
+ printf("%s\n", oid_to_hex(&bases->item->object.oid));
cleanup_return:
free_commit_list(bases);
@@ -252,7 +252,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
if (argc < 2)
usage_with_options(merge_base_usage, options);
- rev = xmalloc(argc * sizeof(*rev));
+ ALLOC_ARRAY(rev, argc);
while (argc-- > 0)
rev[rev_nr++] = get_commit_reference(*argv++);
return show_merge_base(rev, rev_nr, show_all);
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 50d0bc8..13e22a2 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -62,8 +62,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
usage_with_options(merge_file_usage, options);
if (quiet) {
if (!freopen("/dev/null", "w", stderr))
- return error("failed to redirect stderr to /dev/null: "
- "%s", strerror(errno));
+ return error_errno("failed to redirect stderr to /dev/null");
}
if (prefix)
@@ -95,14 +94,18 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
if (!f)
- ret = error("Could not open %s for writing", filename);
+ ret = error_errno("Could not open %s for writing",
+ filename);
else if (result.size &&
fwrite(result.ptr, result.size, 1, f) != 1)
- ret = error("Could not write to %s", filename);
+ ret = error_errno("Could not write to %s", filename);
else if (fclose(f))
- ret = error("Could not close %s", filename);
+ ret = error_errno("Could not close %s", filename);
free(result.ptr);
}
+ if (ret > 127)
+ ret = 127;
+
return ret;
}
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 1a1eafa..1c3427c 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -9,7 +9,7 @@ static int merge_entry(int pos, const char *path)
{
int found;
const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
- char hexbuf[4][60];
+ char hexbuf[4][GIT_SHA1_HEXSZ + 1];
char ownbuf[4][60];
if (pos >= active_nr)
@@ -22,8 +22,8 @@ static int merge_entry(int pos, const char *path)
if (strcmp(ce->name, path))
break;
found++;
- strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
- sprintf(ownbuf[stage], "%o", ce->ce_mode);
+ sha1_to_hex_r(hexbuf[stage], ce->sha1);
+ xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
arguments[stage] = hexbuf[stage];
arguments[stage + 4] = ownbuf[stage];
} while (++pos < active_nr);
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index a90f28f..fd2c455 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -9,22 +9,22 @@ static const char builtin_merge_recursive_usage[] =
static const char *better_branch_name(const char *branch)
{
- static char githead_env[8 + 40 + 1];
+ static char githead_env[8 + GIT_SHA1_HEXSZ + 1];
char *name;
- if (strlen(branch) != 40)
+ if (strlen(branch) != GIT_SHA1_HEXSZ)
return branch;
- sprintf(githead_env, "GITHEAD_%s", branch);
+ xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
name = getenv(githead_env);
return name ? name : branch;
}
int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
{
- const unsigned char *bases[21];
+ const struct object_id *bases[21];
unsigned bases_count = 0;
int i, failed;
- unsigned char h1[20], h2[20];
+ struct object_id h1, h2;
struct merge_options o;
struct commit *result;
@@ -46,10 +46,10 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
continue;
}
if (bases_count < ARRAY_SIZE(bases)-1) {
- unsigned char *sha = xmalloc(20);
- if (get_sha1(argv[i], sha))
+ struct object_id *oid = xmalloc(sizeof(struct object_id));
+ if (get_oid(argv[i], oid))
die("Could not parse object '%s'", argv[i]);
- bases[bases_count++] = sha;
+ bases[bases_count++] = oid;
}
else
warning("Cannot handle more than %d bases. "
@@ -62,9 +62,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
o.branch1 = argv[++i];
o.branch2 = argv[++i];
- if (get_sha1(o.branch1, h1))
+ if (get_oid(o.branch1, &h1))
die("Could not resolve ref '%s'", o.branch1);
- if (get_sha1(o.branch2, h2))
+ if (get_oid(o.branch2, &h2))
die("Could not resolve ref '%s'", o.branch2);
o.branch1 = better_branch_name(o.branch1);
@@ -73,7 +73,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
if (o.verbosity >= 3)
printf("Merging %s with %s\n", o.branch1, o.branch2);
- failed = merge_recursive_generic(&o, h1, h2, bases_count, bases, &result);
+ failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
if (failed < 0)
return 128; /* die() error code */
return failed;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 2a4aafe..5b7ab9b 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -60,7 +60,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
const char *path = entry->path;
if (!entry->stage)
- return read_sha1_file(entry->blob->object.sha1, &type, size);
+ return read_sha1_file(entry->blob->object.oid.hash, &type, size);
base = NULL;
if (entry->stage == 1) {
base = entry->blob;
@@ -82,7 +82,7 @@ static void *origin(struct merge_list *entry, unsigned long *size)
enum object_type type;
while (entry) {
if (entry->stage == 2)
- return read_sha1_file(entry->blob->object.sha1, &type, size);
+ return read_sha1_file(entry->blob->object.oid.hash, &type, size);
entry = entry->link;
}
return NULL;
@@ -130,7 +130,7 @@ static void show_result_list(struct merge_list *entry)
do {
struct merge_list *link = entry->link;
static const char *desc[4] = { "result", "base", "our", "their" };
- printf(" %-6s %o %s %s\n", desc[entry->stage], entry->mode, sha1_to_hex(entry->blob->object.sha1), entry->path);
+ printf(" %-6s %o %s %s\n", desc[entry->stage], entry->mode, oid_to_hex(&entry->blob->object.oid), entry->path);
entry = link;
} while (entry);
}
@@ -150,15 +150,15 @@ static void show_result(void)
/* An empty entry never compares same, not even to another empty entry */
static int same_entry(struct name_entry *a, struct name_entry *b)
{
- return a->sha1 &&
- b->sha1 &&
- !hashcmp(a->sha1, b->sha1) &&
+ return a->oid &&
+ b->oid &&
+ !oidcmp(a->oid, b->oid) &&
a->mode == b->mode;
}
static int both_empty(struct name_entry *a, struct name_entry *b)
{
- return !(a->sha1 || b->sha1);
+ return !(a->oid || b->oid);
}
static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
@@ -174,7 +174,7 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsi
static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
{
- char *path = xmalloc(traverse_path_len(info, n) + 1);
+ char *path = xmallocz(traverse_path_len(info, n));
return make_traverse_path(path, info, n);
}
@@ -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->sha1, path);
- final = create_entry(0, result->mode, result->sha1, path);
+ orig = create_entry(2, ours->mode, ours->oid->hash, path);
+ final = create_entry(0, result->mode, result->oid->hash, path);
final->link = orig;
@@ -213,7 +213,7 @@ 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)->sha1 : NULL)
+#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));
@@ -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->sha1, path);
+ link = create_entry(stage, n->mode, n->oid->hash, path);
link->link = entry;
return link;
}
@@ -314,7 +314,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
}
if (same_entry(entry+0, entry+1)) {
- if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
+ if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
/* We did not touch, they modified -- take theirs */
resolve(info, entry+1, entry+2);
return mask;
diff --git a/builtin/merge.c b/builtin/merge.c
index 3b0f8f9..0ae099f 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -30,6 +30,7 @@
#include "fmt-merge-msg.h"
#include "gpg-interface.h"
#include "sequencer.h"
+#include "string-list.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -64,6 +65,7 @@ static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
static int abort_current_merge;
+static int allow_unrelated_histories;
static int show_progress = -1;
static int default_to_upstream = 1;
static const char *sign_commit;
@@ -210,7 +212,7 @@ static struct option builtin_merge_options[] = {
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY },
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
OPT_BOOL(0, "verify-signatures", &verify_signatures,
- N_("Verify that the named commit has a valid GPG signature")),
+ N_("verify that the named commit has a valid GPG signature")),
OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
N_("merge strategy to use"), option_parse_strategy),
OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
@@ -221,6 +223,8 @@ static struct option builtin_merge_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
+ OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
+ N_("allow merging unrelated histories")),
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
@@ -231,9 +235,9 @@ static struct option builtin_merge_options[] = {
/* Cleans up metadata that is uninteresting after a succeeded merge. */
static void drop_save(void)
{
- unlink(git_path("MERGE_HEAD"));
- unlink(git_path("MERGE_MSG"));
- unlink(git_path("MERGE_MODE"));
+ unlink(git_path_merge_head());
+ unlink(git_path_merge_msg());
+ unlink(git_path_merge_mode());
}
static int save_state(unsigned char *stash)
@@ -333,15 +337,9 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
struct rev_info rev;
struct strbuf out = STRBUF_INIT;
struct commit_list *j;
- const char *filename;
- int fd;
struct pretty_print_context ctx = {0};
printf(_("Squash commit -- not updating HEAD\n"));
- filename = git_path("SQUASH_MSG");
- fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not write to '%s'"), filename);
init_revisions(&rev, NULL);
rev.ignore_merges = 1;
@@ -365,13 +363,10 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
while ((commit = get_revision(&rev)) != NULL) {
strbuf_addch(&out, '\n');
strbuf_addf(&out, "commit %s\n",
- sha1_to_hex(commit->object.sha1));
+ oid_to_hex(&commit->object.oid));
pretty_print_commit(&ctx, commit, &out);
}
- if (write_in_full(fd, out.buf, out.len) != out.len)
- die_errno(_("Writing SQUASH_MSG"));
- if (close(fd))
- die_errno(_("Finishing SQUASH_MSG"));
+ write_file_buf(git_path_squash_msg(), out.buf, out.len);
strbuf_release(&out);
}
@@ -380,7 +375,7 @@ static void finish(struct commit *head_commit,
const unsigned char *new_head, const char *msg)
{
struct strbuf reflog_message = STRBUF_INIT;
- const unsigned char *head = head_commit->object.sha1;
+ const unsigned char *head = head_commit->object.oid.hash;
if (!msg)
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
@@ -404,6 +399,7 @@ static void finish(struct commit *head_commit,
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
+ close_all_packs();
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
@@ -492,40 +488,18 @@ static void merge_name(const char *remote, struct strbuf *msg)
}
if (len) {
struct strbuf truname = STRBUF_INIT;
- strbuf_addstr(&truname, "refs/heads/");
- strbuf_addstr(&truname, remote);
+ strbuf_addf(&truname, "refs/heads/%s", remote);
strbuf_setlen(&truname, truname.len - len);
if (ref_exists(truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
- sha1_to_hex(remote_head->object.sha1),
+ oid_to_hex(&remote_head->object.oid),
truname.buf + 11,
(early ? " (early part)" : ""));
strbuf_release(&truname);
goto cleanup;
}
- }
-
- if (!strcmp(remote, "FETCH_HEAD") &&
- !access(git_path("FETCH_HEAD"), R_OK)) {
- const char *filename;
- FILE *fp;
- struct strbuf line = STRBUF_INIT;
- char *ptr;
-
- filename = git_path("FETCH_HEAD");
- fp = fopen(filename, "r");
- if (!fp)
- die_errno(_("could not open '%s' for reading"),
- filename);
- strbuf_getline(&line, fp, '\n');
- fclose(fp);
- ptr = strstr(line.buf, "\tnot-for-merge\t");
- if (ptr)
- strbuf_remove(&line, ptr-line.buf+1, 13);
- strbuf_addbuf(msg, &line);
- strbuf_release(&line);
- goto cleanup;
+ strbuf_release(&truname);
}
if (remote_head->util) {
@@ -533,7 +507,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
desc = merge_remote_util(remote_head);
if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
strbuf_addf(msg, "%s\t\t%s '%s'\n",
- sha1_to_hex(desc->obj->sha1),
+ oid_to_hex(&desc->obj->oid),
typename(desc->obj->type),
remote);
goto cleanup;
@@ -541,7 +515,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
}
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
- sha1_to_hex(remote_head->object.sha1), remote);
+ oid_to_hex(&remote_head->object.oid), remote);
cleanup:
strbuf_release(&buf);
strbuf_release(&bname);
@@ -700,6 +674,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
hold_locked_index(&lock, 1);
clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
+ if (clean < 0)
+ exit(128);
if (active_cache_changed &&
write_locked_index(&the_index, &lock, COMMIT_LOCK))
die (_("unable to write %s"), get_index_file());
@@ -730,42 +706,17 @@ static int count_unmerged_entries(void)
return ret;
}
-static void split_merge_strategies(const char *string, struct strategy **list,
- int *nr, int *alloc)
-{
- char *p, *q, *buf;
-
- if (!string)
- return;
-
- buf = xstrdup(string);
- q = buf;
- for (;;) {
- p = strchr(q, ' ');
- if (!p) {
- ALLOC_GROW(*list, *nr + 1, *alloc);
- (*list)[(*nr)++].name = xstrdup(q);
- free(buf);
- return;
- } else {
- *p = '\0';
- ALLOC_GROW(*list, *nr + 1, *alloc);
- (*list)[(*nr)++].name = xstrdup(q);
- q = ++p;
- }
- }
-}
-
static void add_strategies(const char *string, unsigned attr)
{
- struct strategy *list = NULL;
- int list_alloc = 0, list_nr = 0, i;
-
- memset(&list, 0, sizeof(list));
- split_merge_strategies(string, &list, &list_nr, &list_alloc);
- if (list) {
- for (i = 0; i < list_nr; i++)
- append_strategy(get_strategy(list[i].name));
+ int i;
+
+ if (string) {
+ struct string_list list = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+ string_list_split(&list, string, ' ', -1);
+ for_each_string_list_item(item, &list)
+ append_strategy(get_strategy(item->string));
+ string_list_clear(&list, 0);
return;
}
for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
@@ -774,21 +725,9 @@ static void add_strategies(const char *string, unsigned attr)
}
-static void write_merge_msg(struct strbuf *msg)
-{
- const char *filename = git_path("MERGE_MSG");
- int fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- filename);
- if (write_in_full(fd, msg->buf, msg->len) != msg->len)
- die_errno(_("Could not write to '%s'"), filename);
- close(fd);
-}
-
static void read_merge_msg(struct strbuf *msg)
{
- const char *filename = git_path("MERGE_MSG");
+ const char *filename = git_path_merge_msg();
strbuf_reset(msg);
if (strbuf_read_file(msg, filename, 0) < 0)
die_errno(_("Could not read from '%s'"), filename);
@@ -819,16 +758,16 @@ static void prepare_to_commit(struct commit_list *remoteheads)
strbuf_addch(&msg, '\n');
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
- write_merge_msg(&msg);
+ 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))
+ git_path_merge_msg(), "merge", NULL))
abort_commit(remoteheads, NULL);
if (0 < option_edit) {
- if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
+ if (launch_editor(git_path_merge_msg(), NULL, NULL))
abort_commit(remoteheads, NULL);
}
read_merge_msg(&msg);
- stripspace(&msg, 0 < option_edit);
+ strbuf_stripspace(&msg, 0 < option_edit);
if (!msg.len)
abort_commit(remoteheads, _("Empty commit message."));
strbuf_release(&merge_msg);
@@ -840,6 +779,14 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
unsigned char result_tree[20], result_commit[20];
struct commit_list *parents, **pptr = &parents;
+ static struct lock_file lock;
+
+ hold_locked_index(&lock, 1);
+ refresh_cache(REFRESH_QUIET);
+ if (active_cache_changed &&
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
+ return error(_("Unable to write index."));
+ rollback_lock_file(&lock);
write_tree_trivial(result_tree);
printf(_("Wonderful.\n"));
@@ -887,7 +834,7 @@ static int suggest_conflicts(void)
FILE *fp;
struct strbuf msgbuf = STRBUF_INIT;
- filename = git_path("MERGE_MSG");
+ filename = git_path_merge_msg();
fp = fopen(filename, "a");
if (!fp)
die_errno(_("Could not open '%s' for writing"), filename);
@@ -914,7 +861,7 @@ static struct commit *is_old_style_invocation(int argc, const char **argv,
second_token = lookup_commit_reference_gently(second_sha1, 0);
if (!second_token)
die(_("'%s' is not a commit"), argv[1]);
- if (hashcmp(second_token->object.sha1, head))
+ if (hashcmp(second_token->object.oid.hash, head))
return NULL;
}
return second_token;
@@ -955,12 +902,12 @@ static int setup_with_upstream(const char ***argv)
if (!branch)
die(_("No current branch."));
- if (!branch->remote)
+ if (!branch->remote_name)
die(_("No remote for the current branch."));
if (!branch->merge_nr)
die(_("No default upstream defined for the current branch."));
- args = xcalloc(branch->merge_nr + 1, sizeof(char *));
+ args = xcalloc(st_add(branch->merge_nr, 1), sizeof(char *));
for (i = 0; i < branch->merge_nr; i++) {
if (!branch->merge[i]->dst)
die(_("No remote-tracking branch for %s from %s"),
@@ -974,41 +921,27 @@ static int setup_with_upstream(const char ***argv)
static void write_merge_state(struct commit_list *remoteheads)
{
- const char *filename;
- int fd;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
for (j = remoteheads; j; j = j->next) {
- unsigned const char *sha1;
+ struct object_id *oid;
struct commit *c = j->item;
if (c->util && merge_remote_util(c)->obj) {
- sha1 = merge_remote_util(c)->obj->sha1;
+ oid = &merge_remote_util(c)->obj->oid;
} else {
- sha1 = c->object.sha1;
+ oid = &c->object.oid;
}
- strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+ strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
}
- filename = git_path("MERGE_HEAD");
- fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"), filename);
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), filename);
- close(fd);
+ write_file_buf(git_path_merge_head(), buf.buf, buf.len);
strbuf_addch(&merge_msg, '\n');
- write_merge_msg(&merge_msg);
+ write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
- filename = git_path("MERGE_MODE");
- fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"), filename);
strbuf_reset(&buf);
if (fast_forward == FF_NO)
strbuf_addf(&buf, "no-ff");
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), filename);
- close(fd);
+ write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
}
static int default_edit_option(void)
@@ -1024,7 +957,7 @@ static int default_edit_option(void)
if (e) {
int v = git_config_maybe_bool(name, e);
if (v < 0)
- die("Bad value '%s' in environment '%s'", e, name);
+ die(_("Bad value '%s' in environment '%s'"), e, name);
return v;
}
@@ -1037,38 +970,144 @@ static int default_edit_option(void)
st_stdin.st_mode == st_stdout.st_mode);
}
+static struct commit_list *reduce_parents(struct commit *head_commit,
+ int *head_subsumed,
+ struct commit_list *remoteheads)
+{
+ struct commit_list *parents, **remotes;
+
+ /*
+ * Is the current HEAD reachable from another commit being
+ * merged? If so we do not want to record it as a parent of
+ * the resulting merge, unless --no-ff is given. We will flip
+ * this variable to 0 when we find HEAD among the independent
+ * tips being merged.
+ */
+ *head_subsumed = 1;
+
+ /* Find what parents to record by checking independent ones. */
+ parents = reduce_heads(remoteheads);
+
+ remoteheads = NULL;
+ remotes = &remoteheads;
+ while (parents) {
+ struct commit *commit = pop_commit(&parents);
+ if (commit == head_commit)
+ *head_subsumed = 0;
+ else
+ remotes = &commit_list_insert(commit, remotes)->next;
+ }
+ return remoteheads;
+}
+
+static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg)
+{
+ struct fmt_merge_msg_opts opts;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.add_title = !have_message;
+ opts.shortlog_len = shortlog_len;
+ opts.credit_people = (0 < option_edit);
+
+ fmt_merge_msg(merge_names, merge_msg, &opts);
+ if (merge_msg->len)
+ strbuf_setlen(merge_msg, merge_msg->len - 1);
+}
+
+static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names)
+{
+ const char *filename;
+ int fd, pos, npos;
+ struct strbuf fetch_head_file = STRBUF_INIT;
+
+ if (!merge_names)
+ merge_names = &fetch_head_file;
+
+ filename = git_path_fetch_head();
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("could not open '%s' for reading"), filename);
+
+ if (strbuf_read(merge_names, fd, 0) < 0)
+ die_errno(_("could not read '%s'"), filename);
+ if (close(fd) < 0)
+ die_errno(_("could not close '%s'"), filename);
+
+ for (pos = 0; pos < merge_names->len; pos = npos) {
+ unsigned char sha1[20];
+ char *ptr;
+ struct commit *commit;
+
+ ptr = strchr(merge_names->buf + pos, '\n');
+ if (ptr)
+ npos = ptr - merge_names->buf + 1;
+ else
+ npos = merge_names->len;
+
+ if (npos - pos < 40 + 2 ||
+ get_sha1_hex(merge_names->buf + pos, sha1))
+ commit = NULL; /* bad */
+ else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
+ continue; /* not-for-merge */
+ else {
+ char saved = merge_names->buf[pos + 40];
+ merge_names->buf[pos + 40] = '\0';
+ commit = get_merge_parent(merge_names->buf + pos);
+ merge_names->buf[pos + 40] = saved;
+ }
+ if (!commit) {
+ if (ptr)
+ *ptr = '\0';
+ die(_("not something we can merge in %s: %s"),
+ filename, merge_names->buf + pos);
+ }
+ remotes = &commit_list_insert(commit, remotes)->next;
+ }
+
+ if (merge_names == &fetch_head_file)
+ strbuf_release(&fetch_head_file);
+}
+
static struct commit_list *collect_parents(struct commit *head_commit,
int *head_subsumed,
- int argc, const char **argv)
+ int argc, const char **argv,
+ struct strbuf *merge_msg)
{
int i;
- struct commit_list *remoteheads = NULL, *parents, *next;
+ struct commit_list *remoteheads = NULL;
struct commit_list **remotes = &remoteheads;
+ struct strbuf merge_names = STRBUF_INIT, *autogen = NULL;
+
+ if (merge_msg && (!have_message || shortlog_len))
+ autogen = &merge_names;
if (head_commit)
remotes = &commit_list_insert(head_commit, remotes)->next;
- for (i = 0; i < argc; i++) {
- struct commit *commit = get_merge_parent(argv[i]);
- if (!commit)
- help_unknown_ref(argv[i], "merge",
- "not something we can merge");
- remotes = &commit_list_insert(commit, remotes)->next;
- }
- *remotes = NULL;
-
- parents = reduce_heads(remoteheads);
- *head_subsumed = 1; /* we will flip this to 0 when we find it */
- for (remoteheads = NULL, remotes = &remoteheads;
- parents;
- parents = next) {
- struct commit *commit = parents->item;
- next = parents->next;
- if (commit == head_commit)
- *head_subsumed = 0;
- else
+ if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) {
+ handle_fetch_head(remotes, autogen);
+ remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+ } else {
+ for (i = 0; i < argc; i++) {
+ struct commit *commit = get_merge_parent(argv[i]);
+ if (!commit)
+ help_unknown_ref(argv[i], "merge",
+ _("not something we can merge"));
remotes = &commit_list_insert(commit, remotes)->next;
+ }
+ remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+ if (autogen) {
+ struct commit_list *p;
+ for (p = remoteheads; p; p = p->next)
+ merge_name(merge_remote_util(p->item)->name, autogen);
+ }
+ }
+
+ if (autogen) {
+ prepare_merge_message(autogen, merge_msg);
+ strbuf_release(autogen);
}
+
return remoteheads;
}
@@ -1080,7 +1119,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, i, ret = 0, head_subsumed;
+ int i, ret = 0, head_subsumed;
int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
@@ -1094,7 +1133,7 @@ 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_sha1, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1102,6 +1141,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+ init_diff_ui_defaults();
git_config(git_merge_config, NULL);
if (branch_mergeoptions)
@@ -1118,7 +1158,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
int nargc = 2;
const char *nargv[] = {"reset", "--merge", NULL};
- if (!file_exists(git_path("MERGE_HEAD")))
+ if (!file_exists(git_path_merge_head()))
die(_("There is no merge to abort (MERGE_HEAD missing)."));
/* Invoke 'git reset --merge' */
@@ -1129,7 +1169,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (read_cache_unmerged())
die_resolve_conflict("merge");
- if (file_exists(git_path("MERGE_HEAD"))) {
+ if (file_exists(git_path_merge_head())) {
/*
* There is no unmerged entry, don't advise 'git
* add/rm <file>', just 'git commit'.
@@ -1140,7 +1180,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
- if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
+ if (file_exists(git_path_cherry_pick_head())) {
if (advice_resolve_conflict)
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you merge."));
@@ -1158,61 +1198,62 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
option_commit = 0;
}
- if (!abort_current_merge) {
- if (!argc) {
- if (default_to_upstream)
- argc = setup_with_upstream(&argv);
- else
- die(_("No commit specified and merge.defaultToUpstream not set."));
- } else if (argc == 1 && !strcmp(argv[0], "-"))
- argv[0] = "@{-1}";
+ if (!argc) {
+ if (default_to_upstream)
+ argc = setup_with_upstream(&argv);
+ else
+ die(_("No commit specified and merge.defaultToUpstream not set."));
+ } else if (argc == 1 && !strcmp(argv[0], "-")) {
+ argv[0] = "@{-1}";
}
+
if (!argc)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
- /*
- * This could be traditional "merge <msg> HEAD <commit>..." and
- * the way we can tell it is to see if the second token is HEAD,
- * but some people might have misused the interface and used a
- * commit-ish that is the same as HEAD there instead.
- * Traditional format never would have "-m" so it is an
- * additional safety measure to check for it.
- */
-
- if (!have_message && head_commit &&
- is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
- strbuf_addstr(&merge_msg, argv[0]);
- head_arg = argv[1];
- argv += 2;