summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c30
-rw-r--r--builtin/am.c478
-rw-r--r--builtin/apply.c4669
-rw-r--r--builtin/archive.c10
-rw-r--r--builtin/blame.c299
-rw-r--r--builtin/branch.c450
-rw-r--r--builtin/bundle.c8
-rw-r--r--builtin/cat-file.c207
-rw-r--r--builtin/check-attr.c88
-rw-r--r--builtin/check-ignore.c20
-rw-r--r--builtin/check-mailmap.c2
-rw-r--r--builtin/check-ref-format.c2
-rw-r--r--builtin/checkout-index.c97
-rw-r--r--builtin/checkout.c160
-rw-r--r--builtin/clean.c62
-rw-r--r--builtin/clone.c272
-rw-r--r--builtin/column.c2
-rw-r--r--builtin/commit-tree.c24
-rw-r--r--builtin/commit.c237
-rw-r--r--builtin/config.c80
-rw-r--r--builtin/count-objects.c16
-rw-r--r--builtin/describe.c165
-rw-r--r--builtin/diff-files.c1
-rw-r--r--builtin/diff-index.c1
-rw-r--r--builtin/diff-tree.c69
-rw-r--r--builtin/diff.c40
-rw-r--r--builtin/difftool.c733
-rw-r--r--builtin/fast-export.c70
-rw-r--r--builtin/fetch-pack.c71
-rw-r--r--builtin/fetch.c314
-rw-r--r--builtin/fmt-merge-msg.c86
-rw-r--r--builtin/for-each-ref.c10
-rw-r--r--builtin/fsck.c261
-rw-r--r--builtin/gc.c85
-rw-r--r--builtin/grep.c575
-rw-r--r--builtin/hash-object.c32
-rw-r--r--builtin/help.c54
-rw-r--r--builtin/index-pack.c140
-rw-r--r--builtin/init-db.c176
-rw-r--r--builtin/interpret-trailers.c15
-rw-r--r--builtin/log.c367
-rw-r--r--builtin/ls-files.c239
-rw-r--r--builtin/ls-remote.c90
-rw-r--r--builtin/ls-tree.c16
-rw-r--r--builtin/mailinfo.c12
-rw-r--r--builtin/mailsplit.c28
-rw-r--r--builtin/merge-base.c31
-rw-r--r--builtin/merge-file.c28
-rw-r--r--builtin/merge-index.c2
-rw-r--r--builtin/merge-recursive.c39
-rw-r--r--builtin/merge-tree.c20
-rw-r--r--builtin/merge.c369
-rw-r--r--builtin/mktree.c25
-rw-r--r--builtin/mv.c89
-rw-r--r--builtin/name-rev.c80
-rw-r--r--builtin/notes.c157
-rw-r--r--builtin/pack-objects.c487
-rw-r--r--builtin/pack-redundant.c2
-rw-r--r--builtin/patch-id.c23
-rw-r--r--builtin/prune-packed.c4
-rw-r--r--builtin/prune.c8
-rw-r--r--builtin/pull.c151
-rw-r--r--builtin/push.c107
-rw-r--r--builtin/read-tree.c69
-rw-r--r--builtin/rebase--helper.c40
-rw-r--r--builtin/receive-pack.c459
-rw-r--r--builtin/reflog.c19
-rw-r--r--builtin/remote-ext.c32
-rw-r--r--builtin/remote.c137
-rw-r--r--builtin/repack.c23
-rw-r--r--builtin/replace.c113
-rw-r--r--builtin/reset.c64
-rw-r--r--builtin/rev-list.c111
-rw-r--r--builtin/rev-parse.c278
-rw-r--r--builtin/revert.c80
-rw-r--r--builtin/rm.c109
-rw-r--r--builtin/send-pack.c9
-rw-r--r--builtin/shortlog.c215
-rw-r--r--builtin/show-branch.c78
-rw-r--r--builtin/show-ref.c49
-rw-r--r--builtin/stripspace.c6
-rw-r--r--builtin/submodule--helper.c1058
-rw-r--r--builtin/symbolic-ref.c6
-rw-r--r--builtin/tag.c152
-rw-r--r--builtin/unpack-objects.c11
-rw-r--r--builtin/update-index.c167
-rw-r--r--builtin/update-ref.c2
-rw-r--r--builtin/upload-archive.c9
-rw-r--r--builtin/verify-tag.c79
-rw-r--r--builtin/worktree.c154
90 files changed, 7617 insertions, 8397 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 145f06e..9f53f02 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -30,6 +30,21 @@ struct update_callback_data {
int add_errors;
};
+static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
+{
+ int i;
+
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+
+ if (pathspec && !ce_path_match(ce, pathspec, NULL))
+ continue;
+
+ if (chmod_cache_entry(ce, force_mode) < 0)
+ fprintf(stderr, "cannot chmod '%s'", ce->name);
+ }
+}
+
static int fix_unmerged_status(struct diff_filepair *p,
struct update_callback_data *data)
{
@@ -65,7 +80,7 @@ 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)) {
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
die(_("updating files failed"));
data->add_errors++;
@@ -238,6 +253,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 +280,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(),
};
@@ -289,7 +307,7 @@ 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)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
@@ -336,10 +354,14 @@ 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 (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
+ chmod_arg[1] != 'x' || chmod_arg[2]))
+ die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
+
add_new_files = !take_worktree_changes && !refresh_only;
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
- hold_locked_index(&lock_file, 1);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
(show_only ? ADD_CACHE_PRETEND : 0) |
@@ -431,6 +453,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (add_new_files)
exit_status |= add_files(&dir, flags);
+ if (chmod_arg && pathspec.nr)
+ chmod_pathspec(&pathspec, chmod_arg[0]);
unplug_bulk_checkin();
finish:
diff --git a/builtin/am.c b/builtin/am.c
index 9fb42fd..f7a7a97 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -28,6 +28,8 @@
#include "rerere.h"
#include "prompt.h"
#include "mailinfo.h"
+#include "apply.h"
+#include "string-list.h"
/**
* Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -46,21 +48,6 @@ static int is_empty_file(const char *filename)
}
/**
- * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators.
- */
-static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp)
-{
- if (strbuf_getwholeline(sb, fp, '\n'))
- return EOF;
- if (sb->buf[sb->len - 1] == '\n') {
- strbuf_setlen(sb, sb->len - 1);
- if (sb->len > 0 && sb->buf[sb->len - 1] == '\r')
- strbuf_setlen(sb, sb->len - 1);
- }
- return 0;
-}
-
-/**
* Returns the length of the first line of msg.
*/
static int linelen(const char *msg)
@@ -85,7 +72,8 @@ enum patch_format {
PATCH_FORMAT_MBOX,
PATCH_FORMAT_STGIT,
PATCH_FORMAT_STGIT_SERIES,
- PATCH_FORMAT_HG
+ PATCH_FORMAT_HG,
+ PATCH_FORMAT_MBOXRD
};
enum keep_type {
@@ -122,7 +110,7 @@ struct am_state {
size_t msg_len;
/* when --rebasing, records the original commit the patch came from */
- unsigned char orig_commit[GIT_SHA1_RAWSZ];
+ struct object_id orig_commit;
/* number of digits in patch filename */
int prec;
@@ -198,22 +186,22 @@ static inline const char *am_path(const struct am_state *state, const char *path
/**
* For convenience to call write_file()
*/
-static int write_state_text(const struct am_state *state,
- const char *name, const char *string)
+static void write_state_text(const struct am_state *state,
+ const char *name, const char *string)
{
- return write_file(am_path(state, name), "%s", string);
+ write_file(am_path(state, name), "%s", string);
}
-static int write_state_count(const struct am_state *state,
- const char *name, int value)
+static void write_state_count(const struct am_state *state,
+ const char *name, int value)
{
- return write_file(am_path(state, name), "%d", value);
+ write_file(am_path(state, name), "%d", value);
}
-static int write_state_bool(const struct am_state *state,
- const char *name, int value)
+static void write_state_bool(const struct am_state *state,
+ const char *name, int value)
{
- return write_state_text(state, name, value ? "t" : "f");
+ write_state_text(state, name, value ? "t" : "f");
}
/**
@@ -272,38 +260,29 @@ static int read_state_file(struct strbuf *sb, const struct am_state *state,
}
/**
- * 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.
+ * Take a series of KEY='VALUE' lines where VALUE part is
+ * sq-quoted, and append <KEY, VALUE> at the end of the string list
*/
-static char *read_shell_var(FILE *fp, const char *key)
-{
- struct strbuf sb = STRBUF_INIT;
- const char *str;
-
- if (strbuf_getline(&sb, fp, '\n'))
- 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;
+static int parse_key_value_squoted(char *buf, struct string_list *list)
+{
+ while (*buf) {
+ struct string_list_item *item;
+ char *np;
+ char *cp = strchr(buf, '=');
+ if (!cp)
+ return -1;
+ np = strchrnul(cp, '\n');
+ *cp++ = '\0';
+ item = string_list_append(list, buf);
+
+ buf = np + (*np == '\n');
+ *np = '\0';
+ cp = sq_dequote(cp);
+ if (!cp)
+ return -1;
+ item->util = xstrdup(cp);
+ }
+ return 0;
}
/**
@@ -325,44 +304,39 @@ fail:
static int read_author_script(struct am_state *state)
{
const char *filename = am_path(state, "author-script");
- FILE *fp;
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list kv = STRING_LIST_INIT_DUP;
+ int retval = -1; /* assume failure */
+ int fd;
assert(!state->author_name);
assert(!state->author_email);
assert(!state->author_date);
- fp = fopen(filename, "r");
- if (!fp) {
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
if (errno == ENOENT)
return 0;
die_errno(_("could not open '%s' for reading"), filename);
}
+ strbuf_read(&buf, fd, 0);
+ close(fd);
+ if (parse_key_value_squoted(buf.buf, &kv))
+ goto finish;
- 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;
+ if (kv.nr != 3 ||
+ strcmp(kv.items[0].string, "GIT_AUTHOR_NAME") ||
+ strcmp(kv.items[1].string, "GIT_AUTHOR_EMAIL") ||
+ strcmp(kv.items[2].string, "GIT_AUTHOR_DATE"))
+ goto finish;
+ state->author_name = kv.items[0].util;
+ state->author_email = kv.items[1].util;
+ state->author_date = kv.items[2].util;
+ retval = 0;
+finish:
+ string_list_clear(&kv, !!retval);
+ strbuf_release(&buf);
+ return retval;
}
/**
@@ -417,13 +391,8 @@ static int read_commit_msg(struct am_state *state)
*/
static void write_commit_msg(const struct am_state *state)
{
- int fd;
const char *filename = am_path(state, "final-commit");
-
- fd = xopen(filename, O_WRONLY | O_CREAT, 0666);
- if (write_in_full(fd, state->msg, state->msg_len) < 0)
- die_errno(_("could not write to %s"), filename);
- close(fd);
+ write_file_buf(filename, state->msg, state->msg_len);
}
/**
@@ -447,8 +416,8 @@ static void am_load(struct am_state *state)
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)
+ oidclr(&state->orig_commit);
+ else if (get_oid_hex(sb.buf, &state->orig_commit) < 0)
die(_("could not parse %s"), am_path(state, "original-commit"));
read_state_file(&sb, state, "threeway", 1);
@@ -573,15 +542,15 @@ static int copy_notes_for_rebase(const struct am_state *state)
fp = xfopen(am_path(state, "rewritten"), "r");
- while (!strbuf_getline(&sb, fp, '\n')) {
- unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ];
+ while (!strbuf_getline_lf(&sb, fp)) {
+ struct object_id from_obj, to_obj;
if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
ret = error(invalid_line, sb.buf);
goto finish;
}
- if (get_sha1_hex(sb.buf, from_obj)) {
+ if (get_oid_hex(sb.buf, &from_obj)) {
ret = error(invalid_line, sb.buf);
goto finish;
}
@@ -591,14 +560,14 @@ static int copy_notes_for_rebase(const struct am_state *state)
goto finish;
}
- if (get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ + 1, to_obj)) {
+ if (get_oid_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))
+ if (copy_note_for_rewrite(c, from_obj.hash, to_obj.hash))
ret = error(_("Failed to copy notes from '%s' to '%s'"),
- sha1_to_hex(from_obj), sha1_to_hex(to_obj));
+ oid_to_hex(&from_obj), oid_to_hex(&to_obj));
}
finish:
@@ -628,7 +597,7 @@ static int is_mail(FILE *fp)
if (regcomp(&regex, header_regex, REG_NOSUB | REG_EXTENDED))
die("invalid pattern: %s", header_regex);
- while (!strbuf_getline_crlf(&sb, fp)) {
+ while (!strbuf_getline(&sb, fp)) {
if (!sb.len)
break; /* End of header */
@@ -675,7 +644,7 @@ static int detect_patch_format(const char **paths)
fp = xfopen(*paths, "r");
- while (!strbuf_getline_crlf(&l1, fp)) {
+ while (!strbuf_getline(&l1, fp)) {
if (l1.len)
break;
}
@@ -696,9 +665,9 @@ static int detect_patch_format(const char **paths)
}
strbuf_reset(&l2);
- strbuf_getline_crlf(&l2, fp);
+ strbuf_getline(&l2, fp);
strbuf_reset(&l3);
- strbuf_getline_crlf(&l3, fp);
+ strbuf_getline(&l3, fp);
/*
* If the second line is empty and the third is a From, Author or Date
@@ -727,7 +696,8 @@ done:
* 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)
+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;
@@ -739,6 +709,8 @@ static int split_mail_mbox(struct am_state *state, const char **paths, int keep_
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);
@@ -784,15 +756,15 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
in = fopen(*paths, "r");
if (!in)
- return error(_("could not open '%s' for reading: %s"),
- *paths, strerror(errno));
+ 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(_("could not open '%s' for writing: %s"),
- mail, strerror(errno));
+ return error_errno(_("could not open '%s' for writing"),
+ mail);
ret = fn(out, in, keep_cr);
@@ -817,7 +789,7 @@ 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(&sb, in, '\n')) {
+ while (!strbuf_getline_lf(&sb, in)) {
const char *str;
if (str_isspace(sb.buf))
@@ -872,10 +844,9 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
fp = fopen(*paths, "r");
if (!fp)
- return error(_("could not open '%s' for reading: %s"), *paths,
- strerror(errno));
+ return error_errno(_("could not open '%s' for reading"), *paths);
- while (!strbuf_getline(&sb, fp, '\n')) {
+ while (!strbuf_getline_lf(&sb, fp)) {
if (*sb.buf == '#')
continue; /* skip comment lines */
@@ -900,7 +871,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
{
struct strbuf sb = STRBUF_INIT;
- while (!strbuf_getline(&sb, in, '\n')) {
+ while (!strbuf_getline_lf(&sb, in)) {
const char *str;
if (skip_prefix(sb.buf, "# User ", &str))
@@ -981,13 +952,15 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
switch (patch_format) {
case PATCH_FORMAT_MBOX:
- return split_mail_mbox(state, paths, keep_cr);
+ 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");
}
@@ -1000,7 +973,7 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
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];
+ struct object_id curr_head;
const char *str;
struct strbuf sb = STRBUF_INIT;
@@ -1068,15 +1041,15 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
else
write_state_text(state, "applying", "");
- if (!get_sha1("HEAD", curr_head)) {
- write_state_text(state, "abort-safety", sha1_to_hex(curr_head));
+ if (!get_oid("HEAD", &curr_head)) {
+ write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
if (!state->rebasing)
- update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
+ update_ref_oid("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);
+ delete_ref(NULL, "ORIG_HEAD", NULL, 0);
}
/*
@@ -1096,7 +1069,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
*/
static void am_next(struct am_state *state)
{
- unsigned char head[GIT_SHA1_RAWSZ];
+ struct object_id head;
free(state->author_name);
state->author_name = NULL;
@@ -1114,11 +1087,11 @@ static void am_next(struct am_state *state)
unlink(am_path(state, "author-script"));
unlink(am_path(state, "final-commit"));
- hashclr(state->orig_commit);
+ oidclr(&state->orig_commit);
unlink(am_path(state, "original-commit"));
- if (!get_sha1("HEAD", head))
- write_state_text(state, "abort-safety", sha1_to_hex(head));
+ if (!get_oid("HEAD", &head))
+ write_state_text(state, "abort-safety", oid_to_hex(&head));
else
write_state_text(state, "abort-safety", "");
@@ -1146,7 +1119,7 @@ static void refresh_and_write_cache(void)
{
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
+ hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write index file"));
@@ -1160,17 +1133,17 @@ static void refresh_and_write_cache(void)
*/
static int index_has_changes(struct strbuf *sb)
{
- unsigned char head[GIT_SHA1_RAWSZ];
+ struct object_id head;
int i;
- if (!get_sha1_tree("HEAD", head)) {
+ if (!get_sha1_tree("HEAD", head.hash)) {
struct diff_options opt;
diff_setup(&opt);
DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
if (!sb)
DIFF_OPT_SET(&opt, QUICK);
- do_diff_cache(head, &opt);
+ do_diff_cache(head.hash, &opt);
diffcore_std(&opt);
for (i = 0; sb && i < diff_queued_diff.nr; i++) {
if (i)
@@ -1317,7 +1290,7 @@ static int parse_mail(struct am_state *state, const char *mail)
/* Extract message and author information */
fp = xfopen(am_path(state, "info"), "r");
- while (!strbuf_getline(&sb, fp, '\n')) {
+ while (!strbuf_getline_lf(&sb, fp)) {
const char *x;
if (skip_prefix(sb.buf, "Subject: ", &x)) {
@@ -1377,19 +1350,19 @@ finish:
* 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)
+static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)
{
struct strbuf sb = STRBUF_INIT;
FILE *fp = xfopen(mail, "r");
const char *x;
- if (strbuf_getline(&sb, fp, '\n'))
+ 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)
+ if (get_oid_hex(x, commit_id) < 0)
return -1;
strbuf_release(&sb);
@@ -1479,12 +1452,12 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
static void write_index_patch(const struct am_state *state)
{
struct tree *tree;
- unsigned char head[GIT_SHA1_RAWSZ];
+ struct object_id head;
struct rev_info rev_info;
FILE *fp;
- if (!get_sha1_tree("HEAD", head))
- tree = lookup_tree(head);
+ if (!get_sha1_tree("HEAD", head.hash))
+ tree = lookup_tree(head.hash);
else
tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
@@ -1514,19 +1487,19 @@ static void write_index_patch(const struct am_state *state)
static int parse_mail_rebase(struct am_state *state, const char *mail)
{
struct commit *commit;
- unsigned char commit_sha1[GIT_SHA1_RAWSZ];
+ struct object_id commit_oid;
- if (get_mail_commit_sha1(commit_sha1, mail) < 0)
+ if (get_mail_commit_oid(&commit_oid, mail) < 0)
die(_("could not parse %s"), mail);
- commit = lookup_commit_or_die(commit_sha1, mail);
+ commit = lookup_commit_or_die(commit_oid.hash, 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));
+ oidcpy(&state->orig_commit, &commit_oid);
+ write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
return 0;
}
@@ -1537,39 +1510,59 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
*/
static int run_apply(const struct am_state *state, const char *index_file)
{
- struct child_process cp = CHILD_PROCESS_INIT;
+ struct argv_array apply_paths = ARGV_ARRAY_INIT;
+ struct argv_array apply_opts = ARGV_ARRAY_INIT;
+ struct apply_state apply_state;
+ int res, opts_left;
+ static struct lock_file lock_file;
+ int force_apply = 0;
+ int options = 0;
- cp.git_cmd = 1;
+ if (init_apply_state(&apply_state, NULL, &lock_file))
+ die("BUG: init_apply_state() failed");
+
+ argv_array_push(&apply_opts, "apply");
+ argv_array_pushv(&apply_opts, state->git_apply_opts.argv);
+
+ opts_left = apply_parse_options(apply_opts.argc, apply_opts.argv,
+ &apply_state, &force_apply, &options,
+ NULL);
- if (index_file)
- argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
+ if (opts_left != 0)
+ die("unknown option passed through to git apply");
+
+ if (index_file) {
+ apply_state.index_file = index_file;
+ apply_state.cached = 1;
+ } else
+ apply_state.check_index = 1;
/*
* 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;
- }
+ if (state->threeway && !index_file)
+ apply_state.apply_verbosity = verbosity_silent;
- argv_array_push(&cp.args, "apply");
+ if (check_apply_state(&apply_state, force_apply))
+ die("BUG: check_apply_state() failed");
- argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+ argv_array_push(&apply_paths, am_path(state, "patch"));
- if (index_file)
- argv_array_push(&cp.args, "--cached");
- else
- argv_array_push(&cp.args, "--index");
+ res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options);
- argv_array_push(&cp.args, am_path(state, "patch"));
+ argv_array_clear(&apply_paths);
+ argv_array_clear(&apply_opts);
+ clear_apply_state(&apply_state);
- if (run_command(&cp))
- return -1;
+ if (res)
+ return res;
- /* Reload index as git-apply will have modified it. */
- discard_cache();
- read_cache_from(index_file ? index_file : get_index_file());
+ if (index_file) {
+ /* Reload index as apply_all_patches() will have modified it. */
+ discard_cache();
+ read_cache_from(index_file);
+ }
return 0;
}
@@ -1594,47 +1587,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
}
/**
- * Do the three-way merge using fake ancestor, his tree constructed
- * from the fake ancestor and the postimage of the patch, and our
- * state.
- */
-static int run_fallback_merge_recursive(const struct am_state *state,
- unsigned char *orig_tree,
- unsigned char *our_tree,
- unsigned char *his_tree)
-{
- struct child_process cp = CHILD_PROCESS_INIT;
- int status;
-
- cp.git_cmd = 1;
-
- argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
- sha1_to_hex(his_tree), linelen(state->msg), state->msg);
- if (state->quiet)
- argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
-
- argv_array_push(&cp.args, "merge-recursive");
- argv_array_push(&cp.args, sha1_to_hex(orig_tree));
- argv_array_push(&cp.args, "--");
- argv_array_push(&cp.args, sha1_to_hex(our_tree));
- argv_array_push(&cp.args, sha1_to_hex(his_tree));
-
- status = run_command(&cp) ? (-1) : 0;
- discard_cache();
- read_cache();
- return status;
-}
-
-/**
* 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)
{
- unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
- our_tree[GIT_SHA1_RAWSZ];
+ 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_sha1("HEAD", our_tree) < 0)
- hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+ 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");
@@ -1642,7 +1606,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
discard_cache();
read_cache_from(index_path);
- if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
+ 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..."));
@@ -1657,8 +1621,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
init_revisions(&rev_info, NULL);
rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
- diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1);
- add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
+ 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);
}
@@ -1667,7 +1631,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
return error(_("Did you hand edit your patch?\n"
"It does not apply to blobs recorded in its index."));
- if (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))
+ 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..."));
@@ -1677,17 +1641,28 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
/*
* This is not so wrong. Depending on which base we picked, orig_tree
- * may be wildly different from ours, but his_tree has the same set of
+ * 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.
*/
- if (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {
+ 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;
}
@@ -1698,9 +1673,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
*/
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 object_id tree, parent, commit;
+ const struct object_id *old_oid;
struct commit_list *parents = NULL;
const char *reflog_msg, *author;
struct strbuf sb = STRBUF_INIT;
@@ -1708,14 +1682,14 @@ static void do_commit(const struct am_state *state)
if (run_hook_le(NULL, "pre-applypatch", NULL))
exit(1);
- if (write_cache_as_tree(tree, 0, NULL))
+ if (write_cache_as_tree(tree.hash, 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);
+ if (!get_sha1_commit("HEAD", parent.hash)) {
+ old_oid = &parent;
+ commit_list_insert(lookup_commit(parent.hash), &parents);
} else {
- ptr = NULL;
+ old_oid = NULL;
say(state, stderr, _("applying to an empty history"));
}
@@ -1727,7 +1701,7 @@ static void do_commit(const struct am_state *state)
setenv("GIT_COMMITTER_DATE",
state->ignore_date ? "" : state->author_date, 1);
- if (commit_tree(state->msg, state->msg_len, tree, parents, commit,
+ if (commit_tree(state->msg, state->msg_len, tree.hash, parents, commit.hash,
author, state->sign_commit))
die(_("failed to write commit object"));
@@ -1738,14 +1712,15 @@ static void do_commit(const struct am_state *state)
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);
+ update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 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));
+ assert(!is_null_oid(&state->orig_commit));
+ fprintf(fp, "%s ", oid_to_hex(&state->orig_commit));
+ fprintf(fp, "%s\n", oid_to_hex(&commit));
fclose(fp);
}
@@ -1821,7 +1796,7 @@ static int do_interactive(struct am_state *state)
if (!pager)
pager = "cat";
- argv_array_push(&cp.args, pager);
+ prepare_pager_args(&cp, pager);
argv_array_push(&cp.args, am_path(state, "patch"));
run_command(&cp);
}
@@ -1855,6 +1830,8 @@ static void am_run(struct am_state *state, int resume)
const char *mail = am_path(state, msgnum(state));
int apply_status;
+ reset_ident_date();
+
if (!file_exists(mail))
goto next;
@@ -1939,6 +1916,7 @@ next:
*/
if (!state->rebasing) {
am_destroy(state);
+ close_all_packs();
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
@@ -1998,7 +1976,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
return -1;
lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
+ hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
@@ -2038,7 +2016,7 @@ static int merge_tree(struct tree *tree)
return -1;
lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
+ hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
@@ -2063,30 +2041,30 @@ static int merge_tree(struct tree *tree)
* 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)
+static int clean_index(const struct object_id *head, const struct object_id *remote)
{
struct tree *head_tree, *remote_tree, *index_tree;
- unsigned char index[GIT_SHA1_RAWSZ];
+ struct object_id index;
- head_tree = parse_tree_indirect(head);
+ head_tree = parse_tree_indirect(head->hash);
if (!head_tree)
- return error(_("Could not parse object '%s'."), sha1_to_hex(head));
+ return error(_("Could not parse object '%s'."), oid_to_hex(head));
- remote_tree = parse_tree_indirect(remote);
+ remote_tree = parse_tree_indirect(remote->hash);
if (!remote_tree)
- return error(_("Could not parse object '%s'."), sha1_to_hex(remote));
+ return error(_("Could not parse object '%s'."), oid_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))
+ if (write_cache_as_tree(index.hash, 0, NULL))
return -1;
- index_tree = parse_tree_indirect(index);
+ index_tree = parse_tree_indirect(index.hash);
if (!index_tree)
- return error(_("Could not parse object '%s'."), sha1_to_hex(index));
+ return error(_("Could not parse object '%s'."), oid_to_hex(&index));
if (fast_forward_to(index_tree, remote_tree, 0))
return -1;
@@ -2114,14 +2092,14 @@ static void am_rerere_clear(void)
*/
static void am_skip(struct am_state *state)
{
- unsigned char head[GIT_SHA1_RAWSZ];
+ struct object_id head;
am_rerere_clear();
- if (get_sha1("HEAD", head))
- hashcpy(head, EMPTY_TREE_SHA1_BIN);
+ if (get_oid("HEAD", &head))
+ hashcpy(head.hash, EMPTY_TREE_SHA1_BIN);
- if (clean_index(head, head))
+ if (clean_index(&head, &head))
die(_("failed to clean index"));
am_next(state);
@@ -2139,24 +2117,24 @@ static void am_skip(struct am_state *state)
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];
+ struct object_id abort_safety, head;
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"));
+ if (get_oid_hex(sb.buf, &abort_safety))
+ die(_("could not parse %s"), am_path(state, "abort-safety"));
} else
- hashclr(abort_safety);
+ oidclr(&abort_safety);
- if (get_sha1("HEAD", head))
- hashclr(head);
+ if (get_oid("HEAD", &head))
+ oidclr(&head);
- if (!hashcmp(head, abort_safety))
+ if (!oidcmp(&head, &abort_safety))
return 1;
- error(_("You seem to have moved HEAD since the last 'am' failure.\n"
+ warning(_("You seem to have moved HEAD since the last 'am' failure.\n"
"Not rewinding to ORIG_HEAD"));
return 0;
@@ -2167,7 +2145,7 @@ static int safe_to_abort(const struct am_state *state)
*/
static void am_abort(struct am_state *state)
{
- unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
+ struct object_id curr_head, orig_head;
int has_curr_head, has_orig_head;
char *curr_branch;
@@ -2178,23 +2156,23 @@ static void am_abort(struct am_state *state)
am_rerere_clear();
- curr_branch = resolve_refdup("HEAD", 0, curr_head, NULL);
- has_curr_head = !is_null_sha1(curr_head);
+ curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
+ has_curr_head = !is_null_oid(&curr_head);
if (!has_curr_head)
- hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
+ hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
- has_orig_head = !get_sha1("ORIG_HEAD", orig_head);
+ has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
if (!has_orig_head)
- hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
+ hashcpy(orig_head.hash, EMPTY_TREE_SHA1_BIN);
- clean_index(curr_head, orig_head);
+ 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_ref_oid("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);
+ delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
free(curr_branch);
am_destroy(state);
@@ -2216,6 +2194,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
*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;
@@ -2250,7 +2230,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
int in_progress;
const char * const usage[] = {
- N_("git am [<options>] [(<mbox>|<Maildir>)...]"),
+ N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
N_("git am [<options>] (--continue | --skip | --abort)"),
NULL
};
diff --git a/builtin/apply.c b/builtin/apply.c
index deb1364..81b9a61 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,4673 +1,36 @@
-/*
- * apply.c
- *
- * Copyright (C) Linus Torvalds, 2005
- *
- * This applies patches on top of some (arbitrary) version of the SCM.
- *
- */
#include "cache.h"
-#include "lockfile.h"
-#include "cache-tree.h"
-#include "quote.h"
-#include "blob.h"
-#include "delta.h"
#include "builtin.h"
-#include "string-list.h"
-#include "dir.h"
-#include "diff.h"
#include "parse-options.h"
-#include "xdiff-interface.h"
-#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;
+#include "lockfile.h"
+#include "apply.h"
-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 {
- 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 {
- ignore_ws_none,
- ignore_ws_change
-} ws_ignore_action = ignore_ws_none;
-
-
-static const char *patch_input_file;
-static struct strbuf root = STRBUF_INIT;
-static int read_stdin = 1;
-static int options;
-
-static void parse_whitespace_option(const char *option)
-{
- if (!option) {
- ws_error_action = warn_on_ws_error;
- return;
- }
- if (!strcmp(option, "warn")) {
- ws_error_action = warn_on_ws_error;
- return;
- }
- if (!strcmp(option, "nowarn")) {
- ws_error_action = nowarn_ws_error;
- return;
- }
- if (!strcmp(option, "error")) {
- ws_error_action = die_on_ws_error;
- return;
- }
- if (!strcmp(option, "error-all")) {
- ws_error_action = die_on_ws_error;
- squelch_whitespace_errors = 0;
- return;
- }
- if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
- ws_error_action = correct_ws_error;
- return;
- }
- die(_("unrecognized whitespace option '%s'"), option);
-}
-
-static void parse_ignorewhitespace_option(const char *option)
-{
- if (!option || !strcmp(option, "no") ||
- !strcmp(option, "false") || !strcmp(option, "never") ||
- !strcmp(option, "none")) {
- ws_ignore_action = ignore_ws_none;
- return;
- }
- if (!strcmp(option, "change")) {
- ws_ignore_action = ignore_ws_change;
- return;
- }
- die(_("unrecognized whitespace ignore option '%s'"), option);
-}
-
-static void set_default_whitespace_mode(const char *whitespace_option)
-{
- if (!whitespace_option && !apply_default_whitespace)
- ws_error_action = (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
- * is stored in size. leading and trailing are the number
- * of context lines.
- */
-struct fragment {
- unsigned long leading, trailing;
- unsigned long oldpos, oldlines;
- unsigned long newpos, newlines;
- /*
- * 'patch' is usually borrowed from buf in apply_patch(),
- * but some codepaths store an allocated buffer.
- */
- const char *patch;
- unsigned free_patch:1,
- rejected:1;
- int size;
- int linenr;
- struct fragment *next;
-};
-
-/*
- * When dealing with a binary patch, we reuse "leading" field
- * to store the type of the binary hunk, either deflated "delta"
- * or deflated "literal".
- */
-#define binary_patch_method leading
-#define BINARY_DELTA_DEFLATED 1
-#define BINARY_LITERAL_DEFLATED 2
-
-/*
- * This represents a "patch" to a file, both metainfo changes
- * such as creation/deletion, filemode and content changes represented
- * as a series of fragments.
- */
-struct patch {
- char *new_name, *old_name, *def_name;
- unsigned int old_mode, new_mode;
- int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */
- int rejected;
- unsigned ws_rule;
- int lines_added, lines_deleted;
- int score;
- unsigned int is_toplevel_relative:1;
- unsigned int inaccurate_eof:1;
- unsigned int is_binary:1;
- unsigned int is_copy:1;
- unsigned int is_rename:1;
- unsigned int recount:1;
- unsigned int conflicted_threeway:1;
- unsigned int direct_to_threeway:1;
- struct fragment *fragments;
- char *result;
- size_t resultsize;
- char old_sha1_prefix[41];
- char new_sha1_prefix[41];
- struct patch *next;
-
- /* three-way fallback result */
- struct object_id threeway_stage[3];
-};
-
-static void free_fragment_list(struct fragment *list)
-{
- while (list) {
- struct fragment *next = list->next;
- if (list->free_patch)
- free((char *)list->patch);
- free(list);
- list = next;
- }
-}
-
-static void free_patch(struct patch *patch)
-{
- free_fragment_list(patch->fragments);
- free(patch->def_name);
- free(patch->old_name);
- free(patch->new_name);
- free(patch->result);
- free(patch);
-}
-
-static void free_patch_list(struct patch *list)
-{
- while (list) {
- struct patch *next = list->next;
- free_patch(list);
- list = next;
- }
-}
-
-/*
- * A line in a file, len-bytes long (includes the terminating LF,
- * except for an incomplete line at the end if the file ends with
- * one), and its contents hashes to 'hash'.
- */
-struct line {
- size_t len;
- unsigned hash : 24;
- unsigned flag : 8;
-#define LINE_COMMON 1
-#define LINE_PATCHED 2
-};
-
-/*
- * This represents a "file", which is an array of "lines".
- */
-struct image {
- char *buf;
- size_t len;
- size_t nr;
- size_t alloc;
- struct line *line_allocated;
- 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;
- uint32_t h;
- for (i = 0, h = 0; i < len; i++) {
- if (!isspace(cp[i])) {
- h = h * 3 + (cp[i] & 0xff);
- }
- }
- return h;
-}
-
-/*
- * Compare lines s1 of length n1 and s2 of length n2, ignoring
- * whitespace difference. Returns 1 if they match, 0 otherwise
- */
-static int fuzzy_matchlines(const char *s1, size_t n1,
- const char *s2, size_t n2)
-{
- const char *last1 = s1 + n1 - 1;
- const char *last2 = s2 + n2 - 1;
- int result = 0;
-
- /* ignore line endings */
- while ((*last1 == '\r') || (*last1 == '\n'))
- last1--;
- while ((*last2 == '\r') || (*last2 == '\n'))
- last2--;
-
- /* skip leading whitespaces, if both begin with whitespace */
- if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) {
- while (isspace(*s1) && (s1 <= last1))
- s1++;
- while (isspace(*s2) && (s2 <= last2))
- s2++;
- }
- /* early return if both lines are empty */
- if ((s1 > last1) && (s2 > last2))
- return 1;
- while (!result) {
- result = *s1++ - *s2++;
- /*
- * Skip whitespace inside. We check for whitespace on
- * both buffers because we don't want "a b" to match
- * "ab"
- */
- if (isspace(*s1) && isspace(*s2)) {
- while (isspace(*s1) && s1 <= last1)
- s1++;
- while (isspace(*s2) && s2 <= last2)
- s2++;
- }
- /*
- * If we reached the end on one side only,
- * lines don't match
- */
- if (
- ((s2 > last2) && (s1 <= last1)) ||
- ((s1 > last1) && (s2 <= last2)))
- return 0;
- if ((s1 > last1) && (s2 > last2))
- break;
- }
-
- return !result;
-}
-
-static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
-{
- ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
- img->line_allocated[img->nr].len = len;
- img->line_allocated[img->nr].hash = hash_line(bol, len);
- img->line_allocated[img->nr].flag = flag;
- img->nr++;
-}
-
-/*
- * "buf" has the file contents to be patched (read from various sources).
- * attach it to "image" and add line-based index to it.
- * "image" now owns the "buf".
- */
-static void prepare_image(struct image *image, char *buf, size_t len,
- int prepare_linetable)
-{
- const char *cp, *ep;
-
- memset(image, 0, sizeof(*image));
- image->buf = buf;
- image->len = len;
-
- if (!prepare_linetable)
- return;
-
- ep = image->buf + image->len;
- cp = image->buf;
- while (cp < ep) {
- const char *next;
- for (next = cp; next < ep && *next != '\n'; next++)
- ;
- if (next < ep)
- next++;
- add_line_info(image, cp, next - cp, 0);
- cp = next;
- }
- image->line = image->line_allocated;
-}
-
-static void clear_image(struct image *image)
-{
- free(image->buf);
- free(image->line_allocated);
- memset(image, 0, sizeof(*image));
-}
-
-/* fmt must contain _one_ %s and no other substitution */
-static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
-{
- struct strbuf sb = STRBUF_INIT;
-
- if (patch->old_name && patch->new_name &&
- strcmp(patch->old_name, patch->new_name)) {
- quote_c_style(patch->old_name, &sb, NULL, 0);
- strbuf_addstr(&sb, " => ");
- quote_c_style(patch->new_name, &sb, NULL, 0);
- } else {
- const char *n = patch->new_name;
- if (!n)
- n = patch->old_name;
- quote_c_style(n, &sb, NULL, 0);
- }
- fprintf(output, fmt, sb.buf);
- fputc('\n', output);
- strbuf_release(&sb);
-}
-
-#define SLOP (16)
-
-static void read_patch_file(struct strbuf *sb, int fd)
-{
- if (strbuf_read(sb, fd, 0) < 0)
- die_errno("git apply: failed to read");
-
- /*
- * Make sure that we have some slop in the buffer
- * so that we can do speculative "memcmp" etc, and
- * see to it that it is NUL-filled.
- */
- strbuf_grow(sb, SLOP);
- memset(sb->buf + sb->len, 0, SLOP);
-}
-
-static unsigned long linelen(const char *buffer, unsigned long size)
-{
- unsigned long len = 0;
- while (size--) {
- len++;
- if (*buffer++ == '\n')
- break;
- }
- return len;
-}
-
-static int is_dev_null(const char *str)
-{
- return skip_prefix(str, "/dev/null", &str) && isspace(*str);
-}
-
-#define TERM_SPACE 1
-#define TERM_TAB 2
-
-static int name_terminate(const char *name, int namelen, int c, int terminate)
-{
- if (c == ' ' && !(terminate & TERM_SPACE))
- return 0;
- if (c == '\t' && !(terminate & TERM_TAB))
- return 0;
-
- return 1;
-}
-
-/* remove double slashes to make --index work with such filenames */
-static char *squash_slash(char *name)
-{
- int i = 0, j = 0;
-
- if (!name)
- return NULL;
-
- while (name[i]) {
- if ((name[j++] = name[i++]) == '/')
- while (name[i] == '/')
- i++;
- }
- name[j] = '\0';
- return name;
-}
-
-static char *find_name_gnu(const char *line, const char *def, int p_value)
-{
- struct strbuf name = STRBUF_INIT;
- char *cp;
-
- /*
- * Proposed "new-style" GNU patch/diff format; see
- * http://marc.info/?l=git&m=112927316408690&w=2
- */
- if (unquote_c_style(&name, line, NULL)) {
- strbuf_release(&name);
- return NULL;
- }
-
- for (cp = name.buf; p_value; p_value--) {
- cp = strchr(cp, '/');
- if (!cp) {
- strbuf_release(&name);
- return NULL;
- }
- cp++;
- }
-
- strbuf_remove(&name, 0, cp - name.buf);
- if (root.len)
- strbuf_insert(&name, 0, root.buf, root.len);
- return squash_slash(strbuf_detach(&name, NULL));
-}
-
-static size_t sane_tz_len(const char *line, size_t len)
-{
- const char *tz, *p;
-
- if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ')
- return 0;
- tz = line + len - strlen(" +0500");
-
- if (tz[1] != '+' && tz[1] != '-')
- return 0;
-
- for (p = tz + 2; p != line + len; p++)
- if (!isdigit(*p))
- return 0;
-
- return line + len - tz;
-}
-
-static size_t tz_with_colon_len(const char *line, size_t len)
-{
- const char *tz, *p;
-
- if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':')
- return 0;
- tz = line + len - strlen(" +08:00");
-
- if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-'))
- return 0;
- p = tz + 2;
- if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
- !isdigit(*p++) || !isdigit(*p++))
- return 0;
-
- return line + len - tz;
-}
-
-static size_t date_len(const char *line, size_t len)
-{
- const char *date, *p;
-
- if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-')
- return 0;
- p = date = line + len - strlen("72-02-05");
-
- if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
- !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
- !isdigit(*p++) || !isdigit(*p++)) /* Not a date. */
- return 0;
-
- if (date - line >= strlen("19") &&
- isdigit(date[-1]) && isdigit(date[-2])) /* 4-digit year */
- date -= strlen("19");
-
- return line + len - date;
-}
-
-static size_t short_time_len(const char *line, size_t len)
-{
- const char *time, *p;
-
- if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':')
- return 0;
- p = time = line + len - strlen(" 07:01:32");
-
- /* Permit 1-digit hours? */
- if (*p++ != ' ' ||
- !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
- !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
- !isdigit(*p++) || !isdigit(*p++)) /* Not a time. */
- return 0;
-
- return line + len - time;
-}
-
-static size_t fractional_time_len(const char *line, size_t len)
-{
- const char *p;
- size_t n;
-
- /* Expected format: 19:41:17.620000023 */
- if (!len || !isdigit(line[len - 1]))
- return 0;
- p = line + len - 1;
-
- /* Fractional seconds. */
- while (p > line && isdigit(*p))
- p--;
- if (*p != '.')
- return 0;
-
- /* Hours, minutes, and whole seconds. */
- n = short_time_len(line, p - line);
- if (!n)
- return 0;
-
- return line + len - p + n;
-}
-
-static size_t trailing_spaces_len(const char *line, size_t len)
-{
- const char *p;
-
- /* Expected format: ' ' x (1 or more) */
- if (!len || line[len - 1] != ' ')
- return 0;
-
- p = line + len;
- while (p != line) {
- p--;
- if (*p != ' ')
- return line + len - (p + 1);
- }
-
- /* All spaces! */
- return len;
-}
-
-static size_t diff_timestamp_len(const char *line, size_t len)
-{
- const char *end = line + len;
- size_t n;
-
- /*
- * Posix: 2010-07-05 19:41:17
- * GNU: 2010-07-05 19:41:17.620000023 -0500
- */
-
- if (!isdigit(end[-1]))
- return 0;
-
- n = sane_tz_len(line, end - line);
- if (!n)
- n = tz_with_colon_len(line, end - line);
- end -= n;
-
- n = short_time_len(line, end - line);
- if (!n)
- n = fractional_time_len(line, end - line);
- end -= n;
-
- n = date_len(line, end - line);
- if (!n) /* No date. Too bad. */
- return 0;
- end -= n;
-
- if (end == line) /* No space before date. */
- return 0;
- if (end[-1] == '\t') { /* Success! */
- end--;
- return line + len - end;
- }
- if (end[-1] != ' ') /* No space before date. */
- return 0;
-
- /* Whitespace damage. */
- end -= trailing_spaces_len(line, end - line);
- return line + len - end;
-}
-
-static char *find_name_common(const char *line, const char *def,
- int p_value, const char *end, int terminate)
-{
- int len;
- const char *start = NULL;
-
- if (p_value == 0)
- start = line;
- while (line != end) {
- char c = *line;
-
- if (!end && isspace(c)) {
- if (c == '\n')
- break;
- if (name_terminate(start, line-start, c, terminate))
- break;
- }
- line++;
- if (c == '/' && !--p_value)
- start = line;
- }
- if (!start)
- return squash_slash(xstrdup_or_null(def));
- len = line - start;
- if (!len)
- return squash_slash(xstrdup_or_null(def));
-
- /*
- * Generally we prefer the shorter name, especially
- * if the other one is just a variation of that with
- * something else tacked on to the end (ie "file.orig"
- * or "file~").
- */
- if (def) {
- int deflen = strlen(def);
- if (deflen < len && !strncmp(start, def, deflen))
- return squash_slash(xstrdup(def));
- }
-
- if (root.len) {
- char *ret = xstrfmt("%s%.*s", 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)
-{
- if (*line == '"') {
- char *name = find_name_gnu(line, def, p_value);
- if (name)
- return name;
- }
-
- return find_name_common(line, def, p_value, NULL, terminate);
-}
-
-static char *find_name_traditional(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);
- if (name)
- return name;
- }
-
- 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);
- len -= date_len;
-
- return find_name_common(line, def, p_value, line + len, 0);
-}
-
-static int count_slashes(const char *cp)
-{
- int cnt = 0;
- char ch;
-
- while ((ch = *cp++))
- if (ch == '/')
- cnt++;
- return cnt;
-}
-
-/*
- * Given the string after "--- " or "+++ ", guess the appropriate
- * p_value for the given patch.
- */
-static int guess_p_value(const char *nameline)
-{
- char *name, *cp;
- int val = -1;
-
- if (is_dev_null(nameline))
- return -1;
- name = find_name_traditional(nameline, NULL, 0);
- if (!name)
- return -1;
- cp = strchr(name, '/');
- if (!cp)
- val = 0;
- else if (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);
- else {
- cp++;
- if (!strncmp(cp, prefix, prefix_length))
- val = count_slashes(prefix) + 1;
- }
- }
- free(name);
- return val;
-}
-
-/*
- * 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?
- */
-static int has_epoch_timestamp(const char *nameline)
-{
- /*
- * We are only interested in epoch timestamp; any non-zero
- * fraction cannot be one, hence "(\.0+)?" in the regexp below.
- * For the same reason, the date must be either 1969-12-31 or
- * 1970-01-01, and the seconds part must be "00".
- */
- const char stamp_regexp[] =
- "^(1969-12-31|1970-01-01)"
- " "
- "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
- " "
- "([-+][0-2][0-9]:?[0-5][0-9])\n";
- const char *timestamp = NULL, *cp, *colon;
- static regex_t *stamp;
- regmatch_t m[10];
- int zoneoffset;
- int hourminute;
- int status;
-
- for (cp = nameline; *cp != '\n'; cp++) {
- if (*cp == '\t')
- timestamp = cp + 1;
- }
- if (!timestamp)
- return 0;
- if (!stamp) {
- stamp = xmalloc(sizeof(*stamp));
- if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
- warning(_("Cannot prepare timestamp regexp %s"),
- stamp_regexp);
- return 0;
- }
- }
-
- status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
- if (status) {
- if (status != REG_NOMATCH)
- warning(_("regexec returned %d for input: %s"),
- status, timestamp);
- return 0;
- }
-
- zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
- if (*colon == ':')
- zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
- else
- zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
- if (timestamp[m[3].rm_so] == '-')
- zoneoffset = -zoneoffset;
-
- /*
- * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
- * (west of GMT) or 1970-01-01 (east of GMT)
- */
- if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
- (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
- return 0;
-
- hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
- strtol(timestamp + 14, NULL, 10) -
- zoneoffset);
-
- return ((zoneoffset < 0 && hourminute == 1440) ||
- (0 <= zoneoffset && !hourminute));
-}
-
-/*
- * Get the name etc info from the ---/+++ lines of a traditional patch header
- *
- * FIXME! The end-of-filename heuristics are kind of screwy. For existing
- * 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)
-{
- char *name;
-
- first += 4; /* skip "--- " */
- second += 4; /* skip "+++ " */
- if (!p_value_known) {
- int p, q;
- p = guess_p_value(first);
- q = guess_p_value(second);
- if (p < 0) p = q;
- if (0 <= p && p == q) {
- p_value = p;
- 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);
- 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);
- 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);
- free(first_name);
- if (has_epoch_timestamp(first)) {
- patch->is_new = 1;
- patch->is_delete = 0;
- patch->new_name = name;
- } else if (has_epoch_timestamp(second)) {
- patch->is_new = 0;
- patch->is_delete = 1;
- patch->old_name = name;
- } else {
- patch->old_name = name;
- patch->new_name = xstrdup_or_null(name);
- }
- }
- if (!name)
- die(_("unable to find filename in patch at line %d"), linenr);
-}
-
-static int gitdiff_hdrend(const char *line, struct patch *patch)
-{
- return -1;
-}
-
-/*
- * We're anal about diff header consistency, to make
- * sure that we don't end up having strange ambiguous
- * patches floating around.
- *
- * As a result, gitdiff_{old|new}name() will check
- * their names against any previous information, just
- * to make sure..
- */
-#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)
-{
- if (!orig_name && !isnull)
- return find_name(line, NULL, p_value, TERM_TAB);
-
- if (orig_name) {
- int len;
- const char *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((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);
- free(another);
- return orig_name;
- }
- 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;
- }
-}
-
-static int gitdiff_oldname(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);
- return 0;
-}
-
-static int gitdiff_newname(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);
- return 0;
-}
-
-static int gitdiff_oldmode(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)
-{
- patch->new_mode = strtoul(line, NULL, 8);
- return 0;
-}
-
-static int gitdiff_delete(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);
-}
-
-static int gitdiff_newfile(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);
-}
-
-static int gitdiff_copysrc(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);
- return 0;
-}
-
-static int gitdiff_copydst(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);
- return 0;
-}
-
-static int gitdiff_renamesrc(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);
- return 0;
-}
-
-static int gitdiff_renamedst(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);
- return 0;
-}
-
-static int gitdiff_similarity(const char *line, struct patch *patch)
-{
- unsigned long val = strtoul(line, NULL, 10);
- if (val <= 100)
- patch->score = val;
- return 0;
-}
-
-static int gitdiff_dissimilarity(const char *line, struct patch *patch)
-{
- unsigned long val = strtoul(line, NULL, 10);
- if (val <= 100)
- patch->score = val;
- return 0;
-}
-
-static int gitdiff_index(const char *line, struct patch *patch)
-{
- /*
- * index line is N hexadecimal, "..", N hexadecimal,
- * and optional space with octal mode.
- */
- const char *ptr, *eol;
- int len;
-
- ptr = strchr(line, '.');
- if (!ptr || ptr[1] != '.' || 40 < ptr - line)
- return 0;
- len = ptr - line;
- memcpy(patch->old_sha1_prefix, line, len);
- patch->old_sha1_prefix[len] = 0;
-
- line = ptr + 2;
- ptr = strchr(line, ' ');
- eol = strchrnul(line, '\n');
-
- if (!ptr || eol < ptr)
- ptr = eol;
- len = ptr - line;
-
- if (40 < len)
- return 0;
- memcpy(patch->new_sha1_prefix, line, len);
- patch->new_sha1_prefix[len] = 0;
- if (*ptr == ' ')
- patch->old_mode = strtoul(ptr+1, NULL, 8);
- return 0;
-}
-
-/*
- * 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)
-{
- return -1;
-}
-
-/*
- * 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)
-{
- int nslash;
- int i;
-
- if (!p_value)
- return (llen && line[0] == '/') ? NULL : line;
-
- nslash = p_value;
- for (i = 0; i < llen; i++) {
- int ch = line[i];
- if (ch == '/' && --nslash <= 0)
- return (i == 0) ? NULL : &line[i + 1];
- }
- return NULL;
-}
-
-/*
- * This is to extract the same name that appears on "diff --git"
- * line. We do not find and return anything if it is a rename
- * patch, and it is OK because we will find the name elsewhere.
- * We need to reliably find name only when it is mode-change only,
- * 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)
-{
- const char *name;
- const char *second = NULL;
- size_t len, line_len;
-
- line += strlen("diff --git ");
- llen -= strlen("diff --git ");
-
- if (*line == '"') {
- const char *cp;
- struct strbuf first = STRBUF_INIT;
- struct strbuf sp = STRBUF_INIT;
-
- if (unquote_c_style(&first, line, &second))
- goto free_and_fail1;
-
- /* strip the a/b prefix including trailing slash */
- cp = skip_tree_prefix(first.buf, first.len);
- if (!cp)
- goto free_and_fail1;
- strbuf_remove(&first, 0, cp - first.buf);
-
- /*
- * second points at one past closing dq of name.
- * find the second name.
- */
- while ((second < line + llen) && isspace(*second))
- second++;
-
- if (line + llen <= second)
- goto free_and_fail1;
- if (*second == '"') {
- if (unquote_c_style(&sp, second, NULL))
- goto free_and_fail1;
- cp = skip_tree_prefix(sp.buf, sp.len);
- if (!cp)
- goto free_and_fail1;
- /* They must match, otherwise ignore */
- if (strcmp(cp, first.buf))
- goto free_and_fail1;
- strbuf_release(&sp);
- return strbuf_detach(&first, NULL);
- }
-
- /* unquoted second */
- cp = skip_tree_prefix(second, line + llen - second);
- if (!cp)
- goto free_and_fail1;
- if (line + llen - cp != first.len ||
- memcmp(first.buf, cp, first.len))
- goto free_and_fail1;
- return strbuf_detach(&first, NULL);
-
- free_and_fail1:
- strbuf_release(&first);
- strbuf_release(&sp);
- return NULL;
- }
-
- /* unquoted first name */
- name = skip_tree_prefix(line, llen);
- if (!name)
- return NULL;
-
- /*
- * since the first name is unquoted, a dq if exists must be
- * the beginning of the second name.
- */
- for (second = name; second < line + llen; second++) {
- if (*second == '"') {
- struct strbuf sp = STRBUF_INIT;
- const char *np;
-
- if (unquote_c_style(&sp, second, NULL))
- goto free_and_fail2;
-
- np = skip_tree_prefix(sp.buf, sp.len);
- if (!np)
- goto free_and_fail2;
-
- len = sp.buf + sp.len - np;
- if (len < second - name &&
- !strncmp(np, name, len) &&
- isspace(name[len])) {
- /* Good */
- strbuf_remove(&sp, 0, np - sp.buf);
- return strbuf_detach(&sp, NULL);
- }
-
- free_and_fail2:
- strbuf_release(&sp);
- return NULL;
- }
- }
-
- /*
- * Accept a name only if it shows up twice, exactly the same
- * form.
- */
- second = strchr(name, '\n');
- if (!second)
- return NULL;
- line_len = second - name;
- for (len = 0 ; ; len++) {
- switch (name[len]) {
- default:
- continue;
- case '\n':
- return NULL;
- case '\t': case ' ':
- /*
- * Is this the separator between the preimage
- * and the postimage pathname? Again, we are
- * only interested in the case where there is
- * no rename, as this is only to set def_name
- * and a rename patch has the names elsewhere
- * in an unambiguous form.
- */
- if (!name[len + 1])
- return NULL; /* no postimage name */
- second = skip_tree_prefix(name + len + 1,
- line_len - (len + 1));
- if (!second)
- return NULL;
- /*
- * Does len bytes starting at "name" and "second"
- * (that are separated by one HT or SP we just
- * found) exactly match?
- */
- if (second[len] == '\n' && !strncmp(name, second, len))
- return xmemdupz(name, len);
- }
- }
-}
-
-/* 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)
-{
- unsigned long offset;
-
- /* A git diff has explicit new/delete information, so we don't guess */
- patch->is_new = 0;
- patch->is_delete = 0;
-
- /*
- * Some things may not have the old name in the
- * rest of the headers anywhere (pure mode changes,
- * 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.len) {
- char *s = xstrfmt("%s%s", 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++) {
- static const struct opentry {
- const char *str;
- int (*fn)(const char *, struct patch *);
- } optable[] = {
- { "@@ -", gitdiff_hdrend },
- { "--- ", gitdiff_oldname },
- { "+++ ", gitdiff_newname },
- { "old mode ", gitdiff_oldmode },
- { "new mode ", gitdiff_newmode },
- { "deleted file mode ", gitdiff_delete },
- { "new file mode ", gitdiff_newfile },
- { "copy from ", gitdiff_copysrc },
- { "copy to ", gitdiff_copydst },
- { "rename old ", gitdiff_renamesrc },
- { "rename new ", gitdiff_renamedst },
- { "rename from ", gitdiff_renamesrc },
- { "rename to ", gitdiff_renamedst },
- { "similarity index ", gitdiff_similarity },
- { "dissimilarity index ", gitdiff_dissimilarity },
- { "index ", gitdiff_index },
- { "", gitdiff_unrecognized },
- };
- int i;
-
- len = linelen(line, size);
- if (!len || line[len-1] != '\n')
- break;
- for (i = 0; i < ARRAY_SIZE(optable); i++) {
- const struct opentry *p = optable + i;
- int oplen = strlen(p->str);
- if (len < oplen || memcmp(p->str, line, oplen))
- continue;
- if (p->fn(line + oplen, patch) < 0)
- return offset;
- break;
- }
- }
-
- return offset;
-}
-
-static int parse_num(const char *line, unsigned long *p)
-{
- char *ptr;
-
- if (!isdigit(*line))
- return 0;
- *p = strtoul(line, &ptr, 10);
- return ptr - line;
-}
-
-static int parse_range(const char *line, int len, int offset, const char *expect,
- unsigned long *p1, unsigned long *p2)
-{
- int digits, ex;
-
- if (offset < 0 || offset >= len)
- return -1;
- line += offset;
- len -= offset;
-
- digits = parse_num(line, p1);
- if (!digits)
- return -1;
-
- offset += digits;
- line += digits;
- len -= digits;
-
- *p2 = 1;
- if (*line == ',') {
- digits = parse_num(line+1, p2);
- if (!digits)
- return -1;
-
- offset += digits+1;
- line += digits+1;
- len -= digits+1;
- }
-
- ex = strlen(expect);
- if (ex > len)
- return -1;
- if (memcmp(line, expect, ex))
- return -1;
-
- return offset + ex;
-}
-
-static void recount_diff(const char *line, int size, struct fragment *fragment)
-{
- int oldlines = 0, newlines = 0, ret = 0;
-
- if (size < 1) {
- warning("recount: ignore empty hunk");
- return;
- }
-
- for (;;) {
- int len = linelen(line, size);
- size -= len;
- line += len;
-
- if (size < 1)
- break;
-
- switch (*line) {
- case ' ': case '\n':
- newlines++;
- /* fall through */
- case '-':
- oldlines++;
- continue;
- case '+':
- newlines++;
- continue;
- case '\\':
- continue;
- case '@':
- ret = size < 3 || !starts_with(line, "@@ ");
- break;
- case 'd':
- ret = size < 5 || !starts_with(line, "diff ");
- break;
- default:
- ret = -1;
- break;
- }
- if (ret) {
- warning(_("recount: unexpected line: %.*s"),
- (int)linelen(line, size), line);
- return;
- }
- break;
- }
- fragment->oldlines = oldlines;
- fragment->newlines = newlines;
-}
-
-/*
- * Parse a unified diff fragment header of the
- * form "@@ -a,b +c,d @@"
- */
-static int parse_fragment_header(const char *line, int len, struct fragment *fragment)
-{
- int offset;
-
- if (!len || line[len-1] != '\n')
- return -1;
-
- /* Figure out the number of lines in a fragment */
- offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines);
- offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines);
-
- return offset;
-}
-
-static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
-{
- unsigned long offset, len;
-
- patch->is_toplevel_relative = 0;
- patch->is_rename = patch->is_copy = 0;
- 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++) {
- unsigned long nextlen;
-
- len = linelen(line, size);
- if (!len)
- break;
-
- /* Testing this early allows us to take a few shortcuts.. */
- if (len < 6)
- continue;
-
- /*
- * Make sure we don't find any unconnected patch fragments.
- * That's a sign that we didn't find a header, and that a
- * patch has become corrupted/broken up.
- */
- if (!memcmp("@@ -", line, 4)) {
- struct fragment dummy;
- if (parse_fragment_header(line, len, &dummy) < 0)
- continue;
- die(_("patch fragment without header at line %d: %.*s"),
- linenr, (int)len-1, line);
- }
-
- if (size < len + 6)
- break;
-
- /*
- * Git patch? It might not have a real patch, just a rename
- * 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);
- if (git_hdr_len <= len)
- continue;
- if (!patch->old_name && !patch->new_name) {
- if (!patch->def_name)
- die(Q_("git diff header lacks filename information when removing "
- "%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);
- 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);
- patch->is_toplevel_relative = 1;
- *hdrsize = git_hdr_len;
- return offset;
- }
-
- /* --- followed by +++ ? */
- if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4))
- continue;
-
- /*
- * We only accept unified patches, so we want it to
- * at least have "@@ -a,b +c,d @@\n", which is 14 chars
- * minimum ("@@ -0,0 +1 @@\n" is the shortest).
- */
- nextlen = linelen(line + len, size - len);
- if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
- continue;
-
- /* Ok, we'll consider it a patch */
- parse_traditional_patch(line, line+len, patch);
- *hdrsize = len + nextlen;
- linenr += 2;
- return offset;
- }
- return -1;
-}
-
-static void record_ws_error(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)
- return;
-
- err = whitespace_error_string(result);
- fprintf(stderr, "%s:%d: %s.\n%.*s\n",
- patch_input_file, linenr, err, len, line);
- free(err);
-}
-
-static void check_whitespace(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);
-}
-
-/*
- * Parse a unified diff. Note that this really needs to parse each
- * fragment separately, since the only way to know the difference
- * 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)
-{
- int added, deleted;
- int len = linelen(line, size), offset;
- unsigned long oldlines, newlines;
- unsigned long leading, trailing;
-
- offset = parse_fragment_header(line, len, fragment);
- if (offset < 0)
- return -1;
- if (offset > 0 && patch->recount)
- recount_diff(line + offset, size - offset, fragment);
- oldlines = fragment->oldlines;
- newlines = fragment->newlines;
- leading = 0;
- trailing = 0;
-
- /* Parse the thing.. */
- line += len;
- size -= len;
- linenr++;
- added = deleted = 0;
- for (offset = len;
- 0 < size;
- offset += len, size -= len, line += len, linenr++) {
- if (!oldlines && !newlines)
- break;
- len = linelen(line, size);
- if (!len || line[len-1] != '\n')
- return -1;
- switch (*line) {
- default:
- return -1;
- case '\n': /* newer GNU diff, an empty context line */
- case ' ':
- oldlines--;
- newlines--;
- if (!deleted && !added)
- leading++;
- trailing++;
- if (!apply_in_reverse &&
- ws_error_action == correct_ws_error)
- check_whitespace(line, len, patch->ws_rule);
- break;
- case '-':
- if (apply_in_reverse &&
- ws_error_action != nowarn_ws_error)
- check_whitespace(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);
- added++;
- newlines--;
- trailing = 0;
- break;
-
- /*
- * We allow "\ No newline at end of file". Depending
- * on locale settings when the patch was produced we
- * don't know what this line looks like. The only
- * thing we do know is that it begins with "\ ".
- * Checking for 12 is just for sanity check -- any
- * l10n of "\ No newline..." is at least that long.
- */
- case '\\':
- if (len < 12 || memcmp(line, "\\ ", 2))
- return -1;
- break;
- }
- }
- if (oldlines || newlines)
- return -1;
- if (!deleted && !added)
- return -1;
-
- fragment->leading = leading;
- fragment->trailing = trailing;
-
- /*
- * If a fragment ends with an incomplete line, we failed to include
- * it in the above loop because we hit oldlines == newlines == 0
- * before seeing it.
- */
- if (12 < size && !memcmp(line, "\\ ", 2))
- offset += linelen(line, size);
-
- patch->lines_added += added;
- patch->lines_deleted += deleted;
-
- if (0 < patch->is_new && oldlines)
- return error(_("new file depends on old contents"));
- if (0 < patch->is_delete && newlines)
- return error(_("deleted file still has contents"));
- return offset;
-}
-
-/*
- * We have seen "diff --git a/... b/..." header (or a traditional patch
- * header). Read hunks that belong to this patch into fragments and hang
- * them to the given patch structure.
- *
- * 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)
-{
- unsigned long offset = 0;
- unsigned long oldlines = 0, newlines = 0, context = 0;
- struct fragment **fragp = &patch->fragments;
-
- while (size > 4 && !memcmp(line, "@@ -", 4)) {
- struct fragment *fragment;
- int len;
-
- fragment = xcalloc(1, sizeof(*fragment));
- fragment->linenr = linenr;
- len = parse_fragment(line, size, patch, fragment);
- if (len <= 0)
- die(_("corrupt patch at line %d"), linenr);
- fragment->patch = line;
- fragment->size = len;
- oldlines += fragment->oldlines;
- newlines += fragment->newlines;
- context += fragment->leading + fragment->trailing;
-
- *fragp = fragment;
- fragp = &fragment->next;
-
- offset += len;
- line += len;
- size -= len;
- }
-
- /*
- * If something was removed (i.e. we have old-lines) it cannot
- * be creation, and if something was added it cannot be
- * deletion. However, the reverse is not true; --unified=0
- * patches that only add are not necessarily creation even
- * though they do not have any old lines, and ones that only
- * delete are not necessarily deletion.
- *
- * Unfortunately, a real creation/deletion patch do _not_ have
- * any context line by definition, so we cannot safely tell it
- * apart with --unified=0 insanity. At least if the patch has
- * more than one hunk it is not creation or deletion.
- */
- if (patch->is_new < 0 &&
- (oldlines || (patch->fragments && patch->fragments->next)))
- patch->is_new = 0;
- if (patch->is_delete < 0 &&
- (newlines || (patch->fragments && patch->fragments->next)))
- patch->is_delete = 0;
-
- if (0 < patch->is_new && oldlines)
- die(_("new file %s depends on old contents"), patch->new_name);
- if (0 < patch->is_delete && newlines)
- die(_("deleted file %s still has contents"), patch->old_name);
- if (!patch->is_delete && !newlines && context)
- fprintf_ln(stderr,
- _("** warning: "
- "file %s becomes empty but is not deleted"),
- patch->new_name);
-
- return offset;
-}
-
-static inline int metadata_changes(struct patch *patch)
-{
- return patch->is_rename > 0 ||
- patch->is_copy > 0 ||
- patch->is_new > 0 ||
- patch->is_delete ||
- (patch->old_mode && patch->new_mode &&
- patch->old_mode != patch->new_mode);
-}
-
-static char *inflate_it(const void *data, unsigned long size,
- unsigned long inflated_size)
-{
- git_zstream stream;
- void *out;
- int st;
-
- memset(&stream, 0, sizeof(stream));
-
- stream.next_in = (unsigned char *)data;
- stream.avail_in = size;
- stream.next_out = out = xmalloc(inflated_size);
- stream.avail_out = inflated_size;
- git_inflate_init(&stream);
- st = git_inflate(&stream, Z_FINISH);
- git_inflate_end(&stream);
- if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
- free(out);
- return NULL;
- }
- return out;
-}
-
-/*
- * Read a binary hunk and return a new fragment; fragment->patch
- * 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,
- unsigned long *sz_p,
- int *status_p,
- int *used_p)
-{
- /*
- * Expect a line that begins with binary patch method ("literal"
- * or "delta"), followed by the length of data before deflating.
- * a sequence of 'length-byte' followed by base-85 encoded data
- * should follow, terminated by a newline.
- *
- * Each 5-byte sequence of base-85 encodes up to 4 bytes,
- * and we would limit the patch line to 66 characters,
- * so one line can fit up to 13 groups that would decode
- * to 52 bytes max. The length byte 'A'-'Z' corresponds
- * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
- */
- int llen, used;
- unsigned long size = *sz_p;
- char *buffer = *buf_p;
- int patch_method;
- unsigned long origlen;
- char *data = NULL;
- int hunk_size = 0;
- struct fragment *frag;
-
- llen = linelen(buffer, size);
- used = llen;
-
- *status_p = 0;
-
- if (starts_with(buffer, "delta ")) {
- patch_method = BINARY_DELTA_DEFLATED;
- origlen = strtoul(buffer + 6, NULL, 10);
- }
- else if (starts_with(buffer, "literal ")) {
- patch_method = BINARY_LITERAL_DEFLATED;
- origlen = strtoul(buffer + 8, NULL, 10);
- }
- else
- return NULL;
-
- linenr++;
- buffer += llen;
- while (1) {
- int byte_length, max_byte_length, newsize;
- llen = linelen(buffer, size);
- used += llen;
- linenr++;
- if (llen == 1) {
- /* consume the blank line */
- buffer++;
- size--;
- break;
- }
- /*
- * Minimum line is "A00000\n" which is 7-byte long,
- * and the line length must be multiple of 5 plus 2.
- */
- if ((llen < 7) || (llen-2) % 5)
- goto corrupt;
- max_byte_length = (llen - 2) / 5 * 4;
- byte_length = *buffer;
- if ('A' <= byte_length && byte_length <= 'Z')
- byte_length = byte_length - 'A' + 1;
- else if ('a' <= byte_length && byte_length <= 'z')
- byte_length = byte_length - 'a' + 27;
- else
- goto corrupt;
- /* if the input length was not multiple of 4, we would
- * have filler at the end but the filler should never
- * exceed 3 bytes
- */
- if (max_byte_length < byte_length ||
- byte_length <= max_byte_length - 4)
- goto corrupt;
- newsize = hunk_size + byte_length;
- data = xrealloc(data, newsize);
- if (decode_85(data + hunk_size, buffer + 1, byte_length))
- goto corrupt;
- hunk_size = newsize;
- buffer += llen;
- size -= llen;
- }
-
- frag = xcalloc(1, sizeof(*frag));
- frag->patch = inflate_it(data, hunk_size, origlen);
- frag->free_patch = 1;
- if (!frag->patch)
- goto corrupt;
- free(data);
- frag->size = origlen;
- *buf_p = buffer;
- *sz_p = size;
- *used_p = used;
- frag->binary_patch_method = patch_method;
- return frag;
-
- corrupt:
- free(data);
- *status_p = -1;
- error(_("corrupt binary patch at line %d: %.*s"),
- linenr-1, llen-1, buffer);
- return NULL;
-}
-
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
-{
- /*
- * We have read "GIT binary patch\n"; what follows is a line
- * that says the patch method (currently, either "literal" or
- * "delta") and the length of data before deflating; a
- * sequence of 'length-byte' followed by base-85 encoded data
- * follows.
- *
- * When a binary patch is reversible, there is another binary
- * hunk in the same format, starting with patch method (either
- * "literal" or "delta") with the length of data, and a sequence
- * of length-byte + base-85 encoded data, terminated with another
- * empty line. This data, when applied to the postimage, produces
- * the preimage.
- */
- struct fragment *forward;
- struct fragment *reverse;
- int status;
- int used, used_1;
-
- forward = parse_binary_hunk(&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);
- if (status)
- /* otherwise we already gave an error message */
- return status;
-
- reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
- if (reverse)
- used += used_1;
- else if (status) {
- /*
- * Not having reverse hunk is not an error, but having
- * a corrupt reverse hunk is.
- */
- free((void*) forward->patch);
- free(forward);
- return status;
- }
- forward->next = reverse;
- patch->fragments = forward;
- patch->is_binary = 1;
- return used;
-}
-
-static void prefix_one(char **name)
-{
- char *old_name = *name;
- if (!old_name)
- return;
- *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
- free(old_name);
-}
-
-static void prefix_patch(struct patch *p)
-{
- if (!prefix || p->is_toplevel_relative)
- return;
- prefix_one(&p->new_name);
- prefix_one(&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)
-{
- struct string_list_item *it;
-
- it = string_list_append(&limit_by_name, name);
- it->util = exclude ? NULL : (void *) 1;
-}
-
-static int use_patch(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) {
- int pathlen = strlen(pathname);
- if (pathlen <= prefix_length ||
- memcmp(prefix, pathname, 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];
- if (!wildmatch(it->string, pathname, 0, NULL))
- return (it->util != NULL);
- }
-
- /*
- * If we had any include, a path that does not match any rule is
- * not used. Otherwise, we saw bunch of exclude rules (or none)
- * and such a path is used.
- */
- return !has_include;
-}
-
-
-/*
- * Read the patch text in "buffer" that extends for "size" bytes; stop
- * reading after seeing a single patch (i.e. changes to a single file).
- * Create fragments (i.e. patch hunks) and hang them to the given patch.
- * 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)
-{
- int hdrsize, patchsize;
- int offset = find_header(buffer, size, &hdrsize, patch);
-
- if (offset < 0)
- return offset;
-
- prefix_patch(patch);
-
- if (!use_patch(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);
-
- if (!patchsize) {
- static const char git_binary[] = "GIT binary patch\n";
- int hd = hdrsize + offset;
- unsigned long llen = linelen(buffer + hd, size - hd);
-
- if (llen == sizeof(git_binary) - 1 &&
- !memcmp(git_binary, buffer + hd, llen)) {
- int used;
- linenr++;
- used = parse_binary(buffer + hd + llen,
- size - hd - llen, patch);
- if (used)
- patchsize = used + llen;
- else
- patchsize = 0;
- }
- else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
- static const char *binhdr[] = {
- "Binary files ",
- "Files ",
- NULL,
- };
- int i;
- for (i = 0; binhdr[i]; i++) {
- int len = strlen(binhdr[i]);
- if (len < size - hd &&
- !memcmp(binhdr[i], buffer + hd, len)) {
- linenr++;
- patch->is_binary = 1;
- patchsize = llen;
- break;
- }
- }
- }
-
- /* Empty patch cannot be applied if it is a text patch
- * without metadata change. A binary patch appears
- * empty to us here.
- */
- if ((apply || check) &&
- (!patch->is_binary && !metadata_changes(patch)))
- die(_("patch with only garbage at line %d"), linenr);
- }
-
- return offset + hdrsize + patchsize;
-}
-
-#define swap(a,b) myswap((a),(b),sizeof(a))
-
-#define myswap(a, b, size) do { \
- unsigned char mytmp[size]; \
- memcpy(mytmp, &a, size); \
- memcpy(&a, &b, size); \
- memcpy(&b, mytmp, size); \
-} while (0)
-
-static void reverse_patches(struct patch *p)
-{
- for (; p; p = p->next) {
- struct fragment *frag = p->fragments;
-
- swap(p->new_name, p->old_name);
- swap(p->new_mode, p->old_mode);
- swap(p->is_new, p->is_delete);
- swap(p->lines_added, p->lines_deleted);
- swap(p->old_sha1_prefix, p->new_sha1_prefix);
-
- for (; frag; frag = frag->next) {
- swap(frag->newpos, frag->oldpos);
- swap(frag->newlines, frag->oldlines);
- }
- }
-}
-
-static const char pluses[] =
-"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
-static const char minuses[]=
-"----------------------------------------------------------------------";
-
-static void show_stats(struct patch *patch)
-{
- struct strbuf qname = STRBUF_INIT;
- char *cp = patch->new_name ? patch->new_name : patch->old_name;
- int max, add, del;
-
- quote_c_style(cp, &qname, NULL, 0);
-
- /*
- * "scale" the filename
- */
- max = max_len;
- if (max > 50)
- max = 50;
-
- if (qname.len > max) {
- cp = strchr(qname.buf + qname.len + 3 - max, '/');
- if (!cp)
- cp = qname.buf + qname.len + 3 - max;
- strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
- }
-
- if (patch->is_binary) {
- printf(" %-*s | Bin\n", max, qname.buf);
- strbuf_release(&qname);
- return;
- }
-
- printf(" %-*s |", max, qname.buf);
- strbuf_release(&qname);
-
- /*
- * scale the add/delete
- */
- max = max + max_change > 70 ? 70 - max : 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;
- del = total - add;
- }
- printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
- add, pluses, del, minuses);
-}
-
-static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
-{
- switch (st->st_mode & S_IFMT) {
- case S_IFLNK:
- if (strbuf_readlink(buf, path, st->st_size) < 0)
- return error(_("unable to read symlink %s"), path);
- return 0;
- case S_IFREG:
- if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
- return error(_("unable to open or read %s"), path);
- convert_to_git(path, buf->buf, buf->len, buf, 0);
- return 0;
- default:
- return -1;
- }
-}
-
-/*
- * Update the preimage, and the common lines in postimage,
- * from buffer buf of length len. If postlen is 0 the postimage
- * is updated in place, otherwise it's updated on a new buffer
- * of length postlen
- */
-
-static void update_pre_post_images(struct image *preimage,
- struct image *postimage,
- char *buf,
- size_t len, size_t postlen)
-{
- int i, ctx, reduced;
- char *new, *old, *fixed;
- struct image fixed_preimage;
-
- /*
- * Update the preimage with whitespace fixes. Note that we
- * are not losing preimage->buf -- apply_one_fragment() will
- * free "oldlines".
- */
- prepare_image(&fixed_preimage, buf, len, 1);
- assert(postlen
- ? fixed_preimage.nr == preimage->nr
- : fixed_preimage.nr <= preimage->nr);
- for (i = 0; i < fixed_preimage.nr; i++)
- fixed_preimage.line[i].flag = preimage->line[i].flag;
- free(preimage->line_allocated);
- *preimage = fixed_preimage;
-
- /*
- * Adjust the common context lines in postimage. This can be
- * done in-place when we are shrinking it with whitespace
- * fixing, but needs a new buffer when ignoring whitespace or
- * expanding leading tabs to spaces.
- *
- * We trust the caller to tell us if the update can be done
- * in place (postlen==0) or not.
- */
- old = postimage->buf;
- if (postlen)
- new = postimage->buf = xmalloc(postlen);
- else
- new = old;
- fixed = preimage->buf;
-
- for (i = reduced = ctx = 0; i < postimage->nr; i++) {
- size_t 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;
- continue;
- }
-
- /* a common context -- skip it in the original postimage */
- old += len;
-
- /* and find the corresponding one in the fixed preimage */
- while (ctx < preimage->nr &&
- !(preimage->line[ctx].flag & LINE_COMMON)) {
- fixed += preimage->line[ctx].len;
- ctx++;
- }
-
- /*
- * preimage is expected to run out, if the caller
- * fixed addition of trailing blank lines.
- */
- if (preimage->nr <= ctx) {
- reduced++;
- continue;
- }
-
- /* 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;
- ctx++;
- }
-
- if (postlen
- ? postlen < new - postimage->buf
- : postimage->len < new - postimage->buf)
- die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d",
- (int)postlen, (int) postimage->len, (int)(new - postimage->buf));
-
- /* Fix the length of the whole thing */
- postimage->len = new - postimage->buf;
- postimage->nr -= reduced;
-}
-
-static int match_fragment(struct image *img,
- struct image *preimage,
- struct image *postimage,
- unsigned long try,
- int try_lno,
- unsigned ws_rule,
- int match_beginning, int match_end)
-{
- int i;
- char *fixed_buf, *buf, *orig, *target;
- struct strbuf fixed;
- size_t fixed_len, postlen;
- int preimage_limit;
-
- if (preimage->nr + try_lno <= img->nr) {
- /*
- * The hunk falls within the boundaries of img.
- */
- preimage_limit = preimage->nr;
- if (match_end && (preimage->nr + try_lno != img->nr))
- return 0;
- } else if (ws_error_action == correct_ws_error &&
- (ws_rule & WS_BLANK_AT_EOF)) {
- /*
- * This hunk extends beyond the end of img, and we are
- * removing blank lines at the end of the file. This
- * many lines from the beginning of the preimage must
- * match with img, and the remainder of the preimage
- * must be blank.
- */
- preimage_limit = img->nr - try_lno;
- } else {
- /*
- * The hunk extends beyond the end of the img and
- * we are not removing blanks at the end, so we
- * should reject the hunk at this position.
- */
- return 0;
- }
-
- if (match_beginning && try_lno)
- return 0;
-
- /* Quick hash check */
- for (i = 0; i < preimage_limit; i++)
- if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
- (preimage->line[i].hash != img->line[try_lno + i].hash))
- return 0;
-
- if (preimage_limit == preimage->nr) {
- /*
- * Do we have an exact match? If we were told to match
- * at the end, size must be exactly at try+fragsize,
- * otherwise try+fragsize must be still within the preimage,
- * and either case, the old piece should match the preimage
- * exactly.
- */
- if ((match_end
- ? (try + preimage->len == img->len)
- : (try + preimage->len <= img->len)) &&
- !memcmp(img->buf + try, preimage->buf, preimage->len))
- return 1;
- } else {
- /*
- * The preimage extends beyond the end of img, so
- * there cannot be an exact match.
- *
- * There must be one non-blank context line that match
- * a line before the end of img.
- */
- char *buf_end;
-
- buf = preimage->buf;
- buf_end = buf;
- for (i = 0; i < preimage_limit; i++)
- buf_end += preimage->line[i].len;
-
- for ( ; buf < buf_end; buf++)
- if (!isspace(*buf))
- break;
- if (buf == buf_end)
- return 0;
- }
-
- /*
- * No exact match. If we are ignoring whitespace, run a line-by-line
- * 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;
- }
-
- /*
- * 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)
- return 0;
-
- /*
- * The hunk does not apply byte-by-byte, but the hash says
- * it might with whitespace fuzz. We weren't asked to
- * ignore whitespace, we were asked to correct whitespace
- * errors, so let's try matching after whitespace correction.
- *
- * While checking the preimage against the target, whitespace
- * errors in both fixed, we count how large the corresponding
- * postimage needs to be. The postimage prepared by
- * apply_one_fragment() has whitespace errors fixed on added
- * lines already, but the common lines were propagated as-is,
- * which may become longer when their whitespace errors are
- * fixed.
- */
-
- /* First count added lines in postimage */
- postlen = 0;
- for (i = 0; i < postimage->nr; i++) {
- if (!(postimage->line[i].flag & LINE_COMMON))
- postlen += postimage->line[i].len;
- }
-
- /*
- * The preimage may extend beyond the end of the file,
- * but in this loop we will only handle the part of the
- * preimage that falls within the file.
- */
- strbuf_init(&fixed, preimage->len + 1);
- orig = preimage->buf;
- target = img->buf + try;
- for (i = 0; i < preimage_limit; i++) {
- size_t oldlen = preimage->line[i].len;
- size_t tgtlen = img->line[try_lno + i].len;
- size_t fixstart = fixed.len;
- struct strbuf tgtfix;
- int match;
-
- /* Try fixing the line in the preimage */
- ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
-
- /* Try fixing the line in the target */
- strbuf_init(&tgtfix, tgtlen);
- ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
-
- /*
- * If they match, either the preimage was based on
- * a version before our tree fixed whitespace breakage,
- * or we are lacking a whitespace-fix patch the tree
- * the preimage was based on already had (i.e. target
- * has whitespace breakage, the preimage doesn't).
- * In either case, we are fixing the whitespace breakages
- * so we might as well take the fix together with their
- * real change.
- */
- match = (tgtfix.len == fixed.len - fixstart &&
- !memcmp(tgtfix.buf, fixed.buf + fixstart,
- fixed.len - fixstart));
-
- /* Add the length if this is common with the postimage */
- if (preimage->line[i].flag & LINE_COMMON)
- postlen += tgtfix.len;
-
- strbuf_release(&tgtfix);
- if (!match)
- goto unmatch_exit;
-
- orig += oldlen;
- target += tgtlen;
- }
-
-
- /*
- * Now handle the lines in the preimage that falls beyond the
- * end of the file (if any). They will only match if they are
- * empty or only contain whitespace (if WS_BLANK_AT_EOL is
- * false).
- */
- for ( ; i < preimage->nr; i++) {
- size_t fixstart = fixed.len; /* start of the fixed preimage */
- size_t oldlen = preimage->line[i].len;
- int j;
-
- /* Try fixing the line in the preimage */
- ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
-
- for (j = fixstart; j < fixed.len; j++)
- if (!isspace(fixed.buf[j]))
- goto unmatch_exit;
-
- orig += oldlen;
- }
-
- /*
- * Yes, the preimage is based on an older version that still
- * has whitespace breakages unfixed, and fixing them makes the
- * hunk match. Update the context lines in the postimage.
- */
- fixed_buf = strbuf_detach(&fixed, &fixed_len);
- if (postlen < postimage->len)
- postlen = 0;
- update_pre_post_images(preimage, postimage,
- fixed_buf, fixed_len, postlen);
- return 1;
-
- unmatch_exit:
- strbuf_release(&fixed);
- return 0;
-}
-
-static int find_pos(struct image *img,
- struct image *preimage,
- struct image *postimage,
- int line,
- unsigned ws_rule,
- int match_beginning, int match_end)
-{
- int i;
- unsigned long backwards, forwards, try;
- int backwards_lno, forwards_lno, try_lno;
-
- /*
- * If match_beginning or match_end is specified, there is no
- * point starting from a wrong line that will never match and
- * wander around and wait for a match at the specified end.
- */
- if (match_beginning)
- line = 0;
- else if (match_end)
- line = img->nr - preimage->nr;
-
- /*
- * Because the comparison is unsigned, the following test
- * will also take care of a negative line number that can
- * result when match_end and preimage is larger than the target.
- */
- if ((size_t) line > img->nr)
- line = img->nr;
-
- try = 0;
- for (i = 0; i < line; i++)
- try += img->line[i].len;
-
- /*
- * There's probably some smart way to do this, but I'll leave
- * that to the smart and beautiful people. I'm simple and stupid.
- */
- backwards = try;
- backwards_lno = line;
- forwards = try;
- forwards_lno = line;
- try_lno = line;
-
- for (i = 0; ; i++) {
- if (match_fragment(img, preimage, postimage,
- try, try_lno, ws_rule,
- match_beginning, match_end))
- return try_lno;
-
- again:
- if (backwards_lno == 0 && forwards_lno == img->nr)
- break;
-
- if (i & 1) {
- if (backwards_lno == 0) {
- i++;
- goto again;
- }
- backwards_lno--;
- backwards -= img->line[backwards_lno].len;
- try = backwards;
- try_lno = backwards_lno;
- } else {
- if (forwards_lno == img->nr) {
- i++;
- goto again;
- }
- forwards += img->line[forwards_lno].len;
- forwards_lno++;
- try = forwards;
- try_lno = forwards_lno;
- }
-
- }
- return -1;
-}
-
-static void remove_first_line(struct image *img)
-{
- img->buf += img->line[0].len;
- img->len -= img->line[0].len;
- img->line++;
- img->nr--;
-}
-
-static void remove_last_line(struct image *img)
-{
- img->len -= img->line[--img->nr].len;
-}
-
-/*
- * The change from "preimage" and "postimage" has been found to
- * 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,
- int applied_pos,
- struct image *preimage,
- struct image *postimage)
-{
- /*
- * remove the copy of preimage at offset in img
- * and replace it with postimage
- */
- int i, nr;
- size_t remove_count, insert_count, applied_at = 0;
- char *result;
- int preimage_limit;
-
- /*
- * If we are removing blank lines at the end of img,
- * the preimage may extend beyond the end.
- * If that is the case, we must be careful only to
- * remove the part of the preimage that falls within
- * the boundaries of img. Initialize preimage_limit
- * to the number of lines in the preimage that falls
- * within the boundaries.
- */
- preimage_limit = preimage->nr;
- if (preimage_limit > img->nr - applied_pos)
- preimage_limit = img->nr - applied_pos;
-
- for (i = 0; i < applied_pos; i++)
- applied_at += img->line[i].len;
-
- remove_count = 0;
- for (i = 0; i < preimage_limit; i++)
- remove_count += img->line[applied_pos + i].len;
- insert_count = postimage->len;
-
- /* Adjust the contents */
- result = xmalloc(img->len + insert_count - remove_count + 1);
- memcpy(result, img->buf, applied_at);
- memcpy(result + applied_at, postimage->buf, postimage->len);
- memcpy(result + applied_at + postimage->len,
- img->buf + (applied_at + remove_count),
- img->len - (applied_at + remove_count));
- free(img->buf);
- img->buf = result;
- img->len += insert_count - remove_count;
- result[img->len] = '\0';
-
- /* Adjust the line table */
- nr = img->nr + postimage->nr - preimage_limit;
- if (preimage_limit < postimage->nr) {
- /*
- * NOTE: this knows that we never call remove_first_line()
- * on anything other than pre/post image.
- */
- REALLOC_ARRAY(img->line, nr);
- img->line_allocated = img->line;
- }
- if (preimage_limit != postimage->nr)
- memmove(img->line + applied_pos + postimage->nr,
- img->line + applied_pos + preimage_limit,
- (img->nr - (applied_pos + preimage_limit)) *
- sizeof(*img->line));
- memcpy(img->line + applied_pos,
- postimage->line,
- postimage->nr * sizeof(*img->line));
- if (!allow_overlap)
- for (i = 0; i < postimage->nr; i++)
- img->line[applied_pos + i].flag |= LINE_PATCHED;
- img->nr = nr;
-}
-
-/*
- * Use the patch-hunk text in "frag" to prepare two images (preimage and
- * 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,
- int inaccurate_eof, unsigned ws_rule,
- int nth_fragment)
-{
- int match_beginning, match_end;
- const char *patch = frag->patch;
- int size = frag->size;
- char *old, *oldlines;
- struct strbuf newlines;
- int new_blank_lines_at_end = 0;
- int found_new_blank_lines_at_end = 0;
- int hunk_linenr = frag->linenr;
- unsigned long leading, trailing;
- int pos, applied_pos;
- struct image preimage;
- struct image postimage;
-
- memset(&preimage, 0, sizeof(preimage));
- memset(&postimage, 0, sizeof(postimage));
- oldlines = xmalloc(size);
- strbuf_init(&newlines, size);
-
- old = oldlines;
- while (size > 0) {
- char first;
- int len = linelen(patch, size);
- int plen;
- int added_blank_line = 0;
- int is_blank_context = 0;
- size_t start;
-
- if (!len)
- break;
-
- /*
- * "plen" is how much of the line we should use for
- * the actual patch data. Normally we just remove the
- * first character on the line, but if the line is
- * followed by "\ No newline", then we also remove the
- * last one (which is the newline, of course).
- */
- plen = len - 1;
- if (len < size && patch[len] == '\\')
- plen--;
- first = *patch;
- if (apply_in_reverse) {
- if (first == '-')
- first = '+';
- else if (first == '+')
- first = '-';
- }
-
- switch (first) {
- case '\n':
- /* Newer GNU diff, empty context line */
- if (plen < 0)
- /* ... followed by '\No newline'; nothing */
- break;
- *old++ = '\n';
- strbuf_addch(&newlines, '\n');
- add_line_info(&preimage, "\n", 1, LINE_COMMON);
- add_line_info(&postimage, "\n", 1, LINE_COMMON);
- is_blank_context = 1;
- break;
- case ' ':
- if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
- ws_blank_line(patch + 1, plen, ws_rule))
- is_blank_context = 1;
- case '-':
- memcpy(old, patch + 1, plen);
- add_line_info(&preimage, old, plen,
- (first == ' ' ? LINE_COMMON : 0));
- old += plen;
- if (first == '-')
- break;
- /* Fall-through for ' ' */
- case '+':
- /* --no-add does not add new lines */
- if (first == '+' && no_add)
- break;
-
- start = newlines.len;
- if (first != '+' ||
- !whitespace_error ||
- 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);
- }
- add_line_info(&postimage, newlines.buf + start, newlines.len - start,
- (first == '+' ? 0 : LINE_COMMON));
- if (first == '+' &&
- (ws_rule & WS_BLANK_AT_EOF) &&
- ws_blank_line(patch + 1, plen, ws_rule))
- added_blank_line = 1;
- break;
- case '@': case '\\':
- /* Ignore it, we already handled it */
- break;
- default:
- if (apply_verbosely)
- error(_("invalid start of line: '%c'"), first);
- applied_pos = -1;
- goto out;
- }
- if (added_blank_line) {
- if (!new_blank_lines_at_end)
- found_new_blank_lines_at_end = hunk_linenr;
- new_blank_lines_at_end++;
- }
- else if (is_blank_context)
- ;
- else
- new_blank_lines_at_end = 0;
- patch += len;
- size -= len;
- hunk_linenr++;
- }
- if (inaccurate_eof &&
- old > oldlines && old[-1] == '\n' &&
- newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
- old--;
- strbuf_setlen(&newlines, newlines.len - 1);
- }
-
- leading = frag->leading;
- trailing = frag->trailing;
-
- /*
- * A hunk to change lines at the beginning would begin with
- * @@ -1,L +N,M @@
- * but we need to be careful. -U0 that inserts before the second
- * line also has this pattern.
- *
- * And a hunk to add to an empty file would begin with
- * @@ -0,0 +N,M @@
- *
- * In other words, a hunk that is (frag->oldpos <= 1) with or
- * without leading context must match at the beginning.
- */
- match_beginning = (!frag->oldpos ||
- (frag->oldpos == 1 && !unidiff_zero));
-
- /*
- * A hunk without trailing lines must match at the end.
- * However, we simply cannot tell if a hunk must match end
- * from the lack of trailing lines if the patch was generated
- * with unidiff without any context.
- */
- match_end = !unidiff_zero && !trailing;
-
- pos = frag->newpos ? (frag->newpos - 1) : 0;
- preimage.buf = oldlines;
- preimage.len = old - oldlines;
- postimage.buf = newlines.buf;
- postimage.len = newlines.len;
- preimage.line = preimage.line_allocated;
- postimage.line = postimage.line_allocated;
-
- for (;;) {
-
- applied_pos = find_pos(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))
- break;
- if (match_beginning || match_end) {
- match_beginning = match_end = 0;
- continue;
- }
-
- /*
- * Reduce the number of context lines; reduce both
- * leading and trailing if they are equal otherwise
- * just reduce the larger context.
- */
- if (leading >= trailing) {
- remove_first_line(&preimage);
- remove_first_line(&postimage);
- pos--;
- leading--;
- }
- if (trailing > leading) {
- remove_last_line(&preimage);
- remove_last_line(&postimage);
- trailing--;
- }
- }
-
- if (applied_pos >= 0) {
- 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,
- found_new_blank_lines_at_end);
- if (ws_error_action == correct_ws_error) {
- while (new_blank_lines_at_end--)
- remove_last_line(&postimage);
- }
- /*
- * We would want to prevent write_out_results()
- * from taking place in apply_patch() that follows
- * the callchain led us here, which is:
- * 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 (apply_verbosely && applied_pos != pos) {
- int offset = applied_pos - pos;
- if (apply_in_reverse)
- offset = 0 - offset;
- fprintf_ln(stderr,
- Q_("Hunk #%d succeeded at %d (offset %d line).",
- "Hunk #%d succeeded at %d (offset %d lines).",
- offset),
- nth_fragment, applied_pos + 1, offset);
- }
-
- /*
- * Warn if it was necessary to reduce the number
- * of context lines.
- */
- if ((leading != frag->leading) ||
- (trailing != frag->trailing))
- 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);
- } else {
- if (apply_verbosely)
- error(_("while searching for:\n%.*s"),
- (int)(old - oldlines), oldlines);
- }
-
-out:
- free(oldlines);
- strbuf_release(&newlines);
- free(preimage.line_allocated);
- free(postimage.line_allocated);
-
- return (applied_pos < 0);
-}
-
-static int apply_binary_fragment(struct image *img, struct patch *patch)
-{
- struct fragment *fragment = patch->fragments;
- unsigned long len;
- void *dst;
-
- if (!fragment)
- return error(_("missing binary patch data for '%s'"),
- patch->new_name ?
- patch->new_name :
- patch->old_name);
-
- /* Binary patch is irreversible without the optional second hunk */
- if (apply_in_reverse) {
- if (!fragment->next)
- return error("cannot reverse-apply a binary patch "
- "without the reverse hunk to '%s'",
- patch->new_name
- ? patch->new_name : patch->old_name);
- fragment = fragment->next;
- }
- switch (fragment->binary_patch_method) {
- case BINARY_DELTA_DEFLATED:
- dst = patch_delta(img->buf, img->len, fragment->patch,
- fragment->size, &len);
- if (!dst)
- return -1;
- clear_image(img);
- img->buf = dst;
- img->len = len;
- return 0;
- case BINARY_LITERAL_DEFLATED:
- clear_image(img);
- img->len = fragment->size;
- img->buf = xmemdupz(fragment->patch, img->len);
- return 0;
- }
- return -1;
-}
-
-/*
- * Replace "img" with the result of applying the binary patch.
- * The binary patch data itself in patch->fragment is still kept
- * 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)
-{
- const char *name = patch->old_name ? patch->old_name : patch->new_name;
- unsigned char sha1[20];
-
- /*
- * For safety, we require patch index line to contain
- * full 40-byte textual SHA1 for old and new, at least for now.
- */
- if (strlen(patch->old_sha1_prefix) != 40 ||
- strlen(patch->new_sha1_prefix) != 40 ||
- get_sha1_hex(patch->old_sha1_prefix, sha1) ||
- get_sha1_hex(patch->new_sha1_prefix, sha1))
- return error("cannot apply binary patch to '%s' "
- "without full index line", name);
-
- if (patch->old_name) {
- /*
- * See if the old one matches what the patch
- * applies to.
- */
- hash_sha1_file(img->buf, img->len, blob_type, sha1);
- if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
- return error("the patch applies to '%s' (%s), "
- "which does not match the "
- "current contents.",
- name, sha1_to_hex(sha1));
- }
- else {
- /* Otherwise, the old one must be empty. */
- if (img->len)
- return error("the patch applies to an empty "
- "'%s' but it is not empty", name);
- }
-
- get_sha1_hex(patch->new_sha1_prefix, sha1);
- if (is_null_sha1(sha1)) {
- clear_image(img);
- return 0; /* deletion patch */
- }
-
- if (has_sha1_file(sha1)) {
- /* We already have the postimage */
- enum object_type type;
- unsigned long size;
- char *result;
-
- result = read_sha1_file(sha1, &type, &size);
- if (!result)
- return error("the necessary postimage %s for "
- "'%s' cannot be read",
- patch->new_sha1_prefix, name);
- clear_image(img);
- img->buf = result;
- img->len = size;
- } else {
- /*
- * We have verified buf matches the preimage;
- * apply the patch data to it, which is stored
- * in the patch->fragments->{patch,size}.
- */
- if (apply_binary_fragment(img, patch))
- return error(_("binary patch does not apply to '%s'"),
- name);
-
- /* verify that the result matches */
- hash_sha1_file(img->buf, img->len, blob_type, sha1);
- if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
- return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
- name, patch->new_sha1_prefix, sha1_to_hex(sha1));
- }
-
- return 0;
-}
-
-static int apply_fragments(struct image *img, struct patch *patch)
-{
- struct fragment *frag = patch->fragments;
- const char *name = patch->old_name ? patch->old_name : patch->new_name;
- unsigned ws_rule = patch->ws_rule;
- unsigned inaccurate_eof = patch->inaccurate_eof;
- int nth = 0;
-
- if (patch->is_binary)
- return apply_binary(img, patch);
-
- while (frag) {
- nth++;
- if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
- error(_("patch failed: %s:%ld"), name, frag->oldpos);
- if (!apply_with_reject)
- return -1;
- frag->rejected = 1;
- }
- frag = frag->next;
- }
- return 0;
-}
-
-static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)
-{
- if (S_ISGITLINK(mode)) {
- strbuf_grow(buf, 100);
- strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1));
- } else {
- enum object_type type;
- unsigned long sz;
- char *result;
-
- result = read_sha1_file(sha1, &type, &sz);
- if (!result)
- return -1;
- /* XXX read_sha1_file NUL-terminates */
- strbuf_attach(buf, result, sz, sz + 1);
- }
- return 0;
-}
-
-static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf)
-{
- if (!ce)
- return 0;
- return read_blob_object(buf, ce->sha1, ce->ce_mode);
-}
-
-static struct patch *in_fn_table(const char *name)
-{
- struct string_list_item *item;
-
- if (name == NULL)
- return NULL;
-
- item = string_list_lookup(&fn_table, name);
- if (item != NULL)
- return (struct patch *)item->util;
-
- return NULL;
-}
-
-/*
- * item->util in the filename table records the status of the path.
- * Usually it points at a patch (whose result records the contents
- * of it after applying it), but it could be PATH_WAS_DELETED for a
- * path that a previously applied patch has already removed, or
- * PATH_TO_BE_DELETED for a path that a later patch would remove.
- *
- * The latter is needed to deal with a case where two paths A and B
- * are swapped by first renaming A to B and then renaming B to A;
- * moving A to B should not be prevented due to presence of B as we
- * will remove it in a later patch.
- */
-#define PATH_TO_BE_DELETED ((struct patch *) -2)
-#define PATH_WAS_DELETED ((struct patch *) -1)
-
-static int to_be_deleted(struct patch *patch)
-{
- return patch == PATH_TO_BE_DELETED;
-}
-
-static int was_deleted(struct patch *patch)
-{
- return patch == PATH_WAS_DELETED;
-}
-
-static void add_to_fn_table(struct patch *patch)
-{
- struct string_list_item *item;
-
- /*
- * Always add new_name unless patch is a deletion
- * This should cover the cases for normal diffs,
- * file creations and copies
- */
- if (patch->new_name != NULL) {
- item = string_list_insert(&fn_table, patch->new_name);
- item->util = patch;
- }
-
- /*
- * store a failure on rename/deletion cases because
- * 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->util = PATH_WAS_DELETED;
- }
-}
-
-static void prepare_fn_table(struct patch *patch)
-{
- /*
- * store information about incoming file deletion
- */
- 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->util = PATH_TO_BE_DELETED;
- }
- patch = patch->next;
- }
-}
-
-static int checkout_target(struct index_state *istate,
- struct cache_entry *ce, struct stat *st)
-{
- struct checkout costate;
-
- memset(&costate, 0, sizeof(costate));
- costate.base_dir = "";
- costate.refresh_cache = 1;
- costate.istate = istate;
- if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
- return error(_("cannot checkout %s"), ce->name);
- return 0;
-}
-
-static struct patch *previous_patch(struct patch *patch, int *gone)
-{
- struct patch *previous;
-
- *gone = 0;
- if (patch->is_copy || patch->is_rename)
- return NULL; /* "git" patches do not depend on the order */
-
- previous = in_fn_table(patch->old_name);
- if (!previous)
- return NULL;
-
- if (to_be_deleted(previous))
- return NULL; /* the deletion hasn't happened yet */
-
- if (was_deleted(previous))
- *gone = 1;
-
- return previous;
-}
-
-static int verify_index_match(const struct cache_entry *ce, struct stat *st)
-{
- if (S_ISGITLINK(ce->ce_mode)) {
- if (!S_ISDIR(st->st_mode))
- return -1;
- return 0;
- }
- return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
-}
-
-#define SUBMODULE_PATCH_WITHOUT_INDEX 1
-
-static int load_patch_target(struct strbuf *buf,
- const struct cache_entry *ce,
- struct stat *st,
- const char *name,
- unsigned expected_mode)
-{
- if (cached || check_index) {
- if (read_file_or_gitlink(ce, buf))
- return error(_("read of %s failed"), name);
- } else if (name) {
- if (S_ISGITLINK(expected_mode)) {
- if (ce)
- return read_file_or_gitlink(ce, buf);
- else
- return SUBMODULE_PATCH_WITHOUT_INDEX;
- } else if (has_symlink_leading_path(name, strlen(name))) {
- 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 0;
-}
-
-/*
- * We are about to apply "patch"; populate the "image" with the
- * current version we have, from the working tree or from the index,
- * depending on the situation e.g. --cached/--index. If we are
- * 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,
- struct patch *patch, struct stat *st,
- const struct cache_entry *ce)
-{
- struct strbuf buf = STRBUF_INIT;
- size_t len;
- char *img;
- struct patch *previous;
- int status;
-
- previous = previous_patch(patch, &status);
- if (status)
- return error(_("path %s has been renamed/deleted"),
- patch->old_name);
- if (previous) {
- /* We have a patched copy in memory; use that. */
- strbuf_add(&buf, previous->result, previous->resultsize);
- } else {
- status = load_patch_target(&buf, ce, st,
- patch->old_name, patch->old_mode);
- if (status < 0)
- return status;
- else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) {
- /*
- * There is no way to apply subproject
- * patch without looking at the index.
- * NEEDSWORK: shouldn't this be flagged
- * as an error???
- */
- free_fragment_list(patch->fragments);
- patch->fragments = NULL;
- } else if (status) {
- return error(_("read of %s failed"), patch->old_name);
- }
- }
-
- img = strbuf_detach(&buf, &len);
- prepare_image(image, img, len, !patch->is_binary);
- return 0;
-}
-
-static int three_way_merge(struct image *image,
- char *path,
- const unsigned char *base,
- const unsigned char *ours,
- const unsigned char *theirs)
-{
- mmfile_t base_file, our_file, their_file;
- mmbuffer_t result = { NULL };
- int status;
-
- read_mmblob(&base_file, base);
- read_mmblob(&our_file, ours);
- read_mmblob(&their_file, theirs);
- status = ll_merge(&result, path,
- &base_file, "base",
- &our_file, "ours",
- &their_file, "theirs", NULL);
- free(base_file.ptr);
- free(our_file.ptr);
- free(their_file.ptr);
- if (status < 0 || !result.ptr) {
- free(result.ptr);
- return -1;
- }
- clear_image(image);
- image->buf = result.ptr;
- image->len = result.size;
-
- return status;
-}
-
-/*
- * When directly falling back to add/add three-way merge, we read from
- * 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)
-{
- struct strbuf buf = STRBUF_INIT;
- int status, pos;
- size_t len;
- char *img;
- struct stat st;
- struct cache_entry *ce;
- char *name = patch->new_name;
- unsigned mode = patch->new_mode;
-
- if (!patch->is_new)
- die("BUG: patch to %s is not a creation", patch->old_name);
-
- pos = cache_name_pos(name, strlen(name));
- if (pos < 0)
- return error(_("%s: does not exist in index"), name);
- ce = active_cache[pos];
- if (lstat(name, &st)) {
- if (errno != ENOENT)
- return error(_("%s: %s"), name, strerror(errno));
- if (checkout_target(&the_index, ce, &st))
- return -1;
- }
- if (verify_index_match(ce, &st))
- return error(_("%s: does not match index"), name);
-
- status = load_patch_target(&buf, ce, &st, name, mode);
- if (status < 0)
- return status;
- else if (status)
- return -1;
- img = strbuf_detach(&buf, &len);
- prepare_image(image, img, len, !patch->is_binary);
- return 0;
-}
-
-static int try_threeway(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;
- size_t len;
- int status;
- char *img;
- struct image tmp_image;
-
- /* No point falling back to 3-way merge in these cases */
- if (patch->is_delete ||
- S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))
- return -1;
-
- /* Preimage the patch was prepared for */
- if (patch->is_new)
- write_sha1_file("", 0, blob_type, pre_sha1);
- else if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
- read_blob_object(&buf, pre_sha1, patch->old_mode))
- return error("repository lacks the necessary blob to fall back on 3-way merge.");
-
- fprintf(stderr, "Falling back to three-way merge...\n");
-
- 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) {
- clear_image(&tmp_image);
- return -1;
- }
- /* post_sha1[] is theirs */
- write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);
- clear_image(&tmp_image);
-
- /* our_sha1[] is ours */
- if (patch->is_new) {
- if (load_current(&tmp_image, patch))
- return error("cannot read the current contents of '%s'",
- patch->new_name);
- } else {
- if (load_preimage(&tmp_image, patch, st, ce))
- return error("cannot read the current contents of '%s'",
- patch->old_name);
- }
- write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);
- clear_image(&tmp_image);
-
- /* in-core three-way merge between post and our using pre as base */
- status = three_way_merge(image, patch->new_name,
- pre_sha1, our_sha1, post_sha1);
- if (status < 0) {
- fprintf(stderr, "Failed to fall back on three-way merge...\n");
- return status;
- }
-
- if (status) {
- patch->conflicted_threeway = 1;
- if (patch->is_new)
- oidclr(&patch->threeway_stage[0]);
- else
- 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);
- }
- return 0;
-}
-
-static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
-{
- struct image image;
-
- if (load_preimage(&image, patch, st, ce) < 0)
- return -1;
-
- if (patch->direct_to_threeway ||
- apply_fragments(&image, patch) < 0) {
- /* Note: with --reject, apply_fragments() returns 0 */
- if (!threeway || try_threeway(&image, patch, st, ce) < 0)
- return -1;
- }
- patch->result = image.buf;
- patch->resultsize = image.len;
- add_to_fn_table(patch);
- free(image.line_allocated);
-
- if (0 < patch->is_delete && patch->resultsize)
- return error(_("removal patch leaves file contents"));
-
- return 0;
-}
-
-/*
- * If "patch" that we are looking at modifies or deletes what we have,
- * we would want it not to lose any local modification we have, either
- * in the working tree or in the index.
- *
- * This also decides if a non-git patch is a creation patch or a
- * modification to an existing empty file. We do not check the state
- * of the current tree for a creation patch in this function; the caller
- * 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)
-{
- const char *old_name = patch->old_name;
- struct patch *previous = NULL;
- int stat_ret = 0, status;
- unsigned st_mode = 0;
-
- if (!old_name)
- return 0;
-
- assert(patch->is_new <= 0);
- previous = previous_patch(patch, &status);
-
- if (status)
- return error(_("path %s has been renamed/deleted"), old_name);
- if (previous) {
- st_mode = previous->new_mode;
- } else if (!cached) {
- stat_ret = lstat(old_name, st);
- if (stat_ret && errno != ENOENT)
- return error(_("%s: %s"), old_name, strerror(errno));
- }
-
- if (check_index && !previous) {
- int pos = cache_name_pos(old_name, strlen(old_name));
- if (pos < 0) {
- if (patch->is_new < 0)
- goto is_new;
- return error(_("%s: does not exist in index"), old_name);
- }
- *ce = active_cache[pos];
- if (stat_ret < 0) {
- if (checkout_target(&the_index, *ce, st))
- return -1;
- }
- if (!cached && verify_index_match(*ce, st))
- return error(_("%s: does not match index"), old_name);
- if (cached)
- st_mode = (*ce)->ce_mode;
- } else if (stat_ret < 0) {
- if (patch->is_new < 0)
- goto is_new;
- return error(_("%s: %s"), old_name, strerror(errno));
- }
-
- if (!cached && !previous)
- st_mode = ce_mode_from_stat(*ce, st->st_mode);
-
- if (patch->is_new < 0)
- patch->is_new = 0;
- if (!patch->old_mode)
- patch->old_mode = st_mode;
- if ((st_mode ^ patch->old_mode) & S_IFMT)
- return error(_("%s: wrong type"), old_name);
- if (st_mode != patch->old_mode)
- warning(_("%s has type %o, expected %o"),
- old_name, st_mode, patch->old_mode);
- if (!patch->new_mode && !patch->is_delete)
- patch->new_mode = st_mode;
- return 0;
-
- is_new:
- patch->is_new = 1;
- patch->is_delete = 0;
- free(patch->old_name);
- patch->old_name = NULL;
- return 0;
-}
-
-
-#define EXISTS_IN_INDEX 1
-#define EXISTS_IN_WORKTREE 2
-
-static int check_to_create(const char *new_name, int ok_if_exists)
-{
- struct stat nst;
-
- if (check_index &&
- cache_name_pos(new_name, strlen(new_name)) >= 0 &&
- !ok_if_exists)
- return EXISTS_IN_INDEX;
- if (cached)
- return 0;
-
- if (!lstat(new_name, &nst)) {
- if (S_ISDIR(nst.st_mode) || ok_if_exists)
- return 0;
- /*
- * A leading component of new_name might be a symlink
- * that is going to be removed with this patch, but
- * still pointing at somewhere that has the path.
- * In such a case, path "new_name" does not exist as
- * far as git is concerned.
- */
- if (has_symlink_leading_path(new_name, strlen(new_name)))
- return 0;
-
- return EXISTS_IN_WORKTREE;
- } else if ((errno != ENOENT) && (errno != ENOTDIR)) {
- return error("%s: %s", new_name, strerror(errno));
- }
- 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)
-{
- struct string_list_item *ent;
-
- ent = string_list_lookup(&symlink_changes, path);
- if (!ent) {
- ent = string_list_insert(&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)
-{
- struct string_list_item *ent;
-
- ent = string_list_lookup(&symlink_changes, path);
- if (!ent)
- return 0;
- return (uintptr_t)ent->util;
-}
-
-static void prepare_symlink_changes(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);
-
- 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);
- }
-}
-
-static int path_is_beyond_symlink_1(struct strbuf *name)
-{
- do {
- unsigned int change;
-
- while (--name->len && name->buf[name->len] != '/')
- ; /* scan backwards */
- if (!name->len)
- break;
- name->buf[name->len] = '\0';
- change = check_symlink_changes(name->buf);
- if (change & SYMLINK_IN_RESULT)
- return 1;
- if (change & SYMLINK_GOES_AWAY)
- /*
- * This cannot be "return 0", because we may
- * see a new one created at a higher level.
- */
- continue;
-
- /* otherwise, check the preimage */
- if (check_index) {
- struct cache_entry *ce;
-
- ce = cache_file_exists(name->buf, name->len, ignore_case);
- if (ce && S_ISLNK(ce->ce_mode))
- return 1;
- } else {
- struct stat st;
- if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode))
- return 1;
- }
- } while (1);
- return 0;
-}
-
-static int path_is_beyond_symlink(const char *name_)
-{
- int ret;
- struct strbuf name = STRBUF_INIT;
-
- assert(*name_ != '\0');
- strbuf_addstr(&name, name_);
- ret = path_is_beyond_symlink_1(&name);
- strbuf_release(&name);
-
- return ret;
-}
-
-static void die_on_unsafe_path(struct patch *patch)
-{
- const char *old_name = NULL;
- const char *new_name = NULL;
- if (patch->is_delete)
- old_name = patch->old_name;
- else if (!patch->is_new && !patch->is_copy)
- old_name = patch->old_name;
- if (!patch->is_delete)
- new_name = patch->new_name;
-
- if (old_name && !verify_path(old_name))
- die(_("invalid path '%s'"), old_name);
- if (new_name && !verify_path(new_name))
- die(_("invalid path '%s'"), new_name);
-}
-
-/*
- * 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)
-{
- struct stat st;
- const char *old_name = patch->old_name;
- const char *new_name = patch->new_name;
- const char *name = old_name ? old_name : new_name;
- struct cache_entry *ce = NULL;
- struct patch *tpatch;
- int ok_if_exists;
- int status;
-
- patch->rejected = 1; /* we will drop this after we succeed */
-
- status = check_preimage(patch, &ce, &st);
- if (status)
- return status;
- old_name = patch->old_name;
-
- /*
- * A type-change diff is always split into a patch to delete
- * old, immediately followed by a patch to create new (see
- * diff.c::run_diff()); in such a case it is Ok that the entry
- * to be deleted by the previous patch is still in the working
- * tree and in the index.
- *
- * A patch to swap-rename between A and B would first rename A
- * to B and then rename B to A. While applying the first one,
- * the presence of B should not stop A from getting renamed to
- * B; ask to_be_deleted() about the later rename. Removal of
- * B and rename from A to B is handled the same way by asking
- * was_deleted().
- */
- if ((tpatch = in_fn_table(new_name)) &&
- (was_deleted(tpatch) || to_be_deleted(tpatch)))
- ok_if_exists = 1;
- else
- ok_if_exists = 0;
-
- if (new_name &&
- ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
- int err = check_to_create(new_name, ok_if_exists);
-
- if (err && threeway) {
- patch->direct_to_threeway = 1;
- } else switch (err) {
- case 0:
- break; /* happy */
- case EXISTS_IN_INDEX:
- return error(_("%s: already exists in index"), new_name);
- break;
- case EXISTS_IN_WORKTREE:
- return error(_("%s: already exists in working directory"),
- new_name);
- default:
- return err;
- }
-
- if (!patch->new_mode) {
- if (0 < patch->is_new)
- patch->new_mode = S_IFREG | 0644;
- else
- patch->new_mode = patch->old_mode;
- }
- }
-
- if (new_name && old_name) {
- int same = !strcmp(old_name, new_name);
- if (!patch->new_mode)
- patch->new_mode = patch->old_mode;
- if ((patch->old_mode ^ patch->new_mode) & S_IFMT) {
- if (same)
- return error(_("new mode (%o) of %s does not "
- "match old mode (%o)"),
- patch->new_mode, new_name,
- patch->old_mode);
- else
- return error(_("new mode (%o) of %s does not "
- "match old mode (%o) of %s"),
- patch->new_mode, new_name,
- patch->old_mode, old_name);
- }
- }
-
- if (!unsafe_paths)
- die_on_unsafe_path(patch);
-
- /*
- * An attempt to read from or delete a path that is beyond a
- * symbolic link will be prevented by load_patch_target() that
- * is called at the beginning of apply_data() so we do not
- * have to worry about a patch marked with "is_delete" bit
- * here. We however need to make sure that the patch result
- * is not deposited to a path that is beyond a symbolic link
- * here.
- */
- if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
- return error(_("affected file '%s' is beyond a symbolic link"),
- patch->new_name);
-
- if (apply_data(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)
-{
- int err = 0;
-
- prepare_symlink_changes(patch);
- prepare_fn_table(patch);
- while (patch) {
- if (apply_verbosely)
- say_patch_name(stderr,
- _("Checking patch %s..."), patch);
- err |= check_patch(patch);
- patch = patch->next;
- }
- return err;
-}
-
-/* This function tries to read the sha1 from the current index */
-static int get_current_sha1(const char *path, unsigned char *sha1)
-{
- int pos;
-
- if (read_cache() < 0)
- return -1;
- pos = cache_name_pos(path, strlen(path));
- if (pos < 0)
- return -1;
- hashcpy(sha1, active_cache[pos]->sha1);
- return 0;
-}
-
-static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20])
-{
- /*
- * A usable gitlink patch has only one fragment (hunk) that looks like:
- * @@ -1 +1 @@
- * -Subproject commit <old sha1>
- * +Subproject commit <new sha1>
- * or
- * @@ -1 +0,0 @@
- * -Subproject commit <old sha1>
- * for a removal patch.
- */
- struct fragment *hunk = p->fragments;
- static const char heading[] = "-Subproject commit ";
- char *preimage;
-
- if (/* does the patch have only one hunk? */
- hunk && !hunk->next &&
- /* is its preimage one line? */
- hunk->oldpos == 1 && hunk->oldlines == 1 &&
- /* does preimage begin with the heading? */
- (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
- starts_with(++preimage, heading) &&
- /* does it record full SHA-1? */
- !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
- preimage[sizeof(heading) + 40 - 1] == '\n' &&
- /* does the abbreviated name on the index line agree with it? */
- starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
- return 0; /* it all looks fine */
-
- /* we may have full object name on the index line */
- return get_sha1_hex(p->old_sha1_prefix, sha1);
-}
-
-/* Build an index that contains the just the files needed for a 3way merge */
-static void build_fake_ancestor(struct patch *list, const char *filename)
-{
- struct patch *patch;
- struct index_state result = { NULL };
- static struct lock_file lock;
-
- /* Once we start supporting the reverse patch, it may be
- * worth showing the new sha1 prefix, but until then...
- */
- for (patch = list; patch; patch = patch->next) {
- unsigned char sha1[20];
- struct cache_entry *ce;
- const char *name;
-
- name = patch->old_name ? patch->old_name : patch->new_name;
- if (0 < patch->is_new)
- continue;
-
- if (S_ISGITLINK(patch->old_mode)) {
- if (!preimage_sha1_in_gitlink_patch(patch, sha1))
- ; /* ok, the textual part looks sane */
- else
- die("sha1 information is lacking or useless for submodule %s",
- name);
- } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
- ; /* ok */
- } else if (!patch->lines_added && !patch->lines_deleted) {
- /* mode-only change: update the current */
- if (get_current_sha1(patch->old_name, sha1))
- die("mode change for %s, which is not "
- "in current HEAD", name);
- } else
- die("sha1 information is lacking or useless "
- "(%s).", name);
-
- ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0);
- if (!ce)
- die(_("make_cache_entry failed for path '%s'"), name);
- if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
- die ("Could not add %s to temporary index", name);
- }
-
- hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
- if (write_locked_index(&result, &lock, COMMIT_LOCK))
- die ("Could not write temporary index to %s", filename);
-
- discard_index(&result);
-}
-
-static void stat_patch_list(struct patch *patch)
-{
- int files, adds, dels;
-
- for (files = adds = dels = 0 ; patch ; patch = patch->next) {
- files++;
- adds += patch->lines_added;
- dels += patch->lines_deleted;
- show_stats(patch);
- }
-
- print_stat_summary(stdout, files, adds, dels);
-}
-
-static void numstat_patch_list(struct patch *patch)
-{
- for ( ; patch; patch = patch->next) {
- const char *name;
- name = patch->new_name ? patch->new_name : patch->old_name;
- if (patch->is_binary)
- printf("-\t-\t");
- else
- printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
- write_name_quoted(name, stdout, line_termination);
- }
-}
-
-static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
-{
- if (mode)
- printf(" %s mode %06o %s\n", newdelete, mode, name);
- else
- printf(" %s %s\n", newdelete, name);
-}
-
-static void show_mode_change(struct patch *p, int show_name)
-{
- if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
- if (show_name)
- printf(" mode change %06o => %06o %s\n",
- p->old_mode, p->new_mode, p->new_name);
- else
- printf(" mode change %06o => %06o\n",
- p->old_mode, p->new_mode);
- }
-}
-
-static void show_rename_copy(struct patch *p)
-{
- const char *renamecopy = p->is_rename ? "rename" : "copy";
- const char *old, *new;
-
- /* Find common prefix */
- old = p->old_name;
- new = p->new_name;
- while (1) {
- const char *slash_old, *slash_new;
- slash_old = strchr(old, '/');
- slash_new = strchr(new, '/');
- if (!slash_old ||
- !slash_new ||
- slash_old - old != slash_new - new ||
- memcmp(old, new, slash_new - new))
- break;
- old = slash_old + 1;
- new = slash_new + 1;
- }
- /* p->old_name thru old is the common prefix, and old and new
- * through the end of names are renames
- */
- if (old != p->old_name)
- printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
- (int)(old - p->old_name), p->old_name,
- old, new, p->score);
- else
- printf(" %s %s => %s (%d%%)\n", renamecopy,
- p->old_name, p->new_name, p->score);
- show_mode_change(p, 0);
-}
-
-static void summary_patch_list(struct patch *patch)
-{
- struct patch *p;
-
- for (p = patch; p; p = p->next) {
- if (p->is_new)
- show_file_mode_name("create", p->new_mode, p->new_name);
- else if (p->is_delete)
- show_file_mode_name("delete", p->old_mode, p->old_name);
- else {
- if (p->is_rename || p->is_copy)
- show_rename_copy(p);
- else {
- if (p->score) {
- printf(" rewrite %s (%d%%)\n",
- p->new_name, p->score);
- show_mode_change(p, 0);
- }
- else
- show_mode_change(p, 1);
- }
- }
- }
-}
-
-static void patch_stats(struct patch *patch)
-{
- int lines = patch->lines_added + patch->lines_deleted;
-
- if (lines > max_change)
- 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 (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;
- }
-}
-
-static void remove_file(struct patch *patch, int rmdir_empty)
-{
- if (update_index) {
- if (remove_file_from_cache(patch->old_name) < 0)
- die(_("unable to remove %s from index"), patch->old_name);
- }
- if (!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)
-{
- struct stat st;
- struct cache_entry *ce;
- int namelen = strlen(path);
- unsigned ce_size = cache_entry_size(namelen);
-
- if (!update_index)
- return;
-
- ce = xcalloc(1, ce_size);
- memcpy(ce->name, path, namelen);
- ce->ce_mode = create_ce_mode(mode);
- ce->ce_flags = create_ce_flags(0);
- ce->ce_namelen = namelen;
- if (S_ISGITLINK(mode)) {
- const char *s;
-
- if (!skip_prefix(buf, "Subproject commit ", &s) ||
- get_sha1_hex(s, ce->sha1))
- die(_("corrupt patch for submodule %s"), path);
- } else {
- if (!cached) {
- if (lstat(path, &st) < 0)
- die_errno(_("unable to stat newly created file '%s'"),
- path);
- fill_stat_cache_info(ce, &st);
- }
- if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
- die(_("unable to create backing store for newly created file %s"), path);
- }
- if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
- die(_("unable to add cache entry for %s"), path);
-}
-
-static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
-{
- int fd;
- struct strbuf nbuf = STRBUF_INIT;
-
- if (S_ISGITLINK(mode)) {
- struct stat st;
- if (!lstat(path, &st) && S_ISDIR(st.st_mode))
- return 0;
- return mkdir(path, 0777);
- }
-
- if (has_symlinks && S_ISLNK(mode))
- /* Although buf:size is counted string, it also is NUL
- * terminated.
- */
- return symlink(buf, path);
-
- fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
- if (fd < 0)
- return -1;
-
- if (convert_to_working_tree(path, buf, size, &nbuf)) {
- size = nbuf.len;
- buf = nbuf.buf;
- }
- write_or_die(fd, buf, size);
- strbuf_release(&nbuf);
-
- if (close(fd) < 0)
- die_errno(_("closing file '%s'"), path);
- return 0;
-}
-
-/*
- * We optimistically assume that the directories exist,
- * 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)
-{
- if (cached)
- return;
- if (!try_create_file(path, mode, buf, size))
- return;
-
- if (errno == ENOENT) {
- if (safe_create_leading_directories(path))
- return;
- if (!try_create_file(path, mode, buf, size))
- return;
- }
-
- if (errno == EEXIST || errno == EACCES) {
- /* We may be trying to create a file where a directory
- * used to be.
- */
- struct stat st;
- if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
- errno = EEXIST;
- }
-
- if (errno == EEXIST) {
- unsigned int nr = getpid();
-
- for (;;) {
- char newpath[PATH_MAX];
- mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
- if (!try_create_file(newpath, mode, buf, size)) {
- if (!rename(newpath, path))
- return;
- unlink_or_warn(newpath);
- break;
- }
- if (errno != EEXIST)
- break;
- ++nr;
- }
- }
- die_errno(_("unable to write file '%s' mode %o"), path, mode);
-}
-
-static void add_conflicted_stages_file(struct patch *patch)
-{
- int stage, namelen;
- unsigned ce_size, mode;
- struct cache_entry *ce;
-
- if (!update_index)
- return;
- namelen = strlen(patch->new_name);
- ce_size = cache_entry_size(namelen);
- mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
-
- remove_file_from_cache(patch->new_name);
- for (stage = 1; stage < 4; stage++) {
- 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].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)
-{
- char *path = patch->new_name;
- unsigned mode = patch->new_mode;
- unsigned long size = patch->resultsize;
- char *buf = patch->result;
-
- if (!mode)
- mode = S_IFREG | 0644;
- create_one_file(path, mode, buf, size);
-
- if (patch->conflicted_threeway)
- add_conflicted_stages_file(patch);
- else
- add_index_file(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)
-{
- if (patch->is_delete > 0) {
- if (phase == 0)
- remove_file(patch, 1);
- return;
- }
- if (patch->is_new > 0 || patch->is_copy) {
- if (phase == 1)
- create_file(patch);
- return;
- }
- /*
- * Rename or modification boils down to the same
- * thing: remove the old, write the new
- */
- if (phase == 0)
- remove_file(patch, patch->is_rename);
- if (phase == 1)
- create_file(patch);
-}
-
-static int write_out_one_reject(struct patch *patch)
-{
- FILE *rej;
- char namebuf[PATH_MAX];
- struct fragment *frag;
- int cnt = 0;
- struct strbuf sb = STRBUF_INIT;
-
- for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
- if (!frag->rejected)
- continue;
- cnt++;
- }
-
- if (!cnt) {
- if (apply_verbosely)
- say_patch_name(stderr,
- _("Applied patch %s cleanly."), patch);
- return 0;
- }
-
- /* This should not happen, because a removal patch that leaves
- * contents are marked "rejected" at the patch level.
- */
- if (!patch->new_name)
- die(_("internal error"));
-
- /* Say this even without --verbose */
- strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...",
- "Applying patch %%s with %d rejects...",
- cnt),
- cnt);
- say_patch_name(stderr, sb.buf, patch);
- strbuf_release(&sb);
-
- cnt = strlen(patch->new_name);
- if (ARRAY_SIZE(namebuf) <= cnt + 5) {
- cnt = ARRAY_SIZE(namebuf) - 5;
- warning(_("truncating .rej filename to %.*s.rej"),
- cnt - 1, patch->new_name);
- }
- memcpy(namebuf, patch->new_name, cnt);
- memcpy(namebuf + cnt, ".rej", 5);
-
- rej = fopen(namebuf, "w");
- if (!rej)
- return error(_("cannot open %s: %s"), namebuf, strerror(errno));
-
- /* Normal git tools never deal with .rej, so do not pretend
- * this is a git patch by saying --git or giving extended
- * headers. While at it, maybe please "kompare" that wants
- * the trailing TAB and some garbage at the end of line ;-).
- */
- fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
- patch->new_name, patch->new_name);
- for (cnt = 1, frag = patch->fragments;
- frag;
- cnt++, frag = frag->next) {
- if (!frag->rejected) {
- fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
- continue;
- }
- fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
- fprintf(rej, "%.*s", frag->size, frag->patch);
- if (frag->patch[frag->size-1] != '\n')
- fputc('\n', rej);
- }
- fclose(rej);
- return -1;
-}
-
-static int write_out_results(struct patch *list)
-{
- int phase;
- int errs = 0;
- struct patch *l;
- struct string_list cpath = STRING_LIST_INIT_DUP;
-
- for (phase = 0; phase < 2; phase++) {
- l = list;
- while (l) {
- if (l->rejected)
- errs = 1;
- else {
- write_out_one_result(l, phase);
- if (phase == 1) {
- if (write_out_one_reject(l))
- errs = 1;
- if (l->conflicted_threeway) {
- string_list_append(&cpath, l->new_name);
- errs = 1;
- }
- }
- }
- l = l->next;
- }
- }
-
- if (cpath.nr) {
- struct string_list_item *item;
-
- string_list_sort(&cpath);
- for_each_string_list_item(item, &cpath)
- fprintf(stderr, "U %s\n", item->string);
- string_list_clear(&cpath, 0);
-
- rerere(0);
- }
-
- return errs;
-}
-
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)
-{
- 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;
- read_patch_file(&buf, fd);
- offset = 0;
- while (offset < buf.len) {
- struct patch *patch;
- int nr;
-
- 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)
- break;
- if (apply_in_reverse)
- reverse_patches(patch);
- if (use_patch(patch)) {
- patch_stats(patch);
- *listp = patch;
- listp = &patch->next;
- }
- else {
- free_patch(patch);
- skipped_patch++;
- }
- offset += nr;
- }
-
- if (!list && !skipped_patch)
- die(_("unrecognized input"));
-
- if (whitespace_error && (ws_error_action == die_on_ws_error))
- apply = 0;
-
- update_index = check_index && apply;
- if (update_index && newfd < 0)
- newfd = hold_locked_index(&lock_file, 1);
-
- if (check_index) {
- if (read_cache() < 0)
- die(_("unable to read index file"));
- }
-
- if ((check || apply) &&
- check_patch_list(list) < 0 &&
- !apply_with_reject)
- exit(1);
-
- if (apply && write_out_results(list)) {
- if (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 (diffstat)
- stat_patch_list(list);
-
- if (numstat)
- numstat_patch_list(list);
-
- if (summary)
- summary_patch_list(list);
-
- free_patch_list(list);
- strbuf_release(&buf);
- string_list_clear(&fn_table, 0);
- return 0;
-}
-
-static void git_apply_config(void)
-{
- git_config_get_string_const("apply.whitespace", &apply_default_whitespace);
- git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace);
- git_config(git_default_config, NULL);
-}
-
-static int option_parse_exclude(const struct option *opt,
- const char *arg, int unset)
-{
- add_name_limit(arg, 1);
- return 0;
-}
-
-static int option_parse_include(const struct option *opt,
- const char *arg, int unset)
+int cmd_apply(int argc, const char **argv, const char *prefix)
{
- add_name_limit(arg, 0);
- 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)
-{
- if (unset)
- line_termination = '\n';
- else
- line_termination = 0;
- return 0;
-}
-
-static int option_parse_space_change(const struct option *opt,
- const char *arg, int unset)
-{
- if (unset)
- ws_ignore_action = ignore_ws_none;
- else
- 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);
- return 0;
-}
-
-static int option_parse_directory(const struct option *opt,
- const char *arg, int unset)
-{
- strbuf_reset(&root);
- strbuf_addstr(&root, arg);
- strbuf_complete(&root, '/');
- return 0;
-}
-
-int cmd_apply(int argc, const char **argv, const char *prefix_)
-{
- int i;
- int errs = 0;
- int is_not_gitdir = !startup_info->have_repository;
int force_apply = 0;
+ int options = 0;
+ int ret;
+ struct apply_state state;
- const char *whitespace_option = NULL;
-
- struct option builtin_apply_options[] = {
- { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
- N_("don't apply changes matching the given path"),
- 0, option_parse_exclude },
- { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
- N_("apply changes matching the given path"),
- 0, option_parse_include },
- { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
- N_("remove <num> leading slashes from traditional diff paths"),
- 0, option_parse_p },
- OPT_BOOL(0, "no-add", &no_add,
- N_("ignore additions made by the patch")),
- OPT_BOOL(0, "stat", &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,
- N_("show number of added and deleted lines in decimal notation")),
- OPT_BOOL(0, "summary", &summary,
- N_("instead of applying the patch, output a summary for the input")),
- OPT_BOOL(0, "check", &check,
- N_("instead of applying the patch, see if the patch is applicable")),
- OPT_BOOL(0, "index", &check_index,
- N_("make sure the patch is applicable to the current index")),
- OPT_BOOL(0, "cached", &cached,
- N_("apply a patch without touching the working tree")),
- OPT_BOOL(0, "unsafe-paths", &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,
- N_( "attempt three-way merge if a patch does not apply")),
- OPT_FILENAME(0, "build-fake-ancestor", &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,
- N_("ensure at least <n> lines of context match")),
- { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
- N_("detect new or modified lines that have whitespace errors"),
- 0, option_parse_whitespace },
- { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
- N_("ignore changes in whitespace when finding context"),
- PARSE_OPT_NOARG, option_parse_space_change },
- { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
- N_("ignore changes in whitespace when finding context"),
- PARSE_OPT_NOARG, option_parse_space_change },
- OPT_BOOL('R', "reverse", &apply_in_reverse,
- N_("apply the patch in reverse")),
- OPT_BOOL(0, "unidiff-zero", &unidiff_zero,
- N_("don't expect at least one line of context")),
- OPT_BOOL(0, "reject", &apply_with_reject,
- N_("leave the rejected hunks in corresponding *.rej files")),
- OPT_BOOL(0, "allow-overlap", &allow_overlap,
- N_("allow overlapping hunks")),
- OPT__VERBOSE(&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"),
- 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);
-
- argc = parse_options(argc, argv, 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;
+ if (init_apply_state(&state, prefix, &lock_file))
+ exit(128);
- for (i = 0; i < argc; i++) {
- const char *arg = argv[i];
- int fd;
+ argc = apply_parse_options(argc, argv,
+ &state, &force_apply, &options,
+ apply_usage);
- 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);
+ if (check_apply_state(&state, force_apply))
+ exit(128);
- 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);
- }
+ ret = apply_all_patches(&state, argc, argv, options);
- if (update_index) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
+ clear_apply_state(&state);
- return !!errs;
+ return ret;
}
diff --git a/builtin/archive.c b/builtin/archive.c
index a1e3b94..f863465 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -47,10 +47,10 @@ static int run_remote_archiver(int argc, const char **argv,
if (name_hint) {
const char *format = archive_format_from_filename(name_hint);
if (format)
- packet_write(fd[1], "argument --format=%s\n", format);
+ packet_write_fmt(fd[1], "argument --format=%s\n", format);
}
for (i = 1; i < argc; i++)
- packet_write(fd[1], "argument %s\n", argv[i]);
+ packet_write_fmt(fd[1], "argument %s\n", argv[i]);
packet_flush(fd[1]);
buf = packet_read_line(fd[0], NULL);
@@ -85,8 +85,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
const char *output = NULL;
const char *remote = NULL;
struct option local_opts[] = {
- OPT_STRING('o', "output", &output, N_("file"),
- N_("write the archive to this file")),
+ OPT_FILENAME('o', "output", &output,
+ N_("write the archive to this file")),
OPT_STRING(0, "remote", &remote, N_("repo"),
N_("retrieve the archive from remote repository <repo>")),
OPT_STRING(0, "exec", &exec, N_("command"),
@@ -105,5 +105,5 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
- return write_archive(argc, argv, prefix, 1, output, 0);
+ return write_archive(argc, argv, prefix, output, 0);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 1df13cf..f7aa95f 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -28,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>");
@@ -50,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 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
@@ -118,7 +120,7 @@ struct origin {
*/
struct blame_entry *suspects;
mmfile_t file;
- unsigned char blob_sha1[20];
+ struct object_id blob_oid;
unsigned mode;
/* guilty gets set when shipping any suspects to the final
* blame list instead of other commits
@@ -127,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};
@@ -135,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);
@@ -148,8 +154,8 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
*/
int textconv_object(const char *path,
unsigned mode,
- const unsigned char *sha1,
- int sha1_valid,
+ const struct object_id *oid,
+ int oid_valid,
char **buf,
unsigned long *buf_size)
{
@@ -157,7 +163,7 @@ int textconv_object(const char *path,
struct userdiff_driver *textconv;
df = alloc_filespec(path);
- fill_filespec(df, sha1, sha1_valid, mode);
+ fill_filespec(df, oid->hash, oid_valid, mode);
textconv = get_textconv(df);
if (!textconv) {
free_filespec(df);
@@ -182,15 +188,16 @@ static void fill_origin_blob(struct diff_options *opt,
num_read_blob++;
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
+ textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
;
else
- file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
+ file->ptr = read_sha1_file(o->blob_oid.hash, &type,
+ &file_size);
file->size = file_size;
if (!file->ptr)
die("Cannot read blob %s for path %s",
- sha1_to_hex(o->blob_sha1),
+ oid_to_hex(&o->blob_oid),
o->path);
o->file = *file;
}
@@ -459,13 +466,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;
- size_t pathlen = strlen(path) + 1;
- o = xcalloc(1, sizeof(*o) + pathlen);
+ FLEX_ALLOC_STR(o, path, path);
o->commit = commit;
o->refcnt = 1;
o->next = commit->util;
commit->util = o;
- memcpy(o->path, path, pathlen); /* includes NUL */
return o;
}
@@ -504,17 +509,17 @@ static struct origin *get_origin(struct scoreboard *sb,
*/
static int fill_blob_sha1_and_mode(struct origin *origin)
{
- if (!is_null_sha1(origin->blob_sha1))
+ if (!is_null_oid(&origin->blob_oid))
return 0;
if (get_tree_entry(origin->commit->object.oid.hash,
origin->path,
- origin->blob_sha1, &origin->mode))
+ origin->blob_oid.hash, &origin->mode))
goto error_out;
- if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
+ if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
goto error_out;
return 0;
error_out:
- hashclr(origin->blob_sha1);
+ oidclr(&origin->blob_oid);
origin->mode = S_IFINVALID;
return -1;
}
@@ -568,7 +573,7 @@ static struct origin *find_origin(struct scoreboard *sb,
if (!diff_queued_diff.nr) {
/* The path is the same as parent */
porigin = get_origin(sb, parent, origin->path);
- hashcpy(porigin->blob_sha1, origin->blob_sha1);
+ oidcpy(&porigin->blob_oid, &origin->blob_oid);
porigin->mode = origin->mode;
} else {
/*
@@ -594,7 +599,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);
+ oidcpy(&porigin->blob_oid, &p->one->oid);
porigin->mode = p->one->mode;
break;
case 'A':
@@ -604,7 +609,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;
}
@@ -640,21 +645,24 @@ 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);
+ oidcpy(&porigin->blob_oid, &p->one->oid);
porigin->mode = p->one->mode;
break;
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
/*
* Append a new blame entry to a given output queue.
*/
-static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e)
+static void add_blame_entry(struct blame_entry ***queue,
+ const struct blame_entry *src)
{
+ struct blame_entry *e = xmalloc(sizeof(*e));
+ memcpy(e, src, sizeof(*e));
origin_incref(e->suspect);
e->next = **queue;
@@ -755,21 +763,15 @@ static void split_blame(struct blame_entry ***blamed,
struct blame_entry *split,
struct blame_entry *e)
{
- struct blame_entry *new_entry;
-
if (split[0].suspect && split[2].suspect) {
/* The first part (reuse storage for the existing entry e) */
dup_entry(unblamed, e, &split[0]);
/* The last part -- me */
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
- add_blame_entry(unblamed, new_entry);
+ add_blame_entry(unblamed, &split[2]);
/* ... and the middle part -- parent */
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
- add_blame_entry(blamed, new_entry);
+ add_blame_entry(blamed, &split[1]);
}
else if (!split[0].suspect && !split[2].suspect)
/*
@@ -780,18 +782,12 @@ static void split_blame(struct blame_entry ***blamed,
else if (split[0].suspect) {
/* me and then parent */
dup_entry(unblamed, e, &split[0]);
-
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
- add_blame_entry(blamed, new_entry);
+ add_blame_entry(blamed, &split[1]);
}
else {
/* parent and then me */
dup_entry(blamed, e, &split[1]);
-
- new_entry = xmalloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
- add_blame_entry(unblamed, new_entry);
+ add_blame_entry(unblamed, &split[2]);
}
}
@@ -975,7 +971,7 @@ 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)",
oid_to_hex(&parent->commit->object.oid),
oid_to_hex(&target->commit->object.oid));
@@ -1124,7 +1120,7 @@ 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)",
oid_to_hex(&parent->commit->object.oid));
/* remainder, if any, all match the preimage */
@@ -1304,7 +1300,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);
+ oidcpy(&norigin->blob_oid, &p->one->oid);
norigin->mode = p->one->mode;
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
@@ -1338,7 +1334,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);
}
/*
@@ -1454,15 +1450,14 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
porigin = find(sb, p, origin);
if (!porigin)
continue;
- if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
+ if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
pass_whole_blame(sb, origin, porigin);
origin_decref(porigin);
goto finish;
}
for (j = same = 0; j < i; j++)
if (sg_origin[j] &&
- !hashcmp(sg_origin[j]->blob_sha1,
- porigin->blob_sha1)) {
+ !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {
same = 1;
break;
}
@@ -1696,13 +1691,23 @@ static void get_commit_info(struct commit *commit,
}
/*
+ * Write out any suspect information which depends on the path. This must be
+ * handled separately from emit_one_suspect_detail(), because a given commit
+ * may have changes in multiple paths. So this needs to appear each time
+ * we mention a new group.
+ *
* To allow LF and other nonportable characters in pathnames,
* they are c-style quoted as needed.
*/
-static void write_filename_info(const char *path)
+static void write_filename_info(struct origin *suspect)
{
+ if (suspect->previous) {
+ struct origin *prev = suspect->previous;
+ printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
+ write_name_quoted(prev->path, stdout, '\n');
+ }
printf("filename ");
- write_name_quoted(path, stdout, '\n');
+ write_name_quoted(suspect->path, stdout, '\n');
}
/*
@@ -1731,11 +1736,6 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
printf("summary %s\n", ci.summary.buf);
if (suspect->commit->object.flags & UNINTERESTING)
printf("boundary\n");
- if (suspect->previous) {
- struct origin *prev = suspect->previous;
- printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
- write_name_quoted(prev->path, stdout, '\n');
- }
commit_info_destroy(&ci);
@@ -1746,7 +1746,8 @@ 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;
@@ -1755,9 +1756,11 @@ static void found_guilty_entry(struct blame_entry *ent)
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);
+ write_filename_info(suspect);
maybe_flush_or_die(stdout, "stdout");
}
+ pi->blamed_lines += ent->num_lines;
+ display_progress(pi->progress, pi->blamed_lines);
}
/*
@@ -1768,6 +1771,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;
@@ -1809,7 +1817,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;
@@ -1825,6 +1833,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,
@@ -1870,7 +1880,7 @@ static void emit_porcelain_details(struct origin *suspect, int repeat)
{
if (emit_one_suspect_detail(suspect, repeat) ||
(suspect->commit->object.flags & MORE_THAN_ONE_PATH))
- write_filename_info(suspect->path);
+ write_filename_info(suspect);
}
static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
@@ -1882,7 +1892,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
struct origin *suspect = ent->suspect;
char hex[GIT_SHA1_HEXSZ + 1];
- sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
+ oid_to_hex_r(hex, &suspect->commit->object.oid);
printf("%s %d %d %d\n",
hex,
ent->s_lno + 1,
@@ -1922,12 +1932,12 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
get_commit_info(suspect->commit, &ci, 1);
- sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
+ oid_to_hex_r(hex, &suspect->commit->object.oid);
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
- int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : abbrev;
+ int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
if (suspect->commit->object.flags & UNINTERESTING) {
if (blank_boundary)
@@ -2042,7 +2052,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;
@@ -2096,7 +2107,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
unsigned largest_score = 0;
struct blame_entry *e;
int compute_auto_abbrev = (abbrev < 0);
- int auto_abbrev = default_abbrev;
+ int auto_abbrev = DEFAULT_ABBREV;
for (e = sb->ent; e; e = e->next) {
struct origin *suspect = e->suspect;
@@ -2205,6 +2216,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (git_diff_heuristic_config(var, value, cb) < 0)
+ return -1;
if (userdiff_config(var, value) < 0)
return -1;
@@ -2214,26 +2227,35 @@ 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.oid.hash;
- unsigned char blob_sha1[20];
+ const struct object_id *commit_oid = &parents->item->object.oid;
+ struct object_id blob_oid;
unsigned mode;
- if (!get_tree_entry(commit_sha1, path, blob_sha1, &mode) &&
- sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
+ if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
+ sha1_object_info(blob_oid.hash, 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)
+static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
{
struct commit *parent;
- parent = lookup_commit_reference(sha1);
+ parent = lookup_commit_reference(oid->hash);
if (!parent)
- die("no such commit %s", sha1_to_hex(sha1));
+ die("no such commit %s", oid_to_hex(oid));
return &commit_list_insert(parent, tail)->next;
}
@@ -2250,10 +2272,10 @@ static void append_merge_parents(struct commit_list **tail)
}
while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
- unsigned char sha1[20];
- if (line.len < 40 || get_sha1_hex(line.buf, sha1))
+ struct object_id oid;
+ if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
- tail = append_parent(tail, sha1);
+ tail = append_parent(tail, &oid);
}
close(merge_head);
strbuf_release(&line);
@@ -2282,7 +2304,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
struct commit *commit;
struct origin *origin;
struct commit_list **parent_tail, *parent;
- unsigned char head_sha1[20];
+ struct object_id head_oid;
struct strbuf buf = STRBUF_INIT;
const char *ident;
time_t now;
@@ -2291,16 +2313,17 @@ 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;
commit->date = now;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
die("no such ref: HEAD");
- parent_tail = append_parent(parent_tail, head_sha1);
+ parent_tail = append_parent(parent_tail, &head_oid);
append_merge_parents(parent_tail);
verify_working_tree_path(commit, path);
@@ -2341,7 +2364,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
+ textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
@@ -2363,7 +2386,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
convert_to_git(path, buf.buf, buf.len, &buf, 0);
origin->file.ptr = buf.buf;
origin->file.size = buf.len;
- pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
+ pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
/*
* Read the current index, replace the path entry with
@@ -2385,18 +2408,13 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
}
size = cache_entry_size(len);
ce = xcalloc(1, size);
- hashcpy(ce->sha1, origin->blob_sha1);
+ oidcpy(&ce->oid, &origin->blob_oid);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = len;
ce->ce_mode = create_ce_mode(mode);
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
- /*
- * 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;
@@ -2413,8 +2431,7 @@ static struct commit *find_single_final(struct rev_info *revs,
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 (found)
@@ -2435,6 +2452,41 @@ static char *prepare_final(struct scoreboard *sb)
return xstrdup_or_null(name);
}
+static const char *dwim_reverse_initial(struct scoreboard *sb)
+{
+ /*
+ * DWIM "git blame --reverse ONE -- PATH" as
+ * "git blame --reverse ONE..HEAD -- PATH" but only do so
+ * when it makes sense.
+ */
+ struct object *obj;
+ struct commit *head_commit;
+ unsigned char head_sha1[20];
+
+ if (sb->revs->pending.nr != 1)
+ return NULL;
+
+ /* Is that sole rev a committish? */
+ obj = sb->revs->pending.objects[0].item;
+ obj = deref_tag(obj, NULL, 0);
+ if (obj->type != OBJ_COMMIT)
+ return NULL;
+
+ /* Do we have HEAD? */
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
+ return NULL;
+ head_commit = lookup_commit_reference_gently(head_sha1, 1);
+ if (!head_commit)
+ return NULL;
+
+ /* Turn "ONE" into "ONE..HEAD" then */
+ obj->flags |= UNINTERESTING;
+ add_pending_object(sb->revs, &head_commit->object, "HEAD");
+
+ sb->final = (struct commit *)obj;
+ return sb->revs->pending.objects[0].name;
+}
+
static char *prepare_initial(struct scoreboard *sb)
{
int i;
@@ -2449,19 +2501,21 @@ 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)
- die("More than one commit to dig down to %s and %s?",
+ die("More than one commit to dig up from, %s and %s?",
revs->pending.objects[i].name,
final_commit_name);
sb->final = (struct commit *) obj;
final_commit_name = revs->pending.objects[i].name;
}
+
if (!final_commit_name)
- die("No commit to dig down to?");
+ final_commit_name = dwim_reverse_initial(sb);
+ if (!final_commit_name)
+ die("No commit to dig up from?");
return xstrdup(final_commit_name);
}
@@ -2510,16 +2564,17 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
enum object_type type;
struct commit *final_commit = NULL;
- 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 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),
@@ -2531,6 +2586,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+
+ /*
+ * The following two options are parsed by parse_revision_opt()
+ * and are only included here to get included in the "-h"
+ * output:
+ */
+ { OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },
+
OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
@@ -2555,6 +2618,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);
@@ -2576,12 +2640,22 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
}
parse_done:
no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
+ xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
argc = parse_options_end(&ctx);
- if (0 < abbrev)
+ 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 && abbrev < GIT_SHA1_HEXSZ)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
+ else if (!abbrev)
+ abbrev = GIT_SHA1_HEXSZ;
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);
@@ -2607,6 +2681,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;
@@ -2697,7 +2774,7 @@ parse_done:
sb.commits.compare = compare_commits_by_commit_date;
}
else if (contents_from)
- die("--contents and --reverse 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;
@@ -2717,12 +2794,12 @@ parse_done:
add_pending_object(&revs, &(sb.final->object), ":");
}
else if (contents_from)
- die("Cannot use --contents with final commit object name");
+ 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");
+ die(_("--reverse and --first-parent together require specified latest commit"));
}
/*
@@ -2749,7 +2826,7 @@ parse_done:
}
if (oidcmp(&c->object.oid, &sb.final->object.oid))
- die("--reverse --first-parent together require range along first-parent chain");
+ die(_("--reverse --first-parent together require range along first-parent chain"));
}
if (is_null_oid(&sb.final->object.oid)) {
@@ -2760,26 +2837,26 @@ parse_done:
else {
o = get_origin(&sb, sb.final, path);
if (fill_blob_sha1_and_mode(o))
- die("no such path %s in %s", path, final_commit_name);
+ die(_("no such path %s in %s"), path, final_commit_name);
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
- textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
+ textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf,
&sb.final_buf_size))
;
else
- sb.final_buf = read_sha1_file(o->blob_sha1, &type,
+ sb.final_buf = read_sha1_file(o->blob_oid.hash, &type,
&sb.final_buf_size);
if (!sb.final_buf)
- die("Cannot read blob %s for path %s",
- sha1_to_hex(o->blob_sha1),
+ die(_("cannot read blob %s for path %s"),
+ oid_to_hex(&o->blob_oid),
path);
}
num_read_blob++;
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);
@@ -2790,7 +2867,9 @@ parse_done:
&bottom, &top, sb.path))
usage(blame_usage);
if (lno < top || ((lno || bottom) && lno < bottom))
- die("file %s has only %lu lines", path, lno);
+ die(Q_("file %s has only %lu line",
+ "file %s has only %lu lines",
+ lno), path, lno);
if (bottom < 1)
bottom = 1;
if (top < 1)
@@ -2828,11 +2907,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 3f6c825..0552c42 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -20,6 +20,7 @@
#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]"),
@@ -27,20 +28,21 @@ static const char * const builtin_branch_usage[] = {
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]"),
+ N_("git branch [<options>] [-r | -a] [--format]"),
NULL
};
static const char *head;
-static unsigned char head_sha1[20];
+static struct object_id head_oid;
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
- GIT_COLOR_NORMAL, /* PLAIN */
- GIT_COLOR_RED, /* REMOTE */
- GIT_COLOR_NORMAL, /* LOCAL */
- GIT_COLOR_GREEN, /* CURRENT */
- GIT_COLOR_BLUE, /* UPSTREAM */
+ GIT_COLOR_NORMAL, /* PLAIN */
+ GIT_COLOR_RED, /* REMOTE */
+ GIT_COLOR_NORMAL, /* LOCAL */
+ GIT_COLOR_GREEN, /* CURRENT */
+ GIT_COLOR_BLUE, /* UPSTREAM */
};
enum color_branch {
BRANCH_COLOR_RESET = 0,
@@ -116,13 +118,13 @@ static int branch_merged(int kind, const char *name,
if (kind == FILTER_REFS_BRANCHES) {
struct branch *branch = branch_get(name);
const char *upstream = branch_get_upstream(branch, NULL);
- unsigned char sha1[20];
+ struct object_id oid;
if (upstream &&
(reference_name = reference_name_to_free =
resolve_refdup(upstream, RESOLVE_REF_READING,
- sha1, NULL)) != NULL)
- reference_rev = lookup_commit_reference(sha1);
+ oid.hash, NULL)) != NULL)
+ reference_rev = lookup_commit_reference(oid.hash);
}
if (!reference_rev)
reference_rev = head_rev;
@@ -152,10 +154,10 @@ static int branch_merged(int kind, const char *name,
}
static int check_branch_commit(const char *branchname, const char *refname,
- const unsigned char *sha1, struct commit *head_rev,
+ const struct object_id *oid, struct commit *head_rev,
int kinds, int force)
{
- struct commit *rev = lookup_commit_reference(sha1);
+ struct commit *rev = lookup_commit_reference(oid->hash);
if (!rev) {
error(_("Couldn't look up commit object for '%s'"), refname);
return -1;
@@ -182,54 +184,62 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
int quiet)
{
struct commit *head_rev = NULL;
- unsigned char sha1[20];
+ struct object_id oid;
char *name = NULL;
const char *fmt;
int i;
int ret = 0;
int remote_branch = 0;
struct strbuf bname = STRBUF_INIT;
+ unsigned allowed_interpret;
switch (kinds) {
case FILTER_REFS_REMOTES:
fmt = "refs/remotes/%s";
/* For subsequent UI messages */
remote_branch = 1;
+ allowed_interpret = INTERPRET_BRANCH_REMOTE;
force = 1;
break;
case FILTER_REFS_BRANCHES:
fmt = "refs/heads/%s";
+ allowed_interpret = INTERPRET_BRANCH_LOCAL;
break;
default:
die(_("cannot use -a with -d"));
}
if (!force) {
- head_rev = lookup_commit_reference(head_sha1);
+ head_rev = lookup_commit_reference(head_oid.hash);
if (!head_rev)
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 == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) {
- error(_("Cannot delete the branch '%s' "
- "which you are currently on."), bname.buf);
- ret = 1;
- continue;
- }
-
+ strbuf_branchname(&bname, argv[i], allowed_interpret);
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,
+ oid.hash, &flags);
if (!target) {
error(remote_branch
? _("remote-tracking branch '%s' not found.")
@@ -239,20 +249,20 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
- check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
+ check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
force)) {
ret = 1;
- continue;
+ goto next;
}
- if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
+ if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash,
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
@@ -261,9 +271,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
bname.buf,
(flags & REF_ISBROKEN) ? "broken"
: (flags & REF_ISSYMREF) ? target
- : find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ : find_unique_abbrev(oid.hash, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
+
+ next:
+ free(target);
}
free(name);
@@ -271,214 +284,109 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
return(ret);
}
-static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
- int show_upstream_ref)
+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
{
- 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;
-
- if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
- if (!upstream)
- return;
- upstream_is_gone = 1;
- }
-
- if (show_upstream_ref) {
- ref = shorten_unambiguous_ref(upstream, 0);
- if (want_color(branch_use_color))
- strbuf_addf(&fancy, "%s%s%s",
- branch_get_color(BRANCH_COLOR_UPSTREAM),
- ref, branch_get_color(BRANCH_COLOR_RESET));
- else
- strbuf_addstr(&fancy, ref);
- }
+ 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;
- if (upstream_is_gone) {
- if (show_upstream_ref)
- strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
- else
- added_decoration = 0;
- } else if (!ours && !theirs) {
- if (show_upstream_ref)
- strbuf_addf(stat, _("[%s]"), fancy.buf);
- else
- added_decoration = 0;
- } else if (!ours) {
- if (show_upstream_ref)
- strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
- else
- strbuf_addf(stat, _("[behind %d]"), theirs);
+ skip_prefix(it->refname, "refs/heads/", &desc);
+ skip_prefix(it->refname, "refs/remotes/", &desc);
+ if (it->kind == FILTER_REFS_DETACHED_HEAD) {
+ char *head_desc = get_head_description();
+ w = utf8_strwidth(head_desc);
+ free(head_desc);
+ } else
+ w = utf8_strwidth(desc);
- } else if (!theirs) {
- if (show_upstream_ref)
- strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
- else
- strbuf_addf(stat, _("[ahead %d]"), ours);
- } else {
- if (show_upstream_ref)
- strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
- fancy.buf, ours, theirs);
- else
- strbuf_addf(stat, _("[ahead %d, behind %d]"),
- ours, theirs);
+ if (it->kind == FILTER_REFS_REMOTES)
+ w += remote_bonus;
+ if (w > max)
+ max = w;
}
- strbuf_release(&fancy);
- if (added_decoration)
- strbuf_addch(stat, ' ');
- free(ref);
+ return max;
}
-static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
- struct ref_filter *filter, const char *refname)
+static const char *quote_literal_for_format(const char *s)
{
- struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
- const char *sub = _(" **** invalid ref ****");
- struct commit *commit = item->commit;
+ static struct strbuf buf = STRBUF_INIT;
- if (!parse_commit(commit)) {
- pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
- sub = subject.buf;
+ strbuf_reset(&buf);
+ while (*s) {
+ const char *ep = strchrnul(s, '%');
+ if (s < ep)
+ strbuf_add(&buf, s, ep - s);
+ if (*ep == '%') {
+ strbuf_addstr(&buf, "%%");
+ s = ep + 1;
+ } else {
+ s = ep;
+ }
}
-
- 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.oid.hash, filter->abbrev),
- stat.buf, sub);
- strbuf_release(&stat);
- strbuf_release(&subject);
+ return buf.buf;
}
-static char *get_head_description(void)
+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
{
- 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);
-}
+ struct strbuf fmt = STRBUF_INIT;
+ struct strbuf local = STRBUF_INIT;
+ struct strbuf remote = STRBUF_INIT;
-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;
- const char *prefix = "";
- const char *desc = item->refname;
- char *to_free = NULL;
+ strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else) %%(end)",
+ branch_get_color(BRANCH_COLOR_CURRENT));
- switch (item->kind) {
- case FILTER_REFS_BRANCHES:
- skip_prefix(desc, "refs/heads/", &desc);
- if (!filter->detached && !strcmp(desc, head))
- current = 1;
- else
- color = BRANCH_COLOR_LOCAL;
- break;
- case FILTER_REFS_REMOTES:
- skip_prefix(desc, "refs/remotes/", &desc);
- color = BRANCH_COLOR_REMOTE;
- prefix = remote_prefix;
- break;
- case FILTER_REFS_DETACHED_HEAD:
- desc = to_free = get_head_description();
- current = 1;
- break;
- default:
- color = BRANCH_COLOR_PLAIN;
- break;
- }
+ if (filter->verbose) {
+ struct strbuf obname = STRBUF_INIT;
- c = ' ';
- if (current) {
- c = '*';
- color = BRANCH_COLOR_CURRENT;
- }
+ if (filter->abbrev < 0)
+ strbuf_addf(&obname, "%%(objectname:short)");
+ else if (!filter->abbrev)
+ strbuf_addf(&obname, "%%(objectname)");
+ else
+ strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
- strbuf_addf(&name, "%s%s", prefix, 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,
- branch_get_color(BRANCH_COLOR_RESET));
- } else
- strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
- name.buf, branch_get_color(BRANCH_COLOR_RESET));
+ strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
+ strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
+ strbuf_addf(&local, " %s ", obname.buf);
- if (item->symref) {
- skip_prefix(item->symref, "refs/remotes/", &desc);
- strbuf_addf(&out, " -> %s", desc);
- }
- else if (filter->verbose)
- /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
- add_verbose_info(&out, item, filter, desc);
- if (column_active(colopts)) {
- assert(!filter->verbose && "--column and --verbose are incompatible");
- string_list_append(&output, out.buf);
+ if (filter->verbose > 1)
+ strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
+ "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
+ branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+ else
+ strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
+
+ strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
+ "%%(if)%%(symref)%%(then) -> %%(symref:short)"
+ "%%(else) %s %%(contents:subject)%%(end)",
+ branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
+ branch_get_color(BRANCH_COLOR_RESET), obname.buf);
+ strbuf_release(&obname);
} else {
- printf("%s\n", out.buf);
+ strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
+ branch_get_color(BRANCH_COLOR_RESET));
+ strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
+ branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),
+ branch_get_color(BRANCH_COLOR_RESET));
}
- strbuf_release(&name);
- strbuf_release(&out);
- free(to_free);
-}
-
-static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
-{
- 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);
+ strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
- if (it->kind == FILTER_REFS_REMOTES)
- w += remote_bonus;
- if (w > max)
- max = w;
- }
- return max;
+ strbuf_release(&local);
+ strbuf_release(&remote);
+ return strbuf_detach(&fmt, NULL);
}
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
{
int i;
struct ref_array array;
int maxwidth = 0;
const char *remote_prefix = "";
+ struct strbuf out = STRBUF_INIT;
+ char *to_free = NULL;
/*
* If we are listing more than just remote branches,
@@ -490,27 +398,55 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
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();
+ if (!format)
+ format = to_free = build_format(filter, maxwidth, remote_prefix);
+ verify_ref_format(format);
+
ref_array_sort(sorting, &array);
- for (i = 0; i < array.nr; i++)
- format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
+ for (i = 0; i < array.nr; i++) {
+ format_ref_array_item(array.items[i], format, 0, &out);
+ if (column_active(colopts)) {
+ assert(!filter->verbose && "--column and --verbose are incompatible");
+ /* format to a string_list to let print_columns() do its job */
+ string_list_append(&output, out.buf);
+ } else {
+ fwrite(out.buf, 1, out.len, stdout);
+ putchar('\n');
+ }
+ strbuf_release(&out);
+ }
ref_array_clear(&array);
+ free(to_free);
+}
+
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+ struct worktree **worktrees = get_worktrees(0);
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+
+ if (!wt->is_detached)
+ continue;
+
+ if (is_worktree_being_rebased(wt, target))
+ die(_("Branch %s is being rebased at %s"),
+ target, wt->path);
+
+ if (is_worktree_being_bisected(wt, target))
+ die(_("Branch %s is being bisected at %s"),
+ target, wt->path);
+ }
+
+ free_worktrees(worktrees);
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -542,20 +478,22 @@ 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);
if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
die(_("Branch rename failed"));
- strbuf_release(&logmsg);
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, logmsg.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
+ strbuf_release(&logmsg);
+
strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
strbuf_release(&oldref);
strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
@@ -570,7 +508,6 @@ static const char edit_description[] = "BRANCH_DESCRIPTION";
static int edit_branch_description(const char *branch_name)
{
- int status;
struct strbuf buf = STRBUF_INIT;
struct strbuf name = STRBUF_INIT;
@@ -578,15 +515,11 @@ 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);
- if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
- 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);
@@ -595,11 +528,11 @@ static int edit_branch_description(const char *branch_name)
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)
@@ -610,7 +543,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
const char *new_upstream = NULL;
enum branch_track track;
struct ref_filter filter;
+ int icase = 0;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ const char *format = NULL;
struct option options[] = {
OPT_GROUP(N_("Generic options")),
@@ -621,13 +556,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
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_BOOL(0, "unset-upstream", &unset_upstream, "Unset 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, N_("Unset the upstream info")),
OPT__COLOR(&branch_use_color, N_("use colored output")),
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
FILTER_REFS_REMOTES),
OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
+ OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
+ OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
OPT__ABBREV(&filter.abbrev),
OPT_GROUP(N_("Specific git-branch actions:")),
@@ -651,9 +588,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
N_("print only branches of the object"), 0, parse_opt_object_name
},
+ OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+ OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
OPT_END(),
};
+ setup_ref_filter_porcelain_msg();
+
memset(&filter, 0, sizeof(filter));
filter.kind = FILTER_REFS_BRANCHES;
filter.abbrev = -1;
@@ -665,7 +606,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", 0, head_sha1, NULL);
+ head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
@@ -679,7 +620,8 @@ 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 (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
+ if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
+ filter.no_commit)
list = 1;
if (!!delete + !!rename + !!new_upstream +
@@ -688,6 +630,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (filter.abbrev == -1)
filter.abbrev = DEFAULT_ABBREV;
+ filter.ignore_case = icase;
+
finalize_colopts(&colopts, -1);
if (filter.verbose) {
if (explicitly_enable_column(colopts))
@@ -709,7 +653,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
filter.kind |= FILTER_REFS_DETACHED_HEAD;
filter.name_patterns = argv;
- print_ref_list(&filter, sorting);
+ /*
+ * 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();
+ sorting->ignore_case = icase;
+ print_ref_list(&filter, sorting, format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
return 0;
@@ -772,7 +726,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
* create_branch takes care of setting up the tracking
* info and making sure new_upstream is correct
*/
- create_branch(head, branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
+ create_branch(branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
} else if (unset_upstream) {
struct branch *branch = branch_get(argv[0]);
struct strbuf buf = STRBUF_INIT;
@@ -818,7 +772,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
branch_existed = ref_exists(branch->refname);
- create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
+ create_branch(argv[0], (argc == 2) ? argv[1] : head,
force, reflog, 0, quiet, track);
/*
@@ -829,8 +783,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/bundle.c b/builtin/bundle.c
index 4883a43..d0de59b 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -20,21 +20,15 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
struct bundle_header header;
const char *cmd, *bundle_file;
int bundle_fd = -1;
- char buffer[PATH_MAX];
if (argc < 3)
usage(builtin_bundle_usage);
cmd = argv[1];
- bundle_file = argv[2];
+ bundle_file = prefix_filename(prefix, argv[2]);
argc -= 2;
argv += 2;
- if (prefix && bundle_file[0] != '/') {
- snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
- bundle_file = buffer;
- }
-
memset(&header, 0, sizeof(header));
if (strcmp(cmd, "create") && (bundle_fd =
read_bundle_header(bundle_file, &header)) < 0)
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index c0fd8db..8b85cb8 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -17,32 +17,63 @@ struct batch_options {
int print_contents;
int buffer_output;
int all_objects;
+ int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
const char *format;
};
+static const char *force_path;
+
+static int filter_object(const char *path, unsigned mode,
+ const struct object_id *oid,
+ char **buf, unsigned long *size)
+{
+ enum object_type type;
+
+ *buf = read_sha1_file(oid->hash, &type, size);
+ if (!*buf)
+ return error(_("cannot read object %s '%s'"),
+ oid_to_hex(oid), path);
+ if ((type == OBJ_BLOB) && S_ISREG(mode)) {
+ struct strbuf strbuf = STRBUF_INIT;
+ if (convert_to_working_tree(path, *buf, *size, &strbuf)) {
+ free(*buf);
+ *size = strbuf.len;
+ *buf = strbuf_detach(&strbuf, NULL);
+ }
+ }
+
+ return 0;
+}
+
static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
int unknown_type)
{
- unsigned char sha1[20];
+ struct object_id oid;
enum object_type type;
char *buf;
unsigned long size;
struct object_context obj_context;
- struct object_info oi = {NULL};
+ struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = LOOKUP_REPLACE_OBJECT;
+ const char *path = force_path;
if (unknown_type)
flags |= LOOKUP_UNKNOWN_OBJECT;
- if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
+ if (get_sha1_with_context(obj_name, 0, oid.hash, &obj_context))
die("Not a valid object name %s", obj_name);
+ if (!path)
+ path = obj_context.path;
+ if (obj_context.mode == S_IFINVALID)
+ obj_context.mode = 0100644;
+
buf = NULL;
switch (opt) {
case 't':
oi.typename = &sb;
- if (sha1_object_info_extended(sha1, &oi, flags) < 0)
+ if (sha1_object_info_extended(oid.hash, &oi, flags) < 0)
die("git cat-file: could not get object info");
if (sb.len) {
printf("%s\n", sb.buf);
@@ -53,24 +84,34 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 's':
oi.sizep = &size;
- if (sha1_object_info_extended(sha1, &oi, flags) < 0)
+ if (sha1_object_info_extended(oid.hash, &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);
+ return !has_object_file(&oid);
+
+ case 'w':
+ if (!path[0])
+ die("git cat-file --filters %s: <object> must be "
+ "<sha1:path>", obj_name);
+
+ if (filter_object(path, obj_context.mode,
+ &oid, &buf, &size))
+ return -1;
+ break;
case 'c':
- if (!obj_context.path[0])
+ if (!path[0])
die("git cat-file --textconv %s: <object> must be <sha1:path>",
obj_name);
- if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
+ if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size))
break;
case 'p':
- type = sha1_object_info(sha1, NULL);
+ type = sha1_object_info(oid.hash, NULL);
if (type < 0)
die("Not a valid object name %s", obj_name);
@@ -83,8 +124,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
}
if (type == OBJ_BLOB)
- return stream_blob_to_fd(1, sha1, NULL, 0);
- buf = read_sha1_file(sha1, &type, &size);
+ return stream_blob_to_fd(1, &oid, NULL, 0);
+ buf = read_sha1_file(oid.hash, &type, &size);
if (!buf)
die("Cannot read object %s", obj_name);
@@ -93,19 +134,19 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
case 0:
if (type_from_string(exp_type) == OBJ_BLOB) {
- unsigned char blob_sha1[20];
- if (sha1_object_info(sha1, NULL) == OBJ_TAG) {
- char *buffer = read_sha1_file(sha1, &type, &size);
+ struct object_id blob_oid;
+ if (sha1_object_info(oid.hash, NULL) == OBJ_TAG) {
+ char *buffer = read_sha1_file(oid.hash, &type, &size);
const char *target;
if (!skip_prefix(buffer, "object ", &target) ||
- get_sha1_hex(target, blob_sha1))
- die("%s not a valid tag", sha1_to_hex(sha1));
+ get_oid_hex(target, &blob_oid))
+ die("%s not a valid tag", oid_to_hex(&oid));
free(buffer);
} else
- hashcpy(blob_sha1, sha1);
+ oidcpy(&blob_oid, &oid);
- if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
- return stream_blob_to_fd(1, blob_sha1, NULL, 0);
+ if (sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
+ return stream_blob_to_fd(1, &blob_oid, NULL, 0);
/*
* we attempted to dereference a tag to a blob
* and failed; there may be new dereference
@@ -113,7 +154,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
* fall-back to the usual case.
*/
}
- buf = read_object_with_reference(sha1, exp_type, &size, NULL);
+ buf = read_object_with_reference(oid.hash, exp_type, &size, NULL);
break;
default:
@@ -128,12 +169,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
}
struct expand_data {
- unsigned char sha1[20];
+ struct object_id oid;
enum object_type type;
unsigned long size;
- unsigned long disk_size;
+ off_t disk_size;
const char *rest;
- unsigned char delta_base_sha1[20];
+ struct object_id delta_base_oid;
/*
* If mark_query is true, we do not expand anything, but rather
@@ -154,6 +195,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)
@@ -169,7 +217,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
if (is_atom("objectname", atom, len)) {
if (!data->mark_query)
- strbuf_addstr(sb, sha1_to_hex(data->sha1));
+ strbuf_addstr(sb, oid_to_hex(&data->oid));
} else if (is_atom("objecttype", atom, len)) {
if (data->mark_query)
data->info.typep = &data->type;
@@ -184,7 +232,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;
@@ -192,9 +240,10 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
strbuf_addstr(sb, data->rest);
} else if (is_atom("deltabase", atom, len)) {
if (data->mark_query)
- data->info.delta_base_sha1 = data->delta_base_sha1;
+ data->info.delta_base_sha1 = data->delta_base_oid.hash;
else
- strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1));
+ strbuf_addstr(sb,
+ oid_to_hex(&data->delta_base_oid));
} else
die("unknown format element: %.*s", len, atom);
}
@@ -225,28 +274,53 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
static void print_object_or_die(struct batch_options *opt, struct expand_data *data)
{
- const unsigned char *sha1 = data->sha1;
+ const struct object_id *oid = &data->oid;
assert(data->info.typep);
if (data->type == OBJ_BLOB) {
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));
+ if (opt->cmdmode) {
+ char *contents;
+ unsigned long size;
+
+ if (!data->rest)
+ die("missing path for '%s'", oid_to_hex(oid));
+
+ if (opt->cmdmode == 'w') {
+ if (filter_object(data->rest, 0100644, oid,
+ &contents, &size))
+ die("could not convert '%s' %s",
+ oid_to_hex(oid), data->rest);
+ } else if (opt->cmdmode == 'c') {
+ enum object_type type;
+ if (!textconv_object(data->rest, 0100644, oid,
+ 1, &contents, &size))
+ contents = read_sha1_file(oid->hash, &type,
+ &size);
+ if (!contents)
+ die("could not convert '%s' %s",
+ oid_to_hex(oid), data->rest);
+ } else
+ die("BUG: invalid cmdmode: %c", opt->cmdmode);
+ batch_write(opt, contents, size);
+ free(contents);
+ } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0)
+ die("unable to stream %s to stdout", oid_to_hex(oid));
}
else {
enum object_type type;
unsigned long size;
void *contents;
- contents = read_sha1_file(sha1, &type, &size);
+ contents = read_sha1_file(oid->hash, &type, &size);
if (!contents)
- die("object %s disappeared", sha1_to_hex(sha1));
+ die("object %s disappeared", oid_to_hex(oid));
if (type != data->type)
- die("object %s changed type!?", sha1_to_hex(sha1));
+ die("object %s changed type!?", oid_to_hex(oid));
if (data->info.sizep && size != data->size)
- die("object %s changed size!?", sha1_to_hex(sha1));
+ die("object %s changed size!?", oid_to_hex(oid));
batch_write(opt, contents, size);
free(contents);
@@ -258,8 +332,10 @@ static void batch_object_write(const char *obj_name, struct batch_options *opt,
{
struct strbuf buf = STRBUF_INIT;
- if (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));
+ if (!data->skip_object_info &&
+ sha1_object_info_extended(data->oid.hash, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
+ printf("%s missing\n",
+ obj_name ? obj_name : oid_to_hex(&data->oid));
fflush(stdout);
return;
}
@@ -282,7 +358,7 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt,
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);
+ result = get_sha1_with_context(obj_name, flags, data->oid.hash, &ctx);
if (result != FOUND) {
switch (result) {
case MISSING_OBJECT:
@@ -325,27 +401,28 @@ struct object_cb_data {
struct expand_data *expand;
};
-static void batch_object_cb(const unsigned char sha1[20], void *vdata)
+static int batch_object_cb(const unsigned char sha1[20], void *vdata)
{
struct object_cb_data *data = vdata;
- hashcpy(data->expand->sha1, sha1);
+ hashcpy(data->expand->oid.hash, sha1);
batch_object_write(NULL, data->opt, data->expand);
+ return 0;
}
-static int batch_loose_object(const unsigned char *sha1,
+static int batch_loose_object(const struct object_id *oid,
const char *path,
void *data)
{
- sha1_array_append(data, sha1);
+ sha1_array_append(data, oid->hash);
return 0;
}
-static int batch_packed_object(const unsigned char *sha1,
+static int batch_packed_object(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
void *data)
{
- sha1_array_append(data, sha1);
+ sha1_array_append(data, oid->hash);
return 0;
}
@@ -368,6 +445,14 @@ static int batch_objects(struct batch_options *opt)
data.mark_query = 1;
strbuf_expand(&buf, opt->format, expand_format, &data);
data.mark_query = 0;
+ if (opt->cmdmode)
+ data.split_on_whitespace = 1;
+
+ if (opt->all_objects) {
+ struct object_info empty = OBJECT_INFO_INIT;
+ 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,
@@ -401,7 +486,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
@@ -425,8 +510,8 @@ static int batch_objects(struct batch_options *opt)
}
static const char * const cat_file_usage[] = {
- 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]"),
+ N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
+ N_("git cat-file (--batch | --batch-check) [--follow-symlinks] [--textconv | --filters]"),
NULL
};
@@ -471,6 +556,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
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_CMDMODE(0, "filters", &opt,
+ N_("for blob objects, run filters on object's content"), 'w'),
+ OPT_STRING(0, "path", &force_path, N_("blob"),
+ N_("use a specific path for --textconv/--filters")),
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")),
@@ -489,10 +578,13 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
git_config(git_cat_file_config, NULL);
+ batch.buffer_output = -1;
argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
if (opt) {
- if (argc == 1)
+ if (batch.enabled && (opt == 'c' || opt == 'w'))
+ batch.cmdmode = opt;
+ else if (argc == 1)
obj_name = argv[0];
else
usage_with_options(cat_file_usage, options);
@@ -504,14 +596,31 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
} else
usage_with_options(cat_file_usage, options);
}
- if (batch.enabled && (opt || argc)) {
- usage_with_options(cat_file_usage, options);
+ if (batch.enabled) {
+ if (batch.cmdmode != opt || argc)
+ usage_with_options(cat_file_usage, options);
+ if (batch.cmdmode && batch.all_objects)
+ die("--batch-all-objects cannot be combined with "
+ "--textconv nor with --filters");
}
if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) {
usage_with_options(cat_file_usage, options);
}
+ if (force_path && opt != 'c' && opt != 'w') {
+ error("--path=<path> needs --textconv or --filters");
+ usage_with_options(cat_file_usage, options);
+ }
+
+ if (force_path && batch.enabled) {
+ error("--path=<path> incompatible with --batch");
+ 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);
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 265c9ba..4d01ca0 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -24,12 +24,13 @@ static const struct option check_attr_options[] = {
OPT_END()
};
-static void output_attr(int cnt, struct git_attr_check *check,
- const char *file)
+static void output_attr(struct attr_check *check, const char *file)
{
int j;
+ int cnt = check->nr;
+
for (j = 0; j < cnt; j++) {
- const char *value = check[j].value;
+ const char *value = check->items[j].value;
if (ATTR_TRUE(value))
value = "set";
@@ -42,53 +43,56 @@ static void output_attr(int cnt, struct git_attr_check *check,
printf("%s%c" /* path */
"%s%c" /* attrname */
"%s%c" /* attrvalue */,
- file, 0, git_attr_name(check[j].attr), 0, value, 0);
+ file, 0,
+ git_attr_name(check->items[j].attr), 0, value, 0);
} else {
quote_c_style(file, NULL, stdout, 0);
- printf(": %s: %s\n", git_attr_name(check[j].attr), value);
+ printf(": %s: %s\n",
+ git_attr_name(check->items[j].attr), value);
}
-
}
}
-static void check_attr(const char *prefix, int cnt,
- struct git_attr_check *check, const char *file)
+static void check_attr(const char *prefix,
+ struct attr_check *check,
+ int collect_all,
+ const char *file)
{
char *full_path =
prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
- if (check != NULL) {
- if (git_check_attr(full_path, cnt, check))
- die("git_check_attr died");
- output_attr(cnt, check, file);
+
+ if (collect_all) {
+ git_all_attrs(full_path, check);
} else {
- if (git_all_attrs(full_path, &cnt, &check))
- die("git_all_attrs died");
- output_attr(cnt, check, file);
- free(check);
+ if (git_check_attr(full_path, check))
+ die("git_check_attr died");
}
+ output_attr(check, file);
+
free(full_path);
}
-static void check_attr_stdin_paths(const char *prefix, int cnt,
- struct git_attr_check *check)
+static void check_attr_stdin_paths(const char *prefix,
+ struct attr_check *check,
+ int collect_all)
{
- 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);
+ check_attr(prefix, check, collect_all, 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)
@@ -99,7 +103,7 @@ static NORETURN void error_with_usage(const char *msg)
int cmd_check_attr(int argc, const char **argv, const char *prefix)
{
- struct git_attr_check *check;
+ struct attr_check *check;
int cnt, i, doubledash, filei;
if (!is_bare_repository())
@@ -159,28 +163,26 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
error_with_usage("No file specified");
}
- if (all_attrs) {
- check = NULL;
- } else {
- check = xcalloc(cnt, sizeof(*check));
+ check = attr_check_alloc();
+ if (!all_attrs) {
for (i = 0; i < cnt; i++) {
- const char *name;
- struct git_attr *a;
- name = argv[i];
- a = git_attr(name);
+ const struct git_attr *a = git_attr(argv[i]);
+
if (!a)
return error("%s: not a valid attribute name",
- name);
- check[i].attr = a;
+ argv[i]);
+ attr_check_append(check, a);
}
}
if (stdin_paths)
- check_attr_stdin_paths(prefix, cnt, check);
+ check_attr_stdin_paths(prefix, check, all_attrs);
else {
for (i = filei; i < argc; i++)
- check_attr(prefix, cnt, check, argv[i]);
+ check_attr(prefix, check, all_attrs, argv[i]);
maybe_flush_or_die(stdout, "attribute to stdout");
}
+
+ attr_check_free(check);
return 0;
}
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 43f3617..1d73d3c 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -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 8028c37..07631d0 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -11,12 +11,12 @@
#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];
-static struct checkout state;
+static struct checkout state = CHECKOUT_INIT;
static void write_tempfile_record(const char *name, const char *prefix)
{
@@ -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,15 +195,17 @@ 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, LOCK_DIE_ON_ERROR);
}
/* Check out named files first */
@@ -253,24 +223,27 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
}
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) {
+ getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
+ while (getline_fn(&buf, stdin) != EOF) {
char *p;
- if (line_termination && buf.buf[0] == '"') {
- strbuf_reset(&nbuf);
- if (unquote_c_style(&nbuf, buf.buf, NULL))
+ 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);
free(p);
}
- strbuf_release(&nbuf);
+ strbuf_release(&unquoted);
strbuf_release(&buf);
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index e8110a9..3ecc47b 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -21,12 +21,31 @@
#include "submodule-config.h"
#include "submodule.h"
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
N_("git checkout [<options>] [<branch>] -- <file>..."),
NULL,
};
+static int option_parse_recurse_submodules(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset) {
+ recurse_submodules = RECURSE_SUBMODULES_OFF;
+ return 0;
+ }
+ if (arg)
+ recurse_submodules =
+ parse_update_recurse_submodules_arg(opt->long_name,
+ arg);
+ else
+ recurse_submodules = RECURSE_SUBMODULES_ON;
+
+ return 0;
+}
+
struct checkout_opts {
int patch_mode;
int quiet;
@@ -56,8 +75,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.oid.hash : null_sha1),
- sha1_to_hex(new ? new->object.oid.hash : null_sha1),
+ oid_to_hex(old ? &old->object.oid : &null_oid),
+ oid_to_hex(new ? &new->object.oid : &null_oid),
changed ? "1" : "0", NULL);
/* "new" can be NULL when checking out from the index before
a commit exists. */
@@ -76,7 +95,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base,
len = base->len + strlen(pathname);
ce = xcalloc(1, cache_entry_size(len));
- hashcpy(ce->sha1, sha1);
+ hashcpy(ce->oid.hash, sha1);
memcpy(ce->name, base->buf, base->len);
memcpy(ce->name + base->len, pathname, len - base->len);
ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
@@ -92,7 +111,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base,
if (pos >= 0) {
struct cache_entry *old = active_cache[pos];
if (ce->ce_mode == old->ce_mode &&
- !hashcmp(ce->sha1, old->sha1)) {
+ !oidcmp(&ce->oid, &old->oid)) {
old->ce_flags |= CE_UPDATE;
free(ce);
return 0;
@@ -154,8 +173,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)) {
@@ -169,15 +188,15 @@ 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;
mmfile_t ancestor, ours, theirs;
int status;
- unsigned char sha1[20];
+ struct object_id oid;
mmbuffer_t result_buf;
- unsigned char threeway[3][20];
+ struct object_id threeway[3];
unsigned mode = 0;
memset(threeway, 0, sizeof(threeway));
@@ -186,18 +205,18 @@ static int checkout_merged(int pos, struct checkout *state)
stage = ce_stage(ce);
if (!stage || strcmp(path, ce->name))
break;
- hashcpy(threeway[stage - 1], ce->sha1);
+ oidcpy(&threeway[stage - 1], &ce->oid);
if (stage == 2)
mode = create_ce_mode(ce->ce_mode);
pos++;
ce = active_cache[pos];
}
- if (is_null_sha1(threeway[1]) || is_null_sha1(threeway[2]))
+ if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
return error(_("path '%s' does not have necessary versions"), path);
- read_mmblob(&ancestor, threeway[0]);
- read_mmblob(&ours, threeway[1]);
- read_mmblob(&theirs, threeway[2]);
+ read_mmblob(&ancestor, &threeway[0]);
+ read_mmblob(&ours, &threeway[1]);
+ read_mmblob(&theirs, &threeway[2]);
/*
* NEEDSWORK: re-create conflicts from merges with
@@ -226,9 +245,9 @@ static int checkout_merged(int pos, struct checkout *state)
* object database even when it may contain conflicts).
*/
if (write_sha1_file(result_buf.ptr, result_buf.size,
- blob_type, sha1))
+ blob_type, oid.hash))
die(_("Unable to add merge result for '%s'"), path);
- ce = make_cache_entry(mode, sha1, path, 2, 0);
+ ce = make_cache_entry(mode, oid.hash, path, 2, 0);
if (!ce)
die(_("make_cache_entry failed for path '%s'"), path);
status = checkout_entry(ce, state, NULL);
@@ -239,10 +258,9 @@ static int checkout_paths(const struct checkout_opts *opts,
const char *revision)
{
int pos;
- struct checkout state;
+ struct checkout state = CHECKOUT_INIT;
static char *ps_matched;
- unsigned char rev[20];
- int flag;
+ struct object_id rev;
struct commit *head;
int errs = 0;
struct lock_file *lock_file;
@@ -275,9 +293,9 @@ static int checkout_paths(const struct checkout_opts *opts,
lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
+ hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
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);
@@ -353,7 +371,6 @@ static int checkout_paths(const struct checkout_opts *opts,
return 1;
/* Now we are committed to check them out */
- memset(&state, 0, sizeof(state));
state.force = 1;
state.refresh_cache = 1;
state.istate = &the_index;
@@ -375,8 +392,8 @@ 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);
- head = lookup_commit_reference_gently(rev, 1);
+ read_ref_full("HEAD", 0, rev.hash, NULL);
+ head = lookup_commit_reference_gently(rev.hash, 1);
errs |= post_checkout_hook(head, head, 0);
return errs;
@@ -454,7 +471,7 @@ static void setup_branch_path(struct branch_info *branch)
{
struct strbuf buf = STRBUF_INIT;
- strbuf_branchname(&buf, branch->name);
+ strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
if (strcmp(buf.buf, branch->name))
branch->name = xstrdup(buf.buf);
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
@@ -469,9 +486,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
int ret;
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
+ hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(NULL) < 0)
- return error(_("corrupt index file"));
+ return error(_("index file corrupt"));
resolve_undo_clear();
if (opts->force) {
@@ -568,10 +585,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;
}
@@ -611,25 +631,28 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
const char *old_desc, *reflog_msg;
if (opts->new_branch) {
if (opts->new_orphan_branch) {
- if (opts->new_branch_log && !log_all_ref_updates) {
+ char *refname;
+
+ refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
+ if (opts->new_branch_log &&
+ !should_autocreate_reflog(refname)) {
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);
+ free(refname);
return;
}
strbuf_release(&err);
}
+ free(refname);
}
else
- create_branch(old->name, opts->new_branch, new->name,
+ create_branch(opts->new_branch, new->name,
opts->new_branch_force ? 1 : 0,
opts->new_branch_log,
opts->new_branch_force ? 1 : 0,
@@ -656,12 +679,14 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
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)
@@ -703,8 +728,7 @@ static int add_pending_uninteresting_ref(const char *refname,
static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
{
strbuf_addstr(sb, " ");
- strbuf_addstr(sb,
- find_unique_abbrev(commit->object.oid.hash, 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);
@@ -805,11 +829,11 @@ static int switch_branches(const struct checkout_opts *opts,
int ret = 0;
struct branch_info old;
void *path_to_free;
- unsigned char rev[20];
+ struct object_id rev;
int flag, writeout_error = 0;
memset(&old, 0, sizeof(old));
- old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
- old.commit = lookup_commit_reference_gently(rev, 1);
+ old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
+ old.commit = lookup_commit_reference_gently(rev.hash, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -857,7 +881,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
struct tracking_name_data {
/* const */ char *src_ref;
char *dst_ref;
- unsigned char *dst_sha1;
+ struct object_id *dst_oid;
int unique;
};
@@ -868,7 +892,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
memset(&query, 0, sizeof(struct refspec));
query.src = cb->src_ref;
if (remote_find_tracking(remote, &query) ||
- get_sha1(query.dst, cb->dst_sha1)) {
+ get_oid(query.dst, cb->dst_oid)) {
free(query.dst);
return 0;
}
@@ -881,13 +905,13 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
return 0;
}
-static const char *unique_tracking_name(const char *name, unsigned char *sha1)
+static const char *unique_tracking_name(const char *name, struct object_id *oid)
{
struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
char src_ref[PATH_MAX];
snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
cb_data.src_ref = src_ref;
- cb_data.dst_sha1 = sha1;
+ cb_data.dst_oid = oid;
for_each_remote(check_tracking_name, &cb_data);
if (cb_data.unique)
return cb_data.dst_ref;
@@ -899,12 +923,12 @@ static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
struct branch_info *new,
struct checkout_opts *opts,
- unsigned char rev[20])
+ struct object_id *rev)
{
struct tree **source_tree = &opts->source_tree;
const char **new_branch = &opts->new_branch;
int argcount = 0;
- unsigned char branch_rev[20];
+ struct object_id branch_rev;
const char *arg;
int dash_dash_pos;
int has_dash_dash = 0;
@@ -970,7 +994,7 @@ static int parse_branchname_arg(int argc, const char **argv,
if (!strcmp(arg, "-"))
arg = "@{-1}";
- if (get_sha1_mb(arg, rev)) {
+ if (get_oid_mb(arg, rev)) {
/*
* Either case (3) or (4), with <something> not being
* a commit, or an attempt to use case (1) with an
@@ -981,7 +1005,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(opts->prefix, arg) || !no_wildcard(arg)))
recover_with_dwim = 0;
/*
* Accept "git checkout foo" and "git checkout foo --"
@@ -1018,15 +1043,15 @@ static int parse_branchname_arg(int argc, const char **argv,
setup_branch_path(new);
if (!check_refname_format(new->path, 0) &&
- !read_ref(new->path, branch_rev))
- hashcpy(rev, branch_rev);
+ !read_ref(new->path, branch_rev.hash))
+ oidcpy(rev, &branch_rev);
else
new->path = NULL; /* not an existing branch */
- new->commit = lookup_commit_reference_gently(rev, 1);
+ new->commit = lookup_commit_reference_gently(rev->hash, 1);
if (!new->commit) {
/* not a commit */
- *source_tree = parse_tree_indirect(rev);
+ *source_tree = parse_tree_indirect(rev->hash);
} else {
parse_commit_or_die(new->commit);
*source_tree = new->commit->tree;
@@ -1034,7 +1059,7 @@ static int parse_branchname_arg(int argc, const char **argv,
if (!*source_tree) /* case (1): want a tree */
die(_("reference is not a tree: %s"), arg);
- if (!has_dash_dash) {/* case (3).(d) -> (1) */
+ if (!has_dash_dash) { /* case (3).(d) -> (1) */
/*
* Do not complain the most common case
* git checkout branch
@@ -1042,7 +1067,7 @@ static int parse_branchname_arg(int argc, const char **argv,
* it would be extremely annoying.
*/
if (argc)
- verify_non_filename(NULL, arg);
+ verify_non_filename(opts->prefix, arg);
} else {
argcount++;
argv++;
@@ -1104,21 +1129,21 @@ static int checkout_branch(struct checkout_opts *opts,
if (new->path && !opts->force_detach && !opts->new_branch &&
!opts->ignore_other_worktrees) {
- unsigned char sha1[20];
+ struct object_id oid;
int flag;
- char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
+ char *head_ref = resolve_refdup("HEAD", 0, oid.hash, &flag);
if (head_ref &&
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
- die_if_checked_out(new->path);
+ die_if_checked_out(new->path, 1);
free(head_ref);
}
if (!new->commit && opts->new_branch) {
- unsigned char rev[20];
+ struct object_id rev;
int flag;
- if (!read_ref_full("HEAD", 0, rev, &flag) &&
- (flag & REF_ISSYMREF) && is_null_sha1(rev))
+ if (!read_ref_full("HEAD", 0, rev.hash, &flag) &&
+ (flag & REF_ISSYMREF) && is_null_oid(&rev))
return switch_unborn_to_new_branch(opts);
}
return switch_branches(opts, new);
@@ -1137,7 +1162,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")),
@@ -1157,6 +1182,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
N_("second guess 'git checkout <no-such-branch>'")),
OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
N_("do not check if another worktree is holding the given ref")),
+ { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+ "checkout", "control recursive updating of submodules",
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules },
OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
OPT_END(),
};
@@ -1187,6 +1215,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
}
+ if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+ git_config(submodule_config, NULL);
+ if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+ set_config_update_recurse_submodules(recurse_submodules);
+ }
+
if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
die(_("-b, -B and --orphan are mutually exclusive"));
@@ -1228,14 +1262,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
* remote branches, erroring out for invalid or ambiguous cases.
*/
if (argc) {
- unsigned char rev[20];
+ struct object_id rev;
int dwim_ok =
!opts.patch_mode &&
dwim_new_local_branch &&
opts.track == BRANCH_TRACK_UNSPECIFIED &&
!opts.new_branch;
int n = parse_branchname_arg(argc, argv, dwim_ok,
- &new, &opts, rev);
+ &new, &opts, &rev);
argv += n;
argc -= n;
}
diff --git a/builtin/clean.c b/builtin/clean.c
index d7acb94..d861f83 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -147,30 +147,6 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
return 0;
}
-/*
- * Return 1 if the given path is the root of a git repository or
- * submodule else 0. Will not return 1 for bare repositories with the
- * exception of creating a bare repository in "foo/.git" and calling
- * is_git_repository("foo").
- */
-static int is_git_repository(struct strbuf *path)
-{
- int ret = 0;
- int gitfile_error;
- size_t orig_path_len = path->len;
- assert(orig_path_len != 0);
- strbuf_complete(path, '/');
- strbuf_addstr(path, ".git");
- if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))
- ret = 1;
- if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED ||
- gitfile_error == READ_GITFILE_ERR_READ_FAILED)
- ret = 1; /* This could be a real .git file, take the
- * safe option and avoid cleaning */
- strbuf_setlen(path, orig_path_len);
- return ret;
-}
-
static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
int dry_run, int quiet, int *dir_gone)
{
@@ -182,7 +158,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
*dir_gone = 1;
- if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_git_repository(path)) {
+ if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
if (!quiet) {
quote_path_relative(path->buf, prefix, &quoted);
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
@@ -198,8 +174,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
/* an empty dir could be removed even if it is unreadble */
res = dry_run ? 0 : rmdir(path->buf);
if (res) {
+ int saved_errno = errno;
quote_path_relative(path->buf, prefix, &quoted);
- warning(_(msg_warn_remove_failed), quoted.buf);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
}
return res;
@@ -232,8 +210,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
quote_path_relative(path->buf, prefix, &quoted);
string_list_append(&dels, quoted.buf);
} else {
+ int saved_errno = errno;
quote_path_relative(path->buf, prefix, &quoted);
- warning(_(msg_warn_remove_failed), quoted.buf);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
ret = 1;
}
@@ -254,8 +234,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if (!res)
*dir_gone = 1;
else {
+ int saved_errno = errno;
quote_path_relative(path->buf, prefix, &quoted);
- warning(_(msg_warn_remove_failed), quoted.buf);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
ret = 1;
}
@@ -311,11 +293,11 @@ static void pretty_print_menus(struct string_list *menu_list)
static void prompt_help_cmd(int singleton)
{
clean_print_color(CLEAN_COLOR_HELP);
- printf_ln(singleton ?
+ printf(singleton ?
_("Prompt help:\n"
"1 - select a numbered item\n"
"foo - select item based on unique prefix\n"
- " - (empty) select nothing") :
+ " - (empty) select nothing\n") :
_("Prompt help:\n"
"1 - select a single item\n"
"3-5 - select a range of items\n"
@@ -323,7 +305,7 @@ static void prompt_help_cmd(int singleton)
"foo - select item based on unique prefix\n"
"-... - unselect specified items\n"
"* - choose all items\n"
- " - (empty) finish selecting"));
+ " - (empty) finish selecting\n"));
clean_print_color(CLEAN_COLOR_RESET);
}
@@ -532,7 +514,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
(is_single && bottom != top)) {
clean_print_color(CLEAN_COLOR_ERROR);
- printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+ printf(_("Huh (%s)?\n"), (*ptr)->buf);
clean_print_color(CLEAN_COLOR_RESET);
continue;
}
@@ -567,7 +549,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;
@@ -594,7 +576,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;
@@ -639,7 +621,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;
@@ -676,7 +658,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');
@@ -774,7 +756,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');
@@ -798,7 +780,7 @@ static int ask_each_cmd(void)
static int quit_cmd(void)
{
string_list_clear(&del_list, 0);
- printf_ln(_("Bye."));
+ printf(_("Bye.\n"));
return MENU_RETURN_NO_LOOP;
}
@@ -1005,8 +987,10 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
} else {
res = dry_run ? 0 : unlink(abs_path.buf);
if (res) {
+ int saved_errno = errno;
qname = quote_path_relative(item->string, NULL, &buf);
- warning(_(msg_warn_remove_failed), qname);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), qname);
errors++;
} else if (!quiet) {
qname = quote_path_relative(item->string, NULL, &buf);
diff --git a/builtin/clone.c b/builtin/clone.c
index a0b3cd9..de85b85 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -39,17 +39,38 @@ 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 char *option_template, *option_depth;
+static int option_local = -1, option_no_hardlinks, option_shared;
+static int option_shallow_submodules;
+static int deepen;
+static char *option_template, *option_depth, *option_since;
static char *option_origin = NULL;
static char *option_branch = NULL;
+static struct string_list option_not = STRING_LIST_INIT_NODUP;
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_required_reference = STRING_LIST_INIT_NODUP;
+static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
+static int max_jobs = -1;
+static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
+
+static int recurse_submodules_cb(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset)
+ string_list_clear((struct string_list *)opt->value, 0);
+ else if (arg)
+ string_list_append((struct string_list *)opt->value, arg);
+ else
+ string_list_append((struct string_list *)opt->value,
+ (const char *)opt->defval);
+
+ return 0;
+}
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
@@ -68,14 +89,21 @@ static struct option builtin_clone_options[] = {
N_("don't use local hardlinks, always copy")),
OPT_BOOL('s', "shared", &option_shared,
N_("setup as shared repository")),
- OPT_BOOL(0, "recursive", &option_recursive,
- N_("initialize submodules in the clone")),
- OPT_BOOL(0, "recurse-submodules", &option_recursive,
- N_("initialize submodules in the clone")),
+ { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
+ N_("pathspec"), N_("initialize submodules in the clone"),
+ PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
+ (intptr_t)"." },
+ { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
+ N_("pathspec"), N_("initialize submodules in the clone"),
+ PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
+ 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"),
+ OPT_STRING_LIST(0, "reference", &option_required_reference, N_("repo"),
N_("reference repository")),
+ OPT_STRING_LIST(0, "reference-if-able", &option_optional_reference,
+ N_("repo"), N_("reference repository")),
OPT_BOOL(0, "dissociate", &option_dissociate,
N_("use --reference only while cloning")),
OPT_STRING('o', "origin", &option_origin, N_("name"),
@@ -86,19 +114,25 @@ static struct option builtin_clone_options[] = {
N_("path to git-upload-pack on the remote")),
OPT_STRING(0, "depth", &option_depth, N_("depth"),
N_("create a shallow clone of that depth")),
+ OPT_STRING(0, "shallow-since", &option_since, N_("time"),
+ N_("create a shallow clone since a specific time")),
+ OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"),
+ N_("deepen history of shallow clone, excluding rev")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "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 const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
@@ -154,7 +188,7 @@ static char *get_repo_path(const char *repo, int *is_bundle)
strbuf_addstr(&path, repo);
raw = get_repo_path_1(&path, is_bundle);
- canon = raw ? xstrdup(absolute_path(raw)) : NULL;
+ canon = raw ? absolute_pathdup(raw) : NULL;
strbuf_release(&path);
return canon;
}
@@ -231,8 +265,8 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
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");
+ 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);
@@ -275,50 +309,37 @@ static void strip_trailing_slashes(char *dir)
static int add_one_reference(struct string_list_item *item, void *cb_data)
{
- char *ref_git;
- const char *repo;
- struct strbuf alternate = STRBUF_INIT;
-
- /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */
- ref_git = xstrdup(real_path(item->string));
-
- repo = read_gitfile(ref_git);
- if (!repo)
- repo = read_gitfile(mkpath("%s/.git", ref_git));
- if (repo) {
- free(ref_git);
- ref_git = xstrdup(repo);
- }
+ struct strbuf err = STRBUF_INIT;
+ int *required = cb_data;
+ char *ref_git = compute_alternate_path(item->string, &err);
- if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
- 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))) {
+ if (!ref_git) {
+ if (*required)
+ die("%s", err.buf);
+ else
+ fprintf(stderr,
+ _("info: Could not add alternate for '%s': %s\n"),
+ item->string, err.buf);
+ } else {
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);
+ strbuf_addf(&sb, "%s/objects", ref_git);
+ add_to_alternates_file(sb.buf);
+ strbuf_release(&sb);
}
- if (!access(mkpath("%s/shallow", ref_git), F_OK))
- die(_("reference repository '%s' is shallow"), item->string);
-
- if (!access(mkpath("%s/info/grafts", ref_git), F_OK))
- die(_("reference repository '%s' is grafted"), item->string);
-
- strbuf_addf(&alternate, "%s/objects", ref_git);
- add_to_alternates_file(alternate.buf);
- strbuf_release(&alternate);
+ strbuf_release(&err);
free(ref_git);
return 0;
}
static void setup_reference(void)
{
- for_each_string_list(&option_reference, add_one_reference, NULL);
+ int required = 1;
+ for_each_string_list(&option_required_reference,
+ add_one_reference, &required);
+ required = 0;
+ for_each_string_list(&option_optional_reference,
+ add_one_reference, &required);
}
static void copy_alternates(struct strbuf *src, struct strbuf *dst,
@@ -339,7 +360,7 @@ 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) {
+ while (strbuf_getline(&line, in) != EOF) {
char *abs_path;
if (!line.len || line.buf[0] == '#')
continue;
@@ -348,8 +369,11 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
continue;
}
abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
- normalize_path_copy(abs_path, abs_path);
- add_to_alternates_file(abs_path);
+ if (!normalize_path_copy(abs_path, abs_path))
+ add_to_alternates_file(abs_path);
+ else
+ warning("skipping invalid relative alternate: %s/%s",
+ src_repo, line.buf);
free(abs_path);
}
strbuf_release(&line);
@@ -617,13 +641,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) {
@@ -636,9 +660,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);
}
}
@@ -648,7 +674,8 @@ 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_oid.hash, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
@@ -670,9 +697,9 @@ static void update_head(const struct ref *our, const struct ref *remote,
}
}
-static int checkout(void)
+static int checkout(int submodule_progress)
{
- unsigned char sha1[20];
+ struct object_id oid;
char *head;
struct lock_file *lock_file;
struct unpack_trees_options opts;
@@ -683,7 +710,7 @@ static int checkout(void)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout.\n"));
@@ -691,7 +718,7 @@ static int checkout(void)
}
if (!strcmp(head, "HEAD")) {
if (advice_detached_head)
- detach_advice(sha1_to_hex(sha1));
+ detach_advice(oid_to_hex(&oid));
} else {
if (!starts_with(head, "refs/heads/"))
die(_("HEAD not found below refs/heads!"));
@@ -702,7 +729,7 @@ static int checkout(void)
setup_work_tree();
lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
+ hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof opts);
opts.update = 1;
@@ -712,7 +739,7 @@ static int checkout(void)
opts.src_index = &the_index;
opts.dst_index = &the_index;
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(oid.hash);
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts) < 0)
@@ -722,17 +749,31 @@ static int checkout(void)
die(_("unable to write new index file"));
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
- sha1_to_hex(sha1), "1", NULL);
+ oid_to_hex(&oid), "1", NULL);
+
+ if (!err && (option_recurse_submodules.nr > 0)) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+ argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+ if (option_shallow_submodules == 1)
+ argv_array_push(&args, "--depth=1");
+
+ if (max_jobs != -1)
+ argv_array_pushf(&args, "--jobs=%d", max_jobs);
- if (!err && option_recursive)
- err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+ if (submodule_progress)
+ argv_array_push(&args, "--progress");
+
+ 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)
@@ -742,7 +783,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"));
}
}
@@ -830,6 +871,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
const char *src_ref_prefix = "refs/heads/";
struct remote *remote;
int err = 0, complete_refs_before_fetch = 1;
+ int submodule_progress;
struct refspec *refspec;
const char *fetch_pattern;
@@ -846,8 +888,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
usage_msg_opt(_("You must specify a repository to clone."),
builtin_clone_usage, builtin_clone_options);
+ if (option_depth || option_since || option_not.nr)
+ deepen = 1;
if (option_single_branch == -1)
- option_single_branch = option_depth ? 1 : 0;
+ option_single_branch = deepen ? 1 : 0;
if (option_mirror)
option_bare = 1;
@@ -868,7 +912,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
path = get_repo_path(repo_name, &is_bundle);
if (path)
- repo = xstrdup(absolute_path(repo_name));
+ repo = absolute_pathdup(repo_name);
else if (!strchr(repo_name, ':'))
die(_("repository '%s' does not exist"), repo_name);
else
@@ -920,23 +964,58 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
set_git_work_tree(work_tree);
}
- junk_git_dir = git_dir;
+ junk_git_dir = real_git_dir ? real_git_dir : git_dir;
if (safe_create_leading_directories_const(git_dir) < 0)
die(_("could not create leading directories of '%s'"), git_dir);
- set_git_dir_init(git_dir, real_git_dir, 0);
- if (real_git_dir) {
- git_dir = real_git_dir;
- junk_git_dir = real_git_dir;
- }
-
if (0 <= option_verbosity) {
if (option_bare)
fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir);
else
fprintf(stderr, _("Cloning into '%s'...\n"), dir);
}
- init_db(option_template, INIT_DB_QUIET);
+
+ if (option_recurse_submodules.nr > 0) {
+ struct string_list_item *item;
+ struct strbuf sb = STRBUF_INIT;
+
+ /* remove duplicates */
+ string_list_sort(&option_recurse_submodules);
+ string_list_remove_duplicates(&option_recurse_submodules, 0);
+
+ /*
+ * NEEDSWORK: In a multi-working-tree world, this needs to be
+ * set in the per-worktree config.
+ */
+ for_each_string_list_item(item, &option_recurse_submodules) {
+ strbuf_addf(&sb, "submodule.active=%s",
+ item->string);
+ string_list_append(&option_config,
+ strbuf_detach(&sb, NULL));
+ }
+
+ if (option_required_reference.nr &&
+ option_optional_reference.nr)
+ die(_("clone --recursive is not compatible with "
+ "both --reference and --reference-if-able"));
+ else if (option_required_reference.nr) {
+ string_list_append(&option_config,
+ "submodule.alternateLocation=superproject");
+ string_list_append(&option_config,
+ "submodule.alternateErrorStrategy=die");
+ } else if (option_optional_reference.nr) {
+ string_list_append(&option_config,
+ "submodule.alternateLocation=superproject");
+ string_list_append(&option_config,
+ "submodule.alternateErrorStrategy=info");
+ }
+ }
+
+ init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);
+
+ if (real_git_dir)
+ git_dir = real_git_dir;
+
write_config(&option_config);
git_config(git_default_config, NULL);
@@ -956,7 +1035,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, repo);
strbuf_reset(&key);
- if (option_reference.nr)
+ if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
fetch_pattern = value.buf;
@@ -967,12 +1046,17 @@ 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;
if (is_local) {
if (option_depth)
warning(_("--depth is ignored in local clones; use file:// instead."));
+ if (option_since)
+ warning(_("--shallow-since is ignored in local clones; use file:// instead."));
+ if (option_not.nr)
+ warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
if (!access(mkpath("%s/shallow", path), F_OK)) {
if (option_local > 0)
warning(_("source repository is shallow, ignoring --local"));
@@ -991,6 +1075,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_depth)
transport_set_option(transport, TRANS_OPT_DEPTH,
option_depth);
+ if (option_since)
+ transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,
+ option_since);
+ if (option_not.nr)
+ transport_set_option(transport, TRANS_OPT_DEEPEN_NOT,
+ (const char *)&option_not);
if (option_single_branch)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
@@ -998,7 +1088,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_set_option(transport, TRANS_OPT_UPLOADPACK,
option_upload_pack);
- if (transport->smart_options && !option_depth)
+ if (transport->smart_options && !deepen)
transport->smart_options->check_self_contained_and_connected = 1;
refs = transport_get_remote_refs(transport);
@@ -1068,6 +1158,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
update_head(our_head_points_at, remote_head, reflog_msg.buf);
+ /*
+ * We want to show progress for recursive submodule clones iff
+ * we did so for the main clone. But only the transport knows
+ * the final decision for this flag, so we need to rescue the value
+ * before we free the transport.
+ */
+ submodule_progress = transport->progress;
+
transport_unlock_pack(transport);
transport_disconnect(transport);
@@ -1077,7 +1175,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
junk_mode = JUNK_LEAVE_REPO;
- err = checkout();
+ err = checkout(submodule_progress);
strbuf_release(&reflog_msg);
strbuf_release(&branch_top);
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 3feeffe..6050172 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -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);
}
@@ -44,8 +40,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
int i, got_tree = 0;
struct commit_list *parents = NULL;
- unsigned char tree_sha1[20];
- unsigned char commit_sha1[20];
+ struct object_id tree_oid;
+ struct object_id commit_oid;
struct strbuf buffer = STRBUF_INIT;
git_config(commit_tree_config, NULL);
@@ -56,13 +52,13 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "-p")) {
- unsigned char sha1[20];
+ struct object_id oid;
if (argc <= ++i)
usage(commit_tree_usage);
- if (get_sha1_commit(argv[i], sha1))
+ if (get_sha1_commit(argv[i], oid.hash))
die("Not a valid object name %s", argv[i]);
- assert_sha1_type(sha1, OBJ_COMMIT);
- new_parent(lookup_commit(sha1), &parents);
+ assert_sha1_type(oid.hash, OBJ_COMMIT);
+ new_parent(lookup_commit(oid.hash), &parents);
continue;
}
@@ -109,7 +105,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
continue;
}
- if (get_sha1_tree(arg, tree_sha1))
+ if (get_sha1_tree(arg, tree_oid.hash))
die("Not a valid object name %s", arg);
if (got_tree)
die("Cannot give more than one trees");
@@ -121,13 +117,13 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
die_errno("git commit-tree: failed to read");
}
- if (commit_tree(buffer.buf, buffer.len, tree_sha1, parents,
- commit_sha1, NULL, sign_commit)) {
+ if (commit_tree(buffer.buf, buffer.len, tree_oid.hash, parents,
+ commit_oid.hash, NULL, sign_commit)) {
strbuf_release(&buffer);
return 1;
}
- printf("%s\n", sha1_to_hex(commit_sha1));
+ printf("%s\n", oid_to_hex(&commit_oid));
strbuf_release(&buffer);
return 0;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index d054f84..4e288bc 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -92,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 {
@@ -114,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;
@@ -140,14 +142,24 @@ static int show_ignored_in_status, have_option_m;
static const char *only_include_assumed;
static struct strbuf message = STRBUF_INIT;
-static enum status_format {
- STATUS_FORMAT_NONE = 0,
- STATUS_FORMAT_LONG,
- STATUS_FORMAT_SHORT,
- STATUS_FORMAT_PORCELAIN,
+static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
- STATUS_FORMAT_UNSPECIFIED
-} status_format = STATUS_FORMAT_UNSPECIFIED;
+static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
+{
+ enum wt_status_format *value = (enum wt_status_format *)opt->value;
+ if (unset)
+ *value = STATUS_FORMAT_NONE;
+ else if (!arg)
+ *value = STATUS_FORMAT_PORCELAIN;
+ else if (!strcmp(arg, "v1") || !strcmp(arg, "1"))
+ *value = STATUS_FORMAT_PORCELAIN;
+ else if (!strcmp(arg, "v2") || !strcmp(arg, "2"))
+ *value = STATUS_FORMAT_PORCELAIN_V2;
+ else
+ die("unsupported porcelain version '%s'", arg);
+
+ return 0;
+}
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
{
@@ -171,7 +183,7 @@ static void determine_whence(struct wt_status *s)
whence = FROM_MERGE;
else if (file_exists(git_path_cherry_pick_head())) {
whence = FROM_CHERRY_PICK;
- if (file_exists(git_path(SEQ_DIR)))
+ if (file_exists(git_path_seq_dir()))
sequencer_in_use = 1;
}
else
@@ -186,6 +198,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() */
}
@@ -338,7 +351,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
if (interactive) {
char *old_index_env = NULL;
- hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
@@ -383,7 +396,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
* (B) on failure, rollback the real index.
*/
if (all || (also && pathspec.nr)) {
- hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
refresh_cache_or_die(refresh_flags);
update_main_cache_tree(WRITE_TREE_SILENT);
@@ -403,7 +416,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
* We still need to refresh the index here.
*/
if (!only && !pathspec.nr) {
- hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
refresh_cache_or_die(refresh_flags);
if (active_cache_changed
|| !cache_tree_fully_valid(active_cache_tree))
@@ -455,7 +468,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
if (read_cache() < 0)
die(_("cannot read the index"));
- hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
update_main_cache_tree(WRITE_TREE_SILENT);
@@ -483,7 +496,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
struct wt_status *s)
{
- unsigned char sha1[20];
+ struct object_id oid;
if (s->relative_paths)
s->prefix = prefix;
@@ -496,25 +509,14 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->index_file = index_file;
s->fp = fp;
s->nowarn = nowarn;
- s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+ s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
+ if (!s->is_initial)
+ hashcpy(s->sha1_commit, oid.hash);
+ s->status_format = status_format;
+ s->ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(s);
-
- switch (status_format) {
- case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(s);
- break;
- case STATUS_FORMAT_PORCELAIN:
- wt_porcelain_print(s);
- break;
- case STATUS_FORMAT_UNSPECIFIED:
- die("BUG: finalize_deferred_config() should have been called");
- break;
- case STATUS_FORMAT_NONE:
- case STATUS_FORMAT_LONG:
- wt_status_print(s);
- break;
- }
+ wt_status_print(s);
return s->commitable;
}
@@ -694,7 +696,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 +714,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) {
@@ -726,9 +728,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
&sb, &ctx);
hook_arg1 = "message";
} 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)
die_errno(_("could not read SQUASH_MSG"));
@@ -761,9 +772,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;
@@ -779,7 +790,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
strbuf_stripspace(&sb, 0);
if (signoff)
- append_signoff(&sb, ignore_non_trailer(&sb), 0);
+ append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
die_errno(_("could not write commit template"));
@@ -874,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
commitable = run_status(s->fp, index_file, prefix, 1, s);
s->use_color = saved_color_setting;
} else {
- unsigned char sha1[20];
+ struct object_id oid;
const char *parent = "HEAD";
if (!active_nr && read_cache() < 0)
@@ -883,9 +894,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (amend)
parent = "HEAD^1";
- if (get_sha1(parent, sha1))
- commitable = !!active_nr;
- else {
+ if (get_sha1(parent, oid.hash)) {
+ int i, ita_nr = 0;
+
+ for (i = 0; i < active_nr; i++)
+ if (ce_intent_to_add(active_cache[i]))
+ ita_nr++;
+ commitable = active_nr - ita_nr > 0;
+ } else {
/*
* Unless the user did explicitly request a submodule
* ignore mode by passing a command line option we do
@@ -899,7 +915,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (ignore_submodule_arg &&
!strcmp(ignore_submodule_arg, "all"))
diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
- commitable = index_differs_from(parent, diff_flags);
+ commitable = index_differs_from(parent, diff_flags, 1);
}
}
strbuf_release(&committer_ident);
@@ -940,23 +956,23 @@ 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) {
- char index[PATH_MAX];
- 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)) {
+ struct argv_array env = ARGV_ARRAY_INIT;
+
+ argv_array_pushf(&env, "GIT_INDEX_FILE=%s", index_file);
+ if (launch_editor(git_path_commit_editmsg(), NULL, env.argv)) {
fprintf(stderr,
_("Please supply the message using either -m or -F option.\n"));
exit(1);
}
+ argv_array_clear(&env);
}
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;
}
@@ -1087,7 +1103,7 @@ static const char *read_commit_message(const char *name)
* is not in effect here.
*/
static struct status_deferred_config {
- enum status_format status_format;
+ enum wt_status_format status_format;
int show_branch;
} status_deferred_config = {
STATUS_FORMAT_UNSPECIFIED,
@@ -1097,6 +1113,7 @@ static struct status_deferred_config {
static void finalize_deferred_config(struct wt_status *s)
{
int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+ status_format != STATUS_FORMAT_PORCELAIN_V2 &&
!s->null_termination);
if (s->null_termination) {
@@ -1162,9 +1179,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;
@@ -1189,10 +1206,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (also + only + all + interactive > 1)
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
- if (argc == 0 && (also || (only && !amend)))
+ if (argc == 0 && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));
- if (argc == 0 && only && amend)
- only_include_assumed = _("Clever... amending the last one with dirty index.");
if (argc > 0 && !also && !only)
only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths...");
if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
@@ -1317,16 +1332,16 @@ int cmd_status(int argc, const char **argv, const char *prefix)
{
static struct wt_status s;
int fd;
- unsigned char sha1[20];
+ struct object_id oid;
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT_SET_INT('s', "short", &status_format,
N_("show status concisely"), STATUS_FORMAT_SHORT),
OPT_BOOL('b', "branch", &s.show_branch,
N_("show branch information")),
- OPT_SET_INT(0, "porcelain", &status_format,
- N_("machine-readable output"),
- STATUS_FORMAT_PORCELAIN),
+ { OPTION_CALLBACK, 0, "porcelain", &status_format,
+ N_("version"), N_("machine-readable output"),
+ PARSE_OPT_OPTARG, opt_parse_porcelain },
OPT_SET_INT(0, "long", &status_format,
N_("show status in long format (default)"),
STATUS_FORMAT_LONG),
@@ -1367,8 +1382,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
fd = hold_locked_index(&index_lock, 0);
- s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
+ s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
+ if (!s.is_initial)
+ hashcpy(s.sha1_commit, oid.hash);
+
s.ignore_submodule_arg = ignore_submodule_arg;
+ s.status_format = status_format;
+ s.verbose = verbose;
+
wt_status_collect(&s);
if (0 <= fd)
@@ -1377,23 +1398,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (s.relative_paths)
s.prefix = prefix;
- switch (status_format) {
- case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(&s);
- break;
- case STATUS_FORMAT_PORCELAIN:
- wt_porcelain_print(&s);
- break;
- case STATUS_FORMAT_UNSPECIFIED:
- die("BUG: finalize_deferred_config() should have been called");
- break;
- case STATUS_FORMAT_NONE:
- case STATUS_FORMAT_LONG:
- s.verbose = verbose;
- s.ignore_submodule_arg = ignore_submodule_arg;
- wt_status_print(&s);
- break;
- }
+ wt_status_print(&s);
return 0;
}
@@ -1413,19 +1418,19 @@ static const char *implicit_ident_advice(void)
}
-static void print_summary(const char *prefix, const unsigned char *sha1,
+static void print_summary(const char *prefix, const struct object_id *oid,
int initial_commit)
{
struct rev_info rev;
struct commit *commit;
struct strbuf format = STRBUF_INIT;
- unsigned char junk_sha1[20];
+ struct object_id junk_oid;
const char *head;
struct pretty_print_context pctx = {0};
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
- commit = lookup_commit(sha1);
+ commit = lookup_commit(oid->hash);
if (!commit)
die(_("couldn't look up newly created commit"));
if (parse_commit(commit))
@@ -1472,7 +1477,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
+ head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
if (!strcmp(head, "HEAD"))
head = _("detached HEAD");
else
@@ -1505,6 +1510,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)
@@ -1512,15 +1522,13 @@ static int git_commit_config(const char *k, const char *v, void *cb)
return git_status_config(k, v, s);
}
-static int run_rewrite_hook(const unsigned char *oldsha1,
- const unsigned char *newsha1)
+static int run_rewrite_hook(const struct object_id *oldoid,
+ const struct object_id *newoid)
{
- /* oldsha1 SP newsha1 LF NUL */
- static char buf[2*40 + 3];
struct child_process proc = CHILD_PROCESS_INIT;
const char *argv[3];
int code;
- size_t n;
+ struct strbuf sb = STRBUF_INIT;
argv[0] = find_hook("post-rewrite");
if (!argv[0])
@@ -1536,34 +1544,33 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
code = start_command(&proc);
if (code)
return code;
- n = snprintf(buf, sizeof(buf), "%s %s\n",
- sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+ strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
sigchain_push(SIGPIPE, SIG_IGN);
- write_in_full(proc.in, buf, n);
+ write_in_full(proc.in, sb.buf, sb.len);
close(proc.in);
+ strbuf_release(&sb);
sigchain_pop(SIGPIPE);
return finish_command(&proc);
}
int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
{
- const char *hook_env[3] = { NULL };
- char index[PATH_MAX];
+ struct argv_array hook_env = ARGV_ARRAY_INIT;
va_list args;
int ret;
- snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
- hook_env[0] = index;
+ argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
/*
* Let the hook know that no editor will be launched.
*/
if (!editor_is_used)
- hook_env[1] = "GIT_EDITOR=:";
+ argv_array_push(&hook_env, "GIT_EDITOR=:");
va_start(args, name);
- ret = run_hook_ve(hook_env, name, args);
+ ret = run_hook_ve(hook_env.argv,name, args);
va_end(args);
+ argv_array_clear(&hook_env);
return ret;
}
@@ -1600,7 +1607,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),
@@ -1629,8 +1636,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct strbuf author_ident = STRBUF_INIT;
const char *index_file, *reflog_msg;
char *nl;
- unsigned char sha1[20];
- struct commit_list *parents = NULL, **pptr = &parents;
+ struct object_id oid;
+ struct commit_list *parents = NULL;
struct stat statbuf;
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
@@ -1644,16 +1651,20 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
s.colopts = 0;
- if (get_sha1("HEAD", sha1))
+ if (get_sha1("HEAD", oid.hash))
current_head = NULL;
else {
- current_head = lookup_commit_or_die(sha1, "HEAD");
+ current_head = lookup_commit_or_die(oid.hash, "HEAD");
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);
@@ -1672,31 +1683,29 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!reflog_msg)
reflog_msg = "commit (initial)";
} else if (amend) {
- struct commit_list *c;
-
if (!reflog_msg)
reflog_msg = "commit (amend)";
- for (c = current_head->parents; c; c = c->next)
- pptr = &commit_list_insert(c->item, pptr)->next;
+ parents = copy_commit_list(current_head->parents);
} else if (whence == FROM_MERGE) {
struct strbuf m = STRBUF_INIT;
FILE *fp;
int allow_fast_forward = 1;
+ struct commit_list **pptr = &parents;
if (!reflog_msg)
reflog_msg = "commit (merge)";
- pptr = &commit_list_insert(current_head, pptr)->next;
+ pptr = commit_list_append(current_head, pptr);
fp = fopen(git_path_merge_head(), "r");
if (fp == NULL)
die_errno(_("could not open '%s' for reading"),
git_path_merge_head());
- while (strbuf_getline(&m, fp, '\n') != EOF) {
+ while (strbuf_getline_lf(&m, fp) != EOF) {
struct commit *parent;
parent = get_merge_parent(m.buf);
if (!parent)
die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
- pptr = &commit_list_insert(parent, pptr)->next;
+ pptr = commit_list_append(parent, pptr);
}
fclose(fp);
strbuf_release(&m);
@@ -1713,12 +1722,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
reflog_msg = (whence == FROM_CHERRY_PICK)
? "commit (cherry-pick)"
: "commit";
- pptr = &commit_list_insert(current_head, pptr)->next;
+ commit_list_insert(current_head, &parents);
}
/* 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));
@@ -1750,7 +1759,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
- parents, sha1, author_ident.buf, sign_commit, extra)) {
+ parents, oid.hash, author_ident.buf, sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
@@ -1767,7 +1776,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, "HEAD", sha1,
+ ref_transaction_update(transaction, "HEAD", oid.hash,
current_head
? current_head->object.oid.hash : null_sha1,
0, sb.buf, &err) ||
@@ -1796,13 +1805,13 @@ 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.oid.hash, sha1);
+ copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash);
finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
}
- run_rewrite_hook(current_head->object.oid.hash, sha1);
+ run_rewrite_hook(&current_head->object.oid, &oid);
}
if (!quiet)
- print_summary(prefix, sha1, !current_head);
+ print_summary(prefix, &oid, !current_head);
strbuf_release(&err);
return 0;
diff --git a/builtin/config.c b/builtin/config.c
index adc7727..4f49a0e 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>]"),
@@ -24,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)
@@ -81,6 +82,7 @@ static struct option builtin_config_options[] = {
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(),
};
@@ -91,8 +93,28 @@ 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 (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
@@ -108,6 +130,8 @@ struct strbuf_list {
static int format_config(struct strbuf *buf, const char *key_, const char *value_)
{
+ if (show_origin)
+ show_config_origin(buf);
if (show_keys)
strbuf_addstr(buf, key_);
if (!omit_values) {
@@ -352,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");
@@ -389,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 };
@@ -415,6 +443,8 @@ 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 buf = STRBUF_INIT;
@@ -431,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)
@@ -497,9 +527,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
else if (given_config_source.file) {
if (!is_absolute_path(given_config_source.file) && prefix)
given_config_source.file =
- xstrdup(prefix_filename(prefix,
- strlen(prefix),
- given_config_source.file));
+ prefix_filename(prefix, given_config_source.file);
}
if (respect_includes == -1)
@@ -538,6 +566,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
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);
if (git_config_with_options(show_all_config, NULL,
@@ -565,7 +601,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);
@@ -582,33 +618,33 @@ 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]);
+ error(_("cannot overwrite multiple values with a single value\n"
+ " Use a regexp, --add or --replace-all to change %s."), argv[0]);
return ret;
}
else if (actions == ACTION_SET_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], 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);
@@ -634,17 +670,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 ba92919..acb0594 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -8,6 +8,7 @@
#include "dir.h"
#include "builtin.h"
#include "parse-options.h"
+#include "quote.h"
static unsigned long garbage;
static off_t size_garbage;
@@ -52,7 +53,7 @@ static void loose_garbage(const char *path)
report_garbage(PACKDIR_FILE_GARBAGE, path);
}
-static int count_loose(const unsigned char *sha1, const char *path, void *data)
+static int count_loose(const struct object_id *oid, const char *path, void *data)
{
struct stat st;
@@ -61,7 +62,7 @@ static int count_loose(const unsigned char *sha1, const char *path, void *data)
else {
loose_size += on_disk_bytes(st);
loose++;
- if (verbose && has_sha1_pack(sha1))
+ if (verbose && has_sha1_pack(oid->hash))
packed_loose++;
}
return 0;
@@ -73,6 +74,14 @@ static int count_cruft(const char *basename, const char *path, void *data)
return 0;
}
+static int print_alternate(struct alternate_object_database *alt, void *data)
+{
+ printf("alternate: ");
+ quote_c_style(alt->path, NULL, stdout, 0);
+ putchar('\n');
+ return 0;
+}
+
static char const * const count_objects_usage[] = {
N_("git count-objects [-v] [-H | --human-readable]"),
NULL
@@ -88,6 +97,8 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ git_config(git_default_config, NULL);
+
argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0);
/* we do not take arguments other than flags for now */
if (argc)
@@ -140,6 +151,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
printf("prune-packable: %lu\n", packed_loose);
printf("garbage: %lu\n", garbage);
printf("size-garbage: %s\n", garbage_buf.buf);
+ foreach_alt_odb(print_alternate, NULL);
strbuf_release(&loose_buf);
strbuf_release(&pack_buf);
strbuf_release(&garbage_buf);
diff --git a/builtin/describe.c b/builtin/describe.c
index 8a25abe..a5cd8c5 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -9,6 +9,7 @@
#include "diff.h"
#include "hashmap.h"
#include "argv-array.h"
+#include "run-command.h"
#define SEEN (1u << 0)
#define MAX_TAGS (FLAG_BITS - 1)
@@ -28,9 +29,10 @@ static int abbrev = -1; /* unspecified */
static int max_candidates = 10;
static struct hashmap names;
static int have_util;
-static const char *pattern;
+static struct string_list patterns = STRING_LIST_INIT_NODUP;
+static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
static int always;
-static const char *dirty;
+static const char *suffix, *dirty, *broken;
/* diff-index command arguments to check if working tree is dirty. */
static const char *diff_index_args[] = {
@@ -39,32 +41,32 @@ static const char *diff_index_args[] = {
struct commit_name {
struct hashmap_entry entry;
- unsigned char peeled[20];
+ struct object_id peeled;
struct tag *tag;
unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
unsigned name_checked:1;
- unsigned char sha1[20];
+ struct object_id oid;
char *path;
};
static const char *prio_names[] = {
- "head", "lightweight", "annotated",
+ N_("head"), N_("lightweight"), N_("annotated"),
};
static int commit_name_cmp(const struct commit_name *cn1,
const struct commit_name *cn2, const void *peeled)
{
- return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
+ return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
}
-static inline struct commit_name *find_commit_name(const unsigned char *peeled)
+static inline struct commit_name *find_commit_name(const struct object_id *peeled)
{
- return hashmap_get_from_hash(&names, sha1hash(peeled), peeled);
+ return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
}
static int replace_name(struct commit_name *e,
int prio,
- const unsigned char *sha1,
+ const struct object_id *oid,
struct tag **tag)
{
if (!e || e->prio < prio)
@@ -77,13 +79,13 @@ static int replace_name(struct commit_name *e,
struct tag *t;
if (!e->tag) {
- t = lookup_tag(e->sha1);
+ t = lookup_tag(e->oid.hash);
if (!t || parse_tag(t))
return 1;
e->tag = t;
}
- t = lookup_tag(sha1);
+ t = lookup_tag(oid->hash);
if (!t || parse_tag(t))
return 0;
*tag = t;
@@ -96,24 +98,24 @@ static int replace_name(struct commit_name *e,
}
static void add_to_known_names(const char *path,
- const unsigned char *peeled,
+ const struct object_id *peeled,
int prio,
- const unsigned char *sha1)
+ const struct object_id *oid)
{
struct commit_name *e = find_commit_name(peeled);
struct tag *tag = NULL;
- if (replace_name(e, prio, sha1, &tag)) {
+ if (replace_name(e, prio, oid, &tag)) {
if (!e) {
e = xmalloc(sizeof(struct commit_name));
- hashcpy(e->peeled, peeled);
- hashmap_entry_init(e, sha1hash(peeled));
+ oidcpy(&e->peeled, peeled);
+ hashmap_entry_init(e, sha1hash(peeled->hash));
hashmap_add(&names, e);
e->path = NULL;
}
e->tag = tag;
e->prio = prio;
e->name_checked = 0;
- hashcpy(e->sha1, sha1);
+ oidcpy(&e->oid, oid);
free(e->path);
e->path = xstrdup(path);
}
@@ -129,9 +131,40 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
if (!all && !is_tag)
return 0;
- /* Accept only tags that match the pattern, if given */
- if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL)))
- return 0;
+ /*
+ * If we're given exclude patterns, first exclude any tag which match
+ * any of the exclude pattern.
+ */
+ if (exclude_patterns.nr) {
+ struct string_list_item *item;
+
+ if (!is_tag)
+ return 0;
+
+ for_each_string_list_item(item, &exclude_patterns) {
+ if (!wildmatch(item->string, path + 10, 0, NULL))
+ return 0;
+ }
+ }
+
+ /*
+ * If we're given patterns, accept only tags which match at least one
+ * pattern.
+ */
+ if (patterns.nr) {
+ struct string_list_item *item;
+
+ if (!is_tag)
+ return 0;
+
+ for_each_string_list_item(item, &patterns) {
+ if (!wildmatch(item->string, path + 10, 0, NULL))
+ break;
+
+ /* If we get here, no pattern matched. */
+ return 0;
+ }
+ }
/* Is it annotated? */
if (!peel_ref(path, peeled.hash)) {
@@ -154,7 +187,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash);
+ add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
return 0;
}
@@ -212,7 +245,7 @@ static unsigned long finish_depth_computation(
static void display_name(struct commit_name *n)
{
if (n->prio == 2 && !n->tag) {
- n->tag = lookup_tag(n->sha1);
+ n->tag = lookup_tag(n->oid.hash);
if (!n->tag || parse_tag(n->tag))
die(_("annotated tag %s not available"), n->path);
}
@@ -230,14 +263,14 @@ static void display_name(struct commit_name *n)
printf("%s", n->path);
}
-static void show_suffix(int depth, const unsigned char *sha1)
+static void show_suffix(int depth, const struct object_id *oid)
{
- printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
+ printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
}
static void describe(const char *arg, int last_one)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *cmit, *gave_up_on = NULL;
struct commit_list *list;
struct commit_name *n;
@@ -246,22 +279,22 @@ static void describe(const char *arg, int last_one)
unsigned long seen_commits = 0;
unsigned int unannotated_cnt = 0;
- if (get_sha1(arg, sha1))
+ if (get_oid(arg, &oid))
die(_("Not a valid object name %s"), arg);
- cmit = lookup_commit_reference(sha1);
+ cmit = lookup_commit_reference(oid.hash);
if (!cmit)
die(_("%s is not a valid '%s' object"), arg, commit_type);
- n = find_commit_name(cmit->object.oid.hash);
+ n = find_commit_name(&cmit->object.oid);
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->oid.hash : sha1);
- if (dirty)
- printf("%s", dirty);
+ show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
+ if (suffix)
+ printf("%s", suffix);
printf("\n");
return;
}
@@ -276,7 +309,7 @@ static void describe(const char *arg, int last_one)
struct commit *c;
struct commit_name *n = hashmap_iter_first(&names, &iter);
for (; n; n = hashmap_iter_next(&iter)) {
- c = lookup_commit_reference_gently(n->peeled, 1);
+ c = lookup_commit_reference_gently(n->peeled.hash, 1);
if (c)
c->util = n;
}
@@ -337,8 +370,8 @@ static void describe(const char *arg, int last_one)
struct object_id *oid = &cmit->object.oid;
if (always) {
printf("%s", find_unique_abbrev(oid->hash, abbrev));
- if (dirty)
- printf("%s", dirty);
+ if (suffix)
+ printf("%s", suffix);
printf("\n");
return;
}
@@ -352,7 +385,7 @@ static void describe(const char *arg, int last_one)
oid_to_hex(oid));
}
- qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
+ QSORT(all_matches, match_cnt, compare_pt);
if (gave_up_on) {
commit_list_insert_by_date(gave_up_on, &list);
@@ -362,10 +395,19 @@ static void describe(const char *arg, int last_one)
free_commit_list(list);
if (debug) {
+ static int label_width = -1;
+ if (label_width < 0) {
+ int i, w;
+ for (i = 0; i < ARRAY_SIZE(prio_names); i++) {
+ w = strlen(_(prio_names[i]));
+ if (label_width < w)
+ label_width = w;
+ }
+ }
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
struct possible_tag *t = &all_matches[cur_match];
- fprintf(stderr, " %-11s %8d %s\n",
- prio_names[t->name->prio],
+ fprintf(stderr, " %-*s %8d %s\n",
+ label_width, _(prio_names[t->name->prio]),
t->depth, t->name->path);
}
fprintf(stderr, _("traversed %lu commits\n"), seen_commits);
@@ -380,9 +422,9 @@ static void describe(const char *arg, int last_one)
display_name(all_matches[0].name);
if (abbrev)
- show_suffix(all_matches[0].depth, cmit->object.oid.hash);
- if (dirty)
- printf("%s", dirty);
+ show_suffix(all_matches[0].depth, &cmit->object.oid);
+ if (suffix)
+ printf("%s", suffix);
printf("\n");
if (!last_one)
@@ -404,13 +446,18 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
N_("only output exact matches"), 0),
OPT_INTEGER(0, "candidates", &max_candidates,
N_("consider <n> most recent tags (default: 10)")),
- OPT_STRING(0, "match", &pattern, N_("pattern"),
+ OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
N_("only consider tags matching <pattern>")),
+ OPT_STRING_LIST(0, "exclude", &exclude_patterns, N_("pattern"),
+ N_("do not consider tags matching <pattern>")),
OPT_BOOL(0, "always", &always,
N_("show abbreviated commit object as fallback")),
{OPTION_STRING, 0, "dirty", &dirty, N_("mark"),
N_("append <mark> on dirty working tree (default: \"-dirty\")"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
+ {OPTION_STRING, 0, "broken", &broken, N_("mark"),
+ N_("append <mark> on broken working tree (default: \"-broken\")"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
OPT_END(),
};
@@ -430,6 +477,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die(_("--long is incompatible with --abbrev=0"));
if (contains) {
+ struct string_list_item *item;
struct argv_array args;
argv_array_init(&args);
@@ -440,8 +488,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
argv_array_push(&args, "--always");
if (!all) {
argv_array_push(&args, "--tags");
- if (pattern)
- argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
+ for_each_string_list_item(item, &patterns)
+ argv_array_pushf(&args, "--refs=refs/tags/%s", item->string);
+ for_each_string_list_item(item, &exclude_patterns)
+ argv_array_pushf(&args, "--exclude=refs/tags/%s", item->string);
}
if (argc)
argv_array_pushv(&args, argv);
@@ -456,7 +506,28 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die(_("No names found, cannot describe anything."));
if (argc == 0) {
- if (dirty) {
+ if (broken) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+ argv_array_pushv(&cp.args, diff_index_args);
+ cp.git_cmd = 1;
+ cp.no_stdin = 1;
+ cp.no_stdout = 1;
+
+ if (!dirty)
+ dirty = "-dirty";
+
+ switch (run_command(&cp)) {
+ case 0:
+ suffix = NULL;
+ break;
+ case 1:
+ suffix = dirty;
+ break;
+ default:
+ /* diff-index aborted abnormally */
+ suffix = broken;
+ }
+ } else if (dirty) {
static struct lock_file index_lock;
int fd;
@@ -469,11 +540,15 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
diff_index_args, prefix))
- dirty = NULL;
+ suffix = NULL;
+ else
+ suffix = dirty;
}
describe("HEAD", 1);
} else if (dirty) {
die(_("--dirty is incompatible with commit-ishes"));
+ } else if (broken) {
+ die(_("--broken is incompatible with commit-ishes"));
} else {
while (argc-- > 0)
describe(*argv++, argc == 0);
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 2a12b81..326f88b 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -7,46 +7,44 @@
static struct rev_info log_tree_opt;
-static int diff_tree_commit_sha1(const unsigned char *sha1)
+static int diff_tree_commit_sha1(const struct object_id *oid)
{
- struct commit *commit = lookup_commit_reference(sha1);
+ struct commit *commit = lookup_commit_reference(oid->hash);
if (!commit)
return -1;
return log_tree_commit(&log_tree_opt, commit);
}
/* Diff one or more commits. */
-static int stdin_diff_commit(struct commit *commit, char *line, int len)
+static int stdin_diff_commit(struct commit *commit, const char *p)
{
- unsigned char sha1[20];
- if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
- /* Graft the fake parents locally to the commit */
- int pos = 41;
- struct commit_list **pptr;
-
- /* Free the real parent list */
- free_commit_list(commit->parents);
- commit->parents = NULL;
- pptr = &(commit->parents);
- while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
- struct commit *parent = lookup_commit(sha1);
- if (parent) {
- pptr = &commit_list_insert(parent, pptr)->next;
- }
- pos += 41;
+ struct object_id oid;
+ struct commit_list **pptr = NULL;
+
+ /* Graft the fake parents locally to the commit */
+ while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
+ struct commit *parent = lookup_commit(oid.hash);
+ if (!pptr) {
+ /* Free the real parent list */
+ free_commit_list(commit->parents);
+ commit->parents = NULL;
+ pptr = &(commit->parents);
+ }
+ if (parent) {
+ pptr = &commit_list_insert(parent, pptr)->next;
}
}
return log_tree_commit(&log_tree_opt, commit);
}
/* Diff two trees. */
-static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+static int stdin_diff_trees(struct tree *tree1, const char *p)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct tree *tree2;
- if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+ if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
return error("Need exactly two trees, separated by a space");
- tree2 = lookup_tree(sha1);
+ tree2 = lookup_tree(oid.hash);
if (!tree2 || parse_tree(tree2))
return -1;
printf("%s %s\n", oid_to_hex(&tree1->object.oid),
@@ -60,23 +58,24 @@ static int stdin_diff_trees(struct tree *tree1, char *line, int len)
static int diff_tree_stdin(char *line)
{
int len = strlen(line);
- unsigned char sha1[20];
+ struct object_id oid;
struct object *obj;
+ const char *p;
if (!len || line[len-1] != '\n')
return -1;
line[len-1] = 0;
- if (get_sha1_hex(line, sha1))
+ if (parse_oid_hex(line, &oid, &p))
return -1;
- obj = parse_object(sha1);
+ obj = parse_object(oid.hash);
if (!obj)
return -1;
if (obj->type == OBJ_COMMIT)
- return stdin_diff_commit((struct commit *)obj, line, len);
+ return stdin_diff_commit((struct commit *)obj, p);
if (obj->type == OBJ_TREE)
- return stdin_diff_trees((struct tree *)obj, line, len);
+ return stdin_diff_trees((struct tree *)obj, p);
error("Object %s is a %s, not a commit or tree",
- sha1_to_hex(sha1), typename(obj->type));
+ oid_to_hex(&oid), typename(obj->type));
return -1;
}
@@ -114,6 +113,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,15 +140,13 @@ 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->oid.hash);
+ diff_tree_commit_sha1(&tree1->oid);
break;
case 2:
tree1 = opt->pending.objects[0].item;
tree2 = opt->pending.objects[1].item;
if (tree2->flags & UNINTERESTING) {
- struct object *tmp = tree2;
- tree2 = tree1;
- tree1 = tmp;
+ SWAP(tree2, tree1);
}
diff_tree_sha1(tree1->oid.hash,
tree2->oid.hash,
@@ -164,9 +163,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
DIFF_SETUP_USE_CACHE);
while (fgets(line, sizeof(line), stdin)) {
- unsigned char sha1[20];
+ struct object_id oid;
- if (get_sha1_hex(line, sha1)) {
+ if (get_oid_hex(line, &oid)) {
fputs(line, stdout);
fflush(stdout);
}
diff --git a/builtin/diff.c b/builtin/diff.c
index ed0acca..3d64b85 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -45,12 +45,9 @@ static void stuff_change(struct diff_options *opt,
return;
if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
- unsigned tmp;
- const unsigned char *tmp_u;
- const char *tmp_c;
- tmp = old_mode; old_mode = new_mode; new_mode = tmp;
- tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
- tmp_c = old_name; old_name = new_name; new_name = tmp_c;
+ SWAP(old_mode, new_mode);
+ SWAP(old_sha1, new_sha1);
+ SWAP(old_name, new_name);
}
if (opt->prefix &&
@@ -301,24 +298,27 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
break;
}
- if (!no_index)
- prefix = setup_git_directory_gently(&nongit);
+ prefix = setup_git_directory_gently(&nongit);
- /*
- * Treat git diff with at least one path outside of the
- * repo the same as if the command would have been executed
- * outside of a git repository. In this case it behaves
- * the same way as "git diff --no-index <a> <b>", which acts
- * as a colourful "diff" replacement.
- */
- if (nongit || ((argc == i + 2) &&
- (!path_inside_repo(prefix, argv[i]) ||
- !path_inside_repo(prefix, argv[i + 1]))))
- no_index = DIFF_NO_INDEX_IMPLICIT;
+ if (!no_index) {
+ /*
+ * Treat git diff with at least one path outside of the
+ * repo the same as if the command would have been executed
+ * outside of a git repository. In this case it behaves
+ * the same way as "git diff --no-index <a> <b>", which acts
+ * as a colourful "diff" replacement.
+ */
+ if (nongit || ((argc == i + 2) &&
+ (!path_inside_repo(prefix, argv[i]) ||
+ !path_inside_repo(prefix, argv[i + 1]))))
+ no_index = DIFF_NO_INDEX_IMPLICIT;
+ }
if (!no_index)
gitmodules_config();
+ init_diff_ui_defaults();
git_config(git_diff_ui_config, NULL);
+ precompose_argv(argc, argv);
init_revisions(&rev, prefix);
@@ -341,7 +341,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;
diff --git a/builtin/difftool.c b/builtin/difftool.c
new file mode 100644
index 0000000..25e54ad
--- /dev/null
+++ b/builtin/difftool.c
@@ -0,0 +1,733 @@
+/*
+ * "git difftool" builtin command
+ *
+ * This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
+ * git-difftool--helper script.
+ *
+ * This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
+ * The GIT_DIFF* variables are exported for use by git-difftool--helper.
+ *
+ * Any arguments that are unknown to this script are forwarded to 'git diff'.
+ *
+ * Copyright (C) 2016 Johannes Schindelin
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "exec_cmd.h"
+#include "parse-options.h"
+#include "argv-array.h"
+#include "strbuf.h"
+#include "lockfile.h"
+#include "dir.h"
+
+static char *diff_gui_tool;
+static int trust_exit_code;
+
+static const char *const builtin_difftool_usage[] = {
+ N_("git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"),
+ NULL
+};
+
+static int difftool_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "diff.guitool")) {
+ diff_gui_tool = xstrdup(value);
+ return 0;
+ }
+
+ if (!strcmp(var, "difftool.trustexitcode")) {
+ trust_exit_code = git_config_bool(var, value);
+ return 0;
+ }
+
+ return git_default_config(var, value, cb);
+}
+
+static int print_tool_help(void)
+{
+ const char *argv[] = { "mergetool", "--tool-help=diff", NULL };
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int parse_index_info(char *p, int *mode1, int *mode2,
+ struct object_id *oid1, struct object_id *oid2,
+ char *status)
+{
+ if (*p != ':')
+ return error("expected ':', got '%c'", *p);
+ *mode1 = (int)strtol(p + 1, &p, 8);
+ if (*p != ' ')
+ return error("expected ' ', got '%c'", *p);
+ *mode2 = (int)strtol(p + 1, &p, 8);
+ if (*p != ' ')
+ return error("expected ' ', got '%c'", *p);
+ if (get_oid_hex(++p, oid1))
+ return error("expected object ID, got '%s'", p + 1);
+ p += GIT_SHA1_HEXSZ;
+ if (*p != ' ')
+ return error("expected ' ', got '%c'", *p);
+ if (get_oid_hex(++p, oid2))
+ return error("expected object ID, got '%s'", p + 1);
+ p += GIT_SHA1_HEXSZ;
+ if (*p != ' ')
+ return error("expected ' ', got '%c'", *p);
+ *status = *++p;
+ if (!*status)
+ return error("missing status");
+ if (p[1] && !isdigit(p[1]))
+ return error("unexpected trailer: '%s'", p + 1);
+ return 0;
+}
+
+/*
+ * Remove any trailing slash from $workdir
+ * before starting to avoid double slashes in symlink targets.
+ */
+static void add_path(struct strbuf *buf, size_t base_len, const char *path)
+{
+ strbuf_setlen(buf, base_len);
+ if (buf->len && buf->buf[buf->len - 1] != '/')
+ strbuf_addch(buf, '/');
+ strbuf_addstr(buf, path);
+}
+
+/*
+ * Determine whether we can simply reuse the file in the worktree.
+ */
+static int use_wt_file(const char *workdir, const char *name,
+ struct object_id *oid)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct stat st;
+ int use = 0;
+
+ strbuf_addstr(&buf, workdir);
+ add_path(&buf, buf.len, name);
+
+ if (!lstat(buf.buf, &st) && !S_ISLNK(st.st_mode)) {
+ struct object_id wt_oid;
+ int fd = open(buf.buf, O_RDONLY);
+
+ if (fd >= 0 &&
+ !index_fd(wt_oid.hash, fd, &st, OBJ_BLOB, name, 0)) {
+ if (is_null_oid(oid)) {
+ oidcpy(oid, &wt_oid);
+ use = 1;
+ } else if (!oidcmp(oid, &wt_oid))
+ use = 1;
+ }
+ }
+
+ strbuf_release(&buf);
+
+ return use;
+}
+
+struct working_tree_entry {
+ struct hashmap_entry entry;
+ char path[FLEX_ARRAY];
+};
+
+static int working_tree_entry_cmp(struct working_tree_entry *a,
+ struct working_tree_entry *b, void *keydata)
+{
+ return strcmp(a->path, b->path);
+}
+
+/*
+ * The `left` and `right` entries hold paths for the symlinks hashmap,
+ * and a SHA-1 surrounded by brief text for submodules.
+ */
+struct pair_entry {
+ struct hashmap_entry entry;
+ char left[PATH_MAX], right[PATH_MAX];
+ const char path[FLEX_ARRAY];
+};
+
+static int pair_cmp(struct pair_entry *a, struct pair_entry *b, void *keydata)
+{
+ return strcmp(a->path, b->path);
+}
+
+static void add_left_or_right(struct hashmap *map, const char *path,
+ const char *content, int is_right)
+{
+ struct pair_entry *e, *existing;
+
+ FLEX_ALLOC_STR(e, path, path);
+ hashmap_entry_init(e, strhash(path));
+ existing = hashmap_get(map, e, NULL);
+ if (existing) {
+ free(e);
+ e = existing;
+ } else {
+ e->left[0] = e->right[0] = '\0';
+ hashmap_add(map, e);
+ }
+ strlcpy(is_right ? e->right : e->left, content, PATH_MAX);
+}
+
+struct path_entry {
+ struct hashmap_entry entry;
+ char path[FLEX_ARRAY];
+};
+
+static int path_entry_cmp(struct path_entry *a, struct path_entry *b, void *key)
+{
+ return strcmp(a->path, key ? key : b->path);
+}
+
+static void changed_files(struct hashmap *result, const char *index_path,
+ const char *workdir)
+{
+ struct child_process update_index = CHILD_PROCESS_INIT;
+ struct child_process diff_files = CHILD_PROCESS_INIT;
+ struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT;
+ const char *git_dir = absolute_path(get_git_dir()), *env[] = {
+ NULL, NULL
+ };
+ FILE *fp;
+
+ strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
+ env[0] = index_env.buf;
+
+ argv_array_pushl(&update_index.args,
+ "--git-dir", git_dir, "--work-tree", workdir,
+ "update-index", "--really-refresh", "-q",
+ "--unmerged", NULL);
+ update_index.no_stdin = 1;
+ update_index.no_stdout = 1;
+ update_index.no_stderr = 1;
+ update_index.git_cmd = 1;
+ update_index.use_shell = 0;
+ update_index.clean_on_exit = 1;
+ update_index.dir = workdir;
+ update_index.env = env;
+ /* Ignore any errors of update-index */
+ run_command(&update_index);
+
+ argv_array_pushl(&diff_files.args,
+ "--git-dir", git_dir, "--work-tree", workdir,
+ "diff-files", "--name-only", "-z", NULL);
+ diff_files.no_stdin = 1;
+ diff_files.git_cmd = 1;
+ diff_files.use_shell = 0;
+ diff_files.clean_on_exit = 1;
+ diff_files.out = -1;
+ diff_files.dir = workdir;
+ diff_files.env = env;
+ if (start_command(&diff_files))
+ die("could not obtain raw diff");
+ fp = xfdopen(diff_files.out, "r");
+ while (!strbuf_getline_nul(&buf, fp)) {
+ struct path_entry *entry;
+ FLEX_ALLOC_STR(entry, path, buf.buf);
+ hashmap_entry_init(entry, strhash(buf.buf));
+ hashmap_add(result, entry);
+ }
+ if (finish_command(&diff_files))
+ die("diff-files did not exit properly");
+ strbuf_release(&index_env);
+ strbuf_release(&buf);
+}
+
+static NORETURN void exit_cleanup(const char *tmpdir, int exit_code)
+{
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addstr(&buf, tmpdir);
+ remove_dir_recursively(&buf, 0);
+ if (exit_code)
+ warning(_("failed: %d"), exit_code);
+ exit(exit_code);
+}
+
+static int ensure_leading_directories(char *path)
+{
+ switch (safe_create_leading_directories(path)) {
+ case SCLD_OK:
+ case SCLD_EXISTS:
+ return 0;
+ default:
+ return error(_("could not create leading directories "
+ "of '%s'"), path);
+ }
+}
+
+/*
+ * Unconditional writing of a plain regular file is what
+ * "git difftool --dir-diff" wants to do for symlinks. We are preparing two
+ * temporary directories to be fed to a Git-unaware tool that knows how to
+ * show a diff of two directories (e.g. "diff -r A B").
+ *
+ * Because the tool is Git-unaware, if a symbolic link appears in either of
+ * these temporary directories, it will try to dereference and show the
+ * difference of the target of the symbolic link, which is not what we want,
+ * as the goal of the dir-diff mode is to produce an output that is logically
+ * equivalent to what "git diff" produces.
+ *
+ * Most importantly, we want to get textual comparison of the result of the
+ * readlink(2). get_symlink() provides that---it returns the contents of
+ * the symlink that gets written to a regular file to force the external tool
+ * to compare the readlink(2) result as text, even on a filesystem that is
+ * capable of doing a symbolic link.
+ */
+static char *get_symlink(const struct object_id *oid, const char *path)
+{
+ char *data;
+ if (is_null_oid(oid)) {
+ /* The symlink is unknown to Git so read from the filesystem */
+ struct strbuf link = STRBUF_INIT;
+ if (has_symlinks) {
+ if (strbuf_readlink(&link, path, strlen(path)))
+ die(_("could not read symlink %s"), path);
+ } else if (strbuf_read_file(&link, path, 128))
+ die(_("could not read symlink file %s"), path);
+
+ data = strbuf_detach(&link, NULL);
+ } else {
+ enum object_type type;
+ unsigned long size;
+ data = read_sha1_file(oid->hash, &type, &size);
+ if (!data)
+ die(_("could not read object %s for symlink %s"),
+ oid_to_hex(oid), path);
+ }
+
+ return data;
+}
+
+static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
+ int argc, const char **argv)
+{
+ char tmpdir[PATH_MAX];
+ struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
+ struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
+ struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
+ struct strbuf wtdir = STRBUF_INIT;
+ size_t ldir_len, rdir_len, wtdir_len;
+ struct cache_entry *ce = xcalloc(1, sizeof(ce) + PATH_MAX + 1);
+ const char *workdir, *tmp;
+ int ret = 0, i;
+ FILE *fp;
+ struct hashmap working_tree_dups, submodules, symlinks2;
+ struct hashmap_iter iter;
+ struct pair_entry *entry;
+ struct index_state wtindex;
+ struct checkout lstate, rstate;
+ int rc, flags = RUN_GIT_CMD, err = 0;
+ struct child_process child = CHILD_PROCESS_INIT;
+ const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
+ struct hashmap wt_modified, tmp_modified;
+ int indices_loaded = 0;
+
+ workdir = get_git_work_tree();
+
+ /* Setup temp directories */
+ tmp = getenv("TMPDIR");
+ xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp");
+ if (!mkdtemp(tmpdir))
+ return error("could not create '%s'", tmpdir);
+ strbuf_addf(&ldir, "%s/left/", tmpdir);
+ strbuf_addf(&rdir, "%s/right/", tmpdir);
+ strbuf_addstr(&wtdir, workdir);
+ if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1]))
+ strbuf_addch(&wtdir, '/');
+ mkdir(ldir.buf, 0700);
+ mkdir(rdir.buf, 0700);
+
+ memset(&wtindex, 0, sizeof(wtindex));
+
+ memset(&lstate, 0, sizeof(lstate));
+ lstate.base_dir = ldir.buf;
+ lstate.base_dir_len = ldir.len;
+ lstate.force = 1;
+ memset(&rstate, 0, sizeof(rstate));
+ rstate.base_dir = rdir.buf;
+ rstate.base_dir_len = rdir.len;
+ rstate.force = 1;
+
+ ldir_len = ldir.len;
+ rdir_len = rdir.len;
+ wtdir_len = wtdir.len;
+
+ hashmap_init(&working_tree_dups,
+ (hashmap_cmp_fn)working_tree_entry_cmp, 0);
+ hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, 0);
+ hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, 0);
+
+ child.no_stdin = 1;
+ child.git_cmd = 1;
+ child.use_shell = 0;
+ child.clean_on_exit = 1;
+ child.dir = prefix;
+ child.out = -1;
+ argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
+ NULL);
+ for (i = 0; i < argc; i++)
+ argv_array_push(&child.args, argv[i]);
+ if (start_command(&child))
+ die("could not obtain raw diff");
+ fp = xfdopen(child.out, "r");
+
+ /* Build index info for left and right sides of the diff */
+ i = 0;
+ while (!strbuf_getline_nul(&info, fp)) {
+ int lmode, rmode;
+ struct object_id loid, roid;
+ char status;
+ const char *src_path, *dst_path;
+ size_t src_path_len, dst_path_len;
+
+ if (starts_with(info.buf, "::"))
+ die(N_("combined diff formats('-c' and '--cc') are "
+ "not supported in\n"
+ "directory diff mode('-d' and '--dir-diff')."));
+
+ if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
+ &status))
+ break;
+ if (strbuf_getline_nul(&lpath, fp))
+ break;
+ src_path = lpath.buf;
+ src_path_len = lpath.len;
+
+ i++;
+ if (status != 'C' && status != 'R') {
+ dst_path = src_path;
+ dst_path_len = src_path_len;
+ } else {
+ if (strbuf_getline_nul(&rpath, fp))
+ break;
+ dst_path = rpath.buf;
+ dst_path_len = rpath.len;
+ }
+
+ if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "Subproject commit %s",
+ oid_to_hex(&loid));
+ add_left_or_right(&submodules, src_path, buf.buf, 0);
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "Subproject commit %s",
+ oid_to_hex(&roid));
+ if (!oidcmp(&loid, &roid))
+ strbuf_addstr(&buf, "-dirty");
+ add_left_or_right(&submodules, dst_path, buf.buf, 1);
+ continue;
+ }
+
+ if (S_ISLNK(lmode)) {
+ char *content = get_symlink(&loid, src_path);
+ add_left_or_right(&symlinks2, src_path, content, 0);
+ free(content);
+ }
+
+ if (S_ISLNK(rmode)) {
+ char *content = get_symlink(&roid, dst_path);
+ add_left_or_right(&symlinks2, dst_path, content, 1);
+ free(content);
+ }
+
+ if (lmode && status != 'C') {
+ ce->ce_mode = lmode;
+ oidcpy(&ce->oid, &loid);
+ strcpy(ce->name, src_path);
+ ce->ce_namelen = src_path_len;
+ if (checkout_entry(ce, &lstate, NULL))
+ return error("could not write '%s'", src_path);
+ }
+
+ if (rmode && !S_ISLNK(rmode)) {
+ struct working_tree_entry *entry;
+
+ /* Avoid duplicate working_tree entries */
+ FLEX_ALLOC_STR(entry, path, dst_path);
+ hashmap_entry_init(entry, strhash(dst_path));
+ if (hashmap_get(&working_tree_dups, entry, NULL)) {
+ free(entry);
+ continue;
+ }
+ hashmap_add(&working_tree_dups, entry);
+
+ if (!use_wt_file(workdir, dst_path, &roid)) {
+ ce->ce_mode = rmode;
+ oidcpy(&ce->oid, &roid);
+ strcpy(ce->name, dst_path);
+ ce->ce_namelen = dst_path_len;
+ if (checkout_entry(ce, &rstate, NULL))
+ return error("could not write '%s'",
+ dst_path);
+ } else if (!is_null_oid(&roid)) {
+ /*
+ * Changes in the working tree need special
+ * treatment since they are not part of the
+ * index.
+ */
+ struct cache_entry *ce2 =
+ make_cache_entry(rmode, roid.hash,
+ dst_path, 0, 0);
+
+ add_index_entry(&wtindex, ce2,
+ ADD_CACHE_JUST_APPEND);
+
+ add_path(&rdir, rdir_len, dst_path);
+ if (ensure_leading_directories(rdir.buf))
+ return error("could not create "
+ "directory for '%s'",
+ dst_path);
+ add_path(&wtdir, wtdir_len, dst_path);
+ if (symlinks) {
+ if (symlink(wtdir.buf, rdir.buf)) {
+ ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf);
+ goto finish;
+ }
+ } else {
+ struct stat st;
+ if (stat(wtdir.buf, &st))
+ st.st_mode = 0644;
+ if (copy_file(rdir.buf, wtdir.buf,
+ st.st_mode)) {
+ ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf);
+ goto finish;
+ }
+ }
+ }
+ }
+ }
+
+ if (finish_command(&child)) {
+ ret = error("error occurred running diff --raw");
+ goto finish;
+ }
+
+ if (!i)
+ return 0;
+
+ /*
+ * Changes to submodules require special treatment.This loop writes a
+ * temporary file to both the left and right directories to show the
+ * change in the recorded SHA1 for the submodule.
+ */
+ hashmap_iter_init(&submodules, &iter);
+ while ((entry = hashmap_iter_next(&iter))) {
+ if (*entry->left) {
+ add_path(&ldir, ldir_len, entry->path);
+ ensure_leading_directories(ldir.buf);
+ write_file(ldir.buf, "%s", entry->left);
+ }
+ if (*entry->right) {
+ add_path(&rdir, rdir_len, entry->path);
+ ensure_leading_directories(rdir.buf);
+ write_file(rdir.buf, "%s", entry->right);
+ }
+ }
+
+ /*
+ * Symbolic links require special treatment.The standard "git diff"
+ * shows only the link itself, not the contents of the link target.
+ * This loop replicates that behavior.
+ */
+ hashmap_iter_init(&symlinks2, &iter);
+ while ((entry = hashmap_iter_next(&iter))) {
+ if (*entry->left) {
+ add_path(&ldir, ldir_len, entry->path);
+ ensure_leading_directories(ldir.buf);
+ write_file(ldir.buf, "%s", entry->left);
+ }
+ if (*entry->right) {
+ add_path(&rdir, rdir_len, entry->path);
+ ensure_leading_directories(rdir.buf);
+ write_file(rdir.buf, "%s", entry->right);
+ }
+ }
+
+ strbuf_release(&buf);
+
+ strbuf_setlen(&ldir, ldir_len);
+ helper_argv[1] = ldir.buf;
+ strbuf_setlen(&rdir, rdir_len);
+ helper_argv[2] = rdir.buf;
+
+ if (extcmd) {
+ helper_argv[0] = extcmd;
+ flags = 0;
+ } else
+ setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
+ rc = run_command_v_opt(helper_argv, flags);
+
+ /*
+ * If the diff includes working copy files and those
+ * files were modified during the diff, then the changes
+ * should be copied back to the working tree.
+ * Do not copy back files when symlinks are used and the
+ * external tool did not replace the original link with a file.
+ *
+ * These hashes are loaded lazily since they aren't needed
+ * in the common case of --symlinks and the difftool updating
+ * files through the symlink.
+ */
+ hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp,
+ wtindex.cache_nr);
+ hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp,
+ wtindex.cache_nr);
+
+ for (i = 0; i < wtindex.cache_nr; i++) {
+ struct hashmap_entry dummy;
+ const char *name = wtindex.cache[i]->name;
+ struct stat st;
+
+ add_path(&rdir, rdir_len, name);
+ if (lstat(rdir.buf, &st))
+ continue;
+
+ if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode))
+ continue;
+
+ if (!indices_loaded) {
+ static struct lock_file lock;
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "%s/wtindex", tmpdir);
+ if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
+ write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
+ ret = error("could not write %s", buf.buf);
+ rollback_lock_file(&lock);
+ goto finish;
+ }
+ changed_files(&wt_modified, buf.buf, workdir);
+ strbuf_setlen(&rdir, rdir_len);
+ changed_files(&tmp_modified, buf.buf, rdir.buf);
+ add_path(&rdir, rdir_len, name);
+ indices_loaded = 1;
+ }
+
+ hashmap_entry_init(&dummy, strhash(name));
+ if (hashmap_get(&tmp_modified, &dummy, name)) {
+ add_path(&wtdir, wtdir_len, name);
+ if (hashmap_get(&wt_modified, &dummy, name)) {
+ warning(_("both files modified: '%s' and '%s'."),
+ wtdir.buf, rdir.buf);
+ warning(_("working tree file has been left."));
+ warning("%s", "");
+ err = 1;
+ } else if (unlink(wtdir.buf) ||
+ copy_file(wtdir.buf, rdir.buf, st.st_mode))
+ warning_errno(_("could not copy '%s' to '%s'"),
+ rdir.buf, wtdir.buf);
+ }
+ }
+
+ if (err) {
+ warning(_("temporary files exist in '%s'."), tmpdir);
+ warning(_("you may want to cleanup or recover these."));
+ exit(1);
+ } else
+ exit_cleanup(tmpdir, rc);
+
+finish:
+ free(ce);
+ strbuf_release(&ldir);
+ strbuf_release(&rdir);
+ strbuf_release(&wtdir);
+ strbuf_release(&buf);
+
+ return ret;
+}
+
+static int run_file_diff(int prompt, const char *prefix,
+ int argc, const char **argv)
+{
+ struct argv_array args = ARGV_ARRAY_INIT;
+ const char *env[] = {
+ "GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
+ NULL
+ };
+ int ret = 0, i;
+
+ if (prompt > 0)
+ env[2] = "GIT_DIFFTOOL_PROMPT=true";
+ else if (!prompt)
+ env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+
+
+ argv_array_push(&args, "diff");
+ for (i = 0; i < argc; i++)
+ argv_array_push(&args, argv[i]);
+ ret = run_command_v_opt_cd_env(args.argv, RUN_GIT_CMD, prefix, env);
+ exit(ret);
+}
+
+int cmd_difftool(int argc, const char **argv, const char *prefix)
+{
+ int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
+ tool_help = 0;
+ static char *difftool_cmd = NULL, *extcmd = NULL;
+ struct option builtin_difftool_options[] = {
+ OPT_BOOL('g', "gui", &use_gui_tool,
+ N_("use `diff.guitool` instead of `diff.tool`")),
+ OPT_BOOL('d', "dir-diff", &dir_diff,
+ N_("perform a full-directory diff")),
+ { OPTION_SET_INT, 'y', "no-prompt", &prompt, NULL,
+ N_("do not prompt before launching a diff tool"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
+ { OPTION_SET_INT, 0, "prompt", &prompt, NULL, NULL,
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ NULL, 1 },
+ OPT_BOOL(0, "symlinks", &symlinks,
+ N_("use symlinks in dir-diff mode")),
+ OPT_STRING('t', "tool", &difftool_cmd, N_("<tool>"),
+ N_("use the specified diff tool")),
+ OPT_BOOL(0, "tool-help", &tool_help,
+ N_("print a list of diff tools that may be used with "
+ "`--tool`")),
+ OPT_BOOL(0, "trust-exit-code", &trust_exit_code,
+ N_("make 'git-difftool' exit when an invoked diff "
+ "tool returns a non - zero exit code")),
+ OPT_STRING('x', "extcmd", &extcmd, N_("<command>"),
+ N_("specify a custom command for viewing diffs")),
+ OPT_END()
+ };
+
+ git_config(difftool_config, NULL);
+ symlinks = has_symlinks;
+
+ argc = parse_options(argc, argv, prefix, builtin_difftool_options,
+ builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN |
+ PARSE_OPT_KEEP_DASHDASH);
+
+ if (tool_help)
+ return print_tool_help();
+
+ /* NEEDSWORK: once we no longer spawn anything, remove this */
+ setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
+ setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
+
+ if (use_gui_tool && diff_gui_tool && *diff_gui_tool)
+ setenv("GIT_DIFF_TOOL", diff_gui_tool, 1);
+ else if (difftool_cmd) {
+ if (*difftool_cmd)
+ setenv("GIT_DIFF_TOOL", difftool_cmd, 1);
+ else
+ die(_("no <tool> given for --tool=<tool>"));
+ }
+
+ if (extcmd) {
+ if (*extcmd)
+ setenv("GIT_DIFFTOOL_EXTCMD", extcmd, 1);
+ else
+ die(_("no <cmd> given for --extcmd=<cmd>"));
+ }
+
+ setenv("GIT_DIFFTOOL_TRUST_EXIT_CODE",
+ trust_exit_code ? "true" : "false", 1);
+
+ /*
+ * In directory diff mode, 'git-difftool--helper' is called once
+ * to compare the a / b directories. In file diff mode, 'git diff'
+ * will invoke a separate instance of 'git-difftool--helper' for
+ * each file that changed.
+ */
+ if (dir_diff)
+ return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
+ return run_file_diff(prompt, prefix, argc, argv);
+}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index d9ac5d8..e022063 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -212,7 +212,7 @@ static char *anonymize_blob(unsigned long *size)
return strbuf_detach(&out, NULL);
}
-static void export_blob(const unsigned char *sha1)
+static void export_blob(const struct object_id *oid)
{
unsigned long size;
enum object_type type;
@@ -223,34 +223,34 @@ static void export_blob(const unsigned char *sha1)
if (no_data)
return;
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return;
- object = lookup_object(sha1);
+ object = lookup_object(oid->hash);
if (object && object->flags & SHOWN)
return;
if (anonymize) {
buf = anonymize_blob(&size);
- object = (struct object *)lookup_blob(sha1);
+ object = (struct object *)lookup_blob(oid->hash);
eaten = 0;
} else {
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf)
- die ("Could not read blob %s", sha1_to_hex(sha1));
- if (check_sha1_signature(sha1, buf, size, typename(type)) < 0)
- die("sha1 mismatch in blob %s", sha1_to_hex(sha1));
- object = parse_object_buffer(sha1, type, size, buf, &eaten);
+ die ("Could not read blob %s", oid_to_hex(oid));
+ if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0)
+ die("sha1 mismatch in blob %s", oid_to_hex(oid));
+ object = parse_object_buffer(oid->hash, type, size, buf, &eaten);
}
if (!object)
- die("Could not read blob %s", sha1_to_hex(sha1));
+ die("Could not read blob %s", oid_to_hex(oid));
mark_next_object(object);
printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
if (size && fwrite(buf, size, 1, stdout) != 1)
- die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
+ die_errno ("Could not write blob '%s'", oid_to_hex(oid));
printf("\n");
show_progress();
@@ -323,19 +323,19 @@ static void print_path(const char *path)
}
}
-static void *generate_fake_sha1(const void *old, size_t *len)
+static void *generate_fake_oid(const void *old, size_t *len)
{
static uint32_t counter = 1; /* avoid null sha1 */
- unsigned char *out = xcalloc(20, 1);
- put_be32(out + 16, counter++);
+ unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1);
+ put_be32(out + GIT_SHA1_RAWSZ - 4, counter++);
return out;
}
-static const unsigned char *anonymize_sha1(const unsigned char *sha1)
+static const unsigned char *anonymize_sha1(const struct object_id *oid)
{
static struct hashmap sha1s;
- size_t len = 20;
- return anonymize_mem(&sha1s, generate_fake_sha1, sha1, &len);
+ size_t len = GIT_SHA1_RAWSZ;
+ return anonymize_mem(&sha1s, generate_fake_oid, oid, &len);
}
static void show_filemodify(struct diff_queue_struct *q,
@@ -347,7 +347,7 @@ static void show_filemodify(struct diff_queue_struct *q,
* Handle files below a directory first, in case they are all deleted
* and the directory changes to a file or symlink.
*/
- qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+ QSORT(q->queue, q->nr, depth_first);
for (i = 0; i < q->nr; i++) {
struct diff_filespec *ospec = q->queue[i]->one;
@@ -368,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 */
@@ -383,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) :
+ 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));
}
@@ -572,7 +572,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
/* 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);
refname = commit->util;
if (anonymize) {
@@ -797,14 +797,14 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
for (i = 0; i < info->nr; i++) {
struct rev_cmdline_entry *e = info->rev + i;
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *commit;
char *full_name;
if (e->flags & UNINTERESTING)
continue;
- if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
+ if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1)
continue;
if (refspecs) {
@@ -828,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.oid.hash);
+ export_blob(&commit->object.oid);
continue;
default: /* OBJ_TAG (nested tags) is already handled */
warning("Tag points to object of unexpected type %s, skipping.",
@@ -880,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);
@@ -912,7 +912,7 @@ static void import_marks(char *input_file)
while (fgets(line, sizeof(line), f)) {
uint32_t mark;
char *line_end, *mark_end;
- unsigned char sha1[20];
+ struct object_id oid;
struct object *object;
struct commit *commit;
enum object_type type;
@@ -924,28 +924,28 @@ static void import_marks(char *input_file)
mark = strtoumax(line + 1, &mark_end, 10);
if (!mark || mark_end == line + 1
- || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
+ || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
die("corrupt mark line: %s", line);
if (last_idnum < mark)
last_idnum = mark;
- type = sha1_object_info(sha1, NULL);
+ type = sha1_object_info(oid.hash, NULL);
if (type < 0)
- die("object not found: %s", sha1_to_hex(sha1));
+ die("object not found: %s", oid_to_hex(&oid));
if (type != OBJ_COMMIT)
/* only commits */
continue;
- commit = lookup_commit(sha1);
+ commit = lookup_commit(oid.hash);
if (!commit)
- die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+ die("not a commit? can't happen: %s", oid_to_hex(&oid));
object = &commit->object;
if (object->flags & SHOWN)
- error("Object %s already has a mark", sha1_to_hex(sha1));
+ error("Object %s already has a mark", oid_to_hex(&oid));
mark_object(object, mark);
@@ -1021,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 cf3019e..2a1c1c2 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -10,33 +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);
+ struct ref *ref;
struct object_id oid;
- const int chunksz = GIT_SHA1_HEXSZ + 1;
- if (namelen > chunksz && name[chunksz - 1] == ' ' &&
- !get_oid_hex(name, &oid)) {
- oidcpy(&ref->old_oid, &oid);
- name += chunksz;
- namelen -= chunksz;
+ 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;
@@ -50,6 +51,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
struct child_process *conn;
struct fetch_pack_args args;
struct sha1_array shallow = SHA1_ARRAY_INIT;
+ struct string_list deepen_not = STRING_LIST_INIT_DUP;
packet_trace_identity("fetch-pack");
@@ -59,12 +61,12 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc && *argv[i] == '-'; i++) {
const char *arg = argv[i];
- if (starts_with(arg, "--upload-pack=")) {
- args.uploadpack = arg + 14;
+ if (skip_prefix(arg, "--upload-pack=", &arg)) {
+ args.uploadpack = arg;
continue;
}
- if (starts_with(arg, "--exec=")) {
- args.uploadpack = arg + 7;
+ if (skip_prefix(arg, "--exec=", &arg)) {
+ args.uploadpack = arg;
continue;
}
if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
@@ -100,8 +102,20 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.verbose = 1;
continue;
}
- if (starts_with(arg, "--depth=")) {
- args.depth = strtol(arg + 8, NULL, 0);
+ if (skip_prefix(arg, "--depth=", &arg)) {
+ args.depth = strtol(arg, NULL, 0);
+ continue;
+ }
+ if (skip_prefix(arg, "--shallow-since=", &arg)) {
+ args.deepen_since = xstrdup(arg);
+ continue;
+ }
+ if (skip_prefix(arg, "--shallow-exclude=", &arg)) {
+ string_list_append(&deepen_not, arg);
+ continue;
+ }
+ if (!strcmp(arg, "--deepen-relative")) {
+ args.deepen_relative = 1;
continue;
}
if (!strcmp("--no-progress", arg)) {
@@ -131,6 +145,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
}
usage(fetch_pack_usage);
}
+ if (deepen_not.nr)
+ args.deepen_not = &deepen_not;
if (i < argc)
dest = argv[i++];
@@ -158,7 +174,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);
}
@@ -203,12 +219,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
* remote no-such-ref' would silently succeed without issuing
* an error.
*/
- for (i = 0; i < nr_sought; i++) {
- if (!sought[i] || sought[i]->matched)
- continue;
- error("no such remote ref %s", sought[i]->name);
- ret = 1;
- }
+ ret |= report_unmatched_refs(sought, nr_sought);
while (ref) {
printf("%s %s\n",
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c85f347..b5ad09d 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -15,6 +15,7 @@
#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>...]]"),
@@ -34,11 +35,15 @@ static int fetch_prune_config = -1; /* unspecified */
static int prune = -1; /* unspecified */
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int tags = TAGS_DEFAULT, unshallow, update_shallow;
+static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
+static int max_children = -1;
+static enum transport_family family;
static const char *depth;
+static const char *deepen_since;
static const char *upload_pack;
+static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
static struct strbuf default_rla = STRBUF_INIT;
static struct transport *gtransport;
static struct transport *gsecondary;
@@ -99,6 +104,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"),
@@ -112,6 +119,12 @@ static struct option builtin_fetch_options[] = {
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
OPT_STRING(0, "depth", &depth, N_("depth"),
N_("deepen history of shallow clone")),
+ OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+ N_("deepen history of shallow repository based on time")),
+ OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+ N_("deepen history of shallow clone, excluding rev")),
+ OPT_INTEGER(0, "deepen", &deepen_relative,
+ N_("deepen history of shallow clone")),
{ OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
N_("convert to a complete repository"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
@@ -124,6 +137,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()
};
@@ -224,9 +241,10 @@ 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_object_file(&ref->old_oid) &&
+ if (item &&
+ !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) &&
!will_fetch(head, ref->old_oid.hash) &&
- !has_sha1_file(item->util) &&
+ !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
item = NULL;
@@ -239,7 +257,8 @@ static void find_non_local_tags(struct transport *transport,
* to check if it is a lightweight tag that we want to
* fetch.
*/
- if (item && !has_sha1_file(item->util) &&
+ if (item &&
+ !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
@@ -259,7 +278,8 @@ static void find_non_local_tags(struct transport *transport,
* We may have a final lightweight tag that needs to be
* checked to see if it needs fetching.
*/
- if (item && !has_sha1_file(item->util) &&
+ if (item &&
+ !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
@@ -339,9 +359,6 @@ static struct ref *get_ref_map(struct transport *transport,
for (i = 0; i < fetch_refspec_nr; i++)
get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
-
- if (tags == TAGS_SET)
- get_fetch_map(remote_refs, tag_refspec, &tail, 0);
} else if (refmap_array) {
die("--refmap option is only meaningful with command-line refspec(s).");
} else {
@@ -441,12 +458,141 @@ 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,
+ int summary_width)
+{
+ int width = (summary_width + strlen(summary) - gettext_width(summary));
+
+ strbuf_addf(display, "%c %-*s ", code, width, 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,
const struct ref *remote_ref,
- struct strbuf *display)
+ struct strbuf *display,
+ int summary_width)
{
struct commit *current = NULL, *updated;
enum object_type type;
@@ -459,9 +605,8 @@ static int update_local_ref(struct ref *ref,
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, summary_width);
return 0;
}
@@ -473,10 +618,9 @@ static int update_local_ref(struct ref *ref,
* 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, summary_width);
return 1;
}
@@ -484,11 +628,9 @@ static int update_local_ref(struct ref *ref,
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, summary_width);
return r;
}
@@ -519,11 +661,9 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
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, summary_width);
return r;
}
@@ -537,11 +677,9 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
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.buf,
- 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, summary_width);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
@@ -554,18 +692,14 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
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.buf,
- 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, summary_width);
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, summary_width);
return 1;
}
}
@@ -596,10 +730,11 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
char *url;
const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
int want_status;
+ int summary_width = transport_summary_width(ref_map);
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);
@@ -607,11 +742,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
@@ -703,14 +840,14 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
strbuf_reset(&note);
if (ref) {
- rc |= update_local_ref(ref, what, rm, &note);
+ rc |= update_local_ref(ref, what, rm, &note,
+ summary_width);
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", summary_width);
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
@@ -743,6 +880,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
@@ -751,9 +889,10 @@ static int quickfetch(struct ref *ref_map)
* really need to perform. Claiming failure now will ensure
* we perform the network exchange to deepen our history.
*/
- if (depth)
+ if (deepen)
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)
@@ -775,6 +914,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
int url_len, i, result = 0;
struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
char *url;
+ int summary_width = transport_summary_width(stale_refs);
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
@@ -798,19 +938,22 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs(&refnames);
+ 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;
}
- fprintf(stderr, " x %-*s %-*s -> %s\n",
- TRANSPORT_SUMMARY(_("[deleted]")),
- REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
+ format_display(&sb, '-', _("[deleted]"), NULL,
+ _("(none)"), prettify_refname(ref->name),
+ summary_width);
+ fprintf(stderr, " %s\n",sb.buf);
+ strbuf_release(&sb);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
@@ -837,10 +980,10 @@ static void check_not_current_branch(struct ref *ref_map)
static int truncate_fetch_head(void)
{
const char *filename = git_path_fetch_head();
- FILE *fp = fopen(filename, "w");
+ 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;
}
@@ -856,17 +999,25 @@ static void set_option(struct transport *transport, const char *name, const char
name, transport->url);
}
-static struct transport *prepare_transport(struct remote *remote)
+static struct transport *prepare_transport(struct remote *remote, int deepen)
{
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)
set_option(transport, TRANS_OPT_KEEP, "yes");
if (depth)
set_option(transport, TRANS_OPT_DEPTH, depth);
+ if (deepen && deepen_since)
+ set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);
+ if (deepen && deepen_not.nr)
+ set_option(transport, TRANS_OPT_DEEPEN_NOT,
+ (const char *)&deepen_not);
+ if (deepen_relative)
+ set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
if (update_shallow)
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
return transport;
@@ -874,13 +1025,25 @@ static struct transport *prepare_transport(struct remote *remote)
static void backfill_tags(struct transport *transport, struct ref *ref_map)
{
- if (transport->cannot_reuse) {
- gsecondary = prepare_transport(transport->remote);
+ int cannot_reuse;
+
+ /*
+ * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
+ * when remote helper is used (setting it to an empty string
+ * is not unsetting). We could extend the remote helper
+ * protocol for that, but for now, just force a new connection
+ * without deepen-since. Similar story for deepen-not.
+ */
+ cannot_reuse = transport->cannot_reuse ||
+ deepen_since || deepen_not.nr;
+ if (cannot_reuse) {
+ gsecondary = prepare_transport(transport->remote, 0);
transport = gsecondary;
}
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+ transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
fetch_refs(transport, ref_map);
if (gsecondary) {
@@ -996,7 +1159,7 @@ static int get_remote_group(const char *key, const char *value, void *priv)
size_t wordlen = strcspn(value, " \t\n");
if (wordlen >= 1)
- string_list_append(g->list,
+ string_list_append_nodup(g->list,
xstrndup(value, wordlen));
value += wordlen + (value[wordlen] != '\0');
}
@@ -1013,10 +1176,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, 0))
return 0;
- remote = remote_get(name);
string_list_append(list, remote->name);
}
return 1;
@@ -1092,7 +1254,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
die(_("No remote repository specified. Please, specify either a URL or a\n"
"remote name from which new revisions should be fetched."));
- gtransport = prepare_transport(remote);
+ gtransport = prepare_transport(remote, 1);
if (prune < 0) {
/* no command line request */
@@ -1107,7 +1269,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++;
@@ -1135,7 +1297,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;
@@ -1152,6 +1314,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
+ if (deepen_relative) {
+ if (deepen_relative < 0)
+ die(_("Negative depth in --deepen is not supported"));
+ if (depth)
+ die(_("--deepen and --depth are mutually exclusive"));
+ depth = xstrfmt("%d", deepen_relative);
+ }
if (unshallow) {
if (depth)
die(_("--depth and --unshallow cannot be used together"));
@@ -1164,6 +1333,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
/* no need to be strict, transport_set_option() will validate it again */
if (depth && atoi(depth) < 1)
die(_("depth %s is not a positive number"), depth);
+ if (depth || deepen_since || deepen_not.nr)
+ deepen = 1;
if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
if (recurse_submodules_default) {
@@ -1213,14 +1384,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 e5658c3..6faa3c0 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -41,7 +41,7 @@ struct src_data {
};
struct origin_data {
- unsigned char sha1[20];
+ struct object_id oid;
unsigned is_local_branch:1;
};
@@ -59,8 +59,8 @@ static struct string_list origins = STRING_LIST_INIT_DUP;
struct merge_parents {
int alloc, nr;
struct merge_parent {
- unsigned char given[20];
- unsigned char commit[20];
+ struct object_id given;
+ struct object_id commit;
unsigned char used;
} *item;
};
@@ -70,14 +70,14 @@ struct merge_parents {
* hundreds of heads at a time anyway.
*/
static struct merge_parent *find_merge_parent(struct merge_parents *table,
- unsigned char *given,
- unsigned char *commit)
+ struct object_id *given,
+ struct object_id *commit)
{
int i;
for (i = 0; i < table->nr; i++) {
- if (given && hashcmp(table->item[i].given, given))
+ if (given && oidcmp(&table->item[i].given, given))
continue;
- if (commit && hashcmp(table->item[i].commit, commit))
+ if (commit && oidcmp(&table->item[i].commit, commit))
continue;
return &table->item[i];
}
@@ -85,14 +85,14 @@ static struct merge_parent *find_merge_parent(struct merge_parents *table,
}
static void add_merge_parent(struct merge_parents *table,
- unsigned char *given,
- unsigned char *commit)
+ struct object_id *given,
+ struct object_id *commit)
{
if (table->nr && find_merge_parent(table, given, commit))
return;
ALLOC_GROW(table->item, table->nr + 1, table->alloc);
- hashcpy(table->item[table->nr].given, given);
- hashcpy(table->item[table->nr].commit, commit);
+ oidcpy(&table->item[table->nr].given, given);
+ oidcpy(&table->item[table->nr].commit, commit);
table->item[table->nr].used = 0;
table->nr++;
}
@@ -106,30 +106,30 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
struct src_data *src_data;
struct string_list_item *item;
int pulling_head = 0;
- unsigned char sha1[20];
+ struct object_id oid;
- if (len < 43 || line[40] != '\t')
+ if (len < GIT_SHA1_HEXSZ + 3 || line[GIT_SHA1_HEXSZ] != '\t')
return 1;
- if (starts_with(line + 41, "not-for-merge"))
+ if (starts_with(line + GIT_SHA1_HEXSZ + 1, "not-for-merge"))
return 0;
- if (line[41] != '\t')
+ if (line[GIT_SHA1_HEXSZ + 1] != '\t')
return 2;
- i = get_sha1_hex(line, sha1);
+ i = get_oid_hex(line, &oid);
if (i)
return 3;
- if (!find_merge_parent(merge_parents, sha1, NULL))
+ if (!find_merge_parent(merge_parents, &oid, NULL))
return 0; /* subsumed by other parents */
origin_data = xcalloc(1, sizeof(struct origin_data));
- hashcpy(origin_data->sha1, sha1);
+ oidcpy(&origin_data->oid, &oid);
if (line[len - 1] == '\n')
line[len - 1] = 0;
- line += 42;
+ line += GIT_SHA1_HEXSZ + 2;
/*
* At this point, line points at the beginning of comment e.g.
@@ -272,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,
@@ -314,14 +314,10 @@ static void add_people_info(struct strbuf *out,
struct string_list *authors,
struct string_list *committers)
{
- if (authors->nr)
- qsort(authors->items,
- authors->nr, sizeof(authors->items[0]),
- cmp_string_list_util_as_integral);
- if (committers->nr)
- qsort(committers->items,
- committers->nr, sizeof(committers->items[0]),
- cmp_string_list_util_as_integral);
+ QSORT(authors->items, authors->nr,
+ cmp_string_list_util_as_integral);
+ QSORT(committers->items, committers->nr,
+ cmp_string_list_util_as_integral);
credit_people(out, authors, 'a');
credit_people(out, committers, 'c');
@@ -342,10 +338,10 @@ static void shortlog(const char *name,
struct string_list committers = STRING_LIST_INIT_DUP;
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
struct strbuf sb = STRBUF_INIT;
- const unsigned char *sha1 = origin_data->sha1;
+ const struct object_id *oid = &origin_data->oid;
int limit = opts->shortlog_len;
- branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+ branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ);
if (!branch || branch->type != OBJ_COMMIT)
return;
@@ -395,7 +391,7 @@ static void shortlog(const char *name,
for (i = 0; i < subjects.nr; i++)
if (i >= limit)
- strbuf_addf(out, " ...\n");
+ strbuf_addstr(out, " ...\n");
else
strbuf_addf(out, " %s\n", subjects.items[i].string);
@@ -535,7 +531,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 strbuf *in, struct object_id *head)
{
struct commit_list *parents;
struct commit *head_commit;
@@ -546,31 +542,31 @@ static void find_merge_parents(struct merge_parents *result,
int len;
char *p = in->buf + pos;
char *newline = strchr(p, '\n');
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *parent;
struct object *obj;
len = newline ? newline - p : strlen(p);
pos += len + !!newline;
- if (len < 43 ||
- get_sha1_hex(p, sha1) ||
- p[40] != '\t' ||
- p[41] != '\t')
+ if (len < GIT_SHA1_HEXSZ + 3 ||
+ get_oid_hex(p, &oid) ||
+ p[GIT_SHA1_HEXSZ] != '\t' ||
+ p[GIT_SHA1_HEXSZ + 1] != '\t')
continue; /* skip not-for-merge */
/*
* Do not use get_merge_parent() here; we do not have
* "name" here and we do not want to contaminate its
* util field yet.
*/
- obj = parse_object(sha1);
+ obj = parse_object(oid.hash);
parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
if (!parent)
continue;
commit_list_insert(parent, &parents);
- add_merge_parent(result, obj->oid.hash, parent->object.oid.hash);
+ add_merge_parent(result, &obj->oid, &parent->object.oid);
}
- head_commit = lookup_commit(head);
+ head_commit = lookup_commit(head->hash);
if (head_commit)
commit_list_insert(head_commit, &parents);
parents = reduce_heads(parents);
@@ -578,7 +574,7 @@ static void find_merge_parents(struct merge_parents *result,
while (parents) {
struct commit *cmit = pop_commit(&parents);
for (i = 0; i < result->nr; i++)
- if (!hashcmp(result->item[i].commit, cmit->object.oid.hash))
+ if (!oidcmp(&result->item[i].commit, &cmit->object.oid))
result->item[i].used = 1;
}
@@ -596,7 +592,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct fmt_merge_msg_opts *opts)
{
int i = 0, pos = 0;
- unsigned char head_sha1[20];
+ struct object_id head_oid;
const char *current_branch;
void *current_branch_to_free;
struct merge_parents merge_parents;
@@ -605,13 +601,13 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* get current branch */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
+ resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
if (!current_branch)
die("No current branch");
if (starts_with(current_branch, "refs/heads/"))
current_branch += 11;
- find_merge_parents(&merge_parents, in, head_sha1);
+ find_merge_parents(&merge_parents, in, &head_oid);
/* get a line */
while (pos < in->len) {
@@ -637,7 +633,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct commit *head;
struct rev_info rev;
- head = lookup_commit_or_die(head_sha1, "HEAD");
+ head = lookup_commit_or_die(head_oid.hash, "HEAD");
init_revisions(&rev, NULL);
rev.commit_format = CMIT_FMT_ONELINE;
rev.ignore_merges = 1;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 4e9f6c2..eca365b 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -8,8 +8,8 @@
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>]]"),
+ N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
+ N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
NULL
};
@@ -18,7 +18,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
int i;
const char *format = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
- int maxcount = 0, quote_style = 0;
+ int maxcount = 0, quote_style = 0, icase = 0;
struct ref_array array;
struct ref_filter filter;
@@ -43,6 +43,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
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_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
+ OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END(),
};
@@ -63,6 +65,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (!sorting)
sorting = ref_default_sorting();
+ sorting->ignore_case = icase;
+ filter.ignore_case = icase;
/* for warn_ambiguous_refs */
git_config(git_default_config, NULL);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 55eac75..f76e416 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
@@ -35,11 +36,43 @@ 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
+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 const char *printable_type(struct object *obj)
+{
+ const char *ret;
+
+ if (obj->type == OBJ_NONE) {
+ enum object_type type = sha1_object_info(obj->oid.hash, NULL);
+ if (type > 0)
+ object_as_type(obj, type, 0);
+ }
+
+ ret = typename(obj->type);
+ if (!ret)
+ ret = "unknown";
+
+ return ret;
+}
+
static int fsck_config(const char *var, const char *value, void *cb)
{
if (strcmp(var, "fsck.skiplist") == 0) {
@@ -67,7 +100,7 @@ static void objreport(struct object *obj, const char *msg_type,
const char *err)
{
fprintf(stderr, "%s in %s %s: %s\n",
- msg_type, typename(obj->type), oid_to_hex(&obj->oid), err);
+ msg_type, printable_type(obj), describe_object(obj), err);
}
static int objerror(struct object *obj, const char *err)
@@ -77,7 +110,8 @@ static int objerror(struct object *obj, const char *err)
return -1;
}
-static int fsck_error_func(struct object *obj, int type, const char *message)
+static int fsck_error_func(struct fsck_options *o,
+ struct object *obj, int type, const char *message)
{
objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
return (type == FSCK_WARN) ? 0 : 1;
@@ -97,7 +131,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (!obj) {
/* ... these references to parent->fld are safe here */
printf("broken link from %7s %s\n",
- typename(parent->type), oid_to_hex(&parent->oid));
+ printable_type(parent), describe_object(parent));
printf("broken link from %7s %s\n",
(type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
errors_found |= ERROR_REACHABLE;
@@ -114,9 +148,9 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (!(obj->flags & HAS_OBJ)) {
if (parent && !has_object_file(&obj->oid)) {
printf("broken link from %7s %s\n",
- typename(parent->type), oid_to_hex(&parent->oid));
+ printable_type(parent), describe_object(parent));
printf(" to %7s %s\n",
- typename(obj->type), oid_to_hex(&obj->oid));
+ printable_type(obj), describe_object(obj));
errors_found |= ERROR_REACHABLE;
}
return 1;
@@ -188,9 +222,8 @@ static void check_reachable_object(struct object *obj)
if (!(obj->flags & HAS_OBJ)) {
if (has_sha1_pack(obj->oid.hash))
return; /* it is in pack - forget about it */
- if (connectivity_only && has_object_file(&obj->oid))
- return;
- printf("missing %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
+ printf("missing %s %s\n", printable_type(obj),
+ describe_object(obj));
errors_found |= ERROR_REACHABLE;
return;
}
@@ -207,7 +240,7 @@ static void check_unreachable_object(struct object *obj)
* to complain about it being unreachable (since it does
* not exist).
*/
- if (!obj->parsed)
+ if (!(obj->flags & HAS_OBJ))
return;
/*
@@ -215,7 +248,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), oid_to_hex(&obj->oid));
+ printf("unreachable %s %s\n", printable_type(obj),
+ describe_object(obj));
return;
}
@@ -233,12 +267,12 @@ static void check_unreachable_object(struct object *obj)
*/
if (!obj->used) {
if (show_dangling)
- printf("dangling %s %s\n", typename(obj->type),
- oid_to_hex(&obj->oid));
+ printf("dangling %s %s\n", printable_type(obj),
+ describe_object(obj));
if (write_lost_and_found) {
char *filename = git_pathdup("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
- oid_to_hex(&obj->oid));
+ describe_object(obj));
FILE *f;
if (safe_create_leading_directories_const(filename)) {
@@ -249,10 +283,10 @@ static void check_unreachable_object(struct object *obj)
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->oid.hash, NULL, 1))
+ if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
die_errno("Could not write '%s'", filename);
} else
- fprintf(f, "%s\n", oid_to_hex(&obj->oid));
+ fprintf(f, "%s\n", describe_object(obj));
if (fclose(f))
die_errno("Could not finish '%s'",
filename);
@@ -271,7 +305,7 @@ static void check_unreachable_object(struct object *obj)
static void check_object(struct object *obj)
{
if (verbose)
- fprintf(stderr, "Checking %s\n", oid_to_hex(&obj->oid));
+ fprintf(stderr, "Checking %s\n", describe_object(obj));
if (obj->flags & REACHABLE)
check_reachable_object(obj);
@@ -307,7 +341,7 @@ static int fsck_obj(struct object *obj)
if (verbose)
fprintf(stderr, "Checking %s %s\n",
- typename(obj->type), oid_to_hex(&obj->oid));
+ printable_type(obj), describe_object(obj));
if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, "broken links");
@@ -326,36 +360,30 @@ static int fsck_obj(struct object *obj)
free_commit_buffer(commit);
if (!commit->parents && show_root)
- printf("root %s\n", oid_to_hex(&commit->object.oid));
+ 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), oid_to_hex(&tag->tagged->oid));
- printf(" (%s) in %s\n", tag->tag, oid_to_hex(&tag->object.oid));
+ printf("tagged %s %s", printable_type(tag->tagged),
+ describe_object(tag->tagged));
+ printf(" (%s) in %s\n", tag->tag,
+ describe_object(&tag->object));
}
}
return 0;
}
-static int fsck_sha1(const unsigned char *sha1)
-{
- struct object *obj = parse_object(sha1);
- if (!obj) {
- errors_found |= ERROR_OBJECT;
- return error("%s: object corrupt or missing",
- sha1_to_hex(sha1));
- }
- obj->flags |= HAS_OBJ;
- return fsck_obj(obj);
-}
-
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) {
@@ -368,23 +396,28 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
static int default_refs;
-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
+static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
+ unsigned long timestamp)
{
struct object *obj;
- if (!is_null_sha1(sha1)) {
- obj = lookup_object(sha1);
- if (obj) {
+ if (!is_null_oid(oid)) {
+ obj = lookup_object(oid->hash);
+ if (obj && (obj->flags & HAS_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 {
- error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1));
+ error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
errors_found |= ERROR_REACHABLE;
}
}
}
-static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{
@@ -392,10 +425,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
if (verbose)
fprintf(stderr, "Checking reflog %s->%s\n",
- sha1_to_hex(osha1), sha1_to_hex(nsha1));
+ oid_to_hex(ooid), oid_to_hex(noid));
- fsck_handle_reflog_sha1(refname, osha1);
- fsck_handle_reflog_sha1(refname, nsha1);
+ fsck_handle_reflog_oid(refname, ooid, 0);
+ fsck_handle_reflog_oid(refname, noid, timestamp);
return 0;
}
@@ -424,6 +457,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
}
default_refs++;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(refname));
mark_object_reachable(obj);
return 0;
@@ -455,9 +491,41 @@ static void get_default_heads(void)
}
}
-static int fsck_loose(const unsigned char *sha1, const char *path, void *data)
+static struct object *parse_loose_object(const struct object_id *oid,
+ const char *path)
{
- if (fsck_sha1(sha1))
+ struct object *obj;
+ void *contents;
+ enum object_type type;
+ unsigned long size;
+ int eaten;
+
+ if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0)
+ return NULL;
+
+ if (!contents && type != OBJ_BLOB)
+ die("BUG: read_loose_object streamed a non-blob");
+
+ obj = parse_object_buffer(oid->hash, type, size, contents, &eaten);
+
+ if (!eaten)
+ free(contents);
+ return obj;
+}
+
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
+{
+ struct object *obj = parse_loose_object(oid, path);
+
+ if (!obj) {
+ errors_found |= ERROR_OBJECT;
+ error("%s: object corrupt or missing: %s",
+ oid_to_hex(oid), path);
+ return 0; /* keep checking other objects */
+ }
+
+ obj->flags = HAS_OBJ;
+ if (fsck_obj(obj))
errors_found |= ERROR_OBJECT;
return 0;
}
@@ -493,13 +561,12 @@ static void fsck_object_dir(const char *path)
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_oid.hash, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
if (!head_points_at) {
errors_found |= ERROR_REFS;
return error("Invalid HEAD");
@@ -540,6 +607,9 @@ static int fsck_cache_tree(struct cache_tree *it)
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");
@@ -549,6 +619,29 @@ static int fsck_cache_tree(struct cache_tree *it)
return err;
}
+static void mark_object_for_connectivity(const struct object_id *oid)
+{
+ struct object *obj = lookup_unknown_object(oid->hash);
+ obj->flags |= HAS_OBJ;
+}
+
+static int mark_loose_for_connectivity(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ mark_object_for_connectivity(oid);
+ return 0;
+}
+
+static int mark_packed_for_connectivity(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *data)
+{
+ mark_object_for_connectivity(oid);
+ return 0;
+}
+
static char const * const fsck_usage[] = {
N_("git fsck [<options>] [<object>...]"),
NULL
@@ -568,6 +661,7 @@ static struct option fsck_opts[] = {
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(),
};
@@ -597,47 +691,48 @@ 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();
- if (!connectivity_only) {
+ if (connectivity_only) {
+ for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
+ for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
+ } else {
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);
- }
- }
+ for (alt = alt_odb_list; alt; alt = alt->next)
+ fsck_object_dir(alt->path);
+
+ if (check_full) {
+ struct packed_git *p;
+ uint32_t total = 0, count = 0;
+ struct progress *progress = NULL;
- if (check_full) {
- struct packed_git *p;
- uint32_t total = 0, count = 0;
- struct progress *progress = NULL;
+ prepare_packed_git();
- prepare_packed_git();
+ if (show_progress) {
+ for (p = packed_git; p; p = p->next) {
+ if (open_pack_index(p))
+ continue;
+ total += p->num_objects;
+ }
- if (show_progress) {
+ progress = start_progress(_("Checking objects"), total);
+ }
for (p = packed_git; p; p = p->next) {
- if (open_pack_index(p))
- continue;
- total += p->num_objects;
+ /* verify gives error messages itself */
+ if (verify_pack(p, fsck_obj_buffer,
+ progress, count))
+ errors_found |= ERROR_PACK;
+ count += p->num_objects;
}
-
- progress = start_progress(_("Checking objects"), total);
+ stop_progress(&progress);
}
- for (p = packed_git; p; p = p->next) {
- /* verify gives error messages itself */
- if (verify_pack(p, fsck_obj_buffer,
- progress, count))
- errors_found |= ERROR_PACK;
- count += p->num_objects;
- }
- stop_progress(&progress);
}
heads = 0;
@@ -647,16 +742,22 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (!get_sha1(arg, sha1)) {
struct object *obj = lookup_object(sha1);
- /* Error is printed by lookup_object(). */
- if (!obj)
+ if (!obj || !(obj->flags & HAS_OBJ)) {
+ error("%s: object missing", sha1_to_hex(sha1));
+ errors_found |= ERROR_OBJECT;
continue;
+ }
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(arg));
mark_object_reachable(obj);
heads++;
continue;
}
error("invalid parameter: expected sha1, got '%s'", arg);
+ errors_found |= ERROR_OBJECT;
}
/*
@@ -664,7 +765,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
* default ones from .git/refs. We also consider the index file
* in this case (ie this implies --cache).
*/
- if (!heads) {
+ if (!argc) {
get_default_heads();
keep_cache_objects = 1;
}
@@ -679,11 +780,15 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
mode = active_cache[i]->ce_mode;
if (S_ISGITLINK(mode))
continue;
- blob = lookup_blob(active_cache[i]->sha1);
+ blob = lookup_blob(active_cache[i]->oid.hash);
if (!blob)
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 c583aad..c2c61a5 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -28,11 +28,13 @@ static const char * const builtin_gc_usage[] = {
static int pack_refs = 1;
static int prune_reflogs = 1;
-static int aggressive_depth = 250;
+static int aggressive_depth = 50;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static int detach_auto = 1;
+static unsigned long gc_log_expire_time;
+static const char *gc_log_expire = "1.day.ago";
static const char *prune_expire = "2.weeks.ago";
static const char *prune_worktrees_expire = "3.months.ago";
@@ -62,24 +64,31 @@ static void report_pack_garbage(unsigned seen_bits, const char *path)
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)
+ if (fstat(get_lock_file_fd(&log_lock), &st)) {
+ /*
+ * Perhaps there was an i/o error or another
+ * unlikely situation. Try to make a note of
+ * this in gc.log along with any existing
+ * messages.
+ */
+ int saved_errno = errno;
+ fprintf(stderr, _("Failed to fstat %s: %s"),
+ get_tempfile_path(&log_lock.tempfile),
+ strerror(saved_errno));
+ fflush(stderr);
commit_lock_file(&log_lock);
- else
+ errno = saved_errno;
+ } else if (st.st_size) {
+ /* There was some error recorded in the lock file */
+ commit_lock_file(&log_lock);
+ } else {
+ /* No error, clean up any old gc.log */
+ unlink(git_path("gc.log"));
rollback_lock_file(&log_lock);
+ }
}
static void process_log_file_at_exit(void)
@@ -111,8 +120,10 @@ 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);
- git_config_date_string("gc.pruneexpire", &prune_expire);
- git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
+ git_config_get_expiry("gc.pruneexpire", &prune_expire);
+ git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
+ git_config_get_expiry("gc.logexpiry", &gc_log_expire);
+
git_config(git_default_config, NULL);
}
@@ -177,7 +188,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)
@@ -191,6 +202,11 @@ static void add_repack_all_option(void)
}
}
+static void add_repack_incremental_option(void)
+{
+ argv_array_push(&repack, "--no-write-bitmap-index");
+}
+
static int need_to_gc(void)
{
/*
@@ -208,7 +224,9 @@ static int need_to_gc(void)
*/
if (too_many_packs())
add_repack_all_option();
- else if (!too_many_loose_objects())
+ else if (too_many_loose_objects())
+ add_repack_incremental_option();
+ else
return 0;
if (run_hook_le(NULL, "pre-auto-gc", NULL))
@@ -283,19 +301,34 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
static int report_last_gc_error(void)
{
struct strbuf sb = STRBUF_INIT;
- int ret;
+ int ret = 0;
+ struct stat st;
+ char *gc_log_path = git_pathdup("gc.log");
+
+ if (stat(gc_log_path, &st)) {
+ if (errno == ENOENT)
+ goto done;
- ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
+ ret = error_errno(_("Can't stat %s"), gc_log_path);
+ goto done;
+ }
+
+ if (st.st_mtime < gc_log_expire_time)
+ goto done;
+
+ ret = strbuf_read_file(&sb, gc_log_path, 0);
if (ret > 0)
- return error(_("The last gc run reported the following. "
+ ret = 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);
+ gc_log_path, sb.buf);
strbuf_release(&sb);
- return 0;
+done:
+ free(gc_log_path);
+ return ret;
}
static int gc_before_repack(void)
@@ -342,7 +375,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
argv_array_pushl(&rerere, "rerere", "gc", NULL);
+ /* default expiry time, overwritten in gc_config */
gc_config();
+ if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
+ die(_("Failed to parse gc.logexpiry value %s"), gc_log_expire);
if (pack_refs < 0)
pack_refs = !is_bare_repository();
@@ -441,5 +477,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
+ if (!daemonized)
+ unlink(git_path("gc.log"));
+
return 0;
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 4229cae..65070c5 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -18,17 +18,27 @@
#include "quote.h"
#include "dir.h"
#include "pathspec.h"
+#include "submodule.h"
+#include "submodule-config.h"
static char const * const grep_usage[] = {
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
NULL
};
-static int use_threads = 1;
+static const char *super_prefix;
+static int recurse_submodules;
+static struct argv_array submodule_options = ARGV_ARRAY_INIT;
+static const char *parent_basename;
+
+static int grep_submodule_launch(struct grep_opt *opt,
+ const struct grep_source *gs);
+
+#define GREP_NUM_THREADS_DEFAULT 8
+static int num_threads;
#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 +73,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);
}
@@ -174,7 +184,10 @@ static void *run(void *arg)
break;
opt->output_priv = w;
- hit |= grep_source(opt, &w->source);
+ if (w->source.type == GREP_SOURCE_SUBMODULE)
+ hit |= grep_submodule_launch(opt, &w->source);
+ else
+ hit |= grep_source(opt, &w->source);
grep_source_clear_data(&w->source);
work_done(w);
}
@@ -206,7 +219,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 +252,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,35 +283,51 @@ 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;
}
-static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
{
void *data;
grep_read_lock();
- data = read_sha1_file(sha1, type, size);
+ data = read_sha1_file(oid->hash, type, size);
grep_read_unlock();
return data;
}
-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
+static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
const char *filename, int tree_name_len,
const char *path)
{
struct strbuf pathbuf = STRBUF_INIT;
- if (opt->relative && opt->prefix_length) {
- quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
- strbuf_insert(&pathbuf, 0, filename, tree_name_len);
+ if (super_prefix) {
+ strbuf_add(&pathbuf, filename, tree_name_len);
+ strbuf_addstr(&pathbuf, super_prefix);
+ strbuf_addstr(&pathbuf, filename + tree_name_len);
} else {
strbuf_addstr(&pathbuf, filename);
}
+ if (opt->relative && opt->prefix_length) {
+ char *name = strbuf_detach(&pathbuf, NULL);
+ quote_path_relative(name + tree_name_len, opt->prefix, &pathbuf);
+ strbuf_insert(&pathbuf, 0, name, tree_name_len);
+ free(name);
+ }
+
#ifndef NO_PTHREADS
- if (use_threads) {
- add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
+ if (num_threads) {
+ add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
strbuf_release(&pathbuf);
return 0;
} else
@@ -304,7 +336,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
struct grep_source gs;
int hit;
- grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
+ grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
strbuf_release(&pathbuf);
hit = grep_source(opt, &gs);
@@ -317,13 +349,18 @@ static int grep_file(struct grep_opt *opt, const char *filename)
{
struct strbuf buf = STRBUF_INIT;
- if (opt->relative && opt->prefix_length)
- quote_path_relative(filename, opt->prefix, &buf);
- else
- strbuf_addstr(&buf, filename);
+ if (super_prefix)
+ strbuf_addstr(&buf, super_prefix);
+ strbuf_addstr(&buf, filename);
+
+ if (opt->relative && opt->prefix_length) {
+ char *name = strbuf_detach(&buf, NULL);
+ quote_path_relative(name, opt->prefix, &buf);
+ free(name);
+ }
#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,43 +391,323 @@ 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)
+static void compile_submodule_options(const struct grep_opt *opt,
+ const char **argv,
+ int cached, int untracked,
+ int opt_exclude, int use_index,
+ int pattern_type_arg)
+{
+ struct grep_pat *pattern;
+
+ if (recurse_submodules)
+ argv_array_push(&submodule_options, "--recurse-submodules");
+
+ if (cached)
+ argv_array_push(&submodule_options, "--cached");
+ if (!use_index)
+ argv_array_push(&submodule_options, "--no-index");
+ if (untracked)
+ argv_array_push(&submodule_options, "--untracked");
+ if (opt_exclude > 0)
+ argv_array_push(&submodule_options, "--exclude-standard");
+
+ if (opt->invert)
+ argv_array_push(&submodule_options, "-v");
+ if (opt->ignore_case)
+ argv_array_push(&submodule_options, "-i");
+ if (opt->word_regexp)
+ argv_array_push(&submodule_options, "-w");
+ switch (opt->binary) {
+ case GREP_BINARY_NOMATCH:
+ argv_array_push(&submodule_options, "-I");
+ break;
+ case GREP_BINARY_TEXT:
+ argv_array_push(&submodule_options, "-a");
+ break;
+ default:
+ break;
+ }
+ if (opt->allow_textconv)
+ argv_array_push(&submodule_options, "--textconv");
+ if (opt->max_depth != -1)
+ argv_array_pushf(&submodule_options, "--max-depth=%d",
+ opt->max_depth);
+ if (opt->linenum)
+ argv_array_push(&submodule_options, "-n");
+ if (!opt->pathname)
+ argv_array_push(&submodule_options, "-h");
+ if (!opt->relative)
+ argv_array_push(&submodule_options, "--full-name");
+ if (opt->name_only)
+ argv_array_push(&submodule_options, "-l");
+ if (opt->unmatch_name_only)
+ argv_array_push(&submodule_options, "-L");
+ if (opt->null_following_name)
+ argv_array_push(&submodule_options, "-z");
+ if (opt->count)
+ argv_array_push(&submodule_options, "-c");
+ if (opt->file_break)
+ argv_array_push(&submodule_options, "--break");
+ if (opt->heading)
+ argv_array_push(&submodule_options, "--heading");
+ if (opt->pre_context)
+ argv_array_pushf(&submodule_options, "--before-context=%d",
+ opt->pre_context);
+ if (opt->post_context)
+ argv_array_pushf(&submodule_options, "--after-context=%d",
+ opt->post_context);
+ if (opt->funcname)
+ argv_array_push(&submodule_options, "-p");
+ if (opt->funcbody)
+ argv_array_push(&submodule_options, "-W");
+ if (opt->all_match)
+ argv_array_push(&submodule_options, "--all-match");
+ if (opt->debug)
+ argv_array_push(&submodule_options, "--debug");
+ if (opt->status_only)
+ argv_array_push(&submodule_options, "-q");
+
+ switch (pattern_type_arg) {
+ case GREP_PATTERN_TYPE_BRE:
+ argv_array_push(&submodule_options, "-G");
+ break;
+ case GREP_PATTERN_TYPE_ERE:
+ argv_array_push(&submodule_options, "-E");
+ break;
+ case GREP_PATTERN_TYPE_FIXED:
+ argv_array_push(&submodule_options, "-F");
+ break;
+ case GREP_PATTERN_TYPE_PCRE:
+ argv_array_push(&submodule_options, "-P");
+ break;
+ case GREP_PATTERN_TYPE_UNSPECIFIED:
+ break;
+ }
+
+ for (pattern = opt->pattern_list; pattern != NULL;
+ pattern = pattern->next) {
+ switch (pattern->token) {
+ case GREP_PATTERN:
+ argv_array_pushf(&submodule_options, "-e%s",
+ pattern->pattern);
+ break;
+ case GREP_AND:
+ case GREP_OPEN_PAREN:
+ case GREP_CLOSE_PAREN:
+ case GREP_NOT:
+ case GREP_OR:
+ argv_array_push(&submodule_options, pattern->pattern);
+ break;
+ /* BODY and HEAD are not used by git-grep */
+ case GREP_PATTERN_BODY:
+ case GREP_PATTERN_HEAD:
+ break;
+ }
+ }
+
+ /*
+ * Limit number of threads for child process to use.
+ * This is to prevent potential fork-bomb behavior of git-grep as each
+ * submodule process has its own thread pool.
+ */
+ argv_array_pushf(&submodule_options, "--threads=%d",
+ (num_threads + 1) / 2);
+
+ /* Add Pathspecs */
+ argv_array_push(&submodule_options, "--");
+ for (; *argv; argv++)
+ argv_array_push(&submodule_options, *argv);
+}
+
+/*
+ * Launch child process to grep contents of a submodule
+ */
+static int grep_submodule_launch(struct grep_opt *opt,
+ const struct grep_source *gs)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ int status, i;
+ const char *end_of_base;
+ const char *name;
+ struct strbuf child_output = STRBUF_INIT;
+
+ end_of_base = strchr(gs->name, ':');
+ if (gs->identifier && end_of_base)
+ name = end_of_base + 1;
+ else
+ name = gs->name;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
+
+ if (opt->relative && opt->prefix_length)
+ argv_array_pushf(&cp.env_array, "%s=%s",
+ GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
+ opt->prefix);
+
+ /* Add super prefix */
+ argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+ super_prefix ? super_prefix : "",
+ name);
+ argv_array_push(&cp.args, "grep");
+
+ /*
+ * Add basename of parent project
+ * When performing grep on a tree object the filename is prefixed
+ * with the object's name: 'tree-name:filename'. In order to
+ * provide uniformity of output we want to pass the name of the
+ * parent project's object name to the submodule so the submodule can
+ * prefix its output with the parent's name and not its own SHA1.
+ */
+ if (gs->identifier && end_of_base)
+ argv_array_pushf(&cp.args, "--parent-basename=%.*s",
+ (int) (end_of_base - gs->name),
+ gs->name);
+
+ /* Add options */
+ for (i = 0; i < submodule_options.argc; i++) {
+ /*
+ * If there is a tree identifier for the submodule, add the
+ * rev after adding the submodule options but before the
+ * pathspecs. To do this we listen for the '--' and insert the
+ * sha1 before pushing the '--' onto the child process argv
+ * array.
+ */
+ if (gs->identifier &&
+ !strcmp("--", submodule_options.argv[i])) {
+ argv_array_push(&cp.args, sha1_to_hex(gs->identifier));
+ }
+
+ argv_array_push(&cp.args, submodule_options.argv[i]);
+ }
+
+ cp.git_cmd = 1;
+ cp.dir = gs->path;
+
+ /*
+ * Capture output to output buffer and check the return code from the
+ * child process. A '0' indicates a hit, a '1' indicates no hit and
+ * anything else is an error.
+ */
+ status = capture_command(&cp, &child_output, 0);
+ if (status && (status != 1)) {
+ /* flush the buffer */
+ write_or_die(1, child_output.buf, child_output.len);
+ die("process for submodule '%s' failed with exit code: %d",
+ gs->name, status);
+ }
+
+ opt->output(opt, child_output.buf, child_output.len);
+ strbuf_release(&child_output);
+ /* invert the return code to make a hit equal to 1 */
+ return !status;
+}
+
+/*
+ * Prep grep structures for a submodule grep
+ * sha1: the sha1 of the submodule or NULL if using the working tree
+ * filename: name of the submodule including tree name of parent
+ * path: location of the submodule
+ */
+static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
+ const char *filename, const char *path)
+{
+ if (!is_submodule_initialized(path))
+ return 0;
+ if (!is_submodule_populated_gently(path, NULL)) {
+ /*
+ * If searching history, check for the presense of the
+ * submodule's gitdir before skipping the submodule.
+ */
+ if (sha1) {
+ const struct submodule *sub =
+ submodule_from_path(null_sha1, path);
+ if (sub)
+ path = git_path("modules/%s", sub->name);
+
+ if (!(is_directory(path) && is_git_directory(path)))
+ return 0;
+ } else {
+ return 0;
+ }
+ }
+
+#ifndef NO_PTHREADS
+ if (num_threads) {
+ add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, sha1);
+ return 0;
+ } else
+#endif
+ {
+ struct grep_source gs;
+ int hit;
+
+ grep_source_init(&gs, GREP_SOURCE_SUBMODULE,
+ filename, path, sha1);
+ hit = grep_submodule_launch(opt, &gs);
+
+ grep_source_clear(&gs);
+ return hit;
+ }
+}
+
+static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
+ int cached)
{
int hit = 0;
int nr;
+ struct strbuf name = STRBUF_INIT;
+ int name_base_len = 0;
+ if (super_prefix) {
+ name_base_len = strlen(super_prefix);
+ strbuf_addstr(&name, super_prefix);
+ }
+
read_cache();
for (nr = 0; nr < active_nr; nr++) {
const struct cache_entry *ce = active_cache[nr];
- if (!S_ISREG(ce->ce_mode))
- continue;
- if (!ce_path_match(ce, pathspec, NULL))
+ strbuf_setlen(&name, name_base_len);
+ strbuf_addstr(&name, ce->name);
+
+ if (S_ISREG(ce->ce_mode) &&
+ match_pathspec(pathspec, name.buf, name.len, 0, NULL,
+ S_ISDIR(ce->ce_mode) ||
+ S_ISGITLINK(ce->ce_mode))) {
+ /*
+ * If CE_VALID is on, we assume worktree file and its
+ * cache entry are identical, even if worktree file has
+ * been modified, so use cache version instead
+ */
+ if (cached || (ce->ce_flags & CE_VALID) ||
+ ce_skip_worktree(ce)) {
+ if (ce_stage(ce) || ce_intent_to_add(ce))
+ continue;
+ hit |= grep_oid(opt, &ce->oid, ce->name,
+ 0, ce->name);
+ } else {
+ hit |= grep_file(opt, ce->name);
+ }
+ } else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+ submodule_path_match(pathspec, name.buf, NULL)) {
+ hit |= grep_submodule(opt, NULL, ce->name, ce->name);
+ } else {
continue;
- /*
- * If CE_VALID is on, we assume worktree file and its cache entry
- * are identical, even if worktree file has been modified, so use
- * cache version instead
- */
- if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
- if (ce_stage(ce))
- continue;
- hit |= grep_sha1(opt, ce->sha1, ce->name, 0, ce->name);
}
- else
- hit |= grep_file(opt, ce->name);
+
if (ce_stage(ce)) {
do {
nr++;
@@ -401,6 +718,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
if (hit && opt->status_only)
break;
}
+
+ strbuf_release(&name);
return hit;
}
@@ -412,12 +731,22 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
enum interesting match = entry_not_interesting;
struct name_entry entry;
int old_baselen = base->len;
+ struct strbuf name = STRBUF_INIT;
+ int name_base_len = 0;
+ if (super_prefix) {
+ strbuf_addstr(&name, super_prefix);
+ name_base_len = name.len;
+ }
while (tree_entry(tree, &entry)) {
int te_len = tree_entry_len(&entry);
if (match != all_entries_interesting) {
- match = tree_entry_interesting(&entry, base, tn_len, pathspec);
+ strbuf_addstr(&name, base->buf + tn_len);
+ match = tree_entry_interesting(&entry, &name,
+ 0, pathspec);
+ strbuf_setlen(&name, name_base_len);
+
if (match == all_entries_not_interesting)
break;
if (match == entry_not_interesting)
@@ -427,31 +756,36 @@ 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_oid(opt, entry.oid, base->buf, tn_len,
check_attr ? base->buf + tn_len : NULL);
- }
- else if (S_ISDIR(entry.mode)) {
+ } else if (S_ISDIR(entry.mode)) {
enum object_type type;
struct tree_desc sub;
void *data;
unsigned long size;
- data = lock_and_read_sha1_file(entry.sha1, &type, &size);
+ data = lock_and_read_oid_file(entry.oid, &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);
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
check_attr);
free(data);
+ } else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
+ hit |= grep_submodule(opt, entry.oid->hash, base->buf,
+ base->buf + tn_len);
}
+
strbuf_setlen(base, old_baselen);
if (hit && opt->status_only)
break;
}
+
+ strbuf_release(&name);
return hit;
}
@@ -459,7 +793,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->oid.hash, name, 0, path);
+ return grep_oid(opt, &obj->oid, name, 0, path);
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
struct tree_desc tree;
void *data;
@@ -475,6 +809,10 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
if (!data)
die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
+ /* Use parent's name as base when recursing submodules */
+ if (recurse_submodules && parent_basename)
+ name = parent_basename;
+
len = name ? strlen(name) : 0;
strbuf_init(&base, PATH_MAX + len + 1);
if (len) {
@@ -501,6 +839,12 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
for (i = 0; i < nr; i++) {
struct object *real_obj;
real_obj = deref_tag(list->objects[i].item, NULL, 0);
+
+ /* load the gitmodules file for this rev */
+ if (recurse_submodules) {
+ submodule_free();
+ gitmodules_config_sha1(real_obj->oid.hash);
+ }
if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
hit = 1;
if (opt->status_only)
@@ -511,12 +855,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 +908,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;
@@ -627,6 +973,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int dummy;
int use_index = 1;
int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
+ int allow_revs;
struct option options[] = {
OPT_BOOL(0, "cached", &cached,
@@ -637,6 +984,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
N_("search in both tracked and untracked files")),
OPT_SET_INT(0, "exclude-standard", &opt_exclude,
N_("ignore files specified via '.gitignore'"), 1),
+ OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+ N_("recursively search in each submodule")),
+ OPT_STRING(0, "parent-basename", &parent_basename,
+ N_("basename"),
+ N_("prepend parent project's basename to output")),
OPT_GROUP(""),
OPT_BOOL('v', "invert-match", &opt.invert,
N_("show non-matching lines")),
@@ -697,6 +1049,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,
@@ -739,6 +1093,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
init_grep_defaults();
git_config(grep_cmd_config, NULL);
grep_init(&opt, prefix);
+ super_prefix = get_super_prefix();
/*
* If there is no -- then the paths must exist in the working
@@ -755,9 +1110,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
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
@@ -786,7 +1147,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)
@@ -796,35 +1156,82 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
compile_grep_patterns(&opt);
- /* Check revs and then paths */
+ /*
+ * We have to find "--" in a separate pass, because its presence
+ * influences how we will parse arguments that come before it.
+ */
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ seen_dashdash = 1;
+ break;
+ }
+ }
+
+ /*
+ * Resolve any rev arguments. If we have a dashdash, then everything up
+ * to it must resolve as a rev. If not, then we stop at the first
+ * non-rev and assume everything else is a path.
+ */
+ allow_revs = use_index && !untracked;
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- unsigned char sha1[20];
+ struct object_id oid;
struct object_context oc;
- /* Is it a rev? */
- if (!get_sha1_with_context(arg, 0, sha1, &oc)) {
- struct object *object = parse_object_or_die(sha1, arg);
- if (!seen_dashdash)
- verify_non_filename(prefix, arg);
- add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
- continue;
- }
+ struct object *object;
+
if (!strcmp(arg, "--")) {
i++;
- seen_dashdash = 1;
+ break;
}
- break;
+
+ if (!allow_revs) {
+ if (seen_dashdash)
+ die(_("--no-index or --untracked cannot be used with revs"));
+ break;
+ }
+
+ if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
+ if (seen_dashdash)
+ die(_("unable to resolve revision: %s"), arg);
+ break;
+ }
+
+ object = parse_object_or_die(oid.hash, arg);
+ if (!seen_dashdash)
+ verify_non_filename(prefix, arg);
+ add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
+ }
+
+ /*
+ * Anything left over is presumed to be a path. But in the non-dashdash
+ * "do what I mean" case, we verify and complain when that isn't true.
+ */
+ if (!seen_dashdash) {
+ int j;
+ for (j = i; j < argc; j++)
+ verify_filename(prefix, argv[j], j == i && allow_revs);
}
+ parse_pathspec(&pathspec, 0,
+ PATHSPEC_PREFER_CWD |
+ (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
+ prefix, argv + i);
+ pathspec.max_depth = opt.max_depth;
+ pathspec.recursive = 1;
+
#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))
@@ -833,20 +1240,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
#endif
- /* The rest are paths */
- if (!seen_dashdash) {
- int j;
- for (j = i; j < argc; j++)
- verify_filename(prefix, argv[j], j == i);
+ if (recurse_submodules) {
+ gitmodules_config();
+ compile_submodule_options(&opt, argv + i, cached, untracked,
+ opt_exclude, use_index,
+ pattern_type_arg);
}
- parse_pathspec(&pathspec, 0,
- PATHSPEC_PREFER_CWD |
- (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
- prefix, argv + i);
- pathspec.max_depth = opt.max_depth;
- pathspec.recursive = 1;
-
if (show_in_pager && (cached || list.nr))
die(_("--open-files-in-pager only works on the worktree"));
@@ -870,6 +1270,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
}
}
+ if (recurse_submodules && (!use_index || untracked))
+ die(_("option not supported with --recurse-submodules."));
+
if (!show_in_pager && !opt.status_only)
setup_pager();
@@ -878,9 +1281,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!use_index || untracked) {
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) {
@@ -894,7 +1295,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 43b098b..bbeaf20 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -58,20 +58,21 @@ 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)
@@ -86,6 +87,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
int stdin_paths = 0;
int no_filters = 0;
int literally = 0;
+ int nongit = 0;
unsigned flags = HASH_FORMAT_CHECK;
const char *vpath = NULL;
const struct option hash_object_options[] = {
@@ -100,18 +102,18 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
OPT_END()
};
int i;
- int prefix_length = -1;
const char *errstr = NULL;
argc = parse_options(argc, argv, NULL, hash_object_options,
hash_object_usage, 0);
- if (flags & HASH_WRITE_OBJECT) {
+ if (flags & HASH_WRITE_OBJECT)
prefix = setup_git_directory();
- prefix_length = prefix ? strlen(prefix) : 0;
- if (vpath && prefix)
- vpath = prefix_filename(prefix, prefix_length, vpath);
- }
+ else
+ prefix = setup_git_directory_gently(&nongit);
+
+ if (vpath && prefix)
+ vpath = xstrdup(prefix_filename(prefix, vpath));
git_config(git_default_config, NULL);
@@ -140,11 +142,13 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
for (i = 0 ; i < argc; i++) {
const char *arg = argv[i];
+ char *to_free = NULL;
- if (0 <= prefix_length)
- arg = prefix_filename(prefix, prefix_length, arg);
+ if (prefix)
+ arg = to_free = prefix_filename(prefix, arg);
hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
flags, literally);
+ free(to_free);
}
if (stdin_paths)
diff --git a/builtin/help.c b/builtin/help.c
index 1cd0c1e..49f7a07 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -37,8 +37,10 @@ static int show_all = 0;
static int show_guides = 0;
static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
+static int exclude_guides;
static struct option builtin_help_options[] = {
OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
+ OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
@@ -127,7 +129,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);
}
}
@@ -148,7 +150,7 @@ static void exec_man_konqueror(const char *path, const char *page)
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);
}
}
@@ -157,7 +159,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)
@@ -165,18 +167,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));
- memcpy((*p)->name, name, len); /* NUL-terminated by xcalloc */
+ FLEX_ALLOC_STR(*p, name, name);
}
static int supported_man_viewer(const char *name, size_t len)
@@ -190,9 +190,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);
-
- memcpy(new->name, name, len); /* NUL-terminated by xcalloc */
+ 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;
@@ -382,17 +381,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)
{
@@ -436,10 +428,29 @@ static void list_common_guides_help(void)
putchar('\n');
}
+static const char *check_git_cmd(const char* cmd)
+{
+ char *alias;
+
+ if (is_git_command(cmd))
+ return cmd;
+
+ alias = alias_lookup(cmd);
+ if (alias) {
+ printf_ln(_("`git %s' is aliased to `%s'"), cmd, alias);
+ free(alias);
+ exit(0);
+ }
+
+ if (exclude_guides)
+ return help_unknown_cmd(cmd);
+
+ return cmd;
+}
+
int cmd_help(int argc, const char **argv, const char *prefix)
{
int nongit;
- char *alias;
enum help_format parsed_help_format;
argc = parse_options(argc, argv, prefix, builtin_help_options,
@@ -479,12 +490,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
if (help_format == HELP_FORMAT_NONE)
help_format = parse_help_format(DEFAULT_HELP_FORMAT);
- alias = alias_lookup(argv[0]);
- if (alias && !is_git_command(argv[0])) {
- printf_ln(_("`git %s' is aliased to `%s'"), argv[0], alias);
- free(alias);
- return 0;
- }
+ argv[0] = check_git_cmd(argv[0]);
switch (help_format) {
case HELP_FORMAT_NONE:
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 6a01509..88d205f 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -77,6 +77,7 @@ 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;
@@ -86,6 +87,7 @@ static struct progress *progress;
static unsigned char input_buffer[4096];
static unsigned int input_offset, input_len;
static off_t consumed_bytes;
+static off_t max_input_size;
static unsigned deepest_delta;
static git_SHA_CTX input_ctx;
static uint32_t input_crc32;
@@ -296,6 +298,8 @@ static void use(int bytes)
if (signed_add_overflows(consumed_bytes, bytes))
die(_("pack too large for current definition of off_t"));
consumed_bytes += bytes;
+ if (max_input_size && consumed_bytes > max_input_size)
+ die(_("pack exceeds maximum allowed size"));
}
static const char *open_pack_file(const char *pack_name)
@@ -338,10 +342,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];
@@ -349,7 +353,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)
@@ -429,7 +434,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];
@@ -549,13 +554,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);
@@ -563,15 +568,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;
@@ -782,13 +787,15 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
const unsigned char *sha1)
{
void *new_data = NULL;
- int collision_test_needed;
+ int collision_test_needed = 0;
assert(data || obj_entry);
- read_lock();
- collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
- read_unlock();
+ if (startup_info->have_repository) {
+ read_lock();
+ collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
+ read_unlock();
+ }
if (collision_test_needed && !data) {
read_lock();
@@ -1185,12 +1192,10 @@ static void resolve_deltas(void)
return;
/* Sort deltas by base SHA1/offset for fast searching */
- 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);
+ QSORT(ofs_deltas, nr_ofs_deltas, compare_ofs_delta_entry);
+ QSORT(ref_deltas, nr_ref_deltas, compare_ref_delta_entry);
- if (verbose)
+ if (verbose || show_resolving_progress)
progress = start_progress(_("Resolving deltas"),
nr_ref_deltas + nr_ofs_deltas);
@@ -1250,7 +1255,9 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
nr_unresolved * sizeof(*objects));
f = sha1fd(output_fd, curr_pack);
fix_unresolved_deltas(f);
- strbuf_addf(&msg, _("completed with %d local objects"),
+ 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);
@@ -1346,10 +1353,10 @@ static void fix_unresolved_deltas(struct sha1file *f)
* 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_ref_deltas * sizeof(*sorted_by_pos));
+ 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);
+ QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
for (i = 0; i < nr_ref_deltas; i++) {
struct ref_delta_entry *d = sorted_by_pos[i];
@@ -1379,7 +1386,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
unsigned char *sha1)
{
const char *report = "pack";
- char name[PATH_MAX];
+ struct strbuf pack_name = STRBUF_INIT;
+ struct strbuf index_name = STRBUF_INIT;
+ struct strbuf keep_name_buf = STRBUF_INIT;
int err;
if (!from_stdin) {
@@ -1395,14 +1404,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
int keep_fd, keep_msg_len = strlen(keep_msg);
if (!keep_name)
- keep_fd = odb_pack_keep(name, sizeof(name), sha1);
- else
- keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+ keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
+ keep_fd = odb_pack_keep(keep_name);
if (keep_fd < 0) {
if (errno != EEXIST)
die_errno(_("cannot write keep file '%s'"),
- keep_name ? keep_name : name);
+ keep_name);
} else {
if (keep_msg_len > 0) {
write_or_die(keep_fd, keep_msg, keep_msg_len);
@@ -1410,28 +1418,22 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
}
if (close(keep_fd) != 0)
die_errno(_("cannot close written keep file '%s'"),
- keep_name ? keep_name : name);
+ keep_name);
report = "keep";
}
}
if (final_pack_name != curr_pack_name) {
- if (!final_pack_name) {
- snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
- get_object_directory(), sha1_to_hex(sha1));
- final_pack_name = name;
- }
+ if (!final_pack_name)
+ final_pack_name = odb_pack_name(&pack_name, sha1, "pack");
if (finalize_object_file(curr_pack_name, final_pack_name))
die(_("cannot store pack file"));
} else if (from_stdin)
chmod(final_pack_name, 0444);
if (final_index_name != curr_index_name) {
- if (!final_index_name) {
- snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
- get_object_directory(), sha1_to_hex(sha1));
- final_index_name = name;
- }
+ if (!final_index_name)
+ final_index_name = odb_pack_name(&index_name, sha1, "idx");
if (finalize_object_file(curr_index_name, final_index_name))
die(_("cannot store index file"));
} else
@@ -1457,6 +1459,10 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
input_offset += err;
}
}
+
+ strbuf_release(&index_name);
+ strbuf_release(&pack_name);
+ strbuf_release(&keep_name_buf);
}
static int git_index_pack_config(const char *k, const char *v, void *cb)
@@ -1514,6 +1520,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;
/*
@@ -1525,8 +1532,7 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
opts->anomaly[opts->anomaly_nr++] = ntohl(idx2[off * 2 + 1]);
}
- if (1 < opts->anomaly_nr)
- qsort(opts->anomaly, opts->anomaly_nr, sizeof(uint32_t), cmp_uint32);
+ QSORT(opts->anomaly, opts->anomaly_nr, cmp_uint32);
}
static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
@@ -1598,6 +1604,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;
@@ -1610,6 +1628,7 @@ 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);
@@ -1679,6 +1698,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);
@@ -1692,6 +1715,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
opts.off32_limit = strtoul(c+1, &c, 0);
if (*c || opts.off32_limit & 0x80000000)
die(_("bad %s"), arg);
+ } else if (skip_prefix(arg, "--max-input-size=", &arg)) {
+ max_input_size = strtoumax(arg, NULL, 10);
} else
usage(index_pack_usage);
continue;
@@ -1706,24 +1731,13 @@ 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 (from_stdin && !startup_info->have_repository)
+ die(_("--stdin requires a git repository"));
+ if (!index_name && pack_name)
+ index_name = derive_filename(pack_name, ".idx", &index_name_buf);
+ if (keep_msg && !keep_name && pack_name)
+ keep_name = derive_filename(pack_name, ".keep", &keep_name_buf);
+
if (verify) {
if (!index_name)
die(_("--verify with no packfile name given"));
@@ -1744,11 +1758,13 @@ 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));
+ objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry));
if (show_stat)
- obj_stat = xcalloc(nr_objects + 1, sizeof(struct object_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(ofs_deltas);
@@ -1759,7 +1775,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
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 07229d6..8a6acb0 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -22,7 +22,6 @@
static int init_is_bare_repository = 0;
static int init_shared_repository = -1;
static const char *init_db_template_dir;
-static const char *git_link;
static void copy_templates_1(struct strbuf *path, struct strbuf *template,
DIR *dir)
@@ -95,6 +94,8 @@ static void copy_templates(const char *template_dir)
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;
char *to_free = NULL;
@@ -121,21 +122,22 @@ static void copy_templates(const char *template_dir)
/* Make sure that template is from the correct vintage */
strbuf_addstr(&template_path, "config");
- repository_format_version = 0;
- git_config_from_file(check_repository_format_version,
- template_path.buf, NULL);
+ read_repository_format(&template_format, template_path.buf);
strbuf_setlen(&template_path, template_len);
- 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);
+ /*
+ * 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;
}
- strbuf_addstr(&path, get_git_dir());
+ strbuf_addstr(&path, get_git_common_dir());
strbuf_complete(&path, '/');
copy_templates_1(&path, &template_path, dir);
close_free_return:
@@ -168,7 +170,8 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
return 1;
}
-static int create_default_files(const char *template_path)
+static int create_default_files(const char *template_path,
+ const char *original_git_dir)
{
struct stat st1;
struct strbuf buf = STRBUF_INIT;
@@ -177,42 +180,52 @@ static int create_default_files(const char *template_path)
char junk[2];
int reinit;
int filemode;
-
- /*
- * Create .git/refs/{heads,tags}
- */
- 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);
+ struct strbuf err = STRBUF_INIT;
/* Just look for `init.templatedir` */
git_config(git_init_db_config, NULL);
- /* First copy the templates -- we might have the default
+ /*
+ * First copy the templates -- we might have the default
* config file there, in which case we would want to read
* from it after installing.
+ *
+ * Before reading that config, we also need to clear out any cached
+ * values (since we've just potentially changed what's available on
+ * disk).
*/
copy_templates(template_path);
-
+ git_config_clear();
+ reset_shared_repository();
git_config(git_default_config, NULL);
- is_bare_repository_cfg = init_is_bare_repository;
- /* reading existing config may have overwrote it */
+ /*
+ * We must make sure command-line options continue to override any
+ * values we might have just re-read from the config.
+ */
+ is_bare_repository_cfg = init_is_bare_repository;
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_buf(&buf, "refs"));
- adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
- adjust_shared_perm(git_path_buf(&buf, "refs/tags"));
}
/*
+ * We need to create a "refs" dir in any case so that older
+ * versions of git can tell that this is a repository.
+ */
+ safe_create_dir(git_path("refs"), 1);
+ adjust_shared_perm(git_path("refs"));
+
+ if (refs_init_db(&err))
+ die("failed to set up refs db: %s", err.buf);
+
+ /*
* Create the default symlink from ".git/HEAD" to the "master"
* branch, if it does not exist yet.
*/
@@ -249,9 +262,9 @@ static int create_default_files(const char *template_path)
const char *work_tree = get_git_work_tree();
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(get_git_dir(), work_tree))
+ if (log_all_ref_updates == LOG_REFS_UNSET)
+ git_config_set("core.logallrefupdates", "true");
+ if (needs_work_tree_config(original_git_dir, work_tree))
git_config_set("core.worktree", work_tree);
}
@@ -299,33 +312,7 @@ static void create_object_directory(void)
strbuf_release(&path);
}
-int set_git_dir_init(const char *git_dir, const char *real_git_dir,
- int exist_ok)
-{
- if (real_git_dir) {
- struct stat st;
-
- if (!exist_ok && !stat(git_dir, &st))
- die(_("%s already exists"), git_dir);
-
- if (!exist_ok && !stat(real_git_dir, &st))
- die(_("%s already exists"), real_git_dir);
-
- /*
- * make sure symlinks are resolved because we'll be
- * moving the target repo later on in separate_git_dir()
- */
- git_link = xstrdup(real_path(git_dir));
- set_git_dir(real_path(real_git_dir));
- }
- else {
- set_git_dir(real_path(git_dir));
- git_link = NULL;
- }
- return 0;
-}
-
-static void separate_git_dir(const char *git_dir)
+static void separate_git_dir(const char *git_dir, const char *git_link)
{
struct stat st;
@@ -346,13 +333,31 @@ static void separate_git_dir(const char *git_dir)
write_file(git_link, "gitdir: %s", git_dir);
}
-int init_db(const char *template_dir, unsigned int flags)
+int init_db(const char *git_dir, const char *real_git_dir,
+ const char *template_dir, unsigned int flags)
{
int reinit;
- const char *git_dir = get_git_dir();
+ int exist_ok = flags & INIT_DB_EXIST_OK;
+ char *original_git_dir = real_pathdup(git_dir, 1);
- if (git_link)
- separate_git_dir(git_dir);
+ if (real_git_dir) {
+ struct stat st;
+
+ if (!exist_ok && !stat(git_dir, &st))
+ die(_("%s already exists"), git_dir);
+
+ if (!exist_ok && !stat(real_git_dir, &st))
+ die(_("%s already exists"), real_git_dir);
+
+ set_git_dir(real_path(real_git_dir));
+ git_dir = get_git_dir();
+ separate_git_dir(git_dir, original_git_dir);
+ }
+ else {
+ set_git_dir(real_path(git_dir));
+ git_dir = get_git_dir();
+ }
+ startup_info->have_repository = 1;
safe_create_dir(git_dir, 0);
@@ -365,11 +370,11 @@ int init_db(const char *template_dir, unsigned int flags)
*/
check_repository_format();
- reinit = create_default_files(template_dir);
+ reinit = create_default_files(template_dir, original_git_dir);
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
@@ -377,12 +382,12 @@ 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 */
- xsnprintf(buf, sizeof(buf), "0%o", -shared_repository);
- else if (shared_repository == PERM_GROUP)
+ 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 (shared_repository == PERM_EVERYBODY)
+ else if (get_shared_repository() == PERM_EVERYBODY)
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
else
die("BUG: invalid value for shared_repository");
@@ -393,15 +398,19 @@ 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] != '/' ? "/" : "");
}
+ free(original_git_dir);
return 0;
}
@@ -480,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
if (real_git_dir && !is_absolute_path(real_git_dir))
- real_git_dir = xstrdup(real_path(real_git_dir));
+ real_git_dir = real_pathdup(real_git_dir, 1);
if (argc == 1) {
int mkdir_tried = 0;
@@ -493,8 +502,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:
@@ -506,7 +515,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;
@@ -524,7 +533,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
@@ -551,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *git_dir_parent = strrchr(git_dir, '/');
if (git_dir_parent) {
char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
- git_work_tree_cfg = xstrdup(real_path(rel));
+ git_work_tree_cfg = real_pathdup(rel, 1);
free(rel);
}
if (!git_work_tree_cfg)
@@ -569,7 +578,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
set_git_work_tree(work_tree);
}
- set_git_dir_init(git_dir, real_git_dir, 1);
-
- return init_db(template_dir, flags);
+ flags |= INIT_DB_EXIST_OK;
+ return init_db(git_dir, real_git_dir, template_dir, flags);
}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 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 069bd3a..b3b10cc 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -33,6 +33,7 @@ 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;
@@ -51,6 +52,11 @@ struct line_opt_callback_data {
struct string_list args;
};
+static int auto_decoration_style(void)
+{
+ return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
+}
+
static int parse_decoration_style(const char *var, const char *value)
{
switch (git_config_maybe_bool(var, value)) {
@@ -66,7 +72,7 @@ static int parse_decoration_style(const char *var, const char *value)
else if (!strcmp(value, "short"))
return DECORATE_SHORT_REFS;
else if (!strcmp(value, "auto"))
- return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
+ return auto_decoration_style();
return -1;
}
@@ -100,6 +106,12 @@ 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)
@@ -113,6 +125,7 @@ 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)
@@ -230,16 +243,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;
@@ -256,14 +270,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
@@ -325,7 +344,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);
@@ -341,6 +360,7 @@ 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)
/*
@@ -361,6 +381,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)) {
@@ -388,6 +410,8 @@ static int git_log_config(const char *var, const char *value, void *cb)
if (decoration_style < 0)
decoration_style = 0; /* maybe warn? */
return 0;
+ } else {
+ decoration_style = auto_decoration_style();
}
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
@@ -403,6 +427,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
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;
@@ -416,7 +444,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);
@@ -439,27 +467,27 @@ 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);
}
-static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, const char *obj_name)
+static int show_blob_object(const struct object_id *oid, struct rev_info *rev, const char *obj_name)
{
- unsigned char sha1c[20];
+ struct object_id oidc;
struct object_context obj_context;
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);
+ return stream_blob_to_fd(1, oid, NULL, 0);
- if (get_sha1_with_context(obj_name, 0, sha1c, &obj_context))
+ if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context))
die(_("Not a valid object name %s"), obj_name);
if (!obj_context.path[0] ||
- !textconv_object(obj_context.path, obj_context.mode, sha1c, 1, &buf, &size))
- return stream_blob_to_fd(1, sha1, NULL, 0);
+ !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size))
+ return stream_blob_to_fd(1, oid, NULL, 0);
if (!buf)
die(_("git show %s: bad file"), obj_name);
@@ -468,15 +496,15 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con
return 0;
}
-static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
+static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
{
unsigned long size;
enum object_type type;
- char *buf = read_sha1_file(sha1, &type, &size);
+ char *buf = read_sha1_file(oid->hash, &type, &size);
int offset = 0;
if (!buf)
- return error(_("Could not read object %s"), sha1_to_hex(sha1));
+ return error(_("Could not read object %s"), oid_to_hex(oid));
assert(type == OBJ_TAG);
while (offset < size && buf[offset] != '\n') {
@@ -490,7 +518,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;
}
@@ -499,7 +527,8 @@ 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;
}
@@ -527,7 +556,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));
@@ -552,18 +581,18 @@ 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->oid.hash, &rev, name);
+ ret = show_blob_object(&o->oid, &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->oid.hash, &rev);
+ ret = show_tag_object(&o->oid, &rev);
rev.shown_one = 1;
if (ret)
break;
@@ -578,12 +607,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:
@@ -608,7 +637,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);
@@ -647,7 +676,7 @@ 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);
@@ -668,9 +697,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)
{
@@ -696,9 +725,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,
@@ -777,15 +809,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;
@@ -807,9 +855,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);
@@ -868,15 +916,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)
@@ -939,17 +987,16 @@ 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,
- &need_8bit_cte);
+ log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte);
for (i = 0; !need_8bit_cte && i < nr; i++) {
const char *buf = get_commit_buffer(list[i], NULL);
@@ -964,11 +1011,13 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
msg = body;
pp.fmt = CMIT_FMT_EMAIL;
pp.date_mode.type = DATE_RFC2822;
+ pp.rev = rev;
+ pp.print_email_subject = 1;
pp_user_info(&pp, NULL, &sb, committer, encoding);
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
add_branch_description(&sb, branch_name);
- printf("%s\n", sb.buf);
+ fprintf(rev->diffopt.file, "%s\n", sb.buf);
strbuf_release(&sb);
@@ -977,6 +1026,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]);
@@ -999,8 +1049,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
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)
@@ -1042,8 +1091,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory)
if (!output_directory)
return prefix;
- return xstrdup(prefix_filename(prefix, outdir_offset,
- output_directory));
+ return prefix_filename(prefix, output_directory);
}
static const char * const builtin_format_patch_usage[] = {
@@ -1070,6 +1118,11 @@ static int subject_prefix_callback(const struct option *opt, const char *arg,
return 0;
}
+static int rfc_callback(const struct option *opt, const char *arg, int unset)
+{
+ return subject_prefix_callback(opt, "RFC PATCH", unset);
+}
+
static int numbered_cmdline_opt = 0;
static int numbered_callback(const struct option *opt, const char *arg,
@@ -1182,6 +1235,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;
+ struct object_id oid;
+
+ if (get_oid(upstream, &oid))
+ die(_("Failed to resolve '%s' as a valid ref."), upstream);
+ commit = lookup_commit_or_die(oid.hash, "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) {
+ struct object_id oid;
+ struct object_id *patch_id;
+ if (commit->util)
+ continue;
+ if (commit_patch_id(commit, &diffopt, oid.hash, 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;
+ oidcpy(patch_id, &oid);
+ 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;
@@ -1196,6 +1398,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;
@@ -1204,7 +1407,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"),
@@ -1225,6 +1430,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
N_("start numbering patches at <n> instead of 1")),
OPT_INTEGER('v', "reroll-count", &reroll_count,
N_("mark the series as Nth re-roll")),
+ { OPTION_CALLBACK, 0, "rfc", &rev, NULL,
+ N_("Use [RFC PATCH] instead of [PATCH]"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
{ OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
N_("Use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback },
@@ -1236,6 +1444,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,
@@ -1265,6 +1475,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")),
@@ -1274,10 +1486,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;
@@ -1358,7 +1571,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (numbered && keep_subject)
die (_("-n and -k are mutually exclusive."));
if (keep_subject && subject_prefix)
- die (_("--subject-prefix and -k are mutually exclusive."));
+ die (_("--subject-prefix/--rfc and -k are mutually exclusive."));
rev.preserve_subject = keep_subject;
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
@@ -1380,18 +1593,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)
@@ -1422,10 +1642,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
check_head = 1;
if (check_head) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *ref, *v;
ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- sha1, NULL);
+ oid.hash, NULL);
if (ref && skip_prefix(ref, "refs/heads/", &v))
branch_name = xstrdup(v);
else
@@ -1449,9 +1669,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
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;
@@ -1473,16 +1690,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
/* nothing to do */
return 0;
total = nr;
- if (!keep_subject && auto_number && total > 1)
- numbered = 1;
- if (numbered)
- rev.total = total + start_number - 1;
if (cover_letter == -1) {
if (config_cover_letter == COVER_AUTO)
cover_letter = (total > 1);
else
cover_letter = (config_cover_letter == COVER_ON);
}
+ if (!keep_subject && auto_number && (total > 1 || cover_letter))
+ numbered = 1;
+ if (numbered)
+ rev.total = total + start_number - 1;
if (!signature) {
; /* --no-signature inhibits all signatures */
@@ -1496,6 +1713,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) {
@@ -1509,6 +1733,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--;
}
@@ -1554,7 +1780,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
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);
@@ -1568,15 +1794,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);
@@ -1590,9 +1817,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
{
- unsigned char sha1[20];
- if (get_sha1(arg, sha1) == 0) {
- struct commit *commit = lookup_commit_reference(sha1);
+ struct object_id oid;
+ if (get_oid(arg, &oid) == 0) {
+ struct commit *commit = lookup_commit_reference(oid.hash);
if (commit) {
commit->object.flags |= flags;
add_pending_object(revs, &commit->object, arg);
@@ -1608,15 +1835,15 @@ 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,
+ 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,
+ fprintf(file, "%c %s %s\n", sign,
find_unique_abbrev(commit->object.oid.hash, abbrev),
buf.buf);
strbuf_release(&buf);
@@ -1697,7 +1924,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 b6a7cb0..d449e46 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -14,6 +14,7 @@
#include "resolve-undo.h"
#include "string-list.h"
#include "pathspec.h"
+#include "run-command.h"
static int abbrev;
static int show_deleted;
@@ -27,8 +28,12 @@ static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
static int debug_mode;
+static int show_eol;
+static int recurse_submodules;
+static struct argv_array submodule_options = ARGV_ARRAY_INIT;
static const char *prefix;
+static const char *super_prefix;
static int max_prefix_len;
static int prefix_len;
static struct pathspec pathspec;
@@ -47,14 +52,44 @@ 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)
{
/*
+ * Prepend the super_prefix to name to construct the full_name to be
+ * written.
+ */
+ struct strbuf full_name = STRBUF_INIT;
+ if (super_prefix) {
+ strbuf_addstr(&full_name, super_prefix);
+ strbuf_addstr(&full_name, name);
+ name = full_name.buf;
+ }
+
+ /*
* With "--full-name", prefix_len=0; this caller needs to pass
* an empty string in that case (a NULL is good for "").
*/
write_name_quoted_relative(name, prefix_len ? prefix : NULL,
stdout, line_terminator);
+
+ strbuf_release(&full_name);
}
static void show_dir_entry(const char *tag, struct dir_entry *ent)
@@ -68,6 +103,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 +135,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]))
@@ -132,54 +169,118 @@ static void show_killed_files(struct dir_struct *dir)
}
}
+/*
+ * Compile an argv_array with all of the options supported by --recurse_submodules
+ */
+static void compile_submodule_options(const char **argv,
+ const struct dir_struct *dir,
+ int show_tag)
+{
+ if (line_terminator == '\0')
+ argv_array_push(&submodule_options, "-z");
+ if (show_tag)
+ argv_array_push(&submodule_options, "-t");
+ if (show_valid_bit)
+ argv_array_push(&submodule_options, "-v");
+ if (show_cached)
+ argv_array_push(&submodule_options, "--cached");
+ if (show_eol)
+ argv_array_push(&submodule_options, "--eol");
+ if (debug_mode)
+ argv_array_push(&submodule_options, "--debug");
+
+ /* Add Pathspecs */
+ argv_array_push(&submodule_options, "--");
+ for (; *argv; argv++)
+ argv_array_push(&submodule_options, *argv);
+}
+
+/**
+ * Recursively call ls-files on a submodule
+ */
+static void show_gitlink(const struct cache_entry *ce)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ int status;
+
+ if (prefix_len)
+ argv_array_pushf(&cp.env_array, "%s=%s",
+ GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
+ prefix);
+ argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+ super_prefix ? super_prefix : "",
+ ce->name);
+ argv_array_push(&cp.args, "ls-files");
+ argv_array_push(&cp.args, "--recurse-submodules");
+
+ /* add supported options */
+ argv_array_pushv(&cp.args, submodule_options.argv);
+
+ cp.git_cmd = 1;
+ cp.dir = ce->name;
+ status = run_command(&cp);
+ if (status)
+ exit(status);
+}
+
static void show_ce_entry(const char *tag, const struct cache_entry *ce)
{
+ struct strbuf name = STRBUF_INIT;
int len = max_prefix_len;
+ if (super_prefix)
+ strbuf_addstr(&name, super_prefix);
+ strbuf_addstr(&name, ce->name);
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
- if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
- len, ps_matched,
- S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
- return;
+ if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+ submodule_path_match(&pathspec, name.buf, ps_matched)) {
+ show_gitlink(ce);
+ } else if (match_pathspec(&pathspec, name.buf, name.len,
+ len, ps_matched,
+ S_ISDIR(ce->ce_mode) ||
+ S_ISGITLINK(ce->ce_mode))) {
+ if (tag && *tag && show_valid_bit &&
+ (ce->ce_flags & CE_VALID)) {
+ static char alttag[4];
+ memcpy(alttag, tag, 3);
+ if (isalpha(tag[0]))
+ alttag[0] = tolower(tag[0]);
+ else if (tag[0] == '?')
+ alttag[0] = '!';
+ else {
+ alttag[0] = 'v';
+ alttag[1] = tag[0];
+ alttag[2] = ' ';
+ alttag[3] = 0;
+ }
+ tag = alttag;
+ }
- if (tag && *tag && show_valid_bit &&
- (ce->ce_flags & CE_VALID)) {
- static char alttag[4];
- memcpy(alttag, tag, 3);
- if (isalpha(tag[0]))
- alttag[0] = tolower(tag[0]);
- else if (tag[0] == '?')
- alttag[0] = '!';
- else {
- alttag[0] = 'v';
- alttag[1] = tag[0];
- alttag[2] = ' ';
- alttag[3] = 0;
+ if (!show_stage) {
+ fputs(tag, stdout);
+ } else {
+ printf("%s%06o %s %d\t",
+ tag,
+ ce->ce_mode,
+ find_unique_abbrev(ce->oid.hash, abbrev),
+ ce_stage(ce));
+ }
+ write_eolinfo(ce, ce->name);
+ write_name(ce->name);
+ if (debug_mode) {
+ const struct stat_data *sd = &ce->ce_stat_data;
+
+ printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+ printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+ printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+ printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
}
- tag = alttag;
}
- if (!show_stage) {
- fputs(tag, stdout);
- } else {
- printf("%s%06o %s %d\t",
- tag,
- ce->ce_mode,
- find_unique_abbrev(ce->sha1,abbrev),
- ce_stage(ce));
- }
- write_name(ce->name);
- if (debug_mode) {
- const struct stat_data *sd = &ce->ce_stat_data;
-
- printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
- printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
- printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
- printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
- printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
- }
+ strbuf_release(&name);
}
static void show_ru_info(void)
@@ -269,28 +370,30 @@ static void show_files(struct dir_struct *dir)
/*
* Prune the index to only contain stuff starting with "prefix"
*/
-static void prune_cache(const char *prefix)
+static void prune_cache(const char *prefix, size_t prefixlen)
{
- int pos = cache_name_pos(prefix, max_prefix_len);
+ int pos;
unsigned int first, last;
+ if (!prefix)
+ return;
+ pos = cache_name_pos(prefix, prefixlen);
if (pos < 0)
pos = -pos-1;
- memmove(active_cache, active_cache + pos,
- (active_nr - pos) * sizeof(struct cache_entry *));
- active_nr -= pos;
- first = 0;
+ first = pos;
last = active_nr;
while (last > first) {
int next = (last + first) >> 1;
const struct cache_entry *ce = active_cache[next];
- if (!strncmp(ce->name, prefix, max_prefix_len)) {
+ if (!strncmp(ce->name, prefix, prefixlen)) {
first = next+1;
continue;
}
last = next;
}
- active_nr = last;
+ memmove(active_cache, active_cache + pos,
+ (last - pos) * sizeof(struct cache_entry *));
+ active_nr = last - pos;
}
/*
@@ -359,14 +462,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 +503,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 +528,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),
@@ -454,6 +550,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
{ OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
N_("make the output relative to the project top directory"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
+ OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+ N_("recurse through submodules")),
OPT_BOOL(0, "error-unmatch", &error_unmatch,
N_("if any <file> is not in the index, treat this as an error")),
OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
@@ -470,6 +568,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
+ super_prefix = get_super_prefix();
git_config(git_default_config, NULL);
if (read_cache() < 0)
@@ -505,13 +604,32 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
+ if (recurse_submodules)
+ compile_submodule_options(argv, &dir, show_tag);
+
+ if (recurse_submodules &&
+ (show_stage || show_deleted || show_others || show_unmerged ||
+ show_killed || show_modified || show_resolve_undo || with_tree))
+ die("ls-files --recurse-submodules unsupported mode");
+
+ if (recurse_submodules && error_unmatch)
+ die("ls-files --recurse-submodules does not support "
+ "--error-unmatch");
+
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD |
PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
prefix, argv);
- /* Find common prefix for all pathspec's */
- max_prefix = common_prefix(&pathspec);
+ /*
+ * Find common prefix for all pathspec's
+ * This is used as a performance optimization which unfortunately cannot
+ * be done when recursing into submodules
+ */
+ if (recurse_submodules)
+ max_prefix = NULL;
+ else
+ max_prefix = common_prefix(&pathspec);
max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
/* Treat unmatching pathspec elements as errors */
@@ -526,8 +644,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
show_killed || show_modified || show_resolve_undo))
show_cached = 1;
- if (max_prefix)
- prune_cache(max_prefix);
+ prune_cache(max_prefix, max_prefix_len);
if (with_tree) {
/*
* Basic sanity check; show-stages and show-unmerged
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index fa65a84..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] [--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,59 +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++)
- pattern[j - i] = xstrfmt("*/%s", argv[j]);
- }
remote = remote_get(dest);
if (!remote) {
if (dest)
@@ -125,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", oid_to_hex(&ref->old_oid), 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 0e30d86..d7ebeb4 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -31,21 +31,18 @@ static const char * const ls_tree_usage[] = {
static int show_recursive(const char *base, int baselen, const char *pathname)
{
- const char **s;
+ int i;
if (ls_options & LS_RECURSIVE)
return 1;
- s = pathspec._raw;
- if (!s)
+ if (!pathspec.nr)
return 0;
- for (;;) {
- const char *spec = *s++;
+ for (i = 0; i < pathspec.nr; i++) {
+ const char *spec = pathspec.items[i].match;
int len, speclen;
- if (!spec)
- return 0;
if (strncmp(base, spec, baselen))
continue;
len = strlen(pathname);
@@ -59,6 +56,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
continue;
return 1;
}
+ return 0;
}
static int show_tree(const unsigned char *sha1, struct strbuf *base,
@@ -175,8 +173,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
* cannot be lifted until it is converted to use
* match_pathspec() or tree_entry_interesting()
*/
- parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE |
- PATHSPEC_EXCLUDE,
+ parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
+ ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
PATHSPEC_PREFER_CWD,
prefix, argv + 1);
for (i = 0; i < pathspec.nr; i++)
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index f6df274..cfb667a 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -16,10 +16,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
const char *def_charset;
struct mailinfo mi;
int status;
+ char *msgfile, *patchfile;
- /* NEEDSWORK: might want to do the optional .git/ directory
- * discovery
- */
setup_mailinfo(&mi);
def_charset = get_commit_output_encoding();
@@ -54,8 +52,14 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
mi.input = stdin;
mi.output = stdout;
- status = !!mailinfo(&mi, argv[1], argv[2]);
+
+ msgfile = prefix_filename(prefix, argv[1]);
+ patchfile = prefix_filename(prefix, argv[2]);
+
+ status = !!mailinfo(&mi, msgfile, patchfile);
clear_mailinfo(&mi);
+ free(msgfile);
+ free(patchfile);
return status;
}
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 104277a..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");
@@ -109,7 +125,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
if ((dir = opendir(name)) == NULL) {
if (errno == ENOENT)
continue;
- error("cannot opendir %s (%s)", name, strerror(errno));
+ error_errno("cannot opendir %s", name);
goto out;
}
@@ -174,12 +190,12 @@ static int split_maildir(const char *maildir, const char *dir,
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;
}
@@ -210,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;
}
@@ -271,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;
@@ -318,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 a891162..cfe2a79 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -36,12 +36,12 @@ static const char * const merge_base_usage[] = {
static struct commit *get_commit_reference(const char *arg)
{
- unsigned char revkey[20];
+ struct object_id revkey;
struct commit *r;
- if (get_sha1(arg, revkey))
+ if (get_oid(arg, &revkey))
die("Not a valid object name %s", arg);
- r = lookup_commit_reference(revkey);
+ r = lookup_commit_reference(revkey.hash);
if (!r)
die("Not a valid commit name %s", arg);
@@ -113,14 +113,14 @@ struct rev_collect {
unsigned int initial : 1;
};
-static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
{
struct commit *commit;
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return;
- commit = lookup_commit(sha1);
+ commit = lookup_commit(oid->hash);
if (!commit ||
(commit->object.flags & TMP_MARK) ||
parse_commit(commit))
@@ -131,7 +131,7 @@ static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
commit->object.flags |= TMP_MARK;
}
-static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
const char *ident, unsigned long timestamp,
int tz, const char *message, void *cbdata)
{
@@ -139,15 +139,15 @@ static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
if (revs->initial) {
revs->initial = 0;
- add_one_commit(osha1, revs);
+ add_one_commit(ooid, revs);
}
- add_one_commit(nsha1, revs);
+ add_one_commit(noid, revs);
return 0;
}
static int handle_fork_point(int argc, const char **argv)
{
- unsigned char sha1[20];
+ struct object_id oid;
char *refname;
const char *commitname;
struct rev_collect revs;
@@ -155,7 +155,7 @@ static int handle_fork_point(int argc, const char **argv)
struct commit_list *bases;
int i, ret = 0;
- switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) {
+ switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) {
case 0:
die("No such ref: '%s'", argv[0]);
case 1:
@@ -165,14 +165,17 @@ static int handle_fork_point(int argc, const char **argv)
}
commitname = (argc == 2) ? argv[1] : "HEAD";
- if (get_sha1(commitname, sha1))
+ if (get_oid(commitname, &oid))
die("Not a valid object name: '%s'", commitname);
- derived = lookup_commit_reference(sha1);
+ derived = lookup_commit_reference(oid.hash);
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
+ if (!revs.nr && !get_oid(refname, &oid))
+ add_one_commit(&oid, &revs);
+
for (i = 0; i < revs.nr; i++)
revs.commit[i]->object.flags &= ~TMP_MARK;
@@ -252,7 +255,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 5544705..47dde7c 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -28,7 +28,6 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
xmparam_t xmp = {{0}};
int ret = 0, i = 0, to_stdout = 0;
int quiet = 0;
- int prefixlen = 0;
struct option options[] = {
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
@@ -62,19 +61,22 @@ 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)
- prefixlen = strlen(prefix);
-
for (i = 0; i < 3; i++) {
- const char *fname = prefix_filename(prefix, prefixlen, argv[i]);
+ char *fname;
+ int ret;
+
if (!names[i])
names[i] = argv[i];
- if (read_mmfile(mmfs + i, fname))
+
+ fname = prefix_filename(prefix, argv[i]);
+ ret = read_mmfile(mmfs + i, fname);
+ free(fname);
+ if (ret)
return -1;
+
if (mmfs[i].size > MAX_XDIFF_SIZE ||
buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
return error("Cannot merge binary files: %s",
@@ -91,17 +93,19 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
if (ret >= 0) {
const char *filename = argv[0];
- const char *fpath = prefix_filename(prefix, prefixlen, argv[0]);
+ char *fpath = prefix_filename(prefix, argv[0]);
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);
+ free(fpath);
}
if (ret > 127)
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 1c3427c..2d1b6db 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -22,7 +22,7 @@ static int merge_entry(int pos, const char *path)
if (strcmp(ce->name, path))
break;
found++;
- sha1_to_hex_r(hexbuf[stage], ce->sha1);
+ oid_to_hex_r(hexbuf[stage], &ce->oid);
xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
arguments[stage] = hexbuf[stage];
arguments[stage + 4] = ownbuf[stage];
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index 491efd5..0dd9021 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -9,10 +9,10 @@ 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;
xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
name = getenv(githead_env);
@@ -21,10 +21,10 @@ static const char *better_branch_name(const char *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;
@@ -42,38 +42,41 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
if (!arg[2])
break;
if (parse_merge_opt(&o, arg + 2))
- die("Unknown option %s", arg);
+ die(_("unknown option %s"), arg);
continue;
}
if (bases_count < ARRAY_SIZE(bases)-1) {
- unsigned char *sha = xmalloc(20);
- if (get_sha1(argv[i], sha))
- die("Could not parse object '%s'", argv[i]);
- bases[bases_count++] = 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++] = oid;
}
else
- warning("Cannot handle more than %d bases. "
- "Ignoring %s.",
+ warning(Q_("cannot handle more than %d base. "
+ "Ignoring %s.",
+ "cannot handle more than %d bases. "
+ "Ignoring %s.",
+ (int)ARRAY_SIZE(bases)-1),
(int)ARRAY_SIZE(bases)-1, argv[i]);
}
if (argc - i != 3) /* "--" "<head>" "<remote>" */
- die("Not handling anything other than two heads merge.");
+ die(_("not handling anything other than two heads merge."));
o.branch1 = argv[++i];
o.branch2 = argv[++i];
- if (get_sha1(o.branch1, h1))
- die("Could not resolve ref '%s'", o.branch1);
- if (get_sha1(o.branch2, h2))
- die("Could not resolve ref '%s'", o.branch2);
+ if (get_oid(o.branch1, &h1))
+ die(_("could not resolve ref '%s'"), o.branch1);
+ if (get_oid(o.branch2, &h2))
+ die(_("could not resolve ref '%s'"), o.branch2);
o.branch1 = better_branch_name(o.branch1);
o.branch2 = better_branch_name(o.branch2);
if (o.verbosity >= 3)
- printf("Merging %s with %s\n", o.branch1, o.branch2);
+ 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 d4f0cbd..5b7ab9b 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -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 15bf95b..95572b1 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)
@@ -43,8 +44,8 @@ struct strategy {
static const char * const builtin_merge_usage[] = {
N_("git merge [<options>] [<commit>...]"),
- N_("git merge [<options>] <msg> HEAD <commit>"),
N_("git merge --abort"),
+ N_("git merge --continue"),
NULL
};
@@ -64,6 +65,8 @@ static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
static int abort_current_merge;
+static int continue_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 +213,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 +224,10 @@ 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, "continue", &continue_current_merge,
+ N_("continue 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) "" },
@@ -236,7 +243,7 @@ static void drop_save(void)
unlink(git_path_merge_mode());
}
-static int save_state(unsigned char *stash)
+static int save_state(struct object_id *stash)
{
int len;
struct child_process cp = CHILD_PROCESS_INIT;
@@ -257,7 +264,7 @@ static int save_state(unsigned char *stash)
else if (!len) /* no changes */
return -1;
strbuf_setlen(&buffer, buffer.len-1);
- if (get_sha1(buffer.buf, stash))
+ if (get_oid(buffer.buf, stash))
die(_("not a valid object: %s"), buffer.buf);
return 0;
}
@@ -297,18 +304,18 @@ static void reset_hard(unsigned const char *sha1, int verbose)
die(_("read-tree failed"));
}
-static void restore_state(const unsigned char *head,
- const unsigned char *stash)
+static void restore_state(const struct object_id *head,
+ const struct object_id *stash)
{
struct strbuf sb = STRBUF_INIT;
const char *args[] = { "stash", "apply", NULL, NULL };
- if (is_null_sha1(stash))
+ if (is_null_oid(stash))
return;
- reset_hard(head, 1);
+ reset_hard(head->hash, 1);
- args[2] = sha1_to_hex(stash);
+ args[2] = oid_to_hex(stash);
/*
* It is OK to ignore error here, for example when there was
@@ -333,15 +340,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;
@@ -368,19 +369,16 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
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);
}
static void finish(struct commit *head_commit,
struct commit_list *remoteheads,
- const unsigned char *new_head, const char *msg)
+ const struct object_id *new_head, const char *msg)
{
struct strbuf reflog_message = STRBUF_INIT;
- const unsigned char *head = head_commit->object.oid.hash;
+ const struct object_id *head = &head_commit->object.oid;
if (!msg)
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
@@ -398,12 +396,13 @@ static void finish(struct commit *head_commit,
else {
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
update_ref(reflog_message.buf, "HEAD",
- new_head, head, 0,
+ new_head->hash, head->hash, 0,
UPDATE_REFS_DIE_ON_ERR);
/*
* 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);
}
}
@@ -416,7 +415,7 @@ static void finish(struct commit *head_commit,
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
opts.detect_rename = DIFF_DETECT_RENAME;
diff_setup_done(&opts);
- diff_tree_sha1(head, new_head, "", &opts);
+ diff_tree_sha1(head->hash, new_head->hash, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
}
@@ -431,35 +430,35 @@ static void finish(struct commit *head_commit,
static void merge_name(const char *remote, struct strbuf *msg)
{
struct commit *remote_head;
- unsigned char branch_head[20];
+ struct object_id branch_head;
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
const char *ptr;
char *found_ref;
int len, early;
- strbuf_branchname(&bname, remote);
+ strbuf_branchname(&bname, remote, 0);
remote = bname.buf;
- memset(branch_head, 0, sizeof(branch_head));
+ oidclr(&branch_head);
remote_head = get_merge_parent(remote);
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
- if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
+ if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) {
if (starts_with(found_ref, "refs/heads/")) {
strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
- sha1_to_hex(branch_head), remote);
+ oid_to_hex(&branch_head), remote);
goto cleanup;
}
if (starts_with(found_ref, "refs/tags/")) {
strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
- sha1_to_hex(branch_head), remote);
+ oid_to_hex(&branch_head), remote);
goto cleanup;
}
if (starts_with(found_ref, "refs/remotes/")) {
strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
- sha1_to_hex(branch_head), remote);
+ oid_to_hex(&branch_head), remote);
goto cleanup;
}
}
@@ -497,7 +496,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (ref_exists(truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
- sha1_to_hex(remote_head->object.oid.hash),
+ oid_to_hex(&remote_head->object.oid),
truname.buf + 11,
(early ? " (early part)" : ""));
strbuf_release(&truname);
@@ -511,7 +510,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->oid.hash),
+ oid_to_hex(&desc->obj->oid),
typename(desc->obj->type),
remote);
goto cleanup;
@@ -519,7 +518,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.oid.hash), remote);
+ oid_to_hex(&remote_head->object.oid), remote);
cleanup:
strbuf_release(&buf);
strbuf_release(&bname);
@@ -590,8 +589,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return git_diff_ui_config(k, v, cb);
}
-static int read_tree_trivial(unsigned char *common, unsigned char *head,
- unsigned char *one)
+static int read_tree_trivial(struct object_id *common, struct object_id *head,
+ struct object_id *one)
{
int i, nr_trees = 0;
struct tree *trees[MAX_UNPACK_TREES];
@@ -606,13 +605,13 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
opts.merge = 1;
- trees[nr_trees] = parse_tree_indirect(common);
+ trees[nr_trees] = parse_tree_indirect(common->hash);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(head);
+ trees[nr_trees] = parse_tree_indirect(head->hash);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(one);
+ trees[nr_trees] = parse_tree_indirect(one->hash);
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
@@ -626,19 +625,20 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,
return 0;
}
-static void write_tree_trivial(unsigned char *sha1)
+static void write_tree_trivial(struct object_id *oid)
{
- if (write_cache_as_tree(sha1, 0, NULL))
+ if (write_cache_as_tree(oid->hash, 0, NULL))
die(_("git write-tree failed to write a tree"));
}
static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
- struct commit *head, const char *head_arg)
+ struct commit *head)
{
static struct lock_file lock;
+ const char *head_arg = "HEAD";
- hold_locked_index(&lock, 1);
+ hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
if (active_cache_changed &&
write_locked_index(&the_index, &lock, COMMIT_LOCK))
@@ -675,9 +675,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
- hold_locked_index(&lock, 1);
+ hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
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());
@@ -708,42 +710,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++)
@@ -752,18 +729,6 @@ 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();
@@ -797,7 +762,7 @@ 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))
abort_commit(remoteheads, NULL);
@@ -816,18 +781,26 @@ static void prepare_to_commit(struct commit_list *remoteheads)
static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
- unsigned char result_tree[20], result_commit[20];
+ struct object_id result_tree, result_commit;
struct commit_list *parents, **pptr = &parents;
+ static struct lock_file lock;
- write_tree_trivial(result_tree);
+ hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+ 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"));
pptr = commit_list_append(head, pptr);
pptr = commit_list_append(remoteheads->item, pptr);
prepare_to_commit(remoteheads);
- if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
- result_commit, NULL, sign_commit))
+ if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents,
+ result_commit.hash, NULL, sign_commit))
die(_("failed to write commit object"));
- finish(head, remoteheads, result_commit, "In-index merge");
+ finish(head, remoteheads, &result_commit, "In-index merge");
drop_save();
return 0;
}
@@ -836,12 +809,12 @@ static int finish_automerge(struct commit *head,
int head_subsumed,
struct commit_list *common,
struct commit_list *remoteheads,
- unsigned char *result_tree,
+ struct object_id *result_tree,
const char *wt_strategy)
{
struct commit_list *parents = NULL;
struct strbuf buf = STRBUF_INIT;
- unsigned char result_commit[20];
+ struct object_id result_commit;
free_commit_list(common);
parents = remoteheads;
@@ -849,11 +822,11 @@ static int finish_automerge(struct commit *head,
commit_list_insert(head, &parents);
strbuf_addch(&merge_msg, '\n');
prepare_to_commit(remoteheads);
- if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
- result_commit, NULL, sign_commit))
+ if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents,
+ result_commit.hash, NULL, sign_commit))
die(_("failed to write commit object"));
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
- finish(head, remoteheads, result_commit, buf.buf);
+ finish(head, remoteheads, &result_commit, buf.buf);
strbuf_release(&buf);
drop_save();
return 0;
@@ -880,24 +853,6 @@ static int suggest_conflicts(void)
return 1;
}
-static struct commit *is_old_style_invocation(int argc, const char **argv,
- const unsigned char *head)
-{
- struct commit *second_token = NULL;
- if (argc > 2) {
- unsigned char second_sha1[20];
-
- if (get_sha1(argv[1], second_sha1))
- return NULL;
- 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.oid.hash, head))
- return NULL;
- }
- return second_token;
-}
-
static int evaluate_result(void)
{
int cnt = 0;
@@ -938,7 +893,7 @@ static int setup_with_upstream(const char ***argv)
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"),
@@ -952,8 +907,6 @@ 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;
@@ -967,26 +920,14 @@ static void write_merge_state(struct commit_list *remoteheads)
}
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);
+ strbuf_addstr(&buf, "no-ff");
+ write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
}
static int default_edit_option(void)
@@ -1002,7 +943,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;
}
@@ -1079,7 +1020,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
die_errno(_("could not close '%s'"), filename);
for (pos = 0; pos < merge_names->len; pos = npos) {
- unsigned char sha1[20];
+ struct object_id oid;
char *ptr;
struct commit *commit;
@@ -1089,21 +1030,21 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
else
npos = merge_names->len;
- if (npos - pos < 40 + 2 ||
- get_sha1_hex(merge_names->buf + pos, sha1))
+ if (npos - pos < GIT_SHA1_HEXSZ + 2 ||
+ get_oid_hex(merge_names->buf + pos, &oid))
commit = NULL; /* bad */
- else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
+ else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2))
continue; /* not-for-merge */
else {
- char saved = merge_names->buf[pos + 40];
- merge_names->buf[pos + 40] = '\0';
+ char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ];
+ merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0';
commit = get_merge_parent(merge_names->buf + pos);
- merge_names->buf[pos + 40] = saved;
+ merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved;
}
if (!commit) {
if (ptr)
*ptr = '\0';
- die("not something we can merge in %s: %s",
+ die(_("not something we can merge in %s: %s"),
filename, merge_names->buf + pos);
}
remotes = &commit_list_insert(commit, remotes)->next;
@@ -1137,7 +1078,7 @@ static struct commit_list *collect_parents(struct commit *head_commit,
struct commit *commit = get_merge_parent(argv[i]);
if (!commit)
help_unknown_ref(argv[i], "merge",
- "not something we can merge");
+ _("not something we can merge"));
remotes = &commit_list_insert(commit, remotes)->next;
}
remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
@@ -1158,18 +1099,16 @@ static struct commit_list *collect_parents(struct commit *head_commit,
int cmd_merge(int argc, const char **argv, const char *prefix)
{
- unsigned char result_tree[20];
- unsigned char stash[20];
- unsigned char head_sha1[20];
+ struct object_id result_tree, stash, head_oid;
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;
struct commit_list *remoteheads, *p;
void *branch_to_free;
+ int orig_argc = argc;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1178,14 +1117,15 @@ 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_oid.hash, NULL);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
- if (!branch || is_null_sha1(head_sha1))
+ if (!branch || is_null_oid(&head_oid))
head_commit = NULL;
else
- head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+ head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
+ init_diff_ui_defaults();
git_config(git_merge_config, NULL);
if (branch_mergeoptions)
@@ -1202,6 +1142,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
int nargc = 2;
const char *nargv[] = {"reset", "--merge", NULL};
+ if (orig_argc != 2)
+ usage_msg_opt(_("--abort expects no arguments"),
+ builtin_merge_usage, builtin_merge_options);
+
if (!file_exists(git_path_merge_head()))
die(_("There is no merge to abort (MERGE_HEAD missing)."));
@@ -1210,6 +1154,22 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
+ if (continue_current_merge) {
+ int nargc = 1;
+ const char *nargv[] = {"commit", NULL};
+
+ if (orig_argc != 2)
+ usage_msg_opt(_("--continue expects no arguments"),
+ builtin_merge_usage, builtin_merge_options);
+
+ if (!file_exists(git_path_merge_head()))
+ die(_("There is no merge in progress (MERGE_HEAD missing)."));
+
+ /* Invoke 'git commit' */
+ ret = cmd_commit(nargc, nargv, prefix);
+ goto done;
+ }
+
if (read_cache_unmerged())
die_resolve_conflict("merge");
@@ -1256,12 +1216,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
builtin_merge_options);
if (!head_commit) {
- struct commit *remote_head;
/*
* If the merged head is a valid one there is no reason
* to forbid "git merge" into a branch yet to be born.
* We do the same for "git pull".
*/
+ struct object_id *remote_head_oid;
if (squash)
die(_("Squash commit into empty head not supported yet"));
if (fast_forward == FF_NO)
@@ -1269,46 +1229,24 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
"an empty head"));
remoteheads = collect_parents(head_commit, &head_subsumed,
argc, argv, NULL);
- remote_head = remoteheads->item;
- if (!remote_head)
+ if (!remoteheads)
die(_("%s - not something we can merge"), argv[0]);
if (remoteheads->next)
die(_("Can merge only exactly one commit into empty head"));
- read_empty(remote_head->object.oid.hash, 0);
- update_ref("initial pull", "HEAD", remote_head->object.oid.hash,
+ remote_head_oid = &remoteheads->item->object.oid;
+ read_empty(remote_head_oid->hash, 0);
+ update_ref("initial pull", "HEAD", remote_head_oid->hash,
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
goto done;
}
/*
- * 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.
+ * All the rest are the commits being merged; prepare
+ * the standard merge summary message to be appended
+ * to the given message.
*/
- if (!have_message &&
- is_old_style_invocation(argc, argv, head_commit->object.oid.hash)) {
- warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
- strbuf_addstr(&merge_msg, argv[0]);
- head_arg = argv[1];
- argv += 2;
- argc -= 2;
- remoteheads = collect_parents(head_commit, &head_subsumed,
- argc, argv, NULL);
- } else {
- /* We are invoked directly as the first-class UI. */
- head_arg = "HEAD";
-
- /*
- * All the rest are the commits being merged; prepare
- * the standard merge summary message to be appended
- * to the given message.
- */
- remoteheads = collect_parents(head_commit, &head_subsumed,
- argc, argv, &merge_msg);
- }
+ remoteheads = collect_parents(head_commit, &head_subsumed,
+ argc, argv, &merge_msg);
if (!head_commit || !argc)
usage_with_options(builtin_merge_usage,
@@ -1353,7 +1291,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
for (p = remoteheads; p; p = p->next) {
struct commit *commit = p->item;
strbuf_addf(&buf, "GITHEAD_%s",
- sha1_to_hex(commit->object.oid.hash));
+ oid_to_hex(&commit->object.oid));
setenv(buf.buf, merge_remote_util(commit)->name, 1);
strbuf_reset(&buf);
if (fast_forward != FF_ONLY &&
@@ -1396,31 +1334,33 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
- if (remoteheads && !common)
- ; /* No common ancestors found. We need a real merge. */
- else if (!remoteheads ||
+ if (remoteheads && !common) {
+ /* No common ancestors found. */
+ if (!allow_unrelated_histories)
+ die(_("refusing to merge unrelated histories"));
+ /* otherwise, we need a real merge. */
+ } else if (!remoteheads ||
(!remoteheads->next && !common->next &&
common->item == remoteheads->item)) {
/*
* If head can reach all the merge then we are up to date.
* but first the most common case of merging one remote.
*/
- finish_up_to_date("Already up-to-date.");
+ finish_up_to_date(_("Already up-to-date."));
goto done;
} else if (fast_forward != FF_NO && !remoteheads->next &&
!common->next &&
- !hashcmp(common->item->object.oid.hash, head_commit->object.oid.hash)) {
+ !oidcmp(&common->item->object.oid, &head_commit->object.oid)) {
/* Again the most common case of merging one remote. */
struct strbuf msg = STRBUF_INIT;
struct commit *commit;
if (verbosity >= 0) {
- char from[GIT_SHA1_HEXSZ + 1], to[GIT_SHA1_HEXSZ + 1];
- find_unique_abbrev_r(from, head_commit->object.oid.hash,
- DEFAULT_ABBREV);
- find_unique_abbrev_r(to, remoteheads->item->object.oid.hash,
- DEFAULT_ABBREV);
- printf(_("Updating %s..%s\n"), from, to);
+ printf(_("Updating %s..%s\n"),
+ find_unique_abbrev(head_commit->object.oid.hash,
+ DEFAULT_ABBREV),
+ find_unique_abbrev(remoteheads->item->object.oid.hash,
+ DEFAULT_ABBREV));
}
strbuf_addstr(&msg, "Fast-forward");
if (have_message)
@@ -1439,7 +1379,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
- finish(head_commit, remoteheads, commit->object.oid.hash, msg.buf);
+ finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
drop_save();
goto done;
} else if (!remoteheads->next && common->next)
@@ -1458,9 +1398,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
/* See if it is really trivial. */
git_committer_info(IDENT_STRICT);
printf(_("Trying really trivial in-index merge...\n"));
- if (!read_tree_trivial(common->item->object.oid.hash,
- head_commit->object.oid.hash,
- remoteheads->item->object.oid.hash)) {
+ if (!read_tree_trivial(&common->item->object.oid,
+ &head_commit->object.oid,
+ &remoteheads->item->object.oid)) {
ret = merge_trivial(head_commit, remoteheads);
goto done;
}
@@ -1483,14 +1423,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* HEAD^^" would be missed.
*/
common_one = get_merge_bases(head_commit, j->item);
- if (hashcmp(common_one->item->object.oid.hash,
- j->item->object.oid.hash)) {
+ if (oidcmp(&common_one->item->object.oid, &j->item->object.oid)) {
up_to_date = 0;
break;
}
}
if (up_to_date) {
- finish_up_to_date("Already up-to-date. Yeeah!");
+ finish_up_to_date(_("Already up-to-date. Yeeah!"));
goto done;
}
}
@@ -1513,14 +1452,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
/*
* Stash away the local changes so that we can try more than one.
*/
- save_state(stash))
- hashcpy(stash, null_sha1);
+ save_state(&stash))
+ oidclr(&stash);
for (i = 0; i < use_strategies_nr; i++) {
int ret;
if (i) {
printf(_("Rewinding the tree to pristine...\n"));
- restore_state(head_commit->object.oid.hash, stash);
+ restore_state(&head_commit->object.oid, &stash);