2006-05-17 20:33:32 +04: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 07:55:50 +03:00
|
|
|
#include "exec_cmd.h"
|
2006-05-20 12:28:49 +04:00
|
|
|
#include "cache-tree.h"
|
2007-04-20 12:39:39 +04:00
|
|
|
#include "diff.h"
|
|
|
|
#include "diffcore.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "revision.h"
|
2007-09-18 04:06:44 +04:00
|
|
|
#include "run-command.h"
|
2007-10-04 01:45:02 +04:00
|
|
|
#include "parse-options.h"
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2007-10-04 01:45:02 +04:00
|
|
|
static const char * const builtin_add_usage[] = {
|
2008-07-13 17:36:15 +04:00
|
|
|
"git add [options] [--] <filepattern>...",
|
2007-10-04 01:45:02 +04:00
|
|
|
NULL
|
|
|
|
};
|
2007-11-25 16:15:42 +03:00
|
|
|
static int patch_interactive = 0, add_interactive = 0;
|
2007-05-12 10:42:00 +04:00
|
|
|
static int take_worktree_changes;
|
2007-02-28 06:31:10 +03:00
|
|
|
|
2006-05-17 20:33:32 +04:00
|
|
|
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
|
|
|
{
|
2006-05-18 00:23:19 +04:00
|
|
|
char *seen;
|
|
|
|
int i, specs;
|
2006-05-17 20:33:32 +04:00
|
|
|
struct dir_entry **src, **dst;
|
|
|
|
|
2006-05-18 00:23:19 +04:00
|
|
|
for (specs = 0; pathspec[specs]; specs++)
|
|
|
|
/* nothing */;
|
2006-07-25 11:30:18 +04:00
|
|
|
seen = xcalloc(specs, 1);
|
2006-05-18 00:23:19 +04:00
|
|
|
|
2006-05-17 20:33:32 +04:00
|
|
|
src = dst = dir->entries;
|
|
|
|
i = dir->nr;
|
|
|
|
while (--i >= 0) {
|
|
|
|
struct dir_entry *entry = *src++;
|
2006-12-29 22:01:31 +03:00
|
|
|
if (match_pathspec(pathspec, entry->name, entry->len,
|
|
|
|
prefix, seen))
|
|
|
|
*dst++ = entry;
|
2006-05-17 20:33:32 +04:00
|
|
|
}
|
|
|
|
dir->nr = dst - dir->entries;
|
2006-05-18 00:23:19 +04:00
|
|
|
|
|
|
|
for (i = 0; i < specs; i++) {
|
builtin-add: simplify (and increase accuracy of) exclude handling
Previously, the code would always set up the excludes, and then manually
pick through the pathspec we were given, assuming that non-added but
existing paths were just ignored. This was mostly correct, but would
erroneously mark a totally empty directory as 'ignored'.
Instead, we now use the collect_ignored option of dir_struct, which
unambiguously tells us whether a path was ignored. This simplifies the
code, and means empty directories are now just not mentioned at all.
Furthermore, we now conditionally ask dir_struct to respect excludes,
depending on whether the '-f' flag has been set. This means we don't have
to pick through the result, checking for an 'ignored' flag; ignored entries
were either added or not in the first place.
We can safely get rid of the special 'ignored' flags to dir_entry, which
were not used anywhere else.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-06-13 01:42:14 +04:00
|
|
|
if (!seen[i] && !file_exists(pathspec[i]))
|
|
|
|
die("pathspec '%s' did not match any files",
|
|
|
|
pathspec[i]);
|
2006-05-18 00:23:19 +04:00
|
|
|
}
|
2007-10-29 10:00:33 +03:00
|
|
|
free(seen);
|
2006-05-17 20:33:32 +04:00
|
|
|
}
|
|
|
|
|
builtin-add: simplify (and increase accuracy of) exclude handling
Previously, the code would always set up the excludes, and then manually
pick through the pathspec we were given, assuming that non-added but
existing paths were just ignored. This was mostly correct, but would
erroneously mark a totally empty directory as 'ignored'.
Instead, we now use the collect_ignored option of dir_struct, which
unambiguously tells us whether a path was ignored. This simplifies the
code, and means empty directories are now just not mentioned at all.
Furthermore, we now conditionally ask dir_struct to respect excludes,
depending on whether the '-f' flag has been set. This means we don't have
to pick through the result, checking for an 'ignored' flag; ignored entries
were either added or not in the first place.
We can safely get rid of the special 'ignored' flags to dir_entry, which
were not used anywhere else.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-06-13 01:42:14 +04:00
|
|
|
static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
|
|
|
int ignored_too)
|
2006-05-17 20:33:32 +04:00
|
|
|
{
|
|
|
|
const char *path, *base;
|
|
|
|
int baselen;
|
|
|
|
|
|
|
|
/* Set up the default git porcelain excludes */
|
|
|
|
memset(dir, 0, sizeof(*dir));
|
builtin-add: simplify (and increase accuracy of) exclude handling
Previously, the code would always set up the excludes, and then manually
pick through the pathspec we were given, assuming that non-added but
existing paths were just ignored. This was mostly correct, but would
erroneously mark a totally empty directory as 'ignored'.
Instead, we now use the collect_ignored option of dir_struct, which
unambiguously tells us whether a path was ignored. This simplifies the
code, and means empty directories are now just not mentioned at all.
Furthermore, we now conditionally ask dir_struct to respect excludes,
depending on whether the '-f' flag has been set. This means we don't have
to pick through the result, checking for an 'ignored' flag; ignored entries
were either added or not in the first place.
We can safely get rid of the special 'ignored' flags to dir_entry, which
were not used anywhere else.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-06-13 01:42:14 +04:00
|
|
|
if (!ignored_too) {
|
|
|
|
dir->collect_ignored = 1;
|
core.excludesfile clean-up
There are inconsistencies in the way commands currently handle
the core.excludesfile configuration variable. The problem is
the variable is too new to be noticed by anything other than
git-add and git-status.
* git-ls-files does not notice any of the "ignore" files by
default, as it predates the standardized set of ignore files.
The calling scripts established the convention to use
.git/info/exclude, .gitignore, and later core.excludesfile.
* git-add and git-status know about it because they call
add_excludes_from_file() directly with their own notion of
which standard set of ignore files to use. This is just a
stupid duplication of code that need to be updated every time
the definition of the standard set of ignore files is
changed.
* git-read-tree takes --exclude-per-directory=<gitignore>,
not because the flexibility was needed. Again, this was
because the option predates the standardization of the ignore
files.
* git-merge-recursive uses hardcoded per-directory .gitignore
and nothing else. git-clean (scripted version) does not
honor core.* because its call to underlying ls-files does not
know about it. git-clean in C (parked in 'pu') doesn't either.
We probably could change git-ls-files to use the standard set
when no excludes are specified on the command line and ignore
processing was asked, or something like that, but that will be a
change in semantics and might break people's scripts in a subtle
way. I am somewhat reluctant to make such a change.
On the other hand, I think it makes perfect sense to fix
git-read-tree, git-merge-recursive and git-clean to follow the
same rule as other commands. I do not think of a valid use case
to give an exclude-per-directory that is nonstandard to
read-tree command, outside a "negative" test in the t1004 test
script.
This patch is the first step to untangle this mess.
The next step would be to teach read-tree, merge-recursive and
clean (in C) to use setup_standard_excludes().
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-14 11:05:00 +03:00
|
|
|
setup_standard_excludes(dir);
|
builtin-add: simplify (and increase accuracy of) exclude handling
Previously, the code would always set up the excludes, and then manually
pick through the pathspec we were given, assuming that non-added but
existing paths were just ignored. This was mostly correct, but would
erroneously mark a totally empty directory as 'ignored'.
Instead, we now use the collect_ignored option of dir_struct, which
unambiguously tells us whether a path was ignored. This simplifies the
code, and means empty directories are now just not mentioned at all.
Furthermore, we now conditionally ask dir_struct to respect excludes,
depending on whether the '-f' flag has been set. This means we don't have
to pick through the result, checking for an 'ignored' flag; ignored entries
were either added or not in the first place.
We can safely get rid of the special 'ignored' flags to dir_entry, which
were not used anywhere else.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-06-13 01:42:14 +04:00
|
|
|
}
|
2006-05-17 20:33:32 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate common prefix for the pathspec, and
|
|
|
|
* use that to optimize the directory walk
|
|
|
|
*/
|
|
|
|
baselen = common_prefix(pathspec);
|
|
|
|
path = ".";
|
|
|
|
base = "";
|
2007-09-16 02:32:36 +04:00
|
|
|
if (baselen)
|
|
|
|
path = base = xmemdupz(*pathspec, baselen);
|
2006-05-17 20:33:32 +04:00
|
|
|
|
|
|
|
/* Read the directory and prune it */
|
Optimize directory listing with pathspec limiter.
The way things are set up, you can now pass a "pathspec" to the
"read_directory()" function. If you pass NULL, it acts exactly
like it used to do (read everything). If you pass a non-NULL
pointer, it will simplify it into a "these are the prefixes
without any special characters", and stop any readdir() early if
the path in question doesn't match any of the prefixes.
NOTE! This does *not* obviate the need for the caller to do the *exact*
pathspec match later. It's a first-level filter on "read_directory()", but
it does not do the full pathspec thing. Maybe it should. But in the
meantime, builtin-add.c really does need to do first
read_directory(dir, .., pathspec);
if (pathspec)
prune_directory(dir, pathspec, baselen);
ie the "prune_directory()" part will do the *exact* pathspec pruning,
while the "read_directory()" will use the pathspec just to do some quick
high-level pruning of the directories it will recurse into.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-03-31 07:39:30 +04:00
|
|
|
read_directory(dir, path, base, baselen, pathspec);
|
2006-05-17 20:33:32 +04:00
|
|
|
if (pathspec)
|
|
|
|
prune_directory(dir, pathspec, baselen);
|
|
|
|
}
|
|
|
|
|
2008-05-12 21:58:10 +04:00
|
|
|
struct update_callback_data
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
int add_errors;
|
|
|
|
};
|
|
|
|
|
2007-04-20 12:39:39 +04:00
|
|
|
static void update_callback(struct diff_queue_struct *q,
|
|
|
|
struct diff_options *opt, void *cbdata)
|
|
|
|
{
|
2008-05-12 21:58:10 +04:00
|
|
|
int i;
|
|
|
|
struct update_callback_data *data = cbdata;
|
2007-04-20 12:39:39 +04:00
|
|
|
|
|
|
|
for (i = 0; i < q->nr; i++) {
|
|
|
|
struct diff_filepair *p = q->queue[i];
|
|
|
|
const char *path = p->one->path;
|
|
|
|
switch (p->status) {
|
|
|
|
default:
|
2007-09-14 12:29:04 +04:00
|
|
|
die("unexpected diff status %c", p->status);
|
2007-04-20 12:39:39 +04:00
|
|
|
case DIFF_STATUS_UNMERGED:
|
|
|
|
case DIFF_STATUS_MODIFIED:
|
2007-09-14 11:45:29 +04:00
|
|
|
case DIFF_STATUS_TYPE_CHANGED:
|
2008-05-26 01:03:50 +04:00
|
|
|
if (add_file_to_cache(path, data->flags)) {
|
|
|
|
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
|
2008-05-12 21:58:10 +04:00
|
|
|
die("updating files failed");
|
|
|
|
data->add_errors++;
|
|
|
|
}
|
2007-04-20 12:39:39 +04:00
|
|
|
break;
|
|
|
|
case DIFF_STATUS_DELETED:
|
2008-05-26 01:03:50 +04:00
|
|
|
if (!(data->flags & ADD_CACHE_PRETEND))
|
2008-05-21 23:04:34 +04:00
|
|
|
remove_file_from_cache(path);
|
2008-05-26 01:03:50 +04:00
|
|
|
if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
|
2007-04-20 12:39:39 +04:00
|
|
|
printf("remove '%s'\n", path);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-12 21:58:10 +04:00
|
|
|
int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
2007-04-20 12:39:39 +04:00
|
|
|
{
|
2008-05-12 21:58:10 +04:00
|
|
|
struct update_callback_data data;
|
2007-04-20 12:39:39 +04:00
|
|
|
struct rev_info rev;
|
2007-08-15 21:01:43 +04:00
|
|
|
init_revisions(&rev, prefix);
|
2007-04-20 12:39:39 +04:00
|
|
|
setup_revisions(0, NULL, &rev, NULL);
|
2007-11-18 12:12:04 +03:00
|
|
|
rev.prune_data = pathspec;
|
2007-04-20 12:39:39 +04:00
|
|
|
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
|
|
|
rev.diffopt.format_callback = update_callback;
|
2008-05-12 21:58:10 +04:00
|
|
|
data.flags = flags;
|
|
|
|
data.add_errors = 0;
|
|
|
|
rev.diffopt.format_callback_data = &data;
|
git-add: make the entry stat-clean after re-adding the same contents
Earlier in commit 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe
(add_file_to_index: skip rehashing if the cached stat already
matches), add_file_to_index() were taught not to re-add the path
if it already matches the index.
The change meant well, but was not executed quite right. It
used ie_modified() to see if the file on the work tree is really
different from the index, and skipped adding the contents if the
function says "not modified".
This was wrong. There are three possible comparison results
between the index and the file in the work tree:
- with lstat(2) we _know_ they are different. E.g. if the
length or the owner in the cached stat information is
different from the length we just obtained from lstat(2), we
can tell the file is modified without looking at the actual
contents.
- with lstat(2) we _know_ they are the same. The same length,
the same owner, the same everything (but this has a twist, as
described below).
- we cannot tell from lstat(2) information alone and need to go
to the filesystem to actually compare.
The last case arises from what we call 'racy git' situation,
that can be caused with this sequence:
$ echo hello >file
$ git add file
$ echo aeiou >file ;# the same length
If the second "echo" is done within the same filesystem
timestamp granularity as the first "echo", then the timestamp
recorded by "git add" and the timestamp we get from lstat(2)
will be the same, and we can mistakenly say the file is not
modified. The path is called 'racily clean'. We need to
reliably detect racily clean paths are in fact modified.
To solve this problem, when we write out the index, we mark the
index entry that has the same timestamp as the index file itself
(that is the time from the point of view of the filesystem) to
tell any later code that does the lstat(2) comparison not to
trust the cached stat info, and ie_modified() then actually goes
to the filesystem to compare the contents for such a path.
That's all good, but it should not be used for this "git add"
optimization, as the goal of "git add" is to actually update the
path in the index and make it stat-clean. With the false
optimization, we did _not_ cause any data loss (after all, what
we failed to do was only to update the cached stat information),
but it made the following sequence leave the file stat dirty:
$ echo hello >file
$ git add file
$ echo hello >file ;# the same contents
$ git add file
The solution is not to use ie_modified() which goes to the
filesystem to see if it is really clean, but instead use
ie_match_stat() with "assume racily clean paths are dirty"
option, to force re-adding of such a path.
There was another problem with "git add -u". The codepath
shares the same issue when adding the paths that are found to be
modified, but in addition, it asked "git diff-files" machinery
run_diff_files() function (which is "git diff-files") to list
the paths that are modified. But "git diff-files" machinery
uses the same ie_modified() call so that it does not report
racily clean _and_ actually clean paths as modified, which is
not what we want.
The patch allows the callers of run_diff_files() to pass the
same "assume racily clean paths are dirty" option, and makes
"git-add -u" codepath to use that option, to discover and re-add
racily clean _and_ actually clean paths.
We could further optimize on top of this patch to differentiate
the case where the path really needs re-adding (i.e. the content
of the racily clean entry was indeed different) and the case
where only the cached stat information needs to be refreshed
(i.e. the racily clean entry was actually clean), but I do not
think it is worth it.
This patch applies to maint and all the way up.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-10 05:22:52 +03:00
|
|
|
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
|
2008-05-12 21:58:10 +04:00
|
|
|
return !!data.add_errors;
|
2007-04-20 12:39:39 +04:00
|
|
|
}
|
|
|
|
|
2007-08-12 01:59:01 +04:00
|
|
|
static void refresh(int verbose, const char **pathspec)
|
|
|
|
{
|
|
|
|
char *seen;
|
|
|
|
int i, specs;
|
|
|
|
|
|
|
|
for (specs = 0; pathspec[specs]; specs++)
|
|
|
|
/* nothing */;
|
|
|
|
seen = xcalloc(specs, 1);
|
2008-07-20 11:21:38 +04:00
|
|
|
refresh_index(&the_index, verbose ? REFRESH_SAY_CHANGED : REFRESH_QUIET,
|
|
|
|
pathspec, seen);
|
2007-08-12 01:59:01 +04:00
|
|
|
for (i = 0; i < specs; i++) {
|
|
|
|
if (!seen[i])
|
|
|
|
die("pathspec '%s' did not match any files", pathspec[i]);
|
|
|
|
}
|
2007-10-29 10:00:33 +03:00
|
|
|
free(seen);
|
2007-08-12 01:59:01 +04:00
|
|
|
}
|
|
|
|
|
2007-11-25 21:10:10 +03:00
|
|
|
static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
const char **pathspec = get_pathspec(prefix, argv);
|
|
|
|
|
|
|
|
return pathspec;
|
|
|
|
}
|
|
|
|
|
|
|
|
int interactive_add(int argc, const char **argv, const char *prefix)
|
2007-09-18 04:06:44 +04:00
|
|
|
{
|
2007-11-25 16:15:42 +03:00
|
|
|
int status, ac;
|
2007-11-25 21:10:10 +03:00
|
|
|
const char **args;
|
|
|
|
const char **pathspec = NULL;
|
|
|
|
|
|
|
|
if (argc) {
|
|
|
|
pathspec = validate_pathspec(argc, argv, prefix);
|
|
|
|
if (!pathspec)
|
|
|
|
return -1;
|
|
|
|
}
|
2007-11-25 21:07:55 +03:00
|
|
|
|
2007-11-25 16:15:42 +03:00
|
|
|
args = xcalloc(sizeof(const char *), (argc + 4));
|
|
|
|
ac = 0;
|
|
|
|
args[ac++] = "add--interactive";
|
|
|
|
if (patch_interactive)
|
|
|
|
args[ac++] = "--patch";
|
|
|
|
args[ac++] = "--";
|
|
|
|
if (argc) {
|
|
|
|
memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
|
|
|
|
ac += argc;
|
|
|
|
}
|
|
|
|
args[ac] = NULL;
|
2007-11-22 03:02:52 +03:00
|
|
|
|
|
|
|
status = run_command_v_opt(args, RUN_GIT_CMD);
|
|
|
|
free(args);
|
|
|
|
return status;
|
2007-09-18 04:06:44 +04:00
|
|
|
}
|
|
|
|
|
2006-06-06 23:51:49 +04:00
|
|
|
static struct lock_file lock_file;
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2007-08-29 02:41:23 +04:00
|
|
|
static const char ignore_error[] =
|
2006-12-26 04:46:38 +03:00
|
|
|
"The following paths are ignored by one of your .gitignore files:\n";
|
|
|
|
|
2007-10-04 01:45:02 +04:00
|
|
|
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
|
git-add --all: add all files
People sometimes find that "git add -u && git add ." are 13 keystrokes too
many. This reduces it by nine.
The support of this has been very low priority for me personally, because
I almost never do "git add ." in a directory with already tracked files,
and in a new directory, there is no point saying "git add -u".
However, for two types of people (that are very different from me), this
mode of operation may make sense and there is no reason to leave it
unsupported. That is:
(1) If you are extremely well disciplined and keep perfect .gitignore, it
always is safe to say "git add ."; or
(2) If you are extremely undisciplined and do not even know what files
you created, and you do not very much care what goes in your history,
it does not matter if "git add ." included everything.
So there it is, although I suspect I will not use it myself, ever.
It will be too much of a change that is against the expectation of the
existing users to allow "git commit -a" to include untracked files, and
it would be inconsistent if we named this new option "-a", so the short
option is "-A". We _might_ want to later add "git commit -A" but that is
a separate topic.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-20 06:51:11 +04:00
|
|
|
static int ignore_add_errors, addremove;
|
2007-10-04 01:45:02 +04:00
|
|
|
|
|
|
|
static struct option builtin_add_options[] = {
|
|
|
|
OPT__DRY_RUN(&show_only),
|
|
|
|
OPT__VERBOSE(&verbose),
|
|
|
|
OPT_GROUP(""),
|
|
|
|
OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
|
2007-11-25 16:15:42 +03:00
|
|
|
OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
|
2008-06-14 13:48:00 +04:00
|
|
|
OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
|
|
|
|
OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
|
git-add --all: add all files
People sometimes find that "git add -u && git add ." are 13 keystrokes too
many. This reduces it by nine.
The support of this has been very low priority for me personally, because
I almost never do "git add ." in a directory with already tracked files,
and in a new directory, there is no point saying "git add -u".
However, for two types of people (that are very different from me), this
mode of operation may make sense and there is no reason to leave it
unsupported. That is:
(1) If you are extremely well disciplined and keep perfect .gitignore, it
always is safe to say "git add ."; or
(2) If you are extremely undisciplined and do not even know what files
you created, and you do not very much care what goes in your history,
it does not matter if "git add ." included everything.
So there it is, although I suspect I will not use it myself, ever.
It will be too much of a change that is against the expectation of the
existing users to allow "git commit -a" to include untracked files, and
it would be inconsistent if we named this new option "-a", so the short
option is "-A". We _might_ want to later add "git commit -A" but that is
a separate topic.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-20 06:51:11 +04:00
|
|
|
OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
|
2007-10-04 01:45:02 +04:00
|
|
|
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
|
2008-05-12 21:58:29 +04:00
|
|
|
OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
|
2007-10-04 01:45:02 +04:00
|
|
|
OPT_END(),
|
|
|
|
};
|
|
|
|
|
2008-05-26 01:25:02 +04:00
|
|
|
static int add_config(const char *var, const char *value, void *cb)
|
2008-05-12 21:59:23 +04:00
|
|
|
{
|
|
|
|
if (!strcasecmp(var, "add.ignore-errors")) {
|
|
|
|
ignore_add_errors = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-05-26 01:25:02 +04:00
|
|
|
return git_default_config(var, value, cb);
|
2008-05-12 21:59:23 +04:00
|
|
|
}
|
|
|
|
|
2008-07-20 06:22:25 +04:00
|
|
|
static int add_files(struct dir_struct *dir, int flags)
|
|
|
|
{
|
|
|
|
int i, exit_status = 0;
|
|
|
|
|
|
|
|
if (dir->ignored_nr) {
|
|
|
|
fprintf(stderr, ignore_error);
|
|
|
|
for (i = 0; i < dir->ignored_nr; i++)
|
|
|
|
fprintf(stderr, "%s\n", dir->ignored[i]->name);
|
|
|
|
fprintf(stderr, "Use -f if you really want to add them.\n");
|
|
|
|
die("no files added");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < dir->nr; i++)
|
|
|
|
if (add_file_to_cache(dir->entries[i]->name, flags)) {
|
|
|
|
if (!ignore_add_errors)
|
|
|
|
die("adding files failed");
|
|
|
|
exit_status = 1;
|
|
|
|
}
|
|
|
|
return exit_status;
|
|
|
|
}
|
|
|
|
|
2006-07-29 09:44:25 +04:00
|
|
|
int cmd_add(int argc, const char **argv, const char *prefix)
|
2006-05-17 20:33:32 +04:00
|
|
|
{
|
2008-05-12 21:58:10 +04:00
|
|
|
int exit_status = 0;
|
2008-07-20 06:22:25 +04:00
|
|
|
int newfd;
|
2006-05-17 20:33:32 +04:00
|
|
|
const char **pathspec;
|
|
|
|
struct dir_struct dir;
|
2008-05-23 01:59:42 +04:00
|
|
|
int flags;
|
2008-07-20 06:22:25 +04:00
|
|
|
int add_new_files;
|
|
|
|
int require_pathspec;
|
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 07:55:50 +03:00
|
|
|
|
2007-10-04 01:45:02 +04:00
|
|
|
argc = parse_options(argc, argv, builtin_add_options,
|
|
|
|
builtin_add_usage, 0);
|
2007-11-25 16:15:42 +03:00
|
|
|
if (patch_interactive)
|
|
|
|
add_interactive = 1;
|
2007-11-22 03:02:52 +03:00
|
|
|
if (add_interactive)
|
2007-11-25 21:10:10 +03:00
|
|
|
exit(interactive_add(argc, argv, prefix));
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2008-05-26 01:25:02 +04:00
|
|
|
git_config(add_config, NULL);
|
2006-05-17 20:33:32 +04:00
|
|
|
|
git-add --all: add all files
People sometimes find that "git add -u && git add ." are 13 keystrokes too
many. This reduces it by nine.
The support of this has been very low priority for me personally, because
I almost never do "git add ." in a directory with already tracked files,
and in a new directory, there is no point saying "git add -u".
However, for two types of people (that are very different from me), this
mode of operation may make sense and there is no reason to leave it
unsupported. That is:
(1) If you are extremely well disciplined and keep perfect .gitignore, it
always is safe to say "git add ."; or
(2) If you are extremely undisciplined and do not even know what files
you created, and you do not very much care what goes in your history,
it does not matter if "git add ." included everything.
So there it is, although I suspect I will not use it myself, ever.
It will be too much of a change that is against the expectation of the
existing users to allow "git commit -a" to include untracked files, and
it would be inconsistent if we named this new option "-a", so the short
option is "-A". We _might_ want to later add "git commit -A" but that is
a separate topic.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-20 06:51:11 +04:00
|
|
|
if (addremove && take_worktree_changes)
|
|
|
|
die("-A and -u are mutually incompatible");
|
|
|
|
if (addremove && !argc) {
|
|
|
|
static const char *here[2] = { ".", NULL };
|
|
|
|
argc = 1;
|
|
|
|
argv = here;
|
|
|
|
}
|
|
|
|
|
2008-07-20 06:22:25 +04:00
|
|
|
add_new_files = !take_worktree_changes && !refresh_only;
|
|
|
|
require_pathspec = !take_worktree_changes;
|
|
|
|
|
_GIT_INDEX_OUTPUT: allow plumbing to output to an alternative index file.
When defined, this allows plumbing commands that update the
index (add, apply, checkout-index, merge-recursive, mv,
read-tree, rm, update-index, and write-tree) to write their
resulting index to an alternative index file while holding a
lock to the original index file. With this, git-commit that
jumps the index does not have to make an extra copy of the index
file, and more importantly, it can do the update while holding
the lock on the index.
However, I think the interface to let an environment variable
specify the output is a mistake, as shown in the documentation.
If a curious user has the environment variable set to something
other than the file GIT_INDEX_FILE points at, almost everything
will break. This should instead be a command line parameter to
tell these plumbing commands to write the result in the named
file, to prevent stupid mistakes.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-01 10:09:02 +04:00
|
|
|
newfd = hold_locked_index(&lock_file, 1);
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2008-05-23 01:59:42 +04:00
|
|
|
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
|
2008-05-26 01:03:50 +04:00
|
|
|
(show_only ? ADD_CACHE_PRETEND : 0) |
|
|
|
|
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
|
2008-05-23 01:59:42 +04:00
|
|
|
|
2008-07-20 06:22:25 +04:00
|
|
|
if (require_pathspec && argc == 0) {
|
2006-12-21 00:06:46 +03:00
|
|
|
fprintf(stderr, "Nothing specified, nothing added.\n");
|
|
|
|
fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2007-10-04 01:45:02 +04:00
|
|
|
pathspec = get_pathspec(prefix, argv);
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2008-07-20 06:22:25 +04:00
|
|
|
/*
|
|
|
|
* If we are adding new files, we need to scan the working
|
|
|
|
* tree to find the ones that match pathspecs; this needs
|
|
|
|
* to be done before we read the index.
|
|
|
|
*/
|
|
|
|
if (add_new_files)
|
|
|
|
fill_directory(&dir, pathspec, ignored_too);
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2006-12-04 19:13:39 +03:00
|
|
|
if (read_cache() < 0)
|
|
|
|
die("index file corrupt");
|
|
|
|
|
2008-07-20 06:22:25 +04:00
|
|
|
if (refresh_only) {
|
|
|
|
refresh(verbose, pathspec);
|
|
|
|
goto finish;
|
2006-12-26 04:46:38 +03:00
|
|
|
}
|
|
|
|
|
git-add --all: add all files
People sometimes find that "git add -u && git add ." are 13 keystrokes too
many. This reduces it by nine.
The support of this has been very low priority for me personally, because
I almost never do "git add ." in a directory with already tracked files,
and in a new directory, there is no point saying "git add -u".
However, for two types of people (that are very different from me), this
mode of operation may make sense and there is no reason to leave it
unsupported. That is:
(1) If you are extremely well disciplined and keep perfect .gitignore, it
always is safe to say "git add ."; or
(2) If you are extremely undisciplined and do not even know what files
you created, and you do not very much care what goes in your history,
it does not matter if "git add ." included everything.
So there it is, although I suspect I will not use it myself, ever.
It will be too much of a change that is against the expectation of the
existing users to allow "git commit -a" to include untracked files, and
it would be inconsistent if we named this new option "-a", so the short
option is "-A". We _might_ want to later add "git commit -A" but that is
a separate topic.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-20 06:51:11 +04:00
|
|
|
if (take_worktree_changes || addremove)
|
2008-07-20 06:22:25 +04:00
|
|
|
exit_status |= add_files_to_cache(prefix, pathspec, flags);
|
|
|
|
|
|
|
|
if (add_new_files)
|
|
|
|
exit_status |= add_files(&dir, flags);
|
2006-05-17 20:33:32 +04:00
|
|
|
|
2007-04-20 12:39:39 +04:00
|
|
|
finish:
|
2006-05-17 20:33:32 +04:00
|
|
|
if (active_cache_changed) {
|
|
|
|
if (write_cache(newfd, active_cache, active_nr) ||
|
2008-01-16 22:12:46 +03:00
|
|
|
commit_locked_index(&lock_file))
|
2006-05-17 20:33:32 +04:00
|
|
|
die("Unable to write new index file");
|
|
|
|
}
|
|
|
|
|
2008-05-12 21:58:10 +04:00
|
|
|
return exit_status;
|
2006-05-17 20:33:32 +04:00
|
|
|
}
|