8000 Merge pull request #6037 from libgit2/ethomson/checkout_safety · libgit2/libgit2@68bffe4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 68bffe4

Browse files
authored
Merge pull request #6037 from libgit2/ethomson/checkout_safety
checkout: make safe checkout the default
2 parents 6ea625c + 6955504 commit 68bffe4

File tree

29 files changed

+336
-323
lines changed

29 files changed

+336
-323
lines changed

include/git2/checkout.h

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,11 @@ GIT_BEGIN_DECL
3131
* check out, the "baseline" tree of what was checked out previously, the
3232
* working directory for actual files, and the index for staged changes.
3333
*
34-
* You give checkout one of three strategies for update:
34+
* You give checkout one of two strategies for update:
3535
*
36-
* - `GIT_CHECKOUT_NONE` is a dry-run strategy that checks for conflicts,
37-
* etc., but doesn't make any actual changes.
38-
*
39-
* - `GIT_CHECKOUT_FORCE` is at the opposite extreme, taking any action to
40-
* make the working directory match the target (including potentially
41-
* discarding modified files).
42-
*
43-
* - `GIT_CHECKOUT_SAFE` is between these two options, it will only make
44-
* modifications that will not lose changes.
36+
* - `GIT_CHECKOUT_SAFE` is the default, and similar to git's default,
37+
* which will make modifications that will not lose changes in the
38+
* working directory.
4539
*
4640
* | target == baseline | target != baseline |
4741
* ---------------------|-----------------------|----------------------|
@@ -55,6 +49,10 @@ GIT_BEGIN_DECL
5549
* baseline present | | |
5650
* ---------------------|-----------------------|----------------------|
5751
*
52+
* - `GIT_CHECKOUT_FORCE` will take any action to make the working
53+
* directory match the target (including potentially discarding
54+
* modified files).
55+
*
5856
* To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout
5957
* notification callback (see below) that displays information about dirty
6058
* files. The default behavior will cancel checkout on conflicts.
@@ -69,6 +67,9 @@ GIT_BEGIN_DECL
6967
*
7068
* There are some additional flags to modify the behavior of checkout:
7169
*
70+
* - `GIT_CHECKOUT_DRY_RUN` is a dry-run strategy that checks for conflicts,
71+
* etc., but doesn't make any actual changes.
72+
*
7273
* - GIT_CHECKOUT_ALLOW_CONFLICTS makes SAFE mode apply safe file updates
7374
* even if there are conflicts (instead of cancelling the checkout).
7475
*
@@ -104,27 +105,20 @@ GIT_BEGIN_DECL
104105
* and write through existing symbolic links.
105106
*/
106107
typedef enum {
107-
GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
108-
109108
/**
110109
* Allow safe updates that cannot overwrite uncommitted data.
111-
* If the uncommitted changes don't conflict with the checked out files,
112-
* the checkout will still proceed, leaving the changes intact.
113-
*
114-
* Mutually exclusive with GIT_CHECKOUT_FORCE.
115-
* GIT_CHECKOUT_FORCE takes precedence over GIT_CHECKOUT_SAFE.
110+
* If the uncommitted changes don't conflict with the checked
111+
* out files, the checkout will still proceed, leaving the
112+
* changes intact.
116113
*/
117-
GIT_CHECKOUT_SAFE = (1u << 0),
114+
GIT_CHECKOUT_SAFE = 0,
118115

119116
/**
120-
* Allow all updates to force working directory to look like index.
121-
*
122-
* Mutually exclusive with GIT_CHECKOUT_SAFE.
123-
* GIT_CHECKOUT_FORCE takes precedence over GIT_CHECKOUT_SAFE.
117+
* Allow all updates to force working directory to look like
118+
* the index, potentially losing data in the process.
124119
*/
125120
GIT_CHECKOUT_FORCE = (1u << 1),
126121

127-
128122
/** Allow checkout to recreate missing files */
129123
GIT_CHECKOUT_RECREATE_MISSING = (1u << 2),
130124

@@ -178,23 +172,31 @@ typedef enum {
178172
GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23),
179173

180174
/**
181-
* Show what would be done by a checkout. Stop after sending
182-
* notifications; don't update the working directory or index.
175+
* Perform a "dry run", reporting what _would_ be done but
176+
* without actually making changes in the working directory
177+
* or the index.
183178
*/
184179
GIT_CHECKOUT_DRY_RUN = (1u << 24),
185180

186181
/** Include common ancestor data in zdiff3 format for conflicts */
187182
GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3 = (1u << 25),
188183

189184
/**
185+
* Do not do a checkout and do not fire callbacks; this is primarily
186+
* useful only for internal functions that will perform the
187+
* checkout themselves but need to pass checkout options into
188+
* another function, for example, `git_clone`.
189+
*/
190+
GIT_CHECKOUT_NONE = (1u << 30),
191+
192+
/*
190193
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
191194
*/
192195

193196
/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
194197
GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
195198
/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
196199
GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17)
197-
198200
} git_checkout_strategy_t;
199201

200202
/**
@@ -345,7 +347,7 @@ typedef struct git_checkout_options {
345347
} git_checkout_options;
346348

347349
#define GIT_CHECKOUT_OPTIONS_VERSION 1
348-
#define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE}
350+
#define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION}
349351

350352
/**
351353
* Initialize git_checkout_options structure

include/git2/clone.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ typedef struct git_clone_options {
105105

106106
/**
107107
* These options are passed to the checkout step. To disable
108-
* checkout, set the `checkout_strategy` to
109-
* `GIT_CHECKOUT_NONE`.
108+
* checkout, set the `checkout_strategy` to `GIT_CHECKOUT_NONE`
109+
* or `GIT_CHECKOUT_DRY_RUN`.
110110
*/
111111
git_checkout_options checkout_opts;
112112

@@ -164,9 +164,10 @@ typedef struct git_clone_options {
164164
} git_clone_options;
165165

166166
#define GIT_CLONE_OPTIONS_VERSION 1
167-
#define GIT_CLONE_OPTIONS_INIT { GIT_CLONE_OPTIONS_VERSION, \
168-
{ GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
169-
GIT_FETCH_OPTIONS_INIT }
167+
#define GIT_CLONE_OPTIONS_INIT \
168+
{ GIT_CLONE_OPTIONS_VERSION, \
169+
GIT_CHECKOUT_OPTIONS_INIT, \
170+
GIT_FETCH_OPTIONS_INIT }
170171

171172
/**
172173
* Initialize git_clone_options structure

include/git2/rebase.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@ typedef struct {
6767

6868
/**
6969
* Options to control how files are written during `git_rebase_init`,
70-
* `git_rebase_next` and `git_rebase_abort`. Note that a minimum
71-
* strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`,
72-
* and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in
73-
* `abort` to match git semantics.
70+
* `git_rebase_next` and `git_rebase_abort`. Note that during
71+
* `abort`, these options will add an implied `GIT_CHECKOUT_FORCE`
72+
* to match git semantics.
7473
*/
7574
git_checkout_options checkout_options;
7675

include/git2/stash.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,6 @@ GIT_EXTERN(int) git_stash_apply_options_init(
225225
* GIT_EMERGECONFLICT and both the working directory and index will be left
226226
* unmodified.
227227
*
228-
* Note that a minimum checkout strategy of `GIT_CHECKOUT_SAFE` is implied.
229-
*
230228
* @param repo The owning repository.
231229
* @param index The position within the stash list. 0 points to the
232230
* most recent stashed state.

include/git2/submodule.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,8 @@ typedef struct git_submodule_update_options {
130130

131131
/**
132132
* These options are passed to the checkout step. To disable
133-
* checkout, set the `checkout_strategy` to
134-
* `GIT_CHECKOUT_NONE`. Generally you will want the use
135-
* GIT_CHECKOUT_SAFE to update files in the working
136-
* directory.
133+
* checkout, set the `checkout_strategy` to `GIT_CHECKOUT_NONE`
134+
* or `GIT_CHECKOUT_DRY_RUN`.
137135
*/
138136
git_checkout_options checkout_opts;
139137

@@ -155,8 +153,9 @@ typedef struct git_submodule_update_options {
155153
#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION 1
156154
#define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \
157155
{ GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
158-
{ GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
159-
GIT_FETCH_OPTIONS_INIT, 1 }
156+
GIT_CHECKOUT_OPTIONS_INIT, \
157+
GIT_FETCH_OPTIONS_INIT, \
158+
1 }
160159

161160
/**
162161
* Initialize git_submodule_update_options structure

src/libgit2/apply.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,6 @@ static int git_apply__to_workdir(
713713
goto done;
714714
}
715715

716-
checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
717716
checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
718717
checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
719718

src/libgit2/checkout.c

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ static int checkout_action_no_wd(
294294

295295
*action = CHECKOUT_ACTION__NONE;
296296

297+
if ((data->strategy & GIT_CHECKOUT_NONE))
298+
return 0;
299+
297300
switch (delta->status) {
298301
case GIT_DELTA_UNMODIFIED: /* case 12 */
299302
error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL);
@@ -302,17 +305,17 @@ static int checkout_action_no_wd(
302305
*action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, NONE);
303306
break;
304307
case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
305-
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
308+
*action = CHECKOUT_ACTION__UPDATE_BLOB;
306309
break;
307310
case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
308311
*action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, CONFLICT);
309312
break;
310313
case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
311314
if (delta->new_file.mode == GIT_FILEMODE_TREE)
312-
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
315+
*action = CHECKOUT_ACTION__UPDATE_BLOB;
313316
break;
314317
case GIT_DELTA_DELETED: /* case 8 or 25 */
315-
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
318+
*action = CHECKOUT_ACTION__REMOVE;
316319
break;
317320
default: /* impossible */
318321
break;
@@ -494,6 +497,9 @@ static int checkout_action_with_wd(
494497
{
495498
*action = CHECKOUT_ACTION__NONE;
496499

500+
if ((data->strategy & GIT_CHECKOUT_NONE))
501+
return 0;
502+
497503
switch (delta->status) {
498504
case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
499505
if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) {
@@ -512,28 +518,28 @@ static int checkout_action_with_wd(
512518
if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
513519
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
514520
else
515-
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
521+
*action = CHECKOUT_ACTION__REMOVE;
516522
break;
517523
case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
518524
if (wd->mode != GIT_FILEMODE_COMMIT &&
519525
checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
520526
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
521527
else
522-
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
528+
*action = CHECKOUT_ACTION__UPDATE_BLOB;
523529
break;
524530
case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */
525531
if (delta->old_file.mode == GIT_FILEMODE_TREE) {
526532
if (wd->mode == GIT_FILEMODE_TREE)
527533
/* either deleting items in old tree will delete the wd dir,
528534
* or we'll get a conflict when we attempt blob update...
529535
*/
530-
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
536+
*action = CHECKOUT_ACTION__UPDATE_BLOB;
531537
else if (wd->mode == GIT_FILEMODE_COMMIT) {
532538
/* workdir is possibly a "phantom" submodule - treat as a
533539
* tree if the only submodule info came from the config
534540
*/
535541
if (submodule_is_config_only(data, wd->path))
536-
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
542+
*action = CHECKOUT_ACTION__UPDATE_BLOB;
537543
else
538544
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
539545
} else
@@ -542,7 +548,7 @@ static int checkout_action_with_wd(
542548
else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
543549
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
544550
else
545-
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
551+
*action = CHECKOUT_ACTION__REMOVE_AND_UPDATE;
546552

547553
/* don't update if the typechange is to a tree */
548554
if (delta->new_file.mode == GIT_FILEMODE_TREE)
@@ -563,6 +569,9 @@ static int checkout_action_with_wd_blocker(
563569
{
564570
*action = CHECKOUT_ACTION__NONE;
565571

572+
if ((data->strategy & GIT_CHECKOUT_NONE))
573+
return 0;
574+
566575
switch (delta->status) {
567576
case GIT_DELTA_UNMODIFIED:
568577
/* should show delta as dirty / deleted */
@@ -597,6 +606,9 @@ static int checkout_action_with_wd_dir(
597606
{
598607
*action = CHECKOUT_ACTION__NONE;
599608

609+
if ((data->strategy & GIT_CHECKOUT_NONE))
610+
return 0;
611+
600612
switch (delta->status) {
601613
case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */
602614
GIT_ERROR_CHECK_ERROR(
@@ -627,7 +639,7 @@ static int checkout_action_with_wd_dir(
627639
* directory if is it left empty, so we can defer removing the
628640
* dir and it will succeed if no children are left.
629641
*/
630-
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
642+
*action = CHECKOUT_ACTION__UPDATE_BLOB;
631643
}
632644
else if (delta->new_file.mode != GIT_FILEMODE_TREE)
633645
/* For typechange to dir, dir is already created so no action */
@@ -2433,14 +2445,12 @@ static int checkout_data_init(
24332445

24342446
/* if you are forcing, allow all safe updates, plus recreate missing */
24352447
if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
2436-
data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE |
2437-
GIT_CHECKOUT_RECREATE_MISSING;
2448+
data->opts.checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING;
24382449

24392450
/* if the repository does not actually have an index file, then this
24402451
* is an initial checkout (perhaps from clone), so we allow safe updates
24412452
*/
2442-
if (!data->index->on_disk &&
2443-
(data->opts.checkout_strategy & GIT_CHECKOUT_SAFE) != 0)
2453+
if (!data->index->on_disk)
24442454
data->opts.checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING;
24452455

24462456
data->strategy = data->opts.checkout_strategy;

src/libgit2/checkout.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
#include "git2/checkout.h"
1313
#include "iterator.h"
1414

15-
#define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12)
16-
1715
/**
1816
* Update the working directory to match the target iterator. The
1917
* expected baseline value can be passed in via the checkout options

src/libgit2/cherrypick.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ static int cherrypick_normalize_opts(
7373
const char *their_label)
7474
{
7575
int error = 0;
76-
unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE |
77-
GIT_CHECKOUT_ALLOW_CONFLICTS;
76+
unsigned int default_checkout_strategy = GIT_CHECKOUT_ALLOW_CONFLICTS;
7877

7978
GIT_UNUSED(repo);
8079

0 commit comments

Comments
 (0)
0