Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * "git reset" builtin command |
3 | | * |
4 | | * Copyright (c) 2007 Carlos Rica |
5 | | * |
6 | | * Based on git-reset.sh, which is |
7 | | * |
8 | | * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano |
9 | | */ |
10 | | |
11 | | #include "builtin.h" |
12 | | #include "advice.h" |
13 | | #include "config.h" |
14 | | #include "environment.h" |
15 | | #include "gettext.h" |
16 | | #include "hash.h" |
17 | | #include "hex.h" |
18 | | #include "lockfile.h" |
19 | | #include "object.h" |
20 | | #include "pretty.h" |
21 | | #include "refs.h" |
22 | | #include "diff.h" |
23 | | #include "diffcore.h" |
24 | | #include "tree.h" |
25 | | #include "branch.h" |
26 | | #include "object-name.h" |
27 | | #include "parse-options.h" |
28 | | #include "path.h" |
29 | | #include "unpack-trees.h" |
30 | | #include "cache-tree.h" |
31 | | #include "setup.h" |
32 | | #include "sparse-index.h" |
33 | | #include "submodule.h" |
34 | | #include "trace.h" |
35 | | #include "trace2.h" |
36 | | #include "dir.h" |
37 | | #include "add-interactive.h" |
38 | | |
39 | 0 | #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000) |
40 | | |
41 | | static const char * const git_reset_usage[] = { |
42 | | N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), |
43 | | N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), |
44 | | N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"), |
45 | | N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), |
46 | | NULL |
47 | | }; |
48 | | |
49 | | enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP, NONE }; |
50 | | static const char *reset_type_names[] = { |
51 | | N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL |
52 | | }; |
53 | | |
54 | | static inline int is_merge(void) |
55 | 0 | { |
56 | 0 | return !access(git_path_merge_head(the_repository), F_OK); |
57 | 0 | } |
58 | | |
59 | | static int reset_index(const char *ref, const struct object_id *oid, int reset_type, int quiet) |
60 | 0 | { |
61 | 0 | int i, nr = 0; |
62 | 0 | struct tree_desc desc[2]; |
63 | 0 | struct tree *tree; |
64 | 0 | struct unpack_trees_options opts; |
65 | 0 | int ret = -1; |
66 | |
|
67 | 0 | memset(&opts, 0, sizeof(opts)); |
68 | 0 | opts.head_idx = 1; |
69 | 0 | opts.src_index = the_repository->index; |
70 | 0 | opts.dst_index = the_repository->index; |
71 | 0 | opts.fn = oneway_merge; |
72 | 0 | opts.merge = 1; |
73 | 0 | init_checkout_metadata(&opts.meta, ref, oid, NULL); |
74 | 0 | if (!quiet) |
75 | 0 | opts.verbose_update = 1; |
76 | 0 | switch (reset_type) { |
77 | 0 | case KEEP: |
78 | 0 | case MERGE: |
79 | 0 | opts.update = 1; |
80 | 0 | opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */ |
81 | 0 | break; |
82 | 0 | case HARD: |
83 | 0 | opts.update = 1; |
84 | 0 | opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED; |
85 | 0 | opts.skip_cache_tree_update = 1; |
86 | 0 | break; |
87 | 0 | case MIXED: |
88 | 0 | opts.reset = UNPACK_RESET_PROTECT_UNTRACKED; |
89 | 0 | opts.skip_cache_tree_update = 1; |
90 | | /* but opts.update=0, so working tree not updated */ |
91 | 0 | break; |
92 | 0 | default: |
93 | 0 | BUG("invalid reset_type passed to reset_index"); |
94 | 0 | } |
95 | | |
96 | 0 | repo_read_index_unmerged(the_repository); |
97 | |
|
98 | 0 | if (reset_type == KEEP) { |
99 | 0 | struct object_id head_oid; |
100 | 0 | if (repo_get_oid(the_repository, "HEAD", &head_oid)) |
101 | 0 | return error(_("You do not have a valid HEAD.")); |
102 | 0 | if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid)) |
103 | 0 | return error(_("Failed to find tree of HEAD.")); |
104 | 0 | nr++; |
105 | 0 | opts.fn = twoway_merge; |
106 | 0 | } |
107 | | |
108 | 0 | if (!fill_tree_descriptor(the_repository, desc + nr, oid)) { |
109 | 0 | error(_("Failed to find tree of %s."), oid_to_hex(oid)); |
110 | 0 | goto out; |
111 | 0 | } |
112 | 0 | nr++; |
113 | |
|
114 | 0 | if (unpack_trees(nr, desc, &opts)) |
115 | 0 | goto out; |
116 | | |
117 | 0 | if (reset_type == MIXED || reset_type == HARD) { |
118 | 0 | tree = parse_tree_indirect(oid); |
119 | 0 | if (!tree) { |
120 | 0 | error(_("unable to read tree (%s)"), oid_to_hex(oid)); |
121 | 0 | goto out; |
122 | 0 | } |
123 | 0 | prime_cache_tree(the_repository, the_repository->index, tree); |
124 | 0 | } |
125 | | |
126 | 0 | ret = 0; |
127 | |
|
128 | 0 | out: |
129 | 0 | for (i = 0; i < nr; i++) |
130 | 0 | free((void *)desc[i].buffer); |
131 | 0 | return ret; |
132 | 0 | } |
133 | | |
134 | | static void print_new_head_line(struct commit *commit) |
135 | 0 | { |
136 | 0 | struct strbuf buf = STRBUF_INIT; |
137 | |
|
138 | 0 | printf(_("HEAD is now at %s"), |
139 | 0 | repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV)); |
140 | |
|
141 | 0 | pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); |
142 | 0 | if (buf.len > 0) |
143 | 0 | printf(" %s", buf.buf); |
144 | 0 | putchar('\n'); |
145 | 0 | strbuf_release(&buf); |
146 | 0 | } |
147 | | |
148 | | static void update_index_from_diff(struct diff_queue_struct *q, |
149 | | struct diff_options *opt UNUSED, |
150 | | void *data) |
151 | 0 | { |
152 | 0 | int i; |
153 | 0 | int intent_to_add = *(int *)data; |
154 | |
|
155 | 0 | for (i = 0; i < q->nr; i++) { |
156 | 0 | int pos; |
157 | 0 | struct diff_filespec *one = q->queue[i]->one; |
158 | 0 | int is_in_reset_tree = one->mode && !is_null_oid(&one->oid); |
159 | 0 | struct cache_entry *ce; |
160 | |
|
161 | 0 | if (!is_in_reset_tree && !intent_to_add) { |
162 | 0 | remove_file_from_index(the_repository->index, one->path); |
163 | 0 | continue; |
164 | 0 | } |
165 | | |
166 | 0 | ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path, |
167 | 0 | 0, 0); |
168 | | |
169 | | /* |
170 | | * If the file 1) corresponds to an existing index entry with |
171 | | * skip-worktree set, or 2) does not exist in the index but is |
172 | | * outside the sparse checkout definition, add a skip-worktree bit |
173 | | * to the new index entry. Note that a sparse index will be expanded |
174 | | * if this entry is outside the sparse cone - this is necessary |
175 | | * to properly construct the reset sparse directory. |
176 | | */ |
177 | 0 | pos = index_name_pos(the_repository->index, one->path, strlen(one->path)); |
178 | 0 | if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) || |
179 | 0 | (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index))) |
180 | 0 | ce->ce_flags |= CE_SKIP_WORKTREE; |
181 | |
|
182 | 0 | if (!ce) |
183 | 0 | die(_("make_cache_entry failed for path '%s'"), |
184 | 0 | one->path); |
185 | 0 | if (!is_in_reset_tree) { |
186 | 0 | ce->ce_flags |= CE_INTENT_TO_ADD; |
187 | 0 | set_object_name_for_intent_to_add_entry(ce); |
188 | 0 | } |
189 | 0 | add_index_entry(the_repository->index, ce, |
190 | 0 | ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | static int read_from_tree(const struct pathspec *pathspec, |
195 | | struct object_id *tree_oid, |
196 | | int intent_to_add) |
197 | 0 | { |
198 | 0 | struct diff_options opt; |
199 | |
|
200 | 0 | memset(&opt, 0, sizeof(opt)); |
201 | 0 | copy_pathspec(&opt.pathspec, pathspec); |
202 | 0 | opt.output_format = DIFF_FORMAT_CALLBACK; |
203 | 0 | opt.format_callback = update_index_from_diff; |
204 | 0 | opt.format_callback_data = &intent_to_add; |
205 | 0 | opt.flags.override_submodule_config = 1; |
206 | 0 | opt.flags.recursive = 1; |
207 | 0 | opt.repo = the_repository; |
208 | 0 | opt.change = diff_change; |
209 | 0 | opt.add_remove = diff_addremove; |
210 | |
|
211 | 0 | if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec)) |
212 | 0 | ensure_full_index(the_repository->index); |
213 | |
|
214 | 0 | if (do_diff_cache(tree_oid, &opt)) |
215 | 0 | return 1; |
216 | 0 | diffcore_std(&opt); |
217 | 0 | diff_flush(&opt); |
218 | |
|
219 | 0 | return 0; |
220 | 0 | } |
221 | | |
222 | | static void set_reflog_message(struct strbuf *sb, const char *action, |
223 | | const char *rev) |
224 | 0 | { |
225 | 0 | const char *rla = getenv("GIT_REFLOG_ACTION"); |
226 | |
|
227 | 0 | strbuf_reset(sb); |
228 | 0 | if (rla) |
229 | 0 | strbuf_addf(sb, "%s: %s", rla, action); |
230 | 0 | else if (rev) |
231 | 0 | strbuf_addf(sb, "reset: moving to %s", rev); |
232 | 0 | else |
233 | 0 | strbuf_addf(sb, "reset: %s", action); |
234 | 0 | } |
235 | | |
236 | | static void die_if_unmerged_cache(int reset_type) |
237 | 0 | { |
238 | 0 | if (is_merge() || unmerged_index(the_repository->index)) |
239 | 0 | die(_("Cannot do a %s reset in the middle of a merge."), |
240 | 0 | _(reset_type_names[reset_type])); |
241 | |
|
242 | 0 | } |
243 | | |
244 | | static void parse_args(struct pathspec *pathspec, |
245 | | const char **argv, const char *prefix, |
246 | | int patch_mode, |
247 | | const char **rev_ret) |
248 | 0 | { |
249 | 0 | const char *rev = "HEAD"; |
250 | 0 | struct object_id unused; |
251 | | /* |
252 | | * Possible arguments are: |
253 | | * |
254 | | * git reset [-opts] [<rev>] |
255 | | * git reset [-opts] <tree> [<paths>...] |
256 | | * git reset [-opts] <tree> -- [<paths>...] |
257 | | * git reset [-opts] -- [<paths>...] |
258 | | * git reset [-opts] <paths>... |
259 | | * |
260 | | * At this point, argv points immediately after [-opts]. |
261 | | */ |
262 | |
|
263 | 0 | if (argv[0]) { |
264 | 0 | if (!strcmp(argv[0], "--")) { |
265 | 0 | argv++; /* reset to HEAD, possibly with paths */ |
266 | 0 | } else if (argv[1] && !strcmp(argv[1], "--")) { |
267 | 0 | rev = argv[0]; |
268 | 0 | argv += 2; |
269 | 0 | } |
270 | | /* |
271 | | * Otherwise, argv[0] could be either <rev> or <paths> and |
272 | | * has to be unambiguous. If there is a single argument, it |
273 | | * can not be a tree |
274 | | */ |
275 | 0 | else if ((!argv[1] && !repo_get_oid_committish(the_repository, argv[0], &unused)) || |
276 | 0 | (argv[1] && !repo_get_oid_treeish(the_repository, argv[0], &unused))) { |
277 | | /* |
278 | | * Ok, argv[0] looks like a commit/tree; it should not |
279 | | * be a filename. |
280 | | */ |
281 | 0 | verify_non_filename(prefix, argv[0]); |
282 | 0 | rev = *argv++; |
283 | 0 | } else { |
284 | | /* Otherwise we treat this as a filename */ |
285 | 0 | verify_filename(prefix, argv[0], 1); |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | /* treat '@' as a shortcut for 'HEAD' */ |
290 | 0 | *rev_ret = !strcmp("@", rev) ? "HEAD" : rev; |
291 | |
|
292 | 0 | parse_pathspec(pathspec, 0, |
293 | 0 | PATHSPEC_PREFER_FULL | |
294 | 0 | (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0), |
295 | 0 | prefix, argv); |
296 | 0 | } |
297 | | |
298 | | static int reset_refs(const char *rev, const struct object_id *oid) |
299 | 0 | { |
300 | 0 | int update_ref_status; |
301 | 0 | struct strbuf msg = STRBUF_INIT; |
302 | 0 | struct object_id *orig = NULL, oid_orig, |
303 | 0 | *old_orig = NULL, oid_old_orig; |
304 | |
|
305 | 0 | if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig)) |
306 | 0 | old_orig = &oid_old_orig; |
307 | 0 | if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) { |
308 | 0 | orig = &oid_orig; |
309 | 0 | set_reflog_message(&msg, "updating ORIG_HEAD", NULL); |
310 | 0 | refs_update_ref(get_main_ref_store(the_repository), msg.buf, |
311 | 0 | "ORIG_HEAD", orig, old_orig, 0, |
312 | 0 | UPDATE_REFS_MSG_ON_ERR); |
313 | 0 | } else if (old_orig) |
314 | 0 | refs_delete_ref(get_main_ref_store(the_repository), NULL, |
315 | 0 | "ORIG_HEAD", old_orig, 0); |
316 | 0 | set_reflog_message(&msg, "updating HEAD", rev); |
317 | 0 | update_ref_status = refs_update_ref(get_main_ref_store(the_repository), |
318 | 0 | msg.buf, "HEAD", oid, orig, 0, |
319 | 0 | UPDATE_REFS_MSG_ON_ERR); |
320 | 0 | strbuf_release(&msg); |
321 | 0 | return update_ref_status; |
322 | 0 | } |
323 | | |
324 | | static int git_reset_config(const char *var, const char *value, |
325 | | const struct config_context *ctx, void *cb) |
326 | 0 | { |
327 | 0 | if (!strcmp(var, "submodule.recurse")) |
328 | 0 | return git_default_submodule_config(var, value, cb); |
329 | | |
330 | 0 | return git_default_config(var, value, ctx, cb); |
331 | 0 | } |
332 | | |
333 | | int cmd_reset(int argc, const char **argv, const char *prefix) |
334 | 0 | { |
335 | 0 | int reset_type = NONE, update_ref_status = 0, quiet = 0; |
336 | 0 | int no_refresh = 0; |
337 | 0 | int patch_mode = 0, pathspec_file_nul = 0, unborn; |
338 | 0 | const char *rev; |
339 | 0 | char *pathspec_from_file = NULL; |
340 | 0 | struct object_id oid; |
341 | 0 | struct pathspec pathspec; |
342 | 0 | int intent_to_add = 0; |
343 | 0 | const struct option options[] = { |
344 | 0 | OPT__QUIET(&quiet, N_("be quiet, only report errors")), |
345 | 0 | OPT_BOOL(0, "no-refresh", &no_refresh, |
346 | 0 | N_("skip refreshing the index after reset")), |
347 | 0 | OPT_SET_INT_F(0, "mixed", &reset_type, |
348 | 0 | N_("reset HEAD and index"), |
349 | 0 | MIXED, PARSE_OPT_NONEG), |
350 | 0 | OPT_SET_INT_F(0, "soft", &reset_type, |
351 | 0 | N_("reset only HEAD"), |
352 | 0 | SOFT, PARSE_OPT_NONEG), |
353 | 0 | OPT_SET_INT_F(0, "hard", &reset_type, |
354 | 0 | N_("reset HEAD, index and working tree"), |
355 | 0 | HARD, PARSE_OPT_NONEG), |
356 | 0 | OPT_SET_INT_F(0, "merge", &reset_type, |
357 | 0 | N_("reset HEAD, index and working tree"), |
358 | 0 | MERGE, PARSE_OPT_NONEG), |
359 | 0 | OPT_SET_INT_F(0, "keep", &reset_type, |
360 | 0 | N_("reset HEAD but keep local changes"), |
361 | 0 | KEEP, PARSE_OPT_NONEG), |
362 | 0 | OPT_CALLBACK_F(0, "recurse-submodules", NULL, |
363 | 0 | "reset", "control recursive updating of submodules", |
364 | 0 | PARSE_OPT_OPTARG, |
365 | 0 | option_parse_recurse_submodules_worktree_updater), |
366 | 0 | OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), |
367 | 0 | OPT_BOOL('N', "intent-to-add", &intent_to_add, |
368 | 0 | N_("record only the fact that removed paths will be added later")), |
369 | 0 | OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), |
370 | 0 | OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), |
371 | 0 | OPT_END() |
372 | 0 | }; |
373 | |
|
374 | 0 | git_config(git_reset_config, NULL); |
375 | |
|
376 | 0 | argc = parse_options(argc, argv, prefix, options, git_reset_usage, |
377 | 0 | PARSE_OPT_KEEP_DASHDASH); |
378 | 0 | parse_args(&pathspec, argv, prefix, patch_mode, &rev); |
379 | |
|
380 | 0 | if (pathspec_from_file) { |
381 | 0 | if (patch_mode) |
382 | 0 | die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch"); |
383 | | |
384 | 0 | if (pathspec.nr) |
385 | 0 | die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); |
386 | | |
387 | 0 | parse_pathspec_file(&pathspec, 0, |
388 | 0 | PATHSPEC_PREFER_FULL, |
389 | 0 | prefix, pathspec_from_file, pathspec_file_nul); |
390 | 0 | } else if (pathspec_file_nul) { |
391 | 0 | die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); |
392 | 0 | } |
393 | | |
394 | 0 | unborn = !strcmp(rev, "HEAD") && repo_get_oid(the_repository, "HEAD", |
395 | 0 | &oid); |
396 | 0 | if (unborn) { |
397 | | /* reset on unborn branch: treat as reset to empty tree */ |
398 | 0 | oidcpy(&oid, the_hash_algo->empty_tree); |
399 | 0 | } else if (!pathspec.nr && !patch_mode) { |
400 | 0 | struct commit *commit; |
401 | 0 | if (repo_get_oid_committish(the_repository, rev, &oid)) |
402 | 0 | die(_("Failed to resolve '%s' as a valid revision."), rev); |
403 | 0 | commit = lookup_commit_reference(the_repository, &oid); |
404 | 0 | if (!commit) |
405 | 0 | die(_("Could not parse object '%s'."), rev); |
406 | 0 | oidcpy(&oid, &commit->object.oid); |
407 | 0 | } else { |
408 | 0 | struct tree *tree; |
409 | 0 | if (repo_get_oid_treeish(the_repository, rev, &oid)) |
410 | 0 | die(_("Failed to resolve '%s' as a valid tree."), rev); |
411 | 0 | tree = parse_tree_indirect(&oid); |
412 | 0 | if (!tree) |
413 | 0 | die(_("Could not parse object '%s'."), rev); |
414 | 0 | oidcpy(&oid, &tree->object.oid); |
415 | 0 | } |
416 | | |
417 | 0 | if (patch_mode) { |
418 | 0 | if (reset_type != NONE) |
419 | 0 | die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); |
420 | 0 | trace2_cmd_mode("patch-interactive"); |
421 | 0 | update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, |
422 | 0 | &pathspec); |
423 | 0 | goto cleanup; |
424 | 0 | } |
425 | | |
426 | | /* git reset tree [--] paths... can be used to |
427 | | * load chosen paths from the tree into the index without |
428 | | * affecting the working tree nor HEAD. */ |
429 | 0 | if (pathspec.nr) { |
430 | 0 | if (reset_type == MIXED) |
431 | 0 | warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); |
432 | 0 | else if (reset_type != NONE) |
433 | 0 | die(_("Cannot do %s reset with paths."), |
434 | 0 | _(reset_type_names[reset_type])); |
435 | 0 | } |
436 | 0 | if (reset_type == NONE) |
437 | 0 | reset_type = MIXED; /* by default */ |
438 | |
|
439 | 0 | if (pathspec.nr) |
440 | 0 | trace2_cmd_mode("path"); |
441 | 0 | else |
442 | 0 | trace2_cmd_mode(reset_type_names[reset_type]); |
443 | |
|
444 | 0 | if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree())) |
445 | 0 | setup_work_tree(); |
446 | |
|
447 | 0 | if (reset_type == MIXED && is_bare_repository()) |
448 | 0 | die(_("%s reset is not allowed in a bare repository"), |
449 | 0 | _(reset_type_names[reset_type])); |
450 | | |
451 | 0 | if (intent_to_add && reset_type != MIXED) |
452 | 0 | die(_("the option '%s' requires '%s'"), "-N", "--mixed"); |
453 | | |
454 | 0 | prepare_repo_settings(the_repository); |
455 | 0 | the_repository->settings.command_requires_full_index = 0; |
456 | |
|
457 | 0 | if (repo_read_index(the_repository) < 0) |
458 | 0 | die(_("index file corrupt")); |
459 | | |
460 | | /* Soft reset does not touch the index file nor the working tree |
461 | | * at all, but requires them in a good order. Other resets reset |
462 | | * the index file to the tree object we are switching to. */ |
463 | 0 | if (reset_type == SOFT || reset_type == KEEP) |
464 | 0 | die_if_unmerged_cache(reset_type); |
465 | |
|
466 | 0 | if (reset_type != SOFT) { |
467 | 0 | struct lock_file lock = LOCK_INIT; |
468 | 0 | repo_hold_locked_index(the_repository, &lock, |
469 | 0 | LOCK_DIE_ON_ERROR); |
470 | 0 | if (reset_type == MIXED) { |
471 | 0 | int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; |
472 | 0 | if (read_from_tree(&pathspec, &oid, intent_to_add)) { |
473 | 0 | update_ref_status = 1; |
474 | 0 | goto cleanup; |
475 | 0 | } |
476 | 0 | the_repository->index->updated_skipworktree = 1; |
477 | 0 | if (!no_refresh && get_git_work_tree()) { |
478 | 0 | uint64_t t_begin, t_delta_in_ms; |
479 | |
|
480 | 0 | t_begin = getnanotime(); |
481 | 0 | refresh_index(the_repository->index, flags, NULL, NULL, |
482 | 0 | _("Unstaged changes after reset:")); |
483 | 0 | t_delta_in_ms = (getnanotime() - t_begin) / 1000000; |
484 | 0 | if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) { |
485 | 0 | advise(_("It took %.2f seconds to refresh the index after reset. You can use\n" |
486 | 0 | "'--no-refresh' to avoid this."), t_delta_in_ms / 1000.0); |
487 | 0 | } |
488 | 0 | } |
489 | 0 | } else { |
490 | 0 | struct object_id dummy; |
491 | 0 | char *ref = NULL; |
492 | 0 | int err; |
493 | |
|
494 | 0 | repo_dwim_ref(the_repository, rev, strlen(rev), |
495 | 0 | &dummy, &ref, 0); |
496 | 0 | if (ref && !starts_with(ref, "refs/")) |
497 | 0 | FREE_AND_NULL(ref); |
498 | |
|
499 | 0 | err = reset_index(ref, &oid, reset_type, quiet); |
500 | 0 | if (reset_type == KEEP && !err) |
501 | 0 | err = reset_index(ref, &oid, MIXED, quiet); |
502 | 0 | if (err) |
503 | 0 | die(_("Could not reset index file to revision '%s'."), rev); |
504 | 0 | free(ref); |
505 | 0 | } |
506 | | |
507 | 0 | if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK)) |
508 | 0 | die(_("Could not write new index file.")); |
509 | 0 | } |
510 | | |
511 | 0 | if (!pathspec.nr && !unborn) { |
512 | | /* Any resets without paths update HEAD to the head being |
513 | | * switched to, saving the previous head in ORIG_HEAD before. */ |
514 | 0 | update_ref_status = reset_refs(rev, &oid); |
515 | |
|
516 | 0 | if (reset_type == HARD && !update_ref_status && !quiet) |
517 | 0 | print_new_head_line(lookup_commit_reference(the_repository, &oid)); |
518 | 0 | } |
519 | 0 | if (!pathspec.nr) |
520 | 0 | remove_branch_state(the_repository, 0); |
521 | |
|
522 | 0 | discard_index(the_repository->index); |
523 | |
|
524 | 0 | cleanup: |
525 | 0 | clear_pathspec(&pathspec); |
526 | 0 | free(pathspec_from_file); |
527 | 0 | return update_ref_status; |
528 | 0 | } |