From 366bfcb68f4d98a43faaf17893a1aa0a7a9e2c58 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 4 Dec 2006 11:13:39 -0500 Subject: make 'git add' a first class user friendly interface to the index This brings the power of the index up front using a proper mental model without talking about the index at all. See for example how all the technical discussion has been evacuated from the git-add man page. Any content to be committed must be added together. Whether that content comes from new files or modified files doesn't matter. You just need to "add" it, either with git-add, or by providing git-commit with -a (for already known files only of course). No need for a separate command to distinguish new vs modified files please. That would only screw the mental model everybody should have when using GIT. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-add.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index febb75ed99..b3f920676a 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -94,9 +94,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1); - if (read_cache() < 0) - die("index file corrupt"); - for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -131,6 +128,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) return 0; } + if (read_cache() < 0) + die("index file corrupt"); + for (i = 0; i < dir.nr; i++) add_file_to_index(dir.entries[i]->name, verbose); -- cgit v1.2.1 From 5cde71d64aff03d305099b4d239552679ecfaab6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Dec 2006 20:55:50 -0800 Subject: git-add --interactive A script to be driven when the user says "git add --interactive" is introduced. When it is run, first it runs its internal 'status' command to show the current status, and then goes into its internactive command loop. The command loop shows the list of subcommands available, and gives a prompt "What now> ". In general, when the prompt ends with a single '>', you can pick only one of the choices given and type return, like this: *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 1 You also could say "s" or "sta" or "status" above as long as the choice is unique. The main command loop has 6 subcommands (plus help and quit). * 'status' shows the change between HEAD and index (i.e. what will be committed if you say "git commit"), and between index and working tree files (i.e. what you could stage further before "git commit" using "git-add") for each path. A sample output looks like this: staged unstaged path 1: binary nothing foo.png 2: +403/-35 +1/-1 git-add--interactive.perl It shows that foo.png has differences from HEAD (but that is binary so line count cannot be shown) and there is no difference between indexed copy and the working tree version (if the working tree version were also different, 'binary' would have been shown in place of 'nothing'). The other file, git-add--interactive.perl, has 403 lines added and 35 lines deleted if you commit what is in the index, but working tree file has further modifications (one addition and one deletion). * 'update' shows the status information and gives prompt "Update>>". When the prompt ends with double '>>', you can make more than one selection, concatenated with whitespace or comma. Also you can say ranges. E.g. "2-5 7,9" to choose 2,3,4,5,7,9 from the list. You can say '*' to choose everything. What you chose are then highlighted with '*', like this: staged unstaged path 1: binary nothing foo.png * 2: +403/-35 +1/-1 git-add--interactive.perl To remove selection, prefix the input with - like this: Update>> -2 After making the selection, answer with an empty line to stage the contents of working tree files for selected paths in the index. * 'revert' has a very similar UI to 'update', and the staged information for selected paths are reverted to that of the HEAD version. Reverting new paths makes them untracked. * 'add untracked' has a very similar UI to 'update' and 'revert', and lets you add untracked paths to the index. * 'patch' lets you choose one path out of 'status' like selection. After choosing the path, it presents diff between the index and the working tree file and asks you if you want to stage the change of each hunk. You can say: y - add the change from that hunk to index n - do not add the change from that hunk to index a - add the change from that hunk and all the rest to index d - do not the change from that hunk nor any of the rest to index j - do not decide on this hunk now, and view the next undecided hunk J - do not decide on this hunk now, and view the next hunk k - do not decide on this hunk now, and view the previous undecided hunk K - do not decide on this hunk now, and view the previous hunk After deciding the fate for all hunks, if there is any hunk that was chosen, the index is updated with the selected hunks. * 'diff' lets you review what will be committed (i.e. between HEAD and index). This is still rough, but does everything except a few things I think are needed. * 'patch' should be able to allow splitting a hunk into multiple hunks. * 'patch' does not adjust the line offsets @@ -k,l +m,n @@ in the hunk header. This does not have major problem in practice, but it _should_ do the adjustment. * It does not have any explicit support for a merge in progress; it may not work at all. Signed-off-by: Junio C Hamano --- builtin-add.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index b3f920676a..aa2f0f32af 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -8,6 +8,7 @@ #include "cache.h" #include "builtin.h" #include "dir.h" +#include "exec_cmd.h" #include "cache-tree.h" static const char builtin_add_usage[] = @@ -89,6 +90,20 @@ int cmd_add(int argc, const char **argv, const char *prefix) int verbose = 0, show_only = 0; const char **pathspec; struct dir_struct dir; + int add_interactive = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp("--interactive", argv[i])) + add_interactive++; + } + if (add_interactive) { + const char *args[] = { "add--interactive", NULL }; + + if (add_interactive != 1 || argc != 2) + die("add --interactive does not take any parameters"); + execv_git_cmd(args); + exit(1); + } git_config(git_default_config); -- cgit v1.2.1 From 85023577a8f4b540aa64aa37f6f44578c0c305a3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 19 Dec 2006 14:34:12 -0800 Subject: simplify inclusion of system header files. This is a mechanical clean-up of the way *.c files include system header files. (1) sources under compat/, platform sha-1 implementations, and xdelta code are exempt from the following rules; (2) the first #include must be "git-compat-util.h" or one of our own header file that includes it first (e.g. config.h, builtin.h, pkt-line.h); (3) system headers that are included in "git-compat-util.h" need not be included in individual C source files. (4) "git-compat-util.h" does not have to include subsystem specific header files (e.g. expat.h). Signed-off-by: Junio C Hamano --- builtin-add.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index b3f920676a..c8a114fefb 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -3,8 +3,6 @@ * * Copyright (C) 2006 Linus Torvalds */ -#include - #include "cache.h" #include "builtin.h" #include "dir.h" -- cgit v1.2.1 From 93b0d86aafdd3e00de2c3c6ebb3c109b4571e1c4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Dec 2006 13:06:46 -0800 Subject: git-add: error out when given no arguments. Signed-off-by: Junio C Hamano --- builtin-add.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index c8a114fefb..f306f82b16 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -111,6 +111,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) } usage(builtin_add_usage); } + if (argc <= i) { + fprintf(stderr, "Nothing specified, nothing added.\n"); + fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); + return 0; + } pathspec = get_pathspec(prefix, argv + i); fill_directory(&dir, pathspec); -- cgit v1.2.1 From 6a5ad23de6aa95b35d90d631062e5a353e59d3f2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 Dec 2006 01:30:55 -0800 Subject: git-add --interactive: add documentation Signed-off-by: Junio C Hamano --- builtin-add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index aa2f0f32af..9443bae535 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -12,7 +12,7 @@ #include "cache-tree.h" static const char builtin_add_usage[] = -"git-add [-n] [-v] ..."; +"git-add [-n] [-v] [--interactive] [--] ..."; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { -- cgit v1.2.1 From e23ca9e1f95a756bfe598568be9d03059db1dad2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 Dec 2006 03:13:45 -0800 Subject: git-add: add ignored files when asked explicitly. One thing many people found confusing about git-add was that a file whose name matches an ignored pattern could not be added to the index. With this, such a file can be added by explicitly spelling its name to git-add. Fileglobs and recursive behaviour do not add ignored files to the index. That is, if a pattern '*.o' is in .gitignore, and two files foo.o, bar/baz.o are in the working tree: $ git add foo.o $ git add '*.o' $ git add bar Only the first form adds foo.o to the index. Signed-off-by: Junio C Hamano --- builtin-add.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index 17641b433d..822075ac22 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -26,7 +26,14 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) { + int how = match_pathspec(pathspec, entry->name, entry->len, + prefix, seen); + /* + * ignored entries can be added with exact match, + * but not with glob nor recursive. + */ + if (!how || + (entry->ignored_entry && how != MATCHED_EXACTLY)) { free(entry); continue; } @@ -55,6 +62,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec) /* Set up the default git porcelain excludes */ memset(dir, 0, sizeof(*dir)); + if (pathspec) + dir->show_both = 1; dir->exclude_per_dir = ".gitignore"; path = git_path("info/exclude"); if (!access(path, R_OK)) -- cgit v1.2.1 From 1e423f56558e74a3a94c653108f64f0305199b9a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 Dec 2006 10:38:35 -0800 Subject: git-add: warn when adding an ignored file with an explicit request. We allow otherwise ignored paths to be added to the index by spelling its path out on the command line, but we would warn the user about them when we do so. Signed-off-by: Junio C Hamano --- builtin-add.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index 822075ac22..c54c694532 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -37,6 +37,9 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p free(entry); continue; } + if (entry->ignored_entry) + fprintf(stderr, "warning: '%s' is an ignored path.\n", + entry->name); *dst++ = entry; } dir->nr = dst - dir->entries; -- cgit v1.2.1 From 6a1ad32519898a8fa4400e081c2d61fd3af42e2c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 Dec 2006 17:46:38 -0800 Subject: git-add -f: allow adding otherwise ignored files. Instead of just warning, refuse to add otherwise ignored files by default, and allow it with an -f option. Signed-off-by: Junio C Hamano --- builtin-add.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'builtin-add.c') diff --git a/builtin-add.c b/builtin-add.c index c54c694532..8ed4a6a9f3 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -10,7 +10,7 @@ #include "cache-tree.h" static const char builtin_add_usage[] = -"git-add [-n] [-v] [--interactive] [--] ..."; +"git-add [-n] [-v] [-f] [--interactive] [--] ..."; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { @@ -37,9 +37,6 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p free(entry); continue; } - if (entry->ignored_entry) - fprintf(stderr, "warning: '%s' is an ignored path.\n", - entry->name); *dst++ = entry; } dir->nr = dst - dir->entries; @@ -94,10 +91,13 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec) static struct lock_file lock_file; +static const char ignore_warning[] = +"The following paths are ignored by one of your .gitignore files:\n"; + int cmd_add(int argc, const char **argv, const char *prefix) { int i, newfd; - int verbose = 0, show_only = 0; + int verbose = 0, show_only = 0, ignored_too = 0; const char **pathspec; struct dir_struct dir; int add_interactive = 0; @@ -132,6 +132,10 @@ int cmd_add(int argc, const char **argv, const char *prefix) show_only = 1; continue; } + if (!strcmp(arg, "-f")) { + ignored_too = 1; + continue; + } if (!strcmp(arg, "-v")) { verbose = 1; continue; @@ -150,6 +154,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (show_only) { const char *sep = "", *eof = ""; for (i = 0; i < dir.nr; i++) { + if (!ignored_too && dir.entries[i]->ignored_entry) + continue; printf("%s%s", sep, dir.entries[i]->name); sep = " "; eof = "\n"; @@ -161,6 +167,24 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); + if (!ignored_too) { + int has_ignored = -1; + for (i = 0; has_ignored < 0 && i < dir.nr; i++) + if (dir.entries[i]->ignored_entry) + has_ignored = i; + if (0 <= has_ignored) { + fprintf(stderr, ignore_warning); + for (i = has_ignored; i < dir.nr; i++) { + if (!dir.entries[i]->ignored_entry) + continue; + fprintf(stderr, "%s\n", dir.entries[i]->name); + } + fprintf(stderr, + "Use -f if you really want to add them.\n"); + exit(1); + } + } + for (i = 0; i < dir.nr; i++) add_file_to_index(dir.entries[i]->name, verbose); -- cgit v1.2.1