зеркало из https://github.com/microsoft/git.git
reset: support "--mixed --intent-to-add" mode
When --mixed is used, entries could be removed from index if the target ref does not have them. When "reset" is used in preparation for commit spliting (in a dirty worktree), it could be hard to track what files to be added back. The new option --intent-to-add simplifies it by marking all removed files intent-to-add. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
This commit is contained in:
Родитель
be961c292f
Коммит
b4b313f94a
|
@ -10,7 +10,7 @@ SYNOPSIS
|
||||||
[verse]
|
[verse]
|
||||||
'git reset' [-q] [<tree-ish>] [--] <paths>...
|
'git reset' [-q] [<tree-ish>] [--] <paths>...
|
||||||
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
|
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
|
||||||
'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
|
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -60,6 +60,9 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
|
||||||
Resets the index but not the working tree (i.e., the changed files
|
Resets the index but not the working tree (i.e., the changed files
|
||||||
are preserved but not marked for commit) and reports what has not
|
are preserved but not marked for commit) and reports what has not
|
||||||
been updated. This is the default action.
|
been updated. This is the default action.
|
||||||
|
+
|
||||||
|
If `-N` is specified, removed paths are marked as intent-to-add (see
|
||||||
|
linkgit:git-add[1]).
|
||||||
|
|
||||||
--hard::
|
--hard::
|
||||||
Resets the index and working tree. Any changes to tracked files in the
|
Resets the index and working tree. Any changes to tracked files in the
|
||||||
|
|
|
@ -116,25 +116,34 @@ static void update_index_from_diff(struct diff_queue_struct *q,
|
||||||
struct diff_options *opt, void *data)
|
struct diff_options *opt, void *data)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int intent_to_add = *(int *)data;
|
||||||
|
|
||||||
for (i = 0; i < q->nr; i++) {
|
for (i = 0; i < q->nr; i++) {
|
||||||
struct diff_filespec *one = q->queue[i]->one;
|
struct diff_filespec *one = q->queue[i]->one;
|
||||||
if (one->mode && !is_null_sha1(one->sha1)) {
|
int is_missing = !(one->mode && !is_null_sha1(one->sha1));
|
||||||
struct cache_entry *ce;
|
struct cache_entry *ce;
|
||||||
ce = make_cache_entry(one->mode, one->sha1, one->path,
|
|
||||||
0, 0);
|
if (is_missing && !intent_to_add) {
|
||||||
if (!ce)
|
|
||||||
die(_("make_cache_entry failed for path '%s'"),
|
|
||||||
one->path);
|
|
||||||
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
|
|
||||||
ADD_CACHE_OK_TO_REPLACE);
|
|
||||||
} else
|
|
||||||
remove_file_from_cache(one->path);
|
remove_file_from_cache(one->path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ce = make_cache_entry(one->mode, one->sha1, one->path,
|
||||||
|
0, 0);
|
||||||
|
if (!ce)
|
||||||
|
die(_("make_cache_entry failed for path '%s'"),
|
||||||
|
one->path);
|
||||||
|
if (is_missing) {
|
||||||
|
ce->ce_flags |= CE_INTENT_TO_ADD;
|
||||||
|
set_object_name_for_intent_to_add_entry(ce);
|
||||||
|
}
|
||||||
|
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_from_tree(const struct pathspec *pathspec,
|
static int read_from_tree(const struct pathspec *pathspec,
|
||||||
unsigned char *tree_sha1)
|
unsigned char *tree_sha1,
|
||||||
|
int intent_to_add)
|
||||||
{
|
{
|
||||||
struct diff_options opt;
|
struct diff_options opt;
|
||||||
|
|
||||||
|
@ -142,6 +151,7 @@ static int read_from_tree(const struct pathspec *pathspec,
|
||||||
copy_pathspec(&opt.pathspec, pathspec);
|
copy_pathspec(&opt.pathspec, pathspec);
|
||||||
opt.output_format = DIFF_FORMAT_CALLBACK;
|
opt.output_format = DIFF_FORMAT_CALLBACK;
|
||||||
opt.format_callback = update_index_from_diff;
|
opt.format_callback = update_index_from_diff;
|
||||||
|
opt.format_callback_data = &intent_to_add;
|
||||||
|
|
||||||
if (do_diff_cache(tree_sha1, &opt))
|
if (do_diff_cache(tree_sha1, &opt))
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -258,6 +268,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||||
const char *rev;
|
const char *rev;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
struct pathspec pathspec;
|
struct pathspec pathspec;
|
||||||
|
int intent_to_add = 0;
|
||||||
const struct option options[] = {
|
const struct option options[] = {
|
||||||
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
|
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
|
||||||
OPT_SET_INT(0, "mixed", &reset_type,
|
OPT_SET_INT(0, "mixed", &reset_type,
|
||||||
|
@ -270,6 +281,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||||
OPT_SET_INT(0, "keep", &reset_type,
|
OPT_SET_INT(0, "keep", &reset_type,
|
||||||
N_("reset HEAD but keep local changes"), KEEP),
|
N_("reset HEAD but keep local changes"), KEEP),
|
||||||
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
|
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
|
||||||
|
OPT_BOOL('N', "intent-to-add", &intent_to_add,
|
||||||
|
N_("record only the fact that removed paths will be added later")),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -327,6 +340,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||||
die(_("%s reset is not allowed in a bare repository"),
|
die(_("%s reset is not allowed in a bare repository"),
|
||||||
_(reset_type_names[reset_type]));
|
_(reset_type_names[reset_type]));
|
||||||
|
|
||||||
|
if (intent_to_add && reset_type != MIXED)
|
||||||
|
die(_("-N can only be used with --mixed"));
|
||||||
|
|
||||||
/* Soft reset does not touch the index file nor the working tree
|
/* Soft reset does not touch the index file nor the working tree
|
||||||
* at all, but requires them in a good order. Other resets reset
|
* at all, but requires them in a good order. Other resets reset
|
||||||
* the index file to the tree object we are switching to. */
|
* the index file to the tree object we are switching to. */
|
||||||
|
@ -338,7 +354,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||||
int newfd = hold_locked_index(lock, 1);
|
int newfd = hold_locked_index(lock, 1);
|
||||||
if (reset_type == MIXED) {
|
if (reset_type == MIXED) {
|
||||||
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
|
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
|
||||||
if (read_from_tree(&pathspec, sha1))
|
if (read_from_tree(&pathspec, sha1, intent_to_add))
|
||||||
return 1;
|
return 1;
|
||||||
refresh_index(&the_index, flags, NULL, NULL,
|
refresh_index(&the_index, flags, NULL, NULL,
|
||||||
_("Unstaged changes after reset:"));
|
_("Unstaged changes after reset:"));
|
||||||
|
|
1
cache.h
1
cache.h
|
@ -489,6 +489,7 @@ extern int add_to_index(struct index_state *, const char *path, struct stat *, i
|
||||||
extern int add_file_to_index(struct index_state *, const char *path, int flags);
|
extern int add_file_to_index(struct index_state *, const char *path, int flags);
|
||||||
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
|
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
|
||||||
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
|
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
|
||||||
|
extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
|
||||||
extern int index_name_is_other(const struct index_state *, const char *, int);
|
extern int index_name_is_other(const struct index_state *, const char *, int);
|
||||||
extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
|
extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
|
||||||
|
|
||||||
|
|
|
@ -579,7 +579,7 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void record_intent_to_add(struct cache_entry *ce)
|
void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
if (write_sha1_file("", 0, blob_type, sha1))
|
if (write_sha1_file("", 0, blob_type, sha1))
|
||||||
|
@ -665,7 +665,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
|
||||||
if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT))
|
if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT))
|
||||||
return error("unable to index file %s", path);
|
return error("unable to index file %s", path);
|
||||||
} else
|
} else
|
||||||
record_intent_to_add(ce);
|
set_object_name_for_intent_to_add_entry(ce);
|
||||||
|
|
||||||
if (ignore_case && alias && different_name(ce, alias))
|
if (ignore_case && alias && different_name(ce, alias))
|
||||||
ce = create_alias_ce(ce, alias);
|
ce = create_alias_ce(ce, alias);
|
||||||
|
|
|
@ -535,4 +535,19 @@ test_expect_success 'reset with paths accepts tree' '
|
||||||
git diff HEAD --exit-code
|
git diff HEAD --exit-code
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'reset -N keeps removed files as intent-to-add' '
|
||||||
|
echo new-file >new-file &&
|
||||||
|
git add new-file &&
|
||||||
|
git reset -N HEAD &&
|
||||||
|
|
||||||
|
tree=$(git write-tree) &&
|
||||||
|
git ls-tree $tree new-file >actual &&
|
||||||
|
>expect &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
git diff --name-only >actual &&
|
||||||
|
echo new-file >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Загрузка…
Ссылка в новой задаче