2006-05-18 00:33:32 +08:00
|
|
|
/*
|
|
|
|
* "git add" builtin command
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Linus Torvalds
|
|
|
|
*/
|
|
|
|
#include "cache.h"
|
|
|
|
#include "builtin.h"
|
|
|
|
#include "dir.h"
|
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 <junkio@cox.net>
2006-12-11 12:55:50 +08:00
|
|
|
#include "exec_cmd.h"
|
2006-05-20 16:28:49 +08:00
|
|
|
#include "cache-tree.h"
|
2006-05-18 00:33:32 +08:00
|
|
|
|
|
|
|
static const char builtin_add_usage[] =
|
2007-01-18 02:52:36 +08:00
|
|
|
"git-add [-n] [-v] [-f] [--interactive | -i] [--] <filepattern>...";
|
2006-05-18 00:33:32 +08:00
|
|
|
|
|
|
|
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
|
|
|
{
|
2006-05-18 04:23:19 +08:00
|
|
|
char *seen;
|
|
|
|
int i, specs;
|
2006-05-18 00:33:32 +08:00
|
|
|
struct dir_entry **src, **dst;
|
|
|
|
|
2006-05-18 04:23:19 +08:00
|
|
|
for (specs = 0; pathspec[specs]; specs++)
|
|
|
|
/* nothing */;
|
2006-07-25 15:30:18 +08:00
|
|
|
seen = xcalloc(specs, 1);
|
2006-05-18 04:23:19 +08:00
|
|
|
|
2006-05-18 00:33:32 +08:00
|
|
|
src = dst = dir->entries;
|
|
|
|
i = dir->nr;
|
|
|
|
while (--i >= 0) {
|
|
|
|
struct dir_entry *entry = *src++;
|
2006-12-30 03:01:31 +08:00
|
|
|
if (match_pathspec(pathspec, entry->name, entry->len,
|
|
|
|
prefix, seen))
|
|
|
|
*dst++ = entry;
|
2006-05-18 00:33:32 +08:00
|
|
|
}
|
|
|
|
dir->nr = dst - dir->entries;
|
2006-05-18 04:23:19 +08:00
|
|
|
|
|
|
|
for (i = 0; i < specs; i++) {
|
|
|
|
struct stat st;
|
|
|
|
const char *match;
|
|
|
|
if (seen[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
match = pathspec[i];
|
2006-12-30 03:01:31 +08:00
|
|
|
if (!match[0])
|
2006-05-18 04:23:19 +08:00
|
|
|
continue;
|
2006-12-30 03:01:31 +08:00
|
|
|
|
|
|
|
/* Existing file? We must have ignored it */
|
|
|
|
if (!lstat(match, &st)) {
|
|
|
|
struct dir_entry *ent;
|
|
|
|
|
|
|
|
ent = dir_add_name(dir, match, strlen(match));
|
|
|
|
ent->ignored = 1;
|
|
|
|
if (S_ISDIR(st.st_mode))
|
|
|
|
ent->ignored_dir = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2006-05-18 04:23:19 +08:00
|
|
|
die("pathspec '%s' did not match any files", match);
|
|
|
|
}
|
2006-05-18 00:33:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fill_directory(struct dir_struct *dir, const char **pathspec)
|
|
|
|
{
|
|
|
|
const char *path, *base;
|
|
|
|
int baselen;
|
|
|
|
|
|
|
|
/* Set up the default git porcelain excludes */
|
|
|
|
memset(dir, 0, sizeof(*dir));
|
|
|
|
dir->exclude_per_dir = ".gitignore";
|
|
|
|
path = git_path("info/exclude");
|
|
|
|
if (!access(path, R_OK))
|
|
|
|
add_excludes_from_file(dir, path);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate common prefix for the pathspec, and
|
|
|
|
* use that to optimize the directory walk
|
|
|
|
*/
|
|
|
|
baselen = common_prefix(pathspec);
|
|
|
|
path = ".";
|
|
|
|
base = "";
|
|
|
|
if (baselen) {
|
|
|
|
char *common = xmalloc(baselen + 1);
|
|
|
|
memcpy(common, *pathspec, baselen);
|
|
|
|
common[baselen] = 0;
|
|
|
|
path = base = common;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the directory and prune it */
|
|
|
|
read_directory(dir, path, base, baselen);
|
|
|
|
if (pathspec)
|
|
|
|
prune_directory(dir, pathspec, baselen);
|
|
|
|
}
|
|
|
|
|
2006-06-07 03:51:49 +08:00
|
|
|
static struct lock_file lock_file;
|
2006-05-18 00:33:32 +08:00
|
|
|
|
2006-12-26 09:46:38 +08:00
|
|
|
static const char ignore_warning[] =
|
|
|
|
"The following paths are ignored by one of your .gitignore files:\n";
|
|
|
|
|
2006-07-29 13:44:25 +08:00
|
|
|
int cmd_add(int argc, const char **argv, const char *prefix)
|
2006-05-18 00:33:32 +08:00
|
|
|
{
|
|
|
|
int i, newfd;
|
2006-12-26 09:46:38 +08:00
|
|
|
int verbose = 0, show_only = 0, ignored_too = 0;
|
2006-05-18 00:33:32 +08:00
|
|
|
const char **pathspec;
|
|
|
|
struct dir_struct dir;
|
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 <junkio@cox.net>
2006-12-11 12:55:50 +08:00
|
|
|
int add_interactive = 0;
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
2007-01-18 02:52:36 +08:00
|
|
|
if (!strcmp("--interactive", argv[i]) ||
|
|
|
|
!strcmp("-i", argv[i]))
|
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 <junkio@cox.net>
2006-12-11 12:55:50 +08:00
|
|
|
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);
|
|
|
|
}
|
2006-05-18 00:33:32 +08:00
|
|
|
|
|
|
|
git_config(git_default_config);
|
|
|
|
|
2006-08-12 16:03:47 +08:00
|
|
|
newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
|
2006-05-18 00:33:32 +08:00
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
const char *arg = argv[i];
|
|
|
|
|
|
|
|
if (arg[0] != '-')
|
|
|
|
break;
|
|
|
|
if (!strcmp(arg, "--")) {
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "-n")) {
|
|
|
|
show_only = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2006-12-26 09:46:38 +08:00
|
|
|
if (!strcmp(arg, "-f")) {
|
|
|
|
ignored_too = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2006-05-18 00:33:32 +08:00
|
|
|
if (!strcmp(arg, "-v")) {
|
|
|
|
verbose = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2006-08-03 23:48:41 +08:00
|
|
|
usage(builtin_add_usage);
|
2006-05-18 00:33:32 +08:00
|
|
|
}
|
2006-12-21 05:06:46 +08:00
|
|
|
if (argc <= i) {
|
|
|
|
fprintf(stderr, "Nothing specified, nothing added.\n");
|
|
|
|
fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2006-05-18 00:33:32 +08:00
|
|
|
pathspec = get_pathspec(prefix, argv + i);
|
|
|
|
|
|
|
|
fill_directory(&dir, pathspec);
|
|
|
|
|
|
|
|
if (show_only) {
|
|
|
|
const char *sep = "", *eof = "";
|
|
|
|
for (i = 0; i < dir.nr; i++) {
|
2006-12-30 03:01:31 +08:00
|
|
|
if (!ignored_too && dir.entries[i]->ignored)
|
2006-12-26 09:46:38 +08:00
|
|
|
continue;
|
2006-05-18 00:33:32 +08:00
|
|
|
printf("%s%s", sep, dir.entries[i]->name);
|
|
|
|
sep = " ";
|
|
|
|
eof = "\n";
|
|
|
|
}
|
|
|
|
fputs(eof, stdout);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-12-05 00:13:39 +08:00
|
|
|
if (read_cache() < 0)
|
|
|
|
die("index file corrupt");
|
|
|
|
|
2006-12-26 09:46:38 +08:00
|
|
|
if (!ignored_too) {
|
2006-12-30 03:01:31 +08:00
|
|
|
int has_ignored = 0;
|
|
|
|
for (i = 0; i < dir.nr; i++)
|
|
|
|
if (dir.entries[i]->ignored)
|
|
|
|
has_ignored = 1;
|
|
|
|
if (has_ignored) {
|
2006-12-26 09:46:38 +08:00
|
|
|
fprintf(stderr, ignore_warning);
|
2006-12-30 03:01:31 +08:00
|
|
|
for (i = 0; i < dir.nr; i++) {
|
|
|
|
if (!dir.entries[i]->ignored)
|
2006-12-26 09:46:38 +08:00
|
|
|
continue;
|
2006-12-30 03:01:31 +08:00
|
|
|
fprintf(stderr, "%s", dir.entries[i]->name);
|
|
|
|
if (dir.entries[i]->ignored_dir)
|
|
|
|
fprintf(stderr, " (directory)");
|
|
|
|
fputc('\n', stderr);
|
2006-12-26 09:46:38 +08:00
|
|
|
}
|
|
|
|
fprintf(stderr,
|
|
|
|
"Use -f if you really want to add them.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-18 00:33:32 +08:00
|
|
|
for (i = 0; i < dir.nr; i++)
|
|
|
|
add_file_to_index(dir.entries[i]->name, verbose);
|
|
|
|
|
|
|
|
if (active_cache_changed) {
|
|
|
|
if (write_cache(newfd, active_cache, active_nr) ||
|
2006-07-08 16:56:28 +08:00
|
|
|
close(newfd) || commit_lock_file(&lock_file))
|
2006-05-18 00:33:32 +08:00
|
|
|
die("Unable to write new index file");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|