add--interactive: handle initial commit better
There were several points where we looked at the HEAD commit; for initial commits, this is meaningless. So instead we: - show staged status data as a diff against the empty tree instead of HEAD - show file diffs as creation events - use "git rm --cached" to revert instead of going back to the HEAD commit We magically reference the empty tree to implement this. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King committed
Feb 13, 2008 at 05:50 UTC
18bc76164dbf4f6d54b8fa9c9c29db9ca66c7877
2 files changed
+108
-16
git-add--interactive.perl
+39
-16
@@ -82,6 +82,19 @@ sub list_untracked {
82
my $status_fmt = '%12s %12s %s';
83
my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
84
85
+{
86
+ my $initial;
87
+ sub is_initial_commit {
88
+ $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
89
+ unless defined $initial;
90
+ return $initial;
91
+ }
92
+}
93
+
94
+sub get_empty_tree {
95
+ return '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
96
+}
97
+
98
# Returns list of hashes, contents of each of which are:
99
# VALUE: pathname
100
# BINARY: is a binary path
@@ -103,8 +116,10 @@ sub list_modified {
116
return if (!@tracked);
117
}
118
119
+ my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
120
for (run_cmd_pipe(qw(git diff-index --cached
107
- --numstat --summary HEAD --), @tracked)) {
121
+ --numstat --summary), $reference,
122
+ '--', @tracked)) {
123
if (($add, $del, $file) =
124
/^([-\d]+) ([-\d]+) (.*)/) {
125
my ($change, $bin);
@@ -476,21 +491,27 @@ sub revert_cmd {
491
HEADER => $status_head, },
492
list_modified());
493
if (@update) {
479
- my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
480
- map { $_->{VALUE} } @update);
481
- my $fh;
482
- open $fh, '| git update-index --index-info'
483
- or die;
484
- for (@lines) {
485
- print $fh $_;
494
+ if (is_initial_commit()) {
495
+ system(qw(git rm --cached),
496
+ map { $_->{VALUE} } @update);
497
}
487
- close($fh);
488
- for (@update) {
489
- if ($_->{INDEX_ADDDEL} &&
490
- $_->{INDEX_ADDDEL} eq 'create') {
491
- system(qw(git update-index --force-remove --),
492
- $_->{VALUE});
493
- print "note: $_->{VALUE} is untracked now.\n";
498
+ else {
499
+ my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
500
+ map { $_->{VALUE} } @update);
501
+ my $fh;
502
+ open $fh, '| git update-index --index-info'
503
+ or die;
504
+ for (@lines) {
505
+ print $fh $_;
506
+ }
507
+ close($fh);
508
+ for (@update) {
509
+ if ($_->{INDEX_ADDDEL} &&
510
+ $_->{INDEX_ADDDEL} eq 'create') {
511
+ system(qw(git update-index --force-remove --),
512
+ $_->{VALUE});
513
+ print "note: $_->{VALUE} is untracked now.\n";
514
+ }
515
}
516
}
517
refresh();
@@ -956,7 +977,9 @@ sub diff_cmd {
977
HEADER => $status_head, },
978
@mods);
979
return if (!@them);
959
- system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them);
980
+ my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
981
+ system(qw(git diff -p --cached), $reference, '--',
982
+ map { $_->{VALUE} } @them);
983
}
984
985
sub quit_cmd {
t/t3701-add-interactive.sh
new
+69
@@ -0,0 +1,69 @@
1
+#!/bin/sh
2
+
3
+test_description='add -i basic tests'
4
+. ./test-lib.sh
5
+
6
+test_expect_success 'setup (initial)' '
7
+ echo content >file &&
8
+ git add file &&
9
+ echo more >>file &&
10
+ echo lines >>file
11
+'
12
+test_expect_success 'status works (initial)' '
13
+ git add -i </dev/null >output &&
14
+ grep "+1/-0 *+2/-0 file" output
15
+'
16
+cat >expected <<EOF
17
+new file mode 100644
18
+index 0000000..d95f3ad
19
+--- /dev/null
20
++++ b/file
21
+@@ -0,0 +1 @@
22
++content
23
+EOF
24
+test_expect_success 'diff works (initial)' '
25
+ (echo d; echo 1) | git add -i >output &&
26
+ sed -ne "/new file/,/content/p" <output >diff &&
27
+ diff -u expected diff
28
+'
29
+test_expect_success 'revert works (initial)' '
30
+ git add file &&
31
+ (echo r; echo 1) | git add -i &&
32
+ git ls-files >output &&
33
+ ! grep . output
34
+'
35
+
36
+test_expect_success 'setup (commit)' '
37
+ echo baseline >file &&
38
+ git add file &&
39
+ git commit -m commit &&
40
+ echo content >>file &&
41
+ git add file &&
42
+ echo more >>file &&
43
+ echo lines >>file
44
+'
45
+test_expect_success 'status works (commit)' '
46
+ git add -i </dev/null >output &&
47
+ grep "+1/-0 *+2/-0 file" output
48
+'
49
+cat >expected <<EOF
50
+index 180b47c..b6f2c08 100644
51
+--- a/file
52
++++ b/file
53
+@@ -1 +1,2 @@
54
+ baseline
55
++content
56
+EOF
57
+test_expect_success 'diff works (commit)' '
58
+ (echo d; echo 1) | git add -i >output &&
59
+ sed -ne "/^index/,/content/p" <output >diff &&
60
+ diff -u expected diff
61
+'
62
+test_expect_success 'revert works (commit)' '
63
+ git add file &&
64
+ (echo r; echo 1) | git add -i &&
65
+ git add -i </dev/null >output &&
66
+ grep "unchanged *+3/-0 file" output
67
+'
68
+
69
+test_done