diff options
Diffstat (limited to 'builtin')
65 files changed, 2508 insertions, 1789 deletions
diff --git a/builtin/add.c b/builtin/add.c index 1d74763f58..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,35 +56,14 @@ 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. - */ + die(_("unexpected diff status %c"), p->status); case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: if (add_file_to_index(&the_index, path, data->flags)) { if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) - die("updating files failed"); + die(_("updating files failed")); data->add_errors++; } break; @@ -73,7 +73,7 @@ static void update_callback(struct diff_queue_struct *q, if (!(data->flags & ADD_CACHE_PRETEND)) remove_file_from_index(&the_index, path); if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) - printf("remove '%s'\n", path); + printf(_("remove '%s'\n"), path); break; } } @@ -85,12 +85,13 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags) struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = pathspec; + init_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; 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; } @@ -171,7 +172,7 @@ static void treat_gitlinks(const char **pathspec) /* strip trailing slash */ pathspec[j] = xstrndup(ce->name, len); else - die ("Path '%s' is in submodule '%.*s'", + die (_("Path '%s' is in submodule '%.*s'"), pathspec[j], len, ce->name); } } @@ -187,10 +188,10 @@ static void refresh(int verbose, const char **pathspec) /* nothing */; seen = xcalloc(specs, 1); refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET, - pathspec, seen, "Unstaged changes after refreshing the index:"); + pathspec, seen, _("Unstaged changes after refreshing the index:")); for (i = 0; i < specs; i++) { if (!seen[i]) - die("pathspec '%s' did not match any files", pathspec[i]); + die(_("pathspec '%s' did not match any files"), pathspec[i]); } free(seen); } @@ -204,7 +205,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p for (p = pathspec; *p; p++) { if (has_symlink_leading_path(*p, strlen(*p))) { int len = prefix ? strlen(prefix) : 0; - die("'%s' is beyond a symbolic link", *p + len); + die(_("'%s' is beyond a symbolic link"), *p + len); } } } @@ -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); } @@ -271,7 +272,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ if (read_cache() < 0) - die ("Could not read the index"); + die (_("Could not read the index")); init_revisions(&rev, prefix); rev.diffopt.context = 7; @@ -280,24 +281,24 @@ static int edit_patch(int argc, const char **argv, const char *prefix) rev.diffopt.output_format = DIFF_FORMAT_PATCH; out = open(file, O_CREAT | O_WRONLY, 0644); if (out < 0) - die ("Could not open '%s' for writing.", file); + die (_("Could not open '%s' for writing."), file); rev.diffopt.file = xfdopen(out, "w"); rev.diffopt.close_file = 1; if (run_diff_files(&rev, 0)) - die ("Could not write patch"); + die (_("Could not write patch")); launch_editor(file, NULL, NULL); if (stat(file, &st)) - die_errno("Could not stat '%s'", file); + die_errno(_("Could not stat '%s'"), file); if (!st.st_size) - die("Empty patch. Aborted."); + die(_("Empty patch. Aborted.")); memset(&child, 0, sizeof(child)); child.git_cmd = 1; child.argv = apply_argv; if (run_command(&child)) - die ("Could not apply '%s'", file); + die (_("Could not apply '%s'"), file); unlink(file); return 0; @@ -306,7 +307,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) static struct lock_file lock_file; static const char ignore_error[] = -"The following paths are ignored by one of your .gitignore files:\n"; +N_("The following paths are ignored by one of your .gitignore files:\n"); static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0; @@ -321,7 +322,7 @@ static struct option builtin_add_options[] = { OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"), - OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"), + OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"), @@ -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; } @@ -343,17 +344,17 @@ static int add_files(struct dir_struct *dir, int flags) int i, exit_status = 0; if (dir->ignored_nr) { - fprintf(stderr, ignore_error); + fprintf(stderr, _(ignore_error)); for (i = 0; i < dir->ignored_nr; i++) fprintf(stderr, "%s\n", dir->ignored[i]->name); - fprintf(stderr, "Use -f if you really want to add them.\n"); - die("no files added"); + fprintf(stderr, _("Use -f if you really want to add them.\n")); + die(_("no files added")); } for (i = 0; i < dir->nr; i++) if (add_file_to_cache(dir->entries[i]->name, flags)) { if (!ignore_add_errors) - die("adding files failed"); + die(_("adding files failed")); exit_status = 1; } return exit_status; @@ -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)); @@ -385,9 +386,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) argv++; if (addremove && take_worktree_changes) - die("-A and -u are mutually incompatible"); + die(_("-A and -u are mutually incompatible")); if (!show_only && ignore_missing) - die("Option --ignore-missing can only be used together with --dry-run"); + die(_("Option --ignore-missing can only be used together with --dry-run")); if ((addremove || take_worktree_changes) && !argc) { static const char *here[2] = { ".", NULL }; argc = 1; @@ -407,14 +408,14 @@ int cmd_add(int argc, const char **argv, const char *prefix) ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { - fprintf(stderr, "Nothing specified, nothing added.\n"); - fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); + fprintf(stderr, _("Nothing specified, nothing added.\n")); + fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } pathspec = validate_pathspec(argc, argv, prefix); if (read_cache() < 0) - die("index file corrupt"); + die(_("index file corrupt")); treat_gitlinks(pathspec); if (add_new_files) { @@ -450,7 +451,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (excluded(&dir, pathspec[i], &dtype)) dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); } else - die("pathspec '%s' did not match any files", + die(_("pathspec '%s' did not match any files"), pathspec[i]); } } @@ -466,7 +467,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) - die("Unable to write new index file"); + die(_("Unable to write new index file")); } return exit_status; diff --git a/builtin/apply.c b/builtin/apply.c index 36e150768b..f2edc52818 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'; @@ -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; } @@ -3889,6 +3890,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 6a887f5a9d..883c0092ad 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -14,17 +14,18 @@ static void create_output_file(const char *output_file) { int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (output_fd < 0) - die_errno("could not create archive file '%s'", output_file); + die_errno(_("could not create archive file '%s'"), output_file); if (output_fd != 1) { if (dup2(output_fd, 1) < 0) - die_errno("could not redirect output"); + die_errno(_("could not redirect output")); else close(output_fd); } } 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; @@ -33,28 +34,39 @@ static int run_remote_archiver(int argc, const char **argv, _remote = remote_get(remote); if (!_remote->url[0]) - die("git archive: Remote with no URL"); + die(_("git archive: Remote with no URL")); 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]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) - die("git archive: expected ACK/NAK, got EOF"); + die(_("git archive: expected ACK/NAK, got EOF")); if (buf[len-1] == '\n') buf[--len] = 0; if (strcmp(buf, "ACK")) { if (len > 5 && !prefixcmp(buf, "NACK ")) - die("git archive: NACK %s", buf + 5); - die("git archive: protocol error"); + die(_("git archive: NACK %s"), buf + 5); + die(_("git archive: protocol error")); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) - die("git archive: expected a flush"); + die(_("git archive: expected a flush")); /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1); @@ -63,17 +75,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 +85,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 +98,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/blame.c b/builtin/blame.c index f6b03f750a..26a5d424b8 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"); } @@ -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); } @@ -2299,6 +2312,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 +2324,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 +2360,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 b9ba011f7b..3142daa57a 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 }; @@ -133,12 +133,12 @@ static int branch_merged(int kind, const char *name, if ((head_rev != reference_rev) && in_merge_bases(rev, &head_rev, 1) != merged) { if (merged) - warning("deleting branch '%s' that has been merged to\n" - " '%s', but not yet been merged to HEAD.", + warning(_("deleting branch '%s' that has been merged to\n" + " '%s', but not yet merged to HEAD."), name, reference_name); else - warning("not deleting branch '%s' that is not yet merged to\n" - " '%s', even though it is merged to HEAD.", + warning(_("not deleting branch '%s' that is not yet merged to\n" + " '%s', even though it is merged to HEAD."), name, reference_name); } return merged; @@ -157,7 +157,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) switch (kinds) { case REF_REMOTE_BRANCH: fmt = "refs/remotes/%s"; - remote = "remote "; + /* TRANSLATORS: This is "remote " in "remote branch '%s' not found" */ + remote = _("remote "); force = 1; break; case REF_LOCAL_BRANCH: @@ -165,19 +166,19 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) remote = ""; break; default: - die("cannot use -a with -d"); + die(_("cannot use -a with -d")); } if (!force) { head_rev = lookup_commit_reference(head_sha1); if (!head_rev) - die("Couldn't look up commit object for HEAD"); + die(_("Couldn't look up commit object for HEAD")); } for (i = 0; i < argc; i++, strbuf_release(&bname)) { strbuf_branchname(&bname, argv[i]); if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { - error("Cannot delete the branch '%s' " - "which you are currently on.", bname.buf); + error(_("Cannot delete the branch '%s' " + "which you are currently on."), bname.buf); ret = 1; continue; } @@ -186,7 +187,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) name = xstrdup(mkpath(fmt, bname.buf)); if (!resolve_ref(name, sha1, 1, NULL)) { - error("%sbranch '%s' not found.", + error(_("%sbranch '%s' not found."), remote, bname.buf); ret = 1; continue; @@ -194,31 +195,31 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) rev = lookup_commit_reference(sha1); if (!rev) { - error("Couldn't look up commit object for '%s'", name); + error(_("Couldn't look up commit object for '%s'"), name); ret = 1; continue; } if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) { - error("The branch '%s' is not fully merged.\n" + error(_("The branch '%s' is not fully merged.\n" "If you are sure you want to delete it, " - "run 'git branch -D %s'.", bname.buf, bname.buf); + "run 'git branch -D %s'."), bname.buf, bname.buf); ret = 1; continue; } if (delete_ref(name, sha1, 0)) { - error("Error deleting %sbranch '%s'", remote, + error(_("Error deleting %sbranch '%s'"), remote, bname.buf); ret = 1; } else { struct strbuf buf = STRBUF_INIT; - printf("Deleted %sbranch %s (was %s).\n", remote, + printf(_("Deleted %sbranch %s (was %s).\n"), remote, bname.buf, find_unique_abbrev(sha1, DEFAULT_ABBREV)); strbuf_addf(&buf, "branch.%s", bname.buf); if (git_config_rename_section(buf.buf, NULL) < 0) - warning("Update of config-file failed"); + warning(_("Update of config-file failed")); strbuf_release(&buf); } } @@ -300,7 +301,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) { commit = lookup_commit_reference_gently(sha1, 1); if (!commit) { - cb->ret = error("branch '%s' does not point at a commit", refname); + cb->ret = error(_("branch '%s' does not point at a commit"), refname); return 0; } @@ -372,11 +373,11 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name, strbuf_addf(stat, "%s: ", shorten_unambiguous_ref(branch->merge[0]->dst, 0)); if (!ours) - strbuf_addf(stat, "behind %d] ", theirs); + strbuf_addf(stat, _("behind %d] "), theirs); else if (!theirs) - strbuf_addf(stat, "ahead %d] ", ours); + strbuf_addf(stat, _("ahead %d] "), ours); else - strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs); + strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs); } static int matches_merge_filter(struct commit *commit) @@ -398,9 +399,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; } @@ -481,7 +480,7 @@ static void show_detached(struct ref_list *ref_list) if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) { struct ref_item item; - item.name = xstrdup("(no branch)"); + item.name = xstrdup(_("(no branch)")); item.len = strlen(item.name); item.kind = REF_LOCAL_BRANCH; item.dest = NULL; @@ -541,7 +540,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru free_ref_list(&ref_list); if (cb.ret) - error("some refs could not be read"); + error(_("some refs could not be read")); return cb.ret; } @@ -554,7 +553,7 @@ static void rename_branch(const char *oldname, const char *newname, int force) int recovery = 0; if (!oldname) - die("cannot rename the current branch while not on any."); + die(_("cannot rename the current branch while not on any.")); if (strbuf_check_branch_ref(&oldref, oldname)) { /* @@ -564,35 +563,35 @@ static void rename_branch(const char *oldname, const char *newname, int force) if (resolve_ref(oldref.buf, sha1, 1, NULL)) recovery = 1; else - die("Invalid branch name: '%s'", oldname); + die(_("Invalid branch name: '%s'"), oldname); } if (strbuf_check_branch_ref(&newref, newname)) - die("Invalid branch name: '%s'", newname); + die(_("Invalid branch name: '%s'"), newname); if (resolve_ref(newref.buf, sha1, 1, NULL) && !force) - die("A branch named '%s' already exists.", newref.buf + 11); + die(_("A branch named '%s' already exists."), newref.buf + 11); strbuf_addf(&logmsg, "Branch: renamed %s to %s", oldref.buf, newref.buf); if (rename_ref(oldref.buf, newref.buf, logmsg.buf)) - die("Branch rename failed"); + die(_("Branch rename failed")); strbuf_release(&logmsg); if (recovery) - warning("Renamed a misnamed branch '%s' away", oldref.buf + 11); + warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11); /* no need to pass logmsg here as HEAD didn't really move */ if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL)) - die("Branch renamed to %s, but HEAD is not updated!", newname); + die(_("Branch renamed to %s, but HEAD is not updated!"), newname); strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11); strbuf_release(&oldref); strbuf_addf(&newsection, "branch.%s", newref.buf + 11); strbuf_release(&newref); if (git_config_rename_section(oldsection.buf, newsection.buf) < 0) - die("Branch is renamed, but update of config-file failed"); + die(_("Branch is renamed, but update of config-file failed")); strbuf_release(&oldsection); strbuf_release(&newsection); } @@ -607,7 +606,7 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int if (!arg) arg = "HEAD"; if (get_sha1(arg, merge_filter_ref)) - die("malformed object name %s", arg); + die(_("malformed object name %s"), arg); return 0; } @@ -681,13 +680,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) head = resolve_ref("HEAD", head_sha1, 0, NULL); if (!head) - die("Failed to resolve HEAD as a valid ref."); + die(_("Failed to resolve HEAD as a valid ref.")); head = xstrdup(head); if (!strcmp(head, "HEAD")) { detached = 1; } else { if (prefixcmp(head, "refs/heads/")) - die("HEAD not found below refs/heads!"); + die(_("HEAD not found below refs/heads!")); head += 11; } hashcpy(merge_filter_ref, head_sha1); @@ -707,7 +706,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) rename_branch(argv[0], argv[1], rename > 1); else if (argc <= 2) { if (kinds != REF_LOCAL_BRANCH) - die("-a and -r options to 'git branch' do not make sense with a branch name"); + 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); } else diff --git a/builtin/bundle.c b/builtin/bundle.c index 9b87fb9ac2..81046a9cb8 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -44,7 +44,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix) close(bundle_fd); if (verify_bundle(&header, 1)) return 1; - fprintf(stderr, "%s is okay\n", bundle_file); + fprintf(stderr, _("%s is okay\n"), bundle_file); return 0; } if (!strcmp(cmd, "list-heads")) { @@ -53,11 +53,11 @@ int cmd_bundle(int argc, const char **argv, const char *prefix) } if (!strcmp(cmd, "create")) { if (!startup_info->have_repository) - die("Need a repository to create a bundle."); + die(_("Need a repository to create a bundle.")); return !!create_bundle(&header, bundle_file, argc, argv); } else if (!strcmp(cmd, "unbundle")) { if (!startup_info->have_repository) - die("Need a repository to unbundle."); + die(_("Need a repository to unbundle.")); return !!unbundle(&header, bundle_fd) || list_bundle_refs(&header, argc, argv); } else 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/checkout.c b/builtin/checkout.c index e98576f22e..d647a31303 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -30,6 +30,7 @@ struct checkout_opts { int quiet; int merge; int force; + int force_detach; int writeout_stage; int writeout_error; @@ -78,7 +79,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 @@ -103,9 +107,10 @@ static int check_stage(int stage, struct cache_entry *ce, int pos) return 0; pos++; } - return error("path '%s' does not have %s version", - ce->name, - (stage == 2) ? "our" : "their"); + if (stage == 2) + return error(_("path '%s' does not have our version"), ce->name); + else + return error(_("path '%s' does not have their version"), ce->name); } static int check_all_stages(struct cache_entry *ce, int pos) @@ -116,7 +121,7 @@ static int check_all_stages(struct cache_entry *ce, int pos) ce_stage(active_cache[pos+1]) != 2 || strcmp(active_cache[pos+2]->name, ce->name) || ce_stage(active_cache[pos+2]) != 3) - return error("path '%s' does not have all three versions", + return error(_("path '%s' does not have all three versions"), ce->name); return 0; } @@ -130,9 +135,10 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos, return checkout_entry(active_cache[pos], state, NULL); pos++; } - return error("path '%s' does not have %s version", - ce->name, - (stage == 2) ? "our" : "their"); + if (stage == 2) + return error(_("path '%s' does not have our version"), ce->name); + else + return error(_("path '%s' does not have their version"), ce->name); } static int checkout_merged(int pos, struct checkout *state) @@ -150,7 +156,7 @@ static int checkout_merged(int pos, struct checkout *state) ce_stage(active_cache[pos+1]) != 2 || strcmp(active_cache[pos+2]->name, path) || ce_stage(active_cache[pos+2]) != 3) - return error("path '%s' does not have all 3 versions", path); + return error(_("path '%s' does not have all 3 versions"), path); read_mmblob(&ancestor, active_cache[pos]->sha1); read_mmblob(&ours, active_cache[pos+1]->sha1); @@ -167,7 +173,7 @@ static int checkout_merged(int pos, struct checkout *state) free(theirs.ptr); if (status < 0 || !result_buf.ptr) { free(result_buf.ptr); - return error("path '%s': cannot merge", path); + return error(_("path '%s': cannot merge"), path); } /* @@ -184,12 +190,12 @@ static int checkout_merged(int pos, struct checkout *state) */ if (write_sha1_file(result_buf.ptr, result_buf.size, blob_type, sha1)) - die("Unable to add merge result for '%s'", path); + die(_("Unable to add merge result for '%s'"), path); ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode), sha1, path, 2, 0); if (!ce) - die("make_cache_entry failed for path '%s'", path); + die(_("make_cache_entry failed for path '%s'"), path); status = checkout_entry(ce, state, NULL); return status; } @@ -211,7 +217,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, newfd = hold_locked_index(lock_file, 1); if (read_cache_preload(pathspec) < 0) - return error("corrupt index file"); + return error(_("corrupt index file")); if (source_tree) read_tree_some(source_tree, pathspec); @@ -239,14 +245,14 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, if (!ce_stage(ce)) continue; if (opts->force) { - warning("path '%s' is unmerged", ce->name); + warning(_("path '%s' is unmerged"), ce->name); } else if (stage) { errs |= check_stage(stage, ce, pos); } else if (opts->merge) { errs |= check_all_stages(ce, pos); } else { errs = 1; - error("path '%s' is unmerged", ce->name); + error(_("path '%s' is unmerged"), ce->name); } pos = skip_same_name(ce, pos) - 1; } @@ -275,7 +281,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock_file)) - die("unable to write new index file"); + die(_("unable to write new index file")); resolve_ref("HEAD", rev, 0, &flag); head = lookup_commit_reference_gently(rev, 1); @@ -292,17 +298,16 @@ static void show_local_changes(struct object *head, struct diff_options *opts) rev.diffopt.flags = opts->flags; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; if (diff_setup_done(&rev.diffopt) < 0) - die("diff_setup_done failed"); + die(_("diff_setup_done failed")); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); } -static void describe_detached_head(char *msg, struct commit *commit) +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); @@ -366,7 +371,7 @@ static int merge_working_tree(struct checkout_opts *opts, int newfd = hold_locked_index(lock_file, 1); if (read_cache_preload(NULL) < 0) - return error("corrupt index file"); + return error(_("corrupt index file")); resolve_undo_clear(); if (opts->force) { @@ -388,7 +393,7 @@ static int merge_working_tree(struct checkout_opts *opts, refresh_cache(REFRESH_QUIET); if (unmerged_cache()) { - error("you need to resolve your current index first"); + error(_("you need to resolve your current index first")); return 1; } @@ -470,7 +475,7 @@ static int merge_working_tree(struct checkout_opts *opts, if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock_file)) - die("unable to write new index file"); + die(_("unable to write new index file")); if (!opts->force && !opts->quiet) show_local_changes(&new->commit->object, &opts->diff_options); @@ -519,7 +524,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, 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", + fprintf(stderr, _("Can not do reflog for '%s'\n"), opts->new_orphan_branch); log_all_ref_updates = temp; return; @@ -541,19 +546,31 @@ static void update_refs_for_switch(struct checkout_opts *opts, strbuf_addf(&msg, "checkout: moving from %s to %s", old_desc ? old_desc : "(invalid)", new->name); - if (new->path) { + if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) { + /* Nothing to do. */ + } else if (opts->force_detach || !new->path) { /* No longer on any branch. */ + update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL, + REF_NODEREF, DIE_ON_ERR); + if (!opts->quiet) { + if (old->path && advice_detached_head) + detach_advice(old->path, new->name); + describe_detached_head(_("HEAD is now at"), new->commit); + } + } else if (new->path) { /* Switch branches. */ create_symref("HEAD", new->path, msg.buf); if (!opts->quiet) { - if (old->path && !strcmp(new->path, old->path)) - fprintf(stderr, "Already on '%s'\n", - new->name); - else if (opts->new_branch) - fprintf(stderr, "Switched to%s branch '%s'\n", - opts->branch_exists ? " and reset" : " a new", + if (old->path && !strcmp(new->path, old->path)) { + fprintf(stderr, _("Already on '%s'\n"), new->name); - else - fprintf(stderr, "Switched to branch '%s'\n", + } else if (opts->new_branch) { + if (opts->branch_exists) + fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name); + else + fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name); + } else { + fprintf(stderr, _("Switched to branch '%s'\n"), new->name); + } } if (old->path && old->name) { char log_file[PATH_MAX], ref_file[PATH_MAX]; @@ -563,21 +580,135 @@ static void update_refs_for_switch(struct checkout_opts *opts, 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); - if (!opts->quiet) { - if (old->path && advice_detached_head) - detach_advice(old->path, new->name); - describe_detached_head("HEAD is now at", new->commit); - } } remove_branch_state(); strbuf_release(&msg); - if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD"))) + if (!opts->quiet && + (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD")))) 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) +{ + 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); + return 0; +} + +static void describe_one_orphan(struct strbuf *sb, struct commit *commit) +{ + parse_commit(commit); + strbuf_addstr(sb, " "); + strbuf_addstr(sb, + find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); + strbuf_addch(sb, ' '); + pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); + strbuf_addch(sb, '\n'); +} + +#define ORPHAN_CUTOFF 4 +static void suggest_reattach(struct commit *commit, struct rev_info *revs) +{ + struct commit *c, *last = NULL; + struct strbuf sb = STRBUF_INIT; + int lost = 0; + while ((c = get_revision(revs)) != NULL) { + if (lost < ORPHAN_CUTOFF) + describe_one_orphan(&sb, c); + last = c; + lost++; + } + if (ORPHAN_CUTOFF < lost) { + int more = lost - ORPHAN_CUTOFF; + if (more == 1) + describe_one_orphan(&sb, last); + else + strbuf_addf(&sb, _(" ... and %d more.\n"), more); + } + + fprintf(stderr, + Q_( + /* The singular version */ + "Warning: you are leaving %d commit behind, " + "not connected to\n" + "any of your branches:\n\n" + "%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)); +} + +/* + * We are about to leave commit that was at the tip of a detached + * HEAD. If it is not reachable from any ref, this is the last chance + * for the user to do so without resorting to reflog. + */ +static void orphaned_commit_warning(struct commit *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); + + init_revisions(&revs, NULL); + if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1) + die(_("internal error: only -- alone should have been left")); + if (prepare_revision_walk(&revs)) + die(_("internal error in revision walk")); + if (!(commit->object.flags & UNINTERESTING)) + suggest_reattach(commit, &revs); + else + describe_detached_head(_("Previous HEAD position was"), commit); + + clear_commit_marks(commit, -1); + for_each_ref(clear_commit_marks_from_one_ref, NULL); +} + static int switch_branches(struct checkout_opts *opts, struct branch_info *new) { int ret = 0; @@ -585,10 +716,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/"); @@ -597,7 +730,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) new->name = "HEAD"; new->commit = old.commit; if (!new->commit) - die("You are on a branch yet to be born"); + die(_("You are on a branch yet to be born")); parse_commit(new->commit); } @@ -605,17 +738,13 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) if (ret) return ret; - /* - * If we were on a detached HEAD, but have now moved to - * a new commit, we want to mention the old commit once more - * to remind the user that it might be lost. - */ if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) - describe_detached_head("Previous HEAD position was", old.commit); + orphaned_commit_warning(old.commit); 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; } @@ -675,11 +804,123 @@ static const char *unique_tracking_name(const char *name) return NULL; } +static int parse_branchname_arg(int argc, const char **argv, + int dwim_new_local_branch_ok, + struct branch_info *new, + struct tree **source_tree, + unsigned char rev[20], + const char **new_branch) +{ + int argcount = 0; + unsigned char branch_rev[20]; + const char *arg; + int has_dash_dash; + + /* + * case 1: git checkout <ref> -- [<paths>] + * + * <ref> must be a valid tree, everything after the '--' must be + * a path. + * + * case 2: git checkout -- [<paths>] + * + * everything after the '--' must be paths. + * + * case 3: git checkout <something> [<paths>] + * + * With no paths, if <something> is a commit, that is to + * switch to the branch or detach HEAD at it. As a special case, + * if <something> is A...B (missing A or B means HEAD but you can + * omit at most one side), and if there is a unique merge base + * between A and B, A...B names that merge base. + * + * With no paths, if <something> is _not_ a commit, no -t nor -b + * was given, and there is a tracking branch whose name is + * <something> in one and only one remote, then this is a short-hand + * to fork local <something> from that remote-tracking branch. + * + * Otherwise <something> shall not be ambiguous. + * - If it's *only* a reference, treat it like case (1). + * - If it's only a path, treat it like case (2). + * - else: fail. + * + */ + if (!argc) + return 0; + + if (!strcmp(argv[0], "--")) /* case (2) */ + return 1; + + arg = argv[0]; + has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + + if (!strcmp(arg, "-")) + arg = "@{-1}"; + + if (get_sha1_mb(arg, rev)) { + if (has_dash_dash) /* case (1) */ + die(_("invalid reference: %s"), arg); + if (dwim_new_local_branch_ok && + !check_filename(NULL, arg) && + argc == 1) { + const char *remote = unique_tracking_name(arg); + if (!remote || get_sha1(remote, rev)) + return argcount; + *new_branch = arg; + arg = remote; + /* DWIMmed to create local branch */ + } else { + return argcount; + } + } + + /* we can't end up being in (2) anymore, eat the argument */ + argcount++; + argv++; + argc--; + + new->name = arg; + setup_branch_path(new); + + if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK && + resolve_ref(new->path, branch_rev, 1, NULL)) + hashcpy(rev, branch_rev); + else + new->path = NULL; /* not an existing branch */ + + new->commit = lookup_commit_reference_gently(rev, 1); + if (!new->commit) { + /* not a commit */ + *source_tree = parse_tree_indirect(rev); + } else { + parse_commit(new->commit); + *source_tree = new->commit->tree; + } + + if (!*source_tree) /* case (1): want a tree */ + die(_("reference is not a tree: %s"), arg); + if (!has_dash_dash) {/* case (3 -> 1) */ + /* + * Do not complain the most common case + * git checkout branch + * even if there happen to be a file called 'branch'; + * it would be extremely annoying. + */ + if (argc) + verify_non_filename(NULL, arg); + } else { + argcount++; + argv++; + argc--; + } + + return argcount; +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; - unsigned char rev[20], branch_rev[20]; - const char *arg; + unsigned char rev[20]; struct branch_info new; struct tree *source_tree = NULL; char *conflict_style = NULL; @@ -692,6 +933,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_STRING('B', NULL, &opts.new_branch_force, "branch", "create/reset and checkout a branch"), OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"), + OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"), OPT_SET_INT('t', "track", &opts.track, "set upstream info for new branch", BRANCH_TRACK_EXPLICIT), OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"), @@ -709,7 +951,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END(), }; - int has_dash_dash; memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); @@ -724,36 +965,42 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) /* we can assume from now on new_branch = !new_branch_force */ if (opts.new_branch && opts.new_branch_force) - die("-B cannot be used with -b"); + die(_("-B cannot be used with -b")); /* copy -B over to -b, so that we can just check the latter */ if (opts.new_branch_force) opts.new_branch = opts.new_branch_force; if (patch_mode && (opts.track > 0 || opts.new_branch - || opts.new_branch_log || opts.merge || opts.force)) - die ("--patch is incompatible with all other options"); + || opts.new_branch_log || opts.merge || opts.force + || opts.force_detach)) + 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")); + if (opts.force_detach && 0 < opts.track) + die(_("--detach cannot be used with -t")); /* --track without -b should DWIM */ if (0 < opts.track && !opts.new_branch) { const char *argv0 = argv[0]; if (!argc || !strcmp(argv0, "--")) - die ("--track needs a branch name"); + die (_("--track needs a branch name")); if (!prefixcmp(argv0, "refs/")) argv0 += 5; if (!prefixcmp(argv0, "remotes/")) argv0 += 8; argv0 = strchr(argv0, '/'); if (!argv0 || !argv0[1]) - die ("Missing branch name; try -b"); + die (_("Missing branch name; try -b")); opts.new_branch = argv0 + 1; } if (opts.new_orphan_branch) { if (opts.new_branch) - die("--orphan and -b|-B are mutually exclusive"); + die(_("--orphan and -b|-B are mutually exclusive")); if (opts.track > 0) - die("--orphan cannot be used with -t"); + die(_("--orphan cannot be used with -t")); opts.new_branch = opts.new_orphan_branch; } @@ -763,111 +1010,33 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) } if (opts.force && opts.merge) - die("git checkout: -f and -m are incompatible"); + die(_("git checkout: -f and -m are incompatible")); /* - * case 1: git checkout <ref> -- [<paths>] + * Extract branch name from command line arguments, so + * all that is left is pathspecs. * - * <ref> must be a valid tree, everything after the '--' must be - * a path. - * - * case 2: git checkout -- [<paths>] + * Handle * - * everything after the '--' must be paths. - * - * case 3: git checkout <something> [<paths>] - * - * With no paths, if <something> is a commit, that is to - * switch to the branch or detach HEAD at it. As a special case, - * if <something> is A...B (missing A or B means HEAD but you can - * omit at most one side), and if there is a unique merge base - * between A and B, A...B names that merge base. - * - * With no paths, if <something> is _not_ a commit, no -t nor -b - * was given, and there is a remote-tracking branch whose name is - * <something> in one and only one remote, then this is a short-hand - * to fork local <something> from that remote-tracking branch. - * - * Otherwise <something> shall not be ambiguous. - * - If it's *only* a reference, treat it like case (1). - * - If it's only a path, treat it like case (2). - * - else: fail. + * 1) git checkout <tree> -- [<paths>] + * 2) git checkout -- [<paths>] + * 3) git checkout <something> [<paths>] * + * including "last branch" syntax and DWIM-ery for names of + * remote branches, erroring out for invalid or ambiguous cases. */ if (argc) { - if (!strcmp(argv[0], "--")) { /* case (2) */ - argv++; - argc--; - goto no_reference; - } - - arg = argv[0]; - has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); - - if (!strcmp(arg, "-")) - arg = "@{-1}"; - - if (get_sha1_mb(arg, rev)) { - if (has_dash_dash) /* case (1) */ - die("invalid reference: %s", arg); - if (!patch_mode && - dwim_new_local_branch && - opts.track == BRANCH_TRACK_UNSPECIFIED && - !opts.new_branch && - !check_filename(NULL, arg) && - argc == 1) { - const char *remote = unique_tracking_name(arg); - if (!remote || get_sha1(remote, rev)) - goto no_reference; - opts.new_branch = arg; - arg = remote; - /* DWIMmed to create local branch */ - } - else - goto no_reference; - } - - /* we can't end up being in (2) anymore, eat the argument */ - argv++; - argc--; - - new.name = arg; - setup_branch_path(&new); - - if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK && - resolve_ref(new.path, branch_rev, 1, NULL)) - hashcpy(rev, branch_rev); - else - new.path = NULL; /* not an existing branch */ - - if (!(new.commit = lookup_commit_reference_gently(rev, 1))) { - /* not a commit */ - source_tree = parse_tree_indirect(rev); - } else { - parse_commit(new.commit); - source_tree = new.commit->tree; - } - - if (!source_tree) /* case (1): want a tree */ - die("reference is not a tree: %s", arg); - if (!has_dash_dash) {/* case (3 -> 1) */ - /* - * Do not complain the most common case - * git checkout branch - * even if there happen to be a file called 'branch'; - * it would be extremely annoying. - */ - if (argc) - verify_non_filename(NULL, arg); - } - else { - argv++; - argc--; - } + int dwim_ok = + !patch_mode && + dwim_new_local_branch && + opts.track == BRANCH_TRACK_UNSPECIFIED && + !opts.new_branch; + int n = parse_branchname_arg(argc, argv, dwim_ok, + &new, &source_tree, rev, &opts.new_branch); + argv += n; + argc -= n; } -no_reference: - if (opts.track == BRANCH_TRACK_UNSPECIFIED) opts.track = git_branch_track; @@ -875,7 +1044,7 @@ no_reference: const char **pathspec = get_pathspec(prefix, argv); if (!pathspec) - die("invalid path specification"); + die(_("invalid path specification")); if (patch_mode) return interactive_checkout(new.name, pathspec, &opts); @@ -883,14 +1052,17 @@ no_reference: /* Checkout paths */ if (opts.new_branch) { if (argc == 1) { - die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); + die(_("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?"), argv[0]); } else { - die("git checkout: updating paths is incompatible with switching branches."); + die(_("git checkout: updating paths is incompatible with switching branches.")); } } + if (opts.force_detach) + 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."); + die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.")); return checkout_paths(source_tree, pathspec, &opts); } @@ -901,22 +1073,22 @@ no_reference: 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.", + die(_("git checkout: we do not like '%s' as a branch name."), opts.new_branch); - if (!get_sha1(buf.buf, rev)) { + if (ref_exists(buf.buf)) { opts.branch_exists = 1; if (!opts.new_branch_force) - die("git checkout: branch %s already exists", + die(_("git checkout: branch %s already exists"), opts.new_branch); } strbuf_release(&buf); } if (new.name && !new.commit) { - die("Cannot switch branch to a non-commit."); + die(_("Cannot switch branch to a non-commit.")); } if (opts.writeout_stage) - die("--ours/--theirs is incompatible with switching branches."); + die(_("--ours/--theirs is incompatible with switching branches.")); return switch_branches(&opts, &new); } diff --git a/builtin/clean.c b/builtin/clean.c index 4a312abc6b..75697f7111 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -75,11 +75,16 @@ int cmd_clean(int argc, const char **argv, const char *prefix) dir.flags |= DIR_SHOW_IGNORED; if (ignored && ignored_only) - die("-x and -X cannot be used together"); - - if (!show_only && !force) - die("clean.requireForce %s to true and neither -n nor -f given; " - "refusing to clean", config_set ? "set" : "defaults"); + die(_("-x and -X cannot be used together")); + + if (!show_only && !force) { + if (config_set) + die(_("clean.requireForce set to true and neither -n nor -f given; " + "refusing to clean")); + else + die(_("clean.requireForce defaults to true and neither -n nor -f given; " + "refusing to clean")); + } if (force > 1) rm_flags = 0; @@ -87,7 +92,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (read_cache() < 0) - die("index file corrupt"); + die(_("index file corrupt")); if (!ignored) setup_standard_excludes(&dir); @@ -146,20 +151,20 @@ int cmd_clean(int argc, const char **argv, const char *prefix) qname = quote_path_relative(directory.buf, directory.len, &buf, prefix); if (show_only && (remove_directories || (matches == MATCHED_EXACTLY))) { - printf("Would remove %s\n", qname); + printf(_("Would remove %s\n"), qname); } else if (remove_directories || (matches == MATCHED_EXACTLY)) { if (!quiet) - printf("Removing %s\n", qname); + printf(_("Removing %s\n"), qname); if (remove_dir_recursively(&directory, rm_flags) != 0) { - warning("failed to remove %s", qname); + warning(_("failed to remove %s"), qname); errors++; } } else if (show_only) { - printf("Would not remove %s\n", qname); + printf(_("Would not remove %s\n"), qname); } else { - printf("Not removing %s\n", qname); + printf(_("Not removing %s\n"), qname); } strbuf_reset(&directory); } else { @@ -167,13 +172,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix) continue; qname = quote_path_relative(ent->name, -1, &buf, prefix); if (show_only) { - printf("Would remove %s\n", qname); + printf(_("Would remove %s\n"), qname); continue; } else if (!quiet) { - printf("Removing %s\n", qname); + printf(_("Removing %s\n"), qname); } if (unlink(ent->name) != 0) { - warning("failed to remove %s", qname); + warning(_("failed to remove %s"), qname); errors++; } } diff --git a/builtin/clone.c b/builtin/clone.c index 20496bdad0..7663bc22c9 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -8,7 +8,7 @@ * Clone a repository into a different directory that does not yet exist. */ -#include "cache.h" +#include "builtin.h" #include "parse-options.h" #include "fetch-pack.h" #include "refs.h" @@ -42,9 +42,11 @@ static int option_local, option_no_hardlinks, option_shared, option_recursive; static char *option_template, *option_reference, *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 option builtin_clone_options[] = { OPT__VERBOSITY(&option_verbosity), @@ -80,7 +82,10 @@ static struct option builtin_clone_options[] = { "path to git-upload-pack on the remote"), OPT_STRING(0, "depth", &option_depth, "depth", "create a shallow clone of that depth"), - + 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() }; @@ -100,7 +105,7 @@ static char *get_repo_path(const char *repo, int *is_bundle) path = mkpath("%s%s", repo, suffix[i]); if (is_directory(path)) { *is_bundle = 0; - return xstrdup(make_nonrelative_path(path)); + return xstrdup(absolute_path(path)); } } @@ -109,7 +114,7 @@ static char *get_repo_path(const char *repo, int *is_bundle) path = mkpath("%s%s", repo, bundle_suffix[i]); if (!stat(path, &st) && S_ISREG(st.st_mode)) { *is_bundle = 1; - return xstrdup(make_nonrelative_path(path)); + return xstrdup(absolute_path(path)); } } @@ -203,12 +208,12 @@ static void setup_reference(const char *repo) struct transport *transport; const struct ref *extra; - ref_git = make_absolute_path(option_reference); + 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))) - die("reference repository '%s' is not a local directory.", + die(_("reference repository '%s' is not a local directory."), option_reference); ref_git_copy = xstrdup(ref_git); @@ -235,15 +240,15 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) dir = opendir(src->buf); if (!dir) - die_errno("failed to open '%s'", src->buf); + die_errno(_("failed to open '%s'"), src->buf); if (mkdir(dest->buf, 0777)) { if (errno != EEXIST) - die_errno("failed to create directory '%s'", dest->buf); + die_errno(_("failed to create directory '%s'"), dest->buf); else if (stat(dest->buf, &buf)) - die_errno("failed to stat '%s'", dest->buf); + die_errno(_("failed to stat '%s'"), dest->buf); else if (!S_ISDIR(buf.st_mode)) - die("%s exists and is not a directory", dest->buf); + die(_("%s exists and is not a directory"), dest->buf); } strbuf_addch(src, '/'); @@ -257,7 +262,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) strbuf_setlen(dest, dest_len); strbuf_addstr(dest, de->d_name); if (stat(src->buf, &buf)) { - warning ("failed to stat %s\n", src->buf); + warning (_("failed to stat %s\n"), src->buf); continue; } if (S_ISDIR(buf.st_mode)) { @@ -267,16 +272,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) } if (unlink(dest->buf) && errno != ENOENT) - die_errno("failed to unlink '%s'", dest->buf); + die_errno(_("failed to unlink '%s'"), dest->buf); if (!option_no_hardlinks) { if (!link(src->buf, dest->buf)) continue; if (option_local) - die_errno("failed to create link '%s'", dest->buf); + die_errno(_("failed to create link '%s'"), dest->buf); option_no_hardlinks = 1; } if (copy_file_with_time(dest->buf, src->buf, 0666)) - die_errno("failed to copy file to '%s'", dest->buf); + die_errno(_("failed to copy file to '%s'"), dest->buf); } closedir(dir); } @@ -305,7 +310,7 @@ static const struct ref *clone_local(const char *src_repo, ret = transport_get_remote_refs(transport); transport_disconnect(transport); if (0 <= option_verbosity) - printf("done.\n"); + printf(_("done.\n")); return ret; } @@ -365,6 +370,22 @@ static void write_remote_refs(const struct ref *local_refs) 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; @@ -387,15 +408,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix) junk_pid = getpid(); + packet_trace_identity("clone"); argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); if (argc > 2) - usage_msg_opt("Too many arguments.", + usage_msg_opt(_("Too many arguments."), builtin_clone_usage, builtin_clone_options); if (argc == 0) - usage_msg_opt("You must specify a repository to clone.", + usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); if (option_mirror) @@ -403,7 +425,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_bare) { if (option_origin) - die("--bare and --origin %s options are incompatible.", + die(_("--bare and --origin %s options are incompatible."), option_origin); option_no_checkout = 1; } @@ -415,14 +437,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix) path = get_repo_path(repo_name, &is_bundle); if (path) - repo = xstrdup(make_nonrelative_path(repo_name)); + 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; if (is_local && option_depth) - warning("--depth is ignored in local clones; use file:// instead."); + warning(_("--depth is ignored in local clones; use file:// instead.")); if (argc == 2) dir = xstrdup(argv[1]); @@ -432,8 +454,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) dest_exists = !stat(dir, &buf); if (dest_exists && !is_empty_dir(dir)) - die("destination path '%s' already exists and is not " - "an empty directory.", dir); + die(_("destination path '%s' already exists and is not " + "an empty directory."), dir); strbuf_addf(&reflog_msg, "clone: from %s", repo); @@ -442,7 +464,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) else { work_tree = getenv("GIT_WORK_TREE"); if (work_tree && !stat(work_tree, &buf)) - die("working tree '%s' already exists.", work_tree); + die(_("working tree '%s' already exists."), work_tree); } if (option_bare || work_tree) @@ -455,10 +477,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (!option_bare) { junk_work_tree = work_tree; if (safe_create_leading_directories_const(work_tree) < 0) - die_errno("could not create leading directories of '%s'", + die_errno(_("could not create leading directories of '%s'"), work_tree); if (!dest_exists && mkdir(work_tree, 0755)) - die_errno("could not create work tree dir '%s'.", + die_errno(_("could not create work tree dir '%s'."), work_tree); set_git_work_tree(work_tree); } @@ -469,13 +491,20 @@ int cmd_clone(int argc, const char **argv, const char *prefix) setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1); if (safe_create_leading_directories_const(git_dir) < 0) - die("could not create leading directories of '%s'", git_dir); - set_git_dir(make_absolute_path(git_dir)); + die(_("could not create leading directories of '%s'"), git_dir); - if (0 <= option_verbosity) - printf("Cloning into %s%s...\n", - option_bare ? "bare repository " : "", dir); + set_git_dir_init(git_dir, real_git_dir, 0); + if (real_git_dir) + git_dir = real_git_dir; + + if (0 <= option_verbosity) { + if (option_bare) + printf(_("Cloning into bare repository %s...\n"), dir); + else + 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 @@ -531,7 +560,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport = transport_get(remote, remote->url[0]); if (!transport->get_refs_list || !transport->fetch) - die("Don't know how to clone %s", transport->url); + die(_("Don't know how to clone %s"), transport->url); transport_set_option(transport, TRANS_OPT_KEEP, "yes"); @@ -570,8 +599,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_release(&head); if (!our_head_points_at) { - warning("Remote branch %s not found in " - "upstream %s, using HEAD instead", + warning(_("Remote branch %s not found in " + "upstream %s, using HEAD instead"), option_branch, option_origin); our_head_points_at = remote_head_points_at; } @@ -580,7 +609,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) our_head_points_at = remote_head_points_at; } else { - warning("You appear to have cloned an empty repository."); + warning(_("You appear to have cloned an empty repository.")); our_head_points_at = NULL; remote_head_points_at = NULL; remote_head = NULL; @@ -622,8 +651,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } else { /* Nothing to checkout out */ if (!option_no_checkout) - warning("remote HEAD refers to nonexistent ref, " - "unable to checkout.\n"); + warning(_("remote HEAD refers to nonexistent ref, " + "unable to checkout.\n")); option_no_checkout = 1; } @@ -659,7 +688,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (write_cache(fd, active_cache, active_nr) || commit_locked_index(lock_file)) - die("unable to write new index file"); + die(_("unable to write new index file")); err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1), sha1_to_hex(our_head_points_at->old_sha1), "1", diff --git a/builtin/commit.c b/builtin/commit.c index 6e32166a29..e1af9b19f0 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -38,7 +38,7 @@ static const char * const builtin_status_usage[] = { }; static const char implicit_ident_advice[] = -"Your name and email address were configured automatically based\n" +N_("Your name and email address were configured automatically based\n" "on your username and hostname. Please check that they are accurate.\n" "You can suppress this message by setting them explicitly:\n" "\n" @@ -47,16 +47,24 @@ static const char implicit_ident_advice[] = "\n" "After doing this, you may fix the identity used for this commit with:\n" "\n" -" git commit --amend --reset-author\n"; +" git commit --amend --reset-author\n"); static const char empty_amend_advice[] = -"You asked to amend the most recent commit, but doing so would make\n" +N_("You asked to amend the most recent commit, but doing so would make\n" "it empty. You can repeat your command with --allow-empty, or you can\n" -"remove the commit entirely with \"git reset HEAD^\".\n"; +"remove the commit entirely with \"git reset HEAD^\".\n"); + +static const char empty_cherry_pick_advice[] = +N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\n" +"If you wish to commit it anyway, use:\n" +"\n" +" git commit --allow-empty\n" +"\n" +"Otherwise, please use 'git reset'\n"); static unsigned char head_sha1[20]; -static char *use_message_buffer; +static const char *use_message_buffer; static const char commit_editmsg[] = "COMMIT_EDITMSG"; static struct lock_file index_lock; /* real index */ static struct lock_file false_lock; /* used only for partial commits */ @@ -68,9 +76,14 @@ static enum { static const char *logfile, *force_author; static const char *template_file; +/* + * The _message variables are commit names from which to take + * the commit message and/or authorship. + */ +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; @@ -88,7 +101,8 @@ static enum { } cleanup_mode; static char *cleanup_arg; -static int use_editor = 1, initial_commit, in_merge, include_status = 1; +static enum commit_whence whence; +static int use_editor = 1, initial_commit, include_status = 1; static int show_ignored_in_status; static const char *only_include_assumed; static struct strbuf message; @@ -138,6 +152,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"), @@ -163,6 +178,36 @@ static struct option builtin_commit_options[] = { OPT_END() }; +static void determine_whence(struct wt_status *s) +{ + if (file_exists(git_path("MERGE_HEAD"))) + whence = FROM_MERGE; + else if (file_exists(git_path("CHERRY_PICK_HEAD"))) + whence = FROM_CHERRY_PICK; + else + whence = FROM_COMMIT; + if (s) + s->whence = whence; +} + +static const char *whence_s(void) +{ + char *s = ""; + + switch (whence) { + case FROM_COMMIT: + break; + case FROM_MERGE: + s = "merge"; + break; + case FROM_CHERRY_PICK: + s = "cherry-pick"; + break; + } + + return s; +} + static void rollback_index_files(void) { switch (commit_style) { @@ -243,7 +288,7 @@ static void add_remove_files(struct string_list *list) if (!lstat(p->string, &st)) { if (add_to_cache(p->string, &st, 0)) - die("updating files failed"); + die(_("updating files failed")); } else remove_file_from_cache(p->string); } @@ -270,7 +315,7 @@ static void create_base_index(void) opts.fn = oneway_merge; tree = parse_tree_indirect(head_sha1); if (!tree) - die("failed to unpack HEAD tree object"); + die(_("failed to unpack HEAD tree object")); parse_tree(tree); init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) @@ -292,24 +337,44 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int 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); if (read_cache_preload(pathspec) < 0) - die("index file corrupt"); + 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. @@ -329,7 +394,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int refresh_cache_or_die(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) - die("unable to write new_index file"); + die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; return index_lock.filename; } @@ -349,7 +414,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int if (active_cache_changed) { if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) - die("unable to write new_index file"); + die(_("unable to write new_index file")); } else { rollback_lock_file(&index_lock); } @@ -378,8 +443,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int */ commit_style = COMMIT_PARTIAL; - if (in_merge) - die("cannot do a partial commit during a merge."); + if (whence != FROM_COMMIT) + die(_("cannot do a partial commit during a %s."), whence_s()); memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; @@ -388,14 +453,14 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int discard_cache(); if (read_cache() < 0) - die("cannot read the index"); + die(_("cannot read the index")); fd = hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) - die("unable to write new_index file"); + die(_("unable to write new_index file")); fd = hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, @@ -408,7 +473,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) - die("unable to write temporary index file"); + die(_("unable to write temporary index file")); discard_cache(); read_cache_from(false_lock.filename); @@ -455,7 +520,7 @@ static int is_a_merge(const unsigned char *sha1) { struct commit *commit = lookup_commit(sha1); if (!commit || parse_commit(commit)) - die("could not parse HEAD commit"); + die(_("could not parse HEAD commit")); return !!(commit->parents && commit->parents->next); } @@ -469,18 +534,18 @@ static void determine_author_info(struct strbuf *author_ident) email = getenv("GIT_AUTHOR_EMAIL"); date = getenv("GIT_AUTHOR_DATE"); - if (use_message && !renew_authorship) { + if (author_message) { const char *a, *lb, *rb, *eol; - a = strstr(use_message_buffer, "\nauthor "); + a = strstr(author_message_buffer, "\nauthor "); if (!a) - die("invalid commit: %s", use_message); + die(_("invalid commit: %s"), author_message); lb = strchrnul(a + strlen("\nauthor "), '<'); rb = strchrnul(lb, '>'); eol = strchrnul(rb, '\n'); if (!*lb || !*rb || !*eol) - die("invalid commit: %s", use_message); + die(_("invalid commit: %s"), author_message); if (lb == a + strlen("\nauthor ")) /* \nauthor <foo@example.com> */ @@ -498,7 +563,7 @@ static void determine_author_info(struct strbuf *author_ident) const char *rb = strchr(force_author, '>'); if (!lb || !rb) - die("malformed --author parameter"); + die(_("malformed --author parameter")); name = xstrndup(force_author, lb - force_author); email = xstrndup(lb + 2, rb - (lb + 2)); } @@ -554,7 +619,7 @@ static char *cut_ident_timestamp_part(char *string) { char *ket = strrchr(string, '>'); if (!ket || ket[1] != ' ') - die("Malformed ident string: '%s'", string); + die(_("Malformed ident string: '%s'"), string); *++ket = '\0'; return ket; } @@ -568,10 +633,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix, int commitable, saved_color_setting; struct strbuf sb = STRBUF_INIT; char *buffer; - FILE *fp; 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; @@ -588,7 +653,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *c; c = lookup_commit_reference_by_name(squash_message); if (!c) - die("could not lookup commit %s", squash_message); + die(_("could not lookup commit %s"), squash_message); ctx.output_encoding = get_commit_output_encoding(); format_commit_message(c, "squash! %s\n\n", &sb, &ctx); @@ -600,19 +665,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix, hook_arg1 = "message"; } else if (logfile && !strcmp(logfile, "-")) { if (isatty(0)) - fprintf(stderr, "(reading log message from standard input)\n"); + fprintf(stderr, _("(reading log message from standard input)\n")); if (strbuf_read(&sb, 0, 0) < 0) - die_errno("could not read log from standard input"); + die_errno(_("could not read log from standard input")); hook_arg1 = "message"; } else if (logfile) { if (strbuf_read_file(&sb, logfile, 0) < 0) - die_errno("could not read log file '%s'", + die_errno(_("could not read log file '%s'"), logfile); hook_arg1 = "message"; } else if (use_message) { buffer = strstr(use_message_buffer, "\n\n"); if (!buffer || buffer[2] == '\0') - die("commit has empty message"); + die(_("commit has empty message")); strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); hook_arg1 = "commit"; hook_arg2 = use_message; @@ -621,31 +686,36 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *commit; commit = lookup_commit_reference_by_name(fixup_message); if (!commit) - die("could not lookup commit %s", fixup_message); + die(_("could not lookup commit %s"), fixup_message); ctx.output_encoding = get_commit_output_encoding(); format_commit_message(commit, "fixup! %s\n\n", &sb, &ctx); hook_arg1 = "message"; } else if (!stat(git_path("MERGE_MSG"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0) - die_errno("could not read MERGE_MSG"); + die_errno(_("could not read MERGE_MSG")); hook_arg1 = "merge"; } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) { if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0) - die_errno("could not read SQUASH_MSG"); + die_errno(_("could not read SQUASH_MSG")); hook_arg1 = "squash"; } else if (template_file) { if (strbuf_read_file(&sb, template_file, 0) < 0) - die_errno("could not read '%s'", template_file); + die_errno(_("could not read '%s'"), template_file); hook_arg1 = "template"; + clean_message_contents = 0; } /* - * This final case does not modify the template message, - * it just sets the argument to the prepare-commit-msg hook. + * The remaining cases don't modify the template message, but + * just set the argument(s) to the prepare-commit-msg hook. */ - else if (in_merge) + else if (whence == FROM_MERGE) hook_arg1 = "merge"; + else if (whence == FROM_CHERRY_PICK) { + hook_arg1 = "commit"; + hook_arg2 = "CHERRY_PICK_HEAD"; + } if (squash_message) { /* @@ -657,11 +727,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix, hook_arg2 = ""; } - fp = fopen(git_path(commit_editmsg), "w"); - if (fp == NULL) - die_errno("could not open '%s'", git_path(commit_editmsg)); + s->fp = fopen(git_path(commit_editmsg), "w"); + 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) { @@ -682,8 +752,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, strbuf_release(&sob); } - if (fwrite(sb.buf, 1, sb.len, fp) < sb.len) - die_errno("could not write commit template"); + if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) + die_errno(_("could not write commit template")); strbuf_release(&sb); @@ -694,55 +764,59 @@ static int prepare_to_commit(const char *index_file, const char *prefix, strbuf_addstr(&committer_ident, git_committer_info(0)); if (use_editor && include_status) { char *ai_tmp, *ci_tmp; - if (in_merge) - fprintf(fp, - "#\n" - "# It looks like you may be committing a MERGE.\n" - "# If this is not correct, please remove the file\n" - "# %s\n" - "# and try again.\n" - "#\n", - git_path("MERGE_HEAD")); - - fprintf(fp, - "\n" - "# Please enter the commit message for your changes."); + if (whence != FROM_COMMIT) + status_printf_ln(s, GIT_COLOR_NORMAL, + _("\n" + "It looks like you may be committing a %s.\n" + "If this is not correct, please remove the file\n" + " %s\n" + "and try again.\n" + ""), + whence_s(), + git_path(whence == FROM_MERGE + ? "MERGE_HEAD" + : "CHERRY_PICK_HEAD")); + + fprintf(s->fp, "\n"); + status_printf(s, GIT_COLOR_NORMAL, + _("Please enter the commit message for your changes.")); if (cleanup_mode == CLEANUP_ALL) - fprintf(fp, - " Lines starting\n" - "# with '#' will be ignored, and an empty" - " message aborts the commit.\n"); + status_printf_more(s, GIT_COLOR_NORMAL, + _(" Lines starting\n" + "with '#' will be ignored, and an empty" + " message aborts the commit.\n")); else /* CLEANUP_SPACE, that is. */ - fprintf(fp, - " Lines starting\n" - "# with '#' will be kept; you may remove them" + status_printf_more(s, GIT_COLOR_NORMAL, + _(" Lines starting\n" + "with '#' will be kept; you may remove them" " yourself if you want to.\n" - "# An empty message aborts the commit.\n"); + "An empty message aborts the commit.\n")); if (only_include_assumed) - fprintf(fp, "# %s\n", only_include_assumed); + status_printf_ln(s, GIT_COLOR_NORMAL, + "%s", only_include_assumed); ai_tmp = cut_ident_timestamp_part(author_ident->buf); ci_tmp = cut_ident_timestamp_part(committer_ident.buf); if (strcmp(author_ident->buf, committer_ident.buf)) - fprintf(fp, - "%s" - "# Author: %s\n", - ident_shown++ ? "" : "#\n", + status_printf_ln(s, GIT_COLOR_NORMAL, + _("%s" + "Author: %s"), + ident_shown++ ? "" : "\n", author_ident->buf); if (!user_ident_sufficiently_given()) - fprintf(fp, - "%s" - "# Committer: %s\n", - ident_shown++ ? "" : "#\n", + status_printf_ln(s, GIT_COLOR_NORMAL, + _("%s" + "Committer: %s"), + ident_shown++ ? "" : "\n", committer_ident.buf); if (ident_shown) - fprintf(fp, "#\n"); + status_printf_ln(s, GIT_COLOR_NORMAL, ""); saved_color_setting = s->use_color; s->use_color = 0; - commitable = run_status(fp, index_file, prefix, 1, s); + commitable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; *ai_tmp = ' '; @@ -752,7 +826,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, const char *parent = "HEAD"; if (!active_nr && read_cache() < 0) - die("Cannot read index"); + die(_("Cannot read index")); if (amend) parent = "HEAD^1"; @@ -764,13 +838,20 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } strbuf_release(&committer_ident); - fclose(fp); + fclose(s->fp); - if (!commitable && !in_merge && !allow_empty && + /* + * Reject an attempt to record a non-merge empty commit without + * explicit --allow-empty. In the cherry-pick case, it may be + * empty due to conflict resolution, which the user should okay. + */ + if (!commitable && whence != FROM_MERGE && !allow_empty && !(amend && is_a_merge(head_sha1))) { run_status(stdout, index_file, prefix, 0, s); if (amend) - fputs(empty_amend_advice, stderr); + fputs(_(empty_amend_advice), stderr); + else if (whence == FROM_CHERRY_PICK) + fputs(_(empty_cherry_pick_advice), stderr); return 0; } @@ -785,7 +866,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, active_cache_tree = cache_tree(); if (cache_tree_update(active_cache_tree, active_cache, active_nr, 0, 0) < 0) { - error("Error building trees"); + error(_("Error building trees")); return 0; } @@ -800,7 +881,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); if (launch_editor(git_path(commit_editmsg), NULL, env)) { fprintf(stderr, - "Please supply the message using either -m or -F option.\n"); + _("Please supply the message using either -m or -F option.\n")); exit(1); } } @@ -880,7 +961,7 @@ static const char *find_author_by_nickname(const char *name) format_commit_message(commit, "%an <%ae>", &buf, &ctx); return strbuf_detach(&buf, NULL); } - die("No existing author found with '%s'", name); + die(_("No existing author found with '%s'"), name); } @@ -895,7 +976,29 @@ static void handle_untracked_files_arg(struct wt_status *s) else if (!strcmp(untracked_files_arg, "all")) s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; else - die("Invalid untracked files mode '%s'", untracked_files_arg); + die(_("Invalid untracked files mode '%s'"), untracked_files_arg); +} + +static const char *read_commit_message(const char *name) +{ + const char *out_enc, *out; + struct commit *commit; + + commit = lookup_commit_reference_by_name(name); + if (!commit) + die(_("could not lookup commit %s"), name); + out_enc = get_commit_output_encoding(); + out = logmsg_reencode(commit, out_enc); + + /* + * If we failed to reencode the buffer, just copy it + * byte for byte so the user can try to fix it up. + * This also handles the case where input and output + * encodings are identical. + */ + if (out == NULL) + out = xstrdup(commit->buffer); + return out; } static int parse_and_validate_options(int argc, const char *argv[], @@ -912,7 +1015,7 @@ static int parse_and_validate_options(int argc, const char *argv[], force_author = find_author_by_nickname(force_author); if (force_author && renew_authorship) - die("Using both --reset-author and --author does not make sense"); + die(_("Using both --reset-author and --author does not make sense")); if (logfile || message.len || use_message || fixup_message) use_editor = 0; @@ -926,11 +1029,11 @@ static int parse_and_validate_options(int argc, const char *argv[], /* Sanity check options */ if (amend && initial_commit) - die("You have nothing to amend."); - if (amend && in_merge) - die("You are in the middle of a merge -- cannot amend."); + die(_("You have nothing to amend.")); + if (amend && whence != FROM_COMMIT) + die(_("You are in the middle of a %s -- cannot amend."), whence_s()); if (fixup_message && squash_message) - die("Options --squash and --fixup cannot be used together"); + die(_("Options --squash and --fixup cannot be used together")); if (use_message) f++; if (edit_message) @@ -940,43 +1043,38 @@ static int parse_and_validate_options(int argc, const char *argv[], if (logfile) f++; if (f > 1) - die("Only one of -c/-C/-F/--fixup can be used."); + die(_("Only one of -c/-C/-F/--fixup can be used.")); if (message.len && f > 0) - die("Option -m cannot be combined with -c/-C/-F/--fixup."); + die((_("Option -m cannot be combined with -c/-C/-F/--fixup."))); if (edit_message) use_message = edit_message; if (amend && !use_message && !fixup_message) use_message = "HEAD"; - if (!use_message && renew_authorship) - die("--reset-author can be used only with -C, -c or --amend."); + if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship) + die(_("--reset-author can be used only with -C, -c or --amend.")); if (use_message) { - const char *out_enc; - struct commit *commit; - - commit = lookup_commit_reference_by_name(use_message); - if (!commit) - die("could not lookup commit %s", use_message); - out_enc = get_commit_output_encoding(); - use_message_buffer = logmsg_reencode(commit, out_enc); - - /* - * If we failed to reencode the buffer, just copy it - * byte for byte so the user can try to fix it up. - * This also handles the case where input and output - * encodings are identical. - */ - if (use_message_buffer == NULL) - use_message_buffer = xstrdup(commit->buffer); + use_message_buffer = read_commit_message(use_message); + if (!renew_authorship) { + author_message = use_message; + author_message_buffer = use_message_buffer; + } + } + if (whence == FROM_CHERRY_PICK && !renew_authorship) { + author_message = "CHERRY_PICK_HEAD"; + 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."); + die(_("No paths with --include/--only does not make sense.")); if (argc == 0 && only && amend) - only_include_assumed = "Clever... amending the last one with dirty index."; + only_include_assumed = _("Clever... amending the last one with dirty index."); if (argc > 0 && !also && !only) - only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths..."; + only_include_assumed = _("Explicit paths specified without -i nor -o; assuming --only paths..."); if (!cleanup_arg || !strcmp(cleanup_arg, "default")) cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) @@ -986,14 +1084,12 @@ static int parse_and_validate_options(int argc, const char *argv[], else if (!strcmp(cleanup_arg, "strip")) cleanup_mode = CLEANUP_ALL; else - die("Invalid cleanup mode %s", cleanup_arg); + die(_("Invalid cleanup mode %s"), cleanup_arg); handle_untracked_files_arg(s); 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."); + die(_("Paths with -a does not make sense.")); if (null_termination && status_format == STATUS_FORMAT_LONG) status_format = STATUS_FORMAT_PORCELAIN; @@ -1074,7 +1170,7 @@ static int git_status_config(const char *k, const char *v, void *cb) else if (!strcmp(v, "all")) s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; else - return error("Invalid untracked files mode '%s'", v); + return error(_("Invalid untracked files mode '%s'"), v); return 0; } return git_diff_ui_config(k, v, NULL); @@ -1111,16 +1207,17 @@ 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); - in_merge = file_exists(git_path("MERGE_HEAD")); + determine_whence(&s); 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; @@ -1135,7 +1232,6 @@ int cmd_status(int argc, const char **argv, const char *prefix) update_index_if_able(&the_index, &index_lock); s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; - s.in_merge = in_merge; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(&s); @@ -1175,9 +1271,9 @@ static void print_summary(const char *prefix, const unsigned char *sha1) commit = lookup_commit(sha1); if (!commit) - die("couldn't look up newly created commit"); + die(_("couldn't look up newly created commit")); if (!commit || parse_commit(commit)) - die("could not parse newly created commit"); + die(_("could not parse newly created commit")); strbuf_addstr(&format, "format:%h] %s"); @@ -1192,7 +1288,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1) strbuf_addbuf_percentquote(&format, &committer_ident); if (advice_implicit_identity) { strbuf_addch(&format, '\n'); - strbuf_addstr(&format, implicit_ident_advice); + strbuf_addstr(&format, _(implicit_ident_advice)); } } strbuf_release(&author_ident); @@ -1210,7 +1306,6 @@ static void print_summary(const char *prefix, const unsigned char *sha1) get_commit_format(format.buf, &rev); rev.always_show_header = 0; rev.diffopt.detect_rename = 1; - rev.diffopt.rename_limit = 100; rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); @@ -1218,9 +1313,9 @@ static void print_summary(const char *prefix, const unsigned char *sha1) !prefixcmp(head, "refs/heads/") ? head + 11 : !strcmp(head, "HEAD") ? - "detached HEAD" : + _("detached HEAD") : head, - initial_commit ? " (root-commit)" : ""); + initial_commit ? _(" (root-commit)") : ""); if (!log_tree_commit(&rev, commit)) { rev.always_show_header = 1; @@ -1297,8 +1392,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) wt_status_prepare(&s); git_config(git_commit_config, &s); - in_merge = file_exists(git_path("MERGE_HEAD")); - s.in_merge = in_merge; + determine_whence(&s); if (s.use_color == -1) s.use_color = git_use_color_default; @@ -1331,11 +1425,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix) reflog_msg = "commit (amend)"; commit = lookup_commit(head_sha1); if (!commit || parse_commit(commit)) - die("could not parse HEAD commit"); + die(_("could not parse HEAD commit")); for (c = commit->parents; c; c = c->next) pptr = &commit_list_insert(c->item, pptr)->next; - } else if (in_merge) { + } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; @@ -1344,19 +1438,19 @@ int cmd_commit(int argc, const char **argv, const char *prefix) pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next; fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) - die_errno("could not open '%s' for reading", + die_errno(_("could not open '%s' for reading"), git_path("MERGE_HEAD")); while (strbuf_getline(&m, fp, '\n') != EOF) { unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) - die("Corrupt MERGE_HEAD file (%s)", m.buf); + die(_("Corrupt MERGE_HEAD file (%s)"), m.buf); pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next; } fclose(fp); strbuf_release(&m); if (!stat(git_path("MERGE_MODE"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0) - die_errno("could not read MERGE_MODE"); + die_errno(_("could not read MERGE_MODE")); if (!strcmp(sb.buf, "no-ff")) allow_fast_forward = 0; } @@ -1364,7 +1458,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix) parents = reduce_heads(parents); } else { if (!reflog_msg) - reflog_msg = "commit"; + reflog_msg = (whence == FROM_CHERRY_PICK) + ? "commit (cherry-pick)" + : "commit"; pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next; } @@ -1373,7 +1469,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { int saved_errno = errno; rollback_index_files(); - die("could not read commit message: %s", strerror(saved_errno)); + die(_("could not read commit message: %s"), strerror(saved_errno)); } /* Truncate the message just before the diff, if any. */ @@ -1387,14 +1483,14 @@ int cmd_commit(int argc, const char **argv, const char *prefix) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (message_is_empty(&sb) && !allow_empty_message) { rollback_index_files(); - fprintf(stderr, "Aborting commit due to empty commit message.\n"); + fprintf(stderr, _("Aborting commit due to empty commit message.\n")); exit(1); } if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1, author_ident.buf)) { rollback_index_files(); - die("failed to write commit object"); + die(_("failed to write commit object")); } strbuf_release(&author_ident); @@ -1412,22 +1508,23 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!ref_lock) { rollback_index_files(); - die("cannot lock HEAD ref"); + die(_("cannot lock HEAD ref")); } if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { rollback_index_files(); - die("cannot update HEAD ref"); + die(_("cannot update HEAD ref")); } + unlink(git_path("CHERRY_PICK_HEAD")); unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) - die ("Repository has been updated, but unable to write\n" + die (_("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" - "not exceeded, and then \"git reset HEAD\" to recover."); + "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); run_hook(get_index_file(), "post-commit", NULL); diff --git a/builtin/config.c b/builtin/config.c index f87443698a..211e118d57 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -160,7 +160,7 @@ static int get_value(const char *key_, const char *regex_) if (!local) { const char *home = getenv("HOME"); local = repo_config = git_pathdup("config"); - if (git_config_global() && home) + if (home) global = xstrdup(mkpath("%s/.gitconfig", home)); if (git_config_system()) system_wide = git_etc_gitconfig(); @@ -436,9 +436,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); @@ -513,3 +518,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 0; } + +int cmd_repo_config(int argc, const char **argv, const char *prefix) +{ + fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n"); + return cmd_config(argc, argv, prefix); +} diff --git a/builtin/describe.c b/builtin/describe.c index 4afd1504a6..66fc291c8a 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -231,13 +231,13 @@ static void display_name(struct commit_name *n) if (n->prio == 2 && !n->tag) { n->tag = lookup_tag(n->sha1); if (!n->tag || parse_tag(n->tag)) - die("annotated tag %s not available", n->path); + die(_("annotated tag %s not available"), n->path); } if (n->tag && !n->name_checked) { if (!n->tag->tag) - die("annotated tag %s has no embedded name", n->path); + die(_("annotated tag %s has no embedded name"), n->path); if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) - warning("tag '%s' is really '%s' here", n->tag->tag, n->path); + warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path); n->name_checked = 1; } @@ -264,10 +264,10 @@ static void describe(const char *arg, int last_one) unsigned int unannotated_cnt = 0; if (get_sha1(arg, sha1)) - die("Not a valid object name %s", arg); + die(_("Not a valid object name %s"), arg); cmit = lookup_commit_reference(sha1); if (!cmit) - die("%s is not a valid '%s' object", arg, commit_type); + die(_("%s is not a valid '%s' object"), arg, commit_type); n = find_commit_name(cmit->object.sha1); if (n && (tags || all || n->prio == 2)) { @@ -284,9 +284,9 @@ static void describe(const char *arg, int last_one) } if (!max_candidates) - die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1)); + die(_("no tag exactly matches '%s'"), sha1_to_hex(cmit->object.sha1)); if (debug) - fprintf(stderr, "searching to describe %s\n", arg); + fprintf(stderr, _("searching to describe %s\n"), arg); if (!have_util) { for_each_hash(&names, set_util, NULL); @@ -326,7 +326,7 @@ static void describe(const char *arg, int last_one) } if (annotated_cnt && !list) { if (debug) - fprintf(stderr, "finished search at %s\n", + fprintf(stderr, _("finished search at %s\n"), sha1_to_hex(c->object.sha1)); break; } @@ -350,12 +350,12 @@ static void describe(const char *arg, int last_one) return; } if (unannotated_cnt) - die("No annotated tags can describe '%s'.\n" - "However, there were unannotated tags: try --tags.", + die(_("No annotated tags can describe '%s'.\n" + "However, there were unannotated tags: try --tags."), sha1_to_hex(sha1)); else - die("No tags can describe '%s'.\n" - "Try --always, or create some tags.", + die(_("No tags can describe '%s'.\n" + "Try --always, or create some tags."), sha1_to_hex(sha1)); } @@ -375,11 +375,11 @@ static void describe(const char *arg, int last_one) prio_names[t->name->prio], t->depth, t->name->path); } - fprintf(stderr, "traversed %lu commits\n", seen_commits); + fprintf(stderr, _("traversed %lu commits\n"), seen_commits); if (gave_up_on) { fprintf(stderr, - "more than %i tags found; listed %i most recent\n" - "gave up search at %s\n", + _("more than %i tags found; listed %i most recent\n" + "gave up search at %s\n"), max_candidates, max_candidates, sha1_to_hex(gave_up_on->object.sha1)); } @@ -433,7 +433,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; if (longformat && abbrev == 0) - die("--long is incompatible with --abbrev=0"); + die(_("--long is incompatible with --abbrev=0")); if (contains) { const char **args = xmalloc((7 + argc) * sizeof(char *)); @@ -459,14 +459,14 @@ int cmd_describe(int argc, const char **argv, const char *prefix) init_hash(&names); for_each_rawref(get_name, NULL); if (!names.nr && !always) - die("No names found, cannot describe anything."); + 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; describe("HEAD", 1); } else if (dirty) { - die("--dirty is incompatible with committishes"); + die(_("--dirty is incompatible with committishes")); } else { while (argc-- > 0) { describe(*argv++, argc == 0); diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 951c7c8994..46085f862f 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; - if (read_cache_preload(rev.diffopt.paths) < 0) { + if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } 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 d4d80c982e..69cd5eed78 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -71,9 +71,9 @@ static int builtin_diff_b_f(struct rev_info *revs, usage(builtin_diff_usage); if (lstat(path, &st)) - die_errno("failed to stat '%s'", path); + die_errno(_("failed to stat '%s'"), path); if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) - die("'%s': not a regular file or symlink", path); + die(_("'%s': not a regular file or symlink"), path); diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/"); @@ -135,7 +135,7 @@ static int builtin_diff_index(struct rev_info *revs, revs->max_count != -1 || revs->min_age != -1 || revs->max_age != -1) usage(builtin_diff_usage); - if (read_cache_preload(revs->diffopt.paths) < 0) { + if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } @@ -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(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] == '-') { @@ -217,7 +217,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv else if (!strcmp(argv[1], "-h")) usage(builtin_diff_usage); else - return error("invalid option: %s", argv[1]); + return error(_("invalid option: %s"), argv[1]); argv++; argc--; } @@ -232,12 +232,11 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); - if (read_cache_preload(revs->diffopt.paths) < 0) { + if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { 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) @@ -294,12 +293,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix) DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) - die("Not a git repository"); + die(_("Not a git repository")); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; if (diff_setup_done(&rev.diffopt) < 0) - die("diff_setup_done failed"); + die(_("diff_setup_done failed")); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); @@ -344,12 +343,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix) obj = parse_object(obj->sha1); obj = deref_tag(obj, NULL, 0); if (!obj) - die("invalid object '%s' given.", name); + die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { if (ARRAY_SIZE(ent) <= ents) - die("more than %d trees given: '%s'", + die(_("more than %d trees given: '%s'"), (int) ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; @@ -359,7 +358,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } if (obj->type == OBJ_BLOB) { if (2 <= blobs) - die("more than two blobs given: '%s'", name); + die(_("more than two blobs given: '%s'"), name); hashcpy(blob[blobs].sha1, obj->sha1); blob[blobs].name = name; blob[blobs].mode = list->mode; @@ -367,16 +366,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix) continue; } - die("unhandled object '%s' given.", name); + die(_("unhandled object '%s' given."), name); } - if (rev.prune_data) { - const char **pathspec = rev.prune_data; - while (*pathspec) { - if (!path) - path = *pathspec; - paths++; - pathspec++; - } + if (rev.prune_data.nr) { + if (!path) + path = rev.prune_data.items[0].match; + paths += rev.prune_data.nr; } /* diff --git a/builtin/fast-export.c b/builtin/fast-export.c index b18fc85c4c..becef85782 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -26,6 +26,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; @@ -627,6 +628,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,10 +651,13 @@ 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); - if (import_filename && revs.prune_data) + if (import_filename && revs.prune_data.nr) full_tree = 1; get_tags_and_duplicates(&revs.pending, &extra_refs); @@ -675,5 +681,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 b999413934..4367984102 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -1,4 +1,4 @@ -#include "cache.h" +#include "builtin.h" #include "refs.h" #include "pkt-line.h" #include "commit.h" @@ -9,11 +9,13 @@ #include "fetch-pack.h" #include "remote.h" #include "run-command.h" +#include "transport.h" 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 struct fetch_pack_args args = { /* .uploadpack = */ "git-upload-pack", }; @@ -217,14 +219,40 @@ static void send_request(int fd, struct strbuf *buf) safe_write(fd, buf->buf, buf->len); } +static void insert_one_alternate_ref(const struct ref *ref, void *unused) +{ + rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL); +} + +static void insert_alternate_refs(void) +{ + for_each_alternate_ref(insert_one_alternate_ref, NULL); +} + +#define INITIAL_FLUSH 16 +#define PIPESAFE_FLUSH 32 +#define LARGE_FLUSH 1024 + +static int next_flush(int count) +{ + int flush_limit = args.stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH; + + if (count < flush_limit) + count <<= 1; + else + count += flush_limit; + return count; +} + static int find_common(int fd[2], unsigned char *result_sha1, struct ref *refs) { int fetching; - int count = 0, flushes = 0, retval; + int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval; const unsigned char *sha1; unsigned in_vain = 0; int got_continue = 0; + int got_ready = 0; struct strbuf req_buf = STRBUF_INIT; size_t state_len = 0; @@ -235,6 +263,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, marked = 1; for_each_ref(rev_list_insert_ref, NULL); + insert_alternate_refs(); fetching = 0; for ( ; refs ; refs = refs->next) { @@ -262,6 +291,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, struct strbuf c = STRBUF_INIT; if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed"); if (multi_ack == 1) strbuf_addstr(&c, " multi_ack"); + if (no_done) strbuf_addstr(&c, " no-done"); if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k"); if (use_sideband == 1) strbuf_addstr(&c, " side-band"); if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack"); @@ -332,19 +362,20 @@ static int find_common(int fd[2], unsigned char *result_sha1, if (args.verbose) fprintf(stderr, "have %s\n", sha1_to_hex(sha1)); in_vain++; - if (!(31 & ++count)) { + if (flush_at <= ++count) { int ack; packet_buf_flush(&req_buf); send_request(fd[1], &req_buf); strbuf_setlen(&req_buf, state_len); flushes++; + flush_at = next_flush(count); /* * We keep one window "ahead" of the other side, and * will wait for an ACK only on the next one */ - if (!args.stateless_rpc && count == 32) + if (!args.stateless_rpc && count == INITIAL_FLUSH) continue; consume_shallow_list(fd[0]); @@ -379,6 +410,10 @@ static int find_common(int fd[2], unsigned char *result_sha1, retval = 0; in_vain = 0; got_continue = 1; + if (ack == ACK_ready) { + rev_list = NULL; + got_ready = 1; + } break; } } @@ -392,8 +427,10 @@ static int find_common(int fd[2], unsigned char *result_sha1, } } done: - packet_buf_write(&req_buf, "done\n"); - send_request(fd[1], &req_buf); + if (!got_ready || !no_done) { + packet_buf_write(&req_buf, "done\n"); + send_request(fd[1], &req_buf); + } if (args.verbose) fprintf(stderr, "done\n"); if (retval != 0) { @@ -435,8 +472,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; } @@ -696,6 +735,12 @@ static struct ref *do_fetch_pack(int fd[2], if (args.verbose) fprintf(stderr, "Server supports multi_ack_detailed\n"); multi_ack = 2; + if (server_supports("no-done")) { + if (args.verbose) + fprintf(stderr, "Server supports no-done\n"); + if (args.stateless_rpc) + no_done = 1; + } } else if (server_supports("multi_ack")) { if (args.verbose) @@ -804,6 +849,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) char **pack_lockfile_ptr = NULL; struct child_process *conn; + packet_trace_identity("fetch-pack"); + nr_heads = 0; heads = NULL; for (i = 1; i < argc; i++) { diff --git a/builtin/fetch.c b/builtin/fetch.c index 7efecfe1cf..93c99385a9 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -28,12 +28,6 @@ enum { TAGS_SET = 2 }; -enum { - RECURSE_SUBMODULES_OFF = 0, - RECURSE_SUBMODULES_DEFAULT = 1, - RECURSE_SUBMODULES_ON = 2 -}; - static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity; static int progress, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int tags = TAGS_DEFAULT; @@ -42,6 +36,21 @@ static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; static struct transport *transport; static const char *submodule_prefix = ""; +static const char *recurse_submodules_default; + +static int option_parse_recurse_submodules(const struct option *opt, + const char *arg, int unset) +{ + if (unset) { + recurse_submodules = RECURSE_SUBMODULES_OFF; + } else { + if (arg) + recurse_submodules = parse_fetch_recurse_submodules_arg(opt->long_name, arg); + else + recurse_submodules = RECURSE_SUBMODULES_ON; + } + return 0; +} static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), @@ -60,9 +69,9 @@ static struct option builtin_fetch_options[] = { "do not fetch all tags (--no-tags)", TAGS_UNSET), OPT_BOOLEAN('p', "prune", &prune, "prune remote-tracking branches no longer on remote"), - OPT_SET_INT(0, "recurse-submodules", &recurse_submodules, + { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "on-demand", "control recursive fetching of submodules", - RECURSE_SUBMODULES_ON), + PARSE_OPT_OPTARG, option_parse_recurse_submodules }, OPT_BOOLEAN(0, "dry-run", &dry_run, "dry run"), OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"), @@ -73,6 +82,9 @@ static struct option builtin_fetch_options[] = { "deepen history of shallow clone"), { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir", "prepend this to submodule path output", PARSE_OPT_HIDDEN }, + { OPTION_STRING, 0, "recurse-submodules-default", + &recurse_submodules_default, NULL, + "default mode for recursion", PARSE_OPT_HIDDEN }, OPT_END() }; @@ -184,7 +196,7 @@ static struct ref *get_ref_map(struct transport *transport, } else { ref_map = get_remote_ref(remote_refs, "HEAD"); if (!ref_map) - die("Couldn't find remote ref HEAD"); + die(_("Couldn't find remote ref HEAD")); ref_map->merge = 1; tail = &ref_map->next; } @@ -237,12 +249,12 @@ static int update_local_ref(struct ref *ref, *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) - die("object %s not found", sha1_to_hex(ref->new_sha1)); + 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, + _("[up to date]"), REFCOL_WIDTH, remote, pretty_ref); return 0; } @@ -255,8 +267,8 @@ 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, + sprintf(display, _("! %-*s %-*s -> %s (can't fetch in current branch)"), + TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote, pretty_ref); return 1; } @@ -266,8 +278,8 @@ static int update_local_ref(struct ref *ref, 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)" : ""); + TRANSPORT_SUMMARY_WIDTH, _("[tag update]"), REFCOL_WIDTH, remote, + pretty_ref, r ? _(" (unable to update local ref)") : ""); return r; } @@ -279,17 +291,20 @@ static int update_local_ref(struct ref *ref, int r; if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; - what = "[new tag]"; + what = _("[new tag]"); } else { msg = "storing head"; - what = "[new branch]"; + what = _("[new branch]"); + if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && + (recurse_submodules != RECURSE_SUBMODULES_ON)) + check_for_new_submodule_commits(ref->new_sha1); } 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)" : ""); + r ? _(" (unable to update local ref)") : ""); return r; } @@ -299,10 +314,13 @@ static int update_local_ref(struct ref *ref, strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, ".."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && + (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)" : ""); + pretty_ref, r ? _(" (unable to update local ref)") : ""); return r; } else if (force || ref->force) { char quickref[84]; @@ -310,16 +328,19 @@ static int update_local_ref(struct ref *ref, strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, "..."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && + (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"); + r ? _("unable to update local ref") : _("forced update")); return r; } else { - sprintf(display, "! %-*s %-*s -> %s (non-fast-forward)", - TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, - pretty_ref); + sprintf(display, "! %-*s %-*s -> %s %s", + TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote, + pretty_ref, _("(non-fast-forward)")); return 1; } } @@ -337,7 +358,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, fp = fopen(filename, "a"); if (!fp) - return error("cannot open %s: %s\n", filename, strerror(errno)); + return error(_("cannot open %s: %s\n"), filename, strerror(errno)); if (raw_url) url = transport_anonymize_url(raw_url); @@ -415,7 +436,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, REFCOL_WIDTH, *what ? what : "HEAD"); if (*note) { if (verbosity >= 0 && !shown_url) { - fprintf(stderr, "From %.*s\n", + fprintf(stderr, _("From %.*s\n"), url_len, url); shown_url = 1; } @@ -426,9 +447,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, free(url); fclose(fp); if (rc & STORE_REF_ERROR_DF_CONFLICT) - error("some local refs could not be updated; try running\n" + error(_("some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting " - "branches", remote_name); + "branches"), remote_name); return rc; } @@ -476,7 +497,7 @@ static int quickfetch(struct ref *ref_map) err = start_command(&revlist); if (err) { - error("could not run rev-list"); + error(_("could not run rev-list")); return err; } @@ -490,14 +511,14 @@ static int quickfetch(struct ref *ref_map) 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)); + 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)); + error(_("failed to close rev-list's stdin: %s"), strerror(errno)); err = -1; } @@ -524,16 +545,16 @@ static int prune_refs(struct transport *transport, struct ref *ref_map) int result = 0; struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map); const char *dangling_msg = dry_run - ? " (%s will become dangling)\n" - : " (%s has become dangling)\n"; + ? _(" (%s will become dangling)\n") + : _(" (%s has become dangling)\n"); for (ref = stale_refs; ref; ref = ref->next) { if (!dry_run) result |= delete_ref(ref->name, NULL, 0); if (verbosity >= 0) { fprintf(stderr, " x %-*s %-*s -> %s\n", - TRANSPORT_SUMMARY_WIDTH, "[deleted]", - REFCOL_WIDTH, "(none)", prettify_refname(ref->name)); + TRANSPORT_SUMMARY_WIDTH, _("[deleted]"), + REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name)); warn_dangling_symref(stderr, dangling_msg, ref->name); } } @@ -650,8 +671,8 @@ static void check_not_current_branch(struct ref *ref_map) for (; ref_map; ref_map = ref_map->next) if (ref_map->peer_ref && !strcmp(current_branch->refname, ref_map->peer_ref->name)) - die("Refusing to fetch into current branch %s " - "of non-bare repository", current_branch->refname); + die(_("Refusing to fetch into current branch %s " + "of non-bare repository"), current_branch->refname); } static int truncate_fetch_head(void) @@ -660,7 +681,7 @@ static int truncate_fetch_head(void) FILE *fp = fopen(filename, "w"); if (!fp) - return error("cannot open %s: %s\n", filename, strerror(errno)); + return error(_("cannot open %s: %s\n"), filename, strerror(errno)); fclose(fp); return 0; } @@ -684,7 +705,7 @@ static int do_fetch(struct transport *transport, } if (!transport->get_refs_list || !transport->fetch) - die("Don't know how to fetch from %s", transport->url); + die(_("Don't know how to fetch from %s"), transport->url); /* if not appending, truncate FETCH_HEAD */ if (!append && !dry_run) { @@ -738,10 +759,10 @@ static void set_option(const char *name, const char *value) { int r = transport_set_option(transport, name, value); if (r < 0) - die("Option \"%s\" value \"%s\" is not valid for %s", + die(_("Option \"%s\" value \"%s\" is not valid for %s"), name, value, transport->url); if (r > 0) - warning("Option \"%s\" is ignored for %s\n", + warning(_("Option \"%s\" is ignored for %s\n"), name, transport->url); } @@ -810,6 +831,8 @@ static void add_options_to_argv(int *argc, const char **argv) argv[(*argc)++] = "--keep"; if (recurse_submodules == RECURSE_SUBMODULES_ON) argv[(*argc)++] = "--recurse-submodules"; + else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) + argv[(*argc)++] = "--recurse-submodules=on-demand"; if (verbosity >= 2) argv[(*argc)++] = "-v"; if (verbosity >= 1) @@ -838,9 +861,9 @@ static int fetch_multiple(struct string_list *list) argv[argc] = name; argv[argc + 1] = NULL; if (verbosity >= 0) - printf("Fetching %s\n", name); + printf(_("Fetching %s\n"), name); if (run_command_v_opt(argv, RUN_GIT_CMD)) { - error("Could not fetch %s", name); + error(_("Could not fetch %s"), name); result = 1; } } @@ -852,12 +875,13 @@ 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; if (!remote) - die("No remote repository specified. Please, specify either a URL or a\n" - "remote name from which new revisions should be fetched."); + die(_("No remote repository specified. Please, specify either a URL or a\n" + "remote name from which new revisions should be fetched.")); transport = transport_get(remote, NULL); transport_set_verbosity(transport, verbosity, progress); @@ -876,7 +900,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) char *ref; i++; if (i >= argc) - die("You need to specify a tag name."); + die(_("You need to specify a tag name.")); ref = xmalloc(strlen(argv[i]) * 2 + 22); strcpy(ref, "refs/tags/"); strcat(ref, argv[i]); @@ -892,8 +916,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); transport_disconnect(transport); transport = NULL; return exit_code; @@ -906,6 +931,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) struct remote *remote; int result = 0; + packet_trace_identity("fetch"); + /* Record the command line for the reflog */ strbuf_addstr(&default_rla, "fetch"); for (i = 1; i < argc; i++) @@ -916,9 +943,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (all) { if (argc == 1) - die("fetch --all does not take a repository argument"); + die(_("fetch --all does not take a repository argument")); else if (argc > 1) - die("fetch --all does not make sense with refspecs"); + die(_("fetch --all does not make sense with refspecs")); (void) for_each_remote(get_one_remote_for_fetch, &list); result = fetch_multiple(&list); } else if (argc == 0) { @@ -929,7 +956,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) /* All arguments are assumed to be remotes or groups */ for (i = 0; i < argc; i++) if (!add_remote_or_group(argv[i], &list)) - die("No such remote or remote group: %s", argv[i]); + die(_("No such remote or remote group: %s"), argv[i]); result = fetch_multiple(&list); } else { /* Single remote or group */ @@ -937,7 +964,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (list.nr > 1) { /* More than one remote */ if (argc > 1) - die("Fetching a group and specifying refspecs does not make sense"); + die(_("Fetching a group and specifying refspecs does not make sense")); result = fetch_multiple(&list); } else { /* Zero or one remotes */ @@ -949,15 +976,16 @@ 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; - /* Set recursion as default when we already are recursing */ - if (submodule_prefix[0]) - set_config_fetch_recurse_submodules(1); + 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, - recurse_submodules == RECURSE_SUBMODULES_ON, + recurse_submodules, verbosity < 0); } diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 5189b16c9e..75816329d6 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -31,7 +31,7 @@ struct src_data { int head_status; }; -void init_src_data(struct src_data *data) +static void init_src_data(struct src_data *data) { data->branch.strdup_strings = 1; data->tag.strdup_strings = 1; diff --git a/builtin/fsck.c b/builtin/fsck.c index 795aba087f..5ae0366bc8 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -140,11 +140,10 @@ static int traverse_reachable(void) int result = 0; while (pending.nr) { struct object_array_entry *entry; - struct object *obj, *parent; + struct object *obj; entry = pending.objects + --pending.nr; obj = entry->item; - parent = (struct object *) entry->name; result |= traverse_one_object(obj); } return !!result; diff --git a/builtin/gc.c b/builtin/gc.c index 1a80702b3d..0498094711 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -60,7 +60,7 @@ static int gc_config(const char *var, const char *value, void *cb) if (value && strcmp(value, "now")) { unsigned long now = approxidate("now"); if (approxidate(value) >= now) - return error("Invalid %s: '%s'", var, value); + return error(_("Invalid %s: '%s'"), var, value); } return git_config_string(&prune_expire, var, value); } @@ -75,7 +75,7 @@ static void append_option(const char **cmd, const char *opt, int max_length) ; if (i + 2 >= max_length) - die("Too many options specified"); + die(_("Too many options specified")); cmd[i++] = opt; cmd[i] = NULL; } @@ -100,7 +100,7 @@ static int too_many_loose_objects(void) return 0; if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { - warning("insanely long object directory %.*s", 50, objdir); + warning(_("insanely long object directory %.*s"), 50, objdir); return 0; } dir = opendir(path); @@ -219,13 +219,13 @@ int cmd_gc(int argc, const char **argv, const char *prefix) */ if (!need_to_gc()) return 0; - fprintf(stderr, - "Auto packing the repository for optimum performance.%s\n", - quiet - ? "" - : (" You may also\n" - "run \"git gc\" manually. See " - "\"git help gc\" for more information.")); + if (quiet) + fprintf(stderr, _("Auto packing the repository for optimum performance.\n")); + else + fprintf(stderr, + _("Auto packing the repository for optimum performance. You may also\n" + "run \"git gc\" manually. See " + "\"git help gc\" for more information.\n")); } else append_option(argv_repack, prune_expire && !strcmp(prune_expire, "now") @@ -251,8 +251,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) return error(FAILED_RUN, argv_rerere[0]); if (auto_gc && too_many_loose_objects()) - warning("There are too many unreachable loose objects; " - "run 'git prune' to remove them."); + warning(_("There are too many unreachable loose objects; " + "run 'git prune' to remove them.")); return 0; } diff --git a/builtin/grep.c b/builtin/grep.c index bb0f93208f..cccf8da6d2 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -93,8 +93,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 +159,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); @@ -244,7 +253,7 @@ static void start_threads(struct grep_opt *opt) err = pthread_create(&threads[i], NULL, run, o); if (err) - die("grep: failed to create thread: %s", + die(_("grep: failed to create thread: %s"), strerror(err)); } } @@ -302,6 +311,19 @@ static int grep_config(const char *var, const char *value, void *cb) default: return 0; } + if (!strcmp(var, "grep.extendedregexp")) { + if (git_config_bool(var, value)) + opt->regflags |= REG_EXTENDED; + else + opt->regflags &= ~REG_EXTENDED; + return 0; + } + + if (!strcmp(var, "grep.linenumber")) { + opt->linenum = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "color.grep")) opt->color = git_config_colorbool(var, value, -1); else if (!strcmp(var, "color.grep.context")) @@ -328,106 +350,6 @@ static int grep_config(const char *var, const char *value, void *cb) return 0; } -/* - * Return non-zero if max_depth is negative or path has no more then max_depth - * slashes. - */ -static int accept_subdir(const char *path, int max_depth) -{ - if (max_depth < 0) - return 1; - - while ((path = strchr(path, '/')) != NULL) { - max_depth--; - if (max_depth < 0) - return 0; - path++; - } - return 1; -} - -/* - * Return non-zero if name is a subdirectory of match and is not too deep. - */ -static int is_subdir(const char *name, int namelen, - const char *match, int matchlen, int max_depth) -{ - if (matchlen > namelen || strncmp(name, match, matchlen)) - return 0; - - if (name[matchlen] == '\0') /* exact match */ - return 1; - - if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/') - return accept_subdir(name + matchlen + 1, max_depth); - - return 0; -} - -/* - * git grep pathspecs are somewhat different from diff-tree pathspecs; - * pathname wildcards are allowed. - */ -static int pathspec_matches(const char **paths, const char *name, int max_depth) -{ - int namelen, i; - if (!paths || !*paths) - return accept_subdir(name, max_depth); - namelen = strlen(name); - for (i = 0; paths[i]; i++) { - const char *match = paths[i]; - int matchlen = strlen(match); - const char *cp, *meta; - - if (is_subdir(name, namelen, match, matchlen, max_depth)) - return 1; - if (!fnmatch(match, name, 0)) - return 1; - if (name[namelen-1] != '/') - continue; - - /* We are being asked if the directory ("name") is worth - * descending into. - * - * Find the longest leading directory name that does - * not have metacharacter in the pathspec; the name - * we are looking at must overlap with that directory. - */ - for (cp = match, meta = NULL; cp - match < matchlen; cp++) { - char ch = *cp; - if (ch == '*' || ch == '[' || ch == '?') { - meta = cp; - break; - } - } - if (!meta) - meta = cp; /* fully literal */ - - if (namelen <= meta - match) { - /* Looking at "Documentation/" and - * the pattern says "Documentation/howto/", or - * "Documentation/diff*.txt". The name we - * have should match prefix. - */ - if (!memcmp(match, name, namelen)) - return 1; - continue; - } - - if (meta - match < namelen) { - /* Looking at "Documentation/howto/" and - * the pattern says "Documentation/h*"; - * match up to "Do.../h"; this avoids descending - * into "Documentation/technical/". - */ - if (!memcmp(match, name, meta - match)) - return 1; - continue; - } - } - return 0; -} - static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { void *data; @@ -449,7 +371,7 @@ static void *load_sha1(const unsigned char *sha1, unsigned long *size, void *data = lock_and_read_sha1_file(sha1, &type, size); if (!data) - error("'%s': unable to read %s", name, sha1_to_hex(sha1)); + error(_("'%s': unable to read %s"), name, sha1_to_hex(sha1)); return data; } @@ -500,21 +422,21 @@ static void *load_file(const char *filename, size_t *sz) if (lstat(filename, &st) < 0) { err_ret: if (errno != ENOENT) - error("'%s': %s", filename, strerror(errno)); - return 0; + error(_("'%s': %s"), filename, strerror(errno)); + return NULL; } if (!S_ISREG(st.st_mode)) - return 0; + return NULL; *sz = xsize_t(st.st_size); i = open(filename, O_RDONLY); if (i < 0) goto err_ret; data = xmalloc(*sz + 1); if (st.st_size != read_in_full(i, data, *sz)) { - error("'%s': short read %s", filename, strerror(errno)); + error(_("'%s': short read %s"), filename, strerror(errno)); close(i); free(data); - return 0; + return NULL; } close(i); data[*sz] = 0; @@ -573,14 +495,14 @@ static void run_pager(struct grep_opt *opt, const char *prefix) argv[path_list->nr] = NULL; if (prefix && chdir(prefix)) - die("Failed to chdir: %s", prefix); + die(_("Failed to chdir: %s"), prefix); status = run_command_v_opt(argv, RUN_USING_SHELL); if (status) exit(status); free(argv); } -static int grep_cache(struct grep_opt *opt, const char **paths, int cached) +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached) { int hit = 0; int nr; @@ -590,7 +512,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name, opt->max_depth)) + if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -617,44 +539,29 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) return hit; } -static int grep_tree(struct grep_opt *opt, const char **paths, - struct tree_desc *tree, - const char *tree_name, const char *base) +static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, + struct tree_desc *tree, struct strbuf *base, int tn_len) { - int len; - int hit = 0; + int hit = 0, match = 0; struct name_entry entry; - char *down; - int tn_len = strlen(tree_name); - struct strbuf pathbuf; - - strbuf_init(&pathbuf, PATH_MAX + tn_len); - - if (tn_len) { - strbuf_add(&pathbuf, tree_name, tn_len); - strbuf_addch(&pathbuf, ':'); - tn_len = pathbuf.len; - } - strbuf_addstr(&pathbuf, base); - len = pathbuf.len; + int old_baselen = base->len; while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(entry.path, entry.sha1); - pathbuf.len = len; - strbuf_add(&pathbuf, entry.path, te_len); - - if (S_ISDIR(entry.mode)) - /* Match "abc/" against pathspec to - * decide if we want to descend into "abc" - * directory. - */ - strbuf_addch(&pathbuf, '/'); - - down = pathbuf.buf + tn_len; - if (!pathspec_matches(paths, down, opt->max_depth)) - ; - else if (S_ISREG(entry.mode)) - hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); + + if (match != 2) { + match = tree_entry_interesting(&entry, base, tn_len, pathspec); + if (match < 0) + break; + if (match == 0) + continue; + } + + strbuf_add(base, entry.path, te_len); + + if (S_ISREG(entry.mode)) { + hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len); + } else if (S_ISDIR(entry.mode)) { enum object_type type; struct tree_desc sub; @@ -663,20 +570,23 @@ static int grep_tree(struct grep_opt *opt, const char **paths, data = lock_and_read_sha1_file(entry.sha1, &type, &size); if (!data) - die("unable to read tree (%s)", + die(_("unable to read tree (%s)"), sha1_to_hex(entry.sha1)); + + strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); - hit |= grep_tree(opt, paths, &sub, tree_name, down); + hit |= grep_tree(opt, pathspec, &sub, base, tn_len); free(data); } + strbuf_setlen(base, old_baselen); + if (hit && opt->status_only) break; } - strbuf_release(&pathbuf); return hit; } -static int grep_object(struct grep_opt *opt, const char **paths, +static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct object *obj, const char *name) { if (obj->type == OBJ_BLOB) @@ -685,20 +595,30 @@ static int grep_object(struct grep_opt *opt, const char **paths, struct tree_desc tree; void *data; unsigned long size; - int hit; + struct strbuf base; + int hit, len; + data = read_object_with_reference(obj->sha1, tree_type, &size, NULL); if (!data) - die("unable to read tree (%s)", sha1_to_hex(obj->sha1)); + die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1)); + + len = name ? strlen(name) : 0; + strbuf_init(&base, PATH_MAX + len + 1); + if (len) { + strbuf_add(&base, name, len); + strbuf_addch(&base, ':'); + } init_tree_desc(&tree, data, size); - hit = grep_tree(opt, paths, &tree, name, ""); + hit = grep_tree(opt, pathspec, &tree, &base, base.len); + strbuf_release(&base); free(data); return hit; } - die("unable to grep from object of type %s", typename(obj->type)); + die(_("unable to grep from object of type %s"), typename(obj->type)); } -static int grep_objects(struct grep_opt *opt, const char **paths, +static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_array *list) { unsigned int i; @@ -708,7 +628,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths, for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); - if (grep_object(opt, paths, real_obj, list->objects[i].name)) { + if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) { hit = 1; if (opt->status_only) break; @@ -717,7 +637,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths, return hit; } -static int grep_directory(struct grep_opt *opt, const char **paths) +static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec) { struct dir_struct dir; int i, hit = 0; @@ -725,8 +645,12 @@ static int grep_directory(struct grep_opt *opt, const char **paths) memset(&dir, 0, sizeof(dir)); setup_standard_excludes(&dir); - fill_directory(&dir, paths); + fill_directory(&dir, pathspec->raw); for (i = 0; i < dir.nr; i++) { + const char *name = dir.entries[i]->name; + int namelen = strlen(name); + if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL)) + continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) break; @@ -747,7 +671,7 @@ static int context_callback(const struct option *opt, const char *arg, } value = strtol(arg, (char **)&endp, 10); if (*endp) { - return error("switch `%c' expects a numerical value", + return error(_("switch `%c' expects a numerical value"), opt->short_name); } grep_opt->pre_context = grep_opt->post_context = value; @@ -757,13 +681,14 @@ static int context_callback(const struct option *opt, const char *arg, static int file_callback(const struct option *opt, const char *arg, int unset) { struct grep_opt *grep_opt = opt->value; + int from_stdin = !strcmp(arg, "-"); FILE *patterns; int lno = 0; struct strbuf sb = STRBUF_INIT; - patterns = fopen(arg, "r"); + patterns = from_stdin ? stdin : fopen(arg, "r"); if (!patterns) - die_errno("cannot open '%s'", arg); + die_errno(_("cannot open '%s'"), arg); while (strbuf_getline(&sb, patterns, '\n') == 0) { char *s; size_t len; @@ -775,7 +700,8 @@ static int file_callback(const struct option *opt, const char *arg, int unset) s = strbuf_detach(&sb, &len); append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN); } - fclose(patterns); + if (!from_stdin) + fclose(patterns); strbuf_release(&sb); return 0; } @@ -831,10 +757,20 @@ int cmd_grep(int argc, const char **argv, const char *prefix) struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; const char **paths = NULL; + struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; 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"), @@ -856,13 +792,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), @@ -881,6 +822,10 @@ 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", "show <n> context lines before and after matches", @@ -968,6 +913,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 */ @@ -1004,19 +971,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } if (!opt.pattern_list) - die("no pattern given."); + 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) + skip_first_line = 1; start_threads(&opt); } #else @@ -1033,7 +998,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) - die("bad object %s", arg); + die(_("bad object %s"), arg); add_object_array(object, arg, &list); continue; } @@ -1051,16 +1016,13 @@ 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; if (show_in_pager && (cached || list.nr)) - die("--open-files-in-pager only works on the worktree"); + die(_("--open-files-in-pager only works on the worktree")); if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) { const char *pager = path_list.items[0].string; @@ -1085,19 +1047,19 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!use_index) { if (cached) - die("--cached cannot be used with --no-index."); + die(_("--cached cannot be used with --no-index.")); if (list.nr) - die("--no-index cannot be used with revs."); - hit = grep_directory(&opt, paths); + die(_("--no-index cannot be used with revs.")); + hit = grep_directory(&opt, &pathspec); } else if (!list.nr) { if (!cached) setup_work_tree(); - hit = grep_cache(&opt, paths, cached); + hit = grep_cache(&opt, &pathspec, cached); } else { if (cached) - die("both --cached and trees are given."); - hit = grep_objects(&opt, paths, &list); + die(_("both --cached and trees are given.")); + hit = grep_objects(&opt, &pathspec, &list); } if (use_threads) diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 080af1a01b..33911fd5e9 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -4,7 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 * Copyright (C) Junio C Hamano, 2005 */ -#include "cache.h" +#include "builtin.h" #include "blob.h" #include "quote.h" #include "parse-options.h" @@ -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)) + 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 c7e600db47..0945adbb3b 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; @@ -207,7 +210,7 @@ static void parse_pack_header(void) static NORETURN void bad_object(unsigned long offset, const char *format, ...) __attribute__((format (printf, 2, 3))); -static void bad_object(unsigned long offset, const char *format, ...) +static NORETURN void bad_object(unsigned long offset, const char *format, ...) { va_list params; char buf[1024]; @@ -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)); @@ -294,7 +297,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ void *data; obj->idx.offset = consumed_bytes; - input_crc32 = crc32(0, Z_NULL, 0); + input_crc32 = crc32(0, NULL, 0); p = fill(1); c = *p; @@ -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,17 @@ 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; + } 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 +1168,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 fbeb380ee2..025aa47c80 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -21,6 +21,7 @@ static int init_is_bare_repository = 0; static int init_shared_repository = -1; static const char *init_db_template_dir; +static const char *git_link; static void safe_create_dir(const char *dir, int share) { @@ -31,7 +32,7 @@ static void safe_create_dir(const char *dir, int share) } } else if (share && adjust_shared_perm(dir)) - die("Could not make %s writable by group", dir); + die(_("Could not make %s writable by group"), dir); } static void copy_templates_1(char *path, int baselen, @@ -58,25 +59,25 @@ static void copy_templates_1(char *path, int baselen, namelen = strlen(de->d_name); if ((PATH_MAX <= baselen + namelen) || (PATH_MAX <= template_baselen + namelen)) - die("insanely long template name %s", de->d_name); + die(_("insanely long template name %s"), de->d_name); memcpy(path + baselen, de->d_name, namelen+1); memcpy(template + template_baselen, de->d_name, namelen+1); if (lstat(path, &st_git)) { if (errno != ENOENT) - die_errno("cannot stat '%s'", path); + die_errno(_("cannot stat '%s'"), path); } else exists = 1; if (lstat(template, &st_template)) - die_errno("cannot stat template '%s'", template); + die_errno(_("cannot stat template '%s'"), template); if (S_ISDIR(st_template.st_mode)) { DIR *subdir = opendir(template); int baselen_sub = baselen + namelen; int template_baselen_sub = template_baselen + namelen; if (!subdir) - die_errno("cannot opendir '%s'", template); + die_errno(_("cannot opendir '%s'"), template); path[baselen_sub++] = template[template_baselen_sub++] = '/'; path[baselen_sub] = @@ -93,20 +94,20 @@ static void copy_templates_1(char *path, int baselen, int len; len = readlink(template, lnk, sizeof(lnk)); if (len < 0) - die_errno("cannot readlink '%s'", template); + die_errno(_("cannot readlink '%s'"), template); if (sizeof(lnk) <= len) - die("insanely long symlink %s", template); + die(_("insanely long symlink %s"), template); lnk[len] = 0; if (symlink(lnk, path)) - die_errno("cannot symlink '%s' '%s'", lnk, path); + die_errno(_("cannot symlink '%s' '%s'"), lnk, path); } else if (S_ISREG(st_template.st_mode)) { if (copy_file(path, template, st_template.st_mode)) - die_errno("cannot copy '%s' to '%s'", template, + die_errno(_("cannot copy '%s' to '%s'"), template, path); } else - error("ignoring template %s", template); + error(_("ignoring template %s"), template); } } @@ -129,7 +130,7 @@ static void copy_templates(const char *template_dir) return; template_len = strlen(template_dir); if (PATH_MAX <= (template_len+strlen("/config"))) - die("insanely long template path %s", template_dir); + die(_("insanely long template path %s"), template_dir); strcpy(template_path, template_dir); if (template_path[template_len-1] != '/') { template_path[template_len++] = '/'; @@ -137,7 +138,7 @@ static void copy_templates(const char *template_dir) } dir = opendir(template_path); if (!dir) { - warning("templates not found %s", template_dir); + warning(_("templates not found %s"), template_dir); return; } @@ -150,8 +151,8 @@ static void copy_templates(const char *template_dir) if (repository_format_version && repository_format_version != GIT_REPO_VERSION) { - warning("not copying templates of " - "a wrong format version %d from '%s'", + warning(_("not copying templates of " + "a wrong format version %d from '%s'"), repository_format_version, template_dir); closedir(dir); @@ -188,7 +189,7 @@ static int create_default_files(const char *template_path) int filemode; if (len > sizeof(path)-50) - die("insane git directory %s", git_dir); + die(_("insane git directory %s"), git_dir); memcpy(path, git_dir, len); if (len && path[len-1] != '/') @@ -311,11 +312,67 @@ static void create_object_directory(void) free(path); } +int set_git_dir_init(const char *git_dir, const char *real_git_dir, + int exist_ok) +{ + if (real_git_dir) { + struct stat st; + + if (!exist_ok && !stat(git_dir, &st)) + die(_("%s already exists"), git_dir); + + if (!exist_ok && !stat(real_git_dir, &st)) + die(_("%s already exists"), real_git_dir); + + /* + * make sure symlinks are resolved because we'll be + * moving the target repo later on in separate_git_dir() + */ + git_link = xstrdup(real_path(git_dir)); + } + else { + real_git_dir = real_path(git_dir); + git_link = NULL; + } + set_git_dir(real_path(real_git_dir)); + return 0; +} + +static void separate_git_dir(const char *git_dir) +{ + struct stat st; + FILE *fp; + + if (!stat(git_link, &st)) { + const char *src; + + if (S_ISREG(st.st_mode)) + src = read_gitfile_gently(git_link); + else if (S_ISDIR(st.st_mode)) + src = git_link; + else + die(_("unable to handle file type %d"), st.st_mode); + + if (rename(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); + fprintf(fp, "gitdir: %s\n", git_dir); + fclose(fp); +} + int init_db(const char *template_dir, unsigned int flags) { int reinit; + const char *git_dir = get_git_dir(); + + if (git_link) + separate_git_dir(git_dir); - safe_create_dir(get_git_dir(), 0); + safe_create_dir(git_dir, 0); init_is_bare_repository = is_bare_repository(); @@ -352,11 +409,16 @@ int init_db(const char *template_dir, unsigned int flags) } if (!(flags & INIT_DB_QUIET)) { - const char *git_dir = get_git_dir(); int len = strlen(git_dir); - printf("%s%s Git repository in %s%s\n", - reinit ? "Reinitialized existing" : "Initialized empty", - shared_repository ? " shared" : "", + + /* + * TRANSLATORS: The first '%s' is either "Reinitialized + * existing" or "Initialized empty", the second " shared" or + * "", and the last '%s%s' is the verbatim directory name. + */ + printf(_("%s%s Git repository in %s%s\n"), + reinit ? _("Reinitialized existing") : _("Initialized empty"), + shared_repository ? _(" shared") : "", git_dir, len && git_dir[len-1] != '/' ? "/" : ""); } @@ -375,7 +437,7 @@ static int guess_repository_type(const char *git_dir) if (!strcmp(".", git_dir)) return 1; if (!getcwd(cwd, sizeof(cwd))) - die_errno("cannot tell cwd"); + die_errno(_("cannot tell cwd")); if (!strcmp(git_dir, cwd)) return 1; /* @@ -414,6 +476,7 @@ static const char *const init_db_usage[] = { int cmd_init_db(int argc, const char **argv, const char *prefix) { const char *git_dir; + const char *real_git_dir = NULL; const char *work_tree; const char *template_dir = NULL; unsigned int flags = 0; @@ -427,11 +490,16 @@ 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(0, "separate-git-dir", &real_git_dir, "gitdir", + "separate git dir from working tree"), OPT_END() }; argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); + if (real_git_dir && !is_absolute_path(real_git_dir)) + real_git_dir = xstrdup(real_path(real_git_dir)); + if (argc == 1) { int mkdir_tried = 0; retry: @@ -450,18 +518,18 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) errno = EEXIST; /* fallthru */ case -1: - die_errno("cannot mkdir %s", argv[0]); + die_errno(_("cannot mkdir %s"), argv[0]); break; default: break; } shared_repository = saved; if (mkdir(argv[0], 0777) < 0) - die_errno("cannot mkdir %s", argv[0]); + die_errno(_("cannot mkdir %s"), argv[0]); mkdir_tried = 1; goto retry; } - die_errno("cannot chdir to %s", argv[0]); + die_errno(_("cannot chdir to %s"), argv[0]); } } else if (0 < argc) { usage(init_db_usage[0]); @@ -483,8 +551,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) git_dir = getenv(GIT_DIR_ENVIRONMENT); work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT); if ((!git_dir || is_bare_repository_cfg == 1) && work_tree) - die("%s (or --work-tree=<directory>) not allowed without " - "specifying %s (or --git-dir=<directory>)", + die(_("%s (or --work-tree=<directory>) not allowed without " + "specifying %s (or --git-dir=<directory>)"), GIT_WORK_TREE_ENVIRONMENT, GIT_DIR_ENVIRONMENT); @@ -501,28 +569,28 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir_parent = strrchr(git_dir, '/'); if (git_dir_parent) { char *rel = xstrndup(git_dir, git_dir_parent - git_dir); - git_work_tree_cfg = xstrdup(make_absolute_path(rel)); + git_work_tree_cfg = xstrdup(real_path(rel)); free(rel); } if (!git_work_tree_cfg) { git_work_tree_cfg = xcalloc(PATH_MAX, 1); if (!getcwd(git_work_tree_cfg, PATH_MAX)) - die_errno ("Cannot access current working directory"); + die_errno (_("Cannot access current working directory")); } if (work_tree) - set_git_work_tree(make_absolute_path(work_tree)); + set_git_work_tree(real_path(work_tree)); else set_git_work_tree(git_work_tree_cfg); if (access(get_git_work_tree(), X_OK)) - die_errno ("Cannot access work tree '%s'", + die_errno (_("Cannot access work tree '%s'"), get_git_work_tree()); } else { if (work_tree) - set_git_work_tree(make_absolute_path(work_tree)); + set_git_work_tree(real_path(work_tree)); } - set_git_dir(make_absolute_path(git_dir)); + set_git_dir_init(git_dir, real_git_dir, 1); return init_db(template_dir, flags); } diff --git a/builtin/log.c b/builtin/log.c index 9a15d69617..5c2af59004 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,66 @@ 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; +} + +static void cmd_log_init_defaults(struct rev_info *rev) +{ rev->abbrev = DEFAULT_ABBREV; rev->commit_format = CMIT_FMT_DEFAULT; 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); @@ -89,38 +126,24 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->always_show_header = 0; if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { rev->always_show_header = 0; - if (rev->diffopt.nr_paths != 1) + 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 +151,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. @@ -153,7 +183,7 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr) if (rev->commit_format != CMIT_FMT_ONELINE) putchar(rev->diffopt.line_termination); } - printf("Final output: %d %s\n", nr, stage); + printf(_("Final output: %d %s\n"), nr, stage); } static struct itimerval early_output_timer; @@ -247,12 +277,14 @@ 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); if (prepare_revision_walk(rev)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); if (rev->early_output) finish_early_output(rev); @@ -277,7 +309,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 +330,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")) { @@ -333,9 +376,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); } @@ -349,7 +394,7 @@ static int show_object(const unsigned char *sha1, int show_tag_object, int offset = 0; if (!buf) - return error("Could not read object %s", sha1_to_hex(sha1)); + return error(_("Could not read object %s"), sha1_to_hex(sha1)); if (show_tag_object) while (offset < size && buf[offset] != '\n') { @@ -396,6 +441,7 @@ 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); @@ -403,6 +449,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) 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; @@ -436,7 +483,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) break; o = parse_object(t->tagged->sha1); if (!o) - ret = error("Could not read object %s", + ret = error(_("Could not read object %s"), sha1_to_hex(t->tagged->sha1)); objects[i].item = o; i--; @@ -449,7 +496,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; @@ -460,7 +507,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) ret = cmd_log_walk(&rev); break; default: - ret = error("Unknown type: %d", o->type); + ret = error(_("Unknown type: %d"), o->type); } } free(objects); @@ -482,20 +529,15 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) 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); } @@ -560,7 +602,7 @@ static int git_format_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "format.headers")) { if (!value) - die("format.headers without value"); + die(_("format.headers without value")); add_header(value); return 0; } @@ -623,7 +665,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; @@ -632,18 +674,18 @@ static int reopen_stdout(struct commit *commit, struct rev_info *rev) strbuf_addstr(&filename, output_directory); if (filename.len >= PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) - return error("name of output directory is too long"); + return error(_("name of output directory is too long")); if (filename.buf[filename.len - 1] != '/') strbuf_addch(&filename, '/'); } 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) - return error("Cannot open patch file %s", filename.buf); + return error(_("Cannot open patch file %s"), filename.buf); strbuf_release(&filename); return 0; @@ -657,7 +699,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha unsigned flags1, flags2; if (rev->pending.nr != 2) - die("Need exactly one range."); + die(_("Need exactly one range.")); o1 = rev->pending.objects[0].item; flags1 = o1->flags; @@ -665,7 +707,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha flags2 = o2->flags; if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) - die("Not a range."); + die(_("Not a range.")); init_patch_ids(ids); @@ -676,7 +718,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha add_pending_object(&check_rev, o1, "o1"); add_pending_object(&check_rev, o2, "o2"); if (prepare_revision_walk(&check_rev)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); while ((commit = get_revision(&check_rev)) != NULL) { /* ignore merges */ @@ -702,7 +744,7 @@ static void gen_message_id(struct rev_info *info, char *base) const char *email_end = strrchr(committer, '>'); struct strbuf buf = STRBUF_INIT; if (!email_start || !email_end || email_start > email_end - 1) - die("Could not extract email from committer identity."); + die(_("Could not extract email from committer identity.")); strbuf_addf(&buf, "%s.%lu.git.%.*s", base, (unsigned long) time(NULL), (int)(email_end - email_start - 1), email_start + 1); @@ -718,13 +760,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,9 +773,10 @@ 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"); + die(_("Cover letter needs email format")); committer = git_committer_info(0); @@ -754,7 +796,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 +805,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 +813,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); @@ -827,7 +869,7 @@ static const char *clean_message_id(const char *msg_id) m++; } if (!z) - die("insane in-reply-to: %s", msg_id); + die(_("insane in-reply-to: %s"), msg_id); if (++z == m) return a; return xmemdupz(a, z - a); @@ -900,7 +942,7 @@ static int output_directory_callback(const struct option *opt, const char *arg, { const char **dir = (const char **)opt->value; if (*dir) - die("Two output directories?"); + die(_("Two output directories?")); *dir = arg; return 0; } @@ -995,6 +1037,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 +1093,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() }; @@ -1061,7 +1106,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; - rev.no_merges = 1; + rev.max_parents = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); @@ -1088,7 +1133,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) - die("bogus committer info %s", committer); + die(_("bogus committer info %s"), committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } @@ -1133,20 +1178,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) numbered = 0; if (numbered && keep_subject) - die ("-n and -k are mutually exclusive."); + die (_("-n and -k are mutually exclusive.")); if (keep_subject && subject_prefix) - die ("--subject-prefix and -k are mutually exclusive."); + 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) - die ("unrecognized argument: %s", argv[1]); + die (_("unrecognized argument: %s"), argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) - die("--name-only does not make sense"); + die(_("--name-only does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) - die("--name-status does not make sense"); + die(_("--name-status does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) - die("--check does not make sense"); + die(_("--check does not make sense")); if (!use_patch_format && (!rev.diffopt.output_format || @@ -1169,9 +1215,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (output_directory) { if (use_stdout) - die("standard output, or directory, which one?"); + die(_("standard output, or directory, which one?")); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) - die_errno("Could not create directory '%s'", + die_errno(_("Could not create directory '%s'"), output_directory); } @@ -1225,7 +1271,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { @@ -1259,7 +1305,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,8 +1351,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, - &rev)) - die("Failed to create output files"); + &rev, quiet)) + die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; @@ -1366,8 +1412,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); @@ -1410,9 +1455,9 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) if (!current_branch || !current_branch->merge || !current_branch->merge[0] || !current_branch->merge[0]->dst) { - fprintf(stderr, "Could not find a tracked" + fprintf(stderr, _("Could not find a tracked" " remote branch, please" - " specify <upstream> manually.\n"); + " specify <upstream> manually.\n")); usage_with_options(cherry_usage, options); } @@ -1426,9 +1471,9 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) DIFF_OPT_SET(&revs.diffopt, RECURSIVE); if (add_pending_commit(head, &revs, 0)) - die("Unknown commit %s", head); + die(_("Unknown commit %s"), head); if (add_pending_commit(upstream, &revs, UNINTERESTING)) - die("Unknown commit %s", upstream); + die(_("Unknown commit %s"), upstream); /* Don't say anything if head and upstream are the same. */ if (revs.pending.nr == 2) { @@ -1440,11 +1485,11 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) get_patch_ids(&revs, &ids, prefix); if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) - die("Unknown commit %s", limit); + die(_("Unknown commit %s"), limit); /* reverse the list of commits */ if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); while ((commit = get_revision(&revs)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index fb2d5f4b1f..15701233e2 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -338,7 +338,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 +360,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++) { diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 97eed4012b..10223092a9 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 @@ -33,7 +33,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) int i; const char *dest = NULL; unsigned flags = 0; + int get_url = 0; int quiet = 0; + int status = 0; const char *uploadpack = NULL; const char **pattern = NULL; @@ -69,6 +71,15 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) quiet = 1; continue; } + if (!strcmp("--get-url", arg)) { + get_url = 1; + continue; + } + if (!strcmp("--exit-code", arg)) { + /* return this code if no refs are reported */ + status = 2; + continue; + } usage(ls_remote_usage); } dest = arg; @@ -94,6 +105,12 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) } if (!remote->url_nr) die("remote %s has no configured URL", dest); + + if (get_url) { + printf("%s\n", *remote->url); + return 0; + } + transport = transport_get(remote, NULL); if (uploadpack != NULL) transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); @@ -110,6 +127,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..f08c5b0c94 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,14 @@ 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); + read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL); return 0; } 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-index.c b/builtin/merge-index.c index 2c4cf5e559..2338832587 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -1,6 +1,5 @@ -#include "cache.h" +#include "builtin.h" #include "run-command.h" -#include "exec_cmd.h" static const char *pgm; static int one_shot, quiet; diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index c33091b3ed..3a64f5d0bd 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -1,4 +1,4 @@ -#include "cache.h" +#include "builtin.h" #include "commit.h" #include "tag.h" #include "merge-recursive.h" diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 9b25ddc979..897a563bc6 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -1,8 +1,9 @@ -#include "cache.h" +#include "builtin.h" #include "tree-walk.h" #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 f9fb26dc33..325891edb6 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -25,6 +25,7 @@ #include "help.h" #include "merge-recursive.h" #include "resolve-undo.h" +#include "remote.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -37,8 +38,9 @@ struct strategy { }; static const char * const builtin_merge_usage[] = { - "git merge [options] <remote>...", - "git merge [options] <msg> HEAD <remote>", + "git merge [options] [<commit>...]", + "git merge [options] <msg> HEAD <commit>", + "git merge --abort", NULL }; @@ -54,10 +56,13 @@ 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; static int abort_current_merge; +static int show_progress = -1; +static int default_to_upstream; static struct strategy all_strategy[] = { { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, @@ -80,7 +85,7 @@ static int option_parse_message(const struct option *opt, strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg); have_message = 1; } else - return error("switch `m' requires a value"); + return error(_("switch `m' requires a value")); return 0; } @@ -117,13 +122,13 @@ static struct strategy *get_strategy(const char *name) exclude_cmds(&main_cmds, ¬_strategies); } if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) { - fprintf(stderr, "Could not find merge strategy '%s'.\n", name); - fprintf(stderr, "Available strategies are:"); + fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name); + fprintf(stderr, _("Available strategies are:")); for (i = 0; i < main_cmds.cnt; i++) fprintf(stderr, " %s", main_cmds.names[i]->name); fprintf(stderr, ".\n"); if (other_cmds.cnt) { - fprintf(stderr, "Available custom strategies are:"); + fprintf(stderr, _("Available custom strategies are:")); for (i = 0; i < other_cmds.cnt; i++) fprintf(stderr, " %s", other_cmds.names[i]->name); fprintf(stderr, ".\n"); @@ -200,6 +205,7 @@ static struct option builtin_merge_options[] = { OPT__VERBOSITY(&verbosity), OPT_BOOLEAN(0, "abort", &abort_current_merge, "abort the current in-progress merge"), + OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1), OPT_END() }; @@ -224,17 +230,17 @@ static void save_state(void) cp.git_cmd = 1; if (start_command(&cp)) - die("could not run stash."); + die(_("could not run stash.")); len = strbuf_read(&buffer, cp.out, 1024); close(cp.out); if (finish_command(&cp) || len < 0) - die("stash failed"); + die(_("stash failed")); else if (!len) return; strbuf_setlen(&buffer, buffer.len-1); if (get_sha1(buffer.buf, stash)) - die("not a valid object: %s", buffer.buf); + die(_("not a valid object: %s"), buffer.buf); } static void read_empty(unsigned const char *sha1, int verbose) @@ -252,7 +258,7 @@ static void read_empty(unsigned const char *sha1, int verbose) args[i] = NULL; if (run_command_v_opt(args, RUN_GIT_CMD)) - die("read-tree failed"); + die(_("read-tree failed")); } static void reset_hard(unsigned const char *sha1, int verbose) @@ -269,7 +275,7 @@ static void reset_hard(unsigned const char *sha1, int verbose) args[i] = NULL; if (run_command_v_opt(args, RUN_GIT_CMD)) - die("read-tree failed"); + die(_("read-tree failed")); } static void restore_state(void) @@ -298,7 +304,7 @@ static void restore_state(void) static void finish_up_to_date(const char *msg) { if (verbosity >= 0) - printf("%s%s\n", squash ? " (nothing to squash)" : "", msg); + printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg); drop_save(); } @@ -311,10 +317,10 @@ static void squash_message(void) int fd; struct pretty_print_context ctx = {0}; - printf("Squash commit -- not updating HEAD\n"); + printf(_("Squash commit -- not updating HEAD\n")); fd = open(git_path("SQUASH_MSG"), 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'"), git_path("SQUASH_MSG")); init_revisions(&rev, NULL); rev.ignore_merges = 1; @@ -329,22 +335,23 @@ static void squash_message(void) setup_revisions(0, NULL, &rev, NULL); if (prepare_revision_walk(&rev)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); 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"); + die_errno(_("Writing SQUASH_MSG")); if (close(fd)) - die_errno("Finishing SQUASH_MSG"); + die_errno(_("Finishing SQUASH_MSG")); strbuf_release(&out); } @@ -364,7 +371,7 @@ static void finish(const unsigned char *new_head, const char *msg) squash_message(); } else { if (verbosity >= 0 && !merge_msg.len) - printf("No merge message -- not updating HEAD\n"); + printf(_("No merge message -- not updating HEAD\n")); else { const char *argv_gc_auto[] = { "gc", "--auto", NULL }; update_ref(reflog_message.buf, "HEAD", @@ -386,7 +393,7 @@ static void finish(const unsigned char *new_head, const char *msg) if (diff_use_color_default > 0) DIFF_OPT_SET(&opts, COLOR_DIFF); if (diff_setup_done(&opts) < 0) - die("diff_setup_done failed"); + die(_("diff_setup_done failed")); diff_tree_sha1(head, new_head, "", &opts); diffcore_std(&opts); diff_flush(&opts); @@ -415,7 +422,7 @@ static void merge_name(const char *remote, struct strbuf *msg) memset(branch_head, 0, sizeof(branch_head)); remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); if (!remote_head) - die("'%s' does not point to a commit", remote); + die(_("'%s' does not point to a commit"), remote); if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { if (!prefixcmp(found_ref, "refs/heads/")) { @@ -480,7 +487,7 @@ static void merge_name(const char *remote, struct strbuf *msg) fp = fopen(git_path("FETCH_HEAD"), "r"); if (!fp) - die_errno("could not open '%s' for reading", + die_errno(_("could not open '%s' for reading"), git_path("FETCH_HEAD")); strbuf_getline(&line, fp, '\n'); fclose(fp); @@ -498,26 +505,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")) @@ -532,10 +547,22 @@ static int git_merge_config(const char *k, const char *v, void *cb) int is_bool; shortlog_len = git_config_bool_or_int(k, v, &is_bool); if (!is_bool && shortlog_len < 0) - return error("%s: negative length %s", k, v); + return error(_("%s: negative length %s"), k, v); 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; } return git_diff_ui_config(k, v, cb); } @@ -579,10 +606,19 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head, static void write_tree_trivial(unsigned char *sha1) { if (write_cache_as_tree(sha1, 0, NULL)) - die("git write-tree failed to write a tree"); + 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, struct commit_list *common, +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) { const char **args; @@ -601,11 +637,11 @@ int try_merge_command(const char *strategy, struct commit_list *common, 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); @@ -620,7 +656,7 @@ int try_merge_command(const char *strategy, struct commit_list *common, free(args); discard_cache(); if (read_cache() < 0) - die("failed to read the cache"); + die(_("failed to read the cache")); resolve_undo_clear(); return ret; @@ -637,7 +673,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(lock))) - return error("Unable to write index."); + return error(_("Unable to write index.")); rollback_lock_file(lock); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { @@ -650,7 +686,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, struct commit_list *j; if (remoteheads->next) { - error("Not handling anything other than two heads merge."); + error(_("Not handling anything other than two heads merge.")); return 2; } @@ -659,10 +695,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, o.subtree_shift = ""; o.renormalize = option_renormalize; + o.show_rename_progress = + show_progress == -1 ? isatty(2) : show_progress; for (x = 0; x < xopts_nr; x++) if (parse_merge_opt(&o, xopts[x])) - die("Unknown option for merge-recursive: -X%s", xopts[x]); + die(_("Unknown option for merge-recursive: -X%s"), xopts[x]); o.branch1 = head_arg; o.branch2 = remoteheads->item->util; @@ -676,11 +714,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(lock))) - die ("unable to write %s", get_index_file()); + die (_("unable to write %s"), get_index_file()); rollback_lock_file(lock); return clean ? 0 : 1; } else { - return try_merge_command(strategy, common, head_arg, remoteheads); + return try_merge_command(strategy, xopts_nr, xopts, + common, head_arg, remoteheads); } } @@ -747,7 +786,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote return -1; if (write_cache(fd, active_cache, active_nr) || commit_locked_index(lock_file)) - die("unable to write new index file"); + die(_("unable to write new index file")); return 0; } @@ -799,10 +838,10 @@ static void write_merge_msg(void) { int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); if (fd < 0) - die_errno("Could not open '%s' for writing", + 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")); + die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG")); close(fd); } @@ -810,7 +849,7 @@ static void read_merge_msg(void) { 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")); + die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG")); } static void run_prepare_commit_msg(void) @@ -827,7 +866,7 @@ static int merge_trivial(void) struct commit_list *parent = xmalloc(sizeof(*parent)); write_tree_trivial(result_tree); - printf("Wonderful.\n"); + printf(_("Wonderful.\n")); parent->item = lookup_commit(head); parent->next = xmalloc(sizeof(*parent->next)); parent->next->item = remoteheads->item; @@ -878,7 +917,7 @@ static int suggest_conflicts(int renormalizing) fp = fopen(git_path("MERGE_MSG"), "a"); if (!fp) - die_errno("Could not open '%s' for writing", + die_errno(_("Could not open '%s' for writing"), git_path("MERGE_MSG")); fprintf(fp, "\nConflicts:\n"); for (pos = 0; pos < active_nr; pos++) { @@ -894,8 +933,8 @@ static int suggest_conflicts(int renormalizing) } fclose(fp); rerere(allow_rerere_auto); - printf("Automatic merge failed; " - "fix conflicts and then commit the result.\n"); + printf(_("Automatic merge failed; " + "fix conflicts and then commit the result.\n")); return 1; } @@ -909,7 +948,7 @@ static struct commit *is_old_style_invocation(int argc, const char **argv) return NULL; second_token = lookup_commit_reference_gently(second_sha1, 0); if (!second_token) - die("'%s' is not a commit", argv[1]); + die(_("'%s' is not a commit"), argv[1]); if (hashcmp(second_token->object.sha1, head)) return NULL; } @@ -939,6 +978,35 @@ static int evaluate_result(void) return cnt; } +/* + * Pretend as if the user told us to merge with the tracking + * branch we have for the upstream of the current branch + */ +static int setup_with_upstream(const char ***argv) +{ + struct branch *branch = branch_get(NULL); + int i; + const char **args; + + if (!branch) + die(_("No current branch.")); + if (!branch->remote) + die(_("No remote for the current branch.")); + if (!branch->merge_nr) + die(_("No default upstream defined for the current branch.")); + + args = xcalloc(branch->merge_nr + 1, sizeof(char *)); + for (i = 0; i < branch->merge_nr; i++) { + if (!branch->merge[i]->dst) + die(_("No remote tracking branch for %s from %s"), + branch->merge[i]->src, branch->remote_name); + args[i] = branch->merge[i]->dst; + } + args[i] = NULL; + *argv = args; + return i; +} + int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; @@ -969,15 +1037,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix) 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); + if (verbosity < 0 && show_progress == -1) + show_progress = 0; + if (abort_current_merge) { int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; if (!file_exists(git_path("MERGE_HEAD"))) - die("There is no merge to abort (MERGE_HEAD missing)."); + die(_("There is no merge to abort (MERGE_HEAD missing).")); /* Invoke 'git reset --merge' */ return cmd_reset(nargc, nargv, prefix); @@ -992,10 +1065,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * add/rm <file>', just 'git commit'. */ if (advice_resolve_conflict) - die("You have not concluded your merge (MERGE_HEAD exists).\n" - "Please, commit your changes before you can merge."); + die(_("You have not concluded your merge (MERGE_HEAD exists).\n" + "Please, commit your changes before you can merge.")); + else + die(_("You have not concluded your merge (MERGE_HEAD exists).")); + } + 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.")); else - die("You have not concluded your merge (MERGE_HEAD exists)."); + die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } resolve_undo_clear(); @@ -1004,13 +1084,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (squash) { if (!allow_fast_forward) - die("You cannot combine --squash with --no-ff."); + die(_("You cannot combine --squash with --no-ff.")); option_commit = 0; } if (!allow_fast_forward && fast_forward_only) - die("You cannot combine --no-ff with --ff-only."); + die(_("You cannot combine --no-ff with --ff-only.")); + 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); @@ -1037,19 +1123,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * We do the same for "git pull". */ if (argc != 1) - die("Can merge only exactly one commit into " - "empty head"); + die(_("Can merge only exactly one commit into " + "empty head")); if (squash) - die("Squash commit into empty head not supported yet"); + die(_("Squash commit into empty head not supported yet")); if (!allow_fast_forward) - die("Non-fast-forward commit does not make sense into " - "an empty head"); + die(_("Non-fast-forward commit does not make sense into " + "an empty head")); remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT); if (!remote_head) - die("%s - not something we can merge", argv[0]); + die(_("%s - not something we can merge"), argv[0]); + read_empty(remote_head->sha1, 0); update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0, DIE_ON_ERR); - read_empty(remote_head->sha1, 0); return 0; } else { struct strbuf merge_names = STRBUF_INIT; @@ -1092,7 +1178,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT); if (!o) - die("%s - not something we can merge", argv[i]); + die(_("%s - not something we can merge"), argv[i]); commit = lookup_commit(o->sha1); commit->util = (void *)argv[i]; remotes = &commit_list_insert(commit, remotes)->next; @@ -1150,7 +1236,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV)); if (verbosity >= 0) - printf("Updating %s..%s\n", + printf(_("Updating %s..%s\n"), hex, find_unique_abbrev(remoteheads->item->object.sha1, DEFAULT_ABBREV)); @@ -1184,11 +1270,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (allow_trivial && !fast_forward_only) { /* See if it is really trivial. */ git_committer_info(IDENT_ERROR_ON_NO_NAME); - printf("Trying really trivial in-index merge...\n"); + printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, head, remoteheads->item->object.sha1)) return merge_trivial(); - printf("Nope.\n"); + printf(_("Nope.\n")); } } else { /* @@ -1221,7 +1307,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } if (fast_forward_only) - die("Not possible to fast-forward, aborting."); + die(_("Not possible to fast-forward, aborting.")); /* We are going to make a new commit. */ git_committer_info(IDENT_ERROR_ON_NO_NAME); @@ -1247,11 +1333,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix) for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { - printf("Rewinding the tree to pristine...\n"); + printf(_("Rewinding the tree to pristine...\n")); restore_state(); } if (use_strategies_nr != 1) - printf("Trying merge strategy %s...\n", + printf(_("Trying merge strategy %s...\n"), use_strategies[i]->name); /* * Remember which strategy left the state in the working @@ -1312,17 +1398,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix) restore_state(); if (use_strategies_nr > 1) fprintf(stderr, - "No merge strategy handled the merge.\n"); + _("No merge strategy handled the merge.\n")); else - fprintf(stderr, "Merge with strategy %s failed.\n", + fprintf(stderr, _("Merge with strategy %s failed.\n"), use_strategies[0]->name); return 2; } else if (best_strategy == wt_strategy) ; /* We already have its result in the working tree. */ else { - printf("Rewinding the tree to pristine...\n"); + printf(_("Rewinding the tree to pristine...\n")); restore_state(); - printf("Using the %s to prepare resolving by hand.\n", + printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); try_merge_strategy(best_strategy, common, head_arg); } @@ -1338,28 +1424,28 @@ int cmd_merge(int argc, const char **argv, const char *prefix) 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", + 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")); + 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", + 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")); + die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE")); close(fd); } if (merge_was_ok) { - fprintf(stderr, "Automatic merge went well; " - "stopped before committing as requested\n"); + fprintf(stderr, _("Automatic merge went well; " + "stopped before committing as requested\n")); return 0; } else return suggest_conflicts(option_renormalize); diff --git a/builtin/mktag.c b/builtin/mktag.c index 1cb0f3f2a7..640ab64f41 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -1,6 +1,5 @@ -#include "cache.h" +#include "builtin.h" #include "tag.h" -#include "exec_cmd.h" /* * A signature file has a very simple fixed format: four lines @@ -24,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)) @@ -35,12 +34,6 @@ static int verify_object(const unsigned char *sha1, const char *expected_type) return ret; } -#ifdef NO_C99_FORMAT -#define PD_FMT "%d" -#else -#define PD_FMT "%td" -#endif - static int verify_tag(char *buffer, unsigned long size) { int typelen; @@ -70,15 +63,18 @@ static int verify_tag(char *buffer, unsigned long size) /* Verify tag-line */ tag_line = strchr(type_line, '\n'); if (!tag_line) - return error("char" PD_FMT ": could not find next \"\\n\"", type_line - buffer); + return error("char%"PRIuMAX": could not find next \"\\n\"", + (uintmax_t) (type_line - buffer)); tag_line++; if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n') - return error("char" PD_FMT ": no \"tag \" found", tag_line - buffer); + return error("char%"PRIuMAX": no \"tag \" found", + (uintmax_t) (tag_line - buffer)); /* Get the actual type */ typelen = tag_line - type_line - strlen("type \n"); if (typelen >= sizeof(type)) - return error("char" PD_FMT ": type too long", type_line+5 - buffer); + return error("char%"PRIuMAX": type too long", + (uintmax_t) (type_line+5 - buffer)); memcpy(type, type_line+5, typelen); type[typelen] = 0; @@ -95,15 +91,16 @@ static int verify_tag(char *buffer, unsigned long size) break; if (c > ' ') continue; - return error("char" PD_FMT ": could not verify tag name", tag_line - buffer); + return error("char%"PRIuMAX": could not verify tag name", + (uintmax_t) (tag_line - buffer)); } /* Verify the tagger line */ tagger_line = tag_line; if (memcmp(tagger_line, "tagger ", 7)) - return error("char" PD_FMT ": could not find \"tagger \"", - tagger_line - buffer); + return error("char%"PRIuMAX": could not find \"tagger \"", + (uintmax_t) (tagger_line - buffer)); /* * Check for correct form for name and email @@ -115,44 +112,42 @@ static int verify_tag(char *buffer, unsigned long size) if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) || strpbrk(tagger_line, "<>\n") != lb+1 || strpbrk(lb+2, "><\n ") != rb) - return error("char" PD_FMT ": malformed tagger field", - tagger_line - buffer); + return error("char%"PRIuMAX": malformed tagger field", + (uintmax_t) (tagger_line - buffer)); /* Check for author name, at least one character, space is acceptable */ if (lb == tagger_line) - return error("char" PD_FMT ": missing tagger name", - tagger_line - buffer); + return error("char%"PRIuMAX": missing tagger name", + (uintmax_t) (tagger_line - buffer)); /* timestamp, 1 or more digits followed by space */ tagger_line = rb + 2; if (!(len = strspn(tagger_line, "0123456789"))) - return error("char" PD_FMT ": missing tag timestamp", - tagger_line - buffer); + return error("char%"PRIuMAX": missing tag timestamp", + (uintmax_t) (tagger_line - buffer)); tagger_line += len; if (*tagger_line != ' ') - return error("char" PD_FMT ": malformed tag timestamp", - tagger_line - buffer); + return error("char%"PRIuMAX": malformed tag timestamp", + (uintmax_t) (tagger_line - buffer)); tagger_line++; /* timezone, 5 digits [+-]hhmm, max. 1400 */ if (!((tagger_line[0] == '+' || tagger_line[0] == '-') && strspn(tagger_line+1, "0123456789") == 4 && tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400)) - return error("char" PD_FMT ": malformed tag timezone", - tagger_line - buffer); + return error("char%"PRIuMAX": malformed tag timezone", + (uintmax_t) (tagger_line - buffer)); tagger_line += 6; /* Verify the blank line separating the header from the body */ if (*tagger_line != '\n') - return error("char" PD_FMT ": trailing garbage in tag header", - tagger_line - buffer); + return error("char%"PRIuMAX": trailing garbage in tag header", + (uintmax_t) (tagger_line - buffer)); /* The actual stuff afterwards we don't care about.. */ return 0; } -#undef PD_FMT - int cmd_mktag(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; diff --git a/builtin/mv.c b/builtin/mv.c index 93e8995d9e..40f33ca4d0 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -74,7 +74,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) - die("index file corrupt"); + die(_("index file corrupt")); source = copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); @@ -100,17 +100,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix) const char *bad = NULL; if (show_only) - printf("Checking rename of '%s' to '%s'\n", src, dst); + printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) - bad = "bad source"; + bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { - bad = "can not move directory into itself"; + bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) - bad = "cannot move directory over file"; + bad = _("cannot move directory over file"); else if (src_is_dir) { const char *src_w_slash = add_slash(src); int len_w_slash = length + 1; @@ -120,7 +120,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) first = cache_name_pos(src_w_slash, len_w_slash); if (first >= 0) - die ("Huh? %.*s is in index?", + die (_("Huh? %.*s is in index?"), len_w_slash, src_w_slash); first = -1 - first; @@ -132,7 +132,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) free((char *)src_w_slash); if (last - first < 1) - bad = "source directory is empty"; + bad = _("source directory is empty"); else { int j, dst_len; @@ -163,22 +163,22 @@ int cmd_mv(int argc, const char **argv, const char *prefix) argc += last - first; } } else if (cache_name_pos(src, length) < 0) - bad = "not under version control"; + bad = _("not under version control"); else if (lstat(dst, &st) == 0) { - bad = "destination exists"; + bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - warning("%s; will overwrite!", bad); + warning(_("%s; will overwrite!"), bad); bad = NULL; } else - bad = "Cannot overwrite"; + bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) - bad = "multiple sources for the same target"; + bad = _("multiple sources for the same target"); else string_list_insert(&src_for_dst, dst); @@ -193,7 +193,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) i--; } } else - die ("%s, source=%s, destination=%s", + die (_("%s, source=%s, destination=%s"), bad, src, dst); } } @@ -203,10 +203,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix) enum update_mode mode = modes[i]; int pos; if (show_only || verbose) - printf("Renaming %s to %s\n", src, dst); + printf(_("Renaming %s to %s\n"), src, dst); if (!show_only && mode != INDEX && rename(src, dst) < 0 && !ignore_errors) - die_errno ("renaming '%s' failed", src); + die_errno (_("renaming '%s' failed"), src); if (mode == WORKING_DIRECTORY) continue; @@ -220,7 +220,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) - die("Unable to write new index file"); + die(_("Unable to write new index file")); } return 0; diff --git a/builtin/notes.c b/builtin/notes.c index 0aab150c52..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) @@ -146,13 +136,13 @@ static void write_commented_object(int fd, const unsigned char *object) show.err = 0; show.git_cmd = 1; if (start_command(&show)) - die("unable to start 'show' for object '%s'", + die(_("unable to start 'show' for object '%s'"), sha1_to_hex(object)); /* Open the output as FILE* so strbuf_getline() can be used. */ show_out = xfdopen(show.out, "r"); if (show_out == NULL) - die_errno("can't fdopen 'show' output fd"); + die_errno(_("can't fdopen 'show' output fd")); /* Prepend "# " to each output line and write result to 'fd' */ while (strbuf_getline(&buf, show_out, '\n') != EOF) { @@ -162,10 +152,10 @@ static void write_commented_object(int fd, const unsigned char *object) } strbuf_release(&buf); if (fclose(show_out)) - die_errno("failed to close pipe to 'show' for object '%s'", + die_errno(_("failed to close pipe to 'show' for object '%s'"), sha1_to_hex(object)); if (finish_command(&show)) - die("failed to finish 'show' for object '%s'", + die(_("failed to finish 'show' for object '%s'"), sha1_to_hex(object)); } @@ -182,7 +172,7 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, path = git_pathdup("NOTES_EDITMSG"); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) - die_errno("could not create file '%s'", path); + die_errno(_("could not create file '%s'"), path); if (msg->given) write_or_die(fd, msg->buf.buf, msg->buf.len); @@ -196,8 +186,8 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, strbuf_reset(&(msg->buf)); if (launch_editor(path, &(msg->buf), NULL)) { - die("Please supply the note contents using either -m" \ - " or -F option"); + die(_("Please supply the note contents using either -m" \ + " or -F option")); } stripspace(&(msg->buf), 1); } @@ -217,14 +207,14 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, } if (!msg->buf.len) { - fprintf(stderr, "Removing note for object %s\n", + fprintf(stderr, _("Removing note for object %s\n"), sha1_to_hex(object)); hashclr(result); } else { if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) { - error("unable to write note object"); + error(_("unable to write note object")); if (path) - error("The note contents has been left in %s", + error(_("The note contents has been left in %s"), path); exit(128); } @@ -258,9 +248,9 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset) strbuf_addch(&(msg->buf), '\n'); if (!strcmp(arg, "-")) { if (strbuf_read(&(msg->buf), 0, 1024) < 0) - die_errno("cannot read '%s'", arg); + die_errno(_("cannot read '%s'"), arg); } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0) - die_errno("could not open or read '%s'", arg); + die_errno(_("could not open or read '%s'"), arg); stripspace(&(msg->buf), 0); msg->given = 1; @@ -279,10 +269,10 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) strbuf_addch(&(msg->buf), '\n'); if (get_sha1(arg, object)) - die("Failed to resolve '%s' as a valid ref.", arg); + die(_("Failed to resolve '%s' as a valid ref."), arg); if (!(buf = read_sha1_file(object, &type, &len)) || !len) { free(buf); - die("Failed to read object '%s'.", arg);; + die(_("Failed to read object '%s'."), arg);; } strbuf_add(&(msg->buf), buf, len); free(buf); @@ -306,7 +296,7 @@ void commit_notes(struct notes_tree *t, const char *msg) if (!t) t = &default_notes_tree; if (!t->initialized || !t->ref || !*t->ref) - die("Cannot commit uninitialized/unreferenced notes tree"); + die(_("Cannot commit uninitialized/unreferenced notes tree")); if (!t->dirty) return; /* don't have to commit an unchanged tree */ @@ -347,7 +337,7 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb) config_error_nonbool(k); c->combine = parse_combine_notes_fn(v); if (!c->combine) { - error("Bad notes.rewriteMode value: '%s'", v); + error(_("Bad notes.rewriteMode value: '%s'"), v); return 1; } return 0; @@ -357,8 +347,8 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb) if (!prefixcmp(v, "refs/notes/")) string_list_add_refs_by_glob(c->refs, v); else - warning("Refusing to rewrite notes in %s" - " (outside of refs/notes/)", v); + warning(_("Refusing to rewrite notes in %s" + " (outside of refs/notes/)"), v); return 0; } @@ -382,8 +372,10 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd) c->mode_from_env = 1; c->combine = parse_combine_notes_fn(rewrite_mode_env); if (!c->combine) - error("Bad " GIT_NOTES_REWRITE_MODE_ENVIRONMENT - " value: '%s'", rewrite_mode_env); + /* TRANSLATORS: The first %s is the name of the + environment variable, the second %s is its value */ + error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT, + rewrite_mode_env); } if (rewrite_refs_env) { c->refs_from_env = 1; @@ -423,7 +415,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c) free(c); } -int notes_copy_from_stdin(int force, const char *rewrite_cmd) +static int notes_copy_from_stdin(int force, const char *rewrite_cmd) { struct strbuf buf = STRBUF_INIT; struct notes_rewrite_cfg *c = NULL; @@ -446,13 +438,13 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd) split = strbuf_split(&buf, ' '); if (!split[0] || !split[1]) - die("Malformed input line: '%s'.", buf.buf); + die(_("Malformed input line: '%s'."), buf.buf); strbuf_rtrim(split[0]); strbuf_rtrim(split[1]); if (get_sha1(split[0]->buf, from_obj)) - die("Failed to resolve '%s' as a valid ref.", split[0]->buf); + die(_("Failed to resolve '%s' as a valid ref."), split[0]->buf); if (get_sha1(split[1]->buf, to_obj)) - die("Failed to resolve '%s' as a valid ref.", split[1]->buf); + die(_("Failed to resolve '%s' as a valid ref."), split[1]->buf); if (rewrite_cmd) err = copy_note_for_rewrite(c, from_obj, to_obj); @@ -461,7 +453,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd) combine_notes_overwrite); if (err) { - error("Failed to copy notes from '%s' to '%s'", + error(_("Failed to copy notes from '%s' to '%s'"), split[0]->buf, split[1]->buf); ret = 1; } @@ -505,20 +497,20 @@ static int list(int argc, const char **argv, const char *prefix) git_notes_list_usage, 0); if (1 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_list_usage, options); } t = init_notes_check("list"); if (argc) { if (get_sha1(argv[0], object)) - die("Failed to resolve '%s' as a valid ref.", argv[0]); + die(_("Failed to resolve '%s' as a valid ref."), argv[0]); note = get_note(t, object); if (note) { puts(sha1_to_hex(note)); retval = 0; } else - retval = error("No note found for object %s.", + retval = error(_("No note found for object %s."), sha1_to_hex(object)); } else retval = for_each_note(t, 0, list_each_note, NULL); @@ -527,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; @@ -554,29 +548,41 @@ 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) { - error("too many parameters"); + 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); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("add"); note = get_note(t, object); if (note) { if (!force) { - retval = error("Cannot add notes. Found existing notes " + 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)); + "existing notes"), sha1_to_hex(object)); goto out; } - fprintf(stderr, "Overwriting existing notes for object %s\n", + fprintf(stderr, _("Overwriting existing notes for object %s\n"), sha1_to_hex(object)); } @@ -618,7 +624,7 @@ static int copy(int argc, const char **argv, const char *prefix) if (from_stdin || rewrite_cmd) { if (argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_copy_usage, options); } else { return notes_copy_from_stdin(force, rewrite_cmd); @@ -626,41 +632,41 @@ static int copy(int argc, const char **argv, const char *prefix) } if (argc < 2) { - error("too few parameters"); + error(_("too few parameters")); usage_with_options(git_notes_copy_usage, options); } if (2 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_copy_usage, options); } if (get_sha1(argv[0], from_obj)) - die("Failed to resolve '%s' as a valid ref.", argv[0]); + die(_("Failed to resolve '%s' as a valid ref."), argv[0]); object_ref = 1 < argc ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("copy"); note = get_note(t, object); if (note) { if (!force) { - retval = error("Cannot copy notes. Found existing " + retval = error(_("Cannot copy notes. Found existing " "notes for object %s. Use '-f' to " - "overwrite existing notes", + "overwrite existing notes"), sha1_to_hex(object)); goto out; } - fprintf(stderr, "Overwriting existing notes for object %s\n", + fprintf(stderr, _("Overwriting existing notes for object %s\n"), sha1_to_hex(object)); } from_note = get_note(t, from_obj); if (!from_note) { - retval = error("Missing notes on source object %s. Cannot " - "copy.", sha1_to_hex(from_obj)); + retval = error(_("Missing notes on source object %s. Cannot " + "copy."), sha1_to_hex(from_obj)); goto out; } @@ -703,19 +709,19 @@ static int append_edit(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_ARGV0); if (2 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(usage, options); } if (msg.given && edit) - fprintf(stderr, "The -m/-F/-c/-C options have been deprecated " + fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " "for the 'edit' subcommand.\n" - "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"); + "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); object_ref = 1 < argc ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check(argv[0]); note = get_note(t, object); @@ -750,20 +756,20 @@ static int show(int argc, const char **argv, const char *prefix) 0); if (1 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_show_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); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("show"); note = get_note(t, object); if (!note) - retval = error("No note found for object %s.", + retval = error(_("No note found for object %s."), sha1_to_hex(object)); else { const char *show_args[3] = {"show", sha1_to_hex(note), NULL}; @@ -819,7 +825,7 @@ static int merge_commit(struct notes_merge_options *o) t = xcalloc(1, sizeof(struct notes_tree)); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); - o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, 0); + o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL); if (!o->local_ref) die("Failed to resolve NOTES_MERGE_REF"); @@ -947,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; } @@ -999,7 +1025,7 @@ static int prune(int argc, const char **argv, const char *prefix) 0); if (argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_prune_usage, options); } @@ -1069,7 +1095,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[0], "get-ref")) result = get_ref(argc, argv, prefix); else { - result = error("Unknown subcommand: %s", argv[0]); + result = error(_("Unknown subcommand: %s"), argv[0]); usage_with_options(git_notes_usage, options); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index b0503b202a..84e6dafb12 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -70,6 +70,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; @@ -126,13 +127,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 +143,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 +161,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,12 +188,12 @@ 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; @@ -493,8 +494,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)); @@ -994,7 +995,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 +1143,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 +1885,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")) { @@ -2130,6 +2135,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 +2280,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/pack-redundant.c b/builtin/pack-redundant.c index 41e1615a28..f5c6afc5dd 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -6,8 +6,7 @@ * */ -#include "cache.h" -#include "exec_cmd.h" +#include "builtin.h" #define BLKSIZE 512 diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index 091860b2e3..39a9d89fbd 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -1,4 +1,4 @@ -#include "cache.h" +#include "builtin.h" #include "parse-options.h" #include "pack-refs.h" diff --git a/builtin/patch-id.c b/builtin/patch-id.c index 49a0472a9b..f821eb3f0b 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -1,5 +1,4 @@ -#include "cache.h" -#include "exec_cmd.h" +#include "builtin.h" static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c) { @@ -57,7 +56,7 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after) return 1; } -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) { static char line[1000]; int patchlen = 0, found_next = 0; diff --git a/builtin/push.c b/builtin/push.c index 31da418cf4..9cebf9ea23 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -40,7 +40,7 @@ static void set_refspecs(const char **refs, int nr) char *tag; int len; if (nr <= ++i) - die("tag shorthand without <tag>"); + die(_("tag shorthand without <tag>")); len = strlen(refs[i]) + 11; if (deleterefs) { tag = xmalloc(len+1); @@ -59,28 +59,38 @@ static void set_refspecs(const char **refs, int nr) strcat(delref, ref); ref = delref; } else if (deleterefs) - die("--delete only accepts plain target ref names"); + die(_("--delete only accepts plain target ref names")); add_refspec(ref); } } -static void setup_push_upstream(void) +static void setup_push_upstream(struct remote *remote) { struct strbuf refspec = STRBUF_INIT; struct branch *branch = branch_get(NULL); if (!branch) - die("You are not currently on a branch."); + die(_("You are not currently on a branch.\n" + "To push the history leading to the current (detached HEAD)\n" + "state now, use\n" + "\n" + " git push %s HEAD:<name-of-remote-branch>\n"), + remote->name); if (!branch->merge_nr || !branch->merge) - die("The current branch %s has no upstream branch.", + die(_("The current branch %s has no upstream branch.\n" + "To push the current branch and set the remote as upstream, use\n" + "\n" + " git push --set-upstream %s %s\n"), + branch->name, + remote->name, branch->name); if (branch->merge_nr != 1) - die("The current branch %s has multiple upstream branches, " - "refusing to push.", branch->name); + die(_("The current branch %s has multiple upstream branches, " + "refusing to push."), branch->name); strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); add_refspec(refspec.buf); } -static void setup_default_push_refspecs(void) +static void setup_default_push_refspecs(struct remote *remote) { switch (push_default) { default: @@ -89,7 +99,7 @@ static void setup_default_push_refspecs(void) break; case PUSH_DEFAULT_UPSTREAM: - setup_push_upstream(); + setup_push_upstream(remote); break; case PUSH_DEFAULT_CURRENT: @@ -97,8 +107,8 @@ static void setup_default_push_refspecs(void) break; case PUSH_DEFAULT_NOTHING: - die("You didn't specify any refspecs to push, and " - "push.default is \"nothing\"."); + die(_("You didn't specify any refspecs to push, and " + "push.default is \"nothing\".")); break; } } @@ -117,11 +127,11 @@ static int push_with_options(struct transport *transport, int flags) transport_set_option(transport, TRANS_OPT_THIN, "yes"); if (verbosity > 0) - fprintf(stderr, "Pushing to %s\n", transport->url); + fprintf(stderr, _("Pushing to %s\n"), transport->url); err = transport_push(transport, refspec_nr, refspec, flags, &nonfastforward); if (err != 0) - error("failed to push some refs to '%s'", transport->url); + error(_("failed to push some refs to '%s'"), transport->url); err |= transport_disconnect(transport); @@ -129,9 +139,9 @@ static int push_with_options(struct transport *transport, int flags) return 0; if (nonfastforward && advice_push_nonfastforward) { - fprintf(stderr, "To prevent you from losing history, non-fast-forward updates were rejected\n" + fprintf(stderr, _("To prevent you from losing history, non-fast-forward updates were rejected\n" "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n" - "'Note about fast-forwards' section of 'git push --help' for details.\n"); + "'Note about fast-forwards' section of 'git push --help' for details.\n")); } return 1; @@ -146,8 +156,15 @@ static int do_push(const char *repo, int flags) if (!remote) { if (repo) - die("bad repository '%s'", repo); - die("No destination configured to push to."); + die(_("bad repository '%s'"), repo); + die(_("No configured push destination.\n" + "Either specify the URL from the command-line or configure a remote repository using\n" + "\n" + " git remote add <name> <url>\n" + "\n" + "and then push using the remote name\n" + "\n" + " git push <name>\n")); } if (remote->mirror) @@ -155,19 +172,19 @@ static int do_push(const char *repo, int flags) if ((flags & TRANSPORT_PUSH_ALL) && refspec) { if (!strcmp(*refspec, "refs/tags/*")) - return error("--all and --tags are incompatible"); - return error("--all can't be combined with refspecs"); + return error(_("--all and --tags are incompatible")); + return error(_("--all can't be combined with refspecs")); } if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) { if (!strcmp(*refspec, "refs/tags/*")) - return error("--mirror and --tags are incompatible"); - return error("--mirror can't be combined with refspecs"); + return error(_("--mirror and --tags are incompatible")); + return error(_("--mirror can't be combined with refspecs")); } if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { - return error("--all and --mirror are incompatible"); + return error(_("--all and --mirror are incompatible")); } if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) { @@ -175,7 +192,7 @@ static int do_push(const char *repo, int flags) refspec = remote->push_refspec; refspec_nr = remote->push_refspec_nr; } else if (!(flags & TRANSPORT_PUSH_MIRROR)) - setup_default_push_refspecs(); + setup_default_push_refspecs(remote); } errs = 0; if (remote->pushurl_nr) { @@ -228,13 +245,14 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_END() }; + packet_trace_identity("push"); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, push_usage, 0); if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) - die("--delete is incompatible with --all, --mirror and --tags"); + die(_("--delete is incompatible with --all, --mirror and --tags")); if (deleterefs && argc < 2) - die("--delete doesn't make sense without any refs"); + die(_("--delete doesn't make sense without any refs")); if (tags) add_refspec("refs/tags/*"); 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 760817dbd7..e1a687ad07 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1,4 +1,4 @@ -#include "cache.h" +#include "builtin.h" #include "pack.h" #include "refs.h" #include "pkt-line.h" @@ -10,6 +10,7 @@ #include "remote.h" #include "transport.h" #include "string-list.h" +#include "sha1-array.h" static const char receive_pack_usage[] = "git receive-pack <git-dir>"; @@ -731,43 +732,23 @@ static int delete_only(struct command *commands) return 1; } -static int add_refs_from_alternate(struct alternate_object_database *e, void *unused) +static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused) { - char *other; - size_t len; - struct remote *remote; - struct transport *transport; - const struct ref *extra; - - e->name[-1] = '\0'; - other = xstrdup(make_absolute_path(e->base)); - e->name[-1] = '/'; - len = strlen(other); - - while (other[len-1] == '/') - other[--len] = '\0'; - if (len < 8 || memcmp(other + len - 8, "/objects", 8)) - return 0; - /* Is this a git repository with refs? */ - memcpy(other + len - 8, "/refs", 6); - if (!is_directory(other)) - return 0; - other[len - 8] = '\0'; - remote = remote_get(other); - transport = transport_get(remote, other); - for (extra = transport_get_remote_refs(transport); - extra; - extra = extra->next) { - add_extra_ref(".have", extra->old_sha1, 0); - } - transport_disconnect(transport); - free(other); - return 0; + add_extra_ref(".have", sha1, 0); +} + +static void collect_one_alternate_ref(const struct ref *ref, void *data) +{ + struct sha1_array *sa = data; + sha1_array_append(sa, ref->old_sha1); } static void add_alternate_refs(void) { - foreach_alt_odb(add_refs_from_alternate, NULL); + 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) @@ -778,6 +759,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) char *dir = NULL; struct command *commands; + packet_trace_identity("receive-pack"); + argv++; for (i = 1; i < argc; i++) { const char *arg = *argv++; diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index ea71977c83..692c834d9d 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -1,4 +1,4 @@ -#include "git-compat-util.h" +#include "builtin.h" #include "transport.h" #include "run-command.h" @@ -30,16 +30,12 @@ static char *strip_escapes(const char *str, const char *service, size_t rpos = 0; int escape = 0; char special = 0; - size_t pslen = 0; - size_t pSlen = 0; size_t psoff = 0; struct strbuf ret = STRBUF_INIT; /* Calculate prefix length for \s and lengths for \s and \S */ if (!strncmp(service, "git-", 4)) psoff = 4; - pSlen = strlen(service); - pslen = pSlen - psoff; /* Pass the service to command. */ setenv("GIT_EXT_SERVICE", service, 1); diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c index 1f2467bdb7..08d7121b6d 100644 --- a/builtin/remote-fd.c +++ b/builtin/remote-fd.c @@ -1,4 +1,4 @@ -#include "git-compat-util.h" +#include "builtin.h" #include "transport.h" /* diff --git a/builtin/remote.c b/builtin/remote.c index cb26080956..05b1f5b76d 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1,4 +1,4 @@ -#include "cache.h" +#include "builtin.h" #include "parse-options.h" #include "transport.h" #include "remote.h" @@ -9,7 +9,7 @@ static const char * const builtin_remote_usage[] = { "git remote [-v | --verbose]", - "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>", + "git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>", "git remote rename <old> <new>", "git remote rm <name>", "git remote set-head <name> (-a | -d | <branch>)", @@ -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 }; @@ -117,6 +107,11 @@ enum { TAGS_SET = 2 }; +#define MIRROR_NONE 0 +#define MIRROR_FETCH 1 +#define MIRROR_PUSH 2 +#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH) + static int add_branch(const char *key, const char *branchname, const char *remotename, int mirror, struct strbuf *tmp) { @@ -131,9 +126,32 @@ static int add_branch(const char *key, const char *branchname, return git_config_set_multivar(key, tmp->buf, "^$", 0); } +static const char mirror_advice[] = +"--mirror is dangerous and deprecated; please\n" +"\t use --mirror=fetch or --mirror=push instead"; + +static int parse_mirror_opt(const struct option *opt, const char *arg, int not) +{ + unsigned *mirror = opt->value; + if (not) + *mirror = MIRROR_NONE; + else if (!arg) { + warning("%s", mirror_advice); + *mirror = MIRROR_BOTH; + } + else if (!strcmp(arg, "fetch")) + *mirror = MIRROR_FETCH; + else if (!strcmp(arg, "push")) + *mirror = MIRROR_PUSH; + else + return error("unknown mirror argument: %s", arg); + return 0; +} + static int add(int argc, const char **argv) { - int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT; + int fetch = 0, fetch_tags = TAGS_DEFAULT; + unsigned mirror = MIRROR_NONE; struct string_list track = STRING_LIST_INIT_NODUP; const char *master = NULL; struct remote *remote; @@ -148,10 +166,12 @@ 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"), - OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"), + { OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch", + "set up remote as a mirror to push to or fetch from", + PARSE_OPT_OPTARG, parse_mirror_opt }, OPT_END() }; @@ -161,6 +181,11 @@ static int add(int argc, const char **argv) if (argc < 2) usage_with_options(builtin_remote_add_usage, options); + if (mirror && master) + die("specifying a master branch 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]; @@ -177,18 +202,19 @@ static int add(int argc, const char **argv) if (git_config_set(buf.buf, url)) return 1; - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", name); - - if (track.nr == 0) - string_list_append(&track, "*"); - for (i = 0; i < track.nr; i++) { - if (add_branch(buf.buf, track.items[i].string, - name, mirror, &buf2)) - return 1; + if (!mirror || mirror & MIRROR_FETCH) { + strbuf_reset(&buf); + strbuf_addf(&buf, "remote.%s.fetch", name); + if (track.nr == 0) + string_list_append(&track, "*"); + for (i = 0; i < track.nr; i++) { + if (add_branch(buf.buf, track.items[i].string, + name, mirror, &buf2)) + return 1; + } } - if (mirror) { + if (mirror & MIRROR_PUSH) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.mirror", name); if (git_config_set(buf.buf, "true")) diff --git a/builtin/rerere.c b/builtin/rerere.c index 642bf35587..08213c7c0b 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -8,78 +8,10 @@ #include "xdiff-interface.h" static const char * const rerere_usage[] = { - "git rerere [clear | status | diff | gc]", + "git rerere [clear | forget path... | status | remaining | diff | gc]", 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; @@ -136,7 +68,10 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) return rerere(flags); if (!strcmp(argv[0], "forget")) { - const char **pathspec = get_pathspec(prefix, argv + 1); + const char **pathspec; + if (argc < 2) + warning("'git rerere forget' without paths is deprecated"); + pathspec = get_pathspec(prefix, argv + 1); return rerere_forget(pathspec); } @@ -145,18 +80,23 @@ 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); - else if (!strcmp(argv[0], "diff")) + else if (!strcmp(argv[0], "remaining")) { + rerere_remaining(&merge_rr); + for (i = 0; i < merge_rr.nr; i++) { + if (merge_rr.items[i].util != RERERE_RESOLVED) + printf("%s\n", merge_rr.items[i].string); + else + /* prepare for later call to + * string_list_clear() */ + merge_rr.items[i].util = NULL; + } + } else if (!strcmp(argv[0], "diff")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff --git a/builtin/reset.c b/builtin/reset.c index 5de2bceeec..777e7c6129 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -7,7 +7,7 @@ * * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano */ -#include "cache.h" +#include "builtin.h" #include "tag.h" #include "object.h" #include "commit.h" @@ -30,7 +30,7 @@ static const char * const git_reset_usage[] = { enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP, NONE }; static const char *reset_type_names[] = { - "mixed", "soft", "hard", "merge", "keep", NULL + N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL }; static char *args_to_str(const char **argv) @@ -92,20 +92,20 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet if (reset_type == KEEP) { unsigned char head_sha1[20]; if (get_sha1("HEAD", head_sha1)) - return error("You do not have a valid HEAD."); + return error(_("You do not have a valid HEAD.")); if (!fill_tree_descriptor(desc, head_sha1)) - return error("Failed to find tree of HEAD."); + return error(_("Failed to find tree of HEAD.")); nr++; opts.fn = twoway_merge; } if (!fill_tree_descriptor(desc + nr - 1, sha1)) - return error("Failed to find tree of %s.", sha1_to_hex(sha1)); + return error(_("Failed to find tree of %s."), sha1_to_hex(sha1)); if (unpack_trees(nr, desc, &opts)) return -1; if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock)) - return error("Could not write new index file."); + return error(_("Could not write new index file.")); return 0; } @@ -115,7 +115,7 @@ static void print_new_head_line(struct commit *commit) const char *hex, *body; hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV); - printf("HEAD is now at %s", hex); + printf(_("HEAD is now at %s"), hex); body = strstr(commit->buffer, "\n\n"); if (body) { const char *eol; @@ -139,10 +139,10 @@ static int update_index_refresh(int fd, struct lock_file *index_lock, int flags) } if (read_cache() < 0) - return error("Could not read index"); + return error(_("Could not read index")); result = refresh_index(&the_index, (flags), NULL, NULL, - "Unstaged changes after reset:") ? 1 : 0; + _("Unstaged changes after reset:")) ? 1 : 0; if (write_cache(fd, active_cache, active_nr) || commit_locked_index(index_lock)) return error ("Could not refresh index"); @@ -162,12 +162,12 @@ 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); if (!ce) - die("make_cache_entry failed for path '%s'", + die(_("make_cache_entry failed for path '%s'"), one->path); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); @@ -222,14 +222,14 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size) if (!rla) rla = sep = ""; if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size) - warning("Reflog action message too long: %.*s...", 50, buf); + warning(_("Reflog action message too long: %.*s..."), 50, buf); } static void die_if_unmerged_cache(int reset_type) { if (is_merge() || read_cache() < 0 || unmerged_cache()) - die("Cannot do a %s reset in the middle of a merge.", - reset_type_names[reset_type]); + die(_("Cannot do a %s reset in the middle of a merge."), + _(reset_type_names[reset_type])); } @@ -300,16 +300,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix) } if (get_sha1(rev, sha1)) - die("Failed to resolve '%s' as a valid ref.", rev); + die(_("Failed to resolve '%s' as a valid ref."), rev); commit = lookup_commit_reference(sha1); if (!commit) - die("Could not parse object '%s'.", rev); + die(_("Could not parse object '%s'."), rev); hashcpy(sha1, commit->object.sha1); if (patch_mode) { if (reset_type != NONE) - die("--patch is incompatible with --{hard,mixed,soft}"); + die(_("--patch is incompatible with --{hard,mixed,soft}")); return interactive_reset(rev, argv + i, prefix); } @@ -318,10 +318,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) * affecting the working tree nor HEAD. */ if (i < argc) { if (reset_type == MIXED) - warning("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."); + warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); else if (reset_type != NONE) - die("Cannot do %s reset with paths.", - reset_type_names[reset_type]); + die(_("Cannot do %s reset with paths."), + _(reset_type_names[reset_type])); return read_from_tree(prefix, argv + i, sha1, quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); } @@ -332,8 +332,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) setup_work_tree(); if (reset_type == MIXED && is_bare_repository()) - die("%s reset is not allowed in a bare repository", - reset_type_names[reset_type]); + die(_("%s reset is not allowed in a bare repository"), + _(reset_type_names[reset_type])); /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset @@ -348,7 +348,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type == KEEP) err = err || reset_index_file(sha1, MIXED, quiet); if (err) - die("Could not reset index file to revision '%s'.", rev); + die(_("Could not reset index file to revision '%s'."), rev); } /* Any resets update HEAD to the head being switched to, diff --git a/builtin/rev-list.c b/builtin/rev-list.c index ba27d39f97..56727e8c1d 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -16,6 +16,10 @@ static const char rev_list_usage[] = " --min-age=<epoch>\n" " --sparse\n" " --no-merges\n" +" --min-parents=<n>\n" +" --no-min-parents\n" +" --max-parents=<n>\n" +" --no-max-parents\n" " --remove-empty\n" " --all\n" " --branches\n" @@ -51,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++; @@ -64,18 +70,8 @@ static void show_commit(struct commit *commit, void *data) if (info->header_prefix) fputs(info->header_prefix, stdout); - if (!revs->graph) { - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & UNINTERESTING) - putchar('^'); - else if (revs->left_right) { - if (commit->object.flags & SYMMETRIC_LEFT) - putchar('<'); - else - putchar('>'); - } - } + if (!revs->graph) + fputs(get_revision_mark(revs, commit), stdout); if (revs->abbrev_commit && revs->abbrev) fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev), stdout); @@ -108,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) @@ -412,8 +409,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 a5a1c86e92..4c19f844a9 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -44,10 +44,15 @@ static int is_rev_argument(const char *arg) "--branches=", "--branches", "--header", + "--ignore-missing", "--max-age=", "--max-count=", "--min-age=", "--no-merges", + "--min-parents=", + "--no-min-parents", + "--max-parents=", + "--no-max-parents", "--objects", "--objects-edge", "--parents", diff --git a/builtin/revert.c b/builtin/revert.c index bb6e9e83b7..1f27c63343 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -3,7 +3,6 @@ #include "object.h" #include "commit.h" #include "tag.h" -#include "wt-status.h" #include "run-command.h" #include "exec_cmd.h" #include "utf8.h" @@ -44,7 +43,11 @@ static const char **commit_argv; static int allow_rerere_auto; static const char *me; + +/* Merge strategy. */ static const char *strategy; +static const char **xopts; +static size_t xopts_nr, xopts_alloc; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -55,6 +58,17 @@ static const char * const *revert_or_cherry_pick_usage(void) return action == REVERT ? revert_usage : cherry_pick_usage; } +static int option_parse_x(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + return 0; + + ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); + xopts[xopts_nr++] = xstrdup(arg); + return 0; +} + static void parse_args(int argc, const char **argv) { const char * const * usage_str = revert_or_cherry_pick_usage(); @@ -62,11 +76,14 @@ static void parse_args(int argc, const char **argv) 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('r', NULL, &noop, "no-op (backward compatibility)"), + { 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", + "option for merge strategy", option_parse_x), OPT_END(), OPT_END(), OPT_END(), @@ -79,7 +96,7 @@ static void parse_args(int argc, const char **argv) OPT_END(), }; if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra)) - die("program error"); + die(_("program error")); } commit_argc = parse_options(argc, argv, NULL, options, usage_str, @@ -151,7 +168,7 @@ static char *get_encoding(const char *message) const char *p = message, *eol; if (!p) - die ("Could not read commit message of %s", + 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++) @@ -181,54 +198,20 @@ static void add_message_to_msg(struct strbuf *msgbuf, const char *message) strbuf_addstr(msgbuf, p); } -static void set_author_ident_env(const char *message) +static void write_cherry_pick_head(void) { - const char *p = message; - if (!p) - die ("Could not read commit message of %s", - sha1_to_hex(commit->object.sha1)); - while (*p && *p != '\n') { - const char *eol; - - for (eol = p; *eol && *eol != '\n'; eol++) - ; /* do nothing */ - if (!prefixcmp(p, "author ")) { - char *line, *pend, *email, *timestamp; - - p += 7; - line = xmemdupz(p, eol - p); - email = strchr(line, '<'); - if (!email) - die ("Could not extract author email from %s", - sha1_to_hex(commit->object.sha1)); - if (email == line) - pend = line; - else - for (pend = email; pend != line + 1 && - isspace(pend[-1]); pend--); - ; /* do nothing */ - *pend = '\0'; - email++; - timestamp = strchr(email, '>'); - if (!timestamp) - die ("Could not extract author time from %s", - sha1_to_hex(commit->object.sha1)); - *timestamp = '\0'; - for (timestamp++; *timestamp && isspace(*timestamp); - timestamp++) - ; /* do nothing */ - setenv("GIT_AUTHOR_NAME", line, 1); - setenv("GIT_AUTHOR_EMAIL", email, 1); - setenv("GIT_AUTHOR_DATE", timestamp, 1); - free(line); - return; - } - p = eol; - if (*p == '\n') - p++; - } - die ("No author information found in %s", - sha1_to_hex(commit->object.sha1)); + 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); + if (fd < 0) + die_errno(_("Could not open '%s' for writing"), + git_path("CHERRY_PICK_HEAD")); + 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")); + strbuf_release(&buf); } static void advise(const char *advice, ...) @@ -246,15 +229,18 @@ static void print_advice(void) if (msg) { fprintf(stderr, "%s\n", msg); + /* + * A conflict has occured but the porcelain + * (typically rebase --interactive) wants to take care + * of the commit itself so remove CHERRY_PICK_HEAD + */ + unlink(git_path("CHERRY_PICK_HEAD")); return; } advise("after resolving the conflicts, mark the corrected paths"); advise("with 'git add <paths>' or 'git rm <paths>'"); - - if (action == CHERRY_PICK) - advise("and commit the result with 'git commit -c %s'", - find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); + advise("and commit the result with 'git commit'"); } static void write_message(struct strbuf *msgbuf, const char *filename) @@ -264,10 +250,10 @@ 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); + die(_("Error wrapping up %s"), filename); } static struct tree *empty_tree(void) @@ -285,11 +271,19 @@ static NORETURN void die_dirty_index(const char *me) if (read_cache_unmerged()) { die_resolve_conflict(me); } else { - if (advice_commit_before_merge) - die("Your local changes would be overwritten by %s.\n" - "Please, commit your changes or stash them to proceed.", me); - else - die("Your local changes would be overwritten by %s.\n", me); + 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")); + } } } @@ -311,18 +305,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next, struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; int clean, index_fd; + const char **xopt; static struct lock_file index_lock; index_fd = hold_locked_index(&index_lock, 1); read_cache(); - /* - * NEEDSWORK: cherry-picking between branches with - * different end-of-line normalization is a pain; - * plumb in an option to set o.renormalize? - * (or better: arbitrary -X options) - */ init_merge_options(&o); o.ancestor = base ? base_label : "(empty tree)"; o.branch1 = "HEAD"; @@ -332,6 +321,9 @@ 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++) + parse_merge_opt(&o, *xopt); + clean = merge_trees(&o, head_tree, next_tree, base_tree, &result); @@ -339,7 +331,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next, 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); + /* TRANSLATORS: %s will be "revert" or "cherry-pick" */ + die(_("%s: Unable to write new index file"), me); rollback_lock_file(&index_lock); if (!clean) { @@ -405,18 +398,16 @@ static int do_pick_commit(void) * to work on. */ if (write_cache_as_tree(head, 0, NULL)) - die ("Your index file is unmerged."); + die (_("Your index file is unmerged.")); } else { if (get_sha1("HEAD", head)) - die ("You do not have a valid HEAD"); + die (_("You do not have a valid HEAD")); if (index_differs_from("HEAD", 0)) die_dirty_index(me); } discard_cache(); if (!commit->parents) { - if (action == REVERT) - die ("Cannot revert a root commit"); parent = NULL; } else if (commit->parents->next) { @@ -425,7 +416,7 @@ static int do_pick_commit(void) struct commit_list *p; if (!mainline) - die("Commit %s is a merge but no -m option was given.", + die(_("Commit %s is a merge but no -m option was given."), sha1_to_hex(commit->object.sha1)); for (cnt = 1, p = commit->parents; @@ -433,11 +424,11 @@ static int do_pick_commit(void) cnt++) p = p->next; if (cnt != mainline || !p) - die("Commit %s does not have parent %d", + die(_("Commit %s does not have parent %d"), sha1_to_hex(commit->object.sha1), mainline); parent = p->item; } else if (0 < mainline) - die("Mainline was specified but commit %s is not a merge.", + die(_("Mainline was specified but commit %s is not a merge."), sha1_to_hex(commit->object.sha1)); else parent = commit->parents->item; @@ -446,11 +437,13 @@ static int do_pick_commit(void) return fast_forward_to(commit->object.sha1, head); if (parent && parse_commit(parent) < 0) - die("%s: cannot parse parent commit %s", + /* 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)); if (get_message(commit->buffer, &msg) != 0) - die("Cannot get commit message for %s", + die(_("Cannot get commit message for %s"), sha1_to_hex(commit->object.sha1)); /* @@ -472,7 +465,7 @@ 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)); } @@ -482,13 +475,14 @@ static int do_pick_commit(void) base_label = msg.parent_label; next = commit; next_label = msg.label; - set_author_ident_env(msg.message); add_message_to_msg(&msgbuf, msg.message); if (no_replay) { 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) { @@ -503,15 +497,16 @@ static int do_pick_commit(void) commit_list_insert(base, &common); commit_list_insert(next, &remotes); - res = try_merge_command(strategy, common, + res = try_merge_command(strategy, xopts_nr, xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); } if (res) { - error("could not %s %s... %s", - action == REVERT ? "revert" : "apply", + error(action == REVERT + ? _("could not revert %s... %s") + : _("could not apply %s... %s"), find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), msg.subject); print_advice(); @@ -541,10 +536,10 @@ static void prepare_revs(struct rev_info *revs) usage(*revert_or_cherry_pick_usage()); if (prepare_revision_walk(revs)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); if (!revs->commits) - die("empty commit set passed"); + die(_("empty commit set passed")); } static void read_and_refresh_cache(const char *me) @@ -552,12 +547,12 @@ static void read_and_refresh_cache(const char *me) 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"), me); 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"), me); } rollback_lock_file(&index_lock); } @@ -573,13 +568,13 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (allow_ff) { if (signoff) - die("cherry-pick --ff cannot be used with --signoff"); + die(_("cherry-pick --ff cannot be used with --signoff")); if (no_commit) - die("cherry-pick --ff cannot be used with --no-commit"); + die(_("cherry-pick --ff cannot be used with --no-commit")); if (no_replay) - die("cherry-pick --ff cannot be used with -x"); + die(_("cherry-pick --ff cannot be used with -x")); if (edit) - die("cherry-pick --ff cannot be used with --edit"); + die(_("cherry-pick --ff cannot be used with --edit")); } read_and_refresh_cache(me); diff --git a/builtin/rm.c b/builtin/rm.c index ff491d7761..90c8a5047c 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -106,19 +106,19 @@ static int check_local_mod(unsigned char *head, int index_only) */ if (local_changes && staged_changes) { if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD)) - errs = error("'%s' has staged content different " + errs = error(_("'%s' has staged content different " "from both the file and the HEAD\n" - "(use -f to force removal)", name); + "(use -f to force removal)"), name); } else if (!index_only) { if (staged_changes) - errs = error("'%s' has changes staged in the index\n" + errs = error(_("'%s' has changes staged in the index\n" "(use --cached to keep the file, " - "or -f to force removal)", name); + "or -f to force removal)"), name); if (local_changes) - errs = error("'%s' has local modifications\n" + errs = error(_("'%s' has local modifications\n" "(use --cached to keep the file, " - "or -f to force removal)", name); + "or -f to force removal)"), name); } } return errs; @@ -159,7 +159,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) - die("index file corrupt"); + die(_("index file corrupt")); pathspec = get_pathspec(prefix, argv); refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); @@ -183,7 +183,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) for (i = 0; (match = pathspec[i]) != NULL ; i++) { if (!seen[i]) { if (!ignore_unmatch) { - die("pathspec '%s' did not match any files", + die(_("pathspec '%s' did not match any files"), match); } } @@ -191,7 +191,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) - die("not removing '%s' recursively without -r", + die(_("not removing '%s' recursively without -r"), *match ? match : "."); } @@ -227,7 +227,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) printf("rm '%s'\n", path); if (remove_file_from_cache(path)) - die("git rm: unable to remove %s", path); + die(_("git rm: unable to remove %s"), path); } if (show_only) @@ -257,7 +257,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) - die("Unable to write new index file"); + die(_("Unable to write new index file")); } return 0; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index fe07bb7ba3..c1f6ddd927 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -1,4 +1,4 @@ -#include "cache.h" +#include "builtin.h" #include "commit.h" #include "refs.h" #include "pkt-line.h" diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 1a21e4b053..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'); @@ -158,15 +154,16 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) buffer = eol; } if (!author) - die("Missing author: %s", + die(_("Missing author: %s"), 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++; @@ -181,7 +178,7 @@ static void get_from_rev(struct rev_info *rev, struct shortlog *log) struct commit *commit; if (prepare_revision_walk(rev)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); while ((commit = get_revision(rev)) != NULL) shortlog_add_commit(log, commit); } @@ -284,7 +281,7 @@ parse_done: argc = parse_options_end(&ctx); if (setup_revisions(argc, argv, &rev, NULL) != 1) { - error("unrecognized argument: %s", argv[1]); + error(_("unrecognized argument: %s"), argv[1]); usage_with_options(shortlog_usage, options); } @@ -296,7 +293,7 @@ parse_done: add_head_to_pending(&rev); if (rev.pending.nr == 0) { if (isatty(0)) - fprintf(stderr, "(reading log message from standard input)\n"); + fprintf(stderr, _("(reading log message from standard input)\n")); read_from_stdin(&log); } else diff --git a/builtin/show-branch.c b/builtin/show-branch.c index da695815e2..facc63a79e 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; @@ -37,7 +27,7 @@ static const char **default_arg; static const char *get_color_code(int idx) { if (showbranch_use_color) - return column_colors[idx]; + return column_colors_ansi[idx % column_colors_ansi_max]; 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] ")) @@ -892,7 +881,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 +943,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/tag.c b/builtin/tag.c index 7cf48abca8..667515e527 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; @@ -118,12 +170,12 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn) for (p = argv; *p; p++) { if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p) >= sizeof(ref)) { - error("tag name too long: %.*s...", 50, *p); + error(_("tag name too long: %.*s..."), 50, *p); had_error = 1; continue; } if (!resolve_ref(ref, sha1, 1, NULL)) { - error("tag '%s' not found.", *p); + error(_("tag '%s' not found."), *p); had_error = 1; continue; } @@ -138,7 +190,7 @@ static int delete_tag(const char *name, const char *ref, { if (delete_ref(ref, sha1, 0)) return 1; - printf("Deleted tag '%s' (was %s)\n", name, find_unique_abbrev(sha1, DEFAULT_ABBREV)); + printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV)); return 0; } @@ -150,7 +202,7 @@ static int verify_tag(const char *name, const char *ref, argv_verify_tag[2] = sha1_to_hex(sha1); if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD)) - return error("could not verify the tag '%s'", name); + return error(_("could not verify the tag '%s'"), name); return 0; } @@ -165,7 +217,7 @@ static int do_sign(struct strbuf *buffer) if (!*signingkey) { if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME), sizeof(signingkey)) > sizeof(signingkey) - 1) - return error("committer info too long."); + return error(_("committer info too long.")); bracket = strchr(signingkey, '>'); if (bracket) bracket[1] = '\0'; @@ -185,20 +237,20 @@ static int do_sign(struct strbuf *buffer) args[3] = NULL; if (start_command(&gpg)) - return error("could not run gpg."); + return error(_("could not run gpg.")); if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) { close(gpg.in); close(gpg.out); finish_command(&gpg); - return error("gpg did not accept the tag data"); + return error(_("gpg did not accept the tag data")); } close(gpg.in); len = strbuf_read(buffer, gpg.out, 1024); close(gpg.out); if (finish_command(&gpg) || !len || len < 0) - return error("gpg failed to sign the tag"); + return error(_("gpg failed to sign the tag")); /* Strip CR from the line endings, in case we are on Windows. */ for (i = j = 0; i < buffer->len; i++) @@ -213,15 +265,15 @@ static int do_sign(struct strbuf *buffer) } static const char tag_template[] = - "\n" + N_("\n" "#\n" "# Write a tag message\n" - "#\n"; + "#\n"); static void set_signingkey(const char *value) { if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey)) - die("signing key value too long (%.10s...)", value); + die(_("signing key value too long (%.10s...)"), value); } static int git_tag_config(const char *var, const char *value, void *cb) @@ -261,9 +313,9 @@ static void write_tag_body(int fd, const unsigned char *sha1) static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result) { if (sign && do_sign(buf) < 0) - return error("unable to sign the tag"); + return error(_("unable to sign the tag")); if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0) - return error("unable to write tag file"); + return error(_("unable to write tag file")); return 0; } @@ -278,7 +330,7 @@ static void create_tag(const unsigned char *object, const char *tag, type = sha1_object_info(object, NULL); if (type <= OBJ_NONE) - die("bad object type."); + die(_("bad object type.")); header_len = snprintf(header_buf, sizeof(header_buf), "object %s\n" @@ -291,7 +343,7 @@ static void create_tag(const unsigned char *object, const char *tag, git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (header_len > sizeof(header_buf) - 1) - die("tag header too big."); + die(_("tag header too big.")); if (!message) { int fd; @@ -300,17 +352,17 @@ static void create_tag(const unsigned char *object, const char *tag, path = git_pathdup("TAG_EDITMSG"); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) - die_errno("could not create file '%s'", path); + die_errno(_("could not create file '%s'"), path); if (!is_null_sha1(prev)) write_tag_body(fd, prev); else - write_or_die(fd, tag_template, strlen(tag_template)); + write_or_die(fd, _(tag_template), strlen(_(tag_template))); close(fd); if (launch_editor(path, buf, NULL)) { fprintf(stderr, - "Please supply the message using either -m or -F option.\n"); + _("Please supply the message using either -m or -F option.\n")); exit(1); } } @@ -318,13 +370,13 @@ static void create_tag(const unsigned char *object, const char *tag, stripspace(buf, 1); if (!message && !buf->len) - die("no tag message?"); + die(_("no tag message?")); strbuf_insert(buf, 0, header_buf, header_len); if (build_tag_object(buf, sign, result) < 0) { if (path) - fprintf(stderr, "The tag message has been left in %s\n", + fprintf(stderr, _("The tag message has been left in %s\n"), path); exit(128); } @@ -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 CHECK_REF_FORMAT_ERROR; + + strbuf_reset(sb); + strbuf_addf(sb, "refs/tags/%s", name); + + return check_ref_format(sb->buf); +} + 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; @@ -414,12 +477,12 @@ 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."); + die(_("-n option is only allowed with -l.")); if (with_commit) - die("--contains option is only allowed with -l."); + die(_("--contains option is only allowed with -l.")); if (delete) return for_each_tag_name(argv, delete_tag); if (verify) @@ -427,17 +490,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (msg.given || msgfile) { if (msg.given && msgfile) - die("only one -F or -m option is allowed."); + die(_("only one -F or -m option is allowed.")); annotate = 1; if (msg.given) strbuf_addbuf(&buf, &(msg.buf)); else { if (!strcmp(msgfile, "-")) { if (strbuf_read(&buf, 0, 1024) < 0) - die_errno("cannot read '%s'", msgfile); + die_errno(_("cannot read '%s'"), msgfile); } else { if (strbuf_read_file(&buf, msgfile, 1024) < 0) - die_errno("could not open or read '%s'", + die_errno(_("could not open or read '%s'"), msgfile); } } @@ -447,33 +510,32 @@ int cmd_tag(int argc, const char **argv, const char *prefix) object_ref = argc == 2 ? argv[1] : "HEAD"; if (argc > 2) - die("too many params"); + die(_("too many params")); if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + 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)) - die("'%s' is not a valid tag name.", tag); + 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); + die(_("tag '%s' already exists"), tag); if (annotate) 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)); + 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-file.c b/builtin/unpack-file.c index 608590ada8..19200291a2 100644 --- a/builtin/unpack-file.c +++ b/builtin/unpack-file.c @@ -1,6 +1,4 @@ -#include "cache.h" -#include "blob.h" -#include "exec_cmd.h" +#include "builtin.h" static char *create_temp_file(unsigned char *sha1) { 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 56baf27fb7..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)) @@ -546,7 +549,10 @@ static int do_reupdate(int ac, const char **av, */ int pos; int has_head = 1; - const char **pathspec = get_pathspec(prefix, av + 1); + const char **paths = get_pathspec(prefix, av + 1); + struct pathspec pathspec; + + init_pathspec(&pathspec, paths); if (read_ref("HEAD", head_sha1)) /* If there is no HEAD, that means it is an initial @@ -559,7 +565,7 @@ static int do_reupdate(int ac, const char **av, struct cache_entry *old = NULL; int save_nr; - if (ce_stage(ce) || !ce_path_match(ce, pathspec)) + if (ce_stage(ce) || !ce_path_match(ce, &pathspec)) continue; if (has_head) old = read_one_ent(NULL, head_sha1, @@ -578,6 +584,7 @@ static int do_reupdate(int ac, const char **av, if (save_nr != active_nr) goto redo; } + free_pathspec(&pathspec); return 0; } 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/var.c b/builtin/var.c index 0744bb8318..99d068a532 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -3,8 +3,7 @@ * * Copyright (C) Eric Biederman, 2005 */ -#include "cache.h" -#include "exec_cmd.h" +#include "builtin.h" static const char var_usage[] = "git var (-l | <variable>)"; 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; |