diff options
Diffstat (limited to 'builtin')
-rw-r--r-- | builtin/add.c | 4 | ||||
-rw-r--r-- | builtin/apply.c | 85 | ||||
-rw-r--r-- | builtin/blame.c | 95 | ||||
-rw-r--r-- | builtin/branch.c | 32 | ||||
-rw-r--r-- | builtin/cat-file.c | 42 | ||||
-rw-r--r-- | builtin/checkout.c | 43 | ||||
-rw-r--r-- | builtin/clone.c | 16 | ||||
-rw-r--r-- | builtin/commit-tree.c | 70 | ||||
-rw-r--r-- | builtin/commit.c | 35 | ||||
-rw-r--r-- | builtin/config.c | 6 | ||||
-rw-r--r-- | builtin/fetch.c | 6 | ||||
-rw-r--r-- | builtin/for-each-ref.c | 3 | ||||
-rw-r--r-- | builtin/grep.c | 8 | ||||
-rw-r--r-- | builtin/help.c | 2 | ||||
-rw-r--r-- | builtin/log.c | 64 | ||||
-rw-r--r-- | builtin/ls-remote.c | 19 | ||||
-rw-r--r-- | builtin/mailinfo.c | 4 | ||||
-rw-r--r-- | builtin/merge.c | 81 | ||||
-rw-r--r-- | builtin/notes.c | 13 | ||||
-rw-r--r-- | builtin/pack-objects.c | 6 | ||||
-rw-r--r-- | builtin/patch-id.c | 107 | ||||
-rw-r--r-- | builtin/receive-pack.c | 130 | ||||
-rw-r--r-- | builtin/reflog.c | 159 | ||||
-rw-r--r-- | builtin/remote.c | 134 | ||||
-rw-r--r-- | builtin/rev-parse.c | 3 | ||||
-rw-r--r-- | builtin/revert.c | 291 |
26 files changed, 998 insertions, 460 deletions
diff --git a/builtin/add.c b/builtin/add.c index 87d2980313..17149cfeed 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -261,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix) { char *file = xstrdup(git_path("ADD_EDIT.patch")); const char *apply_argv[] = { "apply", "--recount", "--cached", - file, NULL }; + NULL, NULL }; struct child_process child; struct rev_info rev; int out; struct stat st; + apply_argv[3] = file; + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ if (read_cache() < 0) diff --git a/builtin/apply.c b/builtin/apply.c index 59bbcdb132..562e5345fc 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -56,7 +56,7 @@ static enum ws_error_action { nowarn_ws_error, warn_on_ws_error, die_on_ws_error, - correct_ws_error, + correct_ws_error } ws_error_action = warn_on_ws_error; static int whitespace_error; static int squelch_whitespace_errors = 5; @@ -64,7 +64,7 @@ static int applied_after_fixing_ws; static enum ws_ignore { ignore_ws_none, - ignore_ws_change, + ignore_ws_change } ws_ignore_action = ignore_ws_none; @@ -1854,6 +1854,8 @@ static int match_fragment(struct image *img, { int i; char *fixed_buf, *buf, *orig, *target; + struct strbuf fixed; + size_t fixed_len; int preimage_limit; if (preimage->nr + try_lno <= img->nr) { @@ -1977,12 +1979,12 @@ static int match_fragment(struct image *img, * use the whitespace from the preimage. */ extra_chars = preimage_end - preimage_eof; - fixed_buf = xmalloc(imgoff + extra_chars); - memcpy(fixed_buf, img->buf + try, imgoff); - memcpy(fixed_buf + imgoff, preimage_eof, extra_chars); - imgoff += extra_chars; + strbuf_init(&fixed, imgoff + extra_chars); + strbuf_add(&fixed, img->buf + try, imgoff); + strbuf_add(&fixed, preimage_eof, extra_chars); + fixed_buf = strbuf_detach(&fixed, &fixed_len); update_pre_post_images(preimage, postimage, - fixed_buf, imgoff, postlen); + fixed_buf, fixed_len, postlen); return 1; } @@ -1999,27 +2001,22 @@ static int match_fragment(struct image *img, * but in this loop we will only handle the part of the * preimage that falls within the file. */ - fixed_buf = xmalloc(preimage->len + 1); - buf = fixed_buf; + strbuf_init(&fixed, preimage->len + 1); orig = preimage->buf; target = img->buf + try; for (i = 0; i < preimage_limit; i++) { - size_t fixlen; /* length after fixing the preimage */ size_t oldlen = preimage->line[i].len; size_t tgtlen = img->line[try_lno + i].len; - size_t tgtfixlen; /* length after fixing the target line */ - char tgtfixbuf[1024], *tgtfix; + size_t fixstart = fixed.len; + struct strbuf tgtfix; int match; /* Try fixing the line in the preimage */ - fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL); + ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); /* Try fixing the line in the target */ - if (sizeof(tgtfixbuf) > tgtlen) - tgtfix = tgtfixbuf; - else - tgtfix = xmalloc(tgtlen); - tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL); + strbuf_init(&tgtfix, tgtlen); + ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL); /* * If they match, either the preimage was based on @@ -2031,15 +2028,15 @@ static int match_fragment(struct image *img, * so we might as well take the fix together with their * real change. */ - match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen)); + match = (tgtfix.len == fixed.len - fixstart && + !memcmp(tgtfix.buf, fixed.buf + fixstart, + fixed.len - fixstart)); - if (tgtfix != tgtfixbuf) - free(tgtfix); + strbuf_release(&tgtfix); if (!match) goto unmatch_exit; orig += oldlen; - buf += fixlen; target += tgtlen; } @@ -2051,19 +2048,18 @@ static int match_fragment(struct image *img, * false). */ for ( ; i < preimage->nr; i++) { - size_t fixlen; /* length after fixing the preimage */ + size_t fixstart = fixed.len; /* start of the fixed preimage */ size_t oldlen = preimage->line[i].len; int j; /* Try fixing the line in the preimage */ - fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL); + ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); - for (j = 0; j < fixlen; j++) - if (!isspace(buf[j])) + for (j = fixstart; j < fixed.len; j++) + if (!isspace(fixed.buf[j])) goto unmatch_exit; orig += oldlen; - buf += fixlen; } /* @@ -2071,12 +2067,13 @@ static int match_fragment(struct image *img, * has whitespace breakages unfixed, and fixing them makes the * hunk match. Update the context lines in the postimage. */ + fixed_buf = strbuf_detach(&fixed, &fixed_len); update_pre_post_images(preimage, postimage, - fixed_buf, buf - fixed_buf, 0); + fixed_buf, fixed_len, 0); return 1; unmatch_exit: - free(fixed_buf); + strbuf_release(&fixed); return 0; } @@ -2244,7 +2241,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, int match_beginning, match_end; const char *patch = frag->patch; int size = frag->size; - char *old, *new, *oldlines, *newlines; + char *old, *oldlines; + struct strbuf newlines; int new_blank_lines_at_end = 0; unsigned long leading, trailing; int pos, applied_pos; @@ -2254,16 +2252,16 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, memset(&preimage, 0, sizeof(preimage)); memset(&postimage, 0, sizeof(postimage)); oldlines = xmalloc(size); - newlines = xmalloc(size); + strbuf_init(&newlines, size); old = oldlines; - new = newlines; while (size > 0) { char first; int len = linelen(patch, size); - int plen, added; + int plen; int added_blank_line = 0; int is_blank_context = 0; + size_t start; if (!len) break; @@ -2293,7 +2291,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, /* ... followed by '\No newline'; nothing */ break; *old++ = '\n'; - *new++ = '\n'; + strbuf_addch(&newlines, '\n'); add_line_info(&preimage, "\n", 1, LINE_COMMON); add_line_info(&postimage, "\n", 1, LINE_COMMON); is_blank_context = 1; @@ -2315,18 +2313,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, if (first == '+' && no_add) break; + start = newlines.len; if (first != '+' || !whitespace_error || ws_error_action != correct_ws_error) { - memcpy(new, patch + 1, plen); - added = plen; + strbuf_add(&newlines, patch + 1, plen); } else { - added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws); + ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws); } - add_line_info(&postimage, new, added, + add_line_info(&postimage, newlines.buf + start, newlines.len - start, (first == '+' ? 0 : LINE_COMMON)); - new += added; if (first == '+' && (ws_rule & WS_BLANK_AT_EOF) && ws_blank_line(patch + 1, plen, ws_rule)) @@ -2351,9 +2348,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, } if (inaccurate_eof && old > oldlines && old[-1] == '\n' && - new > newlines && new[-1] == '\n') { + newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') { old--; - new--; + strbuf_setlen(&newlines, newlines.len - 1); } leading = frag->leading; @@ -2385,8 +2382,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, pos = frag->newpos ? (frag->newpos - 1) : 0; preimage.buf = oldlines; preimage.len = old - oldlines; - postimage.buf = newlines; - postimage.len = new - newlines; + postimage.buf = newlines.buf; + postimage.len = newlines.len; preimage.line = preimage.line_allocated; postimage.line = postimage.line_allocated; @@ -2462,7 +2459,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, } free(oldlines); - free(newlines); + strbuf_release(&newlines); free(preimage.line_allocated); free(postimage.line_allocated); diff --git a/builtin/blame.c b/builtin/blame.c index 8506286dd2..01e62fdeb0 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -20,6 +20,7 @@ #include "mailmap.h" #include "parse-options.h" #include "utf8.h" +#include "userdiff.h" static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file"; @@ -86,16 +87,50 @@ struct origin { }; /* + * Prepare diff_filespec and convert it using diff textconv API + * if the textconv driver exists. + * Return 1 if the conversion succeeds, 0 otherwise. + */ +int textconv_object(const char *path, + const unsigned char *sha1, + char **buf, + unsigned long *buf_size) +{ + struct diff_filespec *df; + struct userdiff_driver *textconv; + + df = alloc_filespec(path); + fill_filespec(df, sha1, S_IFREG | 0664); + textconv = get_textconv(df); + if (!textconv) { + free_filespec(df); + return 0; + } + + *buf_size = fill_textconv(textconv, df, buf); + free_filespec(df); + return 1; +} + +/* * Given an origin, prepare mmfile_t structure to be used by the * diff machinery */ -static void fill_origin_blob(struct origin *o, mmfile_t *file) +static void fill_origin_blob(struct diff_options *opt, + struct origin *o, mmfile_t *file) { if (!o->file.ptr) { enum object_type type; + unsigned long file_size; + num_read_blob++; - file->ptr = read_sha1_file(o->blob_sha1, &type, - (unsigned long *)(&(file->size))); + if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && + textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size)) + ; + else + file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size); + file->size = file_size; + if (!file->ptr) die("Cannot read blob %s for path %s", sha1_to_hex(o->blob_sha1), @@ -282,7 +317,6 @@ static struct origin *get_origin(struct scoreboard *sb, static int fill_blob_sha1(struct origin *origin) { unsigned mode; - if (!is_null_sha1(origin->blob_sha1)) return 0; if (get_tree_entry(origin->commit->object.sha1, @@ -733,16 +767,17 @@ static int pass_blame_to_parent(struct scoreboard *sb, { int last_in_target; mmfile_t file_p, file_o; - struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 }; + struct blame_chunk_cb_data d; xpparam_t xpp; xdemitconf_t xecfg; - + memset(&d, 0, sizeof(d)); + d.sb = sb; d.target = target; d.parent = parent; last_in_target = find_last_in_target(sb, target); if (last_in_target < 0) return 1; /* nothing remains for this target */ - fill_origin_blob(parent, &file_p); - fill_origin_blob(target, &file_o); + fill_origin_blob(&sb->revs->diffopt, parent, &file_p); + fill_origin_blob(&sb->revs->diffopt, target, &file_o); num_get_patch++; memset(&xpp, 0, sizeof(xpp)); @@ -875,10 +910,11 @@ static void find_copy_in_blob(struct scoreboard *sb, const char *cp; int cnt; mmfile_t file_o; - struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 }; + struct handle_split_cb_data d; xpparam_t xpp; xdemitconf_t xecfg; - + memset(&d, 0, sizeof(d)); + d.sb = sb; d.ent = ent; d.parent = parent; d.split = split; /* * Prepare mmfile that contains only the lines in ent. */ @@ -922,7 +958,7 @@ static int find_move_in_parent(struct scoreboard *sb, if (last_in_target < 0) return 1; /* nothing remains for this target */ - fill_origin_blob(parent, &file_p); + fill_origin_blob(&sb->revs->diffopt, parent, &file_p); if (!file_p.ptr) return 0; @@ -1063,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb, norigin = get_origin(sb, parent, p->one->path); hashcpy(norigin->blob_sha1, p->one->sha1); - fill_origin_blob(norigin, &file_p); + fill_origin_blob(&sb->revs->diffopt, norigin, &file_p); if (!file_p.ptr) continue; @@ -1983,6 +2019,16 @@ static int git_blame_config(const char *var, const char *value, void *cb) blame_date_mode = parse_date_format(value); return 0; } + + switch (userdiff_config(var, value)) { + case 0: + break; + case -1: + return -1; + default: + return 0; + } + return git_default_config(var, value, cb); } @@ -1990,7 +2036,9 @@ static int git_blame_config(const char *var, const char *value, void *cb) * Prepare a dummy commit that represents the work tree (or staged) item. * Note that annotating work tree item never works in the reverse. */ -static struct commit *fake_working_tree_commit(const char *path, const char *contents_from) +static struct commit *fake_working_tree_commit(struct diff_options *opt, + const char *path, + const char *contents_from) { struct commit *commit; struct origin *origin; @@ -2018,6 +2066,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con if (!contents_from || strcmp("-", contents_from)) { struct stat st; const char *read_from; + unsigned long buf_len; if (contents_from) { if (stat(contents_from, &st) < 0) @@ -2030,9 +2079,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con read_from = path; } mode = canon_mode(st.st_mode); + switch (st.st_mode & S_IFMT) { case S_IFREG: - if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) + if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && + textconv_object(read_from, null_sha1, &buf.buf, &buf_len)) + buf.len = buf_len; + else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) die_errno("cannot open or read '%s'", read_from); break; case S_IFLNK: @@ -2248,6 +2301,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) git_config(git_blame_config, NULL); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; + DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); save_commit_buffer = 0; dashdash_pos = 0; @@ -2384,7 +2438,8 @@ parse_done: * or "--contents". */ setup_work_tree(); - sb.final = fake_working_tree_commit(path, contents_from); + sb.final = fake_working_tree_commit(&sb.revs->diffopt, + path, contents_from); add_pending_object(&revs, &(sb.final->object), ":"); } else if (contents_from) @@ -2411,8 +2466,14 @@ parse_done: if (fill_blob_sha1(o)) die("no such path %s in %s", path, final_commit_name); - sb.final_buf = read_sha1_file(o->blob_sha1, &type, - &sb.final_buf_size); + if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) && + textconv_object(path, o->blob_sha1, (char **) &sb.final_buf, + &sb.final_buf_size)) + ; + else + sb.final_buf = read_sha1_file(o->blob_sha1, &type, + &sb.final_buf_size); + if (!sb.final_buf) die("Cannot read blob %s for path %s", sha1_to_hex(o->blob_sha1), diff --git a/builtin/branch.c b/builtin/branch.c index 6cf7e721e6..87976f0921 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -43,13 +43,13 @@ enum color_branch { BRANCH_COLOR_PLAIN = 1, BRANCH_COLOR_REMOTE = 2, BRANCH_COLOR_LOCAL = 3, - BRANCH_COLOR_CURRENT = 4, + BRANCH_COLOR_CURRENT = 4 }; static enum merge_filter { NO_FILTER = 0, SHOW_NOT_MERGED, - SHOW_MERGED, + SHOW_MERGED } merge_filter; static unsigned char merge_filter_ref[20]; @@ -257,9 +257,15 @@ static char *resolve_symref(const char *src, const char *prefix) return xstrdup(dst); } +struct append_ref_cb { + struct ref_list *ref_list; + int ret; +}; + static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { - struct ref_list *ref_list = (struct ref_list*)(cb_data); + struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data); + struct ref_list *ref_list = cb->ref_list; struct ref_item *newitem; struct commit *commit; int kind, i; @@ -293,8 +299,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, commit = NULL; if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) { commit = lookup_commit_reference_gently(sha1, 1); - if (!commit) - return error("branch '%s' does not point at a commit", refname); + if (!commit) { + cb->ret = error("branch '%s' does not point at a commit", refname); + return 0; + } /* Filter with with_commit if specified */ if (!is_descendant_of(commit, ref_list->with_commit)) @@ -484,9 +492,10 @@ static void show_detached(struct ref_list *ref_list) } } -static void 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) { int i; + struct append_ref_cb cb; struct ref_list ref_list; memset(&ref_list, 0, sizeof(ref_list)); @@ -496,7 +505,9 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str ref_list.with_commit = with_commit; if (merge_filter != NO_FILTER) init_revisions(&ref_list.revs, NULL); - for_each_rawref(append_ref, &ref_list); + cb.ref_list = &ref_list; + cb.ret = 0; + for_each_rawref(append_ref, &cb); if (merge_filter != NO_FILTER) { struct commit *filter; filter = lookup_commit_reference_gently(merge_filter_ref, 0); @@ -527,6 +538,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str } free_ref_list(&ref_list); + + if (cb.ret) + error("some refs could not be read"); + + return cb.ret; } static void rename_branch(const char *oldname, const char *newname, int force) @@ -679,7 +695,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (delete) return delete_branches(argc, argv, delete > 1, kinds); else if (argc == 0) - print_ref_list(kinds, detached, verbose, abbrev, with_commit); + 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)) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index a933eaa043..76ec3fec92 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -9,6 +9,8 @@ #include "tree.h" #include "builtin.h" #include "parse-options.h" +#include "diff.h" +#include "userdiff.h" #define BATCH 1 #define BATCH_CHECK 2 @@ -84,10 +86,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) { unsigned char sha1[20]; enum object_type type; - void *buf; + char *buf; unsigned long size; + struct object_context obj_context; - if (get_sha1(obj_name, sha1)) + if (get_sha1_with_context(obj_name, sha1, &obj_context)) die("Not a valid object name %s", obj_name); buf = NULL; @@ -118,7 +121,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) /* custom pretty-print here */ if (type == OBJ_TREE) { - const char *ls_args[3] = {"ls-tree", obj_name, NULL}; + const char *ls_args[3] = { NULL }; + ls_args[0] = "ls-tree"; + ls_args[1] = obj_name; return cmd_ls_tree(2, ls_args, NULL); } @@ -132,6 +137,17 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) /* otherwise just spit out the data */ break; + + case 'c': + if (!obj_context.path[0]) + die("git cat-file --textconv %s: <object> must be <sha1:path>", + obj_name); + + if (!textconv_object(obj_context.path, sha1, &buf, &size)) + die("git cat-file --textconv: unable to run textconv on %s", + obj_name); + break; + case 0: buf = read_object_with_reference(sha1, exp_type, &size, NULL); break; @@ -201,11 +217,25 @@ static int batch_objects(int print_contents) } static const char * const cat_file_usage[] = { - "git cat-file (-t|-s|-e|-p|<type>) <object>", + "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>", "git cat-file (--batch|--batch-check) < <list_of_objects>", NULL }; +static int git_cat_file_config(const char *var, const char *value, void *cb) +{ + switch (userdiff_config(var, value)) { + case 0: + break; + case -1: + return -1; + default: + return 0; + } + + return git_default_config(var, value, cb); +} + int cmd_cat_file(int argc, const char **argv, const char *prefix) { int opt = 0, batch = 0; @@ -218,6 +248,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) OPT_SET_INT('e', NULL, &opt, "exit with zero when there's no error", 'e'), OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'), + OPT_SET_INT(0, "textconv", &opt, + "for blob objects, run textconv on object's content", 'c'), OPT_SET_INT(0, "batch", &batch, "show info and content of objects fed from the standard input", BATCH), @@ -227,7 +259,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_default_config, NULL); + git_config(git_cat_file_config, NULL); if (argc != 3 && argc != 2) usage_with_options(cat_file_usage, options); diff --git a/builtin/checkout.c b/builtin/checkout.c index 88b1f43e05..1994be92c6 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -33,6 +33,7 @@ struct checkout_opts { int writeout_error; const char *new_branch; + const char *new_orphan_branch; int new_branch_log; enum branch_track track; }; @@ -492,8 +493,26 @@ static void update_refs_for_switch(struct checkout_opts *opts, struct strbuf msg = STRBUF_INIT; const char *old_desc; if (opts->new_branch) { - create_branch(old->name, opts->new_branch, new->name, 0, - opts->new_branch_log, opts->track); + if (opts->new_orphan_branch) { + if (opts->new_branch_log && !log_all_ref_updates) { + int temp; + char log_file[PATH_MAX]; + char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); + + temp = log_all_ref_updates; + log_all_ref_updates = 1; + if (log_ref_setup(ref_name, log_file, sizeof(log_file))) { + fprintf(stderr, "Can not do reflog for '%s'\n", + opts->new_orphan_branch); + log_all_ref_updates = temp; + return; + } + log_all_ref_updates = temp; + } + } + else + create_branch(old->name, opts->new_branch, new->name, 0, + opts->new_branch_log, opts->track); new->name = opts->new_branch; setup_branch_path(new); } @@ -515,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts, opts->new_branch ? " a new" : "", new->name); } + if (old->path && old->name) { + char log_file[PATH_MAX], ref_file[PATH_MAX]; + + git_snpath(log_file, sizeof(log_file), "logs/%s", old->path); + git_snpath(ref_file, sizeof(ref_file), "%s", old->path); + if (!file_exists(ref_file) && file_exists(log_file)) + remove_path(log_file); + } } else if (strcmp(new->name, "HEAD")) { update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL, REF_NODEREF, DIE_ON_ERR); @@ -609,7 +636,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1, static const char *unique_tracking_name(const char *name) { - struct tracking_name_data cb_data = { name, NULL, 1 }; + struct tracking_name_data cb_data = { NULL, NULL, 1 }; + cb_data.name = name; for_each_ref(check_tracking_name, &cb_data); if (cb_data.unique) return cb_data.remote; @@ -633,6 +661,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), OPT_SET_INT('t', "track", &opts.track, "track", BRANCH_TRACK_EXPLICIT), + OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"), OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage", 2), OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage", @@ -678,6 +707,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.new_branch = argv0 + 1; } + if (opts.new_orphan_branch) { + if (opts.new_branch) + die("--orphan and -b are mutually exclusive"); + if (opts.track > 0) + die("--orphan cannot be used with -t"); + opts.new_branch = opts.new_orphan_branch; + } + if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); diff --git a/builtin/clone.c b/builtin/clone.c index 3a3625b2ad..efb1e6faa5 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -475,9 +475,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) */ unsetenv(CONFIG_ENVIRONMENT); - if (option_reference) - setup_reference(git_dir); - git_config(git_default_config, NULL); if (option_bare) { @@ -503,12 +500,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix) git_config_set(key.buf, "true"); strbuf_reset(&key); } - - strbuf_addf(&key, "remote.%s.url", option_origin); - git_config_set(key.buf, repo); - strbuf_reset(&key); } + strbuf_addf(&key, "remote.%s.url", option_origin); + git_config_set(key.buf, repo); + strbuf_reset(&key); + + if (option_reference) + setup_reference(git_dir); + fetch_pattern = value.buf; refspec = parse_fetch_refspec(1, &fetch_pattern); @@ -518,7 +518,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) refs = clone_local(path, git_dir); mapped_refs = wanted_peer_refs(refs, refspec); } else { - struct remote *remote = remote_get(argv[0]); + struct remote *remote = remote_get(option_origin); transport = transport_get(remote, remote->url[0]); if (!transport->get_refs_list || !transport->fetch) diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 90dac349a3..87f0591c2f 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -9,19 +9,6 @@ #include "builtin.h" #include "utf8.h" -/* - * FIXME! Share the code with "write-tree.c" - */ -static void check_valid(unsigned char *sha1, enum object_type expect) -{ - enum object_type type = sha1_object_info(sha1, NULL); - if (type < 0) - die("%s is not a valid object", sha1_to_hex(sha1)); - if (type != expect) - die("%s is not a valid '%s' object", sha1_to_hex(sha1), - typename(expect)); -} - static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog"; static void new_parent(struct commit *parent, struct commit_list **parents_p) @@ -38,61 +25,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p) commit_list_insert(parent, parents_p); } -static const char commit_utf8_warn[] = -"Warning: commit message does not conform to UTF-8.\n" -"You may want to amend it after fixing the message, or set the config\n" -"variable i18n.commitencoding to the encoding your project uses.\n"; - -int commit_tree(const char *msg, unsigned char *tree, - struct commit_list *parents, unsigned char *ret, - const char *author) -{ - int result; - int encoding_is_utf8; - struct strbuf buffer; - - check_valid(tree, OBJ_TREE); - - /* Not having i18n.commitencoding is the same as having utf-8 */ - encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); - - strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */ - strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree)); - - /* - * NOTE! This ordering means that the same exact tree merged with a - * different order of parents will be a _different_ changeset even - * if everything else stays the same. - */ - while (parents) { - struct commit_list *next = parents->next; - strbuf_addf(&buffer, "parent %s\n", - sha1_to_hex(parents->item->object.sha1)); - free(parents); - parents = next; - } - - /* Person/date information */ - if (!author) - author = git_author_info(IDENT_ERROR_ON_NO_NAME); - strbuf_addf(&buffer, "author %s\n", author); - strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); - if (!encoding_is_utf8) - strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); - strbuf_addch(&buffer, '\n'); - - /* And add the comment */ - strbuf_addstr(&buffer, msg); - - /* And check the encoding */ - if (encoding_is_utf8 && !is_utf8(buffer.buf)) - fprintf(stderr, commit_utf8_warn); - - result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret); - strbuf_release(&buffer); - return result; -} - int cmd_commit_tree(int argc, const char **argv, const char *prefix) { int i; @@ -117,7 +49,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) if (get_sha1(b, sha1)) die("Not a valid object name %s", b); - check_valid(sha1, OBJ_COMMIT); + assert_sha1_type(sha1, OBJ_COMMIT); new_parent(lookup_commit(sha1), &parents); } diff --git a/builtin/commit.c b/builtin/commit.c index 80c621dc06..3d99cf9158 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -62,7 +62,7 @@ static struct lock_file false_lock; /* used only for partial commits */ static enum { COMMIT_AS_IS = 1, COMMIT_NORMAL, - COMMIT_PARTIAL, + COMMIT_PARTIAL } commit_style; static const char *logfile, *force_author; @@ -71,7 +71,7 @@ static char *edit_message, *use_message; static char *author_name, *author_email, *author_date; static int all, edit_flag, also, interactive, only, amend, signoff; static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; -static int no_post_rewrite; +static int no_post_rewrite, allow_empty_message; static char *untracked_files_arg, *force_date; /* * The default commit message cleanup mode will remove the lines @@ -83,11 +83,12 @@ static char *untracked_files_arg, *force_date; static enum { CLEANUP_SPACE, CLEANUP_NONE, - CLEANUP_ALL, + CLEANUP_ALL } cleanup_mode; static char *cleanup_arg; static int use_editor = 1, initial_commit, in_merge, include_status = 1; +static int show_ignored_in_status; static const char *only_include_assumed; static struct strbuf message; @@ -95,8 +96,9 @@ static int null_termination; static enum { STATUS_FORMAT_LONG, STATUS_FORMAT_SHORT, - STATUS_FORMAT_PORCELAIN, + STATUS_FORMAT_PORCELAIN } status_format = STATUS_FORMAT_LONG; +static int status_show_branch; static int opt_parse_m(const struct option *opt, const char *arg, int unset) { @@ -138,6 +140,7 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"), OPT_SET_INT(0, "short", &status_format, "show status concisely", STATUS_FORMAT_SHORT), + OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"), OPT_SET_INT(0, "porcelain", &status_format, "show porcelain output format", STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &null_termination, @@ -145,9 +148,15 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, - OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"), /* end commit contents options */ + { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL, + "ok to record an empty change", + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, + { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL, + "ok to record a change with an empty message", + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, + OPT_END() }; @@ -422,7 +431,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int switch (status_format) { case STATUS_FORMAT_SHORT: - wt_shortstatus_print(s, null_termination); + wt_shortstatus_print(s, null_termination, status_show_branch); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(s, null_termination); @@ -730,7 +739,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (use_editor) { char index[PATH_MAX]; - const char *env[2] = { index, NULL }; + const char *env[2] = { NULL }; + env[0] = index; snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); if (launch_editor(git_path(commit_editmsg), NULL, env)) { fprintf(stderr, @@ -1036,6 +1046,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) OPT__VERBOSE(&verbose), OPT_SET_INT('s', "short", &status_format, "show status concisely", STATUS_FORMAT_SHORT), + OPT_BOOLEAN('b', "branch", &status_show_branch, + "show branch information"), OPT_SET_INT(0, "porcelain", &status_format, "show porcelain output format", STATUS_FORMAT_PORCELAIN), @@ -1045,6 +1057,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + OPT_BOOLEAN(0, "ignored", &show_ignored_in_status, + "show ignored files"), OPT_END(), }; @@ -1058,7 +1072,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) builtin_status_options, builtin_status_usage, 0); handle_untracked_files_arg(&s); - + if (show_ignored_in_status) + s.show_ignored_files = 1; if (*argv) s.pathspec = get_pathspec(prefix, argv); @@ -1085,7 +1100,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) switch (status_format) { case STATUS_FORMAT_SHORT: - wt_shortstatus_print(&s, null_termination); + wt_shortstatus_print(&s, null_termination, status_show_branch); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(&s, null_termination); @@ -1318,7 +1333,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); - if (message_is_empty(&sb)) { + if (message_is_empty(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, "Aborting commit due to empty commit message.\n"); exit(1); diff --git a/builtin/config.c b/builtin/config.c index 4bc46b15fd..f3d1660d02 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -197,7 +197,11 @@ static int get_value(const char *key_, const char *regex_) git_config_from_file(show_config, system_wide, NULL); if (do_all && global) git_config_from_file(show_config, global, NULL); - git_config_from_file(show_config, local, NULL); + if (do_all) + git_config_from_file(show_config, local, NULL); + git_config_from_parameters(show_config, NULL); + if (!do_all && !seen) + git_config_from_file(show_config, local, NULL); if (!do_all && !seen && global) git_config_from_file(show_config, global, NULL); if (!do_all && !seen && system_wide) diff --git a/builtin/fetch.c b/builtin/fetch.c index 8470850415..5cb369cfd1 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -574,9 +574,10 @@ static void find_non_local_tags(struct transport *transport, { struct string_list existing_refs = { NULL, 0, 0, 0 }; struct string_list remote_refs = { NULL, 0, 0, 0 }; - struct tag_data data = {head, tail}; + struct tag_data data; const struct ref *ref; struct string_list_item *item = NULL; + data.head = head; data.tail = tail; for_each_ref(add_existing, &existing_refs); for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { @@ -778,7 +779,8 @@ static int get_remote_group(const char *key, const char *value, void *priv) static int add_remote_or_group(const char *name, struct string_list *list) { int prev_nr = list->nr; - struct remote_group_data g = { name, list }; + struct remote_group_data g; + g.name = name; g.list = list; git_config(get_remote_group, &g); if (list->nr == prev_nr) { diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 7f5011f75e..a2b28c6962 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -227,6 +227,9 @@ static void grab_common_values(struct atom_value *val, int deref, struct object strcpy(s, sha1_to_hex(obj->sha1)); v->s = s; } + else if (!strcmp(name, "objectname:short")) { + v->s = find_unique_abbrev(obj->sha1, DEFAULT_ABBREV); + } } } diff --git a/builtin/grep.c b/builtin/grep.c index b194ea3cea..d0a73da07a 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -724,11 +724,15 @@ static int file_callback(const struct option *opt, const char *arg, int unset) if (!patterns) die_errno("cannot open '%s'", arg); while (strbuf_getline(&sb, patterns, '\n') == 0) { + char *s; + size_t len; + /* ignore empty line like grep does */ if (sb.len == 0) continue; - append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg, - ++lno, GREP_PATTERN); + + s = strbuf_detach(&sb, &len); + append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN); } fclose(patterns); strbuf_release(&sb); diff --git a/builtin/help.c b/builtin/help.c index 3182a2bec4..a9836b00ae 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -26,7 +26,7 @@ enum help_format { HELP_FORMAT_NONE, HELP_FORMAT_MAN, HELP_FORMAT_INFO, - HELP_FORMAT_WEB, + HELP_FORMAT_WEB }; static int show_all = 0; diff --git a/builtin/log.c b/builtin/log.c index 6208703c06..f068583618 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -24,6 +24,7 @@ static const char *default_date_mode = NULL; static int default_show_root = 1; +static int decoration_style; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; @@ -31,11 +32,28 @@ static const char * const builtin_log_usage = "git log [<options>] [<since>..<until>] [[--] <path>...]\n" " or: git show [options] <object>..."; +static int parse_decoration_style(const char *var, const char *value) +{ + switch (git_config_maybe_bool(var, value)) { + case 1: + return DECORATE_SHORT_REFS; + case 0: + return 0; + default: + break; + } + if (!strcmp(value, "full")) + return DECORATE_FULL_REFS; + else if (!strcmp(value, "short")) + return DECORATE_SHORT_REFS; + return -1; +} + static void cmd_log_init(int argc, const char **argv, const char *prefix, struct rev_info *rev, struct setup_revision_opt *opt) { int i; - int decoration_style = 0; + int decoration_given = 0; struct userformat_want w; rev->abbrev = DEFAULT_ABBREV; @@ -78,14 +96,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, 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="); - if (!strcmp(v, "full")) - decoration_style = DECORATE_FULL_REFS; - else if (!strcmp(v, "short")) - decoration_style = DECORATE_SHORT_REFS; - else + 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")) { + decoration_style = 0; } else if (!strcmp(arg, "--source")) { rev->show_source = 1; } else if (!strcmp(arg, "-h")) { @@ -93,6 +112,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, } else die("unrecognized argument: %s", arg); } + + /* + * 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); @@ -258,6 +286,12 @@ static int git_log_config(const char *var, const char *value, void *cb) return git_config_string(&fmt_patch_subject_prefix, var, value); if (!strcmp(var, "log.date")) return git_config_string(&default_date_mode, var, value); + if (!strcmp(var, "log.decorate")) { + decoration_style = parse_decoration_style(var, value); + if (decoration_style < 0) + decoration_style = 0; /* maybe warn? */ + return 0; + } if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); return 0; @@ -515,8 +549,9 @@ static void add_header(const char *value) #define THREAD_SHALLOW 1 #define THREAD_DEEP 2 -static int thread = 0; -static int do_signoff = 0; +static int thread; +static int do_signoff; +static const char *signature = git_version_string; static int git_format_config(const char *var, const char *value, void *cb) { @@ -575,6 +610,8 @@ static int git_format_config(const char *var, const char *value, void *cb) do_signoff = git_config_bool(var, value); return 0; } + if (!strcmp(var, "format.signature")) + return git_config_string(&signature, var, value); return git_log_config(var, value, cb); } @@ -669,6 +706,12 @@ static void gen_message_id(struct rev_info *info, char *base) info->message_id = strbuf_detach(&buf, NULL); } +static void print_signature(void) +{ + if (signature && *signature) + printf("-- \n%s\n\n", signature); +} + static void make_cover_letter(struct rev_info *rev, int use_stdout, int numbered, int numbered_files, struct commit *origin, @@ -762,6 +805,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, diff_flush(&opts); printf("\n"); + print_signature(); } static const char *clean_message_id(const char *msg_id) @@ -1001,6 +1045,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "thread", &thread, "style", "enable message threading, styles: shallow, deep", PARSE_OPT_OPTARG, thread_callback }, + OPT_STRING(0, "signature", &signature, "signature", + "add a signature"), OPT_END() }; @@ -1279,7 +1325,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) mime_boundary_leader, rev.mime_boundary); else - printf("-- \n%s\n\n", git_version_string); + print_signature(); } if (!use_stdout) fclose(stdout); diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 70f5622d9d..34480cfad6 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -4,7 +4,8 @@ #include "remote.h" static const char ls_remote_usage[] = -"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>] <repository> <refs>..."; +"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n" +" [-q|--quiet] [<repository> [<refs>...]]"; /* * Is there one among the list of patterns that match the tail part @@ -33,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) const char *dest = NULL; int nongit; unsigned flags = 0; + int quiet = 0; const char *uploadpack = NULL; const char **pattern = NULL; @@ -66,6 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) flags |= REF_NORMAL; continue; } + if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { + quiet = 1; + continue; + } usage(ls_remote_usage); } dest = arg; @@ -73,9 +79,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) break; } - if (!dest) - usage(ls_remote_usage); - if (argv[i]) { int j; pattern = xcalloc(sizeof(const char *), argc - i + 1); @@ -87,6 +90,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) } } remote = remote_get(dest); + if (!remote) { + if (dest) + die("bad repository '%s'", dest); + die("No remote configured to list refs from."); + } if (!remote->url_nr) die("remote %s has no configured URL", dest); transport = transport_get(remote, NULL); @@ -96,6 +104,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) ref = transport_get_remote_refs(transport); if (transport_disconnect(transport)) return 1; + + if (!dest && !quiet) + fprintf(stderr, "From %s\n", *remote->url); for ( ; ref; ref = ref->next) { if (!check_ref_type(ref, flags)) continue; diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 4a9729b9b3..2320d981ce 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -17,10 +17,10 @@ static struct strbuf name = STRBUF_INIT; static struct strbuf email = STRBUF_INIT; static enum { - TE_DONTCARE, TE_QP, TE_BASE64, + TE_DONTCARE, TE_QP, TE_BASE64 } transfer_encoding; static enum { - TYPE_TEXT, TYPE_OTHER, + TYPE_TEXT, TYPE_OTHER } message_type; static struct strbuf charset = STRBUF_INIT; diff --git a/builtin/merge.c b/builtin/merge.c index cae1cbee26..37ce4f589f 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -548,13 +548,53 @@ static void write_tree_trivial(unsigned char *sha1) die("git write-tree failed to write a tree"); } -static int try_merge_strategy(const char *strategy, struct commit_list *common, - const char *head_arg) +int try_merge_command(const char *strategy, struct commit_list *common, + const char *head_arg, struct commit_list *remotes) { const char **args; int i = 0, x = 0, ret; struct commit_list *j; struct strbuf buf = STRBUF_INIT; + + args = xmalloc((4 + xopts_nr + commit_list_count(common) + + commit_list_count(remotes)) * sizeof(char *)); + strbuf_addf(&buf, "merge-%s", strategy); + args[i++] = buf.buf; + for (x = 0; x < xopts_nr; x++) { + char *s = xmalloc(strlen(xopts[x])+2+1); + strcpy(s, "--"); + strcpy(s+2, xopts[x]); + args[i++] = s; + } + for (j = common; j; j = j->next) + args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1)); + 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] = NULL; + ret = run_command_v_opt(args, RUN_GIT_CMD); + strbuf_release(&buf); + i = 1; + for (x = 0; x < xopts_nr; x++) + free((void *)args[i++]); + for (j = common; j; j = j->next) + free((void *)args[i++]); + i += 2; + for (j = remotes; j; j = j->next) + free((void *)args[i++]); + free(args); + discard_cache(); + if (read_cache() < 0) + die("failed to read the cache"); + resolve_undo_clear(); + + return ret; +} + +static int try_merge_strategy(const char *strategy, struct commit_list *common, + const char *head_arg) +{ int index_fd; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); @@ -567,12 +607,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, rollback_lock_file(lock); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { - int clean; + int clean, x; struct commit *result; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd; struct commit_list *reversed = NULL; struct merge_options o; + struct commit_list *j; if (remoteheads->next) { error("Not handling anything other than two heads merge."); @@ -612,39 +653,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, rollback_lock_file(lock); return clean ? 0 : 1; } else { - args = xmalloc((4 + xopts_nr + commit_list_count(common) + - commit_list_count(remoteheads)) * sizeof(char *)); - strbuf_addf(&buf, "merge-%s", strategy); - args[i++] = buf.buf; - for (x = 0; x < xopts_nr; x++) { - char *s = xmalloc(strlen(xopts[x])+2+1); - strcpy(s, "--"); - strcpy(s+2, xopts[x]); - args[i++] = s; - } - for (j = common; j; j = j->next) - args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1)); - args[i++] = "--"; - args[i++] = head_arg; - for (j = remoteheads; j; j = j->next) - args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1)); - args[i] = NULL; - ret = run_command_v_opt(args, RUN_GIT_CMD); - strbuf_release(&buf); - i = 1; - for (x = 0; x < xopts_nr; x++) - free((void *)args[i++]); - for (j = common; j; j = j->next) - free((void *)args[i++]); - i += 2; - for (j = remoteheads; j; j = j->next) - free((void *)args[i++]); - free(args); - discard_cache(); - if (read_cache() < 0) - die("failed to read the cache"); - resolve_undo_clear(); - return ret; + return try_merge_command(strategy, common, head_arg, remoteheads); } } diff --git a/builtin/notes.c b/builtin/notes.c index f678f9cb52..190005f3cd 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = { "git notes [--ref <notes_ref>] edit [<object>]", "git notes [--ref <notes_ref>] show [<object>]", "git notes [--ref <notes_ref>] remove [<object>]", - "git notes [--ref <notes_ref>] prune", + "git notes [--ref <notes_ref>] prune [-n | -v]", NULL }; @@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = { }; static const char * const git_notes_prune_usage[] = { - "git notes prune", + "git notes prune [<options>]", NULL }; @@ -796,7 +796,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) static int prune(int argc, const char **argv, const char *prefix) { struct notes_tree *t; + int show_only = 0, verbose = 0; struct option options[] = { + OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"), + OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"), OPT_END() }; @@ -810,8 +813,10 @@ static int prune(int argc, const char **argv, const char *prefix) t = init_notes_check("prune"); - prune_notes(t); - commit_notes(t, "Notes removed by 'git notes prune'"); + prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) | + (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) ); + if (!show_only) + commit_notes(t, "Notes removed by 'git notes prune'"); free_notes(t); return 0; } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 214d7ef2b1..0e81673118 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1529,6 +1529,8 @@ static void try_to_free_from_threads(size_t size) read_unlock(); } +try_to_free_t old_try_to_free_routine; + /* * The main thread waits on the condition that (at least) one of the workers * has stopped working (which is indicated in the .working member of @@ -1563,12 +1565,12 @@ static void init_threaded_search(void) pthread_mutex_init(&cache_mutex, NULL); pthread_mutex_init(&progress_mutex, NULL); pthread_cond_init(&progress_cond, NULL); - set_try_to_free_routine(try_to_free_from_threads); + old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads); } static void cleanup_threaded_search(void) { - set_try_to_free_routine(NULL); + set_try_to_free_routine(old_try_to_free_routine); pthread_cond_destroy(&progress_cond); pthread_mutex_destroy(&read_mutex); pthread_mutex_destroy(&cache_mutex); diff --git a/builtin/patch-id.c b/builtin/patch-id.c index af0911e4bd..512530022e 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -28,16 +28,42 @@ static int remove_space(char *line) return dst - line; } -static void generate_id_list(void) +static int scan_hunk_header(const char *p, int *p_before, int *p_after) +{ + static const char digits[] = "0123456789"; + const char *q, *r; + int n; + + q = p + 4; + n = strspn(q, digits); + if (q[n] == ',') { + q += n + 1; + n = strspn(q, digits); + } + if (n == 0 || q[n] != ' ' || q[n+1] != '+') + return 0; + + r = q + n + 2; + n = strspn(r, digits); + if (r[n] == ',') { + r += n + 1; + n = strspn(r, digits); + } + if (n == 0) + return 0; + + *p_before = atoi(q); + *p_after = atoi(r); + return 1; +} + +int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx) { - static unsigned char sha1[20]; static char line[1000]; - git_SHA_CTX ctx; - int patchlen = 0; + int patchlen = 0, found_next = 0; + int before = -1, after = -1; - git_SHA1_Init(&ctx); while (fgets(line, sizeof(line), stdin) != NULL) { - unsigned char n[20]; char *p = line; int len; @@ -45,32 +71,75 @@ static void generate_id_list(void) p += 10; else if (!memcmp(line, "commit ", 7)) p += 7; + else if (!memcmp(line, "From ", 5)) + p += 5; - if (!get_sha1_hex(p, n)) { - flush_current_id(patchlen, sha1, &ctx); - hashcpy(sha1, n); - patchlen = 0; - continue; + if (!get_sha1_hex(p, next_sha1)) { + found_next = 1; + break; } /* Ignore commit comments */ if (!patchlen && memcmp(line, "diff ", 5)) continue; - /* Ignore git-diff index header */ - if (!memcmp(line, "index ", 6)) - continue; + /* Parsing diff header? */ + if (before == -1) { + if (!memcmp(line, "index ", 6)) + continue; + else if (!memcmp(line, "--- ", 4)) + before = after = 1; + else if (!isalpha(line[0])) + break; + } - /* Ignore line numbers when computing the SHA1 of the patch */ - if (!memcmp(line, "@@ -", 4)) - continue; + /* Looking for a valid hunk header? */ + if (before == 0 && after == 0) { + if (!memcmp(line, "@@ -", 4)) { + /* Parse next hunk, but ignore line numbers. */ + scan_hunk_header(line, &before, &after); + continue; + } + + /* Split at the end of the patch. */ + if (memcmp(line, "diff ", 5)) + break; + + /* Else we're parsing another header. */ + before = after = -1; + } + + /* If we get here, we're inside a hunk. */ + if (line[0] == '-' || line[0] == ' ') + before--; + if (line[0] == '+' || line[0] == ' ') + after--; /* Compute the sha without whitespace */ len = remove_space(line); patchlen += len; - git_SHA1_Update(&ctx, line, len); + git_SHA1_Update(ctx, line, len); + } + + if (!found_next) + hashclr(next_sha1); + + return patchlen; +} + +static void generate_id_list(void) +{ + unsigned char sha1[20], n[20]; + git_SHA_CTX ctx; + int patchlen; + + git_SHA1_Init(&ctx); + hashclr(sha1); + while (!feof(stdin)) { + patchlen = get_one_patchid(n, &ctx); + flush_current_id(patchlen, sha1, &ctx); + hashcpy(sha1, n); } - flush_current_id(patchlen, sha1, &ctx); } static const char patch_id_usage[] = "git patch-id < patch"; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 0559fcc871..29bc8d50bb 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -9,6 +9,7 @@ #include "object.h" #include "remote.h" #include "transport.h" +#include "string-list.h" static const char receive_pack_usage[] = "git receive-pack <git-dir>"; @@ -16,7 +17,7 @@ enum deny_action { DENY_UNCONFIGURED, DENY_IGNORE, DENY_WARN, - DENY_REFUSE, + DENY_REFUSE }; static int deny_deletes; @@ -129,13 +130,12 @@ static void write_head_info(void) struct command { struct command *next; const char *error_string; + unsigned int skip_update; unsigned char old_sha1[20]; unsigned char new_sha1[20]; char ref_name[FLEX_ARRAY]; /* more */ }; -static struct command *commands; - static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; @@ -188,7 +188,7 @@ static int copy_to_sideband(int in, int out, void *arg) return 0; } -static int run_receive_hook(const char *hook_name) +static int run_receive_hook(struct command *commands, const char *hook_name) { static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4]; struct command *cmd; @@ -447,15 +447,15 @@ static const char *update(struct command *cmd) static char update_post_hook[] = "hooks/post-update"; -static void run_update_post_hook(struct command *cmd) +static void run_update_post_hook(struct command *commands) { - struct command *cmd_p; + struct command *cmd; int argc; const char **argv; struct child_process proc; - for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) { - if (cmd_p->error_string) + for (argc = 0, cmd = commands; cmd; cmd = cmd->next) { + if (cmd->error_string) continue; argc++; } @@ -464,12 +464,12 @@ static void run_update_post_hook(struct command *cmd) argv = xmalloc(sizeof(*argv) * (2 + argc)); argv[0] = update_post_hook; - for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) { + for (argc = 1, cmd = commands; cmd; cmd = cmd->next) { char *p; - if (cmd_p->error_string) + if (cmd->error_string) continue; - p = xmalloc(strlen(cmd_p->ref_name) + 1); - strcpy(p, cmd_p->ref_name); + p = xmalloc(strlen(cmd->ref_name) + 1); + strcpy(p, cmd->ref_name); argv[argc] = p; argc++; } @@ -488,37 +488,92 @@ static void run_update_post_hook(struct command *cmd) } } -static void execute_commands(const char *unpacker_error) +static void check_aliased_update(struct command *cmd, struct string_list *list) +{ + 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); + + if (!(flag & REF_ISSYMREF)) + return; + + if ((item = string_list_lookup(dst_name, list)) == NULL) + return; + + cmd->skip_update = 1; + + dst_cmd = (struct command *) item->util; + + if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) && + !hashcmp(cmd->new_sha1, dst_cmd->new_sha1)) + return; + + dst_cmd->skip_update = 1; + + strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV)); + strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV)); + strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV)); + strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV)); + rp_error("refusing inconsistent update between symref '%s' (%s..%s) and" + " its target '%s' (%s..%s)", + cmd->ref_name, cmd_oldh, cmd_newh, + dst_cmd->ref_name, dst_oldh, dst_newh); + + cmd->error_string = dst_cmd->error_string = + "inconsistent aliased update"; +} + +static void check_aliased_updates(struct command *commands) +{ + struct command *cmd; + struct string_list ref_list = { NULL, 0, 0, 0 }; + + for (cmd = commands; cmd; cmd = cmd->next) { + struct string_list_item *item = + string_list_append(cmd->ref_name, &ref_list); + item->util = (void *)cmd; + } + sort_string_list(&ref_list); + + for (cmd = commands; cmd; cmd = cmd->next) + check_aliased_update(cmd, &ref_list); + + string_list_clear(&ref_list, 0); +} + +static void execute_commands(struct command *commands, const char *unpacker_error) { - struct command *cmd = commands; + struct command *cmd; unsigned char sha1[20]; if (unpacker_error) { - while (cmd) { + for (cmd = commands; cmd; cmd = cmd->next) cmd->error_string = "n/a (unpacker error)"; - cmd = cmd->next; - } return; } - if (run_receive_hook(pre_receive_hook)) { - while (cmd) { + if (run_receive_hook(commands, pre_receive_hook)) { + for (cmd = commands; cmd; cmd = cmd->next) cmd->error_string = "pre-receive hook declined"; - cmd = cmd->next; - } return; } + check_aliased_updates(commands); + head_name = resolve_ref("HEAD", sha1, 0, NULL); - while (cmd) { - cmd->error_string = update(cmd); - cmd = cmd->next; - } + for (cmd = commands; cmd; cmd = cmd->next) + if (!cmd->skip_update) + cmd->error_string = update(cmd); } -static void read_head_info(void) +static struct command *read_head_info(void) { + struct command *commands = NULL; struct command **p = &commands; for (;;) { static char line[1000]; @@ -548,15 +603,14 @@ static void read_head_info(void) if (strstr(refname + reflen + 1, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; } - cmd = xmalloc(sizeof(struct command) + len - 80); + cmd = xcalloc(1, sizeof(struct command) + len - 80); hashcpy(cmd->old_sha1, old_sha1); hashcpy(cmd->new_sha1, new_sha1); memcpy(cmd->ref_name, line + 82, len - 81); - cmd->error_string = NULL; - cmd->next = NULL; *p = cmd; p = &cmd->next; } + return commands; } static const char *parse_pack_header(struct pack_header *hdr) @@ -643,7 +697,7 @@ static const char *unpack(void) } } -static void report(const char *unpack_status) +static void report(struct command *commands, const char *unpack_status) { struct command *cmd; struct strbuf buf = STRBUF_INIT; @@ -667,12 +721,12 @@ static void report(const char *unpack_status) strbuf_release(&buf); } -static int delete_only(struct command *cmd) +static int delete_only(struct command *commands) { - while (cmd) { + struct command *cmd; + for (cmd = commands; cmd; cmd = cmd->next) { if (!is_null_sha1(cmd->new_sha1)) return 0; - cmd = cmd->next; } return 1; } @@ -722,6 +776,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) int stateless_rpc = 0; int i; char *dir = NULL; + struct command *commands; argv++; for (i = 1; i < argc; i++) { @@ -772,18 +827,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (advertise_refs) return 0; - read_head_info(); - if (commands) { + if ((commands = read_head_info()) != NULL) { const char *unpack_status = NULL; if (!delete_only(commands)) unpack_status = unpack(); - execute_commands(unpack_status); + execute_commands(commands, unpack_status); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) - report(unpack_status); - run_receive_hook(post_receive_hook); + report(commands, unpack_status); + run_receive_hook(commands, post_receive_hook); run_update_post_hook(commands); if (auto_gc) { const char *argv_gc_auto[] = { diff --git a/builtin/reflog.c b/builtin/reflog.c index bd7880dc04..ebf610e64a 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -34,8 +34,13 @@ struct cmd_reflog_expire_cb { struct expire_reflog_cb { FILE *newlog; - const char *ref; - struct commit *ref_commit; + enum { + UE_NORMAL, + UE_ALWAYS, + UE_HEAD + } unreachable_expire_kind; + struct commit_list *mark_list; + unsigned long mark_limit; struct cmd_reflog_expire_cb *cmd; unsigned char last_kept_sha1[20]; }; @@ -210,46 +215,23 @@ static int keep_entry(struct commit **it, unsigned char *sha1) return 1; } -static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1) +/* + * Starting from commits in the cb->mark_list, mark commits that are + * reachable from them. Stop the traversal at commits older than + * the expire_limit and queue them back, so that the caller can call + * us again to restart the traversal with longer expire_limit. + */ +static void mark_reachable(struct expire_reflog_cb *cb) { - /* - * We may or may not have the commit yet - if not, look it - * up using the supplied sha1. - */ - if (!commit) { - if (is_null_sha1(sha1)) - return 0; - - commit = lookup_commit_reference_gently(sha1, 1); - - /* Not a commit -- keep it */ - if (!commit) - return 0; - } - - /* Reachable from the current ref? Don't prune. */ - if (commit->object.flags & REACHABLE) - return 0; - if (in_merge_bases(commit, &cb->ref_commit, 1)) - return 0; - - /* We can't reach it - prune it. */ - return 1; -} + struct commit *commit; + struct commit_list *pending; + unsigned long expire_limit = cb->mark_limit; + struct commit_list *leftover = NULL; -static void mark_reachable(struct commit *commit, unsigned long expire_limit) -{ - /* - * We need to compute whether the commit on either side of a reflog - * entry is reachable from the tip of the ref for all entries. - * Mark commits that are reachable from the tip down to the - * time threshold first; we know a commit marked thusly is - * reachable from the tip without running in_merge_bases() - * at all. - */ - struct commit_list *pending = NULL; + for (pending = cb->mark_list; pending; pending = pending->next) + pending->item->object.flags &= ~REACHABLE; - commit_list_insert(commit, &pending); + pending = cb->mark_list; while (pending) { struct commit_list *entry = pending; struct commit_list *parent; @@ -261,8 +243,11 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit) if (parse_commit(commit)) continue; commit->object.flags |= REACHABLE; - if (commit->date < expire_limit) + if (commit->date < expire_limit) { + commit_list_insert(commit, &leftover); continue; + } + commit->object.flags |= REACHABLE; parent = commit->parents; while (parent) { commit = parent->item; @@ -272,6 +257,36 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit) commit_list_insert(commit, &pending); } } + cb->mark_list = leftover; +} + +static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1) +{ + /* + * We may or may not have the commit yet - if not, look it + * up using the supplied sha1. + */ + if (!commit) { + if (is_null_sha1(sha1)) + return 0; + + commit = lookup_commit_reference_gently(sha1, 1); + + /* Not a commit -- keep it */ + if (!commit) + return 0; + } + + /* Reachable from the current ref? Don't prune. */ + if (commit->object.flags & REACHABLE) + return 0; + + if (cb->mark_list && cb->mark_limit) { + cb->mark_limit = 0; /* dig down to the root */ + mark_reachable(cb); + } + + return !(commit->object.flags & REACHABLE); } static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, @@ -293,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, goto prune; if (timestamp < cb->cmd->expire_unreachable) { - if (!cb->ref_commit) + if (cb->unreachable_expire_kind == UE_ALWAYS) goto prune; if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1)) goto prune; @@ -320,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, return 0; } +static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data) +{ + struct commit_list **list = cb_data; + struct commit *tip_commit; + if (flags & REF_ISSYMREF) + return 0; + tip_commit = lookup_commit_reference_gently(sha1, 1); + if (!tip_commit) + return 0; + commit_list_insert(tip_commit, list); + return 0; +} + static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data) { struct cmd_reflog_expire_cb *cmd = cb_data; struct expire_reflog_cb cb; struct ref_lock *lock; char *log_file, *newlog_path = NULL; + struct commit *tip_commit; + struct commit_list *tips; int status = 0; memset(&cb, 0, sizeof(cb)); @@ -345,14 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, cb.newlog = fopen(newlog_path, "w"); } - cb.ref_commit = lookup_commit_reference_gently(sha1, 1); - cb.ref = ref; cb.cmd = cmd; - if (cb.ref_commit) - mark_reachable(cb.ref_commit, cmd->expire_total); + + if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) { + tip_commit = NULL; + cb.unreachable_expire_kind = UE_HEAD; + } else { + tip_commit = lookup_commit_reference_gently(sha1, 1); + if (!tip_commit) + cb.unreachable_expire_kind = UE_ALWAYS; + else + cb.unreachable_expire_kind = UE_NORMAL; + } + + if (cmd->expire_unreachable <= cmd->expire_total) + cb.unreachable_expire_kind = UE_ALWAYS; + + cb.mark_list = NULL; + tips = NULL; + if (cb.unreachable_expire_kind != UE_ALWAYS) { + if (cb.unreachable_expire_kind == UE_HEAD) { + struct commit_list *elem; + for_each_ref(push_tip_to_list, &tips); + for (elem = tips; elem; elem = elem->next) + commit_list_insert(elem->item, &cb.mark_list); + } else { + commit_list_insert(tip_commit, &cb.mark_list); + } + cb.mark_limit = cmd->expire_total; + mark_reachable(&cb); + } + for_each_reflog_ent(ref, expire_reflog_ent, &cb); - if (cb.ref_commit) - clear_commit_marks(cb.ref_commit, REACHABLE); + + if (cb.unreachable_expire_kind != UE_ALWAYS) { + if (cb.unreachable_expire_kind == UE_HEAD) { + struct commit_list *elem; + for (elem = tips; elem; elem = elem->next) + clear_commit_marks(tip_commit, REACHABLE); + free_commit_list(tips); + } else { + clear_commit_marks(tip_commit, REACHABLE); + } + } finish: if (cb.newlog) { if (fclose(cb.newlog)) { diff --git a/builtin/remote.c b/builtin/remote.c index 277765b864..0a52667e0f 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = { "git remote [-v | --verbose] show [-n] <name>", "git remote prune [-n | --dry-run] <name>", "git remote [-v | --verbose] update [-p | --prune] [group | remote]", + "git remote set-branches <name> [--add] <branch>...", "git remote set-url <name> <newurl> [<oldurl>]", "git remote set-url --add <name> <newurl>", "git remote set-url --delete <name> <url>", @@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = { NULL }; +static const char * const builtin_remote_setbranches_usage[] = { + "git remote set-branches <name> <branch>...", + "git remote set-branches --add <name> <branch>...", + NULL +}; + static const char * const builtin_remote_show_usage[] = { "git remote show [<options>] <name>", NULL @@ -104,9 +111,29 @@ static int fetch_remote(const char *name) return 0; } +enum { + TAGS_UNSET = 0, + TAGS_DEFAULT = 1, + TAGS_SET = 2 +}; + +static int add_branch(const char *key, const char *branchname, + const char *remotename, int mirror, struct strbuf *tmp) +{ + strbuf_reset(tmp); + strbuf_addch(tmp, '+'); + if (mirror) + strbuf_addf(tmp, "refs/%s:refs/%s", + branchname, branchname); + else + strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s", + branchname, remotename, branchname); + return git_config_set_multivar(key, tmp->buf, "^$", 0); +} + static int add(int argc, const char **argv) { - int fetch = 0, mirror = 0; + int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT; struct string_list track = { NULL, 0, 0 }; const char *master = NULL; struct remote *remote; @@ -116,6 +143,11 @@ static int add(int argc, const char **argv) struct option options[] = { OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"), + OPT_SET_INT(0, "tags", &fetch_tags, + "import all tags and associated objects when fetching", + 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('m', "master", &master, "branch", "master branch"), @@ -151,17 +183,8 @@ static int add(int argc, const char **argv) if (track.nr == 0) string_list_append("*", &track); for (i = 0; i < track.nr; i++) { - struct string_list_item *item = track.items + i; - - strbuf_reset(&buf2); - strbuf_addch(&buf2, '+'); - if (mirror) - strbuf_addf(&buf2, "refs/%s:refs/%s", - item->string, item->string); - else - strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s", - item->string, name, item->string); - if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0)) + if (add_branch(buf.buf, track.items[i].string, + name, mirror, &buf2)) return 1; } @@ -172,6 +195,14 @@ static int add(int argc, const char **argv) return 1; } + if (fetch_tags != TAGS_DEFAULT) { + strbuf_reset(&buf); + strbuf_addf(&buf, "remote.%s.tagopt", name); + if (git_config_set(buf.buf, + fetch_tags == TAGS_SET ? "--tags" : "--no-tags")) + return 1; + } + if (fetch && fetch_remote(name)) return 1; @@ -317,7 +348,7 @@ struct push_info { PUSH_STATUS_UPTODATE, PUSH_STATUS_FASTFORWARD, PUSH_STATUS_OUTOFDATE, - PUSH_STATUS_NOTQUERIED, + PUSH_STATUS_NOTQUERIED } status; }; @@ -705,11 +736,14 @@ static int rm(int argc, const char **argv) struct known_remotes known_remotes = { NULL, NULL }; struct string_list branches = { NULL, 0, 0, 1 }; struct string_list skipped = { NULL, 0, 0, 1 }; - struct branches_for_remote cb_data = { - NULL, &branches, &skipped, &known_remotes - }; + struct branches_for_remote cb_data; int i, result; + memset(&cb_data, 0, sizeof(cb_data)); + cb_data.branches = &branches; + cb_data.skipped = &skipped; + cb_data.keep = &known_remotes; + if (argc != 2) usage_with_options(builtin_remote_rm_usage, options); @@ -1265,6 +1299,72 @@ static int update(int argc, const char **argv) return run_command_v_opt(fetch_argv, RUN_GIT_CMD); } +static int remove_all_fetch_refspecs(const char *remote, const char *key) +{ + return git_config_set_multivar(key, NULL, NULL, 1); +} + +static int add_branches(struct remote *remote, const char **branches, + const char *key) +{ + const char *remotename = remote->name; + int mirror = remote->mirror; + struct strbuf refspec = STRBUF_INIT; + + for (; *branches; branches++) + if (add_branch(key, *branches, remotename, mirror, &refspec)) { + strbuf_release(&refspec); + return 1; + } + + strbuf_release(&refspec); + return 0; +} + +static int set_remote_branches(const char *remotename, const char **branches, + int add_mode) +{ + struct strbuf key = STRBUF_INIT; + struct remote *remote; + + strbuf_addf(&key, "remote.%s.fetch", remotename); + + if (!remote_is_configured(remotename)) + die("No such remote '%s'", remotename); + remote = remote_get(remotename); + + if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) { + strbuf_release(&key); + return 1; + } + if (add_branches(remote, branches, key.buf)) { + strbuf_release(&key); + return 1; + } + + strbuf_release(&key); + return 0; +} + +static int set_branches(int argc, const char **argv) +{ + int add_mode = 0; + struct option options[] = { + OPT_BOOLEAN('\0', "add", &add_mode, "add branch"), + OPT_END() + }; + + argc = parse_options(argc, argv, NULL, options, + builtin_remote_setbranches_usage, 0); + if (argc == 0) { + error("no remote specified"); + usage_with_options(builtin_remote_seturl_usage, options); + } + argv[argc] = NULL; + + return set_remote_branches(argv[0], argv + 1, add_mode); +} + static int set_url(int argc, const char **argv) { int i, push_mode = 0, add_mode = 0, delete_mode = 0; @@ -1430,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix) result = rm(argc, argv); else if (!strcmp(argv[0], "set-head")) result = set_head(argc, argv); + else if (!strcmp(argv[0], "set-branches")) + result = set_branches(argc, argv); else if (!strcmp(argv[0], "set-url")) result = set_url(argc, argv); else if (!strcmp(argv[0], "show")) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 8fbf9d0db6..b676e29635 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -408,7 +408,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) memset(opts + onb, 0, sizeof(opts[onb])); argc = parse_options(argc, argv, prefix, opts, usage, keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 | - stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0); + stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0 | + PARSE_OPT_SHELL_EVAL); strbuf_addf(&parsed, " --"); sq_quote_argv(&parsed, argv, 0); diff --git a/builtin/revert.c b/builtin/revert.c index 7d68ef714e..853e9e406c 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -39,10 +39,12 @@ static const char * const cherry_pick_usage[] = { static int edit, no_replay, no_commit, mainline, signoff, allow_ff; static enum { REVERT, CHERRY_PICK } action; static struct commit *commit; -static const char *commit_name; +static int commit_argc; +static const char **commit_argv; static int allow_rerere_auto; static const char *me; +static const char *strategy; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -52,16 +54,15 @@ static void parse_args(int argc, const char **argv) { const char * const * usage_str = action == REVERT ? revert_usage : cherry_pick_usage; - unsigned char sha1[20]; int noop; struct option options[] = { OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"), OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), - OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), 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_END(), OPT_END(), OPT_END(), @@ -69,6 +70,7 @@ static void parse_args(int argc, const char **argv) if (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_END(), }; @@ -76,15 +78,11 @@ static void parse_args(int argc, const char **argv) die("program error"); } - if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1) + commit_argc = parse_options(argc, argv, NULL, options, usage_str, 0); + if (commit_argc < 1) usage_with_options(usage_str, options); - commit_name = argv[0]; - if (get_sha1(commit_name, sha1)) - die ("Cannot find '%s'", commit_name); - commit = lookup_commit_reference(sha1); - if (!commit) - exit(1); + commit_argv = argv; } struct commit_message { @@ -174,28 +172,17 @@ static char *get_encoding(const char *message) return NULL; } -static struct lock_file msg_file; -static int msg_fd; - -static void add_to_msg(const char *string) -{ - int len = strlen(string); - if (write_in_full(msg_fd, string, len) < 0) - die_errno ("Could not write to MERGE_MSG"); -} - -static void add_message_to_msg(const char *message) +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) - add_to_msg(sha1_to_hex(commit->object.sha1)); + strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1)); p += 2; - add_to_msg(p); - return; + strbuf_addstr(msgbuf, p); } static void set_author_ident_env(const char *message) @@ -248,7 +235,7 @@ static void set_author_ident_env(const char *message) sha1_to_hex(commit->object.sha1)); } -static char *help_msg(const char *name) +static char *help_msg(void) { struct strbuf helpbuf = STRBUF_INIT; char *msg = getenv("GIT_CHERRY_PICK_HELP"); @@ -264,13 +251,26 @@ static char *help_msg(const char *name) strbuf_addf(&helpbuf, " with: \n" "\n" " git commit -c %s\n", - name); + sha1_to_hex(commit->object.sha1)); } else strbuf_addch(&helpbuf, '.'); return strbuf_detach(&helpbuf, NULL); } +static void write_message(struct strbuf *msgbuf, const char *filename) +{ + static struct lock_file msg_file; + + 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); + strbuf_release(msgbuf); + if (commit_lock_file(&msg_file) < 0) + die("Error wrapping up %s", filename); +} + static struct tree *empty_tree(void) { struct tree *tree = xcalloc(1, sizeof(struct tree)); @@ -305,40 +305,71 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from) return write_ref_sha1(ref_lock, to, "cherry-pick"); } -static int revert_or_cherry_pick(int argc, const char **argv) +static void do_recursive_merge(struct commit *base, struct commit *next, + const char *base_label, const char *next_label, + unsigned char *head, struct strbuf *msgbuf, + char *defmsg) { - unsigned char head[20]; - struct commit *base, *next, *parent; - const char *base_label, *next_label; - int i, index_fd, clean; - struct commit_message msg = { NULL, NULL, NULL, NULL, NULL }; - char *defmsg = NULL; struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; + int clean, index_fd; static struct lock_file index_lock; - git_config(git_default_config, NULL); - me = action == REVERT ? "revert" : "cherry-pick"; - setenv(GIT_REFLOG_ACTION, me, 0); - parse_args(argc, argv); + index_fd = hold_locked_index(&index_lock, 1); - /* this is copied from the shell script, but it's never triggered... */ - if (action == REVERT && !no_replay) - die("revert is incompatible with replay"); + read_cache(); + init_merge_options(&o); + o.ancestor = base ? base_label : "(empty tree)"; + o.branch1 = "HEAD"; + o.branch2 = next ? next_label : "(empty tree)"; - 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"); + head_tree = parse_tree_indirect(head); + next_tree = next ? next->tree : empty_tree(); + base_tree = base ? base->tree : empty_tree(); + + clean = merge_trees(&o, + head_tree, + next_tree, base_tree, &result); + + if (active_cache_changed && + (write_cache(index_fd, active_cache, active_nr) || + commit_locked_index(&index_lock))) + die("%s: Unable to write new index file", me); + rollback_lock_file(&index_lock); + + if (!clean) { + int i; + strbuf_addstr(msgbuf, "\nConflicts:\n\n"); + for (i = 0; i < active_nr;) { + struct cache_entry *ce = active_cache[i++]; + if (ce_stage(ce)) { + strbuf_addch(msgbuf, '\t'); + strbuf_addstr(msgbuf, ce->name); + strbuf_addch(msgbuf, '\n'); + while (i < active_nr && !strcmp(ce->name, + active_cache[i]->name)) + i++; + } + } + write_message(msgbuf, defmsg); + fprintf(stderr, "Automatic %s failed.%s\n", + me, help_msg()); + rerere(allow_rerere_auto); + exit(1); } + write_message(msgbuf, defmsg); + fprintf(stderr, "Finished one %s.\n", me); +} + +static int do_pick_commit(void) +{ + unsigned char head[20]; + struct commit *base, *next, *parent; + const char *base_label, *next_label; + struct commit_message msg = { NULL, NULL, NULL, NULL, NULL }; + char *defmsg = NULL; + struct strbuf msgbuf = STRBUF_INIT; - if (read_cache() < 0) - die("git %s: failed to read the index", me); if (no_commit) { /* * We do not intend to commit immediately. We just want to @@ -403,83 +434,59 @@ static int revert_or_cherry_pick(int argc, const char **argv) */ defmsg = git_pathdup("MERGE_MSG"); - msg_fd = hold_lock_file_for_update(&msg_file, defmsg, - LOCK_DIE_ON_ERROR); - - index_fd = hold_locked_index(&index_lock, 1); if (action == REVERT) { base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; - add_to_msg("Revert \""); - add_to_msg(msg.subject); - add_to_msg("\"\n\nThis reverts commit "); - add_to_msg(sha1_to_hex(commit->object.sha1)); + strbuf_addstr(&msgbuf, "Revert \""); + strbuf_addstr(&msgbuf, msg.subject); + strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit "); + strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); if (commit->parents->next) { - add_to_msg(", reversing\nchanges made to "); - add_to_msg(sha1_to_hex(parent->object.sha1)); + strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); + strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1)); } - add_to_msg(".\n"); + strbuf_addstr(&msgbuf, ".\n"); } else { base = parent; base_label = msg.parent_label; next = commit; next_label = msg.label; set_author_ident_env(msg.message); - add_message_to_msg(msg.message); + add_message_to_msg(&msgbuf, msg.message); if (no_replay) { - add_to_msg("(cherry picked from commit "); - add_to_msg(sha1_to_hex(commit->object.sha1)); - add_to_msg(")\n"); + strbuf_addstr(&msgbuf, "(cherry picked from commit "); + strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); + strbuf_addstr(&msgbuf, ")\n"); } } - read_cache(); - init_merge_options(&o); - o.ancestor = base ? base_label : "(empty tree)"; - o.branch1 = "HEAD"; - o.branch2 = next ? next_label : "(empty tree)"; - - head_tree = parse_tree_indirect(head); - next_tree = next ? next->tree : empty_tree(); - base_tree = base ? base->tree : empty_tree(); - - clean = merge_trees(&o, - head_tree, - next_tree, base_tree, &result); - - if (active_cache_changed && - (write_cache(index_fd, active_cache, active_nr) || - commit_locked_index(&index_lock))) - die("%s: Unable to write new index file", me); - rollback_lock_file(&index_lock); - - if (!clean) { - add_to_msg("\nConflicts:\n\n"); - for (i = 0; i < active_nr;) { - struct cache_entry *ce = active_cache[i++]; - if (ce_stage(ce)) { - add_to_msg("\t"); - add_to_msg(ce->name); - add_to_msg("\n"); - while (i < active_nr && !strcmp(ce->name, - active_cache[i]->name)) - i++; - } + if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) + do_recursive_merge(base, next, base_label, next_label, + head, &msgbuf, defmsg); + else { + int res; + struct commit_list *common = NULL; + struct commit_list *remotes = NULL; + write_message(&msgbuf, defmsg); + commit_list_insert(base, &common); + commit_list_insert(next, &remotes); + res = try_merge_command(strategy, common, + sha1_to_hex(head), remotes); + free_commit_list(common); + free_commit_list(remotes); + if (res) { + fprintf(stderr, "Automatic %s with strategy %s failed.%s\n", + me, strategy, help_msg()); + rerere(allow_rerere_auto); + exit(1); } - if (commit_lock_file(&msg_file) < 0) - die ("Error wrapping up %s", defmsg); - fprintf(stderr, "Automatic %s failed.%s\n", - me, help_msg(commit_name)); - rerere(allow_rerere_auto); - exit(1); } - if (commit_lock_file(&msg_file) < 0) - die ("Error wrapping up %s", defmsg); - fprintf(stderr, "Finished one %s.\n", me); + + free_message(&msg); /* * @@ -493,7 +500,9 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (!no_commit) { /* 6 is max possible length of our args array including NULL */ const char *args[6]; + int res; int i = 0; + args[i++] = "commit"; args[i++] = "-n"; if (signoff) @@ -503,26 +512,86 @@ static int revert_or_cherry_pick(int argc, const char **argv) args[i++] = defmsg; } args[i] = NULL; - return execv_git_cmd(args); + res = run_command_v_opt(args, RUN_GIT_CMD); + free(defmsg); + + return res; } - free_message(&msg); + free(defmsg); return 0; } +static void prepare_revs(struct rev_info *revs) +{ + int argc = 0; + int i; + const char **argv = xmalloc((commit_argc + 4) * sizeof(*argv)); + + argv[argc++] = NULL; + argv[argc++] = "--no-walk"; + if (action != REVERT) + argv[argc++] = "--reverse"; + for (i = 0; i < commit_argc; i++) + argv[argc++] = commit_argv[i]; + argv[argc++] = NULL; + + init_revisions(revs, NULL); + setup_revisions(argc - 1, argv, revs, NULL); + if (prepare_revision_walk(revs)) + die("revision walk setup failed"); + + if (!revs->commits) + die("empty commit set passed"); + + free(argv); +} + +static int revert_or_cherry_pick(int argc, const char **argv) +{ + struct rev_info revs; + + git_config(git_default_config, NULL); + me = action == REVERT ? "revert" : "cherry-pick"; + setenv(GIT_REFLOG_ACTION, me, 0); + parse_args(argc, argv); + + 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"); + } + + if (read_cache() < 0) + die("git %s: failed to read the index", me); + + prepare_revs(&revs); + + while ((commit = get_revision(&revs))) { + int res = do_pick_commit(); + if (res) + return res; + } + + return 0; +} + int cmd_revert(int argc, const char **argv, const char *prefix) { if (isatty(0)) edit = 1; - no_replay = 1; action = REVERT; return revert_or_cherry_pick(argc, argv); } int cmd_cherry_pick(int argc, const char **argv, const char *prefix) { - no_replay = 0; action = CHERRY_PICK; return revert_or_cherry_pick(argc, argv); } |