Code

Merge branch 'lt/unpack-trees'
[git.git] / builtin-reset.c
index 9626d4c54a812e0ad94204cfb434309da33b6a57..79424bb26efb4525b6bc2080e639b51d59eaabd9 100644 (file)
 #include "diff.h"
 #include "diffcore.h"
 #include "tree.h"
+#include "branch.h"
+#include "parse-options.h"
 
-static const char builtin_reset_usage[] =
-"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
+static const char * const git_reset_usage[] = {
+       "git-reset [--mixed | --soft | --hard] [-q] [<commit>]",
+       "git-reset [--mixed] <commit> [--] <paths>...",
+       NULL
+};
 
 static char *args_to_str(const char **argv)
 {
@@ -44,30 +49,6 @@ static inline int is_merge(void)
        return !access(git_path("MERGE_HEAD"), F_OK);
 }
 
-static int unmerged_files(void)
-{
-       char b;
-       ssize_t len;
-       struct child_process cmd;
-       const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
-
-       memset(&cmd, 0, sizeof(cmd));
-       cmd.argv = argv_ls_files;
-       cmd.git_cmd = 1;
-       cmd.out = -1;
-
-       if (start_command(&cmd))
-               die("Could not run sub-command: git ls-files");
-
-       len = xread(cmd.out, &b, 1);
-       if (len < 0)
-               die("Could not read output from git ls-files: %s",
-                                               strerror(errno));
-       finish_command(&cmd);
-
-       return len;
-}
-
 static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
 {
        int i = 0;
@@ -86,14 +67,10 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
 
 static void print_new_head_line(struct commit *commit)
 {
-       const char *hex, *dots = "...", *body;
+       const char *hex, *body;
 
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
-       if (!hex) {
-               hex = sha1_to_hex(commit->object.sha1);
-               dots = "";
-       }
-       printf("HEAD is now at %s%s", hex, dots);
+       printf("HEAD is now at %s", hex);
        body = strstr(commit->buffer, "\n\n");
        if (body) {
                const char *eol;
@@ -107,26 +84,33 @@ static void print_new_head_line(struct commit *commit)
                printf("\n");
 }
 
-static int update_index_refresh(void)
+static int update_index_refresh(int fd, struct lock_file *index_lock)
 {
-       const char *argv_update_index[] = {"update-index", "--refresh", NULL};
-       return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
-}
+       int result;
 
-struct update_cb_data {
-       int index_fd;
-       struct lock_file *lock;
-       int exit_code;
-};
+       if (!index_lock) {
+               index_lock = xcalloc(1, sizeof(struct lock_file));
+               fd = hold_locked_index(index_lock, 1);
+       }
+
+       if (read_cache() < 0)
+               return error("Could not read index");
+       result = refresh_cache(0) ? 1 : 0;
+       if (write_cache(fd, active_cache, active_nr) ||
+                       commit_locked_index(index_lock))
+               return error ("Could not refresh index");
+       return result;
+}
 
 static void update_index_from_diff(struct diff_queue_struct *q,
                struct diff_options *opt, void *data)
 {
        int i;
-       struct update_cb_data *cb = data;
+       int *discard_flag = data;
 
        /* do_diff_cache() mangled the index */
        discard_cache();
+       *discard_flag = 1;
        read_cache();
 
        for (i = 0; i < q->nr; i++) {
@@ -140,34 +124,34 @@ static void update_index_from_diff(struct diff_queue_struct *q,
                } else
                        remove_file_from_cache(one->path);
        }
-
-       cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) ||
-               close(cb->index_fd) ||
-               commit_locked_index(cb->lock);
 }
 
 static int read_from_tree(const char *prefix, const char **argv,
                unsigned char *tree_sha1)
 {
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       int index_fd, index_was_discarded = 0;
        struct diff_options opt;
-       struct update_cb_data cb;
 
        memset(&opt, 0, sizeof(opt));
        diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
-       opt.format_callback_data = &cb;
+       opt.format_callback_data = &index_was_discarded;
 
-       cb.lock = xcalloc(1, sizeof(struct lock_file));
-       cb.index_fd = hold_locked_index(cb.lock, 1);
-       cb.exit_code = 0;
+       index_fd = hold_locked_index(lock, 1);
+       index_was_discarded = 0;
        read_cache();
        if (do_diff_cache(tree_sha1, &opt))
                return 1;
        diffcore_std(&opt);
        diff_flush(&opt);
+       diff_tree_release_paths(&opt);
 
-       return cb.exit_code;
+       if (!index_was_discarded)
+               /* The index is still clobbered from do_diff_cache() */
+               discard_cache();
+       return update_index_refresh(index_fd, lock);
 }
 
 static void prepend_reflog_action(const char *action, char *buf, size_t size)
@@ -185,40 +169,31 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
-       int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
+       int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
        const char *rev = "HEAD";
        unsigned char sha1[20], *orig = NULL, sha1_orig[20],
                                *old_orig = NULL, sha1_old_orig[20];
        struct commit *commit;
        char *reflog_action, msg[1024];
+       const struct option options[] = {
+               OPT_SET_INT(0, "mixed", &reset_type,
+                                               "reset HEAD and index", MIXED),
+               OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+               OPT_SET_INT(0, "hard", &reset_type,
+                               "reset HEAD, index and working tree", HARD),
+               OPT_BOOLEAN('q', NULL, &quiet,
+                               "disable showing new HEAD in hard reset"),
+               OPT_END()
+       };
 
        git_config(git_default_config);
 
+       argc = parse_options(argc, argv, options, git_reset_usage,
+                                               PARSE_OPT_KEEP_DASHDASH);
        reflog_action = args_to_str(argv);
        setenv("GIT_REFLOG_ACTION", reflog_action, 0);
 
-       while (i < argc) {
-               if (!strcmp(argv[i], "--mixed")) {
-                       reset_type = MIXED;
-                       i++;
-               }
-               else if (!strcmp(argv[i], "--soft")) {
-                       reset_type = SOFT;
-                       i++;
-               }
-               else if (!strcmp(argv[i], "--hard")) {
-                       reset_type = HARD;
-                       i++;
-               }
-               else if (!strcmp(argv[i], "-q")) {
-                       quiet = 1;
-                       i++;
-               }
-               else
-                       break;
-       }
-
-       if (i < argc && argv[i][0] != '-')
+       if (i < argc && strcmp(argv[i], "--"))
                rev = argv[i++];
 
        if (get_sha1(rev, sha1))
@@ -231,8 +206,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (i < argc && !strcmp(argv[i], "--"))
                i++;
-       else if (i < argc && argv[i][0] == '-')
-               usage(builtin_reset_usage);
 
        /* git reset tree [--] paths... can be used to
         * load chosen paths from the tree into the index without
@@ -243,18 +216,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                else if (reset_type != NONE)
                        die("Cannot do %s reset with paths.",
                                        reset_type_names[reset_type]);
-               if (read_from_tree(prefix, argv + i, sha1))
-                       return 1;
-               return update_index_refresh() ? 1 : 0;
+               return read_from_tree(prefix, argv + i, sha1);
        }
        if (reset_type == NONE)
                reset_type = MIXED; /* by default */
 
+       if (reset_type == HARD && is_bare_repository())
+               die("hard reset makes no sense in a bare repository");
+
        /* Soft reset does not touch the index file nor the working tree
         * at all, but requires them in a good order.  Other resets reset
         * the index file to the tree object we are switching to. */
        if (reset_type == SOFT) {
-               if (is_merge() || unmerged_files())
+               if (is_merge() || read_cache() < 0 || unmerged_cache())
                        die("Cannot do a soft reset in the middle of a merge.");
        }
        else if (reset_index_file(sha1, (reset_type == HARD)))
@@ -282,14 +256,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        case SOFT: /* Nothing else to do. */
                break;
        case MIXED: /* Report what has not been updated. */
-               update_index_refresh();
+               update_index_refresh(0, NULL);
                break;
        }
 
-       unlink(git_path("MERGE_HEAD"));
-       unlink(git_path("rr-cache/MERGE_RR"));
-       unlink(git_path("MERGE_MSG"));
-       unlink(git_path("SQUASH_MSG"));
+       remove_branch_state();
 
        free(reflog_action);