The on-disk format of index only saves 16 bit flags, nearly all have
been used. The last bit (CE_EXTENDED) is used to for future extension.
This patch extends index entry format to save more flags in future.
The new entry format will be used when CE_EXTENDED bit is 1.
Because older implementation may not understand CE_EXTENDED bit and
misread the new format, if there is any extended entry in index, index
header version will turn 3, which makes it incompatible for older git.
If there is none, header version will return to 2 again.
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
cache.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
read-cache.c | 51 +++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 95 insertions(+), 14 deletions(-)
@@ -1096,7 +1096,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)if(hdr->hdr_signature!=htonl(CACHE_SIGNATURE))returnerror("bad signature");-if(hdr->hdr_version!=htonl(2))+if(hdr->hdr_version!=htonl(2)&&hdr->hdr_version!=htonl(3))returnerror("bad index version");SHA1_Init(&c);SHA1_Update(&c,hdr,size-20);
@@ -1131,6 +1131,7 @@ int read_index(struct index_state *istate)staticvoidconvert_from_disk(structondisk_cache_entry*ondisk,structcache_entry*ce){size_tlen;+constchar*name;ce->ce_ctime=ntohl(ondisk->ctime.sec);ce->ce_mtime=ntohl(ondisk->mtime.sec);
@@ -1143,19 +1144,31 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en/* On-disk flags are just 16 bits */ce->ce_flags=ntohs(ondisk->flags);-/* For future extension: we do not understand this entry yet */-if(ce->ce_flags&CE_EXTENDED)-die("Unknown index entry format");hashcpy(ce->sha1,ondisk->sha1);len=ce->ce_flags&CE_NAMEMASK;++if(ce->ce_flags&CE_EXTENDED){+structondisk_cache_entry_extended*ondisk2;+intextended_flags;+ondisk2=(structondisk_cache_entry_extended*)ondisk;+extended_flags=ntohs(ondisk2->flags2)<<16;+/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */+if(extended_flags&~CE_EXTENDED_FLAGS)+die("Unknown index entry format %08x",extended_flags);+ce->ce_flags|=extended_flags;+name=ondisk2->name;+}+else+name=ondisk->name;+if(len==CE_NAMEMASK)-len=strlen(ondisk->name);+len=strlen(name);/**NEEDSWORK:Iftheoriginalindexiscrafted,thiscopycould*gounchecked.*/-memcpy(ce->name,ondisk->name,len+1);+memcpy(ce->name,name,len+1);}staticinlinesize_testimate_cache_size(size_tondisk_size,unsignedintentries)
@@ -1415,6 +1428,7 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce){intsize=ondisk_ce_size(ce);structondisk_cache_entry*ondisk=xcalloc(1,size);+char*name;ondisk->ctime.sec=htonl(ce->ce_ctime);ondisk->ctime.nsec=0;
@@ -1428,7 +1442,15 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)ondisk->size=htonl(ce->ce_size);hashcpy(ondisk->sha1,ce->sha1);ondisk->flags=htons(ce->ce_flags);-memcpy(ondisk->name,ce->name,ce_namelen(ce));+if(ce->ce_flags&CE_EXTENDED){+structondisk_cache_entry_extended*ondisk2;+ondisk2=(structondisk_cache_entry_extended*)ondisk;+ondisk2->flags2=htons((ce->ce_flags&CE_EXTENDED_FLAGS)>>16);+name=ondisk2->name;+}+else+name=ondisk->name;+memcpy(name,ce->name,ce_namelen(ce));returnce_write(c,fd,ondisk,size);}
@@ -1437,16 +1459,25 @@ int write_index(const struct index_state *istate, int newfd){SHA_CTXc;structcache_headerhdr;-inti,err,removed;+inti,err,removed,extended;structcache_entry**cache=istate->cache;intentries=istate->cache_nr;-for(i=removed=0;i<entries;i++)+for(i=removed=extended=0;i<entries;i++){if(cache[i]->ce_flags&CE_REMOVE)removed++;+/* reduce extended entries if possible */+cache[i]->ce_flags&=~CE_EXTENDED;+if(cache[i]->ce_flags&CE_EXTENDED_FLAGS){+extended++;+cache[i]->ce_flags|=CE_EXTENDED;+}+}+hdr.hdr_signature=htonl(CACHE_SIGNATURE);-hdr.hdr_version=htonl(2);+/* for extended format, increase version so older git won't try to read it */+hdr.hdr_version=htonl(extended?3:2);hdr.hdr_entries=htonl(entries-removed);SHA1_Init(&c);
This bit is the basis of sparse checkout. If this bit is on, the entry
is outside sparse checkout and therefore should be ignored (similar
to CE_VALID)
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
Documentation/git-checkout.txt | 33 +++++++++++++++++++++++++++++++++
cache.h | 10 +++++++++-
read-cache.c | 6 +++---
3 files changed, 45 insertions(+), 4 deletions(-)
@@ -171,6 +171,39 @@ the reflog for HEAD where you were, e.g. $ git log -g -2 HEAD ------------+Sparse checkout+---------------++Normally when you checkout a branch, your working directory+will be fully populated. In some situations, you just need to+work on certain files, no full checkout is needed. Sparse+checkout is a mode that limits the checkout area according to your+needs. With sparse checkout, you can work on a single file, a+collection of files, a subdirectory or a collection of separated+subdirectories.++Because sparse checkout uses a new index format, it will be+incompatible with git prior to 1.6.0 regarding worktree operations.+Operations that only need access to the repository itself, such as+clone, push, or pull/fetch from another (normal) repository... should+not be affected by sparse checkout.++In sparse checkout mode, checkout status of every files in your+working directory will be recorded in index. If a file is marked+"no-checkout", it means that file is not needed to be present in+working directory by user or any git command. When a new file is added+to index, it will be marked "checkout" unless sparse patterns are+applied. Unmerged files are always "checkout". When you checkout new+files using "git checkout <file>" they will be automatically marked+"checkout". Other commands such as "git apply" can also checkout new+files if they are needed.++"No-checkout" status is very similar to "assume-unchanged bit"+(see linkgit:git-update-index[1]). The main difference between them+is "assume unchanged" bit just ignores corresponding files in working+directory while sparse checkout goes a bit farther, remove those files+when it is safe to do so.+ EXAMPLES --------
The first option to be introduced is --sparse, which puts ls-files
in "sparse mode". In this mode, cached entries are divided into
- checkout entries: shown by --cached (new behavior with --sparse)
- no-checkout entries: show by --no-checkout (new option)
- orphaned entries: shown by --orphaned (new option)
Orphaned entries are themselves no-checkout ones but for some reasons
still be present in working directory.
While at it, fix "--deleted" running out of checkout area.
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
Documentation/git-ls-files.txt | 24 +++++++++++++++++++++-
builtin-ls-files.c | 41 ++++++++++++++++++++++++++++++++++++---
2 files changed, 59 insertions(+), 6 deletions(-)
@@ -32,7 +33,9 @@ OPTIONS ------- -c:: --cached::- Show cached files in the output (default)+ Show cached files in the output (default). When used with --sparse,+ show only cached files that are marked "checkout", no-checkout+ entries will be excluded. -d:: --deleted::
@@ -72,6 +75,21 @@ OPTIONS to file/directory conflicts for checkout-index to succeed.+--no-checkout::+ Show no-checkout entries. This option implies --sparse.++--orphaned::+ Show orphaned entries. Orphaned entries are no-checkout+ entries that are present in working directory. This option+ implies --sparse.++--sparse::+ When --sparse is passed, cached files will be divided into two+ parts: checkout entries and no-checkout entries.+ --cached will only show checkout entries.+ No-checkout entries can be shown using --orphaned or+ --no-checkout (or both).+ -z:: \0 line termination on output.
@@ -107,6 +125,8 @@ OPTIONS Identify the file status with the following tags (followed by a space) at the start of each line: H:: cached+ -:: no-checkout entries+ O:: orphaned entries M:: unmerged R:: removed/deleted C:: modified/changed
@@ -593,7 +626,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)/* With no flags, we default to showing the cached files */if(!(show_stage|show_deleted|show_others|show_unmerged|-show_killed|show_modified))+show_killed|show_modified|show_orphaned|show_no_checkout))show_cached=1;read_cache();
@@ -196,7 +196,8 @@ to index, it will be marked "checkout" unless sparse patterns are applied. Unmerged files are always "checkout". When you checkout new files using "git checkout <file>" they will be automatically marked "checkout". Other commands such as "git apply" can also checkout new-files if they are needed.+files if they are needed. linkgit:git-update-index[1] can be used to+update "checkout/no-checkout" status in index. "No-checkout" status is very similar to "assume-unchanged bit" (see linkgit:git-update-index[1]). The main difference between them
@@ -99,6 +100,18 @@ in the index e.g. when merging in a commit; thus, in case the assumed-untracked file is changed upstream, you will need to handle the situation manually.+--checkout::+--no-checkout::+ When one of these flags is specified, the object name recorded+ for the paths are not updated. Instead, these options+ set and unset the "no-checkout" bit for the paths. This+ bit is used for marking files for sparse checkout. If+ a path is marked "no-checkout", then it should not be+ checked out unless requested by user or needed for a git+ command to function.+ See linkgit:git-checkout[1] for more information about+ sparse checkout.+ -g:: --again:: Runs 'git-update-index' itself on the paths whose index
@@ -276,6 +277,11 @@ static void update_one(const char *path, const char *prefix, int prefix_length)die("Unable to mark file %s",path);gotofree_return;}+if(mark_no_checkout_only){+if(mark_ce_flags(p,CE_NO_CHECKOUT,mark_no_checkout_only==MARK_FLAG))+die("Unable to mark file %s",path);+gotofree_return;+}if(force_remove){if(remove_file_from_cache(p))
@@ -0,0 +1,36 @@+#!/bin/sh+#+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy+#++test_description='git update-index no-checkout bits (a.k.a sparse checkout)'++../test-lib.sh++test_expect_success'setup''+mkdirsub&&+touch12sub/1sub/2&&+gitadd12sub/1sub/2+'++test_expect_success'index is at version 2''+test"$(test-index-version<.git/index)"=2+'++test_expect_success'update-index --no-checkout''+gitupdate-index--no-checkout1sub/1&&+test-z"$(gitls-files--sparse|grep1)"'++test_expect_success'index is at version 3 after having some no-checkout entries''+test"$(test-index-version<.git/index)"=3+'++test_expect_success'update-index --checkout''+gitupdate-index--checkout1sub/1&&+test"$(gitls-files)"="$(gitls-files--sparse)"'++test_expect_success'index version is back to 2 when there is no no-checkout entry''+test"$(test-index-version<.git/index)"=2+'++test_done
@@ -161,7 +161,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)continue;}-if(ce_uptodate(ce))+if(ce_uptodate(ce)||ce_no_checkout(ce))continue;changed=check_removed(ce,&st);
@@ -348,6 +348,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,structrev_info*revs=cbdata->revs;intmatch_missing,cached;+/* if the entry is not checked out, don't examine work tree */+cached=o->index_only||(idx&&ce_no_checkout(idx));/**Backwardcompatibilitywart-"diff-index -m"does*notmean"do not ignore merges",but"match_missing".
With this you can just do "git checkout some-files" to
widen your checkout. One caveat though: caller must save
the index.
For all of its callers (unpack_trees(), checkout-index, checkout
and apply), only "git apply" does not write index back.
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
entry.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
@@ -24,7 +24,9 @@ SYNOPSIS DESCRIPTION ----------- Look for specified patterns in the working tree files, blobs-registered in the index file, or given tree objects.+registered in the index file, or given tree objects. By default+it will search in the working tree files. When in sparse checkout+mode, it only searches checked-out files. OPTIONS
Not in this half but I've seen that you added a --default-sparse flag
to "git clone". I think it is useless as mostly always the one given
in -sparse-checkout should be used.
Thanks
Santi
On Wed, Oct 1, 2008 at 6:04 AM, Nguyễn Thái Ngọc Duy [off-list ref] wrote:
This is the first half of the series, making git ready for sparse
checkout. The only difference from the last (first half) sent
series is safeguard bitmask fix in 1/9
Nguyễn Thái Ngọc Duy (9):
Extend index to save more flags
Introduce CE_NO_CHECKOUT bit
ls-files: add options to support sparse checkout
update-index: refactor mark_valid() in preparation for new options
update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT
bit
ls-files: Add tests for --sparse and friends
Prevent diff machinery from examining worktree outside sparse
checkout
checkout_entry(): CE_NO_CHECKOUT on checked out entries.
grep: skip files outside sparse checkout area
.gitignore | 1 +
Documentation/git-checkout.txt | 34 +++++++++++++++++
Documentation/git-grep.txt | 4 +-
Documentation/git-ls-files.txt | 24 +++++++++++-
Documentation/git-update-index.txt | 13 ++++++
Makefile | 2 +-
builtin-grep.c | 7 +++-
builtin-ls-files.c | 41 ++++++++++++++++++--
builtin-update-index.c | 40 ++++++++++++-------
cache.h | 66 +++++++++++++++++++++++++++++++--
diff-lib.c | 5 +-
diff.c | 4 +-
entry.c | 1 +
read-cache.c | 57 ++++++++++++++++++++++------
t/t2104-update-index-no-checkout.sh | 36 ++++++++++++++++++
t/t3004-ls-files-sparse.sh | 40 ++++++++++++++++++++
t/t3004/cached.expected | 5 ++
t/t3004/deleted.expected | 1 +
t/t3004/everything.expected | 10 +++++
t/t3004/modified.expected | 2 +
t/t3004/no-checkout.expected | 2 +
t/t3004/orphaned-no-checkout.expected | 3 +
t/t3004/orphaned.expected | 1 +
t/t3004/others.expected | 2 +
t/t3004/sparse-cached.expected | 3 +
t/t3004/sparse-everything.expected | 11 +++++
test-index-version.c | 14 +++++++
27 files changed, 385 insertions(+), 44 deletions(-)
create mode 100755 t/t2104-update-index-no-checkout.sh
create mode 100755 t/t3004-ls-files-sparse.sh
create mode 100644 t/t3004/cached.expected
create mode 100644 t/t3004/deleted.expected
create mode 100644 t/t3004/everything.expected
create mode 100644 t/t3004/modified.expected
create mode 100644 t/t3004/no-checkout.expected
create mode 100644 t/t3004/orphaned-no-checkout.expected
create mode 100644 t/t3004/orphaned.expected
create mode 100644 t/t3004/others.expected
create mode 100644 t/t3004/sparse-cached.expected
create mode 100644 t/t3004/sparse-everything.expected
create mode 100644 test-index-version.c
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar [off-list ref] wrote:
Not in this half but I've seen that you added a --default-sparse flag
to "git clone". I think it is useless as mostly always the one given
in -sparse-checkout should be used.
To be more precise, whenever you do "git clone --sparse-checkout" you
will want it to be the default sparse pattern.
Best regards,
Santi
On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar [off-list ref] wrote:
> Not in this half but I've seen that you added a --default-sparse flag
> to "git clone". I think it is useless as mostly always the one given
> in -sparse-checkout should be used.
To be more precise, whenever you do "git clone --sparse-checkout" you
will want it to be the default sparse pattern.
Yes, if only the default pattern gets updated properly when you update
your checkout area. If "git clone --sparse-checkout" makes the default
pattern, then "git checkout --reset-sparse" should reset the default
pattern as well. The hard part is how to update default pattern with
--include-sparse and --exclude-sparse. Also, people can use "git
update-index" to update checkout area, which should not touch default
pattern at all. Maybe just throw a warning when default pattern no
longer matches the checkout area, then let them decide.
--
Duy
On Wed, Oct 1, 2008 at 2:09 PM, Nguyen Thai Ngoc Duy [off-list ref] wrote:
On 10/1/08, Santi Béjar [off-list ref] wrote:
quoted
On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar [off-list ref] wrote:
> Not in this half but I've seen that you added a --default-sparse flag
> to "git clone". I think it is useless as mostly always the one given
> in -sparse-checkout should be used.
To be more precise, whenever you do "git clone --sparse-checkout" you
will want it to be the default sparse pattern.
Yes, if only the default pattern gets updated properly when you update
your checkout area.
I don't parse this sentence, but I cannot see the connection between
the default sparse pattern for _clone_ and updating it or the working
area.
If "git clone --sparse-checkout" makes the default
pattern, then "git checkout --reset-sparse" should reset the default
pattern as well.
Makes sense.
The hard part is how to update default pattern with
--include-sparse and --exclude-sparse.
Maybe you could let the core.defaultsparse be defined multiple times.
[core]
defaultsparse = Documentation/*
defaultsparse = t/*
defaultsparse = !t/*svn*
equivalent to
[core]
defaultsparse = Documentation/*:t/*:!t/*svn*
but it moves the complexity to the parsing of the config.
Also, people can use "git
update-index" to update checkout area, which should not touch default
pattern at all.
I would prefer having a --not-update-sparse-pattern.
The most common workflow should be the most straightforward. The most
common use-case would be somebody working _only_ in some subdirectory
(say Documentation), then what s/he normally does is:
1) cloning and default sparse chechout:
$ git clone --sparse-checkout=Documentation/* ...
or within an existing wd
$ git checkout --reset-sparse=Documentation/* ...
(maybe just --sparse?)
2) Work normally inside the Documentation directory...
3) Abort a merge with conflicts outside the sparse area:
$ git reset --hard ORIG_HEAD)
<Now there are files outside the default sparse area>
$ git checkout --reset-sparse # with --sparse to set the sparse pattern
Including/excluding more paths in the default sparse checkout is not
something you would do normally, but I think it makes sense to add
them to the default pattern.
Please correct me if you think there are other use-cases, or more
things in my use-case.
Maybe just throw a warning when default pattern no
longer matches the checkout area, then let them decide.
Sure, and inform in the "git status" that you are in a sparse checkout.
Best regards,
Santi
Mind-boggling, but this manages to break on Solaris. Fix is
below.
-- >8 --
t2104: touch portability fix
Some versions of touch support the syntax:
touch [MMDDhhmm[yy]] file...
which makes the first argument an optional time
specification. They can get confused by
touch 1 2 foo bar
as they assume that '1' is a bogus time specification. This
is broken, for example, with /bin/touch on Solaris 8.
To fix it, we'll just reverse the order of arguments so that
an unambiguous argument is in the slot for the optional time
specification.
Signed-off-by: Jeff King <redacted>
---
Note that this has implications for 'touch "$FOO" "$BAR"'
used in scripts if FOO might be entirely numeric. However, a
quick grep shows we usually touch one file at a time.
t/t2104-update-index-no-checkout.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)