[PATCH 0/7] [un]stage: officially start moving to "staging area"

STALE1828d

12 messages, 3 authors, 2021-08-11 · open the first message on its own page

[PATCH 0/7] [un]stage: officially start moving to "staging area"

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:33

In the last 13 years of discussions virtually *everyone* has agreed that
the term "the index" is not a good approximation to how most users
perceive and utilize this feature. For a summary of the discssions see
my blog post [1].

This is particularly true of newcomers, which is why everyone that
teaches git uses the term "staging area".

Among all the proposals for a better name "staging area" is by far the
one with the most consensus.

Everyone except two people agreed that "the index" is not a good term.

All non-official documentation already uses the term "staging area" [2]
[3] [4], including what is considered by most people the best
documentation out there: the Pro Git book.

There is absolutely no reason not to start using the term "staging area"
officially.

Let's start by making the staging area a first-class citizen and making
'git stage' a prominent command, similar to 'git branch'. Additionally
add 'git unstage' too.

Only *one* person expressed discontent with the term "staging area".

In favor:

* Felipe Contreras
* Scott Chacon
* Jonathan Nieder
* Matthieu Moy
* Jeff King
* Miles Bader
* Ævar Arnfjörð Bjarmason
* Jay Soffian
* Pete Harlan
* Aghiles
* Piotr Krukowiecki
* Phil Hord
* Victor Engmark
* David (bouncingcats)
* Alexey Feldgendler
* Alexei Sholik
* Zbigniew Jędrzejewski-Szmek
* Sebastien Douche
* Thiago Farina
* Mark Lodato
* Philip Oakley
* William Swanson
* Ping Yin
* Hilco Wijbenga
* Lars Vogel
* David A. Wheeler

[1] https://felipec.wordpress.com/2021/08/10/git-staging-area-rename/
[2] https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
[3] https://www.atlassian.com/git/tutorials/saving-changes
[4] https://coderefinery.github.io/git-intro/04-staging-area/

Felipe Contreras (7):
  stage: add proper 'stage' command
  stage: add helper to run commands
  stage: add 'add' subcommand
  stage: add 'remove' subcommand
  unstage: add 'unstage' command
  stage: add 'diff' subcommand
  stage: add 'edit' command

 Documentation/git-stage.txt            |  38 ++++++-
 Documentation/git-unstage.txt          |  25 +++++
 Makefile                               |   2 +-
 builtin.h                              |   2 +
 builtin/stage.c                        | 147 +++++++++++++++++++++++++
 contrib/completion/git-completion.bash |   5 +
 git.c                                  |   3 +-
 t/t3710-stage.sh                       |  51 +++++++++
 8 files changed, 267 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/git-unstage.txt
 create mode 100644 builtin/stage.c
 create mode 100755 t/t3710-stage.sh

-- 
2.32.0.48.g096519100f

[PATCH 1/7] stage: add proper 'stage' command

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:33

This is still basically the same as `git add`, but now more easily
extendable.

Signed-off-by: Felipe Contreras <redacted>
---
 Documentation/git-stage.txt | 11 ++++++++---
 Makefile                    |  2 +-
 builtin.h                   |  1 +
 builtin/stage.c             | 23 +++++++++++++++++++++++
 git.c                       |  2 +-
 t/t3710-stage.sh            | 20 ++++++++++++++++++++
 6 files changed, 54 insertions(+), 5 deletions(-)
 create mode 100644 builtin/stage.c
 create mode 100755 t/t3710-stage.sh
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index 25bcda936d..3f7b036901 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -9,14 +9,19 @@ git-stage - Add file contents to the staging area
 SYNOPSIS
 --------
 [verse]
-'git stage' args...
+'git stage' [options] [--] [<paths>...]
 
 
 DESCRIPTION
 -----------
 
-This is a synonym for linkgit:git-add[1].  Please refer to the
-documentation of that command.
+The staging area is a location where changes are stored in preparation for a commit.
+
+This is a synonym for linkgit:git-add[1].
+
+SEE ALSO
+--------
+linkgit:git-add[1]
 
 GIT
 ---
diff --git a/Makefile b/Makefile
index c3565fc0f8..0223ed7cb1 100644
--- a/Makefile
+++ b/Makefile
@@ -779,7 +779,6 @@ BUILT_INS += git-maintenance$X
 BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-restore$X
 BUILT_INS += git-show$X
-BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
 BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
@@ -1153,6 +1152,7 @@ BUILTIN_OBJS += builtin/show-branch.o
 BUILTIN_OBJS += builtin/show-index.o
 BUILTIN_OBJS += builtin/show-ref.o
 BUILTIN_OBJS += builtin/sparse-checkout.o
+BUILTIN_OBJS += builtin/stage.o
 BUILTIN_OBJS += builtin/stash.o
 BUILTIN_OBJS += builtin/stripspace.o
 BUILTIN_OBJS += builtin/submodule--helper.o
diff --git a/builtin.h b/builtin.h
index 16ecd5586f..d08d803c4f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -218,6 +218,7 @@ int cmd_show(int argc, const char **argv, const char *prefix);
 int cmd_show_branch(int argc, const char **argv, const char *prefix);
 int cmd_show_index(int argc, const char **argv, const char *prefix);
 int cmd_sparse_checkout(int argc, const char **argv, const char *prefix);
+int cmd_stage(int argc, const char **argv, const char *prefix);
 int cmd_status(int argc, const char **argv, const char *prefix);
 int cmd_stash(int argc, const char **argv, const char *prefix);
 int cmd_stripspace(int argc, const char **argv, const char *prefix);
diff --git a/builtin/stage.c b/builtin/stage.c
new file mode 100644
index 0000000000..4dcefbedba
--- /dev/null
+++ b/builtin/stage.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013-2021 Felipe Contreras
+ */
+
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char *const stage_usage[] = {
+	N_("git stage [options] [--] <paths>..."),
+	NULL
+};
+
+int cmd_stage(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, stage_usage,
+		PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+
+	return cmd_add(argc, argv, prefix);
+}
diff --git a/git.c b/git.c
index 18bed9a996..3b92e60329 100644
--- a/git.c
+++ b/git.c
@@ -599,7 +599,7 @@ static struct cmd_struct commands[] = {
 	{ "show-index", cmd_show_index, RUN_SETUP_GENTLY },
 	{ "show-ref", cmd_show_ref, RUN_SETUP },
 	{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
-	{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+	{ "stage", cmd_stage, RUN_SETUP | NEED_WORK_TREE },
 	{ "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
diff --git a/t/t3710-stage.sh b/t/t3710-stage.sh
new file mode 100755
index 0000000000..2bf59905ca
--- /dev/null
+++ b/t/t3710-stage.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (C) 2021 Felipe Contreras
+#
+
+test_description='Tests of git stage'
+
+. ./test-lib.sh
+
+in_stage () {
+	test "$(git ls-files "$1")" == "$1"
+}
+
+test_expect_success 'basic' '
+	touch foo &&
+	git stage foo &&
+	in_stage foo
+'
+
+test_done
-- 
2.32.0.48.g096519100f

[PATCH 2/7] stage: add helper to run commands

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:39

Will be useful to run other commands.

Signed-off-by: Felipe Contreras <redacted>
---
 builtin/stage.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/builtin/stage.c b/builtin/stage.c
index 4dcefbedba..49016b0d5f 100644
--- a/builtin/stage.c
+++ b/builtin/stage.c
@@ -4,12 +4,34 @@
 
 #include "builtin.h"
 #include "parse-options.h"
+#include "run-command.h"
 
 static const char *const stage_usage[] = {
 	N_("git stage [options] [--] <paths>..."),
 	NULL
 };
 
+static int rerun(int argc, const char **argv, ...)
+{
+	int ret;
+	struct strvec args = STRVEC_INIT;
+	va_list ap;
+	const char *arg;
+	int i;
+
+	va_start(ap, argv);
+	while ((arg = va_arg(ap, const char *)))
+		strvec_push(&args, arg);
+	va_end(ap);
+
+	for (i = 0; i < argc; i++)
+		strvec_push(&args, argv[i]);
+
+	ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+	strvec_clear(&args);
+	return ret;
+}
+
 int cmd_stage(int argc, const char **argv, const char *prefix)
 {
 	struct option options[] = {
@@ -17,7 +39,7 @@ int cmd_stage(int argc, const char **argv, const char *prefix)
 	};
 
 	argc = parse_options(argc, argv, prefix, options, stage_usage,
-		PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+		PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
 
-	return cmd_add(argc, argv, prefix);
+	return rerun(argc, argv, "add", NULL);
 }
-- 
2.32.0.48.g096519100f

[PATCH 3/7] stage: add 'add' subcommand

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:39

Signed-off-by: Felipe Contreras <redacted>
---
 Documentation/git-stage.txt            | 15 ++++++++++++---
 builtin/stage.c                        |  4 ++++
 contrib/completion/git-completion.bash |  5 +++++
 t/t3710-stage.sh                       |  6 ++++++
 4 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index 3f7b036901..348d7d1d92 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -3,21 +3,30 @@ git-stage(1)
 
 NAME
 ----
-git-stage - Add file contents to the staging area
+git-stage - Manage the staging area
 
 
 SYNOPSIS
 --------
 [verse]
 'git stage' [options] [--] [<paths>...]
+'git stage' (-a | --add) [options] [--] [<paths>...]
 
 
 DESCRIPTION
 -----------
 
-The staging area is a location where changes are stored in preparation for a commit.
+This command is useful to manage the staging area which is a location where
+changes are stored in preparation for a commit.
+
+Without arguments it's a synonym for linkgit:git-add[1].
+
+OPTIONS
+-------
+-a::
+--add::
+	Add changes to the staging area. See linkgit:git-add[1].
 
-This is a synonym for linkgit:git-add[1].
 
 SEE ALSO
 --------
diff --git a/builtin/stage.c b/builtin/stage.c
index 49016b0d5f..5a80bbc76c 100644
--- a/builtin/stage.c
+++ b/builtin/stage.c
@@ -8,6 +8,7 @@
 
 static const char *const stage_usage[] = {
 	N_("git stage [options] [--] <paths>..."),
+	N_("git stage --add [options] [--] <paths>..."),
 	NULL
 };
 
@@ -34,7 +35,10 @@ static int rerun(int argc, const char **argv, ...)
 
 int cmd_stage(int argc, const char **argv, const char *prefix)
 {
+	int add = 0;
+
 	struct option options[] = {
+		OPT_BOOL_F('a', "add", &add, N_("add changes"), PARSE_OPT_NONEG),
 		OPT_END()
 	};
 
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b50c5d0ea3..8656c47f39 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2376,6 +2376,11 @@ _git_send_email ()
 
 _git_stage ()
 {
+	if [[ "$cur" == --* ]]; then
+		__gitcomp_builtin stage
+		return
+	fi
+
 	_git_add
 }
 
diff --git a/t/t3710-stage.sh b/t/t3710-stage.sh
index 2bf59905ca..225c6dd739 100755
--- a/t/t3710-stage.sh
+++ b/t/t3710-stage.sh
@@ -17,4 +17,10 @@ test_expect_success 'basic' '
 	in_stage foo
 '
 
+test_expect_success 'add' '
+	touch bar &&
+	git stage --add bar &&
+	in_stage bar
+'
+
 test_done
-- 
2.32.0.48.g096519100f

[PATCH 4/7] stage: add 'remove' subcommand

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:41

Signed-off-by: Felipe Contreras <redacted>
---
 Documentation/git-stage.txt | 6 ++++++
 builtin/stage.c             | 7 ++++++-
 t/t3710-stage.sh            | 5 +++++
 3 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index 348d7d1d92..e2f83783b2 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git stage' [options] [--] [<paths>...]
 'git stage' (-a | --add) [options] [--] [<paths>...]
+'git stage' (-r | --remove) [options] [--] [<paths>...]
 
 
 DESCRIPTION
@@ -27,10 +28,15 @@ OPTIONS
 --add::
 	Add changes to the staging area. See linkgit:git-add[1].
 
+-r::
+--remove::
+	Remove changes from the staging area. See linkgit:git-reset[1].
+
 
 SEE ALSO
 --------
 linkgit:git-add[1]
+linkgit:git-reset[1]
 
 GIT
 ---
diff --git a/builtin/stage.c b/builtin/stage.c
index 5a80bbc76c..dee8781dc5 100644
--- a/builtin/stage.c
+++ b/builtin/stage.c
@@ -9,6 +9,7 @@
 static const char *const stage_usage[] = {
 	N_("git stage [options] [--] <paths>..."),
 	N_("git stage --add [options] [--] <paths>..."),
+	N_("git stage --remove [options] [--] <paths>..."),
 	NULL
 };
 
@@ -35,15 +36,19 @@ static int rerun(int argc, const char **argv, ...)
 
 int cmd_stage(int argc, const char **argv, const char *prefix)
 {
-	int add = 0;
+	int add = 0, remove = 0;
 
 	struct option options[] = {
 		OPT_BOOL_F('a', "add", &add, N_("add changes"), PARSE_OPT_NONEG),
+		OPT_BOOL_F('r', "remove", &remove, N_("remove changes"), PARSE_OPT_NONEG),
 		OPT_END()
 	};
 
 	argc = parse_options(argc, argv, prefix, options, stage_usage,
 		PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
 
+	if (remove)
+		return rerun(argc, argv, "reset", NULL);
+
 	return rerun(argc, argv, "add", NULL);
 }
diff --git a/t/t3710-stage.sh b/t/t3710-stage.sh
index 225c6dd739..209f2bde9a 100755
--- a/t/t3710-stage.sh
+++ b/t/t3710-stage.sh
@@ -23,4 +23,9 @@ test_expect_success 'add' '
 	in_stage bar
 '
 
+test_expect_success 'remove' '
+	git stage --remove bar &&
+	! in_stage bar
+'
+
 test_done
-- 
2.32.0.48.g096519100f

[PATCH 5/7] unstage: add 'unstage' command

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:44

Signed-off-by: Felipe Contreras <redacted>
---
 Documentation/git-unstage.txt | 25 +++++++++++++++++++++++++
 builtin.h                     |  1 +
 builtin/stage.c               | 15 +++++++++++++++
 git.c                         |  1 +
 t/t3710-stage.sh              |  7 +++++++
 5 files changed, 49 insertions(+)
 create mode 100644 Documentation/git-unstage.txt
diff --git a/Documentation/git-unstage.txt b/Documentation/git-unstage.txt
new file mode 100644
index 0000000000..cafc24cb36
--- /dev/null
+++ b/Documentation/git-unstage.txt
@@ -0,0 +1,25 @@
+git-unstage(1)
+==============
+
+NAME
+----
+git-unstage - Remove changes from the staging area
+
+
+SYNOPSIS
+--------
+[verse]
+'git unstage' [options] [--] [<paths>...]
+
+DESCRIPTION
+-----------
+
+Removes changes from the staging area. This is the same as `git reset`.
+
+SEE ALSO
+--------
+linkgit:git-reset[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/builtin.h b/builtin.h
index d08d803c4f..326f8af3e7 100644
--- a/builtin.h
+++ b/builtin.h
@@ -229,6 +229,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix);
 int cmd_tar_tree(int argc, const char **argv, const char *prefix);
 int cmd_unpack_file(int argc, const char **argv, const char *prefix);
 int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
+int cmd_unstage(int argc, const char **argv, const char *prefix);
 int cmd_update_index(int argc, const char **argv, const char *prefix);
 int cmd_update_ref(int argc, const char **argv, const char *prefix);
 int cmd_update_server_info(int argc, const char **argv, const char *prefix);
diff --git a/builtin/stage.c b/builtin/stage.c
index dee8781dc5..2d50b3c393 100644
--- a/builtin/stage.c
+++ b/builtin/stage.c
@@ -13,6 +13,11 @@ static const char *const stage_usage[] = {
 	NULL
 };
 
+static const char *const unstage_usage[] = {
+	N_("git unstage [options] [--] <paths>..."),
+	NULL
+};
+
 static int rerun(int argc, const char **argv, ...)
 {
 	int ret;
@@ -52,3 +57,13 @@ int cmd_stage(int argc, const char **argv, const char *prefix)
 
 	return rerun(argc, argv, "add", NULL);
 }
+
+int cmd_unstage(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = { OPT_END() };
+
+	argc = parse_options(argc, argv, prefix, options, unstage_usage,
+		PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+
+	return rerun(argc, argv, "reset", NULL);
+}
diff --git a/git.c b/git.c
index 3b92e60329..2d319cc15b 100644
--- a/git.c
+++ b/git.c
@@ -607,6 +607,7 @@ static struct cmd_struct commands[] = {
 	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
+	{ "unstage", cmd_unstage, RUN_SETUP | NEED_WORK_TREE },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
 	{ "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
 	{ "update-index", cmd_update_index, RUN_SETUP },
diff --git a/t/t3710-stage.sh b/t/t3710-stage.sh
index 209f2bde9a..834eee66d5 100755
--- a/t/t3710-stage.sh
+++ b/t/t3710-stage.sh
@@ -28,4 +28,11 @@ test_expect_success 'remove' '
 	! in_stage bar
 '
 
+test_expect_success 'unstage' '
+	touch bar &&
+	git stage bar &&
+	git unstage bar &&
+	! in_stage bar
+'
+
 test_done
-- 
2.32.0.48.g096519100f

[PATCH 6/7] stage: add 'diff' subcommand

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:45

Signed-off-by: Felipe Contreras <redacted>
---
 Documentation/git-stage.txt | 5 +++++
 builtin/stage.c             | 6 +++++-
 t/t3710-stage.sh            | 7 +++++++
 3 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index e2f83783b2..460a8d6228 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -12,6 +12,7 @@ SYNOPSIS
 'git stage' [options] [--] [<paths>...]
 'git stage' (-a | --add) [options] [--] [<paths>...]
 'git stage' (-r | --remove) [options] [--] [<paths>...]
+'git stage' (-d | --diff) [options] [--] [<paths>...]
 
 
 DESCRIPTION
@@ -32,11 +33,15 @@ OPTIONS
 --remove::
 	Remove changes from the staging area. See linkgit:git-reset[1].
 
+-d::
+--diff::
+	View the changes staged for the next commit. See linkgit:git-diff[1].
 
 SEE ALSO
 --------
 linkgit:git-add[1]
 linkgit:git-reset[1]
+linkgit:git-diff[1]
 
 GIT
 ---
diff --git a/builtin/stage.c b/builtin/stage.c
index 2d50b3c393..c57bb2d683 100644
--- a/builtin/stage.c
+++ b/builtin/stage.c
@@ -10,6 +10,7 @@ static const char *const stage_usage[] = {
 	N_("git stage [options] [--] <paths>..."),
 	N_("git stage --add [options] [--] <paths>..."),
 	N_("git stage --remove [options] [--] <paths>..."),
+	N_("git stage --diff [options] [<commit>] [--] <paths>..."),
 	NULL
 };
 
@@ -41,11 +42,12 @@ static int rerun(int argc, const char **argv, ...)
 
 int cmd_stage(int argc, const char **argv, const char *prefix)
 {
-	int add = 0, remove = 0;
+	int add = 0, remove = 0, diff = 0;
 
 	struct option options[] = {
 		OPT_BOOL_F('a', "add", &add, N_("add changes"), PARSE_OPT_NONEG),
 		OPT_BOOL_F('r', "remove", &remove, N_("remove changes"), PARSE_OPT_NONEG),
+		OPT_BOOL_F('d', "diff", &diff, N_("show changes"), PARSE_OPT_NONEG),
 		OPT_END()
 	};
 
@@ -54,6 +56,8 @@ int cmd_stage(int argc, const char **argv, const char *prefix)
 
 	if (remove)
 		return rerun(argc, argv, "reset", NULL);
+	if (diff)
+		return rerun(argc, argv, "diff", "--staged", NULL);
 
 	return rerun(argc, argv, "add", NULL);
 }
diff --git a/t/t3710-stage.sh b/t/t3710-stage.sh
index 834eee66d5..aab979c20c 100755
--- a/t/t3710-stage.sh
+++ b/t/t3710-stage.sh
@@ -35,4 +35,11 @@ test_expect_success 'unstage' '
 	! in_stage bar
 '
 
+test_expect_success 'diff' '
+	echo foo > foo &&
+	git stage --add foo &&
+	git stage --diff > out &&
+	test_file_not_empty out
+'
+
 test_done
-- 
2.32.0.48.g096519100f

[PATCH 7/7] stage: add 'edit' command

From: Felipe Contreras <hidden>
Date: 2021-08-11 04:57:47

Signed-off-by: Felipe Contreras <redacted>
---
 Documentation/git-stage.txt |  5 +++
 builtin/stage.c             | 76 ++++++++++++++++++++++++++++++++++++-
 t/t3710-stage.sh            |  6 +++
 3 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index 460a8d6228..baea9d96e0 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git stage' (-a | --add) [options] [--] [<paths>...]
 'git stage' (-r | --remove) [options] [--] [<paths>...]
 'git stage' (-d | --diff) [options] [--] [<paths>...]
+'git stage' (-e | --edit)
 
 
 DESCRIPTION
@@ -37,6 +38,10 @@ OPTIONS
 --diff::
 	View the changes staged for the next commit. See linkgit:git-diff[1].
 
+-e::
+--edit::
+	Edit the changes in the staging area.
+
 SEE ALSO
 --------
 linkgit:git-add[1]
diff --git a/builtin/stage.c b/builtin/stage.c
index c57bb2d683..49af18dda7 100644
--- a/builtin/stage.c
+++ b/builtin/stage.c
@@ -5,6 +5,9 @@
 #include "builtin.h"
 #include "parse-options.h"
 #include "run-command.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
 
 static const char *const stage_usage[] = {
 	N_("git stage [options] [--] <paths>..."),
@@ -40,14 +43,83 @@ static int rerun(int argc, const char **argv, ...)
 	return ret;
 }
 
+static int do_reset(const char *prefix)
+{
+	const char *argv[] = { "reset", "--quiet", NULL };
+	return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int do_apply(const char *file, const char *prefix)
+{
+	const char *argv[] = { "apply", "--recount", "--cached", file, NULL };
+	return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int run_edit(int argc, const char **argv, const char *prefix)
+{
+	char *file = git_pathdup("STAGE_EDIT.patch");
+	int out;
+	struct rev_info rev;
+	int ret = 0;
+	struct stat st;
+
+	repo_read_index(the_repository);
+
+	repo_init_revisions(the_repository, &rev, prefix);
+	rev.diffopt.context = 7;
+
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	add_head_to_pending(&rev);
+	if (!rev.pending.nr) {
+		struct tree *tree;
+		tree = lookup_tree(the_repository, the_repository->hash_algo->empty_tree);
+		add_pending_object(&rev, &tree->object, "HEAD");
+	}
+
+	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+	rev.diffopt.use_color = 0;
+	rev.diffopt.flags.ignore_dirty_submodules = 1;
+
+	out = open(file, O_CREAT | O_WRONLY, 0666);
+	if (out < 0)
+		die(_("Could not open '%s' for writing."), file);
+	rev.diffopt.file = xfdopen(out, "w");
+	rev.diffopt.close_file = 1;
+
+	if (run_diff_index(&rev, DIFF_INDEX_CACHED))
+		die(_("Could not write patch"));
+	if (launch_editor(file, NULL, NULL))
+		exit(1);
+
+	if (stat(file, &st))
+		die_errno(_("Could not stat '%s'"), file);
+
+	ret = do_reset(prefix);
+	if (ret)
+		goto leave;
+
+	if (!st.st_size)
+		goto leave;
+
+	ret = do_apply(file, prefix);
+	if (ret)
+		goto leave;
+
+leave:
+	unlink(file);
+	free(file);
+	return ret;
+}
+
 int cmd_stage(int argc, const char **argv, const char *prefix)
 {
-	int add = 0, remove = 0, diff = 0;
+	int add = 0, remove = 0, diff = 0, edit = 0;
 
 	struct option options[] = {
 		OPT_BOOL_F('a', "add", &add, N_("add changes"), PARSE_OPT_NONEG),
 		OPT_BOOL_F('r', "remove", &remove, N_("remove changes"), PARSE_OPT_NONEG),
 		OPT_BOOL_F('d', "diff", &diff, N_("show changes"), PARSE_OPT_NONEG),
+		OPT_BOOL_F('e', "edit", &edit, N_("edit changes"), PARSE_OPT_NONEG),
 		OPT_END()
 	};
 
@@ -58,6 +130,8 @@ int cmd_stage(int argc, const char **argv, const char *prefix)
 		return rerun(argc, argv, "reset", NULL);
 	if (diff)
 		return rerun(argc, argv, "diff", "--staged", NULL);
+	if (edit)
+		return run_edit(argc, argv, prefix);
 
 	return rerun(argc, argv, "add", NULL);
 }
diff --git a/t/t3710-stage.sh b/t/t3710-stage.sh
index aab979c20c..3c9522249f 100755
--- a/t/t3710-stage.sh
+++ b/t/t3710-stage.sh
@@ -42,4 +42,10 @@ test_expect_success 'diff' '
 	test_file_not_empty out
 '
 
+test_expect_success 'edit' '
+	GIT_EDITOR="sed -i -e \"s/^+foo$/+edit/\"" git stage --edit &&
+	git stage --diff > out &&
+	grep "^+edit$" out
+'
+
 test_done
-- 
2.32.0.48.g096519100f

Re: [PATCH 6/7] stage: add 'diff' subcommand

From: Bagas Sanjaya <hidden>
Date: 2021-08-11 06:12:17

On 11/08/21 11.57, Felipe Contreras wrote:
quoted hunk
@@ -12,6 +12,7 @@ SYNOPSIS
  'git stage' [options] [--] [<paths>...]
  'git stage' (-a | --add) [options] [--] [<paths>...]
  'git stage' (-r | --remove) [options] [--] [<paths>...]
+'git stage' (-d | --diff) [options] [--] [<paths>...]
  
  
  DESCRIPTION
@@ -32,11 +33,15 @@ OPTIONS
  --remove::
  	Remove changes from the staging area. See linkgit:git-reset[1].
  
+-d::
+--diff::
+	View the changes staged for the next commit. See linkgit:git-diff[1].
  
Is it synonym to `git diff --staged`?

-- 
An old man doll... just what I always wanted! - Clara

Re: [PATCH 6/7] stage: add 'diff' subcommand

From: Felipe Contreras <hidden>
Date: 2021-08-11 07:24:40

Bagas Sanjaya wrote:
On 11/08/21 11.57, Felipe Contreras wrote:
quoted
@@ -12,6 +12,7 @@ SYNOPSIS
  'git stage' [options] [--] [<paths>...]
  'git stage' (-a | --add) [options] [--] [<paths>...]
  'git stage' (-r | --remove) [options] [--] [<paths>...]
+'git stage' (-d | --diff) [options] [--] [<paths>...]
  
  
  DESCRIPTION
@@ -32,11 +33,15 @@ OPTIONS
  --remove::
  	Remove changes from the staging area. See linkgit:git-reset[1].
  
+-d::
+--diff::
+	View the changes staged for the next commit. See linkgit:git-diff[1].
  
Is it synonym to `git diff --staged`?
Yes, it's the same thing.

Although from discussions in 2013 people found it odd that the option is
--staged when specifying something that affects the future. It should
probably be `git diff --stage`.

-- 
Felipe Contreras

Re: [PATCH 0/7] [un]stage: officially start moving to "staging area"

From: Michael J Gruber <hidden>
Date: 2021-08-11 10:50:41

Felipe Contreras venit, vidit, dixit 2021-08-11 06:57:20:
In the last 13 years of discussions virtually *everyone* has agreed that
the term "the index" is not a good approximation to how most users
perceive and utilize this feature. For a summary of the discssions see
my blog post [1].

This is particularly true of newcomers, which is why everyone that
teaches git uses the term "staging area".

Among all the proposals for a better name "staging area" is by far the
one with the most consensus.

Everyone except two people agreed that "the index" is not a good term.

All non-official documentation already uses the term "staging area" [2]
[3] [4], including what is considered by most people the best
documentation out there: the Pro Git book.

There is absolutely no reason not to start using the term "staging area"
officially.

Let's start by making the staging area a first-class citizen and making
'git stage' a prominent command, similar to 'git branch'. Additionally
add 'git unstage' too.

Only *one* person expressed discontent with the term "staging area".

In favor:

* Felipe Contreras
* Scott Chacon
* Jonathan Nieder
* Matthieu Moy
* Jeff King
* Miles Bader
* Ævar Arnfjörð Bjarmason
* Jay Soffian
* Pete Harlan
* Aghiles
* Piotr Krukowiecki
* Phil Hord
* Victor Engmark
* David (bouncingcats)
* Alexey Feldgendler
* Alexei Sholik
* Zbigniew Jędrzejewski-Szmek
* Sebastien Douche
* Thiago Farina
* Mark Lodato
* Philip Oakley
* William Swanson
* Ping Yin
* Hilco Wijbenga
* Lars Vogel
* David A. Wheeler

[1] https://felipec.wordpress.com/2021/08/10/git-staging-area-rename/
[2] https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
[3] https://www.atlassian.com/git/tutorials/saving-changes
[4] https://coderefinery.github.io/git-intro/04-staging-area/
Of the many possible ways to restart this discussion, calling out names
and pitching people against each other is quite possibly the worst.
Incidentally, this proves (again) what I wrote back then.

Git (the project) has always been about the strive for the "best"
solution, and different people have different views about the metrics
(technically sound, user friedly, consistency, backwards compatibility ...),
their scales and their relative weights - and the same is true for
judging a particular suggested solution in these metrics.

There are definitely more than two "staging camps" in this community -
you can come up with a count of "2" only if you conflate a multitude of
aspects, such as:

- use the term staging area in documentation
- scratch the term index from the documentation
- use "--staged" option aliases
- rename "--cached" option to "--staged"
- use "stage" command alias for "add"
- rename "add" to "stage" (and possibly revamp)
- use pseudorev STAGE (and dump "--cached")
- use pseudorev INDEX (and dump "--cached")
- one of the above plus WORKTREE
- ...

I would in fact think that this community is comprised of individuals
rather than camps, and that overall it does not object against the use of
"stage/staging area". At the same time, the fact that 13 years on we
still have the same "git diff --cached" etc. indicates that we never had
an alternative concept and implementation which the majority agreed on.
So, depending on how you conflate the aspects above into two, either the
"pro" or the "contra" camp consists of 1 person, and you can certainly
reach most 2-partitionings.

Just imagine someone who thinks purely in terms of "technically sound"
and "backwards compatible" (you know who you are ;) ): that person will
never be "pro stage" or "contra stage", but if they don't like your
implementation of "git stage" you will put them in the "contra camp",
giving up all chances of winning them over technically.

[As a side note, index might be more l10n-friendly than stage because of
its latin origin (so it's there in more languages already).]

I have been away from the project quite a bit (merely for lack of time),
but some people will remember me as striving for "consistency" in the UI
(e.g., favouring "git diff [HEAD] INDEX" over "git diff --cached [HEAD]").

Independent of my remarks about the restart of the discussion, I had a
quick glance at this series. The questions which arose already around
"stage/staged" seem to indicate that the series is more about renaming,
some reorganising but not so much about a fresh look at a consistent
user experience.

In other words (and to show a way forward), a cover letter with the
potential to bring many on board could start with:

##

It has been 13 years that we discussed about "the index", "the cache",
"staging" and the "staging area". There seemed to be overall consensus
that the concept of "put something on stage that is to be committed [to]"
serves well in training and documentation when we explain git's index
and related commands which change the index (git add) and compare to it
(git diff --cached).

Let's try and find a common ground for implementing this in user facing
commands. Here are a few possible appoaches:
- command/option aliases
- command/option renaming
- revamping of the command structure (as we did "checkout->switch")
- revamping the rev UI (from "--cached" to "INDEX" or "STAGE")
- you name it

To kick start this, here are a few more thoughts on some of these/an
example implemenation for one of these/...

##


Cheers
Michael

Re: [PATCH 0/7] [un]stage: officially start moving to "staging area"

From: Felipe Contreras <hidden>
Date: 2021-08-11 16:55:34

Michael J Gruber wrote:
Felipe Contreras venit, vidit, dixit 2021-08-11 06:57:20:
quoted
In the last 13 years of discussions virtually *everyone* has agreed that
the term "the index" is not a good approximation to how most users
perceive and utilize this feature. For a summary of the discssions see
my blog post [1].

This is particularly true of newcomers, which is why everyone that
teaches git uses the term "staging area".

Among all the proposals for a better name "staging area" is by far the
one with the most consensus.

Everyone except two people agreed that "the index" is not a good term.

All non-official documentation already uses the term "staging area" [2]
[3] [4], including what is considered by most people the best
documentation out there: the Pro Git book.

There is absolutely no reason not to start using the term "staging area"
officially.

Let's start by making the staging area a first-class citizen and making
'git stage' a prominent command, similar to 'git branch'. Additionally
add 'git unstage' too.

Only *one* person expressed discontent with the term "staging area".

In favor:

* Felipe Contreras
* Scott Chacon
* Jonathan Nieder
* Matthieu Moy
* Jeff King
* Miles Bader
* Ævar Arnfjörð Bjarmason
* Jay Soffian
* Pete Harlan
* Aghiles
* Piotr Krukowiecki
* Phil Hord
* Victor Engmark
* David (bouncingcats)
* Alexey Feldgendler
* Alexei Sholik
* Zbigniew Jędrzejewski-Szmek
* Sebastien Douche
* Thiago Farina
* Mark Lodato
* Philip Oakley
* William Swanson
* Ping Yin
* Hilco Wijbenga
* Lars Vogel
* David A. Wheeler

[1] https://felipec.wordpress.com/2021/08/10/git-staging-area-rename/
[2] https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
[3] https://www.atlassian.com/git/tutorials/saving-changes
[4] https://coderefinery.github.io/git-intro/04-staging-area/
Of the many possible ways to restart this discussion, calling out
names and pitching people against each other is quite possibly the
worst.  Incidentally, this proves (again) what I wrote back then.
Everyone already agrees that "staging area" is a better term than "the
index".
Git (the project) has always been about the strive for the "best"
solution, and different people have different views about the metrics
(technically sound, user friedly, consistency, backwards compatibility ...),
their scales and their relative weights - and the same is true for
judging a particular suggested solution in these metrics.

There are definitely more than two "staging camps" in this community -
you can come up with a count of "2" only if you conflate a multitude of
aspects, such as:
For any question there's always three camps: in favor, against, neutral.

In the particular question of "staging area" versus "the index" the
consensus is overwhelmingly in favor, very very few are neutral, and
only one person is against.
- use the term staging area in documentation
- scratch the term index from the documentation
- use "--staged" option aliases
- rename "--cached" option to "--staged"
- use "stage" command alias for "add"
- rename "add" to "stage" (and possibly revamp)
- use pseudorev STAGE (and dump "--cached")
- use pseudorev INDEX (and dump "--cached")
- one of the above plus WORKTREE
- ...
Each one of these questions has different people in
favor/against/neutral and they are mostly orthogonal to each other. For
example a person can be in favor of adding the term "staging area" in
the documentation but neutral to the "stage" command alias.

Either way the whole point of my proposal was to shine a light to the
question we have all agreed to: "staging area" versus "the index".

This means by extension that everyone (except one person) would be in
favor of using the term "staging area" in the documentation, which is
your first point.

Each one of those points could be implemented in a different patch
series, and each patch series can be discussed separately.

But my main concern is the *first* patch series, regardless of which it
is, so the term finally becomes official. If you don't like the first
patch series that I chose, then please state which would it be easier
for you to get behind and I'll gladly work on that.

I also have a patch series that implements --stage everywhere, and that
series already received feedback in 2014.

The reason why I chose this point is that 1) it's the one that I find
more useful, 2) it's the one that it's easier to implement, 3)
already received positive feedback, and 4) I wasn't the only one who
suggested this.

In case you don't remember, you yourself stated "in principle I like this
a lot" [1] back in 2011.
I would in fact think that this community is comprised of individuals
rather than camps, and that overall it does not object against the use of
"stage/staging area".
Junio objects to the use of the term "staging area".
At the same time, the fact that 13 years on we still have the same
"git diff --cached" etc. indicates that we never had an alternative
concept and implementation which the majority agreed on.
No it doesn't. Git is not a democracy: the majority can agree on
something and yet if Junio disagrees, that's the end of that (as is the
case here).
So, depending on how you conflate the aspects above into two, either the
"pro" or the "contra" camp consists of 1 person, and you can certainly
reach most 2-partitionings.

Just imagine someone who thinks purely in terms of "technically sound"
and "backwards compatible" (you know who you are ;) ): that person will
never be "pro stage" or "contra stage", but if they don't like your
implementation of "git stage" you will put them in the "contra camp",
giving up all chances of winning them over technically.
No. Being against a particular patch series doesn't necessarily mean
against the spirit of the patch series.

If that person would rather start with the --stage aliases, or using
"staging area" in the documentation, then that person can simply state
so on this patch series, and I will work on that instead.
[As a side note, index might be more l10n-friendly than stage because of
its latin origin (so it's there in more languages already).]
We don't need to go into the hypotheticals of "might" because this has
already been discussed, and translators already agreed that "staging
area" is better.

Additionally Ævar Arnfjörð Bjarmason explained [2] that it makes no sense to
look for a term in English that is easier to translate, but instead the
better term in English should be picked, and then that meaning is what
gets translated.

All this is mentioned in my blog post.
I have been away from the project quite a bit (merely for lack of time),
but some people will remember me as striving for "consistency" in the UI
(e.g., favouring "git diff [HEAD] INDEX" over "git diff --cached [HEAD]").

Independent of my remarks about the restart of the discussion, I had a
quick glance at this series. The questions which arose already around
"stage/staged" seem to indicate that the series is more about renaming,
some reorganising but not so much about a fresh look at a consistent
user experience.
I did have a "fresh look" at a consistent user experience, and I did
send those patches in 2014 [3]. However, those patches had no hope of
going anywhere until Junio agrees to start using the term "staging area"
officially. So that's what this patch series attempts to do (although
I'm not hopeful in the least).
In other words (and to show a way forward), a cover letter with the
potential to bring many on board could start with:
Once again everyone is already on board.
##

It has been 13 years that we discussed about "the index", "the cache",
"staging" and the "staging area". There seemed to be overall consensus
that the concept of "put something on stage that is to be committed [to]"
serves well in training and documentation when we explain git's index
and related commands which change the index (git add) and compare to it
(git diff --cached).
It didn't "seem" there was overall consensus, there *was* close to 100%
consensus.
Let's try and find a common ground for implementing this in user facing
commands. Here are a few possible appoaches:
- command/option aliases
- command/option renaming
- revamping of the command structure (as we did "checkout->switch")
- revamping the rev UI (from "--cached" to "INDEX" or "STAGE")
- you name it
Most of these are not practical. For example why start renaming if we
can start with aliases, and then later on deprecate the old ones, and
finally obsolete them?

And again, these have already been discussed before and what people
seemed to gravitate towards are:

 * Officially use "staging area" instead of "the index" in the
   documentation
 * Add --stage aliases instead of --cache and --index
 * Make `git stage` more prominent

Anything else would require more discussion, and that's not my goal
here (not yet).
To kick start this, here are a few more thoughts on some of these/an
example implemenation for one of these/...

##
I'm all in favor of changing the cover letter and even pick another
point on the list to officially start moving to the term "staging area",
but I think you are missing the main point:

Consensus has *already* been reached.

In fact, in all my years working on git I have never seen a UI change
proposal with more consensus.

Cheers.

[1] https://lore.kernel.org/git/4D594911.40409@drmicha.warpmail.net/
[2] https://lore.kernel.org/git/CACBZZX7LOKqKNe09636N0xJ_VvmKP58BMDtjoKn1t5e9hJ0OiQ@mail.gmail.com/
[3] https://lore.kernel.org/git/1398449567-16314-1-git-send-email-felipe.contreras@gmail.com/

-- 
Felipe Contreras
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help