From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:05
Here's the series I mentioned a couple of times on the list already,
introducing a no-overlay mode in 'git checkout'. The inspiration for
this came from Junios message in [*1*].
Basically the idea is to also delete files when the match <pathspec>
in 'git checkout <tree-ish> -- <pathspec>' in the current tree, but
don't match <pathspec> in <tree-ish>. The rest of the cases are
already properly taken care of by 'git checkout'.
The final step in the series is to actually make use of this in 'git
stash', which simplifies the code there a bit. I am however happy to
hold off on this step until the stash-in-C series is merged, so we
don't delay that further.
In addition to the no-overlay mode, we also add a --cached mode, which
works only on the index, thus similar to 'git reset <tree-ish> -- <pathspec>'.
Actually deprecating 'git reset <tree-ish> -- <pathspec>' should come
later, probably not before Duy's restore-files command lands, as 'git
checkout --no-overlay <tree-ish> -- <pathspec>' is a bit cumbersome to
type compared to 'git reset <tree-ish> -- <pathspec>'.
My hope is also that the no-overlay mode could become the new default
in the restore-files command Duy is currently working on.
No documentation yet, as I wanted to get this out for review first.
I'm not familiar with most of the code I touched here, so there may
well be much better ways to implement some of this, that I wasn't able
to figure out. I'd be very happy with some feedback around that.
Another thing I'm not sure about is how to deal with conflicts. In
the cached mode this patch series is not dealing with it at all, as
'git checkout -- <pathspec>' when pathspec matches a file with
conflicts doesn't update the index. For the no-overlay mode, the file
is removed if the corresponding stage is not found in the index. I'm
however not sure this is the right thing to do in all cases?
*1*: [off-list ref]
Thomas Gummerer (8):
move worktree tests to t24*
entry: factor out unlink_entry function
entry: support CE_WT_REMOVE flag in checkout_entry
read-cache: add invalidate parameter to remove_marked_cache_entries
checkout: introduce --{,no-}overlay option
checkout: add --cached option
checkout: add allow ignoring unmatched pathspec
stash: use git checkout --index
builtin/checkout.c | 66 +++++++++--
cache.h | 7 +-
entry.c | 22 ++++
git-stash.sh | 12 +-
read-cache.c | 8 +-
split-index.c | 2 +-
t/t2016-checkout-patch.sh | 8 ++
t/t2022-checkout-paths.sh | 9 ++
t/t2025-checkout-no-overlay.sh | 31 ++++++
t/t2026-checkout-cached.sh | 103 ++++++++++++++++++
...-worktree-add.sh => t2400-worktree-add.sh} | 0
...ktree-prune.sh => t2401-worktree-prune.sh} | 0
...orktree-list.sh => t2402-worktree-list.sh} | 0
t/t9902-completion.sh | 3 +
unpack-trees.c | 21 +---
15 files changed, 251 insertions(+), 41 deletions(-)
create mode 100755 t/t2025-checkout-no-overlay.sh
create mode 100755 t/t2026-checkout-cached.sh
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
--
2.20.0.rc2.411.g8f28e744c2
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:05
The 'git worktree' command used to be just another mode in 'git
checkout', namely 'git checkout --to'. When the tests for the latter
were retrofitted for the former, the test name was adjusted, but the
test number was kept, even though the test is testing a different
command now. t/README states: "Second digit tells the particular
command we are testing.", so 'git worktree' should have a separate
number just for itself.
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
t/{t2025-worktree-add.sh => t2400-worktree-add.sh} | 0
t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} | 0
t/{t2027-worktree-list.sh => t2402-worktree-list.sh} | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
diff --git a/t/t2025-worktree-add.sh b/t/t2400-worktree-add.shsimilarity index 100%rename from t/t2025-worktree-add.shrename to t/t2400-worktree-add.shdiff --git a/t/t2026-worktree-prune.sh b/t/t2401-worktree-prune.shsimilarity index 100%rename from t/t2026-worktree-prune.shrename to t/t2401-worktree-prune.shdiff --git a/t/t2027-worktree-list.sh b/t/t2402-worktree-list.shsimilarity index 100%rename from t/t2027-worktree-list.shrename to t/t2402-worktree-list.sh
--
2.20.0.405.gbc1bbc6f85
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:05
'checkout_entry()' currently only supports creating new entries in the
working tree, but not deleting them. Add the ability to remove
entries at the same time if the entry is marked with the CE_WT_REMOVE
flag.
Currently this doesn't have any effect, as the CE_WT_REMOVE flag is
only used in unpack-tree, however we will make use of this in a
subsequent step in the series.
Signed-off-by: Thomas Gummerer <redacted>
---
entry.c | 7 +++++++
1 file changed, 7 insertions(+)
@@ -441,6 +441,13 @@ int checkout_entry(struct cache_entry *ce,staticstructstrbufpath=STRBUF_INIT;structstatst;+if(ce->ce_flags&CE_WT_REMOVE){+if(topath)+BUG("Can't remove entry to a path");+unlink_entry(ce);+return0;+}+if(topath)returnwrite_entry(ce,topath,state,1);
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:05
Factor out the 'unlink_entry()' function from unpack-trees.c to
entry.c. It will be used in other places as well in subsequent
steps.
As it's no longer a static function, also move the documentation to
the header file to make it more discoverable.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 5 +++++
entry.c | 15 +++++++++++++++
unpack-trees.c | 19 -------------------
3 files changed, 20 insertions(+), 19 deletions(-)
@@ -508,3 +508,18 @@ int checkout_entry(struct cache_entry *ce,create_directories(path.buf,path.len,state);returnwrite_entry(ce,path.buf,state,0);}++voidunlink_entry(conststructcache_entry*ce)+{+conststructsubmodule*sub=submodule_from_ce(ce);+if(sub){+/* state.force is set at the caller. */+submodule_move_head(ce->name,"HEAD",NULL,+SUBMODULE_MOVE_HEAD_FORCE);+}+if(!check_leading_path(ce->name,ce_namelen(ce)))+return;+if(remove_or_warn(ce->ce_mode,ce->name))+return;+schedule_dir_for_removal(ce->name,ce_namelen(ce));+}
@@ -300,25 +300,6 @@ static void load_gitmodules_file(struct index_state *index,}}-/*-*Unlinkthelastcomponentandscheduletheleadingdirectoriesfor-*removal,suchthatemptydirectoriesgetremoved.-*/-staticvoidunlink_entry(conststructcache_entry*ce)-{-conststructsubmodule*sub=submodule_from_ce(ce);-if(sub){-/* state.force is set at the caller. */-submodule_move_head(ce->name,"HEAD",NULL,-SUBMODULE_MOVE_HEAD_FORCE);-}-if(!check_leading_path(ce->name,ce_namelen(ce)))-return;-if(remove_or_warn(ce->ce_mode,ce->name))-return;-schedule_dir_for_removal(ce->name,ce_namelen(ce));-}-staticstructprogress*get_progress(structunpack_trees_options*o){unsignedcnt=0,total=0;
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:09
When marking cache entries for removal, and later removing them all at
once using 'remove_marked_cache_entries()', cache entries currently
have to be invalidated manually in the cache tree and in the untracked
cache.
Add an invalidate flag to the function. With the flag set, the
function will take care of invalidating the path in the cache tree and
in the untracked cache.
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
For the two current callsites, unpack-trees seems to do this
invalidation itself internally. I don't quite understand why we don't
need it in split-index mode though. I assume it's because the cache
tree in the main index would already have been invalidated? I didn't
have much time to dig, but couldn't produce any failures with it
either, so I assume not invalidating paths is the right thing to do
here.
cache.h | 2 +-
read-cache.c | 8 +++++++-
split-index.c | 2 +-
unpack-trees.c | 2 +-
4 files changed, 10 insertions(+), 4 deletions(-)
@@ -751,7 +751,7 @@ extern void rename_index_entry_at(struct index_state *, int pos, const char *new/* Remove entry, return true if there are more entries to go. */externintremove_index_entry_at(structindex_state*,intpos);-externvoidremove_marked_cache_entries(structindex_state*istate);+externvoidremove_marked_cache_entries(structindex_state*istate,intinvalidate);externintremove_file_from_index(structindex_state*,constchar*path);#define ADD_CACHE_VERBOSE 1#define ADD_CACHE_PRETEND 2
@@ -590,13 +590,19 @@ int remove_index_entry_at(struct index_state *istate, int pos)*CE_REMOVEissetince_flags.Thisismuchmoreeffectivethan*callingremove_index_entry_at()foreachentrytoberemoved.*/-voidremove_marked_cache_entries(structindex_state*istate)+voidremove_marked_cache_entries(structindex_state*istate,intinvalidate){structcache_entry**ce_array=istate->cache;unsignedinti,j;for(i=j=0;i<istate->cache_nr;i++){if(ce_array[i]->ce_flags&CE_REMOVE){+if(invalidate){+cache_tree_invalidate_path(istate,+ce_array[i]->name);+untracked_cache_remove_from_index(istate,+ce_array[i]->name);+}remove_name_hash(istate,ce_array[i]);save_or_free_index_entry(istate,ce_array[i]);}
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:09
Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.
Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.
Note that 'git checkout -p <tree-ish> -- [<pathspec>]' already works
this way, so no changes are needed for the patch mode. We disallow
'git checkout --overlay -p' to avoid confusing users who would expect
to be able to force overlay mode in 'git checkout -p' this way.
Signed-off-by: Thomas Gummerer <redacted>
---
builtin/checkout.c | 64 +++++++++++++++++++++++++++-------
t/t2025-checkout-no-overlay.sh | 47 +++++++++++++++++++++++++
t/t9902-completion.sh | 1 +
3 files changed, 99 insertions(+), 13 deletions(-)
create mode 100755 t/t2025-checkout-no-overlay.sh
@@ -132,7 +133,8 @@ static int skip_same_name(const struct cache_entry *ce, int pos)returnpos;}-staticintcheck_stage(intstage,conststructcache_entry*ce,intpos)+staticintcheck_stage(intstage,conststructcache_entry*ce,intpos,+intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -140,6 +142,8 @@ static int check_stage(int stage, const struct cache_entry *ce, int pos)return0;pos++;}+if(!overlay_mode)+return0;if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -165,7 +169,7 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)}staticintcheckout_stage(intstage,conststructcache_entry*ce,intpos,-conststructcheckout*state)+conststructcheckout*state,intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -173,6 +177,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,returncheckout_entry(active_cache[pos],state,NULL);pos++;}+if(!overlay_mode){+unlink_entry(ce);+return0;+}if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -348,7 +370,7 @@ static int checkout_paths(const struct checkout_opts *opts,if(opts->force){warning(_("path '%s' is unmerged"),ce->name);}elseif(opts->writeout_stage){-errs|=check_stage(opts->writeout_stage,ce,pos);+errs|=check_stage(opts->writeout_stage,ce,pos,opts->overlay_mode);}elseif(opts->merge){errs|=check_stages((1<<2)|(1<<3),ce,pos);}else{
@@ -375,12 +397,14 @@ static int checkout_paths(const struct checkout_opts *opts,continue;}if(opts->writeout_stage)-errs|=checkout_stage(opts->writeout_stage,ce,pos,&state);+errs|=checkout_stage(opts->writeout_stage,ce,pos,&state,opts->overlay_mode);elseif(opts->merge)errs|=checkout_merged(pos,&state);pos=skip_same_name(ce,pos)-1;}}+remove_marked_cache_entries(&the_index,1);+remove_scheduled_dirs();errs|=finish_delayed_checkout(&state);if(write_locked_index(&the_index,&lock_file,COMMIT_LOCK))
@@ -542,6 +566,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*opts->show_progressonlyimpactsoutputsodoesn'trequireamerge*/+/*+*opts->overlay_modecannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1178,6 +1207,10 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");+if(!opts->overlay_mode)+die(_("'%s' cannot be used with switching branches"),+"--no-overlay");+if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),"--ours/--theirs");
@@ -1297,6 +1332,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)if((!!opts.new_branch+!!opts.new_branch_force+!!opts.new_orphan_branch)>1)die(_("-b, -B and --orphan are mutually exclusive"));+if(opts.overlay_mode==1&&opts.patch_mode)+die(_("-p and --overlay are mutually exclusive"));+/**Fromhereon,new_branchwillcontainthebranchtobecheckedout,*andnew_branch_forceandnew_orphan_branchwilltelluswhichoneof
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:12
Add a new --cached option to git checkout, which works only on the
index, but not the working tree, similar to what 'git reset <tree-ish>
-- <pathspec>... does. Indeed the tests are adapted from the 'git
reset' tests.
In the longer term the idea is to potentially deprecate 'git reset
<tree-ish> -- <pathspec>...', so the 'git reset' command becomes only
about re-pointing the HEAD, and not also about copying entries from
<tree-ish> to the index.
Note that 'git checkout' by default works in overlay mode, meaning
files that match the pathspec that don't exist in <tree-ish>, but
exist in the index would not be removed. 'git checkout --no-overlay
--cached' can be used to get the same behaviour as 'git reset
<tree-ish> -- <pathspec>'.
One thing this patch doesn't currently deal with is conflicts.
Currently 'git checkout --{ours,theirs} -- <file-with-conflicts>'
doesn't do anything with the index, so the --cached option just
mirrors that behaviour. But given it doesn't even deal with
conflicts, the '--cached' option doesn't make much sense when no
<tree-ish> is given. As it operates only on the index, it's always a
no-op if no tree-ish is given.
Signed-off-by: Thomas Gummerer <redacted>
---
Maybe we can just disallow --cached without <tree-ish> given for now,
and possibly later allow it with some different behaviour for
conflicts, not sure what the best way forward here is. We can also
just make it update the index as appropriate, and have it behave
different than 'git checkout' curerntly does when handling conflicts?
builtin/checkout.c | 26 ++++++++--
t/t2016-checkout-patch.sh | 8 +++
t/t2026-checkout-cached.sh | 103 +++++++++++++++++++++++++++++++++++++
t/t9902-completion.sh | 1 +
4 files changed, 135 insertions(+), 3 deletions(-)
create mode 100755 t/t2026-checkout-cached.sh
@@ -288,6 +289,10 @@ static int checkout_paths(const struct checkout_opts *opts,die(_("Cannot update paths and switch to branch '%s' at the same time."),opts->new_branch);+if(opts->patch_mode&&opts->cached)+returnrun_add_interactive(revision,"--patch=reset",+&opts->pathspec);+if(opts->patch_mode)returnrun_add_interactive(revision,"--patch=checkout",&opts->pathspec);
@@ -319,7 +324,9 @@ static int checkout_paths(const struct checkout_opts *opts,*thecurrentindex,whichmeansthatitshould*beremoved.*/-ce->ce_flags|=CE_MATCHED|CE_REMOVE|CE_WT_REMOVE;+ce->ce_flags|=CE_MATCHED|CE_REMOVE;+if(!opts->cached)+ce->ce_flags|=CE_WT_REMOVE;continue;}else{/*
@@ -392,6 +399,9 @@ static int checkout_paths(const struct checkout_opts *opts,for(pos=0;pos<active_nr;pos++){structcache_entry*ce=active_cache[pos];if(ce->ce_flags&CE_MATCHED){+if(opts->cached){+continue;+}if(!ce_stage(ce)){errs|=checkout_entry(ce,&state,NULL);continue;
@@ -571,6 +581,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*nottestedhere*/+/*+*opts->cachedcannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1207,9 +1222,13 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");-if(!opts->overlay_mode)+if(opts->overlay_mode!=-1)+die(_("'%s' cannot be used with switching branches"),+"--overlay/--no-overlay");++if(opts->cached)die(_("'%s' cannot be used with switching branches"),-"--no-overlay");+"--cached");if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),
@@ -1300,6 +1319,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)PARSE_OPT_OPTARG,option_parse_recurse_submodules_worktree_updater},OPT_BOOL(0,"progress",&opts.show_progress,N_("force progress reporting")),OPT_BOOL(0,"overlay",&opts.overlay_mode,N_("use overlay mode")),+OPT_BOOL(0,"cached",&opts.cached,N_("work on the index only")),OPT_END(),};
@@ -0,0 +1,103 @@+#!/bin/sh++test_description='checkout --cached <pathspec>'++../test-lib.sh++test_expect_success'checkout --cached <pathspec>''+echo1>file1&&+echo2>file2&&+gitaddfile1file2&&+test_tick&&+gitcommit-mfiles&&+gitrmfile2&&+echo3>file3&&+echo4>file1&&+gitaddfile1file3&&+gitcheckout--cachedHEAD--file1file2&&+test_must_failgitdiff--quiet&&++cat>expect<<-\EOF&&+diff--gita/file1b/file1+indexd00491f..b8626c4100644+---a/file1++++b/file1+@@-1+1@@+-1++4+diff--gita/file2b/file2+deletedfilemode100644+index0cfbf08..0000000+---a/file2++++/dev/null+@@-1+0,0@@+-2+EOF+gitdiff>actual&&+test_cmpexpectactual&&++cat>expect<<-\EOF&&+diff--gita/file3b/file3+newfilemode100644+index0000000..00750ed+---/dev/null++++b/file3+@@-0,0+1@@++3+EOF+gitdiff--cached>actual&&+test_cmpexpectactual+'++test_expect_success'checking out an unmodified path is a no-op''+gitreset--hard&&+gitcheckout--cachedHEAD--file1&&+gitdiff-files--exit-code&&+gitdiff-index--cached--exit-codeHEAD+'++test_expect_success'checking out specific path that is unmerged''+test_commitfile3file3&&+gitrm--cachedfile2&&+echo1234>file2&&+F1=$(gitrev-parseHEAD:file1)&&+F2=$(gitrev-parseHEAD:file2)&&+F3=$(gitrev-parseHEAD:file3)&&+{+echo"100644 $F1 1 file2"&&+echo"100644 $F2 2 file2"&&+echo"100644 $F3 3 file2"+}|gitupdate-index--index-info&&+gitls-files-u&&+gitcheckout--cachedHEADfile2&&+test_must_failgitdiff--quiet&&+gitdiff-index--exit-code--cachedHEAD+'++test_expect_success'--cached without --no-overlay does not remove entry from index''+test_must_failgitcheckout--cachedHEAD^file3&&+gitls-files--error-unmatch--file3+'++test_expect_success'file is removed from the index with --no-overlay''+gitcheckout--cached--no-overlayHEAD^file3&&+test_path_is_filefile3&&+test_must_failgitls-files--error-unmatch--file3+'++test_expect_success'test checkout --cached --no-overlay at given paths''+mkdirsub&&+>sub/file1&&+>sub/file2&&+gitupdate-index--addsub/file1sub/file2&&+T=$(gitwrite-tree)&&+gitcheckout--cached--no-overlayHEADsub/file2&&+test_must_failgitdiff--quiet&&+U=$(gitwrite-tree)&&+echo"$T"&&+echo"$U"&&+test_must_failgitdiff-index--cached--exit-code"$T"&&+test"$T"!="$U"+'++test_done
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:12
Currently when 'git checkout -- <pathspec>...' is invoked with
multiple pathspecs, where one or more of the pathspecs don't match
anything, checkout errors out.
This can be inconvenient in some cases, such as when using git
checkout from a script. Introduce a new --ignore-unmatched option,
which which allows us to ignore a non-matching pathspec instead of
erroring out.
In a subsequent commit we're going to start using 'git checkout' in
'git stash' and are going to make use of this feature.
Signed-off-by: Thomas Gummerer <redacted>
---
builtin/checkout.c | 10 +++++++++-
t/t2022-checkout-paths.sh | 9 +++++++++
t/t9902-completion.sh | 1 +
3 files changed, 19 insertions(+), 1 deletion(-)
@@ -358,7 +359,8 @@ static int checkout_paths(const struct checkout_opts *opts,ce->ce_flags|=CE_MATCHED;}-if(report_path_error(ps_matched,&opts->pathspec,opts->prefix)){+if(!opts->ignore_unmatched&&+report_path_error(ps_matched,&opts->pathspec,opts->prefix)){free(ps_matched);return1;}
@@ -586,6 +588,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*nottestedhere*/+/*+*opts->ignore_unmatchedcannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1320,6 +1327,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)OPT_BOOL(0,"progress",&opts.show_progress,N_("force progress reporting")),OPT_BOOL(0,"overlay",&opts.overlay_mode,N_("use overlay mode")),OPT_BOOL(0,"cached",&opts.cached,N_("work on the index only")),+OPT_BOOL(0,"ignore-unmatched",&opts.ignore_unmatched,N_("don't error on unmatched pathspecs")),OPT_END(),};
From: Thomas Gummerer <hidden> Date: 2018-12-09 20:05:14
Now that we have 'git checkout --no-overlay', we can use it in git
stash, making the codepaths for 'git stash push' with and without
pathspec more similar, and thus easier to follow.
Signed-off-by: Thomas Gummerer <redacted>
---
As mentioned in the cover letter, not sure if we want to apply this
now. There are two reasons I did this:
- Showing the new functionality of git checkout
- Increased test coverage, as we are running the new code with all git
stash tests for free, which helped look at some cases that I was
missing initially.
git-stash.sh | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
On Sun, Dec 9, 2018 at 9:04 PM Thomas Gummerer [off-list ref] wrote:
The 'git worktree' command used to be just another mode in 'git
checkout', namely 'git checkout --to'. When the tests for the latter
were retrofitted for the former, the test name was adjusted, but the
test number was kept, even though the test is testing a different
command now. t/README states: "Second digit tells the particular
command we are testing.", so 'git worktree' should have a separate
number just for itself.
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
t/{t2025-worktree-add.sh => t2400-worktree-add.sh} | 0
t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} | 0
t/{t2027-worktree-list.sh => t2402-worktree-list.sh} | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
Heh.. I did the same thing (in my unsent switch-branch/restore-files
series) and even used the same 24xx range :D You probably want to move
t2028 and t2029 too (not sure if they have landed on 'master')
--
Duy
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted hunk
Factor out the 'unlink_entry()' function from unpack-trees.c to
entry.c. It will be used in other places as well in subsequent
steps.
As it's no longer a static function, also move the documentation to
the header file to make it more discoverable.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 5 +++++
entry.c | 15 +++++++++++++++
unpack-trees.c | 19 -------------------
3 files changed, 20 insertions(+), 19 deletions(-)
I'm torn. We try to remove 'extern' but I can see you may want to add
it here to be consistent with others. And removing extern even from
functions from entry.c only would cause some conflicts.
I wonder if we should move the 'removal' variable in symlinks to
'struct checkout' to reduce another global variable. But I guess
that's the problem for another day. It's not the focus of this series.
--
Duy
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted hunk
'checkout_entry()' currently only supports creating new entries in the
working tree, but not deleting them. Add the ability to remove
entries at the same time if the entry is marked with the CE_WT_REMOVE
flag.
Currently this doesn't have any effect, as the CE_WT_REMOVE flag is
only used in unpack-tree, however we will make use of this in a
subsequent step in the series.
Signed-off-by: Thomas Gummerer <redacted>
---
entry.c | 7 +++++++
1 file changed, 7 insertions(+)
@@ -441,6 +441,13 @@ int checkout_entry(struct cache_entry *ce,staticstructstrbufpath=STRBUF_INIT;structstatst;+if(ce->ce_flags&CE_WT_REMOVE){+if(topath)+BUG("Can't remove entry to a path");+unlink_entry(ce);+return0;+}
This makes the path counting in nd/checkout-noisy less accurate. But
it's not your fault of course.
Junio, do you still want to merge that series down to 'next' or drop
it? If it will be merged down, I'll keep a note and fix it once this
one lands too.
+
if (topath)
return write_entry(ce, topath, state, 1);
--
2.20.0.405.gbc1bbc6f85
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
When marking cache entries for removal, and later removing them all at
once using 'remove_marked_cache_entries()', cache entries currently
have to be invalidated manually in the cache tree and in the untracked
cache.
Add an invalidate flag to the function. With the flag set, the
function will take care of invalidating the path in the cache tree and
in the untracked cache.
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
For the two current callsites, unpack-trees seems to do this
invalidation itself internally.
I'm still a bit scared of this invalidation business in unpack-trees.
The thing is, we handle two separate index_state there, src_index and
result and invalidation has to be done on the right one (because index
extensions are on src_index until the very end of unpack-trees;
invalidating on 'result' would be no-op and wrong).
remove_marked_cache_entries() seems to be called on 'result' while
invalidate_ce_path() is on src_index, hm....
I don't quite understand why we don't
need it in split-index mode though. I assume it's because the cache
tree in the main index would already have been invalidated? I didn't
have much time to dig, but couldn't produce any failures with it
either, so I assume not invalidating paths is the right thing to do
here.
Yeah I think it's because cache-tree and untracked cache are already
properly invalidated. This merge base thingy is done when we load the
index files up, not when we write them down. The "front" index may
record that a few paths in the base index are no longer valid and need
to be deleted. But untracked cache and cache-tree both should have
recorded that same info when these paths are marked for delete at
index write time.
--
Duy
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted hunk
@@ -302,15 +310,29 @@ static int checkout_paths(const struct checkout_opts *opts, ce->ce_flags &= ~CE_MATCHED; if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) continue;- if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))- /*- * "git checkout tree-ish -- path", but this entry- * is in the original index; it will not be checked- * out to the working tree and it does not matter- * if pathspec matched this entry. We will not do- * anything to this entry at all.- */- continue;+ if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) {+ if (!opts->overlay_mode &&+ ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {+ /*+ * "git checkout --no-overlay <tree-ish> -- path",+ * and the path is not in tree-ish, but is in+ * the current index, which means that it should+ * be removed.+ */+ ce->ce_flags |= CE_MATCHED | CE_REMOVE | CE_WT_REMOVE;+ continue;+ } else {
In non-overlay mode but when pathspec does not match, we come here too.
+ /*
+ * "git checkout tree-ish -- path", but this
+ * entry is in the original index; it will not
I think the missing key point in this comment block is "..is in the
original index _and it's not in tree-ish_". In non-overlay mode, if
pathspec does not match then it's safe to ignore too. But this logic
starts too get to complex and hurt my brain.
+ * be checked out to the working tree and it
+ * does not matter if pathspec matched this
+ * entry. We will not do anything to this entry
+ * at all.
+ */
+ continue;
+ }
+ }
/*
* Either this entry came from the tree-ish we are
* checking the paths out of, or we are checking out
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
Add a new --cached option to git checkout, which works only on the
index, but not the working tree, similar to what 'git reset <tree-ish>
-- <pathspec>... does.
Elijah wanted another mode (and I agree) that modifies worktree but
leaves the index alone. This is most useful (or least confusing) when
used with <tree-ish> and would be default in restore-files. I'm not
saying you have to implement it, but how do the new command line
options are designed to make sense?
I guess if --cached is "update index only" then --no-cached goes back
to the default "update both worktree and index" and we need a another
option for "worktree only"? Can we have one option with three possible
values (index-only, index-and-worktree, worktree-only) maybe?
--
Duy
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
Currently when 'git checkout -- <pathspec>...' is invoked with
multiple pathspecs, where one or more of the pathspecs don't match
anything, checkout errors out.
This can be inconvenient in some cases, such as when using git
checkout from a script.
Wait, should scripts go with read-tree, checkout-index or other
plumbing commands instead?
--
Duy
On Sun, Dec 9, 2018 at 9:04 PM Thomas Gummerer [off-list ref] wrote:
My hope is also that the no-overlay mode could become the new default
in the restore-files command Duy is currently working on.
I already wrote something like that in git-restore-files.txt even
though the implementation is not there :D
There will be a hell of conflicts when the two series enter 'pu' (or
even worse, when the third one to update worktree only appears) so I'm
going to send the switch-branch/restore-files series out but mostly to
gather comments and will rebase once the other series land.
(Alternatively I'll split my series and let the switch-branch part
land first, may be simpler)
--
Duy
On Sun, Dec 9, 2018 at 12:04 PM Thomas Gummerer [off-list ref] wrote:
Here's the series I mentioned a couple of times on the list already,
introducing a no-overlay mode in 'git checkout'. The inspiration for
this came from Junios message in [*1*].
Basically the idea is to also delete files when the match <pathspec>
in 'git checkout <tree-ish> -- <pathspec>' in the current tree, but
don't match <pathspec> in <tree-ish>. The rest of the cases are
already properly taken care of by 'git checkout'.
Yes, but I'd put it a little differently:
"""
Basically, the idea is when the user run "git checkout --no-overlay
<tree-ish> -- <pathspec>" that the given pathspecs should exactly
match <tree-ish> after the operation completes. This means that we
also want to delete files that match <pathspec> if those paths are not
found in <tree-ish>.
"""
...and maybe even toss in some comments about the fact that this is
the way git checkout should have always behaved, it just traditionally
hasn't. (You could also work in comments about how with this new mode
the user can run git diff afterward with the given commit-ish and
pathspecs and get back an empty diff, as expected, which wasn't true
before. But maybe I'm belaboring the point.)
The final step in the series is to actually make use of this in 'git
stash', which simplifies the code there a bit. I am however happy to
hold off on this step until the stash-in-C series is merged, so we
don't delay that further.
In addition to the no-overlay mode, we also add a --cached mode, which
works only on the index, thus similar to 'git reset <tree-ish> -- <pathspec>'.
If you're adding a --cached mode to make it only work on the index,
should there be a similar mode to allow it to only work on the working
tree? (I'm not as concerned with that here, but I really think the
new restore-files command by default should only operate on the
working tree, and then have options to affect the index either in
addition or instead of the working tree.)
Actually deprecating 'git reset <tree-ish> -- <pathspec>' should come
later, probably not before Duy's restore-files command lands, as 'git
checkout --no-overlay <tree-ish> -- <pathspec>' is a bit cumbersome to
type compared to 'git reset <tree-ish> -- <pathspec>'.
Makes sense.
My hope is also that the no-overlay mode could become the new default
in the restore-files command Duy is currently working on.
Absolutely, yes. I don't want another broken command. :-)
No documentation yet, as I wanted to get this out for review first.
I'm not familiar with most of the code I touched here, so there may
well be much better ways to implement some of this, that I wasn't able
to figure out. I'd be very happy with some feedback around that.
Another thing I'm not sure about is how to deal with conflicts. In
the cached mode this patch series is not dealing with it at all, as
'git checkout -- <pathspec>' when pathspec matches a file with
conflicts doesn't update the index. For the no-overlay mode, the file
is removed if the corresponding stage is not found in the index. I'm
however not sure this is the right thing to do in all cases?
Here's how I'd go about analyzing that...
If the user passes a <tree-ish>, then the answer about what to do is
pretty obvious; the <tree-ish> didn't have conflicts, so conflicted
paths in the index that match the pathspec should be overwritten with
whatever version of those paths existed in <tree-ish> (possibly
implying deletion of some paths).
Also, as you point out, --cached means only modify the index and not
the working tree; so if they specify both --cached and provide no
tree, then they've specified a no-op.
So it's only interesting when you have conflicts in the index and
specify --no-overlay without a <tree-ish> or --cached. This boils
down to "how do we update the working tree to match the index, when
the index is conflicted?" A couple points to consider:
* This is somewhat of an edge case
* In the normal case --no-overlay is only different from --overlay
behavior for directories; it'd be nice if that extended to all cases
* How does this command behave without a <tree-ish> when
--no-overlay is specified and a directory is given for a <pathspec>
and there aren't any conflicts? Are we being consistent with that
behavior?
However, I think it turns out that the answer is much simpler than all
that initial analysis or what you say you've implemented. Here's why:
If <pathspec> is a file which is present in both the working tree and
the index and it has conflicts, then "git checkout -- <pathspec>" will
currently throw an error:
$ git checkout -- subdir/counting
error: path 'subdir/counting' is unmerged
In fact, even if every entry in subdir/ is a path that is in both the
index and the working tree (so that --no-overlay and --overlay ought
to behave the same), if any one of the files in subdir is conflicted,
attempting to checkout the subdir will abort with this same error
message and no paths will be updated at all:
$ git checkout -- subdir
error: path 'subdir/counting' is unmerged
as such, the answer with what to do with --no-overlay mode is pretty
clear: if the <pathspec> matches _any_ path that is conflicted, simply
throw an error and abort the operation without making any changes at
all.
On Mon, Dec 10, 2018 at 7:50 AM Duy Nguyen [off-list ref] wrote:
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
Factor out the 'unlink_entry()' function from unpack-trees.c to
entry.c. It will be used in other places as well in subsequent
steps.
As it's no longer a static function, also move the documentation to
the header file to make it more discoverable.
I also started using unlink_entry() in another place in a local patch
series that I haven't submitted yet (and which I need to get back to
at some point). So this will help me too. :-)
I'm torn. We try to remove 'extern' but I can see you may want to add
it here to be consistent with others. And removing extern even from
functions from entry.c only would cause some conflicts.
I wonder if we should move the 'removal' variable in symlinks to
'struct checkout' to reduce another global variable. But I guess
that's the problem for another day. It's not the focus of this series.
"move the 'removal' variable in symlinks"? I'm having a really hard
time parsing that phrase and the sentence it's embedded in. Could you
reword for me Duy?
On Mon, Dec 10, 2018 at 6:23 PM Elijah Newren [off-list ref] wrote:
quoted
I wonder if we should move the 'removal' variable in symlinks to
'struct checkout' to reduce another global variable. But I guess
that's the problem for another day. It's not the focus of this series.
"move the 'removal' variable in symlinks"? I'm having a really hard
time parsing that phrase and the sentence it's embedded in. Could you
reword for me Duy?
Sorry s/in symlinks/&.c/. There's a global variable named 'removal' in
symlinks.c which is used by schedule_dir_for_removal() and this
function in turn is used by unlink_entry().
--
Duy
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
quoted hunk
'checkout_entry()' currently only supports creating new entries in the
working tree, but not deleting them. Add the ability to remove
entries at the same time if the entry is marked with the CE_WT_REMOVE
flag.
Currently this doesn't have any effect, as the CE_WT_REMOVE flag is
only used in unpack-tree, however we will make use of this in a
subsequent step in the series.
Signed-off-by: Thomas Gummerer <redacted>
---
entry.c | 7 +++++++
1 file changed, 7 insertions(+)
@@ -441,6 +441,13 @@ int checkout_entry(struct cache_entry *ce,staticstructstrbufpath=STRBUF_INIT;structstatst;+if(ce->ce_flags&CE_WT_REMOVE){+if(topath)+BUG("Can't remove entry to a path");
Minor nit: This error message is kinda hard to parse, for someone not
that familiar with all the *_entry functions, like myself. Maybe add
a comment before this line:
/* No content and thus no path to create, so we have no pathname
to return */
or reword the error slightly? Or maybe it's fine and I was just
confused from lack of code familiarity, but I'll throw it out there
since I stumbled on it a bit.
On Mon, Dec 10, 2018 at 8:09 AM Duy Nguyen [off-list ref] wrote:
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
When marking cache entries for removal, and later removing them all at
once using 'remove_marked_cache_entries()', cache entries currently
have to be invalidated manually in the cache tree and in the untracked
cache.
Add an invalidate flag to the function. With the flag set, the
function will take care of invalidating the path in the cache tree and
in the untracked cache.
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
For the two current callsites, unpack-trees seems to do this
invalidation itself internally.
I'm still a bit scared of this invalidation business in unpack-trees.
The thing is, we handle two separate index_state there, src_index and
result and invalidation has to be done on the right one (because index
extensions are on src_index until the very end of unpack-trees;
invalidating on 'result' would be no-op and wrong).
remove_marked_cache_entries() seems to be called on 'result' while
invalidate_ce_path() is on src_index, hm....
Is Thomas avoiding problems here simply because merge is the only
caller of unpack_trees with src_index != dst_index? Or does src_index
== dst_index for checkout not actually help?
If that does help with the checkout case, then allow me to find a
different way to muddy the waters... I think I might want to make use
of this function in the merge machinery at some point, so I either
need to figure out how to convince you to verify if all this cache
tree invalidation stuff is sane, or somehow figure out all the
cache_tree stuff stuff myself so I can figure out what is right here.
:-)
quoted
I don't quite understand why we don't
need it in split-index mode though. I assume it's because the cache
tree in the main index would already have been invalidated? I didn't
have much time to dig, but couldn't produce any failures with it
either, so I assume not invalidating paths is the right thing to do
here.
Yeah I think it's because cache-tree and untracked cache are already
properly invalidated. This merge base thingy is done when we load the
index files up, not when we write them down. The "front" index may
record that a few paths in the base index are no longer valid and need
to be deleted. But untracked cache and cache-tree both should have
recorded that same info when these paths are marked for delete at
index write time.
On Mon, Dec 10, 2018 at 7:09 PM Elijah Newren [off-list ref] wrote:
quoted
quoted
For the two current callsites, unpack-trees seems to do this
invalidation itself internally.
I'm still a bit scared of this invalidation business in unpack-trees.
The thing is, we handle two separate index_state there, src_index and
result and invalidation has to be done on the right one (because index
extensions are on src_index until the very end of unpack-trees;
invalidating on 'result' would be no-op and wrong).
remove_marked_cache_entries() seems to be called on 'result' while
invalidate_ce_path() is on src_index, hm....
Is Thomas avoiding problems here simply because merge is the only
caller of unpack_trees with src_index != dst_index? Or does src_index
== dst_index for checkout not actually help?
I think it would not help. 'result' is a temporary index where we copy
things to (and it does not have anything from the beginning). If you
invalidate stuff in there, you invalidate nothing, regardless whether
dst_index == src_index.
If that does help with the checkout case, then allow me to find a
different way to muddy the waters... I think I might want to make use
of this function in the merge machinery at some point, so I either
need to figure out how to convince you to verify if all this cache
tree invalidation stuff is sane, or somehow figure out all the
cache_tree stuff stuff myself so I can figure out what is right here.
:-)
I'm not the unpack-trees man (I think that would still be Junio). And
I'm not saying it's sane either. I think it's just some leftover
things since Linus split "the index" in unpack-tree operation to
'src', 'result' and 'dst' many years ago and nobody was brave enough
to clean it up (then I piled on with untracked cache and split index,
but I did not see it clearly either). That person could be you ;-)
--
Duy
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.
Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.
Note that 'git checkout -p <tree-ish> -- [<pathspec>]' already works
this way, so no changes are needed for the patch mode. We disallow
'git checkout --overlay -p' to avoid confusing users who would expect
to be able to force overlay mode in 'git checkout -p' this way.
Whoa...that's interesting. To me, that argues even further that the
traditional checkout behavior was wrong all along and the choice of
--overlay vs. --no-overlay in the original implementation was a total
oversight. I'm really tempted to say that --no-overlay should just be
the default in checkout too...but maybe that's too high a hill to
climb, at least for now.
Making --overlap and -p incompatible is a reasonable first step. But
you should probably add a comment to the -p option documentation that
it implies --no-overlay.
@@ -132,7 +133,8 @@ static int skip_same_name(const struct cache_entry *ce, int pos)returnpos;}-staticintcheck_stage(intstage,conststructcache_entry*ce,intpos)+staticintcheck_stage(intstage,conststructcache_entry*ce,intpos,+intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -140,6 +142,8 @@ static int check_stage(int stage, const struct cache_entry *ce, int pos)return0;pos++;}+if(!overlay_mode)+return0;if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -165,7 +169,7 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)}staticintcheckout_stage(intstage,conststructcache_entry*ce,intpos,-conststructcheckout*state)+conststructcheckout*state,intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -173,6 +177,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,returncheckout_entry(active_cache[pos],state,NULL);pos++;}+if(!overlay_mode){+unlink_entry(ce);+return0;+}if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -348,7 +370,7 @@ static int checkout_paths(const struct checkout_opts *opts,if(opts->force){warning(_("path '%s' is unmerged"),ce->name);}elseif(opts->writeout_stage){-errs|=check_stage(opts->writeout_stage,ce,pos);+errs|=check_stage(opts->writeout_stage,ce,pos,opts->overlay_mode);}elseif(opts->merge){errs|=check_stages((1<<2)|(1<<3),ce,pos);}else{
@@ -375,12 +397,14 @@ static int checkout_paths(const struct checkout_opts *opts,continue;}if(opts->writeout_stage)-errs|=checkout_stage(opts->writeout_stage,ce,pos,&state);+errs|=checkout_stage(opts->writeout_stage,ce,pos,&state,opts->overlay_mode);elseif(opts->merge)errs|=checkout_merged(pos,&state);pos=skip_same_name(ce,pos)-1;}}+remove_marked_cache_entries(&the_index,1);+remove_scheduled_dirs();errs|=finish_delayed_checkout(&state);if(write_locked_index(&the_index,&lock_file,COMMIT_LOCK))
@@ -542,6 +566,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*opts->show_progressonlyimpactsoutputsodoesn'trequireamerge*/+/*+*opts->overlay_modecannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1178,6 +1207,10 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");+if(!opts->overlay_mode)+die(_("'%s' cannot be used with switching branches"),+"--no-overlay");+if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),"--ours/--theirs");
@@ -1297,6 +1332,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)if((!!opts.new_branch+!!opts.new_branch_force+!!opts.new_orphan_branch)>1)die(_("-b, -B and --orphan are mutually exclusive"));+if(opts.overlay_mode==1&&opts.patch_mode)+die(_("-p and --overlay are mutually exclusive"));+/**Fromhereon,new_branchwillcontainthebranchtobecheckedout,*andnew_branch_forceandnew_orphan_branchwilltelluswhichoneof
On Mon, Dec 10, 2018 at 10:19 AM Duy Nguyen [off-list ref] wrote:
On Mon, Dec 10, 2018 at 7:09 PM Elijah Newren [off-list ref] wrote:
quoted
quoted
quoted
For the two current callsites, unpack-trees seems to do this
invalidation itself internally.
I'm still a bit scared of this invalidation business in unpack-trees.
The thing is, we handle two separate index_state there, src_index and
result and invalidation has to be done on the right one (because index
extensions are on src_index until the very end of unpack-trees;
invalidating on 'result' would be no-op and wrong).
remove_marked_cache_entries() seems to be called on 'result' while
invalidate_ce_path() is on src_index, hm....
Is Thomas avoiding problems here simply because merge is the only
caller of unpack_trees with src_index != dst_index? Or does src_index
== dst_index for checkout not actually help?
I think it would not help. 'result' is a temporary index where we copy
things to (and it does not have anything from the beginning). If you
invalidate stuff in there, you invalidate nothing, regardless whether
dst_index == src_index.
quoted
If that does help with the checkout case, then allow me to find a
different way to muddy the waters... I think I might want to make use
of this function in the merge machinery at some point, so I either
need to figure out how to convince you to verify if all this cache
tree invalidation stuff is sane, or somehow figure out all the
cache_tree stuff stuff myself so I can figure out what is right here.
:-)
I'm not the unpack-trees man (I think that would still be Junio). And
I'm not saying it's sane either. I think it's just some leftover
things since Linus split "the index" in unpack-tree operation to
'src', 'result' and 'dst' many years ago and nobody was brave enough
to clean it up (then I piled on with untracked cache and split index,
but I did not see it clearly either). That person could be you ;-)
Hmm, might make a good New Year's resolution: Enter the abyss, find
out if one can return from it... or maybe I could just sanely run
away screaming. We'll see.
On Mon, Dec 10, 2018 at 7:25 PM Elijah Newren [off-list ref] wrote:
quoted
I'm not the unpack-trees man (I think that would still be Junio). And
I'm not saying it's sane either. I think it's just some leftover
things since Linus split "the index" in unpack-tree operation to
'src', 'result' and 'dst' many years ago and nobody was brave enough
to clean it up (then I piled on with untracked cache and split index,
but I did not see it clearly either). That person could be you ;-)
Hmm, might make a good New Year's resolution: Enter the abyss, find
out if one can return from it... or maybe I could just sanely run
away screaming. We'll see.
I'm getting off topic. But my new years resolution would be optimize
for the case where src_index == dst_index, which is somewhat ironic
because we used to do everything in the same index, but it was a messy
mess and had to be split up.
--
Duy
On Mon, Dec 10, 2018 at 6:18 PM Elijah Newren [off-list ref] wrote:
quoted
The final step in the series is to actually make use of this in 'git
stash', which simplifies the code there a bit. I am however happy to
hold off on this step until the stash-in-C series is merged, so we
don't delay that further.
In addition to the no-overlay mode, we also add a --cached mode, which
works only on the index, thus similar to 'git reset <tree-ish> -- <pathspec>'.
If you're adding a --cached mode to make it only work on the index,
should there be a similar mode to allow it to only work on the working
tree? (I'm not as concerned with that here, but I really think the
new restore-files command by default should only operate on the
working tree, and then have options to affect the index either in
addition or instead of the working tree.)
In the context of restore-files, --target=<worktree|index|both> is a
very good candidate because "restore-files --from=foo --target=index"
is almost like saying "restore files in the index from "foo"". For
checkout, probably not as good. But then we can have different option
names for the two commands. So if "git checkout" is going to never
have "update worktree only" mode, then --cached is a still good way to
go.
--
Duy
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
Add a new --cached option to git checkout, which works only on the
index, but not the working tree, similar to what 'git reset <tree-ish>
-- <pathspec>... does. Indeed the tests are adapted from the 'git
reset' tests.
In the longer term the idea is to potentially deprecate 'git reset
<tree-ish> -- <pathspec>...', so the 'git reset' command becomes only
about re-pointing the HEAD, and not also about copying entries from
<tree-ish> to the index.
Note that 'git checkout' by default works in overlay mode, meaning
files that match the pathspec that don't exist in <tree-ish>, but
exist in the index would not be removed. 'git checkout --no-overlay
--cached' can be used to get the same behaviour as 'git reset
<tree-ish> -- <pathspec>'.
I think this argues _even more_ that --no-overlay should be the
default. Your series is valuable even if we don't push on that, I'm
just being noisy about what I think would be an even better world.
Also, I don't think I've mentioned it yet, but I'm really excited
about this series and what you're doing. It's super cool. (Which I
expected when I saw the description of the desired behavior, but I'm
also liking and contemplating re-using some code...)
One thing this patch doesn't currently deal with is conflicts.
Currently 'git checkout --{ours,theirs} -- <file-with-conflicts>'
doesn't do anything with the index, so the --cached option just
mirrors that behaviour. But given it doesn't even deal with
conflicts, the '--cached' option doesn't make much sense when no
<tree-ish> is given. As it operates only on the index, it's always a
no-op if no tree-ish is given.
Signed-off-by: Thomas Gummerer <redacted>
---
Maybe we can just disallow --cached without <tree-ish> given for now,
and possibly later allow it with some different behaviour for
conflicts, not sure what the best way forward here is. We can also
just make it update the index as appropriate, and have it behave
different than 'git checkout' curerntly does when handling conflicts?
Huh?
"git checkout -- <path>"
means update <path> from the index, meaning the index is left alone
(it's the source) and only the working tree is touched.
When you add a flag named --cached to only update the index and not
the working tree, then the index becomes the sole destination.
Now we combine: no tree is specified means the index is the source of
the writing, and --cached being specified means the index is the sole
destination of the writing. Thus, you have a no-op. If the user
specifies --cached and no tree, you should immediately exit with a
message along the lines of "Nothing to do; no tree given and --cached
specified." The presence of conflicts seems completely irrelevant to
me here.
@@ -288,6 +289,10 @@ static int checkout_paths(const struct checkout_opts *opts,die(_("Cannot update paths and switch to branch '%s' at the same time."),opts->new_branch);+if(opts->patch_mode&&opts->cached)+returnrun_add_interactive(revision,"--patch=reset",+&opts->pathspec);+if(opts->patch_mode)returnrun_add_interactive(revision,"--patch=checkout",&opts->pathspec);
@@ -319,7 +324,9 @@ static int checkout_paths(const struct checkout_opts *opts,*thecurrentindex,whichmeansthatitshould*beremoved.*/-ce->ce_flags|=CE_MATCHED|CE_REMOVE|CE_WT_REMOVE;+ce->ce_flags|=CE_MATCHED|CE_REMOVE;+if(!opts->cached)+ce->ce_flags|=CE_WT_REMOVE;continue;}else{/*
@@ -392,6 +399,9 @@ static int checkout_paths(const struct checkout_opts *opts,for(pos=0;pos<active_nr;pos++){structcache_entry*ce=active_cache[pos];if(ce->ce_flags&CE_MATCHED){+if(opts->cached){+continue;+}if(!ce_stage(ce)){errs|=checkout_entry(ce,&state,NULL);continue;
@@ -571,6 +581,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*nottestedhere*/+/*+*opts->cachedcannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1207,9 +1222,13 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");-if(!opts->overlay_mode)+if(opts->overlay_mode!=-1)+die(_("'%s' cannot be used with switching branches"),+"--overlay/--no-overlay");++if(opts->cached)die(_("'%s' cannot be used with switching branches"),-"--no-overlay");+"--cached");if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),
@@ -1300,6 +1319,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)PARSE_OPT_OPTARG,option_parse_recurse_submodules_worktree_updater},OPT_BOOL(0,"progress",&opts.show_progress,N_("force progress reporting")),OPT_BOOL(0,"overlay",&opts.overlay_mode,N_("use overlay mode")),+OPT_BOOL(0,"cached",&opts.cached,N_("work on the index only")),OPT_END(),};
@@ -0,0 +1,103 @@+#!/bin/sh++test_description='checkout --cached <pathspec>'++../test-lib.sh++test_expect_success'checkout --cached <pathspec>''+echo1>file1&&+echo2>file2&&+gitaddfile1file2&&+test_tick&&+gitcommit-mfiles&&+gitrmfile2&&+echo3>file3&&+echo4>file1&&+gitaddfile1file3&&+gitcheckout--cachedHEAD--file1file2&&+test_must_failgitdiff--quiet&&++cat>expect<<-\EOF&&+diff--gita/file1b/file1+indexd00491f..b8626c4100644+---a/file1++++b/file1+@@-1+1@@+-1++4+diff--gita/file2b/file2+deletedfilemode100644+index0cfbf08..0000000+---a/file2++++/dev/null+@@-1+0,0@@+-2+EOF+gitdiff>actual&&+test_cmpexpectactual&&++cat>expect<<-\EOF&&+diff--gita/file3b/file3+newfilemode100644+index0000000..00750ed+---/dev/null++++b/file3+@@-0,0+1@@++3+EOF+gitdiff--cached>actual&&+test_cmpexpectactual+'++test_expect_success'checking out an unmodified path is a no-op''+gitreset--hard&&+gitcheckout--cachedHEAD--file1&&+gitdiff-files--exit-code&&+gitdiff-index--cached--exit-codeHEAD+'++test_expect_success'checking out specific path that is unmerged''+test_commitfile3file3&&+gitrm--cachedfile2&&+echo1234>file2&&+F1=$(gitrev-parseHEAD:file1)&&+F2=$(gitrev-parseHEAD:file2)&&+F3=$(gitrev-parseHEAD:file3)&&+{+echo"100644 $F1 1 file2"&&+echo"100644 $F2 2 file2"&&+echo"100644 $F3 3 file2"+}|gitupdate-index--index-info&&+gitls-files-u&&+gitcheckout--cachedHEADfile2&&+test_must_failgitdiff--quiet&&+gitdiff-index--exit-code--cachedHEAD+'++test_expect_success'--cached without --no-overlay does not remove entry from index''+test_must_failgitcheckout--cachedHEAD^file3&&+gitls-files--error-unmatch--file3+'++test_expect_success'file is removed from the index with --no-overlay''+gitcheckout--cached--no-overlayHEAD^file3&&+test_path_is_filefile3&&+test_must_failgitls-files--error-unmatch--file3+'++test_expect_success'test checkout --cached --no-overlay at given paths''+mkdirsub&&+>sub/file1&&+>sub/file2&&+gitupdate-index--addsub/file1sub/file2&&+T=$(gitwrite-tree)&&+gitcheckout--cached--no-overlayHEADsub/file2&&+test_must_failgitdiff--quiet&&+U=$(gitwrite-tree)&&
Do we need to worry at all about losing the exit status of write-tree
in either invocation? In particular, if the second one for U fails
somehow, we'd end up with $U being a blank string and we'd still
probably get "$T" != "$U" below.
You also had some rev-parse invocations hidden in a sub-shell in both
this patch and patch 5, but subsequent commands relied on non-empty
output out of those, so I figured those were fine. This one might be
too, but I thought I'd at least mention it.
On Mon, Dec 10, 2018 at 10:34 AM Duy Nguyen [off-list ref] wrote:
On Mon, Dec 10, 2018 at 7:25 PM Elijah Newren [off-list ref] wrote:
quoted
quoted
I'm not the unpack-trees man (I think that would still be Junio). And
I'm not saying it's sane either. I think it's just some leftover
things since Linus split "the index" in unpack-tree operation to
'src', 'result' and 'dst' many years ago and nobody was brave enough
to clean it up (then I piled on with untracked cache and split index,
but I did not see it clearly either). That person could be you ;-)
Hmm, might make a good New Year's resolution: Enter the abyss, find
out if one can return from it... or maybe I could just sanely run
away screaming. We'll see.
I'm getting off topic. But my new years resolution would be optimize
for the case where src_index == dst_index, which is somewhat ironic
because we used to do everything in the same index, but it was a messy
mess and had to be split up.
Ooh, that sounds cool too. I look forward to seeing it.
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
Currently when 'git checkout -- <pathspec>...' is invoked with
multiple pathspecs, where one or more of the pathspecs don't match
anything, checkout errors out.
This can be inconvenient in some cases, such as when using git
checkout from a script. Introduce a new --ignore-unmatched option,
which which allows us to ignore a non-matching pathspec instead of
erroring out.
In a subsequent commit we're going to start using 'git checkout' in
'git stash' and are going to make use of this feature.
This makes sense, but seems incomplete. But to explain it, I think
there's another bug I need to demonstrate first because it's related
on builds on it. First, the setup:
$ echo foo >subdir/newfile
$ git add subdir/newfile
$ echo bar >>subdir/newfile
$ git status
On branch A
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: subdir/newfile
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: subdir/newfile
Now, does it do what we expect?
$ git checkout HEAD -- subdir/newfile
error: pathspec 'subdir/newfile' did not match any file(s) known to git
This is the old overlay behavior; kinda lame, but you made no claims
about fixing the default behavior. What about with your new option?
$ git checkout --no-overlay HEAD -- subdir
$ git status
On branch A
nothing to commit, working tree clean
Yes, the feature seems to work as advertised. However, let's try
again with a different variant:
$ echo foo >subdir/newfile
$ git checkout --no-overlay HEAD -- subdir
$ git status
On branch A
Untracked files:
(use "git add <file>..." to include in what will be committed)
subdir/newfile
Why is the file ignored and left there? Also:
$ git checkout --no-overlay HEAD -- subdir/newfile
error: pathspec 'subdir/newfile' did not match any file(s) known to git
That seems wrong to me. The point of no-overlay is to make it match
HEAD, and while subdir/newfile doesn't exist in HEAD or the index it
does match in the working tree so the intent is clear. But let's say
that the user did go ahead and specify your new flag:
$ git checkout --no-overlay --ignore-unmatch HEAD -- subdir/newfile
$ git status
On branch A
Untracked files:
(use "git add <file>..." to include in what will be committed)
subdir/newfile
nothing added to commit but untracked files present (use "git add" to track)
So now it avoids erroring out when the user does more work than
necessary, but it still misses appropriately cleaning up the file.
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
quoted hunk
Now that we have 'git checkout --no-overlay', we can use it in git
stash, making the codepaths for 'git stash push' with and without
pathspec more similar, and thus easier to follow.
Signed-off-by: Thomas Gummerer <redacted>
---
As mentioned in the cover letter, not sure if we want to apply this
now. There are two reasons I did this:
- Showing the new functionality of git checkout
- Increased test coverage, as we are running the new code with all git
stash tests for free, which helped look at some cases that I was
missing initially.
git-stash.sh | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
From: Thomas Gummerer <hidden> Date: 2018-12-11 21:50:24
On 12/10, Duy Nguyen wrote:
On Sun, Dec 9, 2018 at 9:04 PM Thomas Gummerer [off-list ref] wrote:
quoted
The 'git worktree' command used to be just another mode in 'git
checkout', namely 'git checkout --to'. When the tests for the latter
were retrofitted for the former, the test name was adjusted, but the
test number was kept, even though the test is testing a different
command now. t/README states: "Second digit tells the particular
command we are testing.", so 'git worktree' should have a separate
number just for itself.
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
t/{t2025-worktree-add.sh => t2400-worktree-add.sh} | 0
t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} | 0
t/{t2027-worktree-list.sh => t2402-worktree-list.sh} | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
Heh.. I did the same thing (in my unsent switch-branch/restore-files
series) and even used the same 24xx range :D You probably want to move
t2028 and t2029 too (not sure if they have landed on 'master')
:) I unfortunately didn't have time to read the
switch-branch/restore-files series in detail, but good to know someone
thought the same way. I started this work before t2028 and t2029
landed on master, so I failed to notice them. But I'll rebase on
master and move these two tests as well, thanks for noticing.
From: Thomas Gummerer <hidden> Date: 2018-12-11 21:59:15
On 12/10, Elijah Newren wrote:
On Mon, Dec 10, 2018 at 8:09 AM Duy Nguyen [off-list ref] wrote:
quoted
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
When marking cache entries for removal, and later removing them all at
once using 'remove_marked_cache_entries()', cache entries currently
have to be invalidated manually in the cache tree and in the untracked
cache.
Add an invalidate flag to the function. With the flag set, the
function will take care of invalidating the path in the cache tree and
in the untracked cache.
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
For the two current callsites, unpack-trees seems to do this
invalidation itself internally.
I'm still a bit scared of this invalidation business in unpack-trees.
The thing is, we handle two separate index_state there, src_index and
result and invalidation has to be done on the right one (because index
extensions are on src_index until the very end of unpack-trees;
invalidating on 'result' would be no-op and wrong).
remove_marked_cache_entries() seems to be called on 'result' while
invalidate_ce_path() is on src_index, hm....
Is Thomas avoiding problems here simply because merge is the only
caller of unpack_trees with src_index != dst_index? Or does src_index
== dst_index for checkout not actually help?
I'm trying to avoid problems in this patch by keeping status quo, and
not changing the cache-tree invalidation in any way. 'git checkout --
<pathspec>' doesn't use unpack-trees, so I don't think I have to worry
about src_index vs. dst_index.
In what I was saying above I was merely trying to explain why we don't
need invalidate the cache-tree in the 'remove_marked_cache_entries()'
function.
If that does help with the checkout case, then allow me to find a
different way to muddy the waters... I think I might want to make use
of this function in the merge machinery at some point, so I either
need to figure out how to convince you to verify if all this cache
tree invalidation stuff is sane, or somehow figure out all the
cache_tree stuff stuff myself so I can figure out what is right here.
:-)
quoted
quoted
I don't quite understand why we don't
need it in split-index mode though. I assume it's because the cache
tree in the main index would already have been invalidated? I didn't
have much time to dig, but couldn't produce any failures with it
either, so I assume not invalidating paths is the right thing to do
here.
Yeah I think it's because cache-tree and untracked cache are already
properly invalidated. This merge base thingy is done when we load the
index files up, not when we write them down. The "front" index may
record that a few paths in the base index are no longer valid and need
to be deleted. But untracked cache and cache-tree both should have
recorded that same info when these paths are marked for delete at
index write time.
From: Thomas Gummerer <hidden> Date: 2018-12-11 22:00:29
On 12/10, Elijah Newren wrote:
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
'checkout_entry()' currently only supports creating new entries in the
working tree, but not deleting them. Add the ability to remove
entries at the same time if the entry is marked with the CE_WT_REMOVE
flag.
Currently this doesn't have any effect, as the CE_WT_REMOVE flag is
only used in unpack-tree, however we will make use of this in a
subsequent step in the series.
Signed-off-by: Thomas Gummerer <redacted>
---
entry.c | 7 +++++++
1 file changed, 7 insertions(+)
@@ -441,6 +441,13 @@ int checkout_entry(struct cache_entry *ce,staticstructstrbufpath=STRBUF_INIT;structstatst;+if(ce->ce_flags&CE_WT_REMOVE){+if(topath)+BUG("Can't remove entry to a path");
Minor nit: This error message is kinda hard to parse, for someone not
that familiar with all the *_entry functions, like myself. Maybe add
a comment before this line:
/* No content and thus no path to create, so we have no pathname
to return */
or reword the error slightly? Or maybe it's fine and I was just
confused from lack of code familiarity, but I'll throw it out there
since I stumbled on it a bit.
I'll try to make it more clear in the new round, thanks!
From: Thomas Gummerer <hidden> Date: 2018-12-11 22:18:31
On 12/10, Elijah Newren wrote:
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
Add a new --cached option to git checkout, which works only on the
index, but not the working tree, similar to what 'git reset <tree-ish>
-- <pathspec>... does. Indeed the tests are adapted from the 'git
reset' tests.
In the longer term the idea is to potentially deprecate 'git reset
<tree-ish> -- <pathspec>...', so the 'git reset' command becomes only
about re-pointing the HEAD, and not also about copying entries from
<tree-ish> to the index.
Note that 'git checkout' by default works in overlay mode, meaning
files that match the pathspec that don't exist in <tree-ish>, but
exist in the index would not be removed. 'git checkout --no-overlay
--cached' can be used to get the same behaviour as 'git reset
<tree-ish> -- <pathspec>'.
I think this argues _even more_ that --no-overlay should be the
default. Your series is valuable even if we don't push on that, I'm
just being noisy about what I think would be an even better world.
I think just having that mode in 'git restore-files' Duy is working on
may have to be enough for now.
Also, I don't think I've mentioned it yet, but I'm really excited
about this series and what you're doing. It's super cool. (Which I
expected when I saw the description of the desired behavior, but I'm
also liking and contemplating re-using some code...)
Thanks :)
quoted
One thing this patch doesn't currently deal with is conflicts.
Currently 'git checkout --{ours,theirs} -- <file-with-conflicts>'
doesn't do anything with the index, so the --cached option just
mirrors that behaviour. But given it doesn't even deal with
conflicts, the '--cached' option doesn't make much sense when no
<tree-ish> is given. As it operates only on the index, it's always a
no-op if no tree-ish is given.
Signed-off-by: Thomas Gummerer <redacted>
---
Maybe we can just disallow --cached without <tree-ish> given for now,
and possibly later allow it with some different behaviour for
conflicts, not sure what the best way forward here is. We can also
just make it update the index as appropriate, and have it behave
different than 'git checkout' curerntly does when handling conflicts?
Huh?
"git checkout -- <path>"
means update <path> from the index, meaning the index is left alone
(it's the source) and only the working tree is touched.
When you add a flag named --cached to only update the index and not
the working tree, then the index becomes the sole destination.
Now we combine: no tree is specified means the index is the source of
the writing, and --cached being specified means the index is the sole
destination of the writing. Thus, you have a no-op. If the user
specifies --cached and no tree, you should immediately exit with a
message along the lines of "Nothing to do; no tree given and --cached
specified." The presence of conflicts seems completely irrelevant to
me here.
Ah yeah you're right, thanks for a sanity check. The command I was
most worried about was 'git checkout --cached --{ours,theirs} -- <pathspec>',
which I thought should update the index. But as we don't give any
tree-ish, I'm not sure anymore it should. Maybe just always exiting
with the message you mention above is the right thing to do.
@@ -288,6 +289,10 @@ static int checkout_paths(const struct checkout_opts *opts,die(_("Cannot update paths and switch to branch '%s' at the same time."),opts->new_branch);+if(opts->patch_mode&&opts->cached)+returnrun_add_interactive(revision,"--patch=reset",+&opts->pathspec);+if(opts->patch_mode)returnrun_add_interactive(revision,"--patch=checkout",&opts->pathspec);
@@ -319,7 +324,9 @@ static int checkout_paths(const struct checkout_opts *opts,*thecurrentindex,whichmeansthatitshould*beremoved.*/-ce->ce_flags|=CE_MATCHED|CE_REMOVE|CE_WT_REMOVE;+ce->ce_flags|=CE_MATCHED|CE_REMOVE;+if(!opts->cached)+ce->ce_flags|=CE_WT_REMOVE;continue;}else{/*
@@ -392,6 +399,9 @@ static int checkout_paths(const struct checkout_opts *opts,for(pos=0;pos<active_nr;pos++){structcache_entry*ce=active_cache[pos];if(ce->ce_flags&CE_MATCHED){+if(opts->cached){+continue;+}if(!ce_stage(ce)){errs|=checkout_entry(ce,&state,NULL);continue;
@@ -571,6 +581,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*nottestedhere*/+/*+*opts->cachedcannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1207,9 +1222,13 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");-if(!opts->overlay_mode)+if(opts->overlay_mode!=-1)+die(_("'%s' cannot be used with switching branches"),+"--overlay/--no-overlay");++if(opts->cached)die(_("'%s' cannot be used with switching branches"),-"--no-overlay");+"--cached");if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),
@@ -1300,6 +1319,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)PARSE_OPT_OPTARG,option_parse_recurse_submodules_worktree_updater},OPT_BOOL(0,"progress",&opts.show_progress,N_("force progress reporting")),OPT_BOOL(0,"overlay",&opts.overlay_mode,N_("use overlay mode")),+OPT_BOOL(0,"cached",&opts.cached,N_("work on the index only")),OPT_END(),};
@@ -0,0 +1,103 @@+#!/bin/sh++test_description='checkout --cached <pathspec>'++../test-lib.sh++test_expect_success'checkout --cached <pathspec>''+echo1>file1&&+echo2>file2&&+gitaddfile1file2&&+test_tick&&+gitcommit-mfiles&&+gitrmfile2&&+echo3>file3&&+echo4>file1&&+gitaddfile1file3&&+gitcheckout--cachedHEAD--file1file2&&+test_must_failgitdiff--quiet&&++cat>expect<<-\EOF&&+diff--gita/file1b/file1+indexd00491f..b8626c4100644+---a/file1++++b/file1+@@-1+1@@+-1++4+diff--gita/file2b/file2+deletedfilemode100644+index0cfbf08..0000000+---a/file2++++/dev/null+@@-1+0,0@@+-2+EOF+gitdiff>actual&&+test_cmpexpectactual&&++cat>expect<<-\EOF&&+diff--gita/file3b/file3+newfilemode100644+index0000000..00750ed+---/dev/null++++b/file3+@@-0,0+1@@++3+EOF+gitdiff--cached>actual&&+test_cmpexpectactual+'++test_expect_success'checking out an unmodified path is a no-op''+gitreset--hard&&+gitcheckout--cachedHEAD--file1&&+gitdiff-files--exit-code&&+gitdiff-index--cached--exit-codeHEAD+'++test_expect_success'checking out specific path that is unmerged''+test_commitfile3file3&&+gitrm--cachedfile2&&+echo1234>file2&&+F1=$(gitrev-parseHEAD:file1)&&+F2=$(gitrev-parseHEAD:file2)&&+F3=$(gitrev-parseHEAD:file3)&&+{+echo"100644 $F1 1 file2"&&+echo"100644 $F2 2 file2"&&+echo"100644 $F3 3 file2"+}|gitupdate-index--index-info&&+gitls-files-u&&+gitcheckout--cachedHEADfile2&&+test_must_failgitdiff--quiet&&+gitdiff-index--exit-code--cachedHEAD+'++test_expect_success'--cached without --no-overlay does not remove entry from index''+test_must_failgitcheckout--cachedHEAD^file3&&+gitls-files--error-unmatch--file3+'++test_expect_success'file is removed from the index with --no-overlay''+gitcheckout--cached--no-overlayHEAD^file3&&+test_path_is_filefile3&&+test_must_failgitls-files--error-unmatch--file3+'++test_expect_success'test checkout --cached --no-overlay at given paths''+mkdirsub&&+>sub/file1&&+>sub/file2&&+gitupdate-index--addsub/file1sub/file2&&+T=$(gitwrite-tree)&&+gitcheckout--cached--no-overlayHEADsub/file2&&+test_must_failgitdiff--quiet&&+U=$(gitwrite-tree)&&
Do we need to worry at all about losing the exit status of write-tree
in either invocation? In particular, if the second one for U fails
somehow, we'd end up with $U being a blank string and we'd still
probably get "$T" != "$U" below.
Hmm this seems to be a fairly common pattern in our test suite:
$ git grep -F '$(git write-tree)' t/* | wc -l
112
But maybe it's just something we used to do, but should move away
from. Just writing the output to a file shouldn't be much harder
either, I'll do that in the next iteration.
You also had some rev-parse invocations hidden in a sub-shell in both
this patch and patch 5, but subsequent commands relied on non-empty
output out of those, so I figured those were fine. This one might be
too, but I thought I'd at least mention it.
From: Thomas Gummerer <hidden> Date: 2018-12-11 22:23:19
On 12/10, Duy Nguyen wrote:
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
Currently when 'git checkout -- <pathspec>...' is invoked with
multiple pathspecs, where one or more of the pathspecs don't match
anything, checkout errors out.
This can be inconvenient in some cases, such as when using git
checkout from a script.
Wait, should scripts go with read-tree, checkout-index or other
plumbing commands instead?
Possibly. As mentioned in an other email, we do seem to have some
scripts in git.git that are using 'git checkout' already, but they are
using it in the checkout branch mode, rather than the checkout paths
mode that I would like to use it in git-stash.
But with the rewrite of 'git stash' in C, maybe this step is moot
anyway, and we can just call the checkout_paths function internally
without using the run_command API at all. We could then have an
internal mode for ignoring unmatched pathspecs that we wouldn't need
to expose to users.
From: Thomas Gummerer <hidden> Date: 2018-12-11 22:36:48
On 12/10, Elijah Newren wrote:
On Sun, Dec 9, 2018 at 12:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
Currently when 'git checkout -- <pathspec>...' is invoked with
multiple pathspecs, where one or more of the pathspecs don't match
anything, checkout errors out.
This can be inconvenient in some cases, such as when using git
checkout from a script. Introduce a new --ignore-unmatched option,
which which allows us to ignore a non-matching pathspec instead of
erroring out.
In a subsequent commit we're going to start using 'git checkout' in
'git stash' and are going to make use of this feature.
This makes sense, but seems incomplete. But to explain it, I think
there's another bug I need to demonstrate first because it's related
on builds on it. First, the setup:
$ echo foo >subdir/newfile
$ git add subdir/newfile
$ echo bar >>subdir/newfile
$ git status
On branch A
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: subdir/newfile
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: subdir/newfile
Now, does it do what we expect?
$ git checkout HEAD -- subdir/newfile
error: pathspec 'subdir/newfile' did not match any file(s) known to git
This is the old overlay behavior; kinda lame, but you made no claims
about fixing the default behavior. What about with your new option?
$ git checkout --no-overlay HEAD -- subdir
$ git status
On branch A
nothing to commit, working tree clean
Yes, the feature seems to work as advertised. However, let's try
again with a different variant:
$ echo foo >subdir/newfile
$ git checkout --no-overlay HEAD -- subdir
$ git status
On branch A
Untracked files:
(use "git add <file>..." to include in what will be committed)
subdir/newfile
Why is the file ignored and left there? Also:
$ git checkout --no-overlay HEAD -- subdir/newfile
error: pathspec 'subdir/newfile' did not match any file(s) known to git
That seems wrong to me.
Ah interesting, this is a case I didn't consider. I'm a bit torn on
this one. My intention for the no overlay mode was that it would work
similar to what I'd expect 'git reset --hard -- <pathspec>' to work if
it existed, which means not removing untracked files if they exist.
While I think in the example you have above removing subdir/newfile
may be the right behaviour I'm not so sure in the case of 'git
checkout --no-overlay HEAD -- .' or ''git checkout --no-overlay HEAD
-- t/*' for example. I don't think that should remove all untracked
files in the repository or in the t/ directory. Removing untracked
files in that case would probably surprise users more than your case
above would.
I think it's okay to keep considering untracked files as special with
respect to how they are treated by 'git checkout --no-overlay'.
The point of no-overlay is to make it match
HEAD, and while subdir/newfile doesn't exist in HEAD or the index it
does match in the working tree so the intent is clear. But let's say
that the user did go ahead and specify your new flag:
$ git checkout --no-overlay --ignore-unmatch HEAD -- subdir/newfile
$ git status
On branch A
Untracked files:
(use "git add <file>..." to include in what will be committed)
subdir/newfile
nothing added to commit but untracked files present (use "git add" to track)
So now it avoids erroring out when the user does more work than
necessary, but it still misses appropriately cleaning up the file.
Yeah this is a good point, this could be more confusing to the user
than the previous case in my opinion. Maybe I'll just drop this patch
for now (and the next one, as it's better to hold of until stash in C
lands anyway), and then try to do all this in-core for 'git stash'.
From: Thomas Gummerer <hidden> Date: 2018-12-11 22:42:44
On 12/10, Duy Nguyen wrote:
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
@@ -302,15 +310,29 @@ static int checkout_paths(const struct checkout_opts *opts, ce->ce_flags &= ~CE_MATCHED; if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) continue;- if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))- /*- * "git checkout tree-ish -- path", but this entry- * is in the original index; it will not be checked- * out to the working tree and it does not matter- * if pathspec matched this entry. We will not do- * anything to this entry at all.- */- continue;+ if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) {+ if (!opts->overlay_mode &&+ ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {+ /*+ * "git checkout --no-overlay <tree-ish> -- path",+ * and the path is not in tree-ish, but is in+ * the current index, which means that it should+ * be removed.+ */+ ce->ce_flags |= CE_MATCHED | CE_REMOVE | CE_WT_REMOVE;+ continue;+ } else {
In non-overlay mode but when pathspec does not match, we come here too.
quoted
+ /*
+ * "git checkout tree-ish -- path", but this
+ * entry is in the original index; it will not
I think the missing key point in this comment block is "..is in the
original index _and it's not in tree-ish_". In non-overlay mode, if
pathspec does not match then it's safe to ignore too. But this logic
starts too get to complex and hurt my brain.
Yes, that would make it a bit easier to read. I took a while to try
and refactor this to make it easier to read, but couldn't come up with
anything much better unfortunately. I'll have another stab at
simplifying the logic a bit for v2.
quoted
+ * be checked out to the working tree and it
+ * does not matter if pathspec matched this
+ * entry. We will not do anything to this entry
+ * at all.
+ */
+ continue;
+ }
+ }
/*
* Either this entry came from the tree-ish we are
* checking the paths out of, or we are checking out
From: Thomas Gummerer <hidden> Date: 2018-12-11 22:52:47
On 12/10, Elijah Newren wrote:
On Sun, Dec 9, 2018 at 12:04 PM Thomas Gummerer [off-list ref] wrote:
quoted
Here's the series I mentioned a couple of times on the list already,
introducing a no-overlay mode in 'git checkout'. The inspiration for
this came from Junios message in [*1*].
Basically the idea is to also delete files when the match <pathspec>
in 'git checkout <tree-ish> -- <pathspec>' in the current tree, but
don't match <pathspec> in <tree-ish>. The rest of the cases are
already properly taken care of by 'git checkout'.
Yes, but I'd put it a little differently:
"""
Basically, the idea is when the user run "git checkout --no-overlay
<tree-ish> -- <pathspec>" that the given pathspecs should exactly
match <tree-ish> after the operation completes. This means that we
also want to delete files that match <pathspec> if those paths are not
found in <tree-ish>.
"""
...and maybe even toss in some comments about the fact that this is
the way git checkout should have always behaved, it just traditionally
hasn't. (You could also work in comments about how with this new mode
the user can run git diff afterward with the given commit-ish and
pathspecs and get back an empty diff, as expected, which wasn't true
before. But maybe I'm belaboring the point.)
quoted
The final step in the series is to actually make use of this in 'git
stash', which simplifies the code there a bit. I am however happy to
hold off on this step until the stash-in-C series is merged, so we
don't delay that further.
In addition to the no-overlay mode, we also add a --cached mode, which
works only on the index, thus similar to 'git reset <tree-ish> -- <pathspec>'.
If you're adding a --cached mode to make it only work on the index,
should there be a similar mode to allow it to only work on the working
tree? (I'm not as concerned with that here, but I really think the
new restore-files command by default should only operate on the
working tree, and then have options to affect the index either in
addition or instead of the working tree.)
Yeah I think that would be nice to have, though I'm not sure what we
would name it in 'git checkout'. Maybe just having it in 'git
restore-files' is good enough?
quoted
Actually deprecating 'git reset <tree-ish> -- <pathspec>' should come
later, probably not before Duy's restore-files command lands, as 'git
checkout --no-overlay <tree-ish> -- <pathspec>' is a bit cumbersome to
type compared to 'git reset <tree-ish> -- <pathspec>'.
Makes sense.
quoted
My hope is also that the no-overlay mode could become the new default
in the restore-files command Duy is currently working on.
Absolutely, yes. I don't want another broken command. :-)
quoted
No documentation yet, as I wanted to get this out for review first.
I'm not familiar with most of the code I touched here, so there may
well be much better ways to implement some of this, that I wasn't able
to figure out. I'd be very happy with some feedback around that.
Another thing I'm not sure about is how to deal with conflicts. In
the cached mode this patch series is not dealing with it at all, as
'git checkout -- <pathspec>' when pathspec matches a file with
conflicts doesn't update the index. For the no-overlay mode, the file
is removed if the corresponding stage is not found in the index. I'm
however not sure this is the right thing to do in all cases?
Here's how I'd go about analyzing that...
If the user passes a <tree-ish>, then the answer about what to do is
pretty obvious; the <tree-ish> didn't have conflicts, so conflicted
paths in the index that match the pathspec should be overwritten with
whatever version of those paths existed in <tree-ish> (possibly
implying deletion of some paths).
Also, as you point out, --cached means only modify the index and not
the working tree; so if they specify both --cached and provide no
tree, then they've specified a no-op.
So it's only interesting when you have conflicts in the index and
specify --no-overlay without a <tree-ish> or --cached. This boils
down to "how do we update the working tree to match the index, when
the index is conflicted?" A couple points to consider:
* This is somewhat of an edge case
* In the normal case --no-overlay is only different from --overlay
behavior for directories; it'd be nice if that extended to all cases
I'm not sure I follow what you mean here. How is --no-overlay
different from --overlay with respect to directories? It's only
different with respect to deletions, no?
* How does this command behave without a <tree-ish> when
--no-overlay is specified and a directory is given for a <pathspec>
and there aren't any conflicts? Are we being consistent with that
behavior?
However, I think it turns out that the answer is much simpler than all
that initial analysis or what you say you've implemented. Here's why:
If <pathspec> is a file which is present in both the working tree and
the index and it has conflicts, then "git checkout -- <pathspec>" will
currently throw an error:
I think what was missing from my original description, that actually
makes it slightly more interesting from what you describe below is the
'--ours' and '--theirs' flags in 'git checkout', with which one can
check out a version of the file in the working tree. This is where it
gets more interesting.
I think I got the right solution for that in patch 5, with deleting
the file if it's deleted in "their" version and we pass --theirs to
'git checkout', and analogous for --ours. I was just wondering if
there were any further edge cases that I can't think of right no.
$ git checkout -- subdir/counting
error: path 'subdir/counting' is unmerged
In fact, even if every entry in subdir/ is a path that is in both the
index and the working tree (so that --no-overlay and --overlay ought
to behave the same), if any one of the files in subdir is conflicted,
attempting to checkout the subdir will abort with this same error
message and no paths will be updated at all:
$ git checkout -- subdir
error: path 'subdir/counting' is unmerged
as such, the answer with what to do with --no-overlay mode is pretty
clear: if the <pathspec> matches _any_ path that is conflicted, simply
throw an error and abort the operation without making any changes at
all.
From: Eric Sunshine <hidden> Date: 2018-12-12 13:27:13
On Tue, Dec 11, 2018 at 4:50 PM Thomas Gummerer [off-list ref] wrote:
On 12/10, Duy Nguyen wrote:
quoted
On Sun, Dec 9, 2018 at 9:04 PM Thomas Gummerer [off-list ref] wrote:
quoted
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Heh.. I did the same thing (in my unsent switch-branch/restore-files
series) and even used the same 24xx range :D You probably want to move
t2028 and t2029 too (not sure if they have landed on 'master')
[...] good to know someone
thought the same way. I started this work before t2028 and t2029
landed on master, so I failed to notice them.
On Wed, Dec 12, 2018 at 2:27 PM Eric Sunshine [off-list ref] wrote:
On Tue, Dec 11, 2018 at 4:50 PM Thomas Gummerer [off-list ref] wrote:
quoted
On 12/10, Duy Nguyen wrote:
quoted
On Sun, Dec 9, 2018 at 9:04 PM Thomas Gummerer [off-list ref] wrote:
quoted
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Heh.. I did the same thing (in my unsent switch-branch/restore-files
series) and even used the same 24xx range :D You probably want to move
t2028 and t2029 too (not sure if they have landed on 'master')
[...] good to know someone
thought the same way. I started this work before t2028 and t2029
landed on master, so I failed to notice them.
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:36:49
On 12/10, Duy Nguyen wrote:
On Sun, Dec 9, 2018 at 9:05 PM Thomas Gummerer [off-list ref] wrote:
quoted
Factor out the 'unlink_entry()' function from unpack-trees.c to
entry.c. It will be used in other places as well in subsequent
steps.
As it's no longer a static function, also move the documentation to
the header file to make it more discoverable.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 5 +++++
entry.c | 15 +++++++++++++++
unpack-trees.c | 19 -------------------
3 files changed, 20 insertions(+), 19 deletions(-)
I'm torn. We try to remove 'extern' but I can see you may want to add
it here to be consistent with others. And removing extern even from
functions from entry.c only would cause some conflicts.
Yeah I felt like favoring consistency here would be better. Once your
path counting series and my series land, this may get quieter and we
can remove the 'extern' then?
I wonder if we should move the 'removal' variable in symlinks to
'struct checkout' to reduce another global variable. But I guess
that's the problem for another day. It's not the focus of this
series.
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:40
Previous round is at [off-list ref].
Thanks Junio, Duy and Elijah for your comments and suggestions on the
previous round.
This round drops the last three patches from the previous round,
namely introducing a "--cached" and a "--ignore-unmatched" option, and
using the new no-overlay mode in "git stash". The --ignore-unmatched
option may not be necessary, while using the new mode in 'git stash'
will be done once the stash-in-C topic landed.
Introducing a --cached and --worktree-only (as suggested by Elijah)
option can come in a future step, they are orthogonal to this topic.
Other changes from v1:
- Rebase onto the current master, so we can also move t2028 and t2029
to the t24xx range.
- Add a comment clarifying why using the CE_WT_REMOVE flag and topath
in checkout_entry is a bug.
- clarify a comment in checkout.c
- factor out the function to mark a cache entry as CE_MATCHED, and
have separate such functions for overlay mode and no-overlay mode.
This should hopefully make the logic a bit easier to follow.
- Adjust the commit message, justifying why we don't remove untracked
files even in the new no-overlay mode.
- add documentation for the new feature
- document that -p defaults to no overlay mode, and cannot be used
with overlay mode.
- add a config option checkout.overlayMode, so overlay mode can be
turned on by default.
Range-diff can be found after the diffstat.
Thomas Gummerer (8):
move worktree tests to t24*
entry: factor out unlink_entry function
entry: support CE_WT_REMOVE flag in checkout_entry
read-cache: add invalidate parameter to remove_marked_cache_entries
checkout: clarify comment
checkout: factor out mark_cache_entry_for_checkout function
checkout: introduce --{,no-}overlay option
checkout: introduce checkout.overlayMode config
Documentation/config/checkout.txt | 7 +
Documentation/git-checkout.txt | 10 ++
builtin/checkout.c | 133 +++++++++++++-----
cache.h | 7 +-
entry.c | 26 ++++
read-cache.c | 8 +-
split-index.c | 2 +-
t/t2025-checkout-no-overlay.sh | 57 ++++++++
...-worktree-add.sh => t2400-worktree-add.sh} | 0
...ktree-prune.sh => t2401-worktree-prune.sh} | 0
...orktree-list.sh => t2402-worktree-list.sh} | 0
...orktree-move.sh => t2403-worktree-move.sh} | 0
...ree-config.sh => t2404-worktree-config.sh} | 0
t/t9902-completion.sh | 1 +
unpack-trees.c | 21 +--
15 files changed, 213 insertions(+), 59 deletions(-)
create mode 100755 t/t2025-checkout-no-overlay.sh
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
rename t/{t2028-worktree-move.sh => t2403-worktree-move.sh} (100%)
rename t/{t2029-worktree-config.sh => t2404-worktree-config.sh} (100%)
1: 70bd75b202 ! 1: fa450cda7c move worktree tests to t24*
@@ -29,3 +29,13 @@
similarity index 100%
rename from t/t2027-worktree-list.sh
rename to t/t2402-worktree-list.sh
+
+ diff --git a/t/t2028-worktree-move.sh b/t/t2403-worktree-move.sh
+ similarity index 100%
+ rename from t/t2028-worktree-move.sh
+ rename to t/t2403-worktree-move.sh
+
+ diff --git a/t/t2029-worktree-config.sh b/t/t2404-worktree-config.sh
+ similarity index 100%
+ rename from t/t2029-worktree-config.sh
+ rename to t/t2404-worktree-config.sh
2: 0fd9be987d = 2: 9ada8d3484 entry: factor out unlink_entry function
3: 4d6112b112 ! 3: 41c0ea4047 entry: support CE_WT_REMOVE flag in checkout_entry
@@ -22,6 +22,10 @@
+ if (ce->ce_flags & CE_WT_REMOVE) {
+ if (topath)
++ /*
++ * No content and thus no path to create, so we have
++ * no pathname to return.
++ */
+ BUG("Can't remove entry to a path");
+ unlink_entry(ce);
+ return 0;
4: 6e9f68b8f1 ! 4: afccb0848d read-cache: add invalidate parameter to remove_marked_cache_entries
@@ -11,6 +11,10 @@
function will take care of invalidating the path in the cache tree and
in the untracked cache.
+ Note that the current callsites already do the invalidation properly
+ in other places, so we're just passing 0 from there to keep the status
+ quo.
+
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer [off-list ref]
-: ---------- > 5: 8a2b5efdad checkout: clarify comment
-: ---------- > 6: c405f20471 checkout: factor out mark_cache_entry_for_checkout function
5: 4a7670d34c ! 7: e5b18bcd02 checkout: introduce --{,no-}overlay option
@@ -17,8 +17,43 @@
'git checkout --overlay -p' to avoid confusing users who would expect
to be able to force overlay mode in 'git checkout -p' this way.
+ Untracked files are not affected by this change, so 'git checkout
+ --no-overlay HEAD -- untracked' will not remove untracked from the
+ working tree. This is so e.g. 'git checkout --no-overlay HEAD -- dir/'
+ doesn't delete all untracked files in dir/, but rather just resets the
+ state of files that are known to git.
+
+ Suggested-by: Junio C Hamano [off-list ref]
Signed-off-by: Thomas Gummerer [off-list ref]
+ diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
+ --- a/Documentation/git-checkout.txt
+ +++ b/Documentation/git-checkout.txt
+@@
+ This means that you can use `git checkout -p` to selectively discard
+ edits from your current working tree. See the ``Interactive Mode''
+ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
+++
++Note that this option uses the no overlay mode by default (see also
++-`--[no-]overlay`), and currently doesn't support overlay mode.
+
+ --ignore-other-worktrees::
+ `git checkout` refuses when the wanted ref is already checked
+@@
+ Just like linkgit:git-submodule[1], this will detach the
+ submodules HEAD.
+
++--[no-]overlay::
++ In the default overlay mode files `git checkout` never
++ removes files from the index or the working tree. When
++ specifying --no-overlay, files that appear in the index and
++ working tree, but not in <tree-ish> are removed, to make them
++ match <tree-ish> exactly.
++
+ <branch>::
+ Branch to checkout; if it refers to a branch (i.e., a name that,
+ when prepended with "refs/heads/", is a valid ref), then that
+
diff --git a/builtin/checkout.c b/builtin/checkout.c
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -70,44 +105,60 @@
return error(_("path '%s' does not have our version"), ce->name);
else
@@
- ce->ce_flags &= ~CE_MATCHED;
- if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
- continue;
-- if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
-- /*
-- * "git checkout tree-ish -- path", but this entry
-- * is in the original index; it will not be checked
-- * out to the working tree and it does not matter
-- * if pathspec matched this entry. We will not do
-- * anything to this entry at all.
-- */
-- continue;
-+ if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) {
-+ if (!opts->overlay_mode &&
-+ ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
-+ /*
-+ * "git checkout --no-overlay <tree-ish> -- path",
-+ * and the path is not in tree-ish, but is in
-+ * the current index, which means that it should
-+ * be removed.
-+ */
-+ ce->ce_flags |= CE_MATCHED | CE_REMOVE | CE_WT_REMOVE;
-+ continue;
-+ } else {
-+ /*
-+ * "git checkout tree-ish -- path", but this
-+ * entry is in the original index; it will not
-+ * be checked out to the working tree and it
-+ * does not matter if pathspec matched this
-+ * entry. We will not do anything to this entry
-+ * at all.
-+ */
-+ continue;
-+ }
-+ }
- /*
- * Either this entry came from the tree-ish we are
- * checking the paths out of, or we are checking out
+ return status;
+ }
+
+-static void mark_ce_for_checkout(struct cache_entry *ce,
+- char *ps_matched,
+- const struct checkout_opts *opts)
++static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
++ char *ps_matched,
++ const struct checkout_opts *opts)
+ {
+ ce->ce_flags &= ~CE_MATCHED;
+ if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
+@@
+ ce->ce_flags |= CE_MATCHED;
+ }
+
++static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
++ char *ps_matched,
++ const struct checkout_opts *opts)
++{
++ ce->ce_flags &= ~CE_MATCHED;
++ if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
++ return;
++ if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
++ ce->ce_flags |= CE_MATCHED;
++ if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
++ /*
++ * In overlay mode, but the path is not in
++ * tree-ish, which means we should remove it
++ * from the index and the working tree.
++ */
++ ce->ce_flags |= CE_REMOVE | CE_WT_REMOVE;
++ }
++}
++
+ static int checkout_paths(const struct checkout_opts *opts,
+ const char *revision)
+ {
+@@
+ * to be checked out.
+ */
+ for (pos = 0; pos < active_nr; pos++)
+- mark_ce_for_checkout(active_cache[pos], ps_matched, opts);
++ if (opts->overlay_mode)
++ mark_ce_for_checkout_overlay(active_cache[pos],
++ ps_matched,
++ opts);
++ else
++ mark_ce_for_checkout_no_overlay(active_cache[pos],
++ ps_matched,
++ opts);
+
+ if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
+ free(ps_matched);
@@
if (opts->force) {
warning(_("path '%s' is unmerged"), ce->name);
@@ -160,7 +211,7 @@
"checkout", "control recursive updating of submodules",
PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
-+ OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
++ OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
OPT_END(),
};
@@ -198,7 +249,7 @@
+ git commit --allow-empty -m "initial"
+'
+
-+test_expect_success 'checkout --no-overlay deletes files not in <tree>' '
++test_expect_success 'checkout --no-overlay deletes files not in <tree-ish>' '
+ >file &&
+ mkdir dir &&
+ >dir/file1 &&
@@ -218,7 +269,7 @@
+ test_i18ngrep "fatal: -p and --overlay are mutually exclusive" actual
+'
+
-+test_expect_success '--no-overlay --theirs with M/D conflict deletes file' '
++test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
+ test_commit file1 file1 &&
+ test_commit file2 file2 &&
+ git rm --cached file1 &&
6: 695b671675 < -: ---------- checkout: add --cached option
7: d0b5a356b2 < -: ---------- checkout: allow ignoring unmatched pathspec
8: 0a4565acc1 < -: ---------- stash: use git checkout --no-overlay
-: ---------- > 8: de24990d57 checkout: introduce checkout.overlayMode config
--
2.20.1.415.g653613c723
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:42
The 'git worktree' command used to be just another mode in 'git
checkout', namely 'git checkout --to'. When the tests for the latter
were retrofitted for the former, the test name was adjusted, but the
test number was kept, even though the test is testing a different
command now. t/README states: "Second digit tells the particular
command we are testing.", so 'git worktree' should have a separate
number just for itself.
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
t/{t2025-worktree-add.sh => t2400-worktree-add.sh} | 0
t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} | 0
t/{t2027-worktree-list.sh => t2402-worktree-list.sh} | 0
t/{t2028-worktree-move.sh => t2403-worktree-move.sh} | 0
t/{t2029-worktree-config.sh => t2404-worktree-config.sh} | 0
5 files changed, 0 insertions(+), 0 deletions(-)
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
rename t/{t2028-worktree-move.sh => t2403-worktree-move.sh} (100%)
rename t/{t2029-worktree-config.sh => t2404-worktree-config.sh} (100%)
diff --git a/t/t2025-worktree-add.sh b/t/t2400-worktree-add.shsimilarity index 100%rename from t/t2025-worktree-add.shrename to t/t2400-worktree-add.shdiff --git a/t/t2026-worktree-prune.sh b/t/t2401-worktree-prune.shsimilarity index 100%rename from t/t2026-worktree-prune.shrename to t/t2401-worktree-prune.shdiff --git a/t/t2027-worktree-list.sh b/t/t2402-worktree-list.shsimilarity index 100%rename from t/t2027-worktree-list.shrename to t/t2402-worktree-list.shdiff --git a/t/t2028-worktree-move.sh b/t/t2403-worktree-move.shsimilarity index 100%rename from t/t2028-worktree-move.shrename to t/t2403-worktree-move.shdiff --git a/t/t2029-worktree-config.sh b/t/t2404-worktree-config.shsimilarity index 100%rename from t/t2029-worktree-config.shrename to t/t2404-worktree-config.sh
--
2.20.1.415.g653613c723
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:43
Factor out the 'unlink_entry()' function from unpack-trees.c to
entry.c. It will be used in other places as well in subsequent
steps.
As it's no longer a static function, also move the documentation to
the header file to make it more discoverable.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 5 +++++
entry.c | 15 +++++++++++++++
unpack-trees.c | 19 -------------------
3 files changed, 20 insertions(+), 19 deletions(-)
@@ -508,3 +508,18 @@ int checkout_entry(struct cache_entry *ce,create_directories(path.buf,path.len,state);returnwrite_entry(ce,path.buf,state,0);}++voidunlink_entry(conststructcache_entry*ce)+{+conststructsubmodule*sub=submodule_from_ce(ce);+if(sub){+/* state.force is set at the caller. */+submodule_move_head(ce->name,"HEAD",NULL,+SUBMODULE_MOVE_HEAD_FORCE);+}+if(!check_leading_path(ce->name,ce_namelen(ce)))+return;+if(remove_or_warn(ce->ce_mode,ce->name))+return;+schedule_dir_for_removal(ce->name,ce_namelen(ce));+}
@@ -300,25 +300,6 @@ static void load_gitmodules_file(struct index_state *index,}}-/*-*Unlinkthelastcomponentandscheduletheleadingdirectoriesfor-*removal,suchthatemptydirectoriesgetremoved.-*/-staticvoidunlink_entry(conststructcache_entry*ce)-{-conststructsubmodule*sub=submodule_from_ce(ce);-if(sub){-/* state.force is set at the caller. */-submodule_move_head(ce->name,"HEAD",NULL,-SUBMODULE_MOVE_HEAD_FORCE);-}-if(!check_leading_path(ce->name,ce_namelen(ce)))-return;-if(remove_or_warn(ce->ce_mode,ce->name))-return;-schedule_dir_for_removal(ce->name,ce_namelen(ce));-}-staticstructprogress*get_progress(structunpack_trees_options*o){unsignedcnt=0,total=0;
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:46
'checkout_entry()' currently only supports creating new entries in the
working tree, but not deleting them. Add the ability to remove
entries at the same time if the entry is marked with the CE_WT_REMOVE
flag.
Currently this doesn't have any effect, as the CE_WT_REMOVE flag is
only used in unpack-tree, however we will make use of this in a
subsequent step in the series.
Signed-off-by: Thomas Gummerer <redacted>
---
entry.c | 11 +++++++++++
1 file changed, 11 insertions(+)
@@ -441,6 +441,17 @@ int checkout_entry(struct cache_entry *ce,staticstructstrbufpath=STRBUF_INIT;structstatst;+if(ce->ce_flags&CE_WT_REMOVE){+if(topath)+/*+*Nocontentandthusnopathtocreate,sowehave+*nopathnametoreturn.+*/+BUG("Can't remove entry to a path");+unlink_entry(ce);+return0;+}+if(topath)returnwrite_entry(ce,topath,state,1);
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:49
When marking cache entries for removal, and later removing them all at
once using 'remove_marked_cache_entries()', cache entries currently
have to be invalidated manually in the cache tree and in the untracked
cache.
Add an invalidate flag to the function. With the flag set, the
function will take care of invalidating the path in the cache tree and
in the untracked cache.
Note that the current callsites already do the invalidation properly
in other places, so we're just passing 0 from there to keep the status
quo.
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 2 +-
read-cache.c | 8 +++++++-
split-index.c | 2 +-
unpack-trees.c | 2 +-
4 files changed, 10 insertions(+), 4 deletions(-)
@@ -751,7 +751,7 @@ extern void rename_index_entry_at(struct index_state *, int pos, const char *new/* Remove entry, return true if there are more entries to go. */externintremove_index_entry_at(structindex_state*,intpos);-externvoidremove_marked_cache_entries(structindex_state*istate);+externvoidremove_marked_cache_entries(structindex_state*istate,intinvalidate);externintremove_file_from_index(structindex_state*,constchar*path);#define ADD_CACHE_VERBOSE 1#define ADD_CACHE_PRETEND 2
@@ -590,13 +590,19 @@ int remove_index_entry_at(struct index_state *istate, int pos)*CE_REMOVEissetince_flags.Thisismuchmoreeffectivethan*callingremove_index_entry_at()foreachentrytoberemoved.*/-voidremove_marked_cache_entries(structindex_state*istate)+voidremove_marked_cache_entries(structindex_state*istate,intinvalidate){structcache_entry**ce_array=istate->cache;unsignedinti,j;for(i=j=0;i<istate->cache_nr;i++){if(ce_array[i]->ce_flags&CE_REMOVE){+if(invalidate){+cache_tree_invalidate_path(istate,+ce_array[i]->name);+untracked_cache_remove_from_index(istate,+ce_array[i]->name);+}remove_name_hash(istate,ce_array[i]);save_or_free_index_entry(istate,ce_array[i]);}
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:50
The key point for the if statement is that read_tree_some did not
update the entry, because either it doesn't exist in tree-ish or
doesn't match the pathspec. Clarify that.
Suggested-by: Nguyễn Thái Ngọc Duy <redacted>
Signed-off-by: Thomas Gummerer <redacted>
---
builtin/checkout.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:52
Factor out the code that marks a cache entry as matched for checkout
into a separate function. We are going to introduce a new mode in
'git checkout' in a subsequent commit, that is going to have a
slightly different logic. This would make this code unnecessarily
complex.
Moving that complexity into separate functions will make the code in
the subsequent step easier to follow.
Signed-off-by: Thomas Gummerer <redacted>
---
builtin/checkout.c | 67 +++++++++++++++++++++++++---------------------
1 file changed, 36 insertions(+), 31 deletions(-)
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:54
Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.
Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.
Note that 'git checkout -p <tree-ish> -- [<pathspec>]' already works
this way, so no changes are needed for the patch mode. We disallow
'git checkout --overlay -p' to avoid confusing users who would expect
to be able to force overlay mode in 'git checkout -p' this way.
Untracked files are not affected by this change, so 'git checkout
--no-overlay HEAD -- untracked' will not remove untracked from the
working tree. This is so e.g. 'git checkout --no-overlay HEAD -- dir/'
doesn't delete all untracked files in dir/, but rather just resets the
state of files that are known to git.
Suggested-by: Junio C Hamano <redacted>
Signed-off-by: Thomas Gummerer <redacted>
---
Documentation/git-checkout.txt | 10 ++++++
builtin/checkout.c | 66 +++++++++++++++++++++++++++++-----
t/t2025-checkout-no-overlay.sh | 47 ++++++++++++++++++++++++
t/t9902-completion.sh | 1 +
4 files changed, 116 insertions(+), 8 deletions(-)
create mode 100755 t/t2025-checkout-no-overlay.sh
@@ -260,6 +260,9 @@ the conflicted merge in the specified paths. This means that you can use `git checkout -p` to selectively discard edits from your current working tree. See the ``Interactive Mode'' section of linkgit:git-add[1] to learn how to operate the `--patch` mode.+++Note that this option uses the no overlay mode by default (see also+-`--[no-]overlay`), and currently doesn't support overlay mode. --ignore-other-worktrees:: `git checkout` refuses when the wanted ref is already checked
@@ -276,6 +279,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Just like linkgit:git-submodule[1], this will detach the submodules HEAD.+--[no-]overlay::+ In the default overlay mode files `git checkout` never+ removes files from the index or the working tree. When+ specifying --no-overlay, files that appear in the index and+ working tree, but not in <tree-ish> are removed, to make them+ match <tree-ish> exactly.+ <branch>:: Branch to checkout; if it refers to a branch (i.e., a name that, when prepended with "refs/heads/", is a valid ref), then that
@@ -132,7 +133,8 @@ static int skip_same_name(const struct cache_entry *ce, int pos)returnpos;}-staticintcheck_stage(intstage,conststructcache_entry*ce,intpos)+staticintcheck_stage(intstage,conststructcache_entry*ce,intpos,+intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -140,6 +142,8 @@ static int check_stage(int stage, const struct cache_entry *ce, int pos)return0;pos++;}+if(!overlay_mode)+return0;if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -165,7 +169,7 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)}staticintcheckout_stage(intstage,conststructcache_entry*ce,intpos,-conststructcheckout*state)+conststructcheckout*state,intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -173,6 +177,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,returncheckout_entry(active_cache[pos],state,NULL);pos++;}+if(!overlay_mode){+unlink_entry(ce);+return0;+}if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -332,7 +359,14 @@ static int checkout_paths(const struct checkout_opts *opts,*tobecheckedout.*/for(pos=0;pos<active_nr;pos++)-mark_ce_for_checkout(active_cache[pos],ps_matched,opts);+if(opts->overlay_mode)+mark_ce_for_checkout_overlay(active_cache[pos],+ps_matched,+opts);+else+mark_ce_for_checkout_no_overlay(active_cache[pos],+ps_matched,+opts);if(report_path_error(ps_matched,&opts->pathspec,opts->prefix)){free(ps_matched);
@@ -353,7 +387,7 @@ static int checkout_paths(const struct checkout_opts *opts,if(opts->force){warning(_("path '%s' is unmerged"),ce->name);}elseif(opts->writeout_stage){-errs|=check_stage(opts->writeout_stage,ce,pos);+errs|=check_stage(opts->writeout_stage,ce,pos,opts->overlay_mode);}elseif(opts->merge){errs|=check_stages((1<<2)|(1<<3),ce,pos);}else{
@@ -380,12 +414,14 @@ static int checkout_paths(const struct checkout_opts *opts,continue;}if(opts->writeout_stage)-errs|=checkout_stage(opts->writeout_stage,ce,pos,&state);+errs|=checkout_stage(opts->writeout_stage,ce,pos,&state,opts->overlay_mode);elseif(opts->merge)errs|=checkout_merged(pos,&state);pos=skip_same_name(ce,pos)-1;}}+remove_marked_cache_entries(&the_index,1);+remove_scheduled_dirs();errs|=finish_delayed_checkout(&state);if(write_locked_index(&the_index,&lock_file,COMMIT_LOCK))
@@ -547,6 +583,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*opts->show_progressonlyimpactsoutputsodoesn'trequireamerge*/+/*+*opts->overlay_modecannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1183,6 +1224,10 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");+if(!opts->overlay_mode)+die(_("'%s' cannot be used with switching branches"),+"--no-overlay");+if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),"--ours/--theirs");
@@ -1302,6 +1349,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)if((!!opts.new_branch+!!opts.new_branch_force+!!opts.new_orphan_branch)>1)die(_("-b, -B and --orphan are mutually exclusive"));+if(opts.overlay_mode==1&&opts.patch_mode)+die(_("-p and --overlay are mutually exclusive"));+/**Fromhereon,new_branchwillcontainthebranchtobecheckedout,*andnew_branch_forceandnew_orphan_branchwilltelluswhichoneof
From: Thomas Gummerer <hidden> Date: 2018-12-20 13:48:57
In the previous patch we introduced a new no-overlay mode for git
checkout. Some users (such as the author of this commit) may want to
have this mode turned on by default as it matches their mental model
more closely. Make that possible by introducing a new config option
to that extend.
Signed-off-by: Thomas Gummerer <redacted>
---
Documentation/config/checkout.txt | 7 +++++++
builtin/checkout.c | 8 +++++++-
t/t2025-checkout-no-overlay.sh | 10 ++++++++++
3 files changed, 24 insertions(+), 1 deletion(-)
@@ -21,3 +21,10 @@ checkout.optimizeNewBranch:: will not update the skip-worktree bit in the index nor add/remove files in the working directory to reflect the current sparse checkout settings nor will it show the local changes.++checkout.overlayMode::+ In the default overlay mode files `git checkout` never+ removes files from the index or the working tree. When+ setting checkout.overlayMode to false, files that appear in+ the index and working tree, but not in <tree-ish> are removed,+ to make them match <tree-ish> exactly.
@@ -260,6 +260,9 @@ the conflicted merge in the specified paths. This means that you can use `git checkout -p` to selectively discard edits from your current working tree. See the ``Interactive Mode'' section of linkgit:git-add[1] to learn how to operate the `--patch` mode.+++Note that this option uses the no overlay mode by default (see also+-`--[no-]overlay`), and currently doesn't support overlay mode. --ignore-other-worktrees:: `git checkout` refuses when the wanted ref is already checked
@@ -276,6 +279,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Just like linkgit:git-submodule[1], this will detach the submodules HEAD.+--[no-]overlay::+ In the default overlay mode files `git checkout` never
-ECANTPARSE. Maybe "files" should be removed from this line?
+ removes files from the index or the working tree. When
+ specifying --no-overlay, files that appear in the index and
+ working tree, but not in <tree-ish> are removed, to make them
+ match <tree-ish> exactly.
+
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:37
Previous rounds are at [off-list ref]
and [off-list ref].
Thanks Duy, Eric and Junio for comments on the previous round.
This round fixes some inconsistencies and improves the grammar in the
docs. Range-diff below:
1: fa450cda7c = 1: fa450cda7c move worktree tests to t24*
2: 9ada8d3484 = 2: 9ada8d3484 entry: factor out unlink_entry function
3: 41c0ea4047 = 3: 41c0ea4047 entry: support CE_WT_REMOVE flag in checkout_entry
4: afccb0848d = 4: afccb0848d read-cache: add invalidate parameter to remove_marked_cache_entries
5: 8a2b5efdad = 5: 8a2b5efdad checkout: clarify comment
6: c405f20471 = 6: c405f20471 checkout: factor out mark_cache_entry_for_checkout function
7: e5b18bcd02 ! 7: a291dc78fa checkout: introduce --{,no-}overlay option
@@ -35,7 +35,7 @@
section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
++
+Note that this option uses the no overlay mode by default (see also
-+-`--[no-]overlay`), and currently doesn't support overlay mode.
++`--[no-]overlay`), and currently doesn't support overlay mode.
--ignore-other-worktrees::
`git checkout` refuses when the wanted ref is already checked
@@ -44,9 +44,9 @@
submodules HEAD.
+--[no-]overlay::
-+ In the default overlay mode files `git checkout` never
++ In the default overlay mode, `git checkout` never
+ removes files from the index or the working tree. When
-+ specifying --no-overlay, files that appear in the index and
++ specifying `--no-overlay`, files that appear in the index and
+ working tree, but not in <tree-ish> are removed, to make them
+ match <tree-ish> exactly.
+
8: de24990d57 ! 8: 8d4070f142 checkout: introduce checkout.overlayMode config
@@ -19,9 +19,9 @@
settings nor will it show the local changes.
+
+checkout.overlayMode::
-+ In the default overlay mode files `git checkout` never
++ In the default overlay mode, `git checkout` never
+ removes files from the index or the working tree. When
-+ setting checkout.overlayMode to false, files that appear in
++ setting `checkout.overlayMode` to false, files that appear in
+ the index and working tree, but not in <tree-ish> are removed,
+ to make them match <tree-ish> exactly.
Thomas Gummerer (8):
move worktree tests to t24*
entry: factor out unlink_entry function
entry: support CE_WT_REMOVE flag in checkout_entry
read-cache: add invalidate parameter to remove_marked_cache_entries
checkout: clarify comment
checkout: factor out mark_cache_entry_for_checkout function
checkout: introduce --{,no-}overlay option
checkout: introduce checkout.overlayMode config
Documentation/config/checkout.txt | 7 +
Documentation/git-checkout.txt | 10 ++
builtin/checkout.c | 133 +++++++++++++-----
cache.h | 7 +-
entry.c | 26 ++++
read-cache.c | 8 +-
split-index.c | 2 +-
t/t2025-checkout-no-overlay.sh | 57 ++++++++
...-worktree-add.sh => t2400-worktree-add.sh} | 0
...ktree-prune.sh => t2401-worktree-prune.sh} | 0
...orktree-list.sh => t2402-worktree-list.sh} | 0
...orktree-move.sh => t2403-worktree-move.sh} | 0
...ree-config.sh => t2404-worktree-config.sh} | 0
t/t9902-completion.sh | 1 +
unpack-trees.c | 21 +--
15 files changed, 213 insertions(+), 59 deletions(-)
create mode 100755 t/t2025-checkout-no-overlay.sh
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
rename t/{t2028-worktree-move.sh => t2403-worktree-move.sh} (100%)
rename t/{t2029-worktree-config.sh => t2404-worktree-config.sh} (100%)
--
2.20.1.153.gd81d796ee0
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:38
Factor out the 'unlink_entry()' function from unpack-trees.c to
entry.c. It will be used in other places as well in subsequent
steps.
As it's no longer a static function, also move the documentation to
the header file to make it more discoverable.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 5 +++++
entry.c | 15 +++++++++++++++
unpack-trees.c | 19 -------------------
3 files changed, 20 insertions(+), 19 deletions(-)
@@ -508,3 +508,18 @@ int checkout_entry(struct cache_entry *ce,create_directories(path.buf,path.len,state);returnwrite_entry(ce,path.buf,state,0);}++voidunlink_entry(conststructcache_entry*ce)+{+conststructsubmodule*sub=submodule_from_ce(ce);+if(sub){+/* state.force is set at the caller. */+submodule_move_head(ce->name,"HEAD",NULL,+SUBMODULE_MOVE_HEAD_FORCE);+}+if(!check_leading_path(ce->name,ce_namelen(ce)))+return;+if(remove_or_warn(ce->ce_mode,ce->name))+return;+schedule_dir_for_removal(ce->name,ce_namelen(ce));+}
@@ -300,25 +300,6 @@ static void load_gitmodules_file(struct index_state *index,}}-/*-*Unlinkthelastcomponentandscheduletheleadingdirectoriesfor-*removal,suchthatemptydirectoriesgetremoved.-*/-staticvoidunlink_entry(conststructcache_entry*ce)-{-conststructsubmodule*sub=submodule_from_ce(ce);-if(sub){-/* state.force is set at the caller. */-submodule_move_head(ce->name,"HEAD",NULL,-SUBMODULE_MOVE_HEAD_FORCE);-}-if(!check_leading_path(ce->name,ce_namelen(ce)))-return;-if(remove_or_warn(ce->ce_mode,ce->name))-return;-schedule_dir_for_removal(ce->name,ce_namelen(ce));-}-staticstructprogress*get_progress(structunpack_trees_options*o){unsignedcnt=0,total=0;
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:39
The 'git worktree' command used to be just another mode in 'git
checkout', namely 'git checkout --to'. When the tests for the latter
were retrofitted for the former, the test name was adjusted, but the
test number was kept, even though the test is testing a different
command now. t/README states: "Second digit tells the particular
command we are testing.", so 'git worktree' should have a separate
number just for itself.
Move the worktree tests to t24* to adhere to that guideline. We're
going to make use of the free'd up numbers in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
t/{t2025-worktree-add.sh => t2400-worktree-add.sh} | 0
t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} | 0
t/{t2027-worktree-list.sh => t2402-worktree-list.sh} | 0
t/{t2028-worktree-move.sh => t2403-worktree-move.sh} | 0
t/{t2029-worktree-config.sh => t2404-worktree-config.sh} | 0
5 files changed, 0 insertions(+), 0 deletions(-)
rename t/{t2025-worktree-add.sh => t2400-worktree-add.sh} (100%)
rename t/{t2026-worktree-prune.sh => t2401-worktree-prune.sh} (100%)
rename t/{t2027-worktree-list.sh => t2402-worktree-list.sh} (100%)
rename t/{t2028-worktree-move.sh => t2403-worktree-move.sh} (100%)
rename t/{t2029-worktree-config.sh => t2404-worktree-config.sh} (100%)
diff --git a/t/t2025-worktree-add.sh b/t/t2400-worktree-add.shsimilarity index 100%rename from t/t2025-worktree-add.shrename to t/t2400-worktree-add.shdiff --git a/t/t2026-worktree-prune.sh b/t/t2401-worktree-prune.shsimilarity index 100%rename from t/t2026-worktree-prune.shrename to t/t2401-worktree-prune.shdiff --git a/t/t2027-worktree-list.sh b/t/t2402-worktree-list.shsimilarity index 100%rename from t/t2027-worktree-list.shrename to t/t2402-worktree-list.shdiff --git a/t/t2028-worktree-move.sh b/t/t2403-worktree-move.shsimilarity index 100%rename from t/t2028-worktree-move.shrename to t/t2403-worktree-move.shdiff --git a/t/t2029-worktree-config.sh b/t/t2404-worktree-config.shsimilarity index 100%rename from t/t2029-worktree-config.shrename to t/t2404-worktree-config.sh
--
2.20.1.153.gd81d796ee0
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:42
When marking cache entries for removal, and later removing them all at
once using 'remove_marked_cache_entries()', cache entries currently
have to be invalidated manually in the cache tree and in the untracked
cache.
Add an invalidate flag to the function. With the flag set, the
function will take care of invalidating the path in the cache tree and
in the untracked cache.
Note that the current callsites already do the invalidation properly
in other places, so we're just passing 0 from there to keep the status
quo.
This will be useful in a subsequent commit.
Signed-off-by: Thomas Gummerer <redacted>
---
cache.h | 2 +-
read-cache.c | 8 +++++++-
split-index.c | 2 +-
unpack-trees.c | 2 +-
4 files changed, 10 insertions(+), 4 deletions(-)
@@ -751,7 +751,7 @@ extern void rename_index_entry_at(struct index_state *, int pos, const char *new/* Remove entry, return true if there are more entries to go. */externintremove_index_entry_at(structindex_state*,intpos);-externvoidremove_marked_cache_entries(structindex_state*istate);+externvoidremove_marked_cache_entries(structindex_state*istate,intinvalidate);externintremove_file_from_index(structindex_state*,constchar*path);#define ADD_CACHE_VERBOSE 1#define ADD_CACHE_PRETEND 2
@@ -590,13 +590,19 @@ int remove_index_entry_at(struct index_state *istate, int pos)*CE_REMOVEissetince_flags.Thisismuchmoreeffectivethan*callingremove_index_entry_at()foreachentrytoberemoved.*/-voidremove_marked_cache_entries(structindex_state*istate)+voidremove_marked_cache_entries(structindex_state*istate,intinvalidate){structcache_entry**ce_array=istate->cache;unsignedinti,j;for(i=j=0;i<istate->cache_nr;i++){if(ce_array[i]->ce_flags&CE_REMOVE){+if(invalidate){+cache_tree_invalidate_path(istate,+ce_array[i]->name);+untracked_cache_remove_from_index(istate,+ce_array[i]->name);+}remove_name_hash(istate,ce_array[i]);save_or_free_index_entry(istate,ce_array[i]);}
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:43
'checkout_entry()' currently only supports creating new entries in the
working tree, but not deleting them. Add the ability to remove
entries at the same time if the entry is marked with the CE_WT_REMOVE
flag.
Currently this doesn't have any effect, as the CE_WT_REMOVE flag is
only used in unpack-tree, however we will make use of this in a
subsequent step in the series.
Signed-off-by: Thomas Gummerer <redacted>
---
entry.c | 11 +++++++++++
1 file changed, 11 insertions(+)
@@ -441,6 +441,17 @@ int checkout_entry(struct cache_entry *ce,staticstructstrbufpath=STRBUF_INIT;structstatst;+if(ce->ce_flags&CE_WT_REMOVE){+if(topath)+/*+*Nocontentandthusnopathtocreate,sowehave+*nopathnametoreturn.+*/+BUG("Can't remove entry to a path");+unlink_entry(ce);+return0;+}+if(topath)returnwrite_entry(ce,topath,state,1);
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:45
The key point for the if statement is that read_tree_some did not
update the entry, because either it doesn't exist in tree-ish or
doesn't match the pathspec. Clarify that.
Suggested-by: Nguyễn Thái Ngọc Duy <redacted>
Signed-off-by: Thomas Gummerer <redacted>
---
builtin/checkout.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:46
Factor out the code that marks a cache entry as matched for checkout
into a separate function. We are going to introduce a new mode in
'git checkout' in a subsequent commit, that is going to have a
slightly different logic. This would make this code unnecessarily
complex.
Moving that complexity into separate functions will make the code in
the subsequent step easier to follow.
Signed-off-by: Thomas Gummerer <redacted>
---
builtin/checkout.c | 67 +++++++++++++++++++++++++---------------------
1 file changed, 36 insertions(+), 31 deletions(-)
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:48
Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.
Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.
Note that 'git checkout -p <tree-ish> -- [<pathspec>]' already works
this way, so no changes are needed for the patch mode. We disallow
'git checkout --overlay -p' to avoid confusing users who would expect
to be able to force overlay mode in 'git checkout -p' this way.
Untracked files are not affected by this change, so 'git checkout
--no-overlay HEAD -- untracked' will not remove untracked from the
working tree. This is so e.g. 'git checkout --no-overlay HEAD -- dir/'
doesn't delete all untracked files in dir/, but rather just resets the
state of files that are known to git.
Suggested-by: Junio C Hamano <redacted>
Signed-off-by: Thomas Gummerer <redacted>
---
Documentation/git-checkout.txt | 10 ++++++
builtin/checkout.c | 66 +++++++++++++++++++++++++++++-----
t/t2025-checkout-no-overlay.sh | 47 ++++++++++++++++++++++++
t/t9902-completion.sh | 1 +
4 files changed, 116 insertions(+), 8 deletions(-)
create mode 100755 t/t2025-checkout-no-overlay.sh
@@ -260,6 +260,9 @@ the conflicted merge in the specified paths. This means that you can use `git checkout -p` to selectively discard edits from your current working tree. See the ``Interactive Mode'' section of linkgit:git-add[1] to learn how to operate the `--patch` mode.+++Note that this option uses the no overlay mode by default (see also+`--[no-]overlay`), and currently doesn't support overlay mode. --ignore-other-worktrees:: `git checkout` refuses when the wanted ref is already checked
@@ -276,6 +279,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Just like linkgit:git-submodule[1], this will detach the submodules HEAD.+--[no-]overlay::+ In the default overlay mode, `git checkout` never+ removes files from the index or the working tree. When+ specifying `--no-overlay`, files that appear in the index and+ working tree, but not in <tree-ish> are removed, to make them+ match <tree-ish> exactly.+ <branch>:: Branch to checkout; if it refers to a branch (i.e., a name that, when prepended with "refs/heads/", is a valid ref), then that
@@ -132,7 +133,8 @@ static int skip_same_name(const struct cache_entry *ce, int pos)returnpos;}-staticintcheck_stage(intstage,conststructcache_entry*ce,intpos)+staticintcheck_stage(intstage,conststructcache_entry*ce,intpos,+intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -140,6 +142,8 @@ static int check_stage(int stage, const struct cache_entry *ce, int pos)return0;pos++;}+if(!overlay_mode)+return0;if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -165,7 +169,7 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)}staticintcheckout_stage(intstage,conststructcache_entry*ce,intpos,-conststructcheckout*state)+conststructcheckout*state,intoverlay_mode){while(pos<active_nr&&!strcmp(active_cache[pos]->name,ce->name)){
@@ -173,6 +177,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,returncheckout_entry(active_cache[pos],state,NULL);pos++;}+if(!overlay_mode){+unlink_entry(ce);+return0;+}if(stage==2)returnerror(_("path '%s' does not have our version"),ce->name);else
@@ -332,7 +359,14 @@ static int checkout_paths(const struct checkout_opts *opts,*tobecheckedout.*/for(pos=0;pos<active_nr;pos++)-mark_ce_for_checkout(active_cache[pos],ps_matched,opts);+if(opts->overlay_mode)+mark_ce_for_checkout_overlay(active_cache[pos],+ps_matched,+opts);+else+mark_ce_for_checkout_no_overlay(active_cache[pos],+ps_matched,+opts);if(report_path_error(ps_matched,&opts->pathspec,opts->prefix)){free(ps_matched);
@@ -353,7 +387,7 @@ static int checkout_paths(const struct checkout_opts *opts,if(opts->force){warning(_("path '%s' is unmerged"),ce->name);}elseif(opts->writeout_stage){-errs|=check_stage(opts->writeout_stage,ce,pos);+errs|=check_stage(opts->writeout_stage,ce,pos,opts->overlay_mode);}elseif(opts->merge){errs|=check_stages((1<<2)|(1<<3),ce,pos);}else{
@@ -380,12 +414,14 @@ static int checkout_paths(const struct checkout_opts *opts,continue;}if(opts->writeout_stage)-errs|=checkout_stage(opts->writeout_stage,ce,pos,&state);+errs|=checkout_stage(opts->writeout_stage,ce,pos,&state,opts->overlay_mode);elseif(opts->merge)errs|=checkout_merged(pos,&state);pos=skip_same_name(ce,pos)-1;}}+remove_marked_cache_entries(&the_index,1);+remove_scheduled_dirs();errs|=finish_delayed_checkout(&state);if(write_locked_index(&the_index,&lock_file,COMMIT_LOCK))
@@ -547,6 +583,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,*opts->show_progressonlyimpactsoutputsodoesn'trequireamerge*/+/*+*opts->overlay_modecannotbeusedwithswitchingbranchessois+*nottestedhere+*/+/**Ifwearen'tcreatinganewbranchanychangesorupdateswill*happenintheexistingbranch.Sincethatcouldonlybeupdating
@@ -1183,6 +1224,10 @@ static int checkout_branch(struct checkout_opts *opts,die(_("'%s' cannot be used with switching branches"),"--patch");+if(!opts->overlay_mode)+die(_("'%s' cannot be used with switching branches"),+"--no-overlay");+if(opts->writeout_stage)die(_("'%s' cannot be used with switching branches"),"--ours/--theirs");
@@ -1302,6 +1349,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)if((!!opts.new_branch+!!opts.new_branch_force+!!opts.new_orphan_branch)>1)die(_("-b, -B and --orphan are mutually exclusive"));+if(opts.overlay_mode==1&&opts.patch_mode)+die(_("-p and --overlay are mutually exclusive"));+/**Fromhereon,new_branchwillcontainthebranchtobecheckedout,*andnew_branch_forceandnew_orphan_branchwilltelluswhichoneof
From: Thomas Gummerer <hidden> Date: 2019-01-08 21:52:48
In the previous patch we introduced a new no-overlay mode for git
checkout. Some users (such as the author of this commit) may want to
have this mode turned on by default as it matches their mental model
more closely. Make that possible by introducing a new config option
to that extend.
Signed-off-by: Thomas Gummerer <redacted>
---
Documentation/config/checkout.txt | 7 +++++++
builtin/checkout.c | 8 +++++++-
t/t2025-checkout-no-overlay.sh | 10 ++++++++++
3 files changed, 24 insertions(+), 1 deletion(-)
@@ -21,3 +21,10 @@ checkout.optimizeNewBranch:: will not update the skip-worktree bit in the index nor add/remove files in the working directory to reflect the current sparse checkout settings nor will it show the local changes.++checkout.overlayMode::+ In the default overlay mode, `git checkout` never+ removes files from the index or the working tree. When+ setting `checkout.overlayMode` to false, files that appear in+ the index and working tree, but not in <tree-ish> are removed,+ to make them match <tree-ish> exactly.
From: Jonathan Nieder <hidden> Date: 2019-01-22 23:53:18
Hi,
Thomas Gummerer wrote:
Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.
Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.
This patch just hit my workstation. Some initial thoughts:
I had no idea what --overlay would mean and am still not clear on it.
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Thank you thank you thank you for working on this. I run into this
all the time and am super excited about the "default to
--no-ignore-removal" future.
I'm nervous about the config with no associated warning or plan for
phasing it out. It means that scripts using "git checkout" don't
get a consistent behavior unless they explicitly pass this option,
which didn't exist in older versions of Git --- in other words,
scripts have no real good option. Can we plan a transition to
making --no-ignore-removal the default, in multiple steps? For
example:
1. First introduce the commandline option, as in this series
2. Next, change the default to warn whenever the difference would
matter, printing a hint about how to configure to explicitly
request the old or new behavior.
3. After a release or two has passed so people get a chance
to update their scripts, flip the default.
4. Finally, remove the warning.
5. Warn whenver the difference would matter when a user has
requested the old behavior through config, in preparation
for removing the config.
6. Remove the config.
Steps 5 and 6 are optional but might be nice.
What do you think?
Thanks,
Jonathan
From: Thomas Gummerer <hidden> Date: 2019-01-23 20:22:02
On 01/22, Jonathan Nieder wrote:
Hi,
Thomas Gummerer wrote:
quoted
Currently 'git checkout' is defined as an overlay operation, which
means that if in 'git checkout <tree-ish> -- [<pathspec>]' we have an
entry in the index that matches <pathspec>, but that doesn't exist in
<tree-ish>, that entry will not be removed from the index or the
working tree.
Introduce a new --{,no-}overlay option, which allows using 'git
checkout' in non-overlay mode, thus removing files from the working
tree if they do not exist in <tree-ish> but match <pathspec>.
This patch just hit my workstation. Some initial thoughts:
I had no idea what --overlay would mean and am still not clear on it.
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Yes, it seems like they are very similar. I'm happy to rename the
option. The topic seems to have made it to 'next' already, so I'll
submit the patches on top, unless reverting the topic out of next and
replacing it is preferred?
Thank you thank you thank you for working on this. I run into this
all the time and am super excited about the "default to
--no-ignore-removal" future.
:)
I'm nervous about the config with no associated warning or plan for
phasing it out. It means that scripts using "git checkout" don't
get a consistent behavior unless they explicitly pass this option,
which didn't exist in older versions of Git --- in other words,
scripts have no real good option. Can we plan a transition to
making --no-ignore-removal the default, in multiple steps? For
example:
As Junio mentioned, the plan was to just have this mode default when
we introduce the new checkout-paths command.
As checkout is a porcelain command, I had hoped it would be okay to
also have this as a configuration option, for the time before
'checkout-paths' exists and while I'm getting used to actually typing
'checkout-paths' instead of 'checkout'. However I get that there may
be scripts that are using git checkout, and expect the previous
behaviour, so I'm also okay with dropping the config option for now.
If we still want to make this the default even after 'checkout-paths'
exists, the plan you outline below sounds good to me, though maybe we
can make the "flip the default" step once we decide to release git
3.0.
1. First introduce the commandline option, as in this series
2. Next, change the default to warn whenever the difference would
matter, printing a hint about how to configure to explicitly
request the old or new behavior.
3. After a release or two has passed so people get a chance
to update their scripts, flip the default.
4. Finally, remove the warning.
5. Warn whenver the difference would matter when a user has
requested the old behavior through config, in preparation
for removing the config.
6. Remove the config.
Steps 5 and 6 are optional but might be nice.
What do you think?
Thanks,
Jonathan
From: Jonathan Nieder <hidden> Date: 2019-01-23 20:47:25
Thomas Gummerer wrote:
On 01/22, Jonathan Nieder wrote:
quoted
I had no idea what --overlay would mean and am still not clear on it.
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Yes, it seems like they are very similar. I'm happy to rename the
option. The topic seems to have made it to 'next' already, so I'll
submit the patches on top, unless reverting the topic out of next and
replacing it is preferred?
A patch on top sounds good.
[...]
quoted
I'm nervous about the config with no associated warning or plan for
phasing it out. It means that scripts using "git checkout" don't
get a consistent behavior unless they explicitly pass this option,
which didn't exist in older versions of Git --- in other words,
scripts have no real good option. Can we plan a transition to
making --no-ignore-removal the default, in multiple steps? For
example:
As Junio mentioned, the plan was to just have this mode default when
we introduce the new checkout-paths command.
As checkout is a porcelain command, I had hoped it would be okay to
also have this as a configuration option, for the time before
'checkout-paths' exists and while I'm getting used to actually typing
'checkout-paths' instead of 'checkout'. However I get that there may
be scripts that are using git checkout, and expect the previous
behaviour, so I'm also okay with dropping the config option for now.
Yes, if we have no plan for flipping the default later, then I would
prefer to eliminate the config option. Scripts very frequently use
human-facing commands like "git checkout" when they want the command
to produce (unparsable) friendly output to show to humans, and I don't
think we've provided a good alternative for that use case.
If we still want to make this the default even after 'checkout-paths'
exists, the plan you outline below sounds good to me, though maybe we
can make the "flip the default" step once we decide to release git
3.0.
I would really like this, so I might write a series for it. Please
don't wait for me, though --- feel free to send any patches you're
thinking about and we can work together or I can just appreciate your
work. ;-)
Sincerely,
Jonathan
From: Junio C Hamano <hidden> Date: 2019-01-23 21:08:45
Thomas Gummerer [off-list ref] writes:
quoted
I had no idea what --overlay would mean and am still not clear on it.
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Yes, it seems like they are very similar.
Hmm, I am not sure if the word "removal" makes sense in the context
of "checkout", as "removal" is an _action_ just like "checking out"
itself is, and not a _state_. You'd check out a state out of a tree
to the index and the working tree, so "checking out absence of a
path" may make sense, though, as "absence of a path" is a state
recorded in that source tree object.
The word "removal" makes little sense in "git add --ignore-removal",
but it and "git add --no-all" outlived their usefulness already, so
it may not be worth _fixing_ it. But I am mildly opposed to spread
the earlier mistake to a new option.
From: Jonathan Nieder <hidden> Date: 2019-01-24 01:12:49
Hi,
Junio C Hamano wrote:
Thomas Gummerer [off-list ref] writes:
quoted
Jonathan Nieder wrote:
quoted
quoted
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Yes, it seems like they are very similar.
Hmm, I am not sure if the word "removal" makes sense in the context
of "checkout", as "removal" is an _action_ just like "checking out"
itself is, and not a _state_. You'd check out a state out of a tree
to the index and the working tree, so "checking out absence of a
path" may make sense, though, as "absence of a path" is a state
recorded in that source tree object.
I find --ignore-removal fairly easy to understand, and I had no idea
what --overlay would mean.
I realize this is just one user's experience. I'd be happy to do a
little informal survey (e.g. taking the description from the manpage
and asking people to name the option) if that's useful.
See also https://dl.acm.org/citation.cfm?id=32212 on this subject.
The word "removal" makes little sense in "git add --ignore-removal",
but it and "git add --no-all" outlived their usefulness already, so
it may not be worth _fixing_ it. But I am mildly opposed to spread
the earlier mistake to a new option.
I think that's a good place to end up: once we flip the default for
checkout, then --ignore-removal would be an obscure option in that
command as well. The consistency with "git add" is just a bonus.
Thanks,
Jonathan
From: Thomas Gummerer <hidden> Date: 2019-01-24 22:02:42
On 01/23, Jonathan Nieder wrote:
Hi,
Junio C Hamano wrote:
quoted
Thomas Gummerer [off-list ref] writes:
quoted
Jonathan Nieder wrote:
quoted
quoted
quoted
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Yes, it seems like they are very similar.
Hmm, I am not sure if the word "removal" makes sense in the context
of "checkout", as "removal" is an _action_ just like "checking out"
itself is, and not a _state_. You'd check out a state out of a tree
to the index and the working tree, so "checking out absence of a
path" may make sense, though, as "absence of a path" is a state
recorded in that source tree object.
I find --ignore-removal fairly easy to understand, and I had no idea
what --overlay would mean.
What do you think about --[no-]ignore-removed? That would not be the same
as we are using in 'git add' though, and the slight difference may be
worse than a different option? Though I suspect not too many people
are using --ignore-removal in 'git add' in the first place.
I realize this is just one user's experience. I'd be happy to do a
little informal survey (e.g. taking the description from the manpage
and asking people to name the option) if that's useful.
Sure, that sounds like an option if we can't come to an agreement
here. What would such a survey look like?
From: Thomas Gummerer <hidden> Date: 2019-01-24 22:08:07
On 01/23, Jonathan Nieder wrote:
Thomas Gummerer wrote:
quoted
On 01/22, Jonathan Nieder wrote:
quoted
As checkout is a porcelain command, I had hoped it would be okay to
also have this as a configuration option, for the time before
'checkout-paths' exists and while I'm getting used to actually typing
'checkout-paths' instead of 'checkout'. However I get that there may
be scripts that are using git checkout, and expect the previous
behaviour, so I'm also okay with dropping the config option for now.
Yes, if we have no plan for flipping the default later, then I would
prefer to eliminate the config option. Scripts very frequently use
human-facing commands like "git checkout" when they want the command
to produce (unparsable) friendly output to show to humans, and I don't
think we've provided a good alternative for that use case.
Ok, I'm happy to drop that for now, and possibly re-introduce that
with another series to start flipping the default. I'll probably wait
for Duy's checkout-paths command first though, and possibly send a
series later.
Junio, do you just want to revert the patch (1495ff7da5 ("checkout:
introduce checkout.overlayMode config", 2019-01-08)), or would you
prefer me sending a patch for that?
quoted
If we still want to make this the default even after 'checkout-paths'
exists, the plan you outline below sounds good to me, though maybe we
can make the "flip the default" step once we decide to release git
3.0.
I would really like this, so I might write a series for it. Please
don't wait for me, though --- feel free to send any patches you're
thinking about and we can work together or I can just appreciate your
work. ;-)
Sincerely,
Jonathan
From: Philip Oakley <hidden> Date: 2019-02-09 19:02:29
Hi,
On 24/01/2019 01:12, Jonathan Nieder wrote:
Hi,
Junio C Hamano wrote:
quoted
Thomas Gummerer[off-list ref] writes:
quoted
Jonathan Nieder wrote:
quoted
Is this analogous to "git add --ignore-removal"? If so, can we just
call it --ignore-removal?
Yes, it seems like they are very similar.
Hmm, I am not sure if the word "removal" makes sense in the context
of "checkout", as "removal" is an_action_ just like "checking out"
itself is, and not a_state_. You'd check out a state out of a tree
to the index and the working tree, so "checking out absence of a
path" may make sense, though, as "absence of a path" is a state
recorded in that source tree object.
I find --ignore-removal fairly easy to understand, and I had no idea
what --overlay would mean.
I too had difficulty initially as to what 'overlay' meant, or that there
were options.
I realize this is just one user's experience. I'd be happy to do a
little informal survey (e.g. taking the description from the manpage
and asking people to name the option) if that's useful.
See alsohttps://dl.acm.org/citation.cfm?id=32212 on this subject.
I did locate a copy at
http://zhang.ist.psu.edu/teaching/501/readings/Furnas.pdf
The whole word choosing problem does smack a bit of Orwell's Vocabulary
C (OVC) where:
"The [Newspeak] C vocabulary encompasses words that relate specifically
to science and to technical fields and disciplines. It is designed to
ensure that technical knowledge remains segmented among many fields, so
that no one individual can gain access to too much knowledge. In fact,
there is no word for “science” " [1]
Most of the DVCS concepts have a newness to them that means that we
don't have good words yet, hence the difficulties. Just my 2 cents.
quoted
The word "removal" makes little sense in "git add --ignore-removal",
but it and "git add --no-all" outlived their usefulness already, so
it may not be worth_fixing_ it. But I am mildly opposed to spread
the earlier mistake to a new option.
I think that's a good place to end up: once we flip the default for
checkout, then --ignore-removal would be an obscure option in that
command as well. The consistency with "git add" is just a bonus.
Thanks,
Jonathan