Code

i18n: git-revert literal "me" messages
[git.git] / builtin / revert.c
index 54d13cf5ef87b7cfa29ac2c204fc6e30d5211a67..b0a5ffb18b84a1600589f36f7f4cac662b2788c9 100644 (file)
@@ -44,7 +44,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 +59,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();
@@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv)
                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,
@@ -102,9 +119,9 @@ struct commit_message {
 static int get_message(const char *raw_message, struct commit_message *out)
 {
        const char *encoding;
-       const char *p, *abbrev, *eol;
+       const char *abbrev, *subject;
+       int abbrev_len, subject_len;
        char *q;
-       int abbrev_len, oneline_len;
 
        if (!raw_message)
                return -1;
@@ -125,27 +142,17 @@ static int get_message(const char *raw_message, struct commit_message *out)
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
-       /* Find beginning and end of commit subject. */
-       p = out->message;
-       while (*p && (*p != '\n' || p[1] != '\n'))
-               p++;
-       if (*p) {
-               p += 2;
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-       } else
-               eol = p;
-       oneline_len = eol - p;
+       subject_len = find_commit_subject(out->message, &subject);
 
        out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
-                             strlen("... ") + oneline_len + 1);
+                             strlen("... ") + subject_len + 1);
        q = out->parent_label;
        q = mempcpy(q, "parent of ", strlen("parent of "));
        out->label = q;
        q = mempcpy(q, abbrev, abbrev_len);
        q = mempcpy(q, "... ", strlen("... "));
        out->subject = q;
-       q = mempcpy(q, p, oneline_len);
+       q = mempcpy(q, subject, subject_len);
        *q = '\0';
        return 0;
 }
@@ -161,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++)
@@ -195,7 +202,7 @@ static void set_author_ident_env(const char *message)
 {
        const char *p = message;
        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') {
                const char *eol;
@@ -209,7 +216,7 @@ static void set_author_ident_env(const char *message)
                        line = xmemdupz(p, eol - p);
                        email = strchr(line, '<');
                        if (!email)
-                               die ("Could not extract author email from %s",
+                               die (_("Could not extract author email from %s"),
                                        sha1_to_hex(commit->object.sha1));
                        if (email == line)
                                pend = line;
@@ -221,7 +228,7 @@ static void set_author_ident_env(const char *message)
                        email++;
                        timestamp = strchr(email, '>');
                        if (!timestamp)
-                               die ("Could not extract author time from %s",
+                               die (_("Could not extract author time from %s"),
                                        sha1_to_hex(commit->object.sha1));
                        *timestamp = '\0';
                        for (timestamp++; *timestamp && isspace(*timestamp);
@@ -237,31 +244,34 @@ static void set_author_ident_env(const char *message)
                if (*p == '\n')
                        p++;
        }
-       die ("No author information found in %s",
+       die (_("No author information found in %s"),
                        sha1_to_hex(commit->object.sha1));
 }
 
-static char *help_msg(void)
+static void advise(const char *advice, ...)
 {
-       struct strbuf helpbuf = STRBUF_INIT;
-       char *msg = getenv("GIT_CHERRY_PICK_HELP");
+       va_list params;
 
-       if (msg)
-               return msg;
+       va_start(params, advice);
+       vreportf("hint: ", advice, params);
+       va_end(params);
+}
 
-       strbuf_addstr(&helpbuf, "  After resolving the conflicts,\n"
-               "mark the corrected paths with 'git add <paths>' or 'git rm <paths>'\n"
-               "and commit the result");
+static void print_advice(void)
+{
+       char *msg = getenv("GIT_CHERRY_PICK_HELP");
 
-       if (action == CHERRY_PICK) {
-               strbuf_addf(&helpbuf, " with: \n"
-                       "\n"
-                       "        git commit -c %s\n",
-                           sha1_to_hex(commit->object.sha1));
+       if (msg) {
+               fprintf(stderr, "%s\n", msg);
+               return;
        }
-       else
-               strbuf_addch(&helpbuf, '.');
-       return strbuf_detach(&helpbuf, NULL);
+
+       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));
 }
 
 static void write_message(struct strbuf *msgbuf, const char *filename)
@@ -271,10 +281,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)
@@ -292,11 +302,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,19 +329,20 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
        return write_ref_sha1(ref_lock, to, "cherry-pick");
 }
 
-static void do_recursive_merge(struct commit *base, struct commit *next,
-                              const char *base_label, const char *next_label,
-                              unsigned char *head, struct strbuf *msgbuf,
-                              char *defmsg)
+static int do_recursive_merge(struct commit *base, struct commit *next,
+                             const char *base_label, const char *next_label,
+                             unsigned char *head, struct strbuf *msgbuf)
 {
        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();
+
        init_merge_options(&o);
        o.ancestor = base ? base_label : "(empty tree)";
        o.branch1 = "HEAD";
@@ -333,6 +352,9 @@ static void 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);
@@ -340,7 +362,8 @@ static void 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) {
@@ -357,14 +380,35 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
                                        i++;
                        }
                }
-               write_message(msgbuf, defmsg);
-               fprintf(stderr, "Automatic %s failed.%s\n",
-                       me, help_msg());
-               rerere(allow_rerere_auto);
-               exit(1);
        }
-       write_message(msgbuf, defmsg);
-       fprintf(stderr, "Finished one %s.\n", me);
+
+       return !clean;
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg)
+{
+       /* 6 is max possible length of our args array including NULL */
+       const char *args[6];
+       int i = 0;
+
+       args[i++] = "commit";
+       args[i++] = "-n";
+       if (signoff)
+               args[i++] = "-s";
+       if (!edit) {
+               args[i++] = "-F";
+               args[i++] = defmsg;
+       }
+       args[i] = NULL;
+
+       return run_command_v_opt(args, RUN_GIT_CMD);
 }
 
 static int do_pick_commit(void)
@@ -375,6 +419,7 @@ static int do_pick_commit(void)
        struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
+       int res;
 
        if (no_commit) {
                /*
@@ -384,10 +429,10 @@ 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);
        }
@@ -395,7 +440,7 @@ static int do_pick_commit(void)
 
        if (!commit->parents) {
                if (action == REVERT)
-                       die ("Cannot revert a root commit");
+                       die (_("Cannot revert a root commit"));
                parent = NULL;
        }
        else if (commit->parents->next) {
@@ -404,7 +449,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;
@@ -412,24 +457,26 @@ 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;
 
-       if (allow_ff && !hashcmp(parent->object.sha1, head))
+       if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
                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));
 
        /*
@@ -470,62 +517,40 @@ static int do_pick_commit(void)
                }
        }
 
-       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT)
-               do_recursive_merge(base, next, base_label, next_label,
-                                  head, &msgbuf, defmsg);
-       else {
-               int res;
+       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+               res = do_recursive_merge(base, next, base_label, next_label,
+                                        head, &msgbuf);
+               write_message(&msgbuf, defmsg);
+       } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
+
                write_message(&msgbuf, defmsg);
+
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res = try_merge_command(strategy, common,
+               res = try_merge_command(strategy, xopts_nr, xopts, common,
                                        sha1_to_hex(head), remotes);
                free_commit_list(common);
                free_commit_list(remotes);
-               if (res) {
-                       fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
-                               me, strategy, help_msg());
-                       rerere(allow_rerere_auto);
-                       exit(1);
-               }
        }
 
-       free_message(&msg);
-
-       /*
-        * If we are cherry-pick, and if the merge did not result in
-        * hand-editing, we will hit this commit and inherit the original
-        * author date and name.
-        * If we are revert, or if our cherry-pick results in a hand merge,
-        * we had better say that the current user is responsible for that.
-        */
-
-       if (!no_commit) {
-               /* 6 is max possible length of our args array including NULL */
-               const char *args[6];
-               int res;
-               int i = 0;
-
-               args[i++] = "commit";
-               args[i++] = "-n";
-               if (signoff)
-                       args[i++] = "-s";
-               if (!edit) {
-                       args[i++] = "-F";
-                       args[i++] = defmsg;
-               }
-               args[i] = NULL;
-               res = run_command_v_opt(args, RUN_GIT_CMD);
-               free(defmsg);
-
-               return res;
+       if (res) {
+               error("could not %s %s... %s",
+                     action == REVERT ? "revert" : "apply",
+                     find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+                     msg.subject);
+               print_advice();
+               rerere(allow_rerere_auto);
+       } else {
+               if (!no_commit)
+                       res = run_git_commit(defmsg);
        }
 
+       free_message(&msg);
        free(defmsg);
 
-       return 0;
+       return res;
 }
 
 static void prepare_revs(struct rev_info *revs)
@@ -542,10 +567,25 @@ 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)
+{
+       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);
+       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);
+       }
+       rollback_lock_file(&index_lock);
 }
 
 static int revert_or_cherry_pick(int argc, const char **argv)
@@ -559,17 +599,16 @@ 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"));
        }
 
-       if (read_cache() < 0)
-               die("git %s: failed to read the index", me);
+       read_and_refresh_cache(me);
 
        prepare_revs(&revs);