зеркало из https://github.com/microsoft/git.git
Merge branch 'jc/rm-i-t-a'
* jc/rm-i-t-a: git add --intent-to-add: do not let an empty blob be committed by accident git add --intent-to-add: fix removal of cached emptiness builtin-rm.c: explain and clarify the "local change" logic Extend index to save more flags
This commit is contained in:
Коммит
ae26e7c749
|
@ -639,7 +639,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
|
||||||
active_cache_tree = cache_tree();
|
active_cache_tree = cache_tree();
|
||||||
if (cache_tree_update(active_cache_tree,
|
if (cache_tree_update(active_cache_tree,
|
||||||
active_cache, active_nr, 0, 0) < 0) {
|
active_cache, active_nr, 0, 0) < 0) {
|
||||||
error("Error building trees; the index is unmerged?");
|
error("Error building trees");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
47
builtin-rm.c
47
builtin-rm.c
|
@ -31,7 +31,8 @@ static void add_list(const char *name)
|
||||||
|
|
||||||
static int check_local_mod(unsigned char *head, int index_only)
|
static int check_local_mod(unsigned char *head, int index_only)
|
||||||
{
|
{
|
||||||
/* items in list are already sorted in the cache order,
|
/*
|
||||||
|
* Items in list are already sorted in the cache order,
|
||||||
* so we could do this a lot more efficiently by using
|
* so we could do this a lot more efficiently by using
|
||||||
* tree_desc based traversal if we wanted to, but I am
|
* tree_desc based traversal if we wanted to, but I am
|
||||||
* lazy, and who cares if removal of files is a tad
|
* lazy, and who cares if removal of files is a tad
|
||||||
|
@ -71,25 +72,55 @@ static int check_local_mod(unsigned char *head, int index_only)
|
||||||
*/
|
*/
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "rm" of a path that has changes need to be treated
|
||||||
|
* carefully not to allow losing local changes
|
||||||
|
* accidentally. A local change could be (1) file in
|
||||||
|
* work tree is different since the index; and/or (2)
|
||||||
|
* the user staged a content that is different from
|
||||||
|
* the current commit in the index.
|
||||||
|
*
|
||||||
|
* In such a case, you would need to --force the
|
||||||
|
* removal. However, "rm --cached" (remove only from
|
||||||
|
* the index) is safe if the index matches the file in
|
||||||
|
* the work tree or the HEAD commit, as it means that
|
||||||
|
* the content being removed is available elsewhere.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is the index different from the file in the work tree?
|
||||||
|
*/
|
||||||
if (ce_match_stat(ce, &st, 0))
|
if (ce_match_stat(ce, &st, 0))
|
||||||
local_changes = 1;
|
local_changes = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is the index different from the HEAD commit? By
|
||||||
|
* definition, before the very initial commit,
|
||||||
|
* anything staged in the index is treated by the same
|
||||||
|
* way as changed from the HEAD.
|
||||||
|
*/
|
||||||
if (no_head
|
if (no_head
|
||||||
|| get_tree_entry(head, name, sha1, &mode)
|
|| get_tree_entry(head, name, sha1, &mode)
|
||||||
|| ce->ce_mode != create_ce_mode(mode)
|
|| ce->ce_mode != create_ce_mode(mode)
|
||||||
|| hashcmp(ce->sha1, sha1))
|
|| hashcmp(ce->sha1, sha1))
|
||||||
staged_changes = 1;
|
staged_changes = 1;
|
||||||
|
|
||||||
if (local_changes && staged_changes &&
|
/*
|
||||||
!(index_only && is_empty_blob_sha1(ce->sha1)))
|
* If the index does not match the file in the work
|
||||||
|
* tree and if it does not match the HEAD commit
|
||||||
|
* either, (1) "git rm" without --cached definitely
|
||||||
|
* will lose information; (2) "git rm --cached" will
|
||||||
|
* lose information unless it is about removing an
|
||||||
|
* "intent to add" entry.
|
||||||
|
*/
|
||||||
|
if (local_changes && staged_changes) {
|
||||||
|
if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
|
||||||
errs = error("'%s' has staged content different "
|
errs = error("'%s' has staged content different "
|
||||||
"from both the file and the HEAD\n"
|
"from both the file and the HEAD\n"
|
||||||
"(use -f to force removal)", name);
|
"(use -f to force removal)", name);
|
||||||
|
}
|
||||||
else if (!index_only) {
|
else if (!index_only) {
|
||||||
/* It's not dangerous to "git rm --cached" a
|
|
||||||
* file if the index matches the file or the
|
|
||||||
* HEAD, since it means the deleted content is
|
|
||||||
* still available somewhere.
|
|
||||||
*/
|
|
||||||
if (staged_changes)
|
if (staged_changes)
|
||||||
errs = error("'%s' has changes staged in the index\n"
|
errs = error("'%s' has changes staged in the index\n"
|
||||||
"(use --cached to keep the file, "
|
"(use --cached to keep the file, "
|
||||||
|
|
|
@ -42,7 +42,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
|
||||||
die("%s: error reading the index", me);
|
die("%s: error reading the index", me);
|
||||||
break;
|
break;
|
||||||
case WRITE_TREE_UNMERGED_INDEX:
|
case WRITE_TREE_UNMERGED_INDEX:
|
||||||
die("%s: error building trees; the index is unmerged?", me);
|
die("%s: error building trees", me);
|
||||||
break;
|
break;
|
||||||
case WRITE_TREE_PREFIX_ERROR:
|
case WRITE_TREE_PREFIX_ERROR:
|
||||||
die("%s: prefix %s not found", me, prefix);
|
die("%s: prefix %s not found", me, prefix);
|
||||||
|
|
|
@ -155,13 +155,17 @@ static int verify_cache(struct cache_entry **cache,
|
||||||
funny = 0;
|
funny = 0;
|
||||||
for (i = 0; i < entries; i++) {
|
for (i = 0; i < entries; i++) {
|
||||||
struct cache_entry *ce = cache[i];
|
struct cache_entry *ce = cache[i];
|
||||||
if (ce_stage(ce)) {
|
if (ce_stage(ce) || (ce->ce_flags & CE_INTENT_TO_ADD)) {
|
||||||
if (10 < ++funny) {
|
if (10 < ++funny) {
|
||||||
fprintf(stderr, "...\n");
|
fprintf(stderr, "...\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (ce_stage(ce))
|
||||||
fprintf(stderr, "%s: unmerged (%s)\n",
|
fprintf(stderr, "%s: unmerged (%s)\n",
|
||||||
ce->name, sha1_to_hex(ce->sha1));
|
ce->name, sha1_to_hex(ce->sha1));
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: not added yet\n",
|
||||||
|
ce->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (funny)
|
if (funny)
|
||||||
|
|
59
cache.h
59
cache.h
|
@ -115,6 +115,26 @@ struct ondisk_cache_entry {
|
||||||
char name[FLEX_ARRAY]; /* more */
|
char name[FLEX_ARRAY]; /* more */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This struct is used when CE_EXTENDED bit is 1
|
||||||
|
* The struct must match ondisk_cache_entry exactly from
|
||||||
|
* ctime till flags
|
||||||
|
*/
|
||||||
|
struct ondisk_cache_entry_extended {
|
||||||
|
struct cache_time ctime;
|
||||||
|
struct cache_time mtime;
|
||||||
|
unsigned int dev;
|
||||||
|
unsigned int ino;
|
||||||
|
unsigned int mode;
|
||||||
|
unsigned int uid;
|
||||||
|
unsigned int gid;
|
||||||
|
unsigned int size;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
unsigned short flags;
|
||||||
|
unsigned short flags2;
|
||||||
|
char name[FLEX_ARRAY]; /* more */
|
||||||
|
};
|
||||||
|
|
||||||
struct cache_entry {
|
struct cache_entry {
|
||||||
unsigned int ce_ctime;
|
unsigned int ce_ctime;
|
||||||
unsigned int ce_mtime;
|
unsigned int ce_mtime;
|
||||||
|
@ -136,7 +156,15 @@ struct cache_entry {
|
||||||
#define CE_VALID (0x8000)
|
#define CE_VALID (0x8000)
|
||||||
#define CE_STAGESHIFT 12
|
#define CE_STAGESHIFT 12
|
||||||
|
|
||||||
/* In-memory only */
|
/*
|
||||||
|
* Range 0xFFFF0000 in ce_flags is divided into
|
||||||
|
* two parts: in-memory flags and on-disk ones.
|
||||||
|
* Flags in CE_EXTENDED_FLAGS will get saved on-disk
|
||||||
|
* if you want to save a new flag, add it in
|
||||||
|
* CE_EXTENDED_FLAGS
|
||||||
|
*
|
||||||
|
* In-memory only flags
|
||||||
|
*/
|
||||||
#define CE_UPDATE (0x10000)
|
#define CE_UPDATE (0x10000)
|
||||||
#define CE_REMOVE (0x20000)
|
#define CE_REMOVE (0x20000)
|
||||||
#define CE_UPTODATE (0x40000)
|
#define CE_UPTODATE (0x40000)
|
||||||
|
@ -145,6 +173,25 @@ struct cache_entry {
|
||||||
#define CE_HASHED (0x100000)
|
#define CE_HASHED (0x100000)
|
||||||
#define CE_UNHASHED (0x200000)
|
#define CE_UNHASHED (0x200000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extended on-disk flags
|
||||||
|
*/
|
||||||
|
#define CE_INTENT_TO_ADD 0x20000000
|
||||||
|
/* CE_EXTENDED2 is for future extension */
|
||||||
|
#define CE_EXTENDED2 0x80000000
|
||||||
|
|
||||||
|
#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Safeguard to avoid saving wrong flags:
|
||||||
|
* - CE_EXTENDED2 won't get saved until its semantic is known
|
||||||
|
* - Bits in 0x0000FFFF have been saved in ce_flags already
|
||||||
|
* - Bits in 0x003F0000 are currently in-memory flags
|
||||||
|
*/
|
||||||
|
#if CE_EXTENDED_FLAGS & 0x803FFFFF
|
||||||
|
#error "CE_EXTENDED_FLAGS out of range"
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the sha1 and stat state of a cache entry from one to
|
* Copy the sha1 and stat state of a cache entry from one to
|
||||||
* another. But we never change the name, or the hash state!
|
* another. But we never change the name, or the hash state!
|
||||||
|
@ -177,7 +224,9 @@ static inline size_t ce_namelen(const struct cache_entry *ce)
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
|
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
|
||||||
#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
|
#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
|
||||||
|
ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
|
||||||
|
ondisk_cache_entry_size(ce_namelen(ce)))
|
||||||
#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
|
#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
|
||||||
#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
|
#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
|
||||||
#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
|
#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
|
||||||
|
@ -220,8 +269,10 @@ static inline int ce_to_dtype(const struct cache_entry *ce)
|
||||||
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
|
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
|
||||||
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
|
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
|
||||||
|
|
||||||
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
|
#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
|
||||||
#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
|
#define cache_entry_size(len) flexible_size(cache_entry,len)
|
||||||
|
#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
|
||||||
|
#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
|
||||||
|
|
||||||
struct index_state {
|
struct index_state {
|
||||||
struct cache_entry **cache;
|
struct cache_entry **cache;
|
||||||
|
|
61
read-cache.c
61
read-cache.c
|
@ -257,6 +257,14 @@ int ie_match_stat(const struct index_state *istate,
|
||||||
if (!ignore_valid && (ce->ce_flags & CE_VALID))
|
if (!ignore_valid && (ce->ce_flags & CE_VALID))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Intent-to-add entries have not been added, so the index entry
|
||||||
|
* by definition never matches what is in the work tree until it
|
||||||
|
* actually gets added.
|
||||||
|
*/
|
||||||
|
if (ce->ce_flags & CE_INTENT_TO_ADD)
|
||||||
|
return DATA_CHANGED | TYPE_CHANGED | MODE_CHANGED;
|
||||||
|
|
||||||
changed = ce_match_stat_basic(ce, st);
|
changed = ce_match_stat_basic(ce, st);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -546,6 +554,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
|
||||||
ce->ce_flags = namelen;
|
ce->ce_flags = namelen;
|
||||||
if (!intent_only)
|
if (!intent_only)
|
||||||
fill_stat_cache_info(ce, st);
|
fill_stat_cache_info(ce, st);
|
||||||
|
else
|
||||||
|
ce->ce_flags |= CE_INTENT_TO_ADD;
|
||||||
|
|
||||||
if (trust_executable_bit && has_symlinks)
|
if (trust_executable_bit && has_symlinks)
|
||||||
ce->ce_mode = create_ce_mode(st_mode);
|
ce->ce_mode = create_ce_mode(st_mode);
|
||||||
|
@ -1098,7 +1108,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
|
||||||
|
|
||||||
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
|
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
|
||||||
return error("bad signature");
|
return error("bad signature");
|
||||||
if (hdr->hdr_version != htonl(2))
|
if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
|
||||||
return error("bad index version");
|
return error("bad index version");
|
||||||
git_SHA1_Init(&c);
|
git_SHA1_Init(&c);
|
||||||
git_SHA1_Update(&c, hdr, size - 20);
|
git_SHA1_Update(&c, hdr, size - 20);
|
||||||
|
@ -1133,6 +1143,7 @@ int read_index(struct index_state *istate)
|
||||||
static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
|
static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
ce->ce_ctime = ntohl(ondisk->ctime.sec);
|
ce->ce_ctime = ntohl(ondisk->ctime.sec);
|
||||||
ce->ce_mtime = ntohl(ondisk->mtime.sec);
|
ce->ce_mtime = ntohl(ondisk->mtime.sec);
|
||||||
|
@ -1145,19 +1156,31 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
|
||||||
/* On-disk flags are just 16 bits */
|
/* On-disk flags are just 16 bits */
|
||||||
ce->ce_flags = ntohs(ondisk->flags);
|
ce->ce_flags = ntohs(ondisk->flags);
|
||||||
|
|
||||||
/* For future extension: we do not understand this entry yet */
|
|
||||||
if (ce->ce_flags & CE_EXTENDED)
|
|
||||||
die("Unknown index entry format");
|
|
||||||
hashcpy(ce->sha1, ondisk->sha1);
|
hashcpy(ce->sha1, ondisk->sha1);
|
||||||
|
|
||||||
len = ce->ce_flags & CE_NAMEMASK;
|
len = ce->ce_flags & CE_NAMEMASK;
|
||||||
|
|
||||||
|
if (ce->ce_flags & CE_EXTENDED) {
|
||||||
|
struct ondisk_cache_entry_extended *ondisk2;
|
||||||
|
int extended_flags;
|
||||||
|
ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
|
||||||
|
extended_flags = ntohs(ondisk2->flags2) << 16;
|
||||||
|
/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
|
||||||
|
if (extended_flags & ~CE_EXTENDED_FLAGS)
|
||||||
|
die("Unknown index entry format %08x", extended_flags);
|
||||||
|
ce->ce_flags |= extended_flags;
|
||||||
|
name = ondisk2->name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name = ondisk->name;
|
||||||
|
|
||||||
if (len == CE_NAMEMASK)
|
if (len == CE_NAMEMASK)
|
||||||
len = strlen(ondisk->name);
|
len = strlen(name);
|
||||||
/*
|
/*
|
||||||
* NEEDSWORK: If the original index is crafted, this copy could
|
* NEEDSWORK: If the original index is crafted, this copy could
|
||||||
* go unchecked.
|
* go unchecked.
|
||||||
*/
|
*/
|
||||||
memcpy(ce->name, ondisk->name, len + 1);
|
memcpy(ce->name, name, len + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
|
static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
|
||||||
|
@ -1422,6 +1445,7 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
int size = ondisk_ce_size(ce);
|
int size = ondisk_ce_size(ce);
|
||||||
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
|
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
|
||||||
|
char *name;
|
||||||
|
|
||||||
ondisk->ctime.sec = htonl(ce->ce_ctime);
|
ondisk->ctime.sec = htonl(ce->ce_ctime);
|
||||||
ondisk->ctime.nsec = 0;
|
ondisk->ctime.nsec = 0;
|
||||||
|
@ -1435,7 +1459,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
|
||||||
ondisk->size = htonl(ce->ce_size);
|
ondisk->size = htonl(ce->ce_size);
|
||||||
hashcpy(ondisk->sha1, ce->sha1);
|
hashcpy(ondisk->sha1, ce->sha1);
|
||||||
ondisk->flags = htons(ce->ce_flags);
|
ondisk->flags = htons(ce->ce_flags);
|
||||||
memcpy(ondisk->name, ce->name, ce_namelen(ce));
|
if (ce->ce_flags & CE_EXTENDED) {
|
||||||
|
struct ondisk_cache_entry_extended *ondisk2;
|
||||||
|
ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
|
||||||
|
ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
|
||||||
|
name = ondisk2->name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name = ondisk->name;
|
||||||
|
memcpy(name, ce->name, ce_namelen(ce));
|
||||||
|
|
||||||
return ce_write(c, fd, ondisk, size);
|
return ce_write(c, fd, ondisk, size);
|
||||||
}
|
}
|
||||||
|
@ -1444,16 +1476,25 @@ int write_index(const struct index_state *istate, int newfd)
|
||||||
{
|
{
|
||||||
git_SHA_CTX c;
|
git_SHA_CTX c;
|
||||||
struct cache_header hdr;
|
struct cache_header hdr;
|
||||||
int i, err, removed;
|
int i, err, removed, extended;
|
||||||
struct cache_entry **cache = istate->cache;
|
struct cache_entry **cache = istate->cache;
|
||||||
int entries = istate->cache_nr;
|
int entries = istate->cache_nr;
|
||||||
|
|
||||||
for (i = removed = 0; i < entries; i++)
|
for (i = removed = extended = 0; i < entries; i++) {
|
||||||
if (cache[i]->ce_flags & CE_REMOVE)
|
if (cache[i]->ce_flags & CE_REMOVE)
|
||||||
removed++;
|
removed++;
|
||||||
|
|
||||||
|
/* reduce extended entries if possible */
|
||||||
|
cache[i]->ce_flags &= ~CE_EXTENDED;
|
||||||
|
if (cache[i]->ce_flags & CE_EXTENDED_FLAGS) {
|
||||||
|
extended++;
|
||||||
|
cache[i]->ce_flags |= CE_EXTENDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hdr.hdr_signature = htonl(CACHE_SIGNATURE);
|
hdr.hdr_signature = htonl(CACHE_SIGNATURE);
|
||||||
hdr.hdr_version = htonl(2);
|
/* for extended format, increase version so older git won't try to read it */
|
||||||
|
hdr.hdr_version = htonl(extended ? 3 : 2);
|
||||||
hdr.hdr_entries = htonl(entries - removed);
|
hdr.hdr_entries = htonl(entries - removed);
|
||||||
|
|
||||||
git_SHA1_Init(&c);
|
git_SHA1_Init(&c);
|
||||||
|
|
|
@ -32,5 +32,33 @@ test_expect_success 'intent to add does not clobber existing paths' '
|
||||||
! grep "$empty" actual
|
! grep "$empty" actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cannot commit with i-t-a entry' '
|
||||||
|
test_tick &&
|
||||||
|
git commit -a -m initial &&
|
||||||
|
git reset --hard &&
|
||||||
|
|
||||||
|
echo xyzzy >rezrov &&
|
||||||
|
echo frotz >nitfol &&
|
||||||
|
git add rezrov &&
|
||||||
|
git add -N nitfol &&
|
||||||
|
test_must_fail git commit
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'can commit with an unrelated i-t-a entry in index' '
|
||||||
|
git reset --hard &&
|
||||||
|
echo xyzzy >rezrov &&
|
||||||
|
echo frotz >nitfol &&
|
||||||
|
git add rezrov &&
|
||||||
|
git add -N nitfol &&
|
||||||
|
git commit -m partial rezrov
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'can "commit -a" with an i-t-a entry' '
|
||||||
|
git reset --hard &&
|
||||||
|
: >nitfol &&
|
||||||
|
git add -N nitfol &&
|
||||||
|
git commit -a -m all
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
||||||
|
|
|
@ -187,8 +187,8 @@ test_expect_success 'but with -f it should work.' '
|
||||||
test_must_fail git ls-files --error-unmatch baz
|
test_must_fail git ls-files --error-unmatch baz
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_failure 'refuse to remove cached empty file with modifications' '
|
test_expect_success 'refuse to remove cached empty file with modifications' '
|
||||||
touch empty &&
|
>empty &&
|
||||||
git add empty &&
|
git add empty &&
|
||||||
echo content >empty &&
|
echo content >empty &&
|
||||||
test_must_fail git rm --cached empty
|
test_must_fail git rm --cached empty
|
||||||
|
|
Загрузка…
Ссылка в новой задаче