summaryrefslogtreecommitdiff
path: root/git-reset.sh
blob: dfa9cb8bb17f6f8694d0c639fe8b1663f9ec3c8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/bin/sh
. git-sh-setup || die "Not a git archive"

tmp=/var/tmp/reset.$$
trap 'rm -f $tmp-*' 0 1 2 3 15

reset_type=--mixed
case "$1" in
--mixed | --soft | --hard)
	reset_type="$1"
	shift
	;;
esac

rev=$(git-rev-parse --verify --default HEAD "$@") || exit
rev=$(git-rev-parse --verify $rev^0) || exit

# We need to remember the set of paths that _could_ be left
# behind before a hard reset, so that we can remove them.
if test "$reset_type" = "--hard"
then
	{
		git-ls-files --stage -z
		git-rev-parse --verify HEAD 2>/dev/null &&
		git-ls-tree -r -z HEAD
	} | perl -e '
	    use strict;
	    my %seen;
	    $/ = "\0";
	    while (<>) {
		chomp;
		my ($info, $path) = split(/\t/, $_);
		next if ($info =~ / tree /);
		if (!$seen{$path}) {
			$seen{$path} = 1;
			print "$path\0";
		}
	    }
	' >$tmp-exists
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 --reset "$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
echo "$rev" >"$GIT_DIR/HEAD"

case "$reset_type" in
--hard )
	# Hard reset matches the working tree to that of the tree
	# being switched to.
	git-checkout-index -f -u -q -a
	git-ls-files --cached -z |
	perl -e '
		use strict;
		my (%keep, $fh);
		$/ = "\0";
		while (<STDIN>) {
			chomp;
			$keep{$_} = 1;
		}
		open $fh, "<", $ARGV[0]
			or die "cannot open $ARGV[0]";
		while (<$fh>) {
			chomp;
			if (! exists $keep{$_}) {
				# it is ok if this fails -- it may already
				# have been culled by checkout-index.
				unlink $_;
			}
		}
	' $tmp-exists
	;;
--soft )
	;; # Nothing else to do
--mixed )
	# Report what has not been updated.
	git-update-index --refresh
	;;
esac

rm -f "$GIT_DIR/MERGE_HEAD"