Merge branch 'jc/cachetree' into cr/reset
authorJunio C Hamano <gitster@pobox.com>
Fri, 14 Sep 2007 08:19:30 +0000 (01:19 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 14 Sep 2007 08:19:30 +0000 (01:19 -0700)
* jc/cachetree:
Simplify cache API
git-format-patch --in-reply-to: accept <message@id> with angle brackets
git-add -u: do not barf on type changes
Remove duplicate note about removing commits with git-filter-branch
git-clone: improve error message if curl program is missing or not executable
git.el: Allow the add and remove commands to be applied to ignored files.
git.el: Allow selecting whether to display uptodate/unknown/ignored files.
git.el: Keep the status buffer sorted by filename.
hooks--update: Explicitly check for all zeros for a deleted ref.

Makefile
builtin-reset.c [new file with mode: 0644]
builtin.h
cache.h
contrib/examples/git-reset.sh [new file with mode: 0755]
git-reset.sh [deleted file]
git.c
merge-recursive.c
read-cache.c
t/t7102-reset.sh [new file with mode: 0755]
index 78cdaa155ba424475fe091cd19cf212e32fcfc2f..e6740fc3928c239f0f16c10c661d190399882ca0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -208,7 +208,7 @@ SCRIPT_SH = \
        git-ls-remote.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
-       git-repack.sh git-request-pull.sh git-reset.sh \
+       git-repack.sh git-request-pull.sh \
        git-sh-setup.sh \
        git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@ -355,6 +355,7 @@ BUILTIN_OBJS = \
        builtin-reflog.o \
        builtin-config.o \
        builtin-rerere.o \
+       builtin-reset.o \
        builtin-rev-list.o \
        builtin-rev-parse.o \
        builtin-revert.o \
diff --git a/builtin-reset.c b/builtin-reset.c
new file mode 100644 (file)
index 0000000..99d5c08
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * "git reset" builtin command
+ *
+ * Copyright (c) 2007 Carlos Rica
+ *
+ * Based on git-reset.sh, which is
+ *
+ * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+ */
+#include "cache.h"
+#include "tag.h"
+#include "object.h"
+#include "commit.h"
+#include "run-command.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "tree.h"
+
+static const char builtin_reset_usage[] =
+"git-reset [--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]";
+
+static char *args_to_str(const char **argv)
+{
+       char *buf = NULL;
+       unsigned long len, space = 0, nr = 0;
+
+       for (; *argv; argv++) {
+               len = strlen(*argv);
+               ALLOC_GROW(buf, nr + 1 + len, space);
+               if (nr)
+                       buf[nr++] = ' ';
+               memcpy(buf + nr, *argv, len);
+               nr += len;
+       }
+       ALLOC_GROW(buf, nr + 1, space);
+       buf[nr] = '\0';
+
+       return buf;
+}
+
+static inline int is_merge(void)
+{
+       return !access(git_path("MERGE_HEAD"), F_OK);
+}
+
+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;
+       const char *args[6];
+
+       args[i++] = "read-tree";
+       args[i++] = "-v";
+       args[i++] = "--reset";
+       if (is_hard_reset)
+               args[i++] = "-u";
+       args[i++] = sha1_to_hex(sha1);
+       args[i] = NULL;
+
+       return run_command_v_opt(args, RUN_GIT_CMD);
+}
+
+static void print_new_head_line(struct commit *commit)
+{
+       const char *hex, *dots = "...", *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);
+       body = strstr(commit->buffer, "\n\n");
+       if (body) {
+               const char *eol;
+               size_t len;
+               body += 2;
+               eol = strchr(body, '\n');
+               len = eol ? eol - body : strlen(body);
+               printf(" %.*s\n", (int) len, body);
+       }
+       else
+               printf("\n");
+}
+
+static int update_index_refresh(void)
+{
+       const char *argv_update_index[] = {"update-index", "--refresh", NULL};
+       return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
+}
+
+static void update_index_from_diff(struct diff_queue_struct *q,
+               struct diff_options *opt, void *data)
+{
+       int i;
+
+       /* do_diff_cache() mangled the index */
+       discard_cache();
+       read_cache();
+
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filespec *one = q->queue[i]->one;
+               if (one->mode) {
+                       struct cache_entry *ce;
+                       ce = make_cache_entry(one->mode, one->sha1, one->path,
+                               0, 0);
+                       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
+                               ADD_CACHE_OK_TO_REPLACE);
+               } else
+                       remove_file_from_cache(one->path);
+       }
+}
+
+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;
+       struct diff_options opt;
+
+       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;
+
+       index_fd = hold_locked_index(lock, 1);
+       read_cache();
+       if (do_diff_cache(tree_sha1, &opt))
+               return 1;
+       diffcore_std(&opt);
+       diff_flush(&opt);
+       return write_cache(index_fd, active_cache, active_nr) ||
+               close(index_fd) ||
+               commit_locked_index(lock);
+}
+
+static void prepend_reflog_action(const char *action, char *buf, size_t size)
+{
+       const char *sep = ": ";
+       const char *rla = getenv("GIT_REFLOG_ACTION");
+       if (!rla)
+               rla = sep = "";
+       if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
+               warning("Reflog action message too long: %.*s...", 50, buf);
+}
+
+enum reset_type { MIXED, SOFT, HARD, NONE };
+static 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;
+       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];
+
+       git_config(git_default_config);
+
+       reflog_action = args_to_str(argv);
+       setenv("GIT_REFLOG_ACTION", reflog_action, 0);
+
+       if (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++;
+               }
+       }
+
+       if (i < argc && argv[i][0] != '-')
+               rev = argv[i++];
+
+       if (get_sha1(rev, sha1))
+               die("Failed to resolve '%s' as a valid ref.", rev);
+
+       commit = lookup_commit_reference(sha1);
+       if (!commit)
+               die("Could not parse object '%s'.", rev);
+       hashcpy(sha1, commit->object.sha1);
+
+       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
+        * affecting the working tree nor HEAD. */
+       if (i < argc) {
+               if (reset_type == MIXED)
+                       warning("--mixed option is deprecated with paths.");
+               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;
+       }
+       if (reset_type == NONE)
+               reset_type = MIXED; /* by default */
+
+       /* 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())
+                       die("Cannot do a soft reset in the middle of a merge.");
+       }
+       else if (reset_index_file(sha1, (reset_type == HARD)))
+               die("Could not reset index file to revision '%s'.", rev);
+
+       /* Any resets update HEAD to the head being switched to,
+        * saving the previous head in ORIG_HEAD before. */
+       if (!get_sha1("ORIG_HEAD", sha1_old_orig))
+               old_orig = sha1_old_orig;
+       if (!get_sha1("HEAD", sha1_orig)) {
+               orig = sha1_orig;
+               prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
+               update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+       }
+       else if (old_orig)
+               delete_ref("ORIG_HEAD", old_orig);
+       prepend_reflog_action("updating HEAD", msg, sizeof(msg));
+       update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+
+       switch (reset_type) {
+       case HARD:
+               if (!update_ref_status)
+                       print_new_head_line(commit);
+               break;
+       case SOFT: /* Nothing else to do. */
+               break;
+       case MIXED: /* Report what has not been updated. */
+               update_index_refresh();
+               break;
+       }
+
+       unlink(git_path("MERGE_HEAD"));
+       unlink(git_path("rr-cache/MERGE_RR"));
+       unlink(git_path("MERGE_MSG"));
+       unlink(git_path("SQUASH_MSG"));
+
+       free(reflog_action);
+
+       return update_ref_status;
+}
index bb720004afeb632005a5622ecb9cd25f95f5caa5..03ee7bf780be93601f9190b733984ad82951952b 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -60,6 +60,7 @@ extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
+extern int cmd_reset(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 493983cbaef90cb845d0660dce19072b6ec0b353..824650016677353cfa8c8a140eb3d904f56d60ee 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -264,6 +264,7 @@ extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern int remove_file_from_index(struct index_state *, const char *path);
 extern int add_file_to_index(struct index_state *, const char *path, int verbose);
+extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);
 extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
diff --git a/contrib/examples/git-reset.sh b/contrib/examples/git-reset.sh
new file mode 100755 (executable)
index 0000000..1dc606f
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+#
+# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+#
+USAGE='[--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+set_reflog_action "reset $*"
+require_work_tree
+
+update= reset_type=--mixed
+unset rev
+
+while case $# in 0) break ;; esac
+do
+       case "$1" in
+       --mixed | --soft | --hard)
+               reset_type="$1"
+               ;;
+       --)
+               break
+               ;;
+       -*)
+               usage
+               ;;
+       *)
+               rev=$(git rev-parse --verify "$1") || exit
+               shift
+               break
+               ;;
+       esac
+       shift
+done
+
+: ${rev=HEAD}
+rev=$(git rev-parse --verify $rev^0) || exit
+
+# Skip -- in "git reset HEAD -- foo" and "git reset -- foo".
+case "$1" in --) shift ;; esac
+
+# git reset --mixed tree [--] paths... can be used to
+# load chosen paths from the tree into the index without
+# affecting the working tree nor HEAD.
+if test $# != 0
+then
+       test "$reset_type" = "--mixed" ||
+               die "Cannot do partial $reset_type reset."
+
+       git diff-index --cached $rev -- "$@" |
+       sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z]   \(.*\)$/\1 \2   \3/' |
+       git update-index --add --remove --index-info || exit
+       git update-index --refresh
+       exit
+fi
+
+cd_to_toplevel
+
+if test "$reset_type" = "--hard"
+then
+       update=-u
+fi
+
+# 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 test "$reset_type" = "--soft"
+then
+       if test -f "$GIT_DIR/MERGE_HEAD" ||
+          test "" != "$(git ls-files --unmerged)"
+       then
+               die "Cannot do a soft reset in the middle of a merge."
+       fi
+else
+       git read-tree -v --reset $update "$rev" || exit
+fi
+
+# Any resets update HEAD to the head being switched to.
+if orig=$(git rev-parse --verify HEAD 2>/dev/null)
+then
+       echo "$orig" >"$GIT_DIR/ORIG_HEAD"
+else
+       rm -f "$GIT_DIR/ORIG_HEAD"
+fi
+git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev"
+update_ref_status=$?
+
+case "$reset_type" in
+--hard )
+       test $update_ref_status = 0 && {
+               printf "HEAD is now at "
+               GIT_PAGER= git log --max-count=1 --pretty=oneline \
+                       --abbrev-commit HEAD
+       }
+       ;;
+--soft )
+       ;; # Nothing else to do
+--mixed )
+       # Report what has not been updated.
+       git update-index --refresh
+       ;;
+esac
+
+rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
+       "$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG"
+
+exit $update_ref_status
diff --git a/git-reset.sh b/git-reset.sh
deleted file mode 100755 (executable)
index 1dc606f..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
-#
-USAGE='[--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]'
-SUBDIRECTORY_OK=Yes
-. git-sh-setup
-set_reflog_action "reset $*"
-require_work_tree
-
-update= reset_type=--mixed
-unset rev
-
-while case $# in 0) break ;; esac
-do
-       case "$1" in
-       --mixed | --soft | --hard)
-               reset_type="$1"
-               ;;
-       --)
-               break
-               ;;
-       -*)
-               usage
-               ;;
-       *)
-               rev=$(git rev-parse --verify "$1") || exit
-               shift
-               break
-               ;;
-       esac
-       shift
-done
-
-: ${rev=HEAD}
-rev=$(git rev-parse --verify $rev^0) || exit
-
-# Skip -- in "git reset HEAD -- foo" and "git reset -- foo".
-case "$1" in --) shift ;; esac
-
-# git reset --mixed tree [--] paths... can be used to
-# load chosen paths from the tree into the index without
-# affecting the working tree nor HEAD.
-if test $# != 0
-then
-       test "$reset_type" = "--mixed" ||
-               die "Cannot do partial $reset_type reset."
-
-       git diff-index --cached $rev -- "$@" |
-       sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z]   \(.*\)$/\1 \2   \3/' |
-       git update-index --add --remove --index-info || exit
-       git update-index --refresh
-       exit
-fi
-
-cd_to_toplevel
-
-if test "$reset_type" = "--hard"
-then
-       update=-u
-fi
-
-# 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 test "$reset_type" = "--soft"
-then
-       if test -f "$GIT_DIR/MERGE_HEAD" ||
-          test "" != "$(git ls-files --unmerged)"
-       then
-               die "Cannot do a soft reset in the middle of a merge."
-       fi
-else
-       git read-tree -v --reset $update "$rev" || exit
-fi
-
-# Any resets update HEAD to the head being switched to.
-if orig=$(git rev-parse --verify HEAD 2>/dev/null)
-then
-       echo "$orig" >"$GIT_DIR/ORIG_HEAD"
-else
-       rm -f "$GIT_DIR/ORIG_HEAD"
-fi
-git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev"
-update_ref_status=$?
-
-case "$reset_type" in
---hard )
-       test $update_ref_status = 0 && {
-               printf "HEAD is now at "
-               GIT_PAGER= git log --max-count=1 --pretty=oneline \
-                       --abbrev-commit HEAD
-       }
-       ;;
---soft )
-       ;; # Nothing else to do
---mixed )
-       # Report what has not been updated.
-       git update-index --refresh
-       ;;
-esac
-
-rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
-       "$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG"
-
-exit $update_ref_status
diff --git a/git.c b/git.c
index fd3d83cd4c49409214dcc3e54480b7650e1b8b18..56ae8ccccf6917fb27f74d56d7d96a9c4788cb54 100644 (file)
--- a/git.c
+++ b/git.c
@@ -364,6 +364,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "reflog", cmd_reflog, RUN_SETUP },
                { "repo-config", cmd_config },
                { "rerere", cmd_rerere, RUN_SETUP },
+               { "reset", cmd_reset, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
                { "rev-parse", cmd_rev_parse, RUN_SETUP },
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
index 16f6a0f98b5df8fbb9bb5263d0d32afca678cbec..19d5f3b2872ec7039508c9b12a89f3013d58c68d 100644 (file)
@@ -171,30 +171,6 @@ static void output_commit_title(struct commit *commit)
        }
 }
 
-static struct cache_entry *make_cache_entry(unsigned int mode,
-               const unsigned char *sha1, const char *path, int stage, int refresh)
-{
-       int size, len;
-       struct cache_entry *ce;
-
-       if (!verify_path(path))
-               return NULL;
-
-       len = strlen(path);
-       size = cache_entry_size(len);
-       ce = xcalloc(1, size);
-
-       hashcpy(ce->sha1, sha1);
-       memcpy(ce->name, path, len);
-       ce->ce_flags = create_ce_flags(len, stage);
-       ce->ce_mode = create_ce_mode(mode);
-
-       if (refresh)
-               return refresh_cache_entry(ce, 0);
-
-       return ce;
-}
-
 static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
                const char *path, int stage, int refresh, int options)
 {
index d82a99a9158f46cbf81d5dda8de1a28228ac16cb..2e40a344209e010e664758865846b63dc9546c1f 100644 (file)
@@ -434,6 +434,31 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
        return 0;
 }
 
+struct cache_entry *make_cache_entry(unsigned int mode,
+               const unsigned char *sha1, const char *path, int stage,
+               int refresh)
+{
+       int size, len;
+       struct cache_entry *ce;
+
+       if (!verify_path(path))
+               return NULL;
+
+       len = strlen(path);
+       size = cache_entry_size(len);
+       ce = xcalloc(1, size);
+
+       hashcpy(ce->sha1, sha1);
+       memcpy(ce->name, path, len);
+       ce->ce_flags = create_ce_flags(len, stage);
+       ce->ce_mode = create_ce_mode(mode);
+
+       if (refresh)
+               return refresh_cache_entry(ce, 0);
+
+       return ce;
+}
+
 int ce_same_name(struct cache_entry *a, struct cache_entry *b)
 {
        int len = ce_namelen(a);
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
new file mode 100755 (executable)
index 0000000..f64b1cb
--- /dev/null
@@ -0,0 +1,405 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git-reset
+
+Documented tests for git-reset'
+
+. ./test-lib.sh
+
+test_expect_success 'creating initial files and commits' '
+       test_tick &&
+       echo "1st file" >first &&
+       git add first &&
+       git commit -m "create 1st file" &&
+
+       echo "2nd file" >second &&
+       git add second &&
+       git commit -m "create 2nd file" &&
+
+       echo "2nd line 1st file" >>first &&
+       git commit -a -m "modify 1st file" &&
+
+       git rm first &&
+       git mv second secondfile &&
+       git commit -a -m "remove 1st and rename 2nd" &&
+
+       echo "1st line 2nd file" >secondfile &&
+       echo "2nd line 2nd file" >>secondfile &&
+       git commit -a -m "modify 2nd file"
+'
+# git log --pretty=oneline # to see those SHA1 involved
+
+check_changes () {
+       test "$(git rev-parse HEAD)" = "$1" &&
+       git diff | git diff .diff_expect - &&
+       git diff --cached | git diff .cached_expect - &&
+       for FILE in *
+       do
+               echo $FILE':'
+               cat $FILE || return
+       done | git diff .cat_expect -
+}
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+
+test_expect_success 'giving a non existing revision should fail' '
+       ! git reset aaaaaa &&
+       ! git reset --mixed aaaaaa &&
+       ! git reset --soft aaaaaa &&
+       ! git reset --hard aaaaaa &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+       'giving paths with options different than --mixed should fail' '
+       ! git reset --soft -- first &&
+       ! git reset --hard -- first &&
+       ! git reset --soft HEAD^ -- first &&
+       ! git reset --hard HEAD^ -- first &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success 'giving unrecognized options should fail' '
+       ! git reset --other &&
+       ! git reset -o &&
+       ! git reset --mixed --other &&
+       ! git reset --mixed -o &&
+       ! git reset --soft --other &&
+       ! git reset --soft -o &&
+       ! git reset --hard --other &&
+       ! git reset --hard -o &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+       'trying to do reset --soft with pending merge should fail' '
+       git branch branch1 &&
+       git branch branch2 &&
+
+       git checkout branch1 &&
+       echo "3rd line in branch1" >>secondfile &&
+       git commit -a -m "change in branch1" &&
+
+       git checkout branch2 &&
+       echo "3rd line in branch2" >>secondfile &&
+       git commit -a -m "change in branch2" &&
+
+       ! git merge branch1 &&
+       ! git reset --soft &&
+
+       printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
+       git commit -a -m "the change in branch2" &&
+
+       git checkout master &&
+       git branch -D branch1 branch2 &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+       'trying to do reset --soft with pending checkout merge should fail' '
+       git branch branch3 &&
+       git branch branch4 &&
+
+       git checkout branch3 &&
+       echo "3rd line in branch3" >>secondfile &&
+       git commit -a -m "line in branch3" &&
+
+       git checkout branch4 &&
+       echo "3rd line in branch4" >>secondfile &&
+
+       git checkout -m branch3 &&
+       ! git reset --soft &&
+
+       printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
+       git commit -a -m "the line in branch3" &&
+
+       git checkout master &&
+       git branch -D branch3 branch4 &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+       'resetting to HEAD with no changes should succeed and do nothing' '
+       git reset --hard &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset --hard HEAD &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset --soft &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset --soft HEAD &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset --mixed &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset --mixed HEAD &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+       git reset HEAD &&
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+cat >.cached_expect <<EOF
+diff --git a/secondfile b/secondfile
+index 1bbba79..44c5b58 100644
+--- a/secondfile
++++ b/secondfile
+@@ -1 +1,2 @@
+-2nd file
++1st line 2nd file
++2nd line 2nd file
+EOF
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success '--soft reset only should show changes in diff --cached' '
+       git reset --soft HEAD^ &&
+       check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 &&
+       test "$(git rev-parse ORIG_HEAD)" = \
+                       3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+3rd line 2nd file
+EOF
+test_expect_success \
+       'changing files and redo the last commit should succeed' '
+       echo "3rd line 2nd file" >>secondfile &&
+       git commit -a -C ORIG_HEAD &&
+       check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+       test "$(git rev-parse ORIG_HEAD)" = \
+                       3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+first:
+1st file
+2nd line 1st file
+second:
+2nd file
+EOF
+test_expect_success \
+       '--hard reset should change the files and undo commits permanently' '
+       git reset --hard HEAD~2 &&
+       check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+       test "$(git rev-parse ORIG_HEAD)" = \
+                       3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+'
+
+>.diff_expect
+cat >.cached_expect <<EOF
+diff --git a/first b/first
+deleted file mode 100644
+index 8206c22..0000000
+--- a/first
++++ /dev/null
+@@ -1,2 +0,0 @@
+-1st file
+-2nd line 1st file
+diff --git a/second b/second
+deleted file mode 100644
+index 1bbba79..0000000
+--- a/second
++++ /dev/null
+@@ -1 +0,0 @@
+-2nd file
+diff --git a/secondfile b/secondfile
+new file mode 100644
+index 0000000..44c5b58
+--- /dev/null
++++ b/secondfile
+@@ -0,0 +1,2 @@
++1st line 2nd file
++2nd line 2nd file
+EOF
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success \
+       'redoing changes adding them without commit them should succeed' '
+       git rm first &&
+       git mv second secondfile &&
+
+       echo "1st line 2nd file" >secondfile &&
+       echo "2nd line 2nd file" >>secondfile &&
+       git add secondfile &&
+       check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+'
+
+cat >.diff_expect <<EOF
+diff --git a/first b/first
+deleted file mode 100644
+index 8206c22..0000000
+--- a/first
++++ /dev/null
+@@ -1,2 +0,0 @@
+-1st file
+-2nd line 1st file
+diff --git a/second b/second
+deleted file mode 100644
+index 1bbba79..0000000
+--- a/second
++++ /dev/null
+@@ -1 +0,0 @@
+-2nd file
+EOF
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success '--mixed reset to HEAD should unadd the files' '
+       git reset &&
+       check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+       test "$(git rev-parse ORIG_HEAD)" = \
+                       ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success 'redoing the last two commits should succeed' '
+       git add secondfile &&
+       git reset --hard ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+
+       git rm first &&
+       git mv second secondfile &&
+       git commit -a -m "remove 1st and rename 2nd" &&
+
+       echo "1st line 2nd file" >secondfile &&
+       echo "2nd line 2nd file" >>secondfile &&
+       git commit -a -m "modify 2nd file" &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+3rd line in branch2
+EOF
+test_expect_success '--hard reset to HEAD should clear a failed merge' '
+       git branch branch1 &&
+       git branch branch2 &&
+
+       git checkout branch1 &&
+       echo "3rd line in branch1" >>secondfile &&
+       git commit -a -m "change in branch1" &&
+
+       git checkout branch2 &&
+       echo "3rd line in branch2" >>secondfile &&
+       git commit -a -m "change in branch2" &&
+
+       ! git pull . branch1 &&
+       git reset --hard &&
+       check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success \
+       '--hard reset to ORIG_HEAD should clear a fast-forward merge' '
+       git reset --hard HEAD^ &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+
+       git pull . branch1 &&
+       git reset --hard ORIG_HEAD &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+
+       git checkout master &&
+       git branch -D branch1 branch2 &&
+       check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+cat > expect << EOF
+diff --git a/file1 b/file1
+index d00491f..7ed6ff8 100644
+--- a/file1
++++ b/file1
+@@ -1 +1 @@
+-1
++5
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 0cfbf08..0000000
+--- a/file2
++++ /dev/null
+@@ -1 +0,0 @@
+-2
+EOF
+cat > cached_expect << EOF
+diff --git a/file4 b/file4
+new file mode 100644
+index 0000000..b8626c4
+--- /dev/null
++++ b/file4
+@@ -0,0 +1 @@
++4
+EOF
+test_expect_success 'test --mixed <paths>' '
+       echo 1 > file1 &&
+       echo 2 > file2 &&
+       git add file1 file2 &&
+       test_tick &&
+       git commit -m files &&
+       git rm file2 &&
+       echo 3 > file3 &&
+       echo 4 > file4 &&
+       echo 5 > file1 &&
+       git add file1 file3 file4 &&
+       ! git reset HEAD -- file1 file2 file3 &&
+       git diff > output &&
+       git diff output expect &&
+       git diff --cached > output &&
+       git diff output cached_expect
+'
+
+test_expect_success 'test resetting the index at give paths' '
+
+       mkdir sub &&
+       >sub/file1 &&
+       >sub/file2 &&
+       git update-index --add sub/file1 sub/file2 &&
+       T=$(git write-tree) &&
+       ! git reset HEAD sub/file2 &&
+       U=$(git write-tree) &&
+       echo "$T" &&
+       echo "$U" &&
+       ! git diff-index --cached --exit-code "$T" &&
+       test "$T" != "$U"
+
+'
+
+test_done