Skip to content

Commit 3942581

Browse files
committed
git-add --intent-to-add (-N)
This adds "--intent-to-add" option to "git add". This is to let the system know that you will tell it the final contents to be staged later, iow, just be aware of the presense of the path with the type of the blob for now. It is implemented by staging an empty blob as the content. With this sequence: $ git reset --hard $ edit newfile $ git add -N newfile $ edit newfile oldfile $ git diff the diff will show all changes relative to the current commit. Then you can do: $ git commit -a ;# commit everything or $ git commit oldfile ;# only oldfile, newfile not yet added to pretend you are working with an index-free system like CVS. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7df437e commit 3942581

File tree

4 files changed

+65
-6
lines changed

4 files changed

+65
-6
lines changed

builtin-add.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ static const char ignore_error[] =
166166
"The following paths are ignored by one of your .gitignore files:\n";
167167

168168
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
169-
static int ignore_add_errors, addremove;
169+
static int ignore_add_errors, addremove, intent_to_add;
170170

171171
static struct option builtin_add_options[] = {
172172
OPT__DRY_RUN(&show_only),
@@ -176,6 +176,7 @@ static struct option builtin_add_options[] = {
176176
OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
177177
OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
178178
OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
179+
OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
179180
OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
180181
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
181182
OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
@@ -246,6 +247,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
246247

247248
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
248249
(show_only ? ADD_CACHE_PRETEND : 0) |
250+
(intent_to_add ? ADD_CACHE_INTENT : 0) |
249251
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
250252
(!(addremove || take_worktree_changes)
251253
? ADD_CACHE_IGNORE_REMOVAL : 0));

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
371371
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
372372
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
373373
#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
374+
#define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */
374375
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
375376
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
376377
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
@@ -380,6 +381,7 @@ extern int remove_file_from_index(struct index_state *, const char *path);
380381
#define ADD_CACHE_PRETEND 2
381382
#define ADD_CACHE_IGNORE_ERRORS 4
382383
#define ADD_CACHE_IGNORE_REMOVAL 8
384+
#define ADD_CACHE_INTENT 16
383385
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
384386
extern int add_file_to_index(struct index_state *, const char *path, int flags);
385387
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);

read-cache.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "diff.h"
1414
#include "diffcore.h"
1515
#include "revision.h"
16+
#include "blob.h"
1617

1718
/* Index extensions.
1819
*
@@ -511,6 +512,14 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
511512
return new;
512513
}
513514

515+
static void record_intent_to_add(struct cache_entry *ce)
516+
{
517+
unsigned char sha1[20];
518+
if (write_sha1_file("", 0, blob_type, sha1))
519+
die("cannot create an empty blob in the object database");
520+
hashcpy(ce->sha1, sha1);
521+
}
522+
514523
int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
515524
{
516525
int size, namelen, was_same;
@@ -519,6 +528,9 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
519528
unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
520529
int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
521530
int pretend = flags & ADD_CACHE_PRETEND;
531+
int intent_only = flags & ADD_CACHE_INTENT;
532+
int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
533+
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
522534

523535
if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
524536
return error("%s: can only add regular files, symbolic links or git-directories", path);
@@ -532,7 +544,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
532544
ce = xcalloc(1, size);
533545
memcpy(ce->name, path, namelen);
534546
ce->ce_flags = namelen;
535-
fill_stat_cache_info(ce, st);
547+
if (!intent_only)
548+
fill_stat_cache_info(ce, st);
536549

537550
if (trust_executable_bit && has_symlinks)
538551
ce->ce_mode = create_ce_mode(st_mode);
@@ -555,8 +568,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
555568
alias->ce_flags |= CE_ADDED;
556569
return 0;
557570
}
558-
if (index_path(ce->sha1, path, st, 1))
559-
return error("unable to index file %s", path);
571+
if (!intent_only) {
572+
if (index_path(ce->sha1, path, st, 1))
573+
return error("unable to index file %s", path);
574+
} else
575+
record_intent_to_add(ce);
576+
560577
if (ignore_case && alias && different_name(ce, alias))
561578
ce = create_alias_ce(ce, alias);
562579
ce->ce_flags |= CE_ADDED;
@@ -569,7 +586,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
569586

570587
if (pretend)
571588
;
572-
else if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
589+
else if (add_index_entry(istate, ce, add_option))
573590
return error("unable to add %s to index",path);
574591
if (verbose && !was_same)
575592
printf("add '%s'\n", path);
@@ -848,13 +865,15 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
848865
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
849866
int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
850867
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
868+
int new_only = option & ADD_CACHE_NEW_ONLY;
851869

852870
cache_tree_invalidate_path(istate->cache_tree, ce->name);
853871
pos = index_name_pos(istate, ce->name, ce->ce_flags);
854872

855873
/* existing match? Just replace it. */
856874
if (pos >= 0) {
857-
replace_index_entry(istate, pos, ce);
875+
if (!new_only)
876+
replace_index_entry(istate, pos, ce);
858877
return 0;
859878
}
860879
pos = -pos-1;

t/t2203-add-intent.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/sh
2+
3+
test_description='Intent to add'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'intent to add' '
8+
echo hello >file &&
9+
echo hello >elif &&
10+
git add -N file &&
11+
git add elif
12+
'
13+
14+
test_expect_success 'check result of "add -N"' '
15+
git ls-files -s file >actual &&
16+
empty=$(git hash-object --stdin </dev/null) &&
17+
echo "100644 $empty 0 file" >expect &&
18+
test_cmp expect actual
19+
'
20+
21+
test_expect_success 'intent to add is just an ordinary empty blob' '
22+
git add -u &&
23+
git ls-files -s file >actual &&
24+
git ls-files -s elif | sed -e "s/elif/file/" >expect &&
25+
test_cmp expect actual
26+
'
27+
28+
test_expect_success 'intent to add does not clobber existing paths' '
29+
git add -N file elif &&
30+
empty=$(git hash-object --stdin </dev/null) &&
31+
git ls-files -s >actual &&
32+
! grep "$empty" actual
33+
'
34+
35+
test_done
36+

0 commit comments

Comments
 (0)