summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c55
-rw-r--r--builtin/apply.c47
-rw-r--r--builtin/archive.c53
-rw-r--r--builtin/bisect--helper.c7
-rw-r--r--builtin/blame.c51
-rw-r--r--builtin/branch.c89
-rw-r--r--builtin/bundle.c2
-rw-r--r--builtin/cat-file.c2
-rw-r--r--builtin/check-attr.c132
-rw-r--r--builtin/check-ref-format.c63
-rw-r--r--builtin/checkout-index.c32
-rw-r--r--builtin/checkout.c157
-rw-r--r--builtin/clean.c5
-rw-r--r--builtin/clone.c181
-rw-r--r--builtin/commit.c175
-rw-r--r--builtin/config.c55
-rw-r--r--builtin/describe.c17
-rw-r--r--builtin/diff-tree.c12
-rw-r--r--builtin/diff.c8
-rw-r--r--builtin/fast-export.c40
-rw-r--r--builtin/fetch-pack.c77
-rw-r--r--builtin/fetch.c223
-rw-r--r--builtin/fmt-merge-msg.c2
-rw-r--r--builtin/for-each-ref.c93
-rw-r--r--builtin/fsck.c9
-rw-r--r--builtin/gc.c2
-rw-r--r--builtin/grep.c187
-rw-r--r--builtin/hash-object.c5
-rw-r--r--builtin/index-pack.c266
-rw-r--r--builtin/init-db.c14
-rw-r--r--builtin/log.c194
-rw-r--r--builtin/ls-files.c57
-rw-r--r--builtin/ls-remote.c14
-rw-r--r--builtin/ls-tree.c15
-rw-r--r--builtin/mailinfo.c2
-rw-r--r--builtin/merge-base.c3
-rw-r--r--builtin/merge-tree.c3
-rw-r--r--builtin/merge.c376
-rw-r--r--builtin/mktag.c4
-rw-r--r--builtin/mktree.c1
-rw-r--r--builtin/mv.c12
-rw-r--r--builtin/name-rev.c6
-rw-r--r--builtin/notes.c94
-rw-r--r--builtin/pack-objects.c285
-rw-r--r--builtin/patch-id.c10
-rw-r--r--builtin/push.c19
-rw-r--r--builtin/read-tree.c3
-rw-r--r--builtin/receive-pack.c221
-rw-r--r--builtin/reflog.c3
-rw-r--r--builtin/remote.c59
-rw-r--r--builtin/replace.c2
-rw-r--r--builtin/rerere.c77
-rw-r--r--builtin/reset.c51
-rw-r--r--builtin/rev-list.c40
-rw-r--r--builtin/rev-parse.c9
-rw-r--r--builtin/revert.c923
-rw-r--r--builtin/send-pack.c18
-rw-r--r--builtin/shortlog.c9
-rw-r--r--builtin/show-branch.c28
-rw-r--r--builtin/show-ref.c2
-rw-r--r--builtin/stripspace.c4
-rw-r--r--builtin/tag.c114
-rw-r--r--builtin/unpack-objects.c2
-rw-r--r--builtin/update-index.c5
-rw-r--r--builtin/update-ref.c2
-rw-r--r--builtin/upload-archive.c2
-rw-r--r--builtin/verify-pack.c132
67 files changed, 3214 insertions, 1648 deletions
diff --git a/builtin/add.c b/builtin/add.c
index d39a6ab930..c59b0c98fe 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -26,6 +26,27 @@ struct update_callback_data {
int add_errors;
};
+static int fix_unmerged_status(struct diff_filepair *p,
+ struct update_callback_data *data)
+{
+ if (p->status != DIFF_STATUS_UNMERGED)
+ return p->status;
+ if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
+ /*
+ * This is not an explicit add request, and the
+ * path is missing from the working tree (deleted)
+ */
+ return DIFF_STATUS_DELETED;
+ else
+ /*
+ * Either an explicit add request, or path exists
+ * in the working tree. An attempt to explicitly
+ * add a path that does not exist in the working tree
+ * will be caught as an error by the caller immediately.
+ */
+ return DIFF_STATUS_MODIFIED;
+}
+
static void update_callback(struct diff_queue_struct *q,
struct diff_options *opt, void *cbdata)
{
@@ -35,30 +56,9 @@ static void update_callback(struct diff_queue_struct *q,
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
const char *path = p->one->path;
- switch (p->status) {
+ switch (fix_unmerged_status(p, data)) {
default:
die(_("unexpected diff status %c"), p->status);
- case DIFF_STATUS_UNMERGED:
- /*
- * ADD_CACHE_IGNORE_REMOVAL is unset if "git
- * add -u" is calling us, In such a case, a
- * missing work tree file needs to be removed
- * if there is an unmerged entry at stage #2,
- * but such a diff record is followed by
- * another with DIFF_STATUS_DELETED (and if
- * there is no stage #2, we won't see DELETED
- * nor MODIFIED). We can simply continue
- * either way.
- */
- if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
- continue;
- /*
- * Otherwise, it is "git add path" is asking
- * to explicitly add it; we fall through. A
- * missing work tree file is an error and is
- * caught by add_file_to_index() in such a
- * case.
- */
case DIFF_STATUS_MODIFIED:
case DIFF_STATUS_TYPE_CHANGED:
if (add_file_to_index(&the_index, path, data->flags)) {
@@ -91,6 +91,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
data.flags = flags;
data.add_errors = 0;
rev.diffopt.format_callback_data = &data;
+ rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
return !!data.add_errors;
}
@@ -241,7 +242,7 @@ int run_add_interactive(const char *revision, const char *patch_mode,
return status;
}
-int interactive_add(int argc, const char **argv, const char *prefix)
+int interactive_add(int argc, const char **argv, const char *prefix, int patch)
{
const char **pathspec = NULL;
@@ -252,7 +253,7 @@ int interactive_add(int argc, const char **argv, const char *prefix)
}
return run_add_interactive(NULL,
- patch_interactive ? "--patch" : NULL,
+ patch ? "--patch" : NULL,
pathspec);
}
@@ -330,8 +331,8 @@ static struct option builtin_add_options[] = {
static int add_config(const char *var, const char *value, void *cb)
{
- if (!strcasecmp(var, "add.ignoreerrors") ||
- !strcasecmp(var, "add.ignore-errors")) {
+ if (!strcmp(var, "add.ignoreerrors") ||
+ !strcmp(var, "add.ignore-errors")) {
ignore_add_errors = git_config_bool(var, value);
return 0;
}
@@ -377,7 +378,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (patch_interactive)
add_interactive = 1;
if (add_interactive)
- exit(interactive_add(argc - 1, argv + 1, prefix));
+ exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
if (edit_interactive)
return(edit_patch(argc, argv, prefix));
diff --git a/builtin/apply.c b/builtin/apply.c
index 36e150768b..c24dc546d0 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -43,6 +43,7 @@ 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 const char *fake_ancestor;
static int line_termination = '\n';
@@ -249,9 +250,6 @@ static int fuzzy_matchlines(const char *s1, size_t n1,
const char *last2 = s2 + n2 - 1;
int result = 0;
- if (n1 < 0 || n2 < 0)
- return 0;
-
/* ignore line endings */
while ((*last1 == '\r') || (*last1 == '\n'))
last1--;
@@ -1406,6 +1404,9 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
"%d leading pathname components (line %d)" , p_value, linenr);
patch->old_name = patch->new_name = 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;
@@ -1633,7 +1634,7 @@ static inline int metadata_changes(struct patch *patch)
static char *inflate_it(const void *data, unsigned long size,
unsigned long inflated_size)
{
- z_stream stream;
+ git_zstream stream;
void *out;
int st;
@@ -2430,9 +2431,9 @@ static void update_image(struct image *img,
memcpy(img->line + applied_pos,
postimage->line,
postimage->nr * sizeof(*img->line));
- for (i = 0; i < postimage->nr; i++)
- img->line[applied_pos + i].flag |= LINE_PATCHED;
-
+ if (!allow_overlap)
+ for (i = 0; i < postimage->nr; i++)
+ img->line[applied_pos + i].flag |= LINE_PATCHED;
img->nr = nr;
}
@@ -2446,6 +2447,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
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;
@@ -2539,14 +2542,18 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
error("invalid start of line: '%c'", first);
return -1;
}
- if (added_blank_line)
+ 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' &&
@@ -2628,7 +2635,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
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, frag->linenr);
+ 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);
@@ -3579,15 +3587,12 @@ static int write_out_one_reject(struct patch *patch)
return -1;
}
-static int write_out_results(struct patch *list, int skipped_patch)
+static int write_out_results(struct patch *list)
{
int phase;
int errs = 0;
struct patch *l;
- if (!list && !skipped_patch)
- return error("No changes");
-
for (phase = 0; phase < 2; phase++) {
l = list;
while (l) {
@@ -3713,6 +3718,9 @@ static int apply_patch(int fd, const char *filename, int options)
offset += nr;
}
+ if (!list && !skipped_patch)
+ die("unrecognized input");
+
if (whitespace_error && (ws_error_action == die_on_ws_error))
apply = 0;
@@ -3730,7 +3738,7 @@ static int apply_patch(int fd, const char *filename, int options)
!apply_with_reject)
exit(1);
- if (apply && write_out_results(list, skipped_patch))
+ if (apply && write_out_results(list))
exit(1);
if (fake_ancestor)
@@ -3830,7 +3838,6 @@ 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 binary;
int force_apply = 0;
const char *whitespace_option = NULL;
@@ -3849,12 +3856,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
"ignore additions made by the patch"),
OPT_BOOLEAN(0, "stat", &diffstat,
"instead of applying the patch, output diffstat for the input"),
- { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
- NULL, "old option, now no-op",
- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
- { OPTION_BOOLEAN, 0, "binary", &binary,
- NULL, "old option, now no-op",
- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
+ OPT_NOOP_NOARG(0, "allow-binary-replacement"),
+ OPT_NOOP_NOARG(0, "binary"),
OPT_BOOLEAN(0, "numstat", &numstat,
"shows number of added and deleted lines in decimal notation"),
OPT_BOOLEAN(0, "summary", &summary,
@@ -3889,6 +3892,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
"don't expect at least one line of context"),
OPT_BOOLEAN(0, "reject", &apply_with_reject,
"leave the rejected hunks in corresponding *.rej files"),
+ OPT_BOOLEAN(0, "allow-overlap", &allow_overlap,
+ "allow overlapping hunks"),
OPT__VERBOSE(&apply_verbosely, "be verbose"),
OPT_BIT(0, "inaccurate-eof", &options,
"tolerate incorrectly detected missing new-line at the end of file",
diff --git a/builtin/archive.c b/builtin/archive.c
index b14eaba159..931956def9 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -24,7 +24,8 @@ static void create_output_file(const char *output_file)
}
static int run_remote_archiver(int argc, const char **argv,
- const char *remote, const char *exec)
+ const char *remote, const char *exec,
+ const char *name_hint)
{
char buf[LARGE_PACKET_MAX];
int fd[2], i, len, rv;
@@ -37,6 +38,17 @@ static int run_remote_archiver(int argc, const char **argv,
transport = transport_get(_remote, _remote->url[0]);
transport_connect(transport, "git-upload-archive", exec, fd);
+ /*
+ * Inject a fake --format field at the beginning of the
+ * arguments, with the format inferred from our output
+ * filename. This way explicit --format options can override
+ * it.
+ */
+ if (name_hint) {
+ const char *format = archive_format_from_filename(name_hint);
+ if (format)
+ packet_write(fd[1], "argument --format=%s\n", format);
+ }
for (i = 1; i < argc; i++)
packet_write(fd[1], "argument %s\n", argv[i]);
packet_flush(fd[1]);
@@ -49,6 +61,8 @@ static int run_remote_archiver(int argc, const char **argv,
if (strcmp(buf, "ACK")) {
if (len > 5 && !prefixcmp(buf, "NACK "))
die(_("git archive: NACK %s"), buf + 5);
+ if (len > 4 && !prefixcmp(buf, "ERR "))
+ die(_("remote error: %s"), buf + 4);
die(_("git archive: protocol error"));
}
@@ -63,17 +77,6 @@ static int run_remote_archiver(int argc, const char **argv,
return !!rv;
}
-static const char *format_from_name(const char *filename)
-{
- const char *ext = strrchr(filename, '.');
- if (!ext)
- return NULL;
- ext++;
- if (!strcasecmp(ext, "zip"))
- return "--format=zip";
- return NULL;
-}
-
#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \
PARSE_OPT_KEEP_ARGV0 | \
PARSE_OPT_KEEP_UNKNOWN | \
@@ -84,7 +87,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
const char *exec = "git-upload-archive";
const char *output = NULL;
const char *remote = NULL;
- const char *format_option = NULL;
struct option local_opts[] = {
OPT_STRING('o', "output", &output, "file",
"write the archive to this file"),
@@ -98,32 +100,13 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, local_opts, NULL,
PARSE_OPT_KEEP_ALL);
- if (output) {
+ if (output)
create_output_file(output);
- format_option = format_from_name(output);
- }
-
- /*
- * We have enough room in argv[] to muck it in place, because
- * --output must have been given on the original command line
- * if we get to this point, and parse_options() must have eaten
- * it, i.e. we can add back one element to the array.
- *
- * We add a fake --format option at the beginning, with the
- * format inferred from our output filename. This way explicit
- * --format options can override it, and the fake option is
- * inserted before any "--" that might have been given.
- */
- if (format_option) {
- memmove(argv + 2, argv + 1, sizeof(*argv) * argc);
- argv[1] = format_option;
- argv[++argc] = NULL;
- }
if (remote)
- return run_remote_archiver(argc, argv, remote, exec);
+ return run_remote_archiver(argc, argv, remote, exec, output);
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
- return write_archive(argc, argv, prefix, 1);
+ return write_archive(argc, argv, prefix, 1, output, 0);
}
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 5b226399e1..8d325a5179 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -4,16 +4,19 @@
#include "bisect.h"
static const char * const git_bisect_helper_usage[] = {
- "git bisect--helper --next-all",
+ "git bisect--helper --next-all [--no-checkout]",
NULL
};
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
int next_all = 0;
+ int no_checkout = 0;
struct option options[] = {
OPT_BOOLEAN(0, "next-all", &next_all,
"perform 'git bisect next'"),
+ OPT_BOOLEAN(0, "no-checkout", &no_checkout,
+ "update BISECT_HEAD instead of checking out the current commit"),
OPT_END()
};
@@ -24,5 +27,5 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
usage_with_options(git_bisect_helper_usage, options);
/* next-all */
- return bisect_next_all(prefix);
+ return bisect_next_all(prefix, no_checkout);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index f6b03f750a..5a67c202f0 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -41,6 +41,7 @@ static int reverse;
static int blank_boundary;
static int incremental;
static int xdl_opts;
+static int abbrev = -1;
static enum date_mode blame_date_mode = DATE_ISO8601;
static size_t blame_date_width;
@@ -1377,7 +1378,7 @@ static void get_ac_line(const char *inbuf, const char *what,
timepos = tmp;
*tmp = 0;
- while (person < tmp && *tmp != ' ')
+ while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
tmp--;
if (tmp <= person)
return;
@@ -1483,13 +1484,14 @@ static void write_filename_info(const char *path)
/*
* Porcelain/Incremental format wants to show a lot of details per
* commit. Instead of repeating this every line, emit it only once,
- * the first time each commit appears in the output.
+ * the first time each commit appears in the output (unless the
+ * user has specifically asked for us to repeat).
*/
-static int emit_one_suspect_detail(struct origin *suspect)
+static int emit_one_suspect_detail(struct origin *suspect, int repeat)
{
struct commit_info ci;
- if (suspect->commit->object.flags & METAINFO_SHOWN)
+ if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
return 0;
suspect->commit->object.flags |= METAINFO_SHOWN;
@@ -1528,7 +1530,7 @@ static void found_guilty_entry(struct blame_entry *ent)
printf("%s %d %d %d\n",
sha1_to_hex(suspect->commit->object.sha1),
ent->s_lno + 1, ent->lno + 1, ent->num_lines);
- emit_one_suspect_detail(suspect);
+ emit_one_suspect_detail(suspect, 0);
write_filename_info(suspect->path);
maybe_flush_or_die(stdout, "stdout");
}
@@ -1596,7 +1598,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
int tz;
if (show_raw_time) {
- sprintf(time_buf, "%lu %s", time, tz_str);
+ snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
}
else {
tz = atoi(tz_str);
@@ -1617,9 +1619,19 @@ static const char *format_time(unsigned long time, const char *tz_str,
#define OUTPUT_SHOW_SCORE 0100
#define OUTPUT_NO_AUTHOR 0200
#define OUTPUT_SHOW_EMAIL 0400
+#define OUTPUT_LINE_PORCELAIN 01000
+
+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);
+}
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
+static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+ int opt)
{
+ int repeat = opt & OUTPUT_LINE_PORCELAIN;
int cnt;
const char *cp;
struct origin *suspect = ent->suspect;
@@ -1632,17 +1644,18 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
ent->s_lno + 1,
ent->lno + 1,
ent->num_lines);
- if (emit_one_suspect_detail(suspect) ||
- (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
- write_filename_info(suspect->path);
+ emit_porcelain_details(suspect, repeat);
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
- if (cnt)
+ if (cnt) {
printf("%s %d %d\n", hex,
ent->s_lno + 1 + cnt,
ent->lno + 1 + cnt);
+ if (repeat)
+ emit_porcelain_details(suspect, 1);
+ }
putchar('\t');
do {
ch = *cp++;
@@ -1670,7 +1683,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
- int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
+ int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : abbrev;
if (suspect->commit->object.flags & UNINTERESTING) {
if (blank_boundary)
@@ -1755,7 +1768,7 @@ static void output(struct scoreboard *sb, int option)
for (ent = sb->ent; ent; ent = ent->next) {
if (option & OUTPUT_PORCELAIN)
- emit_porcelain(sb, ent);
+ emit_porcelain(sb, ent, option);
else {
emit_other(sb, ent, option);
}
@@ -2083,6 +2096,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
+ char *buf_ptr;
unsigned long buf_len;
if (contents_from) {
@@ -2100,8 +2114,8 @@ 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, &buf.buf, &buf_len))
- buf.len = buf_len;
+ textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+ strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
break;
@@ -2299,6 +2313,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
+ OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
@@ -2310,6 +2325,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
+ OPT__ABBREV(&abbrev),
OPT_END()
};
@@ -2345,6 +2361,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
parse_done:
argc = parse_options_end(&ctx);
+ if (abbrev == -1)
+ abbrev = default_abbrev;
+ /* one more abbrev length is needed for the boundary commit */
+ abbrev++;
+
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);
diff --git a/builtin/branch.c b/builtin/branch.c
index 9cca1b9afc..df908ed8f5 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -19,7 +19,7 @@
static const char * const builtin_branch_usage[] = {
"git branch [options] [-r | -a] [--merged | --no-merged]",
"git branch [options] [-l] [-f] <branchname> [<start-point>]",
- "git branch [options] [-r] (-d | -D) <branchname>",
+ "git branch [options] [-r] (-d | -D) <branchname>...",
"git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
NULL
};
@@ -71,7 +71,7 @@ static int parse_branch_color_slot(const char *var, int ofs)
static int git_branch_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "color.branch")) {
- branch_use_color = git_config_colorbool(var, value, -1);
+ branch_use_color = git_config_colorbool(var, value);
return 0;
}
if (!prefixcmp(var, "color.branch.")) {
@@ -88,7 +88,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
static const char *branch_get_color(enum color_branch ix)
{
- if (branch_use_color > 0)
+ if (want_color(branch_use_color))
return branch_colors[ix];
return "";
}
@@ -260,9 +260,22 @@ static char *resolve_symref(const char *src, const char *prefix)
struct append_ref_cb {
struct ref_list *ref_list;
+ const char **pattern;
int ret;
};
+static int match_patterns(const char **pattern, const char *refname)
+{
+ if (!*pattern)
+ return 1; /* no pattern always matches */
+ while (*pattern) {
+ if (!fnmatch(*pattern, refname, 0))
+ return 1;
+ pattern++;
+ }
+ return 0;
+}
+
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
@@ -297,6 +310,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
if ((kind & ref_list->kinds) == 0)
return 0;
+ if (!match_patterns(cb->pattern, refname))
+ return 0;
+
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
commit = lookup_commit_reference_gently(sha1, 1);
@@ -399,9 +415,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
struct commit *commit = item->commit;
if (commit && !parse_commit(commit)) {
- struct pretty_print_context ctx = {0};
- pretty_print_commit(CMIT_FMT_ONELINE, commit,
- &subject, &ctx);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
sub = subject.buf;
}
@@ -494,7 +508,7 @@ static void show_detached(struct ref_list *ref_list)
}
}
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
{
int i;
struct append_ref_cb cb;
@@ -508,6 +522,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
if (merge_filter != NO_FILTER)
init_revisions(&ref_list.revs, NULL);
cb.ref_list = &ref_list;
+ cb.pattern = pattern;
cb.ret = 0;
for_each_rawref(append_ref, &cb);
if (merge_filter != NO_FILTER) {
@@ -525,7 +540,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached)
+ if (detached && match_patterns(pattern, "HEAD"))
show_detached(&ref_list);
for (i = 0; i < ref_list.index; i++) {
@@ -553,6 +568,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)
unsigned char sha1[20];
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
int recovery = 0;
+ int clobber_head_ok;
if (!oldname)
die(_("cannot rename the current branch while not on any."));
@@ -568,11 +584,13 @@ static void rename_branch(const char *oldname, const char *newname, int force)
die(_("Invalid branch name: '%s'"), oldname);
}
- if (strbuf_check_branch_ref(&newref, newname))
- die(_("Invalid branch name: '%s'"), newname);
+ /*
+ * A command like "git branch -M currentbranch currentbranch" cannot
+ * cause the worktree to become inconsistent with HEAD, so allow it.
+ */
+ clobber_head_ok = !strcmp(oldname, newname);
- if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
- die(_("A branch named '%s' already exists."), newref.buf + 11);
+ validate_new_branchname(newname, &newref, force, clobber_head_ok);
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
@@ -614,8 +632,8 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
int cmd_branch(int argc, const char **argv, const char *prefix)
{
- int delete = 0, rename = 0, force_create = 0;
- int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
+ int delete = 0, rename = 0, force_create = 0, list = 0;
+ int verbose = 0, abbrev = -1, detached = 0;
int reflog = 0;
enum branch_track track;
int kinds = REF_LOCAL_BRANCH;
@@ -630,7 +648,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_SET_INT( 0, "set-upstream", &track, "change upstream info",
BRANCH_TRACK_OVERRIDE),
OPT__COLOR(&branch_use_color, "use colored output"),
- OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
+ OPT_SET_INT('r', "remotes", &kinds, "act on remote-tracking branches",
REF_REMOTE_BRANCH),
{
OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
@@ -647,13 +665,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT__ABBREV(&abbrev),
OPT_GROUP("Specific git-branch actions:"),
- OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
+ OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
- OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1),
+ OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1),
OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
- OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
+ OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
- OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
+ OPT_BOOLEAN(0, "list", &list, "list branch names"),
+ OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
OPT__FORCE(&force_create, "force creation (when already exists)"),
{
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
@@ -675,9 +694,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
git_config(git_branch_config, NULL);
- if (branch_use_color == -1)
- branch_use_color = git_use_color_default;
-
track = git_branch_track;
head = resolve_ref("HEAD", head_sha1, 0, NULL);
@@ -695,22 +711,33 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
- if (!!delete + !!rename + !!force_create > 1)
+
+ if (!delete && !rename && argc == 0)
+ list = 1;
+
+ if (!!delete + !!rename + !!force_create + !!list > 1)
usage_with_options(builtin_branch_usage, options);
+ if (abbrev == -1)
+ abbrev = DEFAULT_ABBREV;
+
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
- else if (argc == 0)
- return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
- else if (rename && (argc == 1))
- rename_branch(head, argv[0], rename > 1);
- else if (rename && (argc == 2))
- rename_branch(argv[0], argv[1], rename > 1);
- else if (argc <= 2) {
+ else if (list)
+ return print_ref_list(kinds, detached, verbose, abbrev,
+ with_commit, argv);
+ else if (rename) {
+ if (argc == 1)
+ rename_branch(head, argv[0], rename > 1);
+ else if (argc == 2)
+ rename_branch(argv[0], argv[1], rename > 1);
+ else
+ usage_with_options(builtin_branch_usage, options);
+ } else if (argc > 0 && argc <= 2) {
if (kinds != REF_LOCAL_BRANCH)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
- force_create, reflog, track);
+ force_create, reflog, 0, track);
} else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 81046a9cb8..92a8a6026a 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -58,7 +58,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
} else if (!strcmp(cmd, "unbundle")) {
if (!startup_info->have_repository)
die(_("Need a repository to unbundle."));
- return !!unbundle(&header, bundle_fd) ||
+ return !!unbundle(&header, bundle_fd, 0) ||
list_bundle_refs(&header, argc, argv);
} else
usage(builtin_bundle_usage);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 94632dbdb4..07bd984084 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -187,6 +187,8 @@ static int batch_one_object(const char *obj_name, int print_contents)
if (type <= 0) {
printf("%s missing\n", obj_name);
fflush(stdout);
+ if (print_contents == BATCH)
+ free(contents);
return 0;
}
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 3016d29caa..44c421eb0f 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -4,28 +4,30 @@
#include "quote.h"
#include "parse-options.h"
+static int all_attrs;
+static int cached_attrs;
static int stdin_paths;
static const char * const check_attr_usage[] = {
-"git check-attr attr... [--] pathname...",
-"git check-attr --stdin attr... < <list-of-paths>",
+"git check-attr [-a | --all | attr...] [--] pathname...",
+"git check-attr --stdin [-a | --all | attr...] < <list-of-paths>",
NULL
};
static int null_term_line;
static const struct option check_attr_options[] = {
+ OPT_BOOLEAN('a', "all", &all_attrs, "report all attributes set on file"),
+ OPT_BOOLEAN(0, "cached", &cached_attrs, "use .gitattributes only from the index"),
OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
OPT_BOOLEAN('z', NULL, &null_term_line,
"input paths are terminated by a null character"),
OPT_END()
};
-static void check_attr(int cnt, struct git_attr_check *check,
- const char** name, const char *file)
+static void output_attr(int cnt, struct git_attr_check *check,
+ const char *file)
{
int j;
- if (git_checkattr(file, cnt, check))
- die("git_checkattr died");
for (j = 0; j < cnt; j++) {
const char *value = check[j].value;
@@ -37,12 +39,30 @@ static void check_attr(int cnt, struct git_attr_check *check,
value = "unspecified";
quote_c_style(file, NULL, stdout, 0);
- printf(": %s: %s\n", name[j], value);
+ printf(": %s: %s\n", git_attr_name(check[j].attr), value);
}
}
-static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
- const char** name)
+static void check_attr(const char *prefix, int cnt,
+ struct git_attr_check *check, 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);
+ } else {
+ if (git_all_attrs(full_path, &cnt, &check))
+ die("git_all_attrs died");
+ output_attr(cnt, check, file);
+ free(check);
+ }
+ free(full_path);
+}
+
+static void check_attr_stdin_paths(const char *prefix, int cnt,
+ struct git_attr_check *check)
{
struct strbuf buf, nbuf;
int line_termination = null_term_line ? 0 : '\n';
@@ -56,67 +76,99 @@ static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
die("line is badly quoted");
strbuf_swap(&buf, &nbuf);
}
- check_attr(cnt, check, name, buf.buf);
+ check_attr(prefix, cnt, check, buf.buf);
maybe_flush_or_die(stdout, "attribute to stdout");
}
strbuf_release(&buf);
strbuf_release(&nbuf);
}
+static NORETURN void error_with_usage(const char *msg)
+{
+ error("%s", msg);
+ usage_with_options(check_attr_usage, check_attr_options);
+}
+
int cmd_check_attr(int argc, const char **argv, const char *prefix)
{
struct git_attr_check *check;
- int cnt, i, doubledash;
- const char *errstr = NULL;
+ int cnt, i, doubledash, filei;
+
+ git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, check_attr_options,
check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
- if (!argc)
- usage_with_options(check_attr_usage, check_attr_options);
if (read_cache() < 0) {
die("invalid cache");
}
+ if (cached_attrs)
+ git_attr_set_direction(GIT_ATTR_INDEX, NULL);
+
doubledash = -1;
for (i = 0; doubledash < 0 && i < argc; i++) {
if (!strcmp(argv[i], "--"))
doubledash = i;
}
- /* If there is no double dash, we handle only one attribute */
- if (doubledash < 0) {
- cnt = 1;
- doubledash = 0;
- } else
+ /* Process --all and/or attribute arguments: */
+ if (all_attrs) {
+ if (doubledash >= 1)
+ error_with_usage("Attributes and --all both specified");
+
+ cnt = 0;
+ filei = doubledash + 1;
+ } else if (doubledash == 0) {
+ error_with_usage("No attribute specified");
+ } else if (doubledash < 0) {
+ if (!argc)
+ error_with_usage("No attribute specified");
+
+ if (stdin_paths) {
+ /* Treat all arguments as attribute names. */
+ cnt = argc;
+ filei = argc;
+ } else {
+ /* Treat exactly one argument as an attribute name. */
+ cnt = 1;
+ filei = 1;
+ }
+ } else {
cnt = doubledash;
- doubledash++;
-
- if (cnt <= 0)
- errstr = "No attribute specified";
- else if (stdin_paths && doubledash < argc)
- errstr = "Can't specify files with --stdin";
- if (errstr) {
- error("%s", errstr);
- usage_with_options(check_attr_usage, check_attr_options);
+ filei = doubledash + 1;
}
- check = xcalloc(cnt, sizeof(*check));
- for (i = 0; i < cnt; i++) {
- const char *name;
- struct git_attr *a;
- name = argv[i];
- a = git_attr(name);
- if (!a)
- return error("%s: not a valid attribute name", name);
- check[i].attr = a;
+ /* Check file argument(s): */
+ if (stdin_paths) {
+ if (filei < argc)
+ error_with_usage("Can't specify files with --stdin");
+ } else {
+ if (filei >= argc)
+ error_with_usage("No file specified");
+ }
+
+ if (all_attrs) {
+ check = NULL;
+ } else {
+ check = xcalloc(cnt, sizeof(*check));
+ for (i = 0; i < cnt; i++) {
+ const char *name;
+ struct git_attr *a;
+ name = argv[i];
+ a = git_attr(name);
+ if (!a)
+ return error("%s: not a valid attribute name",
+ name);
+ check[i].attr = a;
+ }
}
if (stdin_paths)
- check_attr_stdin_paths(cnt, check, argv);
+ check_attr_stdin_paths(prefix, cnt, check);
else {
- for (i = doubledash; i < argc; i++)
- check_attr(cnt, check, argv, argv[i]);
+ for (i = filei; i < argc; i++)
+ check_attr(prefix, cnt, check, argv[i]);
maybe_flush_or_die(stdout, "attribute to stdout");
}
return 0;
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index ae3f28115a..28a7320271 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -8,29 +8,32 @@
#include "strbuf.h"
static const char builtin_check_ref_format_usage[] =
-"git check-ref-format [--print] <refname>\n"
+"git check-ref-format [--normalize] [options] <refname>\n"
" or: git check-ref-format --branch <branchname-shorthand>";
/*
- * Replace each run of adjacent slashes in src with a single slash,
- * and write the result to dst.
+ * Return a copy of refname but with leading slashes removed and runs
+ * of adjacent slashes replaced with single slashes.
*
* This function is similar to normalize_path_copy(), but stripped down
* to meet check_ref_format's simpler needs.
*/
-static void collapse_slashes(char *dst, const char *src)
+static char *collapse_slashes(const char *refname)
{
+ char *ret = xmalloc(strlen(refname) + 1);
char ch;
- char prev = '\0';
+ char prev = '/';
+ char *cp = ret;
- while ((ch = *src++) != '\0') {
+ while ((ch = *refname++) != '\0') {
if (prev == '/' && ch == prev)
continue;
- *dst++ = ch;
+ *cp++ = ch;
prev = ch;
}
- *dst = '\0';
+ *cp = '\0';
+ return ret;
}
static int check_ref_format_branch(const char *arg)
@@ -45,27 +48,41 @@ static int check_ref_format_branch(const char *arg)
return 0;
}
-static int check_ref_format_print(const char *arg)
-{
- char *refname = xmalloc(strlen(arg) + 1);
-
- if (check_ref_format(arg))
- return 1;
- collapse_slashes(refname, arg);
- printf("%s\n", refname);
- return 0;
-}
-
int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
{
+ int i;
+ int normalize = 0;
+ int flags = 0;
+ const char *refname;
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_check_ref_format_usage);
if (argc == 3 && !strcmp(argv[1], "--branch"))
return check_ref_format_branch(argv[2]);
- if (argc == 3 && !strcmp(argv[1], "--print"))
- return check_ref_format_print(argv[2]);
- if (argc != 2)
+
+ for (i = 1; i < argc && argv[i][0] == '-'; i++) {
+ if (!strcmp(argv[i], "--normalize") || !strcmp(argv[i], "--print"))
+ normalize = 1;
+ else if (!strcmp(argv[i], "--allow-onelevel"))
+ flags |= REFNAME_ALLOW_ONELEVEL;
+ else if (!strcmp(argv[i], "--no-allow-onelevel"))
+ flags &= ~REFNAME_ALLOW_ONELEVEL;
+ else if (!strcmp(argv[i], "--refspec-pattern"))
+ flags |= REFNAME_REFSPEC_PATTERN;
+ else
+ usage(builtin_check_ref_format_usage);
+ }
+ if (! (i == argc - 1))
usage(builtin_check_ref_format_usage);
- return !!check_ref_format(argv[1]);
+
+ refname = argv[i];
+ if (normalize)
+ refname = collapse_slashes(refname);
+ if (check_refname_format(refname, flags))
+ return 1;
+ if (normalize)
+ printf("%s\n", refname);
+
+ return 0;
}
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index f1fec24745..c16d82b7de 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -3,38 +3,6 @@
*
* Copyright (C) 2005 Linus Torvalds
*
- * Careful: order of argument flags does matter. For example,
- *
- * git checkout-index -a -f file.c
- *
- * Will first check out all files listed in the cache (but not
- * overwrite any old ones), and then force-checkout "file.c" a
- * second time (ie that one _will_ overwrite any old contents
- * with the same filename).
- *
- * Also, just doing "git checkout-index" does nothing. You probably
- * meant "git checkout-index -a". And if you want to force it, you
- * want "git checkout-index -f -a".
- *
- * Intuitiveness is not the goal here. Repeatability is. The
- * reason for the "no arguments means no work" thing is that
- * from scripts you are supposed to be able to do things like
- *
- * find . -name '*.h' -print0 | xargs -0 git checkout-index -f --
- *
- * or:
- *
- * find . -name '*.h' -print0 | git checkout-index -f -z --stdin
- *
- * which will force all existing *.h files to be replaced with
- * their cached copies. If an empty command line implied "all",
- * then this would force-refresh everything in the cache, which
- * was not the point.
- *
- * Oh, and the "--" is just a good idea when you know the rest
- * will be filenames. Just so that you wouldn't have a filename
- * of "-a" causing problems (not possible in the above example,
- * but get used to it in scripting!).
*/
#include "builtin.h"
#include "cache.h"
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 064e7a14c1..e79003f5bf 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -19,6 +19,7 @@
#include "ll-merge.h"
#include "resolve-undo.h"
#include "submodule.h"
+#include "argv-array.h"
static const char * const checkout_usage[] = {
"git checkout [options] <branch>",
@@ -71,7 +72,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
hashcpy(ce->sha1, sha1);
memcpy(ce->name, base, baselen);
memcpy(ce->name + baselen, pathname, len - baselen);
- ce->ce_flags = create_ce_flags(len, 0);
+ ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE;
ce->ce_mode = create_ce_mode(mode);
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
return 0;
@@ -79,7 +80,10 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
static int read_tree_some(struct tree *tree, const char **pathspec)
{
- read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
+ struct pathspec ps;
+ init_pathspec(&ps, pathspec);
+ read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
+ free_pathspec(&ps);
/* update the index with the given tree's info
* for all args, expanding wildcards, and exit
@@ -210,7 +214,7 @@ static int checkout_merged(int pos, struct checkout *state)
}
static int checkout_paths(struct tree *source_tree, const char **pathspec,
- struct checkout_opts *opts)
+ const char *prefix, struct checkout_opts *opts)
{
int pos;
struct checkout state;
@@ -237,10 +241,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
+ if (source_tree && !(ce->ce_flags & CE_UPDATE))
+ continue;
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
}
- if (report_path_error(ps_matched, pathspec, 0))
+ if (report_path_error(ps_matched, pathspec, prefix))
return 1;
/* "checkout -m path" to recreate conflicted state */
@@ -275,6 +281,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
state.refresh_cache = 1;
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
+ if (source_tree && !(ce->ce_flags & CE_UPDATE))
+ continue;
if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
if (!ce_stage(ce)) {
errs |= checkout_entry(ce, &state, NULL);
@@ -315,9 +323,8 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
static void describe_detached_head(const char *msg, struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
- struct pretty_print_context ctx = {0};
parse_commit(commit);
- pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
fprintf(stderr, "%s %s... %s\n", msg,
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
strbuf_release(&sb);
@@ -416,7 +423,7 @@ static int merge_working_tree(struct checkout_opts *opts,
topts.fn = twoway_merge;
topts.dir = xcalloc(1, sizeof(*topts.dir));
topts.dir->flags |= DIR_SHOW_IGNORED;
- topts.dir->exclude_per_dir = ".gitignore";
+ setup_standard_excludes(topts.dir);
tree = parse_tree_indirect(old->commit ?
old->commit->object.sha1 :
EMPTY_TREE_SHA1_BIN);
@@ -545,7 +552,9 @@ static void update_refs_for_switch(struct checkout_opts *opts,
else
create_branch(old->name, opts->new_branch, new->name,
opts->new_branch_force ? 1 : 0,
- opts->new_branch_log, opts->track);
+ opts->new_branch_log,
+ opts->new_branch_force ? 1 : 0,
+ opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
@@ -570,8 +579,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
create_symref("HEAD", new->path, msg.buf);
if (!opts->quiet) {
if (old->path && !strcmp(new->path, old->path)) {
- fprintf(stderr, _("Already on '%s'\n"),
- new->name);
+ if (opts->new_branch_force)
+ fprintf(stderr, _("Reset branch '%s'\n"),
+ new->name);
+ else
+ fprintf(stderr, _("Already on '%s'\n"),
+ new->name);
} else if (opts->new_branch) {
if (opts->branch_exists)
fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
@@ -598,48 +611,22 @@ static void update_refs_for_switch(struct checkout_opts *opts,
report_tracking(new);
}
-struct rev_list_args {
- int argc;
- int alloc;
- const char **argv;
-};
-
-static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
+static int add_pending_uninteresting_ref(const char *refname,
+ const unsigned char *sha1,
+ int flags, void *cb_data)
{
- ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
- args->argv[args->argc++] = s;
-}
-
-static int add_one_ref_to_rev_list_arg(const char *refname,
- const unsigned char *sha1,
- int flags,
- void *cb_data)
-{
- add_one_rev_list_arg(cb_data, refname);
- return 0;
-}
-
-static int clear_commit_marks_from_one_ref(const char *refname,
- const unsigned char *sha1,
- int flags,
- void *cb_data)
-{
- struct commit *commit = lookup_commit_reference_gently(sha1, 1);
- if (commit)
- clear_commit_marks(commit, -1);
+ add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
return 0;
}
static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
{
- struct pretty_print_context ctx = { 0 };
-
parse_commit(commit);
strbuf_addstr(sb, " ");
strbuf_addstr(sb,
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
strbuf_addch(sb, ' ');
- pretty_print_commit(CMIT_FMT_ONELINE, commit, sb, &ctx);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
strbuf_addch(sb, '\n');
}
@@ -660,21 +647,34 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
if (more == 1)
describe_one_orphan(&sb, last);
else
- strbuf_addf(&sb, " ... and %d more.\n", more);
+ strbuf_addf(&sb, _(" ... and %d more.\n"), more);
}
fprintf(stderr,
- "Warning: you are leaving %d commit%s behind, "
+ Q_(
+ /* The singular version */
+ "Warning: you are leaving %d commit behind, "
"not connected to\n"
"any of your branches:\n\n"
- "%s\n"
- "If you want to keep them by creating a new branch, "
- "this may be a good time\nto do so with:\n\n"
- " git branch new_branch_name %s\n\n",
- lost, ((1 < lost) ? "s" : ""),
- sb.buf,
- sha1_to_hex(commit->object.sha1));
+ "%s\n",
+ /* The plural version */
+ "Warning: you are leaving %d commits behind, "
+ "not connected to\n"
+ "any of your branches:\n\n"
+ "%s\n",
+ /* Give ngettext() the count */
+ lost),
+ lost,
+ sb.buf);
strbuf_release(&sb);
+
+ if (advice_detached_head)
+ fprintf(stderr,
+ _(
+ "If you want to keep them by creating a new branch, "
+ "this may be a good time\nto do so with:\n\n"
+ " git branch new_branch_name %s\n\n"),
+ sha1_to_hex(commit->object.sha1));
}
/*
@@ -684,19 +684,21 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
*/
static void orphaned_commit_warning(struct commit *commit)
{
- struct rev_list_args args = { 0, 0, NULL };
struct rev_info revs;
-
- add_one_rev_list_arg(&args, "(internal)");
- add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
- add_one_rev_list_arg(&args, "--not");
- for_each_ref(add_one_ref_to_rev_list_arg, &args);
- add_one_rev_list_arg(&args, "--");
- add_one_rev_list_arg(&args, NULL);
+ struct object *object = &commit->object;
+ struct object_array refs;
init_revisions(&revs, NULL);
- if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
- die(_("internal error: only -- alone should have been left"));
+ setup_revisions(0, NULL, &revs, NULL);
+
+ object->flags &= ~UNINTERESTING;
+ add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+
+ for_each_ref(add_pending_uninteresting_ref, &revs);
+
+ refs = revs.pending;
+ revs.leak_pending = 1;
+
if (prepare_revision_walk(&revs))
die(_("internal error in revision walk"));
if (!(commit->object.flags & UNINTERESTING))
@@ -704,8 +706,8 @@ static void orphaned_commit_warning(struct commit *commit)
else
describe_detached_head(_("Previous HEAD position was"), commit);
- clear_commit_marks(commit, -1);
- for_each_ref(clear_commit_marks_from_one_ref, NULL);
+ clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
+ free(refs.objects);
}
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
@@ -715,10 +717,12 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
unsigned char rev[20];
int flag;
memset(&old, 0, sizeof(old));
- old.path = resolve_ref("HEAD", rev, 0, &flag);
+ old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
old.commit = lookup_commit_reference_gently(rev, 1);
- if (!(flag & REF_ISSYMREF))
+ if (!(flag & REF_ISSYMREF)) {
+ free((char *)old.path);
old.path = NULL;
+ }
if (old.path && !prefixcmp(old.path, "refs/heads/"))
old.name = old.path + strlen("refs/heads/");
@@ -741,6 +745,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
update_refs_for_switch(opts, &old, new);
ret = post_checkout_hook(old.commit, new->commit, 1);
+ free((char *)old.path);
return ret || opts->writeout_error;
}
@@ -878,7 +883,7 @@ static int parse_branchname_arg(int argc, const char **argv,
new->name = arg;
setup_branch_path(new);
- if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+ if (!check_refname_format(new->path, 0) &&
resolve_ref(new->path, branch_rev, 1, NULL))
hashcpy(rev, branch_rev);
else
@@ -973,9 +978,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
die (_("--patch is incompatible with all other options"));
if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
- die("--detach cannot be used with -b/-B/--orphan");
+ die(_("--detach cannot be used with -b/-B/--orphan"));
if (opts.force_detach && 0 < opts.track)
- die("--detach cannot be used with -t");
+ die(_("--detach cannot be used with -t"));
/* --track without -b should DWIM */
if (0 < opts.track && !opts.new_branch) {
@@ -1055,12 +1060,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
}
if (opts.force_detach)
- die("git checkout: --detach does not take a path argument");
+ die(_("git checkout: --detach does not take a path argument"));
if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
- return checkout_paths(source_tree, pathspec, &opts);
+ return checkout_paths(source_tree, pathspec, prefix, &opts);
}
if (patch_mode)
@@ -1068,15 +1073,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT;
- if (strbuf_check_branch_ref(&buf, opts.new_branch))
- die(_("git checkout: we do not like '%s' as a branch name."),
- opts.new_branch);
- if (!get_sha1(buf.buf, rev)) {
- opts.branch_exists = 1;
- if (!opts.new_branch_force)
- die(_("git checkout: branch %s already exists"),
- opts.new_branch);
- }
+
+ opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
+ !!opts.new_branch_force,
+ !!opts.new_branch_force);
+
strbuf_release(&buf);
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 75697f7111..0c7b3d0f4c 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -54,7 +54,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('d', NULL, &remove_directories,
"remove whole directories"),
{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
- "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
+ "add <pattern> to ignore rules", PARSE_OPT_NONEG, exclude_cb },
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
OPT_BOOLEAN('X', NULL, &ignored_only,
"remove only ignored files"),
@@ -98,7 +98,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
setup_standard_excludes(&dir);
for (i = 0; i < exclude_list.nr; i++)
- add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
+ add_exclude(exclude_list.items[i].string, "", 0,
+ &dir.exclude_list[EXC_CMDL]);
pathspec = get_pathspec(prefix, argv);
diff --git a/builtin/clone.c b/builtin/clone.c
index 4144bcf5ca..86db954730 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -39,13 +39,24 @@ static const char * const builtin_clone_usage[] = {
static int option_no_checkout, option_bare, option_mirror;
static int option_local, option_no_hardlinks, option_shared, option_recursive;
-static char *option_template, *option_reference, *option_depth;
+static char *option_template, *option_depth;
static char *option_origin = NULL;
static char *option_branch = NULL;
static const char *real_git_dir;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress;
+static struct string_list option_config;
+static struct string_list option_reference;
+
+static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
+{
+ struct string_list *option_reference = opt->value;
+ if (!arg)
+ return -1;
+ string_list_append(option_reference, arg);
+ return 0;
+}
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
@@ -71,19 +82,20 @@ static struct option builtin_clone_options[] = {
"initialize submodules in the clone"),
OPT_STRING(0, "template", &option_template, "template-directory",
"directory from which templates will be used"),
- OPT_STRING(0, "reference", &option_reference, "repo",
- "reference repository"),
- OPT_STRING('o', "origin", &option_origin, "branch",
- "use <branch> instead of 'origin' to track upstream"),
+ OPT_CALLBACK(0 , "reference", &option_reference, "repo",
+ "reference repository", &opt_parse_reference),
+ OPT_STRING('o', "origin", &option_origin, "name",
+ "use <name> instead of 'origin' to track upstream"),
OPT_STRING('b', "branch", &option_branch, "branch",
"checkout <branch> instead of the remote's HEAD"),
OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
"path to git-upload-pack on the remote"),
OPT_STRING(0, "depth", &option_depth, "depth",
"create a shallow clone of that depth"),
- OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
+ OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
"separate git dir from working tree"),
-
+ OPT_STRING_LIST('c', "config", &option_config, "key=value",
+ "set config inside the new repository"),
OPT_END()
};
@@ -101,9 +113,26 @@ static char *get_repo_path(const char *repo, int *is_bundle)
for (i = 0; i < ARRAY_SIZE(suffix); i++) {
const char *path;
path = mkpath("%s%s", repo, suffix[i]);
- if (is_directory(path)) {
+ if (stat(path, &st))
+ continue;
+ if (S_ISDIR(st.st_mode)) {
*is_bundle = 0;
return xstrdup(absolute_path(path));
+ } else if (S_ISREG(st.st_mode) && st.st_size > 8) {
+ /* Is it a "gitfile"? */
+ char signature[8];
+ int len, fd = open(path, O_RDONLY);
+ if (fd < 0)
+ continue;
+ len = read_in_full(fd, signature, 8);
+ close(fd);
+ if (len != 8 || strncmp(signature, "gitdir: ", 8))
+ continue;
+ path = read_gitfile(path);
+ if (path) {
+ *is_bundle = 0;
+ return xstrdup(absolute_path(path));
+ }
}
}
@@ -197,39 +226,80 @@ static void strip_trailing_slashes(char *dir)
*end = '\0';
}
-static void setup_reference(const char *repo)
+static int add_one_reference(struct string_list_item *item, void *cb_data)
{
- const char *ref_git;
- char *ref_git_copy;
-
+ char *ref_git;
+ struct strbuf alternate = STRBUF_INIT;
struct remote *remote;
struct transport *transport;
const struct ref *extra;
- ref_git = real_path(option_reference);
-
- if (is_directory(mkpath("%s/.git/objects", ref_git)))
- ref_git = mkpath("%s/.git", ref_git);
- else if (!is_directory(mkpath("%s/objects", ref_git)))
+ /* Beware: real_path() and mkpath() return static buffer */
+ ref_git = xstrdup(real_path(item->string));
+ if (is_directory(mkpath("%s/.git/objects", ref_git))) {
+ char *ref_git_git = xstrdup(mkpath("%s/.git", ref_git));
+ free(ref_git);
+ ref_git = ref_git_git;
+ } else if (!is_directory(mkpath("%s/objects", ref_git)))
die(_("reference repository '%s' is not a local directory."),
- option_reference);
+ item->string);
- ref_git_copy = xstrdup(ref_git);
+ strbuf_addf(&alternate, "%s/objects", ref_git);
+ add_to_alternates_file(alternate.buf);
+ strbuf_release(&alternate);
- add_to_alternates_file(ref_git_copy);
-
- remote = remote_get(ref_git_copy);
- transport = transport_get(remote, ref_git_copy);
+ remote = remote_get(ref_git);
+ transport = transport_get(remote, ref_git);
for (extra = transport_get_remote_refs(transport); extra;
extra = extra->next)
add_extra_ref(extra->name, extra->old_sha1, 0);
transport_disconnect(transport);
+ free(ref_git);
+ return 0;
+}
- free(ref_git_copy);
+static void setup_reference(void)
+{
+ for_each_string_list(&option_reference, add_one_reference, NULL);
}
-static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
+static void copy_alternates(struct strbuf *src, struct strbuf *dst,
+ const char *src_repo)
+{
+ /*
+ * Read from the source objects/info/alternates file
+ * and copy the entries to corresponding file in the
+ * destination repository with add_to_alternates_file().
+ * Both src and dst have "$path/objects/info/alternates".
+ *
+ * Instead of copying bit-for-bit from the original,
+ * we need to append to existing one so that the already
+ * created entry via "clone -s" is not lost, and also
+ * to turn entries with paths relative to the original
+ * absolute, so that they can be used in the new repository.
+ */
+ FILE *in = fopen(src->buf, "r");
+ struct strbuf line = STRBUF_INIT;
+
+ while (strbuf_getline(&line, in, '\n') != EOF) {
+ char *abs_path, abs_buf[PATH_MAX];
+ if (!line.len || line.buf[0] == '#')
+ continue;
+ if (is_absolute_path(line.buf)) {
+ add_to_alternates_file(line.buf);
+ continue;
+ }
+ abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
+ normalize_path_copy(abs_buf, abs_path);
+ add_to_alternates_file(abs_buf);
+ }
+ strbuf_release(&line);
+ fclose(in);
+}
+
+static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
+ const char *src_repo, int src_baselen)
{
struct dirent *de;
struct stat buf;
@@ -265,7 +335,14 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
}
if (S_ISDIR(buf.st_mode)) {
if (de->d_name[0] != '.')
- copy_or_link_directory(src, dest);
+ copy_or_link_directory(src, dest,
+ src_repo, src_baselen);
+ continue;
+ }
+
+ /* Files that cannot be copied bit-for-bit... */
+ if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
+ copy_alternates(src, dest, src_repo);
continue;
}
@@ -288,17 +365,20 @@ static const struct ref *clone_local(const char *src_repo,
const char *dest_repo)
{
const struct ref *ret;
- struct strbuf src = STRBUF_INIT;
- struct strbuf dest = STRBUF_INIT;
struct remote *remote;
struct transport *transport;
- if (option_shared)
- add_to_alternates_file(src_repo);
- else {
+ if (option_shared) {
+ struct strbuf alt = STRBUF_INIT;
+ strbuf_addf(&alt, "%s/objects", src_repo);
+ add_to_alternates_file(alt.buf);
+ strbuf_release(&alt);
+ } else {
+ struct strbuf src = STRBUF_INIT;
+ struct strbuf dest = STRBUF_INIT;
strbuf_addf(&src, "%s/objects", src_repo);
strbuf_addf(&dest, "%s/objects", dest_repo);
- copy_or_link_directory(&src, &dest);
+ copy_or_link_directory(&src, &dest, src_repo, src.len);
strbuf_release(&src);
strbuf_release(&dest);
}
@@ -343,8 +423,9 @@ static void remove_junk_on_signal(int signo)
static struct ref *wanted_peer_refs(const struct ref *refs,
struct refspec *refspec)
{
- struct ref *local_refs = NULL;
- struct ref **tail = &local_refs;
+ struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
+ struct ref *local_refs = head;
+ struct ref **tail = head ? &head->next : &local_refs;
get_fetch_map(refs, refspec, &tail, 0);
if (!option_mirror)
@@ -357,13 +438,32 @@ static void write_remote_refs(const struct ref *local_refs)
{
const struct ref *r;
- for (r = local_refs; r; r = r->next)
+ for (r = local_refs; r; r = r->next) {
+ if (!r->peer_ref)
+ continue;
add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
+ }
pack_refs(PACK_REFS_ALL);
clear_extra_refs();
}
+static int write_one_config(const char *key, const char *value, void *data)
+{
+ return git_config_set_multivar(key, value ? value : "true", "^$", 0);
+}
+
+static void write_config(struct string_list *config)
+{
+ int i;
+
+ 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");
+ }
+}
+
int cmd_clone(int argc, const char **argv, const char *prefix)
{
int is_bundle = 0, is_local;
@@ -417,7 +517,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (path)
repo = xstrdup(absolute_path(repo_name));
else if (!strchr(repo_name, ':'))
- die("repository '%s' does not exist", repo_name);
+ die(_("repository '%s' does not exist"), repo_name);
else
repo = repo_name;
is_local = path && !is_bundle;
@@ -477,11 +577,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (0 <= option_verbosity) {
if (option_bare)
- printf(_("Cloning into bare repository %s...\n"), dir);
+ printf(_("Cloning into bare repository '%s'...\n"), dir);
else
- printf(_("Cloning into %s...\n"), dir);
+ printf(_("Cloning into '%s'...\n"), dir);
}
init_db(option_template, INIT_DB_QUIET);
+ write_config(&option_config);
/*
* At this point, the config exists, so we do not need the
@@ -521,8 +622,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, repo);
strbuf_reset(&key);
- if (option_reference)
- setup_reference(git_dir);
+ if (option_reference.nr)
+ setup_reference();
fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern);
diff --git a/builtin/commit.c b/builtin/commit.c
index 67757e999f..d8d6dd5eaa 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -62,8 +62,6 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
"\n"
"Otherwise, please use 'git reset'\n");
-static unsigned char head_sha1[20];
-
static const char *use_message_buffer;
static const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
@@ -83,7 +81,7 @@ static const char *template_file;
static const char *author_message, *author_message_buffer;
static char *edit_message, *use_message;
static char *fixup_message, *squash_message;
-static int all, edit_flag, also, interactive, only, amend, signoff;
+static int all, edit_flag, also, interactive, patch_interactive, only, amend, signoff;
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
@@ -102,10 +100,10 @@ static enum {
static char *cleanup_arg;
static enum commit_whence whence;
-static int use_editor = 1, initial_commit, include_status = 1;
+static int use_editor = 1, include_status = 1;
static int show_ignored_in_status;
static const char *only_include_assumed;
-static struct strbuf message;
+static struct strbuf message = STRBUF_INIT;
static int null_termination;
static enum {
@@ -140,7 +138,7 @@ static struct option builtin_commit_options[] = {
OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
- OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
+ OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
@@ -152,6 +150,7 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+ OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
@@ -255,8 +254,11 @@ static int list_paths(struct string_list *list, const char *with_tree,
;
m = xcalloc(1, i);
- if (with_tree)
- overlay_tree_on_cache(with_tree, prefix);
+ if (with_tree) {
+ char *max_prefix = common_prefix(pattern);
+ overlay_tree_on_cache(with_tree, max_prefix ? max_prefix : prefix);
+ free(max_prefix);
+ }
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
@@ -271,7 +273,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
item->util = item; /* better a valid pointer than a fake one */
}
- return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
+ return report_path_error(m, pattern, prefix);
}
static void add_remove_files(struct string_list *list)
@@ -293,13 +295,13 @@ static void add_remove_files(struct string_list *list)
}
}
-static void create_base_index(void)
+static void create_base_index(const struct commit *current_head)
{
struct tree *tree;
struct unpack_trees_options opts;
struct tree_desc t;
- if (initial_commit) {
+ if (!current_head) {
discard_cache();
return;
}
@@ -312,7 +314,7 @@ static void create_base_index(void)
opts.dst_index = &the_index;
opts.fn = oneway_merge;
- tree = parse_tree_indirect(head_sha1);
+ tree = parse_tree_indirect(current_head->object.sha1);
if (!tree)
die(_("failed to unpack HEAD tree object"));
parse_tree(tree);
@@ -331,23 +333,17 @@ static void refresh_cache_or_die(int refresh_flags)
die_resolve_conflict("commit");
}
-static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
+static char *prepare_index(int argc, const char **argv, const char *prefix,
+ const struct commit *current_head, int is_status)
{
int fd;
struct string_list partial;
const char **pathspec = NULL;
+ char *old_index_env = NULL;
int refresh_flags = REFRESH_QUIET;
if (is_status)
refresh_flags |= REFRESH_UNMERGED;
- if (interactive) {
- if (interactive_add(argc, argv, prefix) != 0)
- die(_("interactive add failed"));
- if (read_cache_preload(NULL) < 0)
- die(_("index file corrupt"));
- commit_style = COMMIT_AS_IS;
- return get_index_file();
- }
if (*argv)
pathspec = get_pathspec(prefix, argv);
@@ -355,6 +351,33 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
if (read_cache_preload(pathspec) < 0)
die(_("index file corrupt"));
+ if (interactive) {
+ fd = hold_locked_index(&index_lock, 1);
+
+ refresh_cache_or_die(refresh_flags);
+
+ if (write_cache(fd, active_cache, active_nr) ||
+ close_lock_file(&index_lock))
+ die(_("unable to create temporary index"));
+
+ old_index_env = getenv(INDEX_ENVIRONMENT);
+ setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
+
+ if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
+ die(_("interactive add failed"));
+
+ if (old_index_env && *old_index_env)
+ setenv(INDEX_ENVIRONMENT, old_index_env, 1);
+ else
+ unsetenv(INDEX_ENVIRONMENT);
+
+ discard_cache();
+ read_cache_from(index_lock.filename);
+
+ commit_style = COMMIT_NORMAL;
+ return index_lock.filename;
+ }
+
/*
* Non partial, non as-is commit.
*
@@ -427,7 +450,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
memset(&partial, 0, sizeof(partial));
partial.strdup_strings = 1;
- if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec))
+ if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
exit(1);
discard_cache();
@@ -446,7 +469,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
(uintmax_t) getpid()),
LOCK_DIE_ON_ERROR);
- create_base_index();
+ create_base_index(current_head);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
@@ -495,12 +518,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
return s->commitable;
}
-static int is_a_merge(const unsigned char *sha1)
+static int is_a_merge(const struct commit *current_head)
{
- struct commit *commit = lookup_commit(sha1);
- if (!commit || parse_commit(commit))
- die(_("could not parse HEAD commit"));
- return !!(commit->parents && commit->parents->next);
+ return !!(current_head->parents && current_head->parents->next);
}
static const char sign_off_header[] = "Signed-off-by: ";
@@ -604,6 +624,7 @@ static char *cut_ident_timestamp_part(char *string)
}
static int prepare_to_commit(const char *index_file, const char *prefix,
+ struct commit *current_head,
struct wt_status *s,
struct strbuf *author_ident)
{
@@ -615,6 +636,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
const char *hook_arg1 = NULL;
const char *hook_arg2 = NULL;
int ident_shown = 0;
+ int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
if (!no_verify && run_hook(index_file, "pre-commit", NULL))
return 0;
@@ -681,6 +703,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (strbuf_read_file(&sb, template_file, 0) < 0)
die_errno(_("could not read '%s'"), template_file);
hook_arg1 = "template";
+ clean_message_contents = 0;
}
/*
@@ -708,7 +731,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (s->fp == NULL)
die_errno(_("could not open '%s'"), git_path(commit_editmsg));
- if (cleanup_mode != CLEANUP_NONE)
+ if (clean_message_contents)
stripspace(&sb, 0);
if (signoff) {
@@ -823,7 +846,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
* empty due to conflict resolution, which the user should okay.
*/
if (!commitable && whence != FROM_MERGE && !allow_empty &&
- !(amend && is_a_merge(head_sha1))) {
+ !(amend && is_a_merge(current_head))) {
run_status(stdout, index_file, prefix, 0, s);
if (amend)
fputs(_(empty_amend_advice), stderr);
@@ -981,6 +1004,7 @@ static const char *read_commit_message(const char *name)
static int parse_and_validate_options(int argc, const char *argv[],
const char * const usage[],
const char *prefix,
+ struct commit *current_head,
struct wt_status *s)
{
int f = 0;
@@ -1001,11 +1025,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (!use_editor)
setenv("GIT_EDITOR", ":", 1);
- if (get_sha1("HEAD", head_sha1))
- initial_commit = 1;
-
/* Sanity check options */
- if (amend && initial_commit)
+ if (amend && !current_head)
die(_("You have nothing to amend."));
if (amend && whence != FROM_COMMIT)
die(_("You are in the middle of a %s -- cannot amend."), whence_s());
@@ -1041,8 +1062,11 @@ static int parse_and_validate_options(int argc, const char *argv[],
author_message_buffer = read_commit_message(author_message);
}
+ if (patch_interactive)
+ interactive = 1;
+
if (!!also + !!only + !!all + !!interactive > 1)
- die(_("Only one of --include/--only/--all/--interactive can be used."));
+ die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
if (argc == 0 && (also || (only && !amend)))
die(_("No paths with --include/--only does not make sense."));
if (argc == 0 && only && amend)
@@ -1064,8 +1088,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (all && argc > 0)
die(_("Paths with -a does not make sense."));
- else if (interactive && argc > 0)
- die(_("Paths with --interactive does not make sense."));
if (null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN;
@@ -1076,12 +1098,12 @@ static int parse_and_validate_options(int argc, const char *argv[],
}
static int dry_run_commit(int argc, const char **argv, const char *prefix,
- struct wt_status *s)
+ const struct commit *current_head, struct wt_status *s)
{
int commitable;
const char *index_file;
- index_file = prepare_index(argc, argv, prefix, 1);
+ index_file = prepare_index(argc, argv, prefix, current_head, 1);
commitable = run_status(stdout, index_file, prefix, 0, s);
rollback_index_files();
@@ -1120,7 +1142,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
return 0;
}
if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
- s->use_color = git_config_colorbool(k, v, -1);
+ s->use_color = git_config_colorbool(k, v);
return 0;
}
if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
@@ -1183,9 +1205,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_status_usage, builtin_status_options);
- if (null_termination && status_format == STATUS_FORMAT_LONG)
- status_format = STATUS_FORMAT_PORCELAIN;
-
wt_status_prepare(&s);
gitmodules_config();
git_config(git_status_config, &s);
@@ -1193,6 +1212,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
+
+ if (null_termination && status_format == STATUS_FORMAT_LONG)
+ status_format = STATUS_FORMAT_PORCELAIN;
+
handle_untracked_files_arg(&s);
if (show_ignored_in_status)
s.show_ignored_files = 1;
@@ -1212,10 +1235,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (s.relative_paths)
s.prefix = prefix;
- if (s.use_color == -1)
- s.use_color = git_use_color_default;
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
switch (status_format) {
case STATUS_FORMAT_SHORT:
@@ -1233,7 +1252,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
return 0;
}
-static void print_summary(const char *prefix, const unsigned char *sha1)
+static void print_summary(const char *prefix, const unsigned char *sha1,
+ int initial_commit)
{
struct rev_info rev;
struct commit *commit;
@@ -1355,12 +1375,13 @@ 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, *p;
- unsigned char commit_sha1[20];
+ unsigned char sha1[20];
struct ref_lock *ref_lock;
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
int allow_fast_forward = 1;
struct wt_status s;
+ struct commit *current_head = NULL;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
@@ -1369,48 +1390,47 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
git_config(git_commit_config, &s);
determine_whence(&s);
- if (s.use_color == -1)
- s.use_color = git_use_color_default;
- argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
- prefix, &s);
- if (dry_run) {
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
- return dry_run_commit(argc, argv, prefix, &s);
+ if (get_sha1("HEAD", sha1))
+ current_head = NULL;
+ else {
+ current_head = lookup_commit_or_die(sha1, "HEAD");
+ if (!current_head || parse_commit(current_head))
+ die(_("could not parse HEAD commit"));
}
- index_file = prepare_index(argc, argv, prefix, 0);
+ argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+ prefix, current_head, &s);
+ if (dry_run)
+ return dry_run_commit(argc, argv, prefix, current_head, &s);
+ index_file = prepare_index(argc, argv, prefix, current_head, 0);
/* Set up everything for writing the commit object. This includes
running hooks, writing the trees, and interacting with the user. */
- if (!prepare_to_commit(index_file, prefix, &s, &author_ident)) {
+ if (!prepare_to_commit(index_file, prefix,
+ current_head, &s, &author_ident)) {
rollback_index_files();
return 1;
}
/* Determine parents */
reflog_msg = getenv("GIT_REFLOG_ACTION");
- if (initial_commit) {
+ if (!current_head) {
if (!reflog_msg)
reflog_msg = "commit (initial)";
} else if (amend) {
struct commit_list *c;
- struct commit *commit;
if (!reflog_msg)
reflog_msg = "commit (amend)";
- commit = lookup_commit(head_sha1);
- if (!commit || parse_commit(commit))
- die(_("could not parse HEAD commit"));
-
- for (c = commit->parents; c; c = c->next)
+ for (c = current_head->parents; c; c = c->next)
pptr = &commit_list_insert(c->item, pptr)->next;
} else if (whence == FROM_MERGE) {
struct strbuf m = STRBUF_INIT;
+ struct commit *commit;
FILE *fp;
if (!reflog_msg)
reflog_msg = "commit (merge)";
- pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
+ pptr = &commit_list_insert(current_head, pptr)->next;
fp = fopen(git_path("MERGE_HEAD"), "r");
if (fp == NULL)
die_errno(_("could not open '%s' for reading"),
@@ -1419,7 +1439,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
if (get_sha1_hex(m.buf, sha1) < 0)
die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
- pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next;
+ commit = lookup_commit_or_die(sha1, "MERGE_HEAD");
+ pptr = &commit_list_insert(commit, pptr)->next;
}
fclose(fp);
strbuf_release(&m);
@@ -1436,7 +1457,7 @@ 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(lookup_commit(head_sha1), pptr)->next;
+ pptr = &commit_list_insert(current_head, pptr)->next;
}
/* Finally, get the commit message */
@@ -1462,7 +1483,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
exit(1);
}
- if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
+ if (commit_tree(sb.buf, active_cache_tree->sha1, parents, sha1,
author_ident.buf)) {
rollback_index_files();
die(_("failed to write commit object"));
@@ -1470,7 +1491,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
strbuf_release(&author_ident);
ref_lock = lock_any_ref_for_update("HEAD",
- initial_commit ? NULL : head_sha1,
+ !current_head
+ ? NULL
+ : current_head->object.sha1,
0);
nl = strchr(sb.buf, '\n');
@@ -1485,12 +1508,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
rollback_index_files();
die(_("cannot lock HEAD ref"));
}
- if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) {
+ if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
rollback_index_files();
die(_("cannot update HEAD ref"));
}
unlink(git_path("CHERRY_PICK_HEAD"));
+ unlink(git_path("REVERT_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
@@ -1507,13 +1531,14 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct notes_rewrite_cfg *cfg;
cfg = init_copy_notes_for_rewrite("amend");
if (cfg) {
- copy_note_for_rewrite(cfg, head_sha1, commit_sha1);
+ /* we are amending, so current_head is not NULL */
+ copy_note_for_rewrite(cfg, current_head->object.sha1, sha1);
finish_copy_notes_for_rewrite(cfg);
}
- run_rewrite_hook(head_sha1, commit_sha1);
+ run_rewrite_hook(current_head->object.sha1, sha1);
}
if (!quiet)
- print_summary(prefix, commit_sha1);
+ print_summary(prefix, sha1, !current_head);
return 0;
}
diff --git a/builtin/config.c b/builtin/config.c
index 3e3c528497..0315ad76f8 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -99,6 +99,7 @@ static int show_config(const char *key_, const char *value_, void *cb)
const char *vptr = value;
int must_free_vptr = 0;
int dup_error = 0;
+ int must_print_delim = 0;
if (!use_key_regexp && strcmp(key_, key))
return 0;
@@ -109,10 +110,8 @@ static int show_config(const char *key_, const char *value_, void *cb)
return 0;
if (show_keys) {
- if (value_)
- printf("%s%c", key_, key_delim);
- else
- printf("%s", key_);
+ printf("%s", key_);
+ must_print_delim = 1;
}
if (seen && !do_all)
dup_error = 1;
@@ -130,16 +129,23 @@ static int show_config(const char *key_, const char *value_, void *cb)
} else if (types == TYPE_PATH) {
git_config_pathname(&vptr, key_, value_);
must_free_vptr = 1;
+ } else if (value_) {
+ vptr = value_;
+ } else {
+ /* Just show the key name */
+ vptr = "";
+ must_print_delim = 0;
}
- else
- vptr = value_?value_:"";
seen++;
if (dup_error) {
error("More than one value for the key %s: %s",
key_, vptr);
}
- else
+ else {
+ if (must_print_delim)
+ printf("%c", key_delim);
printf("%s%c", vptr, term);
+ }
if (must_free_vptr)
/* If vptr must be freed, it's a pointer to a
* dynamically allocated buffer, it's safe to cast to
@@ -303,24 +309,18 @@ static void get_color(const char *def_color)
fputs(parsed_color, stdout);
}
-static int stdout_is_tty;
static int get_colorbool_found;
static int get_diff_color_found;
+static int get_color_ui_found;
static int git_get_colorbool_config(const char *var, const char *value,
void *cb)
{
- if (!strcmp(var, get_colorbool_slot)) {
- get_colorbool_found =
- git_config_colorbool(var, value, stdout_is_tty);
- }
- if (!strcmp(var, "diff.color")) {
- get_diff_color_found =
- git_config_colorbool(var, value, stdout_is_tty);
- }
- if (!strcmp(var, "color.ui")) {
- git_use_color_default = git_config_colorbool(var, value, stdout_is_tty);
- return 0;
- }
+ if (!strcmp(var, get_colorbool_slot))
+ get_colorbool_found = git_config_colorbool(var, value);
+ else if (!strcmp(var, "diff.color"))
+ get_diff_color_found = git_config_colorbool(var, value);
+ else if (!strcmp(var, "color.ui"))
+ get_color_ui_found = git_config_colorbool(var, value);
return 0;
}
@@ -334,9 +334,11 @@ static int get_colorbool(int print)
if (!strcmp(get_colorbool_slot, "color.diff"))
get_colorbool_found = get_diff_color_found;
if (get_colorbool_found < 0)
- get_colorbool_found = git_use_color_default;
+ get_colorbool_found = get_color_ui_found;
}
+ get_colorbool_found = want_color(get_colorbool_found);
+
if (print) {
printf("%s\n", get_colorbool_found ? "true" : "false");
return 0;
@@ -436,9 +438,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
NULL, NULL);
}
else if (actions == ACTION_SET) {
+ int ret;
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
- return git_config_set(argv[0], value);
+ ret = git_config_set(argv[0], value);
+ if (ret == CONFIG_NOTHING_SET)
+ error("cannot overwrite multiple values with a single value\n"
+ " Use a regexp, --add or --set-all to change %s.", argv[0]);
+ return ret;
}
else if (actions == ACTION_SET_ALL) {
check_argc(argc, 2, 3);
@@ -505,9 +512,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (actions == ACTION_GET_COLORBOOL) {
if (argc == 1)
- stdout_is_tty = git_config_bool("command line", argv[0]);
- else if (argc == 0)
- stdout_is_tty = isatty(1);
+ color_stdout_is_tty = git_config_bool("command line", argv[0]);
return get_colorbool(argc != 0);
}
diff --git a/builtin/describe.c b/builtin/describe.c
index 66fc291c8a..9f63067f50 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -462,8 +462,21 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die(_("No names found, cannot describe anything."));
if (argc == 0) {
- if (dirty && !cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix))
- dirty = NULL;
+ if (dirty) {
+ static struct lock_file index_lock;
+ int fd;
+
+ read_cache_preload(NULL);
+ refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+ NULL, NULL, NULL);
+ fd = hold_locked_index(&index_lock, 0);
+ if (0 <= fd)
+ update_index_if_able(&the_index, &index_lock);
+
+ if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
+ diff_index_args, prefix))
+ dirty = NULL;
+ }
describe("HEAD", 1);
} else if (dirty) {
die(_("--dirty is incompatible with committishes"));
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 0d2a3e9fa2..be6417d166 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -163,6 +163,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
}
if (read_stdin) {
+ int saved_nrl = 0;
+ int saved_dcctc = 0;
+
if (opt->diffopt.detect_rename)
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
DIFF_SETUP_USE_CACHE);
@@ -173,9 +176,16 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
fputs(line, stdout);
fflush(stdout);
}
- else
+ else {
diff_tree_stdin(line);
+ if (saved_nrl < opt->diffopt.needed_rename_limit)
+ saved_nrl = opt->diffopt.needed_rename_limit;
+ if (opt->diffopt.degraded_cc_to_c)
+ saved_dcctc = 1;
+ }
}
+ opt->diffopt.degraded_cc_to_c = saved_dcctc;
+ opt->diffopt.needed_rename_limit = saved_nrl;
}
return diff_result_code(&opt->diffopt, 0);
diff --git a/builtin/diff.c b/builtin/diff.c
index 717fa1a341..0fe638fc45 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -182,6 +182,7 @@ static int builtin_diff_combined(struct rev_info *revs,
hashcpy((unsigned char *)(parent + i), ent[i].item->sha1);
diff_tree_combined(parent[0], parent + 1, ents - 1,
revs->dense_combined_merges, revs);
+ free((void *)parent);
return 0;
}
@@ -202,7 +203,6 @@ static void refresh_index_quietly(void)
static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
{
- int result;
unsigned int options = 0;
while (1 < argc && argv[1][0] == '-') {
@@ -236,8 +236,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
perror("read_cache_preload");
return -1;
}
- result = run_diff_files(revs, options);
- return diff_result_code(&revs->diffopt, result);
+ return run_diff_files(revs, options);
}
int cmd_diff(int argc, const char **argv, const char *prefix)
@@ -278,9 +277,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
gitmodules_config();
git_config(git_diff_ui_config, NULL);
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
init_revisions(&rev, prefix);
/* If this is a no-index diff, just run it and exit there. */
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index daf19451ba..9836e6b7ca 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -16,6 +16,7 @@
#include "string-list.h"
#include "utf8.h"
#include "parse-options.h"
+#include "quote.h"
static const char *fast_export_usage[] = {
"git fast-export [rev-list-opts]",
@@ -26,6 +27,7 @@ static int progress;
static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
static int fake_missing_tagger;
+static int use_done_feature;
static int no_data;
static int full_tree;
@@ -178,6 +180,15 @@ static int depth_first(const void *a_, const void *b_)
return (a->status == 'R') - (b->status == 'R');
}
+static void print_path(const char *path)
+{
+ int need_quote = quote_c_style(path, NULL, NULL, 0);
+ if (need_quote)
+ quote_c_style(path, NULL, stdout, 0);
+ else
+ printf("%s", path);
+}
+
static void show_filemodify(struct diff_queue_struct *q,
struct diff_options *options, void *data)
{
@@ -195,13 +206,18 @@ static void show_filemodify(struct diff_queue_struct *q,
switch (q->queue[i]->status) {
case DIFF_STATUS_DELETED:
- printf("D %s\n", spec->path);
+ printf("D ");
+ print_path(spec->path);
+ putchar('\n');
break;
case DIFF_STATUS_COPIED:
case DIFF_STATUS_RENAMED:
- printf("%c \"%s\" \"%s\"\n", q->queue[i]->status,
- ospec->path, spec->path);
+ printf("%c ", q->queue[i]->status);
+ print_path(ospec->path);
+ putchar(' ');
+ print_path(spec->path);
+ putchar('\n');
if (!hashcmp(ospec->sha1, spec->sha1) &&
ospec->mode == spec->mode)
@@ -216,13 +232,15 @@ static void show_filemodify(struct diff_queue_struct *q,
* output the SHA-1 verbatim.
*/
if (no_data || S_ISGITLINK(spec->mode))
- printf("M %06o %s %s\n", spec->mode,
- sha1_to_hex(spec->sha1), spec->path);
+ printf("M %06o %s ", spec->mode,
+ sha1_to_hex(spec->sha1));
else {
struct object *object = lookup_object(spec->sha1);
- printf("M %06o :%d %s\n", spec->mode,
- get_object_mark(object), spec->path);
+ printf("M %06o :%d ", spec->mode,
+ get_object_mark(object));
}
+ print_path(spec->path);
+ putchar('\n');
break;
default:
@@ -627,6 +645,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
"Fake a tagger when tags lack one"),
OPT_BOOLEAN(0, "full-tree", &full_tree,
"Output full tree for each commit"),
+ OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
+ "Use the done feature to terminate the stream"),
{ OPTION_NEGBIT, 0, "data", &no_data, NULL,
"Skip output of blob data",
PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
@@ -648,6 +668,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (argc > 1)
usage_with_options (fast_export_usage, options);
+ if (use_done_feature)
+ printf("feature done\n");
+
if (import_filename)
import_marks(import_filename);
@@ -675,5 +698,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (export_filename)
export_marks(export_filename);
+ if (use_done_feature)
+ printf("done\n");
+
return 0;
}
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 85aff029b2..6207ecd298 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -15,7 +15,9 @@ static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
static int unpack_limit = 100;
static int prefer_ofs_delta = 1;
-static int no_done = 0;
+static int no_done;
+static int fetch_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
static struct fetch_pack_args args = {
/* .uploadpack = */ "git-upload-pack",
};
@@ -185,6 +187,36 @@ static void consume_shallow_list(int fd)
}
}
+struct write_shallow_data {
+ struct strbuf *out;
+ int use_pack_protocol;
+ int count;
+};
+
+static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
+{
+ struct write_shallow_data *data = cb_data;
+ const char *hex = sha1_to_hex(graft->sha1);
+ data->count++;
+ if (data->use_pack_protocol)
+ packet_buf_write(data->out, "shallow %s", hex);
+ else {
+ strbuf_addstr(data->out, hex);
+ strbuf_addch(data->out, '\n');
+ }
+ return 0;
+}
+
+static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
+{
+ struct write_shallow_data data;
+ data.out = out;
+ data.use_pack_protocol = use_pack_protocol;
+ data.count = 0;
+ for_each_commit_graft(write_one_shallow, &data);
+ return data.count;
+}
+
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{
static char line[1000];
@@ -226,7 +258,7 @@ static void insert_one_alternate_ref(const struct ref *ref, void *unused)
static void insert_alternate_refs(void)
{
- foreach_alt_odb(refs_from_alternate_cb, insert_one_alternate_ref);
+ for_each_alternate_ref(insert_one_alternate_ref, NULL);
}
#define INITIAL_FLUSH 16
@@ -395,6 +427,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
case ACK_continue: {
struct commit *commit =
lookup_commit(result_sha1);
+ if (!commit)
+ die("invalid commit %s", sha1_to_hex(result_sha1));
if (args.stateless_rpc
&& ack == ACK_common
&& !(commit->object.flags & COMMON)) {
@@ -472,8 +506,10 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
- commit->object.flags |= COMPLETE;
- commit_list_insert_by_date(commit, &complete);
+ if (!(commit->object.flags & COMPLETE)) {
+ commit->object.flags |= COMPLETE;
+ commit_list_insert_by_date(commit, &complete);
+ }
}
return 0;
}
@@ -510,7 +546,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
for (ref = *refs; ref; ref = next) {
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
- check_ref_format(ref->name + 5))
+ check_refname_format(ref->name + 5, 0))
; /* trash */
else if (args.fetch_all &&
(!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
@@ -520,11 +556,16 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
continue;
}
else {
- int order = path_match(ref->name, nr_match, match);
- if (order) {
- return_refs[order-1] = ref;
- continue; /* we will link it later */
+ int i;
+ for (i = 0; i < nr_match; i++) {
+ if (!strcmp(ref->name, match[i])) {
+ match[i][0] = '\0';
+ return_refs[i] = ref;
+ break;
+ }
}
+ if (i < nr_match)
+ continue; /* we will link it later */
}
free(ref);
}
@@ -700,6 +741,12 @@ static int get_pack(int xd[2], char **pack_lockfile)
}
if (*hdr_arg)
*av++ = hdr_arg;
+ if (fetch_fsck_objects >= 0
+ ? fetch_fsck_objects
+ : transfer_fsck_objects >= 0
+ ? transfer_fsck_objects
+ : 0)
+ *av++ = "--strict";
*av++ = NULL;
cmd.in = demux.out;
@@ -819,6 +866,16 @@ static int fetch_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (!strcmp(var, "fetch.fsckobjects")) {
+ fetch_fsck_objects = git_config_bool(var, value);
+ return 0;
+ }
+
+ if (!strcmp(var, "transfer.fsckobjects")) {
+ transfer_fsck_objects = git_config_bool(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -924,7 +981,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.verbose ? CONNECT_VERBOSE : 0);
}
- get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+ get_remote_heads(fd[0], &ref, 0, NULL);
ref = fetch_pack(&args, fd, conn, ref, dest,
nr_heads, heads, pack_lockfile_ptr);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f9c41da475..8761a33b49 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -13,6 +13,7 @@
#include "sigchain.h"
#include "transport.h"
#include "submodule.h"
+#include "connected.h"
static const char * const builtin_fetch_usage[] = {
"git fetch [<options>] [<repository> [<refspec>...]]",
@@ -239,23 +240,23 @@ static int s_update_ref(const char *action,
static int update_local_ref(struct ref *ref,
const char *remote,
- char *display)
+ struct strbuf *display)
{
struct commit *current = NULL, *updated;
enum object_type type;
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
- *display = 0;
type = sha1_object_info(ref->new_sha1, NULL);
if (type < 0)
die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
if (verbosity > 0)
- sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
- _("[up to date]"), REFCOL_WIDTH, remote,
- pretty_ref);
+ strbuf_addf(display, "= %-*s %-*s -> %s",
+ TRANSPORT_SUMMARY_WIDTH,
+ _("[up to date]"), REFCOL_WIDTH,
+ remote, pretty_ref);
return 0;
}
@@ -267,9 +268,10 @@ 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...
*/
- sprintf(display, _("! %-*s %-*s -> %s (can't fetch in current branch)"),
- TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
- pretty_ref);
+ strbuf_addf(display,
+ _("! %-*s %-*s -> %s (can't fetch in current branch)"),
+ TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+ REFCOL_WIDTH, remote, pretty_ref);
return 1;
}
@@ -277,9 +279,11 @@ static int update_local_ref(struct ref *ref,
!prefixcmp(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
- TRANSPORT_SUMMARY_WIDTH, _("[tag update]"), REFCOL_WIDTH, remote,
- pretty_ref, r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : '-',
+ TRANSPORT_SUMMARY_WIDTH, _("[tag update]"),
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
}
@@ -302,9 +306,11 @@ static int update_local_ref(struct ref *ref,
}
r = s_update_ref(msg, ref, 0);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
- TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : '*',
+ TRANSPORT_SUMMARY_WIDTH, what,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
}
@@ -318,9 +324,11 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("fast-forward", ref, 1);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
- pretty_ref, r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : ' ',
+ TRANSPORT_SUMMARY_WIDTH, quickref,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
} else if (force || ref->force) {
char quickref[84];
@@ -332,26 +340,40 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("forced-update", ref, 1);
- sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
- pretty_ref,
- r ? _("unable to update local ref") : _("forced update"));
+ strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
+ r ? '!' : '+',
+ TRANSPORT_SUMMARY_WIDTH, quickref,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _("unable to update local ref") : _("forced update"));
return r;
} else {
- sprintf(display, "! %-*s %-*s -> %s %s",
- TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
- pretty_ref, _("(non-fast-forward)"));
+ strbuf_addf(display, "! %-*s %-*s -> %s %s",
+ TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+ REFCOL_WIDTH, remote, pretty_ref,
+ _("(non-fast-forward)"));
return 1;
}
}
+static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+{
+ struct ref **rm = cb_data;
+ struct ref *ref = *rm;
+
+ if (!ref)
+ return -1; /* end of the list */
+ *rm = ref->next;
+ hashcpy(sha1, ref->old_sha1);
+ return 0;
+}
+
static int store_updated_refs(const char *raw_url, const char *remote_name,
struct ref *ref_map)
{
FILE *fp;
struct commit *commit;
- int url_len, i, note_len, shown_url = 0, rc = 0;
- char note[1024];
+ int url_len, i, shown_url = 0, rc = 0;
+ struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
@@ -364,6 +386,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
url = transport_anonymize_url(raw_url);
else
url = xstrdup("foreign");
+
+ rm = ref_map;
+ if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+ rc = error(_("%s did not send all necessary objects\n"), url);
+ goto abort;
+ }
+
for (rm = ref_map; rm; rm = rm->next) {
struct ref *ref = NULL;
@@ -407,19 +436,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
if (4 < i && !strncmp(".git", url + i - 3, 4))
url_len = i - 3;
- note_len = 0;
+ strbuf_reset(&note);
if (*what) {
if (*kind)
- note_len += sprintf(note + note_len, "%s ",
- kind);
- note_len += sprintf(note + note_len, "'%s' of ", what);
+ strbuf_addf(&note, "%s ", kind);
+ strbuf_addf(&note, "'%s' of ", what);
}
- note[note_len] = '\0';
fprintf(fp, "%s\t%s\t%s",
sha1_to_hex(commit ? commit->object.sha1 :
rm->old_sha1),
rm->merge ? "" : "not-for-merge",
- note);
+ note.buf);
for (i = 0; i < url_len; ++i)
if ('\n' == url[i])
fputs("\\n", fp);
@@ -427,29 +454,36 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
fputc(url[i], fp);
fputc('\n', fp);
+ strbuf_reset(&note);
if (ref) {
- rc |= update_local_ref(ref, what, note);
+ rc |= update_local_ref(ref, what, &note);
free(ref);
} else
- sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH, *kind ? kind : "branch",
- REFCOL_WIDTH, *what ? what : "HEAD");
- if (*note) {
+ strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
+ TRANSPORT_SUMMARY_WIDTH,
+ *kind ? kind : "branch",
+ REFCOL_WIDTH,
+ *what ? what : "HEAD");
+ if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
url_len, url);
shown_url = 1;
}
if (verbosity >= 0)
- fprintf(stderr, " %s\n", note);
+ fprintf(stderr, " %s\n", note.buf);
}
}
- free(url);
- fclose(fp);
+
if (rc & STORE_REF_ERROR_DF_CONFLICT)
error(_("some local refs could not be updated; try running\n"
" 'git remote prune %s' to remove any old, conflicting "
"branches"), remote_name);
+
+ abort:
+ strbuf_release(&note);
+ free(url);
+ fclose(fp);
return rc;
}
@@ -457,23 +491,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
* We would want to bypass the object transfer altogether if
* everything we are going to fetch already exists and is connected
* locally.
- *
- * The refs we are going to fetch are in ref_map. If running
- *
- * $ git rev-list --objects --stdin --not --all
- *
- * (feeding all the refs in ref_map on its standard input)
- * does not error out, that means everything reachable from the
- * refs we are going to fetch exists and is connected to some of
- * our existing refs.
*/
static int quickfetch(struct ref *ref_map)
{
- struct child_process revlist;
- struct ref *ref;
- int err;
- const char *argv[] = {"rev-list",
- "--quiet", "--objects", "--stdin", "--not", "--all", NULL};
+ struct ref *rm = ref_map;
/*
* If we are deepening a shallow clone we already have these
@@ -484,47 +505,7 @@ static int quickfetch(struct ref *ref_map)
*/
if (depth)
return -1;
-
- if (!ref_map)
- return 0;
-
- memset(&revlist, 0, sizeof(revlist));
- revlist.argv = argv;
- revlist.git_cmd = 1;
- revlist.no_stdout = 1;
- revlist.no_stderr = 1;
- revlist.in = -1;
-
- err = start_command(&revlist);
- if (err) {
- error(_("could not run rev-list"));
- return err;
- }
-
- /*
- * If rev-list --stdin encounters an unknown commit, it terminates,
- * which will cause SIGPIPE in the write loop below.
- */
- sigchain_push(SIGPIPE, SIG_IGN);
-
- for (ref = ref_map; ref; ref = ref->next) {
- if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
- write_str_in_full(revlist.in, "\n") < 0) {
- if (errno != EPIPE && errno != EINVAL)
- error(_("failed write to rev-list: %s"), strerror(errno));
- err = -1;
- break;
- }
- }
-
- if (close(revlist.in)) {
- error(_("failed to close rev-list's stdin: %s"), strerror(errno));
- err = -1;
- }
-
- sigchain_pop(SIGPIPE);
-
- return finish_command(&revlist) || err;
+ return check_everything_connected(iterate_ref_map, 1, &rm);
}
static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@ -540,10 +521,10 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
return ret;
}
-static int prune_refs(struct transport *transport, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
{
int result = 0;
- struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+ struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
const char *dangling_msg = dry_run
? _(" (%s will become dangling)\n")
: _(" (%s has become dangling)\n");
@@ -734,8 +715,31 @@ static int do_fetch(struct transport *transport,
free_refs(ref_map);
return 1;
}
- if (prune)
- prune_refs(transport, ref_map);
+ if (prune) {
+ /* If --tags was specified, pretend the user gave us the canonical tags refspec */
+ if (tags == TAGS_SET) {
+ const char *tags_str = "refs/tags/*:refs/tags/*";
+ struct refspec *tags_refspec, *refspec;
+
+ /* Copy the refspec and add the tags to it */
+ refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
+ tags_refspec = parse_fetch_refspec(1, &tags_str);
+ memcpy(refspec, refs, ref_count * sizeof(struct refspec));
+ memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
+ ref_count++;
+
+ prune_refs(refspec, ref_count, ref_map);
+
+ ref_count--;
+ /* The rest of the strings belong to fetch_one */
+ free_refspec(1, tags_refspec);
+ free(refspec);
+ } else if (ref_count) {
+ prune_refs(refs, ref_count, ref_map);
+ } else {
+ prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+ }
+ }
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -875,6 +879,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
{
int i;
static const char **refs = NULL;
+ struct refspec *refspec;
int ref_nr = 0;
int exit_code;
@@ -915,8 +920,9 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack);
- exit_code = do_fetch(transport,
- parse_fetch_refspec(ref_nr, refs), ref_nr);
+ refspec = parse_fetch_refspec(ref_nr, refs);
+ exit_code = do_fetch(transport, refspec, ref_nr);
+ free_refspec(ref_nr, refspec);
transport_disconnect(transport);
transport = NULL;
return exit_code;
@@ -939,6 +945,15 @@ 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 (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+ if (recurse_submodules_default) {
+ int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
+ set_config_fetch_recurse_submodules(arg);
+ }
+ gitmodules_config();
+ git_config(submodule_config, NULL);
+ }
+
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
@@ -974,12 +989,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
const char *options[10];
int num_options = 0;
- if (recurse_submodules_default) {
- int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
- set_config_fetch_recurse_submodules(arg);
- }
- gitmodules_config();
- git_config(submodule_config, NULL);
add_options_to_argv(&num_options, options);
result = fetch_populated_submodules(num_options, options,
submodule_prefix,
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 75816329d6..7e2f22589d 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -293,7 +293,7 @@ static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
struct commit *head;
struct rev_info rev;
- head = lookup_commit(head_sha1);
+ head = lookup_commit_or_die(head_sha1, "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 89e75c6894..d90e5d2b29 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -69,6 +69,9 @@ static struct {
{ "subject" },
{ "body" },
{ "contents" },
+ { "contents:subject" },
+ { "contents:body" },
+ { "contents:signature" },
{ "upstream" },
{ "symref" },
{ "flag" },
@@ -361,6 +364,18 @@ static const char *copy_email(const char *buf)
return xmemdupz(email, eoemail + 1 - email);
}
+static char *copy_subject(const char *buf, unsigned long len)
+{
+ char *r = xmemdupz(buf, len);
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (r[i] == '\n')
+ r[i] = ' ';
+
+ return r;
+}
+
static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
{
const char *eoemail = strstr(buf, "> ");
@@ -458,38 +473,56 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
}
}
-static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)
+static void find_subpos(const char *buf, unsigned long sz,
+ const char **sub, unsigned long *sublen,
+ const char **body, unsigned long *bodylen,
+ unsigned long *nonsiglen,
+ const char **sig, unsigned long *siglen)
{
- while (*buf) {
- const char *eol = strchr(buf, '\n');
- if (!eol)
- return;
- if (eol[1] == '\n') {
- buf = eol + 1;
- break; /* found end of header */
- }
- buf = eol + 1;
+ const char *eol;
+ /* skip past header until we hit empty line */
+ while (*buf && *buf != '\n') {
+ eol = strchrnul(buf, '\n');
+ if (*eol)
+ eol++;
+ buf = eol;
}
+ /* skip any empty lines */
while (*buf == '\n')
buf++;
- if (!*buf)
- return;
- *sub = buf; /* first non-empty line */
- buf = strchr(buf, '\n');
- if (!buf) {
- *body = "";
- return; /* no body */
+
+ /* parse signature first; we might not even have a subject line */
+ *sig = buf + parse_signature(buf, strlen(buf));
+ *siglen = strlen(*sig);
+
+ /* subject is first non-empty line */
+ *sub = buf;
+ /* subject goes to first empty line */
+ while (buf < *sig && *buf && *buf != '\n') {
+ eol = strchrnul(buf, '\n');
+ if (*eol)
+ eol++;
+ buf = eol;
}
+ *sublen = buf - *sub;
+ /* drop trailing newline, if present */
+ if (*sublen && (*sub)[*sublen - 1] == '\n')
+ *sublen -= 1;
+
+ /* skip any empty lines */
while (*buf == '\n')
- buf++; /* skip blank between subject and body */
+ buf++;
*body = buf;
+ *bodylen = strlen(buf);
+ *nonsiglen = *sig - buf;
}
/* See grab_values */
static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
{
int i;
- const char *subpos = NULL, *bodypos = NULL;
+ const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
+ unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i];
@@ -500,17 +533,27 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
name++;
if (strcmp(name, "subject") &&
strcmp(name, "body") &&
- strcmp(name, "contents"))
+ strcmp(name, "contents") &&
+ strcmp(name, "contents:subject") &&
+ strcmp(name, "contents:body") &&
+ strcmp(name, "contents:signature"))
continue;
if (!subpos)
- find_subpos(buf, sz, &subpos, &bodypos);
- if (!subpos)
- return;
+ find_subpos(buf, sz,
+ &subpos, &sublen,
+ &bodypos, &bodylen, &nonsiglen,
+ &sigpos, &siglen);
if (!strcmp(name, "subject"))
- v->s = copy_line(subpos);
+ v->s = copy_subject(subpos, sublen);
+ else if (!strcmp(name, "contents:subject"))
+ v->s = copy_subject(subpos, sublen);
else if (!strcmp(name, "body"))
- v->s = xstrdup(bodypos);
+ v->s = xmemdupz(bodypos, bodylen);
+ else if (!strcmp(name, "contents:body"))
+ v->s = xmemdupz(bodypos, nonsiglen);
+ else if (!strcmp(name, "contents:signature"))
+ v->s = xmemdupz(sigpos, siglen);
else if (!strcmp(name, "contents"))
v->s = xstrdup(subpos);
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 5ae0366bc8..df1a88b51a 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -231,12 +231,9 @@ static void check_unreachable_object(struct object *obj)
unsigned long size;
char *buf = read_sha1_file(obj->sha1,
&type, &size);
- if (buf) {
- if (fwrite(buf, size, 1, f) != 1)
- die_errno("Could not write '%s'",
- filename);
- free(buf);
- }
+ if (buf && fwrite(buf, 1, size, f) != size)
+ die_errno("Could not write '%s'", filename);
+ free(buf);
} else
fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
if (fclose(f))
diff --git a/builtin/gc.c b/builtin/gc.c
index ff5f73ba87..0498094711 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -225,7 +225,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
fprintf(stderr,
_("Auto packing the repository for optimum performance. You may also\n"
"run \"git gc\" manually. See "
- "\"git help gc\" for more information."));
+ "\"git help gc\" for more information.\n"));
} else
append_option(argv_repack,
prune_expire && !strcmp(prune_expire, "now")
diff --git a/builtin/grep.c b/builtin/grep.c
index 10a1f65310..988ea1d332 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -74,13 +74,32 @@ static int all_work_added;
/* This lock protects all the variables above. */
static pthread_mutex_t grep_mutex;
+static inline void grep_lock(void)
+{
+ if (use_threads)
+ pthread_mutex_lock(&grep_mutex);
+}
+
+static inline void grep_unlock(void)
+{
+ if (use_threads)
+ pthread_mutex_unlock(&grep_mutex);
+}
+
/* Used to serialize calls to read_sha1_file. */
static pthread_mutex_t read_sha1_mutex;
-#define grep_lock() pthread_mutex_lock(&grep_mutex)
-#define grep_unlock() pthread_mutex_unlock(&grep_mutex)
-#define read_sha1_lock() pthread_mutex_lock(&read_sha1_mutex)
-#define read_sha1_unlock() pthread_mutex_unlock(&read_sha1_mutex)
+static inline void read_sha1_lock(void)
+{
+ if (use_threads)
+ pthread_mutex_lock(&read_sha1_mutex);
+}
+
+static inline void read_sha1_unlock(void)
+{
+ if (use_threads)
+ pthread_mutex_unlock(&read_sha1_mutex);
+}
/* Signalled when a new work_item is added to todo. */
static pthread_cond_t cond_add;
@@ -93,8 +112,7 @@ static pthread_cond_t cond_write;
/* Signalled when we are finished with everything. */
static pthread_cond_t cond_result;
-static int print_hunk_marks_between_files;
-static int printed_something;
+static int skip_first_line;
static void add_work(enum work_type type, char *name, void *id)
{
@@ -160,10 +178,20 @@ static void work_done(struct work_item *w)
todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
w = &todo[todo_done];
if (w->out.len) {
- if (print_hunk_marks_between_files && printed_something)
- write_or_die(1, "--\n", 3);
- write_or_die(1, w->out.buf, w->out.len);
- printed_something = 1;
+ const char *p = w->out.buf;
+ size_t len = w->out.len;
+
+ /* Skip the leading hunk mark of the first file. */
+ if (skip_first_line) {
+ while (len) {
+ len--;
+ if (*p++ == '\n')
+ break;
+ }
+ skip_first_line = 0;
+ }
+
+ write_or_die(1, p, len);
}
free(w->name);
free(w->identifier);
@@ -316,7 +344,7 @@ static int grep_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "color.grep"))
- opt->color = git_config_colorbool(var, value, -1);
+ opt->color = git_config_colorbool(var, value);
else if (!strcmp(var, "color.grep.context"))
color = opt->color_context;
else if (!strcmp(var, "color.grep.filename"))
@@ -345,13 +373,9 @@ static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type
{
void *data;
- if (use_threads) {
- read_sha1_lock();
- data = read_sha1_file(sha1, type, size);
- read_sha1_unlock();
- } else {
- data = read_sha1_file(sha1, type, size);
- }
+ read_sha1_lock();
+ data = read_sha1_file(sha1, type, size);
+ read_sha1_unlock();
return data;
}
@@ -533,18 +557,19 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
struct tree_desc *tree, struct strbuf *base, int tn_len)
{
- int hit = 0, matched = 0;
+ int hit = 0;
+ enum interesting match = entry_not_interesting;
struct name_entry entry;
int old_baselen = base->len;
while (tree_entry(tree, &entry)) {
- int te_len = tree_entry_len(entry.path, entry.sha1);
+ int te_len = tree_entry_len(&entry);
- if (matched != 2) {
- matched = tree_entry_interesting(&entry, base, tn_len, pathspec);
- if (matched == -1)
- break; /* no more matches */
- if (!matched)
+ if (match != all_entries_interesting) {
+ match = tree_entry_interesting(&entry, base, tn_len, pathspec);
+ if (match == all_entries_not_interesting)
+ break;
+ if (match == entry_not_interesting)
continue;
}
@@ -589,8 +614,11 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
struct strbuf base;
int hit, len;
+ read_sha1_lock();
data = read_object_with_reference(obj->sha1, tree_type,
&size, NULL);
+ read_sha1_unlock();
+
if (!data)
die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
@@ -628,13 +656,15 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
return hit;
}
-static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec)
+static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
+ int exc_std)
{
struct dir_struct dir;
int i, hit = 0;
memset(&dir, 0, sizeof(dir));
- setup_standard_excludes(&dir);
+ if (exc_std)
+ setup_standard_excludes(&dir);
fill_directory(&dir, pathspec->raw);
for (i = 0; i < dir.nr; i++) {
@@ -741,7 +771,7 @@ static int help_callback(const struct option *opt, const char *arg, int unset)
int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
- int cached = 0;
+ int cached = 0, untracked = 0, opt_exclude = -1;
int seen_dashdash = 0;
int external_grep_allowed__ignored;
const char *show_in_pager = NULL, *default_pager = "dummy";
@@ -753,11 +783,25 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int i;
int dummy;
int use_index = 1;
+ enum {
+ pattern_type_unspecified = 0,
+ pattern_type_bre,
+ pattern_type_ere,
+ pattern_type_fixed,
+ pattern_type_pcre,
+ };
+ int pattern_type = pattern_type_unspecified;
+
struct option options[] = {
OPT_BOOLEAN(0, "cached", &cached,
"search in index instead of in the work tree"),
- OPT_BOOLEAN(0, "index", &use_index,
- "--no-index finds in contents not managed by git"),
+ { OPTION_BOOLEAN, 0, "index", &use_index, NULL,
+ "finds in contents not managed by git",
+ PARSE_OPT_NOARG | PARSE_OPT_NEGHELP },
+ OPT_BOOLEAN(0, "untracked", &untracked,
+ "search in both tracked and untracked files"),
+ OPT_SET_INT(0, "exclude-standard", &opt_exclude,
+ "search also in ignored files", 1),
OPT_GROUP(""),
OPT_BOOLEAN('v', "invert-match", &opt.invert,
"show non-matching lines"),
@@ -774,13 +818,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
"descend at most <depth> levels", PARSE_OPT_NONEG,
NULL, 1 },
OPT_GROUP(""),
- OPT_BIT('E', "extended-regexp", &opt.regflags,
- "use extended POSIX regular expressions", REG_EXTENDED),
- OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
- "use basic POSIX regular expressions (default)",
- REG_EXTENDED),
- OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
- "interpret patterns as fixed strings"),
+ OPT_SET_INT('E', "extended-regexp", &pattern_type,
+ "use extended POSIX regular expressions",
+ pattern_type_ere),
+ OPT_SET_INT('G', "basic-regexp", &pattern_type,
+ "use basic POSIX regular expressions (default)",
+ pattern_type_bre),
+ OPT_SET_INT('F', "fixed-strings", &pattern_type,
+ "interpret patterns as fixed strings",
+ pattern_type_fixed),
+ OPT_SET_INT('P', "perl-regexp", &pattern_type,
+ "use Perl-compatible regular expressions",
+ pattern_type_pcre),
OPT_GROUP(""),
OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
@@ -799,18 +848,24 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('c', "count", &opt.count,
"show the number of matches instead of matching lines"),
OPT__COLOR(&opt.color, "highlight matches"),
+ OPT_BOOLEAN(0, "break", &opt.file_break,
+ "print empty line between matches from different files"),
+ OPT_BOOLEAN(0, "heading", &opt.heading,
+ "show filename only once above matches from same file"),
OPT_GROUP(""),
- OPT_CALLBACK('C', NULL, &opt, "n",
+ OPT_CALLBACK('C', "context", &opt, "n",
"show <n> context lines before and after matches",
context_callback),
- OPT_INTEGER('B', NULL, &opt.pre_context,
+ OPT_INTEGER('B', "before-context", &opt.pre_context,
"show <n> context lines before matches"),
- OPT_INTEGER('A', NULL, &opt.post_context,
+ OPT_INTEGER('A', "after-context", &opt.post_context,
"show <n> context lines after matches"),
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
context_callback),
OPT_BOOLEAN('p', "show-function", &opt.funcname,
"show a line with the function name before matches"),
+ OPT_BOOLEAN('W', "function-context", &opt.funcbody,
+ "show the surrounding function"),
OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, "file",
"read patterns from file", file_callback),
@@ -869,8 +924,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
strcpy(opt.color_sep, GIT_COLOR_CYAN);
opt.color = -1;
git_config(grep_config, &opt);
- if (opt.color == -1)
- opt.color = git_use_color_default;
/*
* If there is no -- then the paths must exist in the working
@@ -886,6 +939,28 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_STOP_AT_NON_OPTION |
PARSE_OPT_NO_INTERNAL_HELP);
+ switch (pattern_type) {
+ case pattern_type_fixed:
+ opt.fixed = 1;
+ opt.pcre = 0;
+ break;
+ case pattern_type_bre:
+ opt.fixed = 0;
+ opt.pcre = 0;
+ opt.regflags &= ~REG_EXTENDED;
+ break;
+ case pattern_type_ere:
+ opt.fixed = 0;
+ opt.pcre = 0;
+ opt.regflags |= REG_EXTENDED;
+ break;
+ case pattern_type_pcre:
+ opt.fixed = 0;
+ opt.pcre = 1;
+ break;
+ default:
+ break; /* nothing */
+ }
if (use_index && !startup_info->have_repository)
/* die the same way as if we did it at the beginning */
@@ -925,16 +1000,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
die(_("no pattern given."));
if (!opt.fixed && opt.ignore_case)
opt.regflags |= REG_ICASE;
- if ((opt.regflags != REG_NEWLINE) && opt.fixed)
- die(_("cannot mix --fixed-strings and regexp"));
#ifndef NO_PTHREADS
if (online_cpus() == 1 || !grep_threads_ok(&opt))
use_threads = 0;
if (use_threads) {
- if (opt.pre_context || opt.post_context)
- print_hunk_marks_between_files = 1;
+ if (opt.pre_context || opt.post_context || opt.file_break ||
+ opt.funcbody)
+ skip_first_line = 1;
start_threads(&opt);
}
#else
@@ -969,13 +1043,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
verify_filename(prefix, argv[j]);
}
- if (i < argc)
- paths = get_pathspec(prefix, argv + i);
- else if (prefix) {
- paths = xcalloc(2, sizeof(const char *));
- paths[0] = prefix;
- paths[1] = NULL;
- }
+ paths = get_pathspec(prefix, argv + i);
init_pathspec(&pathspec, paths);
pathspec.max_depth = opt.max_depth;
pathspec.recursive = 1;
@@ -1003,13 +1071,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!show_in_pager)
setup_pager();
+ if (!use_index && (untracked || cached))
+ die(_("--cached or --untracked cannot be used with --no-index."));
- if (!use_index) {
- if (cached)
- die(_("--cached cannot be used with --no-index."));
+ if (!use_index || untracked) {
+ int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
if (list.nr)
- die(_("--no-index cannot be used with revs."));
- hit = grep_directory(&opt, &pathspec);
+ die(_("--no-index or --untracked cannot be used with revs."));
+ hit = grep_directory(&opt, &pathspec, use_exclude);
+ } else if (0 <= opt_exclude) {
+ die(_("--[no-]exclude-standard cannot be used for tracked contents."));
} else if (!list.nr) {
if (!cached)
setup_work_tree();
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index b96f46acf5..33911fd5e9 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -14,8 +14,11 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path
{
struct stat st;
unsigned char sha1[20];
+ unsigned flags = (HASH_FORMAT_CHECK |
+ (write_object ? HASH_WRITE_OBJECT : 0));
+
if (fstat(fd, &st) < 0 ||
- index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1))
+ index_fd(sha1, fd, &st, type_from_string(type), path, flags))
die(write_object
? "Unable to add %s to database"
: "Unable to hash %s", path);
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 31f001f105..98025da767 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "builtin.h"
#include "delta.h"
#include "pack.h"
#include "csum-file.h"
@@ -11,7 +11,7 @@
#include "exec_cmd.h"
static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [ --keep | --keep=<msg> ] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
struct object_entry {
struct pack_idx_entry idx;
@@ -19,6 +19,8 @@ struct object_entry {
unsigned int hdr_size;
enum object_type type;
enum object_type real_type;
+ unsigned delta_depth;
+ int base_object_no;
};
union delta_base {
@@ -66,6 +68,7 @@ static struct progress *progress;
static unsigned char input_buffer[4096];
static unsigned int input_offset, input_len;
static off_t consumed_bytes;
+static unsigned deepest_delta;
static git_SHA_CTX input_ctx;
static uint32_t input_crc32;
static int input_fd, output_fd, pack_fd;
@@ -265,7 +268,7 @@ static void unlink_base_data(struct base_data *c)
static void *unpack_entry_data(unsigned long offset, unsigned long size)
{
int status;
- z_stream stream;
+ git_zstream stream;
void *buf = xmalloc(size);
memset(&stream, 0, sizeof(stream));
@@ -355,7 +358,7 @@ static void *get_data_from_pack(struct object_entry *obj)
off_t from = obj[0].idx.offset + obj[0].hdr_size;
unsigned long len = obj[1].idx.offset - from;
unsigned char *data, *inbuf;
- z_stream stream;
+ git_zstream stream;
int status;
data = xmalloc(obj->size);
@@ -389,7 +392,18 @@ static void *get_data_from_pack(struct object_entry *obj)
return data;
}
-static int find_delta(const union delta_base *base)
+static int compare_delta_bases(const union delta_base *base1,
+ const union delta_base *base2,
+ enum object_type type1,
+ enum object_type type2)
+{
+ int cmp = type1 - type2;
+ if (cmp)
+ return cmp;
+ return memcmp(base1, base2, UNION_BASE_SZ);
+}
+
+static int find_delta(const union delta_base *base, enum object_type type)
{
int first = 0, last = nr_deltas;
@@ -398,7 +412,8 @@ static int find_delta(const union delta_base *base)
struct delta_entry *delta = &deltas[next];
int cmp;
- cmp = memcmp(base, &delta->base, UNION_BASE_SZ);
+ cmp = compare_delta_bases(base, &delta->base,
+ type, objects[delta->obj_no].type);
if (!cmp)
return next;
if (cmp < 0) {
@@ -411,9 +426,10 @@ static int find_delta(const union delta_base *base)
}
static void find_delta_children(const union delta_base *base,
- int *first_index, int *last_index)
+ int *first_index, int *last_index,
+ enum object_type type)
{
- int first = find_delta(base);
+ int first = find_delta(base, type);
int last = first;
int end = nr_deltas - 1;
@@ -483,12 +499,17 @@ static void sha1_object(const void *data, unsigned long size,
}
}
+static int is_delta_type(enum object_type type)
+{
+ return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
+}
+
static void *get_base_data(struct base_data *c)
{
if (!c->data) {
struct object_entry *obj = c->obj;
- if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
+ if (is_delta_type(obj->type)) {
void *base = get_base_data(c->base);
void *raw = get_data_from_pack(obj);
c->data = patch_delta(
@@ -515,6 +536,10 @@ static void resolve_delta(struct object_entry *delta_obj,
void *base_data, *delta_data;
delta_obj->real_type = base->obj->real_type;
+ delta_obj->delta_depth = base->obj->delta_depth + 1;
+ if (deepest_delta < delta_obj->delta_depth)
+ deepest_delta = delta_obj->delta_depth;
+ delta_obj->base_object_no = base->obj - objects;
delta_data = get_data_from_pack(delta_obj);
base_data = get_base_data(base);
result->obj = delta_obj;
@@ -541,11 +566,13 @@ static void find_unresolved_deltas(struct base_data *base,
union delta_base base_spec;
hashcpy(base_spec.sha1, base->obj->idx.sha1);
- find_delta_children(&base_spec, &ref_first, &ref_last);
+ find_delta_children(&base_spec,
+ &ref_first, &ref_last, OBJ_REF_DELTA);
memset(&base_spec, 0, sizeof(base_spec));
base_spec.offset = base->obj->idx.offset;
- find_delta_children(&base_spec, &ofs_first, &ofs_last);
+ find_delta_children(&base_spec,
+ &ofs_first, &ofs_last, OBJ_OFS_DELTA);
}
if (ref_last == -1 && ofs_last == -1) {
@@ -557,24 +584,24 @@ static void find_unresolved_deltas(struct base_data *base,
for (i = ref_first; i <= ref_last; i++) {
struct object_entry *child = objects + deltas[i].obj_no;
- if (child->real_type == OBJ_REF_DELTA) {
- struct base_data result;
- resolve_delta(child, base, &result);
- if (i == ref_last && ofs_last == -1)
- free_base_data(base);
- find_unresolved_deltas(&result, base);
- }
+ struct base_data result;
+
+ assert(child->real_type == OBJ_REF_DELTA);
+ resolve_delta(child, base, &result);
+ if (i == ref_last && ofs_last == -1)
+ free_base_data(base);
+ find_unresolved_deltas(&result, base);
}
for (i = ofs_first; i <= ofs_last; i++) {
struct object_entry *child = objects + deltas[i].obj_no;
- if (child->real_type == OBJ_OFS_DELTA) {
- struct base_data result;
- resolve_delta(child, base, &result);
- if (i == ofs_last)
- free_base_data(base);
- find_unresolved_deltas(&result, base);
- }
+ struct base_data result;
+
+ assert(child->real_type == OBJ_OFS_DELTA);
+ resolve_delta(child, base, &result);
+ if (i == ofs_last)
+ free_base_data(base);
+ find_unresolved_deltas(&result, base);
}
unlink_base_data(base);
@@ -584,7 +611,11 @@ static int compare_delta_entry(const void *a, const void *b)
{
const struct delta_entry *delta_a = a;
const struct delta_entry *delta_b = b;
- return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ);
+
+ /* group by type (ref vs ofs) and then by value (sha-1 or offset) */
+ return compare_delta_bases(&delta_a->base, &delta_b->base,
+ objects[delta_a->obj_no].type,
+ objects[delta_b->obj_no].type);
}
/* Parse all objects and return the pack content SHA1 hash */
@@ -608,7 +639,7 @@ static void parse_pack_objects(unsigned char *sha1)
struct object_entry *obj = &objects[i];
void *data = unpack_raw_entry(obj, &delta->base);
obj->real_type = obj->type;
- if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
+ if (is_delta_type(obj->type)) {
nr_deltas++;
delta->obj_no = i;
delta++;
@@ -655,7 +686,7 @@ static void parse_pack_objects(unsigned char *sha1)
struct object_entry *obj = &objects[i];
struct base_data base_obj;
- if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
+ if (is_delta_type(obj->type))
continue;
base_obj.obj = obj;
base_obj.data = NULL;
@@ -666,26 +697,26 @@ static void parse_pack_objects(unsigned char *sha1)
static int write_compressed(struct sha1file *f, void *in, unsigned int size)
{
- z_stream stream;
+ git_zstream stream;
int status;
unsigned char outbuf[4096];
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, zlib_compression_level);
+ git_deflate_init(&stream, zlib_compression_level);
stream.next_in = in;
stream.avail_in = size;
do {
stream.next_out = outbuf;
stream.avail_out = sizeof(outbuf);
- status = deflate(&stream, Z_FINISH);
+ status = git_deflate(&stream, Z_FINISH);
sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
} while (status == Z_OK);
if (status != Z_STREAM_END)
die("unable to deflate appended object (%d)", status);
size = stream.total_out;
- deflateEnd(&stream);
+ git_deflate_end(&stream);
return size;
}
@@ -859,24 +890,137 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
static int git_index_pack_config(const char *k, const char *v, void *cb)
{
+ struct pack_idx_option *opts = cb;
+
if (!strcmp(k, "pack.indexversion")) {
- pack_idx_default_version = git_config_int(k, v);
- if (pack_idx_default_version > 2)
- die("bad pack.indexversion=%"PRIu32,
- pack_idx_default_version);
+ opts->version = git_config_int(k, v);
+ if (opts->version > 2)
+ die("bad pack.indexversion=%"PRIu32, opts->version);
return 0;
}
return git_default_config(k, v, cb);
}
+static int cmp_uint32(const void *a_, const void *b_)
+{
+ uint32_t a = *((uint32_t *)a_);
+ uint32_t b = *((uint32_t *)b_);
+
+ return (a < b) ? -1 : (a != b);
+}
+
+static void read_v2_anomalous_offsets(struct packed_git *p,
+ struct pack_idx_option *opts)
+{
+ const uint32_t *idx1, *idx2;
+ uint32_t i;
+
+ /* The address of the 4-byte offset table */
+ idx1 = (((const uint32_t *)p->index_data)
+ + 2 /* 8-byte header */
+ + 256 /* fan out */
+ + 5 * p->num_objects /* 20-byte SHA-1 table */
+ + p->num_objects /* CRC32 table */
+ );
+
+ /* The address of the 8-byte offset table */
+ idx2 = idx1 + p->num_objects;
+
+ for (i = 0; i < p->num_objects; i++) {
+ uint32_t off = ntohl(idx1[i]);
+ if (!(off & 0x80000000))
+ continue;
+ off = off & 0x7fffffff;
+ if (idx2[off * 2])
+ continue;
+ /*
+ * The real offset is ntohl(idx2[off * 2]) in high 4
+ * octets, and ntohl(idx2[off * 2 + 1]) in low 4
+ * octets. But idx2[off * 2] is Zero!!!
+ */
+ ALLOC_GROW(opts->anomaly, opts->anomaly_nr + 1, opts->anomaly_alloc);
+ 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);
+}
+
+static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
+{
+ struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1);
+
+ if (!p)
+ die("Cannot open existing pack file '%s'", pack_name);
+ if (open_pack_index(p))
+ die("Cannot open existing pack idx file for '%s'", pack_name);
+
+ /* Read the attributes from the existing idx file */
+ opts->version = p->index_version;
+
+ if (opts->version == 2)
+ read_v2_anomalous_offsets(p, opts);
+
+ /*
+ * Get rid of the idx file as we do not need it anymore.
+ * NEEDSWORK: extract this bit from free_pack_by_name() in
+ * sha1_file.c, perhaps? It shouldn't matter very much as we
+ * know we haven't installed this pack (hence we never have
+ * read anything from it).
+ */
+ close_pack_index(p);
+ free(p);
+}
+
+static void show_pack_info(int stat_only)
+{
+ int i, baseobjects = nr_objects - nr_deltas;
+ unsigned long *chain_histogram = NULL;
+
+ if (deepest_delta)
+ chain_histogram = xcalloc(deepest_delta, sizeof(unsigned long));
+
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *obj = &objects[i];
+
+ if (is_delta_type(obj->type))
+ chain_histogram[obj->delta_depth - 1]++;
+ if (stat_only)
+ continue;
+ printf("%s %-6s %lu %lu %"PRIuMAX,
+ sha1_to_hex(obj->idx.sha1),
+ typename(obj->real_type), obj->size,
+ (unsigned long)(obj[1].idx.offset - obj->idx.offset),
+ (uintmax_t)obj->idx.offset);
+ if (is_delta_type(obj->type)) {
+ struct object_entry *bobj = &objects[obj->base_object_no];
+ printf(" %u %s", obj->delta_depth, sha1_to_hex(bobj->idx.sha1));
+ }
+ putchar('\n');
+ }
+
+ if (baseobjects)
+ printf("non delta: %d object%s\n",
+ baseobjects, baseobjects > 1 ? "s" : "");
+ for (i = 0; i < deepest_delta; i++) {
+ if (!chain_histogram[i])
+ continue;
+ printf("chain length = %d: %lu object%s\n",
+ i + 1,
+ chain_histogram[i],
+ chain_histogram[i] > 1 ? "s" : "");
+ }
+}
+
int cmd_index_pack(int argc, const char **argv, const char *prefix)
{
- int i, fix_thin_pack = 0;
+ int i, fix_thin_pack = 0, verify = 0, stat_only = 0, stat = 0;
const char *curr_pack, *curr_index;
const char *index_name = NULL, *pack_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
char *index_name_buf = NULL, *keep_name_buf = NULL;
struct pack_idx_entry **idx_objects;
+ struct pack_idx_option opts;
unsigned char pack_sha1[20];
if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -884,7 +1028,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
read_replace_refs = 0;
- git_config(git_index_pack_config, NULL);
+ reset_pack_idx_option(&opts);
+ git_config(git_index_pack_config, &opts);
if (prefix && chdir(prefix))
die("Cannot come back to cwd");
@@ -898,6 +1043,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
fix_thin_pack = 1;
} else if (!strcmp(arg, "--strict")) {
strict = 1;
+ } else if (!strcmp(arg, "--verify")) {
+ verify = 1;
+ } else if (!strcmp(arg, "--verify-stat")) {
+ verify = 1;
+ stat = 1;
+ } else if (!strcmp(arg, "--verify-stat-only")) {
+ verify = 1;
+ stat = 1;
+ stat_only = 1;
} else if (!strcmp(arg, "--keep")) {
keep_msg = "";
} else if (!prefixcmp(arg, "--keep=")) {
@@ -923,12 +1077,12 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
index_name = argv[++i];
} else if (!prefixcmp(arg, "--index-version=")) {
char *c;
- pack_idx_default_version = strtoul(arg + 16, &c, 10);
- if (pack_idx_default_version > 2)
+ opts.version = strtoul(arg + 16, &c, 10);
+ if (opts.version > 2)
die("bad %s", arg);
if (*c == ',')
- pack_idx_off32_limit = strtoul(c+1, &c, 0);
- if (*c || pack_idx_off32_limit & 0x80000000)
+ opts.off32_limit = strtoul(c+1, &c, 0);
+ if (*c || opts.off32_limit & 0x80000000)
die("bad %s", arg);
} else
usage(index_pack_usage);
@@ -964,11 +1118,19 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
strcpy(keep_name_buf + len - 5, ".keep");
keep_name = keep_name_buf;
}
+ if (verify) {
+ if (!index_name)
+ die("--verify with no packfile name given");
+ read_idx_option(&opts, index_name);
+ opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
+ }
+ if (strict)
+ opts.flags |= WRITE_IDX_STRICT;
curr_pack = open_pack_file(pack_name);
parse_pack_header();
- objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));
- deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
+ objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
+ deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
parse_pack_objects(pack_sha1);
if (nr_deltas == nr_resolved_deltas) {
stop_progress(&progress);
@@ -1008,16 +1170,22 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (strict)
check_objects();
+ if (stat)
+ show_pack_info(stat_only);
+
idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
for (i = 0; i < nr_objects; i++)
idx_objects[i] = &objects[i].idx;
- curr_index = write_idx_file(index_name, idx_objects, nr_objects, pack_sha1);
+ curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_sha1);
free(idx_objects);
- final(pack_name, curr_pack,
- index_name, curr_index,
- keep_name, keep_msg,
- pack_sha1);
+ if (!verify)
+ final(pack_name, curr_pack,
+ index_name, curr_index,
+ keep_name, keep_msg,
+ pack_sha1);
+ else
+ close(input_fd);
free(objects);
free(index_name_buf);
free(keep_name_buf);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index b7370d9bb8..0dacb8b79c 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -319,10 +319,10 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir,
struct stat st;
if (!exist_ok && !stat(git_dir, &st))
- die("%s already exists", git_dir);
+ die(_("%s already exists"), git_dir);
if (!exist_ok && !stat(real_git_dir, &st))
- die("%s already exists", real_git_dir);
+ die(_("%s already exists"), real_git_dir);
/*
* make sure symlinks are resolved because we'll be
@@ -347,19 +347,19 @@ static void separate_git_dir(const char *git_dir)
const char *src;
if (S_ISREG(st.st_mode))
- src = read_gitfile_gently(git_link);
+ src = read_gitfile(git_link);
else if (S_ISDIR(st.st_mode))
src = git_link;
else
- die("unable to handle file type %d", st.st_mode);
+ die(_("unable to handle file type %d"), (int)st.st_mode);
if (rename(src, git_dir))
- die_errno("unable to move %s to %s", src, git_dir);
+ die_errno(_("unable to move %s to %s"), src, git_dir);
}
fp = fopen(git_link, "w");
if (!fp)
- die("Could not create git link %s", git_link);
+ die(_("Could not create git link %s"), git_link);
fprintf(fp, "gitdir: %s\n", git_dir);
fclose(fp);
}
@@ -490,7 +490,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
"specify that the git repository is to be shared amongst several users",
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
- OPT_STRING('L', "separate-git-dir", &real_git_dir, "gitdir",
+ OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
"separate git dir from working tree"),
OPT_END()
};
diff --git a/builtin/log.c b/builtin/log.c
index 9db43edb06..56bc555d11 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -23,14 +23,18 @@
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
+static int default_abbrev_commit;
static int default_show_root = 1;
static int decoration_style;
+static int decoration_given;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
-static const char * const builtin_log_usage =
+static const char * const builtin_log_usage[] = {
"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
- " or: git show [options] <object>...";
+ " or: git show [options] <object>...",
+ NULL
+};
static int parse_decoration_style(const char *var, const char *value)
{
@@ -49,33 +53,64 @@ static int parse_decoration_style(const char *var, const char *value)
return -1;
}
-static void cmd_log_init(int argc, const char **argv, const char *prefix,
- struct rev_info *rev, struct setup_revision_opt *opt)
+static int decorate_callback(const struct option *opt, const char *arg, int unset)
{
- int i;
- int decoration_given = 0;
- struct userformat_want w;
+ if (unset)
+ decoration_style = 0;
+ else if (arg)
+ decoration_style = parse_decoration_style("command line", arg);
+ else
+ decoration_style = DECORATE_SHORT_REFS;
+
+ if (decoration_style < 0)
+ die("invalid --decorate option: %s", arg);
+
+ decoration_given = 1;
+
+ return 0;
+}
- rev->abbrev = DEFAULT_ABBREV;
- rev->commit_format = CMIT_FMT_DEFAULT;
+static void cmd_log_init_defaults(struct rev_info *rev)
+{
if (fmt_pretty)
get_commit_format(fmt_pretty, rev);
rev->verbose_header = 1;
DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+ rev->abbrev_commit = default_abbrev_commit;
rev->show_root_diff = default_show_root;
rev->subject_prefix = fmt_patch_subject_prefix;
DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
if (default_date_mode)
rev->date_mode = parse_date_format(default_date_mode);
+}
+
+static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
+ struct rev_info *rev, struct setup_revision_opt *opt)
+{
+ struct userformat_want w;
+ int quiet = 0, source = 0;
+
+ const struct option builtin_log_options[] = {
+ OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
+ OPT_BOOLEAN(0, "source", &source, "show source"),
+ { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
+ PARSE_OPT_OPTARG, decorate_callback},
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_log_options, builtin_log_usage,
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+ PARSE_OPT_KEEP_DASHDASH);
- /*
- * Check for -h before setup_revisions(), or "git log -h" will
- * fail when run without a git directory.
- */
- if (argc == 2 && !strcmp(argv[1], "-h"))
- usage(builtin_log_usage);
argc = setup_revisions(argc, argv, rev, opt);
+ if (quiet)
+ rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
+
+ /* Any arguments at this point are not recognized */
+ if (argc > 1)
+ die("unrecognized argument: %s", argv[1]);
memset(&w, 0, sizeof(w));
userformat_find_requirements(NULL, &w);
@@ -92,35 +127,21 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
if (rev->diffopt.pathspec.nr != 1)
usage("git logs can only follow renames on one pathname at a time");
}
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "--decorate")) {
- decoration_style = DECORATE_SHORT_REFS;
- decoration_given = 1;
- } else if (!prefixcmp(arg, "--decorate=")) {
- const char *v = skip_prefix(arg, "--decorate=");
- decoration_style = parse_decoration_style(arg, v);
- if (decoration_style < 0)
- die(_("invalid --decorate option: %s"), arg);
- decoration_given = 1;
- } else if (!strcmp(arg, "--no-decorate")) {
+
+ if (source)
+ rev->show_source = 1;
+
+ if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
+ /*
+ * "log --pretty=raw" is special; ignore UI oriented
+ * configuration variables such as decoration.
+ */
+ if (!decoration_given)
decoration_style = 0;
- } else if (!strcmp(arg, "--source")) {
- rev->show_source = 1;
- } else if (!strcmp(arg, "-h")) {
- usage(builtin_log_usage);
- } else
- die(_("unrecognized argument: %s"), arg);
+ if (!rev->abbrev_commit_given)
+ rev->abbrev_commit = 0;
}
- /*
- * defeat log.decorate configuration interacting with --pretty=raw
- * from the command line.
- */
- if (!decoration_given && rev->pretty_given
- && rev->commit_format == CMIT_FMT_RAW)
- decoration_style = 0;
-
if (decoration_style) {
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
@@ -128,6 +149,13 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
setup_pager();
}
+static void cmd_log_init(int argc, const char **argv, const char *prefix,
+ struct rev_info *rev, struct setup_revision_opt *opt)
+{
+ cmd_log_init_defaults(rev);
+ cmd_log_init_finish(argc, argv, prefix, rev, opt);
+}
+
/*
* This gives a rough estimate for how many commits we
* will print out in the list.
@@ -247,6 +275,8 @@ static void finish_early_output(struct rev_info *rev)
static int cmd_log_walk(struct rev_info *rev)
{
struct commit *commit;
+ int saved_nrl = 0;
+ int saved_dcctc = 0;
if (rev->early_output)
setup_early_output(rev);
@@ -277,7 +307,14 @@ static int cmd_log_walk(struct rev_info *rev)
}
free_commit_list(commit->parents);
commit->parents = NULL;
+ if (saved_nrl < rev->diffopt.needed_rename_limit)
+ saved_nrl = rev->diffopt.needed_rename_limit;
+ if (rev->diffopt.degraded_cc_to_c)
+ saved_dcctc = 1;
}
+ rev->diffopt.degraded_cc_to_c = saved_dcctc;
+ rev->diffopt.needed_rename_limit = saved_nrl;
+
if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
return 02;
@@ -291,6 +328,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
return git_config_string(&fmt_pretty, var, value);
if (!strcmp(var, "format.subjectprefix"))
return git_config_string(&fmt_patch_subject_prefix, var, value);
+ if (!strcmp(var, "log.abbrevcommit")) {
+ default_abbrev_commit = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
if (!strcmp(var, "log.decorate")) {
@@ -316,9 +357,6 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
git_config(git_log_config, NULL);
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
init_revisions(&rev, prefix);
rev.diff = 1;
rev.simplify_history = 0;
@@ -333,9 +371,11 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
static void show_tagger(char *buf, int len, struct rev_info *rev)
{
struct strbuf out = STRBUF_INIT;
+ struct pretty_print_context pp = {0};
- pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
- get_log_output_encoding());
+ 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);
strbuf_release(&out);
}
@@ -396,13 +436,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct object_array_entry *objects;
struct setup_revision_opt opt;
+ struct pathspec match_all;
int i, count, ret = 0;
git_config(git_log_config, NULL);
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
+ init_pathspec(&match_all, NULL);
init_revisions(&rev, prefix);
rev.diff = 1;
rev.always_show_header = 1;
@@ -449,7 +488,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
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, NULL,
+ read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
show_tree_object, NULL);
rev.shown_one = 1;
break;
@@ -477,25 +516,17 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
git_config(git_log_config, NULL);
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
init_revisions(&rev, prefix);
init_reflog_walk(&rev.reflog_info);
- rev.abbrev_commit = 1;
rev.verbose_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
- cmd_log_init(argc, argv, prefix, &rev, &opt);
-
- /*
- * This means that we override whatever commit format the user gave
- * on the cmd line. Sad, but cmd_log_init() currently doesn't
- * allow us to set a different default.
- */
+ cmd_log_init_defaults(&rev);
+ rev.abbrev_commit = 1;
rev.commit_format = CMIT_FMT_ONELINE;
rev.use_terminator = 1;
rev.always_show_header = 1;
+ cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
return cmd_log_walk(&rev);
}
@@ -507,9 +538,6 @@ int cmd_log(int argc, const char **argv, const char *prefix)
git_config(git_log_config, NULL);
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
init_revisions(&rev, prefix);
rev.always_show_header = 1;
memset(&opt, 0, sizeof(opt));
@@ -578,7 +606,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
string_list_append(&extra_cc, value);
return 0;
}
- if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
+ if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
+ !strcmp(var, "color.ui")) {
return 0;
}
if (!strcmp(var, "format.numbered")) {
@@ -623,7 +652,7 @@ static FILE *realstdout = NULL;
static const char *output_directory = NULL;
static int outdir_offset;
-static int reopen_stdout(struct commit *commit, struct rev_info *rev)
+static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet)
{
struct strbuf filename = STRBUF_INIT;
int suffix_len = strlen(fmt_patch_suffix) + 1;
@@ -639,7 +668,7 @@ static int reopen_stdout(struct commit *commit, struct rev_info *rev)
get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
- if (!DIFF_OPT_TST(&rev->diffopt, QUICK))
+ if (!quiet)
fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
if (freopen(filename.buf, "w", stdout) == NULL)
@@ -718,13 +747,12 @@ static void print_signature(void)
static void make_cover_letter(struct rev_info *rev, int use_stdout,
int numbered, int numbered_files,
struct commit *origin,
- int nr, struct commit **list, struct commit *head)
+ int nr, struct commit **list, struct commit *head,
+ int quiet)
{
const char *committer;
- const char *subject_start = NULL;
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
const char *msg;
- const char *extra_headers = rev->extra_headers;
struct shortlog log;
struct strbuf sb = STRBUF_INIT;
int i;
@@ -732,6 +760,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct diff_options opts;
int need_8bit_cte = 0;
struct commit *commit = NULL;
+ struct pretty_print_context pp = {0};
if (rev->commit_format != CMIT_FMT_EMAIL)
die(_("Cover letter needs email format"));
@@ -754,7 +783,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
sha1_to_hex(head->object.sha1), committer, committer);
}
- if (!use_stdout && reopen_stdout(commit, rev))
+ if (!use_stdout && reopen_stdout(commit, rev, quiet))
return;
if (commit) {
@@ -763,7 +792,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
free(commit);
}
- log_write_email_headers(rev, head, &subject_start, &extra_headers,
+ log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
&need_8bit_cte);
for (i = 0; !need_8bit_cte && i < nr; i++)
@@ -771,11 +800,11 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
need_8bit_cte = 1;
msg = body;
- pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
- encoding);
- pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
- encoding, need_8bit_cte);
- pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
+ pp.fmt = CMIT_FMT_EMAIL;
+ pp.date_mode = DATE_RFC2822;
+ pp_user_info(&pp, NULL, &sb, committer, encoding);
+ pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
+ pp_remainder(&pp, &msg, &sb, 0);
printf("%s\n", sb.buf);
strbuf_release(&sb);
@@ -995,6 +1024,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
char *add_signoff = NULL;
struct strbuf buf = STRBUF_INIT;
int use_patch_format = 0;
+ int quiet = 0;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
"use [PATCH n/m] even with a single patch",
@@ -1050,6 +1080,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_OPTARG, thread_callback },
OPT_STRING(0, "signature", &signature, "signature",
"add a signature"),
+ OPT_BOOLEAN(0, "quiet", &quiet,
+ "don't print the patch filenames"),
OPT_END()
};
@@ -1136,6 +1168,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
die (_("-n and -k are mutually exclusive."));
if (keep_subject && subject_prefix)
die (_("--subject-prefix and -k are mutually exclusive."));
+ rev.preserve_subject = keep_subject;
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
if (argc > 1)
@@ -1259,7 +1292,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout, numbered, numbered_files,
- origin, nr, list, head);
+ origin, nr, list, head, quiet);
total++;
start_number--;
}
@@ -1305,7 +1338,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
- &rev))
+ &rev, quiet))
die(_("Failed to create output files"));
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
@@ -1366,8 +1399,7 @@ static void print_commit(char sign, struct commit *commit, int verbose,
find_unique_abbrev(commit->object.sha1, abbrev));
} else {
struct strbuf buf = STRBUF_INIT;
- struct pretty_print_context ctx = {0};
- pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
printf("%c %s %s\n", sign,
find_unique_abbrev(commit->object.sha1, abbrev),
buf.buf);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index fb2d5f4b1f..7cff175745 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -276,41 +276,6 @@ static void prune_cache(const char *prefix)
active_nr = last;
}
-static const char *pathspec_prefix(const char *prefix)
-{
- const char **p, *n, *prev;
- unsigned long max;
-
- if (!pathspec) {
- max_prefix_len = prefix ? strlen(prefix) : 0;
- return prefix;
- }
-
- prev = NULL;
- max = PATH_MAX;
- for (p = pathspec; (n = *p) != NULL; p++) {
- int i, len = 0;
- for (i = 0; i < max; i++) {
- char c = n[i];
- if (prev && prev[i] != c)
- break;
- if (!c || c == '*' || c == '?')
- break;
- if (c == '/')
- len = i+1;
- }
- prev = n;
- if (len < max) {
- max = len;
- if (!max)
- break;
- }
- }
-
- max_prefix_len = max;
- return max ? xmemdupz(prev, max) : NULL;
-}
-
static void strip_trailing_slash_from_submodules(void)
{
const char **p;
@@ -338,7 +303,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
{
struct tree *tree;
unsigned char sha1[20];
- const char **match;
+ struct pathspec pathspec;
struct cache_entry *last_stage0 = NULL;
int i;
@@ -360,10 +325,11 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
static const char *(matchbuf[2]);
matchbuf[0] = prefix;
matchbuf[1] = NULL;
- match = matchbuf;
+ init_pathspec(&pathspec, matchbuf);
+ pathspec.items[0].use_wildcard = 0;
} else
- match = NULL;
- if (read_tree(tree, 1, match))
+ init_pathspec(&pathspec, NULL);
+ if (read_tree(tree, 1, &pathspec))
die("unable to read tree entries %s", tree_name);
for (i = 0; i < active_nr; i++) {
@@ -387,11 +353,13 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
}
}
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
+int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix)
{
/*
* Make sure all pathspec matched; otherwise it is an error.
*/
+ struct strbuf sb = STRBUF_INIT;
+ const char *name;
int num, errors = 0;
for (num = 0; pathspec[num]; num++) {
int other, found_dup;
@@ -416,10 +384,12 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
if (found_dup)
continue;
+ name = quote_path_relative(pathspec[num], -1, &sb, prefix);
error("pathspec '%s' did not match any file(s) known to git.",
- pathspec[num] + prefix_len);
+ name);
errors++;
}
+ strbuf_release(&sb);
return errors;
}
@@ -575,7 +545,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
strip_trailing_slash_from_submodules();
/* Find common prefix for all pathspec's */
- max_prefix = pathspec_prefix(prefix);
+ max_prefix = common_prefix(pathspec);
+ max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
@@ -610,7 +581,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (ps_matched) {
int bad;
- bad = report_path_error(ps_matched, pathspec, prefix_len);
+ bad = report_path_error(ps_matched, pathspec, prefix);
if (bad)
fprintf(stderr, "Did you forget to 'git add'?\n");
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 1a1ff87e8f..41c88a98a2 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -5,7 +5,7 @@
static const char ls_remote_usage[] =
"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n"
-" [-q|--quiet] [<repository> [<refs>...]]";
+" [-q|--quiet] [--exit-code] [<repository> [<refs>...]]";
/*
* Is there one among the list of patterns that match the tail part
@@ -35,6 +35,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
unsigned flags = 0;
int get_url = 0;
int quiet = 0;
+ int status = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
@@ -42,6 +43,9 @@ 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);
+
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -74,6 +78,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
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;
@@ -121,6 +130,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (!tail_match(pattern, ref->name))
continue;
printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name);
+ status = 0; /* we found something */
}
- return 0;
+ return status;
}
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index f73e6bd962..6b666e1e87 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -19,7 +19,7 @@ static int line_termination = '\n';
#define LS_SHOW_SIZE 16
static int abbrev;
static int ls_options;
-static const char **pathspec;
+static struct pathspec pathspec;
static int chomp_prefix;
static const char *ls_tree_prefix;
@@ -35,7 +35,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
if (ls_options & LS_RECURSIVE)
return 1;
- s = pathspec;
+ s = pathspec.raw;
if (!s)
return 0;
@@ -120,7 +120,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
{
unsigned char sha1[20];
struct tree *tree;
- int full_tree = 0;
+ int i, full_tree = 0;
const struct option ls_tree_options[] = {
OPT_BIT('d', NULL, &ls_options, "only show trees",
LS_TREE_ONLY),
@@ -166,11 +166,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
if (get_sha1(argv[0], sha1))
die("Not a valid object name %s", argv[0]);
- pathspec = get_pathspec(prefix, argv + 1);
+ init_pathspec(&pathspec, get_pathspec(prefix, argv + 1));
+ for (i = 0; i < pathspec.nr; i++)
+ pathspec.items[i].use_wildcard = 0;
+ pathspec.has_wildcard = 0;
tree = parse_tree_indirect(sha1);
if (!tree)
die("not a tree object");
- read_tree_recursive(tree, "", 0, 0, pathspec, show_tree, NULL);
-
- return 0;
+ return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
}
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 71e6262a87..bfb32b7233 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -400,7 +400,7 @@ static int read_one_header_line(struct strbuf *line, FILE *in)
break;
if (strbuf_getline(&continuation, in, '\n'))
break;
- continuation.buf[0] = '\n';
+ continuation.buf[0] = ' ';
strbuf_rtrim(&continuation);
strbuf_addbuf(line, &continuation);
}
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 96dd160731..4f30f1b0c8 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -23,7 +23,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
}
static const char * const merge_base_usage[] = {
- "git merge-base [-a|--all] [--octopus] <commit> <commit>...",
+ "git merge-base [-a|--all] <commit> <commit>...",
+ "git merge-base [-a|--all] --octopus <commit>...",
"git merge-base --independent <commit>...",
NULL
};
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 19917426fb..897a563bc6 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -3,6 +3,7 @@
#include "xdiff-interface.h"
#include "blob.h"
#include "exec_cmd.h"
+#include "merge-file.h"
static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
static int resolve_directories = 1;
@@ -54,8 +55,6 @@ static const char *explanation(struct merge_list *entry)
return "removed in remote";
}
-extern void *merge_file(const char *, struct blob *, struct blob *, struct blob *, unsigned long *);
-
static void *result(struct merge_list *entry, unsigned long *size)
{
enum object_type type;
diff --git a/builtin/merge.c b/builtin/merge.c
index d54e7ddbb1..7d92e2064b 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -46,16 +46,16 @@ static const char * const builtin_merge_usage[] = {
static int show_diffstat = 1, shortlog_len, squash;
static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only;
+static int fast_forward_only, option_edit;
static int allow_trivial = 1, have_message;
-static struct strbuf merge_msg;
+static struct strbuf merge_msg = STRBUF_INIT;
static struct commit_list *remoteheads;
-static unsigned char head[20], stash[20];
static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
static const char **xopts;
static size_t xopts_nr, xopts_alloc;
static const char *branch;
+static char *branch_mergeoptions;
static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
@@ -189,6 +189,8 @@ static struct option builtin_merge_options[] = {
"create a single commit instead of doing a merge"),
OPT_BOOLEAN(0, "commit", &option_commit,
"perform a commit if the merge succeeds (default)"),
+ OPT_BOOLEAN('e', "edit", &option_edit,
+ "edit message before committing"),
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
"allow fast-forward (default)"),
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
@@ -216,7 +218,7 @@ static void drop_save(void)
unlink(git_path("MERGE_MODE"));
}
-static void save_state(void)
+static int save_state(unsigned char *stash)
{
int len;
struct child_process cp;
@@ -235,11 +237,12 @@ static void save_state(void)
if (finish_command(&cp) || len < 0)
die(_("stash failed"));
- else if (!len)
- return;
+ else if (!len) /* no changes */
+ return -1;
strbuf_setlen(&buffer, buffer.len-1);
if (get_sha1(buffer.buf, stash))
die(_("not a valid object: %s"), buffer.buf);
+ return 0;
}
static void read_empty(unsigned const char *sha1, int verbose)
@@ -277,7 +280,8 @@ static void reset_hard(unsigned const char *sha1, int verbose)
die(_("read-tree failed"));
}
-static void restore_state(void)
+static void restore_state(const unsigned char *head,
+ const unsigned char *stash)
{
struct strbuf sb = STRBUF_INIT;
const char *args[] = { "stash", "apply", NULL, NULL };
@@ -307,25 +311,25 @@ static void finish_up_to_date(const char *msg)
drop_save();
}
-static void squash_message(void)
+static void squash_message(struct commit *commit)
{
struct rev_info rev;
- struct commit *commit;
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"));
- fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("SQUASH_MSG");
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG"));
+ die_errno(_("Could not write to '%s'"), filename);
init_revisions(&rev, NULL);
rev.ignore_merges = 1;
rev.commit_format = CMIT_FMT_MEDIUM;
- commit = lookup_commit(head);
commit->object.flags |= UNINTERESTING;
add_pending_object(&rev, &commit->object, NULL);
@@ -338,13 +342,14 @@ static void squash_message(void)
ctx.abbrev = rev.abbrev;
ctx.date_mode = rev.date_mode;
+ ctx.fmt = rev.commit_format;
strbuf_addstr(&out, "Squashed commit of the following:\n");
while ((commit = get_revision(&rev)) != NULL) {
strbuf_addch(&out, '\n');
strbuf_addf(&out, "commit %s\n",
sha1_to_hex(commit->object.sha1));
- pretty_print_commit(rev.commit_format, commit, &out, &ctx);
+ pretty_print_commit(&ctx, commit, &out);
}
if (write(fd, out.buf, out.len) < 0)
die_errno(_("Writing SQUASH_MSG"));
@@ -353,9 +358,11 @@ static void squash_message(void)
strbuf_release(&out);
}
-static void finish(const unsigned char *new_head, const char *msg)
+static void finish(struct commit *head_commit,
+ const unsigned char *new_head, const char *msg)
{
struct strbuf reflog_message = STRBUF_INIT;
+ const unsigned char *head = head_commit->object.sha1;
if (!msg)
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
@@ -366,7 +373,7 @@ static void finish(const unsigned char *new_head, const char *msg)
getenv("GIT_REFLOG_ACTION"), msg);
}
if (squash) {
- squash_message();
+ squash_message(head_commit);
} else {
if (verbosity >= 0 && !merge_msg.len)
printf(_("No merge message -- not updating HEAD\n"));
@@ -388,8 +395,6 @@ static void finish(const unsigned char *new_head, const char *msg)
opts.output_format |=
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
opts.detect_rename = DIFF_DETECT_RENAME;
- if (diff_use_color_default > 0)
- DIFF_OPT_SET(&opts, COLOR_DIFF);
if (diff_setup_done(&opts) < 0)
die(_("diff_setup_done failed"));
diff_tree_sha1(head, new_head, "", &opts);
@@ -403,6 +408,16 @@ static void finish(const unsigned char *new_head, const char *msg)
strbuf_release(&reflog_message);
}
+static struct object *want_commit(const char *name)
+{
+ struct object *obj;
+ unsigned char sha1[20];
+ if (get_sha1(name, sha1))
+ return NULL;
+ obj = parse_object(sha1);
+ return peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
/* Get the name for the merge commit's message. */
static void merge_name(const char *remote, struct strbuf *msg)
{
@@ -418,7 +433,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
remote = bname.buf;
memset(branch_head, 0, sizeof(branch_head));
- remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+ remote_head = want_commit(remote);
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
@@ -479,14 +494,16 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!strcmp(remote, "FETCH_HEAD") &&
!access(git_path("FETCH_HEAD"), R_OK)) {
+ const char *filename;
FILE *fp;
struct strbuf line = STRBUF_INIT;
char *ptr;
- fp = fopen(git_path("FETCH_HEAD"), "r");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "r");
if (!fp)
die_errno(_("could not open '%s' for reading"),
- git_path("FETCH_HEAD"));
+ filename);
strbuf_getline(&line, fp, '\n');
fclose(fp);
ptr = strstr(line.buf, "\tnot-for-merge\t");
@@ -503,26 +520,34 @@ cleanup:
strbuf_release(&bname);
}
+static void parse_branch_merge_options(char *bmo)
+{
+ const char **argv;
+ int argc;
+
+ if (!bmo)
+ return;
+ argc = split_cmdline(bmo, &argv);
+ if (argc < 0)
+ die(_("Bad branch.%s.mergeoptions string: %s"), branch,
+ split_cmdline_strerror(argc));
+ argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+ memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+ argc++;
+ argv[0] = "branch.*.mergeoptions";
+ parse_options(argc, argv, NULL, builtin_merge_options,
+ builtin_merge_usage, 0);
+ free(argv);
+}
+
static int git_merge_config(const char *k, const char *v, void *cb)
{
if (branch && !prefixcmp(k, "branch.") &&
!prefixcmp(k + 7, branch) &&
!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
- const char **argv;
- int argc;
- char *buf;
-
- buf = xstrdup(v);
- argc = split_cmdline(buf, &argv);
- if (argc < 0)
- die(_("Bad branch.%s.mergeoptions string: %s"), branch,
- split_cmdline_strerror(argc));
- argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
- memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
- argc++;
- parse_options(argc, argv, NULL, builtin_merge_options,
- builtin_merge_usage, 0);
- free(buf);
+ free(branch_mergeoptions);
+ branch_mergeoptions = xstrdup(v);
+ return 0;
}
if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
@@ -541,6 +566,15 @@ static int git_merge_config(const char *k, const char *v, void *cb)
if (is_bool && shortlog_len)
shortlog_len = DEFAULT_MERGE_LOG_LEN;
return 0;
+ } else if (!strcmp(k, "merge.ff")) {
+ int boolval = git_config_maybe_bool(k, v);
+ if (0 <= boolval) {
+ allow_fast_forward = boolval;
+ } else if (v && !strcmp(v, "only")) {
+ allow_fast_forward = 1;
+ fast_forward_only = 1;
+ } /* do not barf on values from future versions of git */
+ return 0;
} else if (!strcmp(k, "merge.defaulttoupstream")) {
default_to_upstream = git_config_bool(k, v);
return 0;
@@ -590,6 +624,14 @@ static void write_tree_trivial(unsigned char *sha1)
die(_("git write-tree failed to write a tree"));
}
+static const char *merge_argument(struct commit *commit)
+{
+ if (commit)
+ return sha1_to_hex(commit->object.sha1);
+ else
+ return EMPTY_TREE_SHA1_HEX;
+}
+
int try_merge_command(const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes)
@@ -610,11 +652,11 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
args[i++] = s;
}
for (j = common; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i++] = xstrdup(merge_argument(j->item));
args[i++] = "--";
args[i++] = head_arg;
for (j = remotes; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i++] = xstrdup(merge_argument(j->item));
args[i] = NULL;
ret = run_command_v_opt(args, RUN_GIT_CMD);
strbuf_release(&buf);
@@ -636,7 +678,7 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
}
static int try_merge_strategy(const char *strategy, struct commit_list *common,
- const char *head_arg)
+ struct commit *head, const char *head_arg)
{
int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
@@ -682,7 +724,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
commit_list_insert(j->item, &reversed);
index_fd = hold_locked_index(lock, 1);
- clean = merge_recursive(&o, lookup_commit(head),
+ clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
if (active_cache_changed &&
(write_cache(index_fd, active_cache, active_nr) ||
@@ -733,7 +775,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
memset(&t, 0, sizeof(t));
memset(&dir, 0, sizeof(dir));
dir.flags |= DIR_SHOW_IGNORED;
- dir.exclude_per_dir = ".gitignore";
+ setup_standard_excludes(&dir);
opts.dir = &dir;
opts.head_idx = 1;
@@ -807,51 +849,78 @@ static void add_strategies(const char *string, unsigned attr)
}
-static void write_merge_msg(void)
+static void write_merge_msg(struct strbuf *msg)
{
- int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+ 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"),
- git_path("MERGE_MSG"));
- if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG"));
+ 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(void)
+static void read_merge_msg(struct strbuf *msg)
{
- strbuf_reset(&merge_msg);
- if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0)
- die_errno("Could not read from '%s'", git_path("MERGE_MSG"));
+ const char *filename = git_path("MERGE_MSG");
+ strbuf_reset(msg);
+ if (strbuf_read_file(msg, filename, 0) < 0)
+ die_errno(_("Could not read from '%s'"), filename);
}
-static void run_prepare_commit_msg(void)
+static void write_merge_state(void);
+static void abort_commit(const char *err_msg)
{
- write_merge_msg();
+ if (err_msg)
+ error("%s", err_msg);
+ fprintf(stderr,
+ _("Not committing merge; use 'git commit' to complete the merge.\n"));
+ write_merge_state();
+ exit(1);
+}
+
+static void prepare_to_commit(void)
+{
+ struct strbuf msg = STRBUF_INIT;
+ strbuf_addbuf(&msg, &merge_msg);
+ strbuf_addch(&msg, '\n');
+ write_merge_msg(&msg);
run_hook(get_index_file(), "prepare-commit-msg",
git_path("MERGE_MSG"), "merge", NULL, NULL);
- read_merge_msg();
+ if (option_edit) {
+ if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
+ abort_commit(NULL);
+ }
+ read_merge_msg(&msg);
+ stripspace(&msg, option_edit);
+ if (!msg.len)
+ abort_commit(_("Empty commit message."));
+ strbuf_release(&merge_msg);
+ strbuf_addbuf(&merge_msg, &msg);
+ strbuf_release(&msg);
}
-static int merge_trivial(void)
+static int merge_trivial(struct commit *head)
{
unsigned char result_tree[20], result_commit[20];
struct commit_list *parent = xmalloc(sizeof(*parent));
write_tree_trivial(result_tree);
printf(_("Wonderful.\n"));
- parent->item = lookup_commit(head);
+ parent->item = head;
parent->next = xmalloc(sizeof(*parent->next));
parent->next->item = remoteheads->item;
parent->next->next = NULL;
- run_prepare_commit_msg();
+ prepare_to_commit();
commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
- finish(result_commit, "In-index merge");
+ finish(head, result_commit, "In-index merge");
drop_save();
return 0;
}
-static int finish_automerge(struct commit_list *common,
+static int finish_automerge(struct commit *head,
+ struct commit_list *common,
unsigned char *result_tree,
const char *wt_strategy)
{
@@ -862,22 +931,22 @@ static int finish_automerge(struct commit_list *common,
free_commit_list(common);
if (allow_fast_forward) {
parents = remoteheads;
- commit_list_insert(lookup_commit(head), &parents);
+ commit_list_insert(head, &parents);
parents = reduce_heads(parents);
} else {
struct commit_list **pptr = &parents;
- pptr = &commit_list_insert(lookup_commit(head),
+ pptr = &commit_list_insert(head,
pptr)->next;
for (j = remoteheads; j; j = j->next)
pptr = &commit_list_insert(j->item, pptr)->next;
}
- free_commit_list(remoteheads);
strbuf_addch(&merge_msg, '\n');
- run_prepare_commit_msg();
+ prepare_to_commit();
+ free_commit_list(remoteheads);
commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
- strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
- finish(result_commit, buf.buf);
+ strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
+ finish(head, result_commit, buf.buf);
strbuf_release(&buf);
drop_save();
return 0;
@@ -885,13 +954,14 @@ static int finish_automerge(struct commit_list *common,
static int suggest_conflicts(int renormalizing)
{
+ const char *filename;
FILE *fp;
int pos;
- fp = fopen(git_path("MERGE_MSG"), "a");
+ filename = git_path("MERGE_MSG");
+ fp = fopen(filename, "a");
if (!fp)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MSG"));
+ die_errno(_("Could not open '%s' for writing"), filename);
fprintf(fp, "\nConflicts:\n");
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
@@ -911,7 +981,8 @@ static int suggest_conflicts(int renormalizing)
return 1;
}
-static struct commit *is_old_style_invocation(int argc, const char **argv)
+static struct commit *is_old_style_invocation(int argc, const char **argv,
+ const unsigned char *head)
{
struct commit *second_token = NULL;
if (argc > 2) {
@@ -962,16 +1033,16 @@ static int setup_with_upstream(const char ***argv)
const char **args;
if (!branch)
- die("No current branch.");
+ die(_("No current branch."));
if (!branch->remote)
- die("No remote for the current branch.");
+ die(_("No remote for the current branch."));
if (!branch->merge_nr)
- die("No default upstream defined for the current branch.");
+ die(_("No default upstream defined for the current branch."));
args = xcalloc(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",
+ die(_("No remote tracking branch for %s from %s"),
branch->merge[i]->src, branch->remote_name);
args[i] = branch->merge[i]->dst;
}
@@ -980,12 +1051,47 @@ static int setup_with_upstream(const char ***argv)
return i;
}
+static void write_merge_state(void)
+{
+ const char *filename;
+ int fd;
+ struct commit_list *j;
+ struct strbuf buf = STRBUF_INIT;
+
+ for (j = remoteheads; j; j = j->next)
+ strbuf_addf(&buf, "%s\n",
+ sha1_to_hex(j->item->object.sha1));
+ 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);
+ strbuf_addch(&merge_msg, '\n');
+ write_merge_msg(&merge_msg);
+
+ 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 (!allow_fast_forward)
+ 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);
+}
+
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 commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, head_invalid = 0, i;
+ int flag, i;
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;
@@ -998,18 +1104,18 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = resolve_ref("HEAD", head, 0, &flag);
+ branch = resolve_ref("HEAD", head_sha1, 0, &flag);
if (branch && !prefixcmp(branch, "refs/heads/"))
branch += 11;
- if (is_null_sha1(head))
- head_invalid = 1;
+ if (!branch || is_null_sha1(head_sha1))
+ head_commit = NULL;
+ else
+ head_commit = lookup_commit_or_die(head_sha1, "HEAD");
git_config(git_merge_config, NULL);
- /* for color.ui */
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
-
+ if (branch_mergeoptions)
+ parse_branch_merge_options(branch_mergeoptions);
argc = parse_options(argc, argv, prefix, builtin_merge_options,
builtin_merge_usage, 0);
@@ -1043,10 +1149,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
if (advice_resolve_conflict)
- die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
- "Please, commit your changes before you can merge.");
+ die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+ "Please, commit your changes before you can merge."));
else
- die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).");
+ die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
}
resolve_undo_clear();
@@ -1062,9 +1168,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!allow_fast_forward && fast_forward_only)
die(_("You cannot combine --no-ff with --ff-only."));
- if (!argc && !abort_current_merge && default_to_upstream)
- argc = setup_with_upstream(&argv);
-
+ if (!abort_current_merge) {
+ if (!argc && default_to_upstream)
+ argc = setup_with_upstream(&argv);
+ else if (argc == 1 && !strcmp(argv[0], "-"))
+ argv[0] = "@{-1}";
+ }
if (!argc)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
@@ -1078,12 +1187,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* additional safety measure to check for it.
*/
- if (!have_message && is_old_style_invocation(argc, argv)) {
+ if (!have_message && head_commit &&
+ is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
strbuf_addstr(&merge_msg, argv[0]);
head_arg = argv[1];
argv += 2;
argc -= 2;
- } else if (head_invalid) {
+ } else if (!head_commit) {
struct object *remote_head;
/*
* If the merged head is a valid one there is no reason
@@ -1098,7 +1208,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!allow_fast_forward)
die(_("Non-fast-forward commit does not make sense into "
"an empty head"));
- remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+ remote_head = want_commit(argv[0]);
if (!remote_head)
die(_("%s - not something we can merge"), argv[0]);
read_empty(remote_head->sha1, 0);
@@ -1130,7 +1240,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
}
- if (head_invalid || !argc)
+ if (!head_commit || !argc)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
@@ -1144,7 +1254,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct object *o;
struct commit *commit;
- o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+ o = want_commit(argv[i]);
if (!o)
die(_("%s - not something we can merge"), argv[i]);
commit = lookup_commit(o->sha1);
@@ -1171,17 +1281,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (!remoteheads->next)
- common = get_merge_bases(lookup_commit(head),
- remoteheads->item, 1);
+ common = get_merge_bases(head_commit, remoteheads->item, 1);
else {
struct commit_list *list = remoteheads;
- commit_list_insert(lookup_commit(head), &list);
+ commit_list_insert(head_commit, &list);
common = get_octopus_merge_bases(list);
free(list);
}
- update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
- DIE_ON_ERR);
+ update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
+ NULL, 0, DIE_ON_ERR);
if (!common)
; /* No common ancestors found. We need a real merge. */
@@ -1195,13 +1304,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
return 0;
} else if (allow_fast_forward && !remoteheads->next &&
!common->next &&
- !hashcmp(common->item->object.sha1, head)) {
+ !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
/* Again the most common case of merging one remote. */
struct strbuf msg = STRBUF_INIT;
struct object *o;
char hex[41];
- strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+ strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
if (verbosity >= 0)
printf(_("Updating %s..%s\n"),
@@ -1212,15 +1321,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (have_message)
strbuf_addstr(&msg,
" (no commit created; -m option ignored)");
- o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
- 0, NULL, OBJ_COMMIT);
+ o = want_commit(sha1_to_hex(remoteheads->item->object.sha1));
if (!o)
return 1;
- if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+ if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1))
return 1;
- finish(o->sha1, msg.buf);
+ finish(head_commit, o->sha1, msg.buf);
drop_save();
return 0;
} else if (!remoteheads->next && common->next)
@@ -1240,8 +1348,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
git_committer_info(IDENT_ERROR_ON_NO_NAME);
printf(_("Trying really trivial in-index merge...\n"));
if (!read_tree_trivial(common->item->object.sha1,
- head, remoteheads->item->object.sha1))
- return merge_trivial();
+ head_commit->object.sha1, remoteheads->item->object.sha1))
+ return merge_trivial(head_commit);
printf(_("Nope.\n"));
}
} else {
@@ -1260,8 +1368,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* merge_bases again, otherwise "git merge HEAD^
* HEAD^^" would be missed.
*/
- common_one = get_merge_bases(lookup_commit(head),
- j->item, 1);
+ common_one = get_merge_bases(head_commit, j->item, 1);
if (hashcmp(common_one->item->object.sha1,
j->item->object.sha1)) {
up_to_date = 0;
@@ -1288,21 +1395,18 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* sync with the head commit. The strategies are responsible
* to ensure this.
*/
- if (use_strategies_nr != 1) {
- /*
- * Stash away the local changes so that we can try more
- * than one.
- */
- save_state();
- } else {
- memcpy(stash, null_sha1, 20);
- }
+ if (use_strategies_nr == 1 ||
+ /*
+ * Stash away the local changes so that we can try more than one.
+ */
+ save_state(stash))
+ hashcpy(stash, null_sha1);
for (i = 0; i < use_strategies_nr; i++) {
int ret;
if (i) {
printf(_("Rewinding the tree to pristine...\n"));
- restore_state();
+ restore_state(head_commit->object.sha1, stash);
}
if (use_strategies_nr != 1)
printf(_("Trying merge strategy %s...\n"),
@@ -1314,7 +1418,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
wt_strategy = use_strategies[i]->name;
ret = try_merge_strategy(use_strategies[i]->name,
- common, head_arg);
+ common, head_commit, head_arg);
if (!option_commit && !ret) {
merge_was_ok = 1;
/*
@@ -1356,14 +1460,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* auto resolved the merge cleanly.
*/
if (automerge_was_ok)
- return finish_automerge(common, result_tree, wt_strategy);
+ return finish_automerge(head_commit, common, result_tree,
+ wt_strategy);
/*
* Pick the result from the best strategy and have the user fix
* it up.
*/
if (!best_strategy) {
- restore_state();
+ restore_state(head_commit->object.sha1, stash);
if (use_strategies_nr > 1)
fprintf(stderr,
_("No merge strategy handled the merge.\n"));
@@ -1375,41 +1480,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
; /* We already have its result in the working tree. */
else {
printf(_("Rewinding the tree to pristine...\n"));
- restore_state();
+ restore_state(head_commit->object.sha1, stash);
printf(_("Using the %s to prepare resolving by hand.\n"),
best_strategy);
- try_merge_strategy(best_strategy, common, head_arg);
+ try_merge_strategy(best_strategy, common, head_commit, head_arg);
}
if (squash)
- finish(NULL, NULL);
- else {
- int fd;
- struct commit_list *j;
-
- for (j = remoteheads; j; j = j->next)
- strbuf_addf(&buf, "%s\n",
- sha1_to_hex(j->item->object.sha1));
- fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_HEAD"));
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
- close(fd);
- strbuf_addch(&merge_msg, '\n');
- write_merge_msg();
- fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MODE"));
- strbuf_reset(&buf);
- if (!allow_fast_forward)
- strbuf_addf(&buf, "no-ff");
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
- close(fd);
- }
+ finish(head_commit, NULL, NULL);
+ else
+ write_merge_state();
if (merge_was_ok) {
fprintf(stderr, _("Automatic merge went well; "
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 324a267163..640ab64f41 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -23,8 +23,8 @@ static int verify_object(const unsigned char *sha1, const char *expected_type)
int ret = -1;
enum object_type type;
unsigned long size;
- const unsigned char *repl;
- void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
+ void *buffer = read_sha1_file(sha1, &type, &size);
+ const unsigned char *repl = lookup_replace_object(sha1);
if (buffer) {
if (type == type_from_string(expected_type))
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 098395fda1..4ae1c412d4 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -60,6 +60,7 @@ static void write_tree(unsigned char *sha1)
}
write_sha1_file(buf.buf, buf.len, tree_type, sha1);
+ strbuf_release(&buf);
}
static const char *mktree_usage[] = {
diff --git a/builtin/mv.c b/builtin/mv.c
index 40f33ca4d0..2a144b011c 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -29,7 +29,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
to_copy--;
if (to_copy != length || base_name) {
char *it = xmemdupz(result[i], to_copy);
- result[i] = base_name ? strdup(basename(it)) : it;
+ if (base_name) {
+ result[i] = xstrdup(basename(it));
+ free(it);
+ } else
+ result[i] = it;
}
}
return get_pathspec(prefix, result);
@@ -55,6 +59,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int i, newfd;
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
struct option builtin_mv_options[] = {
+ OPT__VERBOSE(&verbose, "be verbose"),
OPT__DRY_RUN(&show_only, "dry run"),
OPT__FORCE(&force, "force move/rename even if target exists"),
OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
@@ -89,7 +94,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
destination = copy_pathspec(dest_path[0], argv, argc, 1);
} else {
if (argc != 1)
- usage_with_options(builtin_mv_usage, builtin_mv_options);
+ die("destination '%s' is not a directory", dest_path[0]);
destination = dest_path;
}
@@ -172,7 +177,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
* check both source and destination
*/
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- warning(_("%s; will overwrite!"), bad);
+ if (verbose)
+ warning(_("overwriting '%s'"), dst);
bad = NULL;
} else
bad = _("Cannot overwrite");
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 31f5c1c971..1b374583c2 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -172,7 +172,9 @@ static void show_name(const struct object *obj,
}
static char const * const name_rev_usage[] = {
- "git name-rev [options] ( --all | --stdin | <commit>... )",
+ "git name-rev [options] <commit>...",
+ "git name-rev [options] --all",
+ "git name-rev [options] --stdin",
NULL
};
@@ -289,7 +291,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
max = get_max_object_index();
for (i = 0; i < max; i++) {
struct object *obj = get_indexed_object(i);
- if (!obj)
+ if (!obj || obj->type != OBJ_COMMIT)
continue;
show_name(obj, NULL,
always, allow_undefined, data.name_only);
diff --git a/builtin/notes.c b/builtin/notes.c
index d6dcfcb014..f8e437db01 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -29,7 +29,7 @@ static const char * const git_notes_usage[] = {
"git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
"git notes merge --commit [-v | -q]",
"git notes merge --abort [-v | -q]",
- "git notes [--ref <notes_ref>] remove [<object>]",
+ "git notes [--ref <notes_ref>] remove [<object>...]",
"git notes [--ref <notes_ref>] prune [-n | -v]",
"git notes [--ref <notes_ref>] get-ref",
NULL
@@ -100,16 +100,6 @@ struct msg_arg {
struct strbuf buf;
};
-static void expand_notes_ref(struct strbuf *sb)
-{
- if (!prefixcmp(sb->buf, "refs/notes/"))
- return; /* we're happy */
- else if (!prefixcmp(sb->buf, "notes/"))
- strbuf_insert(sb, 0, "refs/", 5);
- else
- strbuf_insert(sb, 0, "refs/notes/", 11);
-}
-
static int list_each_note(const unsigned char *object_sha1,
const unsigned char *note_sha1, char *note_path,
void *cb_data)
@@ -529,6 +519,8 @@ static int list(int argc, const char **argv, const char *prefix)
return retval;
}
+static int append_edit(int argc, const char **argv, const char *prefix);
+
static int add(int argc, const char **argv, const char *prefix)
{
int retval = 0, force = 0;
@@ -556,14 +548,14 @@ static int add(int argc, const char **argv, const char *prefix)
};
argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
- 0);
+ PARSE_OPT_KEEP_ARGV0);
- if (1 < argc) {
+ if (2 < argc) {
error(_("too many parameters"));
usage_with_options(git_notes_add_usage, options);
}
- object_ref = argc ? argv[0] : "HEAD";
+ object_ref = argc > 1 ? argv[1] : "HEAD";
if (get_sha1(object_ref, object))
die(_("Failed to resolve '%s' as a valid ref."), object_ref);
@@ -573,6 +565,18 @@ static int add(int argc, const char **argv, const char *prefix)
if (note) {
if (!force) {
+ if (!msg.given) {
+ /*
+ * Redirect to "edit" subcommand.
+ *
+ * We only end up here if none of -m/-F/-c/-C
+ * or -f are given. The original args are
+ * therefore still in argv[0-1].
+ */
+ argv[0] = "edit";
+ free_notes(t);
+ return append_edit(argc, argv, prefix);
+ }
retval = error(_("Cannot add notes. Found existing notes "
"for object %s. Use '-f' to overwrite "
"existing notes"), sha1_to_hex(object));
@@ -949,40 +953,60 @@ static int merge(int argc, const char **argv, const char *prefix)
return result < 0; /* return non-zero on conflicts */
}
+#define IGNORE_MISSING 1
+
+static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
+{
+ int status;
+ unsigned char sha1[20];
+ if (get_sha1(name, sha1))
+ return error(_("Failed to resolve '%s' as a valid ref."), name);
+ status = remove_note(t, sha1);
+ if (status)
+ fprintf(stderr, _("Object %s has no note\n"), name);
+ else
+ fprintf(stderr, _("Removing note for object %s\n"), name);
+ return (flag & IGNORE_MISSING) ? 0 : status;
+}
+
static int remove_cmd(int argc, const char **argv, const char *prefix)
{
+ unsigned flag = 0;
+ int from_stdin = 0;
struct option options[] = {
+ OPT_BIT(0, "ignore-missing", &flag,
+ "attempt to remove non-existent note is not an error",
+ IGNORE_MISSING),
+ OPT_BOOLEAN(0, "stdin", &from_stdin,
+ "read object names from the standard input"),
OPT_END()
};
- const char *object_ref;
struct notes_tree *t;
- unsigned char object[20];
- int retval;
+ int retval = 0;
argc = parse_options(argc, argv, prefix, options,
git_notes_remove_usage, 0);
- if (1 < argc) {
- error(_("too many parameters"));
- usage_with_options(git_notes_remove_usage, options);
- }
-
- object_ref = argc ? argv[0] : "HEAD";
-
- if (get_sha1(object_ref, object))
- die(_("Failed to resolve '%s' as a valid ref."), object_ref);
-
t = init_notes_check("remove");
- retval = remove_note(t, object);
- if (retval)
- fprintf(stderr, _("Object %s has no note\n"), sha1_to_hex(object));
- else {
- fprintf(stderr, _("Removing note for object %s\n"),
- sha1_to_hex(object));
-
- commit_notes(t, "Notes removed by 'git notes remove'");
+ if (!argc && !from_stdin) {
+ retval = remove_one_note(t, "HEAD", flag);
+ } else {
+ while (*argv) {
+ retval |= remove_one_note(t, *argv, flag);
+ argv++;
+ }
}
+ if (from_stdin) {
+ struct strbuf sb = STRBUF_INIT;
+ while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+ strbuf_rtrim(&sb);
+ retval |= remove_one_note(t, sb.buf, flag);
+ }
+ strbuf_release(&sb);
+ }
+ if (!retval)
+ commit_notes(t, "Notes removed by 'git notes remove'");
free_notes(t);
return retval;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index b0503b202a..b1895aaaa1 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -51,6 +51,8 @@ struct object_entry {
* objects against.
*/
unsigned char no_try_delta;
+ unsigned char tagged; /* near the very tip of refs */
+ unsigned char filled; /* assigned write-order */
};
/*
@@ -70,6 +72,7 @@ static int local;
static int incremental;
static int ignore_packed_keep;
static int allow_ofs_delta;
+static struct pack_idx_option pack_idx_opts;
static const char *base_name;
static int progress = 1;
static int window = 10;
@@ -95,6 +98,7 @@ static unsigned long window_memory_limit = 0;
*/
static int *object_ix;
static int object_ix_hashsz;
+static struct object_entry *locate_object_entry(const unsigned char *sha1);
/*
* stats
@@ -126,13 +130,13 @@ static void *get_delta(struct object_entry *entry)
static unsigned long do_compress(void **pptr, unsigned long size)
{
- z_stream stream;
+ git_zstream stream;
void *in, *out;
unsigned long maxsize;
memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, pack_compression_level);
- maxsize = deflateBound(&stream, size);
+ git_deflate_init(&stream, pack_compression_level);
+ maxsize = git_deflate_bound(&stream, size);
in = *pptr;
out = xmalloc(maxsize);
@@ -142,9 +146,9 @@ static unsigned long do_compress(void **pptr, unsigned long size)
stream.avail_in = size;
stream.next_out = out;
stream.avail_out = maxsize;
- while (deflate(&stream, Z_FINISH) == Z_OK)
+ while (git_deflate(&stream, Z_FINISH) == Z_OK)
; /* nothing */
- deflateEnd(&stream);
+ git_deflate_end(&stream);
free(in);
return stream.total_out;
@@ -160,7 +164,7 @@ static int check_pack_inflate(struct packed_git *p,
off_t len,
unsigned long expect)
{
- z_stream stream;
+ git_zstream stream;
unsigned char fakebuf[4096], *in;
int st;
@@ -187,18 +191,19 @@ static void copy_pack_data(struct sha1file *f,
off_t len)
{
unsigned char *in;
- unsigned int avail;
+ unsigned long avail;
while (len) {
in = use_pack(p, w_curs, offset, &avail);
if (avail > len)
- avail = (unsigned int)len;
+ avail = (unsigned long)len;
sha1write(f, in, avail);
offset += avail;
len -= avail;
}
}
+/* Return 0 if we will bust the pack-size limit */
static unsigned long write_object(struct sha1file *f,
struct object_entry *entry,
off_t write_offset)
@@ -404,25 +409,56 @@ static unsigned long write_object(struct sha1file *f,
return hdrlen + datalen;
}
-static int write_one(struct sha1file *f,
- struct object_entry *e,
- off_t *offset)
+enum write_one_status {
+ WRITE_ONE_SKIP = -1, /* already written */
+ WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+ WRITE_ONE_WRITTEN = 1, /* normal */
+ WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static enum write_one_status write_one(struct sha1file *f,
+ struct object_entry *e,
+ off_t *offset)
{
unsigned long size;
+ int recursing;
- /* offset is non zero if object is written already. */
- if (e->idx.offset || e->preferred_base)
- return -1;
+ /*
+ * we set offset to 1 (which is an impossible value) to mark
+ * the fact that this object is involved in "write its base
+ * first before writing a deltified object" recursion.
+ */
+ recursing = (e->idx.offset == 1);
+ if (recursing) {
+ warning("recursive delta detected for object %s",
+ sha1_to_hex(e->idx.sha1));
+ return WRITE_ONE_RECURSIVE;
+ } else if (e->idx.offset || e->preferred_base) {
+ /* offset is non zero if object is written already. */
+ return WRITE_ONE_SKIP;
+ }
/* if we are deltified, write out base object first. */
- if (e->delta && !write_one(f, e->delta, offset))
- return 0;
+ if (e->delta) {
+ e->idx.offset = 1; /* now recurse */
+ switch (write_one(f, e->delta, offset)) {
+ case WRITE_ONE_RECURSIVE:
+ /* we cannot depend on this one */
+ e->delta = NULL;
+ break;
+ default:
+ break;
+ case WRITE_ONE_BREAK:
+ e->idx.offset = recursing;
+ return WRITE_ONE_BREAK;
+ }
+ }
e->idx.offset = *offset;
size = write_object(f, e, *offset);
if (!size) {
- e->idx.offset = 0;
- return 0;
+ e->idx.offset = recursing;
+ return WRITE_ONE_BREAK;
}
written_list[nr_written++] = &e->idx;
@@ -430,7 +466,171 @@ static int write_one(struct sha1file *f,
if (signed_add_overflows(*offset, size))
die("pack too large for current definition of off_t");
*offset += size;
- return 1;
+ return WRITE_ONE_WRITTEN;
+}
+
+static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
+ void *cb_data)
+{
+ unsigned char peeled[20];
+ struct object_entry *entry = locate_object_entry(sha1);
+
+ if (entry)
+ entry->tagged = 1;
+ if (!peel_ref(path, peeled)) {
+ entry = locate_object_entry(peeled);
+ if (entry)
+ entry->tagged = 1;
+ }
+ return 0;
+}
+
+static inline void add_to_write_order(struct object_entry **wo,
+ unsigned int *endp,
+ struct object_entry *e)
+{
+ if (e->filled)
+ return;
+ wo[(*endp)++] = e;
+ e->filled = 1;
+}
+
+static void add_descendants_to_write_order(struct object_entry **wo,
+ unsigned int *endp,
+ struct object_entry *e)
+{
+ int add_to_order = 1;
+ while (e) {
+ if (add_to_order) {
+ struct object_entry *s;
+ /* add this node... */
+ add_to_write_order(wo, endp, e);
+ /* all its siblings... */
+ for (s = e->delta_sibling; s; s = s->delta_sibling) {
+ add_to_write_order(wo, endp, s);
+ }
+ }
+ /* drop down a level to add left subtree nodes if possible */
+ if (e->delta_child) {
+ add_to_order = 1;
+ e = e->delta_child;
+ } else {
+ add_to_order = 0;
+ /* our sibling might have some children, it is next */
+ if (e->delta_sibling) {
+ e = e->delta_sibling;
+ continue;
+ }
+ /* go back to our parent node */
+ e = e->delta;
+ while (e && !e->delta_sibling) {
+ /* we're on the right side of a subtree, keep
+ * going up until we can go right again */
+ e = e->delta;
+ }
+ if (!e) {
+ /* done- we hit our original root node */
+ return;
+ }
+ /* pass it off to sibling at this level */
+ e = e->delta_sibling;
+ }
+ };
+}
+
+static void add_family_to_write_order(struct object_entry **wo,
+ unsigned int *endp,
+ struct object_entry *e)
+{
+ struct object_entry *root;
+
+ for (root = e; root->delta; root = root->delta)
+ ; /* nothing */
+ add_descendants_to_write_order(wo, endp, root);
+}
+
+static struct object_entry **compute_write_order(void)
+{
+ unsigned int i, wo_end, last_untagged;
+
+ struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
+
+ for (i = 0; i < nr_objects; i++) {
+ objects[i].tagged = 0;
+ objects[i].filled = 0;
+ objects[i].delta_child = NULL;
+ objects[i].delta_sibling = NULL;
+ }
+
+ /*
+ * Fully connect delta_child/delta_sibling network.
+ * Make sure delta_sibling is sorted in the original
+ * recency order.
+ */
+ for (i = nr_objects; i > 0;) {
+ struct object_entry *e = &objects[--i];
+ if (!e->delta)
+ continue;
+ /* Mark me as the first child */
+ e->delta_sibling = e->delta->delta_child;
+ e->delta->delta_child = e;
+ }
+
+ /*
+ * Mark objects that are at the tip of tags.
+ */
+ for_each_tag_ref(mark_tagged, NULL);
+
+ /*
+ * Give the objects in the original recency order until
+ * we see a tagged tip.
+ */
+ for (i = wo_end = 0; i < nr_objects; i++) {
+ if (objects[i].tagged)
+ break;
+ add_to_write_order(wo, &wo_end, &objects[i]);
+ }
+ last_untagged = i;
+
+ /*
+ * Then fill all the tagged tips.
+ */
+ for (; i < nr_objects; i++) {
+ if (objects[i].tagged)
+ add_to_write_order(wo, &wo_end, &objects[i]);
+ }
+
+ /*
+ * And then all remaining commits and tags.
+ */
+ for (i = last_untagged; i < nr_objects; i++) {
+ if (objects[i].type != OBJ_COMMIT &&
+ objects[i].type != OBJ_TAG)
+ continue;
+ add_to_write_order(wo, &wo_end, &objects[i]);
+ }
+
+ /*
+ * And then all the trees.
+ */
+ for (i = last_untagged; i < nr_objects; i++) {
+ if (objects[i].type != OBJ_TREE)
+ continue;
+ add_to_write_order(wo, &wo_end, &objects[i]);
+ }
+
+ /*
+ * Finally all the rest in really tight order
+ */
+ for (i = last_untagged; i < nr_objects; i++) {
+ if (!objects[i].filled)
+ add_family_to_write_order(wo, &wo_end, &objects[i]);
+ }
+
+ if (wo_end != nr_objects)
+ die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects);
+
+ return wo;
}
static void write_pack_file(void)
@@ -441,10 +641,12 @@ static void write_pack_file(void)
struct pack_header hdr;
uint32_t nr_remaining = nr_result;
time_t last_mtime = 0;
+ struct object_entry **write_order;
if (progress > pack_to_stdout)
progress_state = start_progress("Writing objects", nr_result);
written_list = xmalloc(nr_objects * sizeof(*written_list));
+ write_order = compute_write_order();
do {
unsigned char sha1[20];
@@ -468,7 +670,8 @@ static void write_pack_file(void)
offset = sizeof(hdr);
nr_written = 0;
for (; i < nr_objects; i++) {
- if (!write_one(f, objects + i, &offset))
+ struct object_entry *e = write_order[i];
+ if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
break;
display_progress(progress_state, written);
}
@@ -493,8 +696,8 @@ static void write_pack_file(void)
const char *idx_tmp_name;
char tmpname[PATH_MAX];
- idx_tmp_name = write_idx_file(NULL, written_list,
- nr_written, sha1);
+ idx_tmp_name = write_idx_file(NULL, written_list, nr_written,
+ &pack_idx_opts, sha1);
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
base_name, sha1_to_hex(sha1));
@@ -545,6 +748,7 @@ static void write_pack_file(void)
} while (nr_remaining && i < nr_objects);
free(written_list);
+ free(write_order);
stop_progress(&progress_state);
if (written != nr_result)
die("wrote %"PRIu32" objects while expecting %"PRIu32,
@@ -633,7 +837,7 @@ static int no_try_delta(const char *path)
struct git_attr_check check[1];
setup_delta_attr_check(check);
- if (git_checkattr(path, ARRAY_SIZE(check), check))
+ if (git_check_attr(path, ARRAY_SIZE(check), check))
return 0;
if (ATTR_FALSE(check->value))
return 1;
@@ -667,6 +871,10 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
off_t offset = find_pack_entry_one(sha1, p);
if (offset) {
if (!found_pack) {
+ if (!is_pack_valid(p)) {
+ warning("packfile %s cannot be accessed", p->pack_name);
+ continue;
+ }
found_offset = offset;
found_pack = p;
}
@@ -838,7 +1046,7 @@ static void add_pbase_object(struct tree_desc *tree,
while (tree_entry(tree,&entry)) {
if (S_ISGITLINK(entry.mode))
continue;
- cmp = tree_entry_len(entry.path, entry.sha1) != cmplen ? 1 :
+ cmp = tree_entry_len(&entry) != cmplen ? 1 :
memcmp(name, entry.path, cmplen);
if (cmp > 0)
continue;
@@ -994,7 +1202,7 @@ static void check_object(struct object_entry *entry)
const unsigned char *base_ref = NULL;
struct object_entry *base_entry;
unsigned long used, used_0;
- unsigned int avail;
+ unsigned long avail;
off_t ofs;
unsigned char *buf, c;
@@ -1142,8 +1350,12 @@ static void get_object_details(void)
sorted_by_offset[i] = objects + i;
qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
- for (i = 0; i < nr_objects; i++)
- check_object(sorted_by_offset[i]);
+ for (i = 0; i < nr_objects; i++) {
+ struct object_entry *entry = sorted_by_offset[i];
+ check_object(entry);
+ if (big_file_threshold <= entry->size)
+ entry->no_try_delta = 1;
+ }
free(sorted_by_offset);
}
@@ -1880,10 +2092,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)
return 0;
}
if (!strcmp(k, "pack.indexversion")) {
- pack_idx_default_version = git_config_int(k, v);
- if (pack_idx_default_version > 2)
+ pack_idx_opts.version = git_config_int(k, v);
+ if (pack_idx_opts.version > 2)
die("bad pack.indexversion=%"PRIu32,
- pack_idx_default_version);
+ pack_idx_opts.version);
return 0;
}
if (!strcmp(k, "pack.packsizelimit")) {
@@ -1932,7 +2144,9 @@ static void show_commit(struct commit *commit, void *data)
commit->object.flags |= OBJECT_ADDED;
}
-static void show_object(struct object *obj, const struct name_path *path, const char *last)
+static void show_object(struct object *obj,
+ const struct name_path *path, const char *last,
+ void *data)
{
char *name = path_name(path, last);
@@ -2130,6 +2344,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
rp_ac = 2;
+ reset_pack_idx_option(&pack_idx_opts);
git_config(git_pack_config, NULL);
if (!pack_compression_seen && core_compression_seen)
pack_compression_level = core_compression_level;
@@ -2274,12 +2489,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
if (!prefixcmp(arg, "--index-version=")) {
char *c;
- pack_idx_default_version = strtoul(arg + 16, &c, 10);
- if (pack_idx_default_version > 2)
+ pack_idx_opts.version = strtoul(arg + 16, &c, 10);
+ if (pack_idx_opts.version > 2)
die("bad %s", arg);
if (*c == ',')
- pack_idx_off32_limit = strtoul(c+1, &c, 0);
- if (*c || pack_idx_off32_limit & 0x80000000)
+ pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
+ if (*c || pack_idx_opts.off32_limit & 0x80000000)
die("bad %s", arg);
continue;
}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index f821eb3f0b..3cfe02d5a5 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -56,13 +56,13 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
return 1;
}
-static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx)
+static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct strbuf *line_buf)
{
- static char line[1000];
int patchlen = 0, found_next = 0;
int before = -1, after = -1;
- while (fgets(line, sizeof(line), stdin) != NULL) {
+ while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
+ char *line = line_buf->buf;
char *p = line;
int len;
@@ -133,14 +133,16 @@ static void generate_id_list(void)
unsigned char sha1[20], n[20];
git_SHA_CTX ctx;
int patchlen;
+ struct strbuf line_buf = STRBUF_INIT;
git_SHA1_Init(&ctx);
hashclr(sha1);
while (!feof(stdin)) {
- patchlen = get_one_patchid(n, &ctx);
+ patchlen = get_one_patchid(n, &ctx, &line_buf);
flush_current_id(patchlen, sha1, &ctx);
hashcpy(sha1, n);
}
+ strbuf_release(&line_buf);
}
static const char patch_id_usage[] = "git patch-id < patch";
diff --git a/builtin/push.c b/builtin/push.c
index 9cebf9ea23..35cce532f2 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -8,6 +8,7 @@
#include "remote.h"
#include "transport.h"
#include "parse-options.h"
+#include "submodule.h"
static const char * const push_usage[] = {
"git push [<options>] [<repository> [<refspec>...]]",
@@ -219,6 +220,21 @@ static int do_push(const char *repo, int flags)
return !!errs;
}
+static int option_parse_recurse_submodules(const struct option *opt,
+ const char *arg, int unset)
+{
+ int *flags = opt->value;
+ if (arg) {
+ if (!strcmp(arg, "check"))
+ *flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
+ else
+ die("bad %s argument: %s", opt->long_name, arg);
+ } else
+ die("option %s needs an argument (check)", opt->long_name);
+
+ return 0;
+}
+
int cmd_push(int argc, const char **argv, const char *prefix)
{
int flags = 0;
@@ -236,6 +252,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
OPT_BIT( 0, "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
+ { OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check",
+ "controls recursive pushing of submodules",
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules },
OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 93c92814cf..df6c4c8819 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -130,6 +130,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
PARSE_OPT_NONEG, exclude_per_directory_cb },
OPT_SET_INT('i', NULL, &opts.index_only,
"don't check the working tree after merging", 1),
+ OPT__DRY_RUN(&opts.dry_run, "don't update the index or the work tree"),
OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
"skip applying sparse checkout filter", 1),
OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack,
@@ -219,7 +220,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
if (unpack_trees(nr_trees, t, &opts))
return 128;
- if (opts.debug_unpack)
+ if (opts.debug_unpack || opts.dry_run)
return 0; /* do not write the index out */
/*
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e1ba4dc697..7ec68a1e80 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -10,6 +10,8 @@
#include "remote.h"
#include "transport.h"
#include "string-list.h"
+#include "sha1-array.h"
+#include "connected.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -24,7 +26,8 @@ static int deny_deletes;
static int deny_non_fast_forwards;
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
-static int receive_fsck_objects;
+static int receive_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
@@ -78,6 +81,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "transfer.fsckobjects") == 0) {
+ transfer_fsck_objects = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "receive.denycurrentbranch")) {
deny_current_branch = parse_deny_action(var, value);
return 0;
@@ -119,9 +127,25 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
return 0;
}
+static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+ path = strip_namespace(path);
+ /*
+ * Advertise refs outside our current namespace as ".have"
+ * refs, so that the client can use them to minimize data
+ * transfer but will otherwise ignore them. This happens to
+ * cover ".have" that are thrown in by add_one_alternate_ref()
+ * to mark histories that are complete in our alternates as
+ * well.
+ */
+ if (!path)
+ path = ".have";
+ return show_ref(path, sha1, flag, cb_data);
+}
+
static void write_head_info(void)
{
- for_each_ref(show_ref, NULL);
+ for_each_ref(show_ref_cb, NULL);
if (!sent_capabilities)
show_ref("capabilities^{}", null_sha1, 0, NULL);
@@ -130,7 +154,8 @@ static void write_head_info(void)
struct command {
struct command *next;
const char *error_string;
- unsigned int skip_update;
+ unsigned int skip_update:1,
+ did_not_exist:1;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char ref_name[FLEX_ARRAY]; /* more */
@@ -188,21 +213,15 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
-static int run_receive_hook(struct command *commands, const char *hook_name)
+typedef int (*feed_fn)(void *, const char **, size_t *);
+static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
{
- static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
- struct command *cmd;
struct child_process proc;
struct async muxer;
const char *argv[2];
- int have_input = 0, code;
-
- for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
- if (!cmd->error_string)
- have_input = 1;
- }
+ int code;
- if (!have_input || access(hook_name, X_OK) < 0)
+ if (access(hook_name, X_OK) < 0)
return 0;
argv[0] = hook_name;
@@ -230,15 +249,13 @@ static int run_receive_hook(struct command *commands, const char *hook_name)
return code;
}
- for (cmd = commands; cmd; cmd = cmd->next) {
- if (!cmd->error_string) {
- size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
- sha1_to_hex(cmd->old_sha1),
- sha1_to_hex(cmd->new_sha1),
- cmd->ref_name);
- if (write_in_full(proc.in, buf, n) != n)
- break;
- }
+ while (1) {
+ const char *buf;
+ size_t n;
+ if (feed(feed_state, &buf, &n))
+ break;
+ if (write_in_full(proc.in, buf, n) != n)
+ break;
}
close(proc.in);
if (use_sideband)
@@ -246,6 +263,51 @@ static int run_receive_hook(struct command *commands, const char *hook_name)
return finish_command(&proc);
}
+struct receive_hook_feed_state {
+ struct command *cmd;
+ int skip_broken;
+ struct strbuf buf;
+};
+
+static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
+{
+ struct receive_hook_feed_state *state = state_;
+ struct command *cmd = state->cmd;
+
+ while (cmd &&
+ state->skip_broken && (cmd->error_string || cmd->did_not_exist))
+ cmd = cmd->next;
+ if (!cmd)
+ return -1; /* EOF */
+ strbuf_reset(&state->buf);
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
+ cmd->ref_name);
+ state->cmd = cmd->next;
+ if (bufp) {
+ *bufp = state->buf.buf;
+ *sizep = state->buf.len;
+ }
+ return 0;
+}
+
+static int run_receive_hook(struct command *commands, const char *hook_name,
+ int skip_broken)
+{
+ struct receive_hook_feed_state state;
+ int status;
+
+ strbuf_init(&state.buf, 0);
+ state.cmd = commands;
+ state.skip_broken = skip_broken;
+ if (feed_receive_hook(&state, NULL, NULL))
+ return 0;
+ state.cmd = commands;
+ status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
+ strbuf_release(&state.buf);
+ return status;
+}
+
static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
@@ -332,17 +394,22 @@ static void refuse_unconfigured_deny_delete_current(void)
static const char *update(struct command *cmd)
{
const char *name = cmd->ref_name;
+ struct strbuf namespaced_name_buf = STRBUF_INIT;
+ const char *namespaced_name;
unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1;
struct ref_lock *lock;
/* only refs/... are allowed */
- if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
+ if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name);
return "funny refname";
}
- if (is_ref_checked_out(name)) {
+ strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+ namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
+
+ if (is_ref_checked_out(namespaced_name)) {
switch (deny_current_branch) {
case DENY_IGNORE:
break;
@@ -370,7 +437,7 @@ static const char *update(struct command *cmd)
return "deletion prohibited";
}
- if (!strcmp(name, head_name)) {
+ if (!strcmp(namespaced_name, head_name)) {
switch (deny_delete_current) {
case DENY_IGNORE:
break;
@@ -423,17 +490,22 @@ static const char *update(struct command *cmd)
if (is_null_sha1(new_sha1)) {
if (!parse_object(old_sha1)) {
- rp_warning("Allowing deletion of corrupt ref.");
old_sha1 = NULL;
+ if (ref_exists(name)) {
+ rp_warning("Allowing deletion of corrupt ref.");
+ } else {
+ rp_warning("Deleting a non-existent ref.");
+ cmd->did_not_exist = 1;
+ }
}
- if (delete_ref(name, old_sha1, 0)) {
+ if (delete_ref(namespaced_name, old_sha1, 0)) {
rp_error("failed to delete %s", name);
return "failed to delete";
}
return NULL; /* good */
}
else {
- lock = lock_any_ref_for_update(name, old_sha1, 0);
+ lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
if (!lock) {
rp_error("failed to lock %s", name);
return "failed to lock";
@@ -455,7 +527,7 @@ static void run_update_post_hook(struct command *commands)
struct child_process proc;
for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
- if (cmd->error_string)
+ if (cmd->error_string || cmd->did_not_exist)
continue;
argc++;
}
@@ -466,7 +538,7 @@ static void run_update_post_hook(struct command *commands)
for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
char *p;
- if (cmd->error_string)
+ if (cmd->error_string || cmd->did_not_exist)
continue;
p = xmalloc(strlen(cmd->ref_name) + 1);
strcpy(p, cmd->ref_name);
@@ -490,17 +562,29 @@ static void run_update_post_hook(struct command *commands)
static void check_aliased_update(struct command *cmd, struct string_list *list)
{
+ struct strbuf buf = STRBUF_INIT;
+ const char *dst_name;
struct string_list_item *item;
struct command *dst_cmd;
unsigned char sha1[20];
char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
int flag;
- const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+ strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
+ dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+ strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
return;
+ dst_name = strip_namespace(dst_name);
+ if (!dst_name) {
+ rp_error("refusing update to broken symref '%s'", cmd->ref_name);
+ cmd->skip_update = 1;
+ cmd->error_string = "broken symref";
+ return;
+ }
+
if ((item = string_list_lookup(list, dst_name)) == NULL)
return;
@@ -545,6 +629,48 @@ static void check_aliased_updates(struct command *commands)
string_list_clear(&ref_list, 0);
}
+static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
+{
+ struct command **cmd_list = cb_data;
+ struct command *cmd = *cmd_list;
+
+ if (!cmd || is_null_sha1(cmd->new_sha1))
+ return -1; /* end of list */
+ *cmd_list = NULL; /* this returns only one */
+ hashcpy(sha1, cmd->new_sha1);
+ return 0;
+}
+
+static void set_connectivity_errors(struct command *commands)
+{
+ struct command *cmd;
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ struct command *singleton = cmd;
+ if (!check_everything_connected(command_singleton_iterator,
+ 0, &singleton))
+ continue;
+ cmd->error_string = "missing necessary objects";
+ }
+}
+
+static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
+{
+ struct command **cmd_list = cb_data;
+ struct command *cmd = *cmd_list;
+
+ while (cmd) {
+ if (!is_null_sha1(cmd->new_sha1)) {
+ hashcpy(sha1, cmd->new_sha1);
+ *cmd_list = cmd->next;
+ return 0;
+ }
+ cmd = cmd->next;
+ }
+ *cmd_list = NULL;
+ return -1; /* end of list */
+}
+
static void execute_commands(struct command *commands, const char *unpacker_error)
{
struct command *cmd;
@@ -556,7 +682,12 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
return;
}
- if (run_receive_hook(commands, pre_receive_hook)) {
+ cmd = commands;
+ if (check_everything_connected(iterate_receive_command_list,
+ 0, &cmd))
+ set_connectivity_errors(commands);
+
+ if (run_receive_hook(commands, pre_receive_hook, 0)) {
for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "pre-receive hook declined";
return;
@@ -640,6 +771,11 @@ static const char *unpack(void)
struct pack_header hdr;
const char *hdr_err;
char hdr_arg[38];
+ int fsck_objects = (receive_fsck_objects >= 0
+ ? receive_fsck_objects
+ : transfer_fsck_objects >= 0
+ ? transfer_fsck_objects
+ : 0);
hdr_err = parse_pack_header(&hdr);
if (hdr_err)
@@ -652,7 +788,7 @@ static const char *unpack(void)
int code, i = 0;
const char *unpacker[4];
unpacker[i++] = "unpack-objects";
- if (receive_fsck_objects)
+ if (fsck_objects)
unpacker[i++] = "--strict";
unpacker[i++] = hdr_arg;
unpacker[i++] = NULL;
@@ -672,7 +808,7 @@ static const char *unpack(void)
keeper[i++] = "index-pack";
keeper[i++] = "--stdin";
- if (receive_fsck_objects)
+ if (fsck_objects)
keeper[i++] = "--strict";
keeper[i++] = "--fix-thin";
keeper[i++] = hdr_arg;
@@ -731,14 +867,23 @@ static int delete_only(struct command *commands)
return 1;
}
-static void add_one_alternate_ref(const struct ref *ref, void *unused)
+static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused)
+{
+ add_extra_ref(".have", sha1, 0);
+}
+
+static void collect_one_alternate_ref(const struct ref *ref, void *data)
{
- add_extra_ref(".have", ref->old_sha1, 0);
+ struct sha1_array *sa = data;
+ sha1_array_append(sa, ref->old_sha1);
}
static void add_alternate_refs(void)
{
- foreach_alt_odb(refs_from_alternate_cb, add_one_alternate_ref);
+ struct sha1_array sa = SHA1_ARRAY_INIT;
+ for_each_alternate_ref(collect_one_alternate_ref, &sa);
+ sha1_array_for_each_unique(&sa, add_one_alternate_sha1, NULL);
+ sha1_array_clear(&sa);
}
int cmd_receive_pack(int argc, const char **argv, const char *prefix)
@@ -810,7 +955,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
unlink_or_warn(pack_lockfile);
if (report_status)
report(commands, unpack_status);
- run_receive_hook(commands, post_receive_hook);
+ run_receive_hook(commands, post_receive_hook, 1);
run_update_post_hook(commands);
if (auto_gc) {
const char *argv_gc_auto[] = {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index ebf610e64a..3a9c80f3db 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -777,6 +777,5 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[1], "delete"))
return cmd_reflog_delete(argc - 1, argv + 1, prefix);
- /* Not a recognized reflog command..*/
- usage(reflog_usage);
+ return cmd_log_reflog(argc, argv, prefix);
}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8424152269..c810643815 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -88,16 +88,6 @@ static inline int postfixcmp(const char *string, const char *postfix)
return strcmp(string + len1 - len2, postfix);
}
-static int opt_parse_track(const struct option *opt, const char *arg, int not)
-{
- struct string_list *list = opt->value;
- if (not)
- string_list_clear(list, 0);
- else
- string_list_append(list, arg);
- return 0;
-}
-
static int fetch_remote(const char *name)
{
const char *argv[] = { "fetch", name, NULL, NULL };
@@ -176,8 +166,8 @@ static int add(int argc, const char **argv)
TAGS_SET),
OPT_SET_INT(0, NULL, &fetch_tags,
"or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
- OPT_CALLBACK('t', "track", &track, "branch",
- "branch(es) to track", opt_parse_track),
+ OPT_STRING_LIST('t', "track", &track, "branch",
+ "branch(es) to track"),
OPT_STRING('m', "master", &master, "branch", "master branch"),
{ OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch",
"set up remote as a mirror to push to or fetch from",
@@ -193,8 +183,8 @@ static int add(int argc, const char **argv)
if (mirror && master)
die("specifying a master branch makes no sense with --mirror");
- if (mirror && track.nr)
- die("specifying branches to track makes no sense with --mirror");
+ if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
+ die("specifying branches to track makes sense only with fetch mirrors");
name = argv[0];
url = argv[1];
@@ -359,7 +349,8 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
}
- stale_refs = get_stale_heads(states->remote, fetch_map);
+ stale_refs = get_stale_heads(states->remote->fetch,
+ states->remote->fetch_refspec_nr, fetch_map);
for (ref = stale_refs; ref; ref = ref->next) {
struct string_list_item *item =
string_list_append(&states->stale, abbrev_branch(ref->name));
@@ -399,8 +390,8 @@ static int get_push_ref_states(const struct ref *remote_refs,
local_refs = get_local_heads();
push_map = copy_ref_list(remote_refs);
- match_refs(local_refs, &push_map, remote->push_refspec_nr,
- remote->push_refspec, MATCH_REFS_NONE);
+ match_push_refs(local_refs, &push_map, remote->push_refspec_nr,
+ remote->push_refspec, MATCH_REFS_NONE);
states->push.strdup_strings = 1;
for (ref = push_map; ref; ref = ref->next) {
@@ -580,7 +571,7 @@ static int read_remote_branches(const char *refname,
unsigned char orig_sha1[20];
const char *symref;
- strbuf_addf(&buf, "refs/remotes/%s", rename->old);
+ strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (!prefixcmp(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref(refname, orig_sha1, 1, &flag);
@@ -631,10 +622,11 @@ static int mv(int argc, const char **argv)
OPT_END()
};
struct remote *oldremote, *newremote;
- struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT,
+ old_remote_context = STRBUF_INIT;
struct string_list remote_branches = STRING_LIST_INIT_NODUP;
struct rename_info rename;
- int i;
+ int i, refspec_updated = 0;
if (argc != 3)
usage_with_options(builtin_remote_rename_usage, options);
@@ -669,15 +661,25 @@ static int mv(int argc, const char **argv)
strbuf_addf(&buf, "remote.%s.fetch", rename.new);
if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
return error("Could not remove config section '%s'", buf.buf);
+ strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
char *ptr;
strbuf_reset(&buf2);
strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
- ptr = strstr(buf2.buf, rename.old);
- if (ptr)
- strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
- rename.new, strlen(rename.new));
+ ptr = strstr(buf2.buf, old_remote_context.buf);
+ if (ptr) {
+ refspec_updated = 1;
+ strbuf_splice(&buf2,
+ ptr-buf2.buf + strlen(":refs/remotes/"),
+ strlen(rename.old), rename.new,
+ strlen(rename.new));
+ } else
+ warning("Not updating non-default fetch respec\n"
+ "\t%s\n"
+ "\tPlease update the configuration manually if necessary.",
+ buf2.buf);
+
if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
return error("Could not append '%s'", buf.buf);
}
@@ -695,6 +697,9 @@ static int mv(int argc, const char **argv)
}
}
+ if (!refspec_updated)
+ return 0;
+
/*
* First remove symrefs, then rename the rest, finally create
* the new symrefs.
@@ -1113,7 +1118,7 @@ static int show(int argc, const char **argv)
url = states.remote->url;
url_nr = states.remote->url_nr;
}
- for (i=0; i < url_nr; i++)
+ for (i = 0; i < url_nr; i++)
printf(" Push URL: %s\n", url[i]);
if (!i)
printf(" Push URL: %s\n", "(no URL)");
@@ -1394,7 +1399,7 @@ static int set_branches(int argc, const char **argv)
builtin_remote_setbranches_usage, 0);
if (argc == 0) {
error("no remote specified");
- usage_with_options(builtin_remote_seturl_usage, options);
+ usage_with_options(builtin_remote_setbranches_usage, options);
}
argv[argc] = NULL;
@@ -1422,7 +1427,7 @@ static int set_url(int argc, const char **argv)
"delete URLs"),
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+ argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
PARSE_OPT_KEEP_ARGV0);
if (add_mode && delete_mode)
diff --git a/builtin/replace.c b/builtin/replace.c
index fe3a647a36..517fa1031a 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -94,7 +94,7 @@ static int replace_object(const char *object_ref, const char *replace_ref,
"refs/replace/%s",
sha1_to_hex(object)) > sizeof(ref) - 1)
die("replace ref name too long: %.*s...", 50, ref);
- if (check_ref_format(ref))
+ if (check_refname_format(ref, 0))
die("'%s' is not a valid ref name.", ref);
if (!resolve_ref(ref, prev, 1, NULL))
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 82358855d1..08213c7c0b 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -12,74 +12,6 @@ static const char * const rerere_usage[] = {
NULL,
};
-/* these values are days */
-static int cutoff_noresolve = 15;
-static int cutoff_resolve = 60;
-
-static time_t rerere_created_at(const char *name)
-{
- struct stat st;
- return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
-}
-
-static time_t rerere_last_used_at(const char *name)
-{
- struct stat st;
- return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
-}
-
-static void unlink_rr_item(const char *name)
-{
- unlink(rerere_path(name, "thisimage"));
- unlink(rerere_path(name, "preimage"));
- unlink(rerere_path(name, "postimage"));
- rmdir(git_path("rr-cache/%s", name));
-}
-
-static int git_rerere_gc_config(const char *var, const char *value, void *cb)
-{
- if (!strcmp(var, "gc.rerereresolved"))
- cutoff_resolve = git_config_int(var, value);
- else if (!strcmp(var, "gc.rerereunresolved"))
- cutoff_noresolve = git_config_int(var, value);
- else
- return git_default_config(var, value, cb);
- return 0;
-}
-
-static void garbage_collect(struct string_list *rr)
-{
- struct string_list to_remove = STRING_LIST_INIT_DUP;
- DIR *dir;
- struct dirent *e;
- int i, cutoff;
- time_t now = time(NULL), then;
-
- git_config(git_rerere_gc_config, NULL);
- dir = opendir(git_path("rr-cache"));
- if (!dir)
- die_errno("unable to open rr-cache directory");
- while ((e = readdir(dir))) {
- if (is_dot_or_dotdot(e->d_name))
- continue;
-
- then = rerere_last_used_at(e->d_name);
- if (then) {
- cutoff = cutoff_resolve;
- } else {
- then = rerere_created_at(e->d_name);
- if (!then)
- continue;
- cutoff = cutoff_noresolve;
- }
- if (then < now - cutoff * 86400)
- string_list_append(&to_remove, e->d_name);
- }
- for (i = 0; i < to_remove.nr; i++)
- unlink_rr_item(to_remove.items[i].string);
- string_list_clear(&to_remove, 0);
-}
-
static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
{
int i;
@@ -148,14 +80,9 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
return 0;
if (!strcmp(argv[0], "clear")) {
- for (i = 0; i < merge_rr.nr; i++) {
- const char *name = (const char *)merge_rr.items[i].util;
- if (!has_rerere_resolution(name))
- unlink_rr_item(name);
- }
- unlink_or_warn(git_path("MERGE_RR"));
+ rerere_clear(&merge_rr);
} else if (!strcmp(argv[0], "gc"))
- garbage_collect(&merge_rr);
+ rerere_gc(&merge_rr);
else if (!strcmp(argv[0], "status"))
for (i = 0; i < merge_rr.nr; i++)
printf("%s\n", merge_rr.items[i].string);
diff --git a/builtin/reset.c b/builtin/reset.c
index 98bca044c1..811e8e252c 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -33,25 +33,6 @@ static const char *reset_type_names[] = {
N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL
};
-static char *args_to_str(const char **argv)
-{
- char *buf = NULL;
- unsigned long len, space = 0, nr = 0;
-
- for (; *argv; argv++) {
- len = strlen(*argv);
- ALLOC_GROW(buf, nr + 1 + len, space);
- if (nr)
- buf[nr++] = ' ';
- memcpy(buf + nr, *argv, len);
- nr += len;
- }
- ALLOC_GROW(buf, nr + 1, space);
- buf[nr] = '\0';
-
- return buf;
-}
-
static inline int is_merge(void)
{
return !access(git_path("MERGE_HEAD"), F_OK);
@@ -162,7 +143,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
for (i = 0; i < q->nr; i++) {
struct diff_filespec *one = q->queue[i]->one;
- if (one->mode) {
+ if (one->mode && !is_null_sha1(one->sha1)) {
struct cache_entry *ce;
ce = make_cache_entry(one->mode, one->sha1, one->path,
0, 0);
@@ -215,14 +196,18 @@ static int read_from_tree(const char *prefix, const char **argv,
return update_index_refresh(index_fd, lock, refresh_flags);
}
-static void prepend_reflog_action(const char *action, char *buf, size_t size)
+static void set_reflog_message(struct strbuf *sb, const char *action,
+ const char *rev)
{
- const char *sep = ": ";
const char *rla = getenv("GIT_REFLOG_ACTION");
- if (!rla)
- rla = sep = "";
- if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
- warning(_("Reflog action message too long: %.*s..."), 50, buf);
+
+ strbuf_reset(sb);
+ if (rla)
+ strbuf_addf(sb, "%s: %s", rla, action);
+ else if (rev)
+ strbuf_addf(sb, "reset: moving to %s", rev);
+ else
+ strbuf_addf(sb, "reset: %s", action);
}
static void die_if_unmerged_cache(int reset_type)
@@ -241,7 +226,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
struct commit *commit;
- char *reflog_action, msg[1024];
+ struct strbuf msg = STRBUF_INIT;
const struct option options[] = {
OPT__QUIET(&quiet, "be quiet, only report errors"),
OPT_SET_INT(0, "mixed", &reset_type,
@@ -261,8 +246,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, git_reset_usage,
PARSE_OPT_KEEP_DASHDASH);
- reflog_action = args_to_str(argv);
- setenv("GIT_REFLOG_ACTION", reflog_action, 0);
/*
* Possible arguments are:
@@ -357,13 +340,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
old_orig = sha1_old_orig;
if (!get_sha1("HEAD", sha1_orig)) {
orig = sha1_orig;
- prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
- update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+ set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+ update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
}
else if (old_orig)
delete_ref("ORIG_HEAD", old_orig, 0);
- prepend_reflog_action("updating HEAD", msg, sizeof(msg));
- update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+ set_reflog_message(&msg, "updating HEAD", rev);
+ update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
switch (reset_type) {
case HARD:
@@ -380,7 +363,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
remove_branch_state();
- free(reflog_action);
+ strbuf_release(&msg);
return update_ref_status;
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 9bfb94201f..ab3be7ca82 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -55,7 +55,9 @@ static void show_commit(struct commit *commit, void *data)
graph_show_commit(revs->graph);
if (revs->count) {
- if (commit->object.flags & SYMMETRIC_LEFT)
+ if (commit->object.flags & PATCHSAME)
+ revs->count_same++;
+ else if (commit->object.flags & SYMMETRIC_LEFT)
revs->count_left++;
else
revs->count_right++;
@@ -102,7 +104,8 @@ static void show_commit(struct commit *commit, void *data)
struct pretty_print_context ctx = {0};
ctx.abbrev = revs->abbrev;
ctx.date_mode = revs->date_mode;
- pretty_print_commit(revs->commit_format, commit, &buf, &ctx);
+ ctx.fmt = revs->commit_format;
+ pretty_print_commit(&ctx, commit, &buf);
if (revs->graph) {
if (buf.len) {
if (revs->commit_format != CMIT_FMT_ONELINE)
@@ -165,29 +168,24 @@ static void finish_commit(struct commit *commit, void *data)
commit->buffer = NULL;
}
-static void finish_object(struct object *obj, const struct name_path *path, const char *name)
+static void finish_object(struct object *obj,
+ const struct name_path *path, const char *name,
+ void *cb_data)
{
if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
die("missing blob object '%s'", sha1_to_hex(obj->sha1));
}
-static void show_object(struct object *obj, const struct name_path *path, const char *component)
+static void show_object(struct object *obj,
+ const struct name_path *path, const char *component,
+ void *cb_data)
{
- char *name = path_name(path, component);
- /* An object with name "foo\n0000000..." can be used to
- * confuse downstream "git pack-objects" very badly.
- */
- const char *ep = strchr(name, '\n');
+ struct rev_info *info = cb_data;
- finish_object(obj, path, name);
- if (ep) {
- printf("%s %.*s\n", sha1_to_hex(obj->sha1),
- (int) (ep - name),
- name);
- }
- else
- printf("%s %s\n", sha1_to_hex(obj->sha1), name);
- free(name);
+ finish_object(obj, path, component, cb_data);
+ if (info->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
+ parse_object(obj->sha1);
+ show_object_with_name(stdout, obj, path, component);
}
static void show_edge(struct commit *commit)
@@ -406,8 +404,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
&info);
if (revs.count) {
- if (revs.left_right)
+ if (revs.left_right && revs.cherry_mark)
+ printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
+ else if (revs.left_right)
printf("%d\t%d\n", revs.count_left, revs.count_right);
+ else if (revs.cherry_mark)
+ printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same);
else
printf("%d\n", revs.count_left + revs.count_right);
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index adb1cae4f2..98d1cbecca 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -44,6 +44,7 @@ static int is_rev_argument(const char *arg)
"--branches=",
"--branches",
"--header",
+ "--ignore-missing",
"--max-age=",
"--max-count=",
"--min-age=",
@@ -467,6 +468,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
return 0;
}
+ if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
+ const char *gitdir = resolve_gitdir(argv[2]);
+ if (!gitdir)
+ die("not a gitdir '%s'", argv[2]);
+ puts(gitdir);
+ return 0;
+ }
+
if (argc > 1 && !strcmp("-h", argv[1]))
usage(builtin_rev_parse_usage);
diff --git a/builtin/revert.c b/builtin/revert.c
index f697e66953..028bcbcd75 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -13,6 +13,8 @@
#include "rerere.h"
#include "merge-recursive.h"
#include "refs.h"
+#include "dir.h"
+#include "sequencer.h"
/*
* This implements the builtins revert and cherry-pick.
@@ -27,85 +29,212 @@
static const char * const revert_usage[] = {
"git revert [options] <commit-ish>",
+ "git revert <subcommand>",
NULL
};
static const char * const cherry_pick_usage[] = {
"git cherry-pick [options] <commit-ish>",
+ "git cherry-pick <subcommand>",
NULL
};
-static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
-static enum { REVERT, CHERRY_PICK } action;
-static struct commit *commit;
-static int commit_argc;
-static const char **commit_argv;
-static int allow_rerere_auto;
+enum replay_action { REVERT, CHERRY_PICK };
+enum replay_subcommand {
+ REPLAY_NONE,
+ REPLAY_REMOVE_STATE,
+ REPLAY_CONTINUE,
+ REPLAY_ROLLBACK
+};
+
+struct replay_opts {
+ enum replay_action action;
+ enum replay_subcommand subcommand;
-static const char *me;
+ /* Boolean options */
+ int edit;
+ int record_origin;
+ int no_commit;
+ int signoff;
+ int allow_ff;
+ int allow_rerere_auto;
-/* Merge strategy. */
-static const char *strategy;
-static const char **xopts;
-static size_t xopts_nr, xopts_alloc;
+ int mainline;
+
+ /* Merge strategy */
+ const char *strategy;
+ const char **xopts;
+ size_t xopts_nr, xopts_alloc;
+
+ /* Only used by REPLAY_NONE */
+ struct rev_info *revs;
+};
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
+static const char *action_name(const struct replay_opts *opts)
+{
+ return opts->action == REVERT ? "revert" : "cherry-pick";
+}
+
static char *get_encoding(const char *message);
-static const char * const *revert_or_cherry_pick_usage(void)
+static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
{
- return action == REVERT ? revert_usage : cherry_pick_usage;
+ return opts->action == REVERT ? revert_usage : cherry_pick_usage;
}
static int option_parse_x(const struct option *opt,
const char *arg, int unset)
{
+ struct replay_opts **opts_ptr = opt->value;
+ struct replay_opts *opts = *opts_ptr;
+
if (unset)
return 0;
- ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
- xopts[xopts_nr++] = xstrdup(arg);
+ ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+ opts->xopts[opts->xopts_nr++] = xstrdup(arg);
return 0;
}
-static void parse_args(int argc, const char **argv)
+static void verify_opt_compatible(const char *me, const char *base_opt, ...)
+{
+ const char *this_opt;
+ va_list ap;
+
+ va_start(ap, base_opt);
+ while ((this_opt = va_arg(ap, const char *))) {
+ if (va_arg(ap, int))
+ break;
+ }
+ va_end(ap);
+
+ if (this_opt)
+ die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
+}
+
+static void verify_opt_mutually_compatible(const char *me, ...)
{
- const char * const * usage_str = revert_or_cherry_pick_usage();
- int noop;
+ const char *opt1, *opt2 = NULL;
+ va_list ap;
+
+ va_start(ap, me);
+ while ((opt1 = va_arg(ap, const char *))) {
+ if (va_arg(ap, int))
+ break;
+ }
+ if (opt1) {
+ while ((opt2 = va_arg(ap, const char *))) {
+ if (va_arg(ap, int))
+ break;
+ }
+ }
+
+ if (opt1 && opt2)
+ die(_("%s: %s cannot be used with %s"), me, opt1, opt2);
+}
+
+static void parse_args(int argc, const char **argv, struct replay_opts *opts)
+{
+ const char * const * usage_str = revert_or_cherry_pick_usage(opts);
+ const char *me = action_name(opts);
+ int remove_state = 0;
+ int contin = 0;
+ int rollback = 0;
struct option options[] = {
- OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
- OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
- { OPTION_BOOLEAN, 'r', NULL, &noop, NULL, "no-op (backward compatibility)",
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 0 },
- OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
- OPT_INTEGER('m', "mainline", &mainline, "parent number"),
- OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
- OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
- OPT_CALLBACK('X', "strategy-option", &xopts, "option",
+ OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
+ OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
+ OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
+ OPT_NOOP_NOARG('r', NULL),
+ OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
+ OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
+ OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
+ OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
+ OPT_CALLBACK('X', "strategy-option", &opts, "option",
"option for merge strategy", option_parse_x),
OPT_END(),
OPT_END(),
OPT_END(),
};
- if (action == CHERRY_PICK) {
+ if (opts->action == CHERRY_PICK) {
struct option cp_extra[] = {
- OPT_BOOLEAN('x', NULL, &no_replay, "append commit name"),
- OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
+ OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
+ OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
OPT_END(),
};
if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
die(_("program error"));
}
- commit_argc = parse_options(argc, argv, NULL, options, usage_str,
- PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN);
- if (commit_argc < 2)
- usage_with_options(usage_str, options);
+ argc = parse_options(argc, argv, NULL, options, usage_str,
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ /* Check for incompatible subcommands */
+ verify_opt_mutually_compatible(me,
+ "--quit", remove_state,
+ "--continue", contin,
+ "--abort", rollback,
+ NULL);
+
+ /* Set the subcommand */
+ if (remove_state)
+ opts->subcommand = REPLAY_REMOVE_STATE;
+ else if (contin)
+ opts->subcommand = REPLAY_CONTINUE;
+ else if (rollback)
+ opts->subcommand = REPLAY_ROLLBACK;
+ else
+ opts->subcommand = REPLAY_NONE;
+
+ /* Check for incompatible command line arguments */
+ if (opts->subcommand != REPLAY_NONE) {
+ char *this_operation;
+ if (opts->subcommand == REPLAY_REMOVE_STATE)
+ this_operation = "--quit";
+ else if (opts->subcommand == REPLAY_CONTINUE)
+ this_operation = "--continue";
+ else {
+ assert(opts->subcommand == REPLAY_ROLLBACK);
+ this_operation = "--abort";
+ }
- commit_argv = argv;
+ verify_opt_compatible(me, this_operation,
+ "--no-commit", opts->no_commit,
+ "--signoff", opts->signoff,
+ "--mainline", opts->mainline,
+ "--strategy", opts->strategy ? 1 : 0,
+ "--strategy-option", opts->xopts ? 1 : 0,
+ "-x", opts->record_origin,
+ "--ff", opts->allow_ff,
+ NULL);
+ }
+
+ if (opts->allow_ff)
+ verify_opt_compatible(me, "--ff",
+ "--signoff", opts->signoff,
+ "--no-commit", opts->no_commit,
+ "-x", opts->record_origin,
+ "--edit", opts->edit,
+ NULL);
+
+ if (opts->subcommand != REPLAY_NONE) {
+ opts->revs = NULL;
+ } else {
+ opts->revs = xmalloc(sizeof(*opts->revs));
+ init_revisions(opts->revs, NULL);
+ opts->revs->no_walk = 1;
+ if (argc < 2)
+ usage_with_options(usage_str, options);
+ argc = setup_revisions(argc, argv, opts->revs, NULL);
+ }
+
+ if (argc > 1)
+ usage_with_options(usage_str, options);
}
struct commit_message {
@@ -116,25 +245,25 @@ struct commit_message {
const char *message;
};
-static int get_message(const char *raw_message, struct commit_message *out)
+static int get_message(struct commit *commit, struct commit_message *out)
{
const char *encoding;
const char *abbrev, *subject;
int abbrev_len, subject_len;
char *q;
- if (!raw_message)
+ if (!commit->buffer)
return -1;
- encoding = get_encoding(raw_message);
+ encoding = get_encoding(commit->buffer);
if (!encoding)
encoding = "UTF-8";
if (!git_commit_encoding)
git_commit_encoding = "UTF-8";
out->reencoded_message = NULL;
- out->message = raw_message;
+ out->message = commit->buffer;
if (strcmp(encoding, git_commit_encoding))
- out->reencoded_message = reencode_string(raw_message,
+ out->reencoded_message = reencode_string(commit->buffer,
git_commit_encoding, encoding);
if (out->reencoded_message)
out->message = out->reencoded_message;
@@ -167,9 +296,6 @@ static char *get_encoding(const char *message)
{
const char *p = message, *eol;
- if (!p)
- die (_("Could not read commit message of %s"),
- sha1_to_hex(commit->object.sha1));
while (*p && *p != '\n') {
for (eol = p + 1; *eol && *eol != '\n'; eol++)
; /* do nothing */
@@ -185,45 +311,24 @@ static char *get_encoding(const char *message)
return NULL;
}
-static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
-{
- const char *p = message;
- while (*p && (*p != '\n' || p[1] != '\n'))
- p++;
-
- if (!*p)
- strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
-
- p += 2;
- strbuf_addstr(msgbuf, p);
-}
-
-static void write_cherry_pick_head(void)
+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
{
+ const char *filename;
int fd;
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
- fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("%s", pseudoref);
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("CHERRY_PICK_HEAD"));
+ die_errno(_("Could not open '%s' for writing"), filename);
if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
- die_errno(_("Could not write to '%s'"), git_path("CHERRY_PICK_HEAD"));
+ die_errno(_("Could not write to '%s'"), filename);
strbuf_release(&buf);
}
-static void advise(const char *advice, ...)
-{
- va_list params;
-
- va_start(params, advice);
- vreportf("hint: ", advice, params);
- va_end(params);
-}
-
-static void print_advice(void)
+static void print_advice(int show_hint)
{
char *msg = getenv("GIT_CHERRY_PICK_HELP");
@@ -238,9 +343,11 @@ static void print_advice(void)
return;
}
- advise("after resolving the conflicts, mark the corrected paths");
- advise("with 'git add <paths>' or 'git rm <paths>'");
- advise("and commit the result with 'git commit'");
+ if (show_hint) {
+ advise("after resolving the conflicts, mark the corrected paths");
+ advise("with 'git add <paths>' or 'git rm <paths>'");
+ advise("and commit the result with 'git commit'");
+ }
}
static void write_message(struct strbuf *msgbuf, const char *filename)
@@ -250,7 +357,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
int msg_fd = hold_lock_file_for_update(&msg_file, filename,
LOCK_DIE_ON_ERROR);
if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
- die_errno(_("Could not write to %s."), filename);
+ die_errno(_("Could not write to %s"), filename);
strbuf_release(msgbuf);
if (commit_lock_file(&msg_file) < 0)
die(_("Error wrapping up %s"), filename);
@@ -258,33 +365,23 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
static struct tree *empty_tree(void)
{
- struct tree *tree = xcalloc(1, sizeof(struct tree));
-
- tree->object.parsed = 1;
- tree->object.type = OBJ_TREE;
- pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
- return tree;
+ return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
}
-static NORETURN void die_dirty_index(const char *me)
+static int error_dirty_index(struct replay_opts *opts)
{
- if (read_cache_unmerged()) {
- die_resolve_conflict(me);
- } else {
- if (advice_commit_before_merge) {
- if (action == REVERT)
- die(_("Your local changes would be overwritten by revert.\n"
- "Please, commit your changes or stash them to proceed."));
- else
- die(_("Your local changes would be overwritten by cherry-pick.\n"
- "Please, commit your changes or stash them to proceed."));
- } else {
- if (action == REVERT)
- die(_("Your local changes would be overwritten by revert.\n"));
- else
- die(_("Your local changes would be overwritten by cherry-pick.\n"));
- }
- }
+ if (read_cache_unmerged())
+ return error_resolve_conflict(action_name(opts));
+
+ /* Different translation strings for cherry-pick and revert */
+ if (opts->action == CHERRY_PICK)
+ error(_("Your local changes would be overwritten by cherry-pick."));
+ else
+ error(_("Your local changes would be overwritten by revert."));
+
+ if (advice_commit_before_merge)
+ advise(_("Commit your changes or stash them to proceed."));
+ return -1;
}
static int fast_forward_to(const unsigned char *to, const unsigned char *from)
@@ -300,7 +397,8 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
static int do_recursive_merge(struct commit *base, struct commit *next,
const char *base_label, const char *next_label,
- unsigned char *head, struct strbuf *msgbuf)
+ unsigned char *head, struct strbuf *msgbuf,
+ struct replay_opts *opts)
{
struct merge_options o;
struct tree *result, *next_tree, *base_tree, *head_tree;
@@ -321,7 +419,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
next_tree = next ? next->tree : empty_tree();
base_tree = base ? base->tree : empty_tree();
- for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
+ for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
parse_merge_opt(&o, *xopt);
clean = merge_trees(&o,
@@ -332,7 +430,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
(write_cache(index_fd, active_cache, active_nr) ||
commit_locked_index(&index_lock)))
/* TRANSLATORS: %s will be "revert" or "cherry-pick" */
- die(_("%s: Unable to write new index file"), me);
+ die(_("%s: Unable to write new index file"), action_name(opts));
rollback_lock_file(&index_lock);
if (!clean) {
@@ -361,7 +459,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
* If we are revert, or if our cherry-pick results in a hand merge,
* we had better say that the current user is responsible for that.
*/
-static int run_git_commit(const char *defmsg)
+static int run_git_commit(const char *defmsg, struct replay_opts *opts)
{
/* 6 is max possible length of our args array including NULL */
const char *args[6];
@@ -369,9 +467,9 @@ static int run_git_commit(const char *defmsg)
args[i++] = "commit";
args[i++] = "-n";
- if (signoff)
+ if (opts->signoff)
args[i++] = "-s";
- if (!edit) {
+ if (!opts->edit) {
args[i++] = "-F";
args[i++] = defmsg;
}
@@ -380,7 +478,7 @@ static int run_git_commit(const char *defmsg)
return run_command_v_opt(args, RUN_GIT_CMD);
}
-static int do_pick_commit(void)
+static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
{
unsigned char head[20];
struct commit *base, *next, *parent;
@@ -390,7 +488,7 @@ static int do_pick_commit(void)
struct strbuf msgbuf = STRBUF_INIT;
int res;
- if (no_commit) {
+ if (opts->no_commit) {
/*
* We do not intend to commit immediately. We just want to
* merge the differences in, so let's compute the tree
@@ -401,15 +499,13 @@ static int do_pick_commit(void)
die (_("Your index file is unmerged."));
} else {
if (get_sha1("HEAD", head))
- die (_("You do not have a valid HEAD"));
+ return error(_("You do not have a valid HEAD"));
if (index_differs_from("HEAD", 0))
- die_dirty_index(me);
+ return error_dirty_index(opts);
}
discard_cache();
if (!commit->parents) {
- if (action == REVERT)
- die (_("Cannot revert a root commit"));
parent = NULL;
}
else if (commit->parents->next) {
@@ -417,36 +513,36 @@ static int do_pick_commit(void)
int cnt;
struct commit_list *p;
- if (!mainline)
- die(_("Commit %s is a merge but no -m option was given."),
- sha1_to_hex(commit->object.sha1));
+ if (!opts->mainline)
+ return error(_("Commit %s is a merge but no -m option was given."),
+ sha1_to_hex(commit->object.sha1));
for (cnt = 1, p = commit->parents;
- cnt != mainline && p;
+ cnt != opts->mainline && p;
cnt++)
p = p->next;
- if (cnt != mainline || !p)
- die(_("Commit %s does not have parent %d"),
- sha1_to_hex(commit->object.sha1), mainline);
+ if (cnt != opts->mainline || !p)
+ return error(_("Commit %s does not have parent %d"),
+ sha1_to_hex(commit->object.sha1), opts->mainline);
parent = p->item;
- } else if (0 < mainline)
- die(_("Mainline was specified but commit %s is not a merge."),
- sha1_to_hex(commit->object.sha1));
+ } else if (0 < opts->mainline)
+ return error(_("Mainline was specified but commit %s is not a merge."),
+ sha1_to_hex(commit->object.sha1));
else
parent = commit->parents->item;
- if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
+ if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
return fast_forward_to(commit->object.sha1, head);
if (parent && parse_commit(parent) < 0)
/* TRANSLATORS: The first %s will be "revert" or
"cherry-pick", the second %s a SHA1 */
- die(_("%s: cannot parse parent commit %s"),
- me, sha1_to_hex(parent->object.sha1));
+ return error(_("%s: cannot parse parent commit %s"),
+ action_name(opts), sha1_to_hex(parent->object.sha1));
- if (get_message(commit->buffer, &msg) != 0)
- die(_("Cannot get commit message for %s"),
- sha1_to_hex(commit->object.sha1));
+ if (get_message(commit, &msg) != 0)
+ return error(_("Cannot get commit message for %s"),
+ sha1_to_hex(commit->object.sha1));
/*
* "commit" is an existing commit. We would want to apply
@@ -457,7 +553,7 @@ static int do_pick_commit(void)
defmsg = git_pathdup("MERGE_MSG");
- if (action == REVERT) {
+ if (opts->action == REVERT) {
base = commit;
base_label = msg.label;
next = parent;
@@ -467,29 +563,40 @@ static int do_pick_commit(void)
strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
- if (commit->parents->next) {
+ if (commit->parents && commit->parents->next) {
strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
}
strbuf_addstr(&msgbuf, ".\n");
} else {
+ const char *p;
+
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
- add_message_to_msg(&msgbuf, msg.message);
- if (no_replay) {
+
+ /*
+ * Append the commit log message to msgbuf; it starts
+ * after the tree, parent, author, committer
+ * information followed by "\n\n".
+ */
+ p = strstr(msg.message, "\n\n");
+ if (p) {
+ p += 2;
+ strbuf_addstr(&msgbuf, p);
+ }
+
+ if (opts->record_origin) {
strbuf_addstr(&msgbuf, "(cherry picked from commit ");
strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
strbuf_addstr(&msgbuf, ")\n");
}
- if (!no_commit)
- write_cherry_pick_head();
}
- if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+ if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REVERT) {
res = do_recursive_merge(base, next, base_label, next_label,
- head, &msgbuf);
+ head, &msgbuf, opts);
write_message(&msgbuf, defmsg);
} else {
struct commit_list *common = NULL;
@@ -499,23 +606,34 @@ static int do_pick_commit(void)
commit_list_insert(base, &common);
commit_list_insert(next, &remotes);
- res = try_merge_command(strategy, xopts_nr, xopts, common,
- sha1_to_hex(head), remotes);
+ res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+ common, sha1_to_hex(head), remotes);
free_commit_list(common);
free_commit_list(remotes);
}
+ /*
+ * If the merge was clean or if it failed due to conflict, we write
+ * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
+ * However, if the merge did not even start, then we don't want to
+ * write it at all.
+ */
+ if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))
+ write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+ if (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))
+ write_cherry_pick_head(commit, "REVERT_HEAD");
+
if (res) {
- error(action == REVERT
+ error(opts->action == REVERT
? _("could not revert %s... %s")
: _("could not apply %s... %s"),
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
msg.subject);
- print_advice();
- rerere(allow_rerere_auto);
+ print_advice(res == 1);
+ rerere(opts->allow_rerere_auto);
} else {
- if (!no_commit)
- res = run_git_commit(defmsg);
+ if (!opts->no_commit)
+ res = run_git_commit(defmsg, opts);
}
free_message(&msg);
@@ -524,84 +642,523 @@ static int do_pick_commit(void)
return res;
}
-static void prepare_revs(struct rev_info *revs)
+static void prepare_revs(struct replay_opts *opts)
{
- int argc;
-
- init_revisions(revs, NULL);
- revs->no_walk = 1;
- if (action != REVERT)
- revs->reverse = 1;
-
- argc = setup_revisions(commit_argc, commit_argv, revs, NULL);
- if (argc > 1)
- usage(*revert_or_cherry_pick_usage());
+ if (opts->action != REVERT)
+ opts->revs->reverse ^= 1;
- if (prepare_revision_walk(revs))
+ if (prepare_revision_walk(opts->revs))
die(_("revision walk setup failed"));
- if (!revs->commits)
+ if (!opts->revs->commits)
die(_("empty commit set passed"));
}
-static void read_and_refresh_cache(const char *me)
+static void read_and_refresh_cache(struct replay_opts *opts)
{
static struct lock_file index_lock;
int index_fd = hold_locked_index(&index_lock, 0);
if (read_index_preload(&the_index, NULL) < 0)
- die(_("git %s: failed to read the index"), me);
+ die(_("git %s: failed to read the index"), action_name(opts));
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
if (the_index.cache_changed) {
if (write_index(&the_index, index_fd) ||
commit_locked_index(&index_lock))
- die(_("git %s: failed to refresh the index"), me);
+ die(_("git %s: failed to refresh the index"), action_name(opts));
}
rollback_lock_file(&index_lock);
}
-static int revert_or_cherry_pick(int argc, const char **argv)
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ * struct commit_list *list;
+ * struct commit_list **next = &list;
+ *
+ * next = commit_list_append(c1, next);
+ * next = commit_list_append(c2, next);
+ * assert(commit_list_count(list) == 2);
+ * return list;
+ */
+static struct commit_list **commit_list_append(struct commit *commit,
+ struct commit_list **next)
{
- struct rev_info revs;
+ struct commit_list *new = xmalloc(sizeof(struct commit_list));
+ new->item = commit;
+ *next = new;
+ new->next = NULL;
+ return &new->next;
+}
- git_config(git_default_config, NULL);
- me = action == REVERT ? "revert" : "cherry-pick";
- setenv(GIT_REFLOG_ACTION, me, 0);
- parse_args(argc, argv);
+static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
+ struct replay_opts *opts)
+{
+ struct commit_list *cur = NULL;
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+ const char *sha1_abbrev = NULL;
+ const char *action_str = opts->action == REVERT ? "revert" : "pick";
+
+ for (cur = todo_list; cur; cur = cur->next) {
+ sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
+ if (get_message(cur->item, &msg))
+ return error(_("Cannot get commit message for %s"), sha1_abbrev);
+ strbuf_addf(buf, "%s %s %s\n", action_str, sha1_abbrev, msg.subject);
+ }
+ return 0;
+}
+
+static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
+{
+ unsigned char commit_sha1[20];
+ char sha1_abbrev[40];
+ enum replay_action action;
+ int insn_len = 0;
+ char *p, *q;
+
+ if (!prefixcmp(start, "pick ")) {
+ action = CHERRY_PICK;
+ insn_len = strlen("pick");
+ p = start + insn_len + 1;
+ } else if (!prefixcmp(start, "revert ")) {
+ action = REVERT;
+ insn_len = strlen("revert");
+ p = start + insn_len + 1;
+ } else
+ return NULL;
+
+ q = strchr(p, ' ');
+ if (!q)
+ return NULL;
+ q++;
+
+ strlcpy(sha1_abbrev, p, q - p);
+
+ /*
+ * Verify that the action matches up with the one in
+ * opts; we don't support arbitrary instructions
+ */
+ if (action != opts->action) {
+ const char *action_str;
+ action_str = action == REVERT ? "revert" : "cherry-pick";
+ error(_("Cannot %s during a %s"), action_str, action_name(opts));
+ return NULL;
+ }
+
+ if (get_sha1(sha1_abbrev, commit_sha1) < 0)
+ return NULL;
+
+ return lookup_commit_reference(commit_sha1);
+}
+
+static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
+ struct replay_opts *opts)
+{
+ struct commit_list **next = todo_list;
+ struct commit *commit;
+ char *p = buf;
+ int i;
+
+ for (i = 1; *p; i++) {
+ commit = parse_insn_line(p, opts);
+ if (!commit)
+ return error(_("Could not parse line %d."), i);
+ next = commit_list_append(commit, next);
+ p = strchrnul(p, '\n');
+ if (*p)
+ p++;
+ }
+ if (!*todo_list)
+ return error(_("No commits parsed."));
+ return 0;
+}
+
+static void read_populate_todo(struct commit_list **todo_list,
+ struct replay_opts *opts)
+{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
+ struct strbuf buf = STRBUF_INIT;
+ int fd, res;
+
+ fd = open(todo_file, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("Could not open %s"), todo_file);
+ if (strbuf_read(&buf, fd, 0) < 0) {
+ close(fd);
+ strbuf_release(&buf);
+ die(_("Could not read %s."), todo_file);
+ }
+ close(fd);
+
+ res = parse_insn_buffer(buf.buf, todo_list, opts);
+ strbuf_release(&buf);
+ if (res)
+ die(_("Unusable instruction sheet: %s"), todo_file);
+}
- if (allow_ff) {
- if (signoff)
- die(_("cherry-pick --ff cannot be used with --signoff"));
- if (no_commit)
- die(_("cherry-pick --ff cannot be used with --no-commit"));
- if (no_replay)
- die(_("cherry-pick --ff cannot be used with -x"));
- if (edit)
- die(_("cherry-pick --ff cannot be used with --edit"));
+static int populate_opts_cb(const char *key, const char *value, void *data)
+{
+ struct replay_opts *opts = data;
+ int error_flag = 1;
+
+ if (!value)
+ error_flag = 0;
+ else if (!strcmp(key, "options.no-commit"))
+ opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.edit"))
+ opts->edit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.signoff"))
+ opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.record-origin"))
+ opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.allow-ff"))
+ opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.mainline"))
+ opts->mainline = git_config_int(key, value);
+ else if (!strcmp(key, "options.strategy"))
+ git_config_string(&opts->strategy, key, value);
+ else if (!strcmp(key, "options.strategy-option")) {
+ ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+ opts->xopts[opts->xopts_nr++] = xstrdup(value);
+ } else
+ return error(_("Invalid key: %s"), key);
+
+ if (!error_flag)
+ return error(_("Invalid value for %s: %s"), key, value);
+
+ return 0;
+}
+
+static void read_populate_opts(struct replay_opts **opts_ptr)
+{
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+ if (!file_exists(opts_file))
+ return;
+ if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
+ die(_("Malformed options sheet: %s"), opts_file);
+}
+
+static void walk_revs_populate_todo(struct commit_list **todo_list,
+ struct replay_opts *opts)
+{
+ struct commit *commit;
+ struct commit_list **next;
+
+ prepare_revs(opts);
+
+ next = todo_list;
+ while ((commit = get_revision(opts->revs)))
+ next = commit_list_append(commit, next);
+}
+
+static int create_seq_dir(void)
+{
+ const char *seq_dir = git_path(SEQ_DIR);
+
+ if (file_exists(seq_dir)) {
+ error(_("a cherry-pick or revert is already in progress"));
+ advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
+ return -1;
+ }
+ else if (mkdir(seq_dir, 0777) < 0)
+ die_errno(_("Could not create sequencer directory %s"), seq_dir);
+ return 0;
+}
+
+static void save_head(const char *head)
+{
+ const char *head_file = git_path(SEQ_HEAD_FILE);
+ static struct lock_file head_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
+
+ fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+ strbuf_addf(&buf, "%s\n", head);
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ die_errno(_("Could not write to %s"), head_file);
+ if (commit_lock_file(&head_lock) < 0)
+ die(_("Error wrapping up %s."), head_file);
+}
+
+static int reset_for_rollback(const unsigned char *sha1)
+{
+ const char *argv[4]; /* reset --merge <arg> + NULL */
+ argv[0] = "reset";
+ argv[1] = "--merge";
+ argv[2] = sha1_to_hex(sha1);
+ argv[3] = NULL;
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int rollback_single_pick(void)
+{
+ unsigned char head_sha1[20];
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ if (!resolve_ref("HEAD", head_sha1, 0, NULL))
+ return error(_("cannot resolve HEAD"));
+ if (is_null_sha1(head_sha1))
+ return error(_("cannot abort from a branch yet to be born"));
+ return reset_for_rollback(head_sha1);
+}
+
+static int sequencer_rollback(struct replay_opts *opts)
+{
+ const char *filename;
+ FILE *f;
+ unsigned char sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+
+ filename = git_path(SEQ_HEAD_FILE);
+ f = fopen(filename, "r");
+ if (!f && errno == ENOENT) {
+ /*
+ * There is no multiple-cherry-pick in progress.
+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
+ * a single-cherry-pick in progress, abort that.
+ */
+ return rollback_single_pick();
}
+ if (!f)
+ return error(_("cannot open %s: %s"), filename,
+ strerror(errno));
+ if (strbuf_getline(&buf, f, '\n')) {
+ error(_("cannot read %s: %s"), filename, ferror(f) ?
+ strerror(errno) : _("unexpected end of file"));
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+ error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
+ filename);
+ goto fail;
+ }
+ if (reset_for_rollback(sha1))
+ goto fail;
+ remove_sequencer_state();
+ strbuf_release(&buf);
+ return 0;
+fail:
+ strbuf_release(&buf);
+ return -1;
+}
- read_and_refresh_cache(me);
+static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
+{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
+ static struct lock_file todo_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
- prepare_revs(&revs);
+ fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+ if (format_todo(&buf, todo_list, opts) < 0)
+ die(_("Could not format %s."), todo_file);
+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
+ strbuf_release(&buf);
+ die_errno(_("Could not write to %s"), todo_file);
+ }
+ if (commit_lock_file(&todo_lock) < 0) {
+ strbuf_release(&buf);
+ die(_("Error wrapping up %s."), todo_file);
+ }
+ strbuf_release(&buf);
+}
- while ((commit = get_revision(&revs))) {
- int res = do_pick_commit();
+static void save_opts(struct replay_opts *opts)
+{
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+ if (opts->no_commit)
+ git_config_set_in_file(opts_file, "options.no-commit", "true");
+ if (opts->edit)
+ git_config_set_in_file(opts_file, "options.edit", "true");
+ if (opts->signoff)
+ git_config_set_in_file(opts_file, "options.signoff", "true");
+ if (opts->record_origin)
+ git_config_set_in_file(opts_file, "options.record-origin", "true");
+ if (opts->allow_ff)
+ git_config_set_in_file(opts_file, "options.allow-ff", "true");
+ if (opts->mainline) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "%d", opts->mainline);
+ git_config_set_in_file(opts_file, "options.mainline", buf.buf);
+ strbuf_release(&buf);
+ }
+ if (opts->strategy)
+ git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+ if (opts->xopts) {
+ int i;
+ for (i = 0; i < opts->xopts_nr; i++)
+ git_config_set_multivar_in_file(opts_file,
+ "options.strategy-option",
+ opts->xopts[i], "^$", 0);
+ }
+}
+
+static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
+{
+ struct commit_list *cur;
+ int res;
+
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ if (opts->allow_ff)
+ assert(!(opts->signoff || opts->no_commit ||
+ opts->record_origin || opts->edit));
+ read_and_refresh_cache(opts);
+
+ for (cur = todo_list; cur; cur = cur->next) {
+ save_todo(cur, opts);
+ res = do_pick_commit(cur->item, opts);
if (res)
return res;
}
+ /*
+ * Sequence of picks finished successfully; cleanup by
+ * removing the .git/sequencer directory
+ */
+ remove_sequencer_state();
return 0;
}
+static int continue_single_pick(void)
+{
+ const char *argv[] = { "commit", NULL };
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int sequencer_continue(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
+ return continue_single_pick();
+ read_populate_opts(&opts);
+ read_populate_todo(&todo_list, opts);
+
+ /* Verify that the conflict has been resolved */
+ if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+ file_exists(git_path("REVERT_HEAD"))) {
+ int ret = continue_single_pick();
+ if (ret)
+ return ret;
+ }
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ todo_list = todo_list->next;
+ return pick_commits(todo_list, opts);
+}
+
+static int single_pick(struct commit *cmit, struct replay_opts *opts)
+{
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ return do_pick_commit(cmit, opts);
+}
+
+static int pick_revisions(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+ unsigned char sha1[20];
+
+ if (opts->subcommand == REPLAY_NONE)
+ assert(opts->revs);
+
+ read_and_refresh_cache(opts);
+
+ /*
+ * Decide what to do depending on the arguments; a fresh
+ * cherry-pick should be handled differently from an existing
+ * one that is being continued
+ */
+ if (opts->subcommand == REPLAY_REMOVE_STATE) {
+ remove_sequencer_state();
+ return 0;
+ }
+ if (opts->subcommand == REPLAY_ROLLBACK)
+ return sequencer_rollback(opts);
+ if (opts->subcommand == REPLAY_CONTINUE)
+ return sequencer_continue(opts);
+
+ /*
+ * If we were called as "git cherry-pick <commit>", just
+ * cherry-pick/revert it, set CHERRY_PICK_HEAD /
+ * REVERT_HEAD, and don't touch the sequencer state.
+ * This means it is possible to cherry-pick in the middle
+ * of a cherry-pick sequence.
+ */
+ if (opts->revs->cmdline.nr == 1 &&
+ opts->revs->cmdline.rev->whence == REV_CMD_REV &&
+ opts->revs->no_walk &&
+ !opts->revs->cmdline.rev->flags) {
+ struct commit *cmit;
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+ cmit = get_revision(opts->revs);
+ if (!cmit || get_revision(opts->revs))
+ die("BUG: expected exactly one commit from walk");
+ return single_pick(cmit, opts);
+ }
+
+ /*
+ * Start a new cherry-pick/ revert sequence; but
+ * first, make sure that an existing one isn't in
+ * progress
+ */
+
+ walk_revs_populate_todo(&todo_list, opts);
+ if (create_seq_dir() < 0)
+ return -1;
+ if (get_sha1("HEAD", sha1)) {
+ if (opts->action == REVERT)
+ return error(_("Can't revert as initial commit"));
+ return error(_("Can't cherry-pick into empty head"));
+ }
+ save_head(sha1_to_hex(sha1));
+ save_opts(opts);
+ return pick_commits(todo_list, opts);
+}
+
int cmd_revert(int argc, const char **argv, const char *prefix)
{
+ struct replay_opts opts;
+ int res;
+
+ memset(&opts, 0, sizeof(opts));
if (isatty(0))
- edit = 1;
- action = REVERT;
- return revert_or_cherry_pick(argc, argv);
+ opts.edit = 1;
+ opts.action = REVERT;
+ git_config(git_default_config, NULL);
+ parse_args(argc, argv, &opts);
+ res = pick_revisions(&opts);
+ if (res < 0)
+ die(_("revert failed"));
+ return res;
}
int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
{
- action = CHERRY_PICK;
- return revert_or_cherry_pick(argc, argv);
+ struct replay_opts opts;
+ int res;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.action = CHERRY_PICK;
+ git_config(git_default_config, NULL);
+ parse_args(argc, argv, &opts);
+ res = pick_revisions(&opts);
+ if (res < 0)
+ die(_("cherry-pick failed"));
+ return res;
}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 8b0911c0d2..cd1115ffc6 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -228,8 +228,11 @@ static void print_helper_status(struct ref *ref)
static int sideband_demux(int in, int out, void *data)
{
- int *fd = data;
- int ret = recv_sideband("send-pack", fd[0], out);
+ int *fd = data, ret;
+#ifdef NO_PTHREADS
+ close(fd[1]);
+#endif
+ ret = recv_sideband("send-pack", fd[0], out);
close(out);
return ret;
}
@@ -331,7 +334,7 @@ int send_pack(struct send_pack_args *args,
demux.data = fd;
demux.out = -1;
if (start_async(&demux))
- die("receive-pack: unable to fork off sideband demultiplexer");
+ die("send-pack: unable to fork off sideband demultiplexer");
in = demux.out;
}
@@ -339,6 +342,10 @@ int send_pack(struct send_pack_args *args,
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
for (ref = remote_refs; ref; ref = ref->next)
ref->status = REF_STATUS_NONE;
+ if (args->stateless_rpc)
+ close(out);
+ if (git_connection_is_socket(conn))
+ shutdown(fd[0], SHUT_WR);
if (use_sideband)
finish_async(&demux);
return -1;
@@ -487,8 +494,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
memset(&extra_have, 0, sizeof(extra_have));
- get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
- &extra_have);
+ get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
transport_verify_remote_names(nr_refspecs, refspecs);
@@ -502,7 +508,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
flags |= MATCH_REFS_MIRROR;
/* match them up */
- if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
+ if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
return -1;
set_ref_status_for_push(remote_refs, args.send_mirror,
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index f5efc67c9c..37f3193a33 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -29,9 +29,6 @@ static int compare_by_number(const void *a1, const void *a2)
return -1;
}
-const char *format_subject(struct strbuf *sb, const char *msg,
- const char *line_separator);
-
static void insert_one_record(struct shortlog *log,
const char *author,
const char *oneline)
@@ -141,9 +138,8 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
const char *author = NULL, *buffer;
struct strbuf buf = STRBUF_INIT;
struct strbuf ufbuf = STRBUF_INIT;
- struct pretty_print_context ctx = {0};
- pretty_print_commit(CMIT_FMT_RAW, commit, &buf, &ctx);
+ pp_commit_easy(CMIT_FMT_RAW, commit, &buf);
buffer = buf.buf;
while (*buffer && *buffer != '\n') {
const char *eol = strchr(buffer, '\n');
@@ -162,11 +158,12 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
sha1_to_hex(commit->object.sha1));
if (log->user_format) {
struct pretty_print_context ctx = {0};
+ ctx.fmt = CMIT_FMT_USERFORMAT;
ctx.abbrev = log->abbrev;
ctx.subject = "";
ctx.after_subject = "";
ctx.date_mode = DATE_NORMAL;
- pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &ufbuf, &ctx);
+ pretty_print_commit(&ctx, commit, &ufbuf);
buffer = ufbuf.buf;
} else if (*buffer) {
buffer++;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index da695815e2..4b480d7c7c 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -12,16 +12,6 @@ static const char* show_branch_usage[] = {
};
static int showbranch_use_color = -1;
-static char column_colors[][COLOR_MAXLEN] = {
- GIT_COLOR_RED,
- GIT_COLOR_GREEN,
- GIT_COLOR_YELLOW,
- GIT_COLOR_BLUE,
- GIT_COLOR_MAGENTA,
- GIT_COLOR_CYAN,
-};
-
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
static int default_num;
static int default_alloc;
@@ -36,14 +26,14 @@ static const char **default_arg;
static const char *get_color_code(int idx)
{
- if (showbranch_use_color)
- return column_colors[idx];
+ if (want_color(showbranch_use_color))
+ return column_colors_ansi[idx % column_colors_ansi_max];
return "";
}
static const char *get_color_reset_code(void)
{
- if (showbranch_use_color)
+ if (want_color(showbranch_use_color))
return GIT_COLOR_RESET;
return "";
}
@@ -293,8 +283,7 @@ static void show_one_commit(struct commit *commit, int no_name)
struct commit_name *name = commit->util;
if (commit->object.parsed) {
- struct pretty_print_context ctx = {0};
- pretty_print_commit(CMIT_FMT_ONELINE, commit, &pretty, &ctx);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
pretty_str = pretty.buf;
}
if (!prefixcmp(pretty_str, "[PATCH] "))
@@ -584,7 +573,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "color.showbranch")) {
- showbranch_use_color = git_config_colorbool(var, value, -1);
+ showbranch_use_color = git_config_colorbool(var, value);
return 0;
}
@@ -696,9 +685,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
git_config(git_show_branch_config, NULL);
- if (showbranch_use_color == -1)
- showbranch_use_color = git_use_color_default;
-
/* If nothing is specified, try the default first */
if (ac == 1 && default_num) {
ac = default_num;
@@ -892,7 +878,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
for (j = 0; j < i; j++)
putchar(' ');
printf("%s%c%s [%s] ",
- get_color_code(i % COLUMN_COLORS_MAX),
+ get_color_code(i),
is_head ? '*' : '!',
get_color_reset_code(), ref_name[i]);
}
@@ -954,7 +940,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
else
mark = '+';
printf("%s%c%s",
- get_color_code(i % COLUMN_COLORS_MAX),
+ get_color_code(i),
mark, get_color_reset_code());
}
putchar(' ');
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 45f0340c3e..fafb6dd500 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -145,7 +145,7 @@ static int exclude_existing(const char *match)
if (strncmp(ref, match, matchlen))
continue;
}
- if (check_ref_format(ref)) {
+ if (check_refname_format(ref, 0)) {
warning("ref '%s' ignored", ref);
continue;
}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 4d3b93fedb..f16986c0ae 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -22,8 +22,6 @@ static size_t cleanup(char *line, size_t len)
* Remove empty lines from the beginning and end
* and also trailing spaces from every line.
*
- * Note that the buffer will not be NUL-terminated.
- *
* Turn multiple consecutive empty lines between paragraphs
* into just one empty line.
*
@@ -77,7 +75,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
!strcmp(argv[1], "--strip-comments")))
strip_comments = 1;
else if (argc > 1)
- usage("git stripspace [-s | --strip-comments] < <stream>");
+ usage("git stripspace [-s | --strip-comments] < input");
if (strbuf_read(&buf, 0, 1024) < 0)
die_errno("could not read the input");
diff --git a/builtin/tag.c b/builtin/tag.c
index b66b34a182..9b6fd95494 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -12,11 +12,13 @@
#include "tag.h"
#include "run-command.h"
#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
static const char * const git_tag_usage[] = {
"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
"git tag -d <tagname>...",
- "git tag -l [-n[<num>]] [<pattern>]",
+ "git tag -l [-n[<num>]] [<pattern>...]",
"git tag -v <tagname>...",
NULL
};
@@ -24,17 +26,70 @@ static const char * const git_tag_usage[] = {
static char signingkey[1000];
struct tag_filter {
- const char *pattern;
+ const char **patterns;
int lines;
struct commit_list *with_commit;
};
+static int match_pattern(const char **patterns, const char *ref)
+{
+ /* no pattern means match everything */
+ if (!*patterns)
+ return 1;
+ for (; *patterns; patterns++)
+ if (!fnmatch(*patterns, ref, 0))
+ return 1;
+ return 0;
+}
+
+static int in_commit_list(const struct commit_list *want, struct commit *c)
+{
+ for (; want; want = want->next)
+ if (!hashcmp(want->item->object.sha1, c->object.sha1))
+ return 1;
+ return 0;
+}
+
+static int contains_recurse(struct commit *candidate,
+ const struct commit_list *want)
+{
+ struct commit_list *p;
+
+ /* was it previously marked as containing a want commit? */
+ if (candidate->object.flags & TMP_MARK)
+ return 1;
+ /* or marked as not possibly containing a want commit? */
+ if (candidate->object.flags & UNINTERESTING)
+ return 0;
+ /* or are we it? */
+ if (in_commit_list(want, candidate))
+ return 1;
+
+ if (parse_commit(candidate) < 0)
+ return 0;
+
+ /* Otherwise recurse and mark ourselves for future traversals. */
+ for (p = candidate->parents; p; p = p->next) {
+ if (contains_recurse(p->item, want)) {
+ candidate->object.flags |= TMP_MARK;
+ return 1;
+ }
+ }
+ candidate->object.flags |= UNINTERESTING;
+ return 0;
+}
+
+static int contains(struct commit *candidate, const struct commit_list *want)
+{
+ return contains_recurse(candidate, want);
+}
+
static int show_reference(const char *refname, const unsigned char *sha1,
int flag, void *cb_data)
{
struct tag_filter *filter = cb_data;
- if (!fnmatch(filter->pattern, refname, 0)) {
+ if (match_pattern(filter->patterns, refname)) {
int i;
unsigned long size;
enum object_type type;
@@ -47,7 +102,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,
commit = lookup_commit_reference_gently(sha1, 1);
if (!commit)
return 0;
- if (!is_descendant_of(commit, filter->with_commit))
+ if (!contains(commit, filter->with_commit))
return 0;
}
@@ -88,15 +143,12 @@ static int show_reference(const char *refname, const unsigned char *sha1,
return 0;
}
-static int list_tags(const char *pattern, int lines,
+static int list_tags(const char **patterns, int lines,
struct commit_list *with_commit)
{
struct tag_filter filter;
- if (pattern == NULL)
- pattern = "*";
-
- filter.pattern = pattern;
+ filter.patterns = patterns;
filter.lines = lines;
filter.with_commit = with_commit;
@@ -352,11 +404,22 @@ static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
+{
+ if (name[0] == '-')
+ return -1;
+
+ strbuf_reset(sb);
+ strbuf_addf(sb, "refs/tags/%s", name);
+
+ return check_refname_format(sb->buf, 0);
+}
+
int cmd_tag(int argc, const char **argv, const char *prefix)
{
struct strbuf buf = STRBUF_INIT;
+ struct strbuf ref = STRBUF_INIT;
unsigned char object[20], prev[20];
- char ref[PATH_MAX];
const char *object_ref, *tag;
struct ref_lock *lock;
@@ -366,21 +429,21 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
struct option options[] = {
- OPT_BOOLEAN('l', NULL, &list, "list tag names"),
+ OPT_BOOLEAN('l', "list", &list, "list tag names"),
{ OPTION_INTEGER, 'n', NULL, &lines, "n",
"print <n> lines of each tag message",
PARSE_OPT_OPTARG, NULL, 1 },
- OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
- OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
+ OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
+ OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
OPT_GROUP("Tag creation options"),
- OPT_BOOLEAN('a', NULL, &annotate,
+ OPT_BOOLEAN('a', "annotate", &annotate,
"annotated tag, needs a message"),
- OPT_CALLBACK('m', NULL, &msg, "message",
+ OPT_CALLBACK('m', "message", &msg, "message",
"tag message", parse_msg_arg),
- OPT_FILENAME('F', NULL, &msgfile, "read message from file"),
- OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
- OPT_STRING('u', NULL, &keyid, "key-id",
+ OPT_FILENAME('F', "file", &msgfile, "read message from file"),
+ OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
+ OPT_STRING('u', "local-user", &keyid, "key-id",
"use another key to sign the tag"),
OPT__FORCE(&force, "replace the tag if exists"),
@@ -414,7 +477,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (list + delete + verify > 1)
usage_with_options(git_tag_usage, options);
if (list)
- return list_tags(argv[0], lines == -1 ? 0 : lines,
+ return list_tags(argv, lines == -1 ? 0 : lines,
with_commit);
if (lines != -1)
die(_("-n option is only allowed with -l."));
@@ -452,12 +515,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (get_sha1(object_ref, object))
die(_("Failed to resolve '%s' as a valid ref."), object_ref);
- if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1)
- die(_("tag name too long: %.*s..."), 50, tag);
- if (check_ref_format(ref))
+ if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (!resolve_ref(ref, prev, 1, NULL))
+ if (!resolve_ref(ref.buf, prev, 1, NULL))
hashclr(prev);
else if (!force)
die(_("tag '%s' already exists"), tag);
@@ -466,14 +527,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
create_tag(object, tag, &buf, msg.given || msgfile,
sign, prev, object);
- lock = lock_any_ref_for_update(ref, prev, 0);
+ lock = lock_any_ref_for_update(ref.buf, prev, 0);
if (!lock)
- die(_("%s: cannot lock the ref"), ref);
+ die(_("%s: cannot lock the ref"), ref.buf);
if (write_ref_sha1(lock, object, NULL) < 0)
- die(_("%s: cannot update the ref"), ref);
+ die(_("%s: cannot update the ref"), ref.buf);
if (force && hashcmp(prev, object))
printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
strbuf_release(&buf);
+ strbuf_release(&ref);
return 0;
}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index f63973c914..14e04e6795 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -90,7 +90,7 @@ static void use(int bytes)
static void *get_data(unsigned long size)
{
- z_stream stream;
+ git_zstream stream;
void *buf = xmalloc(size);
memset(&stream, 0, sizeof(stream));
diff --git a/builtin/update-index.c b/builtin/update-index.c
index d7850c6309..a6a23fa1f3 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -99,8 +99,11 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru
fill_stat_cache_info(ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(ce->sha1, path, st, !info_only))
+ if (index_path(ce->sha1, path, st,
+ info_only ? 0 : HASH_WRITE_OBJECT)) {
+ free(ce);
return -1;
+ }
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
if (add_cache_entry(ce, option))
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 76ba1d5881..835c62ab15 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -11,7 +11,7 @@ static const char * const git_update_ref_usage[] = {
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname, *oldval, *msg=NULL;
+ const char *refname, *oldval, *msg = NULL;
unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0, flags = 0;
struct option options[] = {
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 73f788ef24..2d0b38333e 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -64,7 +64,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix)
sent_argv[sent_argc] = NULL;
/* parse all options sent by the client */
- return write_archive(sent_argc, sent_argv, prefix, 0);
+ return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
}
__attribute__((format (printf, 1, 2)))
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index b6079ae6cb..e841b4a38d 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -1,134 +1,53 @@
#include "builtin.h"
#include "cache.h"
-#include "pack.h"
-#include "pack-revindex.h"
+#include "run-command.h"
#include "parse-options.h"
-#define MAX_CHAIN 50
-
#define VERIFY_PACK_VERBOSE 01
#define VERIFY_PACK_STAT_ONLY 02
-static void show_pack_info(struct packed_git *p, unsigned int flags)
-{
- uint32_t nr_objects, i;
- int cnt;
- int stat_only = flags & VERIFY_PACK_STAT_ONLY;
- unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;
-
- nr_objects = p->num_objects;
- memset(chain_histogram, 0, sizeof(chain_histogram));
- baseobjects = 0;
-
- for (i = 0; i < nr_objects; i++) {
- const unsigned char *sha1;
- unsigned char base_sha1[20];
- const char *type;
- unsigned long size;
- unsigned long store_size;
- off_t offset;
- unsigned int delta_chain_length;
-
- sha1 = nth_packed_object_sha1(p, i);
- if (!sha1)
- die("internal error pack-check nth-packed-object");
- offset = nth_packed_object_offset(p, i);
- type = packed_object_info_detail(p, offset, &size, &store_size,
- &delta_chain_length,
- base_sha1);
- if (!stat_only)
- printf("%s ", sha1_to_hex(sha1));
- if (!delta_chain_length) {
- if (!stat_only)
- printf("%-6s %lu %lu %"PRIuMAX"\n",
- type, size, store_size, (uintmax_t)offset);
- baseobjects++;
- }
- else {
- if (!stat_only)
- printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
- type, size, store_size, (uintmax_t)offset,
- delta_chain_length, sha1_to_hex(base_sha1));
- if (delta_chain_length <= MAX_CHAIN)
- chain_histogram[delta_chain_length]++;
- else
- chain_histogram[0]++;
- }
- }
-
- if (baseobjects)
- printf("non delta: %lu object%s\n",
- baseobjects, baseobjects > 1 ? "s" : "");
-
- for (cnt = 1; cnt <= MAX_CHAIN; cnt++) {
- if (!chain_histogram[cnt])
- continue;
- printf("chain length = %d: %lu object%s\n", cnt,
- chain_histogram[cnt],
- chain_histogram[cnt] > 1 ? "s" : "");
- }
- if (chain_histogram[0])
- printf("chain length > %d: %lu object%s\n", MAX_CHAIN,
- chain_histogram[0],
- chain_histogram[0] > 1 ? "s" : "");
-}
-
static int verify_one_pack(const char *path, unsigned int flags)
{
- char arg[PATH_MAX];
- int len;
+ struct child_process index_pack;
+ const char *argv[] = {"index-pack", NULL, NULL, NULL };
+ struct strbuf arg = STRBUF_INIT;
int verbose = flags & VERIFY_PACK_VERBOSE;
int stat_only = flags & VERIFY_PACK_STAT_ONLY;
- struct packed_git *pack;
int err;
- len = strlcpy(arg, path, PATH_MAX);
- if (len >= PATH_MAX)
- return error("name too long: %s", path);
-
- /*
- * In addition to "foo.idx" we accept "foo.pack" and "foo";
- * normalize these forms to "foo.idx" for add_packed_git().
- */
- if (has_extension(arg, ".pack")) {
- strcpy(arg + len - 5, ".idx");
- len--;
- } else if (!has_extension(arg, ".idx")) {
- if (len + 4 >= PATH_MAX)
- return error("name too long: %s.idx", arg);
- strcpy(arg + len, ".idx");
- len += 4;
- }
+ if (stat_only)
+ argv[1] = "--verify-stat-only";
+ else if (verbose)
+ argv[1] = "--verify-stat";
+ else
+ argv[1] = "--verify";
/*
- * add_packed_git() uses our buffer (containing "foo.idx") to
- * build the pack filename ("foo.pack"). Make sure it fits.
+ * In addition to "foo.pack" we accept "foo.idx" and "foo";
+ * normalize these forms to "foo.pack" for "index-pack --verify".
*/
- if (len + 1 >= PATH_MAX) {
- arg[len - 4] = '\0';
- return error("name too long: %s.pack", arg);
- }
-
- pack = add_packed_git(arg, len, 1);
- if (!pack)
- return error("packfile %s not found.", arg);
+ strbuf_addstr(&arg, path);
+ if (has_extension(arg.buf, ".idx"))
+ strbuf_splice(&arg, arg.len - 3, 3, "pack", 4);
+ else if (!has_extension(arg.buf, ".pack"))
+ strbuf_add(&arg, ".pack", 5);
+ argv[2] = arg.buf;
- install_packed_git(pack);
+ memset(&index_pack, 0, sizeof(index_pack));
+ index_pack.argv = argv;
+ index_pack.git_cmd = 1;
- if (!stat_only)
- err = verify_pack(pack);
- else
- err = open_pack_index(pack);
+ err = run_command(&index_pack);
if (verbose || stat_only) {
if (err)
- printf("%s: bad\n", pack->pack_name);
+ printf("%s: bad\n", arg.buf);
else {
- show_pack_info(pack, flags);
if (!stat_only)
- printf("%s: ok\n", pack->pack_name);
+ printf("%s: ok\n", arg.buf);
}
}
+ strbuf_release(&arg);
return err;
}
@@ -159,7 +78,6 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
for (i = 0; i < argc; i++) {
if (verify_one_pack(argv[i], flags))
err = 1;
- discard_revindex();
}
return err;