git-add: introduce --edit (to edit the diff vs. the index)
With "git add -e [<files>]", Git will fire up an editor with the current diff relative to the index (i.e. what you would get with "git diff [<files>]"). Now you can edit the patch as much as you like, including adding/removing lines, editing the text, whatever. Make sure, though, that the first character of the hunk lines is still a space, a plus or a minus. After you closed the editor, Git will adjust the line counts of the hunks if necessary, thanks to the --recount option of apply, and commit the patch. Except if you deleted everything, in which case nothing happens (for obvious reasons). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Johannes Schindelin committed
Apr 8, 2009 at 23:30 UTC
c59cb03a8bfc4b09758b07b23b6fe70a909ff9f4
3 files changed
+175
-4
Documentation/git-add.txt
+10
-1
@@ -9,7 +9,7 @@ SYNOPSIS
9
--------
10
[verse]
11
'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
12
- [--all | [--update | -u]] [--intent-to-add | -N]
12
+ [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
13
[--refresh] [--ignore-errors] [--] <filepattern>...
14
15
DESCRIPTION
@@ -76,6 +76,15 @@ OPTIONS
76
bypassed and the 'patch' subcommand is invoked using each of
77
the specified filepatterns before exiting.
78
79
+-e, \--edit::
80
+ Open the diff vs. the index in an editor and let the user
81
+ edit it. After the editor was closed, adjust the hunk headers
82
+ and apply the patch to the index.
83
++
84
+*NOTE*: Obviously, if you change anything else than the first character
85
+on lines beginning with a space or a minus, the patch will no longer
86
+apply.
87
+
88
-u::
89
--update::
90
Update only files that git already knows about, staging modified
builtin-add.c
+56
-3
@@ -10,12 +10,14 @@
10
#include "cache-tree.h"
11
#include "run-command.h"
12
#include "parse-options.h"
13
+#include "diff.h"
14
+#include "revision.h"
15
16
static const char * const builtin_add_usage[] = {
17
"git add [options] [--] <filepattern>...",
18
NULL
19
};
18
-static int patch_interactive, add_interactive;
20
+static int patch_interactive, add_interactive, edit_interactive;
21
static int take_worktree_changes;
22
23
static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
@@ -187,6 +189,51 @@ int interactive_add(int argc, const char **argv, const char *prefix)
189
return status;
190
}
191
192
+int edit_patch(int argc, const char **argv, const char *prefix)
193
+{
194
+ char *file = xstrdup(git_path("ADD_EDIT.patch"));
195
+ const char *apply_argv[] = { "apply", "--recount", "--cached",
196
+ file, NULL };
197
+ struct child_process child;
198
+ struct rev_info rev;
199
+ int out;
200
+ struct stat st;
201
+
202
+ git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
203
+
204
+ if (read_cache() < 0)
205
+ die ("Could not read the index");
206
+
207
+ init_revisions(&rev, prefix);
208
+ rev.diffopt.context = 7;
209
+
210
+ argc = setup_revisions(argc, argv, &rev, NULL);
211
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
212
+ out = open(file, O_CREAT | O_WRONLY, 0644);
213
+ if (out < 0)
214
+ die ("Could not open '%s' for writing.", file);
215
+ rev.diffopt.file = fdopen(out, "w");
216
+ rev.diffopt.close_file = 1;
217
+ if (run_diff_files(&rev, 0))
218
+ die ("Could not write patch");
219
+
220
+ launch_editor(file, NULL, NULL);
221
+
222
+ if (stat(file, &st))
223
+ die("Could not stat '%s'", file);
224
+ if (!st.st_size)
225
+ die("Empty patch. Aborted.");
226
+
227
+ memset(&child, 0, sizeof(child));
228
+ child.git_cmd = 1;
229
+ child.argv = apply_argv;
230
+ if (run_command(&child))
231
+ die ("Could not apply '%s'", file);
232
+
233
+ unlink(file);
234
+ return 0;
235
+}
236
+
237
static struct lock_file lock_file;
238
239
static const char ignore_error[] =
@@ -201,6 +248,7 @@ static struct option builtin_add_options[] = {
248
OPT_GROUP(""),
249
OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
250
OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
251
+ OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
252
OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
253
OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
254
OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
@@ -251,14 +299,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
299
int require_pathspec;
300
301
argc = parse_options(argc, argv, builtin_add_options,
254
- builtin_add_usage, 0);
302
+ builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
303
if (patch_interactive)
304
add_interactive = 1;
305
if (add_interactive)
258
- exit(interactive_add(argc, argv, prefix));
306
+ exit(interactive_add(argc - 1, argv + 1, prefix));
307
308
git_config(add_config, NULL);
309
310
+ if (edit_interactive)
311
+ return(edit_patch(argc, argv, prefix));
312
+ argc--;
313
+ argv++;
314
+
315
if (addremove && take_worktree_changes)
316
die("-A and -u are mutually incompatible");
317
if ((addremove || take_worktree_changes) && !argc) {
t/t3702-add-edit.sh
new
+109
@@ -0,0 +1,109 @@
1
+#!/bin/sh
2
+#
3
+# Copyright (c) 2007 Johannes E. Schindelin
4
+#
5
+
6
+test_description='add -e basic tests'
7
+. ./test-lib.sh
8
+
9
+
10
+cat > file << EOF
11
+LO, praise of the prowess of people-kings
12
+of spear-armed Danes, in days long sped,
13
+we have heard, and what honor the athelings won!
14
+Oft Scyld the Scefing from squadroned foes,
15
+from many a tribe, the mead-bench tore,
16
+awing the earls. Since erst he lay
17
+friendless, a foundling, fate repaid him:
18
+for he waxed under welkin, in wealth he throve,
19
+till before him the folk, both far and near,
20
+who house by the whale-path, heard his mandate,
21
+gave him gifts: a good king he!
22
+EOF
23
+
24
+test_expect_success 'setup' '
25
+
26
+ git add file &&
27
+ test_tick &&
28
+ git commit -m initial file
29
+
30
+'
31
+
32
+cat > expected-patch << EOF
33
+diff --git a/file b/file
34
+index b9834b5..0b8f197 100644
35
+--- a/file
36
++++ b/file
37
+@@ -1,11 +1,3 @@
38
+-LO, praise of the prowess of people-kings
39
+-of spear-armed Danes, in days long sped,
40
+-we have heard, and what honor the athelings won!
41
+-Oft Scyld the Scefing from squadroned foes,
42
+-from many a tribe, the mead-bench tore,
43
+-awing the earls. Since erst he lay
44
+-friendless, a foundling, fate repaid him:
45
+-for he waxed under welkin, in wealth he throve,
46
+-till before him the folk, both far and near,
47
+-who house by the whale-path, heard his mandate,
48
+-gave him gifts: a good king he!
49
++#!$SHELL_PATH
50
++mv -f "\$1" orig-patch &&
51
++mv -f patch "\$1"
52
+EOF
53
+
54
+cat > patch << EOF
55
+diff --git a/file b/file
56
+index b9834b5..ef6e94c 100644
57
+--- a/file
58
++++ b/file
59
+@@ -3,1 +3,333 @@ of spear-armed Danes, in days long sped,
60
+ we have heard, and what honor the athelings won!
61
++
62
+ Oft Scyld the Scefing from squadroned foes,
63
+@@ -2,7 +1,5 @@ awing the earls. Since erst he lay
64
+ friendless, a foundling, fate repaid him:
65
++
66
+ for he waxed under welkin, in wealth he throve,
67
+EOF
68
+
69
+cat > expected << EOF
70
+diff --git a/file b/file
71
+index b9834b5..ef6e94c 100644
72
+--- a/file
73
++++ b/file
74
+@@ -1,10 +1,12 @@
75
+ LO, praise of the prowess of people-kings
76
+ of spear-armed Danes, in days long sped,
77
+ we have heard, and what honor the athelings won!
78
++
79
+ Oft Scyld the Scefing from squadroned foes,
80
+ from many a tribe, the mead-bench tore,
81
+ awing the earls. Since erst he lay
82
+ friendless, a foundling, fate repaid him:
83
++
84
+ for he waxed under welkin, in wealth he throve,
85
+ till before him the folk, both far and near,
86
+ who house by the whale-path, heard his mandate,
87
+EOF
88
+
89
+echo "#!$SHELL_PATH" >fake-editor.sh
90
+cat >> fake-editor.sh <<\EOF
91
+mv -f "$1" orig-patch &&
92
+mv -f patch "$1"
93
+EOF
94
+
95
+test_set_editor "$(pwd)/fake-editor.sh"
96
+chmod a+x fake-editor.sh
97
+
98
+test_expect_success 'add -e' '
99
+
100
+ cp fake-editor.sh file &&
101
+ git add -e &&
102
+ test_cmp fake-editor.sh file &&
103
+ test_cmp orig-patch expected-patch &&
104
+ git diff --cached > out &&
105
+ test_cmp out expected
106
+
107
+'
108
+
109
+test_done