From: Johannes Schindelin <johannes.schindelin@xxxxxx> 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 --fixup-line-counts 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@xxxxxx> Signed-off-by: Thomas Rast <trast@xxxxxxxxxxxxxxx> --- Documentation/git-add.txt | 12 ++++- builtin-add.c | 55 +++++++++++++++++++- t/t3702-add-edit.sh | 126 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 2 deletions(-) create mode 100755 t/t3702-add-edit.sh diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index b8e3fa6..c6de028 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git-add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p] - [--update | -u] [--refresh] [--ignore-errors] [--] + [--edit | -e] [--update | -u] [--refresh] [--ignore-errors] [--] <filepattern>... DESCRIPTION @@ -76,6 +76,16 @@ OPTIONS bypassed and the 'patch' subcommand is invoked using each of the specified filepatterns before exiting. +-e:: +--edit:: + Open the diff vs. the index in an editor and let the user + edit it. After the editor was closed, adjust the hunk headers + and apply the patch to the index. ++ +*NOTE*: Obviously, if you change anything else than the first character +on lines beginning with a space or a minus, the patch will no longer +apply. + -u:: --update:: Update only files that git already knows about, staging modified diff --git a/builtin-add.c b/builtin-add.c index 9930cf5..b9e9a17 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -19,7 +19,7 @@ static const char * const builtin_add_usage[] = { "git-add [options] [--] <filepattern>...", NULL }; -static int patch_interactive = 0, add_interactive = 0; +static int patch_interactive = 0, add_interactive = 0, edit_interactive = 0; static int take_worktree_changes; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) @@ -186,6 +186,56 @@ int interactive_add(int argc, const char **argv, const char *prefix) return status; } +int edit_patch(int argc, const char **argv, const char *prefix) +{ + char *file = xstrdup(git_path("ADD_EDIT.patch")); + const char *apply_argv[] = { "apply", "--recount", "--cached", + file, NULL }; + struct child_process child; + int result = 0, ac; + struct stat st; + + memset(&child, 0, sizeof(child)); + child.argv = xcalloc(sizeof(const char *), (argc + 5)); + ac = 0; + child.git_cmd = 1; + child.argv[ac++] = "diff-files"; + child.argv[ac++] = "--no-color"; + child.argv[ac++] = "-p"; + child.argv[ac++] = "--"; + if (argc) { + const char **pathspec = validate_pathspec(argc, argv, prefix); + if (!pathspec) + return -1; + memcpy(&(child.argv[ac]), pathspec, sizeof(*argv) * argc); + ac += argc; + } + child.argv[ac] = NULL; + child.out = open(file, O_CREAT | O_WRONLY, 0644); + result = child.out < 0 && error("Could not write to '%s'", file); + + if (!result) + result = run_command(&child); + free(child.argv); + + launch_editor(file, NULL, NULL); + + if (!result) + result = stat(file, &st) && error("Could not stat '%s'", file); + if (!result && !st.st_size) + result = error("Empty patch. Aborted."); + + memset(&child, 0, sizeof(child)); + child.git_cmd = 1; + child.argv = apply_argv; + if (!result) + result = run_command(&child) && + error("Could not apply '%s'", file); + if (!result) + unlink(file); + return result; +} + static struct lock_file lock_file; static const char ignore_error[] = @@ -202,6 +252,7 @@ static struct option builtin_add_options[] = { OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), + OPT_BOOLEAN('e', "edit", &edit_interactive, "super-interactive patching"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), OPT_END(), @@ -226,6 +277,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, builtin_add_options, builtin_add_usage, 0); + if (edit_interactive) + return(edit_patch(argc, argv, prefix)); if (patch_interactive) add_interactive = 1; if (add_interactive) diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh new file mode 100755 index 0000000..decf727 --- /dev/null +++ b/t/t3702-add-edit.sh @@ -0,0 +1,126 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='add -e basic tests' +. ./test-lib.sh + + +cat > file << EOF +LO, praise of the prowess of people-kings +of spear-armed Danes, in days long sped, +we have heard, and what honor the athelings won! +Oft Scyld the Scefing from squadroned foes, +from many a tribe, the mead-bench tore, +awing the earls. Since erst he lay +friendless, a foundling, fate repaid him: +for he waxed under welkin, in wealth he throve, +till before him the folk, both far and near, +who house by the whale-path, heard his mandate, +gave him gifts: a good king he! +EOF + +test_expect_success 'setup' ' + + git add file && + test_tick && + git commit -m initial file + +' + +cat > patch << EOF +diff --git a/file b/file +index b9834b5..ef6e94c 100644 +--- a/file ++++ b/file +@@ -3,1 +3,333 @@ of spear-armed Danes, in days long sped, + we have heard, and what honor the athelings won! ++ + Oft Scyld the Scefing from squadroned foes, +@@ -2,7 +1,5 @@ awing the earls. Since erst he lay + friendless, a foundling, fate repaid him: ++ + for he waxed under welkin, in wealth he throve, +EOF + +cat > expected << EOF +diff --git a/file b/file +index b9834b5..ef6e94c 100644 +--- a/file ++++ b/file +@@ -1,10 +1,12 @@ + LO, praise of the prowess of people-kings + of spear-armed Danes, in days long sped, + we have heard, and what honor the athelings won! ++ + Oft Scyld the Scefing from squadroned foes, + from many a tribe, the mead-bench tore, + awing the earls. Since erst he lay + friendless, a foundling, fate repaid him: ++ + for he waxed under welkin, in wealth he throve, + till before him the folk, both far and near, + who house by the whale-path, heard his mandate, +EOF + +echo "#!$SHELL_PATH" >fake-editor.sh +cat >> fake-editor.sh <<\EOF +mv -f "$1" orig-patch && +mv -f patch "$1" +EOF + +test_set_editor "$(pwd)/fake-editor.sh" +chmod a+x fake-editor.sh + +test_expect_success 'add -e' ' + + cp fake-editor.sh file && + git add -e && + test_cmp fake-editor.sh file && + git diff --cached > out && + test_cmp out expected + +' + +cat > patch << EOF +diff --git a/file b/file +--- a/file ++++ b/file +@@ -1,1 +1,1 @@ + gave him gifts: a good king he! ++ +EOF + +test_expect_success 'add -e adds to the end of the file' ' + + test_tick && + git commit -m update && + git checkout && + git add -e && + git diff --cached > out && + test "" = "$(git show :file | tail -n 1)" + +' + +cat > patch << EOF +diff --git a/file b/file +--- a/file ++++ b/file +@@ -1,1 +1,1 @@ ++ + LO, praise of the prowess of people-kings +EOF + +test_expect_success 'add -e adds to the beginning of the file' ' + + test_tick && + git commit -m update && + git checkout && + git add -e && + git diff --cached > out && + test "" = "$(git show :file | head -n 1)" + +' + +test_done -- 1.5.6.84.ge5c1 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html