From 359048d6ec296757266887a8ced5a927b97b94c1 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 11 Sep 2007 03:09:52 +0200 Subject: Add tests for documented features of "git reset". This adds the new file t/t7102-reset.sh following the text and examples in "Documentation/git-reset.txt" in order to check the behaviour of the upcoming "builtin-reset.c", and be able to compare it with the original "git-reset.sh". Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- t/t7102-reset.sh | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100755 t/t7102-reset.sh diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh new file mode 100755 index 0000000000..2cad4db127 --- /dev/null +++ b/t/t7102-reset.sh @@ -0,0 +1,389 @@ +#!/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 <>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 <.cat_expect <.diff_expect +>.cached_expect +cat >.cat_expect <>secondfile && + git commit -a -C ORIG_HEAD && + check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d && + test "$(git rev-parse ORIG_HEAD)" = \ + 3ec39651e7f44ea531a5de18a9fa791c0fd370fc +' + +>.diff_expect +>.cached_expect +cat >.cat_expect <.diff_expect +cat >.cached_expect <.cat_expect <secondfile && + echo "2nd line 2nd file" >>secondfile && + git add secondfile && + check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e +' + +cat >.diff_expect <.cached_expect +cat >.cat_expect <.diff_expect +>.cached_expect +cat >.cat_expect <secondfile && + echo "2nd line 2nd file" >>secondfile && + git commit -a -m "modify 2nd file" && + check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc +' + +>.diff_expect +>.cached_expect +cat >.cat_expect <>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 < 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 ' ' + 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_done -- cgit v1.2.1 From 6640f88165f77edcc266a2c0c56fb017dc613198 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 11 Sep 2007 05:17:28 +0200 Subject: Move make_cache_entry() from merge-recursive.c into read-cache.c The function make_cache_entry() is too useful to be hidden away in merge-recursive. So move it to libgit.a (exposing it via cache.h). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- cache.h | 1 + merge-recursive.c | 24 ------------------------ read-cache.c | 25 +++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/cache.h b/cache.h index 493983cbae..8246500166 100644 --- 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/merge-recursive.c b/merge-recursive.c index 16f6a0f98b..19d5f3b287 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -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) { diff --git a/read-cache.c b/read-cache.c index 8b1c94e0e3..536f4d0875 100644 --- a/read-cache.c +++ b/read-cache.c @@ -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); -- cgit v1.2.1 From 0e5a7faa3a903cf7a0a66c81e20a76b91f17faab Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 11 Sep 2007 05:19:34 +0200 Subject: Make "git reset" a builtin. This replaces the script "git-reset.sh" with "builtin-reset.c". A few git commands used in the script are called from the builtin also: "ls-files" to check for unmerged files, "read-tree" for resetting the index file in "mixed" and "hard" resets, and "update-index" to refresh at the end in the "mixed" reset and also for the option that gets selected paths into the index. The reset option with paths was implemented by Johannes Schindelin. Since the option that gets selected paths into the index is not a "reset" like the others because it does not change the HEAD at all, now the command is showing a warning when the "--mixed" option is supplied for that purpose. The following table shows the behaviour of "git reset" for the different supported options, where X means "changing" the HEAD, index or working tree: reset: --soft --mixed --hard -- HEAD X X X - index - X X X files - - X - Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- Makefile | 3 +- builtin-reset.c | 279 ++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + contrib/examples/git-reset.sh | 106 ++++++++++++++++ git-reset.sh | 106 ---------------- git.c | 1 + 6 files changed, 389 insertions(+), 107 deletions(-) create mode 100644 builtin-reset.c create mode 100755 contrib/examples/git-reset.sh delete mode 100755 git-reset.sh diff --git a/Makefile b/Makefile index 78cdaa155b..e6740fc392 100644 --- 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 index 0000000000..99d5c082a6 --- /dev/null +++ b/builtin-reset.c @@ -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] [] [ [--] ...]"; + +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; +} diff --git a/builtin.h b/builtin.h index bb720004af..03ee7bf780 100644 --- 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/contrib/examples/git-reset.sh b/contrib/examples/git-reset.sh new file mode 100755 index 0000000000..1dc606fbd3 --- /dev/null +++ b/contrib/examples/git-reset.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano +# +USAGE='[--mixed | --soft | --hard] [] [ [--] ...]' +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 index 1dc606fbd3..0000000000 --- a/git-reset.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano -# -USAGE='[--mixed | --soft | --hard] [] [ [--] ...]' -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 fd3d83cd4c..56ae8ccccf 100644 --- 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 }, -- cgit v1.2.1 From cbb390cd8f58ca6fc5c7b2c710425649b057b6d6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 13 Sep 2007 20:54:14 -0700 Subject: An additional test for "git-reset -- path" Signed-off-by: Junio C Hamano --- t/t7102-reset.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 2cad4db127..f64b1cbf75 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -386,4 +386,20 @@ test_expect_success 'test --mixed ' ' 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 -- cgit v1.2.1 From 09d5dc32fbdfa7bfd23fe377455445dd2605c3b9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 13 Sep 2007 20:33:11 -0700 Subject: Simplify cache API Earlier, add_file_to_index() invalidated the path in the cache-tree but remove_file_from_cache() did not, and the user of the latter needed to invalidate the entry himself. This led to a few bugs due to missed invalidate calls already. This patch makes the management of cache-tree less error prone by making more invalidate calls from lower level cache API functions. The rules are: - If you are going to write the index, you should either maintain cache_tree correctly. - If you cannot, alternatively you can remove the entire cache_tree by calling cache_tree_free() before you call write_cache(). - When you modify the index, cache_tree_invalidate_path() should be called with the path you are modifying, to discard the entry from the cache-tree structure. - The following cache API functions exported from read-cache.c (and the macro whose names have "cache" instead of "index") automatically call cache_tree_invalidate_path() for you: - remove_file_from_index(); - add_file_to_index(); - add_index_entry(); You can modify the index bypassing the above API functions (e.g. find an existing cache entry from the index and modify it in place). You need to call cache_tree_invalidate_path() yourself in such a case. Signed-off-by: Junio C Hamano --- builtin-add.c | 1 - builtin-apply.c | 2 -- builtin-mv.c | 7 ++----- builtin-rm.c | 1 - builtin-update-index.c | 9 --------- read-cache.c | 3 ++- 6 files changed, 4 insertions(+), 19 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 9847b7e019..866d19dd77 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -103,7 +103,6 @@ static void update_callback(struct diff_queue_struct *q, break; case DIFF_STATUS_DELETED: remove_file_from_cache(path); - cache_tree_invalidate_path(active_cache_tree, path); if (verbose) printf("remove '%s'\n", path); break; diff --git a/builtin-apply.c b/builtin-apply.c index 976ec77041..79a4852bc2 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2394,7 +2394,6 @@ static void remove_file(struct patch *patch, int rmdir_empty) if (update_index) { if (remove_file_from_cache(patch->old_name) < 0) die("unable to remove %s from index", patch->old_name); - cache_tree_invalidate_path(active_cache_tree, patch->old_name); } if (!cached) { if (S_ISGITLINK(patch->old_mode)) { @@ -2549,7 +2548,6 @@ static void create_file(struct patch *patch) mode = S_IFREG | 0644; create_one_file(path, mode, buf, size); add_index_file(path, mode, buf, size); - cache_tree_invalidate_path(active_cache_tree, path); } /* phase zero is to remove, phase one is to create */ diff --git a/builtin-mv.c b/builtin-mv.c index 3563216aca..b95b7d286a 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -276,11 +276,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) add_file_to_cache(path, verbose); } - for (i = 0; i < deleted.nr; i++) { - const char *path = deleted.items[i].path; - remove_file_from_cache(path); - cache_tree_invalidate_path(active_cache_tree, path); - } + for (i = 0; i < deleted.nr; i++) + remove_file_from_cache(deleted.items[i].path); if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || diff --git a/builtin-rm.c b/builtin-rm.c index 9a808c1bf9..3b0677e44b 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -227,7 +227,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (remove_file_from_cache(path)) die("git-rm: unable to remove %s", path); - cache_tree_invalidate_path(active_cache_tree, path); } if (show_only) diff --git a/builtin-update-index.c b/builtin-update-index.c index a7a4574f2b..55fb679d68 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -195,11 +195,6 @@ static int process_path(const char *path) int len; struct stat st; - /* We probably want to do this in remove_file_from_cache() and - * add_cache_entry() instead... - */ - cache_tree_invalidate_path(active_cache_tree, path); - /* * First things first: get the stat information, to decide * what to do about the pathname! @@ -239,7 +234,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, return error("%s: cannot add to the index - missing --add option?", path); report("add '%s'", path); - cache_tree_invalidate_path(active_cache_tree, path); return 0; } @@ -284,7 +278,6 @@ static void update_one(const char *path, const char *prefix, int prefix_length) die("Unable to mark file %s", path); goto free_return; } - cache_tree_invalidate_path(active_cache_tree, path); if (force_remove) { if (remove_file_from_cache(p)) @@ -367,7 +360,6 @@ static void read_index_info(int line_termination) free(path_name); continue; } - cache_tree_invalidate_path(active_cache_tree, path_name); if (!mode) { /* mode == 0 means there is no such path -- remove */ @@ -474,7 +466,6 @@ static int unresolve_one(const char *path) goto free_return; } - cache_tree_invalidate_path(active_cache_tree, path); remove_file_from_cache(path); if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { error("%s: cannot add our version to the index.", path); diff --git a/read-cache.c b/read-cache.c index 8b1c94e0e3..d82a99a915 100644 --- a/read-cache.c +++ b/read-cache.c @@ -346,6 +346,7 @@ int remove_file_from_index(struct index_state *istate, const char *path) int pos = index_name_pos(istate, path, strlen(path)); if (pos < 0) pos = -pos-1; + cache_tree_invalidate_path(istate->cache_tree, path); while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path)) remove_index_entry_at(istate, pos); return 0; @@ -430,7 +431,6 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) die("unable to add %s to index",path); if (verbose) printf("add '%s'\n", path); - cache_tree_invalidate_path(istate->cache_tree, path); return 0; } @@ -673,6 +673,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE; int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK; + cache_tree_invalidate_path(istate->cache_tree, ce->name); pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags)); /* existing match? Just replace it. */ -- cgit v1.2.1